blume 2.1.0 → 2.1.1
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 +94 -0
- package/dist/cli/{chunk-5cg2rzgf.js → chunk-073rdvgb.js} +19 -48
- package/dist/cli/{chunk-5cg2rzgf.js.map → chunk-073rdvgb.js.map} +4 -5
- 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-150jk7km.js +52 -0
- package/dist/cli/chunk-150jk7km.js.map +10 -0
- package/dist/cli/{chunk-rekqz4qg.js → chunk-313vnn40.js} +16 -15
- package/dist/cli/chunk-313vnn40.js.map +10 -0
- package/dist/cli/{chunk-gp84za0h.js → chunk-3v3ffbrj.js} +2 -2
- package/dist/cli/{chunk-9xr4dgee.js → chunk-3x6x3pph.js} +29 -16
- package/dist/cli/chunk-3x6x3pph.js.map +10 -0
- package/dist/cli/{chunk-0gz36akr.js → chunk-42t4jxdx.js} +21 -5
- package/dist/cli/chunk-42t4jxdx.js.map +12 -0
- package/dist/cli/{chunk-ta27feev.js → chunk-54ztw1nj.js} +21 -2
- package/dist/cli/chunk-54ztw1nj.js.map +10 -0
- package/dist/cli/{chunk-jnwj5kjj.js → chunk-5vjtgs6e.js} +20 -7
- package/dist/cli/{chunk-jnwj5kjj.js.map → chunk-5vjtgs6e.js.map} +3 -3
- package/dist/cli/chunk-66w9j7p6.js +322 -0
- package/dist/cli/chunk-66w9j7p6.js.map +14 -0
- package/dist/cli/{chunk-nd1821sk.js → chunk-7ppf1rva.js} +32 -18
- package/dist/cli/chunk-7ppf1rva.js.map +10 -0
- package/dist/cli/{chunk-p804f3pe.js → chunk-8y2kmjv8.js} +6 -6
- package/dist/cli/{chunk-2f7n4qzt.js → chunk-c1y538kd.js} +1 -1
- package/dist/cli/{chunk-1n228xqb.js → chunk-c887fj0s.js} +2 -2
- package/dist/cli/{chunk-y5h4r2y0.js → chunk-cfyg47zg.js} +15 -5
- package/dist/cli/chunk-cfyg47zg.js.map +10 -0
- package/dist/cli/{chunk-cmtcnh9f.js → chunk-dzwehpy7.js} +2 -2
- package/dist/cli/{chunk-h2a48yhd.js → chunk-e6atmn4h.js} +119 -15
- package/dist/cli/{chunk-h2a48yhd.js.map → chunk-e6atmn4h.js.map} +10 -10
- package/dist/cli/{chunk-5ffywsmb.js → chunk-e8xw9qkh.js} +8 -6
- package/dist/cli/{chunk-5ffywsmb.js.map → chunk-e8xw9qkh.js.map} +3 -3
- package/dist/cli/{chunk-ntv4qrt7.js → chunk-e9q7a2ak.js} +14 -14
- package/dist/cli/{chunk-xkz5xbtv.js → chunk-ehweg4fr.js} +2366 -1114
- package/dist/cli/chunk-ehweg4fr.js.map +218 -0
- package/dist/cli/{chunk-zf748avv.js → chunk-ep6nd0rn.js} +176 -281
- package/dist/cli/{chunk-zf748avv.js.map → chunk-ep6nd0rn.js.map} +11 -12
- package/dist/cli/{chunk-yxmk4pec.js → chunk-fdrkhpjy.js} +16 -15
- package/dist/cli/{chunk-yxmk4pec.js.map → chunk-fdrkhpjy.js.map} +3 -3
- package/dist/cli/{chunk-5wj2qwn2.js → chunk-ft9vfys6.js} +4 -4
- package/dist/cli/{chunk-4h8z9vv5.js → chunk-gb420vvz.js} +89 -46
- package/dist/cli/chunk-gb420vvz.js.map +17 -0
- package/dist/cli/{chunk-713mngdq.js → chunk-hebn0tpx.js} +76 -10
- package/dist/cli/chunk-hebn0tpx.js.map +14 -0
- package/dist/cli/{chunk-2n4ps1b3.js → chunk-htrwgabr.js} +40 -22
- package/dist/cli/chunk-htrwgabr.js.map +15 -0
- package/dist/cli/{chunk-zhvd6adm.js → chunk-hx3xqnk9.js} +13 -13
- package/dist/cli/chunk-j134vrp2.js +65 -0
- package/dist/cli/chunk-j134vrp2.js.map +10 -0
- package/dist/cli/{chunk-y4ykbark.js → chunk-kma9f6sf.js} +2 -2
- package/dist/cli/{chunk-py62fewh.js → chunk-mrcccgnh.js} +3 -3
- package/dist/cli/{chunk-2f3ndkpw.js → chunk-n0v7m8ry.js} +3 -3
- package/dist/cli/{chunk-43zcf4gz.js → chunk-ncj7w596.js} +522 -353
- package/dist/cli/chunk-ncj7w596.js.map +35 -0
- package/dist/cli/{chunk-9geymqns.js → chunk-nq1d20w2.js} +14 -14
- package/dist/cli/{chunk-g2z9x01v.js → chunk-nrns10mj.js} +3 -3
- package/dist/cli/{chunk-e7vvk5ya.js → chunk-sds923fa.js} +3 -3
- package/dist/cli/{chunk-cy5bm0j0.js → chunk-t2m5j4pr.js} +2 -2
- package/dist/cli/{chunk-cy5bm0j0.js.map → chunk-t2m5j4pr.js.map} +1 -1
- package/dist/cli/{chunk-yj17vs39.js → chunk-tdqhyxfz.js} +5 -5
- package/dist/cli/{chunk-q3wxxq39.js → chunk-tphac74f.js} +5 -5
- package/dist/cli/{chunk-6bm0xpgd.js → chunk-w1r4vase.js} +2 -2
- 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/component-markdown.d.ts +2 -0
- package/dist/types/ai/openapi-components.d.ts +4 -1
- package/dist/types/astro/integration.d.ts +5 -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 +30 -3
- 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/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 +119 -0
- 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/normalize.d.ts +20 -1
- 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/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 +20 -1
- 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 +42 -21
- 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/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/sources/contentful.d.ts +16 -0
- package/dist/types/sources/github-releases.d.ts +8 -1
- package/dist/types/sources/payload.d.ts +2 -0
- package/dist/types/sources/registry.d.ts +54 -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 +31 -13
- package/docs/advanced/changelog.mdx +11 -0
- package/docs/advanced/skills.mdx +1 -1
- package/docs/cli/audit.mdx +30 -7
- package/docs/cli/doctor.mdx +5 -1
- 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 +1 -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 +3 -2
- 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 +4 -4
- package/docs/content/sources/contentful.mdx +7 -4
- package/docs/content/sources/custom.mdx +7 -3
- package/docs/content/sources/github-releases.mdx +10 -2
- package/docs/content/sources/index.mdx +3 -3
- package/docs/content/sources/notion.mdx +1 -1
- package/docs/content/sources/obsidian.mdx +1 -1
- package/docs/content/sources/payload.mdx +3 -2
- package/docs/content/sources/remote-mdx.mdx +4 -0
- package/docs/content/sources/sanity.mdx +2 -2
- package/docs/content/sources/strapi.mdx +3 -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 +4 -0
- package/docs/discoverability/sitemap-and-robots.mdx +1 -1
- package/docs/references/api-pages.mdx +1 -1
- package/docs/references/asyncapi.mdx +3 -1
- package/docs/references/graphql.mdx +13 -1
- package/docs/references/openapi.mdx +50 -8
- package/package.json +9 -9
- package/skills/blume-migrate/SKILL.md +1 -1
- package/skills/blume-migrate/references/docusaurus.md +4 -4
- package/skills/blume-migrate/references/fumadocs.md +17 -13
- 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/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/astro/generate.ts +78 -48
- package/src/astro/integration.ts +16 -13
- 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/templates.ts +125 -80
- 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 +3 -3
- package/src/cli/prepare.ts +6 -3
- package/src/cli/required-secrets.ts +1 -1
- 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/islands/hooks.ts +27 -1
- package/src/components/layout/PageActions.astro +139 -24
- package/src/components/layout/RootLayout.astro +10 -2
- package/src/components/layout/Search.astro +17 -3
- package/src/components/layout/TableOfContents.astro +24 -17
- 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 +24 -5
- 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/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 +31 -3
- 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/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 +21 -2
- 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/mdx-remote.ts +62 -10
- package/src/core/sources/normalize.ts +227 -41
- package/src/core/sources/notion.ts +7 -1
- 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/platforms/cloudflare.ts +13 -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 +113 -16
- 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 +290 -102
- 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/relative-links.ts +3 -2
- 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/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 +3 -1
- 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/sources/contentful.ts +12 -0
- package/src/sources/github-releases.ts +7 -1
- package/src/sources/shared.ts +3 -0
- package/src/theme/entry.ts +29 -3
- 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-gp84za0h.js.map → chunk-3v3ffbrj.js.map} +0 -0
- /package/dist/cli/{chunk-p804f3pe.js.map → chunk-8y2kmjv8.js.map} +0 -0
- /package/dist/cli/{chunk-2f7n4qzt.js.map → chunk-c1y538kd.js.map} +0 -0
- /package/dist/cli/{chunk-1n228xqb.js.map → chunk-c887fj0s.js.map} +0 -0
- /package/dist/cli/{chunk-cmtcnh9f.js.map → chunk-dzwehpy7.js.map} +0 -0
- /package/dist/cli/{chunk-ntv4qrt7.js.map → chunk-e9q7a2ak.js.map} +0 -0
- /package/dist/cli/{chunk-5wj2qwn2.js.map → chunk-ft9vfys6.js.map} +0 -0
- /package/dist/cli/{chunk-zhvd6adm.js.map → chunk-hx3xqnk9.js.map} +0 -0
- /package/dist/cli/{chunk-y4ykbark.js.map → chunk-kma9f6sf.js.map} +0 -0
- /package/dist/cli/{chunk-py62fewh.js.map → chunk-mrcccgnh.js.map} +0 -0
- /package/dist/cli/{chunk-2f3ndkpw.js.map → chunk-n0v7m8ry.js.map} +0 -0
- /package/dist/cli/{chunk-9geymqns.js.map → chunk-nq1d20w2.js.map} +0 -0
- /package/dist/cli/{chunk-g2z9x01v.js.map → chunk-nrns10mj.js.map} +0 -0
- /package/dist/cli/{chunk-e7vvk5ya.js.map → chunk-sds923fa.js.map} +0 -0
- /package/dist/cli/{chunk-yj17vs39.js.map → chunk-tdqhyxfz.js.map} +0 -0
- /package/dist/cli/{chunk-q3wxxq39.js.map → chunk-tphac74f.js.map} +0 -0
- /package/dist/cli/{chunk-6bm0xpgd.js.map → chunk-w1r4vase.js.map} +0 -0
package/src/eval/prompts.ts
CHANGED
|
@@ -40,8 +40,11 @@ Reply with ONLY this JSON object on a single line, no markdown fences:
|
|
|
40
40
|
{"pass": true|false, "score": 0.0-1.0, "missing": ["expected facts absent or contradicted"], "notes": "one sentence"}`;
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
-
/**
|
|
44
|
-
|
|
43
|
+
/**
|
|
44
|
+
* The `--fix` handoff prompt: where the report is, the ground rules, and the
|
|
45
|
+
* `blume eval` command that verifies the fixes (the run's own flags).
|
|
46
|
+
*/
|
|
47
|
+
export const evalFixPrompt = (reportPath: string, command: string): string =>
|
|
45
48
|
`Fix the documentation gaps found by \`blume eval\` in this project.
|
|
46
49
|
|
|
47
50
|
The full report is at ${reportPath}. It is JSON: each entry in \`eval.results\` with status "fail" is one question the documentation could not answer. Each carries the \`question\`, the \`expected\` facts, the judge's \`missing\` facts, and the reader agent's \`answer\` (what the docs currently convey). The matching \`diagnostics\` entry names the source \`file\` of the page that should answer it.
|
|
@@ -51,7 +54,7 @@ Work through every failed question:
|
|
|
51
54
|
2. Edit the documentation so it states the missing facts explicitly. Add prose, not filler; keep the page's voice.
|
|
52
55
|
3. Never delete questions from the evals file or weaken expected facts.
|
|
53
56
|
|
|
54
|
-
When you are done, run \`
|
|
57
|
+
When you are done, run \`${command}\` to verify, and repeat until every question passes.`;
|
|
55
58
|
|
|
56
59
|
/** The `eval init` prompt: draft a starter evals file from the docs. */
|
|
57
60
|
export const initPrompt = (evalsPath: string): string =>
|
package/src/eval/report.ts
CHANGED
|
@@ -10,27 +10,40 @@ import { duration, money, seconds } from "../cli/report-format.ts";
|
|
|
10
10
|
import { countBySeverity } from "../core/diagnostics.ts";
|
|
11
11
|
import type { EvalResult, QuestionResult, QuestionStatus } from "./run.ts";
|
|
12
12
|
|
|
13
|
+
/** How a question reads in the report: its status, or `warn` for a warned miss. */
|
|
14
|
+
type ShownStatus = QuestionStatus | "warn";
|
|
15
|
+
|
|
13
16
|
const GLYPH = {
|
|
14
17
|
error: "!",
|
|
15
18
|
fail: "✖",
|
|
16
19
|
pass: "✔",
|
|
17
20
|
skip: "⊘",
|
|
18
|
-
|
|
21
|
+
warn: "⚠",
|
|
22
|
+
} satisfies Record<ShownStatus, string>;
|
|
19
23
|
|
|
20
24
|
const STATUS_COLOR = {
|
|
21
25
|
error: colors.yellow,
|
|
22
26
|
fail: colors.red,
|
|
23
27
|
pass: colors.green,
|
|
24
28
|
skip: colors.dim,
|
|
25
|
-
|
|
29
|
+
warn: colors.yellow,
|
|
30
|
+
} satisfies Record<ShownStatus, ColorFunction>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* A failed `severity: warning` question: its miss never counts against the
|
|
34
|
+
* gate, so the report shows and counts it as a warning, not a failure.
|
|
35
|
+
*/
|
|
36
|
+
const warned = (result: QuestionResult): boolean =>
|
|
37
|
+
result.status === "fail" && result.severity === "warning";
|
|
26
38
|
|
|
27
39
|
/** Longest id gets the room; everything shorter aligns to it. */
|
|
28
40
|
const ID_PAD = 28;
|
|
29
41
|
|
|
30
42
|
/** One question's progress/report line: glyph, id, status, score, time, cost. */
|
|
31
43
|
export const questionLine = (result: QuestionResult): string => {
|
|
32
|
-
const
|
|
33
|
-
const
|
|
44
|
+
const shown: ShownStatus = warned(result) ? "warn" : result.status;
|
|
45
|
+
const color = STATUS_COLOR[shown];
|
|
46
|
+
const glyph = color(GLYPH[shown]);
|
|
34
47
|
const id = result.id.padEnd(ID_PAD);
|
|
35
48
|
if (result.status === "skip") {
|
|
36
49
|
return ` ${glyph} ${id} ${colors.dim("skipped")}`;
|
|
@@ -38,7 +51,7 @@ export const questionLine = (result: QuestionResult): string => {
|
|
|
38
51
|
const score = result.score === undefined ? "" : result.score.toFixed(2);
|
|
39
52
|
const cost = money(result.costUsd);
|
|
40
53
|
const cells = [
|
|
41
|
-
color(
|
|
54
|
+
color(shown),
|
|
42
55
|
score,
|
|
43
56
|
colors.dim(seconds(result.durationMs)),
|
|
44
57
|
cost === "" ? "" : colors.dim(cost),
|
|
@@ -75,9 +88,12 @@ export const questionDetails = (
|
|
|
75
88
|
/** The one-line totals: `9 passed · 2 failed · 1 skipped · 1m 42s · $0.71`. */
|
|
76
89
|
export const summaryLine = (result: EvalResult): string => {
|
|
77
90
|
const { counts } = result;
|
|
91
|
+
const warnings = result.results.filter(warned).length;
|
|
92
|
+
const failures = counts.fail - warnings;
|
|
78
93
|
const parts = [
|
|
79
94
|
`${counts.pass} passed`,
|
|
80
|
-
|
|
95
|
+
failures > 0 ? `${failures} failed` : "",
|
|
96
|
+
warnings > 0 ? `${warnings} warned` : "",
|
|
81
97
|
counts.error > 0 ? `${counts.error} errored` : "",
|
|
82
98
|
counts.skip > 0 ? `${counts.skip} skipped` : "",
|
|
83
99
|
duration(result.durationMs),
|
|
@@ -98,10 +114,11 @@ export const startLine = (id: string, index: number, total: number): string =>
|
|
|
98
114
|
* `fix:` pointers for failed questions, naming the file that resolves each. A
|
|
99
115
|
* question whose agent run errored gets a `run failed:` line pointing at the
|
|
100
116
|
* question instead: the run never graded the docs, so there's no page to fix.
|
|
117
|
+
* A `severity: warning` question's miss is a warning line instead.
|
|
101
118
|
*/
|
|
102
119
|
export const fixLines = (result: EvalResult, root: string): string[] =>
|
|
103
120
|
result.diagnostics
|
|
104
|
-
.filter((diagnostic) => diagnostic.
|
|
121
|
+
.filter((diagnostic) => diagnostic.severity !== "warning")
|
|
105
122
|
.map((finding) => {
|
|
106
123
|
const site = finding.file
|
|
107
124
|
? `${relative(root, finding.file)}${finding.line ? `:${finding.line}` : ""}`
|
|
@@ -113,10 +130,13 @@ export const fixLines = (result: EvalResult, root: string): string[] =>
|
|
|
113
130
|
return ` ${label} ${site} ${colors.dim(finding.message)}`;
|
|
114
131
|
});
|
|
115
132
|
|
|
116
|
-
/**
|
|
133
|
+
/**
|
|
134
|
+
* Dim warnings: route hints that no longer match a page, and the misses of
|
|
135
|
+
* `severity: warning` questions.
|
|
136
|
+
*/
|
|
117
137
|
export const warningLines = (result: EvalResult, root: string): string[] =>
|
|
118
138
|
result.diagnostics
|
|
119
|
-
.filter((diagnostic) => diagnostic.
|
|
139
|
+
.filter((diagnostic) => diagnostic.severity === "warning")
|
|
120
140
|
.map((finding) => {
|
|
121
141
|
const site = finding.file
|
|
122
142
|
? ` ${relative(root, finding.file)}${finding.line ? `:${finding.line}` : ""}`
|
|
@@ -140,10 +160,11 @@ export const formatEvalReport = (
|
|
|
140
160
|
}
|
|
141
161
|
lines.push("");
|
|
142
162
|
|
|
143
|
-
// The finding tells the author which file fixes which failure
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
163
|
+
// The finding tells the author which file fixes which failure, warnings
|
|
164
|
+
// first, as the command prints them.
|
|
165
|
+
const findings = [...warningLines(result, root), ...fixLines(result, root)];
|
|
166
|
+
lines.push(...findings);
|
|
167
|
+
if (findings.length > 0) {
|
|
147
168
|
lines.push("");
|
|
148
169
|
}
|
|
149
170
|
|
package/src/eval/run.ts
CHANGED
|
@@ -40,6 +40,8 @@ export interface QuestionResult {
|
|
|
40
40
|
question: string;
|
|
41
41
|
routes: string[];
|
|
42
42
|
score?: number;
|
|
43
|
+
/** The question's severity: a `warning` miss is reported as a warning. */
|
|
44
|
+
severity?: EvalQuestion["severity"];
|
|
43
45
|
status: QuestionStatus;
|
|
44
46
|
}
|
|
45
47
|
|
|
@@ -99,6 +101,7 @@ const errored = (
|
|
|
99
101
|
missing: [],
|
|
100
102
|
question: question.question,
|
|
101
103
|
routes: question.routes,
|
|
104
|
+
severity: question.severity,
|
|
102
105
|
status: "error",
|
|
103
106
|
});
|
|
104
107
|
|
|
@@ -177,6 +180,7 @@ const runQuestion = async (
|
|
|
177
180
|
question: question.question,
|
|
178
181
|
routes: question.routes,
|
|
179
182
|
score: verdict.score,
|
|
183
|
+
severity: question.severity,
|
|
180
184
|
status: verdict.pass ? "pass" : "fail",
|
|
181
185
|
};
|
|
182
186
|
};
|
|
@@ -250,6 +254,7 @@ export const runEval = async (options: EvalRunOptions): Promise<EvalResult> => {
|
|
|
250
254
|
missing: [],
|
|
251
255
|
question: question.question,
|
|
252
256
|
routes: question.routes,
|
|
257
|
+
severity: question.severity,
|
|
253
258
|
status: "skip",
|
|
254
259
|
});
|
|
255
260
|
continue;
|
|
@@ -266,8 +266,9 @@ export const relativeLinksPlugin = (
|
|
|
266
266
|
|
|
267
267
|
// A component's string `href` (`<Card href="./install">`) is the same kind
|
|
268
268
|
// of link, and `blume validate` reads it as one. Lowercase elements are
|
|
269
|
-
// raw HTML in a `.md` page, which
|
|
270
|
-
//
|
|
269
|
+
// raw HTML in a `.md` page, which isn't rewritten, so only components are
|
|
270
|
+
// rewritten (validate reads a raw `<a href>` as the browser does); an
|
|
271
|
+
// expression-valued `href={…}` is left alone.
|
|
271
272
|
const rewriteHref = (node: JsxNode, ctx: RelativeLinksContext): void => {
|
|
272
273
|
if (!node.name || !COMPONENT_NAME.test(node.name)) {
|
|
273
274
|
return;
|
|
@@ -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[];
|