blume 2.0.3 → 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 +48 -0
- package/README.md +1 -1
- package/dist/cli/{chunk-1d7ve1dm.js → chunk-0gz36akr.js} +18 -2
- package/dist/cli/{chunk-1d7ve1dm.js.map → chunk-0gz36akr.js.map} +3 -3
- package/dist/cli/{chunk-2eytanqx.js → chunk-1n228xqb.js} +2 -2
- package/dist/cli/{chunk-yfyb25rh.js → chunk-2f3ndkpw.js} +24 -8
- package/dist/cli/chunk-2f3ndkpw.js.map +14 -0
- package/dist/cli/{chunk-r20tn01b.js → chunk-2f7n4qzt.js} +8 -2
- package/dist/cli/{chunk-r20tn01b.js.map → chunk-2f7n4qzt.js.map} +3 -3
- package/dist/cli/{chunk-35d4wj9f.js → chunk-2n4ps1b3.js} +7 -7
- package/dist/cli/{chunk-j85vccga.js → chunk-43zcf4gz.js} +97 -57
- package/dist/cli/{chunk-j85vccga.js.map → chunk-43zcf4gz.js.map} +6 -6
- package/dist/cli/{chunk-e04dxsz1.js → chunk-4ejxbzee.js} +1 -1
- package/dist/cli/{chunk-5m5nmvyq.js → chunk-4h8z9vv5.js} +233 -39
- package/dist/cli/chunk-4h8z9vv5.js.map +16 -0
- package/dist/cli/{chunk-5f86nr5m.js → chunk-5cg2rzgf.js} +18 -16
- package/dist/cli/{chunk-5f86nr5m.js.map → chunk-5cg2rzgf.js.map} +2 -2
- package/dist/cli/{chunk-tzmab476.js → chunk-5ffywsmb.js} +4 -4
- package/dist/cli/{chunk-2z928egk.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-vg9r4eb9.js → chunk-713mngdq.js} +12 -5
- package/dist/cli/{chunk-vg9r4eb9.js.map → chunk-713mngdq.js.map} +3 -3
- package/dist/cli/{chunk-5xvm6tfj.js → chunk-9geymqns.js} +17 -15
- package/dist/cli/{chunk-5xvm6tfj.js.map → chunk-9geymqns.js.map} +2 -2
- package/dist/cli/{chunk-q58y5e6a.js → chunk-9xr4dgee.js} +26 -17
- package/dist/cli/chunk-9xr4dgee.js.map +10 -0
- package/dist/cli/{chunk-b07cmahc.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-akbpwfxc.js → chunk-cmtcnh9f.js} +2 -2
- package/dist/cli/chunk-cy5bm0j0.js +104 -0
- package/dist/cli/chunk-cy5bm0j0.js.map +11 -0
- package/dist/cli/{chunk-crgn1q09.js → chunk-e7vvk5ya.js} +4 -4
- package/dist/cli/{chunk-pehfxfta.js → chunk-g2z9x01v.js} +6 -98
- package/dist/cli/chunk-g2z9x01v.js.map +10 -0
- package/dist/cli/{chunk-tkacnehg.js → chunk-gp84za0h.js} +2 -2
- package/dist/cli/{chunk-wrr3j9w9.js → chunk-h2a48yhd.js} +186 -63
- package/dist/cli/chunk-h2a48yhd.js.map +36 -0
- package/dist/cli/{chunk-hm3vjy5s.js → chunk-jnwj5kjj.js} +14 -9
- package/dist/cli/{chunk-hm3vjy5s.js.map → chunk-jnwj5kjj.js.map} +4 -4
- package/dist/cli/chunk-kwrg63tm.js +31 -0
- package/dist/cli/chunk-kwrg63tm.js.map +10 -0
- package/dist/cli/{chunk-2hsdwb9n.js → chunk-nd1821sk.js} +18 -16
- package/dist/cli/{chunk-2hsdwb9n.js.map → chunk-nd1821sk.js.map} +2 -2
- package/dist/cli/{chunk-6dsbexzp.js → chunk-ntv4qrt7.js} +17 -15
- package/dist/cli/{chunk-6dsbexzp.js.map → chunk-ntv4qrt7.js.map} +2 -2
- package/dist/cli/{chunk-r9rcc4w7.js → chunk-p804f3pe.js} +7 -7
- package/dist/cli/{chunk-ey84smr6.js → chunk-py62fewh.js} +6 -6
- package/dist/cli/{chunk-ey84smr6.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-9t7a85s3.js → chunk-rekqz4qg.js} +23 -13
- package/dist/cli/chunk-rekqz4qg.js.map +10 -0
- package/dist/cli/{chunk-8ktnccpt.js → chunk-ta27feev.js} +1 -1
- package/dist/cli/{chunk-pv29h0wf.js → chunk-xkz5xbtv.js} +3476 -822
- package/dist/cli/chunk-xkz5xbtv.js.map +213 -0
- package/dist/cli/{chunk-a58773jm.js → chunk-y4ykbark.js} +2 -2
- package/dist/cli/{chunk-p3v96n38.js → chunk-y5h4r2y0.js} +4 -4
- package/dist/cli/{chunk-acanzt5p.js → chunk-yj17vs39.js} +6 -6
- package/dist/cli/{chunk-p73c0m7w.js → chunk-yxmk4pec.js} +16 -14
- package/dist/cli/{chunk-p73c0m7w.js.map → chunk-yxmk4pec.js.map} +2 -2
- package/dist/cli/{chunk-3em5wd2y.js → chunk-zccbjcj0.js} +4 -3
- package/dist/cli/{chunk-3em5wd2y.js.map → chunk-zccbjcj0.js.map} +2 -2
- package/dist/cli/{chunk-c8chx29p.js → chunk-zf748avv.js} +384 -207
- package/dist/cli/chunk-zf748avv.js.map +37 -0
- package/dist/cli/{chunk-g4hq16wv.js → chunk-zhvd6adm.js} +16 -14
- package/dist/cli/{chunk-g4hq16wv.js.map → chunk-zhvd6adm.js.map} +2 -2
- package/dist/cli/index.js +26 -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/site-skill.d.ts +26 -0
- package/dist/types/ai/skills.d.ts +16 -0
- package/dist/types/astro/integration.d.ts +8 -0
- 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/config-input.d.ts +187 -20
- package/dist/types/core/data.d.ts +49 -4
- package/dist/types/core/footer.d.ts +8 -0
- package/dist/types/core/i18n-ui.d.ts +86 -0
- package/dist/types/core/includes.d.ts +8 -2
- package/dist/types/core/localizable.d.ts +7 -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 +551 -56
- package/dist/types/core/sources/read.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/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/vercel.d.ts +6 -0
- package/dist/types/deploy/redirects.d.ts +39 -11
- package/dist/types/deploy/vercel-negotiation.d.ts +9 -0
- package/dist/types/markdown/code-title.d.ts +17 -0
- package/dist/types/markdown/features.d.ts +3 -2
- 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/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 +41 -5
- package/docs/03-upgrading.mdx +4 -4
- package/docs/04-migrating.mdx +1 -1
- package/docs/advanced/changelog.mdx +2 -2
- package/docs/advanced/skills.mdx +14 -0
- package/docs/cli/audit.mdx +21 -1
- package/docs/cli/index.mdx +1 -0
- package/docs/configuration/analytics.mdx +3 -1
- package/docs/configuration/assistant.mdx +188 -36
- package/docs/configuration/consent.mdx +100 -0
- package/docs/configuration/customization.mdx +4 -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 +29 -1
- package/docs/configuration/theming.mdx +3 -1
- package/docs/content/components.mdx +106 -0
- package/docs/content/frontmatter.mdx +76 -0
- package/docs/content/i18n.mdx +38 -3
- package/docs/content/includes.mdx +16 -0
- package/docs/content/meta.mdx +1 -0
- 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 +67 -2
- package/docs/content/variables.mdx +41 -0
- package/docs/discoverability/agent-discovery.mdx +29 -2
- package/docs/discoverability/index.mdx +1 -1
- package/docs/discoverability/llms-txt.mdx +1 -1
- package/docs/discoverability/metadata.mdx +17 -0
- package/docs/references/api-pages.mdx +74 -0
- package/docs/references/asyncapi.mdx +1 -1
- package/docs/references/graphql.mdx +2 -2
- package/docs/references/meta.ts +1 -1
- package/docs/references/openapi.mdx +102 -3
- 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/mintlify.md +37 -19
- package/skills/blume-migrate/references/nextra.md +2 -2
- package/skills/blume-migrate/references/starlight.md +2 -1
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +60 -14
- package/skills/blume-write-skill/SKILL.md +82 -0
- package/src/ai/agent-readability.ts +2 -1
- package/src/ai/agent-surface.ts +9 -7
- package/src/ai/ai-catalog.ts +6 -3
- 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/component-markdown.ts +75 -0
- package/src/ai/cors.ts +2 -0
- package/src/ai/index.ts +12 -0
- package/src/ai/llms.ts +6 -2
- package/src/ai/markdown.ts +2 -1
- package/src/ai/mcp/data.ts +6 -0
- package/src/ai/openapi-components.ts +9 -3
- package/src/ai/site-skill.ts +274 -0
- package/src/ai/skills.ts +35 -2
- package/src/ai/write-skill.ts +97 -0
- package/src/astro/generate.ts +132 -18
- package/src/astro/index.ts +1 -0
- package/src/astro/integration.ts +35 -0
- package/src/astro/module-types.ts +6 -0
- package/src/astro/templates.ts +283 -30
- 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 +5 -12
- package/src/audit/checks/sitemap.ts +2 -3
- package/src/audit/redirects.ts +46 -15
- package/src/audit/run.ts +2 -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/command-meta.ts +5 -0
- package/src/cli/commands/doctor.ts +22 -16
- package/src/cli/commands/skill.ts +96 -0
- package/src/cli/index.ts +5 -0
- package/src/cli/required-secrets.ts +71 -29
- package/src/components/BlumePage.astro +2 -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/Color.astro +1 -0
- 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/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/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 +104 -14
- package/src/components/islands/code-question.ts +62 -0
- package/src/components/islands/hooks.ts +110 -21
- package/src/components/islands/support-link.ts +68 -0
- package/src/components/layout/Analytics.astro +11 -2
- package/src/components/layout/Banner.astro +14 -10
- package/src/components/layout/Breadcrumbs.astro +6 -2
- 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/DrawerSwitchers.astro +51 -0
- package/src/components/layout/Empty.astro +2 -2
- package/src/components/layout/Header.astro +122 -80
- package/src/components/layout/LanguageSwitcher.astro +48 -15
- package/src/components/layout/NarrationPlayer.astro +1085 -0
- package/src/components/layout/NavSelector.astro +33 -7
- package/src/components/layout/PageFeedback.astro +54 -49
- package/src/components/layout/PageLayout.astro +71 -3
- package/src/components/layout/ReferenceLayout.astro +17 -6
- package/src/components/layout/RelatedPages.astro +42 -0
- package/src/components/layout/RootLayout.astro +308 -45
- package/src/components/layout/Search.astro +31 -17
- package/src/components/layout/SiteFooter.astro +124 -0
- package/src/components/layout/SiteMetatags.astro +16 -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/openapi/Operation.astro +161 -13
- package/src/components/openapi/RequestBody.astro +6 -4
- package/src/components/openapi/RequestPanel.astro +59 -7
- package/src/components/openapi/Responses.astro +6 -4
- package/src/components/openapi/async-snippets.ts +4 -4
- package/src/components/openapi/code-samples.ts +97 -0
- package/src/components/openapi/security.ts +6 -3
- package/src/components/openapi/snippets.ts +869 -8
- 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/builtin-tags.ts +5 -0
- package/src/core/client-address.ts +19 -0
- package/src/core/config-input.ts +194 -20
- package/src/core/data.ts +44 -4
- package/src/core/diagnostics.ts +3 -2
- package/src/core/footer.ts +26 -0
- package/src/core/graph.ts +39 -36
- package/src/core/i18n-ui.ts +82 -2
- package/src/core/includes.ts +20 -3
- package/src/core/links.ts +16 -2
- package/src/core/localizable.ts +26 -0
- package/src/core/navigation.ts +43 -12
- package/src/core/page-modes.ts +56 -0
- package/src/core/project-graph.ts +111 -5
- package/src/core/redirect-patterns.ts +491 -0
- package/src/core/related.ts +131 -0
- package/src/core/schema.ts +318 -64
- package/src/core/sources/normalize.ts +2 -1
- package/src/core/sources/read.ts +8 -3
- 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/deploy/artifacts.ts +77 -8
- package/src/deploy/cloudflare-negotiation.ts +35 -5
- package/src/deploy/headers.ts +2 -1
- package/src/deploy/node-headers.ts +63 -9
- package/src/deploy/platforms/cloudflare.ts +32 -0
- package/src/deploy/platforms/netlify.ts +33 -1
- package/src/deploy/platforms/vercel.ts +37 -2
- package/src/deploy/redirects.ts +91 -24
- package/src/deploy/vercel-negotiation.ts +36 -0
- package/src/markdown/api-rail.ts +259 -0
- package/src/markdown/code-title.ts +35 -9
- package/src/markdown/features.ts +3 -2
- package/src/markdown/fence-meta.ts +2 -0
- package/src/markdown/include.ts +11 -1
- package/src/markdown/index.ts +28 -5
- package/src/markdown/math.ts +4 -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 +7 -6
- package/src/openapi/model.ts +57 -24
- package/src/openapi/overlay.ts +256 -0
- package/src/openapi/parse.ts +55 -2
- package/src/openapi/references.ts +21 -5
- package/src/openapi/render-mdx.ts +5 -1
- 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 +20 -4
- package/src/registry/registry.ts +7 -0
- package/src/search/documents.ts +26 -2
- package/src/search/orama-index.ts +28 -3
- package/src/search/pagefind-ranking.ts +39 -0
- 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/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/prompts.ts +1 -0
- package/src/translate/validate.ts +8 -2
- package/src/upgrade/upgrade.ts +4 -171
- package/dist/cli/chunk-364znk6q.js +0 -75
- package/dist/cli/chunk-364znk6q.js.map +0 -11
- package/dist/cli/chunk-5m5nmvyq.js.map +0 -13
- package/dist/cli/chunk-9t7a85s3.js.map +0 -10
- package/dist/cli/chunk-c8chx29p.js.map +0 -36
- package/dist/cli/chunk-pehfxfta.js.map +0 -10
- package/dist/cli/chunk-pv29h0wf.js.map +0 -190
- package/dist/cli/chunk-q58y5e6a.js.map +0 -10
- package/dist/cli/chunk-wrr3j9w9.js.map +0 -35
- package/dist/cli/chunk-yfyb25rh.js.map +0 -14
- package/docs/content/sources.mdx +0 -334
- /package/dist/cli/{chunk-2eytanqx.js.map → chunk-1n228xqb.js.map} +0 -0
- /package/dist/cli/{chunk-35d4wj9f.js.map → chunk-2n4ps1b3.js.map} +0 -0
- /package/dist/cli/{chunk-e04dxsz1.js.map → chunk-4ejxbzee.js.map} +0 -0
- /package/dist/cli/{chunk-tzmab476.js.map → chunk-5ffywsmb.js.map} +0 -0
- /package/dist/cli/{chunk-2z928egk.js.map → chunk-5wj2qwn2.js.map} +0 -0
- /package/dist/cli/{chunk-b07cmahc.js.map → chunk-amrb6hgh.js.map} +0 -0
- /package/dist/cli/{chunk-akbpwfxc.js.map → chunk-cmtcnh9f.js.map} +0 -0
- /package/dist/cli/{chunk-crgn1q09.js.map → chunk-e7vvk5ya.js.map} +0 -0
- /package/dist/cli/{chunk-tkacnehg.js.map → chunk-gp84za0h.js.map} +0 -0
- /package/dist/cli/{chunk-r9rcc4w7.js.map → chunk-p804f3pe.js.map} +0 -0
- /package/dist/cli/{chunk-8ktnccpt.js.map → chunk-ta27feev.js.map} +0 -0
- /package/dist/cli/{chunk-a58773jm.js.map → chunk-y4ykbark.js.map} +0 -0
- /package/dist/cli/{chunk-p3v96n38.js.map → chunk-y5h4r2y0.js.map} +0 -0
- /package/dist/cli/{chunk-acanzt5p.js.map → chunk-yj17vs39.js.map} +0 -0
|
@@ -270,6 +270,89 @@ export const getPagination = (flat: FlatPage[], route: string) => {
|
|
|
270
270
|
};
|
|
271
271
|
};
|
|
272
272
|
|
|
273
|
+
/** A group node in the sidebar tree. */
|
|
274
|
+
export type NavGroup = Extract<NavNode, { kind: "group" }>;
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* The group whose own page `route` is, when that page lists the group's
|
|
278
|
+
* pages: its `directory` (inherited when the group sets none) is `card` or
|
|
279
|
+
* `accordion`. `null` on every other page.
|
|
280
|
+
*/
|
|
281
|
+
export const directoryGroupForRoute = (
|
|
282
|
+
nodes: NavNode[],
|
|
283
|
+
route: string
|
|
284
|
+
): NavGroup | null => {
|
|
285
|
+
for (const node of nodes) {
|
|
286
|
+
if (node.kind !== "group") {
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (
|
|
290
|
+
node.route === route &&
|
|
291
|
+
(node.directory === "card" || node.directory === "accordion")
|
|
292
|
+
) {
|
|
293
|
+
return node;
|
|
294
|
+
}
|
|
295
|
+
const nested = directoryGroupForRoute(node.children, route);
|
|
296
|
+
if (nested) {
|
|
297
|
+
return nested;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return null;
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
/** One entry in a group page's directory: a page, or a subgroup and its pages. */
|
|
304
|
+
export interface DirectoryEntry {
|
|
305
|
+
description?: string;
|
|
306
|
+
/** The page, or the subgroup's own page (its first page when it has none). */
|
|
307
|
+
href: string;
|
|
308
|
+
icon?: string;
|
|
309
|
+
label: string;
|
|
310
|
+
/** A subgroup's pages in sidebar order, its own page first; absent for a page. */
|
|
311
|
+
pages?: FlatPage[];
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* What a group's page lists: the group's pages and subgroups, in sidebar
|
|
316
|
+
* order, without the page itself or external links. A subgroup is described
|
|
317
|
+
* by its own page, and one with no page to link to is left out.
|
|
318
|
+
*/
|
|
319
|
+
export const directoryEntries = (group: NavGroup): DirectoryEntry[] =>
|
|
320
|
+
group.children.flatMap((child): DirectoryEntry[] => {
|
|
321
|
+
if (child.kind === "page") {
|
|
322
|
+
return child.pageId && child.route !== group.route
|
|
323
|
+
? [
|
|
324
|
+
{
|
|
325
|
+
description: child.description,
|
|
326
|
+
href: child.route,
|
|
327
|
+
icon: child.icon,
|
|
328
|
+
label: child.label,
|
|
329
|
+
},
|
|
330
|
+
]
|
|
331
|
+
: [];
|
|
332
|
+
}
|
|
333
|
+
const listed = flattenPages(child.children);
|
|
334
|
+
const pages =
|
|
335
|
+
child.route && !listed.some((page) => page.route === child.route)
|
|
336
|
+
? [{ label: child.label, route: child.route }, ...listed]
|
|
337
|
+
: listed;
|
|
338
|
+
const [first] = pages;
|
|
339
|
+
if (!first) {
|
|
340
|
+
return [];
|
|
341
|
+
}
|
|
342
|
+
const own = child.children.find(
|
|
343
|
+
(node) => node.kind === "page" && node.route === child.route
|
|
344
|
+
);
|
|
345
|
+
return [
|
|
346
|
+
{
|
|
347
|
+
description: own?.kind === "page" ? own.description : undefined,
|
|
348
|
+
href: child.route ?? first.route,
|
|
349
|
+
icon: child.icon,
|
|
350
|
+
label: child.label,
|
|
351
|
+
pages,
|
|
352
|
+
},
|
|
353
|
+
];
|
|
354
|
+
});
|
|
355
|
+
|
|
273
356
|
/**
|
|
274
357
|
* A stable id for every group in a sidebar — `g<n>` by pre-order position in
|
|
275
358
|
* the full tree. The layout hands `NavTree` a scoped view of that tree (a
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { track } from "./analytics-client.ts";
|
|
2
|
+
/**
|
|
3
|
+
* The "Was this page helpful?" rating (`PageFeedback.astro`). A rating is
|
|
4
|
+
* sent as a `feedback` analytics event, the buttons give way to a thank-you,
|
|
5
|
+
* and, with `feedback.comments` on, a box below asks the reader to say more.
|
|
6
|
+
* A comment they send goes out as a `feedback_comment` event with the same
|
|
7
|
+
* rating, path, and title, so the two line up in a dashboard.
|
|
8
|
+
*
|
|
9
|
+
* Both go through analytics, so under a consent layer (`consent`) the widget
|
|
10
|
+
* renders hidden and only shows once the reader allows analytics: from
|
|
11
|
+
* anyone else, an answer would go nowhere.
|
|
12
|
+
*
|
|
13
|
+
* Runs on the initial load and again after every client-router swap, which
|
|
14
|
+
* rebuilds the widget from server-rendered markup, so the handlers always
|
|
15
|
+
* bind to the freshly swapped-in elements.
|
|
16
|
+
*/
|
|
17
|
+
import type { BlumeConsent } from "./consent/types.ts";
|
|
18
|
+
|
|
19
|
+
/** The longest comment the box takes. */
|
|
20
|
+
export const COMMENT_MAX_LENGTH = 1000;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Whether an answer would reach analytics: the site has no consent layer, or
|
|
24
|
+
* the reader has allowed analytics.
|
|
25
|
+
*/
|
|
26
|
+
const answersReachAnalytics = (): boolean => {
|
|
27
|
+
// SAFETY: the consent init script (`consent/init.ts`) is the only writer of
|
|
28
|
+
// `window.blumeConsent`; without a consent layer it's absent.
|
|
29
|
+
const { blumeConsent } = window as Window & { blumeConsent?: BlumeConsent };
|
|
30
|
+
return blumeConsent === undefined || blumeConsent.analytics === true;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Show the page's rating only while its answers would reach analytics. Runs
|
|
35
|
+
* when the widget is wired and again on every `blume:consent` change, so a
|
|
36
|
+
* reader who accepts the banner gets the rating right away.
|
|
37
|
+
*/
|
|
38
|
+
export const syncFeedbackVisibility = (): void => {
|
|
39
|
+
const root = document.querySelector<HTMLElement>(
|
|
40
|
+
"[data-blume-page-feedback]"
|
|
41
|
+
);
|
|
42
|
+
if (root) {
|
|
43
|
+
root.hidden = !answersReachAnalytics();
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/** Wire the page's rating, when it has one. */
|
|
48
|
+
export const initFeedback = (): void => {
|
|
49
|
+
const root = document.querySelector("[data-blume-page-feedback]");
|
|
50
|
+
if (!root) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
syncFeedbackVisibility();
|
|
54
|
+
const actions = root.querySelector("[data-feedback-actions]");
|
|
55
|
+
const thanks = root.querySelector<HTMLElement>("[data-feedback-thanks]");
|
|
56
|
+
const form = root.querySelector<HTMLFormElement>("[data-feedback-form]");
|
|
57
|
+
let helpful = "";
|
|
58
|
+
const rate = (value: string) => {
|
|
59
|
+
helpful = value;
|
|
60
|
+
track("feedback", {
|
|
61
|
+
helpful,
|
|
62
|
+
path: location.pathname,
|
|
63
|
+
title: document.title,
|
|
64
|
+
});
|
|
65
|
+
actions?.classList.add("hidden");
|
|
66
|
+
thanks?.classList.remove("hidden");
|
|
67
|
+
if (form) {
|
|
68
|
+
form.hidden = false;
|
|
69
|
+
}
|
|
70
|
+
// The clicked button just disappeared under focus; hand focus to the
|
|
71
|
+
// (live) thanks line so the reader isn't dropped at the top of the page.
|
|
72
|
+
// The comment box is the next stop from there.
|
|
73
|
+
thanks?.focus();
|
|
74
|
+
};
|
|
75
|
+
for (const button of root.querySelectorAll<HTMLElement>(
|
|
76
|
+
"[data-feedback-value]"
|
|
77
|
+
)) {
|
|
78
|
+
button.addEventListener("click", () =>
|
|
79
|
+
rate(button.dataset.feedbackValue ?? "")
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
form?.addEventListener("submit", (event) => {
|
|
83
|
+
event.preventDefault();
|
|
84
|
+
const comment = form.querySelector("textarea")?.value.trim() ?? "";
|
|
85
|
+
if (comment) {
|
|
86
|
+
track("feedback_comment", {
|
|
87
|
+
comment: comment.slice(0, COMMENT_MAX_LENGTH),
|
|
88
|
+
helpful,
|
|
89
|
+
path: location.pathname,
|
|
90
|
+
title: document.title,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
form.hidden = true;
|
|
94
|
+
thanks?.focus();
|
|
95
|
+
});
|
|
96
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search analytics: what readers search for, what they find, and what they
|
|
3
|
+
* open, sent through the configured analytics adapters (`track`).
|
|
4
|
+
*
|
|
5
|
+
* The dialog searches on every keystroke, so a query is only recorded once it
|
|
6
|
+
* settles: a second without typing, or the reader picking a result or closing
|
|
7
|
+
* the dialog. Each settled query is one `search` event with the number of
|
|
8
|
+
* results, so a dashboard can list the queries that found nothing. Picking a
|
|
9
|
+
* result is a `search_select` event with its position, for click-through. The
|
|
10
|
+
* same query settling twice in a row (a filter toggled back and forth) counts
|
|
11
|
+
* once.
|
|
12
|
+
*/
|
|
13
|
+
import type { TrackProps } from "../analytics-client.ts";
|
|
14
|
+
|
|
15
|
+
/** The longest query sent: past this it's rarely a search term. */
|
|
16
|
+
export const MAX_QUERY_CHARS = 100;
|
|
17
|
+
|
|
18
|
+
/** How long a query sits untouched before it counts as settled. */
|
|
19
|
+
export const SETTLE_MS = 1000;
|
|
20
|
+
|
|
21
|
+
/** Sends one analytics event. */
|
|
22
|
+
export type SendEvent = (event: string, props: TrackProps) => void;
|
|
23
|
+
|
|
24
|
+
/** The timer a tracker waits on; injectable for tests. */
|
|
25
|
+
export interface SettleTimer {
|
|
26
|
+
cancel: (handle: number) => void;
|
|
27
|
+
start: (callback: () => void, ms: number) => number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** What the dialog tells the tracker. */
|
|
31
|
+
export interface SearchTracker {
|
|
32
|
+
/** The dialog closed; record the query it was showing. */
|
|
33
|
+
closed: () => void;
|
|
34
|
+
/** The reader picked result `position` (1-based) for `query`. */
|
|
35
|
+
selected: (query: string, position: number, url: string) => void;
|
|
36
|
+
/** Results for `query` are on screen. */
|
|
37
|
+
settled: (query: string, results: number) => void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** A query and how many results it found. */
|
|
41
|
+
interface PendingSearch {
|
|
42
|
+
query: string;
|
|
43
|
+
results: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const browserTimer: SettleTimer = {
|
|
47
|
+
cancel: (handle) => clearTimeout(handle),
|
|
48
|
+
start: (callback, ms) => Number(setTimeout(callback, ms)),
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/** A tracker for one search dialog. */
|
|
52
|
+
export const createSearchTracker = (
|
|
53
|
+
send: SendEvent,
|
|
54
|
+
timer: SettleTimer = browserTimer
|
|
55
|
+
): SearchTracker => {
|
|
56
|
+
let pending: PendingSearch | undefined;
|
|
57
|
+
let handle: number | undefined;
|
|
58
|
+
let last: string | undefined;
|
|
59
|
+
|
|
60
|
+
const flush = (): void => {
|
|
61
|
+
if (handle !== undefined) {
|
|
62
|
+
timer.cancel(handle);
|
|
63
|
+
handle = undefined;
|
|
64
|
+
}
|
|
65
|
+
if (pending && pending.query !== last) {
|
|
66
|
+
send("search", {
|
|
67
|
+
path: location.pathname,
|
|
68
|
+
query: pending.query,
|
|
69
|
+
results: pending.results,
|
|
70
|
+
});
|
|
71
|
+
last = pending.query;
|
|
72
|
+
}
|
|
73
|
+
pending = undefined;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
closed: () => {
|
|
78
|
+
flush();
|
|
79
|
+
last = undefined;
|
|
80
|
+
},
|
|
81
|
+
selected: (query, position, url) => {
|
|
82
|
+
flush();
|
|
83
|
+
send("search_select", {
|
|
84
|
+
path: location.pathname,
|
|
85
|
+
position,
|
|
86
|
+
query: query.slice(0, MAX_QUERY_CHARS),
|
|
87
|
+
url,
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
settled: (query, results) => {
|
|
91
|
+
if (handle !== undefined) {
|
|
92
|
+
timer.cancel(handle);
|
|
93
|
+
}
|
|
94
|
+
pending = { query: query.slice(0, MAX_QUERY_CHARS), results };
|
|
95
|
+
handle = timer.start(flush, SETTLE_MS);
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Document } from "flexsearch";
|
|
2
2
|
|
|
3
|
-
import { buildResult, RESULT_POOL } from "./types.ts";
|
|
3
|
+
import { buildResult, byBoost, RESULT_POOL } from "./types.ts";
|
|
4
4
|
import type { IndexedDocument, SearchFn } from "./types.ts";
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -11,7 +11,7 @@ import type { IndexedDocument, SearchFn } from "./types.ts";
|
|
|
11
11
|
type SearchDocument = Pick<
|
|
12
12
|
IndexedDocument,
|
|
13
13
|
"content" | "description" | "route" | "title"
|
|
14
|
-
|
|
14
|
+
> & { keywords: string };
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* FlexSearch: reuse the same static `blume-search.json` Orama ships, but build
|
|
@@ -29,11 +29,14 @@ export const createSearch = async (opts: {
|
|
|
29
29
|
const byRoute = new Map(documents.map((doc) => [doc.route, doc]));
|
|
30
30
|
|
|
31
31
|
const index = new Document<SearchDocument>({
|
|
32
|
-
document: {
|
|
32
|
+
document: {
|
|
33
|
+
id: "route",
|
|
34
|
+
index: ["title", "keywords", "description", "content"],
|
|
35
|
+
},
|
|
33
36
|
tokenize: "forward",
|
|
34
37
|
});
|
|
35
38
|
for (const doc of documents) {
|
|
36
|
-
index.add(doc);
|
|
39
|
+
index.add({ ...doc, keywords: doc.keywords?.join(" ") ?? "" });
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
// FlexSearch's in-memory search is synchronous, so the SearchFn resolves
|
|
@@ -63,6 +66,15 @@ export const createSearch = async (opts: {
|
|
|
63
66
|
}
|
|
64
67
|
}
|
|
65
68
|
}
|
|
66
|
-
|
|
69
|
+
// FlexSearch returns an order, not scores: rank by position, then apply
|
|
70
|
+
// each page's `search.boost` to that.
|
|
71
|
+
const ranked = byBoost(
|
|
72
|
+
matched.map((doc, position) => ({
|
|
73
|
+
boost: doc.boost,
|
|
74
|
+
match: doc,
|
|
75
|
+
score: 1 / (position + 1),
|
|
76
|
+
}))
|
|
77
|
+
);
|
|
78
|
+
return Promise.resolve(buildResult(ranked, query, options?.section));
|
|
67
79
|
};
|
|
68
80
|
};
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { OramaClient } from "@oramacloud/client";
|
|
2
2
|
|
|
3
3
|
import type { OramaCloudOptions } from "../../../search/adapters/orama-cloud.ts";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
byBoost,
|
|
6
|
+
excerptFor,
|
|
7
|
+
highlight,
|
|
8
|
+
RESULT_POOL,
|
|
9
|
+
SEARCH_LIMIT,
|
|
10
|
+
} from "./types.ts";
|
|
5
11
|
import type { SearchFn } from "./types.ts";
|
|
6
12
|
|
|
7
13
|
interface OramaCloudRecord {
|
|
@@ -9,6 +15,7 @@ interface OramaCloudRecord {
|
|
|
9
15
|
title: string;
|
|
10
16
|
description?: string;
|
|
11
17
|
content?: string;
|
|
18
|
+
boost?: number;
|
|
12
19
|
}
|
|
13
20
|
|
|
14
21
|
/**
|
|
@@ -25,25 +32,30 @@ export const createSearch = (opts: OramaCloudOptions): SearchFn => {
|
|
|
25
32
|
endpoint,
|
|
26
33
|
});
|
|
27
34
|
return async (query, options) => {
|
|
35
|
+
// A pool beyond the visible limit, so each page's `search.boost` can
|
|
36
|
+
// lift a hit into view before the list is cut.
|
|
28
37
|
const results = await client.search({
|
|
29
|
-
limit:
|
|
38
|
+
limit: RESULT_POOL,
|
|
30
39
|
term: query,
|
|
31
40
|
// The sync carries `locale` on every record so an i18n site can scope
|
|
32
41
|
// hosted results to the active language.
|
|
33
42
|
...(options?.locale && { where: { locale: options.locale } }),
|
|
34
43
|
});
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
const ranked = byBoost(
|
|
45
|
+
(results?.hits ?? []).map((hit) => {
|
|
46
|
+
const doc: OramaCloudRecord = hit.document;
|
|
47
|
+
return { boost: doc.boost, match: doc, score: hit.score };
|
|
48
|
+
})
|
|
49
|
+
);
|
|
50
|
+
const hits = ranked.slice(0, SEARCH_LIMIT).map((doc) => ({
|
|
51
|
+
content: doc.content ?? "",
|
|
52
|
+
excerpt: highlight(
|
|
53
|
+
excerptFor(doc.description ?? "", doc.content ?? "", query),
|
|
54
|
+
query
|
|
55
|
+
),
|
|
56
|
+
title: highlight(doc.title, query),
|
|
57
|
+
url: doc.url,
|
|
58
|
+
}));
|
|
47
59
|
return { hits, sections: [] };
|
|
48
60
|
};
|
|
49
61
|
};
|
|
@@ -53,8 +53,28 @@ export interface IndexedDocument {
|
|
|
53
53
|
section?: string;
|
|
54
54
|
locale?: string;
|
|
55
55
|
version?: string;
|
|
56
|
+
/** The page's relevance multiplier (`search.boost`). */
|
|
57
|
+
boost?: number;
|
|
58
|
+
/** Extra terms the page is found by (`search.keywords`). */
|
|
59
|
+
keywords?: string[];
|
|
56
60
|
}
|
|
57
61
|
|
|
62
|
+
/** Something ranked with a relevance score, and the page it belongs to. */
|
|
63
|
+
export interface ScoredMatch<Match> {
|
|
64
|
+
boost?: number;
|
|
65
|
+
match: Match;
|
|
66
|
+
score: number;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Re-rank matches by score times their page's `search.boost`. The sort is
|
|
71
|
+
* stable, so equal scores keep the provider's order.
|
|
72
|
+
*/
|
|
73
|
+
export const byBoost = <Match>(scored: ScoredMatch<Match>[]): Match[] =>
|
|
74
|
+
scored
|
|
75
|
+
.toSorted((a, b) => b.score * (b.boost ?? 1) - a.score * (a.boost ?? 1))
|
|
76
|
+
.map(({ match }) => match);
|
|
77
|
+
|
|
58
78
|
/** Max results surfaced in the dialog. */
|
|
59
79
|
export const SEARCH_LIMIT = 12;
|
|
60
80
|
|
|
@@ -34,7 +34,11 @@ export const createSearch = (opts: TypesenseOptions): SearchFn => {
|
|
|
34
34
|
{
|
|
35
35
|
per_page: SEARCH_LIMIT,
|
|
36
36
|
q: query,
|
|
37
|
-
query_by: "title,description,content",
|
|
37
|
+
query_by: "title,keywords,description,content",
|
|
38
|
+
// Relevance first, in ten bands, then each page's `search.boost`
|
|
39
|
+
// within a band: a boost lifts a page past similar matches without
|
|
40
|
+
// floating it over clearly better ones.
|
|
41
|
+
sort_by: "_text_match(buckets: 10):desc,boost:desc",
|
|
38
42
|
// The sync marks `locale` and `version` as facets so hosted results
|
|
39
43
|
// scope to the active language and the viewed docs version (the
|
|
40
44
|
// current docs upload as "current").
|