blume 2.0.1 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +147 -0
- package/dist/cli/{chunk-qwsrynx5.js → chunk-1d7ve1dm.js} +38 -19
- package/dist/cli/{chunk-qwsrynx5.js.map → chunk-1d7ve1dm.js.map} +4 -4
- package/dist/cli/{chunk-s6jhgk0q.js → chunk-2eytanqx.js} +2 -2
- package/dist/cli/{chunk-kdp5q7ke.js → chunk-2hsdwb9n.js} +18 -19
- package/dist/cli/{chunk-kdp5q7ke.js.map → chunk-2hsdwb9n.js.map} +2 -2
- package/dist/cli/{chunk-hdpx1tax.js → chunk-2z928egk.js} +5 -5
- package/dist/cli/{chunk-jwyddg7y.js → chunk-35d4wj9f.js} +30 -24
- package/dist/cli/chunk-35d4wj9f.js.map +15 -0
- package/dist/cli/{chunk-ah61y8py.js → chunk-364znk6q.js} +2 -2
- package/dist/cli/{chunk-jts8mvcz.js → chunk-3em5wd2y.js} +25 -6
- package/dist/cli/{chunk-jts8mvcz.js.map → chunk-3em5wd2y.js.map} +3 -3
- package/dist/cli/{chunk-s1p84fyh.js → chunk-5f86nr5m.js} +63 -22
- package/dist/cli/chunk-5f86nr5m.js.map +11 -0
- package/dist/cli/{chunk-2hn4b8z7.js → chunk-5m5nmvyq.js} +170 -76
- package/dist/cli/chunk-5m5nmvyq.js.map +13 -0
- package/dist/cli/{chunk-epjnccmv.js → chunk-5xvm6tfj.js} +18 -15
- package/dist/cli/chunk-5xvm6tfj.js.map +10 -0
- package/dist/cli/{chunk-fz5wtpmh.js → chunk-6dsbexzp.js} +18 -15
- package/dist/cli/chunk-6dsbexzp.js.map +10 -0
- package/dist/cli/{chunk-vtk4a6dg.js → chunk-8ktnccpt.js} +1 -1
- package/dist/cli/{chunk-6hsn950k.js → chunk-9t7a85s3.js} +178 -48
- package/dist/cli/chunk-9t7a85s3.js.map +10 -0
- package/dist/cli/{chunk-fxypxtvm.js → chunk-a58773jm.js} +2 -2
- package/dist/cli/{chunk-mb2919y2.js → chunk-acanzt5p.js} +17 -6
- package/dist/cli/chunk-acanzt5p.js.map +10 -0
- package/dist/cli/{chunk-27g6wdth.js → chunk-akbpwfxc.js} +90 -26
- package/dist/cli/chunk-akbpwfxc.js.map +10 -0
- package/dist/cli/{chunk-wm7js3j9.js → chunk-b07cmahc.js} +2 -2
- package/dist/cli/{chunk-wgm7m9qk.js → chunk-c8chx29p.js} +179 -51
- package/dist/cli/chunk-c8chx29p.js.map +36 -0
- package/dist/cli/{chunk-qs4q5p4e.js → chunk-crgn1q09.js} +32 -14
- package/dist/cli/chunk-crgn1q09.js.map +10 -0
- package/dist/cli/chunk-e04dxsz1.js +39 -0
- package/dist/cli/chunk-e04dxsz1.js.map +10 -0
- package/dist/cli/{chunk-yt5n7ppj.js → chunk-ey84smr6.js} +17 -7
- package/dist/cli/chunk-ey84smr6.js.map +10 -0
- package/dist/cli/{chunk-f2z5v128.js → chunk-g4hq16wv.js} +14 -15
- package/dist/cli/{chunk-f2z5v128.js.map → chunk-g4hq16wv.js.map} +2 -2
- package/dist/cli/{chunk-fa25z98p.js → chunk-ga0pf4aj.js} +9 -8
- package/dist/cli/chunk-ga0pf4aj.js.map +11 -0
- package/dist/cli/{chunk-kpf8rrjc.js → chunk-hm3vjy5s.js} +109 -58
- package/dist/cli/chunk-hm3vjy5s.js.map +19 -0
- package/dist/cli/{chunk-fs23ddbb.js → chunk-j85vccga.js} +625 -750
- package/dist/cli/chunk-j85vccga.js.map +36 -0
- package/dist/cli/{chunk-ch6g3ar0.js → chunk-p3v96n38.js} +6 -6
- package/dist/cli/{chunk-ch6g3ar0.js.map → chunk-p3v96n38.js.map} +3 -3
- package/dist/cli/{chunk-q5163e60.js → chunk-p73c0m7w.js} +21 -19
- package/dist/cli/chunk-p73c0m7w.js.map +11 -0
- package/dist/cli/{chunk-dh8cwk36.js → chunk-pehfxfta.js} +24 -9
- package/dist/cli/chunk-pehfxfta.js.map +10 -0
- package/dist/cli/{chunk-qkqwkpte.js → chunk-pv29h0wf.js} +3688 -1313
- package/dist/cli/chunk-pv29h0wf.js.map +190 -0
- package/dist/cli/{chunk-6vm74dry.js → chunk-q58y5e6a.js} +10 -10
- package/dist/cli/{chunk-6vm74dry.js.map → chunk-q58y5e6a.js.map} +3 -3
- package/dist/cli/{chunk-zxcczpyx.js → chunk-r20tn01b.js} +1 -1
- package/dist/cli/{chunk-m3vmjgmq.js → chunk-r9rcc4w7.js} +16 -8
- package/dist/cli/chunk-r9rcc4w7.js.map +10 -0
- package/dist/cli/{chunk-5shv93fd.js → chunk-tkacnehg.js} +2 -2
- package/dist/cli/{chunk-zxh4d9vy.js → chunk-tzmab476.js} +4 -4
- package/dist/cli/{chunk-yw7dm696.js → chunk-vg9r4eb9.js} +24 -11
- package/dist/cli/chunk-vg9r4eb9.js.map +14 -0
- package/dist/cli/{chunk-79jhk4py.js → chunk-wrr3j9w9.js} +260 -132
- package/dist/cli/chunk-wrr3j9w9.js.map +35 -0
- package/dist/cli/{chunk-6crbhc3x.js → chunk-yfyb25rh.js} +54 -27
- package/dist/cli/chunk-yfyb25rh.js.map +14 -0
- package/dist/cli/index.js +162 -35
- package/dist/cli/index.js.map +4 -4
- package/dist/types/ai/agent-surface.d.ts +32 -0
- package/dist/types/ai/api-catalog.d.ts +7 -1
- package/dist/types/ai/ask-context.d.ts +7 -0
- package/dist/types/ai/component-markdown.d.ts +4 -4
- package/dist/types/ai/link-headers.d.ts +8 -1
- package/dist/types/ai/openapi-components.d.ts +5 -2
- package/dist/types/ai/relative-links.d.ts +11 -4
- package/dist/types/ai/skills.d.ts +4 -1
- package/dist/types/ai/static-expression.d.ts +28 -0
- package/dist/types/ai/tar.d.ts +1 -3
- package/dist/types/analytics/databuddy.d.ts +43 -0
- package/dist/types/analytics/index.d.ts +4 -0
- package/dist/types/analytics/one-dollar-stats.d.ts +48 -0
- package/dist/types/analytics/schema.d.ts +28 -0
- package/dist/types/astro/integration.d.ts +3 -2
- package/dist/types/cli/env.d.ts +5 -0
- package/dist/types/cli/init/scaffold.d.ts +19 -3
- package/dist/types/cli/init/starter-spec.d.ts +11 -0
- package/dist/types/core/base-path.d.ts +24 -1
- package/dist/types/core/config-input.d.ts +13 -11
- package/dist/types/core/config.d.ts +2 -2
- package/dist/types/core/directive-diagnostics.d.ts +12 -0
- package/dist/types/core/graph.d.ts +2 -0
- package/dist/types/core/heading-markers.d.ts +5 -7
- package/dist/types/core/i18n-ui.d.ts +31 -0
- package/dist/types/core/i18n.d.ts +9 -1
- package/dist/types/core/last-modified.d.ts +10 -0
- package/dist/types/core/links.d.ts +3 -1
- package/dist/types/core/load-module.d.ts +10 -0
- package/dist/types/core/locale-links.d.ts +12 -2
- package/dist/types/core/meta.d.ts +13 -1
- package/dist/types/core/nav-diagnostics.d.ts +10 -0
- package/dist/types/core/navigation.d.ts +38 -0
- package/dist/types/core/ordering-prefix.d.ts +4 -0
- package/dist/types/core/safe-links.d.ts +3 -1
- package/dist/types/core/schema.d.ts +59 -13
- package/dist/types/core/sources/github-releases.d.ts +5 -0
- package/dist/types/core/sources/lower.d.ts +38 -13
- package/dist/types/core/sources/normalize.d.ts +23 -2
- package/dist/types/core/sources/remote.d.ts +11 -1
- package/dist/types/core/sources/resolve.d.ts +12 -0
- package/dist/types/core/sources/types.d.ts +31 -0
- package/dist/types/core/sources/watch.d.ts +12 -5
- package/dist/types/core/standard-schema.d.ts +5 -0
- package/dist/types/core/types.d.ts +9 -0
- package/dist/types/deploy/adapters/node.d.ts +5 -2
- package/dist/types/deploy/adapters/types.d.ts +7 -0
- package/dist/types/deploy/artifacts.d.ts +6 -4
- package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
- package/dist/types/deploy/headers.d.ts +36 -7
- package/dist/types/deploy/node-headers.d.ts +43 -8
- package/dist/types/deploy/platforms/netlify.d.ts +27 -2
- package/dist/types/deploy/platforms/node.d.ts +6 -5
- package/dist/types/deploy/platforms/types.d.ts +15 -0
- package/dist/types/deploy/platforms/vercel.d.ts +3 -2
- package/dist/types/deploy/redirects.d.ts +31 -15
- package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
- package/dist/types/markdown/directives.d.ts +62 -0
- package/dist/types/markdown/features.d.ts +21 -0
- package/dist/types/markdown/mdast.d.ts +63 -0
- package/dist/types/openapi/asyncapi.d.ts +4 -2
- package/dist/types/openapi/model.d.ts +20 -6
- package/dist/types/search/sync/algolia.d.ts +3 -1
- package/docs/01-quickstart.mdx +3 -2
- package/docs/02-deployment.mdx +20 -9
- package/docs/08-faq.mdx +10 -3
- package/docs/advanced/changelog.mdx +1 -1
- package/docs/advanced/custom-pages.mdx +7 -5
- package/docs/cli/audit.mdx +19 -3
- package/docs/cli/doctor.mdx +2 -2
- package/docs/cli/evals.mdx +4 -4
- package/docs/cli/index.mdx +4 -1
- package/docs/cli/translate.mdx +4 -4
- package/docs/cli/version.mdx +1 -1
- package/docs/configuration/analytics.mdx +42 -2
- package/docs/configuration/assistant.mdx +1 -1
- package/docs/configuration/customization.mdx +6 -3
- package/docs/configuration/index.mdx +5 -3
- package/docs/configuration/search.mdx +2 -2
- package/docs/content/components.mdx +2 -2
- package/docs/content/frontmatter.mdx +5 -1
- package/docs/content/i18n.mdx +3 -1
- package/docs/content/index.mdx +8 -4
- package/docs/content/islands.mdx +1 -1
- package/docs/content/meta.mdx +7 -3
- package/docs/content/navigation.mdx +29 -4
- package/docs/content/sources.mdx +18 -16
- package/docs/content/syntax.mdx +14 -0
- package/docs/content/versioning.mdx +2 -1
- package/docs/discoverability/agent-discovery.mdx +22 -8
- package/docs/discoverability/index.mdx +4 -3
- package/docs/discoverability/llms-txt.mdx +2 -5
- package/docs/discoverability/markdown.mdx +6 -4
- package/docs/discoverability/mcp.mdx +4 -0
- package/docs/discoverability/metadata.mdx +3 -2
- package/docs/discoverability/open-graph.mdx +8 -4
- package/docs/discoverability/rss.mdx +4 -2
- package/docs/references/asyncapi.mdx +2 -2
- package/docs/references/graphql.mdx +2 -2
- package/docs/references/openapi.mdx +6 -4
- package/package.json +1 -1
- package/skills/blume-migrate/SKILL.md +6 -6
- package/skills/blume-migrate/references/docusaurus.md +6 -6
- package/skills/blume-migrate/references/fumadocs.md +1 -1
- package/skills/blume-migrate/references/mintlify.md +3 -3
- package/skills/blume-migrate/references/monorepo.md +1 -1
- package/skills/blume-migrate/references/nextra.md +2 -2
- package/skills/blume-migrate/references/starlight.md +5 -5
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +9 -4
- package/skills/blume-update-docs/references/audit-checklist.md +1 -1
- package/src/ai/agent-readability.ts +2 -2
- package/src/ai/agent-surface.ts +56 -0
- package/src/ai/ai-catalog.ts +6 -2
- package/src/ai/api/handlers.ts +2 -2
- package/src/ai/api/spec.ts +17 -4
- package/src/ai/api-catalog.ts +13 -3
- package/src/ai/ask-context.ts +14 -2
- package/src/ai/ask-data.ts +26 -12
- package/src/ai/changelog-markdown.ts +2 -2
- package/src/ai/component-markdown.ts +43 -37
- package/src/ai/link-headers.ts +22 -8
- package/src/ai/llms.ts +22 -10
- package/src/ai/markdown.ts +23 -11
- package/src/ai/mcp/discovery.ts +2 -2
- package/src/ai/mcp/query.ts +2 -2
- package/src/ai/mcp/server.ts +80 -9
- package/src/ai/openapi-components.ts +22 -6
- package/src/ai/relative-links.ts +99 -15
- package/src/ai/serializers.ts +2 -1
- package/src/ai/skills.ts +14 -1
- package/src/ai/static-expression.ts +416 -0
- package/src/ai/tar.ts +139 -12
- package/src/ai/visibility.ts +45 -14
- package/src/analytics/databuddy.ts +67 -0
- package/src/analytics/head.ts +8 -0
- package/src/analytics/index.ts +7 -0
- package/src/analytics/one-dollar-stats.ts +86 -0
- package/src/analytics/posthog.ts +21 -3
- package/src/analytics/schema.ts +4 -0
- package/src/astro/generate.ts +72 -28
- package/src/astro/include-hmr.ts +31 -12
- package/src/astro/include-refresh.ts +19 -8
- package/src/astro/integration.ts +108 -14
- package/src/astro/runtime-modules.ts +28 -13
- package/src/astro/templates.ts +139 -57
- package/src/audit/catalog.ts +2 -2
- package/src/audit/checks/assets.ts +25 -5
- package/src/audit/checks/content.ts +20 -2
- package/src/audit/checks/i18n.ts +46 -8
- package/src/audit/checks/indexability.ts +39 -19
- package/src/audit/checks/links.ts +22 -1
- package/src/audit/checks/llms.ts +6 -3
- package/src/audit/checks/network.ts +3 -1
- package/src/audit/checks/og-image.ts +10 -0
- package/src/audit/checks/robots.ts +6 -1
- package/src/audit/checks/sitemap.ts +56 -31
- package/src/audit/checks/social.ts +21 -2
- package/src/audit/crawl.ts +88 -15
- package/src/audit/graph.ts +3 -1
- package/src/audit/report.ts +54 -17
- package/src/audit/run.ts +13 -9
- package/src/audit/snapshot.ts +5 -0
- package/src/audit/types.ts +18 -0
- package/src/audit/url.ts +37 -6
- package/src/cli/args.ts +32 -0
- package/src/cli/build-failure.ts +50 -0
- package/src/cli/commands/audit.ts +11 -1
- package/src/cli/commands/build.ts +19 -5
- package/src/cli/commands/check.ts +2 -0
- package/src/cli/commands/doctor.ts +3 -1
- package/src/cli/commands/eval.ts +20 -18
- package/src/cli/commands/init.ts +15 -5
- package/src/cli/commands/preview.ts +15 -0
- package/src/cli/commands/sync.ts +2 -0
- package/src/cli/commands/translate.ts +18 -18
- package/src/cli/commands/upgrade.ts +11 -0
- package/src/cli/commands/validate.ts +3 -1
- package/src/cli/dev-lock.ts +157 -37
- package/src/cli/eject-scripts.ts +32 -7
- package/src/cli/env.ts +12 -1
- package/src/cli/init/scaffold.ts +117 -11
- package/src/cli/init/starter-spec.ts +235 -0
- package/src/cli/report-format.ts +11 -6
- package/src/components/colors.ts +19 -0
- package/src/components/content/AccordionItem.astro +26 -22
- package/src/components/content/Badge.astro +7 -12
- package/src/components/content/Card.astro +2 -2
- package/src/components/content/Component.astro +11 -5
- package/src/components/content/Expandable.astro +5 -1
- package/src/components/content/Frame.astro +2 -7
- package/src/components/content/GithubInfo.astro +12 -2
- package/src/components/content/Prompt.astro +2 -7
- package/src/components/content/Tab.astro +0 -1
- package/src/components/content/Tabs.astro +58 -5
- package/src/components/content/Tile.astro +1 -1
- package/src/components/content/Tooltip.astro +69 -7
- package/src/components/content/Tree.astro +7 -2
- package/src/components/content/TypeTable.astro +10 -5
- package/src/components/content/Update.astro +8 -2
- package/src/components/content/auto-type-table.ts +4 -1
- package/src/components/content/badge-color.ts +20 -0
- package/src/components/content/base-href.ts +14 -22
- package/src/components/content/inline-markdown.ts +27 -7
- package/src/components/copy-feedback.ts +35 -8
- package/src/components/islands/assistant.tsx +37 -6
- package/src/components/islands/base-path.ts +47 -10
- package/src/components/islands/hooks.ts +42 -21
- package/src/components/islands/webmcp.ts +16 -10
- package/src/components/layout/Banner.astro +23 -4
- package/src/components/layout/Breadcrumbs.astro +5 -2
- package/src/components/layout/DiscoveryLinks.astro +9 -5
- package/src/components/layout/Header.astro +23 -10
- package/src/components/layout/LanguageSwitcher.astro +2 -2
- package/src/components/layout/Logo.astro +5 -0
- package/src/components/layout/NavSelector.astro +8 -3
- package/src/components/layout/NavTabMenu.astro +133 -0
- package/src/components/layout/NavTree.astro +31 -12
- package/src/components/layout/NavTreeCache.astro +5 -2
- package/src/components/layout/NavTreeScript.astro +45 -5
- package/src/components/layout/PageActions.astro +4 -3
- package/src/components/layout/PageLayout.astro +28 -13
- package/src/components/layout/Pagination.astro +3 -3
- package/src/components/layout/ReferenceLayout.astro +4 -1
- package/src/components/layout/RootLayout.astro +25 -12
- package/src/components/layout/Search.astro +38 -14
- package/src/components/layout/VersionBanner.astro +2 -2
- package/src/components/layout/analytics-client.ts +14 -0
- package/src/components/layout/toc-active.ts +41 -0
- package/src/components/layout/toc-element.ts +8 -14
- package/src/components/openapi/ApiTagOperations.astro +2 -2
- package/src/components/openapi/AsyncApiOperation.astro +7 -4
- package/src/components/openapi/GraphqlChip.astro +2 -2
- package/src/components/openapi/GraphqlType.astro +11 -3
- package/src/components/openapi/MessageComposer.astro +1 -1
- package/src/components/openapi/Operation.astro +23 -5
- package/src/components/openapi/PanelTabs.astro +4 -1
- package/src/components/openapi/Playground.astro +8 -2
- package/src/components/openapi/RequestPanel.astro +13 -6
- package/src/components/openapi/SchemaProperty.astro +11 -33
- package/src/components/openapi/SchemaTable.astro +34 -52
- package/src/components/openapi/async.ts +38 -6
- package/src/components/openapi/helpers.ts +100 -12
- package/src/components/openapi/message-composer.ts +14 -1
- package/src/components/openapi/message-model.ts +12 -2
- package/src/components/openapi/message.ts +21 -3
- package/src/components/openapi/operation-model.ts +119 -17
- package/src/components/openapi/panel.ts +31 -5
- package/src/components/openapi/param-style.ts +181 -0
- package/src/components/openapi/playground-client.ts +110 -23
- package/src/components/openapi/playground-schema.ts +25 -6
- package/src/components/openapi/request.ts +192 -26
- package/src/components/openapi/schema-tree.ts +209 -0
- package/src/components/openapi/snippets.ts +113 -19
- package/src/components/openapi/validate-json.ts +1 -1
- package/src/components/openapi/ws-client.ts +18 -2
- package/src/core/base-path.ts +69 -8
- package/src/core/config-input.ts +13 -11
- package/src/core/config.ts +18 -4
- package/src/core/diagnostics.ts +219 -30
- package/src/core/directive-diagnostics.ts +99 -0
- package/src/core/frontmatter.ts +21 -18
- package/src/core/graph.ts +120 -11
- package/src/core/heading-markers.ts +5 -18
- package/src/core/i18n-ui.ts +39 -2
- package/src/core/i18n.ts +44 -5
- package/src/core/last-modified.ts +25 -3
- package/src/core/links.ts +7 -4
- package/src/core/load-module.ts +20 -0
- package/src/core/locale-links.ts +22 -18
- package/src/core/manifest.ts +3 -2
- package/src/core/meta.ts +89 -12
- package/src/core/nav-diagnostics.ts +56 -1
- package/src/core/navigation.ts +272 -83
- package/src/core/ordering-prefix.ts +27 -0
- package/src/core/project-graph.ts +50 -6
- package/src/core/safe-href.ts +53 -1
- package/src/core/safe-links.ts +11 -2
- package/src/core/schema.ts +94 -18
- package/src/core/sources/assets.ts +83 -41
- package/src/core/sources/contentful-rich-text.ts +28 -20
- package/src/core/sources/contentful.ts +25 -12
- package/src/core/sources/filesystem.ts +25 -3
- package/src/core/sources/github-releases.ts +131 -11
- package/src/core/sources/lexical.ts +30 -20
- package/src/core/sources/lower.ts +276 -56
- package/src/core/sources/mdx-remote.ts +52 -16
- package/src/core/sources/normalize.ts +456 -119
- package/src/core/sources/notion.ts +77 -34
- package/src/core/sources/obsidian.ts +40 -9
- package/src/core/sources/payload.ts +1 -0
- package/src/core/sources/portable-text.ts +85 -49
- package/src/core/sources/remote.ts +18 -2
- package/src/core/sources/resolve.ts +52 -33
- package/src/core/sources/sanity.ts +1 -0
- package/src/core/sources/strapi-blocks.ts +28 -19
- package/src/core/sources/strapi.ts +1 -0
- package/src/core/sources/types.ts +31 -0
- package/src/core/sources/watch.ts +20 -7
- package/src/core/standard-schema.ts +10 -6
- package/src/core/types.ts +9 -0
- package/src/core/ui-packs/ar.ts +13 -0
- package/src/core/ui-packs/bg.ts +13 -0
- package/src/core/ui-packs/bn.ts +13 -0
- package/src/core/ui-packs/ca.ts +13 -0
- package/src/core/ui-packs/cs.ts +13 -0
- package/src/core/ui-packs/da.ts +13 -0
- package/src/core/ui-packs/de.ts +13 -0
- package/src/core/ui-packs/el.ts +13 -0
- package/src/core/ui-packs/es.ts +13 -0
- package/src/core/ui-packs/fa.ts +13 -0
- package/src/core/ui-packs/fi.ts +13 -0
- package/src/core/ui-packs/fr.ts +13 -0
- package/src/core/ui-packs/he.ts +13 -0
- package/src/core/ui-packs/hi.ts +13 -0
- package/src/core/ui-packs/hr.ts +13 -0
- package/src/core/ui-packs/hu.ts +13 -0
- package/src/core/ui-packs/id.ts +13 -0
- package/src/core/ui-packs/it.ts +13 -0
- package/src/core/ui-packs/ja.ts +13 -0
- package/src/core/ui-packs/ko.ts +13 -0
- package/src/core/ui-packs/nl.ts +13 -0
- package/src/core/ui-packs/no.ts +13 -0
- package/src/core/ui-packs/pl.ts +13 -0
- package/src/core/ui-packs/pt-br.ts +13 -0
- package/src/core/ui-packs/pt.ts +13 -0
- package/src/core/ui-packs/ro.ts +13 -0
- package/src/core/ui-packs/ru.ts +13 -0
- package/src/core/ui-packs/sk.ts +13 -0
- package/src/core/ui-packs/sr.ts +13 -0
- package/src/core/ui-packs/sv.ts +13 -0
- package/src/core/ui-packs/th.ts +13 -0
- package/src/core/ui-packs/tr.ts +13 -0
- package/src/core/ui-packs/uk.ts +13 -0
- package/src/core/ui-packs/vi.ts +13 -0
- package/src/core/ui-packs/zh-tw.ts +13 -0
- package/src/core/ui-packs/zh.ts +13 -0
- package/src/core/version-cut.ts +69 -17
- package/src/core/versions.ts +4 -1
- package/src/deploy/adapters/node.ts +5 -2
- package/src/deploy/adapters/registry.ts +2 -1
- package/src/deploy/adapters/types.ts +13 -1
- package/src/deploy/artifacts.ts +52 -11
- package/src/deploy/cloudflare-negotiation.ts +27 -16
- package/src/deploy/headers.ts +75 -55
- package/src/deploy/node-headers.ts +148 -27
- package/src/deploy/platforms/cloudflare.ts +13 -3
- package/src/deploy/platforms/netlify.ts +83 -5
- package/src/deploy/platforms/node.ts +8 -5
- package/src/deploy/platforms/static.ts +2 -0
- package/src/deploy/platforms/types.ts +15 -0
- package/src/deploy/platforms/vercel.ts +10 -3
- package/src/deploy/redirects.ts +68 -21
- package/src/deploy/robots.ts +2 -2
- package/src/deploy/rss.ts +4 -3
- package/src/deploy/sitemap.ts +7 -5
- package/src/deploy/vercel-negotiation.ts +35 -3
- package/src/eval/agents.ts +10 -2
- package/src/eval/run.ts +25 -0
- package/src/markdown/base-links.ts +74 -29
- package/src/markdown/directives.ts +242 -36
- package/src/markdown/features.ts +17 -0
- package/src/markdown/index.ts +13 -9
- package/src/markdown/mdast.ts +5 -2
- package/src/markdown/relative-links.ts +15 -27
- package/src/markdown/route-snapshot.ts +37 -0
- package/src/og/card.ts +149 -9
- package/src/og/derive.ts +156 -5
- package/src/og/index.ts +1 -0
- package/src/openapi/asyncapi.ts +4 -2
- package/src/openapi/graphql-build.ts +28 -2
- package/src/openapi/model.ts +99 -27
- package/src/openapi/proxy.ts +63 -10
- package/src/openapi/render-mdx.ts +51 -3
- package/src/registry/eject.ts +297 -43
- package/src/search/adapters/version-scope.ts +30 -0
- package/src/search/documents.ts +89 -27
- package/src/search/popular.ts +2 -1
- package/src/search/sync/algolia.ts +36 -2
- package/src/seo/jsonld.ts +7 -3
- package/src/sources/registry.ts +5 -0
- package/src/theme/entry.ts +11 -4
- package/src/translate/agents.ts +6 -1
- package/src/translate/ledger.ts +26 -3
- package/src/translate/meta.ts +76 -24
- package/src/translate/run.ts +14 -8
- package/src/translate/validate.ts +14 -2
- package/src/translate/work-list.ts +91 -19
- package/src/upgrade/upgrade.ts +36 -4
- package/dist/cli/chunk-27g6wdth.js.map +0 -10
- package/dist/cli/chunk-2hn4b8z7.js.map +0 -12
- package/dist/cli/chunk-6crbhc3x.js.map +0 -14
- package/dist/cli/chunk-6hsn950k.js.map +0 -10
- package/dist/cli/chunk-79jhk4py.js.map +0 -35
- package/dist/cli/chunk-82bbrxdn.js +0 -51
- package/dist/cli/chunk-82bbrxdn.js.map +0 -10
- package/dist/cli/chunk-abh8yjkn.js +0 -31
- package/dist/cli/chunk-abh8yjkn.js.map +0 -10
- package/dist/cli/chunk-ce574jw2.js +0 -23
- package/dist/cli/chunk-ce574jw2.js.map +0 -10
- package/dist/cli/chunk-dh8cwk36.js.map +0 -10
- package/dist/cli/chunk-epjnccmv.js.map +0 -10
- package/dist/cli/chunk-fa25z98p.js.map +0 -11
- package/dist/cli/chunk-fs23ddbb.js.map +0 -35
- package/dist/cli/chunk-fz5wtpmh.js.map +0 -10
- package/dist/cli/chunk-jwyddg7y.js.map +0 -15
- package/dist/cli/chunk-kpf8rrjc.js.map +0 -19
- package/dist/cli/chunk-m3vmjgmq.js.map +0 -10
- package/dist/cli/chunk-mb2919y2.js.map +0 -10
- package/dist/cli/chunk-q5163e60.js.map +0 -11
- package/dist/cli/chunk-qkqwkpte.js.map +0 -182
- package/dist/cli/chunk-qs4q5p4e.js.map +0 -10
- package/dist/cli/chunk-s1p84fyh.js.map +0 -10
- package/dist/cli/chunk-wgm7m9qk.js.map +0 -36
- package/dist/cli/chunk-yt5n7ppj.js.map +0 -10
- package/dist/cli/chunk-yw7dm696.js.map +0 -14
- /package/dist/cli/{chunk-s6jhgk0q.js.map → chunk-2eytanqx.js.map} +0 -0
- /package/dist/cli/{chunk-hdpx1tax.js.map → chunk-2z928egk.js.map} +0 -0
- /package/dist/cli/{chunk-ah61y8py.js.map → chunk-364znk6q.js.map} +0 -0
- /package/dist/cli/{chunk-vtk4a6dg.js.map → chunk-8ktnccpt.js.map} +0 -0
- /package/dist/cli/{chunk-fxypxtvm.js.map → chunk-a58773jm.js.map} +0 -0
- /package/dist/cli/{chunk-wm7js3j9.js.map → chunk-b07cmahc.js.map} +0 -0
- /package/dist/cli/{chunk-zxcczpyx.js.map → chunk-r20tn01b.js.map} +0 -0
- /package/dist/cli/{chunk-5shv93fd.js.map → chunk-tkacnehg.js.map} +0 -0
- /package/dist/cli/{chunk-zxh4d9vy.js.map → chunk-tzmab476.js.map} +0 -0
|
@@ -1,14 +1,81 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
|
|
4
|
+
import { join } from "pathe";
|
|
5
|
+
|
|
6
|
+
import { buildHomeLinkHeader } from "../../ai/link-headers.ts";
|
|
7
|
+
import { markdownRoutePaths } from "../../ai/markdown.ts";
|
|
8
|
+
import type { BlumeProject } from "../../core/project-graph.ts";
|
|
1
9
|
import { NETLIFY_ADAPTER_PACKAGE } from "../adapters/netlify.ts";
|
|
10
|
+
import { buildNetlifyConfigHeaders } from "../headers.ts";
|
|
11
|
+
import type { NetlifyConfigHeaders } from "../headers.ts";
|
|
2
12
|
import { buildNetlifyRedirects } from "../redirects.ts";
|
|
3
13
|
import { distDir, toSiteUrl } from "./paths.ts";
|
|
4
|
-
import type { DeployPlatform, RedirectFile } from "./types.ts";
|
|
14
|
+
import type { BuildLog, DeployPlatform, RedirectFile } from "./types.ts";
|
|
5
15
|
|
|
6
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* `_redirects`, the format Netlify and Cloudflare Pages/Workers read, in the
|
|
18
|
+
* form both accept: a static build for Cloudflare, or for no named host.
|
|
19
|
+
*/
|
|
7
20
|
export const REDIRECTS_FILE: RedirectFile = {
|
|
8
21
|
build: buildNetlifyRedirects,
|
|
9
22
|
name: "_redirects",
|
|
10
23
|
};
|
|
11
24
|
|
|
25
|
+
/**
|
|
26
|
+
* `_redirects` for a `netlify()` static build, every rule forced (`301!`).
|
|
27
|
+
* Astro writes a redirect page at each `from` (`dist/old/index.html`), and
|
|
28
|
+
* Netlify serves a file that exists ahead of an unforced rule, so without the
|
|
29
|
+
* flag that page would answer with a 200 instead of the redirect.
|
|
30
|
+
*/
|
|
31
|
+
export const NETLIFY_REDIRECTS_FILE: RedirectFile = {
|
|
32
|
+
build: (redirects) => buildNetlifyRedirects(redirects, true),
|
|
33
|
+
name: "_redirects",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** The Frameworks API config, once the build surfaced it to the project root. */
|
|
37
|
+
export const NETLIFY_CONFIG_FILE = join(".netlify", "v1", "config.json");
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Carry the static header rules into a server build. A static `netlify()`
|
|
41
|
+
* build gets them from `_headers` (see `deploy/artifacts.ts`), but a server
|
|
42
|
+
* build's header rules go in the Frameworks API config the adapter writes
|
|
43
|
+
* (`.netlify/v1/config.json`, beside its own `Cache-Control` rule for
|
|
44
|
+
* `/_astro/*`), which follows the same rules as `netlify.toml`'s
|
|
45
|
+
* `[[headers]]`: the charset on the raw Markdown and text files, the homepage
|
|
46
|
+
* `Link` header, the discovery files' media types and CORS header, and the
|
|
47
|
+
* sandbox on downloaded SVGs, which Netlify serves from the publish directory
|
|
48
|
+
* as static files, so the prerendered endpoint's own headers never ship.
|
|
49
|
+
*/
|
|
50
|
+
export const emitNetlifyHeaders = async (
|
|
51
|
+
project: BlumeProject,
|
|
52
|
+
log: BuildLog
|
|
53
|
+
): Promise<void> => {
|
|
54
|
+
const { config, context } = project;
|
|
55
|
+
const configPath = join(context.root, NETLIFY_CONFIG_FILE);
|
|
56
|
+
if (!existsSync(configPath)) {
|
|
57
|
+
log.warn(
|
|
58
|
+
`Could not find Netlify's deploy config at ${configPath}, so the site is served without its header rules (the homepage Link header, the discovery files' media types and CORS header, and the sandbox on downloaded SVGs).`
|
|
59
|
+
);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// Only `headers` is read; every other key rides through as parsed.
|
|
63
|
+
const frameworks: { headers?: NetlifyConfigHeaders[] } = JSON.parse(
|
|
64
|
+
await readFile(configPath, "utf-8")
|
|
65
|
+
);
|
|
66
|
+
const ours = buildNetlifyConfigHeaders(
|
|
67
|
+
config,
|
|
68
|
+
buildHomeLinkHeader(config, markdownRoutePaths(project))
|
|
69
|
+
);
|
|
70
|
+
// A second pass over the same build replaces its own entries.
|
|
71
|
+
const paths = new Set(ours.map((entry) => entry.for));
|
|
72
|
+
frameworks.headers = [
|
|
73
|
+
...(frameworks.headers ?? []).filter((entry) => !paths.has(entry.for)),
|
|
74
|
+
...ours,
|
|
75
|
+
];
|
|
76
|
+
await writeFile(configPath, JSON.stringify(frameworks), "utf-8");
|
|
77
|
+
};
|
|
78
|
+
|
|
12
79
|
/**
|
|
13
80
|
* Netlify. A server build runs on Netlify Functions from the Frameworks API
|
|
14
81
|
* tree the adapter writes to `.netlify/v1`, relative to the Astro root —
|
|
@@ -34,7 +101,14 @@ export const netlifyPlatform: DeployPlatform = {
|
|
|
34
101
|
toSiteUrl(env.DEPLOY_PRIME_URL) ??
|
|
35
102
|
toSiteUrl(env.DEPLOY_URL),
|
|
36
103
|
},
|
|
37
|
-
finalizeBuild:
|
|
104
|
+
finalizeBuild: async ({ isolated, log, project }) => {
|
|
105
|
+
// The Frameworks API config is a deploy artifact; an isolated verify
|
|
106
|
+
// never surfaces it.
|
|
107
|
+
if (!isolated) {
|
|
108
|
+
await emitNetlifyHeaders(project, log);
|
|
109
|
+
}
|
|
110
|
+
return true;
|
|
111
|
+
},
|
|
38
112
|
hiddenRuntime: {
|
|
39
113
|
ignoreDir: ".netlify/",
|
|
40
114
|
showProjectRoot: false,
|
|
@@ -42,11 +116,15 @@ export const netlifyPlatform: DeployPlatform = {
|
|
|
42
116
|
},
|
|
43
117
|
kind: "netlify",
|
|
44
118
|
negotiatesMarkdown: false,
|
|
119
|
+
// @astrojs/netlify declares no preview entrypoint.
|
|
120
|
+
previewDeploy: "netlify deploy",
|
|
45
121
|
// Netlify reads `_headers` from the publish directory of a static deploy.
|
|
46
122
|
// A server build's static assets ride the Frameworks API tree instead,
|
|
47
|
-
// where the file has never been applied
|
|
123
|
+
// where the file has never been applied, so `finalizeBuild` writes the
|
|
124
|
+
// same rules into that tree's config.
|
|
48
125
|
readsHeaderFiles: { server: false, static: true },
|
|
49
|
-
redirectFiles: [
|
|
126
|
+
redirectFiles: [NETLIFY_REDIRECTS_FILE],
|
|
127
|
+
serverClientUnderBase: false,
|
|
50
128
|
serverOutputDir: distDir,
|
|
51
129
|
serverStaticDir: distDir,
|
|
52
130
|
};
|
|
@@ -6,11 +6,12 @@ import type { DeployPlatform } from "./types.ts";
|
|
|
6
6
|
/**
|
|
7
7
|
* A self-hosted Node server. The standalone server in `dist/server` serves
|
|
8
8
|
* `dist/client` through its own static handler, which has no `_headers` or
|
|
9
|
-
* `_redirects` support
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* `_redirects` support, so a file written there would be inert. A wrapper
|
|
10
|
+
* around the server entry (`deploy/node-headers.ts`) sets the headers the
|
|
11
|
+
* discovery files and downloaded SVGs need instead, and answers the
|
|
12
|
+
* configured redirects with their exact status, which Astro's own handler
|
|
13
|
+
* would default to 301 or 308. No platform env to detect: the site URL has to
|
|
14
|
+
* be configured.
|
|
14
15
|
*/
|
|
15
16
|
export const nodePlatform: DeployPlatform = {
|
|
16
17
|
astro: {
|
|
@@ -33,8 +34,10 @@ export const nodePlatform: DeployPlatform = {
|
|
|
33
34
|
},
|
|
34
35
|
kind: "node",
|
|
35
36
|
negotiatesMarkdown: false,
|
|
37
|
+
previewDeploy: null,
|
|
36
38
|
readsHeaderFiles: { server: false, static: false },
|
|
37
39
|
redirectFiles: [],
|
|
40
|
+
serverClientUnderBase: false,
|
|
38
41
|
serverOutputDir: distDir,
|
|
39
42
|
serverStaticDir: clientDir,
|
|
40
43
|
};
|
|
@@ -22,8 +22,10 @@ export const staticPlatform: DeployPlatform = {
|
|
|
22
22
|
},
|
|
23
23
|
kind: "static",
|
|
24
24
|
negotiatesMarkdown: false,
|
|
25
|
+
previewDeploy: null,
|
|
25
26
|
readsHeaderFiles: { server: false, static: true },
|
|
26
27
|
redirectFiles: [REDIRECTS_FILE, VERCEL_JSON_FILE],
|
|
28
|
+
serverClientUnderBase: false,
|
|
27
29
|
serverOutputDir: distDir,
|
|
28
30
|
serverStaticDir: distDir,
|
|
29
31
|
};
|
|
@@ -98,6 +98,13 @@ export interface DeployPlatform {
|
|
|
98
98
|
kind: DeployAdapterKind;
|
|
99
99
|
/** Whether a server build honors `Accept: text/markdown` at the content URLs. */
|
|
100
100
|
negotiatesMarkdown: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* The host CLI command that deploys a preview, for a platform whose
|
|
103
|
+
* `@astrojs/*` adapter has no local preview server: Astro's `preview`
|
|
104
|
+
* throws for its server build, so `blume preview` points here instead. Null
|
|
105
|
+
* when the adapter can preview a server build locally, or there is none.
|
|
106
|
+
*/
|
|
107
|
+
previewDeploy: string | null;
|
|
101
108
|
/**
|
|
102
109
|
* Whether the platform applies a `_headers` file to the static assets it
|
|
103
110
|
* serves, per output mode.
|
|
@@ -105,6 +112,14 @@ export interface DeployPlatform {
|
|
|
105
112
|
readsHeaderFiles: Record<DeployOutput, boolean>;
|
|
106
113
|
/** The redirect files a static build writes, beside the manifest. */
|
|
107
114
|
redirectFiles: RedirectFile[];
|
|
115
|
+
/**
|
|
116
|
+
* Whether the adapter moves a server build's client output under
|
|
117
|
+
* `deployment.base` (`dist/client/<base>/`, the directory Astro then hands
|
|
118
|
+
* `astro:build:done`) while the platform keeps serving `serverStaticDir`
|
|
119
|
+
* as the assets root. The files the platform reads there, like `_headers`,
|
|
120
|
+
* belong above the directory Astro reports.
|
|
121
|
+
*/
|
|
122
|
+
serverClientUnderBase: boolean;
|
|
108
123
|
/** Where a server build's deploy bundle lands. */
|
|
109
124
|
serverOutputDir: (context: ProjectContext) => string;
|
|
110
125
|
/** The directory a server build serves as static files. */
|
|
@@ -124,7 +124,10 @@ export const emitVercelNegotiation = async (
|
|
|
124
124
|
json: existsSync(join(staticDir, "404.json")),
|
|
125
125
|
markdown: existsSync(join(staticDir, "404.md")),
|
|
126
126
|
},
|
|
127
|
-
crossOriginDiscoveryPaths(config)
|
|
127
|
+
crossOriginDiscoveryPaths(config),
|
|
128
|
+
// Downloaded content assets are prerendered static files; only a build
|
|
129
|
+
// that has them needs the SVG sandbox route.
|
|
130
|
+
existsSync(join(staticDir, "blume-assets"))
|
|
128
131
|
);
|
|
129
132
|
if (injected === null) {
|
|
130
133
|
log.warn(
|
|
@@ -144,8 +147,9 @@ export const emitVercelNegotiation = async (
|
|
|
144
147
|
* `@vercel/nft` dependency trace is rooted there too, and tracing from the
|
|
145
148
|
* hidden runtime silently drops the function's chunks and `node_modules`.
|
|
146
149
|
* Static assets are served from the tree's `static/` half, so the deploy
|
|
147
|
-
* artifacts are written there; headers
|
|
148
|
-
*
|
|
150
|
+
* artifacts are written there; headers (the discovery files', the sandbox on
|
|
151
|
+
* downloaded SVGs) arrive through the routing config rather than a
|
|
152
|
+
* `_headers` file, which Vercel never reads.
|
|
149
153
|
*/
|
|
150
154
|
export const vercelPlatform: DeployPlatform = {
|
|
151
155
|
astro: {
|
|
@@ -186,8 +190,11 @@ export const vercelPlatform: DeployPlatform = {
|
|
|
186
190
|
},
|
|
187
191
|
kind: "vercel",
|
|
188
192
|
negotiatesMarkdown: true,
|
|
193
|
+
// @astrojs/vercel declares no preview entrypoint.
|
|
194
|
+
previewDeploy: "vercel deploy",
|
|
189
195
|
readsHeaderFiles: { server: false, static: false },
|
|
190
196
|
redirectFiles: [VERCEL_JSON_FILE],
|
|
197
|
+
serverClientUnderBase: false,
|
|
191
198
|
serverOutputDir: buildOutputDir,
|
|
192
199
|
serverStaticDir: (context) => join(buildOutputDir(context), "static"),
|
|
193
200
|
};
|
package/src/deploy/redirects.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
normalizeBasePath,
|
|
3
|
+
withAuthoredBasePath,
|
|
3
4
|
withBasePath,
|
|
4
5
|
withComposedBasePath,
|
|
5
6
|
} from "../core/base-path.ts";
|
|
7
|
+
import { routeSetFor, servesRoute } from "../core/locale-links.ts";
|
|
8
|
+
import type { RouteSet } from "../core/locale-links.ts";
|
|
9
|
+
import type { BlumeProject } from "../core/project-graph.ts";
|
|
6
10
|
import type { ResolvedConfig } from "../core/schema.ts";
|
|
11
|
+
import { escapeVercelSource } from "./headers.ts";
|
|
7
12
|
import type { VercelHeader } from "./headers.ts";
|
|
8
13
|
|
|
9
14
|
/**
|
|
@@ -16,6 +21,23 @@ import type { VercelHeader } from "./headers.ts";
|
|
|
16
21
|
|
|
17
22
|
type Redirect = ResolvedConfig["redirects"][number];
|
|
18
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Base a redirect destination authored as if mounted at root by what it
|
|
26
|
+
* names (see `withAuthoredBasePath`): a page gains the full
|
|
27
|
+
* `{deployment.base}{basePath}` stack, a public file (`/files/spec.pdf`, with
|
|
28
|
+
* no page in `routes`) only the deployment base — `public/` is served there
|
|
29
|
+
* whatever `basePath` is. External URLs pass through.
|
|
30
|
+
*/
|
|
31
|
+
const baseRedirectTarget = (
|
|
32
|
+
to: string,
|
|
33
|
+
basePath: string,
|
|
34
|
+
base: string,
|
|
35
|
+
routes: RouteSet
|
|
36
|
+
): string =>
|
|
37
|
+
withAuthoredBasePath(base, basePath, to, (route) =>
|
|
38
|
+
servesRoute(routes, route)
|
|
39
|
+
);
|
|
40
|
+
|
|
19
41
|
/**
|
|
20
42
|
* Base a redirect for Astro's `redirects` config, where the two sides are not
|
|
21
43
|
* symmetric:
|
|
@@ -23,19 +45,23 @@ type Redirect = ResolvedConfig["redirects"][number];
|
|
|
23
45
|
* - `from` gains only `basePath`. Astro builds the match pattern with
|
|
24
46
|
* `deployment.base` already applied (`getPattern(segments, config.base)`), so
|
|
25
47
|
* adding it here would serve the redirect at `{base}{base}/from`.
|
|
26
|
-
* - `to` gains the full `{deployment.base}{basePath}` stack
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
48
|
+
* - `to` gains the full `{deployment.base}{basePath}` stack, or the deployment
|
|
49
|
+
* base alone for a public file (see {@link baseRedirectTarget}). Astro
|
|
50
|
+
* resolves a destination that matches a known route by regenerating it from
|
|
51
|
+
* that route's segments, which carry no base — and passes an unmatched
|
|
52
|
+
* destination through verbatim. Neither path prepends `base`, so a
|
|
53
|
+
* root-relative `to` escapes the base entirely (withastro/astro#7774, still
|
|
54
|
+
* the behavior in Astro 7).
|
|
31
55
|
*
|
|
32
|
-
* Both sides are authored as if mounted at root;
|
|
33
|
-
*
|
|
56
|
+
* Both sides are authored as if mounted at root; `routes` (the served page
|
|
57
|
+
* routes, carrying `basePath`) tell a dotted page route like `/releases/v1.2`
|
|
58
|
+
* from a file. Idempotent, so a hand-written base isn't doubled.
|
|
34
59
|
*/
|
|
35
60
|
export const applyBaseToAstroRedirects = (
|
|
36
61
|
redirects: Redirect[],
|
|
37
62
|
basePath: string,
|
|
38
|
-
deployBase: string
|
|
63
|
+
deployBase: string,
|
|
64
|
+
routes: RouteSet
|
|
39
65
|
): Redirect[] => {
|
|
40
66
|
// `deployment.base` arrives as the user wrote it (Astro accepts `/base/`,
|
|
41
67
|
// even `base`); normalizing here keeps the composed paths well-formed.
|
|
@@ -44,27 +70,29 @@ export const applyBaseToAstroRedirects = (
|
|
|
44
70
|
? redirects.map((redirect) => ({
|
|
45
71
|
...redirect,
|
|
46
72
|
from: withBasePath(basePath, redirect.from),
|
|
47
|
-
to:
|
|
73
|
+
to: baseRedirectTarget(redirect.to, basePath, base, routes),
|
|
48
74
|
}))
|
|
49
75
|
: redirects;
|
|
50
76
|
};
|
|
51
77
|
|
|
52
78
|
/**
|
|
53
79
|
* Base a redirect for the host platform files below. Unlike Astro's config,
|
|
54
|
-
* these are matched against the real served URL, so
|
|
55
|
-
* `{deployment.base}{basePath}` stack
|
|
80
|
+
* these are matched against the real served URL, so `from` carries the full
|
|
81
|
+
* `{deployment.base}{basePath}` stack, and so does `to` unless it names a
|
|
82
|
+
* public file (see {@link baseRedirectTarget}).
|
|
56
83
|
*/
|
|
57
84
|
export const applyBaseToPlatformRedirects = (
|
|
58
85
|
redirects: Redirect[],
|
|
59
86
|
basePath: string,
|
|
60
|
-
deployBase: string
|
|
87
|
+
deployBase: string,
|
|
88
|
+
routes: RouteSet
|
|
61
89
|
): Redirect[] => {
|
|
62
90
|
const base = normalizeBasePath(deployBase);
|
|
63
91
|
return basePath || base
|
|
64
92
|
? redirects.map((redirect) => ({
|
|
65
93
|
...redirect,
|
|
66
94
|
from: withComposedBasePath(base, basePath, redirect.from),
|
|
67
|
-
to:
|
|
95
|
+
to: baseRedirectTarget(redirect.to, basePath, base, routes),
|
|
68
96
|
}))
|
|
69
97
|
: redirects;
|
|
70
98
|
};
|
|
@@ -75,17 +103,34 @@ export const applyBaseToPlatformRedirects = (
|
|
|
75
103
|
* share: the emitted redirect files and the Cloudflare worker-first redirect
|
|
76
104
|
* exemptions both compare these paths against real served URLs.
|
|
77
105
|
*/
|
|
78
|
-
export const platformRedirects = (
|
|
79
|
-
|
|
106
|
+
export const platformRedirects = (project: {
|
|
107
|
+
config: ResolvedConfig;
|
|
108
|
+
manifest: Pick<BlumeProject["manifest"], "routes">;
|
|
109
|
+
}): Redirect[] => {
|
|
110
|
+
const { config } = project;
|
|
111
|
+
return applyBaseToPlatformRedirects(
|
|
80
112
|
config.redirects,
|
|
81
113
|
config.basePath,
|
|
82
|
-
config.deployment.options.base ?? ""
|
|
114
|
+
config.deployment.options.base ?? "",
|
|
115
|
+
routeSetFor(project.manifest.routes)
|
|
83
116
|
);
|
|
117
|
+
};
|
|
84
118
|
|
|
85
|
-
/**
|
|
86
|
-
|
|
119
|
+
/**
|
|
120
|
+
* `_redirects` text (Netlify + Cloudflare Pages): `from to status` per line.
|
|
121
|
+
* `force` appends Netlify's `!` to each status (`301!`), so the rule wins over
|
|
122
|
+
* the redirect page Astro writes at `from`; Cloudflare, which always applies
|
|
123
|
+
* its rules first, rejects a line carrying it.
|
|
124
|
+
*/
|
|
125
|
+
export const buildNetlifyRedirects = (
|
|
126
|
+
redirects: Redirect[],
|
|
127
|
+
force = false
|
|
128
|
+
): string =>
|
|
87
129
|
`${redirects
|
|
88
|
-
.map(
|
|
130
|
+
.map(
|
|
131
|
+
(redirect) =>
|
|
132
|
+
`${redirect.from} ${redirect.to} ${redirect.status}${force ? "!" : ""}`
|
|
133
|
+
)
|
|
89
134
|
.join("\n")}\n`;
|
|
90
135
|
|
|
91
136
|
/** The `vercel.json` Blume writes for a static deploy of `dist/`. */
|
|
@@ -99,7 +144,9 @@ interface VercelConfig {
|
|
|
99
144
|
* header rules are given (see `buildVercelHeaders`). Uses `statusCode` (Vercel's
|
|
100
145
|
* alternative to the boolean `permanent`) so the configured code ships exactly:
|
|
101
146
|
* `permanent` would silently coerce a 301 to 308 and a 302 to 307, diverging
|
|
102
|
-
* from the `_redirects` file, which preserves exact codes.
|
|
147
|
+
* from the `_redirects` file, which preserves exact codes. A `source` is a
|
|
148
|
+
* `path-to-regexp` pattern, so each exact `from` path is escaped: unescaped,
|
|
149
|
+
* `/c++-guide` fails the whole config and `/faq(old)` never matches.
|
|
103
150
|
*/
|
|
104
151
|
export const buildVercelConfig = (
|
|
105
152
|
redirects: Redirect[],
|
|
@@ -108,7 +155,7 @@ export const buildVercelConfig = (
|
|
|
108
155
|
const config: VercelConfig = {
|
|
109
156
|
redirects: redirects.map((redirect) => ({
|
|
110
157
|
destination: redirect.to,
|
|
111
|
-
source: redirect.from,
|
|
158
|
+
source: escapeVercelSource(redirect.from),
|
|
112
159
|
statusCode: redirect.status,
|
|
113
160
|
})),
|
|
114
161
|
};
|
package/src/deploy/robots.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mountBasePath, normalizeBasePath } from "../core/base-path.ts";
|
|
2
2
|
import type { BlumeProject } from "../core/project-graph.ts";
|
|
3
3
|
import type { ContentSignalPolicy, ContentSignals } from "../core/schema.ts";
|
|
4
4
|
import { absoluteUrl } from "../core/site-url.ts";
|
|
@@ -49,7 +49,7 @@ export const buildRobots = (project: BlumeProject): string | null => {
|
|
|
49
49
|
|
|
50
50
|
const { site } = config.deployment.options;
|
|
51
51
|
if (site && config.seo.sitemap) {
|
|
52
|
-
const sitemapPath =
|
|
52
|
+
const sitemapPath = mountBasePath(
|
|
53
53
|
normalizeBasePath(config.deployment.options.base),
|
|
54
54
|
"/sitemap.xml"
|
|
55
55
|
);
|
package/src/deploy/rss.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// `'` reference rather than `'`, which XML accepts equally.
|
|
3
3
|
import { escape as escapeXml } from "html-escaper";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { mountBasePath, normalizeBasePath } from "../core/base-path.ts";
|
|
6
6
|
import type { BlumeProject } from "../core/project-graph.ts";
|
|
7
7
|
import { siteRoot } from "../core/site-url.ts";
|
|
8
8
|
import type { PageRecord } from "../core/types.ts";
|
|
@@ -57,7 +57,8 @@ export const buildRssFeeds = (project: BlumeProject): RssFeed[] => {
|
|
|
57
57
|
return [];
|
|
58
58
|
}
|
|
59
59
|
const base = siteRoot(site);
|
|
60
|
-
// Routes carry `basePath`; a `deployment.base` subdirectory is
|
|
60
|
+
// Routes carry `basePath`; a `deployment.base` subdirectory is mounted on top
|
|
61
|
+
// of every one, even a route whose first segment matches it (see sitemap.ts).
|
|
61
62
|
// The feed's own `link`/self URL points at the docs root under that base, while
|
|
62
63
|
// `path` stays base-less (it's also the on-disk output location).
|
|
63
64
|
const deployBase = normalizeBasePath(config.deployment.options.base);
|
|
@@ -80,7 +81,7 @@ export const buildRssFeeds = (project: BlumeProject): RssFeed[] => {
|
|
|
80
81
|
description: page.description,
|
|
81
82
|
// Encode like the sitemap does: a route with spaces or non-ASCII
|
|
82
83
|
// must still yield a valid <link>/<guid> URL after XML decoding.
|
|
83
|
-
link: encodeURI(`${base}${
|
|
84
|
+
link: encodeURI(`${base}${mountBasePath(deployBase, page.route)}`),
|
|
84
85
|
title: page.title,
|
|
85
86
|
}))
|
|
86
87
|
.toSorted((a, b) => (b.date?.getTime() ?? 0) - (a.date?.getTime() ?? 0))
|
package/src/deploy/sitemap.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
discoverPagesSync,
|
|
7
7
|
hasGeneratedChangelog,
|
|
8
8
|
} from "../astro/pages.ts";
|
|
9
|
-
import {
|
|
9
|
+
import { mountBasePath, normalizeBasePath } from "../core/base-path.ts";
|
|
10
10
|
import type { BlumeProject } from "../core/project-graph.ts";
|
|
11
11
|
import { siteRoot } from "../core/site-url.ts";
|
|
12
12
|
|
|
@@ -78,7 +78,9 @@ export const buildSitemapFiles = (
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
const base = siteRoot(site);
|
|
81
|
-
// Routes carry `basePath`; a `deployment.base` subdirectory is
|
|
81
|
+
// Routes carry `basePath`; a `deployment.base` subdirectory is mounted on
|
|
82
|
+
// top of every one, even a route whose first segment matches it: under base
|
|
83
|
+
// `/guides`, `guides/setup.md` is served at `/guides/guides/setup`.
|
|
82
84
|
const deployBase = normalizeBasePath(project.config.deployment.options.base);
|
|
83
85
|
|
|
84
86
|
// Archived-version pages leave the sitemap when the version is noindexed,
|
|
@@ -135,7 +137,7 @@ export const buildSitemapFiles = (
|
|
|
135
137
|
) {
|
|
136
138
|
continue;
|
|
137
139
|
}
|
|
138
|
-
pushUrl(
|
|
140
|
+
pushUrl(mountBasePath(deployBase, page.route), page.lastModified);
|
|
139
141
|
}
|
|
140
142
|
// Custom `.astro` pages and the generated changelog index mount outside
|
|
141
143
|
// `basePath` (they're injected at their pattern — see `blumeIntegration`), so
|
|
@@ -150,7 +152,7 @@ export const buildSitemapFiles = (
|
|
|
150
152
|
extraRoutes.push("/changelog");
|
|
151
153
|
}
|
|
152
154
|
for (const route of extraRoutes) {
|
|
153
|
-
pushUrl(
|
|
155
|
+
pushUrl(mountBasePath(deployBase, route));
|
|
154
156
|
}
|
|
155
157
|
urls.sort();
|
|
156
158
|
|
|
@@ -169,7 +171,7 @@ export const buildSitemapFiles = (
|
|
|
169
171
|
// Chunks sit next to sitemap.xml, so their URLs layer the same deployment
|
|
170
172
|
// base robots.txt uses for the index.
|
|
171
173
|
const loc = escapeXml(
|
|
172
|
-
encodeURI(`${base}${
|
|
174
|
+
encodeURI(`${base}${mountBasePath(deployBase, `/${name}`)}`)
|
|
173
175
|
);
|
|
174
176
|
references.push(` <sitemap><loc>${loc}</loc></sitemap>`);
|
|
175
177
|
}
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
* prerendered problem-details 404.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
import { SVG_ASSET_HEADERS } from "./headers.ts";
|
|
20
|
+
|
|
19
21
|
/**
|
|
20
22
|
* Regex for the `accept` header condition. Written to hold under both matching
|
|
21
23
|
* semantics a router may apply — full-string and substring — by anchoring the
|
|
@@ -248,6 +250,30 @@ const corsRoute = (path: string): VercelRoute => ({
|
|
|
248
250
|
src: `^${routePattern(path)}$`,
|
|
249
251
|
});
|
|
250
252
|
|
|
253
|
+
/**
|
|
254
|
+
* The `src` of the route stamping the content-source SVG headers: every `.svg`
|
|
255
|
+
* under `/blume-assets/`. The adapter copies the static files into the Build
|
|
256
|
+
* Output tree without the deployment base, like every path matched here.
|
|
257
|
+
*/
|
|
258
|
+
const SVG_ASSET_SRC = String.raw`^/blume-assets/.+\.svg$`;
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* A main-phase `continue` route stamping the sandbox headers
|
|
262
|
+
* (`deploy/headers.ts`) on a downloaded SVG, which Vercel serves from the
|
|
263
|
+
* static layer — where the prerendered asset endpoint's own headers never
|
|
264
|
+
* reach.
|
|
265
|
+
*/
|
|
266
|
+
const svgAssetRoute = (): VercelRoute => ({
|
|
267
|
+
continue: true,
|
|
268
|
+
headers: Object.fromEntries(
|
|
269
|
+
Object.entries(SVG_ASSET_HEADERS).map(([name, value]) => [
|
|
270
|
+
name.toLowerCase(),
|
|
271
|
+
value,
|
|
272
|
+
])
|
|
273
|
+
),
|
|
274
|
+
src: SVG_ASSET_SRC,
|
|
275
|
+
});
|
|
276
|
+
|
|
251
277
|
/** Whether a route is a `corsRoute` — the same three-field shape test as the others. */
|
|
252
278
|
const isCorsRoute = (route: VercelRoute): boolean =>
|
|
253
279
|
route.continue === true &&
|
|
@@ -279,7 +305,8 @@ const isNegotiationRoute = (route: VercelRoute): boolean =>
|
|
|
279
305
|
isString(route.headers?.link) &&
|
|
280
306
|
route.src === HOME_SRC &&
|
|
281
307
|
Object.keys(route).length === 3) ||
|
|
282
|
-
isCorsRoute(route)
|
|
308
|
+
isCorsRoute(route) ||
|
|
309
|
+
(route.continue === true && route.src === SVG_ASSET_SRC);
|
|
283
310
|
|
|
284
311
|
/**
|
|
285
312
|
* Splice the negotiation routes into a Build Output `config.json`, plus — when
|
|
@@ -297,7 +324,8 @@ const isNegotiationRoute = (route: VercelRoute): boolean =>
|
|
|
297
324
|
* `404.md`, `notFound.json` for `404.json`), its routes go into the miss
|
|
298
325
|
* phase right before the adapter's `/404.html` fallback — and nowhere when
|
|
299
326
|
* that fallback is absent, since a `dest` with no file behind it would serve
|
|
300
|
-
* nothing.
|
|
327
|
+
* nothing. With `svgAssets`, a main-phase route also sandboxes the SVGs a
|
|
328
|
+
* content source downloaded (see {@link svgAssetRoute}). Returns the updated JSON
|
|
301
329
|
* text (tab-indented, like the adapter's own output), or `null` when there is
|
|
302
330
|
* nowhere safe to splice: an unparsable config, no `routes` array, or no
|
|
303
331
|
* `handle: "filesystem"` marker to anchor the splice.
|
|
@@ -309,7 +337,8 @@ export const injectNegotiationRoutes = (
|
|
|
309
337
|
contentTypeOverrides?: Record<string, string>,
|
|
310
338
|
homeTokens?: number,
|
|
311
339
|
notFound: NotFoundVariants = {},
|
|
312
|
-
corsPaths: readonly string[] = []
|
|
340
|
+
corsPaths: readonly string[] = [],
|
|
341
|
+
svgAssets = false
|
|
313
342
|
): string | null => {
|
|
314
343
|
const overrideEntries = Object.entries(contentTypeOverrides ?? {});
|
|
315
344
|
let config: {
|
|
@@ -348,6 +377,9 @@ export const injectNegotiationRoutes = (
|
|
|
348
377
|
});
|
|
349
378
|
}
|
|
350
379
|
headerRoutes.push(...corsPaths.map(corsRoute));
|
|
380
|
+
if (svgAssets) {
|
|
381
|
+
headerRoutes.push(svgAssetRoute());
|
|
382
|
+
}
|
|
351
383
|
// Headers first: `continue` routes accumulate, so a request the rewrite
|
|
352
384
|
// route then terminates (Markdown negotiation on the homepage) still carries
|
|
353
385
|
// the Link header.
|
package/src/eval/agents.ts
CHANGED
|
@@ -124,6 +124,14 @@ export const runAgentHeadless = (
|
|
|
124
124
|
}
|
|
125
125
|
});
|
|
126
126
|
|
|
127
|
+
// An agent that exits before reading the whole prompt (it rejected a flag,
|
|
128
|
+
// say) closes the pipe mid-write, and the resulting EPIPE would otherwise
|
|
129
|
+
// be an uncaught exception that aborts the run. The exit itself is the
|
|
130
|
+
// outcome: `close` resolves with the agent's code and stderr, which
|
|
131
|
+
// `readAgentOutput` reports.
|
|
132
|
+
child.stdin?.on("error", () => {
|
|
133
|
+
// Deliberately ignored — see above.
|
|
134
|
+
});
|
|
127
135
|
child.stdin?.end(options.prompt);
|
|
128
136
|
});
|
|
129
137
|
|
|
@@ -208,7 +216,7 @@ const claudeArgs = (context: InvocationContext): string[] => {
|
|
|
208
216
|
|
|
209
217
|
/**
|
|
210
218
|
* Codex config overrides that leave an eval run nothing but its prompt and the
|
|
211
|
-
* docs MCP tools. `--sandbox read-only` blocks writes and the network but still
|
|
219
|
+
* docs MCP tools (and a `blume translate` run nothing but its prompt). `--sandbox read-only` blocks writes and the network but still
|
|
212
220
|
* lets the shell tool read any file the user can, and the docs a reader
|
|
213
221
|
* searches — or the answer a judge grades — can come from a remote source, so
|
|
214
222
|
* an instruction planted there could otherwise pull a local secret into the
|
|
@@ -216,7 +224,7 @@ const claudeArgs = (context: InvocationContext): string[] => {
|
|
|
216
224
|
* local-image tool off, and any subprocess started with no inherited
|
|
217
225
|
* environment, there is nothing on the machine for a run to read.
|
|
218
226
|
*/
|
|
219
|
-
const CODEX_LOCKDOWN = [
|
|
227
|
+
export const CODEX_LOCKDOWN = [
|
|
220
228
|
"-c",
|
|
221
229
|
"features.shell_tool=false",
|
|
222
230
|
"-c",
|
package/src/eval/run.ts
CHANGED
|
@@ -181,6 +181,31 @@ const runQuestion = async (
|
|
|
181
181
|
};
|
|
182
182
|
};
|
|
183
183
|
|
|
184
|
+
/** The findings a missed question produces: failed, or its agent run errored. */
|
|
185
|
+
const MISS_CODES: ReadonlySet<string> = new Set([
|
|
186
|
+
"BLUME_EVAL_QUESTION_ERROR",
|
|
187
|
+
"BLUME_EVAL_QUESTION_FAILED",
|
|
188
|
+
]);
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* The fraction of run (non-skipped) questions that pass the CI gate. A miss on
|
|
192
|
+
* a `severity: warning` question is reported as a warning and never counts
|
|
193
|
+
* against the gate — each miss's finding carries its question's severity, so
|
|
194
|
+
* the gate reads the same findings the JSON summary counts, and the exit code
|
|
195
|
+
* can't disagree with the summary's error total.
|
|
196
|
+
*/
|
|
197
|
+
export const passFraction = (result: EvalResult): number => {
|
|
198
|
+
const ran = result.results.length - result.counts.skip;
|
|
199
|
+
if (ran === 0) {
|
|
200
|
+
return 1;
|
|
201
|
+
}
|
|
202
|
+
const misses = result.diagnostics.filter(
|
|
203
|
+
(diagnostic) =>
|
|
204
|
+
MISS_CODES.has(diagnostic.code) && diagnostic.severity === "error"
|
|
205
|
+
).length;
|
|
206
|
+
return (ran - misses) / ran;
|
|
207
|
+
};
|
|
208
|
+
|
|
184
209
|
/**
|
|
185
210
|
* Run every question through the reader/judge pair, sequentially: each
|
|
186
211
|
* question is already two agent sessions, and a serial run keeps progress
|