fumadocs-openapi 10.6.5 → 10.6.6

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/ui/base.d.ts CHANGED
@@ -6,6 +6,7 @@ import { SchemaUIOptions } from "./schema/index.js";
6
6
  import { OpenAPIServer } from "../server/create.js";
7
7
  import { ApiPageProps, OperationItem, WebhookItem } from "./api-page.js";
8
8
  import { ResponseTab } from "./operation/response-tabs.js";
9
+ import { RequestTabsRenderContext } from "./operation/request-tabs.js";
9
10
  import { ClientCodeBlockProvider } from "./components/codeblock.js";
10
11
  import { Awaitable, MethodInformation, RenderContext } from "../types.js";
11
12
  import { NoReference } from "../utils/schema/index.js";
@@ -74,10 +75,7 @@ interface CreateAPIPageOptions {
74
75
  */
75
76
  content?: {
76
77
  renderResponseTabs?: (tabs: ResponseTab[], ctx: RenderContext) => ReactNode;
77
- renderRequestTabs?: (items: ExampleRequestItem[], ctx: RenderContext & {
78
- route: string;
79
- operation: NoReference<MethodInformation>;
80
- }) => ReactNode;
78
+ renderRequestTabs?: (items: ExampleRequestItem[], ctx: RequestTabsRenderContext) => ReactNode;
81
79
  renderAPIExampleLayout?: (slots: {
82
80
  selector: ReactNode;
83
81
  usageTabs: ReactNode;
@@ -142,6 +140,12 @@ interface CreateAPIPageOptions {
142
140
  * @defaultValue true
143
141
  */
144
142
  enabled?: boolean;
143
+ /**
144
+ * render a page-level provider (useful for handling auth)
145
+ */
146
+ provider?: (props: {
147
+ children: ReactNode;
148
+ }) => ReactNode;
145
149
  /**
146
150
  * replace the server-side renderer
147
151
  */
package/dist/ui/base.js CHANGED
@@ -4,6 +4,7 @@ import { encodeInternalRef } from "../utils/schema/ref.js";
4
4
  import { ClientCodeBlockProvider } from "./components/codeblock.js";
5
5
  import { APIPage } from "./api-page.js";
6
6
  import { pickSchema } from "../utils/schema/pick.js";
7
+ import { PlaygroundAuthProvider } from "./client/boundary.lazy.js";
7
8
  import Slugger from "github-slugger";
8
9
  import * as JsxRuntime from "react/jsx-runtime";
9
10
  import { jsx } from "react/jsx-runtime";
@@ -38,6 +39,9 @@ function createAPIPage(server, options) {
38
39
  ...options.shikiOptions
39
40
  }).use(rehypeReact);
40
41
  }
42
+ function renderPlaygroundProviderDefault({ children }) {
43
+ return /* @__PURE__ */ jsx(PlaygroundAuthProvider, { children });
44
+ }
41
45
  function renderPlaygroundDefault({ path, method, ctx }) {
42
46
  return /* @__PURE__ */ jsx(ctx.clientBoundary.PlaygroundClient, {
43
47
  route: path,
@@ -77,6 +81,7 @@ function createAPIPage(server, options) {
77
81
  },
78
82
  playground: {
79
83
  ...options.playground,
84
+ provider: options.playground?.provider ?? renderPlaygroundProviderDefault,
80
85
  render: options.playground?.render ?? renderPlaygroundDefault
81
86
  },
82
87
  renderHeading(depth, text, props) {
@@ -8,6 +8,7 @@ const UsageTabsSelector = wrapLazy(() => import("../operation/usage-tabs/client.
8
8
  const UsageTab = wrapLazy(() => import("../operation/usage-tabs/client.js").then((mod) => ({ default: mod.UsageTab })));
9
9
  const SchemaUI = wrapLazy(() => import("../schema/client.js").then((mod) => ({ default: mod.SchemaUI })));
10
10
  const PlaygroundClient = wrapLazy(() => import("../../playground/client.js"));
11
+ const PlaygroundAuthProvider = wrapLazy(() => import("../../playground/auth.js").then((mod) => ({ default: mod.AuthProvider })));
11
12
  function wrapLazy(load) {
12
13
  const V = lazy(load);
13
14
  return function wrapper(props) {
@@ -15,4 +16,4 @@ function wrapLazy(load) {
15
16
  };
16
17
  }
17
18
  //#endregion
18
- export { ApiProvider, PlaygroundClient, SchemaUI, ServerProvider, UsageTab, UsageTabsSelector };
19
+ export { ApiProvider, PlaygroundAuthProvider, PlaygroundClient, SchemaUI, ServerProvider, UsageTab, UsageTabsSelector };
@@ -5,7 +5,7 @@ function useStorageKey() {
5
5
  const { storageKeyPrefix } = useApiContext().client;
6
6
  return useMemo(() => ({
7
7
  of: (name) => getStorageKey(storageKeyPrefix, name),
8
- AuthField: (field) => getStorageKey(storageKeyPrefix, `auth-${field.original?.id ?? field.fieldName}`)
8
+ AuthField: (schemeId) => getStorageKey(storageKeyPrefix, `auth-${schemeId}`)
9
9
  }), [storageKeyPrefix]);
10
10
  }
11
11
  function getStorageKey(prefix = "fumadocs-openapi-", name) {
@@ -1,5 +1,5 @@
1
1
  import { APIPageClientOptions } from "../client/index.js";
2
- import { RenderContext, ServerObject } from "../../types.js";
2
+ import { RenderContext, SecuritySchemeObject, ServerObject } from "../../types.js";
3
3
  import { ReactNode } from "react";
4
4
  import * as _$react_jsx_runtime0 from "react/jsx-runtime";
5
5
 
@@ -7,11 +7,14 @@ import * as _$react_jsx_runtime0 from "react/jsx-runtime";
7
7
  interface InheritFromContext extends Pick<RenderContext, 'shikiOptions'> {
8
8
  client: APIPageClientOptions;
9
9
  }
10
- type ApiProviderProps = InheritFromContext;
10
+ interface ApiProviderProps extends InheritFromContext {
11
+ schemes: Record<string, SecuritySchemeObject>;
12
+ }
11
13
  declare function ApiProvider({
12
14
  children,
13
15
  shikiOptions,
14
- client
16
+ client,
17
+ schemes
15
18
  }: ApiProviderProps & {
16
19
  children: ReactNode;
17
20
  }): _$react_jsx_runtime0.JSX.Element;
@@ -18,7 +18,7 @@ function useServerContext() {
18
18
  if (!ctx) throw new Error("Component must be used under <ApiProvider />");
19
19
  return ctx;
20
20
  }
21
- function ApiProvider({ children, shikiOptions, client }) {
21
+ function ApiProvider({ children, shikiOptions, client, schemes }) {
22
22
  return /* @__PURE__ */ jsx(ApiContext, {
23
23
  value: useMemo(() => {
24
24
  let codeUsages;
@@ -31,12 +31,17 @@ function ApiProvider({ children, shikiOptions, client }) {
31
31
  shikiOptions,
32
32
  client,
33
33
  codeUsages,
34
+ schemes,
34
35
  mediaAdapters: {
35
36
  ...defaultAdapters,
36
37
  ...client.mediaAdapters
37
38
  }
38
39
  };
39
- }, [client, shikiOptions]),
40
+ }, [
41
+ client,
42
+ schemes,
43
+ shikiOptions
44
+ ]),
40
45
  children
41
46
  });
42
47
  }
@@ -1,6 +1,7 @@
1
1
  import { parseSecurities } from "../utils/schema/index.js";
2
2
  import { defaultAdapters } from "../requests/media/adapter.js";
3
3
  import { ClientCodeBlock, ClientCodeBlockProvider } from "./components/codeblock.js";
4
+ import { AuthProvider } from "../playground/auth.js";
4
5
  import { dereferenceDocument } from "../utils/document/dereference.js";
5
6
  import { APIPage } from "./api-page.js";
6
7
  import { boundary_exports } from "./client/boundary.js";
@@ -66,6 +67,9 @@ function createClientAPIPage({ shiki = defaultShikiFactory, shikiOptions = { the
66
67
  readOnly: false
67
68
  });
68
69
  }
70
+ function renderPlaygroundProviderDefault({ children }) {
71
+ return /* @__PURE__ */ jsx(AuthProvider, { children });
72
+ }
69
73
  return function ClientAPIPage({ payload, ...props }) {
70
74
  const processed = useMemo(() => dereferenceDocument(payload.bundled), [payload.bundled]);
71
75
  const ctx = useMemo(() => ({
@@ -82,6 +86,7 @@ function createClientAPIPage({ shiki = defaultShikiFactory, shikiOptions = { the
82
86
  },
83
87
  playground: {
84
88
  ...options.playground,
89
+ provider: options.playground?.provider ?? renderPlaygroundProviderDefault,
85
90
  render: options.playground?.render ?? renderPlaygroundDefault
86
91
  },
87
92
  renderHeading(depth, text, props) {
@@ -0,0 +1,10 @@
1
+ import { MethodInformation, RenderContext } from "../../types.js";
2
+ import { NoReference } from "../../utils/schema/index.js";
3
+ import { ReactNode } from "react";
4
+ //#region src/ui/operation/request-tabs.d.ts
5
+ interface RequestTabsRenderContext extends RenderContext {
6
+ route: string;
7
+ operation: NoReference<MethodInformation>;
8
+ }
9
+ //#endregion
10
+ export { RequestTabsRenderContext };
@@ -15,41 +15,6 @@ function RequestTabs({ path, operation, examples, ctx }) {
15
15
  });
16
16
  }
17
17
  function renderRequestTabsDefault(items, ctx) {
18
- function renderItem(item) {
19
- const requestData = item.data;
20
- const displayNames = {
21
- body: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(I18nLabel, { label: "titleRequestBody" }), /* @__PURE__ */ jsx("code", {
22
- className: "text-xs text-fd-muted-foreground ms-auto",
23
- children: requestData.bodyMediaType
24
- })] }),
25
- cookie: /* @__PURE__ */ jsx(I18nLabel, { label: "cookieParameters" }),
26
- header: /* @__PURE__ */ jsx(I18nLabel, { label: "headerParameters" }),
27
- query: /* @__PURE__ */ jsx(I18nLabel, { label: "queryParameters" }),
28
- path: /* @__PURE__ */ jsx(I18nLabel, { label: "pathParameters" })
29
- };
30
- return /* @__PURE__ */ jsxs(Fragment, { children: [
31
- item.description && ctx.renderMarkdown(item.description),
32
- /* @__PURE__ */ jsxs("div", {
33
- className: "flex flex-row gap-2 items-center justify-between",
34
- children: [/* @__PURE__ */ jsx(MethodLabel, { children: requestData.method }), /* @__PURE__ */ jsx("code", { children: resolveRequestData(ctx.route, item.encoded) })]
35
- }),
36
- /* @__PURE__ */ jsx(Accordions, {
37
- type: "multiple",
38
- className: "mt-2",
39
- children: Object.entries(displayNames).map(([k, v]) => {
40
- const data = requestData[k];
41
- if (!data || Object.keys(data).length === 0) return;
42
- return /* @__PURE__ */ jsxs(AccordionItem, {
43
- value: k,
44
- children: [/* @__PURE__ */ jsx(AccordionHeader, { children: /* @__PURE__ */ jsx(AccordionTrigger, { children: v }) }), /* @__PURE__ */ jsx(AccordionContent, {
45
- className: "prose-no-margin",
46
- children: ctx.renderCodeBlock("json", JSON.stringify(data, null, 2))
47
- })]
48
- }, k);
49
- })
50
- })
51
- ] });
52
- }
53
18
  let children;
54
19
  if (items.length > 1) children = /* @__PURE__ */ jsxs(Tabs, {
55
20
  defaultValue: items[0].id,
@@ -58,10 +23,16 @@ function renderRequestTabsDefault(items, ctx) {
58
23
  children: item.id === "_default" ? /* @__PURE__ */ jsx(I18nLabel, { label: "requestTabNameDefault" }) : item.name
59
24
  }, item.id)) }), items.map((item) => /* @__PURE__ */ jsx(TabsContent, {
60
25
  value: item.id,
61
- children: renderItem(item)
26
+ children: /* @__PURE__ */ jsx(RequestTabsItem, {
27
+ item,
28
+ ctx
29
+ })
62
30
  }, item.id))]
63
31
  });
64
- else if (items.length === 1) children = renderItem(items[0]);
32
+ else if (items.length === 1) children = /* @__PURE__ */ jsx(RequestTabsItem, {
33
+ item: items[0],
34
+ ctx
35
+ });
65
36
  else children = /* @__PURE__ */ jsx("p", {
66
37
  className: "text-fd-muted-foreground text-xs",
67
38
  children: /* @__PURE__ */ jsx(I18nLabel, { label: "empty" })
@@ -74,5 +45,40 @@ function renderRequestTabsDefault(items, ctx) {
74
45
  }), children]
75
46
  });
76
47
  }
48
+ function RequestTabsItem({ item, ctx }) {
49
+ const requestData = item.data;
50
+ const displayNames = {
51
+ body: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(I18nLabel, { label: "titleRequestBody" }), /* @__PURE__ */ jsx("code", {
52
+ className: "text-xs text-fd-muted-foreground ms-auto",
53
+ children: requestData.bodyMediaType
54
+ })] }),
55
+ cookie: /* @__PURE__ */ jsx(I18nLabel, { label: "cookieParameters" }),
56
+ header: /* @__PURE__ */ jsx(I18nLabel, { label: "headerParameters" }),
57
+ query: /* @__PURE__ */ jsx(I18nLabel, { label: "queryParameters" }),
58
+ path: /* @__PURE__ */ jsx(I18nLabel, { label: "pathParameters" })
59
+ };
60
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
61
+ item.description && ctx.renderMarkdown(item.description),
62
+ /* @__PURE__ */ jsxs("div", {
63
+ className: "flex flex-row gap-2 items-center justify-between",
64
+ children: [/* @__PURE__ */ jsx(MethodLabel, { children: requestData.method }), /* @__PURE__ */ jsx("code", { children: resolveRequestData(ctx.route, item.encoded) })]
65
+ }),
66
+ /* @__PURE__ */ jsx(Accordions, {
67
+ type: "multiple",
68
+ className: "mt-2",
69
+ children: Object.entries(displayNames).map(([k, v]) => {
70
+ const data = requestData[k];
71
+ if (!data || Object.keys(data).length === 0) return;
72
+ return /* @__PURE__ */ jsxs(AccordionItem, {
73
+ value: k,
74
+ children: [/* @__PURE__ */ jsx(AccordionHeader, { children: /* @__PURE__ */ jsx(AccordionTrigger, { children: v }) }), /* @__PURE__ */ jsx(AccordionContent, {
75
+ className: "prose-no-margin",
76
+ children: ctx.renderCodeBlock("json", JSON.stringify(data, null, 2))
77
+ })]
78
+ }, k);
79
+ })
80
+ })
81
+ ] });
82
+ }
77
83
  //#endregion
78
84
  export { RequestTabs };
@@ -1,5 +1,6 @@
1
1
  import { removeUndefined } from "../remove-undefined.js";
2
2
  import { toStaticData } from "./to-static-data.js";
3
+ import { doubleQuote } from "../../requests/string-utils.js";
3
4
  import { dump } from "js-yaml";
4
5
  //#region src/utils/pages/to-text.ts
5
6
  function toText(entry, processed, options = {}) {
@@ -36,7 +37,7 @@ function generateDocument(frontmatter, content, options) {
36
37
  commentContent = commentContent.replaceAll("/", "\\/");
37
38
  out.push(`{/* ${commentContent} */}`);
38
39
  }
39
- if (imports) out.push(...imports.map((item) => `import { ${item.names.join(", ")} } from ${JSON.stringify(item.from)};`).join("\n"));
40
+ if (imports) out.push(...imports.map((item) => `import { ${item.names.join(", ")} } from ${doubleQuote(item.from)};`).join("\n"));
40
41
  out.push(content);
41
42
  return out.join("\n\n");
42
43
  }
@@ -70,7 +71,7 @@ function generatePage(schemaId, processed, pageProps, options, context) {
70
71
  }, content.join("\n\n"), options);
71
72
  }
72
73
  function pageContent({ showTitle, showDescription, document, webhooks, operations }) {
73
- const propStrs = [`document={${JSON.stringify(document)}}`];
74
+ const propStrs = [`document={${doubleQuote(document)}}`];
74
75
  if (webhooks) propStrs.push(`webhooks={${JSON.stringify(webhooks.map((item) => ({
75
76
  name: item.name,
76
77
  method: item.method
@@ -79,8 +80,8 @@ function pageContent({ showTitle, showDescription, document, webhooks, operation
79
80
  path: item.path,
80
81
  method: item.method
81
82
  })))}}`);
82
- if (showTitle) propStrs.push(`showTitle={${JSON.stringify(showTitle)}}`);
83
- if (showDescription) propStrs.push(`showDescription={${JSON.stringify(showDescription)}}`);
83
+ if (showTitle) propStrs.push(`showTitle`);
84
+ if (showDescription) propStrs.push(`showDescription`);
84
85
  return `<APIPage ${propStrs.join(" ")} />`;
85
86
  }
86
87
  //#endregion
@@ -1,4 +1,4 @@
1
- import { ReferenceObject, SecuritySchemeObject } from "../../types.js";
1
+ import { ReferenceObject } from "../../types.js";
2
2
  import { JSONSchema } from "json-schema-typed/draft-2020-12";
3
3
 
4
4
  //#region src/utils/schema/index.d.ts
@@ -6,10 +6,9 @@ type NoReference<T> = T extends (infer I)[] ? NoReference<I>[] : T extends Refer
6
6
  type ParsedSchema = (JSONSchema & {
7
7
  'x-playground-lazy'?: boolean;
8
8
  }) | boolean;
9
- /** parsed security scheme objects */
10
- type SecurityEntry = SecuritySchemeObject & {
9
+ interface SecurityEntry {
11
10
  scopes: string[];
12
11
  id: string;
13
- };
12
+ }
14
13
  //#endregion
15
14
  export { NoReference, ParsedSchema, SecurityEntry };
@@ -51,15 +51,10 @@ function parseSecurities(method, dereferenced) {
51
51
  if (security.length === 0) return result;
52
52
  for (const map of security) {
53
53
  const list = [];
54
- for (const [key, scopes] of Object.entries(map)) {
55
- const scheme = dereferenced.components?.securitySchemes?.[key];
56
- if (!scheme) continue;
57
- list.push({
58
- ...scheme,
59
- scopes,
60
- id: key
61
- });
62
- }
54
+ for (const [key, scopes] of Object.entries(map)) list.push({
55
+ id: key,
56
+ scopes
57
+ });
63
58
  if (list.length > 0) result.push(list);
64
59
  }
65
60
  return result;
@@ -12,9 +12,10 @@ function useQuery(fn) {
12
12
  error,
13
13
  start(...input) {
14
14
  setLoading(true);
15
- fnRef.current(...input).then((res) => {
15
+ return fnRef.current(...input).then((res) => {
16
16
  setData(res);
17
17
  setError(void 0);
18
+ return res;
18
19
  }).catch((err) => {
19
20
  setData(void 0);
20
21
  setError(err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-openapi",
3
- "version": "10.6.5",
3
+ "version": "10.6.6",
4
4
  "description": "Generate MDX docs for your OpenAPI spec",
5
5
  "keywords": [
6
6
  "Docs",
@@ -65,7 +65,7 @@
65
65
  "remark-rehype": "^11.1.2",
66
66
  "tailwind-merge": "^3.5.0",
67
67
  "xml-js": "^1.6.11",
68
- "@fumari/stf": "1.0.3"
68
+ "@fumari/stf": "1.0.4"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@scalar/api-client-react": "^1.4.15",