blume 2.0.1 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +82 -0
- package/dist/cli/{chunk-vtk4a6dg.js → chunk-00gs3wqs.js} +1 -1
- package/dist/cli/{chunk-fa25z98p.js → chunk-273ygyr4.js} +4 -3
- package/dist/cli/chunk-273ygyr4.js.map +11 -0
- package/dist/cli/{chunk-yw7dm696.js → chunk-3yce002v.js} +21 -8
- package/dist/cli/{chunk-yw7dm696.js.map → chunk-3yce002v.js.map} +3 -3
- package/dist/cli/{chunk-yt5n7ppj.js → chunk-4e9b9ra6.js} +17 -7
- package/dist/cli/chunk-4e9b9ra6.js.map +10 -0
- package/dist/cli/{chunk-fs23ddbb.js → chunk-5r8g91qn.js} +352 -676
- package/dist/cli/chunk-5r8g91qn.js.map +34 -0
- package/dist/cli/{chunk-6vm74dry.js → chunk-6dtt0zfn.js} +8 -8
- package/dist/cli/{chunk-qwsrynx5.js → chunk-6k4ftwze.js} +38 -19
- package/dist/cli/{chunk-qwsrynx5.js.map → chunk-6k4ftwze.js.map} +4 -4
- package/dist/cli/{chunk-qs4q5p4e.js → chunk-7mbqtmgb.js} +16 -7
- package/dist/cli/chunk-7mbqtmgb.js.map +10 -0
- package/dist/cli/{chunk-q5163e60.js → chunk-7vtckvaw.js} +21 -19
- package/dist/cli/chunk-7vtckvaw.js.map +11 -0
- package/dist/cli/{chunk-kdp5q7ke.js → chunk-8g8ytmgx.js} +17 -18
- package/dist/cli/{chunk-kdp5q7ke.js.map → chunk-8g8ytmgx.js.map} +2 -2
- package/dist/cli/{chunk-epjnccmv.js → chunk-91ws1n6j.js} +18 -15
- package/dist/cli/chunk-91ws1n6j.js.map +10 -0
- package/dist/cli/{chunk-fxypxtvm.js → chunk-bbnwccaz.js} +2 -2
- package/dist/cli/{chunk-m3vmjgmq.js → chunk-bfwp9vp6.js} +16 -8
- package/dist/cli/chunk-bfwp9vp6.js.map +10 -0
- package/dist/cli/{chunk-f2z5v128.js → chunk-d1v5rhy0.js} +14 -15
- package/dist/cli/{chunk-f2z5v128.js.map → chunk-d1v5rhy0.js.map} +2 -2
- package/dist/cli/{chunk-6crbhc3x.js → chunk-ddndchfr.js} +21 -6
- package/dist/cli/chunk-ddndchfr.js.map +14 -0
- package/dist/cli/{chunk-ce574jw2.js → chunk-esh98wmb.js} +1 -1
- package/dist/cli/{chunk-zxcczpyx.js → chunk-fsmrqk8a.js} +1 -1
- package/dist/cli/{chunk-hdpx1tax.js → chunk-g698a744.js} +5 -5
- package/dist/cli/{chunk-jts8mvcz.js → chunk-gs7r695n.js} +9 -3
- package/dist/cli/{chunk-jts8mvcz.js.map → chunk-gs7r695n.js.map} +3 -3
- package/dist/cli/{chunk-zxh4d9vy.js → chunk-h2ez8dzb.js} +4 -4
- package/dist/cli/{chunk-5shv93fd.js → chunk-h7k3nq3v.js} +2 -2
- package/dist/cli/{chunk-79jhk4py.js → chunk-hqp2ajnh.js} +251 -122
- package/dist/cli/chunk-hqp2ajnh.js.map +35 -0
- package/dist/cli/{chunk-2hn4b8z7.js → chunk-hr8ne106.js} +109 -40
- package/dist/cli/chunk-hr8ne106.js.map +13 -0
- package/dist/cli/{chunk-6hsn950k.js → chunk-j85scx15.js} +62 -18
- package/dist/cli/chunk-j85scx15.js.map +10 -0
- package/dist/cli/{chunk-s1p84fyh.js → chunk-k7pj68a8.js} +63 -22
- package/dist/cli/chunk-k7pj68a8.js.map +11 -0
- package/dist/cli/{chunk-ah61y8py.js → chunk-mqc662a6.js} +2 -2
- package/dist/cli/{chunk-ch6g3ar0.js → chunk-n1yg3tj3.js} +4 -4
- package/dist/cli/{chunk-mb2919y2.js → chunk-nfcyttvj.js} +17 -6
- package/dist/cli/chunk-nfcyttvj.js.map +10 -0
- package/dist/cli/{chunk-kpf8rrjc.js → chunk-pbg5a4s3.js} +56 -24
- package/dist/cli/chunk-pbg5a4s3.js.map +19 -0
- package/dist/cli/{chunk-jwyddg7y.js → chunk-ppzjqwx2.js} +21 -14
- package/dist/cli/{chunk-jwyddg7y.js.map → chunk-ppzjqwx2.js.map} +4 -4
- package/dist/cli/{chunk-dh8cwk36.js → chunk-qkb5a8sa.js} +24 -9
- package/dist/cli/chunk-qkb5a8sa.js.map +10 -0
- package/dist/cli/{chunk-wm7js3j9.js → chunk-sqw4ekg1.js} +2 -2
- package/dist/cli/{chunk-qkqwkpte.js → chunk-v6ya5kcb.js} +3083 -953
- package/dist/cli/chunk-v6ya5kcb.js.map +189 -0
- package/dist/cli/{chunk-fz5wtpmh.js → chunk-w4bxdvsa.js} +18 -15
- package/dist/cli/chunk-w4bxdvsa.js.map +10 -0
- package/dist/cli/{chunk-27g6wdth.js → chunk-wdrt2k2v.js} +2 -2
- package/dist/cli/{chunk-wgm7m9qk.js → chunk-xh43dwgw.js} +158 -43
- package/dist/cli/chunk-xh43dwgw.js.map +36 -0
- package/dist/cli/{chunk-s6jhgk0q.js → chunk-zp79m0ts.js} +2 -2
- package/dist/cli/index.js +160 -35
- package/dist/cli/index.js.map +4 -4
- package/dist/types/ai/agent-surface.d.ts +32 -0
- package/dist/types/ai/api-catalog.d.ts +7 -1
- package/dist/types/ai/ask-context.d.ts +7 -0
- package/dist/types/ai/component-markdown.d.ts +4 -4
- package/dist/types/ai/relative-links.d.ts +9 -4
- package/dist/types/ai/static-expression.d.ts +28 -0
- package/dist/types/analytics/databuddy.d.ts +43 -0
- package/dist/types/analytics/index.d.ts +2 -0
- package/dist/types/analytics/schema.d.ts +14 -0
- package/dist/types/cli/env.d.ts +5 -0
- package/dist/types/cli/init/scaffold.d.ts +19 -3
- package/dist/types/cli/init/starter-spec.d.ts +11 -0
- package/dist/types/core/base-path.d.ts +9 -0
- package/dist/types/core/config-input.d.ts +4 -4
- package/dist/types/core/config.d.ts +2 -2
- package/dist/types/core/graph.d.ts +2 -0
- package/dist/types/core/i18n-ui.d.ts +31 -0
- package/dist/types/core/i18n.d.ts +7 -1
- package/dist/types/core/links.d.ts +3 -1
- package/dist/types/core/load-module.d.ts +10 -0
- package/dist/types/core/locale-links.d.ts +12 -2
- package/dist/types/core/meta.d.ts +5 -1
- package/dist/types/core/nav-diagnostics.d.ts +10 -0
- package/dist/types/core/navigation.d.ts +38 -0
- package/dist/types/core/ordering-prefix.d.ts +4 -0
- package/dist/types/core/safe-links.d.ts +3 -1
- package/dist/types/core/schema.d.ts +52 -13
- package/dist/types/core/sources/github-releases.d.ts +5 -0
- package/dist/types/core/sources/lower.d.ts +14 -2
- package/dist/types/core/sources/normalize.d.ts +10 -1
- package/dist/types/core/sources/remote.d.ts +11 -1
- package/dist/types/core/sources/resolve.d.ts +12 -0
- package/dist/types/core/sources/types.d.ts +31 -0
- package/dist/types/core/types.d.ts +9 -0
- package/dist/types/deploy/adapters/node.d.ts +5 -2
- package/dist/types/deploy/adapters/types.d.ts +7 -0
- package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
- package/dist/types/deploy/headers.d.ts +31 -7
- package/dist/types/deploy/node-headers.d.ts +43 -8
- package/dist/types/deploy/platforms/netlify.d.ts +27 -2
- package/dist/types/deploy/platforms/node.d.ts +6 -5
- package/dist/types/deploy/platforms/types.d.ts +7 -0
- package/dist/types/deploy/platforms/vercel.d.ts +3 -2
- package/dist/types/deploy/redirects.d.ts +7 -2
- package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
- package/dist/types/openapi/model.d.ts +20 -6
- package/dist/types/search/sync/algolia.d.ts +3 -1
- package/docs/01-quickstart.mdx +3 -2
- package/docs/02-deployment.mdx +17 -8
- package/docs/08-faq.mdx +9 -2
- package/docs/advanced/changelog.mdx +1 -1
- package/docs/advanced/custom-pages.mdx +4 -2
- package/docs/cli/audit.mdx +2 -2
- package/docs/cli/doctor.mdx +2 -2
- package/docs/cli/evals.mdx +2 -2
- package/docs/cli/index.mdx +4 -1
- package/docs/cli/translate.mdx +1 -1
- package/docs/configuration/analytics.mdx +23 -2
- package/docs/configuration/assistant.mdx +1 -1
- package/docs/configuration/customization.mdx +4 -3
- package/docs/configuration/index.mdx +5 -3
- package/docs/configuration/search.mdx +1 -1
- package/docs/content/components.mdx +1 -1
- package/docs/content/frontmatter.mdx +5 -1
- package/docs/content/i18n.mdx +1 -1
- package/docs/content/index.mdx +4 -2
- package/docs/content/meta.mdx +5 -3
- package/docs/content/navigation.mdx +29 -4
- package/docs/content/sources.mdx +18 -12
- package/docs/content/versioning.mdx +1 -0
- package/docs/discoverability/agent-discovery.mdx +21 -7
- package/docs/discoverability/index.mdx +2 -2
- package/docs/discoverability/llms-txt.mdx +2 -5
- package/docs/discoverability/markdown.mdx +5 -3
- package/docs/discoverability/mcp.mdx +4 -0
- package/docs/discoverability/metadata.mdx +3 -2
- package/docs/discoverability/open-graph.mdx +6 -4
- package/docs/discoverability/rss.mdx +3 -1
- package/docs/references/asyncapi.mdx +1 -1
- package/docs/references/graphql.mdx +2 -2
- package/docs/references/openapi.mdx +3 -3
- package/package.json +1 -1
- package/skills/blume-migrate/SKILL.md +6 -6
- package/skills/blume-migrate/references/docusaurus.md +6 -6
- package/skills/blume-migrate/references/mintlify.md +3 -3
- package/skills/blume-migrate/references/monorepo.md +1 -1
- package/skills/blume-migrate/references/nextra.md +1 -1
- package/skills/blume-migrate/references/starlight.md +5 -5
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +2 -2
- package/skills/blume-update-docs/references/audit-checklist.md +1 -1
- package/src/ai/agent-surface.ts +56 -0
- package/src/ai/api/spec.ts +15 -2
- package/src/ai/api-catalog.ts +7 -1
- package/src/ai/ask-context.ts +14 -2
- package/src/ai/ask-data.ts +26 -12
- package/src/ai/component-markdown.ts +43 -37
- package/src/ai/link-headers.ts +8 -3
- package/src/ai/llms.ts +4 -3
- package/src/ai/markdown.ts +23 -11
- package/src/ai/mcp/server.ts +78 -7
- package/src/ai/relative-links.ts +78 -10
- package/src/ai/static-expression.ts +416 -0
- package/src/ai/visibility.ts +45 -14
- package/src/analytics/databuddy.ts +67 -0
- package/src/analytics/head.ts +4 -0
- package/src/analytics/index.ts +2 -0
- package/src/analytics/posthog.ts +21 -3
- package/src/analytics/schema.ts +2 -0
- package/src/astro/generate.ts +71 -27
- package/src/astro/templates.ts +61 -23
- package/src/audit/catalog.ts +2 -2
- package/src/audit/checks/assets.ts +25 -5
- package/src/audit/checks/content.ts +20 -2
- package/src/audit/checks/i18n.ts +46 -8
- package/src/audit/checks/indexability.ts +39 -19
- package/src/audit/checks/links.ts +14 -0
- package/src/audit/checks/llms.ts +6 -3
- package/src/audit/checks/network.ts +3 -1
- package/src/audit/checks/og-image.ts +10 -0
- package/src/audit/checks/robots.ts +6 -1
- package/src/audit/checks/sitemap.ts +56 -31
- package/src/audit/checks/social.ts +21 -2
- package/src/audit/crawl.ts +88 -15
- package/src/audit/report.ts +54 -17
- package/src/audit/run.ts +1 -0
- package/src/audit/types.ts +18 -0
- package/src/audit/url.ts +37 -6
- package/src/cli/build-failure.ts +50 -0
- package/src/cli/commands/audit.ts +7 -0
- package/src/cli/commands/build.ts +19 -5
- package/src/cli/commands/check.ts +2 -0
- package/src/cli/commands/eval.ts +1 -7
- package/src/cli/commands/init.ts +15 -5
- package/src/cli/commands/preview.ts +15 -0
- package/src/cli/commands/sync.ts +2 -0
- package/src/cli/commands/translate.ts +9 -6
- package/src/cli/commands/upgrade.ts +11 -0
- package/src/cli/eject-scripts.ts +32 -7
- package/src/cli/env.ts +12 -1
- package/src/cli/init/scaffold.ts +117 -11
- package/src/cli/init/starter-spec.ts +235 -0
- package/src/components/content/AccordionItem.astro +26 -22
- package/src/components/content/Badge.astro +2 -9
- package/src/components/content/Card.astro +2 -2
- package/src/components/content/Component.astro +9 -3
- package/src/components/content/Expandable.astro +5 -1
- package/src/components/content/Frame.astro +2 -7
- package/src/components/content/GithubInfo.astro +12 -2
- package/src/components/content/Prompt.astro +2 -7
- package/src/components/content/Tabs.astro +54 -5
- package/src/components/content/Tile.astro +1 -1
- package/src/components/content/Tooltip.astro +69 -7
- package/src/components/content/Tree.astro +7 -2
- package/src/components/content/TypeTable.astro +10 -5
- package/src/components/content/Update.astro +8 -2
- package/src/components/content/auto-type-table.ts +4 -1
- package/src/components/content/badge-color.ts +17 -0
- package/src/components/content/base-href.ts +18 -3
- package/src/components/content/inline-markdown.ts +27 -7
- package/src/components/copy-feedback.ts +35 -8
- package/src/components/islands/assistant.tsx +16 -2
- package/src/components/islands/hooks.ts +7 -2
- package/src/components/islands/webmcp.ts +12 -8
- package/src/components/layout/Banner.astro +23 -4
- package/src/components/layout/Header.astro +22 -9
- package/src/components/layout/Logo.astro +5 -0
- package/src/components/layout/NavSelector.astro +6 -1
- package/src/components/layout/NavTabMenu.astro +133 -0
- package/src/components/layout/NavTree.astro +15 -6
- package/src/components/layout/NavTreeCache.astro +5 -2
- package/src/components/layout/NavTreeScript.astro +45 -5
- package/src/components/layout/PageLayout.astro +22 -8
- package/src/components/layout/ReferenceLayout.astro +4 -1
- package/src/components/layout/RootLayout.astro +21 -8
- package/src/components/layout/Search.astro +5 -1
- package/src/components/layout/analytics-client.ts +2 -0
- package/src/components/openapi/GraphqlType.astro +11 -3
- package/src/components/openapi/MessageComposer.astro +1 -1
- package/src/components/openapi/Operation.astro +21 -5
- package/src/components/openapi/PanelTabs.astro +4 -1
- package/src/components/openapi/Playground.astro +8 -2
- package/src/components/openapi/RequestPanel.astro +9 -5
- package/src/components/openapi/SchemaProperty.astro +11 -33
- package/src/components/openapi/SchemaTable.astro +27 -52
- package/src/components/openapi/helpers.ts +90 -12
- package/src/components/openapi/message-composer.ts +8 -0
- package/src/components/openapi/message-model.ts +12 -2
- package/src/components/openapi/message.ts +4 -1
- package/src/components/openapi/operation-model.ts +40 -8
- package/src/components/openapi/panel.ts +29 -5
- package/src/components/openapi/playground-client.ts +37 -7
- package/src/components/openapi/playground-schema.ts +25 -6
- package/src/components/openapi/request.ts +2 -0
- package/src/components/openapi/schema-tree.ts +203 -0
- package/src/components/openapi/snippets.ts +30 -15
- package/src/components/openapi/validate-json.ts +1 -1
- package/src/core/base-path.ts +15 -0
- package/src/core/config-input.ts +4 -4
- package/src/core/config.ts +18 -4
- package/src/core/diagnostics.ts +217 -30
- package/src/core/graph.ts +120 -11
- package/src/core/i18n-ui.ts +39 -2
- package/src/core/i18n.ts +13 -2
- package/src/core/links.ts +7 -4
- package/src/core/load-module.ts +20 -0
- package/src/core/locale-links.ts +17 -17
- package/src/core/manifest.ts +3 -2
- package/src/core/meta.ts +69 -6
- package/src/core/nav-diagnostics.ts +56 -1
- package/src/core/navigation.ts +245 -76
- package/src/core/ordering-prefix.ts +27 -0
- package/src/core/project-graph.ts +23 -1
- package/src/core/safe-href.ts +53 -1
- package/src/core/safe-links.ts +11 -2
- package/src/core/schema.ts +70 -14
- package/src/core/sources/assets.ts +83 -41
- package/src/core/sources/contentful-rich-text.ts +3 -2
- package/src/core/sources/contentful.ts +25 -12
- package/src/core/sources/filesystem.ts +5 -1
- package/src/core/sources/github-releases.ts +106 -5
- package/src/core/sources/lexical.ts +9 -4
- package/src/core/sources/lower.ts +79 -26
- package/src/core/sources/mdx-remote.ts +1 -0
- package/src/core/sources/normalize.ts +132 -30
- package/src/core/sources/notion.ts +26 -10
- package/src/core/sources/obsidian.ts +17 -3
- package/src/core/sources/payload.ts +1 -0
- package/src/core/sources/portable-text.ts +63 -44
- package/src/core/sources/remote.ts +18 -2
- package/src/core/sources/resolve.ts +52 -33
- package/src/core/sources/sanity.ts +1 -0
- package/src/core/sources/strapi-blocks.ts +9 -4
- package/src/core/sources/strapi.ts +1 -0
- package/src/core/sources/types.ts +31 -0
- package/src/core/types.ts +9 -0
- package/src/core/ui-packs/ar.ts +13 -0
- package/src/core/ui-packs/bg.ts +13 -0
- package/src/core/ui-packs/bn.ts +13 -0
- package/src/core/ui-packs/ca.ts +13 -0
- package/src/core/ui-packs/cs.ts +13 -0
- package/src/core/ui-packs/da.ts +13 -0
- package/src/core/ui-packs/de.ts +13 -0
- package/src/core/ui-packs/el.ts +13 -0
- package/src/core/ui-packs/es.ts +13 -0
- package/src/core/ui-packs/fa.ts +13 -0
- package/src/core/ui-packs/fi.ts +13 -0
- package/src/core/ui-packs/fr.ts +13 -0
- package/src/core/ui-packs/he.ts +13 -0
- package/src/core/ui-packs/hi.ts +13 -0
- package/src/core/ui-packs/hr.ts +13 -0
- package/src/core/ui-packs/hu.ts +13 -0
- package/src/core/ui-packs/id.ts +13 -0
- package/src/core/ui-packs/it.ts +13 -0
- package/src/core/ui-packs/ja.ts +13 -0
- package/src/core/ui-packs/ko.ts +13 -0
- package/src/core/ui-packs/nl.ts +13 -0
- package/src/core/ui-packs/no.ts +13 -0
- package/src/core/ui-packs/pl.ts +13 -0
- package/src/core/ui-packs/pt-br.ts +13 -0
- package/src/core/ui-packs/pt.ts +13 -0
- package/src/core/ui-packs/ro.ts +13 -0
- package/src/core/ui-packs/ru.ts +13 -0
- package/src/core/ui-packs/sk.ts +13 -0
- package/src/core/ui-packs/sr.ts +13 -0
- package/src/core/ui-packs/sv.ts +13 -0
- package/src/core/ui-packs/th.ts +13 -0
- package/src/core/ui-packs/tr.ts +13 -0
- package/src/core/ui-packs/uk.ts +13 -0
- package/src/core/ui-packs/vi.ts +13 -0
- package/src/core/ui-packs/zh-tw.ts +13 -0
- package/src/core/ui-packs/zh.ts +13 -0
- package/src/core/version-cut.ts +17 -2
- package/src/core/versions.ts +4 -1
- package/src/deploy/adapters/node.ts +5 -2
- package/src/deploy/adapters/registry.ts +2 -1
- package/src/deploy/adapters/types.ts +13 -1
- package/src/deploy/artifacts.ts +25 -5
- package/src/deploy/cloudflare-negotiation.ts +23 -4
- package/src/deploy/headers.ts +67 -51
- package/src/deploy/node-headers.ts +148 -27
- package/src/deploy/platforms/cloudflare.ts +1 -0
- package/src/deploy/platforms/netlify.ts +82 -5
- package/src/deploy/platforms/node.ts +7 -5
- package/src/deploy/platforms/static.ts +1 -0
- package/src/deploy/platforms/types.ts +7 -0
- package/src/deploy/platforms/vercel.ts +9 -3
- package/src/deploy/redirects.ts +14 -3
- package/src/deploy/vercel-negotiation.ts +35 -3
- package/src/eval/agents.ts +10 -2
- package/src/eval/run.ts +25 -0
- package/src/markdown/base-links.ts +55 -8
- package/src/markdown/index.ts +6 -3
- package/src/markdown/relative-links.ts +3 -23
- package/src/markdown/route-snapshot.ts +37 -0
- package/src/og/card.ts +20 -4
- package/src/og/derive.ts +145 -4
- package/src/openapi/graphql-build.ts +28 -2
- package/src/openapi/model.ts +77 -13
- package/src/openapi/render-mdx.ts +10 -1
- package/src/registry/eject.ts +119 -22
- package/src/search/documents.ts +22 -1
- package/src/search/sync/algolia.ts +36 -2
- package/src/sources/registry.ts +5 -0
- package/src/theme/entry.ts +11 -4
- package/src/translate/agents.ts +6 -1
- package/src/translate/ledger.ts +26 -3
- package/src/translate/meta.ts +11 -3
- package/src/translate/run.ts +11 -5
- package/src/translate/validate.ts +10 -1
- package/src/translate/work-list.ts +36 -3
- package/src/upgrade/upgrade.ts +36 -4
- package/dist/cli/chunk-2hn4b8z7.js.map +0 -12
- package/dist/cli/chunk-6crbhc3x.js.map +0 -14
- package/dist/cli/chunk-6hsn950k.js.map +0 -10
- package/dist/cli/chunk-79jhk4py.js.map +0 -35
- package/dist/cli/chunk-82bbrxdn.js +0 -51
- package/dist/cli/chunk-82bbrxdn.js.map +0 -10
- package/dist/cli/chunk-abh8yjkn.js +0 -31
- package/dist/cli/chunk-abh8yjkn.js.map +0 -10
- package/dist/cli/chunk-dh8cwk36.js.map +0 -10
- package/dist/cli/chunk-epjnccmv.js.map +0 -10
- package/dist/cli/chunk-fa25z98p.js.map +0 -11
- package/dist/cli/chunk-fs23ddbb.js.map +0 -35
- package/dist/cli/chunk-fz5wtpmh.js.map +0 -10
- package/dist/cli/chunk-kpf8rrjc.js.map +0 -19
- package/dist/cli/chunk-m3vmjgmq.js.map +0 -10
- package/dist/cli/chunk-mb2919y2.js.map +0 -10
- package/dist/cli/chunk-q5163e60.js.map +0 -11
- package/dist/cli/chunk-qkqwkpte.js.map +0 -182
- package/dist/cli/chunk-qs4q5p4e.js.map +0 -10
- package/dist/cli/chunk-s1p84fyh.js.map +0 -10
- package/dist/cli/chunk-wgm7m9qk.js.map +0 -36
- package/dist/cli/chunk-yt5n7ppj.js.map +0 -10
- /package/dist/cli/{chunk-vtk4a6dg.js.map → chunk-00gs3wqs.js.map} +0 -0
- /package/dist/cli/{chunk-6vm74dry.js.map → chunk-6dtt0zfn.js.map} +0 -0
- /package/dist/cli/{chunk-fxypxtvm.js.map → chunk-bbnwccaz.js.map} +0 -0
- /package/dist/cli/{chunk-ce574jw2.js.map → chunk-esh98wmb.js.map} +0 -0
- /package/dist/cli/{chunk-zxcczpyx.js.map → chunk-fsmrqk8a.js.map} +0 -0
- /package/dist/cli/{chunk-hdpx1tax.js.map → chunk-g698a744.js.map} +0 -0
- /package/dist/cli/{chunk-zxh4d9vy.js.map → chunk-h2ez8dzb.js.map} +0 -0
- /package/dist/cli/{chunk-5shv93fd.js.map → chunk-h7k3nq3v.js.map} +0 -0
- /package/dist/cli/{chunk-ah61y8py.js.map → chunk-mqc662a6.js.map} +0 -0
- /package/dist/cli/{chunk-ch6g3ar0.js.map → chunk-n1yg3tj3.js.map} +0 -0
- /package/dist/cli/{chunk-wm7js3j9.js.map → chunk-sqw4ekg1.js.map} +0 -0
- /package/dist/cli/{chunk-27g6wdth.js.map → chunk-wdrt2k2v.js.map} +0 -0
- /package/dist/cli/{chunk-s6jhgk0q.js.map → chunk-zp79m0ts.js.map} +0 -0
package/src/ai/visibility.ts
CHANGED
|
@@ -1,10 +1,44 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { nextFenceState } from "../core/code-fences.ts";
|
|
2
|
+
import type { FenceState } from "../core/code-fences.ts";
|
|
3
|
+
|
|
4
|
+
/** An inline code span on one line, as the other source scanners read it. */
|
|
5
|
+
const INLINE_CODE = /`[^`\n]*`/gu;
|
|
5
6
|
// NUL delimiters cannot appear in authored markdown, so tokens never collide.
|
|
6
7
|
// oxlint-disable-next-line no-control-regex -- the NUL is the collision guard.
|
|
7
|
-
const
|
|
8
|
+
const CODE_TOKEN = /\u0000blume-code-(?<index>\d+)\u0000/gu;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Swap every code sample for a one-line token — fenced blocks at any indent
|
|
12
|
+
* (a fence inside a `<Tab>` or `<Step>` is indented under it) and inline code
|
|
13
|
+
* spans — so markup that *shows* `<Visibility>` keeps showing what the author
|
|
14
|
+
* wrote. An unclosed fence runs to the end, as CommonMark reads it.
|
|
15
|
+
*/
|
|
16
|
+
const maskCode = (markdown: string, stash: string[]): string => {
|
|
17
|
+
const token = (code: string): string => {
|
|
18
|
+
stash.push(code);
|
|
19
|
+
return `\u0000blume-code-${stash.length - 1}\u0000`;
|
|
20
|
+
};
|
|
21
|
+
const lines: string[] = [];
|
|
22
|
+
let fence: FenceState = null;
|
|
23
|
+
let block: string[] = [];
|
|
24
|
+
for (const line of markdown.split("\n")) {
|
|
25
|
+
const next = nextFenceState(line, fence);
|
|
26
|
+
if (fence === null && next === null) {
|
|
27
|
+
lines.push(line.replaceAll(INLINE_CODE, token));
|
|
28
|
+
} else {
|
|
29
|
+
block.push(line);
|
|
30
|
+
if (next === null) {
|
|
31
|
+
lines.push(token(block.join("\n")));
|
|
32
|
+
block = [];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
fence = next;
|
|
36
|
+
}
|
|
37
|
+
if (block.length > 0) {
|
|
38
|
+
lines.push(token(block.join("\n")));
|
|
39
|
+
}
|
|
40
|
+
return lines.join("\n");
|
|
41
|
+
};
|
|
8
42
|
|
|
9
43
|
/** The audience an output surface serves — the component's two `for` values. */
|
|
10
44
|
export type VisibilityAudience = "agents" | "web";
|
|
@@ -36,12 +70,9 @@ export const applyAudienceVisibility = (
|
|
|
36
70
|
markdown: string,
|
|
37
71
|
audience: VisibilityAudience
|
|
38
72
|
): string => {
|
|
39
|
-
// Mask
|
|
40
|
-
const
|
|
41
|
-
const masked = markdown
|
|
42
|
-
fences.push(block);
|
|
43
|
-
return `\u0000blume-fence-${fences.length - 1}\u0000`;
|
|
44
|
-
});
|
|
73
|
+
// Mask code so documentation *about* Visibility survives.
|
|
74
|
+
const code: string[] = [];
|
|
75
|
+
const masked = maskCode(markdown, code);
|
|
45
76
|
|
|
46
77
|
let touched = false;
|
|
47
78
|
const filtered = masked
|
|
@@ -56,12 +87,12 @@ export const applyAudienceVisibility = (
|
|
|
56
87
|
|
|
57
88
|
// Removing/unwrapping block-level tags leaves runs of blank lines behind;
|
|
58
89
|
// collapse them only when something matched so untouched files round-trip
|
|
59
|
-
// exactly.
|
|
90
|
+
// exactly. Code is masked as single-line tokens, so it is unaffected.
|
|
60
91
|
const tidied = touched ? filtered.replaceAll(/\n{3,}/gu, "\n\n") : filtered;
|
|
61
92
|
|
|
62
93
|
return tidied.replaceAll(
|
|
63
|
-
|
|
64
|
-
(token, index) =>
|
|
94
|
+
CODE_TOKEN,
|
|
95
|
+
(token, index) => code[Number(index)] ?? token
|
|
65
96
|
);
|
|
66
97
|
};
|
|
67
98
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import type { AdapterDescriptor } from "../core/adapter.ts";
|
|
4
|
+
import { adapterDescriptorSchema } from "../core/adapter.ts";
|
|
5
|
+
import type { HeadScript } from "./head.ts";
|
|
6
|
+
|
|
7
|
+
/** The script Databuddy's install snippet loads. */
|
|
8
|
+
export const DATABUDDY_SCRIPT_SRC = "https://cdn.databuddy.cc/databuddy.js";
|
|
9
|
+
|
|
10
|
+
/** The options {@link databuddy} maps itself. */
|
|
11
|
+
export interface DatabuddyNamedOptions {
|
|
12
|
+
/** Client ID, from the website's settings in the Databuddy dashboard. */
|
|
13
|
+
clientId: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Options for {@link databuddy}: the client ID plus any other tracker setting,
|
|
18
|
+
* forwarded as a `data-` attribute on the tag (`"track-web-vitals"` →
|
|
19
|
+
* `data-track-web-vitals`, `"track-errors"`, `"track-outgoing-links"`,
|
|
20
|
+
* `"api-url"`, …).
|
|
21
|
+
*/
|
|
22
|
+
export type DatabuddyOptions = DatabuddyNamedOptions & {
|
|
23
|
+
[setting: string]: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const databuddyOptionsSchema = z
|
|
27
|
+
.object({
|
|
28
|
+
clientId: z.string().min(1),
|
|
29
|
+
})
|
|
30
|
+
.catchall(z.string());
|
|
31
|
+
|
|
32
|
+
export type DatabuddyAdapter = AdapterDescriptor<"databuddy", DatabuddyOptions>;
|
|
33
|
+
|
|
34
|
+
export const databuddyAdapterSchema = adapterDescriptorSchema(
|
|
35
|
+
"databuddy",
|
|
36
|
+
databuddyOptionsSchema
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Databuddy. The client ID is public — it's in the tag on every page.
|
|
41
|
+
*/
|
|
42
|
+
export const databuddy = (options: DatabuddyOptions): DatabuddyAdapter => ({
|
|
43
|
+
kind: "databuddy",
|
|
44
|
+
options,
|
|
45
|
+
requiredSecrets: [],
|
|
46
|
+
runtimeDeps: [],
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The async tag from the install instructions, with `clientId` as
|
|
51
|
+
* `data-client-id` and every other option as its own `data-` attribute. The
|
|
52
|
+
* tracker skips setup when `window.databuddy` already exists, so there is no
|
|
53
|
+
* queue stub. Databuddy's script tracks history changes on its own, so
|
|
54
|
+
* client-router navigations need no extra hook.
|
|
55
|
+
*/
|
|
56
|
+
export const databuddyHead = (options: DatabuddyOptions): HeadScript[] => {
|
|
57
|
+
const { clientId, ...settings } = options;
|
|
58
|
+
const attributes: HeadScript["attributes"] = {};
|
|
59
|
+
for (const [setting, value] of Object.entries(settings)) {
|
|
60
|
+
attributes[`data-${setting}`] = value;
|
|
61
|
+
}
|
|
62
|
+
attributes["data-client-id"] = clientId;
|
|
63
|
+
attributes.async = true;
|
|
64
|
+
attributes.crossorigin = "anonymous";
|
|
65
|
+
attributes.src = DATABUDDY_SCRIPT_SRC;
|
|
66
|
+
return [{ attributes, content: null }];
|
|
67
|
+
};
|
package/src/analytics/head.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { amplitudeHead } from "./amplitude.ts";
|
|
|
3
3
|
import { clarityHead } from "./clarity.ts";
|
|
4
4
|
import { clearbitHead } from "./clearbit.ts";
|
|
5
5
|
import { cloudflareHead } from "./cloudflare.ts";
|
|
6
|
+
import { databuddyHead } from "./databuddy.ts";
|
|
6
7
|
import { fathomHead } from "./fathom.ts";
|
|
7
8
|
import { googleAnalyticsHead } from "./google-analytics.ts";
|
|
8
9
|
import { googleTagManagerHead } from "./google-tag-manager.ts";
|
|
@@ -64,6 +65,9 @@ const adapterScripts = (
|
|
|
64
65
|
case "cloudflare": {
|
|
65
66
|
return cloudflareHead(adapter.options);
|
|
66
67
|
}
|
|
68
|
+
case "databuddy": {
|
|
69
|
+
return databuddyHead(adapter.options);
|
|
70
|
+
}
|
|
67
71
|
case "fathom": {
|
|
68
72
|
return fathomHead(adapter.options);
|
|
69
73
|
}
|
package/src/analytics/index.ts
CHANGED
|
@@ -30,6 +30,8 @@ export { clearbit } from "./clearbit.ts";
|
|
|
30
30
|
export type { ClearbitAdapter, ClearbitOptions } from "./clearbit.ts";
|
|
31
31
|
export { cloudflare } from "./cloudflare.ts";
|
|
32
32
|
export type { CloudflareAdapter, CloudflareOptions } from "./cloudflare.ts";
|
|
33
|
+
export { databuddy } from "./databuddy.ts";
|
|
34
|
+
export type { DatabuddyAdapter, DatabuddyOptions } from "./databuddy.ts";
|
|
33
35
|
export { fathom } from "./fathom.ts";
|
|
34
36
|
export type { FathomAdapter, FathomOptions } from "./fathom.ts";
|
|
35
37
|
export { googleAnalytics } from "./google-analytics.ts";
|
package/src/analytics/posthog.ts
CHANGED
|
@@ -54,13 +54,30 @@ export const posthog = (options: PosthogOptions): PosthogAdapter => ({
|
|
|
54
54
|
const POSTHOG_LOADER =
|
|
55
55
|
'!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.crossOrigin="anonymous",p.async=!0,p.src=s.api_host.replace(".i.posthog.com","-assets.i.posthog.com")+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="init capture register register_once register_for_session unregister unregister_for_session getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey getNextSurveyStep identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty createPersonProfile opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing debug getPageViewId captureTraceFeedback captureTraceMetric".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);';
|
|
56
56
|
|
|
57
|
-
// PostHog's
|
|
58
|
-
// router's in-place swaps are captured here.
|
|
57
|
+
// With PostHog's classic pageview setting its loader captures one pageview
|
|
58
|
+
// per real page load, so the client router's in-place swaps are captured here.
|
|
59
59
|
const POSTHOG_SPA_PAGEVIEWS = spaPageviews(
|
|
60
60
|
"__blumePosthogPath",
|
|
61
61
|
'posthog.capture("$pageview");'
|
|
62
62
|
);
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Whether Blume should capture client-router navigations itself, given the
|
|
66
|
+
* `posthog.init` options. Only when PostHog captures load-time pageviews and
|
|
67
|
+
* nothing more: `capture_pageview: false` turns pageviews off, and
|
|
68
|
+
* `capture_pageview: "history_change"` — the default under any `defaults`
|
|
69
|
+
* snapshot (`defaults: "2025-05-24"` in PostHog's current snippet), unless
|
|
70
|
+
* `capture_pageview` says otherwise — already captures every navigation, so
|
|
71
|
+
* sending another would count each one twice.
|
|
72
|
+
*/
|
|
73
|
+
const capturesNavigations = (init: Record<string, JsonValue>): boolean => {
|
|
74
|
+
const setting = init.capture_pageview;
|
|
75
|
+
if (setting === undefined) {
|
|
76
|
+
return init.defaults === undefined || init.defaults === "unset";
|
|
77
|
+
}
|
|
78
|
+
return setting !== false && setting !== "history_change";
|
|
79
|
+
};
|
|
80
|
+
|
|
64
81
|
/**
|
|
65
82
|
* The inline loader-plus-init snippet. `key` and `host` are the options Blume
|
|
66
83
|
* maps (`host` becomes `api_host`); everything else lands in `posthog.init`'s
|
|
@@ -69,10 +86,11 @@ const POSTHOG_SPA_PAGEVIEWS = spaPageviews(
|
|
|
69
86
|
export const posthogHead = (options: PosthogOptions): HeadScript[] => {
|
|
70
87
|
const { host, key, ...init } = options;
|
|
71
88
|
const initOptions = { api_host: host ?? POSTHOG_DEFAULT_HOST, ...init };
|
|
89
|
+
const pageviews = capturesNavigations(init) ? POSTHOG_SPA_PAGEVIEWS : "";
|
|
72
90
|
return [
|
|
73
91
|
{
|
|
74
92
|
attributes: {},
|
|
75
|
-
content: `${POSTHOG_LOADER}posthog.init(${inlineJson(key)},${inlineJson(initOptions)});${
|
|
93
|
+
content: `${POSTHOG_LOADER}posthog.init(${inlineJson(key)},${inlineJson(initOptions)});${pageviews}`,
|
|
76
94
|
},
|
|
77
95
|
];
|
|
78
96
|
};
|
package/src/analytics/schema.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { amplitudeAdapterSchema } from "./amplitude.ts";
|
|
|
5
5
|
import { clarityAdapterSchema } from "./clarity.ts";
|
|
6
6
|
import { clearbitAdapterSchema } from "./clearbit.ts";
|
|
7
7
|
import { cloudflareAdapterSchema } from "./cloudflare.ts";
|
|
8
|
+
import { databuddyAdapterSchema } from "./databuddy.ts";
|
|
8
9
|
import { fathomAdapterSchema } from "./fathom.ts";
|
|
9
10
|
import { googleAnalyticsAdapterSchema } from "./google-analytics.ts";
|
|
10
11
|
import { googleTagManagerAdapterSchema } from "./google-tag-manager.ts";
|
|
@@ -27,6 +28,7 @@ export const analyticsAdapterSchema = z.discriminatedUnion("kind", [
|
|
|
27
28
|
clarityAdapterSchema,
|
|
28
29
|
clearbitAdapterSchema,
|
|
29
30
|
cloudflareAdapterSchema,
|
|
31
|
+
databuddyAdapterSchema,
|
|
30
32
|
fathomAdapterSchema,
|
|
31
33
|
googleAnalyticsAdapterSchema,
|
|
32
34
|
googleTagManagerAdapterSchema,
|
package/src/astro/generate.ts
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
hasDeferrableGroups,
|
|
32
32
|
navVariants,
|
|
33
33
|
} from "../components/layout/nav-utils.ts";
|
|
34
|
-
import { normalizeBasePath } from "../core/base-path.ts";
|
|
34
|
+
import { normalizeBasePath, withBasePath } from "../core/base-path.ts";
|
|
35
35
|
import { validateUsedComponents } from "../core/component-diagnostics.ts";
|
|
36
36
|
import {
|
|
37
37
|
analyzeComponentOverrides,
|
|
@@ -81,7 +81,9 @@ import { ogCacheDir } from "../og/cache.ts";
|
|
|
81
81
|
import { missingFontFiles, resolveOgFonts } from "../og/derive.ts";
|
|
82
82
|
import type { DerivedOgFonts } from "../og/derive.ts";
|
|
83
83
|
import { resolveOgLogo } from "../og/logo.ts";
|
|
84
|
-
import type {
|
|
84
|
+
import type { AsyncApiSpecValue } from "../openapi/asyncapi.ts";
|
|
85
|
+
import type { ApiSpecData, HttpMethod, OpenApiData } from "../openapi/model.ts";
|
|
86
|
+
import { HTTP_METHODS, withServerDefaults } from "../openapi/model.ts";
|
|
85
87
|
import {
|
|
86
88
|
builtinProxyReferences,
|
|
87
89
|
hasScalarReferences,
|
|
@@ -954,6 +956,8 @@ const resolveBanner = (config: ResolvedConfig): BlumeBanner | null => {
|
|
|
954
956
|
if (isStringShorthand(banner)) {
|
|
955
957
|
return { content: banner, dismissible: false, key: banner };
|
|
956
958
|
}
|
|
959
|
+
// `link.href` stays as authored; the link the banner renders is the
|
|
960
|
+
// navigation's `bannerHref`, localized and mounted under `basePath`.
|
|
957
961
|
return {
|
|
958
962
|
content: banner.content,
|
|
959
963
|
dismissible: banner.dismissible,
|
|
@@ -1489,28 +1493,41 @@ const writeApiFiles = async (
|
|
|
1489
1493
|
await Promise.all(writes);
|
|
1490
1494
|
};
|
|
1491
1495
|
|
|
1496
|
+
/** The proxy endpoint's file under the Astro `src/` dir (eject writes it too). */
|
|
1497
|
+
export const PLAYGROUND_PROXY_ENTRY = join("blume-openapi", "api-proxy.ts");
|
|
1498
|
+
|
|
1499
|
+
/** The URL the built-in proxy is injected at (see {@link planPlaygroundProxy}). */
|
|
1500
|
+
export const playgroundProxyPattern = (config: ResolvedConfig): string =>
|
|
1501
|
+
withBasePath(config.basePath, "/_api-proxy");
|
|
1502
|
+
|
|
1492
1503
|
/**
|
|
1493
1504
|
* Decide whether to generate the playground's built-in CORS proxy endpoint.
|
|
1494
1505
|
* Only the Blume renderer's playground with `proxy: true` needs it — a proxy
|
|
1495
1506
|
* URL string points at an external service, and `false` sends requests
|
|
1496
1507
|
* directly. Injected at `/_api-proxy` (rather than written under `pages/`)
|
|
1497
1508
|
* because Astro treats `_`-prefixed page files as private; the endpoint's own
|
|
1498
|
-
* `prerender = false` export wins over the injection default.
|
|
1509
|
+
* `prerender = false` export wins over the injection default. Mounted under
|
|
1510
|
+
* the site `basePath`, at the URL the playground sends to (see
|
|
1511
|
+
* openapi/source.ts); Astro layers `deployment.base` onto both.
|
|
1499
1512
|
*/
|
|
1500
1513
|
const planPlaygroundProxy = (config: ResolvedConfig, srcDir: string) => ({
|
|
1501
1514
|
enabled: needsPlaygroundProxy(config),
|
|
1502
|
-
entrypoint: join(srcDir,
|
|
1503
|
-
pattern:
|
|
1515
|
+
entrypoint: join(srcDir, PLAYGROUND_PROXY_ENTRY),
|
|
1516
|
+
pattern: playgroundProxyPattern(config),
|
|
1504
1517
|
});
|
|
1505
1518
|
|
|
1506
1519
|
/**
|
|
1507
1520
|
* The origins the built-in proxy is allowed to reach: one per absolute
|
|
1508
|
-
* `servers[].url` across the parsed specs
|
|
1509
|
-
*
|
|
1510
|
-
*
|
|
1511
|
-
*
|
|
1521
|
+
* `servers[].url` across the parsed specs — the document's, each path item's,
|
|
1522
|
+
* and each operation's, since an operation's playground sends to the most
|
|
1523
|
+
* specific of those. This is the endpoint's whole trust boundary — the client
|
|
1524
|
+
* sends the target as a query parameter, and a reader's custom base URL is not
|
|
1525
|
+
* a documented server — so it is derived here, at build time, from the same
|
|
1526
|
+
* documents the operation pages render.
|
|
1512
1527
|
*
|
|
1513
|
-
*
|
|
1528
|
+
* A templated URL (`https://{env}.api.example.com`) is allowed as the
|
|
1529
|
+
* playground sends it, with each variable at its declared default. Relative
|
|
1530
|
+
* (`/v1`) URLs, and templates with a variable left undefaulted, carry no
|
|
1514
1531
|
* origin to allow and are skipped; AsyncAPI documents declare `servers` as a
|
|
1515
1532
|
* map and contribute nothing (the proxy is OpenAPI-only).
|
|
1516
1533
|
*/
|
|
@@ -1526,27 +1543,53 @@ const specOriginsOf = (spec: ApiSpecData): string[] => {
|
|
|
1526
1543
|
}
|
|
1527
1544
|
}
|
|
1528
1545
|
// SAFETY: `document` is arbitrary parsed JSON; the assertion only names
|
|
1529
|
-
// the optional `servers`
|
|
1530
|
-
// `Array.isArray
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1546
|
+
// the optional `servers` and `paths` shapes, and every access below
|
|
1547
|
+
// re-checks them — `Array.isArray` guards each list, optional chaining each
|
|
1548
|
+
// path item and operation, and `server.url ?? ""` the url.
|
|
1549
|
+
const document = spec.document as {
|
|
1550
|
+
paths?: Record<string, DocumentPathItem | null | undefined>;
|
|
1551
|
+
servers?: DocumentServer[];
|
|
1552
|
+
};
|
|
1553
|
+
const lists = [document.servers];
|
|
1554
|
+
for (const item of Object.values(document.paths ?? {})) {
|
|
1555
|
+
lists.push(item?.servers);
|
|
1556
|
+
for (const method of HTTP_METHODS) {
|
|
1557
|
+
lists.push(item?.[method]?.servers);
|
|
1539
1558
|
}
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1559
|
+
}
|
|
1560
|
+
for (const servers of lists) {
|
|
1561
|
+
for (const server of Array.isArray(servers) ? servers : []) {
|
|
1562
|
+
const url = withServerDefaults(server.url ?? "", server.variables);
|
|
1563
|
+
// `new URL("https://{region}.api.example.com")` parses — the braces land
|
|
1564
|
+
// in the hostname — so a variable with no default needs an explicit
|
|
1565
|
+
// check or its junk literal becomes an allowlist entry no real request
|
|
1566
|
+
// can match.
|
|
1567
|
+
if (url.includes("{")) {
|
|
1568
|
+
continue;
|
|
1569
|
+
}
|
|
1570
|
+
try {
|
|
1571
|
+
origins.add(new URL(url).origin);
|
|
1572
|
+
} catch {
|
|
1573
|
+
// Not an absolute URL: nothing to allow.
|
|
1574
|
+
}
|
|
1544
1575
|
}
|
|
1545
1576
|
}
|
|
1546
1577
|
return [...origins];
|
|
1547
1578
|
};
|
|
1548
1579
|
|
|
1549
|
-
|
|
1580
|
+
/** A `servers[]` entry as a parsed spec document declares it. */
|
|
1581
|
+
interface DocumentServer {
|
|
1582
|
+
url?: string;
|
|
1583
|
+
variables?: AsyncApiSpecValue;
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
/** An OpenAPI path item: its own `servers` plus one per operation. */
|
|
1587
|
+
type DocumentPathItem = { servers?: DocumentServer[] } & Partial<
|
|
1588
|
+
Record<HttpMethod, { servers?: DocumentServer[] } | null>
|
|
1589
|
+
>;
|
|
1590
|
+
|
|
1591
|
+
/** The built-in proxy's allowlist: every spec's origins, deduped and sorted. */
|
|
1592
|
+
export const specOrigins = (data: OpenApiData): string[] =>
|
|
1550
1593
|
[...new Set(Object.values(data).flatMap(specOriginsOf))].toSorted();
|
|
1551
1594
|
|
|
1552
1595
|
/**
|
|
@@ -1558,7 +1601,7 @@ const specOrigins = (data: OpenApiData): string[] =>
|
|
|
1558
1601
|
* whose own origins came out empty (no absolute `servers[].url`, no absolute
|
|
1559
1602
|
* GraphQL `endpoint`) gets a warning naming it.
|
|
1560
1603
|
*/
|
|
1561
|
-
const proxyAllowlistWarnings = (
|
|
1604
|
+
export const proxyAllowlistWarnings = (
|
|
1562
1605
|
config: ResolvedConfig,
|
|
1563
1606
|
data: OpenApiData
|
|
1564
1607
|
): string[] => {
|
|
@@ -1573,7 +1616,7 @@ const proxyAllowlistWarnings = (
|
|
|
1573
1616
|
warnings.push(
|
|
1574
1617
|
spec.kind === "graphql"
|
|
1575
1618
|
? `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.`
|
|
1576
|
-
: `The "${spec.label}" reference (${spec.route}) has playground.proxy: true, but its spec declares no absolute servers[].url (relative and templated
|
|
1619
|
+
: `The "${spec.label}" reference (${spec.route}) has playground.proxy: true, but its spec declares no absolute servers[].url (relative URLs, and templated ones with a variable missing its default, 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.`
|
|
1577
1620
|
);
|
|
1578
1621
|
}
|
|
1579
1622
|
return warnings;
|
|
@@ -1699,6 +1742,7 @@ export const analyzeComponentsFile = async (
|
|
|
1699
1742
|
const projectOgFonts = (project: BlumeProject): DerivedOgFonts =>
|
|
1700
1743
|
resolveOgFonts(
|
|
1701
1744
|
{
|
|
1745
|
+
locales: fontLocaleCodes(project.config.i18n),
|
|
1702
1746
|
ogFonts: project.config.seo.og.fonts,
|
|
1703
1747
|
themeFonts: project.config.theme.fonts,
|
|
1704
1748
|
themeFontsConfigured: project.themeFontsConfigured,
|
package/src/astro/templates.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { BLUME_IGNORE_DIRS } from "../core/sources/watch.ts";
|
|
|
14
14
|
import { trimChar } from "../core/trim.ts";
|
|
15
15
|
import type { ProjectContext } from "../core/types.ts";
|
|
16
16
|
import { deployPassthrough } from "../deploy/adapters/types.ts";
|
|
17
|
-
import {
|
|
17
|
+
import { SVG_ASSET_HEADERS } from "../deploy/headers.ts";
|
|
18
18
|
import { deployPlatform } from "../deploy/platforms/index.ts";
|
|
19
19
|
import { adapterRoot, distDir } from "../deploy/platforms/paths.ts";
|
|
20
20
|
import { applyBaseToAstroRedirects } from "../deploy/redirects.ts";
|
|
@@ -1377,7 +1377,10 @@ export const createSearch = () => create(${JSON.stringify(provider.options)});
|
|
|
1377
1377
|
* site uses the feature and `null` otherwise, so an unused library is absent
|
|
1378
1378
|
* from the module graph (never bundled, never pre-bundled in dev) rather than
|
|
1379
1379
|
* merely lazy. Regenerated on every pass: a page that gains a mermaid fence
|
|
1380
|
-
* turns the loader on.
|
|
1380
|
+
* turns the loader on. The EPUB module is named — even in a type — only when
|
|
1381
|
+
* `export.epub` is on: an ejected app depends on `epub-gen-memory` only then,
|
|
1382
|
+
* and a type import of a package it can't resolve fails its type check under
|
|
1383
|
+
* a strict linker such as pnpm.
|
|
1381
1384
|
*/
|
|
1382
1385
|
export const featuresTemplate = (features: ClientFeatures): string =>
|
|
1383
1386
|
`// Generated by Blume. Do not edit.
|
|
@@ -1394,9 +1397,13 @@ export const loadMermaid: (() => Promise<unknown>) | null = ${
|
|
|
1394
1397
|
};
|
|
1395
1398
|
|
|
1396
1399
|
/** The EPUB generator's browser bundle; null when export.epub is off. */
|
|
1397
|
-
|
|
1400
|
+
${
|
|
1401
|
+
features.epub
|
|
1402
|
+
? `export const loadEpub:
|
|
1398
1403
|
| (() => Promise<typeof import("epub-gen-memory/bundle")>)
|
|
1399
|
-
| null =
|
|
1404
|
+
| null = () => import("epub-gen-memory/bundle");`
|
|
1405
|
+
: "export const loadEpub: (() => Promise<unknown>) | null = null;"
|
|
1406
|
+
}
|
|
1400
1407
|
`;
|
|
1401
1408
|
|
|
1402
1409
|
/**
|
|
@@ -1561,12 +1568,13 @@ export function GET({ props }: { props: { route: string } }) {
|
|
|
1561
1568
|
* materializes under `.blume/public/blume-assets` (see
|
|
1562
1569
|
* `core/sources/assets.ts`). That directory is NOT Astro's `publicDir`
|
|
1563
1570
|
* (which points at the user project's `public/`), so without this endpoint
|
|
1564
|
-
* those references 404.
|
|
1571
|
+
* those references 404. `null` for an ejected app, which copies those
|
|
1572
|
+
* files into its own `public/blume-assets` instead.
|
|
1565
1573
|
* Prerendered: every asset becomes a static file in the build output; the dev
|
|
1566
1574
|
* server renders on demand, so new images appear without a restart.
|
|
1567
1575
|
*/
|
|
1568
1576
|
export const contentAssetsEndpointTemplate = (
|
|
1569
|
-
stagedAssetsDir: string
|
|
1577
|
+
stagedAssetsDir: string | null
|
|
1570
1578
|
): string =>
|
|
1571
1579
|
`// Generated by Blume. Do not edit.
|
|
1572
1580
|
import { existsSync } from "node:fs";
|
|
@@ -1588,18 +1596,29 @@ const TYPES: Record<string, string> = {
|
|
|
1588
1596
|
".ico": "image/x-icon",
|
|
1589
1597
|
".jpeg": "image/jpeg",
|
|
1590
1598
|
".jpg": "image/jpeg",
|
|
1599
|
+
".mov": "video/quicktime",
|
|
1600
|
+
".mp4": "video/mp4",
|
|
1601
|
+
".ogv": "video/ogg",
|
|
1591
1602
|
".png": "image/png",
|
|
1592
1603
|
".svg": "image/svg+xml",
|
|
1593
1604
|
".tiff": "image/tiff",
|
|
1605
|
+
".webm": "video/webm",
|
|
1594
1606
|
".webp": "image/webp",
|
|
1595
1607
|
};
|
|
1596
1608
|
|
|
1609
|
+
const extensionOf = (path: string): string =>
|
|
1610
|
+
path.slice(path.lastIndexOf(".")).toLowerCase();
|
|
1611
|
+
|
|
1597
1612
|
const contentType = (path: string): string =>
|
|
1598
|
-
TYPES[
|
|
1599
|
-
|
|
1613
|
+
TYPES[extensionOf(path)] ?? "application/octet-stream";
|
|
1614
|
+
|
|
1615
|
+
// A source downloads only images and videos; anything else in the staged
|
|
1616
|
+
// directory (a page an older build saved under its URL's extension) is never
|
|
1617
|
+
// published from the docs origin.
|
|
1618
|
+
const isMedia = (path: string): boolean => Object.hasOwn(TYPES, extensionOf(path));
|
|
1600
1619
|
|
|
1601
1620
|
const stagedParams = async (): Promise<string[]> => {
|
|
1602
|
-
if (!existsSync(STAGED_DIR)) {
|
|
1621
|
+
if (STAGED_DIR === null || !existsSync(STAGED_DIR)) {
|
|
1603
1622
|
return [];
|
|
1604
1623
|
}
|
|
1605
1624
|
const entries = await readdir(STAGED_DIR, {
|
|
@@ -1607,7 +1626,7 @@ const stagedParams = async (): Promise<string[]> => {
|
|
|
1607
1626
|
withFileTypes: true,
|
|
1608
1627
|
});
|
|
1609
1628
|
return entries
|
|
1610
|
-
.filter((entry) => entry.isFile())
|
|
1629
|
+
.filter((entry) => entry.isFile() && isMedia(entry.name))
|
|
1611
1630
|
.map((entry) =>
|
|
1612
1631
|
relative(STAGED_DIR, join(entry.parentPath, entry.name)).replaceAll(
|
|
1613
1632
|
"\\\\",
|
|
@@ -1628,6 +1647,9 @@ const resolveAsset = (asset: string): string | null => {
|
|
|
1628
1647
|
if (asset.startsWith("content/")) {
|
|
1629
1648
|
return files[asset.slice("content/".length)] ?? null;
|
|
1630
1649
|
}
|
|
1650
|
+
if (STAGED_DIR === null) {
|
|
1651
|
+
return null;
|
|
1652
|
+
}
|
|
1631
1653
|
const abs = resolve(STAGED_DIR, asset);
|
|
1632
1654
|
// Traversal guard: the dev server renders on demand, so the param is
|
|
1633
1655
|
// attacker-controlled there — never step outside the staged directory.
|
|
@@ -1637,7 +1659,7 @@ const resolveAsset = (asset: string): string | null => {
|
|
|
1637
1659
|
// asset — and a bare prefix also admits a sibling directory that merely
|
|
1638
1660
|
// shares the name.
|
|
1639
1661
|
const rel = relative(STAGED_DIR, abs);
|
|
1640
|
-
if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) {
|
|
1662
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute(rel) || !isMedia(rel)) {
|
|
1641
1663
|
return null;
|
|
1642
1664
|
}
|
|
1643
1665
|
return existsSync(abs) ? abs : null;
|
|
@@ -1651,11 +1673,12 @@ export const GET: APIRoute = async ({ params }) => {
|
|
|
1651
1673
|
const body = await readFile(path);
|
|
1652
1674
|
const type = contentType(path);
|
|
1653
1675
|
// An SVG opened directly is a document that can run script; the sandbox
|
|
1654
|
-
// keeps an uploaded one inert on the docs origin.
|
|
1655
|
-
//
|
|
1676
|
+
// keeps an uploaded one inert on the docs origin. This endpoint
|
|
1677
|
+
// prerenders, so these headers reach dev only: a build serves the files
|
|
1678
|
+
// with the same headers from its host's rules (see deploy/headers.ts).
|
|
1656
1679
|
const headers: Record<string, string> =
|
|
1657
1680
|
type === "image/svg+xml"
|
|
1658
|
-
? {
|
|
1681
|
+
? { ...${JSON.stringify(SVG_ASSET_HEADERS)}, "Content-Type": type }
|
|
1659
1682
|
: { "Content-Type": type };
|
|
1660
1683
|
return new Response(new Uint8Array(body), { headers });
|
|
1661
1684
|
};
|
|
@@ -2758,6 +2781,7 @@ export const notFoundPageTemplate = (): string => `---
|
|
|
2758
2781
|
import Icon from "blume/components/Icon.astro";
|
|
2759
2782
|
import PageLayout from "blume/components/layout/PageLayout.astro";
|
|
2760
2783
|
import { withBase } from "blume/components/islands/base-path.ts";
|
|
2784
|
+
import { withBasePath } from "blume/core/base-path.ts";
|
|
2761
2785
|
import data from "blume:data";
|
|
2762
2786
|
|
|
2763
2787
|
export const prerender = true;
|
|
@@ -2795,8 +2819,13 @@ const suggestions = [
|
|
|
2795
2819
|
// Every host serves this one page for any missing URL, so a reader who
|
|
2796
2820
|
// mistyped \`/ar/…\` would get the default locale's message. The other locales'
|
|
2797
2821
|
// copy rides along, and the script below swaps it in when the URL's first
|
|
2798
|
-
// segment names one of them — the message, its language
|
|
2799
|
-
// tab title, and the home link (to that locale's root).
|
|
2822
|
+
// segment past the site's base names one of them — the message, its language
|
|
2823
|
+
// and direction, the tab title, and the home link (to that locale's root).
|
|
2824
|
+
// Localized routes sit under both bases (\`{deployment.base}/{basePath}/ar/…\`),
|
|
2825
|
+
// so the script strips the two together, trailing slash included.
|
|
2826
|
+
const { basePath } = data.config;
|
|
2827
|
+
const siteRoot = withBase(withBasePath(basePath, "/"));
|
|
2828
|
+
const localeBase = siteRoot.endsWith("/") ? siteRoot : siteRoot + "/";
|
|
2800
2829
|
const localized = Object.fromEntries(
|
|
2801
2830
|
(i18n?.locales ?? [])
|
|
2802
2831
|
.filter((l) => l.code !== i18n?.defaultLocale && data.uiByLocale[l.code])
|
|
@@ -2808,7 +2837,7 @@ const localized = Object.fromEntries(
|
|
|
2808
2837
|
description: strings.description,
|
|
2809
2838
|
dir: l.dir ?? "ltr",
|
|
2810
2839
|
home: strings.home,
|
|
2811
|
-
homeHref: withBase("/" + l.code),
|
|
2840
|
+
homeHref: withBase(withBasePath(basePath, "/" + l.code)),
|
|
2812
2841
|
suggestions: strings.suggestions,
|
|
2813
2842
|
title: strings.title,
|
|
2814
2843
|
},
|
|
@@ -2837,7 +2866,7 @@ const localizedJson = JSON.stringify(localized).replaceAll("<", "\\\\u003c");
|
|
|
2837
2866
|
>
|
|
2838
2867
|
<div
|
|
2839
2868
|
class="mx-auto grid w-full max-w-5xl gap-12 px-6 py-20 sm:py-28 md:grid-cols-[3fr_2fr] md:gap-16 lg:gap-24 lg:py-36"
|
|
2840
|
-
data-base={
|
|
2869
|
+
data-base={localeBase}
|
|
2841
2870
|
data-blume-not-found
|
|
2842
2871
|
>
|
|
2843
2872
|
<div class="flex flex-col items-start">
|
|
@@ -3300,11 +3329,20 @@ const Example = entry.Component;
|
|
|
3300
3329
|
root.dataset.theme = theme;
|
|
3301
3330
|
root.classList.toggle("dark", theme === "dark");
|
|
3302
3331
|
};
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3332
|
+
// Blocked storage (a sandboxed or privacy-locked frame) throws on
|
|
3333
|
+
// access; it falls through to the OS setting like an unset preference.
|
|
3334
|
+
const stored = () => {
|
|
3335
|
+
let theme = null;
|
|
3336
|
+
try {
|
|
3337
|
+
theme = localStorage.getItem("blume-theme");
|
|
3338
|
+
} catch {}
|
|
3339
|
+
return (
|
|
3340
|
+
theme ??
|
|
3341
|
+
(matchMedia("(prefers-color-scheme: dark)").matches
|
|
3342
|
+
? "dark"
|
|
3343
|
+
: "light")
|
|
3344
|
+
);
|
|
3345
|
+
};
|
|
3308
3346
|
try {
|
|
3309
3347
|
const host = window.parent.document.documentElement;
|
|
3310
3348
|
apply(host.dataset.theme ?? stored());
|
package/src/audit/catalog.ts
CHANGED
|
@@ -583,7 +583,7 @@ export const CHECKS = [
|
|
|
583
583
|
},
|
|
584
584
|
{
|
|
585
585
|
category: "sitemap",
|
|
586
|
-
fix: "Sitemaps must be valid XML in the sitemaps.org urlset format.",
|
|
586
|
+
fix: "Sitemaps must be valid XML in the sitemaps.org urlset or sitemap index format.",
|
|
587
587
|
id: "BLUME_AUDIT_SITEMAP_INVALID",
|
|
588
588
|
severity: "error",
|
|
589
589
|
tier: "static",
|
|
@@ -684,7 +684,7 @@ export const CHECKS = [
|
|
|
684
684
|
},
|
|
685
685
|
{
|
|
686
686
|
category: "ai",
|
|
687
|
-
fix: "Rebuild so llms.txt matches the site; if the page is deliberately excluded,
|
|
687
|
+
fix: "Rebuild so llms.txt matches the site; if the page is deliberately excluded, set `ai.exclude: true` in its front matter.",
|
|
688
688
|
id: "BLUME_AUDIT_LLMS_TXT_PAGE_MISSING",
|
|
689
689
|
severity: "warning",
|
|
690
690
|
tier: "static",
|