fumadocs-openapi 11.1.1 → 11.2.1

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 (48) hide show
  1. package/css/generated/shared.css +1 -1
  2. package/dist/generate-file.d.ts +0 -1
  3. package/dist/i18n.d.ts +0 -1
  4. package/dist/node_modules/.pnpm/xml-js@1.6.11/node_modules/xml-js/lib/js2xml.js +2 -1
  5. package/dist/playground/auth.js +4 -3
  6. package/dist/playground/client.d.ts +4 -18
  7. package/dist/playground/client.js +52 -42
  8. package/dist/playground/components/oauth-dialog.js +3 -2
  9. package/dist/playground/components/result-display.d.ts +1 -6
  10. package/dist/playground/fetcher.d.ts +1 -1
  11. package/dist/requests/generators/all.d.ts +0 -1
  12. package/dist/requests/generators/csharp.d.ts +0 -1
  13. package/dist/requests/generators/curl.d.ts +0 -1
  14. package/dist/requests/generators/go.d.ts +0 -1
  15. package/dist/requests/generators/index.d.ts +1 -5
  16. package/dist/requests/generators/java.d.ts +0 -1
  17. package/dist/requests/generators/javascript.d.ts +0 -1
  18. package/dist/requests/generators/python.d.ts +0 -1
  19. package/dist/requests/generators/rust.d.ts +0 -1
  20. package/dist/requests/media/encode.d.ts +2 -0
  21. package/dist/requests/types.d.ts +0 -1
  22. package/dist/scalar/index.d.ts +0 -1
  23. package/dist/server/index.d.ts +2 -2
  24. package/dist/types/openapi.d.ts +4 -4
  25. package/dist/types.d.ts +0 -1
  26. package/dist/ui/base.d.ts +1 -9
  27. package/dist/ui/base.js +10 -13
  28. package/dist/ui/create-client.d.ts +0 -1
  29. package/dist/ui/index.d.ts +6 -8
  30. package/dist/ui/operation/index.js +41 -30
  31. package/dist/ui/operation/request-tabs.d.ts +2 -4
  32. package/dist/ui/operation/response-tabs.d.ts +1 -3
  33. package/dist/ui/operation/response-tabs.js +19 -9
  34. package/dist/utils/document/dereference.d.ts +9 -6
  35. package/dist/utils/document/dereference.js +5 -7
  36. package/dist/utils/get-example-requests.d.ts +1 -2
  37. package/dist/utils/get-example-requests.js +17 -13
  38. package/dist/utils/pages/builder.d.ts +0 -1
  39. package/dist/utils/pages/builder.js +10 -8
  40. package/dist/utils/pages/preset-auto.d.ts +0 -1
  41. package/dist/utils/pages/preset-auto.js +2 -2
  42. package/dist/utils/pages/to-static-data.js +4 -2
  43. package/dist/utils/pages/to-text.d.ts +1 -1
  44. package/dist/utils/pages/to-text.js +5 -3
  45. package/dist/utils/schema.d.ts +1 -0
  46. package/dist/utils/schema.js +7 -4
  47. package/package.json +11 -10
  48. package/dist/utils/remove-undefined.js +0 -24
@@ -8,10 +8,8 @@ import { PlaygroundClientOptions } from "../playground/client.js";
8
8
  import { GeneratedPageProps, OperationItem, WebhookItem } from "../utils/pages/builder.js";
9
9
  import { Awaitable, Document, HttpMethods, OperationObject, PathItemObject, RenderContext } from "../types.js";
10
10
  import { ComponentProps, FC, HTMLAttributes, ReactNode } from "react";
11
- import { NoReference } from "@fumadocs/api-docs/schema";
12
11
  import { ShikiFactory } from "fumadocs-core/highlight/shiki";
13
12
  import { BundledTheme, CodeOptionsThemes, CodeToHastOptionsCommon } from "shiki";
14
-
15
13
  //#region src/ui/index.d.ts
16
14
  interface GenerateTypeScriptDefinitionsContext {
17
15
  name: string;
@@ -22,8 +20,8 @@ interface GenerateTypeScriptDefinitionsContext {
22
20
  interface APIPlaygroundProps {
23
21
  path: string;
24
22
  method: HttpMethods;
25
- operation: NoReference<OperationObject>;
26
- pathItem: NoReference<PathItemObject>;
23
+ operation: OperationObject;
24
+ pathItem: PathItemObject;
27
25
  ctx: RenderContext;
28
26
  }
29
27
  interface CreateOpenAPIPageOptions {
@@ -41,9 +39,9 @@ interface CreateOpenAPIPageOptions {
41
39
  * Generate example code usage for each endpoint.
42
40
  */
43
41
  generateCodeSamples?: (options: {
44
- operation: NoReference<OperationObject>;
42
+ operation: OperationObject;
45
43
  method: HttpMethods;
46
- pathItem: NoReference<PathItemObject>;
44
+ pathItem: PathItemObject;
47
45
  }) => InlineCodeUsageGenerator[];
48
46
  shiki?: ShikiFactory;
49
47
  shikiOptions?: Omit<CodeToHastOptionsCommon, 'lang'> & CodeOptionsThemes<BundledTheme>;
@@ -96,9 +94,9 @@ interface CreateOpenAPIPageOptions {
96
94
  responses: ReactNode;
97
95
  callbacks: ReactNode;
98
96
  }, context: {
99
- operation: NoReference<OperationObject>;
97
+ operation: OperationObject;
100
98
  method: HttpMethods;
101
- pathItem: NoReference<PathItemObject>;
99
+ pathItem: PathItemObject;
102
100
  ctx: RenderContext;
103
101
  }) => ReactNode;
104
102
  renderWebhookLayout?: (slots: {
@@ -33,8 +33,8 @@ const paramTypeKeys = [
33
33
  function Operation({ type = "operation", path, operation, pathItem, method, showTitle, showDescription, headingLevel = 2 }) {
34
34
  const t = useTranslations({ note: "operation page" });
35
35
  const ctx = useRenderContext();
36
- const { schema: { dereferenced } } = ctx;
37
- const body = operation.requestBody;
36
+ const { schema: { dereferenced, resolve } } = ctx;
37
+ const body = resolve(operation.requestBody);
38
38
  let headNode = null;
39
39
  const operationDescription = operation.description ?? pathItem.description;
40
40
  const descriptionNode = showDescription && operationDescription && /* @__PURE__ */ jsx(Markdown, { md: operationDescription });
@@ -110,7 +110,7 @@ function Operation({ type = "operation", path, operation, pathItem, method, show
110
110
  anchorSegments: ["request-body", type],
111
111
  value: type,
112
112
  children: /* @__PURE__ */ jsx(RequestBodyContentItem, {
113
- content,
113
+ content: resolve(content),
114
114
  operation,
115
115
  method
116
116
  })
@@ -133,7 +133,7 @@ function Operation({ type = "operation", path, operation, pathItem, method, show
133
133
  }, status))
134
134
  })] });
135
135
  }
136
- const parameters = [...operation.parameters ?? [], ...pathItem.parameters ?? []];
136
+ const parameters = [...operation.parameters ?? [], ...pathItem.parameters ?? []].map((param) => resolve(param));
137
137
  const parameterNode = paramTypeKeys.map((type) => {
138
138
  const params = parameters.filter((param) => param.in === type);
139
139
  if (!params || params.length === 0) return;
@@ -146,19 +146,23 @@ function Operation({ type = "operation", path, operation, pathItem, method, show
146
146
  segments: ["parameters", type],
147
147
  children: /* @__PURE__ */ jsx("div", {
148
148
  className: "flex flex-col",
149
- children: params.map((param) => param.schema != null && /* @__PURE__ */ jsx(ctx.SchemaUI, {
150
- client: {
151
- name: param.name,
152
- required: param.required
153
- },
154
- root: typeof param.schema === "object" ? {
155
- ...param.schema,
156
- description: param.description ?? param.schema?.description,
157
- deprecated: (param.deprecated ?? false) || (param.schema?.deprecated ?? false)
158
- } : param.schema,
159
- readOnly: method === "get",
160
- writeOnly: method !== "get"
161
- }, param.name))
149
+ children: params.map((param) => {
150
+ if (param.schema == null) return;
151
+ const schema = resolve(param.schema);
152
+ return /* @__PURE__ */ jsx(ctx.SchemaUI, {
153
+ client: {
154
+ name: param.name,
155
+ required: param.required
156
+ },
157
+ root: typeof schema === "object" ? {
158
+ ...schema,
159
+ description: param.description ?? schema.description,
160
+ deprecated: (param.deprecated ?? false) || (schema.deprecated ?? false)
161
+ } : schema,
162
+ readOnly: method === "get",
163
+ writeOnly: method !== "get"
164
+ }, param.name);
165
+ })
162
166
  })
163
167
  })] }, type);
164
168
  });
@@ -203,7 +207,7 @@ function Operation({ type = "operation", path, operation, pathItem, method, show
203
207
  }), securities.map((security, i) => /* @__PURE__ */ jsx(SelectTab, {
204
208
  value: items[i].value,
205
209
  children: Object.entries(security).map(([key, scopes]) => {
206
- const scheme = securitySchemes?.[key];
210
+ const scheme = resolve(securitySchemes?.[key]);
207
211
  if (!scheme) return;
208
212
  return /* @__PURE__ */ jsx(AuthScheme, {
209
213
  scheme,
@@ -214,15 +218,21 @@ function Operation({ type = "operation", path, operation, pathItem, method, show
214
218
  });
215
219
  }
216
220
  const webhookCallbacks = [];
217
- for (const [name, callbacks] of Object.entries(operation.callbacks ?? {})) for (const [path, callback] of Object.entries(callbacks)) for (const method of methodKeys) {
218
- if (!callback[method]) continue;
219
- webhookCallbacks.push({
220
- name,
221
- path,
222
- method,
223
- callback,
224
- operation: callback[method]
225
- });
221
+ for (const [name, item] of Object.entries(operation.callbacks ?? {})) {
222
+ const callbacks = resolve(item);
223
+ for (const [path, pathItem] of Object.entries(callbacks)) {
224
+ const callback = resolve(pathItem);
225
+ for (const method of methodKeys) {
226
+ if (!callback[method]) continue;
227
+ webhookCallbacks.push({
228
+ name,
229
+ path,
230
+ method,
231
+ callback,
232
+ operation: callback[method]
233
+ });
234
+ }
235
+ }
226
236
  }
227
237
  if (webhookCallbacks.length > 0) callbacksNode = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Heading, {
228
238
  id: "callbacks",
@@ -399,7 +409,7 @@ function RequestBodyContentItem({ content, method, operation }) {
399
409
  client: {
400
410
  name: "body",
401
411
  as: "body",
402
- required: operation.requestBody?.required
412
+ required: ctx.schema.resolve(operation.requestBody)?.required
403
413
  },
404
414
  root: content.schema,
405
415
  readOnly: method === "get",
@@ -407,7 +417,8 @@ function RequestBodyContentItem({ content, method, operation }) {
407
417
  })] });
408
418
  }
409
419
  function ResponseAccordion({ status, operation }) {
410
- const response = operation.responses[status];
420
+ const { schema } = useRenderContext();
421
+ const response = schema.resolve(operation.responses[status]);
411
422
  const contentTypes = response.content ? Object.entries(response.content) : [];
412
423
  const items = contentTypes.map(([key]) => ({
413
424
  label: /* @__PURE__ */ jsx("code", {
@@ -439,7 +450,7 @@ function ResponseAccordion({ status, operation }) {
439
450
  }), contentTypes.map(([type, item]) => /* @__PURE__ */ jsx(SelectTab, {
440
451
  value: type,
441
452
  anchorSegments: [type],
442
- children: /* @__PURE__ */ jsx(RepsonseAccordionItem, { item })
453
+ children: /* @__PURE__ */ jsx(RepsonseAccordionItem, { item: schema.resolve(item) })
443
454
  }, type))]
444
455
  })]
445
456
  })
@@ -1,15 +1,13 @@
1
1
  import { ExampleRequestItem } from "../../utils/get-example-requests.js";
2
2
  import { HttpMethods, OperationObject, PathItemObject } from "../../types.js";
3
3
  import { ReactNode } from "react";
4
- import { NoReference } from "@fumadocs/api-docs/schema";
5
-
6
4
  //#region src/ui/operation/request-tabs.d.ts
7
5
  interface RequestTabsRenderOptions {
8
6
  route: string;
9
7
  items: ExampleRequestItem[];
10
8
  method: HttpMethods;
11
- pathItem: NoReference<PathItemObject>;
12
- operation: NoReference<OperationObject>;
9
+ pathItem: PathItemObject;
10
+ operation: OperationObject;
13
11
  }
14
12
  //#endregion
15
13
  export { RequestTabsRenderOptions };
@@ -1,14 +1,12 @@
1
1
  import { ResponseObject } from "../../types.js";
2
2
  import { ReactNode } from "react";
3
- import { NoReference } from "@fumadocs/api-docs/schema";
4
-
5
3
  //#region src/ui/operation/response-tabs.d.ts
6
4
  interface ResponseTab {
7
5
  /**
8
6
  * HTTP response code
9
7
  */
10
8
  code: string;
11
- response: NoReference<ResponseObject>;
9
+ response: ResponseObject;
12
10
  /**
13
11
  * media type of response
14
12
  */
@@ -3,6 +3,7 @@ import { getPreferredType } from "../../utils/schema.js";
3
3
  import { useRenderContext } from "../contexts/api.js";
4
4
  import { ClientCodeBlock } from "../components/codeblock.js";
5
5
  import { Markdown } from "../components/markdown.js";
6
+ import { getRaw } from "@scalar/json-magic/magic-proxy";
6
7
  import { useMemo } from "react";
7
8
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
9
  import { useTranslations } from "@fuma-translate/react";
@@ -12,13 +13,15 @@ import { sample } from "@fumadocs/api-docs/schema/sample";
12
13
  //#region src/ui/operation/response-tabs.tsx
13
14
  function ResponseTabs({ operation }) {
14
15
  const ctx = useRenderContext();
16
+ const { resolve } = ctx.schema;
15
17
  const t = useTranslations({ note: "operation page" });
16
18
  const tabs = useMemo(() => {
17
19
  const tabs = [];
18
20
  if (!operation.responses) return tabs;
19
- for (const [code, response] of Object.entries(operation.responses)) {
21
+ for (const [code, item] of Object.entries(operation.responses)) {
22
+ const response = resolve(item);
20
23
  const media = response.content ? getPreferredType(response.content) : null;
21
- const responseOfType = media ? response.content?.[media] : null;
24
+ const responseOfType = media ? resolve(response.content?.[media]) : null;
22
25
  const tab = {
23
26
  code,
24
27
  response,
@@ -26,22 +29,29 @@ function ResponseTabs({ operation }) {
26
29
  };
27
30
  if (responseOfType?.examples) {
28
31
  tab.examples ??= [];
29
- for (const [key, sample] of Object.entries(responseOfType.examples)) tab.examples.push({
30
- label: sample?.summary ?? t("Example {key}", { variables: { key } }),
31
- sample: sample.value,
32
- description: sample?.description
33
- });
32
+ for (const [key, item] of Object.entries(responseOfType.examples)) {
33
+ const example = resolve(item);
34
+ tab.examples.push({
35
+ label: example?.summary ?? t("Example {key}", { variables: { key } }),
36
+ sample: getRaw(example.value),
37
+ description: example?.description
38
+ });
39
+ }
34
40
  } else if (responseOfType?.example || responseOfType?.schema) {
35
41
  tab.examples ??= [];
36
42
  tab.examples.push({
37
43
  label: t("Example"),
38
- sample: responseOfType.example ?? sample(responseOfType.schema)
44
+ sample: getRaw(responseOfType.example) ?? sample(responseOfType.schema)
39
45
  });
40
46
  }
41
47
  tabs.push(tab);
42
48
  }
43
49
  return tabs;
44
- }, [operation.responses, t]);
50
+ }, [
51
+ operation.responses,
52
+ resolve,
53
+ t
54
+ ]);
45
55
  if (tabs.length === 0) return null;
46
56
  const { renderResponseTabs = renderResponseTabsDefault } = ctx.content ?? {};
47
57
  return renderResponseTabs({ tabs }, ctx);
@@ -1,16 +1,19 @@
1
1
  import { Document } from "../../types.js";
2
- import { NoReference } from "@fumadocs/api-docs/schema";
3
-
2
+ import { NoReferenceSwallow } from "@fumadocs/api-docs/schema";
4
3
  //#region src/utils/document/dereference.d.ts
5
4
  interface DereferencedDocument {
6
5
  /**
7
- * dereferenced document
6
+ * document wrapped in a magic proxy (`@scalar/json-magic`).
7
+ *
8
+ * Reference Objects remain in the document — resolve them lazily with {@link resolve}.
8
9
  */
9
- dereferenced: NoReference<Document>;
10
+ dereferenced: Document;
10
11
  /**
11
- * Get raw $ref from dereferenced object
12
+ * Shallowly resolve a Reference Object from the document, merging sibling keywords.
13
+ *
14
+ * Non-reference values are returned as-is.
12
15
  */
13
- getRawRef: (obj: object) => string | undefined;
16
+ resolve: <T>(node: T) => NoReferenceSwallow<T>;
14
17
  bundled: Document;
15
18
  }
16
19
  //#endregion
@@ -1,14 +1,12 @@
1
- import { dereferenceSync } from "@fumadocs/api-docs/schema/dereference";
1
+ import { dereferenceShallow } from "@fumadocs/api-docs/schema/dereference";
2
+ import { createMagicProxy } from "@scalar/json-magic/magic-proxy";
2
3
  //#region src/utils/document/dereference.ts
3
4
  function dereferenceBundledDocument(bundled) {
4
- const dereferenceMap = /* @__PURE__ */ new Map();
5
5
  return {
6
6
  bundled,
7
- dereferenced: dereferenceSync(bundled, { setOriginalRef(schema, ref) {
8
- dereferenceMap.set(schema, ref);
9
- } }),
10
- getRawRef(obj) {
11
- return dereferenceMap.get(obj);
7
+ dereferenced: createMagicProxy(bundled),
8
+ resolve(node) {
9
+ return dereferenceShallow(node);
12
10
  }
13
11
  };
14
12
  }
@@ -1,6 +1,5 @@
1
1
  import { RawRequestData, RequestData } from "../requests/types.js";
2
- import { NoReference } from "@fumadocs/api-docs/schema";
3
-
2
+ import "../types.js";
4
3
  //#region src/utils/get-example-requests.d.ts
5
4
  interface ExampleRequestItem {
6
5
  id: string;
@@ -1,26 +1,29 @@
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
+ import { getRaw } from "@scalar/json-magic/magic-proxy";
3
5
  import { sample } from "@fumadocs/api-docs/schema/sample";
4
6
  //#region src/utils/get-example-requests.ts
5
7
  function getExampleRequests({ path, method, ctx, operation, pathItem }) {
6
- const requestBody = operation.requestBody;
8
+ const requestBody = dereferenceShallow(operation.requestBody);
7
9
  const media = requestBody?.content ? getPreferredType(requestBody.content) : null;
8
- const bodyOfType = media ? requestBody.content[media] : null;
9
- const parameters = [...operation.parameters ?? [], ...pathItem.parameters ?? []];
10
+ const bodyOfType = media ? dereferenceShallow(requestBody.content[media]) : null;
11
+ const parameters = [...operation.parameters ?? [], ...pathItem.parameters ?? []].map(dereferenceShallow);
10
12
  if (bodyOfType?.examples) {
11
13
  const result = [];
12
- for (const [key, value] of Object.entries(bodyOfType.examples)) {
14
+ for (const [key, item] of Object.entries(bodyOfType.examples)) {
15
+ const { summary, description } = dereferenceShallow(item);
13
16
  const data = getRequestData({
14
17
  path,
15
- body: operation.requestBody,
18
+ body: requestBody,
16
19
  parameters,
17
20
  sampleKey: key,
18
21
  method
19
22
  });
20
23
  result.push({
21
24
  id: key,
22
- name: value.summary || key,
23
- description: value.description,
25
+ name: summary || key,
26
+ description,
24
27
  data,
25
28
  encoded: encodeRequestData(data, ctx.mediaAdapters, parameters)
26
29
  });
@@ -29,14 +32,15 @@ function getExampleRequests({ path, method, ctx, operation, pathItem }) {
29
32
  }
30
33
  const data = getRequestData({
31
34
  path,
32
- body: operation.requestBody,
35
+ body: requestBody,
33
36
  method,
34
37
  parameters
35
38
  });
39
+ const schema = dereferenceShallow(bodyOfType?.schema);
36
40
  return [{
37
41
  id: "_default",
38
42
  name: "Default",
39
- description: typeof bodyOfType?.schema === "object" ? bodyOfType.schema.description : void 0,
43
+ description: typeof schema === "object" ? schema.description : void 0,
40
44
  data,
41
45
  encoded: encodeRequestData(data, ctx.mediaAdapters, parameters)
42
46
  }];
@@ -55,7 +59,7 @@ function getRequestData({ method, path, parameters, sampleKey, body }) {
55
59
  if (param.schema) value = sample(param.schema);
56
60
  else if (param.content) {
57
61
  const type = getPreferredType(param.content);
58
- const content = type ? param.content[type] : void 0;
62
+ const content = type ? dereferenceShallow(param.content[type]) : void 0;
59
63
  if (!content || !content.schema) throw new Error(`Cannot find "${param.name}" parameter info for media type "${type}" in ${path} ${method}`);
60
64
  value = sample(content.schema);
61
65
  }
@@ -77,9 +81,9 @@ function getRequestData({ method, path, parameters, sampleKey, body }) {
77
81
  const type = getPreferredType(body.content);
78
82
  if (!type) throw new Error(`Cannot find body schema for ${path} ${method}: missing media type`);
79
83
  result.bodyMediaType = type;
80
- const bodyOfType = body.content[type];
81
- if (bodyOfType.examples && sampleKey) result.body = bodyOfType.examples[sampleKey].value;
82
- else if (bodyOfType.example) result.body = bodyOfType.example;
84
+ const bodyOfType = dereferenceShallow(body.content[type]);
85
+ if (bodyOfType.examples && sampleKey) result.body = getRaw(dereferenceShallow(bodyOfType.examples[sampleKey]).value);
86
+ else if (bodyOfType.example) result.body = getRaw(bodyOfType.example);
83
87
  else result.body = sample(bodyOfType?.schema ?? {}, {
84
88
  skipReadOnly: method !== "get",
85
89
  skipWriteOnly: method === "get",
@@ -1,6 +1,5 @@
1
1
  import { Document, HttpMethods, OperationObject, PathItemObject, TagObject } from "../../types.js";
2
2
  import { NoReferenceSwallow } from "@fumadocs/api-docs/schema";
3
-
4
3
  //#region src/utils/pages/builder.d.ts
5
4
  interface BaseEntry {
6
5
  path: string;
@@ -1,14 +1,16 @@
1
1
  import { getTagDisplayName, methodKeys } from "../schema.js";
2
2
  import { idToTitle } from "@fumadocs/api-docs/utils/id-to-title";
3
3
  import { dereferenceShallow } from "@fumadocs/api-docs/schema/dereference";
4
+ import { createMagicProxy } from "@scalar/json-magic/magic-proxy";
4
5
  //#region src/utils/pages/builder.ts
5
6
  function fromSchema(schemaId, bundled, config) {
6
7
  const files = [];
7
8
  const { toPages } = config;
9
+ const document = createMagicProxy(bundled);
8
10
  toPages({
9
11
  id: schemaId,
10
- document: bundled,
11
- dereferenceShallow: (s) => dereferenceShallow(s, bundled),
12
+ document,
13
+ dereferenceShallow: (s) => dereferenceShallow(s),
12
14
  create(entry) {
13
15
  files.push(entry);
14
16
  },
@@ -17,7 +19,7 @@ function fromSchema(schemaId, bundled, config) {
17
19
  webhooks: [],
18
20
  operations: []
19
21
  };
20
- for (const [path, pathItem] of Object.entries(bundled.paths ?? {})) {
22
+ for (const [path, pathItem] of Object.entries(document.paths ?? {})) {
21
23
  if (!pathItem) continue;
22
24
  for (const methodKey of methodKeys) {
23
25
  if (!pathItem[methodKey]) continue;
@@ -28,8 +30,8 @@ function fromSchema(schemaId, bundled, config) {
28
30
  });
29
31
  }
30
32
  }
31
- for (const [name, _pathItem] of Object.entries(bundled.webhooks ?? {})) {
32
- const pathItem = dereferenceShallow(_pathItem, bundled);
33
+ for (const [name, _pathItem] of Object.entries(document.webhooks ?? {})) {
34
+ const pathItem = dereferenceShallow(_pathItem);
33
35
  if (!pathItem) continue;
34
36
  for (const methodKey of methodKeys) {
35
37
  if (!pathItem[methodKey]) continue;
@@ -50,7 +52,7 @@ function fromSchema(schemaId, bundled, config) {
50
52
  }).join("/");
51
53
  },
52
54
  fromExtractedWebhook(item) {
53
- const pathItem = dereferenceShallow(bundled.webhooks?.[item.name], bundled);
55
+ const pathItem = dereferenceShallow(document.webhooks?.[item.name]);
54
56
  if (!pathItem) return;
55
57
  const operation = pathItem?.[item.method];
56
58
  if (!operation) return;
@@ -63,7 +65,7 @@ function fromSchema(schemaId, bundled, config) {
63
65
  };
64
66
  },
65
67
  fromExtractedOperation(item) {
66
- const pathItem = dereferenceShallow(bundled.paths?.[item.path], bundled);
68
+ const pathItem = dereferenceShallow(document.paths?.[item.path]);
67
69
  if (!pathItem) return;
68
70
  const operation = pathItem?.[item.method];
69
71
  if (!operation) return;
@@ -81,7 +83,7 @@ function fromSchema(schemaId, bundled, config) {
81
83
  } };
82
84
  },
83
85
  fromTagName(name) {
84
- const tag = bundled.tags?.find((item) => item.name === name);
86
+ const tag = document.tags?.find((item) => item.name === name);
85
87
  if (!tag) return;
86
88
  return {
87
89
  info: tag,
@@ -1,6 +1,5 @@
1
1
  import { OperationOutput, PageOutput, PagesBuilder, PagesBuilderConfig, WebhookOutput } from "./builder.js";
2
2
  import { DistributiveOmit } from "../../types.js";
3
-
4
3
  //#region src/utils/pages/preset-auto.d.ts
5
4
  interface OperationConfig extends BaseConfig {
6
5
  /**
@@ -21,7 +21,7 @@ function createAutoPreset(options) {
21
21
  if (algorithm === "v2" && operation.operationId) return operation.operationId;
22
22
  return path$1.join(this.routePathToFilePath(result.item.path), result.item.method.toLowerCase());
23
23
  }
24
- const hook = dereferenceShallow(this.document.webhooks[result.item.name], this.document)[result.item.method];
24
+ const hook = dereferenceShallow(this.document.webhooks[result.item.name])[result.item.method];
25
25
  if (algorithm === "v2" && hook.operationId) return hook.operationId;
26
26
  return slugify(result.item.name);
27
27
  };
@@ -52,7 +52,7 @@ function createAutoPreset(options) {
52
52
  break;
53
53
  }
54
54
  case "tag": {
55
- let tags = entry.type === "operation" ? dereferenceShallow(doc.paths?.[entry.item.path], doc)?.[entry.item.method]?.tags : dereferenceShallow(doc.webhooks?.[entry.item.name], doc)?.[entry.item.method]?.tags;
55
+ let tags = entry.type === "operation" ? dereferenceShallow(doc.paths?.[entry.item.path])?.[entry.item.method]?.tags : dereferenceShallow(doc.webhooks?.[entry.item.name])?.[entry.item.method]?.tags;
56
56
  if (!tags || tags.length === 0) {
57
57
  console.warn("When `groupBy` is set to `tag`, make sure a `tags` is defined for every operation schema.");
58
58
  tags = ["unknown"];
@@ -1,8 +1,10 @@
1
1
  import { idToTitle } from "@fumadocs/api-docs/utils/id-to-title";
2
2
  import { dereferenceShallow } from "@fumadocs/api-docs/schema/dereference";
3
+ import { createMagicProxy } from "@scalar/json-magic/magic-proxy";
3
4
  import Slugger from "github-slugger";
4
5
  //#region src/utils/pages/to-static-data.ts
5
6
  function toStaticData(page, doc) {
7
+ const proxied = createMagicProxy(doc);
6
8
  const slugger = new Slugger();
7
9
  const toc = [];
8
10
  const structuredData = {
@@ -29,12 +31,12 @@ function toStaticData(page, doc) {
29
31
  });
30
32
  }
31
33
  for (const item of page.operations ?? []) {
32
- const operation = dereferenceShallow(doc.paths?.[item.path], doc)?.[item.method];
34
+ const operation = dereferenceShallow(proxied.paths?.[item.path])?.[item.method];
33
35
  if (!operation) continue;
34
36
  pathItem(operation, item.path);
35
37
  }
36
38
  for (const item of page.webhooks ?? []) {
37
- const webhook = dereferenceShallow(doc.webhooks?.[item.name], doc)?.[item.method];
39
+ const webhook = dereferenceShallow(proxied.webhooks?.[item.name])?.[item.method];
38
40
  if (!webhook) continue;
39
41
  pathItem(webhook, item.name);
40
42
  }
@@ -1,5 +1,5 @@
1
+ import "./builder.js";
1
2
  import { TagObject } from "../../types.js";
2
-
3
3
  //#region src/utils/pages/to-text.d.ts
4
4
  interface PagesToTextOptions {
5
5
  /**
@@ -1,8 +1,7 @@
1
1
  import { getPageProps } from "./builder.js";
2
2
  import { toStaticData } from "./to-static-data.js";
3
3
  import { doubleQuote } from "../../requests/string-utils.js";
4
- import { removeUndefined } from "../remove-undefined.js";
5
- import { dump } from "js-yaml";
4
+ import { stringify } from "yaml";
6
5
  //#region src/utils/pages/to-text.ts
7
6
  function toText(entry, doc, options = {}) {
8
7
  const { frontmatter, includeDescription = false } = options;
@@ -34,7 +33,10 @@ function toText(entry, doc, options = {}) {
34
33
  function generateDocument(frontmatter, content, options) {
35
34
  const { addGeneratedComment = true, imports } = options;
36
35
  const out = [];
37
- const banner = dump(removeUndefined(frontmatter)).trimEnd();
36
+ const banner = stringify(frontmatter, {
37
+ compat: "yaml-1.1",
38
+ singleQuote: true
39
+ }).trimEnd();
38
40
  if (banner.length > 0) out.push(`---\n${banner}\n---`);
39
41
  if (addGeneratedComment) {
40
42
  let commentContent = "This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again.";
@@ -1,2 +1,3 @@
1
+ import "../types.js";
1
2
  import { ParsedSchema } from "@fumadocs/api-docs/schema";
2
3
  export type { ParsedSchema };
@@ -1,4 +1,6 @@
1
1
  import { idToTitle } from "@fumadocs/api-docs/utils/id-to-title";
2
+ import { dereferenceShallow } from "@fumadocs/api-docs/schema/dereference";
3
+ import { getRaw } from "@scalar/json-magic/magic-proxy";
2
4
  //#region src/utils/schema.ts
3
5
  const methodKeys = [
4
6
  "get",
@@ -18,18 +20,19 @@ function getTagDisplayName(tag) {
18
20
  return idToTitle(tag.name);
19
21
  }
20
22
  function pickExample(value) {
21
- if (value.example !== void 0) return value.example;
23
+ if (value.example !== void 0) return getRaw(value.example);
22
24
  if (value.content) {
23
25
  const type = getPreferredType(value.content);
24
- const content = type ? value.content[type] : void 0;
26
+ const content = type ? dereferenceShallow(value.content[type]) : void 0;
25
27
  if (type && content) {
26
- const out = value.examples?.[type].value ?? pickExample(content);
28
+ const example = value.examples?.[type];
29
+ const out = (example !== void 0 ? getRaw(dereferenceShallow(example).value) : void 0) ?? pickExample(content);
27
30
  if (out !== void 0) return out;
28
31
  }
29
32
  }
30
33
  if (value.examples) {
31
34
  const examples = Object.values(value.examples);
32
- if (examples.length > 0) return examples[0].value;
35
+ if (examples.length > 0) return getRaw(dereferenceShallow(examples[0]).value);
33
36
  }
34
37
  }
35
38
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-openapi",
3
- "version": "11.1.1",
3
+ "version": "11.2.1",
4
4
  "description": "Generate MDX docs for your OpenAPI spec",
5
5
  "keywords": [
6
6
  "Docs",
@@ -44,33 +44,34 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@fuma-translate/react": "^1.0.2",
47
- "@fumari/json-schema-ts": "^0.0.2",
47
+ "@fumari/json-schema-ts": "^1.0.2",
48
+ "@scalar/json-magic": "^0.12.17",
48
49
  "chokidar": "^5.0.0",
49
50
  "class-variance-authority": "^0.7.1",
50
51
  "cnfast": "^0.0.8",
51
52
  "github-slugger": "^2.0.0",
52
53
  "hast-util-to-jsx-runtime": "^2.3.6",
53
- "js-yaml": "^5.2.1",
54
- "lucide-react": "^1.23.0",
54
+ "lucide-react": "^1.24.0",
55
55
  "remark": "^15.0.1",
56
56
  "remark-rehype": "^11.1.2",
57
57
  "shiki": "^4.3.1",
58
- "@fumadocs/api-docs": "0.1.0",
58
+ "yaml": "^2.9.0",
59
+ "@fumadocs/api-docs": "0.2.0",
59
60
  "@fumari/stf": "1.1.0"
60
61
  },
61
62
  "devDependencies": {
62
- "@scalar/api-client-react": "^2.0.31",
63
+ "@scalar/api-client-react": "^2.0.32",
63
64
  "@scalar/openapi-upgrader": "^0.2.9",
64
65
  "@tailwindcss/oxide": "^4.3.2",
65
- "@types/node": "26.1.0",
66
+ "@types/node": "26.1.1",
66
67
  "@types/react": "^19.2.17",
67
68
  "fast-content-type-parse": "^3.0.0",
68
69
  "json-schema-typed": "^8.0.2",
69
- "tsdown": "0.22.3",
70
+ "tsdown": "0.22.5",
70
71
  "xml-js": "^1.6.11",
71
- "fumadocs-core": "16.11.1",
72
72
  "tsconfig": "0.0.0",
73
- "fumadocs-ui": "npm:@fumadocs/base-ui@16.11.1"
73
+ "fumadocs-core": "16.11.5",
74
+ "fumadocs-ui": "npm:@fumadocs/base-ui@16.11.5"
74
75
  },
75
76
  "peerDependencies": {
76
77
  "@scalar/api-client-react": "^2.0.20",