blume 2.1.2 → 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 +133 -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 +10 -9
- 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
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// operation-routes.mjs: list the route Blume gave every generated API
|
|
3
|
+
// operation page, read from a built site. Migrations need it to redirect a
|
|
4
|
+
// source's old operation URLs (Redocly, ReadMe, Fern, …) to Blume's: match an
|
|
5
|
+
// old URL to its endpoint in the spec, then look the endpoint up here.
|
|
6
|
+
//
|
|
7
|
+
// It reads Blume's own output rather than re-deriving Blume's slug rules, so it
|
|
8
|
+
// stays right when those rules change. The source is the Markdown mirror
|
|
9
|
+
// (`<route>.md`) Blume writes for every page:
|
|
10
|
+
//
|
|
11
|
+
// - A generated operation page's mirror has `type: openapi-operation` in its
|
|
12
|
+
// frontmatter, and its `<Operation>` component downlevels to the endpoint
|
|
13
|
+
// in the spec's notation, alone on a line: `GET /pets/{id}`. A webhook
|
|
14
|
+
// reads `POST petAdopted`, and the next line is the serializer's
|
|
15
|
+
// `**Webhook.** The API sends this request to your endpoint.` note. (Its
|
|
16
|
+
// `Webhook` search tag isn't used: an operation's spec tag is a search tag
|
|
17
|
+
// too, and an API can have a tag named "Webhook".)
|
|
18
|
+
// - A reference's overview page, at the reference's route, lists every
|
|
19
|
+
// operation it holds, by tag, each one under that route:
|
|
20
|
+
// - [`GET /pets/{id}`](/api/pets/get-pets-id) — Get a pet.
|
|
21
|
+
// which ties each operation to its reference (two specs can both have
|
|
22
|
+
// `GET /status`). A list like that on any other page (a guide linking the
|
|
23
|
+
// endpoints it uses) points outside the page's own route, so it's skipped.
|
|
24
|
+
//
|
|
25
|
+
// A server build keeps Astro's `dist/client` + `dist/server` split, with the
|
|
26
|
+
// pages (and their mirrors) in `dist/client`: given such a `dist`, this reads
|
|
27
|
+
// `dist/client`. A Vercel server build writes them to `.vercel/output/static`
|
|
28
|
+
// instead: pass that directory.
|
|
29
|
+
//
|
|
30
|
+
// Why the mirrors: Blume writes them on every build with no switch to turn
|
|
31
|
+
// them off, and the docs (discoverability/markdown) promise both downlevels.
|
|
32
|
+
// The JSON API (`api/docs/pages.json`) carries the same Markdown but can be
|
|
33
|
+
// disabled with `agents.api: false`; llms.txt has titles but no endpoints; and
|
|
34
|
+
// no output carries operationIds. A project that replaces the `Operation` or
|
|
35
|
+
// `ApiTagOperations` serializer through `agents.markdownComponents` changes
|
|
36
|
+
// what this reads.
|
|
37
|
+
//
|
|
38
|
+
// operationIds: pass a spec with `--spec <reference-route>=<file.json>` and
|
|
39
|
+
// each endpoint gains its spec `operationId`, matched by method and path (no
|
|
40
|
+
// slug logic). JSON only, to stay dependency-free: convert YAML first, e.g.
|
|
41
|
+
// `npx @redocly/cli bundle openapi.yaml --output openapi.json`.
|
|
42
|
+
//
|
|
43
|
+
// Zero dependencies, read-only, deterministic (sorted output).
|
|
44
|
+
//
|
|
45
|
+
// Usage:
|
|
46
|
+
// node operation-routes.mjs [dist] # default: ./dist
|
|
47
|
+
// node operation-routes.mjs dist --spec /api=openapi.json
|
|
48
|
+
// node operation-routes.mjs .vercel/output/static # Vercel server build
|
|
49
|
+
// node operation-routes.mjs --help
|
|
50
|
+
//
|
|
51
|
+
// Output (stdout, JSON):
|
|
52
|
+
// {
|
|
53
|
+
// "references": {
|
|
54
|
+
// "/api": {
|
|
55
|
+
// "title": "Acme API",
|
|
56
|
+
// "endpoints": { "GET /pets/{id}": "/api/pets/get-pets-id", … },
|
|
57
|
+
// "webhooks": ["POST petAdopted"],
|
|
58
|
+
// "operationIds": { "getPet": "/api/pets/get-pets-id" } // with --spec
|
|
59
|
+
// }
|
|
60
|
+
// },
|
|
61
|
+
// "unlisted": { "GET /x": ["/route"] } // operation pages no overview lists
|
|
62
|
+
// }
|
|
63
|
+
// Warnings go to stderr. Exit 1 when the directory holds no operation pages.
|
|
64
|
+
|
|
65
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
66
|
+
import path from "node:path";
|
|
67
|
+
|
|
68
|
+
const HELP = `Usage: node operation-routes.mjs [dist] [--spec <route>=<file.json>]...
|
|
69
|
+
|
|
70
|
+
Reads a built Blume site (default ./dist) and prints, per API reference, a map
|
|
71
|
+
from each operation's endpoint ("METHOD /path", or "METHOD name" for a webhook)
|
|
72
|
+
to the route of its generated page. --spec adds operationIds from a JSON spec
|
|
73
|
+
for the reference mounted at <route>.
|
|
74
|
+
|
|
75
|
+
A server build's pages are in dist/client, which is read when dist holds
|
|
76
|
+
client/ and server/. For a Vercel server build, pass .vercel/output/static.`;
|
|
77
|
+
|
|
78
|
+
const HTTP_METHODS = [
|
|
79
|
+
"get",
|
|
80
|
+
"put",
|
|
81
|
+
"post",
|
|
82
|
+
"delete",
|
|
83
|
+
"options",
|
|
84
|
+
"head",
|
|
85
|
+
"patch",
|
|
86
|
+
"trace",
|
|
87
|
+
"query",
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
const fail = (message) => {
|
|
91
|
+
process.stderr.write(`operation-routes: ${message}\n`);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const warn = (message) => {
|
|
96
|
+
process.stderr.write(`operation-routes: warning: ${message}\n`);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const normalizeRoute = (route) => {
|
|
100
|
+
const trimmed = route.replace(/\/+$/u, "");
|
|
101
|
+
if (trimmed === "") {
|
|
102
|
+
return "/";
|
|
103
|
+
}
|
|
104
|
+
return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/** An object with its keys in code-point order, for deterministic output. */
|
|
108
|
+
const sortObject = (object) =>
|
|
109
|
+
Object.fromEntries(
|
|
110
|
+
Object.entries(object).toSorted(([a], [b]) => (a < b ? -1 : 1))
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// --- arguments ---------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
const parseSpecArg = (value) => {
|
|
116
|
+
const split = value ? value.indexOf("=") : -1;
|
|
117
|
+
if (split <= 0 || split === value.length - 1) {
|
|
118
|
+
fail(`--spec takes <reference-route>=<file.json>, got "${value}".`);
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
file: value.slice(split + 1),
|
|
122
|
+
route: normalizeRoute(value.slice(0, split)),
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const parseArgs = (argv) => {
|
|
127
|
+
const options = { dist: "dist", specs: [] };
|
|
128
|
+
let distSet = false;
|
|
129
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
130
|
+
const arg = argv[index];
|
|
131
|
+
if (arg === "--help" || arg === "-h") {
|
|
132
|
+
process.stdout.write(`${HELP}\n`);
|
|
133
|
+
process.exit(0);
|
|
134
|
+
}
|
|
135
|
+
if (arg === "--spec") {
|
|
136
|
+
index += 1;
|
|
137
|
+
options.specs.push(parseSpecArg(argv[index]));
|
|
138
|
+
} else if (arg.startsWith("--spec=")) {
|
|
139
|
+
options.specs.push(parseSpecArg(arg.slice("--spec=".length)));
|
|
140
|
+
} else if (arg.startsWith("-")) {
|
|
141
|
+
fail(`unknown option ${arg}. Run with --help.`);
|
|
142
|
+
} else if (distSet) {
|
|
143
|
+
fail(`one dist directory only (got "${options.dist}" and "${arg}").`);
|
|
144
|
+
} else {
|
|
145
|
+
options.dist = arg;
|
|
146
|
+
distSet = true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return options;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// --- reading the mirrors -------------------------------------------------------
|
|
153
|
+
|
|
154
|
+
/** Every `.md` file under `dir`, as paths relative to it, sorted. */
|
|
155
|
+
const markdownFiles = (dir) => {
|
|
156
|
+
const found = [];
|
|
157
|
+
const walk = (relative) => {
|
|
158
|
+
const entries = readdirSync(path.join(dir, relative), {
|
|
159
|
+
withFileTypes: true,
|
|
160
|
+
});
|
|
161
|
+
for (const entry of entries) {
|
|
162
|
+
const child = relative ? `${relative}/${entry.name}` : entry.name;
|
|
163
|
+
if (entry.isDirectory()) {
|
|
164
|
+
walk(child);
|
|
165
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
166
|
+
found.push(child);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
walk("");
|
|
171
|
+
return found.toSorted();
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
/** The route a mirror file stands for: `api/pets/x.md` → `/api/pets/x`. */
|
|
175
|
+
const routeOf = (file) => {
|
|
176
|
+
const stem = file.slice(0, -".md".length);
|
|
177
|
+
return stem === "index" ? "/" : `/${stem}`;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
/** Split a mirror into its frontmatter lines and its body. */
|
|
181
|
+
const splitFrontmatter = (text) => {
|
|
182
|
+
const normalized = text.replaceAll("\r\n", "\n");
|
|
183
|
+
const end = normalized.startsWith("---\n")
|
|
184
|
+
? normalized.indexOf("\n---", 3)
|
|
185
|
+
: -1;
|
|
186
|
+
if (end === -1) {
|
|
187
|
+
return { body: normalized, frontmatter: [] };
|
|
188
|
+
}
|
|
189
|
+
const after = normalized.indexOf("\n", end + 1);
|
|
190
|
+
return {
|
|
191
|
+
body: after === -1 ? "" : normalized.slice(after + 1),
|
|
192
|
+
frontmatter: normalized.slice(4, end).split("\n"),
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/** A top-level scalar frontmatter value (`title: Get a pet`), unquoted. */
|
|
197
|
+
const topLevelValue = (lines, key) => {
|
|
198
|
+
const prefix = `${key}:`;
|
|
199
|
+
const line = lines.find((candidate) => candidate.startsWith(prefix));
|
|
200
|
+
if (!line) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const value = line.slice(prefix.length).trim();
|
|
204
|
+
const quoted = /^(?<quote>["'])(?<inner>.*)\k<quote>$/u.exec(value);
|
|
205
|
+
if (!quoted) {
|
|
206
|
+
return value;
|
|
207
|
+
}
|
|
208
|
+
const { inner, quote } = quoted.groups;
|
|
209
|
+
return quote === "'"
|
|
210
|
+
? inner.replaceAll("''", "'")
|
|
211
|
+
: inner.replaceAll('\\"', '"');
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
/** Body lines outside fenced code blocks. */
|
|
215
|
+
const proseLines = (body) => {
|
|
216
|
+
const lines = [];
|
|
217
|
+
let fence = null;
|
|
218
|
+
for (const line of body.split("\n")) {
|
|
219
|
+
const marker = /^\s*(?<run>`{3,}|~{3,})/u.exec(line);
|
|
220
|
+
if (!marker) {
|
|
221
|
+
if (fence === null) {
|
|
222
|
+
lines.push(line);
|
|
223
|
+
}
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
const { run } = marker.groups;
|
|
227
|
+
if (fence === null) {
|
|
228
|
+
fence = run;
|
|
229
|
+
} else if (run[0] === fence[0] && run.length >= fence.length) {
|
|
230
|
+
fence = null;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return lines;
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
/** The text of an inline-code span that fills `text` entirely, if it does. */
|
|
237
|
+
const wholeInlineCode = (text) => {
|
|
238
|
+
const match = /^(?<ticks>`+)(?<code>.+?)\k<ticks>$/u.exec(text.trim());
|
|
239
|
+
if (!match) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const { code } = match.groups;
|
|
243
|
+
// A span padded to hold a backtick (`` `x` ``) carries one space each side.
|
|
244
|
+
return code.startsWith(" ") && code.endsWith(" ") && code.trim() !== ""
|
|
245
|
+
? code.slice(1, -1)
|
|
246
|
+
: code;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* An operation page's endpoint: the last line outside code that is only an
|
|
251
|
+
* inline-code span. `<Operation>` is appended after the description, so a
|
|
252
|
+
* description that happens to hold such a line comes first. A webhook's
|
|
253
|
+
* endpoint line is followed by the `**Webhook.**` note.
|
|
254
|
+
*/
|
|
255
|
+
const operationOf = (body) => {
|
|
256
|
+
const lines = proseLines(body).filter((line) => line.trim() !== "");
|
|
257
|
+
let at = -1;
|
|
258
|
+
let endpoint;
|
|
259
|
+
for (const [index, line] of lines.entries()) {
|
|
260
|
+
const code = wholeInlineCode(line);
|
|
261
|
+
if (code !== undefined) {
|
|
262
|
+
at = index;
|
|
263
|
+
endpoint = code;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const next = at === -1 ? "" : (lines[at + 1] ?? "");
|
|
267
|
+
return { endpoint, webhook: next.trim().startsWith("**Webhook.**") };
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
/** `- [`GET /x`](/route) — Summary.` items on an overview page. */
|
|
271
|
+
const LIST_ITEM =
|
|
272
|
+
/^-\s+\[(?<ticks>`+)(?<code>.+?)\k<ticks>\]\((?:<(?<angled>[^>]+)>|(?<plain>[^)\s]+))\)/u;
|
|
273
|
+
|
|
274
|
+
const listedOperations = (body) => {
|
|
275
|
+
const items = [];
|
|
276
|
+
for (const line of proseLines(body)) {
|
|
277
|
+
const match = LIST_ITEM.exec(line);
|
|
278
|
+
if (match) {
|
|
279
|
+
const { angled, code, plain } = match.groups;
|
|
280
|
+
items.push({
|
|
281
|
+
endpoint: wholeInlineCode(`\`${code}\``) ?? code,
|
|
282
|
+
href: angled ?? plain,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return items;
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
/** A link's path, decoded, whatever origin or encoding it was written with. */
|
|
290
|
+
const hrefPath = (href) => {
|
|
291
|
+
let pathname = href;
|
|
292
|
+
try {
|
|
293
|
+
({ pathname } = new URL(href, "http://blume.invalid"));
|
|
294
|
+
pathname = decodeURI(pathname);
|
|
295
|
+
} catch {
|
|
296
|
+
// An unparseable href is matched as written; it simply finds nothing.
|
|
297
|
+
}
|
|
298
|
+
return normalizeRoute(pathname);
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
/** The operation route an overview link points at (it may carry a base). */
|
|
302
|
+
const resolveHref = (href, operationRoutes) => {
|
|
303
|
+
const route = hrefPath(href);
|
|
304
|
+
if (operationRoutes.has(route)) {
|
|
305
|
+
return route;
|
|
306
|
+
}
|
|
307
|
+
// `deployment.base` prefixes links but not the files in dist/: take the
|
|
308
|
+
// longest operation route the link ends with, behind a `/base` prefix.
|
|
309
|
+
let best;
|
|
310
|
+
for (const candidate of operationRoutes) {
|
|
311
|
+
const prefix = route.slice(0, route.length - candidate.length);
|
|
312
|
+
if (
|
|
313
|
+
route.endsWith(candidate) &&
|
|
314
|
+
prefix.startsWith("/") &&
|
|
315
|
+
(best === undefined || candidate.length > best.length)
|
|
316
|
+
) {
|
|
317
|
+
best = candidate;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return best;
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
/** Every operation page and every overview list, from the dist's mirrors. */
|
|
324
|
+
const readMirrors = (dist) => {
|
|
325
|
+
// route → { endpoint, title, webhook }
|
|
326
|
+
const pages = new Map();
|
|
327
|
+
// { items, route, title }
|
|
328
|
+
const overviews = [];
|
|
329
|
+
for (const file of markdownFiles(dist)) {
|
|
330
|
+
const { body, frontmatter } = splitFrontmatter(
|
|
331
|
+
readFileSync(path.join(dist, file), "utf-8")
|
|
332
|
+
);
|
|
333
|
+
const route = routeOf(file);
|
|
334
|
+
const title = topLevelValue(frontmatter, "title");
|
|
335
|
+
if (topLevelValue(frontmatter, "type") === "openapi-operation") {
|
|
336
|
+
pages.set(route, { ...operationOf(body), title });
|
|
337
|
+
} else {
|
|
338
|
+
const items = listedOperations(body);
|
|
339
|
+
if (items.length > 0) {
|
|
340
|
+
overviews.push({ items, route, title });
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return { overviews, pages };
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
// --- grouping --------------------------------------------------------------------
|
|
348
|
+
|
|
349
|
+
/** Whether `route` sits under `base` (every route sits under `/`). */
|
|
350
|
+
const isUnder = (route, base) => base === "/" || route.startsWith(`${base}/`);
|
|
351
|
+
|
|
352
|
+
/** One overview's operations: its endpoints and webhooks, claiming each page. */
|
|
353
|
+
const referenceOf = (overview, pages, claimed) => {
|
|
354
|
+
const operationRoutes = new Set(pages.keys());
|
|
355
|
+
const endpoints = {};
|
|
356
|
+
const webhooks = new Set();
|
|
357
|
+
for (const item of overview.items) {
|
|
358
|
+
const route = resolveHref(item.href, operationRoutes);
|
|
359
|
+
const page = route === undefined ? undefined : pages.get(route);
|
|
360
|
+
// An overview's operations live under its route; a list that links
|
|
361
|
+
// elsewhere is a page quoting endpoints, not a reference.
|
|
362
|
+
if (!(page && isUnder(route, overview.route))) {
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
if (page.endpoint !== undefined && page.endpoint !== item.endpoint) {
|
|
366
|
+
warn(
|
|
367
|
+
`${route}: the overview lists it as "${item.endpoint}" but the page reads "${page.endpoint}"; using the page.`
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
const endpoint = page.endpoint ?? item.endpoint;
|
|
371
|
+
if (endpoints[endpoint] !== undefined && endpoints[endpoint] !== route) {
|
|
372
|
+
warn(
|
|
373
|
+
`${overview.route}: "${endpoint}" maps to both ${endpoints[endpoint]} and ${route}; keeping the first.`
|
|
374
|
+
);
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
endpoints[endpoint] = route;
|
|
378
|
+
if (page.webhook) {
|
|
379
|
+
webhooks.add(endpoint);
|
|
380
|
+
}
|
|
381
|
+
claimed.add(route);
|
|
382
|
+
}
|
|
383
|
+
return {
|
|
384
|
+
endpoints: sortObject(endpoints),
|
|
385
|
+
title: overview.title,
|
|
386
|
+
webhooks: [...webhooks].toSorted(),
|
|
387
|
+
};
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
/** Operation pages no overview listed, keyed by endpoint. */
|
|
391
|
+
const unlistedPages = (pages, claimed) => {
|
|
392
|
+
const unlisted = {};
|
|
393
|
+
for (const [route, page] of pages) {
|
|
394
|
+
if (!claimed.has(route)) {
|
|
395
|
+
const key = page.endpoint ?? `(no endpoint line) ${route}`;
|
|
396
|
+
unlisted[key] = [...(unlisted[key] ?? []), route];
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return sortObject(unlisted);
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
// --- spec operationIds ---------------------------------------------------------
|
|
403
|
+
|
|
404
|
+
/** `METHOD /path` (or `METHOD name` for a webhook) → operationId, from a JSON spec. */
|
|
405
|
+
const specOperationIds = (file) => {
|
|
406
|
+
if (/\.ya?ml$/iu.test(file)) {
|
|
407
|
+
fail(
|
|
408
|
+
`${file}: YAML specs aren't read (no dependencies). Convert it first, e.g. \`npx @redocly/cli bundle ${file} --output ${file.replace(/\.ya?ml$/iu, ".json")}\`.`
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
let spec;
|
|
412
|
+
try {
|
|
413
|
+
spec = JSON.parse(readFileSync(file, "utf-8"));
|
|
414
|
+
} catch (error) {
|
|
415
|
+
fail(`${file}: not readable as JSON (${error.message}).`);
|
|
416
|
+
}
|
|
417
|
+
const ids = new Map();
|
|
418
|
+
for (const items of [spec.paths, spec.webhooks]) {
|
|
419
|
+
for (const [key, item] of Object.entries(items ?? {})) {
|
|
420
|
+
for (const method of HTTP_METHODS) {
|
|
421
|
+
const operationId = item?.[method]?.operationId;
|
|
422
|
+
if (operationId) {
|
|
423
|
+
ids.set(`${method.toUpperCase()} ${key}`, String(operationId));
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return ids;
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
const addOperationIds = (references, spec) => {
|
|
432
|
+
const reference = references[spec.route];
|
|
433
|
+
if (!reference) {
|
|
434
|
+
fail(
|
|
435
|
+
`--spec ${spec.route}=${spec.file}: no reference overview at ${spec.route}. References: ${Object.keys(references).toSorted().join(", ") || "none"}.`
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
const operationIds = {};
|
|
439
|
+
for (const [endpoint, operationId] of specOperationIds(spec.file)) {
|
|
440
|
+
const route = reference.endpoints[endpoint];
|
|
441
|
+
if (route === undefined) {
|
|
442
|
+
warn(
|
|
443
|
+
`${spec.file}: ${endpoint} (${operationId}) has no page under ${spec.route}.`
|
|
444
|
+
);
|
|
445
|
+
} else {
|
|
446
|
+
operationIds[operationId] = route;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
reference.operationIds = sortObject(operationIds);
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
// --- main ------------------------------------------------------------------------
|
|
453
|
+
|
|
454
|
+
const isDirectory = (dir) => existsSync(dir) && statSync(dir).isDirectory();
|
|
455
|
+
|
|
456
|
+
/** The directory holding the pages: a server build's `client/` half, else `dist`. */
|
|
457
|
+
const pagesRoot = (dist) => {
|
|
458
|
+
const client = path.join(dist, "client");
|
|
459
|
+
if (isDirectory(client) && isDirectory(path.join(dist, "server"))) {
|
|
460
|
+
process.stderr.write(
|
|
461
|
+
`operation-routes: reading ${client}, a server build's pages.\n`
|
|
462
|
+
);
|
|
463
|
+
return client;
|
|
464
|
+
}
|
|
465
|
+
return dist;
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
const main = () => {
|
|
469
|
+
const options = parseArgs(process.argv.slice(2));
|
|
470
|
+
const dist = path.resolve(options.dist);
|
|
471
|
+
if (!isDirectory(dist)) {
|
|
472
|
+
fail(
|
|
473
|
+
`${options.dist} is not a directory. Run \`blume build\` first, then pass its output directory.`
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const { overviews, pages } = readMirrors(pagesRoot(dist));
|
|
478
|
+
if (pages.size === 0) {
|
|
479
|
+
fail(
|
|
480
|
+
`no generated operation pages in ${options.dist} (no .md mirror with \`type: openapi-operation\`). Is it a Blume build with an openapi() reference? A Vercel server build writes its pages to .vercel/output/static: pass that.`
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const claimed = new Set();
|
|
485
|
+
const references = {};
|
|
486
|
+
for (const overview of overviews) {
|
|
487
|
+
const reference = referenceOf(overview, pages, claimed);
|
|
488
|
+
if (Object.keys(reference.endpoints).length > 0) {
|
|
489
|
+
references[overview.route] = reference;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
for (const spec of options.specs) {
|
|
493
|
+
addOperationIds(references, spec);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const output = { references: sortObject(references) };
|
|
497
|
+
const unlisted = unlistedPages(pages, claimed);
|
|
498
|
+
const unlistedCount = Object.values(unlisted).flat().length;
|
|
499
|
+
if (unlistedCount > 0) {
|
|
500
|
+
warn(
|
|
501
|
+
`${unlistedCount} operation page(s) appear in no overview list; see "unlisted".`
|
|
502
|
+
);
|
|
503
|
+
output.unlisted = unlisted;
|
|
504
|
+
}
|
|
505
|
+
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
main();
|