blume 2.0.2 → 2.1.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 +1 -0
- package/CHANGELOG.md +113 -0
- package/README.md +1 -1
- package/dist/cli/{chunk-6k4ftwze.js → chunk-0gz36akr.js} +18 -2
- package/dist/cli/{chunk-6k4ftwze.js.map → chunk-0gz36akr.js.map} +3 -3
- package/dist/cli/{chunk-zp79m0ts.js → chunk-1n228xqb.js} +2 -2
- package/dist/cli/{chunk-ddndchfr.js → chunk-2f3ndkpw.js} +57 -29
- package/dist/cli/chunk-2f3ndkpw.js.map +14 -0
- package/dist/cli/{chunk-fsmrqk8a.js → chunk-2f7n4qzt.js} +8 -2
- package/dist/cli/{chunk-fsmrqk8a.js.map → chunk-2f7n4qzt.js.map} +3 -3
- package/dist/cli/{chunk-ppzjqwx2.js → chunk-2n4ps1b3.js} +17 -18
- package/dist/cli/{chunk-ppzjqwx2.js.map → chunk-2n4ps1b3.js.map} +3 -3
- package/dist/cli/{chunk-5r8g91qn.js → chunk-43zcf4gz.js} +398 -159
- package/dist/cli/chunk-43zcf4gz.js.map +36 -0
- package/dist/cli/chunk-4ejxbzee.js +39 -0
- package/dist/cli/chunk-4ejxbzee.js.map +10 -0
- package/dist/cli/{chunk-hr8ne106.js → chunk-4h8z9vv5.js} +296 -77
- package/dist/cli/chunk-4h8z9vv5.js.map +16 -0
- package/dist/cli/{chunk-k7pj68a8.js → chunk-5cg2rzgf.js} +18 -16
- package/dist/cli/{chunk-k7pj68a8.js.map → chunk-5cg2rzgf.js.map} +2 -2
- package/dist/cli/{chunk-h2ez8dzb.js → chunk-5ffywsmb.js} +4 -4
- package/dist/cli/{chunk-g698a744.js → chunk-5wj2qwn2.js} +5 -5
- package/dist/cli/chunk-6bm0xpgd.js +18 -0
- package/dist/cli/chunk-6bm0xpgd.js.map +10 -0
- package/dist/cli/{chunk-3yce002v.js → chunk-713mngdq.js} +15 -8
- package/dist/cli/chunk-713mngdq.js.map +14 -0
- package/dist/cli/{chunk-91ws1n6j.js → chunk-9geymqns.js} +17 -15
- package/dist/cli/{chunk-91ws1n6j.js.map → chunk-9geymqns.js.map} +2 -2
- package/dist/cli/{chunk-6dtt0zfn.js → chunk-9xr4dgee.js} +27 -18
- package/dist/cli/chunk-9xr4dgee.js.map +10 -0
- package/dist/cli/{chunk-sqw4ekg1.js → chunk-amrb6hgh.js} +2 -2
- package/dist/cli/chunk-cm89scdv.js +443 -0
- package/dist/cli/chunk-cm89scdv.js.map +11 -0
- package/dist/cli/{chunk-wdrt2k2v.js → chunk-cmtcnh9f.js} +90 -26
- package/dist/cli/chunk-cmtcnh9f.js.map +10 -0
- package/dist/cli/chunk-cy5bm0j0.js +104 -0
- package/dist/cli/chunk-cy5bm0j0.js.map +11 -0
- package/dist/cli/{chunk-7mbqtmgb.js → chunk-e7vvk5ya.js} +21 -12
- package/dist/cli/chunk-e7vvk5ya.js.map +10 -0
- package/dist/cli/{chunk-qkb5a8sa.js → chunk-g2z9x01v.js} +6 -98
- package/dist/cli/chunk-g2z9x01v.js.map +10 -0
- package/dist/cli/{chunk-273ygyr4.js → chunk-ga0pf4aj.js} +7 -7
- package/dist/cli/{chunk-273ygyr4.js.map → chunk-ga0pf4aj.js.map} +3 -3
- package/dist/cli/{chunk-h7k3nq3v.js → chunk-gp84za0h.js} +2 -2
- package/dist/cli/{chunk-hqp2ajnh.js → chunk-h2a48yhd.js} +195 -73
- package/dist/cli/chunk-h2a48yhd.js.map +36 -0
- package/dist/cli/{chunk-pbg5a4s3.js → chunk-jnwj5kjj.js} +71 -47
- package/dist/cli/chunk-jnwj5kjj.js.map +19 -0
- package/dist/cli/chunk-kwrg63tm.js +31 -0
- package/dist/cli/chunk-kwrg63tm.js.map +10 -0
- package/dist/cli/{chunk-8g8ytmgx.js → chunk-nd1821sk.js} +21 -19
- package/dist/cli/{chunk-8g8ytmgx.js.map → chunk-nd1821sk.js.map} +2 -2
- package/dist/cli/{chunk-w4bxdvsa.js → chunk-ntv4qrt7.js} +17 -15
- package/dist/cli/{chunk-w4bxdvsa.js.map → chunk-ntv4qrt7.js.map} +2 -2
- package/dist/cli/{chunk-bfwp9vp6.js → chunk-p804f3pe.js} +7 -7
- package/dist/cli/{chunk-4e9b9ra6.js → chunk-py62fewh.js} +6 -6
- package/dist/cli/{chunk-4e9b9ra6.js.map → chunk-py62fewh.js.map} +1 -1
- package/dist/cli/chunk-q3wxxq39.js +126 -0
- package/dist/cli/chunk-q3wxxq39.js.map +11 -0
- package/dist/cli/{chunk-j85scx15.js → chunk-rekqz4qg.js} +142 -46
- package/dist/cli/chunk-rekqz4qg.js.map +10 -0
- package/dist/cli/{chunk-00gs3wqs.js → chunk-ta27feev.js} +1 -1
- package/dist/cli/{chunk-v6ya5kcb.js → chunk-xkz5xbtv.js} +4031 -1132
- package/dist/cli/chunk-xkz5xbtv.js.map +213 -0
- package/dist/cli/{chunk-bbnwccaz.js → chunk-y4ykbark.js} +2 -2
- package/dist/cli/{chunk-n1yg3tj3.js → chunk-y5h4r2y0.js} +6 -6
- package/dist/cli/{chunk-n1yg3tj3.js.map → chunk-y5h4r2y0.js.map} +3 -3
- package/dist/cli/{chunk-nfcyttvj.js → chunk-yj17vs39.js} +9 -9
- package/dist/cli/{chunk-nfcyttvj.js.map → chunk-yj17vs39.js.map} +1 -1
- package/dist/cli/{chunk-7vtckvaw.js → chunk-yxmk4pec.js} +16 -14
- package/dist/cli/{chunk-7vtckvaw.js.map → chunk-yxmk4pec.js.map} +2 -2
- package/dist/cli/{chunk-gs7r695n.js → chunk-zccbjcj0.js} +22 -8
- package/dist/cli/{chunk-gs7r695n.js.map → chunk-zccbjcj0.js.map} +3 -3
- package/dist/cli/{chunk-xh43dwgw.js → chunk-zf748avv.js} +415 -225
- package/dist/cli/chunk-zf748avv.js.map +37 -0
- package/dist/cli/{chunk-d1v5rhy0.js → chunk-zhvd6adm.js} +16 -14
- package/dist/cli/{chunk-d1v5rhy0.js.map → chunk-zhvd6adm.js.map} +2 -2
- package/dist/cli/index.js +28 -20
- package/dist/cli/index.js.map +5 -5
- package/dist/types/ai/agent-surface.d.ts +8 -7
- package/dist/types/ai/ai-catalog.d.ts +7 -0
- package/dist/types/ai/ask-context.d.ts +22 -0
- package/dist/types/ai/ask.d.ts +271 -58
- package/dist/types/ai/index.d.ts +2 -2
- package/dist/types/ai/link-headers.d.ts +8 -1
- package/dist/types/ai/openapi-components.d.ts +5 -2
- package/dist/types/ai/relative-links.d.ts +9 -7
- package/dist/types/ai/site-skill.d.ts +26 -0
- package/dist/types/ai/skills.d.ts +20 -1
- package/dist/types/ai/tar.d.ts +1 -3
- package/dist/types/analytics/index.d.ts +2 -0
- package/dist/types/analytics/one-dollar-stats.d.ts +48 -0
- package/dist/types/analytics/schema.d.ts +14 -0
- package/dist/types/astro/integration.d.ts +11 -2
- package/dist/types/captcha/hcaptcha.d.ts +27 -0
- package/dist/types/captcha/index.d.ts +28 -0
- package/dist/types/captcha/schema.d.ts +29 -0
- package/dist/types/captcha/turnstile.d.ts +28 -0
- package/dist/types/components/content/api-field.d.ts +59 -0
- package/dist/types/components/content/api-page.d.ts +95 -0
- package/dist/types/components/layout/nav-utils.d.ts +26 -0
- package/dist/types/components/openapi/helpers.d.ts +137 -0
- package/dist/types/components/openapi/security.d.ts +85 -0
- package/dist/types/consent/ethyca.d.ts +48 -0
- package/dist/types/consent/index.d.ts +37 -0
- package/dist/types/consent/native.d.ts +28 -0
- package/dist/types/consent/osano.d.ts +42 -0
- package/dist/types/consent/schema.d.ts +110 -0
- package/dist/types/core/base-path.d.ts +21 -7
- package/dist/types/core/config-input.d.ts +196 -27
- package/dist/types/core/data.d.ts +49 -4
- package/dist/types/core/directive-diagnostics.d.ts +12 -0
- package/dist/types/core/footer.d.ts +8 -0
- package/dist/types/core/heading-markers.d.ts +5 -7
- package/dist/types/core/i18n-ui.d.ts +86 -0
- package/dist/types/core/i18n.d.ts +2 -0
- package/dist/types/core/includes.d.ts +8 -2
- package/dist/types/core/last-modified.d.ts +10 -0
- package/dist/types/core/localizable.d.ts +7 -0
- package/dist/types/core/meta.d.ts +8 -0
- package/dist/types/core/page-modes.d.ts +29 -0
- package/dist/types/core/redirect-patterns.d.ts +144 -0
- package/dist/types/core/related.d.ts +40 -0
- package/dist/types/core/schema.d.ts +558 -56
- package/dist/types/core/sources/lower.d.ts +29 -16
- package/dist/types/core/sources/normalize.d.ts +13 -1
- package/dist/types/core/sources/read.d.ts +5 -0
- package/dist/types/core/sources/watch.d.ts +12 -5
- package/dist/types/core/standard-schema.d.ts +5 -0
- package/dist/types/core/types.d.ts +1 -1
- package/dist/types/core/variables.d.ts +42 -0
- package/dist/types/deploy/artifacts.d.ts +6 -4
- package/dist/types/deploy/headers.d.ts +5 -0
- package/dist/types/deploy/node-headers.d.ts +13 -2
- package/dist/types/deploy/platforms/cloudflare.d.ts +5 -0
- package/dist/types/deploy/platforms/netlify.d.ts +7 -0
- package/dist/types/deploy/platforms/types.d.ts +8 -0
- package/dist/types/deploy/platforms/vercel.d.ts +6 -0
- package/dist/types/deploy/redirects.d.ts +61 -22
- package/dist/types/deploy/vercel-negotiation.d.ts +9 -0
- package/dist/types/markdown/code-title.d.ts +17 -0
- package/dist/types/markdown/directives.d.ts +62 -0
- package/dist/types/markdown/features.d.ts +24 -2
- package/dist/types/markdown/mdast.d.ts +63 -0
- package/dist/types/narration/build.d.ts +70 -0
- package/dist/types/narration/provider.d.ts +26 -0
- package/dist/types/narration/script.d.ts +154 -0
- package/dist/types/openapi/asyncapi.d.ts +4 -2
- package/dist/types/openapi/model.d.ts +12 -6
- package/dist/types/openapi/overlay.d.ts +54 -0
- package/dist/types/openapi/parse.d.ts +13 -0
- package/dist/types/openapi/references.d.ts +14 -4
- package/dist/types/ratelimit/cloudflare.d.ts +39 -0
- package/dist/types/ratelimit/index.d.ts +27 -0
- package/dist/types/ratelimit/memory.d.ts +36 -0
- package/dist/types/ratelimit/schema.d.ts +111 -0
- package/dist/types/ratelimit/upstash.d.ts +21 -0
- package/dist/types/ratelimit/wrangler.d.ts +23 -0
- package/dist/types/reference/asyncapi.d.ts +10 -10
- package/dist/types/reference/graphql.d.ts +13 -10
- package/dist/types/reference/openapi.d.ts +33 -12
- package/dist/types/reference/options.d.ts +45 -5
- package/dist/types/reference/scalar.d.ts +13 -0
- package/dist/types/reference/schema.d.ts +60 -36
- package/dist/types/search/documents.d.ts +12 -1
- package/dist/types/search/orama-index.d.ts +4 -0
- package/dist/types/seo/metatags.d.ts +20 -0
- package/docs/02-deployment.mdx +44 -6
- package/docs/03-upgrading.mdx +4 -4
- package/docs/04-migrating.mdx +1 -1
- package/docs/08-faq.mdx +1 -1
- package/docs/advanced/changelog.mdx +2 -2
- package/docs/advanced/custom-pages.mdx +3 -3
- package/docs/advanced/skills.mdx +14 -0
- package/docs/cli/audit.mdx +38 -2
- package/docs/cli/evals.mdx +3 -3
- package/docs/cli/index.mdx +1 -0
- package/docs/cli/translate.mdx +3 -3
- package/docs/cli/version.mdx +1 -1
- package/docs/configuration/analytics.mdx +22 -1
- package/docs/configuration/assistant.mdx +188 -36
- package/docs/configuration/consent.mdx +100 -0
- package/docs/configuration/customization.mdx +6 -4
- package/docs/configuration/index.mdx +63 -5
- package/docs/configuration/meta.ts +3 -0
- package/docs/configuration/narration.mdx +119 -0
- package/docs/configuration/rate-limiting.mdx +69 -0
- package/docs/configuration/search.mdx +30 -2
- package/docs/configuration/theming.mdx +3 -1
- package/docs/content/components.mdx +107 -1
- package/docs/content/frontmatter.mdx +77 -1
- package/docs/content/i18n.mdx +40 -3
- package/docs/content/includes.mdx +16 -0
- package/docs/content/index.mdx +4 -2
- package/docs/content/islands.mdx +1 -1
- package/docs/content/meta.mdx +4 -1
- package/docs/content/meta.ts +1 -0
- package/docs/content/navigation.mdx +28 -6
- package/docs/content/sources/contentful.mdx +32 -0
- package/docs/content/sources/custom.mdx +39 -0
- package/docs/content/sources/github-releases.mdx +31 -0
- package/docs/content/sources/index.mdx +91 -0
- package/docs/content/sources/meta.ts +16 -0
- package/docs/content/sources/notion.mdx +32 -0
- package/docs/content/sources/obsidian.mdx +37 -0
- package/docs/content/sources/payload.mdx +30 -0
- package/docs/content/sources/remote-mdx.mdx +40 -0
- package/docs/content/sources/sanity.mdx +29 -0
- package/docs/content/sources/strapi.mdx +31 -0
- package/docs/content/syntax.mdx +81 -2
- package/docs/content/variables.mdx +41 -0
- package/docs/content/versioning.mdx +1 -1
- package/docs/discoverability/agent-discovery.mdx +30 -3
- package/docs/discoverability/index.mdx +3 -2
- package/docs/discoverability/llms-txt.mdx +1 -1
- package/docs/discoverability/markdown.mdx +2 -2
- package/docs/discoverability/metadata.mdx +17 -0
- package/docs/discoverability/open-graph.mdx +5 -3
- package/docs/discoverability/rss.mdx +1 -1
- package/docs/references/api-pages.mdx +74 -0
- package/docs/references/asyncapi.mdx +2 -2
- package/docs/references/graphql.mdx +3 -3
- package/docs/references/meta.ts +1 -1
- package/docs/references/openapi.mdx +106 -5
- package/docs/references/scalar.mdx +3 -1
- package/package.json +36 -1
- package/skills/blume/SKILL.md +3 -0
- package/skills/blume-migrate/SKILL.md +5 -5
- package/skills/blume-migrate/references/docusaurus.md +3 -3
- package/skills/blume-migrate/references/fumadocs.md +1 -1
- package/skills/blume-migrate/references/mintlify.md +37 -19
- package/skills/blume-migrate/references/nextra.md +3 -3
- package/skills/blume-migrate/references/starlight.md +2 -1
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +67 -16
- package/skills/blume-write-skill/SKILL.md +82 -0
- package/src/ai/agent-readability.ts +4 -3
- package/src/ai/agent-surface.ts +9 -7
- package/src/ai/ai-catalog.ts +12 -5
- package/src/ai/api/handlers.ts +2 -2
- package/src/ai/api/spec.ts +2 -2
- package/src/ai/api-catalog.ts +6 -2
- package/src/ai/ask-context.ts +73 -20
- package/src/ai/ask-data.ts +6 -0
- package/src/ai/ask-tools.ts +142 -0
- package/src/ai/ask.ts +354 -76
- package/src/ai/changelog-markdown.ts +2 -2
- package/src/ai/component-markdown.ts +75 -0
- package/src/ai/cors.ts +2 -0
- package/src/ai/index.ts +12 -0
- package/src/ai/link-headers.ts +14 -5
- package/src/ai/llms.ts +24 -9
- package/src/ai/markdown.ts +2 -1
- package/src/ai/mcp/data.ts +6 -0
- package/src/ai/mcp/discovery.ts +2 -2
- package/src/ai/mcp/query.ts +2 -2
- package/src/ai/mcp/server.ts +2 -2
- package/src/ai/openapi-components.ts +31 -9
- package/src/ai/relative-links.ts +42 -26
- package/src/ai/serializers.ts +2 -1
- package/src/ai/site-skill.ts +274 -0
- package/src/ai/skills.ts +49 -3
- package/src/ai/tar.ts +139 -12
- package/src/ai/write-skill.ts +97 -0
- package/src/analytics/head.ts +4 -0
- package/src/analytics/index.ts +5 -0
- package/src/analytics/one-dollar-stats.ts +86 -0
- package/src/analytics/schema.ts +2 -0
- package/src/astro/generate.ts +133 -19
- package/src/astro/include-hmr.ts +31 -12
- package/src/astro/include-refresh.ts +19 -8
- package/src/astro/index.ts +1 -0
- package/src/astro/integration.ts +143 -14
- package/src/astro/module-types.ts +6 -0
- package/src/astro/runtime-modules.ts +28 -13
- package/src/astro/templates.ts +364 -67
- package/src/astro/variables.ts +31 -0
- package/src/audit/catalog.ts +19 -0
- package/src/audit/checks/contrast.ts +183 -0
- package/src/audit/checks/i18n.ts +2 -3
- package/src/audit/checks/indexability.ts +2 -3
- package/src/audit/checks/links.ts +13 -13
- package/src/audit/checks/sitemap.ts +2 -3
- package/src/audit/graph.ts +3 -1
- package/src/audit/redirects.ts +46 -15
- package/src/audit/run.ts +14 -9
- package/src/audit/snapshot.ts +5 -0
- package/src/audit/types.ts +2 -1
- package/src/blume-modules.d.ts +7 -0
- package/src/captcha/client.ts +152 -0
- package/src/captcha/hcaptcha.ts +36 -0
- package/src/captcha/index.ts +28 -0
- package/src/captcha/schema.ts +32 -0
- package/src/captcha/turnstile.ts +38 -0
- package/src/captcha/verify.ts +71 -0
- package/src/cli/args.ts +32 -0
- package/src/cli/command-meta.ts +5 -0
- package/src/cli/commands/audit.ts +4 -1
- package/src/cli/commands/doctor.ts +25 -17
- package/src/cli/commands/eval.ts +19 -11
- package/src/cli/commands/skill.ts +96 -0
- package/src/cli/commands/translate.ts +9 -12
- package/src/cli/commands/validate.ts +3 -1
- package/src/cli/dev-lock.ts +157 -37
- package/src/cli/index.ts +5 -0
- package/src/cli/report-format.ts +11 -6
- package/src/cli/required-secrets.ts +71 -29
- package/src/components/BlumePage.astro +2 -0
- package/src/components/colors.ts +19 -0
- package/src/components/content/ApiEndpoint.astro +18 -0
- package/src/components/content/ApiField.astro +79 -0
- package/src/components/content/ApiPlayground.astro +62 -0
- package/src/components/content/ApiRail.astro +19 -0
- package/src/components/content/Badge.astro +5 -3
- package/src/components/content/Color.astro +1 -0
- package/src/components/content/Component.astro +2 -2
- package/src/components/content/FileTree.astro +1 -0
- package/src/components/content/GithubInfo.astro +1 -0
- package/src/components/content/Panel.astro +1 -0
- package/src/components/content/ParamField.astro +25 -0
- package/src/components/content/RequestExample.astro +17 -0
- package/src/components/content/ResponseExample.astro +17 -0
- package/src/components/content/ResponseField.astro +32 -0
- package/src/components/content/Step.astro +1 -0
- package/src/components/content/Tab.astro +0 -1
- package/src/components/content/Tabs.astro +4 -0
- package/src/components/content/Tile.astro +1 -0
- package/src/components/content/Tooltip.astro +1 -0
- package/src/components/content/Tree.astro +1 -0
- package/src/components/content/TypeTable.astro +4 -1
- package/src/components/content/View.astro +21 -0
- package/src/components/content/ViewSwitcher.astro +168 -0
- package/src/components/content/api-field.ts +100 -0
- package/src/components/content/api-page.ts +224 -0
- package/src/components/content/badge-color.ts +5 -2
- package/src/components/content/base-href.ts +13 -36
- package/src/components/content/view-switcher.ts +71 -0
- package/src/components/index.ts +2 -0
- package/src/components/islands/Assistant.astro +10 -1
- package/src/components/islands/assistant.tsx +125 -18
- package/src/components/islands/base-path.ts +47 -10
- package/src/components/islands/code-question.ts +62 -0
- package/src/components/islands/hooks.ts +145 -40
- package/src/components/islands/support-link.ts +68 -0
- package/src/components/islands/webmcp.ts +4 -2
- package/src/components/layout/Analytics.astro +11 -2
- package/src/components/layout/Banner.astro +14 -10
- package/src/components/layout/Breadcrumbs.astro +11 -4
- package/src/components/layout/ConsentBanner.astro +62 -0
- package/src/components/layout/ConsentHead.astro +38 -0
- package/src/components/layout/Directory.astro +94 -0
- package/src/components/layout/DiscoveryLinks.astro +9 -5
- package/src/components/layout/DrawerSwitchers.astro +51 -0
- package/src/components/layout/Empty.astro +2 -2
- package/src/components/layout/Header.astro +124 -82
- package/src/components/layout/LanguageSwitcher.astro +50 -17
- package/src/components/layout/NarrationPlayer.astro +1085 -0
- package/src/components/layout/NavSelector.astro +35 -9
- package/src/components/layout/NavTabMenu.astro +3 -3
- package/src/components/layout/NavTree.astro +16 -6
- package/src/components/layout/PageActions.astro +4 -3
- package/src/components/layout/PageFeedback.astro +54 -49
- package/src/components/layout/PageLayout.astro +78 -9
- package/src/components/layout/Pagination.astro +3 -3
- package/src/components/layout/ReferenceLayout.astro +17 -6
- package/src/components/layout/RelatedPages.astro +42 -0
- package/src/components/layout/RootLayout.astro +313 -50
- package/src/components/layout/Search.astro +64 -30
- package/src/components/layout/SiteFooter.astro +124 -0
- package/src/components/layout/SiteMetatags.astro +16 -0
- package/src/components/layout/VersionBanner.astro +2 -2
- package/src/components/layout/analytics-client.ts +12 -0
- package/src/components/layout/consent/native.ts +79 -0
- package/src/components/layout/consent/types.ts +37 -0
- package/src/components/layout/footer-links.ts +53 -0
- package/src/components/layout/footer-socials.ts +87 -0
- package/src/components/layout/locale-redirect.ts +107 -0
- package/src/components/layout/nav-utils.ts +83 -0
- package/src/components/layout/page-feedback.ts +96 -0
- package/src/components/layout/search/analytics.ts +98 -0
- package/src/components/layout/search/flexsearch.ts +17 -5
- package/src/components/layout/search/orama-cloud.ts +26 -14
- package/src/components/layout/search/types.ts +20 -0
- package/src/components/layout/search/typesense.ts +5 -1
- package/src/components/layout/toc-active.ts +41 -0
- package/src/components/layout/toc-element.ts +8 -14
- package/src/components/openapi/ApiTagOperations.astro +2 -2
- package/src/components/openapi/AsyncApiOperation.astro +7 -4
- package/src/components/openapi/GraphqlChip.astro +2 -2
- package/src/components/openapi/Operation.astro +163 -13
- package/src/components/openapi/Playground.astro +4 -4
- package/src/components/openapi/RequestBody.astro +6 -4
- package/src/components/openapi/RequestPanel.astro +63 -8
- package/src/components/openapi/Responses.astro +6 -4
- package/src/components/openapi/SchemaTable.astro +7 -0
- package/src/components/openapi/async-snippets.ts +4 -4
- package/src/components/openapi/async.ts +38 -6
- package/src/components/openapi/code-samples.ts +97 -0
- package/src/components/openapi/helpers.ts +19 -9
- package/src/components/openapi/message-composer.ts +6 -1
- package/src/components/openapi/message.ts +17 -2
- package/src/components/openapi/operation-model.ts +81 -11
- package/src/components/openapi/panel.ts +4 -2
- package/src/components/openapi/param-style.ts +181 -0
- package/src/components/openapi/playground-client.ts +73 -16
- package/src/components/openapi/request.ts +190 -26
- package/src/components/openapi/schema-tree.ts +30 -24
- package/src/components/openapi/security.ts +6 -3
- package/src/components/openapi/snippets.ts +953 -13
- package/src/components/openapi/ws-client.ts +18 -2
- package/src/components/props.ts +6 -0
- package/src/consent/client.ts +95 -0
- package/src/consent/clients.ts +19 -0
- package/src/consent/ethyca.ts +71 -0
- package/src/consent/head.ts +24 -0
- package/src/consent/index.ts +37 -0
- package/src/consent/init.ts +29 -0
- package/src/consent/native.ts +36 -0
- package/src/consent/osano.ts +59 -0
- package/src/consent/schema.ts +30 -0
- package/src/core/base-path.ts +62 -16
- package/src/core/builtin-tags.ts +5 -0
- package/src/core/client-address.ts +19 -0
- package/src/core/config-input.ts +203 -27
- package/src/core/data.ts +44 -4
- package/src/core/diagnostics.ts +5 -2
- package/src/core/directive-diagnostics.ts +99 -0
- package/src/core/footer.ts +26 -0
- package/src/core/frontmatter.ts +21 -18
- package/src/core/graph.ts +39 -36
- package/src/core/heading-markers.ts +5 -18
- package/src/core/i18n-ui.ts +82 -2
- package/src/core/i18n.ts +31 -3
- package/src/core/includes.ts +20 -3
- package/src/core/last-modified.ts +25 -3
- package/src/core/links.ts +16 -2
- package/src/core/locale-links.ts +5 -1
- package/src/core/localizable.ts +26 -0
- package/src/core/meta.ts +33 -19
- package/src/core/navigation.ts +71 -20
- package/src/core/page-modes.ts +56 -0
- package/src/core/project-graph.ts +138 -10
- package/src/core/redirect-patterns.ts +491 -0
- package/src/core/related.ts +131 -0
- package/src/core/schema.ts +342 -68
- package/src/core/sources/contentful-rich-text.ts +27 -20
- package/src/core/sources/filesystem.ts +20 -2
- package/src/core/sources/github-releases.ts +25 -6
- package/src/core/sources/lexical.ts +23 -18
- package/src/core/sources/lower.ts +201 -34
- package/src/core/sources/mdx-remote.ts +51 -16
- package/src/core/sources/normalize.ts +327 -91
- package/src/core/sources/notion.ts +52 -25
- package/src/core/sources/obsidian.ts +23 -6
- package/src/core/sources/portable-text.ts +38 -21
- package/src/core/sources/read.ts +8 -3
- package/src/core/sources/strapi-blocks.ts +20 -16
- package/src/core/sources/watch.ts +20 -7
- package/src/core/standard-schema.ts +10 -6
- package/src/core/types.ts +1 -1
- package/src/core/ui-packs/ar.ts +43 -0
- package/src/core/ui-packs/bg.ts +45 -0
- package/src/core/ui-packs/bn.ts +43 -0
- package/src/core/ui-packs/ca.ts +46 -0
- package/src/core/ui-packs/cs.ts +44 -0
- package/src/core/ui-packs/da.ts +45 -0
- package/src/core/ui-packs/de.ts +46 -0
- package/src/core/ui-packs/el.ts +45 -0
- package/src/core/ui-packs/es.ts +46 -0
- package/src/core/ui-packs/fa.ts +45 -0
- package/src/core/ui-packs/fi.ts +45 -0
- package/src/core/ui-packs/fr.ts +46 -0
- package/src/core/ui-packs/he.ts +43 -0
- package/src/core/ui-packs/hi.ts +44 -0
- package/src/core/ui-packs/hr.ts +45 -0
- package/src/core/ui-packs/hu.ts +44 -0
- package/src/core/ui-packs/id.ts +45 -0
- package/src/core/ui-packs/it.ts +45 -0
- package/src/core/ui-packs/ja.ts +45 -0
- package/src/core/ui-packs/ko.ts +44 -0
- package/src/core/ui-packs/nl.ts +46 -0
- package/src/core/ui-packs/no.ts +44 -0
- package/src/core/ui-packs/pl.ts +45 -0
- package/src/core/ui-packs/pt-br.ts +46 -0
- package/src/core/ui-packs/pt.ts +45 -0
- package/src/core/ui-packs/ro.ts +45 -0
- package/src/core/ui-packs/ru.ts +45 -0
- package/src/core/ui-packs/sk.ts +44 -0
- package/src/core/ui-packs/sr.ts +45 -0
- package/src/core/ui-packs/sv.ts +44 -0
- package/src/core/ui-packs/th.ts +43 -0
- package/src/core/ui-packs/tr.ts +45 -0
- package/src/core/ui-packs/uk.ts +45 -0
- package/src/core/ui-packs/vi.ts +45 -0
- package/src/core/ui-packs/zh-tw.ts +43 -0
- package/src/core/ui-packs/zh.ts +43 -0
- package/src/core/variables.ts +103 -0
- package/src/core/version-cut.ts +52 -15
- package/src/deploy/artifacts.ts +104 -14
- package/src/deploy/cloudflare-negotiation.ts +39 -17
- package/src/deploy/headers.ts +10 -5
- package/src/deploy/node-headers.ts +64 -10
- package/src/deploy/platforms/cloudflare.ts +44 -3
- package/src/deploy/platforms/netlify.ts +34 -1
- package/src/deploy/platforms/node.ts +1 -0
- package/src/deploy/platforms/static.ts +1 -0
- package/src/deploy/platforms/types.ts +8 -0
- package/src/deploy/platforms/vercel.ts +38 -2
- package/src/deploy/redirects.ts +139 -36
- package/src/deploy/robots.ts +2 -2
- package/src/deploy/rss.ts +4 -3
- package/src/deploy/sitemap.ts +7 -5
- package/src/deploy/vercel-negotiation.ts +36 -0
- package/src/markdown/api-rail.ts +259 -0
- package/src/markdown/base-links.ts +34 -36
- package/src/markdown/code-title.ts +35 -9
- package/src/markdown/directives.ts +242 -36
- package/src/markdown/features.ts +20 -2
- package/src/markdown/fence-meta.ts +2 -0
- package/src/markdown/include.ts +11 -1
- package/src/markdown/index.ts +35 -11
- package/src/markdown/math.ts +4 -4
- package/src/markdown/mdast.ts +5 -2
- package/src/markdown/relative-links.ts +12 -4
- package/src/markdown/variables.ts +56 -0
- package/src/markdown/views.ts +82 -0
- package/src/narration/build.ts +412 -0
- package/src/narration/player.ts +249 -0
- package/src/narration/provider.ts +29 -0
- package/src/narration/script.ts +592 -0
- package/src/og/card.ts +136 -11
- package/src/og/derive.ts +41 -31
- package/src/og/index.ts +1 -0
- package/src/openapi/asyncapi.ts +4 -2
- package/src/openapi/model.ts +77 -36
- package/src/openapi/overlay.ts +256 -0
- package/src/openapi/parse.ts +55 -2
- package/src/openapi/proxy.ts +63 -10
- package/src/openapi/references.ts +21 -5
- package/src/openapi/render-mdx.ts +46 -3
- package/src/openapi/scalar.ts +40 -6
- package/src/openapi/source.ts +1 -1
- package/src/ratelimit/cloudflare.ts +54 -0
- package/src/ratelimit/index.ts +30 -0
- package/src/ratelimit/memory.ts +45 -0
- package/src/ratelimit/runtime.ts +229 -0
- package/src/ratelimit/schema.ts +28 -0
- package/src/ratelimit/upstash.ts +30 -0
- package/src/ratelimit/wrangler.ts +70 -0
- package/src/reference/asyncapi.ts +2 -2
- package/src/reference/graphql.ts +5 -2
- package/src/reference/openapi.ts +21 -7
- package/src/reference/options.ts +51 -7
- package/src/reference/scalar.ts +33 -8
- package/src/registry/eject.ts +201 -28
- package/src/registry/registry.ts +7 -0
- package/src/search/adapters/version-scope.ts +30 -0
- package/src/search/documents.ts +93 -28
- package/src/search/orama-index.ts +28 -3
- package/src/search/pagefind-ranking.ts +39 -0
- package/src/search/popular.ts +2 -1
- package/src/search/sync/algolia.ts +15 -4
- package/src/search/sync/orama-cloud.ts +3 -0
- package/src/search/sync/typesense.ts +5 -0
- package/src/seo/jsonld.ts +7 -3
- package/src/seo/metatags.ts +67 -0
- package/src/theme/chrome-icons.ts +1 -0
- package/src/theme/color.ts +500 -0
- package/src/theme/entry.ts +56 -3
- package/src/theme/palette.ts +121 -45
- package/src/translate/meta.ts +68 -24
- package/src/translate/prompts.ts +1 -0
- package/src/translate/run.ts +3 -3
- package/src/translate/validate.ts +12 -3
- package/src/translate/work-list.ts +56 -17
- package/src/upgrade/upgrade.ts +4 -171
- package/dist/cli/chunk-3yce002v.js.map +0 -14
- package/dist/cli/chunk-5r8g91qn.js.map +0 -34
- package/dist/cli/chunk-6dtt0zfn.js.map +0 -10
- package/dist/cli/chunk-7mbqtmgb.js.map +0 -10
- package/dist/cli/chunk-ddndchfr.js.map +0 -14
- package/dist/cli/chunk-esh98wmb.js +0 -23
- package/dist/cli/chunk-esh98wmb.js.map +0 -10
- package/dist/cli/chunk-hqp2ajnh.js.map +0 -35
- package/dist/cli/chunk-hr8ne106.js.map +0 -13
- package/dist/cli/chunk-j85scx15.js.map +0 -10
- package/dist/cli/chunk-mqc662a6.js +0 -75
- package/dist/cli/chunk-mqc662a6.js.map +0 -11
- package/dist/cli/chunk-pbg5a4s3.js.map +0 -19
- package/dist/cli/chunk-qkb5a8sa.js.map +0 -10
- package/dist/cli/chunk-v6ya5kcb.js.map +0 -189
- package/dist/cli/chunk-wdrt2k2v.js.map +0 -10
- package/dist/cli/chunk-xh43dwgw.js.map +0 -36
- package/docs/content/sources.mdx +0 -338
- /package/dist/cli/{chunk-zp79m0ts.js.map → chunk-1n228xqb.js.map} +0 -0
- /package/dist/cli/{chunk-h2ez8dzb.js.map → chunk-5ffywsmb.js.map} +0 -0
- /package/dist/cli/{chunk-g698a744.js.map → chunk-5wj2qwn2.js.map} +0 -0
- /package/dist/cli/{chunk-sqw4ekg1.js.map → chunk-amrb6hgh.js.map} +0 -0
- /package/dist/cli/{chunk-h7k3nq3v.js.map → chunk-gp84za0h.js.map} +0 -0
- /package/dist/cli/{chunk-bfwp9vp6.js.map → chunk-p804f3pe.js.map} +0 -0
- /package/dist/cli/{chunk-00gs3wqs.js.map → chunk-ta27feev.js.map} +0 -0
- /package/dist/cli/{chunk-bbnwccaz.js.map → chunk-y4ykbark.js.map} +0 -0
package/src/search/documents.ts
CHANGED
|
@@ -23,7 +23,12 @@ import { HTML_COMMENT } from "../core/sources/normalize.ts";
|
|
|
23
23
|
import { readExpandedEntryText } from "../core/sources/read.ts";
|
|
24
24
|
import type { NavNode, PageRecord } from "../core/types.ts";
|
|
25
25
|
import { parseCodeTitle } from "../markdown/code-title.ts";
|
|
26
|
-
import {
|
|
26
|
+
import { directiveSource } from "../markdown/directives.ts";
|
|
27
|
+
import type { DirectiveNode } from "../markdown/directives.ts";
|
|
28
|
+
import {
|
|
29
|
+
MARKDOWN_BODY_FEATURES,
|
|
30
|
+
MDX_BODY_FEATURES,
|
|
31
|
+
} from "../markdown/features.ts";
|
|
27
32
|
import { pageFacets } from "./facets.ts";
|
|
28
33
|
|
|
29
34
|
/** A document indexed by the client-side search providers (Orama, FlexSearch). */
|
|
@@ -49,6 +54,10 @@ export interface SearchDocument {
|
|
|
49
54
|
* Filterable through the MCP tools' `filters` input.
|
|
50
55
|
*/
|
|
51
56
|
facets?: Record<string, string>;
|
|
57
|
+
/** Frontmatter `search.boost`: the page's relevance multiplier, when set. */
|
|
58
|
+
boost?: number;
|
|
59
|
+
/** Frontmatter `search.keywords`: extra terms the page is found by. */
|
|
60
|
+
keywords?: string[];
|
|
52
61
|
}
|
|
53
62
|
|
|
54
63
|
/**
|
|
@@ -72,6 +81,13 @@ export interface SearchRecord {
|
|
|
72
81
|
version: string;
|
|
73
82
|
/** Single faceting tag (the first frontmatter tag, when present). */
|
|
74
83
|
tag?: string;
|
|
84
|
+
/**
|
|
85
|
+
* The page's relevance multiplier, 1 unless `search.boost` sets it: a
|
|
86
|
+
* ranking attribute every record carries, so the backend can sort by it.
|
|
87
|
+
*/
|
|
88
|
+
boost: number;
|
|
89
|
+
/** Extra terms the page is found by (`search.keywords`). */
|
|
90
|
+
keywords?: string[];
|
|
75
91
|
}
|
|
76
92
|
|
|
77
93
|
// Tag-shaped only: a name (or closing slash/fragment) right after `<`, and no
|
|
@@ -94,7 +110,6 @@ const INLINE_PARENTS = new Set([
|
|
|
94
110
|
"strong",
|
|
95
111
|
"subscript",
|
|
96
112
|
"superscript",
|
|
97
|
-
"textDirective",
|
|
98
113
|
]);
|
|
99
114
|
|
|
100
115
|
// Nodes that never render as prose: image alt text was never indexed, MDX
|
|
@@ -120,9 +135,9 @@ interface Walk {
|
|
|
120
135
|
|
|
121
136
|
// Front matter is already off (core/frontmatter.ts) by the time a body gets
|
|
122
137
|
// here, so a body that opens with a `---` divider must read as a thematic
|
|
123
|
-
// break rather than a second front matter block.
|
|
124
|
-
const MD_PARSE = { features:
|
|
125
|
-
const MDX_PARSE = { features:
|
|
138
|
+
// break rather than a second front matter block — as the renderer reads it.
|
|
139
|
+
const MD_PARSE = { features: MARKDOWN_BODY_FEATURES };
|
|
140
|
+
const MDX_PARSE = { features: MDX_BODY_FEATURES };
|
|
126
141
|
|
|
127
142
|
const parseMdx = (markdown: string): Nodes | undefined => {
|
|
128
143
|
try {
|
|
@@ -170,6 +185,25 @@ const collectCode = (
|
|
|
170
185
|
out.push(node.value, " ");
|
|
171
186
|
};
|
|
172
187
|
|
|
188
|
+
/**
|
|
189
|
+
* A text or leaf directive as the page shows it. Blume renders neither (see
|
|
190
|
+
* `markdown/directives.ts`), and prose is full of text that parses as one —
|
|
191
|
+
* `16:9`, `og:image` — so the page shows the source the author wrote, and the
|
|
192
|
+
* index reads the same text rather than dropping the `:name` part.
|
|
193
|
+
*/
|
|
194
|
+
const directiveText = (
|
|
195
|
+
node: Extract<Nodes, { type: "leafDirective" | "textDirective" }>,
|
|
196
|
+
walk: Walk
|
|
197
|
+
): string =>
|
|
198
|
+
directiveSource(
|
|
199
|
+
// SAFETY: DirectiveNode is a structural subset of Satteri's directive
|
|
200
|
+
// nodes — a `name`, optional attributes and children, and a position.
|
|
201
|
+
node as DirectiveNode,
|
|
202
|
+
node.type === "textDirective" ? ":" : "::",
|
|
203
|
+
// The walked tree was parsed from this text, so its offsets index into it.
|
|
204
|
+
walk.downlevel.source
|
|
205
|
+
);
|
|
206
|
+
|
|
173
207
|
// Block boundaries separate words; so does an empty inline element (`<br />`,
|
|
174
208
|
// a self-closing icon), which otherwise fuses its neighbors.
|
|
175
209
|
const separatesWords = (node: Extract<Nodes, { children: unknown }>): boolean =>
|
|
@@ -223,28 +257,17 @@ const collectText = (node: Nodes, out: string[], walk: Walk): void => {
|
|
|
223
257
|
out.push(" ");
|
|
224
258
|
return;
|
|
225
259
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
260
|
+
case "textDirective": {
|
|
261
|
+
out.push(directiveText(node, walk));
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
case "leafDirective": {
|
|
265
|
+
out.push(directiveText(node, walk), " ");
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
234
268
|
case "heading": {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const inner: string[] = [];
|
|
238
|
-
const kept = trailing ? node.children.slice(0, -1) : node.children;
|
|
239
|
-
for (const child of kept) {
|
|
240
|
-
collectText(child, inner, walk);
|
|
241
|
-
}
|
|
242
|
-
if (trailing) {
|
|
243
|
-
const stripped = parseHeadingMarkers(trailing.value).text;
|
|
244
|
-
const literal = stripped === "" && node.children.length === 1;
|
|
245
|
-
inner.push(literal ? trailing.value : stripped);
|
|
246
|
-
}
|
|
247
|
-
out.push(inner.join("").trimEnd(), " ");
|
|
269
|
+
// oxlint-disable-next-line no-use-before-define -- mutual recursion: a heading's children are walked like any others
|
|
270
|
+
collectHeading(node, out, walk);
|
|
248
271
|
return;
|
|
249
272
|
}
|
|
250
273
|
default: {
|
|
@@ -265,6 +288,35 @@ const collectText = (node: Nodes, out: string[], walk: Walk): void => {
|
|
|
265
288
|
}
|
|
266
289
|
};
|
|
267
290
|
|
|
291
|
+
/**
|
|
292
|
+
* Fold a heading into the accumulator. Trailing heading markers
|
|
293
|
+
* (`[#custom-id]`, `{#custom-id}`, `[!toc]`, `[toc]`) are anchor metadata,
|
|
294
|
+
* not prose — strip them so they never pollute the index. Only a marker that
|
|
295
|
+
* ends the heading's final plain-text child counts, mirroring the renderer: a
|
|
296
|
+
* heading ending in inline code or an image keeps its bracketed text on the
|
|
297
|
+
* page (so it stays searchable), and a heading that is nothing but markers
|
|
298
|
+
* renders them literally.
|
|
299
|
+
*/
|
|
300
|
+
const collectHeading = (
|
|
301
|
+
node: Extract<Nodes, { type: "heading" }>,
|
|
302
|
+
out: string[],
|
|
303
|
+
walk: Walk
|
|
304
|
+
): void => {
|
|
305
|
+
const last = node.children.at(-1);
|
|
306
|
+
const trailing = last?.type === "text" ? last : undefined;
|
|
307
|
+
const inner: string[] = [];
|
|
308
|
+
const kept = trailing ? node.children.slice(0, -1) : node.children;
|
|
309
|
+
for (const child of kept) {
|
|
310
|
+
collectText(child, inner, walk);
|
|
311
|
+
}
|
|
312
|
+
if (trailing) {
|
|
313
|
+
const stripped = parseHeadingMarkers(trailing.value).text;
|
|
314
|
+
const literal = stripped === "" && node.children.length === 1;
|
|
315
|
+
inner.push(literal ? trailing.value : stripped);
|
|
316
|
+
}
|
|
317
|
+
out.push(inner.join("").trimEnd(), " ");
|
|
318
|
+
};
|
|
319
|
+
|
|
268
320
|
/**
|
|
269
321
|
* Reduce Markdown/MDX to plain, searchable text: parse (GFM included) and walk
|
|
270
322
|
* the tree instead of regex-stripping the source, so reference-style links,
|
|
@@ -373,6 +425,17 @@ const pageBody = async (
|
|
|
373
425
|
});
|
|
374
426
|
};
|
|
375
427
|
|
|
428
|
+
/** The ranking a page asks for: its `search.boost` and `search.keywords`. */
|
|
429
|
+
const rankingFields = (
|
|
430
|
+
search: PageRecord["meta"]["search"] | undefined
|
|
431
|
+
): Pick<SearchDocument, "boost" | "keywords"> => {
|
|
432
|
+
const { boost, keywords } = search ?? {};
|
|
433
|
+
return {
|
|
434
|
+
...(boost !== undefined && boost !== 1 && { boost }),
|
|
435
|
+
...(keywords && keywords.length > 0 && { keywords }),
|
|
436
|
+
};
|
|
437
|
+
};
|
|
438
|
+
|
|
376
439
|
/**
|
|
377
440
|
* Build search documents from the content graph. Only indexable pages are
|
|
378
441
|
* included (per the route manifest), and content comes from the source files,
|
|
@@ -490,7 +553,7 @@ export const buildSearchDocuments = async (
|
|
|
490
553
|
if (facets) {
|
|
491
554
|
document.facets = facets;
|
|
492
555
|
}
|
|
493
|
-
return document;
|
|
556
|
+
return { ...document, ...rankingFields(page?.meta?.search) };
|
|
494
557
|
})
|
|
495
558
|
);
|
|
496
559
|
};
|
|
@@ -498,13 +561,15 @@ export const buildSearchDocuments = async (
|
|
|
498
561
|
/**
|
|
499
562
|
* Map per-page search documents to the flat record shape hosted backends
|
|
500
563
|
* ingest. One record per page, keyed by route; the first tag becomes the
|
|
501
|
-
* faceting `tag
|
|
564
|
+
* faceting `tag`, and every record carries a `boost` (1 by default).
|
|
502
565
|
*/
|
|
503
566
|
export const toSearchRecords = (documents: SearchDocument[]): SearchRecord[] =>
|
|
504
567
|
documents.map((doc) => ({
|
|
505
568
|
_id: doc.route,
|
|
569
|
+
boost: doc.boost ?? 1,
|
|
506
570
|
content: doc.content,
|
|
507
571
|
description: doc.description,
|
|
572
|
+
...(doc.keywords && { keywords: doc.keywords }),
|
|
508
573
|
locale: doc.locale,
|
|
509
574
|
tag: doc.tags?.[0],
|
|
510
575
|
title: doc.title,
|
|
@@ -31,6 +31,10 @@ export interface OramaDoc {
|
|
|
31
31
|
* but not indexed, so they ride along on the returned document untouched. */
|
|
32
32
|
breadcrumb?: string[];
|
|
33
33
|
section?: string;
|
|
34
|
+
/** The page's relevance multiplier (`search.boost`); stored, not indexed. */
|
|
35
|
+
boost?: number;
|
|
36
|
+
/** Extra terms the page is found by (`search.keywords`). */
|
|
37
|
+
keywords?: string[];
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
const SCHEMA = {
|
|
@@ -42,6 +46,7 @@ const SCHEMA = {
|
|
|
42
46
|
// serves every project's facet keys, with `containsAll` matching a filter
|
|
43
47
|
// set. Derived from `facets` at insert time.
|
|
44
48
|
facetTerms: "enum[]",
|
|
49
|
+
keywords: "string[]",
|
|
45
50
|
locale: "enum",
|
|
46
51
|
route: "string",
|
|
47
52
|
title: "string",
|
|
@@ -52,8 +57,21 @@ const SCHEMA = {
|
|
|
52
57
|
const toFacetTerms = (facets: Record<string, string>): string[] =>
|
|
53
58
|
Object.entries(facets).map(([key, value]) => `${key}:${value}`);
|
|
54
59
|
|
|
55
|
-
/**
|
|
56
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Title and description outrank body text, matching the search dialog; a
|
|
62
|
+
* page's keywords count as much as its title, since its author chose them.
|
|
63
|
+
*/
|
|
64
|
+
const BOOST = { description: 2, keywords: 3, title: 3 };
|
|
65
|
+
|
|
66
|
+
/** Databases holding a boosted page, which rank by boosted score. */
|
|
67
|
+
const boostedDatabases = new WeakSet<AnyOrama>();
|
|
68
|
+
|
|
69
|
+
/** A search match as Orama's sort function sees it: id, score, document. */
|
|
70
|
+
type RankedMatch = [unknown, number, OramaDoc];
|
|
71
|
+
|
|
72
|
+
/** Rank matches by relevance times the page's `search.boost`. */
|
|
73
|
+
const byBoostedScore = (a: RankedMatch, b: RankedMatch): number =>
|
|
74
|
+
b[1] * (b[2].boost ?? 1) - a[1] * (a[2].boost ?? 1);
|
|
57
75
|
|
|
58
76
|
/**
|
|
59
77
|
* The script whose text Orama's default tokenizer keeps mostly intact. Its
|
|
@@ -322,6 +340,9 @@ export const buildOramaIndex = async (
|
|
|
322
340
|
doc.facets ? { ...doc, facetTerms: toFacetTerms(doc.facets) } : doc
|
|
323
341
|
)
|
|
324
342
|
);
|
|
343
|
+
if (documents.some((doc) => (doc.boost ?? 1) !== 1)) {
|
|
344
|
+
boostedDatabases.add(db);
|
|
345
|
+
}
|
|
325
346
|
return db;
|
|
326
347
|
};
|
|
327
348
|
|
|
@@ -391,10 +412,14 @@ export const queryOramaIndex = async (
|
|
|
391
412
|
if (facetTerms.length > 0) {
|
|
392
413
|
where.facetTerms = { containsAll: facetTerms };
|
|
393
414
|
}
|
|
415
|
+
// Boosts multiply every match's score before the limit applies (Orama
|
|
416
|
+
// sorts the whole match set with a sort function), so a boosted page can
|
|
417
|
+
// rise past matches outside the first page.
|
|
394
418
|
const unfiltered = {
|
|
395
419
|
boost: BOOST,
|
|
396
420
|
limit,
|
|
397
|
-
properties: ["title", "description", "content"],
|
|
421
|
+
properties: ["title", "keywords", "description", "content"],
|
|
422
|
+
...(boostedDatabases.has(db) && { sortBy: byBoostedScore }),
|
|
398
423
|
term,
|
|
399
424
|
};
|
|
400
425
|
const params =
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A page's `search.boost` and `search.keywords`, as Pagefind reads them from
|
|
3
|
+
* the built page. Pagefind has no page-level boost, only element weights, so
|
|
4
|
+
* the page's body carries its boost as its weight: a boosted page's matches
|
|
5
|
+
* count for more and it ranks higher. Pagefind damps repeated matches, so the
|
|
6
|
+
* lift is smaller than the full multiplier: a boost of 5 scored its page about
|
|
7
|
+
* 1.5 to 2 times higher in testing. A boost past Pagefind's largest weight,
|
|
8
|
+
* 10, counts as 10.
|
|
9
|
+
* Keywords go on an empty element as an indexed attribute, found like text
|
|
10
|
+
* and never shown.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** The largest weight Pagefind takes. */
|
|
14
|
+
const MAX_WEIGHT = 10;
|
|
15
|
+
|
|
16
|
+
/** The attribute a page's keywords ride on. */
|
|
17
|
+
export const KEYWORDS_ATTRIBUTE = "data-blume-keywords";
|
|
18
|
+
|
|
19
|
+
/** What the page body carries for Pagefind. */
|
|
20
|
+
export interface PagefindRanking {
|
|
21
|
+
/** `data-pagefind-weight` for the body, when the page is boosted. */
|
|
22
|
+
weight?: string;
|
|
23
|
+
/** The keywords, space-separated, when the page sets any. */
|
|
24
|
+
keywords?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const pagefindRanking = (search: {
|
|
28
|
+
boost?: number;
|
|
29
|
+
keywords?: string[];
|
|
30
|
+
}): PagefindRanking => {
|
|
31
|
+
const { boost, keywords } = search;
|
|
32
|
+
return {
|
|
33
|
+
...(boost !== undefined &&
|
|
34
|
+
boost !== 1 && {
|
|
35
|
+
weight: String(Math.min(boost, MAX_WEIGHT)),
|
|
36
|
+
}),
|
|
37
|
+
...(keywords && keywords.length > 0 && { keywords: keywords.join(" ") }),
|
|
38
|
+
};
|
|
39
|
+
};
|
package/src/search/popular.ts
CHANGED
|
@@ -17,7 +17,8 @@ export interface SearchPopularPage {
|
|
|
17
17
|
* `page.route`). `withBasePath` is idempotent and skips external URLs.
|
|
18
18
|
*
|
|
19
19
|
* `deployment.base` is deliberately *not* applied: routes stay deploy-base-less
|
|
20
|
-
* so `
|
|
20
|
+
* so `Search.astro` can add it when it renders the entry, same as
|
|
21
|
+
* sidebar-derived pages.
|
|
21
22
|
*
|
|
22
23
|
* `icon` stays a name here (like `navigation.featured`): the icon set is a
|
|
23
24
|
* server-only module, so `Search.astro` resolves it to inline SVG at render.
|
|
@@ -16,6 +16,9 @@ export type AlgoliaSyncConfig = Pick<AlgoliaOptions, "appId" | "indexName">;
|
|
|
16
16
|
*/
|
|
17
17
|
const FILTER_ATTRIBUTES = ["locale", "version"] as const;
|
|
18
18
|
|
|
19
|
+
/** The custom ranking rule that orders close matches by `search.boost`. */
|
|
20
|
+
const BOOST_RANKING = "desc(boost)";
|
|
21
|
+
|
|
19
22
|
/** A faceting declaration's attribute: `filterOnly(locale)` → `locale`. */
|
|
20
23
|
const facetAttribute = (declaration: string): string =>
|
|
21
24
|
declaration.replace(/^\w+\((?<name>.*)\)$/u, "$<name>");
|
|
@@ -48,12 +51,17 @@ export const syncAlgolia = async (
|
|
|
48
51
|
indexName,
|
|
49
52
|
objects: records.map((record) => ({ ...record, objectID: record._id })),
|
|
50
53
|
});
|
|
51
|
-
const { attributesForFaceting = []
|
|
52
|
-
indexName
|
|
53
|
-
});
|
|
54
|
+
const { attributesForFaceting = [], customRanking = [] } =
|
|
55
|
+
await client.getSettings({ indexName });
|
|
54
56
|
const declared = new Set(attributesForFaceting.map(facetAttribute));
|
|
55
57
|
const missing = FILTER_ATTRIBUTES.filter((name) => !declared.has(name));
|
|
56
|
-
|
|
58
|
+
// Every record carries `boost` (search.boost, 1 by default): ranking by it
|
|
59
|
+
// after textual relevance puts boosted pages first among close matches.
|
|
60
|
+
// A ranking the site set in the dashboard keeps its place ahead of it.
|
|
61
|
+
const ranksByBoost = customRanking.some((rule) =>
|
|
62
|
+
/^(?:asc|desc)\(boost\)$/u.test(rule)
|
|
63
|
+
);
|
|
64
|
+
if (missing.length === 0 && ranksByBoost) {
|
|
57
65
|
return;
|
|
58
66
|
}
|
|
59
67
|
const { taskID } = await client.setSettings({
|
|
@@ -63,6 +71,9 @@ export const syncAlgolia = async (
|
|
|
63
71
|
...attributesForFaceting,
|
|
64
72
|
...missing.map((name) => `filterOnly(${name})`),
|
|
65
73
|
],
|
|
74
|
+
customRanking: ranksByBoost
|
|
75
|
+
? customRanking
|
|
76
|
+
: [...customRanking, BOOST_RANKING],
|
|
66
77
|
},
|
|
67
78
|
});
|
|
68
79
|
await client.waitForTask({ indexName, taskID });
|
|
@@ -31,9 +31,12 @@ export const syncOramaCloud = async (
|
|
|
31
31
|
const index = manager.index(config.indexId);
|
|
32
32
|
await index.snapshot(
|
|
33
33
|
records.map((record) => ({
|
|
34
|
+
// The dialog multiplies each hit's score by it (search.boost).
|
|
35
|
+
boost: record.boost,
|
|
34
36
|
content: record.content,
|
|
35
37
|
description: record.description,
|
|
36
38
|
id: record._id,
|
|
39
|
+
keywords: record.keywords ?? [],
|
|
37
40
|
// Carried so an i18n site can filter hosted results per language.
|
|
38
41
|
locale: record.locale,
|
|
39
42
|
tag: record.tag,
|
|
@@ -57,6 +57,9 @@ export const syncTypesense = async (
|
|
|
57
57
|
{ name: "description", optional: true, type: "string" },
|
|
58
58
|
{ name: "content", type: "string" },
|
|
59
59
|
{ name: "url", type: "string" },
|
|
60
|
+
{ name: "keywords", optional: true, type: "string[]" },
|
|
61
|
+
// The dialog sorts close matches by it (search.boost, 1 by default).
|
|
62
|
+
{ name: "boost", type: "float" },
|
|
60
63
|
{ facet: true, name: "tag", optional: true, type: "string" },
|
|
61
64
|
// Carried as facets so hosted results can filter per language and per
|
|
62
65
|
// docs version (the SearchRecord contract; current docs = "current").
|
|
@@ -67,9 +70,11 @@ export const syncTypesense = async (
|
|
|
67
70
|
});
|
|
68
71
|
|
|
69
72
|
const documents = records.map((record) => ({
|
|
73
|
+
boost: record.boost,
|
|
70
74
|
content: record.content,
|
|
71
75
|
description: record.description,
|
|
72
76
|
id: record._id,
|
|
77
|
+
keywords: record.keywords ?? [],
|
|
73
78
|
locale: record.locale,
|
|
74
79
|
tag: record.tag,
|
|
75
80
|
title: record.title,
|
package/src/seo/jsonld.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { Crumb } from "../components/layout/nav-utils.ts";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
mountBasePath,
|
|
4
|
+
normalizeBasePath,
|
|
5
|
+
withBasePath,
|
|
6
|
+
} from "../core/base-path.ts";
|
|
3
7
|
|
|
4
8
|
/** A date-ish value carried through frontmatter (string, YAML Date, or unset). */
|
|
5
9
|
type DateInput = string | Date | null;
|
|
@@ -301,7 +305,7 @@ const breadcrumbNode = (
|
|
|
301
305
|
"@type": "BreadcrumbList",
|
|
302
306
|
itemListElement: linked.map((crumb, index) => ({
|
|
303
307
|
"@type": "ListItem",
|
|
304
|
-
item: absolute(base,
|
|
308
|
+
item: absolute(base, mountBasePath(deployBase, crumb.route)),
|
|
305
309
|
name: crumb.label,
|
|
306
310
|
position: index + 1,
|
|
307
311
|
})),
|
|
@@ -323,7 +327,7 @@ export const buildStructuredData = (
|
|
|
323
327
|
// Routes carry `basePath`; a `deployment.base` subdirectory is layered on top
|
|
324
328
|
// so JSON-LD URLs match the served location.
|
|
325
329
|
const deployBase = normalizeBasePath(input.base);
|
|
326
|
-
const pageUrl = absolute(base,
|
|
330
|
+
const pageUrl = absolute(base, mountBasePath(deployBase, input.route));
|
|
327
331
|
const rootUrl = absolute(base, deployBase);
|
|
328
332
|
const graph: JsonLdNode[] = [];
|
|
329
333
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Site-wide meta tags (`seo.metatags`): tags written into every page's head,
|
|
3
|
+
* for what Blume has no setting of its own for, such as site-verification
|
|
4
|
+
* tokens, `theme-color`, or an app banner. The tags Blume writes itself are
|
|
5
|
+
* refused, each pointing at the setting that controls it, so a page never
|
|
6
|
+
* carries two values for one tag.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const IMAGE =
|
|
10
|
+
"`seo.og` for the generated cards, or `seo.image` in a page's frontmatter";
|
|
11
|
+
const TITLE = "`title`, or `seo.title` in a page's frontmatter";
|
|
12
|
+
const DESCRIPTION =
|
|
13
|
+
"`description` in a page's frontmatter or the config, or a page's `seo.description`";
|
|
14
|
+
const FROM_PAGE = "Blume sets it from each page's type and dates";
|
|
15
|
+
|
|
16
|
+
/** The tags Blume writes itself, by lowercase name, and what sets each. */
|
|
17
|
+
const OWNED = new Map([
|
|
18
|
+
["article:modified_time", FROM_PAGE],
|
|
19
|
+
["article:published_time", FROM_PAGE],
|
|
20
|
+
["description", DESCRIPTION],
|
|
21
|
+
["og:description", DESCRIPTION],
|
|
22
|
+
["og:site_name", "the config's `title`"],
|
|
23
|
+
["og:title", TITLE],
|
|
24
|
+
["og:type", FROM_PAGE],
|
|
25
|
+
["og:url", "`deployment.site`, which sets each page's canonical URL"],
|
|
26
|
+
["robots", "`seo.noindex` in a page's frontmatter"],
|
|
27
|
+
["twitter:card", "Blume sets it from each page's image"],
|
|
28
|
+
["twitter:creator", "`seo.x.creator` in the config or a page's frontmatter"],
|
|
29
|
+
["twitter:description", DESCRIPTION],
|
|
30
|
+
["twitter:site", "`seo.x.handle`"],
|
|
31
|
+
["twitter:title", TITLE],
|
|
32
|
+
["viewport", "Blume sets it for every page"],
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
/** `og:image` and `twitter:image`, and their sub-properties. */
|
|
36
|
+
const IMAGE_TAG = /^(?:og|twitter):image(?::|$)/u;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* What sets a tag Blume writes itself, or `undefined` for a tag the site can
|
|
40
|
+
* add.
|
|
41
|
+
*/
|
|
42
|
+
export const ownedMetatag = (name: string): string | undefined => {
|
|
43
|
+
const key = name.trim().toLowerCase();
|
|
44
|
+
return IMAGE_TAG.test(key) ? IMAGE : OWNED.get(key);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The Open Graph families, which the protocol names with `property`; every
|
|
49
|
+
* other tag takes `name`.
|
|
50
|
+
*/
|
|
51
|
+
const PROPERTY_TAG =
|
|
52
|
+
/^(?:og|fb|article|book|profile|music|video|al|product):/iu;
|
|
53
|
+
|
|
54
|
+
/** One site-wide tag, as its `<meta>` element's attributes. */
|
|
55
|
+
export interface MetatagAttributes {
|
|
56
|
+
content: string;
|
|
57
|
+
name?: string;
|
|
58
|
+
property?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The site's `seo.metatags` as `<meta>` attributes, in the order written. */
|
|
62
|
+
export const metatagAttributes = (
|
|
63
|
+
tags?: Readonly<Record<string, string>> | null
|
|
64
|
+
): MetatagAttributes[] =>
|
|
65
|
+
Object.entries(tags ?? {}).map(([name, content]) =>
|
|
66
|
+
PROPERTY_TAG.test(name) ? { content, property: name } : { content, name }
|
|
67
|
+
);
|
|
@@ -19,6 +19,7 @@ interface ChromeIconSet {
|
|
|
19
19
|
|
|
20
20
|
export const chromeIcons: ChromeIconSet = {
|
|
21
21
|
check: '<path d="M20 6 9 17l-5-5"/>',
|
|
22
|
+
"chevron-down": '<path d="m6 9 6 6 6-6"/>',
|
|
22
23
|
copy: '<rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>',
|
|
23
24
|
file: '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/>',
|
|
24
25
|
search: '<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>',
|