fumadocs-openapi 4.0.5 → 4.1.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/{chunk-RSFOKBAM.js → chunk-BYW6XCQ2.js} +14 -1
- package/dist/index.d.ts +32 -23
- package/dist/index.js +13 -6
- package/dist/{playground-7UGUJXDH.js → playground-EVJUDTB3.js} +123 -95
- package/dist/ui/index.d.ts +45 -4
- package/dist/ui/index.js +13 -11
- package/package.json +5 -5
|
@@ -104,6 +104,17 @@ function ApiProvider({
|
|
|
104
104
|
return /* @__PURE__ */ jsx(ApiContext.Provider, { value: { baseUrl, setBaseUrl, highlighter }, children });
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
// src/ui/contexts/schema.tsx
|
|
108
|
+
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
109
|
+
var SchemaContext = createContext2(
|
|
110
|
+
void 0
|
|
111
|
+
);
|
|
112
|
+
function useSchemaContext() {
|
|
113
|
+
const ctx = useContext2(SchemaContext);
|
|
114
|
+
if (!ctx) throw new Error("Missing provider");
|
|
115
|
+
return ctx;
|
|
116
|
+
}
|
|
117
|
+
|
|
107
118
|
export {
|
|
108
119
|
badgeVariants,
|
|
109
120
|
getBadgeColor,
|
|
@@ -111,5 +122,7 @@ export {
|
|
|
111
122
|
getDefaultValues,
|
|
112
123
|
resolve,
|
|
113
124
|
useApiContext,
|
|
114
|
-
ApiProvider
|
|
125
|
+
ApiProvider,
|
|
126
|
+
SchemaContext,
|
|
127
|
+
useSchemaContext
|
|
115
128
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -51,6 +51,25 @@ interface Renderer {
|
|
|
51
51
|
|
|
52
52
|
declare const defaultRenderer: Renderer;
|
|
53
53
|
|
|
54
|
+
interface Endpoint {
|
|
55
|
+
/**
|
|
56
|
+
* URL, including path and query parameters
|
|
57
|
+
*/
|
|
58
|
+
url: string;
|
|
59
|
+
method: string;
|
|
60
|
+
body?: unknown;
|
|
61
|
+
responses: Record<string, Response>;
|
|
62
|
+
parameters: Parameter[];
|
|
63
|
+
}
|
|
64
|
+
interface Response {
|
|
65
|
+
schema: OpenAPIV3.SchemaObject;
|
|
66
|
+
}
|
|
67
|
+
interface Parameter {
|
|
68
|
+
name: string;
|
|
69
|
+
in: string;
|
|
70
|
+
schema: OpenAPIV3.SchemaObject;
|
|
71
|
+
}
|
|
72
|
+
|
|
54
73
|
interface CodeSample {
|
|
55
74
|
lang: string;
|
|
56
75
|
label: string;
|
|
@@ -67,33 +86,27 @@ interface MethodInformation extends OpenAPIV3.OperationObject {
|
|
|
67
86
|
parameters: OpenAPIV3.ParameterObject[];
|
|
68
87
|
method: string;
|
|
69
88
|
}
|
|
89
|
+
type Awaitable<T> = T | Promise<T>;
|
|
70
90
|
interface RenderContext {
|
|
71
91
|
renderer: Renderer;
|
|
72
92
|
document: OpenAPIV3.Document;
|
|
73
93
|
baseUrl: string;
|
|
74
|
-
generateCodeSamples?: (endpoint: Endpoint) => CodeSample[];
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
interface Endpoint {
|
|
78
94
|
/**
|
|
79
|
-
*
|
|
95
|
+
* Generate TypeScript definitions from response schema.
|
|
96
|
+
*
|
|
97
|
+
* Pass `false` to disable it.
|
|
98
|
+
*
|
|
99
|
+
* @param endpoint - the API endpoint
|
|
100
|
+
* @param code - status code
|
|
80
101
|
*/
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
interface Response {
|
|
88
|
-
schema: OpenAPIV3.SchemaObject;
|
|
89
|
-
}
|
|
90
|
-
interface Parameter {
|
|
91
|
-
name: string;
|
|
92
|
-
in: string;
|
|
93
|
-
schema: OpenAPIV3.SchemaObject;
|
|
102
|
+
generateTypeScriptSchema?: ((endpoint: Endpoint, code: string) => Awaitable<string>) | false;
|
|
103
|
+
/**
|
|
104
|
+
* Generate code samples for endpoint.
|
|
105
|
+
*/
|
|
106
|
+
generateCodeSamples?: (endpoint: Endpoint) => Awaitable<CodeSample[]>;
|
|
94
107
|
}
|
|
95
108
|
|
|
96
|
-
interface GenerateOptions {
|
|
109
|
+
interface GenerateOptions extends Pick<RenderContext, 'generateCodeSamples' | 'generateTypeScriptSchema'> {
|
|
97
110
|
/**
|
|
98
111
|
* The imports of your MDX components.
|
|
99
112
|
*
|
|
@@ -109,10 +122,6 @@ interface GenerateOptions {
|
|
|
109
122
|
* A `full: true` property will be added by default.
|
|
110
123
|
*/
|
|
111
124
|
frontmatter?: (title: string, description: string | undefined) => Record<string, unknown>;
|
|
112
|
-
/**
|
|
113
|
-
* Generate code samples for endpoint
|
|
114
|
-
*/
|
|
115
|
-
generateCodeSamples?: (endpoint: Endpoint) => CodeSample[];
|
|
116
125
|
renderer?: Partial<Renderer>;
|
|
117
126
|
}
|
|
118
127
|
interface GenerateTagOutput {
|
package/dist/index.js
CHANGED
|
@@ -621,6 +621,7 @@ async function renderOperation(path, method, ctx, noTitle = false) {
|
|
|
621
621
|
level++;
|
|
622
622
|
}
|
|
623
623
|
if (method.description) info.push(p(method.description));
|
|
624
|
+
info.push(renderPlayground(path, method, ctx));
|
|
624
625
|
if (security) {
|
|
625
626
|
info.push(heading(level, "Authorization"));
|
|
626
627
|
info.push(getAuthSection(security, ctx));
|
|
@@ -676,7 +677,6 @@ async function renderOperation(path, method, ctx, noTitle = false) {
|
|
|
676
677
|
info.push(heading(level, group), ...parameters);
|
|
677
678
|
}
|
|
678
679
|
info.push(getResponseTable(method));
|
|
679
|
-
info.push(renderPlayground(path, method, ctx));
|
|
680
680
|
const samples = dedupe([
|
|
681
681
|
{
|
|
682
682
|
label: "cURL",
|
|
@@ -688,7 +688,7 @@ async function renderOperation(path, method, ctx, noTitle = false) {
|
|
|
688
688
|
source: getSampleRequest2(endpoint),
|
|
689
689
|
lang: "js"
|
|
690
690
|
},
|
|
691
|
-
...ctx.generateCodeSamples
|
|
691
|
+
...ctx.generateCodeSamples ? await ctx.generateCodeSamples(endpoint) : [],
|
|
692
692
|
...method["x-codeSamples"] ?? []
|
|
693
693
|
]);
|
|
694
694
|
example.push(
|
|
@@ -792,21 +792,26 @@ function getResponseTable(operation) {
|
|
|
792
792
|
});
|
|
793
793
|
return table.join("\n");
|
|
794
794
|
}
|
|
795
|
-
async function getResponseTabs(endpoint, operation, { renderer }) {
|
|
795
|
+
async function getResponseTabs(endpoint, operation, { renderer, generateTypeScriptSchema }) {
|
|
796
796
|
const items = [];
|
|
797
797
|
const child = [];
|
|
798
798
|
for (const code of Object.keys(operation.responses)) {
|
|
799
799
|
const example = getExampleResponse(endpoint, code);
|
|
800
|
-
|
|
800
|
+
let ts;
|
|
801
|
+
if (generateTypeScriptSchema) {
|
|
802
|
+
ts = await generateTypeScriptSchema(endpoint, code);
|
|
803
|
+
} else if (generateTypeScriptSchema === void 0) {
|
|
804
|
+
ts = await getTypescriptSchema(endpoint, code);
|
|
805
|
+
}
|
|
801
806
|
const description = code in endpoint.responses ? endpoint.responses[code].schema.description : void 0;
|
|
802
|
-
if (example
|
|
807
|
+
if (example) {
|
|
803
808
|
items.push(code);
|
|
804
809
|
child.push(
|
|
805
810
|
renderer.Response({ value: code }, [
|
|
806
811
|
p(description),
|
|
807
812
|
renderer.ResponseTypes([
|
|
808
813
|
renderer.ExampleResponse(example),
|
|
809
|
-
renderer.TypeScriptResponse(ts)
|
|
814
|
+
...ts ? [renderer.TypeScriptResponse(ts)] : []
|
|
810
815
|
])
|
|
811
816
|
])
|
|
812
817
|
);
|
|
@@ -896,6 +901,8 @@ function getContext(document, options) {
|
|
|
896
901
|
...defaultRenderer,
|
|
897
902
|
...options.renderer
|
|
898
903
|
},
|
|
904
|
+
generateTypeScriptSchema: options.generateTypeScriptSchema,
|
|
905
|
+
generateCodeSamples: options.generateCodeSamples,
|
|
899
906
|
baseUrl: document.servers?.[0].url ?? "https://example.com"
|
|
900
907
|
};
|
|
901
908
|
}
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
|
+
SchemaContext,
|
|
2
3
|
getDefaultValue,
|
|
3
4
|
getDefaultValues,
|
|
4
5
|
resolve,
|
|
5
|
-
useApiContext
|
|
6
|
-
|
|
6
|
+
useApiContext,
|
|
7
|
+
useSchemaContext
|
|
8
|
+
} from "./chunk-BYW6XCQ2.js";
|
|
7
9
|
|
|
8
10
|
// src/ui/playground.tsx
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
+
import {
|
|
12
|
+
useMemo,
|
|
13
|
+
useRef,
|
|
14
|
+
useState as useState3
|
|
15
|
+
} from "react";
|
|
16
|
+
import { Controller as Controller2, useForm } from "react-hook-form";
|
|
11
17
|
import useSWRImmutable from "swr/immutable";
|
|
12
18
|
import { Accordion, Accordions } from "fumadocs-ui/components/accordion";
|
|
13
19
|
import { cn as cn5, buttonVariants as buttonVariants2 } from "fumadocs-ui/components/api";
|
|
@@ -57,7 +63,7 @@ var FormItem = forwardRef(({ className, ...props }, ref) => {
|
|
|
57
63
|
});
|
|
58
64
|
FormItem.displayName = "FormItem";
|
|
59
65
|
var labelVariants = cva(
|
|
60
|
-
"text-xs font-medium text-foreground peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
66
|
+
"text-xs font-medium text-fd-foreground peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
61
67
|
);
|
|
62
68
|
var FormLabel = forwardRef(({ className, ...props }, ref) => {
|
|
63
69
|
const { isError, formItemId } = useFormField();
|
|
@@ -93,7 +99,7 @@ var FormDescription = forwardRef(({ className, ...props }, ref) => {
|
|
|
93
99
|
{
|
|
94
100
|
ref,
|
|
95
101
|
id: formDescriptionId,
|
|
96
|
-
className: cn("text-xs text-muted-foreground", className),
|
|
102
|
+
className: cn("text-xs text-fd-muted-foreground", className),
|
|
97
103
|
...props
|
|
98
104
|
}
|
|
99
105
|
);
|
|
@@ -180,7 +186,7 @@ var statusMap = {
|
|
|
180
186
|
403: { description: "Forbidden", color: "text-red-500", icon: CircleXIcon },
|
|
181
187
|
404: {
|
|
182
188
|
description: "Not Found",
|
|
183
|
-
color: "text-muted-foreground",
|
|
189
|
+
color: "text-fd-muted-foreground",
|
|
184
190
|
icon: CircleXIcon
|
|
185
191
|
},
|
|
186
192
|
500: {
|
|
@@ -205,7 +211,7 @@ function getStatusInfo(status) {
|
|
|
205
211
|
}
|
|
206
212
|
return {
|
|
207
213
|
description: "No Description",
|
|
208
|
-
color: "text-muted-foreground",
|
|
214
|
+
color: "text-fd-muted-foreground",
|
|
209
215
|
icon: CircleXIcon
|
|
210
216
|
};
|
|
211
217
|
}
|
|
@@ -233,13 +239,13 @@ var SelectTrigger = forwardRef2(({ className, children, ...props }, ref) => /* @
|
|
|
233
239
|
{
|
|
234
240
|
ref,
|
|
235
241
|
className: cn2(
|
|
236
|
-
"flex h-10 items-center justify-between rounded-md border px-3 py-2 text-sm text-foreground hover:bg-accent focus:outline-none focus:ring-2 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
242
|
+
"flex h-10 items-center justify-between rounded-md border px-3 py-2 text-sm text-fd-foreground hover:bg-fd-accent focus:outline-none focus:ring-2 focus:ring-fd-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
237
243
|
className
|
|
238
244
|
),
|
|
239
245
|
...props,
|
|
240
246
|
children: [
|
|
241
247
|
children,
|
|
242
|
-
/* @__PURE__ */ jsx2(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx2(ChevronDown, { className: "size-4 text-muted-foreground" }) })
|
|
248
|
+
/* @__PURE__ */ jsx2(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx2(ChevronDown, { className: "size-4 text-fd-muted-foreground" }) })
|
|
243
249
|
]
|
|
244
250
|
}
|
|
245
251
|
));
|
|
@@ -269,7 +275,7 @@ var SelectContent = forwardRef2(({ className, children, position = "popper", ...
|
|
|
269
275
|
{
|
|
270
276
|
ref,
|
|
271
277
|
className: cn2(
|
|
272
|
-
"z-50 overflow-hidden rounded-lg border bg-popover text-popover-foreground shadow-md data-[state=closed]:animate-popover-out data-[state=open]:animate-popover-in",
|
|
278
|
+
"z-50 overflow-hidden rounded-lg border bg-fd-popover text-fd-popover-foreground shadow-md data-[state=closed]:animate-fd-popover-out data-[state=open]:animate-fd-popover-in",
|
|
273
279
|
className
|
|
274
280
|
),
|
|
275
281
|
position,
|
|
@@ -305,7 +311,7 @@ var SelectItem = forwardRef2(({ className, children, ...props }, ref) => /* @__P
|
|
|
305
311
|
{
|
|
306
312
|
ref,
|
|
307
313
|
className: cn2(
|
|
308
|
-
"flex select-none flex-row items-center rounded-md py-1.5 pe-2 ps-6 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
314
|
+
"flex select-none flex-row items-center rounded-md py-1.5 pe-2 ps-6 text-sm outline-none focus:bg-fd-accent focus:text-fd-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
309
315
|
className
|
|
310
316
|
),
|
|
311
317
|
...props,
|
|
@@ -320,7 +326,7 @@ var SelectSeparator = forwardRef2(({ className, ...props }, ref) => /* @__PURE__
|
|
|
320
326
|
SelectPrimitive.Separator,
|
|
321
327
|
{
|
|
322
328
|
ref,
|
|
323
|
-
className: cn2("my-1 h-px bg-muted", className),
|
|
329
|
+
className: cn2("my-1 h-px bg-fd-muted", className),
|
|
324
330
|
...props
|
|
325
331
|
}
|
|
326
332
|
));
|
|
@@ -337,7 +343,7 @@ var Input = React.forwardRef(
|
|
|
337
343
|
{
|
|
338
344
|
type,
|
|
339
345
|
className: cn3(
|
|
340
|
-
"flex h-9 w-full rounded-md border bg-transparent px-3 py-1 text-sm text-foreground transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
346
|
+
"flex h-9 w-full rounded-md border bg-transparent px-3 py-1 text-sm text-fd-foreground transition-colors placeholder:text-fd-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-fd-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
341
347
|
className
|
|
342
348
|
),
|
|
343
349
|
ref,
|
|
@@ -348,17 +354,6 @@ var Input = React.forwardRef(
|
|
|
348
354
|
);
|
|
349
355
|
Input.displayName = "Input";
|
|
350
356
|
|
|
351
|
-
// src/ui/contexts/schema.tsx
|
|
352
|
-
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
353
|
-
var SchemaContext = createContext2(
|
|
354
|
-
void 0
|
|
355
|
-
);
|
|
356
|
-
function useSchemaContext() {
|
|
357
|
-
const ctx = useContext2(SchemaContext);
|
|
358
|
-
if (!ctx) throw new Error("Missing provider");
|
|
359
|
-
return ctx;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
357
|
// src/ui/inputs.tsx
|
|
363
358
|
import { Fragment, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
364
359
|
function renderInner({ field, ...props }) {
|
|
@@ -368,7 +363,7 @@ function renderInner({ field, ...props }) {
|
|
|
368
363
|
{
|
|
369
364
|
field,
|
|
370
365
|
...props,
|
|
371
|
-
className: cn4("rounded-lg border bg-accent/20 p-3", props.className)
|
|
366
|
+
className: cn4("rounded-lg border bg-fd-accent/20 p-3", props.className)
|
|
372
367
|
}
|
|
373
368
|
);
|
|
374
369
|
if (field.type === "switcher")
|
|
@@ -379,7 +374,10 @@ function renderInner({ field, ...props }) {
|
|
|
379
374
|
{
|
|
380
375
|
field,
|
|
381
376
|
...props,
|
|
382
|
-
className: cn4(
|
|
377
|
+
className: cn4(
|
|
378
|
+
"rounded-lg border bg-fd-background p-3",
|
|
379
|
+
props.className
|
|
380
|
+
)
|
|
383
381
|
}
|
|
384
382
|
);
|
|
385
383
|
if (field.type === "null") return null;
|
|
@@ -399,7 +397,7 @@ function InputContainer({
|
|
|
399
397
|
name,
|
|
400
398
|
required ? /* @__PURE__ */ jsx4("span", { className: "text-red-500", children: "*" }) : null,
|
|
401
399
|
/* @__PURE__ */ jsx4("div", { className: "flex-1" }),
|
|
402
|
-
type ? /* @__PURE__ */ jsx4("code", { className: "text-xs text-muted-foreground", children: type }) : null,
|
|
400
|
+
type ? /* @__PURE__ */ jsx4("code", { className: "text-xs text-fd-muted-foreground", children: type }) : null,
|
|
403
401
|
toolbar
|
|
404
402
|
] }),
|
|
405
403
|
!inline ? /* @__PURE__ */ jsx4("p", { className: "text-xs", children: description }) : null,
|
|
@@ -627,7 +625,7 @@ function InputField({
|
|
|
627
625
|
{
|
|
628
626
|
field,
|
|
629
627
|
fieldName,
|
|
630
|
-
className: "rounded-lg border bg-accent/20 p-3"
|
|
628
|
+
className: "rounded-lg border bg-fd-accent/20 p-3"
|
|
631
629
|
}
|
|
632
630
|
)
|
|
633
631
|
}
|
|
@@ -646,7 +644,7 @@ function InputField({
|
|
|
646
644
|
{
|
|
647
645
|
fieldName,
|
|
648
646
|
field,
|
|
649
|
-
className: "rounded-lg border bg-background p-3"
|
|
647
|
+
className: "rounded-lg border bg-fd-background p-3"
|
|
650
648
|
}
|
|
651
649
|
)
|
|
652
650
|
}
|
|
@@ -665,7 +663,7 @@ function InputField({
|
|
|
665
663
|
/* @__PURE__ */ jsxs2(FormLabel, { className: "inline-flex items-center gap-1", children: [
|
|
666
664
|
name,
|
|
667
665
|
field.isRequired ? /* @__PURE__ */ jsx4("span", { className: "text-red-500", children: "*" }) : null,
|
|
668
|
-
/* @__PURE__ */ jsx4("code", { className: "ms-auto text-xs text-muted-foreground", children: field.type }),
|
|
666
|
+
/* @__PURE__ */ jsx4("code", { className: "ms-auto text-xs text-fd-muted-foreground", children: field.type }),
|
|
669
667
|
toolbar
|
|
670
668
|
] }),
|
|
671
669
|
!inline ? /* @__PURE__ */ jsx4(FormDescription, { className: "text-xs", children: field.description }) : null
|
|
@@ -821,6 +819,7 @@ function APIPlayground({
|
|
|
821
819
|
header = [],
|
|
822
820
|
query = [],
|
|
823
821
|
body,
|
|
822
|
+
fields = {},
|
|
824
823
|
schemas
|
|
825
824
|
}) {
|
|
826
825
|
const { baseUrl } = useApiContext();
|
|
@@ -840,14 +839,16 @@ function APIPlayground({
|
|
|
840
839
|
async () => {
|
|
841
840
|
if (!input) return;
|
|
842
841
|
let pathname = route;
|
|
843
|
-
Object.keys(input.path
|
|
844
|
-
const paramValue = input.path
|
|
845
|
-
if (paramValue
|
|
842
|
+
Object.keys(input.path).forEach((key) => {
|
|
843
|
+
const paramValue = input.path[key];
|
|
844
|
+
if (typeof paramValue === "string")
|
|
845
|
+
pathname = pathname.replace(`{${key}}`, paramValue);
|
|
846
846
|
});
|
|
847
847
|
const url = new URL(pathname, baseUrl ?? window.location.origin);
|
|
848
|
-
Object.keys(input.query
|
|
849
|
-
const paramValue = input.query
|
|
850
|
-
if (paramValue
|
|
848
|
+
Object.keys(input.query).forEach((key) => {
|
|
849
|
+
const paramValue = input.query[key];
|
|
850
|
+
if (typeof paramValue === "string")
|
|
851
|
+
url.searchParams.append(key, paramValue);
|
|
851
852
|
});
|
|
852
853
|
const headers = new Headers({
|
|
853
854
|
"Content-Type": "application/json"
|
|
@@ -855,9 +856,9 @@ function APIPlayground({
|
|
|
855
856
|
if (input.authorization) {
|
|
856
857
|
headers.append("Authorization", input.authorization);
|
|
857
858
|
}
|
|
858
|
-
Object.keys(input.header
|
|
859
|
-
const paramValue = input.header
|
|
860
|
-
if (paramValue) headers.append(key, paramValue);
|
|
859
|
+
Object.keys(input.header).forEach((key) => {
|
|
860
|
+
const paramValue = input.header[key];
|
|
861
|
+
if (typeof paramValue === "string") headers.append(key, paramValue);
|
|
861
862
|
});
|
|
862
863
|
const bodyValue = body && input.body && Object.keys(input.body).length > 0 ? createBodyFromValue(input.body, body, schemas, dynamicRef.current) : void 0;
|
|
863
864
|
const response = await fetch(url, {
|
|
@@ -872,10 +873,31 @@ function APIPlayground({
|
|
|
872
873
|
shouldRetryOnError: false
|
|
873
874
|
}
|
|
874
875
|
);
|
|
875
|
-
const statusInfo = testQuery.data ? getStatusInfo(testQuery.data.status) : void 0;
|
|
876
876
|
const onSubmit = form.handleSubmit((value) => {
|
|
877
877
|
setInput(value);
|
|
878
878
|
});
|
|
879
|
+
function renderCustomField(fieldName, info, field, key) {
|
|
880
|
+
if (field) {
|
|
881
|
+
return /* @__PURE__ */ jsx6(
|
|
882
|
+
Controller2,
|
|
883
|
+
{
|
|
884
|
+
control: form.control,
|
|
885
|
+
render: (props) => field.render({ ...props, info }),
|
|
886
|
+
name: fieldName
|
|
887
|
+
},
|
|
888
|
+
key
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
return /* @__PURE__ */ jsx6(
|
|
892
|
+
InputField,
|
|
893
|
+
{
|
|
894
|
+
name: info.name,
|
|
895
|
+
fieldName,
|
|
896
|
+
field: info
|
|
897
|
+
},
|
|
898
|
+
key
|
|
899
|
+
);
|
|
900
|
+
}
|
|
879
901
|
return /* @__PURE__ */ jsx6(Form, { ...form, children: /* @__PURE__ */ jsx6(
|
|
880
902
|
SchemaContext.Provider,
|
|
881
903
|
{
|
|
@@ -886,11 +908,11 @@ function APIPlayground({
|
|
|
886
908
|
children: /* @__PURE__ */ jsxs3(
|
|
887
909
|
"form",
|
|
888
910
|
{
|
|
889
|
-
className: "not-prose flex flex-col gap-4 rounded-lg border bg-card p-4",
|
|
911
|
+
className: "not-prose flex flex-col gap-4 rounded-lg border bg-fd-card p-4",
|
|
890
912
|
onSubmit,
|
|
891
913
|
children: [
|
|
892
914
|
/* @__PURE__ */ jsxs3("div", { className: "flex flex-row gap-2", children: [
|
|
893
|
-
/* @__PURE__ */ jsx6("code", { className: "flex-1 overflow-auto rounded-lg border bg-secondary px-3 py-1.5 text-sm", children: route }),
|
|
915
|
+
/* @__PURE__ */ jsx6("code", { className: "flex-1 overflow-auto rounded-lg border bg-fd-secondary px-3 py-1.5 text-sm", children: route }),
|
|
894
916
|
/* @__PURE__ */ jsx6(
|
|
895
917
|
"button",
|
|
896
918
|
{
|
|
@@ -901,64 +923,70 @@ function APIPlayground({
|
|
|
901
923
|
}
|
|
902
924
|
)
|
|
903
925
|
] }),
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
testQuery.data
|
|
943
|
-
/* @__PURE__ */ jsxs3("div", { className: "inline-flex items-center gap-1.5 text-sm font-medium text-foreground", children: [
|
|
944
|
-
/* @__PURE__ */ jsx6(statusInfo.icon, { className: cn5("size-4", statusInfo.color) }),
|
|
945
|
-
statusInfo.description
|
|
946
|
-
] }),
|
|
947
|
-
/* @__PURE__ */ jsx6("p", { className: "text-sm text-muted-foreground", children: testQuery.data.status }),
|
|
948
|
-
testQuery.data.data ? /* @__PURE__ */ jsx6(
|
|
949
|
-
CodeBlock2,
|
|
950
|
-
{
|
|
951
|
-
code: JSON.stringify(testQuery.data.data, null, 2),
|
|
952
|
-
className: "max-h-[288px]"
|
|
953
|
-
}
|
|
954
|
-
) : null
|
|
955
|
-
] }) : null
|
|
926
|
+
authorization ? renderCustomField("authorization", authorization, fields.auth) : null,
|
|
927
|
+
/* @__PURE__ */ jsxs3(
|
|
928
|
+
Accordions,
|
|
929
|
+
{
|
|
930
|
+
type: "multiple",
|
|
931
|
+
className: cn5(
|
|
932
|
+
"-m-4 border-0 text-sm",
|
|
933
|
+
path.length === 0 && query.length === 0 && header.length === 0 && !body && "hidden"
|
|
934
|
+
),
|
|
935
|
+
children: [
|
|
936
|
+
path.length > 0 ? /* @__PURE__ */ jsx6(Accordion, { title: "Path", children: path.map(
|
|
937
|
+
(field) => renderCustomField(
|
|
938
|
+
`path.${field.name}`,
|
|
939
|
+
field,
|
|
940
|
+
fields.path,
|
|
941
|
+
field.name
|
|
942
|
+
)
|
|
943
|
+
) }) : null,
|
|
944
|
+
query.length > 0 ? /* @__PURE__ */ jsx6(Accordion, { title: "Query", children: query.map(
|
|
945
|
+
(field) => renderCustomField(
|
|
946
|
+
`query.${field.name}`,
|
|
947
|
+
field,
|
|
948
|
+
fields.query,
|
|
949
|
+
field.name
|
|
950
|
+
)
|
|
951
|
+
) }) : null,
|
|
952
|
+
header.length > 0 ? /* @__PURE__ */ jsx6(Accordion, { title: "Headers", children: header.map(
|
|
953
|
+
(field) => renderCustomField(
|
|
954
|
+
`header.${field.name}`,
|
|
955
|
+
field,
|
|
956
|
+
fields.header,
|
|
957
|
+
field.name
|
|
958
|
+
)
|
|
959
|
+
) }) : null,
|
|
960
|
+
body ? /* @__PURE__ */ jsx6(Accordion, { title: "Body", children: body.type === "object" && !fields.body ? /* @__PURE__ */ jsx6(ObjectInput, { field: body, fieldName: "body" }) : renderCustomField("body", body, fields.body) }) : null
|
|
961
|
+
]
|
|
962
|
+
}
|
|
963
|
+
),
|
|
964
|
+
testQuery.data ? /* @__PURE__ */ jsx6(ResultDisplay, { data: testQuery.data }) : null
|
|
956
965
|
]
|
|
957
966
|
}
|
|
958
967
|
)
|
|
959
968
|
}
|
|
960
969
|
) });
|
|
961
970
|
}
|
|
971
|
+
function ResultDisplay({
|
|
972
|
+
data
|
|
973
|
+
}) {
|
|
974
|
+
const statusInfo = useMemo(() => getStatusInfo(data.status), [data.status]);
|
|
975
|
+
return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-3 rounded-lg border bg-fd-card p-4", children: [
|
|
976
|
+
/* @__PURE__ */ jsxs3("div", { className: "inline-flex items-center gap-1.5 text-sm font-medium text-fd-foreground", children: [
|
|
977
|
+
/* @__PURE__ */ jsx6(statusInfo.icon, { className: cn5("size-4", statusInfo.color) }),
|
|
978
|
+
statusInfo.description
|
|
979
|
+
] }),
|
|
980
|
+
/* @__PURE__ */ jsx6("p", { className: "text-sm text-fd-muted-foreground", children: data.status }),
|
|
981
|
+
data.data ? /* @__PURE__ */ jsx6(
|
|
982
|
+
CodeBlock2,
|
|
983
|
+
{
|
|
984
|
+
code: JSON.stringify(data.data, null, 2),
|
|
985
|
+
className: "max-h-[288px]"
|
|
986
|
+
}
|
|
987
|
+
) : null
|
|
988
|
+
] });
|
|
989
|
+
}
|
|
962
990
|
export {
|
|
963
991
|
APIPlayground
|
|
964
992
|
};
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
|
-
import {
|
|
2
|
+
import { ReactElement, HTMLAttributes, ReactNode, MutableRefObject } from 'react';
|
|
3
|
+
import { FieldPath, ControllerRenderProps, ControllerFieldState, UseFormStateReturn } from 'react-hook-form';
|
|
4
|
+
import { R as RequestSchema, a as ReferenceSchema, A as APIPlaygroundProps, P as PrimitiveRequestField } from '../playground-D8pYn13F.js';
|
|
4
5
|
import { Tabs, Tab } from 'fumadocs-ui/components/tabs';
|
|
5
6
|
|
|
7
|
+
interface FormValues {
|
|
8
|
+
authorization: string;
|
|
9
|
+
path: Record<string, unknown>;
|
|
10
|
+
query: Record<string, unknown>;
|
|
11
|
+
header: Record<string, unknown>;
|
|
12
|
+
body: unknown;
|
|
13
|
+
}
|
|
14
|
+
interface CustomField<TName extends FieldPath<FormValues>, Info> {
|
|
15
|
+
render: (props: {
|
|
16
|
+
/**
|
|
17
|
+
* Field Info
|
|
18
|
+
*/
|
|
19
|
+
info: Info;
|
|
20
|
+
field: ControllerRenderProps<FormValues, TName>;
|
|
21
|
+
fieldState: ControllerFieldState;
|
|
22
|
+
formState: UseFormStateReturn<FormValues>;
|
|
23
|
+
}) => ReactElement;
|
|
24
|
+
}
|
|
25
|
+
|
|
6
26
|
interface RootProps extends HTMLAttributes<HTMLDivElement> {
|
|
7
27
|
baseUrl?: string;
|
|
8
28
|
}
|
|
@@ -37,10 +57,31 @@ declare function ObjectCollapsible(props: {
|
|
|
37
57
|
children: ReactNode;
|
|
38
58
|
}): React.ReactElement;
|
|
39
59
|
|
|
40
|
-
|
|
60
|
+
interface SchemaContextType {
|
|
61
|
+
references: Record<string, RequestSchema>;
|
|
62
|
+
dynamic: MutableRefObject<Map<string, DynamicField>>;
|
|
63
|
+
}
|
|
64
|
+
type DynamicField = {
|
|
65
|
+
type: 'object';
|
|
66
|
+
properties: string[];
|
|
67
|
+
} | {
|
|
68
|
+
type: 'field';
|
|
69
|
+
schema: RequestSchema | ReferenceSchema;
|
|
70
|
+
};
|
|
71
|
+
declare function useSchemaContext(): SchemaContextType;
|
|
72
|
+
|
|
73
|
+
declare const APIPlayground: react.ComponentType<APIPlaygroundProps & {
|
|
74
|
+
fields?: {
|
|
75
|
+
auth?: CustomField<"authorization", PrimitiveRequestField>;
|
|
76
|
+
path?: CustomField<`path.${string}`, PrimitiveRequestField>;
|
|
77
|
+
query?: CustomField<`query.${string}`, PrimitiveRequestField>;
|
|
78
|
+
header?: CustomField<`header.${string}`, PrimitiveRequestField>;
|
|
79
|
+
body?: CustomField<"body", RequestSchema>;
|
|
80
|
+
};
|
|
81
|
+
} & react.HTMLAttributes<HTMLFormElement>>;
|
|
41
82
|
declare const Responses: typeof Tabs;
|
|
42
83
|
declare const Response: typeof Tab;
|
|
43
84
|
declare const Requests: typeof Tabs;
|
|
44
85
|
declare const Request: typeof Tab;
|
|
45
86
|
|
|
46
|
-
export { API, APIExample, APIInfo, type APIInfoProps, APIPlayground, ExampleResponse, ObjectCollapsible, Property, Request, Requests, Response, ResponseTypes, Responses, Root, type RootProps, TypeScriptResponse };
|
|
87
|
+
export { API, APIExample, APIInfo, type APIInfoProps, APIPlayground, ExampleResponse, ObjectCollapsible, Property, Request, Requests, Response, ResponseTypes, Responses, Root, type RootProps, TypeScriptResponse, useSchemaContext };
|
package/dist/ui/index.js
CHANGED
|
@@ -3,8 +3,9 @@ import {
|
|
|
3
3
|
ApiProvider,
|
|
4
4
|
badgeVariants,
|
|
5
5
|
getBadgeColor,
|
|
6
|
-
useApiContext
|
|
7
|
-
|
|
6
|
+
useApiContext,
|
|
7
|
+
useSchemaContext
|
|
8
|
+
} from "../chunk-BYW6XCQ2.js";
|
|
8
9
|
|
|
9
10
|
// src/ui/index.ts
|
|
10
11
|
import { Tab, Tabs } from "fumadocs-ui/components/tabs";
|
|
@@ -28,7 +29,7 @@ function Root({
|
|
|
28
29
|
"div",
|
|
29
30
|
{
|
|
30
31
|
className: cn(
|
|
31
|
-
"flex flex-col gap-24 text-sm text-muted-foreground",
|
|
32
|
+
"flex flex-col gap-24 text-sm text-fd-muted-foreground",
|
|
32
33
|
className
|
|
33
34
|
),
|
|
34
35
|
...props,
|
|
@@ -55,9 +56,9 @@ function API({
|
|
|
55
56
|
}
|
|
56
57
|
function Route({ route }) {
|
|
57
58
|
const segments = route.split("/").filter((part) => part.length > 0);
|
|
58
|
-
return /* @__PURE__ */ jsx("div", { className: "flex flex-row items-center gap-1 text-sm", children: segments.map((part, index) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
59
|
-
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "/" }),
|
|
60
|
-
/* @__PURE__ */ jsx("span", { className: "text-foreground", children: part })
|
|
59
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-row flex-wrap items-center gap-1 text-sm", children: segments.map((part, index) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
60
|
+
/* @__PURE__ */ jsx("span", { className: "text-fd-muted-foreground", children: "/" }),
|
|
61
|
+
part.startsWith("{") && part.endsWith("}") ? /* @__PURE__ */ jsx("code", { className: "text-fd-primary", children: part }) : /* @__PURE__ */ jsx("span", { className: "text-fd-foreground", children: part })
|
|
61
62
|
] }, index)) });
|
|
62
63
|
}
|
|
63
64
|
function APIInfo({
|
|
@@ -73,7 +74,7 @@ function APIInfo({
|
|
|
73
74
|
"div",
|
|
74
75
|
{
|
|
75
76
|
className: cn(
|
|
76
|
-
"sticky top-24 z-[2] flex flex-row items-center gap-2 rounded-lg border bg-card p-3 md:top-10"
|
|
77
|
+
"sticky top-24 z-[2] flex flex-row items-center gap-2 rounded-lg border bg-fd-card p-3 md:top-10"
|
|
77
78
|
),
|
|
78
79
|
children: [
|
|
79
80
|
/* @__PURE__ */ jsx(
|
|
@@ -101,12 +102,12 @@ function Property({
|
|
|
101
102
|
deprecated,
|
|
102
103
|
children
|
|
103
104
|
}) {
|
|
104
|
-
return /* @__PURE__ */ jsxs("div", { className: "mb-4 flex flex-col rounded-lg border bg-card p-3 prose-no-margin", children: [
|
|
105
|
+
return /* @__PURE__ */ jsxs("div", { className: "mb-4 flex flex-col rounded-lg border bg-fd-card p-3 prose-no-margin", children: [
|
|
105
106
|
/* @__PURE__ */ jsxs("h4", { className: "inline-flex items-center gap-4", children: [
|
|
106
107
|
/* @__PURE__ */ jsx("code", { children: name }),
|
|
107
108
|
required ? /* @__PURE__ */ jsx("div", { className: cn(badgeVariants({ color: "red" })), children: "Required" }) : null,
|
|
108
109
|
deprecated ? /* @__PURE__ */ jsx("div", { className: cn(badgeVariants({ color: "yellow" })), children: "Deprecated" }) : null,
|
|
109
|
-
/* @__PURE__ */ jsx("span", { className: "ms-auto font-mono text-[13px] text-muted-foreground", children: type })
|
|
110
|
+
/* @__PURE__ */ jsx("span", { className: "ms-auto font-mono text-[13px] text-fd-muted-foreground", children: type })
|
|
110
111
|
] }),
|
|
111
112
|
children
|
|
112
113
|
] });
|
|
@@ -173,7 +174,7 @@ function CopyRouteButton({
|
|
|
173
174
|
|
|
174
175
|
// src/ui/index.ts
|
|
175
176
|
var APIPlayground = dynamic(
|
|
176
|
-
() => import("../playground-
|
|
177
|
+
() => import("../playground-EVJUDTB3.js").then((mod) => mod.APIPlayground)
|
|
177
178
|
);
|
|
178
179
|
var Responses = Tabs;
|
|
179
180
|
var Response = Tab;
|
|
@@ -193,5 +194,6 @@ export {
|
|
|
193
194
|
ResponseTypes,
|
|
194
195
|
Responses,
|
|
195
196
|
Root,
|
|
196
|
-
TypeScriptResponse
|
|
197
|
+
TypeScriptResponse,
|
|
198
|
+
useSchemaContext
|
|
197
199
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fumadocs-openapi",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Generate MDX docs for your OpenAPI spec",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"NextJs",
|
|
@@ -47,14 +47,14 @@
|
|
|
47
47
|
"lucide-react": "^0.414.0",
|
|
48
48
|
"openapi-sampler": "^1.5.1",
|
|
49
49
|
"react-hook-form": "^7.52.1",
|
|
50
|
-
"shiki": "^1.11.
|
|
50
|
+
"shiki": "^1.11.1",
|
|
51
51
|
"swr": "^2.2.5",
|
|
52
|
-
"fumadocs-core": "
|
|
53
|
-
"fumadocs-ui": "
|
|
52
|
+
"fumadocs-core": "13.0.0",
|
|
53
|
+
"fumadocs-ui": "13.0.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@types/js-yaml": "^4.0.9",
|
|
57
|
-
"@types/node": "20.14.
|
|
57
|
+
"@types/node": "20.14.12",
|
|
58
58
|
"@types/openapi-sampler": "^1.0.3",
|
|
59
59
|
"@types/react": "^18.3.3",
|
|
60
60
|
"next": "^14.2.5",
|