schema-components 1.20.0 → 1.22.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 +1 -1
- package/dist/core/adapter.d.mts +28 -4
- package/dist/core/adapter.mjs +408 -71
- package/dist/core/constraints.d.mts +2 -2
- package/dist/core/constraints.mjs +0 -2
- package/dist/core/diagnostics.d.mts +1 -1
- package/dist/core/errors.d.mts +1 -1
- package/dist/core/errors.mjs +9 -15
- package/dist/core/fieldOrder.d.mts +1 -1
- package/dist/core/formats.d.mts +22 -1
- package/dist/core/formats.mjs +21 -0
- package/dist/core/limits.d.mts +2 -0
- package/dist/core/limits.mjs +23 -0
- package/dist/core/merge.d.mts +11 -2
- package/dist/core/merge.mjs +11 -0
- package/dist/core/normalise.d.mts +36 -4
- package/dist/core/normalise.mjs +2 -2
- package/dist/core/openapi30.d.mts +24 -1
- package/dist/core/openapi30.mjs +2 -2
- package/dist/core/ref.d.mts +1 -1
- package/dist/core/ref.mjs +35 -9
- package/dist/core/renderer.d.mts +1 -1
- package/dist/core/renderer.mjs +0 -2
- package/dist/core/swagger2.d.mts +1 -1
- package/dist/core/swagger2.mjs +1 -1
- package/dist/core/typeInference.d.mts +2 -2
- package/dist/core/types.d.mts +2 -2
- package/dist/core/types.mjs +1 -4
- package/dist/core/version.d.mts +1 -1
- package/dist/core/walkBuilders.d.mts +13 -5
- package/dist/core/walkBuilders.mjs +11 -3
- package/dist/core/walker.d.mts +1 -1
- package/dist/core/walker.mjs +110 -26
- package/dist/{diagnostics-CbBPsxSt.d.mts → diagnostics-D0QCYGv0.d.mts} +1 -1
- package/dist/{errors-C2iABcn9.d.mts → errors-DpFwqs5C.d.mts} +7 -11
- package/dist/html/a11y.d.mts +2 -2
- package/dist/html/a11y.mjs +10 -3
- package/dist/html/renderToHtml.d.mts +10 -3
- package/dist/html/renderToHtml.mjs +13 -3
- package/dist/html/renderToHtmlStream.d.mts +2 -2
- package/dist/html/renderers.d.mts +2 -2
- package/dist/html/renderers.mjs +1 -6
- package/dist/html/streamRenderers.d.mts +5 -4
- package/dist/html/streamRenderers.mjs +91 -30
- package/dist/limits-Cw5QZND8.d.mts +29 -0
- package/dist/{normalise-CMMEl4cd.mjs → normalise-DVEJQmF7.mjs} +791 -141
- package/dist/openapi/ApiCallbacks.d.mts +1 -1
- package/dist/openapi/ApiLinks.d.mts +1 -1
- package/dist/openapi/ApiResponseHeaders.d.mts +1 -1
- package/dist/openapi/ApiSecurity.d.mts +1 -1
- package/dist/openapi/ApiSecurity.mjs +127 -7
- package/dist/openapi/components.d.mts +175 -21
- package/dist/openapi/components.mjs +145 -21
- package/dist/openapi/parser.d.mts +1 -1
- package/dist/openapi/parser.mjs +74 -7
- package/dist/openapi/resolve.d.mts +70 -12
- package/dist/openapi/resolve.mjs +265 -42
- package/dist/react/SchemaComponent.d.mts +100 -35
- package/dist/react/SchemaComponent.mjs +66 -24
- package/dist/react/SchemaView.d.mts +3 -3
- package/dist/react/SchemaView.mjs +2 -2
- package/dist/react/fieldPath.d.mts +1 -1
- package/dist/react/headless.d.mts +1 -1
- package/dist/react/headless.mjs +1 -2
- package/dist/react/headlessRenderers.d.mts +3 -4
- package/dist/react/headlessRenderers.mjs +11 -31
- package/dist/{ref-C8JbwfiS.d.mts → ref-D-_JBZkF.d.mts} +7 -2
- package/dist/{renderer-SOIbJBtk.d.mts → renderer-BaRlQIuN.d.mts} +2 -2
- package/dist/themes/mantine.d.mts +1 -1
- package/dist/themes/mui.d.mts +1 -1
- package/dist/themes/radix.d.mts +1 -1
- package/dist/themes/shadcn.d.mts +1 -1
- package/dist/typeInference-DkcUHfaM.d.mts +982 -0
- package/dist/{types-C9zw9wbX.d.mts → types-BrRMV0en.d.mts} +15 -12
- package/package.json +1 -3
- package/dist/typeInference-CDoD_LZ_.d.mts +0 -533
- /package/dist/{version-D-u7aMfy.d.mts → version-D2jfdX6E.d.mts} +0 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { isObject, toRecordOrUndefined } from "../core/guards.mjs";
|
|
2
|
+
import { emitDiagnostic } from "../core/diagnostics.mjs";
|
|
2
3
|
import { walk } from "../core/walker.mjs";
|
|
3
4
|
import { ApiCallbacks } from "./ApiCallbacks.mjs";
|
|
4
5
|
import { ApiLinks } from "./ApiLinks.mjs";
|
|
5
6
|
import { ApiResponseHeaders } from "./ApiResponseHeaders.mjs";
|
|
6
7
|
import { ApiSecurity } from "./ApiSecurity.mjs";
|
|
7
|
-
import { getLinks, getSecurityRequirements, getSecuritySchemes, listCallbacks } from "./parser.mjs";
|
|
8
|
+
import { getExternalDocs, getLinks, getSecurityRequirements, getSecuritySchemes, getXmlInfo, listCallbacks, listWebhooks } from "./parser.mjs";
|
|
8
9
|
import { joinPath, renderField, sanitisePrefix } from "../react/SchemaComponent.mjs";
|
|
9
|
-
import { getParsed,
|
|
10
|
+
import { getParsed, resolveOperationFromParsed, resolveParametersFromParsed, resolveRequestBodyFromParsed, resolveResponseFromParsed, toDoc } from "./resolve.mjs";
|
|
10
11
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
11
12
|
import { useId } from "react";
|
|
12
13
|
//#region src/openapi/components.tsx
|
|
@@ -23,6 +24,32 @@ import { useId } from "react";
|
|
|
23
24
|
* rendered via the walker + headless resolver directly, bypassing
|
|
24
25
|
* SchemaComponent to avoid deferred-conditional-type compatibility issues.
|
|
25
26
|
*/
|
|
27
|
+
function buildDiagnostics(onDiagnostic, strict) {
|
|
28
|
+
if (onDiagnostic === void 0 && strict !== true) return void 0;
|
|
29
|
+
const opts = {};
|
|
30
|
+
if (onDiagnostic !== void 0) opts.diagnostics = onDiagnostic;
|
|
31
|
+
if (strict === true) opts.strict = true;
|
|
32
|
+
return opts;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Coerce an `unknown` `schema` prop to a document record. Returns
|
|
36
|
+
* `undefined` when the prop is not a plain object, surfacing a
|
|
37
|
+
* `doc-not-object` diagnostic so silent "empty document" misbehaviour
|
|
38
|
+
* (the historic `toDoc` `{}` fallback) is impossible.
|
|
39
|
+
*
|
|
40
|
+
* Components MUST short-circuit when this returns `undefined` rather
|
|
41
|
+
* than rendering empty operation lists.
|
|
42
|
+
*/
|
|
43
|
+
function resolveRootDoc(doc, diagnostics) {
|
|
44
|
+
const resolved = toDoc(doc);
|
|
45
|
+
if (resolved === void 0) emitDiagnostic(diagnostics, {
|
|
46
|
+
code: "doc-not-object",
|
|
47
|
+
message: "OpenAPI document prop is not a plain object; nothing to render",
|
|
48
|
+
pointer: "",
|
|
49
|
+
detail: { received: typeof doc }
|
|
50
|
+
});
|
|
51
|
+
return resolved;
|
|
52
|
+
}
|
|
26
53
|
function noop() {}
|
|
27
54
|
function renderSchema(schema, rootDocument, options) {
|
|
28
55
|
if (!isObject(schema)) throw new Error("renderSchema received a non-object schema from the resolver.");
|
|
@@ -42,14 +69,55 @@ function renderSchema(schema, rootDocument, options) {
|
|
|
42
69
|
};
|
|
43
70
|
return renderField(tree, options.value, options.onChange ?? noop, void 0, makeRenderChild(options.rootPath), options.rootPath, options.widgets);
|
|
44
71
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Render a Schema Object or Operation Object's `externalDocs` as a
|
|
74
|
+
* simple anchor with optional descriptive text. Returns `null` when no
|
|
75
|
+
* externalDocs are attached so callers can drop it into JSX without an
|
|
76
|
+
* extra guard.
|
|
77
|
+
*/
|
|
78
|
+
function ExternalDocsLink({ externalDocs }) {
|
|
79
|
+
if (externalDocs === void 0) return null;
|
|
80
|
+
return /* @__PURE__ */ jsx("p", {
|
|
81
|
+
"data-external-docs": true,
|
|
82
|
+
children: /* @__PURE__ */ jsx("a", {
|
|
83
|
+
href: externalDocs.url,
|
|
84
|
+
children: externalDocs.description ?? externalDocs.url
|
|
85
|
+
})
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Render a Schema Object's `xml` metadata as a footnote. The library
|
|
90
|
+
* does not render XML payloads natively, but the metadata still
|
|
91
|
+
* carries author intent (namespaces, element names, wrapping rules).
|
|
92
|
+
* Surface it so consumers can audit the dropped feature without
|
|
93
|
+
* losing the underlying information.
|
|
94
|
+
*/
|
|
95
|
+
function SchemaXmlFootnote({ xml }) {
|
|
96
|
+
if (xml === void 0) return null;
|
|
97
|
+
return /* @__PURE__ */ jsx("aside", {
|
|
98
|
+
"data-schema-xml": true,
|
|
99
|
+
children: /* @__PURE__ */ jsxs("small", { children: [
|
|
100
|
+
"XML representation",
|
|
101
|
+
xml.name !== void 0 && ` — name: ${xml.name}`,
|
|
102
|
+
xml.namespace !== void 0 && ` — namespace: ${xml.namespace}`,
|
|
103
|
+
xml.prefix !== void 0 && ` — prefix: ${xml.prefix}`,
|
|
104
|
+
xml.attribute && " — attribute",
|
|
105
|
+
xml.wrapped && " — wrapped"
|
|
106
|
+
] })
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBodyChange, responseValue, meta, requestBodyFields, widgets, onDiagnostic, strict }) {
|
|
110
|
+
const diagnostics = buildDiagnostics(onDiagnostic, strict);
|
|
111
|
+
const instancePrefix = sanitisePrefix(useId());
|
|
112
|
+
const rootDoc = resolveRootDoc(doc, diagnostics);
|
|
113
|
+
if (rootDoc === void 0) return null;
|
|
114
|
+
const parsed = getParsed(rootDoc, diagnostics);
|
|
115
|
+
const resolved = resolveOperationFromParsed(parsed, path, method, diagnostics);
|
|
49
116
|
const securityReqs = getSecurityRequirements(parsed, path, method);
|
|
50
117
|
const securitySchemes = getSecuritySchemes(parsed);
|
|
51
118
|
const callbacks = listCallbacks(parsed, path, method);
|
|
52
|
-
const
|
|
119
|
+
const operationExternalDocs = getExternalDocs(resolved.operation.operation);
|
|
120
|
+
const requestBodyXml = resolved.requestBody?.schema !== void 0 ? getXmlInfo(resolved.requestBody.schema) : void 0;
|
|
53
121
|
return /* @__PURE__ */ jsxs("section", {
|
|
54
122
|
"data-operation": `${method.toUpperCase()} ${path}`,
|
|
55
123
|
children: [
|
|
@@ -57,6 +125,7 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
|
|
|
57
125
|
operation: resolved.operation,
|
|
58
126
|
pathItem: resolved.pathItem
|
|
59
127
|
}),
|
|
128
|
+
/* @__PURE__ */ jsx(ExternalDocsLink, { externalDocs: operationExternalDocs }),
|
|
60
129
|
/* @__PURE__ */ jsx(ApiSecurity, {
|
|
61
130
|
requirements: securityReqs,
|
|
62
131
|
schemes: securitySchemes
|
|
@@ -91,7 +160,8 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
|
|
|
91
160
|
meta,
|
|
92
161
|
widgets,
|
|
93
162
|
rootPath: joinPath(instancePrefix, "requestBody")
|
|
94
|
-
})
|
|
163
|
+
}),
|
|
164
|
+
/* @__PURE__ */ jsx(SchemaXmlFootnote, { xml: requestBodyXml })
|
|
95
165
|
]
|
|
96
166
|
}),
|
|
97
167
|
resolved.responses.length > 0 && /* @__PURE__ */ jsxs("section", {
|
|
@@ -99,6 +169,7 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
|
|
|
99
169
|
children: [/* @__PURE__ */ jsx("h4", { children: "Responses" }), resolved.responses.map((response) => /* @__PURE__ */ jsx(ResponseCard, {
|
|
100
170
|
response,
|
|
101
171
|
rootDoc,
|
|
172
|
+
parsed,
|
|
102
173
|
value: responseValue,
|
|
103
174
|
meta,
|
|
104
175
|
widgets,
|
|
@@ -110,10 +181,12 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
|
|
|
110
181
|
]
|
|
111
182
|
});
|
|
112
183
|
}
|
|
113
|
-
function ApiParameters({ schema: doc, path, method, meta, overrides, widgets }) {
|
|
114
|
-
const
|
|
115
|
-
const params = resolveParameters(rootDoc, path, method);
|
|
184
|
+
function ApiParameters({ schema: doc, path, method, meta, overrides, widgets, onDiagnostic, strict }) {
|
|
185
|
+
const diagnostics = buildDiagnostics(onDiagnostic, strict);
|
|
116
186
|
const instancePrefix = sanitisePrefix(useId());
|
|
187
|
+
const rootDoc = resolveRootDoc(doc, diagnostics);
|
|
188
|
+
if (rootDoc === void 0) return null;
|
|
189
|
+
const params = resolveParametersFromParsed(getParsed(rootDoc, diagnostics), path, method);
|
|
117
190
|
if (params.length === 0) return null;
|
|
118
191
|
return /* @__PURE__ */ jsxs("section", {
|
|
119
192
|
"data-parameters": true,
|
|
@@ -127,11 +200,14 @@ function ApiParameters({ schema: doc, path, method, meta, overrides, widgets })
|
|
|
127
200
|
})]
|
|
128
201
|
});
|
|
129
202
|
}
|
|
130
|
-
function ApiRequestBody({ schema: doc, path, method, value, onChange, meta, fields, widgets }) {
|
|
131
|
-
const
|
|
132
|
-
const requestBody = resolveRequestBody(rootDoc, path, method);
|
|
203
|
+
function ApiRequestBody({ schema: doc, path, method, value, onChange, meta, fields, widgets, onDiagnostic, strict }) {
|
|
204
|
+
const diagnostics = buildDiagnostics(onDiagnostic, strict);
|
|
133
205
|
const instancePrefix = sanitisePrefix(useId());
|
|
206
|
+
const rootDoc = resolveRootDoc(doc, diagnostics);
|
|
207
|
+
if (rootDoc === void 0) return null;
|
|
208
|
+
const requestBody = resolveRequestBodyFromParsed(getParsed(rootDoc, diagnostics), path, method);
|
|
134
209
|
if (requestBody?.schema === void 0) return null;
|
|
210
|
+
const requestBodyXml = getXmlInfo(requestBody.schema);
|
|
135
211
|
return /* @__PURE__ */ jsxs("section", {
|
|
136
212
|
"data-request-body": true,
|
|
137
213
|
children: [
|
|
@@ -151,14 +227,18 @@ function ApiRequestBody({ schema: doc, path, method, value, onChange, meta, fiel
|
|
|
151
227
|
meta,
|
|
152
228
|
widgets,
|
|
153
229
|
rootPath: instancePrefix
|
|
154
|
-
})
|
|
230
|
+
}),
|
|
231
|
+
/* @__PURE__ */ jsx(SchemaXmlFootnote, { xml: requestBodyXml })
|
|
155
232
|
]
|
|
156
233
|
});
|
|
157
234
|
}
|
|
158
|
-
function ApiResponse({ schema: doc, path, method, status, value, meta, fields, widgets }) {
|
|
159
|
-
const
|
|
160
|
-
const response = resolveResponse(rootDoc, path, method, status);
|
|
235
|
+
function ApiResponse({ schema: doc, path, method, status, value, meta, fields, widgets, onDiagnostic, strict }) {
|
|
236
|
+
const diagnostics = buildDiagnostics(onDiagnostic, strict);
|
|
161
237
|
const instancePrefix = sanitisePrefix(useId());
|
|
238
|
+
const rootDoc = resolveRootDoc(doc, diagnostics);
|
|
239
|
+
if (rootDoc === void 0) return null;
|
|
240
|
+
const parsed = getParsed(rootDoc, diagnostics);
|
|
241
|
+
const response = resolveResponseFromParsed(parsed, path, method, status);
|
|
162
242
|
if (response.schema === void 0) return /* @__PURE__ */ jsxs("div", {
|
|
163
243
|
"data-status": status,
|
|
164
244
|
children: [
|
|
@@ -170,6 +250,7 @@ function ApiResponse({ schema: doc, path, method, status, value, meta, fields, w
|
|
|
170
250
|
return /* @__PURE__ */ jsx(ResponseCard, {
|
|
171
251
|
response,
|
|
172
252
|
rootDoc,
|
|
253
|
+
parsed,
|
|
173
254
|
value,
|
|
174
255
|
fields,
|
|
175
256
|
meta,
|
|
@@ -179,6 +260,49 @@ function ApiResponse({ schema: doc, path, method, status, value, meta, fields, w
|
|
|
179
260
|
idPrefix: instancePrefix
|
|
180
261
|
});
|
|
181
262
|
}
|
|
263
|
+
function ApiWebhook({ schema: doc, name, widgets, meta, onDiagnostic, strict }) {
|
|
264
|
+
const diagnostics = buildDiagnostics(onDiagnostic, strict);
|
|
265
|
+
const instancePrefix = sanitisePrefix(useId());
|
|
266
|
+
const rootDoc = resolveRootDoc(doc, diagnostics);
|
|
267
|
+
if (rootDoc === void 0) return null;
|
|
268
|
+
const webhook = listWebhooks(getParsed(rootDoc, diagnostics)).find((w) => w.name === name);
|
|
269
|
+
if (webhook === void 0) return null;
|
|
270
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
271
|
+
"data-webhook": name,
|
|
272
|
+
"data-instance": instancePrefix,
|
|
273
|
+
children: [/* @__PURE__ */ jsxs("h3", { children: ["Webhook: ", name] }), webhook.operations.map((op) => {
|
|
274
|
+
const opProps = {
|
|
275
|
+
schema: rootDoc,
|
|
276
|
+
path: name,
|
|
277
|
+
method: op.method
|
|
278
|
+
};
|
|
279
|
+
if (widgets !== void 0) opProps.widgets = widgets;
|
|
280
|
+
if (meta !== void 0) opProps.meta = meta;
|
|
281
|
+
return /* @__PURE__ */ jsx(ApiOperation, { ...opProps }, `${name}-${op.method}`);
|
|
282
|
+
})]
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
function ApiWebhooks({ schema: doc, widgets, meta, onDiagnostic, strict }) {
|
|
286
|
+
const diagnostics = buildDiagnostics(onDiagnostic, strict);
|
|
287
|
+
const instancePrefix = sanitisePrefix(useId());
|
|
288
|
+
const rootDoc = resolveRootDoc(doc, diagnostics);
|
|
289
|
+
if (rootDoc === void 0) return null;
|
|
290
|
+
const webhooks = listWebhooks(getParsed(rootDoc, diagnostics));
|
|
291
|
+
if (webhooks.length === 0) return null;
|
|
292
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
293
|
+
"data-webhooks": true,
|
|
294
|
+
"data-instance": instancePrefix,
|
|
295
|
+
children: [/* @__PURE__ */ jsx("h2", { children: "Webhooks" }), webhooks.map((webhook) => {
|
|
296
|
+
const props = {
|
|
297
|
+
schema: rootDoc,
|
|
298
|
+
name: webhook.name
|
|
299
|
+
};
|
|
300
|
+
if (widgets !== void 0) props.widgets = widgets;
|
|
301
|
+
if (meta !== void 0) props.meta = meta;
|
|
302
|
+
return /* @__PURE__ */ jsx(ApiWebhook, { ...props }, webhook.name);
|
|
303
|
+
})]
|
|
304
|
+
});
|
|
305
|
+
}
|
|
182
306
|
function OperationHeader({ operation, pathItem }) {
|
|
183
307
|
return /* @__PURE__ */ jsxs("header", { children: [
|
|
184
308
|
(pathItem.summary !== void 0 || pathItem.description !== void 0) && /* @__PURE__ */ jsxs("div", {
|
|
@@ -227,7 +351,7 @@ function ParameterList({ parameters, rootDoc, overrides, meta, widgets, idPrefix
|
|
|
227
351
|
]
|
|
228
352
|
}, param.name)) });
|
|
229
353
|
}
|
|
230
|
-
function ResponseCard({ response, rootDoc, value, fields, meta, widgets, path, method, idPrefix }) {
|
|
354
|
+
function ResponseCard({ response, rootDoc, parsed, value, fields, meta, widgets, path, method, idPrefix }) {
|
|
231
355
|
if (response.schema === void 0) return /* @__PURE__ */ jsxs("div", {
|
|
232
356
|
"data-status": response.statusCode,
|
|
233
357
|
children: [
|
|
@@ -237,7 +361,7 @@ function ResponseCard({ response, rootDoc, value, fields, meta, widgets, path, m
|
|
|
237
361
|
]
|
|
238
362
|
});
|
|
239
363
|
let links = [];
|
|
240
|
-
if (path !== void 0 && method !== void 0) links = getLinks(
|
|
364
|
+
if (path !== void 0 && method !== void 0) links = getLinks(parsed, path, method, response.statusCode);
|
|
241
365
|
return /* @__PURE__ */ jsxs("div", {
|
|
242
366
|
"data-status": response.statusCode,
|
|
243
367
|
children: [
|
|
@@ -283,4 +407,4 @@ function extractRootMetaFromSchema(jsonSchema) {
|
|
|
283
407
|
return Object.keys(meta).length > 0 ? meta : void 0;
|
|
284
408
|
}
|
|
285
409
|
//#endregion
|
|
286
|
-
export { ApiOperation, ApiParameters, ApiRequestBody, ApiResponse };
|
|
410
|
+
export { ApiOperation, ApiParameters, ApiRequestBody, ApiResponse, ApiWebhook, ApiWebhooks };
|
package/dist/openapi/parser.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getProperty, isObject } from "../core/guards.mjs";
|
|
2
|
+
import "../core/limits.mjs";
|
|
2
3
|
import { isPrototypePollutingKey } from "../core/uri.mjs";
|
|
3
4
|
//#region src/openapi/parser.ts
|
|
4
5
|
function getString(value, key) {
|
|
@@ -44,11 +45,27 @@ const METHODS = [
|
|
|
44
45
|
* (OpenAPI 3.1) if present. Returns `undefined` when the value is not a
|
|
45
46
|
* path item, the ref is malformed, or the target does not resolve.
|
|
46
47
|
*/
|
|
48
|
+
/**
|
|
49
|
+
* Follow Path Item Object `$ref` chains (up to MAX_PATH_ITEM_REF_HOPS).
|
|
50
|
+
* Returns the resolved Path Item, or `undefined` when the chain cycles,
|
|
51
|
+
* exceeds the cap, or any intermediate ref fails to resolve. The
|
|
52
|
+
* detailed diagnostics for cycle and depth-cap are emitted by the
|
|
53
|
+
* mirroring resolver in `resolve.ts` — this parser-side resolver simply
|
|
54
|
+
* stops walking.
|
|
55
|
+
*/
|
|
47
56
|
function resolvePathItem(parsed, pathItem) {
|
|
48
57
|
if (!isObject(pathItem)) return void 0;
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
58
|
+
const visited = /* @__PURE__ */ new Set();
|
|
59
|
+
let current = pathItem;
|
|
60
|
+
for (let hop = 0; hop < 8; hop++) {
|
|
61
|
+
const ref = getString(current, "$ref");
|
|
62
|
+
if (ref === void 0) return current;
|
|
63
|
+
if (visited.has(ref)) return void 0;
|
|
64
|
+
visited.add(ref);
|
|
65
|
+
const target = resolveRefInDoc(parsed.doc, ref);
|
|
66
|
+
if (target === void 0) return void 0;
|
|
67
|
+
current = target;
|
|
68
|
+
}
|
|
52
69
|
}
|
|
53
70
|
function lookupPathItem(parsed, path) {
|
|
54
71
|
const resolved = resolvePathItem(parsed, getProperty(getProperty(parsed.doc, "paths"), path));
|
|
@@ -120,8 +137,10 @@ function resolveParam(doc, param) {
|
|
|
120
137
|
return param;
|
|
121
138
|
}
|
|
122
139
|
function getRequestBody(parsed, path, method) {
|
|
123
|
-
const
|
|
124
|
-
if (!isObject(
|
|
140
|
+
const requestBodyRaw = getProperty(getProperty(lookupPathItem(parsed, path), method), "requestBody");
|
|
141
|
+
if (!isObject(requestBodyRaw)) return void 0;
|
|
142
|
+
const requestBody = resolveWrapperRef(parsed.doc, requestBodyRaw);
|
|
143
|
+
if (requestBody === void 0) return void 0;
|
|
125
144
|
const content = getProperty(requestBody, "content");
|
|
126
145
|
if (!isObject(content)) return {
|
|
127
146
|
required: getProperty(requestBody, "required") === true,
|
|
@@ -142,8 +161,10 @@ function getResponses(parsed, path, method) {
|
|
|
142
161
|
const responses = getProperty(getProperty(lookupPathItem(parsed, path), method), "responses");
|
|
143
162
|
if (!isObject(responses)) return [];
|
|
144
163
|
const result = [];
|
|
145
|
-
for (const [statusCode,
|
|
146
|
-
if (!isObject(
|
|
164
|
+
for (const [statusCode, responseRaw] of Object.entries(responses)) {
|
|
165
|
+
if (!isObject(responseRaw)) continue;
|
|
166
|
+
const response = resolveWrapperRef(parsed.doc, responseRaw);
|
|
167
|
+
if (response === void 0) continue;
|
|
147
168
|
const content = getProperty(response, "content");
|
|
148
169
|
const contentTypes = isObject(content) ? Object.keys(content) : [];
|
|
149
170
|
const schema = isObject(content) ? extractSchemaFromContent(content) : void 0;
|
|
@@ -158,15 +179,61 @@ function getResponses(parsed, path, method) {
|
|
|
158
179
|
}
|
|
159
180
|
return result;
|
|
160
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Resolve a single-hop `$ref` on a wrapper object — Response Object,
|
|
184
|
+
* Request Body Object, etc. — against the document root. Returns the
|
|
185
|
+
* referenced node when the wrapper is a `$ref`, the wrapper itself when
|
|
186
|
+
* it has no `$ref`, or `undefined` when the `$ref` is malformed or
|
|
187
|
+
* cannot be resolved (so the caller skips the entry rather than reading
|
|
188
|
+
* stale fields from the bare `{ $ref }` envelope).
|
|
189
|
+
*/
|
|
190
|
+
function resolveWrapperRef(doc, wrapper) {
|
|
191
|
+
const ref = getString(wrapper, "$ref");
|
|
192
|
+
if (ref === void 0) return wrapper;
|
|
193
|
+
return resolveRefInDoc(doc, ref);
|
|
194
|
+
}
|
|
161
195
|
function extractSchemaFromContent(content) {
|
|
162
196
|
const jsonSchema = getProperty(getProperty(content, "application/json"), "schema");
|
|
163
197
|
if (isObject(jsonSchema)) return jsonSchema;
|
|
198
|
+
for (const [mediaType, mediaObj] of Object.entries(content)) {
|
|
199
|
+
if (!isJsonSuffixMediaType(mediaType)) continue;
|
|
200
|
+
if (!isObject(mediaObj)) continue;
|
|
201
|
+
const schema = getProperty(mediaObj, "schema");
|
|
202
|
+
if (isObject(schema)) return schema;
|
|
203
|
+
}
|
|
164
204
|
for (const mediaType of Object.values(content)) {
|
|
165
205
|
if (!isObject(mediaType)) continue;
|
|
166
206
|
const schema = getProperty(mediaType, "schema");
|
|
167
207
|
if (isObject(schema)) return schema;
|
|
168
208
|
}
|
|
169
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Detect RFC 6839 structured-syntax-suffix media types that encode JSON.
|
|
212
|
+
* Matches `application/<anything>+json`, optionally with parameters
|
|
213
|
+
* (`; charset=utf-8`). Excludes the literal `application/json`, which
|
|
214
|
+
* the caller checks separately to preserve preference order.
|
|
215
|
+
*/
|
|
216
|
+
function isJsonSuffixMediaType(mediaType) {
|
|
217
|
+
const lower = mediaType.toLowerCase();
|
|
218
|
+
if (lower === "application/json") return false;
|
|
219
|
+
const baseType = lower.split(";", 1)[0]?.trim() ?? "";
|
|
220
|
+
return baseType.startsWith("application/") && baseType.endsWith("+json");
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Resolve an in-document `$ref` against the supplied doc root.
|
|
224
|
+
*
|
|
225
|
+
* Limitation — cross-Schema-Object relative refs: refs that do NOT
|
|
226
|
+
* start with `#/` are not resolved here. The `normaliseOpenApiSchemas`
|
|
227
|
+
* pipeline (see `resolveRelativeRefs` in `core/normalise.ts`) rewrites
|
|
228
|
+
* relative refs WITHIN a Schema Object using that schema's `$id` base
|
|
229
|
+
* URI, but it does not currently model `$id` scopes that span Schema
|
|
230
|
+
* Object boundaries (e.g. a sibling component schema with its own
|
|
231
|
+
* `$id` that another schema's relative ref targets). Such refs survive
|
|
232
|
+
* normalisation unchanged and fall through this function returning
|
|
233
|
+
* `undefined`. `resolve.ts:detectUnsupportedCrossSchemaRefs` walks the
|
|
234
|
+
* normalised doc and emits `cross-schema-relative-ref-unsupported` per
|
|
235
|
+
* offending ref so consumers notice the silent failure.
|
|
236
|
+
*/
|
|
170
237
|
function resolveRefInDoc(doc, ref) {
|
|
171
238
|
if (!ref.startsWith("#/")) return void 0;
|
|
172
239
|
const parts = ref.slice(2).split("/");
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { i as DiagnosticsOptions } from "../diagnostics-D0QCYGv0.mjs";
|
|
1
2
|
import { OpenApiDocument, OperationInfo, ParameterInfo, ResponseInfo, getRequestBody } from "./parser.mjs";
|
|
2
3
|
|
|
3
4
|
//#region src/openapi/resolve.d.ts
|
|
@@ -14,15 +15,31 @@ import { OpenApiDocument, OperationInfo, ParameterInfo, ResponseInfo, getRequest
|
|
|
14
15
|
* same form `<SchemaComponent>` does, keeping the OpenAPI components on
|
|
15
16
|
* the same pipeline as the top-level adapter.
|
|
16
17
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
18
|
+
* When `diagnostics` is supplied, normalisation events
|
|
19
|
+
* (`duplicate-body-parameter`, `dropped-swagger-feature`,
|
|
20
|
+
* `unknown-json-schema-dialect`, `divisible-by-conflict`,
|
|
21
|
+
* `relative-ref-resolved`, etc.) are forwarded to the sink. Passing
|
|
22
|
+
* diagnostics also bypasses the cache so each call observes the
|
|
23
|
+
* normalisation pipeline running against the supplied sink — caching
|
|
24
|
+
* would silently swallow every emission after the first.
|
|
25
|
+
*
|
|
26
|
+
* The cache is keyed by the caller-supplied document so subsequent
|
|
27
|
+
* cache-eligible calls with the same input bypass both normalisation
|
|
28
|
+
* and parsing.
|
|
19
29
|
*/
|
|
20
|
-
declare function getParsed(doc: Record<string, unknown
|
|
30
|
+
declare function getParsed(doc: Record<string, unknown>, diagnostics?: DiagnosticsOptions): OpenApiDocument;
|
|
21
31
|
/**
|
|
22
|
-
* Coerce an unknown value to a record, returning
|
|
23
|
-
*
|
|
32
|
+
* Coerce an unknown value to a record, returning `undefined` when the
|
|
33
|
+
* value is not a plain object. Callers MUST handle the `undefined` case
|
|
34
|
+
* explicitly — typically by rendering a "doc not an object" diagnostic
|
|
35
|
+
* and short-circuiting, never by silently substituting `{}`.
|
|
36
|
+
*
|
|
37
|
+
* A previous implementation fell back to `{}` for non-objects, which
|
|
38
|
+
* masked configuration mistakes (passing a string, `null`, an array, or
|
|
39
|
+
* `undefined` as the OpenAPI document) as an empty document with no
|
|
40
|
+
* operations.
|
|
24
41
|
*/
|
|
25
|
-
declare function toDoc(value: unknown): Record<string, unknown
|
|
42
|
+
declare function toDoc(value: unknown): Record<string, unknown> | undefined;
|
|
26
43
|
/**
|
|
27
44
|
* Path-Item-level metadata. OpenAPI 3.1 added `summary` and `description`
|
|
28
45
|
* to Path Item Objects alongside the existing operation-level fields.
|
|
@@ -39,26 +56,67 @@ interface ResolvedOperation {
|
|
|
39
56
|
requestBody: ReturnType<typeof getRequestBody>;
|
|
40
57
|
responses: ResponseInfo[];
|
|
41
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Resolve an operation against an already-parsed document. Throws if
|
|
61
|
+
* the operation is not found.
|
|
62
|
+
*
|
|
63
|
+
* Used by callers that have already obtained a parsed document via
|
|
64
|
+
* {@link getParsed} — most importantly the React components, which
|
|
65
|
+
* supply `diagnostics` to `getParsed` and must avoid re-running the
|
|
66
|
+
* normalisation pipeline (every re-run would emit each diagnostic
|
|
67
|
+
* again into the sink).
|
|
68
|
+
*/
|
|
69
|
+
declare function resolveOperationFromParsed(parsed: OpenApiDocument, path: string, method: string, diagnostics?: DiagnosticsOptions): ResolvedOperation;
|
|
42
70
|
/**
|
|
43
71
|
* Resolve an operation from an OpenAPI document by path and method.
|
|
44
72
|
* Throws if the operation is not found.
|
|
73
|
+
*
|
|
74
|
+
* `diagnostics` is forwarded to {@link getParsed} so normalisation
|
|
75
|
+
* events surface to the caller's sink.
|
|
76
|
+
*/
|
|
77
|
+
declare function resolveOperation(doc: Record<string, unknown>, path: string, method: string, diagnostics?: DiagnosticsOptions): ResolvedOperation;
|
|
78
|
+
/**
|
|
79
|
+
* Resolve parameters against an already-parsed document. See
|
|
80
|
+
* {@link resolveOperationFromParsed} for the rationale.
|
|
45
81
|
*/
|
|
46
|
-
declare function
|
|
82
|
+
declare function resolveParametersFromParsed(parsed: OpenApiDocument, path: string, method: string): ParameterInfo[];
|
|
47
83
|
/**
|
|
48
84
|
* Resolve parameters for an operation. Returns empty array if none.
|
|
85
|
+
*
|
|
86
|
+
* `diagnostics` is forwarded to {@link getParsed} so normalisation
|
|
87
|
+
* events surface to the caller's sink.
|
|
88
|
+
*/
|
|
89
|
+
declare function resolveParameters(doc: Record<string, unknown>, path: string, method: string, diagnostics?: DiagnosticsOptions): ParameterInfo[];
|
|
90
|
+
/**
|
|
91
|
+
* Resolve a request body against an already-parsed document. See
|
|
92
|
+
* {@link resolveOperationFromParsed} for the rationale.
|
|
49
93
|
*/
|
|
50
|
-
declare function
|
|
94
|
+
declare function resolveRequestBodyFromParsed(parsed: OpenApiDocument, path: string, method: string): ReturnType<typeof getRequestBody>;
|
|
51
95
|
/**
|
|
52
96
|
* Resolve request body for an operation. Returns undefined if none.
|
|
97
|
+
*
|
|
98
|
+
* `diagnostics` is forwarded to {@link getParsed} so normalisation
|
|
99
|
+
* events surface to the caller's sink.
|
|
53
100
|
*/
|
|
54
|
-
declare function resolveRequestBody(doc: Record<string, unknown>, path: string, method: string): ReturnType<typeof getRequestBody>;
|
|
101
|
+
declare function resolveRequestBody(doc: Record<string, unknown>, path: string, method: string, diagnostics?: DiagnosticsOptions): ReturnType<typeof getRequestBody>;
|
|
102
|
+
/**
|
|
103
|
+
* Resolve a specific response against an already-parsed document. See
|
|
104
|
+
* {@link resolveOperationFromParsed} for the rationale.
|
|
105
|
+
*/
|
|
106
|
+
declare function resolveResponseFromParsed(parsed: OpenApiDocument, path: string, method: string, statusCode: string): ResponseInfo;
|
|
55
107
|
/**
|
|
56
108
|
* Resolve a specific response by status code. Throws if not found.
|
|
109
|
+
*
|
|
110
|
+
* `diagnostics` is forwarded to {@link getParsed} so normalisation
|
|
111
|
+
* events surface to the caller's sink.
|
|
57
112
|
*/
|
|
58
|
-
declare function resolveResponse(doc: Record<string, unknown>, path: string, method: string, statusCode: string): ResponseInfo;
|
|
113
|
+
declare function resolveResponse(doc: Record<string, unknown>, path: string, method: string, statusCode: string, diagnostics?: DiagnosticsOptions): ResponseInfo;
|
|
59
114
|
/**
|
|
60
115
|
* Resolve all responses for an operation.
|
|
116
|
+
*
|
|
117
|
+
* `diagnostics` is forwarded to {@link getParsed} so normalisation
|
|
118
|
+
* events surface to the caller's sink.
|
|
61
119
|
*/
|
|
62
|
-
declare function resolveResponses(doc: Record<string, unknown>, path: string, method: string): ResponseInfo[];
|
|
120
|
+
declare function resolveResponses(doc: Record<string, unknown>, path: string, method: string, diagnostics?: DiagnosticsOptions): ResponseInfo[];
|
|
63
121
|
//#endregion
|
|
64
|
-
export { PathItemInfo, ResolvedOperation, getParsed, resolveOperation, resolveParameters, resolveRequestBody, resolveResponse, resolveResponses, toDoc };
|
|
122
|
+
export { PathItemInfo, ResolvedOperation, getParsed, resolveOperation, resolveOperationFromParsed, resolveParameters, resolveParametersFromParsed, resolveRequestBody, resolveRequestBodyFromParsed, resolveResponse, resolveResponseFromParsed, resolveResponses, toDoc };
|