blume 2.1.0 → 2.1.1
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 +94 -0
- package/dist/cli/{chunk-5cg2rzgf.js → chunk-073rdvgb.js} +19 -48
- package/dist/cli/{chunk-5cg2rzgf.js.map → chunk-073rdvgb.js.map} +4 -5
- 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-150jk7km.js +52 -0
- package/dist/cli/chunk-150jk7km.js.map +10 -0
- package/dist/cli/{chunk-rekqz4qg.js → chunk-313vnn40.js} +16 -15
- package/dist/cli/chunk-313vnn40.js.map +10 -0
- package/dist/cli/{chunk-gp84za0h.js → chunk-3v3ffbrj.js} +2 -2
- package/dist/cli/{chunk-9xr4dgee.js → chunk-3x6x3pph.js} +29 -16
- package/dist/cli/chunk-3x6x3pph.js.map +10 -0
- package/dist/cli/{chunk-0gz36akr.js → chunk-42t4jxdx.js} +21 -5
- package/dist/cli/chunk-42t4jxdx.js.map +12 -0
- package/dist/cli/{chunk-ta27feev.js → chunk-54ztw1nj.js} +21 -2
- package/dist/cli/chunk-54ztw1nj.js.map +10 -0
- package/dist/cli/{chunk-jnwj5kjj.js → chunk-5vjtgs6e.js} +20 -7
- package/dist/cli/{chunk-jnwj5kjj.js.map → chunk-5vjtgs6e.js.map} +3 -3
- package/dist/cli/chunk-66w9j7p6.js +322 -0
- package/dist/cli/chunk-66w9j7p6.js.map +14 -0
- package/dist/cli/{chunk-nd1821sk.js → chunk-7ppf1rva.js} +32 -18
- package/dist/cli/chunk-7ppf1rva.js.map +10 -0
- package/dist/cli/{chunk-p804f3pe.js → chunk-8y2kmjv8.js} +6 -6
- package/dist/cli/{chunk-2f7n4qzt.js → chunk-c1y538kd.js} +1 -1
- package/dist/cli/{chunk-1n228xqb.js → chunk-c887fj0s.js} +2 -2
- package/dist/cli/{chunk-y5h4r2y0.js → chunk-cfyg47zg.js} +15 -5
- package/dist/cli/chunk-cfyg47zg.js.map +10 -0
- package/dist/cli/{chunk-cmtcnh9f.js → chunk-dzwehpy7.js} +2 -2
- package/dist/cli/{chunk-h2a48yhd.js → chunk-e6atmn4h.js} +119 -15
- package/dist/cli/{chunk-h2a48yhd.js.map → chunk-e6atmn4h.js.map} +10 -10
- package/dist/cli/{chunk-5ffywsmb.js → chunk-e8xw9qkh.js} +8 -6
- package/dist/cli/{chunk-5ffywsmb.js.map → chunk-e8xw9qkh.js.map} +3 -3
- package/dist/cli/{chunk-ntv4qrt7.js → chunk-e9q7a2ak.js} +14 -14
- package/dist/cli/{chunk-xkz5xbtv.js → chunk-ehweg4fr.js} +2366 -1114
- package/dist/cli/chunk-ehweg4fr.js.map +218 -0
- package/dist/cli/{chunk-zf748avv.js → chunk-ep6nd0rn.js} +176 -281
- package/dist/cli/{chunk-zf748avv.js.map → chunk-ep6nd0rn.js.map} +11 -12
- package/dist/cli/{chunk-yxmk4pec.js → chunk-fdrkhpjy.js} +16 -15
- package/dist/cli/{chunk-yxmk4pec.js.map → chunk-fdrkhpjy.js.map} +3 -3
- package/dist/cli/{chunk-5wj2qwn2.js → chunk-ft9vfys6.js} +4 -4
- package/dist/cli/{chunk-4h8z9vv5.js → chunk-gb420vvz.js} +89 -46
- package/dist/cli/chunk-gb420vvz.js.map +17 -0
- package/dist/cli/{chunk-713mngdq.js → chunk-hebn0tpx.js} +76 -10
- package/dist/cli/chunk-hebn0tpx.js.map +14 -0
- package/dist/cli/{chunk-2n4ps1b3.js → chunk-htrwgabr.js} +40 -22
- package/dist/cli/chunk-htrwgabr.js.map +15 -0
- package/dist/cli/{chunk-zhvd6adm.js → chunk-hx3xqnk9.js} +13 -13
- package/dist/cli/chunk-j134vrp2.js +65 -0
- package/dist/cli/chunk-j134vrp2.js.map +10 -0
- package/dist/cli/{chunk-y4ykbark.js → chunk-kma9f6sf.js} +2 -2
- package/dist/cli/{chunk-py62fewh.js → chunk-mrcccgnh.js} +3 -3
- package/dist/cli/{chunk-2f3ndkpw.js → chunk-n0v7m8ry.js} +3 -3
- package/dist/cli/{chunk-43zcf4gz.js → chunk-ncj7w596.js} +522 -353
- package/dist/cli/chunk-ncj7w596.js.map +35 -0
- package/dist/cli/{chunk-9geymqns.js → chunk-nq1d20w2.js} +14 -14
- package/dist/cli/{chunk-g2z9x01v.js → chunk-nrns10mj.js} +3 -3
- package/dist/cli/{chunk-e7vvk5ya.js → chunk-sds923fa.js} +3 -3
- package/dist/cli/{chunk-cy5bm0j0.js → chunk-t2m5j4pr.js} +2 -2
- package/dist/cli/{chunk-cy5bm0j0.js.map → chunk-t2m5j4pr.js.map} +1 -1
- package/dist/cli/{chunk-yj17vs39.js → chunk-tdqhyxfz.js} +5 -5
- package/dist/cli/{chunk-q3wxxq39.js → chunk-tphac74f.js} +5 -5
- package/dist/cli/{chunk-6bm0xpgd.js → chunk-w1r4vase.js} +2 -2
- 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/component-markdown.d.ts +2 -0
- package/dist/types/ai/openapi-components.d.ts +4 -1
- package/dist/types/astro/integration.d.ts +5 -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 +30 -3
- 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/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 +119 -0
- 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/normalize.d.ts +20 -1
- 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/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 +20 -1
- 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 +42 -21
- 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/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/sources/contentful.d.ts +16 -0
- package/dist/types/sources/github-releases.d.ts +8 -1
- package/dist/types/sources/payload.d.ts +2 -0
- package/dist/types/sources/registry.d.ts +54 -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 +31 -13
- package/docs/advanced/changelog.mdx +11 -0
- package/docs/advanced/skills.mdx +1 -1
- package/docs/cli/audit.mdx +30 -7
- package/docs/cli/doctor.mdx +5 -1
- 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 +1 -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 +3 -2
- 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 +4 -4
- package/docs/content/sources/contentful.mdx +7 -4
- package/docs/content/sources/custom.mdx +7 -3
- package/docs/content/sources/github-releases.mdx +10 -2
- package/docs/content/sources/index.mdx +3 -3
- package/docs/content/sources/notion.mdx +1 -1
- package/docs/content/sources/obsidian.mdx +1 -1
- package/docs/content/sources/payload.mdx +3 -2
- package/docs/content/sources/remote-mdx.mdx +4 -0
- package/docs/content/sources/sanity.mdx +2 -2
- package/docs/content/sources/strapi.mdx +3 -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 +4 -0
- package/docs/discoverability/sitemap-and-robots.mdx +1 -1
- package/docs/references/api-pages.mdx +1 -1
- package/docs/references/asyncapi.mdx +3 -1
- package/docs/references/graphql.mdx +13 -1
- package/docs/references/openapi.mdx +50 -8
- package/package.json +9 -9
- package/skills/blume-migrate/SKILL.md +1 -1
- package/skills/blume-migrate/references/docusaurus.md +4 -4
- package/skills/blume-migrate/references/fumadocs.md +17 -13
- 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/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/astro/generate.ts +78 -48
- package/src/astro/integration.ts +16 -13
- 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/templates.ts +125 -80
- 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 +3 -3
- package/src/cli/prepare.ts +6 -3
- package/src/cli/required-secrets.ts +1 -1
- 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/islands/hooks.ts +27 -1
- package/src/components/layout/PageActions.astro +139 -24
- package/src/components/layout/RootLayout.astro +10 -2
- package/src/components/layout/Search.astro +17 -3
- package/src/components/layout/TableOfContents.astro +24 -17
- 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 +24 -5
- 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/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 +31 -3
- 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/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 +21 -2
- 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/mdx-remote.ts +62 -10
- package/src/core/sources/normalize.ts +227 -41
- package/src/core/sources/notion.ts +7 -1
- 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/platforms/cloudflare.ts +13 -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 +113 -16
- 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 +290 -102
- 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/relative-links.ts +3 -2
- 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/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 +3 -1
- 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/sources/contentful.ts +12 -0
- package/src/sources/github-releases.ts +7 -1
- package/src/sources/shared.ts +3 -0
- package/src/theme/entry.ts +29 -3
- 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-gp84za0h.js.map → chunk-3v3ffbrj.js.map} +0 -0
- /package/dist/cli/{chunk-p804f3pe.js.map → chunk-8y2kmjv8.js.map} +0 -0
- /package/dist/cli/{chunk-2f7n4qzt.js.map → chunk-c1y538kd.js.map} +0 -0
- /package/dist/cli/{chunk-1n228xqb.js.map → chunk-c887fj0s.js.map} +0 -0
- /package/dist/cli/{chunk-cmtcnh9f.js.map → chunk-dzwehpy7.js.map} +0 -0
- /package/dist/cli/{chunk-ntv4qrt7.js.map → chunk-e9q7a2ak.js.map} +0 -0
- /package/dist/cli/{chunk-5wj2qwn2.js.map → chunk-ft9vfys6.js.map} +0 -0
- /package/dist/cli/{chunk-zhvd6adm.js.map → chunk-hx3xqnk9.js.map} +0 -0
- /package/dist/cli/{chunk-y4ykbark.js.map → chunk-kma9f6sf.js.map} +0 -0
- /package/dist/cli/{chunk-py62fewh.js.map → chunk-mrcccgnh.js.map} +0 -0
- /package/dist/cli/{chunk-2f3ndkpw.js.map → chunk-n0v7m8ry.js.map} +0 -0
- /package/dist/cli/{chunk-9geymqns.js.map → chunk-nq1d20w2.js.map} +0 -0
- /package/dist/cli/{chunk-g2z9x01v.js.map → chunk-nrns10mj.js.map} +0 -0
- /package/dist/cli/{chunk-e7vvk5ya.js.map → chunk-sds923fa.js.map} +0 -0
- /package/dist/cli/{chunk-yj17vs39.js.map → chunk-tdqhyxfz.js.map} +0 -0
- /package/dist/cli/{chunk-q3wxxq39.js.map → chunk-tphac74f.js.map} +0 -0
- /package/dist/cli/{chunk-6bm0xpgd.js.map → chunk-w1r4vase.js.map} +0 -0
package/src/astro/integration.ts
CHANGED
|
@@ -342,10 +342,11 @@ export interface BlumeIntegrationOptions {
|
|
|
342
342
|
*/
|
|
343
343
|
buildArtifactsRoot?: string;
|
|
344
344
|
/**
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
-
* carry only the exact ones, since it can't
|
|
348
|
-
*
|
|
345
|
+
* Every configured redirect, compiled against base-less request paths
|
|
346
|
+
* (see `compileEveryRedirect`), for the dev server to answer itself.
|
|
347
|
+
* Astro's own `redirects` carry only the exact ones, since it can't
|
|
348
|
+
* prerender a pattern's pages, and its dev handler answers one whose
|
|
349
|
+
* destination `[...slug]` serves with a `301` whatever its status.
|
|
349
350
|
*/
|
|
350
351
|
redirects?: CompiledRedirect[];
|
|
351
352
|
}
|
|
@@ -398,10 +399,12 @@ const negotiateMarkdown =
|
|
|
398
399
|
if (homeLinkHeader && isHomeUrl(req.url)) {
|
|
399
400
|
res.setHeader("Link", homeLinkHeader);
|
|
400
401
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
402
|
+
const variant = markdownVariantUrl(req.url, contentRoutes);
|
|
403
|
+
if (variant) {
|
|
404
|
+
// Both answers at a negotiated URL depend on `Accept`, the HTML one
|
|
405
|
+
// too, so a cache never hands one client's variant to the other.
|
|
406
|
+
res.setHeader("Vary", "Accept");
|
|
407
|
+
if (prefersMarkdown(req.headers.accept)) {
|
|
405
408
|
req.url = variant;
|
|
406
409
|
}
|
|
407
410
|
}
|
|
@@ -410,11 +413,11 @@ const negotiateMarkdown =
|
|
|
410
413
|
};
|
|
411
414
|
|
|
412
415
|
/**
|
|
413
|
-
* Answer a
|
|
414
|
-
*
|
|
415
|
-
* {@link negotiateMarkdown}), and the
|
|
416
|
+
* Answer a configured redirect in dev with its status, the way the server
|
|
417
|
+
* wrappers do in a build. Request URLs arrive base-less (see
|
|
418
|
+
* {@link negotiateMarkdown}), and the redirects were based to match.
|
|
416
419
|
*/
|
|
417
|
-
const
|
|
420
|
+
const answerRedirects =
|
|
418
421
|
(redirects: readonly CompiledRedirect[]) =>
|
|
419
422
|
(req: IncomingMessage, res: ServerResponse, next: () => void): void => {
|
|
420
423
|
const answer = matchCompiledRedirect(redirects, requestPath(req.url ?? ""));
|
|
@@ -533,7 +536,7 @@ export const blumeIntegration = (
|
|
|
533
536
|
});
|
|
534
537
|
if (options.redirects) {
|
|
535
538
|
server.middlewares.stack.unshift({
|
|
536
|
-
handle:
|
|
539
|
+
handle: answerRedirects(options.redirects),
|
|
537
540
|
route: "",
|
|
538
541
|
});
|
|
539
542
|
}
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
* helpers (`deploy/cloudflare-negotiation.ts` — its deploy bundle is uploaded
|
|
9
9
|
* unbundled, so it cannot import this module); when editing here, mirror the
|
|
10
10
|
* change there. Parity is enforced by `test/cloudflare-negotiation.test.ts`.
|
|
11
|
+
* Vercel's routing conditions (`deploy/vercel-negotiation.ts`) spell the same
|
|
12
|
+
* rule as regexes, checked against {@link prefersMarkdown} by
|
|
13
|
+
* `test/vercel-negotiation.test.ts`.
|
|
11
14
|
*/
|
|
12
15
|
|
|
13
16
|
interface AcceptEntry {
|
|
@@ -82,7 +82,9 @@ declare module "blume:consent-client" {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
declare module "blume:search-client" {
|
|
85
|
-
export const createSearch: (
|
|
85
|
+
export const createSearch: (
|
|
86
|
+
options?: import("blume/components/layout/search/types.ts").SearchClientOptions
|
|
87
|
+
) =>
|
|
86
88
|
| import("blume/components/layout/search/types.ts").SearchFn
|
|
87
89
|
| Promise<import("blume/components/layout/search/types.ts").SearchFn>;
|
|
88
90
|
}
|
package/src/astro/pages.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
} from "../core/changelog-index.ts";
|
|
5
5
|
import { staticSegments } from "../core/custom-pages.ts";
|
|
6
6
|
import type { BlumeProject } from "../core/project-graph.ts";
|
|
7
|
+
import { referenceRoutes } from "../openapi/references.ts";
|
|
7
8
|
import type { BlumePageRoute } from "./integration.ts";
|
|
8
9
|
|
|
9
10
|
export {
|
|
@@ -48,6 +49,28 @@ export const hasGeneratedChangelog = (
|
|
|
48
49
|
hasChangelogIndex(project.graph.pages, project.config) &&
|
|
49
50
|
!routeIsTaken(userPages, project.graph.pages, CHANGELOG_INDEX_ROUTE);
|
|
50
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Every route a navigation entry (a tab, selector item, featured link, header
|
|
54
|
+
* action, or CTA) may point at: content routes, custom `.astro` pages, the
|
|
55
|
+
* generated changelog, and reference routes (so a tab an author points at one
|
|
56
|
+
* still validates). `generateRuntime` and `blume doctor` both check
|
|
57
|
+
* `validateNavTargets` against it, so they flag the same entries.
|
|
58
|
+
*/
|
|
59
|
+
export const navTargetRoutes = (
|
|
60
|
+
project: BlumeProject,
|
|
61
|
+
userPages: { pattern: string }[]
|
|
62
|
+
): Set<string> => {
|
|
63
|
+
const routes = new Set<string>([
|
|
64
|
+
...project.graph.routes.keys(),
|
|
65
|
+
...userPages.map((page) => page.pattern),
|
|
66
|
+
...referenceRoutes(project.config),
|
|
67
|
+
]);
|
|
68
|
+
if (hasGeneratedChangelog(project, userPages)) {
|
|
69
|
+
routes.add(CHANGELOG_INDEX_ROUTE);
|
|
70
|
+
}
|
|
71
|
+
return routes;
|
|
72
|
+
};
|
|
73
|
+
|
|
51
74
|
const humanizeSegment = (segment: string): string =>
|
|
52
75
|
segment
|
|
53
76
|
.split(/[-_]/u)
|
package/src/astro/templates.ts
CHANGED
|
@@ -4,13 +4,17 @@ import { pathToFileURL } from "node:url";
|
|
|
4
4
|
import { dirname, isAbsolute, join, relative } from "pathe";
|
|
5
5
|
|
|
6
6
|
import type { AskRetrievalOptions } from "../ai/ask-context.ts";
|
|
7
|
+
import { ASK_MAX_MESSAGES, ASK_MAX_MESSAGES_CHARS } from "../ai/ask-limits.ts";
|
|
7
8
|
import type { AskBackend } from "../ai/ask.ts";
|
|
8
9
|
import { buildHomeLinkHeader } from "../ai/link-headers.ts";
|
|
9
10
|
import type { CaptchaAdapter } from "../captcha/schema.ts";
|
|
10
11
|
import { CONSENT_CLIENT_MODULES } from "../consent/clients.ts";
|
|
11
12
|
import { normalizeBasePath } from "../core/base-path.ts";
|
|
12
13
|
import { TOC_HIDDEN_KEY } from "../core/heading-markers.ts";
|
|
13
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
compileEveryRedirect,
|
|
16
|
+
isPatternPath,
|
|
17
|
+
} from "../core/redirect-patterns.ts";
|
|
14
18
|
import type { CompiledRedirect } from "../core/redirect-patterns.ts";
|
|
15
19
|
import type { ResolvedConfig } from "../core/schema.ts";
|
|
16
20
|
import { resolveDocsCollection } from "../core/sources/collection.ts";
|
|
@@ -22,7 +26,10 @@ import { deployPassthrough } from "../deploy/adapters/types.ts";
|
|
|
22
26
|
import { SVG_ASSET_HEADERS } from "../deploy/headers.ts";
|
|
23
27
|
import { deployPlatform } from "../deploy/platforms/index.ts";
|
|
24
28
|
import { adapterRoot, distDir } from "../deploy/platforms/paths.ts";
|
|
25
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
applyBaseToAstroRedirects,
|
|
31
|
+
withMirrorRedirects,
|
|
32
|
+
} from "../deploy/redirects.ts";
|
|
26
33
|
import { API_RAIL_KEY } from "../markdown/api-rail.ts";
|
|
27
34
|
import { VIEWS_KEY } from "../markdown/views.ts";
|
|
28
35
|
import type { OgCache } from "../og/cache.ts";
|
|
@@ -34,6 +41,7 @@ import type {
|
|
|
34
41
|
ResolvedSearchAdapter,
|
|
35
42
|
SearchAdapterKind,
|
|
36
43
|
} from "../search/adapters/registry.ts";
|
|
44
|
+
import type { SourcePage } from "../search/source-pages.ts";
|
|
37
45
|
import { buildFontEntries, fontLocaleCodes } from "../theme/fonts.ts";
|
|
38
46
|
import { importSpecifier, wrapperPropsType } from "./component-slots.ts";
|
|
39
47
|
import type { ExampleSpec } from "./examples.ts";
|
|
@@ -122,7 +130,7 @@ const renderAstroAdapter = (
|
|
|
122
130
|
const { astro } = platform;
|
|
123
131
|
const args = {
|
|
124
132
|
...astro.options(context),
|
|
125
|
-
...deployPassthrough(deployment.options),
|
|
133
|
+
...deployPassthrough(deployment.options, astro.configOptions),
|
|
126
134
|
};
|
|
127
135
|
const argsLiteral = Object.keys(args).length > 0 ? JSON.stringify(args) : "";
|
|
128
136
|
const construct = `adapter(${argsLiteral})`;
|
|
@@ -133,7 +141,7 @@ const renderAstroAdapter = (
|
|
|
133
141
|
? `withAdapterRoot(${construct}, ${JSON.stringify(adapterRoot(context))})`
|
|
134
142
|
: construct;
|
|
135
143
|
return {
|
|
136
|
-
configEntries: Object.entries(astro.config)
|
|
144
|
+
configEntries: Object.entries(astro.config(deployment.options))
|
|
137
145
|
.map(([key, value]) => `\n ${key}: ${JSON.stringify(value)},`)
|
|
138
146
|
.join(""),
|
|
139
147
|
importLine: `import adapter from "${astro.package}";\n`,
|
|
@@ -283,18 +291,14 @@ const renderUserAliases = (
|
|
|
283
291
|
.join("");
|
|
284
292
|
|
|
285
293
|
/**
|
|
286
|
-
* Excludes
|
|
287
|
-
* react()
|
|
288
|
-
*
|
|
289
|
-
*
|
|
290
|
-
* re-
|
|
291
|
-
*
|
|
292
|
-
* resolve under `node_modules/blume/src`, and exclude beats include in the
|
|
293
|
-
* plugin's filter), so only the pre-bundle cache is excluded. The hidden runtime
|
|
294
|
-
* relocates that cache to `<runtime>/.cache/vite` (see `cacheOptions`), so both
|
|
295
|
-
* the default and the relocated path are excluded.
|
|
294
|
+
* Excludes the hidden runtime's pre-bundled dep cache from @vitejs/plugin-react.
|
|
295
|
+
* Astro's react() already excludes `/node_modules/`, which covers Vite's
|
|
296
|
+
* default `node_modules/.vite` cache, but the hidden runtime relocates that
|
|
297
|
+
* cache to `<runtime>/.cache/vite` (see `cacheOptions`). Without this the React
|
|
298
|
+
* Compiler re-transforms every optimized dep chunk served from there — a 500KB+
|
|
299
|
+
* vendor bundle per chunk, re-done on each re-optimization.
|
|
296
300
|
*/
|
|
297
|
-
const REACT_EXCLUDE = String.raw`exclude: [
|
|
301
|
+
const REACT_EXCLUDE = String.raw`exclude: [/\/\.cache\/vite\//]`;
|
|
298
302
|
|
|
299
303
|
/**
|
|
300
304
|
* The `cacheDir` entries for the generated config's top level and its `vite`
|
|
@@ -324,17 +328,14 @@ const runtimeCacheOptions = (
|
|
|
324
328
|
};
|
|
325
329
|
|
|
326
330
|
/**
|
|
327
|
-
* The `react()` integration call.
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
* specifier wouldn't resolve in a user project. `target: "19"` matches Blume's
|
|
332
|
-
* React pin. `null`/`undefined` (compiler off or unresolvable) omits the babel
|
|
333
|
-
* block. Both variants carry the pre-bundle exclude above.
|
|
331
|
+
* The `react()` integration call. `compiler` turns on @astrojs/react's React
|
|
332
|
+
* Compiler, which runs on `oxc-transform-react` and targets the installed
|
|
333
|
+
* React major; false/absent (compiler off or unresolvable) leaves it out. Both
|
|
334
|
+
* variants carry the pre-bundle exclude above.
|
|
334
335
|
*/
|
|
335
|
-
const reactIntegration = (
|
|
336
|
-
|
|
337
|
-
? `react({
|
|
336
|
+
const reactIntegration = (compiler: boolean | undefined): string =>
|
|
337
|
+
compiler
|
|
338
|
+
? `react({ compiler: true, ${REACT_EXCLUDE} })`
|
|
338
339
|
: `react({ ${REACT_EXCLUDE} })`;
|
|
339
340
|
|
|
340
341
|
interface IntegrationBridgeOptions {
|
|
@@ -388,11 +389,10 @@ interface OptimizeDepsConfig {
|
|
|
388
389
|
* the Vite root is the generated runtime, so user pages, convention islands,
|
|
389
390
|
* and alias-reachable components all live outside it and are otherwise only
|
|
390
391
|
* crawled when first requested. The compiler runtime rides the include list
|
|
391
|
-
* because
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
* the optimizeDeps comment in the generated config for the failure this prevents.
|
|
392
|
+
* because the compiler injects its import and no source scan can see it. The
|
|
393
|
+
* compiler plugin in `@vitejs/plugin-react` lists it too; Blume keeps it
|
|
394
|
+
* explicit so the guard doesn't hinge on that plugin's internals. See the
|
|
395
|
+
* optimizeDeps comment in the generated config for the failure this prevents.
|
|
396
396
|
*/
|
|
397
397
|
/**
|
|
398
398
|
* The client-side libraries a site needs, decided at generation time. A
|
|
@@ -432,7 +432,7 @@ const resolveOptimizeDeps = (options: {
|
|
|
432
432
|
context: ProjectContext;
|
|
433
433
|
features: ClientFeatures;
|
|
434
434
|
needsReact: boolean;
|
|
435
|
-
|
|
435
|
+
reactCompiler: boolean | undefined;
|
|
436
436
|
searchKind: SearchAdapterKind;
|
|
437
437
|
}): OptimizeDepsConfig => {
|
|
438
438
|
const { context, features } = options;
|
|
@@ -456,7 +456,7 @@ const resolveOptimizeDeps = (options: {
|
|
|
456
456
|
// (__PREFETCH_PREFETCH_ALL__ and friends) that a pre-bundled copy loses,
|
|
457
457
|
// throwing ReferenceError on every page. Astro manages their optimization
|
|
458
458
|
// itself, without a mid-session reload.
|
|
459
|
-
...(options.needsReact && options.
|
|
459
|
+
...(options.needsReact && options.reactCompiler
|
|
460
460
|
? ["react/compiler-runtime"]
|
|
461
461
|
: []),
|
|
462
462
|
];
|
|
@@ -612,11 +612,10 @@ export const astroConfigTemplate = (options: {
|
|
|
612
612
|
*/
|
|
613
613
|
generatedModulesDir?: string;
|
|
614
614
|
/**
|
|
615
|
-
*
|
|
616
|
-
*
|
|
617
|
-
* disables the compiler and emits a bare `react()`.
|
|
615
|
+
* Turn on the React Compiler (the caller checked that `oxc-transform-react`
|
|
616
|
+
* resolves); false/absent emits `react()` without it.
|
|
618
617
|
*/
|
|
619
|
-
|
|
618
|
+
reactCompiler?: boolean;
|
|
620
619
|
/** Project tsconfig path aliases (`find` -> absolute dir), e.g. `@` -> src. */
|
|
621
620
|
aliases?: Record<string, string>;
|
|
622
621
|
/**
|
|
@@ -664,7 +663,7 @@ export const astroConfigTemplate = (options: {
|
|
|
664
663
|
context,
|
|
665
664
|
features,
|
|
666
665
|
needsReact,
|
|
667
|
-
|
|
666
|
+
reactCompiler: options.reactCompiler,
|
|
668
667
|
searchKind: config.search.provider.kind,
|
|
669
668
|
});
|
|
670
669
|
|
|
@@ -700,11 +699,17 @@ export const astroConfigTemplate = (options: {
|
|
|
700
699
|
// Base the redirect paths the same way routes are based, so a redirect lands
|
|
701
700
|
// under `basePath` too. Astro layers its own `base` (deployment.base) onto
|
|
702
701
|
// `from` when matching, but never onto `to` — see applyBaseToAstroRedirects.
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
702
|
+
// A moved page's Markdown copies move with it (see withMirrorRedirects).
|
|
703
|
+
const redirectPages = new Set(contentRoutes);
|
|
704
|
+
const basedRedirects = withMirrorRedirects(
|
|
705
|
+
applyBaseToAstroRedirects(
|
|
706
|
+
config.redirects,
|
|
707
|
+
config.basePath,
|
|
708
|
+
deployment.options.base ?? "",
|
|
709
|
+
redirectPages
|
|
710
|
+
),
|
|
711
|
+
redirectPages,
|
|
712
|
+
{ from: "", to: normalizeBasePath(deployment.options.base) }
|
|
708
713
|
);
|
|
709
714
|
// Only exact redirects: Astro can't prerender a pattern's redirect pages
|
|
710
715
|
// (it would need every path the pattern covers), so a pattern reaches the
|
|
@@ -837,7 +842,7 @@ export const astroConfigTemplate = (options: {
|
|
|
837
842
|
`mdx({ processor: blumeMdxProcessor(${processorOptions}) })`,
|
|
838
843
|
];
|
|
839
844
|
if (needsReact) {
|
|
840
|
-
integrations.push(reactIntegration(options.
|
|
845
|
+
integrations.push(reactIntegration(options.reactCompiler));
|
|
841
846
|
}
|
|
842
847
|
if (needsVue) {
|
|
843
848
|
integrations.push("vue()");
|
|
@@ -855,7 +860,7 @@ export const astroConfigTemplate = (options: {
|
|
|
855
860
|
contentRoutes,
|
|
856
861
|
ejected,
|
|
857
862
|
pages,
|
|
858
|
-
redirects:
|
|
863
|
+
redirects: compileEveryRedirect(basedRedirects),
|
|
859
864
|
})
|
|
860
865
|
)})`
|
|
861
866
|
);
|
|
@@ -931,8 +936,8 @@ ${userConfigSetup}export default defineConfig({
|
|
|
931
936
|
// Everything hydration can reach must be part of the dev dep optimizer's
|
|
932
937
|
// FIRST run. The Vite root is the generated runtime, so user pages,
|
|
933
938
|
// islands, and aliased components live outside it and are only crawled
|
|
934
|
-
// when first requested — and \`react/compiler-runtime\` is
|
|
935
|
-
// so no source scan can ever see it. A dependency discovered after
|
|
939
|
+
// when first requested — and \`react/compiler-runtime\` is injected by the
|
|
940
|
+
// React Compiler, so no source scan can ever see it. A dependency discovered after
|
|
936
941
|
// hydration begins triggers a mid-session re-optimization whose new
|
|
937
942
|
// generation imports React through new \`?v=\` URLs; the browser then
|
|
938
943
|
// evaluates a second React copy and every island tears down with
|
|
@@ -1270,8 +1275,8 @@ const askCaptchaTemplate = (adapter?: CaptchaAdapter): RateLimitTemplate => {
|
|
|
1270
1275
|
|
|
1271
1276
|
/**
|
|
1272
1277
|
* Largest request body the assistant route reads: 64 KB, well above the
|
|
1273
|
-
*
|
|
1274
|
-
* never meets it.
|
|
1278
|
+
* message budget it validates next (`ai/ask-limits.ts`), so a real
|
|
1279
|
+
* conversation never meets it.
|
|
1275
1280
|
*/
|
|
1276
1281
|
const ASK_BODY_LIMIT_BYTES = 65_536;
|
|
1277
1282
|
|
|
@@ -1380,7 +1385,7 @@ ${limit.check} const text = await readCappedText(request, ${ASK_BODY_LIMIT_BYTE
|
|
|
1380
1385
|
const valid =
|
|
1381
1386
|
Array.isArray(raw) &&
|
|
1382
1387
|
raw.length > 0 &&
|
|
1383
|
-
raw.length <=
|
|
1388
|
+
raw.length <= ${ASK_MAX_MESSAGES} &&
|
|
1384
1389
|
raw.every(
|
|
1385
1390
|
(m: unknown) =>
|
|
1386
1391
|
typeof m === "object" &&
|
|
@@ -1388,10 +1393,10 @@ ${limit.check} const text = await readCappedText(request, ${ASK_BODY_LIMIT_BYTE
|
|
|
1388
1393
|
("role" in m && (m.role === "user" || m.role === "assistant")) &&
|
|
1389
1394
|
("content" in m && typeof m.content === "string")
|
|
1390
1395
|
) &&
|
|
1391
|
-
JSON.stringify(raw).length <=
|
|
1396
|
+
JSON.stringify(raw).length <= ${ASK_MAX_MESSAGES_CHARS};
|
|
1392
1397
|
if (!valid) {
|
|
1393
1398
|
return new Response(
|
|
1394
|
-
"Invalid request: send 1
|
|
1399
|
+
"Invalid request: send 1-${ASK_MAX_MESSAGES} user/assistant messages with string content.",
|
|
1395
1400
|
{ status: 400 }
|
|
1396
1401
|
);
|
|
1397
1402
|
}
|
|
@@ -1649,10 +1654,12 @@ export const searchClientTemplate = (config: ResolvedConfig): string => {
|
|
|
1649
1654
|
return hostedSearchClient(provider);
|
|
1650
1655
|
}
|
|
1651
1656
|
case "server": {
|
|
1652
|
-
|
|
1657
|
+
// The dialog passes `typing`, which paces its queries (see endpoint.ts).
|
|
1658
|
+
return `${SEARCH_CLIENT_HEADER}${searchClientImport("endpoint")}${SEARCH_BASE_IMPORT}import type { SearchClientOptions } from "blume/components/layout/search/types.ts";
|
|
1653
1659
|
const api = joinBase(import.meta.env.BASE_URL, "api/search");
|
|
1654
1660
|
|
|
1655
|
-
export const createSearch = (
|
|
1661
|
+
export const createSearch = (options: SearchClientOptions = {}) =>
|
|
1662
|
+
create({ ...options, api });
|
|
1656
1663
|
`;
|
|
1657
1664
|
}
|
|
1658
1665
|
case "pagefind": {
|
|
@@ -1674,14 +1681,17 @@ export const createSearch = () => create({ url });
|
|
|
1674
1681
|
/**
|
|
1675
1682
|
* Generate the Mixedbread search endpoint (`/api/search`). It holds the secret
|
|
1676
1683
|
* key server-side and proxies semantic queries to the configured store. The
|
|
1677
|
-
* adapter's options are inlined as
|
|
1678
|
-
* config.
|
|
1679
|
-
*
|
|
1684
|
+
* adapter's options are inlined as literals so the route never imports the
|
|
1685
|
+
* config. `pages` (see `sourcePages`) maps each source file to its page: a
|
|
1686
|
+
* chunk links to the page whose file `mxbai store sync` uploaded it from,
|
|
1687
|
+
* found by the longest trailing run of the path the CLI recorded.
|
|
1680
1688
|
*/
|
|
1681
1689
|
export const mixedbreadSearchEndpointTemplate = (
|
|
1682
1690
|
options: MixedbreadOptions,
|
|
1691
|
+
pages: [string, SourcePage][],
|
|
1683
1692
|
rateLimit?: RateLimitAdapter | null
|
|
1684
1693
|
): string => {
|
|
1694
|
+
const { search_options: tuning, storeId, ...searchOptions } = options;
|
|
1685
1695
|
const limit = rateLimitTemplate(rateLimit, "search");
|
|
1686
1696
|
const imports = [
|
|
1687
1697
|
'import type { APIRoute } from "astro";',
|
|
@@ -1696,9 +1706,26 @@ ${imports.join("\n")}
|
|
|
1696
1706
|
export const prerender = false;
|
|
1697
1707
|
|
|
1698
1708
|
const client = new Mixedbread({ apiKey: getSecret("MIXEDBREAD_API_KEY") ?? "" });
|
|
1699
|
-
const
|
|
1700
|
-
// Every option
|
|
1701
|
-
const
|
|
1709
|
+
const STORE_ID = ${JSON.stringify(storeId)};
|
|
1710
|
+
// Every other option reaches the search call verbatim.
|
|
1711
|
+
const SEARCH_OPTIONS = ${JSON.stringify(searchOptions)};
|
|
1712
|
+
const SEARCH_TUNING = ${JSON.stringify(tuning ?? {})};
|
|
1713
|
+
// Each page's source file, relative to the project root, and the page it renders.
|
|
1714
|
+
const PAGES = new Map<string, { title: string; url: string }>(${JSON.stringify(pages)});
|
|
1715
|
+
|
|
1716
|
+
// The page a synced file renders. \`mxbai store sync\` records the path
|
|
1717
|
+
// relative to where it ran (the project root, or a monorepo root above it), so
|
|
1718
|
+
// the longest trailing run of that path that names a page's file wins.
|
|
1719
|
+
const pageFor = (path: string) => {
|
|
1720
|
+
const segments = path.replaceAll("\\\\", "/").split("/");
|
|
1721
|
+
for (let start = 0; start < segments.length; start += 1) {
|
|
1722
|
+
const page = PAGES.get(segments.slice(start).join("/"));
|
|
1723
|
+
if (page) {
|
|
1724
|
+
return page;
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
return undefined;
|
|
1728
|
+
};
|
|
1702
1729
|
${limit.setup}
|
|
1703
1730
|
export const POST: APIRoute = async (context) => {
|
|
1704
1731
|
const { request } = context;
|
|
@@ -1724,23 +1751,32 @@ ${limit.check} // A search body is one short query: read it under a 16 KB cap,
|
|
|
1724
1751
|
});
|
|
1725
1752
|
}
|
|
1726
1753
|
// \`top_k\` defaults to 8 unless an option sets it; the query and store
|
|
1727
|
-
// always come from the request and \`storeId\`.
|
|
1754
|
+
// always come from the request and \`storeId\`. File metadata is always
|
|
1755
|
+
// returned: it holds the path \`mxbai store sync\` recorded for the file.
|
|
1728
1756
|
const response = await client.stores.search({
|
|
1729
1757
|
top_k: 8,
|
|
1730
1758
|
...SEARCH_OPTIONS,
|
|
1731
1759
|
query,
|
|
1760
|
+
search_options: { ...SEARCH_TUNING, return_metadata: true },
|
|
1732
1761
|
store_identifiers: [STORE_ID],
|
|
1733
1762
|
});
|
|
1734
|
-
const
|
|
1763
|
+
const seen = new Set<string>();
|
|
1764
|
+
const hits = (response.data ?? []).flatMap((chunk) => {
|
|
1765
|
+
const { file_path: path } = (chunk.metadata ?? {}) as { file_path?: unknown };
|
|
1766
|
+
const page = typeof path === "string" ? pageFor(path) : undefined;
|
|
1767
|
+
// A file no page renders has nowhere to link, and a page's later chunks
|
|
1768
|
+
// would repeat it: one hit per page, at its best chunk.
|
|
1769
|
+
if (!page || seen.has(page.url)) {
|
|
1770
|
+
return [];
|
|
1771
|
+
}
|
|
1772
|
+
seen.add(page.url);
|
|
1735
1773
|
const meta = chunk.generated_metadata ?? {};
|
|
1736
1774
|
// Only a text chunk carries \`text\`; image, audio, and video chunks fall
|
|
1737
1775
|
// back to the excerpt the store generated for them.
|
|
1738
1776
|
const text = "text" in chunk ? chunk.text : undefined;
|
|
1739
|
-
return
|
|
1740
|
-
excerpt: text ?? meta.excerpt ?? "",
|
|
1741
|
-
|
|
1742
|
-
url: meta.url ?? "",
|
|
1743
|
-
};
|
|
1777
|
+
return [
|
|
1778
|
+
{ excerpt: text ?? meta.excerpt ?? "", title: page.title, url: page.url },
|
|
1779
|
+
];
|
|
1744
1780
|
});
|
|
1745
1781
|
return new Response(JSON.stringify(hits), {
|
|
1746
1782
|
headers: { "Content-Type": "application/json" },
|
|
@@ -2417,6 +2453,7 @@ import type { CollectionKey } from "astro:content";
|
|
|
2417
2453
|
import RootLayout from "blume/components/layout/RootLayout.astro";
|
|
2418
2454
|
import { withBase, withMountedBase } from "blume/components/islands/base-path.ts";
|
|
2419
2455
|
import { mountBasePath, stripBasePath } from "blume/core/base-path.ts";
|
|
2456
|
+
import { routeSetFor, servesRoute } from "blume/core/locale-links.ts";
|
|
2420
2457
|
import { resolveSlot } from "blume/components/layout/overrides.ts";
|
|
2421
2458
|
${componentImports}
|
|
2422
2459
|
import data from "blume:data";
|
|
@@ -2620,18 +2657,24 @@ const logicalRoute = i18n
|
|
|
2620
2657
|
? stripLocale(stripBasePath(data.config.basePath, route), locale)
|
|
2621
2658
|
: route;
|
|
2622
2659
|
// A page from a one-language source (GitHub Releases) gets no switcher: every
|
|
2623
|
-
// other locale would only repeat the same text.
|
|
2660
|
+
// other locale would only repeat the same text. A locale with no real
|
|
2661
|
+
// translation links the page's fallback copy, which exists only while
|
|
2662
|
+
// fallbacks are on; where nothing is served at that URL (\`fallbackLocale:
|
|
2663
|
+
// null\`), the locale is left out rather than linked to a 404.
|
|
2624
2664
|
const localeSwitch = i18n && !monolingual
|
|
2625
|
-
? i18n.locales.
|
|
2665
|
+
? i18n.locales.flatMap((l) => {
|
|
2626
2666
|
const alt = (alternates ?? []).find((x) => x.locale === l.code);
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2667
|
+
const href = alt ? alt.path : mountLocalized(logicalRoute, l.code);
|
|
2668
|
+
return alt || servesRoute(routeSetFor(data.routes), href)
|
|
2669
|
+
? [{
|
|
2670
|
+
code: l.code,
|
|
2671
|
+
current: l.code === locale,
|
|
2672
|
+
dir: l.dir,
|
|
2673
|
+
href,
|
|
2674
|
+
label: l.label,
|
|
2675
|
+
untranslated: !alt,
|
|
2676
|
+
}]
|
|
2677
|
+
: [];
|
|
2635
2678
|
})
|
|
2636
2679
|
: [];
|
|
2637
2680
|
|
|
@@ -2958,13 +3001,15 @@ const canonical = base ? base + basedRoute : null;
|
|
|
2958
3001
|
const ogPath = data.config.og.enabled ? withMountedBase("/og/changelog.png") : null;
|
|
2959
3002
|
const ogImage = ogPath && base ? base + ogPath : ogPath;
|
|
2960
3003
|
|
|
2961
|
-
// The page chrome (h1, title, description) comes from the
|
|
2962
|
-
// \`changelog\` group
|
|
2963
|
-
//
|
|
3004
|
+
// The page chrome (h1, title, description, empty state) comes from the
|
|
3005
|
+
// translatable \`changelog\` group, which carries the \`changelog\` config's title and
|
|
3006
|
+
// description when it sets them; optional chaining tolerates a
|
|
3007
|
+
// not-yet-regenerated data snapshot from before these keys existed.
|
|
2964
3008
|
const changelogTitle = data.ui.changelog?.title ?? "Changelog";
|
|
2965
3009
|
const changelogDescription =
|
|
2966
3010
|
data.ui.changelog?.description ??
|
|
2967
3011
|
"Product updates, new features, and fixes from every release.";
|
|
3012
|
+
const changelogEmpty = data.ui.changelog?.empty ?? "No changelog entries yet.";
|
|
2968
3013
|
// The layout suffixes "- {site title}" itself, so the page title is just the
|
|
2969
3014
|
// changelog's own name — prefixing the site title too would double it
|
|
2970
3015
|
// ("Acme Changelog - Acme").
|
|
@@ -3017,7 +3062,7 @@ const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
|
|
|
3017
3062
|
<p class="text-lg text-muted-foreground">{changelogDescription}</p>
|
|
3018
3063
|
{
|
|
3019
3064
|
items.length === 0 ? (
|
|
3020
|
-
<p>
|
|
3065
|
+
<p>{changelogEmpty}</p>
|
|
3021
3066
|
) : (
|
|
3022
3067
|
<div class="not-prose mt-10 divide-y divide-border border-border border-y">
|
|
3023
3068
|
{groups.map((group) => (
|
package/src/audit/catalog.ts
CHANGED
|
@@ -161,7 +161,7 @@ export const CHECKS = [
|
|
|
161
161
|
},
|
|
162
162
|
{
|
|
163
163
|
category: "duplicates",
|
|
164
|
-
fix: "Merge the pages, or
|
|
164
|
+
fix: "Merge the pages, or point `seo.canonical` on all but one at the page to index.",
|
|
165
165
|
id: "BLUME_AUDIT_DUPLICATE_CONTENT",
|
|
166
166
|
severity: "warning",
|
|
167
167
|
tier: "static",
|
|
@@ -359,7 +359,7 @@ export const CHECKS = [
|
|
|
359
359
|
},
|
|
360
360
|
{
|
|
361
361
|
category: "redirects",
|
|
362
|
-
fix: "Remove the redirect, or delete the page it shadows —
|
|
362
|
+
fix: "Remove the redirect, or delete the page it shadows — a path can't be both, and which one readers get depends on the host.",
|
|
363
363
|
id: "BLUME_AUDIT_REDIRECT_SOURCE_IS_PAGE",
|
|
364
364
|
severity: "error",
|
|
365
365
|
tier: "static",
|
|
@@ -373,6 +373,14 @@ export const CHECKS = [
|
|
|
373
373
|
tier: "network",
|
|
374
374
|
title: "HTTPS to HTTP redirect",
|
|
375
375
|
},
|
|
376
|
+
{
|
|
377
|
+
category: "redirects",
|
|
378
|
+
fix: "Deploy the redirect file your host reads (see Redirects in the deployment docs), or name the host in `deployment`.",
|
|
379
|
+
id: "BLUME_AUDIT_REDIRECT_NOT_SERVED",
|
|
380
|
+
severity: "error",
|
|
381
|
+
tier: "network",
|
|
382
|
+
title: "Live site doesn't redirect as configured",
|
|
383
|
+
},
|
|
376
384
|
|
|
377
385
|
// Social
|
|
378
386
|
{
|
|
@@ -570,7 +578,7 @@ export const CHECKS = [
|
|
|
570
578
|
// Sitemap
|
|
571
579
|
{
|
|
572
580
|
category: "sitemap",
|
|
573
|
-
fix: "Remove `draft`/`hidden`/`noindex` from the page's frontmatter if it should be indexed.",
|
|
581
|
+
fix: "Remove `draft`/`hidden`/`noindex` from the page's frontmatter if it should be indexed. A `public/sitemap.xml` replaces the generated sitemap, so add the page there or delete that file.",
|
|
574
582
|
id: "BLUME_AUDIT_INDEXABLE_PAGE_NOT_IN_SITEMAP",
|
|
575
583
|
severity: "warning",
|
|
576
584
|
tier: "static",
|
|
@@ -666,6 +674,14 @@ export const CHECKS = [
|
|
|
666
674
|
tier: "static",
|
|
667
675
|
title: "robots.txt disallows a page that is in the sitemap",
|
|
668
676
|
},
|
|
677
|
+
{
|
|
678
|
+
category: "robots",
|
|
679
|
+
fix: "Narrow or remove the rule in that crawler's `User-agent` group if it should read these pages. Blocking an AI crawler on purpose is fine; skip this check.",
|
|
680
|
+
id: "BLUME_AUDIT_ROBOTS_BLOCKS_CRAWLER",
|
|
681
|
+
severity: "warning",
|
|
682
|
+
tier: "static",
|
|
683
|
+
title: "robots.txt blocks one crawler from pages in the sitemap",
|
|
684
|
+
},
|
|
669
685
|
{
|
|
670
686
|
category: "robots",
|
|
671
687
|
fix: "Set `deployment.site` so robots.txt can reference the sitemap.",
|
|
@@ -101,6 +101,12 @@ export const linkChecks: CheckModule = {
|
|
|
101
101
|
return;
|
|
102
102
|
}
|
|
103
103
|
if (resolved.kind === "outside-base") {
|
|
104
|
+
// A full URL on this host outside the base names another app there
|
|
105
|
+
// (the navigation docs say to link one that way), not a page of this
|
|
106
|
+
// build that lost its base.
|
|
107
|
+
if (resolved.absolute) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
104
110
|
if (link.content) {
|
|
105
111
|
found.push(
|
|
106
112
|
finding(
|
|
@@ -215,11 +221,18 @@ export const linkChecks: CheckModule = {
|
|
|
215
221
|
|
|
216
222
|
const homeUrl = normalizeBasePath(context.project.config.basePath) || "/";
|
|
217
223
|
for (const page of orphanPages(context.pages, context.graph, homeUrl)) {
|
|
224
|
+
// A `sidebar.hidden` page is in no other page's navigation either, so
|
|
225
|
+
// it isn't reachable from the sidebar at all.
|
|
226
|
+
const fromNavigation = [
|
|
227
|
+
...(context.graph.chromeIn.get(page.url) ?? []),
|
|
228
|
+
].some((from) => from !== page.url);
|
|
218
229
|
found.push(
|
|
219
230
|
finding(
|
|
220
231
|
"BLUME_AUDIT_ORPHAN_PAGE",
|
|
221
232
|
pageSite(context, page),
|
|
222
|
-
|
|
233
|
+
fromNavigation
|
|
234
|
+
? "No other page's body links here — it is reachable only from the sidebar."
|
|
235
|
+
: "No page links here, from its body or from navigation, so readers and crawlers can't reach it."
|
|
223
236
|
)
|
|
224
237
|
);
|
|
225
238
|
}
|