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
|
@@ -14,22 +14,104 @@
|
|
|
14
14
|
* prerendered Markdown 404 body with the 404 status, instead of the HTML
|
|
15
15
|
* shell, and one that prefers JSON (or asks for a `.json` URL) gets the
|
|
16
16
|
* prerendered problem-details 404.
|
|
17
|
+
*
|
|
18
|
+
* Under `deployment.base` every route here matches the served URL, base
|
|
19
|
+
* included, and names the files where Blume moves them (`static/<base>/`, see
|
|
20
|
+
* {@link rebaseAdapterRoutes}); `base` is the normalized base, `""` for none.
|
|
17
21
|
*/
|
|
18
22
|
|
|
23
|
+
import { mountBasePath } from "../core/base-path.ts";
|
|
24
|
+
import { POWERED_BY_HEADERS } from "../core/powered-by.ts";
|
|
19
25
|
import { SVG_ASSET_HEADERS } from "./headers.ts";
|
|
20
26
|
|
|
21
|
-
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
27
|
+
/*
|
|
28
|
+
* The `accept` header conditions. Dev and the Cloudflare Worker negotiate the
|
|
29
|
+
* way `prefersMarkdown` (`astro/markdown-negotiation.ts`) does: media types in
|
|
30
|
+
* any case, and Markdown only when its q-value is above zero and at least
|
|
31
|
+
* HTML's. A routing condition is a regex over the whole header, with no
|
|
32
|
+
* arithmetic, so the comparison is spelled as two routes per rewrite:
|
|
33
|
+
*
|
|
34
|
+
* - a Markdown entry at full weight (no q-value, or `q=1`) always wins, since
|
|
35
|
+
* nothing can outrank it;
|
|
36
|
+
* - a Markdown entry below full weight wins only when the header has no
|
|
37
|
+
* `text/html` entry above zero (a `missing` condition), since two q-values
|
|
38
|
+
* below 1 can't be compared. That case, like `text/markdown;q=0.9,
|
|
39
|
+
* text/html;q=0.8`, gets HTML on Vercel where dev and Cloudflare send
|
|
40
|
+
* Markdown.
|
|
41
|
+
*
|
|
42
|
+
* Every pattern is anchored at both ends and lets `(?:.*,)?` absorb the
|
|
43
|
+
* earlier list entries, so it holds under full-string and substring matching
|
|
44
|
+
* alike. Case-insensitivity is spelled out letter by letter and the patterns
|
|
45
|
+
* are lookaround-free, so they read the same in RE2, PCRE, and JavaScript.
|
|
46
|
+
* Like dev, only the first `q=` parameter of an entry counts, and a `q=`
|
|
47
|
+
* value is read as a decimal. Browsers never send `text/markdown`, so
|
|
48
|
+
* ordinary page requests are unaffected.
|
|
30
49
|
*/
|
|
31
|
-
|
|
32
|
-
|
|
50
|
+
|
|
51
|
+
/** `word` in any case, one character class per letter: `[Tt][Ee][Xx][Tt]`. */
|
|
52
|
+
const anyCase = (word: string): string =>
|
|
53
|
+
word.replaceAll(/[a-z]/gu, (letter) => `[${letter.toUpperCase()}${letter}]`);
|
|
54
|
+
|
|
55
|
+
/** An entry's parameters before its first `q=`: any that isn't one. */
|
|
56
|
+
const OTHER_PARAMS = String.raw`(?:;\s*(?:[^\sq;,][^;,]*|q(?:[^=;,][^;,]*)?)?)*`;
|
|
57
|
+
|
|
58
|
+
/** Everything after an entry's first `q=` value, up to the next entry. */
|
|
59
|
+
const LATER_PARAMS = "(?:;[^,]*)?";
|
|
60
|
+
|
|
61
|
+
/** A `q=` value of 1 or more: full weight. */
|
|
62
|
+
const FULL_WEIGHT = String.raw`0*[1-9][0-9]*(?:\.[0-9]*)?`;
|
|
63
|
+
|
|
64
|
+
/** A `q=` value above zero and below 1. */
|
|
65
|
+
const PARTIAL_WEIGHT = String.raw`0*\.[0-9]*[1-9][0-9]*`;
|
|
66
|
+
|
|
67
|
+
/** A `q=` value that isn't zero: it has a character besides `0` and `.`. */
|
|
68
|
+
const NONZERO_WEIGHT = String.raw`[\s0.]*[^\s0.;,][^;,]*`;
|
|
69
|
+
|
|
70
|
+
/** A header with an entry of `type` weighed by `weight`, or by no `q=` when it's optional. */
|
|
71
|
+
const acceptEntry = (type: string, weight: string, optional: boolean) =>
|
|
72
|
+
String.raw`^(?:.*,)?\s*${type}\s*${OTHER_PARAMS}(?:;\s*q=\s*${weight}\s*${LATER_PARAMS})${optional ? "?" : ""}(?:,.*)?$`;
|
|
73
|
+
|
|
74
|
+
/** A header with a `type` entry at full weight. */
|
|
75
|
+
const fullWeight = (type: string) => acceptEntry(type, FULL_WEIGHT, true);
|
|
76
|
+
|
|
77
|
+
/** A header with a `type` entry weighed above zero and below 1. */
|
|
78
|
+
const partialWeight = (type: string) =>
|
|
79
|
+
acceptEntry(type, PARTIAL_WEIGHT, false);
|
|
80
|
+
|
|
81
|
+
/** A header with a `text/html` entry weighed above zero. */
|
|
82
|
+
const ACCEPT_HTML = acceptEntry(anyCase("text/html"), NONZERO_WEIGHT, true);
|
|
83
|
+
|
|
84
|
+
const MARKDOWN_TYPE = `${anyCase("text/")}(?:${anyCase("x-")})?${anyCase("markdown")}`;
|
|
85
|
+
|
|
86
|
+
const JSON_TYPE = `${anyCase("application/")}(?:${anyCase("problem")}\\+)?${anyCase("json")}`;
|
|
87
|
+
|
|
88
|
+
/** One header condition on a Build Output route. */
|
|
89
|
+
export interface VercelCondition {
|
|
90
|
+
key?: string;
|
|
91
|
+
type: string;
|
|
92
|
+
value?: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** The conditions of one route: all of `has`, none of `missing`. */
|
|
96
|
+
export interface AcceptConditions {
|
|
97
|
+
has: VercelCondition[];
|
|
98
|
+
missing?: VercelCondition[];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const accept = (value: string): VercelCondition => ({
|
|
102
|
+
key: "accept",
|
|
103
|
+
type: "header",
|
|
104
|
+
value,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
/** The pair of conditions a route answering `type` is written once for each. */
|
|
108
|
+
const preferring = (type: string): AcceptConditions[] => [
|
|
109
|
+
{ has: [accept(fullWeight(type))] },
|
|
110
|
+
{ has: [accept(partialWeight(type))], missing: [accept(ACCEPT_HTML)] },
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
/** When a client prefers `text/markdown` (or `text/x-markdown`) over HTML. */
|
|
114
|
+
export const ACCEPT_MARKDOWN_CONDITIONS = preferring(MARKDOWN_TYPE);
|
|
33
115
|
|
|
34
116
|
/**
|
|
35
117
|
* The JSON counterpart, for the problem-details 404: `application/json` or
|
|
@@ -37,8 +119,7 @@ export const ACCEPT_MARKDOWN_HEADER_VALUE =
|
|
|
37
119
|
* (the catch-all wildcard does not match), so ordinary page requests are
|
|
38
120
|
* unaffected.
|
|
39
121
|
*/
|
|
40
|
-
export const
|
|
41
|
-
"(.*,)?\\s*application/(problem\\+)?json(\\s*[;,].*)?$";
|
|
122
|
+
export const ACCEPT_JSON_CONDITIONS = preferring(JSON_TYPE);
|
|
42
123
|
|
|
43
124
|
/**
|
|
44
125
|
* A Build Output API route — the subset these helpers read and write. Parsed
|
|
@@ -49,23 +130,29 @@ export interface VercelRoute {
|
|
|
49
130
|
continue?: boolean;
|
|
50
131
|
dest?: string;
|
|
51
132
|
handle?: string;
|
|
52
|
-
has?:
|
|
133
|
+
has?: VercelCondition[];
|
|
53
134
|
headers?: Record<string, string>;
|
|
135
|
+
missing?: VercelCondition[];
|
|
54
136
|
src?: string;
|
|
55
137
|
status?: number;
|
|
56
138
|
}
|
|
57
139
|
|
|
140
|
+
/** `route` once per condition pair, so it fires when either holds. */
|
|
141
|
+
const conditioned = (
|
|
142
|
+
route: VercelRoute,
|
|
143
|
+
conditions: readonly AcceptConditions[]
|
|
144
|
+
): VercelRoute[] => conditions.map((condition) => ({ ...route, ...condition }));
|
|
145
|
+
|
|
58
146
|
/** Whether a parsed route field is a real string (the config is raw JSON). */
|
|
59
147
|
const isString = (value: string | undefined): value is string =>
|
|
60
148
|
typeof value === "string";
|
|
61
149
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
];
|
|
150
|
+
/** The Markdown conditions' `has` values, to recognize a route this module wrote. */
|
|
151
|
+
const MARKDOWN_VALUES = new Set(
|
|
152
|
+
ACCEPT_MARKDOWN_CONDITIONS.flatMap(({ has }) =>
|
|
153
|
+
has.map((condition) => condition.value)
|
|
154
|
+
)
|
|
155
|
+
);
|
|
69
156
|
|
|
70
157
|
const VARY_ACCEPT = { vary: "Accept" };
|
|
71
158
|
|
|
@@ -78,44 +165,6 @@ const NOT_FOUND_JSON_DEST = "/404.json";
|
|
|
78
165
|
/** The adapter's own not-found fallback — the anchor the Markdown 404 precedes. */
|
|
79
166
|
const NOT_FOUND_HTML_DEST = "/404.html";
|
|
80
167
|
|
|
81
|
-
/**
|
|
82
|
-
* Miss-phase routes that answer a missing page with the Markdown 404 body: any
|
|
83
|
-
* path when the client prefers Markdown, and any `.md`/`.mdx` URL (a request
|
|
84
|
-
* for a raw-Markdown mirror that has no page wants Markdown back, not the HTML
|
|
85
|
-
* shell). Both keep the 404 status. Spliced immediately before the adapter's
|
|
86
|
-
* `/404.html` fallback, so they run after every server route (the MCP
|
|
87
|
-
* endpoint, server islands, images) has had its turn and never hijack a
|
|
88
|
-
* request one of those would have answered.
|
|
89
|
-
*/
|
|
90
|
-
const NOT_FOUND_MARKDOWN_ROUTES: readonly VercelRoute[] = [
|
|
91
|
-
{
|
|
92
|
-
dest: NOT_FOUND_MARKDOWN_DEST,
|
|
93
|
-
has: ACCEPT_MARKDOWN_CONDITION,
|
|
94
|
-
headers: VARY_ACCEPT,
|
|
95
|
-
src: "^/.*$",
|
|
96
|
-
status: 404,
|
|
97
|
-
},
|
|
98
|
-
{ dest: NOT_FOUND_MARKDOWN_DEST, src: "^/.*\\.mdx?$", status: 404 },
|
|
99
|
-
];
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* The JSON 404's miss-phase routes, the problem-details twin of the Markdown
|
|
103
|
-
* ones: any path when the client prefers JSON, and any `.json` URL no file
|
|
104
|
-
* backs. Spliced at the same anchor, after every server route — so the
|
|
105
|
-
* `/api/` catch-all (which answers its own namespace with a problem document)
|
|
106
|
-
* has already had its turn.
|
|
107
|
-
*/
|
|
108
|
-
const NOT_FOUND_JSON_ROUTES: readonly VercelRoute[] = [
|
|
109
|
-
{
|
|
110
|
-
dest: NOT_FOUND_JSON_DEST,
|
|
111
|
-
has: ACCEPT_JSON_CONDITION,
|
|
112
|
-
headers: VARY_ACCEPT,
|
|
113
|
-
src: "^/.*$",
|
|
114
|
-
status: 404,
|
|
115
|
-
},
|
|
116
|
-
{ dest: NOT_FOUND_JSON_DEST, src: "^/.*\\.json$", status: 404 },
|
|
117
|
-
];
|
|
118
|
-
|
|
119
168
|
/** Which prerendered 404 twins the build emitted, so their routes get wired. */
|
|
120
169
|
export interface NotFoundVariants {
|
|
121
170
|
json?: boolean;
|
|
@@ -140,6 +189,53 @@ const REGEX_SPECIALS = /[$()*+.?[\]^{|}\\]/gu;
|
|
|
140
189
|
const routePattern = (route: string): string =>
|
|
141
190
|
encodeURI(route).replace(REGEX_SPECIALS, "\\$&");
|
|
142
191
|
|
|
192
|
+
/**
|
|
193
|
+
* The `src` matching the homepage: `^/$`, or the base itself under one, served
|
|
194
|
+
* without a trailing slash (the adapter's trailing-slash route strips it).
|
|
195
|
+
*/
|
|
196
|
+
const homeSource = (base: string): string =>
|
|
197
|
+
`^${routePattern(mountBasePath(base, "/"))}$`;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Miss-phase routes that answer a missing page with the Markdown 404 body: any
|
|
201
|
+
* path when the client prefers Markdown, and any `.md`/`.mdx` URL (a request
|
|
202
|
+
* for a raw-Markdown mirror that has no page wants Markdown back, not the HTML
|
|
203
|
+
* shell). Both keep the 404 status. Spliced immediately before the adapter's
|
|
204
|
+
* `/404.html` fallback, so they run after every server route (the MCP
|
|
205
|
+
* endpoint, server islands, images) has had its turn and never hijack a
|
|
206
|
+
* request one of those would have answered.
|
|
207
|
+
*/
|
|
208
|
+
const notFoundMarkdownRoutes = (base: string): VercelRoute[] => {
|
|
209
|
+
const dest = mountBasePath(base, NOT_FOUND_MARKDOWN_DEST);
|
|
210
|
+
const prefix = routePattern(base);
|
|
211
|
+
return [
|
|
212
|
+
...conditioned(
|
|
213
|
+
{ dest, headers: VARY_ACCEPT, src: `^${prefix}/.*$`, status: 404 },
|
|
214
|
+
ACCEPT_MARKDOWN_CONDITIONS
|
|
215
|
+
),
|
|
216
|
+
{ dest, src: `^${prefix}/.*\\.mdx?$`, status: 404 },
|
|
217
|
+
];
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* The JSON 404's miss-phase routes, the problem-details twin of the Markdown
|
|
222
|
+
* ones: any path when the client prefers JSON, and any `.json` URL no file
|
|
223
|
+
* backs. Spliced at the same anchor, after every server route — so the
|
|
224
|
+
* `/api/` catch-all (which answers its own namespace with a problem document)
|
|
225
|
+
* has already had its turn.
|
|
226
|
+
*/
|
|
227
|
+
const notFoundJsonRoutes = (base: string): VercelRoute[] => {
|
|
228
|
+
const dest = mountBasePath(base, NOT_FOUND_JSON_DEST);
|
|
229
|
+
const prefix = routePattern(base);
|
|
230
|
+
return [
|
|
231
|
+
...conditioned(
|
|
232
|
+
{ dest, headers: VARY_ACCEPT, src: `^${prefix}/.*$`, status: 404 },
|
|
233
|
+
ACCEPT_JSON_CONDITIONS
|
|
234
|
+
),
|
|
235
|
+
{ dest, src: `^${prefix}/.*\\.json$`, status: 404 },
|
|
236
|
+
];
|
|
237
|
+
};
|
|
238
|
+
|
|
143
239
|
/** Group patterns so each group's alternation stays under the `src` limit. */
|
|
144
240
|
const chunkPatterns = (patterns: readonly string[]): string[][] => {
|
|
145
241
|
const chunks: string[][] = [];
|
|
@@ -190,41 +286,49 @@ export interface NegotiationRoutes {
|
|
|
190
286
|
* rewrite also stamps `x-markdown-tokens` — the estimated token count of the
|
|
191
287
|
* homepage mirror (Cloudflare's Markdown for Agents convention). Only the home
|
|
192
288
|
* route can carry it: the other rewrites are chunked alternations spanning
|
|
193
|
-
* many pages, and a count is per-page.
|
|
289
|
+
* many pages, and a count is per-page. Under a `base`, each path is matched
|
|
290
|
+
* and rewritten where it's served (`/docs` → `/docs/index.md`).
|
|
194
291
|
*/
|
|
195
292
|
export const buildNegotiationRoutes = (
|
|
196
293
|
routePaths: readonly string[],
|
|
197
|
-
homeTokens?: number
|
|
294
|
+
homeTokens?: number,
|
|
295
|
+
base = ""
|
|
198
296
|
): NegotiationRoutes => {
|
|
199
297
|
const home = routePaths.includes("/");
|
|
200
298
|
const rest = routePaths
|
|
201
299
|
.filter((path) => path !== "/")
|
|
202
|
-
.map((path) => routePattern(path));
|
|
300
|
+
.map((path) => routePattern(mountBasePath(base, path)));
|
|
203
301
|
const chunks = chunkPatterns(rest);
|
|
204
302
|
|
|
205
303
|
const rewriteRoutes: VercelRoute[] = home
|
|
206
|
-
?
|
|
304
|
+
? conditioned(
|
|
207
305
|
{
|
|
208
|
-
dest: "/index.md",
|
|
209
|
-
has: ACCEPT_MARKDOWN_CONDITION,
|
|
306
|
+
dest: mountBasePath(base, "/index.md"),
|
|
210
307
|
headers:
|
|
211
308
|
homeTokens === undefined
|
|
212
309
|
? VARY_ACCEPT
|
|
213
310
|
: { ...VARY_ACCEPT, "x-markdown-tokens": String(homeTokens) },
|
|
214
|
-
src:
|
|
311
|
+
src: homeSource(base),
|
|
215
312
|
},
|
|
216
|
-
|
|
313
|
+
ACCEPT_MARKDOWN_CONDITIONS
|
|
314
|
+
)
|
|
217
315
|
: [];
|
|
218
316
|
for (const chunk of chunks) {
|
|
219
|
-
rewriteRoutes.push(
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
317
|
+
rewriteRoutes.push(
|
|
318
|
+
...conditioned(
|
|
319
|
+
{
|
|
320
|
+
dest: "$1.md",
|
|
321
|
+
headers: VARY_ACCEPT,
|
|
322
|
+
src: `^(${chunk.join("|")})/?$`,
|
|
323
|
+
},
|
|
324
|
+
ACCEPT_MARKDOWN_CONDITIONS
|
|
325
|
+
)
|
|
326
|
+
);
|
|
225
327
|
}
|
|
226
328
|
|
|
227
|
-
const headerChunks = chunkPatterns(
|
|
329
|
+
const headerChunks = chunkPatterns(
|
|
330
|
+
home ? [routePattern(mountBasePath(base, "/")), ...rest] : rest
|
|
331
|
+
);
|
|
228
332
|
const headerRoutes: VercelRoute[] = headerChunks.map((chunk) => ({
|
|
229
333
|
continue: true,
|
|
230
334
|
headers: VARY_ACCEPT,
|
|
@@ -234,9 +338,6 @@ export const buildNegotiationRoutes = (
|
|
|
234
338
|
return { headerRoutes, rewriteRoutes };
|
|
235
339
|
};
|
|
236
340
|
|
|
237
|
-
/** The `src` of the injected homepage `Link` header route. */
|
|
238
|
-
const HOME_SRC = "^/$";
|
|
239
|
-
|
|
240
341
|
const ALLOW_ANY_ORIGIN = "*";
|
|
241
342
|
|
|
242
343
|
/**
|
|
@@ -244,18 +345,18 @@ const ALLOW_ANY_ORIGIN = "*";
|
|
|
244
345
|
* one static discovery document (see `crossOriginDiscoveryPaths`), so a
|
|
245
346
|
* registry reading it from another origin isn't refused by the browser.
|
|
246
347
|
*/
|
|
247
|
-
const corsRoute = (path: string): VercelRoute => ({
|
|
348
|
+
const corsRoute = (path: string, base: string): VercelRoute => ({
|
|
248
349
|
continue: true,
|
|
249
350
|
headers: { "access-control-allow-origin": ALLOW_ANY_ORIGIN },
|
|
250
|
-
src: `^${routePattern(path)}$`,
|
|
351
|
+
src: `^${routePattern(mountBasePath(base, path))}$`,
|
|
251
352
|
});
|
|
252
353
|
|
|
253
354
|
/**
|
|
254
355
|
* The `src` of the route stamping the content-source SVG headers: every `.svg`
|
|
255
|
-
* under `/blume-assets/`.
|
|
256
|
-
* Output tree without the deployment base, like every path matched here.
|
|
356
|
+
* under `/blume-assets/`.
|
|
257
357
|
*/
|
|
258
|
-
const
|
|
358
|
+
const svgAssetSource = (base: string): string =>
|
|
359
|
+
String.raw`^${routePattern(base)}/blume-assets/.+\.svg$`;
|
|
259
360
|
|
|
260
361
|
/**
|
|
261
362
|
* A main-phase `continue` route stamping the sandbox headers
|
|
@@ -263,7 +364,7 @@ const SVG_ASSET_SRC = String.raw`^/blume-assets/.+\.svg$`;
|
|
|
263
364
|
* static layer — where the prerendered asset endpoint's own headers never
|
|
264
365
|
* reach.
|
|
265
366
|
*/
|
|
266
|
-
const svgAssetRoute = (): VercelRoute => ({
|
|
367
|
+
const svgAssetRoute = (base: string): VercelRoute => ({
|
|
267
368
|
continue: true,
|
|
268
369
|
headers: Object.fromEntries(
|
|
269
370
|
Object.entries(SVG_ASSET_HEADERS).map(([name, value]) => [
|
|
@@ -271,9 +372,34 @@ const svgAssetRoute = (): VercelRoute => ({
|
|
|
271
372
|
value,
|
|
272
373
|
])
|
|
273
374
|
),
|
|
274
|
-
src:
|
|
375
|
+
src: svgAssetSource(base),
|
|
275
376
|
});
|
|
276
377
|
|
|
378
|
+
/** The `src` of the `X-Powered-By` route: the base and every path beneath it. */
|
|
379
|
+
const poweredBySource = (base: string): string =>
|
|
380
|
+
`^${routePattern(base)}(?:/.*)?$`;
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* A main-phase `continue` route naming Blume in `X-Powered-By` (see
|
|
384
|
+
* `core/powered-by.ts`) on every response: the prerendered pages and files
|
|
385
|
+
* the static layer serves, which Blume's runtime middleware never sees, the
|
|
386
|
+
* functions, and the redirects.
|
|
387
|
+
*/
|
|
388
|
+
const poweredByRoute = (base: string): VercelRoute => ({
|
|
389
|
+
continue: true,
|
|
390
|
+
headers: Object.fromEntries(
|
|
391
|
+
Object.entries(POWERED_BY_HEADERS).map(([name, value]) => [
|
|
392
|
+
name.toLowerCase(),
|
|
393
|
+
value,
|
|
394
|
+
])
|
|
395
|
+
),
|
|
396
|
+
src: poweredBySource(base),
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
/** Whether a route is the {@link poweredByRoute}. */
|
|
400
|
+
const isPoweredByRoute = (route: VercelRoute, base: string): boolean =>
|
|
401
|
+
route.continue === true && route.src === poweredBySource(base);
|
|
402
|
+
|
|
277
403
|
/** Whether a route is a `corsRoute` — the same three-field shape test as the others. */
|
|
278
404
|
const isCorsRoute = (route: VercelRoute): boolean =>
|
|
279
405
|
route.continue === true &&
|
|
@@ -289,24 +415,27 @@ const isCorsRoute = (route: VercelRoute): boolean =>
|
|
|
289
415
|
* the one re-added); the homepage `Link` route by its three-field
|
|
290
416
|
* continue-with-link shape (the Build Output config is adapter-generated, so
|
|
291
417
|
* no user-authored route competes in this file); the Markdown 404 routes by
|
|
292
|
-
* their `/404.md` destination
|
|
418
|
+
* their `/404.md` destination; the SVG and `X-Powered-By` routes by their
|
|
419
|
+
* `src`.
|
|
293
420
|
*/
|
|
294
|
-
const isNegotiationRoute = (route: VercelRoute): boolean =>
|
|
295
|
-
route.has?.some(
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
(route.dest === NOT_FOUND_JSON_DEST &&
|
|
421
|
+
const isNegotiationRoute = (route: VercelRoute, base: string): boolean =>
|
|
422
|
+
route.has?.some((condition) => MARKDOWN_VALUES.has(condition.value)) ===
|
|
423
|
+
true ||
|
|
424
|
+
(route.dest === mountBasePath(base, NOT_FOUND_MARKDOWN_DEST) &&
|
|
425
|
+
route.status === 404) ||
|
|
426
|
+
(route.dest === mountBasePath(base, NOT_FOUND_JSON_DEST) &&
|
|
427
|
+
route.status === 404) ||
|
|
300
428
|
(route.continue === true &&
|
|
301
429
|
route.headers?.vary === "Accept" &&
|
|
302
430
|
isString(route.src) &&
|
|
303
431
|
Object.keys(route).length === 3) ||
|
|
304
432
|
(route.continue === true &&
|
|
305
433
|
isString(route.headers?.link) &&
|
|
306
|
-
route.src ===
|
|
434
|
+
route.src === homeSource(base) &&
|
|
307
435
|
Object.keys(route).length === 3) ||
|
|
308
436
|
isCorsRoute(route) ||
|
|
309
|
-
(route.continue === true && route.src ===
|
|
437
|
+
(route.continue === true && route.src === svgAssetSource(base)) ||
|
|
438
|
+
isPoweredByRoute(route, base);
|
|
310
439
|
|
|
311
440
|
/**
|
|
312
441
|
* Splice the negotiation routes into a Build Output `config.json`, plus — when
|
|
@@ -325,7 +454,11 @@ const isNegotiationRoute = (route: VercelRoute): boolean =>
|
|
|
325
454
|
* phase right before the adapter's `/404.html` fallback — and nowhere when
|
|
326
455
|
* that fallback is absent, since a `dest` with no file behind it would serve
|
|
327
456
|
* nothing. With `svgAssets`, a main-phase route also sandboxes the SVGs a
|
|
328
|
-
* content source downloaded (see {@link svgAssetRoute}).
|
|
457
|
+
* content source downloaded (see {@link svgAssetRoute}). With `poweredBy`, a
|
|
458
|
+
* route at the head of the config, ahead of the adapter's redirects, names
|
|
459
|
+
* Blume on every response (see {@link poweredByRoute}). Under a `base`, every
|
|
460
|
+
* route matches and names paths beneath it, and the fallback anchor is the
|
|
461
|
+
* one {@link rebaseAdapterRoutes} moved there. Returns the updated JSON
|
|
329
462
|
* text (tab-indented, like the adapter's own output), or `null` when there is
|
|
330
463
|
* nowhere safe to splice: an unparsable config, no `routes` array, or no
|
|
331
464
|
* `handle: "filesystem"` marker to anchor the splice.
|
|
@@ -338,7 +471,9 @@ export const injectNegotiationRoutes = (
|
|
|
338
471
|
homeTokens?: number,
|
|
339
472
|
notFound: NotFoundVariants = {},
|
|
340
473
|
corsPaths: readonly string[] = [],
|
|
341
|
-
svgAssets = false
|
|
474
|
+
svgAssets = false,
|
|
475
|
+
base = "",
|
|
476
|
+
poweredBy = false
|
|
342
477
|
): string | null => {
|
|
343
478
|
const overrideEntries = Object.entries(contentTypeOverrides ?? {});
|
|
344
479
|
let config: {
|
|
@@ -358,7 +493,9 @@ export const injectNegotiationRoutes = (
|
|
|
358
493
|
// user's own override of the same path is simply refreshed.
|
|
359
494
|
config.overrides = { ...config.overrides, [path]: { contentType } };
|
|
360
495
|
}
|
|
361
|
-
const routes = config.routes.filter(
|
|
496
|
+
const routes = config.routes.filter(
|
|
497
|
+
(route) => !isNegotiationRoute(route, base)
|
|
498
|
+
);
|
|
362
499
|
const filesystemIndex = routes.findIndex(
|
|
363
500
|
(route) => route.handle === "filesystem"
|
|
364
501
|
);
|
|
@@ -367,30 +504,35 @@ export const injectNegotiationRoutes = (
|
|
|
367
504
|
}
|
|
368
505
|
const { headerRoutes, rewriteRoutes } = buildNegotiationRoutes(
|
|
369
506
|
routePaths,
|
|
370
|
-
homeTokens
|
|
507
|
+
homeTokens,
|
|
508
|
+
base
|
|
371
509
|
);
|
|
372
510
|
if (homeLinkHeader) {
|
|
373
511
|
headerRoutes.push({
|
|
374
512
|
continue: true,
|
|
375
513
|
headers: { link: homeLinkHeader },
|
|
376
|
-
src:
|
|
514
|
+
src: homeSource(base),
|
|
377
515
|
});
|
|
378
516
|
}
|
|
379
|
-
headerRoutes.push(...corsPaths.map(corsRoute));
|
|
517
|
+
headerRoutes.push(...corsPaths.map((path) => corsRoute(path, base)));
|
|
380
518
|
if (svgAssets) {
|
|
381
|
-
headerRoutes.push(svgAssetRoute());
|
|
519
|
+
headerRoutes.push(svgAssetRoute(base));
|
|
382
520
|
}
|
|
383
521
|
// Headers first: `continue` routes accumulate, so a request the rewrite
|
|
384
522
|
// route then terminates (Markdown negotiation on the homepage) still carries
|
|
385
523
|
// the Link header.
|
|
386
524
|
routes.splice(filesystemIndex, 0, ...headerRoutes, ...rewriteRoutes);
|
|
525
|
+
if (poweredBy) {
|
|
526
|
+
routes.unshift(poweredByRoute(base));
|
|
527
|
+
}
|
|
387
528
|
const notFoundRoutes = [
|
|
388
|
-
...(notFound.markdown ?
|
|
389
|
-
...(notFound.json ?
|
|
529
|
+
...(notFound.markdown ? notFoundMarkdownRoutes(base) : []),
|
|
530
|
+
...(notFound.json ? notFoundJsonRoutes(base) : []),
|
|
390
531
|
];
|
|
391
532
|
if (notFoundRoutes.length > 0) {
|
|
533
|
+
const fallbackDest = mountBasePath(base, NOT_FOUND_HTML_DEST);
|
|
392
534
|
const fallbackIndex = routes.findIndex(
|
|
393
|
-
(route) => route.status === 404 && route.dest ===
|
|
535
|
+
(route) => route.status === 404 && route.dest === fallbackDest
|
|
394
536
|
);
|
|
395
537
|
if (fallbackIndex !== -1) {
|
|
396
538
|
routes.splice(fallbackIndex, 0, ...notFoundRoutes);
|
|
@@ -435,3 +577,83 @@ export const injectRedirectRoutes = (
|
|
|
435
577
|
config.routes = routes;
|
|
436
578
|
return `${JSON.stringify(config, null, "\t")}\n`;
|
|
437
579
|
};
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* The trailing-slash route `trailingSlash: "never"` gives the adapter's config
|
|
583
|
+
* (`^/(.*)/$` → `/$1`): it keeps whatever path it strips the slash from, base
|
|
584
|
+
* and all, so it already serves a based site.
|
|
585
|
+
*/
|
|
586
|
+
const isTrailingSlashRoute = (route: VercelRoute): boolean =>
|
|
587
|
+
route.headers?.Location === "/$1";
|
|
588
|
+
|
|
589
|
+
/** The pattern body of the root route, anchored (`^/$`) or not (`/`). */
|
|
590
|
+
const ROOT_BODY = /^\/\$?$/u;
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Re-anchor an adapter route pattern under the base, whose escaped form is
|
|
594
|
+
* `prefix`: each matches a base-less path from its start, and the root route
|
|
595
|
+
* becomes the base itself, served without a trailing slash.
|
|
596
|
+
*/
|
|
597
|
+
const rebaseSource = (src: string, prefix: string): string => {
|
|
598
|
+
const body = src.startsWith("^") ? src.slice(1) : src;
|
|
599
|
+
return `^${prefix}${ROOT_BODY.test(body) ? "$" : body}`;
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Move the adapter's routes under `deployment.base`, to match the static files
|
|
604
|
+
* Blume moves to `static/<base>/`. `@astrojs/vercel` routes a server build as
|
|
605
|
+
* if it were served from the root: its route patterns (the `_astro/` cache
|
|
606
|
+
* header, every server route) match base-less paths, its 404 fallback names
|
|
607
|
+
* `/404.html`, and it joins the base onto each redirect with no slash on the
|
|
608
|
+
* `from` side and a second time on the `to` side, which Astro hands it
|
|
609
|
+
* already based (`^/docsold$` → `/docs/docs/new`). Each pattern is
|
|
610
|
+
* re-anchored under the base and the fallback names the base's `404.html`;
|
|
611
|
+
* the adapter's redirects make way for `redirectRoutes`, the exact redirects
|
|
612
|
+
* as the host matches them. Returns the updated JSON text, or `null` when
|
|
613
|
+
* there is nowhere safe to splice: an unparsable config, no `routes` array,
|
|
614
|
+
* or no `handle: "filesystem"` marker.
|
|
615
|
+
*/
|
|
616
|
+
export const rebaseAdapterRoutes = (
|
|
617
|
+
configText: string,
|
|
618
|
+
base: string,
|
|
619
|
+
redirectRoutes: readonly VercelRoute[]
|
|
620
|
+
): string | null => {
|
|
621
|
+
let config: { routes?: VercelRoute[] };
|
|
622
|
+
try {
|
|
623
|
+
config = JSON.parse(configText);
|
|
624
|
+
} catch {
|
|
625
|
+
return null;
|
|
626
|
+
}
|
|
627
|
+
if (!Array.isArray(config.routes)) {
|
|
628
|
+
return null;
|
|
629
|
+
}
|
|
630
|
+
const filesystemIndex = config.routes.findIndex(
|
|
631
|
+
(route) => route.handle === "filesystem"
|
|
632
|
+
);
|
|
633
|
+
if (filesystemIndex === -1) {
|
|
634
|
+
return null;
|
|
635
|
+
}
|
|
636
|
+
const prefix = routePattern(base);
|
|
637
|
+
const routes = config.routes.flatMap((route, index): VercelRoute[] => {
|
|
638
|
+
// Ahead of the filesystem handler, the adapter writes its redirects and
|
|
639
|
+
// nothing else with a `Location`.
|
|
640
|
+
if (index < filesystemIndex && isString(route.headers?.Location)) {
|
|
641
|
+
return isTrailingSlashRoute(route) ? [route] : [];
|
|
642
|
+
}
|
|
643
|
+
const rebased = { ...route };
|
|
644
|
+
if (isString(route.src)) {
|
|
645
|
+
rebased.src = rebaseSource(route.src, prefix);
|
|
646
|
+
}
|
|
647
|
+
if (route.status === 404 && route.dest === NOT_FOUND_HTML_DEST) {
|
|
648
|
+
rebased.dest = mountBasePath(base, NOT_FOUND_HTML_DEST);
|
|
649
|
+
}
|
|
650
|
+
return [rebased];
|
|
651
|
+
});
|
|
652
|
+
routes.splice(
|
|
653
|
+
routes.findIndex((route) => route.handle === "filesystem"),
|
|
654
|
+
0,
|
|
655
|
+
...redirectRoutes
|
|
656
|
+
);
|
|
657
|
+
config.routes = routes;
|
|
658
|
+
return `${JSON.stringify(config, null, "\t")}\n`;
|
|
659
|
+
};
|
package/src/eval/agents.ts
CHANGED
|
@@ -188,7 +188,24 @@ const CLAUDE_READER_MAX_TURNS = "25";
|
|
|
188
188
|
const CLAUDE_JUDGE_MAX_TURNS = "1";
|
|
189
189
|
|
|
190
190
|
const claudeArgs = (context: InvocationContext): string[] => {
|
|
191
|
-
|
|
191
|
+
// `--tools ""` turns off every Claude Code built-in, including ones the
|
|
192
|
+
// deny list below doesn't name (`Monitor` runs shell commands, `Skill`
|
|
193
|
+
// loads the user's skills), so a reader has only the docs MCP tools and the
|
|
194
|
+
// judge has no tools at all. The deny list stays as a second layer.
|
|
195
|
+
// `--setting-sources ""` is codex's `--ignore-user-config`: no settings
|
|
196
|
+
// files, so the user's hooks, plugins, and CLAUDE.md can't hand the reader
|
|
197
|
+
// context the docs never gave it. Not `--bare`, which also stops reading a
|
|
198
|
+
// subscription login and fails with "Not logged in".
|
|
199
|
+
const base = [
|
|
200
|
+
"-p",
|
|
201
|
+
"--output-format",
|
|
202
|
+
"json",
|
|
203
|
+
"--strict-mcp-config",
|
|
204
|
+
"--tools",
|
|
205
|
+
"",
|
|
206
|
+
"--setting-sources",
|
|
207
|
+
"",
|
|
208
|
+
];
|
|
192
209
|
if (context.mcp) {
|
|
193
210
|
const allowed = MCP_TOOL_NAMES.map(
|
|
194
211
|
(tool) => `mcp__${MCP_SERVER_NAME}__${tool}`
|
package/src/eval/prompts.ts
CHANGED
|
@@ -40,8 +40,11 @@ Reply with ONLY this JSON object on a single line, no markdown fences:
|
|
|
40
40
|
{"pass": true|false, "score": 0.0-1.0, "missing": ["expected facts absent or contradicted"], "notes": "one sentence"}`;
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
-
/**
|
|
44
|
-
|
|
43
|
+
/**
|
|
44
|
+
* The `--fix` handoff prompt: where the report is, the ground rules, and the
|
|
45
|
+
* `blume eval` command that verifies the fixes (the run's own flags).
|
|
46
|
+
*/
|
|
47
|
+
export const evalFixPrompt = (reportPath: string, command: string): string =>
|
|
45
48
|
`Fix the documentation gaps found by \`blume eval\` in this project.
|
|
46
49
|
|
|
47
50
|
The full report is at ${reportPath}. It is JSON: each entry in \`eval.results\` with status "fail" is one question the documentation could not answer. Each carries the \`question\`, the \`expected\` facts, the judge's \`missing\` facts, and the reader agent's \`answer\` (what the docs currently convey). The matching \`diagnostics\` entry names the source \`file\` of the page that should answer it.
|
|
@@ -51,7 +54,7 @@ Work through every failed question:
|
|
|
51
54
|
2. Edit the documentation so it states the missing facts explicitly. Add prose, not filler; keep the page's voice.
|
|
52
55
|
3. Never delete questions from the evals file or weaken expected facts.
|
|
53
56
|
|
|
54
|
-
When you are done, run \`
|
|
57
|
+
When you are done, run \`${command}\` to verify, and repeat until every question passes.`;
|
|
55
58
|
|
|
56
59
|
/** The `eval init` prompt: draft a starter evals file from the docs. */
|
|
57
60
|
export const initPrompt = (evalsPath: string): string =>
|