blume 2.0.3 → 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 +48 -0
- package/README.md +1 -1
- package/dist/cli/{chunk-1d7ve1dm.js → chunk-0gz36akr.js} +18 -2
- package/dist/cli/{chunk-1d7ve1dm.js.map → chunk-0gz36akr.js.map} +3 -3
- package/dist/cli/{chunk-2eytanqx.js → chunk-1n228xqb.js} +2 -2
- package/dist/cli/{chunk-yfyb25rh.js → chunk-2f3ndkpw.js} +24 -8
- package/dist/cli/chunk-2f3ndkpw.js.map +14 -0
- package/dist/cli/{chunk-r20tn01b.js → chunk-2f7n4qzt.js} +8 -2
- package/dist/cli/{chunk-r20tn01b.js.map → chunk-2f7n4qzt.js.map} +3 -3
- package/dist/cli/{chunk-35d4wj9f.js → chunk-2n4ps1b3.js} +7 -7
- package/dist/cli/{chunk-j85vccga.js → chunk-43zcf4gz.js} +97 -57
- package/dist/cli/{chunk-j85vccga.js.map → chunk-43zcf4gz.js.map} +6 -6
- package/dist/cli/{chunk-e04dxsz1.js → chunk-4ejxbzee.js} +1 -1
- package/dist/cli/{chunk-5m5nmvyq.js → chunk-4h8z9vv5.js} +233 -39
- package/dist/cli/chunk-4h8z9vv5.js.map +16 -0
- package/dist/cli/{chunk-5f86nr5m.js → chunk-5cg2rzgf.js} +18 -16
- package/dist/cli/{chunk-5f86nr5m.js.map → chunk-5cg2rzgf.js.map} +2 -2
- package/dist/cli/{chunk-tzmab476.js → chunk-5ffywsmb.js} +4 -4
- package/dist/cli/{chunk-2z928egk.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-vg9r4eb9.js → chunk-713mngdq.js} +12 -5
- package/dist/cli/{chunk-vg9r4eb9.js.map → chunk-713mngdq.js.map} +3 -3
- package/dist/cli/{chunk-5xvm6tfj.js → chunk-9geymqns.js} +17 -15
- package/dist/cli/{chunk-5xvm6tfj.js.map → chunk-9geymqns.js.map} +2 -2
- package/dist/cli/{chunk-q58y5e6a.js → chunk-9xr4dgee.js} +26 -17
- package/dist/cli/chunk-9xr4dgee.js.map +10 -0
- package/dist/cli/{chunk-b07cmahc.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-akbpwfxc.js → chunk-cmtcnh9f.js} +2 -2
- package/dist/cli/chunk-cy5bm0j0.js +104 -0
- package/dist/cli/chunk-cy5bm0j0.js.map +11 -0
- package/dist/cli/{chunk-crgn1q09.js → chunk-e7vvk5ya.js} +4 -4
- package/dist/cli/{chunk-pehfxfta.js → chunk-g2z9x01v.js} +6 -98
- package/dist/cli/chunk-g2z9x01v.js.map +10 -0
- package/dist/cli/{chunk-tkacnehg.js → chunk-gp84za0h.js} +2 -2
- package/dist/cli/{chunk-wrr3j9w9.js → chunk-h2a48yhd.js} +186 -63
- package/dist/cli/chunk-h2a48yhd.js.map +36 -0
- package/dist/cli/{chunk-hm3vjy5s.js → chunk-jnwj5kjj.js} +14 -9
- package/dist/cli/{chunk-hm3vjy5s.js.map → chunk-jnwj5kjj.js.map} +4 -4
- package/dist/cli/chunk-kwrg63tm.js +31 -0
- package/dist/cli/chunk-kwrg63tm.js.map +10 -0
- package/dist/cli/{chunk-2hsdwb9n.js → chunk-nd1821sk.js} +18 -16
- package/dist/cli/{chunk-2hsdwb9n.js.map → chunk-nd1821sk.js.map} +2 -2
- package/dist/cli/{chunk-6dsbexzp.js → chunk-ntv4qrt7.js} +17 -15
- package/dist/cli/{chunk-6dsbexzp.js.map → chunk-ntv4qrt7.js.map} +2 -2
- package/dist/cli/{chunk-r9rcc4w7.js → chunk-p804f3pe.js} +7 -7
- package/dist/cli/{chunk-ey84smr6.js → chunk-py62fewh.js} +6 -6
- package/dist/cli/{chunk-ey84smr6.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-9t7a85s3.js → chunk-rekqz4qg.js} +23 -13
- package/dist/cli/chunk-rekqz4qg.js.map +10 -0
- package/dist/cli/{chunk-8ktnccpt.js → chunk-ta27feev.js} +1 -1
- package/dist/cli/{chunk-pv29h0wf.js → chunk-xkz5xbtv.js} +3476 -822
- package/dist/cli/chunk-xkz5xbtv.js.map +213 -0
- package/dist/cli/{chunk-a58773jm.js → chunk-y4ykbark.js} +2 -2
- package/dist/cli/{chunk-p3v96n38.js → chunk-y5h4r2y0.js} +4 -4
- package/dist/cli/{chunk-acanzt5p.js → chunk-yj17vs39.js} +6 -6
- package/dist/cli/{chunk-p73c0m7w.js → chunk-yxmk4pec.js} +16 -14
- package/dist/cli/{chunk-p73c0m7w.js.map → chunk-yxmk4pec.js.map} +2 -2
- package/dist/cli/{chunk-3em5wd2y.js → chunk-zccbjcj0.js} +4 -3
- package/dist/cli/{chunk-3em5wd2y.js.map → chunk-zccbjcj0.js.map} +2 -2
- package/dist/cli/{chunk-c8chx29p.js → chunk-zf748avv.js} +384 -207
- package/dist/cli/chunk-zf748avv.js.map +37 -0
- package/dist/cli/{chunk-g4hq16wv.js → chunk-zhvd6adm.js} +16 -14
- package/dist/cli/{chunk-g4hq16wv.js.map → chunk-zhvd6adm.js.map} +2 -2
- package/dist/cli/index.js +26 -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/site-skill.d.ts +26 -0
- package/dist/types/ai/skills.d.ts +16 -0
- package/dist/types/astro/integration.d.ts +8 -0
- 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/config-input.d.ts +187 -20
- package/dist/types/core/data.d.ts +49 -4
- package/dist/types/core/footer.d.ts +8 -0
- package/dist/types/core/i18n-ui.d.ts +86 -0
- package/dist/types/core/includes.d.ts +8 -2
- package/dist/types/core/localizable.d.ts +7 -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 +551 -56
- package/dist/types/core/sources/read.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/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/vercel.d.ts +6 -0
- package/dist/types/deploy/redirects.d.ts +39 -11
- package/dist/types/deploy/vercel-negotiation.d.ts +9 -0
- package/dist/types/markdown/code-title.d.ts +17 -0
- package/dist/types/markdown/features.d.ts +3 -2
- 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/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 +41 -5
- package/docs/03-upgrading.mdx +4 -4
- package/docs/04-migrating.mdx +1 -1
- package/docs/advanced/changelog.mdx +2 -2
- package/docs/advanced/skills.mdx +14 -0
- package/docs/cli/audit.mdx +21 -1
- package/docs/cli/index.mdx +1 -0
- package/docs/configuration/analytics.mdx +3 -1
- package/docs/configuration/assistant.mdx +188 -36
- package/docs/configuration/consent.mdx +100 -0
- package/docs/configuration/customization.mdx +4 -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 +29 -1
- package/docs/configuration/theming.mdx +3 -1
- package/docs/content/components.mdx +106 -0
- package/docs/content/frontmatter.mdx +76 -0
- package/docs/content/i18n.mdx +38 -3
- package/docs/content/includes.mdx +16 -0
- package/docs/content/meta.mdx +1 -0
- 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 +67 -2
- package/docs/content/variables.mdx +41 -0
- package/docs/discoverability/agent-discovery.mdx +29 -2
- package/docs/discoverability/index.mdx +1 -1
- package/docs/discoverability/llms-txt.mdx +1 -1
- package/docs/discoverability/metadata.mdx +17 -0
- package/docs/references/api-pages.mdx +74 -0
- package/docs/references/asyncapi.mdx +1 -1
- package/docs/references/graphql.mdx +2 -2
- package/docs/references/meta.ts +1 -1
- package/docs/references/openapi.mdx +102 -3
- 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/mintlify.md +37 -19
- package/skills/blume-migrate/references/nextra.md +2 -2
- package/skills/blume-migrate/references/starlight.md +2 -1
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +60 -14
- package/skills/blume-write-skill/SKILL.md +82 -0
- package/src/ai/agent-readability.ts +2 -1
- package/src/ai/agent-surface.ts +9 -7
- package/src/ai/ai-catalog.ts +6 -3
- 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/component-markdown.ts +75 -0
- package/src/ai/cors.ts +2 -0
- package/src/ai/index.ts +12 -0
- package/src/ai/llms.ts +6 -2
- package/src/ai/markdown.ts +2 -1
- package/src/ai/mcp/data.ts +6 -0
- package/src/ai/openapi-components.ts +9 -3
- package/src/ai/site-skill.ts +274 -0
- package/src/ai/skills.ts +35 -2
- package/src/ai/write-skill.ts +97 -0
- package/src/astro/generate.ts +132 -18
- package/src/astro/index.ts +1 -0
- package/src/astro/integration.ts +35 -0
- package/src/astro/module-types.ts +6 -0
- package/src/astro/templates.ts +283 -30
- 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 +5 -12
- package/src/audit/checks/sitemap.ts +2 -3
- package/src/audit/redirects.ts +46 -15
- package/src/audit/run.ts +2 -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/command-meta.ts +5 -0
- package/src/cli/commands/doctor.ts +22 -16
- package/src/cli/commands/skill.ts +96 -0
- package/src/cli/index.ts +5 -0
- package/src/cli/required-secrets.ts +71 -29
- package/src/components/BlumePage.astro +2 -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/Color.astro +1 -0
- 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/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/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 +104 -14
- package/src/components/islands/code-question.ts +62 -0
- package/src/components/islands/hooks.ts +110 -21
- package/src/components/islands/support-link.ts +68 -0
- package/src/components/layout/Analytics.astro +11 -2
- package/src/components/layout/Banner.astro +14 -10
- package/src/components/layout/Breadcrumbs.astro +6 -2
- 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/DrawerSwitchers.astro +51 -0
- package/src/components/layout/Empty.astro +2 -2
- package/src/components/layout/Header.astro +122 -80
- package/src/components/layout/LanguageSwitcher.astro +48 -15
- package/src/components/layout/NarrationPlayer.astro +1085 -0
- package/src/components/layout/NavSelector.astro +33 -7
- package/src/components/layout/PageFeedback.astro +54 -49
- package/src/components/layout/PageLayout.astro +71 -3
- package/src/components/layout/ReferenceLayout.astro +17 -6
- package/src/components/layout/RelatedPages.astro +42 -0
- package/src/components/layout/RootLayout.astro +308 -45
- package/src/components/layout/Search.astro +31 -17
- package/src/components/layout/SiteFooter.astro +124 -0
- package/src/components/layout/SiteMetatags.astro +16 -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/openapi/Operation.astro +161 -13
- package/src/components/openapi/RequestBody.astro +6 -4
- package/src/components/openapi/RequestPanel.astro +59 -7
- package/src/components/openapi/Responses.astro +6 -4
- package/src/components/openapi/async-snippets.ts +4 -4
- package/src/components/openapi/code-samples.ts +97 -0
- package/src/components/openapi/security.ts +6 -3
- package/src/components/openapi/snippets.ts +869 -8
- 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/builtin-tags.ts +5 -0
- package/src/core/client-address.ts +19 -0
- package/src/core/config-input.ts +194 -20
- package/src/core/data.ts +44 -4
- package/src/core/diagnostics.ts +3 -2
- package/src/core/footer.ts +26 -0
- package/src/core/graph.ts +39 -36
- package/src/core/i18n-ui.ts +82 -2
- package/src/core/includes.ts +20 -3
- package/src/core/links.ts +16 -2
- package/src/core/localizable.ts +26 -0
- package/src/core/navigation.ts +43 -12
- package/src/core/page-modes.ts +56 -0
- package/src/core/project-graph.ts +111 -5
- package/src/core/redirect-patterns.ts +491 -0
- package/src/core/related.ts +131 -0
- package/src/core/schema.ts +318 -64
- package/src/core/sources/normalize.ts +2 -1
- package/src/core/sources/read.ts +8 -3
- 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/deploy/artifacts.ts +77 -8
- package/src/deploy/cloudflare-negotiation.ts +35 -5
- package/src/deploy/headers.ts +2 -1
- package/src/deploy/node-headers.ts +63 -9
- package/src/deploy/platforms/cloudflare.ts +32 -0
- package/src/deploy/platforms/netlify.ts +33 -1
- package/src/deploy/platforms/vercel.ts +37 -2
- package/src/deploy/redirects.ts +91 -24
- package/src/deploy/vercel-negotiation.ts +36 -0
- package/src/markdown/api-rail.ts +259 -0
- package/src/markdown/code-title.ts +35 -9
- package/src/markdown/features.ts +3 -2
- package/src/markdown/fence-meta.ts +2 -0
- package/src/markdown/include.ts +11 -1
- package/src/markdown/index.ts +28 -5
- package/src/markdown/math.ts +4 -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 +7 -6
- package/src/openapi/model.ts +57 -24
- package/src/openapi/overlay.ts +256 -0
- package/src/openapi/parse.ts +55 -2
- package/src/openapi/references.ts +21 -5
- package/src/openapi/render-mdx.ts +5 -1
- 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 +20 -4
- package/src/registry/registry.ts +7 -0
- package/src/search/documents.ts +26 -2
- package/src/search/orama-index.ts +28 -3
- package/src/search/pagefind-ranking.ts +39 -0
- 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/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/prompts.ts +1 -0
- package/src/translate/validate.ts +8 -2
- package/src/upgrade/upgrade.ts +4 -171
- package/dist/cli/chunk-364znk6q.js +0 -75
- package/dist/cli/chunk-364znk6q.js.map +0 -11
- package/dist/cli/chunk-5m5nmvyq.js.map +0 -13
- package/dist/cli/chunk-9t7a85s3.js.map +0 -10
- package/dist/cli/chunk-c8chx29p.js.map +0 -36
- package/dist/cli/chunk-pehfxfta.js.map +0 -10
- package/dist/cli/chunk-pv29h0wf.js.map +0 -190
- package/dist/cli/chunk-q58y5e6a.js.map +0 -10
- package/dist/cli/chunk-wrr3j9w9.js.map +0 -35
- package/dist/cli/chunk-yfyb25rh.js.map +0 -14
- package/docs/content/sources.mdx +0 -334
- /package/dist/cli/{chunk-2eytanqx.js.map → chunk-1n228xqb.js.map} +0 -0
- /package/dist/cli/{chunk-35d4wj9f.js.map → chunk-2n4ps1b3.js.map} +0 -0
- /package/dist/cli/{chunk-e04dxsz1.js.map → chunk-4ejxbzee.js.map} +0 -0
- /package/dist/cli/{chunk-tzmab476.js.map → chunk-5ffywsmb.js.map} +0 -0
- /package/dist/cli/{chunk-2z928egk.js.map → chunk-5wj2qwn2.js.map} +0 -0
- /package/dist/cli/{chunk-b07cmahc.js.map → chunk-amrb6hgh.js.map} +0 -0
- /package/dist/cli/{chunk-akbpwfxc.js.map → chunk-cmtcnh9f.js.map} +0 -0
- /package/dist/cli/{chunk-crgn1q09.js.map → chunk-e7vvk5ya.js.map} +0 -0
- /package/dist/cli/{chunk-tkacnehg.js.map → chunk-gp84za0h.js.map} +0 -0
- /package/dist/cli/{chunk-r9rcc4w7.js.map → chunk-p804f3pe.js.map} +0 -0
- /package/dist/cli/{chunk-8ktnccpt.js.map → chunk-ta27feev.js.map} +0 -0
- /package/dist/cli/{chunk-a58773jm.js.map → chunk-y4ykbark.js.map} +0 -0
- /package/dist/cli/{chunk-p3v96n38.js.map → chunk-y5h4r2y0.js.map} +0 -0
- /package/dist/cli/{chunk-acanzt5p.js.map → chunk-yj17vs39.js.map} +0 -0
package/src/openapi/model.ts
CHANGED
|
@@ -46,6 +46,9 @@ export type HttpMethod = (typeof HTTP_METHODS)[number];
|
|
|
46
46
|
/** Group used for operations that declare no tag. */
|
|
47
47
|
const UNTAGGED = "Operations";
|
|
48
48
|
|
|
49
|
+
/** Group used for webhooks that declare no tag. */
|
|
50
|
+
const UNTAGGED_WEBHOOKS = "Webhooks";
|
|
51
|
+
|
|
49
52
|
/** A lower-case letter or digit followed by a capital: `addPet`, `v2List`. */
|
|
50
53
|
const CASE_BOUNDARY = /(?<before>[\p{Ll}\p{N}])(?<capital>\p{Lu})/gu;
|
|
51
54
|
/** The last capital of an acronym before a capitalized word: `HTTPResponse`. */
|
|
@@ -101,6 +104,12 @@ export interface ApiOperationRef {
|
|
|
101
104
|
deprecated: boolean;
|
|
102
105
|
/** The channel the operation acts on (AsyncAPI only). */
|
|
103
106
|
channelId?: string;
|
|
107
|
+
/**
|
|
108
|
+
* An OpenAPI 3.1 webhook: a request the API sends to the reader's endpoint
|
|
109
|
+
* rather than one it serves. `path` then holds the webhook's name, the key
|
|
110
|
+
* it has under the document's `webhooks`.
|
|
111
|
+
*/
|
|
112
|
+
webhook?: true;
|
|
104
113
|
}
|
|
105
114
|
|
|
106
115
|
/**
|
|
@@ -134,8 +143,8 @@ export interface ApiSpecData {
|
|
|
134
143
|
/** Operations keyed by {@link ApiOperationRef.key}. */
|
|
135
144
|
operations: Record<string, ApiOperationRef>;
|
|
136
145
|
tags: ApiTagRef[];
|
|
137
|
-
/** Code-sample languages to render per operation. */
|
|
138
|
-
codeSamples: string[];
|
|
146
|
+
/** Code-sample languages to render per operation; `false` for none. */
|
|
147
|
+
codeSamples: string[] | false;
|
|
139
148
|
/** Whether nested schema rows start expanded. */
|
|
140
149
|
expandSchemas: boolean;
|
|
141
150
|
/**
|
|
@@ -301,6 +310,11 @@ export const tagSlugger = (): ((name: string) => string) => {
|
|
|
301
310
|
};
|
|
302
311
|
};
|
|
303
312
|
|
|
313
|
+
/** One entry of a document's `paths` or `webhooks` map. */
|
|
314
|
+
type PathItemEntry =
|
|
315
|
+
| NonNullable<ApiDocument["paths"]>[string]
|
|
316
|
+
| NonNullable<ApiDocument["webhooks"]>[string];
|
|
317
|
+
|
|
304
318
|
/** An operation before the collector assigns its unique key and route. */
|
|
305
319
|
type CollectedOperation = Omit<ApiOperationRef, "route" | "tagSlug">;
|
|
306
320
|
|
|
@@ -386,10 +400,10 @@ export const operationCollector = (
|
|
|
386
400
|
|
|
387
401
|
/**
|
|
388
402
|
* Flatten a 3.1 document into a route-mapped operation list and its ordered
|
|
389
|
-
* tags. Operations inherit the first tag they declare; keys
|
|
390
|
-
* a repeated `operationId` still yields distinct routes.
|
|
391
|
-
* anything skipped (a `$ref` path item
|
|
392
|
-
* aren't silent.
|
|
403
|
+
* tags, webhooks included. Operations inherit the first tag they declare; keys
|
|
404
|
+
* are de-duplicated so a repeated `operationId` still yields distinct routes.
|
|
405
|
+
* `warnings` reports anything skipped (a `$ref` path item), so missing
|
|
406
|
+
* operations aren't silent.
|
|
393
407
|
*/
|
|
394
408
|
export const extractOperations = (
|
|
395
409
|
document: ApiDocument,
|
|
@@ -404,16 +418,24 @@ export const extractOperations = (
|
|
|
404
418
|
);
|
|
405
419
|
const collector = operationCollector(baseRoute, tagMeta);
|
|
406
420
|
|
|
407
|
-
|
|
421
|
+
// A path item's operations, filed under `name`: a path for `paths`, the
|
|
422
|
+
// webhook's key for `webhooks`.
|
|
423
|
+
const addPathItem = (
|
|
424
|
+
name: string,
|
|
425
|
+
item: PathItemEntry,
|
|
426
|
+
webhook: boolean
|
|
427
|
+
): void => {
|
|
408
428
|
// A parsed spec can carry a null path item despite the type; skip it.
|
|
409
429
|
if (!item) {
|
|
410
|
-
|
|
430
|
+
return;
|
|
411
431
|
}
|
|
412
432
|
if ("$ref" in item) {
|
|
413
433
|
warnings.push(
|
|
414
|
-
|
|
434
|
+
webhook
|
|
435
|
+
? `Webhook "${name}" is a $ref to a shared path item; referenced path items are not resolved, so it is missing from the reference. Inline the path item under "webhooks" to render it.`
|
|
436
|
+
: `Path "${name}" is a $ref to a shared path item; referenced path items are not resolved, so its operations are missing from the reference. Inline the path item under "paths" to render it.`
|
|
415
437
|
);
|
|
416
|
-
|
|
438
|
+
return;
|
|
417
439
|
}
|
|
418
440
|
for (const method of HTTP_METHODS) {
|
|
419
441
|
const operation = item[method];
|
|
@@ -421,26 +443,35 @@ export const extractOperations = (
|
|
|
421
443
|
continue;
|
|
422
444
|
}
|
|
423
445
|
const operationId = specText(operation.operationId);
|
|
424
|
-
|
|
446
|
+
const entry: CollectedOperation = {
|
|
425
447
|
deprecated: operation.deprecated ?? false,
|
|
426
448
|
description: specText(operation.description) ?? "",
|
|
427
|
-
|
|
449
|
+
// A webhook without an operationId is keyed by its name, which is
|
|
450
|
+
// already an identifier (`newPet` -> `new-pet`).
|
|
451
|
+
key: operationKey(
|
|
452
|
+
method,
|
|
453
|
+
name,
|
|
454
|
+
operationId ?? (webhook ? name : undefined)
|
|
455
|
+
),
|
|
428
456
|
method,
|
|
429
457
|
operationId,
|
|
430
|
-
path,
|
|
458
|
+
path: name,
|
|
431
459
|
summary: specText(operation.summary) ?? "",
|
|
432
|
-
tag:
|
|
433
|
-
|
|
460
|
+
tag:
|
|
461
|
+
specText(operation.tags?.[0]) ??
|
|
462
|
+
(webhook ? UNTAGGED_WEBHOOKS : UNTAGGED),
|
|
463
|
+
};
|
|
464
|
+
collector.add(webhook ? { ...entry, webhook: true } : entry);
|
|
434
465
|
}
|
|
435
|
-
}
|
|
466
|
+
};
|
|
436
467
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
);
|
|
468
|
+
for (const [path, item] of Object.entries(document.paths ?? {})) {
|
|
469
|
+
addPathItem(path, item, false);
|
|
470
|
+
}
|
|
471
|
+
// OpenAPI 3.1 webhooks: requests the API sends rather than serves. They
|
|
472
|
+
// follow the paths, so an untagged webhook group sits after the endpoints.
|
|
473
|
+
for (const [name, item] of Object.entries(document.webhooks ?? {})) {
|
|
474
|
+
addPathItem(name, item, true);
|
|
444
475
|
}
|
|
445
476
|
|
|
446
477
|
return { ...collector.finish(), warnings };
|
|
@@ -458,7 +489,9 @@ export const operationObject = (
|
|
|
458
489
|
// (AsyncAPI documents go to `asyncApiOperationObject`), so the spec's
|
|
459
490
|
// document is the OpenAPI shape.
|
|
460
491
|
const document = spec.document as ApiDocument;
|
|
461
|
-
const item =
|
|
492
|
+
const item = ref.webhook
|
|
493
|
+
? document.webhooks?.[ref.path]
|
|
494
|
+
: document.paths?.[ref.path];
|
|
462
495
|
const operation = method === undefined ? undefined : item?.[method];
|
|
463
496
|
return isOperation(operation) ? operation : undefined;
|
|
464
497
|
};
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { exec, query } from "jsonpath-rfc9535";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
import type { JsonValue } from "../core/adapter.ts";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* OpenAPI Overlays: the OpenAPI Initiative's format for changing an API
|
|
8
|
+
* description without editing it — an ordered list of actions, each an RFC
|
|
9
|
+
* 9535 JSONPath `target` with an `update` to merge in, a `copy` of another
|
|
10
|
+
* node to merge in, or `remove`. A spec generated by another tool or owned by
|
|
11
|
+
* another team can then carry docs-only changes (better descriptions, hidden
|
|
12
|
+
* internal operations, a public server URL) in a file of its own.
|
|
13
|
+
*
|
|
14
|
+
* Implements Overlay Specification 1.0 and 1.1 over `jsonpath-rfc9535`, which
|
|
15
|
+
* passes the JSONPath compliance suite in full, as the spec requires. The
|
|
16
|
+
* documents here are already parsed; `parse.ts` reads the files.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** An overlay document or action that doesn't follow the spec. */
|
|
20
|
+
export class OverlayError extends Error {
|
|
21
|
+
constructor(message: string) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = "OverlayError";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** An object node, the only kind that holds named properties. */
|
|
28
|
+
interface JsonObject {
|
|
29
|
+
[key: string]: JsonValue;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** A node that holds other nodes: where a match's parent can be. */
|
|
33
|
+
type JsonContainer = JsonObject | JsonValue[];
|
|
34
|
+
|
|
35
|
+
const isObject = (value: JsonValue | undefined): value is JsonObject =>
|
|
36
|
+
typeof value === "object" && value !== null && !Array.isArray(value);
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A parsed YAML or JSON mapping, as the document an overlay acts on: the
|
|
40
|
+
* parser's own types say nothing of JSON, but what it parses is JSON.
|
|
41
|
+
*/
|
|
42
|
+
export const isJsonObject = <Value>(
|
|
43
|
+
value: Value
|
|
44
|
+
): value is Value & JsonObject =>
|
|
45
|
+
typeof value === "object" && value !== null && !Array.isArray(value);
|
|
46
|
+
|
|
47
|
+
const isContainer = (value: JsonValue | undefined): value is JsonContainer =>
|
|
48
|
+
typeof value === "object" && value !== null;
|
|
49
|
+
|
|
50
|
+
const isIndex = (key: string | number | undefined): key is number =>
|
|
51
|
+
typeof key === "number";
|
|
52
|
+
|
|
53
|
+
/** An object, an array, or anything else: what update merging keys on. */
|
|
54
|
+
type MergeKind = "array" | "object" | "primitive";
|
|
55
|
+
|
|
56
|
+
const mergeKindOf = (value: JsonValue): MergeKind => {
|
|
57
|
+
if (Array.isArray(value)) {
|
|
58
|
+
return "array";
|
|
59
|
+
}
|
|
60
|
+
return isObject(value) ? "object" : "primitive";
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** A merge kind in a sentence. */
|
|
64
|
+
const MERGE_KIND_PHRASE: Record<MergeKind, string> = {
|
|
65
|
+
array: "an array",
|
|
66
|
+
object: "an object",
|
|
67
|
+
primitive: "a primitive value",
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const actionSchema = z.looseObject({
|
|
71
|
+
/** A JSONPath expression selecting one node to merge into the targets (1.1). */
|
|
72
|
+
copy: z.string().optional(),
|
|
73
|
+
description: z.string().optional(),
|
|
74
|
+
/** Remove every target from the map or array it's in. */
|
|
75
|
+
remove: z.boolean().default(false),
|
|
76
|
+
/** An RFC 9535 JSONPath expression selecting the nodes to act on. */
|
|
77
|
+
target: z.string().min(1),
|
|
78
|
+
/** A value merged into every target. */
|
|
79
|
+
update: z.json().optional(),
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
/** A validated overlay action. */
|
|
83
|
+
type OverlayAction = z.output<typeof actionSchema>;
|
|
84
|
+
|
|
85
|
+
const overlaySchema = z.looseObject({
|
|
86
|
+
actions: z.array(actionSchema).min(1),
|
|
87
|
+
extends: z.string().optional(),
|
|
88
|
+
info: z.looseObject({ title: z.string(), version: z.string() }),
|
|
89
|
+
overlay: z.string().regex(/^1\.[01]\.\d+$/u, {
|
|
90
|
+
message: "must be an Overlay Specification version, 1.0.x or 1.1.x",
|
|
91
|
+
}),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
/** A validated overlay document. */
|
|
95
|
+
export type OverlayDocument = z.output<typeof overlaySchema>;
|
|
96
|
+
|
|
97
|
+
/** Validate a parsed overlay document, naming the file in any error. */
|
|
98
|
+
export const overlayDocument = <Value>(
|
|
99
|
+
value: Value,
|
|
100
|
+
name: string
|
|
101
|
+
): OverlayDocument => {
|
|
102
|
+
const parsed = overlaySchema.safeParse(value);
|
|
103
|
+
if (parsed.success) {
|
|
104
|
+
return parsed.data;
|
|
105
|
+
}
|
|
106
|
+
const [issue] = parsed.error.issues;
|
|
107
|
+
const at = issue?.path.length ? `\`${issue.path.join(".")}\` ` : "";
|
|
108
|
+
throw new OverlayError(
|
|
109
|
+
`${name} is not an overlay document: ${at}${issue?.message ?? "invalid"}.`
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
/** A matched node, where it sits, and how to reach it. */
|
|
114
|
+
interface Match {
|
|
115
|
+
key?: string | number;
|
|
116
|
+
parent?: JsonContainer;
|
|
117
|
+
value: JsonValue;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** The nodes `expression` selects, each with its parent (none for the root). */
|
|
121
|
+
const select = (document: JsonValue, expression: string): Match[] => {
|
|
122
|
+
const matches: Match[] = [];
|
|
123
|
+
exec(document, expression, (value, path) => {
|
|
124
|
+
let parent: JsonValue = document;
|
|
125
|
+
for (const key of path.slice(0, -1)) {
|
|
126
|
+
// SAFETY: the path is one the query walked, so every step is a key of
|
|
127
|
+
// the container before it.
|
|
128
|
+
parent = (parent as Record<string | number, JsonValue>)[key] ?? null;
|
|
129
|
+
}
|
|
130
|
+
const key = path.at(-1);
|
|
131
|
+
matches.push(
|
|
132
|
+
key === undefined || !isContainer(parent)
|
|
133
|
+
? { value }
|
|
134
|
+
: { key, parent, value }
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
return matches;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Merge `patch` into `target` as the spec says: objects recursively, arrays
|
|
142
|
+
* by concatenation, a primitive replaced by a primitive. Returns the merged
|
|
143
|
+
* value; any other pairing throws.
|
|
144
|
+
*/
|
|
145
|
+
const merge = (target: JsonValue, patch: JsonValue, at: string): JsonValue => {
|
|
146
|
+
if (isObject(target) && isObject(patch)) {
|
|
147
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
148
|
+
// Only recurse into the target's own keys, so a `__proto__` or
|
|
149
|
+
// `constructor` key can't reach a prototype.
|
|
150
|
+
target[key] = Object.hasOwn(target, key)
|
|
151
|
+
? merge(target[key] ?? null, value, `${at}.${key}`)
|
|
152
|
+
: structuredClone(value);
|
|
153
|
+
}
|
|
154
|
+
return target;
|
|
155
|
+
}
|
|
156
|
+
if (Array.isArray(target) && Array.isArray(patch)) {
|
|
157
|
+
target.push(...structuredClone(patch));
|
|
158
|
+
return target;
|
|
159
|
+
}
|
|
160
|
+
if (
|
|
161
|
+
mergeKindOf(target) === "primitive" &&
|
|
162
|
+
mergeKindOf(patch) === "primitive"
|
|
163
|
+
) {
|
|
164
|
+
return patch;
|
|
165
|
+
}
|
|
166
|
+
throw new OverlayError(
|
|
167
|
+
`can't merge ${MERGE_KIND_PHRASE[mergeKindOf(patch)]} into ${MERGE_KIND_PHRASE[mergeKindOf(target)]} at ${at}`
|
|
168
|
+
);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/** Remove each match from its container, later siblings first. */
|
|
172
|
+
const removeAll = (matches: Match[]): void => {
|
|
173
|
+
for (const { key, parent } of matches.toReversed()) {
|
|
174
|
+
if (Array.isArray(parent) && isIndex(key)) {
|
|
175
|
+
parent.splice(key, 1);
|
|
176
|
+
} else if (isObject(parent) && key !== undefined && !isIndex(key)) {
|
|
177
|
+
// oxlint-disable-next-line typescript/no-dynamic-delete -- an overlay target's key is only known at run time
|
|
178
|
+
delete parent[key];
|
|
179
|
+
} else {
|
|
180
|
+
throw new OverlayError("can't remove the document itself");
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Merge `patch` into every match, which must all be objects, all arrays, or
|
|
187
|
+
* all primitives. A primitive match is replaced in its parent.
|
|
188
|
+
*/
|
|
189
|
+
const mergeAll = (matches: Match[], patch: JsonValue, target: string) => {
|
|
190
|
+
const kinds = new Set(matches.map((match) => mergeKindOf(match.value)));
|
|
191
|
+
if (kinds.size > 1) {
|
|
192
|
+
throw new OverlayError(
|
|
193
|
+
`its target selects a mix of ${[...kinds].toSorted().join(", ")} values, and an update needs all one kind`
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
for (const { key, parent, value } of matches) {
|
|
197
|
+
const merged = merge(value, patch, target);
|
|
198
|
+
if (merged !== value && parent !== undefined && key !== undefined) {
|
|
199
|
+
// SAFETY: `key` came from walking `parent`, so it indexes it.
|
|
200
|
+
(parent as Record<string | number, JsonValue>)[key] = merged;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
/** The single node a `copy` expression selects, cloned. */
|
|
206
|
+
const copied = (document: JsonValue, expression: string): JsonValue => {
|
|
207
|
+
const values = query(document, expression);
|
|
208
|
+
const [value] = values;
|
|
209
|
+
if (values.length !== 1 || value === undefined) {
|
|
210
|
+
throw new OverlayError(
|
|
211
|
+
`its copy expression "${expression}" selects ${values.length} nodes, and copy needs exactly one`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
return structuredClone(value);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const applyAction = (document: JsonValue, action: OverlayAction): void => {
|
|
218
|
+
const matches = select(document, action.target);
|
|
219
|
+
if (matches.length === 0) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (action.remove) {
|
|
223
|
+
removeAll(matches);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const patch =
|
|
227
|
+
action.update ??
|
|
228
|
+
(action.copy === undefined ? undefined : copied(document, action.copy));
|
|
229
|
+
if (patch !== undefined) {
|
|
230
|
+
mergeAll(matches, patch, action.target);
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Apply an overlay to a parsed document, in place and in action order: each
|
|
236
|
+
* action sees the result of the one before. A target that matches nothing
|
|
237
|
+
* changes nothing. Throws an {@link OverlayError} naming the overlay file and
|
|
238
|
+
* the action that failed.
|
|
239
|
+
*/
|
|
240
|
+
export const applyOverlay = (
|
|
241
|
+
document: JsonValue,
|
|
242
|
+
overlay: OverlayDocument,
|
|
243
|
+
name: string
|
|
244
|
+
): void => {
|
|
245
|
+
for (const [index, action] of overlay.actions.entries()) {
|
|
246
|
+
try {
|
|
247
|
+
applyAction(document, action);
|
|
248
|
+
} catch (error) {
|
|
249
|
+
// SAFETY: the JSONPath parser and this module throw Error instances.
|
|
250
|
+
const { message } = error as Error;
|
|
251
|
+
throw new OverlayError(
|
|
252
|
+
`${name} action ${index + 1} (${action.target}): ${message}`
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
};
|
package/src/openapi/parse.ts
CHANGED
|
@@ -18,6 +18,7 @@ import { normalizeAsyncApiDocument } from "./asyncapi.ts";
|
|
|
18
18
|
import { buildGraphqlDocument } from "./graphql-build.ts";
|
|
19
19
|
import type { GraphqlDocument } from "./graphql.ts";
|
|
20
20
|
import type { ApiDocument } from "./model.ts";
|
|
21
|
+
import { applyOverlay, isJsonObject, overlayDocument } from "./overlay.ts";
|
|
21
22
|
import { SpecDependencyError } from "./spec-dependency-error.ts";
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -75,6 +76,11 @@ export class InvalidSpecError extends Error {
|
|
|
75
76
|
|
|
76
77
|
/** Where and whether to cache a remote spec's text between runs. */
|
|
77
78
|
export interface SpecFetchOptions {
|
|
79
|
+
/**
|
|
80
|
+
* Overlay documents (local paths or `http(s)` URLs) applied in order to
|
|
81
|
+
* the parsed spec, before it's upgraded. See `overlay.ts`.
|
|
82
|
+
*/
|
|
83
|
+
overlays?: readonly string[];
|
|
78
84
|
/** Dir for a last-good on-disk copy of a remote spec (offline fallback). */
|
|
79
85
|
cacheDir?: string;
|
|
80
86
|
/**
|
|
@@ -310,13 +316,60 @@ const readSpecText = async (
|
|
|
310
316
|
const isApiDocument = <Value>(value: Value): value is Value & ApiDocument =>
|
|
311
317
|
typeof value === "object" && value !== null;
|
|
312
318
|
|
|
319
|
+
/**
|
|
320
|
+
* Read a spec and apply its overlays, in order, to the parsed document as
|
|
321
|
+
* written (before any upgrade: an overlay targets its spec's own version).
|
|
322
|
+
* The overlays are read like the spec, local or remote, cached alike.
|
|
323
|
+
*/
|
|
324
|
+
const readOverlaid = async (
|
|
325
|
+
spec: string,
|
|
326
|
+
root: string,
|
|
327
|
+
options: SpecFetchOptions
|
|
328
|
+
): Promise<{ document: ReturnType<typeof normalize>; warnings: string[] }> => {
|
|
329
|
+
const [read, ...overlays] = await Promise.all(
|
|
330
|
+
[spec, ...(options.overlays ?? [])].map((path) =>
|
|
331
|
+
readSpecText(path, root, options)
|
|
332
|
+
)
|
|
333
|
+
);
|
|
334
|
+
const document = normalize(read?.text ?? "");
|
|
335
|
+
for (const [index, overlay] of overlays.entries()) {
|
|
336
|
+
const name = options.overlays?.[index] ?? "";
|
|
337
|
+
const parsed = overlayDocument(normalize(overlay.text), name);
|
|
338
|
+
// A spec that isn't a mapping has nothing to overlay; the caller
|
|
339
|
+
// rejects it as it would without overlays.
|
|
340
|
+
if (isJsonObject(document)) {
|
|
341
|
+
applyOverlay(document, parsed, name);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return {
|
|
345
|
+
document,
|
|
346
|
+
warnings: [read, ...overlays].flatMap((entry) => entry?.warnings ?? []),
|
|
347
|
+
};
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* A spec with its overlays applied, as JSON text: what the Scalar embed
|
|
352
|
+
* inlines, since it can't apply overlays itself.
|
|
353
|
+
*/
|
|
354
|
+
export const readOverlaidSpec = async (
|
|
355
|
+
spec: string,
|
|
356
|
+
root: string,
|
|
357
|
+
options: SpecFetchOptions
|
|
358
|
+
): Promise<{ text: string; warnings: string[] }> => {
|
|
359
|
+
const { document, warnings } = await readOverlaid(spec, root, options);
|
|
360
|
+
return { text: JSON.stringify(document), warnings };
|
|
361
|
+
};
|
|
362
|
+
|
|
313
363
|
export const parseSpec = async (
|
|
314
364
|
spec: string,
|
|
315
365
|
root: string,
|
|
316
366
|
options: SpecFetchOptions = {}
|
|
317
367
|
): Promise<ParsedSpec> => {
|
|
318
|
-
const {
|
|
319
|
-
|
|
368
|
+
const { document: normalized, warnings } = await readOverlaid(
|
|
369
|
+
spec,
|
|
370
|
+
root,
|
|
371
|
+
options
|
|
372
|
+
);
|
|
320
373
|
const { specification } = upgrade(normalized);
|
|
321
374
|
// Reject a non-mapping here so the renderer never sees a non-document.
|
|
322
375
|
if (!isApiDocument(specification)) {
|
|
@@ -28,8 +28,8 @@ export type BlumeReferenceKind = Exclude<ReferenceKind, "scalar">;
|
|
|
28
28
|
|
|
29
29
|
/** Per-adapter display options for the Blume renderer. */
|
|
30
30
|
export interface ReferenceDisplay {
|
|
31
|
-
/** Code-sample languages shown per operation. */
|
|
32
|
-
codeSamples: string[];
|
|
31
|
+
/** Code-sample languages shown per operation; `false` for none. */
|
|
32
|
+
codeSamples: string[] | false;
|
|
33
33
|
/** Whether nested schema rows start expanded. */
|
|
34
34
|
expandSchemas: boolean;
|
|
35
35
|
/**
|
|
@@ -67,6 +67,11 @@ export interface ReferenceSource {
|
|
|
67
67
|
seoDescriptionSuffix: boolean;
|
|
68
68
|
/** Local path or `http(s)` URL, verbatim from config. */
|
|
69
69
|
spec: string;
|
|
70
|
+
/**
|
|
71
|
+
* OpenAPI Overlay documents applied to `spec` in order (`openapi()` and
|
|
72
|
+
* `scalar()` only).
|
|
73
|
+
*/
|
|
74
|
+
overlays?: string[];
|
|
70
75
|
/**
|
|
71
76
|
* URL of the live GraphQL endpoint the playground and code samples target
|
|
72
77
|
* (GraphQL only — a schema, unlike an OpenAPI document, names no server).
|
|
@@ -184,6 +189,7 @@ interface SourceRow {
|
|
|
184
189
|
route?: string;
|
|
185
190
|
seoDescriptionSuffix: boolean;
|
|
186
191
|
spec: string;
|
|
192
|
+
overlays?: string[];
|
|
187
193
|
}
|
|
188
194
|
|
|
189
195
|
/**
|
|
@@ -255,6 +261,9 @@ const referencesFor = (
|
|
|
255
261
|
if (source.endpoint !== undefined) {
|
|
256
262
|
reference.endpoint = source.endpoint;
|
|
257
263
|
}
|
|
264
|
+
if (source.overlays !== undefined) {
|
|
265
|
+
reference.overlays = source.overlays;
|
|
266
|
+
}
|
|
258
267
|
return reference;
|
|
259
268
|
});
|
|
260
269
|
};
|
|
@@ -363,11 +372,18 @@ export const builtinProxyReferences = (
|
|
|
363
372
|
ref.display.playground.proxy === true
|
|
364
373
|
);
|
|
365
374
|
|
|
375
|
+
/**
|
|
376
|
+
* Whether hand-written endpoint pages (`api` frontmatter) send their Try it
|
|
377
|
+
* requests through the built-in proxy: `api.playground.proxy: true`.
|
|
378
|
+
*/
|
|
379
|
+
export const apiPagesUseBuiltinProxy = (config: ResolvedConfig): boolean =>
|
|
380
|
+
config.api.playground.enabled && config.api.playground.proxy === true;
|
|
381
|
+
|
|
366
382
|
/**
|
|
367
383
|
* Whether the built-in playground CORS proxy endpoint (`/_api-proxy`) must be
|
|
368
384
|
* generated: some Blume-rendered reference's playground opted into it with
|
|
369
|
-
* `proxy: true
|
|
370
|
-
* two can never disagree.
|
|
385
|
+
* `proxy: true`, or the hand-written endpoint pages' did. Shared by the server
|
|
386
|
+
* feature gate and the generator so the two can never disagree.
|
|
371
387
|
*/
|
|
372
388
|
export const needsPlaygroundProxy = (config: ResolvedConfig): boolean =>
|
|
373
|
-
builtinProxyReferences(config).length > 0;
|
|
389
|
+
builtinProxyReferences(config).length > 0 || apiPagesUseBuiltinProxy(config);
|
|
@@ -270,7 +270,8 @@ const operationDescription = (
|
|
|
270
270
|
// method (see `extractGraphqlOperations`).
|
|
271
271
|
subject = `${operation.path} ${GRAPHQL_MEMBER_PHRASES[operation.method as GraphqlMember]}`;
|
|
272
272
|
} else {
|
|
273
|
-
|
|
273
|
+
// A webhook is a request the API sends, named rather than pathed.
|
|
274
|
+
subject = `${operation.method.toUpperCase()} ${operation.path} ${operation.webhook ? "webhook" : "endpoint"}`;
|
|
274
275
|
}
|
|
275
276
|
const suffix = `Reference for the ${subject} in the ${apiPhrase(spec)}.`;
|
|
276
277
|
// Room for the joining space and the period `asSentence` may add, so a
|
|
@@ -348,6 +349,9 @@ export const operationMdx = (
|
|
|
348
349
|
sidebar.badge = method;
|
|
349
350
|
}
|
|
350
351
|
const tags = graphql ? [operation.tag] : [operation.tag, method];
|
|
352
|
+
if (operation.webhook) {
|
|
353
|
+
tags.push("Webhook");
|
|
354
|
+
}
|
|
351
355
|
return {
|
|
352
356
|
body: withDescription(
|
|
353
357
|
description,
|
package/src/openapi/scalar.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { scalarReferenceTemplate } from "../astro/templates.ts";
|
|
|
6
6
|
import type { ResolvedConfig } from "../core/schema.ts";
|
|
7
7
|
import { trimChar } from "../core/trim.ts";
|
|
8
8
|
import { resolveAccent, resolveRadius } from "../theme/palette.ts";
|
|
9
|
+
import { readOverlaidSpec } from "./parse.ts";
|
|
9
10
|
import { resolveReferences } from "./references.ts";
|
|
10
11
|
import type { ReferenceSource } from "./references.ts";
|
|
11
12
|
|
|
@@ -53,14 +54,44 @@ const themeConfiguration = (config: ResolvedConfig, override?: string) => {
|
|
|
53
54
|
};
|
|
54
55
|
};
|
|
55
56
|
|
|
57
|
+
/** A source's Scalar spec config, or none when its overlays failed. */
|
|
58
|
+
interface SpecConfiguration {
|
|
59
|
+
config?: { content: string } | { url: string };
|
|
60
|
+
warning?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The spec with its overlays applied, inlined: Scalar can't apply overlays
|
|
65
|
+
* itself. A failed overlay skips the page rather than embed the spec without
|
|
66
|
+
* it, since an overlay is often what hides the internal operations.
|
|
67
|
+
*/
|
|
68
|
+
const overlaidConfiguration = async (
|
|
69
|
+
ref: ReferenceSource,
|
|
70
|
+
root: string
|
|
71
|
+
): Promise<SpecConfiguration> => {
|
|
72
|
+
try {
|
|
73
|
+
const { text, warnings } = await readOverlaidSpec(ref.spec, root, {
|
|
74
|
+
overlays: ref.overlays,
|
|
75
|
+
});
|
|
76
|
+
return { config: { content: text }, warning: warnings[0] };
|
|
77
|
+
} catch (error) {
|
|
78
|
+
// SAFETY: reading, parsing, and overlaying throw Error instances.
|
|
79
|
+
const { message } = error as Error;
|
|
80
|
+
return {
|
|
81
|
+
warning: `API reference ${ref.route} was skipped: its overlays couldn't be applied to "${ref.spec}" (${message}).`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
56
86
|
/** Build the Scalar spec config for a source: inline `content` or remote `url`. */
|
|
57
87
|
const specConfiguration = async (
|
|
58
|
-
|
|
88
|
+
ref: ReferenceSource,
|
|
59
89
|
root: string
|
|
60
|
-
): Promise<{
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
90
|
+
): Promise<SpecConfiguration> => {
|
|
91
|
+
const { spec } = ref;
|
|
92
|
+
if (ref.overlays?.length) {
|
|
93
|
+
return await overlaidConfiguration(ref, root);
|
|
94
|
+
}
|
|
64
95
|
if (URL_SPEC.test(spec)) {
|
|
65
96
|
return { config: { url: spec } };
|
|
66
97
|
}
|
|
@@ -133,7 +164,7 @@ export const buildReferenceFiles = async (options: {
|
|
|
133
164
|
const built = await Promise.all(
|
|
134
165
|
accepted.map(async (ref) => ({
|
|
135
166
|
ref,
|
|
136
|
-
spec: await specConfiguration(ref
|
|
167
|
+
spec: await specConfiguration(ref, root),
|
|
137
168
|
}))
|
|
138
169
|
);
|
|
139
170
|
|
|
@@ -142,6 +173,9 @@ export const buildReferenceFiles = async (options: {
|
|
|
142
173
|
if (spec.warning) {
|
|
143
174
|
warnings.push(spec.warning);
|
|
144
175
|
}
|
|
176
|
+
if (!spec.config) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
145
179
|
const pagePath = referencePagePath(ref.route);
|
|
146
180
|
// `theme` is the one option Blume maps (a named theme replaces the
|
|
147
181
|
// accent/radius layering); every other `scalar()` option is forwarded
|
package/src/openapi/source.ts
CHANGED
|
@@ -250,7 +250,7 @@ const parseReference = async (
|
|
|
250
250
|
const { document, warnings } = await parseSpec(
|
|
251
251
|
reference.spec,
|
|
252
252
|
ctx.projectRoot,
|
|
253
|
-
options
|
|
253
|
+
{ ...options, overlays: reference.overlays }
|
|
254
254
|
);
|
|
255
255
|
const extracted = extractOperations(document, reference.route);
|
|
256
256
|
return {
|