@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.
- package/dist/{chunk-ZWDLAW5M.js → chunk-2GUUVNTP.js} +222 -28
- package/dist/chunk-2GUUVNTP.js.map +1 -0
- package/dist/{chunk-ZKOOKLZ3.js → chunk-6H6OB7YP.js} +17 -2
- package/dist/chunk-6H6OB7YP.js.map +1 -0
- package/dist/generator.cjs +161 -18
- package/dist/generator.cjs.map +1 -1
- package/dist/generator.d.cts +24 -0
- package/dist/generator.d.ts +24 -0
- package/dist/generator.js +140 -13
- package/dist/generator.js.map +1 -1
- package/dist/index.cjs +224 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +249 -4
- package/dist/index.d.ts +249 -4
- package/dist/index.js +1 -1
- package/dist/{navigation-CGqFIPlP.d.cts → navigation-DvgyWSZ4.d.cts} +22 -1
- package/dist/{navigation-CGqFIPlP.d.ts → navigation-DvgyWSZ4.d.ts} +22 -1
- package/dist/ui/api-try-it.cjs +15 -0
- package/dist/ui/api-try-it.cjs.map +1 -1
- package/dist/ui/api-try-it.js +1 -1
- package/dist/ui/ask-ai.cjs +26 -6
- package/dist/ui/ask-ai.cjs.map +1 -1
- package/dist/ui/ask-ai.d.cts +6 -1
- package/dist/ui/ask-ai.d.ts +6 -1
- package/dist/ui/ask-ai.js +26 -6
- package/dist/ui/ask-ai.js.map +1 -1
- package/dist/ui/copy-page.cjs +15 -0
- package/dist/ui/copy-page.cjs.map +1 -1
- package/dist/ui/copy-page.js +1 -1
- package/dist/ui.cjs +77 -6
- package/dist/ui.cjs.map +1 -1
- package/dist/ui.d.cts +99 -8
- package/dist/ui.d.ts +99 -8
- package/dist/ui.js +62 -8
- package/dist/ui.js.map +1 -1
- package/package.json +1 -1
- package/skills/SKILL.md +68 -27
- package/styles/api-reference.css +45 -2
- package/styles/ask-ai.css +28 -0
- package/styles/docs.css +96 -1
- package/styles/home.css +34 -1
- package/dist/chunk-ZKOOKLZ3.js.map +0 -1
- package/dist/chunk-ZWDLAW5M.js.map +0 -1
package/dist/generator.js.map
CHANGED
|
@@ -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
|
-
|
|
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(
|
|
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: {
|
|
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(
|
|
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
|
|
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
|
|
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 (
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 (
|
|
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 (
|
|
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 (!
|
|
3254
|
+
if (!isRecord3(components)) {
|
|
3061
3255
|
continue;
|
|
3062
3256
|
}
|
|
3063
3257
|
for (const [section, value] of Object.entries(components)) {
|
|
3064
|
-
if (!
|
|
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 (!
|
|
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 (!
|
|
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()) &&
|
|
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 (!
|
|
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;
|