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
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pattern redirects: a `from` that covers many paths, written the way
|
|
3
|
+
* Mintlify and `vercel.json` write them.
|
|
4
|
+
*
|
|
5
|
+
* - `:name` matches one whole segment: `/blog/:slug` → `/articles/:slug`.
|
|
6
|
+
* - `:name*` as the last segment matches the rest of the path, any number of
|
|
7
|
+
* segments: `/beta/:slug*` → `/v2/:slug*` sends `/beta/a/b` to `/v2/a/b`
|
|
8
|
+
* and `/beta` itself to `/v2`. A last segment of `*` does the same.
|
|
9
|
+
* - `*` ending a segment matches the rest of the path from there:
|
|
10
|
+
* `/articles/concepts-*` → `/overview`, or `/old/article-*` →
|
|
11
|
+
* `/new/article-*` to carry what it matched (`/new/article-:splat`, as
|
|
12
|
+
* `_redirects` spells it, reads the same capture).
|
|
13
|
+
*
|
|
14
|
+
* `to` reads a capture as a whole segment (`/:name`, `/:name*`, `/*`) or, for
|
|
15
|
+
* `*`, wherever it stands. Hosts disagree on the rest of the syntax, so each
|
|
16
|
+
* gets its own translation from {@link expandRedirect}: `_redirects` splats,
|
|
17
|
+
* `vercel.json` sources, and the regular expressions Vercel's routing config,
|
|
18
|
+
* the Node and Cloudflare wrappers, and the dev server match with. A capture
|
|
19
|
+
* that may be empty is expanded into two rules first, so `/beta` lands on
|
|
20
|
+
* `/v2` rather than `/v2/` on every host.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/** The name `*` captures under, which `_redirects` reads as `:splat`. */
|
|
24
|
+
export const SPLAT = "splat";
|
|
25
|
+
|
|
26
|
+
/** A piece of a parsed `from`, or of a rule it expands to. */
|
|
27
|
+
type Part =
|
|
28
|
+
| { kind: "text"; text: string }
|
|
29
|
+
/** `:name`: one whole segment. */
|
|
30
|
+
| { kind: "param"; name: string }
|
|
31
|
+
/** `/:name*` or `/*` ending the path: zero or more segments. */
|
|
32
|
+
| { kind: "rest"; name: string }
|
|
33
|
+
/** A rest in an expanded rule: one or more characters after its `/`. */
|
|
34
|
+
| { kind: "tail"; name: string }
|
|
35
|
+
/** `*` ending a segment: any characters, none included. */
|
|
36
|
+
| { kind: "splat" };
|
|
37
|
+
|
|
38
|
+
/** A piece of an expanded rule, where a rest has become a tail. */
|
|
39
|
+
type RulePart = Exclude<Part, { kind: "rest" }>;
|
|
40
|
+
|
|
41
|
+
/** A redirect's two ends, as configured or based. */
|
|
42
|
+
interface RedirectEnds {
|
|
43
|
+
from: string;
|
|
44
|
+
status: number;
|
|
45
|
+
to: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** One rule a redirect expands to. */
|
|
49
|
+
export interface RedirectRule {
|
|
50
|
+
parts: RulePart[];
|
|
51
|
+
status: number;
|
|
52
|
+
to: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const PARAM = /^:(?<name>[A-Za-z_]\w*)(?<rest>\*)?$/u;
|
|
56
|
+
const PARAM_START = /^:[A-Za-z_]/u;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* A capture read in `to`: a whole segment (`/:name`, `/:name*`, `/*`), which
|
|
60
|
+
* an empty value drops along with its slash, or a `*` anywhere.
|
|
61
|
+
*/
|
|
62
|
+
const REFERENCE = /\/(?::(?<name>[A-Za-z_]\w*)\*?|\*)(?=[/?#]|$)|\*/gu;
|
|
63
|
+
|
|
64
|
+
/** Parsed pieces, or why they can't be parsed. */
|
|
65
|
+
type Parsed = { parts: Part[] } | { error: string };
|
|
66
|
+
|
|
67
|
+
const text = (value: string): Part[] =>
|
|
68
|
+
value ? [{ kind: "text", text: value }] : [];
|
|
69
|
+
|
|
70
|
+
/** The pieces of one `from` segment after the ones before it. */
|
|
71
|
+
const segmentParts = (
|
|
72
|
+
segment: string,
|
|
73
|
+
last: boolean,
|
|
74
|
+
names: Set<string>
|
|
75
|
+
): Parsed => {
|
|
76
|
+
const param = PARAM.exec(segment)?.groups;
|
|
77
|
+
if (param?.name) {
|
|
78
|
+
const { name } = param;
|
|
79
|
+
if (name === SPLAT) {
|
|
80
|
+
return {
|
|
81
|
+
error:
|
|
82
|
+
"`:splat` is the name `*` captures under; name the segment something else.",
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (names.has(name)) {
|
|
86
|
+
return {
|
|
87
|
+
error: `\`:${name}\` appears twice; give each segment its own name.`,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
names.add(name);
|
|
91
|
+
if (!param.rest) {
|
|
92
|
+
return {
|
|
93
|
+
parts: [
|
|
94
|
+
{ kind: "text", text: "/" },
|
|
95
|
+
{ kind: "param", name },
|
|
96
|
+
],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return last
|
|
100
|
+
? { parts: [{ kind: "rest", name }] }
|
|
101
|
+
: {
|
|
102
|
+
error: `\`:${name}*\` matches the rest of the path, so it must be the last segment.`,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if (PARAM_START.test(segment)) {
|
|
106
|
+
return {
|
|
107
|
+
error: `\`${segment}\` isn't a whole-segment \`:param\`; write \`/:name\` or \`/:name*\`.`,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const star = segment.indexOf("*");
|
|
111
|
+
if (star === -1) {
|
|
112
|
+
return { parts: text(`/${segment}`) };
|
|
113
|
+
}
|
|
114
|
+
if (!last || star !== segment.length - 1) {
|
|
115
|
+
return {
|
|
116
|
+
error: "`*` matches the rest of the path, so it can only end it.",
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
names.add(SPLAT);
|
|
120
|
+
return segment === "*"
|
|
121
|
+
? { parts: [{ kind: "rest", name: SPLAT }] }
|
|
122
|
+
: { parts: [...text(`/${segment.slice(0, -1)}`), { kind: "splat" }] };
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/** Merge neighboring text pieces, so each format sees one run of literal path. */
|
|
126
|
+
const joinText = <Piece extends Part>(parts: readonly Piece[]): Piece[] => {
|
|
127
|
+
const joined: Piece[] = [];
|
|
128
|
+
for (const part of parts) {
|
|
129
|
+
const previous = joined.at(-1);
|
|
130
|
+
if (part.kind === "text" && previous?.kind === "text") {
|
|
131
|
+
// A fresh piece: the one before may be shared with another rule.
|
|
132
|
+
joined[joined.length - 1] = {
|
|
133
|
+
...previous,
|
|
134
|
+
text: previous.text + part.text,
|
|
135
|
+
};
|
|
136
|
+
} else {
|
|
137
|
+
joined.push(part);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return joined;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/** Parse a `from` path into literal and captured pieces. */
|
|
144
|
+
const parseFrom = (from: string): Parsed => {
|
|
145
|
+
const segments = from.split("/").slice(1);
|
|
146
|
+
const names = new Set<string>();
|
|
147
|
+
const parts: Part[] = [];
|
|
148
|
+
for (const [index, segment] of segments.entries()) {
|
|
149
|
+
const next = segmentParts(segment, index === segments.length - 1, names);
|
|
150
|
+
if ("error" in next) {
|
|
151
|
+
return next;
|
|
152
|
+
}
|
|
153
|
+
parts.push(...next.parts);
|
|
154
|
+
}
|
|
155
|
+
return { parts: joinText(parts) };
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/** The names a `from` captures. */
|
|
159
|
+
const capturedNames = (parts: readonly Part[]): Set<string> =>
|
|
160
|
+
new Set(
|
|
161
|
+
parts.flatMap((part) => {
|
|
162
|
+
if (part.kind === "param" || part.kind === "rest") {
|
|
163
|
+
return [part.name];
|
|
164
|
+
}
|
|
165
|
+
return part.kind === "splat" ? [SPLAT] : [];
|
|
166
|
+
})
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
/** The capture a `to` reference reads: `*` and `/*` read the splat. */
|
|
170
|
+
const referenceName = (name: string | undefined): string => name ?? SPLAT;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* `to` with each capture reference replaced: `replace` gets the name and
|
|
174
|
+
* whether the reference is a whole segment (its slash included in what it
|
|
175
|
+
* replaces).
|
|
176
|
+
*/
|
|
177
|
+
const replaceReferences = (
|
|
178
|
+
to: string,
|
|
179
|
+
replace: (name: string, segment: boolean) => string
|
|
180
|
+
): string =>
|
|
181
|
+
to.replaceAll(REFERENCE, (match, name: string | undefined) =>
|
|
182
|
+
replace(referenceName(name), match.startsWith("/"))
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
/** Whether a path is a pattern, rather than one exact path. */
|
|
186
|
+
export const isPatternPath = (path: string): boolean => {
|
|
187
|
+
const parsed = parseFrom(path);
|
|
188
|
+
return "error" in parsed || parsed.parts.some((part) => part.kind !== "text");
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
/** Why a redirect's ends can't work, and which end to fix. */
|
|
192
|
+
export interface RedirectPatternError {
|
|
193
|
+
end: "from" | "to";
|
|
194
|
+
message: string;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Why a redirect's ends can't work as a pattern, or `null` when they can:
|
|
199
|
+
* `from` has a malformed capture, or `to` reads one `from` doesn't make.
|
|
200
|
+
*/
|
|
201
|
+
export const redirectPatternError = (
|
|
202
|
+
from: string,
|
|
203
|
+
to: string
|
|
204
|
+
): RedirectPatternError | null => {
|
|
205
|
+
const parsed = parseFrom(from);
|
|
206
|
+
if ("error" in parsed) {
|
|
207
|
+
return { end: "from", message: parsed.error };
|
|
208
|
+
}
|
|
209
|
+
const names = capturedNames(parsed.parts);
|
|
210
|
+
const unknown: string[] = [];
|
|
211
|
+
replaceReferences(to, (name) => {
|
|
212
|
+
if (!names.has(name)) {
|
|
213
|
+
unknown.push(name);
|
|
214
|
+
}
|
|
215
|
+
return "";
|
|
216
|
+
});
|
|
217
|
+
const [first] = unknown;
|
|
218
|
+
if (first === undefined) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
const spelled = first === SPLAT ? "`*`" : `\`:${first}\``;
|
|
222
|
+
return {
|
|
223
|
+
end: "to",
|
|
224
|
+
message: `\`to\` reads ${spelled}, which \`from\` doesn't capture.`,
|
|
225
|
+
};
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* `to` with a rest's capture filled with nothing, its references dropped
|
|
230
|
+
* along with their slashes. Any other reference is to a one-segment
|
|
231
|
+
* `:param` (a `from` holds one rest or `*` at most), so it stays.
|
|
232
|
+
*/
|
|
233
|
+
const dropCapture = (to: string, dropped: string): string => {
|
|
234
|
+
const filled = replaceReferences(to, (name) =>
|
|
235
|
+
name === dropped ? "" : `/:${name}`
|
|
236
|
+
);
|
|
237
|
+
return /^(?:[?#]|$)/u.test(filled) ? `/${filled}` : filled;
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
/** The pieces of a rule: a parsed `from` holds its rest last, if at all. */
|
|
241
|
+
const ruleParts = (parts: readonly Part[]): RulePart[] =>
|
|
242
|
+
parts.filter((part): part is RulePart => part.kind !== "rest");
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* The rules a redirect expands to, in the order to try them. A `from` ending
|
|
246
|
+
* in a rest (`/:name*`, `/*`) becomes two: the bare path, its references
|
|
247
|
+
* dropped, and the path with at least one more segment. Only a pattern has
|
|
248
|
+
* rules; an exact redirect gets none.
|
|
249
|
+
*/
|
|
250
|
+
export const expandRedirect = (redirect: RedirectEnds): RedirectRule[] => {
|
|
251
|
+
const parsed = parseFrom(redirect.from);
|
|
252
|
+
if ("error" in parsed) {
|
|
253
|
+
return [];
|
|
254
|
+
}
|
|
255
|
+
const { parts } = parsed;
|
|
256
|
+
const last = parts.at(-1);
|
|
257
|
+
if (last?.kind !== "rest") {
|
|
258
|
+
const fixed = ruleParts(parts);
|
|
259
|
+
return fixed.some((part) => part.kind !== "text")
|
|
260
|
+
? [{ parts: fixed, status: redirect.status, to: redirect.to }]
|
|
261
|
+
: [];
|
|
262
|
+
}
|
|
263
|
+
const head = ruleParts(parts.slice(0, -1));
|
|
264
|
+
const tail = joinText<RulePart>([...head, { kind: "text", text: "/" }]);
|
|
265
|
+
return [
|
|
266
|
+
{
|
|
267
|
+
parts: head.length > 0 ? head : [{ kind: "text", text: "/" }],
|
|
268
|
+
status: redirect.status,
|
|
269
|
+
to: dropCapture(redirect.to, last.name),
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
parts: [...tail, { kind: "tail", name: last.name }],
|
|
273
|
+
status: redirect.status,
|
|
274
|
+
to: redirect.to,
|
|
275
|
+
},
|
|
276
|
+
];
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
/** Put exact redirects ahead of patterns, which hosts try in order. */
|
|
280
|
+
export const exactFirst = <Redirect extends { from: string }>(
|
|
281
|
+
redirects: readonly Redirect[]
|
|
282
|
+
): Redirect[] => [
|
|
283
|
+
...redirects.filter((redirect) => !isPatternPath(redirect.from)),
|
|
284
|
+
...redirects.filter((redirect) => isPatternPath(redirect.from)),
|
|
285
|
+
];
|
|
286
|
+
|
|
287
|
+
// ---------------------------------------------------------------------------
|
|
288
|
+
// Formats
|
|
289
|
+
// ---------------------------------------------------------------------------
|
|
290
|
+
|
|
291
|
+
/** The captures a rule makes, in order, by name. */
|
|
292
|
+
const captureNames = (parts: readonly RulePart[]): string[] =>
|
|
293
|
+
parts.flatMap((part) => {
|
|
294
|
+
if (part.kind === "param" || part.kind === "tail") {
|
|
295
|
+
return [part.name];
|
|
296
|
+
}
|
|
297
|
+
return part.kind === "splat" ? [SPLAT] : [];
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
/** A rule's path in `_redirects` syntax (Netlify, Cloudflare). */
|
|
301
|
+
const underscorePath = (parts: readonly RulePart[]): string =>
|
|
302
|
+
parts
|
|
303
|
+
.map((part) => {
|
|
304
|
+
if (part.kind === "text") {
|
|
305
|
+
return part.text;
|
|
306
|
+
}
|
|
307
|
+
return part.kind === "param" ? `:${part.name}` : "*";
|
|
308
|
+
})
|
|
309
|
+
.join("");
|
|
310
|
+
|
|
311
|
+
/** A rule as a `_redirects` `from` and `to`: rests and `*` are its splat. */
|
|
312
|
+
export const underscoreRedirect = (rule: RedirectRule) => {
|
|
313
|
+
const splats = new Set(
|
|
314
|
+
rule.parts.flatMap((part) => {
|
|
315
|
+
if (part.kind === "tail") {
|
|
316
|
+
return [part.name];
|
|
317
|
+
}
|
|
318
|
+
return part.kind === "splat" ? [SPLAT] : [];
|
|
319
|
+
})
|
|
320
|
+
);
|
|
321
|
+
return {
|
|
322
|
+
from: underscorePath(rule.parts),
|
|
323
|
+
to: replaceReferences(rule.to, (name, segment) => {
|
|
324
|
+
const spelled = splats.has(name) ? ":splat" : `:${name}`;
|
|
325
|
+
return segment ? `/${spelled}` : spelled;
|
|
326
|
+
}),
|
|
327
|
+
};
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* A rule as a `vercel.json` `source` and `destination` (`path-to-regexp`),
|
|
332
|
+
* each literal run escaped by `escape` so it matches only itself.
|
|
333
|
+
*/
|
|
334
|
+
export const vercelRedirect = (
|
|
335
|
+
rule: RedirectRule,
|
|
336
|
+
escape: (path: string) => string
|
|
337
|
+
) => {
|
|
338
|
+
const tails = new Set(
|
|
339
|
+
rule.parts.flatMap((part) => (part.kind === "tail" ? [part.name] : []))
|
|
340
|
+
);
|
|
341
|
+
const source = rule.parts
|
|
342
|
+
.map((part) => {
|
|
343
|
+
if (part.kind === "text") {
|
|
344
|
+
return escape(part.text);
|
|
345
|
+
}
|
|
346
|
+
if (part.kind === "param") {
|
|
347
|
+
return `:${part.name}`;
|
|
348
|
+
}
|
|
349
|
+
return part.kind === "tail" ? `:${part.name}+` : `:${SPLAT}(.*)`;
|
|
350
|
+
})
|
|
351
|
+
.join("");
|
|
352
|
+
return {
|
|
353
|
+
destination: replaceReferences(rule.to, (name, segment) => {
|
|
354
|
+
const spelled = tails.has(name) ? `:${name}+` : `:${name}`;
|
|
355
|
+
return segment ? `/${spelled}` : spelled;
|
|
356
|
+
}),
|
|
357
|
+
source,
|
|
358
|
+
};
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
const REGEX_SYNTAX = /[$()*+.?[\\\]^{|}]/gu;
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* A rule as a regular expression source over the decoded path (JavaScript
|
|
365
|
+
* and PCRE alike), a trailing slash optional, and its `to` with each capture
|
|
366
|
+
* a numbered `$n`. What Vercel's routing config and the server wrappers
|
|
367
|
+
* match with.
|
|
368
|
+
*/
|
|
369
|
+
export const regexRedirect = (rule: RedirectRule) => {
|
|
370
|
+
const lastIndex = rule.parts.length - 1;
|
|
371
|
+
const body = rule.parts
|
|
372
|
+
.map((part, index) => {
|
|
373
|
+
if (part.kind === "text") {
|
|
374
|
+
// The optional trailing slash is added once, below.
|
|
375
|
+
const literal =
|
|
376
|
+
index === lastIndex && index > 0
|
|
377
|
+
? part.text.replace(/\/$/u, "")
|
|
378
|
+
: part.text;
|
|
379
|
+
return literal.replaceAll(REGEX_SYNTAX, String.raw`\$&`);
|
|
380
|
+
}
|
|
381
|
+
if (part.kind === "param") {
|
|
382
|
+
return "([^/]+)";
|
|
383
|
+
}
|
|
384
|
+
return part.kind === "tail" ? "(.+?)" : "(.*?)";
|
|
385
|
+
})
|
|
386
|
+
.join("");
|
|
387
|
+
const names = captureNames(rule.parts);
|
|
388
|
+
return {
|
|
389
|
+
location: replaceReferences(rule.to, (name, segment) => {
|
|
390
|
+
const reference = `$${names.indexOf(name) + 1}`;
|
|
391
|
+
return segment ? `/${reference}` : reference;
|
|
392
|
+
}),
|
|
393
|
+
source: body === "/" ? "^/$" : `^${body}/?$`,
|
|
394
|
+
};
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
/** A pattern redirect as the server wrappers carry it: regex source, `Location` template, status. */
|
|
398
|
+
export type CompiledRedirect = [
|
|
399
|
+
source: string,
|
|
400
|
+
location: string,
|
|
401
|
+
status: number,
|
|
402
|
+
];
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* The pattern redirects among `redirects`, compiled for the server wrappers
|
|
406
|
+
* and the dev server, in the order to try them. The `Location` template is
|
|
407
|
+
* percent-encoded as the header needs; a capture is encoded when it's filled
|
|
408
|
+
* (see {@link fillLocation}).
|
|
409
|
+
*/
|
|
410
|
+
export const compileRedirects = (
|
|
411
|
+
redirects: readonly RedirectEnds[]
|
|
412
|
+
): CompiledRedirect[] =>
|
|
413
|
+
redirects.flatMap(expandRedirect).map((rule) => {
|
|
414
|
+
const { location, source } = regexRedirect(rule);
|
|
415
|
+
return [source, encodeURI(location), rule.status];
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
/** A captured value, encoded for a `Location` header that must not grow a query or fragment. */
|
|
419
|
+
export const encodeCapture = (value: string): string =>
|
|
420
|
+
encodeURI(value).replaceAll("?", "%3F").replaceAll("#", "%23");
|
|
421
|
+
|
|
422
|
+
/** A `Location` template with its `$n` references filled from `match`. */
|
|
423
|
+
export const fillLocation = (
|
|
424
|
+
location: string,
|
|
425
|
+
match: RegExpExecArray
|
|
426
|
+
): string =>
|
|
427
|
+
location.replaceAll(/\$(?<index>\d+)/gu, (_reference, index: string) =>
|
|
428
|
+
encodeCapture(match[Number(index)] ?? "")
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* The `Location` and status the first compiled redirect matching a decoded
|
|
433
|
+
* path answers with, or `null` when none does.
|
|
434
|
+
*/
|
|
435
|
+
export const matchCompiledRedirect = (
|
|
436
|
+
compiled: readonly CompiledRedirect[],
|
|
437
|
+
path: string
|
|
438
|
+
): [location: string, status: number] | null => {
|
|
439
|
+
for (const [source, location, status] of compiled) {
|
|
440
|
+
const match = new RegExp(source, "u").exec(path);
|
|
441
|
+
if (match) {
|
|
442
|
+
return [fillLocation(location, match), status];
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return null;
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
/** A request URL's path, decoded; a malformed escape leaves it as sent. */
|
|
449
|
+
export const requestPath = (url: string): string => {
|
|
450
|
+
const [path = ""] = url.split(/[?#]/u, 1);
|
|
451
|
+
try {
|
|
452
|
+
return decodeURIComponent(path);
|
|
453
|
+
} catch {
|
|
454
|
+
return path;
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Where a pattern redirect sends a decoded path, unencoded, or `undefined`
|
|
460
|
+
* when none matches: what the link checks follow.
|
|
461
|
+
*/
|
|
462
|
+
export const patternDestination = (
|
|
463
|
+
redirects: readonly RedirectEnds[],
|
|
464
|
+
path: string
|
|
465
|
+
): string | undefined => {
|
|
466
|
+
for (const rule of redirects.flatMap(expandRedirect)) {
|
|
467
|
+
const { location, source } = regexRedirect(rule);
|
|
468
|
+
const match = new RegExp(source, "u").exec(path);
|
|
469
|
+
if (match) {
|
|
470
|
+
return location.replaceAll(
|
|
471
|
+
/\$(?<index>\d+)/gu,
|
|
472
|
+
(_reference, index: string) => match[Number(index)] ?? ""
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return undefined;
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* The paths a pattern `from` matches, of those given; none for an exact
|
|
481
|
+
* `from`. Only the match matters here, so the rules' destinations are moot.
|
|
482
|
+
*/
|
|
483
|
+
export const pathsUnderPattern = (
|
|
484
|
+
from: string,
|
|
485
|
+
paths: readonly string[]
|
|
486
|
+
): string[] => {
|
|
487
|
+
const sources = expandRedirect({ from, status: 0, to: "/" }).map(
|
|
488
|
+
(rule) => new RegExp(regexRedirect(rule).source, "u")
|
|
489
|
+
);
|
|
490
|
+
return paths.filter((path) => sources.some((source) => source.test(path)));
|
|
491
|
+
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Related pages: the `related` frontmatter list a page ends with, as Mintlify
|
|
3
|
+
* writes it. Each entry is a root-relative path to another page, an absolute
|
|
4
|
+
* URL, or a one-key `{ Title: link }` object that names the link itself. A
|
|
5
|
+
* page link resolves to the served route, in the reader's locale when that
|
|
6
|
+
* page is translated, and takes the page's own title and description; an
|
|
7
|
+
* internal path that matches no page is left out here and reported by the
|
|
8
|
+
* link checker, which sees every entry as one of the page's links.
|
|
9
|
+
*/
|
|
10
|
+
import { isExternalUrl, normalizeRoute, withBasePath } from "./base-path.ts";
|
|
11
|
+
import type { LocaleRouting } from "./i18n.ts";
|
|
12
|
+
import { localizeLinkPath, routeSetFor } from "./locale-links.ts";
|
|
13
|
+
import type { PageLink } from "./types.ts";
|
|
14
|
+
|
|
15
|
+
/** The most entries a page lists, as on Mintlify. */
|
|
16
|
+
export const MAX_RELATED = 10;
|
|
17
|
+
|
|
18
|
+
/** A `{ Title: link }` entry: the one key is the title, its value the link. */
|
|
19
|
+
export interface TitledRelatedEntry {
|
|
20
|
+
[title: string]: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** One `related` frontmatter entry. */
|
|
24
|
+
export type RelatedEntry = string | TitledRelatedEntry;
|
|
25
|
+
|
|
26
|
+
/** A resolved entry, ready to render. */
|
|
27
|
+
export interface RelatedLink {
|
|
28
|
+
description?: string;
|
|
29
|
+
external: boolean;
|
|
30
|
+
/** A page's route (`basePath` included, deployment base not), or a URL. */
|
|
31
|
+
href: string;
|
|
32
|
+
title: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** A served route, as the data snapshot lists it. */
|
|
36
|
+
export interface RelatedRoute {
|
|
37
|
+
description: string | null;
|
|
38
|
+
path: string;
|
|
39
|
+
title: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** What resolving needs from the site. */
|
|
43
|
+
export interface RelatedContext {
|
|
44
|
+
basePath: string;
|
|
45
|
+
/** The site's locales, when it has more than one. */
|
|
46
|
+
i18n?: LocaleRouting | null;
|
|
47
|
+
/** The locale of the page being rendered. */
|
|
48
|
+
locale: string;
|
|
49
|
+
routes: readonly RelatedRoute[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** An entry's link and, for a titled entry, its title. */
|
|
53
|
+
interface EntryParts {
|
|
54
|
+
link: string;
|
|
55
|
+
title?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const isPath = (entry: RelatedEntry): entry is string =>
|
|
59
|
+
typeof entry === "string";
|
|
60
|
+
|
|
61
|
+
const partsOf = (entry: RelatedEntry): EntryParts | undefined => {
|
|
62
|
+
if (isPath(entry)) {
|
|
63
|
+
return { link: entry };
|
|
64
|
+
}
|
|
65
|
+
const [first] = Object.entries(entry);
|
|
66
|
+
return first ? { link: first[1], title: first[0] } : undefined;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/** The links a page's `related` entries name, in the order written. */
|
|
70
|
+
export const relatedLinks = (
|
|
71
|
+
entries: readonly RelatedEntry[] | false | undefined,
|
|
72
|
+
context: RelatedContext
|
|
73
|
+
): RelatedLink[] => {
|
|
74
|
+
if (!entries) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
const served = routeSetFor(context.routes);
|
|
78
|
+
return entries.flatMap((entry): RelatedLink[] => {
|
|
79
|
+
const parts = partsOf(entry);
|
|
80
|
+
if (!parts) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
if (isExternalUrl(parts.link)) {
|
|
84
|
+
return [
|
|
85
|
+
{
|
|
86
|
+
external: true,
|
|
87
|
+
href: parts.link,
|
|
88
|
+
title: parts.title ?? new URL(parts.link, "https://x").hostname,
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
const at = parts.link.search(/[#?]/u);
|
|
93
|
+
const path = at === -1 ? parts.link : parts.link.slice(0, at);
|
|
94
|
+
const suffix = at === -1 ? "" : parts.link.slice(at);
|
|
95
|
+
const authored = withBasePath(context.basePath, normalizeRoute(path));
|
|
96
|
+
const route = context.i18n
|
|
97
|
+
? localizeLinkPath(authored, {
|
|
98
|
+
basePath: context.basePath,
|
|
99
|
+
i18n: context.i18n,
|
|
100
|
+
locale: context.locale,
|
|
101
|
+
routes: served,
|
|
102
|
+
})
|
|
103
|
+
: authored;
|
|
104
|
+
const page = context.routes.find((candidate) => candidate.path === route);
|
|
105
|
+
if (!page) {
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
return [
|
|
109
|
+
{
|
|
110
|
+
description: page.description ?? undefined,
|
|
111
|
+
external: false,
|
|
112
|
+
href: `${route}${suffix}`,
|
|
113
|
+
title: parts.title ?? page.title,
|
|
114
|
+
},
|
|
115
|
+
];
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* A page's `related` page links, as links the checker validates like any in
|
|
121
|
+
* its body. Front matter has no line map, so they point at its first line.
|
|
122
|
+
*/
|
|
123
|
+
export const relatedPageLinks = (
|
|
124
|
+
entries?: readonly RelatedEntry[] | false
|
|
125
|
+
): PageLink[] =>
|
|
126
|
+
(entries || []).flatMap((entry) => {
|
|
127
|
+
const link = partsOf(entry)?.link;
|
|
128
|
+
return link && !isExternalUrl(link)
|
|
129
|
+
? [{ column: 1, line: 1, target: link }]
|
|
130
|
+
: [];
|
|
131
|
+
});
|