blume 2.1.3 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +126 -0
- package/README.md +4 -4
- package/dist/cli/{chunk-74wfhv79.js → chunk-05f21r8h.js} +1 -1
- package/dist/cli/{chunk-9qzgg6mj.js → chunk-1cm0h819.js} +3 -3
- package/dist/cli/{chunk-nbyvgnnm.js → chunk-1rkxzg3n.js} +104 -47
- package/dist/cli/chunk-1rkxzg3n.js.map +36 -0
- package/dist/cli/{chunk-desvm72z.js → chunk-2nr29y95.js} +22 -26
- package/dist/cli/chunk-2nr29y95.js.map +13 -0
- package/dist/cli/{chunk-32khwcb6.js → chunk-49r370mb.js} +42 -69
- package/dist/cli/{chunk-32khwcb6.js.map → chunk-49r370mb.js.map} +9 -10
- package/dist/cli/{chunk-ms9z66nz.js → chunk-4rftmttv.js} +2 -2
- package/dist/cli/{chunk-s0bdnc7v.js → chunk-79bzzkx4.js} +5 -4
- package/dist/cli/chunk-79bzzkx4.js.map +10 -0
- package/dist/cli/{chunk-hvcxng5f.js → chunk-7tekknev.js} +6 -6
- package/dist/cli/{chunk-dv2z0rzf.js → chunk-8b489v1b.js} +38 -13
- package/dist/cli/{chunk-dv2z0rzf.js.map → chunk-8b489v1b.js.map} +5 -4
- package/dist/cli/{chunk-0pmb4tr4.js → chunk-8f8xw4x2.js} +1195 -222
- package/dist/cli/chunk-8f8xw4x2.js.map +44 -0
- package/dist/cli/chunk-96snx8yy.js +329 -0
- package/dist/cli/chunk-96snx8yy.js.map +13 -0
- package/dist/cli/{chunk-ckrcm08h.js → chunk-a3ydrqny.js} +48 -62
- package/dist/cli/{chunk-ckrcm08h.js.map → chunk-a3ydrqny.js.map} +4 -5
- package/dist/cli/{chunk-bxwhtvw8.js → chunk-ahc5gc9h.js} +5 -5
- package/dist/cli/{chunk-dpy8qdjf.js → chunk-as09pafp.js} +73 -182
- package/dist/cli/chunk-as09pafp.js.map +18 -0
- package/dist/cli/{chunk-tfycddbt.js → chunk-bjk964fp.js} +2 -2
- package/dist/cli/{chunk-gfc5fqvf.js → chunk-br0n8n3e.js} +18 -18
- package/dist/cli/{chunk-gfc5fqvf.js.map → chunk-br0n8n3e.js.map} +1 -1
- package/dist/cli/{chunk-297ajdcv.js → chunk-c69tw29j.js} +6 -6
- package/dist/cli/{chunk-297ajdcv.js.map → chunk-c69tw29j.js.map} +3 -3
- package/dist/cli/chunk-cn3dy3hz.js +146 -0
- package/dist/cli/chunk-cn3dy3hz.js.map +11 -0
- package/dist/cli/{chunk-124fvmzq.js → chunk-dm4mg9bj.js} +11064 -8742
- package/dist/cli/{chunk-124fvmzq.js.map → chunk-dm4mg9bj.js.map} +78 -64
- package/dist/cli/{chunk-dqhg54mr.js → chunk-dmx6cezx.js} +1 -1
- package/dist/cli/{chunk-60hz265r.js → chunk-dpcahcrb.js} +24 -11
- package/dist/cli/chunk-dpcahcrb.js.map +10 -0
- package/dist/cli/{chunk-a57yh0h9.js → chunk-dyk3r7jw.js} +7 -7
- package/dist/cli/{chunk-a57yh0h9.js.map → chunk-dyk3r7jw.js.map} +1 -1
- package/dist/cli/{chunk-twy7psdz.js → chunk-enk76qd5.js} +14 -10
- package/dist/cli/chunk-enk76qd5.js.map +10 -0
- package/dist/cli/{chunk-k3gr1nm9.js → chunk-fqy4n3q2.js} +13 -16
- package/dist/cli/{chunk-k3gr1nm9.js.map → chunk-fqy4n3q2.js.map} +3 -3
- package/dist/cli/{chunk-sc93apfs.js → chunk-frk2wdq1.js} +17 -17
- package/dist/cli/{chunk-sc93apfs.js.map → chunk-frk2wdq1.js.map} +2 -2
- package/dist/cli/{chunk-kdpzw38a.js → chunk-gprwej7h.js} +95 -7
- package/dist/cli/chunk-gprwej7h.js.map +11 -0
- package/dist/cli/chunk-kkrteb15.js +157 -0
- package/dist/cli/chunk-kkrteb15.js.map +10 -0
- package/dist/cli/{chunk-ave1wcbr.js → chunk-m9bp2hc1.js} +1 -1
- package/dist/cli/{chunk-58bt3hag.js → chunk-mrvh4c88.js} +4 -4
- package/dist/cli/{chunk-5cmts6dv.js → chunk-p8zg3a2h.js} +7 -7
- package/dist/cli/{chunk-5cmts6dv.js.map → chunk-p8zg3a2h.js.map} +1 -1
- package/dist/cli/{chunk-0w6hs479.js → chunk-rkqae7kh.js} +30 -18
- package/dist/cli/chunk-rkqae7kh.js.map +10 -0
- package/dist/cli/{chunk-bqxaqn5p.js → chunk-t5xx74rd.js} +8 -8
- package/dist/cli/{chunk-bqxaqn5p.js.map → chunk-t5xx74rd.js.map} +1 -1
- package/dist/cli/{chunk-1f882900.js → chunk-th0pgaj5.js} +2 -2
- package/dist/cli/{chunk-dhwtqnnd.js → chunk-vnbvhgmd.js} +2 -2
- package/dist/cli/{chunk-gxtg6ed2.js → chunk-vzy9rh6y.js} +21 -21
- package/dist/cli/{chunk-gxtg6ed2.js.map → chunk-vzy9rh6y.js.map} +3 -3
- package/dist/cli/{chunk-j1x92mx9.js → chunk-w6e455pz.js} +1 -1
- package/dist/cli/chunk-wehv26ry.js +155 -0
- package/dist/cli/chunk-wehv26ry.js.map +11 -0
- package/dist/cli/{chunk-y4msrwm9.js → chunk-xgq4eytj.js} +15 -15
- package/dist/cli/{chunk-36bxs9c7.js → chunk-xt0qvz1t.js} +27 -23
- package/dist/cli/chunk-xt0qvz1t.js.map +10 -0
- package/dist/cli/index.js +78 -42
- package/dist/cli/index.js.map +7 -6
- package/dist/types/ai/changelog-components.d.ts +18 -0
- package/dist/types/ai/changelog-markdown.d.ts +12 -0
- package/dist/types/ai/openapi-components.d.ts +7 -7
- package/dist/types/ai/operation-details.d.ts +39 -0
- package/dist/types/ai/relative-links.d.ts +3 -1
- package/dist/types/ai/serializers.d.ts +6 -4
- package/dist/types/analytics/head.d.ts +4 -2
- package/dist/types/analytics/script.d.ts +8 -2
- package/dist/types/components/content/youtube.d.ts +20 -0
- package/dist/types/components/layout/nav-utils.d.ts +8 -0
- package/dist/types/components/openapi/example-panels.d.ts +33 -0
- package/dist/types/components/openapi/helpers.d.ts +53 -11
- package/dist/types/components/openapi/snippets.d.ts +7 -1
- package/dist/types/core/alert-diagnostics.d.ts +10 -0
- package/dist/types/core/builtin-tags.d.ts +16 -0
- package/dist/types/core/changelog-list.d.ts +48 -0
- package/dist/types/core/closest-match.d.ts +6 -0
- package/dist/types/core/code-fence-diagnostics.d.ts +18 -0
- package/dist/types/core/code-fences.d.ts +8 -1
- package/dist/types/core/config-input.d.ts +1 -1
- package/dist/types/core/content-assets.d.ts +16 -18
- package/dist/types/core/diagnostics.d.ts +14 -0
- package/dist/types/core/directive-diagnostics.d.ts +22 -8
- package/dist/types/core/former-routes.d.ts +14 -0
- package/dist/types/core/github.d.ts +5 -4
- package/dist/types/core/heading-markers.d.ts +65 -2
- package/dist/types/core/hidden-pages.d.ts +3 -0
- package/dist/types/core/includes.d.ts +23 -0
- package/dist/types/core/last-modified.d.ts +18 -6
- package/dist/types/core/links.d.ts +42 -2
- package/dist/types/core/load-module.d.ts +19 -4
- package/dist/types/core/manifest.d.ts +14 -1
- package/dist/types/core/markdown-mirrors.d.ts +22 -0
- package/dist/types/core/mdx-elements.d.ts +32 -0
- package/dist/types/core/mdx-syntax.d.ts +100 -0
- package/dist/types/core/meta-diagnostics.d.ts +7 -0
- package/dist/types/core/meta.d.ts +32 -5
- package/dist/types/core/nav-diagnostics.d.ts +9 -2
- package/dist/types/core/navigation.d.ts +13 -0
- package/dist/types/core/ordering-prefix.d.ts +6 -0
- package/dist/types/core/pinned-ids.d.ts +23 -0
- package/dist/types/core/project-graph.d.ts +9 -1
- package/dist/types/core/redirect-patterns.d.ts +18 -0
- package/dist/types/core/relative-files.d.ts +31 -0
- package/dist/types/core/schema.d.ts +267 -7
- package/dist/types/core/sources/normalize.d.ts +101 -9
- package/dist/types/core/sources/types.d.ts +16 -0
- package/dist/types/core/static-files.d.ts +10 -0
- package/dist/types/core/static-host.d.ts +42 -0
- package/dist/types/core/svg-images.d.ts +26 -0
- package/dist/types/core/syntax-diagnostics.d.ts +14 -0
- package/dist/types/core/types.d.ts +42 -1
- package/dist/types/deploy/generated-files.d.ts +41 -0
- package/dist/types/deploy/redirects.d.ts +21 -7
- package/dist/types/markdown/code-title.d.ts +9 -5
- package/dist/types/markdown/directives.d.ts +8 -0
- package/dist/types/markdown/fence-language.d.ts +51 -0
- package/dist/types/markdown/fence-meta.d.ts +5 -0
- package/dist/types/markdown/github-alerts.d.ts +35 -0
- package/dist/types/openapi/asyncapi.d.ts +6 -5
- package/dist/types/openapi/checks.d.ts +10 -5
- package/dist/types/openapi/model.d.ts +55 -8
- package/dist/types/openapi/parse.d.ts +4 -1
- package/dist/types/sources/filesystem.d.ts +14 -4
- package/dist/types/sources/mdx-remote.d.ts +6 -3
- package/dist/types/sources/registry.d.ts +3 -3
- package/dist/types/theme/color.d.ts +33 -0
- package/dist/types/theme/palette.d.ts +122 -0
- package/docs/01-quickstart.mdx +1 -1
- package/docs/02-deployment.mdx +23 -8
- package/docs/04-migrating.mdx +16 -4
- package/docs/advanced/changelog.mdx +27 -1
- package/docs/advanced/skills.mdx +1 -1
- package/docs/cli/audit.mdx +1 -1
- package/docs/cli/doctor.mdx +2 -2
- package/docs/cli/evals.mdx +1 -1
- package/docs/cli/index.mdx +4 -2
- package/docs/cli/validate.mdx +10 -7
- package/docs/configuration/analytics.mdx +2 -0
- package/docs/configuration/customization.mdx +3 -1
- package/docs/configuration/index.mdx +2 -2
- package/docs/configuration/theming.mdx +5 -1
- package/docs/content/components.mdx +26 -6
- package/docs/content/frontmatter.mdx +1 -1
- package/docs/content/includes.mdx +6 -2
- package/docs/content/index.mdx +11 -1
- package/docs/content/islands.mdx +2 -0
- package/docs/content/meta.mdx +9 -3
- package/docs/content/navigation.mdx +14 -6
- package/docs/content/sources/obsidian.mdx +1 -1
- package/docs/content/sources/remote-mdx.mdx +3 -1
- package/docs/content/syntax.mdx +76 -8
- package/docs/content/variables.mdx +3 -1
- package/docs/discoverability/agent-discovery.mdx +1 -1
- package/docs/discoverability/json-api.mdx +1 -1
- package/docs/discoverability/llms-txt.mdx +1 -1
- package/docs/discoverability/markdown.mdx +2 -2
- package/docs/discoverability/open-graph.mdx +1 -1
- package/docs/discoverability/sitemap-and-robots.mdx +1 -1
- package/docs/references/graphql.mdx +2 -0
- package/docs/references/openapi.mdx +45 -7
- package/package.json +4 -4
- package/skills/blume/SKILL.md +2 -2
- package/skills/blume-migrate/SKILL.md +49 -27
- package/skills/blume-migrate/references/docsify.md +262 -0
- package/skills/blume-migrate/references/docus.md +298 -0
- package/skills/blume-migrate/references/docusaurus.md +3 -3
- package/skills/blume-migrate/references/fern.md +327 -0
- package/skills/blume-migrate/references/fumadocs.md +1 -1
- package/skills/blume-migrate/references/gitbook.md +267 -0
- package/skills/blume-migrate/references/github-wiki.md +259 -0
- package/skills/blume-migrate/references/jekyll.md +253 -0
- package/skills/blume-migrate/references/mdbook.md +256 -0
- package/skills/blume-migrate/references/mintlify.md +5 -5
- package/skills/blume-migrate/references/mkdocs.md +236 -0
- package/skills/blume-migrate/references/monorepo.md +20 -8
- package/skills/blume-migrate/references/nextra.md +2 -2
- package/skills/blume-migrate/references/readme.md +257 -0
- package/skills/blume-migrate/references/redocly.md +198 -0
- package/skills/blume-migrate/references/starlight.md +3 -3
- package/skills/blume-migrate/references/vitepress.md +204 -0
- package/skills/blume-migrate/references/vuepress.md +271 -0
- package/skills/blume-migrate/scripts/docsify-codemod.mjs +2387 -0
- package/skills/blume-migrate/scripts/docus-codemod.mjs +2542 -0
- package/skills/blume-migrate/scripts/fern-codemod.mjs +2633 -0
- package/skills/blume-migrate/scripts/github-wiki-codemod.mjs +2808 -0
- package/skills/blume-migrate/scripts/include-excerpts.mjs +649 -0
- package/skills/blume-migrate/scripts/jekyll-codemod.mjs +3649 -0
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +17 -13
- package/skills/blume-migrate/scripts/mkdocs-codemod.mjs +2526 -0
- package/skills/blume-migrate/scripts/operation-routes.mjs +508 -0
- package/skills/blume-migrate/scripts/pin-heading-ids.mjs +1049 -0
- package/skills/blume-migrate/scripts/readme-codemod.mjs +4139 -0
- package/skills/blume-migrate/scripts/vitepress-codemod.mjs +1511 -0
- package/src/ai/changelog-components.ts +24 -0
- package/src/ai/changelog-markdown.ts +45 -41
- package/src/ai/component-markdown.ts +22 -9
- package/src/ai/llms.ts +36 -5
- package/src/ai/markdown.ts +2 -2
- package/src/ai/openapi-components.ts +25 -9
- package/src/ai/operation-details.ts +208 -0
- package/src/ai/relative-links.ts +48 -8
- package/src/ai/serializers.ts +7 -6
- package/src/ai/site-skill.ts +6 -2
- package/src/analytics/head.ts +11 -5
- package/src/analytics/script.ts +13 -3
- package/src/astro/generate.ts +32 -6
- package/src/astro/index.ts +1 -0
- package/src/astro/integration.ts +27 -1
- package/src/astro/mdx-source-errors.ts +46 -0
- package/src/astro/templates.ts +89 -185
- package/src/audit/checks/redirects.ts +5 -1
- package/src/audit/redirects.ts +36 -19
- package/src/audit/report.ts +22 -9
- package/src/audit/run.ts +12 -28
- package/src/audit/terms.ts +23 -0
- package/src/audit/url.ts +2 -2
- package/src/cli/build-failure.ts +256 -7
- package/src/cli/command-meta.ts +1 -1
- package/src/cli/commands/audit.ts +1 -1
- package/src/cli/commands/build.ts +12 -3
- package/src/cli/commands/check.ts +13 -2
- package/src/cli/commands/dev.ts +1 -1
- package/src/cli/commands/doctor.ts +4 -2
- package/src/cli/commands/init.ts +3 -3
- package/src/cli/commands/preview.ts +17 -1
- package/src/cli/commands/validate.ts +22 -10
- package/src/cli/prepare.ts +1 -1
- package/src/cli/preview-pages.ts +161 -0
- package/src/cli/unknown-flags.ts +1 -42
- package/src/components/code-copy.ts +51 -0
- package/src/components/code-language.ts +75 -0
- package/src/components/content/AccordionItem.astro +16 -1
- package/src/components/content/Callout.astro +11 -1
- package/src/components/content/Changelog.astro +189 -0
- package/src/components/content/CodeBlock.astro +8 -11
- package/src/components/content/Tabs.astro +16 -2
- package/src/components/content/YouTube.astro +6 -4
- package/src/components/content/code-block.ts +33 -0
- package/src/components/content/content-strings.ts +5 -1
- package/src/components/content/inline-markdown.ts +35 -8
- package/src/components/content/youtube.ts +49 -7
- package/src/components/layout/Analytics.astro +5 -1
- package/src/components/layout/NavTree.astro +8 -3
- package/src/components/layout/RootLayout.astro +18 -31
- package/src/components/layout/nav-utils.ts +14 -0
- package/src/components/layout/search/pagefind.ts +2 -1
- package/src/components/openapi/Operation.astro +4 -0
- package/src/components/openapi/PanelTabs.astro +9 -3
- package/src/components/openapi/RequestBody.astro +42 -8
- package/src/components/openapi/RequestPanel.astro +34 -53
- package/src/components/openapi/example-panels.ts +110 -0
- package/src/components/openapi/helpers.ts +195 -20
- package/src/components/openapi/operation-model.ts +63 -26
- package/src/components/openapi/request.ts +13 -0
- package/src/components/openapi/snippets.ts +14 -2
- package/src/core/alert-diagnostics.ts +89 -0
- package/src/core/builtin-tags.ts +76 -0
- package/src/core/changelog-list.ts +87 -0
- package/src/core/closest-match.ts +41 -0
- package/src/core/code-fence-diagnostics.ts +267 -0
- package/src/core/code-fences.ts +43 -7
- package/src/core/component-diagnostics.ts +65 -12
- package/src/core/config-input.ts +1 -1
- package/src/core/config.ts +46 -1
- package/src/core/content-assets.ts +64 -144
- package/src/core/diagnostics.ts +36 -3
- package/src/core/directive-diagnostics.ts +265 -47
- package/src/core/former-routes.ts +46 -0
- package/src/core/git-ignored.ts +53 -0
- package/src/core/github.ts +5 -4
- package/src/core/heading-markers.ts +156 -2
- package/src/core/hidden-pages.ts +64 -0
- package/src/core/includes.ts +181 -41
- package/src/core/last-modified.ts +47 -10
- package/src/core/links.ts +438 -83
- package/src/core/load-module.ts +98 -9
- package/src/core/manifest.ts +12 -9
- package/src/core/markdown-mirrors.ts +52 -0
- package/src/core/mdx-elements.ts +153 -0
- package/src/core/mdx-syntax.ts +377 -0
- package/src/core/meta-diagnostics.ts +134 -0
- package/src/core/meta.ts +154 -55
- package/src/core/nav-diagnostics.ts +16 -4
- package/src/core/navigation.ts +70 -10
- package/src/core/ordering-prefix.ts +22 -2
- package/src/core/pinned-ids.ts +141 -0
- package/src/core/project-graph.ts +221 -29
- package/src/core/redirect-patterns.ts +41 -0
- package/src/core/relative-files.ts +145 -0
- package/src/core/schema.ts +17 -3
- package/src/core/sources/mdx-remote.ts +75 -14
- package/src/core/sources/normalize.ts +885 -179
- package/src/core/sources/notion.ts +5 -4
- package/src/core/sources/obsidian.ts +2 -5
- package/src/core/sources/types.ts +17 -0
- package/src/core/static-files.ts +40 -0
- package/src/core/static-host.ts +125 -0
- package/src/core/svg-images.ts +98 -0
- package/src/core/syntax-diagnostics.ts +491 -0
- package/src/core/types.ts +43 -1
- package/src/deploy/artifacts.ts +2 -28
- package/src/deploy/generated-files.ts +128 -0
- package/src/deploy/platforms/vercel.ts +25 -1
- package/src/deploy/redirects.ts +43 -16
- package/src/deploy/sitemap.ts +2 -1
- package/src/markdown/base-links.ts +87 -6
- package/src/markdown/code-title.ts +29 -13
- package/src/markdown/content-assets.ts +210 -0
- package/src/markdown/directives.ts +69 -5
- package/src/markdown/fence-language.ts +106 -0
- package/src/markdown/fence-meta.ts +42 -0
- package/src/markdown/github-alerts.ts +172 -0
- package/src/markdown/heading-anchors.ts +265 -56
- package/src/markdown/include.ts +30 -3
- package/src/markdown/index.ts +145 -40
- package/src/markdown/prompt-text.ts +95 -0
- package/src/markdown/relative-links.ts +37 -2
- package/src/markdown/route-snapshot.ts +22 -12
- package/src/markdown/svg-fallback.ts +51 -0
- package/src/markdown/title-heading.ts +134 -0
- package/src/migrate/migrate.ts +127 -2
- package/src/og/card.ts +14 -4
- package/src/openapi/asyncapi.ts +14 -6
- package/src/openapi/checks.ts +160 -15
- package/src/openapi/model.ts +172 -33
- package/src/openapi/parse.ts +13 -4
- package/src/openapi/render-mdx.ts +55 -9
- package/src/openapi/source.ts +87 -6
- package/src/registry/eject.ts +0 -1
- package/src/search/documents.ts +10 -2
- package/src/sources/filesystem.ts +28 -3
- package/src/sources/mdx-remote.ts +7 -15
- package/src/theme/color.ts +14 -0
- package/src/theme/entry.ts +22 -1
- package/src/theme/palette.ts +74 -1
- package/dist/cli/chunk-0pmb4tr4.js.map +0 -35
- package/dist/cli/chunk-0w6hs479.js.map +0 -10
- package/dist/cli/chunk-150jk7km.js +0 -52
- package/dist/cli/chunk-150jk7km.js.map +0 -10
- package/dist/cli/chunk-36bxs9c7.js.map +0 -10
- package/dist/cli/chunk-53jqw8z0.js +0 -89
- package/dist/cli/chunk-53jqw8z0.js.map +0 -10
- package/dist/cli/chunk-60hz265r.js.map +0 -10
- package/dist/cli/chunk-desvm72z.js.map +0 -14
- package/dist/cli/chunk-dpy8qdjf.js.map +0 -17
- package/dist/cli/chunk-es8h0v3j.js +0 -441
- package/dist/cli/chunk-es8h0v3j.js.map +0 -11
- package/dist/cli/chunk-kdpzw38a.js.map +0 -11
- package/dist/cli/chunk-nbyvgnnm.js.map +0 -36
- package/dist/cli/chunk-s0bdnc7v.js.map +0 -10
- package/dist/cli/chunk-twy7psdz.js.map +0 -10
- package/dist/cli/chunk-ydp3065p.js +0 -64
- package/dist/cli/chunk-ydp3065p.js.map +0 -10
- /package/dist/cli/{chunk-74wfhv79.js.map → chunk-05f21r8h.js.map} +0 -0
- /package/dist/cli/{chunk-9qzgg6mj.js.map → chunk-1cm0h819.js.map} +0 -0
- /package/dist/cli/{chunk-ms9z66nz.js.map → chunk-4rftmttv.js.map} +0 -0
- /package/dist/cli/{chunk-hvcxng5f.js.map → chunk-7tekknev.js.map} +0 -0
- /package/dist/cli/{chunk-bxwhtvw8.js.map → chunk-ahc5gc9h.js.map} +0 -0
- /package/dist/cli/{chunk-tfycddbt.js.map → chunk-bjk964fp.js.map} +0 -0
- /package/dist/cli/{chunk-dqhg54mr.js.map → chunk-dmx6cezx.js.map} +0 -0
- /package/dist/cli/{chunk-ave1wcbr.js.map → chunk-m9bp2hc1.js.map} +0 -0
- /package/dist/cli/{chunk-58bt3hag.js.map → chunk-mrvh4c88.js.map} +0 -0
- /package/dist/cli/{chunk-1f882900.js.map → chunk-th0pgaj5.js.map} +0 -0
- /package/dist/cli/{chunk-dhwtqnnd.js.map → chunk-vnbvhgmd.js.map} +0 -0
- /package/dist/cli/{chunk-j1x92mx9.js.map → chunk-w6e455pz.js.map} +0 -0
- /package/dist/cli/{chunk-y4msrwm9.js.map → chunk-xgq4eytj.js.map} +0 -0
|
@@ -159,6 +159,7 @@ const model =
|
|
|
159
159
|
parameters: params,
|
|
160
160
|
path: ref.path,
|
|
161
161
|
requestBody,
|
|
162
|
+
responses,
|
|
162
163
|
schemas,
|
|
163
164
|
security,
|
|
164
165
|
// An operation's own `servers` override its path item's, which
|
|
@@ -268,6 +269,7 @@ const callbackParts = (entry: CallbackEntry) => ({
|
|
|
268
269
|
<ParametersTable parameters={params} schemas={schemas} />
|
|
269
270
|
{requestBody && (
|
|
270
271
|
<RequestBody
|
|
272
|
+
components={components}
|
|
271
273
|
direction={bodyDirection}
|
|
272
274
|
expandAll={spec.expandSchemas}
|
|
273
275
|
requestBody={requestBody}
|
|
@@ -321,9 +323,11 @@ const callbackParts = (entry: CallbackEntry) => ({
|
|
|
321
323
|
/>
|
|
322
324
|
{parts.requestBody && (
|
|
323
325
|
<RequestBody
|
|
326
|
+
components={components}
|
|
324
327
|
direction="response"
|
|
325
328
|
expandAll={spec.expandSchemas}
|
|
326
329
|
level={4}
|
|
330
|
+
name={`callback-${entry.key}`}
|
|
327
331
|
requestBody={parts.requestBody}
|
|
328
332
|
schemas={schemas}
|
|
329
333
|
/>
|
|
@@ -28,16 +28,22 @@ interface Props {
|
|
|
28
28
|
copy?: boolean;
|
|
29
29
|
/** Render tab labels in a monospace face (status codes). */
|
|
30
30
|
mono?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Names this tab set in its element ids, for a heading that repeats on
|
|
33
|
+
* the page (a callback's request body examples). Defaults to the heading.
|
|
34
|
+
*/
|
|
35
|
+
name?: string;
|
|
31
36
|
}
|
|
32
37
|
|
|
33
|
-
const { heading, panels, copy = false, mono = false } =
|
|
38
|
+
const { heading, panels, copy = false, mono = false, name = heading } =
|
|
39
|
+
Astro.props;
|
|
34
40
|
|
|
35
41
|
// A stable per-instance id prefix so tab/tabpanel pairs can reference each
|
|
36
42
|
// other without colliding across the multiple PanelTabs on one page (the
|
|
37
|
-
//
|
|
43
|
+
// name is unique per tab set; panel keys are unique within a set).
|
|
38
44
|
const NON_ID = /[^a-z0-9]+/gu;
|
|
39
45
|
const idFor = (kind: "tab" | "panel", key: string): string =>
|
|
40
|
-
["panel-tabs",
|
|
46
|
+
["panel-tabs", name, kind, key]
|
|
41
47
|
.map((part) => part.toLowerCase().replace(NON_ID, "-"))
|
|
42
48
|
.join("-");
|
|
43
49
|
|
|
@@ -1,22 +1,31 @@
|
|
|
1
1
|
---
|
|
2
|
+
import data from "blume:data";
|
|
3
|
+
|
|
4
|
+
import { highlightCode } from "../../markdown/index.ts";
|
|
2
5
|
import Description from "./Description.astro";
|
|
3
|
-
import
|
|
6
|
+
import { preferredMedia } from "./example-panels.ts";
|
|
7
|
+
import {
|
|
8
|
+
type ComponentsLike,
|
|
9
|
+
type MediaLike,
|
|
10
|
+
namedExamples,
|
|
11
|
+
type SchemaLike,
|
|
12
|
+
toJson,
|
|
13
|
+
} from "./helpers.ts";
|
|
14
|
+
import PanelTabs from "./PanelTabs.astro";
|
|
4
15
|
import type { SchemaDirection } from "./schema-tree.ts";
|
|
5
16
|
import SchemaTable from "./SchemaTable.astro";
|
|
6
17
|
|
|
7
|
-
interface MediaTypeLike {
|
|
8
|
-
schema?: SchemaLike;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
18
|
interface RequestBodyLike {
|
|
12
19
|
description?: string;
|
|
13
20
|
required?: boolean;
|
|
14
|
-
content?: Record<string,
|
|
21
|
+
content?: Record<string, MediaLike>;
|
|
15
22
|
}
|
|
16
23
|
|
|
17
24
|
interface Props {
|
|
18
25
|
requestBody: RequestBodyLike;
|
|
19
26
|
schemas: Record<string, SchemaLike>;
|
|
27
|
+
/** The document's `components`, which `$ref`'d examples resolve against. */
|
|
28
|
+
components?: ComponentsLike;
|
|
20
29
|
expandAll?: boolean;
|
|
21
30
|
/** The heading's level: 2 on an operation, deeper inside a callback. */
|
|
22
31
|
level?: number;
|
|
@@ -25,20 +34,38 @@ interface Props {
|
|
|
25
34
|
* callback, which the API sends, reads like a response instead.
|
|
26
35
|
*/
|
|
27
36
|
direction?: SchemaDirection;
|
|
37
|
+
/** Names the body's example tabs in their element ids (one per callback). */
|
|
38
|
+
name?: string;
|
|
28
39
|
}
|
|
29
40
|
|
|
30
41
|
const {
|
|
31
42
|
requestBody,
|
|
32
43
|
schemas,
|
|
44
|
+
components,
|
|
33
45
|
expandAll = false,
|
|
34
46
|
level = 2,
|
|
35
47
|
direction = "request",
|
|
48
|
+
name = "request-body",
|
|
36
49
|
} = Astro.props;
|
|
37
50
|
|
|
38
|
-
const
|
|
39
|
-
const chosen = entries.find(([type]) => type.includes("json")) ?? entries[0];
|
|
51
|
+
const chosen = preferredMedia(requestBody.content);
|
|
40
52
|
const contentType = chosen?.[0] ?? "application/json";
|
|
41
53
|
const schema = chosen?.[1]?.schema ?? {};
|
|
54
|
+
|
|
55
|
+
// A body whose `examples` map names two or more shows each, under its name:
|
|
56
|
+
// the code samples and Try it start from the first one only.
|
|
57
|
+
const examplePanels = await Promise.all(
|
|
58
|
+
(chosen ? namedExamples(chosen[1], schemas, direction, components) : []).map(
|
|
59
|
+
async (example) => ({
|
|
60
|
+
html: await highlightCode(toJson(example.value), "json", {
|
|
61
|
+
icons: false,
|
|
62
|
+
themes: data.config.codeThemes,
|
|
63
|
+
}),
|
|
64
|
+
key: example.key,
|
|
65
|
+
label: example.label,
|
|
66
|
+
})
|
|
67
|
+
)
|
|
68
|
+
);
|
|
42
69
|
---
|
|
43
70
|
|
|
44
71
|
<section class:list={[level > 2 ? "mt-5" : "mt-8"]}>
|
|
@@ -72,4 +99,11 @@ const schema = chosen?.[1]?.schema ?? {};
|
|
|
72
99
|
schemas={schemas}
|
|
73
100
|
/>
|
|
74
101
|
</div>
|
|
102
|
+
{
|
|
103
|
+
examplePanels.length > 0 && (
|
|
104
|
+
<div class="not-prose mt-4">
|
|
105
|
+
<PanelTabs copy heading="Examples" name={`${name}-examples`} panels={examplePanels} />
|
|
106
|
+
</div>
|
|
107
|
+
)
|
|
108
|
+
}
|
|
75
109
|
</section>
|
|
@@ -4,27 +4,21 @@ import data from "blume:data";
|
|
|
4
4
|
import { highlightCode } from "../../markdown/index.ts";
|
|
5
5
|
import {
|
|
6
6
|
type ComponentsLike,
|
|
7
|
-
|
|
7
|
+
type MediaLike,
|
|
8
8
|
type SchemaLike,
|
|
9
|
-
type SpecValue,
|
|
10
9
|
toJson,
|
|
11
10
|
} from "./helpers.ts";
|
|
12
11
|
import type { CustomCodeSample } from "./code-samples.ts";
|
|
12
|
+
import {
|
|
13
|
+
type ExamplePanel,
|
|
14
|
+
payloadPanels as payloadExamples,
|
|
15
|
+
type ResponseLike,
|
|
16
|
+
responsePanels as responseExamples,
|
|
17
|
+
} from "./example-panels.ts";
|
|
13
18
|
import { languageSamplePanels } from "./sample-panels.ts";
|
|
14
19
|
import PanelTabs from "./PanelTabs.astro";
|
|
15
20
|
import type { RequestSample, SampleLanguage } from "./snippets.ts";
|
|
16
21
|
|
|
17
|
-
interface MediaTypeLike {
|
|
18
|
-
schema?: SchemaLike;
|
|
19
|
-
example?: SpecValue;
|
|
20
|
-
examples?: SpecValue;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface ResponseLike {
|
|
24
|
-
description?: string;
|
|
25
|
-
content?: Record<string, MediaTypeLike>;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
22
|
interface Props {
|
|
29
23
|
/** The document's `components`, which `$ref`'d examples resolve against. */
|
|
30
24
|
components?: ComponentsLike;
|
|
@@ -34,7 +28,7 @@ interface Props {
|
|
|
34
28
|
* A webhook's body: the rail shows it as a "Payload" example per media
|
|
35
29
|
* type in place of client code, since the API sends the request.
|
|
36
30
|
*/
|
|
37
|
-
payload?: Record<string,
|
|
31
|
+
payload?: Record<string, MediaLike>;
|
|
38
32
|
sample?: RequestSample;
|
|
39
33
|
languages?: SampleLanguage[];
|
|
40
34
|
responses: Record<string, ResponseLike>;
|
|
@@ -51,18 +45,26 @@ const {
|
|
|
51
45
|
schemas,
|
|
52
46
|
} = Astro.props;
|
|
53
47
|
|
|
54
|
-
/**
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
const
|
|
61
|
-
return
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
48
|
+
/**
|
|
49
|
+
* One example tab, highlighted. A payload that isn't JSON shows a string
|
|
50
|
+
* example as written; a response example always reads as JSON.
|
|
51
|
+
*/
|
|
52
|
+
const highlightPanel = async (panel: ExamplePanel, asText: boolean) => {
|
|
53
|
+
const { value } = panel;
|
|
54
|
+
const text = asText && typeof value === "string";
|
|
55
|
+
return {
|
|
56
|
+
html:
|
|
57
|
+
value === undefined || value === null
|
|
58
|
+
? null
|
|
59
|
+
: await highlightCode(
|
|
60
|
+
text ? value : toJson(value),
|
|
61
|
+
text ? "text" : "json",
|
|
62
|
+
{ icons: false, themes: data.config.codeThemes }
|
|
63
|
+
),
|
|
64
|
+
key: panel.key,
|
|
65
|
+
label: panel.label,
|
|
66
|
+
text: panel.text,
|
|
67
|
+
};
|
|
66
68
|
};
|
|
67
69
|
|
|
68
70
|
// Hand-written samples carry no `lang` key, so the playground leaves them
|
|
@@ -78,12 +80,9 @@ const customPanels = await Promise.all(
|
|
|
78
80
|
}))
|
|
79
81
|
);
|
|
80
82
|
const payloadPanels = await Promise.all(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
label: type,
|
|
85
|
-
text: "No example payload.",
|
|
86
|
-
}))
|
|
83
|
+
payloadExamples(payload, schemas, components).map((panel) =>
|
|
84
|
+
highlightPanel(panel, !panel.type.includes("json"))
|
|
85
|
+
)
|
|
87
86
|
);
|
|
88
87
|
const requestPanels = [
|
|
89
88
|
...customPanels,
|
|
@@ -91,28 +90,10 @@ const requestPanels = [
|
|
|
91
90
|
? await languageSamplePanels(languages, sample, data.config.codeThemes)
|
|
92
91
|
: []),
|
|
93
92
|
];
|
|
94
|
-
|
|
95
93
|
const responsePanels = await Promise.all(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
type.includes("json")
|
|
100
|
-
)?.[1] ?? Object.values(response.content ?? {})[0];
|
|
101
|
-
const example = media ? responseExample(media, schemas, components) : undefined;
|
|
102
|
-
const html =
|
|
103
|
-
example === undefined || example === null
|
|
104
|
-
? null
|
|
105
|
-
: await highlightCode(toJson(example), "json", {
|
|
106
|
-
icons: false,
|
|
107
|
-
themes: data.config.codeThemes,
|
|
108
|
-
});
|
|
109
|
-
return {
|
|
110
|
-
html,
|
|
111
|
-
key: status,
|
|
112
|
-
label: status,
|
|
113
|
-
text: response.description || "No example response.",
|
|
114
|
-
};
|
|
115
|
-
})
|
|
94
|
+
responseExamples(responses, schemas, components).map((panel) =>
|
|
95
|
+
highlightPanel(panel, false)
|
|
96
|
+
)
|
|
116
97
|
);
|
|
117
98
|
---
|
|
118
99
|
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { namedExamples, responseExample } from "./helpers.ts";
|
|
2
|
+
import type {
|
|
3
|
+
ComponentsLike,
|
|
4
|
+
MediaLike,
|
|
5
|
+
SchemaLike,
|
|
6
|
+
SpecValue,
|
|
7
|
+
} from "./helpers.ts";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The example tabs of an operation's rail (`RequestPanel.astro`): one per
|
|
11
|
+
* response status, or per webhook payload media type — and one per named
|
|
12
|
+
* example where its `examples` map names two or more, labeled by the
|
|
13
|
+
* example's `summary` (else its key) after the status or media type, so
|
|
14
|
+
* every recorded example shows, under its name. Values are left for the
|
|
15
|
+
* component to highlight.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** One tab before highlighting. */
|
|
19
|
+
export interface ExamplePanel {
|
|
20
|
+
key: string;
|
|
21
|
+
label: string;
|
|
22
|
+
/** The status or media type the tab is under. */
|
|
23
|
+
name: string;
|
|
24
|
+
/** The media type the value is an example of. */
|
|
25
|
+
type: string;
|
|
26
|
+
/** The example, or `null`/`undefined` when there's none to show. */
|
|
27
|
+
value: SpecValue;
|
|
28
|
+
/** What the tab says when there's no example. */
|
|
29
|
+
text: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** A response: its description and its body's media types. */
|
|
33
|
+
export interface ResponseLike {
|
|
34
|
+
description?: string;
|
|
35
|
+
content?: Record<string, MediaLike>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The media entry a body shows: the first JSON-ish one, else the first. */
|
|
39
|
+
export const preferredMedia = (
|
|
40
|
+
content?: Record<string, MediaLike>
|
|
41
|
+
): [string, MediaLike] | undefined => {
|
|
42
|
+
const entries = Object.entries(content ?? {});
|
|
43
|
+
return entries.find(([type]) => type.includes("json")) ?? entries[0];
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The tabs for one media type: each named example, or the one example
|
|
48
|
+
* `responseExample` gives. Webhook payloads and responses both travel from
|
|
49
|
+
* the API, so both read like a response.
|
|
50
|
+
*/
|
|
51
|
+
const mediaPanels = (
|
|
52
|
+
name: string,
|
|
53
|
+
type: string,
|
|
54
|
+
media: MediaLike | undefined,
|
|
55
|
+
text: string,
|
|
56
|
+
schemas: Record<string, SchemaLike>,
|
|
57
|
+
components: ComponentsLike | undefined
|
|
58
|
+
): ExamplePanel[] => {
|
|
59
|
+
const named = media
|
|
60
|
+
? namedExamples(media, schemas, "response", components)
|
|
61
|
+
: [];
|
|
62
|
+
if (named.length > 0) {
|
|
63
|
+
return named.map((example) => ({
|
|
64
|
+
key: `${name}-${example.key}`,
|
|
65
|
+
label: `${name} · ${example.label}`,
|
|
66
|
+
name,
|
|
67
|
+
text,
|
|
68
|
+
type,
|
|
69
|
+
value: example.value,
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
return [
|
|
73
|
+
{
|
|
74
|
+
key: name,
|
|
75
|
+
label: name,
|
|
76
|
+
name,
|
|
77
|
+
text,
|
|
78
|
+
type,
|
|
79
|
+
value: media ? responseExample(media, schemas, components) : undefined,
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/** The Response tabs: per status, per named example. */
|
|
85
|
+
export const responsePanels = (
|
|
86
|
+
responses: Record<string, ResponseLike>,
|
|
87
|
+
schemas: Record<string, SchemaLike>,
|
|
88
|
+
components?: ComponentsLike
|
|
89
|
+
): ExamplePanel[] =>
|
|
90
|
+
Object.entries(responses).flatMap(([status, response]) => {
|
|
91
|
+
const [type, media] = preferredMedia(response.content) ?? ["", undefined];
|
|
92
|
+
return mediaPanels(
|
|
93
|
+
status,
|
|
94
|
+
type,
|
|
95
|
+
media,
|
|
96
|
+
response.description || "No example response.",
|
|
97
|
+
schemas,
|
|
98
|
+
components
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
/** A webhook's Payload tabs: per media type, per named example. */
|
|
103
|
+
export const payloadPanels = (
|
|
104
|
+
payload: Record<string, MediaLike> | undefined,
|
|
105
|
+
schemas: Record<string, SchemaLike>,
|
|
106
|
+
components?: ComponentsLike
|
|
107
|
+
): ExamplePanel[] =>
|
|
108
|
+
Object.entries(payload ?? {}).flatMap(([type, media]) =>
|
|
109
|
+
mediaPanels(type, type, media, "No example payload.", schemas, components)
|
|
110
|
+
);
|
|
@@ -150,7 +150,10 @@ export const soleAllOfMember = (schema: SchemaLike): SchemaLike | undefined =>
|
|
|
150
150
|
/**
|
|
151
151
|
* Resolve one level of `$ref` against the document's component schemas. A
|
|
152
152
|
* single-member `allOf` wrapper resolves like its member, with the wrapper's
|
|
153
|
-
* own keywords on top: its `description` is about this field.
|
|
153
|
+
* own keywords on top: its `description` is about this field. So does a
|
|
154
|
+
* `$ref` with keywords beside it, which OpenAPI 3.1 allows and .NET writes
|
|
155
|
+
* for every enum-typed property: the `description` beside the `$ref` is the
|
|
156
|
+
* property's, the referenced one the type's.
|
|
154
157
|
*/
|
|
155
158
|
export const resolveSchema = (
|
|
156
159
|
schemas: Record<string, SchemaLike>,
|
|
@@ -166,8 +169,10 @@ export const resolveSchema = (
|
|
|
166
169
|
}
|
|
167
170
|
if (isString(schema.$ref)) {
|
|
168
171
|
const name = REF_PATTERN.exec(schema.$ref)?.groups?.name;
|
|
169
|
-
|
|
170
|
-
|
|
172
|
+
const target = name ? schemas[name] : undefined;
|
|
173
|
+
if (target) {
|
|
174
|
+
const { $ref: _ref, ...own } = schema;
|
|
175
|
+
return Object.keys(own).length > 0 ? { ...target, ...own } : target;
|
|
171
176
|
}
|
|
172
177
|
}
|
|
173
178
|
return schema;
|
|
@@ -325,19 +330,129 @@ export const objectProperties = (
|
|
|
325
330
|
return { properties: [...properties.entries()], required };
|
|
326
331
|
};
|
|
327
332
|
|
|
333
|
+
/** Which way an example travels: to the API, or back from it. */
|
|
334
|
+
export type ExampleDirection = "request" | "response";
|
|
335
|
+
|
|
336
|
+
const isRecord = (value: SpecValue): value is Record<string, SpecValue> =>
|
|
337
|
+
typeof value === "object" && value !== null && !Array.isArray(value);
|
|
338
|
+
|
|
339
|
+
/** Keywords whose values are data, not schemas, so never rewritten. */
|
|
340
|
+
const DATA_KEYWORDS = new Set([
|
|
341
|
+
"const",
|
|
342
|
+
"default",
|
|
343
|
+
"enum",
|
|
344
|
+
"example",
|
|
345
|
+
"examples",
|
|
346
|
+
]);
|
|
347
|
+
|
|
348
|
+
/** Keywords beside a `$ref` that give the value an example of its own. */
|
|
349
|
+
const VALUE_KEYWORDS = ["const", "default", "example", "examples"];
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* A schema as openapi-sampler should read it: each `$ref` with an example
|
|
353
|
+
* value beside it (`example`, `examples`, `default`, `const`) wrapped as a
|
|
354
|
+
* single-member `allOf`, so the value is used. The sampler follows a `$ref`
|
|
355
|
+
* before it looks at anything beside it, which OpenAPI 3.1 allows and .NET
|
|
356
|
+
* writes, so `{ $ref: Status, example: "active" }` sampled the referenced
|
|
357
|
+
* enum's first member instead.
|
|
358
|
+
*/
|
|
359
|
+
const sampleable = (schema: SpecValue): SpecValue => {
|
|
360
|
+
if (Array.isArray(schema)) {
|
|
361
|
+
return schema.map(sampleable);
|
|
362
|
+
}
|
|
363
|
+
if (!isRecord(schema)) {
|
|
364
|
+
return schema;
|
|
365
|
+
}
|
|
366
|
+
const out: Record<string, SpecValue> = {};
|
|
367
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
368
|
+
out[key] = DATA_KEYWORDS.has(key) ? value : sampleable(value);
|
|
369
|
+
}
|
|
370
|
+
const { $ref: ref, ...own } = out;
|
|
371
|
+
if (!(isString(ref) && VALUE_KEYWORDS.some((key) => key in own))) {
|
|
372
|
+
return out;
|
|
373
|
+
}
|
|
374
|
+
const members = Array.isArray(own.allOf) ? own.allOf : [];
|
|
375
|
+
return { ...own, allOf: [{ $ref: ref }, ...members] };
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
/** {@link sampleable} component schemas, once per document. */
|
|
379
|
+
const sampleableSchemas = new WeakMap<
|
|
380
|
+
Record<string, SchemaLike>,
|
|
381
|
+
Record<string, SchemaLike>
|
|
382
|
+
>();
|
|
383
|
+
|
|
384
|
+
const sampleableComponents = (
|
|
385
|
+
schemas: Record<string, SchemaLike>
|
|
386
|
+
): Record<string, SchemaLike> => {
|
|
387
|
+
const cached = sampleableSchemas.get(schemas);
|
|
388
|
+
if (cached) {
|
|
389
|
+
return cached;
|
|
390
|
+
}
|
|
391
|
+
// SAFETY: rewriting a `$ref` node into an `allOf` wrapper keeps every
|
|
392
|
+
// entry a schema.
|
|
393
|
+
const rewritten = sampleable(schemas) as Record<string, SchemaLike>;
|
|
394
|
+
sampleableSchemas.set(schemas, rewritten);
|
|
395
|
+
return rewritten;
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* An example value with the properties that don't travel `direction` left
|
|
400
|
+
* out, at every depth its schema describes: `readOnly` ones from a request,
|
|
401
|
+
* which the server sets, and `writeOnly` ones from a response, which it
|
|
402
|
+
* never returns. openapi-sampler skips those only in the values it builds; a
|
|
403
|
+
* declared example (a model's `example`, which TypeSpec writes, or a media
|
|
404
|
+
* type's) keeps them, so a request sample would send the model's `id`. A
|
|
405
|
+
* property the schema doesn't describe stays as written.
|
|
406
|
+
*/
|
|
407
|
+
export const forDirection = (
|
|
408
|
+
value: SpecValue,
|
|
409
|
+
schema: SchemaLike | undefined,
|
|
410
|
+
schemas: Record<string, SchemaLike>,
|
|
411
|
+
direction: ExampleDirection
|
|
412
|
+
): SpecValue => {
|
|
413
|
+
if (!schema) {
|
|
414
|
+
return value;
|
|
415
|
+
}
|
|
416
|
+
if (Array.isArray(value)) {
|
|
417
|
+
const { items } = resolveSchema(schemas, schema);
|
|
418
|
+
return items
|
|
419
|
+
? value.map((item) => forDirection(item, items, schemas, direction))
|
|
420
|
+
: value;
|
|
421
|
+
}
|
|
422
|
+
if (!isRecord(value)) {
|
|
423
|
+
return value;
|
|
424
|
+
}
|
|
425
|
+
const properties = new Map(objectProperties(schema, schemas).properties);
|
|
426
|
+
const omitted = direction === "request" ? "readOnly" : "writeOnly";
|
|
427
|
+
const out: Record<string, SpecValue> = {};
|
|
428
|
+
for (const [name, member] of Object.entries(value)) {
|
|
429
|
+
const property = properties.get(name);
|
|
430
|
+
if (
|
|
431
|
+
property?.[omitted] === true ||
|
|
432
|
+
resolveSchema(schemas, property)[omitted] === true
|
|
433
|
+
) {
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
out[name] = forDirection(member, property, schemas, direction);
|
|
437
|
+
}
|
|
438
|
+
return out;
|
|
439
|
+
};
|
|
440
|
+
|
|
328
441
|
/**
|
|
329
442
|
* Build a representative example value for a schema via openapi-sampler
|
|
330
443
|
* (Redoc's generator): declared `example`/`const`/`default`/`enum` values
|
|
331
|
-
* win,
|
|
332
|
-
* and circular `$ref` chains —
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
444
|
+
* win, a value beside a `$ref` included, formats produce realistic
|
|
445
|
+
* placeholders (`email`, `uuid`, `date-time`), and circular `$ref` chains —
|
|
446
|
+
* which keeping refs intact allows — terminate safely. A request sample
|
|
447
|
+
* leaves out `readOnly` fields (a server-generated field has no place in
|
|
448
|
+
* one); a response sample leaves out `writeOnly` fields (a password the
|
|
449
|
+
* client sends never comes back) and keeps the `readOnly` ones. That holds
|
|
450
|
+
* inside a declared value the sampler copies too (see {@link forDirection}).
|
|
336
451
|
*/
|
|
337
452
|
export const exampleValue = (
|
|
338
453
|
schema: SchemaLike | undefined,
|
|
339
454
|
schemas: Record<string, SchemaLike>,
|
|
340
|
-
direction:
|
|
455
|
+
direction: ExampleDirection = "request"
|
|
341
456
|
): SpecValue => {
|
|
342
457
|
if (!schema) {
|
|
343
458
|
return null;
|
|
@@ -346,11 +461,12 @@ export const exampleValue = (
|
|
|
346
461
|
try {
|
|
347
462
|
// SAFETY: SchemaLike structurally covers the JSONSchema7 fields the
|
|
348
463
|
// sampler reads, and the sampler only ever assembles JSON values.
|
|
349
|
-
|
|
350
|
-
schema as Parameters<typeof sample>[0],
|
|
464
|
+
const value = sample(
|
|
465
|
+
sampleable(schema) as Parameters<typeof sample>[0],
|
|
351
466
|
{ quiet: true, skipReadOnly: !response, skipWriteOnly: response },
|
|
352
|
-
{ components: { schemas } }
|
|
467
|
+
{ components: { schemas: sampleableComponents(schemas) } }
|
|
353
468
|
) as SpecValue;
|
|
469
|
+
return forDirection(value, schema, schemas, direction);
|
|
354
470
|
} catch {
|
|
355
471
|
// An unresolvable $ref or malformed schema is a spec problem the schema
|
|
356
472
|
// tables already surface; a sample is best-effort.
|
|
@@ -364,9 +480,6 @@ export interface ExampleCarrier {
|
|
|
364
480
|
examples?: SpecValue;
|
|
365
481
|
}
|
|
366
482
|
|
|
367
|
-
const isRecord = (value: SpecValue): value is Record<string, SpecValue> =>
|
|
368
|
-
typeof value === "object" && value !== null && !Array.isArray(value);
|
|
369
|
-
|
|
370
483
|
/**
|
|
371
484
|
* The example a parameter or media type declares: its `example`, else the
|
|
372
485
|
* first `value` in its `examples` map — inline, or behind a `$ref` to
|
|
@@ -398,18 +511,80 @@ export const declaredExample = (
|
|
|
398
511
|
return undefined;
|
|
399
512
|
};
|
|
400
513
|
|
|
514
|
+
/** A media type: the schema of a body and the examples it declares. */
|
|
515
|
+
export type MediaLike = ExampleCarrier & { schema?: SchemaLike };
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* A media type's example for a body travelling `direction`: the declared
|
|
519
|
+
* one, else a sample built from its schema, with the properties that don't
|
|
520
|
+
* travel that way left out (see {@link forDirection}).
|
|
521
|
+
*/
|
|
522
|
+
export const mediaExample = (
|
|
523
|
+
media: MediaLike,
|
|
524
|
+
schemas: Record<string, SchemaLike>,
|
|
525
|
+
direction: ExampleDirection,
|
|
526
|
+
components?: ComponentsLike
|
|
527
|
+
): SpecValue => {
|
|
528
|
+
const declared = declaredExample(media, components);
|
|
529
|
+
return declared === undefined
|
|
530
|
+
? exampleValue(media.schema, schemas, direction)
|
|
531
|
+
: forDirection(declared, media.schema, schemas, direction);
|
|
532
|
+
};
|
|
533
|
+
|
|
401
534
|
/**
|
|
402
535
|
* A response media type's example for the Response panel: the declared one,
|
|
403
536
|
* else a sample built for the response direction (`readOnly` kept,
|
|
404
|
-
* `writeOnly`
|
|
537
|
+
* `writeOnly` left out).
|
|
405
538
|
*/
|
|
406
539
|
export const responseExample = (
|
|
407
|
-
media:
|
|
540
|
+
media: MediaLike,
|
|
408
541
|
schemas: Record<string, SchemaLike>,
|
|
409
542
|
components?: ComponentsLike
|
|
410
|
-
): SpecValue =>
|
|
411
|
-
|
|
412
|
-
|
|
543
|
+
): SpecValue => mediaExample(media, schemas, "response", components);
|
|
544
|
+
|
|
545
|
+
/** One of a media type's named examples, as a tab or the Markdown copy shows it. */
|
|
546
|
+
export interface NamedExample {
|
|
547
|
+
/** The example's key in the `examples` map. */
|
|
548
|
+
key: string;
|
|
549
|
+
/** Its `summary`, else its key. */
|
|
550
|
+
label: string;
|
|
551
|
+
value: SpecValue;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Every example a media type's `examples` map names, in order, `$ref`s to
|
|
556
|
+
* `#/components/examples` resolved, each labeled by its `summary` or else
|
|
557
|
+
* its key, with the properties that don't travel `direction` left out. Empty
|
|
558
|
+
* unless the map names two or more: Scalar's upgrade files a 3.0 `example`
|
|
559
|
+
* as the one entry `default`, which is no name to show, and a lone example
|
|
560
|
+
* is the one {@link mediaExample} already gives.
|
|
561
|
+
*/
|
|
562
|
+
export const namedExamples = (
|
|
563
|
+
media: MediaLike,
|
|
564
|
+
schemas: Record<string, SchemaLike>,
|
|
565
|
+
direction: ExampleDirection,
|
|
566
|
+
components?: ComponentsLike
|
|
567
|
+
): NamedExample[] => {
|
|
568
|
+
if (media.example !== undefined || !isRecord(media.examples)) {
|
|
569
|
+
return [];
|
|
570
|
+
}
|
|
571
|
+
const named: NamedExample[] = [];
|
|
572
|
+
for (const [key, entry] of Object.entries(media.examples)) {
|
|
573
|
+
const example = isRecord(entry)
|
|
574
|
+
? resolveComponentRef(entry, components, "examples")
|
|
575
|
+
: undefined;
|
|
576
|
+
if (example?.value === undefined) {
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
named.push({
|
|
580
|
+
key,
|
|
581
|
+
label:
|
|
582
|
+
isString(example.summary) && example.summary ? example.summary : key,
|
|
583
|
+
value: forDirection(example.value, media.schema, schemas, direction),
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
return named.length > 1 ? named : [];
|
|
587
|
+
};
|
|
413
588
|
|
|
414
589
|
/** Pretty-print a JSON value for an example/code block. */
|
|
415
590
|
export const toJson = <T>(value: T): string => JSON.stringify(value, null, 2);
|