fumadocs-openapi 5.4.13 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -1
- package/dist/server/index.d.ts +4 -1
- package/dist/server/index.js +80 -65
- package/dist/ui/client-client-ByT1LZmz.js +317 -0
- package/dist/ui/index.d.ts +114 -3
- package/dist/ui/index.js +13 -6
- package/dist/ui/{playground-client-CbOYGXy9.js → playground-client-CcBhYwDS.js} +66 -67
- package/package.json +16 -30
- package/dist/ui/client-client-CUW5FVMv.js +0 -100
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { OpenAPIV3 } from 'openapi-types';
|
|
2
2
|
import Slugger from 'github-slugger';
|
|
3
3
|
import { ComponentType, ReactNode } from 'react';
|
|
4
|
+
import { CodeToHastOptionsCommon, CodeOptionsThemes, BuiltinTheme } from 'shiki';
|
|
4
5
|
|
|
5
6
|
interface BaseRequestField {
|
|
6
7
|
name: string;
|
|
@@ -189,6 +190,7 @@ interface RenderContext {
|
|
|
189
190
|
* Generate code samples for endpoint.
|
|
190
191
|
*/
|
|
191
192
|
generateCodeSamples?: (endpoint: EndpointSample) => Awaitable<CodeSample[]>;
|
|
193
|
+
shikiOptions?: Omit<CodeToHastOptionsCommon, 'lang'> & CodeOptionsThemes<BuiltinTheme>;
|
|
192
194
|
}
|
|
193
195
|
|
|
194
196
|
type DocumentContext = {
|
package/dist/index.js
CHANGED
|
@@ -228,6 +228,7 @@ async function generateTags(pathOrDocument, options = {}) {
|
|
|
228
228
|
});
|
|
229
229
|
}
|
|
230
230
|
}
|
|
231
|
+
const displayName = info && 'x-displayName' in info && typeof info['x-displayName'] === 'string' ? info['x-displayName'] : idToTitle(tag);
|
|
231
232
|
return {
|
|
232
233
|
tag,
|
|
233
234
|
content: generateDocument({
|
|
@@ -238,7 +239,7 @@ async function generateTags(pathOrDocument, options = {}) {
|
|
|
238
239
|
hasHead: true
|
|
239
240
|
},
|
|
240
241
|
dereferenced: document,
|
|
241
|
-
title:
|
|
242
|
+
title: displayName,
|
|
242
243
|
description: info?.description,
|
|
243
244
|
context: {
|
|
244
245
|
type: 'tag',
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { OpenAPIV3 } from 'openapi-types';
|
|
2
2
|
import { ComponentType, ReactNode, FC } from 'react';
|
|
3
3
|
import Slugger from 'github-slugger';
|
|
4
|
+
import { CodeToHastOptionsCommon, CodeOptionsThemes, BuiltinTheme } from 'shiki';
|
|
4
5
|
import { BuildPageTreeOptions } from 'fumadocs-core/source';
|
|
5
6
|
|
|
6
7
|
interface BaseRequestField {
|
|
@@ -180,9 +181,10 @@ interface RenderContext {
|
|
|
180
181
|
* Generate code samples for endpoint.
|
|
181
182
|
*/
|
|
182
183
|
generateCodeSamples?: (endpoint: EndpointSample) => Awaitable<CodeSample[]>;
|
|
184
|
+
shikiOptions?: Omit<CodeToHastOptionsCommon, 'lang'> & CodeOptionsThemes<BuiltinTheme>;
|
|
183
185
|
}
|
|
184
186
|
|
|
185
|
-
interface ApiPageProps extends Pick<RenderContext, 'generateCodeSamples' | 'generateTypeScriptSchema'> {
|
|
187
|
+
interface ApiPageProps extends Pick<RenderContext, 'generateCodeSamples' | 'generateTypeScriptSchema' | 'shikiOptions'> {
|
|
186
188
|
document: string | OpenAPIV3.Document;
|
|
187
189
|
/**
|
|
188
190
|
* An array of operations
|
|
@@ -190,6 +192,7 @@ interface ApiPageProps extends Pick<RenderContext, 'generateCodeSamples' | 'gene
|
|
|
190
192
|
operations: Operation[];
|
|
191
193
|
hasHead: boolean;
|
|
192
194
|
renderer?: Partial<Renderer>;
|
|
195
|
+
disableCache?: boolean;
|
|
193
196
|
}
|
|
194
197
|
interface Operation {
|
|
195
198
|
path: string;
|
package/dist/server/index.js
CHANGED
|
@@ -79,7 +79,7 @@ function generateSample(path, method, { baseUrl, document }) {
|
|
|
79
79
|
for (const security of getSecurities(requirements[0], document)){
|
|
80
80
|
const prefix = getSecurityPrefix(security);
|
|
81
81
|
params.push({
|
|
82
|
-
name: 'Authorization',
|
|
82
|
+
name: security.type === 'apiKey' ? security.name : 'Authorization',
|
|
83
83
|
schema: {
|
|
84
84
|
type: 'string'
|
|
85
85
|
},
|
|
@@ -274,7 +274,11 @@ print(response.text)`;
|
|
|
274
274
|
|
|
275
275
|
async function getTypescriptSchema(endpoint, code) {
|
|
276
276
|
if (code in endpoint.responses) {
|
|
277
|
-
return compile(
|
|
277
|
+
return compile(// re-running on the same schema results in error
|
|
278
|
+
// because it uses `defineProperty` to define internal references
|
|
279
|
+
// we clone the schema to fix this problem
|
|
280
|
+
structuredClone(endpoint.responses[code].schema), 'Response', {
|
|
281
|
+
$refOptions: false,
|
|
278
282
|
bannerComment: '',
|
|
279
283
|
additionalProperties: false,
|
|
280
284
|
format: true,
|
|
@@ -435,9 +439,28 @@ function idToTitle(id) {
|
|
|
435
439
|
return result.join('');
|
|
436
440
|
}
|
|
437
441
|
|
|
442
|
+
const sharedTransformers = [
|
|
443
|
+
{
|
|
444
|
+
name: 'fumadocs:pre-process',
|
|
445
|
+
line (hast) {
|
|
446
|
+
if (hast.children.length === 0) {
|
|
447
|
+
// Keep the empty lines when using grid layout
|
|
448
|
+
hast.children.push({
|
|
449
|
+
type: 'text',
|
|
450
|
+
value: ' '
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
];
|
|
456
|
+
|
|
438
457
|
const processor = remark().use(remarkGfm).use(remarkImage, {
|
|
439
458
|
useImport: false
|
|
440
|
-
}).use(remarkRehype).use(rehypeCode
|
|
459
|
+
}).use(remarkRehype).use(rehypeCode, {
|
|
460
|
+
langs: [],
|
|
461
|
+
lazy: true,
|
|
462
|
+
transformers: sharedTransformers
|
|
463
|
+
});
|
|
441
464
|
async function Markdown({ text }) {
|
|
442
465
|
const nodes = processor.parse({
|
|
443
466
|
value: text
|
|
@@ -992,22 +1015,7 @@ async function ResponseTabs({ endpoint, operation, ctx: { renderer, generateType
|
|
|
992
1015
|
};
|
|
993
1016
|
}
|
|
994
1017
|
|
|
995
|
-
|
|
996
|
-
{
|
|
997
|
-
name: 'fumadocs:pre-process',
|
|
998
|
-
line (hast) {
|
|
999
|
-
if (hast.children.length === 0) {
|
|
1000
|
-
// Keep the empty lines when using grid layout
|
|
1001
|
-
hast.children.push({
|
|
1002
|
-
type: 'text',
|
|
1003
|
-
value: ' '
|
|
1004
|
-
});
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
];
|
|
1009
|
-
|
|
1010
|
-
async function CodeBlock({ code, lang, ...options }) {
|
|
1018
|
+
async function CodeBlock({ code, lang, options, ...rest }) {
|
|
1011
1019
|
const html = await codeToHast(code, {
|
|
1012
1020
|
lang,
|
|
1013
1021
|
defaultColor: false,
|
|
@@ -1015,22 +1023,20 @@ async function CodeBlock({ code, lang, ...options }) {
|
|
|
1015
1023
|
light: 'github-light',
|
|
1016
1024
|
dark: 'github-dark'
|
|
1017
1025
|
},
|
|
1018
|
-
transformers: sharedTransformers
|
|
1026
|
+
transformers: sharedTransformers,
|
|
1027
|
+
...options
|
|
1019
1028
|
});
|
|
1020
1029
|
const codeblock = toJsxRuntime(html, {
|
|
1021
1030
|
development: false,
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
jsxs,
|
|
1031
|
+
jsx: jsx,
|
|
1032
|
+
jsxs: jsxs,
|
|
1033
|
+
Fragment: Fragment$1,
|
|
1026
1034
|
components: {
|
|
1027
|
-
// eslint-disable-next-line react/no-unstable-nested-components -- server component
|
|
1028
1035
|
pre: (props)=>/*#__PURE__*/ jsx(Base.Pre, {
|
|
1029
1036
|
...props,
|
|
1030
|
-
...
|
|
1037
|
+
...rest
|
|
1031
1038
|
})
|
|
1032
|
-
}
|
|
1033
|
-
Fragment: Fragment$1
|
|
1039
|
+
}
|
|
1034
1040
|
});
|
|
1035
1041
|
return /*#__PURE__*/ jsx(Base.CodeBlock, {
|
|
1036
1042
|
className: "my-0",
|
|
@@ -1038,47 +1044,55 @@ async function CodeBlock({ code, lang, ...options }) {
|
|
|
1038
1044
|
});
|
|
1039
1045
|
}
|
|
1040
1046
|
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
})
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1047
|
+
function createRenders(shikiOptions) {
|
|
1048
|
+
return {
|
|
1049
|
+
Root: (props)=>/*#__PURE__*/ jsx(Root, {
|
|
1050
|
+
shikiOptions: shikiOptions,
|
|
1051
|
+
...props,
|
|
1052
|
+
children: props.children
|
|
1053
|
+
}),
|
|
1054
|
+
API,
|
|
1055
|
+
APIInfo,
|
|
1056
|
+
APIExample: APIExample$1,
|
|
1057
|
+
Responses: Tabs,
|
|
1058
|
+
Response: Tab,
|
|
1059
|
+
ResponseTypes: (props)=>/*#__PURE__*/ jsx(Accordions, {
|
|
1060
|
+
type: "single",
|
|
1061
|
+
className: "!-m-4 border-none pt-2",
|
|
1062
|
+
defaultValue: "Response",
|
|
1063
|
+
children: props.children
|
|
1064
|
+
}),
|
|
1065
|
+
ResponseType: (props)=>/*#__PURE__*/ jsx(Accordion, {
|
|
1066
|
+
title: props.label,
|
|
1067
|
+
children: /*#__PURE__*/ jsx(CodeBlock, {
|
|
1068
|
+
code: props.code,
|
|
1069
|
+
lang: props.lang,
|
|
1070
|
+
options: shikiOptions
|
|
1071
|
+
})
|
|
1072
|
+
}),
|
|
1073
|
+
Property,
|
|
1074
|
+
ObjectCollapsible,
|
|
1075
|
+
Requests: (props)=>/*#__PURE__*/ jsx(Tabs, {
|
|
1076
|
+
groupId: "fumadocs_openapi_requests",
|
|
1077
|
+
...props
|
|
1078
|
+
}),
|
|
1079
|
+
Request: (props)=>/*#__PURE__*/ jsx(Tab, {
|
|
1080
|
+
value: props.name,
|
|
1081
|
+
children: /*#__PURE__*/ jsx(CodeBlock, {
|
|
1082
|
+
lang: props.language,
|
|
1083
|
+
code: props.code,
|
|
1084
|
+
options: shikiOptions
|
|
1085
|
+
})
|
|
1086
|
+
}),
|
|
1087
|
+
APIPlayground
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1076
1090
|
|
|
1077
1091
|
const cache = new Map();
|
|
1078
1092
|
async function APIPage(props) {
|
|
1079
1093
|
const { operations, hasHead = true } = props;
|
|
1080
1094
|
let document;
|
|
1081
|
-
if (typeof props.document === 'string') {
|
|
1095
|
+
if (typeof props.document === 'string' && !props.disableCache) {
|
|
1082
1096
|
const cached = cache.get(props.document);
|
|
1083
1097
|
document = cached ?? await Parser.dereference(props.document);
|
|
1084
1098
|
cache.set(props.document, document);
|
|
@@ -1107,9 +1121,10 @@ function getContext(document, options) {
|
|
|
1107
1121
|
return {
|
|
1108
1122
|
document,
|
|
1109
1123
|
renderer: {
|
|
1110
|
-
...
|
|
1124
|
+
...createRenders(options.shikiOptions),
|
|
1111
1125
|
...options.renderer
|
|
1112
1126
|
},
|
|
1127
|
+
shikiOptions: options.shikiOptions,
|
|
1113
1128
|
generateTypeScriptSchema: options.generateTypeScriptSchema,
|
|
1114
1129
|
generateCodeSamples: options.generateCodeSamples,
|
|
1115
1130
|
baseUrl: document.servers?.[0].url ?? 'https://example.com',
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { forwardRef, createElement, useContext, createContext, useState, useEffect, useMemo } from 'react';
|
|
3
|
+
import { jsx } from 'react/jsx-runtime';
|
|
4
|
+
import { cn, useCopyButton, buttonVariants } from 'fumadocs-ui/components/api';
|
|
5
|
+
import dynamic from 'next/dynamic';
|
|
6
|
+
import { useOnChange } from 'fumadocs-core/utils/use-on-change';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @license lucide-react v0.453.0 - ISC
|
|
10
|
+
*
|
|
11
|
+
* This source code is licensed under the ISC license.
|
|
12
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
13
|
+
*/ const toKebabCase = (string)=>string.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
14
|
+
const mergeClasses = (...classes)=>classes.filter((className, index, array)=>{
|
|
15
|
+
return Boolean(className) && array.indexOf(className) === index;
|
|
16
|
+
}).join(" ");
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @license lucide-react v0.453.0 - ISC
|
|
20
|
+
*
|
|
21
|
+
* This source code is licensed under the ISC license.
|
|
22
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
23
|
+
*/ var defaultAttributes = {
|
|
24
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
25
|
+
width: 24,
|
|
26
|
+
height: 24,
|
|
27
|
+
viewBox: "0 0 24 24",
|
|
28
|
+
fill: "none",
|
|
29
|
+
stroke: "currentColor",
|
|
30
|
+
strokeWidth: 2,
|
|
31
|
+
strokeLinecap: "round",
|
|
32
|
+
strokeLinejoin: "round"
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const Icon = /*#__PURE__*/ forwardRef(({ color = "currentColor", size = 24, strokeWidth = 2, absoluteStrokeWidth, className = "", children, iconNode, ...rest }, ref)=>{
|
|
36
|
+
return /*#__PURE__*/ createElement("svg", {
|
|
37
|
+
ref,
|
|
38
|
+
...defaultAttributes,
|
|
39
|
+
width: size,
|
|
40
|
+
height: size,
|
|
41
|
+
stroke: color,
|
|
42
|
+
strokeWidth: absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth,
|
|
43
|
+
className: mergeClasses("lucide", className),
|
|
44
|
+
...rest
|
|
45
|
+
}, [
|
|
46
|
+
...iconNode.map(([tag, attrs])=>/*#__PURE__*/ createElement(tag, attrs)),
|
|
47
|
+
...Array.isArray(children) ? children : [
|
|
48
|
+
children
|
|
49
|
+
]
|
|
50
|
+
]);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const createLucideIcon = (iconName, iconNode)=>{
|
|
54
|
+
const Component = /*#__PURE__*/ forwardRef(({ className, ...props }, ref)=>/*#__PURE__*/ createElement(Icon, {
|
|
55
|
+
ref,
|
|
56
|
+
iconNode,
|
|
57
|
+
className: mergeClasses(`lucide-${toKebabCase(iconName)}`, className),
|
|
58
|
+
...props
|
|
59
|
+
}));
|
|
60
|
+
Component.displayName = `${iconName}`;
|
|
61
|
+
return Component;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const Check = createLucideIcon("Check", [
|
|
65
|
+
[
|
|
66
|
+
"path",
|
|
67
|
+
{
|
|
68
|
+
d: "M20 6 9 17l-5-5",
|
|
69
|
+
key: "1gmf2c"
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
]);
|
|
73
|
+
|
|
74
|
+
const ChevronDown = createLucideIcon("ChevronDown", [
|
|
75
|
+
[
|
|
76
|
+
"path",
|
|
77
|
+
{
|
|
78
|
+
d: "m6 9 6 6 6-6",
|
|
79
|
+
key: "qrunsl"
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
]);
|
|
83
|
+
|
|
84
|
+
const ChevronUp = createLucideIcon("ChevronUp", [
|
|
85
|
+
[
|
|
86
|
+
"path",
|
|
87
|
+
{
|
|
88
|
+
d: "m18 15-6-6-6 6",
|
|
89
|
+
key: "153udz"
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
]);
|
|
93
|
+
|
|
94
|
+
const CircleCheck = createLucideIcon("CircleCheck", [
|
|
95
|
+
[
|
|
96
|
+
"circle",
|
|
97
|
+
{
|
|
98
|
+
cx: "12",
|
|
99
|
+
cy: "12",
|
|
100
|
+
r: "10",
|
|
101
|
+
key: "1mglay"
|
|
102
|
+
}
|
|
103
|
+
],
|
|
104
|
+
[
|
|
105
|
+
"path",
|
|
106
|
+
{
|
|
107
|
+
d: "m9 12 2 2 4-4",
|
|
108
|
+
key: "dzmm74"
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
const CircleX = createLucideIcon("CircleX", [
|
|
114
|
+
[
|
|
115
|
+
"circle",
|
|
116
|
+
{
|
|
117
|
+
cx: "12",
|
|
118
|
+
cy: "12",
|
|
119
|
+
r: "10",
|
|
120
|
+
key: "1mglay"
|
|
121
|
+
}
|
|
122
|
+
],
|
|
123
|
+
[
|
|
124
|
+
"path",
|
|
125
|
+
{
|
|
126
|
+
d: "m15 9-6 6",
|
|
127
|
+
key: "1uzhvr"
|
|
128
|
+
}
|
|
129
|
+
],
|
|
130
|
+
[
|
|
131
|
+
"path",
|
|
132
|
+
{
|
|
133
|
+
d: "m9 9 6 6",
|
|
134
|
+
key: "z0biqf"
|
|
135
|
+
}
|
|
136
|
+
]
|
|
137
|
+
]);
|
|
138
|
+
|
|
139
|
+
const Copy = createLucideIcon("Copy", [
|
|
140
|
+
[
|
|
141
|
+
"rect",
|
|
142
|
+
{
|
|
143
|
+
width: "14",
|
|
144
|
+
height: "14",
|
|
145
|
+
x: "8",
|
|
146
|
+
y: "8",
|
|
147
|
+
rx: "2",
|
|
148
|
+
ry: "2",
|
|
149
|
+
key: "17jyea"
|
|
150
|
+
}
|
|
151
|
+
],
|
|
152
|
+
[
|
|
153
|
+
"path",
|
|
154
|
+
{
|
|
155
|
+
d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",
|
|
156
|
+
key: "zix9uf"
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
]);
|
|
160
|
+
|
|
161
|
+
const Plus = createLucideIcon("Plus", [
|
|
162
|
+
[
|
|
163
|
+
"path",
|
|
164
|
+
{
|
|
165
|
+
d: "M5 12h14",
|
|
166
|
+
key: "1ays0h"
|
|
167
|
+
}
|
|
168
|
+
],
|
|
169
|
+
[
|
|
170
|
+
"path",
|
|
171
|
+
{
|
|
172
|
+
d: "M12 5v14",
|
|
173
|
+
key: "s699le"
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
]);
|
|
177
|
+
|
|
178
|
+
const Trash2 = createLucideIcon("Trash2", [
|
|
179
|
+
[
|
|
180
|
+
"path",
|
|
181
|
+
{
|
|
182
|
+
d: "M3 6h18",
|
|
183
|
+
key: "d0wm0j"
|
|
184
|
+
}
|
|
185
|
+
],
|
|
186
|
+
[
|
|
187
|
+
"path",
|
|
188
|
+
{
|
|
189
|
+
d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6",
|
|
190
|
+
key: "4alrt4"
|
|
191
|
+
}
|
|
192
|
+
],
|
|
193
|
+
[
|
|
194
|
+
"path",
|
|
195
|
+
{
|
|
196
|
+
d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",
|
|
197
|
+
key: "v07s0e"
|
|
198
|
+
}
|
|
199
|
+
],
|
|
200
|
+
[
|
|
201
|
+
"line",
|
|
202
|
+
{
|
|
203
|
+
x1: "10",
|
|
204
|
+
x2: "10",
|
|
205
|
+
y1: "11",
|
|
206
|
+
y2: "17",
|
|
207
|
+
key: "1uufr5"
|
|
208
|
+
}
|
|
209
|
+
],
|
|
210
|
+
[
|
|
211
|
+
"line",
|
|
212
|
+
{
|
|
213
|
+
x1: "14",
|
|
214
|
+
x2: "14",
|
|
215
|
+
y1: "11",
|
|
216
|
+
y2: "17",
|
|
217
|
+
key: "xtxkd"
|
|
218
|
+
}
|
|
219
|
+
]
|
|
220
|
+
]);
|
|
221
|
+
|
|
222
|
+
const sharedTransformers = [
|
|
223
|
+
{
|
|
224
|
+
name: 'fumadocs:pre-process',
|
|
225
|
+
line (hast) {
|
|
226
|
+
if (hast.children.length === 0) {
|
|
227
|
+
// Keep the empty lines when using grid layout
|
|
228
|
+
hast.children.push({
|
|
229
|
+
type: 'text',
|
|
230
|
+
value: ' '
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
];
|
|
236
|
+
|
|
237
|
+
const ApiContext = /*#__PURE__*/ createContext(undefined);
|
|
238
|
+
function useApiContext() {
|
|
239
|
+
const ctx = useContext(ApiContext);
|
|
240
|
+
if (!ctx) throw new Error('Component must be used under <ApiProvider />');
|
|
241
|
+
return ctx;
|
|
242
|
+
}
|
|
243
|
+
function ApiProvider({ defaultBaseUrl, shikiOptions, children }) {
|
|
244
|
+
const [baseUrl, setBaseUrl] = useState(defaultBaseUrl);
|
|
245
|
+
useEffect(()=>{
|
|
246
|
+
setBaseUrl((prev)=>localStorage.getItem('apiBaseUrl') ?? prev);
|
|
247
|
+
}, []);
|
|
248
|
+
useOnChange(baseUrl, ()=>{
|
|
249
|
+
if (baseUrl) localStorage.setItem('apiBaseUrl', baseUrl);
|
|
250
|
+
});
|
|
251
|
+
return /*#__PURE__*/ jsx(ApiContext.Provider, {
|
|
252
|
+
value: useMemo(()=>({
|
|
253
|
+
baseUrl,
|
|
254
|
+
setBaseUrl,
|
|
255
|
+
highlight: async (lang, code)=>{
|
|
256
|
+
const { codeToHast } = await import('shiki/bundle/web');
|
|
257
|
+
return codeToHast(code, {
|
|
258
|
+
lang,
|
|
259
|
+
themes: {
|
|
260
|
+
light: 'github-light',
|
|
261
|
+
dark: 'github-dark'
|
|
262
|
+
},
|
|
263
|
+
transformers: sharedTransformers,
|
|
264
|
+
defaultColor: false,
|
|
265
|
+
...shikiOptions
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}), [
|
|
269
|
+
baseUrl,
|
|
270
|
+
shikiOptions
|
|
271
|
+
]),
|
|
272
|
+
children: children
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const SchemaContext = /*#__PURE__*/ createContext(undefined);
|
|
277
|
+
function useSchemaContext() {
|
|
278
|
+
const ctx = useContext(SchemaContext);
|
|
279
|
+
if (!ctx) throw new Error('Missing provider');
|
|
280
|
+
return ctx;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const APIPlayground = dynamic(()=>import('./playground-client-CcBhYwDS.js').then((mod)=>mod.APIPlayground));
|
|
284
|
+
function Root({ children, baseUrl, className, shikiOptions, ...props }) {
|
|
285
|
+
return /*#__PURE__*/ jsx("div", {
|
|
286
|
+
className: cn('flex flex-col gap-24 text-sm text-fd-muted-foreground', className),
|
|
287
|
+
...props,
|
|
288
|
+
children: /*#__PURE__*/ jsx(ApiProvider, {
|
|
289
|
+
shikiOptions: shikiOptions,
|
|
290
|
+
defaultBaseUrl: baseUrl,
|
|
291
|
+
children: children
|
|
292
|
+
})
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
function CopyRouteButton({ className, route, ...props }) {
|
|
296
|
+
const { baseUrl } = useApiContext();
|
|
297
|
+
const [checked, onCopy] = useCopyButton(()=>{
|
|
298
|
+
void navigator.clipboard.writeText(`${baseUrl ?? ''}${route}`);
|
|
299
|
+
});
|
|
300
|
+
return /*#__PURE__*/ jsx("button", {
|
|
301
|
+
type: "button",
|
|
302
|
+
className: cn(buttonVariants({
|
|
303
|
+
color: 'ghost',
|
|
304
|
+
className
|
|
305
|
+
})),
|
|
306
|
+
onClick: onCopy,
|
|
307
|
+
"aria-label": "Copy route path",
|
|
308
|
+
...props,
|
|
309
|
+
children: checked ? /*#__PURE__*/ jsx(Check, {
|
|
310
|
+
className: "size-3"
|
|
311
|
+
}) : /*#__PURE__*/ jsx(Copy, {
|
|
312
|
+
className: "size-3"
|
|
313
|
+
})
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export { APIPlayground as A, CircleCheck as C, Plus as P, Root as R, SchemaContext as S, Trash2 as T, CircleX as a, ChevronDown as b, ChevronUp as c, Check as d, useApiContext as e, CopyRouteButton as f, useSchemaContext as u };
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -1,6 +1,66 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ReactNode, ReactElement, MutableRefObject, HTMLAttributes } from 'react';
|
|
2
|
+
import { ReactNode, ComponentType, ReactElement, MutableRefObject, HTMLAttributes } from 'react';
|
|
3
3
|
import { FieldPath, ControllerRenderProps, ControllerFieldState, UseFormStateReturn } from 'react-hook-form';
|
|
4
|
+
import { OpenAPIV3 } from 'openapi-types';
|
|
5
|
+
import Slugger from 'github-slugger';
|
|
6
|
+
import { CodeToHastOptionsCommon, CodeOptionsThemes, BuiltinTheme } from 'shiki';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Sample info of endpoint
|
|
10
|
+
*/
|
|
11
|
+
interface EndpointSample {
|
|
12
|
+
/**
|
|
13
|
+
* Request URL, including path and query parameters
|
|
14
|
+
*/
|
|
15
|
+
url: string;
|
|
16
|
+
method: string;
|
|
17
|
+
body?: {
|
|
18
|
+
schema: OpenAPIV3.SchemaObject;
|
|
19
|
+
mediaType: string;
|
|
20
|
+
sample: unknown;
|
|
21
|
+
};
|
|
22
|
+
responses: Record<string, ResponseSample>;
|
|
23
|
+
parameters: ParameterSample[];
|
|
24
|
+
}
|
|
25
|
+
interface ResponseSample {
|
|
26
|
+
mediaType: string;
|
|
27
|
+
sample: unknown;
|
|
28
|
+
schema: OpenAPIV3.SchemaObject;
|
|
29
|
+
}
|
|
30
|
+
interface ParameterSample {
|
|
31
|
+
name: string;
|
|
32
|
+
in: string;
|
|
33
|
+
schema: OpenAPIV3.SchemaObject;
|
|
34
|
+
sample: unknown;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface CodeSample {
|
|
38
|
+
lang: string;
|
|
39
|
+
label: string;
|
|
40
|
+
source: string | ((endpoint: EndpointSample) => string | undefined) | false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type Awaitable<T> = T | Promise<T>;
|
|
44
|
+
interface RenderContext {
|
|
45
|
+
renderer: Renderer;
|
|
46
|
+
document: OpenAPIV3.Document;
|
|
47
|
+
baseUrl: string;
|
|
48
|
+
slugger: Slugger;
|
|
49
|
+
/**
|
|
50
|
+
* Generate TypeScript definitions from response schema.
|
|
51
|
+
*
|
|
52
|
+
* Pass `false` to disable it.
|
|
53
|
+
*
|
|
54
|
+
* @param endpoint - the API endpoint
|
|
55
|
+
* @param code - status code
|
|
56
|
+
*/
|
|
57
|
+
generateTypeScriptSchema?: ((endpoint: EndpointSample, code: string) => Awaitable<string>) | false;
|
|
58
|
+
/**
|
|
59
|
+
* Generate code samples for endpoint.
|
|
60
|
+
*/
|
|
61
|
+
generateCodeSamples?: (endpoint: EndpointSample) => Awaitable<CodeSample[]>;
|
|
62
|
+
shikiOptions?: Omit<CodeToHastOptionsCommon, 'lang'> & CodeOptionsThemes<BuiltinTheme>;
|
|
63
|
+
}
|
|
4
64
|
|
|
5
65
|
interface BaseRequestField {
|
|
6
66
|
name: string;
|
|
@@ -59,6 +119,14 @@ interface APIPlaygroundProps {
|
|
|
59
119
|
schemas: Record<string, RequestSchema>;
|
|
60
120
|
}
|
|
61
121
|
|
|
122
|
+
interface ResponsesProps {
|
|
123
|
+
items: string[];
|
|
124
|
+
children: ReactNode;
|
|
125
|
+
}
|
|
126
|
+
interface ResponseProps {
|
|
127
|
+
value: string;
|
|
128
|
+
children: ReactNode;
|
|
129
|
+
}
|
|
62
130
|
interface APIInfoProps {
|
|
63
131
|
method: string;
|
|
64
132
|
route: string;
|
|
@@ -71,10 +139,51 @@ interface PropertyProps {
|
|
|
71
139
|
deprecated?: boolean;
|
|
72
140
|
children?: ReactNode;
|
|
73
141
|
}
|
|
142
|
+
interface ObjectCollapsibleProps {
|
|
143
|
+
name: string;
|
|
144
|
+
children: ReactNode;
|
|
145
|
+
}
|
|
146
|
+
interface RequestProps {
|
|
147
|
+
language: string;
|
|
148
|
+
name: string;
|
|
149
|
+
code: string;
|
|
150
|
+
}
|
|
151
|
+
interface ResponseTypeProps {
|
|
152
|
+
lang: string;
|
|
153
|
+
code: string;
|
|
154
|
+
label: string;
|
|
155
|
+
}
|
|
74
156
|
interface RootProps {
|
|
75
157
|
baseUrl?: string;
|
|
76
158
|
children: ReactNode;
|
|
77
159
|
}
|
|
160
|
+
interface Renderer {
|
|
161
|
+
Root: ComponentType<RootProps>;
|
|
162
|
+
API: ComponentType<{
|
|
163
|
+
children: ReactNode;
|
|
164
|
+
}>;
|
|
165
|
+
APIInfo: ComponentType<APIInfoProps>;
|
|
166
|
+
APIExample: ComponentType<{
|
|
167
|
+
children: ReactNode;
|
|
168
|
+
}>;
|
|
169
|
+
Responses: ComponentType<ResponsesProps>;
|
|
170
|
+
Response: ComponentType<ResponseProps>;
|
|
171
|
+
Requests: ComponentType<{
|
|
172
|
+
items: string[];
|
|
173
|
+
children: ReactNode;
|
|
174
|
+
}>;
|
|
175
|
+
Request: ComponentType<RequestProps>;
|
|
176
|
+
ResponseTypes: ComponentType<{
|
|
177
|
+
children: ReactNode;
|
|
178
|
+
}>;
|
|
179
|
+
ResponseType: ComponentType<ResponseTypeProps>;
|
|
180
|
+
/**
|
|
181
|
+
* Collapsible to show object schemas
|
|
182
|
+
*/
|
|
183
|
+
ObjectCollapsible: ComponentType<ObjectCollapsibleProps>;
|
|
184
|
+
Property: ComponentType<PropertyProps>;
|
|
185
|
+
APIPlayground: ComponentType<APIPlaygroundProps>;
|
|
186
|
+
}
|
|
78
187
|
|
|
79
188
|
interface FormValues {
|
|
80
189
|
authorization: string;
|
|
@@ -117,14 +226,16 @@ declare const APIPlayground: react.ComponentType<APIPlaygroundProps & {
|
|
|
117
226
|
body?: CustomField<"body", RequestSchema>;
|
|
118
227
|
};
|
|
119
228
|
} & HTMLAttributes<HTMLFormElement>>;
|
|
120
|
-
declare function Root({ children, baseUrl, className, ...props }: RootProps &
|
|
229
|
+
declare function Root({ children, baseUrl, className, shikiOptions, ...props }: RootProps & {
|
|
230
|
+
shikiOptions: RenderContext['shikiOptions'];
|
|
231
|
+
} & HTMLAttributes<HTMLDivElement>): ReactNode;
|
|
121
232
|
|
|
122
233
|
declare function APIInfo({ children, className, route, badgeClassname, method, ...props }: APIInfoProps & HTMLAttributes<HTMLDivElement> & {
|
|
123
234
|
badgeClassname?: string;
|
|
124
235
|
}): React.ReactElement;
|
|
125
236
|
declare function API({ className, children, ...props }: HTMLAttributes<HTMLDivElement>): React.ReactElement;
|
|
126
237
|
declare function Property({ name, type, required, deprecated, children, }: PropertyProps): React.ReactElement;
|
|
127
|
-
declare function APIExample(
|
|
238
|
+
declare function APIExample(props: HTMLAttributes<HTMLDivElement>): React.ReactElement;
|
|
128
239
|
declare function ObjectCollapsible(props: {
|
|
129
240
|
name: string;
|
|
130
241
|
children: ReactNode;
|
package/dist/ui/index.js
CHANGED
|
@@ -3,8 +3,8 @@ import { cn } from 'fumadocs-ui/components/api';
|
|
|
3
3
|
import { Fragment } from 'react';
|
|
4
4
|
import { Accordions, Accordion } from 'fumadocs-ui/components/accordion';
|
|
5
5
|
import { cva } from 'class-variance-authority';
|
|
6
|
-
import {
|
|
7
|
-
export { A as APIPlayground, R as Root, u as useSchemaContext } from './client-client-
|
|
6
|
+
import { f as CopyRouteButton } from './client-client-ByT1LZmz.js';
|
|
7
|
+
export { A as APIPlayground, R as Root, u as useSchemaContext } from './client-client-ByT1LZmz.js';
|
|
8
8
|
|
|
9
9
|
const badgeVariants = cva('rounded border px-1.5 py-1 text-xs font-medium leading-[12px]', {
|
|
10
10
|
variants: {
|
|
@@ -59,7 +59,10 @@ function APIInfo({ children, className, route, badgeClassname, method = 'GET', .
|
|
|
59
59
|
...props,
|
|
60
60
|
children: [
|
|
61
61
|
/*#__PURE__*/ jsxs("div", {
|
|
62
|
-
className: cn('sticky top-
|
|
62
|
+
className: cn('sticky top-[calc(var(--fd-api-info-top)+36px)] z-20 mb-4 flex flex-row items-center gap-2 rounded-lg border bg-fd-card px-3 py-2 md:top-[var(--fd-api-info-top)]'),
|
|
63
|
+
style: {
|
|
64
|
+
'--fd-api-info-top': 'calc(var(--fd-nav-height) + var(--fd-banner-height) + 4px)'
|
|
65
|
+
},
|
|
63
66
|
children: [
|
|
64
67
|
/*#__PURE__*/ jsx("span", {
|
|
65
68
|
className: cn(badgeVariants({
|
|
@@ -122,11 +125,15 @@ function Property({ name, type, required, deprecated, children }) {
|
|
|
122
125
|
]
|
|
123
126
|
});
|
|
124
127
|
}
|
|
125
|
-
function APIExample(
|
|
128
|
+
function APIExample(props) {
|
|
126
129
|
return /*#__PURE__*/ jsx("div", {
|
|
127
|
-
className: cn('prose-no-margin md:sticky md:top-12 lg:top-1 xl:w-[400px]', className),
|
|
128
130
|
...props,
|
|
129
|
-
|
|
131
|
+
className: cn('prose-no-margin md:sticky md:top-[var(--fd-api-info-top)] xl:w-[400px]', props.className),
|
|
132
|
+
style: {
|
|
133
|
+
'--fd-api-info-top': 'calc(var(--fd-nav-height) + var(--fd-banner-height) + 40px)',
|
|
134
|
+
...props.style
|
|
135
|
+
},
|
|
136
|
+
children: props.children
|
|
130
137
|
});
|
|
131
138
|
}
|
|
132
139
|
function ObjectCollapsible(props) {
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import * as React from 'react';
|
|
4
|
-
import { forwardRef, useId, createContext, useContext, useState, useCallback,
|
|
4
|
+
import { forwardRef, useId, createContext, useContext, useState, useCallback, useEffect, Fragment as Fragment$1, useRef, useMemo } from 'react';
|
|
5
5
|
import { FormProvider, Controller, useFormContext, useFieldArray, useForm, useWatch } from 'react-hook-form';
|
|
6
|
-
import useSWRImmutable from 'swr/immutable';
|
|
7
6
|
import { Accordions, Accordion } from 'fumadocs-ui/components/accordion';
|
|
8
7
|
import { cn, buttonVariants } from 'fumadocs-ui/components/api';
|
|
9
|
-
import { u as useSchemaContext,
|
|
8
|
+
import { C as CircleCheck, a as CircleX, b as ChevronDown, c as ChevronUp, d as Check, u as useSchemaContext, T as Trash2, P as Plus, e as useApiContext, S as SchemaContext } from './client-client-ByT1LZmz.js';
|
|
10
9
|
import { Slot } from '@radix-ui/react-slot';
|
|
11
10
|
import { cva } from 'class-variance-authority';
|
|
12
|
-
import { CircleCheckIcon, CircleXIcon, ChevronDown, ChevronUp, Check, Trash2, Plus } from 'lucide-react';
|
|
13
11
|
import { useOnChange } from 'fumadocs-core/utils/use-on-change';
|
|
14
12
|
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
15
13
|
import * as Base from 'fumadocs-ui/components/codeblock';
|
|
14
|
+
import { toJsxRuntime } from 'hast-util-to-jsx-runtime';
|
|
16
15
|
|
|
17
16
|
const Form = FormProvider;
|
|
18
17
|
const FormFieldContext = /*#__PURE__*/ createContext({
|
|
@@ -124,7 +123,9 @@ FormDescription.displayName = 'FormDescription';
|
|
|
124
123
|
formData.append(key, item);
|
|
125
124
|
}
|
|
126
125
|
}
|
|
127
|
-
|
|
126
|
+
if (prop && !(prop instanceof File)) {
|
|
127
|
+
formData.set(key, JSON.stringify(prop));
|
|
128
|
+
}
|
|
128
129
|
}
|
|
129
130
|
return formData;
|
|
130
131
|
}
|
|
@@ -192,27 +193,27 @@ const statusMap = {
|
|
|
192
193
|
400: {
|
|
193
194
|
description: 'Bad Request',
|
|
194
195
|
color: 'text-red-500',
|
|
195
|
-
icon:
|
|
196
|
+
icon: CircleX
|
|
196
197
|
},
|
|
197
198
|
401: {
|
|
198
199
|
description: 'Unauthorized',
|
|
199
200
|
color: 'text-red-500',
|
|
200
|
-
icon:
|
|
201
|
+
icon: CircleX
|
|
201
202
|
},
|
|
202
203
|
403: {
|
|
203
204
|
description: 'Forbidden',
|
|
204
205
|
color: 'text-red-500',
|
|
205
|
-
icon:
|
|
206
|
+
icon: CircleX
|
|
206
207
|
},
|
|
207
208
|
404: {
|
|
208
209
|
description: 'Not Found',
|
|
209
210
|
color: 'text-fd-muted-foreground',
|
|
210
|
-
icon:
|
|
211
|
+
icon: CircleX
|
|
211
212
|
},
|
|
212
213
|
500: {
|
|
213
214
|
description: 'Internal Server Error',
|
|
214
215
|
color: 'text-red-500',
|
|
215
|
-
icon:
|
|
216
|
+
icon: CircleX
|
|
216
217
|
}
|
|
217
218
|
};
|
|
218
219
|
function getStatusInfo(status) {
|
|
@@ -223,20 +224,20 @@ function getStatusInfo(status) {
|
|
|
223
224
|
return {
|
|
224
225
|
description: 'Successful',
|
|
225
226
|
color: 'text-green-500',
|
|
226
|
-
icon:
|
|
227
|
+
icon: CircleCheck
|
|
227
228
|
};
|
|
228
229
|
}
|
|
229
230
|
if (status >= 400) {
|
|
230
231
|
return {
|
|
231
232
|
description: 'Error',
|
|
232
233
|
color: 'text-red-500',
|
|
233
|
-
icon:
|
|
234
|
+
icon: CircleX
|
|
234
235
|
};
|
|
235
236
|
}
|
|
236
237
|
return {
|
|
237
238
|
description: 'No Description',
|
|
238
239
|
color: 'text-fd-muted-foreground',
|
|
239
|
-
icon:
|
|
240
|
+
icon: CircleX
|
|
240
241
|
};
|
|
241
242
|
}
|
|
242
243
|
|
|
@@ -809,56 +810,43 @@ function ArrayInput({ fieldName, field, ...props }) {
|
|
|
809
810
|
});
|
|
810
811
|
}
|
|
811
812
|
|
|
812
|
-
|
|
813
|
-
{
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
defaultColor: false,
|
|
835
|
-
themes: {
|
|
836
|
-
light: 'github-light',
|
|
837
|
-
dark: 'github-dark'
|
|
838
|
-
},
|
|
839
|
-
transformers: sharedTransformers
|
|
813
|
+
function CodeBlock({ code, lang = 'json' }) {
|
|
814
|
+
const { highlight } = useApiContext();
|
|
815
|
+
const [rendered, setRendered] = useState(/*#__PURE__*/ jsx(Base.Pre, {
|
|
816
|
+
className: "max-h-[288px]",
|
|
817
|
+
children: code
|
|
818
|
+
}));
|
|
819
|
+
useEffect(()=>{
|
|
820
|
+
void highlight(lang, code).then((res)=>{
|
|
821
|
+
const output = toJsxRuntime(res, {
|
|
822
|
+
jsx,
|
|
823
|
+
jsxs,
|
|
824
|
+
development: false,
|
|
825
|
+
Fragment: Fragment$1,
|
|
826
|
+
components: {
|
|
827
|
+
pre: (props)=>/*#__PURE__*/ jsx(Base.Pre, {
|
|
828
|
+
className: "max-h-[288px]",
|
|
829
|
+
...props,
|
|
830
|
+
children: props.children
|
|
831
|
+
})
|
|
832
|
+
}
|
|
833
|
+
});
|
|
834
|
+
setRendered(output);
|
|
840
835
|
});
|
|
841
|
-
setHtml(themedHtml);
|
|
842
836
|
}, [
|
|
843
837
|
code,
|
|
844
|
-
|
|
845
|
-
|
|
838
|
+
highlight,
|
|
839
|
+
lang
|
|
846
840
|
]);
|
|
847
841
|
return /*#__PURE__*/ jsx(Base.CodeBlock, {
|
|
848
842
|
className: "my-0",
|
|
849
|
-
children:
|
|
850
|
-
...props,
|
|
851
|
-
dangerouslySetInnerHTML: {
|
|
852
|
-
__html: html
|
|
853
|
-
}
|
|
854
|
-
})
|
|
843
|
+
children: rendered
|
|
855
844
|
});
|
|
856
845
|
}
|
|
857
846
|
|
|
858
847
|
function APIPlayground({ route, method = 'GET', bodyType, authorization, path = [], header = [], query = [], body, fields = {}, schemas }) {
|
|
859
848
|
const { baseUrl } = useApiContext();
|
|
860
849
|
const dynamicRef = useRef(new Map());
|
|
861
|
-
const [input, setInput] = useState();
|
|
862
850
|
const form = useForm({
|
|
863
851
|
defaultValues: {
|
|
864
852
|
authorization: authorization?.defaultValue,
|
|
@@ -868,18 +856,10 @@ function APIPlayground({ route, method = 'GET', bodyType, authorization, path =
|
|
|
868
856
|
body: body ? getDefaultValue(body, schemas) : undefined
|
|
869
857
|
}
|
|
870
858
|
});
|
|
871
|
-
const testQuery =
|
|
872
|
-
baseUrl,
|
|
873
|
-
route,
|
|
874
|
-
method,
|
|
875
|
-
input,
|
|
876
|
-
bodyType
|
|
877
|
-
] : null, async ()=>{
|
|
878
|
-
if (!input) return;
|
|
859
|
+
const testQuery = useQuery(async (input)=>{
|
|
879
860
|
const url = new URL(`${baseUrl ?? window.location.origin}${createUrlFromInput(route, input.path, input.query)}`);
|
|
880
|
-
const headers = new Headers(
|
|
881
|
-
|
|
882
|
-
});
|
|
861
|
+
const headers = new Headers();
|
|
862
|
+
if (bodyType !== 'form-data') headers.append('Content-Type', 'application/json');
|
|
883
863
|
if (input.authorization) {
|
|
884
864
|
headers.append('Authorization', input.authorization);
|
|
885
865
|
}
|
|
@@ -898,8 +878,6 @@ function APIPlayground({ route, method = 'GET', bodyType, authorization, path =
|
|
|
898
878
|
status: response.status,
|
|
899
879
|
data
|
|
900
880
|
};
|
|
901
|
-
}, {
|
|
902
|
-
shouldRetryOnError: false
|
|
903
881
|
});
|
|
904
882
|
useEffect(()=>{
|
|
905
883
|
if (!authorization) return;
|
|
@@ -916,7 +894,7 @@ function APIPlayground({ route, method = 'GET', bodyType, authorization, path =
|
|
|
916
894
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- mounted only once
|
|
917
895
|
}, []);
|
|
918
896
|
const onSubmit = form.handleSubmit((value)=>{
|
|
919
|
-
|
|
897
|
+
testQuery.start(value);
|
|
920
898
|
});
|
|
921
899
|
function renderCustomField(fieldName, info, field, key) {
|
|
922
900
|
if (field) {
|
|
@@ -1058,11 +1036,32 @@ function ResultDisplay({ data }) {
|
|
|
1058
1036
|
children: data.status
|
|
1059
1037
|
}),
|
|
1060
1038
|
data.data ? /*#__PURE__*/ jsx(CodeBlock, {
|
|
1061
|
-
code: JSON.stringify(data.data, null, 2)
|
|
1062
|
-
className: "max-h-[288px]"
|
|
1039
|
+
code: JSON.stringify(data.data, null, 2)
|
|
1063
1040
|
}) : null
|
|
1064
1041
|
]
|
|
1065
1042
|
});
|
|
1066
1043
|
}
|
|
1044
|
+
function useQuery(fn) {
|
|
1045
|
+
const [loading, setLoading] = useState(false);
|
|
1046
|
+
const [data, setData] = useState();
|
|
1047
|
+
return useMemo(()=>({
|
|
1048
|
+
isLoading: loading,
|
|
1049
|
+
data,
|
|
1050
|
+
start (input) {
|
|
1051
|
+
setLoading(true);
|
|
1052
|
+
void fn(input).then((res)=>{
|
|
1053
|
+
setData(res);
|
|
1054
|
+
}).catch(()=>{
|
|
1055
|
+
setData(undefined);
|
|
1056
|
+
}).finally(()=>{
|
|
1057
|
+
setLoading(false);
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
}), [
|
|
1061
|
+
data,
|
|
1062
|
+
fn,
|
|
1063
|
+
loading
|
|
1064
|
+
]);
|
|
1065
|
+
}
|
|
1067
1066
|
|
|
1068
1067
|
export { APIPlayground };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fumadocs-openapi",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.5.0",
|
|
4
4
|
"description": "Generate MDX docs for your OpenAPI spec",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"NextJs",
|
|
@@ -27,55 +27,41 @@
|
|
|
27
27
|
},
|
|
28
28
|
"main": "./dist/index.js",
|
|
29
29
|
"types": "./dist/index.d.ts",
|
|
30
|
-
"typesVersions": {
|
|
31
|
-
"*": {
|
|
32
|
-
".": [
|
|
33
|
-
"./dist/index.d.ts"
|
|
34
|
-
],
|
|
35
|
-
"ui": [
|
|
36
|
-
"./dist/ui/index.d.ts"
|
|
37
|
-
],
|
|
38
|
-
"server": [
|
|
39
|
-
"./dist/server/index.d.ts"
|
|
40
|
-
]
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
30
|
"files": [
|
|
44
31
|
"dist"
|
|
45
32
|
],
|
|
46
33
|
"dependencies": {
|
|
47
|
-
"@apidevtools/json-schema-ref-parser": "^11.7.
|
|
48
|
-
"@fumari/json-schema-to-typescript": "^1.1.
|
|
49
|
-
"@radix-ui/react-select": "^2.1.
|
|
34
|
+
"@apidevtools/json-schema-ref-parser": "^11.7.2",
|
|
35
|
+
"@fumari/json-schema-to-typescript": "^1.1.1",
|
|
36
|
+
"@radix-ui/react-select": "^2.1.2",
|
|
50
37
|
"@radix-ui/react-slot": "^1.1.0",
|
|
51
38
|
"class-variance-authority": "^0.7.0",
|
|
52
39
|
"fast-glob": "^3.3.1",
|
|
53
40
|
"github-slugger": "^2.0.0",
|
|
54
|
-
"hast-util-to-jsx-runtime": "^2.3.
|
|
41
|
+
"hast-util-to-jsx-runtime": "^2.3.2",
|
|
55
42
|
"js-yaml": "^4.1.0",
|
|
56
|
-
"lucide-react": "^0.438.0",
|
|
57
43
|
"openapi-sampler": "^1.5.1",
|
|
58
|
-
"react-hook-form": "^7.53.
|
|
44
|
+
"react-hook-form": "^7.53.1",
|
|
59
45
|
"remark": "^15.0.1",
|
|
60
|
-
"remark-rehype": "^11.1.
|
|
61
|
-
"shiki": "^1.
|
|
62
|
-
"
|
|
63
|
-
"fumadocs-
|
|
64
|
-
"fumadocs-ui": "13.4.9"
|
|
46
|
+
"remark-rehype": "^11.1.1",
|
|
47
|
+
"shiki": "^1.22.0",
|
|
48
|
+
"fumadocs-core": "14.0.0",
|
|
49
|
+
"fumadocs-ui": "14.0.0"
|
|
65
50
|
},
|
|
66
51
|
"devDependencies": {
|
|
67
52
|
"@types/js-yaml": "^4.0.9",
|
|
68
|
-
"@types/node": "22.
|
|
53
|
+
"@types/node": "22.7.7",
|
|
69
54
|
"@types/openapi-sampler": "^1.0.3",
|
|
70
|
-
"@types/react": "^18.3.
|
|
71
|
-
"bunchee": "^5.
|
|
72
|
-
"
|
|
55
|
+
"@types/react": "^18.3.11",
|
|
56
|
+
"bunchee": "^5.5.1",
|
|
57
|
+
"lucide-react": "^0.453.0",
|
|
58
|
+
"next": "15.0.0",
|
|
73
59
|
"openapi-types": "^12.1.3",
|
|
74
60
|
"eslint-config-custom": "0.0.0",
|
|
75
61
|
"tsconfig": "0.0.0"
|
|
76
62
|
},
|
|
77
63
|
"peerDependencies": {
|
|
78
|
-
"next": "
|
|
64
|
+
"next": "14.x.x || 15.x.x",
|
|
79
65
|
"react": ">= 18",
|
|
80
66
|
"react-dom": ">= 18"
|
|
81
67
|
},
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { useContext, createContext, useState, useEffect } from 'react';
|
|
3
|
-
import { jsx } from 'react/jsx-runtime';
|
|
4
|
-
import { Check, Copy } from 'lucide-react';
|
|
5
|
-
import { cn, useCopyButton, buttonVariants } from 'fumadocs-ui/components/api';
|
|
6
|
-
import dynamic from 'next/dynamic';
|
|
7
|
-
|
|
8
|
-
const ApiContext = /*#__PURE__*/ createContext({
|
|
9
|
-
baseUrl: undefined,
|
|
10
|
-
setBaseUrl: ()=>undefined,
|
|
11
|
-
highlighter: null
|
|
12
|
-
});
|
|
13
|
-
function useApiContext() {
|
|
14
|
-
return useContext(ApiContext);
|
|
15
|
-
}
|
|
16
|
-
async function initHighlighter() {
|
|
17
|
-
const { createHighlighterCore } = await import('shiki/core');
|
|
18
|
-
const getWasm = await import('shiki/wasm');
|
|
19
|
-
return createHighlighterCore({
|
|
20
|
-
themes: [
|
|
21
|
-
import('shiki/themes/github-light.mjs'),
|
|
22
|
-
import('shiki/themes/github-dark.mjs')
|
|
23
|
-
],
|
|
24
|
-
langs: [
|
|
25
|
-
import('shiki/langs/json.mjs')
|
|
26
|
-
],
|
|
27
|
-
loadWasm: getWasm
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
let highlighterInstance;
|
|
31
|
-
function ApiProvider({ defaultBaseUrl, children }) {
|
|
32
|
-
const [highlighter, setHighlighter] = useState(null);
|
|
33
|
-
const [baseUrl, setBaseUrl] = useState(defaultBaseUrl);
|
|
34
|
-
useEffect(()=>{
|
|
35
|
-
setBaseUrl((prev)=>localStorage.getItem('apiBaseUrl') ?? prev);
|
|
36
|
-
if (highlighterInstance) {
|
|
37
|
-
setHighlighter(highlighterInstance);
|
|
38
|
-
} else {
|
|
39
|
-
void initHighlighter().then((res)=>{
|
|
40
|
-
highlighterInstance = res;
|
|
41
|
-
setHighlighter(res);
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
}, []);
|
|
45
|
-
useEffect(()=>{
|
|
46
|
-
if (baseUrl) localStorage.setItem('apiBaseUrl', baseUrl);
|
|
47
|
-
}, [
|
|
48
|
-
baseUrl
|
|
49
|
-
]);
|
|
50
|
-
return /*#__PURE__*/ jsx(ApiContext.Provider, {
|
|
51
|
-
value: {
|
|
52
|
-
baseUrl,
|
|
53
|
-
setBaseUrl,
|
|
54
|
-
highlighter
|
|
55
|
-
},
|
|
56
|
-
children: children
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const SchemaContext = /*#__PURE__*/ createContext(undefined);
|
|
61
|
-
function useSchemaContext() {
|
|
62
|
-
const ctx = useContext(SchemaContext);
|
|
63
|
-
if (!ctx) throw new Error('Missing provider');
|
|
64
|
-
return ctx;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const APIPlayground = dynamic(()=>import('./playground-client-CbOYGXy9.js').then((mod)=>mod.APIPlayground));
|
|
68
|
-
function Root({ children, baseUrl, className, ...props }) {
|
|
69
|
-
return /*#__PURE__*/ jsx("div", {
|
|
70
|
-
className: cn('flex flex-col gap-24 text-sm text-fd-muted-foreground', className),
|
|
71
|
-
...props,
|
|
72
|
-
children: /*#__PURE__*/ jsx(ApiProvider, {
|
|
73
|
-
defaultBaseUrl: baseUrl,
|
|
74
|
-
children: children
|
|
75
|
-
})
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
function CopyRouteButton({ className, route, ...props }) {
|
|
79
|
-
const { baseUrl } = useApiContext();
|
|
80
|
-
const [checked, onCopy] = useCopyButton(()=>{
|
|
81
|
-
void navigator.clipboard.writeText(`${baseUrl ?? ''}${route}`);
|
|
82
|
-
});
|
|
83
|
-
return /*#__PURE__*/ jsx("button", {
|
|
84
|
-
type: "button",
|
|
85
|
-
className: cn(buttonVariants({
|
|
86
|
-
color: 'ghost',
|
|
87
|
-
className
|
|
88
|
-
})),
|
|
89
|
-
onClick: onCopy,
|
|
90
|
-
"aria-label": "Copy route path",
|
|
91
|
-
...props,
|
|
92
|
-
children: checked ? /*#__PURE__*/ jsx(Check, {
|
|
93
|
-
className: "size-3"
|
|
94
|
-
}) : /*#__PURE__*/ jsx(Copy, {
|
|
95
|
-
className: "size-3"
|
|
96
|
-
})
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export { APIPlayground as A, CopyRouteButton as C, Root as R, SchemaContext as S, useApiContext as a, useSchemaContext as u };
|