@blaaiz/docs-core 0.5.0 → 0.6.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 (43) hide show
  1. package/dist/{chunk-ZWDLAW5M.js → chunk-2GUUVNTP.js} +222 -28
  2. package/dist/chunk-2GUUVNTP.js.map +1 -0
  3. package/dist/{chunk-ZKOOKLZ3.js → chunk-6H6OB7YP.js} +17 -2
  4. package/dist/chunk-6H6OB7YP.js.map +1 -0
  5. package/dist/generator.cjs +161 -18
  6. package/dist/generator.cjs.map +1 -1
  7. package/dist/generator.d.cts +24 -0
  8. package/dist/generator.d.ts +24 -0
  9. package/dist/generator.js +140 -13
  10. package/dist/generator.js.map +1 -1
  11. package/dist/index.cjs +224 -25
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.cts +249 -4
  14. package/dist/index.d.ts +249 -4
  15. package/dist/index.js +1 -1
  16. package/dist/{navigation-CGqFIPlP.d.cts → navigation-DvgyWSZ4.d.cts} +22 -1
  17. package/dist/{navigation-CGqFIPlP.d.ts → navigation-DvgyWSZ4.d.ts} +22 -1
  18. package/dist/ui/api-try-it.cjs +15 -0
  19. package/dist/ui/api-try-it.cjs.map +1 -1
  20. package/dist/ui/api-try-it.js +1 -1
  21. package/dist/ui/ask-ai.cjs +26 -6
  22. package/dist/ui/ask-ai.cjs.map +1 -1
  23. package/dist/ui/ask-ai.d.cts +6 -1
  24. package/dist/ui/ask-ai.d.ts +6 -1
  25. package/dist/ui/ask-ai.js +26 -6
  26. package/dist/ui/ask-ai.js.map +1 -1
  27. package/dist/ui/copy-page.cjs +15 -0
  28. package/dist/ui/copy-page.cjs.map +1 -1
  29. package/dist/ui/copy-page.js +1 -1
  30. package/dist/ui.cjs +77 -6
  31. package/dist/ui.cjs.map +1 -1
  32. package/dist/ui.d.cts +99 -8
  33. package/dist/ui.d.ts +99 -8
  34. package/dist/ui.js +62 -8
  35. package/dist/ui.js.map +1 -1
  36. package/package.json +1 -1
  37. package/skills/SKILL.md +68 -27
  38. package/styles/api-reference.css +45 -2
  39. package/styles/ask-ai.css +28 -0
  40. package/styles/docs.css +96 -1
  41. package/styles/home.css +34 -1
  42. package/dist/chunk-ZKOOKLZ3.js.map +0 -1
  43. package/dist/chunk-ZWDLAW5M.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/generator/index.ts"],"names":[],"mappings":"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","file":"generator.js","sourcesContent":["/**\n * `@blaaiz/docs-core/generator` — builds the docs content tree from `docs.json`.\n *\n * One call turns the site's navigation file into everything fumadocs needs:\n * generated API pages placed in their configured groups, and `meta.json` files\n * encoding tab and group order. Authors reorganize the docs by editing\n * `docs.json` — never by moving files.\n *\n * Node-only. A site that publishes an API reference injects `createOpenAPI` and\n * `generateFiles` from its own `fumadocs-openapi` install, so this package takes\n * no dependency on it. A site of prose alone injects nothing: with no API pages\n * in `docs.json` the generator writes the `meta.json` files and `nav.json` and\n * never touches `fumadocs-openapi`.\n *\n * @packageDocumentation\n */\n\nimport { mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { mergeOpenApiDocuments } from '../adapters/index.js';\nimport { parseNavigation } from '../adapters/index.js';\nimport type { NavItem, OpenApiDocument, OpenApiInfo, OpenApiServer } from '../core/index.js';\nimport { planDocsTree } from '../core/index.js';\n\n/**\n * The two fumadocs-openapi functions the generator needs, injected by the site\n * (`createOpenAPI` from `fumadocs-openapi/server`, `generateFiles` from\n * `fumadocs-openapi`). Typed loosely so this package needs no fumadocs types.\n *\n * @public\n */\nexport interface FumadocsOpenApi {\n readonly createOpenAPI: (...args: never[]) => unknown;\n readonly generateFiles: (...args: never[]) => unknown;\n}\n\n/**\n * Options for {@link generateDocsTree}.\n *\n * @public\n */\nexport interface GenerateDocsTreeOptions {\n /** The site's content directory (holds `docs.json`, spec files, and `docs/`). */\n readonly contentDir: string;\n /** Path to the navigation file. Default `<contentDir>/docs.json`. */\n readonly docsJson?: string;\n /** Output directory for the page tree. Default `<contentDir>/docs`. */\n readonly outDir?: string;\n /** Folder for generated API pages. Default `api`. */\n readonly apiDir?: string;\n /**\n * `info` block of the merged OpenAPI document. Required when `docs.json`\n * publishes API pages; ignored on a site of prose alone, which merges no\n * documents.\n */\n readonly info?: OpenApiInfo;\n /** Servers of the merged document. */\n readonly servers?: readonly OpenApiServer[];\n /** Same-origin proxy route for the try-it playground. */\n readonly proxyUrl?: string;\n /** URL base the fumadocs loader serves pages under. Default `/docs`. */\n readonly baseUrl?: string;\n /**\n * The site's fumadocs-openapi functions. Required when `docs.json` publishes\n * API pages; omit it on a site of prose alone.\n */\n readonly fumadocs?: FumadocsOpenApi;\n}\n\n/**\n * A top-navigation link: one tab, pointing at its first page.\n *\n * @public\n */\nexport interface NavLink {\n readonly text: string;\n readonly url: string;\n /** The tab's icon name from `docs.json`, for the site to resolve. */\n readonly icon?: string;\n}\n\n/**\n * What the generator produced.\n *\n * @public\n */\nexport interface GenerateDocsTreeResult {\n readonly pages: number;\n readonly metas: number;\n /**\n * Top-nav links, one per tab, each pointing at the tab's first page — written\n * to `<contentDir>/nav.json` for the site's layout. Keeps the top nav in sync\n * with `docs.json` instead of a hand-maintained list.\n */\n readonly navLinks: readonly NavLink[];\n /**\n * Page URL to HTTP method, written to `<contentDir>/api-methods.json` for the\n * sidebar method badges (`apiMethodBadges` in `@blaaiz/docs-core/ui`).\n */\n readonly methodsByUrl: Readonly<Record<string, string>>;\n /** The merged OpenAPI document (reusable for the runtime page renderer). */\n readonly document: OpenApiDocument;\n}\n\ninterface GeneratedFile {\n path: string;\n content: string;\n}\n\nfunction slugify(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\n/**\n * The frontmatter `title` of an MDX page, or null when the file has none.\n * Reads only the leading frontmatter block; quotes around the value are\n * stripped.\n */\nfunction frontmatterTitle(source: string): string | null {\n const block = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/.exec(source);\n if (!block?.[1]) {\n return null;\n }\n const line = /^title\\s*:\\s*(.+)\\s*$/m.exec(block[1]);\n if (!line?.[1]) {\n return null;\n }\n return line[1].replace(/^(['\"])(.*)\\1$/, '$2');\n}\n\n/** The `METHOD path` key of the first operation a generated page renders. */\nfunction operationKeyOf(content: string): string | null {\n const match = /operations=\\{(\\[[\\s\\S]*?\\])\\}/.exec(content);\n if (!match?.[1]) {\n return null;\n }\n try {\n const ops = JSON.parse(match[1]) as { path: string; method: string }[];\n const op = ops[0];\n return op ? `${op.method.toUpperCase()} ${op.path}` : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The `info` a docs-only run reports on its (empty) merged document. Nothing\n * renders it: with no API pages there is no reference view to title.\n */\nconst PROSE_ONLY_INFO: OpenApiInfo = { title: 'No API reference', version: '0.0.0' };\n\n/**\n * The fumadocs functions, checked. Called only when the navigation publishes\n * API pages, so the message can name the exact reason they are needed.\n */\nfunction requireFumadocs(value: unknown, apiPageCount: number): FumadocsOpenApi {\n const because = `docs.json publishes ${String(apiPageCount)} API page(s)`;\n if (typeof value !== 'object' || value === null) {\n throw new Error(\n `[docs-core] generateDocsTree: the 'fumadocs' option is required because ${because}. ` +\n \"Pass { createOpenAPI, generateFiles } from the site's fumadocs-openapi install.\",\n );\n }\n const candidate = value as Record<string, unknown>;\n for (const name of ['createOpenAPI', 'generateFiles'] as const) {\n if (typeof candidate[name] !== 'function') {\n throw new Error(\n `[docs-core] generateDocsTree: fumadocs.${name} must be a function because ${because}. ` +\n `Import ${name} from ${name === 'createOpenAPI' ? \"'fumadocs-openapi/server'\" : \"'fumadocs-openapi'\"}.`,\n );\n }\n }\n return value as FumadocsOpenApi;\n}\n\n/**\n * Build the docs content tree from `docs.json`.\n *\n * Reads the navigation, merges every referenced spec, generates one page per\n * declared endpoint into its configured group folder, and writes the\n * `meta.json` files that give the sidebar the navigation's exact structure and\n * order. Operations present in a spec but not declared in `docs.json` get no\n * page — the navigation decides what is published, as on Mintlify.\n *\n * A site of prose alone needs neither option: with no API pages in `docs.json`\n * the run writes the `meta.json` files, `nav.json`, and an empty\n * `api-methods.json`, and never loads `fumadocs-openapi`.\n *\n * @example\n * ```ts\n * // A site of prose alone.\n * await generateDocsTree({ contentDir: 'content' });\n * ```\n *\n * @param options - directories, document info, and the injected fumadocs functions\n * @returns counts and the merged document\n * @throws when `docs.json` publishes API pages and `fumadocs` or `info` is missing\n * @public\n */\nexport async function generateDocsTree(\n options: GenerateDocsTreeOptions,\n): Promise<GenerateDocsTreeResult> {\n const docsJson = options.docsJson ?? path.join(options.contentDir, 'docs.json');\n const outDir = options.outDir ?? path.join(options.contentDir, 'docs');\n const apiDir = options.apiDir ?? 'api';\n\n const navigation = parseNavigation(JSON.parse(await readFile(docsJson, 'utf8')));\n const plan = planDocsTree(navigation, { apiDir });\n\n // Where each planned API page lands. Both stay empty on a prose-only site.\n const placements = new Map<string, { dir: string; order: number; slug: string }>();\n const groupPages = new Map<string, { order: number; slug: string }[]>();\n let pages = 0;\n let document: OpenApiDocument;\n\n if (plan.apiPages.length === 0) {\n // A site of prose alone. Nothing to merge, nothing to generate, and — most\n // importantly — nothing removed: a hand-written folder named `api` is the\n // site's own content, not our output.\n document = mergeOpenApiDocuments([], { info: options.info ?? PROSE_ONLY_INFO });\n } else {\n const fumadocs = requireFumadocs(options.fumadocs, plan.apiPages.length);\n if (options.info === undefined) {\n throw new Error(\n `[docs-core] generateDocsTree: the 'info' option is required because docs.json ` +\n `publishes ${String(plan.apiPages.length)} API page(s). ` +\n 'Pass { title, version } for the merged OpenAPI document.',\n );\n }\n\n // Merge exactly the specs the navigation references.\n const docs = await Promise.all(\n plan.specs.map(\n async (file) =>\n JSON.parse(await readFile(path.join(options.contentDir, file), 'utf8')) as unknown,\n ),\n );\n document = mergeOpenApiDocuments(docs, {\n info: options.info,\n ...(options.servers ? { servers: options.servers } : {}),\n });\n\n // Resolve each planned page's slug from the merged document.\n for (const page of plan.apiPages) {\n const operation = document.paths[page.apiPath]?.[page.method.toLowerCase()] as\n { operationId?: string } | undefined;\n if (!operation) {\n throw new Error(\n `[docs-core] docs.json references ${page.method} ${page.apiPath}, ` +\n `but ${page.file} defines no such operation.`,\n );\n }\n const slug = operation.operationId ?? slugify(`${page.method} ${page.apiPath}`);\n placements.set(`${page.method.toUpperCase()} ${page.apiPath}`, { ...page, slug });\n const group = groupPages.get(page.dir) ?? [];\n group.push({ order: page.order, slug });\n groupPages.set(page.dir, group);\n }\n\n // Fresh API tree, then generate pages and route each into its planned folder.\n for (const group of plan.apiGroups) {\n if (group.root) {\n await rm(path.join(outDir, group.dir), { recursive: true, force: true });\n }\n }\n const createOpenAPI = fumadocs.createOpenAPI as (input: unknown) => unknown;\n const generateFiles = fumadocs.generateFiles as (input: unknown) => Promise<void>;\n await generateFiles({\n input: createOpenAPI({\n input: { blaaiz: document },\n ...(options.proxyUrl ? { proxyUrl: options.proxyUrl } : {}),\n }),\n output: outDir,\n per: 'operation',\n groupBy: 'none',\n beforeWrite(files: GeneratedFile[]) {\n for (let i = files.length - 1; i >= 0; i -= 1) {\n const file = files[i];\n if (!file) {\n continue;\n }\n const key = operationKeyOf(file.content);\n const placement = key ? placements.get(key) : undefined;\n if (!placement) {\n files.splice(i, 1); // not declared in docs.json -> not published\n continue;\n }\n file.path = path.join(placement.dir, `${placement.slug}.mdx`);\n pages += 1;\n }\n },\n });\n }\n\n // Doc pages declared inside API tabs become link entries in their group's\n // meta, at their declared position; the page keeps living in its own folder.\n const baseUrl = (options.baseUrl ?? '/docs').replace(/\\/$/, '');\n const docUrl = (file: string): string => {\n const clean = file.replace(/\\/?index$/, '');\n return clean ? `${baseUrl}/${clean}` : baseUrl;\n };\n for (const link of plan.apiDocLinks) {\n const source = path.join(outDir, `${link.file}.mdx`);\n let title: string | null = null;\n try {\n title = frontmatterTitle(await readFile(source, 'utf8'));\n } catch {\n console.warn(\n `[docs-core] docs.json lists ${link.file} inside an API tab, but ` +\n `${source} does not exist. The sidebar entry is skipped.`,\n );\n continue;\n }\n if (title === null) {\n title = link.file.split('/').pop() ?? link.file;\n console.warn(\n `[docs-core] ${link.file}.mdx has no frontmatter title; its sidebar ` +\n `entry falls back to '${title}'.`,\n );\n }\n const group = groupPages.get(link.dir) ?? [];\n // The meta link syntax cannot carry these characters in a name or URL.\n group.push({\n order: link.order,\n slug: `[${title.replace(/[[\\]()]/g, '')}](${docUrl(link.file)})`,\n });\n groupPages.set(link.dir, group);\n }\n\n // Meta files: the plan's static ones, plus each API group's entries — its\n // resolved page slugs and child folders, merged back into declared order.\n const metas = [\n ...plan.metas,\n ...plan.apiGroups.map((group) => ({\n path: `${group.dir}/meta.json`,\n content: {\n title: group.title,\n ...(group.root ? { root: true } : {}),\n ...(group.icon ? { icon: group.icon } : {}),\n pages: [\n ...(groupPages.get(group.dir) ?? []).map((page) => ({\n order: page.order,\n name: page.slug,\n })),\n ...group.children.map((child) => ({ order: child.order, name: child.folder })),\n ]\n .sort((a, b) => a.order - b.order)\n .map((entry) => entry.name),\n },\n })),\n ];\n for (const meta of metas) {\n const target = path.join(outDir, meta.path);\n await mkdir(path.dirname(target), { recursive: true });\n await writeFile(target, `${JSON.stringify(meta.content, null, 2)}\\n`);\n }\n\n // Top-nav links: each tab points at its first page. The URL of an API page\n // needs its resolved slug, which is why this runs here, not in the planner.\n const firstUrl = (items: readonly NavItem[]): string | undefined => {\n for (const item of items) {\n if (item.kind === 'group') {\n const url = firstUrl(item.items);\n if (url) {\n return url;\n }\n } else if (item.kind === 'doc') {\n return docUrl(item.file);\n } else {\n const placement = placements.get(`${item.method.toUpperCase()} ${item.apiPath}`);\n if (placement) {\n return `${baseUrl}/${placement.dir}/${placement.slug}`;\n }\n }\n }\n return undefined;\n };\n const navLinks: NavLink[] = [];\n for (const tab of navigation) {\n const url = firstUrl(tab.items);\n if (url) {\n navLinks.push({ text: tab.title, url, ...(tab.icon ? { icon: tab.icon } : {}) });\n }\n }\n await writeFile(\n path.join(options.contentDir, 'nav.json'),\n `${JSON.stringify(navLinks, null, 2)}\\n`,\n );\n\n // Page URL -> HTTP method, for the sidebar method badges.\n const methodsByUrl: Record<string, string> = {};\n for (const page of plan.apiPages) {\n const placement = placements.get(`${page.method.toUpperCase()} ${page.apiPath}`);\n if (placement) {\n methodsByUrl[`${baseUrl}/${placement.dir}/${placement.slug}`] = page.method.toUpperCase();\n }\n }\n await writeFile(\n path.join(options.contentDir, 'api-methods.json'),\n `${JSON.stringify(methodsByUrl, null, 2)}\\n`,\n );\n\n return { pages, metas: metas.length, navLinks, methodsByUrl, document };\n}\n"]}
1
+ {"version":3,"sources":["../src/generator/index.ts"],"names":[],"mappings":"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","file":"generator.js","sourcesContent":["/**\n * `@blaaiz/docs-core/generator` — builds the docs content tree from `docs.json`.\n *\n * One call turns the site's navigation file into everything fumadocs needs:\n * generated API pages placed in their configured groups, and `meta.json` files\n * encoding tab and group order. Authors reorganize the docs by editing\n * `docs.json` — never by moving files.\n *\n * Node-only. A site that publishes an API reference injects `createOpenAPI` and\n * `generateFiles` from its own `fumadocs-openapi` install, so this package takes\n * no dependency on it. A site of prose alone injects nothing: with no API pages\n * in `docs.json` the generator writes the `meta.json` files and `nav.json` and\n * never touches `fumadocs-openapi`.\n *\n * @packageDocumentation\n */\n\nimport { mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { mergeOpenApiDocuments } from '../adapters/index.js';\nimport { parseNavigation } from '../adapters/index.js';\nimport type {\n NavItem,\n OpenApiDocument,\n OpenApiInfo,\n OpenApiServer,\n SidebarStyle,\n} from '../core/index.js';\nimport { planDocsTree } from '../core/index.js';\nimport { hasOpenApi } from '../core/docs-tree.js';\n\n/**\n * The two fumadocs-openapi functions the generator needs, injected by the site\n * (`createOpenAPI` from `fumadocs-openapi/server`, `generateFiles` from\n * `fumadocs-openapi`). Typed loosely so this package needs no fumadocs types.\n *\n * @public\n */\nexport interface FumadocsOpenApi {\n readonly createOpenAPI: (...args: never[]) => unknown;\n readonly generateFiles: (...args: never[]) => unknown;\n}\n\n/**\n * Options for {@link generateDocsTree}.\n *\n * @public\n */\nexport interface GenerateDocsTreeOptions {\n /** The site's content directory (holds `docs.json`, spec files, and `docs/`). */\n readonly contentDir: string;\n /** Path to the navigation file. Default `<contentDir>/docs.json`. */\n readonly docsJson?: string;\n /** Output directory for the page tree. Default `<contentDir>/docs`. */\n readonly outDir?: string;\n /** Folder for generated API pages. Default `api`. */\n readonly apiDir?: string;\n /**\n * `info` block of the merged OpenAPI document. Required when `docs.json`\n * publishes API pages; ignored on a site of prose alone, which merges no\n * documents.\n */\n readonly info?: OpenApiInfo;\n /** Servers of the merged document. */\n readonly servers?: readonly OpenApiServer[];\n /** Same-origin proxy route for the try-it playground. */\n readonly proxyUrl?: string;\n /** URL base the fumadocs loader serves pages under. Default `/docs`. */\n readonly baseUrl?: string;\n /**\n * The site's fumadocs-openapi functions. Required when `docs.json` publishes\n * API pages; omit it on a site of prose alone.\n */\n readonly fumadocs?: FumadocsOpenApi;\n}\n\n/**\n * A top-navigation link: one tab, pointing at its first page.\n *\n * @public\n */\nexport interface NavLink {\n readonly text: string;\n readonly url: string;\n /** The tab's icon name from `docs.json`, for the site to resolve. */\n readonly icon?: string;\n /**\n * Path prefixes the link is active for. Emitted for an API tab, whose\n * territory the generator knows exactly: the tab's generated folder plus\n * the folders of any doc pages it declares. `docsNavLinks` derives the\n * section from the URL when this is absent.\n */\n readonly match?: readonly string[];\n}\n\n/**\n * What the generator produced.\n *\n * @public\n */\nexport interface GenerateDocsTreeResult {\n readonly pages: number;\n readonly metas: number;\n /**\n * Top-nav links, one per tab, each pointing at the tab's first page — written\n * to `<contentDir>/nav.json` for the site's layout. Keeps the top nav in sync\n * with `docs.json` instead of a hand-maintained list.\n */\n readonly navLinks: readonly NavLink[];\n /**\n * Page URL to HTTP method, written to `<contentDir>/api-methods.json` for the\n * sidebar method badges (`apiMethodBadges` in `@blaaiz/docs-core/ui`).\n */\n readonly methodsByUrl: Readonly<Record<string, string>>;\n /**\n * Page URL to its operation's source, written to\n * `<contentDir>/api-sources.json` — the Markdown route serves the original\n * spec from it.\n */\n readonly sourcesByUrl: Readonly<\n Record<string, { readonly file: string; readonly method: string; readonly route: string }>\n >;\n /**\n * Old page URL to its current one, written to `<contentDir>/redirects.json`.\n * A renamed operation (its summary edited, its page moved) leaves a\n * forwarding address here; the site's middleware serves permanent redirects\n * from it through `redirectTarget`.\n */\n readonly redirects: Readonly<Record<string, string>>;\n /** The merged OpenAPI document (reusable for the runtime page renderer). */\n readonly document: OpenApiDocument;\n}\n\ninterface GeneratedFile {\n path: string;\n content: string;\n}\n\n/**\n * Kebab-case free text, splitting camelCase words. Distinct from the\n * planner's `slugify`, which names folders and must not re-split names that\n * already exist on disk.\n */\nfunction kebabCase(value: string): string {\n return value\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\n/**\n * The page slug for one operation: its summary, kebab-cased — the readable\n * URL. A summary that kebab-cases to nothing falls back to the\n * `operationId`, then to `METHOD path`, which always yields a slug.\n */\nfunction pageSlug(\n operation: { operationId?: string; summary?: string },\n method: string,\n apiPath: string,\n): string {\n for (const candidate of [operation.summary, operation.operationId]) {\n if (candidate !== undefined) {\n const slug = kebabCase(candidate);\n if (slug !== '') {\n return slug;\n }\n }\n }\n return kebabCase(`${method} ${apiPath}`);\n}\n\n/**\n * The frontmatter `title` of an MDX page, or null when the file has none.\n * Reads only the leading frontmatter block; quotes around the value are\n * stripped.\n */\nfunction frontmatterTitle(source: string): string | null {\n const block = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/.exec(source);\n if (!block?.[1]) {\n return null;\n }\n const line = /^title\\s*:\\s*(.+)\\s*$/m.exec(block[1]);\n if (!line?.[1]) {\n return null;\n }\n return line[1].replace(/^(['\"])(.*)\\1$/, '$2');\n}\n\n/** The `METHOD path` key of the first operation a generated page renders. */\nfunction operationKeyOf(content: string): string | null {\n const match = /operations=\\{(\\[[\\s\\S]*?\\])\\}/.exec(content);\n if (!match?.[1]) {\n return null;\n }\n try {\n const ops = JSON.parse(match[1]) as { path: string; method: string }[];\n const op = ops[0];\n return op ? `${op.method.toUpperCase()} ${op.path}` : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The `info` a docs-only run reports on its (empty) merged document. Nothing\n * renders it: with no API pages there is no reference view to title.\n */\nconst PROSE_ONLY_INFO: OpenApiInfo = { title: 'No API reference', version: '0.0.0' };\n\n/** The optional top-level `sidebar` key of `docs.json`, validated. */\nfunction sidebarStyleOf(docsJson: Record<string, unknown>): SidebarStyle {\n const value = docsJson['sidebar'] ?? 'collapsible';\n if (value !== 'collapsible' && value !== 'headings') {\n throw new Error(\n `[docs-core] docs.json has sidebar: ${JSON.stringify(value)}. Use 'headings' ` +\n `(flat group headings, nested groups closed) or 'collapsible' (every group a folder).`,\n );\n }\n return value;\n}\n\n/**\n * The fumadocs functions, checked. Called only when the navigation publishes\n * API pages, so the message can name the exact reason they are needed.\n */\nfunction requireFumadocs(value: unknown, apiPageCount: number): FumadocsOpenApi {\n const because = `docs.json publishes ${String(apiPageCount)} API page(s)`;\n if (typeof value !== 'object' || value === null) {\n throw new Error(\n `[docs-core] generateDocsTree: the 'fumadocs' option is required because ${because}. ` +\n \"Pass { createOpenAPI, generateFiles } from the site's fumadocs-openapi install.\",\n );\n }\n const candidate = value as Record<string, unknown>;\n for (const name of ['createOpenAPI', 'generateFiles'] as const) {\n if (typeof candidate[name] !== 'function') {\n throw new Error(\n `[docs-core] generateDocsTree: fumadocs.${name} must be a function because ${because}. ` +\n `Import ${name} from ${name === 'createOpenAPI' ? \"'fumadocs-openapi/server'\" : \"'fumadocs-openapi'\"}.`,\n );\n }\n }\n return value as FumadocsOpenApi;\n}\n\n/**\n * Build the docs content tree from `docs.json`.\n *\n * Reads the navigation, merges every referenced spec, generates one page per\n * declared endpoint into its configured group folder, and writes the\n * `meta.json` files that give the sidebar the navigation's exact structure and\n * order. Operations present in a spec but not declared in `docs.json` get no\n * page — the navigation decides what is published, as on Mintlify.\n *\n * A site of prose alone needs neither option: with no API pages in `docs.json`\n * the run writes the `meta.json` files, `nav.json`, and an empty\n * `api-methods.json`, and never loads `fumadocs-openapi`.\n *\n * @example\n * ```ts\n * // A site of prose alone.\n * await generateDocsTree({ contentDir: 'content' });\n * ```\n *\n * @param options - directories, document info, and the injected fumadocs functions\n * @returns counts and the merged document\n * @throws when `docs.json` publishes API pages and `fumadocs` or `info` is missing\n * @public\n */\nexport async function generateDocsTree(\n options: GenerateDocsTreeOptions,\n): Promise<GenerateDocsTreeResult> {\n const docsJson = options.docsJson ?? path.join(options.contentDir, 'docs.json');\n const outDir = options.outDir ?? path.join(options.contentDir, 'docs');\n const apiDir = options.apiDir ?? 'api';\n\n const parsed = JSON.parse(await readFile(docsJson, 'utf8')) as Record<string, unknown>;\n const navigation = parseNavigation(parsed);\n const sidebar = sidebarStyleOf(parsed);\n const headings = sidebar === 'headings';\n const plan = planDocsTree(navigation, { apiDir, sidebar });\n\n // Where each planned API page lands. Both stay empty on a prose-only site.\n const placements = new Map<string, { dir: string; order: number; slug: string }>();\n const groupPages = new Map<string, { order: number; slug: string }[]>();\n let pages = 0;\n let document: OpenApiDocument;\n\n if (plan.apiPages.length === 0) {\n // A site of prose alone. Nothing to merge, nothing to generate, and — most\n // importantly — nothing removed: a hand-written folder named `api` is the\n // site's own content, not our output.\n document = mergeOpenApiDocuments([], { info: options.info ?? PROSE_ONLY_INFO });\n } else {\n const fumadocs = requireFumadocs(options.fumadocs, plan.apiPages.length);\n if (options.info === undefined) {\n throw new Error(\n `[docs-core] generateDocsTree: the 'info' option is required because docs.json ` +\n `publishes ${String(plan.apiPages.length)} API page(s). ` +\n 'Pass { title, version } for the merged OpenAPI document.',\n );\n }\n\n // Merge exactly the specs the navigation references.\n const docs = await Promise.all(\n plan.specs.map(\n async (file) =>\n JSON.parse(await readFile(path.join(options.contentDir, file), 'utf8')) as unknown,\n ),\n );\n document = mergeOpenApiDocuments(docs, {\n info: options.info,\n ...(options.servers ? { servers: options.servers } : {}),\n });\n\n // Resolve each planned page's slug from the merged document. Two pages in\n // one folder must never share a slug: that would be a silent overwrite.\n const taken = new Map<string, string>();\n for (const page of plan.apiPages) {\n const operation = document.paths[page.apiPath]?.[page.method.toLowerCase()] as\n { operationId?: string; summary?: string } | undefined;\n if (!operation) {\n throw new Error(\n `[docs-core] docs.json references ${page.method} ${page.apiPath}, ` +\n `but ${page.file} defines no such operation.`,\n );\n }\n const slug = pageSlug(operation, page.method, page.apiPath);\n const key = `${page.dir}/${slug}`;\n const holder = taken.get(key);\n if (holder !== undefined) {\n throw new Error(\n `[docs-core] ${holder} and ${page.method.toUpperCase()} ${page.apiPath} both ` +\n `resolve to the page '${key}'. Give one of them a distinct summary.`,\n );\n }\n taken.set(key, `${page.method.toUpperCase()} ${page.apiPath}`);\n placements.set(`${page.method.toUpperCase()} ${page.apiPath}`, { ...page, slug });\n const group = groupPages.get(page.dir) ?? [];\n group.push({ order: page.order, slug });\n groupPages.set(page.dir, group);\n }\n\n // Fresh API tree, then generate pages and route each into its planned folder.\n for (const group of plan.apiGroups) {\n if (group.root) {\n await rm(path.join(outDir, group.dir), { recursive: true, force: true });\n }\n }\n const createOpenAPI = fumadocs.createOpenAPI as (input: unknown) => unknown;\n const generateFiles = fumadocs.generateFiles as (input: unknown) => Promise<void>;\n await generateFiles({\n input: createOpenAPI({\n input: { blaaiz: document },\n ...(options.proxyUrl ? { proxyUrl: options.proxyUrl } : {}),\n }),\n output: outDir,\n per: 'operation',\n groupBy: 'none',\n beforeWrite(files: GeneratedFile[]) {\n for (let i = files.length - 1; i >= 0; i -= 1) {\n const file = files[i];\n if (!file) {\n continue;\n }\n const key = operationKeyOf(file.content);\n const placement = key ? placements.get(key) : undefined;\n if (!placement) {\n files.splice(i, 1); // not declared in docs.json -> not published\n continue;\n }\n file.path = path.join(placement.dir, `${placement.slug}.mdx`);\n pages += 1;\n }\n },\n });\n }\n\n // Doc pages declared inside API tabs become link entries in their group's\n // meta, at their declared position; the page keeps living in its own folder.\n const baseUrl = (options.baseUrl ?? '/docs').replace(/\\/$/, '');\n const docUrl = (file: string): string => {\n const clean = file.replace(/\\/?index$/, '');\n return clean ? `${baseUrl}/${clean}` : baseUrl;\n };\n for (const link of plan.apiDocLinks) {\n const source = path.join(outDir, `${link.file}.mdx`);\n let title: string | null = null;\n try {\n title = frontmatterTitle(await readFile(source, 'utf8'));\n } catch {\n console.warn(\n `[docs-core] docs.json lists ${link.file} inside an API tab, but ` +\n `${source} does not exist. The sidebar entry is skipped.`,\n );\n continue;\n }\n if (title === null) {\n title = link.file.split('/').pop() ?? link.file;\n console.warn(\n `[docs-core] ${link.file}.mdx has no frontmatter title; its sidebar ` +\n `entry falls back to '${title}'.`,\n );\n }\n const group = groupPages.get(link.dir) ?? [];\n // The meta link syntax cannot carry these characters in a name or URL.\n group.push({\n order: link.order,\n slug: `[${title.replace(/[[\\]()]/g, '')}](${docUrl(link.file)})`,\n });\n groupPages.set(link.dir, group);\n }\n\n // Meta files: the plan's static ones, plus each API group's entries — its\n // resolved page slugs and child folders, merged back into declared order.\n const metas = [\n ...plan.metas,\n ...plan.apiGroups.map((group) => ({\n path: `${group.dir}/meta.json`,\n content: {\n title: group.title,\n ...(group.root ? { root: true } : {}),\n ...(group.icon ? { icon: group.icon } : {}),\n // A nested group starts closed in the headings style, whatever the\n // site's defaultOpenLevel.\n ...(headings && !group.root ? { defaultOpen: false } : {}),\n pages: [\n ...(groupPages.get(group.dir) ?? []).map((page) => ({\n order: page.order,\n names: [page.slug],\n })),\n ...group.children.map((child) => ({\n order: child.order,\n // In the headings style a tab-level group is a flat heading with\n // its pages extracted beneath it, not a collapsible folder.\n names:\n headings && group.root\n ? [`---${child.title}---`, `...${child.folder}`]\n : [child.folder],\n })),\n ]\n .sort((a, b) => a.order - b.order)\n .flatMap((entry) => entry.names),\n },\n })),\n ];\n for (const meta of metas) {\n const target = path.join(outDir, meta.path);\n await mkdir(path.dirname(target), { recursive: true });\n await writeFile(target, `${JSON.stringify(meta.content, null, 2)}\\n`);\n }\n\n // Top-nav links: each tab points at its first page. The URL of an API page\n // needs its resolved slug, which is why this runs here, not in the planner.\n const firstUrl = (items: readonly NavItem[]): string | undefined => {\n for (const item of items) {\n if (item.kind === 'group') {\n const url = firstUrl(item.items);\n if (url) {\n return url;\n }\n } else if (item.kind === 'doc') {\n return docUrl(item.file);\n } else {\n const placement = placements.get(`${item.method.toUpperCase()} ${item.apiPath}`);\n if (placement) {\n return `${baseUrl}/${placement.dir}/${placement.slug}`;\n }\n }\n }\n return undefined;\n };\n const tabDocFiles = (items: readonly NavItem[]): string[] =>\n items.flatMap((item) => {\n if (item.kind === 'group') {\n return tabDocFiles(item.items);\n }\n return item.kind === 'doc' ? [item.file] : [];\n });\n\n // An API tab's active-match territory: its generated folder plus the exact\n // URLs of the doc pages it declares — pages, never their whole folders. The\n // tabs' generated folders sit in plan.apiGroups' root entries, in\n // navigation order.\n const apiTabRoots = plan.apiGroups.filter((group) => group.root).map((group) => group.dir);\n let apiTabIndex = 0;\n const navLinks: NavLink[] = [];\n for (const tab of navigation) {\n let match: string[] | undefined;\n if (hasOpenApi(tab.items)) {\n const root = apiTabRoots[apiTabIndex];\n apiTabIndex += 1;\n if (root !== undefined) {\n match = [\n ...new Set([`${baseUrl}/${root}`, ...tabDocFiles(tab.items).map((file) => docUrl(file))]),\n ];\n }\n }\n const url = firstUrl(tab.items);\n if (!url) {\n continue;\n }\n navLinks.push({\n text: tab.title,\n url,\n ...(tab.icon ? { icon: tab.icon } : {}),\n ...(match ? { match } : {}),\n });\n }\n await writeFile(\n path.join(options.contentDir, 'nav.json'),\n `${JSON.stringify(navLinks, null, 2)}\\n`,\n );\n\n // Page URL -> HTTP method, for the sidebar method badges; and page URL ->\n // the operation's source file, for the Markdown route.\n const methodsByUrl: Record<string, string> = {};\n const sourcesByUrl: Record<string, { file: string; method: string; route: string }> = {};\n for (const page of plan.apiPages) {\n const placement = placements.get(`${page.method.toUpperCase()} ${page.apiPath}`);\n if (placement) {\n const url = `${baseUrl}/${placement.dir}/${placement.slug}`;\n methodsByUrl[url] = page.method.toUpperCase();\n sourcesByUrl[url] = {\n file: page.file,\n method: page.method.toUpperCase(),\n route: page.apiPath,\n };\n }\n }\n // A renamed page leaves a forwarding address. The previous run's\n // api-sources.json keys each operation's old URL; where the same operation\n // (METHOD route) now lives at a new URL, redirects.json gains old -> new.\n // Existing entries are re-pointed through renames and dropped when their\n // old URL is a live page again.\n const sourcesPath = path.join(options.contentDir, 'api-sources.json');\n const redirectsPath = path.join(options.contentDir, 'redirects.json');\n const previousSources =\n await readJson<Record<string, { method: string; route: string }>>(sourcesPath);\n const redirects = (await readJson<Record<string, string>>(redirectsPath)) ?? {};\n if (previousSources) {\n const urlByOperation = new Map(\n Object.entries(sourcesByUrl).map(([url, s]) => [`${s.method} ${s.route}`, url]),\n );\n for (const [oldUrl, source] of Object.entries(previousSources)) {\n const newUrl = urlByOperation.get(`${source.method} ${source.route}`);\n if (newUrl !== undefined && newUrl !== oldUrl) {\n redirects[oldUrl] = newUrl;\n }\n }\n }\n // Each entry points at a live page: chains are chased to their end, cycles\n // and entries whose old URL is a live page again are dropped.\n const finalDestination = (from: string): string | null => {\n const seen = new Set([from]);\n let current = redirects[from];\n while (current !== undefined && !(current in sourcesByUrl)) {\n if (seen.has(current)) {\n return null;\n }\n seen.add(current);\n current = redirects[current];\n }\n return current ?? null;\n };\n const liveRedirects: Record<string, string> = {};\n for (const oldUrl of Object.keys(redirects)) {\n if (oldUrl in sourcesByUrl) {\n continue;\n }\n const destination = finalDestination(oldUrl);\n if (destination !== null && destination !== oldUrl) {\n liveRedirects[oldUrl] = destination;\n }\n }\n\n await writeFile(\n path.join(options.contentDir, 'api-methods.json'),\n `${JSON.stringify(methodsByUrl, null, 2)}\\n`,\n );\n await writeFile(sourcesPath, `${JSON.stringify(sourcesByUrl, null, 2)}\\n`);\n await writeFile(redirectsPath, `${JSON.stringify(liveRedirects, null, 2)}\\n`);\n\n return {\n pages,\n metas: metas.length,\n navLinks,\n methodsByUrl,\n sourcesByUrl,\n redirects: liveRedirects,\n document,\n };\n}\n\n/** A JSON file's parsed contents, or null when it does not exist or parse. */\nasync function readJson<T>(file: string): Promise<T | null> {\n try {\n return JSON.parse(await readFile(file, 'utf8')) as T;\n } catch {\n return null;\n }\n}\n"]}
package/dist/index.cjs CHANGED
@@ -1363,6 +1363,22 @@ function validateFeatures(value) {
1363
1363
  }
1364
1364
  optionalBoolean(features["copyPage"], "features.copyPage");
1365
1365
  }
1366
+ function validateNavbarAction(value, field) {
1367
+ const action = optionalObject(value, field);
1368
+ if (action === void 0) {
1369
+ return;
1370
+ }
1371
+ requireString(action["text"], `${field}.text`, "Example: 'Dashboard'.");
1372
+ requireString(action["href"], `${field}.href`, "Example: 'https://app.example.com'.");
1373
+ }
1374
+ function validateNavbar(value) {
1375
+ const navbar = optionalObject(value, "navbar");
1376
+ if (navbar === void 0) {
1377
+ return;
1378
+ }
1379
+ validateNavbarAction(navbar["primary"], "navbar.primary");
1380
+ validateNavbarAction(navbar["secondary"], "navbar.secondary");
1381
+ }
1366
1382
  function validateSeo(value) {
1367
1383
  const seo = optionalObject(value, "seo");
1368
1384
  if (seo === void 0) {
@@ -1413,6 +1429,7 @@ function validateDocsConfig(config) {
1413
1429
  validateTheme(config["theme"]);
1414
1430
  validateAuth(config["auth"]);
1415
1431
  validateProxy(config["proxy"]);
1432
+ validateNavbar(config["navbar"]);
1416
1433
  validateFeatures(config["features"]);
1417
1434
  validateSeo(config["seo"]);
1418
1435
  if (config["ai"] !== void 0) {
@@ -1460,6 +1477,90 @@ function buildThemeCss(theme) {
1460
1477
  return blocks.join("\n");
1461
1478
  }
1462
1479
 
1480
+ // src/core/redirects.ts
1481
+ function redirectTarget(url, redirects) {
1482
+ const { pathname } = url;
1483
+ const markdown = pathname.endsWith(".md");
1484
+ const page = markdown ? pathname.slice(0, -3) : pathname;
1485
+ const destination = redirects[page];
1486
+ if (destination === void 0) {
1487
+ return null;
1488
+ }
1489
+ const target = new URL(url);
1490
+ target.pathname = markdown ? `${destination}.md` : destination;
1491
+ return target;
1492
+ }
1493
+
1494
+ // src/core/llms-txt.ts
1495
+ function buildLlmsTxt(pages, options) {
1496
+ const origin = options.origin?.replace(/\/$/, "") ?? "";
1497
+ const lines = [`# ${options.name}`];
1498
+ if (options.description !== void 0 && options.description !== "") {
1499
+ lines.push("", `> ${options.description}`);
1500
+ }
1501
+ lines.push("", "## Docs", "");
1502
+ for (const page of pages) {
1503
+ const link = `[${page.title}](${origin}${page.url}.md)`;
1504
+ lines.push(
1505
+ page.description === void 0 || page.description === "" ? `- ${link}` : `- ${link}: ${page.description}`
1506
+ );
1507
+ }
1508
+ lines.push("");
1509
+ return lines.join("\n");
1510
+ }
1511
+
1512
+ // src/core/code-samples.ts
1513
+ function singleQuote(value) {
1514
+ return `'${value.replaceAll("'", "'\\''")}'`;
1515
+ }
1516
+ function isRecord(value) {
1517
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1518
+ }
1519
+ function bodyToString(body, mediaType) {
1520
+ if (typeof body === "string") {
1521
+ return body;
1522
+ }
1523
+ if (mediaType === "application/x-www-form-urlencoded" && isRecord(body)) {
1524
+ const params = new URLSearchParams();
1525
+ for (const [key, value] of Object.entries(body)) {
1526
+ if (value === void 0 || value === null) {
1527
+ continue;
1528
+ }
1529
+ const encoded = typeof value === "string" || typeof value === "number" || typeof value === "boolean" ? String(value) : JSON.stringify(value);
1530
+ params.set(key, encoded);
1531
+ }
1532
+ return params.toString();
1533
+ }
1534
+ if (mediaType === "application/x-ndjson" && Array.isArray(body)) {
1535
+ return body.map((entry) => JSON.stringify(entry)).join("\n");
1536
+ }
1537
+ return JSON.stringify(body, null, 2);
1538
+ }
1539
+ var curlCodeUsage = {
1540
+ label: "cURL",
1541
+ lang: "bash",
1542
+ generate(data) {
1543
+ const lines = [`curl --request ${data.method.toUpperCase()}`, `--url ${singleQuote(data.url)}`];
1544
+ for (const [name, param] of Object.entries(data.header ?? {})) {
1545
+ lines.push(`--header ${singleQuote(`${name}: ${param.value}`)}`);
1546
+ }
1547
+ for (const [name, param] of Object.entries(data.cookie ?? {})) {
1548
+ lines.push(`--cookie ${singleQuote(`${name}=${param.value}`)}`);
1549
+ }
1550
+ if (isRecord(data.body) && data.bodyMediaType === "multipart/form-data") {
1551
+ for (const [key, value] of Object.entries(data.body)) {
1552
+ lines.push(
1553
+ `--form ${singleQuote(`${key}=${typeof value === "string" ? value : JSON.stringify(value)}`)}`
1554
+ );
1555
+ }
1556
+ } else if (data.body !== void 0 && data.body !== null && data.bodyMediaType !== void 0) {
1557
+ lines.push(`--header ${singleQuote(`Content-Type: ${data.bodyMediaType}`)}`);
1558
+ lines.push(`--data ${singleQuote(bodyToString(data.body, data.bodyMediaType))}`);
1559
+ }
1560
+ return lines.map((line, i) => i === 0 ? line : ` ${line}`).join(" \\\n");
1561
+ }
1562
+ };
1563
+
1463
1564
  // src/core/seo.ts
1464
1565
  function trimTrailingSlash(value) {
1465
1566
  return value.replace(/\/+$/, "");
@@ -1562,6 +1663,7 @@ function firstDocFile(items) {
1562
1663
  }
1563
1664
  function planDocsTree(navigation, options = {}) {
1564
1665
  const apiDir = options.apiDir ?? "api";
1666
+ const headings = (options.sidebar ?? "collapsible") === "headings";
1565
1667
  const specs = [];
1566
1668
  const apiPages = [];
1567
1669
  const apiGroups = [];
@@ -1609,7 +1711,7 @@ function planDocsTree(navigation, options = {}) {
1609
1711
  const at = apiGroups.length;
1610
1712
  const sub = planApiItems(item.items, childDir);
1611
1713
  apiGroups.splice(at, 0, { dir: childDir, title: item.title, children: sub });
1612
- children.push({ order, folder });
1714
+ children.push({ order, folder, title: item.title });
1613
1715
  order += 1;
1614
1716
  continue;
1615
1717
  }
@@ -1647,11 +1749,11 @@ function planDocsTree(navigation, options = {}) {
1647
1749
  title: tab.title,
1648
1750
  root: true,
1649
1751
  ...tab.icon ? { icon: tab.icon } : {},
1650
- pages: planDocItems(tab.items, tabDir)
1752
+ pages: planDocItems(tab.items, tabDir, true)
1651
1753
  }
1652
1754
  });
1653
1755
  }
1654
- function planDocItems(items, dir) {
1756
+ function planDocItems(items, dir, tabLevel) {
1655
1757
  const entries = [];
1656
1758
  for (const item of items) {
1657
1759
  if (item.kind === "doc") {
@@ -1666,10 +1768,18 @@ function planDocsTree(navigation, options = {}) {
1666
1768
  continue;
1667
1769
  }
1668
1770
  if (groupDir === dir) {
1669
- entries.push(...planDocItems(item.items, dir));
1771
+ if (headings) {
1772
+ entries.push(`---${item.title}---`);
1773
+ }
1774
+ entries.push(...planDocItems(item.items, dir, false));
1775
+ continue;
1776
+ }
1777
+ const folder = groupDir.slice(dir.length + 1);
1778
+ if (headings && tabLevel) {
1779
+ entries.push(`---${item.title}---`, `...${folder}`);
1670
1780
  continue;
1671
1781
  }
1672
- entries.push(groupDir.slice(dir.length + 1));
1782
+ entries.push(folder);
1673
1783
  }
1674
1784
  return entries;
1675
1785
  }
@@ -1684,7 +1794,14 @@ function planDocsTree(navigation, options = {}) {
1684
1794
  }
1685
1795
  metas.push({
1686
1796
  path: `${groupDir}/meta.json`,
1687
- content: { title: group.title, pages: planDocItems(group.items, groupDir) }
1797
+ content: {
1798
+ title: group.title,
1799
+ // A nested group starts closed in the headings style, whatever the
1800
+ // site's defaultOpenLevel. On a tab-level group the flag is inert:
1801
+ // that folder is extracted, so no folder node renders it.
1802
+ ...headings ? { defaultOpen: false } : {},
1803
+ pages: planDocItems(group.items, groupDir, false)
1804
+ }
1688
1805
  });
1689
1806
  return groupDir;
1690
1807
  }
@@ -2117,10 +2234,67 @@ ${description ? `${description}
2117
2234
  ` : ""}${cleanBody(trimmedBody)}
2118
2235
  `;
2119
2236
  }
2237
+ function operationHead(operation) {
2238
+ let title;
2239
+ let description;
2240
+ try {
2241
+ const parsed = JSON.parse(operation.source);
2242
+ const exact = parsed.paths?.[operation.route]?.[operation.method.toLowerCase()];
2243
+ const first = Object.values(parsed.paths ?? {}).flatMap((item) => Object.values(item))[0];
2244
+ const named = exact ?? first;
2245
+ title = named?.summary ?? parsed.info?.title;
2246
+ description = named?.description ?? parsed.info?.description;
2247
+ } catch {
2248
+ }
2249
+ const head = [`# ${title ?? "API operation"}`];
2250
+ if (description !== void 0 && description !== "") {
2251
+ head.push("", `> ${description.replace(/\n/g, "\n> ")}`);
2252
+ }
2253
+ return head.join("\n");
2254
+ }
2255
+ function fenceFor(source) {
2256
+ const longest = source.match(/`+/g)?.reduce((max, run) => Math.max(max, run.length), 0) ?? 0;
2257
+ return "`".repeat(Math.max(4, longest + 1));
2258
+ }
2120
2259
  function createMarkdownRoute(options) {
2260
+ const requestOrigin = (request) => {
2261
+ const url = new URL(request.url);
2262
+ const proto = request.headers.get("x-forwarded-proto") ?? url.protocol.replace(/:$/, "");
2263
+ const host = request.headers.get("x-forwarded-host") ?? request.headers.get("host") ?? url.host;
2264
+ return `${proto}://${host}`;
2265
+ };
2266
+ const respond = (request, markdown) => {
2267
+ const preamble = options.llmsTxtPath === void 0 ? "" : [
2268
+ "> ## Documentation Index",
2269
+ `> Fetch the complete documentation index at: ${requestOrigin(request)}${options.llmsTxtPath}`,
2270
+ "> Use this file to discover all available pages before exploring further.",
2271
+ "",
2272
+ ""
2273
+ ].join("\n");
2274
+ return new Response(`${preamble}${markdown}`, {
2275
+ headers: { "content-type": "text/plain; charset=utf-8" }
2276
+ });
2277
+ };
2121
2278
  return {
2122
- async GET(_request, context) {
2279
+ async GET(request, context) {
2123
2280
  const { slug } = await context.params;
2281
+ const operation = await options.loadOperationSource?.(slug);
2282
+ if (operation) {
2283
+ const fence = fenceFor(operation.source);
2284
+ return respond(
2285
+ request,
2286
+ [
2287
+ operationHead(operation),
2288
+ "",
2289
+ "## OpenAPI",
2290
+ "",
2291
+ `${fence}json ${operation.file} ${operation.method.toLowerCase()} ${operation.route}`,
2292
+ operation.source.trimEnd(),
2293
+ fence,
2294
+ ""
2295
+ ].join("\n")
2296
+ );
2297
+ }
2124
2298
  const raw = await options.read(slug);
2125
2299
  if (raw === null) {
2126
2300
  return new Response("Not found", { status: 404 });
@@ -2134,12 +2308,32 @@ function createMarkdownRoute(options) {
2134
2308
  if (markdown === null) {
2135
2309
  return new Response("Not found", { status: 404 });
2136
2310
  }
2137
- return new Response(markdown, {
2138
- headers: { "content-type": "text/plain; charset=utf-8" }
2139
- });
2311
+ return respond(request, markdown);
2140
2312
  }
2141
2313
  };
2142
2314
  }
2315
+ function markdownRewriteTarget(url, options = {}) {
2316
+ const baseUrl = (options.baseUrl ?? "/docs").replace(/\/+$/, "");
2317
+ const markdownBase = (options.markdownBase ?? "/md").replace(/\/+$/, "");
2318
+ const { pathname } = url;
2319
+ if (!pathname.endsWith(".md") || pathname.includes("%")) {
2320
+ return null;
2321
+ }
2322
+ const page = pathname.slice(0, -3);
2323
+ if (page !== baseUrl && !page.startsWith(`${baseUrl}/`)) {
2324
+ return null;
2325
+ }
2326
+ if (page.endsWith("/") || page.includes("//")) {
2327
+ return null;
2328
+ }
2329
+ const target = new URL(url);
2330
+ target.pathname = `${markdownBase}${page.slice(baseUrl.length)}`;
2331
+ return target;
2332
+ }
2333
+ function isMarkdownRouteRequest(url, options = {}) {
2334
+ const markdownBase = options.markdownBase ?? "/md";
2335
+ return url.pathname === markdownBase || url.pathname.startsWith(`${markdownBase}/`);
2336
+ }
2143
2337
 
2144
2338
  // src/core/auth/domains.ts
2145
2339
  function isAllowedEmail(email, allowedDomains) {
@@ -2923,7 +3117,7 @@ var InvalidNavigationError = class extends Error {
2923
3117
  this.name = "InvalidNavigationError";
2924
3118
  }
2925
3119
  };
2926
- function isRecord(value) {
3120
+ function isRecord2(value) {
2927
3121
  return typeof value === "object" && value !== null && !Array.isArray(value);
2928
3122
  }
2929
3123
  function parsePageString(entry) {
@@ -2946,7 +3140,7 @@ function parseItem(entry, path) {
2946
3140
  if (typeof entry === "string") {
2947
3141
  return parsePageString(entry);
2948
3142
  }
2949
- if (isRecord(entry) && typeof entry["group"] === "string") {
3143
+ if (isRecord2(entry) && typeof entry["group"] === "string") {
2950
3144
  return parseGroup(entry, path);
2951
3145
  }
2952
3146
  throw new InvalidNavigationError(
@@ -2971,7 +3165,7 @@ function parseGroup(group, path) {
2971
3165
  };
2972
3166
  }
2973
3167
  function parseTab(tab, path) {
2974
- if (!isRecord(tab) || typeof tab["tab"] !== "string") {
3168
+ if (!isRecord2(tab) || typeof tab["tab"] !== "string") {
2975
3169
  throw new InvalidNavigationError(`Tab at ${path} is missing a string "tab" title.`);
2976
3170
  }
2977
3171
  const items = tab["groups"] !== void 0 ? parseGroups(tab["groups"], `${path}.groups`) : parseItems(tab["pages"], `${path}.pages`);
@@ -2989,18 +3183,18 @@ function parseGroups(groups, path) {
2989
3183
  }
2990
3184
  return groups.map((group, index) => {
2991
3185
  const groupPath = `${path}[${String(index)}]`;
2992
- if (!isRecord(group)) {
3186
+ if (!isRecord2(group)) {
2993
3187
  throw new InvalidNavigationError(`Group at ${groupPath} must be an object.`);
2994
3188
  }
2995
3189
  return parseGroup(group, groupPath);
2996
3190
  });
2997
3191
  }
2998
3192
  function parseNavigation(docsJson) {
2999
- if (!isRecord(docsJson)) {
3193
+ if (!isRecord2(docsJson)) {
3000
3194
  throw new InvalidNavigationError("docs.json must be an object.");
3001
3195
  }
3002
3196
  const navigation = docsJson["navigation"];
3003
- if (!isRecord(navigation)) {
3197
+ if (!isRecord2(navigation)) {
3004
3198
  throw new InvalidNavigationError('docs.json must have a "navigation" object.');
3005
3199
  }
3006
3200
  const tabs = navigation["tabs"];
@@ -3017,7 +3211,7 @@ var OpenApiMergeError = class extends Error {
3017
3211
  this.name = "OpenApiMergeError";
3018
3212
  }
3019
3213
  };
3020
- function isRecord2(value) {
3214
+ function isRecord3(value) {
3021
3215
  return typeof value === "object" && value !== null && !Array.isArray(value);
3022
3216
  }
3023
3217
  function collectServers(inputs, override) {
@@ -3031,7 +3225,7 @@ function collectServers(inputs, override) {
3031
3225
  continue;
3032
3226
  }
3033
3227
  for (const server of servers) {
3034
- if (isRecord2(server) && typeof server["url"] === "string" && !byUrl.has(server["url"])) {
3228
+ if (isRecord3(server) && typeof server["url"] === "string" && !byUrl.has(server["url"])) {
3035
3229
  byUrl.set(server["url"], server);
3036
3230
  }
3037
3231
  }
@@ -3046,7 +3240,7 @@ function collectTags(inputs) {
3046
3240
  continue;
3047
3241
  }
3048
3242
  for (const tag of tags) {
3049
- if (isRecord2(tag) && typeof tag["name"] === "string" && !byName.has(tag["name"])) {
3243
+ if (isRecord3(tag) && typeof tag["name"] === "string" && !byName.has(tag["name"])) {
3050
3244
  byName.set(tag["name"], tag);
3051
3245
  }
3052
3246
  }
@@ -3057,11 +3251,11 @@ function mergeComponents(inputs) {
3057
3251
  const merged = {};
3058
3252
  for (const doc of inputs) {
3059
3253
  const components = doc["components"];
3060
- if (!isRecord2(components)) {
3254
+ if (!isRecord3(components)) {
3061
3255
  continue;
3062
3256
  }
3063
3257
  for (const [section, value] of Object.entries(components)) {
3064
- if (!isRecord2(value)) {
3258
+ if (!isRecord3(value)) {
3065
3259
  continue;
3066
3260
  }
3067
3261
  const target = merged[section] ?? (merged[section] = {});
@@ -3079,12 +3273,12 @@ function assemblePaths(inputs) {
3079
3273
  const paths = {};
3080
3274
  for (const doc of inputs) {
3081
3275
  const docPaths = doc["paths"];
3082
- if (!isRecord2(docPaths)) {
3276
+ if (!isRecord3(docPaths)) {
3083
3277
  throw new OpenApiMergeError('Each OpenAPI input must have a "paths" object.');
3084
3278
  }
3085
3279
  const docSecurity = doc["security"];
3086
3280
  for (const [route, pathItem] of Object.entries(docPaths)) {
3087
- if (!isRecord2(pathItem)) {
3281
+ if (!isRecord3(pathItem)) {
3088
3282
  throw new OpenApiMergeError(`Path item for "${route}" must be an object.`);
3089
3283
  }
3090
3284
  const target = paths[route] ?? (paths[route] = {});
@@ -3095,7 +3289,7 @@ function assemblePaths(inputs) {
3095
3289
  );
3096
3290
  }
3097
3291
  let resolved = operation;
3098
- if (HTTP_METHODS2.has(method.toLowerCase()) && isRecord2(operation) && !("security" in operation) && Array.isArray(docSecurity)) {
3292
+ if (HTTP_METHODS2.has(method.toLowerCase()) && isRecord3(operation) && !("security" in operation) && Array.isArray(docSecurity)) {
3099
3293
  resolved = { ...operation, security: docSecurity };
3100
3294
  }
3101
3295
  target[method] = resolved;
@@ -3106,7 +3300,7 @@ function assemblePaths(inputs) {
3106
3300
  }
3107
3301
  function mergeOpenApiDocuments(inputs, options) {
3108
3302
  const records = inputs.map((input, index) => {
3109
- if (!isRecord2(input)) {
3303
+ if (!isRecord3(input)) {
3110
3304
  throw new OpenApiMergeError(`OpenAPI input at index ${String(index)} must be an object.`);
3111
3305
  }
3112
3306
  return input;
@@ -3130,6 +3324,7 @@ exports.OpenApiMergeError = OpenApiMergeError;
3130
3324
  exports.ProxyTargetError = ProxyTargetError;
3131
3325
  exports.SESSION_COOKIE = SESSION_COOKIE;
3132
3326
  exports.assertAllowedTarget = assertAllowedTarget;
3327
+ exports.buildLlmsTxt = buildLlmsTxt;
3133
3328
  exports.buildMetadata = buildMetadata;
3134
3329
  exports.buildRobots = buildRobots;
3135
3330
  exports.buildSearchIndex = buildSearchIndex;
@@ -3147,14 +3342,18 @@ exports.createSignOut = createSignOut;
3147
3342
  exports.createSiteAuth = createSiteAuth;
3148
3343
  exports.createTryItProxy = createTryItProxy;
3149
3344
  exports.createTryItProxyRoute = createTryItProxyRoute;
3345
+ exports.curlCodeUsage = curlCodeUsage;
3150
3346
  exports.defineDocsConfig = defineDocsConfig;
3151
3347
  exports.isAllowedEmail = isAllowedEmail;
3348
+ exports.isMarkdownRouteRequest = isMarkdownRouteRequest;
3349
+ exports.markdownRewriteTarget = markdownRewriteTarget;
3152
3350
  exports.mdxToMarkdown = mdxToMarkdown;
3153
3351
  exports.mergeOpenApiDocuments = mergeOpenApiDocuments;
3154
3352
  exports.openApiOperationToMarkdown = openApiOperationToMarkdown;
3155
3353
  exports.parseNavigation = parseNavigation;
3156
3354
  exports.planDocsTree = planDocsTree;
3157
3355
  exports.readCookie = readCookie;
3356
+ exports.redirectTarget = redirectTarget;
3158
3357
  exports.resolveAiConfig = resolveAiConfig;
3159
3358
  exports.searchIndex = searchIndex;
3160
3359
  exports.signSession = signSession;