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/version-cut.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { cp, readdir, readFile, rm } from "node:fs/promises";
|
|
|
3
3
|
|
|
4
4
|
import { join, relative } from "pathe";
|
|
5
5
|
|
|
6
|
-
import { stripBasePath } from "./base-path.ts";
|
|
6
|
+
import { mountBasePath, stripBasePath } from "./base-path.ts";
|
|
7
7
|
import { nextFenceState } from "./code-fences.ts";
|
|
8
8
|
import type { FenceState } from "./code-fences.ts";
|
|
9
9
|
import { writeTextAtomic } from "./fs-atomic.ts";
|
|
@@ -45,18 +45,22 @@ export class CutError extends Error {
|
|
|
45
45
|
// matching so replacements can splice into the real line by index (the mask
|
|
46
46
|
// preserves length). Mirrors the content-assets rewriter.
|
|
47
47
|
const INLINE_CODE = /`[^`]*`/gu;
|
|
48
|
-
// A markdown link/image target
|
|
49
|
-
//
|
|
50
|
-
|
|
48
|
+
// A root-absolute markdown link/image target, reference-style link definition
|
|
49
|
+
// (`[setup]: /guides/setup`, up to three spaces in, never a `[^note]:`
|
|
50
|
+
// footnote), or HTML href/src attribute in either quote style. Named groups
|
|
51
|
+
// carry the prefix (kept) and target (rewritten).
|
|
52
|
+
const ROOT_LINK =
|
|
53
|
+
/(?<prefix>\]\(|^ {0,3}\[(?!\^)[^\]]+\]:[ \t]*<?|(?:href|src)=["'])(?<target>\/[^\s"'<>)]*)/gu;
|
|
51
54
|
|
|
52
55
|
/**
|
|
53
56
|
* Build the link-rewrite table: every current-version route (basePath
|
|
54
|
-
* stripped, since authors write root-absolute links as if mounted at root
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
57
|
+
* stripped, since authors write root-absolute links as if mounted at root;
|
|
58
|
+
* a link that spells the base out is looked up without it, see
|
|
59
|
+
* `snapshotTarget`) mapped to the same page's route inside the new snapshot.
|
|
60
|
+
* Only pages the snapshot actually contains qualify — filesystem pages under
|
|
61
|
+
* the content root. Spec-rendered references (`/api`, `/events`) and remote
|
|
62
|
+
* sources aren't copied, so links to them keep pointing at the live pages
|
|
63
|
+
* instead of a 404 inside the snapshot.
|
|
60
64
|
*/
|
|
61
65
|
const buildRouteRewrites = (
|
|
62
66
|
project: BlumeProject,
|
|
@@ -105,8 +109,35 @@ const buildRouteRewrites = (
|
|
|
105
109
|
return rewrites;
|
|
106
110
|
};
|
|
107
111
|
|
|
112
|
+
/**
|
|
113
|
+
* The snapshot link a root-absolute path rewrites to, or undefined when the
|
|
114
|
+
* snapshot has no copy of its page. A path written with the base path by hand
|
|
115
|
+
* (`/docs/guides/x` under `basePath: "/docs"`) names the same page as
|
|
116
|
+
* `/guides/x` — rendering leaves an already-based link alone (see
|
|
117
|
+
* `withBasePath`) — so it is looked up without the base and keeps it.
|
|
118
|
+
*/
|
|
119
|
+
const snapshotTarget = (
|
|
120
|
+
path: string,
|
|
121
|
+
rewrites: Map<string, string>,
|
|
122
|
+
basePath: string
|
|
123
|
+
): string | undefined => {
|
|
124
|
+
const based =
|
|
125
|
+
basePath !== "" && (path === basePath || path.startsWith(`${basePath}/`));
|
|
126
|
+
if (!based) {
|
|
127
|
+
return rewrites.get(path);
|
|
128
|
+
}
|
|
129
|
+
const replacement = rewrites.get(stripBasePath(basePath, path));
|
|
130
|
+
return replacement === undefined
|
|
131
|
+
? undefined
|
|
132
|
+
: mountBasePath(basePath, replacement);
|
|
133
|
+
};
|
|
134
|
+
|
|
108
135
|
/** Rewrite one line's root-absolute internal links via the rewrite table. */
|
|
109
|
-
const rewriteLine = (
|
|
136
|
+
const rewriteLine = (
|
|
137
|
+
line: string,
|
|
138
|
+
rewrites: Map<string, string>,
|
|
139
|
+
basePath: string
|
|
140
|
+
): string => {
|
|
110
141
|
const masked = line.replaceAll(INLINE_CODE, (span) =>
|
|
111
142
|
" ".repeat(span.length)
|
|
112
143
|
);
|
|
@@ -120,7 +151,7 @@ const rewriteLine = (line: string, rewrites: Map<string, string>): string => {
|
|
|
120
151
|
const path = hash === -1 ? target : target.slice(0, hash);
|
|
121
152
|
const suffix = hash === -1 ? "" : target.slice(hash);
|
|
122
153
|
const bare = path !== "/" && path.endsWith("/") ? path.slice(0, -1) : path;
|
|
123
|
-
const replacement =
|
|
154
|
+
const replacement = snapshotTarget(bare, rewrites, basePath);
|
|
124
155
|
if (replacement === undefined) {
|
|
125
156
|
continue;
|
|
126
157
|
}
|
|
@@ -142,10 +173,12 @@ export interface SnapshotRewrite {
|
|
|
142
173
|
* Rewrite a copied page's root-absolute internal links to their snapshot
|
|
143
174
|
* equivalents, skipping fenced and inline code. Relative links need no
|
|
144
175
|
* rewriting — the whole tree copies together, so they stay self-contained.
|
|
176
|
+
* `basePath` is the site's, for links that spell it out by hand.
|
|
145
177
|
*/
|
|
146
178
|
export const rewriteSnapshotLinks = (
|
|
147
179
|
source: string,
|
|
148
|
-
rewrites: Map<string, string
|
|
180
|
+
rewrites: Map<string, string>,
|
|
181
|
+
basePath = ""
|
|
149
182
|
): SnapshotRewrite => {
|
|
150
183
|
let fence: FenceState = null;
|
|
151
184
|
let count = 0;
|
|
@@ -156,7 +189,7 @@ export const rewriteSnapshotLinks = (
|
|
|
156
189
|
if (inFence) {
|
|
157
190
|
return line;
|
|
158
191
|
}
|
|
159
|
-
const rewrittenLine = rewriteLine(line, rewrites);
|
|
192
|
+
const rewrittenLine = rewriteLine(line, rewrites, basePath);
|
|
160
193
|
if (rewrittenLine !== line) {
|
|
161
194
|
count += 1;
|
|
162
195
|
}
|
|
@@ -416,7 +449,11 @@ export const cutVersion = async (
|
|
|
416
449
|
return;
|
|
417
450
|
}
|
|
418
451
|
const source = await readFile(abs, "utf-8");
|
|
419
|
-
const { text, count } = rewriteSnapshotLinks(
|
|
452
|
+
const { text, count } = rewriteSnapshotLinks(
|
|
453
|
+
source,
|
|
454
|
+
rewrites,
|
|
455
|
+
project.config.basePath
|
|
456
|
+
);
|
|
420
457
|
if (count > 0) {
|
|
421
458
|
await writeTextAtomic(abs, text);
|
|
422
459
|
rewritten.push({ count, file: relative(dir, abs) });
|
package/src/deploy/artifacts.ts
CHANGED
|
@@ -14,6 +14,11 @@ import { API_CATALOG_PATH, buildApiCatalog } from "../ai/api-catalog.ts";
|
|
|
14
14
|
import { buildHomeLinkHeader } from "../ai/link-headers.ts";
|
|
15
15
|
import { buildLlmsFiles } from "../ai/llms.ts";
|
|
16
16
|
import { markdownRoutePaths } from "../ai/markdown.ts";
|
|
17
|
+
import {
|
|
18
|
+
buildSiteSkill,
|
|
19
|
+
SKILL_MD_PATH,
|
|
20
|
+
siteSkillName,
|
|
21
|
+
} from "../ai/site-skill.ts";
|
|
17
22
|
import {
|
|
18
23
|
AGENT_SKILLS_DIR,
|
|
19
24
|
AGENT_SKILLS_INDEX_PATH,
|
|
@@ -25,9 +30,11 @@ import {
|
|
|
25
30
|
buildSignaturesDirectory,
|
|
26
31
|
SIGNATURES_DIRECTORY_PATH,
|
|
27
32
|
} from "../ai/web-bot-auth.ts";
|
|
33
|
+
import { normalizeBasePath } from "../core/base-path.ts";
|
|
28
34
|
import { discoverPagesSync } from "../core/custom-pages.ts";
|
|
29
35
|
import type { BlumeProject } from "../core/project-graph.ts";
|
|
30
36
|
import type { ResolvedConfig } from "../core/schema.ts";
|
|
37
|
+
import { buildNarration } from "../narration/build.ts";
|
|
31
38
|
import { buildSearchIndex } from "../search/build.ts";
|
|
32
39
|
import { syncSearchProvider } from "../search/sync/index.ts";
|
|
33
40
|
import { readsHeaderFiles } from "./adapter-output.ts";
|
|
@@ -51,10 +58,12 @@ import { buildSitemapFiles, describeSitemapFiles } from "./sitemap.ts";
|
|
|
51
58
|
* Written from the integration's `astro:build:done` hook — Astro's channel
|
|
52
59
|
* for exactly this kind of post-build work — into the directory Astro
|
|
53
60
|
* reports as the client output (`dist/`, or `dist/client` for a server
|
|
54
|
-
* build
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
61
|
+
* build, or `dist/client/<base>/` on Cloudflare, whose `_headers` goes to
|
|
62
|
+
* the root the platform serves above it). The Vercel adapter copies that
|
|
63
|
+
* directory into its Build Output static tree in a later hook, so the
|
|
64
|
+
* artifacts ride along; every other adapter serves it directly. Running
|
|
65
|
+
* inside the hook rather than after `astro build` returns means an ejected
|
|
66
|
+
* project keeps producing them.
|
|
58
67
|
*
|
|
59
68
|
* Every file yields to one the user ships in `public/` (Astro copied it into
|
|
60
69
|
* the output before this runs).
|
|
@@ -88,7 +97,7 @@ const emitRedirectFiles = async (
|
|
|
88
97
|
return;
|
|
89
98
|
}
|
|
90
99
|
const platform = deployPlatform(config.deployment);
|
|
91
|
-
const redirects = platformRedirects(
|
|
100
|
+
const redirects = platformRedirects(project);
|
|
92
101
|
const vercelHeaders = platform.redirectFiles.includes(VERCEL_JSON_FILE)
|
|
93
102
|
? buildVercelHeaders(
|
|
94
103
|
config,
|
|
@@ -127,6 +136,24 @@ const emitRedirectFiles = async (
|
|
|
127
136
|
logger.info(`Emitted redirect files for ${redirects.length} redirect(s)`);
|
|
128
137
|
};
|
|
129
138
|
|
|
139
|
+
/**
|
|
140
|
+
* The root of the static assets the platform serves, where it reads
|
|
141
|
+
* `_headers`: `distDir` itself, except on a server build whose adapter moved
|
|
142
|
+
* the client output under `deployment.base` (`@astrojs/cloudflare` hands
|
|
143
|
+
* `astro:build:done` `dist/client/<base>/` but serves `dist/client`). A file
|
|
144
|
+
* left in `distDir` there would never be read, and would be served publicly
|
|
145
|
+
* at `<base>/_headers`.
|
|
146
|
+
*/
|
|
147
|
+
const assetsRoot = (config: ResolvedConfig, distDir: string): string => {
|
|
148
|
+
const base = normalizeBasePath(config.deployment.options.base);
|
|
149
|
+
const nested =
|
|
150
|
+
config.deployment.options.output === "server" &&
|
|
151
|
+
deployPlatform(config.deployment).serverClientUnderBase;
|
|
152
|
+
// One `..` per base segment climbs back out of `dist/client/<base>/`.
|
|
153
|
+
const depth = base.split("/").length - 1;
|
|
154
|
+
return nested && base ? join(distDir, "../".repeat(depth)) : distDir;
|
|
155
|
+
};
|
|
156
|
+
|
|
130
157
|
/**
|
|
131
158
|
* Emit a `_headers` file so Netlify / Cloudflare serve the raw AI-ready
|
|
132
159
|
* endpoints (`*.md`, `*.mdx`, `*.txt`) with an explicit `charset=utf-8`. Without
|
|
@@ -179,7 +206,7 @@ export const emitHeaderFiles = async (
|
|
|
179
206
|
// An adapter may have written its own rules here already (Cloudflare adds an
|
|
180
207
|
// immutable Cache-Control for /_astro/*). Keep them and append ours: both
|
|
181
208
|
// sets are wanted, and `_headers` has no merge semantics beyond order.
|
|
182
|
-
const target = join(distDir, "_headers");
|
|
209
|
+
const target = join(assetsRoot(config, distDir), "_headers");
|
|
183
210
|
const existing = existsSync(target) ? await readFile(target, "utf-8") : "";
|
|
184
211
|
await writeFile(
|
|
185
212
|
target,
|
|
@@ -191,7 +218,8 @@ export const emitHeaderFiles = async (
|
|
|
191
218
|
|
|
192
219
|
/**
|
|
193
220
|
* Collect the Agent Skills `agents.skills` publishes, once per build, so both the
|
|
194
|
-
* skills surface and llms.txt (which lists them) read the same set
|
|
221
|
+
* skills surface and llms.txt (which lists them) read the same set (the
|
|
222
|
+
* generated site skill joins them in {@link publishedSkills}). Empty
|
|
195
223
|
* when the feature is off, the directory is missing, nothing in it is
|
|
196
224
|
* publishable (each with a warning), or a user-shipped
|
|
197
225
|
* `public/.well-known/agent-skills/index.json` already owns the surface.
|
|
@@ -227,6 +255,57 @@ const collectConfiguredSkills = async (
|
|
|
227
255
|
return skills;
|
|
228
256
|
};
|
|
229
257
|
|
|
258
|
+
/**
|
|
259
|
+
* Every skill the build publishes: the `agents.skills` ones, plus the
|
|
260
|
+
* generated site skill unless one of those shares its name (the hand-written
|
|
261
|
+
* one wins) or a user-shipped index owns the surface.
|
|
262
|
+
*/
|
|
263
|
+
const publishedSkills = async (
|
|
264
|
+
project: BlumeProject,
|
|
265
|
+
distDir: string,
|
|
266
|
+
surface: { mcp: boolean; userIndex: boolean },
|
|
267
|
+
logger: ArtifactLogger
|
|
268
|
+
): Promise<SkillArtifact[]> => {
|
|
269
|
+
const configured = await collectConfiguredSkills(project, distDir, logger);
|
|
270
|
+
// The skill lists the MCP server only when the build serves it.
|
|
271
|
+
const site = surface.userIndex
|
|
272
|
+
? null
|
|
273
|
+
: buildSiteSkill({
|
|
274
|
+
...project,
|
|
275
|
+
config: advertisedConfig(project.config, {
|
|
276
|
+
mcp: surface.mcp,
|
|
277
|
+
skills: true,
|
|
278
|
+
}),
|
|
279
|
+
});
|
|
280
|
+
return site && !configured.some((skill) => skill.name === site.name)
|
|
281
|
+
? [site, ...configured]
|
|
282
|
+
: configured;
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Serve the site skill at `/skill.md` too, the path agents try first: the
|
|
287
|
+
* generated one, or a same-named hand-written skill that is a lone
|
|
288
|
+
* `SKILL.md` (an archive's relative references need its other files). A
|
|
289
|
+
* `public/skill.md` wins.
|
|
290
|
+
*/
|
|
291
|
+
const emitSkillMd = async (
|
|
292
|
+
project: BlumeProject,
|
|
293
|
+
distDir: string,
|
|
294
|
+
skills: readonly SkillArtifact[],
|
|
295
|
+
logger: ArtifactLogger
|
|
296
|
+
): Promise<void> => {
|
|
297
|
+
const target = join(distDir, SKILL_MD_PATH.slice(1));
|
|
298
|
+
const name = siteSkillName(project);
|
|
299
|
+
const skill = skills.find(
|
|
300
|
+
(candidate) => candidate.name === name && candidate.type === "skill-md"
|
|
301
|
+
);
|
|
302
|
+
if (!(project.config.agents.skillMd && skill) || existsSync(target)) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
await writeFile(target, skill.content);
|
|
306
|
+
logger.info(`Generated ${SKILL_MD_PATH.slice(1)} (the "${name}" skill)`);
|
|
307
|
+
};
|
|
308
|
+
|
|
230
309
|
/**
|
|
231
310
|
* Publish the collected Agent Skills: copy each skill artifact under
|
|
232
311
|
* `.well-known/agent-skills/` and emit the discovery index. A user-shipped
|
|
@@ -361,6 +440,10 @@ export const publishBuildArtifacts = async (
|
|
|
361
440
|
logger.info(`Indexed ${indexed} page(s) for search`);
|
|
362
441
|
}
|
|
363
442
|
|
|
443
|
+
// Generated narration reads the built pages, so it runs once they exist.
|
|
444
|
+
// Nothing happens without `narration.provider`.
|
|
445
|
+
await buildNarration(project, distDir, logger);
|
|
446
|
+
|
|
364
447
|
// Upload the index to a hosted provider (Algolia, Orama Cloud, Typesense).
|
|
365
448
|
// Skipped with a warning when its admin key isn't configured.
|
|
366
449
|
await syncSearchProvider(project, {
|
|
@@ -373,18 +456,24 @@ export const publishBuildArtifacts = async (
|
|
|
373
456
|
const userSkillsIndex = existsSync(
|
|
374
457
|
join(distDir, AGENT_SKILLS_INDEX_PATH.slice(1))
|
|
375
458
|
);
|
|
376
|
-
const
|
|
459
|
+
const mcp = servesMcp(
|
|
460
|
+
project,
|
|
461
|
+
project.context.pagesRoot
|
|
462
|
+
? discoverPagesSync(project.context.pagesRoot)
|
|
463
|
+
: []
|
|
464
|
+
);
|
|
465
|
+
const skills = await publishedSkills(
|
|
466
|
+
project,
|
|
467
|
+
distDir,
|
|
468
|
+
{ mcp, userIndex: userSkillsIndex },
|
|
469
|
+
logger
|
|
470
|
+
);
|
|
377
471
|
// The discovery documents advertise what this build serves: no MCP server
|
|
378
472
|
// when a page took its route, no skills index when nothing was published.
|
|
379
473
|
const advertised: BlumeProject = {
|
|
380
474
|
...project,
|
|
381
475
|
config: advertisedConfig(project.config, {
|
|
382
|
-
mcp
|
|
383
|
-
project,
|
|
384
|
-
project.context.pagesRoot
|
|
385
|
-
? discoverPagesSync(project.context.pagesRoot)
|
|
386
|
-
: []
|
|
387
|
-
),
|
|
476
|
+
mcp,
|
|
388
477
|
skills: skills.length > 0 || userSkillsIndex,
|
|
389
478
|
}),
|
|
390
479
|
};
|
|
@@ -423,6 +512,7 @@ export const publishBuildArtifacts = async (
|
|
|
423
512
|
|
|
424
513
|
await emitWellKnownFiles(advertised.config, distDir, skills, logger);
|
|
425
514
|
await emitAgentSkills(project, distDir, skills, logger);
|
|
515
|
+
await emitSkillMd(project, distDir, skills, logger);
|
|
426
516
|
|
|
427
517
|
await emitRedirectFiles(advertised, distDir, logger);
|
|
428
518
|
await emitHeaderFiles(advertised, distDir, logger);
|
|
@@ -84,7 +84,8 @@ import {
|
|
|
84
84
|
API_PAGES_PATH,
|
|
85
85
|
OPENAPI_PATH,
|
|
86
86
|
} from "../ai/api/paths.ts";
|
|
87
|
-
import { normalizePath } from "../core/base-path.ts";
|
|
87
|
+
import { normalizeBasePath, normalizePath } from "../core/base-path.ts";
|
|
88
|
+
import { compileRedirects, isPatternPath } from "../core/redirect-patterns.ts";
|
|
88
89
|
import { CONTENT_ASSETS_ROOT, SVG_ASSET_HEADERS } from "./headers.ts";
|
|
89
90
|
import type { NotFoundVariants } from "./vercel-negotiation.ts";
|
|
90
91
|
|
|
@@ -132,14 +133,6 @@ const STATIC_EXEMPTIONS = [
|
|
|
132
133
|
API_NAVIGATION_PATH,
|
|
133
134
|
];
|
|
134
135
|
|
|
135
|
-
/**
|
|
136
|
-
* The deployment base as a rule/URL prefix: trailing slash stripped, empty
|
|
137
|
-
* for a root deploy — the same normalization the dev middleware applies in
|
|
138
|
-
* `astro/markdown-negotiation.ts`.
|
|
139
|
-
*/
|
|
140
|
-
const basePrefix = (base?: string): string =>
|
|
141
|
-
base && base !== "/" ? base.replace(/\/$/u, "") : "";
|
|
142
|
-
|
|
143
136
|
/** A value as `JSON.parse` produces it (the wrangler config's value space). */
|
|
144
137
|
type JsonValue =
|
|
145
138
|
| string
|
|
@@ -180,7 +173,7 @@ const ruleBody = (rule: string): string =>
|
|
|
180
173
|
* status (see the module comment).
|
|
181
174
|
*/
|
|
182
175
|
export const buildRunWorkerFirstRules = (base?: string): string[] => {
|
|
183
|
-
const prefix = encodeURI(
|
|
176
|
+
const prefix = encodeURI(normalizeBasePath(base));
|
|
184
177
|
const exemptions = STATIC_EXEMPTIONS.map((path) => `!${prefix}${path}`);
|
|
185
178
|
return prefix
|
|
186
179
|
? [prefix, `${prefix}/*`, ...exemptions]
|
|
@@ -282,7 +275,7 @@ export const buildNegotiationWorker = (
|
|
|
282
275
|
const routes = JSON.stringify(options.routePaths);
|
|
283
276
|
const pageJson = JSON.stringify(options.pageJsonPaths ?? []);
|
|
284
277
|
const binding = JSON.stringify(options.assetsBinding);
|
|
285
|
-
const prefix = JSON.stringify(encodeURI(
|
|
278
|
+
const prefix = JSON.stringify(encodeURI(normalizeBasePath(options.base)));
|
|
286
279
|
const homeLinkHeader = JSON.stringify(options.homeLinkHeader ?? null);
|
|
287
280
|
const homeTokens = JSON.stringify(
|
|
288
281
|
options.homeTokens === undefined ? null : String(options.homeTokens)
|
|
@@ -297,12 +290,18 @@ export const buildNegotiationWorker = (
|
|
|
297
290
|
// header, which cannot carry non-ASCII.
|
|
298
291
|
const redirects = JSON.stringify(
|
|
299
292
|
Object.fromEntries(
|
|
300
|
-
(options.redirects ?? [])
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
293
|
+
(options.redirects ?? [])
|
|
294
|
+
.filter((redirect) => !isPatternPath(redirect.from))
|
|
295
|
+
.map((redirect) => [
|
|
296
|
+
normalizePath(redirect.from),
|
|
297
|
+
[encodeURI(redirect.to), redirect.status],
|
|
298
|
+
])
|
|
304
299
|
)
|
|
305
300
|
);
|
|
301
|
+
// Patterns (`/beta/:slug*`) are tried in order after the exact paths.
|
|
302
|
+
const patternRedirects = JSON.stringify(
|
|
303
|
+
compileRedirects(options.redirects ?? [])
|
|
304
|
+
);
|
|
306
305
|
return `// Generated by Blume. Do not edit; this file is recreated on each build.
|
|
307
306
|
//
|
|
308
307
|
// Request-time \`Accept: text/markdown\` negotiation for a Cloudflare server
|
|
@@ -327,6 +326,9 @@ const ASSETS_BINDING = ${binding};
|
|
|
327
326
|
const HOME_LINK_HEADER = ${homeLinkHeader};
|
|
328
327
|
const HOME_TOKENS = ${homeTokens};
|
|
329
328
|
const REDIRECTS = ${redirects};
|
|
329
|
+
const PATTERN_REDIRECTS = ${patternRedirects}.map(
|
|
330
|
+
([source, location, status]) => [new RegExp(source, "u"), location, status]
|
|
331
|
+
);
|
|
330
332
|
const NOT_FOUND = ${notFound};
|
|
331
333
|
const SVG_ASSET_HEADERS = ${JSON.stringify(SVG_ASSET_HEADERS)};
|
|
332
334
|
|
|
@@ -343,11 +345,31 @@ const isSvgAsset = (pathname) => {
|
|
|
343
345
|
// reads — a worker-first route never reaches it. Answering from this baked-in
|
|
344
346
|
// copy keeps the exact configured status; delegating instead would let Astro's
|
|
345
347
|
// SSR handler default a GET to a permanent 301.
|
|
348
|
+
// An exact path is looked up first, then each pattern is tried in order, its
|
|
349
|
+
// Location template filled with the captures, encoded so a capture can't grow
|
|
350
|
+
// a query or fragment.
|
|
346
351
|
const redirectFor = (pathname) => {
|
|
347
352
|
const trimmed =
|
|
348
353
|
pathname !== "/" && pathname.endsWith("/") ? pathname.slice(0, -1) : pathname;
|
|
349
354
|
const path = safeDecode(trimmed);
|
|
350
|
-
|
|
355
|
+
if (Object.hasOwn(REDIRECTS, path)) {
|
|
356
|
+
return REDIRECTS[path];
|
|
357
|
+
}
|
|
358
|
+
const decoded = safeDecode(pathname);
|
|
359
|
+
for (const [pattern, location, status] of PATTERN_REDIRECTS) {
|
|
360
|
+
const match = pattern.exec(decoded);
|
|
361
|
+
if (match) {
|
|
362
|
+
return [
|
|
363
|
+
location.replace(/\\$(\\d+)/gu, (_reference, index) =>
|
|
364
|
+
encodeURI(match[Number(index)] ?? "")
|
|
365
|
+
.replaceAll("?", "%3F")
|
|
366
|
+
.replaceAll("#", "%23")
|
|
367
|
+
),
|
|
368
|
+
status,
|
|
369
|
+
];
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return null;
|
|
351
373
|
};
|
|
352
374
|
|
|
353
375
|
// The request path with the deployment base removed: the whole path on a
|
|
@@ -680,7 +702,7 @@ export const injectWorkerNegotiation = (
|
|
|
680
702
|
// The content-route guard compares against redirect `from`s, which carry the
|
|
681
703
|
// full `{deployment.base}{basePath}` stack; the routes carry only
|
|
682
704
|
// `basePath`, so the deployment base is applied here.
|
|
683
|
-
const deployPrefix =
|
|
705
|
+
const deployPrefix = normalizeBasePath(options.base);
|
|
684
706
|
const guardRoutes = new Set(
|
|
685
707
|
contentRoutePaths.map((route) =>
|
|
686
708
|
normalizePath(
|
package/src/deploy/headers.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
API_CATALOG_TYPE,
|
|
5
5
|
hasApiCatalog,
|
|
6
6
|
} from "../ai/api-catalog.ts";
|
|
7
|
+
import { servesSkillsIndex } from "../ai/skills.ts";
|
|
7
8
|
import {
|
|
8
9
|
SIGNATURES_DIRECTORY_PATH,
|
|
9
10
|
SIGNATURES_DIRECTORY_TYPE,
|
|
@@ -128,7 +129,7 @@ export const headerRules = (
|
|
|
128
129
|
}
|
|
129
130
|
// Published skills live at the deployment base, and the RFC wants their
|
|
130
131
|
// archives served as application/gzip explicitly.
|
|
131
|
-
if (config
|
|
132
|
+
if (servesSkillsIndex(config)) {
|
|
132
133
|
rules.push(
|
|
133
134
|
{
|
|
134
135
|
name: "Content-Type",
|
|
@@ -200,6 +201,13 @@ export interface VercelHeader {
|
|
|
200
201
|
/** The characters `path-to-regexp` would read as syntax in a literal path. */
|
|
201
202
|
const SOURCE_SYNTAX = /[\\(){}?+:]/gu;
|
|
202
203
|
|
|
204
|
+
/**
|
|
205
|
+
* A literal path as `path-to-regexp` source text — what a `vercel.json`
|
|
206
|
+
* `source` is — with each syntax character escaped, so it matches only itself.
|
|
207
|
+
*/
|
|
208
|
+
export const escapeVercelSource = (path: string): string =>
|
|
209
|
+
path.replaceAll(SOURCE_SYNTAX, String.raw`\$&`);
|
|
210
|
+
|
|
203
211
|
/**
|
|
204
212
|
* A `_headers` path as a `vercel.json` `source`, which `path-to-regexp` reads:
|
|
205
213
|
* the literal parts are escaped, and a `*` glob becomes the `(.*)` group that
|
|
@@ -209,10 +217,7 @@ const SOURCE_SYNTAX = /[\\(){}?+:]/gu;
|
|
|
209
217
|
const vercelSource = (path: string): string => {
|
|
210
218
|
const trimmed =
|
|
211
219
|
path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
|
|
212
|
-
return trimmed
|
|
213
|
-
.split("*")
|
|
214
|
-
.map((part) => part.replaceAll(SOURCE_SYNTAX, String.raw`\$&`))
|
|
215
|
-
.join("(.*)");
|
|
220
|
+
return trimmed.split("*").map(escapeVercelSource).join("(.*)");
|
|
216
221
|
};
|
|
217
222
|
|
|
218
223
|
/**
|
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
} from "../ai/web-bot-auth.ts";
|
|
16
16
|
import { normalizeBasePath, normalizePath } from "../core/base-path.ts";
|
|
17
17
|
import type { BlumeProject } from "../core/project-graph.ts";
|
|
18
|
+
import { compileRedirects, isPatternPath } from "../core/redirect-patterns.ts";
|
|
19
|
+
import type { CompiledRedirect } from "../core/redirect-patterns.ts";
|
|
18
20
|
import type { ResolvedConfig } from "../core/schema.ts";
|
|
19
21
|
import { SVG_ASSET_HEADERS, svgAssetPath } from "./headers.ts";
|
|
20
22
|
import { distDir } from "./platforms/paths.ts";
|
|
@@ -106,11 +108,12 @@ const patternRules = (
|
|
|
106
108
|
export type NodeRedirects = Record<string, [string, number]>;
|
|
107
109
|
|
|
108
110
|
/**
|
|
109
|
-
* The configured redirects, based the way the host matches them (see
|
|
111
|
+
* The configured exact redirects, based the way the host matches them (see
|
|
110
112
|
* `platformRedirects`) and keyed for the wrapper's lookup, the `Location`
|
|
111
113
|
* percent-encoded as Astro sends it. A redirect at a content route's own path
|
|
112
114
|
* is left out: the prerendered page owns that URL, and the static handler
|
|
113
|
-
* serves it before Astro's redirect ever could.
|
|
115
|
+
* serves it before Astro's redirect ever could. Patterns are compiled apart
|
|
116
|
+
* ({@link nodePatternRedirects}).
|
|
114
117
|
*/
|
|
115
118
|
export const nodeRedirects = (project: BlumeProject): NodeRedirects => {
|
|
116
119
|
const { config } = project;
|
|
@@ -124,8 +127,14 @@ export const nodeRedirects = (project: BlumeProject): NodeRedirects => {
|
|
|
124
127
|
)
|
|
125
128
|
);
|
|
126
129
|
return Object.fromEntries(
|
|
127
|
-
platformRedirects(
|
|
128
|
-
.filter(
|
|
130
|
+
platformRedirects(project)
|
|
131
|
+
.filter(
|
|
132
|
+
(redirect) =>
|
|
133
|
+
!(
|
|
134
|
+
isPatternPath(redirect.from) ||
|
|
135
|
+
pages.has(normalizePath(redirect.from))
|
|
136
|
+
)
|
|
137
|
+
)
|
|
129
138
|
.map((redirect) => [
|
|
130
139
|
normalizePath(redirect.from),
|
|
131
140
|
[encodeURI(redirect.to), redirect.status],
|
|
@@ -133,12 +142,27 @@ export const nodeRedirects = (project: BlumeProject): NodeRedirects => {
|
|
|
133
142
|
);
|
|
134
143
|
};
|
|
135
144
|
|
|
145
|
+
/**
|
|
146
|
+
* The configured pattern redirects (`/beta/:slug*`), based the way the host
|
|
147
|
+
* matches them and compiled for the wrapper, which tries them in order after
|
|
148
|
+
* the exact ones. None can match a page: the scan rejects a pattern that
|
|
149
|
+
* does (`BLUME_REDIRECT_MATCHES_PAGE`).
|
|
150
|
+
*/
|
|
151
|
+
export const nodePatternRedirects = (
|
|
152
|
+
project: BlumeProject
|
|
153
|
+
): CompiledRedirect[] =>
|
|
154
|
+
project.config.redirects.length === 0
|
|
155
|
+
? []
|
|
156
|
+
: compileRedirects(platformRedirects(project));
|
|
157
|
+
|
|
136
158
|
/** What the wrapper needs beyond the header rules. */
|
|
137
159
|
export interface NodeEntryOptions {
|
|
138
160
|
/** `deployment.base`, normalized (`""` at the root). */
|
|
139
161
|
base?: string;
|
|
140
162
|
/** The configured redirects, from {@link nodeRedirects}. */
|
|
141
163
|
redirects?: NodeRedirects;
|
|
164
|
+
/** The configured pattern redirects, from {@link nodePatternRedirects}. */
|
|
165
|
+
patternRedirects?: CompiledRedirect[];
|
|
142
166
|
}
|
|
143
167
|
|
|
144
168
|
/**
|
|
@@ -177,6 +201,9 @@ const BASE = ${JSON.stringify(options.base ?? "")};
|
|
|
177
201
|
const RULES = new Map(${JSON.stringify(exactRules(rules))});
|
|
178
202
|
const PATTERNS = ${JSON.stringify(patternRules(rules))};
|
|
179
203
|
const REDIRECTS = ${JSON.stringify(options.redirects ?? {})};
|
|
204
|
+
const PATTERN_REDIRECTS = ${JSON.stringify(options.patternRedirects ?? [])}.map(
|
|
205
|
+
([source, location, status]) => [new RegExp(source, "u"), location, status]
|
|
206
|
+
);
|
|
180
207
|
const safeDecode = (path) => {
|
|
181
208
|
try {
|
|
182
209
|
return decodeURIComponent(path);
|
|
@@ -210,16 +237,37 @@ const applyHeaders = (req, res) => {
|
|
|
210
237
|
}
|
|
211
238
|
}
|
|
212
239
|
};
|
|
240
|
+
// A pattern redirect's Location: its template, each $n filled with that
|
|
241
|
+
// capture, encoded so it can't grow a query or fragment.
|
|
242
|
+
const fillLocation = (location, match) =>
|
|
243
|
+
location.replace(/\\$(\\d+)/gu, (_reference, index) =>
|
|
244
|
+
encodeURI(match[Number(index)] ?? "")
|
|
245
|
+
.replaceAll("?", "%3F")
|
|
246
|
+
.replaceAll("#", "%23")
|
|
247
|
+
);
|
|
248
|
+
// The configured redirect for a decoded path: an exact one, else the first
|
|
249
|
+
// pattern that matches it.
|
|
250
|
+
const redirectFor = (path) => {
|
|
251
|
+
const trimmed = path !== "/" && path.endsWith("/") ? path.slice(0, -1) : path;
|
|
252
|
+
if (Object.hasOwn(REDIRECTS, trimmed)) {
|
|
253
|
+
return REDIRECTS[trimmed];
|
|
254
|
+
}
|
|
255
|
+
for (const [pattern, location, status] of PATTERN_REDIRECTS) {
|
|
256
|
+
const match = pattern.exec(path);
|
|
257
|
+
if (match) {
|
|
258
|
+
return [fillLocation(location, match), status];
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return null;
|
|
262
|
+
};
|
|
213
263
|
// Answer a configured redirect with its exact status; Astro's handler would
|
|
214
264
|
// send a 301 (a 308 for other methods) whatever the configured one.
|
|
215
265
|
const answerRedirect = (req, res) => {
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
const key = safeDecode(trimmed);
|
|
219
|
-
if (!Object.hasOwn(REDIRECTS, key)) {
|
|
266
|
+
const redirect = redirectFor(safeDecode(urlPath(req)));
|
|
267
|
+
if (redirect === null) {
|
|
220
268
|
return false;
|
|
221
269
|
}
|
|
222
|
-
const [location, status] =
|
|
270
|
+
const [location, status] = redirect;
|
|
223
271
|
res.writeHead(status, { location });
|
|
224
272
|
res.end();
|
|
225
273
|
return true;
|
|
@@ -291,7 +339,12 @@ export const wrapNodeEntry = async (
|
|
|
291
339
|
: []),
|
|
292
340
|
];
|
|
293
341
|
const redirects = nodeRedirects(project);
|
|
294
|
-
|
|
342
|
+
const patternRedirects = nodePatternRedirects(project);
|
|
343
|
+
if (
|
|
344
|
+
rules.length === 0 &&
|
|
345
|
+
Object.keys(redirects).length === 0 &&
|
|
346
|
+
patternRedirects.length === 0
|
|
347
|
+
) {
|
|
295
348
|
return;
|
|
296
349
|
}
|
|
297
350
|
const serverDir = join(distDir(project.context), "server");
|
|
@@ -311,6 +364,7 @@ export const wrapNodeEntry = async (
|
|
|
311
364
|
entry,
|
|
312
365
|
nodeEntryWrapper(rules, {
|
|
313
366
|
base: normalizeBasePath(project.config.deployment.options.base),
|
|
367
|
+
patternRedirects,
|
|
314
368
|
redirects,
|
|
315
369
|
}),
|
|
316
370
|
"utf-8"
|