fumadocs-openapi 10.6.5 → 10.6.7
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/css/generated/shared.css +59 -6
- package/dist/generate-file.js +3 -2
- package/dist/i18n.d.ts +3 -1
- package/dist/i18n.js +3 -1
- package/dist/playground/auth.js +92 -0
- package/dist/playground/client.d.ts +5 -12
- package/dist/playground/client.js +257 -217
- package/dist/playground/components/inputs.js +2 -2
- package/dist/playground/components/oauth-dialog.js +126 -163
- package/dist/playground/components/result-display.d.ts +16 -0
- package/dist/playground/components/result-display.js +141 -0
- package/dist/playground/components/server-select.js +1 -1
- package/dist/playground/components/spinner.js +14 -0
- package/dist/playground/fetcher.d.ts +9 -3
- package/dist/playground/fetcher.js +7 -18
- package/dist/playground/status-info.js +27 -23
- package/dist/requests/generators/csharp.js +2 -1
- package/dist/requests/generators/curl.js +5 -5
- package/dist/requests/generators/go.js +5 -5
- package/dist/requests/generators/java.js +4 -4
- package/dist/requests/generators/javascript.js +3 -3
- package/dist/requests/generators/python.js +5 -4
- package/dist/requests/media/adapter.js +7 -7
- package/dist/requests/string-utils.js +25 -4
- package/dist/requests/types.d.ts +3 -2
- package/dist/ui/api-page.js +3 -1
- package/dist/ui/base.d.ts +8 -4
- package/dist/ui/base.js +6 -1
- package/dist/ui/client/boundary.lazy.js +2 -1
- package/dist/ui/client/i18n.js +6 -4
- package/dist/ui/client/storage-key.js +1 -1
- package/dist/ui/components/dialog.js +1 -1
- package/dist/ui/contexts/api.d.ts +6 -3
- package/dist/ui/contexts/api.js +7 -2
- package/dist/ui/create-client.js +5 -0
- package/dist/ui/operation/client.js +1 -1
- package/dist/ui/operation/index.js +1 -1
- package/dist/ui/operation/request-tabs.d.ts +10 -0
- package/dist/ui/operation/request-tabs.js +44 -38
- package/dist/ui/operation/usage-tabs/client.js +1 -1
- package/dist/ui/schema/client.js +2 -2
- package/dist/ui/schema/index.js +1 -1
- package/dist/utils/pages/to-text.js +5 -4
- package/dist/utils/schema/index.d.ts +3 -4
- package/dist/utils/schema/index.js +4 -9
- package/dist/utils/use-query.js +2 -1
- package/package.json +11 -10
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { doubleQuote, indent, inputToString, singleQuote } from "../string-utils.js";
|
|
2
2
|
//#region src/requests/generators/curl.ts
|
|
3
3
|
const curl = {
|
|
4
4
|
label: "cURL",
|
|
5
5
|
lang: "bash",
|
|
6
6
|
generate(url, data) {
|
|
7
7
|
const s = [];
|
|
8
|
-
s.push(`curl -X ${data.method} "${url}"`);
|
|
8
|
+
s.push(`curl -X ${data.method.toUpperCase()} "${url}"`);
|
|
9
9
|
for (const header in data.header) {
|
|
10
10
|
const value = `${header}: ${data.header[header].value}`;
|
|
11
11
|
s.push(`-H "${value}"`);
|
|
12
12
|
}
|
|
13
13
|
for (const k in data.cookie) {
|
|
14
14
|
const param = data.cookie[k];
|
|
15
|
-
s.push(`--cookie ${
|
|
15
|
+
s.push(`--cookie ${doubleQuote(`${k}=${param.value}`)}`);
|
|
16
16
|
}
|
|
17
17
|
if (data.body && data.bodyMediaType === "multipart/form-data") {
|
|
18
18
|
if (typeof data.body !== "object") throw new Error("[CURL] request body must be an object.");
|
|
19
|
-
for (const [key, value] of Object.entries(data.body)) s.push(`-F ${key}=${
|
|
19
|
+
for (const [key, value] of Object.entries(data.body)) s.push(`-F ${key}=${doubleQuote(inputToString(value))}`);
|
|
20
20
|
} else if (data.body && data.bodyMediaType) {
|
|
21
|
-
const escaped =
|
|
21
|
+
const escaped = singleQuote(inputToString(data.body, data.bodyMediaType));
|
|
22
22
|
s.push(`-H "Content-Type: ${data.bodyMediaType}"`);
|
|
23
23
|
s.push(`-d ${escaped}`);
|
|
24
24
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { indent } from "../string-utils.js";
|
|
1
|
+
import { doubleQuote, indent } from "../string-utils.js";
|
|
2
2
|
import { resolveMediaAdapter } from "../media/resolve-adapter.js";
|
|
3
3
|
import "../media/adapter.js";
|
|
4
4
|
//#region src/requests/generators/go.ts
|
|
@@ -13,10 +13,10 @@ const go = {
|
|
|
13
13
|
];
|
|
14
14
|
const headers = /* @__PURE__ */ new Map();
|
|
15
15
|
const variables = /* @__PURE__ */ new Map();
|
|
16
|
-
variables.set("url",
|
|
17
|
-
for (const header in data.header) headers.set(header,
|
|
16
|
+
variables.set("url", doubleQuote(url));
|
|
17
|
+
for (const header in data.header) headers.set(header, doubleQuote(data.header[header].value));
|
|
18
18
|
const cookies = Object.entries(data.cookie);
|
|
19
|
-
if (cookies.length > 0) headers.set("Cookie",
|
|
19
|
+
if (cookies.length > 0) headers.set("Cookie", doubleQuote(cookies.map(([k, param]) => `${k}=${param.value}`).join("; ")));
|
|
20
20
|
let body;
|
|
21
21
|
if (data.body && data.bodyMediaType) {
|
|
22
22
|
const adapter = resolveMediaAdapter(data.bodyMediaType, mediaAdapters);
|
|
@@ -37,7 +37,7 @@ ${indent(imports.map((v) => `"${v}"`).join("\n"))}
|
|
|
37
37
|
func main() {
|
|
38
38
|
${Array.from(variables.entries()).map(([k, v]) => indent(`${k} := ${v}`)).join("\n")}
|
|
39
39
|
${body ? indent(body) : ""}
|
|
40
|
-
req, _ := http.NewRequest("${data.method}", url, ${body ? "body" : "nil"})
|
|
40
|
+
req, _ := http.NewRequest("${data.method.toUpperCase()}", url, ${body ? "body" : "nil"})
|
|
41
41
|
${indent(Array.from(headers.entries()).map(([key, value]) => `req.Header.Add("${key}", ${value})`).join("\n"))}
|
|
42
42
|
res, _ := http.DefaultClient.Do(req)
|
|
43
43
|
defer res.Body.Close()
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { indent } from "../string-utils.js";
|
|
1
|
+
import { doubleQuote, indent } from "../string-utils.js";
|
|
2
2
|
import { resolveMediaAdapter } from "../media/resolve-adapter.js";
|
|
3
3
|
import "../media/adapter.js";
|
|
4
4
|
//#region src/requests/generators/java.ts
|
|
@@ -31,13 +31,13 @@ const java = {
|
|
|
31
31
|
s.push(indent(".build();"));
|
|
32
32
|
s.push("");
|
|
33
33
|
s.push("HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()");
|
|
34
|
-
s.push(indent(`.uri(URI.create(${
|
|
35
|
-
for (const [key, param] of Object.entries(headers)) s.push(indent(`.header(${
|
|
34
|
+
s.push(indent(`.uri(URI.create(${doubleQuote(url)}))`));
|
|
35
|
+
for (const [key, param] of Object.entries(headers)) s.push(indent(`.header(${doubleQuote(key)}, ${doubleQuote(param.value)})`));
|
|
36
36
|
if (data.bodyMediaType) s.push(indent(`.header("Content-Type", "${data.bodyMediaType}")`));
|
|
37
37
|
const cookies = Object.entries(data.cookie);
|
|
38
38
|
if (cookies.length > 0) {
|
|
39
39
|
const cookieString = cookies.map(([key, param]) => `${key}=${param.value}`).join("; ");
|
|
40
|
-
s.push(indent(`.header("Cookie", ${
|
|
40
|
+
s.push(indent(`.header("Cookie", ${doubleQuote(cookieString)})`));
|
|
41
41
|
}
|
|
42
42
|
const arg = body ? "body" : "";
|
|
43
43
|
s.push(indent(`.${data.method.toUpperCase()}(${arg})`));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { indent } from "../string-utils.js";
|
|
1
|
+
import { doubleQuote, indent } from "../string-utils.js";
|
|
2
2
|
import { resolveMediaAdapter } from "../media/resolve-adapter.js";
|
|
3
3
|
import "../media/adapter.js";
|
|
4
4
|
//#region src/requests/generators/javascript.ts
|
|
@@ -9,7 +9,7 @@ const javascript = {
|
|
|
9
9
|
const s = [];
|
|
10
10
|
const options = /* @__PURE__ */ new Map();
|
|
11
11
|
const headers = {};
|
|
12
|
-
options.set("method",
|
|
12
|
+
options.set("method", `"${data.method.toUpperCase()}"`);
|
|
13
13
|
if (data.bodyMediaType) headers["Content-Type"] = data.bodyMediaType;
|
|
14
14
|
for (const [k, v] of Object.entries(data.header)) headers[k] = v.value;
|
|
15
15
|
const cookies = Object.entries(data.cookie);
|
|
@@ -26,7 +26,7 @@ const javascript = {
|
|
|
26
26
|
s.push(body);
|
|
27
27
|
options.set("body", "body");
|
|
28
28
|
}
|
|
29
|
-
const params = [
|
|
29
|
+
const params = [doubleQuote(url)];
|
|
30
30
|
if (options.size > 0) {
|
|
31
31
|
const str = Array.from(options.entries()).map(([k, v]) => indent(k === v ? k : `${k}: ${v}`)).join(",\n");
|
|
32
32
|
params.push(`{\n${str}\n}`);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { doubleQuote } from "../string-utils.js";
|
|
1
2
|
import { resolveMediaAdapter } from "../media/resolve-adapter.js";
|
|
2
3
|
import "../media/adapter.js";
|
|
3
4
|
//#region src/requests/generators/python.ts
|
|
@@ -7,7 +8,7 @@ const python = {
|
|
|
7
8
|
generate(url, data, { mediaAdapters }) {
|
|
8
9
|
const headers = {};
|
|
9
10
|
const imports = /* @__PURE__ */ new Set();
|
|
10
|
-
const params = [`"${data.method}"`, "url"];
|
|
11
|
+
const params = [`"${data.method.toUpperCase()}"`, "url"];
|
|
11
12
|
let body;
|
|
12
13
|
imports.add("requests");
|
|
13
14
|
if (data.body && data.bodyMediaType) {
|
|
@@ -26,7 +27,7 @@ const python = {
|
|
|
26
27
|
}
|
|
27
28
|
return `${Array.from(imports).map((name) => "import " + name).join("\n")}
|
|
28
29
|
|
|
29
|
-
url = ${
|
|
30
|
+
url = ${doubleQuote(url)}
|
|
30
31
|
${body ?? ""}
|
|
31
32
|
response = requests.request(${params.join(", ")})
|
|
32
33
|
|
|
@@ -36,13 +37,13 @@ print(response.text)`;
|
|
|
36
37
|
function generatePythonObject(v, imports) {
|
|
37
38
|
if (v === null) return "None";
|
|
38
39
|
else if (typeof v === "boolean") return v ? "True" : "False";
|
|
39
|
-
else if (typeof v === "string") return
|
|
40
|
+
else if (typeof v === "string") return doubleQuote(v);
|
|
40
41
|
else if (typeof v === "number") return v.toString();
|
|
41
42
|
else if (Array.isArray(v)) return `[${v.map((item) => generatePythonObject(item, imports)).join(", ")}]`;
|
|
42
43
|
else if (v instanceof Date) {
|
|
43
44
|
imports.add("datetime");
|
|
44
45
|
return `datetime.datetime(${v.getFullYear()}, ${v.getMonth() + 1}, ${v.getDate()}, ${v.getHours()}, ${v.getMinutes()}, ${v.getSeconds()}, ${v.getMilliseconds()})`;
|
|
45
|
-
} else if (typeof v === "object") return `{\n${Object.entries(v).map(([key, value]) => ` ${
|
|
46
|
+
} else if (typeof v === "object") return `{\n${Object.entries(v).map(([key, value]) => ` ${doubleQuote(key)}: ${generatePythonObject(value, imports)}`).join(", \n")}\n}`;
|
|
46
47
|
else throw new Error(`Unsupported type: ${typeof v}`);
|
|
47
48
|
}
|
|
48
49
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { backtickQuote, inputToString, tripleDoubleQuote } from "../string-utils.js";
|
|
2
2
|
import "./resolve-adapter.js";
|
|
3
3
|
import js2xml from "xml-js/lib/js2xml.js";
|
|
4
4
|
//#region src/requests/media/adapter.ts
|
|
@@ -80,7 +80,7 @@ const defaultAdapters = {
|
|
|
80
80
|
s.push("mp := multipart.NewWriter(payload)");
|
|
81
81
|
for (const [key, value] of Object.entries(data.body)) {
|
|
82
82
|
if (!value) continue;
|
|
83
|
-
const escaped =
|
|
83
|
+
const escaped = backtickQuote(inputToString(value, "application/json"));
|
|
84
84
|
s.push(`mp.WriteField("${key}", ${escaped})`);
|
|
85
85
|
}
|
|
86
86
|
}
|
|
@@ -103,20 +103,20 @@ const defaultAdapters = {
|
|
|
103
103
|
function str(init, mediaType, ctx) {
|
|
104
104
|
if (ctx.lang === "js") {
|
|
105
105
|
if (mediaType === "application/json") return `const body = JSON.stringify(${JSON.stringify(init, null, 2)})`;
|
|
106
|
-
return `const body = ${
|
|
106
|
+
return `const body = ${backtickQuote(inputToString(init, mediaType))}`;
|
|
107
107
|
}
|
|
108
|
-
if (ctx.lang === "python") return `body = ${
|
|
108
|
+
if (ctx.lang === "python") return `body = ${tripleDoubleQuote(inputToString(init, mediaType))}`;
|
|
109
109
|
if (ctx.lang === "go") {
|
|
110
110
|
const { addImport } = ctx;
|
|
111
111
|
addImport("strings");
|
|
112
|
-
return `body := strings.NewReader(${
|
|
112
|
+
return `body := strings.NewReader(${backtickQuote(inputToString(init, mediaType))})`;
|
|
113
113
|
}
|
|
114
114
|
if (ctx.lang === "java") {
|
|
115
115
|
const { addImport } = ctx;
|
|
116
116
|
addImport("java.net.http.HttpRequest.BodyPublishers");
|
|
117
|
-
return `var body = BodyPublishers.ofString(${
|
|
117
|
+
return `var body = BodyPublishers.ofString(${tripleDoubleQuote(inputToString(init, mediaType))});`;
|
|
118
118
|
}
|
|
119
|
-
if (ctx.lang === "csharp") return `var body = new StringContent(${
|
|
119
|
+
if (ctx.lang === "csharp") return `var body = new StringContent(${tripleDoubleQuote(`\n${inputToString(init, mediaType)}\n`)}, Encoding.UTF8, "${mediaType}");`;
|
|
120
120
|
}
|
|
121
121
|
//#endregion
|
|
122
122
|
export { defaultAdapters };
|
|
@@ -18,13 +18,34 @@ function inputToString(value, format = "application/json") {
|
|
|
18
18
|
spaces: 2
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Returns the input string wrapped in single quotes, escaping internal single quotes and backslashes.
|
|
23
|
+
*/
|
|
24
|
+
function singleQuote(str) {
|
|
25
|
+
return `'${str.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Returns the input string wrapped in double quotes, escaping internal double quotes and backslashes.
|
|
29
|
+
*/
|
|
30
|
+
function doubleQuote(str) {
|
|
31
|
+
return `"${str.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Returns the input string wrapped in Python triple double-quotes,
|
|
35
|
+
* escaping embedded triple quotes and backslashes.
|
|
36
|
+
*/
|
|
37
|
+
function tripleDoubleQuote(str) {
|
|
38
|
+
return `"""${str.replace(/\\/g, "\\\\").replace(/"""/g, "\\\"\\\"\\\"")}"""`;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Returns the input string wrapped in backticks, escaping internal backticks and backslashes.
|
|
42
|
+
*/
|
|
43
|
+
function backtickQuote(str) {
|
|
44
|
+
return `\`${str.replace(/\\/g, "\\\\").replace(/`/g, "\\`")}\``;
|
|
24
45
|
}
|
|
25
46
|
function indent(code, tab = 1) {
|
|
26
47
|
const p = " ".repeat(tab);
|
|
27
48
|
return code.split("\n").map((v) => v.length === 0 ? v : p + v).join("\n");
|
|
28
49
|
}
|
|
29
50
|
//#endregion
|
|
30
|
-
export {
|
|
51
|
+
export { backtickQuote, doubleQuote, indent, inputToString, singleQuote, tripleDoubleQuote };
|
package/dist/requests/types.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { EncodedParameter, EncodedParameterMultiple } from "./media/encode.js";
|
|
2
|
+
import { HttpMethods } from "../types.js";
|
|
2
3
|
|
|
3
4
|
//#region src/requests/types.d.ts
|
|
4
5
|
interface RawRequestData {
|
|
5
|
-
method:
|
|
6
|
+
method: HttpMethods;
|
|
6
7
|
path: Record<string, unknown>;
|
|
7
8
|
query: Record<string, unknown>;
|
|
8
9
|
header: Record<string, unknown>;
|
|
@@ -11,7 +12,7 @@ interface RawRequestData {
|
|
|
11
12
|
bodyMediaType?: string;
|
|
12
13
|
}
|
|
13
14
|
interface RequestData {
|
|
14
|
-
method:
|
|
15
|
+
method: HttpMethods;
|
|
15
16
|
path: Record<string, EncodedParameter>;
|
|
16
17
|
query: Record<string, EncodedParameterMultiple>;
|
|
17
18
|
header: Record<string, EncodedParameter>;
|
package/dist/ui/api-page.js
CHANGED
|
@@ -9,7 +9,7 @@ function APIPage({ showTitle: hasHead = false, showDescription, operations, webh
|
|
|
9
9
|
className: "flex flex-col gap-24 text-sm @container",
|
|
10
10
|
children: [slots.operations?.map((op) => op.children), slots.webhooks?.map((op) => op.children)]
|
|
11
11
|
});
|
|
12
|
-
|
|
12
|
+
let content = renderPageLayout({
|
|
13
13
|
operations: operations?.map((item) => {
|
|
14
14
|
const pathItem = dereferenced.paths?.[item.path];
|
|
15
15
|
if (!pathItem) throw new Error(`[Fumadocs OpenAPI] Path not found in OpenAPI schema: ${item.path}`);
|
|
@@ -44,7 +44,9 @@ function APIPage({ showTitle: hasHead = false, showDescription, operations, webh
|
|
|
44
44
|
};
|
|
45
45
|
})
|
|
46
46
|
}, ctx);
|
|
47
|
+
if (ctx.playground?.enabled !== false && ctx.playground?.provider) content = ctx.playground.provider({ children: content });
|
|
47
48
|
return /* @__PURE__ */ jsx(ctx.clientBoundary.ApiProvider, {
|
|
49
|
+
schemes: dereferenced.components?.securitySchemes ?? {},
|
|
48
50
|
shikiOptions: ctx.shikiOptions,
|
|
49
51
|
client: ctx.client ?? {},
|
|
50
52
|
children: /* @__PURE__ */ jsx(ctx.clientBoundary.ServerProvider, {
|
package/dist/ui/base.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { SchemaUIOptions } from "./schema/index.js";
|
|
|
6
6
|
import { OpenAPIServer } from "../server/create.js";
|
|
7
7
|
import { ApiPageProps, OperationItem, WebhookItem } from "./api-page.js";
|
|
8
8
|
import { ResponseTab } from "./operation/response-tabs.js";
|
|
9
|
+
import { RequestTabsRenderContext } from "./operation/request-tabs.js";
|
|
9
10
|
import { ClientCodeBlockProvider } from "./components/codeblock.js";
|
|
10
11
|
import { Awaitable, MethodInformation, RenderContext } from "../types.js";
|
|
11
12
|
import { NoReference } from "../utils/schema/index.js";
|
|
@@ -74,10 +75,7 @@ interface CreateAPIPageOptions {
|
|
|
74
75
|
*/
|
|
75
76
|
content?: {
|
|
76
77
|
renderResponseTabs?: (tabs: ResponseTab[], ctx: RenderContext) => ReactNode;
|
|
77
|
-
renderRequestTabs?: (items: ExampleRequestItem[], ctx:
|
|
78
|
-
route: string;
|
|
79
|
-
operation: NoReference<MethodInformation>;
|
|
80
|
-
}) => ReactNode;
|
|
78
|
+
renderRequestTabs?: (items: ExampleRequestItem[], ctx: RequestTabsRenderContext) => ReactNode;
|
|
81
79
|
renderAPIExampleLayout?: (slots: {
|
|
82
80
|
selector: ReactNode;
|
|
83
81
|
usageTabs: ReactNode;
|
|
@@ -142,6 +140,12 @@ interface CreateAPIPageOptions {
|
|
|
142
140
|
* @defaultValue true
|
|
143
141
|
*/
|
|
144
142
|
enabled?: boolean;
|
|
143
|
+
/**
|
|
144
|
+
* render a page-level provider (useful for handling auth)
|
|
145
|
+
*/
|
|
146
|
+
provider?: (props: {
|
|
147
|
+
children: ReactNode;
|
|
148
|
+
}) => ReactNode;
|
|
145
149
|
/**
|
|
146
150
|
* replace the server-side renderer
|
|
147
151
|
*/
|
package/dist/ui/base.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { parseSecurities } from "../utils/schema/index.js";
|
|
2
2
|
import { defaultAdapters } from "../requests/media/adapter.js";
|
|
3
|
-
import { encodeInternalRef } from "../utils/schema/ref.js";
|
|
4
3
|
import { ClientCodeBlockProvider } from "./components/codeblock.js";
|
|
4
|
+
import { encodeInternalRef } from "../utils/schema/ref.js";
|
|
5
5
|
import { APIPage } from "./api-page.js";
|
|
6
6
|
import { pickSchema } from "../utils/schema/pick.js";
|
|
7
|
+
import { PlaygroundAuthProvider } from "./client/boundary.lazy.js";
|
|
7
8
|
import Slugger from "github-slugger";
|
|
8
9
|
import * as JsxRuntime from "react/jsx-runtime";
|
|
9
10
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -38,6 +39,9 @@ function createAPIPage(server, options) {
|
|
|
38
39
|
...options.shikiOptions
|
|
39
40
|
}).use(rehypeReact);
|
|
40
41
|
}
|
|
42
|
+
function renderPlaygroundProviderDefault({ children }) {
|
|
43
|
+
return /* @__PURE__ */ jsx(PlaygroundAuthProvider, { children });
|
|
44
|
+
}
|
|
41
45
|
function renderPlaygroundDefault({ path, method, ctx }) {
|
|
42
46
|
return /* @__PURE__ */ jsx(ctx.clientBoundary.PlaygroundClient, {
|
|
43
47
|
route: path,
|
|
@@ -77,6 +81,7 @@ function createAPIPage(server, options) {
|
|
|
77
81
|
},
|
|
78
82
|
playground: {
|
|
79
83
|
...options.playground,
|
|
84
|
+
provider: options.playground?.provider ?? renderPlaygroundProviderDefault,
|
|
80
85
|
render: options.playground?.render ?? renderPlaygroundDefault
|
|
81
86
|
},
|
|
82
87
|
renderHeading(depth, text, props) {
|
|
@@ -8,6 +8,7 @@ const UsageTabsSelector = wrapLazy(() => import("../operation/usage-tabs/client.
|
|
|
8
8
|
const UsageTab = wrapLazy(() => import("../operation/usage-tabs/client.js").then((mod) => ({ default: mod.UsageTab })));
|
|
9
9
|
const SchemaUI = wrapLazy(() => import("../schema/client.js").then((mod) => ({ default: mod.SchemaUI })));
|
|
10
10
|
const PlaygroundClient = wrapLazy(() => import("../../playground/client.js"));
|
|
11
|
+
const PlaygroundAuthProvider = wrapLazy(() => import("../../playground/auth.js").then((mod) => ({ default: mod.AuthProvider })));
|
|
11
12
|
function wrapLazy(load) {
|
|
12
13
|
const V = lazy(load);
|
|
13
14
|
return function wrapper(props) {
|
|
@@ -15,4 +16,4 @@ function wrapLazy(load) {
|
|
|
15
16
|
};
|
|
16
17
|
}
|
|
17
18
|
//#endregion
|
|
18
|
-
export { ApiProvider, PlaygroundClient, SchemaUI, ServerProvider, UsageTab, UsageTabsSelector };
|
|
19
|
+
export { ApiProvider, PlaygroundAuthProvider, PlaygroundClient, SchemaUI, ServerProvider, UsageTab, UsageTabsSelector };
|
package/dist/ui/client/i18n.js
CHANGED
|
@@ -9,9 +9,11 @@ function useTranslations() {
|
|
|
9
9
|
* Renders a translated string. Use in server components so the label is resolved on the client from the current locale.
|
|
10
10
|
*/
|
|
11
11
|
function I18nLabel({ label, replacements = {} }) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
return withReplacements(useTranslations()[label], replacements);
|
|
13
|
+
}
|
|
14
|
+
function withReplacements(t, replacements) {
|
|
15
|
+
for (const [k, v] of Object.entries(replacements)) t = t.replaceAll(`{${k}}`, v);
|
|
16
|
+
return t;
|
|
15
17
|
}
|
|
16
18
|
//#endregion
|
|
17
|
-
export { I18nLabel, useTranslations };
|
|
19
|
+
export { I18nLabel, useTranslations, withReplacements };
|
|
@@ -5,7 +5,7 @@ function useStorageKey() {
|
|
|
5
5
|
const { storageKeyPrefix } = useApiContext().client;
|
|
6
6
|
return useMemo(() => ({
|
|
7
7
|
of: (name) => getStorageKey(storageKeyPrefix, name),
|
|
8
|
-
AuthField: (
|
|
8
|
+
AuthField: (schemeId) => getStorageKey(storageKeyPrefix, `auth-${schemeId}`)
|
|
9
9
|
}), [storageKeyPrefix]);
|
|
10
10
|
}
|
|
11
11
|
function getStorageKey(prefix = "fumadocs-openapi-", name) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { cn } from "../../utils/cn.js";
|
|
3
2
|
import { useTranslations } from "../client/i18n.js";
|
|
3
|
+
import { cn } from "../../utils/cn.js";
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
import { X } from "lucide-react";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { APIPageClientOptions } from "../client/index.js";
|
|
2
|
-
import { RenderContext, ServerObject } from "../../types.js";
|
|
2
|
+
import { RenderContext, SecuritySchemeObject, ServerObject } from "../../types.js";
|
|
3
3
|
import { ReactNode } from "react";
|
|
4
4
|
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
5
5
|
|
|
@@ -7,11 +7,14 @@ import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
|
7
7
|
interface InheritFromContext extends Pick<RenderContext, 'shikiOptions'> {
|
|
8
8
|
client: APIPageClientOptions;
|
|
9
9
|
}
|
|
10
|
-
|
|
10
|
+
interface ApiProviderProps extends InheritFromContext {
|
|
11
|
+
schemes: Record<string, SecuritySchemeObject>;
|
|
12
|
+
}
|
|
11
13
|
declare function ApiProvider({
|
|
12
14
|
children,
|
|
13
15
|
shikiOptions,
|
|
14
|
-
client
|
|
16
|
+
client,
|
|
17
|
+
schemes
|
|
15
18
|
}: ApiProviderProps & {
|
|
16
19
|
children: ReactNode;
|
|
17
20
|
}): _$react_jsx_runtime0.JSX.Element;
|
package/dist/ui/contexts/api.js
CHANGED
|
@@ -18,7 +18,7 @@ function useServerContext() {
|
|
|
18
18
|
if (!ctx) throw new Error("Component must be used under <ApiProvider />");
|
|
19
19
|
return ctx;
|
|
20
20
|
}
|
|
21
|
-
function ApiProvider({ children, shikiOptions, client }) {
|
|
21
|
+
function ApiProvider({ children, shikiOptions, client, schemes }) {
|
|
22
22
|
return /* @__PURE__ */ jsx(ApiContext, {
|
|
23
23
|
value: useMemo(() => {
|
|
24
24
|
let codeUsages;
|
|
@@ -31,12 +31,17 @@ function ApiProvider({ children, shikiOptions, client }) {
|
|
|
31
31
|
shikiOptions,
|
|
32
32
|
client,
|
|
33
33
|
codeUsages,
|
|
34
|
+
schemes,
|
|
34
35
|
mediaAdapters: {
|
|
35
36
|
...defaultAdapters,
|
|
36
37
|
...client.mediaAdapters
|
|
37
38
|
}
|
|
38
39
|
};
|
|
39
|
-
}, [
|
|
40
|
+
}, [
|
|
41
|
+
client,
|
|
42
|
+
schemes,
|
|
43
|
+
shikiOptions
|
|
44
|
+
]),
|
|
40
45
|
children
|
|
41
46
|
});
|
|
42
47
|
}
|
package/dist/ui/create-client.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { parseSecurities } from "../utils/schema/index.js";
|
|
2
2
|
import { defaultAdapters } from "../requests/media/adapter.js";
|
|
3
3
|
import { ClientCodeBlock, ClientCodeBlockProvider } from "./components/codeblock.js";
|
|
4
|
+
import { AuthProvider } from "../playground/auth.js";
|
|
4
5
|
import { dereferenceDocument } from "../utils/document/dereference.js";
|
|
5
6
|
import { APIPage } from "./api-page.js";
|
|
6
7
|
import { boundary_exports } from "./client/boundary.js";
|
|
@@ -66,6 +67,9 @@ function createClientAPIPage({ shiki = defaultShikiFactory, shikiOptions = { the
|
|
|
66
67
|
readOnly: false
|
|
67
68
|
});
|
|
68
69
|
}
|
|
70
|
+
function renderPlaygroundProviderDefault({ children }) {
|
|
71
|
+
return /* @__PURE__ */ jsx(AuthProvider, { children });
|
|
72
|
+
}
|
|
69
73
|
return function ClientAPIPage({ payload, ...props }) {
|
|
70
74
|
const processed = useMemo(() => dereferenceDocument(payload.bundled), [payload.bundled]);
|
|
71
75
|
const ctx = useMemo(() => ({
|
|
@@ -82,6 +86,7 @@ function createClientAPIPage({ shiki = defaultShikiFactory, shikiOptions = { the
|
|
|
82
86
|
},
|
|
83
87
|
playground: {
|
|
84
88
|
...options.playground,
|
|
89
|
+
provider: options.playground?.provider ?? renderPlaygroundProviderDefault,
|
|
85
90
|
render: options.playground?.render ?? renderPlaygroundDefault
|
|
86
91
|
},
|
|
87
92
|
renderHeading(depth, text, props) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { cn } from "../../utils/cn.js";
|
|
3
2
|
import { useTranslations } from "../client/i18n.js";
|
|
3
|
+
import { cn } from "../../utils/cn.js";
|
|
4
4
|
import { createContext, use, useMemo, useRef, useState } from "react";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
import { Check, Copy } from "lucide-react";
|
|
@@ -2,9 +2,9 @@ import { idToTitle } from "../../utils/id-to-title.js";
|
|
|
2
2
|
import { createMethod, methodKeys } from "../../utils/schema/index.js";
|
|
3
3
|
import { isMediaTypeSupported } from "../../requests/media/resolve-adapter.js";
|
|
4
4
|
import "../../requests/media/adapter.js";
|
|
5
|
+
import { I18nLabel } from "../client/i18n.js";
|
|
5
6
|
import { cn } from "../../utils/cn.js";
|
|
6
7
|
import { Badge, MethodLabel } from "../components/method-label.js";
|
|
7
|
-
import { I18nLabel } from "../client/i18n.js";
|
|
8
8
|
import { CopyTypeScriptPanel, OperationProvider } from "./client.js";
|
|
9
9
|
import { Schema } from "../schema/index.js";
|
|
10
10
|
import { AccordionContent, AccordionHeader, AccordionItem, AccordionTrigger, Accordions } from "../components/accordion.js";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { MethodInformation, RenderContext } from "../../types.js";
|
|
2
|
+
import { NoReference } from "../../utils/schema/index.js";
|
|
3
|
+
import { ReactNode } from "react";
|
|
4
|
+
//#region src/ui/operation/request-tabs.d.ts
|
|
5
|
+
interface RequestTabsRenderContext extends RenderContext {
|
|
6
|
+
route: string;
|
|
7
|
+
operation: NoReference<MethodInformation>;
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { RequestTabsRenderContext };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveRequestData } from "../../utils/url.js";
|
|
2
|
-
import { MethodLabel } from "../components/method-label.js";
|
|
3
2
|
import { I18nLabel } from "../client/i18n.js";
|
|
3
|
+
import { MethodLabel } from "../components/method-label.js";
|
|
4
4
|
import { AccordionContent, AccordionHeader, AccordionItem, AccordionTrigger, Accordions } from "../components/accordion.js";
|
|
5
5
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "fumadocs-ui/components/tabs";
|
|
@@ -15,41 +15,6 @@ function RequestTabs({ path, operation, examples, ctx }) {
|
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
17
|
function renderRequestTabsDefault(items, ctx) {
|
|
18
|
-
function renderItem(item) {
|
|
19
|
-
const requestData = item.data;
|
|
20
|
-
const displayNames = {
|
|
21
|
-
body: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(I18nLabel, { label: "titleRequestBody" }), /* @__PURE__ */ jsx("code", {
|
|
22
|
-
className: "text-xs text-fd-muted-foreground ms-auto",
|
|
23
|
-
children: requestData.bodyMediaType
|
|
24
|
-
})] }),
|
|
25
|
-
cookie: /* @__PURE__ */ jsx(I18nLabel, { label: "cookieParameters" }),
|
|
26
|
-
header: /* @__PURE__ */ jsx(I18nLabel, { label: "headerParameters" }),
|
|
27
|
-
query: /* @__PURE__ */ jsx(I18nLabel, { label: "queryParameters" }),
|
|
28
|
-
path: /* @__PURE__ */ jsx(I18nLabel, { label: "pathParameters" })
|
|
29
|
-
};
|
|
30
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
31
|
-
item.description && ctx.renderMarkdown(item.description),
|
|
32
|
-
/* @__PURE__ */ jsxs("div", {
|
|
33
|
-
className: "flex flex-row gap-2 items-center justify-between",
|
|
34
|
-
children: [/* @__PURE__ */ jsx(MethodLabel, { children: requestData.method }), /* @__PURE__ */ jsx("code", { children: resolveRequestData(ctx.route, item.encoded) })]
|
|
35
|
-
}),
|
|
36
|
-
/* @__PURE__ */ jsx(Accordions, {
|
|
37
|
-
type: "multiple",
|
|
38
|
-
className: "mt-2",
|
|
39
|
-
children: Object.entries(displayNames).map(([k, v]) => {
|
|
40
|
-
const data = requestData[k];
|
|
41
|
-
if (!data || Object.keys(data).length === 0) return;
|
|
42
|
-
return /* @__PURE__ */ jsxs(AccordionItem, {
|
|
43
|
-
value: k,
|
|
44
|
-
children: [/* @__PURE__ */ jsx(AccordionHeader, { children: /* @__PURE__ */ jsx(AccordionTrigger, { children: v }) }), /* @__PURE__ */ jsx(AccordionContent, {
|
|
45
|
-
className: "prose-no-margin",
|
|
46
|
-
children: ctx.renderCodeBlock("json", JSON.stringify(data, null, 2))
|
|
47
|
-
})]
|
|
48
|
-
}, k);
|
|
49
|
-
})
|
|
50
|
-
})
|
|
51
|
-
] });
|
|
52
|
-
}
|
|
53
18
|
let children;
|
|
54
19
|
if (items.length > 1) children = /* @__PURE__ */ jsxs(Tabs, {
|
|
55
20
|
defaultValue: items[0].id,
|
|
@@ -58,10 +23,16 @@ function renderRequestTabsDefault(items, ctx) {
|
|
|
58
23
|
children: item.id === "_default" ? /* @__PURE__ */ jsx(I18nLabel, { label: "requestTabNameDefault" }) : item.name
|
|
59
24
|
}, item.id)) }), items.map((item) => /* @__PURE__ */ jsx(TabsContent, {
|
|
60
25
|
value: item.id,
|
|
61
|
-
children:
|
|
26
|
+
children: /* @__PURE__ */ jsx(RequestTabsItem, {
|
|
27
|
+
item,
|
|
28
|
+
ctx
|
|
29
|
+
})
|
|
62
30
|
}, item.id))]
|
|
63
31
|
});
|
|
64
|
-
else if (items.length === 1) children =
|
|
32
|
+
else if (items.length === 1) children = /* @__PURE__ */ jsx(RequestTabsItem, {
|
|
33
|
+
item: items[0],
|
|
34
|
+
ctx
|
|
35
|
+
});
|
|
65
36
|
else children = /* @__PURE__ */ jsx("p", {
|
|
66
37
|
className: "text-fd-muted-foreground text-xs",
|
|
67
38
|
children: /* @__PURE__ */ jsx(I18nLabel, { label: "empty" })
|
|
@@ -74,5 +45,40 @@ function renderRequestTabsDefault(items, ctx) {
|
|
|
74
45
|
}), children]
|
|
75
46
|
});
|
|
76
47
|
}
|
|
48
|
+
function RequestTabsItem({ item, ctx }) {
|
|
49
|
+
const requestData = item.data;
|
|
50
|
+
const displayNames = {
|
|
51
|
+
body: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(I18nLabel, { label: "titleRequestBody" }), /* @__PURE__ */ jsx("code", {
|
|
52
|
+
className: "text-xs text-fd-muted-foreground ms-auto",
|
|
53
|
+
children: requestData.bodyMediaType
|
|
54
|
+
})] }),
|
|
55
|
+
cookie: /* @__PURE__ */ jsx(I18nLabel, { label: "cookieParameters" }),
|
|
56
|
+
header: /* @__PURE__ */ jsx(I18nLabel, { label: "headerParameters" }),
|
|
57
|
+
query: /* @__PURE__ */ jsx(I18nLabel, { label: "queryParameters" }),
|
|
58
|
+
path: /* @__PURE__ */ jsx(I18nLabel, { label: "pathParameters" })
|
|
59
|
+
};
|
|
60
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
61
|
+
item.description && ctx.renderMarkdown(item.description),
|
|
62
|
+
/* @__PURE__ */ jsxs("div", {
|
|
63
|
+
className: "flex flex-row gap-2 items-center justify-between",
|
|
64
|
+
children: [/* @__PURE__ */ jsx(MethodLabel, { children: requestData.method }), /* @__PURE__ */ jsx("code", { children: resolveRequestData(ctx.route, item.encoded) })]
|
|
65
|
+
}),
|
|
66
|
+
/* @__PURE__ */ jsx(Accordions, {
|
|
67
|
+
type: "multiple",
|
|
68
|
+
className: "mt-2",
|
|
69
|
+
children: Object.entries(displayNames).map(([k, v]) => {
|
|
70
|
+
const data = requestData[k];
|
|
71
|
+
if (!data || Object.keys(data).length === 0) return;
|
|
72
|
+
return /* @__PURE__ */ jsxs(AccordionItem, {
|
|
73
|
+
value: k,
|
|
74
|
+
children: [/* @__PURE__ */ jsx(AccordionHeader, { children: /* @__PURE__ */ jsx(AccordionTrigger, { children: v }) }), /* @__PURE__ */ jsx(AccordionContent, {
|
|
75
|
+
className: "prose-no-margin",
|
|
76
|
+
children: ctx.renderCodeBlock("json", JSON.stringify(data, null, 2))
|
|
77
|
+
})]
|
|
78
|
+
}, k);
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
] });
|
|
82
|
+
}
|
|
77
83
|
//#endregion
|
|
78
84
|
export { RequestTabs };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { joinURL, resolveRequestData, resolveServerUrl, withBase } from "../../../utils/url.js";
|
|
3
3
|
import { useApiContext, useServerContext } from "../../contexts/api.js";
|
|
4
|
-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../../components/select.js";
|
|
5
4
|
import { ClientCodeBlock } from "../../components/codeblock.js";
|
|
5
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../../components/select.js";
|
|
6
6
|
import { useOperationContext } from "../client.js";
|
|
7
7
|
import { useEffect, useMemo, useState } from "react";
|
|
8
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
package/dist/ui/schema/client.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { useTranslations } from "../client/i18n.js";
|
|
2
3
|
import { cn } from "../../utils/cn.js";
|
|
3
4
|
import { Badge } from "../components/method-label.js";
|
|
4
|
-
import { useTranslations } from "../client/i18n.js";
|
|
5
5
|
import { Fragment, Suspense, createContext, use, useCallback, useDeferredValue, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
6
6
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import { ChevronDown, FilterIcon } from "lucide-react";
|
|
8
|
+
import { buttonVariants } from "fumadocs-ui/components/ui/button";
|
|
8
9
|
import { cva } from "class-variance-authority";
|
|
9
10
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "fumadocs-ui/components/ui/collapsible";
|
|
10
|
-
import { buttonVariants } from "fumadocs-ui/components/ui/button";
|
|
11
11
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "fumadocs-ui/components/tabs";
|
|
12
12
|
import { Popover, PopoverContent, PopoverTrigger } from "fumadocs-ui/components/ui/popover";
|
|
13
13
|
//#region src/ui/schema/client.tsx
|
package/dist/ui/schema/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { I18nLabel } from "../client/i18n.js";
|
|
1
2
|
import { mergeAllOf } from "../../utils/schema/merge.js";
|
|
2
3
|
import { FormatFlags, schemaToString } from "../../utils/schema/to-string.js";
|
|
3
|
-
import { I18nLabel } from "../client/i18n.js";
|
|
4
4
|
import { useMemo } from "react";
|
|
5
5
|
import { jsx } from "react/jsx-runtime";
|
|
6
6
|
//#region src/ui/schema/index.tsx
|