blume 1.7.3 → 2.0.0
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 +19 -0
- package/CHANGELOG.md +217 -0
- package/README.md +34 -21
- package/dist/cli/chunk-11j0384y.js +148 -0
- package/dist/cli/chunk-11j0384y.js.map +10 -0
- package/dist/cli/{chunk-rqy0s5wh.js → chunk-1w8dp3qb.js} +17 -16
- package/dist/cli/{chunk-rqy0s5wh.js.map → chunk-1w8dp3qb.js.map} +3 -3
- package/dist/cli/chunk-2q1dwty4.js +75 -0
- package/dist/cli/chunk-2q1dwty4.js.map +11 -0
- package/dist/cli/chunk-41za066z.js +122 -0
- package/dist/cli/chunk-41za066z.js.map +11 -0
- package/dist/cli/chunk-5a2z0198.js +133 -0
- package/dist/cli/chunk-5a2z0198.js.map +10 -0
- package/dist/cli/{chunk-5g0w1e2c.js → chunk-6k8vp3ta.js} +25 -13
- package/dist/cli/{chunk-5g0w1e2c.js.map → chunk-6k8vp3ta.js.map} +4 -4
- package/dist/cli/{chunk-5qk08vmp.js → chunk-79njf86q.js} +133 -54
- package/dist/cli/chunk-79njf86q.js.map +11 -0
- package/dist/cli/{chunk-e7f42gdj.js → chunk-7ez8ny0t.js} +2 -2
- package/dist/cli/{chunk-nn13znc2.js → chunk-88cpgt6h.js} +1 -1
- package/dist/cli/chunk-a9kptbw5.js +361 -0
- package/dist/cli/chunk-a9kptbw5.js.map +14 -0
- package/dist/cli/chunk-abh8yjkn.js +31 -0
- package/dist/cli/chunk-abh8yjkn.js.map +10 -0
- package/dist/cli/chunk-b5aj94ah.js +91 -0
- package/dist/cli/chunk-b5aj94ah.js.map +10 -0
- package/dist/cli/{chunk-b27xqwn9.js → chunk-bctazmbk.js} +9 -5
- package/dist/cli/chunk-bctazmbk.js.map +10 -0
- package/dist/cli/chunk-beat36xx.js +279 -0
- package/dist/cli/chunk-beat36xx.js.map +10 -0
- package/dist/cli/chunk-bnbmcwfb.js +145 -0
- package/dist/cli/chunk-bnbmcwfb.js.map +11 -0
- package/dist/cli/{chunk-0xjyb285.js → chunk-bw22s759.js} +15 -5
- package/dist/cli/{chunk-0xjyb285.js.map → chunk-bw22s759.js.map} +4 -4
- package/dist/cli/chunk-by2290sx.js +39 -0
- package/dist/cli/chunk-by2290sx.js.map +10 -0
- package/dist/cli/chunk-d1tadaw7.js +79 -0
- package/dist/cli/chunk-d1tadaw7.js.map +10 -0
- package/dist/cli/{chunk-ahnw3kxw.js → chunk-d80hr03s.js} +24 -19
- package/dist/cli/chunk-d80hr03s.js.map +15 -0
- package/dist/cli/chunk-ernrthtr.js +97 -0
- package/dist/cli/chunk-ernrthtr.js.map +10 -0
- package/dist/cli/chunk-f2972sbt.js +374 -0
- package/dist/cli/chunk-f2972sbt.js.map +10 -0
- package/dist/cli/{chunk-vacwm2hv.js → chunk-f7t03s3g.js} +2 -2
- package/dist/cli/{chunk-2z47ypj8.js → chunk-fa25z98p.js} +16 -3
- package/dist/cli/chunk-fa25z98p.js.map +11 -0
- package/dist/cli/{chunk-cjtn640a.js → chunk-fh5hj5jt.js} +43 -20
- package/dist/cli/chunk-fh5hj5jt.js.map +10 -0
- package/dist/cli/{chunk-yg63d42r.js → chunk-j8mw0za6.js} +86 -57
- package/dist/cli/chunk-j8mw0za6.js.map +35 -0
- package/dist/cli/{chunk-ct47dqpx.js → chunk-jts8mvcz.js} +67 -7
- package/dist/cli/{chunk-2mzebbbz.js.map → chunk-jts8mvcz.js.map} +6 -4
- package/dist/cli/{chunk-ps4m1xh4.js → chunk-mnqj32sj.js} +505 -542
- package/dist/cli/chunk-mnqj32sj.js.map +12 -0
- package/dist/cli/{chunk-8cjtbafj.js → chunk-mwt1k8n7.js} +100 -374
- package/dist/cli/chunk-mwt1k8n7.js.map +10 -0
- package/dist/cli/chunk-nk3ts2xk.js +51 -0
- package/dist/cli/chunk-nk3ts2xk.js.map +10 -0
- package/dist/cli/{chunk-3r45185y.js → chunk-pat2zzwc.js} +9 -11
- package/dist/cli/{chunk-3r45185y.js.map → chunk-pat2zzwc.js.map} +2 -2
- package/dist/cli/chunk-pnnvybbk.js +176 -0
- package/dist/cli/chunk-pnnvybbk.js.map +11 -0
- package/dist/cli/{chunk-4x36ddpw.js → chunk-sqn5t4q0.js} +81 -87
- package/dist/cli/chunk-sqn5t4q0.js.map +10 -0
- package/dist/cli/{chunk-bvwwhd84.js → chunk-tzne8qfq.js} +15 -15
- package/dist/cli/{chunk-bvwwhd84.js.map → chunk-tzne8qfq.js.map} +1 -1
- package/dist/cli/chunk-xaz13gwg.js +12449 -0
- package/dist/cli/chunk-xaz13gwg.js.map +182 -0
- package/dist/cli/chunk-y3e45rc8.js +102 -0
- package/dist/cli/chunk-y3e45rc8.js.map +10 -0
- package/dist/cli/chunk-z01ze5c1.js +261 -0
- package/dist/cli/chunk-z01ze5c1.js.map +10 -0
- package/dist/cli/{chunk-k79xp7av.js → chunk-z1f5arsg.js} +219 -689
- package/dist/cli/chunk-z1f5arsg.js.map +36 -0
- package/dist/cli/{chunk-1jefwnfs.js → chunk-zg2gtj10.js} +1076 -2592
- package/dist/cli/chunk-zg2gtj10.js.map +35 -0
- package/dist/cli/{chunk-dwgcp5sm.js → chunk-zxccj738.js} +1 -1
- package/dist/cli/index.js +214 -57
- package/dist/cli/index.js.map +8 -7
- package/dist/types/ai/agent-readability.d.ts +52 -0
- package/dist/types/ai/ai-catalog.d.ts +42 -0
- package/dist/types/ai/api/paths.d.ts +17 -0
- package/dist/types/ai/api-catalog.d.ts +18 -0
- package/dist/types/ai/ask.d.ts +368 -0
- package/dist/types/ai/changelog-markdown.d.ts +2 -0
- package/dist/types/ai/component-markdown.d.ts +2 -2
- package/dist/types/ai/index.d.ts +24 -0
- package/dist/types/ai/link-headers.d.ts +24 -0
- package/dist/types/ai/llms.d.ts +25 -0
- package/dist/types/ai/markdown.d.ts +45 -0
- package/dist/types/ai/mcp/discovery.d.ts +68 -0
- package/dist/types/ai/mcp/tools.d.ts +16 -0
- package/dist/types/ai/openapi-components.d.ts +43 -0
- package/dist/types/ai/relative-links.d.ts +26 -0
- package/dist/types/ai/serializers.d.ts +15 -0
- package/dist/types/ai/skills.d.ts +42 -0
- package/dist/types/ai/tar.d.ts +25 -0
- package/dist/types/ai/visibility.d.ts +17 -0
- package/dist/types/ai/web-bot-auth.d.ts +16 -0
- package/dist/types/analytics/adobe.d.ts +35 -0
- package/dist/types/analytics/amplitude.d.ts +50 -0
- package/dist/types/analytics/clarity.d.ts +31 -0
- package/dist/types/analytics/clearbit.d.ts +29 -0
- package/dist/types/analytics/cloudflare.d.ts +41 -0
- package/dist/types/analytics/fathom.d.ts +41 -0
- package/dist/types/analytics/google-analytics.d.ts +44 -0
- package/dist/types/analytics/google-tag-manager.d.ts +40 -0
- package/dist/types/analytics/head.d.ts +30 -0
- package/dist/types/analytics/heap.d.ts +40 -0
- package/dist/types/analytics/hightouch.d.ts +44 -0
- package/dist/types/analytics/hotjar.d.ts +33 -0
- package/dist/types/analytics/index.d.ts +61 -0
- package/dist/types/analytics/inline.d.ts +17 -0
- package/dist/types/analytics/logrocket.d.ts +44 -0
- package/dist/types/analytics/mixpanel.d.ts +67 -0
- package/dist/types/analytics/pirsch.d.ts +42 -0
- package/dist/types/analytics/plausible.d.ts +56 -0
- package/dist/types/analytics/posthog.d.ts +45 -0
- package/dist/types/analytics/schema.d.ts +320 -0
- package/dist/types/analytics/script.d.ts +46 -0
- package/dist/types/analytics/segment.d.ts +50 -0
- package/dist/types/analytics/vercel.d.ts +48 -0
- package/dist/types/astro/integration.d.ts +76 -0
- package/dist/types/astro/markdown-negotiation.d.ts +23 -0
- package/dist/types/astro/module-types.d.ts +14 -0
- package/dist/types/astro/pages.d.ts +44 -0
- package/dist/types/cli/env.d.ts +12 -0
- package/dist/types/cli/init/scaffold.d.ts +154 -0
- package/dist/types/components/layout/nav-utils.d.ts +11 -0
- package/dist/types/core/adapter.d.ts +47 -0
- package/dist/types/core/api-name.d.ts +7 -0
- package/dist/types/core/changelog-index.d.ts +13 -0
- package/dist/types/core/config-input.d.ts +186 -515
- package/dist/types/core/config.d.ts +60 -34
- package/dist/types/core/content-assets.d.ts +76 -0
- package/dist/types/core/custom-pages.d.ts +33 -0
- package/dist/types/core/data.d.ts +27 -12
- package/dist/types/core/define-components.d.ts +12 -9
- package/dist/types/core/deployment-env.d.ts +6 -11
- package/dist/types/core/frontmatter.d.ts +10 -0
- package/dist/types/core/graph.d.ts +18 -0
- package/dist/types/core/heading-markers.d.ts +54 -0
- package/dist/types/core/i18n-ui.d.ts +6 -2
- package/dist/types/core/i18n.d.ts +87 -0
- package/dist/types/core/includes.d.ts +138 -0
- package/dist/types/core/last-modified.d.ts +47 -0
- package/dist/types/core/links.d.ts +95 -0
- package/dist/types/core/locale-links.d.ts +60 -0
- package/dist/types/core/manifest.d.ts +17 -0
- package/dist/types/core/meta.d.ts +38 -0
- package/dist/types/core/nav-diagnostics.d.ts +26 -0
- package/dist/types/core/navigation.d.ts +6 -0
- package/dist/types/core/node-require.d.ts +19 -0
- package/dist/types/core/package-json.d.ts +13 -0
- package/dist/types/core/probe.d.ts +42 -0
- package/dist/types/core/project-graph.d.ts +51 -0
- package/dist/types/core/project.d.ts +2 -0
- package/dist/types/core/safe-href.d.ts +2 -0
- package/dist/types/core/safe-links.d.ts +26 -0
- package/dist/types/core/schema.d.ts +2282 -452
- package/dist/types/core/site-url.d.ts +16 -0
- package/dist/types/core/sources/assets.d.ts +36 -0
- package/dist/types/core/sources/cache.d.ts +37 -0
- package/dist/types/core/sources/collection.d.ts +28 -0
- package/dist/types/core/sources/contentful-rich-text.d.ts +31 -0
- package/dist/types/core/sources/contentful.d.ts +40 -0
- package/dist/types/core/sources/filesystem.d.ts +24 -0
- package/dist/types/core/sources/github-releases.d.ts +31 -0
- package/dist/types/core/sources/json.d.ts +30 -0
- package/dist/types/core/sources/lexical.d.ts +19 -0
- package/dist/types/core/sources/lower.d.ts +75 -0
- package/dist/types/core/sources/mdx-remote.d.ts +28 -0
- package/dist/types/core/sources/normalize.d.ts +147 -0
- package/dist/types/core/sources/notion.d.ts +131 -0
- package/dist/types/core/sources/obsidian.d.ts +46 -0
- package/dist/types/core/sources/payload.d.ts +39 -0
- package/dist/types/core/sources/portable-text.d.ts +42 -0
- package/dist/types/core/sources/read.d.ts +23 -0
- package/dist/types/core/sources/remote.d.ts +74 -0
- package/dist/types/core/sources/resolve.d.ts +19 -0
- package/dist/types/core/sources/sanity.d.ts +43 -0
- package/dist/types/core/sources/strapi-blocks.d.ts +12 -0
- package/dist/types/core/sources/strapi.d.ts +33 -0
- package/dist/types/core/sources/types.d.ts +7 -0
- package/dist/types/core/sources/watch.d.ts +45 -0
- package/dist/types/core/text-width.d.ts +11 -0
- package/dist/types/core/types.d.ts +15 -1
- package/dist/types/core/unrecognized-keys.d.ts +7 -0
- package/dist/types/core/versions.d.ts +72 -0
- package/dist/types/core/yaml.d.ts +9 -0
- package/dist/types/deploy/adapter-output.d.ts +45 -0
- package/dist/types/deploy/adapters/cloudflare.d.ts +40 -0
- package/dist/types/deploy/adapters/index.d.ts +29 -0
- package/dist/types/deploy/adapters/netlify.d.ts +37 -0
- package/dist/types/deploy/adapters/node.d.ts +37 -0
- package/dist/types/deploy/adapters/registry.d.ts +133 -0
- package/dist/types/deploy/adapters/types.d.ts +71 -0
- package/dist/types/deploy/adapters/vercel.d.ts +38 -0
- package/dist/types/deploy/artifacts.d.ts +65 -0
- package/dist/types/deploy/cloudflare-negotiation.d.ts +196 -0
- package/dist/types/deploy/function-bundle.d.ts +80 -0
- package/dist/types/deploy/headers.d.ts +50 -0
- package/dist/types/deploy/node-headers.d.ts +42 -0
- package/dist/types/deploy/platforms/cloudflare.d.ts +40 -0
- package/dist/types/deploy/platforms/index.d.ts +18 -0
- package/dist/types/deploy/platforms/netlify.d.ts +13 -0
- package/dist/types/deploy/platforms/node.d.ts +11 -0
- package/dist/types/deploy/platforms/paths.d.ts +27 -0
- package/dist/types/deploy/platforms/static.d.ts +10 -0
- package/dist/types/deploy/platforms/types.d.ts +104 -0
- package/dist/types/deploy/platforms/vercel.d.ts +32 -0
- package/dist/types/deploy/redirects.d.ts +53 -0
- package/dist/types/deploy/robots.d.ts +8 -0
- package/dist/types/deploy/rss.d.ts +31 -0
- package/dist/types/deploy/sitemap.d.ts +21 -0
- package/dist/types/deploy/vercel-negotiation.d.ts +109 -0
- package/dist/types/markdown/code-title.d.ts +32 -0
- package/dist/types/markdown/fence-meta.d.ts +23 -0
- package/dist/types/markdown/themes.d.ts +3 -3
- package/dist/types/openapi/asyncapi.d.ts +129 -0
- package/dist/types/openapi/graphql-build.d.ts +8 -0
- package/dist/types/openapi/graphql.d.ts +122 -0
- package/dist/types/openapi/model.d.ts +158 -0
- package/dist/types/openapi/parse.d.ts +57 -0
- package/dist/types/openapi/references.d.ts +37 -25
- package/dist/types/openapi/render-mdx.d.ts +33 -0
- package/dist/types/openapi/sentence.d.ts +7 -0
- package/dist/types/openapi/signature.d.ts +10 -0
- package/dist/types/openapi/source.d.ts +22 -0
- package/dist/types/openapi/spec-dependency-error.d.ts +10 -0
- package/dist/types/reference/asyncapi.d.ts +166 -0
- package/dist/types/reference/graphql.d.ts +181 -0
- package/dist/types/reference/index.d.ts +32 -0
- package/dist/types/reference/openapi.d.ts +165 -0
- package/dist/types/reference/options.d.ts +157 -0
- package/dist/types/reference/scalar.d.ts +136 -0
- package/dist/types/reference/schema.d.ts +630 -0
- package/dist/types/search/adapters/algolia.d.ts +32 -0
- package/dist/types/search/adapters/flexsearch.d.ts +12 -0
- package/dist/types/search/adapters/index.d.ts +31 -0
- package/dist/types/search/adapters/mixedbread.d.ts +22 -0
- package/dist/types/search/adapters/orama-cloud.d.ts +33 -0
- package/dist/types/search/adapters/orama.d.ts +13 -0
- package/dist/types/search/adapters/pagefind.d.ts +12 -0
- package/dist/types/search/adapters/registry.d.ts +228 -0
- package/dist/types/search/adapters/types.d.ts +34 -0
- package/dist/types/search/adapters/typesense.d.ts +42 -0
- package/dist/types/search/build.d.ts +23 -0
- package/dist/types/search/documents.d.ts +90 -0
- package/dist/types/search/facets.d.ts +3 -0
- package/dist/types/search/sync/algolia.d.ts +14 -0
- package/dist/types/search/sync/index.d.ts +14 -0
- package/dist/types/search/sync/orama-cloud.d.ts +10 -0
- package/dist/types/search/sync/typesense.d.ts +14 -0
- package/dist/types/sources/contentful.d.ts +68 -0
- package/dist/types/sources/custom.d.ts +20 -0
- package/dist/types/sources/filesystem.d.ts +42 -0
- package/dist/types/sources/github-releases.d.ts +46 -0
- package/dist/types/sources/index.d.ts +48 -0
- package/dist/types/sources/mdx-remote.d.ts +63 -0
- package/dist/types/sources/notion.d.ts +65 -0
- package/dist/types/sources/obsidian.d.ts +34 -0
- package/dist/types/sources/payload.d.ts +68 -0
- package/dist/types/sources/registry.d.ts +1414 -0
- package/dist/types/sources/sanity.d.ts +69 -0
- package/dist/types/sources/shared.d.ts +46 -0
- package/dist/types/sources/strapi.d.ts +66 -0
- package/dist/types/theme/icon-kind.d.ts +11 -0
- package/dist/types/theme/icons.d.ts +20 -0
- package/docs/01-quickstart.mdx +18 -18
- package/docs/02-deployment.mdx +50 -26
- package/docs/03-upgrading.mdx +351 -0
- package/docs/04-migrating.mdx +58 -0
- package/docs/08-faq.mdx +3 -3
- package/docs/advanced/blog.mdx +13 -6
- package/docs/advanced/changelog.mdx +23 -35
- package/docs/advanced/custom-pages.mdx +20 -8
- package/docs/advanced/meta.ts +1 -8
- package/docs/advanced/skills.mdx +8 -0
- package/docs/cli/audit.mdx +646 -0
- package/docs/cli/doctor.mdx +29 -0
- package/docs/{reference/eval.mdx → cli/evals.mdx} +9 -8
- package/docs/cli/index.mdx +105 -0
- package/docs/cli/meta.ts +7 -0
- package/docs/{reference → cli}/translate.mdx +1 -1
- package/docs/cli/validate.mdx +41 -0
- package/docs/cli/version.mdx +40 -0
- package/docs/configuration/analytics.mdx +350 -59
- package/docs/configuration/ask-ai.mdx +162 -61
- package/docs/configuration/customization.mdx +15 -9
- package/docs/configuration/index.mdx +50 -31
- package/docs/configuration/search.mdx +75 -54
- package/docs/configuration/theming.mdx +24 -15
- package/docs/content/components.mdx +21 -5
- package/docs/{reference → content}/frontmatter.mdx +35 -1
- package/docs/content/i18n.mdx +8 -6
- package/docs/content/includes.mdx +2 -4
- package/docs/content/index.mdx +1 -1
- package/docs/content/islands.mdx +10 -5
- package/docs/content/meta.mdx +1 -1
- package/docs/content/meta.ts +1 -0
- package/docs/content/navigation.mdx +9 -7
- package/docs/content/sources.mdx +145 -50
- package/docs/content/syntax.mdx +16 -12
- package/docs/content/versioning.mdx +3 -3
- package/docs/discoverability/agent-discovery.mdx +11 -11
- package/docs/discoverability/index.mdx +4 -4
- package/docs/discoverability/json-api.mdx +4 -4
- package/docs/discoverability/llms-txt.mdx +6 -6
- package/docs/discoverability/markdown.mdx +4 -4
- package/docs/discoverability/mcp.mdx +11 -11
- package/docs/discoverability/sitemap-and-robots.mdx +3 -3
- package/docs/index.mdx +2 -2
- package/docs/references/asyncapi.mdx +59 -0
- package/docs/{advanced → references}/graphql.mdx +45 -32
- package/docs/{reference → references}/meta.ts +2 -2
- package/docs/references/openapi.mdx +171 -0
- package/docs/references/scalar.mdx +64 -0
- package/package.json +42 -8
- package/skills/blume/SKILL.md +21 -8
- package/skills/blume-migrate/SKILL.md +22 -21
- package/skills/blume-migrate/assets/oxfmt@0.67.0.patch +49 -0
- package/skills/blume-migrate/references/docusaurus.md +5 -4
- package/skills/blume-migrate/references/fumadocs.md +4 -4
- package/skills/blume-migrate/references/mintlify.md +23 -8
- package/skills/blume-migrate/references/monorepo.md +6 -6
- package/skills/blume-migrate/references/starlight.md +4 -4
- package/src/ai/agent-readability.ts +21 -22
- package/src/ai/ai-catalog.ts +50 -32
- package/src/ai/api/handlers.ts +46 -11
- package/src/ai/api-catalog.ts +8 -8
- package/src/ai/ask-data.ts +1 -1
- package/src/ai/ask.ts +624 -100
- package/src/ai/changelog-markdown.ts +91 -0
- package/src/ai/component-markdown.ts +328 -12
- package/src/ai/index.ts +45 -0
- package/src/ai/link-headers.ts +7 -6
- package/src/ai/llms.ts +20 -15
- package/src/ai/markdown.ts +35 -5
- package/src/ai/mcp/data.ts +9 -5
- package/src/ai/openapi-components.ts +4 -1
- package/src/ai/relative-links.ts +170 -0
- package/src/ai/serializers.ts +2 -2
- package/src/ai/skills.ts +1 -1
- package/src/ai/web-bot-auth.ts +2 -2
- package/src/analytics/adobe.ts +46 -0
- package/src/analytics/amplitude.ts +79 -0
- package/src/analytics/clarity.ts +46 -0
- package/src/analytics/clearbit.ts +47 -0
- package/src/analytics/cloudflare.ts +67 -0
- package/src/analytics/fathom.ts +64 -0
- package/src/analytics/google-analytics.ts +84 -0
- package/src/analytics/google-tag-manager.ts +61 -0
- package/src/analytics/head.ts +130 -0
- package/src/analytics/heap.ts +65 -0
- package/src/analytics/hightouch.ts +77 -0
- package/src/analytics/hotjar.ts +47 -0
- package/src/analytics/index.ts +71 -0
- package/src/analytics/inline.ts +21 -0
- package/src/analytics/logrocket.ts +71 -0
- package/src/analytics/mixpanel.ts +92 -0
- package/src/analytics/pirsch.ts +66 -0
- package/src/analytics/plausible.ts +83 -0
- package/src/analytics/posthog.ts +78 -0
- package/src/analytics/schema.ts +60 -0
- package/src/analytics/script.ts +60 -0
- package/src/analytics/segment.ts +85 -0
- package/src/analytics/vercel.ts +50 -0
- package/src/astro/adapter-root.ts +7 -9
- package/src/astro/component-slots.ts +131 -91
- package/src/astro/generate.ts +112 -345
- package/src/astro/integration.ts +2 -6
- package/src/astro/pages.ts +13 -101
- package/src/astro/render-deps.ts +379 -0
- package/src/astro/runtime-deps.ts +201 -0
- package/src/astro/templates.ts +554 -541
- package/src/audit/agent.ts +24 -0
- package/src/audit/catalog.ts +2 -2
- package/src/audit/checks/assets.ts +2 -2
- package/src/audit/checks/dns-aid.ts +1 -1
- package/src/audit/checks/duplicates.ts +3 -1
- package/src/audit/checks/i18n.ts +1 -1
- package/src/audit/checks/indexability.ts +7 -7
- package/src/audit/checks/links.ts +2 -2
- package/src/audit/checks/llms.ts +6 -6
- package/src/audit/checks/network.ts +3 -3
- package/src/audit/checks/og-image.ts +2 -2
- package/src/audit/checks/robots.ts +1 -1
- package/src/audit/checks/sitemap.ts +2 -2
- package/src/audit/checks/social.ts +1 -1
- package/src/audit/run.ts +4 -3
- package/src/audit/terms.ts +31 -0
- package/src/audit/url.ts +13 -13
- package/src/cli/command-meta.ts +10 -0
- package/src/cli/commands/audit.ts +39 -20
- package/src/cli/commands/build.ts +71 -316
- package/src/cli/commands/check.ts +1 -0
- package/src/cli/commands/dev.ts +40 -1
- package/src/cli/commands/doctor.ts +90 -14
- package/src/cli/commands/eject.ts +44 -7
- package/src/cli/commands/eval.ts +2 -2
- package/src/cli/commands/init.ts +161 -41
- package/src/cli/commands/migrate.ts +121 -0
- package/src/cli/commands/preview.ts +7 -1
- package/src/cli/commands/translate.ts +2 -2
- package/src/cli/commands/upgrade.ts +141 -0
- package/src/cli/commands/version.ts +57 -44
- package/src/cli/eject-scripts.ts +121 -6
- package/src/cli/index.ts +11 -1
- package/src/cli/init/install.ts +70 -0
- package/src/cli/init/questions.ts +7 -0
- package/src/cli/init/scaffold.ts +459 -75
- package/src/cli/lazy-command.ts +37 -1
- package/src/cli/prepare.ts +40 -6
- package/src/cli/required-secrets.ts +38 -11
- package/src/cli/unknown-flags.ts +266 -0
- package/src/cli/yarn-pnp.ts +52 -0
- package/src/components/content/AccordionItem.astro +7 -1
- package/src/components/content/Card.astro +4 -3
- package/src/components/content/ColorItem.astro +22 -4
- package/src/components/content/GithubInfo.astro +2 -2
- package/src/components/content/Prompt.astro +25 -25
- package/src/components/content/Tabs.astro +3 -1
- package/src/components/content/Tile.astro +2 -3
- package/src/components/content/Tooltip.astro +57 -9
- package/src/components/content/content-strings.ts +34 -0
- package/src/components/content/diff.ts +1 -1
- package/src/components/content/mermaid-element.ts +13 -2
- package/src/components/content/prompt-markdown.ts +292 -0
- package/src/components/content/tooltip-id.ts +41 -0
- package/src/components/islands/hooks.ts +25 -2
- package/src/components/layout/Analytics.astro +21 -79
- package/src/components/layout/Banner.astro +3 -1
- package/src/components/layout/DiscoveryLinks.astro +69 -0
- package/src/components/layout/Header.astro +83 -20
- package/src/components/layout/LanguageSwitcher.astro +4 -1
- package/src/components/layout/Logo.astro +23 -2
- package/src/components/layout/NavSelector.astro +13 -2
- package/src/components/layout/NavTree.astro +12 -3
- package/src/components/layout/NavTreeScript.astro +17 -3
- package/src/components/layout/PageActions.astro +3 -3
- package/src/components/layout/PageFeedback.astro +11 -1
- package/src/components/layout/PageLayout.astro +52 -5
- package/src/components/layout/ReferenceLayout.astro +21 -12
- package/src/components/layout/RootLayout.astro +69 -66
- package/src/components/layout/Search.astro +30 -6
- package/src/components/layout/WebMcp.astro +1 -1
- package/src/components/layout/analytics-client.ts +72 -15
- package/src/components/layout/drawer-inert.ts +113 -15
- package/src/components/layout/dropdown-clamp.ts +105 -0
- package/src/components/layout/head-scripts.ts +15 -6
- package/src/components/layout/nav-utils.ts +17 -0
- package/src/components/layout/search/algolia.ts +8 -8
- package/src/components/layout/search/orama-cloud.ts +9 -7
- package/src/components/layout/search/typesense.ts +11 -17
- package/src/components/openapi/MessageComposer.astro +2 -2
- package/src/components/openapi/Playground.astro +2 -2
- package/src/components/openapi/playground-client.ts +79 -10
- package/src/core/changelog-index.ts +24 -0
- package/src/core/component-overrides.ts +399 -154
- package/src/core/config-input.ts +188 -553
- package/src/core/config.ts +130 -41
- package/src/core/custom-pages.ts +105 -0
- package/src/core/data.ts +31 -12
- package/src/core/define-components.ts +12 -9
- package/src/core/deployment-env.ts +18 -74
- package/src/core/diagnostics.ts +14 -7
- package/src/core/graph.ts +69 -25
- package/src/core/i18n-ui.ts +6 -2
- package/src/core/i18n.ts +17 -0
- package/src/core/includes.ts +156 -38
- package/src/core/last-modified.ts +6 -11
- package/src/core/links.ts +166 -2
- package/src/core/manifest.ts +10 -3
- package/src/core/navigation.ts +115 -16
- package/src/core/new-tab.ts +35 -0
- package/src/core/node-require.ts +21 -0
- package/src/core/project-graph.ts +42 -26
- package/src/core/project.ts +9 -4
- package/src/core/request-body.ts +61 -0
- package/src/core/safe-href.ts +28 -0
- package/src/core/safe-links.ts +68 -0
- package/src/core/schema.ts +517 -739
- package/src/core/server-features.ts +8 -9
- package/src/core/sources/assets.ts +47 -11
- package/src/core/sources/collection.ts +67 -0
- package/src/core/sources/contentful-rich-text.ts +285 -0
- package/src/core/sources/contentful.ts +173 -0
- package/src/core/sources/github-releases.ts +51 -1
- package/src/core/sources/json.ts +71 -0
- package/src/core/sources/lexical.ts +195 -0
- package/src/core/sources/lower.ts +226 -0
- package/src/core/sources/normalize.ts +52 -2
- package/src/core/sources/notion.ts +39 -28
- package/src/core/sources/payload.ts +135 -0
- package/src/core/sources/portable-text.ts +11 -16
- package/src/core/sources/remote.ts +226 -0
- package/src/core/sources/resolve.ts +104 -166
- package/src/core/sources/sanity.ts +16 -49
- package/src/core/sources/strapi-blocks.ts +124 -0
- package/src/core/sources/strapi.ts +191 -0
- package/src/core/sources/types.ts +12 -1
- package/src/core/types.ts +15 -1
- package/src/core/ui-packs/ar.ts +6 -2
- package/src/core/ui-packs/bg.ts +6 -2
- package/src/core/ui-packs/bn.ts +6 -2
- package/src/core/ui-packs/ca.ts +3 -1
- package/src/core/ui-packs/cs.ts +6 -2
- package/src/core/ui-packs/da.ts +6 -2
- package/src/core/ui-packs/de.ts +6 -2
- package/src/core/ui-packs/el.ts +3 -1
- package/src/core/ui-packs/es.ts +3 -1
- package/src/core/ui-packs/fa.ts +6 -2
- package/src/core/ui-packs/fi.ts +6 -2
- package/src/core/ui-packs/fr.ts +3 -1
- package/src/core/ui-packs/he.ts +6 -2
- package/src/core/ui-packs/hi.ts +6 -2
- package/src/core/ui-packs/hr.ts +6 -2
- package/src/core/ui-packs/hu.ts +6 -2
- package/src/core/ui-packs/id.ts +6 -2
- package/src/core/ui-packs/it.ts +3 -1
- package/src/core/ui-packs/ja.ts +3 -1
- package/src/core/ui-packs/ko.ts +3 -1
- package/src/core/ui-packs/nl.ts +6 -2
- package/src/core/ui-packs/no.ts +6 -2
- package/src/core/ui-packs/pl.ts +6 -2
- package/src/core/ui-packs/pt-br.ts +3 -1
- package/src/core/ui-packs/pt.ts +3 -1
- package/src/core/ui-packs/ro.ts +6 -2
- package/src/core/ui-packs/ru.ts +6 -2
- package/src/core/ui-packs/sk.ts +6 -2
- package/src/core/ui-packs/sr.ts +6 -2
- package/src/core/ui-packs/sv.ts +6 -2
- package/src/core/ui-packs/th.ts +3 -1
- package/src/core/ui-packs/tr.ts +6 -2
- package/src/core/ui-packs/uk.ts +6 -2
- package/src/core/ui-packs/vi.ts +3 -1
- package/src/core/ui-packs/zh-tw.ts +3 -1
- package/src/core/ui-packs/zh.ts +3 -1
- package/src/core/unrecognized-keys.ts +10 -0
- package/src/core/version-cut.ts +116 -8
- package/src/deploy/adapter-output.ts +57 -97
- package/src/deploy/adapters/cloudflare.ts +39 -0
- package/src/deploy/adapters/index.ts +41 -0
- package/src/deploy/adapters/netlify.ts +34 -0
- package/src/deploy/adapters/node.ts +34 -0
- package/src/deploy/adapters/registry.ts +127 -0
- package/src/deploy/adapters/types.ts +92 -0
- package/src/deploy/adapters/vercel.ts +35 -0
- package/src/deploy/artifacts.ts +55 -27
- package/src/deploy/cloudflare-negotiation.ts +179 -100
- package/src/deploy/function-bundle.ts +18 -3
- package/src/deploy/headers.ts +124 -23
- package/src/deploy/node-headers.ts +198 -0
- package/src/deploy/platforms/cloudflare.ts +312 -0
- package/src/deploy/platforms/index.ts +67 -0
- package/src/deploy/platforms/netlify.ts +52 -0
- package/src/deploy/platforms/node.ts +40 -0
- package/src/deploy/platforms/paths.ts +42 -0
- package/src/deploy/platforms/static.ts +29 -0
- package/src/deploy/platforms/types.ts +112 -0
- package/src/deploy/platforms/vercel.ts +193 -0
- package/src/deploy/redirects.ts +30 -18
- package/src/deploy/robots.ts +3 -3
- package/src/deploy/rss.ts +2 -2
- package/src/deploy/sitemap.ts +2 -2
- package/src/deploy/vercel-negotiation.ts +2 -2
- package/src/eval/agents.ts +32 -1
- package/src/eval/findings.ts +19 -11
- package/src/eval/report.ts +10 -2
- package/src/markdown/external-links.ts +65 -0
- package/src/markdown/include.ts +45 -27
- package/src/markdown/index.ts +34 -9
- package/src/markdown/inline-code.ts +12 -6
- package/src/markdown/relative-links.ts +324 -0
- package/src/markdown/themes.ts +3 -3
- package/src/migrate/migrate.ts +149 -0
- package/src/openapi/parse.ts +64 -16
- package/src/openapi/proxy.ts +62 -10
- package/src/openapi/references.ts +147 -147
- package/src/openapi/render-mdx.ts +32 -10
- package/src/openapi/scalar.ts +15 -13
- package/src/openapi/sentence.ts +14 -0
- package/src/openapi/source.ts +37 -21
- package/src/openapi/spec-dependency-error.ts +15 -0
- package/src/reference/asyncapi.ts +83 -0
- package/src/reference/graphql.ts +89 -0
- package/src/reference/index.ts +40 -0
- package/src/reference/openapi.ts +83 -0
- package/src/reference/options.ts +201 -0
- package/src/reference/scalar.ts +136 -0
- package/src/reference/schema.ts +88 -0
- package/src/registry/eject.ts +97 -42
- package/src/search/adapters/algolia.ts +46 -0
- package/src/search/adapters/flexsearch.ts +29 -0
- package/src/search/adapters/index.ts +39 -0
- package/src/search/adapters/mixedbread.ts +39 -0
- package/src/search/adapters/orama-cloud.ts +51 -0
- package/src/search/adapters/orama.ts +24 -0
- package/src/search/adapters/pagefind.ts +27 -0
- package/src/search/adapters/registry.ts +131 -0
- package/src/search/adapters/types.ts +46 -0
- package/src/search/adapters/typesense.ts +57 -0
- package/src/search/build.ts +8 -4
- package/src/search/documents.ts +6 -0
- package/src/search/sync/algolia.ts +12 -11
- package/src/search/sync/index.ts +35 -20
- package/src/search/sync/orama-cloud.ts +12 -9
- package/src/search/sync/typesense.ts +15 -13
- package/src/sources/contentful.ts +63 -0
- package/src/sources/custom.ts +38 -0
- package/src/sources/filesystem.ts +51 -0
- package/src/sources/github-releases.ts +52 -0
- package/src/sources/index.ts +60 -0
- package/src/sources/mdx-remote.ts +76 -0
- package/src/sources/notion.ts +63 -0
- package/src/sources/obsidian.ts +38 -0
- package/src/sources/payload.ts +60 -0
- package/src/sources/registry.ts +182 -0
- package/src/sources/sanity.ts +66 -0
- package/src/sources/shared.ts +52 -0
- package/src/sources/strapi.ts +58 -0
- package/src/theme/entry.ts +24 -2
- package/src/translate/report.ts +40 -7
- package/src/upgrade/upgrade.ts +499 -0
- package/dist/cli/chunk-0qymqwzz.js +0 -164
- package/dist/cli/chunk-0qymqwzz.js.map +0 -15
- package/dist/cli/chunk-1jefwnfs.js.map +0 -48
- package/dist/cli/chunk-2mzebbbz.js +0 -69
- package/dist/cli/chunk-2z47ypj8.js.map +0 -11
- package/dist/cli/chunk-4x36ddpw.js.map +0 -11
- package/dist/cli/chunk-5093q3n7.js +0 -68
- package/dist/cli/chunk-5093q3n7.js.map +0 -10
- package/dist/cli/chunk-5qk08vmp.js.map +0 -11
- package/dist/cli/chunk-7s8hm3b6.js +0 -5347
- package/dist/cli/chunk-7s8hm3b6.js.map +0 -58
- package/dist/cli/chunk-8cjtbafj.js.map +0 -13
- package/dist/cli/chunk-97r59kpr.js +0 -381
- package/dist/cli/chunk-97r59kpr.js.map +0 -12
- package/dist/cli/chunk-ahnw3kxw.js.map +0 -15
- package/dist/cli/chunk-b27xqwn9.js.map +0 -10
- package/dist/cli/chunk-bf6bt1xt.js +0 -185
- package/dist/cli/chunk-bf6bt1xt.js.map +0 -11
- package/dist/cli/chunk-cjtn640a.js.map +0 -10
- package/dist/cli/chunk-ct47dqpx.js.map +0 -11
- package/dist/cli/chunk-esphfr8p.js +0 -107
- package/dist/cli/chunk-esphfr8p.js.map +0 -11
- package/dist/cli/chunk-ex56aa81.js +0 -1016
- package/dist/cli/chunk-ex56aa81.js.map +0 -13
- package/dist/cli/chunk-garjf5z9.js +0 -30
- package/dist/cli/chunk-garjf5z9.js.map +0 -10
- package/dist/cli/chunk-js7saxwm.js +0 -1045
- package/dist/cli/chunk-js7saxwm.js.map +0 -22
- package/dist/cli/chunk-k79xp7av.js.map +0 -39
- package/dist/cli/chunk-ps4m1xh4.js.map +0 -15
- package/dist/cli/chunk-q4rae3bg.js +0 -60
- package/dist/cli/chunk-q4rae3bg.js.map +0 -10
- package/dist/cli/chunk-rz9jmfhz.js +0 -108
- package/dist/cli/chunk-rz9jmfhz.js.map +0 -10
- package/dist/cli/chunk-vh9w1sgp.js +0 -73
- package/dist/cli/chunk-vh9w1sgp.js.map +0 -10
- package/dist/cli/chunk-vrfp10qk.js +0 -81
- package/dist/cli/chunk-vrfp10qk.js.map +0 -10
- package/dist/cli/chunk-yg63d42r.js.map +0 -34
- package/docs/advanced/api-reference.mdx +0 -240
- package/docs/reference/cli.mdx +0 -197
- package/skills/blume-migrate/assets/oxfmt@0.55.0.patch +0 -20
- package/src/components/content/changelog-element.ts +0 -69
- package/src/search/providers.ts +0 -91
- /package/dist/cli/{chunk-e7f42gdj.js.map → chunk-7ez8ny0t.js.map} +0 -0
- /package/dist/cli/{chunk-nn13znc2.js.map → chunk-88cpgt6h.js.map} +0 -0
- /package/dist/cli/{chunk-vacwm2hv.js.map → chunk-f7t03s3g.js.map} +0 -0
- /package/dist/cli/{chunk-dwgcp5sm.js.map → chunk-zxccj738.js.map} +0 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/astro/integration.ts", "../src/deploy/artifacts.ts", "../src/search/build.ts", "../src/deploy/sitemap.ts", "../src/og/cache.ts", "../src/astro/generate.ts", "../src/ai/api/problem.ts", "../src/ai/api/spec.ts", "../src/ai/ask-data.ts", "../src/components/layout/nav-utils.ts", "../src/core/builtin-tags.ts", "../src/core/component-diagnostics.ts", "../src/core/svg-dimensions.ts", "../src/core/tsconfig-aliases.ts", "../src/markdown/language-icon.ts", "../src/markdown/mermaid.ts", "../src/og/derive.ts", "../src/og/logo.ts", "../src/openapi/scalar.ts", "../src/theme/palette.ts", "../src/search/popular.ts", "../src/theme/code-block-padding.ts", "../src/theme/entry.ts", "../src/theme/sources.ts", "../src/theme/twoslash.ts", "../src/astro/examples.ts", "../src/astro/render-deps.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { fileURLToPath } from \"node:url\";\n\nimport type { AstroIntegration } from \"astro\";\nimport { join, relative, resolve } from \"pathe\";\n\nimport { loadEnvFiles } from \"../cli/env.ts\";\nimport type { CustomPageRoute } from \"../core/custom-pages.ts\";\nimport { enrichDiagnostic } from \"../core/diagnostics.ts\";\nimport { scanProject } from \"../core/project-graph.ts\";\nimport type { BlumeProject } from \"../core/project-graph.ts\";\nimport type { Diagnostic } from \"../core/types.ts\";\nimport { publishBuildArtifacts } from \"../deploy/artifacts.ts\";\nimport type { ArtifactLogger } from \"../deploy/artifacts.ts\";\nimport { markdownVariantUrl, prefersMarkdown } from \"./markdown-negotiation.ts\";\nimport { runtimeModuleDeclarations } from \"./module-types.ts\";\n\n/** The `{ type: \"error\" }` payload Vite's browser overlay renders. */\ninterface OverlayErrorPayload {\n err: {\n id?: string;\n message: string;\n plugin: string;\n stack: string;\n };\n type: \"error\";\n}\n\n/** The dev server's HMR channel — either `.ws` (Vite ≤5) or `.hot` (Vite 6+). */\ninterface OverlayChannel {\n send: (payload: OverlayErrorPayload) => void;\n}\ninterface OverlayServer {\n hot?: OverlayChannel;\n ws?: OverlayChannel;\n}\n\n/** The parameters Astro hands `astro:server:setup`. */\ntype ServerSetupParams = Parameters<\n NonNullable<AstroIntegration[\"hooks\"][\"astro:server:setup\"]>\n>[0];\n\n/**\n * Astro's `refreshContent`: re-runs every content-layer loader against the\n * live store, the sanctioned way to re-sync content that changed outside\n * Astro's own file watcher.\n */\ntype RefreshContent = NonNullable<ServerSetupParams[\"refreshContent\"]>;\n\n/** What the dev negotiation middleware needs per request. */\ninterface DevNegotiation {\n /** Page routes that have a raw-Markdown variant (the content manifest). */\n contentRoutes: ReadonlySet<string>;\n /** Homepage agent-discovery `Link` header, when the site has one. */\n homeLinkHeader?: string;\n}\n\n/**\n * The state shared between the CLI and the integration: the live dev server\n * (for the browser error overlay), Astro's `refreshContent` (so the CLI's\n * regeneration can re-sync the content store instead of restarting the\n * server), the negotiation inputs the CLI publishes on every regeneration (so\n * a content-route change never rewrites the generated config, which would\n * restart the server in place), and the scanned project a `blume build`\n * hands over so `astro:build:done` can write the deploy artifacts.\n *\n * Kept on `globalThis` rather than in module state, for the same reason as\n * the runtime-module registry (see `runtime-modules.ts`): on a published\n * install the CLI bundle (`dist/cli`) carries its own copy of this module,\n * while the hooks run in the copy Vite loads from `blume/astro` for the\n * generated config. A module-level variable is set in one copy and read in\n * the other, so the overlay never showed anything outside this repository.\n */\ninterface DevServerRegistry {\n buildProject: BlumeProject | null;\n negotiation: DevNegotiation | null;\n overlay: OverlayServer | null;\n refreshContent: RefreshContent | null;\n}\n\nconst REGISTRY_KEY = Symbol.for(\"blume.integration\");\n\ntype RegistryHost = typeof globalThis & {\n [REGISTRY_KEY]?: DevServerRegistry;\n};\n\nconst registry = (): DevServerRegistry => {\n // SAFETY: the registry is stashed on globalThis under a well-known symbol so\n // every copy of this module in the process shares it; the intersection only\n // names that slot.\n const host = globalThis as RegistryHost;\n host[REGISTRY_KEY] ??= {\n buildProject: null,\n negotiation: null,\n overlay: null,\n refreshContent: null,\n };\n return host[REGISTRY_KEY];\n};\n\n/**\n * Hand the scanned project to the integration ahead of `build()`, so its\n * `astro:build:done` hook can write the deploy artifacts without scanning\n * again. `blume build` publishes it for a real build and nothing for an\n * isolated verify build, which produces no artifacts. `null` withdraws it.\n */\nexport const publishBuildProject = (project: BlumeProject | null): void => {\n registry().buildProject = project;\n};\n\n/**\n * The project an `astro build` with no CLI in front of it (an ejected app)\n * writes artifacts for: scanned from `buildArtifactsRoot`, resolved against\n * the Astro root recorded on `astro:config:done`. Scan diagnostics surface as\n * warnings — there is no `--strict` to honor here, and the build itself has\n * already succeeded. `null` when nothing asked for a scan.\n */\nconst scanForArtifacts = async (\n astroRoot: URL | null,\n artifactsRoot: string | undefined,\n logger: ArtifactLogger\n): Promise<BlumeProject | null> => {\n if (!(astroRoot && artifactsRoot)) {\n return null;\n }\n const root = resolve(fileURLToPath(astroRoot), artifactsRoot);\n // Remote sources read their tokens from the environment during the scan.\n loadEnvFiles(root);\n const project = await scanProject(root, { mode: \"build\" });\n for (const diagnostic of project.diagnostics) {\n logger.warn(`[${diagnostic.code}] ${diagnostic.message}`);\n }\n return project;\n};\n\n/**\n * Publish the dev negotiation inputs for the running server: the content\n * routes with a Markdown variant and the homepage `Link` header. Called by\n * `generateRuntime` on every pass, so the middleware follows a route rename\n * without the generated config changing. `null` withdraws a publication, so\n * the middleware falls back to the options baked into the integration call\n * (an ejected project has no CLI to publish).\n */\nexport const publishDevNegotiation = (\n negotiation: {\n contentRoutes: readonly string[];\n homeLinkHeader?: string;\n } | null\n): void => {\n registry().negotiation = negotiation\n ? {\n contentRoutes: new Set(negotiation.contentRoutes),\n homeLinkHeader: negotiation.homeLinkHeader,\n }\n : null;\n};\n\n/**\n * Re-run Astro's content-layer loaders against the live dev server. Returns\n * `false` when no server has registered one — before the first\n * `astro:server:setup`, or outside `blume dev` — so the caller can fall back.\n * A route-set change (a page added, removed, or a folder renamed) is what\n * needs this: Astro's glob watcher misses directory renames, and its in-place\n * config restart never re-globs, so without a re-sync `getEntry` reads a\n * stale store and the moved page 404s.\n */\nexport const refreshBlumeContent = async (): Promise<boolean> => {\n const { refreshContent } = registry();\n if (!refreshContent) {\n return false;\n }\n // No loader filter: every collection re-syncs (the docs glob and any\n // staged collection alike).\n await refreshContent({});\n return true;\n};\n\nconst overlayChannel = (): OverlayChannel | undefined => {\n const { overlay } = registry();\n return overlay?.ws ?? overlay?.hot;\n};\n\n/**\n * Surface Blume's own diagnostics (config/frontmatter/content errors) in the\n * Vite/Astro browser error overlay during `blume dev`, so they don't hide in the\n * terminal. A no-op when there are no errors or the dev server isn't up. The\n * overlay clears itself on the next successful HMR update.\n */\nexport const showBlumeErrorOverlay = (diagnostics: Diagnostic[]): void => {\n const errors: Diagnostic[] = [];\n for (const diagnostic of diagnostics) {\n if (diagnostic.severity === \"error\") {\n errors.push(enrichDiagnostic(diagnostic));\n }\n }\n const channel = overlayChannel();\n if (errors.length === 0 || !channel) {\n return;\n }\n const body = errors\n .map((diagnostic) => {\n const lineSuffix = diagnostic.line ? `:${diagnostic.line}` : \"\";\n const where = diagnostic.file\n ? `\\n at ${diagnostic.file}${lineSuffix}`\n : \"\";\n const fix = diagnostic.suggestion\n ? `\\n fix: ${diagnostic.suggestion}`\n : \"\";\n const docs = diagnostic.docsUrl ? `\\n docs: ${diagnostic.docsUrl}` : \"\";\n return `[${diagnostic.code}] ${diagnostic.message}${where}${fix}${docs}`;\n })\n .join(\"\\n\\n\");\n channel.send({\n err: {\n id: errors[0]?.file,\n message: `Blume found ${errors.length} error(s):\\n\\n${body}`,\n plugin: \"blume\",\n stack: \"\",\n },\n type: \"error\",\n });\n};\n\n/** A user page mounted into the generated runtime. */\nexport type BlumePageRoute = CustomPageRoute;\n\nexport interface BlumeIntegrationOptions {\n pages: BlumePageRoute[];\n /**\n * Page routes that have a raw-Markdown variant (the content manifest). The\n * hidden runtime leaves this out: the CLI publishes the live set through\n * `publishDevNegotiation` on every regeneration, so a route change never\n * rewrites the generated config. An ejected project, which has no CLI,\n * bakes it in here.\n */\n contentRoutes?: string[];\n /**\n * Homepage `Link` header value for agent discovery (see\n * `ai/link-headers.ts`); the dev-server counterpart of the `_headers` /\n * Vercel-config emission, so `curl -I` against `blume dev` shows what the\n * deployed site will send. Published the same way as `contentRoutes`.\n */\n homeLinkHeader?: string;\n /**\n * Blume project root to scan on `astro:build:done` for the deploy artifacts\n * (search index, llms.txt, sitemap, …) when no CLI has published the\n * project — an ejected app running plain `astro build`. Relative to the\n * Astro root. The hidden runtime leaves it unset: `blume build` publishes\n * its already-scanned project instead.\n */\n buildArtifactsRoot?: string;\n}\n\n/**\n * Whether a dev-server request URL is the homepage: the path (query dropped,\n * trailing slash tolerated) is the root.\n */\nconst isHomeUrl = (rawUrl: string | undefined): boolean => {\n if (!rawUrl) {\n return false;\n }\n const queryIndex = rawUrl.indexOf(\"?\");\n const path = queryIndex === -1 ? rawUrl : rawUrl.slice(0, queryIndex);\n return path === \"\" || path === \"/\";\n};\n\n/**\n * Dev-server content negotiation: when a client asks for `text/markdown`,\n * transparently rewrite a content-page request to its `.md` variant so the\n * existing raw-Markdown endpoint serves it. Runs only under `blume dev` — in\n * production the content pages are prerendered and served from the platform's\n * static layer, which this middleware never fronts. Vercel server builds get\n * the same negotiation from routing rules spliced into the Build Output config\n * (see `deploy/vercel-negotiation.ts`), Cloudflare server builds from a\n * wrapper Worker routed to by `assets.run_worker_first` (see\n * `deploy/cloudflare-negotiation.ts`); every other build exposes the same\n * content at the `.md` URL. Only routes with a Markdown variant are rewritten,\n * so user `.astro` pages keep serving HTML — except the homepage, whose\n * variant falls back to the synthesized llms.txt mirror when it's a landing\n * page (see `markdownRoutePaths`). The same\n * middleware also stamps the homepage agent-discovery `Link` header, mirroring\n * what the deployed site sends via `_headers` / the Vercel routing config.\n *\n * Request URLs arrive base-less: Astro unshifts its own dev middlewares (base,\n * trailing slash, route guard) ahead of this one from its post-`configureServer`\n * hook, and its base middleware has already rewritten `/<base>/guide` to\n * `/guide`. Stripping `deployment.base` here a second time would leave no\n * request matching a content route.\n */\nconst negotiateMarkdown =\n (fallback: DevNegotiation) =>\n (req: IncomingMessage, res: ServerResponse, next: () => void): void => {\n // Read per request: the CLI republishes on every regeneration, so a page\n // renamed while the server runs negotiates under its new route.\n const { contentRoutes, homeLinkHeader } =\n registry().negotiation ?? fallback;\n if (req.method === \"GET\" || req.method === \"HEAD\") {\n if (homeLinkHeader && isHomeUrl(req.url)) {\n res.setHeader(\"Link\", homeLinkHeader);\n }\n if (prefersMarkdown(req.headers.accept)) {\n const variant = markdownVariantUrl(req.url, contentRoutes);\n if (variant) {\n res.setHeader(\"Vary\", \"Accept\");\n req.url = variant;\n }\n }\n }\n next();\n };\n\n/** The `.d.ts` the integration injects for the `blume:*` virtual modules. */\nconst MODULE_TYPES_FILE = \"modules.d.ts\";\n\n/**\n * Where Astro writes an integration's injected types when the integration\n * never asked for its codegen dir (a config run whose `astro:config:setup`\n * was skipped — the test fixtures). Mirrors Astro's own convention.\n */\nconst defaultCodegenDir = (root: URL): URL =>\n new URL(\".astro/integrations/blume/\", root);\n\n/**\n * Blume's Astro integration. Mounts user-authored pages from `pages/` into the\n * generated runtime via `injectRoute`, keeping each file in its original\n * location so relative imports and `getStaticPaths` keep working; declares\n * the `blume:*` virtual modules' types through `injectTypes`; and teaches the\n * dev server to honor `Accept: text/markdown`.\n */\nexport const blumeIntegration = (\n options: BlumeIntegrationOptions\n): AstroIntegration => {\n // Astro hands out the codegen dir on `astro:config:setup`; the types are\n // injected on `astro:config:done`, once `srcDir` is final, and the\n // `blume:examples` declaration needs the path between the two.\n let codegenDir: URL | null = null;\n // The Astro root, kept for the build-artifacts scan: `astro:build:done`\n // receives no config.\n let astroRoot: URL | null = null;\n return {\n hooks: {\n \"astro:build:done\": async ({ dir, logger }) => {\n const project =\n registry().buildProject ??\n (await scanForArtifacts(\n astroRoot,\n options.buildArtifactsRoot,\n logger\n ));\n if (!project) {\n return;\n }\n // `dir` is what Astro reports as the client output — `dist/`, or\n // `dist/client` for a server build — which is what the platform\n // serves (the Vercel adapter copies it into its Build Output static\n // tree in a later hook).\n await publishBuildArtifacts(project, fileURLToPath(dir), logger);\n },\n \"astro:config:done\": ({ config, injectTypes }) => {\n astroRoot = config.root;\n const from = fileURLToPath(\n codegenDir ?? defaultCodegenDir(config.root)\n );\n const examplesModule = relative(\n from,\n join(fileURLToPath(config.srcDir), \"generated\", \"examples.ts\")\n );\n injectTypes({\n content: runtimeModuleDeclarations(examplesModule),\n filename: MODULE_TYPES_FILE,\n });\n },\n \"astro:config:setup\": ({\n addMiddleware,\n createCodegenDir,\n injectRoute,\n }) => {\n codegenDir = createCodegenDir();\n // Splices each page's icon sprite in once the page has rendered (see\n // components/icon-sprite-middleware.ts). Innermost, so a project's\n // own middleware sees the finished HTML.\n addMiddleware({\n entrypoint: \"blume/components/icon-sprite-middleware.ts\",\n order: \"post\",\n });\n for (const page of options.pages) {\n injectRoute({\n entrypoint: page.entrypoint,\n pattern: page.pattern,\n prerender: true,\n });\n }\n },\n \"astro:server:setup\": ({ refreshContent, server }) => {\n // Keep a handle on the dev server so Blume diagnostics can be pushed\n // to its browser error overlay (see `showBlumeErrorOverlay`), and on\n // Astro's content re-sync so a route-set change needs no restart\n // (see `refreshBlumeContent`).\n const shared = registry();\n shared.overlay = server;\n shared.refreshContent = refreshContent ?? null;\n // Prepend so the rewrite happens before Astro's own request handler,\n // letting the rewritten URL resolve to the `.md` endpoint.\n server.middlewares.stack.unshift({\n handle: negotiateMarkdown({\n contentRoutes: new Set(options.contentRoutes),\n homeLinkHeader: options.homeLinkHeader,\n }),\n route: \"\",\n });\n },\n },\n name: \"blume\",\n };\n};\n",
|
|
6
|
+
"import { existsSync } from \"node:fs\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\n\nimport { dirname, join, resolve } from \"pathe\";\n\nimport { buildAgentReadability } from \"../ai/agent-readability.ts\";\nimport {\n AI_CATALOG_PATH,\n ARD_MANIFEST_PATH,\n buildAiCatalog,\n} from \"../ai/ai-catalog.ts\";\nimport { API_CATALOG_PATH, buildApiCatalog } from \"../ai/api-catalog.ts\";\nimport { buildHomeLinkHeader } from \"../ai/link-headers.ts\";\nimport { buildLlmsFiles } from \"../ai/llms.ts\";\nimport { markdownRoutePaths } from \"../ai/markdown.ts\";\nimport {\n AGENT_SKILLS_DIR,\n buildSkillsIndex,\n collectSkills,\n} from \"../ai/skills.ts\";\nimport type { SkillArtifact } from \"../ai/skills.ts\";\nimport {\n buildSignaturesDirectory,\n SIGNATURES_DIRECTORY_PATH,\n} from \"../ai/web-bot-auth.ts\";\nimport type { BlumeProject } from \"../core/project-graph.ts\";\nimport type { ResolvedConfig } from \"../core/schema.ts\";\nimport { buildSearchIndex } from \"../search/build.ts\";\nimport { syncSearchProvider } from \"../search/sync/index.ts\";\nimport { readsHeaderFiles } from \"./adapter-output.ts\";\nimport { buildNetlifyHeaders, buildVercelHeaders } from \"./headers.ts\";\nimport { deployPlatform } from \"./platforms/index.ts\";\nimport { VERCEL_JSON_FILE } from \"./platforms/vercel.ts\";\nimport {\n buildRedirectManifest,\n buildVercelConfig,\n platformRedirects,\n} from \"./redirects.ts\";\nimport { buildRobots } from \"./robots.ts\";\nimport { buildSitemapFiles, describeSitemapFiles } from \"./sitemap.ts\";\n\n/**\n * The deploy artifacts Blume layers onto Astro's build output: the search\n * index (and hosted-provider sync), llms.txt, sitemap, robots, the\n * agent-readability manifest, the `.well-known` discovery files, Agent\n * Skills, and the platform `_redirects`/`_headers` files.\n *\n * Written from the integration's `astro:build:done` hook — Astro's channel\n * for exactly this kind of post-build work — into the directory Astro\n * reports as the client output (`dist/`, or `dist/client` for a server\n * build). The Vercel adapter copies that directory into its Build Output\n * static tree in a later hook, so the artifacts ride along; every other\n * adapter serves it directly. Running inside the hook rather than after\n * `astro build` returns means an ejected project keeps producing them.\n *\n * Every file yields to one the user ships in `public/` (Astro copied it into\n * the output before this runs).\n */\n\n/** The log surface the writers report through (Astro's integration logger). */\nexport interface ArtifactLogger {\n info: (message: string) => void;\n warn: (message: string) => void;\n}\n\n/**\n * Emit platform redirect files for a static build (a server build answers\n * redirects at request time): the files the deployment's platform reads\n * (`_redirects`, `vercel.json` — every one of them when no host is named),\n * each only when the user hasn't shipped one via public/, plus the\n * `blume-redirects.json` manifest when no host is named. `vercel.json` also\n * carries the header rules `_headers` gives the other hosts, since Vercel reads\n * no `_headers`, so it is written even with no redirects. Note that Vercel's\n * git-integration builds read `vercel.json` from the repository root only —\n * the copy emitted here takes effect when the dist folder itself is deployed\n * directly via the Vercel CLI.\n */\nconst emitRedirectFiles = async (\n project: BlumeProject,\n distDir: string,\n logger: ArtifactLogger\n): Promise<void> => {\n const { config } = project;\n if (config.deployment.options.output !== \"static\") {\n return;\n }\n const platform = deployPlatform(config.deployment);\n const redirects = platformRedirects(config);\n const vercelHeaders = platform.redirectFiles.includes(VERCEL_JSON_FILE)\n ? buildVercelHeaders(\n config,\n buildHomeLinkHeader(config, markdownRoutePaths(project))\n )\n : [];\n const files = platform.redirectFiles.filter(\n (file) =>\n !existsSync(join(distDir, file.name)) &&\n (redirects.length > 0 ||\n (file === VERCEL_JSON_FILE && vercelHeaders.length > 0))\n );\n await Promise.all(\n files.map((file) =>\n writeFile(\n join(distDir, file.name),\n file === VERCEL_JSON_FILE\n ? buildVercelConfig(redirects, vercelHeaders)\n : file.build(redirects),\n \"utf-8\"\n )\n )\n );\n if (redirects.length === 0) {\n return;\n }\n // The manifest is for a host Blume can't name, where nothing reads the\n // platform files above.\n if (platform.kind === \"static\") {\n await writeFile(\n join(distDir, \"blume-redirects.json\"),\n buildRedirectManifest(redirects),\n \"utf-8\"\n );\n }\n logger.info(`Emitted redirect files for ${redirects.length} redirect(s)`);\n};\n\n/**\n * Emit a `_headers` file so Netlify / Cloudflare serve the raw AI-ready\n * endpoints (`*.md`, `*.mdx`, `*.txt`) with an explicit `charset=utf-8`. Without\n * it those hosts send `text/markdown` / `text/plain` with no charset and\n * browsers fall back to Windows-1252, garbling any non-ASCII docs (#82).\n *\n * The same file carries the rest of the agent-discovery surface that only a\n * response header can express: the homepage `Link` header (RFC 8288, see\n * `ai/link-headers.ts`), and the registered media types for the extensionless\n * well-known files — `application/linkset+json` for the API catalog, the\n * signatures directory, and the Agent Skills archives. A static host serves\n * those as `octet-stream` or nothing at all without a rule.\n *\n * A `_headers` shipped in `public/` wins, exactly like `_redirects` — the opt-out\n * is checked at its source rather than in `dist`, because on Cloudflare the file\n * in `dist` is not necessarily the user's: `@astrojs/cloudflare` writes its own\n * `_headers` (an immutable `Cache-Control` rule for `/_astro/*`) during the\n * build, before this runs. Testing `dist` therefore read an adapter-generated\n * file as a user opt-out and skipped silently. When both exist, the adapter's\n * rules are preserved and ours are appended.\n *\n * Gated on {@link readsHeaderFiles}, not on `output === \"static\"`. A **Cloudflare\n * server** build serves `dist/client` through the Worker's ASSETS binding, and\n * Workers static assets honor `_headers` from that directory — so the file\n * applies there too, and skipping it left every Cloudflare server build with no\n * `Link` header and no media type on its own discovery files. The charset half\n * of this file *is* redundant on a server build, because the runtime endpoint\n * sets Content-Type on the Response itself; the `Link` and well-known halves are\n * not, and one conclusion about the first was applied to all three.\n *\n * Exported for the test suite, which exercises it in a subprocess like the\n * command helpers.\n */\nexport const emitHeaderFiles = async (\n project: BlumeProject,\n distDir: string,\n logger: ArtifactLogger\n): Promise<void> => {\n const { config } = project;\n if (\n !readsHeaderFiles(config.deployment) ||\n existsSync(join(project.context.root, \"public\", \"_headers\"))\n ) {\n return;\n }\n const ours = buildNetlifyHeaders(\n config,\n buildHomeLinkHeader(config, markdownRoutePaths(project))\n );\n // An adapter may have written its own rules here already (Cloudflare adds an\n // immutable Cache-Control for /_astro/*). Keep them and append ours: both\n // sets are wanted, and `_headers` has no merge semantics beyond order.\n const target = join(distDir, \"_headers\");\n const existing = existsSync(target) ? await readFile(target, \"utf-8\") : \"\";\n await writeFile(\n target,\n existing ? `${existing.trimEnd()}\\n${ours}` : ours,\n \"utf-8\"\n );\n logger.info(\"Emitted _headers (UTF-8 Content-Type + homepage Link header)\");\n};\n\n/**\n * Collect the Agent Skills `agents.skills` publishes, once per build, so both the\n * skills surface and llms.txt (which lists them) read the same set. Empty\n * when the feature is off, the directory is missing, nothing in it is\n * publishable (each with a warning), or a user-shipped\n * `public/.well-known/agent-skills/index.json` already owns the surface.\n */\nconst collectConfiguredSkills = async (\n project: BlumeProject,\n distDir: string,\n logger: ArtifactLogger\n): Promise<SkillArtifact[]> => {\n const configured = project.config.agents.skills;\n if (!configured) {\n return [];\n }\n const dir = resolve(project.context.root, configured);\n if (!existsSync(dir)) {\n logger.warn(\n `agents.skills points at \"${configured}\" (${dir}), which does not exist; no skills published.`\n );\n return [];\n }\n if (existsSync(join(distDir, AGENT_SKILLS_DIR.slice(1), \"index.json\"))) {\n return [];\n }\n const { skills, warnings } = await collectSkills(dir);\n for (const warning of warnings) {\n logger.warn(warning);\n }\n if (skills.length === 0) {\n logger.warn(\n `agents.skills: no publishable skills found in \"${configured}\".`\n );\n }\n return skills;\n};\n\n/**\n * Publish the collected Agent Skills: copy each skill artifact under\n * `.well-known/agent-skills/` and emit the discovery index. A user-shipped\n * `public/.well-known/agent-skills/index.json` takes over the whole surface\n * (the collector returns nothing then), matching every other generated\n * artifact.\n */\nconst emitAgentSkills = async (\n project: BlumeProject,\n distDir: string,\n skills: readonly SkillArtifact[],\n logger: ArtifactLogger\n): Promise<void> => {\n if (skills.length === 0) {\n return;\n }\n const outDir = join(distDir, AGENT_SKILLS_DIR.slice(1));\n await Promise.all(\n skills.map(async (skill) => {\n const target = join(outDir, skill.path);\n await mkdir(dirname(target), { recursive: true });\n await writeFile(target, skill.content);\n })\n );\n await writeFile(\n join(outDir, \"index.json\"),\n buildSkillsIndex(skills, project.config),\n \"utf-8\"\n );\n logger.info(\n `Published ${skills.length} agent skill(s) (.well-known/agent-skills/index.json)`\n );\n};\n\n/**\n * Emit the generated `.well-known` discovery files — the RFC 9727 API catalog\n * and the Web Bot Auth signature directory — each skipped when the feature is\n * off or when the user ships their own copy via public/ (already in dist by\n * the time this runs).\n */\nconst emitWellKnownFiles = async (\n config: ResolvedConfig,\n distDir: string,\n skills: readonly SkillArtifact[],\n logger: ArtifactLogger\n): Promise<void> => {\n // One document, two paths: the ai-catalog spec's well-known URI and the\n // ARD v0.91 one (see `ai/ai-catalog.ts`).\n const aiCatalog = buildAiCatalog(config, skills);\n const files = [\n {\n content: buildSignaturesDirectory(config),\n label: \"Web Bot Auth\",\n path: SIGNATURES_DIRECTORY_PATH,\n },\n {\n content: buildApiCatalog(config),\n label: \"RFC 9727\",\n path: API_CATALOG_PATH,\n },\n { content: aiCatalog, label: \"AI Catalog\", path: AI_CATALOG_PATH },\n { content: aiCatalog, label: \"ARD manifest\", path: ARD_MANIFEST_PATH },\n ];\n for (const file of files) {\n const target = join(distDir, file.path.slice(1));\n if (!file.content || existsSync(target)) {\n continue;\n }\n // Sequential by nature: both files share the .well-known dir creation.\n // oxlint-disable-next-line no-await-in-loop\n await mkdir(join(distDir, \".well-known\"), { recursive: true });\n // oxlint-disable-next-line no-await-in-loop\n await writeFile(target, file.content, \"utf-8\");\n logger.info(`Generated ${file.path.slice(1)} (${file.label})`);\n }\n};\n\n/**\n * Generate `llms.txt`/`llms-full.txt` into the dist dir. A user's own file in\n * `public/` (copied into dist by Astro before this runs, like the sitemap and\n * robots.txt) wins over the generated one — each file is checked and replaced\n * independently, so a custom `llms.txt` still gets a generated `llms-full.txt`.\n */\nconst publishLlmsFiles = async (\n project: BlumeProject,\n distDir: string,\n skills: readonly SkillArtifact[],\n logger: ArtifactLogger\n): Promise<void> => {\n const indexPath = join(distDir, \"llms.txt\");\n const fullPath = join(distDir, \"llms-full.txt\");\n const writeIndex = !existsSync(indexPath);\n const writeFull = !existsSync(fullPath);\n if (!(writeIndex || writeFull)) {\n return;\n }\n const { index, full } = await buildLlmsFiles(project, { skills });\n const writes: Promise<void>[] = [];\n if (writeIndex) {\n writes.push(writeFile(indexPath, index, \"utf-8\"));\n }\n if (writeFull) {\n writes.push(writeFile(fullPath, full, \"utf-8\"));\n }\n await Promise.all(writes);\n logger.info(\n `Generated ${[\n writeIndex ? \"llms.txt\" : null,\n writeFull ? \"llms-full.txt\" : null,\n ]\n .filter(Boolean)\n .join(\" and \")}`\n );\n};\n\n/**\n * Write every deploy artifact into `distDir`, the directory the platform\n * serves as static files (see the module comment). A user's own `public/`\n * file always wins. `indexSearch` is the Pagefind indexer, replaceable by\n * tests: Pagefind's in-process service cannot be reopened once a build has\n * closed it, so only one suite may run the real one.\n */\nexport const publishBuildArtifacts = async (\n project: BlumeProject,\n distDir: string,\n logger: ArtifactLogger,\n indexSearch: (outDir: string) => Promise<number> = buildSearchIndex\n): Promise<void> => {\n if (project.config.search.provider.mode === \"pagefind\") {\n logger.info(\"Building search index\");\n const indexed = await indexSearch(distDir);\n logger.info(`Indexed ${indexed} page(s) for search`);\n }\n\n // Upload the index to a hosted provider (Algolia, Orama Cloud, Typesense).\n // Skipped with a warning when its admin key isn't configured.\n await syncSearchProvider(project, {\n start: logger.info.bind(logger),\n success: logger.info.bind(logger),\n warn: logger.warn.bind(logger),\n });\n\n // Collected once: llms.txt lists the skills the build publishes below.\n const skills = await collectConfiguredSkills(project, distDir, logger);\n if (project.config.agents.llmsTxt.enabled) {\n await publishLlmsFiles(project, distDir, skills, logger);\n }\n\n const sitemapFiles = buildSitemapFiles(project);\n if (sitemapFiles && !existsSync(join(distDir, \"sitemap.xml\"))) {\n await Promise.all(\n sitemapFiles.map((file) =>\n writeFile(join(distDir, file.name), file.xml, \"utf-8\")\n )\n );\n logger.info(describeSitemapFiles(sitemapFiles));\n }\n\n const robots = buildRobots(project);\n if (robots && !existsSync(join(distDir, \"robots.txt\"))) {\n await writeFile(join(distDir, \"robots.txt\"), robots, \"utf-8\");\n logger.info(\"Generated robots.txt\");\n }\n\n const agentReadability = buildAgentReadability(project);\n if (\n agentReadability &&\n !existsSync(join(distDir, \"agent-readability.json\"))\n ) {\n await writeFile(\n join(distDir, \"agent-readability.json\"),\n `${JSON.stringify(agentReadability, null, 2)}\\n`,\n \"utf-8\"\n );\n logger.info(\"Generated agent-readability.json\");\n }\n\n await emitWellKnownFiles(project.config, distDir, skills, logger);\n await emitAgentSkills(project, distDir, skills, logger);\n\n await emitRedirectFiles(project, distDir, logger);\n await emitHeaderFiles(project, distDir, logger);\n};\n",
|
|
7
|
+
"import { mkdir, writeFile } from \"node:fs/promises\";\n\nimport { close, createIndex } from \"pagefind\";\nimport { dirname, join } from \"pathe\";\n\n/**\n * Build a local Pagefind search index over the built site. Pagefind indexes\n * every rendered page except those whose `<html>` carries\n * `data-pagefind-ignore`, which Blume stamps on non-indexable pages\n * (search-excluded, or hidden without the opt-in), so those stay out.\n *\n * Pagefind is imported statically, not with `import()`: this runs in the\n * `astro:build:done` hook, after Astro has closed the module runner an\n * installed Blume's config was evaluated in (see `core/node-require.ts`), and\n * Pagefind ships only an `import` export, so `require` can't load it either.\n *\n * The index files are fetched with `getFiles()` and written here rather than\n * with Pagefind's `writeFiles()`: in service mode Pagefind acknowledges\n * `writeFiles()` once tokio has buffered the writes, not once they are on\n * disk, and `close()` kills the backend, so a loaded CI host can ship a\n * truncated `pagefind-entry.json` (Pagefind/pagefind#1271). Writing the bytes\n * ourselves puts them on disk before the backend goes away. Fixed upstream in\n * Pagefind/pagefind#1272; switch back to `writeFiles()` once a release ships\n * that fix.\n *\n * Returns the number of pages indexed.\n */\nexport const buildSearchIndex = async (outDir: string): Promise<number> => {\n const { index } = await createIndex({});\n if (!index) {\n throw new Error(\"Failed to create Pagefind index.\");\n }\n\n // These awaits are strictly ordered, not independent: the directory must be\n // indexed before its files are read, and the index closed only after.\n // oxlint-disable-next-line react-doctor/async-parallel\n const result = await index.addDirectory({ path: outDir });\n const { files } = await index.getFiles();\n await close();\n\n const searchDir = join(outDir, \"pagefind\");\n const directories = new Set(\n files.map((file) => dirname(join(searchDir, file.path)))\n );\n await Promise.all(\n [...directories].map((directory) => mkdir(directory, { recursive: true }))\n );\n await Promise.all(\n files.map((file) => writeFile(join(searchDir, file.path), file.content))\n );\n\n return result.page_count;\n};\n",
|
|
8
|
+
"// html-escaper's five-entity table is XML-safe (`'` → the numeric `'`).\nimport { escape as escapeXml } from \"html-escaper\";\n\nimport {\n customStaticRoutes,\n discoverPagesSync,\n hasGeneratedChangelog,\n} from \"../astro/pages.ts\";\nimport { normalizeBasePath, withBasePath } from \"../core/base-path.ts\";\nimport type { BlumeProject } from \"../core/project-graph.ts\";\nimport { siteRoot } from \"../core/site-url.ts\";\n\n/**\n * Astro's reserved error routes. A user-authored override (`pages/404.astro`,\n * `pages/500.astro`, or a `404.md` content page — see `writeNotFoundPage` in\n * `astro/generate.ts`) is neither dynamic nor private, so it would otherwise\n * be emitted — but error pages aren't crawlable destinations and must stay out\n * of the sitemap.\n */\nconst ERROR_ROUTES = new Set([\"/404\", \"/500\"]);\n\n/** A `<lastmod>` element (W3C date) when the page has a valid modified date. */\nconst lastmodTag = (value: string | undefined): string => {\n if (!value) {\n return \"\";\n }\n const date = new Date(value);\n return Number.isNaN(date.getTime())\n ? \"\"\n : `<lastmod>${date.toISOString().slice(0, 10)}</lastmod>`;\n};\n\n/** One emitted sitemap artifact: its dist-root filename and XML body. */\nexport interface SitemapFile {\n name: string;\n xml: string;\n}\n\n/** The build log line for an emitted sitemap set: one file, or an index. */\nexport const describeSitemapFiles = (files: readonly SitemapFile[]): string =>\n files.length === 1\n ? \"Generated sitemap.xml\"\n : `Generated sitemap.xml (index of ${files.length - 1} sitemap files)`;\n\n/**\n * The sitemaps.org cap on `<url>` entries in a single file. Beyond it,\n * `sitemap.xml` becomes a sitemap index pointing at numbered chunk files —\n * search engines reject an oversized urlset outright.\n */\nconst URLS_PER_FILE = 50_000;\n\nconst renderUrlset = (\n urls: string[]\n): string => `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n${urls.join(\"\\n\")}\n</urlset>\n`;\n\n/**\n * Build the sitemap files from the route manifest plus the routes the manifest\n * can't see: custom `.astro` pages (most importantly a custom landing `/`) and\n * the generated `/changelog` index. Returns null when the sitemap is disabled\n * or no `site` is configured (absolute URLs are required for a valid sitemap).\n * Drafts, hidden, and `noindex` pages are excluded.\n *\n * Sites within the per-file URL cap get the single classic `sitemap.xml`;\n * larger sites get `sitemap.xml` as a sitemap index over numbered\n * `sitemap-N.xml` chunks, all served from the same directory robots.txt\n * already points at.\n */\nexport const buildSitemapFiles = (\n project: BlumeProject\n): SitemapFile[] | null => {\n const { site } = project.config.deployment.options;\n if (!(site && project.config.seo.sitemap)) {\n return null;\n }\n\n const base = siteRoot(site);\n // Routes carry `basePath`; a `deployment.base` subdirectory is layered on top.\n const deployBase = normalizeBasePath(project.config.deployment.options.base);\n\n // Archived-version pages leave the sitemap when the version is noindexed,\n // or when their canonical points at a still-existing latest equivalent —\n // listing a URL whose canonical says \"index the other page\" invites the\n // noindexed-page-in-sitemap incoherence Docusaurus is known for. A page\n // that exists only in an archived version stays listed (self-canonical).\n const { versions } = project.config;\n const archivedById = new Map(\n (versions?.archived ?? []).map((version) => [version.id, version])\n );\n const currentKeys = versions\n ? new Set(\n project.graph.pages.flatMap((page) =>\n page.version === \"\" ? [`${page.versionKey}\\u0000${page.locale}`] : []\n )\n )\n : null;\n const archivedExcluded = (page: (typeof project.graph.pages)[number]) => {\n if (!page.version) {\n return false;\n }\n // A non-empty version always names a configured archived entry — that is\n // the only way detection assigns one.\n const archived = archivedById.get(page.version);\n return Boolean(\n archived?.noindex ||\n (archived?.canonical === \"latest\" &&\n currentKeys?.has(`${page.versionKey}\\u0000${page.locale}`))\n );\n };\n\n const seen = new Set<string>();\n const urls: string[] = [];\n const pushUrl = (route: string, lastModified?: string): void => {\n // `<loc>` must be a well-formed, XML-escaped URL: percent-encode the path,\n // then escape XML metacharacters (notably `&`) so a route like\n // `/Tips & Tricks` doesn't produce invalid XML that gets the whole sitemap\n // rejected.\n const loc = escapeXml(encodeURI(`${base}${route}`));\n if (seen.has(loc)) {\n return;\n }\n seen.add(loc);\n urls.push(` <url><loc>${loc}</loc>${lastmodTag(lastModified)}</url>`);\n };\n for (const page of project.graph.pages) {\n if (\n page.meta.draft ||\n page.meta.sidebar.hidden ||\n page.meta.seo.noindex ||\n ERROR_ROUTES.has(page.route) ||\n archivedExcluded(page)\n ) {\n continue;\n }\n pushUrl(withBasePath(deployBase, page.route), page.lastModified);\n }\n // Custom `.astro` pages and the generated changelog index mount outside\n // `basePath` (they're injected at their pattern — see `blumeIntegration`), so\n // only the deployment base layers onto their URLs.\n const userPages = project.context.pagesRoot\n ? discoverPagesSync(project.context.pagesRoot)\n : [];\n const extraRoutes = customStaticRoutes(userPages).filter(\n (route) => !ERROR_ROUTES.has(route)\n );\n if (hasGeneratedChangelog(project, userPages)) {\n extraRoutes.push(\"/changelog\");\n }\n for (const route of extraRoutes) {\n pushUrl(withBasePath(deployBase, route));\n }\n urls.sort();\n\n if (urls.length <= URLS_PER_FILE) {\n return [{ name: \"sitemap.xml\", xml: renderUrlset(urls) }];\n }\n\n const chunks: SitemapFile[] = [];\n const references: string[] = [];\n for (let start = 0; start < urls.length; start += URLS_PER_FILE) {\n const name = `sitemap-${chunks.length + 1}.xml`;\n chunks.push({\n name,\n xml: renderUrlset(urls.slice(start, start + URLS_PER_FILE)),\n });\n // Chunks sit next to sitemap.xml, so their URLs layer the same deployment\n // base robots.txt uses for the index.\n const loc = escapeXml(\n encodeURI(`${base}${withBasePath(deployBase, `/${name}`)}`)\n );\n references.push(` <sitemap><loc>${loc}</loc></sitemap>`);\n }\n const index = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<sitemapindex xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n${references.join(\"\\n\")}\n</sitemapindex>\n`;\n return [{ name: \"sitemap.xml\", xml: index }, ...chunks];\n};\n",
|
|
9
|
+
"import { createHash } from \"node:crypto\";\nimport { existsSync } from \"node:fs\";\nimport {\n mkdir,\n readdir,\n readFile,\n rename,\n rm,\n writeFile,\n} from \"node:fs/promises\";\n\nimport { dirname, join } from \"pathe\";\n\nimport type { ProjectContext } from \"../core/types.ts\";\nimport type { OgCardOptions, OgFont } from \"./card.ts\";\n\n/**\n * Where a build keeps rendered OG cards between runs (see {@link cardCacheKey}\n * for what invalidates one). Baked into the generated OG endpoint alongside\n * the Blume version that renders the cards.\n */\nexport interface OgCache {\n /** Absolute directory holding `<key>.png` files. */\n dir: string;\n /** The Blume version rendering the cards; part of every key. */\n version: string;\n}\n\n/**\n * The card cache directory for a project: `node_modules/.cache/blume/og`,\n * the conventional build-cache location. Vercel and Netlify restore\n * `node_modules` from their build caches, so a deploy there re-renders only\n * the cards whose inputs changed; Cloudflare Workers Builds keeps only\n * package-manager caches (and `node_modules/.astro` for a detected Astro\n * project), and a self-managed runner needs a cache step for the directory.\n * A project with no `node_modules` of its own falls back to the runtime's\n * cache dir next to Astro's and Vite's.\n */\nexport const ogCacheDir = (\n context: Pick<ProjectContext, \"outDir\" | \"root\">\n): string =>\n existsSync(join(context.root, \"node_modules\"))\n ? join(context.root, \"node_modules\", \".cache\", \"blume\", \"og\")\n : join(context.outDir, \".cache\", \"og\");\n\n/**\n * Per-process tally of cache hits and misses plus the keys this build asked\n * for, read back by the CLI after `build()` for the summary line and the\n * prune. On `globalThis` for the same reason as the integration registry: the\n * endpoint renders in the copy of this module Vite bundled for the prerender,\n * while the CLI reads from its own bundled copy.\n */\ninterface OgCacheRegistry {\n hits: number;\n misses: number;\n used: Set<string>;\n}\n\nconst REGISTRY_KEY = Symbol.for(\"blume.og-cache\");\n\ntype RegistryHost = typeof globalThis & { [REGISTRY_KEY]?: OgCacheRegistry };\n\nconst registry = (): OgCacheRegistry => {\n // SAFETY: the registry is stashed on globalThis under a well-known symbol so\n // every copy of this module in the process shares it; the intersection only\n // names that slot.\n const host = globalThis as RegistryHost;\n host[REGISTRY_KEY] ??= { hits: 0, misses: 0, used: new Set() };\n return host[REGISTRY_KEY];\n};\n\n/** Type guard: is this OG font a local file entry? */\nexport const isLocalOgFont = (\n font: OgFont\n): font is Extract<OgFont, { src: string }> =>\n typeof font !== \"string\" && \"src\" in font;\n\n// A local font file's contents digest, computed once per path per process: the\n// key must follow the file's bytes, not its mtime (a fresh CI checkout resets\n// every mtime, which would miss the whole cache on each build).\nconst localFontDigests = new Map<string, Promise<string>>();\n\nconst digestFile = async (path: string): Promise<string> =>\n createHash(\"sha256\")\n .update(await readFile(path))\n .digest(\"hex\");\n\nconst localFontDigest = (path: string): Promise<string> => {\n let digest = localFontDigests.get(path);\n if (!digest) {\n digest = digestFile(path);\n localFontDigests.set(path, digest);\n }\n return digest;\n};\n\n/**\n * The cache key of a card: a digest of everything that decides its pixels —\n * the card options (title, description, brand, logo markup, palette, footer\n * text, font families), the fonts (a local file by its contents, a Google\n * family by its request), and the Blume version, since the layout and the\n * renderer it pins ship with the package. Pre-fetched `images` are left out:\n * the endpoint never passes them.\n */\nexport const cardCacheKey = async (\n version: string,\n options: OgCardOptions\n): Promise<string> => {\n const fonts = await Promise.all(\n (options.fonts ?? []).map(async (font) =>\n isLocalOgFont(font)\n ? { ...font, digest: await localFontDigest(font.src) }\n : font\n )\n );\n const card = { ...options, fonts: undefined, images: undefined };\n return createHash(\"sha256\")\n .update(JSON.stringify({ card, fonts, version }))\n .digest(\"hex\");\n};\n\nconst cardPath = (cache: OgCache, key: string): string =>\n join(cache.dir, `${key}.png`);\n\n/** A cached card's bytes, or `null` when there is none. */\nconst readCard = async (file: string): Promise<Uint8Array | null> => {\n try {\n return await readFile(file);\n } catch {\n return null;\n }\n};\n\n// Written to a sibling temp file and renamed into place: concurrent page\n// renders may store the same key, and a reader must never see a half-written\n// card.\nconst storeCard = async (file: string, png: Uint8Array): Promise<void> => {\n await mkdir(dirname(file), { recursive: true });\n const tmp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;\n await writeFile(tmp, png);\n await rename(tmp, file);\n};\n\n/** The in-flight renders of this process, so duplicate titles render once. */\nconst inflight = new Map<string, Promise<Uint8Array>>();\n\nconst renderAndStore = async (\n key: string,\n file: string,\n options: OgCardOptions,\n render: (options: OgCardOptions) => Promise<Uint8Array>\n): Promise<Uint8Array> => {\n try {\n const png = await render(options);\n try {\n await storeCard(file, png);\n } catch {\n // An unwritable cache (a read-only workspace) never fails the build.\n }\n return png;\n } finally {\n inflight.delete(key);\n }\n};\n\n/**\n * Serve a card from `cache`, rendering it with `render` on a miss and storing\n * the result for the next build. Without a cache every card renders; the\n * cache is only ever a shortcut.\n */\nexport const throughCardCache = async (\n cache: OgCache | undefined,\n options: OgCardOptions,\n render: (options: OgCardOptions) => Promise<Uint8Array>\n): Promise<Uint8Array> => {\n if (!cache) {\n return render(options);\n }\n const key = await cardCacheKey(cache.version, options);\n const state = registry();\n state.used.add(key);\n const file = cardPath(cache, key);\n const hit = await readCard(file);\n if (hit) {\n state.hits += 1;\n return hit;\n }\n let pending = inflight.get(key);\n if (!pending) {\n state.misses += 1;\n pending = renderAndStore(key, file, options, render);\n inflight.set(key, pending);\n }\n return pending;\n};\n\n/**\n * Remove the cards this build never asked for — a renamed page, a changed\n * description, a previous Blume version — plus any temp file a crashed build\n * left behind, so a persisted cache holds exactly the current site's cards.\n * Returns how many files were removed; a missing directory removes nothing.\n */\nexport const pruneCardCache = async (dir: string): Promise<number> => {\n const { used } = registry();\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch {\n return 0;\n }\n const stale = entries.filter(\n (name) =>\n name.endsWith(\".tmp\") ||\n (name.endsWith(\".png\") && !used.has(name.slice(0, -\".png\".length)))\n );\n await Promise.all(stale.map((name) => rm(join(dir, name), { force: true })));\n return stale.length;\n};\n\n/**\n * This process's card cache tally — how many cards a build reused and how many\n * it rendered — or `null` when no card was requested (OG cards off, or an\n * endpoint-free build).\n */\nexport const cardCacheTally = (): { hits: number; misses: number } | null => {\n const { hits, misses } = registry();\n return hits + misses === 0 ? null : { hits, misses };\n};\n\n/** Reset the tally and the used-key set (tests). */\nexport const resetCardCacheTally = (): void => {\n const state = registry();\n state.hits = 0;\n state.misses = 0;\n state.used.clear();\n};\n",
|
|
10
|
+
"import { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync, realpathSync } from \"node:fs\";\nimport {\n lstat,\n mkdir,\n readFile,\n readlink,\n realpath,\n rm,\n symlink,\n} from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { pathToFileURL } from \"node:url\";\n\nimport pMap from \"p-map\";\nimport { dirname, isAbsolute, join, normalize, relative, resolve } from \"pathe\";\nimport { glob } from \"tinyglobby\";\n\nimport { hasAiCatalog } from \"../ai/ai-catalog.ts\";\nimport { OPENAPI_PATH } from \"../ai/api/paths.ts\";\nimport { buildApiSpec } from \"../ai/api/spec.ts\";\nimport { buildAskData } from \"../ai/ask-data.ts\";\nimport { resolveAskBackend } from \"../ai/ask.ts\";\nimport { buildHomeLinkHeader } from \"../ai/link-headers.ts\";\nimport { buildRawMarkdown, markdownRoutePaths } from \"../ai/markdown.ts\";\nimport type { RawMarkdownEntry } from \"../ai/markdown.ts\";\nimport { buildMcpData } from \"../ai/mcp/data.ts\";\nimport type { McpData } from \"../ai/mcp/data.ts\";\nimport { buildMcpDiscovery, buildMcpServerCard } from \"../ai/mcp/discovery.ts\";\nimport {\n hasDeferrableGroups,\n navVariants,\n} from \"../components/layout/nav-utils.ts\";\nimport { normalizeBasePath } from \"../core/base-path.ts\";\nimport { validateUsedComponents } from \"../core/component-diagnostics.ts\";\nimport {\n analyzeComponentOverrides,\n emptyComponentOverrides,\n} from \"../core/component-overrides.ts\";\nimport type { ComponentOverrideAnalysis } from \"../core/component-overrides.ts\";\nimport {\n collectContentAssets,\n rewriteRelativeImages,\n} from \"../core/content-assets.ts\";\nimport type {\n BlumeBanner,\n BlumeData,\n BlumeFavicon,\n BlumeLogo,\n} from \"../core/data.ts\";\nimport { BlumeError } from \"../core/diagnostics.ts\";\nimport { writeTextAtomic } from \"../core/fs-atomic.ts\";\nimport {\n apiUrl as githubApiUrl,\n editBaseUrl as githubEditBaseUrl,\n repoUrl as githubRepoUrl,\n} from \"../core/github.ts\";\nimport { EN_UI, resolveUIStrings } from \"../core/i18n-ui.ts\";\nimport { resolveFallbackLocale } from \"../core/i18n.ts\";\nimport { buildIncludeGraph } from \"../core/includes.ts\";\nimport {\n validateNavTargets,\n validateSearchPopularIcons,\n} from \"../core/nav-diagnostics.ts\";\nimport { packageRoot } from \"../core/package-root.ts\";\nimport type { BlumeProject } from \"../core/project-graph.ts\";\nimport type { ResolvedConfig } from \"../core/schema.ts\";\nimport { resolveDocsCollection } from \"../core/sources/resolve.ts\";\nimport { svgDimensions } from \"../core/svg-dimensions.ts\";\nimport { trimChar } from \"../core/trim.ts\";\nimport { resolveTsconfigAliases } from \"../core/tsconfig-aliases.ts\";\nimport type { Diagnostic, Navigation } from \"../core/types.ts\";\nimport { getBlumeVersion } from \"../core/version.ts\";\nimport { buildRssFeeds, renderRssFeed } from \"../deploy/rss.ts\";\nimport {\n languageIconCss,\n languageIconSlugsIn,\n} from \"../markdown/language-icon.ts\";\nimport { hasMermaidFence } from \"../markdown/mermaid.ts\";\nimport { ogCacheDir } from \"../og/cache.ts\";\nimport { missingFontFiles, resolveOgFonts } from \"../og/derive.ts\";\nimport type { DerivedOgFonts } from \"../og/derive.ts\";\nimport { resolveOgLogo } from \"../og/logo.ts\";\nimport type { ApiSpecData, OpenApiData } from \"../openapi/model.ts\";\nimport {\n builtinProxyReferences,\n hasScalarReferences,\n needsPlaygroundProxy,\n referenceRoutes,\n} from \"../openapi/references.ts\";\nimport { buildReferenceFiles } from \"../openapi/scalar.ts\";\nimport { isOpenApiSource } from \"../openapi/source.ts\";\nimport { registry } from \"../registry/registry.ts\";\nimport { buildSearchDocuments } from \"../search/documents.ts\";\nimport { resolveSearchPopular } from \"../search/popular.ts\";\nimport {\n examplesEntryTemplate,\n tailwindEntryTemplate,\n} from \"../theme/entry.ts\";\nimport {\n buildFontsCss,\n configuredFonts,\n fontLocaleCodes,\n} from \"../theme/fonts.ts\";\nimport { buildThemeCss } from \"../theme/palette.ts\";\nimport { rebaseSourceDirectives } from \"../theme/sources.ts\";\nimport { twoslashCss } from \"../theme/twoslash.ts\";\nimport { planComponentSlots } from \"./component-slots.ts\";\nimport {\n EXAMPLE_SCAN_GLOB,\n discoverExamples,\n exampleMarkdownLookup,\n exampleScanRoots,\n} from \"./examples.ts\";\nimport { publishDevNegotiation } from \"./integration.ts\";\nimport { discoverIslands } from \"./islands.ts\";\nimport {\n customOgRoutes,\n discoverPages,\n hasGeneratedChangelog,\n routeIsTaken,\n} from \"./pages.ts\";\nimport { candidateHolding } from \"./render-deps.ts\";\nimport { missingDependencyDiagnostic } from \"./runtime-deps.ts\";\nimport { publishRuntimeModules } from \"./runtime-modules.ts\";\nimport type { RuntimeModuleId } from \"./runtime-modules.ts\";\nimport {\n askComponentTemplate,\n askEndpointTemplate,\n astroConfigTemplate,\n catchAllPageTemplate,\n changelogIndexTemplate,\n contentAssetsEndpointTemplate,\n contentConfigTemplate,\n exampleMapTemplate,\n exampleWrapperTemplate,\n examplesPageTemplate,\n exampleSlug,\n apiNavigationTemplate,\n apiNotFoundTemplate,\n apiPageTemplate,\n apiPagesIndexTemplate,\n apiSearchTemplate,\n mcpEndpointTemplate,\n mcpPageFile,\n mixedbreadSearchEndpointTemplate,\n notFoundJsonTemplate,\n notFoundMarkdownTemplate,\n notFoundPageTemplate,\n ogEndpointTemplate,\n playgroundProxyTemplate,\n rawMarkdownEndpointTemplate,\n rssEndpointTemplate,\n staticJsonEndpointTemplate,\n runtimeDependencies,\n runtimePackageTemplate,\n runtimeTsconfigTemplate,\n featuresTemplate,\n navFragmentTemplate,\n searchClientTemplate,\n searchEndpointTemplate,\n stagedContentDir,\n} from \"./templates.ts\";\nimport type { ClientFeatures } from \"./templates.ts\";\n\n// The render-deps plugin and the dependency-directory probe live in\n// `render-deps.ts`; `blume/astro` and existing callers import them from here.\nexport { blumeDepsDir, prerenderDepsPlugin } from \"./render-deps.ts\";\nexport type { PrerenderDepsPlugin } from \"./render-deps.ts\";\n\n/** Absolute path to the Blume package `src` directory. */\nconst BLUME_SRC = join(packageRoot(), \"src\");\n\n/**\n * Absolute path to `babel-plugin-react-compiler`, resolved from Blume's own\n * package root (Blume ships it). Returns null when React or the compiler is off.\n *\n * The path must be absolute: @vitejs/plugin-react resolves babel plugins from\n * the *project* root, not `.blume/`, so a bare specifier fails in a user project\n * that never installed the plugin directly. Resolving from `packageRoot()` binds\n * to Blume's shipped copy regardless of the user's package manager or hoisting.\n */\nexport const resolveReactCompiler = (\n config: ResolvedConfig,\n needsReact: boolean,\n pkgDir: string = packageRoot()\n): string | null => {\n if (!(needsReact && config.react.compiler)) {\n return null;\n }\n try {\n return createRequire(pathToFileURL(join(pkgDir, \"_.js\")).href).resolve(\n \"babel-plugin-react-compiler\"\n );\n } catch {\n return null;\n }\n};\n\n/**\n * Warning (as a spreadable list) for the case where the React Compiler was\n * requested but its plugin couldn't be resolved — so the build silently drops\n * to uncompiled output rather than failing. Exported for testing.\n */\nexport const reactCompilerWarnings = (\n config: ResolvedConfig,\n needsReact: boolean,\n compilerPath: string | null\n): string[] =>\n needsReact && config.react.compiler && !compilerPath\n ? [\n \"React Compiler is enabled but `babel-plugin-react-compiler` could not be resolved; falling back to an uncompiled build. Reinstall Blume, or set `react: { compiler: false }` to silence this.\",\n ]\n : [];\n\n/** Resolve Astro's package.json directly inside a node_modules directory. */\nconst resolveAstroPackageJson = (modulesDir: string): string | null => {\n try {\n return realpathSync(join(modulesDir, \"astro\", \"package.json\"));\n } catch {\n return null;\n }\n};\n\n/**\n * The `astro` package reachable through the normal node_modules ancestor walk\n * from a generated runtime — its realpath'd `package.json` plus the\n * `node_modules` directory the walk found it in — or null when none resolves.\n *\n * This deliberately does not use `createRequire().resolve()`. pnpm's generated\n * bin shim adds Blume's virtual-store dependencies to `NODE_PATH`, which\n * CommonJS resolution honors but ESM package resolution ignores. The generated\n * Astro config uses ESM imports, so treating a NODE_PATH-only result as\n * reachable skips the dependency link and makes `import \"astro/config\"` fail.\n * Walking the physical node_modules ancestors mirrors the lookup that config\n * actually gets.\n *\n * The containing directory matters as much as the package: under an isolated\n * linker the walk can find a store-deduped astro in a directory that holds\n * nothing else of Blume's, so \"the right astro resolves\" does not imply \"the\n * integrations resolve\" — callers must check where the hit came from.\n */\nconst resolvedAstroHit = (\n fromDir: string\n): { modulesDir: string; pkg: string } | null => {\n let dir = normalize(fromDir);\n while (true) {\n const modulesDir = join(dir, \"node_modules\");\n const pkg = resolveAstroPackageJson(modulesDir);\n if (pkg) {\n return { modulesDir, pkg };\n }\n const parent = dirname(dir);\n if (parent === dir) {\n return null;\n }\n dir = parent;\n }\n};\n\n/**\n * Whether two paths name the same physical directory (realpath equality).\n * Exported for testing.\n */\nexport const sameRealDir = (a: string, b: string): boolean => {\n try {\n return realpathSync(a) === realpathSync(b);\n } catch {\n return false;\n }\n};\n\n/**\n * Create `link` as a directory symlink to `target`, falling back to a junction.\n *\n * The type only matters on Windows, and there a junction is the wrong first\n * choice: Windows can't follow a *relative* symlink reached through a\n * junction (it resolves the relative target against the junction-side path,\n * so it lands outside the store and every lookup is ENOENT), and Bun's\n * isolated linker writes exactly those into Blume's dependency directory\n * whenever it holds symlink privilege. A directory symlink traverses them\n * fine. Without that privilege (no Developer Mode, non-admin shell) creating\n * one fails, so fall back to a junction — and in that session the installer\n * had no privilege either, so the deps are absolute junctions a junction can\n * follow. Exported for testing.\n */\nexport const symlinkDir = async (\n target: string,\n link: string\n): Promise<void> => {\n try {\n await symlink(target, link, \"dir\");\n } catch {\n await symlink(target, link, \"junction\");\n }\n};\n\n/**\n * Point `link` at Blume's dependency directory via a `node_modules` link,\n * replacing a stale link we own and leaving a real directory untouched.\n *\n * `lstat`, not `existsSync`, so a broken link (target since moved) is still\n * detected — `existsSync` follows the link and reports a dangling one as absent.\n */\nconst linkDepsJunction = async (\n link: string,\n depsDir: string\n): Promise<void> => {\n let existing: Awaited<ReturnType<typeof lstat>> | null;\n try {\n existing = await lstat(link);\n } catch {\n existing = null;\n }\n if (existing) {\n if (!existing.isSymbolicLink()) {\n return;\n }\n // Already pointing at the right target — leave it alone. This runs on\n // every dev regeneration, and an unconditional rm+recreate opens a window\n // in which the Vite server's module resolution races a missing\n // `node_modules` and 500s intermittently.\n try {\n if (resolve(dirname(link), await readlink(link)) === resolve(depsDir)) {\n return;\n }\n } catch {\n // Unreadable link — replace it below.\n }\n await rm(link, { force: true });\n }\n await mkdir(dirname(link), { recursive: true });\n await symlinkDir(depsDir, link);\n};\n\n/** Read the `version` field of a `package.json`, or null when unreadable. */\nconst readPkgVersion = (pkgJsonPath: string | null): string | null => {\n if (!pkgJsonPath) {\n return null;\n }\n try {\n return JSON.parse(readFileSync(pkgJsonPath, \"utf-8\")).version ?? null;\n } catch {\n return null;\n }\n};\n\n/**\n * Build the diagnostic for a split-layout Astro conflict that a symlink can't\n * repair: a different Astro is hoisted to the project root, shadowing Blume's,\n * and `@astrojs/mdx` binds to the wrong copy. `blumeAstroPkg`/`shadowAstroPkg`\n * are the resolved `astro/package.json` paths for Blume's set and the one the\n * runtime actually resolves.\n */\nconst astroConflictWarning = (\n blumeAstroPkg: string | null,\n shadowAstroPkg: string | null\n): string => {\n const blume = readPkgVersion(blumeAstroPkg);\n const shadow = readPkgVersion(shadowAstroPkg);\n const versions =\n blume && shadow\n ? `astro@${shadow} shadowing Blume's astro@${blume}`\n : \"a second copy of Astro shadowing Blume's\";\n const pin = blume ?? \"<Blume's astro version>\";\n return `Astro version conflict: another dependency hoisted ${versions} to the project root, so @astrojs/mdx binds to the wrong copy and the build fails on a missing export (e.g. \"chunkToString\"). A single symlink can't reconcile a split install — pin Blume's Astro by adding a package.json \"overrides\" (npm/bun/pnpm) or \"resolutions\" (yarn) entry { \"astro\": \"${pin}\" }, then reinstall. Run \\`npm ls astro\\` to find the dependency pulling the older copy.`;\n};\n\n/**\n * Drop a `.blume/node_modules` junction that resolves a *different* Blume than\n * the one running. A restored build cache (e.g. Vercel's) can resurrect the\n * junction pointing into a superseded store directory — blume@1.1.0's isolated\n * deps dir after 1.1.1 was installed. Releases rarely bump Astro, so the stale\n * target still resolves the very same astro and every astro-based probe in\n * {@link ensureDepsLink} passes through the link — while the `blume/*` imports\n * in the freshly generated config load the previous release, crashing on any\n * export added since. Staleness is judged by realpath: the link is stale\n * exactly when the directory behind it holds a `blume` that isn't `pkgDir`.\n * A target with no `blume` entry (the workspace layout links\n * `packages/blume/node_modules`, which holds only the deps) resolves Blume\n * through the normal ancestor walk and stays. Real directories stay too,\n * mirroring {@link linkDepsJunction} — we only ever remove a link we own.\n */\nconst dropStaleDepsLink = async (\n link: string,\n pkgDir: string\n): Promise<void> => {\n let existing: Awaited<ReturnType<typeof lstat>>;\n try {\n existing = await lstat(link);\n } catch {\n return;\n }\n if (!existing.isSymbolicLink()) {\n return;\n }\n let linkedBlume: string;\n let runningBlume: string;\n try {\n linkedBlume = await realpath(join(link, \"blume\"));\n runningBlume = await realpath(pkgDir);\n } catch {\n return;\n }\n if (linkedBlume !== runningBlume) {\n await rm(link, { force: true });\n }\n};\n\n/**\n * Make the generated runtime resolve Astro and its integrations against Blume's\n * own dependency set. Three failure modes this repairs:\n *\n * - Astro is *unreachable* from `.blume/` (workspaces under isolated linkers,\n * pnpm) — the deps live in a store the upward walk can't see.\n * - Astro *resolves to the wrong copy* — a hoisted sibling pinned an older\n * major (e.g. `astro@6` for a type-only import) that shadows Blume's\n * `astro@7`, so `@astrojs/mdx@7` binds to it and crashes the build on a\n * missing export. Resolving merely *an* astro isn't enough; it must be the\n * same one Blume uses.\n * - The *integrations* are unreachable while astro is fine — npm's split\n * install. An `overrides` pin plus an incremental `npm install` hoists\n * astro to the project root (deleting Blume's nested copy) but leaves\n * `@astrojs/mdx` and friends nested under `blume/node_modules`, where the\n * upward walk from `.blume/` can't see them. The same shape arises under\n * an isolated linker when the workspace itself declares astro at a version\n * matching Blume's: the store dedupes both to one copy, so the walk finds\n * the \"correct\" astro through the workspace's own direct-dep symlink — in\n * a node_modules holding none of Blume's other deps.\n *\n * The repair is the same symlink: Blume's dependency directory linked in as\n * `.blume/node_modules` so the generated config's bare specifiers (`astro`,\n * `@astrojs/mdx`, …) bind to a consistent set. That's safe when the linked\n * directory holds the full set, or when it holds only the integrations but the\n * runtime already resolves Blume's astro — the junction has no `astro` entry,\n * so astro lookups fall through to the hoisted copy the integrations bind to\n * anyway. What it can't fix is the inverse split: Blume's astro nested under a\n * *conflicting* hoisted astro with the integrations hoisted away from it. No\n * single directory yields a consistent set there; only a root `overrides`/\n * `resolutions` pin does, so we return a diagnostic naming the conflict rather\n * than silently shipping a runtime that crashes downstream. Returns the\n * warning, or null when nothing needs saying.\n */\nexport const ensureDepsLink = async (\n outDir: string,\n pkgDir: string = packageRoot()\n): Promise<string | null> => {\n const astroDir = candidateHolding(pkgDir, \"astro\");\n if (!astroDir) {\n return null;\n }\n const mdxDir = candidateHolding(pkgDir, \"@astrojs\", \"mdx\");\n const blumeAstro = resolveAstroPackageJson(astroDir);\n // Before probing what `.blume/` resolves, drop a cache-restored junction\n // that binds it to a superseded Blume — the probes below would otherwise\n // pass right through it (same astro, older blume) and leave it in place.\n await dropStaleDepsLink(join(outDir, \"node_modules\"), pkgDir);\n const outDirHit = resolvedAstroHit(outDir);\n // `.blume/` resolves the very same astro Blume's deps provide.\n const astroCorrect = blumeAstro !== null && outDirHit?.pkg === blumeAstro;\n // Clean hoisted install: astro is correct, found in Blume's own dependency\n // directory, and the integrations sit beside it — the same walk resolves\n // them too, so there is nothing to do. Requiring the walk to land in\n // `astroDir` itself (not merely resolve an identical astro) matters under\n // isolated linkers: a workspace that declares astro at a version matching\n // Blume's gets a store-deduped symlink in its own node_modules, so the walk\n // finds the \"correct\" astro in a directory holding only the workspace's\n // direct deps — none of Blume's integrations (issue #103).\n const walkLandsInDeps =\n astroCorrect &&\n outDirHit !== null &&\n sameRealDir(outDirHit.modulesDir, astroDir);\n if (walkLandsInDeps && mdxDir === astroDir) {\n return null;\n }\n // Linking the integrations' directory yields a consistent set when it also\n // holds Blume's astro (the unreachable and repairable-conflict cases) or\n // when the correct astro is reachable without it (the npm split install).\n // Any existing link here is stale and gets replaced.\n if (mdxDir && (mdxDir === astroDir || astroCorrect)) {\n await linkDepsJunction(join(outDir, \"node_modules\"), mdxDir);\n return null;\n }\n // Split layout: Blume's astro is nested (a conflicting astro took the root\n // spot) but @astrojs/mdx hoisted away from it, binding to the shadow. Only a\n // root pin fixes this — surface it.\n return astroConflictWarning(blumeAstro, outDirHit?.pkg ?? null);\n};\n\n/** Absolute path to the configured `examples.css`, or null when unset. */\nconst examplesCssFile = (\n root: string,\n config: ResolvedConfig\n): string | null =>\n config.examples.css ? join(root, config.examples.css) : null;\n\n/**\n * Write the per-example preview route (`{basePath}/blume-examples/<path>`)\n * that `<Component />` iframes embed — the iframe boundary is what isolates\n * previews from the docs CSS. Nested under `basePath` in the filesystem so\n * the routes stay reachable behind a proxy that only forwards the base;\n * pruneOrphans clears a stale copy when `basePath` changes or the last\n * example is removed. Returns (as a spreadable list) a warning when the\n * configured `examples.css` doesn't exist.\n */\nconst writeExamplesPreview = async (options: {\n config: ResolvedConfig;\n hasExamples: boolean;\n root: string;\n srcDir: string;\n write: (path: string, content: string) => Promise<boolean>;\n}): Promise<string[]> => {\n const { config, hasExamples, root, srcDir, write } = options;\n if (hasExamples) {\n await write(\n join(\n srcDir,\n \"pages\",\n ...config.basePath.split(\"/\").filter(Boolean),\n \"blume-examples\",\n \"[...path].astro\"\n ),\n examplesPageTemplate()\n );\n }\n const cssFile = examplesCssFile(root, config);\n return cssFile && !existsSync(cssFile)\n ? [\n `examples.css points at \"${config.examples.css}\", which doesn't exist; previews render without it.`,\n ]\n : [];\n};\n\n/** Read a file's contents, or return an empty string if it is absent. */\nconst readOptional = async (path: string | null): Promise<string> => {\n if (!path) {\n return \"\";\n }\n try {\n return await readFile(path, \"utf-8\");\n } catch {\n return \"\";\n }\n};\n\n/**\n * Read a user stylesheet (`theme.css`, `examples.css`) that gets inlined into\n * a generated Tailwind entry under `outputDir`, re-rooting its relative\n * `@source` paths so they still resolve from where the author wrote them.\n */\nconst readUserCss = async (\n file: string | null,\n outputDir: string\n): Promise<string> => {\n const css = await readOptional(file);\n return file\n ? rebaseSourceDirectives(css, { from: file, to: outputDir })\n : css;\n};\n\n/** Heuristically detect whether the project uses React islands. */\nexport const detectNeedsReact = async (root: string): Promise<boolean> => {\n const matches = await glob([\"**/*.{tsx,jsx}\"], {\n cwd: root,\n ignore: [\"**/node_modules/**\", \"**/.blume/**\", \"**/dist/**\"],\n onlyFiles: true,\n });\n return matches.length > 0;\n};\n\n/** Block math (`$$…$$`) or an explicitly authored `<Math …>` component. */\nconst containsMath = (content: string): boolean =>\n content.includes(\"$$\") || content.includes(\"<Math\");\n\n/** Ceiling on concurrent content-file reads; unbounded fan-out risks EMFILE. */\nconst READ_CONCURRENCY = 16;\n\n/**\n * Detect whether the project can render math: block math (`$$…$$`) or an\n * explicit `<Math>` tag in any local `.md`/`.mdx`, or in staged (non-filesystem)\n * source bodies. Drives whether the generated runtime imports the `<Math>`\n * component and KaTeX's stylesheet, so a math-free site ships no KaTeX CSS.\n * Math parsing itself is always on but block-only, so one of those literals is\n * a necessary condition — no false negatives. A stray `$$` (e.g. inside a code\n * fence) merely over-includes the idempotent import, which is harmless.\n */\nexport const detectUsesMath = async (\n root: string,\n staged: Iterable<string> = []\n): Promise<boolean> => {\n const files = await glob([\"**/*.{md,mdx}\"], {\n cwd: root,\n ignore: [\"**/node_modules/**\", \"**/.blume/**\", \"**/dist/**\"],\n onlyFiles: true,\n });\n const contents = await pMap(files, (file) => readOptional(join(root, file)), {\n concurrency: READ_CONCURRENCY,\n });\n return [...contents, ...staged].some(containsMath);\n};\n\nconst hashConfigSource = (source: string): string =>\n createHash(\"sha256\").update(source).digest(\"hex\");\n\nconst loadIntegrationBridge = async (\n config: ResolvedConfig,\n context: BlumeProject[\"context\"]\n): Promise<Parameters<typeof astroConfigTemplate>[0][\"integrationBridge\"]> => {\n if (config.integrations.length === 0 || !context.configFile) {\n return;\n }\n return {\n configFile: relative(context.outDir, context.configFile),\n sourceHash: hashConfigSource(await readOptional(context.configFile)),\n };\n};\n\nconst writeIfChanged = async (\n path: string,\n content: string\n): Promise<boolean> => {\n let existing: string | null = null;\n try {\n existing = await readFile(path, \"utf-8\");\n } catch {\n existing = null;\n }\n if (existing === content) {\n return false;\n }\n // Atomic temp-write + rename, so a watching dev server never observes a\n // missing or half-written file mid-regeneration.\n await writeTextAtomic(path, content);\n return true;\n};\n\n/**\n * Delete generated files under `srcDir` that this pass didn't (re)write. The\n * generator emits many files conditionally — an Ask AI endpoint, OG images, a\n * search index, RSS feeds, reference pages, the MCP server — so toggling a\n * feature off would otherwise leave a stale file behind, and a leftover\n * server-rendered endpoint breaks the static build. `writeIfChanged` only ever\n * adds or updates, so this closes the loop. Scoped to `.blume/src`, so it never\n * touches Astro's `dist/`, `.astro/` cache, or the symlinked `node_modules`\n * (all of which live outside `src`). `written` holds normalized absolute paths.\n */\nexport const pruneOrphans = async (\n srcDir: string,\n written: Set<string>\n): Promise<void> => {\n const existing = await glob(\"**/*\", {\n absolute: true,\n cwd: srcDir,\n onlyFiles: true,\n });\n const removals: Promise<void>[] = [];\n for (const path of existing) {\n const normalized = normalize(path);\n if (!written.has(normalized)) {\n removals.push(rm(normalized, { force: true }));\n }\n }\n await Promise.all(removals);\n};\n\n/**\n * Collect staged (non-filesystem) page bodies keyed by their Astro entry id, so\n * i18n duplicates of one entry collapse to a single materialized file. Shared\n * with `eject`, which materializes the same bodies into the owned project.\n */\nexport const collectStaged = (project: BlumeProject): Map<string, string> => {\n const staged = new Map<string, string>();\n for (const page of project.graph.pages) {\n if (page.collection === \"staged\" && page.entryId && page.body) {\n // A colocated `./image.png` reference resolves against `.blume/content`\n // once the body is materialized there, where the file does not exist.\n // Point it at the served original instead — the same rewrite the\n // agent-facing Markdown gets.\n const text = page.sourcePath\n ? rewriteRelativeImages({\n deployBase: project.config.deployment.options.base,\n projectRoot: project.context.root,\n source: page.body.text,\n sourcePath: page.sourcePath,\n })\n : page.body.text;\n staged.set(page.entryId, text);\n }\n }\n return staged;\n};\n\n/**\n * Materialize staged source bodies under `.blume/content` and prune orphans in\n * that tree (separate from `.blume/src`), so a removed remote entry is cleaned up.\n */\nconst writeStagedContent = async (\n out: string,\n staged: Map<string, string>\n): Promise<void> => {\n const contentDir = stagedContentDir(out);\n const written = new Set<string>();\n await Promise.all(\n [...staged].map(async ([entryId, text]) => {\n const path = join(contentDir, entryId);\n written.add(normalize(path));\n await writeIfChanged(path, text);\n })\n );\n if (existsSync(contentDir)) {\n await pruneOrphans(contentDir, written);\n }\n};\n\ninterface LogoDimensions {\n height: number;\n width: number;\n}\n\n/**\n * Read dimensions from an SVG's explicit size or its view box, with the same\n * root-tag parser og/card.ts uses for the OG brand mark so the header and the\n * card can't disagree about one logo. An SVG with no usable size collapses to\n * undefined.\n */\nconst logoDimensions = (svg: string | undefined): LogoDimensions | undefined =>\n svg ? (svgDimensions(svg) ?? undefined) : undefined;\n\n/** Read a local SVG logo from the project root or public directory. */\nconst readLogoSvg = (\n project: BlumeProject,\n source: string | undefined\n): string | undefined => {\n if (!source?.toLowerCase().endsWith(\".svg\")) {\n return;\n }\n const rel = source.replace(/^\\//u, \"\");\n const file = [\n join(project.context.root, \"public\", rel),\n join(project.context.root, rel),\n ].find((path) => existsSync(path));\n return file ? readFileSync(file, \"utf-8\") : undefined;\n};\n\n/** Narrows a config union's string shorthand from its object form. */\nconst isStringShorthand = <T>(value: T | string): value is string =>\n typeof value === \"string\";\n\n/**\n * The URL behind the header's repo mark. A string is used as-is: `github`\n * drives the per-page edit link, the header mark and the manifest's\n * `repository` together, so a project whose docs repo is private has to unset\n * all three, and would otherwise have no way to point the mark at anything.\n */\nconst headerRepoUrl = (\n repo: boolean | string,\n derived: string | null\n): string | null => {\n if (isStringShorthand(repo)) {\n return repo;\n }\n return repo ? derived : null;\n};\n\n/**\n * Resolve the configured logo. A single SVG is read and inlined so a\n * `currentColor` logo follows the theme; other images keep their URL for an\n * `<img>`. The file is looked up under `public/` and the project root.\n */\nconst resolveLogo = (project: BlumeProject): BlumeLogo | null => {\n const { logo } = project.config;\n if (!logo) {\n return null;\n }\n const config = isStringShorthand(logo) ? { image: logo } : logo;\n // `text` is passed through verbatim: `undefined` lets the brand fall back to\n // the site title, `\"\"` renders the mark alone (a logo with the wordmark baked\n // in).\n const { href, image: source, text } = config;\n const image = isStringShorthand(source) ? { light: source } : source;\n const light = image?.light ?? image?.dark;\n const dark = image?.dark ?? image?.light;\n const alt = image?.alt ?? \"\";\n const brandHref = href ?? \"/\";\n const lightSvg = readLogoSvg(project, light);\n const darkSvg = dark === light ? lightSvg : readLogoSvg(project, dark);\n\n if (light && light === dark && lightSvg) {\n return { alt, href: brandHref, svg: lightSvg, text };\n }\n\n const lightDimensions = logoDimensions(lightSvg);\n const darkDimensions = logoDimensions(darkSvg);\n const dimensions =\n lightDimensions || darkDimensions\n ? { dark: darkDimensions, light: lightDimensions }\n : undefined;\n return { alt, dark, dimensions, href: brandHref, light, text };\n};\n\n/**\n * Favicon filenames Blume auto-detects, in priority order. Mirrors the Next.js\n * convention: an `icon.*` or `favicon.*` file in `public/` or the project root\n * becomes the site favicon, no config required.\n */\nconst FAVICON_CANDIDATES = [\n \"icon.svg\",\n \"favicon.svg\",\n \"icon.png\",\n \"favicon.png\",\n \"favicon.ico\",\n \"icon.ico\",\n];\n\n/**\n * Derive the `-dark` sibling of an icon filename (`icon.svg` → `icon-dark.svg`),\n * inserting the suffix before the extension.\n */\nconst darkSibling = (name: string): string =>\n name.replace(/\\.(?=[^.]+$)/u, \"-dark.\");\n\n/** `<link type>` MIME for the favicon extensions we recognize. */\nconst FAVICON_TYPES = new Map([\n [\"ico\", \"image/x-icon\"],\n [\"jpeg\", \"image/jpeg\"],\n [\"jpg\", \"image/jpeg\"],\n [\"png\", \"image/png\"],\n [\"svg\", \"image/svg+xml\"],\n]);\n\n/** Infer the `<link type>` MIME from a filename, when we recognize the extension. */\nconst faviconType = (name: string): string | undefined => {\n const ext = name.split(\".\").pop()?.toLowerCase();\n return ext ? FAVICON_TYPES.get(ext) : undefined;\n};\n\n/** Read a file and encode it as a `data:` URI of the given MIME type. */\nconst inlineDataUri = (file: string, type: string): string =>\n `data:${type};base64,${readFileSync(file).toString(\"base64\")}`;\n\n/**\n * The bundled Blume favicon, inlined as a data URI so it needs no public file.\n * The mark is dark, so it ships with a light-on-transparent dark-scheme variant —\n * otherwise it disappears against dark browser chrome.\n */\nconst defaultFavicon = (): BlumeFavicon => ({\n dark: {\n href: inlineDataUri(\n join(BLUME_SRC, \"assets\", \"icon-dark.png\"),\n \"image/png\"\n ),\n type: \"image/png\",\n },\n href: inlineDataUri(join(BLUME_SRC, \"assets\", \"icon.png\"), \"image/png\"),\n type: \"image/png\",\n});\n\n/**\n * Apple touch icon filenames Blume auto-detects, in priority order. Mirrors the\n * Next.js `apple-icon.*` convention (plus the `apple-touch-icon.png` most favicon\n * generators emit): a match in `public/` or the project root becomes the iOS\n * home-screen icon, no config required.\n */\nconst APPLE_ICON_CANDIDATES = [\n \"apple-icon.png\",\n \"apple-icon.jpg\",\n \"apple-icon.jpeg\",\n \"apple-touch-icon.png\",\n];\n\n/**\n * Resolve an icon file by convention. A candidate in `public/` is served as-is\n * and referenced by URL; one at the project root is inlined as a data URI (the\n * root isn't a served directory). Returns null when the project ships none.\n */\nconst resolveIconFile = (\n project: BlumeProject,\n candidates: string[]\n): BlumeFavicon | null => {\n const { root } = project.context;\n for (const name of candidates) {\n if (existsSync(join(root, \"public\", name))) {\n return { href: `/${name}`, type: faviconType(name) };\n }\n }\n for (const name of candidates) {\n const file = join(root, name);\n if (existsSync(file)) {\n const type = faviconType(name);\n return { href: inlineDataUri(file, type ?? \"image/x-icon\"), type };\n }\n }\n return null;\n};\n\n/**\n * Resolve the site favicon by convention, falling back to the bundled Blume mark\n * when the project ships no `icon.*`/`favicon.*` file. The dark-scheme variant\n * is anchored to the resolved icon: its `-dark` sibling (e.g. `icon.svg` →\n * `icon-dark.svg`) in the same directory — never an unrelated `-dark` file, so a\n * stale or foreign `favicon-dark.*` can't silently pair with a different mark.\n * A `-dark` file with no light sibling is the site's only mark and is used for\n * both schemes, mirroring how `resolveLogo` treats a single-variant image.\n */\nconst resolveFavicon = (project: BlumeProject): BlumeFavicon => {\n const { root } = project.context;\n for (const name of FAVICON_CANDIDATES) {\n if (existsSync(join(root, \"public\", name))) {\n const type = faviconType(name);\n const sibling = darkSibling(name);\n return existsSync(join(root, \"public\", sibling))\n ? { dark: { href: `/${sibling}`, type }, href: `/${name}`, type }\n : { href: `/${name}`, type };\n }\n }\n for (const name of FAVICON_CANDIDATES) {\n const file = join(root, name);\n if (existsSync(file)) {\n const type = faviconType(name);\n const mime = type ?? \"image/x-icon\";\n const siblingFile = join(root, darkSibling(name));\n return existsSync(siblingFile)\n ? {\n dark: { href: inlineDataUri(siblingFile, mime), type },\n href: inlineDataUri(file, mime),\n type,\n }\n : { href: inlineDataUri(file, mime), type };\n }\n }\n return (\n resolveIconFile(project, FAVICON_CANDIDATES.map(darkSibling)) ??\n defaultFavicon()\n );\n};\n\n/**\n * Resolve the Apple touch icon by convention, or null when the project ships\n * none (unlike the favicon, there's no bundled default). Note: iOS ignores\n * `data:`-URI apple-touch-icons, so a `public/` file (served by URL) is the\n * reliable path; a root-level file is still inlined for symmetry with favicons.\n * Deliberately no `-dark` sibling detection here: iOS ignores `media` on\n * `apple-touch-icon` links, so a dark variant could never be served.\n */\nconst resolveAppleIcon = (project: BlumeProject): BlumeFavicon | null =>\n resolveIconFile(project, APPLE_ICON_CANDIDATES);\n\n/** Normalize the banner config (string shorthand or object) for the runtime. */\nconst resolveBanner = (config: ResolvedConfig): BlumeBanner | null => {\n const { banner } = config;\n if (!banner) {\n return null;\n }\n if (isStringShorthand(banner)) {\n return { content: banner, dismissible: false, key: banner };\n }\n return {\n content: banner.content,\n dismissible: banner.dismissible,\n key: banner.id ?? banner.content,\n link: banner.link,\n };\n};\n\n/**\n * The OG card's brand mark: a `seo.og.logo` of `false` opts out of any mark,\n * a configured SVG wins over the site logo, and a non-SVG value resolves to\n * `undefined` (the card falls back to the accent-initial tile).\n */\nconst resolveOgMark = (\n project: BlumeProject,\n siteLogo: string | undefined\n): string | false | undefined => {\n const configured = project.config.seo.og.logo;\n if (configured === false) {\n return false;\n }\n if (configured) {\n return resolveOgLogo(project, configured);\n }\n return siteLogo;\n};\n\n/**\n * The OG card's footer site text. A `seo.og.site` override wins (`false`\n * hides it); the default is the deployment site's host plus the normalized\n * deployment base — on a subpath deploy (a GitHub Pages project site) the\n * bare host is the platform's shared apex, not this site (#139).\n */\nconst resolveOgSite = (config: ResolvedConfig): string | undefined => {\n const configured = config.seo.og.site;\n if (configured === false) {\n return;\n }\n if (configured !== undefined) {\n return configured;\n }\n return config.deployment.options.site\n ? `${new URL(config.deployment.options.site).host}${normalizeBasePath(\n config.deployment.options.base\n )}`\n : undefined;\n};\n\n/**\n * The OG card's site-wide subtitle: `seo.og.description` (`false` omits it)\n * over the site description. The generated endpoint prefers a page's own\n * description and falls back to this.\n */\nconst resolveOgDescription = (config: ResolvedConfig): string | undefined => {\n const configured = config.seo.og.description;\n if (configured === false) {\n return;\n }\n return configured ?? config.description;\n};\n\n/**\n * Repo coordinates for content components. `host` and the derived `api` ride\n * along so a card on an Enterprise site links and queries that instance rather\n * than the public one.\n */\nconst resolveGithubData = (\n github: ResolvedConfig[\"github\"]\n): BlumeData[\"config\"][\"github\"] =>\n github\n ? {\n api: githubApiUrl(github),\n host: github.host,\n owner: github.owner,\n repo: github.repo,\n }\n : null;\n\n/** Serialize the content graph into the data module the runtime consumes. */\nexport const buildRuntimeData = (project: BlumeProject): string => {\n const { config, context, graph, manifest } = project;\n const { github } = config;\n const repoUrl = github ? githubRepoUrl(github) : null;\n const editBase = github ? githubEditBaseUrl(github) : null;\n const logo = resolveLogo(project);\n const ogLogo = resolveOgMark(project, logo?.svg);\n\n const editUrlFor = (sourcePath?: string): string | null => {\n if (!(editBase && sourcePath)) {\n return null;\n }\n const rel = relative(context.root, sourcePath).split(\"\\\\\").join(\"/\");\n // The edit path is repo-relative: `github.dir` places the project inside\n // the repo, so a source above the project dir (a monorepo vault beside the\n // docs app) still resolves to an in-repo file. A path that escapes the\n // repo itself has nothing to edit — fabricating one yields a 404 link.\n // `dir` is a bare string in the schema, so a leading slash (`/apps/docs`)\n // is trimmed rather than read as an absolute path — which would drop the\n // link from every page of a site that has always written it that way.\n const editDir = trimChar(github?.dir ?? \"\", \"/\");\n const editPath = normalize(editDir ? join(editDir, rel) : rel);\n if (editPath.startsWith(\"..\") || isAbsolute(editPath)) {\n return null;\n }\n return `${editBase}/${editPath}`;\n };\n\n const { i18n } = config;\n\n // Resolve the header repo link per locale. API references no longer add a tab\n // automatically — authors point a `navigation.tabs` entry at the reference\n // route to surface it (see `referenceRoutes`).\n const markUrl = headerRepoUrl(config.navigation.repo, repoUrl);\n const withRepoUrl = (nav: Navigation): Navigation => ({\n ...nav,\n repoUrl: markUrl,\n });\n\n // Resolved UI dictionaries: one per locale under i18n, English baseline\n // otherwise. Threaded into chrome so the catch-all can pick the active locale.\n const uiByLocale = i18n\n ? Object.fromEntries(\n i18n.locales.map(({ code }) => [\n code,\n resolveUIStrings(code, {\n defaultLocale: i18n.defaultLocale,\n overrides: i18n.ui,\n }),\n ])\n )\n : {};\n const defaultUi = i18n\n ? resolveUIStrings(i18n.defaultLocale, {\n defaultLocale: i18n.defaultLocale,\n overrides: i18n.ui,\n })\n : EN_UI;\n\n const navigationByLocale = i18n\n ? Object.fromEntries(\n i18n.locales.map(({ code }) => [\n code,\n withRepoUrl(\n graph.navigationByLocale[code] ?? {\n actions: [],\n cta: null,\n featured: [],\n selectors: [],\n sidebar: [],\n tabs: [],\n }\n ),\n ])\n )\n : {};\n\n const data: BlumeData = {\n config: {\n analytics: config.analytics,\n appleIcon: resolveAppleIcon(project),\n ask: config.ai.ask?.enabled\n ? {\n endpoint: config.ai.ask.endpoint ?? null,\n suggestions: config.ai.ask.suggestions,\n }\n : null,\n banner: resolveBanner(config),\n basePath: config.basePath,\n codeThemes: config.markdown.code.theme,\n codeWrap: config.markdown.code.wrap,\n dateFormat: config.dateFormat,\n description: config.description,\n discovery: {\n agentReadability: config.agents.agentReadability,\n aiCatalog: hasAiCatalog(config),\n api: config.agents.api,\n llmsTxt: config.agents.llmsTxt.enabled,\n // Mirrors `buildSitemapFiles`: no site, no sitemap.\n sitemap: config.seo.sitemap && Boolean(config.deployment.options.site),\n },\n favicon: resolveFavicon(project),\n feedback: config.feedback,\n github: resolveGithubData(github),\n i18n: i18n\n ? {\n defaultLocale: i18n.defaultLocale,\n // The locale fallback content is rendered from, so the catch-all can\n // set the content direction to the language it's actually written in.\n fallbackLocale: resolveFallbackLocale(i18n),\n hideDefaultLocalePrefix: i18n.hideDefaultLocalePrefix,\n locales: i18n.locales.map(({ code, dir, label }) => ({\n code,\n dir,\n label,\n })),\n }\n : null,\n // `undefined` members drop out of the JSON snapshot; the null keeps the\n // \"nothing configured\" case explicit for the layouts.\n identity:\n config.seo.organization || config.seo.software\n ? {\n organization: config.seo.organization,\n software: config.seo.software,\n }\n : null,\n imageZoom: config.markdown.imageZoom,\n logo,\n mcp: config.agents.mcp.enabled\n ? {\n name: config.agents.mcp.name ?? config.title,\n route: config.agents.mcp.route,\n }\n : null,\n // `og.enabled` is resolved to a definite boolean in `loadConfig`; coerce\n // the optional schema type so the serialized shape stays `boolean`.\n // Card fonts are baked into the generated OG endpoint (they can carry\n // absolute build-machine paths), not serialized here — this snapshot\n // ends up in every page's client data.\n og: {\n description: resolveOgDescription(config),\n enabled: config.seo.og.enabled ?? false,\n logo: ogLogo,\n palette: config.seo.og.palette,\n site: resolveOgSite(config),\n },\n openInChat: config.ai.openInChat,\n repoUrl,\n search: {\n enabled: config.search.provider.mode !== \"none\",\n popular: resolveSearchPopular(config.search.popular, config.basePath),\n provider: config.search.provider.kind,\n },\n site: config.deployment.options.site ?? null,\n structuredData: config.seo.structuredData,\n theme: config.theme,\n title: config.title,\n toc: config.toc,\n versions: config.versions ?? null,\n webmcp: {\n enabled: config.agents.webmcp,\n llms: config.agents.llmsTxt.enabled,\n },\n x: config.seo.x,\n },\n feeds: buildRssFeeds(project).map((feed) => ({\n href: feed.path,\n title: feed.title,\n })),\n // CSS variables for Astro's <Font> component; matches the astro.config\n // `fonts:` entries derived from the same theme.fonts config (and the\n // same locale-derived subsets, so preloads cover the scripts pages use).\n fontCssVars: configuredFonts(\n config.theme.fonts,\n fontLocaleCodes(config.i18n)\n ),\n navigation: withRepoUrl(graph.navigation),\n // Per-locale navigation; the catch-all selects the active locale's tree.\n navigationByLocale,\n // Per-archived-version navigation; the catch-all selects by the route's\n // version, then locale.\n navigationByVersion: Object.fromEntries(\n Object.entries(graph.navigationByVersion).map(([id, byLocale]) => [\n id,\n Object.fromEntries(\n Object.entries(byLocale).map(([code, nav]) => [\n code,\n withRepoUrl(nav),\n ])\n ),\n ])\n ),\n routes: manifest.routes.map((route) => ({\n alternates: route.alternates,\n collection: route.collection,\n description: route.description ?? null,\n draft: route.draft,\n editUrl: route.editUrl ?? editUrlFor(route.sourcePath),\n entryId: route.entryId,\n fallback: route.fallback ?? false,\n hidden: route.hidden,\n id: route.id,\n indexable: route.indexable,\n lastModified: route.lastModified ?? null,\n locale: route.locale,\n monolingual: route.monolingual ?? false,\n path: route.path,\n title: route.title,\n version: route.version,\n versionAlternates: route.versionAlternates,\n })),\n // Default-locale chrome strings (English baseline when not under i18n).\n ui: defaultUi,\n // Per-locale chrome strings, selected by the catch-all under i18n.\n uiByLocale,\n };\n return `${JSON.stringify(data, null, 2)}\\n`;\n};\n\n/** The resolved plan for the hosted MCP server within a single generate pass. */\ninterface McpPlan {\n /** Directory holding the injected discovery endpoints (`.blume/src/blume-mcp`). */\n dir: string;\n /** `.well-known` discovery routes to inject as prerendered pages. */\n discoveryPages: { entrypoint: string; pattern: string }[];\n enabled: boolean;\n route: string;\n srcDir: string;\n warnings: string[];\n}\n\n/**\n * Decide whether (and how) to generate the MCP server. Skipped — with a\n * warning — when a content page or a custom `.astro` page already occupies its\n * route, so the user's page keeps working instead of colliding.\n */\nconst planMcp = (\n project: BlumeProject,\n srcDir: string,\n userPages: { pattern: string }[]\n): McpPlan => {\n const { config } = project;\n const { route } = config.agents.mcp;\n const dir = join(srcDir, \"blume-mcp\");\n const base: McpPlan = {\n dir,\n discoveryPages: [],\n enabled: false,\n route,\n srcDir,\n warnings: [],\n };\n if (!config.agents.mcp.enabled) {\n return base;\n }\n if (routeIsTaken(userPages, project.graph.pages, route)) {\n return {\n ...base,\n warnings: [\n `MCP server route \"${route}\" is already used by a content or custom page; the MCP server was not generated. Set a different \"agents.mcp.route\" in blume.config.ts.`,\n ],\n };\n }\n return {\n ...base,\n discoveryPages: [\n {\n entrypoint: join(dir, \"discovery.ts\"),\n pattern: \"/.well-known/mcp.json\",\n },\n {\n entrypoint: join(dir, \"server-card.ts\"),\n pattern: \"/.well-known/mcp/server-card.json\",\n },\n ],\n enabled: true,\n };\n};\n\n/** A pass's runtime data modules, published together once every writer ran. */\ntype RuntimeModules = Map<RuntimeModuleId, string>;\n\n/**\n * The agent data snapshot (`blume:mcp-data`) behind the MCP server and the\n * JSON docs API: built once per pass when either is on, published to the\n * runtime modules, and handed to both writers. Null when neither needs it.\n */\nconst publishAgentData = async (\n project: BlumeProject,\n plans: { api: ApiPlan; mcp: McpPlan },\n modules: RuntimeModules\n): Promise<McpData | null> => {\n if (!(plans.mcp.enabled || plans.api.enabled)) {\n return null;\n }\n const data = await buildMcpData(project);\n modules.set(\"blume:mcp-data\", JSON.stringify(data));\n return data;\n};\n\n/** Write the MCP server endpoint and discovery documents. */\nconst writeMcpFiles = async (\n plan: McpPlan,\n write: (path: string, content: string) => Promise<boolean>,\n data: McpData | null\n): Promise<void> => {\n if (!(plan.enabled && data)) {\n return;\n }\n const discoveryInput = {\n base: data.base,\n name: data.name,\n route: plan.route,\n site: data.site,\n version: data.version,\n };\n await Promise.all([\n write(\n join(plan.srcDir, \"pages\", mcpPageFile(plan.route)),\n mcpEndpointTemplate()\n ),\n write(\n join(plan.dir, \"discovery.ts\"),\n staticJsonEndpointTemplate(buildMcpDiscovery(discoveryInput))\n ),\n write(\n join(plan.dir, \"server-card.ts\"),\n staticJsonEndpointTemplate(buildMcpServerCard(discoveryInput))\n ),\n ]);\n};\n\n/** The resolved plan for the JSON docs API within a single generate pass. */\ninterface ApiPlan {\n /**\n * Whether the `/api/` catch-all (JSON 404s) is written: server output, no\n * user page owning a rest route under `/api/`, and no content page served\n * from the `/api` namespace (the catch-all would outrank those pages).\n */\n catchAll: boolean;\n enabled: boolean;\n /** Whether the live endpoints (search) are written — server output only. */\n server: boolean;\n /**\n * Whether `/openapi.json` is generated: skipped when a `public/openapi.json`\n * or a user page owns the route, so a site can publish its own description.\n */\n spec: boolean;\n srcDir: string;\n}\n\n/** Whether a user page pattern is a rest route under `/api/` (`/api/[...x]`). */\nconst ownsApiRest = (page: { pattern: string }): boolean =>\n page.pattern.startsWith(\"/api/[\");\n\n/**\n * Whether a content route lives in the `/api` namespace — a docs section\n * about an API commonly does (`content/api/overview.md` → `/api/overview`).\n * Astro ranks `/api/[...path]` above the content catch-all (`/[...slug]`),\n * so the JSON 404 route would shadow those pages wherever routing decides\n * (the dev server, adapters that don't serve prerendered files first).\n */\nconst contentUnderApi = (page: { route: string }): boolean =>\n page.route === \"/api\" || page.route.startsWith(\"/api/\");\n\n/**\n * Decide what the JSON docs API generates. The prerendered endpoints always\n * ride along when the feature is on (they live under Blume's own `/api/docs/`\n * namespace); the live ones need server output; the OpenAPI description yields\n * to one the project ships itself.\n */\nconst planApi = (\n project: BlumeProject,\n srcDir: string,\n userPages: { pattern: string }[]\n): ApiPlan => {\n const { config, context } = project;\n const server = config.deployment.options.output === \"server\";\n return {\n catchAll:\n server &&\n !userPages.some(ownsApiRest) &&\n !project.graph.pages.some(contentUnderApi),\n enabled: config.agents.api,\n server,\n spec:\n !routeIsTaken(userPages, project.graph.pages, OPENAPI_PATH) &&\n !existsSync(join(context.root, \"public\", \"openapi.json\")),\n srcDir,\n };\n};\n\n/**\n * Write the JSON docs API: the prerendered page index, per-page documents, and\n * navigation; on server output the search endpoint and the `/api/` catch-all;\n * and the OpenAPI description of the whole agent-facing surface. The MCP\n * route reaches the description only when the server was actually planned (a\n * collision can disable it), so it never advertises an endpoint that isn't\n * there.\n */\nconst writeApiFiles = async (\n project: BlumeProject,\n plan: ApiPlan,\n write: (path: string, content: string) => Promise<boolean>,\n data: McpData | null,\n mcp: McpPlan\n): Promise<void> => {\n if (!(plan.enabled && data)) {\n return;\n }\n const { config } = project;\n const mcpRoute = mcp.enabled ? mcp.route : null;\n const apiDir = join(plan.srcDir, \"pages\", \"api\");\n const writes = [\n write(join(apiDir, \"docs\", \"pages.json.ts\"), apiPagesIndexTemplate()),\n write(\n join(apiDir, \"docs\", \"pages\", \"[...route].json.ts\"),\n apiPageTemplate()\n ),\n write(join(apiDir, \"docs\", \"navigation.json.ts\"), apiNavigationTemplate()),\n ];\n if (plan.server) {\n writes.push(write(join(apiDir, \"docs\", \"search.ts\"), apiSearchTemplate()));\n }\n if (plan.catchAll) {\n writes.push(\n write(\n join(apiDir, \"[...path].ts\"),\n apiNotFoundTemplate({ base: data.base, site: data.site })\n )\n );\n }\n if (plan.spec) {\n writes.push(\n write(\n join(plan.srcDir, \"pages\", \"openapi.json.ts\"),\n staticJsonEndpointTemplate(\n buildApiSpec({\n agentReadability: config.agents.agentReadability,\n base: data.base,\n description: config.description,\n llmsTxt: config.agents.llmsTxt.enabled,\n mcpRoute,\n name: config.title,\n search: plan.server,\n site: data.site,\n version: data.version,\n })\n )\n )\n );\n }\n await Promise.all(writes);\n};\n\n/**\n * Decide whether to generate the playground's built-in CORS proxy endpoint.\n * Only the Blume renderer's playground with `proxy: true` needs it — a proxy\n * URL string points at an external service, and `false` sends requests\n * directly. Injected at `/_api-proxy` (rather than written under `pages/`)\n * because Astro treats `_`-prefixed page files as private; the endpoint's own\n * `prerender = false` export wins over the injection default.\n */\nconst planPlaygroundProxy = (config: ResolvedConfig, srcDir: string) => ({\n enabled: needsPlaygroundProxy(config),\n entrypoint: join(srcDir, \"blume-openapi\", \"api-proxy.ts\"),\n pattern: \"/_api-proxy\",\n});\n\n/**\n * The origins the built-in proxy is allowed to reach: one per absolute\n * `servers[].url` across the parsed specs. This is the endpoint's whole trust\n * boundary — the client sends the target as a query parameter, and a reader's\n * custom base URL is not a documented server — so it is derived here, at build\n * time, from the same documents the operation pages render.\n *\n * Relative (`/v1`) and templated (`{env}.api.example.com`) server URLs carry no\n * origin to allow and are skipped; AsyncAPI documents declare `servers` as a\n * map and contribute nothing (the proxy is OpenAPI-only).\n */\nconst specOriginsOf = (spec: ApiSpecData): string[] => {\n const origins = new Set<string>();\n // A GraphQL schema names no servers; its configured live endpoint is the\n // one origin the playground targets.\n if (spec.endpoint !== undefined) {\n try {\n origins.add(new URL(spec.endpoint).origin);\n } catch {\n // Not an absolute URL: nothing to allow.\n }\n }\n // SAFETY: `document` is arbitrary parsed JSON; the assertion only names\n // the optional `servers` shape, and every access below re-checks it —\n // `Array.isArray(servers)` guards the list and `server.url ?? \"\"` the url.\n const { servers } = spec.document as { servers?: { url?: string }[] };\n for (const server of Array.isArray(servers) ? servers : []) {\n const url = server.url ?? \"\";\n // `new URL(\"https://{region}.api.example.com\")` parses — the braces land\n // in the hostname — so templated URLs need an explicit check or their\n // junk literal becomes an allowlist entry no real request can match.\n if (url.includes(\"{\")) {\n continue;\n }\n try {\n origins.add(new URL(url).origin);\n } catch {\n // Not an absolute URL: nothing to allow.\n }\n }\n return [...origins];\n};\n\nconst specOrigins = (data: OpenApiData): string[] =>\n [...new Set(Object.values(data).flatMap(specOriginsOf))].toSorted();\n\n/**\n * Build-time diagnostics for playground sends the built-in proxy would refuse.\n * The baked-in allowlist pools every documented origin, but each spec's\n * playground only ever targets that spec's own servers/endpoint — so a\n * non-empty pool can still leave one spec's Send 403ing on every request.\n * Hence the check is per spec: any spec routed through the built-in proxy\n * whose own origins came out empty (no absolute `servers[].url`, no absolute\n * GraphQL `endpoint`) gets a warning naming it.\n */\nconst proxyAllowlistWarnings = (\n config: ResolvedConfig,\n data: OpenApiData\n): string[] => {\n const proxied = new Set(\n builtinProxyReferences(config).map((reference) => reference.slug)\n );\n const warnings: string[] = [];\n for (const spec of Object.values(data)) {\n if (!proxied.has(spec.slug) || specOriginsOf(spec).length > 0) {\n continue;\n }\n warnings.push(\n spec.kind === \"graphql\"\n ? `The \"${spec.label}\" GraphQL reference (${spec.route}) has playground.proxy: true, but no absolute endpoint is configured for it, so the built-in proxy has no origin to allow and will refuse every request its playground sends. Set \\`endpoint\\` on the graphql() adapter (or the source) to the live GraphQL URL, or point playground.proxy at an external proxy URL.`\n : `The \"${spec.label}\" reference (${spec.route}) has playground.proxy: true, but its spec declares no absolute servers[].url (relative and templated URLs carry no origin), so the built-in proxy has no origin to allow and will refuse every request its playground sends. Add an absolute server URL to the spec, or point playground.proxy at an external proxy URL.`\n );\n }\n return warnings;\n};\n\n/**\n * Write the Ask AI endpoint and, unless the backend runs its own retrieval\n * (Inkeep), the grounding snapshot the endpoint queries at request time. A no-op\n * when Ask AI is disabled.\n */\nconst writeAskFiles = async (\n project: BlumeProject,\n srcDir: string,\n write: (path: string, content: string) => Promise<boolean>,\n modules: RuntimeModules\n): Promise<void> => {\n const { ask } = project.config.ai;\n if (!(ask?.enabled && !ask.endpoint)) {\n return;\n }\n const backend = resolveAskBackend(ask.provider);\n if (backend.grounded) {\n modules.set(\"blume:ask-data\", JSON.stringify(await buildAskData(project)));\n }\n await write(\n join(srcDir, \"pages\", \"api\", \"ask.ts\"),\n askEndpointTemplate(backend, {\n cors: ask.cors,\n instructions: ask.instructions,\n retrieval: ask.retrieval,\n })\n );\n};\n\n/**\n * Write the default 404 page at Astro's reserved `src/pages/404.astro` path so\n * static builds emit `dist/404.html`, plus its Markdown twin at `404.md.ts`\n * (`dist/404.md`) for agents that ask a missing URL for Markdown and its JSON\n * twin at `404.json.ts` (`dist/404.json`, RFC 9457 problem details) for those\n * that ask for JSON. All three are skipped when the project already owns\n * `/404` (a custom `pages/404.astro` or a `404.md` content page), letting it\n * be fully overridden without a route collision; `pruneOrphans` then removes\n * any previously-generated copies.\n */\nconst writeNotFoundPage = async (\n write: (path: string, content: string) => Promise<boolean>,\n srcDir: string,\n pages: { pattern: string }[],\n contentPages: { route: string }[]\n): Promise<void> => {\n if (routeIsTaken(pages, contentPages, \"/404\")) {\n return;\n }\n await Promise.all([\n write(join(srcDir, \"pages\", \"404.astro\"), notFoundPageTemplate()),\n write(join(srcDir, \"pages\", \"404.md.ts\"), notFoundMarkdownTemplate()),\n write(join(srcDir, \"pages\", \"404.json.ts\"), notFoundJsonTemplate()),\n ]);\n};\n\n/**\n * Flatten a diagnostic to a single warning line, appending the suggestion when\n * one exists. Exported for testing.\n */\nexport const diagnosticWarning = (diagnostic: Diagnostic): string =>\n diagnostic.suggestion\n ? `${diagnostic.message} ${diagnostic.suggestion}`\n : diagnostic.message;\n\n/**\n * Missing-dependency preflight: the search provider's SDK, content source\n * SDKs, the Ask AI backend's provider SDK, the deployment adapter's package,\n * and — since React ships with Blume while Vue/Svelte don't — any island\n * framework's Astro integration. A build fails here, before anything is\n * written, with the install command for the project's package manager —\n * otherwise Vite dies later on an opaque unresolved import from the hidden\n * runtime. Dev warns and keeps serving: only the pages and routes that import\n * the package break, and installing it is picked up on the next restart.\n */\nconst dependencyPreflight = async (\n project: BlumeProject,\n frameworks: Set<string>\n): Promise<string[]> => {\n const build = project.mode === \"build\";\n const diagnostic = await missingDependencyDiagnostic(\n project.config,\n project.context.root,\n build ? \"error\" : \"warning\",\n frameworks\n );\n if (!diagnostic) {\n return [];\n }\n if (build) {\n throw new BlumeError(diagnostic);\n }\n return [diagnosticWarning(diagnostic)];\n};\n\nexport interface GenerateResult {\n /** Whether any structural file changed (config/page/content config). */\n structuralChange: boolean;\n /** Non-fatal warnings raised while generating (e.g. a missing API spec). */\n warnings: string[];\n}\n\n/**\n * Statically analyze the user's `components.ts` (never executing it). Empty for\n * a project with no components file; throws `BLUME_COMPONENTS_INVALID` for an\n * override that isn't one of the accepted static forms.\n */\nexport const analyzeComponentsFile = async (\n componentsFile: string | null\n): Promise<ComponentOverrideAnalysis> =>\n componentsFile\n ? analyzeComponentOverrides(\n await readFile(componentsFile, \"utf-8\"),\n componentsFile\n )\n : emptyComponentOverrides();\n\n/** The OG endpoint fonts for a scanned project (see {@link resolveOgFonts}). */\nconst projectOgFonts = (project: BlumeProject): DerivedOgFonts =>\n resolveOgFonts(\n {\n ogFonts: project.config.seo.og.fonts,\n themeFonts: project.config.theme.fonts,\n themeFontsConfigured: project.themeFontsConfigured,\n },\n project.context.root\n );\n\n/**\n * Fail generation when a configured local font file is missing. Warning and\n * continuing is not an option here — the path is emitted into the Astro\n * config and the OG endpoint, which would crash later with a bare ENOENT.\n */\nconst assertFontFilesExist = (project: BlumeProject): void => {\n const { config, context } = project;\n const missing = missingFontFiles(\n { ogFonts: config.seo.og.fonts ?? [], themeFonts: config.theme.fonts },\n context.root\n );\n if (missing.length > 0) {\n throw new BlumeError({\n code: \"BLUME_FONT_FILE_MISSING\",\n message: `Configured font file(s) not found: ${missing.join(\", \")}. Font paths resolve relative to the project root.`,\n severity: \"error\",\n });\n }\n};\n\n/**\n * The client libraries a site needs (see `featuresTemplate`): the EPUB\n * generator when `export.epub` is on, and the Mermaid element when any page's\n * source has a mermaid fence — read from the raw-Markdown mirrors (every\n * route's verbatim source) and, for sources that carry their text on the\n * page record, the record itself.\n */\nexport const clientFeaturesFrom = (\n project: BlumeProject,\n rawMarkdown: Record<string, RawMarkdownEntry>\n): ClientFeatures => ({\n epub: project.config.export.epub,\n mermaid:\n Object.values(rawMarkdown).some((entry) =>\n hasMermaidFence(entry.mdx ?? entry.md ?? \"\")\n ) ||\n project.graph.pages.some(\n (page) => page.body !== undefined && hasMermaidFence(page.body.text)\n ),\n});\n\n/**\n * The theme's code-block icon rules for the languages the site's Markdown\n * uses (see `languageIconCss`), read from the raw-Markdown mirrors and the\n * page records that carry their text.\n */\nexport const languageIconCssFrom = (\n project: BlumeProject,\n rawMarkdown: Record<string, RawMarkdownEntry>\n): string =>\n languageIconCss(\n languageIconSlugsIn(\n [\n ...Object.values(rawMarkdown).map(\n (entry) => entry.mdx ?? entry.md ?? \"\"\n ),\n ...project.graph.pages.map((page) => page.body?.text ?? \"\"),\n ].join(\"\\n\")\n )\n );\n\n/** {@link languageIconCssFrom} over a fresh read of the raw Markdown (eject). */\nexport const languageIconCssFor = async (\n project: BlumeProject\n): Promise<string> =>\n languageIconCssFrom(project, await buildRawMarkdown(project));\n\n/**\n * The deferred sidebar fragments page, only when some group is a disclosure\n * or a drill-in panel — a flat sidebar renders every row on every page, so\n * there is nothing to fetch. A previous pass's page is an orphan the\n * generator removes when the sidebar goes flat again.\n */\nconst writeNavFragments = (\n write: (path: string, content: string) => Promise<boolean>,\n srcDir: string,\n navFragments: boolean\n): Promise<boolean> =>\n navFragments\n ? write(\n join(\n srcDir,\n \"pages\",\n \"blume-nav\",\n \"[version]\",\n \"[locale]\",\n \"[id].astro\"\n ),\n navFragmentTemplate()\n )\n : Promise.resolve(false);\n\n/** {@link clientFeaturesFrom} over a fresh read of the raw Markdown (eject). */\nexport const clientFeaturesFor = async (\n project: BlumeProject\n): Promise<ClientFeatures> =>\n clientFeaturesFrom(project, await buildRawMarkdown(project));\n\n/**\n * Write (or update) the generated `.blume/` Astro runtime for a project.\n * Only files whose content changed are rewritten so Vite HMR stays fast.\n */\nexport const generateRuntime = async (\n project: BlumeProject\n): Promise<GenerateResult> => {\n const { context, config } = project;\n assertFontFilesExist(project);\n const out = context.outDir;\n const srcDir = join(out, \"src\");\n const generatedDir = join(srcDir, \"generated\");\n const askPath = join(srcDir, \"generated\", \"Ask.astro\");\n const themePath = join(srcDir, \"generated\", \"app.css\");\n const searchClientPath = join(srcDir, \"generated\", \"search-client.ts\");\n const featuresPath = join(srcDir, \"generated\", \"features.ts\");\n const examplesPath = join(srcDir, \"generated\", \"examples.ts\");\n const examplesThemePath = join(srcDir, \"generated\", \"examples.css\");\n\n // Record every file this pass writes so orphans (from a now-disabled feature)\n // can be pruned afterwards. `write` wraps the atomic writer and tracks paths.\n const written = new Set<string>();\n const write = (path: string, content: string): Promise<boolean> => {\n written.add(normalize(path));\n return writeIfChanged(path, content);\n };\n // The data snapshots the generated pages import (`blume:data`, the search\n // index, …) are collected here and published in memory at the end of the\n // pass — see `runtime-modules.ts`. An id a feature leaves unset is\n // unpublished, the in-memory counterpart of `pruneOrphans`.\n const modules: RuntimeModules = new Map();\n\n const depsLinkWarning = await ensureDepsLink(out);\n\n // The dev negotiation inputs. Published in memory (below) rather than baked\n // into the generated config, so a content-route change never rewrites\n // `astro.config.mjs` — which would restart the dev server in place.\n const contentRoutes = markdownRoutePaths(project);\n const homeLinkHeader =\n buildHomeLinkHeader(config, contentRoutes) ?? undefined;\n\n const askEnabled = config.ai.ask?.enabled ?? false;\n const exportPdf = config.export.pdf;\n const exportEpub = config.export.epub;\n // Every route's source Markdown: published as `blume:raw-markdown` below,\n // and inspected here for the client features the site needs.\n const rawMarkdown = await buildRawMarkdown(project);\n const clientFeatures = clientFeaturesFrom(project, rawMarkdown);\n const navFragments = navVariants(project.graph).some(({ navigation }) =>\n hasDeferrableGroups(navigation.sidebar)\n );\n const languageIcons = languageIconCssFrom(project, rawMarkdown);\n // Staged (non-filesystem) sources materialize into `.blume/content`; keyed by\n // entryId so i18n duplicates of one entry write a single file. Collected here\n // so math detection also sees staged bodies (they never live under root).\n const staged = collectStaged(project);\n // Statically analyze `components.ts` overrides (never executed): drives\n // hydration on layout/mdx overrides, string-path resolution, and the\n // \"framework component with no client mode\" diagnostic. Independent of the\n // discovery reads, so it joins the same parallel batch.\n const [\n pages,\n detectedReact,\n usesMath,\n userTheme,\n userExamplesCss,\n integrationBridge,\n islandDiscovery,\n exampleDiscovery,\n overrideAnalysis,\n ] = await Promise.all([\n context.pagesRoot ? discoverPages(context.pagesRoot) : Promise.resolve([]),\n detectNeedsReact(context.root),\n detectUsesMath(context.root, staged.values()),\n readUserCss(context.themeFile, generatedDir),\n readUserCss(examplesCssFile(context.root, config), generatedDir),\n loadIntegrationBridge(config, context),\n discoverIslands(context.root),\n discoverExamples(context.root, config.examples.source),\n analyzeComponentsFile(context.componentsFile),\n ]);\n // The `islands/` convention and `components.ts` share one static plan: every\n // entry is imported by path, hydrated ones through a generated wrapper.\n const slotPlan = planComponentSlots(\n islandDiscovery.islands,\n overrideAnalysis\n );\n const overrideWarnings = overrideAnalysis.warnings;\n // Expose the discovered examples for agent-facing Markdown downleveling\n // (`<Component>` → source) before any consumer (raw `.md`, MCP, llms) runs.\n project.examples = exampleMarkdownLookup(exampleDiscovery.examples);\n\n // Each island/example framework enables its Astro renderer. React also\n // switches on for any project `.tsx`/`.jsx` and for Ask AI; Vue/Svelte are\n // island/example-driven. `.astro` examples need no renderer. Component\n // overrides referencing a framework component enable its renderer too.\n const frameworks = new Set<string>([\n ...islandDiscovery.islands.map((island) => island.framework),\n ...exampleDiscovery.examples.map((example) => example.framework),\n ...slotPlan.frameworks,\n ]);\n const dependencyWarnings = await dependencyPreflight(project, frameworks);\n const needsReact = detectedReact || askEnabled || frameworks.has(\"react\");\n const needsVue = frameworks.has(\"vue\");\n const needsSvelte = frameworks.has(\"svelte\");\n\n // Absolute path to the React Compiler babel plugin (null when off). Resolved\n // here, Node-side, so the generated config points babel straight at Blume's\n // shipped copy — see resolveReactCompiler. Any unresolved-but-requested\n // warning is folded into `warnings` below (declared later).\n const reactCompilerPath = resolveReactCompiler(config, needsReact);\n\n // Custom pages that should get a generated OG card (the home most of all).\n // Computed before the MCP `.well-known` routes are appended below — those are\n // private and filtered out anyway, but the intent is the user's pages.\n const ogRoutes = customOgRoutes(pages, config.title, config.seo.og.titles);\n\n // Whether the generated `/changelog` index exists — shared by the OG endpoint\n // (which adds the index's own card) and the page write below. Computed here,\n // before the MCP discovery pages are appended, on the user's own pages.\n const changelogIndex = hasGeneratedChangelog(project, pages);\n\n // The hosted MCP server. The `.well-known` discovery docs are injected as\n // prerendered routes alongside user pages; the server endpoint itself is a\n // normal (server-rendered) page written by `writeMcpFiles`.\n const mcp = planMcp(project, srcDir, pages);\n pages.push(...mcp.discoveryPages);\n\n // The JSON docs API shares the MCP server's snapshot; build it once when\n // either is on.\n const api = planApi(project, srcDir, pages);\n const agentData = await publishAgentData(project, { api, mcp }, modules);\n\n // The parsed OpenAPI specs behind the `blume:openapi` alias, also the source\n // of the proxy's origin allowlist below. The source parsed them during the\n // scan, so reading them here is free.\n const openApiSource = project.sources.find(isOpenApiSource);\n const openApiData = openApiSource ? openApiSource.openApiData() : {};\n\n // The playground's built-in CORS proxy rides the same injection path as the\n // MCP discovery docs; the endpoint itself opts out of prerendering.\n const playgroundProxy = planPlaygroundProxy(config, srcDir);\n if (playgroundProxy.enabled) {\n pages.push({\n entrypoint: playgroundProxy.entrypoint,\n pattern: playgroundProxy.pattern,\n });\n }\n // Computed once: the endpoint template bakes it in below. A spec that\n // contributes no origin of its own gets a per-spec diagnostic\n // (`proxyAllowlistWarnings`) — the proxy would refuse its every send.\n const proxyOrigins = specOrigins(openApiData);\n\n const hasStaged = staged.size > 0;\n // Only emit a project-scanning `docs` collection when a filesystem source\n // actually feeds it. An all-staged project (openapi/notion/…) has only staged\n // sources, so the `docs` glob would otherwise scan (and watch) the whole\n // project root for nothing — see contentConfigTemplate.\n const hasFilesystemSource = project.sources.some((source) => !source.staged);\n const docsCollection = resolveDocsCollection(config, context.root);\n\n // All of these write to distinct generated paths and never read one another's\n // output, so the structural files, the per-convention hydration wrappers, and\n // the Ask/MCP writers all run in one parallel batch. Only the structural\n // writes' change flags feed `structuralChange`, so they stay a nested group.\n const [structural] = await Promise.all([\n Promise.all([\n write(\n join(out, \"astro.config.mjs\"),\n astroConfigTemplate({\n aliases: resolveTsconfigAliases(context.root),\n askPath,\n config,\n contentRoot: docsCollection.base,\n contentRoutes,\n context,\n examplesPath,\n examplesThemePath,\n features: clientFeatures,\n featuresPath,\n integrationBridge,\n needsReact,\n needsSvelte,\n needsVue,\n pages,\n reactCompilerPath,\n searchClientPath,\n themePath,\n })\n ),\n write(\n join(out, \"package.json\"),\n runtimePackageTemplate(\n runtimeDependencies({ config, needsReact, needsSvelte, needsVue })\n )\n ),\n write(join(out, \"tsconfig.json\"), runtimeTsconfigTemplate()),\n write(\n join(srcDir, \"content.config.ts\"),\n contentConfigTemplate({\n collection: docsCollection,\n config,\n context,\n filesystem: hasFilesystemSource,\n staged: hasStaged,\n })\n ),\n write(\n join(srcDir, \"pages\", \"[...slug].astro\"),\n catchAllPageTemplate({\n exportEpub,\n exportPdf,\n mathEnabled: usesMath,\n navFragments,\n needsReact,\n })\n ),\n writeNavFragments(write, srcDir, navFragments),\n // The header's Ask trigger, behind the `blume:ask` alias. Always written\n // (even when Ask is off, as a component that renders nothing) so the alias\n // resolves — the same contract as `blume:search-client`.\n write(askPath, askComponentTemplate(askEnabled)),\n write(join(srcDir, \"generated\", \"components.ts\"), slotPlan.module),\n write(\n join(srcDir, \"generated\", \"examples.ts\"),\n exampleMapTemplate(exampleDiscovery.examples, config.basePath)\n ),\n // The isolated Tailwind entry for `<Component />` preview frames: only\n // the project (and an out-of-root examples directory) is scanned, so the\n // docs theme never reaches a preview. Anything further afield — a sibling\n // package the examples import — is the user's `@source` in examples.css.\n write(\n examplesThemePath,\n examplesEntryTemplate({\n configTokens: buildThemeCss(config.theme),\n sources: exampleScanRoots(context.root, exampleDiscovery.dir).map(\n (dir) => `${dir}/${EXAMPLE_SCAN_GLOB}`\n ),\n userCss: userExamplesCss,\n })\n ),\n write(\n themePath,\n tailwindEntryTemplate({\n configTokens: `${buildThemeCss(config.theme)}${buildFontsCss(config.theme.fonts)}`,\n languageIcons,\n sources: [\n `${BLUME_SRC}/**/*.{astro,ts,tsx}`,\n `${context.root}/**/*.{astro,mdx,ts,tsx}`,\n ],\n twoslashCss: twoslashCss(),\n userTheme,\n })\n ),\n ]),\n // Per-override hydration wrappers for `islands/` convention components and\n // `client:*` layout/mdx overrides. The generated `components.ts` (written\n // above) imports these; orphans from removed overrides are pruned at the\n // end of the pass.\n Promise.all(\n slotPlan.wrappers.map((wrapper) =>\n write(\n join(srcDir, \"generated\", \"component-slots\", `${wrapper.name}.astro`),\n wrapper.content\n )\n )\n ),\n // Per-example live wrappers for the `examples/` convention, resolved by\n // `<Component path>` through the `examples.ts` map (written above, always).\n Promise.all(\n exampleDiscovery.examples.map((example) =>\n write(\n join(\n srcDir,\n \"generated\",\n \"examples\",\n `${exampleSlug(example.path)}.astro`\n ),\n exampleWrapperTemplate(example)\n )\n )\n ),\n writeAskFiles(project, srcDir, write, modules),\n writeMcpFiles(mcp, write, agentData),\n writeApiFiles(project, api, write, agentData, mcp),\n playgroundProxy.enabled\n ? write(playgroundProxy.entrypoint, playgroundProxyTemplate(proxyOrigins))\n : Promise.resolve(false),\n ]);\n\n if (config.seo.og.enabled) {\n await write(\n join(srcDir, \"pages\", \"og\", \"[...slug].png.ts\"),\n ogEndpointTemplate(\n ogRoutes,\n {\n ...projectOgFonts(project),\n cache: {\n dir: ogCacheDir(project.context),\n version: getBlumeVersion(),\n },\n pageDescriptions: config.seo.og.description !== false,\n },\n changelogIndex\n )\n );\n }\n\n // Changelog index (`/changelog`): every release as a row, grouped by year.\n if (changelogIndex) {\n await write(\n join(srcDir, \"pages\", \"changelog.astro\"),\n changelogIndexTemplate({\n exportEpub,\n exportPdf,\n needsReact,\n staged: hasStaged,\n })\n );\n }\n\n // Three independent writes: the per-example preview routes that\n // `<Component />` iframes embed (returning a warning when the configured\n // examples.css is missing), the default 404 page (`/404`, unless the project\n // already owns the route), and the provider-specific client loader behind\n // the `blume:search-client` alias — always (re)generated so the alias\n // resolves even when search is disabled.\n const [examplesWarnings] = await Promise.all([\n writeExamplesPreview({\n config,\n hasExamples: exampleDiscovery.examples.length > 0,\n root: context.root,\n srcDir,\n write,\n }),\n writeNotFoundPage(write, srcDir, pages, project.graph.pages),\n write(searchClientPath, searchClientTemplate(config)),\n write(featuresPath, featuresTemplate(clientFeatures)),\n ]);\n\n // Client-loaded adapters (orama, flexsearch) ship a static index + endpoint.\n const searchAdapter = config.search.provider;\n if (searchAdapter.mode === \"static\") {\n const documents = await buildSearchDocuments(project);\n modules.set(\"blume:search-index\", JSON.stringify(documents));\n await write(\n join(srcDir, \"pages\", \"blume-search.json.ts\"),\n searchEndpointTemplate()\n );\n }\n\n // Mixedbread proxies queries through a server endpoint that holds the key.\n if (searchAdapter.kind === \"mixedbread\") {\n await write(\n join(srcDir, \"pages\", \"api\", \"search.ts\"),\n mixedbreadSearchEndpointTemplate(searchAdapter.options)\n );\n }\n\n // The include graph (partial → including pages) behind `includeHmrPlugin`:\n // editing a partial invalidates every page that splices it. Written even\n // when empty so the plugin's configured path always resolves.\n await write(\n join(srcDir, \"generated\", \"includes.json\"),\n `${JSON.stringify(buildIncludeGraph(project.graph.pages))}\\n`\n );\n\n modules.set(\"blume:raw-markdown\", JSON.stringify(rawMarkdown));\n // The originals behind the rewritten `/blume-assets/content/…` references in\n // the agent-facing Markdown, plus the endpoint that serves them (and the\n // remote-source assets materialized under `.blume/public/blume-assets`).\n const contentAssets = await collectContentAssets(project);\n modules.set(\"blume:content-assets\", JSON.stringify(contentAssets));\n await Promise.all([\n write(\n join(srcDir, \"pages\", \"[...slug].md.ts\"),\n rawMarkdownEndpointTemplate(\"md\")\n ),\n write(\n join(srcDir, \"pages\", \"[...slug].mdx.ts\"),\n rawMarkdownEndpointTemplate(\"mdx\")\n ),\n write(\n join(srcDir, \"pages\", \"blume-assets\", \"[...asset].ts\"),\n contentAssetsEndpointTemplate(\n join(project.context.outDir, \"public\", \"blume-assets\")\n )\n ),\n ]);\n\n // Automatic RSS feeds for blog/changelog content types (a no-op when no such\n // pages exist or no deployment.site is configured).\n const feeds = buildRssFeeds(project);\n if (feeds.length > 0) {\n const feedXml = Object.fromEntries(\n feeds.map((feed) => [feed.type, renderRssFeed(feed)])\n );\n modules.set(\"blume:rss\", JSON.stringify(feedXml));\n await write(\n join(srcDir, \"pages\", \"[section]\", \"rss.xml.ts\"),\n rssEndpointTemplate()\n );\n }\n\n // `scalar()` reference pages. One self-contained page per source, mounted\n // on its configured route and regenerated each run.\n const warnings: string[] = [\n ...(depsLinkWarning ? [depsLinkWarning] : []),\n ...proxyAllowlistWarnings(config, openApiData),\n ...reactCompilerWarnings(config, needsReact, reactCompilerPath),\n ...mcp.warnings,\n ...islandDiscovery.warnings,\n ...exampleDiscovery.warnings,\n ...examplesWarnings,\n ...overrideWarnings,\n ];\n\n // Missing-navigation-target check, now that every servable route is known:\n // content routes, custom `.astro` pages, the generated changelog, and any\n // OpenAPI reference routes (so a tab an author points at one still validates).\n const navTargetRoutes = new Set<string>([\n ...project.graph.routes.keys(),\n ...pages.map((page) => page.pattern),\n ...referenceRoutes(config),\n ]);\n if (changelogIndex) {\n navTargetRoutes.add(\"/changelog\");\n }\n // Curated `search.popular` icons live outside the navigation model, so they\n // miss `validateNavIcons` in the graph build — they're checked here too,\n // where the search config is known. A typo otherwise just renders the\n // default glyph.\n warnings.push(\n ...[\n ...validateNavTargets(project.graph.navigation, navTargetRoutes),\n ...validateSearchPopularIcons(config.search.popular),\n ].map(diagnosticWarning)\n );\n\n // Unknown-component check: a `<Tag>` in MDX that isn't a built-in, an island,\n // or a `components.ts` override. Needs the project's own components, known here.\n const knownComponentTags = new Set<string>([\n ...islandDiscovery.islands.map((island) => island.name),\n ...overrideAnalysis.mdx.map((entry) => entry.key),\n ]);\n warnings.push(\n ...validateUsedComponents(\n project.graph.pages,\n knownComponentTags,\n new Set(registry.map((item) => item.name))\n ).map(diagnosticWarning),\n ...dependencyWarnings\n );\n if (hasScalarReferences(config)) {\n const references = await buildReferenceFiles({\n config,\n contentRoutes: new Set(project.graph.pages.map((page) => page.route)),\n root: context.root,\n });\n warnings.push(...references.warnings);\n await Promise.all(\n references.files.map((file) =>\n write(join(srcDir, \"pages\", file.pagePath), file.content)\n )\n );\n }\n\n // `blume:openapi` is always published — even as `{}` — so the import resolves\n // whether or not a reference is enabled; the specs were parsed during the\n // scan, so this is just serialization. `blume:data` is the page data every\n // layout reads. Neither is \"structural\" for Astro; publishing hot-reloads.\n modules.set(\"blume:data\", buildRuntimeData(project));\n modules.set(\"blume:openapi\", JSON.stringify(openApiData));\n // These write to distinct trees and never read one another, so they batch.\n // `writeStagedContent` owns the `.blume/content` tree (its own pruning),\n // outside `.blume/src`, so a removed remote entry doesn't linger.\n await Promise.all([\n write(\n join(out, \"blume.manifest.json\"),\n `${JSON.stringify(project.manifest, null, 2)}\\n`\n ),\n writeStagedContent(out, staged),\n ]);\n\n // Remove anything under `.blume/src` this pass didn't write — e.g. an Ask AI\n // endpoint left behind after the feature was switched off.\n await pruneOrphans(srcDir, written);\n\n // Publish last, once every page that imports a module is on disk: a live\n // dev server invalidates the changed modules and reloads the browser against\n // the finished tree, never a half-written one.\n publishDevNegotiation({ contentRoutes, homeLinkHeader });\n publishRuntimeModules(modules);\n\n return { structuralChange: structural.some(Boolean), warnings };\n};\n",
|
|
11
|
+
"/**\n * RFC 9457 problem details — the one error shape every Blume JSON endpoint\n * returns, so an agent that hits a missing page, a bad query, or an unknown\n * API route always gets a stable machine-readable `code`, a human-readable\n * `detail`, and a `resolution` telling it where to go next.\n */\n\nexport const PROBLEM_TYPE = \"application/problem+json\";\n\n/** A recovery link carried on a problem (the 404's \"where to look next\"). */\nexport interface ProblemLink {\n href: string;\n label: string;\n}\n\nexport interface Problem {\n /** Stable, screaming-snake error code for programmatic handling. */\n code: string;\n /** Human-readable explanation specific to this occurrence. */\n detail: string;\n /** The request path the problem occurred on, when known. */\n instance?: string;\n /** Recovery links, when the problem has somewhere useful to send the caller. */\n links?: ProblemLink[];\n /** What to do next — the hint agents act on. */\n resolution: string;\n status: number;\n title: string;\n /** Problem type URI; `about:blank` when the status code says it all. */\n type: string;\n}\n\n/** The problem's members, with `type` defaulting to `about:blank`. */\nexport const problem = (\n input: Omit<Problem, \"type\"> & { type?: string }\n): Problem => {\n const body: Problem = {\n code: input.code,\n detail: input.detail,\n resolution: input.resolution,\n status: input.status,\n title: input.title,\n type: input.type ?? \"about:blank\",\n };\n if (input.instance !== undefined) {\n body.instance = input.instance;\n }\n if (input.links !== undefined) {\n body.links = input.links;\n }\n return body;\n};\n\n/** A `Response` carrying the problem as `application/problem+json`. */\nexport const problemResponse = (\n input: Omit<Problem, \"type\"> & { type?: string }\n): Response => {\n const body = problem(input);\n return new Response(`${JSON.stringify(body, null, 2)}\\n`, {\n headers: { \"Content-Type\": `${PROBLEM_TYPE}; charset=utf-8` },\n status: body.status,\n });\n};\n",
|
|
12
|
+
"import { apiNamePhrase } from \"../../core/api-name.ts\";\nimport { withBasePath } from \"../../core/base-path.ts\";\nimport { absoluteUrl, siteRoot } from \"../../core/site-url.ts\";\nimport {\n API_NAVIGATION_PATH,\n API_PAGE_PATH,\n API_PAGES_PATH,\n API_SEARCH_PATH,\n} from \"./paths.ts\";\nimport { PROBLEM_TYPE } from \"./problem.ts\";\n\n/**\n * The OpenAPI 3.1 description of a Blume site's machine-readable surface,\n * served at `/openapi.json`. It documents the JSON docs API (`/api/docs/…`)\n * in full — one `operationId` and description per operation, typed\n * parameters, response schemas, and the RFC 9457 problem shape every error\n * uses — and lists the text surfaces alongside (the `.md` mirrors, `llms.txt`,\n * `llms-full.txt`, `agent-readability.json`) plus the MCP endpoint, so a\n * function-calling framework that ingests OpenAPI gets the same reach an MCP\n * client has. Generated per build from config, so it only describes what the\n * deployed site actually serves.\n */\n\n/** What the document needs to know about the site. */\nexport interface ApiSpecInput {\n /** Whether `agent-readability.json` is published. */\n agentReadability: boolean;\n /** Normalized `deployment.base` (`\"\"` or `/seg`). */\n base: string;\n description?: string;\n /** Whether `llms.txt`/`llms-full.txt` are published. */\n llmsTxt: boolean;\n /** The MCP server's route, or null when the server is off. */\n mcpRoute: string | null;\n name: string;\n /** Whether the live search endpoint exists (server output only). */\n search: boolean;\n site: string | null;\n /** The generating Blume version — the API contract's version. */\n version: string;\n}\n\n/** A schema reference into `components.schemas`. */\ninterface SchemaRef {\n $ref: string;\n}\n\n/** An inline schema, as the text and generic-object responses use. */\ninterface InlineSchema {\n type: string;\n}\n\ninterface MediaType {\n schema: InlineSchema | SchemaRef;\n}\n\n/** A response object: its description and the media types it may carry. */\ninterface ResponseObject {\n content?: Record<string, MediaType>;\n description: string;\n}\n\n/** A parameter object, as this builder emits it. */\ninterface Parameter {\n description: string;\n explode?: boolean;\n in: \"path\" | \"query\";\n name: string;\n required: boolean;\n schema: object;\n style?: \"deepObject\" | \"form\";\n}\n\n/** An operation object, as this builder emits it. */\nexport interface Operation {\n description: string;\n operationId: string;\n parameters?: Parameter[];\n requestBody?: { content: Record<string, MediaType>; required: boolean };\n responses: Record<string, ResponseObject>;\n summary: string;\n tags: string[];\n}\n\n/** A path item: the HTTP methods this builder emits. */\nexport interface PathItem {\n get?: Operation;\n post?: Operation;\n}\n\ninterface Tag {\n description: string;\n name: string;\n}\n\ninterface Info {\n description: string;\n title: string;\n version: string;\n \"x-generator\": string;\n}\n\n/** The OpenAPI document, as this builder emits it. */\nexport interface ApiSpecDocument {\n components: { schemas: typeof schemas };\n externalDocs?: { description: string; url: string };\n info: Info;\n openapi: \"3.1.0\";\n paths: Record<string, PathItem>;\n security: never[];\n servers: { description: string; url: string }[];\n tags: Tag[];\n}\n\nconst JSON_TYPE = \"application/json\";\nconst MARKDOWN_TYPE = \"text/markdown\";\nconst TEXT_TYPE = \"text/plain\";\n\nconst ref = (name: string): SchemaRef => ({\n $ref: `#/components/schemas/${name}`,\n});\n\nconst jsonResponse = (description: string, schema: string): ResponseObject => ({\n content: { [JSON_TYPE]: { schema: ref(schema) } },\n description,\n});\n\nconst problemResponse = (description: string): ResponseObject => ({\n content: { [PROBLEM_TYPE]: { schema: ref(\"Problem\") } },\n description,\n});\n\nconst textResponse = (description: string, type: string): ResponseObject => ({\n content: { [type]: { schema: { type: \"string\" } } },\n description,\n});\n\nconst string = (description: string) => ({ description, type: \"string\" });\n\nconst facetsSchema = {\n additionalProperties: { type: \"string\" },\n description:\n \"Facet values the site declares for the page's content type (`content.types.<type>.facets`), key → value.\",\n type: \"object\",\n};\n\nconst versionProperty = {\n description:\n 'Docs version the page belongs to on a versioned site: `\"\"` for the current docs, else an archived version id. Absent on unversioned sites.',\n type: \"string\",\n};\n\n/** The shared filter parameters of the search operation. */\nconst searchParameters: Parameter[] = [\n {\n description: \"The search query.\",\n in: \"query\",\n name: \"q\",\n required: true,\n schema: { minLength: 1, type: \"string\" },\n },\n {\n description: \"Maximum hits to return (default 8, at most 20).\",\n in: \"query\",\n name: \"limit\",\n required: false,\n schema: { default: 8, maximum: 20, minimum: 1, type: \"integer\" },\n },\n {\n description:\n \"Only include pages of these content types (frontmatter `type`, e.g. `doc`, `rfc`). Comma-separated or repeated. Omit for every type.\",\n explode: false,\n in: \"query\",\n name: \"contentTypes\",\n required: false,\n schema: { items: { type: \"string\" }, type: \"array\" },\n style: \"form\",\n },\n {\n description:\n \"Only include pages in this locale (e.g. `fr`). Omit for every language.\",\n in: \"query\",\n name: \"locale\",\n required: false,\n schema: { type: \"string\" },\n },\n {\n description:\n \"Docs version to scope to on a versioned site: `latest` (the default — current docs only), `all`, or an archived version id. Ignored when the site is unversioned.\",\n in: \"query\",\n name: \"version\",\n required: false,\n schema: { type: \"string\" },\n },\n {\n description:\n \"Only include pages matching every facet, as `filters[key]=value` pairs (e.g. `filters[status]=enforced`). Facets are metadata the site declares per content type; the page index shows each page's values.\",\n explode: true,\n in: \"query\",\n name: \"filters\",\n required: false,\n schema: { additionalProperties: { type: \"string\" }, type: \"object\" },\n style: \"deepObject\",\n },\n];\n\nconst schemas = {\n JsonRpcRequest: {\n description: \"A JSON-RPC 2.0 request, as the Model Context Protocol sends.\",\n properties: {\n id: { description: \"Request id (absent on notifications).\" },\n jsonrpc: { const: \"2.0\", type: \"string\" },\n method: string(\"The MCP method, e.g. `initialize` or `tools/call`.\"),\n params: { description: \"Method parameters.\", type: \"object\" },\n },\n required: [\"jsonrpc\", \"method\"],\n type: \"object\",\n },\n JsonRpcResponse: {\n description: \"A JSON-RPC 2.0 response: a `result` or an `error`.\",\n properties: {\n error: {\n properties: {\n code: { type: \"integer\" },\n data: {},\n message: { type: \"string\" },\n },\n required: [\"code\", \"message\"],\n type: \"object\",\n },\n id: {},\n jsonrpc: { const: \"2.0\", type: \"string\" },\n result: { type: \"object\" },\n },\n required: [\"jsonrpc\"],\n type: \"object\",\n },\n NavLink: {\n properties: {\n href: string(\"Link target — an internal route or an external URL.\"),\n icon: string(\"Optional icon name.\"),\n label: { type: \"string\" },\n },\n required: [\"href\", \"label\"],\n type: \"object\",\n },\n NavNode: {\n description:\n \"A sidebar entry: a page (linking to its route) or a group holding further nodes.\",\n oneOf: [\n {\n properties: {\n badge: { type: \"string\" },\n deprecated: { type: \"boolean\" },\n description: { type: \"string\" },\n icon: { type: \"string\" },\n kind: { const: \"page\", type: \"string\" },\n label: { type: \"string\" },\n pageId: string(\"The page's stable content id.\"),\n route: string(\"The page's route.\"),\n },\n required: [\"kind\", \"label\", \"pageId\", \"route\"],\n type: \"object\",\n },\n {\n properties: {\n badge: { type: \"string\" },\n children: { items: ref(\"NavNode\"), type: \"array\" },\n collapsed: { type: \"boolean\" },\n icon: { type: \"string\" },\n kind: { const: \"group\", type: \"string\" },\n label: { type: \"string\" },\n path: string(\"The group's route prefix (not necessarily a page).\"),\n route: string(\"The group's index page route, when it has one.\"),\n },\n required: [\"children\", \"kind\", \"label\"],\n type: \"object\",\n },\n ],\n },\n NavSelector: {\n description:\n \"A top-level partition selector (products, versions, languages).\",\n properties: {\n items: {\n items: {\n properties: {\n description: { type: \"string\" },\n label: { type: \"string\" },\n path: { type: \"string\" },\n tag: { type: \"string\" },\n },\n required: [\"label\", \"path\"],\n type: \"object\",\n },\n type: \"array\",\n },\n kind: { type: \"string\" },\n label: { type: \"string\" },\n },\n required: [\"items\", \"kind\", \"label\"],\n type: \"object\",\n },\n NavTab: {\n properties: {\n href: string(\n \"The clickable target when it differs from `path` (the section's first page).\"\n ),\n icon: { type: \"string\" },\n label: { type: \"string\" },\n path: string(\"The tab's section prefix.\"),\n },\n required: [\"label\", \"path\"],\n type: \"object\",\n },\n Navigation: {\n description:\n \"The docs navigation model: header tabs, the sidebar tree, partition selectors, and pinned links.\",\n properties: {\n actions: { items: ref(\"NavLink\"), type: \"array\" },\n cta: { oneOf: [ref(\"NavLink\"), { type: \"null\" }] },\n featured: { items: ref(\"NavLink\"), type: \"array\" },\n repoUrl: { type: [\"string\", \"null\"] },\n root: string(\"The tree root's route (`/`, or a locale/version prefix).\"),\n selectors: { items: ref(\"NavSelector\"), type: \"array\" },\n sidebar: { items: ref(\"NavNode\"), type: \"array\" },\n tabs: { items: ref(\"NavTab\"), type: \"array\" },\n },\n required: [\"featured\", \"selectors\", \"sidebar\", \"tabs\"],\n type: \"object\",\n },\n Page: {\n allOf: [\n ref(\"PageSummary\"),\n {\n properties: {\n markdown: string(\n \"The page as agent Markdown: frontmatter included, components downleveled to plain Markdown.\"\n ),\n },\n required: [\"markdown\"],\n type: \"object\",\n },\n ],\n description: \"A page's index entry plus its full Markdown body.\",\n },\n PageSummary: {\n properties: {\n contentType: string(\"The page's content type (frontmatter `type`).\"),\n description: { type: \"string\" },\n facets: facetsSchema,\n json: {\n description:\n \"This page's JSON representation (the `getPage` operation).\",\n format: \"uri-reference\",\n type: \"string\",\n },\n lastModified: {\n description: \"ISO 8601 last-modified date, when known.\",\n type: [\"string\", \"null\"],\n },\n locale: string(\"The page's locale code.\"),\n markdownUrl: {\n description:\n \"The page's raw-Markdown mirror (the `getPageMarkdown` operation).\",\n format: \"uri-reference\",\n type: \"string\",\n },\n route: string(\n \"The page's route (`/guides/install`); the key every other operation takes.\"\n ),\n title: { type: \"string\" },\n url: {\n description: \"Where the rendered page is served.\",\n format: \"uri-reference\",\n type: \"string\",\n },\n version: versionProperty,\n },\n required: [\n \"contentType\",\n \"json\",\n \"lastModified\",\n \"locale\",\n \"markdownUrl\",\n \"route\",\n \"title\",\n \"url\",\n ],\n type: \"object\",\n },\n PagesIndex: {\n properties: {\n count: { type: \"integer\" },\n generator: string(\"The Blume version that built the site.\"),\n pages: { items: ref(\"PageSummary\"), type: \"array\" },\n site: { type: [\"string\", \"null\"] },\n },\n required: [\"count\", \"generator\", \"pages\", \"site\"],\n type: \"object\",\n },\n Problem: {\n description:\n \"RFC 9457 problem details, with a stable code and a resolution hint.\",\n properties: {\n code: string(\"Stable error code (e.g. `PAGE_NOT_FOUND`).\"),\n detail: string(\"Human-readable explanation of this occurrence.\"),\n instance: string(\"The request path the problem occurred on.\"),\n links: {\n description: \"Recovery links, when there is somewhere useful to go.\",\n items: {\n properties: { href: { type: \"string\" }, label: { type: \"string\" } },\n required: [\"href\", \"label\"],\n type: \"object\",\n },\n type: \"array\",\n },\n resolution: string(\"What to do next.\"),\n status: { type: \"integer\" },\n title: { type: \"string\" },\n type: string(\"Problem type URI; `about:blank` by default.\"),\n },\n required: [\"code\", \"detail\", \"resolution\", \"status\", \"title\", \"type\"],\n type: \"object\",\n },\n SearchHit: {\n properties: {\n contentType: { type: \"string\" },\n excerpt: string(\"The page description, else the start of its content.\"),\n facets: facetsSchema,\n route: string(\"The page's route; pass it to `getPage`.\"),\n title: { type: \"string\" },\n url: { format: \"uri-reference\", type: \"string\" },\n version: versionProperty,\n },\n required: [\"excerpt\", \"route\", \"title\", \"url\"],\n type: \"object\",\n },\n SearchResponse: {\n properties: {\n count: { type: \"integer\" },\n query: { type: \"string\" },\n results: { items: ref(\"SearchHit\"), type: \"array\" },\n },\n required: [\"count\", \"query\", \"results\"],\n type: \"object\",\n },\n};\n\nconst ROUTE_PARAM: Parameter = {\n description:\n \"The page route without its leading slash (`guides/install`), or `index` for the home page. May contain slashes.\",\n in: \"path\",\n name: \"route\",\n required: true,\n schema: { type: \"string\" },\n};\n\n/** The document's `servers[0].url`: the site origin plus base, or the base. */\nconst serverUrl = (input: ApiSpecInput): string => {\n if (input.site) {\n return input.base\n ? absoluteUrl(input.site, input.base)\n : siteRoot(input.site);\n }\n return input.base || \"/\";\n};\n\n/** Build the OpenAPI document. Plain data, ready to serialize. */\nexport const buildApiSpec = (input: ApiSpecInput): ApiSpecDocument => {\n const pathEntries: [string, PathItem][] = [\n [\n API_PAGES_PATH,\n {\n get: {\n description:\n \"Every documentation page with its route, title, description, content type, locale, facets, and the URLs of its rendered, Markdown, and JSON forms. Unfiltered; on a versioned site every version is listed with its `version`. Use it to enumerate the docs or to find a page when search is too narrow.\",\n operationId: \"listPages\",\n responses: {\n \"200\": jsonResponse(\"The page index.\", \"PagesIndex\"),\n default: problemResponse(\"An error, as problem details.\"),\n },\n summary: \"List every page\",\n tags: [\"Pages\"],\n },\n },\n ],\n [\n API_PAGE_PATH,\n {\n get: {\n description:\n \"A single page as JSON: its index entry plus the page's agent Markdown (frontmatter included, components downleveled to plain Markdown). Take `route` from `listPages` or `searchDocs`.\",\n operationId: \"getPage\",\n parameters: [ROUTE_PARAM],\n responses: {\n \"200\": jsonResponse(\"The page.\", \"Page\"),\n \"404\": problemResponse(\"No page has that route.\"),\n default: problemResponse(\"An error, as problem details.\"),\n },\n summary: \"Get a page as JSON\",\n tags: [\"Pages\"],\n },\n },\n ],\n [\n API_NAVIGATION_PATH,\n {\n get: {\n description:\n \"The navigation tree (header tabs and the sidebar hierarchy) as readers see it, for the default locale and the current docs.\",\n operationId: \"getNavigation\",\n responses: {\n \"200\": jsonResponse(\"The navigation tree.\", \"Navigation\"),\n default: problemResponse(\"An error, as problem details.\"),\n },\n summary: \"Get the navigation tree\",\n tags: [\"Navigation\"],\n },\n },\n ],\n ];\n if (input.search) {\n pathEntries.push([\n API_SEARCH_PATH,\n {\n get: {\n description:\n \"Full-text search across the documentation. Returns matching pages with their title, route, content type, and a short excerpt; narrow by content type, locale, version, or facet. Use it first to discover relevant pages, then `getPage` to read one in full.\",\n operationId: \"searchDocs\",\n parameters: searchParameters,\n responses: {\n \"200\": jsonResponse(\n \"The matching pages, best first.\",\n \"SearchResponse\"\n ),\n \"400\": problemResponse(\"The query was missing or blank.\"),\n default: problemResponse(\"An error, as problem details.\"),\n },\n summary: \"Search the docs\",\n tags: [\"Search\"],\n },\n },\n ]);\n }\n pathEntries.push([\n \"/{route}.md\",\n {\n get: {\n description:\n \"A page's raw-Markdown mirror: append `.md` to any page URL. Components are downleveled to plain Markdown; `.mdx` serves the source as written. The same body the `getPage` operation carries in its `markdown` field.\",\n operationId: \"getPageMarkdown\",\n parameters: [ROUTE_PARAM],\n responses: {\n \"200\": textResponse(\"The page as Markdown.\", MARKDOWN_TYPE),\n \"404\": textResponse(\n \"No page has that route; the body lists where to look next.\",\n MARKDOWN_TYPE\n ),\n },\n summary: \"Get a page as Markdown\",\n tags: [\"Markdown\"],\n },\n },\n ]);\n if (input.llmsTxt) {\n pathEntries.push(\n [\n \"/llms.txt\",\n {\n get: {\n description:\n \"The llms.txt index: the site's summary, when to use it, and every page with a one-line description, grouped by section.\",\n operationId: \"getLlmsTxt\",\n responses: {\n \"200\": textResponse(\"The index.\", TEXT_TYPE),\n },\n summary: \"Get llms.txt\",\n tags: [\"Markdown\"],\n },\n },\n ],\n [\n \"/llms-full.txt\",\n {\n get: {\n description:\n \"The full Markdown of every current-docs page in one file.\",\n operationId: \"getLlmsFullTxt\",\n responses: {\n \"200\": textResponse(\"Every page's Markdown.\", TEXT_TYPE),\n },\n summary: \"Get llms-full.txt\",\n tags: [\"Markdown\"],\n },\n },\n ]\n );\n }\n if (input.agentReadability) {\n pathEntries.push([\n \"/agent-readability.json\",\n {\n get: {\n description:\n \"A manifest indexing every agent-facing artifact the site publishes — this API, the Markdown mirrors, llms.txt, the MCP server, feeds, and the sitemap.\",\n operationId: \"getAgentReadability\",\n responses: {\n \"200\": {\n content: { [JSON_TYPE]: { schema: { type: \"object\" } } },\n description: \"The manifest.\",\n },\n },\n summary: \"Get the agent-readability manifest\",\n tags: [\"Discovery\"],\n },\n },\n ]);\n }\n if (input.mcpRoute) {\n pathEntries.push([\n input.mcpRoute,\n {\n post: {\n description:\n \"The Model Context Protocol server (Streamable HTTP, stateless, JSON responses). Tools: `search_docs`, `get_page`, `list_pages`, `get_navigation` — the same operations this API exposes — plus every page as a `text/markdown` resource. Discovery document at `/.well-known/mcp.json`.\",\n operationId: \"mcp\",\n requestBody: {\n content: { [JSON_TYPE]: { schema: ref(\"JsonRpcRequest\") } },\n required: true,\n },\n responses: {\n \"200\": jsonResponse(\"The JSON-RPC response.\", \"JsonRpcResponse\"),\n },\n summary: \"Call the MCP server\",\n tags: [\"MCP\"],\n },\n },\n ]);\n }\n\n const tags: Tag[] = [\n { description: \"Enumerate and read documentation pages.\", name: \"Pages\" },\n ...(input.search\n ? [{ description: \"Full-text search over the docs.\", name: \"Search\" }]\n : []),\n { description: \"How the docs are organized.\", name: \"Navigation\" },\n { description: \"Plain-text and Markdown surfaces.\", name: \"Markdown\" },\n ...(input.agentReadability\n ? [{ description: \"Agent discovery documents.\", name: \"Discovery\" }]\n : []),\n ...(input.mcpRoute\n ? [{ description: \"The Model Context Protocol endpoint.\", name: \"MCP\" }]\n : []),\n ];\n\n const info: Info = {\n description: [\n `Read-only JSON API over the ${input.name} documentation${input.description ? `: ${input.description}` : \".\"}`,\n \"Every operation is public and needs no authentication. Errors are RFC 9457 problem details (`application/problem+json`) with a stable `code`, a `detail`, and a `resolution` hint.\",\n ].join(\"\\n\\n\"),\n title: apiNamePhrase(input.name),\n version: input.version,\n \"x-generator\": `blume@${input.version}`,\n };\n const document: ApiSpecDocument = {\n components: { schemas },\n info,\n openapi: \"3.1.0\",\n paths: Object.fromEntries(pathEntries),\n security: [],\n servers: [{ description: input.name, url: serverUrl(input) }],\n tags,\n };\n if (input.site) {\n document.externalDocs = {\n description: `${input.name} documentation`,\n url: absoluteUrl(input.site, withBasePath(input.base, \"/\")),\n };\n }\n return document;\n};\n",
|
|
13
|
+
"import type { BlumeProject } from \"../core/project-graph.ts\";\nimport { buildSearchDocuments } from \"../search/documents.ts\";\nimport type { AskData } from \"./ask-context.ts\";\n\n/**\n * Build the grounding snapshot the Ask AI endpoint serves. Like the MCP server,\n * Ask AI is independent of on-page search, so documents are indexed even when the\n * search provider is `none` (`includeWhenDisabled`). `locale` is kept (unlike the\n * MCP snapshot) so retrieval can be filtered to the current page's language, and\n * content is kept as Markdown so grounding sees fenced code examples — the model\n * answers \"what does the config look like?\" from the docs instead of declining.\n * The reader is an AI agent, so `<Visibility>` resolves for the agents audience\n * (web-only content removed, agents-only unwrapped) and components downlevel to\n * Markdown, both matching llms-full.txt.\n */\nexport const buildAskData = async (project: BlumeProject): Promise<AskData> => {\n const documents = await buildSearchDocuments(project, {\n audience: \"agents\",\n content: \"markdown\",\n includeWhenDisabled: true,\n });\n return {\n defaultLocale: project.config.i18n?.defaultLocale,\n documents: documents.map((doc) => ({\n content: doc.content,\n description: doc.description,\n locale: doc.locale,\n route: doc.route,\n title: doc.title,\n })),\n site: project.config.deployment.options.site ?? null,\n };\n};\n",
|
|
14
|
+
"import { isRootTab, isUnderPath } from \"../../core/navigation.ts\";\nimport type { NavNode, NavTab, Navigation } from \"../../core/types.ts\";\n\n/** A flat, ordered page reference used for previous/next pagination. */\nexport interface FlatPage {\n route: string;\n label: string;\n deprecated?: boolean;\n}\n\n/** A breadcrumb segment; `route` is absent for non-clickable group ancestors. */\nexport interface Crumb {\n label: string;\n route?: string;\n}\n\n/**\n * Whether a group's header row reads as the current page. A routed group links\n * to its folder's index page; when that index is also listed as one of the\n * group's own rows (kept under a different label), the row is the more\n * specific indicator, so the header stays quiet rather than lighting up beside\n * it. With the index row hidden the header is the only link and takes the\n * highlight.\n */\nexport const isGroupRowCurrent = (\n group: Extract<NavNode, { kind: \"group\" }>,\n currentRoute: string\n): boolean =>\n group.route === currentRoute &&\n !group.children.some(\n (child) => child.kind === \"page\" && child.route === currentRoute\n );\n\n/** Flatten the sidebar tree into ordered internal page links. */\nexport const flattenPages = (nodes: NavNode[]): FlatPage[] => {\n const out: FlatPage[] = [];\n const seen = new Set<string>();\n const add = (page: FlatPage): void => {\n if (seen.has(page.route)) {\n return;\n }\n seen.add(page.route);\n out.push(page);\n };\n const walk = (items: NavNode[]): void => {\n for (const item of items) {\n if (item.kind === \"group\") {\n if (item.route) {\n add({ label: item.label, route: item.route });\n }\n walk(item.children);\n } else if (item.pageId) {\n // Skip external links (no backing page).\n add(\n item.deprecated\n ? { deprecated: true, label: item.label, route: item.route }\n : { label: item.label, route: item.route }\n );\n }\n }\n };\n walk(nodes);\n return out;\n};\n\n/** Find the breadcrumb trail (group ancestors + page) for a route. */\nexport const findBreadcrumbs = (nodes: NavNode[], route: string): Crumb[] => {\n const search = (items: NavNode[], trail: Crumb[]): Crumb[] | null => {\n for (const item of items) {\n if (item.kind === \"page\") {\n if (item.route === route) {\n return [...trail, { label: item.label, route: item.route }];\n }\n } else {\n const crumb: Crumb = item.route\n ? { label: item.label, route: item.route }\n : { label: item.label };\n if (item.route === route) {\n return [...trail, crumb];\n }\n const found = search(item.children, [...trail, crumb]);\n if (found) {\n return found;\n }\n }\n }\n return null;\n };\n return search(nodes, []) ?? [];\n};\n\n/**\n * The tab whose `path` is the longest prefix of `route`. The root tab (`/`)\n * acts as the fallback when no more specific tab matches.\n */\nexport const activeTabForRoute = (\n tabs: NavTab[],\n route: string\n): NavTab | null => {\n let match: NavTab | null = null;\n for (const tab of tabs) {\n if (!isUnderPath(route, tab.path)) {\n continue;\n }\n if (!match || tab.path.length > match.path.length) {\n match = tab;\n }\n }\n return match;\n};\n\n/**\n * The tab to mark as the current one (`aria-current`) for `route`. Same\n * longest-prefix match as {@link activeTabForRoute}, except inside an archived\n * version tree: the root tab claims every archived route through its\n * spans-everything fallback while its link points back at the current docs, so\n * no tab is genuinely current there.\n */\nexport const currentTabForRoute = (\n tabs: NavTab[],\n route: string,\n root = \"/\"\n): NavTab | null => {\n const tab = activeTabForRoute(tabs, route);\n // The root tab sitting away from the tree root means the tree is a version\n // snapshot in a different path space — the tab matched as a fallback, not\n // because it owns the route.\n if (tab && isRootTab(tab, root) && tab.path !== root) {\n return null;\n }\n return tab;\n};\n\n/**\n * The children of the group whose path is `base`, searched at any depth — so a\n * content tree wrapped in a top-level container group still resolves to the\n * right section. Returns null when no group sits exactly at `base`.\n */\nconst sectionChildren = (nodes: NavNode[], base: string): NavNode[] | null => {\n for (const node of nodes) {\n if (node.kind !== \"group\") {\n continue;\n }\n if (node.path === base || node.route === base) {\n return node.children;\n }\n const deeper = sectionChildren(node.children, base);\n if (deeper) {\n return deeper;\n }\n }\n return null;\n};\n\n/** Whether a group maps to a header tab (matched on its path or link route). */\nconst isTabSection = (node: NavNode, tabPaths: Set<string>): boolean => {\n if (node.kind !== \"group\") {\n return false;\n }\n const byPath = node.path !== undefined && tabPaths.has(node.path);\n const byRoute = node.route !== undefined && tabPaths.has(node.route);\n return byPath || byRoute;\n};\n\n/**\n * Drop the groups that already own a header tab from the tree, at any depth —\n * so a root/un-tabbed route lists only the pages outside every tab's section\n * instead of duplicating each tab as a sidebar group. A container left empty by\n * this pruning is dropped too, so no bare heading is stranded. The root tab\n * spans everything, so it never removes anything.\n *\n * A group with no tab section anywhere beneath it is kept as the same object:\n * the stable group ids (`navGroupIds`) and the build-time subtree cache are\n * both keyed by node identity, so a copy would lose its id — its collapsed\n * fragment was then requested by a positional name no route serves — and\n * re-render on every page. A container that did lose a section is rebuilt,\n * so it has no id: the deferred fragments render from the full tree, which\n * would put the section back, so `NavTree` renders such a container in full\n * instead (its untouched children still defer by their own ids).\n */\nconst withoutTabSections = (\n nodes: NavNode[],\n tabs: NavTab[],\n root: string\n): NavNode[] => {\n const tabPaths = new Set<string>();\n for (const tab of tabs) {\n if (!isRootTab(tab, root)) {\n tabPaths.add(tab.path);\n }\n }\n if (tabPaths.size === 0) {\n return nodes;\n }\n const prune = (items: NavNode[]): NavNode[] => {\n const kept: NavNode[] = [];\n for (const item of items) {\n if (isTabSection(item, tabPaths)) {\n continue;\n }\n if (item.kind === \"group\") {\n const children = prune(item.children);\n if (\n children.length === item.children.length &&\n children.every((child, index) => child === item.children[index])\n ) {\n kept.push(item);\n } else if (children.length > 0) {\n // A container left empty by pruning is dropped, so no bare heading\n // is stranded.\n kept.push({ ...item, children });\n }\n } else {\n kept.push(item);\n }\n }\n return kept;\n };\n return prune(nodes);\n};\n\n/**\n * Scope the sidebar to the active tab's section. With tabs configured, a route\n * under one tab shows only that tab's group — so a multi-section site (e.g.\n * Adapters / API / AI tabs) drills each tab into its own pages instead of one\n * global tree, the way Fumadocs' root folders do. On a route under no tab (or\n * the root tab), the tab-owned groups are hidden so the root sidebar shows\n * only pages that don't belong to a tab.\n *\n * `root` is the tree root (`Navigation.root`), localized and based like tab\n * paths — under i18n or a `basePath` the root tab sits at `/en` or `/docs`,\n * not `/`, and a bare-`/` comparison would misread it as a section tab (a\n * root-level `(group)` folder's path is exactly that prefix, so the sidebar\n * collapsed to that one group or blanked entirely). In an archived version\n * tree the root is versionized (`/v1.0`) while tab paths stay in current-docs\n * space, so root-tab checks use {@link isRootTab} containment, not equality:\n * the root tab owns no group in a snapshot, and misreading it as a section\n * tab blanked the archived sidebar.\n *\n * When a matched tab owns no sidebar group — a standalone page like the\n * generated changelog timeline (`/changelog`), or a tab whose source produced\n * no pages — the sidebar is empty. It must not fall back to the full tree: that\n * would leak every *other* tab's section (e.g. the OpenAPI operations) onto the\n * page. On a route under no tab, hiding the tab sections falls back to the full\n * sidebar only when it would otherwise blank, so an un-tabbed route stays full.\n */\nexport const sidebarForRoute = (\n sidebar: NavNode[],\n tabs: NavTab[],\n route: string,\n root = \"/\"\n): NavNode[] => {\n const tab = activeTabForRoute(tabs, route);\n if (tab && !isRootTab(tab, root)) {\n return sectionChildren(sidebar, tab.path) ?? [];\n }\n const scoped = withoutTabSections(sidebar, tabs, root);\n return scoped.length > 0 ? scoped : sidebar;\n};\n\n/** Resolve previous/next pages around the current route. */\nexport const getPagination = (flat: FlatPage[], route: string) => {\n const index = flat.findIndex((page) => page.route === route);\n if (index === -1) {\n return { next: null, prev: null };\n }\n return {\n next: flat[index + 1] ?? null,\n prev: index > 0 ? (flat[index - 1] ?? null) : null,\n };\n};\n\n/**\n * A stable id for every group in a sidebar — `g<n>` by pre-order position in\n * the full tree. The layout hands `NavTree` a scoped view of that tree (a\n * tab's section, or the sidebar minus the tab sections), so positions within\n * the rendered slice differ from page to page; these ids name the same group\n * everywhere, which the drill-in panels and the deferred-section fragments\n * (`/blume-nav/…`) rely on. Keyed by node identity: the scoped views reuse\n * the full tree's node objects.\n */\nexport const navGroupIds = (sidebar: NavNode[]): Map<NavNode, string> => {\n const ids = new Map<NavNode, string>();\n const walk = (nodes: NavNode[]): void => {\n for (const node of nodes) {\n if (node.kind === \"group\") {\n ids.set(node, `g${ids.size}`);\n walk(node.children);\n }\n }\n };\n walk(sidebar);\n return ids;\n};\n\n/** Whether any group in a sidebar renders as a disclosure or a drill-in panel. */\nexport const hasDeferrableGroups = (sidebar: NavNode[]): boolean =>\n sidebar.some(\n (node) =>\n node.kind === \"group\" &&\n ((node.display ?? \"flat\") !== \"flat\" ||\n hasDeferrableGroups(node.children))\n );\n\n/** One of the navigation trees a site renders, by URL segment. */\nexport interface NavVariant {\n /** `current`, or an archived version id. */\n version: string;\n /** `default`, or a locale code. */\n locale: string;\n navigation: Navigation;\n}\n\n/**\n * The locale whose code must not appear as a fragment URL segment: the\n * default locale while its URL prefix is hidden. Astro's i18n routing 404s any\n * page URL carrying that locale's code as a segment (it expects the default\n * locale to be unprefixed), so its trees are keyed `default` instead, the same\n * way its page routes drop the prefix. `null` when every locale is prefixed\n * or the site is single-locale.\n */\nexport const hiddenDefaultLocale = (\n i18n: { defaultLocale: string; hideDefaultLocalePrefix: boolean } | null\n): string | null => (i18n?.hideDefaultLocalePrefix ? i18n.defaultLocale : null);\n\n/**\n * Every navigation tree the runtime data holds — the default, each locale's,\n * and each archived version's per locale — keyed the way the deferred\n * sidebar fragments' URLs are (`/blume-nav/<version>/<locale>/…`). An\n * unlocalized version tree is keyed by `\"\"` in the data; it maps to\n * `default` here, as does the hidden-prefix default locale's (see\n * `hiddenDefaultLocale`), whose current tree is `data.navigation` already.\n */\nexport const navVariants = (\n data: {\n navigation: Navigation;\n navigationByLocale: Record<string, Navigation>;\n navigationByVersion: Record<string, Record<string, Navigation>>;\n },\n hiddenDefault: string | null = null\n): NavVariant[] => [\n { locale: \"default\", navigation: data.navigation, version: \"current\" },\n ...Object.entries(data.navigationByLocale)\n .filter(([locale]) => locale !== hiddenDefault)\n .map(([locale, navigation]) => ({\n locale,\n navigation,\n version: \"current\",\n })),\n ...Object.entries(data.navigationByVersion).flatMap(([version, byLocale]) =>\n Object.entries(byLocale).map(([locale, navigation]) => ({\n locale: locale && locale !== hiddenDefault ? locale : \"default\",\n navigation,\n version,\n }))\n ),\n];\n",
|
|
15
|
+
"/**\n * The MDX component tags Blume ships (the keys of the catch-all's component map,\n * plus composed sub-parts like `Color.Item`/`Tree.File`). Used by the\n * missing-component diagnostic to tell an unknown `<Tag>` from a built-in. Keep\n * in sync with the component map in `astro/templates.ts`.\n */\nexport const BUILTIN_MDX_TAGS = new Set<string>([\n \"Accordion\",\n \"AccordionItem\",\n \"ApiOverview\",\n \"ApiTagOperations\",\n \"AutoTypeTable\",\n \"Badge\",\n \"Callout\",\n \"Card\",\n \"CardGroup\",\n \"CodeBlock\",\n \"CodeGroup\",\n \"Color\",\n \"Column\",\n \"Columns\",\n \"Component\",\n \"Diff\",\n \"Expandable\",\n \"FileTree\",\n \"Frame\",\n \"GithubInfo\",\n \"Icon\",\n // Conditionally imported by the catch-all, but `detectUsesMath` matches any\n // authored `<Math`, so it is always wired wherever it can appear.\n \"Math\",\n \"Operation\",\n \"Panel\",\n \"Prompt\",\n \"Step\",\n \"Steps\",\n \"Tab\",\n \"Tabs\",\n \"Tile\",\n \"Tooltip\",\n \"Tree\",\n \"TypeTable\",\n \"Visibility\",\n \"YouTube\",\n]);\n",
|
|
16
|
+
"import { BUILTIN_MDX_TAGS } from \"./builtin-tags.ts\";\nimport type { Diagnostic, PageRecord } from \"./types.ts\";\n\n/** `CardGroup` → `card-group`, matching registry item names. */\nconst toKebab = (tag: string): string =>\n tag\n .replaceAll(/(?<lower>[a-z0-9])(?<upper>[A-Z])/gu, \"$<lower>-$<upper>\")\n .toLowerCase();\n\n/**\n * Warn when an `.mdx` page uses a `<Component>` tag that resolves to nothing —\n * a built-in, an island, or a `components.ts` override — so a typo surfaces as a\n * friendly diagnostic (with a `blume add` hint where one exists) instead of a raw\n * MDX \"X is not defined\" build error. `extraTags` are the project's own known\n * components (islands + overrides); `registryNames` gates the install hint.\n */\nexport const validateUsedComponents = (\n pages: PageRecord[],\n extraTags: Set<string>,\n registryNames: Set<string>\n): Diagnostic[] => {\n const diagnostics: Diagnostic[] = [];\n const seen = new Set<string>();\n for (const page of pages) {\n for (const tag of page.componentsUsed ?? []) {\n if (BUILTIN_MDX_TAGS.has(tag) || extraTags.has(tag) || seen.has(tag)) {\n continue;\n }\n seen.add(tag);\n const name = toKebab(tag);\n const suggestion = registryNames.has(name)\n ? `Run \\`blume add ${name}\\` to install it, or register <${tag}> in components.ts (mdx).`\n : `Register <${tag}> in components.ts (mdx), or add an islands/${tag}.tsx component.`;\n diagnostics.push({\n code: \"BLUME_UNKNOWN_COMPONENT\",\n file: page.sourcePath ?? page.id,\n message: `<${tag}> is used in ${page.route} but isn't a known component.`,\n severity: \"warning\",\n suggestion,\n });\n }\n }\n return diagnostics;\n};\n",
|
|
17
|
+
"/**\n * Pixel dimensions declared by an SVG document's root element.\n *\n * Blume needs an SVG's size in two places — the header logo's reserved\n * `<img>` box and the OG card's brand mark — and both read it through this\n * one parser so the header and the card can't disagree about one logo. It\n * inspects the root `<svg …>` tag only: explicit `width`/`height` win, a\n * `viewBox` fills in whatever is missing (scaled to a lone explicit length\n * when there is one), and lengths carry CSS units at 96dpi. Percentages have\n * no pixel meaning and are ignored, so a percent-sized SVG measures by its\n * viewBox alone.\n */\nexport interface SvgDimensions {\n height: number;\n width: number;\n}\n\n/** The root tag, tolerating `>` inside a quoted attribute value. */\nconst ROOT = /<svg\\s(?:[^>\"']|\"[^\"]*\"|'[^']*')*>/u;\nconst WIDTH = /\\swidth=(?<quote>['\"])(?<value>[^%]+?)\\k<quote>/u;\nconst HEIGHT = /\\sheight=(?<quote>['\"])(?<value>[^%]+?)\\k<quote>/u;\nconst VIEWBOX = /\\sviewBox=(?<quote>['\"])(?<value>.+?)\\k<quote>/iu;\n\n/** Pixels per CSS unit at 96dpi; an omitted unit means pixels. */\nconst INCH_CM = 2.54;\nconst UNITS = new Map([\n [\"cm\", 96 / INCH_CM],\n [\"em\", 16],\n [\"ex\", 8],\n [\"in\", 96],\n [\"m\", (96 / INCH_CM) * 100],\n [\"mm\", 96 / INCH_CM / 10],\n [\"pc\", (96 / 72) * 12],\n [\"pt\", 96 / 72],\n [\"px\", 1],\n]);\nconst LENGTH =\n /^(?<number>[0-9.]+(?:e\\d+)?)(?<unit>in|cm|em|ex|m|mm|pc|pt|px)?$/u;\n\n/** A CSS length as whole pixels, or undefined when it isn't one. */\nconst parseLength = (value: string | undefined): number | undefined => {\n const groups = value === undefined ? undefined : LENGTH.exec(value)?.groups;\n if (!groups) {\n return;\n }\n const pixels = Math.round(\n Number(groups.number) * (UNITS.get(groups.unit ?? \"px\") ?? 1)\n );\n return pixels > 0 ? pixels : undefined;\n};\n\n/** An attribute's raw value on the root tag, or undefined when absent. */\nconst attribute = (root: string, pattern: RegExp): string | undefined =>\n pattern.exec(root)?.groups?.value;\n\n/** The viewBox's own width and height (its third and fourth numbers). */\nconst viewBoxSize = (root: string): SvgDimensions | null => {\n const raw = attribute(root, VIEWBOX);\n if (raw === undefined) {\n return null;\n }\n const bounds = raw.split(\" \");\n const width = parseLength(bounds[2]);\n const height = parseLength(bounds[3]);\n return width && height ? { height, width } : null;\n};\n\n/**\n * The SVG's pixel size, or null when the root carries no usable size: no\n * root tag, neither explicit dimensions nor a viewBox, a viewBox that isn't\n * space-separated, or a zero or unparsable length.\n */\nexport const svgDimensions = (svg: string): SvgDimensions | null => {\n const root = ROOT.exec(svg)?.[0];\n if (root === undefined) {\n return null;\n }\n const width = parseLength(attribute(root, WIDTH));\n const height = parseLength(attribute(root, HEIGHT));\n if (width && height) {\n return { height, width };\n }\n const box = viewBoxSize(root);\n if (!box) {\n return null;\n }\n const ratio = box.width / box.height;\n if (width) {\n const scaled = Math.floor(width / ratio);\n return scaled > 0 ? { height: scaled, width } : null;\n }\n if (height) {\n const scaled = Math.floor(height * ratio);\n return scaled > 0 ? { height, width: scaled } : null;\n }\n return box;\n};\n",
|
|
18
|
+
"import { existsSync } from \"node:fs\";\n\nimport { dirname, join, resolve } from \"pathe\";\nimport ts from \"typescript\";\n\n/**\n * Read the project's TypeScript path aliases (`compilerOptions.paths`) and turn\n * them into Vite `resolve.alias` entries, so `@/`-style imports in custom pages,\n * islands, and components resolve in the generated Astro build exactly as they\n * do in the user's own tooling.\n *\n * The generated `.blume/` runtime is its own Astro project with its own tsconfig\n * and never inherits the project's, so without this every shadcn-style `@/…`\n * import would have to be rewritten to a relative path. Reading the aliases here\n * lets those components port over unchanged.\n *\n * Parsing is the TypeScript compiler's own job — JSONC, the full `extends`\n * chain (relative paths, package specifiers, TS 5.0 arrays), `${configDir}`\n * substitution, and where relative `paths` anchor all follow tsc exactly\n * because tsc does the work. Best-effort and non-fatal: anything unreadable\n * yields no aliases.\n */\n\n/**\n * tsc records the directory of the config that declared `paths` on the\n * resolved options (that is what relative targets anchor to when there is no\n * `baseUrl`), but leaves the field out of its public typings.\n */\ninterface PathsBaseOptions {\n pathsBasePath?: string;\n}\n\n/**\n * Parse-only host: `include` globbing would walk the whole project for a file\n * list nobody reads, so directories always enumerate as empty.\n */\nconst PARSE_HOST: ts.ParseConfigHost = {\n fileExists: ts.sys.fileExists,\n readDirectory: () => [],\n readFile: ts.sys.readFile,\n useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,\n};\n\n/** Whether a `paths` fallback entry is a usable target (raw JSONC may lie). */\nconst isPathTarget = (target: string | undefined): target is string =>\n typeof target === \"string\";\n\n/** Convert one tsconfig `paths` mapping to a Vite alias, or null to skip. */\nconst toAlias = (\n key: string,\n value: string[],\n baseDir: string\n): { find: string; replacement: string } | null => {\n // tsconfig allows a fallback array; Vite aliases are 1:1, so take the first.\n const [first] = value;\n if (!isPathTarget(first)) {\n return null;\n }\n const find = key.endsWith(\"/*\") ? key.slice(0, -2) : key;\n const target = first.endsWith(\"/*\") ? first.slice(0, -2) : first;\n // A bare `*`/`/*` catch-all would alias every import — never wire that.\n if (find === \"\" || find === \"*\") {\n return null;\n }\n return { find, replacement: resolve(baseDir, target) };\n};\n\n/**\n * Resolve the project's tsconfig/jsconfig path aliases to absolute Vite\n * `resolve.alias` entries (`find` → absolute replacement). Returns `{}` when no\n * config or no usable `paths` is found.\n */\nexport const resolveTsconfigAliases = (\n root: string\n): Record<string, string> => {\n const entry = [\"tsconfig.json\", \"jsconfig.json\"]\n .map((name) => join(root, name))\n .find((file) => existsSync(file));\n if (!entry) {\n return {};\n }\n const { config, error } = ts.readConfigFile(entry, ts.sys.readFile);\n if (error) {\n // Unreadable or malformed JSONC: no aliases, as before.\n return {};\n }\n const configDir = dirname(entry);\n // Diagnostics (an unresolvable `extends`, no input files) are tsc's to\n // report; like tsc, keep whatever options still resolved.\n const { options } = ts.parseJsonConfigFileContent(\n config,\n PARSE_HOST,\n configDir,\n undefined,\n entry\n );\n const { paths } = options;\n if (!paths) {\n return {};\n }\n // SAFETY: the assertion only exposes `pathsBasePath`, which tsc populates on\n // the resolved options but omits from `CompilerOptions`.\n const { pathsBasePath } = options as PathsBaseOptions;\n // tsc already made `baseUrl` absolute and expanded `${configDir}`.\n const baseDir = options.baseUrl ?? pathsBasePath ?? configDir;\n const entries: [string, string][] = [];\n for (const [key, value] of Object.entries(paths)) {\n const alias = toAlias(key, value, baseDir);\n if (alias) {\n entries.push([alias.find, alias.replacement]);\n }\n }\n return Object.fromEntries(entries);\n};\n",
|
|
19
|
+
"/**\n * A Shiki transformer that prepends a brand icon to a code block's header,\n * keyed off the fence language. Icons are sourced from `simple-icons` (raw SVG\n * path data) at build time and emitted as an inline `<svg>` — no client JS and\n * no React, so it works in the core theme. The icon renders in `currentColor`\n * (the muted header color) so it stays legible in both light and dark; brand\n * hex colors are skipped because dark-on-dark logos (Next.js, Rust…) vanish.\n *\n * The theme styles `.blume-lang-icon` and shifts the language label\n * (`pre[data-language][data-icon]::before`) to make room — gated on\n * `data-language` so the icon only shows when a header bar exists to hold it.\n */\n\nimport {\n siAstro,\n siC,\n siCplusplus,\n siCss,\n siDart,\n siDocker,\n siGnubash,\n siGo,\n siGraphql,\n siHtml5,\n siJavascript,\n siJson,\n siKotlin,\n siLess,\n siLua,\n siMarkdown,\n siMdx,\n siMysql,\n siNextdotjs,\n siPhp,\n siPrisma,\n siPython,\n siReact,\n siRuby,\n siRust,\n siSass,\n siScala,\n siSvelte,\n siSvg,\n siSwift,\n siToml,\n siTypescript,\n siVuedotjs,\n siWebassembly,\n siYaml,\n} from \"simple-icons\";\n\n/** The slice of a `simple-icons` icon Blume reads: its slug and path data. */\ninterface SimpleIcon {\n path: string;\n slug: string;\n}\n\n/** Fence language (and common aliases) → icon. Unmapped languages get none. */\ninterface LanguageIcons {\n [language: string]: SimpleIcon;\n}\n\nconst LANGUAGE_ICONS: LanguageIcons = {\n astro: siAstro,\n bash: siGnubash,\n c: siC,\n \"c++\": siCplusplus,\n cjs: siJavascript,\n cpp: siCplusplus,\n css: siCss,\n cts: siTypescript,\n dart: siDart,\n docker: siDocker,\n dockerfile: siDocker,\n go: siGo,\n gql: siGraphql,\n graphql: siGraphql,\n html: siHtml5,\n javascript: siJavascript,\n js: siJavascript,\n json: siJson,\n json5: siJson,\n jsonc: siJson,\n jsx: siReact,\n kotlin: siKotlin,\n kt: siKotlin,\n less: siLess,\n lua: siLua,\n markdown: siMarkdown,\n md: siMarkdown,\n mdx: siMdx,\n mjs: siJavascript,\n mts: siTypescript,\n nextjs: siNextdotjs,\n php: siPhp,\n prisma: siPrisma,\n py: siPython,\n python: siPython,\n rb: siRuby,\n react: siReact,\n rs: siRust,\n ruby: siRuby,\n rust: siRust,\n sass: siSass,\n scala: siScala,\n scss: siSass,\n sh: siGnubash,\n shell: siGnubash,\n sql: siMysql,\n svelte: siSvelte,\n svg: siSvg,\n swift: siSwift,\n toml: siToml,\n ts: siTypescript,\n tsx: siReact,\n typescript: siTypescript,\n vue: siVuedotjs,\n wasm: siWebassembly,\n yaml: siYaml,\n yml: siYaml,\n zsh: siGnubash,\n};\n\n/** A minimal hast node (avoids a hast type dependency). */\ninterface HastNode {\n children?: HastNode[];\n properties?: Record<string, boolean | number | string | string[] | undefined>;\n tagName?: string;\n type: string;\n value?: string;\n}\n\n/** The slice of Shiki's transformer `this` context the icon hook reads. */\ninterface IconContext {\n options: { lang?: string };\n}\n\n/** The `<pre>` hast node a Shiki `pre` hook receives. */\ninterface IconPreNode {\n children: HastNode[];\n properties: Record<string, boolean | number | string | undefined>;\n}\n\n/** A Shiki-compatible transformer, typed structurally to avoid a Shiki dep. */\nexport interface LanguageIconTransformer {\n name: string;\n pre: (this: IconContext, node: IconPreNode) => void;\n}\n\n/** Build the transformer. Runs after Shiki's built-in `data-language` hook. */\nexport const languageIconTransformer = (): LanguageIconTransformer => ({\n name: \"blume:language-icon\",\n pre(node) {\n const icon = LANGUAGE_ICONS[(this.options.lang ?? \"\").toLowerCase()];\n if (!icon) {\n return;\n }\n // The icon itself is CSS: the theme paints `pre[data-icon=\"<slug>\"]::after`\n // with the brand path as a mask (see `languageIconCss`), so a block\n // carries a short attribute instead of ~1 kB of SVG — on a reference page\n // with twenty TypeScript blocks, the difference is most of the page.\n node.properties.dataIcon = icon.slug;\n },\n});\n\n/** The icon slug for a fence language, or null for an unmapped language. */\nexport const languageIconSlug = (language: string): string | null =>\n LANGUAGE_ICONS[language.toLowerCase()]?.slug ?? null;\n\n// Fence openers (```ts, ~~~tsx) and the `lang`/`language` props of code\n// components (<CodeBlock lang=\"ts\">), which highlight through the same\n// transformer. Word characters plus the few punctuation marks languages use.\nconst FENCE_LANGUAGE = /^[ \\t]*(?:`{3,}|~{3,})[ \\t]*(?<lang>[\\w+#.-]+)/gmu;\nconst PROP_LANGUAGE = /\\blang(?:uage)?=[\"'](?<lang>[\\w+#.-]+)[\"']/gu;\n\n/**\n * The icon slugs a site's Markdown uses, sorted and deduped, so the theme\n * carries a mask rule for each of them and none for the other thirty.\n */\nexport const languageIconSlugsIn = (markdown: string): string[] => {\n const slugs = new Set<string>();\n for (const pattern of [FENCE_LANGUAGE, PROP_LANGUAGE]) {\n for (const match of markdown.matchAll(pattern)) {\n const slug = languageIconSlug(match.groups?.lang ?? \"\");\n if (slug) {\n slugs.add(slug);\n }\n }\n }\n return [...slugs].toSorted();\n};\n\nconst iconBySlug = (slug: string): SimpleIcon | undefined =>\n Object.values(LANGUAGE_ICONS).find((icon) => icon.slug === slug);\n\n/** A simple-icons path as a `mask-image` data URI (24×24 viewBox). */\nconst maskUri = (path: string): string =>\n `url(\"data:image/svg+xml,${encodeURIComponent(`<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"${path}\"/></svg>`)}\")`;\n\n/**\n * The per-language rules that paint a code block's icon: each gives the\n * block's `::after` (positioned by the theme) the brand path as a mask over\n * the muted foreground. Only the listed slugs get a rule, so an unmapped or\n * unused language paints nothing rather than a blank square.\n */\nexport const languageIconCss = (slugs: string[]): string =>\n slugs\n .map((slug) => {\n const icon = iconBySlug(slug);\n if (!icon) {\n return \"\";\n }\n const mask = maskUri(icon.path);\n return `.prose > :where(pre[data-language][data-icon=\"${slug}\"])::after {\n background-color: var(--blume-muted-foreground);\n -webkit-mask-image: ${mask};\n mask-image: ${mask};\n}`;\n })\n .filter((rule) => rule !== \"\")\n .join(\"\\n\");\n",
|
|
20
|
+
"import { jsxAttribute, jsxFlowElement } from \"./mdast.ts\";\nimport type { MdastNode, MdastVisitorContext } from \"./mdast.ts\";\n\ninterface CodeNode extends MdastNode {\n lang?: string | null;\n value: string;\n}\n\n/**\n * Satteri MDAST plugin that turns a ` ```mermaid ` code block into a\n * `<blume-mermaid>` custom element carrying the raw diagram source. There is no\n * importable component — the fence is the whole interface. The element is\n * rendered on the client (Mermaid needs a DOM), so the source rides on a string\n * attribute rather than as child text (which MDX would try to parse).\n */\n/**\n * A ```mermaid (or ~~~mermaid) fence opener at the start of a line. Used to\n * decide, at generation time, whether the site needs the Mermaid client\n * library at all — see `featuresTemplate`.\n */\nconst MERMAID_FENCE = /^[ \\t]*(?:`{3,}|~{3,})[ \\t]*mermaid\\b/mu;\n\n/** Whether a page's Markdown/MDX source contains a mermaid fence. */\nexport const hasMermaidFence = (text: string): boolean =>\n MERMAID_FENCE.test(text);\n\nexport const mermaidPlugin = () => ({\n code(node: CodeNode, ctx: MdastVisitorContext) {\n if (node.lang !== \"mermaid\") {\n return;\n }\n ctx.replaceNode(\n node,\n jsxFlowElement(\n \"blume-mermaid\",\n [\n jsxAttribute(\n \"class\",\n // Mermaid's SVG has width:100% + a viewBox but no intrinsic width,\n // so a shrink-wrapped flex item collapses to the 300px replaced-\n // element fallback and the viewBox scales the drawing down into it.\n // The child must fill the column (w-full); the svg centers itself\n // via auto margins, and diagrams that opt out of useMaxWidth keep\n // a fixed pixel width and scroll via overflow-x-auto.\n \"not-prose my-6 flex overflow-x-auto [&>div]:w-full [&>div>svg]:mx-auto [&>div>svg]:block\"\n ),\n jsxAttribute(\"data-source\", node.value),\n ],\n []\n )\n );\n },\n name: \"blume-mermaid\",\n});\n",
|
|
21
|
+
"/**\n * Bridges `theme.fonts` into the OG card renderer. A site that explicitly\n * picks its typefaces gets matching cards (and non-Latin coverage) without\n * configuring `seo.og.fonts`; untouched defaults derive nothing, so plain\n * sites keep Takumi's built-in font and gain no build-time font fetch.\n */\n\nimport { existsSync } from \"node:fs\";\n\nimport { isAbsolute, join } from \"pathe\";\n\nimport type {\n FontsConfig,\n FontValue,\n LocalFontConfig,\n} from \"../theme/fonts.ts\";\nimport { GOOGLE_FONTS, isFontSlug } from \"../theme/fonts.ts\";\nimport type { OgFont, OgFontFamilies, OgLocalFont } from \"./card.ts\";\n\n/** Fonts plus per-role families for the generated OG endpoint. */\nexport interface DerivedOgFonts {\n families?: OgFontFamilies;\n fonts: OgFont[];\n}\n\n/** The weights the card actually renders at (title 600, everything else 400). */\nconst CARD_WEIGHTS = [400, 600];\n\n/** Resolve a config path against the project root. */\nconst absoluteSrc = (root: string, src: string): string =>\n isAbsolute(src) ? src : join(root, src);\n\n/** A concrete numeric face weight (as opposed to a variable-range string). */\nconst isNumericWeight = (\n weight: number | string | undefined\n): weight is number => typeof weight === \"number\";\n\n/** A variable-range weight spec string, e.g. `\"100..900\"`. */\nconst isRangeWeight = (weight: number | string | undefined): weight is string =>\n typeof weight === \"string\";\n\n/** A theme role configured as a font slug / family-name string. */\nconst isFontName = (value: FontValue): value is string =>\n typeof value === \"string\";\n\n/** An OG font entry that reads a local file (as opposed to a Google family). */\nconst isLocalOgFont = (font: OgFont): font is OgLocalFont =>\n typeof font !== \"string\" && \"src\" in font;\n\n/**\n * The weight spec to fetch for a derived Google family: the declared weights\n * the card uses, the declared numeric weights otherwise, a lone variable\n * range as-is, or nothing (family default) as the last resort.\n */\nconst googleWeights = (\n weights: (number | string)[]\n): number[] | string | undefined => {\n const numbers = weights.filter(isNumericWeight);\n const used = numbers.filter((weight) => CARD_WEIGHTS.includes(weight));\n if (used.length > 0) {\n return used;\n }\n if (numbers.length > 0) {\n return numbers;\n }\n const [first] = weights;\n return weights.length === 1 && isRangeWeight(first) ? first : undefined;\n};\n\nconst googleOgFont = (name: string, weights: (number | string)[]): OgFont => {\n const weight = googleWeights(weights);\n return weight === undefined ? { name } : { name, weight };\n};\n\n/** Per-variant local entries for the renderer (paths made absolute). */\nconst localOgFonts = (font: LocalFontConfig, root: string): OgLocalFont[] =>\n font.variants.map((variant) => {\n const entry: OgLocalFont = {\n name: font.name,\n src: absoluteSrc(root, variant.src),\n };\n const withWeight: OgLocalFont = isNumericWeight(variant.weight)\n ? { ...entry, weight: variant.weight }\n : entry;\n // Takumi's per-face style is normal/italic; oblique falls back to the file.\n return variant.style === \"normal\" || variant.style === \"italic\"\n ? { ...withWeight, style: variant.style }\n : withWeight;\n });\n\n/**\n * The card fonts for one theme role, or null when the role can't flow into\n * the renderer (an unknown slug string, or a provider Takumi can't fetch —\n * `googleFonts` only speaks Google's css2 endpoint).\n */\nconst roleFonts = (value: FontValue, root: string): OgFont[] | null => {\n if (isFontName(value)) {\n if (!isFontSlug(value)) {\n return null;\n }\n const def = GOOGLE_FONTS[value];\n return [googleOgFont(def.family, def.weights)];\n }\n if (\"variants\" in value) {\n return localOgFonts(value, root);\n }\n if ((value.provider ?? \"google\") !== \"google\") {\n return null;\n }\n return [googleOgFont(value.name, value.weights ?? CARD_WEIGHTS)];\n};\n\n/** The family name a theme role registers under. */\nconst roleFamily = (value: FontValue): string | null => {\n if (isFontName(value)) {\n return isFontSlug(value) ? GOOGLE_FONTS[value].family : null;\n }\n return value.name;\n};\n\n/**\n * Derive the OG card fonts from the theme's display and body roles (the two\n * the card renders), deduped, plus the per-role family names so the title\n * keeps the display face and the body text the body face.\n */\nexport const deriveOgFonts = (\n fonts: FontsConfig,\n root: string\n): DerivedOgFonts => {\n const derived: OgFont[] = [];\n const seen = new Set<string>();\n const families: OgFontFamilies = {};\n\n const roles = [\n [\"title\", fonts?.display],\n [\"body\", fonts?.body],\n ] as const;\n for (const [role, value] of roles) {\n if (value === undefined) {\n continue;\n }\n const roleEntries = roleFonts(value, root);\n if (!roleEntries) {\n continue;\n }\n families[role] = roleFamily(value) ?? undefined;\n for (const entry of roleEntries) {\n const key = JSON.stringify(entry);\n if (!seen.has(key)) {\n seen.add(key);\n derived.push(entry);\n }\n }\n }\n\n const result: DerivedOgFonts = { fonts: derived };\n if (families.title || families.body) {\n result.families = families;\n }\n return result;\n};\n\n/** Explicit `seo.og.fonts` with local `src` paths resolved to absolute. */\nexport const resolveOgFontSources = (fonts: OgFont[], root: string): OgFont[] =>\n fonts.map((font) =>\n isLocalOgFont(font) ? { ...font, src: absoluteSrc(root, font.src) } : font\n );\n\n/**\n * The fonts baked into the generated OG endpoint. An explicit `seo.og.fonts`\n * always wins (including `[]` to opt out, keeping the card's role styling\n * untouched); otherwise a site that explicitly set `theme.fonts` gets its\n * display/body fonts derived so cards match the site without extra config.\n */\nexport const resolveOgFonts = (\n options: {\n /** Explicit `seo.og.fonts`, or undefined when unset. */\n ogFonts: OgFont[] | undefined;\n themeFonts: FontsConfig;\n /** Whether the config file set `theme.fonts` itself (gates derivation). */\n themeFontsConfigured: boolean;\n },\n root: string\n): DerivedOgFonts => {\n if (options.ogFonts) {\n return { fonts: resolveOgFontSources(options.ogFonts, root) };\n }\n return options.themeFontsConfigured\n ? deriveOgFonts(options.themeFonts, root)\n : { fonts: [] };\n};\n\n/**\n * Every configured local font file (theme roles and `seo.og.fonts`) that is\n * missing on disk, as absolute paths. Generation fails on these up front — the\n * alternative is Astro or the OG renderer crashing later with a bare ENOENT.\n */\nexport const missingFontFiles = (\n options: { ogFonts: OgFont[]; themeFonts: FontsConfig },\n root: string\n): string[] => {\n const sources: string[] = [];\n for (const value of Object.values(options.themeFonts ?? {})) {\n if (!isFontName(value) && \"variants\" in value) {\n sources.push(\n ...value.variants.map((variant) => absoluteSrc(root, variant.src))\n );\n }\n }\n for (const font of options.ogFonts) {\n if (isLocalOgFont(font)) {\n sources.push(absoluteSrc(root, font.src));\n }\n }\n return [...new Set(sources)].filter((path) => !existsSync(path));\n};\n",
|
|
22
|
+
"import { existsSync, readFileSync } from \"node:fs\";\n\nimport { join } from \"pathe\";\n\nimport type { BlumeProject } from \"../core/project-graph.ts\";\n\n/** Resolve a configured local SVG for use in generated Open Graph cards. */\nexport const resolveOgLogo = (\n project: BlumeProject,\n source: string | undefined\n): string | undefined => {\n if (!source?.toLowerCase().endsWith(\".svg\")) {\n return;\n }\n const relative = source.replace(/^\\//u, \"\");\n const file = [\n join(project.context.root, \"public\", relative),\n join(project.context.root, relative),\n ].find((path) => existsSync(path));\n return file ? readFileSync(file, \"utf-8\") : undefined;\n};\n",
|
|
23
|
+
"import { readFile } from \"node:fs/promises\";\n\nimport { isAbsolute, join } from \"pathe\";\n\nimport { scalarReferenceTemplate } from \"../astro/templates.ts\";\nimport type { ResolvedConfig } from \"../core/schema.ts\";\nimport { trimChar } from \"../core/trim.ts\";\nimport { resolveAccent, resolveRadius } from \"../theme/palette.ts\";\nimport { resolveReferences } from \"./references.ts\";\nimport type { ReferenceSource } from \"./references.ts\";\n\n/**\n * The `scalar()` adapter's pages. Each source becomes one self-contained\n * `@scalar/astro` page loaded client-side from Scalar's CDN. Blume's own\n * renderer (`openapi()`, `asyncapi()`, `graphql()`) lives in `source.ts` /\n * the `components/openapi` set and does not pass through here.\n */\n\n/** A generated Scalar reference page, ready to write under `src/pages`. */\nexport interface ReferenceFile {\n /** Path relative to `src/pages`, e.g. `reference.astro`, `api/events.astro`. */\n pagePath: string;\n content: string;\n}\n\nconst URL_SPEC = /^https?:\\/\\//u;\n\n/** The `src/pages`-relative file path for a reference route. */\nconst referencePagePath = (route: string): string => {\n const segments = trimChar(route, \"/\");\n return `${segments === \"\" ? \"index\" : segments}.astro`;\n};\n\n/**\n * Map Blume's theme onto Scalar's. An explicit `theme` name wins; otherwise we\n * keep Scalar's default theme and layer Blume's accent/radius on top via\n * `customCss`. Scalar re-injects `customCss` after its bundled theme, so these\n * variables reliably override the defaults. Best-effort, not pixel-exact.\n *\n * Light/dark is not decided here: the page's `data-theme` (stored preference,\n * else the configured mode, else the OS) is only known in the browser, so\n * `ReferenceLayout` pins Scalar's color mode to it at mount and keeps the two\n * in step afterwards (`SCALAR_THEME_INIT_SCRIPT`).\n */\nconst themeConfiguration = (config: ResolvedConfig, override?: string) => {\n if (override) {\n return { theme: override };\n }\n const accent = resolveAccent(config.theme);\n const radius = resolveRadius(config.theme);\n return {\n customCss: `:root,.light-mode,.dark-mode{--scalar-color-accent:${accent.light};--scalar-radius:${radius};}.dark-mode{--scalar-color-accent:${accent.dark};}`,\n };\n};\n\n/** Build the Scalar spec config for a source: inline `content` or remote `url`. */\nconst specConfiguration = async (\n spec: string,\n root: string\n): Promise<{\n config: { content: string } | { url: string };\n warning?: string;\n}> => {\n if (URL_SPEC.test(spec)) {\n return { config: { url: spec } };\n }\n // A local spec is read at generate time and inlined as `content`, so the page\n // stays self-contained and nothing is copied into the user's source tree.\n const absolute = isAbsolute(spec) ? spec : join(root, spec);\n try {\n return { config: { content: await readFile(absolute, \"utf-8\") } };\n } catch {\n return {\n config: { url: spec },\n warning: `API reference spec not found: \"${spec}\" (looked in ${absolute}).`,\n };\n }\n};\n\n/**\n * Accept one resolved reference into the deduped Scalar set, or return null to\n * skip it (recording a warning for a route collision). Mutates `seen`.\n */\nconst acceptScalarReference = (\n ref: ReferenceSource,\n seen: Set<string>,\n contentRoutes: ReadonlySet<string>,\n warnings: string[]\n): ReferenceSource | null => {\n if (ref.kind !== \"scalar\") {\n return null;\n }\n if (seen.has(ref.route)) {\n warnings.push(\n `Two API reference sources resolve to ${ref.route}; keeping the first.`\n );\n return null;\n }\n if (contentRoutes.has(ref.route)) {\n warnings.push(\n `API reference route ${ref.route} collides with a content page; skipping the reference there.`\n );\n return null;\n }\n seen.add(ref.route);\n return ref;\n};\n\n/**\n * Build the Scalar reference page(s) for the project. Only `scalar()`\n * references are emitted here (Blume-rendered kinds are staged content). Reads\n * local specs, maps the theme, and skips routes that collide with a content page\n * or another source. Returns the files to write under `src/pages` plus warnings.\n */\nexport const buildReferenceFiles = async (options: {\n config: ResolvedConfig;\n root: string;\n contentRoutes: ReadonlySet<string>;\n}): Promise<{ files: ReferenceFile[]; warnings: string[] }> => {\n const { config, root, contentRoutes } = options;\n const warnings: string[] = [];\n\n // Resolve and dedupe routes first (sync), then read every spec in parallel.\n const seen = new Set<string>();\n const accepted: ReferenceSource[] = [];\n for (const ref of resolveReferences(config)) {\n const next = acceptScalarReference(ref, seen, contentRoutes, warnings);\n if (next) {\n accepted.push(next);\n }\n }\n\n const built = await Promise.all(\n accepted.map(async (ref) => ({\n ref,\n spec: await specConfiguration(ref.spec, root),\n }))\n );\n\n const files: ReferenceFile[] = [];\n for (const { ref, spec } of built) {\n if (spec.warning) {\n warnings.push(spec.warning);\n }\n const pagePath = referencePagePath(ref.route);\n // `theme` is the one option Blume maps (a named theme replaces the\n // accent/radius layering); every other `scalar()` option is forwarded\n // verbatim and wins outright — a full escape hatch over Blume's derived\n // spec/theme config (localization, agent, hideTestRequestButton,\n // orderSchemaPropertiesBy, customCss, and the rest).\n const { theme, ...passthrough } = ref.scalar ?? {};\n files.push({\n content: scalarReferenceTemplate({\n configuration: {\n ...spec.config,\n ...themeConfiguration(config, theme),\n ...passthrough,\n },\n noindex: ref.noindex,\n route: ref.route,\n title: ref.label,\n }),\n pagePath,\n });\n }\n\n return { files, warnings };\n};\n",
|
|
24
|
+
"import type { ResolvedConfig } from \"../core/schema.ts\";\n\nconst FALLBACK_ACCENT = \"oklch(0.62 0.16 250)\";\n\n/**\n * Named accent presets mapped to OKLCH values. The single source of truth for\n * preset colors: the theme CSS and the OG card (og/card.ts) both resolve from\n * this table, so a site and its social cards can't disagree about \"blue\".\n */\nexport const ACCENTS = {\n blue: FALLBACK_ACCENT,\n green: \"oklch(0.6 0.16 150)\",\n orange: \"oklch(0.68 0.17 50)\",\n pink: \"oklch(0.65 0.2 350)\",\n purple: \"oklch(0.58 0.2 290)\",\n red: \"oklch(0.58 0.22 25)\",\n teal: \"oklch(0.6 0.12 195)\",\n} satisfies Record<string, string>;\n\n/**\n * Whether a raw config value names an accent preset. `hasOwn` keeps a value\n * like \"constructor\" from resolving an Object.prototype member — which would\n * stringify a function into the generated CSS, breaking the rule (the exact\n * breakout {@link safeColor} exists to prevent).\n */\nexport const isAccentPreset = (value: string): value is keyof typeof ACCENTS =>\n Object.hasOwn(ACCENTS, value);\n\n// Characters valid in a CSS color value (hex, rgb/hsl/oklch functions, named\n// colors). Anything else — notably `;`, `{`, `}` — could break out of the\n// declaration and inject rules, so such a value is rejected.\nconst CSS_COLOR = /^[\\w\\s#%.,()/+-]+$/u;\n\n/** Pass a raw color through only if it can't break out of a CSS declaration. */\nconst safeColor = (value: string, fallback: string): string =>\n CSS_COLOR.test(value.trim()) ? value.trim() : fallback;\n\n/** Resolve a named preset or fall back to {@link safeColor}. */\nconst presetOrColor = (value: string): string =>\n isAccentPreset(value) ? ACCENTS[value] : safeColor(value, FALLBACK_ACCENT);\n\n/** Like {@link safeColor} but drops an unsafe/absent value to `null`. */\nconst safeColorOrNull = (value: string | undefined): string | null =>\n value && CSS_COLOR.test(value.trim()) ? value.trim() : null;\n\nconst RADII = {\n lg: \"0.75rem\",\n md: \"0.5rem\",\n none: \"0\",\n sm: \"0.25rem\",\n} satisfies Record<ResolvedConfig[\"theme\"][\"radius\"], string>;\n\nconst cssString = (value: string): string => JSON.stringify(value);\n\nconst backgroundImageCss = (image: string): string =>\n `url(${cssString(image)})`;\n\nconst cssToken = (name: string, value?: string | null): string[] =>\n value ? [` ${name}: ${value};`] : [];\n\nconst themeRootCss = (\n theme: ResolvedConfig[\"theme\"],\n options: {\n accent: string;\n action: string | null;\n radius: string;\n }\n): string =>\n [\n ` --blume-accent: ${options.accent};`,\n ...cssToken(\"--blume-action\", options.action),\n ...cssToken(\n \"--blume-action-foreground\",\n options.action ? \"oklch(1 0 0)\" : null\n ),\n ...cssToken(\"--blume-background\", safeColorOrNull(theme.background?.light)),\n ...cssToken(\n \"--blume-background-image\",\n theme.backgroundImage?.light\n ? backgroundImageCss(theme.backgroundImage.light)\n : null\n ),\n ` --blume-radius: ${options.radius};`,\n ]\n .filter(Boolean)\n .join(\"\\n\");\n\nconst themeDarkCss = (\n theme: ResolvedConfig[\"theme\"],\n options: {\n accent: string;\n action: string | null;\n }\n): string => {\n // Mode-shared tokens (accent, action) must be re-declared here: the base\n // stylesheet's own `:root[data-theme=\"dark\"]` block outranks the `:root`\n // config tokens on specificity, so without this block dark mode would\n // silently keep its neutral defaults and ignore the config.\n const tokens = [\n ` --blume-accent: ${options.accent};`,\n \" --blume-accent-foreground: oklch(1 0 0);\",\n ...cssToken(\"--blume-action\", options.action),\n ...cssToken(\n \"--blume-action-foreground\",\n options.action ? \"oklch(1 0 0)\" : null\n ),\n ...cssToken(\"--blume-background\", safeColorOrNull(theme.background?.dark)),\n ...cssToken(\n \"--blume-background-image\",\n theme.backgroundImage?.dark\n ? backgroundImageCss(theme.backgroundImage.dark)\n : null\n ),\n ].filter(Boolean);\n return `:root[data-theme=\"dark\"] {\n${tokens.join(\"\\n\")}\n}\n`;\n};\n\n/** Per-mode accent CSS colors. */\nexport interface AccentColors {\n dark: string;\n light: string;\n}\n\n/**\n * Resolve the configured accent to per-mode CSS colors. A named accent\n * resolves to its preset; any other value is treated as a raw CSS color so\n * users can pass arbitrary colors without a config change. A string accent\n * has already been normalized by the config schema to the same color for\n * both modes.\n */\nexport const resolveAccent = (\n theme: ResolvedConfig[\"theme\"]\n): AccentColors => ({\n dark: presetOrColor(theme.accent.dark),\n light: presetOrColor(theme.accent.light),\n});\n\n/** Resolve the configured radius preset to a CSS length. */\nexport const resolveRadius = (theme: ResolvedConfig[\"theme\"]): string =>\n RADII[theme.radius];\n\n/**\n * Compile theme config into CSS custom properties. A named accent resolves to\n * its preset; any other value is treated as a raw CSS color so users can pass\n * arbitrary colors without a config change.\n */\nexport const buildThemeCss = (theme: ResolvedConfig[\"theme\"]): string => {\n const accent = resolveAccent(theme);\n const action = theme.action ? presetOrColor(theme.action) : null;\n const radius = RADII[theme.radius];\n const root = themeRootCss(theme, {\n accent: accent.light,\n action,\n radius,\n });\n const dark = themeDarkCss(theme, {\n accent: accent.dark,\n action,\n });\n\n return `/* Generated by Blume from theme config. */\n:root {\n${root}\n}\n${dark}`;\n};\n",
|
|
25
|
+
"import { withBasePath } from \"../core/base-path.ts\";\n\n/** A resolved search empty-state link passed to the Search dialog. */\nexport interface SearchPopularPage {\n /** Built-in icon name; `Search.astro` resolves it to markup at render time. */\n icon?: string;\n label: string;\n route: string;\n}\n\n/**\n * Map configured `search.popular` entries to `{ route, label }` for Search.\n *\n * Curated hrefs are authored as if mounted at root, so `basePath` is applied\n * here — matching `navigation.featured` and tab paths, and agreeing with the\n * sidebar fallback these entries replace (whose routes are already based via\n * `page.route`). `withBasePath` is idempotent and skips external URLs.\n *\n * `deployment.base` is deliberately *not* applied: routes stay deploy-base-less\n * so `createLinkRow` can prefix it at click time, same as sidebar-derived pages.\n *\n * `icon` stays a name here (like `navigation.featured`): the icon set is a\n * server-only module, so `Search.astro` resolves it to inline SVG at render.\n */\nexport const resolveSearchPopular = (\n popular: { href: string; icon?: string; label: string }[],\n basePath: string\n): SearchPopularPage[] =>\n popular.map(({ href, icon, label }) => {\n const page: SearchPopularPage = {\n label,\n route: withBasePath(basePath, href),\n };\n if (icon) {\n page.icon = icon;\n }\n return page;\n });\n",
|
|
26
|
+
"/**\n * Vertical padding of a highlighted code block, shared by the theme (which\n * emits it as CSS) and `<Component>` (which estimates a source pane's SSR\n * height from it) so the two cannot drift.\n */\n\n/** Top and bottom inset of a plain prose block with no chrome, in rem. */\nexport const CODE_PADDING_BLOCK_REM = 1;\n",
|
|
27
|
+
"import { CODE_PADDING_BLOCK_REM } from \"./code-block-padding.ts\";\n\n/**\n * Bottom inset of the scrolling code element, in rem: keeps a horizontal\n * scrollbar thumb off the last line's descenders. It is carved out of the\n * pre's block padding rather than added to it, so a block's height is the\n * same whether or not it scrolls.\n */\nconst CODE_SCROLL_INSET_REM = 0.375;\n\n/** Lucide's arrow-up-right (the featured-link icon) as a `mask` data URI. */\nconst EXTERNAL_LINK_MASK = `url(\"data:image/svg+xml,${encodeURIComponent('<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#000\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"M7 7h10v10M7 17L17 7\"/></svg>')}\")`;\n\ninterface TailwindEntryOptions {\n /**\n * Globs to scan for utility classes. Typically the Blume package source and\n * the user's project (node_modules is skipped automatically).\n */\n sources: string[];\n /** Config-derived token overrides (`:root { --blume-accent: ... }`). */\n configTokens: string;\n /** Raw contents of the user's theme.css, if any. */\n userTheme: string;\n /** Twoslash rich-renderer styles (for fences with the `twoslash` meta). */\n twoslashCss?: string;\n /** The code-block language icon rules (`languageIconCss`), if any. */\n languageIcons?: string;\n}\n\n/** Dark mode is driven by `data-theme` on the root element (both sheets). */\nconst DARK_VARIANT = `/* Dark mode is driven by data-theme on the <html> element. */\n@custom-variant dark (&:where([data-theme=\"dark\"], [data-theme=\"dark\"] *));`;\n\n/** Keep browser-provided UI in sync with the active theme in both sheets. */\nconst COLOR_SCHEME_DEFAULTS = `:root {\n color-scheme: light;\n}\n\n:root[data-theme=\"dark\"] {\n color-scheme: dark;\n}`;\n\n/**\n * The default `--blume-*` design tokens (light + dark), shared by the app\n * sheet and the isolated example-preview sheet so previews inherit the site's\n * palette by default.\n */\nconst TOKEN_DEFAULTS = `:root {\n --blume-background: oklch(1 0 0);\n --blume-background-image: none;\n --blume-background-image-repeat: no-repeat;\n --blume-background-image-size: cover;\n --blume-foreground: oklch(0.145 0 0);\n --blume-muted: oklch(0.965 0 0);\n /* 5.28:1 on the page background. The headroom over 4.5:1 is the point: muted\n text is 14px body copy routinely set on a tinted surface (callouts, badges,\n panels), and each tint costs a few tenths. */\n --blume-muted-foreground: oklch(0.53 0 0);\n --blume-border: oklch(0.88 0.006 260 / 0.72);\n --blume-accent: oklch(0.145 0 0);\n --blume-accent-foreground: oklch(1 0 0);\n --blume-action: var(--blume-accent);\n --blume-action-foreground: var(--blume-accent-foreground);\n --blume-code-background: oklch(0.99 0 0);\n /* Shiki notation transformers: line/word highlight, diff add/remove. */\n --blume-code-highlight: oklch(0.55 0.16 255 / 0.1);\n --blume-code-highlight-border: oklch(0.55 0.16 255 / 0.55);\n --blume-code-add: oklch(0.72 0.16 150 / 0.16);\n --blume-code-add-border: oklch(0.52 0.15 150 / 0.7);\n --blume-code-remove: oklch(0.66 0.21 22 / 0.16);\n --blume-code-remove-border: oklch(0.55 0.2 22 / 0.7);\n --blume-code-word: oklch(0.55 0.16 255 / 0.16);\n --blume-code-word-border: oklch(0.55 0.16 255 / 0.5);\n --blume-radius: 0.75rem;\n\n /* Max width of the prose content column (article, breadcrumb, TOC, feedback,\n pagination, last-updated). Override to re-measure the whole column at once. */\n --blume-content-width: 42rem;\n\n /*\n * Font tokens resolve through optional src variables that Astro's Fonts API\n * populates (via theme.fonts). When unset they fall back to the system\n * stacks, so the default look is unchanged. The display font defaults to the\n * body font.\n */\n --blume-font-body: var(\n --blume-font-body-src,\n ui-sans-serif,\n system-ui,\n -apple-system,\n \"Segoe UI\",\n Roboto,\n Helvetica,\n Arial,\n sans-serif\n );\n --blume-font-mono: var(\n --blume-font-mono-src,\n ui-monospace,\n \"SF Mono\",\n \"Cascadia Code\",\n \"Source Code Pro\",\n Menlo,\n Consolas,\n monospace\n );\n --blume-font-display: var(--blume-font-display-src, var(--blume-font-body));\n}\n\n:root[data-theme=\"dark\"] {\n --blume-background: oklch(0.085 0 0);\n --blume-background-image: none;\n --blume-background-image-repeat: no-repeat;\n --blume-background-image-size: cover;\n --blume-foreground: oklch(0.96 0 0);\n --blume-muted: oklch(0.16 0 0);\n --blume-muted-foreground: oklch(0.68 0 0);\n --blume-border: oklch(0.24 0 0 / 0.8);\n --blume-accent: oklch(0.96 0 0);\n --blume-accent-foreground: oklch(0.085 0 0);\n --blume-action: var(--blume-accent);\n --blume-action-foreground: var(--blume-accent-foreground);\n --blume-code-background: oklch(0.12 0 0);\n /* Brighter tints read better over the dark code surface. */\n --blume-code-highlight: oklch(0.7 0.14 255 / 0.16);\n --blume-code-highlight-border: oklch(0.7 0.14 255 / 0.6);\n --blume-code-add: oklch(0.78 0.17 150 / 0.2);\n --blume-code-add-border: oklch(0.72 0.16 150 / 0.7);\n --blume-code-remove: oklch(0.72 0.21 22 / 0.22);\n --blume-code-remove-border: oklch(0.7 0.2 22 / 0.7);\n --blume-code-word: oklch(0.7 0.14 255 / 0.22);\n --blume-code-word-border: oklch(0.7 0.14 255 / 0.55);\n}`;\n\n/**\n * Tailwind theme mapping from `--blume-*` tokens to utility-facing names\n * (`bg-background`, `border-border`, `rounded-blume`, the font stacks).\n * Shared by both sheets so example code written against Blume's utility\n * vocabulary renders identically inside the isolated preview frame.\n */\nconst THEME_MAPPING = `@theme inline {\n --color-background: var(--blume-background);\n --color-foreground: var(--blume-foreground);\n --color-muted: var(--blume-muted);\n --color-muted-foreground: var(--blume-muted-foreground);\n --color-border: var(--blume-border);\n --color-accent: var(--blume-accent);\n --color-accent-foreground: var(--blume-accent-foreground);\n --color-action: var(--blume-action);\n --color-action-foreground: var(--blume-action-foreground);\n --color-code: var(--blume-code-background);\n --radius-blume: var(--blume-radius);\n --container-content: var(--blume-content-width);\n --font-sans: var(--blume-font-body);\n --font-mono: var(--blume-font-mono);\n --font-display: var(--blume-font-display);\n}`;\n\n/**\n * Build the single Tailwind v4 entry stylesheet for the generated runtime.\n *\n * Everything flows through one Tailwind-processed file so utilities are\n * generated and design tokens cascade deterministically:\n * base defaults -> config tokens -> user theme.css.\n */\nexport const tailwindEntryTemplate = (options: TailwindEntryOptions): string =>\n `/* Generated by Blume. Do not edit. */\n@import \"tailwindcss\";\n@plugin \"@tailwindcss/typography\";\n\n/* Scan Blume's components and the user's project for utility classes. */\n${options.sources.map((source) => `@source \"${source}\";`).join(\"\\n\")}\n\n${DARK_VARIANT}\n\n${COLOR_SCHEME_DEFAULTS}\n\n${TOKEN_DEFAULTS}\n\n${THEME_MAPPING}\n\n@layer base {\n /* Nothing refuses to shrink below its intrinsic content width. This global\n min-width reset defuses the classic flex/grid overflow — a long or\n truncating child forcing its container (and the page) past the viewport\n edge — so components don't need per-element min-w-0 overrides. */\n * {\n min-width: 0;\n }\n /* Interactive controls get a pointer cursor unless disabled. */\n button:not(:disabled),\n [role=\"button\"]:not(:disabled) {\n cursor: pointer;\n }\n html {\n scroll-behavior: smooth;\n scroll-padding-top: 4.5rem;\n text-rendering: optimizeLegibility;\n }\n /* Modal surfaces coordinate through independent root attributes. The lock\n remains until every owner releases its attribute, while !important lets it\n temporarily override (and therefore preserve) an authored inline value.\n Classic (non-overlay) scrollbars would otherwise vanish while locked and\n shift the page and the centered dialog sideways, so keep their gutter. */\n html:where([data-blume-nav-open], [data-blume-search-dialog-open]) {\n overflow: hidden !important;\n scrollbar-gutter: stable;\n }\n /* Headings use the display font (defaults to the body font when unset).\n The tightened tracking is part of the theme, not the font: display-tuned\n families bake it into their metrics, but a text family promoted to\n headings (including the Inter default) reads loose without it. -0.05em\n was matched visually against Inter Tight, the previous display default. */\n h1,\n h2,\n h3,\n h4,\n h5,\n h6 {\n font-family: var(--font-display);\n letter-spacing: -0.05em;\n }\n :focus-visible {\n outline: 2px solid var(--blume-accent);\n outline-offset: 2px;\n border-radius: 2px;\n }\n body {\n background-attachment: fixed;\n background-image: var(--blume-background-image);\n background-position: center top;\n background-repeat: var(--blume-background-image-repeat);\n background-size: var(--blume-background-image-size);\n }\n @media (prefers-reduced-motion: reduce) {\n html {\n scroll-behavior: auto;\n }\n }\n}\n\n/* Same-origin navigations ride Astro's client router (the layouts render\n ClientRouter), which owns the page-transition styling — including its own\n prefers-reduced-motion guard — so the theme declares nothing about it. The\n cross-document view-transition opt-in that briefly lived here is\n deliberately gone: mixing it with the client router double-animates the\n full-load navigations the router hands back to the browser. */\n\n/* Code reads left-to-right regardless of page direction; only the surrounding\n chrome mirrors for RTL. Inline code is isolated so LTR identifiers don't\n disturb the bidi flow of right-to-left prose. */\n[dir=\"rtl\"] pre,\n[dir=\"rtl\"] .prose pre {\n direction: ltr;\n text-align: left;\n}\n[dir=\"rtl\"] :not(pre) > code {\n unicode-bidi: isolate;\n}\n\n/* The sidebar's rows. A docs site renders every sidebar row on every page,\n so a large site's HTML is mostly this markup — one utility per row kind\n instead of the dozen classes each expands to keeps every page (and the\n build's HTML output) small. Kept in sync with NavTree.astro: a page row,\n a page-mode group's drill-in button, a collapsible group's summary and\n the link inside it, and a flat group's heading and the link inside it. */\n@utility blume-nav-link {\n @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;\n}\n@utility blume-nav-drill {\n @apply flex w-full items-center gap-2 rounded-[0.65rem] px-2.5 py-1.5 text-left font-medium text-foreground text-sm transition-colors hover:bg-muted;\n}\n@utility blume-nav-summary {\n @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;\n}\n@utility blume-nav-summary-link {\n @apply -my-1 flex flex-1 items-center gap-1.5 rounded py-1 transition-colors aria-[current=page]:text-foreground aria-[current=page]:font-semibold;\n}\n@utility blume-nav-heading {\n @apply mb-1 flex items-center gap-1.5 px-2.5 py-1.5 font-medium text-foreground text-sm;\n}\n/* The linked heading fills the heading's own box with negative margins, so\n its hover/active pill is the page row's exactly (same padding and radius)\n and an unlinked heading beside it keeps the same text position. */\n@utility blume-nav-heading-link {\n @apply -mx-2.5 -my-1.5 flex flex-1 items-center gap-1.5 rounded-[0.65rem] px-2.5 py-1.5 text-foreground transition-colors hover:bg-muted aria-[current=page]:bg-muted;\n}\n\n/* Theme Tailwind Typography (prose) with Blume tokens. */\n.prose {\n --tw-prose-body: var(--blume-foreground);\n --tw-prose-headings: var(--blume-foreground);\n --tw-prose-lead: var(--blume-muted-foreground);\n --tw-prose-links: var(--blume-foreground);\n --tw-prose-bold: var(--blume-foreground);\n --tw-prose-counters: var(--blume-muted-foreground);\n --tw-prose-bullets: var(--blume-muted-foreground);\n --tw-prose-hr: var(--blume-border);\n --tw-prose-quotes: var(--blume-muted-foreground);\n --tw-prose-quote-borders: var(--blume-border);\n --tw-prose-captions: var(--blume-muted-foreground);\n --tw-prose-code: var(--blume-foreground);\n --tw-prose-pre-code: var(--blume-foreground);\n --tw-prose-pre-bg: var(--blume-code-background);\n --tw-prose-th-borders: var(--blume-border);\n --tw-prose-td-borders: var(--blume-border);\n /* Neutralized: the plugin defaults are near-black text and shadow with no\n dark counterpart (Blume never applies prose-invert), and the kbd rule\n below draws the badge with Blume tokens instead. */\n --tw-prose-kbd: inherit;\n --tw-prose-kbd-shadows: transparent;\n color: var(--blume-muted-foreground);\n font-size: 0.875rem;\n line-height: 1.7;\n /* An unbreakable run — an API permission, a broker list, a bare URL used as\n its own link text, an OpenAPI operation title of the form\n \\`METHOD /very/long/{path}\\`, a module-qualified name in a generated\n reference — paints past the content column, and nothing between the\n paragraph and the viewport clips it, so it lands in the document's scroll\n width and drags the page sideways on a phone. Inherited, so headings and\n inline code are covered too; the only re-declarations below are the\n opt-in code-wrap mode and Twoslash, which want different values.\n \\`break-word\\` and not \\`anywhere\\`: only \\`anywhere\\` reduces min-content,\n which is what sizes \\`table-layout: auto\\` columns, so it would resize\n every table on the site. The flip side is that a token in a table cell\n still never breaks — the column grows to fit it — so wide tables rely on\n the .blume-table-scroll wrapper, not on this. */\n overflow-wrap: break-word;\n}\n\n/* No letter-spacing here: prose headings inherit the base h1-h6 rule's\n display tracking, same as headings outside the prose column. */\n.prose :where(h1, h2, h3, h4) {\n font-weight: 500;\n}\n\n.prose :where(h1) {\n font-size: 3rem;\n line-height: 1.1;\n margin: 0 0 1rem;\n}\n\n.prose :where(h2) {\n font-size: 1.875rem;\n line-height: 1.2;\n margin-top: 3rem;\n}\n\n.prose :where(h3) {\n font-size: 1.25rem;\n line-height: 1.35;\n}\n\n.prose :where(h4) {\n color: var(--blume-muted-foreground);\n font-size: 0.875rem;\n line-height: 1.4;\n}\n\n.prose :where(h1, h2, h3, h4, h5, h6) a {\n color: inherit;\n font-weight: inherit;\n text-decoration: none;\n}\n\n/* Auto-generated heading permalinks (markdown.headingAnchors): the whole\n heading links to its own id, with a muted “#” revealed on hover or keyboard\n focus so the link is discoverable without cluttering the heading at rest. */\n.prose :where(h2, h3, h4, h5, h6) a.blume-heading-anchor::after {\n content: \"#\";\n color: var(--blume-muted-foreground);\n margin-inline-start: 0.35em;\n opacity: 0;\n transition: opacity 0.15s ease;\n}\n.prose :where(h2, h3, h4, h5, h6) a.blume-heading-anchor:hover::after,\n.prose :where(h2, h3, h4, h5, h6) a.blume-heading-anchor:focus-visible::after {\n opacity: 1;\n}\n\n/* A [toc]-marked heading exists only for the table of contents: it stays in\n the flow as a zero-height, invisible anchor target so its TOC entry (and any\n deep link) still has somewhere to scroll to, without rendering on the page. */\n.prose :where(.blume-toc-only) {\n border: 0;\n height: 0;\n margin: 0;\n overflow: hidden;\n padding: 0;\n visibility: hidden;\n}\n\n.prose :where(h2:first-child) {\n border-top: 0;\n padding-top: 0;\n}\n\n.prose :where(p, ul, ol) {\n margin-top: 1rem;\n margin-bottom: 1rem;\n}\n\n.prose :where(strong) {\n font-weight: 600;\n}\n\n.prose :where(a) {\n color: var(--blume-foreground);\n font-weight: 500;\n text-decoration-line: underline;\n text-decoration-style: dotted;\n text-decoration-thickness: 1px;\n text-underline-offset: 0.2em;\n}\n\n.prose :where(a[data-blume-card]) {\n color: inherit;\n font-weight: inherit;\n text-decoration: none;\n}\n\n/* External Markdown links (markdown.externalLinks) open in a new tab and trail\n the same Lucide arrow-up-right the featured sidebar links use, as a mask so it\n takes the link's color. A link wrapping only an image (a badge, a logo)\n skips the arrow. */\n.prose :where(a[data-blume-external]:not(:has(img, svg)))::after {\n background-color: currentColor;\n content: \"\";\n display: inline-block;\n height: 0.75em;\n margin-inline-start: 0.125em;\n -webkit-mask: ${EXTERNAL_LINK_MASK} center / contain no-repeat;\n mask: ${EXTERNAL_LINK_MASK} center / contain no-repeat;\n opacity: 0.6;\n width: 0.75em;\n}\n\n.prose :where(hr) {\n margin: 2.5rem 0;\n}\n\n.prose :where(pre) {\n background: transparent;\n border: 1px solid var(--blume-border);\n border-radius: var(--blume-radius);\n color: var(--blume-foreground);\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n line-height: 1.55;\n margin: 1.5rem 0;\n overflow-x: auto;\n padding: ${CODE_PADDING_BLOCK_REM}rem 0;\n position: relative;\n}\n\n.prose :where(pre.astro-code) {\n background: transparent !important;\n color: var(--shiki-light, var(--blume-foreground)) !important;\n}\n\n:root[data-theme=\"dark\"] .prose :where(pre.astro-code) {\n color: var(--shiki-dark, var(--blume-foreground)) !important;\n}\n\n.prose :where(pre.astro-code span) {\n background: transparent !important;\n color: var(--shiki-light, inherit);\n font-style: var(--shiki-light-font-style, inherit);\n font-weight: var(--shiki-light-font-weight, inherit);\n text-decoration: var(--shiki-light-text-decoration, inherit);\n}\n\n:root[data-theme=\"dark\"] .prose :where(pre.astro-code span) {\n color: var(--shiki-dark, inherit);\n font-style: var(--shiki-dark-font-style, inherit);\n font-weight: var(--shiki-dark-font-weight, inherit);\n text-decoration: var(--shiki-dark-text-decoration, inherit);\n}\n\n/* The Diff component (@pierre/diffs) renders into a declarative shadow root and\n themes itself with light-dark(). Drive that from Blume's data-theme instead\n of the OS, scoped to the diff host: the custom property pierces the shadow\n boundary, where the appended host color-scheme rule consumes it. */\nblume-diff {\n --blume-diff-color-scheme: light;\n display: block;\n}\n\n:root[data-theme=\"dark\"] blume-diff {\n --blume-diff-color-scheme: dark;\n}\n\n.prose :where(pre[data-language]) {\n padding-top: 3.75rem;\n}\n\n.prose :where(pre[data-language])::before {\n align-items: center;\n border-bottom: 1px solid var(--blume-border);\n color: var(--blume-muted-foreground);\n content: attr(data-language);\n display: flex;\n font-family: var(--font-sans);\n font-size: 0.75rem;\n font-weight: 500;\n height: 2.75rem;\n left: 0;\n padding: 0 3.25rem 0 1rem;\n position: absolute;\n right: 0;\n top: 0;\n}\n\n.prose :where(pre[data-title])::before {\n content: attr(data-title);\n}\n\n/* Language icon (simple-icons) sits at the header's left edge; the label shifts\n right to make room. Injected at build time by the language-icon transformer.\n The icon is gated on data-language (the header bar), not just data-icon: the\n transformer runs whenever icons are on, but a header-less standalone\n CodeBlock (no title) never gets data-language, so without this gate the\n absolutely-positioned icon would overlap the first code line. Fenced code\n and titled blocks always have a header, so the icon shows there. */\n.prose > :where(pre[data-language][data-icon])::after {\n content: \"\";\n height: 0.875rem;\n left: 1rem;\n -webkit-mask-position: center;\n -webkit-mask-repeat: no-repeat;\n -webkit-mask-size: contain;\n mask-position: center;\n mask-repeat: no-repeat;\n mask-size: contain;\n pointer-events: none;\n position: absolute;\n top: 0.875rem;\n width: 0.875rem;\n}\n\n.prose > :where(pre[data-language][data-icon])::before {\n padding-left: 2.5rem;\n}\n\n/* One mask rule per language the site's Markdown uses (see\n markdown/language-icon.ts); the ::after above only gets a color and a mask\n from these, so a block whose language has no rule paints nothing. */\n${options.languageIcons ?? \"\"}\n\n.prose :where(pre code) {\n background: transparent;\n border-radius: 0;\n color: inherit;\n font-size: inherit;\n font-weight: 400;\n padding: 0;\n}\n\n/* Long lines scroll inside the code element, not the pre: the pre stays static\n so its absolute header bar (::before) and copy button don't drift with the\n scroll. The pre's horizontal padding lives here so content still scrolls\n edge-to-edge past it. Two contexts opt out: twoslash blocks (popups must\n escape any scroll container — see theme/twoslash.ts), and the API request\n panel, which owns its code layout and keeps the copy control in the panel\n header. Every other component that hosts a code block — Tabs, CodeGroup,\n Steps, Callout, Card, Accordion — is real prose content and keeps the inset,\n even though its chrome wrapper is not-prose. */\n.prose :where(pre:not(.twoslash, .twoslash pre, blume-panel-tabs *) > code) {\n display: block;\n /* Tall blocks scroll vertically in place instead of taking the page. Both\n axes live on the code element for the same reason: the pre stays static\n so the header bar and copy button never drift with the scroll. */\n max-height: 24rem;\n overflow: auto;\n /* The small bottom inset keeps the horizontal thumb off the last line's\n descenders now that scrollbars are visible. The pre gives up the same\n amount below (next rule), so the inset adds no height to the block. */\n padding: 0 1.25rem ${CODE_SCROLL_INSET_REM}rem;\n /* Thin theme-colored scrollbars, matching the sidebar treatment, so a\n height-capped block reads as scrollable instead of simply ending.\n Safari before 18.2 supports neither property and falls back to the\n platform-default scrollbar — acceptable, since macOS overlays it. */\n scrollbar-color: var(--blume-border) transparent;\n scrollbar-width: thin;\n}\n\n/* The scroller's bottom inset comes out of the pre's own block padding: the\n text still sits one full inset above the frame's bottom edge, the thumb sits\n in the gap, and a one-line command is no taller than it was before the\n scroller existed. */\n.prose :where(pre:not(.twoslash, .twoslash pre, blume-panel-tabs *):has(> code)) {\n padding-bottom: calc(${CODE_PADDING_BLOCK_REM}rem - ${CODE_SCROLL_INSET_REM}rem);\n}\n\n/* The dark border token is too close to the page background to read as a\n scrollbar thumb; derive a brighter one from the muted foreground instead. */\n:root[data-theme=\"dark\"]\n .prose\n :where(pre:not(.twoslash, .twoslash pre, blume-panel-tabs *) > code) {\n scrollbar-color: color-mix(in oklab, var(--blume-muted-foreground) 55%, transparent)\n transparent;\n}\n\n/* Word wrap (markdown.code.wrap): long lines wrap instead of scrolling. The\n attribute is set on <body> from config; default code keeps \\`white-space: pre\\`. */\n[data-blume-code-wrap] pre,\n[data-blume-code-wrap] pre code {\n overflow-wrap: break-word;\n white-space: pre-wrap;\n}\n\n.prose :where(table) {\n font-size: 0.8125rem;\n}\n\n/* Scroll wide tables in place; the wrapper is added by the blume:table-wrap hast\n plugin. The border+radius frames the scroll viewport so a clipped wide table\n reads as scrollable rather than cut off, and carries the table's vertical\n rhythm (the inner table's own margin is zeroed so it fills the frame). */\n.blume-table-scroll {\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n margin: 1.5rem 0;\n border: 1px solid var(--blume-border);\n border-radius: var(--blume-radius);\n}\n.blume-table-scroll > table {\n margin: 0;\n}\n/* Restore inner padding on every cell. Typography zeroes the first/last cell's\n inline padding so a borderless table aligns to the prose margin; inside the\n framed wrapper that leaves edge text touching the border. The :is() selector\n outweighs Typography's :where()-scoped rules so the outer columns get it too.\n\n The block padding is 0.75rem against a table line-height near 1.7: at 0.5rem a\n cell whose content wrapped put MORE space between its own two lines than\n between itself and the next row, so a table of wrapping cells read as one\n block rather than as rows. The inline padding stays where it was, deliberately\n — widening it comes out of column width in a capped article, and on one corpus\n that pushed cells fitting on two lines onto three. */\n.blume-table-scroll :is(th, td) {\n padding: 0.75rem;\n}\n/* Keep column labels on one line so a two-word header does not wrap into a\n ragged stack; the table just scrolls a little wider instead. Body cells keep\n wrapping so a wide table stays a scroll away, not an endless horizontal run. */\n.blume-table-scroll th {\n white-space: nowrap;\n}\n\n/* GFM renders cells as <td><code> directly, which the descendant form alone\n never matches (a cell is not its own descendant). */\n.prose :where(td, th) > code,\n.prose :where(td, th) :not(pre) > code {\n white-space: normal;\n}\n\nblume-tabs pre,\n.not-prose > div > pre {\n background: var(--blume-code-background);\n}\n\n/* The Component source pane fills its tab's fixed height (set inline to match the\n preview tab) and scrolls the inner code element, leaving the pre static so the\n copy button — absolute, pinned to the pre — doesn't drift while it scrolls. */\npre.blume-source {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n}\n\npre.blume-source > code {\n flex: 1;\n /* The pane's measured height is authoritative (it can exceed the prose\n 24rem cap), so undo the generic prose max-height: the code must fill\n whatever height the tab was given. */\n max-height: none;\n min-height: 0;\n overflow: auto;\n}\n\n/* Code blocks inside tabs sit flush; the tab container owns the frame. These\n rules are unlayered like the base \\`pre\\` styles, so they win over Tailwind's\n layered utilities (which a class on the panel cannot). The second selector\n covers <CodeGroup>, where each code block IS the panel (a direct child of the\n tab content wrapper) rather than a <pre> nested inside a <Tab>. */\nblume-tabs [data-blume-tab-panel] > pre,\nblume-tabs [data-blume-tab-content] > pre {\n /* Important to beat the astro-code background rule above; the tab container\n owns the surface, so the code sits directly on it. */\n background: transparent !important;\n border: 0;\n border-radius: 0;\n margin: 0;\n}\n\n/* These contexts drop the language bar (\\`::before\\` is cleared below), so the\n bar's padding goes with it and the block keeps the plain inset. Unscoped on\n purpose: <CodeBlock> wraps its pre in its own \\`.prose\\` below the component\n chrome, so without this a titled block on a page with no prose column\n (PageLayout) would keep the 3.75rem bar padding under a cleared bar. */\nblume-tabs pre[data-language],\n.not-prose pre[data-language] {\n padding-top: ${CODE_PADDING_BLOCK_REM}rem;\n}\n\n/* Where no language bar holds the copy button — a flush block in tabs or a\n not-prose component, a bar-less block in prose — the docs layout still pins\n one over the block's top-right corner, so a first line long enough to reach\n it would end underneath. A code switcher (CodeGroup, ts2js) hosts the button\n in its tab strip instead and needs nothing here; for every block that keeps\n an overlay button, give the scrolling code element enough end padding that\n the line's tail clears the button at the end of its scroll. Keyed on the\n injected button itself (which the playground's client-created response pre\n never gets), and \\`.prose\\`-scoped to outrank the base \\`:where(pre code)\\`\n inset. Not a vertical inset — that reserved a strip above every one-line\n command and read as a rendering bug. */\n.prose :is(blume-tabs pre, .not-prose pre, pre:not([data-language])):not(.twoslash, .twoslash pre, blume-panel-tabs *):has(> [data-blume-copy]) > code {\n padding-inline-end: 3.5rem;\n}\n\nblume-tabs pre[data-language]::before,\n.not-prose pre[data-language]::before {\n content: none;\n}\n\n/* Pre-hydration layout-shift guard. The horizontal trigger row and\n single-panel visibility are built by the <blume-tabs> custom element's JS:\n until it upgrades, the trigger row is empty and every panel is in normal flow,\n so the browser paints all panels stacked vertically (e.g. all four\n package-manager commands at once) before JS hides them. While the element is\n :not(:defined), show only the first panel and reserve the trigger row's height\n so nothing jumps when JS takes over. The child selector covers both <Tab>\n panels and <CodeGroup> raw code blocks (which only gain\n [data-blume-tab-panel] from JS). */\nblume-tabs:not(:defined) [data-blume-tab-content] > *:nth-child(n + 2) {\n display: none;\n}\n\nblume-tabs:not(:defined) [data-blume-tablist] {\n /* Matches a trigger's height: py-2.5 + text-xs line-height + border-b-2. */\n min-height: 2.375rem;\n}\n\nblume-tabs:not(:defined)[data-dropdown=\"true\"] [data-blume-tablist] {\n /* Dropdown variant: p-3 padding around a select (py-2 + text-sm + border). */\n min-height: 3.875rem;\n}\n\n/* Code blocks inside request/response example panels sit flush; the panel owns\n the frame. Unlayered (like the tab rules) so they beat the base \\`pre\\` styles\n that a class on the panel cannot. */\n[data-blume-code-panel] pre {\n background: transparent !important;\n border: 0;\n border-radius: 0;\n margin: 0;\n}\n\n/* Opt-in line numbers (\\`\\`\\`ts file.ts lineNumbers): a counter-driven gutter that\n works in prose and flush (tab / API panel) code blocks alike. */\npre[data-line-numbers] code {\n counter-reset: line;\n}\n\npre[data-line-numbers] .line::before {\n color: var(--blume-muted-foreground);\n content: counter(line);\n counter-increment: line;\n display: inline-block;\n margin-right: 1.25rem;\n text-align: right;\n user-select: none;\n width: 1.25rem;\n}\n\n/* Shiki notation transformers (on by default). The notation comments are\n stripped from the output; these style the classes they leave behind. A styled\n line becomes a full-width inline-block so its background fills the row; plain\n lines are left untouched, so blocks without notations render as before. */\n.line.highlighted,\n.line.diff {\n display: inline-block;\n width: 100%;\n}\n\n/* The backgrounds need !important to beat the \\`pre.astro-code span\\` rule above,\n which forces token spans transparent; these line/word spans are exceptions. */\n.line.highlighted {\n background-color: var(--blume-code-highlight) !important;\n box-shadow: inset 2px 0 0 0 var(--blume-code-highlight-border);\n}\n\n.line.diff.add {\n background-color: var(--blume-code-add) !important;\n box-shadow: inset 2px 0 0 0 var(--blume-code-add-border);\n}\n\n.line.diff.remove {\n background-color: var(--blume-code-remove) !important;\n box-shadow: inset 2px 0 0 0 var(--blume-code-remove-border);\n}\n\n/* Word highlight (\\`// [!code word:x]\\`) wraps matches in an inline span. The\n element selector keeps it ahead of the transparent token-span rule. */\nspan.highlighted-word {\n background-color: var(--blume-code-word) !important;\n border-radius: 0.25rem;\n box-shadow: 0 0 0 1px var(--blume-code-word-border);\n padding: 0.1em 0.2em;\n}\n\n/* Focus (\\`// [!code focus]\\`): dim and blur the rest; reveal on hover. */\npre:has(.line.focused) .line:not(.focused) {\n filter: blur(0.085rem);\n opacity: 0.5;\n transition:\n filter 0.2s ease,\n opacity 0.2s ease;\n}\n\npre:has(.line.focused):hover .line:not(.focused) {\n filter: none;\n opacity: 1;\n}\n\n@media (prefers-reduced-motion: reduce) {\n pre:has(.line.focused) .line:not(.focused) {\n transition: none;\n }\n}\n\n@media (max-width: 640px) {\n .prose :where(h1) {\n font-size: 2.25rem;\n }\n\n .prose :where(h2) {\n font-size: 1.625rem;\n }\n}\n\n.prose :not(pre) > code:not(:where([class~=\"not-prose\"] *)) {\n background: var(--blume-code-background);\n padding: 0.15em 0.35em;\n border-radius: 0.3rem;\n font-family: var(--font-mono);\n font-weight: 500;\n}\n\n.prose :not(pre) > code::before,\n.prose :not(pre) > code::after {\n content: none;\n}\n\n/* Keyboard keys (<kbd>) render as the same bordered badge the search dialog\n uses for its shortcut hints. Typography's own kbd rule is scoped with\n :not(.not-prose *), so it never reaches a key inside <Steps>, <Callout>,\n <Card>, or any other not-prose component; this rule is deliberately\n unscoped from that exclusion so a key looks the same everywhere in the\n content column. Unlayered, so it outranks the plugin's layered rule. */\n.prose kbd {\n background: var(--blume-muted);\n border: 1px solid var(--blume-border);\n border-radius: 0.25rem;\n box-shadow: none;\n color: inherit;\n font-family: var(--font-mono);\n font-size: 0.85em;\n font-weight: 500;\n padding: 0.125rem 0.25rem;\n}\n\n/* Inline code highlighting: Shiki colors the tokens of a \\`code\\`{:lang} snippet\n via the same dual-theme CSS variables as fenced blocks, keeping the inline\n pill background. Always on — it only fires on the trailing {:lang} marker. */\n.prose code.blume-inline-code span {\n color: var(--shiki-light);\n}\n\n:root[data-theme=\"dark\"] .prose code.blume-inline-code span {\n color: var(--shiki-dark);\n}\n\n/* Print / \"Save as PDF\" (the page-actions Export → PDF runs window.print()).\n Strip the surrounding chrome so the printout is just the article. */\n@media print {\n [data-blume-banner],\n header,\n aside,\n [data-blume-page-actions],\n #blume-content > nav,\n #blume-content > details {\n display: none !important;\n }\n\n /* Paper can't scroll: uncap the code scroller so long blocks print in\n full, same as tab panels are force-expanded below. */\n .prose :where(pre:not(.twoslash, .twoslash pre, blume-panel-tabs *) > code) {\n max-height: none;\n overflow: visible;\n }\n\n #blume-content {\n padding: 0 !important;\n }\n\n .prose {\n margin: 0 !important;\n max-width: none !important;\n }\n\n /* Reveal collapsed tab panels so their content isn't dropped from the print. */\n blume-tabs [data-blume-tab-panel] {\n display: block !important;\n }\n\n /* Keep code/callout backgrounds (subject to the dialog's \"Background graphics\"\n toggle). */\n pre,\n .prose :not(pre) > code {\n -webkit-print-color-adjust: exact;\n print-color-adjust: exact;\n }\n}\n\n/* Twoslash rich-renderer styles (used by fences with the \\`twoslash\\` meta). */\n${options.twoslashCss ?? \"\"}\n\n/* Token overrides: config first, then the user's theme.css (highest priority). */\n${options.configTokens}\n${options.userTheme}\n`;\n\ninterface ExamplesEntryOptions {\n /** Config-derived token overrides (`:root { --blume-accent: ... }`). */\n configTokens: string;\n /** Globs to scan for utility classes (the project and examples directory). */\n sources: string[];\n /** Raw contents of the configured `examples.css`, if any. */\n userCss: string;\n}\n\n/**\n * Build the Tailwind entry for `<Component />` preview frames. Each example\n * renders in its own iframe so none of the app sheet above — prose typography,\n * component chrome, base overrides — can reach it. This sheet provides only\n * what an example needs to look like it does in an app: Tailwind (preflight +\n * utilities scanned from the example sources), the `--blume-*` tokens and\n * their utility mapping (so `bg-background`-style classes keep working and\n * previews follow the site palette by default), and the user's example CSS\n * (shadcn variables, `@theme` mappings, custom styles) last so it wins.\n */\nexport const examplesEntryTemplate = (options: ExamplesEntryOptions): string =>\n `/* Generated by Blume. Do not edit. */\n@import \"tailwindcss\";\n\n/* Scan the project (and an out-of-root examples directory) for utility classes. */\n${options.sources.map((source) => `@source \"${source}\";`).join(\"\\n\")}\n\n${DARK_VARIANT}\n\n${COLOR_SCHEME_DEFAULTS}\n\n${TOKEN_DEFAULTS}\n\n${THEME_MAPPING}\n\n/* Frame defaults: readable text in both modes, transparent so the docs pane's\n surface shows through. Base layer, so any user/example CSS wins. */\n@layer base {\n body {\n background: transparent;\n color: var(--blume-foreground);\n }\n}\n\n/* Token overrides: config first, then the configured examples css (highest\n priority) — the place for shadcn variables and other component tokens. */\n${options.configTokens}\n${options.userCss}\n`;\n",
|
|
28
|
+
"import { dirname, isAbsolute, relative, resolve } from \"pathe\";\n\n/**\n * A Tailwind `@source \"…\"` / `@source not \"…\"` directive with a quoted path.\n * `@source inline(\"…\")` never matches: the quote must directly follow the\n * keyword (or `not`), and `inline(` sits in between.\n */\nconst SOURCE_DIRECTIVE =\n /@source(?<not>\\s+not)?\\s+(?<quote>[\"'])(?<path>[^\"']+)\\k<quote>/gu;\n\n/**\n * Rewrite the relative `@source` paths in a user stylesheet so they still\n * point where the author meant once the sheet is inlined into a generated\n * Tailwind entry. Tailwind resolves `@source` relative to the stylesheet that\n * declares it, and Blume splices `theme.css` / `examples.css` verbatim into\n * `.blume/src/generated/*.css` (or `src/generated/*.css` after eject), which\n * would silently re-root them there. Resolving each path against the user's\n * file and re-expressing it relative to the generated directory keeps the\n * standard contract — write `@source \"../../packages/ui\"` next to the file\n * that says it — and lets a monorepo scan sibling workspace packages for\n * utility classes without any knowledge of Blume's internal layout. Absolute\n * paths pass through untouched.\n */\nexport const rebaseSourceDirectives = (\n css: string,\n options: {\n /** The user's stylesheet the CSS was read from. */\n from: string;\n /** The directory of the generated entry the CSS is inlined into. */\n to: string;\n }\n): string => {\n const base = dirname(options.from);\n return css.replace(\n SOURCE_DIRECTIVE,\n (\n directive: string,\n not: string | undefined,\n quote: string,\n path: string\n ): string => {\n if (isAbsolute(path)) {\n return directive;\n }\n const rebased = relative(options.to, resolve(base, path));\n return `@source${not ?? \"\"} ${quote}${rebased}${quote}`;\n }\n );\n};\n",
|
|
29
|
+
"/**\n * CSS for Twoslash's rich renderer, emitted into the Tailwind entry (Twoslash\n * runs on any fence with the `twoslash` meta). It is the shipped\n * `@shikijs/twoslash` stylesheet plus a Blume-themed override layer:\n *\n * - maps the renderer's `--twoslash-*` variables onto Blume tokens so popups\n * match the active theme (light/dark);\n * - lets popups escape the code block's horizontal scroll container; and\n * - restores backgrounds the theme's `pre.astro-code span { background:\n * transparent !important }` rule would otherwise clear from popup chrome.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\n\nconst require = createRequire(import.meta.url);\n\n/** Blume-token theming layered over the shipped rich renderer styles. */\nconst OVERRIDES = `\n/* Twoslash: theme the rich renderer with Blume tokens. */\n:root {\n --twoslash-border-color: var(--blume-border);\n --twoslash-popup-bg: var(--blume-code-background);\n --twoslash-popup-color: var(--blume-foreground);\n --twoslash-popup-shadow: 0 6px 24px oklch(0 0 0 / 0.18);\n --twoslash-docs-color: var(--blume-muted-foreground);\n --twoslash-docs-font: var(--font-sans);\n --twoslash-code-font: var(--font-mono);\n --twoslash-matched-color: var(--blume-foreground);\n --twoslash-unmatched-color: var(--blume-muted-foreground);\n --twoslash-cursor-color: var(--blume-muted-foreground);\n}\n\n/* Popups are absolutely positioned and must escape the pre's scroll container.\n The base prose pre rule (from the typography layer) wins the cascade here\n despite lower specificity, so !important is needed to force visibility.\n Regular code blocks scroll their inner code element and carry the horizontal\n padding there; twoslash code opts out of that scroller (popups again), so the\n padding is restored on the pre. */\n.prose pre.twoslash {\n overflow: visible !important;\n padding-left: 1.25rem;\n padding-right: 1.25rem;\n}\n\n/* Twoslash blocks keep \\`overflow: visible\\` so popups can escape, which means\n long lines can't scroll — they'd push past the viewport on narrow screens.\n Wrap them on mobile instead; hover popups still position against the token. */\n@media (max-width: 640px) {\n .prose pre.twoslash code {\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n }\n}\n\n/* The rich renderer renders each popup's type signature as a nested Shiki\n pre. Strip the code-block chrome (border, radius, padding, background) so it\n sits flush inside the popup, which owns the frame. */\n.prose pre.twoslash .twoslash-popup-container pre {\n background: transparent !important;\n border: 0 !important;\n border-radius: 0 !important;\n margin: 0 !important;\n padding: 0 !important;\n}\n\n/* Re-assert chrome backgrounds the transparent token-span rule would clear. */\n.twoslash .twoslash-popup-container,\n.twoslash .twoslash-popup-arrow,\n.twoslash .twoslash-completion-cursor .twoslash-completion-list {\n background: var(--twoslash-popup-bg) !important;\n}\n.twoslash .twoslash-popup-error {\n background: var(--twoslash-error-bg) !important;\n}\n.twoslash .twoslash-highlighted {\n background: var(--twoslash-highlighted-bg) !important;\n}\n\n/* The error squiggle uses a background image the transparent rule clears, so\n draw it as a wavy underline instead. */\n.twoslash .twoslash-error {\n text-decoration: underline wavy var(--twoslash-error-color);\n text-underline-offset: 0.25em;\n}\n\n.twoslash-popup-container {\n max-width: min(90vw, 36rem);\n}\n`;\n\n/** The full Twoslash stylesheet (shipped rich styles + Blume overrides). */\nexport const twoslashCss = (): string => {\n const file = require.resolve(\"@shikijs/twoslash/style-rich.css\");\n return `${readFileSync(file, \"utf-8\")}\\n${OVERRIDES}`;\n};\n",
|
|
30
|
+
"import { readFile } from \"node:fs/promises\";\n\nimport pMap from \"p-map\";\nimport { isAbsolute, join, relative } from \"pathe\";\nimport { glob } from \"tinyglobby\";\n\nimport type { ExampleLookup } from \"../core/types.ts\";\nimport type { IslandClientMode } from \"./islands.ts\";\nimport { readClientMode } from \"./islands.ts\";\n\n/** Framework an example is authored in, inferred from its extension. */\nexport type ExampleFramework = \"astro\" | \"react\" | \"svelte\" | \"vue\";\n\n/** A discovered `examples/` file, ready to wrap (live) and show (source). */\nexport interface ExampleSpec {\n /** Hydration directive for a framework example; `undefined` for `.astro`. */\n client?: IslandClientMode;\n /** Absolute path to the example source file. */\n file: string;\n /** Framework, or `astro` for a server-rendered (static) example. */\n framework: ExampleFramework;\n /** Shiki language for the source pane — the file extension. */\n lang: string;\n /** MDX-facing key: the path under `examples/`, sans extension, `/`-joined. */\n path: string;\n /** Raw source text shown in the code tab. */\n source: string;\n}\n\nexport interface ExampleDiscovery {\n /**\n * Absolute directory the examples were discovered under: the configured\n * `examples.source` (or a glob's static prefix) resolved against the root.\n */\n dir: string;\n examples: ExampleSpec[];\n warnings: string[];\n}\n\n/** Example extensions mapped to the framework that renders them. */\ninterface FrameworkByExtension {\n [extension: string]: ExampleFramework;\n}\n\nconst FRAMEWORK_BY_EXT: FrameworkByExtension = {\n astro: \"astro\",\n jsx: \"react\",\n svelte: \"svelte\",\n tsx: \"react\",\n vue: \"vue\",\n};\n\n// Captures the extension so we can strip it from the path key and pick the\n// framework. Kept in sync with the glob below — non-matching files a user glob\n// happens to sweep in (e.g. a registry's `.ts` sources) are dropped here.\nconst EXAMPLE_FILE = /\\.(?<ext>astro|jsx|svelte|tsx|vue)$/u;\n\n// Renderable example files when `examples` names a plain directory.\nconst DEFAULT_EXAMPLE_GLOB = \"**/*.{astro,jsx,svelte,tsx,vue}\";\n\n/** Ceiling on concurrent example-file reads; unbounded fan-out risks EMFILE. */\nconst READ_CONCURRENCY = 16;\n\n/**\n * Files the preview-frame Tailwind entry scans for utility classes, appended\n * to each directory from `exampleScanRoots`.\n */\nexport const EXAMPLE_SCAN_GLOB = \"**/*.{astro,jsx,svelte,ts,tsx,vue}\";\n\n/**\n * Directories the `<Component />` preview sheet scans for utility classes:\n * the project root, plus the examples directory when it lives outside the\n * root (a sibling workspace package, say). Tailwind's `@source` is a file\n * glob, not an import graph, so an out-of-root examples directory would\n * otherwise contribute no utilities and previews would render half-styled.\n */\nexport const exampleScanRoots = (\n root: string,\n examplesDir: string\n): string[] => {\n const path = relative(root, examplesDir);\n const outside = path.startsWith(\"..\") || isAbsolute(path);\n return outside ? [root, examplesDir] : [root];\n};\n\n// Glob magic that turns `examples` from a plain directory into a pattern. `()`,\n// `@`, and `+` are excluded so literal path segments (npm scopes, parens) keep\n// resolving as directories; the extglob leads `*?!` still trigger here.\nconst GLOB_MAGIC = /[!*?[\\]{}]/u;\n\n/**\n * Split a glob into its static directory prefix and the remaining pattern, so\n * discovered files can be keyed relative to that prefix (e.g.\n * `registry/x/**\\/examples/*` → `{ base: \"registry/x\", rest: \"**\\/examples/*\" }`).\n */\nconst splitGlobBase = (pattern: string) => {\n const segments = pattern.split(\"/\");\n // Only called when the pattern contains glob magic (see the caller), and `/`\n // is never magic, so the magic char always lands in a segment — `findIndex`\n // is never -1 here.\n const firstMagic = segments.findIndex((segment) => GLOB_MAGIC.test(segment));\n return {\n base: segments.slice(0, firstMagic).join(\"/\"),\n rest: segments.slice(firstMagic).join(\"/\"),\n };\n};\n\n/**\n * Discover preview examples for the `examples` config (default `examples`).\n * Every `.astro`/`.tsx`/`.jsx`/`.vue`/`.svelte` file becomes addressable by\n * `<Component path=\"...\">`, where the path is the file's location without its\n * extension (e.g. `forms/login.tsx` → `forms/login`).\n *\n * `pattern` is a directory by default, but may be a glob (anything with\n * `*`/`?`/`[]`/`{}`/`!`) — then only matching files are discovered and each\n * `<Component path>` key is relative to the glob's static prefix. This lets a\n * registry layout that colocates component sources with their examples be\n * targeted directly (e.g. `registry/<pkg>/**\\/examples/*`) without the sources —\n * which have no default export and so can't be wrapped — being swept in.\n *\n * Discovery is path-based (a glob), so no example code is executed. Framework\n * examples carry a hydration mode (default `client:visible`, overridable via\n * `export const client`); `.astro` examples render statically with no client\n * directive.\n */\nexport const discoverExamples = async (\n root: string,\n pattern = \"examples\"\n): Promise<ExampleDiscovery> => {\n const { base, rest } = GLOB_MAGIC.test(pattern)\n ? splitGlobBase(pattern)\n : { base: pattern, rest: DEFAULT_EXAMPLE_GLOB };\n const dir = join(root, base);\n const matches = await glob([rest], {\n absolute: true,\n cwd: dir,\n onlyFiles: true,\n });\n const files = matches.toSorted();\n const sources = await pMap(files, (file) => readFile(file, \"utf-8\"), {\n concurrency: READ_CONCURRENCY,\n });\n\n const examples: ExampleSpec[] = [];\n const warnings: string[] = [];\n const seen = new Map<string, string>();\n\n // Extracted so the two skip paths become early `return`s (one `continue`\n // budget per loop under the lint rule) instead of `continue` statements.\n const collectExample = (file: string, source: string): void => {\n const ext = file.match(EXAMPLE_FILE)?.groups?.ext;\n const framework = ext ? FRAMEWORK_BY_EXT[ext] : undefined;\n if (!(ext && framework)) {\n return;\n }\n // Strip the trailing `.<ext>` to form the `<Component path>` key.\n const path = relative(dir, file).slice(0, -(ext.length + 1));\n const existing = seen.get(path);\n if (existing) {\n warnings.push(\n `Two examples both resolve to \"${path}\" (\"${existing}\" and \"${file}\"); ignoring the second. Give them distinct paths.`\n );\n return;\n }\n seen.set(path, file);\n examples.push({\n client:\n framework === \"astro\"\n ? undefined\n : readClientMode(source, file, warnings),\n file,\n framework,\n lang: ext,\n path,\n source,\n });\n };\n\n for (const [index, file] of files.entries()) {\n collectExample(file, sources[index] ?? \"\");\n }\n\n return { dir, examples, warnings };\n};\n\n/**\n * Reduce discovered examples to the `<Component path>` → source lookup the\n * agent-facing Markdown downleveler needs (see {@link BlumeProject.examples}).\n */\nexport const exampleMarkdownLookup = (examples: ExampleSpec[]): ExampleLookup =>\n Object.fromEntries(\n examples.map((example) => [\n example.path,\n { lang: example.lang, source: example.source },\n ])\n );\n",
|
|
31
|
+
"import { existsSync, realpathSync } from \"node:fs\";\nimport {\n lstat,\n mkdir,\n readdir,\n readlink,\n realpath,\n rm,\n symlink,\n} from \"node:fs/promises\";\nimport { isBuiltin } from \"node:module\";\n\nimport {\n basename,\n dirname,\n isAbsolute,\n join,\n normalize,\n relative,\n resolve,\n} from \"pathe\";\n\nimport { packageRoot } from \"../core/package-root.ts\";\n\n/**\n * The two places an installer can put Blume's dependencies:\n * - `<blume>/node_modules` — deps nested under the package (workspace source,\n * or npm nesting them away from a conflicting hoisted copy)\n * - `dirname(<blume>)` — deps as siblings in the store (isolated/pnpm)\n *\n * `packageRoot()` resolves to Blume's real on-disk path (Node follows the\n * install symlink), so its parent is the store's package directory where the\n * isolated linker places the siblings.\n */\nexport const depsCandidates = (pkgDir: string): string[] => [\n join(pkgDir, \"node_modules\"),\n dirname(pkgDir),\n];\n\n/** First dependency candidate containing the package dir `segments`, or null. */\nexport const candidateHolding = (\n pkgDir: string,\n ...segments: string[]\n): string | null =>\n depsCandidates(pkgDir).find((dir) => existsSync(join(dir, ...segments))) ??\n null;\n\nconst holdsAstro = (dir: string): boolean => existsSync(join(dir, \"astro\"));\nconst holdsMdx = (dir: string): boolean =>\n existsSync(join(dir, \"@astrojs\", \"mdx\"));\n\n/**\n * Locate the directory that holds Blume's installed dependencies (Astro and its\n * integrations).\n *\n * With a clean hoisted install this is moot — the deps sit in a `node_modules`\n * the generated `.blume/` already walks up into. But under isolated linkers\n * (Bun's `isolated` mode, pnpm) Blume's deps are NOT hoisted into the project;\n * they live beside the Blume package in a virtual store, invisible to the\n * upward walk from `.blume/` — so probe the {@link depsCandidates}.\n *\n * Astro alone is a bad probe: an npm split install (an `overrides` pin plus an\n * incremental install) hoists `astro` to the project root while Blume's other\n * deps stay nested, and probing for astro then picks the root directory — one\n * that holds none of them. Prefer a candidate with the full set (astro beside\n * `@astrojs/mdx`, the integration every generated runtime declares), then one\n * with the integrations (astro hoisted away — the rest of Blume's deps sit\n * there too), then one with astro alone.\n */\nexport const blumeDepsDir = (pkgDir: string = packageRoot()): string | null => {\n const candidates = depsCandidates(pkgDir);\n return (\n candidates.find((dir) => holdsAstro(dir) && holdsMdx(dir)) ??\n candidates.find(holdsMdx) ??\n candidates.find(holdsAstro) ??\n null\n );\n};\n\n/**\n * The package a bare specifier names (`@scope/name` or `name`, whatever\n * subpath follows), or null for a Node builtin.\n */\nexport const packageNameOf = (specifier: string): string | null => {\n if (isBuiltin(specifier)) {\n return null;\n }\n const [first = \"\", second] = specifier.split(\"/\");\n if (!first.startsWith(\"@\")) {\n return first || null;\n }\n return second ? `${first}/${second}` : null;\n};\n\n/**\n * The directory an import of package `name` loads from when resolved from\n * `fromDir`: the first `node_modules/<name>` up the physical ancestors — the\n * walk Node itself performs — as a real path, or null when none holds it.\n */\nexport const packageDirFrom = (\n name: string,\n fromDir: string\n): string | null => {\n let dir = normalize(fromDir);\n while (true) {\n const candidate = join(dir, \"node_modules\", name);\n if (existsSync(join(candidate, \"package.json\"))) {\n return normalize(realpathSync(candidate));\n }\n const parent = dirname(dir);\n if (parent === dir) {\n return null;\n }\n dir = parent;\n }\n};\n\n/** Whether `child` is `parent` or sits somewhere under it. */\nconst isWithin = (parent: string, child: string): boolean => {\n const path = relative(parent, child);\n return path === \"\" || !(path.startsWith(\"..\") || isAbsolute(path));\n};\n\n/**\n * Point `link` at the package directory `target`, replacing a link to anything\n * else and leaving a real directory alone. The link is relative, so it keeps\n * working when the project moves as a whole (a server build shipped with its\n * `node_modules`); without symlink privilege on Windows it falls back to a\n * junction, which must be absolute.\n */\nconst linkPackageDir = async (link: string, target: string): Promise<void> => {\n await mkdir(dirname(link), { recursive: true });\n // A relative link resolves from its folder's real path, which differs from\n // the logical one under a symlinked parent (macOS's `/var` → `/private/var`),\n // so the relative target is computed from the real path.\n const from = normalize(await realpath(dirname(link)));\n let existing: Awaited<ReturnType<typeof lstat>> | null;\n try {\n existing = await lstat(link);\n } catch {\n existing = null;\n }\n if (existing) {\n if (!existing.isSymbolicLink()) {\n return;\n }\n try {\n // Both sides in one separator style: Windows hands back `\\\\` paths.\n if (resolve(from, await readlink(link)) === normalize(target)) {\n return;\n }\n } catch {\n // An unreadable link is replaced below.\n }\n await rm(link, { force: true });\n }\n try {\n await symlink(relative(from, target), link, \"dir\");\n } catch {\n await symlink(target, link, \"junction\");\n }\n};\n\n/** The package names a `node_modules` directory holds, scoped ones included. */\nconst packageNames = async (modulesDir: string): Promise<string[]> => {\n const entries = await readdir(modulesDir);\n const grouped = await Promise.all(\n entries\n .filter((entry) => !entry.startsWith(\".\"))\n .map(async (entry) => {\n if (!entry.startsWith(\"@\")) {\n return [entry];\n }\n const scoped = await readdir(join(modulesDir, entry));\n return scoped.map((name) => `${entry}/${name}`);\n })\n );\n return grouped.flat();\n};\n\n/**\n * Give a server-side build output its own `node_modules`: one link per package\n * its bundles import by bare name, each to the copy the importing module\n * resolves. A render bundle mixes code from Blume, Astro, and the adapters\n * into one set of chunks, and its bare imports resolve by walking up from the\n * chunk — so without these links the copy the package manager happened to\n * hoist wins, not the one the importer declared. A root `js-yaml@4` (pulled in\n * by the Netlify or Cloudflare adapter) then breaks Blume's `js-yaml@5`\n * import, a Yarn-hoisted `cookie@0.7` breaks Astro's `cookie@2` import, and\n * under pnpm the server bundle can't reach Blume's dependencies at all.\n *\n * Under an isolated linker the links also cover the rest of Blume's own\n * dependencies, which the upward walk can't reach from the output, so a\n * specifier the build never saw (loaded by a dependency at runtime) still\n * resolves as it did before.\n */\nexport const linkRenderDeps = async (\n dir: string,\n imported: ReadonlyMap<string, string>,\n pkgDir: string = packageRoot()\n): Promise<void> => {\n const modulesDir = join(dir, \"node_modules\");\n // An earlier release linked the whole directory here; per-package links\n // need a real directory in its place.\n try {\n const existing = await lstat(modulesDir);\n if (existing.isSymbolicLink()) {\n await rm(modulesDir, { force: true });\n }\n } catch {\n // Nothing there yet.\n }\n const links = new Map(imported);\n const depsDir = blumeDepsDir(pkgDir);\n if (\n depsDir &&\n !(basename(depsDir) === \"node_modules\" && isWithin(dirname(depsDir), dir))\n ) {\n for (const name of await packageNames(depsDir)) {\n if (!links.has(name)) {\n links.set(name, normalize(realpathSync(join(depsDir, name))));\n }\n }\n }\n await Promise.all(\n [...links].map(([name, target]) =>\n linkPackageDir(join(modulesDir, name), target)\n )\n );\n};\n\n/** The Vite environment a hook runs in (`client`, `ssr`, `prerender`). */\ninterface PluginEnvironment {\n name: string;\n}\n\n/** What `this.resolve` returns: the resolved id, and whether it's external. */\ninterface ResolvedModule {\n external?: boolean | \"absolute\" | \"relative\";\n id: string;\n}\n\n/** The `resolveId` hook options this plugin forwards to `this.resolve`. */\ninterface ResolveIdOptions {\n attributes?: Record<string, string>;\n isEntry?: boolean;\n kind?: string;\n}\n\n/** The slice of the plugin context the `resolveId` hook uses. */\ninterface ResolveContext {\n environment?: PluginEnvironment;\n resolve: (\n source: string,\n importer: string | undefined,\n options: ResolveIdOptions & { skipSelf: boolean }\n ) => Promise<ResolvedModule | null>;\n}\n\n/** The Vite plugin {@link prerenderDepsPlugin} returns. */\nexport interface PrerenderDepsPlugin {\n apply: \"build\";\n configResolved: (config: { root: string }) => void;\n enforce: \"pre\";\n name: string;\n resolveId: {\n filter: { id: RegExp };\n handler: (\n this: ResolveContext,\n source: string,\n importer: string | undefined,\n options: ResolveIdOptions\n ) => Promise<ResolvedModule | null>;\n };\n writeBundle: (\n this: { environment?: PluginEnvironment } | undefined,\n options: { dir?: string }\n ) => Promise<void>;\n}\n\n/**\n * A specifier that could name a package: not relative, absolute, virtual\n * (`\\0`), or carrying a scheme or drive letter (`node:`, `virtual:`, `C:`).\n */\nconst BARE_SPECIFIER = /^[^\\0./\\\\][^:]*$/u;\n\n/**\n * The directory a module id's imports resolve from, or undefined for an id\n * that isn't a file (a virtual module).\n */\nconst importerDir = (importer: string | undefined): string | undefined => {\n const [path = \"\"] = (importer ?? \"\").split(\"?\");\n return isAbsolute(path) ? dirname(path) : undefined;\n};\n\n/**\n * Vite plugin that makes a server-side build's externalized imports resolve\n * to the copies their importers declared, for the static prerender bundle\n * Astro imports to write the HTML and for a server build's output alike.\n *\n * While each server-side environment (`ssr`, `prerender`) builds, it records\n * every import Vite leaves external together with the package directory that\n * import resolves to from the module that makes it. Once the environment's\n * bundle is written, {@link linkRenderDeps} gives the output directory a\n * `node_modules` of links to exactly those directories. Astro deletes the\n * prerender output once generation finishes — `fs.rm` unlinks the links\n * without following them — while a server build ships its links, relative so\n * they survive the project moving as a whole, and a deploy adapter's file\n * trace follows them to the real files.\n *\n * Outside an environment-aware build (a direct call, as in the tests) only a\n * `.prerender` output is handled. The client build is never touched.\n */\nexport const prerenderDepsPlugin = (\n pkgDir: string = packageRoot()\n): PrerenderDepsPlugin => {\n const imported = new Map<string, Map<string, string>>();\n let root: string | undefined;\n\n const record = (\n environment: string,\n source: string,\n importer: string | undefined\n ): void => {\n const name = packageNameOf(source);\n const packages = imported.get(environment) ?? new Map<string, string>();\n imported.set(environment, packages);\n if (!name || packages.has(name)) {\n return;\n }\n const from = importerDir(importer) ?? root;\n const target = from === undefined ? null : packageDirFrom(name, from);\n if (target) {\n packages.set(name, target);\n }\n };\n\n return {\n apply: \"build\",\n configResolved: (config) => {\n ({ root } = config);\n },\n enforce: \"pre\",\n name: \"blume:prerender-deps\",\n resolveId: {\n filter: { id: BARE_SPECIFIER },\n async handler(source, importer, options) {\n const environment = this.environment?.name;\n if (environment === undefined || environment === \"client\") {\n return null;\n }\n const resolved = await this.resolve(source, importer, {\n ...options,\n skipSelf: true,\n });\n if (resolved?.external) {\n record(environment, source, importer);\n }\n return resolved;\n },\n },\n async writeBundle(options) {\n const environment = this?.environment?.name;\n const { dir } = options;\n const serverSide =\n environment === undefined\n ? dir !== undefined && basename(dir) === \".prerender\"\n : environment !== \"client\";\n if (!dir || !serverSide) {\n return;\n }\n await linkRenderDeps(\n dir,\n imported.get(environment ?? \"\") ?? new Map<string, string>(),\n pkgDir\n );\n },\n };\n};\n"
|
|
32
|
+
],
|
|
33
|
+
"mappings": "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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBA4PgB;AAAA,UACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,aAmBG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgGX,QAAQ,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBA8BJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAcE,+BAA+B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAiHvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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",
|
|
34
|
+
"debugId": "8216F00B3292FE6F64756E2164756E21",
|
|
35
|
+
"names": []
|
|
36
|
+
}
|