fumadocs-openapi 10.4.1 → 10.5.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 (37) hide show
  1. package/css/generated/shared.css +8 -1
  2. package/dist/generate-file.d.ts +11 -1
  3. package/dist/generate-file.d.ts.map +1 -1
  4. package/dist/generate-file.js +60 -26
  5. package/dist/generate-file.js.map +1 -1
  6. package/dist/index.d.ts +2 -2
  7. package/dist/playground/schema.d.ts +0 -1
  8. package/dist/playground/schema.d.ts.map +1 -1
  9. package/dist/playground/schema.js +1 -1
  10. package/dist/requests/media/adapter.js +1 -1
  11. package/dist/requests/string-utils.js +1 -1
  12. package/dist/server/source-api.d.ts +7 -3
  13. package/dist/server/source-api.d.ts.map +1 -1
  14. package/dist/server/source-api.js +55 -5
  15. package/dist/server/source-api.js.map +1 -1
  16. package/dist/ui/api-page.d.ts +1 -0
  17. package/dist/ui/api-page.d.ts.map +1 -1
  18. package/dist/ui/operation/request-tabs.d.ts +1 -0
  19. package/dist/ui/operation/request-tabs.d.ts.map +1 -1
  20. package/dist/ui/schema/client.d.ts +1 -0
  21. package/dist/ui/schema/client.d.ts.map +1 -1
  22. package/dist/ui/schema/index.d.ts +1 -0
  23. package/dist/ui/schema/index.d.ts.map +1 -1
  24. package/dist/utils/pages/builder.d.ts +9 -10
  25. package/dist/utils/pages/builder.d.ts.map +1 -1
  26. package/dist/utils/pages/builder.js +29 -30
  27. package/dist/utils/pages/builder.js.map +1 -1
  28. package/dist/utils/pages/preset-auto.d.ts +6 -5
  29. package/dist/utils/pages/preset-auto.d.ts.map +1 -1
  30. package/dist/utils/pages/preset-auto.js +97 -35
  31. package/dist/utils/pages/preset-auto.js.map +1 -1
  32. package/dist/utils/pages/to-text.d.ts.map +1 -1
  33. package/dist/utils/pages/to-text.js +4 -12
  34. package/dist/utils/pages/to-text.js.map +1 -1
  35. package/package.json +8 -8
  36. package/dist/utils/pages/to-body.js +0 -22
  37. package/dist/utils/pages/to-body.js.map +0 -1
@@ -2,6 +2,7 @@
2
2
  @source inline("!last");
3
3
  @source inline("*:data-[collapsible=true]:order-last");
4
4
  @source inline("*:min-w-0");
5
+ @source inline("---");
5
6
  @source inline("--fd-docs-row-1");
6
7
  @source inline("--initial-height");
7
8
  @source inline("-mode");
@@ -300,6 +301,8 @@
300
301
  @source inline("focus:ring");
301
302
  @source inline("focus:ring-fd-ring");
302
303
  @source inline("focus:text-fd-accent-foreground");
304
+ @source inline("folder");
305
+ @source inline("folderStyle");
303
306
  @source inline("font-medium");
304
307
  @source inline("font-mono");
305
308
  @source inline("font-semibold");
@@ -480,6 +483,7 @@
480
483
  @source inline("merge");
481
484
  @source inline("mergeAllOf");
482
485
  @source inline("message");
486
+ @source inline("meta");
483
487
  @source inline("metaData");
484
488
  @source inline("method");
485
489
  @source inline("min");
@@ -516,6 +520,7 @@
516
520
  @source inline("next-themes");
517
521
  @source inline("no-cache");
518
522
  @source inline("node");
523
+ @source inline("node:path");
519
524
  @source inline("non-string");
520
525
  @source inline("not");
521
526
  @source inline("not-last:pb-0");
@@ -582,6 +587,7 @@
582
587
  @source inline("parameterNode");
583
588
  @source inline("parameters");
584
589
  @source inline("params");
590
+ @source inline("parent");
585
591
  @source inline("parsed");
586
592
  @source inline("parsedState");
587
593
  @source inline("parts");
@@ -677,6 +683,7 @@
677
683
  @source inline("registered");
678
684
  @source inline("registry");
679
685
  @source inline("rehype-react");
686
+ @source inline("relativePath");
680
687
  @source inline("remark");
681
688
  @source inline("remark-rehype");
682
689
  @source inline("remarkGfm");
@@ -782,6 +789,7 @@
782
789
  @source inline("selector");
783
790
  @source inline("selectorNode");
784
791
  @source inline("sep");
792
+ @source inline("separator");
785
793
  @source inline("server");
786
794
  @source inline("server-side");
787
795
  @source inline("server-url");
@@ -924,7 +932,6 @@
924
932
  @source inline("titleRequestTabs");
925
933
  @source inline("titleResponseBody");
926
934
  @source inline("to");
927
- @source inline("toBody");
928
935
  @source inline("toJsxRuntime");
929
936
  @source inline("toStaticData");
930
937
  @source inline("toc");
@@ -23,13 +23,14 @@ interface IndexConfig {
23
23
  };
24
24
  }
25
25
  interface IndexItem {
26
+ /** output path of index page */
26
27
  path: string;
27
28
  title?: string;
28
29
  description?: string;
29
30
  /**
30
31
  * Specify linked pages:
31
32
  * - items in `inputs` to include all generated pages of a specific schema.
32
- * - specific Markdown/MDX files.
33
+ * - file paths (using forward slash).
33
34
  */
34
35
  only?: string[];
35
36
  }
@@ -46,11 +47,20 @@ interface GenerateFilesConfig extends PagesToTextOptions {
46
47
  * Generate index files with cards linking to generated pages.
47
48
  */
48
49
  index?: IndexConfig;
50
+ /**
51
+ * Generate `meta.json` files.
52
+ *
53
+ * Note: for flexibility, it's recommended to define them on your own.
54
+ */
55
+ meta?: boolean | MetaOptions;
49
56
  /**
50
57
  * Can add/change/remove output files before writing to file system
51
58
  **/
52
59
  beforeWrite?: (this: BeforeWriteContext, files: OutputFile[]) => void | Promise<void>;
53
60
  }
61
+ interface MetaOptions {
62
+ groupStyle?: 'folder' | 'separator';
63
+ }
54
64
  type Config = SchemaToPagesOptions & GenerateFilesConfig;
55
65
  interface BeforeWriteContext {
56
66
  readonly generated: Record<string, OutputFile[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"generate-file.d.ts","names":[],"sources":["../src/generate-file.ts"],"mappings":";;;;;;;UASiB,UAAA;EACf,IAAA;EACA,OAAA;AAAA;AAAA,UAGQ,WAAA;EACR,KAAA,EAAO,SAAA,OAAgB,GAAA,EAAK,kBAAA,KAAuB,SAAA;EALnD;;AAED;EAQC,GAAA,IACM,QAAA;IAEA,OAAA;IARC;;;IAYD,UAAA;EAAA;AAAA;AAAA,UAIE,SAAA;EACR,IAAA;EACA,KAAA;EACA,WAAA;EAdA;;;;;EAqBA,IAAA;AAAA;AAAA,UAGQ,mBAAA,SAA4B,kBAAA;;;;EAIpC,KAAA,EAAO,aAAA;EAdP;;;EAmBA,MAAA;EATQ;;;EAcR,KAAA,GAAQ,WAAA;EAAA;;;EAKR,WAAA,IAAe,IAAA,EAAM,kBAAA,EAAoB,KAAA,EAAO,UAAA,cAAwB,OAAA;AAAA;AAAA,KAG9D,MAAA,GAAS,oBAAA,GAAuB,mBAAA;AAAA,UAElC,kBAAA;EAAA,SACC,SAAA,EAAW,MAAA,SAAe,UAAA;EAAA,SAC1B,gBAAA,EAAkB,MAAA,SAAe,WAAA;EAAA,SACjC,SAAA,EAAW,MAAA,SAAe,iBAAA;AAAA;AAAA,iBAGf,aAAA,CAAc,OAAA,EAAS,MAAA,GAAS,OAAA;AAAA,iBAehC,iBAAA,CACpB,OAAA,EAAS,oBAAA,GAAuB,IAAA,CAAK,mBAAA,cACpC,OAAA,CAAQ,UAAA"}
1
+ {"version":3,"file":"generate-file.d.ts","names":[],"sources":["../src/generate-file.ts"],"mappings":";;;;;;;UASiB,UAAA;EACf,IAAA;EACA,OAAA;AAAA;AAAA,UAGQ,WAAA;EACR,KAAA,EAAO,SAAA,OAAgB,GAAA,EAAK,kBAAA,KAAuB,SAAA;EALnD;;AAED;EAQC,GAAA,IACM,QAAA;IAEA,OAAA;IARC;;;IAYD,UAAA;EAAA;AAAA;AAAA,UAIE,SAAA;EAhBoB;EAkB5B,IAAA;EACA,KAAA;EACA,WAAA;EAdM;;;;;EAqBN,IAAA;AAAA;AAAA,UAGQ,mBAAA,SAA4B,kBAAA;EAdnB;;;EAkBjB,KAAA,EAAO,aAAA;EAPP;;;EAYA,MAAA;EAT4B;;;EAc5B,KAAA,GAAQ,WAAA;EAOS;;;;;EAAjB,IAAA,aAAiB,WAAA;EArBqC;;;EA0BtD,WAAA,IAAe,IAAA,EAAM,kBAAA,EAAoB,KAAA,EAAO,UAAA,cAAwB,OAAA;AAAA;AAAA,UAGhE,WAAA;EACR,UAAA;AAAA;AAAA,KAGU,MAAA,GAAS,oBAAA,GAAuB,mBAAA;AAAA,UAElC,kBAAA;EAAA,SACC,SAAA,EAAW,MAAA,SAAe,UAAA;EAAA,SAC1B,gBAAA,EAAkB,MAAA,SAAe,WAAA;EAAA,SACjC,SAAA,EAAW,MAAA,SAAe,iBAAA;AAAA;AAAA,iBAGf,aAAA,CAAc,OAAA,EAAS,MAAA,GAAS,OAAA;AAAA,iBAehC,iBAAA,CACpB,OAAA,EAAS,oBAAA,GAAuB,IAAA,CAAK,mBAAA,cACpC,OAAA,CAAQ,UAAA"}
@@ -2,15 +2,15 @@ import { generateDocument, toText } from "./utils/pages/to-text.js";
2
2
  import { createAutoPreset } from "./utils/pages/preset-auto.js";
3
3
  import { fromSchema } from "./utils/pages/builder.js";
4
4
  import { mkdir, writeFile } from "node:fs/promises";
5
- import * as path from "node:path";
6
- import { createGetUrl, getSlugs } from "fumadocs-core/source";
5
+ import * as path$1 from "node:path";
6
+ import { PathUtils, createGetUrl, getSlugs } from "fumadocs-core/source";
7
7
  //#region src/generate-file.ts
8
8
  async function generateFiles(options) {
9
9
  const files = await generateFilesOnly(options);
10
10
  const { output } = options;
11
11
  await Promise.all(files.map(async (file) => {
12
- const filePath = path.join(output, file.path);
13
- await mkdir(path.dirname(filePath), { recursive: true });
12
+ const filePath = path$1.join(output, file.path);
13
+ await mkdir(path$1.dirname(filePath), { recursive: true });
14
14
  await writeFile(filePath, file.content);
15
15
  console.log(`Generated: ${filePath}`);
16
16
  }));
@@ -27,52 +27,84 @@ async function generateFilesOnly(options) {
27
27
  const entries = fromSchema(id, schema, preset);
28
28
  const schemaFiles = generated[id] ??= [];
29
29
  generatedEntries[id] = entries;
30
- for (const entry of entries) {
31
- const file = {
30
+ function scan(entry) {
31
+ if (entry.type === "group") {
32
+ for (const child of entry.entries) scan(child);
33
+ return;
34
+ }
35
+ schemaFiles.push({
32
36
  path: entry.path,
33
37
  content: toText(entry, schema, options)
34
- };
35
- schemaFiles.push(file);
36
- files.push(file);
38
+ });
37
39
  }
40
+ for (const entry of entries) scan(entry);
41
+ files.push(...schemaFiles);
38
42
  }
39
43
  const context = {
40
44
  generated,
41
45
  generatedEntries,
42
46
  documents: schemas
43
47
  };
44
- if (options.index) writeIndexFiles(files, context, options);
48
+ if (options.index) files.push(...writeIndexFiles(context, options));
49
+ if (options.meta) files.push(...generateMeta(context, options.meta === true ? {} : options.meta));
45
50
  await options.beforeWrite?.call(context, files);
46
51
  return files;
47
52
  }
48
- function writeIndexFiles(files, context, options) {
53
+ function generateMeta(context, options) {
54
+ const files = [];
55
+ const { groupStyle = "folder" } = options;
56
+ function scan(entries, parent) {
57
+ const pages = [];
58
+ for (const entry of entries) {
59
+ const relativePath = PathUtils.slash(parent ? path$1.relative(parent.path, entry.path) : entry.path);
60
+ if (entry.type === "group") {
61
+ scan(entry.entries, entry);
62
+ if (groupStyle === "folder") pages.push(relativePath);
63
+ else pages.push(`---${entry.info.title}---`, `...${relativePath}`);
64
+ } else pages.push(relativePath.slice(0, -path$1.extname(entry.path).length));
65
+ }
66
+ if (pages.length === 0) return;
67
+ files.push({
68
+ path: parent ? path$1.join(parent.path, "meta.json") : "meta.json",
69
+ content: JSON.stringify({
70
+ title: parent?.info.title,
71
+ description: parent?.info.description,
72
+ pages
73
+ }, null, 2)
74
+ });
75
+ }
76
+ for (const entries of Object.values(context.generatedEntries)) scan(entries);
77
+ return files;
78
+ }
79
+ function writeIndexFiles(context, options) {
80
+ const files = [];
49
81
  const { generatedEntries } = context;
82
+ const pathToEntry = /* @__PURE__ */ new Map();
50
83
  const { items, url } = options.index;
84
+ function indexEntry(entry) {
85
+ pathToEntry.set(PathUtils.slash(entry.path), entry);
86
+ if (entry.type === "group") for (const child of entry.entries) indexEntry(child);
87
+ }
51
88
  let urlFn;
52
89
  if (typeof url === "object") {
53
90
  const getUrl = createGetUrl(url.baseUrl);
54
- urlFn = (file) => getUrl(getSlugs(path.relative(url.contentDir, file)));
91
+ urlFn = (file) => getUrl(getSlugs(path$1.relative(url.contentDir, file)));
55
92
  } else urlFn = url;
56
- function findEntryByPath(path) {
57
- for (const entries of Object.values(generatedEntries)) {
58
- const match = entries.find((entry) => entry.path === path);
59
- if (match) return match;
60
- }
61
- }
62
93
  function fileContent(index) {
63
94
  const content = [];
64
95
  content.push("<Cards>");
65
- const pathToEntry = /* @__PURE__ */ new Map();
96
+ const outputEntries = [];
66
97
  const only = index.only ?? Object.keys(context.generated);
67
- for (const item of only) if (generatedEntries[item]) for (const entry of generatedEntries[item]) pathToEntry.set(entry.path, entry);
98
+ for (const item of only) if (generatedEntries[item]) for (const entry of generatedEntries[item]) outputEntries.push(entry);
68
99
  else {
69
- const match = findEntryByPath(item);
100
+ const match = pathToEntry.get(item);
70
101
  if (!match) throw new Error(`${item} does not exist on "input", available: ${Object.keys(generatedEntries).join(", ")}.`);
71
- pathToEntry.set(match.path, match);
102
+ outputEntries.push(match);
72
103
  }
73
- for (const file of pathToEntry.values()) {
74
- const descriptionAttr = file.info.description ? `description=${JSON.stringify(file.info.description)} ` : "";
75
- content.push(`<Card href="${urlFn(file.path)}" title=${JSON.stringify(file.info.title)} ${descriptionAttr}/>`);
104
+ for (const entry of outputEntries) {
105
+ if (entry.type === "group") continue;
106
+ const descriptionAttr = entry.info.description ? `description=${JSON.stringify(entry.info.description)} ` : "";
107
+ content.push(`<Card href="${urlFn(entry.path)}" title=${JSON.stringify(entry.info.title)} ${descriptionAttr}/>`);
76
108
  }
77
109
  content.push("</Cards>");
78
110
  return generateDocument({
@@ -80,10 +112,12 @@ function writeIndexFiles(files, context, options) {
80
112
  description: index.description
81
113
  }, content.join("\n"), options);
82
114
  }
115
+ for (const list of Object.values(context.generatedEntries)) for (const item of list) indexEntry(item);
83
116
  for (const item of typeof items === "function" ? items(context) : items) files.push({
84
- path: path.extname(item.path).length === 0 ? `${item.path}.mdx` : item.path,
117
+ path: path$1.extname(item.path).length === 0 ? `${item.path}.mdx` : item.path,
85
118
  content: fileContent(item)
86
119
  });
120
+ return files;
87
121
  }
88
122
  //#endregion
89
123
  export { generateFiles, generateFilesOnly };
@@ -1 +1 @@
1
- {"version":3,"file":"generate-file.js","names":[],"sources":["../src/generate-file.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { generateDocument, type PagesToTextOptions, toText } from './utils/pages/to-text';\nimport type { ProcessedDocument } from '@/utils/process-document';\nimport type { OpenAPIServer } from '@/server';\nimport { createGetUrl, getSlugs } from 'fumadocs-core/source';\nimport { createAutoPreset, type SchemaToPagesOptions } from '@/utils/pages/preset-auto';\nimport { fromSchema, type OutputEntry } from '@/utils/pages/builder';\n\nexport interface OutputFile {\n path: string;\n content: string;\n}\n\ninterface IndexConfig {\n items: IndexItem[] | ((ctx: BeforeWriteContext) => IndexItem[]);\n\n /**\n * Generate URLs for cards\n */\n url:\n | ((filePath: string) => string)\n | {\n baseUrl: string;\n /**\n * Base content directory\n */\n contentDir: string;\n };\n}\n\ninterface IndexItem {\n path: string;\n title?: string;\n description?: string;\n\n /**\n * Specify linked pages:\n * - items in `inputs` to include all generated pages of a specific schema.\n * - specific Markdown/MDX files.\n */\n only?: string[];\n}\n\ninterface GenerateFilesConfig extends PagesToTextOptions {\n /**\n * the OpenAPI server object\n */\n input: OpenAPIServer;\n\n /**\n * Output directory\n */\n output: string;\n\n /**\n * Generate index files with cards linking to generated pages.\n */\n index?: IndexConfig;\n\n /**\n * Can add/change/remove output files before writing to file system\n **/\n beforeWrite?: (this: BeforeWriteContext, files: OutputFile[]) => void | Promise<void>;\n}\n\nexport type Config = SchemaToPagesOptions & GenerateFilesConfig;\n\ninterface BeforeWriteContext {\n readonly generated: Record<string, OutputFile[]>;\n readonly generatedEntries: Record<string, OutputEntry[]>;\n readonly documents: Record<string, ProcessedDocument>;\n}\n\nexport async function generateFiles(options: Config): Promise<void> {\n const files = await generateFilesOnly(options);\n const { output } = options;\n\n await Promise.all(\n files.map(async (file) => {\n const filePath = path.join(output, file.path);\n\n await mkdir(path.dirname(filePath), { recursive: true });\n await writeFile(filePath, file.content);\n console.log(`Generated: ${filePath}`);\n }),\n );\n}\n\nexport async function generateFilesOnly(\n options: SchemaToPagesOptions & Omit<GenerateFilesConfig, 'output'>,\n): Promise<OutputFile[]> {\n const schemas = await options.input.getSchemas();\n\n const files: OutputFile[] = [];\n const generated: Record<string, OutputFile[]> = {};\n const generatedEntries: Record<string, OutputEntry[]> = {};\n\n const entries = Object.entries(schemas);\n if (entries.length === 0) {\n throw new Error('No input files found.');\n }\n const preset = createAutoPreset(options);\n for (const [id, schema] of entries) {\n const entries = fromSchema(id, schema, preset);\n const schemaFiles = (generated[id] ??= []);\n\n generatedEntries[id] = entries;\n for (const entry of entries) {\n const file: OutputFile = {\n path: entry.path,\n content: toText(entry, schema, options),\n };\n\n schemaFiles.push(file);\n files.push(file);\n }\n }\n\n const context: BeforeWriteContext = {\n generated,\n generatedEntries,\n documents: schemas,\n };\n\n if (options.index) {\n writeIndexFiles(files, context, options);\n }\n\n await options.beforeWrite?.call(context, files);\n return files;\n}\n\nfunction writeIndexFiles(\n files: OutputFile[],\n context: BeforeWriteContext,\n options: SchemaToPagesOptions & Omit<GenerateFilesConfig, 'output'>,\n) {\n const { generatedEntries } = context;\n const { items, url } = options.index!;\n\n let urlFn: (path: string) => string;\n if (typeof url === 'object') {\n const getUrl = createGetUrl(url.baseUrl);\n\n urlFn = (file) => getUrl(getSlugs(path.relative(url.contentDir, file)));\n } else {\n urlFn = url;\n }\n\n function findEntryByPath(path: string) {\n for (const entries of Object.values(generatedEntries)) {\n const match = entries.find((entry) => entry.path === path);\n\n if (match) return match;\n }\n }\n\n function fileContent(index: IndexItem): string {\n const content: string[] = [];\n content.push('<Cards>');\n const pathToEntry = new Map<string, OutputEntry>();\n const only = index.only ?? Object.keys(context.generated);\n\n for (const item of only) {\n if (generatedEntries[item]) {\n for (const entry of generatedEntries[item]) {\n pathToEntry.set(entry.path, entry);\n }\n } else {\n const match = findEntryByPath(item);\n if (!match) {\n throw new Error(\n `${item} does not exist on \"input\", available: ${Object.keys(generatedEntries).join(', ')}.`,\n );\n }\n\n pathToEntry.set(match.path, match);\n }\n }\n\n for (const file of pathToEntry.values()) {\n const descriptionAttr = file.info.description\n ? `description=${JSON.stringify(file.info.description)} `\n : '';\n content.push(\n `<Card href=\"${urlFn(file.path)}\" title=${JSON.stringify(file.info.title)} ${descriptionAttr}/>`,\n );\n }\n\n content.push('</Cards>');\n return generateDocument(\n {\n title: index.title ?? 'Overview',\n description: index.description,\n },\n content.join('\\n'),\n options,\n );\n }\n\n for (const item of typeof items === 'function' ? items(context) : items) {\n files.push({\n path: path.extname(item.path).length === 0 ? `${item.path}.mdx` : item.path,\n content: fileContent(item),\n });\n }\n}\n"],"mappings":";;;;;;;AA0EA,eAAsB,cAAc,SAAgC;CAClE,MAAM,QAAQ,MAAM,kBAAkB,QAAQ;CAC9C,MAAM,EAAE,WAAW;AAEnB,OAAM,QAAQ,IACZ,MAAM,IAAI,OAAO,SAAS;EACxB,MAAM,WAAW,KAAK,KAAK,QAAQ,KAAK,KAAK;AAE7C,QAAM,MAAM,KAAK,QAAQ,SAAS,EAAE,EAAE,WAAW,MAAM,CAAC;AACxD,QAAM,UAAU,UAAU,KAAK,QAAQ;AACvC,UAAQ,IAAI,cAAc,WAAW;GACrC,CACH;;AAGH,eAAsB,kBACpB,SACuB;CACvB,MAAM,UAAU,MAAM,QAAQ,MAAM,YAAY;CAEhD,MAAM,QAAsB,EAAE;CAC9B,MAAM,YAA0C,EAAE;CAClD,MAAM,mBAAkD,EAAE;CAE1D,MAAM,UAAU,OAAO,QAAQ,QAAQ;AACvC,KAAI,QAAQ,WAAW,EACrB,OAAM,IAAI,MAAM,wBAAwB;CAE1C,MAAM,SAAS,iBAAiB,QAAQ;AACxC,MAAK,MAAM,CAAC,IAAI,WAAW,SAAS;EAClC,MAAM,UAAU,WAAW,IAAI,QAAQ,OAAO;EAC9C,MAAM,cAAe,UAAU,QAAQ,EAAE;AAEzC,mBAAiB,MAAM;AACvB,OAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,OAAmB;IACvB,MAAM,MAAM;IACZ,SAAS,OAAO,OAAO,QAAQ,QAAQ;IACxC;AAED,eAAY,KAAK,KAAK;AACtB,SAAM,KAAK,KAAK;;;CAIpB,MAAM,UAA8B;EAClC;EACA;EACA,WAAW;EACZ;AAED,KAAI,QAAQ,MACV,iBAAgB,OAAO,SAAS,QAAQ;AAG1C,OAAM,QAAQ,aAAa,KAAK,SAAS,MAAM;AAC/C,QAAO;;AAGT,SAAS,gBACP,OACA,SACA,SACA;CACA,MAAM,EAAE,qBAAqB;CAC7B,MAAM,EAAE,OAAO,QAAQ,QAAQ;CAE/B,IAAI;AACJ,KAAI,OAAO,QAAQ,UAAU;EAC3B,MAAM,SAAS,aAAa,IAAI,QAAQ;AAExC,WAAS,SAAS,OAAO,SAAS,KAAK,SAAS,IAAI,YAAY,KAAK,CAAC,CAAC;OAEvE,SAAQ;CAGV,SAAS,gBAAgB,MAAc;AACrC,OAAK,MAAM,WAAW,OAAO,OAAO,iBAAiB,EAAE;GACrD,MAAM,QAAQ,QAAQ,MAAM,UAAU,MAAM,SAAS,KAAK;AAE1D,OAAI,MAAO,QAAO;;;CAItB,SAAS,YAAY,OAA0B;EAC7C,MAAM,UAAoB,EAAE;AAC5B,UAAQ,KAAK,UAAU;EACvB,MAAM,8BAAc,IAAI,KAA0B;EAClD,MAAM,OAAO,MAAM,QAAQ,OAAO,KAAK,QAAQ,UAAU;AAEzD,OAAK,MAAM,QAAQ,KACjB,KAAI,iBAAiB,MACnB,MAAK,MAAM,SAAS,iBAAiB,MACnC,aAAY,IAAI,MAAM,MAAM,MAAM;OAE/B;GACL,MAAM,QAAQ,gBAAgB,KAAK;AACnC,OAAI,CAAC,MACH,OAAM,IAAI,MACR,GAAG,KAAK,yCAAyC,OAAO,KAAK,iBAAiB,CAAC,KAAK,KAAK,CAAC,GAC3F;AAGH,eAAY,IAAI,MAAM,MAAM,MAAM;;AAItC,OAAK,MAAM,QAAQ,YAAY,QAAQ,EAAE;GACvC,MAAM,kBAAkB,KAAK,KAAK,cAC9B,eAAe,KAAK,UAAU,KAAK,KAAK,YAAY,CAAC,KACrD;AACJ,WAAQ,KACN,eAAe,MAAM,KAAK,KAAK,CAAC,UAAU,KAAK,UAAU,KAAK,KAAK,MAAM,CAAC,GAAG,gBAAgB,IAC9F;;AAGH,UAAQ,KAAK,WAAW;AACxB,SAAO,iBACL;GACE,OAAO,MAAM,SAAS;GACtB,aAAa,MAAM;GACpB,EACD,QAAQ,KAAK,KAAK,EAClB,QACD;;AAGH,MAAK,MAAM,QAAQ,OAAO,UAAU,aAAa,MAAM,QAAQ,GAAG,MAChE,OAAM,KAAK;EACT,MAAM,KAAK,QAAQ,KAAK,KAAK,CAAC,WAAW,IAAI,GAAG,KAAK,KAAK,QAAQ,KAAK;EACvE,SAAS,YAAY,KAAK;EAC3B,CAAC"}
1
+ {"version":3,"file":"generate-file.js","names":["path"],"sources":["../src/generate-file.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { generateDocument, type PagesToTextOptions, toText } from './utils/pages/to-text';\nimport type { ProcessedDocument } from '@/utils/process-document';\nimport type { OpenAPIServer } from '@/server';\nimport { createGetUrl, getSlugs, PathUtils } from 'fumadocs-core/source';\nimport { createAutoPreset, type SchemaToPagesOptions } from '@/utils/pages/preset-auto';\nimport { fromSchema, type OutputGroup, type OutputEntry } from '@/utils/pages/builder';\n\nexport interface OutputFile {\n path: string;\n content: string;\n}\n\ninterface IndexConfig {\n items: IndexItem[] | ((ctx: BeforeWriteContext) => IndexItem[]);\n\n /**\n * Generate URLs for cards\n */\n url:\n | ((filePath: string) => string)\n | {\n baseUrl: string;\n /**\n * Base content directory\n */\n contentDir: string;\n };\n}\n\ninterface IndexItem {\n /** output path of index page */\n path: string;\n title?: string;\n description?: string;\n\n /**\n * Specify linked pages:\n * - items in `inputs` to include all generated pages of a specific schema.\n * - file paths (using forward slash).\n */\n only?: string[];\n}\n\ninterface GenerateFilesConfig extends PagesToTextOptions {\n /**\n * the OpenAPI server object\n */\n input: OpenAPIServer;\n\n /**\n * Output directory\n */\n output: string;\n\n /**\n * Generate index files with cards linking to generated pages.\n */\n index?: IndexConfig;\n\n /**\n * Generate `meta.json` files.\n *\n * Note: for flexibility, it's recommended to define them on your own.\n */\n meta?: boolean | MetaOptions;\n\n /**\n * Can add/change/remove output files before writing to file system\n **/\n beforeWrite?: (this: BeforeWriteContext, files: OutputFile[]) => void | Promise<void>;\n}\n\ninterface MetaOptions {\n groupStyle?: 'folder' | 'separator';\n}\n\nexport type Config = SchemaToPagesOptions & GenerateFilesConfig;\n\ninterface BeforeWriteContext {\n readonly generated: Record<string, OutputFile[]>;\n readonly generatedEntries: Record<string, OutputEntry[]>;\n readonly documents: Record<string, ProcessedDocument>;\n}\n\nexport async function generateFiles(options: Config): Promise<void> {\n const files = await generateFilesOnly(options);\n const { output } = options;\n\n await Promise.all(\n files.map(async (file) => {\n const filePath = path.join(output, file.path);\n\n await mkdir(path.dirname(filePath), { recursive: true });\n await writeFile(filePath, file.content);\n console.log(`Generated: ${filePath}`);\n }),\n );\n}\n\nexport async function generateFilesOnly(\n options: SchemaToPagesOptions & Omit<GenerateFilesConfig, 'output'>,\n): Promise<OutputFile[]> {\n const schemas = await options.input.getSchemas();\n\n const files: OutputFile[] = [];\n const generated: Record<string, OutputFile[]> = {};\n const generatedEntries: Record<string, OutputEntry[]> = {};\n\n const entries = Object.entries(schemas);\n if (entries.length === 0) {\n throw new Error('No input files found.');\n }\n const preset = createAutoPreset(options);\n for (const [id, schema] of entries) {\n const entries = fromSchema(id, schema, preset);\n const schemaFiles = (generated[id] ??= []);\n\n generatedEntries[id] = entries;\n function scan(entry: OutputEntry) {\n if (entry.type === 'group') {\n for (const child of entry.entries) scan(child);\n return;\n }\n\n schemaFiles.push({\n path: entry.path,\n content: toText(entry, schema, options),\n });\n }\n\n for (const entry of entries) scan(entry);\n files.push(...schemaFiles);\n }\n\n const context: BeforeWriteContext = {\n generated,\n generatedEntries,\n documents: schemas,\n };\n\n if (options.index) {\n files.push(...writeIndexFiles(context, options));\n }\n\n if (options.meta) {\n files.push(...generateMeta(context, options.meta === true ? {} : options.meta));\n }\n\n await options.beforeWrite?.call(context, files);\n return files;\n}\n\nfunction generateMeta(context: BeforeWriteContext, options: MetaOptions): OutputFile[] {\n const files: OutputFile[] = [];\n const { groupStyle = 'folder' } = options;\n\n function scan(entries: OutputEntry[], parent?: OutputGroup) {\n const pages: string[] = [];\n\n for (const entry of entries) {\n const relativePath = PathUtils.slash(\n parent ? path.relative(parent.path, entry.path) : entry.path,\n );\n\n if (entry.type === 'group') {\n scan(entry.entries, entry);\n\n if (groupStyle === 'folder') {\n pages.push(relativePath);\n } else {\n pages.push(`---${entry.info.title}---`, `...${relativePath}`);\n }\n } else {\n pages.push(relativePath.slice(0, -path.extname(entry.path).length));\n }\n }\n\n if (pages.length === 0) return;\n files.push({\n path: parent ? path.join(parent.path, 'meta.json') : 'meta.json',\n content: JSON.stringify(\n {\n title: parent?.info.title,\n description: parent?.info.description,\n pages,\n },\n null,\n 2,\n ),\n });\n }\n\n for (const entries of Object.values(context.generatedEntries)) {\n scan(entries);\n }\n\n return files;\n}\n\nfunction writeIndexFiles(\n context: BeforeWriteContext,\n options: SchemaToPagesOptions & Omit<GenerateFilesConfig, 'output'>,\n): OutputFile[] {\n const files: OutputFile[] = [];\n const { generatedEntries } = context;\n const pathToEntry = new Map<string, OutputEntry>();\n const { items, url } = options.index!;\n\n function indexEntry(entry: OutputEntry) {\n pathToEntry.set(PathUtils.slash(entry.path), entry);\n if (entry.type === 'group') {\n for (const child of entry.entries) {\n indexEntry(child);\n }\n }\n }\n\n let urlFn: (path: string) => string;\n if (typeof url === 'object') {\n const getUrl = createGetUrl(url.baseUrl);\n\n urlFn = (file) => getUrl(getSlugs(path.relative(url.contentDir, file)));\n } else {\n urlFn = url;\n }\n\n function fileContent(index: IndexItem): string {\n const content: string[] = [];\n content.push('<Cards>');\n const outputEntries: OutputEntry[] = [];\n const only = index.only ?? Object.keys(context.generated);\n\n for (const item of only) {\n if (generatedEntries[item]) {\n for (const entry of generatedEntries[item]) {\n outputEntries.push(entry);\n }\n } else {\n const match = pathToEntry.get(item);\n if (!match) {\n throw new Error(\n `${item} does not exist on \"input\", available: ${Object.keys(generatedEntries).join(', ')}.`,\n );\n }\n\n outputEntries.push(match);\n }\n }\n\n for (const entry of outputEntries) {\n // cannot link to groups\n if (entry.type === 'group') continue;\n const descriptionAttr = entry.info.description\n ? `description=${JSON.stringify(entry.info.description)} `\n : '';\n content.push(\n `<Card href=\"${urlFn(entry.path)}\" title=${JSON.stringify(entry.info.title)} ${descriptionAttr}/>`,\n );\n }\n\n content.push('</Cards>');\n return generateDocument(\n {\n title: index.title ?? 'Overview',\n description: index.description,\n },\n content.join('\\n'),\n options,\n );\n }\n\n for (const list of Object.values(context.generatedEntries)) {\n for (const item of list) indexEntry(item);\n }\n\n for (const item of typeof items === 'function' ? items(context) : items) {\n files.push({\n path: path.extname(item.path).length === 0 ? `${item.path}.mdx` : item.path,\n content: fileContent(item),\n });\n }\n\n return files;\n}\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { MediaAdapter } from "./requests/media/adapter.js";
2
- import { OperationOutput, OutputEntry, OutputGroup, PagesBuilder, PagesBuilderConfig, TagOutput, WebhookOutput, fromSchema, fromServer } from "./utils/pages/builder.js";
2
+ import { OperationOutput, OutputEntry, OutputGroup, PageOutput, PagesBuilder, PagesBuilderConfig, WebhookOutput, fromSchema, fromServer } from "./utils/pages/builder.js";
3
3
  import { SchemaToPagesOptions, createAutoPreset } from "./utils/pages/preset-auto.js";
4
4
  import { Awaitable, CallbackObject, DistributiveOmit, Document, ExampleObject, HttpMethods, MediaTypeObject, MethodInformation, OAuth2SecurityScheme, OperationObject, ParameterObject, PathItemObject, ReferenceObject, RenderContext, RequestBodyObject, ResponseObject, SecuritySchemeObject, ServerObject, ServerVariableObject, TagObject } from "./types.js";
5
5
  import { Config, OutputFile, generateFiles, generateFilesOnly } from "./generate-file.js";
6
- export { Awaitable, CallbackObject, Config, DistributiveOmit, Document, ExampleObject, HttpMethods, type MediaAdapter, MediaTypeObject, MethodInformation, OAuth2SecurityScheme, OperationObject, OperationOutput, OutputEntry, OutputFile, OutputGroup, PagesBuilder, PagesBuilderConfig, ParameterObject, PathItemObject, ReferenceObject, RenderContext, RequestBodyObject, ResponseObject, SchemaToPagesOptions, SecuritySchemeObject, ServerObject, ServerVariableObject, TagObject, TagOutput, WebhookOutput, createAutoPreset, fromSchema, fromServer, generateFiles, generateFilesOnly };
6
+ export { Awaitable, CallbackObject, Config, DistributiveOmit, Document, ExampleObject, HttpMethods, type MediaAdapter, MediaTypeObject, MethodInformation, OAuth2SecurityScheme, OperationObject, OperationOutput, OutputEntry, OutputFile, OutputGroup, PageOutput, PagesBuilder, PagesBuilderConfig, ParameterObject, PathItemObject, ReferenceObject, RenderContext, RequestBodyObject, ResponseObject, SchemaToPagesOptions, SecuritySchemeObject, ServerObject, ServerVariableObject, TagObject, WebhookOutput, createAutoPreset, fromSchema, fromServer, generateFiles, generateFilesOnly };
@@ -1,5 +1,4 @@
1
1
  import { ReactNode } from "react";
2
- import { Ajv2020 } from "ajv/dist/2020";
3
2
  import { FieldKey } from "@fumari/stf";
4
3
 
5
4
  //#region src/playground/schema.d.ts
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","names":[],"sources":["../../src/playground/schema.tsx"],"mappings":";;;;;UAciB,WAAA;;;;EAIf,SAAA;;;;EAKA,QAAA;AAAA"}
1
+ {"version":3,"file":"schema.d.ts","names":[],"sources":["../../src/playground/schema.tsx"],"mappings":";;;;UAciB,WAAA;EASP;;;EALR,SAAA;;;;EAKA,QAAA;AAAA"}
@@ -2,7 +2,7 @@ import { mergeAllOf } from "../utils/merge-schema.js";
2
2
  import { schemaToString } from "../utils/schema-to-string.js";
3
3
  import { createContext, use, useMemo } from "react";
4
4
  import { jsx } from "react/jsx-runtime";
5
- import { Ajv2020 } from "ajv/dist/2020";
5
+ import { Ajv2020 } from "ajv/dist/2020.js";
6
6
  import { useDataEngine, useFieldValue, useNamespace } from "@fumari/stf";
7
7
  import { stringifyFieldKey } from "@fumari/stf/lib/utils";
8
8
  import { sample } from "openapi-sampler";
@@ -1,6 +1,6 @@
1
1
  import { escapeString, inputToString } from "../string-utils.js";
2
2
  import "./resolve-adapter.js";
3
- import js2xml from "xml-js/lib/js2xml";
3
+ import js2xml from "xml-js/lib/js2xml.js";
4
4
  //#region src/requests/media/adapter.ts
5
5
  const defaultAdapters = {
6
6
  "application/json": {
@@ -1,4 +1,4 @@
1
- import js2xml from "xml-js/lib/js2xml";
1
+ import js2xml from "xml-js/lib/js2xml.js";
2
2
  //#region src/requests/string-utils.ts
3
3
  /**
4
4
  * Convert input value to hardcoded string (with quotes)
@@ -2,7 +2,7 @@ import { OpenAPIServer } from "./create.js";
2
2
  import { ApiPageProps } from "../ui/api-page.js";
3
3
  import { SchemaToPagesOptions } from "../utils/pages/preset-auto.js";
4
4
  import { ProcessedDocument } from "../utils/process-document.js";
5
- import { LoaderPlugin, PageData, PageTreeTransformer, Source } from "fumadocs-core/source";
5
+ import { LoaderPlugin, MetaData, PageData, PageTreeTransformer, Source } from "fumadocs-core/source";
6
6
  import { StructuredData } from "fumadocs-core/mdx-plugins";
7
7
  import { TOCItemType } from "fumadocs-core/toc";
8
8
 
@@ -31,13 +31,17 @@ interface OpenAPIPageData extends PageData {
31
31
  structuredData: StructuredData;
32
32
  toc: TOCItemType[];
33
33
  }
34
+ interface MetaOptions {
35
+ folderStyle?: 'folder' | 'separator';
36
+ }
34
37
  /**
35
38
  * Generate virtual pages for Fumadocs Source API
36
39
  */
37
40
  declare function openapiSource(server: OpenAPIServer, options?: SchemaToPagesOptions & {
38
- baseDir?: string;
41
+ baseDir?: string; /** Generate `meta.json` files */
42
+ meta?: boolean | MetaOptions;
39
43
  }): Promise<Source<{
40
- metaData: never;
44
+ metaData: MetaData;
41
45
  pageData: OpenAPIPageData;
42
46
  }>>;
43
47
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"source-api.d.ts","names":[],"sources":["../../src/server/source-api.tsx"],"mappings":";;;;;;;;;;YAgBmB,QAAA;IAH+C;;;IAO9D,QAAA,GAAW,mBAAA;EAAA;AAAA;AAAA,UAIE,mBAAA;EACf,MAAA;EACA,OAAA;AAAA;AAFF;;;AAAA,iBAQgB,aAAA,CAAA,GAAiB,YAAA;AAAA,UAuCvB,eAAA,SAAwB,QAAA;EAChC,eAAA,QAAuB,YAAA;EACvB,SAAA;IAAmB,EAAA;EAAA,IAAe,iBAAA;EAClC,cAAA,EAAgB,cAAA;EAChB,GAAA,EAAK,WAAA;AAAA;;;;iBAMe,aAAA,CACpB,MAAA,EAAQ,aAAA,EACR,OAAA,GAAS,oBAAA;EACP,OAAA;AAAA,IAED,OAAA,CACD,MAAA;EACE,QAAA;EACA,QAAA,EAAU,eAAA;AAAA;;;;iBAuDE,kBAAA,CAAA,GAAsB,mBAAA"}
1
+ {"version":3,"file":"source-api.d.ts","names":[],"sources":["../../src/server/source-api.tsx"],"mappings":";;;;;;;;;;YAyBmB,QAAA;IAV+C;;;IAc9D,QAAA,GAAW,mBAAA;EAAA;AAAA;AAAA,UAIE,mBAAA;EACf,MAAA;EACA,OAAA;AAAA;AAFF;;;AAAA,iBAQgB,aAAA,CAAA,GAAiB,YAAA;AAAA,UAuCvB,eAAA,SAAwB,QAAA;EAChC,eAAA,QAAuB,YAAA;EACvB,SAAA;IAAmB,EAAA;EAAA,IAAe,iBAAA;EAClC,cAAA,EAAgB,cAAA;EAChB,GAAA,EAAK,WAAA;AAAA;AAAA,UAGG,WAAA;EACR,WAAA;AAAA;;;;iBAMoB,aAAA,CACpB,MAAA,EAAQ,aAAA,EACR,OAAA,GAAS,oBAAA;EACP,OAAA,WAjB8B;EAmB9B,IAAA,aAAiB,WAAA;AAAA,IAElB,OAAA,CACD,MAAA;EACE,QAAA,EAAU,QAAA;EACV,QAAA,EAAU,eAAA;AAAA;;;;iBA6HE,kBAAA,CAAA,GAAsB,mBAAA"}
@@ -1,4 +1,6 @@
1
1
  import { MethodLabel } from "../ui/components/method-label.js";
2
+ import path from "node:path";
3
+ import { PathUtils } from "fumadocs-core/source";
2
4
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
5
  //#region src/server/source-api.tsx
4
6
  /**
@@ -38,18 +40,16 @@ function openapiPlugin() {
38
40
  * Generate virtual pages for Fumadocs Source API
39
41
  */
40
42
  async function openapiSource(server, options = {}) {
41
- const { baseDir = "" } = options;
43
+ const { baseDir = "", meta = false } = options;
42
44
  const { createAutoPreset } = await import("../utils/pages/preset-auto.js");
43
45
  const { fromServer } = await import("../utils/pages/builder.js");
44
- const { toBody } = await import("../utils/pages/to-body.js");
45
46
  const { toStaticData } = await import("../utils/pages/to-static-data.js");
46
47
  const files = [];
47
48
  const entries = await fromServer(server, createAutoPreset(options));
48
49
  for (const [schemaId, list] of Object.entries(entries)) {
49
50
  const processed = await server.getSchema(schemaId);
50
- for (const entry of list) {
51
- const props = toBody(entry);
52
- props.showDescription ??= true;
51
+ function onEntry(entry) {
52
+ const props = getProps(entry);
53
53
  files.push({
54
54
  type: "page",
55
55
  path: `${baseDir}/${entry.path}`,
@@ -72,9 +72,59 @@ async function openapiSource(server, options = {}) {
72
72
  }
73
73
  });
74
74
  }
75
+ function onEntries(entries, parent) {
76
+ if (!meta) {
77
+ for (const entry of entries) if (entry.type === "group") onEntries(entry.entries, entry);
78
+ else onEntry(entry);
79
+ return;
80
+ }
81
+ const { folderStyle = "folder" } = meta === true ? {} : meta;
82
+ const pages = [];
83
+ for (const entry of entries) {
84
+ const relativePath = PathUtils.slash(parent ? path.relative(parent.path, entry.path) : entry.path);
85
+ if (entry.type === "group") {
86
+ onEntries(entry.entries, entry);
87
+ if (folderStyle === "folder") pages.push(relativePath);
88
+ else pages.push(`---${entry.info.title}---`, `...${relativePath}`);
89
+ } else {
90
+ onEntry(entry);
91
+ pages.push(relativePath.slice(0, -path.extname(entry.path).length));
92
+ }
93
+ }
94
+ if (pages.length === 0) return;
95
+ files.push({
96
+ type: "meta",
97
+ path: path.join(baseDir, parent?.path ?? "", "meta.json"),
98
+ data: {
99
+ title: parent?.info.title,
100
+ description: parent?.info.description,
101
+ pages
102
+ }
103
+ });
104
+ }
105
+ onEntries(list);
75
106
  }
76
107
  return { files };
77
108
  }
109
+ function getProps(entry) {
110
+ if (entry.type === "operation") return {
111
+ document: entry.schemaId,
112
+ operations: [entry.item],
113
+ showDescription: true
114
+ };
115
+ if (entry.type === "webhook") return {
116
+ document: entry.schemaId,
117
+ webhooks: [entry.item],
118
+ showDescription: true
119
+ };
120
+ return {
121
+ showTitle: true,
122
+ showDescription: true,
123
+ document: entry.schemaId,
124
+ operations: entry.operations,
125
+ webhooks: entry.webhooks
126
+ };
127
+ }
78
128
  /**
79
129
  * @deprecated use `openapiPlugin()`
80
130
  */
@@ -1 +1 @@
1
- {"version":3,"file":"source-api.js","names":[],"sources":["../../src/server/source-api.tsx"],"sourcesContent":["import { MethodLabel } from '@/ui/components/method-label';\nimport type {\n LoaderPlugin,\n PageData,\n PageTreeTransformer,\n Source,\n VirtualFile,\n} from 'fumadocs-core/source';\nimport type { OpenAPIServer } from '@/server/create';\nimport type { SchemaToPagesOptions } from '@/utils/pages/preset-auto';\nimport type { ApiPageProps } from '@/ui/api-page';\nimport type { StructuredData } from 'fumadocs-core/mdx-plugins';\nimport type { TOCItemType } from 'fumadocs-core/toc';\nimport type { ProcessedDocument } from '@/utils/process-document';\n\ndeclare module 'fumadocs-core/source' {\n export interface PageData {\n /**\n * Added by Fumadocs OpenAPI\n */\n _openapi?: InternalOpenAPIMeta;\n }\n}\n\nexport interface InternalOpenAPIMeta {\n method?: string;\n webhook?: boolean;\n}\n\n/**\n * Fumadocs Source API integration, pass this to `plugins` array in `loader()`.\n */\nexport function openapiPlugin(): LoaderPlugin {\n return {\n name: 'fumadocs:openapi',\n enforce: 'pre',\n transformPageTree: {\n file(node, filePath) {\n if (!filePath) return node;\n const file = this.storage.read(filePath);\n if (!file || file.format !== 'page') return node;\n\n const openApiData = file.data._openapi;\n if (!openApiData || typeof openApiData !== 'object') return node;\n\n if (openApiData.webhook) {\n node.name = (\n <>\n {node.name}{' '}\n <span className=\"ms-auto border border-current px-1 rounded-lg text-xs text-nowrap font-mono\">\n Webhook\n </span>\n </>\n );\n } else if (openApiData.method) {\n node.name = (\n <>\n {node.name}{' '}\n <MethodLabel className=\"ms-auto text-xs text-nowrap\">\n {openApiData.method}\n </MethodLabel>\n </>\n );\n }\n\n return node;\n },\n },\n };\n}\n\ninterface OpenAPIPageData extends PageData {\n getAPIPageProps: () => ApiPageProps;\n getSchema: () => { id: string } & ProcessedDocument;\n structuredData: StructuredData;\n toc: TOCItemType[];\n}\n\n/**\n * Generate virtual pages for Fumadocs Source API\n */\nexport async function openapiSource(\n server: OpenAPIServer,\n options: SchemaToPagesOptions & {\n baseDir?: string;\n } = {},\n): Promise<\n Source<{\n metaData: never;\n pageData: OpenAPIPageData;\n }>\n> {\n const { baseDir = '' } = options;\n const { createAutoPreset } = await import('@/utils/pages/preset-auto');\n const { fromServer } = await import('@/utils/pages/builder');\n const { toBody } = await import('@/utils/pages/to-body');\n const { toStaticData } = await import('@/utils/pages/to-static-data');\n const files: VirtualFile<{\n pageData: OpenAPIPageData;\n metaData: never;\n }>[] = [];\n\n const entries = await fromServer(server, createAutoPreset(options));\n for (const [schemaId, list] of Object.entries(entries)) {\n const processed = await server.getSchema(schemaId);\n for (const entry of list) {\n const props = toBody(entry);\n props.showDescription ??= true;\n\n files.push({\n type: 'page',\n path: `${baseDir}/${entry.path}`,\n data: {\n ...entry.info,\n getAPIPageProps() {\n return props;\n },\n getSchema() {\n return {\n id: schemaId,\n ...processed,\n };\n },\n ...toStaticData(props, processed.dereferenced),\n _openapi: {\n method:\n entry.type === 'operation' || entry.type === 'webhook'\n ? entry.item.method\n : undefined,\n webhook: entry.type === 'webhook',\n },\n },\n });\n }\n }\n\n return {\n files,\n };\n}\n\n/**\n * @deprecated use `openapiPlugin()`\n */\nexport function transformerOpenAPI(): PageTreeTransformer {\n return openapiPlugin().transformPageTree!;\n}\n"],"mappings":";;;;;;AAgCA,SAAgB,gBAA8B;AAC5C,QAAO;EACL,MAAM;EACN,SAAS;EACT,mBAAmB,EACjB,KAAK,MAAM,UAAU;AACnB,OAAI,CAAC,SAAU,QAAO;GACtB,MAAM,OAAO,KAAK,QAAQ,KAAK,SAAS;AACxC,OAAI,CAAC,QAAQ,KAAK,WAAW,OAAQ,QAAO;GAE5C,MAAM,cAAc,KAAK,KAAK;AAC9B,OAAI,CAAC,eAAe,OAAO,gBAAgB,SAAU,QAAO;AAE5D,OAAI,YAAY,QACd,MAAK,OACH,qBAAA,UAAA,EAAA,UAAA;IACG,KAAK;IAAM;IACZ,oBAAC,QAAD;KAAM,WAAU;eAA8E;KAEvF,CAAA;IACN,EAAA,CAAA;YAEI,YAAY,OACrB,MAAK,OACH,qBAAA,UAAA,EAAA,UAAA;IACG,KAAK;IAAM;IACZ,oBAAC,aAAD;KAAa,WAAU;eACpB,YAAY;KACD,CAAA;IACb,EAAA,CAAA;AAIP,UAAO;KAEV;EACF;;;;;AAaH,eAAsB,cACpB,QACA,UAEI,EAAE,EAMN;CACA,MAAM,EAAE,UAAU,OAAO;CACzB,MAAM,EAAE,qBAAqB,MAAM,OAAO;CAC1C,MAAM,EAAE,eAAe,MAAM,OAAO;CACpC,MAAM,EAAE,WAAW,MAAM,OAAO;CAChC,MAAM,EAAE,iBAAiB,MAAM,OAAO;CACtC,MAAM,QAGC,EAAE;CAET,MAAM,UAAU,MAAM,WAAW,QAAQ,iBAAiB,QAAQ,CAAC;AACnE,MAAK,MAAM,CAAC,UAAU,SAAS,OAAO,QAAQ,QAAQ,EAAE;EACtD,MAAM,YAAY,MAAM,OAAO,UAAU,SAAS;AAClD,OAAK,MAAM,SAAS,MAAM;GACxB,MAAM,QAAQ,OAAO,MAAM;AAC3B,SAAM,oBAAoB;AAE1B,SAAM,KAAK;IACT,MAAM;IACN,MAAM,GAAG,QAAQ,GAAG,MAAM;IAC1B,MAAM;KACJ,GAAG,MAAM;KACT,kBAAkB;AAChB,aAAO;;KAET,YAAY;AACV,aAAO;OACL,IAAI;OACJ,GAAG;OACJ;;KAEH,GAAG,aAAa,OAAO,UAAU,aAAa;KAC9C,UAAU;MACR,QACE,MAAM,SAAS,eAAe,MAAM,SAAS,YACzC,MAAM,KAAK,SACX,KAAA;MACN,SAAS,MAAM,SAAS;MACzB;KACF;IACF,CAAC;;;AAIN,QAAO,EACL,OACD;;;;;AAMH,SAAgB,qBAA0C;AACxD,QAAO,eAAe,CAAC"}
1
+ {"version":3,"file":"source-api.js","names":[],"sources":["../../src/server/source-api.tsx"],"sourcesContent":["import { MethodLabel } from '@/ui/components/method-label';\nimport {\n PathUtils,\n type LoaderPlugin,\n type MetaData,\n type PageData,\n type PageTreeTransformer,\n type Source,\n type VirtualFile,\n} from 'fumadocs-core/source';\nimport type { OpenAPIServer } from '@/server/create';\nimport type { SchemaToPagesOptions } from '@/utils/pages/preset-auto';\nimport type { ApiPageProps } from '@/ui/api-page';\nimport type { StructuredData } from 'fumadocs-core/mdx-plugins';\nimport type { TOCItemType } from 'fumadocs-core/toc';\nimport type { ProcessedDocument } from '@/utils/process-document';\nimport type {\n OperationOutput,\n OutputEntry,\n PageOutput,\n WebhookOutput,\n} from '@/utils/pages/builder';\nimport path from 'node:path';\n\ndeclare module 'fumadocs-core/source' {\n export interface PageData {\n /**\n * Added by Fumadocs OpenAPI\n */\n _openapi?: InternalOpenAPIMeta;\n }\n}\n\nexport interface InternalOpenAPIMeta {\n method?: string;\n webhook?: boolean;\n}\n\n/**\n * Fumadocs Source API integration, pass this to `plugins` array in `loader()`.\n */\nexport function openapiPlugin(): LoaderPlugin {\n return {\n name: 'fumadocs:openapi',\n enforce: 'pre',\n transformPageTree: {\n file(node, filePath) {\n if (!filePath) return node;\n const file = this.storage.read(filePath);\n if (!file || file.format !== 'page') return node;\n\n const openApiData = file.data._openapi;\n if (!openApiData || typeof openApiData !== 'object') return node;\n\n if (openApiData.webhook) {\n node.name = (\n <>\n {node.name}{' '}\n <span className=\"ms-auto border border-current px-1 rounded-lg text-xs text-nowrap font-mono\">\n Webhook\n </span>\n </>\n );\n } else if (openApiData.method) {\n node.name = (\n <>\n {node.name}{' '}\n <MethodLabel className=\"ms-auto text-xs text-nowrap\">\n {openApiData.method}\n </MethodLabel>\n </>\n );\n }\n\n return node;\n },\n },\n };\n}\n\ninterface OpenAPIPageData extends PageData {\n getAPIPageProps: () => ApiPageProps;\n getSchema: () => { id: string } & ProcessedDocument;\n structuredData: StructuredData;\n toc: TOCItemType[];\n}\n\ninterface MetaOptions {\n folderStyle?: 'folder' | 'separator';\n}\n\n/**\n * Generate virtual pages for Fumadocs Source API\n */\nexport async function openapiSource(\n server: OpenAPIServer,\n options: SchemaToPagesOptions & {\n baseDir?: string;\n /** Generate `meta.json` files */\n meta?: boolean | MetaOptions;\n } = {},\n): Promise<\n Source<{\n metaData: MetaData;\n pageData: OpenAPIPageData;\n }>\n> {\n const { baseDir = '', meta = false } = options;\n const { createAutoPreset } = await import('@/utils/pages/preset-auto');\n const { fromServer } = await import('@/utils/pages/builder');\n const { toStaticData } = await import('@/utils/pages/to-static-data');\n const files: VirtualFile<{\n pageData: OpenAPIPageData;\n metaData: MetaData;\n }>[] = [];\n\n const entries = await fromServer(server, createAutoPreset(options));\n for (const [schemaId, list] of Object.entries(entries)) {\n const processed = await server.getSchema(schemaId);\n\n function onEntry(entry: PageOutput | OperationOutput | WebhookOutput) {\n const props = getProps(entry);\n\n files.push({\n type: 'page',\n path: `${baseDir}/${entry.path}`,\n data: {\n ...entry.info,\n getAPIPageProps() {\n return props;\n },\n getSchema() {\n return {\n id: schemaId,\n ...processed,\n };\n },\n ...toStaticData(props, processed.dereferenced),\n _openapi: {\n method:\n entry.type === 'operation' || entry.type === 'webhook'\n ? entry.item.method\n : undefined,\n webhook: entry.type === 'webhook',\n },\n },\n });\n }\n\n function onEntries(entries: OutputEntry[], parent?: OutputEntry) {\n if (!meta) {\n for (const entry of entries) {\n if (entry.type === 'group') {\n onEntries(entry.entries, entry);\n } else {\n onEntry(entry);\n }\n }\n\n return;\n }\n\n const { folderStyle = 'folder' } = meta === true ? {} : meta;\n const pages: string[] = [];\n\n for (const entry of entries) {\n const relativePath = PathUtils.slash(\n parent ? path.relative(parent.path, entry.path) : entry.path,\n );\n\n if (entry.type === 'group') {\n onEntries(entry.entries, entry);\n if (folderStyle === 'folder') {\n pages.push(relativePath);\n } else {\n pages.push(`---${entry.info.title}---`, `...${relativePath}`);\n }\n } else {\n onEntry(entry);\n pages.push(relativePath.slice(0, -path.extname(entry.path).length));\n }\n }\n\n if (pages.length === 0) return;\n files.push({\n type: 'meta',\n path: path.join(baseDir, parent?.path ?? '', 'meta.json'),\n data: {\n title: parent?.info.title,\n description: parent?.info.description,\n pages,\n },\n });\n }\n\n onEntries(list);\n }\n\n return {\n files,\n };\n}\n\nfunction getProps(entry: PageOutput | OperationOutput | WebhookOutput): ApiPageProps {\n if (entry.type === 'operation')\n return {\n document: entry.schemaId,\n operations: [entry.item],\n showDescription: true,\n };\n if (entry.type === 'webhook')\n return {\n document: entry.schemaId,\n webhooks: [entry.item],\n showDescription: true,\n };\n\n return {\n showTitle: true,\n showDescription: true,\n document: entry.schemaId,\n operations: entry.operations,\n webhooks: entry.webhooks,\n };\n}\n\n/**\n * @deprecated use `openapiPlugin()`\n */\nexport function transformerOpenAPI(): PageTreeTransformer {\n return openapiPlugin().transformPageTree!;\n}\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { HttpMethods } from "../types.js";
2
2
  import { ProcessedDocument } from "../utils/process-document.js";
3
+
3
4
  //#region src/ui/api-page.d.ts
4
5
  interface ApiPageProps {
5
6
  document: Promise<ProcessedDocument> | string | ProcessedDocument;
@@ -1 +1 @@
1
- {"version":3,"file":"api-page.d.ts","names":[],"sources":["../../src/ui/api-page.tsx"],"mappings":";;;UAMiB,YAAA;EACf,QAAA,EAAU,OAAA,CAAQ,iBAAA,aAA8B,iBAAA;EAChD,SAAA;EACA,eAAA;EAH2B;;;EAQ3B,UAAA,GAAa,aAAA;EAEb,QAAA,GAAW,WAAA;AAAA;AAAA,UAGI,WAAA;EAHO;;;EAOtB,IAAA;EACA,MAAA,EAAQ,WAAA;AAAA;AAAA,UAGO,aAAA;EAlBf;;;EAsBA,IAAA;EAfW;;;EAmBX,MAAA,EAAQ,WAAA;AAAA"}
1
+ {"version":3,"file":"api-page.d.ts","names":[],"sources":["../../src/ui/api-page.tsx"],"mappings":";;;;UAMiB,YAAA;EACf,QAAA,EAAU,OAAA,CAAQ,iBAAA,aAA8B,iBAAA;EAChD,SAAA;EACA,eAAA;;;;EAKA,UAAA,GAAa,aAAA;EAEb,QAAA,GAAW,WAAA;AAAA;AAAA,UAGI,WAAA;EAHO;;;EAOtB,IAAA;EACA,MAAA,EAAQ,WAAA;AAAA;AAAA,UAGO,aAAA;EAbf;;;EAiBA,IAAA;EAfsB;;AAGxB;EAgBE,MAAA,EAAQ,WAAA;AAAA"}
@@ -1,5 +1,6 @@
1
1
  import { RawRequestData, RequestData } from "../../requests/types.js";
2
2
  import { ReactNode } from "react";
3
+
3
4
  //#region src/ui/operation/request-tabs.d.ts
4
5
  interface ExampleRequestItem {
5
6
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"request-tabs.d.ts","names":[],"sources":["../../../src/ui/operation/request-tabs.tsx"],"mappings":";;;UAkBiB,kBAAA;EACf,EAAA;EACA,IAAA;EACA,WAAA;EACA,IAAA,EAAM,cAAA;EACN,OAAA,EAAS,WAAA;AAAA"}
1
+ {"version":3,"file":"request-tabs.d.ts","names":[],"sources":["../../../src/ui/operation/request-tabs.tsx"],"mappings":";;;;UAkBiB,kBAAA;EACf,EAAA;EACA,IAAA;EACA,WAAA;EACA,IAAA,EAAM,cAAA;EACN,OAAA,EAAS,WAAA;AAAA"}
@@ -1,4 +1,5 @@
1
1
  import { SchemaUIGeneratedData } from "./index.js";
2
+
2
3
  //#region src/ui/schema/client.d.ts
3
4
  interface SchemaUIProps {
4
5
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","names":[],"sources":["../../../src/ui/schema/client.tsx"],"mappings":";;UAkEiB,aAAA;EACf,IAAA;EACA,QAAA;EACA,EAAA;EAEA,SAAA,EAAW,qBAAA;AAAA"}
1
+ {"version":3,"file":"client.d.ts","names":[],"sources":["../../../src/ui/schema/client.tsx"],"mappings":";;;UAkEiB,aAAA;EACf,IAAA;EACA,QAAA;EACA,EAAA;EAEA,SAAA,EAAW,qBAAA;AAAA"}
@@ -1,6 +1,7 @@
1
1
  import { ResolvedSchema } from "../../utils/schema.js";
2
2
  import { SchemaUIProps } from "./client.js";
3
3
  import { ReactNode } from "react";
4
+
4
5
  //#region src/ui/schema/index.d.ts
5
6
  interface FieldBase {
6
7
  description?: ReactNode;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/ui/schema/index.tsx"],"mappings":";;;;UASiB,SAAA;EACf,WAAA,GAAc,SAAA;EACd,QAAA,GAAW,OAAA;EAEX,QAAA;EACA,SAAA;EAEA,UAAA;AAAA;AAAA,UAGe,OAAA;EACf,KAAA,EAAO,SAAA;EACP,KAAA;AAAA;AAAA,UAGe,wBAAA;EACf,IAAA;EACA,KAAA;EACA,QAAA;AAAA;AAAA,KAGU,UAAA,GAAa,SAAA;EAGjB,IAAA;AAAA;EAGA,IAAA;EACA,KAAA,EAAO,wBAAA;AAAA;EAGP,IAAA;EACA,IAAA;IACE,KAAA;EAAA;AAAA;EAIF,IAAA;EACA,KAAA;IACE,IAAA;IACA,KAAA;EAAA;AAAA;EAIF,IAAA;EACA,KAAA;IACE,IAAA;IACA,KAAA;EAAA;AAAA;AAAA,UAKO,eAAA;EACf,IAAA,EAAM,cAAA;EACN,MAAA,EAAQ,IAAA,CAAK,aAAA;EA1BA;;;EA+Bb,QAAA;EAtBM;;;EA0BN,SAAA;AAAA;AAAA,UAGe,qBAAA;EACf,KAAA;EACA,IAAA,EAAM,MAAA,SAAe,UAAA;AAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/ui/schema/index.tsx"],"mappings":";;;;;UASiB,SAAA;EACf,WAAA,GAAc,SAAA;EACd,QAAA,GAAW,OAAA;EAEX,QAAA;EACA,SAAA;EAEA,UAAA;AAAA;AAAA,UAGe,OAAA;EACf,KAAA,EAAO,SAAA;EACP,KAAA;AAAA;AAAA,UAGe,wBAAA;EACf,IAAA;EACA,KAAA;EACA,QAAA;AAAA;AAAA,KAGU,UAAA,GAAa,SAAA;EAGjB,IAAA;AAAA;EAGA,IAAA;EACA,KAAA,EAAO,wBAAA;AAAA;EAGP,IAAA;EACA,IAAA;IACE,KAAA;EAAA;AAAA;EAIF,IAAA;EACA,KAAA;IACE,IAAA;IACA,KAAA;EAAA;AAAA;EAIF,IAAA;EACA,KAAA;IACE,IAAA;IACA,KAAA;EAAA;AAAA;AAAA,UAKO,eAAA;EACf,IAAA,EAAM,cAAA;EACN,MAAA,EAAQ,IAAA,CAAK,aAAA;EAvBP;;;EA4BN,QAAA;EArBM;;;EAyBN,SAAA;AAAA;AAAA,UAGe,qBAAA;EACf,KAAA;EACA,IAAA,EAAM,MAAA,SAAe,UAAA;AAAA"}
@@ -21,19 +21,20 @@ interface WebhookOutput extends BaseEntry {
21
21
  type: 'webhook';
22
22
  item: WebhookItem;
23
23
  }
24
- interface TagOutput extends BaseEntry {
25
- type: 'tag';
26
- tag: string;
27
- rawTag: TagObject;
24
+ interface PageOutput extends BaseEntry {
25
+ type: 'page';
28
26
  operations: OperationItem[];
29
27
  webhooks: WebhookItem[];
28
+ /** tag info if the page is generated from a tag. */
29
+ tag?: TagObject;
30
30
  }
31
31
  interface OutputGroup extends BaseEntry {
32
32
  type: 'group';
33
- operations: OperationItem[];
34
- webhooks: WebhookItem[];
33
+ entries: OutputEntry[];
34
+ /** tag info if the group is generated from a tag. */
35
+ tag?: TagObject;
35
36
  }
36
- type OutputEntry = TagOutput | OperationOutput | WebhookOutput | OutputGroup;
37
+ type OutputEntry = PageOutput | OperationOutput | WebhookOutput | OutputGroup;
37
38
  interface PagesBuilderConfig {
38
39
  toPages: (builder: PagesBuilder) => void;
39
40
  }
@@ -45,8 +46,6 @@ interface PagesBuilder {
45
46
  document: ProcessedDocument;
46
47
  /**
47
48
  * add output entry.
48
- *
49
- * When the `path` property is unspecified, it will generate one.
50
49
  */
51
50
  create: (entry: OutputEntry) => void;
52
51
  /**
@@ -86,5 +85,5 @@ interface ExtractedInfo {
86
85
  declare function fromServer(server: OpenAPIServer, config: PagesBuilderConfig): Promise<Record<string, OutputEntry[]>>;
87
86
  declare function fromSchema(schemaId: string, processed: ProcessedDocument, config: PagesBuilderConfig): OutputEntry[];
88
87
  //#endregion
89
- export { OperationOutput, OutputEntry, OutputGroup, PagesBuilder, PagesBuilderConfig, TagOutput, WebhookOutput, fromSchema, fromServer };
88
+ export { OperationOutput, OutputEntry, OutputGroup, PageOutput, PagesBuilder, PagesBuilderConfig, WebhookOutput, fromSchema, fromServer };
90
89
  //# sourceMappingURL=builder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"builder.d.ts","names":[],"sources":["../../../src/utils/pages/builder.ts"],"mappings":";;;;;;;UAOU,SAAA;EACR,IAAA;EACA,QAAA;EACA,IAAA;IACE,KAAA;IACA,WAAA;EAAA;AAAA;AAAA,UAIa,eAAA,SAAwB,SAAA;EACvC,IAAA;EACA,IAAA,EAAM,aAAA;AAAA;AAAA,UAGS,aAAA,SAAsB,SAAA;EACrC,IAAA;EACA,IAAA,EAAM,WAAA;AAAA;AAAA,UAGS,SAAA,SAAkB,SAAA;EACjC,IAAA;EACA,GAAA;EACA,MAAA,EAAQ,SAAA;EACR,UAAA,EAAY,aAAA;EACZ,QAAA,EAAU,WAAA;AAAA;AAAA,UAGK,WAAA,SAAoB,SAAA;EACnC,IAAA;EACA,UAAA,EAAY,aAAA;EACZ,QAAA,EAAU,WAAA;AAAA;AAAA,KAGA,WAAA,GAAc,SAAA,GAAY,eAAA,GAAkB,aAAA,GAAgB,WAAA;AAAA,UAEvD,kBAAA;EACf,OAAA,GAAU,OAAA,EAAS,YAAA;AAAA;AAAA,UAGJ,YAAA;EAvBE;AAGnB;;EAwBE,EAAA;EACA,QAAA,EAAU,iBAAA;EArBE;;;;;EA2BZ,MAAA,GAAS,KAAA,EAAO,WAAA;EA9BhB;;;EAmCA,mBAAA,GAAsB,IAAA;EAhCtB;;;EAqCA,OAAA,QAAe,aAAA;EACf,oBAAA,GAAuB,IAAA,EAAM,WAAA;IAAA,IAEnB,WAAA;IACJ,QAAA,EAAU,WAAA,CAAY,cAAA;IACtB,SAAA,EAAW,WAAA,CAAY,eAAA;EAAA;EAG7B,sBAAA,GAAyB,IAAA,EAAM,aAAA;IAAA,IAErB,WAAA;IACJ,QAAA,EAAU,WAAA,CAAY,cAAA;IACtB,SAAA,EAAW,WAAA,CAAY,eAAA;EAAA;EAG7B,OAAA,GAAU,GAAA,EAAK,SAAA;IAAA,IACT,WAAA;EAAA;EAEN,WAAA,GAAc,GAAA;IAER,IAAA,EAAM,SAAA;IAAA,IACF,WAAA;EAAA;AAAA;AAAA,UAKF,aAAA;EACR,QAAA,GAAW,WAAA;IAAgB,IAAA;EAAA;EAC3B,UAAA,GAAa,aAAA;IACX,IAAA;EAAA;AAAA;AAAA,iBAIkB,UAAA,CACpB,MAAA,EAAQ,aAAA,EACR,MAAA,EAAQ,kBAAA,GACP,OAAA,CAAQ,MAAA,SAAe,WAAA;AAAA,iBAgBV,UAAA,CACd,QAAA,UACA,SAAA,EAAW,iBAAA,EACX,MAAA,EAAQ,kBAAA,GACP,WAAA"}
1
+ {"version":3,"file":"builder.d.ts","names":[],"sources":["../../../src/utils/pages/builder.ts"],"mappings":";;;;;;;UAOU,SAAA;EACR,IAAA;EACA,QAAA;EACA,IAAA;IACE,KAAA;IACA,WAAA;EAAA;AAAA;AAAA,UAIa,eAAA,SAAwB,SAAA;EACvC,IAAA;EACA,IAAA,EAAM,aAAA;AAAA;AAAA,UAGS,aAAA,SAAsB,SAAA;EACrC,IAAA;EACA,IAAA,EAAM,WAAA;AAAA;AAAA,UAGS,UAAA,SAAmB,SAAA;EAClC,IAAA;EACA,UAAA,EAAY,aAAA;EACZ,QAAA,EAAU,WAAA;EAXV;EAaA,GAAA,GAAM,SAAA;AAAA;AAAA,UAGS,WAAA,SAAoB,SAAA;EACnC,IAAA;EACA,OAAA,EAAS,WAAA;;EAET,GAAA,GAAM,SAAA;AAAA;AAAA,KAGI,WAAA,GAAc,UAAA,GAAa,eAAA,GAAkB,aAAA,GAAgB,WAAA;AAAA,UAExD,kBAAA;EACf,OAAA,GAAU,OAAA,EAAS,YAAA;AAAA;AAAA,UAGJ,YAAA;EArBA;;;EAyBf,EAAA;EACA,QAAA,EAAU,iBAAA;EArBJ;;;EAyBN,MAAA,GAAS,KAAA,EAAO,WAAA;EA9BkB;;;EAmClC,mBAAA,GAAsB,IAAA;EAhCtB;;;EAqCA,OAAA,QAAe,aAAA;EACf,oBAAA,GAAuB,IAAA,EAAM,WAAA;IAAA,IAEnB,WAAA;IACJ,QAAA,EAAU,WAAA,CAAY,cAAA;IACtB,SAAA,EAAW,WAAA,CAAY,eAAA;EAAA;EAG7B,sBAAA,GAAyB,IAAA,EAAM,aAAA;IAAA,IAErB,WAAA;IACJ,QAAA,EAAU,WAAA,CAAY,cAAA;IACtB,SAAA,EAAW,WAAA,CAAY,eAAA;EAAA;EAG7B,OAAA,GAAU,GAAA,EAAK,SAAA;IAAA,IACT,WAAA;EAAA;EAEN,WAAA,GAAc,GAAA;IAER,IAAA,EAAM,SAAA;IAAA,IACF,WAAA;EAAA;AAAA;AAAA,UAKF,aAAA;EACR,QAAA,GAAW,WAAA;IAAgB,IAAA;EAAA;EAC3B,UAAA,GAAa,aAAA;IACX,IAAA;EAAA;AAAA;AAAA,iBAIkB,UAAA,CACpB,MAAA,EAAQ,aAAA,EACR,MAAA,EAAQ,kBAAA,GACP,OAAA,CAAQ,MAAA,SAAe,WAAA;AAAA,iBAgBV,UAAA,CACd,QAAA,UACA,SAAA,EAAW,iBAAA,EACX,MAAA,EAAQ,kBAAA,GACP,WAAA"}
@@ -19,7 +19,35 @@ function fromSchema(schemaId, processed, config) {
19
19
  create(entry) {
20
20
  files.push(entry);
21
21
  },
22
- extract: () => extractInfo(dereferenced),
22
+ extract() {
23
+ const result = {
24
+ webhooks: [],
25
+ operations: []
26
+ };
27
+ for (const [path, pathItem] of Object.entries(dereferenced.paths ?? {})) {
28
+ if (!pathItem) continue;
29
+ for (const methodKey of methodKeys) {
30
+ if (!pathItem[methodKey]) continue;
31
+ result.operations.push({
32
+ method: methodKey,
33
+ path,
34
+ tags: pathItem[methodKey]?.tags
35
+ });
36
+ }
37
+ }
38
+ for (const [name, pathItem] of Object.entries(dereferenced.webhooks ?? {})) {
39
+ if (!pathItem) continue;
40
+ for (const methodKey of methodKeys) {
41
+ if (!pathItem[methodKey]) continue;
42
+ result.webhooks.push({
43
+ method: methodKey,
44
+ name,
45
+ tags: pathItem[methodKey]?.tags
46
+ });
47
+ }
48
+ }
49
+ return result;
50
+ },
23
51
  routePathToFilePath(path) {
24
52
  return path.toLowerCase().replaceAll(".", "-").split("/").flatMap((v) => {
25
53
  if (v.startsWith("{") && v.endsWith("}")) return v.slice(1, -1);
@@ -69,35 +97,6 @@ function fromSchema(schemaId, processed, config) {
69
97
  });
70
98
  return files;
71
99
  }
72
- function extractInfo(document) {
73
- const result = {
74
- webhooks: [],
75
- operations: []
76
- };
77
- for (const [path, pathItem] of Object.entries(document.paths ?? {})) {
78
- if (!pathItem) continue;
79
- for (const methodKey of methodKeys) {
80
- if (!pathItem[methodKey]) continue;
81
- result.operations.push({
82
- method: methodKey,
83
- path,
84
- tags: pathItem[methodKey]?.tags
85
- });
86
- }
87
- }
88
- for (const [name, pathItem] of Object.entries(document.webhooks ?? {})) {
89
- if (!pathItem) continue;
90
- for (const methodKey of methodKeys) {
91
- if (!pathItem[methodKey]) continue;
92
- result.webhooks.push({
93
- method: methodKey,
94
- name,
95
- tags: pathItem[methodKey]?.tags
96
- });
97
- }
98
- }
99
- return result;
100
- }
101
100
  //#endregion
102
101
  export { fromSchema, fromServer };
103
102
 
@@ -1 +1 @@
1
- {"version":3,"file":"builder.js","names":[],"sources":["../../../src/utils/pages/builder.ts"],"sourcesContent":["import type { ProcessedDocument } from '@/utils/process-document';\nimport type { OpenAPIServer } from '@/server';\nimport type { OperationItem, WebhookItem } from '@/ui/api-page';\nimport type { Document, OperationObject, PathItemObject, TagObject } from '@/types';\nimport { getTagDisplayName, methodKeys, type NoReference } from '@/utils/schema';\nimport { idToTitle } from '@/utils/id-to-title';\n\ninterface BaseEntry {\n path: string;\n schemaId: string;\n info: {\n title: string;\n description?: string;\n };\n}\n\nexport interface OperationOutput extends BaseEntry {\n type: 'operation';\n item: OperationItem;\n}\n\nexport interface WebhookOutput extends BaseEntry {\n type: 'webhook';\n item: WebhookItem;\n}\n\nexport interface TagOutput extends BaseEntry {\n type: 'tag';\n tag: string;\n rawTag: TagObject;\n operations: OperationItem[];\n webhooks: WebhookItem[];\n}\n\nexport interface OutputGroup extends BaseEntry {\n type: 'group';\n operations: OperationItem[];\n webhooks: WebhookItem[];\n}\n\nexport type OutputEntry = TagOutput | OperationOutput | WebhookOutput | OutputGroup;\n\nexport interface PagesBuilderConfig {\n toPages: (builder: PagesBuilder) => void;\n}\n\nexport interface PagesBuilder {\n /**\n * the input ID in OpenAPI server\n */\n id: string;\n document: ProcessedDocument;\n /**\n * add output entry.\n *\n * When the `path` property is unspecified, it will generate one.\n */\n create: (entry: OutputEntry) => void;\n\n /**\n * get file path from operation path, useful for generating output paths.\n */\n routePathToFilePath: (path: string) => string;\n\n /**\n * Extract useful info for rendering\n */\n extract: () => ExtractedInfo;\n fromExtractedWebhook: (item: WebhookItem) =>\n | {\n get displayName(): string;\n pathItem: NoReference<PathItemObject>;\n operation: NoReference<OperationObject>;\n }\n | undefined;\n fromExtractedOperation: (item: OperationItem) =>\n | {\n get displayName(): string;\n pathItem: NoReference<PathItemObject>;\n operation: NoReference<OperationObject>;\n }\n | undefined;\n fromTag: (tag: TagObject) => {\n get displayName(): string;\n };\n fromTagName: (tag: string) =>\n | {\n info: TagObject;\n get displayName(): string;\n }\n | undefined;\n}\n\ninterface ExtractedInfo {\n webhooks: (WebhookItem & { tags?: string[] })[];\n operations: (OperationItem & {\n tags?: string[];\n })[];\n}\n\nexport async function fromServer(\n server: OpenAPIServer,\n config: PagesBuilderConfig,\n): Promise<Record<string, OutputEntry[]>> {\n const schemas = await server.getSchemas();\n const generated: Record<string, OutputEntry[]> = {};\n\n const entries = Object.entries(schemas);\n if (entries.length === 0) {\n throw new Error('No input files found.');\n }\n\n for (const [id, schema] of entries) {\n generated[id] = fromSchema(id, schema, config);\n }\n\n return generated;\n}\n\nexport function fromSchema(\n schemaId: string,\n processed: ProcessedDocument,\n config: PagesBuilderConfig,\n): OutputEntry[] {\n const files: OutputEntry[] = [];\n const { toPages } = config;\n const { dereferenced } = processed;\n\n toPages({\n id: schemaId,\n document: processed,\n create(entry) {\n files.push(entry);\n },\n extract: () => extractInfo(dereferenced),\n routePathToFilePath(path) {\n return path\n .toLowerCase()\n .replaceAll('.', '-')\n .split('/')\n .flatMap((v) => {\n if (v.startsWith('{') && v.endsWith('}')) return v.slice(1, -1);\n if (v.length === 0) return [];\n return v;\n })\n .join('/');\n },\n fromExtractedWebhook(item) {\n const pathItem = dereferenced.webhooks?.[item.name];\n if (!pathItem) return;\n const operation = pathItem?.[item.method];\n if (!operation) return;\n return {\n pathItem,\n operation,\n get displayName() {\n return operation.summary || pathItem.summary || idToTitle(item.name);\n },\n };\n },\n fromExtractedOperation(item) {\n const pathItem = dereferenced.paths?.[item.path];\n if (!pathItem) return;\n const operation = pathItem?.[item.method];\n if (!operation) return;\n return {\n pathItem,\n operation,\n get displayName() {\n return (\n operation.summary ||\n pathItem.summary ||\n (operation.operationId ? idToTitle(operation.operationId) : item.path)\n );\n },\n };\n },\n fromTag(tag) {\n return {\n get displayName() {\n return getTagDisplayName(tag);\n },\n };\n },\n fromTagName(name) {\n const tag = dereferenced.tags?.find((item) => item.name === name);\n if (!tag) return;\n\n return {\n info: tag,\n ...this.fromTag(tag),\n };\n },\n });\n\n return files;\n}\n\nfunction extractInfo(document: NoReference<Document>): ExtractedInfo {\n const result: ExtractedInfo = { webhooks: [], operations: [] };\n\n for (const [path, pathItem] of Object.entries(document.paths ?? {})) {\n if (!pathItem) continue;\n\n for (const methodKey of methodKeys) {\n if (!pathItem[methodKey]) continue;\n\n result.operations.push({\n method: methodKey,\n path,\n tags: pathItem[methodKey]?.tags,\n });\n }\n }\n\n for (const [name, pathItem] of Object.entries(document.webhooks ?? {})) {\n if (!pathItem) continue;\n\n for (const methodKey of methodKeys) {\n if (!pathItem[methodKey]) continue;\n\n result.webhooks.push({\n method: methodKey,\n name,\n tags: pathItem[methodKey]?.tags,\n });\n }\n }\n\n return result;\n}\n"],"mappings":";;;AAoGA,eAAsB,WACpB,QACA,QACwC;CACxC,MAAM,UAAU,MAAM,OAAO,YAAY;CACzC,MAAM,YAA2C,EAAE;CAEnD,MAAM,UAAU,OAAO,QAAQ,QAAQ;AACvC,KAAI,QAAQ,WAAW,EACrB,OAAM,IAAI,MAAM,wBAAwB;AAG1C,MAAK,MAAM,CAAC,IAAI,WAAW,QACzB,WAAU,MAAM,WAAW,IAAI,QAAQ,OAAO;AAGhD,QAAO;;AAGT,SAAgB,WACd,UACA,WACA,QACe;CACf,MAAM,QAAuB,EAAE;CAC/B,MAAM,EAAE,YAAY;CACpB,MAAM,EAAE,iBAAiB;AAEzB,SAAQ;EACN,IAAI;EACJ,UAAU;EACV,OAAO,OAAO;AACZ,SAAM,KAAK,MAAM;;EAEnB,eAAe,YAAY,aAAa;EACxC,oBAAoB,MAAM;AACxB,UAAO,KACJ,aAAa,CACb,WAAW,KAAK,IAAI,CACpB,MAAM,IAAI,CACV,SAAS,MAAM;AACd,QAAI,EAAE,WAAW,IAAI,IAAI,EAAE,SAAS,IAAI,CAAE,QAAO,EAAE,MAAM,GAAG,GAAG;AAC/D,QAAI,EAAE,WAAW,EAAG,QAAO,EAAE;AAC7B,WAAO;KACP,CACD,KAAK,IAAI;;EAEd,qBAAqB,MAAM;GACzB,MAAM,WAAW,aAAa,WAAW,KAAK;AAC9C,OAAI,CAAC,SAAU;GACf,MAAM,YAAY,WAAW,KAAK;AAClC,OAAI,CAAC,UAAW;AAChB,UAAO;IACL;IACA;IACA,IAAI,cAAc;AAChB,YAAO,UAAU,WAAW,SAAS,WAAW,UAAU,KAAK,KAAK;;IAEvE;;EAEH,uBAAuB,MAAM;GAC3B,MAAM,WAAW,aAAa,QAAQ,KAAK;AAC3C,OAAI,CAAC,SAAU;GACf,MAAM,YAAY,WAAW,KAAK;AAClC,OAAI,CAAC,UAAW;AAChB,UAAO;IACL;IACA;IACA,IAAI,cAAc;AAChB,YACE,UAAU,WACV,SAAS,YACR,UAAU,cAAc,UAAU,UAAU,YAAY,GAAG,KAAK;;IAGtE;;EAEH,QAAQ,KAAK;AACX,UAAO,EACL,IAAI,cAAc;AAChB,WAAO,kBAAkB,IAAI;MAEhC;;EAEH,YAAY,MAAM;GAChB,MAAM,MAAM,aAAa,MAAM,MAAM,SAAS,KAAK,SAAS,KAAK;AACjE,OAAI,CAAC,IAAK;AAEV,UAAO;IACL,MAAM;IACN,GAAG,KAAK,QAAQ,IAAI;IACrB;;EAEJ,CAAC;AAEF,QAAO;;AAGT,SAAS,YAAY,UAAgD;CACnE,MAAM,SAAwB;EAAE,UAAU,EAAE;EAAE,YAAY,EAAE;EAAE;AAE9D,MAAK,MAAM,CAAC,MAAM,aAAa,OAAO,QAAQ,SAAS,SAAS,EAAE,CAAC,EAAE;AACnE,MAAI,CAAC,SAAU;AAEf,OAAK,MAAM,aAAa,YAAY;AAClC,OAAI,CAAC,SAAS,WAAY;AAE1B,UAAO,WAAW,KAAK;IACrB,QAAQ;IACR;IACA,MAAM,SAAS,YAAY;IAC5B,CAAC;;;AAIN,MAAK,MAAM,CAAC,MAAM,aAAa,OAAO,QAAQ,SAAS,YAAY,EAAE,CAAC,EAAE;AACtE,MAAI,CAAC,SAAU;AAEf,OAAK,MAAM,aAAa,YAAY;AAClC,OAAI,CAAC,SAAS,WAAY;AAE1B,UAAO,SAAS,KAAK;IACnB,QAAQ;IACR;IACA,MAAM,SAAS,YAAY;IAC5B,CAAC;;;AAIN,QAAO"}
1
+ {"version":3,"file":"builder.js","names":[],"sources":["../../../src/utils/pages/builder.ts"],"sourcesContent":["import type { ProcessedDocument } from '@/utils/process-document';\nimport type { OpenAPIServer } from '@/server';\nimport type { OperationItem, WebhookItem } from '@/ui/api-page';\nimport type { OperationObject, PathItemObject, TagObject } from '@/types';\nimport { getTagDisplayName, methodKeys, type NoReference } from '@/utils/schema';\nimport { idToTitle } from '@/utils/id-to-title';\n\ninterface BaseEntry {\n path: string;\n schemaId: string;\n info: {\n title: string;\n description?: string;\n };\n}\n\nexport interface OperationOutput extends BaseEntry {\n type: 'operation';\n item: OperationItem;\n}\n\nexport interface WebhookOutput extends BaseEntry {\n type: 'webhook';\n item: WebhookItem;\n}\n\nexport interface PageOutput extends BaseEntry {\n type: 'page';\n operations: OperationItem[];\n webhooks: WebhookItem[];\n /** tag info if the page is generated from a tag. */\n tag?: TagObject;\n}\n\nexport interface OutputGroup extends BaseEntry {\n type: 'group';\n entries: OutputEntry[];\n /** tag info if the group is generated from a tag. */\n tag?: TagObject;\n}\n\nexport type OutputEntry = PageOutput | OperationOutput | WebhookOutput | OutputGroup;\n\nexport interface PagesBuilderConfig {\n toPages: (builder: PagesBuilder) => void;\n}\n\nexport interface PagesBuilder {\n /**\n * the input ID in OpenAPI server\n */\n id: string;\n document: ProcessedDocument;\n /**\n * add output entry.\n */\n create: (entry: OutputEntry) => void;\n\n /**\n * get file path from operation path, useful for generating output paths.\n */\n routePathToFilePath: (path: string) => string;\n\n /**\n * Extract useful info for rendering\n */\n extract: () => ExtractedInfo;\n fromExtractedWebhook: (item: WebhookItem) =>\n | {\n get displayName(): string;\n pathItem: NoReference<PathItemObject>;\n operation: NoReference<OperationObject>;\n }\n | undefined;\n fromExtractedOperation: (item: OperationItem) =>\n | {\n get displayName(): string;\n pathItem: NoReference<PathItemObject>;\n operation: NoReference<OperationObject>;\n }\n | undefined;\n fromTag: (tag: TagObject) => {\n get displayName(): string;\n };\n fromTagName: (tag: string) =>\n | {\n info: TagObject;\n get displayName(): string;\n }\n | undefined;\n}\n\ninterface ExtractedInfo {\n webhooks: (WebhookItem & { tags?: string[] })[];\n operations: (OperationItem & {\n tags?: string[];\n })[];\n}\n\nexport async function fromServer(\n server: OpenAPIServer,\n config: PagesBuilderConfig,\n): Promise<Record<string, OutputEntry[]>> {\n const schemas = await server.getSchemas();\n const generated: Record<string, OutputEntry[]> = {};\n\n const entries = Object.entries(schemas);\n if (entries.length === 0) {\n throw new Error('No input files found.');\n }\n\n for (const [id, schema] of entries) {\n generated[id] = fromSchema(id, schema, config);\n }\n\n return generated;\n}\n\nexport function fromSchema(\n schemaId: string,\n processed: ProcessedDocument,\n config: PagesBuilderConfig,\n): OutputEntry[] {\n const files: OutputEntry[] = [];\n const { toPages } = config;\n const { dereferenced } = processed;\n\n toPages({\n id: schemaId,\n document: processed,\n create(entry) {\n files.push(entry);\n },\n extract() {\n const result: ExtractedInfo = { webhooks: [], operations: [] };\n\n for (const [path, pathItem] of Object.entries(dereferenced.paths ?? {})) {\n if (!pathItem) continue;\n\n for (const methodKey of methodKeys) {\n if (!pathItem[methodKey]) continue;\n\n result.operations.push({\n method: methodKey,\n path,\n tags: pathItem[methodKey]?.tags,\n });\n }\n }\n\n for (const [name, pathItem] of Object.entries(dereferenced.webhooks ?? {})) {\n if (!pathItem) continue;\n\n for (const methodKey of methodKeys) {\n if (!pathItem[methodKey]) continue;\n\n result.webhooks.push({\n method: methodKey,\n name,\n tags: pathItem[methodKey]?.tags,\n });\n }\n }\n\n return result;\n },\n routePathToFilePath(path) {\n return path\n .toLowerCase()\n .replaceAll('.', '-')\n .split('/')\n .flatMap((v) => {\n if (v.startsWith('{') && v.endsWith('}')) return v.slice(1, -1);\n if (v.length === 0) return [];\n return v;\n })\n .join('/');\n },\n fromExtractedWebhook(item) {\n const pathItem = dereferenced.webhooks?.[item.name];\n if (!pathItem) return;\n const operation = pathItem?.[item.method];\n if (!operation) return;\n return {\n pathItem,\n operation,\n get displayName() {\n return operation.summary || pathItem.summary || idToTitle(item.name);\n },\n };\n },\n fromExtractedOperation(item) {\n const pathItem = dereferenced.paths?.[item.path];\n if (!pathItem) return;\n const operation = pathItem?.[item.method];\n if (!operation) return;\n return {\n pathItem,\n operation,\n get displayName() {\n return (\n operation.summary ||\n pathItem.summary ||\n (operation.operationId ? idToTitle(operation.operationId) : item.path)\n );\n },\n };\n },\n fromTag(tag) {\n return {\n get displayName() {\n return getTagDisplayName(tag);\n },\n };\n },\n fromTagName(name) {\n const tag = dereferenced.tags?.find((item) => item.name === name);\n if (!tag) return;\n\n return {\n info: tag,\n ...this.fromTag(tag),\n };\n },\n });\n\n return files;\n}\n"],"mappings":";;;AAmGA,eAAsB,WACpB,QACA,QACwC;CACxC,MAAM,UAAU,MAAM,OAAO,YAAY;CACzC,MAAM,YAA2C,EAAE;CAEnD,MAAM,UAAU,OAAO,QAAQ,QAAQ;AACvC,KAAI,QAAQ,WAAW,EACrB,OAAM,IAAI,MAAM,wBAAwB;AAG1C,MAAK,MAAM,CAAC,IAAI,WAAW,QACzB,WAAU,MAAM,WAAW,IAAI,QAAQ,OAAO;AAGhD,QAAO;;AAGT,SAAgB,WACd,UACA,WACA,QACe;CACf,MAAM,QAAuB,EAAE;CAC/B,MAAM,EAAE,YAAY;CACpB,MAAM,EAAE,iBAAiB;AAEzB,SAAQ;EACN,IAAI;EACJ,UAAU;EACV,OAAO,OAAO;AACZ,SAAM,KAAK,MAAM;;EAEnB,UAAU;GACR,MAAM,SAAwB;IAAE,UAAU,EAAE;IAAE,YAAY,EAAE;IAAE;AAE9D,QAAK,MAAM,CAAC,MAAM,aAAa,OAAO,QAAQ,aAAa,SAAS,EAAE,CAAC,EAAE;AACvE,QAAI,CAAC,SAAU;AAEf,SAAK,MAAM,aAAa,YAAY;AAClC,SAAI,CAAC,SAAS,WAAY;AAE1B,YAAO,WAAW,KAAK;MACrB,QAAQ;MACR;MACA,MAAM,SAAS,YAAY;MAC5B,CAAC;;;AAIN,QAAK,MAAM,CAAC,MAAM,aAAa,OAAO,QAAQ,aAAa,YAAY,EAAE,CAAC,EAAE;AAC1E,QAAI,CAAC,SAAU;AAEf,SAAK,MAAM,aAAa,YAAY;AAClC,SAAI,CAAC,SAAS,WAAY;AAE1B,YAAO,SAAS,KAAK;MACnB,QAAQ;MACR;MACA,MAAM,SAAS,YAAY;MAC5B,CAAC;;;AAIN,UAAO;;EAET,oBAAoB,MAAM;AACxB,UAAO,KACJ,aAAa,CACb,WAAW,KAAK,IAAI,CACpB,MAAM,IAAI,CACV,SAAS,MAAM;AACd,QAAI,EAAE,WAAW,IAAI,IAAI,EAAE,SAAS,IAAI,CAAE,QAAO,EAAE,MAAM,GAAG,GAAG;AAC/D,QAAI,EAAE,WAAW,EAAG,QAAO,EAAE;AAC7B,WAAO;KACP,CACD,KAAK,IAAI;;EAEd,qBAAqB,MAAM;GACzB,MAAM,WAAW,aAAa,WAAW,KAAK;AAC9C,OAAI,CAAC,SAAU;GACf,MAAM,YAAY,WAAW,KAAK;AAClC,OAAI,CAAC,UAAW;AAChB,UAAO;IACL;IACA;IACA,IAAI,cAAc;AAChB,YAAO,UAAU,WAAW,SAAS,WAAW,UAAU,KAAK,KAAK;;IAEvE;;EAEH,uBAAuB,MAAM;GAC3B,MAAM,WAAW,aAAa,QAAQ,KAAK;AAC3C,OAAI,CAAC,SAAU;GACf,MAAM,YAAY,WAAW,KAAK;AAClC,OAAI,CAAC,UAAW;AAChB,UAAO;IACL;IACA;IACA,IAAI,cAAc;AAChB,YACE,UAAU,WACV,SAAS,YACR,UAAU,cAAc,UAAU,UAAU,YAAY,GAAG,KAAK;;IAGtE;;EAEH,QAAQ,KAAK;AACX,UAAO,EACL,IAAI,cAAc;AAChB,WAAO,kBAAkB,IAAI;MAEhC;;EAEH,YAAY,MAAM;GAChB,MAAM,MAAM,aAAa,MAAM,MAAM,SAAS,KAAK,SAAS,KAAK;AACjE,OAAI,CAAC,IAAK;AAEV,UAAO;IACL,MAAM;IACN,GAAG,KAAK,QAAQ,IAAI;IACrB;;EAEJ,CAAC;AAEF,QAAO"}
@@ -1,4 +1,5 @@
1
- import { OperationOutput, OutputGroup, PagesBuilder, PagesBuilderConfig, TagOutput, WebhookOutput } from "./builder.js";
1
+ import { OperationOutput, PageOutput, PagesBuilder, PagesBuilderConfig, WebhookOutput } from "./builder.js";
2
+ import { DistributiveOmit } from "../../types.js";
2
3
  import { ProcessedDocument } from "../process-document.js";
3
4
 
4
5
  //#region src/utils/pages/preset-auto.d.ts
@@ -16,7 +17,7 @@ interface OperationConfig extends BaseConfig {
16
17
  *
17
18
  * @defaultValue 'none'
18
19
  */
19
- groupBy?: 'tag' | 'route' | 'none' | ((entry: OperationOutput | WebhookOutput) => string);
20
+ groupBy?: 'tag' | 'route' | 'none' | ((entry: DistributiveOmit<OperationOutput | WebhookOutput, 'path'>) => string);
20
21
  /**
21
22
  * Specify name for output file
22
23
  */
@@ -30,7 +31,7 @@ interface TagConfig extends BaseConfig {
30
31
  /**
31
32
  * Specify name for output file
32
33
  */
33
- name?: NameFn<TagOutput> | NameFnOptions;
34
+ name?: NameFn<PageOutput> | NameFnOptions;
34
35
  }
35
36
  interface SchemaConfig extends BaseConfig {
36
37
  /**
@@ -40,12 +41,12 @@ interface SchemaConfig extends BaseConfig {
40
41
  /**
41
42
  * Specify name for output file
42
43
  */
43
- name?: NameFn<OutputGroup> | NameFnOptions;
44
+ name?: NameFn<PageOutput> | NameFnOptions;
44
45
  }
45
46
  type SchemaToPagesOptions = SchemaConfig | TagConfig | OperationConfig | ({
46
47
  per: 'custom';
47
48
  } & PagesBuilderConfig);
48
- type NameFn<Entry> = (this: PagesBuilder, output: Entry, document: ProcessedDocument['dereferenced']) => string;
49
+ type NameFn<Entry extends OperationOutput | WebhookOutput | PageOutput = OperationOutput | WebhookOutput | PageOutput> = (this: PagesBuilder, output: DistributiveOmit<Entry, 'path'>, document: ProcessedDocument['dereferenced']) => string;
49
50
  interface NameFnOptions {
50
51
  /**
51
52
  * The version of algorithm used to generate file paths.
@@ -1 +1 @@
1
- {"version":3,"file":"preset-auto.d.ts","names":[],"sources":["../../../src/utils/pages/preset-auto.ts"],"mappings":";;;;UAaU,eAAA,SAAwB,UAAA;;AAHH;;EAO7B,GAAA;EAW8C;;;;;;;;;EAA9C,OAAA,gCAAuC,KAAA,EAAO,eAAA,GAAkB,aAAA;EAXhE;;;EAgBA,IAAA,GAAO,MAAA,CAAO,eAAA,GAAkB,aAAA,IAAiB,aAAA;AAAA;AAAA,UAGzC,SAAA,SAAkB,UAAA;EAHnB;;;EAOP,GAAA;EAP8D;;AAAA;EAY9D,IAAA,GAAO,MAAA,CAAO,SAAA,IAAa,aAAA;AAAA;AAAA,UAGnB,YAAA,SAAqB,UAAA;EAHtB;;;EAOP,GAAA;EAhBoC;;;EAqBpC,IAAA,GAAO,MAAA,CAAO,WAAA,IAAe,aAAA;AAAA;AAAA,KAGnB,oBAAA,GACR,YAAA,GACA,SAAA,GACA,eAAA;EAEE,GAAA;AAAA,IACE,kBAAA;AAAA,KAEH,MAAA,WACH,IAAA,EAAM,YAAA,EACN,MAAA,EAAQ,KAAA,EACR,QAAA,EAAU,iBAAA;AAAA,UAGF,aAAA;EA1Ba;;;;;;;;EAmCrB,SAAA;AAAA;AAAA,UAGQ,UAAA;EA7BD;;;;;EAmCP,OAAA,IAAW,IAAA;AAAA;AAAA,iBAGG,gBAAA,CAAiB,OAAA,EAAS,oBAAA,GAAuB,kBAAA"}
1
+ {"version":3,"file":"preset-auto.d.ts","names":[],"sources":["../../../src/utils/pages/preset-auto.ts"],"mappings":";;;;;UAcU,eAAA,SAAwB,UAAA;;AAFc;;EAM9C,GAAA;EAe8B;;;;;;;;;EAJ9B,OAAA,gCAIM,KAAA,EAAO,gBAAA,CAAiB,eAAA,GAAkB,aAAA;EAnBhB;;;EAwBhC,IAAA,GAAO,MAAA,CAAO,eAAA,GAAkB,aAAA,IAAiB,aAAA;AAAA;AAAA,UAGzC,SAAA,SAAkB,UAAA;EARpB;;;EAYN,GAAA;EAPgC;;;EAYhC,IAAA,GAAO,MAAA,CAAO,UAAA,IAAc,aAAA;AAAA;AAAA,UAGpB,YAAA,SAAqB,UAAA;;;;EAI7B,GAAA;EAhB0B;;;EAqB1B,IAAA,GAAO,MAAA,CAAO,UAAA,IAAc,aAAA;AAAA;AAAA,KAGlB,oBAAA,GACR,YAAA,GACA,SAAA,GACA,eAAA;EAEE,GAAA;AAAA,IACE,kBAAA;AAAA,KAEH,MAAA,eACW,eAAA,GAAkB,aAAA,GAAgB,UAAA,GAC5C,eAAA,GACA,aAAA,GACA,UAAA,KAEJ,IAAA,EAAM,YAAA,EACN,MAAA,EAAQ,gBAAA,CAAiB,KAAA,WACzB,QAAA,EAAU,iBAAA;AAAA,UAGF,aAAA;EAlCiC;AAAA;;;;;;;EA2CzC,SAAA;AAAA;AAAA,UAGQ,UAAA;EAvCR;;;;;EA6CA,OAAA,IAAW,IAAA;AAAA;AAAA,iBAGG,gBAAA,CAAiB,OAAA,EAAS,oBAAA,GAAuB,kBAAA"}
@@ -1,5 +1,5 @@
1
1
  import { isUrl } from "../url.js";
2
- import * as path from "node:path";
2
+ import * as path$1 from "node:path";
3
3
  //#region src/utils/pages/preset-auto.ts
4
4
  function createAutoPreset(options) {
5
5
  if (options.per === "custom") return options;
@@ -11,43 +11,114 @@ function createAutoPreset(options) {
11
11
  else {
12
12
  const { algorithm = "v2" } = options.name ?? {};
13
13
  nameFn = function(result, document) {
14
- if (result.type === "tag") return slugify(result.tag);
15
- if (result.type === "group") {
14
+ if (result.type === "page") {
15
+ if (result.tag) return slugify(result.tag.name);
16
16
  const schemaId = result.schemaId;
17
- return isUrl(schemaId) ? "index" : path.basename(schemaId, path.extname(schemaId));
17
+ return isUrl(schemaId) ? "index" : path$1.basename(schemaId, path$1.extname(schemaId));
18
18
  }
19
19
  if (result.type === "operation") {
20
20
  const operation = document.paths[result.item.path][result.item.method];
21
21
  if (algorithm === "v2" && operation.operationId) return operation.operationId;
22
- return path.join(this.routePathToFilePath(result.item.path), result.item.method.toLowerCase());
22
+ return path$1.join(this.routePathToFilePath(result.item.path), result.item.method.toLowerCase());
23
23
  }
24
24
  const hook = 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
  };
28
28
  }
29
- function groupOutput(builder, entry) {
29
+ function group(builder, entries) {
30
+ const groups = /* @__PURE__ */ new Map();
31
+ const rest = [];
30
32
  const { dereferenced } = builder.document;
31
33
  const { groupBy = "none" } = options;
32
- if (groupBy === "route") return [path.join(builder.routePathToFilePath(entry.type === "operation" ? entry.item.path : entry.item.name), `${entry.item.method.toLowerCase()}.mdx`)];
33
- const file = nameFn.call(builder, entry, dereferenced);
34
- if (groupBy === "tag") {
35
- let tags = entry.type === "operation" ? dereferenced.paths[entry.item.path][entry.item.method].tags : dereferenced.webhooks[entry.item.name][entry.item.method].tags;
36
- if (!tags || tags.length === 0) {
37
- console.warn("When `groupBy` is set to `tag`, make sure a `tags` is defined for every operation schema.");
38
- tags = ["unknown"];
34
+ for (const entry of entries) switch (groupBy) {
35
+ case "route": {
36
+ const groupName = builder.routePathToFilePath(entry.type === "operation" ? entry.item.path : entry.item.name);
37
+ let group = groups.get(groupName);
38
+ if (!group) {
39
+ group = {
40
+ type: "group",
41
+ info: { title: groupName },
42
+ entries: [],
43
+ schemaId: builder.id,
44
+ path: groupName
45
+ };
46
+ groups.set(groupName, group);
47
+ }
48
+ group.entries.push({
49
+ ...entry,
50
+ path: path$1.join(groupName, `${entry.item.method.toLowerCase()}.mdx`)
51
+ });
52
+ break;
53
+ }
54
+ case "tag": {
55
+ let tags = entry.type === "operation" ? dereferenced.paths[entry.item.path][entry.item.method].tags : dereferenced.webhooks[entry.item.name][entry.item.method].tags;
56
+ if (!tags || tags.length === 0) {
57
+ console.warn("When `groupBy` is set to `tag`, make sure a `tags` is defined for every operation schema.");
58
+ tags = ["unknown"];
59
+ }
60
+ for (const tag of tags) {
61
+ const groupName = slugify(tag);
62
+ const { displayName, info } = builder.fromTagName(tag);
63
+ let group = groups.get(groupName);
64
+ if (!group) {
65
+ group = {
66
+ type: "group",
67
+ info: {
68
+ title: displayName,
69
+ description: info.description
70
+ },
71
+ tag: info,
72
+ entries: [],
73
+ schemaId: builder.id,
74
+ path: groupName
75
+ };
76
+ groups.set(groupName, group);
77
+ }
78
+ group.entries.push({
79
+ ...entry,
80
+ path: path$1.join(groupName, `${nameFn.call(builder, entry, dereferenced)}.mdx`)
81
+ });
82
+ }
83
+ break;
84
+ }
85
+ default: {
86
+ const fileName = `${nameFn.call(builder, entry, dereferenced)}.mdx`;
87
+ if (typeof groupBy === "function") {
88
+ const groupDisplayName = groupBy(entry);
89
+ const groupName = slugify(groupDisplayName);
90
+ let group = groups.get(groupName);
91
+ if (!group) {
92
+ group = {
93
+ type: "group",
94
+ info: { title: groupDisplayName },
95
+ entries: [],
96
+ schemaId: builder.id,
97
+ path: groupName
98
+ };
99
+ groups.set(groupName, group);
100
+ }
101
+ group.entries.push({
102
+ ...entry,
103
+ path: path$1.join(groupName, fileName)
104
+ });
105
+ break;
106
+ }
107
+ rest.push({
108
+ ...entry,
109
+ path: fileName
110
+ });
39
111
  }
40
- return tags.map((tag) => path.join(slugify(tag), `${file}.mdx`));
41
112
  }
42
- if (typeof groupBy === "function") return [path.join(slugify(groupBy(entry)), `${file}.mdx`)];
43
- return [`${file}.mdx`];
113
+ rest.push(...groups.values());
114
+ return rest;
44
115
  }
45
116
  return { toPages(builder) {
46
117
  const { dereferenced } = builder.document;
47
118
  const items = builder.extract();
48
119
  if (options.per === "file") {
49
120
  const entry = {
50
- type: "group",
121
+ type: "page",
51
122
  schemaId: builder.id,
52
123
  path: "",
53
124
  info: {
@@ -56,7 +127,7 @@ function createAutoPreset(options) {
56
127
  },
57
128
  ...items
58
129
  };
59
- entry.path = nameFn.call(builder, entry, dereferenced) + ".mdx";
130
+ entry.path = `${nameFn.call(builder, entry, dereferenced)}.mdx`;
60
131
  builder.create(entry);
61
132
  return;
62
133
  }
@@ -65,7 +136,7 @@ function createAutoPreset(options) {
65
136
  for (const tag of tags) {
66
137
  const { displayName } = builder.fromTag(tag);
67
138
  const entry = {
68
- type: "tag",
139
+ type: "page",
69
140
  path: "",
70
141
  schemaId: builder.id,
71
142
  info: {
@@ -74,48 +145,39 @@ function createAutoPreset(options) {
74
145
  },
75
146
  webhooks: items.webhooks.filter((webhook) => webhook.tags?.includes(tag.name)),
76
147
  operations: items.operations.filter((op) => op.tags?.includes(tag.name)),
77
- tag: tag.name,
78
- rawTag: tag
148
+ tag
79
149
  };
80
- entry.path = nameFn.call(builder, entry, dereferenced) + ".mdx";
150
+ entry.path = `${nameFn.call(builder, entry, dereferenced)}.mdx`;
81
151
  builder.create(entry);
82
152
  }
83
153
  return;
84
154
  }
155
+ const entries = [];
85
156
  for (const op of items.operations) {
86
157
  const { pathItem, operation, displayName } = builder.fromExtractedOperation(op);
87
- const entry = {
158
+ entries.push({
88
159
  type: "operation",
89
160
  schemaId: builder.id,
90
161
  item: op,
91
- path: "",
92
162
  info: {
93
163
  title: displayName,
94
164
  description: operation.description ?? pathItem.description
95
165
  }
96
- };
97
- for (const outputPath of groupOutput(builder, entry)) builder.create({
98
- ...entry,
99
- path: outputPath
100
166
  });
101
167
  }
102
168
  for (const webhook of items.webhooks) {
103
169
  const { pathItem, operation, displayName } = builder.fromExtractedWebhook(webhook);
104
- const entry = {
170
+ entries.push({
105
171
  type: "webhook",
106
172
  schemaId: builder.id,
107
173
  info: {
108
174
  title: displayName,
109
175
  description: operation.description ?? pathItem.description
110
176
  },
111
- item: webhook,
112
- path: ""
113
- };
114
- for (const outputPath of groupOutput(builder, entry)) builder.create({
115
- ...entry,
116
- path: outputPath
177
+ item: webhook
117
178
  });
118
179
  }
180
+ for (const entry of group(builder, entries)) builder.create(entry);
119
181
  } };
120
182
  }
121
183
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"preset-auto.js","names":[],"sources":["../../../src/utils/pages/preset-auto.ts"],"sourcesContent":["import * as path from 'node:path';\nimport type { ProcessedDocument } from '@/utils/process-document';\nimport type {\n OperationOutput,\n OutputEntry,\n OutputGroup,\n PagesBuilder,\n PagesBuilderConfig,\n TagOutput,\n WebhookOutput,\n} from '@/utils/pages/builder';\nimport { isUrl } from '@/utils/url';\n\ninterface OperationConfig extends BaseConfig {\n /**\n * Generate a page for each API endpoint/operation (default).\n */\n per?: 'operation';\n\n /**\n * Group output using folders (Only works on `operation` mode)\n * - tag: `{tag}/{file}`\n * - route: `{endpoint}/{method}` (it will ignore the `name` option)\n * - none: `{file}` (default)\n * - a function that aligns group name (folder path) to each entry\n *\n * @defaultValue 'none'\n */\n groupBy?: 'tag' | 'route' | 'none' | ((entry: OperationOutput | WebhookOutput) => string);\n\n /**\n * Specify name for output file\n */\n name?: NameFn<OperationOutput | WebhookOutput> | NameFnOptions;\n}\n\ninterface TagConfig extends BaseConfig {\n /**\n * Generate a page for each tag.\n */\n per: 'tag';\n\n /**\n * Specify name for output file\n */\n name?: NameFn<TagOutput> | NameFnOptions;\n}\n\ninterface SchemaConfig extends BaseConfig {\n /**\n * Generate a page for each schema file.\n */\n per: 'file';\n\n /**\n * Specify name for output file\n */\n name?: NameFn<OutputGroup> | NameFnOptions;\n}\n\nexport type SchemaToPagesOptions =\n | SchemaConfig\n | TagConfig\n | OperationConfig\n | ({\n per: 'custom';\n } & PagesBuilderConfig);\n\ntype NameFn<Entry> = (\n this: PagesBuilder,\n output: Entry,\n document: ProcessedDocument['dereferenced'],\n) => string;\n\ninterface NameFnOptions {\n /**\n * The version of algorithm used to generate file paths.\n *\n * v1: Fumadocs OpenAPI v8\n * v2: Fumadocs OpenAPI v9\n *\n * @defaultValue v2\n */\n algorithm?: 'v2' | 'v1';\n}\n\ninterface BaseConfig {\n /**\n * Custom function to convert names into file names.\n *\n * By default, it only escapes whitespaces and upper case (English) characters\n */\n slugify?: (name: string) => string;\n}\n\nexport function createAutoPreset(options: SchemaToPagesOptions): PagesBuilderConfig {\n if (options.per === 'custom') return options;\n const {\n slugify = (s) => {\n return s.replace(/\\s+/g, '-').toLowerCase();\n },\n } = options;\n let nameFn: NameFn<OutputEntry>;\n\n if (typeof options.name === 'function') {\n nameFn = options.name as NameFn<OutputEntry>;\n } else {\n const { algorithm = 'v2' } = options.name ?? {};\n\n nameFn = function (result, document) {\n if (result.type === 'tag') {\n return slugify(result.tag);\n }\n\n if (result.type === 'group') {\n const schemaId = result.schemaId;\n\n return isUrl(schemaId) ? 'index' : path.basename(schemaId, path.extname(schemaId));\n }\n\n if (result.type === 'operation') {\n const operation = document.paths![result.item.path]![result.item.method]!;\n\n if (algorithm === 'v2' && operation.operationId) {\n return operation.operationId;\n }\n\n return path.join(\n this.routePathToFilePath(result.item.path),\n result.item.method.toLowerCase(),\n );\n }\n\n const hook = document.webhooks![result.item.name][result.item.method]!;\n\n if (algorithm === 'v2' && hook.operationId) {\n return hook.operationId;\n }\n\n return slugify(result.item.name);\n };\n }\n\n function groupOutput(builder: PagesBuilder, entry: OperationOutput | WebhookOutput): string[] {\n const { dereferenced } = builder.document;\n const { groupBy = 'none' } = options as OperationConfig;\n\n if (groupBy === 'route') {\n return [\n path.join(\n builder.routePathToFilePath(\n entry.type === 'operation' ? entry.item.path : entry.item.name,\n ),\n `${entry.item.method.toLowerCase()}.mdx`,\n ),\n ];\n }\n\n const file = nameFn.call(builder, entry, dereferenced);\n if (groupBy === 'tag') {\n let tags =\n entry.type === 'operation'\n ? dereferenced.paths![entry.item.path]![entry.item.method]!.tags\n : dereferenced.webhooks![entry.item.name][entry.item.method]!.tags;\n\n if (!tags || tags.length === 0) {\n console.warn(\n 'When `groupBy` is set to `tag`, make sure a `tags` is defined for every operation schema.',\n );\n\n tags = ['unknown'];\n }\n\n return tags.map((tag) => path.join(slugify(tag), `${file}.mdx`));\n }\n\n if (typeof groupBy === 'function') {\n return [path.join(slugify(groupBy(entry)), `${file}.mdx`)];\n }\n\n return [`${file}.mdx`];\n }\n\n return {\n toPages(builder) {\n const { dereferenced } = builder.document;\n const items = builder.extract();\n\n if (options.per === 'file') {\n const entry: OutputGroup = {\n type: 'group',\n schemaId: builder.id,\n path: '',\n info: {\n title: dereferenced.info?.title ?? 'Unknown',\n description: dereferenced.info?.description,\n },\n ...items,\n };\n entry.path = nameFn.call(builder, entry, dereferenced) + '.mdx';\n builder.create(entry);\n return;\n }\n\n if (options.per === 'tag') {\n const tags = dereferenced.tags ?? [];\n for (const tag of tags) {\n const { displayName } = builder.fromTag(tag);\n const entry: TagOutput = {\n type: 'tag',\n path: '',\n schemaId: builder.id,\n info: {\n title: displayName,\n description: tag.description,\n },\n webhooks: items.webhooks.filter((webhook) => webhook.tags?.includes(tag.name!)),\n operations: items.operations.filter((op) => op.tags?.includes(tag.name!)),\n tag: tag.name!,\n rawTag: tag,\n };\n\n entry.path = nameFn.call(builder, entry, dereferenced) + '.mdx';\n builder.create(entry);\n }\n\n return;\n }\n\n for (const op of items.operations) {\n const { pathItem, operation, displayName } = builder.fromExtractedOperation(op)!;\n\n const entry: OperationOutput = {\n type: 'operation',\n schemaId: builder.id,\n item: op,\n path: '',\n info: {\n title: displayName,\n description: operation.description ?? pathItem.description,\n },\n };\n\n for (const outputPath of groupOutput(builder, entry)) {\n builder.create({ ...entry, path: outputPath });\n }\n }\n\n for (const webhook of items.webhooks) {\n const { pathItem, operation, displayName } = builder.fromExtractedWebhook(webhook)!;\n\n const entry: WebhookOutput = {\n type: 'webhook',\n schemaId: builder.id,\n info: {\n title: displayName,\n description: operation.description ?? pathItem.description,\n },\n item: webhook,\n path: '',\n };\n\n for (const outputPath of groupOutput(builder, entry)) {\n builder.create({ ...entry, path: outputPath });\n }\n }\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"preset-auto.js","names":["path"],"sources":["../../../src/utils/pages/preset-auto.ts"],"sourcesContent":["import * as path from 'node:path';\nimport type { ProcessedDocument } from '@/utils/process-document';\nimport type {\n OperationOutput,\n OutputEntry,\n OutputGroup,\n PageOutput,\n PagesBuilder,\n PagesBuilderConfig,\n WebhookOutput,\n} from '@/utils/pages/builder';\nimport { isUrl } from '@/utils/url';\nimport type { DistributiveOmit } from '@/types';\n\ninterface OperationConfig extends BaseConfig {\n /**\n * Generate a page for each API endpoint/operation (default).\n */\n per?: 'operation';\n\n /**\n * Group output using folders (Only works on `operation` mode)\n * - tag: `{tag}/{file}`\n * - route: `{endpoint}/{method}` (it will ignore the `name` option)\n * - none: `{file}` (default)\n * - a function that aligns group name (folder path) to each entry\n *\n * @defaultValue 'none'\n */\n groupBy?:\n | 'tag'\n | 'route'\n | 'none'\n | ((entry: DistributiveOmit<OperationOutput | WebhookOutput, 'path'>) => string);\n\n /**\n * Specify name for output file\n */\n name?: NameFn<OperationOutput | WebhookOutput> | NameFnOptions;\n}\n\ninterface TagConfig extends BaseConfig {\n /**\n * Generate a page for each tag.\n */\n per: 'tag';\n\n /**\n * Specify name for output file\n */\n name?: NameFn<PageOutput> | NameFnOptions;\n}\n\ninterface SchemaConfig extends BaseConfig {\n /**\n * Generate a page for each schema file.\n */\n per: 'file';\n\n /**\n * Specify name for output file\n */\n name?: NameFn<PageOutput> | NameFnOptions;\n}\n\nexport type SchemaToPagesOptions =\n | SchemaConfig\n | TagConfig\n | OperationConfig\n | ({\n per: 'custom';\n } & PagesBuilderConfig);\n\ntype NameFn<\n Entry extends OperationOutput | WebhookOutput | PageOutput =\n | OperationOutput\n | WebhookOutput\n | PageOutput,\n> = (\n this: PagesBuilder,\n output: DistributiveOmit<Entry, 'path'>,\n document: ProcessedDocument['dereferenced'],\n) => string;\n\ninterface NameFnOptions {\n /**\n * The version of algorithm used to generate file paths.\n *\n * v1: Fumadocs OpenAPI v8\n * v2: Fumadocs OpenAPI v9\n *\n * @defaultValue v2\n */\n algorithm?: 'v2' | 'v1';\n}\n\ninterface BaseConfig {\n /**\n * Custom function to convert names into file names.\n *\n * By default, it only escapes whitespaces and upper case (English) characters\n */\n slugify?: (name: string) => string;\n}\n\nexport function createAutoPreset(options: SchemaToPagesOptions): PagesBuilderConfig {\n if (options.per === 'custom') return options;\n const {\n slugify = (s) => {\n return s.replace(/\\s+/g, '-').toLowerCase();\n },\n } = options;\n let nameFn: NameFn;\n\n if (typeof options.name === 'function') {\n nameFn = options.name as NameFn;\n } else {\n const { algorithm = 'v2' } = options.name ?? {};\n\n nameFn = function (result, document) {\n if (result.type === 'page') {\n if (result.tag) return slugify(result.tag.name!);\n const schemaId = result.schemaId;\n\n return isUrl(schemaId) ? 'index' : path.basename(schemaId, path.extname(schemaId));\n }\n\n if (result.type === 'operation') {\n const operation = document.paths![result.item.path]![result.item.method]!;\n\n if (algorithm === 'v2' && operation.operationId) {\n return operation.operationId;\n }\n\n return path.join(\n this.routePathToFilePath(result.item.path),\n result.item.method.toLowerCase(),\n );\n }\n\n const hook = document.webhooks![result.item.name][result.item.method]!;\n\n if (algorithm === 'v2' && hook.operationId) {\n return hook.operationId;\n }\n\n return slugify(result.item.name);\n };\n }\n\n function group(\n builder: PagesBuilder,\n entries: DistributiveOmit<OperationOutput | WebhookOutput, 'path'>[],\n ): OutputEntry[] {\n const groups = new Map<string, OutputGroup>();\n const rest: OutputEntry[] = [];\n const { dereferenced } = builder.document;\n const { groupBy = 'none' } = options as OperationConfig;\n\n for (const entry of entries) {\n switch (groupBy) {\n case 'route': {\n const groupName = builder.routePathToFilePath(\n entry.type === 'operation' ? entry.item.path : entry.item.name,\n );\n\n let group = groups.get(groupName);\n if (!group) {\n group = {\n type: 'group',\n info: { title: groupName },\n entries: [],\n schemaId: builder.id,\n path: groupName,\n };\n groups.set(groupName, group);\n }\n\n group.entries.push({\n ...entry,\n path: path.join(groupName, `${entry.item.method.toLowerCase()}.mdx`),\n });\n break;\n }\n case 'tag': {\n let tags =\n entry.type === 'operation'\n ? dereferenced.paths![entry.item.path]![entry.item.method]!.tags\n : dereferenced.webhooks![entry.item.name][entry.item.method]!.tags;\n\n if (!tags || tags.length === 0) {\n console.warn(\n 'When `groupBy` is set to `tag`, make sure a `tags` is defined for every operation schema.',\n );\n\n tags = ['unknown'];\n }\n\n for (const tag of tags) {\n const groupName = slugify(tag);\n const { displayName, info } = builder.fromTagName(tag)!;\n let group = groups.get(groupName);\n if (!group) {\n group = {\n type: 'group',\n info: { title: displayName, description: info.description },\n tag: info,\n entries: [],\n schemaId: builder.id,\n path: groupName,\n };\n groups.set(groupName, group);\n }\n\n group.entries.push({\n ...entry,\n path: path.join(groupName, `${nameFn.call(builder, entry, dereferenced)}.mdx`),\n });\n }\n\n break;\n }\n default: {\n const fileName = `${nameFn.call(builder, entry, dereferenced)}.mdx`;\n\n if (typeof groupBy === 'function') {\n const groupDisplayName = groupBy(entry);\n const groupName = slugify(groupDisplayName);\n\n let group = groups.get(groupName);\n if (!group) {\n group = {\n type: 'group',\n info: { title: groupDisplayName },\n entries: [],\n schemaId: builder.id,\n path: groupName,\n };\n groups.set(groupName, group);\n }\n\n group.entries.push({\n ...entry,\n path: path.join(groupName, fileName),\n });\n break;\n }\n\n rest.push({\n ...entry,\n path: fileName,\n });\n }\n }\n }\n\n rest.push(...groups.values());\n return rest;\n }\n\n return {\n toPages(builder) {\n const { dereferenced } = builder.document;\n const items = builder.extract();\n\n if (options.per === 'file') {\n const entry: PageOutput = {\n type: 'page',\n schemaId: builder.id,\n path: '',\n info: {\n title: dereferenced.info?.title ?? 'Unknown',\n description: dereferenced.info?.description,\n },\n ...items,\n };\n entry.path = `${nameFn.call(builder, entry, dereferenced)}.mdx`;\n builder.create(entry);\n return;\n }\n\n if (options.per === 'tag') {\n const tags = dereferenced.tags ?? [];\n\n for (const tag of tags) {\n const { displayName } = builder.fromTag(tag);\n const entry: PageOutput = {\n type: 'page',\n path: '',\n schemaId: builder.id,\n info: {\n title: displayName,\n description: tag.description,\n },\n webhooks: items.webhooks.filter((webhook) => webhook.tags?.includes(tag.name!)),\n operations: items.operations.filter((op) => op.tags?.includes(tag.name!)),\n tag,\n };\n\n entry.path = `${nameFn.call(builder, entry, dereferenced)}.mdx`;\n builder.create(entry);\n }\n\n return;\n }\n\n const entries: DistributiveOmit<OperationOutput | WebhookOutput, 'path'>[] = [];\n for (const op of items.operations) {\n const { pathItem, operation, displayName } = builder.fromExtractedOperation(op)!;\n\n entries.push({\n type: 'operation',\n schemaId: builder.id,\n item: op,\n info: {\n title: displayName,\n description: operation.description ?? pathItem.description,\n },\n });\n }\n\n for (const webhook of items.webhooks) {\n const { pathItem, operation, displayName } = builder.fromExtractedWebhook(webhook)!;\n\n entries.push({\n type: 'webhook',\n schemaId: builder.id,\n info: {\n title: displayName,\n description: operation.description ?? pathItem.description,\n },\n item: webhook,\n });\n }\n\n for (const entry of group(builder, entries)) {\n builder.create(entry);\n }\n },\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"to-text.d.ts","names":[],"sources":["../../../src/utils/pages/to-text.ts"],"mappings":";;UASiB,kBAAA;;AAAjB;;EAIE,OAAA;IACE,KAAA;IACA,IAAA;EAAA;EAAA;;;;;EAQF,WAAA,IACE,KAAA,UACA,WAAA,sBACA,OAAA,EAAS,eAAA,KACN,MAAA;EAAA;;;;;AAkIP;;EAzHE,kBAAA;EA4HkB;;;;;;;;EAlHlB,mBAAA;AAAA;AAAA,KA+GU,eAAA;EAEN,IAAA;EACA,GAAA,EAAK,SAAA;AAAA;EAGL,IAAA;AAAA;EAGA,IAAA;AAAA"}
1
+ {"version":3,"file":"to-text.d.ts","names":[],"sources":["../../../src/utils/pages/to-text.ts"],"mappings":";;UASiB,kBAAA;;AAAjB;;EAIE,OAAA;IACE,KAAA;IACA,IAAA;EAAA;EAAA;;;;;EAQF,WAAA,IACE,KAAA,UACA,WAAA,sBACA,OAAA,EAAS,eAAA,KACN,MAAA;EAAA;;;;;AAqHP;;EA5GE,kBAAA;EA+GkB;;;;;;;;EArGlB,mBAAA;AAAA;AAAA,KAkGU,eAAA;EAEN,IAAA;EACA,GAAA,EAAK,SAAA;AAAA;EAGL,IAAA;AAAA;EAGA,IAAA;AAAA"}
@@ -8,25 +8,17 @@ function toText(entry, processed, options = {}) {
8
8
  ...options,
9
9
  ...entry.info
10
10
  }, { type: "operation" });
11
- case "group": return generatePage(entry.schemaId, processed, {
11
+ case "page": return generatePage(entry.schemaId, processed, {
12
12
  operations: entry.operations,
13
13
  webhooks: entry.webhooks,
14
14
  showTitle: true
15
15
  }, {
16
16
  ...options,
17
17
  ...entry.info
18
- }, { type: "file" });
19
- case "tag": return generatePage(entry.schemaId, processed, {
20
- operations: entry.operations,
21
- webhooks: entry.webhooks,
22
- showTitle: true
23
- }, {
24
- ...options,
25
- ...entry.info
26
- }, {
18
+ }, entry.tag ? {
27
19
  type: "tag",
28
- tag: entry.rawTag
29
- });
20
+ tag: entry.tag
21
+ } : { type: "file" });
30
22
  case "webhook": return generatePage(entry.schemaId, processed, { webhooks: [entry.item] }, {
31
23
  ...options,
32
24
  ...entry.info
@@ -1 +1 @@
1
- {"version":3,"file":"to-text.js","names":[],"sources":["../../../src/utils/pages/to-text.ts"],"sourcesContent":["import type { ApiPageProps, OperationItem, WebhookItem } from '@/ui/api-page';\nimport type { ProcessedDocument } from '@/utils/process-document';\nimport type { TagObject } from '@/types';\nimport { dump } from 'js-yaml';\nimport { removeUndefined } from '@/utils/remove-undefined';\nimport type { OutputEntry } from '@/utils/pages/builder';\nimport type { InternalOpenAPIMeta } from '@/server/source-api';\nimport { toStaticData } from '@/utils/pages/to-static-data';\n\nexport interface PagesToTextOptions {\n /**\n * Additional imports of your MDX components.\n */\n imports?: {\n names: string[];\n from: string;\n }[];\n\n /**\n * Customise frontmatter.\n *\n * A `full: true` property will be added by default.\n */\n frontmatter?: (\n title: string,\n description: string | undefined,\n context: DocumentContext,\n ) => Record<string, unknown>;\n\n /**\n * Add description to document body.\n *\n * We recommend but don't enable it by default because some OpenAPI schemas have invalid description that breaks MDX syntax.\n *\n * @defaultValue false\n */\n includeDescription?: boolean;\n\n /**\n * Add a comment to the top of generated files indicating they are auto-generated.\n * - `true`: Adds a standardized comment\n * - `false`: No comment is added\n * - `string`: Adds the provided custom comment\n *\n * @defaultValue true\n */\n addGeneratedComment?: boolean | string;\n}\n\nexport function toText(\n entry: OutputEntry,\n processed: ProcessedDocument,\n options: PagesToTextOptions = {},\n) {\n switch (entry.type) {\n case 'operation':\n return generatePage(\n entry.schemaId,\n processed,\n {\n operations: [entry.item],\n },\n {\n ...options,\n ...entry.info,\n },\n {\n type: 'operation',\n },\n );\n case 'group':\n return generatePage(\n entry.schemaId,\n processed,\n {\n operations: entry.operations,\n webhooks: entry.webhooks,\n showTitle: true,\n },\n {\n ...options,\n ...entry.info,\n },\n {\n type: 'file',\n },\n );\n case 'tag':\n return generatePage(\n entry.schemaId,\n processed,\n {\n operations: entry.operations,\n webhooks: entry.webhooks,\n showTitle: true,\n },\n {\n ...options,\n ...entry.info,\n },\n {\n type: 'tag',\n tag: entry.rawTag,\n },\n );\n case 'webhook':\n return generatePage(\n entry.schemaId,\n processed,\n {\n webhooks: [entry.item],\n },\n {\n ...options,\n ...entry.info,\n },\n {\n type: 'operation',\n },\n );\n }\n}\n\nexport function generateDocument(\n frontmatter: unknown,\n content: string,\n options: PagesToTextOptions,\n): string {\n const { addGeneratedComment = true, imports } = options;\n const out: string[] = [];\n const banner = dump(removeUndefined(frontmatter as object)).trimEnd();\n if (banner.length > 0) out.push(`---\\n${banner}\\n---`);\n\n if (addGeneratedComment) {\n let commentContent =\n 'This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again.';\n\n if (typeof addGeneratedComment === 'string') {\n commentContent = addGeneratedComment;\n }\n\n commentContent = commentContent.replaceAll('/', '\\\\/');\n out.push(`{/* ${commentContent} */}`);\n }\n\n if (imports) {\n out.push(\n ...imports\n .map((item) => `import { ${item.names.join(', ')} } from ${JSON.stringify(item.from)};`)\n .join('\\n'),\n );\n }\n\n out.push(content);\n return out.join('\\n\\n');\n}\n\nexport type DocumentContext =\n | {\n type: 'tag';\n tag: TagObject | undefined;\n }\n | {\n type: 'operation';\n }\n | {\n type: 'file';\n };\n\nfunction generatePage(\n schemaId: string,\n processed: ProcessedDocument,\n pageProps: Omit<ApiPageProps, 'document'>,\n options: PagesToTextOptions & {\n title: string;\n description?: string;\n },\n context: DocumentContext,\n): string {\n const { frontmatter, includeDescription = false } = options;\n const extend = frontmatter?.(options.title, options.description, context);\n const page: ApiPageProps = {\n ...pageProps,\n document: schemaId,\n };\n\n let meta: InternalOpenAPIMeta | undefined;\n if (page.operations?.length === 1) {\n const operation = page.operations[0];\n\n meta = {\n method: operation.method.toUpperCase(),\n };\n } else if (page.webhooks?.length === 1) {\n const webhook = page.webhooks[0];\n\n meta = {\n method: webhook.method.toUpperCase(),\n webhook: true,\n };\n }\n\n const data = toStaticData(page, processed.dereferenced);\n const content: string[] = [];\n\n if (options.description && includeDescription) content.push(options.description);\n content.push(pageContent(page));\n\n return generateDocument(\n {\n title: options.title,\n description: !includeDescription ? options.description : undefined,\n full: true,\n ...extend,\n _openapi: {\n ...meta,\n ...data,\n ...(extend?._openapi as object | undefined),\n },\n },\n content.join('\\n\\n'),\n options,\n );\n}\n\nfunction pageContent({\n showTitle,\n showDescription,\n document,\n webhooks,\n operations,\n}: ApiPageProps): string {\n const propStrs: string[] = [`document={${JSON.stringify(document)}}`];\n\n // filter extra properties in props\n if (webhooks) {\n propStrs.push(\n `webhooks={${JSON.stringify(\n webhooks.map(\n (item) =>\n ({\n name: item.name,\n method: item.method,\n }) satisfies WebhookItem,\n ),\n )}}`,\n );\n }\n if (operations) {\n propStrs.push(\n `operations={${JSON.stringify(\n operations.map(\n (item) =>\n ({\n path: item.path,\n method: item.method,\n }) satisfies OperationItem,\n ),\n )}}`,\n );\n }\n if (showTitle) {\n propStrs.push(`showTitle={${JSON.stringify(showTitle)}}`);\n }\n if (showDescription) {\n propStrs.push(`showDescription={${JSON.stringify(showDescription)}}`);\n }\n\n return `<APIPage ${propStrs.join(' ')} />`;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"to-text.js","names":[],"sources":["../../../src/utils/pages/to-text.ts"],"sourcesContent":["import type { ApiPageProps, OperationItem, WebhookItem } from '@/ui/api-page';\nimport type { ProcessedDocument } from '@/utils/process-document';\nimport type { TagObject } from '@/types';\nimport { dump } from 'js-yaml';\nimport { removeUndefined } from '@/utils/remove-undefined';\nimport type { OperationOutput, PageOutput, WebhookOutput } from '@/utils/pages/builder';\nimport type { InternalOpenAPIMeta } from '@/server/source-api';\nimport { toStaticData } from '@/utils/pages/to-static-data';\n\nexport interface PagesToTextOptions {\n /**\n * Additional imports of your MDX components.\n */\n imports?: {\n names: string[];\n from: string;\n }[];\n\n /**\n * Customise frontmatter.\n *\n * A `full: true` property will be added by default.\n */\n frontmatter?: (\n title: string,\n description: string | undefined,\n context: DocumentContext,\n ) => Record<string, unknown>;\n\n /**\n * Add description to document body.\n *\n * We recommend but don't enable it by default because some OpenAPI schemas have invalid description that breaks MDX syntax.\n *\n * @defaultValue false\n */\n includeDescription?: boolean;\n\n /**\n * Add a comment to the top of generated files indicating they are auto-generated.\n * - `true`: Adds a standardized comment\n * - `false`: No comment is added\n * - `string`: Adds the provided custom comment\n *\n * @defaultValue true\n */\n addGeneratedComment?: boolean | string;\n}\n\nexport function toText(\n entry: PageOutput | OperationOutput | WebhookOutput,\n processed: ProcessedDocument,\n options: PagesToTextOptions = {},\n) {\n switch (entry.type) {\n case 'operation':\n return generatePage(\n entry.schemaId,\n processed,\n {\n operations: [entry.item],\n },\n {\n ...options,\n ...entry.info,\n },\n {\n type: 'operation',\n },\n );\n case 'page':\n return generatePage(\n entry.schemaId,\n processed,\n {\n operations: entry.operations,\n webhooks: entry.webhooks,\n showTitle: true,\n },\n {\n ...options,\n ...entry.info,\n },\n entry.tag\n ? {\n type: 'tag',\n tag: entry.tag,\n }\n : {\n type: 'file',\n },\n );\n case 'webhook':\n return generatePage(\n entry.schemaId,\n processed,\n {\n webhooks: [entry.item],\n },\n {\n ...options,\n ...entry.info,\n },\n {\n type: 'operation',\n },\n );\n }\n}\n\nexport function generateDocument(\n frontmatter: unknown,\n content: string,\n options: PagesToTextOptions,\n): string {\n const { addGeneratedComment = true, imports } = options;\n const out: string[] = [];\n const banner = dump(removeUndefined(frontmatter as object)).trimEnd();\n if (banner.length > 0) out.push(`---\\n${banner}\\n---`);\n\n if (addGeneratedComment) {\n let commentContent =\n 'This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again.';\n\n if (typeof addGeneratedComment === 'string') {\n commentContent = addGeneratedComment;\n }\n\n commentContent = commentContent.replaceAll('/', '\\\\/');\n out.push(`{/* ${commentContent} */}`);\n }\n\n if (imports) {\n out.push(\n ...imports\n .map((item) => `import { ${item.names.join(', ')} } from ${JSON.stringify(item.from)};`)\n .join('\\n'),\n );\n }\n\n out.push(content);\n return out.join('\\n\\n');\n}\n\nexport type DocumentContext =\n | {\n type: 'tag';\n tag: TagObject | undefined;\n }\n | {\n type: 'operation';\n }\n | {\n type: 'file';\n };\n\nfunction generatePage(\n schemaId: string,\n processed: ProcessedDocument,\n pageProps: Omit<ApiPageProps, 'document'>,\n options: PagesToTextOptions & {\n title: string;\n description?: string;\n },\n context: DocumentContext,\n): string {\n const { frontmatter, includeDescription = false } = options;\n const extend = frontmatter?.(options.title, options.description, context);\n const page: ApiPageProps = {\n ...pageProps,\n document: schemaId,\n };\n\n let meta: InternalOpenAPIMeta | undefined;\n if (page.operations?.length === 1) {\n const operation = page.operations[0];\n\n meta = {\n method: operation.method.toUpperCase(),\n };\n } else if (page.webhooks?.length === 1) {\n const webhook = page.webhooks[0];\n\n meta = {\n method: webhook.method.toUpperCase(),\n webhook: true,\n };\n }\n\n const data = toStaticData(page, processed.dereferenced);\n const content: string[] = [];\n\n if (options.description && includeDescription) content.push(options.description);\n content.push(pageContent(page));\n\n return generateDocument(\n {\n title: options.title,\n description: !includeDescription ? options.description : undefined,\n full: true,\n ...extend,\n _openapi: {\n ...meta,\n ...data,\n ...(extend?._openapi as object | undefined),\n },\n },\n content.join('\\n\\n'),\n options,\n );\n}\n\nfunction pageContent({\n showTitle,\n showDescription,\n document,\n webhooks,\n operations,\n}: ApiPageProps): string {\n const propStrs: string[] = [`document={${JSON.stringify(document)}}`];\n\n // filter extra properties in props\n if (webhooks) {\n propStrs.push(\n `webhooks={${JSON.stringify(\n webhooks.map(\n (item) =>\n ({\n name: item.name,\n method: item.method,\n }) satisfies WebhookItem,\n ),\n )}}`,\n );\n }\n if (operations) {\n propStrs.push(\n `operations={${JSON.stringify(\n operations.map(\n (item) =>\n ({\n path: item.path,\n method: item.method,\n }) satisfies OperationItem,\n ),\n )}}`,\n );\n }\n if (showTitle) {\n propStrs.push(`showTitle={${JSON.stringify(showTitle)}}`);\n }\n if (showDescription) {\n propStrs.push(`showDescription={${JSON.stringify(showDescription)}}`);\n }\n\n return `<APIPage ${propStrs.join(' ')} />`;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-openapi",
3
- "version": "10.4.1",
3
+ "version": "10.5.0",
4
4
  "description": "Generate MDX docs for your OpenAPI spec",
5
5
  "keywords": [
6
6
  "Docs",
@@ -50,17 +50,17 @@
50
50
  "@radix-ui/react-select": "^2.2.6",
51
51
  "@radix-ui/react-slot": "^1.2.4",
52
52
  "@scalar/json-magic": "^0.12.4",
53
- "@scalar/openapi-upgrader": "^0.2.1",
53
+ "@scalar/openapi-upgrader": "^0.2.2",
54
54
  "ajv": "^8.18.0",
55
55
  "class-variance-authority": "^0.7.1",
56
56
  "dereference-json-schema": "^0.2.2",
57
57
  "github-slugger": "^2.0.0",
58
58
  "hast-util-to-jsx-runtime": "^2.3.6",
59
59
  "js-yaml": "^4.1.1",
60
- "lucide-react": "^0.577.0",
60
+ "lucide-react": "^1.6.0",
61
61
  "next-themes": "^0.4.6",
62
62
  "openapi-sampler": "^1.7.2",
63
- "react-hook-form": "^7.71.2",
63
+ "react-hook-form": "^7.72.0",
64
64
  "remark": "^15.0.1",
65
65
  "remark-rehype": "^11.1.2",
66
66
  "tailwind-merge": "^3.5.0",
@@ -68,7 +68,7 @@
68
68
  "@fumari/stf": "1.0.3"
69
69
  },
70
70
  "devDependencies": {
71
- "@scalar/api-client-react": "^1.4.11",
71
+ "@scalar/api-client-react": "^1.4.14",
72
72
  "@types/js-yaml": "^4.0.9",
73
73
  "@types/node": "25.5.0",
74
74
  "@types/openapi-sampler": "^1.0.3",
@@ -76,11 +76,11 @@
76
76
  "json-schema-typed": "^8.0.2",
77
77
  "shiki": "^4.0.2",
78
78
  "tailwindcss": "^4.2.2",
79
- "tsdown": "0.21.4",
79
+ "tsdown": "0.21.5",
80
80
  "@fumadocs/tailwind": "0.0.3",
81
81
  "eslint-config-custom": "0.0.0",
82
- "fumadocs-core": "16.7.1",
83
- "fumadocs-ui": "16.7.1",
82
+ "fumadocs-core": "16.7.6",
83
+ "fumadocs-ui": "16.7.6",
84
84
  "tsconfig": "0.0.0"
85
85
  },
86
86
  "peerDependencies": {
@@ -1,22 +0,0 @@
1
- //#region src/utils/pages/to-body.ts
2
- function toBody(entry) {
3
- if (entry.type === "operation") return {
4
- document: entry.schemaId,
5
- operations: [entry.item]
6
- };
7
- if (entry.type === "webhook") return {
8
- document: entry.schemaId,
9
- webhooks: [entry.item]
10
- };
11
- return {
12
- showTitle: true,
13
- showDescription: true,
14
- document: entry.schemaId,
15
- operations: entry.operations,
16
- webhooks: entry.webhooks
17
- };
18
- }
19
- //#endregion
20
- export { toBody };
21
-
22
- //# sourceMappingURL=to-body.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"to-body.js","names":[],"sources":["../../../src/utils/pages/to-body.ts"],"sourcesContent":["import type { ApiPageProps } from '@/ui/api-page';\nimport type { OutputEntry } from '@/utils/pages/builder';\n\nexport function toBody(entry: OutputEntry): ApiPageProps {\n if (entry.type === 'operation')\n return {\n document: entry.schemaId,\n operations: [entry.item],\n };\n if (entry.type === 'webhook')\n return {\n document: entry.schemaId,\n webhooks: [entry.item],\n };\n\n return {\n showTitle: true,\n showDescription: true,\n document: entry.schemaId,\n operations: entry.operations,\n webhooks: entry.webhooks,\n };\n}\n"],"mappings":";AAGA,SAAgB,OAAO,OAAkC;AACvD,KAAI,MAAM,SAAS,YACjB,QAAO;EACL,UAAU,MAAM;EAChB,YAAY,CAAC,MAAM,KAAK;EACzB;AACH,KAAI,MAAM,SAAS,UACjB,QAAO;EACL,UAAU,MAAM;EAChB,UAAU,CAAC,MAAM,KAAK;EACvB;AAEH,QAAO;EACL,WAAW;EACX,iBAAiB;EACjB,UAAU,MAAM;EAChB,YAAY,MAAM;EAClB,UAAU,MAAM;EACjB"}