@gitbook/react-openapi 1.4.2 → 1.5.1
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/CHANGELOG.md +34 -0
- package/dist/index.d.ts +663 -10
- package/dist/index.js +3873 -8
- package/package.json +11 -7
- package/dist/InteractiveSection.d.ts +0 -33
- package/dist/InteractiveSection.jsx +0 -61
- package/dist/Markdown.d.ts +0 -4
- package/dist/Markdown.jsx +0 -5
- package/dist/OpenAPICodeSample.d.ts +0 -19
- package/dist/OpenAPICodeSample.jsx +0 -230
- package/dist/OpenAPICodeSampleInteractive.d.ts +0 -14
- package/dist/OpenAPICodeSampleInteractive.jsx +0 -73
- package/dist/OpenAPICodeSampleSelector.d.ts +0 -14
- package/dist/OpenAPICodeSampleSelector.jsx +0 -44
- package/dist/OpenAPICopyButton.d.ts +0 -13
- package/dist/OpenAPICopyButton.jsx +0 -35
- package/dist/OpenAPIDisclosure.d.ts +0 -11
- package/dist/OpenAPIDisclosure.jsx +0 -30
- package/dist/OpenAPIDisclosureGroup.d.ts +0 -23
- package/dist/OpenAPIDisclosureGroup.jsx +0 -83
- package/dist/OpenAPIExample.d.ts +0 -16
- package/dist/OpenAPIExample.jsx +0 -36
- package/dist/OpenAPIMediaType.d.ts +0 -21
- package/dist/OpenAPIMediaType.jsx +0 -61
- package/dist/OpenAPIOperation.d.ts +0 -10
- package/dist/OpenAPIOperation.jsx +0 -25
- package/dist/OpenAPIOperationContext.d.ts +0 -16
- package/dist/OpenAPIOperationContext.jsx +0 -26
- package/dist/OpenAPIOperationDescription.d.ts +0 -9
- package/dist/OpenAPIOperationDescription.jsx +0 -22
- package/dist/OpenAPIOperationStability.d.ts +0 -9
- package/dist/OpenAPIOperationStability.jsx +0 -27
- package/dist/OpenAPIPath.d.ts +0 -18
- package/dist/OpenAPIPath.jsx +0 -55
- package/dist/OpenAPIPrefillContextProvider.d.ts +0 -22
- package/dist/OpenAPIPrefillContextProvider.jsx +0 -19
- package/dist/OpenAPIRequestBody.d.ts +0 -11
- package/dist/OpenAPIRequestBody.jsx +0 -28
- package/dist/OpenAPIRequestBodyHeaderType.d.ts +0 -8
- package/dist/OpenAPIRequestBodyHeaderType.jsx +0 -25
- package/dist/OpenAPIResponse.d.ts +0 -10
- package/dist/OpenAPIResponse.jsx +0 -57
- package/dist/OpenAPIResponseExample.d.ts +0 -9
- package/dist/OpenAPIResponseExample.jsx +0 -105
- package/dist/OpenAPIResponseExampleContent.d.ts +0 -22
- package/dist/OpenAPIResponseExampleContent.jsx +0 -60
- package/dist/OpenAPIResponses.d.ts +0 -9
- package/dist/OpenAPIResponses.jsx +0 -77
- package/dist/OpenAPISchema.d.ts +0 -27
- package/dist/OpenAPISchema.jsx +0 -400
- package/dist/OpenAPISchemaName.d.ts +0 -16
- package/dist/OpenAPISchemaName.jsx +0 -43
- package/dist/OpenAPISchemaServer.d.ts +0 -12
- package/dist/OpenAPISchemaServer.jsx +0 -8
- package/dist/OpenAPISecurities.d.ts +0 -9
- package/dist/OpenAPISecurities.jsx +0 -114
- package/dist/OpenAPISelect.d.ts +0 -22
- package/dist/OpenAPISelect.jsx +0 -44
- package/dist/OpenAPISpec.d.ts +0 -6
- package/dist/OpenAPISpec.jsx +0 -80
- package/dist/OpenAPITabs.d.ts +0 -26
- package/dist/OpenAPITabs.jsx +0 -109
- package/dist/OpenAPIWebhook.d.ts +0 -10
- package/dist/OpenAPIWebhook.jsx +0 -23
- package/dist/OpenAPIWebhookExample.d.ts +0 -6
- package/dist/OpenAPIWebhookExample.jsx +0 -41
- package/dist/ScalarApiButton.d.ts +0 -14
- package/dist/ScalarApiButton.jsx +0 -81
- package/dist/StaticSection.d.ts +0 -13
- package/dist/StaticSection.jsx +0 -32
- package/dist/code-samples.d.ts +0 -17
- package/dist/code-samples.js +0 -427
- package/dist/common/OpenAPIColumnSpec.d.ts +0 -6
- package/dist/common/OpenAPIColumnSpec.jsx +0 -20
- package/dist/common/OpenAPIOperationDescription.d.ts +0 -6
- package/dist/common/OpenAPIOperationDescription.jsx +0 -19
- package/dist/common/OpenAPIStability.d.ts +0 -4
- package/dist/common/OpenAPIStability.jsx +0 -15
- package/dist/common/OpenAPISummary.d.ts +0 -6
- package/dist/common/OpenAPISummary.jsx +0 -30
- package/dist/contentTypeChecks.d.ts +0 -10
- package/dist/contentTypeChecks.js +0 -30
- package/dist/context.d.ts +0 -75
- package/dist/context.js +0 -43
- package/dist/decycle.d.ts +0 -2
- package/dist/decycle.js +0 -70
- package/dist/dereference.d.ts +0 -5
- package/dist/dereference.js +0 -68
- package/dist/generateSchemaExample.d.ts +0 -45
- package/dist/generateSchemaExample.js +0 -342
- package/dist/getDisclosureLabel.d.ts +0 -7
- package/dist/getDisclosureLabel.js +0 -18
- package/dist/getOrCreateStoreByKey.d.ts +0 -10
- package/dist/getOrCreateStoreByKey.js +0 -19
- package/dist/json2xml.d.ts +0 -4
- package/dist/json2xml.js +0 -7
- package/dist/resolveOpenAPIOperation.d.ts +0 -11
- package/dist/resolveOpenAPIOperation.js +0 -173
- package/dist/resolveOpenAPIWebhook.d.ts +0 -11
- package/dist/resolveOpenAPIWebhook.js +0 -127
- package/dist/schemas/OpenAPISchemaItem.d.ts +0 -7
- package/dist/schemas/OpenAPISchemaItem.jsx +0 -16
- package/dist/schemas/OpenAPISchemas.d.ts +0 -14
- package/dist/schemas/OpenAPISchemas.jsx +0 -59
- package/dist/schemas/index.d.ts +0 -2
- package/dist/schemas/index.js +0 -2
- package/dist/schemas/resolveOpenAPISchemas.d.ts +0 -10
- package/dist/schemas/resolveOpenAPISchemas.js +0 -61
- package/dist/stringifyOpenAPI.d.ts +0 -4
- package/dist/stringifyOpenAPI.js +0 -15
- package/dist/translate.d.ts +0 -10
- package/dist/translate.jsx +0 -75
- package/dist/translations/de.d.ts +0 -43
- package/dist/translations/de.js +0 -43
- package/dist/translations/en.d.ts +0 -43
- package/dist/translations/en.js +0 -43
- package/dist/translations/es.d.ts +0 -43
- package/dist/translations/es.js +0 -43
- package/dist/translations/fr.d.ts +0 -43
- package/dist/translations/fr.js +0 -43
- package/dist/translations/index.d.ts +0 -395
- package/dist/translations/index.js +0 -27
- package/dist/translations/ja.d.ts +0 -43
- package/dist/translations/ja.js +0 -43
- package/dist/translations/nl.d.ts +0 -43
- package/dist/translations/nl.js +0 -43
- package/dist/translations/no.d.ts +0 -43
- package/dist/translations/no.js +0 -43
- package/dist/translations/pt-br.d.ts +0 -43
- package/dist/translations/pt-br.js +0 -43
- package/dist/translations/types.d.ts +0 -5
- package/dist/translations/types.js +0 -1
- package/dist/translations/zh.d.ts +0 -43
- package/dist/translations/zh.js +0 -43
- package/dist/tsconfig.build.tsbuildinfo +0 -1
- package/dist/types.d.ts +0 -31
- package/dist/types.js +0 -1
- package/dist/util/example.d.ts +0 -35
- package/dist/util/example.jsx +0 -103
- package/dist/util/server.d.ts +0 -9
- package/dist/util/server.js +0 -44
- package/dist/util/tryit-prefill.d.ts +0 -20
- package/dist/util/tryit-prefill.js +0 -129
- package/dist/utils.d.ts +0 -50
- package/dist/utils.js +0 -224
- package/src/InteractiveSection.tsx +0 -147
- package/src/Markdown.tsx +0 -12
- package/src/OpenAPICodeSample.tsx +0 -330
- package/src/OpenAPICodeSampleInteractive.tsx +0 -136
- package/src/OpenAPICodeSampleSelector.tsx +0 -94
- package/src/OpenAPICopyButton.tsx +0 -72
- package/src/OpenAPIDisclosure.tsx +0 -46
- package/src/OpenAPIDisclosureGroup.tsx +0 -158
- package/src/OpenAPIExample.tsx +0 -55
- package/src/OpenAPIMediaType.tsx +0 -139
- package/src/OpenAPIOperation.tsx +0 -35
- package/src/OpenAPIOperationContext.tsx +0 -45
- package/src/OpenAPIOperationDescription.tsx +0 -34
- package/src/OpenAPIOperationStability.tsx +0 -39
- package/src/OpenAPIPath.tsx +0 -90
- package/src/OpenAPIPrefillContextProvider.tsx +0 -40
- package/src/OpenAPIRequestBody.tsx +0 -54
- package/src/OpenAPIRequestBodyHeaderType.tsx +0 -36
- package/src/OpenAPIResponse.tsx +0 -82
- package/src/OpenAPIResponseExample.tsx +0 -151
- package/src/OpenAPIResponseExampleContent.tsx +0 -125
- package/src/OpenAPIResponses.tsx +0 -125
- package/src/OpenAPISchema.test.ts +0 -172
- package/src/OpenAPISchema.tsx +0 -654
- package/src/OpenAPISchemaName.tsx +0 -80
- package/src/OpenAPISchemaServer.tsx +0 -34
- package/src/OpenAPISecurities.tsx +0 -231
- package/src/OpenAPISelect.tsx +0 -96
- package/src/OpenAPISpec.tsx +0 -138
- package/src/OpenAPITabs.tsx +0 -147
- package/src/OpenAPIWebhook.tsx +0 -33
- package/src/OpenAPIWebhookExample.tsx +0 -60
- package/src/ScalarApiButton.tsx +0 -132
- package/src/StaticSection.tsx +0 -91
- package/src/__snapshots__/json2xml.test.ts.snap +0 -18
- package/src/code-samples.test.ts +0 -714
- package/src/code-samples.ts +0 -448
- package/src/common/OpenAPIColumnSpec.tsx +0 -31
- package/src/common/OpenAPIOperationDescription.tsx +0 -31
- package/src/common/OpenAPIStability.tsx +0 -23
- package/src/common/OpenAPISummary.tsx +0 -45
- package/src/contentTypeChecks.ts +0 -39
- package/src/context.ts +0 -99
- package/src/decycle.ts +0 -68
- package/src/dereference.ts +0 -29
- package/src/generateSchemaExample.test.ts +0 -1040
- package/src/generateSchemaExample.ts +0 -530
- package/src/getDisclosureLabel.ts +0 -25
- package/src/getOrCreateStoreByKey.ts +0 -33
- package/src/index.ts +0 -10
- package/src/json2xml.test.ts +0 -46
- package/src/json2xml.ts +0 -8
- package/src/resolveOpenAPIOperation.test.ts +0 -177
- package/src/resolveOpenAPIOperation.ts +0 -151
- package/src/resolveOpenAPIWebhook.ts +0 -99
- package/src/schemas/OpenAPISchemaItem.tsx +0 -34
- package/src/schemas/OpenAPISchemas.tsx +0 -98
- package/src/schemas/index.ts +0 -2
- package/src/schemas/resolveOpenAPISchemas.test.ts +0 -174
- package/src/schemas/resolveOpenAPISchemas.ts +0 -28
- package/src/stringifyOpenAPI.ts +0 -25
- package/src/translate.tsx +0 -80
- package/src/translations/de.ts +0 -43
- package/src/translations/en.ts +0 -43
- package/src/translations/es.ts +0 -43
- package/src/translations/fr.ts +0 -43
- package/src/translations/index.ts +0 -33
- package/src/translations/ja.ts +0 -43
- package/src/translations/nl.ts +0 -43
- package/src/translations/no.ts +0 -43
- package/src/translations/pt-br.ts +0 -43
- package/src/translations/types.ts +0 -7
- package/src/translations/zh.ts +0 -43
- package/src/types.ts +0 -46
- package/src/util/example.tsx +0 -129
- package/src/util/server.test.ts +0 -58
- package/src/util/server.ts +0 -47
- package/src/util/tryit-prefill.test.ts +0 -311
- package/src/util/tryit-prefill.ts +0 -160
- package/src/utils.ts +0 -255
package/dist/index.js
CHANGED
|
@@ -1,8 +1,3873 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
import yaml from "js-yaml";
|
|
4
|
+
import { jsXml } from "json-xml-parse";
|
|
5
|
+
import * as React$1 from "react";
|
|
6
|
+
import React, { Fragment, Suspense, createContext, forwardRef, useCallback, useContext, useEffect, useId, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
7
|
+
import { Button, Disclosure, DisclosurePanel, ListBox, ListBoxItem, Popover, Select, SelectValue, Tooltip, TooltipTrigger } from "react-aria-components";
|
|
8
|
+
import { dereference, filterSelectedOpenAPISchemas } from "@gitbook/openapi-parser";
|
|
9
|
+
import { createStore, useStore } from "zustand";
|
|
10
|
+
import { ApiClientModalProvider, useApiClientModal } from "@scalar/api-client-react";
|
|
11
|
+
import { useEventCallback } from "usehooks-ts";
|
|
12
|
+
import { ExpressionRuntime, parseTemplate } from "@gitbook/expr";
|
|
13
|
+
import { mergeProps, useButton, useDisclosure, useFocusRing, useId as useId$1 } from "react-aria";
|
|
14
|
+
import "flatted";
|
|
15
|
+
|
|
16
|
+
//#region rolldown:runtime
|
|
17
|
+
var __create = Object.create;
|
|
18
|
+
var __defProp = Object.defineProperty;
|
|
19
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
20
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
21
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
22
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
23
|
+
var __commonJS = (cb, mod) => function() {
|
|
24
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
25
|
+
};
|
|
26
|
+
var __copyProps = (to, from, except, desc) => {
|
|
27
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
28
|
+
key = keys[i];
|
|
29
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
30
|
+
get: ((k) => from[k]).bind(null, key),
|
|
31
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return to;
|
|
35
|
+
};
|
|
36
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
37
|
+
value: mod,
|
|
38
|
+
enumerable: true
|
|
39
|
+
}) : target, mod));
|
|
40
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
41
|
+
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/json2xml.ts
|
|
44
|
+
/**
|
|
45
|
+
* This function converts an object to XML.
|
|
46
|
+
*/
|
|
47
|
+
function json2xml(data) {
|
|
48
|
+
return jsXml.toXmlString(data, { beautify: true });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
//#endregion
|
|
52
|
+
//#region src/stringifyOpenAPI.ts
|
|
53
|
+
/**
|
|
54
|
+
* Stringify an OpenAPI object. Same API as JSON.stringify.
|
|
55
|
+
*/
|
|
56
|
+
function stringifyOpenAPI(value, replacer, space) {
|
|
57
|
+
return JSON.stringify(value, (key, value$1) => {
|
|
58
|
+
if (key.startsWith("x-gitbook-")) return;
|
|
59
|
+
if (replacer) return replacer(key, value$1);
|
|
60
|
+
return value$1;
|
|
61
|
+
}, space);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/translate.tsx
|
|
66
|
+
/**
|
|
67
|
+
* Translate a string.
|
|
68
|
+
*/
|
|
69
|
+
function t(translation, id, ...args) {
|
|
70
|
+
const string = translation[id];
|
|
71
|
+
if (!string) throw new Error(`Translation not found for "${id}"`);
|
|
72
|
+
const parts = [];
|
|
73
|
+
let currentStringToReplace = string;
|
|
74
|
+
args.forEach((arg, i) => {
|
|
75
|
+
if (typeof arg === "string") currentStringToReplace = currentStringToReplace.replace(`\${${i + 1}}`, arg);
|
|
76
|
+
else {
|
|
77
|
+
const [partToPush, partToReplace] = currentStringToReplace.split(`\${${i + 1}}`);
|
|
78
|
+
if (partToPush === void 0 || partToReplace === void 0) throw new Error(`Invalid translation "${id}"`);
|
|
79
|
+
parts.push(<React.Fragment key={`string-${i}`}>{partToPush}</React.Fragment>);
|
|
80
|
+
parts.push(<React.Fragment key={`arg-${i}`}>{arg}</React.Fragment>);
|
|
81
|
+
currentStringToReplace = partToReplace;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
if (!parts.length) return currentStringToReplace;
|
|
85
|
+
return <>
|
|
86
|
+
{parts}
|
|
87
|
+
{currentStringToReplace}
|
|
88
|
+
</>;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Version of `t` that returns a string.
|
|
92
|
+
*/
|
|
93
|
+
function tString(translation, id, ...args) {
|
|
94
|
+
return reactToString(t(translation, id, ...args));
|
|
95
|
+
}
|
|
96
|
+
function reactToString(el) {
|
|
97
|
+
if (typeof el === "string" || typeof el === "number" || typeof el === "boolean") return `${el}`;
|
|
98
|
+
if (el === null || el === void 0) return "";
|
|
99
|
+
if (Array.isArray(el)) return el.map(reactToString).join("");
|
|
100
|
+
if (typeof el === "object" && "props" in el) return el.props.children.map(reactToString).join("");
|
|
101
|
+
throw new Error(`Unsupported type ${typeof el}`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region src/OpenAPIExample.tsx
|
|
106
|
+
/**
|
|
107
|
+
* Display an example.
|
|
108
|
+
*/
|
|
109
|
+
function OpenAPIExample(props) {
|
|
110
|
+
const { example, context, syntax } = props;
|
|
111
|
+
const code = stringifyExample({
|
|
112
|
+
example,
|
|
113
|
+
syntax
|
|
114
|
+
});
|
|
115
|
+
if (code === null) return <OpenAPIEmptyExample context={context} />;
|
|
116
|
+
return context.renderCodeBlock({
|
|
117
|
+
code,
|
|
118
|
+
syntax
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function stringifyExample(args) {
|
|
122
|
+
const { example, syntax } = args;
|
|
123
|
+
if (!example.value) return null;
|
|
124
|
+
if (typeof example.value === "string") return example.value;
|
|
125
|
+
if (syntax === "xml") return json2xml(example.value);
|
|
126
|
+
if (syntax === "yaml") return yaml.dump(example.value).replace(/'/g, "").replace(/\\n/g, "\n");
|
|
127
|
+
return stringifyOpenAPI(example.value, null, 2);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Empty response example.
|
|
131
|
+
*/
|
|
132
|
+
function OpenAPIEmptyExample(props) {
|
|
133
|
+
const { context } = props;
|
|
134
|
+
return <pre className="openapi-example-empty">
|
|
135
|
+
<p>{t(context.translation, "no_content")}</p>
|
|
136
|
+
</pre>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/Markdown.tsx
|
|
141
|
+
function Markdown(props) {
|
|
142
|
+
const { source, className } = props;
|
|
143
|
+
return <div className={clsx("openapi-markdown", className)} dangerouslySetInnerHTML={{ __html: source }} />;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region src/OpenAPICopyButton.tsx
|
|
148
|
+
function OpenAPICopyButton(props) {
|
|
149
|
+
const { value, label, children, onPress, className, context, withTooltip = true } = props;
|
|
150
|
+
const [copied, setCopied] = useState(false);
|
|
151
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
152
|
+
const handleCopy = () => {
|
|
153
|
+
if (!value) return;
|
|
154
|
+
navigator.clipboard.writeText(value).then(() => {
|
|
155
|
+
setIsOpen(true);
|
|
156
|
+
setCopied(true);
|
|
157
|
+
setTimeout(() => {
|
|
158
|
+
setCopied(false);
|
|
159
|
+
setIsOpen(false);
|
|
160
|
+
}, 2e3);
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
return <TooltipTrigger isOpen={isOpen} onOpenChange={setIsOpen} isDisabled={!withTooltip} closeDelay={200} delay={200}>
|
|
164
|
+
<Button type="button" preventFocusOnPress onPress={(e) => {
|
|
165
|
+
handleCopy();
|
|
166
|
+
onPress?.(e);
|
|
167
|
+
}} className={`openapi-copy-button ${className}`} {...props}>
|
|
168
|
+
{children}
|
|
169
|
+
</Button>
|
|
170
|
+
|
|
171
|
+
<Tooltip isOpen={isOpen} onOpenChange={setIsOpen} placement="top" offset={4} className="openapi-tooltip">
|
|
172
|
+
{copied ? t(context.translation, "copied") : label || t(context.translation, "copy_to_clipboard")}
|
|
173
|
+
</Tooltip>
|
|
174
|
+
</TooltipTrigger>;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
//#endregion
|
|
178
|
+
//#region src/OpenAPIDisclosure.tsx
|
|
179
|
+
/**
|
|
180
|
+
* Display an interactive OpenAPI disclosure.
|
|
181
|
+
*/
|
|
182
|
+
function OpenAPIDisclosure(props) {
|
|
183
|
+
const { icon, header, label, children, className } = props;
|
|
184
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
185
|
+
return <Disclosure className={clsx("openapi-disclosure", className)} isExpanded={isExpanded} onExpandedChange={setIsExpanded}>
|
|
186
|
+
<Button slot="trigger" className="openapi-disclosure-trigger" style={({ isFocusVisible }) => ({ outline: isFocusVisible ? "2px solid rgb(var(--primary-color-500) / 0.4)" : "none" })}>
|
|
187
|
+
{header}
|
|
188
|
+
<div className="openapi-disclosure-trigger-label">
|
|
189
|
+
<span>{typeof label === "function" ? label(isExpanded) : label}</span>
|
|
190
|
+
{icon}
|
|
191
|
+
</div>
|
|
192
|
+
</Button>
|
|
193
|
+
<DisclosurePanel className="openapi-disclosure-panel">
|
|
194
|
+
{isExpanded ? children : null}
|
|
195
|
+
</DisclosurePanel>
|
|
196
|
+
</Disclosure>;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
//#endregion
|
|
200
|
+
//#region src/OpenAPISchemaName.tsx
|
|
201
|
+
/**
|
|
202
|
+
* Display the schema name row.
|
|
203
|
+
* It includes the property name, type, required and deprecated status.
|
|
204
|
+
*/
|
|
205
|
+
function OpenAPISchemaName(props) {
|
|
206
|
+
const { schema, type, propertyName, required, isDiscriminatorProperty, context } = props;
|
|
207
|
+
const additionalItems = schema && getAdditionalItems(schema, context);
|
|
208
|
+
return <span className="openapi-schema-name">
|
|
209
|
+
{propertyName ? <span data-deprecated={schema?.deprecated} className="openapi-schema-propertyname">
|
|
210
|
+
{propertyName}
|
|
211
|
+
</span> : null}
|
|
212
|
+
{isDiscriminatorProperty ? <span className="openapi-schema-discriminator">
|
|
213
|
+
{t(context.translation, "discriminator")}
|
|
214
|
+
</span> : null}
|
|
215
|
+
{type || additionalItems ? <span>
|
|
216
|
+
{schema?.const ? <span className="openapi-schema-type">const: {schema?.const}</span> : type ? <span className="openapi-schema-type">{type}</span> : null}
|
|
217
|
+
{additionalItems ? <span className="openapi-schema-type">{additionalItems}</span> : null}
|
|
218
|
+
</span> : null}
|
|
219
|
+
{schema?.readOnly ? <span className="openapi-schema-readonly">
|
|
220
|
+
{t(context.translation, "read_only")}
|
|
221
|
+
</span> : null}
|
|
222
|
+
{schema?.writeOnly ? <span className="openapi-schema-writeonly">
|
|
223
|
+
{t(context.translation, "write_only")}
|
|
224
|
+
</span> : null}
|
|
225
|
+
{required === null ? null : required ? <span className="openapi-schema-required">
|
|
226
|
+
{t(context.translation, "required")}
|
|
227
|
+
</span> : <span className="openapi-schema-optional">
|
|
228
|
+
{t(context.translation, "optional")}
|
|
229
|
+
</span>}
|
|
230
|
+
{schema?.deprecated ? <span className="openapi-deprecated">{t(context.translation, "deprecated")}</span> : null}
|
|
231
|
+
</span>;
|
|
232
|
+
}
|
|
233
|
+
function getAdditionalItems(schema, context) {
|
|
234
|
+
let additionalItems = "";
|
|
235
|
+
if (schema.minimum || schema.minLength || schema.minItems) additionalItems += ` · ${tString(context.translation, "min").toLowerCase()}: ${schema.minimum || schema.minLength || schema.minItems}`;
|
|
236
|
+
if (schema.maximum || schema.maxLength || schema.maxItems) additionalItems += ` · ${tString(context.translation, "max").toLowerCase()}: ${schema.maximum || schema.maxLength || schema.maxItems}`;
|
|
237
|
+
if (schema.nullable) additionalItems = ` | ${tString(context.translation, "nullable").toLowerCase()}`;
|
|
238
|
+
return additionalItems;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
//#endregion
|
|
242
|
+
//#region src/decycle.ts
|
|
243
|
+
const isObject = (value) => typeof value === "object" && value != null && !(value instanceof Boolean) && !(value instanceof Date) && !(value instanceof Number) && !(value instanceof RegExp) && !(value instanceof String);
|
|
244
|
+
const toPointer = (parts) => `#${parts.map((part) => String(part).replace(/~/g, "~0").replace(/\//g, "~1")).join("/")}`;
|
|
245
|
+
const decycle = () => {
|
|
246
|
+
const paths = /* @__PURE__ */ new WeakMap();
|
|
247
|
+
return function replacer(key, value) {
|
|
248
|
+
if (key !== "$reference" && isObject(value)) {
|
|
249
|
+
if (paths.has(value)) return { $reference: toPointer(paths.get(value)) };
|
|
250
|
+
paths.set(value, [...paths.get(this) ?? [], key]);
|
|
251
|
+
}
|
|
252
|
+
return value;
|
|
253
|
+
};
|
|
254
|
+
};
|
|
255
|
+
function retrocycle() {
|
|
256
|
+
const parents = /* @__PURE__ */ new WeakMap();
|
|
257
|
+
const keys = /* @__PURE__ */ new WeakMap();
|
|
258
|
+
const refs = /* @__PURE__ */ new Set();
|
|
259
|
+
function dereference$1(ref) {
|
|
260
|
+
const parts = ref.$reference.slice(1).split("/");
|
|
261
|
+
let key;
|
|
262
|
+
let value = this;
|
|
263
|
+
for (let i = 0; i < parts.length; i++) {
|
|
264
|
+
key = parts[i]?.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
265
|
+
value = value[key];
|
|
266
|
+
}
|
|
267
|
+
const parent = parents.get(ref);
|
|
268
|
+
parent[keys.get(ref)] = value;
|
|
269
|
+
}
|
|
270
|
+
return function reviver(key, value) {
|
|
271
|
+
if (key === "$reference") refs.add(this);
|
|
272
|
+
else if (isObject(value)) if (key === "" && Object.keys(this).length === 1) refs.forEach(dereference$1, this);
|
|
273
|
+
else {
|
|
274
|
+
parents.set(value, this);
|
|
275
|
+
keys.set(value, key);
|
|
276
|
+
}
|
|
277
|
+
return value;
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
//#endregion
|
|
282
|
+
//#region src/getDisclosureLabel.ts
|
|
283
|
+
function getDisclosureLabel(props) {
|
|
284
|
+
const { schema, isExpanded, context } = props;
|
|
285
|
+
let label;
|
|
286
|
+
if (schema.type === "array" && !!schema.items) if (schema.items.oneOf) label = tString(context.translation, "available_items").toLowerCase();
|
|
287
|
+
else label = tString(context.translation, "properties").toLowerCase();
|
|
288
|
+
else label = tString(context.translation, "properties").toLowerCase();
|
|
289
|
+
return tString(context.translation, isExpanded ? "hide" : "show", label);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
//#endregion
|
|
293
|
+
//#region src/utils.ts
|
|
294
|
+
function checkIsReference(input) {
|
|
295
|
+
return typeof input === "object" && !!input && "$ref" in input;
|
|
296
|
+
}
|
|
297
|
+
function createStateKey(key, scope) {
|
|
298
|
+
return scope ? `${scope}_${key}` : key;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Check if an object has a description. Either at the root level or in items.
|
|
302
|
+
*/
|
|
303
|
+
function hasDescription(object) {
|
|
304
|
+
return "description" in object || "x-gitbook-description-html" in object;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Resolve the description of an object.
|
|
308
|
+
*/
|
|
309
|
+
function resolveDescription(object) {
|
|
310
|
+
if (hasDescription(object)) return "x-gitbook-description-html" in object && typeof object["x-gitbook-description-html"] === "string" ? object["x-gitbook-description-html"].trim() : typeof object.description === "string" ? object.description.trim() : void 0;
|
|
311
|
+
if ("items" in object && typeof object.items === "object" && hasDescription(object.items)) return resolveDescription(object.items);
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Extract descriptions from an object.
|
|
315
|
+
*/
|
|
316
|
+
function extractDescriptions(object) {
|
|
317
|
+
return {
|
|
318
|
+
description: object.description,
|
|
319
|
+
"x-gitbook-description-html": "x-gitbook-description-html" in object ? object["x-gitbook-description-html"] : void 0
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Resolve the first example from an object.
|
|
324
|
+
*/
|
|
325
|
+
function resolveFirstExample(object) {
|
|
326
|
+
if ("examples" in object && typeof object.examples === "object" && object.examples) {
|
|
327
|
+
const firstKey = Object.keys(object.examples)[0];
|
|
328
|
+
if (firstKey && object.examples[firstKey]) return formatExample(object.examples[firstKey]);
|
|
329
|
+
}
|
|
330
|
+
if (shouldDisplayExample(object)) return formatExample(object.example);
|
|
331
|
+
if (object.items && typeof object.items === "object") return formatExample(object.items.example);
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Resolve the schema of a parameter.
|
|
335
|
+
* Extract the description, example and deprecated from parameter.
|
|
336
|
+
*/
|
|
337
|
+
function resolveParameterSchema(parameter) {
|
|
338
|
+
const schema = checkIsReference(parameter.schema) ? void 0 : parameter.schema;
|
|
339
|
+
return {
|
|
340
|
+
...extractDescriptions(parameter),
|
|
341
|
+
example: resolveFirstExample(parameter),
|
|
342
|
+
deprecated: parameter.deprecated,
|
|
343
|
+
...schema
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Transform a parameter object to a property object.
|
|
348
|
+
*/
|
|
349
|
+
function parameterToProperty(parameter) {
|
|
350
|
+
if (checkIsReference(parameter)) return {
|
|
351
|
+
propertyName: parameter.$ref ?? "Unknown ref",
|
|
352
|
+
schema: {},
|
|
353
|
+
required: void 0
|
|
354
|
+
};
|
|
355
|
+
return {
|
|
356
|
+
propertyName: parameter.name,
|
|
357
|
+
schema: resolveParameterSchema(parameter),
|
|
358
|
+
required: parameter.required
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Format the example of a schema.
|
|
363
|
+
*/
|
|
364
|
+
function formatExample(example) {
|
|
365
|
+
if (typeof example === "string") return example.replace(/\n/g, " ").replace(/\s+/g, " ").replace(/([\{\}:,])\s+/g, "$1 ").replace(/\s+([\{\}:,])/g, " $1").trim();
|
|
366
|
+
return stringifyOpenAPI(example);
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Check if an example should be displayed.
|
|
370
|
+
*/
|
|
371
|
+
function shouldDisplayExample(schema) {
|
|
372
|
+
return typeof schema.example === "string" && !!schema.example || typeof schema.example === "number" || typeof schema.example === "boolean" || Array.isArray(schema.example) && schema.example.length > 0 || typeof schema.example === "object" && schema.example !== null && Object.keys(schema.example).length > 0;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Get the class name for a status code.
|
|
376
|
+
* 1xx: informational
|
|
377
|
+
* 2xx: success
|
|
378
|
+
* 3xx: redirect
|
|
379
|
+
* 4xx, 5xx: error
|
|
380
|
+
*/
|
|
381
|
+
function getStatusCodeClassName(statusCode) {
|
|
382
|
+
switch (getStatusCodeCategory(statusCode)) {
|
|
383
|
+
case 1: return "informational";
|
|
384
|
+
case 2: return "success";
|
|
385
|
+
case 3: return "redirect";
|
|
386
|
+
case 4:
|
|
387
|
+
case 5: return "error";
|
|
388
|
+
default: return "unknown";
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Get a default label for a status code.
|
|
393
|
+
* This is used when there is no label provided in the OpenAPI spec.
|
|
394
|
+
* 1xx: Information
|
|
395
|
+
* 2xx: Success
|
|
396
|
+
* 3xx: Redirect
|
|
397
|
+
* 4xx, 5xx: Error
|
|
398
|
+
*/
|
|
399
|
+
function getStatusCodeDefaultLabel(statusCode, context) {
|
|
400
|
+
switch (getStatusCodeCategory(statusCode)) {
|
|
401
|
+
case 1: return tString(context.translation, "information");
|
|
402
|
+
case 2: return tString(context.translation, "success");
|
|
403
|
+
case 3: return tString(context.translation, "redirect");
|
|
404
|
+
case 4:
|
|
405
|
+
case 5: return tString(context.translation, "error");
|
|
406
|
+
default: return "";
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function getStatusCodeCategory(statusCode) {
|
|
410
|
+
const code = typeof statusCode === "string" ? Number.parseInt(statusCode, 10) : statusCode;
|
|
411
|
+
if (Number.isNaN(code) || code < 100 || code >= 600) return "unknown";
|
|
412
|
+
return Math.floor(code / 100);
|
|
413
|
+
}
|
|
414
|
+
function getSchemaTitle(schema) {
|
|
415
|
+
let type = "any";
|
|
416
|
+
if (schema.enum || schema["x-enumDescriptions"] || schema["x-gitbook-enum"]) type = `${schema.type} · enum`;
|
|
417
|
+
else if (schema.type === "array" && !!schema.items) type = `${getSchemaTitle(schema.items)}[]`;
|
|
418
|
+
else if (Array.isArray(schema.type)) type = schema.type.join(" | ");
|
|
419
|
+
else if (schema.type || schema.properties) {
|
|
420
|
+
type = schema.type ?? "object";
|
|
421
|
+
if (schema.format) type += ` · ${schema.format}`;
|
|
422
|
+
if (type === "object" && schema.title) type += ` · ${schema.title.replaceAll(" ", "")}`;
|
|
423
|
+
}
|
|
424
|
+
if ("anyOf" in schema) type = "any of";
|
|
425
|
+
else if ("oneOf" in schema) type = "one of";
|
|
426
|
+
else if ("allOf" in schema) type = "all of";
|
|
427
|
+
else if ("not" in schema) type = "not";
|
|
428
|
+
return type;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Extract security information for an operation based on its security requirements and the spec security schemes.
|
|
432
|
+
*/
|
|
433
|
+
function extractOperationSecurityInfo(args) {
|
|
434
|
+
const { securityRequirement, securities } = args;
|
|
435
|
+
const securitiesMap = new Map(securities);
|
|
436
|
+
if (!securityRequirement || securityRequirement.length === 0) return securities.map(([key, security]) => ({
|
|
437
|
+
key,
|
|
438
|
+
label: key,
|
|
439
|
+
schemes: [security]
|
|
440
|
+
}));
|
|
441
|
+
return securityRequirement.map((requirement, idx) => {
|
|
442
|
+
const schemeKeys = Object.keys(requirement);
|
|
443
|
+
return {
|
|
444
|
+
key: `security-${idx}`,
|
|
445
|
+
label: schemeKeys.join(" & "),
|
|
446
|
+
schemes: schemeKeys.map((schemeKey) => securitiesMap.get(schemeKey)).filter((s) => s !== void 0)
|
|
447
|
+
};
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
//#endregion
|
|
452
|
+
//#region src/OpenAPISchema.tsx
|
|
453
|
+
/**
|
|
454
|
+
* Render a property of an OpenAPI schema.
|
|
455
|
+
*/
|
|
456
|
+
function OpenAPISchemaProperty(props) {
|
|
457
|
+
const { circularRefs: parentCircularRefs, context, className, property,...rest } = props;
|
|
458
|
+
const { schema } = property;
|
|
459
|
+
const id = useId();
|
|
460
|
+
const circularRefId = parentCircularRefs.get(schema);
|
|
461
|
+
if (circularRefId) return <OpenAPISchemaCircularRef id={circularRefId} schema={schema} />;
|
|
462
|
+
const circularRefs = new Map(parentCircularRefs);
|
|
463
|
+
circularRefs.set(schema, id);
|
|
464
|
+
const properties = getSchemaProperties(schema);
|
|
465
|
+
const alternatives = getSchemaAlternatives(schema, new Set(circularRefs.keys()));
|
|
466
|
+
const header = <OpenAPISchemaPresentation context={context} property={property} />;
|
|
467
|
+
const content = (() => {
|
|
468
|
+
if (alternatives?.schemas) {
|
|
469
|
+
const { schemas, discriminator } = alternatives;
|
|
470
|
+
return <div className="openapi-schema-alternatives">
|
|
471
|
+
{schemas.map((alternativeSchema, index) => <div key={index} className="openapi-schema-alternative">
|
|
472
|
+
<OpenAPISchemaAlternative schema={alternativeSchema} discriminator={discriminator} circularRefs={circularRefs} context={context} />
|
|
473
|
+
{index < schemas.length - 1 ? <OpenAPISchemaAlternativeSeparator schema={schema} context={context} /> : null}
|
|
474
|
+
</div>)}
|
|
475
|
+
</div>;
|
|
476
|
+
}
|
|
477
|
+
if (properties?.length) return <OpenAPISchemaProperties$1 properties={properties} circularRefs={circularRefs} context={context} />;
|
|
478
|
+
return null;
|
|
479
|
+
})();
|
|
480
|
+
if (properties?.length) return <OpenAPIDisclosure icon={context.icons.plus} className={clsx("openapi-schema", className)} header={header} label={(isExpanded) => getDisclosureLabel({
|
|
481
|
+
schema,
|
|
482
|
+
isExpanded,
|
|
483
|
+
context
|
|
484
|
+
})} {...rest}>
|
|
485
|
+
{content}
|
|
486
|
+
</OpenAPIDisclosure>;
|
|
487
|
+
return <div id={id} {...rest} className={clsx("openapi-schema", className)}>
|
|
488
|
+
{header}
|
|
489
|
+
{content}
|
|
490
|
+
</div>;
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Render a set of properties of an OpenAPI schema.
|
|
494
|
+
*/
|
|
495
|
+
function OpenAPISchemaProperties$1(props) {
|
|
496
|
+
const { id, properties, circularRefs = /* @__PURE__ */ new Map(), context } = props;
|
|
497
|
+
return <div id={id} className="openapi-schema-properties">
|
|
498
|
+
{properties.map((property, index) => {
|
|
499
|
+
return <OpenAPISchemaProperty key={index} circularRefs={circularRefs} property={property} context={context} style={{ animationDelay: `${index * .02}s` }} />;
|
|
500
|
+
})}
|
|
501
|
+
</div>;
|
|
502
|
+
}
|
|
503
|
+
function OpenAPISchemaPropertiesFromServer(props) {
|
|
504
|
+
return <OpenAPISchemaProperties$1 id={props.id} properties={JSON.parse(props.properties, retrocycle())} context={props.context} />;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Render a root schema (such as the request body or response body).
|
|
508
|
+
*/
|
|
509
|
+
function OpenAPIRootSchema$1(props) {
|
|
510
|
+
const { schema, context, circularRefs: parentCircularRefs = /* @__PURE__ */ new Map() } = props;
|
|
511
|
+
const id = useId();
|
|
512
|
+
const properties = getSchemaProperties(schema);
|
|
513
|
+
const description = resolveDescription(schema);
|
|
514
|
+
if (properties?.length) {
|
|
515
|
+
const circularRefs = new Map(parentCircularRefs);
|
|
516
|
+
circularRefs.set(schema, id);
|
|
517
|
+
return <>
|
|
518
|
+
{description ? <Markdown source={description} className="openapi-schema-root-description" /> : null}
|
|
519
|
+
<OpenAPISchemaProperties$1 properties={properties} circularRefs={circularRefs} context={context} />
|
|
520
|
+
</>;
|
|
521
|
+
}
|
|
522
|
+
return <OpenAPISchemaProperty className="openapi-schema-root" property={{ schema }} context={context} circularRefs={parentCircularRefs} />;
|
|
523
|
+
}
|
|
524
|
+
function OpenAPIRootSchemaFromServer(props) {
|
|
525
|
+
return <OpenAPIRootSchema$1 schema={JSON.parse(props.schema, retrocycle())} context={props.context} />;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Render a tab for an alternative schema.
|
|
529
|
+
* It renders directly the properties if relevant;
|
|
530
|
+
* for primitives, it renders the schema itself.
|
|
531
|
+
*/
|
|
532
|
+
function OpenAPISchemaAlternative(props) {
|
|
533
|
+
const { schema, discriminator, circularRefs, context } = props;
|
|
534
|
+
const properties = getSchemaProperties(schema, discriminator);
|
|
535
|
+
return properties?.length ? <OpenAPIDisclosure icon={context.icons.plus} header={<OpenAPISchemaPresentation property={{ schema }} context={context} />} label={(isExpanded) => getDisclosureLabel({
|
|
536
|
+
schema,
|
|
537
|
+
isExpanded,
|
|
538
|
+
context
|
|
539
|
+
})}>
|
|
540
|
+
<OpenAPISchemaProperties$1 properties={properties} circularRefs={circularRefs} context={context} />
|
|
541
|
+
</OpenAPIDisclosure> : <OpenAPISchemaProperty property={{ schema }} circularRefs={circularRefs} context={context} />;
|
|
542
|
+
}
|
|
543
|
+
function OpenAPISchemaAlternativeSeparator(props) {
|
|
544
|
+
const { schema, context } = props;
|
|
545
|
+
const anyOf = schema.anyOf || schema.items?.anyOf;
|
|
546
|
+
const oneOf = schema.oneOf || schema.items?.oneOf;
|
|
547
|
+
const allOf = schema.allOf || schema.items?.allOf;
|
|
548
|
+
if (!anyOf && !oneOf && !allOf) return null;
|
|
549
|
+
return <span className="openapi-schema-alternative-separator">
|
|
550
|
+
{(anyOf || oneOf) && tString(context.translation, "or")}
|
|
551
|
+
{allOf && tString(context.translation, "and")}
|
|
552
|
+
</span>;
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Render a circular reference to a schema.
|
|
556
|
+
*/
|
|
557
|
+
function OpenAPISchemaCircularRef(props) {
|
|
558
|
+
const { id, schema } = props;
|
|
559
|
+
return <div className="openapi-schema-circular">
|
|
560
|
+
Circular reference to <a href={`#${id}`}>{getSchemaTitle(schema)}</a>{" "}
|
|
561
|
+
<span className="openapi-schema-circular-glyph">↩</span>
|
|
562
|
+
</div>;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Render the enum value for a schema.
|
|
566
|
+
*/
|
|
567
|
+
function OpenAPISchemaEnum(props) {
|
|
568
|
+
const { schema, context } = props;
|
|
569
|
+
const enumValues = (() => {
|
|
570
|
+
if (schema["x-gitbook-enum"]) return Object.entries(schema["x-gitbook-enum"]).map(([name, { description }]) => {
|
|
571
|
+
return {
|
|
572
|
+
value: name,
|
|
573
|
+
description
|
|
574
|
+
};
|
|
575
|
+
});
|
|
576
|
+
if (schema["x-enumDescriptions"]) return Object.entries(schema["x-enumDescriptions"]).map(([value, description]) => {
|
|
577
|
+
return {
|
|
578
|
+
value,
|
|
579
|
+
description
|
|
580
|
+
};
|
|
581
|
+
});
|
|
582
|
+
return schema.enum?.map((value) => {
|
|
583
|
+
return {
|
|
584
|
+
value,
|
|
585
|
+
description: void 0
|
|
586
|
+
};
|
|
587
|
+
});
|
|
588
|
+
})();
|
|
589
|
+
if (!enumValues?.length) return null;
|
|
590
|
+
return <span className="openapi-schema-enum">
|
|
591
|
+
{tString(context.translation, "possible_values")}:{" "}
|
|
592
|
+
{enumValues.map((item, index) => <span key={index} className="openapi-schema-enum-value">
|
|
593
|
+
<OpenAPICopyButton value={item.value} label={item.description} withTooltip={!!item.description} context={context}>
|
|
594
|
+
<code>{`${item.value}`}</code>
|
|
595
|
+
</OpenAPICopyButton>
|
|
596
|
+
</span>)}
|
|
597
|
+
</span>;
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Render the top row of a schema. e.g: name, type, and required status.
|
|
601
|
+
*/
|
|
602
|
+
function OpenAPISchemaPresentation(props) {
|
|
603
|
+
const { property: { schema, propertyName, required, isDiscriminatorProperty }, context } = props;
|
|
604
|
+
const description = resolveDescription(schema);
|
|
605
|
+
const example = resolveFirstExample(schema);
|
|
606
|
+
return <div className="openapi-schema-presentation">
|
|
607
|
+
<OpenAPISchemaName schema={schema} type={getSchemaTitle(schema)} propertyName={propertyName} isDiscriminatorProperty={isDiscriminatorProperty} required={required} context={context} />
|
|
608
|
+
{typeof schema["x-deprecated-sunset"] === "string" ? <div className="openapi-deprecated-sunset openapi-schema-description openapi-markdown">
|
|
609
|
+
Sunset date:{" "}
|
|
610
|
+
<span className="openapi-deprecated-sunset-date">
|
|
611
|
+
{schema["x-deprecated-sunset"]}
|
|
612
|
+
</span>
|
|
613
|
+
</div> : null}
|
|
614
|
+
{description ? <Markdown source={description} className="openapi-schema-description" /> : null}
|
|
615
|
+
{schema.default !== void 0 ? <span className="openapi-schema-default">
|
|
616
|
+
Default:{" "}
|
|
617
|
+
<code>
|
|
618
|
+
{typeof schema.default === "string" && schema.default ? schema.default : stringifyOpenAPI(schema.default)}
|
|
619
|
+
</code>
|
|
620
|
+
</span> : null}
|
|
621
|
+
{typeof example === "string" ? <span className="openapi-schema-example">
|
|
622
|
+
Example: <code>{example}</code>
|
|
623
|
+
</span> : null}
|
|
624
|
+
{schema.pattern ? <span className="openapi-schema-pattern">
|
|
625
|
+
Pattern: <code>{schema.pattern}</code>
|
|
626
|
+
</span> : null}
|
|
627
|
+
<OpenAPISchemaEnum schema={schema} context={context} />
|
|
628
|
+
</div>;
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Get the sub-properties of a schema.
|
|
632
|
+
*/
|
|
633
|
+
function getSchemaProperties(schema, discriminator) {
|
|
634
|
+
if (schema.type === "array" && schema.items && !checkIsReference(schema.items)) {
|
|
635
|
+
const items = schema.items;
|
|
636
|
+
const itemProperties = getSchemaProperties(items);
|
|
637
|
+
if (itemProperties) return itemProperties.map((prop) => ({
|
|
638
|
+
...prop,
|
|
639
|
+
isDiscriminatorProperty: discriminator?.propertyName === prop.propertyName
|
|
640
|
+
}));
|
|
641
|
+
if ((items.type === "string" || items.type === "number" || items.type === "boolean" || items.type === "integer") && !items.enum) return null;
|
|
642
|
+
return [{
|
|
643
|
+
propertyName: "items",
|
|
644
|
+
schema: items
|
|
645
|
+
}];
|
|
646
|
+
}
|
|
647
|
+
if (schema.type === "object" || schema.properties) {
|
|
648
|
+
const result = [];
|
|
649
|
+
if (schema.properties) Object.entries(schema.properties).forEach(([propertyName, propertySchema]) => {
|
|
650
|
+
if (checkIsReference(propertySchema)) return;
|
|
651
|
+
result.push({
|
|
652
|
+
propertyName,
|
|
653
|
+
required: Array.isArray(schema.required) ? schema.required.includes(propertyName) : void 0,
|
|
654
|
+
isDiscriminatorProperty: discriminator?.propertyName === propertyName,
|
|
655
|
+
schema: propertySchema
|
|
656
|
+
});
|
|
657
|
+
});
|
|
658
|
+
if (schema.additionalProperties && !checkIsReference(schema.additionalProperties)) result.push({
|
|
659
|
+
propertyName: "Other properties",
|
|
660
|
+
schema: schema.additionalProperties === true ? {} : schema.additionalProperties
|
|
661
|
+
});
|
|
662
|
+
return result;
|
|
663
|
+
}
|
|
664
|
+
return null;
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Get the alternatives to display for a schema.
|
|
668
|
+
*/
|
|
669
|
+
function getSchemaAlternatives(schema, ancestors = /* @__PURE__ */ new Set()) {
|
|
670
|
+
if (schema.items && ("oneOf" in schema.items || "allOf" in schema.items || "anyOf" in schema.items)) return getSchemaAlternatives(schema.items, ancestors);
|
|
671
|
+
const alternatives = (() => {
|
|
672
|
+
if (schema.anyOf) return [
|
|
673
|
+
"anyOf",
|
|
674
|
+
schema.anyOf,
|
|
675
|
+
schema.discriminator
|
|
676
|
+
];
|
|
677
|
+
if (schema.oneOf) return [
|
|
678
|
+
"oneOf",
|
|
679
|
+
schema.oneOf,
|
|
680
|
+
schema.discriminator
|
|
681
|
+
];
|
|
682
|
+
if (schema.allOf) return [
|
|
683
|
+
"allOf",
|
|
684
|
+
schema.allOf,
|
|
685
|
+
schema.discriminator
|
|
686
|
+
];
|
|
687
|
+
return null;
|
|
688
|
+
})();
|
|
689
|
+
if (!alternatives) return null;
|
|
690
|
+
const [type, schemas, discriminator] = alternatives;
|
|
691
|
+
return {
|
|
692
|
+
type,
|
|
693
|
+
schemas: mergeAlternatives(type, flattenAlternatives(type, schemas, new Set(ancestors).add(schema))) ?? [],
|
|
694
|
+
discriminator
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
const safeExtensions = [
|
|
698
|
+
"description",
|
|
699
|
+
"title",
|
|
700
|
+
"example",
|
|
701
|
+
"examples",
|
|
702
|
+
"default",
|
|
703
|
+
"readOnly",
|
|
704
|
+
"writeOnly",
|
|
705
|
+
"deprecated"
|
|
706
|
+
];
|
|
707
|
+
/**
|
|
708
|
+
* Determine if a schema is safe to merge based on its properties
|
|
709
|
+
*/
|
|
710
|
+
function isSafeToMerge(schema) {
|
|
711
|
+
const keys = Object.keys(schema);
|
|
712
|
+
const coreProperties = [
|
|
713
|
+
"type",
|
|
714
|
+
"properties",
|
|
715
|
+
"required",
|
|
716
|
+
"nullable"
|
|
717
|
+
];
|
|
718
|
+
const coreKeys = keys.filter((key) => coreProperties.includes(key));
|
|
719
|
+
const unknownKeys = keys.filter((key) => !coreProperties.includes(key) && !safeExtensions.includes(key) && !key.startsWith("x-"));
|
|
720
|
+
return coreKeys.length > 0 && unknownKeys.length === 0;
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Merge alternatives of the same type into a single schema.
|
|
724
|
+
* - Merge string enums
|
|
725
|
+
* - Safely merge object schemas with compatible properties
|
|
726
|
+
*/
|
|
727
|
+
function mergeAlternatives(alternativeType, schemasOrRefs) {
|
|
728
|
+
switch (alternativeType) {
|
|
729
|
+
case "oneOf": return schemasOrRefs.reduce((acc, schemaOrRef) => {
|
|
730
|
+
const latest = acc.at(-1);
|
|
731
|
+
if (latest && latest.type === "string" && latest.enum && schemaOrRef.type === "string" && schemaOrRef.enum) {
|
|
732
|
+
latest.enum = Array.from(new Set([...latest.enum, ...schemaOrRef.enum]));
|
|
733
|
+
latest.nullable = latest.nullable || schemaOrRef.nullable;
|
|
734
|
+
return acc;
|
|
735
|
+
}
|
|
736
|
+
acc.push(schemaOrRef);
|
|
737
|
+
return acc;
|
|
738
|
+
}, []);
|
|
739
|
+
case "allOf": return schemasOrRefs.reduce((acc, schemaOrRef) => {
|
|
740
|
+
const latest = acc.at(-1);
|
|
741
|
+
if (latest && latest.type === "string" && latest.enum && schemaOrRef.type === "string" && schemaOrRef.enum) {
|
|
742
|
+
if (Object.keys(schemaOrRef).every((key) => [
|
|
743
|
+
"type",
|
|
744
|
+
"enum",
|
|
745
|
+
"nullable"
|
|
746
|
+
].includes(key))) {
|
|
747
|
+
latest.enum = Array.from(new Set([...latest.enum, ...schemaOrRef.enum]));
|
|
748
|
+
latest.nullable = latest.nullable || schemaOrRef.nullable;
|
|
749
|
+
return acc;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
if (latest && latest.type === "object" && schemaOrRef.type === "object") {
|
|
753
|
+
const keys = Object.keys(schemaOrRef);
|
|
754
|
+
if (isSafeToMerge(schemaOrRef)) {
|
|
755
|
+
const safeKeys = keys.filter((key) => safeExtensions.includes(key));
|
|
756
|
+
const vendorKeys = keys.filter((key) => key.startsWith("x-"));
|
|
757
|
+
latest.properties = {
|
|
758
|
+
...latest.properties || {},
|
|
759
|
+
...schemaOrRef.properties || {}
|
|
760
|
+
};
|
|
761
|
+
latest.required = Array.from(new Set([...latest.required && Array.isArray(latest.required) ? latest.required : [], ...schemaOrRef.required && Array.isArray(schemaOrRef.required) ? schemaOrRef.required : []]));
|
|
762
|
+
latest.nullable = latest.nullable || schemaOrRef.nullable;
|
|
763
|
+
[...vendorKeys, ...safeKeys].forEach((key) => {
|
|
764
|
+
if (typeof latest[key] === "object" && typeof schemaOrRef[key] === "object") latest[key] = {
|
|
765
|
+
...latest[key],
|
|
766
|
+
...schemaOrRef[key]
|
|
767
|
+
};
|
|
768
|
+
else latest[key] = schemaOrRef[key];
|
|
769
|
+
});
|
|
770
|
+
return acc;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
acc.push(schemaOrRef);
|
|
774
|
+
return acc;
|
|
775
|
+
}, []);
|
|
776
|
+
default: return schemasOrRefs;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
function flattenAlternatives(alternativeType, schemasOrRefs, ancestors) {
|
|
780
|
+
const latestAncestor = Array.from(ancestors).pop();
|
|
781
|
+
return schemasOrRefs.reduce((acc, schemaOrRef) => {
|
|
782
|
+
if (checkIsReference(schemaOrRef)) return acc;
|
|
783
|
+
if (schemaOrRef[alternativeType] && !ancestors.has(schemaOrRef)) {
|
|
784
|
+
const alternatives = getSchemaAlternatives(schemaOrRef, ancestors);
|
|
785
|
+
if (alternatives?.schemas) acc.push(...alternatives.schemas.map((schema$1) => ({
|
|
786
|
+
...schema$1,
|
|
787
|
+
required: mergeRequiredFields(schema$1, latestAncestor)
|
|
788
|
+
})));
|
|
789
|
+
return acc;
|
|
790
|
+
}
|
|
791
|
+
const schema = {
|
|
792
|
+
...schemaOrRef,
|
|
793
|
+
required: mergeRequiredFields(schemaOrRef, latestAncestor)
|
|
794
|
+
};
|
|
795
|
+
acc.push(schema);
|
|
796
|
+
return acc;
|
|
797
|
+
}, []);
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Merge the required fields of a schema with the required fields of its latest ancestor.
|
|
801
|
+
*/
|
|
802
|
+
function mergeRequiredFields(schemaOrRef, latestAncestor) {
|
|
803
|
+
if (!schemaOrRef.required && !latestAncestor?.required) return;
|
|
804
|
+
if (checkIsReference(schemaOrRef)) return latestAncestor?.required;
|
|
805
|
+
return Array.from(new Set([...latestAncestor?.required || [], ...schemaOrRef.required || []]));
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
//#endregion
|
|
809
|
+
//#region src/OpenAPISchemaServer.tsx
|
|
810
|
+
function OpenAPISchemaProperties(props) {
|
|
811
|
+
return <OpenAPISchemaPropertiesFromServer id={props.id} properties={JSON.stringify(props.properties, decycle())} context={props.context} />;
|
|
812
|
+
}
|
|
813
|
+
function OpenAPIRootSchema(props) {
|
|
814
|
+
return <OpenAPIRootSchemaFromServer schema={JSON.stringify(props.schema, decycle())} context={props.context} />;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
//#endregion
|
|
818
|
+
//#region src/StaticSection.tsx
|
|
819
|
+
function Section(props) {
|
|
820
|
+
return <div {...props} className={clsx("openapi-section", props.className)} />;
|
|
821
|
+
}
|
|
822
|
+
function SectionHeader(props) {
|
|
823
|
+
return <div {...props} className={clsx("openapi-section-header", props.className ? `${props.className}-header` : void 0)} />;
|
|
824
|
+
}
|
|
825
|
+
function SectionHeaderContent(props) {
|
|
826
|
+
return <div {...props} className={clsx("openapi-section-header-content", props.className && `${props.className}-header-content`)} />;
|
|
827
|
+
}
|
|
828
|
+
const SectionBody = forwardRef(function SectionBody$1(props, ref) {
|
|
829
|
+
return <div ref={ref} {...props} className={clsx("openapi-section-body", props.className && `${props.className}-body`)} />;
|
|
830
|
+
});
|
|
831
|
+
function SectionFooter(props) {
|
|
832
|
+
return <div {...props} className={clsx("openapi-section-footer", props.className && `${props.className}-footer`)} />;
|
|
833
|
+
}
|
|
834
|
+
function SectionFooterContent(props) {
|
|
835
|
+
return <div {...props} className={clsx("openapi-section-footer-content", props.className && `${props.className}-footer-content`)} />;
|
|
836
|
+
}
|
|
837
|
+
function StaticSection(props) {
|
|
838
|
+
const { className, header, children, footer } = props;
|
|
839
|
+
return <Section className={className}>
|
|
840
|
+
{header ? <SectionHeader className={className}>
|
|
841
|
+
<SectionHeaderContent className={className}>{header}</SectionHeaderContent>
|
|
842
|
+
</SectionHeader> : null}
|
|
843
|
+
<SectionBody className={className}>{children}</SectionBody>
|
|
844
|
+
{footer ? <SectionFooter className={className}>
|
|
845
|
+
<SectionFooterContent className={className}>{footer}</SectionFooterContent>
|
|
846
|
+
</SectionFooter> : null}
|
|
847
|
+
</Section>;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
//#endregion
|
|
851
|
+
//#region src/translations/de.ts
|
|
852
|
+
const de = {
|
|
853
|
+
required: "Erforderlich",
|
|
854
|
+
deprecated: "Veraltet",
|
|
855
|
+
deprecated_and_sunset_on: "Diese Operation ist veraltet und wird am ${1} eingestellt.",
|
|
856
|
+
stability_experimental: "Experimentell",
|
|
857
|
+
stability_alpha: "Alpha",
|
|
858
|
+
stability_beta: "Beta",
|
|
859
|
+
discriminator: "Diskriminator",
|
|
860
|
+
copy_to_clipboard: "In die Zwischenablage kopieren",
|
|
861
|
+
copied: "Kopiert",
|
|
862
|
+
no_content: "Kein Inhalt",
|
|
863
|
+
unresolved_reference: "Nicht aufgelöste Referenz",
|
|
864
|
+
circular_reference: "Zirkuläre Referenz",
|
|
865
|
+
read_only: "Nur lesen",
|
|
866
|
+
write_only: "Nur schreiben",
|
|
867
|
+
optional: "Optional",
|
|
868
|
+
min: "Min",
|
|
869
|
+
max: "Max",
|
|
870
|
+
nullable: "Nullfähig",
|
|
871
|
+
body: "Rumpf",
|
|
872
|
+
payload: "Nutzlast",
|
|
873
|
+
headers: "Header",
|
|
874
|
+
header: "Header",
|
|
875
|
+
authorizations: "Autorisierungen",
|
|
876
|
+
responses: "Antworten",
|
|
877
|
+
response: "Antwort",
|
|
878
|
+
path_parameters: "Pfadparameter",
|
|
879
|
+
query_parameters: "Abfrageparameter",
|
|
880
|
+
header_parameters: "Header-Parameter",
|
|
881
|
+
attributes: "Attribute",
|
|
882
|
+
test_it: "Teste es",
|
|
883
|
+
information: "Information",
|
|
884
|
+
success: "Erfolg",
|
|
885
|
+
redirect: "Umleitung",
|
|
886
|
+
error: "Fehler",
|
|
887
|
+
show: "Zeige ${1}",
|
|
888
|
+
hide: "Verstecke ${1}",
|
|
889
|
+
available_items: "Verfügbare Elemente",
|
|
890
|
+
required_scopes: "Erforderliche Scopes",
|
|
891
|
+
properties: "Eigenschaften",
|
|
892
|
+
or: "oder",
|
|
893
|
+
and: "und",
|
|
894
|
+
possible_values: "Mögliche Werte"
|
|
895
|
+
};
|
|
896
|
+
|
|
897
|
+
//#endregion
|
|
898
|
+
//#region src/translations/en.ts
|
|
899
|
+
const en = {
|
|
900
|
+
required: "Required",
|
|
901
|
+
deprecated: "Deprecated",
|
|
902
|
+
deprecated_and_sunset_on: "This operation is deprecated and will be sunset on ${1}.",
|
|
903
|
+
stability_experimental: "Experimental",
|
|
904
|
+
stability_alpha: "Alpha",
|
|
905
|
+
stability_beta: "Beta",
|
|
906
|
+
discriminator: "Discriminator",
|
|
907
|
+
copy_to_clipboard: "Copy to clipboard",
|
|
908
|
+
copied: "Copied",
|
|
909
|
+
no_content: "No content",
|
|
910
|
+
unresolved_reference: "Unresolved reference",
|
|
911
|
+
circular_reference: "Circular reference",
|
|
912
|
+
read_only: "Read-only",
|
|
913
|
+
write_only: "Write-only",
|
|
914
|
+
optional: "Optional",
|
|
915
|
+
min: "Min",
|
|
916
|
+
max: "Max",
|
|
917
|
+
nullable: "Nullable",
|
|
918
|
+
body: "Body",
|
|
919
|
+
payload: "Payload",
|
|
920
|
+
headers: "Headers",
|
|
921
|
+
header: "Header",
|
|
922
|
+
authorizations: "Authorizations",
|
|
923
|
+
responses: "Responses",
|
|
924
|
+
response: "Response",
|
|
925
|
+
path_parameters: "Path parameters",
|
|
926
|
+
query_parameters: "Query parameters",
|
|
927
|
+
header_parameters: "Header parameters",
|
|
928
|
+
attributes: "Attributes",
|
|
929
|
+
test_it: "Test it",
|
|
930
|
+
information: "Information",
|
|
931
|
+
success: "Success",
|
|
932
|
+
redirect: "Redirect",
|
|
933
|
+
error: "Error",
|
|
934
|
+
show: "Show ${1}",
|
|
935
|
+
hide: "Hide ${1}",
|
|
936
|
+
available_items: "Available items",
|
|
937
|
+
required_scopes: "Required scopes",
|
|
938
|
+
possible_values: "Possible values",
|
|
939
|
+
properties: "Properties",
|
|
940
|
+
or: "or",
|
|
941
|
+
and: "and"
|
|
942
|
+
};
|
|
943
|
+
|
|
944
|
+
//#endregion
|
|
945
|
+
//#region src/translations/es.ts
|
|
946
|
+
const es = {
|
|
947
|
+
required: "Requerido",
|
|
948
|
+
deprecated: "Obsoleto",
|
|
949
|
+
deprecated_and_sunset_on: "Esta operación está obsoleta y se retirará el ${1}.",
|
|
950
|
+
stability_experimental: "Experimental",
|
|
951
|
+
stability_alpha: "Alfa",
|
|
952
|
+
stability_beta: "Beta",
|
|
953
|
+
discriminator: "Discriminador",
|
|
954
|
+
copy_to_clipboard: "Copiar al portapapeles",
|
|
955
|
+
copied: "Copiado",
|
|
956
|
+
no_content: "Sin contenido",
|
|
957
|
+
unresolved_reference: "Referencia no resuelta",
|
|
958
|
+
circular_reference: "Referencia circular",
|
|
959
|
+
read_only: "Solo lectura",
|
|
960
|
+
write_only: "Solo escritura",
|
|
961
|
+
optional: "Opcional",
|
|
962
|
+
min: "Mín",
|
|
963
|
+
max: "Máx",
|
|
964
|
+
nullable: "Nulo",
|
|
965
|
+
body: "Cuerpo",
|
|
966
|
+
payload: "Caga útil",
|
|
967
|
+
headers: "Headers",
|
|
968
|
+
header: "Header",
|
|
969
|
+
authorizations: "Autorizaciones",
|
|
970
|
+
responses: "Respuestas",
|
|
971
|
+
response: "Respuesta",
|
|
972
|
+
path_parameters: "Parámetros de ruta",
|
|
973
|
+
query_parameters: "Parámetros de consulta",
|
|
974
|
+
header_parameters: "Parámetros de encabezado",
|
|
975
|
+
attributes: "Atributos",
|
|
976
|
+
test_it: "Pruébalo",
|
|
977
|
+
information: "Información",
|
|
978
|
+
success: "Éxito",
|
|
979
|
+
redirect: "Redirección",
|
|
980
|
+
error: "Error",
|
|
981
|
+
show: "Mostrar ${1}",
|
|
982
|
+
hide: "Ocultar ${1}",
|
|
983
|
+
available_items: "Elementos disponibles",
|
|
984
|
+
required_scopes: "Scopes requeridos",
|
|
985
|
+
properties: "Propiedades",
|
|
986
|
+
or: "o",
|
|
987
|
+
and: "y",
|
|
988
|
+
possible_values: "Valores posibles"
|
|
989
|
+
};
|
|
990
|
+
|
|
991
|
+
//#endregion
|
|
992
|
+
//#region src/translations/fr.ts
|
|
993
|
+
const fr = {
|
|
994
|
+
required: "Requis",
|
|
995
|
+
deprecated: "Obsolète",
|
|
996
|
+
deprecated_and_sunset_on: "Cette opération est obsolète et sera supprimée le ${1}.",
|
|
997
|
+
stability_experimental: "Expérimental",
|
|
998
|
+
stability_alpha: "Alpha",
|
|
999
|
+
stability_beta: "Bêta",
|
|
1000
|
+
discriminator: "Discriminateur",
|
|
1001
|
+
copy_to_clipboard: "Copier dans le presse-papiers",
|
|
1002
|
+
copied: "Copié",
|
|
1003
|
+
no_content: "Aucun contenu",
|
|
1004
|
+
unresolved_reference: "Référence non résolue",
|
|
1005
|
+
circular_reference: "Référence circulaire",
|
|
1006
|
+
read_only: "Lecture seule",
|
|
1007
|
+
write_only: "Écriture seule",
|
|
1008
|
+
optional: "Optionnel",
|
|
1009
|
+
min: "Min",
|
|
1010
|
+
max: "Max",
|
|
1011
|
+
nullable: "Nullable",
|
|
1012
|
+
body: "Corps",
|
|
1013
|
+
payload: "Charge utile",
|
|
1014
|
+
headers: "Headers",
|
|
1015
|
+
header: "Header",
|
|
1016
|
+
authorizations: "Autorisations",
|
|
1017
|
+
responses: "Réponses",
|
|
1018
|
+
response: "Réponse",
|
|
1019
|
+
path_parameters: "Paramètres de chemin",
|
|
1020
|
+
query_parameters: "Paramètres de requête",
|
|
1021
|
+
header_parameters: "Paramètres d'en-tête",
|
|
1022
|
+
attributes: "Attributs",
|
|
1023
|
+
test_it: "Tester",
|
|
1024
|
+
information: "Information",
|
|
1025
|
+
success: "Succès",
|
|
1026
|
+
redirect: "Redirection",
|
|
1027
|
+
error: "Erreur",
|
|
1028
|
+
show: "Afficher ${1}",
|
|
1029
|
+
hide: "Masquer ${1}",
|
|
1030
|
+
available_items: "Éléments disponibles",
|
|
1031
|
+
required_scopes: "Scopes requis",
|
|
1032
|
+
properties: "Propriétés",
|
|
1033
|
+
or: "ou",
|
|
1034
|
+
and: "et",
|
|
1035
|
+
possible_values: "Valeurs possibles"
|
|
1036
|
+
};
|
|
1037
|
+
|
|
1038
|
+
//#endregion
|
|
1039
|
+
//#region src/translations/ja.ts
|
|
1040
|
+
const ja = {
|
|
1041
|
+
required: "必須",
|
|
1042
|
+
deprecated: "非推奨",
|
|
1043
|
+
deprecated_and_sunset_on: "この操作は非推奨であり、${1}に廃止されます。",
|
|
1044
|
+
stability_experimental: "実験的",
|
|
1045
|
+
stability_alpha: "アルファ",
|
|
1046
|
+
stability_beta: "ベータ",
|
|
1047
|
+
discriminator: "識別子",
|
|
1048
|
+
copy_to_clipboard: "クリップボードにコピー",
|
|
1049
|
+
copied: "コピー済み",
|
|
1050
|
+
no_content: "コンテンツなし",
|
|
1051
|
+
unresolved_reference: "未解決の参照",
|
|
1052
|
+
circular_reference: "循環参照",
|
|
1053
|
+
read_only: "読み取り専用",
|
|
1054
|
+
write_only: "書き込み専用",
|
|
1055
|
+
optional: "オプション",
|
|
1056
|
+
min: "最小",
|
|
1057
|
+
max: "最大",
|
|
1058
|
+
nullable: "ヌル許容",
|
|
1059
|
+
body: "本文",
|
|
1060
|
+
payload: "ペイロード",
|
|
1061
|
+
headers: "ヘッダー",
|
|
1062
|
+
header: "ヘッダー",
|
|
1063
|
+
authorizations: "認可",
|
|
1064
|
+
responses: "レスポンス",
|
|
1065
|
+
response: "レスポンス",
|
|
1066
|
+
path_parameters: "パスパラメータ",
|
|
1067
|
+
query_parameters: "クエリパラメータ",
|
|
1068
|
+
header_parameters: "ヘッダーパラメータ",
|
|
1069
|
+
attributes: "属性",
|
|
1070
|
+
test_it: "テストする",
|
|
1071
|
+
information: "情報",
|
|
1072
|
+
success: "成功",
|
|
1073
|
+
redirect: "リダイレクト",
|
|
1074
|
+
error: "エラー",
|
|
1075
|
+
show: "${1}を表示",
|
|
1076
|
+
hide: "${1}を非表示",
|
|
1077
|
+
available_items: "利用可能なアイテム",
|
|
1078
|
+
required_scopes: "必須スコープ",
|
|
1079
|
+
properties: "プロパティ",
|
|
1080
|
+
or: "または",
|
|
1081
|
+
and: "かつ",
|
|
1082
|
+
possible_values: "可能な値"
|
|
1083
|
+
};
|
|
1084
|
+
|
|
1085
|
+
//#endregion
|
|
1086
|
+
//#region src/translations/nl.ts
|
|
1087
|
+
const nl = {
|
|
1088
|
+
required: "Vereist",
|
|
1089
|
+
deprecated: "Verouderd",
|
|
1090
|
+
deprecated_and_sunset_on: "Deze bewerking is verouderd en wordt op ${1} beëindigd.",
|
|
1091
|
+
stability_experimental: "Experimenteel",
|
|
1092
|
+
stability_alpha: "Alfa",
|
|
1093
|
+
stability_beta: "Bèta",
|
|
1094
|
+
discriminator: "Discriminator",
|
|
1095
|
+
copy_to_clipboard: "Kopiëren naar klembord",
|
|
1096
|
+
copied: "Gekopieerd",
|
|
1097
|
+
no_content: "Geen inhoud",
|
|
1098
|
+
unresolved_reference: "Onopgeloste verwijzing",
|
|
1099
|
+
circular_reference: "Circulaire verwijzing",
|
|
1100
|
+
read_only: "Alleen lezen",
|
|
1101
|
+
write_only: "Alleen schrijven",
|
|
1102
|
+
optional: "Optioneel",
|
|
1103
|
+
min: "Min",
|
|
1104
|
+
max: "Max",
|
|
1105
|
+
nullable: "Null toegestaan",
|
|
1106
|
+
body: "Body",
|
|
1107
|
+
payload: "Payload",
|
|
1108
|
+
headers: "Headers",
|
|
1109
|
+
header: "Header",
|
|
1110
|
+
authorizations: "Autorisaties",
|
|
1111
|
+
responses: "Reacties",
|
|
1112
|
+
response: "Reactie",
|
|
1113
|
+
path_parameters: "Padparameters",
|
|
1114
|
+
query_parameters: "Queryparameters",
|
|
1115
|
+
header_parameters: "Headerparameters",
|
|
1116
|
+
attributes: "Attributen",
|
|
1117
|
+
test_it: "Test het",
|
|
1118
|
+
information: "Informatie",
|
|
1119
|
+
success: "Succes",
|
|
1120
|
+
redirect: "Omleiding",
|
|
1121
|
+
error: "Fout",
|
|
1122
|
+
show: "Toon ${1}",
|
|
1123
|
+
hide: "Verberg ${1}",
|
|
1124
|
+
available_items: "Beschikbare items",
|
|
1125
|
+
required_scopes: "Vereiste scopes",
|
|
1126
|
+
properties: "Eigenschappen",
|
|
1127
|
+
or: "of",
|
|
1128
|
+
and: "en",
|
|
1129
|
+
possible_values: "Mogelijke waarden"
|
|
1130
|
+
};
|
|
1131
|
+
|
|
1132
|
+
//#endregion
|
|
1133
|
+
//#region src/translations/no.ts
|
|
1134
|
+
const no = {
|
|
1135
|
+
required: "Påkrevd",
|
|
1136
|
+
deprecated: "Foreldet",
|
|
1137
|
+
deprecated_and_sunset_on: "Denne operasjonen er foreldet og vil bli avviklet den ${1}.",
|
|
1138
|
+
stability_experimental: "Eksperimentell",
|
|
1139
|
+
stability_alpha: "Alfa",
|
|
1140
|
+
stability_beta: "Beta",
|
|
1141
|
+
discriminator: "Diskriminator",
|
|
1142
|
+
copy_to_clipboard: "Kopier til utklippstavle",
|
|
1143
|
+
copied: "Kopiert",
|
|
1144
|
+
no_content: "Ingen innhold",
|
|
1145
|
+
unresolved_reference: "Uavklart referanse",
|
|
1146
|
+
circular_reference: "Sirkulær referanse",
|
|
1147
|
+
read_only: "Skrivebeskyttet",
|
|
1148
|
+
write_only: "Kun skriving",
|
|
1149
|
+
optional: "Valgfri",
|
|
1150
|
+
min: "Min",
|
|
1151
|
+
max: "Maks",
|
|
1152
|
+
nullable: "Kan være null",
|
|
1153
|
+
body: "Brødtekst",
|
|
1154
|
+
payload: "Nyttelast",
|
|
1155
|
+
headers: "Headers",
|
|
1156
|
+
header: "Header",
|
|
1157
|
+
authorizations: "Autorisasjoner",
|
|
1158
|
+
responses: "Responser",
|
|
1159
|
+
response: "Respons",
|
|
1160
|
+
path_parameters: "Sti-parametere",
|
|
1161
|
+
query_parameters: "Forespørselsparametere",
|
|
1162
|
+
header_parameters: "Header-parametere",
|
|
1163
|
+
attributes: "Attributter",
|
|
1164
|
+
test_it: "Test det",
|
|
1165
|
+
information: "Informasjon",
|
|
1166
|
+
success: "Suksess",
|
|
1167
|
+
redirect: "Viderekobling",
|
|
1168
|
+
error: "Feil",
|
|
1169
|
+
show: "Vis ${1}",
|
|
1170
|
+
hide: "Skjul ${1}",
|
|
1171
|
+
available_items: "Tilgjengelige elementer",
|
|
1172
|
+
required_scopes: "Påkrevde scopes",
|
|
1173
|
+
properties: "Egenskaper",
|
|
1174
|
+
or: "eller",
|
|
1175
|
+
and: "og",
|
|
1176
|
+
possible_values: "Mulige verdier"
|
|
1177
|
+
};
|
|
1178
|
+
|
|
1179
|
+
//#endregion
|
|
1180
|
+
//#region src/translations/pt-br.ts
|
|
1181
|
+
const pt_br = {
|
|
1182
|
+
required: "Obrigatório",
|
|
1183
|
+
deprecated: "Obsoleto",
|
|
1184
|
+
deprecated_and_sunset_on: "Esta operação está obsoleta e será descontinuada em ${1}.",
|
|
1185
|
+
stability_experimental: "Experimental",
|
|
1186
|
+
stability_alpha: "Alfa",
|
|
1187
|
+
stability_beta: "Beta",
|
|
1188
|
+
discriminator: "Discriminador",
|
|
1189
|
+
copy_to_clipboard: "Copiar para a área de transferência",
|
|
1190
|
+
copied: "Copiado",
|
|
1191
|
+
no_content: "Sem conteúdo",
|
|
1192
|
+
unresolved_reference: "Referência não resolvida",
|
|
1193
|
+
circular_reference: "Referência circular",
|
|
1194
|
+
read_only: "Somente leitura",
|
|
1195
|
+
write_only: "Somente escrita",
|
|
1196
|
+
optional: "Opcional",
|
|
1197
|
+
min: "Mín",
|
|
1198
|
+
max: "Máx",
|
|
1199
|
+
nullable: "Nulo",
|
|
1200
|
+
body: "Corpo",
|
|
1201
|
+
payload: "Carga útil",
|
|
1202
|
+
headers: "Headers",
|
|
1203
|
+
header: "Header",
|
|
1204
|
+
authorizations: "Autorizações",
|
|
1205
|
+
responses: "Respostas",
|
|
1206
|
+
response: "Resposta",
|
|
1207
|
+
path_parameters: "Parâmetros de rota",
|
|
1208
|
+
query_parameters: "Parâmetros de consulta",
|
|
1209
|
+
header_parameters: "Parâmetros de cabeçalho",
|
|
1210
|
+
attributes: "Atributos",
|
|
1211
|
+
test_it: "Testar",
|
|
1212
|
+
information: "Informação",
|
|
1213
|
+
success: "Sucesso",
|
|
1214
|
+
redirect: "Redirecionamento",
|
|
1215
|
+
error: "Erro",
|
|
1216
|
+
show: "Mostrar ${1}",
|
|
1217
|
+
hide: "Ocultar ${1}",
|
|
1218
|
+
available_items: "Itens disponíveis",
|
|
1219
|
+
required_scopes: "Scopes obrigatórios",
|
|
1220
|
+
properties: "Propriedades",
|
|
1221
|
+
or: "ou",
|
|
1222
|
+
and: "e",
|
|
1223
|
+
possible_values: "Valores possíveis"
|
|
1224
|
+
};
|
|
1225
|
+
|
|
1226
|
+
//#endregion
|
|
1227
|
+
//#region src/translations/zh.ts
|
|
1228
|
+
const zh = {
|
|
1229
|
+
required: "必填",
|
|
1230
|
+
deprecated: "已弃用",
|
|
1231
|
+
deprecated_and_sunset_on: "此操作已弃用,将于 ${1} 停止使用。",
|
|
1232
|
+
stability_experimental: "实验性",
|
|
1233
|
+
stability_alpha: "Alpha",
|
|
1234
|
+
stability_beta: "Beta",
|
|
1235
|
+
discriminator: "判别器",
|
|
1236
|
+
copy_to_clipboard: "复制到剪贴板",
|
|
1237
|
+
copied: "已复制",
|
|
1238
|
+
no_content: "无内容",
|
|
1239
|
+
unresolved_reference: "未解析的引用",
|
|
1240
|
+
circular_reference: "循环引用",
|
|
1241
|
+
read_only: "只读",
|
|
1242
|
+
write_only: "只写",
|
|
1243
|
+
optional: "可选",
|
|
1244
|
+
min: "最小值",
|
|
1245
|
+
max: "最大值",
|
|
1246
|
+
nullable: "可为 null",
|
|
1247
|
+
body: "请求体",
|
|
1248
|
+
payload: "有效载荷",
|
|
1249
|
+
headers: "头字段",
|
|
1250
|
+
header: "头部",
|
|
1251
|
+
authorizations: "授权",
|
|
1252
|
+
responses: "响应",
|
|
1253
|
+
response: "响应",
|
|
1254
|
+
path_parameters: "路径参数",
|
|
1255
|
+
query_parameters: "查询参数",
|
|
1256
|
+
header_parameters: "头参数",
|
|
1257
|
+
attributes: "属性",
|
|
1258
|
+
test_it: "测试一下",
|
|
1259
|
+
information: "信息",
|
|
1260
|
+
success: "成功",
|
|
1261
|
+
redirect: "重定向",
|
|
1262
|
+
error: "错误",
|
|
1263
|
+
show: "显示${1}",
|
|
1264
|
+
hide: "隐藏${1}",
|
|
1265
|
+
available_items: "可用项",
|
|
1266
|
+
required_scopes: "必需范围",
|
|
1267
|
+
properties: "属性",
|
|
1268
|
+
or: "或",
|
|
1269
|
+
and: "和",
|
|
1270
|
+
possible_values: "可能的值"
|
|
1271
|
+
};
|
|
1272
|
+
|
|
1273
|
+
//#endregion
|
|
1274
|
+
//#region src/translations/index.ts
|
|
1275
|
+
const translations = {
|
|
1276
|
+
en,
|
|
1277
|
+
de,
|
|
1278
|
+
es,
|
|
1279
|
+
fr,
|
|
1280
|
+
ja,
|
|
1281
|
+
nl,
|
|
1282
|
+
no,
|
|
1283
|
+
"pt-br": pt_br,
|
|
1284
|
+
zh
|
|
1285
|
+
};
|
|
1286
|
+
/**
|
|
1287
|
+
* Check if the locale is valid.
|
|
1288
|
+
*/
|
|
1289
|
+
function checkIsValidLocale(locale) {
|
|
1290
|
+
return Object.prototype.hasOwnProperty.call(translations, locale);
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
//#endregion
|
|
1294
|
+
//#region src/context.ts
|
|
1295
|
+
/**
|
|
1296
|
+
* Resolve OpenAPI context from the input.
|
|
1297
|
+
*/
|
|
1298
|
+
function resolveOpenAPIContext(context) {
|
|
1299
|
+
const { locale,...rest } = context;
|
|
1300
|
+
return {
|
|
1301
|
+
...rest,
|
|
1302
|
+
translation: translations[locale ?? "en"]
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1305
|
+
/**
|
|
1306
|
+
* Get the client context from the OpenAPI context.
|
|
1307
|
+
*/
|
|
1308
|
+
function getOpenAPIClientContext(context) {
|
|
1309
|
+
return {
|
|
1310
|
+
translation: context.translation,
|
|
1311
|
+
icons: context.icons,
|
|
1312
|
+
defaultInteractiveOpened: context.defaultInteractiveOpened,
|
|
1313
|
+
blockKey: context.blockKey,
|
|
1314
|
+
id: context.id,
|
|
1315
|
+
$$isClientContext$$: true
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
//#endregion
|
|
1320
|
+
//#region src/generateSchemaExample.ts
|
|
1321
|
+
/**
|
|
1322
|
+
* Generate a JSON example from a schema
|
|
1323
|
+
*/
|
|
1324
|
+
function generateSchemaExample(schema, options) {
|
|
1325
|
+
return getExampleFromSchema$1(schema, {
|
|
1326
|
+
emptyString: "text",
|
|
1327
|
+
...options
|
|
1328
|
+
});
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* Generate an example for a media type.
|
|
1332
|
+
*/
|
|
1333
|
+
function generateMediaTypeExamples(mediaType, options) {
|
|
1334
|
+
if (mediaType.example) return [{
|
|
1335
|
+
summary: "default",
|
|
1336
|
+
value: mediaType.example
|
|
1337
|
+
}];
|
|
1338
|
+
if (mediaType.examples) {
|
|
1339
|
+
const { examples } = mediaType;
|
|
1340
|
+
const keys = Object.keys(examples);
|
|
1341
|
+
if (keys.length > 0) return keys.reduce((result, key) => {
|
|
1342
|
+
const example = examples[key];
|
|
1343
|
+
if (!example || checkIsReference(example)) return result;
|
|
1344
|
+
result.push({
|
|
1345
|
+
summary: example.summary || key,
|
|
1346
|
+
value: example.value,
|
|
1347
|
+
description: example.description,
|
|
1348
|
+
externalValue: example.externalValue
|
|
1349
|
+
});
|
|
1350
|
+
return result;
|
|
1351
|
+
}, []);
|
|
1352
|
+
}
|
|
1353
|
+
if (mediaType.schema) return [{
|
|
1354
|
+
summary: "default",
|
|
1355
|
+
value: generateSchemaExample(mediaType.schema, options)
|
|
1356
|
+
}];
|
|
1357
|
+
return [];
|
|
1358
|
+
}
|
|
1359
|
+
/** Hard limit for rendering circular references */
|
|
1360
|
+
const MAX_LEVELS_DEEP = 5;
|
|
1361
|
+
const genericExampleValues = {
|
|
1362
|
+
"date-time": (/* @__PURE__ */ new Date()).toISOString(),
|
|
1363
|
+
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0] ?? "1970-01-01",
|
|
1364
|
+
email: "name@gmail.com",
|
|
1365
|
+
hostname: "example.com",
|
|
1366
|
+
ipv4: "0.0.0.0",
|
|
1367
|
+
ipv6: "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
|
|
1368
|
+
uri: "https://example.com",
|
|
1369
|
+
uuid: "123e4567-e89b-12d3-a456-426614174000",
|
|
1370
|
+
binary: "binary",
|
|
1371
|
+
byte: "Ynl0ZXM=",
|
|
1372
|
+
password: "password",
|
|
1373
|
+
"idn-email": "jane.doe@example.com",
|
|
1374
|
+
"idn-hostname": "example.com",
|
|
1375
|
+
"iri-reference": "/entitiy/1",
|
|
1376
|
+
iri: "https://example.com/entity/123",
|
|
1377
|
+
"json-pointer": "/nested/objects",
|
|
1378
|
+
regex: "/[a-z]/",
|
|
1379
|
+
"relative-json-pointer": "1/nested/objects",
|
|
1380
|
+
time: (/* @__PURE__ */ new Date()).toISOString().split("T")[1]?.split(".")[0] ?? "00:00:00Z",
|
|
1381
|
+
"uri-reference": "../folder",
|
|
1382
|
+
"uri-template": "https://example.com/{id}",
|
|
1383
|
+
"object-id": "6592008029c8c3e4dc76256c"
|
|
1384
|
+
};
|
|
1385
|
+
/**
|
|
1386
|
+
* We can use the `format` to generate some random values.
|
|
1387
|
+
*/
|
|
1388
|
+
function guessFromFormat(schema, fallback = "") {
|
|
1389
|
+
return genericExampleValues[schema.format] ?? fallback;
|
|
1390
|
+
}
|
|
1391
|
+
/**
|
|
1392
|
+
* This function takes an OpenAPI schema and generates an example from it
|
|
1393
|
+
* Forked from : https://github.com/scalar/scalar/blob/main/packages/oas-utils/src/spec-getters/getExampleFromSchema.ts
|
|
1394
|
+
*/
|
|
1395
|
+
const getExampleFromSchema$1 = (schema, options, level = 0, parentSchema, name, resultCache = /* @__PURE__ */ new WeakMap()) => {
|
|
1396
|
+
function cache(schema$1, result) {
|
|
1397
|
+
if (typeof result !== "object" || result === null) return result;
|
|
1398
|
+
resultCache.set(schema$1, result);
|
|
1399
|
+
return result;
|
|
1400
|
+
}
|
|
1401
|
+
if (resultCache.has(schema)) return resultCache.get(schema);
|
|
1402
|
+
if (level === MAX_LEVELS_DEEP + 1) try {
|
|
1403
|
+
JSON.stringify(schema);
|
|
1404
|
+
} catch {
|
|
1405
|
+
return "[Circular Reference]";
|
|
1406
|
+
}
|
|
1407
|
+
const makeUpRandomData = !!options?.emptyString;
|
|
1408
|
+
if (schema.deprecated || schema.type === "array" && schema.items?.deprecated) return;
|
|
1409
|
+
if (options?.mode === "write" && schema.readOnly || options?.mode === "read" && schema.writeOnly) return;
|
|
1410
|
+
if (schema["x-variable"]) {
|
|
1411
|
+
const value = options?.variables?.[schema["x-variable"]];
|
|
1412
|
+
if (value !== void 0) {
|
|
1413
|
+
if (schema.type === "number" || schema.type === "integer") return Number.parseInt(value, 10);
|
|
1414
|
+
return cache(schema, value);
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
if (Array.isArray(schema.examples) && schema.examples.length > 0) return cache(schema, schema.examples[0]);
|
|
1418
|
+
if (schema.example !== void 0) return cache(schema, schema.example);
|
|
1419
|
+
if (schema.default !== void 0 && [
|
|
1420
|
+
"string",
|
|
1421
|
+
"number",
|
|
1422
|
+
"boolean"
|
|
1423
|
+
].includes(typeof schema.default)) return cache(schema, schema.default);
|
|
1424
|
+
if (Array.isArray(schema.enum) && schema.enum.length > 0) return cache(schema, schema.enum[0]);
|
|
1425
|
+
if (!(schema.type === "object" || schema.type === "array" || !!schema.allOf?.at?.(0) || !!schema.anyOf?.at?.(0) || !!schema.oneOf?.at?.(0)) && options?.omitEmptyAndOptionalProperties === true) {
|
|
1426
|
+
if (!(schema.required === true || parentSchema?.required === true || parentSchema?.required?.includes(name ?? schema.name))) return;
|
|
1427
|
+
}
|
|
1428
|
+
if (schema.type === "object" || schema.properties !== void 0) {
|
|
1429
|
+
const response = {};
|
|
1430
|
+
if (schema.properties !== void 0) {
|
|
1431
|
+
for (const propertyName in schema.properties) if (Object.prototype.hasOwnProperty.call(schema.properties, propertyName)) {
|
|
1432
|
+
const property = schema.properties[propertyName];
|
|
1433
|
+
const propertyXmlTagName = options?.xml ? property.xml?.name : void 0;
|
|
1434
|
+
response[propertyXmlTagName ?? propertyName] = getExampleFromSchema$1(property, options, level + 1, schema, propertyName, resultCache);
|
|
1435
|
+
if (typeof response[propertyXmlTagName ?? propertyName] === "undefined") delete response[propertyXmlTagName ?? propertyName];
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
if (schema.patternProperties !== void 0) {
|
|
1439
|
+
for (const pattern in schema.patternProperties) if (Object.prototype.hasOwnProperty.call(schema.patternProperties, pattern)) {
|
|
1440
|
+
const property = schema.patternProperties[pattern];
|
|
1441
|
+
const exampleKey = pattern;
|
|
1442
|
+
response[exampleKey] = getExampleFromSchema$1(property, options, level + 1, schema, exampleKey, resultCache);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
if (schema.additionalProperties !== void 0) {
|
|
1446
|
+
if (schema.additionalProperties === true || typeof schema.additionalProperties === "object" && !Object.keys(schema.additionalProperties).length) response.ANY_ADDITIONAL_PROPERTY = "anything";
|
|
1447
|
+
else if (schema.additionalProperties !== false) response.ANY_ADDITIONAL_PROPERTY = getExampleFromSchema$1(schema.additionalProperties, options, level + 1, void 0, void 0, resultCache);
|
|
1448
|
+
}
|
|
1449
|
+
if (schema.anyOf !== void 0) Object.assign(response, getExampleFromSchema$1(schema.anyOf[0], options, level + 1, void 0, void 0, resultCache));
|
|
1450
|
+
else if (schema.oneOf !== void 0) Object.assign(response, getExampleFromSchema$1(schema.oneOf[0], options, level + 1, void 0, void 0, resultCache));
|
|
1451
|
+
else if (schema.allOf !== void 0) Object.assign(response, ...schema.allOf.map((item) => getExampleFromSchema$1(item, options, level + 1, schema, void 0, resultCache)).filter((item) => item !== void 0));
|
|
1452
|
+
return cache(schema, response);
|
|
1453
|
+
}
|
|
1454
|
+
if (schema.type === "array" || schema.items !== void 0) {
|
|
1455
|
+
const itemsXmlTagName = schema?.items?.xml?.name;
|
|
1456
|
+
const wrapItems = !!(options?.xml && schema.xml?.wrapped && itemsXmlTagName);
|
|
1457
|
+
if (schema.example !== void 0) return cache(schema, wrapItems ? { [itemsXmlTagName]: schema.example } : schema.example);
|
|
1458
|
+
if (schema.items) {
|
|
1459
|
+
if (schema.items.allOf) {
|
|
1460
|
+
if (schema.items.allOf[0].type === "object") {
|
|
1461
|
+
const mergedExample = getExampleFromSchema$1({
|
|
1462
|
+
type: "object",
|
|
1463
|
+
allOf: schema.items.allOf
|
|
1464
|
+
}, options, level + 1, schema, void 0, resultCache);
|
|
1465
|
+
return cache(schema, wrapItems ? [{ [itemsXmlTagName]: mergedExample }] : [mergedExample]);
|
|
1466
|
+
}
|
|
1467
|
+
const examples = schema.items.allOf.map((item) => getExampleFromSchema$1(item, options, level + 1, schema, void 0, resultCache)).filter((item) => item !== void 0);
|
|
1468
|
+
return cache(schema, wrapItems ? examples.map((example) => ({ [itemsXmlTagName]: example })) : examples);
|
|
1469
|
+
}
|
|
1470
|
+
for (const rule of ["anyOf", "oneOf"]) {
|
|
1471
|
+
if (!schema.items[rule]) continue;
|
|
1472
|
+
const exampleFromRule = schema.items[rule].slice(0, 1).map((item) => getExampleFromSchema$1(item, options, level + 1, schema, void 0, resultCache)).filter((item) => item !== void 0);
|
|
1473
|
+
return cache(schema, wrapItems ? [{ [itemsXmlTagName]: exampleFromRule }] : exampleFromRule);
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
if (schema.items?.type) {
|
|
1477
|
+
const exampleFromSchema = getExampleFromSchema$1(schema.items, options, level + 1, void 0, void 0, resultCache);
|
|
1478
|
+
return wrapItems ? [{ [itemsXmlTagName]: exampleFromSchema }] : [exampleFromSchema];
|
|
1479
|
+
}
|
|
1480
|
+
return [];
|
|
1481
|
+
}
|
|
1482
|
+
const exampleValues = {
|
|
1483
|
+
string: makeUpRandomData ? guessFromFormat(schema, options?.emptyString) : "",
|
|
1484
|
+
boolean: true,
|
|
1485
|
+
integer: schema.min ?? 1,
|
|
1486
|
+
number: schema.min ?? 1,
|
|
1487
|
+
array: []
|
|
1488
|
+
};
|
|
1489
|
+
if (schema.type !== void 0 && exampleValues[schema.type] !== void 0) return cache(schema, exampleValues[schema.type]);
|
|
1490
|
+
const discriminateSchema = schema.oneOf || schema.anyOf;
|
|
1491
|
+
if (Array.isArray(discriminateSchema) && discriminateSchema.length > 0) {
|
|
1492
|
+
const firstOneOfItem = discriminateSchema[0];
|
|
1493
|
+
return getExampleFromSchema$1(firstOneOfItem, options, level + 1, void 0, void 0, resultCache);
|
|
1494
|
+
}
|
|
1495
|
+
if (Array.isArray(schema.allOf)) {
|
|
1496
|
+
let example = null;
|
|
1497
|
+
schema.allOf.forEach((allOfItem) => {
|
|
1498
|
+
const newExample = getExampleFromSchema$1(allOfItem, options, level + 1, void 0, void 0, resultCache);
|
|
1499
|
+
example = typeof newExample === "object" && typeof example === "object" ? {
|
|
1500
|
+
...example ?? {},
|
|
1501
|
+
...newExample
|
|
1502
|
+
} : Array.isArray(newExample) && Array.isArray(example) ? [...example ?? {}, ...newExample] : newExample;
|
|
1503
|
+
});
|
|
1504
|
+
return cache(schema, example);
|
|
1505
|
+
}
|
|
1506
|
+
if (Array.isArray(schema.type)) {
|
|
1507
|
+
if (schema.type.includes("null")) return null;
|
|
1508
|
+
const exampleValue = exampleValues[schema.type[0]];
|
|
1509
|
+
if (exampleValue !== void 0) return cache(schema, exampleValue);
|
|
1510
|
+
}
|
|
1511
|
+
return null;
|
|
1512
|
+
};
|
|
1513
|
+
|
|
1514
|
+
//#endregion
|
|
1515
|
+
//#region src/util/example.tsx
|
|
1516
|
+
/**
|
|
1517
|
+
* Generate an example from a reference object.
|
|
1518
|
+
*/
|
|
1519
|
+
function getExampleFromReference(ref, context) {
|
|
1520
|
+
return {
|
|
1521
|
+
summary: tString(context.translation, "unresolved_reference"),
|
|
1522
|
+
value: { $ref: ref.$ref }
|
|
1523
|
+
};
|
|
1524
|
+
}
|
|
1525
|
+
/**
|
|
1526
|
+
* Get examples from a media type object.
|
|
1527
|
+
*/
|
|
1528
|
+
function getExamplesFromMediaTypeObject(args) {
|
|
1529
|
+
const { mediaTypeObject, mediaType, context } = args;
|
|
1530
|
+
if (mediaTypeObject.examples) return Object.entries(mediaTypeObject.examples).map(([key, example]) => {
|
|
1531
|
+
return {
|
|
1532
|
+
key,
|
|
1533
|
+
example: checkIsReference(example) ? getExampleFromReference(example, context) : example
|
|
1534
|
+
};
|
|
1535
|
+
});
|
|
1536
|
+
if (mediaTypeObject.example) return [{
|
|
1537
|
+
key: "default",
|
|
1538
|
+
example: { value: mediaTypeObject.example }
|
|
1539
|
+
}];
|
|
1540
|
+
if (mediaTypeObject.schema) {
|
|
1541
|
+
if (mediaType === "application/xml") return [{
|
|
1542
|
+
key: "default",
|
|
1543
|
+
example: { value: { [mediaTypeObject.schema.xml?.name ?? "object"]: generateSchemaExample(mediaTypeObject.schema, {
|
|
1544
|
+
xml: mediaType === "application/xml",
|
|
1545
|
+
mode: "read"
|
|
1546
|
+
}) } }
|
|
1547
|
+
}];
|
|
1548
|
+
return [{
|
|
1549
|
+
key: "default",
|
|
1550
|
+
example: { value: generateSchemaExample(mediaTypeObject.schema, { mode: "read" }) }
|
|
1551
|
+
}];
|
|
1552
|
+
}
|
|
1553
|
+
return [];
|
|
1554
|
+
}
|
|
1555
|
+
/**
|
|
1556
|
+
* Get example from a schema object.
|
|
1557
|
+
*/
|
|
1558
|
+
function getExampleFromSchema(args) {
|
|
1559
|
+
const { schema } = args;
|
|
1560
|
+
if (schema.example) return { value: schema.example };
|
|
1561
|
+
return { value: generateSchemaExample(schema, { mode: "read" }) };
|
|
1562
|
+
}
|
|
1563
|
+
/**
|
|
1564
|
+
* Get the examples from a media type object.
|
|
1565
|
+
*/
|
|
1566
|
+
function getExamples(props) {
|
|
1567
|
+
const { mediaTypeObject, mediaType, context } = props;
|
|
1568
|
+
const examples = getExamplesFromMediaTypeObject({
|
|
1569
|
+
mediaTypeObject,
|
|
1570
|
+
mediaType,
|
|
1571
|
+
context
|
|
1572
|
+
});
|
|
1573
|
+
const syntax = getSyntaxFromMediaType(mediaType);
|
|
1574
|
+
return examples.map((example) => {
|
|
1575
|
+
return {
|
|
1576
|
+
key: example.key,
|
|
1577
|
+
label: example.example.summary || example.key,
|
|
1578
|
+
body: <OpenAPIExample example={example.example} context={props.context} syntax={syntax} />
|
|
1579
|
+
};
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
/**
|
|
1583
|
+
* Get the syntax from a media type.
|
|
1584
|
+
*/
|
|
1585
|
+
function getSyntaxFromMediaType(mediaType) {
|
|
1586
|
+
if (mediaType.includes("json")) return "json";
|
|
1587
|
+
if (mediaType.includes("yaml")) return "yaml";
|
|
1588
|
+
if (mediaType === "application/xml") return "xml";
|
|
1589
|
+
return "text";
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
//#endregion
|
|
1593
|
+
//#region src/schemas/OpenAPISchemaItem.tsx
|
|
1594
|
+
function OpenAPISchemaItem(props) {
|
|
1595
|
+
const { schema, context, name } = props;
|
|
1596
|
+
return <OpenAPIDisclosure className="openapi-schemas-disclosure" key={name} icon={context.icons.plus} header={name} label={(isExpanded) => getDisclosureLabel({
|
|
1597
|
+
schema,
|
|
1598
|
+
isExpanded,
|
|
1599
|
+
context
|
|
1600
|
+
})}>
|
|
1601
|
+
<Section className="openapi-section-schemas">
|
|
1602
|
+
<SectionBody>
|
|
1603
|
+
<OpenAPIRootSchema schema={schema} context={context} />
|
|
1604
|
+
</SectionBody>
|
|
1605
|
+
</Section>
|
|
1606
|
+
</OpenAPIDisclosure>;
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
//#endregion
|
|
1610
|
+
//#region src/schemas/OpenAPISchemas.tsx
|
|
1611
|
+
/**
|
|
1612
|
+
* OpenAPI Schemas component.
|
|
1613
|
+
*/
|
|
1614
|
+
function OpenAPISchemas(props) {
|
|
1615
|
+
const { schemas, context: contextInput, grouped, className } = props;
|
|
1616
|
+
const firstSchema = schemas[0];
|
|
1617
|
+
if (!firstSchema) return null;
|
|
1618
|
+
const context = resolveOpenAPIContext(contextInput);
|
|
1619
|
+
const clientContext = getOpenAPIClientContext(context);
|
|
1620
|
+
if (schemas.length === 1 && !grouped) {
|
|
1621
|
+
const title = `The ${firstSchema.name} object`;
|
|
1622
|
+
return <div className={clsx("openapi-schemas", className)}>
|
|
1623
|
+
<div className="openapi-summary" id={context.id}>
|
|
1624
|
+
{context.renderHeading({
|
|
1625
|
+
title,
|
|
1626
|
+
deprecated: Boolean(firstSchema.schema.deprecated),
|
|
1627
|
+
stability: firstSchema.schema["x-stability"]
|
|
1628
|
+
})}
|
|
1629
|
+
</div>
|
|
1630
|
+
<div className="openapi-columns">
|
|
1631
|
+
<div className="openapi-column-spec">
|
|
1632
|
+
<StaticSection className="openapi-parameters" header={t(context.translation, "attributes")}>
|
|
1633
|
+
<OpenAPIRootSchema schema={firstSchema.schema} context={clientContext} />
|
|
1634
|
+
</StaticSection>
|
|
1635
|
+
</div>
|
|
1636
|
+
<div className="openapi-column-preview">
|
|
1637
|
+
<div className="openapi-column-preview-body">
|
|
1638
|
+
<div className="openapi-panel">
|
|
1639
|
+
<h4 className="openapi-panel-heading">{title}</h4>
|
|
1640
|
+
<div className="openapi-panel-body">
|
|
1641
|
+
<OpenAPIExample example={getExampleFromSchema({ schema: firstSchema.schema })} context={context} syntax="json" />
|
|
1642
|
+
</div>
|
|
1643
|
+
</div>
|
|
1644
|
+
</div>
|
|
1645
|
+
</div>
|
|
1646
|
+
</div>
|
|
1647
|
+
</div>;
|
|
1648
|
+
}
|
|
1649
|
+
return <div className={clsx("openapi-schemas", className)}>
|
|
1650
|
+
{schemas.map(({ name, schema }) => {
|
|
1651
|
+
return <OpenAPISchemaItem key={name} name={name} context={clientContext} schema={schema} />;
|
|
1652
|
+
})}
|
|
1653
|
+
</div>;
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
//#endregion
|
|
1657
|
+
//#region src/dereference.ts
|
|
1658
|
+
const dereferenceCache = /* @__PURE__ */ new WeakMap();
|
|
1659
|
+
/**
|
|
1660
|
+
* Memoized version of `dereferenceSchema`.
|
|
1661
|
+
*/
|
|
1662
|
+
function dereferenceFilesystem(filesystem) {
|
|
1663
|
+
if (dereferenceCache.has(filesystem)) return dereferenceCache.get(filesystem);
|
|
1664
|
+
const promise = baseDereferenceFilesystem(filesystem);
|
|
1665
|
+
dereferenceCache.set(filesystem, promise);
|
|
1666
|
+
return promise;
|
|
1667
|
+
}
|
|
1668
|
+
/**
|
|
1669
|
+
* Dereference an OpenAPI schema.
|
|
1670
|
+
*/
|
|
1671
|
+
async function baseDereferenceFilesystem(filesystem) {
|
|
1672
|
+
const result = await dereference(filesystem);
|
|
1673
|
+
if (!result.schema) throw new Error("Failed to dereference OpenAPI document");
|
|
1674
|
+
return result.schema;
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
//#endregion
|
|
1678
|
+
//#region src/schemas/resolveOpenAPISchemas.ts
|
|
1679
|
+
/**
|
|
1680
|
+
* Resolve an OpenAPI schemas from a file and compile it to a more usable format.
|
|
1681
|
+
* Schemas are extracted from the OpenAPI components.schemas
|
|
1682
|
+
*/
|
|
1683
|
+
async function resolveOpenAPISchemas(filesystem, options) {
|
|
1684
|
+
const { schemas: selectedSchemas } = options;
|
|
1685
|
+
const schemas = filterSelectedOpenAPISchemas(await dereferenceFilesystem(filesystem), selectedSchemas);
|
|
1686
|
+
if (schemas.length === 0) return null;
|
|
1687
|
+
return { schemas };
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
//#endregion
|
|
1691
|
+
//#region src/getOrCreateStoreByKey.ts
|
|
1692
|
+
const createStateStore = (initial) => {
|
|
1693
|
+
return createStore()((set) => ({
|
|
1694
|
+
key: initial ?? null,
|
|
1695
|
+
setKey: (key) => {
|
|
1696
|
+
set(() => ({ key }));
|
|
1697
|
+
}
|
|
1698
|
+
}));
|
|
1699
|
+
};
|
|
1700
|
+
const defaultStores = /* @__PURE__ */ new Map();
|
|
1701
|
+
const createStateStoreFactory = (stores) => {
|
|
1702
|
+
return (storeKey, initialKey) => {
|
|
1703
|
+
if (!stores.has(storeKey)) stores.set(storeKey, createStateStore(initialKey));
|
|
1704
|
+
return stores.get(storeKey);
|
|
1705
|
+
};
|
|
1706
|
+
};
|
|
1707
|
+
const getOrCreateStoreByKey = createStateStoreFactory(defaultStores);
|
|
1708
|
+
|
|
1709
|
+
//#endregion
|
|
1710
|
+
//#region src/OpenAPISelect.tsx
|
|
1711
|
+
function useSelectState(stateKey = "select-state", initialKey = "default") {
|
|
1712
|
+
const store = useStore(getOrCreateStoreByKey(stateKey, initialKey));
|
|
1713
|
+
return {
|
|
1714
|
+
key: store.key,
|
|
1715
|
+
setKey: useCallback((key) => store.setKey(key), [store.setKey])
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1718
|
+
function OpenAPISelect(props) {
|
|
1719
|
+
const { icon = "▼", items, children, className, placement, stateKey, selectedKey, onSelectionChange } = props;
|
|
1720
|
+
const state = useSelectState(stateKey, items[0]?.key);
|
|
1721
|
+
const selected = items.find((item) => item.key === state.key) || items[0];
|
|
1722
|
+
return <Select aria-label="OpenAPI Select" {...props} selectedKey={selectedKey || selected?.key} onSelectionChange={(key) => {
|
|
1723
|
+
onSelectionChange?.(key);
|
|
1724
|
+
state.setKey(key);
|
|
1725
|
+
}} className={clsx("openapi-select", className)}>
|
|
1726
|
+
<Button>
|
|
1727
|
+
<SelectValue />
|
|
1728
|
+
{icon}
|
|
1729
|
+
</Button>
|
|
1730
|
+
<Popover placement={placement} className="openapi-select-popover">
|
|
1731
|
+
<ListBox className="openapi-select-listbox" items={items}>
|
|
1732
|
+
{children}
|
|
1733
|
+
</ListBox>
|
|
1734
|
+
</Popover>
|
|
1735
|
+
</Select>;
|
|
1736
|
+
}
|
|
1737
|
+
function OpenAPISelectItem(props) {
|
|
1738
|
+
return <ListBoxItem {...props} className={({ isFocused, isSelected }) => clsx("openapi-select-item", {
|
|
1739
|
+
"openapi-select-item-focused": isFocused,
|
|
1740
|
+
"openapi-select-item-selected": isSelected
|
|
1741
|
+
})} />;
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
//#endregion
|
|
1745
|
+
//#region src/OpenAPICodeSampleInteractive.tsx
|
|
1746
|
+
function OpenAPIMediaTypeExamplesSelector(props) {
|
|
1747
|
+
const { method, path, renderers, selectIcon, blockKey } = props;
|
|
1748
|
+
if (!renderers[0]) throw new Error("No renderers provided");
|
|
1749
|
+
const stateKey = createStateKey("request-body-media-type", blockKey);
|
|
1750
|
+
const state = useSelectState(stateKey, renderers[0].mediaType);
|
|
1751
|
+
const selected = renderers.find((r) => r.mediaType === state.key) || renderers[0];
|
|
1752
|
+
return <div className="openapi-codesample-selectors">
|
|
1753
|
+
<MediaTypeSelector selectIcon={selectIcon} stateKey={stateKey} renderers={renderers} />
|
|
1754
|
+
<ExamplesSelector selectIcon={selectIcon} method={method} path={path} renderer={selected} />
|
|
1755
|
+
</div>;
|
|
1756
|
+
}
|
|
1757
|
+
function MediaTypeSelector(props) {
|
|
1758
|
+
const { renderers, stateKey, selectIcon } = props;
|
|
1759
|
+
if (renderers.length < 2) return null;
|
|
1760
|
+
const items = renderers.map((renderer) => ({
|
|
1761
|
+
key: renderer.mediaType,
|
|
1762
|
+
label: renderer.mediaType
|
|
1763
|
+
}));
|
|
1764
|
+
return <OpenAPISelect className={clsx("openapi-select")} items={renderers.map((renderer) => ({
|
|
1765
|
+
key: renderer.mediaType,
|
|
1766
|
+
label: renderer.mediaType
|
|
1767
|
+
}))} icon={selectIcon} stateKey={stateKey} placement="bottom start">
|
|
1768
|
+
{items.map((item) => <OpenAPISelectItem key={item.key} id={item.key} value={item}>
|
|
1769
|
+
{item.label}
|
|
1770
|
+
</OpenAPISelectItem>)}
|
|
1771
|
+
</OpenAPISelect>;
|
|
1772
|
+
}
|
|
1773
|
+
function ExamplesSelector(props) {
|
|
1774
|
+
const { method, path, renderer, selectIcon } = props;
|
|
1775
|
+
if (renderer.examples.length < 2) return null;
|
|
1776
|
+
const items = renderer.examples.map((example, index) => ({
|
|
1777
|
+
key: index,
|
|
1778
|
+
label: example.example.summary || `Example ${index + 1}`
|
|
1779
|
+
}));
|
|
1780
|
+
return <OpenAPISelect items={items} icon={selectIcon} stateKey={`media-type-sample-${renderer.mediaType}-${method}-${path}`} placement="bottom start">
|
|
1781
|
+
{items.map((item) => <OpenAPISelectItem key={item.key} id={item.key} value={item}>
|
|
1782
|
+
{item.label}
|
|
1783
|
+
</OpenAPISelectItem>)}
|
|
1784
|
+
</OpenAPISelect>;
|
|
1785
|
+
}
|
|
1786
|
+
function OpenAPIMediaTypeExamplesBody(props) {
|
|
1787
|
+
const { renderers, method, path, blockKey } = props;
|
|
1788
|
+
if (!renderers[0]) throw new Error("No renderers provided");
|
|
1789
|
+
const mediaTypeState = useSelectState(createStateKey("request-body-media-type", blockKey), renderers[0].mediaType);
|
|
1790
|
+
const selected = renderers.find((r) => r.mediaType === mediaTypeState.key) ?? renderers[0];
|
|
1791
|
+
if (selected.examples.length === 0) return selected.element;
|
|
1792
|
+
return <ExamplesBody method={method} path={path} renderer={selected} />;
|
|
1793
|
+
}
|
|
1794
|
+
function ExamplesBody(props) {
|
|
1795
|
+
const { method, path, renderer } = props;
|
|
1796
|
+
const exampleState = useSelectState(`media-type-sample-${renderer.mediaType}-${method}-${path}`, renderer.mediaType);
|
|
1797
|
+
const example = renderer.examples[Number(exampleState.key)] ?? renderer.examples[0];
|
|
1798
|
+
if (!example) throw new Error(`No example found for key ${exampleState.key}`);
|
|
1799
|
+
return example.element;
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
//#endregion
|
|
1803
|
+
//#region src/util/server.ts
|
|
1804
|
+
/**
|
|
1805
|
+
* Get the default URL for the server.
|
|
1806
|
+
*/
|
|
1807
|
+
function getDefaultServerURL(servers) {
|
|
1808
|
+
const server = servers[0];
|
|
1809
|
+
if (!server) return "";
|
|
1810
|
+
return interpolateServerURL(server);
|
|
1811
|
+
}
|
|
1812
|
+
/**
|
|
1813
|
+
* Interpolate the server URL with the default values of the variables.
|
|
1814
|
+
*/
|
|
1815
|
+
function interpolateServerURL(server) {
|
|
1816
|
+
return parseServerURL(server?.url ?? "").map((part) => {
|
|
1817
|
+
if (part.kind === "text") return part.text;
|
|
1818
|
+
return server.variables?.[part.name]?.default ?? `{${part.name}}`;
|
|
1819
|
+
}).join("");
|
|
1820
|
+
}
|
|
1821
|
+
function parseServerURL(url) {
|
|
1822
|
+
const parts = url.split(/{([^}]+)}/g);
|
|
1823
|
+
const result = [];
|
|
1824
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1825
|
+
const part = parts[i];
|
|
1826
|
+
if (!part) continue;
|
|
1827
|
+
if (i % 2 === 0) result.push({
|
|
1828
|
+
kind: "text",
|
|
1829
|
+
text: part
|
|
1830
|
+
});
|
|
1831
|
+
else result.push({
|
|
1832
|
+
kind: "variable",
|
|
1833
|
+
name: part
|
|
1834
|
+
});
|
|
1835
|
+
}
|
|
1836
|
+
return result;
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
//#endregion
|
|
1840
|
+
//#region src/OpenAPIPath.tsx
|
|
1841
|
+
/**
|
|
1842
|
+
* Display the path of an operation.
|
|
1843
|
+
*/
|
|
1844
|
+
function OpenAPIPath(props) {
|
|
1845
|
+
const { data, context, withServer = true, canCopy = true } = props;
|
|
1846
|
+
const { method, path, operation } = data;
|
|
1847
|
+
const server = getDefaultServerURL(data.servers);
|
|
1848
|
+
const formattedPath = formatPath(path);
|
|
1849
|
+
const element = (() => {
|
|
1850
|
+
return <>
|
|
1851
|
+
{withServer ? <span className="openapi-path-server">{server}</span> : null}
|
|
1852
|
+
{formattedPath}
|
|
1853
|
+
</>;
|
|
1854
|
+
})();
|
|
1855
|
+
return <div className="openapi-path">
|
|
1856
|
+
<div className={`openapi-method openapi-method-${method}`}>{method}</div>
|
|
1857
|
+
|
|
1858
|
+
<OpenAPICopyButton value={`${withServer ? server : ""}${path}`} className="openapi-path-title" data-deprecated={operation.deprecated} isDisabled={!canCopy} context={getOpenAPIClientContext(context)}>
|
|
1859
|
+
{element}
|
|
1860
|
+
</OpenAPICopyButton>
|
|
1861
|
+
</div>;
|
|
1862
|
+
}
|
|
1863
|
+
/**
|
|
1864
|
+
* Format the path by wrapping placeholders in <span> tags.
|
|
1865
|
+
*/
|
|
1866
|
+
function formatPath(path) {
|
|
1867
|
+
const regex = /\{\s*(\w+)\s*\}|:\w+/g;
|
|
1868
|
+
const parts = [];
|
|
1869
|
+
let lastIndex = 0;
|
|
1870
|
+
path.replace(regex, (match, _, offset) => {
|
|
1871
|
+
if (offset > lastIndex) parts.push(path.slice(lastIndex, offset));
|
|
1872
|
+
parts.push(<span key={`offset-${offset}`} className="openapi-path-variable">
|
|
1873
|
+
{match}
|
|
1874
|
+
</span>);
|
|
1875
|
+
lastIndex = offset + match.length;
|
|
1876
|
+
return match;
|
|
1877
|
+
});
|
|
1878
|
+
if (lastIndex < path.length) parts.push(path.slice(lastIndex));
|
|
1879
|
+
return parts.map((part, index) => {
|
|
1880
|
+
if (typeof part === "string") return <span key={`part-${index}`}>{part}</span>;
|
|
1881
|
+
return part;
|
|
1882
|
+
});
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
//#endregion
|
|
1886
|
+
//#region src/OpenAPICodeSampleSelector.tsx
|
|
1887
|
+
function useCodeSampleState(initialKey = "default") {
|
|
1888
|
+
const store = useStore(getOrCreateStoreByKey("codesample", initialKey));
|
|
1889
|
+
return {
|
|
1890
|
+
key: store.key,
|
|
1891
|
+
setKey: useCallback((key) => store.setKey(key), [store.setKey])
|
|
1892
|
+
};
|
|
1893
|
+
}
|
|
1894
|
+
function OpenAPICodeSampleHeader(props) {
|
|
1895
|
+
const { data, items, selectIcon, context } = props;
|
|
1896
|
+
return <>
|
|
1897
|
+
<OpenAPIPath context={context} canCopy={false} withServer={false} data={data} />
|
|
1898
|
+
{items.length > 1 ? <OpenAPISelect icon={selectIcon} items={items} stateKey="codesample" placement="bottom end">
|
|
1899
|
+
{items.map((item) => <OpenAPISelectItem key={item.key} id={item.key} value={item}>
|
|
1900
|
+
{item.label}
|
|
1901
|
+
</OpenAPISelectItem>)}
|
|
1902
|
+
</OpenAPISelect> : items[0] ? <span className="openapi-codesample-label">{items[0].label}</span> : null}
|
|
1903
|
+
</>;
|
|
1904
|
+
}
|
|
1905
|
+
function OpenAPICodeSampleBody(props) {
|
|
1906
|
+
const { items, data, selectIcon, context } = props;
|
|
1907
|
+
if (!items[0]) throw new Error("No items provided");
|
|
1908
|
+
const state = useCodeSampleState(items[0]?.key);
|
|
1909
|
+
const selected = items.find((item) => item.key === state.key) || items[0];
|
|
1910
|
+
if (!selected) return null;
|
|
1911
|
+
return <StaticSection header={<OpenAPICodeSampleHeader context={context} selectIcon={selectIcon} data={data} items={items} />} className="openapi-codesample">
|
|
1912
|
+
<div id={selected.key} className="openapi-codesample-panel">
|
|
1913
|
+
{selected.body ? selected.body : null}
|
|
1914
|
+
{selected.footer ? selected.footer : null}
|
|
1915
|
+
</div>
|
|
1916
|
+
</StaticSection>;
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
//#endregion
|
|
1920
|
+
//#region ../../node_modules/react-dom/cjs/react-dom.production.js
|
|
1921
|
+
var require_react_dom_production = /* @__PURE__ */ __commonJS({ "../../node_modules/react-dom/cjs/react-dom.production.js": ((exports) => {
|
|
1922
|
+
var React$2 = __require("react");
|
|
1923
|
+
function formatProdErrorMessage(code) {
|
|
1924
|
+
var url = "https://react.dev/errors/" + code;
|
|
1925
|
+
if (1 < arguments.length) {
|
|
1926
|
+
url += "?args[]=" + encodeURIComponent(arguments[1]);
|
|
1927
|
+
for (var i = 2; i < arguments.length; i++) url += "&args[]=" + encodeURIComponent(arguments[i]);
|
|
1928
|
+
}
|
|
1929
|
+
return "Minified React error #" + code + "; visit " + url + " for the full message or use the non-minified dev environment for full errors and additional helpful warnings.";
|
|
1930
|
+
}
|
|
1931
|
+
function noop() {}
|
|
1932
|
+
var Internals = {
|
|
1933
|
+
d: {
|
|
1934
|
+
f: noop,
|
|
1935
|
+
r: function() {
|
|
1936
|
+
throw Error(formatProdErrorMessage(522));
|
|
1937
|
+
},
|
|
1938
|
+
D: noop,
|
|
1939
|
+
C: noop,
|
|
1940
|
+
L: noop,
|
|
1941
|
+
m: noop,
|
|
1942
|
+
X: noop,
|
|
1943
|
+
S: noop,
|
|
1944
|
+
M: noop
|
|
1945
|
+
},
|
|
1946
|
+
p: 0,
|
|
1947
|
+
findDOMNode: null
|
|
1948
|
+
}, REACT_PORTAL_TYPE = Symbol.for("react.portal");
|
|
1949
|
+
function createPortal$1(children, containerInfo, implementation) {
|
|
1950
|
+
var key = 3 < arguments.length && void 0 !== arguments[3] ? arguments[3] : null;
|
|
1951
|
+
return {
|
|
1952
|
+
$$typeof: REACT_PORTAL_TYPE,
|
|
1953
|
+
key: null == key ? null : "" + key,
|
|
1954
|
+
children,
|
|
1955
|
+
containerInfo,
|
|
1956
|
+
implementation
|
|
1957
|
+
};
|
|
1958
|
+
}
|
|
1959
|
+
var ReactSharedInternals = React$2.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
|
|
1960
|
+
exports.createPortal = function(children, container) {
|
|
1961
|
+
var key = 2 < arguments.length && void 0 !== arguments[2] ? arguments[2] : null;
|
|
1962
|
+
if (!container || 1 !== container.nodeType && 9 !== container.nodeType && 11 !== container.nodeType) throw Error(formatProdErrorMessage(299));
|
|
1963
|
+
return createPortal$1(children, container, null, key);
|
|
1964
|
+
};
|
|
1965
|
+
}) });
|
|
1966
|
+
|
|
1967
|
+
//#endregion
|
|
1968
|
+
//#region ../../node_modules/react-dom/cjs/react-dom.development.js
|
|
1969
|
+
var require_react_dom_development = /* @__PURE__ */ __commonJS({ "../../node_modules/react-dom/cjs/react-dom.development.js": ((exports) => {
|
|
1970
|
+
"production" !== process.env.NODE_ENV && (function() {
|
|
1971
|
+
function noop$1() {}
|
|
1972
|
+
function testStringCoercion(value) {
|
|
1973
|
+
return "" + value;
|
|
1974
|
+
}
|
|
1975
|
+
function createPortal$1$1(children, containerInfo, implementation) {
|
|
1976
|
+
var key = 3 < arguments.length && void 0 !== arguments[3] ? arguments[3] : null;
|
|
1977
|
+
try {
|
|
1978
|
+
testStringCoercion(key);
|
|
1979
|
+
var JSCompiler_inline_result = !1;
|
|
1980
|
+
} catch (e) {
|
|
1981
|
+
JSCompiler_inline_result = !0;
|
|
1982
|
+
}
|
|
1983
|
+
JSCompiler_inline_result && (console.error("The provided key is an unsupported type %s. This value must be coerced to a string before using it here.", "function" === typeof Symbol && Symbol.toStringTag && key[Symbol.toStringTag] || key.constructor.name || "Object"), testStringCoercion(key));
|
|
1984
|
+
return {
|
|
1985
|
+
$$typeof: REACT_PORTAL_TYPE$1,
|
|
1986
|
+
key: null == key ? null : "" + key,
|
|
1987
|
+
children,
|
|
1988
|
+
containerInfo,
|
|
1989
|
+
implementation
|
|
1990
|
+
};
|
|
1991
|
+
}
|
|
1992
|
+
function getCrossOriginStringAs(as, input) {
|
|
1993
|
+
if ("font" === as) return "";
|
|
1994
|
+
if ("string" === typeof input) return "use-credentials" === input ? input : "";
|
|
1995
|
+
}
|
|
1996
|
+
function getValueDescriptorExpectingObjectForWarning(thing) {
|
|
1997
|
+
return null === thing ? "`null`" : void 0 === thing ? "`undefined`" : "" === thing ? "an empty string" : "something with type \"" + typeof thing + "\"";
|
|
1998
|
+
}
|
|
1999
|
+
function getValueDescriptorExpectingEnumForWarning(thing) {
|
|
2000
|
+
return null === thing ? "`null`" : void 0 === thing ? "`undefined`" : "" === thing ? "an empty string" : "string" === typeof thing ? JSON.stringify(thing) : "number" === typeof thing ? "`" + thing + "`" : "something with type \"" + typeof thing + "\"";
|
|
2001
|
+
}
|
|
2002
|
+
function resolveDispatcher() {
|
|
2003
|
+
var dispatcher = ReactSharedInternals$1.H;
|
|
2004
|
+
null === dispatcher && console.error("Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:\n1. You might have mismatching versions of React and the renderer (such as React DOM)\n2. You might be breaking the Rules of Hooks\n3. You might have more than one copy of React in the same app\nSee https://react.dev/link/invalid-hook-call for tips about how to debug and fix this problem.");
|
|
2005
|
+
return dispatcher;
|
|
2006
|
+
}
|
|
2007
|
+
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
|
|
2008
|
+
var React$3 = __require("react"), Internals$1 = {
|
|
2009
|
+
d: {
|
|
2010
|
+
f: noop$1,
|
|
2011
|
+
r: function() {
|
|
2012
|
+
throw Error("Invalid form element. requestFormReset must be passed a form that was rendered by React.");
|
|
2013
|
+
},
|
|
2014
|
+
D: noop$1,
|
|
2015
|
+
C: noop$1,
|
|
2016
|
+
L: noop$1,
|
|
2017
|
+
m: noop$1,
|
|
2018
|
+
X: noop$1,
|
|
2019
|
+
S: noop$1,
|
|
2020
|
+
M: noop$1
|
|
2021
|
+
},
|
|
2022
|
+
p: 0,
|
|
2023
|
+
findDOMNode: null
|
|
2024
|
+
}, REACT_PORTAL_TYPE$1 = Symbol.for("react.portal"), ReactSharedInternals$1 = React$3.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
|
|
2025
|
+
"function" === typeof Map && null != Map.prototype && "function" === typeof Map.prototype.forEach && "function" === typeof Set && null != Set.prototype && "function" === typeof Set.prototype.clear && "function" === typeof Set.prototype.forEach || console.error("React depends on Map and Set built-in types. Make sure that you load a polyfill in older browsers. https://reactjs.org/link/react-polyfills");
|
|
2026
|
+
exports.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE = Internals$1;
|
|
2027
|
+
exports.createPortal = function(children, container) {
|
|
2028
|
+
var key = 2 < arguments.length && void 0 !== arguments[2] ? arguments[2] : null;
|
|
2029
|
+
if (!container || 1 !== container.nodeType && 9 !== container.nodeType && 11 !== container.nodeType) throw Error("Target container is not a DOM element.");
|
|
2030
|
+
return createPortal$1$1(children, container, null, key);
|
|
2031
|
+
};
|
|
2032
|
+
exports.flushSync = function(fn) {
|
|
2033
|
+
var previousTransition = ReactSharedInternals$1.T, previousUpdatePriority = Internals$1.p;
|
|
2034
|
+
try {
|
|
2035
|
+
if (ReactSharedInternals$1.T = null, Internals$1.p = 2, fn) return fn();
|
|
2036
|
+
} finally {
|
|
2037
|
+
ReactSharedInternals$1.T = previousTransition, Internals$1.p = previousUpdatePriority, Internals$1.d.f() && console.error("flushSync was called from inside a lifecycle method. React cannot flush when React is already rendering. Consider moving this call to a scheduler task or micro task.");
|
|
2038
|
+
}
|
|
2039
|
+
};
|
|
2040
|
+
exports.preconnect = function(href, options) {
|
|
2041
|
+
"string" === typeof href && href ? null != options && "object" !== typeof options ? console.error("ReactDOM.preconnect(): Expected the `options` argument (second) to be an object but encountered %s instead. The only supported option at this time is `crossOrigin` which accepts a string.", getValueDescriptorExpectingEnumForWarning(options)) : null != options && "string" !== typeof options.crossOrigin && console.error("ReactDOM.preconnect(): Expected the `crossOrigin` option (second argument) to be a string but encountered %s instead. Try removing this option or passing a string value instead.", getValueDescriptorExpectingObjectForWarning(options.crossOrigin)) : console.error("ReactDOM.preconnect(): Expected the `href` argument (first) to be a non-empty string but encountered %s instead.", getValueDescriptorExpectingObjectForWarning(href));
|
|
2042
|
+
"string" === typeof href && (options ? (options = options.crossOrigin, options = "string" === typeof options ? "use-credentials" === options ? options : "" : void 0) : options = null, Internals$1.d.C(href, options));
|
|
2043
|
+
};
|
|
2044
|
+
exports.prefetchDNS = function(href) {
|
|
2045
|
+
if ("string" !== typeof href || !href) console.error("ReactDOM.prefetchDNS(): Expected the `href` argument (first) to be a non-empty string but encountered %s instead.", getValueDescriptorExpectingObjectForWarning(href));
|
|
2046
|
+
else if (1 < arguments.length) {
|
|
2047
|
+
var options = arguments[1];
|
|
2048
|
+
"object" === typeof options && options.hasOwnProperty("crossOrigin") ? console.error("ReactDOM.prefetchDNS(): Expected only one argument, `href`, but encountered %s as a second argument instead. This argument is reserved for future options and is currently disallowed. It looks like the you are attempting to set a crossOrigin property for this DNS lookup hint. Browsers do not perform DNS queries using CORS and setting this attribute on the resource hint has no effect. Try calling ReactDOM.prefetchDNS() with just a single string argument, `href`.", getValueDescriptorExpectingEnumForWarning(options)) : console.error("ReactDOM.prefetchDNS(): Expected only one argument, `href`, but encountered %s as a second argument instead. This argument is reserved for future options and is currently disallowed. Try calling ReactDOM.prefetchDNS() with just a single string argument, `href`.", getValueDescriptorExpectingEnumForWarning(options));
|
|
2049
|
+
}
|
|
2050
|
+
"string" === typeof href && Internals$1.d.D(href);
|
|
2051
|
+
};
|
|
2052
|
+
exports.preinit = function(href, options) {
|
|
2053
|
+
"string" === typeof href && href ? null == options || "object" !== typeof options ? console.error("ReactDOM.preinit(): Expected the `options` argument (second) to be an object with an `as` property describing the type of resource to be preinitialized but encountered %s instead.", getValueDescriptorExpectingEnumForWarning(options)) : "style" !== options.as && "script" !== options.as && console.error("ReactDOM.preinit(): Expected the `as` property in the `options` argument (second) to contain a valid value describing the type of resource to be preinitialized but encountered %s instead. Valid values for `as` are \"style\" and \"script\".", getValueDescriptorExpectingEnumForWarning(options.as)) : console.error("ReactDOM.preinit(): Expected the `href` argument (first) to be a non-empty string but encountered %s instead.", getValueDescriptorExpectingObjectForWarning(href));
|
|
2054
|
+
if ("string" === typeof href && options && "string" === typeof options.as) {
|
|
2055
|
+
var as = options.as, crossOrigin = getCrossOriginStringAs(as, options.crossOrigin), integrity = "string" === typeof options.integrity ? options.integrity : void 0, fetchPriority = "string" === typeof options.fetchPriority ? options.fetchPriority : void 0;
|
|
2056
|
+
"style" === as ? Internals$1.d.S(href, "string" === typeof options.precedence ? options.precedence : void 0, {
|
|
2057
|
+
crossOrigin,
|
|
2058
|
+
integrity,
|
|
2059
|
+
fetchPriority
|
|
2060
|
+
}) : "script" === as && Internals$1.d.X(href, {
|
|
2061
|
+
crossOrigin,
|
|
2062
|
+
integrity,
|
|
2063
|
+
fetchPriority,
|
|
2064
|
+
nonce: "string" === typeof options.nonce ? options.nonce : void 0
|
|
2065
|
+
});
|
|
2066
|
+
}
|
|
2067
|
+
};
|
|
2068
|
+
exports.preinitModule = function(href, options) {
|
|
2069
|
+
var encountered = "";
|
|
2070
|
+
"string" === typeof href && href || (encountered += " The `href` argument encountered was " + getValueDescriptorExpectingObjectForWarning(href) + ".");
|
|
2071
|
+
void 0 !== options && "object" !== typeof options ? encountered += " The `options` argument encountered was " + getValueDescriptorExpectingObjectForWarning(options) + "." : options && "as" in options && "script" !== options.as && (encountered += " The `as` option encountered was " + getValueDescriptorExpectingEnumForWarning(options.as) + ".");
|
|
2072
|
+
if (encountered) console.error("ReactDOM.preinitModule(): Expected up to two arguments, a non-empty `href` string and, optionally, an `options` object with a valid `as` property.%s", encountered);
|
|
2073
|
+
else switch (encountered = options && "string" === typeof options.as ? options.as : "script", encountered) {
|
|
2074
|
+
case "script": break;
|
|
2075
|
+
default: encountered = getValueDescriptorExpectingEnumForWarning(encountered), console.error("ReactDOM.preinitModule(): Currently the only supported \"as\" type for this function is \"script\" but received \"%s\" instead. This warning was generated for `href` \"%s\". In the future other module types will be supported, aligning with the import-attributes proposal. Learn more here: (https://github.com/tc39/proposal-import-attributes)", encountered, href);
|
|
2076
|
+
}
|
|
2077
|
+
if ("string" === typeof href) if ("object" === typeof options && null !== options) {
|
|
2078
|
+
if (null == options.as || "script" === options.as) encountered = getCrossOriginStringAs(options.as, options.crossOrigin), Internals$1.d.M(href, {
|
|
2079
|
+
crossOrigin: encountered,
|
|
2080
|
+
integrity: "string" === typeof options.integrity ? options.integrity : void 0,
|
|
2081
|
+
nonce: "string" === typeof options.nonce ? options.nonce : void 0
|
|
2082
|
+
});
|
|
2083
|
+
} else options ?? Internals$1.d.M(href);
|
|
2084
|
+
};
|
|
2085
|
+
exports.preload = function(href, options) {
|
|
2086
|
+
var encountered = "";
|
|
2087
|
+
"string" === typeof href && href || (encountered += " The `href` argument encountered was " + getValueDescriptorExpectingObjectForWarning(href) + ".");
|
|
2088
|
+
null == options || "object" !== typeof options ? encountered += " The `options` argument encountered was " + getValueDescriptorExpectingObjectForWarning(options) + "." : "string" === typeof options.as && options.as || (encountered += " The `as` option encountered was " + getValueDescriptorExpectingObjectForWarning(options.as) + ".");
|
|
2089
|
+
encountered && console.error("ReactDOM.preload(): Expected two arguments, a non-empty `href` string and an `options` object with an `as` property valid for a `<link rel=\"preload\" as=\"...\" />` tag.%s", encountered);
|
|
2090
|
+
if ("string" === typeof href && "object" === typeof options && null !== options && "string" === typeof options.as) {
|
|
2091
|
+
encountered = options.as;
|
|
2092
|
+
var crossOrigin = getCrossOriginStringAs(encountered, options.crossOrigin);
|
|
2093
|
+
Internals$1.d.L(href, encountered, {
|
|
2094
|
+
crossOrigin,
|
|
2095
|
+
integrity: "string" === typeof options.integrity ? options.integrity : void 0,
|
|
2096
|
+
nonce: "string" === typeof options.nonce ? options.nonce : void 0,
|
|
2097
|
+
type: "string" === typeof options.type ? options.type : void 0,
|
|
2098
|
+
fetchPriority: "string" === typeof options.fetchPriority ? options.fetchPriority : void 0,
|
|
2099
|
+
referrerPolicy: "string" === typeof options.referrerPolicy ? options.referrerPolicy : void 0,
|
|
2100
|
+
imageSrcSet: "string" === typeof options.imageSrcSet ? options.imageSrcSet : void 0,
|
|
2101
|
+
imageSizes: "string" === typeof options.imageSizes ? options.imageSizes : void 0,
|
|
2102
|
+
media: "string" === typeof options.media ? options.media : void 0
|
|
2103
|
+
});
|
|
2104
|
+
}
|
|
2105
|
+
};
|
|
2106
|
+
exports.preloadModule = function(href, options) {
|
|
2107
|
+
var encountered = "";
|
|
2108
|
+
"string" === typeof href && href || (encountered += " The `href` argument encountered was " + getValueDescriptorExpectingObjectForWarning(href) + ".");
|
|
2109
|
+
void 0 !== options && "object" !== typeof options ? encountered += " The `options` argument encountered was " + getValueDescriptorExpectingObjectForWarning(options) + "." : options && "as" in options && "string" !== typeof options.as && (encountered += " The `as` option encountered was " + getValueDescriptorExpectingObjectForWarning(options.as) + ".");
|
|
2110
|
+
encountered && console.error("ReactDOM.preloadModule(): Expected two arguments, a non-empty `href` string and, optionally, an `options` object with an `as` property valid for a `<link rel=\"modulepreload\" as=\"...\" />` tag.%s", encountered);
|
|
2111
|
+
"string" === typeof href && (options ? (encountered = getCrossOriginStringAs(options.as, options.crossOrigin), Internals$1.d.m(href, {
|
|
2112
|
+
as: "string" === typeof options.as && "script" !== options.as ? options.as : void 0,
|
|
2113
|
+
crossOrigin: encountered,
|
|
2114
|
+
integrity: "string" === typeof options.integrity ? options.integrity : void 0
|
|
2115
|
+
})) : Internals$1.d.m(href));
|
|
2116
|
+
};
|
|
2117
|
+
exports.requestFormReset = function(form) {
|
|
2118
|
+
Internals$1.d.r(form);
|
|
2119
|
+
};
|
|
2120
|
+
exports.unstable_batchedUpdates = function(fn, a) {
|
|
2121
|
+
return fn(a);
|
|
2122
|
+
};
|
|
2123
|
+
exports.useFormState = function(action, initialState, permalink) {
|
|
2124
|
+
return resolveDispatcher().useFormState(action, initialState, permalink);
|
|
2125
|
+
};
|
|
2126
|
+
exports.useFormStatus = function() {
|
|
2127
|
+
return resolveDispatcher().useHostTransitionStatus();
|
|
2128
|
+
};
|
|
2129
|
+
exports.version = "19.1.0";
|
|
2130
|
+
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());
|
|
2131
|
+
})();
|
|
2132
|
+
}) });
|
|
2133
|
+
|
|
2134
|
+
//#endregion
|
|
2135
|
+
//#region ../../node_modules/react-dom/index.js
|
|
2136
|
+
var require_react_dom = /* @__PURE__ */ __commonJS({ "../../node_modules/react-dom/index.js": ((exports, module) => {
|
|
2137
|
+
function checkDCE() {
|
|
2138
|
+
if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === "undefined" || typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE !== "function") return;
|
|
2139
|
+
if (process.env.NODE_ENV !== "production") throw new Error("^_^");
|
|
2140
|
+
try {
|
|
2141
|
+
__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(checkDCE);
|
|
2142
|
+
} catch (err) {
|
|
2143
|
+
console.error(err);
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
if (process.env.NODE_ENV === "production") {
|
|
2147
|
+
checkDCE();
|
|
2148
|
+
module.exports = require_react_dom_production();
|
|
2149
|
+
} else module.exports = require_react_dom_development();
|
|
2150
|
+
}) });
|
|
2151
|
+
|
|
2152
|
+
//#endregion
|
|
2153
|
+
//#region src/OpenAPIOperationContext.tsx
|
|
2154
|
+
var import_react_dom = /* @__PURE__ */ __toESM(require_react_dom());
|
|
2155
|
+
const OpenAPIOperationContext = createContext({ onOpenClient: () => {} });
|
|
2156
|
+
/**
|
|
2157
|
+
* Provider for the OpenAPIOperationContext.
|
|
2158
|
+
*/
|
|
2159
|
+
function OpenAPIOperationContextProvider(props) {
|
|
2160
|
+
const { children } = props;
|
|
2161
|
+
const onOpenClient = useEventCallback((pointer) => {
|
|
2162
|
+
props.onOpenClient?.(pointer);
|
|
2163
|
+
});
|
|
2164
|
+
const value = useMemo(() => ({ onOpenClient }), [onOpenClient]);
|
|
2165
|
+
return <OpenAPIOperationContext.Provider value={value}>
|
|
2166
|
+
{children}
|
|
2167
|
+
</OpenAPIOperationContext.Provider>;
|
|
2168
|
+
}
|
|
2169
|
+
/**
|
|
2170
|
+
* Hook to access the OpenAPIOperationContext.
|
|
2171
|
+
*/
|
|
2172
|
+
function useOpenAPIOperationContext() {
|
|
2173
|
+
return useContext(OpenAPIOperationContext);
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
//#endregion
|
|
2177
|
+
//#region src/OpenAPIPrefillContextProvider.tsx
|
|
2178
|
+
const OpenAPIPrefillContext = React$1.createContext(null);
|
|
2179
|
+
/**
|
|
2180
|
+
* Provide context to help prefill dynamic info like visitor data in OpenAPI blocks.
|
|
2181
|
+
*/
|
|
2182
|
+
function OpenAPIPrefillContextProvider(props) {
|
|
2183
|
+
const { getPrefillInputContextData, children } = props;
|
|
2184
|
+
return <OpenAPIPrefillContext.Provider value={getPrefillInputContextData}>
|
|
2185
|
+
{children}
|
|
2186
|
+
</OpenAPIPrefillContext.Provider>;
|
|
2187
|
+
}
|
|
2188
|
+
/**
|
|
2189
|
+
* Hook to access the prefill context function.
|
|
2190
|
+
*/
|
|
2191
|
+
function useOpenAPIPrefillContext() {
|
|
2192
|
+
return React$1.useContext(OpenAPIPrefillContext) ?? (() => null);
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
//#endregion
|
|
2196
|
+
//#region src/util/tryit-prefill.ts
|
|
2197
|
+
const PREFILL_CUSTOM_PROPERTY = "x-gitbook-prefill";
|
|
2198
|
+
/**
|
|
2199
|
+
* Resolve the Scalar API client prefill configuration for a given OpenAPI operation.
|
|
2200
|
+
*/
|
|
2201
|
+
function resolveTryItPrefillForOperation(args) {
|
|
2202
|
+
const { operation: { securities, servers }, prefillInputContext } = args;
|
|
2203
|
+
if (!prefillInputContext) return {};
|
|
2204
|
+
const runtime = new ExpressionRuntime();
|
|
2205
|
+
const resolveTryItPrefillExpression = (expr) => {
|
|
2206
|
+
if (!parseTemplate(expr).length) return;
|
|
2207
|
+
return runtime.evaluateTemplate(expr, prefillInputContext);
|
|
2208
|
+
};
|
|
2209
|
+
const prefillAuth = securities ? resolveTryItPrefillAuthForOperationSecurities({
|
|
2210
|
+
securities,
|
|
2211
|
+
resolveTryItPrefillExpression
|
|
2212
|
+
}) : void 0;
|
|
2213
|
+
const prefillServers = servers ? resolveTryItPrefillServersForOperationServers({
|
|
2214
|
+
servers,
|
|
2215
|
+
resolveTryItPrefillExpression
|
|
2216
|
+
}) : [];
|
|
2217
|
+
return {
|
|
2218
|
+
...prefillAuth ? { authentication: prefillAuth } : {},
|
|
2219
|
+
...prefillServers ? { servers: prefillServers } : {}
|
|
2220
|
+
};
|
|
2221
|
+
}
|
|
2222
|
+
/**
|
|
2223
|
+
* Resolve prefill authentication configuration for the security schemes defined for an operation.
|
|
2224
|
+
*/
|
|
2225
|
+
function resolveTryItPrefillAuthForOperationSecurities(args) {
|
|
2226
|
+
const { securities, resolveTryItPrefillExpression } = args;
|
|
2227
|
+
const prefillAuthConfig = {};
|
|
2228
|
+
for (const [schemeName, security] of Object.values(securities)) {
|
|
2229
|
+
const tryitPrefillAuthValue = security[PREFILL_CUSTOM_PROPERTY] ? resolveTryItPrefillExpression(security[PREFILL_CUSTOM_PROPERTY]) : void 0;
|
|
2230
|
+
if (!tryitPrefillAuthValue) continue;
|
|
2231
|
+
switch (security.type) {
|
|
2232
|
+
case "http":
|
|
2233
|
+
if (security.scheme?.includes("bearer")) prefillAuthConfig[schemeName] = { token: tryitPrefillAuthValue };
|
|
2234
|
+
else if (security.scheme?.includes("basic") && tryitPrefillAuthValue.includes(":")) {
|
|
2235
|
+
const [username, password] = tryitPrefillAuthValue.split(":", 2);
|
|
2236
|
+
prefillAuthConfig[schemeName] = {
|
|
2237
|
+
username,
|
|
2238
|
+
password
|
|
2239
|
+
};
|
|
2240
|
+
}
|
|
2241
|
+
break;
|
|
2242
|
+
case "apiKey":
|
|
2243
|
+
prefillAuthConfig[schemeName] = {
|
|
2244
|
+
name: security.name,
|
|
2245
|
+
in: security.in,
|
|
2246
|
+
value: tryitPrefillAuthValue
|
|
2247
|
+
};
|
|
2248
|
+
break;
|
|
2249
|
+
case "oauth2":
|
|
2250
|
+
case "openIdConnect": break;
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
return Object.keys(prefillAuthConfig).length > 0 ? { securitySchemes: prefillAuthConfig } : void 0;
|
|
2254
|
+
}
|
|
2255
|
+
/**
|
|
2256
|
+
* Resolve prefill server configuration for the servers defined for an operation.
|
|
2257
|
+
*/
|
|
2258
|
+
function resolveTryItPrefillServersForOperationServers(args) {
|
|
2259
|
+
const { servers, resolveTryItPrefillExpression } = args;
|
|
2260
|
+
const resolvedServers = [];
|
|
2261
|
+
for (const server of servers) {
|
|
2262
|
+
const tryItPrefillServerUrlExpr = server[PREFILL_CUSTOM_PROPERTY];
|
|
2263
|
+
const tryItPrefillServerUrlValue = tryItPrefillServerUrlExpr ? resolveTryItPrefillExpression(tryItPrefillServerUrlExpr) : void 0;
|
|
2264
|
+
const variables = server.variables ? { ...server.variables } : {};
|
|
2265
|
+
if (server.variables) for (const [varName, variable] of Object.entries(server.variables)) {
|
|
2266
|
+
const { [PREFILL_CUSTOM_PROPERTY]: tryItPrefillVarExpr,...variableProps } = variable;
|
|
2267
|
+
const tryItPrefillVarValue = tryItPrefillVarExpr ? resolveTryItPrefillExpression(tryItPrefillVarExpr) : void 0;
|
|
2268
|
+
variables[varName] = {
|
|
2269
|
+
...variableProps,
|
|
2270
|
+
...tryItPrefillVarValue ? { default: String(tryItPrefillVarValue) } : {}
|
|
2271
|
+
};
|
|
2272
|
+
}
|
|
2273
|
+
const hasServerVariables = Object.keys(variables).length > 0;
|
|
2274
|
+
if (server.url && (tryItPrefillServerUrlValue || hasServerVariables)) {
|
|
2275
|
+
const resolvedServer = {
|
|
2276
|
+
url: tryItPrefillServerUrlValue ?? server.url,
|
|
2277
|
+
...server.description ? { description: server.description } : {},
|
|
2278
|
+
...hasServerVariables ? { variables } : {}
|
|
2279
|
+
};
|
|
2280
|
+
resolvedServers.push(resolvedServer);
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
return resolvedServers.length > 0 ? resolvedServers : void 0;
|
|
2284
|
+
}
|
|
2285
|
+
/**
|
|
2286
|
+
* Return a X-GITBOOK-PREFILL placeholder based on the prefill custom property in the provided security scheme.
|
|
2287
|
+
*/
|
|
2288
|
+
function resolvePrefillCodePlaceholderFromSecurityScheme(args) {
|
|
2289
|
+
const { security, defaultPlaceholderValue } = args;
|
|
2290
|
+
const prefillExprParts = extractPrefillExpressionPartsFromSecurityScheme(security);
|
|
2291
|
+
if (prefillExprParts.length === 0) return defaultPlaceholderValue ?? "";
|
|
2292
|
+
return toPrefillCodePlaceholder(templatePartsToExpression(prefillExprParts), defaultPlaceholderValue);
|
|
2293
|
+
}
|
|
2294
|
+
function extractPrefillExpressionPartsFromSecurityScheme(security) {
|
|
2295
|
+
const expression = security[PREFILL_CUSTOM_PROPERTY];
|
|
2296
|
+
if (!expression || expression.length === 0) return [];
|
|
2297
|
+
return parseTemplate(expression);
|
|
2298
|
+
}
|
|
2299
|
+
/**
|
|
2300
|
+
* Return a server URL with X-GITBOOK-PREFILL placeholders based on the prefill custom properties in the provided security scheme.
|
|
2301
|
+
*/
|
|
2302
|
+
function resolveURLWithPrefillCodePlaceholdersFromServer(server, defaultServerUrl) {
|
|
2303
|
+
const serverVariables = server.variables ?? {};
|
|
2304
|
+
const variableExprs = {};
|
|
2305
|
+
let hasVariablePrefill = false;
|
|
2306
|
+
for (const [name, variable] of Object.entries(serverVariables ?? {})) if (variable[PREFILL_CUSTOM_PROPERTY]) {
|
|
2307
|
+
hasVariablePrefill = true;
|
|
2308
|
+
variableExprs[name] = `(${templatePartsToExpression(parseTemplate(variable[PREFILL_CUSTOM_PROPERTY]))} ?? '${variable.default ?? ""}')`;
|
|
2309
|
+
} else variableExprs[name] = String(variable.default) ?? "";
|
|
2310
|
+
let interpolatedUrl = server.url ?? "";
|
|
2311
|
+
interpolatedUrl = interpolatedUrl.replace(/{([^}]+)}/g, (_, varName) => {
|
|
2312
|
+
const expr = variableExprs[varName];
|
|
2313
|
+
if (serverVariables[varName]?.[PREFILL_CUSTOM_PROPERTY]) return `\${${expr ?? `'${varName}'`}}`;
|
|
2314
|
+
return expr ?? `{${varName}}`;
|
|
2315
|
+
});
|
|
2316
|
+
const interpolatedUrlTemplate = hasVariablePrefill ? `\`${interpolatedUrl}\`` : interpolatedUrl;
|
|
2317
|
+
const urlLevelExpr = server[PREFILL_CUSTOM_PROPERTY];
|
|
2318
|
+
if (urlLevelExpr) return toPrefillCodePlaceholder(`${templatePartsToExpression(parseTemplate(urlLevelExpr))} ?? ${hasVariablePrefill ? interpolatedUrlTemplate : `'${interpolatedUrlTemplate}'`}`, defaultServerUrl);
|
|
2319
|
+
if (hasVariablePrefill) return toPrefillCodePlaceholder(interpolatedUrlTemplate, defaultServerUrl);
|
|
2320
|
+
return interpolatedUrl;
|
|
2321
|
+
}
|
|
2322
|
+
function templatePartsToExpression(parts) {
|
|
2323
|
+
return parts.map((part) => {
|
|
2324
|
+
switch (part.type) {
|
|
2325
|
+
case "text": return `"${part.value}"`;
|
|
2326
|
+
case "expression": return part.value;
|
|
2327
|
+
default: return "";
|
|
2328
|
+
}
|
|
2329
|
+
}).join(" + ");
|
|
2330
|
+
}
|
|
2331
|
+
function toPrefillCodePlaceholder(expression, defaultValue) {
|
|
2332
|
+
return `$$__X-GITBOOK-PREFILL[(${expression})${defaultValue ? ` ?? '${defaultValue}'` : ""}]__$$`;
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
//#endregion
|
|
2336
|
+
//#region src/ScalarApiButton.tsx
|
|
2337
|
+
/**
|
|
2338
|
+
* Button which launches the Scalar API Client
|
|
2339
|
+
*/
|
|
2340
|
+
function ScalarApiButton(props) {
|
|
2341
|
+
const { method, path, securities, servers, specUrl, context } = props;
|
|
2342
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
2343
|
+
const controllerRef = useRef(null);
|
|
2344
|
+
return <div className="scalar scalar-activate">
|
|
2345
|
+
<button className="scalar-activate-button button" onClick={() => {
|
|
2346
|
+
controllerRef.current?.openClient?.();
|
|
2347
|
+
setIsOpen(true);
|
|
2348
|
+
}}>
|
|
2349
|
+
{t(context.translation, "test_it")}
|
|
2350
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 12" fill="currentColor">
|
|
2351
|
+
<path stroke="currentColor" strokeWidth="1.5" d="M1 10.05V1.43c0-.2.2-.31.37-.22l7.26 4.08c.17.1.17.33.01.43l-7.26 4.54a.25.25 0 0 1-.38-.21Z" />
|
|
2352
|
+
</svg>
|
|
2353
|
+
</button>
|
|
2354
|
+
|
|
2355
|
+
{isOpen && (0, import_react_dom.createPortal)(<Suspense fallback={null}>
|
|
2356
|
+
<ScalarModal controllerRef={controllerRef} method={method} path={path} securities={securities} servers={servers} specUrl={specUrl} />
|
|
2357
|
+
</Suspense>, document.body)}
|
|
2358
|
+
</div>;
|
|
2359
|
+
}
|
|
2360
|
+
function ScalarModal(props) {
|
|
2361
|
+
const { method, path, securities, servers, specUrl, controllerRef } = props;
|
|
2362
|
+
const prefillInputContext = useOpenAPIPrefillContext()();
|
|
2363
|
+
const prefillConfig = resolveTryItPrefillForOperation({
|
|
2364
|
+
operation: {
|
|
2365
|
+
securities,
|
|
2366
|
+
servers
|
|
2367
|
+
},
|
|
2368
|
+
prefillInputContext
|
|
2369
|
+
});
|
|
2370
|
+
return <ApiClientModalProvider configuration={{
|
|
2371
|
+
url: specUrl,
|
|
2372
|
+
...prefillConfig
|
|
2373
|
+
}} initialRequest={{
|
|
2374
|
+
method,
|
|
2375
|
+
path
|
|
2376
|
+
}}>
|
|
2377
|
+
<ScalarModalController method={method} path={path} controllerRef={controllerRef} />
|
|
2378
|
+
</ApiClientModalProvider>;
|
|
2379
|
+
}
|
|
2380
|
+
function ScalarModalController(props) {
|
|
2381
|
+
const { method, path, controllerRef } = props;
|
|
2382
|
+
const openScalarClient = useApiClientModal()?.open;
|
|
2383
|
+
const { onOpenClient: trackClientOpening } = useOpenAPIOperationContext();
|
|
2384
|
+
const openClient = useMemo(() => {
|
|
2385
|
+
if (openScalarClient) return () => {
|
|
2386
|
+
openScalarClient({
|
|
2387
|
+
method,
|
|
2388
|
+
path,
|
|
2389
|
+
_source: "gitbook"
|
|
2390
|
+
});
|
|
2391
|
+
trackClientOpening({
|
|
2392
|
+
method,
|
|
2393
|
+
path
|
|
2394
|
+
});
|
|
2395
|
+
};
|
|
2396
|
+
return null;
|
|
2397
|
+
}, [
|
|
2398
|
+
openScalarClient,
|
|
2399
|
+
method,
|
|
2400
|
+
path,
|
|
2401
|
+
trackClientOpening
|
|
2402
|
+
]);
|
|
2403
|
+
useImperativeHandle(controllerRef, () => ({ openClient: openClient ? () => openClient() : void 0 }), [openClient]);
|
|
2404
|
+
useEffect(() => {
|
|
2405
|
+
openClient?.();
|
|
2406
|
+
}, [openClient]);
|
|
2407
|
+
return null;
|
|
2408
|
+
}
|
|
2409
|
+
|
|
2410
|
+
//#endregion
|
|
2411
|
+
//#region src/contentTypeChecks.ts
|
|
2412
|
+
function isJSON(contentType) {
|
|
2413
|
+
return contentType?.toLowerCase().includes("application/json") || false;
|
|
2414
|
+
}
|
|
2415
|
+
function isXML(contentType) {
|
|
2416
|
+
return contentType?.toLowerCase().includes("application/xml") || false;
|
|
2417
|
+
}
|
|
2418
|
+
function isYAML(contentType) {
|
|
2419
|
+
return contentType?.toLowerCase().includes("application/yaml") || false;
|
|
2420
|
+
}
|
|
2421
|
+
function isGraphQL(contentType) {
|
|
2422
|
+
return contentType?.toLowerCase().includes("application/graphql") || false;
|
|
2423
|
+
}
|
|
2424
|
+
function isCSV(contentType) {
|
|
2425
|
+
return contentType?.toLowerCase().includes("text/csv") || false;
|
|
2426
|
+
}
|
|
2427
|
+
function isPDF(contentType) {
|
|
2428
|
+
return contentType?.toLowerCase().includes("application/pdf") || false;
|
|
2429
|
+
}
|
|
2430
|
+
function isText(contentType) {
|
|
2431
|
+
return contentType?.toLowerCase().includes("text/plain") || false;
|
|
2432
|
+
}
|
|
2433
|
+
function isFormUrlEncoded(contentType) {
|
|
2434
|
+
return contentType?.toLowerCase().includes("application/x-www-form-urlencoded") || false;
|
|
2435
|
+
}
|
|
2436
|
+
function isFormData(contentType) {
|
|
2437
|
+
return !!contentType && contentType.toLowerCase().includes("multipart/form-data");
|
|
2438
|
+
}
|
|
2439
|
+
function isPlainObject(value) {
|
|
2440
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
//#endregion
|
|
2444
|
+
//#region src/code-samples.ts
|
|
2445
|
+
const codeSampleGenerators = [
|
|
2446
|
+
{
|
|
2447
|
+
id: "http",
|
|
2448
|
+
label: "HTTP",
|
|
2449
|
+
syntax: "http",
|
|
2450
|
+
generate: ({ method, url: { origin, path }, headers = {}, body }) => {
|
|
2451
|
+
if (body) {
|
|
2452
|
+
const bodyContent = body ? stringifyOpenAPI(body) : "";
|
|
2453
|
+
const encoder = new TextEncoder();
|
|
2454
|
+
const bodyString$1 = BodyGenerators.getHTTPBody(body, headers);
|
|
2455
|
+
if (bodyString$1) body = bodyString$1;
|
|
2456
|
+
headers = {
|
|
2457
|
+
...headers,
|
|
2458
|
+
"Content-Length": encoder.encode(bodyContent).length.toString()
|
|
2459
|
+
};
|
|
2460
|
+
}
|
|
2461
|
+
if (!headers.hasOwnProperty("Accept")) headers.Accept = "*/*";
|
|
2462
|
+
const headerString = headers ? `${Object.entries(headers).map(([key, value]) => key.toLowerCase() !== "host" ? `${key}: ${value}` : "").join("\n")}\n` : "";
|
|
2463
|
+
const bodyString = body ? `\n${body}` : "";
|
|
2464
|
+
return `${method.toUpperCase()} ${decodeURI(path)} HTTP/1.1
|
|
2465
|
+
Host: ${origin.replaceAll(/https*:\/\//g, "")}
|
|
2466
|
+
${headerString}${bodyString}`;
|
|
2467
|
+
}
|
|
2468
|
+
},
|
|
2469
|
+
{
|
|
2470
|
+
id: "curl",
|
|
2471
|
+
label: "cURL",
|
|
2472
|
+
syntax: "bash",
|
|
2473
|
+
generate: ({ method, url: { origin, path }, headers, body }) => {
|
|
2474
|
+
const separator = " \\\n";
|
|
2475
|
+
const lines = ["curl -L"];
|
|
2476
|
+
if (method.toUpperCase() !== "GET") lines.push(`--request ${method.toUpperCase()}`);
|
|
2477
|
+
lines.push(`--url '${origin}${path}'`);
|
|
2478
|
+
if (body) {
|
|
2479
|
+
const bodyContent = BodyGenerators.getCurlBody(body, headers);
|
|
2480
|
+
if (bodyContent) {
|
|
2481
|
+
body = bodyContent.body;
|
|
2482
|
+
headers = bodyContent.headers;
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
if (headers && Object.keys(headers).length > 0) Object.entries(headers).forEach(([key, value]) => {
|
|
2486
|
+
lines.push(`--header '${key}: ${value}'`);
|
|
2487
|
+
});
|
|
2488
|
+
if (body) if (Array.isArray(body)) lines.push(...body);
|
|
2489
|
+
else lines.push(body);
|
|
2490
|
+
return lines.map((line, index) => index > 0 ? indent(line, 2) : line).join(separator);
|
|
2491
|
+
}
|
|
2492
|
+
},
|
|
2493
|
+
{
|
|
2494
|
+
id: "javascript",
|
|
2495
|
+
label: "JavaScript",
|
|
2496
|
+
syntax: "javascript",
|
|
2497
|
+
generate: ({ method, url: { origin, path }, headers, body }) => {
|
|
2498
|
+
let code = "";
|
|
2499
|
+
if (body) {
|
|
2500
|
+
const lines = BodyGenerators.getJavaScriptBody(body, headers);
|
|
2501
|
+
if (lines) {
|
|
2502
|
+
code += lines.code;
|
|
2503
|
+
body = lines.body;
|
|
2504
|
+
headers = lines.headers;
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
code += `const response = await fetch('${origin}${path}', {
|
|
2508
|
+
method: '${method.toUpperCase()}',\n`;
|
|
2509
|
+
if (headers && Object.keys(headers).length > 0) code += indent(`headers: ${stringifyOpenAPI(headers, null, 2)},\n`, 4);
|
|
2510
|
+
if (body) code += indent(`body: ${body}\n`, 4);
|
|
2511
|
+
code += "});\n\n";
|
|
2512
|
+
code += "const data = await response.json();";
|
|
2513
|
+
return code;
|
|
2514
|
+
}
|
|
2515
|
+
},
|
|
2516
|
+
{
|
|
2517
|
+
id: "python",
|
|
2518
|
+
label: "Python",
|
|
2519
|
+
syntax: "python",
|
|
2520
|
+
generate: ({ method, url: { origin, path }, headers, body }) => {
|
|
2521
|
+
const contentType = headers?.["Content-Type"];
|
|
2522
|
+
let code = `${isJSON(contentType) ? "import json\n" : ""}import requests\n\n`;
|
|
2523
|
+
if (body) {
|
|
2524
|
+
const lines = BodyGenerators.getPythonBody(body, headers);
|
|
2525
|
+
if (lines) {
|
|
2526
|
+
code += lines.code;
|
|
2527
|
+
body = lines.body;
|
|
2528
|
+
headers = lines.headers;
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
code += `response = requests.${method.toLowerCase()}(\n`;
|
|
2532
|
+
code += indent(`"${origin}${path}",\n`, 4);
|
|
2533
|
+
if (headers && Object.keys(headers).length > 0) code += indent(`headers=${stringifyOpenAPI(headers)},\n`, 4);
|
|
2534
|
+
if (body) if (body === "files") code += indent(`files=${body}\n`, 4);
|
|
2535
|
+
else if (isJSON(contentType)) code += indent(`data=json.dumps(${body})\n`, 4);
|
|
2536
|
+
else code += indent(`data=${body}\n`, 4);
|
|
2537
|
+
code += ")\n\n";
|
|
2538
|
+
code += "data = response.json()";
|
|
2539
|
+
return code;
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
];
|
|
2543
|
+
function indent(code, spaces) {
|
|
2544
|
+
const indent$1 = " ".repeat(spaces);
|
|
2545
|
+
return code.split("\n").map((line) => line ? indent$1 + line : "").join("\n");
|
|
2546
|
+
}
|
|
2547
|
+
function parseHostAndPath(url) {
|
|
2548
|
+
try {
|
|
2549
|
+
const urlObj = new URL(url);
|
|
2550
|
+
const path = urlObj.pathname || "/";
|
|
2551
|
+
return {
|
|
2552
|
+
host: urlObj.host,
|
|
2553
|
+
path
|
|
2554
|
+
};
|
|
2555
|
+
} catch (_e) {
|
|
2556
|
+
const splitted = url.split("//");
|
|
2557
|
+
const parts = (splitted[1] ? splitted[1] : url).split("/");
|
|
2558
|
+
return {
|
|
2559
|
+
host: parts.shift(),
|
|
2560
|
+
path: `/${parts.join("/")}`
|
|
2561
|
+
};
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
const BodyGenerators = {
|
|
2565
|
+
getCurlBody(body, headers) {
|
|
2566
|
+
if (!body || !headers) return void 0;
|
|
2567
|
+
const headersCopy = { ...headers };
|
|
2568
|
+
const contentType = headersCopy["Content-Type"] || "";
|
|
2569
|
+
if (isFormData(contentType)) body = isPlainObject(body) ? Object.entries(body).map(([key, value]) => `--form '${key}=${String(value)}'`) : `--form 'file=@${body}'`;
|
|
2570
|
+
else if (isFormUrlEncoded(contentType)) body = isPlainObject(body) ? `--data '${Object.entries(body).map(([key, value]) => `${key}=${String(value)}`).join("&")}'` : String(body);
|
|
2571
|
+
else if (isText(contentType)) body = `--data '${String(body).replace(/"/g, "")}'`;
|
|
2572
|
+
else if (isXML(contentType)) body = `--data-binary $'${convertBodyToXML(body)}'`;
|
|
2573
|
+
else if (isCSV(contentType)) body = `--data-binary $'${stringifyOpenAPI(body).replace(/"/g, "").replace(/\\n/g, "\n")}'`;
|
|
2574
|
+
else if (isGraphQL(contentType)) {
|
|
2575
|
+
body = `--data '${stringifyOpenAPI(body)}'`;
|
|
2576
|
+
headersCopy["Content-Type"] = "application/json";
|
|
2577
|
+
} else if (isPDF(contentType)) body = `--data-binary '@${String(body)}'`;
|
|
2578
|
+
else if (isYAML(contentType)) body = `--data-binary $'${yaml.dump(body).replace(/'/g, "").replace(/\\n/g, "\n")}'`;
|
|
2579
|
+
else body = `--data '${stringifyOpenAPI(body, null, 2).replace(/\\n/g, "\n")}'`;
|
|
2580
|
+
return {
|
|
2581
|
+
body,
|
|
2582
|
+
headers: headersCopy
|
|
2583
|
+
};
|
|
2584
|
+
},
|
|
2585
|
+
getJavaScriptBody: (body, headers) => {
|
|
2586
|
+
if (!body || !headers) return;
|
|
2587
|
+
let code = "";
|
|
2588
|
+
const headersCopy = { ...headers };
|
|
2589
|
+
const contentType = headersCopy["Content-Type"] || "";
|
|
2590
|
+
if (isFormData(contentType)) {
|
|
2591
|
+
code += "const formData = new FormData();\n\n";
|
|
2592
|
+
if (isPlainObject(body)) Object.entries(body).forEach(([key, value]) => {
|
|
2593
|
+
code += `formData.append("${key}", "${String(value)}");\n`;
|
|
2594
|
+
});
|
|
2595
|
+
else if (typeof body === "string") code += `formData.append("file", "${body}");\n`;
|
|
2596
|
+
code += "\n";
|
|
2597
|
+
body = "formData";
|
|
2598
|
+
} else if (isFormUrlEncoded(contentType)) {
|
|
2599
|
+
code += "const params = new URLSearchParams();\n\n";
|
|
2600
|
+
if (isPlainObject(body)) Object.entries(body).forEach(([key, value]) => {
|
|
2601
|
+
code += `params.append("${key}", "${String(value)}");\n`;
|
|
2602
|
+
});
|
|
2603
|
+
code += "\n";
|
|
2604
|
+
body = "params.toString()";
|
|
2605
|
+
} else if (isGraphQL(contentType)) if (isPlainObject(body)) {
|
|
2606
|
+
Object.entries(body).forEach(([key, value]) => {
|
|
2607
|
+
code += `const ${key} = \`\n${indent(String(value), 4)}\`;\n\n`;
|
|
2608
|
+
});
|
|
2609
|
+
body = `JSON.stringify({ ${Object.keys(body).join(", ")} })`;
|
|
2610
|
+
headersCopy["Content-Type"] = "application/json";
|
|
2611
|
+
} else {
|
|
2612
|
+
code += `const query = \`\n${indent(String(body), 4)}\`;\n\n`;
|
|
2613
|
+
body = "JSON.stringify(query)";
|
|
2614
|
+
}
|
|
2615
|
+
else if (isCSV(contentType)) {
|
|
2616
|
+
code += "const csv = `\n";
|
|
2617
|
+
code += indent(String(body), 4);
|
|
2618
|
+
code += "`;\n\n";
|
|
2619
|
+
body = "csv";
|
|
2620
|
+
} else if (isPDF(contentType)) {
|
|
2621
|
+
code += "const formData = new FormData();\n\n";
|
|
2622
|
+
code += `formData.append("file", "${body}");\n\n`;
|
|
2623
|
+
body = "formData";
|
|
2624
|
+
} else if (isXML(contentType)) {
|
|
2625
|
+
code += "const xml = `\n";
|
|
2626
|
+
code += indent(convertBodyToXML(body), 4);
|
|
2627
|
+
code += "`;\n\n";
|
|
2628
|
+
body = "xml";
|
|
2629
|
+
} else if (isYAML(contentType)) {
|
|
2630
|
+
code += `const yamlBody = \`\n${indent(yaml.dump(body), 4)}\`;\n\n`;
|
|
2631
|
+
body = "yamlBody";
|
|
2632
|
+
} else if (isText(contentType)) body = stringifyOpenAPI(body, null, 2);
|
|
2633
|
+
else body = `JSON.stringify(${stringifyOpenAPI(body, null, 2)})`;
|
|
2634
|
+
return {
|
|
2635
|
+
body,
|
|
2636
|
+
code,
|
|
2637
|
+
headers: headersCopy
|
|
2638
|
+
};
|
|
2639
|
+
},
|
|
2640
|
+
getPythonBody: (body, headers) => {
|
|
2641
|
+
if (!body || !headers) return;
|
|
2642
|
+
let code = "";
|
|
2643
|
+
const contentType = headers["Content-Type"] || "";
|
|
2644
|
+
if (isFormData(contentType)) {
|
|
2645
|
+
code += "files = {\n";
|
|
2646
|
+
if (isPlainObject(body)) Object.entries(body).forEach(([key, value]) => {
|
|
2647
|
+
code += `${indent(`"${key}": "${String(value)}",`, 4)}\n`;
|
|
2648
|
+
});
|
|
2649
|
+
code += "}\n\n";
|
|
2650
|
+
body = "files";
|
|
2651
|
+
} else if (isPDF(contentType)) {
|
|
2652
|
+
code += "files = {\n";
|
|
2653
|
+
code += `${indent(`"file": "${body}",`, 4)}\n`;
|
|
2654
|
+
code += "}\n\n";
|
|
2655
|
+
body = "files";
|
|
2656
|
+
} else if (isXML(contentType)) body = JSON.stringify(convertBodyToXML(body));
|
|
2657
|
+
else if (isYAML(contentType)) {
|
|
2658
|
+
code += `yamlBody = \"\"\"\n${indent(yaml.dump(body), 4)}\"\"\"\n\n`;
|
|
2659
|
+
body = "yamlBody";
|
|
2660
|
+
} else body = stringifyOpenAPI(body, (_key, value) => {
|
|
2661
|
+
switch (value) {
|
|
2662
|
+
case true: return "$$__TRUE__$$";
|
|
2663
|
+
case false: return "$$__FALSE__$$";
|
|
2664
|
+
case null: return "$$__NULL__$$";
|
|
2665
|
+
default: return value;
|
|
2666
|
+
}
|
|
2667
|
+
}, 2).replaceAll("\"$$__TRUE__$$\"", "True").replaceAll("\"$$__FALSE__$$\"", "False").replaceAll("\"$$__NULL__$$\"", "None");
|
|
2668
|
+
return {
|
|
2669
|
+
body,
|
|
2670
|
+
code,
|
|
2671
|
+
headers
|
|
2672
|
+
};
|
|
2673
|
+
},
|
|
2674
|
+
getHTTPBody: (body, headers) => {
|
|
2675
|
+
if (!body || !headers) return void 0;
|
|
2676
|
+
const contentType = headers["Content-Type"] || "";
|
|
2677
|
+
const typeHandlers = {
|
|
2678
|
+
pdf: () => `${stringifyOpenAPI(body, null, 2)}`,
|
|
2679
|
+
formUrlEncoded: () => {
|
|
2680
|
+
return `"${(isPlainObject(body) ? Object.entries(body).map(([key, value]) => `${key}=${stringifyOpenAPI(value)}`).join("&") : stringifyOpenAPI(body)).replace(/"/g, "'")}"`;
|
|
2681
|
+
},
|
|
2682
|
+
text: () => `"${String(body)}"`,
|
|
2683
|
+
xml: () => {
|
|
2684
|
+
return `"${convertBodyToXML(body)}"`;
|
|
2685
|
+
},
|
|
2686
|
+
yaml: () => `"${yaml.dump(body).replace(/"/g, "\\\"")}"`,
|
|
2687
|
+
csv: () => `"${stringifyOpenAPI(body).replace(/"/g, "")}"`,
|
|
2688
|
+
default: () => `${stringifyOpenAPI(body, null, 2)}`
|
|
2689
|
+
};
|
|
2690
|
+
if (isPDF(contentType)) return typeHandlers.pdf();
|
|
2691
|
+
if (isFormUrlEncoded(contentType)) return typeHandlers.formUrlEncoded();
|
|
2692
|
+
if (isText(contentType)) return typeHandlers.text();
|
|
2693
|
+
if (isXML(contentType)) return typeHandlers.xml();
|
|
2694
|
+
if (isYAML(contentType)) return typeHandlers.yaml();
|
|
2695
|
+
if (isCSV(contentType)) return typeHandlers.csv();
|
|
2696
|
+
return typeHandlers.default();
|
|
2697
|
+
}
|
|
2698
|
+
};
|
|
2699
|
+
/**
|
|
2700
|
+
* Converts a body to XML format
|
|
2701
|
+
*/
|
|
2702
|
+
function convertBodyToXML(body) {
|
|
2703
|
+
if (typeof body === "string" && body.trim().startsWith("<")) return body;
|
|
2704
|
+
if (typeof body !== "object" || body === null) try {
|
|
2705
|
+
body = JSON.parse(body);
|
|
2706
|
+
} catch {
|
|
2707
|
+
return body;
|
|
2708
|
+
}
|
|
2709
|
+
return json2xml(body).replace(/"/g, "").replace(/\\n/g, "\n").replace(/\\t/g, " ");
|
|
2710
|
+
}
|
|
2711
|
+
|
|
2712
|
+
//#endregion
|
|
2713
|
+
//#region src/OpenAPICodeSample.tsx
|
|
2714
|
+
const CUSTOM_CODE_SAMPLES_KEYS = [
|
|
2715
|
+
"x-custom-examples",
|
|
2716
|
+
"x-code-samples",
|
|
2717
|
+
"x-codeSamples"
|
|
2718
|
+
];
|
|
2719
|
+
/**
|
|
2720
|
+
* Display code samples to execute the operation.
|
|
2721
|
+
* It supports the Redocly custom syntax as well (https://redocly.com/docs/api-reference-docs/specification-extensions/x-code-samples/)
|
|
2722
|
+
*/
|
|
2723
|
+
function OpenAPICodeSample(props) {
|
|
2724
|
+
const { data, context } = props;
|
|
2725
|
+
if (data.operation["x-codeSamples"] === false) return null;
|
|
2726
|
+
const customCodeSamples = getCustomCodeSamples(props);
|
|
2727
|
+
if (data["x-codeSamples"] === false && !customCodeSamples) return null;
|
|
2728
|
+
const samples = customCodeSamples ?? generateCodeSamples(props);
|
|
2729
|
+
if (samples.length === 0) return null;
|
|
2730
|
+
return <OpenAPICodeSampleBody context={getOpenAPIClientContext(context)} data={data} items={samples} selectIcon={context.icons.chevronDown} />;
|
|
2731
|
+
}
|
|
2732
|
+
/**
|
|
2733
|
+
* Generate code samples for the operation.
|
|
2734
|
+
*/
|
|
2735
|
+
function generateCodeSamples(props) {
|
|
2736
|
+
const { data, context } = props;
|
|
2737
|
+
const searchParams = new URLSearchParams();
|
|
2738
|
+
const headersObject = {};
|
|
2739
|
+
(Array.isArray(data.operation.parameters) ? data.operation.parameters : []).forEach((param) => {
|
|
2740
|
+
if (!param) return;
|
|
2741
|
+
if (param.in === "header" && param.required) {
|
|
2742
|
+
const example = param.schema ? generateSchemaExample(param.schema, { mode: "write" }) : void 0;
|
|
2743
|
+
if (example !== void 0 && param.name) headersObject[param.name] = typeof example !== "string" ? stringifyOpenAPI(example) : example;
|
|
2744
|
+
} else if (param.in === "query" && param.required) {
|
|
2745
|
+
const example = param.schema ? generateSchemaExample(param.schema, { mode: "write" }) : void 0;
|
|
2746
|
+
if (example !== void 0 && param.name) searchParams.append(param.name, String(Array.isArray(example) ? example[0] : example));
|
|
2747
|
+
}
|
|
2748
|
+
});
|
|
2749
|
+
const requestBody = !checkIsReference(data.operation.requestBody) ? data.operation.requestBody : void 0;
|
|
2750
|
+
const defaultServerUrl = getDefaultServerURL(data.servers);
|
|
2751
|
+
let serverUrlPath = defaultServerUrl ? parseHostAndPath(defaultServerUrl).path : "";
|
|
2752
|
+
serverUrlPath = serverUrlPath === "/" ? "" : serverUrlPath;
|
|
2753
|
+
const serverUrlOrigin = (data.servers[0] ? resolveURLWithPrefillCodePlaceholdersFromServer(data.servers[0], defaultServerUrl) : defaultServerUrl).replaceAll(serverUrlPath, "");
|
|
2754
|
+
const path = serverUrlPath + data.path + (searchParams.size ? `?${searchParams.toString()}` : "");
|
|
2755
|
+
const genericHeaders = {
|
|
2756
|
+
...getSecurityHeaders({
|
|
2757
|
+
securityRequirement: data.operation.security,
|
|
2758
|
+
securities: data.securities
|
|
2759
|
+
}),
|
|
2760
|
+
...headersObject
|
|
2761
|
+
};
|
|
2762
|
+
const mediaTypeRendererFactories = Object.entries(requestBody?.content ?? {}).map(([mediaType, mediaTypeObject]) => {
|
|
2763
|
+
return (generator) => {
|
|
2764
|
+
const mediaTypeHeaders = {
|
|
2765
|
+
...genericHeaders,
|
|
2766
|
+
"Content-Type": mediaType
|
|
2767
|
+
};
|
|
2768
|
+
return {
|
|
2769
|
+
mediaType,
|
|
2770
|
+
element: context.renderCodeBlock({
|
|
2771
|
+
code: generator.generate({
|
|
2772
|
+
url: {
|
|
2773
|
+
origin: serverUrlOrigin,
|
|
2774
|
+
path
|
|
2775
|
+
},
|
|
2776
|
+
method: data.method,
|
|
2777
|
+
body: void 0,
|
|
2778
|
+
headers: mediaTypeHeaders
|
|
2779
|
+
}),
|
|
2780
|
+
syntax: generator.syntax
|
|
2781
|
+
}),
|
|
2782
|
+
examples: generateMediaTypeExamples(mediaTypeObject, { mode: "write" }).map((example) => ({
|
|
2783
|
+
example,
|
|
2784
|
+
element: context.renderCodeBlock({
|
|
2785
|
+
code: generator.generate({
|
|
2786
|
+
url: {
|
|
2787
|
+
origin: serverUrlOrigin,
|
|
2788
|
+
path
|
|
2789
|
+
},
|
|
2790
|
+
method: data.method,
|
|
2791
|
+
body: example.value,
|
|
2792
|
+
headers: mediaTypeHeaders
|
|
2793
|
+
}),
|
|
2794
|
+
syntax: generator.syntax
|
|
2795
|
+
})
|
|
2796
|
+
}))
|
|
2797
|
+
};
|
|
2798
|
+
};
|
|
2799
|
+
});
|
|
2800
|
+
return codeSampleGenerators.map((generator) => {
|
|
2801
|
+
if (mediaTypeRendererFactories.length > 0) {
|
|
2802
|
+
const renderers = mediaTypeRendererFactories.map((generate) => generate(generator));
|
|
2803
|
+
return {
|
|
2804
|
+
key: `default-${generator.id}`,
|
|
2805
|
+
label: generator.label,
|
|
2806
|
+
body: <OpenAPIMediaTypeExamplesBody method={data.method} path={data.path} renderers={renderers} blockKey={context.blockKey} />,
|
|
2807
|
+
footer: <OpenAPICodeSampleFooter renderers={renderers} data={data} context={context} />
|
|
2808
|
+
};
|
|
2809
|
+
}
|
|
2810
|
+
return {
|
|
2811
|
+
key: `default-${generator.id}`,
|
|
2812
|
+
label: generator.label,
|
|
2813
|
+
body: context.renderCodeBlock({
|
|
2814
|
+
code: generator.generate({
|
|
2815
|
+
url: {
|
|
2816
|
+
origin: serverUrlOrigin,
|
|
2817
|
+
path
|
|
2818
|
+
},
|
|
2819
|
+
method: data.method,
|
|
2820
|
+
body: void 0,
|
|
2821
|
+
headers: genericHeaders
|
|
2822
|
+
}),
|
|
2823
|
+
syntax: generator.syntax
|
|
2824
|
+
}),
|
|
2825
|
+
footer: <OpenAPICodeSampleFooter data={data} renderers={[]} context={context} />
|
|
2826
|
+
};
|
|
2827
|
+
});
|
|
2828
|
+
}
|
|
2829
|
+
function OpenAPICodeSampleFooter(props) {
|
|
2830
|
+
const { data, context, renderers } = props;
|
|
2831
|
+
const { method, path, securities, servers } = data;
|
|
2832
|
+
const { specUrl } = context;
|
|
2833
|
+
const hideTryItPanel = data["x-hideTryItPanel"] || data.operation["x-hideTryItPanel"];
|
|
2834
|
+
const hasMultipleMediaTypes = renderers.length > 1 || renderers.some((renderer) => renderer.examples.length > 0);
|
|
2835
|
+
if (hideTryItPanel && !hasMultipleMediaTypes) return null;
|
|
2836
|
+
if (!validateHttpMethod(method)) return null;
|
|
2837
|
+
return <div className="openapi-codesample-footer">
|
|
2838
|
+
{hasMultipleMediaTypes ? <OpenAPIMediaTypeExamplesSelector method={data.method} path={data.path} renderers={renderers} selectIcon={context.icons.chevronDown} blockKey={context.blockKey} /> : <span />}
|
|
2839
|
+
{!hideTryItPanel && <ScalarApiButton context={getOpenAPIClientContext(context)} method={method} path={path} securities={securities} servers={servers} specUrl={specUrl} />}
|
|
2840
|
+
</div>;
|
|
2841
|
+
}
|
|
2842
|
+
/**
|
|
2843
|
+
* Get custom code samples for the operation.
|
|
2844
|
+
*/
|
|
2845
|
+
function getCustomCodeSamples(props) {
|
|
2846
|
+
const { data, context } = props;
|
|
2847
|
+
let customCodeSamples = null;
|
|
2848
|
+
CUSTOM_CODE_SAMPLES_KEYS.forEach((key) => {
|
|
2849
|
+
const customSamples = data.operation[key];
|
|
2850
|
+
if (customSamples && Array.isArray(customSamples)) customCodeSamples = customSamples.filter((sample) => {
|
|
2851
|
+
return typeof sample.source === "string" && typeof sample.lang === "string";
|
|
2852
|
+
}).map((sample, index) => ({
|
|
2853
|
+
key: `custom-sample-${sample.lang}-${index}`,
|
|
2854
|
+
label: sample.label || sample.lang,
|
|
2855
|
+
body: context.renderCodeBlock({
|
|
2856
|
+
code: sample.source,
|
|
2857
|
+
syntax: sample.lang
|
|
2858
|
+
}),
|
|
2859
|
+
footer: <OpenAPICodeSampleFooter renderers={[]} data={data} context={context} />
|
|
2860
|
+
}));
|
|
2861
|
+
});
|
|
2862
|
+
return customCodeSamples;
|
|
2863
|
+
}
|
|
2864
|
+
function getSecurityHeaders(args) {
|
|
2865
|
+
const { securityRequirement, securities } = args;
|
|
2866
|
+
const operationSecurityInfo = extractOperationSecurityInfo({
|
|
2867
|
+
securityRequirement,
|
|
2868
|
+
securities
|
|
2869
|
+
});
|
|
2870
|
+
if (operationSecurityInfo.length === 0) return {};
|
|
2871
|
+
const selectedSecurity = operationSecurityInfo.at(0);
|
|
2872
|
+
if (!selectedSecurity) return {};
|
|
2873
|
+
const headers = {};
|
|
2874
|
+
for (const security of selectedSecurity.schemes) switch (security.type) {
|
|
2875
|
+
case "http": {
|
|
2876
|
+
let scheme = security.scheme;
|
|
2877
|
+
const format = resolvePrefillCodePlaceholderFromSecurityScheme({
|
|
2878
|
+
security,
|
|
2879
|
+
defaultPlaceholderValue: scheme?.includes("basic") ? "username:password" : "YOUR_SECRET_TOKEN"
|
|
2880
|
+
});
|
|
2881
|
+
if (scheme?.includes("bearer")) scheme = "Bearer";
|
|
2882
|
+
else if (scheme?.includes("basic")) scheme = "Basic";
|
|
2883
|
+
else if (scheme?.includes("token")) scheme = "Token";
|
|
2884
|
+
headers.Authorization = `${scheme} ${format}`;
|
|
2885
|
+
break;
|
|
2886
|
+
}
|
|
2887
|
+
case "apiKey": {
|
|
2888
|
+
if (security.in !== "header") break;
|
|
2889
|
+
const name = security.name ?? "Authorization";
|
|
2890
|
+
headers[name] = resolvePrefillCodePlaceholderFromSecurityScheme({
|
|
2891
|
+
security,
|
|
2892
|
+
defaultPlaceholderValue: "YOUR_API_KEY"
|
|
2893
|
+
});
|
|
2894
|
+
break;
|
|
2895
|
+
}
|
|
2896
|
+
case "oauth2":
|
|
2897
|
+
headers.Authorization = `Bearer ${resolvePrefillCodePlaceholderFromSecurityScheme({
|
|
2898
|
+
security,
|
|
2899
|
+
defaultPlaceholderValue: "YOUR_OAUTH2_TOKEN"
|
|
2900
|
+
})}`;
|
|
2901
|
+
break;
|
|
2902
|
+
default: break;
|
|
2903
|
+
}
|
|
2904
|
+
return headers;
|
|
2905
|
+
}
|
|
2906
|
+
function validateHttpMethod(method) {
|
|
2907
|
+
return [
|
|
2908
|
+
"get",
|
|
2909
|
+
"post",
|
|
2910
|
+
"put",
|
|
2911
|
+
"delete",
|
|
2912
|
+
"patch",
|
|
2913
|
+
"head",
|
|
2914
|
+
"options",
|
|
2915
|
+
"trace"
|
|
2916
|
+
].includes(method);
|
|
2917
|
+
}
|
|
2918
|
+
|
|
2919
|
+
//#endregion
|
|
2920
|
+
//#region src/OpenAPIMediaType.tsx
|
|
2921
|
+
/**
|
|
2922
|
+
* Get the state of the response examples select.
|
|
2923
|
+
*/
|
|
2924
|
+
function useMediaTypesState(stateKey, initialKey = "default") {
|
|
2925
|
+
return useSelectState(stateKey, initialKey);
|
|
2926
|
+
}
|
|
2927
|
+
function useMediaTypeExamplesState(stateKey, initialKey = "default") {
|
|
2928
|
+
return useSelectState(stateKey, initialKey);
|
|
2929
|
+
}
|
|
2930
|
+
function OpenAPIMediaTypeContent(props) {
|
|
2931
|
+
const { stateKey, items, selectIcon, context } = props;
|
|
2932
|
+
const state = useMediaTypesState(stateKey, items[0]?.key);
|
|
2933
|
+
const examples = items.find((item) => item.key === state.key)?.examples ?? [];
|
|
2934
|
+
if (!items.length && !examples.length) return null;
|
|
2935
|
+
return <StaticSection footer={items.length > 1 || examples.length > 1 ? <OpenAPIMediaTypeFooter items={items} examples={examples} selectIcon={selectIcon} stateKey={stateKey} /> : null} className="openapi-response-media-types-examples">
|
|
2936
|
+
<OpenAPIMediaTypeBody context={context} stateKey={stateKey} items={items} examples={examples} />
|
|
2937
|
+
</StaticSection>;
|
|
2938
|
+
}
|
|
2939
|
+
function OpenAPIMediaTypeFooter(props) {
|
|
2940
|
+
const { items, examples, stateKey, selectIcon } = props;
|
|
2941
|
+
return <>
|
|
2942
|
+
{items.length > 1 && <OpenAPISelect icon={selectIcon} items={items} stateKey={stateKey} placement="bottom start">
|
|
2943
|
+
{items.map((item) => <OpenAPISelectItem key={item.key} id={item.key} value={item}>
|
|
2944
|
+
<span>{item.label}</span>
|
|
2945
|
+
</OpenAPISelectItem>)}
|
|
2946
|
+
</OpenAPISelect>}
|
|
2947
|
+
|
|
2948
|
+
{examples && examples.length > 1 ? <OpenAPISelect icon={selectIcon} items={examples} stateKey={`${stateKey}-examples`} placement="bottom start">
|
|
2949
|
+
{examples.map((example) => <OpenAPISelectItem key={example.key} id={example.key} value={example}>
|
|
2950
|
+
<span>{example.label}</span>
|
|
2951
|
+
</OpenAPISelectItem>)}
|
|
2952
|
+
</OpenAPISelect> : null}
|
|
2953
|
+
</>;
|
|
2954
|
+
}
|
|
2955
|
+
function OpenAPIMediaTypeBody(props) {
|
|
2956
|
+
const { stateKey, items, examples, context } = props;
|
|
2957
|
+
const state = useMediaTypesState(stateKey, items[0]?.key);
|
|
2958
|
+
const selectedItem = items.find((item) => item.key === state.key) ?? items[0];
|
|
2959
|
+
const exampleState = useMediaTypeExamplesState(`${stateKey}-examples`, selectedItem?.examples?.[0]?.key);
|
|
2960
|
+
if (!selectedItem) return null;
|
|
2961
|
+
if (examples) {
|
|
2962
|
+
const selectedExample = examples.find((example) => example.key === exampleState.key) ?? examples[0];
|
|
2963
|
+
if (!selectedExample) return <OpenAPIEmptyExample context={context} />;
|
|
2964
|
+
return selectedExample.body;
|
|
2965
|
+
}
|
|
2966
|
+
return selectedItem.body;
|
|
2967
|
+
}
|
|
2968
|
+
|
|
2969
|
+
//#endregion
|
|
2970
|
+
//#region src/OpenAPIResponseExampleContent.tsx
|
|
2971
|
+
/**
|
|
2972
|
+
* Get the state of the response examples select.
|
|
2973
|
+
*/
|
|
2974
|
+
function useResponseExamplesState(blockKey, initialKey = "default") {
|
|
2975
|
+
return useSelectState(getResponseExampleStateKey(blockKey), initialKey);
|
|
2976
|
+
}
|
|
2977
|
+
function OpenAPIResponseExampleContent(props) {
|
|
2978
|
+
const { blockKey, items, selectIcon } = props;
|
|
2979
|
+
return <StaticSection header={<OpenAPIResponseExampleHeader selectIcon={selectIcon} blockKey={blockKey} items={items} />} className="openapi-response-examples">
|
|
2980
|
+
<OpenAPIResponseExampleBody blockKey={blockKey} items={items} />
|
|
2981
|
+
</StaticSection>;
|
|
2982
|
+
}
|
|
2983
|
+
function OpenAPIResponseExampleHeader(props) {
|
|
2984
|
+
const { items, blockKey, selectIcon } = props;
|
|
2985
|
+
if (items.length === 1) {
|
|
2986
|
+
const item = items[0];
|
|
2987
|
+
if (!item) return null;
|
|
2988
|
+
return <span className="openapi-response-examples-statuscode-title">
|
|
2989
|
+
<OpenAPIResponseExampleItem item={item} />
|
|
2990
|
+
</span>;
|
|
2991
|
+
}
|
|
2992
|
+
return <OpenAPISelect items={items} icon={selectIcon} stateKey={getResponseExampleStateKey(blockKey)} placement="bottom start">
|
|
2993
|
+
{items.map((item) => <OpenAPISelectItem key={item.key} id={item.key} value={item}>
|
|
2994
|
+
<OpenAPIResponseExampleItem item={item} />
|
|
2995
|
+
</OpenAPISelectItem>)}
|
|
2996
|
+
</OpenAPISelect>;
|
|
2997
|
+
}
|
|
2998
|
+
function OpenAPIResponseExampleItem(props) {
|
|
2999
|
+
const { item } = props;
|
|
3000
|
+
return <>
|
|
3001
|
+
<span className={clsx("openapi-statuscode", `openapi-statuscode-${getStatusCodeClassName(item.statusCode)}`, "openapi-response-examples-statuscode")}>
|
|
3002
|
+
{item.statusCode}
|
|
3003
|
+
</span>
|
|
3004
|
+
<span className="openapi-response-examples-statuscode-label">{item.label}</span>
|
|
3005
|
+
</>;
|
|
3006
|
+
}
|
|
3007
|
+
function OpenAPIResponseExampleBody(props) {
|
|
3008
|
+
const { blockKey, items } = props;
|
|
3009
|
+
const state = useResponseExamplesState(blockKey, items[0]?.key);
|
|
3010
|
+
const selectedItem = items.find((item) => item.key === state.key) ?? items[0];
|
|
3011
|
+
if (!selectedItem) return null;
|
|
3012
|
+
return <div className="openapi-response-examples-panel">{selectedItem.body}</div>;
|
|
3013
|
+
}
|
|
3014
|
+
/**
|
|
3015
|
+
* Return the state key for the response examples.
|
|
3016
|
+
*/
|
|
3017
|
+
function getResponseExampleStateKey(blockKey) {
|
|
3018
|
+
return createStateKey("openapi-responses", blockKey);
|
|
3019
|
+
}
|
|
3020
|
+
|
|
3021
|
+
//#endregion
|
|
3022
|
+
//#region src/OpenAPIResponseExample.tsx
|
|
3023
|
+
/**
|
|
3024
|
+
* Display an example of the response content.
|
|
3025
|
+
*/
|
|
3026
|
+
function OpenAPIResponseExample(props) {
|
|
3027
|
+
const { data, context } = props;
|
|
3028
|
+
if (!data.operation.responses) return null;
|
|
3029
|
+
const responses = Object.entries(data.operation.responses);
|
|
3030
|
+
responses.sort(([a], [b]) => {
|
|
3031
|
+
if (a === "default") return 1;
|
|
3032
|
+
if (b === "default") return -1;
|
|
3033
|
+
if (a === "200") return -1;
|
|
3034
|
+
if (b === "200") return 1;
|
|
3035
|
+
return Number(a) - Number(b);
|
|
3036
|
+
});
|
|
3037
|
+
const tabs = responses.filter(([_, responseObject]) => responseObject && typeof responseObject === "object" && !responseObject["x-hideSample"]).map(([key, responseObject]) => {
|
|
3038
|
+
const description = resolveDescription(responseObject);
|
|
3039
|
+
const label = description ? <Markdown key={`response-description-${key}`} source={description} /> : getStatusCodeDefaultLabel(key, context);
|
|
3040
|
+
if (checkIsReference(responseObject)) return {
|
|
3041
|
+
key,
|
|
3042
|
+
label,
|
|
3043
|
+
statusCode: key,
|
|
3044
|
+
body: <OpenAPIExample example={getExampleFromReference(responseObject, context)} context={context} syntax="json" />
|
|
3045
|
+
};
|
|
3046
|
+
if (!responseObject.content || Object.keys(responseObject.content).length === 0) return {
|
|
3047
|
+
key,
|
|
3048
|
+
label,
|
|
3049
|
+
statusCode: key,
|
|
3050
|
+
body: <OpenAPIEmptyExample context={context} />
|
|
3051
|
+
};
|
|
3052
|
+
return {
|
|
3053
|
+
key,
|
|
3054
|
+
label,
|
|
3055
|
+
statusCode: key,
|
|
3056
|
+
body: <OpenAPIResponse$1 context={context} content={responseObject.content} />
|
|
3057
|
+
};
|
|
3058
|
+
});
|
|
3059
|
+
if (tabs.length === 0) return null;
|
|
3060
|
+
return <OpenAPIResponseExampleContent selectIcon={context.icons.chevronDown} blockKey={context.blockKey} items={tabs} />;
|
|
3061
|
+
}
|
|
3062
|
+
function OpenAPIResponse$1(props) {
|
|
3063
|
+
const { context, content } = props;
|
|
3064
|
+
const entries = Object.entries(content);
|
|
3065
|
+
if (!entries[0]) throw new Error("One media type is required");
|
|
3066
|
+
const tabs = entries.map((entry) => {
|
|
3067
|
+
const [mediaType, mediaTypeObject] = entry;
|
|
3068
|
+
if (!mediaTypeObject) return {
|
|
3069
|
+
key: mediaType,
|
|
3070
|
+
label: mediaType,
|
|
3071
|
+
body: <OpenAPIEmptyExample context={context} />
|
|
3072
|
+
};
|
|
3073
|
+
return {
|
|
3074
|
+
key: mediaType,
|
|
3075
|
+
label: mediaType,
|
|
3076
|
+
body: <></>,
|
|
3077
|
+
examples: getExamples({
|
|
3078
|
+
mediaTypeObject,
|
|
3079
|
+
mediaType,
|
|
3080
|
+
context
|
|
3081
|
+
})
|
|
3082
|
+
};
|
|
3083
|
+
});
|
|
3084
|
+
return <OpenAPIMediaTypeContent selectIcon={context.icons.chevronDown} stateKey={createStateKey("response-media-types", context.blockKey)} items={tabs} context={getOpenAPIClientContext(context)} />;
|
|
3085
|
+
}
|
|
3086
|
+
|
|
3087
|
+
//#endregion
|
|
3088
|
+
//#region ../../node_modules/@react-stately/utils/dist/useControlledState.mjs
|
|
3089
|
+
function $458b0a5536c1a7cf$export$40bfa8c7b0832715(value, defaultValue, onChange) {
|
|
3090
|
+
let [stateValue, setStateValue] = useState(value || defaultValue);
|
|
3091
|
+
let isControlledRef = useRef(value !== void 0);
|
|
3092
|
+
let isControlled = value !== void 0;
|
|
3093
|
+
useEffect(() => {
|
|
3094
|
+
let wasControlled = isControlledRef.current;
|
|
3095
|
+
if (wasControlled !== isControlled) console.warn(`WARN: A component changed from ${wasControlled ? "controlled" : "uncontrolled"} to ${isControlled ? "controlled" : "uncontrolled"}.`);
|
|
3096
|
+
isControlledRef.current = isControlled;
|
|
3097
|
+
}, [isControlled]);
|
|
3098
|
+
let currentValue = isControlled ? value : stateValue;
|
|
3099
|
+
let setValue = useCallback((value$1, ...args) => {
|
|
3100
|
+
let onChangeCaller = (value$2, ...onChangeArgs) => {
|
|
3101
|
+
if (onChange) {
|
|
3102
|
+
if (!Object.is(currentValue, value$2)) onChange(value$2, ...onChangeArgs);
|
|
3103
|
+
}
|
|
3104
|
+
if (!isControlled) currentValue = value$2;
|
|
3105
|
+
};
|
|
3106
|
+
if (typeof value$1 === "function") {
|
|
3107
|
+
console.warn("We can not support a function callback. See Github Issues for details https://github.com/adobe/react-spectrum/issues/2320");
|
|
3108
|
+
let updateFunction = (oldValue, ...functionArgs) => {
|
|
3109
|
+
let interceptedValue = value$1(isControlled ? currentValue : oldValue, ...functionArgs);
|
|
3110
|
+
onChangeCaller(interceptedValue, ...args);
|
|
3111
|
+
if (!isControlled) return interceptedValue;
|
|
3112
|
+
return oldValue;
|
|
3113
|
+
};
|
|
3114
|
+
setStateValue(updateFunction);
|
|
3115
|
+
} else {
|
|
3116
|
+
if (!isControlled) setStateValue(value$1);
|
|
3117
|
+
onChangeCaller(value$1, ...args);
|
|
3118
|
+
}
|
|
3119
|
+
}, [
|
|
3120
|
+
isControlled,
|
|
3121
|
+
currentValue,
|
|
3122
|
+
onChange
|
|
3123
|
+
]);
|
|
3124
|
+
return [currentValue, setValue];
|
|
3125
|
+
}
|
|
3126
|
+
|
|
3127
|
+
//#endregion
|
|
3128
|
+
//#region ../../node_modules/@react-stately/disclosure/dist/useDisclosureState.mjs
|
|
3129
|
+
function $bf996d45f4a36925$export$3fcbf6e4407997e0(props) {
|
|
3130
|
+
let [isExpanded, setExpanded] = $458b0a5536c1a7cf$export$40bfa8c7b0832715(props.isExpanded, props.defaultExpanded || false, props.onExpandedChange);
|
|
3131
|
+
return {
|
|
3132
|
+
isExpanded,
|
|
3133
|
+
setExpanded,
|
|
3134
|
+
expand: useCallback(() => {
|
|
3135
|
+
setExpanded(true);
|
|
3136
|
+
}, [setExpanded]),
|
|
3137
|
+
collapse: useCallback(() => {
|
|
3138
|
+
setExpanded(false);
|
|
3139
|
+
}, [setExpanded]),
|
|
3140
|
+
toggle: useCallback(() => {
|
|
3141
|
+
setExpanded(!isExpanded);
|
|
3142
|
+
}, [setExpanded, isExpanded])
|
|
3143
|
+
};
|
|
3144
|
+
}
|
|
3145
|
+
|
|
3146
|
+
//#endregion
|
|
3147
|
+
//#region ../../node_modules/@react-stately/disclosure/dist/useDisclosureGroupState.mjs
|
|
3148
|
+
function $9385b3affbdec831$export$f36461af0ef4707d(props) {
|
|
3149
|
+
let { allowsMultipleExpanded = false, isDisabled = false } = props;
|
|
3150
|
+
let [expandedKeys, setExpandedKeys] = $458b0a5536c1a7cf$export$40bfa8c7b0832715(useMemo(() => props.expandedKeys ? new Set(props.expandedKeys) : void 0, [props.expandedKeys]), useMemo(() => props.defaultExpandedKeys ? new Set(props.defaultExpandedKeys) : /* @__PURE__ */ new Set(), [props.defaultExpandedKeys]), props.onExpandedChange);
|
|
3151
|
+
useEffect(() => {
|
|
3152
|
+
if (!allowsMultipleExpanded && expandedKeys.size > 1) setExpandedKeys(new Set([expandedKeys.values().next().value]));
|
|
3153
|
+
});
|
|
3154
|
+
return {
|
|
3155
|
+
allowsMultipleExpanded,
|
|
3156
|
+
isDisabled,
|
|
3157
|
+
expandedKeys,
|
|
3158
|
+
setExpandedKeys,
|
|
3159
|
+
toggleKey(key) {
|
|
3160
|
+
let keys;
|
|
3161
|
+
if (allowsMultipleExpanded) {
|
|
3162
|
+
keys = new Set(expandedKeys);
|
|
3163
|
+
if (keys.has(key)) keys.delete(key);
|
|
3164
|
+
else keys.add(key);
|
|
3165
|
+
} else keys = new Set(expandedKeys.has(key) ? [] : [key]);
|
|
3166
|
+
setExpandedKeys(keys);
|
|
3167
|
+
}
|
|
3168
|
+
};
|
|
3169
|
+
}
|
|
3170
|
+
|
|
3171
|
+
//#endregion
|
|
3172
|
+
//#region src/InteractiveSection.tsx
|
|
3173
|
+
/**
|
|
3174
|
+
* To optimize rendering, most of the components are server-components,
|
|
3175
|
+
* and the interactiveness is mainly handled by a few key components like this one.
|
|
3176
|
+
*/
|
|
3177
|
+
function InteractiveSection(props) {
|
|
3178
|
+
const { id, className, toggeable = false, defaultOpened = true, tabs = [], defaultTab = tabs[0]?.key, header, overlay, toggleIcon = "▶", selectIcon, stateKey = "interactive-section" } = props;
|
|
3179
|
+
const state = $bf996d45f4a36925$export$3fcbf6e4407997e0({ defaultExpanded: defaultOpened });
|
|
3180
|
+
const panelRef = useRef(null);
|
|
3181
|
+
const triggerRef = useRef(null);
|
|
3182
|
+
const { buttonProps: triggerProps, panelProps } = useDisclosure({}, state, panelRef);
|
|
3183
|
+
const { buttonProps } = useButton(triggerProps, triggerRef);
|
|
3184
|
+
const { isFocusVisible, focusProps } = useFocusRing();
|
|
3185
|
+
const store = useSelectState(stateKey, defaultTab);
|
|
3186
|
+
const selectedTab = tabs.find((tab) => tab.key === store.key) ?? tabs[0];
|
|
3187
|
+
return <Section id={id} className={clsx("openapi-section", toggeable ? "openapi-section-toggeable" : null, className, toggeable ? `${className}-${state.isExpanded ? "opened" : "closed"}` : null)}>
|
|
3188
|
+
{header ? <SectionHeader onClick={() => {
|
|
3189
|
+
if (toggeable) state.toggle();
|
|
3190
|
+
}} className={className}>
|
|
3191
|
+
<SectionHeaderContent className={className}>
|
|
3192
|
+
{selectedTab?.body && toggeable ? <button {...mergeProps(buttonProps, focusProps)} ref={triggerRef} className={clsx("openapi-section-toggle", `${className}-toggle`)} style={{ outline: isFocusVisible ? "2px solid rgb(var(--primary-color-500) / 0.4)" : "none" }}>
|
|
3193
|
+
{toggleIcon}
|
|
3194
|
+
</button> : null}
|
|
3195
|
+
{header}
|
|
3196
|
+
</SectionHeaderContent>
|
|
3197
|
+
{}
|
|
3198
|
+
<div className={clsx("openapi-section-header-controls", `${className}-header-controls`)} onClick={(event) => {
|
|
3199
|
+
event.stopPropagation();
|
|
3200
|
+
}}>
|
|
3201
|
+
{tabs.length > 0 ? <OpenAPISelect stateKey={stateKey} items={tabs} onSelectionChange={() => {
|
|
3202
|
+
state.expand();
|
|
3203
|
+
}} icon={selectIcon} placement="bottom end">
|
|
3204
|
+
{tabs.map((tab) => <OpenAPISelectItem key={tab.key} id={tab.key} value={tab}>
|
|
3205
|
+
{tab.label}
|
|
3206
|
+
</OpenAPISelectItem>)}
|
|
3207
|
+
</OpenAPISelect> : null}
|
|
3208
|
+
</div>
|
|
3209
|
+
</SectionHeader> : null}
|
|
3210
|
+
{(!toggeable || state.isExpanded) && selectedTab?.body ? <SectionBody ref={panelRef} {...panelProps} className={className}>
|
|
3211
|
+
{selectedTab?.body}
|
|
3212
|
+
</SectionBody> : null}
|
|
3213
|
+
{overlay ? <div className={clsx("openapi-section-overlay", `${className}-overlay`)}>
|
|
3214
|
+
{overlay}
|
|
3215
|
+
</div> : null}
|
|
3216
|
+
</Section>;
|
|
3217
|
+
}
|
|
3218
|
+
|
|
3219
|
+
//#endregion
|
|
3220
|
+
//#region src/OpenAPIRequestBodyHeaderType.tsx
|
|
3221
|
+
/**
|
|
3222
|
+
* Display the type of a request body. It only displays the type if the selected content is an array.
|
|
3223
|
+
*/
|
|
3224
|
+
function OpenAPIRequestBodyHeaderType(props) {
|
|
3225
|
+
const { requestBody, stateKey } = props;
|
|
3226
|
+
const content = requestBody.content ?? {};
|
|
3227
|
+
const state = useSelectState(stateKey, Object.keys(content)[0]);
|
|
3228
|
+
const selectedContentMediaType = Object.entries(content).find(([contentType]) => contentType === state.key)?.[1];
|
|
3229
|
+
if (!selectedContentMediaType || !selectedContentMediaType.schema?.type || selectedContentMediaType.schema.type !== "array") return null;
|
|
3230
|
+
return <span className="openapi-requestbody-header-type">
|
|
3231
|
+
{`${getSchemaTitle(selectedContentMediaType.schema)}`}
|
|
3232
|
+
</span>;
|
|
3233
|
+
}
|
|
3234
|
+
|
|
3235
|
+
//#endregion
|
|
3236
|
+
//#region src/OpenAPIRequestBody.tsx
|
|
3237
|
+
/**
|
|
3238
|
+
* Display an interactive request body.
|
|
3239
|
+
*/
|
|
3240
|
+
function OpenAPIRequestBody(props) {
|
|
3241
|
+
const { requestBody, context, data } = props;
|
|
3242
|
+
if (checkIsReference(requestBody)) return null;
|
|
3243
|
+
const stateKey = createStateKey("request-body-media-type", context.blockKey);
|
|
3244
|
+
return <InteractiveSection header={<>
|
|
3245
|
+
<span>{t(context.translation, "name" in data ? "payload" : "body")}</span>
|
|
3246
|
+
<OpenAPIRequestBodyHeaderType requestBody={requestBody} stateKey={stateKey} />
|
|
3247
|
+
</>} className="openapi-requestbody" stateKey={stateKey} selectIcon={context.icons.chevronDown} tabs={Object.entries(requestBody.content ?? {}).map(([contentType, mediaTypeObject]) => {
|
|
3248
|
+
return {
|
|
3249
|
+
key: contentType,
|
|
3250
|
+
label: contentType,
|
|
3251
|
+
body: <OpenAPIRootSchema schema={mediaTypeObject.schema ?? {}} context={context} key={contentType} />
|
|
3252
|
+
};
|
|
3253
|
+
})} />;
|
|
3254
|
+
}
|
|
3255
|
+
|
|
3256
|
+
//#endregion
|
|
3257
|
+
//#region src/OpenAPIDisclosureGroup.tsx
|
|
3258
|
+
const DisclosureGroupStateContext = createContext(null);
|
|
3259
|
+
/**
|
|
3260
|
+
* Display an interactive OpenAPI disclosure group.
|
|
3261
|
+
*/
|
|
3262
|
+
function OpenAPIDisclosureGroup(props) {
|
|
3263
|
+
const { icon, groups, selectStateKey, selectIcon } = props;
|
|
3264
|
+
const state = $9385b3affbdec831$export$f36461af0ef4707d(props);
|
|
3265
|
+
return <DisclosureGroupStateContext.Provider value={state}>
|
|
3266
|
+
{groups.map((group) => <DisclosureItem selectStateKey={selectStateKey} selectIcon={selectIcon} icon={icon} key={group.key} group={group} />)}
|
|
3267
|
+
</DisclosureGroupStateContext.Provider>;
|
|
3268
|
+
}
|
|
3269
|
+
function DisclosureItem(props) {
|
|
3270
|
+
const { icon, group, selectStateKey, selectIcon } = props;
|
|
3271
|
+
const defaultId = useId$1();
|
|
3272
|
+
const id = group.key || defaultId;
|
|
3273
|
+
const groupState = useContext(DisclosureGroupStateContext);
|
|
3274
|
+
const isExpanded = groupState?.expandedKeys.has(id) || false;
|
|
3275
|
+
const state = $bf996d45f4a36925$export$3fcbf6e4407997e0({
|
|
3276
|
+
isExpanded,
|
|
3277
|
+
onExpandedChange() {
|
|
3278
|
+
if (groupState) groupState.toggleKey(id);
|
|
3279
|
+
}
|
|
3280
|
+
});
|
|
3281
|
+
const panelRef = useRef(null);
|
|
3282
|
+
const triggerRef = useRef(null);
|
|
3283
|
+
const isDisabled = groupState?.isDisabled || !group.tabs?.length || false;
|
|
3284
|
+
const { buttonProps: triggerProps, panelProps } = useDisclosure({
|
|
3285
|
+
...props,
|
|
3286
|
+
isExpanded,
|
|
3287
|
+
isDisabled
|
|
3288
|
+
}, state, panelRef);
|
|
3289
|
+
const { buttonProps } = useButton(triggerProps, triggerRef);
|
|
3290
|
+
const { isFocusVisible, focusProps } = useFocusRing();
|
|
3291
|
+
const store = useSelectState(selectStateKey, group.tabs?.[0]?.key || "");
|
|
3292
|
+
const selectedTab = group.tabs?.find((tab) => tab.key === store.key) || group.tabs?.[0];
|
|
3293
|
+
return <div className="openapi-disclosure-group" aria-expanded={state.isExpanded}>
|
|
3294
|
+
<div slot="trigger" ref={triggerRef} {...mergeProps(buttonProps, focusProps)} aria-disabled={isDisabled} style={{ outline: isFocusVisible ? "2px solid rgb(var(--primary-color-500)/0.4)" : "none" }} className="openapi-disclosure-group-trigger">
|
|
3295
|
+
<div className="openapi-disclosure-group-icon">
|
|
3296
|
+
{icon || <svg viewBox="0 0 24 24" className="openapi-disclosure-group-icon">
|
|
3297
|
+
<path d="m8.25 4.5 7.5 7.5-7.5 7.5" />
|
|
3298
|
+
</svg>}
|
|
3299
|
+
</div>
|
|
3300
|
+
|
|
3301
|
+
<div className="openapi-disclosure-group-label">
|
|
3302
|
+
{group.label}
|
|
3303
|
+
|
|
3304
|
+
{group.tabs ? <div className="openapi-disclosure-group-mediatype" onClick={(e) => e.stopPropagation()}>
|
|
3305
|
+
{group.tabs?.length > 1 ? <OpenAPISelect icon={selectIcon} stateKey={selectStateKey} onSelectionChange={() => {
|
|
3306
|
+
state.expand();
|
|
3307
|
+
}} items={group.tabs} placement="bottom end">
|
|
3308
|
+
{group.tabs.map((tab) => <OpenAPISelectItem key={tab.key} id={tab.key} value={tab}>
|
|
3309
|
+
{tab.label}
|
|
3310
|
+
</OpenAPISelectItem>)}
|
|
3311
|
+
</OpenAPISelect> : group.tabs[0]?.label ? <span>{group.tabs[0].label}</span> : null}
|
|
3312
|
+
</div> : null}
|
|
3313
|
+
</div>
|
|
3314
|
+
</div>
|
|
3315
|
+
|
|
3316
|
+
{state.isExpanded && selectedTab && <div className="openapi-disclosure-group-panel" ref={panelRef} {...panelProps}>
|
|
3317
|
+
{selectedTab.body}
|
|
3318
|
+
</div>}
|
|
3319
|
+
</div>;
|
|
3320
|
+
}
|
|
3321
|
+
|
|
3322
|
+
//#endregion
|
|
3323
|
+
//#region src/OpenAPIResponse.tsx
|
|
3324
|
+
/**
|
|
3325
|
+
* Display an interactive response body.
|
|
3326
|
+
*/
|
|
3327
|
+
function OpenAPIResponse(props) {
|
|
3328
|
+
const { response, context, mediaType } = props;
|
|
3329
|
+
const headers = Object.entries(response.headers ?? {}).map(([name, header]) => [name, header ?? {}]);
|
|
3330
|
+
const content = Object.entries(mediaType?.schema ?? {});
|
|
3331
|
+
const description = resolveDescription(response);
|
|
3332
|
+
if (content.length === 0 && !description && headers.length === 0) return null;
|
|
3333
|
+
return <div className="openapi-response-body">
|
|
3334
|
+
{headers.length > 0 ? <OpenAPIDisclosure header={<OpenAPISchemaPresentation context={context} property={{
|
|
3335
|
+
propertyName: tString(context.translation, "headers"),
|
|
3336
|
+
schema: { type: "object" },
|
|
3337
|
+
required: null
|
|
3338
|
+
}} />} icon={context.icons.plus} label={(isExpanded) => tString(context.translation, isExpanded ? "hide" : "show", tString(context.translation, headers.length === 1 ? "header" : "headers"))}>
|
|
3339
|
+
<OpenAPISchemaProperties properties={headers.map(([name, header]) => parameterToProperty({
|
|
3340
|
+
name,
|
|
3341
|
+
...header
|
|
3342
|
+
}))} context={context} />
|
|
3343
|
+
</OpenAPIDisclosure> : null}
|
|
3344
|
+
{mediaType?.schema && <div className="openapi-responsebody">
|
|
3345
|
+
<OpenAPISchemaProperties id={`response-${context.blockKey}`} properties={[{
|
|
3346
|
+
schema: mediaType.schema,
|
|
3347
|
+
propertyName: tString(context.translation, "response"),
|
|
3348
|
+
required: null
|
|
3349
|
+
}]} context={context} />
|
|
3350
|
+
</div>}
|
|
3351
|
+
</div>;
|
|
3352
|
+
}
|
|
3353
|
+
|
|
3354
|
+
//#endregion
|
|
3355
|
+
//#region src/OpenAPIResponses.tsx
|
|
3356
|
+
/**
|
|
3357
|
+
* Display an interactive response body.
|
|
3358
|
+
*/
|
|
3359
|
+
function OpenAPIResponses(props) {
|
|
3360
|
+
const { responses, context } = props;
|
|
3361
|
+
const groups = Object.entries(responses).filter(([_, response]) => response && typeof response === "object").map(([statusCode, response]) => {
|
|
3362
|
+
const tabs = (() => {
|
|
3363
|
+
if ((!response.content || !Object.keys(response.content).length) && response.headers && Object.keys(response.headers).length) return [{
|
|
3364
|
+
key: "default",
|
|
3365
|
+
label: "",
|
|
3366
|
+
body: <OpenAPIResponse response={response} mediaType={{}} context={context} />
|
|
3367
|
+
}];
|
|
3368
|
+
if (!response.content) return [{
|
|
3369
|
+
key: "default",
|
|
3370
|
+
label: "",
|
|
3371
|
+
body: <pre className="openapi-example-empty">
|
|
3372
|
+
<p>{t(context.translation, "no_content")}</p>
|
|
3373
|
+
</pre>
|
|
3374
|
+
}];
|
|
3375
|
+
return Object.entries(response.content ?? {}).map(([contentType, mediaType]) => ({
|
|
3376
|
+
key: contentType,
|
|
3377
|
+
label: contentType,
|
|
3378
|
+
body: <OpenAPIResponse response={response} mediaType={mediaType} context={context} />
|
|
3379
|
+
}));
|
|
3380
|
+
})();
|
|
3381
|
+
const description = resolveDescription(response);
|
|
3382
|
+
return {
|
|
3383
|
+
key: statusCode,
|
|
3384
|
+
label: <div className="openapi-response-tab-content">
|
|
3385
|
+
<span className={clsx("openapi-statuscode", `openapi-statuscode-${getStatusCodeClassName(statusCode)}`)}>
|
|
3386
|
+
{statusCode}
|
|
3387
|
+
</span>
|
|
3388
|
+
{description ? <Markdown source={description} className="openapi-response-description" /> : getStatusCodeDefaultLabel(statusCode, context)}
|
|
3389
|
+
</div>,
|
|
3390
|
+
tabs
|
|
3391
|
+
};
|
|
3392
|
+
});
|
|
3393
|
+
const state = useResponseExamplesState(context.blockKey, groups[0]?.key);
|
|
3394
|
+
return <StaticSection header={t(context.translation, "responses")} className="openapi-responses">
|
|
3395
|
+
<OpenAPIDisclosureGroup icon={context.icons.chevronRight} expandedKeys={state.key ? new Set([state.key]) : /* @__PURE__ */ new Set()} onExpandedChange={(keys) => {
|
|
3396
|
+
const key = keys.values().next().value ?? null;
|
|
3397
|
+
state.setKey(key);
|
|
3398
|
+
}} groups={groups} selectIcon={context.icons.chevronDown} selectStateKey={createStateKey("response-media-types", context.blockKey)} />
|
|
3399
|
+
</StaticSection>;
|
|
3400
|
+
}
|
|
3401
|
+
|
|
3402
|
+
//#endregion
|
|
3403
|
+
//#region src/OpenAPISecurities.tsx
|
|
3404
|
+
/**
|
|
3405
|
+
* Present securities authorization that can be used for this operation.
|
|
3406
|
+
*/
|
|
3407
|
+
function OpenAPISecurities(props) {
|
|
3408
|
+
const { securityRequirement, securities, context } = props;
|
|
3409
|
+
if (!securities || securities.length === 0) return null;
|
|
3410
|
+
const tabsData = extractOperationSecurityInfo({
|
|
3411
|
+
securityRequirement,
|
|
3412
|
+
securities
|
|
3413
|
+
});
|
|
3414
|
+
return <InteractiveSection header={t(context.translation, "authorizations")} stateKey={createStateKey("securities", context.blockKey)} toggeable defaultOpened={false} toggleIcon={context.icons.chevronRight} selectIcon={context.icons.chevronDown} className="openapi-securities" tabs={tabsData.map(({ key, label, schemes }) => ({
|
|
3415
|
+
key,
|
|
3416
|
+
label,
|
|
3417
|
+
body: <div className="openapi-schema">
|
|
3418
|
+
{schemes.map((security, index) => {
|
|
3419
|
+
const description = resolveDescription(security);
|
|
3420
|
+
return <div key={`${key}-${index}`} className="openapi-schema-presentation">
|
|
3421
|
+
{getLabelForType(security, context)}
|
|
3422
|
+
{description ? <Markdown source={description} className="openapi-securities-description" /> : null}
|
|
3423
|
+
{security.scopes?.length ? <OpenAPISchemaScopes scopes={security.scopes} context={context} /> : null}
|
|
3424
|
+
</div>;
|
|
3425
|
+
})}
|
|
3426
|
+
</div>
|
|
3427
|
+
}))} />;
|
|
3428
|
+
}
|
|
3429
|
+
function getLabelForType(security, context) {
|
|
3430
|
+
switch (security.type) {
|
|
3431
|
+
case "apiKey": return <OpenAPISchemaName context={context} propertyName={security.name ?? "apiKey"} type="string" required={security.required} />;
|
|
3432
|
+
case "http":
|
|
3433
|
+
if (security.scheme === "basic") return <OpenAPISchemaName context={context} propertyName="Authorization" type="string" required={security.required} />;
|
|
3434
|
+
if (security.scheme === "bearer") return <>
|
|
3435
|
+
<OpenAPISchemaName context={context} propertyName="Authorization" type="string" required={security.required} />
|
|
3436
|
+
{ /** Show a default description if none is provided */}
|
|
3437
|
+
{!security.description ? <Markdown source={`Bearer authentication header of the form Bearer <token>.`} className="openapi-securities-description" /> : null}
|
|
3438
|
+
</>;
|
|
3439
|
+
return <OpenAPISchemaName context={context} propertyName="HTTP" required={security.required} />;
|
|
3440
|
+
case "oauth2": return <OpenAPISchemaOAuth2Flows context={context} security={security} />;
|
|
3441
|
+
case "openIdConnect": return <OpenAPISchemaName context={context} propertyName="OpenID Connect" required={security.required} />;
|
|
3442
|
+
default: return security.type;
|
|
3443
|
+
}
|
|
3444
|
+
}
|
|
3445
|
+
function OpenAPISchemaOAuth2Flows(props) {
|
|
3446
|
+
const { context, security } = props;
|
|
3447
|
+
const flows = security.flows ? Object.entries(security.flows) : [];
|
|
3448
|
+
return <div className="openapi-securities-oauth-flows">
|
|
3449
|
+
{flows.map(([name, flow], index) => <Fragment key={index}>
|
|
3450
|
+
<OpenAPISchemaOAuth2Item flow={flow} name={name} context={context} security={security} />
|
|
3451
|
+
{index < flows.length - 1 ? <hr /> : null}
|
|
3452
|
+
</Fragment>)}
|
|
3453
|
+
</div>;
|
|
3454
|
+
}
|
|
3455
|
+
function OpenAPISchemaOAuth2Item(props) {
|
|
3456
|
+
const { flow, context, security, name } = props;
|
|
3457
|
+
if (!flow) return null;
|
|
3458
|
+
const scopes = flow.scopes ? Object.entries(flow.scopes) : [];
|
|
3459
|
+
return <div>
|
|
3460
|
+
<OpenAPISchemaName context={context} propertyName="OAuth2" type={name} required={security.required} />
|
|
3461
|
+
<div className="openapi-securities-oauth-content openapi-markdown">
|
|
3462
|
+
{security.description ? <Markdown source={security.description} /> : null}
|
|
3463
|
+
{"authorizationUrl" in flow && flow.authorizationUrl ? <span>
|
|
3464
|
+
Authorization URL:{" "}
|
|
3465
|
+
<OpenAPICopyButton value={flow.authorizationUrl} context={context} className="openapi-securities-url" withTooltip>
|
|
3466
|
+
{flow.authorizationUrl}
|
|
3467
|
+
</OpenAPICopyButton>
|
|
3468
|
+
</span> : null}
|
|
3469
|
+
{"tokenUrl" in flow && flow.tokenUrl ? <span>
|
|
3470
|
+
Token URL:{" "}
|
|
3471
|
+
<OpenAPICopyButton value={flow.tokenUrl} context={context} className="openapi-securities-url" withTooltip>
|
|
3472
|
+
{flow.tokenUrl}
|
|
3473
|
+
</OpenAPICopyButton>
|
|
3474
|
+
</span> : null}
|
|
3475
|
+
{"refreshUrl" in flow && flow.refreshUrl ? <span>
|
|
3476
|
+
Refresh URL:{" "}
|
|
3477
|
+
<OpenAPICopyButton value={flow.refreshUrl} context={context} className="openapi-securities-url" withTooltip>
|
|
3478
|
+
{flow.refreshUrl}
|
|
3479
|
+
</OpenAPICopyButton>
|
|
3480
|
+
</span> : null}
|
|
3481
|
+
{scopes.length ? <OpenAPISchemaScopes scopes={scopes} context={context} /> : null}
|
|
3482
|
+
</div>
|
|
3483
|
+
</div>;
|
|
3484
|
+
}
|
|
3485
|
+
/**
|
|
3486
|
+
* Render a list of available scopes.
|
|
3487
|
+
*/
|
|
3488
|
+
function OpenAPISchemaScopes(props) {
|
|
3489
|
+
const { scopes, context } = props;
|
|
3490
|
+
return <div className="openapi-securities-scopes openapi-markdown">
|
|
3491
|
+
<span>{t(context.translation, "required_scopes")}: </span>
|
|
3492
|
+
<ul>
|
|
3493
|
+
{scopes.map((scope) => <OpenAPIScopeItem key={scope[0]} scope={scope} context={context} />)}
|
|
3494
|
+
</ul>
|
|
3495
|
+
</div>;
|
|
3496
|
+
}
|
|
3497
|
+
/**
|
|
3498
|
+
* Display a scope item. Either a key-value pair or a single string.
|
|
3499
|
+
*/
|
|
3500
|
+
function OpenAPIScopeItem(props) {
|
|
3501
|
+
const { scope, context } = props;
|
|
3502
|
+
return <li>
|
|
3503
|
+
<OpenAPIScopeItemKey name={scope[0]} context={context} />
|
|
3504
|
+
{scope[1] ? `: ${scope[1]}` : null}
|
|
3505
|
+
</li>;
|
|
3506
|
+
}
|
|
3507
|
+
/**
|
|
3508
|
+
* Displays the scope name within a copyable button.
|
|
3509
|
+
*/
|
|
3510
|
+
function OpenAPIScopeItemKey(props) {
|
|
3511
|
+
const { name, context } = props;
|
|
3512
|
+
return <OpenAPICopyButton value={name} context={context} withTooltip>
|
|
3513
|
+
<code>{name}</code>
|
|
3514
|
+
</OpenAPICopyButton>;
|
|
3515
|
+
}
|
|
3516
|
+
|
|
3517
|
+
//#endregion
|
|
3518
|
+
//#region src/OpenAPISpec.tsx
|
|
3519
|
+
function OpenAPISpec(props) {
|
|
3520
|
+
const { data, context } = props;
|
|
3521
|
+
const { operation } = data;
|
|
3522
|
+
const parameterGroups = groupParameters(deduplicateParameters(operation.parameters ?? []), context);
|
|
3523
|
+
const securities = "securities" in data ? data.securities : [];
|
|
3524
|
+
return <>
|
|
3525
|
+
{securities.length > 0 ? <OpenAPISecurities key="securities" securityRequirement={operation.security} securities={securities} context={context} /> : null}
|
|
3526
|
+
|
|
3527
|
+
{parameterGroups.map((group) => {
|
|
3528
|
+
return <StaticSection key={`parameter-${group.key}`} className="openapi-parameters" header={group.label}>
|
|
3529
|
+
<OpenAPISchemaProperties properties={group.parameters.map(parameterToProperty)} context={context} />
|
|
3530
|
+
</StaticSection>;
|
|
3531
|
+
})}
|
|
3532
|
+
|
|
3533
|
+
{operation.requestBody ? <OpenAPIRequestBody key="body" requestBody={operation.requestBody} context={context} data={data} /> : null}
|
|
3534
|
+
{operation.responses ? <OpenAPIResponses key="responses" responses={operation.responses} context={context} /> : null}
|
|
3535
|
+
</>;
|
|
3536
|
+
}
|
|
3537
|
+
function groupParameters(parameters, context) {
|
|
3538
|
+
const sorted = [
|
|
3539
|
+
"path",
|
|
3540
|
+
"query",
|
|
3541
|
+
"header"
|
|
3542
|
+
];
|
|
3543
|
+
const groups = [];
|
|
3544
|
+
parameters.filter((parameter) => parameter.in).forEach((parameter) => {
|
|
3545
|
+
const key = parameter.in;
|
|
3546
|
+
const label = getParameterGroupName(parameter.in, context);
|
|
3547
|
+
const group = groups.find((group$1) => group$1.key === key);
|
|
3548
|
+
if (group) group.parameters.push(parameter);
|
|
3549
|
+
else groups.push({
|
|
3550
|
+
key,
|
|
3551
|
+
label,
|
|
3552
|
+
parameters: [parameter]
|
|
3553
|
+
});
|
|
3554
|
+
});
|
|
3555
|
+
groups.sort((a, b) => sorted.indexOf(a.key) - sorted.indexOf(b.key));
|
|
3556
|
+
return groups;
|
|
3557
|
+
}
|
|
3558
|
+
function getParameterGroupName(paramIn, context) {
|
|
3559
|
+
switch (paramIn) {
|
|
3560
|
+
case "path": return tString(context.translation, "path_parameters");
|
|
3561
|
+
case "query": return tString(context.translation, "query_parameters");
|
|
3562
|
+
case "header": return tString(context.translation, "header_parameters");
|
|
3563
|
+
default: return paramIn;
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
/** Deduplicate parameters by name and in.
|
|
3567
|
+
* Some specs have both parameters define at path and operation level.
|
|
3568
|
+
* We only want to display one of them.
|
|
3569
|
+
* Parameters can have the wrong type (object instead of array) sometimes, we just return an empty array in that case.
|
|
3570
|
+
*/
|
|
3571
|
+
function deduplicateParameters(parameters) {
|
|
3572
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3573
|
+
return Array.isArray(parameters) ? parameters.filter((param) => {
|
|
3574
|
+
const key = `${param.name}:${param.in}`;
|
|
3575
|
+
if (seen.has(key)) return false;
|
|
3576
|
+
seen.add(key);
|
|
3577
|
+
return true;
|
|
3578
|
+
}) : [];
|
|
3579
|
+
}
|
|
3580
|
+
|
|
3581
|
+
//#endregion
|
|
3582
|
+
//#region src/common/OpenAPIOperationDescription.tsx
|
|
3583
|
+
function OpenAPIOperationDescription(props) {
|
|
3584
|
+
const { operation } = props;
|
|
3585
|
+
if (operation["x-gitbook-description-document"]) return <div className="openapi-intro">
|
|
3586
|
+
{props.context.renderDocument({ document: operation["x-gitbook-description-document"] })}
|
|
3587
|
+
</div>;
|
|
3588
|
+
const description = resolveDescription(operation);
|
|
3589
|
+
if (!description) return null;
|
|
3590
|
+
return <div className="openapi-intro">
|
|
3591
|
+
<Markdown className="openapi-description" source={description} />
|
|
3592
|
+
</div>;
|
|
3593
|
+
}
|
|
3594
|
+
|
|
3595
|
+
//#endregion
|
|
3596
|
+
//#region src/common/OpenAPIColumnSpec.tsx
|
|
3597
|
+
function OpenAPIColumnSpec(props) {
|
|
3598
|
+
const { data, context } = props;
|
|
3599
|
+
const { operation } = data;
|
|
3600
|
+
const clientContext = getOpenAPIClientContext(context);
|
|
3601
|
+
return <div className="openapi-column-spec">
|
|
3602
|
+
{operation["x-deprecated-sunset"] ? <div className="openapi-deprecated-sunset openapi-description openapi-markdown">
|
|
3603
|
+
{t(context.translation, "deprecated_and_sunset_on", [<span key="date" className="openapi-deprecated-sunset-date">
|
|
3604
|
+
{operation["x-deprecated-sunset"]}
|
|
3605
|
+
</span>])}
|
|
3606
|
+
</div> : null}
|
|
3607
|
+
<OpenAPIOperationDescription operation={operation} context={context} />
|
|
3608
|
+
<OpenAPISpec data={data} context={clientContext} />
|
|
3609
|
+
</div>;
|
|
3610
|
+
}
|
|
3611
|
+
|
|
3612
|
+
//#endregion
|
|
3613
|
+
//#region src/common/OpenAPIStability.tsx
|
|
3614
|
+
const stabilityEnum = {
|
|
3615
|
+
experimental: "Experimental",
|
|
3616
|
+
alpha: "Alpha",
|
|
3617
|
+
beta: "Beta"
|
|
3618
|
+
};
|
|
3619
|
+
function OpenAPIStability(props) {
|
|
3620
|
+
const { stability } = props;
|
|
3621
|
+
const foundStability = stabilityEnum[stability];
|
|
3622
|
+
if (!foundStability) return null;
|
|
3623
|
+
return <div className={`openapi-stability openapi-stability-${foundStability.toLowerCase()}`}>
|
|
3624
|
+
{foundStability}
|
|
3625
|
+
</div>;
|
|
3626
|
+
}
|
|
3627
|
+
|
|
3628
|
+
//#endregion
|
|
3629
|
+
//#region src/common/OpenAPISummary.tsx
|
|
3630
|
+
function OpenAPISummary(props) {
|
|
3631
|
+
const { data, context } = props;
|
|
3632
|
+
const { operation } = data;
|
|
3633
|
+
const title = (() => {
|
|
3634
|
+
if (operation.summary) return operation.summary;
|
|
3635
|
+
if ("name" in data) return data.name;
|
|
3636
|
+
})();
|
|
3637
|
+
return <div className="openapi-summary" id={operation.summary ? void 0 : context.id}>
|
|
3638
|
+
{(operation.deprecated || operation["x-stability"]) && <div className="openapi-summary-tags">
|
|
3639
|
+
{operation.deprecated && <div className="openapi-deprecated">Deprecated</div>}
|
|
3640
|
+
{operation["x-stability"] && <OpenAPIStability stability={operation["x-stability"]} />}
|
|
3641
|
+
</div>}
|
|
3642
|
+
{title ? context.renderHeading({
|
|
3643
|
+
deprecated: operation.deprecated ?? false,
|
|
3644
|
+
stability: operation["x-stability"],
|
|
3645
|
+
title
|
|
3646
|
+
}) : null}
|
|
3647
|
+
{"path" in data ? <OpenAPIPath data={data} context={context} /> : null}
|
|
3648
|
+
</div>;
|
|
3649
|
+
}
|
|
3650
|
+
|
|
3651
|
+
//#endregion
|
|
3652
|
+
//#region src/OpenAPIOperation.tsx
|
|
3653
|
+
/**
|
|
3654
|
+
* Display an interactive OpenAPI operation.
|
|
3655
|
+
*/
|
|
3656
|
+
function OpenAPIOperation(props) {
|
|
3657
|
+
const { className, data, context: contextInput } = props;
|
|
3658
|
+
const context = resolveOpenAPIContext(contextInput);
|
|
3659
|
+
return <div className={clsx("openapi-operation", className)}>
|
|
3660
|
+
<OpenAPISummary data={data} context={context} />
|
|
3661
|
+
<div className="openapi-columns">
|
|
3662
|
+
<OpenAPIColumnSpec data={data} context={context} />
|
|
3663
|
+
<div className="openapi-column-preview">
|
|
3664
|
+
<div className="openapi-column-preview-body">
|
|
3665
|
+
<OpenAPICodeSample data={data} context={context} />
|
|
3666
|
+
<OpenAPIResponseExample data={data} context={context} />
|
|
3667
|
+
</div>
|
|
3668
|
+
</div>
|
|
3669
|
+
</div>
|
|
3670
|
+
</div>;
|
|
3671
|
+
}
|
|
3672
|
+
|
|
3673
|
+
//#endregion
|
|
3674
|
+
//#region src/OpenAPIWebhookExample.tsx
|
|
3675
|
+
function OpenAPIWebhookExample(props) {
|
|
3676
|
+
const { data, context } = props;
|
|
3677
|
+
const { operation } = data;
|
|
3678
|
+
const items = (() => {
|
|
3679
|
+
if (!operation.requestBody) return [];
|
|
3680
|
+
return Object.entries(operation.requestBody.content).map(([key, value]) => {
|
|
3681
|
+
if (!value?.schema) return {
|
|
3682
|
+
key,
|
|
3683
|
+
label: key,
|
|
3684
|
+
body: <OpenAPIEmptyExample context={context} />
|
|
3685
|
+
};
|
|
3686
|
+
return {
|
|
3687
|
+
key,
|
|
3688
|
+
label: key,
|
|
3689
|
+
body: <></>,
|
|
3690
|
+
examples: getExamples({
|
|
3691
|
+
mediaTypeObject: value,
|
|
3692
|
+
mediaType: key,
|
|
3693
|
+
context
|
|
3694
|
+
})
|
|
3695
|
+
};
|
|
3696
|
+
});
|
|
3697
|
+
})();
|
|
3698
|
+
return <div className="openapi-panel">
|
|
3699
|
+
<h4 className="openapi-panel-heading">Payload</h4>
|
|
3700
|
+
<div className="openapi-panel-body">
|
|
3701
|
+
<OpenAPIMediaTypeContent selectIcon={context.icons.chevronDown} stateKey={createStateKey("request-body-media-type", context.blockKey)} items={items} context={getOpenAPIClientContext(context)} />
|
|
3702
|
+
</div>
|
|
3703
|
+
</div>;
|
|
3704
|
+
}
|
|
3705
|
+
|
|
3706
|
+
//#endregion
|
|
3707
|
+
//#region src/OpenAPIWebhook.tsx
|
|
3708
|
+
/**
|
|
3709
|
+
* Display an interactive OpenAPI webhook.
|
|
3710
|
+
*/
|
|
3711
|
+
function OpenAPIWebhook(props) {
|
|
3712
|
+
const { className, data, context: contextInput } = props;
|
|
3713
|
+
const context = resolveOpenAPIContext(contextInput);
|
|
3714
|
+
return <div className={clsx("openapi-webhook", className)}>
|
|
3715
|
+
<OpenAPISummary data={data} context={context} />
|
|
3716
|
+
<div className="openapi-columns">
|
|
3717
|
+
<OpenAPIColumnSpec data={data} context={context} />
|
|
3718
|
+
<div className="openapi-column-preview">
|
|
3719
|
+
<div className="openapi-column-preview-body">
|
|
3720
|
+
<OpenAPIWebhookExample data={data} context={context} />
|
|
3721
|
+
</div>
|
|
3722
|
+
</div>
|
|
3723
|
+
</div>
|
|
3724
|
+
</div>;
|
|
3725
|
+
}
|
|
3726
|
+
|
|
3727
|
+
//#endregion
|
|
3728
|
+
//#region src/resolveOpenAPIOperation.ts
|
|
3729
|
+
/**
|
|
3730
|
+
* Resolve an OpenAPI operation in a file and compile it to a more usable format.
|
|
3731
|
+
*/
|
|
3732
|
+
async function resolveOpenAPIOperation(filesystem, operationDescriptor) {
|
|
3733
|
+
const { path, method } = operationDescriptor;
|
|
3734
|
+
const schema = await dereferenceFilesystem(filesystem);
|
|
3735
|
+
let operation = getOperationByPathAndMethod(schema, path, method);
|
|
3736
|
+
if (!operation) return null;
|
|
3737
|
+
const commonParameters = getPathObjectParameter$1(schema, path);
|
|
3738
|
+
if (commonParameters) operation = {
|
|
3739
|
+
...operation,
|
|
3740
|
+
parameters: [...commonParameters, ...operation.parameters ?? []]
|
|
3741
|
+
};
|
|
3742
|
+
const servers = "servers" in schema ? schema.servers ?? [] : [];
|
|
3743
|
+
const schemaSecurity = Array.isArray(schema.security) ? schema.security : schema.security ? [schema.security] : [];
|
|
3744
|
+
const security = operation.security ?? schemaSecurity;
|
|
3745
|
+
const isOptionalSecurity = security.some((entry) => Object.keys(entry).length === 0);
|
|
3746
|
+
const flatSecurities = flattenSecurities(security);
|
|
3747
|
+
const securities = [];
|
|
3748
|
+
for (const entry of flatSecurities) {
|
|
3749
|
+
const [securityKey, operationScopes] = Object.entries(entry)[0] ?? [];
|
|
3750
|
+
if (securityKey) {
|
|
3751
|
+
const securityScheme = schema.components?.securitySchemes?.[securityKey];
|
|
3752
|
+
const scopes = resolveSecurityScopes({
|
|
3753
|
+
securityScheme,
|
|
3754
|
+
operationScopes
|
|
3755
|
+
});
|
|
3756
|
+
securities.push([securityKey, {
|
|
3757
|
+
...securityScheme,
|
|
3758
|
+
required: !isOptionalSecurity,
|
|
3759
|
+
scopes
|
|
3760
|
+
}]);
|
|
3761
|
+
}
|
|
3762
|
+
}
|
|
3763
|
+
return {
|
|
3764
|
+
servers,
|
|
3765
|
+
operation: {
|
|
3766
|
+
...operation,
|
|
3767
|
+
security
|
|
3768
|
+
},
|
|
3769
|
+
method,
|
|
3770
|
+
path,
|
|
3771
|
+
securities,
|
|
3772
|
+
"x-codeSamples": typeof schema["x-codeSamples"] === "boolean" ? schema["x-codeSamples"] : void 0,
|
|
3773
|
+
"x-hideTryItPanel": typeof schema["x-hideTryItPanel"] === "boolean" ? schema["x-hideTryItPanel"] : void 0
|
|
3774
|
+
};
|
|
3775
|
+
}
|
|
3776
|
+
/**
|
|
3777
|
+
* Get a path object from its path.
|
|
3778
|
+
*/
|
|
3779
|
+
function getPathObject$1(schema, path) {
|
|
3780
|
+
return schema.paths?.[path] || null;
|
|
3781
|
+
}
|
|
3782
|
+
/**
|
|
3783
|
+
* Resolve parameters from a path in an OpenAPI schema.
|
|
3784
|
+
*/
|
|
3785
|
+
function getPathObjectParameter$1(schema, path) {
|
|
3786
|
+
const pathObject = getPathObject$1(schema, path);
|
|
3787
|
+
if (pathObject?.parameters) return pathObject.parameters;
|
|
3788
|
+
return null;
|
|
3789
|
+
}
|
|
3790
|
+
/**
|
|
3791
|
+
* Get an operation by its path and method.
|
|
3792
|
+
*/
|
|
3793
|
+
function getOperationByPathAndMethod(schema, path, method) {
|
|
3794
|
+
const pathObject = getPathObject$1(schema, path);
|
|
3795
|
+
if (!pathObject) return null;
|
|
3796
|
+
const normalizedMethod = method.toLowerCase();
|
|
3797
|
+
if (!pathObject[normalizedMethod]) return null;
|
|
3798
|
+
return pathObject[normalizedMethod];
|
|
3799
|
+
}
|
|
3800
|
+
/**
|
|
3801
|
+
* Flatten security objects in case they are nested.
|
|
3802
|
+
* @example [{bearerAuth:[], basicAuth:[]}] => [{ bearerAuth: [] }, { basicAuth: [] }]
|
|
3803
|
+
*/
|
|
3804
|
+
function flattenSecurities(security) {
|
|
3805
|
+
if (!Array.isArray(security) || security.length === 0) return [];
|
|
3806
|
+
return security.flatMap((securityObject) => {
|
|
3807
|
+
return Object.entries(securityObject).map(([authType, config]) => ({ [authType]: config }));
|
|
3808
|
+
});
|
|
3809
|
+
}
|
|
3810
|
+
/**
|
|
3811
|
+
* Resolve the scopes for a security scheme.
|
|
3812
|
+
*/
|
|
3813
|
+
function resolveSecurityScopes({ securityScheme, operationScopes }) {
|
|
3814
|
+
if (!securityScheme || checkIsReference(securityScheme) || isOAuthSecurityScheme(securityScheme)) return null;
|
|
3815
|
+
return operationScopes?.map((scope) => [scope, void 0]) || [];
|
|
3816
|
+
}
|
|
3817
|
+
/**
|
|
3818
|
+
* Check if a security scheme is an OAuth or OpenID Connect security scheme.
|
|
3819
|
+
*/
|
|
3820
|
+
function isOAuthSecurityScheme(securityScheme) {
|
|
3821
|
+
return securityScheme.type === "oauth2";
|
|
3822
|
+
}
|
|
3823
|
+
|
|
3824
|
+
//#endregion
|
|
3825
|
+
//#region src/resolveOpenAPIWebhook.ts
|
|
3826
|
+
/**
|
|
3827
|
+
* Resolve an OpenAPI webhook in a file and compile it to a more usable format.
|
|
3828
|
+
*/
|
|
3829
|
+
async function resolveOpenAPIWebhook(filesystem, webhookDescriptor) {
|
|
3830
|
+
const { name, method } = webhookDescriptor;
|
|
3831
|
+
const schema = await dereferenceFilesystem(filesystem);
|
|
3832
|
+
let operation = getWebhookByNameAndMethod(schema, name, method);
|
|
3833
|
+
if (!operation) return null;
|
|
3834
|
+
const commonParameters = getPathObjectParameter(schema, name);
|
|
3835
|
+
if (commonParameters) operation = {
|
|
3836
|
+
...operation,
|
|
3837
|
+
parameters: [...commonParameters, ...operation.parameters ?? []]
|
|
3838
|
+
};
|
|
3839
|
+
return {
|
|
3840
|
+
servers: "servers" in schema ? schema.servers ?? [] : [],
|
|
3841
|
+
operation,
|
|
3842
|
+
method,
|
|
3843
|
+
name
|
|
3844
|
+
};
|
|
3845
|
+
}
|
|
3846
|
+
/**
|
|
3847
|
+
* Get a path object from its path.
|
|
3848
|
+
*/
|
|
3849
|
+
function getPathObject(schema, name) {
|
|
3850
|
+
if (schema.webhooks?.[name]) return schema.webhooks[name];
|
|
3851
|
+
return null;
|
|
3852
|
+
}
|
|
3853
|
+
/**
|
|
3854
|
+
* Resolve parameters from a path in an OpenAPI schema.
|
|
3855
|
+
*/
|
|
3856
|
+
function getPathObjectParameter(schema, path) {
|
|
3857
|
+
const pathObject = getPathObject(schema, path);
|
|
3858
|
+
if (pathObject?.parameters) return pathObject.parameters;
|
|
3859
|
+
return null;
|
|
3860
|
+
}
|
|
3861
|
+
/**
|
|
3862
|
+
* Get an operation by its path and method.
|
|
3863
|
+
*/
|
|
3864
|
+
function getWebhookByNameAndMethod(schema, name, method) {
|
|
3865
|
+
const pathObject = getPathObject(schema, name);
|
|
3866
|
+
if (!pathObject) return null;
|
|
3867
|
+
const normalizedMethod = method.toLowerCase();
|
|
3868
|
+
if (!pathObject[normalizedMethod]) return null;
|
|
3869
|
+
return pathObject[normalizedMethod];
|
|
3870
|
+
}
|
|
3871
|
+
|
|
3872
|
+
//#endregion
|
|
3873
|
+
export { OpenAPIOperation, OpenAPIOperationContextProvider, OpenAPIPrefillContextProvider, OpenAPISchemas, OpenAPIWebhook, checkIsValidLocale, resolveOpenAPIOperation, resolveOpenAPISchemas, resolveOpenAPIWebhook, useOpenAPIOperationContext, useOpenAPIPrefillContext };
|