blume 2.1.2 → 2.2.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/AGENTS.md +1 -1
- package/CHANGELOG.md +133 -0
- package/README.md +4 -4
- package/dist/cli/{chunk-74wfhv79.js → chunk-05f21r8h.js} +1 -1
- package/dist/cli/{chunk-9qzgg6mj.js → chunk-1cm0h819.js} +3 -3
- package/dist/cli/{chunk-nbyvgnnm.js → chunk-1rkxzg3n.js} +104 -47
- package/dist/cli/chunk-1rkxzg3n.js.map +36 -0
- package/dist/cli/{chunk-desvm72z.js → chunk-2nr29y95.js} +22 -26
- package/dist/cli/chunk-2nr29y95.js.map +13 -0
- package/dist/cli/{chunk-32khwcb6.js → chunk-49r370mb.js} +42 -69
- package/dist/cli/{chunk-32khwcb6.js.map → chunk-49r370mb.js.map} +9 -10
- package/dist/cli/{chunk-ms9z66nz.js → chunk-4rftmttv.js} +2 -2
- package/dist/cli/{chunk-s0bdnc7v.js → chunk-79bzzkx4.js} +5 -4
- package/dist/cli/chunk-79bzzkx4.js.map +10 -0
- package/dist/cli/{chunk-hvcxng5f.js → chunk-7tekknev.js} +6 -6
- package/dist/cli/{chunk-dv2z0rzf.js → chunk-8b489v1b.js} +38 -13
- package/dist/cli/{chunk-dv2z0rzf.js.map → chunk-8b489v1b.js.map} +5 -4
- package/dist/cli/{chunk-0pmb4tr4.js → chunk-8f8xw4x2.js} +1195 -222
- package/dist/cli/chunk-8f8xw4x2.js.map +44 -0
- package/dist/cli/chunk-96snx8yy.js +329 -0
- package/dist/cli/chunk-96snx8yy.js.map +13 -0
- package/dist/cli/{chunk-ckrcm08h.js → chunk-a3ydrqny.js} +48 -62
- package/dist/cli/{chunk-ckrcm08h.js.map → chunk-a3ydrqny.js.map} +4 -5
- package/dist/cli/{chunk-bxwhtvw8.js → chunk-ahc5gc9h.js} +5 -5
- package/dist/cli/{chunk-dpy8qdjf.js → chunk-as09pafp.js} +73 -182
- package/dist/cli/chunk-as09pafp.js.map +18 -0
- package/dist/cli/{chunk-tfycddbt.js → chunk-bjk964fp.js} +2 -2
- package/dist/cli/{chunk-gfc5fqvf.js → chunk-br0n8n3e.js} +18 -18
- package/dist/cli/{chunk-gfc5fqvf.js.map → chunk-br0n8n3e.js.map} +1 -1
- package/dist/cli/{chunk-297ajdcv.js → chunk-c69tw29j.js} +6 -6
- package/dist/cli/{chunk-297ajdcv.js.map → chunk-c69tw29j.js.map} +3 -3
- package/dist/cli/chunk-cn3dy3hz.js +146 -0
- package/dist/cli/chunk-cn3dy3hz.js.map +11 -0
- package/dist/cli/{chunk-124fvmzq.js → chunk-dm4mg9bj.js} +11064 -8742
- package/dist/cli/{chunk-124fvmzq.js.map → chunk-dm4mg9bj.js.map} +78 -64
- package/dist/cli/{chunk-dqhg54mr.js → chunk-dmx6cezx.js} +1 -1
- package/dist/cli/{chunk-60hz265r.js → chunk-dpcahcrb.js} +24 -11
- package/dist/cli/chunk-dpcahcrb.js.map +10 -0
- package/dist/cli/{chunk-a57yh0h9.js → chunk-dyk3r7jw.js} +7 -7
- package/dist/cli/{chunk-a57yh0h9.js.map → chunk-dyk3r7jw.js.map} +1 -1
- package/dist/cli/{chunk-twy7psdz.js → chunk-enk76qd5.js} +14 -10
- package/dist/cli/chunk-enk76qd5.js.map +10 -0
- package/dist/cli/{chunk-k3gr1nm9.js → chunk-fqy4n3q2.js} +13 -16
- package/dist/cli/{chunk-k3gr1nm9.js.map → chunk-fqy4n3q2.js.map} +3 -3
- package/dist/cli/{chunk-sc93apfs.js → chunk-frk2wdq1.js} +17 -17
- package/dist/cli/{chunk-sc93apfs.js.map → chunk-frk2wdq1.js.map} +2 -2
- package/dist/cli/{chunk-kdpzw38a.js → chunk-gprwej7h.js} +95 -7
- package/dist/cli/chunk-gprwej7h.js.map +11 -0
- package/dist/cli/chunk-kkrteb15.js +157 -0
- package/dist/cli/chunk-kkrteb15.js.map +10 -0
- package/dist/cli/{chunk-ave1wcbr.js → chunk-m9bp2hc1.js} +1 -1
- package/dist/cli/{chunk-58bt3hag.js → chunk-mrvh4c88.js} +4 -4
- package/dist/cli/{chunk-5cmts6dv.js → chunk-p8zg3a2h.js} +7 -7
- package/dist/cli/{chunk-5cmts6dv.js.map → chunk-p8zg3a2h.js.map} +1 -1
- package/dist/cli/{chunk-0w6hs479.js → chunk-rkqae7kh.js} +30 -18
- package/dist/cli/chunk-rkqae7kh.js.map +10 -0
- package/dist/cli/{chunk-bqxaqn5p.js → chunk-t5xx74rd.js} +8 -8
- package/dist/cli/{chunk-bqxaqn5p.js.map → chunk-t5xx74rd.js.map} +1 -1
- package/dist/cli/{chunk-1f882900.js → chunk-th0pgaj5.js} +2 -2
- package/dist/cli/{chunk-dhwtqnnd.js → chunk-vnbvhgmd.js} +2 -2
- package/dist/cli/{chunk-gxtg6ed2.js → chunk-vzy9rh6y.js} +21 -21
- package/dist/cli/{chunk-gxtg6ed2.js.map → chunk-vzy9rh6y.js.map} +3 -3
- package/dist/cli/{chunk-j1x92mx9.js → chunk-w6e455pz.js} +1 -1
- package/dist/cli/chunk-wehv26ry.js +155 -0
- package/dist/cli/chunk-wehv26ry.js.map +11 -0
- package/dist/cli/{chunk-y4msrwm9.js → chunk-xgq4eytj.js} +15 -15
- package/dist/cli/{chunk-36bxs9c7.js → chunk-xt0qvz1t.js} +27 -23
- package/dist/cli/chunk-xt0qvz1t.js.map +10 -0
- package/dist/cli/index.js +78 -42
- package/dist/cli/index.js.map +7 -6
- package/dist/types/ai/changelog-components.d.ts +18 -0
- package/dist/types/ai/changelog-markdown.d.ts +12 -0
- package/dist/types/ai/openapi-components.d.ts +7 -7
- package/dist/types/ai/operation-details.d.ts +39 -0
- package/dist/types/ai/relative-links.d.ts +3 -1
- package/dist/types/ai/serializers.d.ts +6 -4
- package/dist/types/analytics/head.d.ts +4 -2
- package/dist/types/analytics/script.d.ts +8 -2
- package/dist/types/components/content/youtube.d.ts +20 -0
- package/dist/types/components/layout/nav-utils.d.ts +8 -0
- package/dist/types/components/openapi/example-panels.d.ts +33 -0
- package/dist/types/components/openapi/helpers.d.ts +53 -11
- package/dist/types/components/openapi/snippets.d.ts +7 -1
- package/dist/types/core/alert-diagnostics.d.ts +10 -0
- package/dist/types/core/builtin-tags.d.ts +16 -0
- package/dist/types/core/changelog-list.d.ts +48 -0
- package/dist/types/core/closest-match.d.ts +6 -0
- package/dist/types/core/code-fence-diagnostics.d.ts +18 -0
- package/dist/types/core/code-fences.d.ts +8 -1
- package/dist/types/core/config-input.d.ts +1 -1
- package/dist/types/core/content-assets.d.ts +16 -18
- package/dist/types/core/diagnostics.d.ts +14 -0
- package/dist/types/core/directive-diagnostics.d.ts +22 -8
- package/dist/types/core/former-routes.d.ts +14 -0
- package/dist/types/core/github.d.ts +5 -4
- package/dist/types/core/heading-markers.d.ts +65 -2
- package/dist/types/core/hidden-pages.d.ts +3 -0
- package/dist/types/core/includes.d.ts +23 -0
- package/dist/types/core/last-modified.d.ts +18 -6
- package/dist/types/core/links.d.ts +42 -2
- package/dist/types/core/load-module.d.ts +19 -4
- package/dist/types/core/manifest.d.ts +14 -1
- package/dist/types/core/markdown-mirrors.d.ts +22 -0
- package/dist/types/core/mdx-elements.d.ts +32 -0
- package/dist/types/core/mdx-syntax.d.ts +100 -0
- package/dist/types/core/meta-diagnostics.d.ts +7 -0
- package/dist/types/core/meta.d.ts +32 -5
- package/dist/types/core/nav-diagnostics.d.ts +9 -2
- package/dist/types/core/navigation.d.ts +13 -0
- package/dist/types/core/ordering-prefix.d.ts +6 -0
- package/dist/types/core/pinned-ids.d.ts +23 -0
- package/dist/types/core/project-graph.d.ts +9 -1
- package/dist/types/core/redirect-patterns.d.ts +18 -0
- package/dist/types/core/relative-files.d.ts +31 -0
- package/dist/types/core/schema.d.ts +267 -7
- package/dist/types/core/sources/normalize.d.ts +101 -9
- package/dist/types/core/sources/types.d.ts +16 -0
- package/dist/types/core/static-files.d.ts +10 -0
- package/dist/types/core/static-host.d.ts +42 -0
- package/dist/types/core/svg-images.d.ts +26 -0
- package/dist/types/core/syntax-diagnostics.d.ts +14 -0
- package/dist/types/core/types.d.ts +42 -1
- package/dist/types/deploy/generated-files.d.ts +41 -0
- package/dist/types/deploy/redirects.d.ts +21 -7
- package/dist/types/markdown/code-title.d.ts +9 -5
- package/dist/types/markdown/directives.d.ts +8 -0
- package/dist/types/markdown/fence-language.d.ts +51 -0
- package/dist/types/markdown/fence-meta.d.ts +5 -0
- package/dist/types/markdown/github-alerts.d.ts +35 -0
- package/dist/types/openapi/asyncapi.d.ts +6 -5
- package/dist/types/openapi/checks.d.ts +10 -5
- package/dist/types/openapi/model.d.ts +55 -8
- package/dist/types/openapi/parse.d.ts +4 -1
- package/dist/types/sources/filesystem.d.ts +14 -4
- package/dist/types/sources/mdx-remote.d.ts +6 -3
- package/dist/types/sources/registry.d.ts +3 -3
- package/dist/types/theme/color.d.ts +33 -0
- package/dist/types/theme/palette.d.ts +122 -0
- package/docs/01-quickstart.mdx +1 -1
- package/docs/02-deployment.mdx +23 -8
- package/docs/04-migrating.mdx +16 -4
- package/docs/advanced/changelog.mdx +27 -1
- package/docs/advanced/skills.mdx +1 -1
- package/docs/cli/audit.mdx +1 -1
- package/docs/cli/doctor.mdx +2 -2
- package/docs/cli/evals.mdx +1 -1
- package/docs/cli/index.mdx +4 -2
- package/docs/cli/validate.mdx +10 -7
- package/docs/configuration/analytics.mdx +2 -0
- package/docs/configuration/customization.mdx +3 -1
- package/docs/configuration/index.mdx +2 -2
- package/docs/configuration/theming.mdx +5 -1
- package/docs/content/components.mdx +26 -6
- package/docs/content/frontmatter.mdx +1 -1
- package/docs/content/includes.mdx +6 -2
- package/docs/content/index.mdx +11 -1
- package/docs/content/islands.mdx +2 -0
- package/docs/content/meta.mdx +9 -3
- package/docs/content/navigation.mdx +14 -6
- package/docs/content/sources/obsidian.mdx +1 -1
- package/docs/content/sources/remote-mdx.mdx +3 -1
- package/docs/content/syntax.mdx +76 -8
- package/docs/content/variables.mdx +3 -1
- package/docs/discoverability/agent-discovery.mdx +1 -1
- package/docs/discoverability/json-api.mdx +1 -1
- package/docs/discoverability/llms-txt.mdx +1 -1
- package/docs/discoverability/markdown.mdx +2 -2
- package/docs/discoverability/open-graph.mdx +1 -1
- package/docs/discoverability/sitemap-and-robots.mdx +1 -1
- package/docs/references/graphql.mdx +2 -0
- package/docs/references/openapi.mdx +45 -7
- package/package.json +10 -9
- package/skills/blume/SKILL.md +2 -2
- package/skills/blume-migrate/SKILL.md +49 -27
- package/skills/blume-migrate/references/docsify.md +262 -0
- package/skills/blume-migrate/references/docus.md +298 -0
- package/skills/blume-migrate/references/docusaurus.md +3 -3
- package/skills/blume-migrate/references/fern.md +327 -0
- package/skills/blume-migrate/references/fumadocs.md +1 -1
- package/skills/blume-migrate/references/gitbook.md +267 -0
- package/skills/blume-migrate/references/github-wiki.md +259 -0
- package/skills/blume-migrate/references/jekyll.md +253 -0
- package/skills/blume-migrate/references/mdbook.md +256 -0
- package/skills/blume-migrate/references/mintlify.md +5 -5
- package/skills/blume-migrate/references/mkdocs.md +236 -0
- package/skills/blume-migrate/references/monorepo.md +20 -8
- package/skills/blume-migrate/references/nextra.md +2 -2
- package/skills/blume-migrate/references/readme.md +257 -0
- package/skills/blume-migrate/references/redocly.md +198 -0
- package/skills/blume-migrate/references/starlight.md +3 -3
- package/skills/blume-migrate/references/vitepress.md +204 -0
- package/skills/blume-migrate/references/vuepress.md +271 -0
- package/skills/blume-migrate/scripts/docsify-codemod.mjs +2387 -0
- package/skills/blume-migrate/scripts/docus-codemod.mjs +2542 -0
- package/skills/blume-migrate/scripts/fern-codemod.mjs +2633 -0
- package/skills/blume-migrate/scripts/github-wiki-codemod.mjs +2808 -0
- package/skills/blume-migrate/scripts/include-excerpts.mjs +649 -0
- package/skills/blume-migrate/scripts/jekyll-codemod.mjs +3649 -0
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +17 -13
- package/skills/blume-migrate/scripts/mkdocs-codemod.mjs +2526 -0
- package/skills/blume-migrate/scripts/operation-routes.mjs +508 -0
- package/skills/blume-migrate/scripts/pin-heading-ids.mjs +1049 -0
- package/skills/blume-migrate/scripts/readme-codemod.mjs +4139 -0
- package/skills/blume-migrate/scripts/vitepress-codemod.mjs +1511 -0
- package/src/ai/changelog-components.ts +24 -0
- package/src/ai/changelog-markdown.ts +45 -41
- package/src/ai/component-markdown.ts +22 -9
- package/src/ai/llms.ts +36 -5
- package/src/ai/markdown.ts +2 -2
- package/src/ai/openapi-components.ts +25 -9
- package/src/ai/operation-details.ts +208 -0
- package/src/ai/relative-links.ts +48 -8
- package/src/ai/serializers.ts +7 -6
- package/src/ai/site-skill.ts +6 -2
- package/src/analytics/head.ts +11 -5
- package/src/analytics/script.ts +13 -3
- package/src/astro/generate.ts +32 -6
- package/src/astro/index.ts +1 -0
- package/src/astro/integration.ts +27 -1
- package/src/astro/mdx-source-errors.ts +46 -0
- package/src/astro/templates.ts +89 -185
- package/src/audit/checks/redirects.ts +5 -1
- package/src/audit/redirects.ts +36 -19
- package/src/audit/report.ts +22 -9
- package/src/audit/run.ts +12 -28
- package/src/audit/terms.ts +23 -0
- package/src/audit/url.ts +2 -2
- package/src/cli/build-failure.ts +256 -7
- package/src/cli/command-meta.ts +1 -1
- package/src/cli/commands/audit.ts +1 -1
- package/src/cli/commands/build.ts +12 -3
- package/src/cli/commands/check.ts +13 -2
- package/src/cli/commands/dev.ts +1 -1
- package/src/cli/commands/doctor.ts +4 -2
- package/src/cli/commands/init.ts +3 -3
- package/src/cli/commands/preview.ts +17 -1
- package/src/cli/commands/validate.ts +22 -10
- package/src/cli/prepare.ts +1 -1
- package/src/cli/preview-pages.ts +161 -0
- package/src/cli/unknown-flags.ts +1 -42
- package/src/components/code-copy.ts +51 -0
- package/src/components/code-language.ts +75 -0
- package/src/components/content/AccordionItem.astro +16 -1
- package/src/components/content/Callout.astro +11 -1
- package/src/components/content/Changelog.astro +189 -0
- package/src/components/content/CodeBlock.astro +8 -11
- package/src/components/content/Tabs.astro +16 -2
- package/src/components/content/YouTube.astro +6 -4
- package/src/components/content/code-block.ts +33 -0
- package/src/components/content/content-strings.ts +5 -1
- package/src/components/content/inline-markdown.ts +35 -8
- package/src/components/content/youtube.ts +49 -7
- package/src/components/layout/Analytics.astro +5 -1
- package/src/components/layout/NavTree.astro +8 -3
- package/src/components/layout/RootLayout.astro +18 -31
- package/src/components/layout/nav-utils.ts +14 -0
- package/src/components/layout/search/pagefind.ts +2 -1
- package/src/components/openapi/Operation.astro +4 -0
- package/src/components/openapi/PanelTabs.astro +9 -3
- package/src/components/openapi/RequestBody.astro +42 -8
- package/src/components/openapi/RequestPanel.astro +34 -53
- package/src/components/openapi/example-panels.ts +110 -0
- package/src/components/openapi/helpers.ts +195 -20
- package/src/components/openapi/operation-model.ts +63 -26
- package/src/components/openapi/request.ts +13 -0
- package/src/components/openapi/snippets.ts +14 -2
- package/src/core/alert-diagnostics.ts +89 -0
- package/src/core/builtin-tags.ts +76 -0
- package/src/core/changelog-list.ts +87 -0
- package/src/core/closest-match.ts +41 -0
- package/src/core/code-fence-diagnostics.ts +267 -0
- package/src/core/code-fences.ts +43 -7
- package/src/core/component-diagnostics.ts +65 -12
- package/src/core/config-input.ts +1 -1
- package/src/core/config.ts +46 -1
- package/src/core/content-assets.ts +64 -144
- package/src/core/diagnostics.ts +36 -3
- package/src/core/directive-diagnostics.ts +265 -47
- package/src/core/former-routes.ts +46 -0
- package/src/core/git-ignored.ts +53 -0
- package/src/core/github.ts +5 -4
- package/src/core/heading-markers.ts +156 -2
- package/src/core/hidden-pages.ts +64 -0
- package/src/core/includes.ts +181 -41
- package/src/core/last-modified.ts +47 -10
- package/src/core/links.ts +438 -83
- package/src/core/load-module.ts +98 -9
- package/src/core/manifest.ts +12 -9
- package/src/core/markdown-mirrors.ts +52 -0
- package/src/core/mdx-elements.ts +153 -0
- package/src/core/mdx-syntax.ts +377 -0
- package/src/core/meta-diagnostics.ts +134 -0
- package/src/core/meta.ts +154 -55
- package/src/core/nav-diagnostics.ts +16 -4
- package/src/core/navigation.ts +70 -10
- package/src/core/ordering-prefix.ts +22 -2
- package/src/core/pinned-ids.ts +141 -0
- package/src/core/project-graph.ts +221 -29
- package/src/core/redirect-patterns.ts +41 -0
- package/src/core/relative-files.ts +145 -0
- package/src/core/schema.ts +17 -3
- package/src/core/sources/mdx-remote.ts +75 -14
- package/src/core/sources/normalize.ts +885 -179
- package/src/core/sources/notion.ts +5 -4
- package/src/core/sources/obsidian.ts +2 -5
- package/src/core/sources/types.ts +17 -0
- package/src/core/static-files.ts +40 -0
- package/src/core/static-host.ts +125 -0
- package/src/core/svg-images.ts +98 -0
- package/src/core/syntax-diagnostics.ts +491 -0
- package/src/core/types.ts +43 -1
- package/src/deploy/artifacts.ts +2 -28
- package/src/deploy/generated-files.ts +128 -0
- package/src/deploy/platforms/vercel.ts +25 -1
- package/src/deploy/redirects.ts +43 -16
- package/src/deploy/sitemap.ts +2 -1
- package/src/markdown/base-links.ts +87 -6
- package/src/markdown/code-title.ts +29 -13
- package/src/markdown/content-assets.ts +210 -0
- package/src/markdown/directives.ts +69 -5
- package/src/markdown/fence-language.ts +106 -0
- package/src/markdown/fence-meta.ts +42 -0
- package/src/markdown/github-alerts.ts +172 -0
- package/src/markdown/heading-anchors.ts +265 -56
- package/src/markdown/include.ts +30 -3
- package/src/markdown/index.ts +145 -40
- package/src/markdown/prompt-text.ts +95 -0
- package/src/markdown/relative-links.ts +37 -2
- package/src/markdown/route-snapshot.ts +22 -12
- package/src/markdown/svg-fallback.ts +51 -0
- package/src/markdown/title-heading.ts +134 -0
- package/src/migrate/migrate.ts +127 -2
- package/src/og/card.ts +14 -4
- package/src/openapi/asyncapi.ts +14 -6
- package/src/openapi/checks.ts +160 -15
- package/src/openapi/model.ts +172 -33
- package/src/openapi/parse.ts +13 -4
- package/src/openapi/render-mdx.ts +55 -9
- package/src/openapi/source.ts +87 -6
- package/src/registry/eject.ts +0 -1
- package/src/search/documents.ts +10 -2
- package/src/sources/filesystem.ts +28 -3
- package/src/sources/mdx-remote.ts +7 -15
- package/src/theme/color.ts +14 -0
- package/src/theme/entry.ts +22 -1
- package/src/theme/palette.ts +74 -1
- package/dist/cli/chunk-0pmb4tr4.js.map +0 -35
- package/dist/cli/chunk-0w6hs479.js.map +0 -10
- package/dist/cli/chunk-150jk7km.js +0 -52
- package/dist/cli/chunk-150jk7km.js.map +0 -10
- package/dist/cli/chunk-36bxs9c7.js.map +0 -10
- package/dist/cli/chunk-53jqw8z0.js +0 -89
- package/dist/cli/chunk-53jqw8z0.js.map +0 -10
- package/dist/cli/chunk-60hz265r.js.map +0 -10
- package/dist/cli/chunk-desvm72z.js.map +0 -14
- package/dist/cli/chunk-dpy8qdjf.js.map +0 -17
- package/dist/cli/chunk-es8h0v3j.js +0 -441
- package/dist/cli/chunk-es8h0v3j.js.map +0 -11
- package/dist/cli/chunk-kdpzw38a.js.map +0 -11
- package/dist/cli/chunk-nbyvgnnm.js.map +0 -36
- package/dist/cli/chunk-s0bdnc7v.js.map +0 -10
- package/dist/cli/chunk-twy7psdz.js.map +0 -10
- package/dist/cli/chunk-ydp3065p.js +0 -64
- package/dist/cli/chunk-ydp3065p.js.map +0 -10
- /package/dist/cli/{chunk-74wfhv79.js.map → chunk-05f21r8h.js.map} +0 -0
- /package/dist/cli/{chunk-9qzgg6mj.js.map → chunk-1cm0h819.js.map} +0 -0
- /package/dist/cli/{chunk-ms9z66nz.js.map → chunk-4rftmttv.js.map} +0 -0
- /package/dist/cli/{chunk-hvcxng5f.js.map → chunk-7tekknev.js.map} +0 -0
- /package/dist/cli/{chunk-bxwhtvw8.js.map → chunk-ahc5gc9h.js.map} +0 -0
- /package/dist/cli/{chunk-tfycddbt.js.map → chunk-bjk964fp.js.map} +0 -0
- /package/dist/cli/{chunk-dqhg54mr.js.map → chunk-dmx6cezx.js.map} +0 -0
- /package/dist/cli/{chunk-ave1wcbr.js.map → chunk-m9bp2hc1.js.map} +0 -0
- /package/dist/cli/{chunk-58bt3hag.js.map → chunk-mrvh4c88.js.map} +0 -0
- /package/dist/cli/{chunk-1f882900.js.map → chunk-th0pgaj5.js.map} +0 -0
- /package/dist/cli/{chunk-dhwtqnnd.js.map → chunk-vnbvhgmd.js.map} +0 -0
- /package/dist/cli/{chunk-j1x92mx9.js.map → chunk-w6e455pz.js.map} +0 -0
- /package/dist/cli/{chunk-y4msrwm9.js.map → chunk-xgq4eytj.js.map} +0 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { BlumeProject } from "../core/project-graph.ts";
|
|
2
|
+
import { changelogIndexBlocks } from "./changelog-markdown.ts";
|
|
3
|
+
import type { ComponentMarkdown } from "./component-markdown.ts";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Downlevel `<Changelog />` to the release list it renders.
|
|
7
|
+
*
|
|
8
|
+
* The generated `/changelog` page publishes its list through
|
|
9
|
+
* `buildChangelogIndexMarkdown`, but a hand-written page that owns the route
|
|
10
|
+
* instead goes through the component downleveler — which leaves a tag it has
|
|
11
|
+
* no serializer for exactly as written. Without this, moving to an authored
|
|
12
|
+
* page would publish a literal `<Changelog />` to `/changelog.md`,
|
|
13
|
+
* llms-full.txt, MCP `get_page` and the search corpus, and an agent would see
|
|
14
|
+
* no releases at all.
|
|
15
|
+
*
|
|
16
|
+
* The list is the same one the mirror publishes, so both routes to a changelog
|
|
17
|
+
* index — generated or authored — reach agents identically.
|
|
18
|
+
*/
|
|
19
|
+
export const changelogComponentSerializers = (project: BlumeProject) =>
|
|
20
|
+
({
|
|
21
|
+
// An empty changelog replaces the tag with nothing, so the page's own prose
|
|
22
|
+
// is all an agent reads. Returning `null` would leave the tag as written.
|
|
23
|
+
Changelog: () => changelogIndexBlocks(project).join("\n\n"),
|
|
24
|
+
}) satisfies Record<string, ComponentMarkdown>;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { mountBasePath, normalizeBasePath } from "../core/base-path.ts";
|
|
2
2
|
import { withChangelogIndexText } from "../core/changelog-index.ts";
|
|
3
|
+
import {
|
|
4
|
+
compareChangelogDates,
|
|
5
|
+
groupChangelogYears,
|
|
6
|
+
parseChangelogDate,
|
|
7
|
+
} from "../core/changelog-list.ts";
|
|
3
8
|
import { EN_UI, resolveUIStrings } from "../core/i18n-ui.ts";
|
|
4
9
|
import type { BlumeProject } from "../core/project-graph.ts";
|
|
5
10
|
import { absoluteUrl } from "../core/site-url.ts";
|
|
@@ -19,14 +24,6 @@ interface IndexRow {
|
|
|
19
24
|
page: PageRecord;
|
|
20
25
|
}
|
|
21
26
|
|
|
22
|
-
const parseDate = (value: string | undefined): Date | null => {
|
|
23
|
-
if (value === undefined) {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
const date = new Date(value);
|
|
27
|
-
return Number.isNaN(date.getTime()) ? null : date;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
27
|
/** A row's link: absolute under a configured site, like llms.txt. */
|
|
31
28
|
const entryUrl = (project: BlumeProject, route: string): string => {
|
|
32
29
|
const { base, site } = project.config.deployment.options;
|
|
@@ -44,18 +41,19 @@ const rowLine = (project: BlumeProject, { date, page }: IndexRow): string => {
|
|
|
44
41
|
return `- [${title}](${entryUrl(project, page.route)})${tail}`;
|
|
45
42
|
};
|
|
46
43
|
|
|
47
|
-
|
|
44
|
+
/**
|
|
45
|
+
* The release list alone, as year headings over linked rows — what
|
|
46
|
+
* `<Changelog />` renders, in Markdown. Split out of the document below so the
|
|
47
|
+
* component's serializer publishes exactly the list the generated page's
|
|
48
|
+
* mirror does: an authored `/changelog` page brings its own heading and prose,
|
|
49
|
+
* and only the list comes from here.
|
|
50
|
+
*
|
|
51
|
+
* Entries are the default locale's, as everywhere else this list is published
|
|
52
|
+
* — see `components/content/Changelog.astro` for why the rule is the same on
|
|
53
|
+
* the rendered page.
|
|
54
|
+
*/
|
|
55
|
+
export const changelogIndexBlocks = (project: BlumeProject): string[] => {
|
|
48
56
|
const { i18n } = project.config;
|
|
49
|
-
const ui = withChangelogIndexText(
|
|
50
|
-
project.config,
|
|
51
|
-
i18n
|
|
52
|
-
? resolveUIStrings(i18n.defaultLocale, {
|
|
53
|
-
defaultLocale: i18n.defaultLocale,
|
|
54
|
-
overrides: i18n.ui,
|
|
55
|
-
})
|
|
56
|
-
: EN_UI,
|
|
57
|
-
i18n?.defaultLocale
|
|
58
|
-
);
|
|
59
57
|
const rows: IndexRow[] = project.graph.pages
|
|
60
58
|
.filter(
|
|
61
59
|
(page) =>
|
|
@@ -67,30 +65,36 @@ export const buildChangelogIndexMarkdown = (project: BlumeProject): string => {
|
|
|
67
65
|
(!i18n || page.locale === i18n.defaultLocale)
|
|
68
66
|
)
|
|
69
67
|
.map((page) => ({
|
|
70
|
-
date:
|
|
68
|
+
date: parseChangelogDate(page.meta.date ?? page.meta.changelog?.date),
|
|
71
69
|
page,
|
|
72
70
|
}))
|
|
73
|
-
.toSorted((a, b) => (
|
|
71
|
+
.toSorted((a, b) => compareChangelogDates(a.date, b.date));
|
|
74
72
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
73
|
+
// Undated entries sort last, under a heading of their own.
|
|
74
|
+
return groupChangelogYears(rows, (row) =>
|
|
75
|
+
row.date ? String(row.date.getUTCFullYear()) : null
|
|
76
|
+
).flatMap((group) => [
|
|
77
|
+
`## ${group.year ?? "Undated"}`,
|
|
78
|
+
group.items.map((row) => rowLine(project, row)).join("\n"),
|
|
79
|
+
]);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const buildChangelogIndexMarkdown = (project: BlumeProject): string => {
|
|
83
|
+
const { i18n } = project.config;
|
|
84
|
+
const ui = withChangelogIndexText(
|
|
85
|
+
project.config,
|
|
86
|
+
i18n
|
|
87
|
+
? resolveUIStrings(i18n.defaultLocale, {
|
|
88
|
+
defaultLocale: i18n.defaultLocale,
|
|
89
|
+
overrides: i18n.ui,
|
|
90
|
+
})
|
|
91
|
+
: EN_UI,
|
|
92
|
+
i18n?.defaultLocale
|
|
93
|
+
);
|
|
94
|
+
const blocks = [
|
|
95
|
+
`# ${ui.changelog.title}`,
|
|
96
|
+
ui.changelog.description,
|
|
97
|
+
...changelogIndexBlocks(project),
|
|
98
|
+
];
|
|
95
99
|
return `${blocks.join("\n\n")}\n`;
|
|
96
100
|
};
|
|
@@ -2,7 +2,10 @@ import { markdownTable } from "markdown-table";
|
|
|
2
2
|
import { mdxToMdast } from "satteri";
|
|
3
3
|
|
|
4
4
|
import { PARAM_LOCATIONS } from "../components/content/api-field.ts";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
parseYouTubeId,
|
|
7
|
+
parseYouTubePlaylist,
|
|
8
|
+
} from "../components/content/youtube.ts";
|
|
6
9
|
import type { ExampleLookup } from "../core/types.ts";
|
|
7
10
|
import { MDX_FEATURES } from "../markdown/features.ts";
|
|
8
11
|
import { readStaticExpression } from "./static-expression.ts";
|
|
@@ -553,18 +556,21 @@ const youtube: ComponentMarkdown = ({ props }) => {
|
|
|
553
556
|
} else if (isString(props.url)) {
|
|
554
557
|
input = props.url;
|
|
555
558
|
}
|
|
559
|
+
const title =
|
|
560
|
+
isString(props.title) && props.title !== ""
|
|
561
|
+
? props.title
|
|
562
|
+
: "Watch on YouTube";
|
|
556
563
|
const videoId = parseYouTubeId(input);
|
|
557
564
|
if (!videoId) {
|
|
558
|
-
|
|
565
|
+
const list = parseYouTubePlaylist(input);
|
|
566
|
+
return list
|
|
567
|
+
? `[${title}](https://www.youtube.com/playlist?list=${list})`
|
|
568
|
+
: null;
|
|
559
569
|
}
|
|
560
570
|
const start =
|
|
561
571
|
isNumber(props.start) && props.start > 0
|
|
562
572
|
? `&t=${Math.floor(props.start)}s`
|
|
563
573
|
: "";
|
|
564
|
-
const title =
|
|
565
|
-
isString(props.title) && props.title !== ""
|
|
566
|
-
? props.title
|
|
567
|
-
: "Watch on YouTube";
|
|
568
574
|
return `[${title}](https://www.youtube.com/watch?v=${videoId}${start})`;
|
|
569
575
|
};
|
|
570
576
|
|
|
@@ -732,11 +738,18 @@ const githubInfo: ComponentMarkdown = ({ lossy, props }) => {
|
|
|
732
738
|
return `[${linkText(`${owner}/${repo}`)}](${linkDestination(`${host}/${owner}/${repo}`)})`;
|
|
733
739
|
};
|
|
734
740
|
|
|
735
|
-
/**
|
|
736
|
-
|
|
737
|
-
|
|
741
|
+
/**
|
|
742
|
+
* `CodeBlock` is a fenced block, its title kept in the fence's meta. Without
|
|
743
|
+
* `code` it renders its children (a fence wrapped in `<CodeBlock>`), so those
|
|
744
|
+
* are its Markdown.
|
|
745
|
+
*/
|
|
746
|
+
const codeBlock: ComponentMarkdown = ({ children, lossy, props }) => {
|
|
747
|
+
if (lossy) {
|
|
738
748
|
return null;
|
|
739
749
|
}
|
|
750
|
+
if (!isString(props.code)) {
|
|
751
|
+
return props.code === undefined && children !== "" ? children : null;
|
|
752
|
+
}
|
|
740
753
|
const lang = textProp(props.lang) || "txt";
|
|
741
754
|
const title = textProp(props.title);
|
|
742
755
|
return fencedBlock(
|
package/src/ai/llms.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { mountBasePath, normalizeBasePath } from "../core/base-path.ts";
|
|
2
|
-
import {
|
|
2
|
+
import { rewriteRelativeAssets } from "../core/content-assets.ts";
|
|
3
3
|
import matter from "../core/frontmatter.ts";
|
|
4
|
+
import { isHiddenPage } from "../core/hidden-pages.ts";
|
|
5
|
+
import { metaDescription } from "../core/manifest.ts";
|
|
4
6
|
import type { BlumeProject } from "../core/project-graph.ts";
|
|
5
7
|
import { absoluteUrl } from "../core/site-url.ts";
|
|
8
|
+
import { extractHeadings } from "../core/sources/normalize.ts";
|
|
6
9
|
import { readExpandedEntryText } from "../core/sources/read.ts";
|
|
7
10
|
import type { NavNode, Navigation, PageRecord } from "../core/types.ts";
|
|
8
11
|
import { buildRssFeeds } from "../deploy/rss.ts";
|
|
@@ -121,7 +124,7 @@ const eligiblePages = (
|
|
|
121
124
|
!(
|
|
122
125
|
page.meta.ai.exclude ||
|
|
123
126
|
page.meta.draft ||
|
|
124
|
-
page.
|
|
127
|
+
isHiddenPage(page, project.graph) ||
|
|
125
128
|
(page.meta.seo.noindex && page.source.name !== "openapi") ||
|
|
126
129
|
(options.versions === "current" && page.version !== "")
|
|
127
130
|
) &&
|
|
@@ -187,7 +190,10 @@ export const buildLlmsIndex = (
|
|
|
187
190
|
|
|
188
191
|
const line = (page: PageRecord): string => {
|
|
189
192
|
seen.add(page.route);
|
|
190
|
-
|
|
193
|
+
// The page's meta description, so a page summarized only in
|
|
194
|
+
// `seo.description` (a generated reference page) still gets one.
|
|
195
|
+
const description = metaDescription(page);
|
|
196
|
+
const summary = description ? `: ${description}` : "";
|
|
191
197
|
return `- [${linkText(page.title)}](${pageUrl(page.route, site, base)})${summary}`;
|
|
192
198
|
};
|
|
193
199
|
|
|
@@ -306,6 +312,26 @@ export const buildLlmsIndex = (
|
|
|
306
312
|
return `${[lead, ...blocks].join("\n\n")}\n`;
|
|
307
313
|
};
|
|
308
314
|
|
|
315
|
+
// A line an ATX heading opens (`# Title`); any other first line can only open
|
|
316
|
+
// a setext heading, underlined on the line after it.
|
|
317
|
+
const ATX_HEADING = /^ {0,3}#/u;
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* A page body without the `# Heading` it opens with, when that heading's text
|
|
321
|
+
* is the page's title. An untitled page takes its title from that heading,
|
|
322
|
+
* and its `llms-full.txt` section already opens with `# <title>`, so the
|
|
323
|
+
* heading would show twice; the rendered page drops it the same way (see
|
|
324
|
+
* `markdown/title-heading.ts`).
|
|
325
|
+
*/
|
|
326
|
+
const withoutTitleHeading = (body: string, title: string): string => {
|
|
327
|
+
const lines = body.split("\n");
|
|
328
|
+
const span = ATX_HEADING.test(lines[0] ?? "") ? 1 : 2;
|
|
329
|
+
const [heading] = extractHeadings(lines.slice(0, span).join("\n"));
|
|
330
|
+
return heading?.depth === 1 && heading.text === title
|
|
331
|
+
? lines.slice(span).join("\n").trimStart()
|
|
332
|
+
: body;
|
|
333
|
+
};
|
|
334
|
+
|
|
309
335
|
/** Build `llms-full.txt`: the full Markdown body of every current-docs page. */
|
|
310
336
|
const buildFull = async (project: BlumeProject): Promise<string> => {
|
|
311
337
|
const { config } = project;
|
|
@@ -321,7 +347,7 @@ const buildFull = async (project: BlumeProject): Promise<string> => {
|
|
|
321
347
|
// Colocated `./image.png` references resolve to nothing for a reader of
|
|
322
348
|
// llms-full.txt; point them at the served originals instead.
|
|
323
349
|
if (page.sourcePath) {
|
|
324
|
-
raw =
|
|
350
|
+
raw = rewriteRelativeAssets({
|
|
325
351
|
deployBase: config.deployment.options.base,
|
|
326
352
|
projectRoot: project.context.root,
|
|
327
353
|
source: raw,
|
|
@@ -346,7 +372,12 @@ const buildFull = async (project: BlumeProject): Promise<string> => {
|
|
|
346
372
|
config.deployment.options.site,
|
|
347
373
|
normalizeBasePath(config.deployment.options.base)
|
|
348
374
|
);
|
|
349
|
-
return [
|
|
375
|
+
return [
|
|
376
|
+
`# ${page.title}`,
|
|
377
|
+
`Source: ${url}`,
|
|
378
|
+
"",
|
|
379
|
+
withoutTitleHeading(body, page.title),
|
|
380
|
+
].join("\n");
|
|
350
381
|
})
|
|
351
382
|
);
|
|
352
383
|
|
package/src/ai/markdown.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { readFile } from "node:fs/promises";
|
|
|
2
2
|
|
|
3
3
|
import { hasGeneratedChangelog } from "../astro/pages.ts";
|
|
4
4
|
import { CHANGELOG_INDEX_ROUTE } from "../core/changelog-index.ts";
|
|
5
|
-
import {
|
|
5
|
+
import { rewriteRelativeAssets } from "../core/content-assets.ts";
|
|
6
6
|
import { discoverPages, discoverPagesSync } from "../core/custom-pages.ts";
|
|
7
7
|
import matter from "../core/frontmatter.ts";
|
|
8
8
|
import type { BlumeProject } from "../core/project-graph.ts";
|
|
@@ -76,7 +76,7 @@ export const buildRawMarkdown = async (
|
|
|
76
76
|
project.manifest.routes.map(async (route) => {
|
|
77
77
|
let text = await readRoute(route);
|
|
78
78
|
if (route.sourcePath) {
|
|
79
|
-
text =
|
|
79
|
+
text = rewriteRelativeAssets({
|
|
80
80
|
deployBase: project.config.deployment.options.base,
|
|
81
81
|
projectRoot: project.context.root,
|
|
82
82
|
source: text,
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { customCodeSamples } from "../components/openapi/code-samples.ts";
|
|
2
2
|
import { mountBasePath, normalizeBasePath } from "../core/base-path.ts";
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
ApiDocument,
|
|
5
|
+
ApiOperationRef,
|
|
6
|
+
OpenApiData,
|
|
7
|
+
} from "../openapi/model.ts";
|
|
4
8
|
import {
|
|
5
9
|
operationObject,
|
|
6
10
|
operationOf,
|
|
@@ -19,6 +23,8 @@ import {
|
|
|
19
23
|
isString,
|
|
20
24
|
linkDestination,
|
|
21
25
|
} from "./component-markdown.ts";
|
|
26
|
+
import { operationDetails } from "./operation-details.ts";
|
|
27
|
+
import type { OperationParts } from "./operation-details.ts";
|
|
22
28
|
|
|
23
29
|
/**
|
|
24
30
|
* Spec-authored prose as one line of inline Markdown. Unlike the props the
|
|
@@ -70,13 +76,13 @@ const listItem = (
|
|
|
70
76
|
* Each serializer emits what its component renders and no more: the endpoint
|
|
71
77
|
* in the spec kind's own notation ({@link operationSignature}), the version and
|
|
72
78
|
* addresses the overview shows ({@link specAddresses}), the linked list a tag
|
|
73
|
-
* section shows.
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
79
|
+
* section shows. The spec's own `x-codeSamples` come along, as fenced code:
|
|
80
|
+
* they're text the spec wrote out (often an SDK call no agent could derive
|
|
81
|
+
* from the endpoint), read through the same `customCodeSamples` the page
|
|
82
|
+
* renders. So do an OpenAPI operation's request body and responses, with
|
|
83
|
+
* their examples ({@link operationDetails}), built from the helpers the
|
|
84
|
+
* page's components use so the two can't disagree; parameters and nested
|
|
85
|
+
* schemas are left to the page.
|
|
80
86
|
*
|
|
81
87
|
* `specs` is the parsed `blume:openapi` data — empty when the project has no
|
|
82
88
|
* API reference, in which case every serializer declines. `deployBase` is the
|
|
@@ -166,7 +172,17 @@ export const openapiComponentSerializers = (
|
|
|
166
172
|
(sample) =>
|
|
167
173
|
`**${inlineText(sample.label)}**\n\n${fencedBlock(sample.lang, sample.source)}`
|
|
168
174
|
);
|
|
169
|
-
|
|
175
|
+
// SAFETY: an operation object only resolves out of an OpenAPI spec,
|
|
176
|
+
// whose document is the OpenAPI shape; its bodies and responses are
|
|
177
|
+
// the JSON `OperationParts` views, which the helpers read defensively.
|
|
178
|
+
const details = object
|
|
179
|
+
? operationDetails(
|
|
180
|
+
data.document as ApiDocument,
|
|
181
|
+
object as OperationParts,
|
|
182
|
+
operation.webhook === true
|
|
183
|
+
)
|
|
184
|
+
: [];
|
|
185
|
+
return [signature, ...notes, ...samples, ...details].join("\n\n");
|
|
170
186
|
},
|
|
171
187
|
} satisfies Record<string, ComponentMarkdown>;
|
|
172
188
|
};
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import {
|
|
2
|
+
preferredMedia,
|
|
3
|
+
responsePanels,
|
|
4
|
+
} from "../components/openapi/example-panels.ts";
|
|
5
|
+
import type { ResponseLike } from "../components/openapi/example-panels.ts";
|
|
6
|
+
import {
|
|
7
|
+
isNullable,
|
|
8
|
+
mediaExample,
|
|
9
|
+
namedExamples,
|
|
10
|
+
objectProperties,
|
|
11
|
+
resolveComponentRef,
|
|
12
|
+
resolveSchema,
|
|
13
|
+
toJson,
|
|
14
|
+
typeLabel,
|
|
15
|
+
} from "../components/openapi/helpers.ts";
|
|
16
|
+
import type {
|
|
17
|
+
ComponentsLike,
|
|
18
|
+
ExampleDirection,
|
|
19
|
+
MediaLike,
|
|
20
|
+
SchemaLike,
|
|
21
|
+
SpecValue,
|
|
22
|
+
} from "../components/openapi/helpers.ts";
|
|
23
|
+
import type { ApiDocument } from "../openapi/model.ts";
|
|
24
|
+
import { fencedBlock, inlineCode } from "./component-markdown.ts";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The body and responses of an OpenAPI operation as compact Markdown, for the
|
|
28
|
+
* agent-facing copy of its page (`<route>.md`, llms-full.txt, MCP
|
|
29
|
+
* `get_page`): the request body's media type and top-level properties, its
|
|
30
|
+
* example (every named one), each response's status and description, and
|
|
31
|
+
* the examples the spec records. Without these an agent read an endpoint
|
|
32
|
+
* with no word of what to send or what comes back, though the spec, and the
|
|
33
|
+
* page, had both. Built from the helpers the page's own components use, so
|
|
34
|
+
* the two can't disagree: the same JSON-ish media type, the same examples,
|
|
35
|
+
* `readOnly` and `writeOnly` properties left out the same way.
|
|
36
|
+
*
|
|
37
|
+
* Kept compact: properties one level deep (an example shows the rest), and
|
|
38
|
+
* a sampled response example only for the first success response that
|
|
39
|
+
* records none, since error bodies often all share one schema.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
/** A request body, `$ref` resolved. */
|
|
43
|
+
interface RequestBodyLike {
|
|
44
|
+
description?: string;
|
|
45
|
+
required?: boolean;
|
|
46
|
+
content?: Record<string, MediaLike>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** The operation fields read here. */
|
|
50
|
+
export interface OperationParts {
|
|
51
|
+
requestBody?: RequestBodyLike & { $ref?: string };
|
|
52
|
+
responses?: Record<string, ResponseLike & { $ref?: string }>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Spec prose on one line, the characters Markdown reads as markup escaped. */
|
|
56
|
+
const oneLine = (text: string): string =>
|
|
57
|
+
text
|
|
58
|
+
.trim()
|
|
59
|
+
.replaceAll(/\s+/gu, " ")
|
|
60
|
+
.replaceAll(/[\\`*_[\]<>~]/gu, String.raw`\$&`);
|
|
61
|
+
|
|
62
|
+
/** A fenced JSON example, under its label. */
|
|
63
|
+
const exampleBlock = (label: string, value: SpecValue): string =>
|
|
64
|
+
`${label}:\n\n${fencedBlock("json", toJson(value))}`;
|
|
65
|
+
|
|
66
|
+
/** One line per top-level property: name, type, required, description. */
|
|
67
|
+
const propertyLines = (
|
|
68
|
+
schema: SchemaLike | undefined,
|
|
69
|
+
schemas: Record<string, SchemaLike>,
|
|
70
|
+
direction: ExampleDirection
|
|
71
|
+
): string[] => {
|
|
72
|
+
if (!schema) {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
const omitted = direction === "request" ? "readOnly" : "writeOnly";
|
|
76
|
+
const { properties, required } = objectProperties(schema, schemas);
|
|
77
|
+
return properties.flatMap(([name, property]) => {
|
|
78
|
+
const resolved = resolveSchema(schemas, property);
|
|
79
|
+
if (property[omitted] === true || resolved[omitted] === true) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
const type = `${typeLabel(property)}${isNullable(resolved) ? " | null" : ""}`;
|
|
83
|
+
const notes = [type, required.has(name) ? "required" : ""]
|
|
84
|
+
.filter(Boolean)
|
|
85
|
+
.join(", ");
|
|
86
|
+
const description = resolved.description?.trim();
|
|
87
|
+
return [
|
|
88
|
+
`- ${inlineCode(name)} (${notes})${description ? ` — ${oneLine(description)}` : ""}`,
|
|
89
|
+
];
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/** The request body (a webhook's payload): media type, properties, examples. */
|
|
94
|
+
const bodyMarkdown = (
|
|
95
|
+
body: RequestBodyLike,
|
|
96
|
+
schemas: Record<string, SchemaLike>,
|
|
97
|
+
components: ComponentsLike | undefined,
|
|
98
|
+
webhook: boolean
|
|
99
|
+
): string[] => {
|
|
100
|
+
const chosen = preferredMedia(body.content);
|
|
101
|
+
if (!chosen) {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
const [type, media] = chosen;
|
|
105
|
+
// A webhook's payload travels from the API, like a response.
|
|
106
|
+
const direction = webhook ? "response" : "request";
|
|
107
|
+
const heading = webhook ? "Payload" : "Request body";
|
|
108
|
+
const lines = propertyLines(media.schema, schemas, direction);
|
|
109
|
+
const named = namedExamples(media, schemas, direction, components);
|
|
110
|
+
const single = mediaExample(media, schemas, direction, components);
|
|
111
|
+
const examples =
|
|
112
|
+
named.length > 0
|
|
113
|
+
? named.map((example) =>
|
|
114
|
+
exampleBlock(
|
|
115
|
+
`${heading} example, ${oneLine(example.label)}`,
|
|
116
|
+
example.value
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
: [
|
|
120
|
+
single === null || single === undefined
|
|
121
|
+
? ""
|
|
122
|
+
: exampleBlock(`${heading} example`, single),
|
|
123
|
+
];
|
|
124
|
+
return [
|
|
125
|
+
`**${heading}** (${[inlineCode(type), body.required ? "required" : ""].filter(Boolean).join(", ")})`,
|
|
126
|
+
body.description?.trim() ? oneLine(body.description) : "",
|
|
127
|
+
lines.join("\n"),
|
|
128
|
+
...examples,
|
|
129
|
+
].filter(Boolean);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/** The responses: one line per status, then the examples the spec records. */
|
|
133
|
+
const responsesMarkdown = (
|
|
134
|
+
responses: Record<string, ResponseLike>,
|
|
135
|
+
schemas: Record<string, SchemaLike>,
|
|
136
|
+
components: ComponentsLike | undefined
|
|
137
|
+
): string[] => {
|
|
138
|
+
const statuses = Object.entries(responses);
|
|
139
|
+
if (statuses.length === 0) {
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
const recorded = new Set(
|
|
143
|
+
statuses.flatMap(([status, response]) => {
|
|
144
|
+
const media = preferredMedia(response.content)?.[1];
|
|
145
|
+
return media?.example === undefined && media?.examples === undefined
|
|
146
|
+
? []
|
|
147
|
+
: [status];
|
|
148
|
+
})
|
|
149
|
+
);
|
|
150
|
+
const sampled = statuses.find(
|
|
151
|
+
([status]) => status.startsWith("2") && !recorded.has(status)
|
|
152
|
+
)?.[0];
|
|
153
|
+
const examples = responsePanels(responses, schemas, components).flatMap(
|
|
154
|
+
(panel) =>
|
|
155
|
+
(recorded.has(panel.name) || panel.name === sampled) &&
|
|
156
|
+
panel.value !== null &&
|
|
157
|
+
panel.value !== undefined
|
|
158
|
+
? [
|
|
159
|
+
exampleBlock(
|
|
160
|
+
`Response example, ${oneLine(panel.label)}`,
|
|
161
|
+
panel.value
|
|
162
|
+
),
|
|
163
|
+
]
|
|
164
|
+
: []
|
|
165
|
+
);
|
|
166
|
+
return [
|
|
167
|
+
"**Responses**",
|
|
168
|
+
statuses
|
|
169
|
+
.map(
|
|
170
|
+
([status, response]) =>
|
|
171
|
+
`- ${inlineCode(status)}${response.description?.trim() ? ` — ${oneLine(response.description)}` : ""}`
|
|
172
|
+
)
|
|
173
|
+
.join("\n"),
|
|
174
|
+
...examples,
|
|
175
|
+
];
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* An operation's body and responses as Markdown blocks, `$ref`s to
|
|
180
|
+
* `components` resolved, ready to join with blank lines.
|
|
181
|
+
*/
|
|
182
|
+
export const operationDetails = (
|
|
183
|
+
document: ApiDocument,
|
|
184
|
+
operation: OperationParts,
|
|
185
|
+
webhook: boolean
|
|
186
|
+
): string[] => {
|
|
187
|
+
// SAFETY: a parsed document's `components` is the section-keyed table
|
|
188
|
+
// `ComponentsLike` describes; the helpers guard every entry they read.
|
|
189
|
+
const components = document.components as ComponentsLike | undefined;
|
|
190
|
+
// SAFETY: component schemas are the JSON Schema objects `SchemaLike` views.
|
|
191
|
+
const schemas = (document.components?.schemas ?? {}) as Record<
|
|
192
|
+
string,
|
|
193
|
+
SchemaLike
|
|
194
|
+
>;
|
|
195
|
+
const body = operation.requestBody
|
|
196
|
+
? resolveComponentRef(operation.requestBody, components, "requestBodies")
|
|
197
|
+
: undefined;
|
|
198
|
+
const responses = Object.fromEntries(
|
|
199
|
+
Object.entries(operation.responses ?? {}).map(([status, response]) => [
|
|
200
|
+
status,
|
|
201
|
+
resolveComponentRef(response, components, "responses"),
|
|
202
|
+
])
|
|
203
|
+
);
|
|
204
|
+
return [
|
|
205
|
+
...(body ? bodyMarkdown(body, schemas, components, webhook) : []),
|
|
206
|
+
...responsesMarkdown(responses, schemas, components),
|
|
207
|
+
];
|
|
208
|
+
};
|