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
package/src/search/sync/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BlumeError } from "../../core/diagnostics.ts";
|
|
1
2
|
import type { BlumeProject } from "../../core/project-graph.ts";
|
|
2
3
|
import type { ResolvedSearchAdapter } from "../adapters/registry.ts";
|
|
3
4
|
import { buildSearchDocuments, toSearchRecords } from "../documents.ts";
|
|
@@ -13,23 +14,49 @@ export interface SyncReporter {
|
|
|
13
14
|
warn: (message: string) => void;
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
/** A hosted adapter's build-time sync. */
|
|
18
|
+
interface HostedSync {
|
|
19
|
+
/** The env var holding the admin key the sync reads. */
|
|
20
|
+
key: string;
|
|
21
|
+
/**
|
|
22
|
+
* Whether the sync is set up to run: its admin key is set, and for Orama
|
|
23
|
+
* Cloud, the `indexId` that turns the sync on.
|
|
24
|
+
*/
|
|
25
|
+
ready: boolean;
|
|
26
|
+
/** The upload, closed over the adapter's options. */
|
|
27
|
+
run: (records: SearchRecord[]) => Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
|
|
16
30
|
/**
|
|
17
|
-
* The sync a hosted adapter runs after a build,
|
|
18
|
-
*
|
|
19
|
-
*
|
|
31
|
+
* The sync a hosted adapter runs after a build, or `null` for adapters with no
|
|
32
|
+
* in-process sync (static and Pagefind indexes are built locally; Mixedbread
|
|
33
|
+
* syncs out-of-band via the `mxbai` CLI).
|
|
20
34
|
*/
|
|
21
|
-
const searchSync = (
|
|
22
|
-
provider: ResolvedSearchAdapter
|
|
23
|
-
): ((records: SearchRecord[]) => Promise<void>) | null => {
|
|
35
|
+
const searchSync = (provider: ResolvedSearchAdapter): HostedSync | null => {
|
|
24
36
|
switch (provider.kind) {
|
|
25
37
|
case "algolia": {
|
|
26
|
-
|
|
38
|
+
const key = "ALGOLIA_ADMIN_API_KEY";
|
|
39
|
+
return {
|
|
40
|
+
key,
|
|
41
|
+
ready: Boolean(process.env[key]),
|
|
42
|
+
run: (records) => syncAlgolia(records, provider.options),
|
|
43
|
+
};
|
|
27
44
|
}
|
|
28
45
|
case "orama-cloud": {
|
|
29
|
-
|
|
46
|
+
const key = "ORAMA_PRIVATE_API_KEY";
|
|
47
|
+
return {
|
|
48
|
+
key,
|
|
49
|
+
ready: Boolean(process.env[key] && provider.options.indexId),
|
|
50
|
+
run: (records) => syncOramaCloud(records, provider.options),
|
|
51
|
+
};
|
|
30
52
|
}
|
|
31
53
|
case "typesense": {
|
|
32
|
-
|
|
54
|
+
const key = "TYPESENSE_ADMIN_API_KEY";
|
|
55
|
+
return {
|
|
56
|
+
key,
|
|
57
|
+
ready: Boolean(process.env[key]),
|
|
58
|
+
run: (records) => syncTypesense(records, provider.options),
|
|
59
|
+
};
|
|
33
60
|
}
|
|
34
61
|
default: {
|
|
35
62
|
return null;
|
|
@@ -41,7 +68,9 @@ const searchSync = (
|
|
|
41
68
|
* After a build, upload the per-page records to the configured hosted adapter.
|
|
42
69
|
* Reads secret admin keys from the environment; if a key (or option) is
|
|
43
70
|
* missing the sync is skipped with a warning rather than failing the build —
|
|
44
|
-
* CI may sync in a separate step.
|
|
71
|
+
* a local build has no secrets, and CI may sync in a separate step. Once the
|
|
72
|
+
* sync is set up, a failure fails the build (`BLUME_SEARCH_SYNC_FAILED`):
|
|
73
|
+
* the site would otherwise deploy against an index this build didn't update.
|
|
45
74
|
*/
|
|
46
75
|
export const syncSearchProvider = async (
|
|
47
76
|
project: BlumeProject,
|
|
@@ -57,11 +86,21 @@ export const syncSearchProvider = async (
|
|
|
57
86
|
reporter.start(`Syncing ${records.length} record(s) to ${provider.kind}`);
|
|
58
87
|
|
|
59
88
|
try {
|
|
60
|
-
await sync(records);
|
|
89
|
+
await sync.run(records);
|
|
61
90
|
reporter.success(`Synced ${records.length} record(s) to ${provider.kind}`);
|
|
62
91
|
} catch (error) {
|
|
63
92
|
// SAFETY: the three sync clients surface network/auth failures as Error
|
|
64
|
-
// instances; the message is read only to annotate the
|
|
65
|
-
|
|
93
|
+
// instances; the message is read only to annotate the warning or error.
|
|
94
|
+
const { message } = error as Error;
|
|
95
|
+
if (!sync.ready) {
|
|
96
|
+
reporter.warn(`Search sync skipped: ${message}`);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
throw new BlumeError({
|
|
100
|
+
code: "BLUME_SEARCH_SYNC_FAILED",
|
|
101
|
+
message: `Search sync to ${provider.kind} failed: ${message}`,
|
|
102
|
+
severity: "error",
|
|
103
|
+
suggestion: `Check ${sync.key} and the adapter's options, or unset ${sync.key} to build without syncing.`,
|
|
104
|
+
});
|
|
66
105
|
}
|
|
67
106
|
};
|
|
@@ -7,17 +7,44 @@ import type { SearchRecord } from "../documents.ts";
|
|
|
7
7
|
/** The adapter options the sync reads (the public `apiKey` is never used). */
|
|
8
8
|
export type TypesenseSyncConfig = Pick<
|
|
9
9
|
TypesenseOptions,
|
|
10
|
-
"collection" | "host" | "port" | "protocol"
|
|
10
|
+
"collection" | "host" | "locale" | "port" | "protocol"
|
|
11
11
|
>;
|
|
12
12
|
|
|
13
|
+
type TypesenseClient = InstanceType<typeof TypesenseSdk.Client>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The collection an alias points at, or `undefined` when there is no such
|
|
17
|
+
* alias. Only a 404 means that: any other failure (a key that can't read
|
|
18
|
+
* aliases, an outage) throws before the sync has touched anything.
|
|
19
|
+
*/
|
|
20
|
+
const aliasTarget = async (
|
|
21
|
+
client: TypesenseClient,
|
|
22
|
+
alias: string,
|
|
23
|
+
errors: typeof TypesenseSdk.Errors
|
|
24
|
+
): Promise<string | undefined> => {
|
|
25
|
+
try {
|
|
26
|
+
const { collection_name: target } = await client.aliases(alias).retrieve();
|
|
27
|
+
return target;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
if (error instanceof errors.ObjectNotFound) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
13
36
|
/**
|
|
14
|
-
* Import the search records into
|
|
15
|
-
*
|
|
16
|
-
* warn.
|
|
37
|
+
* Import the search records into Typesense. Uses the admin key from
|
|
38
|
+
* `TYPESENSE_ADMIN_API_KEY`. Throws on a missing key so the caller can warn.
|
|
17
39
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
40
|
+
* `collection` names an alias. Each sync imports every record into a new
|
|
41
|
+
* collection (`<collection>_<timestamp>`), points the alias at it, then drops
|
|
42
|
+
* the collection it replaced: searches keep reading the previous collection
|
|
43
|
+
* until the new one is complete, pages deleted or renamed since the last sync
|
|
44
|
+
* don't linger as stale hits that 404 when clicked, and a sync that fails
|
|
45
|
+
* leaves the previous collection serving. A collection named `collection`
|
|
46
|
+
* itself, from a sync before the alias existed, is dropped just before the
|
|
47
|
+
* alias takes its name, since Typesense won't let the two share one.
|
|
21
48
|
*/
|
|
22
49
|
export const syncTypesense = async (
|
|
23
50
|
records: SearchRecord[],
|
|
@@ -29,7 +56,7 @@ export const syncTypesense = async (
|
|
|
29
56
|
}
|
|
30
57
|
// `require`, not `import()`: this runs in `astro:build:done` (see
|
|
31
58
|
// `core/node-require.ts`).
|
|
32
|
-
const { Client }: typeof TypesenseSdk = nodeRequire("typesense");
|
|
59
|
+
const { Client, Errors }: typeof TypesenseSdk = nodeRequire("typesense");
|
|
33
60
|
const client = new Client({
|
|
34
61
|
apiKey: adminKey,
|
|
35
62
|
nodes: [
|
|
@@ -41,23 +68,18 @@ export const syncTypesense = async (
|
|
|
41
68
|
],
|
|
42
69
|
});
|
|
43
70
|
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
exists = false;
|
|
50
|
-
}
|
|
51
|
-
if (exists) {
|
|
52
|
-
await collection.delete();
|
|
53
|
-
}
|
|
71
|
+
const alias = config.collection;
|
|
72
|
+
const previous = await aliasTarget(client, alias, Errors);
|
|
73
|
+
const name = `${alias}_${Date.now()}`;
|
|
74
|
+
// The searched text fields, tokenized for `locale` when the site sets one.
|
|
75
|
+
const text = config.locale ? { locale: config.locale } : {};
|
|
54
76
|
await client.collections().create({
|
|
55
77
|
fields: [
|
|
56
|
-
{ name: "title", type: "string" },
|
|
57
|
-
{ name: "description", optional: true, type: "string" },
|
|
58
|
-
{ name: "content", type: "string" },
|
|
78
|
+
{ name: "title", type: "string", ...text },
|
|
79
|
+
{ name: "description", optional: true, type: "string", ...text },
|
|
80
|
+
{ name: "content", type: "string", ...text },
|
|
59
81
|
{ name: "url", type: "string" },
|
|
60
|
-
{ name: "keywords", optional: true, type: "string[]" },
|
|
82
|
+
{ name: "keywords", optional: true, type: "string[]", ...text },
|
|
61
83
|
// The dialog sorts close matches by it (search.boost, 1 by default).
|
|
62
84
|
{ name: "boost", type: "float" },
|
|
63
85
|
{ facet: true, name: "tag", optional: true, type: "string" },
|
|
@@ -66,7 +88,7 @@ export const syncTypesense = async (
|
|
|
66
88
|
{ facet: true, name: "locale", optional: true, type: "string" },
|
|
67
89
|
{ facet: true, name: "version", optional: true, type: "string" },
|
|
68
90
|
],
|
|
69
|
-
name
|
|
91
|
+
name,
|
|
70
92
|
});
|
|
71
93
|
|
|
72
94
|
const documents = records.map((record) => ({
|
|
@@ -81,8 +103,31 @@ export const syncTypesense = async (
|
|
|
81
103
|
url: record.url,
|
|
82
104
|
version: record.version,
|
|
83
105
|
}));
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
106
|
+
try {
|
|
107
|
+
await client
|
|
108
|
+
.collections(name)
|
|
109
|
+
.documents()
|
|
110
|
+
.import(documents, { action: "upsert" });
|
|
111
|
+
} catch (error) {
|
|
112
|
+
// Nothing reads the new collection yet: drop it, and the alias keeps
|
|
113
|
+
// serving the last complete sync.
|
|
114
|
+
await client.collections(name).delete();
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (previous === undefined) {
|
|
119
|
+
let legacy = true;
|
|
120
|
+
try {
|
|
121
|
+
await client.collections(alias).retrieve();
|
|
122
|
+
} catch {
|
|
123
|
+
legacy = false;
|
|
124
|
+
}
|
|
125
|
+
if (legacy) {
|
|
126
|
+
await client.collections(alias).delete();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
await client.aliases().upsert(alias, { collection_name: name });
|
|
130
|
+
if (previous !== undefined) {
|
|
131
|
+
await client.collections(previous).delete();
|
|
132
|
+
}
|
|
88
133
|
};
|
package/src/seo/jsonld.ts
CHANGED
|
@@ -19,7 +19,7 @@ export interface PostalAddressIdentity {
|
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* `seo.organization`: the organization behind the site, emitted on every page
|
|
22
|
-
* as an `Organization` node the WebSite and
|
|
22
|
+
* as an `Organization` node the WebSite and page nodes cite as publisher.
|
|
23
23
|
*/
|
|
24
24
|
export interface OrganizationIdentity {
|
|
25
25
|
address?: PostalAddressIdentity;
|
|
@@ -73,6 +73,12 @@ export interface StructuredDataInput {
|
|
|
73
73
|
description?: string;
|
|
74
74
|
/** Page route, e.g. `/blog/post`. */
|
|
75
75
|
route: string;
|
|
76
|
+
/**
|
|
77
|
+
* The route of this page's homepage: the navigation tree root
|
|
78
|
+
* (`navigation.root` — `/fr` for a locale, `/docs` under a `basePath`,
|
|
79
|
+
* `/v1.0` in an archived version). Defaults to `/`.
|
|
80
|
+
*/
|
|
81
|
+
homeRoute?: string;
|
|
76
82
|
/** Deployment base (`import.meta.env.BASE_URL`); prefixed onto absolute URLs. */
|
|
77
83
|
base?: string;
|
|
78
84
|
/** Content type — `blog` and `changelog` map to richer article types. */
|
|
@@ -246,24 +252,41 @@ const softwareNode = (
|
|
|
246
252
|
return node;
|
|
247
253
|
};
|
|
248
254
|
|
|
249
|
-
/** The page
|
|
250
|
-
const
|
|
255
|
+
/** The page's schema.org `@type`: a `WebPage` for a homepage, else an article. */
|
|
256
|
+
const pageSchemaType = (input: StructuredDataInput, home: boolean): string => {
|
|
257
|
+
if (home) {
|
|
258
|
+
return "WebPage";
|
|
259
|
+
}
|
|
260
|
+
const pageType = input.pageType ?? "";
|
|
261
|
+
return isArticleType(pageType) ? ARTICLE_TYPES[pageType] : "TechArticle";
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* The page's own node: a homepage is a `WebPage` (it isn't an article, but it
|
|
266
|
+
* still needs a machine-readable date, or search engines take whatever other
|
|
267
|
+
* date the page shows), and every other page an article (`BlogPosting`/
|
|
268
|
+
* `TechArticle`) with a `headline`. Both carry the language, description,
|
|
269
|
+
* dates, the WebSite they belong to, and the publisher.
|
|
270
|
+
*/
|
|
271
|
+
const pageNode = (
|
|
251
272
|
input: StructuredDataInput,
|
|
252
273
|
context: {
|
|
253
274
|
base: string | null;
|
|
275
|
+
home: boolean;
|
|
254
276
|
organizationId: string | null;
|
|
255
277
|
pageUrl: string;
|
|
256
278
|
}
|
|
257
279
|
): JsonLdNode => {
|
|
258
|
-
const pageType = input.pageType ?? "";
|
|
259
280
|
const node: JsonLdNode = {
|
|
260
281
|
"@id": `${context.pageUrl}#page`,
|
|
261
|
-
"@type":
|
|
262
|
-
headline: input.title,
|
|
282
|
+
"@type": pageSchemaType(input, context.home),
|
|
263
283
|
inLanguage: input.locale || "en",
|
|
264
284
|
name: input.title,
|
|
265
285
|
url: context.pageUrl,
|
|
266
286
|
};
|
|
287
|
+
if (!context.home) {
|
|
288
|
+
node.headline = input.title;
|
|
289
|
+
}
|
|
267
290
|
if (input.description) {
|
|
268
291
|
node.description = input.description;
|
|
269
292
|
}
|
|
@@ -315,14 +338,11 @@ const breadcrumbNode = (
|
|
|
315
338
|
/**
|
|
316
339
|
* Build a schema.org JSON-LD `@graph` for a page: site identity (the WebSite,
|
|
317
340
|
* plus the configured Organization everywhere and the SoftwareApplication on
|
|
318
|
-
* the homepage), the page
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
* route-relative.
|
|
341
|
+
* the homepage), the page itself (a WebPage on the homepage, an article
|
|
342
|
+
* elsewhere), and an article's breadcrumb trail. URLs are absolute when
|
|
343
|
+
* `siteUrl` is set, otherwise route-relative.
|
|
322
344
|
*/
|
|
323
|
-
export const buildStructuredData = (
|
|
324
|
-
input: StructuredDataInput
|
|
325
|
-
): JsonLdNode | null => {
|
|
345
|
+
export const buildStructuredData = (input: StructuredDataInput): JsonLdNode => {
|
|
326
346
|
const base = input.siteUrl ? trimSlash(input.siteUrl) : null;
|
|
327
347
|
// Routes carry `basePath`; a `deployment.base` subdirectory is layered on top
|
|
328
348
|
// so JSON-LD URLs match the served location.
|
|
@@ -365,9 +385,11 @@ export const buildStructuredData = (
|
|
|
365
385
|
);
|
|
366
386
|
}
|
|
367
387
|
|
|
368
|
-
// The homepage is
|
|
369
|
-
//
|
|
370
|
-
|
|
388
|
+
// The homepage is a WebPage, next to the product when one is configured;
|
|
389
|
+
// deeper pages are articles with a breadcrumb trail.
|
|
390
|
+
const home = input.route === (input.homeRoute ?? "/");
|
|
391
|
+
graph.push(pageNode(input, { base, home, organizationId, pageUrl }));
|
|
392
|
+
if (home) {
|
|
371
393
|
if (software && base) {
|
|
372
394
|
graph.push(
|
|
373
395
|
softwareNode(software, {
|
|
@@ -380,15 +402,11 @@ export const buildStructuredData = (
|
|
|
380
402
|
);
|
|
381
403
|
}
|
|
382
404
|
} else {
|
|
383
|
-
graph.push(articleNode(input, { base, organizationId, pageUrl }));
|
|
384
405
|
const breadcrumbs = breadcrumbNode(input.breadcrumbs, base, deployBase);
|
|
385
406
|
if (breadcrumbs) {
|
|
386
407
|
graph.push(breadcrumbs);
|
|
387
408
|
}
|
|
388
409
|
}
|
|
389
410
|
|
|
390
|
-
if (graph.length === 0) {
|
|
391
|
-
return null;
|
|
392
|
-
}
|
|
393
411
|
return { "@context": "https://schema.org", "@graph": graph };
|
|
394
412
|
};
|
|
@@ -21,10 +21,20 @@ export interface ContentfulOptions extends SharedSourceOptions {
|
|
|
21
21
|
* / `sys.updatedAt`.
|
|
22
22
|
*/
|
|
23
23
|
fields?: SourceFieldMap;
|
|
24
|
+
/**
|
|
25
|
+
* Delivery API host name. Defaults to `cdn.contentful.com`; a space with
|
|
26
|
+
* EU data residency reads from `cdn.eu.contentful.com`.
|
|
27
|
+
*/
|
|
28
|
+
host?: string;
|
|
24
29
|
/** Locale code to fetch; omit for the space's default locale. */
|
|
25
30
|
locale?: string;
|
|
26
31
|
/** Extra query parameters for the entries request (`"fields.section": "sdk"`). */
|
|
27
32
|
params?: Record<string, string>;
|
|
33
|
+
/**
|
|
34
|
+
* Preview API host name for `--preview`. Defaults to
|
|
35
|
+
* `preview.contentful.com` (`preview.eu.contentful.com` in the EU).
|
|
36
|
+
*/
|
|
37
|
+
previewHost?: string;
|
|
28
38
|
/** Space id. */
|
|
29
39
|
space: string;
|
|
30
40
|
}
|
|
@@ -33,8 +43,10 @@ export const contentfulOptionsSchema = sharedSourceOptionsSchema.extend({
|
|
|
33
43
|
contentType: z.string(),
|
|
34
44
|
environment: z.string().optional(),
|
|
35
45
|
fields: sourceFieldMapSchema.optional(),
|
|
46
|
+
host: z.string().optional(),
|
|
36
47
|
locale: z.string().optional(),
|
|
37
48
|
params: queryParamsSchema.optional(),
|
|
49
|
+
previewHost: z.string().optional(),
|
|
38
50
|
space: z.string(),
|
|
39
51
|
});
|
|
40
52
|
|
|
@@ -7,7 +7,12 @@ import { sharedSourceOptionsSchema } from "./shared.ts";
|
|
|
7
7
|
|
|
8
8
|
/** Options for {@link githubReleases}. */
|
|
9
9
|
export interface GithubReleasesOptions extends SharedSourceOptions {
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* The GitHub REST API base URL. Defaults to `https://api.github.com`; a
|
|
12
|
+
* GitHub Enterprise Server's is `https://<host>/api/v3`.
|
|
13
|
+
*/
|
|
14
|
+
baseUrl?: string;
|
|
15
|
+
/** Include draft releases, tagged `Draft` (needs a token with repo write access). */
|
|
11
16
|
drafts?: boolean;
|
|
12
17
|
/** Cap the number of releases materialized, newest-first. Defaults to 100. */
|
|
13
18
|
limit?: number;
|
|
@@ -20,6 +25,7 @@ export interface GithubReleasesOptions extends SharedSourceOptions {
|
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
export const githubReleasesOptionsSchema = sharedSourceOptionsSchema.extend({
|
|
28
|
+
baseUrl: z.string().optional(),
|
|
23
29
|
drafts: z.boolean().optional(),
|
|
24
30
|
limit: z.number().positive().optional(),
|
|
25
31
|
owner: z.string(),
|
package/src/sources/notion.ts
CHANGED
|
@@ -11,6 +11,12 @@ export interface NotionOptions extends SharedSourceOptions {
|
|
|
11
11
|
concurrency?: number;
|
|
12
12
|
/** Notion database id. */
|
|
13
13
|
database: string;
|
|
14
|
+
/**
|
|
15
|
+
* Read text typed in Notion as MDX, so components and Markdown in it
|
|
16
|
+
* render. Anyone who can edit the database can then run code at build
|
|
17
|
+
* time. Default false: text renders as written.
|
|
18
|
+
*/
|
|
19
|
+
mdx?: boolean;
|
|
14
20
|
/** Notion property names mapped onto Blume meta. */
|
|
15
21
|
properties?: {
|
|
16
22
|
/** Property holding the page description. */
|
|
@@ -31,6 +37,7 @@ export interface NotionOptions extends SharedSourceOptions {
|
|
|
31
37
|
export const notionOptionsSchema = sharedSourceOptionsSchema.extend({
|
|
32
38
|
concurrency: z.number().positive().optional(),
|
|
33
39
|
database: z.string(),
|
|
40
|
+
mdx: z.boolean().optional(),
|
|
34
41
|
properties: z
|
|
35
42
|
.strictObject({
|
|
36
43
|
description: z.string().optional(),
|
package/src/sources/shared.ts
CHANGED
|
@@ -33,6 +33,8 @@ export interface SourceFieldMap {
|
|
|
33
33
|
description?: string;
|
|
34
34
|
/** Field holding the last-modified date. */
|
|
35
35
|
lastModified?: string;
|
|
36
|
+
/** Field holding the sidebar order (a number). No default: unset, none is read. */
|
|
37
|
+
order?: string;
|
|
36
38
|
/** Field holding the page slug. */
|
|
37
39
|
slug?: string;
|
|
38
40
|
/** Field holding the page title. */
|
|
@@ -44,6 +46,7 @@ export const sourceFieldMapSchema = z.strictObject({
|
|
|
44
46
|
body: z.string().optional(),
|
|
45
47
|
description: z.string().optional(),
|
|
46
48
|
lastModified: z.string().optional(),
|
|
49
|
+
order: z.string().optional(),
|
|
47
50
|
slug: z.string().optional(),
|
|
48
51
|
title: z.string().optional(),
|
|
49
52
|
});
|
package/src/theme/color.ts
CHANGED
|
@@ -374,16 +374,15 @@ const rectangular = (lightness: number, chroma: number, hue: number) => {
|
|
|
374
374
|
/** A color function's channels, converted to sRGB with `alpha`. */
|
|
375
375
|
type Converter = (channels: Channels, alpha: number) => Rgba;
|
|
376
376
|
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
};
|
|
377
|
+
const rgbChannel = (component: Component): number =>
|
|
378
|
+
clamp(scaled(component, 255) / 255);
|
|
379
|
+
|
|
380
|
+
const rgb: Converter = ([red, green, blue], alpha) => ({
|
|
381
|
+
alpha,
|
|
382
|
+
blue: rgbChannel(blue),
|
|
383
|
+
green: rgbChannel(green),
|
|
384
|
+
red: rgbChannel(red),
|
|
385
|
+
});
|
|
387
386
|
|
|
388
387
|
/** A converter for a hue-and-two-percentages function (`hsl()`, `hwb()`). */
|
|
389
388
|
const cylindrical =
|
package/src/theme/entry.ts
CHANGED
|
@@ -263,13 +263,24 @@ ${THEME_MAPPING}
|
|
|
263
263
|
so a large site's HTML is mostly this markup — one utility per row kind
|
|
264
264
|
instead of the dozen classes each expands to keeps every page (and the
|
|
265
265
|
build's HTML output) small. Kept in sync with NavTree.astro: a page row,
|
|
266
|
-
a page-mode group's drill-in
|
|
267
|
-
|
|
266
|
+
a page-mode group's drill-in row (or, when the group has a page, the
|
|
267
|
+
link and chevron button it splits into), a collapsible group's summary
|
|
268
|
+
and the link inside it, and a flat group's heading and the link inside
|
|
269
|
+
it. */
|
|
268
270
|
@utility blume-nav-link {
|
|
269
271
|
@apply block rounded-[0.65rem] px-2.5 py-1.5 text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=page]:bg-muted aria-[current=page]:font-medium aria-[current=page]:text-foreground;
|
|
270
272
|
}
|
|
271
273
|
@utility blume-nav-drill {
|
|
272
|
-
@apply flex w-full items-center gap-2 rounded-[0.65rem] px-2.5 py-1.5 text-start font-medium text-foreground text-sm transition-colors hover:bg-muted;
|
|
274
|
+
@apply flex w-full items-center gap-2 rounded-[0.65rem] px-2.5 py-1.5 text-start font-medium text-foreground text-sm transition-colors hover:bg-muted aria-[current]:bg-muted;
|
|
275
|
+
}
|
|
276
|
+
/* A routed drill row's label and chevron are separate pills, like a panel's
|
|
277
|
+
back button and title, so each shows which one the pointer is on. The
|
|
278
|
+
chevron's padding matches the drill row's, so chevrons line up. */
|
|
279
|
+
@utility blume-nav-drill-link {
|
|
280
|
+
@apply flex min-w-0 flex-1 items-center gap-2 rounded-[0.65rem] px-2.5 py-1.5 font-medium text-foreground text-sm transition-colors hover:bg-muted aria-[current]:bg-muted;
|
|
281
|
+
}
|
|
282
|
+
@utility blume-nav-drill-toggle {
|
|
283
|
+
@apply flex shrink-0 items-center justify-center self-stretch rounded-[0.65rem] px-2.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground;
|
|
273
284
|
}
|
|
274
285
|
@utility blume-nav-summary {
|
|
275
286
|
@apply flex cursor-pointer list-none items-center gap-1.5 rounded-[0.65rem] px-2.5 py-1.5 text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground [&::-webkit-details-marker]:hidden;
|
|
@@ -940,8 +951,11 @@ pre:has(.line.focused):hover .line:not(.focused) {
|
|
|
940
951
|
Strip the surrounding chrome so the printout is just the article. Scoped to
|
|
941
952
|
the chrome itself, never bare \`header\`/\`aside\`: content renders those too
|
|
942
953
|
(every Callout and Panel is an <aside>, every changelog Update has a
|
|
943
|
-
<header>), and they belong in the printout. \`body > header\`
|
|
944
|
-
\`layout.Header\`
|
|
954
|
+
<header>), and they belong in the printout. \`body > header\` and
|
|
955
|
+
\`body > footer\` keep \`layout.Header\` and \`layout.Footer\` overrides out
|
|
956
|
+
as well. Everything after the article (directory and related-page cards,
|
|
957
|
+
last updated, feedback, prev/next) is page chrome too, and so are the
|
|
958
|
+
copy, ask, and expand buttons on code blocks. */
|
|
945
959
|
@media print {
|
|
946
960
|
[data-blume-banner],
|
|
947
961
|
[data-blume-header],
|
|
@@ -951,7 +965,13 @@ pre:has(.line.focused):hover .line:not(.focused) {
|
|
|
951
965
|
[data-blume-assistant-panel],
|
|
952
966
|
[data-blume-page-actions],
|
|
953
967
|
#blume-content > nav,
|
|
954
|
-
#blume-content > details
|
|
968
|
+
#blume-content > details,
|
|
969
|
+
[data-blume-page-end],
|
|
970
|
+
[data-blume-footer],
|
|
971
|
+
body > footer,
|
|
972
|
+
[data-blume-copy],
|
|
973
|
+
[data-blume-ask],
|
|
974
|
+
[data-blume-code-expand] {
|
|
955
975
|
display: none !important;
|
|
956
976
|
}
|
|
957
977
|
|
|
@@ -962,6 +982,23 @@ pre:has(.line.focused):hover .line:not(.focused) {
|
|
|
962
982
|
overflow: visible;
|
|
963
983
|
}
|
|
964
984
|
|
|
985
|
+
/* A collapsed \`expandable\` block prints open: its toggle is hidden above,
|
|
986
|
+
so the collapse would cut the block off at its first lines. Same
|
|
987
|
+
selector as the collapse, so this later rule wins. */
|
|
988
|
+
.prose pre[data-expandable]:not([data-expanded]) > code {
|
|
989
|
+
mask-image: none;
|
|
990
|
+
max-height: none;
|
|
991
|
+
overflow-y: visible;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
/* The theme script switches a dark page to light for printing
|
|
995
|
+
(\`beforeprint\`); a color transition would print mid-fade. */
|
|
996
|
+
*,
|
|
997
|
+
*::before,
|
|
998
|
+
*::after {
|
|
999
|
+
transition: none !important;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
965
1002
|
#blume-content {
|
|
966
1003
|
padding: 0 !important;
|
|
967
1004
|
}
|
|
@@ -7,7 +7,8 @@ import { TRANSLATABLE_KEY_PATHS } from "./prompts.ts";
|
|
|
7
7
|
* structure: the final frontmatter is *reconstructed* from the source (clone
|
|
8
8
|
* the source data, overlay only the translatable string values), so invented
|
|
9
9
|
* keys are dropped, deleted keys are restored, and slugs/icons/orders/dates
|
|
10
|
-
* stay source-verbatim by construction.
|
|
10
|
+
* stay source-verbatim by construction. The one source key left out is
|
|
11
|
+
* `seo.canonical` (see `dropCanonical`). Only then is the file written.
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
14
|
export type ValidationResult =
|
|
@@ -93,6 +94,26 @@ const setPath = (
|
|
|
93
94
|
parent[path.at(-1) as string] = value;
|
|
94
95
|
};
|
|
95
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Drop `seo.canonical`, and `seo` with it when nothing else is left. The
|
|
99
|
+
* source's canonical names a page in the source language (most often the
|
|
100
|
+
* source page itself); copied into a translation, it would canonicalize the
|
|
101
|
+
* translation to another language and take it out of search. Without it the
|
|
102
|
+
* translation gets Blume's default canonical: its own URL.
|
|
103
|
+
*/
|
|
104
|
+
const dropCanonical = (data: FrontmatterData): void => {
|
|
105
|
+
const { seo } = data;
|
|
106
|
+
if (!isKeyedObject(seo) || !("canonical" in seo)) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const { canonical: _canonical, ...rest } = seo;
|
|
110
|
+
if (Object.keys(rest).length > 0) {
|
|
111
|
+
data.seo = rest;
|
|
112
|
+
} else {
|
|
113
|
+
delete data.seo;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
96
117
|
const ensureTrailingNewline = (text: string): string =>
|
|
97
118
|
text.endsWith("\n") ? text : `${text}\n`;
|
|
98
119
|
|
|
@@ -181,6 +202,7 @@ export const validateTranslation = (
|
|
|
181
202
|
setPath(data, path, translated);
|
|
182
203
|
}
|
|
183
204
|
}
|
|
205
|
+
dropCanonical(data);
|
|
184
206
|
|
|
185
207
|
return {
|
|
186
208
|
ok: true,
|