fumadocs-openapi 10.9.0 → 10.10.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.
Files changed (29) hide show
  1. package/css/generated/shared.css +64 -44
  2. package/dist/index.d.ts +2 -2
  3. package/dist/node_modules/.pnpm/{@scalar_openapi-upgrader@0.2.7 → @scalar_openapi-upgrader@0.2.8}/node_modules/@scalar/openapi-upgrader/dist/2.0-to-3.0/upgrade-from-two-to-three.js +1 -1
  4. package/dist/node_modules/.pnpm/{@scalar_openapi-upgrader@0.2.7 → @scalar_openapi-upgrader@0.2.8}/node_modules/@scalar/openapi-upgrader/dist/3.0-to-3.1/upgrade-from-three-to-three-one.js +1 -1
  5. package/dist/node_modules/.pnpm/{@scalar_openapi-upgrader@0.2.7 → @scalar_openapi-upgrader@0.2.8}/node_modules/@scalar/openapi-upgrader/dist/3.1-to-3.2/upgrade-from-three-one-to-three-two.js +1 -1
  6. package/dist/node_modules/.pnpm/{@scalar_openapi-upgrader@0.2.7 → @scalar_openapi-upgrader@0.2.8}/node_modules/@scalar/openapi-upgrader/dist/helpers/traverse.js +1 -1
  7. package/dist/node_modules/.pnpm/{@scalar_openapi-upgrader@0.2.7 → @scalar_openapi-upgrader@0.2.8}/node_modules/@scalar/openapi-upgrader/dist/upgrade.js +1 -1
  8. package/dist/node_modules/.pnpm/pathe@2.0.3/node_modules/pathe/dist/index.js +1 -1
  9. package/dist/scalar/client.js +1 -4
  10. package/dist/server/index.d.ts +2 -9
  11. package/dist/server/index.js +9 -9
  12. package/dist/types.d.ts +1 -4
  13. package/dist/ui/base.d.ts +1 -0
  14. package/dist/ui/base.js +1 -17
  15. package/dist/ui/components/accordion.js +36 -11
  16. package/dist/ui/components/dialog.js +29 -32
  17. package/dist/ui/components/heading.js +14 -0
  18. package/dist/ui/components/select-tab.js +24 -9
  19. package/dist/ui/create-client.js +0 -17
  20. package/dist/ui/operation/client.js +1 -1
  21. package/dist/ui/operation/index.js +145 -141
  22. package/dist/ui/schema/client.js +339 -289
  23. package/dist/utils/auto-anchor.client.js +20 -0
  24. package/dist/utils/auto-anchor.js +17 -0
  25. package/dist/utils/document/process.js +1 -1
  26. package/dist/utils/merge-refs.js +11 -0
  27. package/dist/utils/pages/builder.d.ts +1 -3
  28. package/dist/utils/pages/builder.js +1 -9
  29. package/package.json +12 -12
@@ -23,11 +23,12 @@ function createOpenAPI(options = {}) {
23
23
  async function getVirtualFiles(server, options) {
24
24
  const { baseDir = "", meta = false } = options;
25
25
  const { createAutoPreset } = await import("../utils/pages/preset-auto.js");
26
- const { fromServer } = await import("../utils/pages/builder.js");
26
+ const { fromSchema } = await import("../utils/pages/builder.js");
27
27
  const files = [];
28
- const entries = await fromServer(server, createAutoPreset(options));
29
- for (const [schemaId, list] of Object.entries(entries)) {
30
- const processed = await server.getSchema(schemaId);
28
+ const schemas = await server.getSchemas();
29
+ const builderOptions = createAutoPreset(options);
30
+ for (const [id, schema] of Object.entries(schemas)) {
31
+ onEntries(fromSchema(id, schema, builderOptions));
31
32
  function onEntry(entry) {
32
33
  const props = getPageProps(entry);
33
34
  files.push({
@@ -41,7 +42,7 @@ function createOpenAPI(options = {}) {
41
42
  async getClientAPIPageProps() {
42
43
  return {
43
44
  payload: {
44
- bundled: processed.bundled,
45
+ bundled: schema.bundled,
45
46
  proxyUrl: server.options.proxyUrl
46
47
  },
47
48
  ...props
@@ -49,11 +50,11 @@ function createOpenAPI(options = {}) {
49
50
  },
50
51
  getSchema() {
51
52
  return {
52
- id: schemaId,
53
- ...processed
53
+ id,
54
+ ...schema
54
55
  };
55
56
  },
56
- ...toStaticData(props, processed.dereferenced),
57
+ ...toStaticData(props, schema.dereferenced),
57
58
  _openapi: {
58
59
  method: entry.type === "operation" || entry.type === "webhook" ? entry.item.method : void 0,
59
60
  webhook: entry.type === "webhook",
@@ -92,7 +93,6 @@ function createOpenAPI(options = {}) {
92
93
  }
93
94
  });
94
95
  }
95
- onEntries(list);
96
96
  }
97
97
  return files;
98
98
  }
package/dist/types.d.ts CHANGED
@@ -6,7 +6,7 @@ import { CreateAPIPageOptions } from "./ui/base.js";
6
6
  import { OpenAPIOptions } from "./server/index.js";
7
7
  import { NoReference } from "./utils/schema/index.js";
8
8
  import { DereferencedDocument } from "./utils/document/dereference.js";
9
- import { HTMLAttributes, ReactNode } from "react";
9
+ import { ReactNode } from "react";
10
10
 
11
11
  //#region src/types.d.ts
12
12
  type Document = OpenAPIV3_2.Document;
@@ -39,9 +39,6 @@ interface RenderContext extends Pick<OpenAPIOptions, 'proxyUrl'>, Omit<RequireKe
39
39
  schema: DereferencedDocument;
40
40
  clientBoundary: typeof boundary_d_exports;
41
41
  mediaAdapters: Record<string, MediaAdapter>;
42
- renderHeading: (depth: number, text: string | ReactNode, props?: HTMLAttributes<HTMLHeadingElement> & {
43
- id?: string;
44
- }) => ReactNode;
45
42
  renderCodeBlock: (lang: string, code: string) => ReactNode;
46
43
  }
47
44
  type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
package/dist/ui/base.d.ts CHANGED
@@ -151,6 +151,7 @@ interface CreateAPIPageOptions {
151
151
  */
152
152
  render?: (props: APIPlaygroundProps) => ReactNode;
153
153
  };
154
+ /** @deprecated no longer used */
154
155
  renderHeading?: (props: HTMLAttributes<HTMLHeadingElement>, depth: number) => ReactNode;
155
156
  renderCodeBlock?: (props: {
156
157
  lang: string;
package/dist/ui/base.js CHANGED
@@ -8,7 +8,6 @@ import { PlaygroundAuthProvider } from "./client/boundary.lazy.js";
8
8
  import Slugger from "github-slugger";
9
9
  import * as JsxRuntime from "react/jsx-runtime";
10
10
  import { jsx } from "react/jsx-runtime";
11
- import { Heading } from "fumadocs-ui/components/heading";
12
11
  import { createRehypeCode } from "fumadocs-core/mdx-plugins/rehype-code.core";
13
12
  import { remarkGfm } from "fumadocs-core/mdx-plugins/remark-gfm";
14
13
  import defaultMdxComponents from "fumadocs-ui/mdx";
@@ -62,7 +61,7 @@ function createAPIPage(server, options) {
62
61
  let processed;
63
62
  if (typeof document === "string") processed = await server.getSchema(document);
64
63
  else processed = document;
65
- const slugger = new Slugger();
64
+ new Slugger();
66
65
  const { ApiProvider, PlaygroundClient, SchemaUI, ServerProvider, UsageTab, UsageTabsSelector } = await import("./client/boundary.lazy.js");
67
66
  const ctx = {
68
67
  schema: processed,
@@ -85,21 +84,6 @@ function createAPIPage(server, options) {
85
84
  provider: options.playground?.provider ?? renderPlaygroundProviderDefault,
86
85
  render: options.playground?.render ?? renderPlaygroundDefault
87
86
  },
88
- renderHeading(depth, text, props) {
89
- const id = typeof text === "string" ? slugger.slug(text) : props?.id;
90
- if (!id) throw new Error("missing 'id' for non-string children");
91
- if (options.renderHeading) return options.renderHeading({
92
- id,
93
- children: text,
94
- ...props
95
- }, depth);
96
- return /* @__PURE__ */ jsx(Heading, {
97
- id,
98
- as: `h${depth}`,
99
- ...props,
100
- children: text
101
- }, id);
102
- },
103
87
  generateTypeScriptDefinitions: options.generateTypeScriptDefinitions ?? ((schema, ctx) => {
104
88
  if (options.generateTypeScriptSchema && ctx._internal_legacy) {
105
89
  const { statusCode, contentType } = ctx._internal_legacy;
@@ -1,27 +1,52 @@
1
1
  "use client";
2
2
  import { cn } from "../../utils/cn.js";
3
+ import { anchorIdStartsWith } from "../../utils/auto-anchor.js";
4
+ import { AnchorSection, useAnchorId } from "../../utils/auto-anchor.client.js";
5
+ import { createContext, use, useEffect, useMemo, useState } from "react";
3
6
  import { jsx, jsxs } from "react/jsx-runtime";
4
7
  import { ChevronRight } from "lucide-react";
5
8
  import * as Primitive from "@radix-ui/react-accordion";
6
9
  //#region src/ui/components/accordion.tsx
7
- function Accordions(props) {
8
- return /* @__PURE__ */ jsx(Primitive.Root, {
9
- ...props,
10
- className: cn("divide-y divide-fd-border", props.className)
10
+ const Context = createContext(null);
11
+ function Accordions({ type, defaultValue, ...props }) {
12
+ const [value, setValue] = useState(() => type === "multiple" ? defaultValue ?? [] : defaultValue ?? "");
13
+ return /* @__PURE__ */ jsx(Context, {
14
+ value: useMemo(() => ({
15
+ type,
16
+ setValue
17
+ }), [type]),
18
+ children: /* @__PURE__ */ jsx(Primitive.Root, {
19
+ type,
20
+ value,
21
+ onValueChange: setValue,
22
+ ...props
23
+ })
11
24
  });
12
25
  }
13
- function AccordionItem(props) {
14
- return /* @__PURE__ */ jsx(Primitive.Item, {
15
- ...props,
16
- className: cn("scroll-m-20", props.className),
17
- children: props.children
26
+ function AccordionItem({ value, className, anchorSegments, ...props }) {
27
+ const ctx = use(Context);
28
+ const id = useAnchorId(anchorSegments ?? false);
29
+ useEffect(() => {
30
+ if (id && anchorIdStartsWith(window.location.hash.slice(1), id)) ctx.setValue(ctx.type === "single" ? value : [value]);
31
+ }, [
32
+ value,
33
+ id,
34
+ ctx
35
+ ]);
36
+ const content = /* @__PURE__ */ jsx(Primitive.Item, {
37
+ value,
38
+ className: cn("scroll-m-20 border-b last:border-b-0", className),
39
+ ...props
18
40
  });
41
+ return anchorSegments ? /* @__PURE__ */ jsx(AnchorSection, {
42
+ segments: anchorSegments,
43
+ children: content
44
+ }) : content;
19
45
  }
20
46
  function AccordionContent(props) {
21
47
  return /* @__PURE__ */ jsx(Primitive.Content, {
22
48
  ...props,
23
- className: cn("overflow-hidden data-[state=closed]:animate-fd-accordion-up data-[state=open]:animate-fd-accordion-down", props.className),
24
- children: props.children
49
+ className: cn("overflow-hidden data-[state=closed]:animate-fd-accordion-up data-[state=open]:animate-fd-accordion-down", props.className)
25
50
  });
26
51
  }
27
52
  function AccordionHeader(props) {
@@ -1,7 +1,6 @@
1
1
  "use client";
2
2
  import { useTranslations } from "../client/i18n.js";
3
3
  import { cn } from "../../utils/cn.js";
4
- import * as React from "react";
5
4
  import { jsx, jsxs } from "react/jsx-runtime";
6
5
  import { X } from "lucide-react";
7
6
  import { buttonVariants } from "fumadocs-ui/components/ui/button";
@@ -10,13 +9,14 @@ import * as DialogPrimitive from "@radix-ui/react-dialog";
10
9
  const Dialog = DialogPrimitive.Root;
11
10
  const DialogTrigger = DialogPrimitive.Trigger;
12
11
  const DialogPortal = DialogPrimitive.Portal;
13
- const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Overlay, {
14
- ref,
15
- className: cn("fixed inset-0 z-50 bg-black/30 backdrop-blur-sm data-[state=open]:animate-fd-fade-in data-[state=closed]:animate-fd-fade-out", className),
16
- ...props
17
- }));
18
- DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
19
- const DialogContent = React.forwardRef(({ className, children, ...props }, ref) => {
12
+ function DialogOverlay({ className, ref, ...props }) {
13
+ return /* @__PURE__ */ jsx(DialogPrimitive.Overlay, {
14
+ ref,
15
+ className: cn("fixed inset-0 z-50 bg-black/30 backdrop-blur-sm data-[state=open]:animate-fd-fade-in data-[state=closed]:animate-fd-fade-out", className),
16
+ ...props
17
+ });
18
+ }
19
+ function DialogContent({ className, children, ref, ...props }) {
20
20
  const t = useTranslations();
21
21
  return /* @__PURE__ */ jsxs(DialogPortal, { children: [/* @__PURE__ */ jsx(DialogOverlay, {}), /* @__PURE__ */ jsxs(DialogPrimitive.Content, {
22
22
  ref,
@@ -31,29 +31,26 @@ const DialogContent = React.forwardRef(({ className, children, ...props }, ref)
31
31
  children: /* @__PURE__ */ jsx(X, {})
32
32
  })]
33
33
  })] });
34
- });
35
- DialogContent.displayName = DialogPrimitive.Content.displayName;
36
- const DialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
37
- className: cn("flex flex-col gap-1.5 text-center sm:text-start", className),
38
- ...props
39
- });
40
- DialogHeader.displayName = "DialogHeader";
41
- const DialogFooter = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
42
- className: cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-2", className),
43
- ...props
44
- });
45
- DialogFooter.displayName = "DialogFooter";
46
- const DialogTitle = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Title, {
47
- ref,
48
- className: cn("text-lg font-semibold leading-none tracking-tight", className),
49
- ...props
50
- }));
51
- DialogTitle.displayName = DialogPrimitive.Title.displayName;
52
- const DialogDescription = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Description, {
53
- ref,
54
- className: cn("text-sm text-fd-muted-foreground", className),
55
- ...props
56
- }));
57
- DialogDescription.displayName = DialogPrimitive.Description.displayName;
34
+ }
35
+ function DialogHeader({ className, ...props }) {
36
+ return /* @__PURE__ */ jsx("div", {
37
+ className: cn("flex flex-col gap-1.5 text-center sm:text-start", className),
38
+ ...props
39
+ });
40
+ }
41
+ function DialogTitle({ className, ref, ...props }) {
42
+ return /* @__PURE__ */ jsx(DialogPrimitive.Title, {
43
+ ref,
44
+ className: cn("text-lg font-semibold leading-none tracking-tight", className),
45
+ ...props
46
+ });
47
+ }
48
+ function DialogDescription({ className, ref, ...props }) {
49
+ return /* @__PURE__ */ jsx(DialogPrimitive.Description, {
50
+ ref,
51
+ className: cn("text-sm text-fd-muted-foreground", className),
52
+ ...props
53
+ });
54
+ }
58
55
  //#endregion
59
56
  export { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger };
@@ -0,0 +1,14 @@
1
+ "use client";
2
+ import { useAnchorId } from "../../utils/auto-anchor.client.js";
3
+ import { jsx } from "react/jsx-runtime";
4
+ import { Heading } from "fumadocs-ui/components/heading";
5
+ //#region src/ui/components/heading.tsx
6
+ function Heading$1({ id: _id, depth, ...props }) {
7
+ return /* @__PURE__ */ jsx(Heading, {
8
+ id: useAnchorId([_id]),
9
+ as: `h${depth}`,
10
+ ...props
11
+ });
12
+ }
13
+ //#endregion
14
+ export { Heading$1 as Heading };
@@ -1,7 +1,9 @@
1
1
  "use client";
2
2
  import { cn } from "../../utils/cn.js";
3
3
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select.js";
4
- import { createContext, use, useMemo, useState } from "react";
4
+ import { anchorIdStartsWith } from "../../utils/auto-anchor.js";
5
+ import { AnchorSection, useAnchorId } from "../../utils/auto-anchor.client.js";
6
+ import { createContext, use, useEffect, useMemo, useState } from "react";
5
7
  import { jsx, jsxs } from "react/jsx-runtime";
6
8
  //#region src/ui/components/select-tab.tsx
7
9
  const Context = createContext(null);
@@ -15,14 +17,24 @@ function SelectTabs({ defaultValue, children }) {
15
17
  children
16
18
  });
17
19
  }
18
- function SelectTab({ value, ...props }) {
19
- if (value !== use(Context)?.value) return;
20
- return /* @__PURE__ */ jsx("div", {
21
- ...props,
22
- children: props.children
23
- });
20
+ function SelectTab({ value, anchorSegments, ...props }) {
21
+ const { value: currentValue, setValue } = use(Context);
22
+ const id = useAnchorId(anchorSegments ?? false);
23
+ useEffect(() => {
24
+ if (id && anchorIdStartsWith(window.location.hash.slice(1), id)) setValue(value);
25
+ }, [
26
+ id,
27
+ value,
28
+ setValue
29
+ ]);
30
+ if (value !== currentValue) return;
31
+ const content = /* @__PURE__ */ jsx("div", { ...props });
32
+ return anchorSegments ? /* @__PURE__ */ jsx(AnchorSection, {
33
+ segments: anchorSegments,
34
+ children: content
35
+ }) : content;
24
36
  }
25
- function SelectTabTrigger({ items, className, ...props }) {
37
+ function SelectTabTrigger({ items, className, placeholder, ...props }) {
26
38
  const { value, setValue } = use(Context);
27
39
  return /* @__PURE__ */ jsxs(Select, {
28
40
  value: value ?? "",
@@ -30,7 +42,10 @@ function SelectTabTrigger({ items, className, ...props }) {
30
42
  children: [/* @__PURE__ */ jsx(SelectTrigger, {
31
43
  className: cn("not-prose w-fit min-w-0 *:min-w-0", className),
32
44
  ...props,
33
- children: /* @__PURE__ */ jsx(SelectValue, { children: value && items.find((item) => item.value === value)?.label })
45
+ children: /* @__PURE__ */ jsx(SelectValue, {
46
+ placeholder,
47
+ children: value && items.find((item) => item.value === value)?.label
48
+ })
34
49
  }), /* @__PURE__ */ jsx(SelectContent, { children: items.map(({ label, value }) => /* @__PURE__ */ jsx(SelectItem, {
35
50
  value,
36
51
  children: label
@@ -5,11 +5,9 @@ import { AuthProvider } from "../playground/auth.js";
5
5
  import { dereferenceDocument } from "../utils/document/dereference.js";
6
6
  import { APIPage } from "./api-page.js";
7
7
  import { boundary_exports } from "./client/boundary.js";
8
- import { slug } from "github-slugger";
9
8
  import { Children, useMemo } from "react";
10
9
  import * as JsxRuntime from "react/jsx-runtime";
11
10
  import { jsx } from "react/jsx-runtime";
12
- import { Heading } from "fumadocs-ui/components/heading";
13
11
  import { remarkGfm } from "fumadocs-core/mdx-plugins/remark-gfm";
14
12
  import defaultMdxComponents from "fumadocs-ui/mdx";
15
13
  import { remark } from "remark";
@@ -90,21 +88,6 @@ function createClientAPIPage({ shiki = defaultShikiFactory, shikiOptions = { the
90
88
  provider: options.playground?.provider ?? renderPlaygroundProviderDefault,
91
89
  render: options.playground?.render ?? renderPlaygroundDefault
92
90
  },
93
- renderHeading(depth, text, props) {
94
- const id = typeof text === "string" ? slug(text) : props?.id;
95
- if (!id) throw new Error("missing 'id' for non-string children");
96
- if (options.renderHeading) return options.renderHeading({
97
- id,
98
- children: text,
99
- ...props
100
- }, depth);
101
- return /* @__PURE__ */ jsx(Heading, {
102
- id,
103
- as: `h${depth}`,
104
- ...props,
105
- children: text
106
- }, id);
107
- },
108
91
  renderMarkdown(text) {
109
92
  if (options.renderMarkdown) return options.renderMarkdown(text);
110
93
  processor ??= createMarkdownProcessor();
@@ -56,7 +56,7 @@ function CopyTypeScriptPanel({ name, code, className }) {
56
56
  const t = useTranslations();
57
57
  const useTypeText = t.useTypeInTypeScript.replace("{name}", name);
58
58
  return /* @__PURE__ */ jsxs("div", {
59
- className: cn("flex items-start justify-between gap-2 bg-fd-card text-fd-card-foreground border rounded-xl p-3 not-prose mb-4 last:mb-0", className),
59
+ className: cn("flex items-start justify-between gap-2 bg-fd-card text-fd-card-foreground border rounded-xl p-3 not-prose", className),
60
60
  children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", {
61
61
  className: "font-medium text-sm mb-2",
62
62
  children: t.typeScriptDefinitions