blume 2.0.1 → 2.0.3
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/CHANGELOG.md +147 -0
- package/dist/cli/{chunk-qwsrynx5.js → chunk-1d7ve1dm.js} +38 -19
- package/dist/cli/{chunk-qwsrynx5.js.map → chunk-1d7ve1dm.js.map} +4 -4
- package/dist/cli/{chunk-s6jhgk0q.js → chunk-2eytanqx.js} +2 -2
- package/dist/cli/{chunk-kdp5q7ke.js → chunk-2hsdwb9n.js} +18 -19
- package/dist/cli/{chunk-kdp5q7ke.js.map → chunk-2hsdwb9n.js.map} +2 -2
- package/dist/cli/{chunk-hdpx1tax.js → chunk-2z928egk.js} +5 -5
- package/dist/cli/{chunk-jwyddg7y.js → chunk-35d4wj9f.js} +30 -24
- package/dist/cli/chunk-35d4wj9f.js.map +15 -0
- package/dist/cli/{chunk-ah61y8py.js → chunk-364znk6q.js} +2 -2
- package/dist/cli/{chunk-jts8mvcz.js → chunk-3em5wd2y.js} +25 -6
- package/dist/cli/{chunk-jts8mvcz.js.map → chunk-3em5wd2y.js.map} +3 -3
- package/dist/cli/{chunk-s1p84fyh.js → chunk-5f86nr5m.js} +63 -22
- package/dist/cli/chunk-5f86nr5m.js.map +11 -0
- package/dist/cli/{chunk-2hn4b8z7.js → chunk-5m5nmvyq.js} +170 -76
- package/dist/cli/chunk-5m5nmvyq.js.map +13 -0
- package/dist/cli/{chunk-epjnccmv.js → chunk-5xvm6tfj.js} +18 -15
- package/dist/cli/chunk-5xvm6tfj.js.map +10 -0
- package/dist/cli/{chunk-fz5wtpmh.js → chunk-6dsbexzp.js} +18 -15
- package/dist/cli/chunk-6dsbexzp.js.map +10 -0
- package/dist/cli/{chunk-vtk4a6dg.js → chunk-8ktnccpt.js} +1 -1
- package/dist/cli/{chunk-6hsn950k.js → chunk-9t7a85s3.js} +178 -48
- package/dist/cli/chunk-9t7a85s3.js.map +10 -0
- package/dist/cli/{chunk-fxypxtvm.js → chunk-a58773jm.js} +2 -2
- package/dist/cli/{chunk-mb2919y2.js → chunk-acanzt5p.js} +17 -6
- package/dist/cli/chunk-acanzt5p.js.map +10 -0
- package/dist/cli/{chunk-27g6wdth.js → chunk-akbpwfxc.js} +90 -26
- package/dist/cli/chunk-akbpwfxc.js.map +10 -0
- package/dist/cli/{chunk-wm7js3j9.js → chunk-b07cmahc.js} +2 -2
- package/dist/cli/{chunk-wgm7m9qk.js → chunk-c8chx29p.js} +179 -51
- package/dist/cli/chunk-c8chx29p.js.map +36 -0
- package/dist/cli/{chunk-qs4q5p4e.js → chunk-crgn1q09.js} +32 -14
- package/dist/cli/chunk-crgn1q09.js.map +10 -0
- package/dist/cli/chunk-e04dxsz1.js +39 -0
- package/dist/cli/chunk-e04dxsz1.js.map +10 -0
- package/dist/cli/{chunk-yt5n7ppj.js → chunk-ey84smr6.js} +17 -7
- package/dist/cli/chunk-ey84smr6.js.map +10 -0
- package/dist/cli/{chunk-f2z5v128.js → chunk-g4hq16wv.js} +14 -15
- package/dist/cli/{chunk-f2z5v128.js.map → chunk-g4hq16wv.js.map} +2 -2
- package/dist/cli/{chunk-fa25z98p.js → chunk-ga0pf4aj.js} +9 -8
- package/dist/cli/chunk-ga0pf4aj.js.map +11 -0
- package/dist/cli/{chunk-kpf8rrjc.js → chunk-hm3vjy5s.js} +109 -58
- package/dist/cli/chunk-hm3vjy5s.js.map +19 -0
- package/dist/cli/{chunk-fs23ddbb.js → chunk-j85vccga.js} +625 -750
- package/dist/cli/chunk-j85vccga.js.map +36 -0
- package/dist/cli/{chunk-ch6g3ar0.js → chunk-p3v96n38.js} +6 -6
- package/dist/cli/{chunk-ch6g3ar0.js.map → chunk-p3v96n38.js.map} +3 -3
- package/dist/cli/{chunk-q5163e60.js → chunk-p73c0m7w.js} +21 -19
- package/dist/cli/chunk-p73c0m7w.js.map +11 -0
- package/dist/cli/{chunk-dh8cwk36.js → chunk-pehfxfta.js} +24 -9
- package/dist/cli/chunk-pehfxfta.js.map +10 -0
- package/dist/cli/{chunk-qkqwkpte.js → chunk-pv29h0wf.js} +3688 -1313
- package/dist/cli/chunk-pv29h0wf.js.map +190 -0
- package/dist/cli/{chunk-6vm74dry.js → chunk-q58y5e6a.js} +10 -10
- package/dist/cli/{chunk-6vm74dry.js.map → chunk-q58y5e6a.js.map} +3 -3
- package/dist/cli/{chunk-zxcczpyx.js → chunk-r20tn01b.js} +1 -1
- package/dist/cli/{chunk-m3vmjgmq.js → chunk-r9rcc4w7.js} +16 -8
- package/dist/cli/chunk-r9rcc4w7.js.map +10 -0
- package/dist/cli/{chunk-5shv93fd.js → chunk-tkacnehg.js} +2 -2
- package/dist/cli/{chunk-zxh4d9vy.js → chunk-tzmab476.js} +4 -4
- package/dist/cli/{chunk-yw7dm696.js → chunk-vg9r4eb9.js} +24 -11
- package/dist/cli/chunk-vg9r4eb9.js.map +14 -0
- package/dist/cli/{chunk-79jhk4py.js → chunk-wrr3j9w9.js} +260 -132
- package/dist/cli/chunk-wrr3j9w9.js.map +35 -0
- package/dist/cli/{chunk-6crbhc3x.js → chunk-yfyb25rh.js} +54 -27
- package/dist/cli/chunk-yfyb25rh.js.map +14 -0
- package/dist/cli/index.js +162 -35
- package/dist/cli/index.js.map +4 -4
- package/dist/types/ai/agent-surface.d.ts +32 -0
- package/dist/types/ai/api-catalog.d.ts +7 -1
- package/dist/types/ai/ask-context.d.ts +7 -0
- package/dist/types/ai/component-markdown.d.ts +4 -4
- package/dist/types/ai/link-headers.d.ts +8 -1
- package/dist/types/ai/openapi-components.d.ts +5 -2
- package/dist/types/ai/relative-links.d.ts +11 -4
- package/dist/types/ai/skills.d.ts +4 -1
- package/dist/types/ai/static-expression.d.ts +28 -0
- package/dist/types/ai/tar.d.ts +1 -3
- package/dist/types/analytics/databuddy.d.ts +43 -0
- package/dist/types/analytics/index.d.ts +4 -0
- package/dist/types/analytics/one-dollar-stats.d.ts +48 -0
- package/dist/types/analytics/schema.d.ts +28 -0
- package/dist/types/astro/integration.d.ts +3 -2
- package/dist/types/cli/env.d.ts +5 -0
- package/dist/types/cli/init/scaffold.d.ts +19 -3
- package/dist/types/cli/init/starter-spec.d.ts +11 -0
- package/dist/types/core/base-path.d.ts +24 -1
- package/dist/types/core/config-input.d.ts +13 -11
- package/dist/types/core/config.d.ts +2 -2
- package/dist/types/core/directive-diagnostics.d.ts +12 -0
- package/dist/types/core/graph.d.ts +2 -0
- package/dist/types/core/heading-markers.d.ts +5 -7
- package/dist/types/core/i18n-ui.d.ts +31 -0
- package/dist/types/core/i18n.d.ts +9 -1
- package/dist/types/core/last-modified.d.ts +10 -0
- package/dist/types/core/links.d.ts +3 -1
- package/dist/types/core/load-module.d.ts +10 -0
- package/dist/types/core/locale-links.d.ts +12 -2
- package/dist/types/core/meta.d.ts +13 -1
- package/dist/types/core/nav-diagnostics.d.ts +10 -0
- package/dist/types/core/navigation.d.ts +38 -0
- package/dist/types/core/ordering-prefix.d.ts +4 -0
- package/dist/types/core/safe-links.d.ts +3 -1
- package/dist/types/core/schema.d.ts +59 -13
- package/dist/types/core/sources/github-releases.d.ts +5 -0
- package/dist/types/core/sources/lower.d.ts +38 -13
- package/dist/types/core/sources/normalize.d.ts +23 -2
- package/dist/types/core/sources/remote.d.ts +11 -1
- package/dist/types/core/sources/resolve.d.ts +12 -0
- package/dist/types/core/sources/types.d.ts +31 -0
- package/dist/types/core/sources/watch.d.ts +12 -5
- package/dist/types/core/standard-schema.d.ts +5 -0
- package/dist/types/core/types.d.ts +9 -0
- package/dist/types/deploy/adapters/node.d.ts +5 -2
- package/dist/types/deploy/adapters/types.d.ts +7 -0
- package/dist/types/deploy/artifacts.d.ts +6 -4
- package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
- package/dist/types/deploy/headers.d.ts +36 -7
- package/dist/types/deploy/node-headers.d.ts +43 -8
- package/dist/types/deploy/platforms/netlify.d.ts +27 -2
- package/dist/types/deploy/platforms/node.d.ts +6 -5
- package/dist/types/deploy/platforms/types.d.ts +15 -0
- package/dist/types/deploy/platforms/vercel.d.ts +3 -2
- package/dist/types/deploy/redirects.d.ts +31 -15
- package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
- package/dist/types/markdown/directives.d.ts +62 -0
- package/dist/types/markdown/features.d.ts +21 -0
- package/dist/types/markdown/mdast.d.ts +63 -0
- package/dist/types/openapi/asyncapi.d.ts +4 -2
- package/dist/types/openapi/model.d.ts +20 -6
- package/dist/types/search/sync/algolia.d.ts +3 -1
- package/docs/01-quickstart.mdx +3 -2
- package/docs/02-deployment.mdx +20 -9
- package/docs/08-faq.mdx +10 -3
- package/docs/advanced/changelog.mdx +1 -1
- package/docs/advanced/custom-pages.mdx +7 -5
- package/docs/cli/audit.mdx +19 -3
- package/docs/cli/doctor.mdx +2 -2
- package/docs/cli/evals.mdx +4 -4
- package/docs/cli/index.mdx +4 -1
- package/docs/cli/translate.mdx +4 -4
- package/docs/cli/version.mdx +1 -1
- package/docs/configuration/analytics.mdx +42 -2
- package/docs/configuration/assistant.mdx +1 -1
- package/docs/configuration/customization.mdx +6 -3
- package/docs/configuration/index.mdx +5 -3
- package/docs/configuration/search.mdx +2 -2
- package/docs/content/components.mdx +2 -2
- package/docs/content/frontmatter.mdx +5 -1
- package/docs/content/i18n.mdx +3 -1
- package/docs/content/index.mdx +8 -4
- package/docs/content/islands.mdx +1 -1
- package/docs/content/meta.mdx +7 -3
- package/docs/content/navigation.mdx +29 -4
- package/docs/content/sources.mdx +18 -16
- package/docs/content/syntax.mdx +14 -0
- package/docs/content/versioning.mdx +2 -1
- package/docs/discoverability/agent-discovery.mdx +22 -8
- package/docs/discoverability/index.mdx +4 -3
- package/docs/discoverability/llms-txt.mdx +2 -5
- package/docs/discoverability/markdown.mdx +6 -4
- package/docs/discoverability/mcp.mdx +4 -0
- package/docs/discoverability/metadata.mdx +3 -2
- package/docs/discoverability/open-graph.mdx +8 -4
- package/docs/discoverability/rss.mdx +4 -2
- package/docs/references/asyncapi.mdx +2 -2
- package/docs/references/graphql.mdx +2 -2
- package/docs/references/openapi.mdx +6 -4
- package/package.json +1 -1
- package/skills/blume-migrate/SKILL.md +6 -6
- package/skills/blume-migrate/references/docusaurus.md +6 -6
- package/skills/blume-migrate/references/fumadocs.md +1 -1
- package/skills/blume-migrate/references/mintlify.md +3 -3
- package/skills/blume-migrate/references/monorepo.md +1 -1
- package/skills/blume-migrate/references/nextra.md +2 -2
- package/skills/blume-migrate/references/starlight.md +5 -5
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +9 -4
- package/skills/blume-update-docs/references/audit-checklist.md +1 -1
- package/src/ai/agent-readability.ts +2 -2
- package/src/ai/agent-surface.ts +56 -0
- package/src/ai/ai-catalog.ts +6 -2
- package/src/ai/api/handlers.ts +2 -2
- package/src/ai/api/spec.ts +17 -4
- package/src/ai/api-catalog.ts +13 -3
- package/src/ai/ask-context.ts +14 -2
- package/src/ai/ask-data.ts +26 -12
- package/src/ai/changelog-markdown.ts +2 -2
- package/src/ai/component-markdown.ts +43 -37
- package/src/ai/link-headers.ts +22 -8
- package/src/ai/llms.ts +22 -10
- package/src/ai/markdown.ts +23 -11
- package/src/ai/mcp/discovery.ts +2 -2
- package/src/ai/mcp/query.ts +2 -2
- package/src/ai/mcp/server.ts +80 -9
- package/src/ai/openapi-components.ts +22 -6
- package/src/ai/relative-links.ts +99 -15
- package/src/ai/serializers.ts +2 -1
- package/src/ai/skills.ts +14 -1
- package/src/ai/static-expression.ts +416 -0
- package/src/ai/tar.ts +139 -12
- package/src/ai/visibility.ts +45 -14
- package/src/analytics/databuddy.ts +67 -0
- package/src/analytics/head.ts +8 -0
- package/src/analytics/index.ts +7 -0
- package/src/analytics/one-dollar-stats.ts +86 -0
- package/src/analytics/posthog.ts +21 -3
- package/src/analytics/schema.ts +4 -0
- package/src/astro/generate.ts +72 -28
- package/src/astro/include-hmr.ts +31 -12
- package/src/astro/include-refresh.ts +19 -8
- package/src/astro/integration.ts +108 -14
- package/src/astro/runtime-modules.ts +28 -13
- package/src/astro/templates.ts +139 -57
- package/src/audit/catalog.ts +2 -2
- package/src/audit/checks/assets.ts +25 -5
- package/src/audit/checks/content.ts +20 -2
- package/src/audit/checks/i18n.ts +46 -8
- package/src/audit/checks/indexability.ts +39 -19
- package/src/audit/checks/links.ts +22 -1
- package/src/audit/checks/llms.ts +6 -3
- package/src/audit/checks/network.ts +3 -1
- package/src/audit/checks/og-image.ts +10 -0
- package/src/audit/checks/robots.ts +6 -1
- package/src/audit/checks/sitemap.ts +56 -31
- package/src/audit/checks/social.ts +21 -2
- package/src/audit/crawl.ts +88 -15
- package/src/audit/graph.ts +3 -1
- package/src/audit/report.ts +54 -17
- package/src/audit/run.ts +13 -9
- package/src/audit/snapshot.ts +5 -0
- package/src/audit/types.ts +18 -0
- package/src/audit/url.ts +37 -6
- package/src/cli/args.ts +32 -0
- package/src/cli/build-failure.ts +50 -0
- package/src/cli/commands/audit.ts +11 -1
- package/src/cli/commands/build.ts +19 -5
- package/src/cli/commands/check.ts +2 -0
- package/src/cli/commands/doctor.ts +3 -1
- package/src/cli/commands/eval.ts +20 -18
- package/src/cli/commands/init.ts +15 -5
- package/src/cli/commands/preview.ts +15 -0
- package/src/cli/commands/sync.ts +2 -0
- package/src/cli/commands/translate.ts +18 -18
- package/src/cli/commands/upgrade.ts +11 -0
- package/src/cli/commands/validate.ts +3 -1
- package/src/cli/dev-lock.ts +157 -37
- package/src/cli/eject-scripts.ts +32 -7
- package/src/cli/env.ts +12 -1
- package/src/cli/init/scaffold.ts +117 -11
- package/src/cli/init/starter-spec.ts +235 -0
- package/src/cli/report-format.ts +11 -6
- package/src/components/colors.ts +19 -0
- package/src/components/content/AccordionItem.astro +26 -22
- package/src/components/content/Badge.astro +7 -12
- package/src/components/content/Card.astro +2 -2
- package/src/components/content/Component.astro +11 -5
- package/src/components/content/Expandable.astro +5 -1
- package/src/components/content/Frame.astro +2 -7
- package/src/components/content/GithubInfo.astro +12 -2
- package/src/components/content/Prompt.astro +2 -7
- package/src/components/content/Tab.astro +0 -1
- package/src/components/content/Tabs.astro +58 -5
- package/src/components/content/Tile.astro +1 -1
- package/src/components/content/Tooltip.astro +69 -7
- package/src/components/content/Tree.astro +7 -2
- package/src/components/content/TypeTable.astro +10 -5
- package/src/components/content/Update.astro +8 -2
- package/src/components/content/auto-type-table.ts +4 -1
- package/src/components/content/badge-color.ts +20 -0
- package/src/components/content/base-href.ts +14 -22
- package/src/components/content/inline-markdown.ts +27 -7
- package/src/components/copy-feedback.ts +35 -8
- package/src/components/islands/assistant.tsx +37 -6
- package/src/components/islands/base-path.ts +47 -10
- package/src/components/islands/hooks.ts +42 -21
- package/src/components/islands/webmcp.ts +16 -10
- package/src/components/layout/Banner.astro +23 -4
- package/src/components/layout/Breadcrumbs.astro +5 -2
- package/src/components/layout/DiscoveryLinks.astro +9 -5
- package/src/components/layout/Header.astro +23 -10
- package/src/components/layout/LanguageSwitcher.astro +2 -2
- package/src/components/layout/Logo.astro +5 -0
- package/src/components/layout/NavSelector.astro +8 -3
- package/src/components/layout/NavTabMenu.astro +133 -0
- package/src/components/layout/NavTree.astro +31 -12
- package/src/components/layout/NavTreeCache.astro +5 -2
- package/src/components/layout/NavTreeScript.astro +45 -5
- package/src/components/layout/PageActions.astro +4 -3
- package/src/components/layout/PageLayout.astro +28 -13
- package/src/components/layout/Pagination.astro +3 -3
- package/src/components/layout/ReferenceLayout.astro +4 -1
- package/src/components/layout/RootLayout.astro +25 -12
- package/src/components/layout/Search.astro +38 -14
- package/src/components/layout/VersionBanner.astro +2 -2
- package/src/components/layout/analytics-client.ts +14 -0
- package/src/components/layout/toc-active.ts +41 -0
- package/src/components/layout/toc-element.ts +8 -14
- package/src/components/openapi/ApiTagOperations.astro +2 -2
- package/src/components/openapi/AsyncApiOperation.astro +7 -4
- package/src/components/openapi/GraphqlChip.astro +2 -2
- package/src/components/openapi/GraphqlType.astro +11 -3
- package/src/components/openapi/MessageComposer.astro +1 -1
- package/src/components/openapi/Operation.astro +23 -5
- package/src/components/openapi/PanelTabs.astro +4 -1
- package/src/components/openapi/Playground.astro +8 -2
- package/src/components/openapi/RequestPanel.astro +13 -6
- package/src/components/openapi/SchemaProperty.astro +11 -33
- package/src/components/openapi/SchemaTable.astro +34 -52
- package/src/components/openapi/async.ts +38 -6
- package/src/components/openapi/helpers.ts +100 -12
- package/src/components/openapi/message-composer.ts +14 -1
- package/src/components/openapi/message-model.ts +12 -2
- package/src/components/openapi/message.ts +21 -3
- package/src/components/openapi/operation-model.ts +119 -17
- package/src/components/openapi/panel.ts +31 -5
- package/src/components/openapi/param-style.ts +181 -0
- package/src/components/openapi/playground-client.ts +110 -23
- package/src/components/openapi/playground-schema.ts +25 -6
- package/src/components/openapi/request.ts +192 -26
- package/src/components/openapi/schema-tree.ts +209 -0
- package/src/components/openapi/snippets.ts +113 -19
- package/src/components/openapi/validate-json.ts +1 -1
- package/src/components/openapi/ws-client.ts +18 -2
- package/src/core/base-path.ts +69 -8
- package/src/core/config-input.ts +13 -11
- package/src/core/config.ts +18 -4
- package/src/core/diagnostics.ts +219 -30
- package/src/core/directive-diagnostics.ts +99 -0
- package/src/core/frontmatter.ts +21 -18
- package/src/core/graph.ts +120 -11
- package/src/core/heading-markers.ts +5 -18
- package/src/core/i18n-ui.ts +39 -2
- package/src/core/i18n.ts +44 -5
- package/src/core/last-modified.ts +25 -3
- package/src/core/links.ts +7 -4
- package/src/core/load-module.ts +20 -0
- package/src/core/locale-links.ts +22 -18
- package/src/core/manifest.ts +3 -2
- package/src/core/meta.ts +89 -12
- package/src/core/nav-diagnostics.ts +56 -1
- package/src/core/navigation.ts +272 -83
- package/src/core/ordering-prefix.ts +27 -0
- package/src/core/project-graph.ts +50 -6
- package/src/core/safe-href.ts +53 -1
- package/src/core/safe-links.ts +11 -2
- package/src/core/schema.ts +94 -18
- package/src/core/sources/assets.ts +83 -41
- package/src/core/sources/contentful-rich-text.ts +28 -20
- package/src/core/sources/contentful.ts +25 -12
- package/src/core/sources/filesystem.ts +25 -3
- package/src/core/sources/github-releases.ts +131 -11
- package/src/core/sources/lexical.ts +30 -20
- package/src/core/sources/lower.ts +276 -56
- package/src/core/sources/mdx-remote.ts +52 -16
- package/src/core/sources/normalize.ts +456 -119
- package/src/core/sources/notion.ts +77 -34
- package/src/core/sources/obsidian.ts +40 -9
- package/src/core/sources/payload.ts +1 -0
- package/src/core/sources/portable-text.ts +85 -49
- package/src/core/sources/remote.ts +18 -2
- package/src/core/sources/resolve.ts +52 -33
- package/src/core/sources/sanity.ts +1 -0
- package/src/core/sources/strapi-blocks.ts +28 -19
- package/src/core/sources/strapi.ts +1 -0
- package/src/core/sources/types.ts +31 -0
- package/src/core/sources/watch.ts +20 -7
- package/src/core/standard-schema.ts +10 -6
- package/src/core/types.ts +9 -0
- package/src/core/ui-packs/ar.ts +13 -0
- package/src/core/ui-packs/bg.ts +13 -0
- package/src/core/ui-packs/bn.ts +13 -0
- package/src/core/ui-packs/ca.ts +13 -0
- package/src/core/ui-packs/cs.ts +13 -0
- package/src/core/ui-packs/da.ts +13 -0
- package/src/core/ui-packs/de.ts +13 -0
- package/src/core/ui-packs/el.ts +13 -0
- package/src/core/ui-packs/es.ts +13 -0
- package/src/core/ui-packs/fa.ts +13 -0
- package/src/core/ui-packs/fi.ts +13 -0
- package/src/core/ui-packs/fr.ts +13 -0
- package/src/core/ui-packs/he.ts +13 -0
- package/src/core/ui-packs/hi.ts +13 -0
- package/src/core/ui-packs/hr.ts +13 -0
- package/src/core/ui-packs/hu.ts +13 -0
- package/src/core/ui-packs/id.ts +13 -0
- package/src/core/ui-packs/it.ts +13 -0
- package/src/core/ui-packs/ja.ts +13 -0
- package/src/core/ui-packs/ko.ts +13 -0
- package/src/core/ui-packs/nl.ts +13 -0
- package/src/core/ui-packs/no.ts +13 -0
- package/src/core/ui-packs/pl.ts +13 -0
- package/src/core/ui-packs/pt-br.ts +13 -0
- package/src/core/ui-packs/pt.ts +13 -0
- package/src/core/ui-packs/ro.ts +13 -0
- package/src/core/ui-packs/ru.ts +13 -0
- package/src/core/ui-packs/sk.ts +13 -0
- package/src/core/ui-packs/sr.ts +13 -0
- package/src/core/ui-packs/sv.ts +13 -0
- package/src/core/ui-packs/th.ts +13 -0
- package/src/core/ui-packs/tr.ts +13 -0
- package/src/core/ui-packs/uk.ts +13 -0
- package/src/core/ui-packs/vi.ts +13 -0
- package/src/core/ui-packs/zh-tw.ts +13 -0
- package/src/core/ui-packs/zh.ts +13 -0
- package/src/core/version-cut.ts +69 -17
- package/src/core/versions.ts +4 -1
- package/src/deploy/adapters/node.ts +5 -2
- package/src/deploy/adapters/registry.ts +2 -1
- package/src/deploy/adapters/types.ts +13 -1
- package/src/deploy/artifacts.ts +52 -11
- package/src/deploy/cloudflare-negotiation.ts +27 -16
- package/src/deploy/headers.ts +75 -55
- package/src/deploy/node-headers.ts +148 -27
- package/src/deploy/platforms/cloudflare.ts +13 -3
- package/src/deploy/platforms/netlify.ts +83 -5
- package/src/deploy/platforms/node.ts +8 -5
- package/src/deploy/platforms/static.ts +2 -0
- package/src/deploy/platforms/types.ts +15 -0
- package/src/deploy/platforms/vercel.ts +10 -3
- package/src/deploy/redirects.ts +68 -21
- package/src/deploy/robots.ts +2 -2
- package/src/deploy/rss.ts +4 -3
- package/src/deploy/sitemap.ts +7 -5
- package/src/deploy/vercel-negotiation.ts +35 -3
- package/src/eval/agents.ts +10 -2
- package/src/eval/run.ts +25 -0
- package/src/markdown/base-links.ts +74 -29
- package/src/markdown/directives.ts +242 -36
- package/src/markdown/features.ts +17 -0
- package/src/markdown/index.ts +13 -9
- package/src/markdown/mdast.ts +5 -2
- package/src/markdown/relative-links.ts +15 -27
- package/src/markdown/route-snapshot.ts +37 -0
- package/src/og/card.ts +149 -9
- package/src/og/derive.ts +156 -5
- package/src/og/index.ts +1 -0
- package/src/openapi/asyncapi.ts +4 -2
- package/src/openapi/graphql-build.ts +28 -2
- package/src/openapi/model.ts +99 -27
- package/src/openapi/proxy.ts +63 -10
- package/src/openapi/render-mdx.ts +51 -3
- package/src/registry/eject.ts +297 -43
- package/src/search/adapters/version-scope.ts +30 -0
- package/src/search/documents.ts +89 -27
- package/src/search/popular.ts +2 -1
- package/src/search/sync/algolia.ts +36 -2
- package/src/seo/jsonld.ts +7 -3
- package/src/sources/registry.ts +5 -0
- package/src/theme/entry.ts +11 -4
- package/src/translate/agents.ts +6 -1
- package/src/translate/ledger.ts +26 -3
- package/src/translate/meta.ts +76 -24
- package/src/translate/run.ts +14 -8
- package/src/translate/validate.ts +14 -2
- package/src/translate/work-list.ts +91 -19
- package/src/upgrade/upgrade.ts +36 -4
- package/dist/cli/chunk-27g6wdth.js.map +0 -10
- package/dist/cli/chunk-2hn4b8z7.js.map +0 -12
- package/dist/cli/chunk-6crbhc3x.js.map +0 -14
- package/dist/cli/chunk-6hsn950k.js.map +0 -10
- package/dist/cli/chunk-79jhk4py.js.map +0 -35
- package/dist/cli/chunk-82bbrxdn.js +0 -51
- package/dist/cli/chunk-82bbrxdn.js.map +0 -10
- package/dist/cli/chunk-abh8yjkn.js +0 -31
- package/dist/cli/chunk-abh8yjkn.js.map +0 -10
- package/dist/cli/chunk-ce574jw2.js +0 -23
- package/dist/cli/chunk-ce574jw2.js.map +0 -10
- package/dist/cli/chunk-dh8cwk36.js.map +0 -10
- package/dist/cli/chunk-epjnccmv.js.map +0 -10
- package/dist/cli/chunk-fa25z98p.js.map +0 -11
- package/dist/cli/chunk-fs23ddbb.js.map +0 -35
- package/dist/cli/chunk-fz5wtpmh.js.map +0 -10
- package/dist/cli/chunk-jwyddg7y.js.map +0 -15
- package/dist/cli/chunk-kpf8rrjc.js.map +0 -19
- package/dist/cli/chunk-m3vmjgmq.js.map +0 -10
- package/dist/cli/chunk-mb2919y2.js.map +0 -10
- package/dist/cli/chunk-q5163e60.js.map +0 -11
- package/dist/cli/chunk-qkqwkpte.js.map +0 -182
- package/dist/cli/chunk-qs4q5p4e.js.map +0 -10
- package/dist/cli/chunk-s1p84fyh.js.map +0 -10
- package/dist/cli/chunk-wgm7m9qk.js.map +0 -36
- package/dist/cli/chunk-yt5n7ppj.js.map +0 -10
- package/dist/cli/chunk-yw7dm696.js.map +0 -14
- /package/dist/cli/{chunk-s6jhgk0q.js.map → chunk-2eytanqx.js.map} +0 -0
- /package/dist/cli/{chunk-hdpx1tax.js.map → chunk-2z928egk.js.map} +0 -0
- /package/dist/cli/{chunk-ah61y8py.js.map → chunk-364znk6q.js.map} +0 -0
- /package/dist/cli/{chunk-vtk4a6dg.js.map → chunk-8ktnccpt.js.map} +0 -0
- /package/dist/cli/{chunk-fxypxtvm.js.map → chunk-a58773jm.js.map} +0 -0
- /package/dist/cli/{chunk-wm7js3j9.js.map → chunk-b07cmahc.js.map} +0 -0
- /package/dist/cli/{chunk-zxcczpyx.js.map → chunk-r20tn01b.js.map} +0 -0
- /package/dist/cli/{chunk-5shv93fd.js.map → chunk-tkacnehg.js.map} +0 -0
- /package/dist/cli/{chunk-zxh4d9vy.js.map → chunk-tzmab476.js.map} +0 -0
|
@@ -4,6 +4,7 @@ import { mdxToMdast } from "satteri";
|
|
|
4
4
|
import { parseYouTubeId } from "../components/content/youtube.ts";
|
|
5
5
|
import type { ExampleLookup } from "../core/types.ts";
|
|
6
6
|
import { MDX_FEATURES } from "../markdown/features.ts";
|
|
7
|
+
import { readStaticExpression } from "./static-expression.ts";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Downlevel Blume's MDX components to plain Markdown for agent-facing output
|
|
@@ -44,11 +45,19 @@ interface MdxAttribute {
|
|
|
44
45
|
|
|
45
46
|
/** A single source replacement: `[start, end)` byte range → `text`. */
|
|
46
47
|
interface Splice {
|
|
48
|
+
/**
|
|
49
|
+
* The range is a flow (block-level) element, so the line after it must
|
|
50
|
+
* start a block of its own.
|
|
51
|
+
*/
|
|
52
|
+
block: boolean;
|
|
47
53
|
end: number;
|
|
48
54
|
start: number;
|
|
49
55
|
text: string;
|
|
50
56
|
}
|
|
51
57
|
|
|
58
|
+
/** Text that continues on the very next line, with no blank line between. */
|
|
59
|
+
const NEXT_LINE_TEXT = /^[\t ]*\n[\t ]*\S/u;
|
|
60
|
+
|
|
52
61
|
/**
|
|
53
62
|
* A statically-recovered data value. Parsed front matter and evaluated
|
|
54
63
|
* attribute literals are both plain data — scalars, dates, arrays, and
|
|
@@ -135,33 +144,13 @@ export type ComponentMarkdown = (
|
|
|
135
144
|
) => string | null;
|
|
136
145
|
|
|
137
146
|
/**
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* other scope throw and report as not evaluable.
|
|
147
|
+
* Read an element's attributes into a plain props object. An expression
|
|
148
|
+
* attribute (`prop={...}`) is read as literal data and never executed (see
|
|
149
|
+
* `static-expression.ts`): the downleveler also runs over plain `.md` pages
|
|
150
|
+
* and remote content, which nothing else executes. `prop={frontmatter.status}`
|
|
151
|
+
* resolves against the page's front matter, as it does when Astro renders
|
|
152
|
+
* the page; anything else — a call, an import — marks the props lossy.
|
|
145
153
|
*/
|
|
146
|
-
const evaluateExpression = (
|
|
147
|
-
raw: string,
|
|
148
|
-
frontmatter: Record<string, EvaluatedValue> | undefined
|
|
149
|
-
) => {
|
|
150
|
-
try {
|
|
151
|
-
// Build-time eval of the author's own attribute literals; a throw falls
|
|
152
|
-
// back to leaving the JSX verbatim.
|
|
153
|
-
// oxlint-disable-next-line no-new-func
|
|
154
|
-
const value: EvaluatedValue = new Function(
|
|
155
|
-
"frontmatter",
|
|
156
|
-
`"use strict"; return (${raw});`
|
|
157
|
-
)(frontmatter);
|
|
158
|
-
return { ok: true, value };
|
|
159
|
-
} catch {
|
|
160
|
-
return { ok: false, value: undefined };
|
|
161
|
-
}
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
/** Evaluate an element's attributes into a plain props object. */
|
|
165
154
|
const readProps = (
|
|
166
155
|
node: MdastNode,
|
|
167
156
|
frontmatter: Record<string, EvaluatedValue> | undefined
|
|
@@ -180,7 +169,7 @@ const readProps = (
|
|
|
180
169
|
} else if (isString(attribute.value)) {
|
|
181
170
|
props[attribute.name] = attribute.value;
|
|
182
171
|
} else {
|
|
183
|
-
const result =
|
|
172
|
+
const result = readStaticExpression(attribute.value.value, frontmatter);
|
|
184
173
|
if (result.ok) {
|
|
185
174
|
props[attribute.name] = result.value;
|
|
186
175
|
} else {
|
|
@@ -208,14 +197,24 @@ const applySplices = (text: string, splices: Splice[]): string => {
|
|
|
208
197
|
const lineStart = result.lastIndexOf("\n", splice.start - 1) + 1;
|
|
209
198
|
const prefix = result.slice(lineStart, splice.start);
|
|
210
199
|
const indent = /^[\t ]+$/u.test(prefix) ? prefix : "";
|
|
200
|
+
// The JSX's closing tag ended its block; the Markdown standing in for it
|
|
201
|
+
// doesn't. Text on the very next line would read as a lazy continuation
|
|
202
|
+
// of a blockquote or list item (`> Body` then `Next.` joins the quote),
|
|
203
|
+
// so a blank line keeps it out.
|
|
204
|
+
const spliced =
|
|
205
|
+
splice.block &&
|
|
206
|
+
splice.text !== "" &&
|
|
207
|
+
NEXT_LINE_TEXT.test(result.slice(splice.end))
|
|
208
|
+
? `${splice.text}\n`
|
|
209
|
+
: splice.text;
|
|
211
210
|
const replacement = indent
|
|
212
|
-
?
|
|
211
|
+
? spliced
|
|
213
212
|
.split("\n")
|
|
214
213
|
.map((line, index) =>
|
|
215
214
|
index === 0 || line === "" ? line : `${indent}${line}`
|
|
216
215
|
)
|
|
217
216
|
.join("\n")
|
|
218
|
-
:
|
|
217
|
+
: spliced;
|
|
219
218
|
result =
|
|
220
219
|
result.slice(0, splice.start) + replacement + result.slice(splice.end);
|
|
221
220
|
}
|
|
@@ -888,15 +887,21 @@ const renderSlice = (
|
|
|
888
887
|
const splices: Splice[] = [];
|
|
889
888
|
// oxlint-disable-next-line no-use-before-define
|
|
890
889
|
collectSplices(walk, nodes, splices);
|
|
890
|
+
// Start the slice at its line's indent, so a component that opens the
|
|
891
|
+
// slice is indented like the lines after it once replaced (see
|
|
892
|
+
// `applySplices`) and dedents with them; the final trim drops that indent
|
|
893
|
+
// from the first line again.
|
|
894
|
+
const indent = indentAt(walk.source, start);
|
|
895
|
+
const from = start - (indent ?? 0);
|
|
891
896
|
const spliced = applySplices(
|
|
892
|
-
walk.source.slice(
|
|
897
|
+
walk.source.slice(from, end),
|
|
893
898
|
splices.map((splice) => ({
|
|
894
899
|
...splice,
|
|
895
|
-
end: splice.end -
|
|
896
|
-
start: splice.start -
|
|
900
|
+
end: splice.end - from,
|
|
901
|
+
start: splice.start - from,
|
|
897
902
|
}))
|
|
898
903
|
);
|
|
899
|
-
return dedent(spliced,
|
|
904
|
+
return dedent(spliced, indent).trim();
|
|
900
905
|
};
|
|
901
906
|
|
|
902
907
|
/** The element's body as Markdown: the slice covering all of its children. */
|
|
@@ -996,6 +1001,7 @@ const collectSplices = (
|
|
|
996
1001
|
const text = hasOffsets(node) ? downlevelComponentNode(node, walk) : null;
|
|
997
1002
|
if (text !== null && hasOffsets(node)) {
|
|
998
1003
|
out.push({
|
|
1004
|
+
block: node.type === "mdxJsxFlowElement",
|
|
999
1005
|
end: node.position.end.offset,
|
|
1000
1006
|
start: node.position.start.offset,
|
|
1001
1007
|
text,
|
|
@@ -1015,10 +1021,10 @@ const collectSplices = (
|
|
|
1015
1021
|
* over the built-ins: a same-name entry replaces the built-in serializer, and
|
|
1016
1022
|
* one that always returns `null` effectively opts that component out.
|
|
1017
1023
|
*
|
|
1018
|
-
* `frontmatter` is the page's parsed front-matter data.
|
|
1019
|
-
*
|
|
1020
|
-
*
|
|
1021
|
-
*
|
|
1024
|
+
* `frontmatter` is the page's parsed front-matter data. Attribute
|
|
1025
|
+
* expressions that reference it — `prop={frontmatter.status}` — resolve the
|
|
1026
|
+
* way they do when Astro renders the page, and it is handed to serializers on
|
|
1027
|
+
* their context. Expressions are read as literal data, never executed.
|
|
1022
1028
|
*/
|
|
1023
1029
|
export const downlevelComponents = (
|
|
1024
1030
|
source: string,
|
package/src/ai/link-headers.ts
CHANGED
|
@@ -5,7 +5,11 @@ import {
|
|
|
5
5
|
AI_CATALOG_TYPE,
|
|
6
6
|
hasAiCatalog,
|
|
7
7
|
} from "./ai-catalog.ts";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
API_CATALOG_PATH,
|
|
10
|
+
API_CATALOG_TYPE,
|
|
11
|
+
hasApiCatalog,
|
|
12
|
+
} from "./api-catalog.ts";
|
|
9
13
|
import { OPENAPI_PATH } from "./api/paths.ts";
|
|
10
14
|
|
|
11
15
|
/**
|
|
@@ -29,23 +33,33 @@ import { OPENAPI_PATH } from "./api/paths.ts";
|
|
|
29
33
|
* headers. Targets are
|
|
30
34
|
* root-relative under `deployment.base` — RFC 8288 resolves them against the
|
|
31
35
|
* request URL. Returns null when nothing is advertisable.
|
|
36
|
+
*
|
|
37
|
+
* `surface` is where the header is sent. A deployed site (`"build"`) serves
|
|
38
|
+
* every target. The dev server (`"dev"`) serves the routes — the JSON API's
|
|
39
|
+
* OpenAPI document and the Markdown mirror — but not the files `blume build`
|
|
40
|
+
* writes after the build (the API and AI catalogs, `agent-readability.json`,
|
|
41
|
+
* `llms.txt`), so its header leaves those out rather than point agents at
|
|
42
|
+
* 404s.
|
|
32
43
|
*/
|
|
33
44
|
export const buildHomeLinkHeader = (
|
|
34
45
|
config: ResolvedConfig,
|
|
35
|
-
routePaths: readonly string[]
|
|
46
|
+
routePaths: readonly string[],
|
|
47
|
+
surface: "build" | "dev" = "build"
|
|
36
48
|
): string | null => {
|
|
37
49
|
const deployBase = normalizeBasePath(config.deployment.options.base);
|
|
50
|
+
const artifacts = surface === "build";
|
|
38
51
|
const links: string[] = [];
|
|
39
52
|
// RFC 9727 §3: the api-catalog relation is how a homepage advertises the
|
|
40
|
-
// well-known catalog.
|
|
41
|
-
|
|
53
|
+
// well-known catalog. Its type carries the RFC 9727 profile, whose quotes
|
|
54
|
+
// are escaped inside the quoted `type` value (RFC 8288 quoted-string).
|
|
55
|
+
if (artifacts && hasApiCatalog(config)) {
|
|
42
56
|
links.push(
|
|
43
|
-
`<${deployBase}${API_CATALOG_PATH}>; rel="api-catalog"; type="
|
|
57
|
+
`<${deployBase}${API_CATALOG_PATH}>; rel="api-catalog"; type="${API_CATALOG_TYPE.replaceAll('"', String.raw`\"`)}"`
|
|
44
58
|
);
|
|
45
59
|
}
|
|
46
60
|
// The ai-catalog spec's own relation for its well-known document, the
|
|
47
61
|
// header form of the `<link rel="ai-catalog">` every page carries.
|
|
48
|
-
if (hasAiCatalog(config)) {
|
|
62
|
+
if (artifacts && hasAiCatalog(config)) {
|
|
49
63
|
links.push(
|
|
50
64
|
`<${deployBase}${AI_CATALOG_PATH}>; rel="ai-catalog"; type="${AI_CATALOG_TYPE}"`
|
|
51
65
|
);
|
|
@@ -57,12 +71,12 @@ export const buildHomeLinkHeader = (
|
|
|
57
71
|
`<${deployBase}${OPENAPI_PATH}>; rel="service-desc"; type="application/json"`
|
|
58
72
|
);
|
|
59
73
|
}
|
|
60
|
-
if (config.agents.agentReadability) {
|
|
74
|
+
if (artifacts && config.agents.agentReadability) {
|
|
61
75
|
links.push(
|
|
62
76
|
`<${deployBase}/agent-readability.json>; rel="describedby"; type="application/json"`
|
|
63
77
|
);
|
|
64
78
|
}
|
|
65
|
-
if (config.agents.llmsTxt.enabled) {
|
|
79
|
+
if (artifacts && config.agents.llmsTxt.enabled) {
|
|
66
80
|
links.push(
|
|
67
81
|
`<${deployBase}/llms.txt>; rel="describedby"; type="text/plain"`
|
|
68
82
|
);
|
package/src/ai/llms.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mountBasePath, normalizeBasePath } from "../core/base-path.ts";
|
|
2
2
|
import { rewriteRelativeImages } from "../core/content-assets.ts";
|
|
3
3
|
import matter from "../core/frontmatter.ts";
|
|
4
4
|
import type { BlumeProject } from "../core/project-graph.ts";
|
|
@@ -17,14 +17,22 @@ import type { SkillArtifact } from "./skills.ts";
|
|
|
17
17
|
import { applyAgentVisibility } from "./visibility.ts";
|
|
18
18
|
|
|
19
19
|
// Routes carry `basePath`; a `deployment.base` subdirectory is layered on top —
|
|
20
|
-
// with or without a `site` (the mcp.json convention)
|
|
21
|
-
//
|
|
22
|
-
//
|
|
20
|
+
// with or without a `site` (the mcp.json convention), and even over a route
|
|
21
|
+
// that starts with the base's own name — so the emitted URL matches where the
|
|
22
|
+
// page is served. Encoded like the sitemap: a route with spaces or non-ASCII
|
|
23
|
+
// must still yield a valid Markdown link.
|
|
23
24
|
const pageUrl = (route: string, site?: string, base = ""): string => {
|
|
24
|
-
const path =
|
|
25
|
+
const path = mountBasePath(base, route);
|
|
25
26
|
return encodeURI(site ? absoluteUrl(site, path) : path);
|
|
26
27
|
};
|
|
27
28
|
|
|
29
|
+
/**
|
|
30
|
+
* A title as Markdown link text. An unescaped bracket in it — `[Beta] Webhooks`
|
|
31
|
+
* — closes or nests the text early, and the line stops being a link.
|
|
32
|
+
*/
|
|
33
|
+
const linkText = (title: string): string =>
|
|
34
|
+
title.replaceAll(/[\\[\]]/gu, String.raw`\$&`);
|
|
35
|
+
|
|
28
36
|
/** Inputs only the build has: the published skills, collected once per build. */
|
|
29
37
|
export interface LlmsIndexOptions {
|
|
30
38
|
/**
|
|
@@ -176,7 +184,7 @@ export const buildLlmsIndex = (
|
|
|
176
184
|
const line = (page: PageRecord): string => {
|
|
177
185
|
seen.add(page.route);
|
|
178
186
|
const summary = page.description ? `: ${page.description}` : "";
|
|
179
|
-
return `- [${page.title}](${pageUrl(page.route, site, base)})${summary}`;
|
|
187
|
+
return `- [${linkText(page.title)}](${pageUrl(page.route, site, base)})${summary}`;
|
|
180
188
|
};
|
|
181
189
|
|
|
182
190
|
// One nav level -> Markdown blocks: the level's loose pages as a link list,
|
|
@@ -259,7 +267,10 @@ export const buildLlmsIndex = (
|
|
|
259
267
|
blocks.push(
|
|
260
268
|
"## RSS Feeds",
|
|
261
269
|
feeds
|
|
262
|
-
.map(
|
|
270
|
+
.map(
|
|
271
|
+
(feed) =>
|
|
272
|
+
`- [${linkText(feed.title)}](${pageUrl(feed.path, site, base)})`
|
|
273
|
+
)
|
|
263
274
|
.join("\n")
|
|
264
275
|
);
|
|
265
276
|
}
|
|
@@ -312,10 +323,11 @@ const buildFull = async (project: BlumeProject): Promise<string> => {
|
|
|
312
323
|
source: raw,
|
|
313
324
|
sourcePath: page.sourcePath,
|
|
314
325
|
});
|
|
315
|
-
// Relative page links too: a reader of one flat file has no page URL
|
|
316
|
-
// to resolve `./install` against.
|
|
317
|
-
raw = rewriteLinks(raw, page);
|
|
318
326
|
}
|
|
327
|
+
// Page links too: a reader of one flat file has no page URL to resolve
|
|
328
|
+
// `./install` against, and `/install` needs the base the site is
|
|
329
|
+
// served under.
|
|
330
|
+
raw = rewriteLinks(raw, page);
|
|
319
331
|
// Resolve `<Visibility>` audiences (web-only content omitted from the
|
|
320
332
|
// agent-facing output, agents-only unwrapped), then downlevel supported
|
|
321
333
|
// components to plain Markdown.
|
package/src/ai/markdown.ts
CHANGED
|
@@ -8,6 +8,7 @@ import matter from "../core/frontmatter.ts";
|
|
|
8
8
|
import type { BlumeProject } from "../core/project-graph.ts";
|
|
9
9
|
import { readExpandedEntryText } from "../core/sources/read.ts";
|
|
10
10
|
import type { RouteManifestEntry } from "../core/types.ts";
|
|
11
|
+
import { advertisedConfig, servesMcp } from "./agent-surface.ts";
|
|
11
12
|
import { buildChangelogIndexMarkdown } from "./changelog-markdown.ts";
|
|
12
13
|
import { downlevelComponents } from "./component-markdown.ts";
|
|
13
14
|
import { buildLlmsIndex } from "./llms.ts";
|
|
@@ -80,13 +81,14 @@ export const buildRawMarkdown = async (
|
|
|
80
81
|
source: text,
|
|
81
82
|
sourcePath: route.sourcePath,
|
|
82
83
|
});
|
|
83
|
-
// `./install` means the page's sibling, not whatever the `.md` URL
|
|
84
|
-
// an agent fetched resolves it to.
|
|
85
|
-
text = rewriteLinks(text, {
|
|
86
|
-
route: route.path,
|
|
87
|
-
sourcePath: route.sourcePath,
|
|
88
|
-
});
|
|
89
84
|
}
|
|
85
|
+
// `./install` means the page's sibling, not whatever the `.md` URL an
|
|
86
|
+
// agent fetched resolves it to, and `/install` gains the base and the
|
|
87
|
+
// page's locale the rendered link has — remote content included.
|
|
88
|
+
text = rewriteLinks(text, {
|
|
89
|
+
route: route.path,
|
|
90
|
+
sourcePath: route.sourcePath,
|
|
91
|
+
});
|
|
90
92
|
const source = applyAgentVisibility(text);
|
|
91
93
|
// The `.md` variant keeps the front-matter block in the output, but its
|
|
92
94
|
// data must also be in scope for `prop={frontmatter.*}` expressions.
|
|
@@ -97,19 +99,29 @@ export const buildRawMarkdown = async (
|
|
|
97
99
|
})
|
|
98
100
|
);
|
|
99
101
|
const map = Object.fromEntries(entries);
|
|
102
|
+
const userPages = project.context.pagesRoot
|
|
103
|
+
? await discoverPages(project.context.pagesRoot)
|
|
104
|
+
: [];
|
|
100
105
|
// A landing-page homepage (user `.astro` page, or no home route at all) has
|
|
101
106
|
// no Markdown source, but agents negotiating `Accept: text/markdown` on `/`
|
|
102
107
|
// still expect a Markdown answer. The llms.txt index — the machine-readable
|
|
103
108
|
// representation of the site a landing page fronts — becomes its mirror, so
|
|
104
|
-
// `/index.md` always exists (see `markdownRoutePaths`).
|
|
109
|
+
// `/index.md` always exists (see `markdownRoutePaths`). Like llms.txt, it
|
|
110
|
+
// leaves out an MCP server a page's route kept from being generated.
|
|
105
111
|
if (!map["/"]) {
|
|
106
|
-
map["/"] = {
|
|
112
|
+
map["/"] = {
|
|
113
|
+
mdx: buildLlmsIndex({
|
|
114
|
+
...project,
|
|
115
|
+
config: advertisedConfig(project.config, {
|
|
116
|
+
mcp: servesMcp(project, userPages),
|
|
117
|
+
// Skills are collected at the end of the build, after this runs.
|
|
118
|
+
skills: Boolean(project.config.agents.skills),
|
|
119
|
+
}),
|
|
120
|
+
}),
|
|
121
|
+
};
|
|
107
122
|
}
|
|
108
123
|
// The generated changelog index has no source either; its release list is
|
|
109
124
|
// its mirror, so `/changelog.md` and `get_page` answer as the page does.
|
|
110
|
-
const userPages = project.context.pagesRoot
|
|
111
|
-
? await discoverPages(project.context.pagesRoot)
|
|
112
|
-
: [];
|
|
113
125
|
if (hasGeneratedChangelog(project, userPages)) {
|
|
114
126
|
map[CHANGELOG_INDEX_ROUTE] = { mdx: buildChangelogIndexMarkdown(project) };
|
|
115
127
|
}
|
package/src/ai/mcp/discovery.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mountBasePath } from "../../core/base-path.ts";
|
|
2
2
|
import { absoluteUrl, siteRoot } from "../../core/site-url.ts";
|
|
3
3
|
import { trimChar } from "../../core/trim.ts";
|
|
4
4
|
import { MCP_TOOLS } from "./tools.ts";
|
|
@@ -18,7 +18,7 @@ export interface McpDiscoveryInput {
|
|
|
18
18
|
const serverUrl = (input: McpDiscoveryInput): string => {
|
|
19
19
|
// The endpoint is a generated Astro page, so it's served under
|
|
20
20
|
// `deployment.base` like every other route (the sitemap/llms.txt convention).
|
|
21
|
-
const path =
|
|
21
|
+
const path = mountBasePath(input.base, input.route);
|
|
22
22
|
// Concatenate rather than `new URL(path, site)` — a root-absolute path
|
|
23
23
|
// would drop the base path of a subpath deployment (`acme.com/docs`).
|
|
24
24
|
return input.site ? absoluteUrl(input.site, path) : path;
|
package/src/ai/mcp/query.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
mountBasePath,
|
|
4
5
|
normalizeRoute as normalizePageRoute,
|
|
5
6
|
stripBasePath,
|
|
6
|
-
withBasePath,
|
|
7
7
|
} from "../../core/base-path.ts";
|
|
8
8
|
import { absoluteUrl } from "../../core/site-url.ts";
|
|
9
9
|
import { trimEnd } from "../../core/trim.ts";
|
|
@@ -244,7 +244,7 @@ export const normalizeRoute = (input: string, data: McpData): string => {
|
|
|
244
244
|
export const urlFor = (route: string, data: McpData): string => {
|
|
245
245
|
// Routes are base-less manifest paths; layer `deployment.base` on top so the
|
|
246
246
|
// URL matches where the page is served (the sitemap/llms.txt convention).
|
|
247
|
-
const path =
|
|
247
|
+
const path = mountBasePath(data.base, route);
|
|
248
248
|
// Concatenate rather than `new URL(path, site)` — a root-absolute path
|
|
249
249
|
// would drop the base path of a subpath deployment (`acme.com/docs`).
|
|
250
250
|
return data.site ? absoluteUrl(data.site, path) : path;
|
package/src/ai/mcp/server.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
2
2
|
import type { ServerOptions } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
3
|
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
ErrorCode,
|
|
6
6
|
ListResourcesRequestSchema,
|
|
7
7
|
ListToolsRequestSchema,
|
|
8
8
|
McpError,
|
|
@@ -10,7 +10,8 @@ import {
|
|
|
10
10
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import { mountBasePath } from "../../core/base-path.ts";
|
|
14
|
+
import { readCappedText } from "../../core/request-body.ts";
|
|
14
15
|
import type { McpData } from "./data.ts";
|
|
15
16
|
import {
|
|
16
17
|
createIndexProvider,
|
|
@@ -42,6 +43,12 @@ const RESOURCE_MIME_TYPE = "text/markdown";
|
|
|
42
43
|
const RESOURCE_NOT_FOUND = -32_002;
|
|
43
44
|
/** URI scheme for page resources when no `deployment.site` is configured. */
|
|
44
45
|
const LOCAL_RESOURCE_SCHEME = "blume:";
|
|
46
|
+
/**
|
|
47
|
+
* The largest request body the HTTP endpoint reads. A JSON-RPC call to these
|
|
48
|
+
* tools is a few hundred bytes; the cap keeps one oversized POST from being
|
|
49
|
+
* buffered whole, as the assistant route's does.
|
|
50
|
+
*/
|
|
51
|
+
const MCP_BODY_LIMIT_BYTES = 65_536;
|
|
45
52
|
|
|
46
53
|
const CORS_HEADERS = {
|
|
47
54
|
"Access-Control-Allow-Headers":
|
|
@@ -97,7 +104,7 @@ interface PageResource {
|
|
|
97
104
|
const resourceUri = (route: string, data: McpData): string =>
|
|
98
105
|
data.site
|
|
99
106
|
? urlFor(route, data)
|
|
100
|
-
: `${LOCAL_RESOURCE_SCHEME}${
|
|
107
|
+
: `${LOCAL_RESOURCE_SCHEME}${mountBasePath(data.base, route)}`;
|
|
101
108
|
|
|
102
109
|
/** The `pages` key a resource URI (either form, or a bare route) names. */
|
|
103
110
|
const resourceRoute = (uri: string, data: McpData): string =>
|
|
@@ -108,6 +115,20 @@ const resourceRoute = (uri: string, data: McpData): string =>
|
|
|
108
115
|
data
|
|
109
116
|
);
|
|
110
117
|
|
|
118
|
+
/**
|
|
119
|
+
* The `tools/call` params, as far as the server reads them before dispatch:
|
|
120
|
+
* each tool validates its own `arguments` against `TOOL_INPUTS`.
|
|
121
|
+
*/
|
|
122
|
+
const TOOL_CALL_PARAMS = z.object({
|
|
123
|
+
arguments: z.record(z.string(), z.unknown()).optional(),
|
|
124
|
+
name: z.string(),
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
/** A tool call's `arguments` object, before the tool's own validation. */
|
|
128
|
+
type ToolArguments = NonNullable<
|
|
129
|
+
z.output<typeof TOOL_CALL_PARAMS>["arguments"]
|
|
130
|
+
>;
|
|
131
|
+
|
|
111
132
|
/** A tool call's text result, marked as an error when `isError` is set. */
|
|
112
133
|
const text = (value: string, isError = false) => {
|
|
113
134
|
const content = [{ text: value, type: "text" as const }];
|
|
@@ -165,9 +186,7 @@ export const buildServer = (
|
|
|
165
186
|
};
|
|
166
187
|
});
|
|
167
188
|
|
|
168
|
-
|
|
169
|
-
const { arguments: args = {}, name } = request.params;
|
|
170
|
-
|
|
189
|
+
const callTool = async (name: string, args: ToolArguments) => {
|
|
171
190
|
if (name === "search_docs") {
|
|
172
191
|
const results = await searchDocs(
|
|
173
192
|
data,
|
|
@@ -206,12 +225,44 @@ export const buildServer = (
|
|
|
206
225
|
return text(JSON.stringify(result.navigation, null, 2));
|
|
207
226
|
}
|
|
208
227
|
|
|
209
|
-
|
|
210
|
-
|
|
228
|
+
// The MCP spec answers an unknown tool with a protocol error, not a
|
|
229
|
+
// failed tool result.
|
|
230
|
+
throw new McpError(ErrorCode.InvalidParams, `Unknown tool: ${name}`);
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// `tools/call` goes through the fallback handler rather than a registered
|
|
234
|
+
// one: the SDK parses a registered handler's request against its own
|
|
235
|
+
// schema first and reports a malformed call (`arguments` that aren't an
|
|
236
|
+
// object) as -32603 Internal error carrying a raw Zod dump. Reading the
|
|
237
|
+
// params here answers it as -32602 Invalid params with a short message.
|
|
238
|
+
server.fallbackRequestHandler = async (request) => {
|
|
239
|
+
if (request.method !== "tools/call") {
|
|
240
|
+
throw new McpError(ErrorCode.MethodNotFound, "Method not found");
|
|
241
|
+
}
|
|
242
|
+
const params = TOOL_CALL_PARAMS.safeParse(request.params);
|
|
243
|
+
if (!params.success) {
|
|
244
|
+
throw new McpError(
|
|
245
|
+
ErrorCode.InvalidParams,
|
|
246
|
+
"Invalid tools/call params: `name` must be a string and `arguments`, when given, an object."
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
return await callTool(params.data.name, params.data.arguments ?? {});
|
|
250
|
+
};
|
|
211
251
|
|
|
212
252
|
return server;
|
|
213
253
|
};
|
|
214
254
|
|
|
255
|
+
/** A JSON-RPC error response the transport would send, for errors raised before it runs. */
|
|
256
|
+
const jsonRpcError = (
|
|
257
|
+
status: number,
|
|
258
|
+
code: number,
|
|
259
|
+
message: string
|
|
260
|
+
): Response =>
|
|
261
|
+
Response.json(
|
|
262
|
+
{ error: { code, message }, id: null, jsonrpc: "2.0" },
|
|
263
|
+
{ headers: CORS_HEADERS, status }
|
|
264
|
+
);
|
|
265
|
+
|
|
215
266
|
/**
|
|
216
267
|
* Build a stateless Streamable-HTTP MCP request handler from a data snapshot.
|
|
217
268
|
*
|
|
@@ -239,6 +290,26 @@ export const createMcpFetchHandler = (
|
|
|
239
290
|
});
|
|
240
291
|
}
|
|
241
292
|
|
|
293
|
+
// The transport would read the body with `request.json()`, buffering
|
|
294
|
+
// whatever a client sends; read it under the cap first and hand the
|
|
295
|
+
// transport a copy.
|
|
296
|
+
let forwarded = request;
|
|
297
|
+
if (request.method === "POST") {
|
|
298
|
+
const body = await readCappedText(request, MCP_BODY_LIMIT_BYTES);
|
|
299
|
+
if (body === undefined) {
|
|
300
|
+
return jsonRpcError(
|
|
301
|
+
413,
|
|
302
|
+
ErrorCode.InvalidRequest,
|
|
303
|
+
"Request too large: the body must be at most 64 KB."
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
forwarded = new Request(request.url, {
|
|
307
|
+
body,
|
|
308
|
+
headers: request.headers,
|
|
309
|
+
method: "POST",
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
242
313
|
const server = buildServer(data, index);
|
|
243
314
|
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
244
315
|
enableJsonResponse: true,
|
|
@@ -248,7 +319,7 @@ export const createMcpFetchHandler = (
|
|
|
248
319
|
sessionIdGenerator: undefined,
|
|
249
320
|
});
|
|
250
321
|
await server.connect(transport);
|
|
251
|
-
const response = await transport.handleRequest(
|
|
322
|
+
const response = await transport.handleRequest(forwarded);
|
|
252
323
|
|
|
253
324
|
const headers = new Headers(response.headers);
|
|
254
325
|
for (const [key, value] of Object.entries(CORS_HEADERS)) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { mountBasePath, normalizeBasePath } from "../core/base-path.ts";
|
|
1
2
|
import type { ApiOperationRef, OpenApiData } from "../openapi/model.ts";
|
|
2
3
|
import { operationOf, specAddresses, specOf } from "../openapi/model.ts";
|
|
3
4
|
import { asSentence } from "../openapi/sentence.ts";
|
|
@@ -20,10 +21,14 @@ const inlineText = (text: string): string =>
|
|
|
20
21
|
.replaceAll(/\s+/gu, " ")
|
|
21
22
|
.replaceAll(/[\\`*_[\]<>~]/gu, String.raw`\$&`);
|
|
22
23
|
|
|
23
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* One operation as a link, for the tag listings. `href` is the operation's
|
|
26
|
+
* served URL: its route under the deployment base.
|
|
27
|
+
*/
|
|
24
28
|
const listItem = (
|
|
25
29
|
signature: string,
|
|
26
|
-
|
|
30
|
+
href: string,
|
|
31
|
+
operation: Pick<ApiOperationRef, "deprecated" | "summary">
|
|
27
32
|
): string => {
|
|
28
33
|
const tail = [
|
|
29
34
|
// Title-like summaries ("Get a flag") need their own period before the
|
|
@@ -33,7 +38,7 @@ const listItem = (
|
|
|
33
38
|
]
|
|
34
39
|
.filter(Boolean)
|
|
35
40
|
.join(" ");
|
|
36
|
-
return `- [${inlineCode(signature)}](${linkDestination(
|
|
41
|
+
return `- [${inlineCode(signature)}](${linkDestination(href)})${tail ? ` — ${tail}` : ""}`;
|
|
37
42
|
};
|
|
38
43
|
|
|
39
44
|
/**
|
|
@@ -60,9 +65,16 @@ const listItem = (
|
|
|
60
65
|
* endpoint and what it does is the part that was missing altogether.
|
|
61
66
|
*
|
|
62
67
|
* `specs` is the parsed `blume:openapi` data — empty when the project has no
|
|
63
|
-
* API reference, in which case every serializer declines.
|
|
68
|
+
* API reference, in which case every serializer declines. `deployBase` is the
|
|
69
|
+
* configured `deployment.base`: operation routes carry `basePath` but not it,
|
|
70
|
+
* and the links written here are made after the agent surfaces rebase the
|
|
71
|
+
* page's own links, so they take it here, as the rendered page's do.
|
|
64
72
|
*/
|
|
65
|
-
export const openapiComponentSerializers = (
|
|
73
|
+
export const openapiComponentSerializers = (
|
|
74
|
+
specs: OpenApiData,
|
|
75
|
+
deployBase?: string
|
|
76
|
+
) => {
|
|
77
|
+
const base = normalizeBasePath(deployBase);
|
|
66
78
|
const spec = (source: EvaluatedValue) =>
|
|
67
79
|
isString(source) ? specOf(specs, source) : undefined;
|
|
68
80
|
|
|
@@ -97,7 +109,11 @@ export const openapiComponentSerializers = (specs: OpenApiData) => {
|
|
|
97
109
|
const items = Object.values(data.operations)
|
|
98
110
|
.filter((operation) => operation.tagSlug === tag)
|
|
99
111
|
.map((operation) =>
|
|
100
|
-
listItem(
|
|
112
|
+
listItem(
|
|
113
|
+
operationSignature(data, operation),
|
|
114
|
+
mountBasePath(base, operation.route),
|
|
115
|
+
operation
|
|
116
|
+
)
|
|
101
117
|
);
|
|
102
118
|
return items.length > 0 ? items.join("\n") : null;
|
|
103
119
|
},
|