schema-components 1.19.0 → 1.21.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 +10 -3
- package/dist/core/adapter.mjs +237 -31
- 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 +10 -8
- package/dist/core/fieldOrder.d.mts +1 -1
- package/dist/core/formats.d.mts +21 -14
- package/dist/core/formats.mjs +88 -4
- package/dist/core/merge.d.mts +11 -2
- package/dist/core/merge.mjs +11 -0
- package/dist/core/normalise.d.mts +9 -3
- package/dist/core/normalise.mjs +1 -1
- 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 +34 -9
- package/dist/core/renderer.d.mts +1 -1
- 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 +1 -1
- package/dist/core/uri.d.mts +41 -0
- package/dist/core/uri.mjs +76 -0
- package/dist/core/version.d.mts +2 -2
- package/dist/core/version.mjs +25 -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 +80 -26
- package/dist/{diagnostics-VgEKI_Ct.d.mts → diagnostics-CbBPsxSt.d.mts} +1 -1
- package/dist/{errors-CnGjT1cg.d.mts → errors-QEwOtQAA.d.mts} +8 -5
- package/dist/html/a11y.d.mts +2 -2
- package/dist/html/renderToHtml.d.mts +2 -2
- package/dist/html/renderToHtmlStream.d.mts +2 -2
- package/dist/html/renderers.d.mts +2 -2
- package/dist/html/renderers.mjs +9 -2
- package/dist/html/streamRenderers.d.mts +2 -2
- package/dist/{normalise-C0ofw3W6.mjs → normalise-DaSrnr8g.mjs} +574 -40
- 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 +113 -7
- package/dist/openapi/bundle.mjs +2 -0
- package/dist/openapi/components.d.mts +32 -10
- package/dist/openapi/components.mjs +37 -16
- package/dist/openapi/parser.d.mts +1 -1
- package/dist/openapi/parser.mjs +41 -4
- package/dist/openapi/resolve.d.mts +70 -9
- package/dist/openapi/resolve.mjs +124 -24
- package/dist/react/SchemaComponent.d.mts +21 -9
- package/dist/react/SchemaComponent.mjs +32 -4
- package/dist/react/SchemaView.d.mts +3 -3
- package/dist/react/fieldPath.d.mts +1 -1
- package/dist/react/headless.d.mts +1 -1
- package/dist/react/headlessRenderers.d.mts +2 -2
- package/dist/react/headlessRenderers.mjs +18 -6
- package/dist/{ref-Bb43ZURY.d.mts → ref-si8ViYun.d.mts} +7 -2
- package/dist/{renderer-BQqiXUYP.d.mts → renderer-DI6ZYf7a.d.mts} +1 -1
- 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-Bxw3NOG1.d.mts +647 -0
- package/dist/{types-D_5ST7SS.d.mts → types-BnxPEElk.d.mts} +18 -2
- package/dist/{version-XNH7PRGP.d.mts → version-D-u7aMfy.d.mts} +36 -1
- package/package.json +1 -1
- package/dist/typeInference-5JiqIZ8t.d.mts +0 -388
|
@@ -1,5 +1,46 @@
|
|
|
1
|
+
import { isObject } from "../core/guards.mjs";
|
|
1
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
3
|
//#region src/openapi/ApiSecurity.tsx
|
|
4
|
+
/**
|
|
5
|
+
* The four OAuth 2 flow keys defined by OpenAPI 3.x. Listed in the
|
|
6
|
+
* canonical specification order so renders are deterministic.
|
|
7
|
+
*/
|
|
8
|
+
const OAUTH_FLOW_KEYS = [
|
|
9
|
+
"implicit",
|
|
10
|
+
"password",
|
|
11
|
+
"clientCredentials",
|
|
12
|
+
"authorizationCode"
|
|
13
|
+
];
|
|
14
|
+
function readString(source, key) {
|
|
15
|
+
const value = source[key];
|
|
16
|
+
return typeof value === "string" ? value : void 0;
|
|
17
|
+
}
|
|
18
|
+
function readScopes(source) {
|
|
19
|
+
const scopes = source.scopes;
|
|
20
|
+
const result = /* @__PURE__ */ new Map();
|
|
21
|
+
if (!isObject(scopes)) return result;
|
|
22
|
+
for (const [name, description] of Object.entries(scopes)) {
|
|
23
|
+
if (typeof description !== "string") continue;
|
|
24
|
+
result.set(name, description);
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
function extractFlows(flows) {
|
|
29
|
+
if (flows === void 0) return [];
|
|
30
|
+
const result = [];
|
|
31
|
+
for (const name of OAUTH_FLOW_KEYS) {
|
|
32
|
+
const flow = flows[name];
|
|
33
|
+
if (!isObject(flow)) continue;
|
|
34
|
+
result.push({
|
|
35
|
+
name,
|
|
36
|
+
authorizationUrl: readString(flow, "authorizationUrl"),
|
|
37
|
+
tokenUrl: readString(flow, "tokenUrl"),
|
|
38
|
+
refreshUrl: readString(flow, "refreshUrl"),
|
|
39
|
+
scopes: readScopes(flow)
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
3
44
|
function ApiSecurity({ requirements, schemes }) {
|
|
4
45
|
if (requirements.length === 0) return null;
|
|
5
46
|
return /* @__PURE__ */ jsxs("section", {
|
|
@@ -13,13 +54,7 @@ function ApiSecurity({ requirements, schemes }) {
|
|
|
13
54
|
"data-security-name": true,
|
|
14
55
|
children: req.name
|
|
15
56
|
}),
|
|
16
|
-
scheme !== void 0 && /* @__PURE__ */
|
|
17
|
-
"data-security-type": true,
|
|
18
|
-
children: scheme.type
|
|
19
|
-
}), scheme.description && /* @__PURE__ */ jsx("span", {
|
|
20
|
-
"data-security-description": true,
|
|
21
|
-
children: scheme.description
|
|
22
|
-
})] }),
|
|
57
|
+
scheme !== void 0 && /* @__PURE__ */ jsx(SchemeDetails, { scheme }),
|
|
23
58
|
req.scopes.length > 0 && /* @__PURE__ */ jsx("span", {
|
|
24
59
|
"data-security-scopes": true,
|
|
25
60
|
children: req.scopes.join(", ")
|
|
@@ -29,5 +64,76 @@ function ApiSecurity({ requirements, schemes }) {
|
|
|
29
64
|
})]
|
|
30
65
|
});
|
|
31
66
|
}
|
|
67
|
+
function SchemeDetails({ scheme }) {
|
|
68
|
+
const flows = extractFlows(scheme.flows);
|
|
69
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
70
|
+
scheme.type !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
71
|
+
"data-security-type": true,
|
|
72
|
+
children: scheme.type
|
|
73
|
+
}),
|
|
74
|
+
scheme.description !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
75
|
+
"data-security-description": true,
|
|
76
|
+
children: scheme.description
|
|
77
|
+
}),
|
|
78
|
+
scheme.scheme !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
79
|
+
"data-security-http-scheme": true,
|
|
80
|
+
children: scheme.scheme
|
|
81
|
+
}),
|
|
82
|
+
scheme.bearerFormat !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
83
|
+
"data-security-bearer-format": true,
|
|
84
|
+
children: scheme.bearerFormat
|
|
85
|
+
}),
|
|
86
|
+
scheme.name !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
87
|
+
"data-security-apikey-name": true,
|
|
88
|
+
children: scheme.name
|
|
89
|
+
}),
|
|
90
|
+
scheme.location !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
91
|
+
"data-security-apikey-in": true,
|
|
92
|
+
children: scheme.location
|
|
93
|
+
}),
|
|
94
|
+
scheme.openIdConnectUrl !== void 0 && /* @__PURE__ */ jsx("a", {
|
|
95
|
+
"data-security-openid-url": true,
|
|
96
|
+
href: scheme.openIdConnectUrl,
|
|
97
|
+
children: scheme.openIdConnectUrl
|
|
98
|
+
}),
|
|
99
|
+
flows.length > 0 && /* @__PURE__ */ jsx("section", {
|
|
100
|
+
"data-security-flows": true,
|
|
101
|
+
children: flows.map((flow) => /* @__PURE__ */ jsx(FlowDetails, { flow }, flow.name))
|
|
102
|
+
})
|
|
103
|
+
] });
|
|
104
|
+
}
|
|
105
|
+
function FlowDetails({ flow }) {
|
|
106
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
107
|
+
"data-security-flow": flow.name,
|
|
108
|
+
children: [
|
|
109
|
+
/* @__PURE__ */ jsx("span", {
|
|
110
|
+
"data-security-flow-name": true,
|
|
111
|
+
children: flow.name
|
|
112
|
+
}),
|
|
113
|
+
flow.authorizationUrl !== void 0 && /* @__PURE__ */ jsx("a", {
|
|
114
|
+
"data-security-flow-authorization-url": true,
|
|
115
|
+
href: flow.authorizationUrl,
|
|
116
|
+
children: flow.authorizationUrl
|
|
117
|
+
}),
|
|
118
|
+
flow.tokenUrl !== void 0 && /* @__PURE__ */ jsx("a", {
|
|
119
|
+
"data-security-flow-token-url": true,
|
|
120
|
+
href: flow.tokenUrl,
|
|
121
|
+
children: flow.tokenUrl
|
|
122
|
+
}),
|
|
123
|
+
flow.refreshUrl !== void 0 && /* @__PURE__ */ jsx("a", {
|
|
124
|
+
"data-security-flow-refresh-url": true,
|
|
125
|
+
href: flow.refreshUrl,
|
|
126
|
+
children: flow.refreshUrl
|
|
127
|
+
}),
|
|
128
|
+
flow.scopes.size > 0 && /* @__PURE__ */ jsx("dl", {
|
|
129
|
+
"data-security-flow-scopes": true,
|
|
130
|
+
children: [...flow.scopes.entries()].map(([name, description]) => /* @__PURE__ */ jsxs("div", {
|
|
131
|
+
"data-security-flow-scope": name,
|
|
132
|
+
children: [/* @__PURE__ */ jsx("dt", { children: name }), /* @__PURE__ */ jsx("dd", { children: description })]
|
|
133
|
+
}, name))
|
|
134
|
+
})
|
|
135
|
+
]
|
|
136
|
+
});
|
|
137
|
+
}
|
|
32
138
|
//#endregion
|
|
33
139
|
export { ApiSecurity };
|
package/dist/openapi/bundle.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isObject } from "../core/guards.mjs";
|
|
2
|
+
import { isPrototypePollutingKey } from "../core/uri.mjs";
|
|
2
3
|
//#region src/openapi/bundle.ts
|
|
3
4
|
/**
|
|
4
5
|
* OpenAPI document bundler — inlines external $ref files.
|
|
@@ -104,6 +105,7 @@ function resolveFragment(doc, fragment) {
|
|
|
104
105
|
for (const part of parts) {
|
|
105
106
|
if (!isObject(current)) return void 0;
|
|
106
107
|
const decoded = part.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
108
|
+
if (isPrototypePollutingKey(decoded)) return void 0;
|
|
107
109
|
current = current[decoded];
|
|
108
110
|
}
|
|
109
111
|
return isObject(current) ? current : void 0;
|
|
@@ -1,10 +1,24 @@
|
|
|
1
|
-
import { T as SchemaMeta, u as FieldOverride } from "../types-
|
|
2
|
-
import {
|
|
1
|
+
import { T as SchemaMeta, u as FieldOverride } from "../types-BnxPEElk.mjs";
|
|
2
|
+
import { r as DiagnosticSink } from "../diagnostics-CbBPsxSt.mjs";
|
|
3
|
+
import { a as InferResponseFields, i as InferRequestBodyFields, m as UnsafeFields, r as InferParameterOverrides } from "../typeInference-Bxw3NOG1.mjs";
|
|
3
4
|
import { WidgetMap } from "../react/SchemaComponent.mjs";
|
|
4
5
|
import { ReactNode } from "react";
|
|
5
6
|
|
|
6
7
|
//#region src/openapi/components.d.ts
|
|
7
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Diagnostics props accepted by every top-level OpenAPI component.
|
|
10
|
+
*
|
|
11
|
+
* `onDiagnostic` is the sink invoked for each event surfaced by the
|
|
12
|
+
* normalisation pipeline (duplicate body parameter, dropped Swagger
|
|
13
|
+
* feature, divisible-by conflict, unknown JSON Schema dialect,
|
|
14
|
+
* relative-ref resolved, etc.). `strict` converts every emitted
|
|
15
|
+
* diagnostic into a thrown `SchemaNormalisationError`.
|
|
16
|
+
*/
|
|
17
|
+
interface ApiDiagnosticsProps {
|
|
18
|
+
onDiagnostic?: DiagnosticSink;
|
|
19
|
+
strict?: boolean;
|
|
20
|
+
}
|
|
21
|
+
interface ApiOperationProps<Doc = unknown, Path extends string = string, Method extends string = string> extends ApiDiagnosticsProps {
|
|
8
22
|
schema: Doc;
|
|
9
23
|
path: Path;
|
|
10
24
|
method: Method;
|
|
@@ -30,9 +44,11 @@ declare function ApiOperation<Doc = unknown, Path extends string = string, Metho
|
|
|
30
44
|
responseValue,
|
|
31
45
|
meta,
|
|
32
46
|
requestBodyFields,
|
|
33
|
-
widgets
|
|
47
|
+
widgets,
|
|
48
|
+
onDiagnostic,
|
|
49
|
+
strict
|
|
34
50
|
}: ApiOperationProps<Doc, Path, Method>): ReactNode;
|
|
35
|
-
interface ApiParametersProps<Doc = unknown, Path extends string = string, Method extends string = string> {
|
|
51
|
+
interface ApiParametersProps<Doc = unknown, Path extends string = string, Method extends string = string> extends ApiDiagnosticsProps {
|
|
36
52
|
schema: Doc;
|
|
37
53
|
path: Path;
|
|
38
54
|
method: Method;
|
|
@@ -47,9 +63,11 @@ declare function ApiParameters<Doc = unknown, Path extends string = string, Meth
|
|
|
47
63
|
method,
|
|
48
64
|
meta,
|
|
49
65
|
overrides,
|
|
50
|
-
widgets
|
|
66
|
+
widgets,
|
|
67
|
+
onDiagnostic,
|
|
68
|
+
strict
|
|
51
69
|
}: ApiParametersProps<Doc, Path, Method>): ReactNode;
|
|
52
|
-
interface ApiRequestBodyProps<Doc = unknown, Path extends string = string, Method extends string = string> {
|
|
70
|
+
interface ApiRequestBodyProps<Doc = unknown, Path extends string = string, Method extends string = string> extends ApiDiagnosticsProps {
|
|
53
71
|
schema: Doc;
|
|
54
72
|
path: Path;
|
|
55
73
|
method: Method;
|
|
@@ -68,9 +86,11 @@ declare function ApiRequestBody<Doc = unknown, Path extends string = string, Met
|
|
|
68
86
|
onChange,
|
|
69
87
|
meta,
|
|
70
88
|
fields,
|
|
71
|
-
widgets
|
|
89
|
+
widgets,
|
|
90
|
+
onDiagnostic,
|
|
91
|
+
strict
|
|
72
92
|
}: ApiRequestBodyProps<Doc, Path, Method>): ReactNode;
|
|
73
|
-
interface ApiResponseProps<Doc = unknown, Path extends string = string, Method extends string = string, Status extends string = string> {
|
|
93
|
+
interface ApiResponseProps<Doc = unknown, Path extends string = string, Method extends string = string, Status extends string = string> extends ApiDiagnosticsProps {
|
|
74
94
|
schema: Doc;
|
|
75
95
|
path: Path;
|
|
76
96
|
method: Method;
|
|
@@ -89,7 +109,9 @@ declare function ApiResponse<Doc = unknown, Path extends string = string, Method
|
|
|
89
109
|
value,
|
|
90
110
|
meta,
|
|
91
111
|
fields,
|
|
92
|
-
widgets
|
|
112
|
+
widgets,
|
|
113
|
+
onDiagnostic,
|
|
114
|
+
strict
|
|
93
115
|
}: ApiResponseProps<Doc, Path, Method, Status>): ReactNode;
|
|
94
116
|
//#endregion
|
|
95
117
|
export { ApiOperation, ApiOperationProps, ApiParameters, ApiParametersProps, ApiRequestBody, ApiRequestBodyProps, ApiResponse, ApiResponseProps };
|
|
@@ -6,7 +6,7 @@ import { ApiResponseHeaders } from "./ApiResponseHeaders.mjs";
|
|
|
6
6
|
import { ApiSecurity } from "./ApiSecurity.mjs";
|
|
7
7
|
import { getLinks, getSecurityRequirements, getSecuritySchemes, listCallbacks } from "./parser.mjs";
|
|
8
8
|
import { joinPath, renderField, sanitisePrefix } from "../react/SchemaComponent.mjs";
|
|
9
|
-
import { getParsed,
|
|
9
|
+
import { getParsed, resolveOperationFromParsed, resolveParametersFromParsed, resolveRequestBodyFromParsed, resolveResponseFromParsed, toDoc } from "./resolve.mjs";
|
|
10
10
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
11
11
|
import { useId } from "react";
|
|
12
12
|
//#region src/openapi/components.tsx
|
|
@@ -23,6 +23,13 @@ import { useId } from "react";
|
|
|
23
23
|
* rendered via the walker + headless resolver directly, bypassing
|
|
24
24
|
* SchemaComponent to avoid deferred-conditional-type compatibility issues.
|
|
25
25
|
*/
|
|
26
|
+
function buildDiagnostics(onDiagnostic, strict) {
|
|
27
|
+
if (onDiagnostic === void 0 && strict !== true) return void 0;
|
|
28
|
+
const opts = {};
|
|
29
|
+
if (onDiagnostic !== void 0) opts.diagnostics = onDiagnostic;
|
|
30
|
+
if (strict === true) opts.strict = true;
|
|
31
|
+
return opts;
|
|
32
|
+
}
|
|
26
33
|
function noop() {}
|
|
27
34
|
function renderSchema(schema, rootDocument, options) {
|
|
28
35
|
if (!isObject(schema)) throw new Error("renderSchema received a non-object schema from the resolver.");
|
|
@@ -42,10 +49,10 @@ function renderSchema(schema, rootDocument, options) {
|
|
|
42
49
|
};
|
|
43
50
|
return renderField(tree, options.value, options.onChange ?? noop, void 0, makeRenderChild(options.rootPath), options.rootPath, options.widgets);
|
|
44
51
|
}
|
|
45
|
-
function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBodyChange, responseValue, meta, requestBodyFields, widgets }) {
|
|
52
|
+
function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBodyChange, responseValue, meta, requestBodyFields, widgets, onDiagnostic, strict }) {
|
|
46
53
|
const rootDoc = toDoc(doc);
|
|
47
|
-
const
|
|
48
|
-
const
|
|
54
|
+
const parsed = getParsed(rootDoc, buildDiagnostics(onDiagnostic, strict));
|
|
55
|
+
const resolved = resolveOperationFromParsed(parsed, path, method);
|
|
49
56
|
const securityReqs = getSecurityRequirements(parsed, path, method);
|
|
50
57
|
const securitySchemes = getSecuritySchemes(parsed);
|
|
51
58
|
const callbacks = listCallbacks(parsed, path, method);
|
|
@@ -53,7 +60,10 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
|
|
|
53
60
|
return /* @__PURE__ */ jsxs("section", {
|
|
54
61
|
"data-operation": `${method.toUpperCase()} ${path}`,
|
|
55
62
|
children: [
|
|
56
|
-
/* @__PURE__ */ jsx(OperationHeader, {
|
|
63
|
+
/* @__PURE__ */ jsx(OperationHeader, {
|
|
64
|
+
operation: resolved.operation,
|
|
65
|
+
pathItem: resolved.pathItem
|
|
66
|
+
}),
|
|
57
67
|
/* @__PURE__ */ jsx(ApiSecurity, {
|
|
58
68
|
requirements: securityReqs,
|
|
59
69
|
schemes: securitySchemes
|
|
@@ -96,6 +106,7 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
|
|
|
96
106
|
children: [/* @__PURE__ */ jsx("h4", { children: "Responses" }), resolved.responses.map((response) => /* @__PURE__ */ jsx(ResponseCard, {
|
|
97
107
|
response,
|
|
98
108
|
rootDoc,
|
|
109
|
+
parsed,
|
|
99
110
|
value: responseValue,
|
|
100
111
|
meta,
|
|
101
112
|
widgets,
|
|
@@ -107,9 +118,9 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
|
|
|
107
118
|
]
|
|
108
119
|
});
|
|
109
120
|
}
|
|
110
|
-
function ApiParameters({ schema: doc, path, method, meta, overrides, widgets }) {
|
|
121
|
+
function ApiParameters({ schema: doc, path, method, meta, overrides, widgets, onDiagnostic, strict }) {
|
|
111
122
|
const rootDoc = toDoc(doc);
|
|
112
|
-
const params =
|
|
123
|
+
const params = resolveParametersFromParsed(getParsed(rootDoc, buildDiagnostics(onDiagnostic, strict)), path, method);
|
|
113
124
|
const instancePrefix = sanitisePrefix(useId());
|
|
114
125
|
if (params.length === 0) return null;
|
|
115
126
|
return /* @__PURE__ */ jsxs("section", {
|
|
@@ -124,9 +135,9 @@ function ApiParameters({ schema: doc, path, method, meta, overrides, widgets })
|
|
|
124
135
|
})]
|
|
125
136
|
});
|
|
126
137
|
}
|
|
127
|
-
function ApiRequestBody({ schema: doc, path, method, value, onChange, meta, fields, widgets }) {
|
|
138
|
+
function ApiRequestBody({ schema: doc, path, method, value, onChange, meta, fields, widgets, onDiagnostic, strict }) {
|
|
128
139
|
const rootDoc = toDoc(doc);
|
|
129
|
-
const requestBody =
|
|
140
|
+
const requestBody = resolveRequestBodyFromParsed(getParsed(rootDoc, buildDiagnostics(onDiagnostic, strict)), path, method);
|
|
130
141
|
const instancePrefix = sanitisePrefix(useId());
|
|
131
142
|
if (requestBody?.schema === void 0) return null;
|
|
132
143
|
return /* @__PURE__ */ jsxs("section", {
|
|
@@ -152,9 +163,10 @@ function ApiRequestBody({ schema: doc, path, method, value, onChange, meta, fiel
|
|
|
152
163
|
]
|
|
153
164
|
});
|
|
154
165
|
}
|
|
155
|
-
function ApiResponse({ schema: doc, path, method, status, value, meta, fields, widgets }) {
|
|
166
|
+
function ApiResponse({ schema: doc, path, method, status, value, meta, fields, widgets, onDiagnostic, strict }) {
|
|
156
167
|
const rootDoc = toDoc(doc);
|
|
157
|
-
const
|
|
168
|
+
const parsed = getParsed(rootDoc, buildDiagnostics(onDiagnostic, strict));
|
|
169
|
+
const response = resolveResponseFromParsed(parsed, path, method, status);
|
|
158
170
|
const instancePrefix = sanitisePrefix(useId());
|
|
159
171
|
if (response.schema === void 0) return /* @__PURE__ */ jsxs("div", {
|
|
160
172
|
"data-status": status,
|
|
@@ -167,6 +179,7 @@ function ApiResponse({ schema: doc, path, method, status, value, meta, fields, w
|
|
|
167
179
|
return /* @__PURE__ */ jsx(ResponseCard, {
|
|
168
180
|
response,
|
|
169
181
|
rootDoc,
|
|
182
|
+
parsed,
|
|
170
183
|
value,
|
|
171
184
|
fields,
|
|
172
185
|
meta,
|
|
@@ -176,8 +189,18 @@ function ApiResponse({ schema: doc, path, method, status, value, meta, fields, w
|
|
|
176
189
|
idPrefix: instancePrefix
|
|
177
190
|
});
|
|
178
191
|
}
|
|
179
|
-
function OperationHeader({ operation }) {
|
|
192
|
+
function OperationHeader({ operation, pathItem }) {
|
|
180
193
|
return /* @__PURE__ */ jsxs("header", { children: [
|
|
194
|
+
(pathItem.summary !== void 0 || pathItem.description !== void 0) && /* @__PURE__ */ jsxs("div", {
|
|
195
|
+
"data-path-info": true,
|
|
196
|
+
children: [pathItem.summary !== void 0 && /* @__PURE__ */ jsx("p", {
|
|
197
|
+
"data-path-summary": true,
|
|
198
|
+
children: pathItem.summary
|
|
199
|
+
}), pathItem.description !== void 0 && /* @__PURE__ */ jsx("p", {
|
|
200
|
+
"data-path-description": true,
|
|
201
|
+
children: pathItem.description
|
|
202
|
+
})]
|
|
203
|
+
}),
|
|
181
204
|
/* @__PURE__ */ jsxs("h3", { children: [
|
|
182
205
|
operation.method.toUpperCase(),
|
|
183
206
|
" ",
|
|
@@ -214,7 +237,7 @@ function ParameterList({ parameters, rootDoc, overrides, meta, widgets, idPrefix
|
|
|
214
237
|
]
|
|
215
238
|
}, param.name)) });
|
|
216
239
|
}
|
|
217
|
-
function ResponseCard({ response, rootDoc, value, fields, meta, widgets, path, method, idPrefix }) {
|
|
240
|
+
function ResponseCard({ response, rootDoc, parsed, value, fields, meta, widgets, path, method, idPrefix }) {
|
|
218
241
|
if (response.schema === void 0) return /* @__PURE__ */ jsxs("div", {
|
|
219
242
|
"data-status": response.statusCode,
|
|
220
243
|
children: [
|
|
@@ -224,9 +247,7 @@ function ResponseCard({ response, rootDoc, value, fields, meta, widgets, path, m
|
|
|
224
247
|
]
|
|
225
248
|
});
|
|
226
249
|
let links = [];
|
|
227
|
-
if (path !== void 0 && method !== void 0)
|
|
228
|
-
links = getLinks(getParsed(rootDoc), path, method, response.statusCode);
|
|
229
|
-
} catch {}
|
|
250
|
+
if (path !== void 0 && method !== void 0) links = getLinks(parsed, path, method, response.statusCode);
|
|
230
251
|
return /* @__PURE__ */ jsxs("div", {
|
|
231
252
|
"data-status": response.statusCode,
|
|
232
253
|
children: [
|
package/dist/openapi/parser.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getProperty, isObject } from "../core/guards.mjs";
|
|
2
|
+
import { isPrototypePollutingKey } from "../core/uri.mjs";
|
|
2
3
|
//#region src/openapi/parser.ts
|
|
3
4
|
function getString(value, key) {
|
|
4
5
|
const result = isObject(value) ? value[key] : void 0;
|
|
@@ -119,8 +120,10 @@ function resolveParam(doc, param) {
|
|
|
119
120
|
return param;
|
|
120
121
|
}
|
|
121
122
|
function getRequestBody(parsed, path, method) {
|
|
122
|
-
const
|
|
123
|
-
if (!isObject(
|
|
123
|
+
const requestBodyRaw = getProperty(getProperty(lookupPathItem(parsed, path), method), "requestBody");
|
|
124
|
+
if (!isObject(requestBodyRaw)) return void 0;
|
|
125
|
+
const requestBody = resolveWrapperRef(parsed.doc, requestBodyRaw);
|
|
126
|
+
if (requestBody === void 0) return void 0;
|
|
124
127
|
const content = getProperty(requestBody, "content");
|
|
125
128
|
if (!isObject(content)) return {
|
|
126
129
|
required: getProperty(requestBody, "required") === true,
|
|
@@ -141,8 +144,10 @@ function getResponses(parsed, path, method) {
|
|
|
141
144
|
const responses = getProperty(getProperty(lookupPathItem(parsed, path), method), "responses");
|
|
142
145
|
if (!isObject(responses)) return [];
|
|
143
146
|
const result = [];
|
|
144
|
-
for (const [statusCode,
|
|
145
|
-
if (!isObject(
|
|
147
|
+
for (const [statusCode, responseRaw] of Object.entries(responses)) {
|
|
148
|
+
if (!isObject(responseRaw)) continue;
|
|
149
|
+
const response = resolveWrapperRef(parsed.doc, responseRaw);
|
|
150
|
+
if (response === void 0) continue;
|
|
146
151
|
const content = getProperty(response, "content");
|
|
147
152
|
const contentTypes = isObject(content) ? Object.keys(content) : [];
|
|
148
153
|
const schema = isObject(content) ? extractSchemaFromContent(content) : void 0;
|
|
@@ -157,15 +162,46 @@ function getResponses(parsed, path, method) {
|
|
|
157
162
|
}
|
|
158
163
|
return result;
|
|
159
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Resolve a single-hop `$ref` on a wrapper object — Response Object,
|
|
167
|
+
* Request Body Object, etc. — against the document root. Returns the
|
|
168
|
+
* referenced node when the wrapper is a `$ref`, the wrapper itself when
|
|
169
|
+
* it has no `$ref`, or `undefined` when the `$ref` is malformed or
|
|
170
|
+
* cannot be resolved (so the caller skips the entry rather than reading
|
|
171
|
+
* stale fields from the bare `{ $ref }` envelope).
|
|
172
|
+
*/
|
|
173
|
+
function resolveWrapperRef(doc, wrapper) {
|
|
174
|
+
const ref = getString(wrapper, "$ref");
|
|
175
|
+
if (ref === void 0) return wrapper;
|
|
176
|
+
return resolveRefInDoc(doc, ref);
|
|
177
|
+
}
|
|
160
178
|
function extractSchemaFromContent(content) {
|
|
161
179
|
const jsonSchema = getProperty(getProperty(content, "application/json"), "schema");
|
|
162
180
|
if (isObject(jsonSchema)) return jsonSchema;
|
|
181
|
+
for (const [mediaType, mediaObj] of Object.entries(content)) {
|
|
182
|
+
if (!isJsonSuffixMediaType(mediaType)) continue;
|
|
183
|
+
if (!isObject(mediaObj)) continue;
|
|
184
|
+
const schema = getProperty(mediaObj, "schema");
|
|
185
|
+
if (isObject(schema)) return schema;
|
|
186
|
+
}
|
|
163
187
|
for (const mediaType of Object.values(content)) {
|
|
164
188
|
if (!isObject(mediaType)) continue;
|
|
165
189
|
const schema = getProperty(mediaType, "schema");
|
|
166
190
|
if (isObject(schema)) return schema;
|
|
167
191
|
}
|
|
168
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Detect RFC 6839 structured-syntax-suffix media types that encode JSON.
|
|
195
|
+
* Matches `application/<anything>+json`, optionally with parameters
|
|
196
|
+
* (`; charset=utf-8`). Excludes the literal `application/json`, which
|
|
197
|
+
* the caller checks separately to preserve preference order.
|
|
198
|
+
*/
|
|
199
|
+
function isJsonSuffixMediaType(mediaType) {
|
|
200
|
+
const lower = mediaType.toLowerCase();
|
|
201
|
+
if (lower === "application/json") return false;
|
|
202
|
+
const baseType = lower.split(";", 1)[0]?.trim() ?? "";
|
|
203
|
+
return baseType.startsWith("application/") && baseType.endsWith("+json");
|
|
204
|
+
}
|
|
169
205
|
function resolveRefInDoc(doc, ref) {
|
|
170
206
|
if (!ref.startsWith("#/")) return void 0;
|
|
171
207
|
const parts = ref.slice(2).split("/");
|
|
@@ -173,6 +209,7 @@ function resolveRefInDoc(doc, ref) {
|
|
|
173
209
|
for (const part of parts) {
|
|
174
210
|
if (!isObject(current)) return void 0;
|
|
175
211
|
const decoded = part.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
212
|
+
if (isPrototypePollutingKey(decoded)) return void 0;
|
|
176
213
|
current = current[decoded];
|
|
177
214
|
}
|
|
178
215
|
return isObject(current) ? current : void 0;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { i as DiagnosticsOptions } from "../diagnostics-CbBPsxSt.mjs";
|
|
1
2
|
import { OpenApiDocument, OperationInfo, ParameterInfo, ResponseInfo, getRequestBody } from "./parser.mjs";
|
|
2
3
|
|
|
3
4
|
//#region src/openapi/resolve.d.ts
|
|
@@ -14,41 +15,101 @@ 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
32
|
* Coerce an unknown value to a record, returning an empty record
|
|
23
33
|
* for non-objects.
|
|
24
34
|
*/
|
|
25
35
|
declare function toDoc(value: unknown): Record<string, unknown>;
|
|
36
|
+
/**
|
|
37
|
+
* Path-Item-level metadata. OpenAPI 3.1 added `summary` and `description`
|
|
38
|
+
* to Path Item Objects alongside the existing operation-level fields.
|
|
39
|
+
* Both are plain strings (no Markdown rendering at this layer).
|
|
40
|
+
*/
|
|
41
|
+
interface PathItemInfo {
|
|
42
|
+
summary: string | undefined;
|
|
43
|
+
description: string | undefined;
|
|
44
|
+
}
|
|
26
45
|
interface ResolvedOperation {
|
|
27
46
|
operation: OperationInfo;
|
|
47
|
+
pathItem: PathItemInfo;
|
|
28
48
|
parameters: ParameterInfo[];
|
|
29
49
|
requestBody: ReturnType<typeof getRequestBody>;
|
|
30
50
|
responses: ResponseInfo[];
|
|
31
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Resolve an operation against an already-parsed document. Throws if
|
|
54
|
+
* the operation is not found.
|
|
55
|
+
*
|
|
56
|
+
* Used by callers that have already obtained a parsed document via
|
|
57
|
+
* {@link getParsed} — most importantly the React components, which
|
|
58
|
+
* supply `diagnostics` to `getParsed` and must avoid re-running the
|
|
59
|
+
* normalisation pipeline (every re-run would emit each diagnostic
|
|
60
|
+
* again into the sink).
|
|
61
|
+
*/
|
|
62
|
+
declare function resolveOperationFromParsed(parsed: OpenApiDocument, path: string, method: string): ResolvedOperation;
|
|
32
63
|
/**
|
|
33
64
|
* Resolve an operation from an OpenAPI document by path and method.
|
|
34
65
|
* Throws if the operation is not found.
|
|
66
|
+
*
|
|
67
|
+
* `diagnostics` is forwarded to {@link getParsed} so normalisation
|
|
68
|
+
* events surface to the caller's sink.
|
|
69
|
+
*/
|
|
70
|
+
declare function resolveOperation(doc: Record<string, unknown>, path: string, method: string, diagnostics?: DiagnosticsOptions): ResolvedOperation;
|
|
71
|
+
/**
|
|
72
|
+
* Resolve parameters against an already-parsed document. See
|
|
73
|
+
* {@link resolveOperationFromParsed} for the rationale.
|
|
35
74
|
*/
|
|
36
|
-
declare function
|
|
75
|
+
declare function resolveParametersFromParsed(parsed: OpenApiDocument, path: string, method: string): ParameterInfo[];
|
|
37
76
|
/**
|
|
38
77
|
* Resolve parameters for an operation. Returns empty array if none.
|
|
78
|
+
*
|
|
79
|
+
* `diagnostics` is forwarded to {@link getParsed} so normalisation
|
|
80
|
+
* events surface to the caller's sink.
|
|
39
81
|
*/
|
|
40
|
-
declare function resolveParameters(doc: Record<string, unknown>, path: string, method: string): ParameterInfo[];
|
|
82
|
+
declare function resolveParameters(doc: Record<string, unknown>, path: string, method: string, diagnostics?: DiagnosticsOptions): ParameterInfo[];
|
|
83
|
+
/**
|
|
84
|
+
* Resolve a request body against an already-parsed document. See
|
|
85
|
+
* {@link resolveOperationFromParsed} for the rationale.
|
|
86
|
+
*/
|
|
87
|
+
declare function resolveRequestBodyFromParsed(parsed: OpenApiDocument, path: string, method: string): ReturnType<typeof getRequestBody>;
|
|
41
88
|
/**
|
|
42
89
|
* Resolve request body for an operation. Returns undefined if none.
|
|
90
|
+
*
|
|
91
|
+
* `diagnostics` is forwarded to {@link getParsed} so normalisation
|
|
92
|
+
* events surface to the caller's sink.
|
|
43
93
|
*/
|
|
44
|
-
declare function resolveRequestBody(doc: Record<string, unknown>, path: string, method: string): ReturnType<typeof getRequestBody>;
|
|
94
|
+
declare function resolveRequestBody(doc: Record<string, unknown>, path: string, method: string, diagnostics?: DiagnosticsOptions): ReturnType<typeof getRequestBody>;
|
|
95
|
+
/**
|
|
96
|
+
* Resolve a specific response against an already-parsed document. See
|
|
97
|
+
* {@link resolveOperationFromParsed} for the rationale.
|
|
98
|
+
*/
|
|
99
|
+
declare function resolveResponseFromParsed(parsed: OpenApiDocument, path: string, method: string, statusCode: string): ResponseInfo;
|
|
45
100
|
/**
|
|
46
101
|
* Resolve a specific response by status code. Throws if not found.
|
|
102
|
+
*
|
|
103
|
+
* `diagnostics` is forwarded to {@link getParsed} so normalisation
|
|
104
|
+
* events surface to the caller's sink.
|
|
47
105
|
*/
|
|
48
|
-
declare function resolveResponse(doc: Record<string, unknown>, path: string, method: string, statusCode: string): ResponseInfo;
|
|
106
|
+
declare function resolveResponse(doc: Record<string, unknown>, path: string, method: string, statusCode: string, diagnostics?: DiagnosticsOptions): ResponseInfo;
|
|
49
107
|
/**
|
|
50
108
|
* Resolve all responses for an operation.
|
|
109
|
+
*
|
|
110
|
+
* `diagnostics` is forwarded to {@link getParsed} so normalisation
|
|
111
|
+
* events surface to the caller's sink.
|
|
51
112
|
*/
|
|
52
|
-
declare function resolveResponses(doc: Record<string, unknown>, path: string, method: string): ResponseInfo[];
|
|
113
|
+
declare function resolveResponses(doc: Record<string, unknown>, path: string, method: string, diagnostics?: DiagnosticsOptions): ResponseInfo[];
|
|
53
114
|
//#endregion
|
|
54
|
-
export { ResolvedOperation, getParsed, resolveOperation, resolveParameters, resolveRequestBody, resolveResponse, resolveResponses, toDoc };
|
|
115
|
+
export { PathItemInfo, ResolvedOperation, getParsed, resolveOperation, resolveOperationFromParsed, resolveParameters, resolveParametersFromParsed, resolveRequestBody, resolveRequestBodyFromParsed, resolveResponse, resolveResponseFromParsed, resolveResponses, toDoc };
|