blume 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +110 -0
- package/dist/cli/{chunk-ga0pf4aj.js → chunk-0n9xkp3d.js} +12 -3
- package/dist/cli/{chunk-ga0pf4aj.js.map → chunk-0n9xkp3d.js.map} +3 -3
- package/dist/cli/{chunk-43zcf4gz.js → chunk-0pmb4tr4.js} +541 -368
- package/dist/cli/chunk-0pmb4tr4.js.map +35 -0
- package/dist/cli/{chunk-9geymqns.js → chunk-0w6hs479.js} +15 -15
- package/dist/cli/{chunk-xkz5xbtv.js → chunk-124fvmzq.js} +2802 -1422
- package/dist/cli/chunk-124fvmzq.js.map +220 -0
- package/dist/cli/chunk-150jk7km.js +52 -0
- package/dist/cli/chunk-150jk7km.js.map +10 -0
- package/dist/cli/{chunk-y4ykbark.js → chunk-1f882900.js} +2 -2
- package/dist/cli/{chunk-5ffywsmb.js → chunk-297ajdcv.js} +8 -6
- package/dist/cli/{chunk-5ffywsmb.js.map → chunk-297ajdcv.js.map} +3 -3
- package/dist/cli/{chunk-h2a48yhd.js → chunk-32khwcb6.js} +120 -16
- package/dist/cli/{chunk-h2a48yhd.js.map → chunk-32khwcb6.js.map} +10 -10
- package/dist/cli/{chunk-5cg2rzgf.js → chunk-36bxs9c7.js} +20 -49
- package/dist/cli/{chunk-5cg2rzgf.js.map → chunk-36bxs9c7.js.map} +4 -5
- package/dist/cli/{chunk-py62fewh.js → chunk-53jqw8z0.js} +3 -3
- package/dist/cli/{chunk-5wj2qwn2.js → chunk-58bt3hag.js} +4 -4
- package/dist/cli/{chunk-e7vvk5ya.js → chunk-5cmts6dv.js} +3 -3
- package/dist/cli/{chunk-y5h4r2y0.js → chunk-60hz265r.js} +15 -5
- package/dist/cli/chunk-60hz265r.js.map +10 -0
- package/dist/cli/chunk-74wfhv79.js +65 -0
- package/dist/cli/chunk-74wfhv79.js.map +10 -0
- package/dist/cli/{chunk-g2z9x01v.js → chunk-9qzgg6mj.js} +3 -3
- package/dist/cli/{chunk-q3wxxq39.js → chunk-a57yh0h9.js} +5 -5
- package/dist/cli/{chunk-713mngdq.js → chunk-ave1wcbr.js} +76 -10
- package/dist/cli/chunk-ave1wcbr.js.map +14 -0
- package/dist/cli/{chunk-2n4ps1b3.js → chunk-bqxaqn5p.js} +40 -22
- package/dist/cli/chunk-bqxaqn5p.js.map +15 -0
- package/dist/cli/{chunk-jnwj5kjj.js → chunk-bxwhtvw8.js} +20 -7
- package/dist/cli/{chunk-jnwj5kjj.js.map → chunk-bxwhtvw8.js.map} +3 -3
- package/dist/cli/chunk-ckrcm08h.js +324 -0
- package/dist/cli/chunk-ckrcm08h.js.map +14 -0
- package/dist/cli/{chunk-2f3ndkpw.js → chunk-desvm72z.js} +3 -3
- package/dist/cli/{chunk-gp84za0h.js → chunk-dhwtqnnd.js} +2 -2
- package/dist/cli/{chunk-4h8z9vv5.js → chunk-dpy8qdjf.js} +93 -47
- package/dist/cli/chunk-dpy8qdjf.js.map +17 -0
- package/dist/cli/{chunk-2f7n4qzt.js → chunk-dqhg54mr.js} +1 -1
- package/dist/cli/{chunk-0gz36akr.js → chunk-dv2z0rzf.js} +21 -5
- package/dist/cli/chunk-dv2z0rzf.js.map +12 -0
- package/dist/cli/{chunk-cm89scdv.js → chunk-es8h0v3j.js} +9 -11
- package/dist/cli/{chunk-cm89scdv.js.map → chunk-es8h0v3j.js.map} +3 -3
- package/dist/cli/{chunk-zhvd6adm.js → chunk-gfc5fqvf.js} +14 -14
- package/dist/cli/{chunk-nd1821sk.js → chunk-gxtg6ed2.js} +33 -19
- package/dist/cli/chunk-gxtg6ed2.js.map +10 -0
- package/dist/cli/{chunk-p804f3pe.js → chunk-hvcxng5f.js} +6 -6
- package/dist/cli/{chunk-ta27feev.js → chunk-j1x92mx9.js} +21 -2
- package/dist/cli/chunk-j1x92mx9.js.map +10 -0
- package/dist/cli/{chunk-rekqz4qg.js → chunk-k3gr1nm9.js} +19 -16
- package/dist/cli/chunk-k3gr1nm9.js.map +10 -0
- package/dist/cli/{chunk-1n228xqb.js → chunk-kdpzw38a.js} +2 -2
- package/dist/cli/{chunk-cy5bm0j0.js → chunk-ms9z66nz.js} +2 -2
- package/dist/cli/{chunk-cy5bm0j0.js.map → chunk-ms9z66nz.js.map} +2 -2
- package/dist/cli/{chunk-zf748avv.js → chunk-nbyvgnnm.js} +193 -286
- package/dist/cli/chunk-nbyvgnnm.js.map +36 -0
- package/dist/cli/{chunk-6bm0xpgd.js → chunk-s0bdnc7v.js} +2 -2
- package/dist/cli/{chunk-yxmk4pec.js → chunk-sc93apfs.js} +17 -16
- package/dist/cli/{chunk-yxmk4pec.js.map → chunk-sc93apfs.js.map} +3 -3
- package/dist/cli/{chunk-cmtcnh9f.js → chunk-tfycddbt.js} +2 -2
- package/dist/cli/{chunk-9xr4dgee.js → chunk-twy7psdz.js} +29 -16
- package/dist/cli/chunk-twy7psdz.js.map +10 -0
- package/dist/cli/{chunk-ntv4qrt7.js → chunk-y4msrwm9.js} +15 -15
- package/dist/cli/{chunk-yj17vs39.js → chunk-ydp3065p.js} +5 -5
- package/dist/cli/index.js +24 -19
- package/dist/cli/index.js.map +3 -3
- package/dist/types/ai/ask-context.d.ts +6 -2
- package/dist/types/ai/ask.d.ts +24 -5
- package/dist/types/ai/component-markdown.d.ts +2 -0
- package/dist/types/ai/openapi-components.d.ts +4 -1
- package/dist/types/analytics/clics.d.ts +37 -0
- package/dist/types/analytics/index.d.ts +2 -0
- package/dist/types/analytics/schema.d.ts +18 -0
- package/dist/types/astro/integration.d.ts +11 -4
- package/dist/types/astro/markdown-negotiation.d.ts +3 -0
- package/dist/types/astro/pages.d.ts +10 -0
- package/dist/types/cli/init/scaffold.d.ts +1 -1
- package/dist/types/components/content/api-field.d.ts +4 -1
- package/dist/types/components/content/api-page.d.ts +6 -1
- package/dist/types/components/openapi/code-samples.d.ts +29 -0
- package/dist/types/components/openapi/helpers.d.ts +22 -1
- package/dist/types/components/openapi/security.d.ts +10 -0
- package/dist/types/components/openapi/snippets.d.ts +60 -0
- package/dist/types/core/changelog-index.d.ts +8 -0
- package/dist/types/core/config-input.d.ts +37 -6
- package/dist/types/core/data.d.ts +4 -0
- package/dist/types/core/i18n-ui.d.ts +2 -0
- package/dist/types/core/links.d.ts +8 -0
- package/dist/types/core/powered-by.d.ts +12 -0
- package/dist/types/core/probe.d.ts +2 -0
- package/dist/types/core/project-graph.d.ts +6 -0
- package/dist/types/core/redirect-patterns.d.ts +11 -3
- package/dist/types/core/schema.d.ts +228 -3
- package/dist/types/core/sources/cache.d.ts +3 -0
- package/dist/types/core/sources/contentful-rich-text.d.ts +11 -2
- package/dist/types/core/sources/contentful.d.ts +17 -1
- package/dist/types/core/sources/lexical.d.ts +5 -0
- package/dist/types/core/sources/lower.d.ts +10 -2
- package/dist/types/core/sources/normalize.d.ts +20 -1
- package/dist/types/core/sources/notion.d.ts +8 -0
- package/dist/types/core/sources/remote.d.ts +37 -10
- package/dist/types/core/sources/sanity.d.ts +1 -1
- package/dist/types/core/types.d.ts +17 -0
- package/dist/types/deploy/adapter-output.d.ts +2 -1
- package/dist/types/deploy/adapters/cloudflare.d.ts +7 -5
- package/dist/types/deploy/adapters/node.d.ts +58 -23
- package/dist/types/deploy/adapters/registry.d.ts +10 -0
- package/dist/types/deploy/adapters/types.d.ts +3 -2
- package/dist/types/deploy/cloudflare-negotiation.d.ts +5 -2
- package/dist/types/deploy/node-headers.d.ts +12 -4
- package/dist/types/deploy/platforms/cloudflare.d.ts +10 -7
- package/dist/types/deploy/platforms/node.d.ts +3 -1
- package/dist/types/deploy/platforms/types.d.ts +18 -7
- package/dist/types/deploy/platforms/vercel.d.ts +21 -2
- package/dist/types/deploy/redirects.d.ts +17 -1
- package/dist/types/deploy/sitemap.d.ts +2 -1
- package/dist/types/deploy/vercel-negotiation.d.ts +44 -21
- package/dist/types/narration/build.d.ts +34 -12
- package/dist/types/narration/provider.d.ts +96 -8
- package/dist/types/openapi/checks.d.ts +27 -0
- package/dist/types/openapi/graphql.d.ts +6 -0
- package/dist/types/openapi/model.d.ts +15 -4
- package/dist/types/openapi/parse.d.ts +3 -0
- package/dist/types/openapi/references.d.ts +16 -0
- package/dist/types/openapi/sentence.d.ts +11 -1
- package/dist/types/ratelimit/index.d.ts +1 -1
- package/dist/types/ratelimit/schema.d.ts +8 -0
- package/dist/types/ratelimit/upstash.d.ts +39 -14
- package/dist/types/reference/graphql.d.ts +76 -1
- package/dist/types/reference/index.d.ts +1 -1
- package/dist/types/reference/openapi.d.ts +2 -2
- package/dist/types/reference/options.d.ts +37 -1
- package/dist/types/reference/schema.d.ts +84 -0
- package/dist/types/search/adapters/registry.d.ts +3 -0
- package/dist/types/search/adapters/typesense.d.ts +14 -4
- package/dist/types/search/build.d.ts +3 -1
- package/dist/types/search/orama-index.d.ts +9 -9
- package/dist/types/search/sync/algolia.d.ts +5 -2
- package/dist/types/search/sync/index.d.ts +3 -1
- package/dist/types/search/sync/typesense.d.ts +11 -7
- package/dist/types/seo/jsonld.d.ts +11 -6
- package/dist/types/sources/contentful.d.ts +16 -0
- package/dist/types/sources/github-releases.d.ts +8 -1
- package/dist/types/sources/notion.d.ts +8 -0
- package/dist/types/sources/payload.d.ts +2 -0
- package/dist/types/sources/registry.d.ts +63 -0
- package/dist/types/sources/shared.d.ts +3 -0
- package/dist/types/sources/strapi.d.ts +2 -0
- package/docs/01-quickstart.mdx +4 -2
- package/docs/02-deployment.mdx +59 -13
- package/docs/03-upgrading.mdx +2 -1
- package/docs/04-migrating.mdx +9 -9
- package/docs/08-faq.mdx +12 -12
- package/docs/advanced/changelog.mdx +30 -2
- package/docs/advanced/custom-pages.mdx +1 -1
- package/docs/advanced/skills.mdx +1 -1
- package/docs/cli/audit.mdx +30 -7
- package/docs/cli/doctor.mdx +6 -2
- package/docs/cli/evals.mdx +4 -4
- package/docs/cli/translate.mdx +1 -0
- package/docs/cli/validate.mdx +4 -3
- package/docs/configuration/analytics.mdx +19 -1
- package/docs/configuration/assistant.mdx +6 -2
- package/docs/configuration/customization.mdx +4 -2
- package/docs/configuration/export.mdx +5 -3
- package/docs/configuration/index.mdx +4 -2
- package/docs/configuration/narration.mdx +43 -6
- package/docs/configuration/rate-limiting.mdx +23 -2
- package/docs/configuration/search.mdx +37 -14
- package/docs/content/components.mdx +2 -2
- package/docs/content/frontmatter.mdx +5 -3
- package/docs/content/i18n.mdx +2 -2
- package/docs/content/includes.mdx +1 -1
- package/docs/content/navigation.mdx +8 -6
- package/docs/content/sources/contentful.mdx +9 -4
- package/docs/content/sources/custom.mdx +7 -3
- package/docs/content/sources/github-releases.mdx +12 -2
- package/docs/content/sources/index.mdx +3 -3
- package/docs/content/sources/notion.mdx +17 -2
- package/docs/content/sources/obsidian.mdx +3 -1
- package/docs/content/sources/payload.mdx +5 -2
- package/docs/content/sources/remote-mdx.mdx +6 -0
- package/docs/content/sources/sanity.mdx +4 -2
- package/docs/content/sources/strapi.mdx +5 -2
- package/docs/content/syntax.mdx +2 -2
- package/docs/discoverability/agent-discovery.mdx +1 -1
- package/docs/discoverability/index.mdx +1 -1
- package/docs/discoverability/json-api.mdx +2 -0
- package/docs/discoverability/llms-txt.mdx +1 -1
- package/docs/discoverability/markdown.mdx +3 -1
- package/docs/discoverability/mcp.mdx +6 -0
- package/docs/discoverability/sitemap-and-robots.mdx +1 -1
- package/docs/discoverability/structured-data.mdx +8 -6
- package/docs/references/api-pages.mdx +1 -1
- package/docs/references/asyncapi.mdx +5 -1
- package/docs/references/graphql.mdx +15 -1
- package/docs/references/openapi.mdx +64 -8
- package/package.json +9 -9
- package/skills/blume/SKILL.md +1 -1
- package/skills/blume-migrate/SKILL.md +2 -2
- package/skills/blume-migrate/assets/{oxfmt@0.67.0.patch → oxfmt@0.71.0.patch} +7 -7
- package/skills/blume-migrate/references/docusaurus.md +4 -4
- package/skills/blume-migrate/references/fumadocs.md +17 -13
- package/skills/blume-migrate/references/monorepo.md +5 -5
- package/skills/blume-migrate/references/nextra.md +8 -5
- package/skills/blume-migrate/references/starlight.md +9 -7
- package/src/ai/ask-context.ts +33 -3
- package/src/ai/ask-limits.ts +13 -0
- package/src/ai/ask.ts +24 -6
- package/src/ai/changelog-markdown.ts +11 -6
- package/src/ai/component-markdown.ts +1 -1
- package/src/ai/cors.ts +12 -3
- package/src/ai/mcp/query.ts +14 -6
- package/src/ai/openapi-components.ts +28 -4
- package/src/ai/relative-links.ts +4 -38
- package/src/analytics/clics.ts +58 -0
- package/src/analytics/head.ts +5 -0
- package/src/analytics/index.ts +2 -0
- package/src/analytics/schema.ts +2 -0
- package/src/astro/generate.ts +91 -56
- package/src/astro/integration.ts +72 -22
- package/src/astro/markdown-negotiation.ts +3 -0
- package/src/astro/module-types.ts +3 -1
- package/src/astro/pages.ts +23 -0
- package/src/astro/render-deps.ts +10 -5
- package/src/astro/runtime-deps.ts +23 -2
- package/src/astro/templates.ts +129 -81
- package/src/audit/catalog.ts +19 -3
- package/src/audit/checks/links.ts +14 -1
- package/src/audit/checks/network.ts +110 -6
- package/src/audit/checks/redirects.ts +6 -4
- package/src/audit/checks/robots.ts +58 -2
- package/src/audit/run.ts +3 -0
- package/src/audit/types.ts +2 -0
- package/src/audit/url.ts +14 -5
- package/src/blume-modules.d.ts +5 -1
- package/src/cli/args.ts +51 -0
- package/src/cli/build-failure.ts +23 -0
- package/src/cli/commands/audit.ts +10 -1
- package/src/cli/commands/dev.ts +18 -2
- package/src/cli/commands/doctor.ts +45 -12
- package/src/cli/commands/eval.ts +36 -4
- package/src/cli/commands/init.ts +6 -4
- package/src/cli/commands/validate.ts +10 -1
- package/src/cli/init/scaffold.ts +9 -6
- package/src/cli/prepare.ts +6 -3
- package/src/cli/required-secrets.ts +3 -2
- package/src/components/content/ApiPlayground.astro +6 -2
- package/src/components/content/api-field.ts +4 -1
- package/src/components/content/api-page.ts +31 -7
- package/src/components/content/mermaid-element.ts +15 -1
- package/src/components/content/view-switcher.ts +7 -4
- package/src/components/islands/hooks.ts +27 -1
- package/src/components/layout/NavTree.astro +49 -5
- package/src/components/layout/NavTreeScript.astro +18 -0
- package/src/components/layout/PageActions.astro +139 -24
- package/src/components/layout/PageLayout.astro +13 -3
- package/src/components/layout/RootLayout.astro +27 -14
- package/src/components/layout/Search.astro +17 -3
- package/src/components/layout/TableOfContents.astro +24 -17
- package/src/components/layout/analytics-client.ts +8 -8
- package/src/components/layout/head-scripts.ts +6 -1
- package/src/components/layout/search/algolia.ts +10 -1
- package/src/components/layout/search/analytics.ts +42 -14
- package/src/components/layout/search/endpoint.ts +48 -5
- package/src/components/layout/search/pagefind.ts +114 -5
- package/src/components/layout/search/types.ts +12 -0
- package/src/components/layout/search/typesense.ts +18 -6
- package/src/components/openapi/AsyncApiOperation.astro +14 -7
- package/src/components/openapi/Authorization.astro +10 -0
- package/src/components/openapi/GraphqlOperation.astro +5 -0
- package/src/components/openapi/Operation.astro +24 -1
- package/src/components/openapi/ParametersTable.astro +8 -4
- package/src/components/openapi/Playground.astro +7 -1
- package/src/components/openapi/RequestBody.astro +19 -2
- package/src/components/openapi/Responses.astro +1 -0
- package/src/components/openapi/SchemaTable.astro +15 -3
- package/src/components/openapi/async-snippets.ts +61 -5
- package/src/components/openapi/async.ts +123 -29
- package/src/components/openapi/graphql-helpers.ts +35 -12
- package/src/components/openapi/helpers.ts +36 -2
- package/src/components/openapi/message-model.ts +54 -11
- package/src/components/openapi/message.ts +14 -4
- package/src/components/openapi/operation-model.ts +16 -7
- package/src/components/openapi/playground-client.ts +77 -58
- package/src/components/openapi/playground-schema.ts +1 -1
- package/src/components/openapi/schema-tree.ts +44 -14
- package/src/components/openapi/security.ts +10 -0
- package/src/components/openapi/snippets.ts +5 -3
- package/src/components/openapi/validate-json.ts +4 -3
- package/src/components/powered-by-middleware.ts +33 -0
- package/src/core/changelog-index.ts +31 -1
- package/src/core/component-diagnostics.ts +53 -0
- package/src/core/component-overrides.ts +32 -0
- package/src/core/config-input.ts +42 -6
- package/src/core/data.ts +2 -0
- package/src/core/diagnostics.ts +5 -0
- package/src/core/i18n-ui.ts +2 -0
- package/src/core/includes.ts +1 -1
- package/src/core/links.ts +47 -17
- package/src/core/powered-by.ts +12 -0
- package/src/core/probe.ts +26 -6
- package/src/core/project-graph.ts +59 -18
- package/src/core/redirect-patterns.ts +36 -7
- package/src/core/schema.ts +26 -6
- package/src/core/sources/cache.ts +6 -0
- package/src/core/sources/contentful-rich-text.ts +29 -6
- package/src/core/sources/contentful.ts +135 -25
- package/src/core/sources/filesystem.ts +26 -4
- package/src/core/sources/github-releases.ts +12 -1
- package/src/core/sources/lexical.ts +15 -10
- package/src/core/sources/lower.ts +19 -3
- package/src/core/sources/mdx-remote.ts +62 -10
- package/src/core/sources/normalize.ts +227 -41
- package/src/core/sources/notion.ts +34 -10
- package/src/core/sources/obsidian.ts +16 -3
- package/src/core/sources/payload.ts +38 -15
- package/src/core/sources/remote.ts +72 -15
- package/src/core/sources/resolve.ts +11 -1
- package/src/core/sources/sanity.ts +43 -25
- package/src/core/sources/strapi.ts +2 -2
- package/src/core/types.ts +18 -0
- package/src/core/ui-packs/ar.ts +1 -0
- package/src/core/ui-packs/bg.ts +1 -0
- package/src/core/ui-packs/bn.ts +1 -0
- package/src/core/ui-packs/ca.ts +1 -0
- package/src/core/ui-packs/cs.ts +1 -0
- package/src/core/ui-packs/da.ts +1 -0
- package/src/core/ui-packs/de.ts +1 -0
- package/src/core/ui-packs/el.ts +1 -0
- package/src/core/ui-packs/es.ts +1 -0
- package/src/core/ui-packs/fa.ts +1 -0
- package/src/core/ui-packs/fi.ts +1 -0
- package/src/core/ui-packs/fr.ts +1 -0
- package/src/core/ui-packs/he.ts +1 -0
- package/src/core/ui-packs/hi.ts +1 -0
- package/src/core/ui-packs/hr.ts +1 -0
- package/src/core/ui-packs/hu.ts +1 -0
- package/src/core/ui-packs/id.ts +1 -0
- package/src/core/ui-packs/it.ts +1 -0
- package/src/core/ui-packs/ja.ts +1 -0
- package/src/core/ui-packs/ko.ts +1 -0
- package/src/core/ui-packs/nl.ts +1 -0
- package/src/core/ui-packs/no.ts +1 -0
- package/src/core/ui-packs/pl.ts +1 -0
- package/src/core/ui-packs/pt-br.ts +1 -0
- package/src/core/ui-packs/pt.ts +1 -0
- package/src/core/ui-packs/ro.ts +1 -0
- package/src/core/ui-packs/ru.ts +1 -0
- package/src/core/ui-packs/sk.ts +1 -0
- package/src/core/ui-packs/sr.ts +1 -0
- package/src/core/ui-packs/sv.ts +1 -0
- package/src/core/ui-packs/th.ts +1 -0
- package/src/core/ui-packs/tr.ts +1 -0
- package/src/core/ui-packs/uk.ts +1 -0
- package/src/core/ui-packs/vi.ts +1 -0
- package/src/core/ui-packs/zh-tw.ts +1 -0
- package/src/core/ui-packs/zh.ts +1 -0
- package/src/deploy/adapter-output.ts +7 -2
- package/src/deploy/adapters/cloudflare.ts +7 -5
- package/src/deploy/adapters/node.ts +47 -9
- package/src/deploy/adapters/types.ts +7 -3
- package/src/deploy/artifacts.ts +2 -1
- package/src/deploy/cloudflare-negotiation.ts +36 -7
- package/src/deploy/headers.ts +15 -1
- package/src/deploy/node-headers.ts +32 -20
- package/src/deploy/platforms/cloudflare.ts +14 -9
- package/src/deploy/platforms/netlify.ts +2 -1
- package/src/deploy/platforms/node.ts +12 -2
- package/src/deploy/platforms/types.ts +21 -7
- package/src/deploy/platforms/vercel.ts +115 -17
- package/src/deploy/redirects.ts +66 -6
- package/src/deploy/rss.ts +3 -1
- package/src/deploy/sitemap.ts +39 -5
- package/src/deploy/vercel-negotiation.ts +325 -103
- package/src/eval/agents.ts +18 -1
- package/src/eval/prompts.ts +6 -3
- package/src/eval/report.ts +34 -13
- package/src/eval/run.ts +5 -0
- package/src/markdown/base-links.ts +15 -14
- package/src/markdown/package-commands.ts +9 -8
- package/src/markdown/relative-links.ts +3 -2
- package/src/narration/build.ts +218 -40
- package/src/narration/provider.ts +89 -9
- package/src/openapi/checks.ts +273 -0
- package/src/openapi/graphql-build.ts +21 -0
- package/src/openapi/graphql.ts +11 -0
- package/src/openapi/model.ts +15 -3
- package/src/openapi/parse.ts +127 -2
- package/src/openapi/references.ts +43 -3
- package/src/openapi/render-mdx.ts +68 -8
- package/src/openapi/sentence.ts +29 -2
- package/src/openapi/source.ts +39 -14
- package/src/ratelimit/index.ts +1 -1
- package/src/ratelimit/runtime.ts +37 -19
- package/src/ratelimit/upstash.ts +40 -10
- package/src/reference/graphql.ts +15 -1
- package/src/reference/index.ts +1 -0
- package/src/reference/openapi.ts +2 -2
- package/src/reference/options.ts +29 -1
- package/src/registry/eject.ts +8 -2
- package/src/search/adapters/locale-scope.ts +30 -0
- package/src/search/adapters/typesense.ts +14 -4
- package/src/search/build.ts +21 -3
- package/src/search/orama-index.ts +163 -31
- package/src/search/source-pages.ts +29 -0
- package/src/search/sync/algolia.ts +99 -9
- package/src/search/sync/index.ts +52 -13
- package/src/search/sync/typesense.ts +72 -27
- package/src/seo/jsonld.ts +38 -20
- package/src/sources/contentful.ts +12 -0
- package/src/sources/github-releases.ts +7 -1
- package/src/sources/notion.ts +7 -0
- package/src/sources/shared.ts +3 -0
- package/src/theme/color.ts +9 -10
- package/src/theme/entry.ts +43 -6
- package/src/translate/validate.ts +23 -1
- package/dist/cli/chunk-0gz36akr.js.map +0 -12
- package/dist/cli/chunk-2n4ps1b3.js.map +0 -15
- package/dist/cli/chunk-43zcf4gz.js.map +0 -36
- package/dist/cli/chunk-4ejxbzee.js +0 -39
- package/dist/cli/chunk-4ejxbzee.js.map +0 -10
- package/dist/cli/chunk-4h8z9vv5.js.map +0 -16
- package/dist/cli/chunk-713mngdq.js.map +0 -14
- package/dist/cli/chunk-9xr4dgee.js.map +0 -10
- package/dist/cli/chunk-amrb6hgh.js +0 -145
- package/dist/cli/chunk-amrb6hgh.js.map +0 -11
- package/dist/cli/chunk-nd1821sk.js.map +0 -10
- package/dist/cli/chunk-rekqz4qg.js.map +0 -10
- package/dist/cli/chunk-ta27feev.js.map +0 -10
- package/dist/cli/chunk-xkz5xbtv.js.map +0 -213
- package/dist/cli/chunk-y5h4r2y0.js.map +0 -10
- package/dist/cli/chunk-zf748avv.js.map +0 -37
- /package/dist/cli/{chunk-9geymqns.js.map → chunk-0w6hs479.js.map} +0 -0
- /package/dist/cli/{chunk-y4ykbark.js.map → chunk-1f882900.js.map} +0 -0
- /package/dist/cli/{chunk-py62fewh.js.map → chunk-53jqw8z0.js.map} +0 -0
- /package/dist/cli/{chunk-5wj2qwn2.js.map → chunk-58bt3hag.js.map} +0 -0
- /package/dist/cli/{chunk-e7vvk5ya.js.map → chunk-5cmts6dv.js.map} +0 -0
- /package/dist/cli/{chunk-g2z9x01v.js.map → chunk-9qzgg6mj.js.map} +0 -0
- /package/dist/cli/{chunk-q3wxxq39.js.map → chunk-a57yh0h9.js.map} +0 -0
- /package/dist/cli/{chunk-2f3ndkpw.js.map → chunk-desvm72z.js.map} +0 -0
- /package/dist/cli/{chunk-gp84za0h.js.map → chunk-dhwtqnnd.js.map} +0 -0
- /package/dist/cli/{chunk-2f7n4qzt.js.map → chunk-dqhg54mr.js.map} +0 -0
- /package/dist/cli/{chunk-zhvd6adm.js.map → chunk-gfc5fqvf.js.map} +0 -0
- /package/dist/cli/{chunk-p804f3pe.js.map → chunk-hvcxng5f.js.map} +0 -0
- /package/dist/cli/{chunk-1n228xqb.js.map → chunk-kdpzw38a.js.map} +0 -0
- /package/dist/cli/{chunk-6bm0xpgd.js.map → chunk-s0bdnc7v.js.map} +0 -0
- /package/dist/cli/{chunk-cmtcnh9f.js.map → chunk-tfycddbt.js.map} +0 -0
- /package/dist/cli/{chunk-ntv4qrt7.js.map → chunk-y4msrwm9.js.map} +0 -0
- /package/dist/cli/{chunk-yj17vs39.js.map → chunk-ydp3065p.js.map} +0 -0
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
* result is a `search_select` event with its position, for click-through. The
|
|
10
10
|
* same query settling twice in a row (a filter toggled back and forth) counts
|
|
11
11
|
* once.
|
|
12
|
+
*
|
|
13
|
+
* With `search.analytics.queries: false`, providers get each query's length
|
|
14
|
+
* (`queryChars`) instead of its text, and the text rides only the
|
|
15
|
+
* `blume:track` DOM event, the way the assistant reports questions.
|
|
12
16
|
*/
|
|
13
17
|
import type { TrackProps } from "../analytics-client.ts";
|
|
14
18
|
|
|
@@ -18,8 +22,15 @@ export const MAX_QUERY_CHARS = 100;
|
|
|
18
22
|
/** How long a query sits untouched before it counts as settled. */
|
|
19
23
|
export const SETTLE_MS = 1000;
|
|
20
24
|
|
|
21
|
-
/**
|
|
22
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Sends one analytics event; `local` rides only the `blume:track` DOM event
|
|
27
|
+
* (see `track`).
|
|
28
|
+
*/
|
|
29
|
+
export type SendEvent = (
|
|
30
|
+
event: string,
|
|
31
|
+
props: TrackProps,
|
|
32
|
+
local?: TrackProps
|
|
33
|
+
) => void;
|
|
23
34
|
|
|
24
35
|
/** The timer a tracker waits on; injectable for tests. */
|
|
25
36
|
export interface SettleTimer {
|
|
@@ -27,6 +38,14 @@ export interface SettleTimer {
|
|
|
27
38
|
start: (callback: () => void, ms: number) => number;
|
|
28
39
|
}
|
|
29
40
|
|
|
41
|
+
/** How a tracker reports. */
|
|
42
|
+
export interface SearchTrackerOptions {
|
|
43
|
+
/** Whether providers receive each query's text (`search.analytics.queries`). */
|
|
44
|
+
queries?: boolean;
|
|
45
|
+
/** The timer a query waits on to settle; injectable for tests. */
|
|
46
|
+
timer?: SettleTimer;
|
|
47
|
+
}
|
|
48
|
+
|
|
30
49
|
/** What the dialog tells the tracker. */
|
|
31
50
|
export interface SearchTracker {
|
|
32
51
|
/** The dialog closed; record the query it was showing. */
|
|
@@ -51,23 +70,33 @@ const browserTimer: SettleTimer = {
|
|
|
51
70
|
/** A tracker for one search dialog. */
|
|
52
71
|
export const createSearchTracker = (
|
|
53
72
|
send: SendEvent,
|
|
54
|
-
timer:
|
|
73
|
+
{ queries = true, timer = browserTimer }: SearchTrackerOptions = {}
|
|
55
74
|
): SearchTracker => {
|
|
56
75
|
let pending: PendingSearch | undefined;
|
|
57
76
|
let handle: number | undefined;
|
|
58
77
|
let last: string | undefined;
|
|
59
78
|
|
|
79
|
+
// Providers get the query's text, or only its length, in which case the
|
|
80
|
+
// text goes to the `blume:track` DOM event alone.
|
|
81
|
+
const report = (event: string, props: TrackProps, query: string): void => {
|
|
82
|
+
if (queries) {
|
|
83
|
+
send(event, { ...props, query });
|
|
84
|
+
} else {
|
|
85
|
+
send(event, { ...props, queryChars: query.length }, { query });
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
60
89
|
const flush = (): void => {
|
|
61
90
|
if (handle !== undefined) {
|
|
62
91
|
timer.cancel(handle);
|
|
63
92
|
handle = undefined;
|
|
64
93
|
}
|
|
65
94
|
if (pending && pending.query !== last) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
95
|
+
report(
|
|
96
|
+
"search",
|
|
97
|
+
{ path: location.pathname, results: pending.results },
|
|
98
|
+
pending.query
|
|
99
|
+
);
|
|
71
100
|
last = pending.query;
|
|
72
101
|
}
|
|
73
102
|
pending = undefined;
|
|
@@ -80,12 +109,11 @@ export const createSearchTracker = (
|
|
|
80
109
|
},
|
|
81
110
|
selected: (query, position, url) => {
|
|
82
111
|
flush();
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
position,
|
|
86
|
-
query
|
|
87
|
-
|
|
88
|
-
});
|
|
112
|
+
report(
|
|
113
|
+
"search_select",
|
|
114
|
+
{ path: location.pathname, position, url },
|
|
115
|
+
query.slice(0, MAX_QUERY_CHARS)
|
|
116
|
+
);
|
|
89
117
|
},
|
|
90
118
|
settled: (query, results) => {
|
|
91
119
|
if (handle !== undefined) {
|
|
@@ -1,22 +1,64 @@
|
|
|
1
1
|
import { highlight, SEARCH_LIMIT } from "./types.ts";
|
|
2
|
-
import type { SearchFn, SearchHit } from "./types.ts";
|
|
2
|
+
import type { SearchClientOptions, SearchFn, SearchHit } from "./types.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* How long a typed query waits for the reader to stop typing before it's
|
|
6
|
+
* sent, in milliseconds. The dialog searches on every keystroke, and each
|
|
7
|
+
* request counts toward the endpoint's `rateLimit`.
|
|
8
|
+
*/
|
|
9
|
+
export const SEARCH_DEBOUNCE_MS = 250;
|
|
10
|
+
|
|
11
|
+
/** Resolve after `ms`, or reject with the abort reason once `signal` aborts. */
|
|
12
|
+
const pause = (ms: number, signal: AbortSignal): Promise<void> =>
|
|
13
|
+
// oxlint-disable-next-line promise/avoid-new -- adapt setTimeout and the abort event
|
|
14
|
+
new Promise((resolve, reject) => {
|
|
15
|
+
const timer = setTimeout(resolve, ms);
|
|
16
|
+
signal.addEventListener(
|
|
17
|
+
"abort",
|
|
18
|
+
() => {
|
|
19
|
+
clearTimeout(timer);
|
|
20
|
+
reject(signal.reason);
|
|
21
|
+
},
|
|
22
|
+
{ once: true }
|
|
23
|
+
);
|
|
24
|
+
});
|
|
3
25
|
|
|
4
26
|
/**
|
|
5
27
|
* Server-proxied search (Mixedbread): POST the query to a generated endpoint
|
|
6
28
|
* that holds the secret key and talks to the service. The returned hits carry
|
|
7
29
|
* service-derived text and the dialog injects title/excerpt as HTML, so both
|
|
8
30
|
* are escaped (and query matches marked) here, like every other provider.
|
|
31
|
+
* A failed request rejects, as a hosted provider's does, so the dialog shows
|
|
32
|
+
* its error rather than an empty "no results".
|
|
33
|
+
*
|
|
34
|
+
* With `typing` (the search dialog), a query waits {@link SEARCH_DEBOUNCE_MS}
|
|
35
|
+
* before it's sent, and a newer one supersedes it: the earlier call's wait or
|
|
36
|
+
* request is aborted and it rejects with an `AbortError`. Typing a word then
|
|
37
|
+
* costs one request rather than one per letter, and a slow earlier response
|
|
38
|
+
* can't land after a later one. Without it, every call is sent at once.
|
|
9
39
|
*/
|
|
10
|
-
export const createSearch =
|
|
11
|
-
|
|
12
|
-
|
|
40
|
+
export const createSearch = (
|
|
41
|
+
opts: { api: string } & SearchClientOptions
|
|
42
|
+
): SearchFn => {
|
|
43
|
+
let pending: AbortController | undefined;
|
|
44
|
+
return async (query) => {
|
|
45
|
+
let signal: AbortSignal | undefined;
|
|
46
|
+
if (opts.typing) {
|
|
47
|
+
pending?.abort();
|
|
48
|
+
pending = new AbortController();
|
|
49
|
+
({ signal } = pending);
|
|
50
|
+
await pause(SEARCH_DEBOUNCE_MS, signal);
|
|
51
|
+
}
|
|
13
52
|
const response = await fetch(opts.api, {
|
|
14
53
|
body: JSON.stringify({ query }),
|
|
15
54
|
headers: { "Content-Type": "application/json" },
|
|
16
55
|
method: "POST",
|
|
56
|
+
signal,
|
|
17
57
|
});
|
|
18
58
|
if (!response.ok) {
|
|
19
|
-
|
|
59
|
+
throw new Error(
|
|
60
|
+
`Search failed: ${opts.api} answered ${response.status}.`
|
|
61
|
+
);
|
|
20
62
|
}
|
|
21
63
|
// SAFETY: the endpoint is Blume-generated (`search-endpoint` template) and
|
|
22
64
|
// responds with the SearchHit list it built; title/excerpt are still
|
|
@@ -29,3 +71,4 @@ export const createSearch =
|
|
|
29
71
|
}));
|
|
30
72
|
return { hits, sections: [] };
|
|
31
73
|
};
|
|
74
|
+
};
|
|
@@ -9,14 +9,63 @@ interface PagefindResult {
|
|
|
9
9
|
}>;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
/** One Pagefind instance: a page language's index, plus any merged into it. */
|
|
13
|
+
interface PagefindInstance {
|
|
14
|
+
mergeIndex: (
|
|
15
|
+
url: string,
|
|
16
|
+
options: { baseUrl: string; language: string }
|
|
17
|
+
) => Promise<void>;
|
|
13
18
|
search: (query: string) => Promise<{ results: PagefindResult[] }>;
|
|
14
19
|
}
|
|
15
20
|
|
|
21
|
+
interface PagefindModule {
|
|
22
|
+
createInstance: (options: {
|
|
23
|
+
basePath: string;
|
|
24
|
+
baseUrl: string;
|
|
25
|
+
}) => PagefindInstance;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** `pagefind-entry.json`: the bundle's index for each language. */
|
|
29
|
+
interface PagefindEntry {
|
|
30
|
+
languages: Record<string, { page_count: number }>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Pagefind names each page after its built file (`quickstart/index.html`), so
|
|
34
|
+
// its URLs end in a slash. Blume serves pages without one (`trailingSlash:
|
|
35
|
+
// "never"`): hosts redirect the slashed URL, and `blume preview` 404s it.
|
|
36
|
+
const TRAILING_SLASH = /(?<=.)\/(?=[#?]|$)/u;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The language whose index Pagefind loads for a page, picked the way its
|
|
40
|
+
* `findIndex` does: the page's own, else its base language (`pt` for
|
|
41
|
+
* `pt-br`), else the language with the most pages (a locale with no
|
|
42
|
+
* translated pages has no index of its own).
|
|
43
|
+
*/
|
|
44
|
+
const loadedLanguage = (
|
|
45
|
+
languages: PagefindEntry["languages"],
|
|
46
|
+
language: string
|
|
47
|
+
): string | undefined => {
|
|
48
|
+
const [base = language] = language.split("-");
|
|
49
|
+
if (languages[language]) {
|
|
50
|
+
return language;
|
|
51
|
+
}
|
|
52
|
+
if (languages[base]) {
|
|
53
|
+
return base;
|
|
54
|
+
}
|
|
55
|
+
return Object.entries(languages).toSorted(
|
|
56
|
+
([, a], [, b]) => b.page_count - a.page_count
|
|
57
|
+
)[0]?.[0];
|
|
58
|
+
};
|
|
59
|
+
|
|
16
60
|
/**
|
|
17
61
|
* Pagefind: load the index emitted into the built site and query it. The bundle
|
|
18
62
|
* lives in the output (not `node_modules`), so it is imported at runtime by URL
|
|
19
63
|
* — which is why this only works in the production build, not `dev`.
|
|
64
|
+
*
|
|
65
|
+
* Pagefind keeps an index per language and searches the one for the page's
|
|
66
|
+
* `<html lang>`, which Blume sets to the locale the dialog scopes to. A
|
|
67
|
+
* search scoped to a locale is that index alone; a search across every
|
|
68
|
+
* language (no `locale`) merges every other language's index into it.
|
|
20
69
|
*/
|
|
21
70
|
export const createSearch = async (opts: {
|
|
22
71
|
url: string;
|
|
@@ -24,25 +73,85 @@ export const createSearch = async (opts: {
|
|
|
24
73
|
// The pagefind bundle lives in the built site (not node_modules) and is
|
|
25
74
|
// resolved at runtime by URL — it can't be a static, code-splittable path.
|
|
26
75
|
// SAFETY: the URL points at the `pagefind.js` module our own build emitted,
|
|
27
|
-
// whose export contract (`
|
|
76
|
+
// whose export contract (`createInstance()`) is fixed by pagefind.
|
|
28
77
|
// oxlint-disable-next-line react-doctor/no-dynamic-import-path
|
|
29
78
|
const pagefind = (await import(
|
|
30
79
|
/* @vite-ignore */
|
|
31
80
|
opts.url
|
|
32
81
|
)) as PagefindModule;
|
|
82
|
+
// The bundle's folder, which holds the entry file and every language's index.
|
|
83
|
+
const bundle = new URL("./", new URL(opts.url, document.baseURI));
|
|
84
|
+
// `baseUrl` "/" keeps result URLs base-less routes, like every other
|
|
85
|
+
// provider's: by default Pagefind prefixes the folder the bundle is served
|
|
86
|
+
// under, and the dialog adds the deployment base itself.
|
|
87
|
+
const create = (): PagefindInstance =>
|
|
88
|
+
pagefind.createInstance({ basePath: bundle.pathname, baseUrl: "/" });
|
|
89
|
+
|
|
90
|
+
// Every language's index: the page's own, and every other one merged in.
|
|
91
|
+
const everyLanguage = async (language: string): Promise<PagefindInstance> => {
|
|
92
|
+
const instance = create();
|
|
93
|
+
const response = await fetch(new URL("pagefind-entry.json", bundle));
|
|
94
|
+
// SAFETY: the entry file is the one Pagefind wrote into this bundle.
|
|
95
|
+
const { languages } = (await response.json()) as PagefindEntry;
|
|
96
|
+
const own = loadedLanguage(languages, language);
|
|
97
|
+
// Pagefind skips a merge whose path its own `basePath` starts with, as
|
|
98
|
+
// the same index twice, so the bundle is merged by its full URL.
|
|
99
|
+
await Promise.all(
|
|
100
|
+
Object.keys(languages)
|
|
101
|
+
.filter((code) => code !== own)
|
|
102
|
+
.map((code) =>
|
|
103
|
+
instance.mergeIndex(bundle.href, { baseUrl: "/", language: code })
|
|
104
|
+
)
|
|
105
|
+
);
|
|
106
|
+
return instance;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// Pagefind reads the page's language once, when an instance is created. A
|
|
110
|
+
// client-side language switch changes the page's language without a
|
|
111
|
+
// reload, so each language gets its own instance, created on its first
|
|
112
|
+
// search: after the switch, search moves to the new page's index. The
|
|
113
|
+
// every-language instance is one for the whole page load.
|
|
114
|
+
const instances = new Map<string, Promise<PagefindInstance>>();
|
|
115
|
+
const cached = (
|
|
116
|
+
key: string,
|
|
117
|
+
load: () => Promise<PagefindInstance>
|
|
118
|
+
): Promise<PagefindInstance> => {
|
|
119
|
+
const existing = instances.get(key);
|
|
120
|
+
if (existing) {
|
|
121
|
+
return existing;
|
|
122
|
+
}
|
|
123
|
+
const loading = (async () => {
|
|
124
|
+
try {
|
|
125
|
+
return await load();
|
|
126
|
+
} catch (error) {
|
|
127
|
+
// A failed load (the entry file's fetch, say) is forgotten, so the
|
|
128
|
+
// next search retries it.
|
|
129
|
+
instances.delete(key);
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
})();
|
|
133
|
+
instances.set(key, loading);
|
|
134
|
+
return loading;
|
|
135
|
+
};
|
|
136
|
+
|
|
33
137
|
// Pagefind builds its own marked-up excerpt; we keep its `<mark>` highlights
|
|
34
138
|
// (dropping any other markup — the excerpt is rendered via innerHTML) and
|
|
35
139
|
// only highlight the title ourselves. It carries no section/breadcrumb data,
|
|
36
140
|
// so pills stay hidden and the preview pane falls back to the excerpt.
|
|
37
|
-
return async (query) => {
|
|
38
|
-
|
|
141
|
+
return async (query, options) => {
|
|
142
|
+
// Lowercased like Pagefind's own reading, which names the entry's indexes.
|
|
143
|
+
const language = document.documentElement.lang.toLowerCase() || "unknown";
|
|
144
|
+
const instance = await (options?.locale
|
|
145
|
+
? cached(language, () => Promise.resolve(create()))
|
|
146
|
+
: cached("*", () => everyLanguage(language)));
|
|
147
|
+
const response = await instance.search(query);
|
|
39
148
|
const docs = await Promise.all(
|
|
40
149
|
response.results.slice(0, SEARCH_LIMIT).map((result) => result.data())
|
|
41
150
|
);
|
|
42
151
|
const hits = docs.map((doc) => ({
|
|
43
152
|
excerpt: sanitizeExcerpt(doc.excerpt),
|
|
44
153
|
title: highlight(doc.meta?.title ?? doc.url, query),
|
|
45
|
-
url: doc.url,
|
|
154
|
+
url: doc.url.replace(TRAILING_SLASH, ""),
|
|
46
155
|
}));
|
|
47
156
|
return { hits, sections: [] };
|
|
48
157
|
};
|
|
@@ -43,6 +43,18 @@ export type SearchFn = (
|
|
|
43
43
|
}
|
|
44
44
|
) => Promise<SearchResult>;
|
|
45
45
|
|
|
46
|
+
/** What a caller tells `createSearch` (`blume:search-client`) about its queries. */
|
|
47
|
+
export interface SearchClientOptions {
|
|
48
|
+
/**
|
|
49
|
+
* The queries come from a reader typing, as in the search dialog, so each
|
|
50
|
+
* one supersedes the last. The server-proxied client (Mixedbread), whose
|
|
51
|
+
* endpoint is rate-limited, then waits for a pause in typing before it
|
|
52
|
+
* sends a query and aborts the one a newer query supersedes. Other
|
|
53
|
+
* providers ignore it.
|
|
54
|
+
*/
|
|
55
|
+
typing?: boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
46
58
|
/** A document in the client-loaded `blume-search.json` index. */
|
|
47
59
|
export interface IndexedDocument {
|
|
48
60
|
route: string;
|
|
@@ -17,10 +17,19 @@ interface TypesenseRecord extends Record<string, unknown> {
|
|
|
17
17
|
* key. Documents are imported at build time by the sync step. Every adapter
|
|
18
18
|
* option Blume doesn't read (`connectionTimeoutSeconds`, `numRetries`, …) is
|
|
19
19
|
* the site's own client option and goes to the `Client` untouched; the named
|
|
20
|
-
* connection options still decide `apiKey` and `nodes`.
|
|
20
|
+
* connection options still decide `apiKey` and `nodes`. `locale` is the
|
|
21
|
+
* sync's: Typesense tokenizes a query for the fields it searches.
|
|
21
22
|
*/
|
|
22
23
|
export const createSearch = (opts: TypesenseOptions): SearchFn => {
|
|
23
|
-
const {
|
|
24
|
+
const {
|
|
25
|
+
apiKey,
|
|
26
|
+
collection,
|
|
27
|
+
host,
|
|
28
|
+
locale: _locale,
|
|
29
|
+
port,
|
|
30
|
+
protocol,
|
|
31
|
+
...clientOptions
|
|
32
|
+
} = opts;
|
|
24
33
|
const client = new Client({
|
|
25
34
|
...clientOptions,
|
|
26
35
|
apiKey,
|
|
@@ -35,10 +44,13 @@ export const createSearch = (opts: TypesenseOptions): SearchFn => {
|
|
|
35
44
|
per_page: SEARCH_LIMIT,
|
|
36
45
|
q: query,
|
|
37
46
|
query_by: "title,keywords,description,content",
|
|
38
|
-
// Relevance first,
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
|
|
47
|
+
// Relevance first, then each page's `search.boost` among pages that
|
|
48
|
+
// match equally well: a boost settles a tie without floating a page
|
|
49
|
+
// over a better match. Bucketing the relevance instead
|
|
50
|
+
// (`_text_match(buckets: 10)`) ranks by boost alone inside each
|
|
51
|
+
// bucket, so at equal boosts a bucket falls back to indexing order
|
|
52
|
+
// and the best match can land behind weaker ones.
|
|
53
|
+
sort_by: "_text_match:desc,boost:desc",
|
|
42
54
|
// The sync marks `locale` and `version` as facets so hosted results
|
|
43
55
|
// scope to the active language and the viewed docs version (the
|
|
44
56
|
// current docs upload as "current").
|
|
@@ -7,11 +7,12 @@ import { asyncApiOperationObject } from "../../openapi/asyncapi.ts";
|
|
|
7
7
|
import { operationOf, specOf } from "../../openapi/model.ts";
|
|
8
8
|
import { highlightCode } from "../../markdown/index.ts";
|
|
9
9
|
import {
|
|
10
|
-
|
|
10
|
+
asyncApiSecurity,
|
|
11
11
|
bindingGroups,
|
|
12
12
|
channelParameters,
|
|
13
|
-
|
|
13
|
+
kafkaTopic,
|
|
14
14
|
messageLabel,
|
|
15
|
+
namedChannelServers,
|
|
15
16
|
operationMessages,
|
|
16
17
|
payloadSchema,
|
|
17
18
|
protocolOf,
|
|
@@ -33,7 +34,6 @@ import PanelTabs from "./PanelTabs.astro";
|
|
|
33
34
|
import ParametersTable from "./ParametersTable.astro";
|
|
34
35
|
import SchemaTable from "./SchemaTable.astro";
|
|
35
36
|
import type { SecuritySchemeLike } from "./security.ts";
|
|
36
|
-
import { resolveAsyncApiSecurity } from "./security.ts";
|
|
37
37
|
import OperationPanel from "./OperationPanel.astro";
|
|
38
38
|
|
|
39
39
|
/**
|
|
@@ -61,9 +61,11 @@ const schemas = (document.components?.schemas ?? {}) as Record<
|
|
|
61
61
|
string,
|
|
62
62
|
SchemaLike
|
|
63
63
|
>;
|
|
64
|
-
const
|
|
65
|
-
const
|
|
66
|
-
|
|
64
|
+
const namedServers = namedChannelServers(channel, document);
|
|
65
|
+
const servers = namedServers.map(({ server }) => server);
|
|
66
|
+
const security = asyncApiSecurity(
|
|
67
|
+
operation,
|
|
68
|
+
namedServers,
|
|
67
69
|
document.components?.securitySchemes as
|
|
68
70
|
| Record<string, SecuritySchemeLike>
|
|
69
71
|
| undefined
|
|
@@ -96,10 +98,15 @@ const messagePanels = await Promise.all(
|
|
|
96
98
|
|
|
97
99
|
// One derived model feeds the static default sample, the composer form, and
|
|
98
100
|
// (client-side) the live samples + WebSocket frame — they can never drift.
|
|
101
|
+
// A Kafka channel binding's `topic` names the real topic when the address
|
|
102
|
+
// doesn't, so the samples use it.
|
|
103
|
+
const topic =
|
|
104
|
+
protocol === "kafka" ? kafkaTopic(channel, document.components) : undefined;
|
|
99
105
|
const model = ref
|
|
100
106
|
? messageModel({
|
|
101
107
|
action: ref.method === "send" ? "send" : "receive",
|
|
102
|
-
address: ref.path,
|
|
108
|
+
address: topic ?? ref.path,
|
|
109
|
+
components: document.components,
|
|
103
110
|
messages,
|
|
104
111
|
parameters,
|
|
105
112
|
protocol,
|
|
@@ -28,6 +28,13 @@ const { security } = Astro.props;
|
|
|
28
28
|
Optional — this operation also accepts unauthenticated requests.
|
|
29
29
|
</p>
|
|
30
30
|
)}
|
|
31
|
+
{security.unauthenticatedServers &&
|
|
32
|
+
security.unauthenticatedServers.length > 0 && (
|
|
33
|
+
<p class="mb-2 text-muted-foreground text-sm">
|
|
34
|
+
No authorization needed on{" "}
|
|
35
|
+
{security.unauthenticatedServers.join(", ")}.
|
|
36
|
+
</p>
|
|
37
|
+
)}
|
|
31
38
|
{security.alternatives.map((alternative, index) => (
|
|
32
39
|
<>
|
|
33
40
|
{index > 0 && (
|
|
@@ -47,6 +54,9 @@ const { security } = Astro.props;
|
|
|
47
54
|
<span class="text-muted-foreground text-xs">
|
|
48
55
|
{schemeLabel(resolved)}
|
|
49
56
|
{carrier ? ` · ${carrier.in}` : ""}
|
|
57
|
+
{resolved.servers
|
|
58
|
+
? ` · on ${resolved.servers.join(", ")}`
|
|
59
|
+
: ""}
|
|
50
60
|
</span>
|
|
51
61
|
{!security.optional && (
|
|
52
62
|
<span class="font-medium text-[0.625rem] text-red-600 uppercase tracking-wide dark:text-red-400">
|
|
@@ -19,10 +19,12 @@ import {
|
|
|
19
19
|
exampleVariables,
|
|
20
20
|
graphqlPlaygroundModel,
|
|
21
21
|
graphqlRoutes,
|
|
22
|
+
graphqlSecurity,
|
|
22
23
|
} from "./graphql-helpers.ts";
|
|
23
24
|
import { buildRequest, defaultValues } from "./request.ts";
|
|
24
25
|
import { languageSamplePanels } from "./sample-panels.ts";
|
|
25
26
|
import { sampleLanguages } from "./snippets.ts";
|
|
27
|
+
import Authorization from "./Authorization.astro";
|
|
26
28
|
import Description from "./Description.astro";
|
|
27
29
|
import GraphqlChip from "./GraphqlChip.astro";
|
|
28
30
|
import GraphqlFieldsTable from "./GraphqlFieldsTable.astro";
|
|
@@ -107,6 +109,7 @@ const responsePanels =
|
|
|
107
109
|
: [];
|
|
108
110
|
|
|
109
111
|
const returnRoute = field ? routes.get(field.type.name) : undefined;
|
|
112
|
+
const security = spec ? graphqlSecurity(spec) : undefined;
|
|
110
113
|
---
|
|
111
114
|
|
|
112
115
|
{
|
|
@@ -134,6 +137,7 @@ const returnRoute = field ? routes.get(field.type.name) : undefined;
|
|
|
134
137
|
Deprecated: <span set:text={field.deprecationReason} />
|
|
135
138
|
</p>
|
|
136
139
|
)}
|
|
140
|
+
{security && <Authorization security={security} />}
|
|
137
141
|
<GraphqlFieldsTable
|
|
138
142
|
routes={routes}
|
|
139
143
|
rows={field.args}
|
|
@@ -169,6 +173,7 @@ const returnRoute = field ? routes.get(field.type.name) : undefined;
|
|
|
169
173
|
model={model}
|
|
170
174
|
operation={id}
|
|
171
175
|
proxy={spec.playground.proxy}
|
|
176
|
+
proxyConfig="graphql"
|
|
172
177
|
slug={spec.slug}
|
|
173
178
|
/>
|
|
174
179
|
)}
|
|
@@ -131,6 +131,10 @@ const params = mergeParameters(
|
|
|
131
131
|
const requestBody = operation?.requestBody
|
|
132
132
|
? resolveComponentRef(operation.requestBody, components, "requestBodies")
|
|
133
133
|
: undefined;
|
|
134
|
+
// A request body table leaves out `readOnly` fields. A webhook's or
|
|
135
|
+
// callback's body is a request the API sends, so it reads like a response
|
|
136
|
+
// (and like its payload example): `readOnly` fields in, `writeOnly` out.
|
|
137
|
+
const bodyDirection = isWebhook ? "response" : "request";
|
|
134
138
|
const responses = Object.fromEntries(
|
|
135
139
|
Object.entries(operation?.responses ?? {}).map(([status, response]) => [
|
|
136
140
|
status,
|
|
@@ -178,6 +182,8 @@ interface CallbackEntry {
|
|
|
178
182
|
method: string;
|
|
179
183
|
name: string;
|
|
180
184
|
operation: FullOperation;
|
|
185
|
+
/** The callback path item's own parameters, which each method inherits. */
|
|
186
|
+
pathParameters?: ParameterLike[];
|
|
181
187
|
}
|
|
182
188
|
|
|
183
189
|
// Callbacks: for each named callback (resolved through
|
|
@@ -202,6 +208,10 @@ const callbacks: CallbackEntry[] = Object.entries(
|
|
|
202
208
|
// SAFETY: a path item's method entry is an operation object; the
|
|
203
209
|
// object check above guards a spec that put a scalar there.
|
|
204
210
|
operation: entry as FullOperation,
|
|
211
|
+
// SAFETY: the object check above passed, and a path item's
|
|
212
|
+
// `parameters` is a parameter list, which `mergeParameters`
|
|
213
|
+
// reads the way it reads an operation's.
|
|
214
|
+
pathParameters: (item as PathItemLike).parameters,
|
|
205
215
|
},
|
|
206
216
|
]
|
|
207
217
|
: [];
|
|
@@ -209,8 +219,13 @@ const callbacks: CallbackEntry[] = Object.entries(
|
|
|
209
219
|
});
|
|
210
220
|
});
|
|
211
221
|
|
|
212
|
-
/** A callback's request body and responses, `$ref`s resolved. */
|
|
222
|
+
/** A callback's parameters, request body, and responses, `$ref`s resolved. */
|
|
213
223
|
const callbackParts = (entry: CallbackEntry) => ({
|
|
224
|
+
parameters: mergeParameters(
|
|
225
|
+
entry.pathParameters,
|
|
226
|
+
entry.operation.parameters,
|
|
227
|
+
components
|
|
228
|
+
),
|
|
214
229
|
requestBody: entry.operation.requestBody
|
|
215
230
|
? resolveComponentRef(entry.operation.requestBody, components, "requestBodies")
|
|
216
231
|
: undefined,
|
|
@@ -253,6 +268,7 @@ const callbackParts = (entry: CallbackEntry) => ({
|
|
|
253
268
|
<ParametersTable parameters={params} schemas={schemas} />
|
|
254
269
|
{requestBody && (
|
|
255
270
|
<RequestBody
|
|
271
|
+
direction={bodyDirection}
|
|
256
272
|
expandAll={spec.expandSchemas}
|
|
257
273
|
requestBody={requestBody}
|
|
258
274
|
schemas={schemas}
|
|
@@ -298,8 +314,14 @@ const callbackParts = (entry: CallbackEntry) => ({
|
|
|
298
314
|
text={entry.operation.description}
|
|
299
315
|
/>
|
|
300
316
|
)}
|
|
317
|
+
<ParametersTable
|
|
318
|
+
level={4}
|
|
319
|
+
parameters={parts.parameters}
|
|
320
|
+
schemas={schemas}
|
|
321
|
+
/>
|
|
301
322
|
{parts.requestBody && (
|
|
302
323
|
<RequestBody
|
|
324
|
+
direction="response"
|
|
303
325
|
expandAll={spec.expandSchemas}
|
|
304
326
|
level={4}
|
|
305
327
|
requestBody={parts.requestBody}
|
|
@@ -339,6 +361,7 @@ const callbackParts = (entry: CallbackEntry) => ({
|
|
|
339
361
|
model={model}
|
|
340
362
|
operation={id}
|
|
341
363
|
proxy={spec.playground.proxy}
|
|
364
|
+
proxyConfig="openapi"
|
|
342
365
|
slug={spec.slug}
|
|
343
366
|
/>
|
|
344
367
|
)}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import Description from "./Description.astro";
|
|
3
3
|
import {
|
|
4
4
|
constraints,
|
|
5
|
+
parameterDescription,
|
|
5
6
|
resolveSchema,
|
|
6
7
|
type SchemaLike,
|
|
7
8
|
typeLabel,
|
|
@@ -19,9 +20,11 @@ interface ParamLike {
|
|
|
19
20
|
interface Props {
|
|
20
21
|
parameters: ParamLike[];
|
|
21
22
|
schemas: Record<string, SchemaLike>;
|
|
23
|
+
/** The headings' level: 2 on an operation, deeper inside a callback. */
|
|
24
|
+
level?: number;
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
const { parameters, schemas } = Astro.props;
|
|
27
|
+
const { parameters, schemas, level = 2 } = Astro.props;
|
|
25
28
|
|
|
26
29
|
const SECTIONS: { in: string; title: string }[] = [
|
|
27
30
|
{ in: "path", title: "Path parameters" },
|
|
@@ -42,7 +45,7 @@ const groups = SECTIONS.map((section) => ({
|
|
|
42
45
|
groups.map((group) => (
|
|
43
46
|
<section class="mt-6">
|
|
44
47
|
<div
|
|
45
|
-
aria-level=
|
|
48
|
+
aria-level={level}
|
|
46
49
|
class="mb-2 font-semibold text-foreground text-sm"
|
|
47
50
|
role="heading"
|
|
48
51
|
>
|
|
@@ -54,6 +57,7 @@ const groups = SECTIONS.map((section) => ({
|
|
|
54
57
|
const type = param.schema ? typeLabel(param.schema) : "string";
|
|
55
58
|
const limits = constraints(resolved);
|
|
56
59
|
const enumValues = Array.isArray(resolved.enum) ? resolved.enum : null;
|
|
60
|
+
const description = parameterDescription(param, schemas);
|
|
57
61
|
return (
|
|
58
62
|
<div class="border-border border-t py-4 first:border-t-0">
|
|
59
63
|
<div class="flex flex-wrap items-baseline gap-x-2 gap-y-1">
|
|
@@ -70,8 +74,8 @@ const groups = SECTIONS.map((section) => ({
|
|
|
70
74
|
</span>
|
|
71
75
|
)}
|
|
72
76
|
</div>
|
|
73
|
-
{
|
|
74
|
-
<Description class="mt-2 text-muted-foreground text-sm" text={
|
|
77
|
+
{description && (
|
|
78
|
+
<Description class="mt-2 text-muted-foreground text-sm" text={description} />
|
|
75
79
|
)}
|
|
76
80
|
{limits.length > 0 && (
|
|
77
81
|
<div class="mt-2 text-muted-foreground text-xs">
|
|
@@ -17,12 +17,17 @@ interface Props {
|
|
|
17
17
|
model: PlaygroundModel;
|
|
18
18
|
/** Proxy route for the send, or false to fetch the API directly. */
|
|
19
19
|
proxy: string | false;
|
|
20
|
+
/**
|
|
21
|
+
* Where this panel's proxy is configured, which the CORS message names:
|
|
22
|
+
* an `openapi()` or `graphql()` reference, or `api` for hand-written pages.
|
|
23
|
+
*/
|
|
24
|
+
proxyConfig: "api" | "graphql" | "openapi";
|
|
20
25
|
/** Spec slug + operation key form the per-operation localStorage key. */
|
|
21
26
|
slug: string;
|
|
22
27
|
operation: string;
|
|
23
28
|
}
|
|
24
29
|
|
|
25
|
-
const { model, proxy, slug, operation } = Astro.props;
|
|
30
|
+
const { model, proxy, proxyConfig, slug, operation } = Astro.props;
|
|
26
31
|
|
|
27
32
|
// The built-in proxy route carries the site `basePath` but, like every
|
|
28
33
|
// internal URL in the spec data, not `deployment.base`; `withMountedBase`
|
|
@@ -53,6 +58,7 @@ const CHECKBOX_ROW =
|
|
|
53
58
|
<blume-playground
|
|
54
59
|
class="mb-6 block"
|
|
55
60
|
data-proxy={proxyUrl}
|
|
61
|
+
data-proxy-config={proxyConfig}
|
|
56
62
|
data-storage-key={`blume-playground:${slug}:${operation}`}
|
|
57
63
|
>
|
|
58
64
|
<script
|