blume 2.0.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -0
- package/CHANGELOG.md +113 -0
- package/README.md +1 -1
- package/dist/cli/{chunk-6k4ftwze.js → chunk-0gz36akr.js} +18 -2
- package/dist/cli/{chunk-6k4ftwze.js.map → chunk-0gz36akr.js.map} +3 -3
- package/dist/cli/{chunk-zp79m0ts.js → chunk-1n228xqb.js} +2 -2
- package/dist/cli/{chunk-ddndchfr.js → chunk-2f3ndkpw.js} +57 -29
- package/dist/cli/chunk-2f3ndkpw.js.map +14 -0
- package/dist/cli/{chunk-fsmrqk8a.js → chunk-2f7n4qzt.js} +8 -2
- package/dist/cli/{chunk-fsmrqk8a.js.map → chunk-2f7n4qzt.js.map} +3 -3
- package/dist/cli/{chunk-ppzjqwx2.js → chunk-2n4ps1b3.js} +17 -18
- package/dist/cli/{chunk-ppzjqwx2.js.map → chunk-2n4ps1b3.js.map} +3 -3
- package/dist/cli/{chunk-5r8g91qn.js → chunk-43zcf4gz.js} +398 -159
- package/dist/cli/chunk-43zcf4gz.js.map +36 -0
- package/dist/cli/chunk-4ejxbzee.js +39 -0
- package/dist/cli/chunk-4ejxbzee.js.map +10 -0
- package/dist/cli/{chunk-hr8ne106.js → chunk-4h8z9vv5.js} +296 -77
- package/dist/cli/chunk-4h8z9vv5.js.map +16 -0
- package/dist/cli/{chunk-k7pj68a8.js → chunk-5cg2rzgf.js} +18 -16
- package/dist/cli/{chunk-k7pj68a8.js.map → chunk-5cg2rzgf.js.map} +2 -2
- package/dist/cli/{chunk-h2ez8dzb.js → chunk-5ffywsmb.js} +4 -4
- package/dist/cli/{chunk-g698a744.js → chunk-5wj2qwn2.js} +5 -5
- package/dist/cli/chunk-6bm0xpgd.js +18 -0
- package/dist/cli/chunk-6bm0xpgd.js.map +10 -0
- package/dist/cli/{chunk-3yce002v.js → chunk-713mngdq.js} +15 -8
- package/dist/cli/chunk-713mngdq.js.map +14 -0
- package/dist/cli/{chunk-91ws1n6j.js → chunk-9geymqns.js} +17 -15
- package/dist/cli/{chunk-91ws1n6j.js.map → chunk-9geymqns.js.map} +2 -2
- package/dist/cli/{chunk-6dtt0zfn.js → chunk-9xr4dgee.js} +27 -18
- package/dist/cli/chunk-9xr4dgee.js.map +10 -0
- package/dist/cli/{chunk-sqw4ekg1.js → chunk-amrb6hgh.js} +2 -2
- package/dist/cli/chunk-cm89scdv.js +443 -0
- package/dist/cli/chunk-cm89scdv.js.map +11 -0
- package/dist/cli/{chunk-wdrt2k2v.js → chunk-cmtcnh9f.js} +90 -26
- package/dist/cli/chunk-cmtcnh9f.js.map +10 -0
- package/dist/cli/chunk-cy5bm0j0.js +104 -0
- package/dist/cli/chunk-cy5bm0j0.js.map +11 -0
- package/dist/cli/{chunk-7mbqtmgb.js → chunk-e7vvk5ya.js} +21 -12
- package/dist/cli/chunk-e7vvk5ya.js.map +10 -0
- package/dist/cli/{chunk-qkb5a8sa.js → chunk-g2z9x01v.js} +6 -98
- package/dist/cli/chunk-g2z9x01v.js.map +10 -0
- package/dist/cli/{chunk-273ygyr4.js → chunk-ga0pf4aj.js} +7 -7
- package/dist/cli/{chunk-273ygyr4.js.map → chunk-ga0pf4aj.js.map} +3 -3
- package/dist/cli/{chunk-h7k3nq3v.js → chunk-gp84za0h.js} +2 -2
- package/dist/cli/{chunk-hqp2ajnh.js → chunk-h2a48yhd.js} +195 -73
- package/dist/cli/chunk-h2a48yhd.js.map +36 -0
- package/dist/cli/{chunk-pbg5a4s3.js → chunk-jnwj5kjj.js} +71 -47
- package/dist/cli/chunk-jnwj5kjj.js.map +19 -0
- package/dist/cli/chunk-kwrg63tm.js +31 -0
- package/dist/cli/chunk-kwrg63tm.js.map +10 -0
- package/dist/cli/{chunk-8g8ytmgx.js → chunk-nd1821sk.js} +21 -19
- package/dist/cli/{chunk-8g8ytmgx.js.map → chunk-nd1821sk.js.map} +2 -2
- package/dist/cli/{chunk-w4bxdvsa.js → chunk-ntv4qrt7.js} +17 -15
- package/dist/cli/{chunk-w4bxdvsa.js.map → chunk-ntv4qrt7.js.map} +2 -2
- package/dist/cli/{chunk-bfwp9vp6.js → chunk-p804f3pe.js} +7 -7
- package/dist/cli/{chunk-4e9b9ra6.js → chunk-py62fewh.js} +6 -6
- package/dist/cli/{chunk-4e9b9ra6.js.map → chunk-py62fewh.js.map} +1 -1
- package/dist/cli/chunk-q3wxxq39.js +126 -0
- package/dist/cli/chunk-q3wxxq39.js.map +11 -0
- package/dist/cli/{chunk-j85scx15.js → chunk-rekqz4qg.js} +142 -46
- package/dist/cli/chunk-rekqz4qg.js.map +10 -0
- package/dist/cli/{chunk-00gs3wqs.js → chunk-ta27feev.js} +1 -1
- package/dist/cli/{chunk-v6ya5kcb.js → chunk-xkz5xbtv.js} +4031 -1132
- package/dist/cli/chunk-xkz5xbtv.js.map +213 -0
- package/dist/cli/{chunk-bbnwccaz.js → chunk-y4ykbark.js} +2 -2
- package/dist/cli/{chunk-n1yg3tj3.js → chunk-y5h4r2y0.js} +6 -6
- package/dist/cli/{chunk-n1yg3tj3.js.map → chunk-y5h4r2y0.js.map} +3 -3
- package/dist/cli/{chunk-nfcyttvj.js → chunk-yj17vs39.js} +9 -9
- package/dist/cli/{chunk-nfcyttvj.js.map → chunk-yj17vs39.js.map} +1 -1
- package/dist/cli/{chunk-7vtckvaw.js → chunk-yxmk4pec.js} +16 -14
- package/dist/cli/{chunk-7vtckvaw.js.map → chunk-yxmk4pec.js.map} +2 -2
- package/dist/cli/{chunk-gs7r695n.js → chunk-zccbjcj0.js} +22 -8
- package/dist/cli/{chunk-gs7r695n.js.map → chunk-zccbjcj0.js.map} +3 -3
- package/dist/cli/{chunk-xh43dwgw.js → chunk-zf748avv.js} +415 -225
- package/dist/cli/chunk-zf748avv.js.map +37 -0
- package/dist/cli/{chunk-d1v5rhy0.js → chunk-zhvd6adm.js} +16 -14
- package/dist/cli/{chunk-d1v5rhy0.js.map → chunk-zhvd6adm.js.map} +2 -2
- package/dist/cli/index.js +28 -20
- package/dist/cli/index.js.map +5 -5
- package/dist/types/ai/agent-surface.d.ts +8 -7
- package/dist/types/ai/ai-catalog.d.ts +7 -0
- package/dist/types/ai/ask-context.d.ts +22 -0
- package/dist/types/ai/ask.d.ts +271 -58
- package/dist/types/ai/index.d.ts +2 -2
- package/dist/types/ai/link-headers.d.ts +8 -1
- package/dist/types/ai/openapi-components.d.ts +5 -2
- package/dist/types/ai/relative-links.d.ts +9 -7
- package/dist/types/ai/site-skill.d.ts +26 -0
- package/dist/types/ai/skills.d.ts +20 -1
- package/dist/types/ai/tar.d.ts +1 -3
- package/dist/types/analytics/index.d.ts +2 -0
- package/dist/types/analytics/one-dollar-stats.d.ts +48 -0
- package/dist/types/analytics/schema.d.ts +14 -0
- package/dist/types/astro/integration.d.ts +11 -2
- package/dist/types/captcha/hcaptcha.d.ts +27 -0
- package/dist/types/captcha/index.d.ts +28 -0
- package/dist/types/captcha/schema.d.ts +29 -0
- package/dist/types/captcha/turnstile.d.ts +28 -0
- package/dist/types/components/content/api-field.d.ts +59 -0
- package/dist/types/components/content/api-page.d.ts +95 -0
- package/dist/types/components/layout/nav-utils.d.ts +26 -0
- package/dist/types/components/openapi/helpers.d.ts +137 -0
- package/dist/types/components/openapi/security.d.ts +85 -0
- package/dist/types/consent/ethyca.d.ts +48 -0
- package/dist/types/consent/index.d.ts +37 -0
- package/dist/types/consent/native.d.ts +28 -0
- package/dist/types/consent/osano.d.ts +42 -0
- package/dist/types/consent/schema.d.ts +110 -0
- package/dist/types/core/base-path.d.ts +21 -7
- package/dist/types/core/config-input.d.ts +196 -27
- package/dist/types/core/data.d.ts +49 -4
- package/dist/types/core/directive-diagnostics.d.ts +12 -0
- package/dist/types/core/footer.d.ts +8 -0
- package/dist/types/core/heading-markers.d.ts +5 -7
- package/dist/types/core/i18n-ui.d.ts +86 -0
- package/dist/types/core/i18n.d.ts +2 -0
- package/dist/types/core/includes.d.ts +8 -2
- package/dist/types/core/last-modified.d.ts +10 -0
- package/dist/types/core/localizable.d.ts +7 -0
- package/dist/types/core/meta.d.ts +8 -0
- package/dist/types/core/page-modes.d.ts +29 -0
- package/dist/types/core/redirect-patterns.d.ts +144 -0
- package/dist/types/core/related.d.ts +40 -0
- package/dist/types/core/schema.d.ts +558 -56
- package/dist/types/core/sources/lower.d.ts +29 -16
- package/dist/types/core/sources/normalize.d.ts +13 -1
- package/dist/types/core/sources/read.d.ts +5 -0
- package/dist/types/core/sources/watch.d.ts +12 -5
- package/dist/types/core/standard-schema.d.ts +5 -0
- package/dist/types/core/types.d.ts +1 -1
- package/dist/types/core/variables.d.ts +42 -0
- package/dist/types/deploy/artifacts.d.ts +6 -4
- package/dist/types/deploy/headers.d.ts +5 -0
- package/dist/types/deploy/node-headers.d.ts +13 -2
- package/dist/types/deploy/platforms/cloudflare.d.ts +5 -0
- package/dist/types/deploy/platforms/netlify.d.ts +7 -0
- package/dist/types/deploy/platforms/types.d.ts +8 -0
- package/dist/types/deploy/platforms/vercel.d.ts +6 -0
- package/dist/types/deploy/redirects.d.ts +61 -22
- package/dist/types/deploy/vercel-negotiation.d.ts +9 -0
- package/dist/types/markdown/code-title.d.ts +17 -0
- package/dist/types/markdown/directives.d.ts +62 -0
- package/dist/types/markdown/features.d.ts +24 -2
- package/dist/types/markdown/mdast.d.ts +63 -0
- package/dist/types/narration/build.d.ts +70 -0
- package/dist/types/narration/provider.d.ts +26 -0
- package/dist/types/narration/script.d.ts +154 -0
- package/dist/types/openapi/asyncapi.d.ts +4 -2
- package/dist/types/openapi/model.d.ts +12 -6
- package/dist/types/openapi/overlay.d.ts +54 -0
- package/dist/types/openapi/parse.d.ts +13 -0
- package/dist/types/openapi/references.d.ts +14 -4
- package/dist/types/ratelimit/cloudflare.d.ts +39 -0
- package/dist/types/ratelimit/index.d.ts +27 -0
- package/dist/types/ratelimit/memory.d.ts +36 -0
- package/dist/types/ratelimit/schema.d.ts +111 -0
- package/dist/types/ratelimit/upstash.d.ts +21 -0
- package/dist/types/ratelimit/wrangler.d.ts +23 -0
- package/dist/types/reference/asyncapi.d.ts +10 -10
- package/dist/types/reference/graphql.d.ts +13 -10
- package/dist/types/reference/openapi.d.ts +33 -12
- package/dist/types/reference/options.d.ts +45 -5
- package/dist/types/reference/scalar.d.ts +13 -0
- package/dist/types/reference/schema.d.ts +60 -36
- package/dist/types/search/documents.d.ts +12 -1
- package/dist/types/search/orama-index.d.ts +4 -0
- package/dist/types/seo/metatags.d.ts +20 -0
- package/docs/02-deployment.mdx +44 -6
- package/docs/03-upgrading.mdx +4 -4
- package/docs/04-migrating.mdx +1 -1
- package/docs/08-faq.mdx +1 -1
- package/docs/advanced/changelog.mdx +2 -2
- package/docs/advanced/custom-pages.mdx +3 -3
- package/docs/advanced/skills.mdx +14 -0
- package/docs/cli/audit.mdx +38 -2
- package/docs/cli/evals.mdx +3 -3
- package/docs/cli/index.mdx +1 -0
- package/docs/cli/translate.mdx +3 -3
- package/docs/cli/version.mdx +1 -1
- package/docs/configuration/analytics.mdx +22 -1
- package/docs/configuration/assistant.mdx +188 -36
- package/docs/configuration/consent.mdx +100 -0
- package/docs/configuration/customization.mdx +6 -4
- package/docs/configuration/index.mdx +63 -5
- package/docs/configuration/meta.ts +3 -0
- package/docs/configuration/narration.mdx +119 -0
- package/docs/configuration/rate-limiting.mdx +69 -0
- package/docs/configuration/search.mdx +30 -2
- package/docs/configuration/theming.mdx +3 -1
- package/docs/content/components.mdx +107 -1
- package/docs/content/frontmatter.mdx +77 -1
- package/docs/content/i18n.mdx +40 -3
- package/docs/content/includes.mdx +16 -0
- package/docs/content/index.mdx +4 -2
- package/docs/content/islands.mdx +1 -1
- package/docs/content/meta.mdx +4 -1
- package/docs/content/meta.ts +1 -0
- package/docs/content/navigation.mdx +28 -6
- package/docs/content/sources/contentful.mdx +32 -0
- package/docs/content/sources/custom.mdx +39 -0
- package/docs/content/sources/github-releases.mdx +31 -0
- package/docs/content/sources/index.mdx +91 -0
- package/docs/content/sources/meta.ts +16 -0
- package/docs/content/sources/notion.mdx +32 -0
- package/docs/content/sources/obsidian.mdx +37 -0
- package/docs/content/sources/payload.mdx +30 -0
- package/docs/content/sources/remote-mdx.mdx +40 -0
- package/docs/content/sources/sanity.mdx +29 -0
- package/docs/content/sources/strapi.mdx +31 -0
- package/docs/content/syntax.mdx +81 -2
- package/docs/content/variables.mdx +41 -0
- package/docs/content/versioning.mdx +1 -1
- package/docs/discoverability/agent-discovery.mdx +30 -3
- package/docs/discoverability/index.mdx +3 -2
- package/docs/discoverability/llms-txt.mdx +1 -1
- package/docs/discoverability/markdown.mdx +2 -2
- package/docs/discoverability/metadata.mdx +17 -0
- package/docs/discoverability/open-graph.mdx +5 -3
- package/docs/discoverability/rss.mdx +1 -1
- package/docs/references/api-pages.mdx +74 -0
- package/docs/references/asyncapi.mdx +2 -2
- package/docs/references/graphql.mdx +3 -3
- package/docs/references/meta.ts +1 -1
- package/docs/references/openapi.mdx +106 -5
- package/docs/references/scalar.mdx +3 -1
- package/package.json +36 -1
- package/skills/blume/SKILL.md +3 -0
- package/skills/blume-migrate/SKILL.md +5 -5
- package/skills/blume-migrate/references/docusaurus.md +3 -3
- package/skills/blume-migrate/references/fumadocs.md +1 -1
- package/skills/blume-migrate/references/mintlify.md +37 -19
- package/skills/blume-migrate/references/nextra.md +3 -3
- package/skills/blume-migrate/references/starlight.md +2 -1
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +67 -16
- package/skills/blume-write-skill/SKILL.md +82 -0
- package/src/ai/agent-readability.ts +4 -3
- package/src/ai/agent-surface.ts +9 -7
- package/src/ai/ai-catalog.ts +12 -5
- package/src/ai/api/handlers.ts +2 -2
- package/src/ai/api/spec.ts +2 -2
- package/src/ai/api-catalog.ts +6 -2
- package/src/ai/ask-context.ts +73 -20
- package/src/ai/ask-data.ts +6 -0
- package/src/ai/ask-tools.ts +142 -0
- package/src/ai/ask.ts +354 -76
- package/src/ai/changelog-markdown.ts +2 -2
- package/src/ai/component-markdown.ts +75 -0
- package/src/ai/cors.ts +2 -0
- package/src/ai/index.ts +12 -0
- package/src/ai/link-headers.ts +14 -5
- package/src/ai/llms.ts +24 -9
- package/src/ai/markdown.ts +2 -1
- package/src/ai/mcp/data.ts +6 -0
- package/src/ai/mcp/discovery.ts +2 -2
- package/src/ai/mcp/query.ts +2 -2
- package/src/ai/mcp/server.ts +2 -2
- package/src/ai/openapi-components.ts +31 -9
- package/src/ai/relative-links.ts +42 -26
- package/src/ai/serializers.ts +2 -1
- package/src/ai/site-skill.ts +274 -0
- package/src/ai/skills.ts +49 -3
- package/src/ai/tar.ts +139 -12
- package/src/ai/write-skill.ts +97 -0
- package/src/analytics/head.ts +4 -0
- package/src/analytics/index.ts +5 -0
- package/src/analytics/one-dollar-stats.ts +86 -0
- package/src/analytics/schema.ts +2 -0
- package/src/astro/generate.ts +133 -19
- package/src/astro/include-hmr.ts +31 -12
- package/src/astro/include-refresh.ts +19 -8
- package/src/astro/index.ts +1 -0
- package/src/astro/integration.ts +143 -14
- package/src/astro/module-types.ts +6 -0
- package/src/astro/runtime-modules.ts +28 -13
- package/src/astro/templates.ts +364 -67
- package/src/astro/variables.ts +31 -0
- package/src/audit/catalog.ts +19 -0
- package/src/audit/checks/contrast.ts +183 -0
- package/src/audit/checks/i18n.ts +2 -3
- package/src/audit/checks/indexability.ts +2 -3
- package/src/audit/checks/links.ts +13 -13
- package/src/audit/checks/sitemap.ts +2 -3
- package/src/audit/graph.ts +3 -1
- package/src/audit/redirects.ts +46 -15
- package/src/audit/run.ts +14 -9
- package/src/audit/snapshot.ts +5 -0
- package/src/audit/types.ts +2 -1
- package/src/blume-modules.d.ts +7 -0
- package/src/captcha/client.ts +152 -0
- package/src/captcha/hcaptcha.ts +36 -0
- package/src/captcha/index.ts +28 -0
- package/src/captcha/schema.ts +32 -0
- package/src/captcha/turnstile.ts +38 -0
- package/src/captcha/verify.ts +71 -0
- package/src/cli/args.ts +32 -0
- package/src/cli/command-meta.ts +5 -0
- package/src/cli/commands/audit.ts +4 -1
- package/src/cli/commands/doctor.ts +25 -17
- package/src/cli/commands/eval.ts +19 -11
- package/src/cli/commands/skill.ts +96 -0
- package/src/cli/commands/translate.ts +9 -12
- package/src/cli/commands/validate.ts +3 -1
- package/src/cli/dev-lock.ts +157 -37
- package/src/cli/index.ts +5 -0
- package/src/cli/report-format.ts +11 -6
- package/src/cli/required-secrets.ts +71 -29
- package/src/components/BlumePage.astro +2 -0
- package/src/components/colors.ts +19 -0
- package/src/components/content/ApiEndpoint.astro +18 -0
- package/src/components/content/ApiField.astro +79 -0
- package/src/components/content/ApiPlayground.astro +62 -0
- package/src/components/content/ApiRail.astro +19 -0
- package/src/components/content/Badge.astro +5 -3
- package/src/components/content/Color.astro +1 -0
- package/src/components/content/Component.astro +2 -2
- package/src/components/content/FileTree.astro +1 -0
- package/src/components/content/GithubInfo.astro +1 -0
- package/src/components/content/Panel.astro +1 -0
- package/src/components/content/ParamField.astro +25 -0
- package/src/components/content/RequestExample.astro +17 -0
- package/src/components/content/ResponseExample.astro +17 -0
- package/src/components/content/ResponseField.astro +32 -0
- package/src/components/content/Step.astro +1 -0
- package/src/components/content/Tab.astro +0 -1
- package/src/components/content/Tabs.astro +4 -0
- package/src/components/content/Tile.astro +1 -0
- package/src/components/content/Tooltip.astro +1 -0
- package/src/components/content/Tree.astro +1 -0
- package/src/components/content/TypeTable.astro +4 -1
- package/src/components/content/View.astro +21 -0
- package/src/components/content/ViewSwitcher.astro +168 -0
- package/src/components/content/api-field.ts +100 -0
- package/src/components/content/api-page.ts +224 -0
- package/src/components/content/badge-color.ts +5 -2
- package/src/components/content/base-href.ts +13 -36
- package/src/components/content/view-switcher.ts +71 -0
- package/src/components/index.ts +2 -0
- package/src/components/islands/Assistant.astro +10 -1
- package/src/components/islands/assistant.tsx +125 -18
- package/src/components/islands/base-path.ts +47 -10
- package/src/components/islands/code-question.ts +62 -0
- package/src/components/islands/hooks.ts +145 -40
- package/src/components/islands/support-link.ts +68 -0
- package/src/components/islands/webmcp.ts +4 -2
- package/src/components/layout/Analytics.astro +11 -2
- package/src/components/layout/Banner.astro +14 -10
- package/src/components/layout/Breadcrumbs.astro +11 -4
- package/src/components/layout/ConsentBanner.astro +62 -0
- package/src/components/layout/ConsentHead.astro +38 -0
- package/src/components/layout/Directory.astro +94 -0
- package/src/components/layout/DiscoveryLinks.astro +9 -5
- package/src/components/layout/DrawerSwitchers.astro +51 -0
- package/src/components/layout/Empty.astro +2 -2
- package/src/components/layout/Header.astro +124 -82
- package/src/components/layout/LanguageSwitcher.astro +50 -17
- package/src/components/layout/NarrationPlayer.astro +1085 -0
- package/src/components/layout/NavSelector.astro +35 -9
- package/src/components/layout/NavTabMenu.astro +3 -3
- package/src/components/layout/NavTree.astro +16 -6
- package/src/components/layout/PageActions.astro +4 -3
- package/src/components/layout/PageFeedback.astro +54 -49
- package/src/components/layout/PageLayout.astro +78 -9
- package/src/components/layout/Pagination.astro +3 -3
- package/src/components/layout/ReferenceLayout.astro +17 -6
- package/src/components/layout/RelatedPages.astro +42 -0
- package/src/components/layout/RootLayout.astro +313 -50
- package/src/components/layout/Search.astro +64 -30
- package/src/components/layout/SiteFooter.astro +124 -0
- package/src/components/layout/SiteMetatags.astro +16 -0
- package/src/components/layout/VersionBanner.astro +2 -2
- package/src/components/layout/analytics-client.ts +12 -0
- package/src/components/layout/consent/native.ts +79 -0
- package/src/components/layout/consent/types.ts +37 -0
- package/src/components/layout/footer-links.ts +53 -0
- package/src/components/layout/footer-socials.ts +87 -0
- package/src/components/layout/locale-redirect.ts +107 -0
- package/src/components/layout/nav-utils.ts +83 -0
- package/src/components/layout/page-feedback.ts +96 -0
- package/src/components/layout/search/analytics.ts +98 -0
- package/src/components/layout/search/flexsearch.ts +17 -5
- package/src/components/layout/search/orama-cloud.ts +26 -14
- package/src/components/layout/search/types.ts +20 -0
- package/src/components/layout/search/typesense.ts +5 -1
- package/src/components/layout/toc-active.ts +41 -0
- package/src/components/layout/toc-element.ts +8 -14
- package/src/components/openapi/ApiTagOperations.astro +2 -2
- package/src/components/openapi/AsyncApiOperation.astro +7 -4
- package/src/components/openapi/GraphqlChip.astro +2 -2
- package/src/components/openapi/Operation.astro +163 -13
- package/src/components/openapi/Playground.astro +4 -4
- package/src/components/openapi/RequestBody.astro +6 -4
- package/src/components/openapi/RequestPanel.astro +63 -8
- package/src/components/openapi/Responses.astro +6 -4
- package/src/components/openapi/SchemaTable.astro +7 -0
- package/src/components/openapi/async-snippets.ts +4 -4
- package/src/components/openapi/async.ts +38 -6
- package/src/components/openapi/code-samples.ts +97 -0
- package/src/components/openapi/helpers.ts +19 -9
- package/src/components/openapi/message-composer.ts +6 -1
- package/src/components/openapi/message.ts +17 -2
- package/src/components/openapi/operation-model.ts +81 -11
- package/src/components/openapi/panel.ts +4 -2
- package/src/components/openapi/param-style.ts +181 -0
- package/src/components/openapi/playground-client.ts +73 -16
- package/src/components/openapi/request.ts +190 -26
- package/src/components/openapi/schema-tree.ts +30 -24
- package/src/components/openapi/security.ts +6 -3
- package/src/components/openapi/snippets.ts +953 -13
- package/src/components/openapi/ws-client.ts +18 -2
- package/src/components/props.ts +6 -0
- package/src/consent/client.ts +95 -0
- package/src/consent/clients.ts +19 -0
- package/src/consent/ethyca.ts +71 -0
- package/src/consent/head.ts +24 -0
- package/src/consent/index.ts +37 -0
- package/src/consent/init.ts +29 -0
- package/src/consent/native.ts +36 -0
- package/src/consent/osano.ts +59 -0
- package/src/consent/schema.ts +30 -0
- package/src/core/base-path.ts +62 -16
- package/src/core/builtin-tags.ts +5 -0
- package/src/core/client-address.ts +19 -0
- package/src/core/config-input.ts +203 -27
- package/src/core/data.ts +44 -4
- package/src/core/diagnostics.ts +5 -2
- package/src/core/directive-diagnostics.ts +99 -0
- package/src/core/footer.ts +26 -0
- package/src/core/frontmatter.ts +21 -18
- package/src/core/graph.ts +39 -36
- package/src/core/heading-markers.ts +5 -18
- package/src/core/i18n-ui.ts +82 -2
- package/src/core/i18n.ts +31 -3
- package/src/core/includes.ts +20 -3
- package/src/core/last-modified.ts +25 -3
- package/src/core/links.ts +16 -2
- package/src/core/locale-links.ts +5 -1
- package/src/core/localizable.ts +26 -0
- package/src/core/meta.ts +33 -19
- package/src/core/navigation.ts +71 -20
- package/src/core/page-modes.ts +56 -0
- package/src/core/project-graph.ts +138 -10
- package/src/core/redirect-patterns.ts +491 -0
- package/src/core/related.ts +131 -0
- package/src/core/schema.ts +342 -68
- package/src/core/sources/contentful-rich-text.ts +27 -20
- package/src/core/sources/filesystem.ts +20 -2
- package/src/core/sources/github-releases.ts +25 -6
- package/src/core/sources/lexical.ts +23 -18
- package/src/core/sources/lower.ts +201 -34
- package/src/core/sources/mdx-remote.ts +51 -16
- package/src/core/sources/normalize.ts +327 -91
- package/src/core/sources/notion.ts +52 -25
- package/src/core/sources/obsidian.ts +23 -6
- package/src/core/sources/portable-text.ts +38 -21
- package/src/core/sources/read.ts +8 -3
- package/src/core/sources/strapi-blocks.ts +20 -16
- package/src/core/sources/watch.ts +20 -7
- package/src/core/standard-schema.ts +10 -6
- package/src/core/types.ts +1 -1
- package/src/core/ui-packs/ar.ts +43 -0
- package/src/core/ui-packs/bg.ts +45 -0
- package/src/core/ui-packs/bn.ts +43 -0
- package/src/core/ui-packs/ca.ts +46 -0
- package/src/core/ui-packs/cs.ts +44 -0
- package/src/core/ui-packs/da.ts +45 -0
- package/src/core/ui-packs/de.ts +46 -0
- package/src/core/ui-packs/el.ts +45 -0
- package/src/core/ui-packs/es.ts +46 -0
- package/src/core/ui-packs/fa.ts +45 -0
- package/src/core/ui-packs/fi.ts +45 -0
- package/src/core/ui-packs/fr.ts +46 -0
- package/src/core/ui-packs/he.ts +43 -0
- package/src/core/ui-packs/hi.ts +44 -0
- package/src/core/ui-packs/hr.ts +45 -0
- package/src/core/ui-packs/hu.ts +44 -0
- package/src/core/ui-packs/id.ts +45 -0
- package/src/core/ui-packs/it.ts +45 -0
- package/src/core/ui-packs/ja.ts +45 -0
- package/src/core/ui-packs/ko.ts +44 -0
- package/src/core/ui-packs/nl.ts +46 -0
- package/src/core/ui-packs/no.ts +44 -0
- package/src/core/ui-packs/pl.ts +45 -0
- package/src/core/ui-packs/pt-br.ts +46 -0
- package/src/core/ui-packs/pt.ts +45 -0
- package/src/core/ui-packs/ro.ts +45 -0
- package/src/core/ui-packs/ru.ts +45 -0
- package/src/core/ui-packs/sk.ts +44 -0
- package/src/core/ui-packs/sr.ts +45 -0
- package/src/core/ui-packs/sv.ts +44 -0
- package/src/core/ui-packs/th.ts +43 -0
- package/src/core/ui-packs/tr.ts +45 -0
- package/src/core/ui-packs/uk.ts +45 -0
- package/src/core/ui-packs/vi.ts +45 -0
- package/src/core/ui-packs/zh-tw.ts +43 -0
- package/src/core/ui-packs/zh.ts +43 -0
- package/src/core/variables.ts +103 -0
- package/src/core/version-cut.ts +52 -15
- package/src/deploy/artifacts.ts +104 -14
- package/src/deploy/cloudflare-negotiation.ts +39 -17
- package/src/deploy/headers.ts +10 -5
- package/src/deploy/node-headers.ts +64 -10
- package/src/deploy/platforms/cloudflare.ts +44 -3
- package/src/deploy/platforms/netlify.ts +34 -1
- package/src/deploy/platforms/node.ts +1 -0
- package/src/deploy/platforms/static.ts +1 -0
- package/src/deploy/platforms/types.ts +8 -0
- package/src/deploy/platforms/vercel.ts +38 -2
- package/src/deploy/redirects.ts +139 -36
- package/src/deploy/robots.ts +2 -2
- package/src/deploy/rss.ts +4 -3
- package/src/deploy/sitemap.ts +7 -5
- package/src/deploy/vercel-negotiation.ts +36 -0
- package/src/markdown/api-rail.ts +259 -0
- package/src/markdown/base-links.ts +34 -36
- package/src/markdown/code-title.ts +35 -9
- package/src/markdown/directives.ts +242 -36
- package/src/markdown/features.ts +20 -2
- package/src/markdown/fence-meta.ts +2 -0
- package/src/markdown/include.ts +11 -1
- package/src/markdown/index.ts +35 -11
- package/src/markdown/math.ts +4 -4
- package/src/markdown/mdast.ts +5 -2
- package/src/markdown/relative-links.ts +12 -4
- package/src/markdown/variables.ts +56 -0
- package/src/markdown/views.ts +82 -0
- package/src/narration/build.ts +412 -0
- package/src/narration/player.ts +249 -0
- package/src/narration/provider.ts +29 -0
- package/src/narration/script.ts +592 -0
- package/src/og/card.ts +136 -11
- package/src/og/derive.ts +41 -31
- package/src/og/index.ts +1 -0
- package/src/openapi/asyncapi.ts +4 -2
- package/src/openapi/model.ts +77 -36
- package/src/openapi/overlay.ts +256 -0
- package/src/openapi/parse.ts +55 -2
- package/src/openapi/proxy.ts +63 -10
- package/src/openapi/references.ts +21 -5
- package/src/openapi/render-mdx.ts +46 -3
- package/src/openapi/scalar.ts +40 -6
- package/src/openapi/source.ts +1 -1
- package/src/ratelimit/cloudflare.ts +54 -0
- package/src/ratelimit/index.ts +30 -0
- package/src/ratelimit/memory.ts +45 -0
- package/src/ratelimit/runtime.ts +229 -0
- package/src/ratelimit/schema.ts +28 -0
- package/src/ratelimit/upstash.ts +30 -0
- package/src/ratelimit/wrangler.ts +70 -0
- package/src/reference/asyncapi.ts +2 -2
- package/src/reference/graphql.ts +5 -2
- package/src/reference/openapi.ts +21 -7
- package/src/reference/options.ts +51 -7
- package/src/reference/scalar.ts +33 -8
- package/src/registry/eject.ts +201 -28
- package/src/registry/registry.ts +7 -0
- package/src/search/adapters/version-scope.ts +30 -0
- package/src/search/documents.ts +93 -28
- package/src/search/orama-index.ts +28 -3
- package/src/search/pagefind-ranking.ts +39 -0
- package/src/search/popular.ts +2 -1
- package/src/search/sync/algolia.ts +15 -4
- package/src/search/sync/orama-cloud.ts +3 -0
- package/src/search/sync/typesense.ts +5 -0
- package/src/seo/jsonld.ts +7 -3
- package/src/seo/metatags.ts +67 -0
- package/src/theme/chrome-icons.ts +1 -0
- package/src/theme/color.ts +500 -0
- package/src/theme/entry.ts +56 -3
- package/src/theme/palette.ts +121 -45
- package/src/translate/meta.ts +68 -24
- package/src/translate/prompts.ts +1 -0
- package/src/translate/run.ts +3 -3
- package/src/translate/validate.ts +12 -3
- package/src/translate/work-list.ts +56 -17
- package/src/upgrade/upgrade.ts +4 -171
- package/dist/cli/chunk-3yce002v.js.map +0 -14
- package/dist/cli/chunk-5r8g91qn.js.map +0 -34
- package/dist/cli/chunk-6dtt0zfn.js.map +0 -10
- package/dist/cli/chunk-7mbqtmgb.js.map +0 -10
- package/dist/cli/chunk-ddndchfr.js.map +0 -14
- package/dist/cli/chunk-esh98wmb.js +0 -23
- package/dist/cli/chunk-esh98wmb.js.map +0 -10
- package/dist/cli/chunk-hqp2ajnh.js.map +0 -35
- package/dist/cli/chunk-hr8ne106.js.map +0 -13
- package/dist/cli/chunk-j85scx15.js.map +0 -10
- package/dist/cli/chunk-mqc662a6.js +0 -75
- package/dist/cli/chunk-mqc662a6.js.map +0 -11
- package/dist/cli/chunk-pbg5a4s3.js.map +0 -19
- package/dist/cli/chunk-qkb5a8sa.js.map +0 -10
- package/dist/cli/chunk-v6ya5kcb.js.map +0 -189
- package/dist/cli/chunk-wdrt2k2v.js.map +0 -10
- package/dist/cli/chunk-xh43dwgw.js.map +0 -36
- package/docs/content/sources.mdx +0 -338
- /package/dist/cli/{chunk-zp79m0ts.js.map → chunk-1n228xqb.js.map} +0 -0
- /package/dist/cli/{chunk-h2ez8dzb.js.map → chunk-5ffywsmb.js.map} +0 -0
- /package/dist/cli/{chunk-g698a744.js.map → chunk-5wj2qwn2.js.map} +0 -0
- /package/dist/cli/{chunk-sqw4ekg1.js.map → chunk-amrb6hgh.js.map} +0 -0
- /package/dist/cli/{chunk-h7k3nq3v.js.map → chunk-gp84za0h.js.map} +0 -0
- /package/dist/cli/{chunk-bfwp9vp6.js.map → chunk-p804f3pe.js.map} +0 -0
- /package/dist/cli/{chunk-00gs3wqs.js.map → chunk-ta27feev.js.map} +0 -0
- /package/dist/cli/{chunk-bbnwccaz.js.map → chunk-y4ykbark.js.map} +0 -0
package/src/core/frontmatter.ts
CHANGED
|
@@ -19,13 +19,18 @@ type StringifyArgs = Parameters<typeof baseMatter.stringify>;
|
|
|
19
19
|
/** Front matter data as gray-matter types it (`GrayMatterFile["data"]`). */
|
|
20
20
|
type FrontMatterData = ReturnType<typeof baseMatter>["data"];
|
|
21
21
|
|
|
22
|
+
/** A YAML load result: a mapping (or list) Astro keeps as front matter data. */
|
|
23
|
+
const isYamlCollection = (
|
|
24
|
+
value: ReturnType<typeof load>
|
|
25
|
+
): value is FrontMatterData => typeof value === "object" && value !== null;
|
|
26
|
+
|
|
22
27
|
const yamlEngine = {
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
// A block that loads to a scalar (`title` alone, or prose between two
|
|
29
|
+
// `---` lines) is no metadata: Astro reads it as `{}`, and so does Blume.
|
|
30
|
+
parse: (input: string): FrontMatterData => {
|
|
31
|
+
const value = load(input, { schema: YAML_SCHEMA });
|
|
32
|
+
return isYamlCollection(value) ? value : {};
|
|
33
|
+
},
|
|
29
34
|
stringify: (data: FrontMatterData): string => dump(data),
|
|
30
35
|
};
|
|
31
36
|
|
|
@@ -45,23 +50,21 @@ const withYamlEngine = <O extends { engines?: object } | undefined>(
|
|
|
45
50
|
|
|
46
51
|
/**
|
|
47
52
|
* True when a document's leading `---` line is a CommonMark thematic break,
|
|
48
|
-
* not a front matter fence.
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
53
|
+
* not a front matter fence: no closing `---` line follows. Astro's own front
|
|
54
|
+
* matter match requires that close too, while gray-matter would swallow the
|
|
55
|
+
* whole document as one unclosed YAML block and hand it to js-yaml, which
|
|
56
|
+
* crashes on ordinary Markdown (`> quote` → "a line break is expected").
|
|
57
|
+
*
|
|
58
|
+
* A blank line after the opening `---` is still front matter: Astro strips
|
|
59
|
+
* the block from the page either way, so reading it as a divider here would
|
|
60
|
+
* ignore its `draft`, `slug`, and `hidden` while its YAML leaked into search
|
|
61
|
+
* and llms.txt.
|
|
56
62
|
*/
|
|
57
63
|
const opensWithThematicBreak = (input: string): boolean => {
|
|
58
|
-
const [first = ""
|
|
64
|
+
const [first = ""] = input.split(/\r?\n/u, 1);
|
|
59
65
|
if (!/^-{3}\s*$/u.test(first)) {
|
|
60
66
|
return false;
|
|
61
67
|
}
|
|
62
|
-
if (second === undefined || second.trim() === "") {
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
65
68
|
// gray-matter closes the block at the next line-leading `---`; matching its
|
|
66
69
|
// search exactly keeps this guard from firing on any document it parses.
|
|
67
70
|
return !input.includes("\n---", 1);
|
package/src/core/graph.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
localizeRoute,
|
|
5
5
|
resolveFallbackLocale,
|
|
6
6
|
} from "./i18n.ts";
|
|
7
|
+
import { resolveLocalizable } from "./localizable.ts";
|
|
7
8
|
import {
|
|
8
9
|
validateNavIcons,
|
|
9
10
|
validateNavStructure,
|
|
@@ -97,31 +98,6 @@ const localePagesFor = (
|
|
|
97
98
|
return [...real, ...filled];
|
|
98
99
|
};
|
|
99
100
|
|
|
100
|
-
/**
|
|
101
|
-
* Resolve a possibly-per-locale header label to the string a locale renders:
|
|
102
|
-
* the active locale's entry, else the default locale's, else the map's first
|
|
103
|
-
* entry (which is also what a single-locale site gets).
|
|
104
|
-
*/
|
|
105
|
-
/** A label is either one string for every locale or a per-locale map. */
|
|
106
|
-
const isSingleLabel = (label: LocalizableLabel): label is string =>
|
|
107
|
-
typeof label === "string";
|
|
108
|
-
|
|
109
|
-
const resolveLabel = (
|
|
110
|
-
label: LocalizableLabel,
|
|
111
|
-
locale: string,
|
|
112
|
-
defaultLocale?: string
|
|
113
|
-
): string => {
|
|
114
|
-
if (isSingleLabel(label)) {
|
|
115
|
-
return label;
|
|
116
|
-
}
|
|
117
|
-
return (
|
|
118
|
-
label[locale] ??
|
|
119
|
-
(defaultLocale === undefined ? undefined : label[defaultLocale]) ??
|
|
120
|
-
Object.values(label)[0] ??
|
|
121
|
-
""
|
|
122
|
-
);
|
|
123
|
-
};
|
|
124
|
-
|
|
125
101
|
/**
|
|
126
102
|
* An explicit sidebar with every item's `href` passed through `localize` (page
|
|
127
103
|
* refs and `root`s resolve against each locale's own pages already).
|
|
@@ -154,11 +130,36 @@ const resolveTabLabels = (
|
|
|
154
130
|
...tab,
|
|
155
131
|
items: tab.items?.map((item) => ({
|
|
156
132
|
...item,
|
|
157
|
-
label:
|
|
133
|
+
label: resolveLocalizable(item.label, locale, defaultLocale),
|
|
158
134
|
})),
|
|
159
|
-
label:
|
|
135
|
+
label: resolveLocalizable(tab.label, locale, defaultLocale),
|
|
160
136
|
}));
|
|
161
137
|
|
|
138
|
+
/** A configured header or footer link with its label resolved for a locale. */
|
|
139
|
+
const withLabel = <T extends { label: LocalizableLabel }>(
|
|
140
|
+
link: T,
|
|
141
|
+
locale: string,
|
|
142
|
+
defaultLocale?: string
|
|
143
|
+
): Omit<T, "label"> & { label: string } => ({
|
|
144
|
+
...link,
|
|
145
|
+
label: resolveLocalizable(link.label, locale, defaultLocale),
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
/** The configured header links (plain, call to action, featured) in `locale`. */
|
|
149
|
+
const resolveHeaderLinks = (
|
|
150
|
+
navigation: BuildContentGraphOptions["navigation"],
|
|
151
|
+
locale: string,
|
|
152
|
+
defaultLocale?: string
|
|
153
|
+
) => ({
|
|
154
|
+
actions: navigation.actions.map((link) =>
|
|
155
|
+
withLabel(link, locale, defaultLocale)
|
|
156
|
+
),
|
|
157
|
+
cta: navigation.cta ? withLabel(navigation.cta, locale, defaultLocale) : null,
|
|
158
|
+
featured: navigation.featured.map((link) =>
|
|
159
|
+
withLabel(link, locale, defaultLocale)
|
|
160
|
+
),
|
|
161
|
+
});
|
|
162
|
+
|
|
162
163
|
/**
|
|
163
164
|
* Mount a configured link authored as if the site were served at root (the
|
|
164
165
|
* banner's): under `basePath`, except a route served outside the content tree
|
|
@@ -274,18 +275,22 @@ const buildLocaleNavigation = (
|
|
|
274
275
|
...item,
|
|
275
276
|
href: localizeServed(item.href, false),
|
|
276
277
|
});
|
|
277
|
-
const { actions, cta, featured } =
|
|
278
|
+
const { actions, cta, featured } = resolveHeaderLinks(
|
|
279
|
+
options.navigation,
|
|
280
|
+
code,
|
|
281
|
+
i18n.defaultLocale
|
|
282
|
+
);
|
|
278
283
|
const sidebarItems = options.navigation.sidebar.items;
|
|
279
284
|
|
|
280
285
|
const navigation = buildNavigation(localePages, {
|
|
281
|
-
actions: actions
|
|
286
|
+
actions: actions.map(localizeHref),
|
|
282
287
|
basePath,
|
|
283
288
|
cta: cta ? localizeHref(cta) : null,
|
|
284
289
|
diagnostics,
|
|
285
290
|
display: options.navigation.sidebar.display,
|
|
286
291
|
extraRoutes: options.extraRoutes,
|
|
287
292
|
fallbackMetaPrefix,
|
|
288
|
-
featured: featured
|
|
293
|
+
featured: featured.map(localizeHref),
|
|
289
294
|
folderMeta: options.folderMeta,
|
|
290
295
|
// The localized tree root ("/" for the hidden default, "/fr" otherwise;
|
|
291
296
|
// "/fr/v1.0" inside a snapshot): the tab pointing here spans the whole
|
|
@@ -415,13 +420,11 @@ const buildVersionNavigation = (
|
|
|
415
420
|
}
|
|
416
421
|
return {
|
|
417
422
|
"": buildNavigation(versionPages, {
|
|
418
|
-
|
|
423
|
+
...resolveHeaderLinks(options.navigation, ""),
|
|
419
424
|
basePath: options.basePath ?? "",
|
|
420
|
-
cta: options.navigation.cta,
|
|
421
425
|
diagnostics,
|
|
422
426
|
display: options.navigation.sidebar.display,
|
|
423
427
|
extraRoutes: options.extraRoutes,
|
|
424
|
-
featured: options.navigation.featured,
|
|
425
428
|
folderMeta: options.folderMeta,
|
|
426
429
|
localizedRoot: versionizeRoute("/", id),
|
|
427
430
|
metaPrefix: id,
|
|
@@ -465,13 +468,13 @@ export const buildContentGraph = (
|
|
|
465
468
|
));
|
|
466
469
|
} else {
|
|
467
470
|
navigation = buildNavigation(currentPages, {
|
|
468
|
-
|
|
471
|
+
// No locale to prefer: a per-locale label map resolves to its first
|
|
472
|
+
// entry on a single-locale site.
|
|
473
|
+
...resolveHeaderLinks(options.navigation, ""),
|
|
469
474
|
basePath: options.basePath ?? "",
|
|
470
|
-
cta: options.navigation.cta,
|
|
471
475
|
diagnostics,
|
|
472
476
|
display: options.navigation.sidebar.display,
|
|
473
477
|
extraRoutes: options.extraRoutes,
|
|
474
|
-
featured: options.navigation.featured,
|
|
475
478
|
folderMeta: options.folderMeta,
|
|
476
479
|
selectors: options.navigation.selectors,
|
|
477
480
|
sharedFolderMeta: options.sharedFolderMeta,
|
|
@@ -55,17 +55,12 @@ export interface HeadingMarkers {
|
|
|
55
55
|
* the heading count (mid-text brackets are ordinary prose); when a marker kind
|
|
56
56
|
* repeats, the last one written wins.
|
|
57
57
|
*
|
|
58
|
-
*
|
|
59
|
-
* bracket
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* The render-time hast plugin never passes it (resolved links are already
|
|
63
|
-
* elements by the time it runs); the raw-source scanner does.
|
|
58
|
+
* Callers pass the heading's trailing text node after a Markdown parse, so a
|
|
59
|
+
* bracket that a link-reference definition turns into a CommonMark shortcut
|
|
60
|
+
* link (`[toc]` with a `[toc]: …` definition) is already an element there and
|
|
61
|
+
* never reaches this parse.
|
|
64
62
|
*/
|
|
65
|
-
export const parseHeadingMarkers = (
|
|
66
|
-
text: string,
|
|
67
|
-
isRefDefined?: (label: string) => boolean
|
|
68
|
-
): HeadingMarkers => {
|
|
63
|
+
export const parseHeadingMarkers = (text: string): HeadingMarkers => {
|
|
69
64
|
let remaining = text;
|
|
70
65
|
let id: string | undefined;
|
|
71
66
|
let toc: HeadingMarkers["toc"];
|
|
@@ -75,14 +70,6 @@ export const parseHeadingMarkers = (
|
|
|
75
70
|
match = MARKER.exec(remaining)
|
|
76
71
|
) {
|
|
77
72
|
const explicitId = match.groups.curlyId ?? match.groups.id;
|
|
78
|
-
const label =
|
|
79
|
-
explicitId === undefined
|
|
80
|
-
? `${match.groups.hide ?? ""}toc`
|
|
81
|
-
: `#${explicitId}`;
|
|
82
|
-
// A `{#id}` is never a shortcut link, whatever the definitions say.
|
|
83
|
-
if (match.groups.curlyId === undefined && isRefDefined?.(label)) {
|
|
84
|
-
break;
|
|
85
|
-
}
|
|
86
73
|
remaining = remaining.slice(0, match.index);
|
|
87
74
|
if (explicitId === undefined) {
|
|
88
75
|
// Stripping runs right-to-left, so keeping the first capture of each
|
package/src/core/i18n-ui.ts
CHANGED
|
@@ -15,6 +15,8 @@ const uiStringsObject = z.object({
|
|
|
15
15
|
.object({
|
|
16
16
|
addToCursor: z.string().default("Add to Cursor"),
|
|
17
17
|
addToVscode: z.string().default("Add to VS Code"),
|
|
18
|
+
// A code block's button that opens the assistant with the code attached.
|
|
19
|
+
askAboutCode: z.string().default("Ask about this code"),
|
|
18
20
|
connectMcp: z.string().default("Connect to MCP"),
|
|
19
21
|
copied: z.string().default("Copied!"),
|
|
20
22
|
copyClaudeCode: z.string().default("Copy Claude Code command"),
|
|
@@ -47,11 +49,32 @@ const uiStringsObject = z.object({
|
|
|
47
49
|
copy: z.string().default("Copy conversation"),
|
|
48
50
|
empty: z.string().default("Ask a question about the docs."),
|
|
49
51
|
error: z.string().default("Sorry, something went wrong."),
|
|
52
|
+
// The question sent about an attached code block when the reader
|
|
53
|
+
// writes none.
|
|
54
|
+
explainCode: z.string().default("Explain this code."),
|
|
50
55
|
label: z.string().default("Ask a question"),
|
|
56
|
+
// The header trigger's tooltip.
|
|
57
|
+
open: z.string().default("Open assistant"),
|
|
51
58
|
placeholder: z.string().default("Ask a question…"),
|
|
59
|
+
// The answer when the rate limit (`rateLimit`) turns a question away.
|
|
60
|
+
rateLimited: z
|
|
61
|
+
.string()
|
|
62
|
+
.default(
|
|
63
|
+
"You've asked a lot of questions. Try again in a few minutes."
|
|
64
|
+
),
|
|
65
|
+
// The attached code block's dismiss button.
|
|
66
|
+
removeCode: z.string().default("Remove code"),
|
|
52
67
|
send: z.string().default("Send"),
|
|
68
|
+
// The link to your support channel (`ai.assistant.support`), and the
|
|
69
|
+
// subject of an email it starts.
|
|
70
|
+
support: z.string().default("Contact support"),
|
|
71
|
+
supportSubject: z.string().default("Question from the docs"),
|
|
53
72
|
tip: z.string().default("Tip: You can open and close chat with"),
|
|
54
73
|
title: z.string().default("Assistant"),
|
|
74
|
+
// The answer when the bot check (`ai.assistant.captcha`) fails.
|
|
75
|
+
verifyFailed: z
|
|
76
|
+
.string()
|
|
77
|
+
.default("We couldn't check that you're human. Try again."),
|
|
55
78
|
you: z.string().default("You"),
|
|
56
79
|
})
|
|
57
80
|
.prefault({}),
|
|
@@ -70,6 +93,23 @@ const uiStringsObject = z.object({
|
|
|
70
93
|
title: z.string().default("Changelog"),
|
|
71
94
|
})
|
|
72
95
|
.prefault({}),
|
|
96
|
+
// The built-in cookie consent banner (`consent: native()`) and the footer
|
|
97
|
+
// link that reopens a consent manager's preferences.
|
|
98
|
+
consent: z
|
|
99
|
+
.object({
|
|
100
|
+
accept: z.string().default("Accept"),
|
|
101
|
+
decline: z.string().default("Decline"),
|
|
102
|
+
// The banner's accessible name.
|
|
103
|
+
label: z.string().default("Cookie consent"),
|
|
104
|
+
message: z
|
|
105
|
+
.string()
|
|
106
|
+
.default(
|
|
107
|
+
"We'd like to use cookies to understand how these docs are used."
|
|
108
|
+
),
|
|
109
|
+
policy: z.string().default("Privacy policy"),
|
|
110
|
+
settings: z.string().default("Cookie settings"),
|
|
111
|
+
})
|
|
112
|
+
.prefault({}),
|
|
73
113
|
content: z
|
|
74
114
|
.object({
|
|
75
115
|
// `<Component>`'s source tab.
|
|
@@ -89,7 +129,12 @@ const uiStringsObject = z.object({
|
|
|
89
129
|
prop: z.string().default("Prop"),
|
|
90
130
|
// `<Tabs dropdown>`: the accessible name of the tab picker.
|
|
91
131
|
selectTab: z.string().default("Select tab"),
|
|
92
|
-
// `<
|
|
132
|
+
// `<View>`: the accessible name of the page's view picker.
|
|
133
|
+
selectView: z.string().default("Select view"),
|
|
134
|
+
// An expanded `expandable` code block's toggle.
|
|
135
|
+
showLess: z.string().default("Show less"),
|
|
136
|
+
// `<Expandable>`'s toggle when it sets no `title`, and a collapsed
|
|
137
|
+
// `expandable` code block's.
|
|
93
138
|
showMore: z.string().default("Show more"),
|
|
94
139
|
stars: z.string().default("Stars"),
|
|
95
140
|
// An untitled tab's label; `{n}` is replaced with its position.
|
|
@@ -102,8 +147,11 @@ const uiStringsObject = z.object({
|
|
|
102
147
|
.prefault({}),
|
|
103
148
|
feedback: z
|
|
104
149
|
.object({
|
|
150
|
+
// The written comment box after the rating (`feedback.comments`).
|
|
151
|
+
comment: z.string().default("Tell us more (optional)"),
|
|
105
152
|
no: z.string().default("No"),
|
|
106
153
|
question: z.string().default("Was this page helpful?"),
|
|
154
|
+
send: z.string().default("Send"),
|
|
107
155
|
thanks: z.string().default("Thanks for your feedback!"),
|
|
108
156
|
yes: z.string().default("Yes"),
|
|
109
157
|
})
|
|
@@ -114,6 +162,36 @@ const uiStringsObject = z.object({
|
|
|
114
162
|
untranslated: z.string().default("Not translated"),
|
|
115
163
|
})
|
|
116
164
|
.prefault({}),
|
|
165
|
+
narration: z
|
|
166
|
+
.object({
|
|
167
|
+
// Spoken, not shown: the cues that introduce a callout of each type, a
|
|
168
|
+
// step (`{n}` is its number), a tab (`{title}` is its label), and a
|
|
169
|
+
// collapsible section. End each with a full stop so it reads as its own
|
|
170
|
+
// sentence.
|
|
171
|
+
cueDanger: z.string().default("Danger."),
|
|
172
|
+
cueInfo: z.string().default("Info."),
|
|
173
|
+
cueNote: z.string().default("Note."),
|
|
174
|
+
cueSection: z.string().default("Expandable section."),
|
|
175
|
+
cueStep: z.string().default("Step {n}."),
|
|
176
|
+
cueSuccess: z.string().default("Success."),
|
|
177
|
+
cueTab: z.string().default("{title} tab."),
|
|
178
|
+
cueTip: z.string().default("Tip."),
|
|
179
|
+
cueWarning: z.string().default("Warning."),
|
|
180
|
+
error: z.string().default("The narration couldn't load."),
|
|
181
|
+
// Resumes following the sentence being read after the reader scrolls away.
|
|
182
|
+
follow: z.string().default("Follow along"),
|
|
183
|
+
label: z.string().default("Listen to this page"),
|
|
184
|
+
// The estimated listening time; `{n}` is whole minutes.
|
|
185
|
+
minutes: z.string().default("{n} min"),
|
|
186
|
+
next: z.string().default("Next sentence"),
|
|
187
|
+
pause: z.string().default("Pause"),
|
|
188
|
+
play: z.string().default("Play"),
|
|
189
|
+
previous: z.string().default("Previous sentence"),
|
|
190
|
+
progress: z.string().default("Narration progress"),
|
|
191
|
+
speed: z.string().default("Playback speed"),
|
|
192
|
+
stop: z.string().default("Stop listening"),
|
|
193
|
+
})
|
|
194
|
+
.prefault({}),
|
|
117
195
|
nav: z
|
|
118
196
|
.object({
|
|
119
197
|
back: z.string().default("Back"),
|
|
@@ -128,7 +206,7 @@ const uiStringsObject = z.object({
|
|
|
128
206
|
primary: z.string().default("Primary"),
|
|
129
207
|
sections: z.string().default("Sections"),
|
|
130
208
|
toggleNavigation: z.string().default("Toggle navigation"),
|
|
131
|
-
toggleTheme: z.string().default("Toggle
|
|
209
|
+
toggleTheme: z.string().default("Toggle theme"),
|
|
132
210
|
})
|
|
133
211
|
.prefault({}),
|
|
134
212
|
notFound: z
|
|
@@ -154,6 +232,8 @@ const uiStringsObject = z.object({
|
|
|
154
232
|
next: z.string().default("Next"),
|
|
155
233
|
pagination: z.string().default("Pagination"),
|
|
156
234
|
previous: z.string().default("Previous"),
|
|
235
|
+
// The heading over a page's `related` links.
|
|
236
|
+
related: z.string().default("Related pages"),
|
|
157
237
|
skipToContent: z.string().default("Skip to content"),
|
|
158
238
|
})
|
|
159
239
|
.prefault({}),
|
package/src/core/i18n.ts
CHANGED
|
@@ -219,11 +219,31 @@ export const localeTargetPath = (
|
|
|
219
219
|
return `${folder}/${rel}`;
|
|
220
220
|
};
|
|
221
221
|
|
|
222
|
+
/**
|
|
223
|
+
* The warning for a top-level folder named after the default locale
|
|
224
|
+
* (`docs/en/` when `defaultLocale` is `en`). Only non-default codes are
|
|
225
|
+
* locale folders — the default locale lives at the content root — so the
|
|
226
|
+
* folder is ordinary default-locale content, published one level down at
|
|
227
|
+
* `/en/…` (`/en/en/…` with the default prefix shown) and filled into every
|
|
228
|
+
* other locale at `/fr/en/…`.
|
|
229
|
+
*/
|
|
230
|
+
const defaultLocaleFolderDiagnostic = (
|
|
231
|
+
folder: string,
|
|
232
|
+
i18n: ResolvedI18nConfig
|
|
233
|
+
): Diagnostic => ({
|
|
234
|
+
code: "BLUME_I18N_DEFAULT_LOCALE_FOLDER",
|
|
235
|
+
message: `Folder "${folder}/" is named after the default locale, but "${i18n.defaultLocale}" pages live at the content root — its pages publish at ${localizeRoute(`/${folder}`, i18n.defaultLocale, i18n)}/… instead.`,
|
|
236
|
+
severity: "warning",
|
|
237
|
+
suggestion: `Move the contents of "${folder}/" up to the content root; only the other locales get a folder.`,
|
|
238
|
+
});
|
|
239
|
+
|
|
222
240
|
/**
|
|
223
241
|
* Warn about top-level content folders that look like a locale (a code Blume
|
|
224
242
|
* recognizes) but aren't declared in `i18n.locales`. Without this they're
|
|
225
243
|
* silently treated as default-locale content under a `/<code>/…` route, which
|
|
226
244
|
* is almost never intended — usually a translation that wasn't registered.
|
|
245
|
+
* A folder named after the default locale gets the same treatment (see
|
|
246
|
+
* {@link defaultLocaleFolderDiagnostic}).
|
|
227
247
|
*/
|
|
228
248
|
export const i18nDiagnostics = (
|
|
229
249
|
pages: PageRecord[],
|
|
@@ -242,9 +262,17 @@ export const i18nDiagnostics = (
|
|
|
242
262
|
// Inside a version snapshot the locale dir sits one level deeper
|
|
243
263
|
// (`v1.0/fr/guide.md`), so a configured version segment is skipped first.
|
|
244
264
|
const parts = page.source.ref.split("/");
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
265
|
+
const folder = parts[0] && versionDirs.has(parts[0]) ? parts[1] : parts[0];
|
|
266
|
+
const first = folder?.toLowerCase();
|
|
267
|
+
if (
|
|
268
|
+
folder !== undefined &&
|
|
269
|
+
first === i18n.defaultLocale.toLowerCase() &&
|
|
270
|
+
!seen.has(first)
|
|
271
|
+
) {
|
|
272
|
+
seen.add(first);
|
|
273
|
+
diagnostics.push(defaultLocaleFolderDiagnostic(folder, i18n));
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
248
276
|
if (
|
|
249
277
|
first &&
|
|
250
278
|
!seen.has(first) &&
|
package/src/core/includes.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type { FenceState } from "./code-fences.ts";
|
|
|
7
7
|
import matter from "./frontmatter.ts";
|
|
8
8
|
import { INLINE_CODE, MD_IMAGE, targetOffsetIn } from "./sources/normalize.ts";
|
|
9
9
|
import type { Diagnostic } from "./types.ts";
|
|
10
|
+
import { substituteVariables } from "./variables.ts";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Content includes: `<include>./relative.mdx</include>` on a line of its own
|
|
@@ -98,6 +99,12 @@ export interface IncludeAttributes {
|
|
|
98
99
|
lang?: string;
|
|
99
100
|
/** Fence meta string in code-block mode (e.g. `title="lib.ts"`). */
|
|
100
101
|
meta?: string;
|
|
102
|
+
/**
|
|
103
|
+
* Every other attribute with a value: the snippet's own variables.
|
|
104
|
+
* `<include plan="Pro">` makes `{{plan}}` in the included file read `Pro`,
|
|
105
|
+
* ahead of any site-wide variable of the same name.
|
|
106
|
+
*/
|
|
107
|
+
props?: Record<string, string>;
|
|
101
108
|
}
|
|
102
109
|
|
|
103
110
|
/** A parsed include statement. */
|
|
@@ -108,8 +115,8 @@ export interface IncludeStatement {
|
|
|
108
115
|
|
|
109
116
|
/**
|
|
110
117
|
* Parse one trimmed line as an include statement, or `null` when it isn't
|
|
111
|
-
* one.
|
|
112
|
-
*
|
|
118
|
+
* one. `lang` and `meta` shape a code include; every other attribute with a
|
|
119
|
+
* value becomes a prop, a variable scoped to the included file.
|
|
113
120
|
*/
|
|
114
121
|
export const parseIncludeStatement = (
|
|
115
122
|
line: string
|
|
@@ -128,6 +135,9 @@ export const parseIncludeStatement = (
|
|
|
128
135
|
if (name === "meta" && value) {
|
|
129
136
|
attributes.meta = value;
|
|
130
137
|
}
|
|
138
|
+
if (name && name !== "lang" && name !== "meta" && value !== undefined) {
|
|
139
|
+
attributes.props = { ...attributes.props, [name]: value };
|
|
140
|
+
}
|
|
131
141
|
}
|
|
132
142
|
return { attributes, target: match.groups.target ?? "" };
|
|
133
143
|
};
|
|
@@ -519,8 +529,15 @@ const expandStatement = async (
|
|
|
519
529
|
lines.shift();
|
|
520
530
|
origins.shift();
|
|
521
531
|
}
|
|
532
|
+
// Props replace their `{{name}}` references line by line, so every line
|
|
533
|
+
// keeps its origin; they reach the file's own nested includes too.
|
|
534
|
+
const { props } = statement.attributes;
|
|
522
535
|
return {
|
|
523
|
-
lines: rebaseImages(
|
|
536
|
+
lines: rebaseImages(
|
|
537
|
+
props ? lines.map((text) => substituteVariables(text, props)) : lines,
|
|
538
|
+
dirname(path),
|
|
539
|
+
dirname(filePath)
|
|
540
|
+
),
|
|
524
541
|
origins,
|
|
525
542
|
};
|
|
526
543
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
2
3
|
|
|
3
|
-
import { relative } from "pathe";
|
|
4
|
+
import { normalize, relative } from "pathe";
|
|
4
5
|
|
|
5
6
|
import type { Diagnostic } from "./types.ts";
|
|
6
7
|
|
|
@@ -58,6 +59,23 @@ export const parseGitLog = (output: string): Map<string, string> => {
|
|
|
58
59
|
return times;
|
|
59
60
|
};
|
|
60
61
|
|
|
62
|
+
/**
|
|
63
|
+
* `path` with every symlink resolved, the spelling git prints paths in: a
|
|
64
|
+
* project opened through a link (`/tmp/site` on macOS, where `/tmp` links to
|
|
65
|
+
* `/private/tmp`) gets `/private/tmp/site` from `rev-parse --show-toplevel`,
|
|
66
|
+
* so the project's own spelling would compare as outside the repository and
|
|
67
|
+
* every page would go undated. The native realpath also expands a Windows
|
|
68
|
+
* 8.3 short name (`RUNNER~1`), which git does too. A path that doesn't exist
|
|
69
|
+
* stays as written.
|
|
70
|
+
*/
|
|
71
|
+
export const realPath = (path: string): string => {
|
|
72
|
+
try {
|
|
73
|
+
return normalize(realpathSync.native(path));
|
|
74
|
+
} catch {
|
|
75
|
+
return path;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
61
79
|
/**
|
|
62
80
|
* The toplevel of the repository containing `root`, or null when git is
|
|
63
81
|
* unavailable or the project isn't a repo. Callers use it to decide which
|
|
@@ -108,6 +126,10 @@ export const gitLastModifiedTimes = (
|
|
|
108
126
|
if (gitRoot === null) {
|
|
109
127
|
return new Map();
|
|
110
128
|
}
|
|
129
|
+
// Git names paths by their real location, so the pathspecs and the paths
|
|
130
|
+
// read back against the log are compared in that spelling too (see
|
|
131
|
+
// `realPath`).
|
|
132
|
+
const top = realPath(gitRoot);
|
|
111
133
|
try {
|
|
112
134
|
const output = execFileSync(
|
|
113
135
|
// oxlint-disable-next-line sonarjs/no-os-command-from-path -- git is a required dev-tool dependency resolved from PATH
|
|
@@ -121,14 +143,14 @@ export const gitLastModifiedTimes = (
|
|
|
121
143
|
"--format=%x00%cI",
|
|
122
144
|
"--name-only",
|
|
123
145
|
"--",
|
|
124
|
-
...contentRoots,
|
|
146
|
+
...contentRoots.map(realPath),
|
|
125
147
|
],
|
|
126
148
|
{ encoding: "utf-8", env: gitEnv(), maxBuffer: 256 * 1024 * 1024 }
|
|
127
149
|
);
|
|
128
150
|
const byRepoPath = parseGitLog(output);
|
|
129
151
|
const result = new Map<string, string>();
|
|
130
152
|
for (const sourcePath of sourcePaths) {
|
|
131
|
-
const iso = byRepoPath.get(relative(
|
|
153
|
+
const iso = byRepoPath.get(relative(top, realPath(sourcePath)));
|
|
132
154
|
if (iso) {
|
|
133
155
|
result.set(sourcePath, iso);
|
|
134
156
|
}
|
package/src/core/links.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
import type { LocaleRouting } from "./i18n.ts";
|
|
11
11
|
import { localizeLinkPath } from "./locale-links.ts";
|
|
12
12
|
import { gradeExternal, probeAll } from "./probe.ts";
|
|
13
|
+
import { isPatternPath, pathsUnderPattern } from "./redirect-patterns.ts";
|
|
13
14
|
import type {
|
|
14
15
|
ContentGraph,
|
|
15
16
|
Diagnostic,
|
|
@@ -87,6 +88,8 @@ interface LinkContext {
|
|
|
87
88
|
publicDir: string | null;
|
|
88
89
|
/** Normalized `redirect.from` paths — valid targets that resolve at runtime. */
|
|
89
90
|
redirects: Set<string>;
|
|
91
|
+
/** Pattern `redirect.from`s (`/beta/:slug*`), based like `redirects`. */
|
|
92
|
+
redirectPatterns: string[];
|
|
90
93
|
routes: Set<string>;
|
|
91
94
|
}
|
|
92
95
|
|
|
@@ -322,10 +325,16 @@ const checkPathLink = (
|
|
|
322
325
|
if (ctx.extraRoutes.has(route)) {
|
|
323
326
|
return null;
|
|
324
327
|
}
|
|
325
|
-
// A configured `redirect.from
|
|
328
|
+
// A configured `redirect.from`, or a path a pattern `from` covers, resolves
|
|
329
|
+
// at runtime, so it's a valid target.
|
|
326
330
|
// Its destination (and any anchor there) is validated on its own page, so we
|
|
327
331
|
// don't follow the redirect to check the fragment here.
|
|
328
|
-
if (
|
|
332
|
+
if (
|
|
333
|
+
ctx.redirects.has(route) ||
|
|
334
|
+
ctx.redirectPatterns.some(
|
|
335
|
+
(from) => pathsUnderPattern(from, [route]).length > 0
|
|
336
|
+
)
|
|
337
|
+
) {
|
|
329
338
|
return null;
|
|
330
339
|
}
|
|
331
340
|
|
|
@@ -585,6 +594,11 @@ export const validateLinks = async (
|
|
|
585
594
|
fileRoutes: buildFileRouteIndex(graph.pages, options.i18n ?? null),
|
|
586
595
|
i18n: options.i18n ?? null,
|
|
587
596
|
publicDir: options.publicDir,
|
|
597
|
+
redirectPatterns: (options.redirects ?? []).flatMap((redirect) =>
|
|
598
|
+
isPatternPath(redirect.from)
|
|
599
|
+
? [withBasePath(basePath, redirect.from)]
|
|
600
|
+
: []
|
|
601
|
+
),
|
|
588
602
|
redirects: new Set(
|
|
589
603
|
(options.redirects ?? []).map((redirect) =>
|
|
590
604
|
toRoute(withBasePath(basePath, redirect.from))
|
package/src/core/locale-links.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isInternalPath,
|
|
3
|
+
mountBasePath,
|
|
3
4
|
normalizePath,
|
|
4
5
|
stripBasePath,
|
|
5
6
|
withBasePath,
|
|
@@ -120,7 +121,10 @@ export const localizeHref = (
|
|
|
120
121
|
if (localized === based) {
|
|
121
122
|
return href;
|
|
122
123
|
}
|
|
123
|
-
|
|
124
|
+
// The base came off above, so it goes back on unconditionally: a localized
|
|
125
|
+
// path that starts with the base's own name (`/ja/…` under base `/ja`) is
|
|
126
|
+
// still served under it.
|
|
127
|
+
return `${mountBasePath(options.deployBase, localized)}${suffix}`;
|
|
124
128
|
};
|
|
125
129
|
|
|
126
130
|
/** Every `<a …>` opening tag; `\s` keeps `<abbr>`/`<astro-island>` out. */
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { LocalizableLabel } from "./schema.ts";
|
|
2
|
+
|
|
3
|
+
/** A label is either one string for every locale or a per-locale map. */
|
|
4
|
+
const isSingleLabel = (label: LocalizableLabel): label is string =>
|
|
5
|
+
typeof label === "string";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Resolve a possibly-per-locale label to the string a locale renders: the
|
|
9
|
+
* active locale's entry, else the default locale's, else the map's first
|
|
10
|
+
* entry (which is also what a single-locale site gets).
|
|
11
|
+
*/
|
|
12
|
+
export const resolveLocalizable = (
|
|
13
|
+
label: LocalizableLabel,
|
|
14
|
+
locale?: string,
|
|
15
|
+
defaultLocale?: string
|
|
16
|
+
): string => {
|
|
17
|
+
if (isSingleLabel(label)) {
|
|
18
|
+
return label;
|
|
19
|
+
}
|
|
20
|
+
return (
|
|
21
|
+
(locale === undefined ? undefined : label[locale]) ??
|
|
22
|
+
(defaultLocale === undefined ? undefined : label[defaultLocale]) ??
|
|
23
|
+
Object.values(label)[0] ??
|
|
24
|
+
""
|
|
25
|
+
);
|
|
26
|
+
};
|