blume 2.0.1 → 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/CHANGELOG.md +82 -0
- package/dist/cli/{chunk-vtk4a6dg.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-yw7dm696.js → chunk-3yce002v.js} +21 -8
- package/dist/cli/{chunk-yw7dm696.js.map → chunk-3yce002v.js.map} +3 -3
- package/dist/cli/{chunk-yt5n7ppj.js → chunk-4e9b9ra6.js} +17 -7
- package/dist/cli/chunk-4e9b9ra6.js.map +10 -0
- package/dist/cli/{chunk-fs23ddbb.js → chunk-5r8g91qn.js} +352 -676
- package/dist/cli/chunk-5r8g91qn.js.map +34 -0
- package/dist/cli/{chunk-6vm74dry.js → chunk-6dtt0zfn.js} +8 -8
- package/dist/cli/{chunk-qwsrynx5.js → chunk-6k4ftwze.js} +38 -19
- package/dist/cli/{chunk-qwsrynx5.js.map → chunk-6k4ftwze.js.map} +4 -4
- package/dist/cli/{chunk-qs4q5p4e.js → chunk-7mbqtmgb.js} +16 -7
- package/dist/cli/chunk-7mbqtmgb.js.map +10 -0
- package/dist/cli/{chunk-q5163e60.js → chunk-7vtckvaw.js} +21 -19
- package/dist/cli/chunk-7vtckvaw.js.map +11 -0
- package/dist/cli/{chunk-kdp5q7ke.js → chunk-8g8ytmgx.js} +17 -18
- package/dist/cli/{chunk-kdp5q7ke.js.map → chunk-8g8ytmgx.js.map} +2 -2
- package/dist/cli/{chunk-epjnccmv.js → chunk-91ws1n6j.js} +18 -15
- package/dist/cli/chunk-91ws1n6j.js.map +10 -0
- package/dist/cli/{chunk-fxypxtvm.js → chunk-bbnwccaz.js} +2 -2
- package/dist/cli/{chunk-m3vmjgmq.js → chunk-bfwp9vp6.js} +16 -8
- package/dist/cli/chunk-bfwp9vp6.js.map +10 -0
- package/dist/cli/{chunk-f2z5v128.js → chunk-d1v5rhy0.js} +14 -15
- package/dist/cli/{chunk-f2z5v128.js.map → chunk-d1v5rhy0.js.map} +2 -2
- package/dist/cli/{chunk-6crbhc3x.js → chunk-ddndchfr.js} +21 -6
- package/dist/cli/chunk-ddndchfr.js.map +14 -0
- package/dist/cli/{chunk-ce574jw2.js → chunk-esh98wmb.js} +1 -1
- package/dist/cli/{chunk-zxcczpyx.js → chunk-fsmrqk8a.js} +1 -1
- package/dist/cli/{chunk-hdpx1tax.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-zxh4d9vy.js → chunk-h2ez8dzb.js} +4 -4
- package/dist/cli/{chunk-5shv93fd.js → chunk-h7k3nq3v.js} +2 -2
- package/dist/cli/{chunk-79jhk4py.js → chunk-hqp2ajnh.js} +251 -122
- package/dist/cli/chunk-hqp2ajnh.js.map +35 -0
- package/dist/cli/{chunk-2hn4b8z7.js → chunk-hr8ne106.js} +109 -40
- package/dist/cli/chunk-hr8ne106.js.map +13 -0
- package/dist/cli/{chunk-6hsn950k.js → chunk-j85scx15.js} +62 -18
- package/dist/cli/chunk-j85scx15.js.map +10 -0
- package/dist/cli/{chunk-s1p84fyh.js → chunk-k7pj68a8.js} +63 -22
- package/dist/cli/chunk-k7pj68a8.js.map +11 -0
- package/dist/cli/{chunk-ah61y8py.js → chunk-mqc662a6.js} +2 -2
- package/dist/cli/{chunk-ch6g3ar0.js → chunk-n1yg3tj3.js} +4 -4
- package/dist/cli/{chunk-mb2919y2.js → chunk-nfcyttvj.js} +17 -6
- package/dist/cli/chunk-nfcyttvj.js.map +10 -0
- package/dist/cli/{chunk-kpf8rrjc.js → chunk-pbg5a4s3.js} +56 -24
- package/dist/cli/chunk-pbg5a4s3.js.map +19 -0
- package/dist/cli/{chunk-jwyddg7y.js → chunk-ppzjqwx2.js} +21 -14
- package/dist/cli/{chunk-jwyddg7y.js.map → chunk-ppzjqwx2.js.map} +4 -4
- package/dist/cli/{chunk-dh8cwk36.js → chunk-qkb5a8sa.js} +24 -9
- package/dist/cli/chunk-qkb5a8sa.js.map +10 -0
- package/dist/cli/{chunk-wm7js3j9.js → chunk-sqw4ekg1.js} +2 -2
- package/dist/cli/{chunk-qkqwkpte.js → chunk-v6ya5kcb.js} +3083 -953
- package/dist/cli/chunk-v6ya5kcb.js.map +189 -0
- package/dist/cli/{chunk-fz5wtpmh.js → chunk-w4bxdvsa.js} +18 -15
- package/dist/cli/chunk-w4bxdvsa.js.map +10 -0
- package/dist/cli/{chunk-27g6wdth.js → chunk-wdrt2k2v.js} +2 -2
- package/dist/cli/{chunk-wgm7m9qk.js → chunk-xh43dwgw.js} +158 -43
- package/dist/cli/chunk-xh43dwgw.js.map +36 -0
- package/dist/cli/{chunk-s6jhgk0q.js → chunk-zp79m0ts.js} +2 -2
- package/dist/cli/index.js +160 -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/relative-links.d.ts +9 -4
- package/dist/types/ai/static-expression.d.ts +28 -0
- 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 +4 -4
- package/dist/types/core/config.d.ts +2 -2
- package/dist/types/core/graph.d.ts +2 -0
- package/dist/types/core/i18n-ui.d.ts +31 -0
- 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 +52 -13
- 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/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/sync/algolia.d.ts +3 -1
- package/docs/01-quickstart.mdx +3 -2
- package/docs/02-deployment.mdx +17 -8
- package/docs/08-faq.mdx +9 -2
- package/docs/advanced/changelog.mdx +1 -1
- package/docs/advanced/custom-pages.mdx +4 -2
- package/docs/cli/audit.mdx +2 -2
- package/docs/cli/doctor.mdx +2 -2
- package/docs/cli/evals.mdx +2 -2
- package/docs/cli/index.mdx +4 -1
- package/docs/cli/translate.mdx +1 -1
- package/docs/configuration/analytics.mdx +23 -2
- package/docs/configuration/assistant.mdx +1 -1
- package/docs/configuration/customization.mdx +4 -3
- package/docs/configuration/index.mdx +5 -3
- package/docs/configuration/search.mdx +1 -1
- package/docs/content/components.mdx +1 -1
- package/docs/content/frontmatter.mdx +5 -1
- package/docs/content/i18n.mdx +1 -1
- package/docs/content/index.mdx +4 -2
- 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 +21 -7
- package/docs/discoverability/index.mdx +2 -2
- 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/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-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-surface.ts +56 -0
- package/src/ai/api/spec.ts +15 -2
- package/src/ai/api-catalog.ts +7 -1
- package/src/ai/ask-context.ts +14 -2
- package/src/ai/ask-data.ts +26 -12
- package/src/ai/component-markdown.ts +43 -37
- 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/relative-links.ts +78 -10
- package/src/ai/static-expression.ts +416 -0
- package/src/ai/visibility.ts +45 -14
- 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 +71 -27
- package/src/astro/templates.ts +61 -23
- 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/cli/build-failure.ts +50 -0
- package/src/cli/commands/audit.ts +7 -0
- package/src/cli/commands/build.ts +19 -5
- package/src/cli/commands/check.ts +2 -0
- package/src/cli/commands/eval.ts +1 -7
- 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 +9 -6
- package/src/cli/commands/upgrade.ts +11 -0
- 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/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 +35 -8
- package/src/components/islands/assistant.tsx +16 -2
- package/src/components/islands/hooks.ts +7 -2
- package/src/components/islands/webmcp.ts +12 -8
- package/src/components/layout/Banner.astro +23 -4
- package/src/components/layout/Header.astro +22 -9
- 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 +22 -8
- package/src/components/layout/ReferenceLayout.astro +4 -1
- package/src/components/layout/RootLayout.astro +21 -8
- package/src/components/layout/Search.astro +5 -1
- package/src/components/layout/analytics-client.ts +2 -0
- 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/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/config-input.ts +4 -4
- package/src/core/config.ts +18 -4
- package/src/core/diagnostics.ts +217 -30
- package/src/core/graph.ts +120 -11
- package/src/core/i18n-ui.ts +39 -2
- 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/safe-href.ts +53 -1
- package/src/core/safe-links.ts +11 -2
- package/src/core/schema.ts +70 -14
- 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 +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 +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 +119 -22
- package/src/search/documents.ts +22 -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/run.ts +11 -5
- package/src/translate/validate.ts +10 -1
- package/src/translate/work-list.ts +36 -3
- package/src/upgrade/upgrade.ts +36 -4
- 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-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-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-vtk4a6dg.js.map → chunk-00gs3wqs.js.map} +0 -0
- /package/dist/cli/{chunk-6vm74dry.js.map → chunk-6dtt0zfn.js.map} +0 -0
- /package/dist/cli/{chunk-fxypxtvm.js.map → chunk-bbnwccaz.js.map} +0 -0
- /package/dist/cli/{chunk-ce574jw2.js.map → chunk-esh98wmb.js.map} +0 -0
- /package/dist/cli/{chunk-zxcczpyx.js.map → chunk-fsmrqk8a.js.map} +0 -0
- /package/dist/cli/{chunk-hdpx1tax.js.map → chunk-g698a744.js.map} +0 -0
- /package/dist/cli/{chunk-zxh4d9vy.js.map → chunk-h2ez8dzb.js.map} +0 -0
- /package/dist/cli/{chunk-5shv93fd.js.map → chunk-h7k3nq3v.js.map} +0 -0
- /package/dist/cli/{chunk-ah61y8py.js.map → chunk-mqc662a6.js.map} +0 -0
- /package/dist/cli/{chunk-ch6g3ar0.js.map → chunk-n1yg3tj3.js.map} +0 -0
- /package/dist/cli/{chunk-wm7js3j9.js.map → chunk-sqw4ekg1.js.map} +0 -0
- /package/dist/cli/{chunk-27g6wdth.js.map → chunk-wdrt2k2v.js.map} +0 -0
- /package/dist/cli/{chunk-s6jhgk0q.js.map → chunk-zp79m0ts.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";
|
|
@@ -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,16 +390,18 @@ 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]
|
|
@@ -328,6 +411,8 @@ const ejectDependencies = (
|
|
|
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"] : []),
|
|
@@ -439,7 +524,9 @@ export const eject = async (
|
|
|
439
524
|
pattern: page.pattern,
|
|
440
525
|
})),
|
|
441
526
|
...mcpDiscoveryPages(project, pages),
|
|
527
|
+
...playgroundProxyPages(config),
|
|
442
528
|
];
|
|
529
|
+
const openApiData = ejectOpenApiData(project);
|
|
443
530
|
|
|
444
531
|
// Non-filesystem sources eject their materialized MDX into `<root>/blume-staged`
|
|
445
532
|
// (a dedicated dir so it never clashes with a content root literally named
|
|
@@ -555,7 +642,7 @@ export const eject = async (
|
|
|
555
642
|
path: join(genDir, "Ask.astro"),
|
|
556
643
|
},
|
|
557
644
|
{
|
|
558
|
-
content: `${JSON.stringify(
|
|
645
|
+
content: `${JSON.stringify(openApiData)}\n`,
|
|
559
646
|
path: join(genDir, "openapi.json"),
|
|
560
647
|
},
|
|
561
648
|
{
|
|
@@ -581,30 +668,38 @@ export const eject = async (
|
|
|
581
668
|
},
|
|
582
669
|
];
|
|
583
670
|
|
|
671
|
+
files.push(...(await contentAssetFiles(project, root, srcDir, genDir)));
|
|
672
|
+
|
|
584
673
|
if (assistantEnabled) {
|
|
585
674
|
files.push(...(await askFiles(project, srcDir, genDir)));
|
|
586
675
|
}
|
|
587
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
|
+
|
|
588
686
|
if (config.seo.og.enabled) {
|
|
589
687
|
files.push({
|
|
590
688
|
content: ogEndpointTemplate(
|
|
591
689
|
customOgRoutes(pages, config.title, config.seo.og.titles),
|
|
592
|
-
{ pageDescriptions: config.seo.og.description !== false }
|
|
690
|
+
{ pageDescriptions: config.seo.og.description !== false },
|
|
691
|
+
changelog.length > 0
|
|
593
692
|
),
|
|
594
693
|
path: join(srcDir, "pages", "og", "[...slug].png.ts"),
|
|
595
694
|
});
|
|
596
695
|
}
|
|
597
696
|
|
|
598
|
-
// The hosted MCP server and the
|
|
599
|
-
// generated runtime (
|
|
697
|
+
// The hosted MCP server and the playground's built-in proxy, mirrored from
|
|
698
|
+
// the generated runtime (`[]` when each is off).
|
|
600
699
|
files.push(
|
|
601
700
|
...(await mcpFiles(project, pages, srcDir, genDir)),
|
|
602
|
-
...
|
|
603
|
-
|
|
604
|
-
exportPdf,
|
|
605
|
-
needsReact,
|
|
606
|
-
staged: hasStaged,
|
|
607
|
-
})
|
|
701
|
+
...changelog,
|
|
702
|
+
...playgroundProxyFiles(config, openApiData, srcDir)
|
|
608
703
|
);
|
|
609
704
|
|
|
610
705
|
// Default 404 page, unless the project already owns `/404` (a custom
|
|
@@ -673,7 +768,9 @@ export const eject = async (
|
|
|
673
768
|
// the ejected app keeps its reference routes — including the warnings (a
|
|
674
769
|
// missing spec file, a route collision), which the caller surfaces exactly
|
|
675
770
|
// like the generated-runtime path does.
|
|
676
|
-
|
|
771
|
+
// A proxied spec with no origin to allow gets the generated runtime's
|
|
772
|
+
// diagnostic too.
|
|
773
|
+
const warnings: string[] = proxyAllowlistWarnings(config, openApiData);
|
|
677
774
|
if (hasScalarReferences(config)) {
|
|
678
775
|
const references = await buildReferenceFiles({
|
|
679
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";
|
|
@@ -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,
|
|
@@ -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/run.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { tmpdir } from "node:os";
|
|
|
4
4
|
|
|
5
5
|
import pLimit from "p-limit";
|
|
6
6
|
import pMap from "p-map";
|
|
7
|
-
import { join } from "pathe";
|
|
7
|
+
import { dirname, join } from "pathe";
|
|
8
8
|
|
|
9
9
|
import { AGENTS } from "../audit/agent.ts";
|
|
10
10
|
import type { AgentKind } from "../audit/agent.ts";
|
|
@@ -18,6 +18,7 @@ import { DEFAULT_TRANSLATE_TIMEOUT_MS, translateAgentArgs } from "./agents.ts";
|
|
|
18
18
|
import { hashSource, stampLedger } from "./ledger.ts";
|
|
19
19
|
import type { TranslationLedger } from "./ledger.ts";
|
|
20
20
|
import { generateMetaModule } from "./meta.ts";
|
|
21
|
+
import type { TranslatableMeta } from "./meta.ts";
|
|
21
22
|
import { metaPrompt, pagePrompt } from "./prompts.ts";
|
|
22
23
|
import { parseMetaTitles, validateTranslation } from "./validate.ts";
|
|
23
24
|
import type {
|
|
@@ -88,8 +89,13 @@ interface RunContext {
|
|
|
88
89
|
timeoutMs: number;
|
|
89
90
|
}
|
|
90
91
|
|
|
91
|
-
/**
|
|
92
|
-
|
|
92
|
+
/**
|
|
93
|
+
* A meta file's key in a meta-titles prompt: its directory relative to the
|
|
94
|
+
* project root. The content-root-relative `dir` isn't unique — two
|
|
95
|
+
* filesystem sources can both have a `guides/` folder — and colliding keys
|
|
96
|
+
* would hand both folders one translation.
|
|
97
|
+
*/
|
|
98
|
+
const metaKey = (meta: TranslatableMeta): string => dirname(meta.sourceRel);
|
|
93
99
|
|
|
94
100
|
/**
|
|
95
101
|
* One headless agent call. A missing executable rejects with ENOENT on every
|
|
@@ -183,7 +189,7 @@ const runMetaItem = async (
|
|
|
183
189
|
});
|
|
184
190
|
|
|
185
191
|
const titles = Object.fromEntries(
|
|
186
|
-
item.entries.map((entry) => [
|
|
192
|
+
item.entries.map((entry) => [metaKey(entry.meta), entry.meta.title])
|
|
187
193
|
);
|
|
188
194
|
// SAFETY: `targets` maps every configured locale, and work items only carry
|
|
189
195
|
// configured locale codes.
|
|
@@ -199,7 +205,7 @@ const runMetaItem = async (
|
|
|
199
205
|
|
|
200
206
|
const parsed = parseMetaTitles(output.text, Object.keys(titles));
|
|
201
207
|
for (const entry of item.entries) {
|
|
202
|
-
const translated = parsed.titles[
|
|
208
|
+
const translated = parsed.titles[metaKey(entry.meta)];
|
|
203
209
|
if (translated === undefined) {
|
|
204
210
|
continue;
|
|
205
211
|
}
|
|
@@ -102,7 +102,16 @@ export const validateTranslation = (
|
|
|
102
102
|
sourceText: string,
|
|
103
103
|
agentText: string
|
|
104
104
|
): ValidationResult => {
|
|
105
|
-
|
|
105
|
+
// Only a fence the agent added is stripped. A source that is itself one
|
|
106
|
+
// code block (a frontmatter-less partial holding a single bash block)
|
|
107
|
+
// starts and ends with its own fence, which the translation must keep — so
|
|
108
|
+
// for such a source the reply loses its outer fence only when it has more
|
|
109
|
+
// fence lines than the source.
|
|
110
|
+
const sourceIsFenced = stripOuterFence(sourceText) !== sourceText.trim();
|
|
111
|
+
const candidate =
|
|
112
|
+
sourceIsFenced && countFenceLines(agentText) <= countFenceLines(sourceText)
|
|
113
|
+
? agentText.trim()
|
|
114
|
+
: stripOuterFence(agentText);
|
|
106
115
|
if (candidate === "") {
|
|
107
116
|
return { ok: false, reason: "agent returned empty output" };
|
|
108
117
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
1
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
|
|
4
4
|
import { dirname, extname, join, relative } from "pathe";
|
|
5
5
|
|
|
6
6
|
import { localeCodes, localeTargetPath } from "../core/i18n.ts";
|
|
7
|
+
import { scanProject } from "../core/project-graph.ts";
|
|
7
8
|
import type { BlumeProject } from "../core/project-graph.ts";
|
|
8
9
|
import type { Diagnostic, PageRecord } from "../core/types.ts";
|
|
9
10
|
import { hashSource } from "./ledger.ts";
|
|
@@ -84,6 +85,36 @@ export interface TranslateWorkList {
|
|
|
84
85
|
|
|
85
86
|
const PAGE_EXTENSIONS = new Set([".md", ".mdx"]);
|
|
86
87
|
|
|
88
|
+
/**
|
|
89
|
+
* The names in each content root, read once per root: a locale folder authored
|
|
90
|
+
* in another casing (`pt-br/` for `pt-BR`) is where `localeTargetPath` puts
|
|
91
|
+
* that locale's translations.
|
|
92
|
+
*/
|
|
93
|
+
const rootFolders = (): ((contentRoot: string) => string[]) => {
|
|
94
|
+
const read = new Map<string, string[]>();
|
|
95
|
+
return (contentRoot) => {
|
|
96
|
+
const cached = read.get(contentRoot);
|
|
97
|
+
if (cached) {
|
|
98
|
+
return cached;
|
|
99
|
+
}
|
|
100
|
+
const names = readdirSync(contentRoot);
|
|
101
|
+
read.set(contentRoot, names);
|
|
102
|
+
return names;
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Scan the project for a translation run: drafts included. A production
|
|
108
|
+
* (`build`) scan drops them, so a hand-written translation marked
|
|
109
|
+
* `draft: true` would look missing and be overwritten, and a source page
|
|
110
|
+
* drafted for a while would lose its ledger stamps — its outdated
|
|
111
|
+
* translations then reading as current once it's published again. A dev scan
|
|
112
|
+
* keeps drafts and differs only in reusing cached remote content, which
|
|
113
|
+
* translation never reads.
|
|
114
|
+
*/
|
|
115
|
+
export const scanForTranslation = (root: string): Promise<BlumeProject> =>
|
|
116
|
+
scanProject(root, { mode: "dev" });
|
|
117
|
+
|
|
87
118
|
/**
|
|
88
119
|
* The translatable page universe: filesystem-backed default-locale pages.
|
|
89
120
|
* Remote/staged sources have no writable path; fallback records are padding;
|
|
@@ -201,6 +232,7 @@ const partialWorkItems = async (
|
|
|
201
232
|
page.sourcePath ? [page.sourcePath] : []
|
|
202
233
|
)
|
|
203
234
|
);
|
|
235
|
+
const folders = rootFolders();
|
|
204
236
|
const partials = new Map<string, string>();
|
|
205
237
|
for (const { page, contentRoot } of universe) {
|
|
206
238
|
for (const partial of page.includes ?? []) {
|
|
@@ -220,7 +252,7 @@ const partialWorkItems = async (
|
|
|
220
252
|
for (const locale of targetLocales) {
|
|
221
253
|
const targetPath = join(
|
|
222
254
|
contentRoot,
|
|
223
|
-
localeTargetPath(contentRel, ext, locale, i18n)
|
|
255
|
+
localeTargetPath(contentRel, ext, locale, i18n, folders(contentRoot))
|
|
224
256
|
);
|
|
225
257
|
const item = (status: WorkStatus): PageWorkItem => {
|
|
226
258
|
const partialItem: PageWorkItem = {
|
|
@@ -291,6 +323,7 @@ export const computeWorkList = async (
|
|
|
291
323
|
let upToDate = 0;
|
|
292
324
|
|
|
293
325
|
const universe = translatablePages(project, i18n);
|
|
326
|
+
const folders = rootFolders();
|
|
294
327
|
|
|
295
328
|
for (const { page, contentRoot, ext, sourcePath } of universe) {
|
|
296
329
|
const sourceRel = relative(root, sourcePath);
|
|
@@ -302,7 +335,7 @@ export const computeWorkList = async (
|
|
|
302
335
|
for (const locale of targetLocales) {
|
|
303
336
|
const targetPath = join(
|
|
304
337
|
contentRoot,
|
|
305
|
-
localeTargetPath(contentRel, ext, locale, i18n)
|
|
338
|
+
localeTargetPath(contentRel, ext, locale, i18n, folders(contentRoot))
|
|
306
339
|
);
|
|
307
340
|
const item = (status: WorkStatus): PageWorkItem => ({
|
|
308
341
|
kind: "page",
|