schema-components 1.28.2 → 2.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.
- package/README.md +38 -16
- package/dist/core/adapter.d.mts +213 -3
- package/dist/core/adapter.mjs +21 -2
- package/dist/core/constraintHint.d.mts +15 -0
- package/dist/core/constraintHint.mjs +24 -0
- package/dist/core/constraints.d.mts +34 -2
- package/dist/core/constraints.mjs +33 -1
- package/dist/core/cssClasses.d.mts +1 -0
- package/dist/core/diagnostics.d.mts +1 -1
- package/dist/core/errors.d.mts +1 -1
- package/dist/core/errors.mjs +22 -12
- package/dist/core/fieldOrder.d.mts +1 -1
- package/dist/core/formats.d.mts +7 -1
- package/dist/core/formats.mjs +6 -0
- package/dist/core/idPath.d.mts +35 -5
- package/dist/core/idPath.mjs +79 -7
- package/dist/core/inferValue.d.mts +2 -0
- package/dist/core/inferValue.mjs +1 -0
- package/dist/core/limits.d.mts +1 -1
- package/dist/core/limits.mjs +6 -0
- package/dist/core/merge.d.mts +22 -1
- package/dist/core/merge.mjs +66 -3
- package/dist/core/normalise.d.mts +17 -2
- package/dist/core/normalise.mjs +1 -1
- package/dist/core/openapi30.mjs +1 -1
- package/dist/core/openapiConstants.d.mts +1 -0
- package/dist/core/ref.d.mts +1 -1
- package/dist/core/refChain.d.mts +3 -4
- package/dist/core/refChain.mjs +2 -3
- package/dist/core/renderer.d.mts +199 -2
- package/dist/core/renderer.mjs +5 -0
- package/dist/core/swagger2.d.mts +1 -1
- package/dist/core/swagger2.mjs +1 -1
- package/dist/core/typeInference.d.mts +3 -3
- package/dist/core/types.d.mts +1 -1
- package/dist/core/types.mjs +17 -0
- package/dist/core/unionMatch.d.mts +1 -1
- package/dist/core/uri.d.mts +12 -4
- package/dist/core/uri.mjs +30 -4
- package/dist/core/version.d.mts +1 -1
- package/dist/core/walkBuilders.d.mts +63 -6
- package/dist/core/walkBuilders.mjs +33 -1
- package/dist/core/walker.d.mts +14 -1
- package/dist/core/walker.mjs +18 -0
- package/dist/{diagnostics-Cbwak-ZX.d.mts → diagnostics-BTrm3O6J.d.mts} +9 -1
- package/dist/{errors-DQSIK4n1.d.mts → errors-Dki7tji4.d.mts} +23 -13
- package/dist/html/a11y.d.mts +3 -7
- package/dist/html/a11y.mjs +1 -16
- package/dist/html/html.d.mts +11 -0
- package/dist/html/html.mjs +11 -0
- package/dist/html/renderToHtml.d.mts +45 -12
- package/dist/html/renderToHtml.mjs +20 -4
- package/dist/html/renderToHtmlStream.d.mts +63 -18
- package/dist/html/renderToHtmlStream.mjs +34 -8
- package/dist/html/renderers.d.mts +6 -31
- package/dist/html/renderers.mjs +45 -91
- package/dist/html/streamRenderers.d.mts +31 -3
- package/dist/html/streamRenderers.mjs +41 -8
- package/dist/inferValue-PPXWJpbN.d.mts +77 -0
- package/dist/{limits-DJhgx5Ay.d.mts → limits-x4OiyJxh.d.mts} +6 -0
- package/dist/{normalise-Db1xaxgx.mjs → normalise-DB-Xtjmn.mjs} +43 -2
- package/dist/openapi/ApiCallbacks.d.mts +13 -1
- package/dist/openapi/ApiCallbacks.mjs +7 -0
- package/dist/openapi/ApiLinks.d.mts +13 -1
- package/dist/openapi/ApiLinks.mjs +7 -0
- package/dist/openapi/ApiResponseHeaders.d.mts +13 -1
- package/dist/openapi/ApiResponseHeaders.mjs +7 -0
- package/dist/openapi/ApiSecurity.d.mts +14 -1
- package/dist/openapi/ApiSecurity.mjs +29 -8
- package/dist/openapi/bundle.d.mts +31 -0
- package/dist/openapi/components.d.mts +135 -20
- package/dist/openapi/components.mjs +90 -15
- package/dist/openapi/parser.d.mts +140 -13
- package/dist/openapi/parser.mjs +84 -12
- package/dist/openapi/resolve.d.mts +42 -47
- package/dist/openapi/resolve.mjs +62 -56
- package/dist/react/SchemaComponent.d.mts +90 -88
- package/dist/react/SchemaComponent.mjs +74 -2
- package/dist/react/SchemaErrorBoundary.d.mts +18 -1
- package/dist/react/SchemaErrorBoundary.mjs +13 -1
- package/dist/react/SchemaView.d.mts +39 -11
- package/dist/react/SchemaView.mjs +23 -6
- package/dist/react/a11y.d.mts +74 -7
- package/dist/react/a11y.mjs +67 -6
- package/dist/react/fieldPath.d.mts +16 -1
- package/dist/react/fieldPath.mjs +25 -1
- package/dist/react/fieldShell.d.mts +49 -0
- package/dist/react/fieldShell.mjs +37 -0
- package/dist/react/headless.d.mts +1 -1
- package/dist/react/headlessRenderers.d.mts +13 -2
- package/dist/react/headlessRenderers.mjs +134 -54
- package/dist/{ref-TdeMfaV_.d.mts → ref-DdsbekXX.d.mts} +33 -1
- package/dist/themes/mantine.d.mts +54 -12
- package/dist/themes/mantine.mjs +195 -140
- package/dist/themes/mui.d.mts +64 -11
- package/dist/themes/mui.mjs +277 -213
- package/dist/themes/radix.d.mts +67 -15
- package/dist/themes/radix.mjs +235 -170
- package/dist/themes/shadcn.d.mts +25 -1
- package/dist/themes/shadcn.mjs +112 -91
- package/dist/{types-BTB73MB8.d.mts → types-BrYbjC7_.d.mts} +30 -0
- package/dist/{version-ZzL5R6cS.d.mts → version-DL8U5RuA.d.mts} +6 -0
- package/package.json +8 -1
- package/dist/adapter-DqlAnZ_w.d.mts +0 -172
- package/dist/renderer-Ul9taFYp.d.mts +0 -169
package/dist/themes/shadcn.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { toRecord } from "../core/guards.mjs";
|
|
|
2
2
|
import { sortFieldsByOrder } from "../core/fieldOrder.mjs";
|
|
3
3
|
import { displayJsonValue } from "../core/walkBuilders.mjs";
|
|
4
4
|
import { inputId, toReactNode } from "../react/headlessRenderers.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { FieldShell } from "../react/fieldShell.mjs";
|
|
6
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
//#region src/themes/shadcn.tsx
|
|
8
8
|
function isString(value) {
|
|
@@ -20,27 +20,23 @@ function renderStringInput(props) {
|
|
|
20
20
|
className: "text-sm",
|
|
21
21
|
children: strValue || "—"
|
|
22
22
|
});
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
},
|
|
41
|
-
placeholder: typeof props.meta.description === "string" ? props.meta.description : void 0,
|
|
42
|
-
minLength: props.constraints.minLength,
|
|
43
|
-
maxLength: props.constraints.maxLength
|
|
23
|
+
const placeholder = typeof props.meta.description === "string" ? props.meta.description : void 0;
|
|
24
|
+
return /* @__PURE__ */ jsx(FieldShell, {
|
|
25
|
+
props,
|
|
26
|
+
inputId: id,
|
|
27
|
+
children: (aria) => /* @__PURE__ */ jsx("input", {
|
|
28
|
+
id,
|
|
29
|
+
type: props.constraints.format === "email" ? "email" : "text",
|
|
30
|
+
className,
|
|
31
|
+
placeholder,
|
|
32
|
+
value: props.writeOnly ? "" : strValue,
|
|
33
|
+
onChange: (e) => {
|
|
34
|
+
props.onChange(e.target.value);
|
|
35
|
+
},
|
|
36
|
+
minLength: props.constraints.minLength,
|
|
37
|
+
maxLength: props.constraints.maxLength,
|
|
38
|
+
...aria
|
|
39
|
+
})
|
|
44
40
|
});
|
|
45
41
|
}
|
|
46
42
|
function renderNumberInput(props) {
|
|
@@ -58,16 +54,21 @@ function renderNumberInput(props) {
|
|
|
58
54
|
children: props.value.toLocaleString()
|
|
59
55
|
});
|
|
60
56
|
}
|
|
61
|
-
return /* @__PURE__ */ jsx(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
57
|
+
return /* @__PURE__ */ jsx(FieldShell, {
|
|
58
|
+
props,
|
|
59
|
+
inputId: id,
|
|
60
|
+
children: (aria) => /* @__PURE__ */ jsx("input", {
|
|
61
|
+
id,
|
|
62
|
+
type: "number",
|
|
63
|
+
className,
|
|
64
|
+
value: props.writeOnly ? "" : typeof props.value === "number" ? props.value : "",
|
|
65
|
+
onChange: (e) => {
|
|
66
|
+
props.onChange(Number(e.target.value));
|
|
67
|
+
},
|
|
68
|
+
min: props.constraints.minimum,
|
|
69
|
+
max: props.constraints.maximum,
|
|
70
|
+
...aria
|
|
71
|
+
})
|
|
71
72
|
});
|
|
72
73
|
}
|
|
73
74
|
function renderBooleanInput(props) {
|
|
@@ -85,14 +86,52 @@ function renderBooleanInput(props) {
|
|
|
85
86
|
children: props.value ? "Yes" : "No"
|
|
86
87
|
});
|
|
87
88
|
}
|
|
88
|
-
return /* @__PURE__ */ jsx(
|
|
89
|
+
return /* @__PURE__ */ jsx(FieldShell, {
|
|
90
|
+
props,
|
|
91
|
+
inputId: id,
|
|
92
|
+
children: (aria) => /* @__PURE__ */ jsx("input", {
|
|
93
|
+
id,
|
|
94
|
+
type: "checkbox",
|
|
95
|
+
className,
|
|
96
|
+
checked: props.writeOnly ? false : props.value === true,
|
|
97
|
+
onChange: (e) => {
|
|
98
|
+
props.onChange(e.target.checked);
|
|
99
|
+
},
|
|
100
|
+
...aria
|
|
101
|
+
})
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function renderEnumInput(props) {
|
|
105
|
+
const id = inputId(props.path);
|
|
106
|
+
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");
|
|
107
|
+
const enumValue = typeof props.value === "string" ? props.value : "";
|
|
108
|
+
if (props.readOnly) return /* @__PURE__ */ jsx("span", {
|
|
89
109
|
id,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
110
|
+
className: "text-sm",
|
|
111
|
+
children: enumValue || "—"
|
|
112
|
+
});
|
|
113
|
+
return /* @__PURE__ */ jsx(FieldShell, {
|
|
114
|
+
props,
|
|
115
|
+
inputId: id,
|
|
116
|
+
children: (aria) => /* @__PURE__ */ jsxs("select", {
|
|
117
|
+
id,
|
|
118
|
+
className,
|
|
119
|
+
value: props.writeOnly ? "" : enumValue,
|
|
120
|
+
onChange: (e) => {
|
|
121
|
+
props.onChange(e.target.value);
|
|
122
|
+
},
|
|
123
|
+
...aria,
|
|
124
|
+
children: [/* @__PURE__ */ jsxs("option", {
|
|
125
|
+
value: "",
|
|
126
|
+
children: ["Select", "…"]
|
|
127
|
+
}), props.tree.type === "enum" ? props.tree.enumValues.map((v) => {
|
|
128
|
+
const display = displayJsonValue(v);
|
|
129
|
+
return /* @__PURE__ */ jsx("option", {
|
|
130
|
+
value: display,
|
|
131
|
+
children: display
|
|
132
|
+
}, display);
|
|
133
|
+
}) : null]
|
|
134
|
+
})
|
|
96
135
|
});
|
|
97
136
|
}
|
|
98
137
|
function renderObjectContainer(props) {
|
|
@@ -106,20 +145,15 @@ function renderObjectContainer(props) {
|
|
|
106
145
|
children: props.meta.description
|
|
107
146
|
}), sortFieldsByOrder(fields).map(([key, field]) => {
|
|
108
147
|
const childValue = toRecord(obj)[key];
|
|
109
|
-
const childId = inputId(`${props.path}.${key}`);
|
|
110
148
|
const childOnChange = (v) => {
|
|
111
149
|
const updated = {};
|
|
112
150
|
for (const [k, val] of Object.entries(obj)) updated[k] = val;
|
|
113
151
|
updated[key] = v;
|
|
114
152
|
props.onChange(updated);
|
|
115
153
|
};
|
|
116
|
-
return /* @__PURE__ */
|
|
154
|
+
return /* @__PURE__ */ jsx("div", {
|
|
117
155
|
className: "space-y-1",
|
|
118
|
-
children:
|
|
119
|
-
htmlFor: childId,
|
|
120
|
-
className: "text-sm font-medium leading-none",
|
|
121
|
-
children: field.meta.description ?? key
|
|
122
|
-
}), toReactNode(props.renderChild(field, childValue, childOnChange, key))]
|
|
156
|
+
children: toReactNode(props.renderChild(field, childValue, childOnChange, key))
|
|
123
157
|
}, key);
|
|
124
158
|
})]
|
|
125
159
|
});
|
|
@@ -141,50 +175,37 @@ function renderArrayContainer(props) {
|
|
|
141
175
|
})
|
|
142
176
|
});
|
|
143
177
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
boolean: renderBooleanInput,
|
|
177
|
-
enum: renderEnumInput,
|
|
178
|
-
object: renderObjectContainer,
|
|
179
|
-
array: renderArrayContainer
|
|
180
|
-
};
|
|
181
|
-
if (headlessResolver.literal !== void 0) resolver.literal = headlessResolver.literal;
|
|
182
|
-
if (headlessResolver.union !== void 0) resolver.union = headlessResolver.union;
|
|
183
|
-
if (headlessResolver.record !== void 0) resolver.record = headlessResolver.record;
|
|
184
|
-
if (headlessResolver.file !== void 0) resolver.file = headlessResolver.file;
|
|
185
|
-
if (headlessResolver.unknown !== void 0) resolver.unknown = headlessResolver.unknown;
|
|
186
|
-
return resolver;
|
|
187
|
-
}
|
|
188
|
-
const shadcnResolver = buildResolver();
|
|
178
|
+
/**
|
|
179
|
+
* Component resolver mapping schema field types to shadcn/ui components.
|
|
180
|
+
*
|
|
181
|
+
* Pass to `SchemaProvider` to render every `<SchemaComponent>` /
|
|
182
|
+
* `<SchemaView>` in the subtree with shadcn/ui inputs, selects, and
|
|
183
|
+
* cards. Returns only the keys this theme overrides — the runtime
|
|
184
|
+
* `mergeResolvers` call inside `<SchemaComponent>` / `<SchemaView>`
|
|
185
|
+
* fills unset keys from `headlessResolver`, so variants like literal,
|
|
186
|
+
* union, discriminatedUnion, record, file, and unknown still render via
|
|
187
|
+
* the headless fallback.
|
|
188
|
+
*
|
|
189
|
+
* Requires `shadcn/ui` components installed in the consuming project.
|
|
190
|
+
*
|
|
191
|
+
* @group Themes
|
|
192
|
+
* @example
|
|
193
|
+
* ```tsx
|
|
194
|
+
* import { SchemaProvider } from "schema-components/react/SchemaComponent";
|
|
195
|
+
* import { shadcnResolver } from "schema-components/themes/shadcn";
|
|
196
|
+
*
|
|
197
|
+
* <SchemaProvider resolver={shadcnResolver}>
|
|
198
|
+
* <SchemaComponent schema={userSchema} value={user} onChange={setUser} />
|
|
199
|
+
* </SchemaProvider>
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
const shadcnResolver = {
|
|
203
|
+
string: renderStringInput,
|
|
204
|
+
number: renderNumberInput,
|
|
205
|
+
boolean: renderBooleanInput,
|
|
206
|
+
enum: renderEnumInput,
|
|
207
|
+
object: renderObjectContainer,
|
|
208
|
+
array: renderArrayContainer
|
|
209
|
+
};
|
|
189
210
|
//#endregion
|
|
190
211
|
export { shadcnResolver };
|
|
@@ -26,6 +26,12 @@ interface SchemaMeta {
|
|
|
26
26
|
/** Arbitrary UI hints passed through to theme adapters. */
|
|
27
27
|
[key: string]: unknown;
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolved editability state for a walked field. `presentation` renders
|
|
31
|
+
* as a read-only value, `input` as a write-only input (e.g. password
|
|
32
|
+
* fields), `editable` as a full input that round-trips through
|
|
33
|
+
* `onChange`. Produced by {@link resolveEditability}.
|
|
34
|
+
*/
|
|
29
35
|
type Editability = "presentation" | "input" | "editable";
|
|
30
36
|
/**
|
|
31
37
|
* Resolved editability state for a single field.
|
|
@@ -124,6 +130,13 @@ interface StringField extends FieldBase {
|
|
|
124
130
|
interface NumberField extends FieldBase {
|
|
125
131
|
type: "number";
|
|
126
132
|
constraints: NumberConstraints;
|
|
133
|
+
/**
|
|
134
|
+
* True when the underlying schema declared `type: "integer"` rather
|
|
135
|
+
* than `type: "number"`. Renderers consult this to set HTML
|
|
136
|
+
* `inputmode="numeric"` and `step="1"` (for whole-number editing)
|
|
137
|
+
* instead of `inputmode="decimal"`.
|
|
138
|
+
*/
|
|
139
|
+
isInteger: boolean;
|
|
127
140
|
}
|
|
128
141
|
interface BooleanField extends FieldBase {
|
|
129
142
|
type: "boolean";
|
|
@@ -285,22 +298,39 @@ interface UnknownField extends FieldBase {
|
|
|
285
298
|
* Use `field.type` to narrow to a specific variant.
|
|
286
299
|
*/
|
|
287
300
|
type WalkedField = StringField | NumberField | BooleanField | NullField | EnumField | LiteralField | ObjectField | ArrayField | TupleField | RecordField | UnionField | DiscriminatedUnionField | ConditionalField | NegationField | NeverField | FileField | UnknownField;
|
|
301
|
+
/** Type guard: narrows a `WalkedField` to its `string` variant. */
|
|
288
302
|
declare function isStringField(field: WalkedField): field is StringField;
|
|
303
|
+
/** Type guard: narrows a `WalkedField` to its `number` variant. */
|
|
289
304
|
declare function isNumberField(field: WalkedField): field is NumberField;
|
|
305
|
+
/** Type guard: narrows a `WalkedField` to its `boolean` variant. */
|
|
290
306
|
declare function isBooleanField(field: WalkedField): field is BooleanField;
|
|
307
|
+
/** Type guard: narrows a `WalkedField` to its `null` variant. */
|
|
291
308
|
declare function isNullField(field: WalkedField): field is NullField;
|
|
309
|
+
/** Type guard: narrows a `WalkedField` to its `enum` variant. */
|
|
292
310
|
declare function isEnumField(field: WalkedField): field is EnumField;
|
|
311
|
+
/** Type guard: narrows a `WalkedField` to its `literal` variant. */
|
|
293
312
|
declare function isLiteralField(field: WalkedField): field is LiteralField;
|
|
313
|
+
/** Type guard: narrows a `WalkedField` to its `object` variant. */
|
|
294
314
|
declare function isObjectField(field: WalkedField): field is ObjectField;
|
|
315
|
+
/** Type guard: narrows a `WalkedField` to its `array` variant. */
|
|
295
316
|
declare function isArrayField(field: WalkedField): field is ArrayField;
|
|
317
|
+
/** Type guard: narrows a `WalkedField` to its `tuple` variant. */
|
|
296
318
|
declare function isTupleField(field: WalkedField): field is TupleField;
|
|
319
|
+
/** Type guard: narrows a `WalkedField` to its `record` variant. */
|
|
297
320
|
declare function isRecordField(field: WalkedField): field is RecordField;
|
|
321
|
+
/** Type guard: narrows a `WalkedField` to its plain `union` variant. */
|
|
298
322
|
declare function isUnionField(field: WalkedField): field is UnionField;
|
|
323
|
+
/** Type guard: narrows a `WalkedField` to its `discriminatedUnion` variant. */
|
|
299
324
|
declare function isDiscriminatedUnionField(field: WalkedField): field is DiscriminatedUnionField;
|
|
325
|
+
/** Type guard: narrows a `WalkedField` to its `conditional` (if/then/else) variant. */
|
|
300
326
|
declare function isConditionalField(field: WalkedField): field is ConditionalField;
|
|
327
|
+
/** Type guard: narrows a `WalkedField` to its `negation` (`not`) variant. */
|
|
301
328
|
declare function isNegationField(field: WalkedField): field is NegationField;
|
|
329
|
+
/** Type guard: narrows a `WalkedField` to its `file` variant. */
|
|
302
330
|
declare function isFileField(field: WalkedField): field is FileField;
|
|
331
|
+
/** Type guard: narrows a `WalkedField` to its `never` variant (false schema). */
|
|
303
332
|
declare function isNeverField(field: WalkedField): field is NeverField;
|
|
333
|
+
/** Type guard: narrows a `WalkedField` to its `unknown` variant (permissive). */
|
|
304
334
|
declare function isUnknownField(field: WalkedField): field is UnknownField;
|
|
305
335
|
//#endregion
|
|
306
336
|
export { UnknownField as A, isNeverField as B, RecordField as C, StringField as D, StringConstraints as E, isDiscriminatedUnionField as F, isStringField as G, isNumberField as H, isEnumField as I, isUnknownField as J, isTupleField as K, isFileField as L, isArrayField as M, isBooleanField as N, TupleField as O, isConditionalField as P, isLiteralField as R, ObjectField as S, SchemaType as T, isObjectField as U, isNullField as V, isRecordField as W, resolveEditability as Y, 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, WalkedField as j, UnionField as k, FieldConstraints as l, JsonObject as m, ArrayField as n, Editability as o, FileField as p, isUnionField as q, BooleanField as r, EnumField as s, ArrayConstraints as t, FieldOverride as u, NullField as v, SchemaMeta as w, ObjectConstraints as x, NumberConstraints as y, isNegationField as z };
|
|
@@ -49,6 +49,12 @@ declare function inferJsonSchemaDraft(schema: Record<string, unknown>): JsonSche
|
|
|
49
49
|
* triggered the inference, for diagnostic emission.
|
|
50
50
|
*/
|
|
51
51
|
declare function inferJsonSchemaDraftWithReason(schema: Record<string, unknown>): InferredDraft;
|
|
52
|
+
/**
|
|
53
|
+
* Parsed OpenAPI version triple (e.g. `{ major: 3, minor: 1, patch: 0 }`).
|
|
54
|
+
* Produced by `detectOpenApiVersion` so downstream helpers can switch on
|
|
55
|
+
* the canonical numeric form rather than re-parsing the raw `openapi` /
|
|
56
|
+
* `swagger` string.
|
|
57
|
+
*/
|
|
52
58
|
interface OpenApiVersionInfo {
|
|
53
59
|
major: number;
|
|
54
60
|
minor: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "schema-components",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "React components that render UI from Zod schemas, JSON Schema, and OpenAPI documents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"_test:coverage": "vitest run --project=unit --coverage",
|
|
42
42
|
"_build": "tsdown && cp src/html/styles.css dist/html/styles.css",
|
|
43
43
|
"_typedoc": "typedoc",
|
|
44
|
+
"_api-urls": "node scripts/build-api-urls.mjs",
|
|
44
45
|
"_readme": "node scripts/build-readme-inventory.mjs",
|
|
45
46
|
"typecheck": "turbo run _typecheck",
|
|
46
47
|
"lint": "turbo run _lint",
|
|
@@ -51,6 +52,7 @@
|
|
|
51
52
|
"validate": "turbo run _validate",
|
|
52
53
|
"build": "turbo run _build",
|
|
53
54
|
"typedoc": "turbo run _typedoc",
|
|
55
|
+
"api-urls": "turbo run _api-urls",
|
|
54
56
|
"readme": "turbo run _readme"
|
|
55
57
|
},
|
|
56
58
|
"keywords": [
|
|
@@ -88,6 +90,9 @@
|
|
|
88
90
|
"@vitest/coverage-v8": "4.1.5",
|
|
89
91
|
"eslint": "10.3.0",
|
|
90
92
|
"eslint-config-prettier": "10.1.8",
|
|
93
|
+
"eslint-plugin-import": "2.32.0",
|
|
94
|
+
"eslint-plugin-jsx-a11y": "6.10.2",
|
|
95
|
+
"eslint-plugin-no-only-tests": "3.4.0",
|
|
91
96
|
"eslint-plugin-prettier": "5.5.5",
|
|
92
97
|
"happy-dom": "20.9.0",
|
|
93
98
|
"prettier": "3.8.3",
|
|
@@ -96,6 +101,8 @@
|
|
|
96
101
|
"tsdown": "0.22.0",
|
|
97
102
|
"tslib": "2.8.1",
|
|
98
103
|
"typedoc": "0.28.19",
|
|
104
|
+
"typedoc-material-theme": "1.4.1",
|
|
105
|
+
"typedoc-plugin-mermaid": "1.12.0",
|
|
99
106
|
"typedoc-plugin-missing-exports": "4.1.3",
|
|
100
107
|
"typescript": "6.0.3",
|
|
101
108
|
"typescript-eslint": "8.59.2",
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { m as JsonObject, w as SchemaMeta } from "./types-BTB73MB8.mjs";
|
|
2
|
-
import { i as DiagnosticsOptions } from "./diagnostics-Cbwak-ZX.mjs";
|
|
3
|
-
|
|
4
|
-
//#region src/core/adapter.d.ts
|
|
5
|
-
type SchemaInput = Record<string, unknown>;
|
|
6
|
-
type SchemaKind = "zod4" | "zod3" | "jsonSchema" | "openapi" | "unsupported-schema-lib";
|
|
7
|
-
/**
|
|
8
|
-
* Classify the input schema by its structural markers.
|
|
9
|
-
*
|
|
10
|
-
* - `zod4` — has a `_zod` marker (further validation that `_zod.def` is a
|
|
11
|
-
* non-null object happens inside `normaliseZod4`).
|
|
12
|
-
* - `zod3` — has `_def` and no `_zod`. The `typeName` field is no longer
|
|
13
|
-
* required: any `_def` without `_zod` is treated as a probable Zod 3
|
|
14
|
-
* schema. Third-party libraries that expose `_def` without `_zod` are
|
|
15
|
-
* nearly always Zod 3 forks; surfacing the migration message is the
|
|
16
|
-
* correct response.
|
|
17
|
-
* - `openapi` — has `openapi` or `swagger` at the root.
|
|
18
|
-
* - `unsupported-schema-lib` — has `parse` and `safeParse` callables but
|
|
19
|
-
* no `_zod` and no `_def` marker. This catches Standard Schema
|
|
20
|
-
* implementations (valibot, arktype, etc.) that would otherwise flow
|
|
21
|
-
* through as "malformed JSON Schema".
|
|
22
|
-
* - `jsonSchema` — fallback for anything that does not match the above.
|
|
23
|
-
*/
|
|
24
|
-
declare function detectSchemaKind(input: unknown): SchemaKind;
|
|
25
|
-
/**
|
|
26
|
-
* Wraps z.toJSONSchema() for a runtime-validated Zod schema.
|
|
27
|
-
*
|
|
28
|
-
* The _zod guard in normaliseZod4 has confirmed this is a valid Zod schema,
|
|
29
|
-
* but TypeScript cannot represent "has _zod.def" as the $ZodType parameter
|
|
30
|
-
* that z.toJSONSchema expects. This is the library boundary equivalent of
|
|
31
|
-
* object → Record<string, unknown> — the type mismatch is genuinely unavoidable.
|
|
32
|
-
*
|
|
33
|
-
* # Options
|
|
34
|
-
*
|
|
35
|
-
* `z.toJSONSchema` is invoked with an explicit options object rather than
|
|
36
|
-
* Zod's defaults so the conversion contract is pinned and stable:
|
|
37
|
-
*
|
|
38
|
-
* - `target: "draft-2020-12"` — matches the walker's draft target.
|
|
39
|
-
* - `unrepresentable: "throw"` — keeps the unrepresentable-type rules in
|
|
40
|
-
* the classifier table firing instead of silently emitting `{}`.
|
|
41
|
-
* - `cycles: "ref"` — converts cyclic graphs into $ref pairs rather than
|
|
42
|
-
* throwing. Cycles in user schemas surface through the walker's $ref
|
|
43
|
-
* resolution rather than the adapter.
|
|
44
|
-
* - `io` — selects which side of every transform / pipe / codec is
|
|
45
|
-
* converted. Defaults to `"output"` (the OUTPUT side); pass `"input"`
|
|
46
|
-
* to render the INPUT side instead. The input side is invisible to
|
|
47
|
-
* the converted schema when `io: "output"` is in force, even though
|
|
48
|
-
* `safeParse` on the same Zod schema consumes the input shape. For
|
|
49
|
-
* transforms this divergence is fatal and the call throws via
|
|
50
|
-
* `Transforms cannot be represented`; for `z.codec(...)` the call
|
|
51
|
-
* succeeds but only the selected side is rendered. Consumers receive
|
|
52
|
-
* a `zod-codec-output-only` diagnostic in the codec case so the
|
|
53
|
-
* asymmetry is visible — see `screenPreConversion`.
|
|
54
|
-
*
|
|
55
|
-
* # Error classification
|
|
56
|
-
*
|
|
57
|
-
* Any exception thrown by z.toJSONSchema is classified into a
|
|
58
|
-
* SchemaNormalisationError so the caller does not have to re-parse error
|
|
59
|
-
* message strings. The classification covers:
|
|
60
|
-
*
|
|
61
|
-
* - Nested Zod 3 schemas inside a Zod 4 tree → zod3-unsupported.
|
|
62
|
-
* Detected structurally (presence of `_def.typeName` markers anywhere
|
|
63
|
-
* in the schema tree) so the check works across V8, JavaScriptCore,
|
|
64
|
-
* and SpiderMonkey, none of which agree on the wording of
|
|
65
|
-
* "Cannot read properties of undefined".
|
|
66
|
-
* - Transforms → zod-transform-unsupported. This also catches `z.codec(…)`
|
|
67
|
-
* because Zod implements codecs as a pipe + transform internally, so
|
|
68
|
-
* they trip the same processor when round-tripping is forced. (Plain
|
|
69
|
-
* `z.toJSONSchema(codec)` itself does NOT throw because Zod picks one
|
|
70
|
-
* side of the codec; the static rejection in `typeInference.ts` is the
|
|
71
|
-
* compile-time guard.)
|
|
72
|
-
* - Dynamic catch values whose handler throws → zod-type-unrepresentable
|
|
73
|
-
* with zodType "dynamic-catch".
|
|
74
|
-
* - Unrepresentable types — bigint, date, map, set, symbol, function, custom,
|
|
75
|
-
* undefined, void, NaN, and the literal-only forms `z.literal(undefined)`
|
|
76
|
-
* ("undefined-literal") and `z.literal(<bigint>)` ("bigint-literal") →
|
|
77
|
-
* zod-type-unrepresentable.
|
|
78
|
-
* - The catch-all "Non-representable type encountered: <type>" fallback Zod
|
|
79
|
-
* emits for any new schema kind without a registered processor →
|
|
80
|
-
* zod-type-unrepresentable with zodType set to the offending def.type.
|
|
81
|
-
* - Cycle detected (`cycles: "throw"`) → zod-cycle-detected.
|
|
82
|
-
* - Duplicate schema id → zod-duplicate-id.
|
|
83
|
-
* - "Unprocessed schema. This is a bug in Zod." → zod-conversion-bug.
|
|
84
|
-
* - "Error converting schema to JSON." → zod-conversion-failed (explicit
|
|
85
|
-
* classification rather than the generic fallback so the contract test
|
|
86
|
-
* protects the prefix from drift).
|
|
87
|
-
* - Anything else → zod-conversion-failed.
|
|
88
|
-
*
|
|
89
|
-
* The original error is preserved on each classified error via the `cause`
|
|
90
|
-
* field so consumers can still inspect the Zod stack trace.
|
|
91
|
-
*/
|
|
92
|
-
/**
|
|
93
|
-
* IO side passed to the internal `callToJsonSchema` helper. The Zod runtime accepts
|
|
94
|
-
* `"input" | "output"` for the corresponding `io` option on
|
|
95
|
-
* `z.toJSONSchema`. Defaults to `"output"` everywhere in the adapter
|
|
96
|
-
* pipeline; the parameter exists so a future renderer or component
|
|
97
|
-
* (currently SchemaComponent — see TODO below) can request the input
|
|
98
|
-
* side without forking the helper.
|
|
99
|
-
*/
|
|
100
|
-
type SchemaIoSide = "input" | "output";
|
|
101
|
-
/**
|
|
102
|
-
* True when `value` is a Zod schema implemented as a codec
|
|
103
|
-
* (`z.codec(...)`). Detection looks for the `$ZodCodec` marker on the
|
|
104
|
-
* schema's `_zod.traits` Set — the same structural check used by Zod
|
|
105
|
-
* itself in `to-json-schema.ts`'s `isTransforming` helper.
|
|
106
|
-
*
|
|
107
|
-
* Promoted from a duplicated local helper in `react/SchemaComponent.tsx`
|
|
108
|
-
* so the validation boundary inside `runValidation` can branch on
|
|
109
|
-
* codec-vs-not-codec without re-implementing the trait check. The
|
|
110
|
-
* shared helper anchors a single source of truth for codec detection:
|
|
111
|
-
* any future change to Zod's trait naming flows through here, not
|
|
112
|
-
* through two parallel copies.
|
|
113
|
-
*
|
|
114
|
-
* Returns `false` for non-objects, plain JSON Schema inputs, OpenAPI
|
|
115
|
-
* documents, or Zod schemas of any other kind. This is structural
|
|
116
|
-
* rather than nominal — a Zod 4 codec produced by any path that ends
|
|
117
|
-
* up tagging `_zod.traits` with `$ZodCodec` is recognised, including
|
|
118
|
-
* schemas wrapped by user-defined helpers.
|
|
119
|
-
*/
|
|
120
|
-
declare function isCodecSchema(value: unknown): boolean;
|
|
121
|
-
/**
|
|
122
|
-
* Exposed for unit testing — lets the contract test enumerate every rule's
|
|
123
|
-
* `prefix` value and assert mutual non-prefixing.
|
|
124
|
-
*/
|
|
125
|
-
declare const __CLASSIFIER_RULES_FOR_TEST: readonly {
|
|
126
|
-
readonly prefix: string;
|
|
127
|
-
}[];
|
|
128
|
-
interface NormalisedSchema {
|
|
129
|
-
/** JSON Schema object — the authoritative schema for rendering. */
|
|
130
|
-
jsonSchema: JsonObject;
|
|
131
|
-
/** Original Zod schema, if input was Zod. Used for validation. */
|
|
132
|
-
zodSchema?: unknown;
|
|
133
|
-
/** Root-level metadata. */
|
|
134
|
-
rootMeta: SchemaMeta | undefined;
|
|
135
|
-
/** The root document for $ref resolution. */
|
|
136
|
-
rootDocument: JsonObject;
|
|
137
|
-
}
|
|
138
|
-
interface NormaliseOptions {
|
|
139
|
-
/** Diagnostics channel for surfacing silent fallbacks. */
|
|
140
|
-
diagnostics?: DiagnosticsOptions;
|
|
141
|
-
/**
|
|
142
|
-
* Side of every transform / pipe / codec to render. Defaults to
|
|
143
|
-
* `"output"`, matching `z.toJSONSchema`'s default and the
|
|
144
|
-
* historic behaviour of the adapter. Passing `"input"` flips the
|
|
145
|
-
* conversion so consumers rendering the input shape of a
|
|
146
|
-
* `z.codec(...)` chain receive that side instead of the output
|
|
147
|
-
* side. Only the Zod 4 branch consults this option — JSON Schema
|
|
148
|
-
* and OpenAPI inputs are already a single canonical shape.
|
|
149
|
-
*/
|
|
150
|
-
io?: SchemaIoSide;
|
|
151
|
-
}
|
|
152
|
-
declare function normaliseSchema(input: unknown, ref?: string, options?: NormaliseOptions): NormalisedSchema;
|
|
153
|
-
/**
|
|
154
|
-
* Surface root-level metadata from the JSON Schema into the `rootMeta`
|
|
155
|
-
* shape consumed by the walker. Pulls `readOnly`, `writeOnly`,
|
|
156
|
-
* `description`, `title`, `deprecated`, `examples`, and `default`
|
|
157
|
-
* directly from the schema root.
|
|
158
|
-
*
|
|
159
|
-
* `examples` is forwarded only when present as an array (per JSON Schema
|
|
160
|
-
* Draft 2020-12 — Draft 04's `example` singular is normalised upstream).
|
|
161
|
-
* `default` is forwarded for any value the schema declares (any JSON
|
|
162
|
-
* value, including `null` and `false`); the presence check uses `in`
|
|
163
|
-
* so a literal `false` or `null` default is preserved.
|
|
164
|
-
*
|
|
165
|
-
* `examples` and `default` ride on the `[key: string]: unknown` index
|
|
166
|
-
* signature of {@link SchemaMeta}. They are not declared as named fields
|
|
167
|
-
* on `SchemaMeta` because that type lives in `types.ts` and is shared
|
|
168
|
-
* with the walker; the index signature is the agreed extension point.
|
|
169
|
-
*/
|
|
170
|
-
declare function extractRootMetaFromJson(jsonSchema: JsonObject): SchemaMeta | undefined;
|
|
171
|
-
//#endregion
|
|
172
|
-
export { SchemaKind as a, extractRootMetaFromJson as c, SchemaIoSide as i, isCodecSchema as l, NormalisedSchema as n, __CLASSIFIER_RULES_FOR_TEST as o, SchemaInput as r, detectSchemaKind as s, NormaliseOptions as t, normaliseSchema as u };
|