schema-components 1.16.3 → 1.18.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/dist/core/adapter.d.mts +1 -1
- package/dist/core/constraints.d.mts +1 -1
- package/dist/core/diagnostics.d.mts +1 -1
- package/dist/core/merge.d.mts +14 -8
- package/dist/core/merge.mjs +81 -12
- package/dist/core/normalise.d.mts +1 -1
- package/dist/core/ref.d.mts +1 -1
- package/dist/core/renderer.d.mts +2 -2
- package/dist/core/renderer.mjs +50 -1
- package/dist/core/swagger2.d.mts +1 -1
- package/dist/core/typeInference.d.mts +2 -2
- package/dist/core/walkBuilders.d.mts +2 -2
- package/dist/core/walker.mjs +2 -2
- package/dist/{diagnostics-DzbZmcLI.d.mts → diagnostics-BYk63jsC.d.mts} +1 -1
- package/dist/html/a11y.d.mts +13 -2
- package/dist/html/a11y.mjs +26 -2
- package/dist/html/renderToHtml.d.mts +1 -1
- package/dist/html/renderToHtml.mjs +5 -3
- package/dist/html/renderToHtmlStream.d.mts +1 -1
- package/dist/html/renderers.d.mts +4 -3
- package/dist/html/renderers.mjs +9 -13
- package/dist/html/streamRenderers.d.mts +1 -1
- package/dist/openapi/bundle.d.mts +9 -4
- package/dist/openapi/bundle.mjs +73 -15
- package/dist/openapi/components.d.mts +1 -1
- package/dist/openapi/components.mjs +61 -27
- package/dist/openapi/parser.mjs +8 -8
- package/dist/openapi/resolve.d.mts +13 -2
- package/dist/openapi/resolve.mjs +19 -3
- package/dist/react/SchemaComponent.d.mts +35 -7
- package/dist/react/SchemaComponent.mjs +49 -43
- package/dist/react/SchemaView.d.mts +12 -4
- package/dist/react/SchemaView.mjs +24 -49
- package/dist/react/headless.d.mts +1 -1
- package/dist/react/headlessRenderers.d.mts +15 -2
- package/dist/react/headlessRenderers.mjs +52 -37
- package/dist/{ref-DvWoULcy.d.mts → ref-Ckt5liZs.d.mts} +1 -1
- package/dist/{renderer-BdSqllx5.d.mts → renderer-DXo-rXHJ.d.mts} +28 -2
- package/dist/themes/mantine.d.mts +6 -1
- package/dist/themes/mantine.mjs +44 -11
- package/dist/themes/mui.d.mts +1 -1
- package/dist/themes/mui.mjs +23 -8
- package/dist/themes/radix.d.mts +1 -1
- package/dist/themes/radix.mjs +43 -11
- package/dist/themes/shadcn.d.mts +1 -1
- package/dist/themes/shadcn.mjs +28 -10
- package/dist/{typeInference-k7FXfTVO.d.mts → typeInference-5JiqIZ8t.d.mts} +57 -4
- package/package.json +5 -2
package/dist/openapi/bundle.mjs
CHANGED
|
@@ -29,28 +29,38 @@ import { isObject } from "../core/guards.mjs";
|
|
|
29
29
|
*
|
|
30
30
|
* Walks every $ref in the document. For external refs (not starting with `#`),
|
|
31
31
|
* calls the resolver to fetch the external document, extracts the referenced
|
|
32
|
-
* schema, inlines it into `components.schemas`
|
|
33
|
-
* rewrites the $ref to point
|
|
32
|
+
* schema, inlines it into `components.schemas` under a synthesised name, and
|
|
33
|
+
* rewrites the original $ref to point at the new internal location
|
|
34
|
+
* (`#/components/schemas/<name>`).
|
|
35
|
+
*
|
|
36
|
+
* Identical external refs share a single entry — the second occurrence of
|
|
37
|
+
* the same `(uri, fragment)` pair reuses the name produced for the first.
|
|
38
|
+
* Name collisions between different refs are resolved by suffixing a counter.
|
|
34
39
|
*
|
|
35
40
|
* The resolver is called once per unique URI and the result is cached.
|
|
36
41
|
*
|
|
37
|
-
* Returns a deep-cloned document with all external refs
|
|
38
|
-
* The original document is never mutated.
|
|
42
|
+
* Returns a deep-cloned document with all external refs replaced by internal
|
|
43
|
+
* refs. The original document is never mutated.
|
|
39
44
|
*/
|
|
40
45
|
async function bundleOpenApiDoc(doc, resolver) {
|
|
41
46
|
const result = structuredClone(doc);
|
|
42
47
|
const uriCache = /* @__PURE__ */ new Map();
|
|
48
|
+
const inlineCache = /* @__PURE__ */ new Map();
|
|
43
49
|
if (!isObject(result.components)) result.components = {};
|
|
44
|
-
|
|
45
|
-
if (isObject(
|
|
46
|
-
|
|
50
|
+
const components = result.components;
|
|
51
|
+
if (!isObject(components)) throw new Error("bundleOpenApiDoc: components is not an object");
|
|
52
|
+
if (!isObject(components.schemas)) components.schemas = {};
|
|
53
|
+
const schemasNode = components.schemas;
|
|
54
|
+
if (!isObject(schemasNode)) throw new Error("bundleOpenApiDoc: components.schemas is not an object");
|
|
55
|
+
await walkAndInline(result, schemasNode, uriCache, inlineCache, resolver);
|
|
47
56
|
return result;
|
|
48
57
|
}
|
|
49
58
|
/**
|
|
50
59
|
* Walk a document tree, find external $ref strings, resolve them,
|
|
51
|
-
* inline the targets
|
|
60
|
+
* inline the targets into `components.schemas`, and rewrite each $ref
|
|
61
|
+
* to point at the new internal location.
|
|
52
62
|
*/
|
|
53
|
-
async function walkAndInline(node, uriCache, resolver) {
|
|
63
|
+
async function walkAndInline(node, schemasNode, uriCache, inlineCache, resolver) {
|
|
54
64
|
if (!isObject(node)) return;
|
|
55
65
|
if (typeof node.$ref === "string" && !node.$ref.startsWith("#")) {
|
|
56
66
|
const ref = node.$ref;
|
|
@@ -66,15 +76,21 @@ async function walkAndInline(node, uriCache, resolver) {
|
|
|
66
76
|
}
|
|
67
77
|
}
|
|
68
78
|
if (externalDoc !== void 0) {
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
79
|
+
const cacheKey = `${uri}${fragment}`;
|
|
80
|
+
let inlinedName = inlineCache.get(cacheKey);
|
|
81
|
+
if (inlinedName === void 0) {
|
|
82
|
+
const target = resolveFragment(externalDoc, fragment);
|
|
83
|
+
if (isObject(target)) {
|
|
84
|
+
inlinedName = registerInline(schemasNode, uri, fragment, target);
|
|
85
|
+
inlineCache.set(cacheKey, inlinedName);
|
|
86
|
+
await walkAndInline(schemasNode[inlinedName], schemasNode, uriCache, inlineCache, resolver);
|
|
87
|
+
}
|
|
73
88
|
}
|
|
89
|
+
if (inlinedName !== void 0) node.$ref = `#/components/schemas/${inlinedName}`;
|
|
74
90
|
}
|
|
75
91
|
}
|
|
76
|
-
for (const value of Object.values(node)) if (isObject(value)) await walkAndInline(value, uriCache, resolver);
|
|
77
|
-
else if (Array.isArray(value)) for (const item of value) await walkAndInline(item, uriCache, resolver);
|
|
92
|
+
for (const value of Object.values(node)) if (isObject(value)) await walkAndInline(value, schemasNode, uriCache, inlineCache, resolver);
|
|
93
|
+
else if (Array.isArray(value)) for (const item of value) await walkAndInline(item, schemasNode, uriCache, inlineCache, resolver);
|
|
78
94
|
}
|
|
79
95
|
/**
|
|
80
96
|
* Resolve a JSON Pointer fragment within a document.
|
|
@@ -91,5 +107,47 @@ function resolveFragment(doc, fragment) {
|
|
|
91
107
|
}
|
|
92
108
|
return isObject(current) ? current : void 0;
|
|
93
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Derive a candidate identifier for an inlined external schema. Prefers
|
|
112
|
+
* the last meaningful segment of the JSON Pointer fragment; falls back
|
|
113
|
+
* to the URI's filename (sans extension), then to a generic prefix.
|
|
114
|
+
*/
|
|
115
|
+
function deriveCandidateName(uri, fragment) {
|
|
116
|
+
if (fragment.startsWith("#/")) {
|
|
117
|
+
const last = fragment.slice(2).split("/").at(-1);
|
|
118
|
+
if (last !== void 0 && last.length > 0) return sanitiseName(last);
|
|
119
|
+
}
|
|
120
|
+
const pathOnly = uri.split(/[?#]/)[0] ?? uri;
|
|
121
|
+
const lastSlash = pathOnly.lastIndexOf("/");
|
|
122
|
+
const filename = lastSlash >= 0 ? pathOnly.slice(lastSlash + 1) : pathOnly;
|
|
123
|
+
const dot = filename.lastIndexOf(".");
|
|
124
|
+
const stem = dot > 0 ? filename.slice(0, dot) : filename;
|
|
125
|
+
if (stem.length > 0) return sanitiseName(stem);
|
|
126
|
+
return "ExternalSchema";
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Sanitise a string into a JSON Pointer-safe identifier: alphanumerics
|
|
130
|
+
* and underscores only. An empty result falls back to "Schema".
|
|
131
|
+
*/
|
|
132
|
+
function sanitiseName(raw) {
|
|
133
|
+
const cleaned = raw.replace(/[^A-Za-z0-9_]/g, "_");
|
|
134
|
+
return cleaned.length > 0 ? cleaned : "Schema";
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Place the resolved target into `components.schemas` under a unique
|
|
138
|
+
* name derived from the ref, and return the chosen name. Collisions
|
|
139
|
+
* with existing entries are resolved by suffixing a counter.
|
|
140
|
+
*/
|
|
141
|
+
function registerInline(schemasNode, uri, fragment, target) {
|
|
142
|
+
const base = deriveCandidateName(uri, fragment);
|
|
143
|
+
let name = base;
|
|
144
|
+
let counter = 2;
|
|
145
|
+
while (name in schemasNode) {
|
|
146
|
+
name = `${base}_${String(counter)}`;
|
|
147
|
+
counter++;
|
|
148
|
+
}
|
|
149
|
+
schemasNode[name] = structuredClone(target);
|
|
150
|
+
return name;
|
|
151
|
+
}
|
|
94
152
|
//#endregion
|
|
95
153
|
export { bundleOpenApiDoc };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { T as SchemaMeta, u as FieldOverride } from "../types-D_5ST7SS.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { a as InferResponseFields, d as UnsafeFields, i as InferRequestBodyFields, r as InferParameterOverrides } from "../typeInference-5JiqIZ8t.mjs";
|
|
3
3
|
import { WidgetMap } from "../react/SchemaComponent.mjs";
|
|
4
4
|
import { ReactNode } from "react";
|
|
5
5
|
|
|
@@ -1,39 +1,46 @@
|
|
|
1
|
-
import { toRecordOrUndefined } from "../core/guards.mjs";
|
|
2
|
-
import { SchemaNormalisationError } from "../core/errors.mjs";
|
|
3
|
-
import { normaliseSchema } from "../core/adapter.mjs";
|
|
1
|
+
import { isObject, toRecordOrUndefined } from "../core/guards.mjs";
|
|
4
2
|
import { walk } from "../core/walker.mjs";
|
|
3
|
+
import { joinPath, renderField, sanitisePrefix } from "../react/SchemaComponent.mjs";
|
|
5
4
|
import { ApiCallbacks } from "./ApiCallbacks.mjs";
|
|
6
5
|
import { ApiLinks } from "./ApiLinks.mjs";
|
|
7
6
|
import { ApiResponseHeaders } from "./ApiResponseHeaders.mjs";
|
|
8
7
|
import { ApiSecurity } from "./ApiSecurity.mjs";
|
|
9
8
|
import { getLinks, getSecurityRequirements, getSecuritySchemes, listCallbacks } from "./parser.mjs";
|
|
10
|
-
import { renderField } from "../react/SchemaComponent.mjs";
|
|
11
9
|
import { getParsed, resolveOperation, resolveParameters, resolveRequestBody, resolveResponse, toDoc } from "./resolve.mjs";
|
|
10
|
+
import { useId } from "react";
|
|
12
11
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
13
12
|
//#region src/openapi/components.tsx
|
|
13
|
+
/**
|
|
14
|
+
* OpenAPI React components with type-safe generics.
|
|
15
|
+
*
|
|
16
|
+
* Render API operations, parameters, request bodies, and response schemas
|
|
17
|
+
* from OpenAPI 3.x documents. When the document is typed `as const`,
|
|
18
|
+
* the `fields` / `overrides` props get full autocomplete.
|
|
19
|
+
*
|
|
20
|
+
* Type safety is enforced at the outer component's props level via
|
|
21
|
+
* conditional types (InferRequestBodyFields, InferResponseFields,
|
|
22
|
+
* InferParameterOverrides). Internally, schemas are extracted and
|
|
23
|
+
* rendered via the walker + headless resolver directly, bypassing
|
|
24
|
+
* SchemaComponent to avoid deferred-conditional-type compatibility issues.
|
|
25
|
+
*/
|
|
14
26
|
function noop() {}
|
|
15
27
|
function renderSchema(schema, rootDocument, options) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
try {
|
|
19
|
-
const normalised = normaliseSchema(schema);
|
|
20
|
-
jsonSchema = normalised.jsonSchema;
|
|
21
|
-
rootMeta = normalised.rootMeta;
|
|
22
|
-
} catch (err) {
|
|
23
|
-
throw new SchemaNormalisationError(err instanceof Error ? err.message : "Failed to normalise schema", schema, "unknown");
|
|
24
|
-
}
|
|
28
|
+
if (!isObject(schema)) throw new Error("renderSchema received a non-object schema from the resolver.");
|
|
29
|
+
const rootMeta = extractRootMetaFromSchema(schema);
|
|
25
30
|
const componentMeta = {};
|
|
26
31
|
if (options.readOnly === true) componentMeta.readOnly = true;
|
|
27
32
|
if (options.meta !== void 0) for (const [k, v] of Object.entries(options.meta)) componentMeta[k] = v;
|
|
28
|
-
const
|
|
33
|
+
const tree = walk(schema, {
|
|
29
34
|
componentMeta,
|
|
30
35
|
rootMeta,
|
|
31
36
|
fieldOverrides: toRecordOrUndefined(options.fields),
|
|
32
37
|
rootDocument
|
|
38
|
+
});
|
|
39
|
+
const makeRenderChild = (parentPath) => (childTree, childValue, childOnChange, pathSuffix) => {
|
|
40
|
+
const childPath = joinPath(parentPath, pathSuffix);
|
|
41
|
+
return renderField(childTree, childValue, childOnChange, void 0, makeRenderChild(childPath), childPath, options.widgets);
|
|
33
42
|
};
|
|
34
|
-
|
|
35
|
-
const renderChild = (childTree, childValue, childOnChange) => renderField(childTree, childValue, childOnChange, void 0, renderChild, options.widgets);
|
|
36
|
-
return renderField(tree, options.value, options.onChange ?? noop, void 0, renderChild, options.widgets);
|
|
43
|
+
return renderField(tree, options.value, options.onChange ?? noop, void 0, makeRenderChild(options.rootPath), options.rootPath, options.widgets);
|
|
37
44
|
}
|
|
38
45
|
function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBodyChange, responseValue, meta, requestBodyFields, widgets }) {
|
|
39
46
|
const rootDoc = toDoc(doc);
|
|
@@ -42,6 +49,7 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
|
|
|
42
49
|
const securityReqs = getSecurityRequirements(parsed, path, method);
|
|
43
50
|
const securitySchemes = getSecuritySchemes(parsed);
|
|
44
51
|
const callbacks = listCallbacks(parsed, path, method);
|
|
52
|
+
const instancePrefix = sanitisePrefix(useId());
|
|
45
53
|
return /* @__PURE__ */ jsxs("section", {
|
|
46
54
|
"data-operation": `${method.toUpperCase()} ${path}`,
|
|
47
55
|
children: [
|
|
@@ -57,7 +65,8 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
|
|
|
57
65
|
parameters: resolved.parameters,
|
|
58
66
|
rootDoc,
|
|
59
67
|
meta,
|
|
60
|
-
widgets
|
|
68
|
+
widgets,
|
|
69
|
+
idPrefix: joinPath(instancePrefix, "params")
|
|
61
70
|
})]
|
|
62
71
|
}),
|
|
63
72
|
resolved.requestBody?.schema !== void 0 && /* @__PURE__ */ jsxs("section", {
|
|
@@ -77,7 +86,8 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
|
|
|
77
86
|
onChange: onRequestBodyChange,
|
|
78
87
|
fields: requestBodyFields,
|
|
79
88
|
meta,
|
|
80
|
-
widgets
|
|
89
|
+
widgets,
|
|
90
|
+
rootPath: joinPath(instancePrefix, "requestBody")
|
|
81
91
|
})
|
|
82
92
|
]
|
|
83
93
|
}),
|
|
@@ -90,7 +100,8 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
|
|
|
90
100
|
meta,
|
|
91
101
|
widgets,
|
|
92
102
|
path,
|
|
93
|
-
method
|
|
103
|
+
method,
|
|
104
|
+
idPrefix: joinPath(instancePrefix, `response-${response.statusCode}`)
|
|
94
105
|
}, response.statusCode))]
|
|
95
106
|
})
|
|
96
107
|
]
|
|
@@ -99,6 +110,7 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
|
|
|
99
110
|
function ApiParameters({ schema: doc, path, method, meta, overrides, widgets }) {
|
|
100
111
|
const rootDoc = toDoc(doc);
|
|
101
112
|
const params = resolveParameters(rootDoc, path, method);
|
|
113
|
+
const instancePrefix = sanitisePrefix(useId());
|
|
102
114
|
if (params.length === 0) return null;
|
|
103
115
|
return /* @__PURE__ */ jsxs("section", {
|
|
104
116
|
"data-parameters": true,
|
|
@@ -107,13 +119,15 @@ function ApiParameters({ schema: doc, path, method, meta, overrides, widgets })
|
|
|
107
119
|
rootDoc,
|
|
108
120
|
overrides,
|
|
109
121
|
meta,
|
|
110
|
-
widgets
|
|
122
|
+
widgets,
|
|
123
|
+
idPrefix: instancePrefix
|
|
111
124
|
})]
|
|
112
125
|
});
|
|
113
126
|
}
|
|
114
127
|
function ApiRequestBody({ schema: doc, path, method, value, onChange, meta, fields, widgets }) {
|
|
115
128
|
const rootDoc = toDoc(doc);
|
|
116
129
|
const requestBody = resolveRequestBody(rootDoc, path, method);
|
|
130
|
+
const instancePrefix = sanitisePrefix(useId());
|
|
117
131
|
if (requestBody?.schema === void 0) return null;
|
|
118
132
|
return /* @__PURE__ */ jsxs("section", {
|
|
119
133
|
"data-request-body": true,
|
|
@@ -132,7 +146,8 @@ function ApiRequestBody({ schema: doc, path, method, value, onChange, meta, fiel
|
|
|
132
146
|
onChange,
|
|
133
147
|
fields,
|
|
134
148
|
meta,
|
|
135
|
-
widgets
|
|
149
|
+
widgets,
|
|
150
|
+
rootPath: instancePrefix
|
|
136
151
|
})
|
|
137
152
|
]
|
|
138
153
|
});
|
|
@@ -140,6 +155,7 @@ function ApiRequestBody({ schema: doc, path, method, value, onChange, meta, fiel
|
|
|
140
155
|
function ApiResponse({ schema: doc, path, method, status, value, meta, fields, widgets }) {
|
|
141
156
|
const rootDoc = toDoc(doc);
|
|
142
157
|
const response = resolveResponse(rootDoc, path, method, status);
|
|
158
|
+
const instancePrefix = sanitisePrefix(useId());
|
|
143
159
|
if (response.schema === void 0) return /* @__PURE__ */ jsxs("div", {
|
|
144
160
|
"data-status": status,
|
|
145
161
|
children: [
|
|
@@ -156,7 +172,8 @@ function ApiResponse({ schema: doc, path, method, status, value, meta, fields, w
|
|
|
156
172
|
meta,
|
|
157
173
|
widgets,
|
|
158
174
|
path,
|
|
159
|
-
method
|
|
175
|
+
method,
|
|
176
|
+
idPrefix: instancePrefix
|
|
160
177
|
});
|
|
161
178
|
}
|
|
162
179
|
function OperationHeader({ operation }) {
|
|
@@ -173,7 +190,7 @@ function OperationHeader({ operation }) {
|
|
|
173
190
|
})
|
|
174
191
|
] });
|
|
175
192
|
}
|
|
176
|
-
function ParameterList({ parameters, rootDoc, overrides, meta, widgets }) {
|
|
193
|
+
function ParameterList({ parameters, rootDoc, overrides, meta, widgets, idPrefix }) {
|
|
177
194
|
return /* @__PURE__ */ jsx(Fragment, { children: parameters.map((param) => /* @__PURE__ */ jsxs("div", {
|
|
178
195
|
"data-parameter": param.name,
|
|
179
196
|
children: [
|
|
@@ -187,12 +204,13 @@ function ParameterList({ parameters, rootDoc, overrides, meta, widgets }) {
|
|
|
187
204
|
}),
|
|
188
205
|
renderSchema(param.schema ?? { type: "string" }, rootDoc, {
|
|
189
206
|
meta: buildParamMeta(param, overrides, meta),
|
|
190
|
-
widgets
|
|
207
|
+
widgets,
|
|
208
|
+
rootPath: joinPath(idPrefix, param.name)
|
|
191
209
|
})
|
|
192
210
|
]
|
|
193
211
|
}, param.name)) });
|
|
194
212
|
}
|
|
195
|
-
function ResponseCard({ response, rootDoc, value, fields, meta, widgets, path, method }) {
|
|
213
|
+
function ResponseCard({ response, rootDoc, value, fields, meta, widgets, path, method, idPrefix }) {
|
|
196
214
|
if (response.schema === void 0) return /* @__PURE__ */ jsxs("div", {
|
|
197
215
|
"data-status": response.statusCode,
|
|
198
216
|
children: [
|
|
@@ -217,7 +235,8 @@ function ResponseCard({ response, rootDoc, value, fields, meta, widgets, path, m
|
|
|
217
235
|
readOnly: true,
|
|
218
236
|
...meta
|
|
219
237
|
},
|
|
220
|
-
widgets
|
|
238
|
+
widgets,
|
|
239
|
+
rootPath: idPrefix
|
|
221
240
|
}),
|
|
222
241
|
/* @__PURE__ */ jsx(ApiResponseHeaders, { headers: response.headers }),
|
|
223
242
|
/* @__PURE__ */ jsx(ApiLinks, { links })
|
|
@@ -233,5 +252,20 @@ function buildParamMeta(param, overrides, meta) {
|
|
|
233
252
|
if (meta !== void 0) for (const [k, v] of Object.entries(meta)) result[k] = v;
|
|
234
253
|
return Object.keys(result).length > 0 ? result : void 0;
|
|
235
254
|
}
|
|
255
|
+
/**
|
|
256
|
+
* Extract root-level meta (title, description, readOnly, etc.) from a
|
|
257
|
+
* JSON Schema node. Mirrors `extractRootMetaFromJson` in the adapter so
|
|
258
|
+
* pre-normalised schemas (extracted from `getParsed`) still surface root
|
|
259
|
+
* meta to the walker without an extra adapter round-trip.
|
|
260
|
+
*/
|
|
261
|
+
function extractRootMetaFromSchema(jsonSchema) {
|
|
262
|
+
const meta = {};
|
|
263
|
+
if (jsonSchema.readOnly === true) meta.readOnly = true;
|
|
264
|
+
if (jsonSchema.writeOnly === true) meta.writeOnly = true;
|
|
265
|
+
if (typeof jsonSchema.description === "string") meta.description = jsonSchema.description;
|
|
266
|
+
if (typeof jsonSchema.title === "string") meta.title = jsonSchema.title;
|
|
267
|
+
if (typeof jsonSchema.deprecated === "boolean") meta.deprecated = jsonSchema.deprecated;
|
|
268
|
+
return Object.keys(meta).length > 0 ? meta : void 0;
|
|
269
|
+
}
|
|
236
270
|
//#endregion
|
|
237
271
|
export { ApiOperation, ApiParameters, ApiRequestBody, ApiResponse };
|
package/dist/openapi/parser.mjs
CHANGED
|
@@ -79,22 +79,22 @@ function getParameters(parsed, path, method) {
|
|
|
79
79
|
if (pathItem === void 0) return [];
|
|
80
80
|
const operation = getProperty(pathItem, method);
|
|
81
81
|
if (!isObject(operation)) return [];
|
|
82
|
-
const pathParams = extractParameterList(getProperty(pathItem, "parameters"));
|
|
83
|
-
const opParams = extractParameterList(getProperty(operation, "parameters"));
|
|
82
|
+
const pathParams = extractParameterList(parsed.doc, getProperty(pathItem, "parameters"));
|
|
83
|
+
const opParams = extractParameterList(parsed.doc, getProperty(operation, "parameters"));
|
|
84
84
|
const map = /* @__PURE__ */ new Map();
|
|
85
85
|
for (const param of pathParams) map.set(`${param.name}:${param.location}`, param);
|
|
86
86
|
for (const param of opParams) map.set(`${param.name}:${param.location}`, param);
|
|
87
87
|
return [...map.values()];
|
|
88
88
|
}
|
|
89
|
-
function extractParameterList(parameters) {
|
|
89
|
+
function extractParameterList(doc, parameters) {
|
|
90
90
|
if (!Array.isArray(parameters)) return [];
|
|
91
91
|
const result = [];
|
|
92
92
|
for (const param of parameters) {
|
|
93
93
|
if (!isObject(param)) continue;
|
|
94
|
-
const
|
|
95
|
-
const
|
|
94
|
+
const resolved = resolveParam(doc, param);
|
|
95
|
+
const name = getProperty(resolved, "name");
|
|
96
|
+
const location = getProperty(resolved, "in");
|
|
96
97
|
if (typeof name !== "string" || typeof location !== "string") continue;
|
|
97
|
-
const resolved = resolveParam(param);
|
|
98
98
|
const schema = getProperty(resolved, "schema");
|
|
99
99
|
result.push({
|
|
100
100
|
name,
|
|
@@ -107,10 +107,10 @@ function extractParameterList(parameters) {
|
|
|
107
107
|
}
|
|
108
108
|
return result;
|
|
109
109
|
}
|
|
110
|
-
function resolveParam(param) {
|
|
110
|
+
function resolveParam(doc, param) {
|
|
111
111
|
const ref = getProperty(param, "$ref");
|
|
112
112
|
if (typeof ref === "string" && ref.startsWith("#/")) {
|
|
113
|
-
const resolved = resolveRefInDoc(
|
|
113
|
+
const resolved = resolveRefInDoc(doc, ref);
|
|
114
114
|
if (resolved !== void 0) return resolved;
|
|
115
115
|
}
|
|
116
116
|
return param;
|
|
@@ -2,8 +2,19 @@ import { OpenApiDocument, OperationInfo, ParameterInfo, ResponseInfo, getRequest
|
|
|
2
2
|
|
|
3
3
|
//#region src/openapi/resolve.d.ts
|
|
4
4
|
/**
|
|
5
|
-
* Parse and cache an OpenAPI document. Returns cached
|
|
6
|
-
*
|
|
5
|
+
* Parse and cache an OpenAPI document. Returns the cached parse for the
|
|
6
|
+
* same object identity.
|
|
7
|
+
*
|
|
8
|
+
* Before parsing, the document is run through the version-aware
|
|
9
|
+
* normalisation pipeline (`normaliseOpenApiSchemas`) so OpenAPI 3.0.x
|
|
10
|
+
* keywords (`nullable`, `discriminator`, `example`) and Swagger 2.0
|
|
11
|
+
* documents are converted to canonical Draft 2020-12 form. The parser
|
|
12
|
+
* and downstream extractors (`getRequestBody`, `getResponses`, etc.) then
|
|
13
|
+
* observe schemas in the same form `<SchemaComponent>` does, keeping the
|
|
14
|
+
* OpenAPI components on the same pipeline as the top-level adapter.
|
|
15
|
+
*
|
|
16
|
+
* The cache is keyed by the caller-supplied document so subsequent calls
|
|
17
|
+
* with the same input bypass both normalisation and parsing.
|
|
7
18
|
*/
|
|
8
19
|
declare function getParsed(doc: Record<string, unknown>): OpenApiDocument;
|
|
9
20
|
/**
|
package/dist/openapi/resolve.mjs
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { isObject } from "../core/guards.mjs";
|
|
2
|
+
import { detectOpenApiVersion } from "../core/version.mjs";
|
|
3
|
+
import { i as normaliseOpenApiSchemas } from "../normalise-tL9FckAk.mjs";
|
|
2
4
|
import { getParameters, getRequestBody, getResponses, listOperations, parseOpenApiDocument } from "./parser.mjs";
|
|
3
5
|
//#region src/openapi/resolve.ts
|
|
4
6
|
/**
|
|
@@ -10,14 +12,28 @@ import { getParameters, getRequestBody, getResponses, listOperations, parseOpenA
|
|
|
10
12
|
*/
|
|
11
13
|
const docCache = /* @__PURE__ */ new WeakMap();
|
|
12
14
|
/**
|
|
13
|
-
* Parse and cache an OpenAPI document. Returns cached
|
|
14
|
-
*
|
|
15
|
+
* Parse and cache an OpenAPI document. Returns the cached parse for the
|
|
16
|
+
* same object identity.
|
|
17
|
+
*
|
|
18
|
+
* Before parsing, the document is run through the version-aware
|
|
19
|
+
* normalisation pipeline (`normaliseOpenApiSchemas`) so OpenAPI 3.0.x
|
|
20
|
+
* keywords (`nullable`, `discriminator`, `example`) and Swagger 2.0
|
|
21
|
+
* documents are converted to canonical Draft 2020-12 form. The parser
|
|
22
|
+
* and downstream extractors (`getRequestBody`, `getResponses`, etc.) then
|
|
23
|
+
* observe schemas in the same form `<SchemaComponent>` does, keeping the
|
|
24
|
+
* OpenAPI components on the same pipeline as the top-level adapter.
|
|
25
|
+
*
|
|
26
|
+
* The cache is keyed by the caller-supplied document so subsequent calls
|
|
27
|
+
* with the same input bypass both normalisation and parsing.
|
|
15
28
|
*/
|
|
16
29
|
function getParsed(doc) {
|
|
17
30
|
const cached = docCache.get(doc);
|
|
18
31
|
if (cached !== void 0) return cached;
|
|
19
|
-
const
|
|
32
|
+
const version = detectOpenApiVersion(doc);
|
|
33
|
+
const normalisedDoc = version !== void 0 ? normaliseOpenApiSchemas(doc, version) : doc;
|
|
34
|
+
const parsed = parseOpenApiDocument(normalisedDoc);
|
|
20
35
|
docCache.set(doc, parsed);
|
|
36
|
+
if (normalisedDoc !== doc) docCache.set(normalisedDoc, parsed);
|
|
21
37
|
return parsed;
|
|
22
38
|
}
|
|
23
39
|
/**
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { M as WalkedField, T as SchemaMeta, d as FieldOverrides, u as FieldOverride } from "../types-D_5ST7SS.mjs";
|
|
2
|
-
import { t as Diagnostic } from "../diagnostics-
|
|
2
|
+
import { t as Diagnostic } from "../diagnostics-BYk63jsC.mjs";
|
|
3
3
|
import { t as SchemaError } from "../errors-C5zRC2PU.mjs";
|
|
4
|
-
import { l as RenderProps, r as ComponentResolver } from "../renderer-
|
|
5
|
-
import { c as
|
|
4
|
+
import { l as RenderProps, r as ComponentResolver } from "../renderer-DXo-rXHJ.mjs";
|
|
5
|
+
import { c as PathOfType, l as ResolveOpenAPIRef, n as FromJSONSchema } from "../typeInference-5JiqIZ8t.mjs";
|
|
6
6
|
import { z } from "zod";
|
|
7
|
-
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
8
7
|
import { ReactNode } from "react";
|
|
8
|
+
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
9
9
|
|
|
10
10
|
//#region src/react/SchemaComponent.d.ts
|
|
11
11
|
/**
|
|
@@ -70,6 +70,13 @@ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = und
|
|
|
70
70
|
description?: string;
|
|
71
71
|
/** Instance-scoped widgets — override context and global widgets. */
|
|
72
72
|
widgets?: WidgetMap;
|
|
73
|
+
/**
|
|
74
|
+
* Prefix used for every input `id`/label `htmlFor` in this component
|
|
75
|
+
* subtree. Defaults to a per-instance value from `useId()` so multiple
|
|
76
|
+
* `<SchemaComponent>` instances on the same page never collide. Override
|
|
77
|
+
* for deterministic ids in screenshot tests.
|
|
78
|
+
*/
|
|
79
|
+
idPrefix?: string;
|
|
73
80
|
}
|
|
74
81
|
declare function SchemaComponent<T = unknown, Ref extends string | undefined = undefined>({
|
|
75
82
|
schema: schemaInput,
|
|
@@ -86,9 +93,30 @@ declare function SchemaComponent<T = unknown, Ref extends string | undefined = u
|
|
|
86
93
|
readOnly,
|
|
87
94
|
writeOnly,
|
|
88
95
|
description,
|
|
89
|
-
widgets: instanceWidgets
|
|
96
|
+
widgets: instanceWidgets,
|
|
97
|
+
idPrefix
|
|
90
98
|
}: SchemaComponentProps<T, Ref>): ReactNode;
|
|
91
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Default root-path sentinel used when no `idPrefix` is supplied AND the
|
|
101
|
+
* component is rendered outside a React tree (e.g. server-side bundling
|
|
102
|
+
* test harnesses). Production callers receive a `useId()`-derived prefix
|
|
103
|
+
* that is unique per instance.
|
|
104
|
+
*/
|
|
105
|
+
declare const ROOT_PATH = "root";
|
|
106
|
+
/**
|
|
107
|
+
* Append a child path suffix to a parent path. When the suffix is omitted
|
|
108
|
+
* (e.g. transparent wrappers like union options), the parent path is
|
|
109
|
+
* returned unchanged so the child inherits the parent's id.
|
|
110
|
+
*/
|
|
111
|
+
declare function joinPath(parent: string, suffix: string | undefined): string;
|
|
112
|
+
/**
|
|
113
|
+
* Normalise a `useId()` value into a DOM-id-safe prefix. React's `useId`
|
|
114
|
+
* returns values containing `:` characters (e.g. `«:r0:»`) which are
|
|
115
|
+
* invalid in CSS selectors. Replace any run of non-alphanumeric characters
|
|
116
|
+
* with a single hyphen and trim leading/trailing hyphens.
|
|
117
|
+
*/
|
|
118
|
+
declare function sanitisePrefix(value: string): string;
|
|
119
|
+
declare function renderField(tree: WalkedField, value: unknown, onChange: (v: unknown) => void, userResolver: ComponentResolver | undefined, renderChild: (tree: WalkedField, value: unknown, onChange: (v: unknown) => void, pathSuffix?: string) => ReactNode, path: string, instanceWidgets?: WidgetMap, contextWidgets?: WidgetMap, depth?: number): ReactNode;
|
|
92
120
|
/**
|
|
93
121
|
* Infer the schema's output type for SchemaField path inference.
|
|
94
122
|
*/
|
|
@@ -125,4 +153,4 @@ declare function SchemaField<T = unknown, Ref extends string | undefined = undef
|
|
|
125
153
|
onValidationError
|
|
126
154
|
}: SchemaFieldProps<T, Ref, P>): ReactNode;
|
|
127
155
|
//#endregion
|
|
128
|
-
export { SchemaComponent, SchemaComponentProps, SchemaField, SchemaFieldProps, SchemaProvider, WidgetMap, registerWidget, renderField };
|
|
156
|
+
export { ROOT_PATH, SchemaComponent, SchemaComponentProps, SchemaField, SchemaFieldProps, SchemaProvider, WidgetMap, joinPath, registerWidget, renderField, sanitisePrefix };
|