blume 2.0.0 → 2.0.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/AGENTS.md +1 -1
- package/CHANGELOG.md +110 -0
- package/README.md +2 -2
- package/dist/cli/{chunk-f2972sbt.js → chunk-00gs3wqs.js} +1 -1
- package/dist/cli/{chunk-fa25z98p.js → chunk-273ygyr4.js} +4 -3
- package/dist/cli/chunk-273ygyr4.js.map +11 -0
- package/dist/cli/{chunk-pat2zzwc.js → chunk-3yce002v.js} +21 -8
- package/dist/cli/{chunk-pat2zzwc.js.map → chunk-3yce002v.js.map} +4 -4
- package/dist/cli/{chunk-d1tadaw7.js → chunk-4e9b9ra6.js} +17 -7
- package/dist/cli/chunk-4e9b9ra6.js.map +10 -0
- package/dist/cli/{chunk-zg2gtj10.js → chunk-5r8g91qn.js} +352 -676
- package/dist/cli/chunk-5r8g91qn.js.map +34 -0
- package/dist/cli/{chunk-11j0384y.js → chunk-6dtt0zfn.js} +14 -14
- package/dist/cli/{chunk-11j0384y.js.map → chunk-6dtt0zfn.js.map} +3 -3
- package/dist/cli/{chunk-bw22s759.js → chunk-6k4ftwze.js} +38 -19
- package/dist/cli/{chunk-bw22s759.js.map → chunk-6k4ftwze.js.map} +4 -4
- package/dist/cli/{chunk-sqn5t4q0.js → chunk-7mbqtmgb.js} +16 -7
- package/dist/cli/chunk-7mbqtmgb.js.map +10 -0
- package/dist/cli/{chunk-79njf86q.js → chunk-7vtckvaw.js} +21 -19
- package/dist/cli/chunk-7vtckvaw.js.map +11 -0
- package/dist/cli/{chunk-fh5hj5jt.js → chunk-8g8ytmgx.js} +17 -18
- package/dist/cli/{chunk-fh5hj5jt.js.map → chunk-8g8ytmgx.js.map} +2 -2
- package/dist/cli/{chunk-1w8dp3qb.js → chunk-91ws1n6j.js} +18 -15
- package/dist/cli/chunk-91ws1n6j.js.map +10 -0
- package/dist/cli/{chunk-7ez8ny0t.js → chunk-bbnwccaz.js} +2 -2
- package/dist/cli/{chunk-5a2z0198.js → chunk-bfwp9vp6.js} +18 -10
- package/dist/cli/chunk-bfwp9vp6.js.map +10 -0
- package/dist/cli/{chunk-ernrthtr.js → chunk-d1v5rhy0.js} +14 -15
- package/dist/cli/{chunk-ernrthtr.js.map → chunk-d1v5rhy0.js.map} +2 -2
- package/dist/cli/{chunk-a9kptbw5.js → chunk-ddndchfr.js} +21 -6
- package/dist/cli/chunk-ddndchfr.js.map +14 -0
- package/dist/cli/{chunk-zxccj738.js → chunk-esh98wmb.js} +1 -1
- package/dist/cli/{chunk-88cpgt6h.js → chunk-fsmrqk8a.js} +1 -1
- package/dist/cli/{chunk-b5aj94ah.js → chunk-g698a744.js} +5 -5
- package/dist/cli/{chunk-jts8mvcz.js → chunk-gs7r695n.js} +9 -3
- package/dist/cli/{chunk-jts8mvcz.js.map → chunk-gs7r695n.js.map} +3 -3
- package/dist/cli/{chunk-41za066z.js → chunk-h2ez8dzb.js} +4 -4
- package/dist/cli/{chunk-by2290sx.js → chunk-h7k3nq3v.js} +2 -2
- package/dist/cli/{chunk-j8mw0za6.js → chunk-hqp2ajnh.js} +252 -123
- package/dist/cli/chunk-hqp2ajnh.js.map +35 -0
- package/dist/cli/{chunk-mnqj32sj.js → chunk-hr8ne106.js} +119 -50
- package/dist/cli/chunk-hr8ne106.js.map +13 -0
- package/dist/cli/{chunk-mwt1k8n7.js → chunk-j85scx15.js} +74 -30
- package/dist/cli/chunk-j85scx15.js.map +10 -0
- package/dist/cli/{chunk-z01ze5c1.js → chunk-k7pj68a8.js} +63 -22
- package/dist/cli/chunk-k7pj68a8.js.map +11 -0
- package/dist/cli/{chunk-2q1dwty4.js → chunk-mqc662a6.js} +8 -8
- package/dist/cli/{chunk-2q1dwty4.js.map → chunk-mqc662a6.js.map} +4 -4
- package/dist/cli/{chunk-y3e45rc8.js → chunk-n1yg3tj3.js} +4 -4
- package/dist/cli/{chunk-bctazmbk.js → chunk-nfcyttvj.js} +17 -6
- package/dist/cli/chunk-nfcyttvj.js.map +10 -0
- package/dist/cli/{chunk-6k8vp3ta.js → chunk-pbg5a4s3.js} +60 -28
- package/dist/cli/chunk-pbg5a4s3.js.map +19 -0
- package/dist/cli/{chunk-d80hr03s.js → chunk-ppzjqwx2.js} +24 -17
- package/dist/cli/{chunk-d80hr03s.js.map → chunk-ppzjqwx2.js.map} +4 -4
- package/dist/cli/{chunk-beat36xx.js → chunk-qkb5a8sa.js} +25 -10
- package/dist/cli/chunk-qkb5a8sa.js.map +10 -0
- package/dist/cli/{chunk-bnbmcwfb.js → chunk-sqw4ekg1.js} +5 -5
- package/dist/cli/{chunk-bnbmcwfb.js.map → chunk-sqw4ekg1.js.map} +3 -3
- package/dist/cli/{chunk-xaz13gwg.js → chunk-v6ya5kcb.js} +3268 -1150
- package/dist/cli/chunk-v6ya5kcb.js.map +189 -0
- package/dist/cli/{chunk-tzne8qfq.js → chunk-w4bxdvsa.js} +18 -15
- package/dist/cli/chunk-w4bxdvsa.js.map +10 -0
- package/dist/cli/{chunk-f7t03s3g.js → chunk-wdrt2k2v.js} +2 -2
- package/dist/cli/{chunk-z1f5arsg.js → chunk-xh43dwgw.js} +170 -55
- package/dist/cli/chunk-xh43dwgw.js.map +36 -0
- package/dist/cli/{chunk-pnnvybbk.js → chunk-zp79m0ts.js} +5 -5
- package/dist/cli/{chunk-pnnvybbk.js.map → chunk-zp79m0ts.js.map} +2 -2
- package/dist/cli/index.js +160 -35
- package/dist/cli/index.js.map +4 -4
- package/dist/types/ai/agent-readability.d.ts +1 -1
- package/dist/types/ai/agent-surface.d.ts +32 -0
- package/dist/types/ai/api/paths.d.ts +1 -1
- package/dist/types/ai/api-catalog.d.ts +7 -1
- package/dist/types/ai/ask-context.d.ts +14 -7
- package/dist/types/ai/ask.d.ts +43 -43
- package/dist/types/ai/component-markdown.d.ts +4 -4
- package/dist/types/ai/index.d.ts +3 -3
- package/dist/types/ai/openapi-components.d.ts +1 -1
- package/dist/types/ai/relative-links.d.ts +9 -4
- package/dist/types/ai/serializers.d.ts +1 -1
- package/dist/types/ai/static-expression.d.ts +28 -0
- package/dist/types/ai/visibility.d.ts +1 -1
- package/dist/types/analytics/databuddy.d.ts +43 -0
- package/dist/types/analytics/index.d.ts +2 -0
- package/dist/types/analytics/schema.d.ts +14 -0
- 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 +9 -0
- package/dist/types/core/config-input.d.ts +21 -21
- package/dist/types/core/config.d.ts +5 -5
- package/dist/types/core/data.d.ts +3 -3
- package/dist/types/core/graph.d.ts +2 -0
- package/dist/types/core/i18n-ui.d.ts +37 -8
- package/dist/types/core/i18n.d.ts +7 -1
- 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 +5 -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 +57 -18
- package/dist/types/core/sources/github-releases.d.ts +5 -0
- package/dist/types/core/sources/lower.d.ts +14 -2
- package/dist/types/core/sources/normalize.d.ts +10 -1
- 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/types.d.ts +9 -0
- package/dist/types/core/unrecognized-keys.d.ts +1 -1
- package/dist/types/deploy/adapters/node.d.ts +5 -2
- package/dist/types/deploy/adapters/types.d.ts +7 -0
- package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
- package/dist/types/deploy/headers.d.ts +31 -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 +7 -0
- package/dist/types/deploy/platforms/vercel.d.ts +3 -2
- package/dist/types/deploy/redirects.d.ts +7 -2
- package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
- package/dist/types/openapi/model.d.ts +20 -6
- package/dist/types/search/documents.d.ts +1 -1
- package/dist/types/search/orama-index.d.ts +1 -1
- package/dist/types/search/sync/algolia.d.ts +3 -1
- package/docs/01-quickstart.mdx +3 -2
- package/docs/02-deployment.mdx +21 -12
- package/docs/03-upgrading.mdx +22 -9
- package/docs/04-migrating.mdx +4 -4
- package/docs/08-faq.mdx +12 -5
- package/docs/advanced/changelog.mdx +1 -1
- package/docs/advanced/custom-pages.mdx +5 -3
- package/docs/advanced/skills.mdx +1 -1
- package/docs/cli/audit.mdx +5 -5
- package/docs/cli/doctor.mdx +4 -4
- package/docs/cli/evals.mdx +9 -9
- package/docs/cli/index.mdx +6 -3
- package/docs/cli/translate.mdx +8 -8
- package/docs/configuration/analytics.mdx +23 -2
- package/docs/configuration/{ask-ai.mdx → assistant.mdx} +20 -20
- package/docs/configuration/customization.mdx +5 -4
- package/docs/configuration/index.mdx +7 -5
- package/docs/configuration/meta.ts +1 -1
- package/docs/configuration/search.mdx +2 -2
- package/docs/content/components.mdx +1 -1
- package/docs/content/frontmatter.mdx +5 -1
- package/docs/content/i18n.mdx +3 -3
- package/docs/content/index.mdx +4 -2
- package/docs/content/islands.mdx +1 -1
- package/docs/content/meta.mdx +5 -3
- package/docs/content/navigation.mdx +29 -4
- package/docs/content/sources.mdx +18 -12
- package/docs/content/versioning.mdx +1 -0
- package/docs/discoverability/agent-discovery.mdx +22 -8
- package/docs/discoverability/index.mdx +3 -3
- package/docs/discoverability/llms-txt.mdx +2 -5
- package/docs/discoverability/markdown.mdx +5 -3
- package/docs/discoverability/mcp.mdx +4 -0
- package/docs/discoverability/metadata.mdx +3 -2
- package/docs/discoverability/open-graph.mdx +6 -4
- package/docs/discoverability/rss.mdx +3 -1
- package/docs/index.mdx +2 -2
- package/docs/references/asyncapi.mdx +1 -1
- package/docs/references/graphql.mdx +2 -2
- package/docs/references/openapi.mdx +3 -3
- package/package.json +1 -1
- package/skills/blume/SKILL.md +5 -5
- package/skills/blume-migrate/SKILL.md +6 -6
- package/skills/blume-migrate/references/docusaurus.md +6 -6
- 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 +1 -1
- package/skills/blume-migrate/references/starlight.md +5 -5
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +2 -2
- package/skills/blume-update-docs/references/audit-checklist.md +1 -1
- package/src/ai/agent-readability.ts +4 -4
- package/src/ai/agent-surface.ts +56 -0
- package/src/ai/api/paths.ts +1 -1
- package/src/ai/api/spec.ts +15 -2
- package/src/ai/api-catalog.ts +7 -1
- package/src/ai/ask-context.ts +21 -9
- package/src/ai/ask-data.ts +28 -14
- package/src/ai/ask.ts +84 -71
- package/src/ai/component-markdown.ts +43 -37
- package/src/ai/cors.ts +3 -3
- package/src/ai/index.ts +16 -16
- package/src/ai/link-headers.ts +8 -3
- package/src/ai/llms.ts +4 -3
- package/src/ai/markdown.ts +23 -11
- package/src/ai/mcp/server.ts +78 -7
- package/src/ai/openapi-components.ts +1 -1
- package/src/ai/relative-links.ts +78 -10
- package/src/ai/serializers.ts +1 -1
- package/src/ai/static-expression.ts +416 -0
- package/src/ai/visibility.ts +46 -15
- package/src/analytics/databuddy.ts +67 -0
- package/src/analytics/head.ts +4 -0
- package/src/analytics/index.ts +2 -0
- package/src/analytics/posthog.ts +21 -3
- package/src/analytics/schema.ts +2 -0
- package/src/astro/generate.ts +90 -45
- package/src/astro/module-types.ts +1 -1
- package/src/astro/runtime-deps.ts +6 -6
- package/src/astro/templates.ts +87 -49
- 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 +14 -0
- 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/report.ts +54 -17
- package/src/audit/run.ts +1 -0
- package/src/audit/types.ts +18 -0
- package/src/audit/url.ts +37 -6
- package/src/blume-modules.d.ts +2 -2
- package/src/cli/build-failure.ts +50 -0
- package/src/cli/commands/audit.ts +8 -1
- package/src/cli/commands/build.ts +19 -5
- package/src/cli/commands/check.ts +2 -0
- package/src/cli/commands/doctor.ts +7 -5
- package/src/cli/commands/eval.ts +4 -10
- package/src/cli/commands/init.ts +15 -5
- package/src/cli/commands/migrate.ts +2 -2
- package/src/cli/commands/preview.ts +15 -0
- package/src/cli/commands/sync.ts +2 -0
- package/src/cli/commands/translate.ts +12 -9
- package/src/cli/commands/upgrade.ts +13 -2
- 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/required-secrets.ts +3 -3
- package/src/components/content/AccordionItem.astro +26 -22
- package/src/components/content/Badge.astro +2 -9
- package/src/components/content/Card.astro +2 -2
- package/src/components/content/Component.astro +9 -3
- 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/Tabs.astro +54 -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 +17 -0
- package/src/components/content/base-href.ts +18 -3
- package/src/components/content/inline-markdown.ts +27 -7
- package/src/components/copy-feedback.ts +36 -9
- package/src/components/islands/{AskAI.astro → Assistant.astro} +10 -10
- package/src/components/islands/{ask-ai.tsx → assistant.tsx} +39 -25
- package/src/components/islands/hooks.ts +21 -14
- package/src/components/islands/webmcp.ts +12 -8
- package/src/components/layout/Banner.astro +23 -4
- package/src/components/layout/Header.astro +32 -19
- package/src/components/layout/Logo.astro +5 -0
- package/src/components/layout/NavSelector.astro +6 -1
- package/src/components/layout/NavTabMenu.astro +133 -0
- package/src/components/layout/NavTree.astro +15 -6
- package/src/components/layout/NavTreeCache.astro +5 -2
- package/src/components/layout/NavTreeScript.astro +45 -5
- package/src/components/layout/PageLayout.astro +28 -14
- package/src/components/layout/Pagination.astro +7 -7
- package/src/components/layout/ReferenceLayout.astro +5 -2
- package/src/components/layout/RootLayout.astro +27 -14
- package/src/components/layout/Search.astro +18 -14
- package/src/components/layout/analytics-client.ts +3 -1
- package/src/components/layout/drawer-inert.ts +1 -1
- package/src/components/openapi/GraphqlType.astro +11 -3
- package/src/components/openapi/MessageComposer.astro +1 -1
- package/src/components/openapi/Operation.astro +21 -5
- package/src/components/openapi/PanelTabs.astro +4 -1
- package/src/components/openapi/Playground.astro +8 -2
- package/src/components/openapi/RequestPanel.astro +9 -5
- package/src/components/openapi/SchemaProperty.astro +11 -33
- package/src/components/openapi/SchemaTable.astro +27 -52
- package/src/components/openapi/description.ts +2 -2
- package/src/components/openapi/helpers.ts +90 -12
- package/src/components/openapi/message-composer.ts +8 -0
- package/src/components/openapi/message-model.ts +12 -2
- package/src/components/openapi/message.ts +4 -1
- package/src/components/openapi/operation-model.ts +40 -8
- package/src/components/openapi/panel.ts +29 -5
- package/src/components/openapi/playground-client.ts +37 -7
- package/src/components/openapi/playground-schema.ts +25 -6
- package/src/components/openapi/request.ts +2 -0
- package/src/components/openapi/schema-tree.ts +203 -0
- package/src/components/openapi/snippets.ts +30 -15
- package/src/components/openapi/validate-json.ts +1 -1
- package/src/core/base-path.ts +15 -0
- package/src/core/code-fences.ts +1 -1
- package/src/core/config-input.ts +23 -23
- package/src/core/config.ts +21 -7
- package/src/core/data.ts +3 -3
- package/src/core/diagnostics.ts +217 -30
- package/src/core/graph.ts +120 -11
- package/src/core/i18n-ui.ts +74 -11
- package/src/core/i18n.ts +13 -2
- package/src/core/links.ts +7 -4
- package/src/core/load-module.ts +20 -0
- package/src/core/locale-links.ts +17 -17
- package/src/core/manifest.ts +3 -2
- package/src/core/meta.ts +69 -6
- package/src/core/nav-diagnostics.ts +56 -1
- package/src/core/navigation.ts +245 -76
- package/src/core/ordering-prefix.ts +27 -0
- package/src/core/project-graph.ts +23 -1
- package/src/core/request-body.ts +1 -1
- package/src/core/safe-href.ts +53 -1
- package/src/core/safe-links.ts +11 -2
- package/src/core/schema.ts +95 -34
- package/src/core/server-features.ts +2 -2
- package/src/core/sources/assets.ts +83 -41
- package/src/core/sources/contentful-rich-text.ts +3 -2
- package/src/core/sources/contentful.ts +25 -12
- package/src/core/sources/filesystem.ts +5 -1
- package/src/core/sources/github-releases.ts +106 -5
- package/src/core/sources/lexical.ts +9 -4
- package/src/core/sources/lower.ts +79 -26
- package/src/core/sources/mdx-remote.ts +1 -0
- package/src/core/sources/normalize.ts +132 -30
- package/src/core/sources/notion.ts +26 -10
- package/src/core/sources/obsidian.ts +17 -3
- package/src/core/sources/payload.ts +1 -0
- package/src/core/sources/portable-text.ts +63 -44
- 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 +9 -4
- package/src/core/sources/strapi.ts +1 -0
- package/src/core/sources/types.ts +31 -0
- package/src/core/types.ts +9 -0
- package/src/core/ui-packs/ar.ts +17 -5
- package/src/core/ui-packs/bg.ts +17 -5
- package/src/core/ui-packs/bn.ts +17 -5
- package/src/core/ui-packs/ca.ts +17 -5
- package/src/core/ui-packs/cs.ts +17 -5
- package/src/core/ui-packs/da.ts +17 -5
- package/src/core/ui-packs/de.ts +17 -5
- package/src/core/ui-packs/el.ts +17 -5
- package/src/core/ui-packs/es.ts +17 -5
- package/src/core/ui-packs/fa.ts +17 -5
- package/src/core/ui-packs/fi.ts +17 -5
- package/src/core/ui-packs/fr.ts +17 -5
- package/src/core/ui-packs/he.ts +17 -5
- package/src/core/ui-packs/hi.ts +17 -5
- package/src/core/ui-packs/hr.ts +17 -5
- package/src/core/ui-packs/hu.ts +17 -5
- package/src/core/ui-packs/id.ts +17 -5
- package/src/core/ui-packs/it.ts +17 -5
- package/src/core/ui-packs/ja.ts +17 -5
- package/src/core/ui-packs/ko.ts +17 -5
- package/src/core/ui-packs/nl.ts +17 -5
- package/src/core/ui-packs/no.ts +17 -5
- package/src/core/ui-packs/pl.ts +17 -5
- package/src/core/ui-packs/pt-br.ts +17 -5
- package/src/core/ui-packs/pt.ts +17 -5
- package/src/core/ui-packs/ro.ts +17 -5
- package/src/core/ui-packs/ru.ts +17 -5
- package/src/core/ui-packs/sk.ts +17 -5
- package/src/core/ui-packs/sr.ts +17 -5
- package/src/core/ui-packs/sv.ts +17 -5
- package/src/core/ui-packs/th.ts +17 -5
- package/src/core/ui-packs/tr.ts +17 -5
- package/src/core/ui-packs/uk.ts +17 -5
- package/src/core/ui-packs/vi.ts +17 -5
- package/src/core/ui-packs/zh-tw.ts +17 -5
- package/src/core/ui-packs/zh.ts +17 -5
- package/src/core/unrecognized-keys.ts +1 -1
- package/src/core/version-cut.ts +17 -2
- 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 +25 -5
- package/src/deploy/cloudflare-negotiation.ts +23 -4
- package/src/deploy/headers.ts +67 -51
- package/src/deploy/node-headers.ts +148 -27
- package/src/deploy/platforms/cloudflare.ts +1 -0
- package/src/deploy/platforms/netlify.ts +82 -5
- package/src/deploy/platforms/node.ts +7 -5
- package/src/deploy/platforms/static.ts +1 -0
- package/src/deploy/platforms/types.ts +7 -0
- package/src/deploy/platforms/vercel.ts +9 -3
- package/src/deploy/redirects.ts +14 -3
- 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 +55 -8
- package/src/markdown/index.ts +6 -3
- package/src/markdown/relative-links.ts +3 -23
- package/src/markdown/route-snapshot.ts +37 -0
- package/src/og/card.ts +20 -4
- package/src/og/derive.ts +145 -4
- package/src/openapi/graphql-build.ts +28 -2
- package/src/openapi/model.ts +77 -13
- package/src/openapi/render-mdx.ts +10 -1
- package/src/registry/eject.ts +135 -37
- package/src/search/documents.ts +24 -3
- package/src/search/orama-index.ts +1 -1
- package/src/search/sync/algolia.ts +36 -2
- 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 +11 -3
- package/src/translate/report.ts +1 -1
- package/src/translate/run.ts +11 -5
- package/src/translate/validate.ts +10 -1
- package/src/translate/work-list.ts +36 -3
- package/src/upgrade/upgrade.ts +37 -5
- package/dist/cli/chunk-1w8dp3qb.js.map +0 -10
- package/dist/cli/chunk-5a2z0198.js.map +0 -10
- package/dist/cli/chunk-6k8vp3ta.js.map +0 -19
- package/dist/cli/chunk-79njf86q.js.map +0 -11
- package/dist/cli/chunk-a9kptbw5.js.map +0 -14
- package/dist/cli/chunk-abh8yjkn.js +0 -31
- package/dist/cli/chunk-abh8yjkn.js.map +0 -10
- package/dist/cli/chunk-bctazmbk.js.map +0 -10
- package/dist/cli/chunk-beat36xx.js.map +0 -10
- package/dist/cli/chunk-d1tadaw7.js.map +0 -10
- package/dist/cli/chunk-fa25z98p.js.map +0 -11
- package/dist/cli/chunk-j8mw0za6.js.map +0 -35
- package/dist/cli/chunk-mnqj32sj.js.map +0 -12
- package/dist/cli/chunk-mwt1k8n7.js.map +0 -10
- package/dist/cli/chunk-nk3ts2xk.js +0 -51
- package/dist/cli/chunk-nk3ts2xk.js.map +0 -10
- package/dist/cli/chunk-sqn5t4q0.js.map +0 -10
- package/dist/cli/chunk-tzne8qfq.js.map +0 -10
- package/dist/cli/chunk-xaz13gwg.js.map +0 -182
- package/dist/cli/chunk-z01ze5c1.js.map +0 -10
- package/dist/cli/chunk-z1f5arsg.js.map +0 -36
- package/dist/cli/chunk-zg2gtj10.js.map +0 -35
- /package/dist/cli/{chunk-f2972sbt.js.map → chunk-00gs3wqs.js.map} +0 -0
- /package/dist/cli/{chunk-7ez8ny0t.js.map → chunk-bbnwccaz.js.map} +0 -0
- /package/dist/cli/{chunk-zxccj738.js.map → chunk-esh98wmb.js.map} +0 -0
- /package/dist/cli/{chunk-88cpgt6h.js.map → chunk-fsmrqk8a.js.map} +0 -0
- /package/dist/cli/{chunk-b5aj94ah.js.map → chunk-g698a744.js.map} +0 -0
- /package/dist/cli/{chunk-41za066z.js.map → chunk-h2ez8dzb.js.map} +0 -0
- /package/dist/cli/{chunk-by2290sx.js.map → chunk-h7k3nq3v.js.map} +0 -0
- /package/dist/cli/{chunk-y3e45rc8.js.map → chunk-n1yg3tj3.js.map} +0 -0
- /package/dist/cli/{chunk-f7t03s3g.js.map → chunk-wdrt2k2v.js.map} +0 -0
package/src/registry/eject.ts
CHANGED
|
@@ -23,6 +23,10 @@ import {
|
|
|
23
23
|
detectNeedsReact,
|
|
24
24
|
detectUsesMath,
|
|
25
25
|
languageIconCssFor,
|
|
26
|
+
PLAYGROUND_PROXY_ENTRY,
|
|
27
|
+
playgroundProxyPattern,
|
|
28
|
+
proxyAllowlistWarnings,
|
|
29
|
+
specOrigins,
|
|
26
30
|
} from "../astro/generate.ts";
|
|
27
31
|
import { discoverIslands } from "../astro/islands.ts";
|
|
28
32
|
import { customOgRoutes, discoverPages, routeIsTaken } from "../astro/pages.ts";
|
|
@@ -32,6 +36,7 @@ import {
|
|
|
32
36
|
astroConfigTemplate,
|
|
33
37
|
catchAllPageTemplate,
|
|
34
38
|
changelogIndexTemplate,
|
|
39
|
+
contentAssetsEndpointTemplate,
|
|
35
40
|
contentConfigTemplate,
|
|
36
41
|
exampleMapTemplate,
|
|
37
42
|
exampleSlug,
|
|
@@ -43,6 +48,7 @@ import {
|
|
|
43
48
|
mixedbreadSearchEndpointTemplate,
|
|
44
49
|
notFoundPageTemplate,
|
|
45
50
|
ogEndpointTemplate,
|
|
51
|
+
playgroundProxyTemplate,
|
|
46
52
|
rawMarkdownEndpointTemplate,
|
|
47
53
|
rssEndpointTemplate,
|
|
48
54
|
runtimeDependencies,
|
|
@@ -51,6 +57,7 @@ import {
|
|
|
51
57
|
searchEndpointTemplate,
|
|
52
58
|
staticJsonEndpointTemplate,
|
|
53
59
|
} from "../astro/templates.ts";
|
|
60
|
+
import { collectContentAssets } from "../core/content-assets.ts";
|
|
54
61
|
import { buildIncludeGraph } from "../core/includes.ts";
|
|
55
62
|
import { packageRoot } from "../core/package-root.ts";
|
|
56
63
|
import { scanProject } from "../core/project-graph.ts";
|
|
@@ -62,7 +69,10 @@ import {
|
|
|
62
69
|
import type { ProjectContext } from "../core/types.ts";
|
|
63
70
|
import { buildRssFeeds, renderRssFeed } from "../deploy/rss.ts";
|
|
64
71
|
import type { OpenApiData } from "../openapi/model.ts";
|
|
65
|
-
import {
|
|
72
|
+
import {
|
|
73
|
+
hasScalarReferences,
|
|
74
|
+
needsPlaygroundProxy,
|
|
75
|
+
} from "../openapi/references.ts";
|
|
66
76
|
import { buildReferenceFiles } from "../openapi/scalar.ts";
|
|
67
77
|
import { isOpenApiSource } from "../openapi/source.ts";
|
|
68
78
|
import { buildSearchDocuments } from "../search/documents.ts";
|
|
@@ -116,16 +126,16 @@ const ejectOpenApiData = (project: BlumeProject): OpenApiData => {
|
|
|
116
126
|
};
|
|
117
127
|
|
|
118
128
|
/**
|
|
119
|
-
* The
|
|
120
|
-
* its grounding snapshot. Empty when
|
|
129
|
+
* The assistant endpoint plus, unless the backend runs its own retrieval (Inkeep),
|
|
130
|
+
* its grounding snapshot. Empty when the assistant is disabled.
|
|
121
131
|
*/
|
|
122
132
|
const askFiles = async (
|
|
123
133
|
project: BlumeProject,
|
|
124
134
|
srcDir: string,
|
|
125
135
|
genDir: string
|
|
126
136
|
): Promise<{ content: string; path: string }[]> => {
|
|
127
|
-
const {
|
|
128
|
-
if (!(
|
|
137
|
+
const { assistant } = project.config.ai;
|
|
138
|
+
if (!(assistant?.enabled && !assistant.endpoint)) {
|
|
129
139
|
const endpointPath = join(srcDir, "pages", "api", "ask.ts");
|
|
130
140
|
if (existsSync(endpointPath)) {
|
|
131
141
|
const content = await readFile(endpointPath, "utf-8");
|
|
@@ -136,13 +146,13 @@ const askFiles = async (
|
|
|
136
146
|
await rm(join(genDir, "ask-data.json"), { force: true });
|
|
137
147
|
return [];
|
|
138
148
|
}
|
|
139
|
-
const backend = resolveAskBackend(
|
|
149
|
+
const backend = resolveAskBackend(assistant.provider);
|
|
140
150
|
const files = [
|
|
141
151
|
{
|
|
142
152
|
content: askEndpointTemplate(backend, {
|
|
143
|
-
cors:
|
|
144
|
-
instructions:
|
|
145
|
-
retrieval:
|
|
153
|
+
cors: assistant.cors,
|
|
154
|
+
instructions: assistant.instructions,
|
|
155
|
+
retrieval: assistant.retrieval,
|
|
146
156
|
}),
|
|
147
157
|
path: join(srcDir, "pages", "api", "ask.ts"),
|
|
148
158
|
},
|
|
@@ -190,6 +200,43 @@ const mcpDiscoveryPages = (
|
|
|
190
200
|
]
|
|
191
201
|
: [];
|
|
192
202
|
|
|
203
|
+
/**
|
|
204
|
+
* The playground's built-in CORS proxy route (`playground: { proxy: true }`),
|
|
205
|
+
* mirroring `planPlaygroundProxy` in generate.ts: injected like the MCP
|
|
206
|
+
* discovery routes, because Astro treats a `_`-prefixed page file as private,
|
|
207
|
+
* and mounted under `basePath` at the URL the playground sends to. Empty when
|
|
208
|
+
* no reference routes its playground through it.
|
|
209
|
+
*/
|
|
210
|
+
const playgroundProxyPages = (
|
|
211
|
+
config: BlumeProject["config"]
|
|
212
|
+
): { entrypoint: string; pattern: string }[] =>
|
|
213
|
+
needsPlaygroundProxy(config)
|
|
214
|
+
? [
|
|
215
|
+
{
|
|
216
|
+
entrypoint: toPosix(join("src", PLAYGROUND_PROXY_ENTRY)),
|
|
217
|
+
pattern: playgroundProxyPattern(config),
|
|
218
|
+
},
|
|
219
|
+
]
|
|
220
|
+
: [];
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* The proxy endpoint behind {@link playgroundProxyPages}, with the origin
|
|
224
|
+
* allowlist baked in from the same parsed specs the reference pages render.
|
|
225
|
+
*/
|
|
226
|
+
const playgroundProxyFiles = (
|
|
227
|
+
config: BlumeProject["config"],
|
|
228
|
+
openApiData: OpenApiData,
|
|
229
|
+
srcDir: string
|
|
230
|
+
): { content: string; path: string }[] =>
|
|
231
|
+
needsPlaygroundProxy(config)
|
|
232
|
+
? [
|
|
233
|
+
{
|
|
234
|
+
content: playgroundProxyTemplate(specOrigins(openApiData)),
|
|
235
|
+
path: join(srcDir, PLAYGROUND_PROXY_ENTRY),
|
|
236
|
+
},
|
|
237
|
+
]
|
|
238
|
+
: [];
|
|
239
|
+
|
|
193
240
|
/**
|
|
194
241
|
* The MCP data snapshot, server endpoint, and `.well-known` discovery
|
|
195
242
|
* documents, mirroring `writeMcpFiles` in generate.ts. Empty when the server
|
|
@@ -265,6 +312,40 @@ const changelogFiles = (
|
|
|
265
312
|
];
|
|
266
313
|
};
|
|
267
314
|
|
|
315
|
+
/**
|
|
316
|
+
* The colocated-image map and the `/blume-assets/[...asset]` endpoint that
|
|
317
|
+
* serves it, mirroring generate.ts: the ejected raw Markdown points
|
|
318
|
+
* `` at `/blume-assets/content/…`, and the config aliases
|
|
319
|
+
* `blume:content-assets` to the map. Each original's path is written relative
|
|
320
|
+
* to the project root (the directory `astro dev`/`astro build` run from), so
|
|
321
|
+
* the ejected app builds from any checkout. Remote-source images need no
|
|
322
|
+
* endpoint: eject copies them into the project's own `public/blume-assets`.
|
|
323
|
+
*/
|
|
324
|
+
const contentAssetFiles = async (
|
|
325
|
+
project: BlumeProject,
|
|
326
|
+
root: string,
|
|
327
|
+
srcDir: string,
|
|
328
|
+
genDir: string
|
|
329
|
+
): Promise<{ content: string; path: string }[]> => {
|
|
330
|
+
const assets = await collectContentAssets(project);
|
|
331
|
+
const portable = Object.fromEntries(
|
|
332
|
+
Object.entries(assets).map(([param, abs]) => [
|
|
333
|
+
param,
|
|
334
|
+
toPosix(relative(root, abs)),
|
|
335
|
+
])
|
|
336
|
+
);
|
|
337
|
+
return [
|
|
338
|
+
{
|
|
339
|
+
content: `${JSON.stringify(portable)}\n`,
|
|
340
|
+
path: join(genDir, "content-assets.json"),
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
content: contentAssetsEndpointTemplate(null),
|
|
344
|
+
path: join(srcDir, "pages", "blume-assets", "[...asset].ts"),
|
|
345
|
+
},
|
|
346
|
+
];
|
|
347
|
+
};
|
|
348
|
+
|
|
268
349
|
/** Contents of the configured `examples.css`, or `""` when unset/absent. */
|
|
269
350
|
/**
|
|
270
351
|
* Read a user stylesheet that eject inlines into a generated entry under
|
|
@@ -309,29 +390,33 @@ const examplesPreviewFiles = (
|
|
|
309
390
|
|
|
310
391
|
/**
|
|
311
392
|
* The packages the ejected app imports by bare name: Astro, Tailwind's Vite
|
|
312
|
-
* plugin,
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
*
|
|
393
|
+
* plugin, Tailwind and its typography plugin (which the generated `app.css`
|
|
394
|
+
* and `examples.css` import by name), `blume` itself, the integrations the
|
|
395
|
+
* config wires in, whatever the configured adapters declare, and React when
|
|
396
|
+
* islands, examples, or the assistant render with it (Blume ships React, so
|
|
397
|
+
* projects rarely list it). They have to be the project's own dependencies
|
|
398
|
+
* after eject — under a strict linker such as pnpm nothing else makes them
|
|
399
|
+
* resolvable, and `astro build` fails. The hidden runtime reaches the same
|
|
400
|
+
* packages through its `node_modules` junction into Blume's own, so only
|
|
401
|
+
* eject declares the ones its generated files import directly: the AI SDK the
|
|
402
|
+
* assistant route streams through (unless `ai.assistant.endpoint` points
|
|
403
|
+
* elsewhere, when no route is written) and the EPUB generator's browser bundle
|
|
404
|
+
* `features.ts` loads (only named there when `export.epub` is on).
|
|
322
405
|
*/
|
|
323
406
|
const ejectDependencies = (
|
|
324
407
|
options: Parameters<typeof runtimeDependencies>[0]
|
|
325
408
|
): string[] => {
|
|
326
|
-
const {
|
|
409
|
+
const { assistant } = options.config.ai;
|
|
327
410
|
return [
|
|
328
411
|
...new Set([
|
|
329
412
|
"astro",
|
|
330
413
|
"@tailwindcss/vite",
|
|
414
|
+
"tailwindcss",
|
|
415
|
+
"@tailwindcss/typography",
|
|
331
416
|
"blume",
|
|
332
417
|
...runtimeDependencies(options),
|
|
333
418
|
...(options.needsReact ? ["react", "react-dom"] : []),
|
|
334
|
-
...(
|
|
419
|
+
...(assistant?.enabled && !assistant.endpoint ? ["ai"] : []),
|
|
335
420
|
...(options.config.export.epub ? ["epub-gen-memory"] : []),
|
|
336
421
|
]),
|
|
337
422
|
];
|
|
@@ -364,7 +449,7 @@ export const eject = async (
|
|
|
364
449
|
|
|
365
450
|
const srcDir = join(root, "src");
|
|
366
451
|
const genDir = join(srcDir, "generated");
|
|
367
|
-
const
|
|
452
|
+
const assistantEnabled = config.ai.assistant?.enabled ?? false;
|
|
368
453
|
const exportPdf = config.export.pdf;
|
|
369
454
|
const exportEpub = config.export.epub;
|
|
370
455
|
|
|
@@ -402,13 +487,14 @@ export const eject = async (
|
|
|
402
487
|
relativeTo: genDir,
|
|
403
488
|
});
|
|
404
489
|
// Island/example/override frameworks drive which Astro renderers the ejected
|
|
405
|
-
// config wires in; React also switches on for project `.tsx`/`.jsx` and
|
|
490
|
+
// config wires in; React also switches on for project `.tsx`/`.jsx` and the assistant.
|
|
406
491
|
const frameworks = new Set<string>([
|
|
407
492
|
...islands.islands.map((island) => island.framework),
|
|
408
493
|
...examples.examples.map((example) => example.framework),
|
|
409
494
|
...slotPlan.frameworks,
|
|
410
495
|
]);
|
|
411
|
-
const needsReact =
|
|
496
|
+
const needsReact =
|
|
497
|
+
needsReactRaw || assistantEnabled || frameworks.has("react");
|
|
412
498
|
const needsVue = frameworks.has("vue");
|
|
413
499
|
const needsSvelte = frameworks.has("svelte");
|
|
414
500
|
|
|
@@ -438,7 +524,9 @@ export const eject = async (
|
|
|
438
524
|
pattern: page.pattern,
|
|
439
525
|
})),
|
|
440
526
|
...mcpDiscoveryPages(project, pages),
|
|
527
|
+
...playgroundProxyPages(config),
|
|
441
528
|
];
|
|
529
|
+
const openApiData = ejectOpenApiData(project);
|
|
442
530
|
|
|
443
531
|
// Non-filesystem sources eject their materialized MDX into `<root>/blume-staged`
|
|
444
532
|
// (a dedicated dir so it never clashes with a content root literally named
|
|
@@ -550,11 +638,11 @@ export const eject = async (
|
|
|
550
638
|
// The header's Ask trigger behind the `blume:ask` alias. Always written — it
|
|
551
639
|
// renders nothing when Ask is off — so the alias always resolves.
|
|
552
640
|
{
|
|
553
|
-
content: askComponentTemplate(
|
|
641
|
+
content: askComponentTemplate(assistantEnabled),
|
|
554
642
|
path: join(genDir, "Ask.astro"),
|
|
555
643
|
},
|
|
556
644
|
{
|
|
557
|
-
content: `${JSON.stringify(
|
|
645
|
+
content: `${JSON.stringify(openApiData)}\n`,
|
|
558
646
|
path: join(genDir, "openapi.json"),
|
|
559
647
|
},
|
|
560
648
|
{
|
|
@@ -580,30 +668,38 @@ export const eject = async (
|
|
|
580
668
|
},
|
|
581
669
|
];
|
|
582
670
|
|
|
583
|
-
|
|
671
|
+
files.push(...(await contentAssetFiles(project, root, srcDir, genDir)));
|
|
672
|
+
|
|
673
|
+
if (assistantEnabled) {
|
|
584
674
|
files.push(...(await askFiles(project, srcDir, genDir)));
|
|
585
675
|
}
|
|
586
676
|
|
|
677
|
+
// The `/changelog` index, mirrored from the generated runtime (`[]` when
|
|
678
|
+
// the project has no changelog). The OG endpoint below adds its card.
|
|
679
|
+
const changelog = changelogFiles(project, pages, srcDir, {
|
|
680
|
+
exportEpub,
|
|
681
|
+
exportPdf,
|
|
682
|
+
needsReact,
|
|
683
|
+
staged: hasStaged,
|
|
684
|
+
});
|
|
685
|
+
|
|
587
686
|
if (config.seo.og.enabled) {
|
|
588
687
|
files.push({
|
|
589
688
|
content: ogEndpointTemplate(
|
|
590
689
|
customOgRoutes(pages, config.title, config.seo.og.titles),
|
|
591
|
-
{ pageDescriptions: config.seo.og.description !== false }
|
|
690
|
+
{ pageDescriptions: config.seo.og.description !== false },
|
|
691
|
+
changelog.length > 0
|
|
592
692
|
),
|
|
593
693
|
path: join(srcDir, "pages", "og", "[...slug].png.ts"),
|
|
594
694
|
});
|
|
595
695
|
}
|
|
596
696
|
|
|
597
|
-
// The hosted MCP server and the
|
|
598
|
-
// generated runtime (
|
|
697
|
+
// The hosted MCP server and the playground's built-in proxy, mirrored from
|
|
698
|
+
// the generated runtime (`[]` when each is off).
|
|
599
699
|
files.push(
|
|
600
700
|
...(await mcpFiles(project, pages, srcDir, genDir)),
|
|
601
|
-
...
|
|
602
|
-
|
|
603
|
-
exportPdf,
|
|
604
|
-
needsReact,
|
|
605
|
-
staged: hasStaged,
|
|
606
|
-
})
|
|
701
|
+
...changelog,
|
|
702
|
+
...playgroundProxyFiles(config, openApiData, srcDir)
|
|
607
703
|
);
|
|
608
704
|
|
|
609
705
|
// Default 404 page, unless the project already owns `/404` (a custom
|
|
@@ -672,7 +768,9 @@ export const eject = async (
|
|
|
672
768
|
// the ejected app keeps its reference routes — including the warnings (a
|
|
673
769
|
// missing spec file, a route collision), which the caller surfaces exactly
|
|
674
770
|
// like the generated-runtime path does.
|
|
675
|
-
|
|
771
|
+
// A proxied spec with no origin to allow gets the generated runtime's
|
|
772
|
+
// diagnostic too.
|
|
773
|
+
const warnings: string[] = proxyAllowlistWarnings(config, openApiData);
|
|
676
774
|
if (hasScalarReferences(config)) {
|
|
677
775
|
const references = await buildReferenceFiles({
|
|
678
776
|
config,
|
package/src/search/documents.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { applyAudienceVisibility } from "../ai/visibility.ts";
|
|
|
16
16
|
import type { VisibilityAudience } from "../ai/visibility.ts";
|
|
17
17
|
import matter from "../core/frontmatter.ts";
|
|
18
18
|
import { parseHeadingMarkers } from "../core/heading-markers.ts";
|
|
19
|
+
import { EN_UI, resolveUIStrings } from "../core/i18n-ui.ts";
|
|
19
20
|
import { contentIndexable } from "../core/manifest.ts";
|
|
20
21
|
import type { BlumeProject } from "../core/project-graph.ts";
|
|
21
22
|
import { HTML_COMMENT } from "../core/sources/normalize.ts";
|
|
@@ -149,7 +150,7 @@ const parseMarkdown = (
|
|
|
149
150
|
format === "mdx" ? parseMdx(markdown) : markdownToMdast(markdown, MD_PARSE);
|
|
150
151
|
|
|
151
152
|
// Fenced code is excluded from the plain index by default (ranking noise) —
|
|
152
|
-
// the "markdown" extraction keeps it for
|
|
153
|
+
// the "markdown" extraction keeps it for assistant grounding. Code-heavy docs opt
|
|
153
154
|
// in via `search.indexing.includeCodeBlocks`, which indexes the fence body and
|
|
154
155
|
// its rendered title (```ts blume.config.ts) but never the fence markers,
|
|
155
156
|
// language, or other meta keywords.
|
|
@@ -383,7 +384,7 @@ const pageBody = async (
|
|
|
383
384
|
*
|
|
384
385
|
* `content` selects the extraction: `"plain"` (default) strips Markdown to bare
|
|
385
386
|
* searchable text; `"markdown"` keeps the body's Markdown — code blocks, lists,
|
|
386
|
-
* headings — for
|
|
387
|
+
* headings — for assistant grounding, where fenced examples are often the answer
|
|
387
388
|
* and stripping them makes the model unable to cite content the docs do contain.
|
|
388
389
|
* The `"markdown"` body is agent-facing, so components are downleveled with the
|
|
389
390
|
* same serializers the `.md` mirror, llms-full.txt and MCP `get_page` use: a
|
|
@@ -447,6 +448,26 @@ export const buildSearchDocuments = async (
|
|
|
447
448
|
// otherwise.
|
|
448
449
|
const components = projectComponentSerializers(project);
|
|
449
450
|
|
|
451
|
+
// A page in no sidebar group falls under the "Docs" section, in its own
|
|
452
|
+
// language: the label is a UI string, resolved once per locale.
|
|
453
|
+
const { i18n } = project.config;
|
|
454
|
+
const docsSections = new Map<string, string>();
|
|
455
|
+
const docsSection = (locale: string): string => {
|
|
456
|
+
let label = docsSections.get(locale);
|
|
457
|
+
if (label === undefined) {
|
|
458
|
+
label = (
|
|
459
|
+
i18n
|
|
460
|
+
? resolveUIStrings(locale, {
|
|
461
|
+
defaultLocale: i18n.defaultLocale,
|
|
462
|
+
overrides: i18n.ui,
|
|
463
|
+
})
|
|
464
|
+
: EN_UI
|
|
465
|
+
).search.docs;
|
|
466
|
+
docsSections.set(locale, label);
|
|
467
|
+
}
|
|
468
|
+
return label;
|
|
469
|
+
};
|
|
470
|
+
|
|
450
471
|
return await Promise.all(
|
|
451
472
|
indexable.map(async (route) => {
|
|
452
473
|
const page = pageById.get(route.id);
|
|
@@ -461,7 +482,7 @@ export const buildSearchDocuments = async (
|
|
|
461
482
|
description: page?.description ?? "",
|
|
462
483
|
locale: route.locale,
|
|
463
484
|
route: route.path,
|
|
464
|
-
section: crumb?.section ||
|
|
485
|
+
section: crumb?.section || docsSection(route.locale),
|
|
465
486
|
tags: tags && tags.length > 0 ? tags : undefined,
|
|
466
487
|
title: route.title,
|
|
467
488
|
version: route.version,
|
|
@@ -298,7 +298,7 @@ const segmentingTokenizer = (locale?: string): Tokenizer | undefined => {
|
|
|
298
298
|
|
|
299
299
|
/**
|
|
300
300
|
* Build an in-memory Orama full-text index from search documents. Shared by the
|
|
301
|
-
* Orama client loader (browser), the MCP server, and
|
|
301
|
+
* Orama client loader (browser), the MCP server, and assistant grounding (Node),
|
|
302
302
|
* so ranking is identical wherever docs are queried. `locale` — the site's
|
|
303
303
|
* `i18n.defaultLocale` — swaps in a word-segmenting tokenizer for every
|
|
304
304
|
* non-Latin script, all of which Orama's default tokenizer reduces to zero
|
|
@@ -7,6 +7,19 @@ import type { SearchRecord } from "../documents.ts";
|
|
|
7
7
|
/** The adapter options the sync reads (the public `apiKey` is never used). */
|
|
8
8
|
export type AlgoliaSyncConfig = Pick<AlgoliaOptions, "appId" | "indexName">;
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* The record attributes the search dialog filters on (`facetFilters` on
|
|
12
|
+
* `locale:<code>` and `version:<id>`). In Algolia a facet filter on an
|
|
13
|
+
* attribute the index doesn't declare for faceting matches nothing — an i18n
|
|
14
|
+
* or versioned site would get no hits — so the sync declares them,
|
|
15
|
+
* filter-only.
|
|
16
|
+
*/
|
|
17
|
+
const FILTER_ATTRIBUTES = ["locale", "version"] as const;
|
|
18
|
+
|
|
19
|
+
/** A faceting declaration's attribute: `filterOnly(locale)` → `locale`. */
|
|
20
|
+
const facetAttribute = (declaration: string): string =>
|
|
21
|
+
declaration.replace(/^\w+\((?<name>.*)\)$/u, "$<name>");
|
|
22
|
+
|
|
10
23
|
/**
|
|
11
24
|
* Upload the search records to Algolia. Uses the admin key from
|
|
12
25
|
* `ALGOLIA_ADMIN_API_KEY` (never the adapter options, which hold only the
|
|
@@ -14,7 +27,9 @@ export type AlgoliaSyncConfig = Pick<AlgoliaOptions, "appId" | "indexName">;
|
|
|
14
27
|
*
|
|
15
28
|
* Uses `replaceAllObjects`, which atomically replaces the index contents, so
|
|
16
29
|
* pages deleted or renamed since the last sync don't linger as stale search
|
|
17
|
-
* hits that 404 when clicked.
|
|
30
|
+
* hits that 404 when clicked. Then adds `filterOnly(locale)` and
|
|
31
|
+
* `filterOnly(version)` to the index's `attributesForFaceting`, keeping any
|
|
32
|
+
* the site declared itself, so the dialog's locale and version filters match.
|
|
18
33
|
*/
|
|
19
34
|
export const syncAlgolia = async (
|
|
20
35
|
records: SearchRecord[],
|
|
@@ -28,8 +43,27 @@ export const syncAlgolia = async (
|
|
|
28
43
|
// `core/node-require.ts`).
|
|
29
44
|
const { algoliasearch }: typeof AlgoliaSdk = nodeRequire("algoliasearch");
|
|
30
45
|
const client = algoliasearch(config.appId, adminKey);
|
|
46
|
+
const { indexName } = config;
|
|
31
47
|
await client.replaceAllObjects({
|
|
32
|
-
indexName
|
|
48
|
+
indexName,
|
|
33
49
|
objects: records.map((record) => ({ ...record, objectID: record._id })),
|
|
34
50
|
});
|
|
51
|
+
const { attributesForFaceting = [] } = await client.getSettings({
|
|
52
|
+
indexName,
|
|
53
|
+
});
|
|
54
|
+
const declared = new Set(attributesForFaceting.map(facetAttribute));
|
|
55
|
+
const missing = FILTER_ATTRIBUTES.filter((name) => !declared.has(name));
|
|
56
|
+
if (missing.length === 0) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const { taskID } = await client.setSettings({
|
|
60
|
+
indexName,
|
|
61
|
+
indexSettings: {
|
|
62
|
+
attributesForFaceting: [
|
|
63
|
+
...attributesForFaceting,
|
|
64
|
+
...missing.map((name) => `filterOnly(${name})`),
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
await client.waitForTask({ indexName, taskID });
|
|
35
69
|
};
|
package/src/sources/registry.ts
CHANGED
|
@@ -165,6 +165,11 @@ const sourceEntrySchema = z
|
|
|
165
165
|
.custom<SourceAdapterInput>(
|
|
166
166
|
(value) => isObjectLike(value) && "kind" in value,
|
|
167
167
|
{
|
|
168
|
+
// `z.custom` marks its failure as aborting by default, which makes Zod
|
|
169
|
+
// skip even the `content` refinement that opts in to running after a
|
|
170
|
+
// failed field (the shorthand beside `sources`). The pipe below stops
|
|
171
|
+
// at this entry either way.
|
|
172
|
+
abort: false,
|
|
168
173
|
error: (issue) => {
|
|
169
174
|
const legacy = legacyEntrySchema.safeParse(issue.input);
|
|
170
175
|
return entryHint(legacy.success ? legacy.data.type : undefined);
|
package/src/theme/entry.ts
CHANGED
|
@@ -268,7 +268,7 @@ ${THEME_MAPPING}
|
|
|
268
268
|
@apply block rounded-[0.65rem] px-2.5 py-1.5 text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=page]:bg-muted aria-[current=page]:font-medium aria-[current=page]:text-foreground;
|
|
269
269
|
}
|
|
270
270
|
@utility blume-nav-drill {
|
|
271
|
-
@apply flex w-full items-center gap-2 rounded-[0.65rem] px-2.5 py-1.5 text-
|
|
271
|
+
@apply flex w-full items-center gap-2 rounded-[0.65rem] px-2.5 py-1.5 text-start font-medium text-foreground text-sm transition-colors hover:bg-muted;
|
|
272
272
|
}
|
|
273
273
|
@utility blume-nav-summary {
|
|
274
274
|
@apply flex cursor-pointer list-none items-center gap-1.5 rounded-[0.65rem] px-2.5 py-1.5 text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground [&::-webkit-details-marker]:hidden;
|
|
@@ -884,11 +884,18 @@ pre:has(.line.focused):hover .line:not(.focused) {
|
|
|
884
884
|
}
|
|
885
885
|
|
|
886
886
|
/* Print / "Save as PDF" (the page-actions Export → PDF runs window.print()).
|
|
887
|
-
Strip the surrounding chrome so the printout is just the article.
|
|
887
|
+
Strip the surrounding chrome so the printout is just the article. Scoped to
|
|
888
|
+
the chrome itself, never bare \`header\`/\`aside\`: content renders those too
|
|
889
|
+
(every Callout and Panel is an <aside>, every changelog Update has a
|
|
890
|
+
<header>), and they belong in the printout. \`body > header\` keeps a
|
|
891
|
+
\`layout.Header\` override out as well. */
|
|
888
892
|
@media print {
|
|
889
893
|
[data-blume-banner],
|
|
890
|
-
header,
|
|
891
|
-
|
|
894
|
+
[data-blume-header],
|
|
895
|
+
body > header,
|
|
896
|
+
[data-blume-nav-drawer],
|
|
897
|
+
[data-blume-toc],
|
|
898
|
+
[data-blume-assistant-panel],
|
|
892
899
|
[data-blume-page-actions],
|
|
893
900
|
#blume-content > nav,
|
|
894
901
|
#blume-content > details {
|
package/src/translate/agents.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentKind } from "../audit/agent.ts";
|
|
2
|
-
import { DISALLOWED_TOOLS } from "../eval/agents.ts";
|
|
2
|
+
import { CODEX_LOCKDOWN, DISALLOWED_TOOLS } from "../eval/agents.ts";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* argv builders for the translator role. The subprocess machinery
|
|
@@ -28,6 +28,10 @@ const claudeArgs = (): string[] => [
|
|
|
28
28
|
"1",
|
|
29
29
|
];
|
|
30
30
|
|
|
31
|
+
// `--sandbox read-only` alone still lets codex's shell read any file the user
|
|
32
|
+
// can, and the source page being translated is untrusted input (a remote
|
|
33
|
+
// contributor's prose), so the translator gets the same lockdown as an eval
|
|
34
|
+
// run: no shell, exec, or local-image tool, and no inherited environment.
|
|
31
35
|
const codexArgs = (lastMessagePath: string): string[] => [
|
|
32
36
|
"exec",
|
|
33
37
|
"--skip-git-repo-check",
|
|
@@ -35,6 +39,7 @@ const codexArgs = (lastMessagePath: string): string[] => [
|
|
|
35
39
|
"--ephemeral",
|
|
36
40
|
"--sandbox",
|
|
37
41
|
"read-only",
|
|
42
|
+
...CODEX_LOCKDOWN,
|
|
38
43
|
"--output-last-message",
|
|
39
44
|
lastMessagePath,
|
|
40
45
|
"-",
|
package/src/translate/ledger.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { readFile } from "node:fs/promises";
|
|
|
4
4
|
import { join } from "pathe";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
|
|
7
|
+
import { BlumeError } from "../core/diagnostics.ts";
|
|
7
8
|
import { writeTextAtomic } from "../core/fs-atomic.ts";
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -39,21 +40,34 @@ export const emptyLedger = (): TranslationLedger => ({
|
|
|
39
40
|
* every locale's stamp. sha256-16 like the audit snapshot's content hash.
|
|
40
41
|
* Kept as its own function rather than sharing core's `hashText`: ledger
|
|
41
42
|
* stamps persist in user repos, so this hash must never change shape when an
|
|
42
|
-
* ephemeral cache hash does.
|
|
43
|
+
* ephemeral cache hash does. CRLF line endings are normalized to LF first, so
|
|
44
|
+
* a Windows checkout (`core.autocrlf`) hashes the same as everyone else's —
|
|
45
|
+
* LF text hashes exactly as it always has.
|
|
43
46
|
*/
|
|
44
47
|
export const hashSource = (text: string): string =>
|
|
45
|
-
createHash("sha256")
|
|
48
|
+
createHash("sha256")
|
|
49
|
+
.update(text.replaceAll("\r\n", "\n"))
|
|
50
|
+
.digest("hex")
|
|
51
|
+
.slice(0, 16);
|
|
52
|
+
|
|
53
|
+
/** A git merge-conflict marker line: `<<<<<<< ours`, `=======`, `>>>>>>> theirs`. */
|
|
54
|
+
const CONFLICT_MARKER = /^(?:<{7}|={7}|>{7})(?:[ \r]|$)/mu;
|
|
46
55
|
|
|
47
56
|
/**
|
|
48
57
|
* Read the ledger at `root`, tolerantly: a missing file, unparseable JSON, or
|
|
49
58
|
* an unknown shape/version all resolve to an empty ledger rather than an error
|
|
50
59
|
* (same posture as the dev lock's `parseLock`) — the worst outcome of a
|
|
51
60
|
* corrupt ledger is retranslating files that were already up to date.
|
|
61
|
+
*
|
|
62
|
+
* Unresolved merge-conflict markers are the exception, and an error: read as
|
|
63
|
+
* empty, every existing translation would be adopted as current, so
|
|
64
|
+
* `--check` would pass over translations that are out of date.
|
|
52
65
|
*/
|
|
53
66
|
export const readLedger = async (root: string): Promise<TranslationLedger> => {
|
|
67
|
+
const path = join(root, LEDGER_FILE);
|
|
54
68
|
let raw: string;
|
|
55
69
|
try {
|
|
56
|
-
raw = await readFile(
|
|
70
|
+
raw = await readFile(path, "utf-8");
|
|
57
71
|
} catch {
|
|
58
72
|
return emptyLedger();
|
|
59
73
|
}
|
|
@@ -61,6 +75,15 @@ export const readLedger = async (root: string): Promise<TranslationLedger> => {
|
|
|
61
75
|
try {
|
|
62
76
|
data = JSON.parse(raw);
|
|
63
77
|
} catch {
|
|
78
|
+
if (CONFLICT_MARKER.test(raw)) {
|
|
79
|
+
throw new BlumeError({
|
|
80
|
+
code: "BLUME_TRANSLATE_LEDGER_CONFLICT",
|
|
81
|
+
file: path,
|
|
82
|
+
message: `${LEDGER_FILE} has unresolved merge-conflict markers, so it can't say which translations are current.`,
|
|
83
|
+
severity: "error",
|
|
84
|
+
suggestion: `Resolve the merge conflict in ${LEDGER_FILE}, then rerun.`,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
64
87
|
return emptyLedger();
|
|
65
88
|
}
|
|
66
89
|
const parsed = ledgerSchema.safeParse(data);
|
package/src/translate/meta.ts
CHANGED
|
@@ -57,7 +57,9 @@ export const metaTargetPath = (
|
|
|
57
57
|
/**
|
|
58
58
|
* Discover the default-locale `meta.{ts,js,mjs}` files whose titles a
|
|
59
59
|
* translation run covers. Skips (in order): non-`dir` i18n projects entirely,
|
|
60
|
-
* files inside a configured locale directory (those ARE translations),
|
|
60
|
+
* files inside a configured locale directory (those ARE translations), files
|
|
61
|
+
* inside an archived-version snapshot (frozen, with their own translations —
|
|
62
|
+
* the same folders the navigation's meta lookup hoists as versions),
|
|
61
63
|
* factory-form modules (a warning — the generator can't re-emit a function),
|
|
62
64
|
* modules that fail meta validation (the scan already errors on those), and
|
|
63
65
|
* modules with no `title` (nothing to translate).
|
|
@@ -74,6 +76,9 @@ export const discoverTranslatableMeta = async (
|
|
|
74
76
|
locale.code === i18n.defaultLocale ? [] : [locale.code.toLowerCase()]
|
|
75
77
|
)
|
|
76
78
|
);
|
|
79
|
+
const versionDirs = new Set(
|
|
80
|
+
project.config.versions?.archived.map((version) => version.id)
|
|
81
|
+
);
|
|
77
82
|
|
|
78
83
|
const load = createModuleLoader();
|
|
79
84
|
const metas: TranslatableMeta[] = [];
|
|
@@ -92,8 +97,11 @@ export const discoverTranslatableMeta = async (
|
|
|
92
97
|
});
|
|
93
98
|
for (const file of files.toSorted()) {
|
|
94
99
|
const dir = relative(contentRoot, dirname(file));
|
|
95
|
-
const
|
|
96
|
-
if (
|
|
100
|
+
const [head] = dir.split("/");
|
|
101
|
+
if (
|
|
102
|
+
head &&
|
|
103
|
+
(localeDirs.has(head.toLowerCase()) || versionDirs.has(head))
|
|
104
|
+
) {
|
|
97
105
|
continue;
|
|
98
106
|
}
|
|
99
107
|
// oxlint-disable-next-line no-await-in-loop -- sequential, ordered discovery
|
package/src/translate/report.ts
CHANGED
|
@@ -185,7 +185,7 @@ const driftDiagnostics = (workList: TranslateWorkList): Diagnostic[] =>
|
|
|
185
185
|
: `The "${row.locale}" translation of ${row.sourceRel} is stale: its source changed since it was translated.`,
|
|
186
186
|
severity: "error",
|
|
187
187
|
suggestion:
|
|
188
|
-
"Run `blume translate --
|
|
188
|
+
"Run `blume translate --codex` (or `--claude`) to bring the translations up to date.",
|
|
189
189
|
}));
|
|
190
190
|
|
|
191
191
|
/** One line per missing/stale pair, plus dim lines for untracked adoptions. */
|