blume 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +110 -0
- package/dist/cli/{chunk-ga0pf4aj.js → chunk-0n9xkp3d.js} +12 -3
- package/dist/cli/{chunk-ga0pf4aj.js.map → chunk-0n9xkp3d.js.map} +3 -3
- package/dist/cli/{chunk-43zcf4gz.js → chunk-0pmb4tr4.js} +541 -368
- package/dist/cli/chunk-0pmb4tr4.js.map +35 -0
- package/dist/cli/{chunk-9geymqns.js → chunk-0w6hs479.js} +15 -15
- package/dist/cli/{chunk-xkz5xbtv.js → chunk-124fvmzq.js} +2802 -1422
- package/dist/cli/chunk-124fvmzq.js.map +220 -0
- package/dist/cli/chunk-150jk7km.js +52 -0
- package/dist/cli/chunk-150jk7km.js.map +10 -0
- package/dist/cli/{chunk-y4ykbark.js → chunk-1f882900.js} +2 -2
- package/dist/cli/{chunk-5ffywsmb.js → chunk-297ajdcv.js} +8 -6
- package/dist/cli/{chunk-5ffywsmb.js.map → chunk-297ajdcv.js.map} +3 -3
- package/dist/cli/{chunk-h2a48yhd.js → chunk-32khwcb6.js} +120 -16
- package/dist/cli/{chunk-h2a48yhd.js.map → chunk-32khwcb6.js.map} +10 -10
- package/dist/cli/{chunk-5cg2rzgf.js → chunk-36bxs9c7.js} +20 -49
- package/dist/cli/{chunk-5cg2rzgf.js.map → chunk-36bxs9c7.js.map} +4 -5
- package/dist/cli/{chunk-py62fewh.js → chunk-53jqw8z0.js} +3 -3
- package/dist/cli/{chunk-5wj2qwn2.js → chunk-58bt3hag.js} +4 -4
- package/dist/cli/{chunk-e7vvk5ya.js → chunk-5cmts6dv.js} +3 -3
- package/dist/cli/{chunk-y5h4r2y0.js → chunk-60hz265r.js} +15 -5
- package/dist/cli/chunk-60hz265r.js.map +10 -0
- package/dist/cli/chunk-74wfhv79.js +65 -0
- package/dist/cli/chunk-74wfhv79.js.map +10 -0
- package/dist/cli/{chunk-g2z9x01v.js → chunk-9qzgg6mj.js} +3 -3
- package/dist/cli/{chunk-q3wxxq39.js → chunk-a57yh0h9.js} +5 -5
- package/dist/cli/{chunk-713mngdq.js → chunk-ave1wcbr.js} +76 -10
- package/dist/cli/chunk-ave1wcbr.js.map +14 -0
- package/dist/cli/{chunk-2n4ps1b3.js → chunk-bqxaqn5p.js} +40 -22
- package/dist/cli/chunk-bqxaqn5p.js.map +15 -0
- package/dist/cli/{chunk-jnwj5kjj.js → chunk-bxwhtvw8.js} +20 -7
- package/dist/cli/{chunk-jnwj5kjj.js.map → chunk-bxwhtvw8.js.map} +3 -3
- package/dist/cli/chunk-ckrcm08h.js +324 -0
- package/dist/cli/chunk-ckrcm08h.js.map +14 -0
- package/dist/cli/{chunk-2f3ndkpw.js → chunk-desvm72z.js} +3 -3
- package/dist/cli/{chunk-gp84za0h.js → chunk-dhwtqnnd.js} +2 -2
- package/dist/cli/{chunk-4h8z9vv5.js → chunk-dpy8qdjf.js} +93 -47
- package/dist/cli/chunk-dpy8qdjf.js.map +17 -0
- package/dist/cli/{chunk-2f7n4qzt.js → chunk-dqhg54mr.js} +1 -1
- package/dist/cli/{chunk-0gz36akr.js → chunk-dv2z0rzf.js} +21 -5
- package/dist/cli/chunk-dv2z0rzf.js.map +12 -0
- package/dist/cli/{chunk-cm89scdv.js → chunk-es8h0v3j.js} +9 -11
- package/dist/cli/{chunk-cm89scdv.js.map → chunk-es8h0v3j.js.map} +3 -3
- package/dist/cli/{chunk-zhvd6adm.js → chunk-gfc5fqvf.js} +14 -14
- package/dist/cli/{chunk-nd1821sk.js → chunk-gxtg6ed2.js} +33 -19
- package/dist/cli/chunk-gxtg6ed2.js.map +10 -0
- package/dist/cli/{chunk-p804f3pe.js → chunk-hvcxng5f.js} +6 -6
- package/dist/cli/{chunk-ta27feev.js → chunk-j1x92mx9.js} +21 -2
- package/dist/cli/chunk-j1x92mx9.js.map +10 -0
- package/dist/cli/{chunk-rekqz4qg.js → chunk-k3gr1nm9.js} +19 -16
- package/dist/cli/chunk-k3gr1nm9.js.map +10 -0
- package/dist/cli/{chunk-1n228xqb.js → chunk-kdpzw38a.js} +2 -2
- package/dist/cli/{chunk-cy5bm0j0.js → chunk-ms9z66nz.js} +2 -2
- package/dist/cli/{chunk-cy5bm0j0.js.map → chunk-ms9z66nz.js.map} +2 -2
- package/dist/cli/{chunk-zf748avv.js → chunk-nbyvgnnm.js} +193 -286
- package/dist/cli/chunk-nbyvgnnm.js.map +36 -0
- package/dist/cli/{chunk-6bm0xpgd.js → chunk-s0bdnc7v.js} +2 -2
- package/dist/cli/{chunk-yxmk4pec.js → chunk-sc93apfs.js} +17 -16
- package/dist/cli/{chunk-yxmk4pec.js.map → chunk-sc93apfs.js.map} +3 -3
- package/dist/cli/{chunk-cmtcnh9f.js → chunk-tfycddbt.js} +2 -2
- package/dist/cli/{chunk-9xr4dgee.js → chunk-twy7psdz.js} +29 -16
- package/dist/cli/chunk-twy7psdz.js.map +10 -0
- package/dist/cli/{chunk-ntv4qrt7.js → chunk-y4msrwm9.js} +15 -15
- package/dist/cli/{chunk-yj17vs39.js → chunk-ydp3065p.js} +5 -5
- package/dist/cli/index.js +24 -19
- package/dist/cli/index.js.map +3 -3
- package/dist/types/ai/ask-context.d.ts +6 -2
- package/dist/types/ai/ask.d.ts +24 -5
- package/dist/types/ai/component-markdown.d.ts +2 -0
- package/dist/types/ai/openapi-components.d.ts +4 -1
- package/dist/types/analytics/clics.d.ts +37 -0
- package/dist/types/analytics/index.d.ts +2 -0
- package/dist/types/analytics/schema.d.ts +18 -0
- package/dist/types/astro/integration.d.ts +11 -4
- package/dist/types/astro/markdown-negotiation.d.ts +3 -0
- package/dist/types/astro/pages.d.ts +10 -0
- package/dist/types/cli/init/scaffold.d.ts +1 -1
- package/dist/types/components/content/api-field.d.ts +4 -1
- package/dist/types/components/content/api-page.d.ts +6 -1
- package/dist/types/components/openapi/code-samples.d.ts +29 -0
- package/dist/types/components/openapi/helpers.d.ts +22 -1
- package/dist/types/components/openapi/security.d.ts +10 -0
- package/dist/types/components/openapi/snippets.d.ts +60 -0
- package/dist/types/core/changelog-index.d.ts +8 -0
- package/dist/types/core/config-input.d.ts +37 -6
- package/dist/types/core/data.d.ts +4 -0
- package/dist/types/core/i18n-ui.d.ts +2 -0
- package/dist/types/core/links.d.ts +8 -0
- package/dist/types/core/powered-by.d.ts +12 -0
- package/dist/types/core/probe.d.ts +2 -0
- package/dist/types/core/project-graph.d.ts +6 -0
- package/dist/types/core/redirect-patterns.d.ts +11 -3
- package/dist/types/core/schema.d.ts +228 -3
- package/dist/types/core/sources/cache.d.ts +3 -0
- package/dist/types/core/sources/contentful-rich-text.d.ts +11 -2
- package/dist/types/core/sources/contentful.d.ts +17 -1
- package/dist/types/core/sources/lexical.d.ts +5 -0
- package/dist/types/core/sources/lower.d.ts +10 -2
- package/dist/types/core/sources/normalize.d.ts +20 -1
- package/dist/types/core/sources/notion.d.ts +8 -0
- package/dist/types/core/sources/remote.d.ts +37 -10
- package/dist/types/core/sources/sanity.d.ts +1 -1
- package/dist/types/core/types.d.ts +17 -0
- package/dist/types/deploy/adapter-output.d.ts +2 -1
- package/dist/types/deploy/adapters/cloudflare.d.ts +7 -5
- package/dist/types/deploy/adapters/node.d.ts +58 -23
- package/dist/types/deploy/adapters/registry.d.ts +10 -0
- package/dist/types/deploy/adapters/types.d.ts +3 -2
- package/dist/types/deploy/cloudflare-negotiation.d.ts +5 -2
- package/dist/types/deploy/node-headers.d.ts +12 -4
- package/dist/types/deploy/platforms/cloudflare.d.ts +10 -7
- package/dist/types/deploy/platforms/node.d.ts +3 -1
- package/dist/types/deploy/platforms/types.d.ts +18 -7
- package/dist/types/deploy/platforms/vercel.d.ts +21 -2
- package/dist/types/deploy/redirects.d.ts +17 -1
- package/dist/types/deploy/sitemap.d.ts +2 -1
- package/dist/types/deploy/vercel-negotiation.d.ts +44 -21
- package/dist/types/narration/build.d.ts +34 -12
- package/dist/types/narration/provider.d.ts +96 -8
- package/dist/types/openapi/checks.d.ts +27 -0
- package/dist/types/openapi/graphql.d.ts +6 -0
- package/dist/types/openapi/model.d.ts +15 -4
- package/dist/types/openapi/parse.d.ts +3 -0
- package/dist/types/openapi/references.d.ts +16 -0
- package/dist/types/openapi/sentence.d.ts +11 -1
- package/dist/types/ratelimit/index.d.ts +1 -1
- package/dist/types/ratelimit/schema.d.ts +8 -0
- package/dist/types/ratelimit/upstash.d.ts +39 -14
- package/dist/types/reference/graphql.d.ts +76 -1
- package/dist/types/reference/index.d.ts +1 -1
- package/dist/types/reference/openapi.d.ts +2 -2
- package/dist/types/reference/options.d.ts +37 -1
- package/dist/types/reference/schema.d.ts +84 -0
- package/dist/types/search/adapters/registry.d.ts +3 -0
- package/dist/types/search/adapters/typesense.d.ts +14 -4
- package/dist/types/search/build.d.ts +3 -1
- package/dist/types/search/orama-index.d.ts +9 -9
- package/dist/types/search/sync/algolia.d.ts +5 -2
- package/dist/types/search/sync/index.d.ts +3 -1
- package/dist/types/search/sync/typesense.d.ts +11 -7
- package/dist/types/seo/jsonld.d.ts +11 -6
- package/dist/types/sources/contentful.d.ts +16 -0
- package/dist/types/sources/github-releases.d.ts +8 -1
- package/dist/types/sources/notion.d.ts +8 -0
- package/dist/types/sources/payload.d.ts +2 -0
- package/dist/types/sources/registry.d.ts +63 -0
- package/dist/types/sources/shared.d.ts +3 -0
- package/dist/types/sources/strapi.d.ts +2 -0
- package/docs/01-quickstart.mdx +4 -2
- package/docs/02-deployment.mdx +59 -13
- package/docs/03-upgrading.mdx +2 -1
- package/docs/04-migrating.mdx +9 -9
- package/docs/08-faq.mdx +12 -12
- package/docs/advanced/changelog.mdx +30 -2
- package/docs/advanced/custom-pages.mdx +1 -1
- package/docs/advanced/skills.mdx +1 -1
- package/docs/cli/audit.mdx +30 -7
- package/docs/cli/doctor.mdx +6 -2
- package/docs/cli/evals.mdx +4 -4
- package/docs/cli/translate.mdx +1 -0
- package/docs/cli/validate.mdx +4 -3
- package/docs/configuration/analytics.mdx +19 -1
- package/docs/configuration/assistant.mdx +6 -2
- package/docs/configuration/customization.mdx +4 -2
- package/docs/configuration/export.mdx +5 -3
- package/docs/configuration/index.mdx +4 -2
- package/docs/configuration/narration.mdx +43 -6
- package/docs/configuration/rate-limiting.mdx +23 -2
- package/docs/configuration/search.mdx +37 -14
- package/docs/content/components.mdx +2 -2
- package/docs/content/frontmatter.mdx +5 -3
- package/docs/content/i18n.mdx +2 -2
- package/docs/content/includes.mdx +1 -1
- package/docs/content/navigation.mdx +8 -6
- package/docs/content/sources/contentful.mdx +9 -4
- package/docs/content/sources/custom.mdx +7 -3
- package/docs/content/sources/github-releases.mdx +12 -2
- package/docs/content/sources/index.mdx +3 -3
- package/docs/content/sources/notion.mdx +17 -2
- package/docs/content/sources/obsidian.mdx +3 -1
- package/docs/content/sources/payload.mdx +5 -2
- package/docs/content/sources/remote-mdx.mdx +6 -0
- package/docs/content/sources/sanity.mdx +4 -2
- package/docs/content/sources/strapi.mdx +5 -2
- package/docs/content/syntax.mdx +2 -2
- package/docs/discoverability/agent-discovery.mdx +1 -1
- package/docs/discoverability/index.mdx +1 -1
- package/docs/discoverability/json-api.mdx +2 -0
- package/docs/discoverability/llms-txt.mdx +1 -1
- package/docs/discoverability/markdown.mdx +3 -1
- package/docs/discoverability/mcp.mdx +6 -0
- package/docs/discoverability/sitemap-and-robots.mdx +1 -1
- package/docs/discoverability/structured-data.mdx +8 -6
- package/docs/references/api-pages.mdx +1 -1
- package/docs/references/asyncapi.mdx +5 -1
- package/docs/references/graphql.mdx +15 -1
- package/docs/references/openapi.mdx +64 -8
- package/package.json +9 -9
- package/skills/blume/SKILL.md +1 -1
- package/skills/blume-migrate/SKILL.md +2 -2
- package/skills/blume-migrate/assets/{oxfmt@0.67.0.patch → oxfmt@0.71.0.patch} +7 -7
- package/skills/blume-migrate/references/docusaurus.md +4 -4
- package/skills/blume-migrate/references/fumadocs.md +17 -13
- package/skills/blume-migrate/references/monorepo.md +5 -5
- package/skills/blume-migrate/references/nextra.md +8 -5
- package/skills/blume-migrate/references/starlight.md +9 -7
- package/src/ai/ask-context.ts +33 -3
- package/src/ai/ask-limits.ts +13 -0
- package/src/ai/ask.ts +24 -6
- package/src/ai/changelog-markdown.ts +11 -6
- package/src/ai/component-markdown.ts +1 -1
- package/src/ai/cors.ts +12 -3
- package/src/ai/mcp/query.ts +14 -6
- package/src/ai/openapi-components.ts +28 -4
- package/src/ai/relative-links.ts +4 -38
- package/src/analytics/clics.ts +58 -0
- package/src/analytics/head.ts +5 -0
- package/src/analytics/index.ts +2 -0
- package/src/analytics/schema.ts +2 -0
- package/src/astro/generate.ts +91 -56
- package/src/astro/integration.ts +72 -22
- package/src/astro/markdown-negotiation.ts +3 -0
- package/src/astro/module-types.ts +3 -1
- package/src/astro/pages.ts +23 -0
- package/src/astro/render-deps.ts +10 -5
- package/src/astro/runtime-deps.ts +23 -2
- package/src/astro/templates.ts +129 -81
- package/src/audit/catalog.ts +19 -3
- package/src/audit/checks/links.ts +14 -1
- package/src/audit/checks/network.ts +110 -6
- package/src/audit/checks/redirects.ts +6 -4
- package/src/audit/checks/robots.ts +58 -2
- package/src/audit/run.ts +3 -0
- package/src/audit/types.ts +2 -0
- package/src/audit/url.ts +14 -5
- package/src/blume-modules.d.ts +5 -1
- package/src/cli/args.ts +51 -0
- package/src/cli/build-failure.ts +23 -0
- package/src/cli/commands/audit.ts +10 -1
- package/src/cli/commands/dev.ts +18 -2
- package/src/cli/commands/doctor.ts +45 -12
- package/src/cli/commands/eval.ts +36 -4
- package/src/cli/commands/init.ts +6 -4
- package/src/cli/commands/validate.ts +10 -1
- package/src/cli/init/scaffold.ts +9 -6
- package/src/cli/prepare.ts +6 -3
- package/src/cli/required-secrets.ts +3 -2
- package/src/components/content/ApiPlayground.astro +6 -2
- package/src/components/content/api-field.ts +4 -1
- package/src/components/content/api-page.ts +31 -7
- package/src/components/content/mermaid-element.ts +15 -1
- package/src/components/content/view-switcher.ts +7 -4
- package/src/components/islands/hooks.ts +27 -1
- package/src/components/layout/NavTree.astro +49 -5
- package/src/components/layout/NavTreeScript.astro +18 -0
- package/src/components/layout/PageActions.astro +139 -24
- package/src/components/layout/PageLayout.astro +13 -3
- package/src/components/layout/RootLayout.astro +27 -14
- package/src/components/layout/Search.astro +17 -3
- package/src/components/layout/TableOfContents.astro +24 -17
- package/src/components/layout/analytics-client.ts +8 -8
- package/src/components/layout/head-scripts.ts +6 -1
- package/src/components/layout/search/algolia.ts +10 -1
- package/src/components/layout/search/analytics.ts +42 -14
- package/src/components/layout/search/endpoint.ts +48 -5
- package/src/components/layout/search/pagefind.ts +114 -5
- package/src/components/layout/search/types.ts +12 -0
- package/src/components/layout/search/typesense.ts +18 -6
- package/src/components/openapi/AsyncApiOperation.astro +14 -7
- package/src/components/openapi/Authorization.astro +10 -0
- package/src/components/openapi/GraphqlOperation.astro +5 -0
- package/src/components/openapi/Operation.astro +24 -1
- package/src/components/openapi/ParametersTable.astro +8 -4
- package/src/components/openapi/Playground.astro +7 -1
- package/src/components/openapi/RequestBody.astro +19 -2
- package/src/components/openapi/Responses.astro +1 -0
- package/src/components/openapi/SchemaTable.astro +15 -3
- package/src/components/openapi/async-snippets.ts +61 -5
- package/src/components/openapi/async.ts +123 -29
- package/src/components/openapi/graphql-helpers.ts +35 -12
- package/src/components/openapi/helpers.ts +36 -2
- package/src/components/openapi/message-model.ts +54 -11
- package/src/components/openapi/message.ts +14 -4
- package/src/components/openapi/operation-model.ts +16 -7
- package/src/components/openapi/playground-client.ts +77 -58
- package/src/components/openapi/playground-schema.ts +1 -1
- package/src/components/openapi/schema-tree.ts +44 -14
- package/src/components/openapi/security.ts +10 -0
- package/src/components/openapi/snippets.ts +5 -3
- package/src/components/openapi/validate-json.ts +4 -3
- package/src/components/powered-by-middleware.ts +33 -0
- package/src/core/changelog-index.ts +31 -1
- package/src/core/component-diagnostics.ts +53 -0
- package/src/core/component-overrides.ts +32 -0
- package/src/core/config-input.ts +42 -6
- package/src/core/data.ts +2 -0
- package/src/core/diagnostics.ts +5 -0
- package/src/core/i18n-ui.ts +2 -0
- package/src/core/includes.ts +1 -1
- package/src/core/links.ts +47 -17
- package/src/core/powered-by.ts +12 -0
- package/src/core/probe.ts +26 -6
- package/src/core/project-graph.ts +59 -18
- package/src/core/redirect-patterns.ts +36 -7
- package/src/core/schema.ts +26 -6
- package/src/core/sources/cache.ts +6 -0
- package/src/core/sources/contentful-rich-text.ts +29 -6
- package/src/core/sources/contentful.ts +135 -25
- package/src/core/sources/filesystem.ts +26 -4
- package/src/core/sources/github-releases.ts +12 -1
- package/src/core/sources/lexical.ts +15 -10
- package/src/core/sources/lower.ts +19 -3
- package/src/core/sources/mdx-remote.ts +62 -10
- package/src/core/sources/normalize.ts +227 -41
- package/src/core/sources/notion.ts +34 -10
- package/src/core/sources/obsidian.ts +16 -3
- package/src/core/sources/payload.ts +38 -15
- package/src/core/sources/remote.ts +72 -15
- package/src/core/sources/resolve.ts +11 -1
- package/src/core/sources/sanity.ts +43 -25
- package/src/core/sources/strapi.ts +2 -2
- package/src/core/types.ts +18 -0
- package/src/core/ui-packs/ar.ts +1 -0
- package/src/core/ui-packs/bg.ts +1 -0
- package/src/core/ui-packs/bn.ts +1 -0
- package/src/core/ui-packs/ca.ts +1 -0
- package/src/core/ui-packs/cs.ts +1 -0
- package/src/core/ui-packs/da.ts +1 -0
- package/src/core/ui-packs/de.ts +1 -0
- package/src/core/ui-packs/el.ts +1 -0
- package/src/core/ui-packs/es.ts +1 -0
- package/src/core/ui-packs/fa.ts +1 -0
- package/src/core/ui-packs/fi.ts +1 -0
- package/src/core/ui-packs/fr.ts +1 -0
- package/src/core/ui-packs/he.ts +1 -0
- package/src/core/ui-packs/hi.ts +1 -0
- package/src/core/ui-packs/hr.ts +1 -0
- package/src/core/ui-packs/hu.ts +1 -0
- package/src/core/ui-packs/id.ts +1 -0
- package/src/core/ui-packs/it.ts +1 -0
- package/src/core/ui-packs/ja.ts +1 -0
- package/src/core/ui-packs/ko.ts +1 -0
- package/src/core/ui-packs/nl.ts +1 -0
- package/src/core/ui-packs/no.ts +1 -0
- package/src/core/ui-packs/pl.ts +1 -0
- package/src/core/ui-packs/pt-br.ts +1 -0
- package/src/core/ui-packs/pt.ts +1 -0
- package/src/core/ui-packs/ro.ts +1 -0
- package/src/core/ui-packs/ru.ts +1 -0
- package/src/core/ui-packs/sk.ts +1 -0
- package/src/core/ui-packs/sr.ts +1 -0
- package/src/core/ui-packs/sv.ts +1 -0
- package/src/core/ui-packs/th.ts +1 -0
- package/src/core/ui-packs/tr.ts +1 -0
- package/src/core/ui-packs/uk.ts +1 -0
- package/src/core/ui-packs/vi.ts +1 -0
- package/src/core/ui-packs/zh-tw.ts +1 -0
- package/src/core/ui-packs/zh.ts +1 -0
- package/src/deploy/adapter-output.ts +7 -2
- package/src/deploy/adapters/cloudflare.ts +7 -5
- package/src/deploy/adapters/node.ts +47 -9
- package/src/deploy/adapters/types.ts +7 -3
- package/src/deploy/artifacts.ts +2 -1
- package/src/deploy/cloudflare-negotiation.ts +36 -7
- package/src/deploy/headers.ts +15 -1
- package/src/deploy/node-headers.ts +32 -20
- package/src/deploy/platforms/cloudflare.ts +14 -9
- package/src/deploy/platforms/netlify.ts +2 -1
- package/src/deploy/platforms/node.ts +12 -2
- package/src/deploy/platforms/types.ts +21 -7
- package/src/deploy/platforms/vercel.ts +115 -17
- package/src/deploy/redirects.ts +66 -6
- package/src/deploy/rss.ts +3 -1
- package/src/deploy/sitemap.ts +39 -5
- package/src/deploy/vercel-negotiation.ts +325 -103
- package/src/eval/agents.ts +18 -1
- package/src/eval/prompts.ts +6 -3
- package/src/eval/report.ts +34 -13
- package/src/eval/run.ts +5 -0
- package/src/markdown/base-links.ts +15 -14
- package/src/markdown/package-commands.ts +9 -8
- package/src/markdown/relative-links.ts +3 -2
- package/src/narration/build.ts +218 -40
- package/src/narration/provider.ts +89 -9
- package/src/openapi/checks.ts +273 -0
- package/src/openapi/graphql-build.ts +21 -0
- package/src/openapi/graphql.ts +11 -0
- package/src/openapi/model.ts +15 -3
- package/src/openapi/parse.ts +127 -2
- package/src/openapi/references.ts +43 -3
- package/src/openapi/render-mdx.ts +68 -8
- package/src/openapi/sentence.ts +29 -2
- package/src/openapi/source.ts +39 -14
- package/src/ratelimit/index.ts +1 -1
- package/src/ratelimit/runtime.ts +37 -19
- package/src/ratelimit/upstash.ts +40 -10
- package/src/reference/graphql.ts +15 -1
- package/src/reference/index.ts +1 -0
- package/src/reference/openapi.ts +2 -2
- package/src/reference/options.ts +29 -1
- package/src/registry/eject.ts +8 -2
- package/src/search/adapters/locale-scope.ts +30 -0
- package/src/search/adapters/typesense.ts +14 -4
- package/src/search/build.ts +21 -3
- package/src/search/orama-index.ts +163 -31
- package/src/search/source-pages.ts +29 -0
- package/src/search/sync/algolia.ts +99 -9
- package/src/search/sync/index.ts +52 -13
- package/src/search/sync/typesense.ts +72 -27
- package/src/seo/jsonld.ts +38 -20
- package/src/sources/contentful.ts +12 -0
- package/src/sources/github-releases.ts +7 -1
- package/src/sources/notion.ts +7 -0
- package/src/sources/shared.ts +3 -0
- package/src/theme/color.ts +9 -10
- package/src/theme/entry.ts +43 -6
- package/src/translate/validate.ts +23 -1
- package/dist/cli/chunk-0gz36akr.js.map +0 -12
- package/dist/cli/chunk-2n4ps1b3.js.map +0 -15
- package/dist/cli/chunk-43zcf4gz.js.map +0 -36
- package/dist/cli/chunk-4ejxbzee.js +0 -39
- package/dist/cli/chunk-4ejxbzee.js.map +0 -10
- package/dist/cli/chunk-4h8z9vv5.js.map +0 -16
- package/dist/cli/chunk-713mngdq.js.map +0 -14
- package/dist/cli/chunk-9xr4dgee.js.map +0 -10
- package/dist/cli/chunk-amrb6hgh.js +0 -145
- package/dist/cli/chunk-amrb6hgh.js.map +0 -11
- package/dist/cli/chunk-nd1821sk.js.map +0 -10
- package/dist/cli/chunk-rekqz4qg.js.map +0 -10
- package/dist/cli/chunk-ta27feev.js.map +0 -10
- package/dist/cli/chunk-xkz5xbtv.js.map +0 -213
- package/dist/cli/chunk-y5h4r2y0.js.map +0 -10
- package/dist/cli/chunk-zf748avv.js.map +0 -37
- /package/dist/cli/{chunk-9geymqns.js.map → chunk-0w6hs479.js.map} +0 -0
- /package/dist/cli/{chunk-y4ykbark.js.map → chunk-1f882900.js.map} +0 -0
- /package/dist/cli/{chunk-py62fewh.js.map → chunk-53jqw8z0.js.map} +0 -0
- /package/dist/cli/{chunk-5wj2qwn2.js.map → chunk-58bt3hag.js.map} +0 -0
- /package/dist/cli/{chunk-e7vvk5ya.js.map → chunk-5cmts6dv.js.map} +0 -0
- /package/dist/cli/{chunk-g2z9x01v.js.map → chunk-9qzgg6mj.js.map} +0 -0
- /package/dist/cli/{chunk-q3wxxq39.js.map → chunk-a57yh0h9.js.map} +0 -0
- /package/dist/cli/{chunk-2f3ndkpw.js.map → chunk-desvm72z.js.map} +0 -0
- /package/dist/cli/{chunk-gp84za0h.js.map → chunk-dhwtqnnd.js.map} +0 -0
- /package/dist/cli/{chunk-2f7n4qzt.js.map → chunk-dqhg54mr.js.map} +0 -0
- /package/dist/cli/{chunk-zhvd6adm.js.map → chunk-gfc5fqvf.js.map} +0 -0
- /package/dist/cli/{chunk-p804f3pe.js.map → chunk-hvcxng5f.js.map} +0 -0
- /package/dist/cli/{chunk-1n228xqb.js.map → chunk-kdpzw38a.js.map} +0 -0
- /package/dist/cli/{chunk-6bm0xpgd.js.map → chunk-s0bdnc7v.js.map} +0 -0
- /package/dist/cli/{chunk-cmtcnh9f.js.map → chunk-tfycddbt.js.map} +0 -0
- /package/dist/cli/{chunk-ntv4qrt7.js.map → chunk-y4msrwm9.js.map} +0 -0
- /package/dist/cli/{chunk-yj17vs39.js.map → chunk-ydp3065p.js.map} +0 -0
|
@@ -1,29 +1,109 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
GATEWAY_API_KEY_ENV,
|
|
5
|
+
OPENAI_API_KEY_ENV,
|
|
6
|
+
sharedOptions,
|
|
7
|
+
} from "../ai/ask.ts";
|
|
4
8
|
import { adapterDescriptorSchema } from "../core/adapter.ts";
|
|
5
9
|
|
|
6
10
|
/** The speech model narration uses when `gateway()` names none. */
|
|
7
11
|
export const DEFAULT_NARRATION_MODEL = "openai/tts-1-hd";
|
|
8
12
|
|
|
9
|
-
/** The voice narration uses when
|
|
13
|
+
/** The voice narration uses when the provider names none. */
|
|
10
14
|
export const DEFAULT_NARRATION_VOICE = "alloy";
|
|
11
15
|
|
|
12
16
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* `voice` and `instructions` are narration options and are rejected by the
|
|
16
|
-
* assistant.
|
|
17
|
+
* The AI SDK provider `openai()` narration speaks through, with or without a
|
|
18
|
+
* `baseUrl`: `@ai-sdk/openai-compatible` has no speech models.
|
|
17
19
|
*/
|
|
18
|
-
export const
|
|
20
|
+
export const OPENAI_SPEECH_DEP = "@ai-sdk/openai";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Whether `url` carries a username or password, which `fetch` refuses to
|
|
24
|
+
* send and whose error message would print them. A URL that doesn't parse
|
|
25
|
+
* has none.
|
|
26
|
+
*/
|
|
27
|
+
export const hasCredentials = (url: string): boolean => {
|
|
28
|
+
const parsed = URL.parse(url);
|
|
29
|
+
return Boolean(parsed?.username || parsed?.password);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/** The narration options both providers take on top of the shared ones. */
|
|
33
|
+
const speechOptions = {
|
|
34
|
+
instructions: z.string().min(1).optional(),
|
|
35
|
+
voice: z.string().min(1).default(DEFAULT_NARRATION_VOICE),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const gatewayNarrationSchema = adapterDescriptorSchema(
|
|
19
39
|
"gateway",
|
|
20
40
|
z.strictObject({
|
|
21
41
|
...sharedOptions(GATEWAY_API_KEY_ENV),
|
|
22
|
-
|
|
42
|
+
...speechOptions,
|
|
23
43
|
model: z.string().min(1).default(DEFAULT_NARRATION_MODEL),
|
|
24
|
-
voice: z.string().min(1).default(DEFAULT_NARRATION_VOICE),
|
|
25
44
|
})
|
|
26
45
|
);
|
|
27
46
|
|
|
47
|
+
const openaiNarrationSchema = adapterDescriptorSchema(
|
|
48
|
+
"openai",
|
|
49
|
+
z.strictObject({
|
|
50
|
+
...sharedOptions(OPENAI_API_KEY_ENV),
|
|
51
|
+
...speechOptions,
|
|
52
|
+
// Replaces the shared default: whether it's OPENAI_API_KEY depends on
|
|
53
|
+
// the `baseUrl` (see below).
|
|
54
|
+
apiKeyEnv: z.string().min(1).optional(),
|
|
55
|
+
// An unparsable URL is left to `z.url()`.
|
|
56
|
+
baseUrl: z
|
|
57
|
+
.url()
|
|
58
|
+
.refine((url) => !hasCredentials(url), {
|
|
59
|
+
message:
|
|
60
|
+
"Credentials don't go in the baseUrl: name the env var holding the key in apiKeyEnv.",
|
|
61
|
+
})
|
|
62
|
+
.optional(),
|
|
63
|
+
model: z.string().min(1),
|
|
64
|
+
})
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* `narration.provider`: the `gateway()` or `openai()` descriptor from
|
|
69
|
+
* `blume/ai`, read with speech defaults. `reasoning` (and `openai()`'s
|
|
70
|
+
* `name`, which speech models don't read) are assistant options and are
|
|
71
|
+
* rejected here; `voice` and `instructions` are narration options and are
|
|
72
|
+
* rejected by the assistant.
|
|
73
|
+
*
|
|
74
|
+
* `openai()` is read for what narration needs, not what the assistant does:
|
|
75
|
+
* it speaks through `@ai-sdk/openai` whatever the `baseUrl`, and a custom
|
|
76
|
+
* endpoint gets only the key `apiKeyEnv` names, never `OPENAI_API_KEY` by
|
|
77
|
+
* default, so it may take none at all. A key that is named is required.
|
|
78
|
+
*/
|
|
79
|
+
export const narrationProviderSchema = z
|
|
80
|
+
.discriminatedUnion("kind", [gatewayNarrationSchema, openaiNarrationSchema])
|
|
81
|
+
.transform((provider) => {
|
|
82
|
+
if (provider.kind !== "openai") {
|
|
83
|
+
return provider;
|
|
84
|
+
}
|
|
85
|
+
const { options } = provider;
|
|
86
|
+
const apiKeyEnv =
|
|
87
|
+
options.apiKeyEnv ?? (options.baseUrl ? undefined : OPENAI_API_KEY_ENV);
|
|
88
|
+
return {
|
|
89
|
+
...provider,
|
|
90
|
+
options: apiKeyEnv ? { ...options, apiKeyEnv } : options,
|
|
91
|
+
requiredSecrets: apiKeyEnv ? [apiKeyEnv] : [],
|
|
92
|
+
runtimeDeps: [OPENAI_SPEECH_DEP],
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
|
|
28
96
|
/** A validated narration provider, defaults filled in. */
|
|
29
97
|
export type NarrationProvider = z.output<typeof narrationProviderSchema>;
|
|
98
|
+
|
|
99
|
+
/** A validated `gateway()` narration provider. */
|
|
100
|
+
export type GatewayNarrationProvider = Extract<
|
|
101
|
+
NarrationProvider,
|
|
102
|
+
{ kind: "gateway" }
|
|
103
|
+
>;
|
|
104
|
+
|
|
105
|
+
/** A validated `openai()` narration provider. */
|
|
106
|
+
export type OpenAINarrationProvider = Extract<
|
|
107
|
+
NarrationProvider,
|
|
108
|
+
{ kind: "openai" }
|
|
109
|
+
>;
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import type { OperationObject } from "@scalar/openapi-types/3.2";
|
|
2
|
+
|
|
3
|
+
import type { ApiDocument } from "./model.ts";
|
|
4
|
+
import { HTTP_METHODS } from "./model.ts";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Targeted checks for spec mistakes that would otherwise render silently
|
|
8
|
+
* wrong. This is not a validator — a linter such as Spectral or oasdiff owns
|
|
9
|
+
* that — and a spec that fails one of these still renders. Each check names
|
|
10
|
+
* one mistake whose symptom on the page is quiet: a path placeholder sent
|
|
11
|
+
* as literal text, a Try it input whose value goes nowhere, an Authorization
|
|
12
|
+
* section with no scheme to describe. Each reports a warning with its own
|
|
13
|
+
* code, so a build says what's wrong instead of shipping it.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** One mistake a check found, before the source names the spec it's in. */
|
|
17
|
+
export interface SpecIssue {
|
|
18
|
+
code: string;
|
|
19
|
+
message: string;
|
|
20
|
+
suggestion: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** A `{name}` placeholder in a path template. */
|
|
24
|
+
const PATH_TEMPLATE = /\{(?<name>[^{}]+)\}/gu;
|
|
25
|
+
|
|
26
|
+
/** A reference to a parameter declared under `components.parameters`. */
|
|
27
|
+
const PARAMETER_REF = /^#\/components\/parameters\/(?<name>[^/]+)$/u;
|
|
28
|
+
|
|
29
|
+
/** How many operations a security-scheme warning names before counting the rest. */
|
|
30
|
+
const NAMED_OPERATIONS = 3;
|
|
31
|
+
|
|
32
|
+
/** One entry of a document's `paths` or `webhooks` map. */
|
|
33
|
+
type PathItem = NonNullable<ApiDocument["paths"]>[string];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* How a warning names an operation: `GET /pets`, or a webhook by its name
|
|
37
|
+
* (`Webhook "newPet" (POST)`), since a webhook has no path.
|
|
38
|
+
*/
|
|
39
|
+
export const operationLabel = (
|
|
40
|
+
name: string,
|
|
41
|
+
method: string,
|
|
42
|
+
webhook: boolean
|
|
43
|
+
): string =>
|
|
44
|
+
webhook
|
|
45
|
+
? `Webhook "${name}" (${method.toUpperCase()})`
|
|
46
|
+
: `${method.toUpperCase()} ${name}`;
|
|
47
|
+
|
|
48
|
+
/** One entry of a `parameters` list: a parameter, or a `$ref` to one. */
|
|
49
|
+
type ParameterEntry = NonNullable<OperationObject["parameters"]>[number];
|
|
50
|
+
|
|
51
|
+
// The runtime object checks stand guard because the document was parsed from
|
|
52
|
+
// arbitrary YAML/JSON: a spec can put a scalar or null where the type
|
|
53
|
+
// promises an object.
|
|
54
|
+
const isObject = <Value>(value: Value): value is NonNullable<Value> =>
|
|
55
|
+
typeof value === "object" && value !== null;
|
|
56
|
+
|
|
57
|
+
/** A parameter's `name`, which a hand-written spec may have left out or mistyped. */
|
|
58
|
+
const isName = <Value>(value: Value): value is Value & string =>
|
|
59
|
+
typeof value === "string";
|
|
60
|
+
|
|
61
|
+
/** A declared list, or none when a hand-written spec put something else there. */
|
|
62
|
+
const listOf = <Item>(value: Item[] | undefined): NonNullable<Item>[] =>
|
|
63
|
+
Array.isArray(value) ? value.filter(isObject) : [];
|
|
64
|
+
|
|
65
|
+
/** A resolved parameter: one that isn't a `$ref`. */
|
|
66
|
+
type Parameter = Extract<ParameterEntry, { in: string }>;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The parameters an operation declares, its path item's and its own, `$ref`s
|
|
70
|
+
* resolved through `components.parameters`. One that doesn't resolve is left
|
|
71
|
+
* out, as the page leaves it out.
|
|
72
|
+
*/
|
|
73
|
+
const parametersOf = (
|
|
74
|
+
document: ApiDocument,
|
|
75
|
+
item: PathItem,
|
|
76
|
+
operation: OperationObject
|
|
77
|
+
): Parameter[] => {
|
|
78
|
+
const components = document.components?.parameters ?? {};
|
|
79
|
+
return [
|
|
80
|
+
...listOf<ParameterEntry>(item.parameters),
|
|
81
|
+
...listOf<ParameterEntry>(operation.parameters),
|
|
82
|
+
].flatMap((entry) => {
|
|
83
|
+
const refName =
|
|
84
|
+
"$ref" in entry
|
|
85
|
+
? PARAMETER_REF.exec(entry.$ref)?.groups?.name
|
|
86
|
+
: undefined;
|
|
87
|
+
const parameter = (refName ? components[refName] : undefined) ?? entry;
|
|
88
|
+
return "in" in parameter ? [parameter] : [];
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* A path whose template and declared path parameters disagree. A placeholder
|
|
94
|
+
* with no parameter gets no input in Try it and no row in the table, so
|
|
95
|
+
* requests send it as literal text; a parameter with no placeholder gets an
|
|
96
|
+
* input whose value never reaches the URL.
|
|
97
|
+
*/
|
|
98
|
+
const pathParameterIssues = (
|
|
99
|
+
signature: string,
|
|
100
|
+
path: string,
|
|
101
|
+
declared: Set<string>
|
|
102
|
+
): SpecIssue[] => {
|
|
103
|
+
const used = new Set(
|
|
104
|
+
[...path.matchAll(PATH_TEMPLATE)].map((match) => match.groups?.name ?? "")
|
|
105
|
+
);
|
|
106
|
+
const issues: SpecIssue[] = [];
|
|
107
|
+
for (const name of used) {
|
|
108
|
+
if (!declared.has(name)) {
|
|
109
|
+
issues.push({
|
|
110
|
+
code: "BLUME_OPENAPI_PATH_PARAMETER_MISSING",
|
|
111
|
+
message: `${signature} has {${name}} in its path but declares no "${name}" path parameter, so Try it and the code samples send "{${name}}" as literal text.`,
|
|
112
|
+
suggestion: `Declare "${name}" in the operation's or the path's \`parameters\`, with \`in: path\` and \`required: true\`.`,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
for (const name of declared) {
|
|
117
|
+
if (!used.has(name)) {
|
|
118
|
+
issues.push({
|
|
119
|
+
code: "BLUME_OPENAPI_PATH_PARAMETER_UNUSED",
|
|
120
|
+
message: `${signature} declares a "${name}" path parameter, but its path has no {${name}}, so the value Try it asks for never reaches the URL.`,
|
|
121
|
+
suggestion: `Add {${name}} to the path, or remove the parameter.`,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return issues;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/** The scheme names a `security` list requires. */
|
|
129
|
+
const requiredSchemes = (
|
|
130
|
+
security: ApiDocument["security"] | OperationObject["security"]
|
|
131
|
+
): string[] =>
|
|
132
|
+
listOf(security).flatMap((requirement) => Object.keys(requirement));
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Security schemes a requirement names that `components.securitySchemes`
|
|
136
|
+
* doesn't define: the Authorization section has nothing to describe, and
|
|
137
|
+
* Try it and the code samples send no credential for them. One warning per
|
|
138
|
+
* scheme, naming where it's required: the root `security`, or the first few
|
|
139
|
+
* operations that list it.
|
|
140
|
+
*/
|
|
141
|
+
const securitySchemeIssues = (
|
|
142
|
+
document: ApiDocument,
|
|
143
|
+
requirers: Map<string, string[]>
|
|
144
|
+
): SpecIssue[] => {
|
|
145
|
+
const defined = document.components?.securitySchemes ?? {};
|
|
146
|
+
const root = new Set(requiredSchemes(document.security));
|
|
147
|
+
const issues: SpecIssue[] = [];
|
|
148
|
+
for (const scheme of new Set([...root, ...requirers.keys()])) {
|
|
149
|
+
if (Object.hasOwn(defined, scheme)) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const operations = requirers.get(scheme) ?? [];
|
|
153
|
+
const more = operations.length - NAMED_OPERATIONS;
|
|
154
|
+
const where = root.has(scheme)
|
|
155
|
+
? "The spec's root `security` requires"
|
|
156
|
+
: `${operations.slice(0, NAMED_OPERATIONS).join(", ")}${more > 0 ? ` and ${more} more` : ""} ${operations.length === 1 ? "requires" : "require"}`;
|
|
157
|
+
issues.push({
|
|
158
|
+
code: "BLUME_OPENAPI_UNKNOWN_SECURITY_SCHEME",
|
|
159
|
+
message: `${where} the "${scheme}" security scheme, which \`components.securitySchemes\` doesn't define, so the Authorization section can't describe it and requests send no credential for it.`,
|
|
160
|
+
suggestion: `Define "${scheme}" in \`components.securitySchemes\`, or fix the name in \`security\`.`,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return issues;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* An operation's `in: querystring` parameter (OpenAPI 3.2: the whole query
|
|
168
|
+
* string as one value), which the parameter table, Try it, and the code
|
|
169
|
+
* samples don't handle yet, so it would drop out of all three without a sign.
|
|
170
|
+
*/
|
|
171
|
+
const querystringIssue = (signature: string): SpecIssue => ({
|
|
172
|
+
code: "BLUME_OPENAPI_UNSUPPORTED",
|
|
173
|
+
message: `${signature} declares an \`in: querystring\` parameter, which OpenAPI 3.2 added and Blume doesn't render yet, so the page has no row or Try it input for it and the code samples leave the query string out.`,
|
|
174
|
+
suggestion:
|
|
175
|
+
"Describe the query string in the operation's `description` until Blume renders `querystring` parameters.",
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* A path item's `additionalOperations` (OpenAPI 3.2: methods beyond the ones
|
|
180
|
+
* a path item names directly, like `COPY`), which the extractor doesn't
|
|
181
|
+
* collect yet, so those operations would be missing without a sign.
|
|
182
|
+
*/
|
|
183
|
+
const additionalOperationsIssue = (
|
|
184
|
+
label: string,
|
|
185
|
+
methods: string[]
|
|
186
|
+
): SpecIssue => ({
|
|
187
|
+
code: "BLUME_OPENAPI_UNSUPPORTED",
|
|
188
|
+
message: `${label} declares \`additionalOperations\` (${methods.join(", ")}), which OpenAPI 3.2 added and Blume doesn't render yet, so those operations are missing from the reference.`,
|
|
189
|
+
suggestion:
|
|
190
|
+
"Document those operations on a page of their own until Blume renders `additionalOperations`.",
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* A root `x-webhooks` and no `webhooks`. It's the extension OpenAPI 3.0 tools
|
|
195
|
+
* read for webhooks, and upgrading a 3.0 spec renames it to `webhooks`; a spec
|
|
196
|
+
* already at 3.1 or later isn't upgraded, so it stays an extension and its
|
|
197
|
+
* webhooks would be missing without a sign.
|
|
198
|
+
*/
|
|
199
|
+
const xWebhooksIssues = (document: ApiDocument): SpecIssue[] =>
|
|
200
|
+
"x-webhooks" in document && document.webhooks === undefined
|
|
201
|
+
? [
|
|
202
|
+
{
|
|
203
|
+
code: "BLUME_OPENAPI_X_WEBHOOKS",
|
|
204
|
+
message:
|
|
205
|
+
"The spec declares its webhooks under `x-webhooks`, the extension OpenAPI 3.0 tools read. Blume reads it only from a 3.0 spec, which it upgrades, so these webhooks are missing from the reference.",
|
|
206
|
+
suggestion:
|
|
207
|
+
"Rename `x-webhooks` to `webhooks`, the field OpenAPI 3.1 added for them.",
|
|
208
|
+
},
|
|
209
|
+
]
|
|
210
|
+
: [];
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Check a parsed document for the mistakes above, and for the OpenAPI 3.2
|
|
214
|
+
* features Blume doesn't render yet. `$ref` path items are skipped: the
|
|
215
|
+
* extractor already reports them as missing from the reference.
|
|
216
|
+
*/
|
|
217
|
+
export const specIssues = (document: ApiDocument): SpecIssue[] => {
|
|
218
|
+
const issues: SpecIssue[] = [...xWebhooksIssues(document)];
|
|
219
|
+
// Scheme name -> the operations whose own `security` lists it.
|
|
220
|
+
const requirers = new Map<string, string[]>();
|
|
221
|
+
const visit = (name: string, item: PathItem, webhook: boolean): void => {
|
|
222
|
+
if (!isObject(item) || "$ref" in item) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const additional = isObject(item.additionalOperations)
|
|
226
|
+
? Object.keys(item.additionalOperations)
|
|
227
|
+
: [];
|
|
228
|
+
if (additional.length > 0) {
|
|
229
|
+
issues.push(
|
|
230
|
+
additionalOperationsIssue(
|
|
231
|
+
webhook ? `Webhook "${name}"` : `Path "${name}"`,
|
|
232
|
+
additional
|
|
233
|
+
)
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
for (const method of HTTP_METHODS) {
|
|
237
|
+
const operation = item[method];
|
|
238
|
+
if (!isObject(operation)) {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
const signature = operationLabel(name, method, webhook);
|
|
242
|
+
for (const scheme of requiredSchemes(operation.security)) {
|
|
243
|
+
requirers.set(scheme, [...(requirers.get(scheme) ?? []), signature]);
|
|
244
|
+
}
|
|
245
|
+
const parameters = parametersOf(document, item, operation);
|
|
246
|
+
// A webhook is keyed by its name, not a path: it has no template.
|
|
247
|
+
if (!webhook) {
|
|
248
|
+
issues.push(
|
|
249
|
+
...pathParameterIssues(
|
|
250
|
+
signature,
|
|
251
|
+
name,
|
|
252
|
+
new Set(
|
|
253
|
+
parameters
|
|
254
|
+
.filter((parameter) => parameter.in === "path")
|
|
255
|
+
.map((parameter) => parameter.name)
|
|
256
|
+
.filter(isName)
|
|
257
|
+
)
|
|
258
|
+
)
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
if (parameters.some((parameter) => parameter.in === "querystring")) {
|
|
262
|
+
issues.push(querystringIssue(signature));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
for (const [path, item] of Object.entries(document.paths ?? {})) {
|
|
267
|
+
visit(path, item, false);
|
|
268
|
+
}
|
|
269
|
+
for (const [name, item] of Object.entries(document.webhooks ?? {})) {
|
|
270
|
+
visit(name, item, true);
|
|
271
|
+
}
|
|
272
|
+
return [...issues, ...securitySchemeIssues(document, requirers)];
|
|
273
|
+
};
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
parse,
|
|
27
27
|
print,
|
|
28
28
|
specifiedDirectives,
|
|
29
|
+
valueFromASTUntyped,
|
|
29
30
|
visit,
|
|
30
31
|
} from "graphql";
|
|
31
32
|
|
|
@@ -34,6 +35,7 @@ import type {
|
|
|
34
35
|
GraphqlEnumValue,
|
|
35
36
|
GraphqlField as ModelField,
|
|
36
37
|
GraphqlInputValue,
|
|
38
|
+
GraphqlJsonValue,
|
|
37
39
|
GraphqlTypeDef,
|
|
38
40
|
GraphqlTypeRef,
|
|
39
41
|
} from "./graphql.ts";
|
|
@@ -68,6 +70,21 @@ const defaultLiteral = (
|
|
|
68
70
|
return literal ? print(literal) : undefined;
|
|
69
71
|
};
|
|
70
72
|
|
|
73
|
+
/**
|
|
74
|
+
* The same default as the JSON a client sends in `variables`, which the
|
|
75
|
+
* example variables start from.
|
|
76
|
+
*/
|
|
77
|
+
const defaultJson = (
|
|
78
|
+
input: GraphQLArgument | GraphQLInputField
|
|
79
|
+
): GraphqlJsonValue | undefined => {
|
|
80
|
+
const literal = input.default?.literal;
|
|
81
|
+
// SAFETY: a const literal has no variables, so it lowers to JSON values
|
|
82
|
+
// only: numbers, strings, booleans, null, enum names, lists and objects.
|
|
83
|
+
return literal
|
|
84
|
+
? (valueFromASTUntyped(literal) as GraphqlJsonValue)
|
|
85
|
+
: undefined;
|
|
86
|
+
};
|
|
87
|
+
|
|
71
88
|
const inputValue = (
|
|
72
89
|
input: GraphQLArgument | GraphQLInputField
|
|
73
90
|
): GraphqlInputValue => {
|
|
@@ -80,6 +97,10 @@ const inputValue = (
|
|
|
80
97
|
if (printed !== undefined) {
|
|
81
98
|
value.default = printed;
|
|
82
99
|
}
|
|
100
|
+
const json = defaultJson(input);
|
|
101
|
+
if (json !== undefined) {
|
|
102
|
+
value.defaultValue = json;
|
|
103
|
+
}
|
|
83
104
|
// `deprecationReason` is null on non-deprecated members in some builds;
|
|
84
105
|
// absent means "not deprecated" downstream, so both collapse to absent.
|
|
85
106
|
if (
|
package/src/openapi/graphql.ts
CHANGED
|
@@ -58,6 +58,15 @@ export interface GraphqlTypeRef {
|
|
|
58
58
|
name: string;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
/** A GraphQL input value as JSON carries it in `variables` (enums by name). */
|
|
62
|
+
export type GraphqlJsonValue =
|
|
63
|
+
| string
|
|
64
|
+
| number
|
|
65
|
+
| boolean
|
|
66
|
+
| null
|
|
67
|
+
| GraphqlJsonValue[]
|
|
68
|
+
| { [key: string]: GraphqlJsonValue };
|
|
69
|
+
|
|
61
70
|
/** An argument or input-object field. */
|
|
62
71
|
export interface GraphqlInputValue {
|
|
63
72
|
name: string;
|
|
@@ -65,6 +74,8 @@ export interface GraphqlInputValue {
|
|
|
65
74
|
type: GraphqlTypeRef;
|
|
66
75
|
/** Default value printed as a GraphQL literal (`10`, `"asc"`, `[1, 2]`). */
|
|
67
76
|
default?: string;
|
|
77
|
+
/** The same default as JSON, for the example variables. */
|
|
78
|
+
defaultValue?: GraphqlJsonValue;
|
|
68
79
|
deprecationReason?: string;
|
|
69
80
|
}
|
|
70
81
|
|
package/src/openapi/model.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { Document, OperationObject } from "@scalar/openapi-types/3.
|
|
1
|
+
import type { Document, OperationObject } from "@scalar/openapi-types/3.2";
|
|
2
2
|
|
|
3
|
+
import type { GraphqlAuthOptions } from "../reference/options.ts";
|
|
3
4
|
import type {
|
|
4
5
|
AsyncApiAction,
|
|
5
6
|
AsyncApiDocument,
|
|
@@ -26,10 +27,18 @@ export { slugify } from "./references.ts";
|
|
|
26
27
|
* per-operation route so it becomes a first-class Blume page.
|
|
27
28
|
*/
|
|
28
29
|
|
|
29
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* A normalized OpenAPI document, internal `$ref`s intact. Typed as 3.2, which
|
|
32
|
+
* is backward compatible with 3.1, so one type covers both the upgraded 3.1
|
|
33
|
+
* documents and the 3.2 ones read as written.
|
|
34
|
+
*/
|
|
30
35
|
export type ApiDocument = Document;
|
|
31
36
|
|
|
32
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* The HTTP methods an OpenAPI path item may declare, in spec order. `query`
|
|
39
|
+
* is OpenAPI 3.2's (the QUERY method: a safe request that carries a body); a
|
|
40
|
+
* 3.1 path item can't declare it, so listing it changes nothing there.
|
|
41
|
+
*/
|
|
33
42
|
export const HTTP_METHODS = [
|
|
34
43
|
"get",
|
|
35
44
|
"put",
|
|
@@ -39,6 +48,7 @@ export const HTTP_METHODS = [
|
|
|
39
48
|
"head",
|
|
40
49
|
"patch",
|
|
41
50
|
"trace",
|
|
51
|
+
"query",
|
|
42
52
|
] as const;
|
|
43
53
|
|
|
44
54
|
export type HttpMethod = (typeof HTTP_METHODS)[number];
|
|
@@ -140,6 +150,8 @@ export interface ApiSpecData {
|
|
|
140
150
|
* (GraphQL only; OpenAPI documents carry their servers in the document).
|
|
141
151
|
*/
|
|
142
152
|
endpoint?: string;
|
|
153
|
+
/** How the GraphQL endpoint authenticates (GraphQL only). */
|
|
154
|
+
auth?: GraphqlAuthOptions;
|
|
143
155
|
/** Operations keyed by {@link ApiOperationRef.key}. */
|
|
144
156
|
operations: Record<string, ApiOperationRef>;
|
|
145
157
|
tags: ApiTagRef[];
|
package/src/openapi/parse.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { setTimeout as sleep } from "node:timers/promises";
|
|
|
4
4
|
import type * as ConverterModule from "@asyncapi/converter";
|
|
5
5
|
import { normalize, upgrade } from "@scalar/openapi-parser";
|
|
6
6
|
import pRetry, { AbortError } from "p-retry";
|
|
7
|
-
import { isAbsolute, join } from "pathe";
|
|
7
|
+
import { dirname, isAbsolute, join, resolve } from "pathe";
|
|
8
8
|
import type * as UndiciModule from "undici";
|
|
9
9
|
|
|
10
10
|
import {
|
|
@@ -15,8 +15,11 @@ import { nodeRequire } from "../core/node-require.ts";
|
|
|
15
15
|
import { hashText } from "../core/sources/cache.ts";
|
|
16
16
|
import type { AsyncApiDocument } from "./asyncapi.ts";
|
|
17
17
|
import { normalizeAsyncApiDocument } from "./asyncapi.ts";
|
|
18
|
+
import { operationLabel } from "./checks.ts";
|
|
19
|
+
import type { SpecIssue } from "./checks.ts";
|
|
18
20
|
import { buildGraphqlDocument } from "./graphql-build.ts";
|
|
19
21
|
import type { GraphqlDocument } from "./graphql.ts";
|
|
22
|
+
import { HTTP_METHODS } from "./model.ts";
|
|
20
23
|
import type { ApiDocument } from "./model.ts";
|
|
21
24
|
import { applyOverlay, isJsonObject, overlayDocument } from "./overlay.ts";
|
|
22
25
|
import { SpecDependencyError } from "./spec-dependency-error.ts";
|
|
@@ -25,6 +28,8 @@ import { SpecDependencyError } from "./spec-dependency-error.ts";
|
|
|
25
28
|
* Spec loading and normalization. Blume reuses Scalar's parser
|
|
26
29
|
* (`@scalar/openapi-parser`) to read a spec (YAML or JSON), then upgrade Swagger
|
|
27
30
|
* 2.0 / OpenAPI 3.0 documents to 3.1 so the renderer only handles one shape.
|
|
31
|
+
* OpenAPI 3.2 is backward compatible with 3.1, so a 3.2 document is read as
|
|
32
|
+
* written.
|
|
28
33
|
* Internal `$ref`s are deliberately left in place (see `model.ts`).
|
|
29
34
|
*
|
|
30
35
|
* Remote (`http(s)`) specs are fetched defensively — bounded per attempt, retried
|
|
@@ -58,6 +63,8 @@ const PROXY_ENV_VARS = [
|
|
|
58
63
|
|
|
59
64
|
export interface ParsedSpec {
|
|
60
65
|
document: ApiDocument;
|
|
66
|
+
/** `x-codeSamples` entries whose `$ref` couldn't be inlined. */
|
|
67
|
+
issues: SpecIssue[];
|
|
61
68
|
warnings: string[];
|
|
62
69
|
}
|
|
63
70
|
|
|
@@ -360,6 +367,123 @@ export const readOverlaidSpec = async (
|
|
|
360
367
|
return { text: JSON.stringify(document), warnings };
|
|
361
368
|
};
|
|
362
369
|
|
|
370
|
+
/** A `$ref` in place of an `x-codeSamples` entry's `source`. */
|
|
371
|
+
interface SampleRef {
|
|
372
|
+
$ref: string;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/** An `x-codeSamples` entry whose `source` is a `$ref` rather than the code. */
|
|
376
|
+
const isRefSample = (value: unknown): value is { source: SampleRef } =>
|
|
377
|
+
isJsonObject(value) &&
|
|
378
|
+
isJsonObject(value.source) &&
|
|
379
|
+
typeof value.source.$ref === "string";
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Where a code sample's `$ref` points, in the form `readSpecText` reads: a
|
|
383
|
+
* URL, or a path resolved against the spec's own location. `undefined` for
|
|
384
|
+
* a JSON pointer (`#/…`, or a file with a fragment), which names a spot
|
|
385
|
+
* inside a document rather than a file of code.
|
|
386
|
+
*/
|
|
387
|
+
const sampleTarget = (
|
|
388
|
+
ref: string,
|
|
389
|
+
spec: string,
|
|
390
|
+
root: string
|
|
391
|
+
): string | undefined => {
|
|
392
|
+
if (ref.includes("#")) {
|
|
393
|
+
return undefined;
|
|
394
|
+
}
|
|
395
|
+
if (URL_SPEC.test(ref)) {
|
|
396
|
+
return ref;
|
|
397
|
+
}
|
|
398
|
+
if (URL_SPEC.test(spec)) {
|
|
399
|
+
return new URL(ref, spec).href;
|
|
400
|
+
}
|
|
401
|
+
return resolve(dirname(isAbsolute(spec) ? spec : join(root, spec)), ref);
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Inline each `x-codeSamples` entry whose `source` is a `$ref` to a file of
|
|
406
|
+
* its own, as Redocly's docs describe (`source: { $ref: ../code_samples/… }`):
|
|
407
|
+
* `redocly bundle` inlines those, and a spec read as written doesn't. Each
|
|
408
|
+
* file is read relative to the spec,
|
|
409
|
+
* the way the spec itself was (local, or fetched and cached), so the sample
|
|
410
|
+
* renders like a written-out one. An entry that can't be read is left out of
|
|
411
|
+
* the page, with a warning, rather than dropped without a sign.
|
|
412
|
+
*/
|
|
413
|
+
const inlineSampleRefs = async (
|
|
414
|
+
document: ApiDocument,
|
|
415
|
+
spec: string,
|
|
416
|
+
root: string,
|
|
417
|
+
options: SpecFetchOptions
|
|
418
|
+
): Promise<SpecIssue[]> => {
|
|
419
|
+
const reads: Promise<SpecIssue | undefined>[] = [];
|
|
420
|
+
const inline = async (
|
|
421
|
+
entry: { source: SampleRef | string },
|
|
422
|
+
ref: string,
|
|
423
|
+
label: string
|
|
424
|
+
): Promise<SpecIssue | undefined> => {
|
|
425
|
+
const target = sampleTarget(ref, spec, root);
|
|
426
|
+
if (target === undefined) {
|
|
427
|
+
return {
|
|
428
|
+
code: "BLUME_OPENAPI_CODE_SAMPLE_REF",
|
|
429
|
+
message: `${label} has an \`x-codeSamples\` entry whose \`source\` is a $ref to "${ref}", a spot inside a document, which Blume doesn't resolve, so the sample is left out.`,
|
|
430
|
+
suggestion:
|
|
431
|
+
"Point the `$ref` at a file that holds only the sample's code, or write the code out in `source`.",
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
try {
|
|
435
|
+
const { text } = await readSpecText(target, root, options);
|
|
436
|
+
entry.source = text;
|
|
437
|
+
return undefined;
|
|
438
|
+
} catch (error) {
|
|
439
|
+
// SAFETY: a failed read throws a Node error, and a failed fetch an
|
|
440
|
+
// Error (`attemptFetch` wraps anything else); only the message is shown.
|
|
441
|
+
const { message } = error as Error;
|
|
442
|
+
return {
|
|
443
|
+
code: "BLUME_OPENAPI_CODE_SAMPLE_REF",
|
|
444
|
+
message: `${label} has an \`x-codeSamples\` entry whose \`source\` is a $ref to "${ref}", which couldn't be read (${message}), so the sample is left out.`,
|
|
445
|
+
suggestion:
|
|
446
|
+
"Check the path, which is relative to the spec, or write the code out in `source`.",
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
const visit = (
|
|
451
|
+
name: string,
|
|
452
|
+
item: NonNullable<ApiDocument["paths"]>[string] | undefined,
|
|
453
|
+
webhook: boolean
|
|
454
|
+
): void => {
|
|
455
|
+
if (!isJsonObject(item) || "$ref" in item) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
for (const method of HTTP_METHODS) {
|
|
459
|
+
const operation = item[method];
|
|
460
|
+
// The spelling `customCodeSamples` renders: the current one wins.
|
|
461
|
+
const samples = isJsonObject(operation)
|
|
462
|
+
? (operation["x-codeSamples"] ?? operation["x-code-samples"])
|
|
463
|
+
: undefined;
|
|
464
|
+
for (const entry of Array.isArray(samples) ? samples : []) {
|
|
465
|
+
if (isRefSample(entry)) {
|
|
466
|
+
reads.push(
|
|
467
|
+
inline(
|
|
468
|
+
entry,
|
|
469
|
+
entry.source.$ref,
|
|
470
|
+
operationLabel(name, method, webhook)
|
|
471
|
+
)
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
for (const [path, item] of Object.entries(document.paths ?? {})) {
|
|
478
|
+
visit(path, item, false);
|
|
479
|
+
}
|
|
480
|
+
for (const [name, item] of Object.entries(document.webhooks ?? {})) {
|
|
481
|
+
visit(name, item, true);
|
|
482
|
+
}
|
|
483
|
+
const issues = await Promise.all(reads);
|
|
484
|
+
return issues.filter((issue) => issue !== undefined);
|
|
485
|
+
};
|
|
486
|
+
|
|
363
487
|
export const parseSpec = async (
|
|
364
488
|
spec: string,
|
|
365
489
|
root: string,
|
|
@@ -377,7 +501,8 @@ export const parseSpec = async (
|
|
|
377
501
|
`${spec} is not a valid OpenAPI document (expected a YAML or JSON object).`
|
|
378
502
|
);
|
|
379
503
|
}
|
|
380
|
-
|
|
504
|
+
const issues = await inlineSampleRefs(specification, spec, root, options);
|
|
505
|
+
return { document: specification, issues, warnings };
|
|
381
506
|
};
|
|
382
507
|
|
|
383
508
|
export interface ParsedAsyncApiSpec {
|