blume 2.1.0 → 2.1.2
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 +110 -0
- package/dist/cli/{chunk-ga0pf4aj.js → chunk-0n9xkp3d.js} +12 -3
- package/dist/cli/{chunk-ga0pf4aj.js.map → chunk-0n9xkp3d.js.map} +3 -3
- package/dist/cli/{chunk-43zcf4gz.js → chunk-0pmb4tr4.js} +541 -368
- package/dist/cli/chunk-0pmb4tr4.js.map +35 -0
- package/dist/cli/{chunk-9geymqns.js → chunk-0w6hs479.js} +15 -15
- package/dist/cli/{chunk-xkz5xbtv.js → chunk-124fvmzq.js} +2802 -1422
- package/dist/cli/chunk-124fvmzq.js.map +220 -0
- package/dist/cli/chunk-150jk7km.js +52 -0
- package/dist/cli/chunk-150jk7km.js.map +10 -0
- package/dist/cli/{chunk-y4ykbark.js → chunk-1f882900.js} +2 -2
- package/dist/cli/{chunk-5ffywsmb.js → chunk-297ajdcv.js} +8 -6
- package/dist/cli/{chunk-5ffywsmb.js.map → chunk-297ajdcv.js.map} +3 -3
- package/dist/cli/{chunk-h2a48yhd.js → chunk-32khwcb6.js} +120 -16
- package/dist/cli/{chunk-h2a48yhd.js.map → chunk-32khwcb6.js.map} +10 -10
- package/dist/cli/{chunk-5cg2rzgf.js → chunk-36bxs9c7.js} +20 -49
- package/dist/cli/{chunk-5cg2rzgf.js.map → chunk-36bxs9c7.js.map} +4 -5
- package/dist/cli/{chunk-py62fewh.js → chunk-53jqw8z0.js} +3 -3
- package/dist/cli/{chunk-5wj2qwn2.js → chunk-58bt3hag.js} +4 -4
- package/dist/cli/{chunk-e7vvk5ya.js → chunk-5cmts6dv.js} +3 -3
- package/dist/cli/{chunk-y5h4r2y0.js → chunk-60hz265r.js} +15 -5
- package/dist/cli/chunk-60hz265r.js.map +10 -0
- package/dist/cli/chunk-74wfhv79.js +65 -0
- package/dist/cli/chunk-74wfhv79.js.map +10 -0
- package/dist/cli/{chunk-g2z9x01v.js → chunk-9qzgg6mj.js} +3 -3
- package/dist/cli/{chunk-q3wxxq39.js → chunk-a57yh0h9.js} +5 -5
- package/dist/cli/{chunk-713mngdq.js → chunk-ave1wcbr.js} +76 -10
- package/dist/cli/chunk-ave1wcbr.js.map +14 -0
- package/dist/cli/{chunk-2n4ps1b3.js → chunk-bqxaqn5p.js} +40 -22
- package/dist/cli/chunk-bqxaqn5p.js.map +15 -0
- package/dist/cli/{chunk-jnwj5kjj.js → chunk-bxwhtvw8.js} +20 -7
- package/dist/cli/{chunk-jnwj5kjj.js.map → chunk-bxwhtvw8.js.map} +3 -3
- package/dist/cli/chunk-ckrcm08h.js +324 -0
- package/dist/cli/chunk-ckrcm08h.js.map +14 -0
- package/dist/cli/{chunk-2f3ndkpw.js → chunk-desvm72z.js} +3 -3
- package/dist/cli/{chunk-gp84za0h.js → chunk-dhwtqnnd.js} +2 -2
- package/dist/cli/{chunk-4h8z9vv5.js → chunk-dpy8qdjf.js} +93 -47
- package/dist/cli/chunk-dpy8qdjf.js.map +17 -0
- package/dist/cli/{chunk-2f7n4qzt.js → chunk-dqhg54mr.js} +1 -1
- package/dist/cli/{chunk-0gz36akr.js → chunk-dv2z0rzf.js} +21 -5
- package/dist/cli/chunk-dv2z0rzf.js.map +12 -0
- package/dist/cli/{chunk-cm89scdv.js → chunk-es8h0v3j.js} +9 -11
- package/dist/cli/{chunk-cm89scdv.js.map → chunk-es8h0v3j.js.map} +3 -3
- package/dist/cli/{chunk-zhvd6adm.js → chunk-gfc5fqvf.js} +14 -14
- package/dist/cli/{chunk-nd1821sk.js → chunk-gxtg6ed2.js} +33 -19
- package/dist/cli/chunk-gxtg6ed2.js.map +10 -0
- package/dist/cli/{chunk-p804f3pe.js → chunk-hvcxng5f.js} +6 -6
- package/dist/cli/{chunk-ta27feev.js → chunk-j1x92mx9.js} +21 -2
- package/dist/cli/chunk-j1x92mx9.js.map +10 -0
- package/dist/cli/{chunk-rekqz4qg.js → chunk-k3gr1nm9.js} +19 -16
- package/dist/cli/chunk-k3gr1nm9.js.map +10 -0
- package/dist/cli/{chunk-1n228xqb.js → chunk-kdpzw38a.js} +2 -2
- package/dist/cli/{chunk-cy5bm0j0.js → chunk-ms9z66nz.js} +2 -2
- package/dist/cli/{chunk-cy5bm0j0.js.map → chunk-ms9z66nz.js.map} +2 -2
- package/dist/cli/{chunk-zf748avv.js → chunk-nbyvgnnm.js} +193 -286
- package/dist/cli/chunk-nbyvgnnm.js.map +36 -0
- package/dist/cli/{chunk-6bm0xpgd.js → chunk-s0bdnc7v.js} +2 -2
- package/dist/cli/{chunk-yxmk4pec.js → chunk-sc93apfs.js} +17 -16
- package/dist/cli/{chunk-yxmk4pec.js.map → chunk-sc93apfs.js.map} +3 -3
- package/dist/cli/{chunk-cmtcnh9f.js → chunk-tfycddbt.js} +2 -2
- package/dist/cli/{chunk-9xr4dgee.js → chunk-twy7psdz.js} +29 -16
- package/dist/cli/chunk-twy7psdz.js.map +10 -0
- package/dist/cli/{chunk-ntv4qrt7.js → chunk-y4msrwm9.js} +15 -15
- package/dist/cli/{chunk-yj17vs39.js → chunk-ydp3065p.js} +5 -5
- package/dist/cli/index.js +24 -19
- package/dist/cli/index.js.map +3 -3
- package/dist/types/ai/ask-context.d.ts +6 -2
- package/dist/types/ai/ask.d.ts +24 -5
- package/dist/types/ai/component-markdown.d.ts +2 -0
- package/dist/types/ai/openapi-components.d.ts +4 -1
- package/dist/types/analytics/clics.d.ts +37 -0
- package/dist/types/analytics/index.d.ts +2 -0
- package/dist/types/analytics/schema.d.ts +18 -0
- package/dist/types/astro/integration.d.ts +11 -4
- package/dist/types/astro/markdown-negotiation.d.ts +3 -0
- package/dist/types/astro/pages.d.ts +10 -0
- package/dist/types/cli/init/scaffold.d.ts +1 -1
- package/dist/types/components/content/api-field.d.ts +4 -1
- package/dist/types/components/content/api-page.d.ts +6 -1
- package/dist/types/components/openapi/code-samples.d.ts +29 -0
- package/dist/types/components/openapi/helpers.d.ts +22 -1
- package/dist/types/components/openapi/security.d.ts +10 -0
- package/dist/types/components/openapi/snippets.d.ts +60 -0
- package/dist/types/core/changelog-index.d.ts +8 -0
- package/dist/types/core/config-input.d.ts +37 -6
- package/dist/types/core/data.d.ts +4 -0
- package/dist/types/core/i18n-ui.d.ts +2 -0
- package/dist/types/core/links.d.ts +8 -0
- package/dist/types/core/powered-by.d.ts +12 -0
- package/dist/types/core/probe.d.ts +2 -0
- package/dist/types/core/project-graph.d.ts +6 -0
- package/dist/types/core/redirect-patterns.d.ts +11 -3
- package/dist/types/core/schema.d.ts +228 -3
- package/dist/types/core/sources/cache.d.ts +3 -0
- package/dist/types/core/sources/contentful-rich-text.d.ts +11 -2
- package/dist/types/core/sources/contentful.d.ts +17 -1
- package/dist/types/core/sources/lexical.d.ts +5 -0
- package/dist/types/core/sources/lower.d.ts +10 -2
- package/dist/types/core/sources/normalize.d.ts +20 -1
- package/dist/types/core/sources/notion.d.ts +8 -0
- package/dist/types/core/sources/remote.d.ts +37 -10
- package/dist/types/core/sources/sanity.d.ts +1 -1
- package/dist/types/core/types.d.ts +17 -0
- package/dist/types/deploy/adapter-output.d.ts +2 -1
- package/dist/types/deploy/adapters/cloudflare.d.ts +7 -5
- package/dist/types/deploy/adapters/node.d.ts +58 -23
- package/dist/types/deploy/adapters/registry.d.ts +10 -0
- package/dist/types/deploy/adapters/types.d.ts +3 -2
- package/dist/types/deploy/cloudflare-negotiation.d.ts +5 -2
- package/dist/types/deploy/node-headers.d.ts +12 -4
- package/dist/types/deploy/platforms/cloudflare.d.ts +10 -7
- package/dist/types/deploy/platforms/node.d.ts +3 -1
- package/dist/types/deploy/platforms/types.d.ts +18 -7
- package/dist/types/deploy/platforms/vercel.d.ts +21 -2
- package/dist/types/deploy/redirects.d.ts +17 -1
- package/dist/types/deploy/sitemap.d.ts +2 -1
- package/dist/types/deploy/vercel-negotiation.d.ts +44 -21
- package/dist/types/narration/build.d.ts +34 -12
- package/dist/types/narration/provider.d.ts +96 -8
- package/dist/types/openapi/checks.d.ts +27 -0
- package/dist/types/openapi/graphql.d.ts +6 -0
- package/dist/types/openapi/model.d.ts +15 -4
- package/dist/types/openapi/parse.d.ts +3 -0
- package/dist/types/openapi/references.d.ts +16 -0
- package/dist/types/openapi/sentence.d.ts +11 -1
- package/dist/types/ratelimit/index.d.ts +1 -1
- package/dist/types/ratelimit/schema.d.ts +8 -0
- package/dist/types/ratelimit/upstash.d.ts +39 -14
- package/dist/types/reference/graphql.d.ts +76 -1
- package/dist/types/reference/index.d.ts +1 -1
- package/dist/types/reference/openapi.d.ts +2 -2
- package/dist/types/reference/options.d.ts +37 -1
- package/dist/types/reference/schema.d.ts +84 -0
- package/dist/types/search/adapters/registry.d.ts +3 -0
- package/dist/types/search/adapters/typesense.d.ts +14 -4
- package/dist/types/search/build.d.ts +3 -1
- package/dist/types/search/orama-index.d.ts +9 -9
- package/dist/types/search/sync/algolia.d.ts +5 -2
- package/dist/types/search/sync/index.d.ts +3 -1
- package/dist/types/search/sync/typesense.d.ts +11 -7
- package/dist/types/seo/jsonld.d.ts +11 -6
- package/dist/types/sources/contentful.d.ts +16 -0
- package/dist/types/sources/github-releases.d.ts +8 -1
- package/dist/types/sources/notion.d.ts +8 -0
- package/dist/types/sources/payload.d.ts +2 -0
- package/dist/types/sources/registry.d.ts +63 -0
- package/dist/types/sources/shared.d.ts +3 -0
- package/dist/types/sources/strapi.d.ts +2 -0
- package/docs/01-quickstart.mdx +4 -2
- package/docs/02-deployment.mdx +59 -13
- package/docs/03-upgrading.mdx +2 -1
- package/docs/04-migrating.mdx +9 -9
- package/docs/08-faq.mdx +12 -12
- package/docs/advanced/changelog.mdx +30 -2
- package/docs/advanced/custom-pages.mdx +1 -1
- package/docs/advanced/skills.mdx +1 -1
- package/docs/cli/audit.mdx +30 -7
- package/docs/cli/doctor.mdx +6 -2
- package/docs/cli/evals.mdx +4 -4
- package/docs/cli/translate.mdx +1 -0
- package/docs/cli/validate.mdx +4 -3
- package/docs/configuration/analytics.mdx +19 -1
- package/docs/configuration/assistant.mdx +6 -2
- package/docs/configuration/customization.mdx +4 -2
- package/docs/configuration/export.mdx +5 -3
- package/docs/configuration/index.mdx +4 -2
- package/docs/configuration/narration.mdx +43 -6
- package/docs/configuration/rate-limiting.mdx +23 -2
- package/docs/configuration/search.mdx +37 -14
- package/docs/content/components.mdx +2 -2
- package/docs/content/frontmatter.mdx +5 -3
- package/docs/content/i18n.mdx +2 -2
- package/docs/content/includes.mdx +1 -1
- package/docs/content/navigation.mdx +8 -6
- package/docs/content/sources/contentful.mdx +9 -4
- package/docs/content/sources/custom.mdx +7 -3
- package/docs/content/sources/github-releases.mdx +12 -2
- package/docs/content/sources/index.mdx +3 -3
- package/docs/content/sources/notion.mdx +17 -2
- package/docs/content/sources/obsidian.mdx +3 -1
- package/docs/content/sources/payload.mdx +5 -2
- package/docs/content/sources/remote-mdx.mdx +6 -0
- package/docs/content/sources/sanity.mdx +4 -2
- package/docs/content/sources/strapi.mdx +5 -2
- package/docs/content/syntax.mdx +2 -2
- package/docs/discoverability/agent-discovery.mdx +1 -1
- package/docs/discoverability/index.mdx +1 -1
- package/docs/discoverability/json-api.mdx +2 -0
- package/docs/discoverability/llms-txt.mdx +1 -1
- package/docs/discoverability/markdown.mdx +3 -1
- package/docs/discoverability/mcp.mdx +6 -0
- package/docs/discoverability/sitemap-and-robots.mdx +1 -1
- package/docs/discoverability/structured-data.mdx +8 -6
- package/docs/references/api-pages.mdx +1 -1
- package/docs/references/asyncapi.mdx +5 -1
- package/docs/references/graphql.mdx +15 -1
- package/docs/references/openapi.mdx +64 -8
- package/package.json +9 -9
- package/skills/blume/SKILL.md +1 -1
- package/skills/blume-migrate/SKILL.md +2 -2
- package/skills/blume-migrate/assets/{oxfmt@0.67.0.patch → oxfmt@0.71.0.patch} +7 -7
- package/skills/blume-migrate/references/docusaurus.md +4 -4
- package/skills/blume-migrate/references/fumadocs.md +17 -13
- package/skills/blume-migrate/references/monorepo.md +5 -5
- package/skills/blume-migrate/references/nextra.md +8 -5
- package/skills/blume-migrate/references/starlight.md +9 -7
- package/src/ai/ask-context.ts +33 -3
- package/src/ai/ask-limits.ts +13 -0
- package/src/ai/ask.ts +24 -6
- package/src/ai/changelog-markdown.ts +11 -6
- package/src/ai/component-markdown.ts +1 -1
- package/src/ai/cors.ts +12 -3
- package/src/ai/mcp/query.ts +14 -6
- package/src/ai/openapi-components.ts +28 -4
- package/src/ai/relative-links.ts +4 -38
- package/src/analytics/clics.ts +58 -0
- package/src/analytics/head.ts +5 -0
- package/src/analytics/index.ts +2 -0
- package/src/analytics/schema.ts +2 -0
- package/src/astro/generate.ts +91 -56
- package/src/astro/integration.ts +72 -22
- package/src/astro/markdown-negotiation.ts +3 -0
- package/src/astro/module-types.ts +3 -1
- package/src/astro/pages.ts +23 -0
- package/src/astro/render-deps.ts +10 -5
- package/src/astro/runtime-deps.ts +23 -2
- package/src/astro/templates.ts +129 -81
- package/src/audit/catalog.ts +19 -3
- package/src/audit/checks/links.ts +14 -1
- package/src/audit/checks/network.ts +110 -6
- package/src/audit/checks/redirects.ts +6 -4
- package/src/audit/checks/robots.ts +58 -2
- package/src/audit/run.ts +3 -0
- package/src/audit/types.ts +2 -0
- package/src/audit/url.ts +14 -5
- package/src/blume-modules.d.ts +5 -1
- package/src/cli/args.ts +51 -0
- package/src/cli/build-failure.ts +23 -0
- package/src/cli/commands/audit.ts +10 -1
- package/src/cli/commands/dev.ts +18 -2
- package/src/cli/commands/doctor.ts +45 -12
- package/src/cli/commands/eval.ts +36 -4
- package/src/cli/commands/init.ts +6 -4
- package/src/cli/commands/validate.ts +10 -1
- package/src/cli/init/scaffold.ts +9 -6
- package/src/cli/prepare.ts +6 -3
- package/src/cli/required-secrets.ts +3 -2
- package/src/components/content/ApiPlayground.astro +6 -2
- package/src/components/content/api-field.ts +4 -1
- package/src/components/content/api-page.ts +31 -7
- package/src/components/content/mermaid-element.ts +15 -1
- package/src/components/content/view-switcher.ts +7 -4
- package/src/components/islands/hooks.ts +27 -1
- package/src/components/layout/NavTree.astro +49 -5
- package/src/components/layout/NavTreeScript.astro +18 -0
- package/src/components/layout/PageActions.astro +139 -24
- package/src/components/layout/PageLayout.astro +13 -3
- package/src/components/layout/RootLayout.astro +27 -14
- package/src/components/layout/Search.astro +17 -3
- package/src/components/layout/TableOfContents.astro +24 -17
- package/src/components/layout/analytics-client.ts +8 -8
- package/src/components/layout/head-scripts.ts +6 -1
- package/src/components/layout/search/algolia.ts +10 -1
- package/src/components/layout/search/analytics.ts +42 -14
- package/src/components/layout/search/endpoint.ts +48 -5
- package/src/components/layout/search/pagefind.ts +114 -5
- package/src/components/layout/search/types.ts +12 -0
- package/src/components/layout/search/typesense.ts +18 -6
- package/src/components/openapi/AsyncApiOperation.astro +14 -7
- package/src/components/openapi/Authorization.astro +10 -0
- package/src/components/openapi/GraphqlOperation.astro +5 -0
- package/src/components/openapi/Operation.astro +24 -1
- package/src/components/openapi/ParametersTable.astro +8 -4
- package/src/components/openapi/Playground.astro +7 -1
- package/src/components/openapi/RequestBody.astro +19 -2
- package/src/components/openapi/Responses.astro +1 -0
- package/src/components/openapi/SchemaTable.astro +15 -3
- package/src/components/openapi/async-snippets.ts +61 -5
- package/src/components/openapi/async.ts +123 -29
- package/src/components/openapi/graphql-helpers.ts +35 -12
- package/src/components/openapi/helpers.ts +36 -2
- package/src/components/openapi/message-model.ts +54 -11
- package/src/components/openapi/message.ts +14 -4
- package/src/components/openapi/operation-model.ts +16 -7
- package/src/components/openapi/playground-client.ts +77 -58
- package/src/components/openapi/playground-schema.ts +1 -1
- package/src/components/openapi/schema-tree.ts +44 -14
- package/src/components/openapi/security.ts +10 -0
- package/src/components/openapi/snippets.ts +5 -3
- package/src/components/openapi/validate-json.ts +4 -3
- package/src/components/powered-by-middleware.ts +33 -0
- package/src/core/changelog-index.ts +31 -1
- package/src/core/component-diagnostics.ts +53 -0
- package/src/core/component-overrides.ts +32 -0
- package/src/core/config-input.ts +42 -6
- package/src/core/data.ts +2 -0
- package/src/core/diagnostics.ts +5 -0
- package/src/core/i18n-ui.ts +2 -0
- package/src/core/includes.ts +1 -1
- package/src/core/links.ts +47 -17
- package/src/core/powered-by.ts +12 -0
- package/src/core/probe.ts +26 -6
- package/src/core/project-graph.ts +59 -18
- package/src/core/redirect-patterns.ts +36 -7
- package/src/core/schema.ts +26 -6
- package/src/core/sources/cache.ts +6 -0
- package/src/core/sources/contentful-rich-text.ts +29 -6
- package/src/core/sources/contentful.ts +135 -25
- package/src/core/sources/filesystem.ts +26 -4
- package/src/core/sources/github-releases.ts +12 -1
- package/src/core/sources/lexical.ts +15 -10
- package/src/core/sources/lower.ts +19 -3
- package/src/core/sources/mdx-remote.ts +62 -10
- package/src/core/sources/normalize.ts +227 -41
- package/src/core/sources/notion.ts +34 -10
- package/src/core/sources/obsidian.ts +16 -3
- package/src/core/sources/payload.ts +38 -15
- package/src/core/sources/remote.ts +72 -15
- package/src/core/sources/resolve.ts +11 -1
- package/src/core/sources/sanity.ts +43 -25
- package/src/core/sources/strapi.ts +2 -2
- package/src/core/types.ts +18 -0
- package/src/core/ui-packs/ar.ts +1 -0
- package/src/core/ui-packs/bg.ts +1 -0
- package/src/core/ui-packs/bn.ts +1 -0
- package/src/core/ui-packs/ca.ts +1 -0
- package/src/core/ui-packs/cs.ts +1 -0
- package/src/core/ui-packs/da.ts +1 -0
- package/src/core/ui-packs/de.ts +1 -0
- package/src/core/ui-packs/el.ts +1 -0
- package/src/core/ui-packs/es.ts +1 -0
- package/src/core/ui-packs/fa.ts +1 -0
- package/src/core/ui-packs/fi.ts +1 -0
- package/src/core/ui-packs/fr.ts +1 -0
- package/src/core/ui-packs/he.ts +1 -0
- package/src/core/ui-packs/hi.ts +1 -0
- package/src/core/ui-packs/hr.ts +1 -0
- package/src/core/ui-packs/hu.ts +1 -0
- package/src/core/ui-packs/id.ts +1 -0
- package/src/core/ui-packs/it.ts +1 -0
- package/src/core/ui-packs/ja.ts +1 -0
- package/src/core/ui-packs/ko.ts +1 -0
- package/src/core/ui-packs/nl.ts +1 -0
- package/src/core/ui-packs/no.ts +1 -0
- package/src/core/ui-packs/pl.ts +1 -0
- package/src/core/ui-packs/pt-br.ts +1 -0
- package/src/core/ui-packs/pt.ts +1 -0
- package/src/core/ui-packs/ro.ts +1 -0
- package/src/core/ui-packs/ru.ts +1 -0
- package/src/core/ui-packs/sk.ts +1 -0
- package/src/core/ui-packs/sr.ts +1 -0
- package/src/core/ui-packs/sv.ts +1 -0
- package/src/core/ui-packs/th.ts +1 -0
- package/src/core/ui-packs/tr.ts +1 -0
- package/src/core/ui-packs/uk.ts +1 -0
- package/src/core/ui-packs/vi.ts +1 -0
- package/src/core/ui-packs/zh-tw.ts +1 -0
- package/src/core/ui-packs/zh.ts +1 -0
- package/src/deploy/adapter-output.ts +7 -2
- package/src/deploy/adapters/cloudflare.ts +7 -5
- package/src/deploy/adapters/node.ts +47 -9
- package/src/deploy/adapters/types.ts +7 -3
- package/src/deploy/artifacts.ts +2 -1
- package/src/deploy/cloudflare-negotiation.ts +36 -7
- package/src/deploy/headers.ts +15 -1
- package/src/deploy/node-headers.ts +32 -20
- package/src/deploy/platforms/cloudflare.ts +14 -9
- package/src/deploy/platforms/netlify.ts +2 -1
- package/src/deploy/platforms/node.ts +12 -2
- package/src/deploy/platforms/types.ts +21 -7
- package/src/deploy/platforms/vercel.ts +115 -17
- package/src/deploy/redirects.ts +66 -6
- package/src/deploy/rss.ts +3 -1
- package/src/deploy/sitemap.ts +39 -5
- package/src/deploy/vercel-negotiation.ts +325 -103
- package/src/eval/agents.ts +18 -1
- package/src/eval/prompts.ts +6 -3
- package/src/eval/report.ts +34 -13
- package/src/eval/run.ts +5 -0
- package/src/markdown/base-links.ts +15 -14
- package/src/markdown/package-commands.ts +9 -8
- package/src/markdown/relative-links.ts +3 -2
- package/src/narration/build.ts +218 -40
- package/src/narration/provider.ts +89 -9
- package/src/openapi/checks.ts +273 -0
- package/src/openapi/graphql-build.ts +21 -0
- package/src/openapi/graphql.ts +11 -0
- package/src/openapi/model.ts +15 -3
- package/src/openapi/parse.ts +127 -2
- package/src/openapi/references.ts +43 -3
- package/src/openapi/render-mdx.ts +68 -8
- package/src/openapi/sentence.ts +29 -2
- package/src/openapi/source.ts +39 -14
- package/src/ratelimit/index.ts +1 -1
- package/src/ratelimit/runtime.ts +37 -19
- package/src/ratelimit/upstash.ts +40 -10
- package/src/reference/graphql.ts +15 -1
- package/src/reference/index.ts +1 -0
- package/src/reference/openapi.ts +2 -2
- package/src/reference/options.ts +29 -1
- package/src/registry/eject.ts +8 -2
- package/src/search/adapters/locale-scope.ts +30 -0
- package/src/search/adapters/typesense.ts +14 -4
- package/src/search/build.ts +21 -3
- package/src/search/orama-index.ts +163 -31
- package/src/search/source-pages.ts +29 -0
- package/src/search/sync/algolia.ts +99 -9
- package/src/search/sync/index.ts +52 -13
- package/src/search/sync/typesense.ts +72 -27
- package/src/seo/jsonld.ts +38 -20
- package/src/sources/contentful.ts +12 -0
- package/src/sources/github-releases.ts +7 -1
- package/src/sources/notion.ts +7 -0
- package/src/sources/shared.ts +3 -0
- package/src/theme/color.ts +9 -10
- package/src/theme/entry.ts +43 -6
- package/src/translate/validate.ts +23 -1
- package/dist/cli/chunk-0gz36akr.js.map +0 -12
- package/dist/cli/chunk-2n4ps1b3.js.map +0 -15
- package/dist/cli/chunk-43zcf4gz.js.map +0 -36
- package/dist/cli/chunk-4ejxbzee.js +0 -39
- package/dist/cli/chunk-4ejxbzee.js.map +0 -10
- package/dist/cli/chunk-4h8z9vv5.js.map +0 -16
- package/dist/cli/chunk-713mngdq.js.map +0 -14
- package/dist/cli/chunk-9xr4dgee.js.map +0 -10
- package/dist/cli/chunk-amrb6hgh.js +0 -145
- package/dist/cli/chunk-amrb6hgh.js.map +0 -11
- package/dist/cli/chunk-nd1821sk.js.map +0 -10
- package/dist/cli/chunk-rekqz4qg.js.map +0 -10
- package/dist/cli/chunk-ta27feev.js.map +0 -10
- package/dist/cli/chunk-xkz5xbtv.js.map +0 -213
- package/dist/cli/chunk-y5h4r2y0.js.map +0 -10
- package/dist/cli/chunk-zf748avv.js.map +0 -37
- /package/dist/cli/{chunk-9geymqns.js.map → chunk-0w6hs479.js.map} +0 -0
- /package/dist/cli/{chunk-y4ykbark.js.map → chunk-1f882900.js.map} +0 -0
- /package/dist/cli/{chunk-py62fewh.js.map → chunk-53jqw8z0.js.map} +0 -0
- /package/dist/cli/{chunk-5wj2qwn2.js.map → chunk-58bt3hag.js.map} +0 -0
- /package/dist/cli/{chunk-e7vvk5ya.js.map → chunk-5cmts6dv.js.map} +0 -0
- /package/dist/cli/{chunk-g2z9x01v.js.map → chunk-9qzgg6mj.js.map} +0 -0
- /package/dist/cli/{chunk-q3wxxq39.js.map → chunk-a57yh0h9.js.map} +0 -0
- /package/dist/cli/{chunk-2f3ndkpw.js.map → chunk-desvm72z.js.map} +0 -0
- /package/dist/cli/{chunk-gp84za0h.js.map → chunk-dhwtqnnd.js.map} +0 -0
- /package/dist/cli/{chunk-2f7n4qzt.js.map → chunk-dqhg54mr.js.map} +0 -0
- /package/dist/cli/{chunk-zhvd6adm.js.map → chunk-gfc5fqvf.js.map} +0 -0
- /package/dist/cli/{chunk-p804f3pe.js.map → chunk-hvcxng5f.js.map} +0 -0
- /package/dist/cli/{chunk-1n228xqb.js.map → chunk-kdpzw38a.js.map} +0 -0
- /package/dist/cli/{chunk-6bm0xpgd.js.map → chunk-s0bdnc7v.js.map} +0 -0
- /package/dist/cli/{chunk-cmtcnh9f.js.map → chunk-tfycddbt.js.map} +0 -0
- /package/dist/cli/{chunk-ntv4qrt7.js.map → chunk-y4msrwm9.js.map} +0 -0
- /package/dist/cli/{chunk-yj17vs39.js.map → chunk-ydp3065p.js.map} +0 -0
|
@@ -64,7 +64,7 @@ navigation: {
|
|
|
64
64
|
|
|
65
65
|
- **`flat`** (default) — a non-collapsible header with its pages listed beneath. Pages that aren't in any group always list first, above the group sections, so they can't be mistaken for a group's children.
|
|
66
66
|
- **`group`** — a collapsible `<details>` disclosure per group. Groups start collapsed by default; a group containing the current page always starts open, so only the section you're in is expanded. Set `collapsed: false` in [folder meta](/docs/content/meta) to force a group open regardless.
|
|
67
|
-
- **`page`** — each group is a single row
|
|
67
|
+
- **`page`** — each group is a single row whose chevron slides the sidebar into a sub-panel showing only that group's items, with a back arrow at the top. A group with an index page links the row's name to that page, and a group without one slides the panel in from anywhere on the row. The panel is route-aware, so landing directly on a page inside the group opens straight to it. After the back arrow, the row of the section you're in stays highlighted.
|
|
68
68
|
|
|
69
69
|
:::tip
|
|
70
70
|
`page` mode keeps deep sections tidy — reach for it when groups have many children and you'd rather drill into them than scroll past them.
|
|
@@ -100,7 +100,7 @@ A generated group's effective mode resolves highest priority first:
|
|
|
100
100
|
3. The global `navigation.sidebar.display`
|
|
101
101
|
4. The Blume default (`flat`)
|
|
102
102
|
|
|
103
|
-
A group's `display` applies to that group only — nested subgroups resolve their own value through the same chain. A `page`-mode group with an index page
|
|
103
|
+
A group's `display` applies to that group only — nested subgroups resolve their own value through the same chain. A `page`-mode group with an index page links its row's name to that page, so clicking the name opens the landing page with its sub-panel already open, while the chevron beside it slides the panel in without leaving the current page. The panel's title links back to the landing page, and the index page lists as the panel's first item unless it's [hidden](#hidden-pages). A `page`-mode group without an index page has nothing to open, so its whole row slides the panel in.
|
|
104
104
|
|
|
105
105
|
`sidebar.display` means nothing anywhere else — on a non-index page, on the content root's own `index` page (the root isn't a group; use `navigation.sidebar.display`), or on any page when an [explicit sidebar](#explicit-sidebar) is configured (its items own each group's mode) — so Blume reports a `BLUME_SIDEBAR_DISPLAY_IGNORED` warning instead of silently dropping it. `collapsed` remains specific to `group` mode; it's inert when a group resolves to `flat` or `page`.
|
|
106
106
|
|
|
@@ -267,7 +267,7 @@ navigation: {
|
|
|
267
267
|
}
|
|
268
268
|
```
|
|
269
269
|
|
|
270
|
-
Each link takes a `label`, an `href`, and an optional `icon` (a [built-in icon](/docs/content/components#icon) name, image path/URL, or inline SVG — the same as anywhere else). An `href` may point anywhere: an external URL opens in a new tab, while an internal route (`/contact`) is validated against your pages at build time, warning you if nothing matches.
|
|
270
|
+
Each link takes a `label`, an `href`, and an optional `icon` (a [built-in icon](/docs/content/components#icon) name, image path/URL, or inline SVG — the same as anywhere else). An `href` may point anywhere: an external URL opens in a new tab, while an internal route (`/contact`) is validated against your pages at build time and by [`blume doctor`](/docs/cli/doctor), warning you if nothing matches.
|
|
271
271
|
|
|
272
272
|
## Explicit sidebar
|
|
273
273
|
|
|
@@ -304,6 +304,8 @@ navigation: {
|
|
|
304
304
|
|
|
305
305
|
Like tab labels, a `label` here can be a map of locale code to text on a [multilingual](/docs/content/i18n#per-locale-navigation) site.
|
|
306
306
|
|
|
307
|
+
An action is only a link, so a changelog linked this way still lists its entries in the sidebar. Link it with a [tab](#tabs) to [keep them out](/docs/advanced/changelog#keep-entries-out-of-the-sidebar).
|
|
308
|
+
|
|
307
309
|
`cta` is singular on purpose — a docs header has room for exactly one thing a reader is being asked to do, and a row of buttons asks for nothing. Secondary links belong in `actions`, or in [`featured`](#featured-links) if they should sit with the sidebar instead.
|
|
308
310
|
|
|
309
311
|
An `http(s)` or protocol-relative href opens in a new tab; a route stays in the tab, and is validated against your pages at build time like a `featured` link — so a page served by another app on the same host (`/signup` on the product, say) should be written as an absolute URL. `actions` are hidden below the `sm` breakpoint, where the header has room for the logo and the navigation toggle and nothing else. `cta` hides there too, except on a page with no navigation toggle — a landing page on `PageLayout` with no tabs — where it stays, since nothing else can surface it on a phone.
|
|
@@ -339,14 +341,14 @@ These come for free from the sidebar tree — no configuration:
|
|
|
339
341
|
|
|
340
342
|
## On this page
|
|
341
343
|
|
|
342
|
-
A right-rail outline is generated automatically from each page's headings — `##` and `###` by default — so long pages stay scannable. Set [`toc`](/docs/configuration#table-of-contents) to change the heading range or turn the outline off. On narrower screens, where the right rail is hidden, it collapses into an “On this page” dropdown above the content.
|
|
344
|
+
A right-rail outline is generated automatically from each page's headings — `##` and `###` by default — so long pages stay scannable. Set [`toc`](/docs/configuration#table-of-contents) to change the heading range or turn the outline off. On narrower screens, where the right rail is hidden, it collapses into an “On this page” dropdown above the content, which also holds the [page actions](#page-actions).
|
|
343
345
|
|
|
344
346
|
## Page actions
|
|
345
347
|
|
|
346
|
-
Below the table of contents, every page shows a set of quick actions:
|
|
348
|
+
Below the table of contents, every page shows a set of quick actions. In windows at least 1,280px wide they sit in the right-hand column with the outline. In narrower windows and on phones they sit at the bottom of the “On this page” dropdown above the content, which shows on a page with no headings too:
|
|
347
349
|
|
|
348
350
|
- **Edit on GitHub** — links straight to the source file. Appears once you set [`github`](/docs/configuration) in your config.
|
|
349
|
-
- **Scroll to top** — smoothly returns to the top of long pages.
|
|
351
|
+
- **Scroll to top** — smoothly returns to the top of long pages. It's in the right-hand column only, since the dropdown already sits at the top of the page.
|
|
350
352
|
|
|
351
353
|
Others hand the page to AI tools — **Copy as Markdown** and **Open in chat** — covered in [Markdown for agents](/docs/discoverability/markdown#copy-as-markdown).
|
|
352
354
|
|
|
@@ -3,7 +3,7 @@ title: Contentful
|
|
|
3
3
|
description: Read a Contentful content type through the Delivery API with the contentful() source, lowering rich text bodies to Markdown with nothing to install.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
The built-in `contentful()` adapter reads the entries of one content type through the Content Delivery API and lowers each entry's rich text body to Markdown: headings, marks, links, lists, quotes,
|
|
6
|
+
The built-in `contentful()` adapter reads the entries of one content type through the Content Delivery API and lowers each entry's rich text body to Markdown: headings, marks, links, lists, quotes, and tables map to their Markdown equivalents, an embedded image asset to an image, and any other embedded file (a PDF, say) to a link to it. A body held in a Markdown long-text field passes through as written, except that a link that isn't a web, mail, phone, or relative address (a `javascript:` URL, say) keeps only its label, as it does in rich text. Nothing to install — the adapter speaks the REST API directly.
|
|
7
7
|
|
|
8
8
|
```ts blume.config.ts
|
|
9
9
|
import { defineConfig } from "blume";
|
|
@@ -19,14 +19,19 @@ export default defineConfig({
|
|
|
19
19
|
contentType: "guide",
|
|
20
20
|
// environment: "master", locale: "en-US"
|
|
21
21
|
// Field ids default to title / description / slug / body, and the
|
|
22
|
-
// date to sys.updatedAt
|
|
23
|
-
fields: { body: "content" },
|
|
22
|
+
// date to sys.updatedAt; order (no default) sets the sidebar order
|
|
23
|
+
fields: { body: "content", order: "position" },
|
|
24
24
|
// Extra Delivery API query parameters
|
|
25
25
|
params: { "fields.section": "sdk" },
|
|
26
|
+
// A space with EU data residency:
|
|
27
|
+
// host: "cdn.eu.contentful.com",
|
|
28
|
+
// previewHost: "preview.eu.contentful.com",
|
|
26
29
|
}),
|
|
27
30
|
],
|
|
28
31
|
},
|
|
29
32
|
});
|
|
30
33
|
```
|
|
31
34
|
|
|
32
|
-
The Delivery API token comes from the `CONTENTFUL_ACCESS_TOKEN` environment variable, which the adapter declares. Under `--preview` the adapter reads drafts through the Preview API with `CONTENTFUL_PREVIEW_TOKEN`. The Preview API rejects delivery tokens, so `--preview` without a preview token fails with a clear error rather than falling back to `CONTENTFUL_ACCESS_TOKEN`. Assets are referenced from Contentful's CDN rather than downloaded — their URLs are stable. An embedded entry maps to a Blume component through the engine's `serializers` option, keyed by content type id, when you construct `contentfulSource` directly and pass it to [`custom()`](/docs/content/sources/custom); setting `serializers` writes the source's pages as MDX so the returned components render, and an embedded entry without a serializer is noted in a comment.
|
|
35
|
+
The Delivery API token comes from the `CONTENTFUL_ACCESS_TOKEN` environment variable, which the adapter declares. Without it, the source stops before its first request with a `BLUME_MISSING_SECRET` error rather than Contentful's `401`, unless it has a [cached snapshot](/docs/content/sources#caching-and-offline-builds) to serve instead. Under `--preview` the adapter reads drafts through the Preview API with `CONTENTFUL_PREVIEW_TOKEN`. The Preview API rejects delivery tokens, so `--preview` without a preview token fails with a clear error rather than falling back to `CONTENTFUL_ACCESS_TOKEN`. A space with EU data residency is served from other hosts: set `host` to `cdn.eu.contentful.com` and `previewHost` to `preview.eu.contentful.com`. Map `fields.order` to a number field to set each page's sidebar order; it has no default. Assets are referenced from Contentful's CDN rather than downloaded — their URLs are stable. A link to another entry of the source's `contentType` points at that entry's page, at the route the page is built at (its slug under the source's `prefix`). A link to an entry the source doesn't turn into a page, one of another content type or one your `params` filter out, keeps only its text. The Delivery API leaves an unpublished, archived, or deleted asset or entry out of its response, so a page that embeds or links one renders without it (a link keeps its text) and the source warns with `BLUME_SOURCE_UNRESOLVED_LINK`. An embedded entry maps to a Blume component through the engine's `serializers` option, keyed by content type id, when you construct `contentfulSource` directly and pass it to [`custom()`](/docs/content/sources/custom); setting `serializers` writes the source's pages as MDX so the returned components render, and an embedded entry without a serializer is noted in a comment.
|
|
36
|
+
|
|
37
|
+
[Publish developer documentation from Contentful](/guides/contentful-documentation-website) walks through it end to end: a content model for docs pages and code blocks, drafts reviewed with a preview token, and a static site that rebuilds whenever an editor publishes.
|
|
@@ -8,6 +8,7 @@ Any object implementing the `ContentSource` interface can be passed to `custom()
|
|
|
8
8
|
```ts blume.config.ts
|
|
9
9
|
import { defineConfig } from "blume";
|
|
10
10
|
import { custom, filesystem } from "blume/sources";
|
|
11
|
+
import { escapeMarkdownText } from "blume/sources/lower.ts";
|
|
11
12
|
import { sanitySource } from "blume/sources/sanity.ts";
|
|
12
13
|
|
|
13
14
|
export default defineConfig({
|
|
@@ -23,7 +24,8 @@ export default defineConfig({
|
|
|
23
24
|
query: `*[_type == "guide"]`,
|
|
24
25
|
// Map custom Portable Text blocks to Blume components
|
|
25
26
|
serializers: {
|
|
26
|
-
callout: (block) =>
|
|
27
|
+
callout: (block) =>
|
|
28
|
+
`<Callout>${escapeMarkdownText(String(block.text ?? ""))}</Callout>`,
|
|
27
29
|
},
|
|
28
30
|
})
|
|
29
31
|
),
|
|
@@ -32,8 +34,10 @@ export default defineConfig({
|
|
|
32
34
|
});
|
|
33
35
|
```
|
|
34
36
|
|
|
35
|
-
A
|
|
37
|
+
A serializer's return value is written into the page as MDX, as is, so text it takes from the CMS must be escaped: a `{` in an editor's text would otherwise run as an expression and fail the build, and a `<b>` would render as HTML. `escapeMarkdownText` escapes it the way Blume's built-in lowering does, so the text renders as written.
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
A source built with one of Blume's engine factories, like `sanitySource` above, is rebuilt on the running command's context, so it reads drafts under `--preview` and keeps its snapshot in `.blume/cache` the way the built-in adapter does. A source of your own can do the same by implementing `withContext(ctx)` and returning itself rebuilt on that context. Unlike a built-in adapter's, a `custom()` source's snapshot is kept per preview mode only, not per set of options, since Blume sees an instance rather than its options. `blume dev` serves that snapshot after you edit the source's query, fields, or serializers, so run `blume sync` to fetch afresh.
|
|
40
|
+
|
|
41
|
+
`custom()` takes a `ContentSource` instance rather than an options object, so there is nothing to pass the [shared options](/docs/content/sources#adapters) `prefix` or `pollInterval` to. The source sets its own `prefix` property, and re-fetching is whatever its `watch` method implements. It carries a live instance rather than plain data, so it declares no runtime dependency or secret of its own — the instance manages those itself. `blume dev` and `blume build` don't warn about an unset variable a `custom()` source reads; an engine whose API requires its token (`notionSource`, `contentfulSource`) stops before its first request with `BLUME_MISSING_SECRET` instead.
|
|
38
42
|
|
|
39
43
|
A custom source that reads local files should set `sourcePath` on each entry and `contentRoot` on the source itself. `sourcePath` names the file in diagnostics and resolves relative images beside it; `contentRoot` bounds the git `log` that dates pages, so without it the source's pages get no git-derived ["Last updated" date](/docs/configuration#last-modified).
|
|
@@ -20,12 +20,22 @@ export default defineConfig({
|
|
|
20
20
|
// prereleases: false, // include prereleases (default off)
|
|
21
21
|
// drafts: false, // include drafts (needs a write token)
|
|
22
22
|
// limit: 100, // cap releases, newest-first
|
|
23
|
+
// baseUrl: "https://github.acme.com/api/v3", // GitHub Enterprise Server
|
|
23
24
|
}),
|
|
24
25
|
],
|
|
25
26
|
},
|
|
26
27
|
});
|
|
27
28
|
```
|
|
28
29
|
|
|
29
|
-
Each release maps to the changelog fields automatically: its name (or tag) becomes the title, its published date drives the timeline order, the tag becomes `changelog.version`, and prereleases are tagged `Prerelease
|
|
30
|
+
Each release maps to the changelog fields automatically: its name (or tag) becomes the title, its published date drives the timeline order, the tag becomes `changelog.version`, and prereleases are tagged `Prerelease`, drafts (when `drafts` includes them) `Draft`, and the rest `Release`. A draft is published like any other release, so include drafts only in a build readers don't see. A summary of the notes becomes the page's meta description, which the changelog's RSS feed uses as the item's description too. The notes render as the entry body, with two changes to their links: a link that isn't a web, mail, phone, or relative address (a `javascript:` URL, say) keeps only its label, and a link back to your own [`deployment.site`](/docs/deployment) is rewritten to its root-relative path, so it follows preview deploys and your deployment base. Give the source a `prefix` so its release pages nest under a route like `/changelog/v1-2-0`. The timeline at `/changelog` takes its title and description from the top-level [`changelog`](/docs/advanced/changelog#the-index-page) config, not from the source:
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
```ts blume.config.ts
|
|
33
|
+
changelog: {
|
|
34
|
+
title: "SDK releases",
|
|
35
|
+
description: "Every release of the Acme SDK, from GitHub.",
|
|
36
|
+
},
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
A private repo authenticates with the `GITHUB_TOKEN` environment variable — the same token the other GitHub features use, never inlined into your config; the adapter declares it, so a build without it warns. For a repository on GitHub Enterprise Server, set `baseUrl` to its REST API (`https://<host>/api/v3`); `GITHUB_TOKEN` is sent there, so it must be a token for that server. Like [every remote source](/docs/content/sources#caching-and-offline-builds) it's cached under `.blume/cache/<source>/` and served offline if the API is unreachable. Because a changelog is supplementary, a fetch failure with no cache (say a CI build without a token) degrades to an empty changelog with a warning rather than failing the build — set `GITHUB_TOKEN` in your CI and deploy environments to populate it.
|
|
40
|
+
|
|
41
|
+
[Turn GitHub releases into a product changelog](/guides/github-releases-changelog-website) walks through it end to end: a changelog timeline, a page per release, and an RSS feed, rebuilt every time you publish a release.
|
|
@@ -62,13 +62,13 @@ Every adapter that takes an options object accepts two shared options:
|
|
|
62
62
|
- **`prefix`** namespaces the source's routes under `/<prefix>/…`, which also becomes the source's name in diagnostics and its cache directory. If two sources resolve to the same route, Blume reports a `BLUME_DUPLICATE_ROUTE` build error — give each source a distinct `prefix`.
|
|
63
63
|
- **`pollInterval`** (seconds) makes a remote source re-fetch on that interval in dev, reloading only when the content actually changes. Leave it unset to fetch once and freeze for the session. Local sources (`filesystem()`, `obsidian()`) watch the filesystem instead and ignore it.
|
|
64
64
|
|
|
65
|
-
An adapter's descriptor also declares the SDK it needs and the environment variables it reads, so the generated project declares that package, `blume dev` and `blume
|
|
65
|
+
An adapter's descriptor also declares the SDK it needs and the environment variables it reads, so the generated project declares that package, `blume dev`, `blume build`, and `blume doctor` warn when a variable is unset, and `blume doctor` lists the configured sources. The warning prints before any source fetches, so it isn't lost when the fetch then fails. `notion()` and `contentful()` can't read anything without their token, so with it unset they stop before their first request with a `BLUME_MISSING_SECRET` error, and a source whose SDK isn't installed stops the same way with `BLUME_SOURCE_SDK_MISSING`; either serves its cached snapshot instead when it has one. Options are validated when the config loads: a missing required option, an unknown key, or a leftover 1.x `{ type: "…" }` object fails with a message naming the fix.
|
|
66
66
|
|
|
67
67
|
A single `filesystem()` source roots the generated docs collection at its own directory. Several filesystem sources must share one root and partition it with `include` globs — a second source rooted elsewhere is reported as `BLUME_ENTRY_ID_MISMATCH` so its pages can't silently 404.
|
|
68
68
|
|
|
69
69
|
## Caching and offline builds
|
|
70
70
|
|
|
71
|
-
Each remote source keeps a snapshot under `.blume/cache/<source>/`. If a fetch fails — a network blip or a CMS outage — Blume serves the last-known-good snapshot with a warning rather than failing the build. Preview and published content get separate snapshots, and so does each set of source options, so a build never falls back to drafts fetched under `--preview`, and editing a source's `query` or `fields` fetches afresh. The cache lives inside `.blume/` and is regenerated, never committed.
|
|
71
|
+
Each remote source keeps a snapshot under `.blume/cache/<source>/`. If a fetch fails — a network blip or a CMS outage — Blume serves the last-known-good snapshot with a warning rather than failing the build. Preview and published content get separate snapshots, and so does each set of source options, so a build never falls back to drafts fetched under `--preview`, and editing a source's `query` or `fields` fetches afresh. A [`custom()`](/docs/content/sources/custom) source is the exception: its snapshot is kept per preview mode only, so after editing its options run `blume sync`. The cache lives inside `.blume/` and is regenerated, never committed.
|
|
72
72
|
|
|
73
73
|
In dev, a remote source is served from its snapshot when it has one, so restarting the dev server doesn't refetch it. Run `blume sync` to pull the latest content (a running dev server hot-reloads), or `blume sync --force` to drop the snapshots first. Local filesystem sources hot-reload as usual. To poll a remote source for changes instead, set the shared `pollInterval` option (seconds) on it — the dev server re-fetches on that interval and reloads only when the content actually changes. Leave it unset to avoid hitting the API while you work.
|
|
74
74
|
|
|
@@ -76,7 +76,7 @@ In dev, a remote source is served from its snapshot when it has one, so restarti
|
|
|
76
76
|
|
|
77
77
|
Two flags control how remote content is fetched and what's included:
|
|
78
78
|
|
|
79
|
-
- **`--preview`** on `blume dev` or `blume build` renders drafts and pulls unpublished CMS content — Sanity switches to its `previewDrafts` perspective,
|
|
79
|
+
- **`--preview`** on `blume dev` or `blume build` renders drafts and pulls unpublished CMS content — Sanity switches to its `previewDrafts` perspective, Contentful reads through the Preview API, and Payload and Strapi request drafts. Notion always reads every page and stages the ones whose `Status` isn't published as drafts, so `blume dev` shows them with or without the flag, and `blume build --preview` renders them. Production builds without the flag exclude drafts as usual, so a preview build is a safe way to review unpublished work before it ships.
|
|
80
80
|
- **`blume sync`** re-fetches every remote source and regenerates the runtime. Dev is cache-first — a remote source is fetched once and served from `.blume/cache` on restart (fast and offline-tolerant), so `blume sync` is how you pull the latest CMS content without restarting the dev server (a running server hot-reloads). Add `--force` to drop the cache first, or set `pollInterval` on a source to refresh automatically.
|
|
81
81
|
|
|
82
82
|
```sh
|
|
@@ -3,7 +3,7 @@ title: Notion
|
|
|
3
3
|
description: Import a Notion database with the notion() source — rows become pages, properties become frontmatter, and blocks become Blume components.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
The built-in `notion()` adapter turns a Notion database into a collection: each row becomes a page, its properties become frontmatter, and its block tree becomes MDX. Callouts, toggles, columns, and code blocks map to the matching Blume components, and the text you type in Notion renders as written: a `{`, `<`, or Markdown character in a page is escaped rather than read as MDX, JSX, or formatting. Video blocks become a `<YouTube>` embed when they hold a YouTube link and a `<video>` player otherwise, with the block's caption as a `<Frame>` caption either way. A link to a video page rather than a media file (a Vimeo or Loom URL, say) is reported as a build warning, and its `<video>` player keeps pointing at the page, which it can't play — link to that video from the text instead. The adapter declares `@notionhq/client` (v5 or later) as its runtime dependency — an optional peer; Blume reads the database through its first data source.
|
|
6
|
+
The built-in `notion()` adapter turns a Notion database into a collection: each row becomes a page, its properties become frontmatter, and its block tree becomes MDX. Callouts, toggles, columns, and code blocks map to the matching Blume components, and the text you type in Notion renders as written: a `{`, `<`, or Markdown character in a page is escaped rather than read as MDX, JSX, or formatting, unless you [opt in to MDX](#writing-mdx-in-notion). Video blocks become a `<YouTube>` embed when they hold a YouTube link and a `<video>` player otherwise, with the block's caption as a `<Frame>` caption either way. A link to a video page rather than a media file (a Vimeo or Loom URL, say) is reported as a build warning, and its `<video>` player keeps pointing at the page, which it can't play — link to that video from the text instead. The adapter declares `@notionhq/client` (v5 or later) as its runtime dependency — an optional peer; Blume reads the database through its first data source.
|
|
7
7
|
|
|
8
8
|
```ts blume.config.ts
|
|
9
9
|
import { defineConfig } from "blume";
|
|
@@ -25,8 +25,23 @@ export default defineConfig({
|
|
|
25
25
|
});
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
The integration token comes from the `NOTION_TOKEN` environment variable (share the database with your integration)
|
|
28
|
+
The integration token comes from the `NOTION_TOKEN` environment variable (share the database with your integration). Without it, the source stops before calling Notion with a `BLUME_MISSING_SECRET` error naming the variable, unless it has a [cached snapshot](/docs/content/sources#caching-and-offline-builds) to serve instead.
|
|
29
29
|
|
|
30
30
|
The `Status` property is a publish gate by default. A page whose Status (a status or select property) holds any value other than `publishedValue`, which defaults to `Published`, imports with `draft: true`, and production builds drop drafts. A page with no Status value is published, and a database without the property publishes every page. Notion's default status options are Not started, In progress, and Done, so a database that uses them has no `Published` value and publishes nothing until you set `publishedValue: "Done"` (or whichever option means published). `properties.status` names a differently named property; to import every page whatever its status, point it at a property the database doesn't have.
|
|
31
31
|
|
|
32
32
|
**Notion image and video URLs are signed and expire**, so the adapter downloads them at build time into the site's assets and rewrites the references — a CMS asset never rots a static build. Only a file the server reports as an image or video is saved (or, when the response doesn't say, one whose URL names an image or video extension); anything else keeps its original URL with a build warning, so nothing but media is ever served from your site's origin. API calls are paced through a small request pool (3 at a time, matching Notion's per-integration rate limit) so databases with hundreds of pages import without tripping `429` responses; set `concurrency` on the source to tune it.
|
|
33
|
+
|
|
34
|
+
[Turn a Notion database into a docs site](/guides/notion-documentation-website) walks through it end to end, with a Status property that decides what goes live. [Combine Markdown and Notion in one documentation site](/guides/markdown-notion-documentation) gives Notion editors a section of their own beside the Markdown in Git.
|
|
35
|
+
|
|
36
|
+
## Writing MDX in Notion
|
|
37
|
+
|
|
38
|
+
Notion has no block for most Blume components. To use one in a Notion page (a `<CardGroup>` of `<Card>`s, say), type its MDX as text and set `mdx: true`: the source then reads the text of paragraphs, headings, list items, quotes, and callouts as MDX, so components, `{expressions}`, and Markdown typed in Notion render alongside Notion's own bold, italic, links, and inline code. Code blocks, toggle titles, image and video captions, and properties stay plain text.
|
|
39
|
+
|
|
40
|
+
```ts blume.config.ts
|
|
41
|
+
notion({
|
|
42
|
+
database: "8f2c1e0a4b7d4f3c9e6a5d2b1c0f9e8d",
|
|
43
|
+
mdx: true,
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The text is then held to MDX's rules, like an `.mdx` file: a stray `{` or `<` fails the page, and a paragraph starting with `import` reads as an import statement. **MDX runs code when the site builds**, so anyone who can edit the database can run code on your build machine and read its environment variables, `NOTION_TOKEN` included. Turn `mdx` on only for a database whose editors you'd trust with the site's code.
|
|
@@ -32,6 +32,8 @@ Locale directories and version snapshots inside the vault are read the same way
|
|
|
32
32
|
|
|
33
33
|
A link to an `index` note lands on its folder's route rather than a phantom `/index`. **An unresolved wikilink degrades to plain text with a build warning instead of failing the build**, so a vault mid-refactor still publishes. Single-line `%%comments%%` are stripped, a wikilink inside an HTML comment (`<!-- [[Draft]] -->`) is left alone since Obsidian hides it too, and a note with no `title` in its frontmatter is titled by its filename — the same rule Obsidian itself applies. An `index` note is the one exception: it names a route rather than a note, so its title falls through to Blume's usual derivation (first heading, then the humanized segment). Fenced, indented, and inline code passes through verbatim, so a note documenting the syntax survives.
|
|
34
34
|
|
|
35
|
-
Dot-folders are skipped, including Obsidian's own `.obsidian` config directory and `.trash` — which the dev watcher also ignores, so moving a pane in the app
|
|
35
|
+
Dot-folders are skipped, including Obsidian's own `.obsidian` config directory and `.trash` — which the dev watcher also ignores, so moving a pane in the app doesn't rebuild your site. Editing a note does, and so does deleting one to the trash, since the note leaves the vault. The directories no content scan reads (`node_modules`, `dist`, `.git`, …) are skipped too, so a vault rooted at the project itself doesn't publish dependency READMEs. A note with `#` or `?` in its path is left out with an error, the way a [content file](/docs/content#files-and-routes) is: Astro can't load its copy, so rename it. Symlinks inside the vault are followed, the way the filesystem source follows them, so a shared folder linked into the vault publishes with it. A vault that sits inside the filesystem source's root must be excluded from it (`filesystem({ root: "docs", exclude: ["**/_*", "**/.*", "vault/**"] })` — an `exclude` replaces the default `["**/_*", "**/.*"]` rather than adding to it, so keep those two to leave `_`-prefixed partials and dot-files unpublished); [`blume version <id>`](/docs/cli/version) then leaves it out of the snapshot, since the vault keeps publishing its own notes as current.
|
|
36
36
|
|
|
37
37
|
Not yet lowered: callouts (`> [!note]`) render as plain blockquotes, embeds (`![[image.png]]`) pass through untouched, multi-line `%%comments%%` are left in place, and there is no backlink graph.
|
|
38
|
+
|
|
39
|
+
[Publish Obsidian notes as a docs site](/guides/publish-obsidian-documentation) walks through it end to end, from a vault to a deployed site.
|
|
@@ -17,7 +17,8 @@ export default defineConfig({
|
|
|
17
17
|
prefix: "handbook",
|
|
18
18
|
url: "https://cms.example.com",
|
|
19
19
|
collection: "docs",
|
|
20
|
-
// Field paths default to title / description / slug / content /
|
|
20
|
+
// Field paths default to title / description / slug / content /
|
|
21
|
+
// updatedAt; order (no default) sets the sidebar order
|
|
21
22
|
fields: { body: "richText" },
|
|
22
23
|
// Extra query parameters: where[...], sort
|
|
23
24
|
params: { sort: "title" },
|
|
@@ -27,4 +28,6 @@ export default defineConfig({
|
|
|
27
28
|
});
|
|
28
29
|
```
|
|
29
30
|
|
|
30
|
-
The API key comes from the `PAYLOAD_API_KEY` environment variable and is sent as `users API-Key <key>`; set `authCollection` when the key belongs to another auth-enabled collection. Only published documents are imported — `--preview` requests drafts and stages them with `draft: true`. Documents are fetched with `depth: 1` so uploads carry their URLs, and a relative upload path (`/media/x.png`) resolves against `url`. A `block` or `inlineBlock` node maps to a Blume component through the engine's `serializers` option, keyed by `blockType`, when you construct `payloadSource` directly and pass it to [`custom()`](/docs/content/sources/custom). Setting `serializers` writes the source's pages as MDX so the returned components render; a body held in a Markdown text field stays Markdown.
|
|
31
|
+
The API key comes from the `PAYLOAD_API_KEY` environment variable and is sent as `users API-Key <key>`; set `authCollection` when the key belongs to another auth-enabled collection. Only published documents are imported — `--preview` requests drafts and stages them with `draft: true`. Documents are fetched with `depth: 1` so uploads carry their URLs, and a relative upload path (`/media/x.png`) resolves against `url`. Uploads are referenced at that URL rather than downloaded, since it doesn't expire the way Notion's do, so readers' browsers load them from your Payload server or its storage; to have Blume download and optimize them at build instead, authorize the host in [`image.domains`](/docs/configuration#images). A `block` or `inlineBlock` node maps to a Blume component through the engine's `serializers` option, keyed by `blockType`, when you construct `payloadSource` directly and pass it to [`custom()`](/docs/content/sources/custom). Setting `serializers` writes the source's pages as MDX so the returned components render; a body held in a Markdown text field stays Markdown. A node with no Markdown equivalent, or a block with no serializer, is left out as a comment, and the source warns with `BLUME_SOURCE_UNSUPPORTED_NODE` naming it and the page.
|
|
32
|
+
|
|
33
|
+
[Build a documentation website with Payload CMS](/guides/payload-cms-documentation-website) walks through it end to end: a collection published as a searchable docs site, with draft previews and a rebuild whenever an editor publishes.
|
|
@@ -33,8 +33,14 @@ mdxRemote({
|
|
|
33
33
|
});
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
`include` (default `["**/*.{md,mdx}"]`) filters the files by their path under `path`, the way a `filesystem()` source's `include` does: a pattern starting with `!` excludes what it matches (`["**/*.mdx", "!drafts/**"]`). With `github`, the source lists the repository's whole tree at `ref` and keeps the files under `path`. If nothing is under `path` at that ref, the source reads no pages and warns with `BLUME_SOURCE_PATH_MISSING`. GitHub caps the listing (100,000 entries or 7 MB for the whole repository, whatever `path` is), and past it the source warns with `BLUME_SOURCE_TRUNCATED`: list the files with `url` and `files` instead.
|
|
37
|
+
|
|
38
|
+
A relative link between remote files (`[Errors](./02-errors.mdx)`) lands on the page the linked file publishes at, with its ordering prefix dropped from the route, and `blume validate` checks it the same way.
|
|
39
|
+
|
|
36
40
|
A private repo's token is read from the `GITHUB_TOKEN` environment variable — the adapter declares it, so `blume dev` and `blume build` warn when it's unset. It is never inlined into your config or generated output, and it is only ever sent to GitHub's own hosts (`api.github.com`, `raw.githubusercontent.com`), never to a custom `url` base. A public repo works without it.
|
|
37
41
|
|
|
38
42
|
Remote pages are rendered with full MDX-plus-component fidelity: their bodies are materialized into a hidden staging directory and rendered through Astro alongside your local docs, so callouts, tabs, and every other Blume component keep working.
|
|
39
43
|
|
|
40
44
|
Like every remote source, fetched pages are cached for offline builds and refreshed with `blume sync` — see [Caching and offline builds](/docs/content/sources#caching-and-offline-builds).
|
|
45
|
+
|
|
46
|
+
[Build one docs portal from multiple GitHub repositories](/guides/documentation-multiple-repositories) uses it to pull each repository's docs folder into one site, pinned to a release that moves only when you change it.
|
|
@@ -3,7 +3,7 @@ title: Sanity
|
|
|
3
3
|
description: Pull documents from a Sanity dataset with the sanity() source — a GROQ query picks the documents and their Portable Text bodies become Markdown.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
The built-in `sanity()` adapter runs a GROQ query and maps each document's fields to frontmatter and its Portable Text body to Markdown. The adapter declares `@sanity/client` as its runtime dependency — an optional peer, so install it only if you use this source.
|
|
6
|
+
The built-in `sanity()` adapter runs a GROQ query and maps each document's fields to frontmatter and its Portable Text body to Markdown. A body held in a Markdown string field passes through as written, except that a link that isn't a web, mail, phone, or relative address (a `javascript:` URL, say) keeps only its label. The adapter declares `@sanity/client` as its runtime dependency — an optional peer, so install it only if you use this source.
|
|
7
7
|
|
|
8
8
|
```ts blume.config.ts
|
|
9
9
|
import { defineConfig } from "blume";
|
|
@@ -26,4 +26,6 @@ export default defineConfig({
|
|
|
26
26
|
});
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
A read token for a private dataset comes from the `SANITY_TOKEN` environment variable,
|
|
29
|
+
A read token for a private dataset comes from the `SANITY_TOKEN` environment variable. The adapter declares it, so `blume dev` and `blume build` warn when it's unset; a public dataset works without it. Sanity answers a private dataset's query without a token with no documents rather than an error, so when a query finds nothing and the token is unset, the source adds a `BLUME_MISSING_SECRET` warning saying so. Under `--preview` the adapter reads drafts with the `previewDrafts` perspective, which `@sanity/client` 8 reports as renamed to `drafts`; the old name still works, and `@sanity/client` 6 accepts only the old one. Custom Portable Text block types map to Blume components through the engine's `serializers` option, available when you construct `sanitySource` directly and pass it to [`custom()`](/docs/content/sources/custom). Setting `serializers` writes the source's pages as MDX, so a component or directive a serializer returns renders.
|
|
30
|
+
|
|
31
|
+
[Use Sanity as a CMS for developer documentation](/guides/sanity-documentation-website) walks through it end to end: editors writing in Sanity Studio while developers keep Markdown in Git, with drafts only in protected previews.
|
|
@@ -18,7 +18,8 @@ export default defineConfig({
|
|
|
18
18
|
url: "https://cms.example.com",
|
|
19
19
|
contentType: "guides",
|
|
20
20
|
// locale: "en"
|
|
21
|
-
// Field paths default to title / description / slug / content /
|
|
21
|
+
// Field paths default to title / description / slug / content /
|
|
22
|
+
// updatedAt; order (no default) sets the sidebar order
|
|
22
23
|
fields: { body: "body" },
|
|
23
24
|
// Extra query parameters: filters[...], sort
|
|
24
25
|
params: { "filters[section][$eq]": "sdk" },
|
|
@@ -28,4 +29,6 @@ export default defineConfig({
|
|
|
28
29
|
});
|
|
29
30
|
```
|
|
30
31
|
|
|
31
|
-
The API token comes from the `STRAPI_API_TOKEN` environment variable, sent as a bearer token. Entries are fetched with `populate=*` so images carry their URLs (set `populate` to narrow it), and a relative upload path (`/uploads/x.png`) resolves against `url`. Only published entries are imported — `--preview` requests drafts (`status=draft` on Strapi 5, `publicationState=preview` on Strapi 4) and stages each document that has never been published with `draft: true`; a published document previews its latest draft.
|
|
32
|
+
The API token comes from the `STRAPI_API_TOKEN` environment variable, sent as a bearer token. Entries are fetched with `populate=*` so images carry their URLs (set `populate` to narrow it), and a relative upload path (`/uploads/x.png`) in a Blocks field resolves against `url`. A Markdown field passes through as written, so a relative path there stays relative to your docs site and breaks: use absolute image URLs in Markdown fields, as Strapi's Markdown editor inserts them. Only published entries are imported — `--preview` requests drafts (`status=draft` on Strapi 5, `publicationState=preview` on Strapi 4) and stages each document that has never been published with `draft: true`; a published document previews its latest draft.
|
|
33
|
+
|
|
34
|
+
[Use Strapi to manage a documentation website](/guides/strapi-documentation-website) walks through it end to end: editors publishing in Strapi, a static site rebuilt on every publish, and drafts kept off production.
|
package/docs/content/syntax.mdx
CHANGED
|
@@ -27,7 +27,7 @@ Anchor ids are generated from the heading text, so a reworded heading gets a new
|
|
|
27
27
|
|
|
28
28
|
Link to it as `/page#setup`. The syntax matches Fumadocs, so migrated content works verbatim.
|
|
29
29
|
|
|
30
|
-
The `{#custom-id}` form used by Pandoc, kramdown, and Markdown-based specification toolchains is accepted as an equivalent in `.md` files. In `.mdx`, a bare `{…}` is a JSX expression and the page fails to compile — `blume check` reports
|
|
30
|
+
The `{#custom-id}` form used by Pandoc, kramdown, and Markdown-based specification toolchains is accepted as an equivalent in `.md` files. Only that unspaced spelling pins an anchor: the spaced `{ #custom-id }` and kramdown's `{: #custom-id }` (both from MkDocs' `attr_list`) stay in the heading text. In `.mdx`, a bare `{…}` is a JSX expression and the page fails to compile — `blume check` reports all three spellings as `BLUME_MDX_CURLY_ANCHOR` — so write `[#custom-id]` there, or escape the braces. The escaped form pins the same anchor in both formats, which makes it the right spelling for a partial that `.mdx` pages [include](/docs/content/includes):
|
|
31
31
|
|
|
32
32
|
```md
|
|
33
33
|
## Getting started \{#setup\}
|
|
@@ -457,7 +457,7 @@ flowchart LR
|
|
|
457
457
|
```
|
|
458
458
|
````
|
|
459
459
|
|
|
460
|
-
Diagrams render on the client, so this is an MDX-only feature, and the Mermaid library loads only on pages that include one; a site with no diagram doesn't ship it at all. Diagrams use Mermaid's dagre layout and classic look by default; opt a single diagram into another layout or look through Mermaid front matter (a `config:` block with `layout: elk` or `look: neo`), and the ELK engine loads only for diagrams that ask for it. The rest of this section is a gallery of common types — see the [Mermaid docs](https://mermaid.js.org/intro/) for the full list.
|
|
460
|
+
Diagrams render on the client, so this is an MDX-only feature, and the Mermaid library loads only on pages that include one; a site with no diagram doesn't ship it at all. Diagrams use Mermaid's dagre layout and classic look by default; opt a single diagram into another layout or look through Mermaid front matter (a `config:` block with `layout: elk` or `look: neo`), and the ELK engine loads only for diagrams that ask for it. A diagram Mermaid can't parse shows "Could not render this diagram." in its place; Mermaid's parser error, with the line it stopped on, goes to the browser console, and `blume dev` also prints it under the message. The rest of this section is a gallery of common types — see the [Mermaid docs](https://mermaid.js.org/intro/) for the full list.
|
|
461
461
|
|
|
462
462
|
### Flowchart
|
|
463
463
|
|
|
@@ -237,7 +237,7 @@ Every build writes an [agent skill](https://agentskills.io) for your docs, a `SK
|
|
|
237
237
|
- your API references
|
|
238
238
|
- a map of the docs in sidebar order, linking each page's Markdown with its one-line description
|
|
239
239
|
|
|
240
|
-
The skill is named after your site's `title` (`Acme Docs` becomes `acme-docs`). It covers the current version in the default language, leaves out API operation pages and changelog entries (each has one line of its own above the map), and lists up to 200 pages, pointing at `llms.txt` for the rest. The links are absolute, so the skill needs [`deployment.site`](/docs/deployment); a build without one skips it.
|
|
240
|
+
The skill is named after your site's `title` (`Acme Docs` becomes `acme-docs`). It covers the current version in the default language, leaves out API operation pages and changelog entries (each has one line of its own above the map), skips the pages `llms.txt` does (drafts, hidden and `noindex` pages, and pages with [`ai.exclude`](/docs/discoverability/llms-txt#excluding-a-page)), and lists up to 200 pages, pointing at `llms.txt` for the rest. The links are absolute, so the skill needs [`deployment.site`](/docs/deployment); a build without one skips it.
|
|
241
241
|
|
|
242
242
|
The generated skill is a guide to your docs, not a summary of your product. For one that teaches an agent your product — setup, core concepts, common tasks, and gotchas — have your coding agent write it from the docs:
|
|
243
243
|
|
|
@@ -46,4 +46,4 @@ The in-page **assistant** is the one AI feature documented elsewhere: it's a rea
|
|
|
46
46
|
|
|
47
47
|
## Checking your work
|
|
48
48
|
|
|
49
|
-
[`blume audit`](/docs/cli/audit) crawls the built site and reports on titles, descriptions, canonicals, Open Graph and X cards, hreflang, the sitemap, `robots.txt`, and structured data. Point it at a deployment with `--url` to also check response headers and [DNS-based agent discovery](/docs/discoverability/agent-discovery#dns-based-discovery-dns-aid).
|
|
49
|
+
[`blume audit`](/docs/cli/audit) crawls the built site and reports on titles, descriptions, canonicals, Open Graph and X cards, hreflang, the sitemap, `robots.txt`, and structured data. Point it at a deployment with `--url` to also check response headers, configured redirects, and [DNS-based agent discovery](/docs/discoverability/agent-discovery#dns-based-discovery-dns-aid).
|
|
@@ -13,6 +13,8 @@ Every Blume site also serves its docs as a small read-only **JSON API** — the
|
|
|
13
13
|
| `/api/docs/search?q=` | Full-text search, with the same `limit`, `contentTypes`, `locale`, `version`, and `filters[key]` scoping as `search_docs`. Server output only. |
|
|
14
14
|
| `/openapi.json` | The OpenAPI 3.1 description of the whole machine-readable surface. |
|
|
15
15
|
|
|
16
|
+
The page index leaves out [hidden pages](/docs/content/navigation#hidden-pages), which get no per-page document either; `get_page` and the page's `.md` mirror still serve one, since hidden isn't private. Pages with `seo.noindex`, `ai.exclude`, or `search.exclude` are listed like any other. A page document's `markdown` is the page's source as agents read it, with its frontmatter block kept at the top.
|
|
17
|
+
|
|
16
18
|
The page index, per-page documents, and navigation are prerendered, so a static site serves them as files from any host. Search is a live endpoint and exists only under [server output](/docs/deployment#server-rendering), where it runs the same index `search_docs` does.
|
|
17
19
|
|
|
18
20
|
## Errors
|
|
@@ -58,7 +58,7 @@ ai:
|
|
|
58
58
|
---
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
The page still renders, stays in search, and keeps its place in the sitemap — only the `llms.txt` files skip it.
|
|
61
|
+
The page still renders, stays in search, and keeps its place in the sitemap — only the `llms.txt` files and the page map in your [site's skill](/docs/discoverability/agent-discovery#your-sites-skill) skip it.
|
|
62
62
|
|
|
63
63
|
## Bringing your own
|
|
64
64
|
|
|
@@ -17,7 +17,7 @@ Append `.md` or `.mdx` to any page's URL to fetch its raw Markdown source — pe
|
|
|
17
17
|
|
|
18
18
|
Nested routes work the same way (`/content/syntax.md`), and the home page is served at `/index.md`.
|
|
19
19
|
|
|
20
|
-
The `.md` variant _downlevels_ components to plain Markdown for consumers that can't interpret JSX: `<TypeTable>` becomes a Markdown table, `<Callout>` a labeled blockquote, `<Steps>` an ordered list, `<Tabs>` bold-labeled sections, `<Card>` its title as a link over its body (and `<CardGroup>` the cards it holds), `<Accordion>` bold questions over their answers, `<FileTree>` its list, `<CodeGroup>` its titled code blocks, `<YouTube>` a link, and every other built-in component — Columns, Frame, Expandable, Badge, Tooltip, and the rest — its readable content. `<AutoTypeTable>`, which needs the type checker, stays as written, as do a `<Diff>` that reads its sides from files (`src`, or `before` and `after`) and a `<GithubInfo>` without `owner` and `repo`. The components a generated [API reference](/docs/references/openapi) page is made of downlevel too: `<Operation>` becomes the endpoint in its spec's own notation (`GET /pets/{id}`, `SEND user/signup`, `query pets`) with a deprecation marker, `<ApiTagOperations>` a list of those endpoints linked to their pages with their summaries, and `<ApiOverview>` the API's version and base URLs — so an agent reading a reference page knows what to call, and site search matches an endpoint's path. Props are read as literal data and never executed — strings, numbers, booleans, arrays, objects, and template strings, plus references to the page's `frontmatter`, so a prop like `title={frontmatter.status}` resolves to the same value the rendered page shows. Anything that can't be converted faithfully — a custom component, or a prop computed by a function call or from an import — is left as-is, and component markup inside code, fenced or inline, is never touched. The same conversion applies to [`llms-full.txt`](/docs/discoverability/llms-txt) and the [MCP server](/docs/discoverability/mcp)'s `get_page` tool, so every agent-facing surface reads clean Markdown.
|
|
20
|
+
The `.md` variant _downlevels_ components to plain Markdown for consumers that can't interpret JSX: `<TypeTable>` becomes a Markdown table, `<Callout>` a labeled blockquote, `<Steps>` an ordered list, `<Tabs>` bold-labeled sections, `<Card>` its title as a link over its body (and `<CardGroup>` the cards it holds), `<Accordion>` bold questions over their answers, `<FileTree>` its list, `<CodeGroup>` its titled code blocks, `<YouTube>` a link, and every other built-in component — Columns, Frame, Expandable, Badge, Tooltip, and the rest — its readable content. `<AutoTypeTable>`, which needs the type checker, stays as written, as do a `<Diff>` that reads its sides from files (`src`, or `before` and `after`) and a `<GithubInfo>` without `owner` and `repo`. The components a generated [API reference](/docs/references/openapi) page is made of downlevel too: `<Operation>` becomes the endpoint in its spec's own notation (`GET /pets/{id}`, `SEND user/signup`, `query pets`) with a deprecation marker, `<ApiTagOperations>` a list of those endpoints linked to their pages with their summaries, and `<ApiOverview>` the API's version and base URLs — so an agent reading a reference page knows what to call, and site search matches an endpoint's path. Props are read as literal data and never executed — strings, numbers, booleans, arrays, objects, and template strings, plus references to the page's `frontmatter`, so a prop like `title={frontmatter.status}` resolves to the same value the rendered page shows. Only props resolve: an expression in the page's own text, like `Owned by {frontmatter.owner}`, stays as written in the Markdown copy. For a value agents should read too, write it into the text, or, when it's the same on every page, make it a [content variable](/docs/content/variables), which every surface replaces. Anything that can't be converted faithfully — a custom component, or a prop computed by a function call or from an import — is left as-is, and component markup inside code, fenced or inline, is never touched. The same conversion applies to [`llms-full.txt`](/docs/discoverability/llms-txt) and the [MCP server](/docs/discoverability/mcp)'s `get_page` tool, so every agent-facing surface reads clean Markdown.
|
|
21
21
|
|
|
22
22
|
When you want the MDX itself, use the `.mdx` variant: its components stay as written, and the rest is prepared for an agent reading it by URL, as in the `.md` variant. [Includes](/docs/content/includes) are spliced in, [`<Visibility>`](/docs/content/components#visibility) resolves for agents (`for="web"` content is removed, `for="agents"` content kept), relative images point at their served URLs, relative page links point at the routes they mean, and root-relative links (`/guides/install`) gain the site's `deployment.base` and `basePath` and, on a translated page, move into the page's locale, the way the rendered page's links do. Root-relative images and links to files in `public/` (`/spec.pdf`) gain `deployment.base` alone, since that's where those files are served.
|
|
23
23
|
|
|
@@ -25,6 +25,8 @@ When you want the MDX itself, use the `.mdx` variant: its components stay as wri
|
|
|
25
25
|
|
|
26
26
|
Agents don't need to know the `.md` convention: requesting a page's own URL with an [`Accept: text/markdown`](https://acceptmarkdown.com) header serves the Markdown variant at the same address, with `Vary: Accept` so caches keep the two apart. The dev server honors the header out of the box, and a [Vercel or Cloudflare server build](/docs/deployment#server-rendering) wires the same negotiation into the deploy automatically — routing rules on Vercel, a generated Worker on Cloudflare — no configuration needed. The homepage always negotiates, even when it's a custom landing page rather than a content page: its Markdown mirror falls back to the [`llms.txt`](/docs/discoverability/llms-txt) index, so an agent asking the site root for Markdown gets the machine-readable map of the site. Markdown responses also carry an `x-markdown-tokens` header — an estimated token count (~4 characters per token), following the convention of [Cloudflare's Markdown for Agents](https://developers.cloudflare.com/fundamentals/reference/markdown-for-agents/) — on every surface where Blume controls response headers: the dev server, server-rendered responses, and the negotiated homepage on Vercel and Cloudflare. Other deploy targets serve prerendered pages from a static layer with no request-time hook, so agents there fetch the `.md` URL directly; the [agent readability manifest](/docs/discoverability/agent-discovery#agent-readability) advertises `contentNegotiation` only on deployments that honor the header.
|
|
27
27
|
|
|
28
|
+
A client gets Markdown when its `Accept` header lists `text/markdown` (or `text/x-markdown`, in any case) with a q-value above zero and at least as high as `text/html`'s: `text/markdown` and `text/markdown, text/html` get Markdown, `text/html, text/markdown;q=0.9` gets HTML. Vercel's routing rules can't compare two q-values, so when both types carry one below 1 (`text/markdown;q=0.9, text/html;q=0.8`), Vercel serves HTML where the dev server and Cloudflare serve Markdown. Send `text/markdown` without a q-value to get Markdown everywhere.
|
|
29
|
+
|
|
28
30
|
Missing pages negotiate too. The default [404 page](/docs/advanced/custom-pages#404-page) has a Markdown twin at `/404.md` — the not-found message followed by recovery links to every top-level section, the sitemap, and `llms.txt` — and on a Vercel or Cloudflare server build a request for a nonexistent URL that prefers Markdown gets that body with a real `404` status rather than the HTML shell. On Vercel the same goes for any `.md` URL with no page behind it. A 404 page of your own (a `pages/404.astro`, or a content page at `/404`) replaces the default along with its `/404.md` twin, so missing pages then answer with your HTML page.
|
|
29
31
|
|
|
30
32
|
### Custom component serializers
|
|
@@ -23,12 +23,16 @@ agents: {
|
|
|
23
23
|
|
|
24
24
|
If a content or custom page already owns the route, the server isn't generated: the build warns, and `llms.txt` and the other [discovery documents](/docs/discoverability/agent-discovery) leave it out.
|
|
25
25
|
|
|
26
|
+
[Add an MCP server to your docs](/guides/mcp-server-for-documentation) walks through it from turning it on to deploying the server and connecting a client.
|
|
27
|
+
|
|
26
28
|
## Tools and resources
|
|
27
29
|
|
|
28
30
|
The server exposes read-only tools — `search_docs`, `get_page`, `list_pages`, and `get_navigation` — and every page as an MCP resource (`resources/list` enumerates the pages at their served URLs with a `text/markdown` type, leaving out i18n fallback copies of untranslated pages as `list_pages` does; `resources/read` returns the page's [agent Markdown](/docs/discoverability/markdown), the same output as `get_page`), so clients that attach context by URI can browse the docs without calling a tool. It publishes discovery documents at `/.well-known/mcp.json` and `/.well-known/mcp/server-card.json`. The server card follows the [SEP-2127](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2127) Server Card extension schema (reverse-DNS `name`, `remotes` transport endpoints), with initialize-shaped compat fields (`serverInfo`, `capabilities`, `transports`) for scanners built against the proposal's earlier revision. Each page's **Connect to MCP** menu offers copy-and-go install for Claude Code, Cursor, VS Code, and Codex (shown once [`deployment.site`](/docs/deployment) is set).
|
|
29
31
|
|
|
30
32
|
`search_docs` runs its own full-text index, so it works regardless of your [search](/docs/configuration/search) provider — and even with `search: false`. The MCP server is a separate feature from on-page search.
|
|
31
33
|
|
|
34
|
+
[Hidden pages](/docs/content/navigation#hidden-pages) stay out of `list_pages` and `resources/list`, and out of `search_docs` unless [`search.indexing.includeHiddenPages`](/docs/configuration/search#excluding-pages) is on, as they stay out of site search and `llms.txt`. `get_page` and `resources/read` still return a hidden page by its route: hidden isn't private, and the page is still served at its URL and as Markdown.
|
|
35
|
+
|
|
32
36
|
The same tools are available over plain HTTP as the [JSON API](/docs/discoverability/json-api), for frameworks that don't speak MCP.
|
|
33
37
|
|
|
34
38
|
The endpoint reads a request body only up to 64 KB and answers anything larger with `413`. A call to a tool that doesn't exist, or with `arguments` that aren't an object, gets the JSON-RPC Invalid params error (`-32602`).
|
|
@@ -49,6 +53,8 @@ Both tools also accept a `filters` object matching against the facets a site dec
|
|
|
49
53
|
|
|
50
54
|
Every `filters` entry must match (results carry their facet values, and `list_pages` shows each page's), so a knowledge base can drive progressive-disclosure agent workflows — enumerate the enforced standards, search only within them — without any server of its own.
|
|
51
55
|
|
|
56
|
+
A value matches exactly, case included, as `list_pages` shows it: `"enforced"` doesn't match a page whose `status` is `Enforced`. Facet values are strings, so a number or boolean in `filters` matches its string form (`{"priority": 1}` finds `priority: 1`), and a value of any other shape, such as a list, is ignored rather than filtering.
|
|
57
|
+
|
|
52
58
|
## Server output required
|
|
53
59
|
|
|
54
60
|
The MCP server is a live endpoint (`/mcp`), so it can't run on a static build. Name a host adapter from `blume/deploy` to switch to server output:
|
|
@@ -7,7 +7,7 @@ Search crawlers and AI crawlers both start from the same two files at your site
|
|
|
7
7
|
|
|
8
8
|
## Sitemap
|
|
9
9
|
|
|
10
|
-
Blume writes a `sitemap.xml` of every indexable page at build time. It needs an absolute [`deployment.site`](/docs/deployment) and lists every page except drafts, hidden, and [`noindex`](/docs/discoverability/metadata#per-page-overrides) pages. On a [versioned](/docs/content/versioning) site, archived pages whose canonical points at their live equivalent are left out too — the live page is the one to index. On by default:
|
|
10
|
+
Blume writes a `sitemap.xml` of every indexable page at build time. It needs an absolute [`deployment.site`](/docs/deployment) and lists every page except drafts, hidden, and [`noindex`](/docs/discoverability/metadata#per-page-overrides) pages, and pages whose `seo.canonical` names another URL. On a [versioned](/docs/content/versioning) site, archived pages whose canonical points at their live equivalent are left out too — the live page is the one to index. On by default:
|
|
11
11
|
|
|
12
12
|
```ts blume.config.ts lineNumbers
|
|
13
13
|
seo: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Structured data
|
|
3
|
-
description: The schema.org JSON-LD every page carries — WebSite,
|
|
3
|
+
description: The schema.org JSON-LD every page carries — WebSite, page, and breadcrumb nodes — plus the optional Organization and SoftwareApplication blocks that tell search engines and agents who you are.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Blume emits [schema.org](https://schema.org) JSON-LD in every page's `<head>` so search engines understand your content. On by default:
|
|
@@ -14,10 +14,12 @@ seo: {
|
|
|
14
14
|
Each page includes:
|
|
15
15
|
|
|
16
16
|
- a **WebSite** node for site identity,
|
|
17
|
-
- the page
|
|
18
|
-
- a **BreadcrumbList** built from the navigation trail.
|
|
17
|
+
- the page itself, with its description, language, and publish date: a **WebPage** on the homepage, and an **article** everywhere else — `BlogPosting` for blog posts, `TechArticle` for changelog and docs,
|
|
18
|
+
- on an article, a **BreadcrumbList** built from the navigation trail.
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
The homepage is the root of the navigation, so each locale's home (`/fr`) and the root of a [`basePath`](/docs/deployment#mount-the-docs-under-a-path) are WebPages too.
|
|
21
|
+
|
|
22
|
+
URLs are absolute when [`deployment.site`](/docs/deployment) is set. Without it they're route-relative, and the WebSite node, which needs an absolute identifier, is left out. Pages marked [`seo.noindex`](/docs/discoverability/metadata#per-page-overrides) are skipped. When [`lastModified`](/docs/configuration#last-modified) is on, each page's date is also emitted as `dateModified`, and the visible "Last updated" date is marked up as a `<time datetime>`, so search engines read that date rather than some other date on the page.
|
|
21
23
|
|
|
22
24
|
## Site identity
|
|
23
25
|
|
|
@@ -42,10 +44,10 @@ seo: {
|
|
|
42
44
|
}
|
|
43
45
|
```
|
|
44
46
|
|
|
45
|
-
`organization` adds an **Organization** node to every page — the WebSite and
|
|
47
|
+
`organization` adds an **Organization** node to every page — the WebSite and page nodes cite it as `publisher` — with the email and telephone as a `ContactPoint` (`contactType` defaults to `"customer support"`) and the address as a `PostalAddress`, the two fields business-verification checks look for. `name` and `url` default to the site's; a root-relative `logo` is absolutized like any page URL.
|
|
46
48
|
|
|
47
49
|
`software` adds a **SoftwareApplication** node to the homepage: the product's name and description (defaulting to the site's), `applicationCategory` (default `"DeveloperApplication"`), operating system, license, an `Offer` when `price` is set, and the registry or repository URLs in `sameAs`. Pass `software: true` to take every default. The organization, when configured, is cited as its `publisher`.
|
|
48
50
|
|
|
49
51
|
## Custom pages
|
|
50
52
|
|
|
51
|
-
A custom [`.astro` page](/docs/advanced/custom-pages) rendered through `PageLayout` emits the same
|
|
53
|
+
A custom [`.astro` page](/docs/advanced/custom-pages) rendered through `PageLayout` emits the same graph, so a marketing home page isn't the one URL without structured data. A custom page has no content file to date, so pass `lastModified` to give it a `dateModified`. Pass `structuredDataEnabled={config.structuredData}` to keep it in sync with this setting, or `structuredDataEnabled={false}` to turn it off for that page alone.
|
|
@@ -42,7 +42,7 @@ Creates a user and sends them an invite.
|
|
|
42
42
|
|
|
43
43
|
## The playground and samples
|
|
44
44
|
|
|
45
|
-
The page's `ParamField`s become the Try it panel's inputs, the way an operation's parameters do in a spec: `path`, `query`, and `header` fields are parameters, and `body` fields make up a JSON body, with a field's nested fields (inside its [`Expandable`](/docs/content/components#expandable)) as the properties of an object. A `type` of `string[]` is an array, and one that isn't a JSON type, such as `enum<string>`, is sent as a string. A field's `default` fills the body, and its `placeholder` is the example value the samples show.
|
|
45
|
+
The page's `ParamField`s become the Try it panel's inputs, the way an operation's parameters do in a spec: `path`, `query`, and `header` fields are parameters, and `body` fields make up a JSON body, with a field's nested fields (inside its [`Expandable`](/docs/content/components#expandable)) as the properties of an object. A `type` of `string[]` is an array, and one that isn't a JSON type, such as `enum<string>`, is sent as a string. A field's `default` fills the body, and its `placeholder` is the example value the samples show. An optional body field with neither starts empty and stays out of the request until the reader fills it in.
|
|
46
46
|
|
|
47
47
|
Beside the panel, the page gets request samples in cURL, JavaScript, and Python. A page with its own `RequestExample` shows that in their place.
|
|
48
48
|
|
|
@@ -3,7 +3,7 @@ title: AsyncAPI
|
|
|
3
3
|
description: Drop in an AsyncAPI spec and get a native event reference — one real page per send and receive operation, in your sidebar and search.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Event-driven APIs use the `asyncapi()` adapter from `blume/reference`, listed under `reference` beside any [OpenAPI](/docs/references/openapi) or [GraphQL](/docs/references/graphql) adapters. It takes the same options as `openapi()` and renders with the same native renderer. Each `send`/`receive` operation becomes a real page with message payload and header schema tables, channel parameters, protocol bindings, an Authorization section derived from the spec's `securitySchemes` (server-level and operation-level, alternatives as "or" groups), and a [Try it](#try-it-for-events) message composer. Because each operation is a genuine Blume page, it gets its own URL, shows up in **site search** and `llms.txt`, and gets an Open Graph image — the same as any hand-written doc.
|
|
6
|
+
Event-driven APIs use the `asyncapi()` adapter from `blume/reference`, listed under `reference` beside any [OpenAPI](/docs/references/openapi) or [GraphQL](/docs/references/graphql) adapters. It takes the same options as `openapi()` and renders with the same native renderer. Each `send`/`receive` operation becomes a real page with message payload and header schema tables, channel parameters, protocol bindings, an Authorization section derived from the spec's `securitySchemes` (server-level and operation-level, alternatives as "or" groups, and when a channel's servers differ, which servers each scheme applies to), and a [Try it](#try-it-for-events) message composer. Because each operation is a genuine Blume page, it gets its own URL, shows up in **site search** and `llms.txt`, and gets an Open Graph image — the same as any hand-written doc.
|
|
7
7
|
|
|
8
8
|
```ts blume.config.ts lineNumbers
|
|
9
9
|
import { defineConfig } from "blume";
|
|
@@ -22,6 +22,8 @@ navigation: {
|
|
|
22
22
|
}
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
[Document Kafka events with AsyncAPI](/guides/asyncapi-kafka-documentation) walks through it for Kafka topics, with producer and consumer code beside the reference.
|
|
26
|
+
|
|
25
27
|
## Spec versions
|
|
26
28
|
|
|
27
29
|
AsyncAPI **2.x specs are normalized to 3.x automatically** with the official AsyncAPI converter, so `publish`/`subscribe` channels map onto `send`/`receive` operation pages with stable URLs — later upgrading the spec file itself through the converter moves nothing. The converter is an optional peer dependency, so a site with a 1.x or 2.x spec installs it (`npm install @asyncapi/converter`); without it the build fails with that install command. A 3.x spec needs nothing extra. Operations group by tag; untagged operations group under their channel address.
|
|
@@ -30,6 +32,8 @@ AsyncAPI **2.x specs are normalized to 3.x automatically** with the official Asy
|
|
|
30
32
|
|
|
31
33
|
Code samples are **protocol-aware**, keyed off the operation's binding (or its servers' protocol): `wscat` and a browser `WebSocket` snippet for WebSockets, `kcat` for Kafka, `mosquitto_pub`/`mosquitto_sub` for MQTT. `codeSamples` filters that set, the same way it picks languages on `openapi()`, and `codeSamples: false` shows none. A protocol without a supported tool renders the message payload example alone rather than a fabricated client.
|
|
32
34
|
|
|
35
|
+
The `kcat` sample reads the Kafka bindings too. A channel binding's `topic` replaces the channel address as the topic name. A message binding's `key` keys the message: the sample produces `key|payload` lines with `-K '|'`, and consumes with the same flag so each key prints before its message. The key value comes from the key schema like a payload's does, so give the schema an `examples` entry. When the server declares a SASL scheme (`plain`, `scramSha256`, `scramSha512`, or `gssapi`), the sample adds the `-X` settings librdkafka needs, reading the username and password from `$KAFKA_USERNAME` and `$KAFKA_PASSWORD`. A `kafka-secure` server adds TLS.
|
|
36
|
+
|
|
33
37
|
## Shared options
|
|
34
38
|
|
|
35
39
|
Everything documented for [OpenAPI](/docs/references/openapi) carries over, [`playground`](#try-it-for-events) included: [`route`](/docs/references/openapi#route), [`sources`](/docs/references/openapi#multiple-specs) with `label`/`route`, `expandSchemas`, the [per-source indexing](/docs/references/openapi#per-source-indexing) flags (`seoDescriptionSuffix` too — the generated sentence names the channel and action instead of an endpoint), and search indexing by operation summary, description, tag, and endpoint (`SEND user/signup`).
|