fumadocs-openapi 11.4.3 → 12.0.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/css/generated/shared.css +1 -1
- package/css/preset.css +0 -1
- package/dist/_virtual/_rolldown/runtime.js +10 -1
- package/dist/generate-file.d.ts +2 -2
- package/dist/generate-file.js +1 -1
- package/dist/i18n.d.ts +6 -8
- package/dist/i18n.js +2 -17
- package/dist/index.browser.d.ts +13 -0
- package/dist/index.browser.js +11 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +3 -1
- package/dist/operation.d.ts +133 -0
- package/dist/operation.js +296 -0
- package/dist/packages/shared-api/dist/.translations/index.d.ts +29 -0
- package/dist/packages/shared-api/dist/.translations/keys.js +28 -0
- package/dist/packages/shared-api/dist/auto-anchor/client.js +20 -0
- package/dist/packages/shared-api/dist/auto-anchor/index.js +17 -0
- package/dist/packages/shared-api/dist/codegen.d.ts +42 -0
- package/dist/packages/shared-api/dist/codegen.js +35 -0
- package/dist/packages/shared-api/dist/components/accordion.js +71 -0
- package/dist/packages/shared-api/dist/components/badge.js +21 -0
- package/dist/packages/shared-api/dist/components/collapsible.d.ts +7 -0
- package/dist/packages/shared-api/dist/components/collapsible.js +16 -0
- package/dist/packages/shared-api/dist/components/defaults.d.ts +20 -0
- package/dist/packages/shared-api/dist/components/defaults.js +70 -0
- package/dist/packages/shared-api/dist/components/dialog.js +55 -0
- package/dist/packages/shared-api/dist/components/input.js +14 -0
- package/dist/packages/shared-api/dist/components/label.js +11 -0
- package/dist/packages/shared-api/dist/components/playground/inputs.js +439 -0
- package/dist/packages/shared-api/dist/components/playground/schema.js +112 -0
- package/dist/packages/shared-api/dist/components/popover.js +23 -0
- package/dist/packages/shared-api/dist/components/schema/client.d.ts +12 -0
- package/dist/packages/shared-api/dist/components/schema/client.js +508 -0
- package/dist/packages/shared-api/dist/components/schema/index.d.ts +9 -0
- package/dist/packages/shared-api/dist/components/schema/index.js +28 -0
- package/dist/packages/shared-api/dist/components/select-tab.js +54 -0
- package/dist/packages/shared-api/dist/components/select.js +70 -0
- package/dist/packages/shared-api/dist/components/spinner.js +14 -0
- package/dist/packages/shared-api/dist/i18n.d.ts +3 -0
- package/dist/packages/shared-api/dist/i18n.js +7 -0
- package/dist/packages/shared-api/dist/utils/cn.js +2 -0
- package/dist/packages/shared-api/dist/utils/id-to-title.js +12 -0
- package/dist/packages/shared-api/dist/utils/is-plain-object.js +8 -0
- package/dist/packages/shared-api/dist/utils/merge-refs.js +11 -0
- package/dist/packages/shared-api/dist/utils/url.js +13 -0
- package/dist/{utils → packages/shared-api/dist/utils}/use-query.js +2 -1
- package/dist/packages/shared-api/dist/utils/use-server-store.js +56 -0
- package/dist/playground/auth.d.ts +37 -0
- package/dist/playground/auth.js +216 -21
- package/dist/playground/fetcher.d.ts +11 -1
- package/dist/playground/index.d.ts +3 -0
- package/dist/playground/index.js +3 -0
- package/dist/requests/generators/index.d.ts +4 -4
- package/dist/requests/generators/index.js +2 -2
- package/dist/requests/index.d.ts +4 -0
- package/dist/requests/index.js +3 -0
- package/dist/requests/media/adapter.d.ts +1 -0
- package/dist/requests/media/encode.d.ts +7 -2
- package/dist/requests/media/resolve-adapter.d.ts +20 -0
- package/dist/server/index.d.ts +4 -7
- package/dist/server/index.js +1 -7
- package/dist/types.d.ts +0 -14
- package/dist/ui/base.d.ts +3 -2
- package/dist/ui/base.js +15 -166
- package/dist/ui/components/codeblock.js +3 -10
- package/dist/ui/components/heading.js +10 -19
- package/dist/ui/components/markdown.js +4 -6
- package/dist/ui/components/method-label.js +2 -17
- package/dist/ui/components/schema.js +28 -0
- package/dist/ui/index.d.ts +7 -198
- package/dist/ui/index.js +1 -3
- package/dist/ui/operation/index.js +142 -229
- package/dist/ui/operation/request-tabs.js +15 -23
- package/dist/ui/operation/response-tabs.js +9 -53
- package/dist/ui/operation/usage-tabs.js +35 -93
- package/dist/{playground → ui/playground}/client.d.ts +10 -21
- package/dist/ui/playground/client.js +432 -0
- package/dist/{playground → ui/playground}/components/oauth-dialog.js +26 -91
- package/dist/{playground → ui/playground}/components/result-display.d.ts +2 -2
- package/dist/{playground → ui/playground}/components/result-display.js +7 -7
- package/dist/{playground → ui/playground}/components/server-select.js +12 -13
- package/dist/{playground → ui/playground}/status-info.js +2 -2
- package/dist/{scalar → ui/scalar}/client.js +7 -5
- package/dist/{scalar → ui/scalar}/index.d.ts +2 -2
- package/dist/{scalar → ui/scalar}/index.js +3 -7
- package/dist/utils/create-page.d.ts +216 -0
- package/dist/utils/create-page.js +221 -0
- package/dist/utils/document/dereference.d.ts +2 -2
- package/dist/utils/document/dereference.js +8 -3
- package/dist/utils/document/load.js +2 -2
- package/dist/utils/get-example-requests.js +11 -13
- package/dist/utils/pages/builder.d.ts +15 -2
- package/dist/utils/pages/builder.js +6 -6
- package/dist/utils/pages/preset-auto.d.ts +1 -1
- package/dist/utils/pages/preset-auto.js +3 -3
- package/dist/utils/pages/to-static-data.js +4 -4
- package/dist/utils/pages/to-text.d.ts +1 -1
- package/dist/utils/pages/to-text.js +3 -5
- package/dist/utils/schema.js +5 -5
- package/dist/utils/use-server.d.ts +24 -0
- package/dist/utils/use-server.js +57 -0
- package/package.json +28 -20
- package/dist/node_modules/.pnpm/@scalar_openapi-upgrader@0.2.15/node_modules/@scalar/openapi-upgrader/dist/2.0-to-3.0/upgrade-from-two-to-three.js +0 -508
- package/dist/node_modules/.pnpm/@scalar_openapi-upgrader@0.2.15/node_modules/@scalar/openapi-upgrader/dist/3.0-to-3.1/upgrade-from-three-to-three-one.js +0 -152
- package/dist/node_modules/.pnpm/@scalar_openapi-upgrader@0.2.15/node_modules/@scalar/openapi-upgrader/dist/3.1-to-3.2/upgrade-from-three-one-to-three-two.js +0 -61
- package/dist/node_modules/.pnpm/@scalar_openapi-upgrader@0.2.15/node_modules/@scalar/openapi-upgrader/dist/helpers/traverse.js +0 -25
- package/dist/node_modules/.pnpm/@scalar_openapi-upgrader@0.2.15/node_modules/@scalar/openapi-upgrader/dist/upgrade.js +0 -15
- package/dist/playground/client.js +0 -574
- package/dist/ui/contexts/api.d.ts +0 -17
- package/dist/ui/contexts/api.js +0 -84
- package/dist/ui/create-client.d.ts +0 -9
- package/dist/ui/create-client.js +0 -7
- package/dist/ui/operation/context.d.ts +0 -15
- package/dist/ui/operation/context.js +0 -48
- package/dist/ui/operation/request-tabs.d.ts +0 -12
- package/dist/ui/operation/response-tabs.d.ts +0 -30
- package/dist/utils/get-example-requests.d.ts +0 -11
- package/dist/utils/schema.d.ts +0 -3
- package/dist/utils/storage-key.js +0 -15
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { useServer } from "../../../utils/use-server.js";
|
|
3
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../../../packages/shared-api/dist/components/select.js";
|
|
4
|
+
import { cn } from "../../../utils/cn.js";
|
|
5
|
+
import { Label } from "../../../packages/shared-api/dist/components/label.js";
|
|
6
|
+
import { Input } from "../../../packages/shared-api/dist/components/input.js";
|
|
7
|
+
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "../../../packages/shared-api/dist/components/dialog.js";
|
|
4
8
|
import { useEffect, useRef, useState } from "react";
|
|
5
9
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
-
import { EditIcon } from "lucide-react";
|
|
7
|
-
import { useTranslations } from "@fuma-translate/react";
|
|
8
|
-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@fumadocs/api-docs/components/select";
|
|
9
|
-
import { Input, labelVariants } from "@fumadocs/api-docs/components/input";
|
|
10
|
-
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@fumadocs/api-docs/components/dialog";
|
|
11
10
|
import { StfProvider, useFieldValue, useListener, useStf } from "@fumari/stf";
|
|
12
|
-
import {
|
|
13
|
-
|
|
11
|
+
import { useTranslations } from "@fuma-translate/react";
|
|
12
|
+
import { EditIcon } from "lucide-react";
|
|
13
|
+
//#region src/ui/playground/components/server-select.tsx
|
|
14
14
|
function ServerSelect(props) {
|
|
15
|
-
const { servers, server, setServer, setServerVariables } =
|
|
15
|
+
const { servers, server, resolveUrl, setServer, setServerVariables } = useServer();
|
|
16
16
|
const [open, setOpen] = useState(false);
|
|
17
17
|
const [isMounted, setIsMounted] = useState(false);
|
|
18
18
|
const t = useTranslations({ note: "playground server select" });
|
|
@@ -34,7 +34,7 @@ function ServerSelect(props) {
|
|
|
34
34
|
}),
|
|
35
35
|
/* @__PURE__ */ jsx("code", {
|
|
36
36
|
className: "truncate min-w-0 flex-1",
|
|
37
|
-
children: isMounted ?
|
|
37
|
+
children: isMounted ? resolveUrl() : t("loading...")
|
|
38
38
|
}),
|
|
39
39
|
/* @__PURE__ */ jsx(EditIcon, { className: "size-4" })
|
|
40
40
|
]
|
|
@@ -87,8 +87,7 @@ function ServerSelectContent({ defaultValues, onChange, schema }) {
|
|
|
87
87
|
return /* @__PURE__ */ jsxs("fieldset", {
|
|
88
88
|
className: "flex flex-col gap-1",
|
|
89
89
|
children: [
|
|
90
|
-
/* @__PURE__ */ jsx(
|
|
91
|
-
className: cn(labelVariants()),
|
|
90
|
+
/* @__PURE__ */ jsx(Label, {
|
|
92
91
|
htmlFor: key,
|
|
93
92
|
children: key
|
|
94
93
|
}),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useMemo } from "react";
|
|
2
|
-
import { CircleCheck, CircleX } from "lucide-react";
|
|
3
2
|
import { useTranslations } from "@fuma-translate/react";
|
|
4
|
-
|
|
3
|
+
import { CircleCheck, CircleX } from "lucide-react";
|
|
4
|
+
//#region src/ui/playground/status-info.tsx
|
|
5
5
|
function useStatusInfo(status) {
|
|
6
6
|
const t = useTranslations({ note: "playground status info" });
|
|
7
7
|
return useMemo(() => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
3
|
-
import { MethodLabel } from "../
|
|
2
|
+
import { useOpenAPI } from "../../utils/create-page.js";
|
|
3
|
+
import { MethodLabel } from "../components/method-label.js";
|
|
4
|
+
import { cn } from "../../utils/cn.js";
|
|
4
5
|
import { useEffect, useState } from "react";
|
|
5
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
7
|
import { useTranslations } from "@fuma-translate/react";
|
|
@@ -8,12 +9,13 @@ import { buttonVariants } from "fumadocs-ui/components/ui/button";
|
|
|
8
9
|
import { useApiClient } from "@scalar/api-client-react";
|
|
9
10
|
import { useTheme } from "fumadocs-ui/provider/base";
|
|
10
11
|
import "@scalar/api-client-react/style.css";
|
|
11
|
-
//#region src/scalar/client.tsx
|
|
12
|
-
function ScalarPlayground({ path, method
|
|
12
|
+
//#region src/ui/scalar/client.tsx
|
|
13
|
+
function ScalarPlayground({ path, method }) {
|
|
13
14
|
const { resolvedTheme } = useTheme();
|
|
15
|
+
const { bundled } = useOpenAPI().doc;
|
|
14
16
|
const t = useTranslations({ note: "scalar API client" });
|
|
15
17
|
const [mounted, setMounted] = useState(false);
|
|
16
|
-
const client = useApiClient({ configuration: { content:
|
|
18
|
+
const client = useApiClient({ configuration: { content: bundled } });
|
|
17
19
|
useEffect(() => {
|
|
18
20
|
setMounted(true);
|
|
19
21
|
}, []);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { CreateOpenAPIPageOptions } from "../
|
|
2
|
-
//#region src/scalar/index.d.ts
|
|
1
|
+
import { CreateOpenAPIPageOptions } from "../index.js";
|
|
2
|
+
//#region src/ui/scalar/index.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* Enable Scalar for API playgrounds by wrapping your options inside.
|
|
5
5
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { lazy } from "react";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
|
-
//#region src/scalar/index.tsx
|
|
4
|
+
//#region src/ui/scalar/index.tsx
|
|
5
5
|
const Client = lazy(() => import("./client.js"));
|
|
6
6
|
/**
|
|
7
7
|
* Enable Scalar for API playgrounds by wrapping your options inside.
|
|
@@ -13,14 +13,10 @@ function withScalar(options = {}) {
|
|
|
13
13
|
...options,
|
|
14
14
|
playground: {
|
|
15
15
|
...options.playground,
|
|
16
|
-
|
|
17
|
-
return props.children;
|
|
18
|
-
},
|
|
19
|
-
render({ method, path, ctx }) {
|
|
16
|
+
render({ method, path }) {
|
|
20
17
|
return /* @__PURE__ */ jsx(Client, {
|
|
21
18
|
method,
|
|
22
|
-
path
|
|
23
|
-
spec: ctx.schema.bundled
|
|
19
|
+
path
|
|
24
20
|
});
|
|
25
21
|
}
|
|
26
22
|
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { MediaAdapter } from "../requests/media/adapter.js";
|
|
2
|
+
import { CodeUsageGeneratorRegistry, InlineCodeUsageGenerator } from "../requests/generators/index.js";
|
|
3
|
+
import { Awaitable, HttpMethods, OperationObject, PathItemObject } from "../types.js";
|
|
4
|
+
import { ExampleRequest, PageOperationProps, ResponseTab } from "../operation.js";
|
|
5
|
+
import { OpenAPIPageProps, OperationItem, WebhookItem } from "./pages/builder.js";
|
|
6
|
+
import { DereferencedDocument } from "./document/dereference.js";
|
|
7
|
+
import { SchemaUIOptions } from "../packages/shared-api/dist/components/schema/index.js";
|
|
8
|
+
import { PlaygroundClientOptions } from "../ui/playground/client.js";
|
|
9
|
+
import { PageComponents, ShikiOptions } from "../packages/shared-api/dist/components/defaults.js";
|
|
10
|
+
import { FC, ReactNode } from "react";
|
|
11
|
+
import { JsonSchema } from "@fumadocs/json-schema";
|
|
12
|
+
import { ShikiFactory } from "fumadocs-core/highlight/shiki";
|
|
13
|
+
//#region src/utils/create-page.d.ts
|
|
14
|
+
/** components the UI renders through, so a page can replace them */
|
|
15
|
+
export interface OpenAPIComponents extends PageComponents {
|
|
16
|
+
SchemaUI: FC<SchemaUIOptions>;
|
|
17
|
+
}
|
|
18
|
+
export interface GenerateTypeScriptDefinitionsContext {
|
|
19
|
+
name: string;
|
|
20
|
+
readOnly: boolean;
|
|
21
|
+
writeOnly: boolean;
|
|
22
|
+
doc: DereferencedDocument;
|
|
23
|
+
}
|
|
24
|
+
export interface OpenAPIRuntimeOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Support other media types.
|
|
27
|
+
*/
|
|
28
|
+
mediaAdapters?: Record<string, MediaAdapter>;
|
|
29
|
+
/**
|
|
30
|
+
* Set a prefix for `localStorage` keys.
|
|
31
|
+
*
|
|
32
|
+
* Useful when using multiple OpenAPI instances to prevent state conflicts.
|
|
33
|
+
*
|
|
34
|
+
* @defaultValue `fumadocs-openapi-`
|
|
35
|
+
*/
|
|
36
|
+
storageKeyPrefix?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Generate example code usage for all endpoints.
|
|
39
|
+
*/
|
|
40
|
+
codeUsages?: CodeUsageGeneratorRegistry;
|
|
41
|
+
/**
|
|
42
|
+
* Generate example code usage for each endpoint.
|
|
43
|
+
*/
|
|
44
|
+
generateCodeSamples?: (options: {
|
|
45
|
+
path: string;
|
|
46
|
+
operation: OperationObject;
|
|
47
|
+
method: HttpMethods;
|
|
48
|
+
pathItem: PathItemObject;
|
|
49
|
+
}) => InlineCodeUsageGenerator[];
|
|
50
|
+
/**
|
|
51
|
+
* Generate TypeScript definitions from JSON schema.
|
|
52
|
+
*
|
|
53
|
+
* Pass `false` to disable it.
|
|
54
|
+
*/
|
|
55
|
+
generateTypeScriptDefinitions?: ((schema: JsonSchema, ctx: GenerateTypeScriptDefinitionsContext) => Awaitable<string | undefined>) | false;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The document and request options of the page, read from `useOpenAPI()`.
|
|
59
|
+
*/
|
|
60
|
+
export interface OpenAPIRuntime extends OpenAPIRuntimeOptions {
|
|
61
|
+
doc: DereferencedDocument;
|
|
62
|
+
proxyUrl?: string;
|
|
63
|
+
mediaAdapters: Record<string, MediaAdapter>;
|
|
64
|
+
storageKeyPrefix: string;
|
|
65
|
+
}
|
|
66
|
+
export interface APIPlaygroundProps {
|
|
67
|
+
path: string;
|
|
68
|
+
method: HttpMethods;
|
|
69
|
+
operation: OperationObject;
|
|
70
|
+
pathItem: PathItemObject;
|
|
71
|
+
}
|
|
72
|
+
export interface OperationPlaygroundOptions extends PlaygroundClientOptions {
|
|
73
|
+
/**
|
|
74
|
+
* @defaultValue true
|
|
75
|
+
*/
|
|
76
|
+
enabled?: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Replace the renderer, e.g. the playground installed with Fumadocs CLI.
|
|
79
|
+
*/
|
|
80
|
+
render?: (props: APIPlaygroundProps) => ReactNode;
|
|
81
|
+
}
|
|
82
|
+
export interface PageLayoutProps {
|
|
83
|
+
operations?: {
|
|
84
|
+
item: OperationItem;
|
|
85
|
+
children: ReactNode;
|
|
86
|
+
}[];
|
|
87
|
+
webhooks?: {
|
|
88
|
+
item: WebhookItem;
|
|
89
|
+
children: ReactNode;
|
|
90
|
+
}[];
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The options the UI renders with, read from `useRenderContext()`.
|
|
94
|
+
*/
|
|
95
|
+
export interface OpenAPIRenderOptions {
|
|
96
|
+
/** the Shiki highlighter of code blocks */
|
|
97
|
+
shiki?: ShikiFactory;
|
|
98
|
+
shikiOptions?: ShikiOptions;
|
|
99
|
+
/**
|
|
100
|
+
* Show full response schema instead of only example response & Typescript definitions.
|
|
101
|
+
*
|
|
102
|
+
* @default true
|
|
103
|
+
*/
|
|
104
|
+
showResponseSchema?: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Customize page content.
|
|
107
|
+
*/
|
|
108
|
+
content?: {
|
|
109
|
+
renderResponseTabs?: (options: {
|
|
110
|
+
tabs: ResponseTab[];
|
|
111
|
+
}, ctx: RenderContext) => ReactNode;
|
|
112
|
+
renderRequestTabs?: (options: {
|
|
113
|
+
route: string;
|
|
114
|
+
items: ExampleRequest[];
|
|
115
|
+
method: HttpMethods;
|
|
116
|
+
pathItem: PathItemObject;
|
|
117
|
+
operation: OperationObject;
|
|
118
|
+
}, ctx: RenderContext) => ReactNode;
|
|
119
|
+
renderAPIExampleLayout?: (slots: {
|
|
120
|
+
selector: ReactNode;
|
|
121
|
+
usageTabs: ReactNode;
|
|
122
|
+
responseTabs: ReactNode;
|
|
123
|
+
}, ctx: RenderContext) => ReactNode;
|
|
124
|
+
/**
|
|
125
|
+
* @param generators - codegens for API example usages
|
|
126
|
+
*/
|
|
127
|
+
renderAPIExampleUsageTabs?: (generators: CodeUsageGeneratorRegistry, ctx: RenderContext) => ReactNode;
|
|
128
|
+
/**
|
|
129
|
+
* renderer of the entire page's layout (containing all operations & webhooks UI)
|
|
130
|
+
*/
|
|
131
|
+
renderPageLayout?: (slots: PageLayoutProps, ctx: RenderContext) => ReactNode;
|
|
132
|
+
renderOperationLayout?: (slots: {
|
|
133
|
+
header: ReactNode;
|
|
134
|
+
description: ReactNode;
|
|
135
|
+
apiExample: ReactNode;
|
|
136
|
+
apiPlayground: ReactNode;
|
|
137
|
+
authSchemes: ReactNode;
|
|
138
|
+
parameters: ReactNode;
|
|
139
|
+
body: ReactNode;
|
|
140
|
+
responses: ReactNode;
|
|
141
|
+
callbacks: ReactNode;
|
|
142
|
+
}, context: {
|
|
143
|
+
path: string;
|
|
144
|
+
operation: OperationObject;
|
|
145
|
+
method: HttpMethods;
|
|
146
|
+
pathItem: PathItemObject;
|
|
147
|
+
ctx: RenderContext;
|
|
148
|
+
}) => ReactNode;
|
|
149
|
+
renderWebhookLayout?: (slots: {
|
|
150
|
+
header: ReactNode;
|
|
151
|
+
description: ReactNode;
|
|
152
|
+
authSchemes: ReactNode;
|
|
153
|
+
parameters: ReactNode;
|
|
154
|
+
body: ReactNode;
|
|
155
|
+
requests: ReactNode;
|
|
156
|
+
responses: ReactNode;
|
|
157
|
+
callbacks: ReactNode;
|
|
158
|
+
}) => ReactNode;
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Info UI for JSON schemas.
|
|
162
|
+
*/
|
|
163
|
+
schemaUI?: {
|
|
164
|
+
/**
|
|
165
|
+
* Show examples under the generated content of JSON schemas.
|
|
166
|
+
*
|
|
167
|
+
* @defaultValue false
|
|
168
|
+
*/
|
|
169
|
+
showExample?: boolean;
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* Customize API playground.
|
|
173
|
+
*/
|
|
174
|
+
playground?: OperationPlaygroundOptions;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* The render options of the page with their defaults applied, read from `useRenderContext()`.
|
|
178
|
+
*/
|
|
179
|
+
export interface RenderContext extends OpenAPIRenderOptions {
|
|
180
|
+
shiki: ShikiFactory;
|
|
181
|
+
shikiOptions: ShikiOptions;
|
|
182
|
+
}
|
|
183
|
+
export interface CreateOpenAPIRendererOptions extends OpenAPIRuntimeOptions, OpenAPIRenderOptions {
|
|
184
|
+
components: Partial<PageComponents> & {
|
|
185
|
+
SchemaUI: FC<SchemaUIOptions>;
|
|
186
|
+
/** renders an operation or webhook of the page */
|
|
187
|
+
Operation: FC<PageOperationProps>;
|
|
188
|
+
/** wraps the rendered operations and webhooks */
|
|
189
|
+
Layout?: FC<PageLayoutProps>;
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* The runtime of the API page: the document and request options.
|
|
194
|
+
*/
|
|
195
|
+
export declare function useOpenAPI(): OpenAPIRuntime;
|
|
196
|
+
export declare function useComponents(): OpenAPIComponents;
|
|
197
|
+
/**
|
|
198
|
+
* The render options of the page, available under a page created with `createOpenAPIRenderer()`.
|
|
199
|
+
*/
|
|
200
|
+
export declare function useRenderContext(): RenderContext;
|
|
201
|
+
/**
|
|
202
|
+
* Generate TypeScript definitions of a JSON schema, `undefined` when disabled.
|
|
203
|
+
*/
|
|
204
|
+
export declare function useTypeScriptDefinitions(schema: JsonSchema | undefined, options: Pick<GenerateTypeScriptDefinitionsContext, 'name' | 'readOnly' | 'writeOnly'>): string | undefined;
|
|
205
|
+
/**
|
|
206
|
+
* Create `<OpenAPIPage />` from your own UI, it takes the props of generated pages.
|
|
207
|
+
*/
|
|
208
|
+
export declare function createOpenAPIRenderer(options: CreateOpenAPIRendererOptions): FC<OpenAPIPageProps>;
|
|
209
|
+
/**
|
|
210
|
+
* `createOpenAPIRenderer()` with nothing built in: it highlights through the `shiki` you pass, and
|
|
211
|
+
* generates code usages and TypeScript definitions only when you pass them.
|
|
212
|
+
*/
|
|
213
|
+
export declare function createOpenAPIBaseRenderer({ components, ...options }: CreateOpenAPIRendererOptions & {
|
|
214
|
+
shiki: ShikiFactory;
|
|
215
|
+
}): FC<OpenAPIPageProps>;
|
|
216
|
+
//#endregion
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { dereferenceBundledDocument } from "./document/dereference.js";
|
|
3
|
+
import { defaultAdapters } from "../requests/media/adapter.js";
|
|
4
|
+
import { createCodeUsageGeneratorRegistry } from "../requests/generators/index.js";
|
|
5
|
+
import { registerDefault } from "../requests/generators/all.js";
|
|
6
|
+
import { createPageComponents, defaultShikiOptions } from "../packages/shared-api/dist/components/defaults.js";
|
|
7
|
+
import { AuthProvider } from "../playground/auth.js";
|
|
8
|
+
import { ServerProvider } from "./use-server.js";
|
|
9
|
+
import { createContext, use, useMemo } from "react";
|
|
10
|
+
import { getRaw } from "@scalar/json-magic/magic-proxy";
|
|
11
|
+
import { generate } from "@fumari/json-schema-ts";
|
|
12
|
+
import { defaultShikiFactory } from "fumadocs-core/highlight/shiki/full";
|
|
13
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
+
//#region src/utils/create-page.tsx
|
|
15
|
+
const OpenAPIContext = createContext(null);
|
|
16
|
+
const ComponentsContext = createContext(null);
|
|
17
|
+
const OptionsContext = createContext(null);
|
|
18
|
+
/**
|
|
19
|
+
* The runtime of the API page: the document and request options.
|
|
20
|
+
*/
|
|
21
|
+
function useOpenAPI() {
|
|
22
|
+
const ctx = use(OpenAPIContext);
|
|
23
|
+
if (!ctx) throw new Error("Component must be used under <OpenAPIProvider />");
|
|
24
|
+
return ctx;
|
|
25
|
+
}
|
|
26
|
+
function useComponents() {
|
|
27
|
+
const components = use(ComponentsContext);
|
|
28
|
+
if (!components) throw new Error("Component must be used under <OpenAPIProvider />");
|
|
29
|
+
return components;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The render options of the page, available under a page created with `createOpenAPIRenderer()`.
|
|
33
|
+
*/
|
|
34
|
+
function useRenderContext() {
|
|
35
|
+
const ctx = use(OptionsContext);
|
|
36
|
+
if (!ctx) throw new Error("Component must be used under <OpenAPIProvider />");
|
|
37
|
+
return ctx;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Generate TypeScript definitions of a JSON schema, `undefined` when disabled.
|
|
41
|
+
*/
|
|
42
|
+
function useTypeScriptDefinitions(schema, options) {
|
|
43
|
+
const runtime = useOpenAPI();
|
|
44
|
+
const { name, readOnly, writeOnly } = options;
|
|
45
|
+
const result = useMemo(() => {
|
|
46
|
+
if (!schema || !runtime.generateTypeScriptDefinitions) return;
|
|
47
|
+
return runtime.generateTypeScriptDefinitions(schema, {
|
|
48
|
+
name,
|
|
49
|
+
readOnly,
|
|
50
|
+
writeOnly,
|
|
51
|
+
doc: runtime.doc
|
|
52
|
+
});
|
|
53
|
+
}, [
|
|
54
|
+
runtime,
|
|
55
|
+
schema,
|
|
56
|
+
name,
|
|
57
|
+
readOnly,
|
|
58
|
+
writeOnly
|
|
59
|
+
]);
|
|
60
|
+
return result instanceof Promise ? use(result) : result;
|
|
61
|
+
}
|
|
62
|
+
function OpenAPIProvider({ document, mediaAdapters, codeUsages, generateCodeSamples, generateTypeScriptDefinitions, proxyUrl, storageKeyPrefix = "fumadocs-openapi-", shiki, shikiOptions = defaultShikiOptions, showResponseSchema, content, schemaUI, playground, components, children }) {
|
|
63
|
+
const runtime = useMemo(() => ({
|
|
64
|
+
doc: dereferenceBundledDocument(document),
|
|
65
|
+
mediaAdapters: {
|
|
66
|
+
...defaultAdapters,
|
|
67
|
+
...mediaAdapters
|
|
68
|
+
},
|
|
69
|
+
codeUsages,
|
|
70
|
+
generateCodeSamples,
|
|
71
|
+
generateTypeScriptDefinitions,
|
|
72
|
+
proxyUrl,
|
|
73
|
+
storageKeyPrefix
|
|
74
|
+
}), [
|
|
75
|
+
document,
|
|
76
|
+
mediaAdapters,
|
|
77
|
+
codeUsages,
|
|
78
|
+
generateCodeSamples,
|
|
79
|
+
generateTypeScriptDefinitions,
|
|
80
|
+
proxyUrl,
|
|
81
|
+
storageKeyPrefix
|
|
82
|
+
]);
|
|
83
|
+
const render = useMemo(() => ({
|
|
84
|
+
shiki,
|
|
85
|
+
shikiOptions,
|
|
86
|
+
showResponseSchema,
|
|
87
|
+
content,
|
|
88
|
+
schemaUI,
|
|
89
|
+
playground
|
|
90
|
+
}), [
|
|
91
|
+
shiki,
|
|
92
|
+
shikiOptions,
|
|
93
|
+
showResponseSchema,
|
|
94
|
+
content,
|
|
95
|
+
schemaUI,
|
|
96
|
+
playground
|
|
97
|
+
]);
|
|
98
|
+
return /* @__PURE__ */ jsx(OpenAPIContext, {
|
|
99
|
+
value: runtime,
|
|
100
|
+
children: /* @__PURE__ */ jsx(ComponentsContext, {
|
|
101
|
+
value: components,
|
|
102
|
+
children: /* @__PURE__ */ jsx(OptionsContext, {
|
|
103
|
+
value: render,
|
|
104
|
+
children: /* @__PURE__ */ jsx(ServerProvider, {
|
|
105
|
+
servers: runtime.doc.dereferenced.servers,
|
|
106
|
+
storageKeyPrefix: runtime.storageKeyPrefix,
|
|
107
|
+
children: /* @__PURE__ */ jsx(AuthProvider, { children })
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
let defaultCodeUsages;
|
|
114
|
+
const defaultTypeScriptDefinitions = (schema, ctx) => {
|
|
115
|
+
if (typeof schema !== "object") return;
|
|
116
|
+
try {
|
|
117
|
+
return generate({
|
|
118
|
+
...ctx.doc.bundled,
|
|
119
|
+
...getRaw(schema)
|
|
120
|
+
}, {
|
|
121
|
+
name: ctx.name,
|
|
122
|
+
readOnly: ctx.readOnly,
|
|
123
|
+
writeOnly: ctx.writeOnly
|
|
124
|
+
});
|
|
125
|
+
} catch (e) {
|
|
126
|
+
console.warn("Failed to generate typescript schema:", e);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Create `<OpenAPIPage />` from your own UI, it takes the props of generated pages.
|
|
131
|
+
*/
|
|
132
|
+
function createOpenAPIRenderer(options) {
|
|
133
|
+
return createOpenAPIBaseRenderer({
|
|
134
|
+
...options,
|
|
135
|
+
shiki: options.shiki ?? defaultShikiFactory,
|
|
136
|
+
codeUsages: options.codeUsages ?? (defaultCodeUsages ??= registerDefault(createCodeUsageGeneratorRegistry())),
|
|
137
|
+
generateTypeScriptDefinitions: options.generateTypeScriptDefinitions ?? defaultTypeScriptDefinitions
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* `createOpenAPIRenderer()` with nothing built in: it highlights through the `shiki` you pass, and
|
|
142
|
+
* generates code usages and TypeScript definitions only when you pass them.
|
|
143
|
+
*/
|
|
144
|
+
function createOpenAPIBaseRenderer({ components, ...options }) {
|
|
145
|
+
const { Operation, Layout = DefaultLayout } = components;
|
|
146
|
+
const slots = {
|
|
147
|
+
SchemaUI: components.SchemaUI,
|
|
148
|
+
...createPageComponents({
|
|
149
|
+
...options,
|
|
150
|
+
components
|
|
151
|
+
})
|
|
152
|
+
};
|
|
153
|
+
function Content({ showTitle, showDescription, operations, webhooks }) {
|
|
154
|
+
const { dereferenced, resolve } = useOpenAPI().doc;
|
|
155
|
+
const ctx = useRenderContext();
|
|
156
|
+
const layout = {
|
|
157
|
+
operations: operations?.map((item) => {
|
|
158
|
+
const pathItem = resolve(dereferenced.paths?.[item.path]);
|
|
159
|
+
if (!pathItem) throw new Error(`[Fumadocs OpenAPI] Path not found in OpenAPI schema: ${item.path}`);
|
|
160
|
+
const operation = pathItem[item.method];
|
|
161
|
+
if (!operation) throw new Error(`[Fumadocs OpenAPI] Method ${item.method} not found in operation: ${item.path}`);
|
|
162
|
+
return {
|
|
163
|
+
item,
|
|
164
|
+
children: /* @__PURE__ */ jsx(Operation, {
|
|
165
|
+
type: "operation",
|
|
166
|
+
path: item.path,
|
|
167
|
+
method: item.method,
|
|
168
|
+
operation,
|
|
169
|
+
pathItem,
|
|
170
|
+
showTitle,
|
|
171
|
+
showDescription
|
|
172
|
+
}, `${item.path}:${item.method}`)
|
|
173
|
+
};
|
|
174
|
+
}),
|
|
175
|
+
webhooks: webhooks?.map((item) => {
|
|
176
|
+
const pathItem = resolve(dereferenced.webhooks?.[item.name]);
|
|
177
|
+
if (!pathItem) throw new Error(`[Fumadocs OpenAPI] Webhook not found in OpenAPI schema: ${item.name}`);
|
|
178
|
+
const operation = pathItem[item.method];
|
|
179
|
+
if (!operation) throw new Error(`[Fumadocs OpenAPI] Method ${item.method} not found in webhook: ${item.name}`);
|
|
180
|
+
return {
|
|
181
|
+
item,
|
|
182
|
+
children: /* @__PURE__ */ jsx(Operation, {
|
|
183
|
+
type: "webhook",
|
|
184
|
+
path: `/${item.name}`,
|
|
185
|
+
method: item.method,
|
|
186
|
+
operation,
|
|
187
|
+
pathItem,
|
|
188
|
+
showTitle,
|
|
189
|
+
showDescription
|
|
190
|
+
}, `${item.name}:${item.method}`)
|
|
191
|
+
};
|
|
192
|
+
})
|
|
193
|
+
};
|
|
194
|
+
if (ctx.content?.renderPageLayout) return ctx.content.renderPageLayout(layout, ctx);
|
|
195
|
+
return /* @__PURE__ */ jsx(Layout, { ...layout });
|
|
196
|
+
}
|
|
197
|
+
return function OpenAPIPage(props) {
|
|
198
|
+
let document;
|
|
199
|
+
let proxyUrl;
|
|
200
|
+
if ("preloaded" in props) {
|
|
201
|
+
document = props.preloaded.docs[props.document];
|
|
202
|
+
if (!document) throw new Error(`[Fumadocs OpenAPI] the document ${props.document} is not preloaded, make sure to pass the "preloaded" prop to <OpenAPIPage />`);
|
|
203
|
+
proxyUrl = props.preloaded.proxyUrl;
|
|
204
|
+
} else {
|
|
205
|
+
document = props.payload.bundled;
|
|
206
|
+
proxyUrl = props.payload.proxyUrl;
|
|
207
|
+
}
|
|
208
|
+
return /* @__PURE__ */ jsx(OpenAPIProvider, {
|
|
209
|
+
...options,
|
|
210
|
+
document,
|
|
211
|
+
proxyUrl,
|
|
212
|
+
components: slots,
|
|
213
|
+
children: /* @__PURE__ */ jsx(Content, { ...props })
|
|
214
|
+
});
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function DefaultLayout({ operations, webhooks }) {
|
|
218
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [operations?.map((item) => item.children), webhooks?.map((item) => item.children)] });
|
|
219
|
+
}
|
|
220
|
+
//#endregion
|
|
221
|
+
export { createOpenAPIBaseRenderer, createOpenAPIRenderer, useComponents, useOpenAPI, useRenderContext, useTypeScriptDefinitions };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Document } from "../../types.js";
|
|
2
|
-
import {
|
|
2
|
+
import { DereferencedShallow } from "@fumadocs/json-schema";
|
|
3
3
|
//#region src/utils/document/dereference.d.ts
|
|
4
4
|
export interface DereferencedDocument {
|
|
5
5
|
/**
|
|
@@ -13,7 +13,7 @@ export interface DereferencedDocument {
|
|
|
13
13
|
*
|
|
14
14
|
* Non-reference values are returned as-is.
|
|
15
15
|
*/
|
|
16
|
-
resolve: <T>(node: T) =>
|
|
16
|
+
resolve: <T>(node: T) => DereferencedShallow<T>;
|
|
17
17
|
bundled: Document;
|
|
18
18
|
}
|
|
19
19
|
//#endregion
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import { dereferenceShallow } from "@fumadocs/api-docs/schema/dereference";
|
|
2
1
|
import { createMagicProxy } from "@scalar/json-magic/magic-proxy";
|
|
2
|
+
import { dereference } from "@fumadocs/json-schema";
|
|
3
3
|
//#region src/utils/document/dereference.ts
|
|
4
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
4
5
|
function dereferenceBundledDocument(bundled) {
|
|
5
|
-
|
|
6
|
+
const cached = cache.get(bundled);
|
|
7
|
+
if (cached) return cached;
|
|
8
|
+
const doc = {
|
|
6
9
|
bundled,
|
|
7
10
|
dereferenced: createMagicProxy(bundled),
|
|
8
11
|
resolve(node) {
|
|
9
|
-
return
|
|
12
|
+
return dereference(node);
|
|
10
13
|
}
|
|
11
14
|
};
|
|
15
|
+
cache.set(bundled, doc);
|
|
16
|
+
return doc;
|
|
12
17
|
}
|
|
13
18
|
//#endregion
|
|
14
19
|
export { dereferenceBundledDocument };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { upgrade } from "
|
|
2
|
-
import { bundle } from "@fumadocs/
|
|
1
|
+
import { upgrade } from "@scalar/openapi-upgrader";
|
|
2
|
+
import { bundle } from "@fumadocs/json-schema/bundle";
|
|
3
3
|
//#region src/utils/document/load.ts
|
|
4
4
|
/**
|
|
5
5
|
* Process input document to a Fumadocs OpenAPI compatible format
|
|
@@ -1,18 +1,16 @@
|
|
|
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
3
|
import { getRaw } from "@scalar/json-magic/magic-proxy";
|
|
5
|
-
import { sample } from "@fumadocs/
|
|
4
|
+
import { dereference, sample } from "@fumadocs/json-schema";
|
|
6
5
|
//#region src/utils/get-example-requests.ts
|
|
7
|
-
function getExampleRequests({ path, method,
|
|
8
|
-
const requestBody =
|
|
6
|
+
function getExampleRequests({ path, method, mediaAdapters, operation, parameters }) {
|
|
7
|
+
const requestBody = dereference(operation.requestBody);
|
|
9
8
|
const media = requestBody?.content ? getPreferredType(requestBody.content) : null;
|
|
10
|
-
const bodyOfType = media ?
|
|
11
|
-
const parameters = [...operation.parameters ?? [], ...pathItem.parameters ?? []].map(dereferenceShallow);
|
|
9
|
+
const bodyOfType = media ? dereference(requestBody.content[media]) : null;
|
|
12
10
|
if (bodyOfType?.examples) {
|
|
13
11
|
const result = [];
|
|
14
12
|
for (const [key, item] of Object.entries(bodyOfType.examples)) {
|
|
15
|
-
const { summary, description } =
|
|
13
|
+
const { summary, description } = dereference(item);
|
|
16
14
|
const data = getRequestData({
|
|
17
15
|
path,
|
|
18
16
|
body: requestBody,
|
|
@@ -25,7 +23,7 @@ function getExampleRequests({ path, method, ctx, operation, pathItem }) {
|
|
|
25
23
|
name: summary || key,
|
|
26
24
|
description,
|
|
27
25
|
data,
|
|
28
|
-
encoded: encodeRequestData(data,
|
|
26
|
+
encoded: encodeRequestData(data, mediaAdapters, parameters)
|
|
29
27
|
});
|
|
30
28
|
}
|
|
31
29
|
if (result.length > 0) return result;
|
|
@@ -36,13 +34,13 @@ function getExampleRequests({ path, method, ctx, operation, pathItem }) {
|
|
|
36
34
|
method,
|
|
37
35
|
parameters
|
|
38
36
|
});
|
|
39
|
-
const schema =
|
|
37
|
+
const schema = dereference(bodyOfType?.schema);
|
|
40
38
|
return [{
|
|
41
39
|
id: "_default",
|
|
42
40
|
name: "Default",
|
|
43
41
|
description: typeof schema === "object" ? schema.description : void 0,
|
|
44
42
|
data,
|
|
45
|
-
encoded: encodeRequestData(data,
|
|
43
|
+
encoded: encodeRequestData(data, mediaAdapters, parameters)
|
|
46
44
|
}];
|
|
47
45
|
}
|
|
48
46
|
function getRequestData({ method, path, parameters, sampleKey, body }) {
|
|
@@ -59,7 +57,7 @@ function getRequestData({ method, path, parameters, sampleKey, body }) {
|
|
|
59
57
|
if (param.schema) value = sample(param.schema);
|
|
60
58
|
else if (param.content) {
|
|
61
59
|
const type = getPreferredType(param.content);
|
|
62
|
-
const content = type ?
|
|
60
|
+
const content = type ? dereference(param.content[type]) : void 0;
|
|
63
61
|
if (!content || !content.schema) throw new Error(`Cannot find "${param.name}" parameter info for media type "${type}" in ${path} ${method}`);
|
|
64
62
|
value = sample(content.schema);
|
|
65
63
|
}
|
|
@@ -81,8 +79,8 @@ function getRequestData({ method, path, parameters, sampleKey, body }) {
|
|
|
81
79
|
const type = getPreferredType(body.content);
|
|
82
80
|
if (!type) throw new Error(`Cannot find body schema for ${path} ${method}: missing media type`);
|
|
83
81
|
result.bodyMediaType = type;
|
|
84
|
-
const bodyOfType =
|
|
85
|
-
if (bodyOfType.examples && sampleKey) result.body = getRaw(
|
|
82
|
+
const bodyOfType = dereference(body.content[type]);
|
|
83
|
+
if (bodyOfType.examples && sampleKey) result.body = getRaw(dereference(bodyOfType.examples[sampleKey]).value);
|
|
86
84
|
else if (bodyOfType.example) result.body = getRaw(bodyOfType.example);
|
|
87
85
|
else result.body = sample(bodyOfType?.schema ?? {}, {
|
|
88
86
|
skipReadOnly: method !== "get",
|