fumadocs-openapi 11.1.1 → 11.2.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/css/generated/shared.css +1 -1
- package/dist/generate-file.d.ts +0 -1
- package/dist/i18n.d.ts +0 -1
- package/dist/node_modules/.pnpm/xml-js@1.6.11/node_modules/xml-js/lib/js2xml.js +2 -1
- package/dist/playground/auth.js +4 -3
- package/dist/playground/client.d.ts +4 -18
- package/dist/playground/client.js +52 -42
- package/dist/playground/components/oauth-dialog.js +3 -2
- package/dist/playground/components/result-display.d.ts +1 -6
- package/dist/playground/fetcher.d.ts +1 -1
- package/dist/requests/generators/all.d.ts +0 -1
- package/dist/requests/generators/csharp.d.ts +0 -1
- package/dist/requests/generators/curl.d.ts +0 -1
- package/dist/requests/generators/go.d.ts +0 -1
- package/dist/requests/generators/index.d.ts +1 -5
- package/dist/requests/generators/java.d.ts +0 -1
- package/dist/requests/generators/javascript.d.ts +0 -1
- package/dist/requests/generators/python.d.ts +0 -1
- package/dist/requests/generators/rust.d.ts +0 -1
- package/dist/requests/media/encode.d.ts +2 -0
- package/dist/requests/types.d.ts +0 -1
- package/dist/scalar/index.d.ts +0 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/types/openapi.d.ts +4 -4
- package/dist/types.d.ts +0 -1
- package/dist/ui/base.d.ts +1 -9
- package/dist/ui/base.js +10 -13
- package/dist/ui/create-client.d.ts +0 -1
- package/dist/ui/index.d.ts +6 -8
- package/dist/ui/operation/index.js +41 -30
- package/dist/ui/operation/request-tabs.d.ts +2 -4
- package/dist/ui/operation/response-tabs.d.ts +1 -3
- package/dist/ui/operation/response-tabs.js +19 -9
- package/dist/utils/document/dereference.d.ts +9 -6
- package/dist/utils/document/dereference.js +5 -7
- package/dist/utils/get-example-requests.d.ts +1 -2
- package/dist/utils/get-example-requests.js +17 -13
- package/dist/utils/pages/builder.d.ts +0 -1
- package/dist/utils/pages/builder.js +10 -8
- package/dist/utils/pages/preset-auto.d.ts +0 -1
- package/dist/utils/pages/preset-auto.js +2 -2
- package/dist/utils/pages/to-static-data.js +4 -2
- package/dist/utils/pages/to-text.d.ts +1 -1
- package/dist/utils/pages/to-text.js +5 -3
- package/dist/utils/schema.d.ts +1 -0
- package/dist/utils/schema.js +7 -4
- package/package.json +11 -10
- package/dist/utils/remove-undefined.js +0 -24
package/dist/ui/index.d.ts
CHANGED
|
@@ -8,10 +8,8 @@ import { PlaygroundClientOptions } from "../playground/client.js";
|
|
|
8
8
|
import { GeneratedPageProps, OperationItem, WebhookItem } from "../utils/pages/builder.js";
|
|
9
9
|
import { Awaitable, Document, HttpMethods, OperationObject, PathItemObject, RenderContext } from "../types.js";
|
|
10
10
|
import { ComponentProps, FC, HTMLAttributes, ReactNode } from "react";
|
|
11
|
-
import { NoReference } from "@fumadocs/api-docs/schema";
|
|
12
11
|
import { ShikiFactory } from "fumadocs-core/highlight/shiki";
|
|
13
12
|
import { BundledTheme, CodeOptionsThemes, CodeToHastOptionsCommon } from "shiki";
|
|
14
|
-
|
|
15
13
|
//#region src/ui/index.d.ts
|
|
16
14
|
interface GenerateTypeScriptDefinitionsContext {
|
|
17
15
|
name: string;
|
|
@@ -22,8 +20,8 @@ interface GenerateTypeScriptDefinitionsContext {
|
|
|
22
20
|
interface APIPlaygroundProps {
|
|
23
21
|
path: string;
|
|
24
22
|
method: HttpMethods;
|
|
25
|
-
operation:
|
|
26
|
-
pathItem:
|
|
23
|
+
operation: OperationObject;
|
|
24
|
+
pathItem: PathItemObject;
|
|
27
25
|
ctx: RenderContext;
|
|
28
26
|
}
|
|
29
27
|
interface CreateOpenAPIPageOptions {
|
|
@@ -41,9 +39,9 @@ interface CreateOpenAPIPageOptions {
|
|
|
41
39
|
* Generate example code usage for each endpoint.
|
|
42
40
|
*/
|
|
43
41
|
generateCodeSamples?: (options: {
|
|
44
|
-
operation:
|
|
42
|
+
operation: OperationObject;
|
|
45
43
|
method: HttpMethods;
|
|
46
|
-
pathItem:
|
|
44
|
+
pathItem: PathItemObject;
|
|
47
45
|
}) => InlineCodeUsageGenerator[];
|
|
48
46
|
shiki?: ShikiFactory;
|
|
49
47
|
shikiOptions?: Omit<CodeToHastOptionsCommon, 'lang'> & CodeOptionsThemes<BundledTheme>;
|
|
@@ -96,9 +94,9 @@ interface CreateOpenAPIPageOptions {
|
|
|
96
94
|
responses: ReactNode;
|
|
97
95
|
callbacks: ReactNode;
|
|
98
96
|
}, context: {
|
|
99
|
-
operation:
|
|
97
|
+
operation: OperationObject;
|
|
100
98
|
method: HttpMethods;
|
|
101
|
-
pathItem:
|
|
99
|
+
pathItem: PathItemObject;
|
|
102
100
|
ctx: RenderContext;
|
|
103
101
|
}) => ReactNode;
|
|
104
102
|
renderWebhookLayout?: (slots: {
|
|
@@ -33,8 +33,8 @@ const paramTypeKeys = [
|
|
|
33
33
|
function Operation({ type = "operation", path, operation, pathItem, method, showTitle, showDescription, headingLevel = 2 }) {
|
|
34
34
|
const t = useTranslations({ note: "operation page" });
|
|
35
35
|
const ctx = useRenderContext();
|
|
36
|
-
const { schema: { dereferenced } } = ctx;
|
|
37
|
-
const body = operation.requestBody;
|
|
36
|
+
const { schema: { dereferenced, resolve } } = ctx;
|
|
37
|
+
const body = resolve(operation.requestBody);
|
|
38
38
|
let headNode = null;
|
|
39
39
|
const operationDescription = operation.description ?? pathItem.description;
|
|
40
40
|
const descriptionNode = showDescription && operationDescription && /* @__PURE__ */ jsx(Markdown, { md: operationDescription });
|
|
@@ -110,7 +110,7 @@ function Operation({ type = "operation", path, operation, pathItem, method, show
|
|
|
110
110
|
anchorSegments: ["request-body", type],
|
|
111
111
|
value: type,
|
|
112
112
|
children: /* @__PURE__ */ jsx(RequestBodyContentItem, {
|
|
113
|
-
content,
|
|
113
|
+
content: resolve(content),
|
|
114
114
|
operation,
|
|
115
115
|
method
|
|
116
116
|
})
|
|
@@ -133,7 +133,7 @@ function Operation({ type = "operation", path, operation, pathItem, method, show
|
|
|
133
133
|
}, status))
|
|
134
134
|
})] });
|
|
135
135
|
}
|
|
136
|
-
const parameters = [...operation.parameters ?? [], ...pathItem.parameters ?? []];
|
|
136
|
+
const parameters = [...operation.parameters ?? [], ...pathItem.parameters ?? []].map((param) => resolve(param));
|
|
137
137
|
const parameterNode = paramTypeKeys.map((type) => {
|
|
138
138
|
const params = parameters.filter((param) => param.in === type);
|
|
139
139
|
if (!params || params.length === 0) return;
|
|
@@ -146,19 +146,23 @@ function Operation({ type = "operation", path, operation, pathItem, method, show
|
|
|
146
146
|
segments: ["parameters", type],
|
|
147
147
|
children: /* @__PURE__ */ jsx("div", {
|
|
148
148
|
className: "flex flex-col",
|
|
149
|
-
children: params.map((param) =>
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
149
|
+
children: params.map((param) => {
|
|
150
|
+
if (param.schema == null) return;
|
|
151
|
+
const schema = resolve(param.schema);
|
|
152
|
+
return /* @__PURE__ */ jsx(ctx.SchemaUI, {
|
|
153
|
+
client: {
|
|
154
|
+
name: param.name,
|
|
155
|
+
required: param.required
|
|
156
|
+
},
|
|
157
|
+
root: typeof schema === "object" ? {
|
|
158
|
+
...schema,
|
|
159
|
+
description: param.description ?? schema.description,
|
|
160
|
+
deprecated: (param.deprecated ?? false) || (schema.deprecated ?? false)
|
|
161
|
+
} : schema,
|
|
162
|
+
readOnly: method === "get",
|
|
163
|
+
writeOnly: method !== "get"
|
|
164
|
+
}, param.name);
|
|
165
|
+
})
|
|
162
166
|
})
|
|
163
167
|
})] }, type);
|
|
164
168
|
});
|
|
@@ -203,7 +207,7 @@ function Operation({ type = "operation", path, operation, pathItem, method, show
|
|
|
203
207
|
}), securities.map((security, i) => /* @__PURE__ */ jsx(SelectTab, {
|
|
204
208
|
value: items[i].value,
|
|
205
209
|
children: Object.entries(security).map(([key, scopes]) => {
|
|
206
|
-
const scheme = securitySchemes?.[key];
|
|
210
|
+
const scheme = resolve(securitySchemes?.[key]);
|
|
207
211
|
if (!scheme) return;
|
|
208
212
|
return /* @__PURE__ */ jsx(AuthScheme, {
|
|
209
213
|
scheme,
|
|
@@ -214,15 +218,21 @@ function Operation({ type = "operation", path, operation, pathItem, method, show
|
|
|
214
218
|
});
|
|
215
219
|
}
|
|
216
220
|
const webhookCallbacks = [];
|
|
217
|
-
for (const [name,
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
221
|
+
for (const [name, item] of Object.entries(operation.callbacks ?? {})) {
|
|
222
|
+
const callbacks = resolve(item);
|
|
223
|
+
for (const [path, pathItem] of Object.entries(callbacks)) {
|
|
224
|
+
const callback = resolve(pathItem);
|
|
225
|
+
for (const method of methodKeys) {
|
|
226
|
+
if (!callback[method]) continue;
|
|
227
|
+
webhookCallbacks.push({
|
|
228
|
+
name,
|
|
229
|
+
path,
|
|
230
|
+
method,
|
|
231
|
+
callback,
|
|
232
|
+
operation: callback[method]
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
226
236
|
}
|
|
227
237
|
if (webhookCallbacks.length > 0) callbacksNode = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Heading, {
|
|
228
238
|
id: "callbacks",
|
|
@@ -399,7 +409,7 @@ function RequestBodyContentItem({ content, method, operation }) {
|
|
|
399
409
|
client: {
|
|
400
410
|
name: "body",
|
|
401
411
|
as: "body",
|
|
402
|
-
required: operation.requestBody?.required
|
|
412
|
+
required: ctx.schema.resolve(operation.requestBody)?.required
|
|
403
413
|
},
|
|
404
414
|
root: content.schema,
|
|
405
415
|
readOnly: method === "get",
|
|
@@ -407,7 +417,8 @@ function RequestBodyContentItem({ content, method, operation }) {
|
|
|
407
417
|
})] });
|
|
408
418
|
}
|
|
409
419
|
function ResponseAccordion({ status, operation }) {
|
|
410
|
-
const
|
|
420
|
+
const { schema } = useRenderContext();
|
|
421
|
+
const response = schema.resolve(operation.responses[status]);
|
|
411
422
|
const contentTypes = response.content ? Object.entries(response.content) : [];
|
|
412
423
|
const items = contentTypes.map(([key]) => ({
|
|
413
424
|
label: /* @__PURE__ */ jsx("code", {
|
|
@@ -439,7 +450,7 @@ function ResponseAccordion({ status, operation }) {
|
|
|
439
450
|
}), contentTypes.map(([type, item]) => /* @__PURE__ */ jsx(SelectTab, {
|
|
440
451
|
value: type,
|
|
441
452
|
anchorSegments: [type],
|
|
442
|
-
children: /* @__PURE__ */ jsx(RepsonseAccordionItem, { item })
|
|
453
|
+
children: /* @__PURE__ */ jsx(RepsonseAccordionItem, { item: schema.resolve(item) })
|
|
443
454
|
}, type))]
|
|
444
455
|
})]
|
|
445
456
|
})
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { ExampleRequestItem } from "../../utils/get-example-requests.js";
|
|
2
2
|
import { HttpMethods, OperationObject, PathItemObject } from "../../types.js";
|
|
3
3
|
import { ReactNode } from "react";
|
|
4
|
-
import { NoReference } from "@fumadocs/api-docs/schema";
|
|
5
|
-
|
|
6
4
|
//#region src/ui/operation/request-tabs.d.ts
|
|
7
5
|
interface RequestTabsRenderOptions {
|
|
8
6
|
route: string;
|
|
9
7
|
items: ExampleRequestItem[];
|
|
10
8
|
method: HttpMethods;
|
|
11
|
-
pathItem:
|
|
12
|
-
operation:
|
|
9
|
+
pathItem: PathItemObject;
|
|
10
|
+
operation: OperationObject;
|
|
13
11
|
}
|
|
14
12
|
//#endregion
|
|
15
13
|
export { RequestTabsRenderOptions };
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { ResponseObject } from "../../types.js";
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
|
-
import { NoReference } from "@fumadocs/api-docs/schema";
|
|
4
|
-
|
|
5
3
|
//#region src/ui/operation/response-tabs.d.ts
|
|
6
4
|
interface ResponseTab {
|
|
7
5
|
/**
|
|
8
6
|
* HTTP response code
|
|
9
7
|
*/
|
|
10
8
|
code: string;
|
|
11
|
-
response:
|
|
9
|
+
response: ResponseObject;
|
|
12
10
|
/**
|
|
13
11
|
* media type of response
|
|
14
12
|
*/
|
|
@@ -3,6 +3,7 @@ import { getPreferredType } from "../../utils/schema.js";
|
|
|
3
3
|
import { useRenderContext } from "../contexts/api.js";
|
|
4
4
|
import { ClientCodeBlock } from "../components/codeblock.js";
|
|
5
5
|
import { Markdown } from "../components/markdown.js";
|
|
6
|
+
import { getRaw } from "@scalar/json-magic/magic-proxy";
|
|
6
7
|
import { useMemo } from "react";
|
|
7
8
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
8
9
|
import { useTranslations } from "@fuma-translate/react";
|
|
@@ -12,13 +13,15 @@ import { sample } from "@fumadocs/api-docs/schema/sample";
|
|
|
12
13
|
//#region src/ui/operation/response-tabs.tsx
|
|
13
14
|
function ResponseTabs({ operation }) {
|
|
14
15
|
const ctx = useRenderContext();
|
|
16
|
+
const { resolve } = ctx.schema;
|
|
15
17
|
const t = useTranslations({ note: "operation page" });
|
|
16
18
|
const tabs = useMemo(() => {
|
|
17
19
|
const tabs = [];
|
|
18
20
|
if (!operation.responses) return tabs;
|
|
19
|
-
for (const [code,
|
|
21
|
+
for (const [code, item] of Object.entries(operation.responses)) {
|
|
22
|
+
const response = resolve(item);
|
|
20
23
|
const media = response.content ? getPreferredType(response.content) : null;
|
|
21
|
-
const responseOfType = media ? response.content?.[media] : null;
|
|
24
|
+
const responseOfType = media ? resolve(response.content?.[media]) : null;
|
|
22
25
|
const tab = {
|
|
23
26
|
code,
|
|
24
27
|
response,
|
|
@@ -26,22 +29,29 @@ function ResponseTabs({ operation }) {
|
|
|
26
29
|
};
|
|
27
30
|
if (responseOfType?.examples) {
|
|
28
31
|
tab.examples ??= [];
|
|
29
|
-
for (const [key,
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
for (const [key, item] of Object.entries(responseOfType.examples)) {
|
|
33
|
+
const example = resolve(item);
|
|
34
|
+
tab.examples.push({
|
|
35
|
+
label: example?.summary ?? t("Example {key}", { variables: { key } }),
|
|
36
|
+
sample: getRaw(example.value),
|
|
37
|
+
description: example?.description
|
|
38
|
+
});
|
|
39
|
+
}
|
|
34
40
|
} else if (responseOfType?.example || responseOfType?.schema) {
|
|
35
41
|
tab.examples ??= [];
|
|
36
42
|
tab.examples.push({
|
|
37
43
|
label: t("Example"),
|
|
38
|
-
sample: responseOfType.example ?? sample(responseOfType.schema)
|
|
44
|
+
sample: getRaw(responseOfType.example) ?? sample(responseOfType.schema)
|
|
39
45
|
});
|
|
40
46
|
}
|
|
41
47
|
tabs.push(tab);
|
|
42
48
|
}
|
|
43
49
|
return tabs;
|
|
44
|
-
}, [
|
|
50
|
+
}, [
|
|
51
|
+
operation.responses,
|
|
52
|
+
resolve,
|
|
53
|
+
t
|
|
54
|
+
]);
|
|
45
55
|
if (tabs.length === 0) return null;
|
|
46
56
|
const { renderResponseTabs = renderResponseTabsDefault } = ctx.content ?? {};
|
|
47
57
|
return renderResponseTabs({ tabs }, ctx);
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { Document } from "../../types.js";
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import { NoReferenceSwallow } from "@fumadocs/api-docs/schema";
|
|
4
3
|
//#region src/utils/document/dereference.d.ts
|
|
5
4
|
interface DereferencedDocument {
|
|
6
5
|
/**
|
|
7
|
-
*
|
|
6
|
+
* document wrapped in a magic proxy (`@scalar/json-magic`).
|
|
7
|
+
*
|
|
8
|
+
* Reference Objects remain in the document — resolve them lazily with {@link resolve}.
|
|
8
9
|
*/
|
|
9
|
-
dereferenced:
|
|
10
|
+
dereferenced: Document;
|
|
10
11
|
/**
|
|
11
|
-
*
|
|
12
|
+
* Shallowly resolve a Reference Object from the document, merging sibling keywords.
|
|
13
|
+
*
|
|
14
|
+
* Non-reference values are returned as-is.
|
|
12
15
|
*/
|
|
13
|
-
|
|
16
|
+
resolve: <T>(node: T) => NoReferenceSwallow<T>;
|
|
14
17
|
bundled: Document;
|
|
15
18
|
}
|
|
16
19
|
//#endregion
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { dereferenceShallow } from "@fumadocs/api-docs/schema/dereference";
|
|
2
|
+
import { createMagicProxy } from "@scalar/json-magic/magic-proxy";
|
|
2
3
|
//#region src/utils/document/dereference.ts
|
|
3
4
|
function dereferenceBundledDocument(bundled) {
|
|
4
|
-
const dereferenceMap = /* @__PURE__ */ new Map();
|
|
5
5
|
return {
|
|
6
6
|
bundled,
|
|
7
|
-
dereferenced:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
getRawRef(obj) {
|
|
11
|
-
return dereferenceMap.get(obj);
|
|
7
|
+
dereferenced: createMagicProxy(bundled),
|
|
8
|
+
resolve(node) {
|
|
9
|
+
return dereferenceShallow(node);
|
|
12
10
|
}
|
|
13
11
|
};
|
|
14
12
|
}
|
|
@@ -1,26 +1,29 @@
|
|
|
1
1
|
import { getPreferredType, pickExample } from "./schema.js";
|
|
2
2
|
import { encodeRequestData } from "../requests/media/encode.js";
|
|
3
|
+
import { dereferenceShallow } from "@fumadocs/api-docs/schema/dereference";
|
|
4
|
+
import { getRaw } from "@scalar/json-magic/magic-proxy";
|
|
3
5
|
import { sample } from "@fumadocs/api-docs/schema/sample";
|
|
4
6
|
//#region src/utils/get-example-requests.ts
|
|
5
7
|
function getExampleRequests({ path, method, ctx, operation, pathItem }) {
|
|
6
|
-
const requestBody = operation.requestBody;
|
|
8
|
+
const requestBody = dereferenceShallow(operation.requestBody);
|
|
7
9
|
const media = requestBody?.content ? getPreferredType(requestBody.content) : null;
|
|
8
|
-
const bodyOfType = media ? requestBody.content[media] : null;
|
|
9
|
-
const parameters = [...operation.parameters ?? [], ...pathItem.parameters ?? []];
|
|
10
|
+
const bodyOfType = media ? dereferenceShallow(requestBody.content[media]) : null;
|
|
11
|
+
const parameters = [...operation.parameters ?? [], ...pathItem.parameters ?? []].map(dereferenceShallow);
|
|
10
12
|
if (bodyOfType?.examples) {
|
|
11
13
|
const result = [];
|
|
12
|
-
for (const [key,
|
|
14
|
+
for (const [key, item] of Object.entries(bodyOfType.examples)) {
|
|
15
|
+
const { summary, description } = dereferenceShallow(item);
|
|
13
16
|
const data = getRequestData({
|
|
14
17
|
path,
|
|
15
|
-
body:
|
|
18
|
+
body: requestBody,
|
|
16
19
|
parameters,
|
|
17
20
|
sampleKey: key,
|
|
18
21
|
method
|
|
19
22
|
});
|
|
20
23
|
result.push({
|
|
21
24
|
id: key,
|
|
22
|
-
name:
|
|
23
|
-
description
|
|
25
|
+
name: summary || key,
|
|
26
|
+
description,
|
|
24
27
|
data,
|
|
25
28
|
encoded: encodeRequestData(data, ctx.mediaAdapters, parameters)
|
|
26
29
|
});
|
|
@@ -29,14 +32,15 @@ function getExampleRequests({ path, method, ctx, operation, pathItem }) {
|
|
|
29
32
|
}
|
|
30
33
|
const data = getRequestData({
|
|
31
34
|
path,
|
|
32
|
-
body:
|
|
35
|
+
body: requestBody,
|
|
33
36
|
method,
|
|
34
37
|
parameters
|
|
35
38
|
});
|
|
39
|
+
const schema = dereferenceShallow(bodyOfType?.schema);
|
|
36
40
|
return [{
|
|
37
41
|
id: "_default",
|
|
38
42
|
name: "Default",
|
|
39
|
-
description: typeof
|
|
43
|
+
description: typeof schema === "object" ? schema.description : void 0,
|
|
40
44
|
data,
|
|
41
45
|
encoded: encodeRequestData(data, ctx.mediaAdapters, parameters)
|
|
42
46
|
}];
|
|
@@ -55,7 +59,7 @@ function getRequestData({ method, path, parameters, sampleKey, body }) {
|
|
|
55
59
|
if (param.schema) value = sample(param.schema);
|
|
56
60
|
else if (param.content) {
|
|
57
61
|
const type = getPreferredType(param.content);
|
|
58
|
-
const content = type ? param.content[type] : void 0;
|
|
62
|
+
const content = type ? dereferenceShallow(param.content[type]) : void 0;
|
|
59
63
|
if (!content || !content.schema) throw new Error(`Cannot find "${param.name}" parameter info for media type "${type}" in ${path} ${method}`);
|
|
60
64
|
value = sample(content.schema);
|
|
61
65
|
}
|
|
@@ -77,9 +81,9 @@ function getRequestData({ method, path, parameters, sampleKey, body }) {
|
|
|
77
81
|
const type = getPreferredType(body.content);
|
|
78
82
|
if (!type) throw new Error(`Cannot find body schema for ${path} ${method}: missing media type`);
|
|
79
83
|
result.bodyMediaType = type;
|
|
80
|
-
const bodyOfType = body.content[type];
|
|
81
|
-
if (bodyOfType.examples && sampleKey) result.body = bodyOfType.examples[sampleKey].value;
|
|
82
|
-
else if (bodyOfType.example) result.body = bodyOfType.example;
|
|
84
|
+
const bodyOfType = dereferenceShallow(body.content[type]);
|
|
85
|
+
if (bodyOfType.examples && sampleKey) result.body = getRaw(dereferenceShallow(bodyOfType.examples[sampleKey]).value);
|
|
86
|
+
else if (bodyOfType.example) result.body = getRaw(bodyOfType.example);
|
|
83
87
|
else result.body = sample(bodyOfType?.schema ?? {}, {
|
|
84
88
|
skipReadOnly: method !== "get",
|
|
85
89
|
skipWriteOnly: method === "get",
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { getTagDisplayName, methodKeys } from "../schema.js";
|
|
2
2
|
import { idToTitle } from "@fumadocs/api-docs/utils/id-to-title";
|
|
3
3
|
import { dereferenceShallow } from "@fumadocs/api-docs/schema/dereference";
|
|
4
|
+
import { createMagicProxy } from "@scalar/json-magic/magic-proxy";
|
|
4
5
|
//#region src/utils/pages/builder.ts
|
|
5
6
|
function fromSchema(schemaId, bundled, config) {
|
|
6
7
|
const files = [];
|
|
7
8
|
const { toPages } = config;
|
|
9
|
+
const document = createMagicProxy(bundled);
|
|
8
10
|
toPages({
|
|
9
11
|
id: schemaId,
|
|
10
|
-
document
|
|
11
|
-
dereferenceShallow: (s) => dereferenceShallow(s
|
|
12
|
+
document,
|
|
13
|
+
dereferenceShallow: (s) => dereferenceShallow(s),
|
|
12
14
|
create(entry) {
|
|
13
15
|
files.push(entry);
|
|
14
16
|
},
|
|
@@ -17,7 +19,7 @@ function fromSchema(schemaId, bundled, config) {
|
|
|
17
19
|
webhooks: [],
|
|
18
20
|
operations: []
|
|
19
21
|
};
|
|
20
|
-
for (const [path, pathItem] of Object.entries(
|
|
22
|
+
for (const [path, pathItem] of Object.entries(document.paths ?? {})) {
|
|
21
23
|
if (!pathItem) continue;
|
|
22
24
|
for (const methodKey of methodKeys) {
|
|
23
25
|
if (!pathItem[methodKey]) continue;
|
|
@@ -28,8 +30,8 @@ function fromSchema(schemaId, bundled, config) {
|
|
|
28
30
|
});
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
|
-
for (const [name, _pathItem] of Object.entries(
|
|
32
|
-
const pathItem = dereferenceShallow(_pathItem
|
|
33
|
+
for (const [name, _pathItem] of Object.entries(document.webhooks ?? {})) {
|
|
34
|
+
const pathItem = dereferenceShallow(_pathItem);
|
|
33
35
|
if (!pathItem) continue;
|
|
34
36
|
for (const methodKey of methodKeys) {
|
|
35
37
|
if (!pathItem[methodKey]) continue;
|
|
@@ -50,7 +52,7 @@ function fromSchema(schemaId, bundled, config) {
|
|
|
50
52
|
}).join("/");
|
|
51
53
|
},
|
|
52
54
|
fromExtractedWebhook(item) {
|
|
53
|
-
const pathItem = dereferenceShallow(
|
|
55
|
+
const pathItem = dereferenceShallow(document.webhooks?.[item.name]);
|
|
54
56
|
if (!pathItem) return;
|
|
55
57
|
const operation = pathItem?.[item.method];
|
|
56
58
|
if (!operation) return;
|
|
@@ -63,7 +65,7 @@ function fromSchema(schemaId, bundled, config) {
|
|
|
63
65
|
};
|
|
64
66
|
},
|
|
65
67
|
fromExtractedOperation(item) {
|
|
66
|
-
const pathItem = dereferenceShallow(
|
|
68
|
+
const pathItem = dereferenceShallow(document.paths?.[item.path]);
|
|
67
69
|
if (!pathItem) return;
|
|
68
70
|
const operation = pathItem?.[item.method];
|
|
69
71
|
if (!operation) return;
|
|
@@ -81,7 +83,7 @@ function fromSchema(schemaId, bundled, config) {
|
|
|
81
83
|
} };
|
|
82
84
|
},
|
|
83
85
|
fromTagName(name) {
|
|
84
|
-
const tag =
|
|
86
|
+
const tag = document.tags?.find((item) => item.name === name);
|
|
85
87
|
if (!tag) return;
|
|
86
88
|
return {
|
|
87
89
|
info: tag,
|
|
@@ -21,7 +21,7 @@ function createAutoPreset(options) {
|
|
|
21
21
|
if (algorithm === "v2" && operation.operationId) return operation.operationId;
|
|
22
22
|
return path$1.join(this.routePathToFilePath(result.item.path), result.item.method.toLowerCase());
|
|
23
23
|
}
|
|
24
|
-
const hook = dereferenceShallow(this.document.webhooks[result.item.name]
|
|
24
|
+
const hook = dereferenceShallow(this.document.webhooks[result.item.name])[result.item.method];
|
|
25
25
|
if (algorithm === "v2" && hook.operationId) return hook.operationId;
|
|
26
26
|
return slugify(result.item.name);
|
|
27
27
|
};
|
|
@@ -52,7 +52,7 @@ function createAutoPreset(options) {
|
|
|
52
52
|
break;
|
|
53
53
|
}
|
|
54
54
|
case "tag": {
|
|
55
|
-
let tags = entry.type === "operation" ? dereferenceShallow(doc.paths?.[entry.item.path]
|
|
55
|
+
let tags = entry.type === "operation" ? dereferenceShallow(doc.paths?.[entry.item.path])?.[entry.item.method]?.tags : dereferenceShallow(doc.webhooks?.[entry.item.name])?.[entry.item.method]?.tags;
|
|
56
56
|
if (!tags || tags.length === 0) {
|
|
57
57
|
console.warn("When `groupBy` is set to `tag`, make sure a `tags` is defined for every operation schema.");
|
|
58
58
|
tags = ["unknown"];
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { idToTitle } from "@fumadocs/api-docs/utils/id-to-title";
|
|
2
2
|
import { dereferenceShallow } from "@fumadocs/api-docs/schema/dereference";
|
|
3
|
+
import { createMagicProxy } from "@scalar/json-magic/magic-proxy";
|
|
3
4
|
import Slugger from "github-slugger";
|
|
4
5
|
//#region src/utils/pages/to-static-data.ts
|
|
5
6
|
function toStaticData(page, doc) {
|
|
7
|
+
const proxied = createMagicProxy(doc);
|
|
6
8
|
const slugger = new Slugger();
|
|
7
9
|
const toc = [];
|
|
8
10
|
const structuredData = {
|
|
@@ -29,12 +31,12 @@ function toStaticData(page, doc) {
|
|
|
29
31
|
});
|
|
30
32
|
}
|
|
31
33
|
for (const item of page.operations ?? []) {
|
|
32
|
-
const operation = dereferenceShallow(
|
|
34
|
+
const operation = dereferenceShallow(proxied.paths?.[item.path])?.[item.method];
|
|
33
35
|
if (!operation) continue;
|
|
34
36
|
pathItem(operation, item.path);
|
|
35
37
|
}
|
|
36
38
|
for (const item of page.webhooks ?? []) {
|
|
37
|
-
const webhook = dereferenceShallow(
|
|
39
|
+
const webhook = dereferenceShallow(proxied.webhooks?.[item.name])?.[item.method];
|
|
38
40
|
if (!webhook) continue;
|
|
39
41
|
pathItem(webhook, item.name);
|
|
40
42
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { getPageProps } from "./builder.js";
|
|
2
2
|
import { toStaticData } from "./to-static-data.js";
|
|
3
3
|
import { doubleQuote } from "../../requests/string-utils.js";
|
|
4
|
-
import {
|
|
5
|
-
import { dump } from "js-yaml";
|
|
4
|
+
import { stringify } from "yaml";
|
|
6
5
|
//#region src/utils/pages/to-text.ts
|
|
7
6
|
function toText(entry, doc, options = {}) {
|
|
8
7
|
const { frontmatter, includeDescription = false } = options;
|
|
@@ -34,7 +33,10 @@ function toText(entry, doc, options = {}) {
|
|
|
34
33
|
function generateDocument(frontmatter, content, options) {
|
|
35
34
|
const { addGeneratedComment = true, imports } = options;
|
|
36
35
|
const out = [];
|
|
37
|
-
const banner =
|
|
36
|
+
const banner = stringify(frontmatter, {
|
|
37
|
+
compat: "yaml-1.1",
|
|
38
|
+
singleQuote: true
|
|
39
|
+
}).trimEnd();
|
|
38
40
|
if (banner.length > 0) out.push(`---\n${banner}\n---`);
|
|
39
41
|
if (addGeneratedComment) {
|
|
40
42
|
let commentContent = "This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again.";
|
package/dist/utils/schema.d.ts
CHANGED
package/dist/utils/schema.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { idToTitle } from "@fumadocs/api-docs/utils/id-to-title";
|
|
2
|
+
import { dereferenceShallow } from "@fumadocs/api-docs/schema/dereference";
|
|
3
|
+
import { getRaw } from "@scalar/json-magic/magic-proxy";
|
|
2
4
|
//#region src/utils/schema.ts
|
|
3
5
|
const methodKeys = [
|
|
4
6
|
"get",
|
|
@@ -18,18 +20,19 @@ function getTagDisplayName(tag) {
|
|
|
18
20
|
return idToTitle(tag.name);
|
|
19
21
|
}
|
|
20
22
|
function pickExample(value) {
|
|
21
|
-
if (value.example !== void 0) return value.example;
|
|
23
|
+
if (value.example !== void 0) return getRaw(value.example);
|
|
22
24
|
if (value.content) {
|
|
23
25
|
const type = getPreferredType(value.content);
|
|
24
|
-
const content = type ? value.content[type] : void 0;
|
|
26
|
+
const content = type ? dereferenceShallow(value.content[type]) : void 0;
|
|
25
27
|
if (type && content) {
|
|
26
|
-
const
|
|
28
|
+
const example = value.examples?.[type];
|
|
29
|
+
const out = (example !== void 0 ? getRaw(dereferenceShallow(example).value) : void 0) ?? pickExample(content);
|
|
27
30
|
if (out !== void 0) return out;
|
|
28
31
|
}
|
|
29
32
|
}
|
|
30
33
|
if (value.examples) {
|
|
31
34
|
const examples = Object.values(value.examples);
|
|
32
|
-
if (examples.length > 0) return examples[0].value;
|
|
35
|
+
if (examples.length > 0) return getRaw(dereferenceShallow(examples[0]).value);
|
|
33
36
|
}
|
|
34
37
|
}
|
|
35
38
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fumadocs-openapi",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.2.1",
|
|
4
4
|
"description": "Generate MDX docs for your OpenAPI spec",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Docs",
|
|
@@ -44,33 +44,34 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@fuma-translate/react": "^1.0.2",
|
|
47
|
-
"@fumari/json-schema-ts": "^
|
|
47
|
+
"@fumari/json-schema-ts": "^1.0.2",
|
|
48
|
+
"@scalar/json-magic": "^0.12.17",
|
|
48
49
|
"chokidar": "^5.0.0",
|
|
49
50
|
"class-variance-authority": "^0.7.1",
|
|
50
51
|
"cnfast": "^0.0.8",
|
|
51
52
|
"github-slugger": "^2.0.0",
|
|
52
53
|
"hast-util-to-jsx-runtime": "^2.3.6",
|
|
53
|
-
"
|
|
54
|
-
"lucide-react": "^1.23.0",
|
|
54
|
+
"lucide-react": "^1.24.0",
|
|
55
55
|
"remark": "^15.0.1",
|
|
56
56
|
"remark-rehype": "^11.1.2",
|
|
57
57
|
"shiki": "^4.3.1",
|
|
58
|
-
"
|
|
58
|
+
"yaml": "^2.9.0",
|
|
59
|
+
"@fumadocs/api-docs": "0.2.0",
|
|
59
60
|
"@fumari/stf": "1.1.0"
|
|
60
61
|
},
|
|
61
62
|
"devDependencies": {
|
|
62
|
-
"@scalar/api-client-react": "^2.0.
|
|
63
|
+
"@scalar/api-client-react": "^2.0.32",
|
|
63
64
|
"@scalar/openapi-upgrader": "^0.2.9",
|
|
64
65
|
"@tailwindcss/oxide": "^4.3.2",
|
|
65
|
-
"@types/node": "26.1.
|
|
66
|
+
"@types/node": "26.1.1",
|
|
66
67
|
"@types/react": "^19.2.17",
|
|
67
68
|
"fast-content-type-parse": "^3.0.0",
|
|
68
69
|
"json-schema-typed": "^8.0.2",
|
|
69
|
-
"tsdown": "0.22.
|
|
70
|
+
"tsdown": "0.22.5",
|
|
70
71
|
"xml-js": "^1.6.11",
|
|
71
|
-
"fumadocs-core": "16.11.1",
|
|
72
72
|
"tsconfig": "0.0.0",
|
|
73
|
-
"fumadocs-
|
|
73
|
+
"fumadocs-core": "16.11.5",
|
|
74
|
+
"fumadocs-ui": "npm:@fumadocs/base-ui@16.11.5"
|
|
74
75
|
},
|
|
75
76
|
"peerDependencies": {
|
|
76
77
|
"@scalar/api-client-react": "^2.0.20",
|