fumadocs-openapi 5.0.1 → 5.0.3

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.js CHANGED
@@ -202,16 +202,19 @@ function pageContent(doc, props) {
202
202
  heading: structuredData.headings.at(-1)?.id
203
203
  });
204
204
  }
205
+ // modify toc and structured data if possible
206
+ // it may not be compatible with other content sources except Fumadocs MDX
207
+ // TODO: Maybe add to frontmatter and let developers to handle them?
205
208
  return `<APIPage operations={${JSON.stringify(props.operations)}} hasHead={${JSON.stringify(props.hasHead)}} />
206
209
 
207
210
  export function startup() {
208
- if (toc) {
211
+ if (typeof toc !== 'undefined') {
209
212
  // toc might be immutable
210
213
  while (toc.length > 0) toc.pop()
211
214
  toc.push(...${JSON.stringify(toc)})
212
215
  }
213
216
 
214
- if (structuredData) {
217
+ if (typeof structuredData !== 'undefined') {
215
218
  structuredData.headings = ${JSON.stringify(structuredData.headings)}
216
219
  structuredData.contents = ${JSON.stringify(structuredData.contents)}
217
220
  }
@@ -28,7 +28,28 @@ function getPreferredType(body) {
28
28
  return typeof value === 'string' ? value : JSON.stringify(value, null, 2);
29
29
  }
30
30
 
31
- function generateSample(path, method, baseUrl) {
31
+ function getSecurities(requirement, document) {
32
+ const results = [];
33
+ const schemas = document.components?.securitySchemes ?? {};
34
+ for (const [key, scopes] of Object.entries(requirement)){
35
+ if (!(key in schemas)) return [];
36
+ const schema = noRef(schemas[key]);
37
+ results.push({
38
+ ...schema,
39
+ scopes
40
+ });
41
+ }
42
+ return results;
43
+ }
44
+ function getSecurityPrefix(security) {
45
+ if (security.type === 'http') return ({
46
+ basic: 'Basic',
47
+ bearer: 'Bearer'
48
+ })[security.scheme];
49
+ if (security.type === 'oauth2') return 'Bearer';
50
+ }
51
+
52
+ function generateSample(path, method, { baseUrl, document }) {
32
53
  const params = [];
33
54
  const responses = {};
34
55
  for (const param of method.parameters){
@@ -51,6 +72,20 @@ function generateSample(path, method, baseUrl) {
51
72
  });
52
73
  }
53
74
  }
75
+ const requirements = method.security ?? document.security;
76
+ if (requirements && requirements.length > 0) {
77
+ for (const security of getSecurities(requirements[0], document)){
78
+ const prefix = getSecurityPrefix(security);
79
+ params.push({
80
+ name: 'Authorization',
81
+ schema: {
82
+ type: 'string'
83
+ },
84
+ sample: prefix ? `${prefix} <token>` : '<token>',
85
+ in: 'header'
86
+ });
87
+ }
88
+ }
54
89
  let bodyOutput;
55
90
  if (method.requestBody) {
56
91
  const body = noRef(method.requestBody).content;
@@ -199,7 +234,7 @@ func main() {
199
234
  ${Array.from(variables.entries()).map(([k, v])=>` ${k} := ${v}`).join('\n')}
200
235
  ${additional.join('\n ')}
201
236
  req, _ := http.NewRequest("${endpoint.method}", url, ${variables.has('payload') ? 'payload' : 'nil'})
202
- ${Array.from(headers.entries()).map(([key, value])=>`req.Header.Add("${key}", ${value})`).join('\n')}
237
+ ${Array.from(headers.entries()).map(([key, value])=>`req.Header.Add("${key}", ${value})`).join('\n ')}
203
238
  res, _ := http.DefaultClient.Do(req)
204
239
  defer res.Body.Close()
205
240
  body, _ := ioutil.ReadAll(res.Body)
@@ -220,20 +255,6 @@ async function getTypescriptSchema(endpoint, code) {
220
255
  }
221
256
  }
222
257
 
223
- function getScheme(requirement, document) {
224
- const results = [];
225
- const schemas = document.components?.securitySchemes ?? {};
226
- for (const [key, scopes] of Object.entries(requirement)){
227
- if (!(key in schemas)) return [];
228
- const schema = noRef(schemas[key]);
229
- results.push({
230
- ...schema,
231
- scopes
232
- });
233
- }
234
- return results;
235
- }
236
-
237
258
  function Playground({ path, method, ctx }) {
238
259
  let currentId = 0;
239
260
  const bodyContent = noRef(method.requestBody)?.content;
@@ -266,7 +287,7 @@ function getAuthorizationField(method, ctx) {
266
287
  if (security.length === 0) return;
267
288
  const singular = security.find((requirements)=>Object.keys(requirements).length === 1);
268
289
  if (!singular) return;
269
- const scheme = getScheme(singular, ctx.document)[0];
290
+ const scheme = getSecurities(singular, ctx.document)[0];
270
291
  return {
271
292
  type: 'string',
272
293
  name: 'Authorization',
@@ -676,7 +697,7 @@ function Operation({ path, method, ctx, hasHead }) {
676
697
  }, "body"));
677
698
  }
678
699
  const parameterGroups = new Map();
679
- const endpoint = generateSample(path, method, ctx.baseUrl);
700
+ const endpoint = generateSample(path, method, ctx);
680
701
  for (const param of method.parameters){
681
702
  const pInfo = endpoint.parameters.find((item)=>item.name === param.name && item.in === param.in);
682
703
  if (!pInfo) continue;
@@ -778,15 +799,12 @@ function AuthSection({ ctx: { document, renderer }, requirements }) {
778
799
  let id = 0;
779
800
  const info = [];
780
801
  for (const requirement of requirements){
781
- if (info.length > 0) info.push(`---`);
782
- for (const schema of getScheme(requirement, document)){
802
+ for (const schema of getSecurities(requirement, document)){
803
+ const prefix = getSecurityPrefix(schema);
783
804
  if (schema.type === 'http') {
784
805
  info.push(/*#__PURE__*/ jsxs(renderer.Property, {
785
806
  name: "Authorization",
786
- type: {
787
- basic: 'Basic <token>',
788
- bearer: 'Bearer <token>'
789
- }[schema.scheme] ?? '<token>',
807
+ type: prefix ? `${prefix} <token>` : '<token>',
790
808
  required: true,
791
809
  children: [
792
810
  schema.description ? /*#__PURE__*/ jsx(Markdown, {
@@ -806,7 +824,7 @@ function AuthSection({ ctx: { document, renderer }, requirements }) {
806
824
  if (schema.type === 'oauth2') {
807
825
  info.push(/*#__PURE__*/ jsxs(renderer.Property, {
808
826
  name: "Authorization",
809
- type: "Bearer <token>",
827
+ type: prefix ? `${prefix} <token>` : '<token>',
810
828
  required: true,
811
829
  children: [
812
830
  schema.description ? /*#__PURE__*/ jsx(Markdown, {
@@ -820,15 +838,14 @@ function AuthSection({ ctx: { document, renderer }, requirements }) {
820
838
  })
821
839
  ]
822
840
  }),
823
- /*#__PURE__*/ jsxs("p", {
841
+ schema.scopes.length > 0 ? /*#__PURE__*/ jsxs("p", {
824
842
  children: [
825
- "Scope:",
826
- ' ',
843
+ "Scope: ",
827
844
  /*#__PURE__*/ jsx("code", {
828
- children: schema.scopes.length > 0 ? schema.scopes.join(', ') : 'none'
845
+ children: schema.scopes.join(', ')
829
846
  })
830
847
  ]
831
- })
848
+ }) : null
832
849
  ]
833
850
  }, id++));
834
851
  }
@@ -27,14 +27,20 @@ async function initHighlighter() {
27
27
  loadWasm: getWasm
28
28
  });
29
29
  }
30
+ let highlighterInstance;
30
31
  function ApiProvider({ defaultBaseUrl, children }) {
31
32
  const [highlighter, setHighlighter] = useState(null);
32
33
  const [baseUrl, setBaseUrl] = useState(defaultBaseUrl);
33
34
  useEffect(()=>{
34
35
  setBaseUrl((prev)=>localStorage.getItem('apiBaseUrl') ?? prev);
35
- void initHighlighter().then((res)=>{
36
- setHighlighter(res);
37
- });
36
+ if (highlighterInstance) {
37
+ setHighlighter(highlighterInstance);
38
+ } else {
39
+ void initHighlighter().then((res)=>{
40
+ highlighterInstance = res;
41
+ setHighlighter(res);
42
+ });
43
+ }
38
44
  }, []);
39
45
  useEffect(()=>{
40
46
  if (baseUrl) localStorage.setItem('apiBaseUrl', baseUrl);
@@ -58,7 +64,7 @@ function useSchemaContext() {
58
64
  return ctx;
59
65
  }
60
66
 
61
- const APIPlayground = dynamic(()=>import('./playground-client-Cn3a3hra.js').then((mod)=>mod.APIPlayground));
67
+ const APIPlayground = dynamic(()=>import('./playground-client-CjCikhf6.js').then((mod)=>mod.APIPlayground));
62
68
  function Root({ children, baseUrl, className, ...props }) {
63
69
  return /*#__PURE__*/ jsx("div", {
64
70
  className: cn('flex flex-col gap-24 text-sm text-fd-muted-foreground', className),
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 { C as CopyRouteButton } from './client-client-CMzXLiVt.js';
7
- export { A as APIPlayground, R as Root, u as useSchemaContext } from './client-client-CMzXLiVt.js';
6
+ import { C as CopyRouteButton } from './client-client-BIwAR59x.js';
7
+ export { A as APIPlayground, R as Root, u as useSchemaContext } from './client-client-BIwAR59x.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,7 @@ function APIInfo({ children, className, route, badgeClassname, method = 'GET', .
59
59
  ...props,
60
60
  children: [
61
61
  /*#__PURE__*/ jsxs("div", {
62
- className: cn('sticky top-24 z-[2] mb-4 flex flex-row items-center gap-2 rounded-lg border bg-fd-card p-3 md:top-10'),
62
+ className: cn('sticky top-24 z-20 mb-4 flex flex-row items-center gap-2 rounded-lg border bg-fd-card px-3 py-2 md:top-12 lg:top-1'),
63
63
  children: [
64
64
  /*#__PURE__*/ jsx("span", {
65
65
  className: cn(badgeVariants({
@@ -85,7 +85,7 @@ function APIInfo({ children, className, route, badgeClassname, method = 'GET', .
85
85
  }
86
86
  function API({ className, children, ...props }) {
87
87
  return /*#__PURE__*/ jsx("div", {
88
- className: cn('flex flex-col gap-x-6 gap-y-2 xl:flex-row xl:items-start', className),
88
+ className: cn('flex flex-col gap-x-6 gap-y-4 xl:flex-row xl:items-start', className),
89
89
  ...props,
90
90
  children: children
91
91
  });
@@ -124,7 +124,7 @@ function Property({ name, type, required, deprecated, children }) {
124
124
  }
125
125
  function APIExample({ children, className, ...props }) {
126
126
  return /*#__PURE__*/ jsx("div", {
127
- className: cn('sticky top-10 prose-no-margin xl:w-[400px]', className),
127
+ className: cn('prose-no-margin md:sticky md:top-12 lg:top-1 xl:w-[400px]', className),
128
128
  ...props,
129
129
  children: children
130
130
  });
@@ -6,7 +6,7 @@ import { FormProvider, Controller, useFormContext, useFieldArray, useForm, useWa
6
6
  import useSWRImmutable from 'swr/immutable';
7
7
  import { Accordions, Accordion } from 'fumadocs-ui/components/accordion';
8
8
  import { cn, buttonVariants } from 'fumadocs-ui/components/api';
9
- import { u as useSchemaContext, a as useApiContext, S as SchemaContext } from './client-client-CMzXLiVt.js';
9
+ import { u as useSchemaContext, a as useApiContext, S as SchemaContext } from './client-client-BIwAR59x.js';
10
10
  import { Slot } from '@radix-ui/react-slot';
11
11
  import { cva } from 'class-variance-authority';
12
12
  import { CircleCheckIcon, CircleXIcon, ChevronDown, ChevronUp, Check, Trash2, Plus } from 'lucide-react';
@@ -343,7 +343,7 @@ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
343
343
  const Input = /*#__PURE__*/ React.forwardRef(({ className, type, ...props }, ref)=>{
344
344
  return /*#__PURE__*/ jsx("input", {
345
345
  type: type,
346
- className: cn('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', className),
346
+ className: cn('flex h-9 w-full rounded-md border bg-transparent px-2 py-1.5 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', className),
347
347
  ref: ref,
348
348
  ...props
349
349
  });
@@ -919,7 +919,7 @@ function APIPlayground({ route, method = 'GET', bodyType, authorization, path =
919
919
  schemas
920
920
  ]),
921
921
  children: /*#__PURE__*/ jsxs("form", {
922
- className: "not-prose flex flex-col gap-5 rounded-lg border bg-fd-card p-4",
922
+ className: "not-prose flex flex-col gap-5 rounded-lg border bg-fd-card p-3",
923
923
  onSubmit: onSubmit,
924
924
  children: [
925
925
  /*#__PURE__*/ jsxs("div", {
@@ -941,7 +941,7 @@ function APIPlayground({ route, method = 'GET', bodyType, authorization, path =
941
941
  authorization ? renderCustomField('authorization', authorization, fields.auth) : null,
942
942
  /*#__PURE__*/ jsxs(Accordions, {
943
943
  type: "multiple",
944
- className: cn('-m-4 border-0 text-sm', path.length === 0 && query.length === 0 && header.length === 0 && !body && 'hidden'),
944
+ className: cn('-m-3 border-0 text-sm', path.length === 0 && query.length === 0 && header.length === 0 && !body && 'hidden'),
945
945
  children: [
946
946
  path.length > 0 ? /*#__PURE__*/ jsx(Accordion, {
947
947
  title: "Path",
@@ -998,7 +998,7 @@ function RouteDisplay({ route }) {
998
998
  pathInput
999
999
  ]);
1000
1000
  return /*#__PURE__*/ jsx("code", {
1001
- className: "flex-1 overflow-auto text-nowrap rounded-lg border bg-fd-muted px-3 py-1.5 text-sm text-fd-muted-foreground",
1001
+ className: "flex-1 overflow-auto text-nowrap rounded-lg border bg-fd-muted px-2 py-1.5 text-sm text-fd-muted-foreground",
1002
1002
  children: pathname
1003
1003
  });
1004
1004
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-openapi",
3
- "version": "5.0.1",
3
+ "version": "5.0.3",
4
4
  "description": "Generate MDX docs for your OpenAPI spec",
5
5
  "keywords": [
6
6
  "NextJs",
@@ -60,8 +60,8 @@
60
60
  "remark": "^15.0.0",
61
61
  "shiki": "^1.11.1",
62
62
  "swr": "^2.2.5",
63
- "fumadocs-core": "13.1.0",
64
- "fumadocs-ui": "13.1.0"
63
+ "fumadocs-core": "13.2.0",
64
+ "fumadocs-ui": "13.2.0"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@types/js-yaml": "^4.0.9",