blume 2.0.1 → 2.0.3
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 +147 -0
- package/dist/cli/{chunk-qwsrynx5.js → chunk-1d7ve1dm.js} +38 -19
- package/dist/cli/{chunk-qwsrynx5.js.map → chunk-1d7ve1dm.js.map} +4 -4
- package/dist/cli/{chunk-s6jhgk0q.js → chunk-2eytanqx.js} +2 -2
- package/dist/cli/{chunk-kdp5q7ke.js → chunk-2hsdwb9n.js} +18 -19
- package/dist/cli/{chunk-kdp5q7ke.js.map → chunk-2hsdwb9n.js.map} +2 -2
- package/dist/cli/{chunk-hdpx1tax.js → chunk-2z928egk.js} +5 -5
- package/dist/cli/{chunk-jwyddg7y.js → chunk-35d4wj9f.js} +30 -24
- package/dist/cli/chunk-35d4wj9f.js.map +15 -0
- package/dist/cli/{chunk-ah61y8py.js → chunk-364znk6q.js} +2 -2
- package/dist/cli/{chunk-jts8mvcz.js → chunk-3em5wd2y.js} +25 -6
- package/dist/cli/{chunk-jts8mvcz.js.map → chunk-3em5wd2y.js.map} +3 -3
- package/dist/cli/{chunk-s1p84fyh.js → chunk-5f86nr5m.js} +63 -22
- package/dist/cli/chunk-5f86nr5m.js.map +11 -0
- package/dist/cli/{chunk-2hn4b8z7.js → chunk-5m5nmvyq.js} +170 -76
- package/dist/cli/chunk-5m5nmvyq.js.map +13 -0
- package/dist/cli/{chunk-epjnccmv.js → chunk-5xvm6tfj.js} +18 -15
- package/dist/cli/chunk-5xvm6tfj.js.map +10 -0
- package/dist/cli/{chunk-fz5wtpmh.js → chunk-6dsbexzp.js} +18 -15
- package/dist/cli/chunk-6dsbexzp.js.map +10 -0
- package/dist/cli/{chunk-vtk4a6dg.js → chunk-8ktnccpt.js} +1 -1
- package/dist/cli/{chunk-6hsn950k.js → chunk-9t7a85s3.js} +178 -48
- package/dist/cli/chunk-9t7a85s3.js.map +10 -0
- package/dist/cli/{chunk-fxypxtvm.js → chunk-a58773jm.js} +2 -2
- package/dist/cli/{chunk-mb2919y2.js → chunk-acanzt5p.js} +17 -6
- package/dist/cli/chunk-acanzt5p.js.map +10 -0
- package/dist/cli/{chunk-27g6wdth.js → chunk-akbpwfxc.js} +90 -26
- package/dist/cli/chunk-akbpwfxc.js.map +10 -0
- package/dist/cli/{chunk-wm7js3j9.js → chunk-b07cmahc.js} +2 -2
- package/dist/cli/{chunk-wgm7m9qk.js → chunk-c8chx29p.js} +179 -51
- package/dist/cli/chunk-c8chx29p.js.map +36 -0
- package/dist/cli/{chunk-qs4q5p4e.js → chunk-crgn1q09.js} +32 -14
- package/dist/cli/chunk-crgn1q09.js.map +10 -0
- package/dist/cli/chunk-e04dxsz1.js +39 -0
- package/dist/cli/chunk-e04dxsz1.js.map +10 -0
- package/dist/cli/{chunk-yt5n7ppj.js → chunk-ey84smr6.js} +17 -7
- package/dist/cli/chunk-ey84smr6.js.map +10 -0
- package/dist/cli/{chunk-f2z5v128.js → chunk-g4hq16wv.js} +14 -15
- package/dist/cli/{chunk-f2z5v128.js.map → chunk-g4hq16wv.js.map} +2 -2
- package/dist/cli/{chunk-fa25z98p.js → chunk-ga0pf4aj.js} +9 -8
- package/dist/cli/chunk-ga0pf4aj.js.map +11 -0
- package/dist/cli/{chunk-kpf8rrjc.js → chunk-hm3vjy5s.js} +109 -58
- package/dist/cli/chunk-hm3vjy5s.js.map +19 -0
- package/dist/cli/{chunk-fs23ddbb.js → chunk-j85vccga.js} +625 -750
- package/dist/cli/chunk-j85vccga.js.map +36 -0
- package/dist/cli/{chunk-ch6g3ar0.js → chunk-p3v96n38.js} +6 -6
- package/dist/cli/{chunk-ch6g3ar0.js.map → chunk-p3v96n38.js.map} +3 -3
- package/dist/cli/{chunk-q5163e60.js → chunk-p73c0m7w.js} +21 -19
- package/dist/cli/chunk-p73c0m7w.js.map +11 -0
- package/dist/cli/{chunk-dh8cwk36.js → chunk-pehfxfta.js} +24 -9
- package/dist/cli/chunk-pehfxfta.js.map +10 -0
- package/dist/cli/{chunk-qkqwkpte.js → chunk-pv29h0wf.js} +3688 -1313
- package/dist/cli/chunk-pv29h0wf.js.map +190 -0
- package/dist/cli/{chunk-6vm74dry.js → chunk-q58y5e6a.js} +10 -10
- package/dist/cli/{chunk-6vm74dry.js.map → chunk-q58y5e6a.js.map} +3 -3
- package/dist/cli/{chunk-zxcczpyx.js → chunk-r20tn01b.js} +1 -1
- package/dist/cli/{chunk-m3vmjgmq.js → chunk-r9rcc4w7.js} +16 -8
- package/dist/cli/chunk-r9rcc4w7.js.map +10 -0
- package/dist/cli/{chunk-5shv93fd.js → chunk-tkacnehg.js} +2 -2
- package/dist/cli/{chunk-zxh4d9vy.js → chunk-tzmab476.js} +4 -4
- package/dist/cli/{chunk-yw7dm696.js → chunk-vg9r4eb9.js} +24 -11
- package/dist/cli/chunk-vg9r4eb9.js.map +14 -0
- package/dist/cli/{chunk-79jhk4py.js → chunk-wrr3j9w9.js} +260 -132
- package/dist/cli/chunk-wrr3j9w9.js.map +35 -0
- package/dist/cli/{chunk-6crbhc3x.js → chunk-yfyb25rh.js} +54 -27
- package/dist/cli/chunk-yfyb25rh.js.map +14 -0
- package/dist/cli/index.js +162 -35
- package/dist/cli/index.js.map +4 -4
- package/dist/types/ai/agent-surface.d.ts +32 -0
- package/dist/types/ai/api-catalog.d.ts +7 -1
- package/dist/types/ai/ask-context.d.ts +7 -0
- package/dist/types/ai/component-markdown.d.ts +4 -4
- package/dist/types/ai/link-headers.d.ts +8 -1
- package/dist/types/ai/openapi-components.d.ts +5 -2
- package/dist/types/ai/relative-links.d.ts +11 -4
- package/dist/types/ai/skills.d.ts +4 -1
- package/dist/types/ai/static-expression.d.ts +28 -0
- package/dist/types/ai/tar.d.ts +1 -3
- package/dist/types/analytics/databuddy.d.ts +43 -0
- package/dist/types/analytics/index.d.ts +4 -0
- package/dist/types/analytics/one-dollar-stats.d.ts +48 -0
- package/dist/types/analytics/schema.d.ts +28 -0
- package/dist/types/astro/integration.d.ts +3 -2
- package/dist/types/cli/env.d.ts +5 -0
- package/dist/types/cli/init/scaffold.d.ts +19 -3
- package/dist/types/cli/init/starter-spec.d.ts +11 -0
- package/dist/types/core/base-path.d.ts +24 -1
- package/dist/types/core/config-input.d.ts +13 -11
- package/dist/types/core/config.d.ts +2 -2
- package/dist/types/core/directive-diagnostics.d.ts +12 -0
- package/dist/types/core/graph.d.ts +2 -0
- package/dist/types/core/heading-markers.d.ts +5 -7
- package/dist/types/core/i18n-ui.d.ts +31 -0
- package/dist/types/core/i18n.d.ts +9 -1
- package/dist/types/core/last-modified.d.ts +10 -0
- package/dist/types/core/links.d.ts +3 -1
- package/dist/types/core/load-module.d.ts +10 -0
- package/dist/types/core/locale-links.d.ts +12 -2
- package/dist/types/core/meta.d.ts +13 -1
- package/dist/types/core/nav-diagnostics.d.ts +10 -0
- package/dist/types/core/navigation.d.ts +38 -0
- package/dist/types/core/ordering-prefix.d.ts +4 -0
- package/dist/types/core/safe-links.d.ts +3 -1
- package/dist/types/core/schema.d.ts +59 -13
- package/dist/types/core/sources/github-releases.d.ts +5 -0
- package/dist/types/core/sources/lower.d.ts +38 -13
- package/dist/types/core/sources/normalize.d.ts +23 -2
- package/dist/types/core/sources/remote.d.ts +11 -1
- package/dist/types/core/sources/resolve.d.ts +12 -0
- package/dist/types/core/sources/types.d.ts +31 -0
- package/dist/types/core/sources/watch.d.ts +12 -5
- package/dist/types/core/standard-schema.d.ts +5 -0
- package/dist/types/core/types.d.ts +9 -0
- package/dist/types/deploy/adapters/node.d.ts +5 -2
- package/dist/types/deploy/adapters/types.d.ts +7 -0
- package/dist/types/deploy/artifacts.d.ts +6 -4
- package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
- package/dist/types/deploy/headers.d.ts +36 -7
- package/dist/types/deploy/node-headers.d.ts +43 -8
- package/dist/types/deploy/platforms/netlify.d.ts +27 -2
- package/dist/types/deploy/platforms/node.d.ts +6 -5
- package/dist/types/deploy/platforms/types.d.ts +15 -0
- package/dist/types/deploy/platforms/vercel.d.ts +3 -2
- package/dist/types/deploy/redirects.d.ts +31 -15
- package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
- package/dist/types/markdown/directives.d.ts +62 -0
- package/dist/types/markdown/features.d.ts +21 -0
- package/dist/types/markdown/mdast.d.ts +63 -0
- package/dist/types/openapi/asyncapi.d.ts +4 -2
- package/dist/types/openapi/model.d.ts +20 -6
- package/dist/types/search/sync/algolia.d.ts +3 -1
- package/docs/01-quickstart.mdx +3 -2
- package/docs/02-deployment.mdx +20 -9
- package/docs/08-faq.mdx +10 -3
- package/docs/advanced/changelog.mdx +1 -1
- package/docs/advanced/custom-pages.mdx +7 -5
- package/docs/cli/audit.mdx +19 -3
- package/docs/cli/doctor.mdx +2 -2
- package/docs/cli/evals.mdx +4 -4
- package/docs/cli/index.mdx +4 -1
- package/docs/cli/translate.mdx +4 -4
- package/docs/cli/version.mdx +1 -1
- package/docs/configuration/analytics.mdx +42 -2
- package/docs/configuration/assistant.mdx +1 -1
- package/docs/configuration/customization.mdx +6 -3
- package/docs/configuration/index.mdx +5 -3
- package/docs/configuration/search.mdx +2 -2
- package/docs/content/components.mdx +2 -2
- package/docs/content/frontmatter.mdx +5 -1
- package/docs/content/i18n.mdx +3 -1
- package/docs/content/index.mdx +8 -4
- package/docs/content/islands.mdx +1 -1
- package/docs/content/meta.mdx +7 -3
- package/docs/content/navigation.mdx +29 -4
- package/docs/content/sources.mdx +18 -16
- package/docs/content/syntax.mdx +14 -0
- package/docs/content/versioning.mdx +2 -1
- package/docs/discoverability/agent-discovery.mdx +22 -8
- package/docs/discoverability/index.mdx +4 -3
- package/docs/discoverability/llms-txt.mdx +2 -5
- package/docs/discoverability/markdown.mdx +6 -4
- package/docs/discoverability/mcp.mdx +4 -0
- package/docs/discoverability/metadata.mdx +3 -2
- package/docs/discoverability/open-graph.mdx +8 -4
- package/docs/discoverability/rss.mdx +4 -2
- package/docs/references/asyncapi.mdx +2 -2
- package/docs/references/graphql.mdx +2 -2
- package/docs/references/openapi.mdx +6 -4
- package/package.json +1 -1
- package/skills/blume-migrate/SKILL.md +6 -6
- package/skills/blume-migrate/references/docusaurus.md +6 -6
- package/skills/blume-migrate/references/fumadocs.md +1 -1
- package/skills/blume-migrate/references/mintlify.md +3 -3
- package/skills/blume-migrate/references/monorepo.md +1 -1
- package/skills/blume-migrate/references/nextra.md +2 -2
- package/skills/blume-migrate/references/starlight.md +5 -5
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +9 -4
- package/skills/blume-update-docs/references/audit-checklist.md +1 -1
- package/src/ai/agent-readability.ts +2 -2
- package/src/ai/agent-surface.ts +56 -0
- package/src/ai/ai-catalog.ts +6 -2
- package/src/ai/api/handlers.ts +2 -2
- package/src/ai/api/spec.ts +17 -4
- package/src/ai/api-catalog.ts +13 -3
- package/src/ai/ask-context.ts +14 -2
- package/src/ai/ask-data.ts +26 -12
- package/src/ai/changelog-markdown.ts +2 -2
- package/src/ai/component-markdown.ts +43 -37
- package/src/ai/link-headers.ts +22 -8
- package/src/ai/llms.ts +22 -10
- package/src/ai/markdown.ts +23 -11
- package/src/ai/mcp/discovery.ts +2 -2
- package/src/ai/mcp/query.ts +2 -2
- package/src/ai/mcp/server.ts +80 -9
- package/src/ai/openapi-components.ts +22 -6
- package/src/ai/relative-links.ts +99 -15
- package/src/ai/serializers.ts +2 -1
- package/src/ai/skills.ts +14 -1
- package/src/ai/static-expression.ts +416 -0
- package/src/ai/tar.ts +139 -12
- package/src/ai/visibility.ts +45 -14
- package/src/analytics/databuddy.ts +67 -0
- package/src/analytics/head.ts +8 -0
- package/src/analytics/index.ts +7 -0
- package/src/analytics/one-dollar-stats.ts +86 -0
- package/src/analytics/posthog.ts +21 -3
- package/src/analytics/schema.ts +4 -0
- package/src/astro/generate.ts +72 -28
- package/src/astro/include-hmr.ts +31 -12
- package/src/astro/include-refresh.ts +19 -8
- package/src/astro/integration.ts +108 -14
- package/src/astro/runtime-modules.ts +28 -13
- package/src/astro/templates.ts +139 -57
- package/src/audit/catalog.ts +2 -2
- package/src/audit/checks/assets.ts +25 -5
- package/src/audit/checks/content.ts +20 -2
- package/src/audit/checks/i18n.ts +46 -8
- package/src/audit/checks/indexability.ts +39 -19
- package/src/audit/checks/links.ts +22 -1
- package/src/audit/checks/llms.ts +6 -3
- package/src/audit/checks/network.ts +3 -1
- package/src/audit/checks/og-image.ts +10 -0
- package/src/audit/checks/robots.ts +6 -1
- package/src/audit/checks/sitemap.ts +56 -31
- package/src/audit/checks/social.ts +21 -2
- package/src/audit/crawl.ts +88 -15
- package/src/audit/graph.ts +3 -1
- package/src/audit/report.ts +54 -17
- package/src/audit/run.ts +13 -9
- package/src/audit/snapshot.ts +5 -0
- package/src/audit/types.ts +18 -0
- package/src/audit/url.ts +37 -6
- package/src/cli/args.ts +32 -0
- package/src/cli/build-failure.ts +50 -0
- package/src/cli/commands/audit.ts +11 -1
- package/src/cli/commands/build.ts +19 -5
- package/src/cli/commands/check.ts +2 -0
- package/src/cli/commands/doctor.ts +3 -1
- package/src/cli/commands/eval.ts +20 -18
- package/src/cli/commands/init.ts +15 -5
- package/src/cli/commands/preview.ts +15 -0
- package/src/cli/commands/sync.ts +2 -0
- package/src/cli/commands/translate.ts +18 -18
- package/src/cli/commands/upgrade.ts +11 -0
- package/src/cli/commands/validate.ts +3 -1
- package/src/cli/dev-lock.ts +157 -37
- package/src/cli/eject-scripts.ts +32 -7
- package/src/cli/env.ts +12 -1
- package/src/cli/init/scaffold.ts +117 -11
- package/src/cli/init/starter-spec.ts +235 -0
- package/src/cli/report-format.ts +11 -6
- package/src/components/colors.ts +19 -0
- package/src/components/content/AccordionItem.astro +26 -22
- package/src/components/content/Badge.astro +7 -12
- package/src/components/content/Card.astro +2 -2
- package/src/components/content/Component.astro +11 -5
- package/src/components/content/Expandable.astro +5 -1
- package/src/components/content/Frame.astro +2 -7
- package/src/components/content/GithubInfo.astro +12 -2
- package/src/components/content/Prompt.astro +2 -7
- package/src/components/content/Tab.astro +0 -1
- package/src/components/content/Tabs.astro +58 -5
- package/src/components/content/Tile.astro +1 -1
- package/src/components/content/Tooltip.astro +69 -7
- package/src/components/content/Tree.astro +7 -2
- package/src/components/content/TypeTable.astro +10 -5
- package/src/components/content/Update.astro +8 -2
- package/src/components/content/auto-type-table.ts +4 -1
- package/src/components/content/badge-color.ts +20 -0
- package/src/components/content/base-href.ts +14 -22
- package/src/components/content/inline-markdown.ts +27 -7
- package/src/components/copy-feedback.ts +35 -8
- package/src/components/islands/assistant.tsx +37 -6
- package/src/components/islands/base-path.ts +47 -10
- package/src/components/islands/hooks.ts +42 -21
- package/src/components/islands/webmcp.ts +16 -10
- package/src/components/layout/Banner.astro +23 -4
- package/src/components/layout/Breadcrumbs.astro +5 -2
- package/src/components/layout/DiscoveryLinks.astro +9 -5
- package/src/components/layout/Header.astro +23 -10
- package/src/components/layout/LanguageSwitcher.astro +2 -2
- package/src/components/layout/Logo.astro +5 -0
- package/src/components/layout/NavSelector.astro +8 -3
- package/src/components/layout/NavTabMenu.astro +133 -0
- package/src/components/layout/NavTree.astro +31 -12
- package/src/components/layout/NavTreeCache.astro +5 -2
- package/src/components/layout/NavTreeScript.astro +45 -5
- package/src/components/layout/PageActions.astro +4 -3
- package/src/components/layout/PageLayout.astro +28 -13
- package/src/components/layout/Pagination.astro +3 -3
- package/src/components/layout/ReferenceLayout.astro +4 -1
- package/src/components/layout/RootLayout.astro +25 -12
- package/src/components/layout/Search.astro +38 -14
- package/src/components/layout/VersionBanner.astro +2 -2
- package/src/components/layout/analytics-client.ts +14 -0
- package/src/components/layout/toc-active.ts +41 -0
- package/src/components/layout/toc-element.ts +8 -14
- package/src/components/openapi/ApiTagOperations.astro +2 -2
- package/src/components/openapi/AsyncApiOperation.astro +7 -4
- package/src/components/openapi/GraphqlChip.astro +2 -2
- package/src/components/openapi/GraphqlType.astro +11 -3
- package/src/components/openapi/MessageComposer.astro +1 -1
- package/src/components/openapi/Operation.astro +23 -5
- package/src/components/openapi/PanelTabs.astro +4 -1
- package/src/components/openapi/Playground.astro +8 -2
- package/src/components/openapi/RequestPanel.astro +13 -6
- package/src/components/openapi/SchemaProperty.astro +11 -33
- package/src/components/openapi/SchemaTable.astro +34 -52
- package/src/components/openapi/async.ts +38 -6
- package/src/components/openapi/helpers.ts +100 -12
- package/src/components/openapi/message-composer.ts +14 -1
- package/src/components/openapi/message-model.ts +12 -2
- package/src/components/openapi/message.ts +21 -3
- package/src/components/openapi/operation-model.ts +119 -17
- package/src/components/openapi/panel.ts +31 -5
- package/src/components/openapi/param-style.ts +181 -0
- package/src/components/openapi/playground-client.ts +110 -23
- package/src/components/openapi/playground-schema.ts +25 -6
- package/src/components/openapi/request.ts +192 -26
- package/src/components/openapi/schema-tree.ts +209 -0
- package/src/components/openapi/snippets.ts +113 -19
- package/src/components/openapi/validate-json.ts +1 -1
- package/src/components/openapi/ws-client.ts +18 -2
- package/src/core/base-path.ts +69 -8
- package/src/core/config-input.ts +13 -11
- package/src/core/config.ts +18 -4
- package/src/core/diagnostics.ts +219 -30
- package/src/core/directive-diagnostics.ts +99 -0
- package/src/core/frontmatter.ts +21 -18
- package/src/core/graph.ts +120 -11
- package/src/core/heading-markers.ts +5 -18
- package/src/core/i18n-ui.ts +39 -2
- package/src/core/i18n.ts +44 -5
- package/src/core/last-modified.ts +25 -3
- package/src/core/links.ts +7 -4
- package/src/core/load-module.ts +20 -0
- package/src/core/locale-links.ts +22 -18
- package/src/core/manifest.ts +3 -2
- package/src/core/meta.ts +89 -12
- package/src/core/nav-diagnostics.ts +56 -1
- package/src/core/navigation.ts +272 -83
- package/src/core/ordering-prefix.ts +27 -0
- package/src/core/project-graph.ts +50 -6
- package/src/core/safe-href.ts +53 -1
- package/src/core/safe-links.ts +11 -2
- package/src/core/schema.ts +94 -18
- package/src/core/sources/assets.ts +83 -41
- package/src/core/sources/contentful-rich-text.ts +28 -20
- package/src/core/sources/contentful.ts +25 -12
- package/src/core/sources/filesystem.ts +25 -3
- package/src/core/sources/github-releases.ts +131 -11
- package/src/core/sources/lexical.ts +30 -20
- package/src/core/sources/lower.ts +276 -56
- package/src/core/sources/mdx-remote.ts +52 -16
- package/src/core/sources/normalize.ts +456 -119
- package/src/core/sources/notion.ts +77 -34
- package/src/core/sources/obsidian.ts +40 -9
- package/src/core/sources/payload.ts +1 -0
- package/src/core/sources/portable-text.ts +85 -49
- package/src/core/sources/remote.ts +18 -2
- package/src/core/sources/resolve.ts +52 -33
- package/src/core/sources/sanity.ts +1 -0
- package/src/core/sources/strapi-blocks.ts +28 -19
- package/src/core/sources/strapi.ts +1 -0
- package/src/core/sources/types.ts +31 -0
- package/src/core/sources/watch.ts +20 -7
- package/src/core/standard-schema.ts +10 -6
- package/src/core/types.ts +9 -0
- package/src/core/ui-packs/ar.ts +13 -0
- package/src/core/ui-packs/bg.ts +13 -0
- package/src/core/ui-packs/bn.ts +13 -0
- package/src/core/ui-packs/ca.ts +13 -0
- package/src/core/ui-packs/cs.ts +13 -0
- package/src/core/ui-packs/da.ts +13 -0
- package/src/core/ui-packs/de.ts +13 -0
- package/src/core/ui-packs/el.ts +13 -0
- package/src/core/ui-packs/es.ts +13 -0
- package/src/core/ui-packs/fa.ts +13 -0
- package/src/core/ui-packs/fi.ts +13 -0
- package/src/core/ui-packs/fr.ts +13 -0
- package/src/core/ui-packs/he.ts +13 -0
- package/src/core/ui-packs/hi.ts +13 -0
- package/src/core/ui-packs/hr.ts +13 -0
- package/src/core/ui-packs/hu.ts +13 -0
- package/src/core/ui-packs/id.ts +13 -0
- package/src/core/ui-packs/it.ts +13 -0
- package/src/core/ui-packs/ja.ts +13 -0
- package/src/core/ui-packs/ko.ts +13 -0
- package/src/core/ui-packs/nl.ts +13 -0
- package/src/core/ui-packs/no.ts +13 -0
- package/src/core/ui-packs/pl.ts +13 -0
- package/src/core/ui-packs/pt-br.ts +13 -0
- package/src/core/ui-packs/pt.ts +13 -0
- package/src/core/ui-packs/ro.ts +13 -0
- package/src/core/ui-packs/ru.ts +13 -0
- package/src/core/ui-packs/sk.ts +13 -0
- package/src/core/ui-packs/sr.ts +13 -0
- package/src/core/ui-packs/sv.ts +13 -0
- package/src/core/ui-packs/th.ts +13 -0
- package/src/core/ui-packs/tr.ts +13 -0
- package/src/core/ui-packs/uk.ts +13 -0
- package/src/core/ui-packs/vi.ts +13 -0
- package/src/core/ui-packs/zh-tw.ts +13 -0
- package/src/core/ui-packs/zh.ts +13 -0
- package/src/core/version-cut.ts +69 -17
- package/src/core/versions.ts +4 -1
- package/src/deploy/adapters/node.ts +5 -2
- package/src/deploy/adapters/registry.ts +2 -1
- package/src/deploy/adapters/types.ts +13 -1
- package/src/deploy/artifacts.ts +52 -11
- package/src/deploy/cloudflare-negotiation.ts +27 -16
- package/src/deploy/headers.ts +75 -55
- package/src/deploy/node-headers.ts +148 -27
- package/src/deploy/platforms/cloudflare.ts +13 -3
- package/src/deploy/platforms/netlify.ts +83 -5
- package/src/deploy/platforms/node.ts +8 -5
- package/src/deploy/platforms/static.ts +2 -0
- package/src/deploy/platforms/types.ts +15 -0
- package/src/deploy/platforms/vercel.ts +10 -3
- package/src/deploy/redirects.ts +68 -21
- package/src/deploy/robots.ts +2 -2
- package/src/deploy/rss.ts +4 -3
- package/src/deploy/sitemap.ts +7 -5
- package/src/deploy/vercel-negotiation.ts +35 -3
- package/src/eval/agents.ts +10 -2
- package/src/eval/run.ts +25 -0
- package/src/markdown/base-links.ts +74 -29
- package/src/markdown/directives.ts +242 -36
- package/src/markdown/features.ts +17 -0
- package/src/markdown/index.ts +13 -9
- package/src/markdown/mdast.ts +5 -2
- package/src/markdown/relative-links.ts +15 -27
- package/src/markdown/route-snapshot.ts +37 -0
- package/src/og/card.ts +149 -9
- package/src/og/derive.ts +156 -5
- package/src/og/index.ts +1 -0
- package/src/openapi/asyncapi.ts +4 -2
- package/src/openapi/graphql-build.ts +28 -2
- package/src/openapi/model.ts +99 -27
- package/src/openapi/proxy.ts +63 -10
- package/src/openapi/render-mdx.ts +51 -3
- package/src/registry/eject.ts +297 -43
- package/src/search/adapters/version-scope.ts +30 -0
- package/src/search/documents.ts +89 -27
- package/src/search/popular.ts +2 -1
- package/src/search/sync/algolia.ts +36 -2
- package/src/seo/jsonld.ts +7 -3
- package/src/sources/registry.ts +5 -0
- package/src/theme/entry.ts +11 -4
- package/src/translate/agents.ts +6 -1
- package/src/translate/ledger.ts +26 -3
- package/src/translate/meta.ts +76 -24
- package/src/translate/run.ts +14 -8
- package/src/translate/validate.ts +14 -2
- package/src/translate/work-list.ts +91 -19
- package/src/upgrade/upgrade.ts +36 -4
- package/dist/cli/chunk-27g6wdth.js.map +0 -10
- package/dist/cli/chunk-2hn4b8z7.js.map +0 -12
- package/dist/cli/chunk-6crbhc3x.js.map +0 -14
- package/dist/cli/chunk-6hsn950k.js.map +0 -10
- package/dist/cli/chunk-79jhk4py.js.map +0 -35
- package/dist/cli/chunk-82bbrxdn.js +0 -51
- package/dist/cli/chunk-82bbrxdn.js.map +0 -10
- package/dist/cli/chunk-abh8yjkn.js +0 -31
- package/dist/cli/chunk-abh8yjkn.js.map +0 -10
- package/dist/cli/chunk-ce574jw2.js +0 -23
- package/dist/cli/chunk-ce574jw2.js.map +0 -10
- package/dist/cli/chunk-dh8cwk36.js.map +0 -10
- package/dist/cli/chunk-epjnccmv.js.map +0 -10
- package/dist/cli/chunk-fa25z98p.js.map +0 -11
- package/dist/cli/chunk-fs23ddbb.js.map +0 -35
- package/dist/cli/chunk-fz5wtpmh.js.map +0 -10
- package/dist/cli/chunk-jwyddg7y.js.map +0 -15
- package/dist/cli/chunk-kpf8rrjc.js.map +0 -19
- package/dist/cli/chunk-m3vmjgmq.js.map +0 -10
- package/dist/cli/chunk-mb2919y2.js.map +0 -10
- package/dist/cli/chunk-q5163e60.js.map +0 -11
- package/dist/cli/chunk-qkqwkpte.js.map +0 -182
- package/dist/cli/chunk-qs4q5p4e.js.map +0 -10
- package/dist/cli/chunk-s1p84fyh.js.map +0 -10
- package/dist/cli/chunk-wgm7m9qk.js.map +0 -36
- package/dist/cli/chunk-yt5n7ppj.js.map +0 -10
- package/dist/cli/chunk-yw7dm696.js.map +0 -14
- /package/dist/cli/{chunk-s6jhgk0q.js.map → chunk-2eytanqx.js.map} +0 -0
- /package/dist/cli/{chunk-hdpx1tax.js.map → chunk-2z928egk.js.map} +0 -0
- /package/dist/cli/{chunk-ah61y8py.js.map → chunk-364znk6q.js.map} +0 -0
- /package/dist/cli/{chunk-vtk4a6dg.js.map → chunk-8ktnccpt.js.map} +0 -0
- /package/dist/cli/{chunk-fxypxtvm.js.map → chunk-a58773jm.js.map} +0 -0
- /package/dist/cli/{chunk-wm7js3j9.js.map → chunk-b07cmahc.js.map} +0 -0
- /package/dist/cli/{chunk-zxcczpyx.js.map → chunk-r20tn01b.js.map} +0 -0
- /package/dist/cli/{chunk-5shv93fd.js.map → chunk-tkacnehg.js.map} +0 -0
- /package/dist/cli/{chunk-zxh4d9vy.js.map → chunk-tzmab476.js.map} +0 -0
|
@@ -1,17 +1,26 @@
|
|
|
1
|
+
import { withServerDefaults } from "../../openapi/model.ts";
|
|
1
2
|
import {
|
|
3
|
+
declaredExample,
|
|
2
4
|
exampleValue,
|
|
3
5
|
objectProperties,
|
|
4
6
|
resolveSchema,
|
|
5
7
|
toJson,
|
|
6
8
|
} from "./helpers.ts";
|
|
7
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
ComponentsLike,
|
|
11
|
+
ParameterLike,
|
|
12
|
+
SchemaLike,
|
|
13
|
+
SpecValue,
|
|
14
|
+
} from "./helpers.ts";
|
|
8
15
|
import {
|
|
9
16
|
declaredTypes,
|
|
10
17
|
inputValue,
|
|
11
18
|
scalarType,
|
|
12
19
|
validationSchema,
|
|
13
20
|
} from "./playground-schema.ts";
|
|
21
|
+
import { bodyEncoding } from "./request.ts";
|
|
14
22
|
import type {
|
|
23
|
+
ParamSerialization,
|
|
15
24
|
PlaygroundAuthInput,
|
|
16
25
|
PlaygroundBody,
|
|
17
26
|
PlaygroundBodyField,
|
|
@@ -30,9 +39,31 @@ import type { OperationSecurity, ResolvedScheme } from "./security.ts";
|
|
|
30
39
|
|
|
31
40
|
interface MediaTypeLike {
|
|
32
41
|
schema?: SchemaLike;
|
|
33
|
-
example?:
|
|
42
|
+
example?: SpecValue;
|
|
43
|
+
examples?: SpecValue;
|
|
44
|
+
encoding?: SpecValue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** A server entry: its URL template and the variables that fill it. */
|
|
48
|
+
export interface ServerLike {
|
|
49
|
+
url?: string;
|
|
50
|
+
variables?: SpecValue;
|
|
34
51
|
}
|
|
35
52
|
|
|
53
|
+
/**
|
|
54
|
+
* The servers an operation is sent to: its own `servers` when it declares
|
|
55
|
+
* any, else its path item's, else the document's — each level overrides the
|
|
56
|
+
* one above it, as the OpenAPI spec defines.
|
|
57
|
+
*/
|
|
58
|
+
export const effectiveServers = (
|
|
59
|
+
operation: ServerLike[] | undefined,
|
|
60
|
+
pathItem: ServerLike[] | undefined,
|
|
61
|
+
document: ServerLike[] | undefined
|
|
62
|
+
): ServerLike[] =>
|
|
63
|
+
[operation, pathItem, document].find(
|
|
64
|
+
(servers) => Array.isArray(servers) && servers.length > 0
|
|
65
|
+
) ?? [];
|
|
66
|
+
|
|
36
67
|
/** Primitive types the flat-body fields UI can edit directly. */
|
|
37
68
|
const PRIMITIVE_TYPES = {
|
|
38
69
|
boolean: true,
|
|
@@ -47,6 +78,45 @@ const isExampleObject = (
|
|
|
47
78
|
): value is Record<string, SpecValue> =>
|
|
48
79
|
typeof value === "object" && value !== null && !Array.isArray(value);
|
|
49
80
|
|
|
81
|
+
const isString = (value: SpecValue): value is string =>
|
|
82
|
+
typeof value === "string";
|
|
83
|
+
|
|
84
|
+
const isBoolean = (value: SpecValue): value is boolean =>
|
|
85
|
+
typeof value === "boolean";
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The `style`/`explode` a parameter or form-body encoding declares, kept only
|
|
89
|
+
* when declared so the embedded model stays small; the request builder
|
|
90
|
+
* applies the OpenAPI defaults for the rest.
|
|
91
|
+
*/
|
|
92
|
+
const serialization = (node: Record<string, SpecValue>): ParamSerialization => {
|
|
93
|
+
const rule: ParamSerialization = {};
|
|
94
|
+
if (isString(node.style)) {
|
|
95
|
+
rule.style = node.style;
|
|
96
|
+
}
|
|
97
|
+
if (isBoolean(node.explode)) {
|
|
98
|
+
rule.explode = node.explode;
|
|
99
|
+
}
|
|
100
|
+
return rule;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/** A form body's declared per-field `encoding`, when it names any style. */
|
|
104
|
+
const formEncoding = (
|
|
105
|
+
encoding: SpecValue
|
|
106
|
+
): PlaygroundBody["encoding"] | undefined => {
|
|
107
|
+
if (!isExampleObject(encoding)) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
const fields: NonNullable<PlaygroundBody["encoding"]> = {};
|
|
111
|
+
for (const [name, entry] of Object.entries(encoding)) {
|
|
112
|
+
const rule = isExampleObject(entry) ? serialization(entry) : {};
|
|
113
|
+
if (Object.keys(rule).length > 0) {
|
|
114
|
+
fields[name] = rule;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return Object.keys(fields).length > 0 ? fields : undefined;
|
|
118
|
+
};
|
|
119
|
+
|
|
50
120
|
/**
|
|
51
121
|
* Playground inputs for the operation's parameters. Cookie params are skipped
|
|
52
122
|
* (not supported in v1 — browsers won't let a page set arbitrary cookies).
|
|
@@ -57,7 +127,8 @@ const isExampleObject = (
|
|
|
57
127
|
*/
|
|
58
128
|
const modelParams = (
|
|
59
129
|
parameters: ParameterLike[],
|
|
60
|
-
schemas: Record<string, SchemaLike
|
|
130
|
+
schemas: Record<string, SchemaLike>,
|
|
131
|
+
components: ComponentsLike | undefined
|
|
61
132
|
): PlaygroundParam[] => {
|
|
62
133
|
const params: PlaygroundParam[] = [];
|
|
63
134
|
for (const param of parameters) {
|
|
@@ -78,8 +149,12 @@ const modelParams = (
|
|
|
78
149
|
required,
|
|
79
150
|
type: scalarType(param.schema, schemas),
|
|
80
151
|
value: required
|
|
81
|
-
? inputValue(
|
|
152
|
+
? inputValue(
|
|
153
|
+
declaredExample(param, components) ??
|
|
154
|
+
exampleValue(param.schema, schemas)
|
|
155
|
+
)
|
|
82
156
|
: "",
|
|
157
|
+
...serialization(param),
|
|
83
158
|
});
|
|
84
159
|
}
|
|
85
160
|
return params;
|
|
@@ -127,7 +202,12 @@ const bodyFields = (
|
|
|
127
202
|
description: propertySchema.description,
|
|
128
203
|
enum: propertySchema.enum?.map(String),
|
|
129
204
|
name,
|
|
130
|
-
required:
|
|
205
|
+
// A `readOnly` property's `required` binds responses only: a request
|
|
206
|
+
// shouldn't send the server-generated field at all.
|
|
207
|
+
required:
|
|
208
|
+
required.has(name) &&
|
|
209
|
+
property.readOnly !== true &&
|
|
210
|
+
propertySchema.readOnly !== true,
|
|
131
211
|
type,
|
|
132
212
|
value: inputValue(defaults?.[name]),
|
|
133
213
|
});
|
|
@@ -135,26 +215,42 @@ const bodyFields = (
|
|
|
135
215
|
return fields;
|
|
136
216
|
};
|
|
137
217
|
|
|
138
|
-
/**
|
|
218
|
+
/**
|
|
219
|
+
* The playground's body editor state, when the operation takes a request body.
|
|
220
|
+
* JSON, form-urlencoded, and multipart bodies are edited as JSON (typed fields
|
|
221
|
+
* when flat) and serialized for their media type on send; any other media
|
|
222
|
+
* type is raw text, prefilled with a string example as written.
|
|
223
|
+
*/
|
|
139
224
|
const modelBody = (
|
|
140
225
|
requestBody: { content?: Record<string, MediaTypeLike> } | undefined,
|
|
141
|
-
schemas: Record<string, SchemaLike
|
|
226
|
+
schemas: Record<string, SchemaLike>,
|
|
227
|
+
components: ComponentsLike | undefined
|
|
142
228
|
): PlaygroundBody | undefined => {
|
|
143
229
|
const media = jsonContentType(requestBody?.content);
|
|
144
230
|
if (!media) {
|
|
145
231
|
return undefined;
|
|
146
232
|
}
|
|
147
233
|
const [contentType, mediaType] = media;
|
|
148
|
-
// SAFETY: `example` comes from the parsed spec document (YAML/JSON), whose
|
|
149
|
-
// values are exactly the JSON-shaped tree `SpecValue` models.
|
|
150
234
|
const exampleData =
|
|
151
|
-
(mediaType
|
|
152
|
-
|
|
235
|
+
declaredExample(mediaType, components) ??
|
|
236
|
+
exampleValue(mediaType.schema, schemas);
|
|
237
|
+
const encoding = bodyEncoding(contentType);
|
|
238
|
+
const raw = encoding === "raw";
|
|
239
|
+
const body: PlaygroundBody = {
|
|
153
240
|
contentType,
|
|
154
|
-
example:
|
|
155
|
-
|
|
241
|
+
example:
|
|
242
|
+
raw && isString(exampleData) ? exampleData : (toJson(exampleData) ?? ""),
|
|
243
|
+
fields: raw
|
|
244
|
+
? undefined
|
|
245
|
+
: bodyFields(mediaType.schema, schemas, exampleData),
|
|
156
246
|
schema: validationSchema(mediaType.schema, schemas),
|
|
157
247
|
};
|
|
248
|
+
const fieldEncoding =
|
|
249
|
+
encoding === "form" ? formEncoding(mediaType.encoding) : undefined;
|
|
250
|
+
if (fieldEncoding) {
|
|
251
|
+
body.encoding = fieldEncoding;
|
|
252
|
+
}
|
|
253
|
+
return body;
|
|
158
254
|
};
|
|
159
255
|
|
|
160
256
|
const AUTHORIZATION_HEADER = { in: "header", name: "Authorization" } as const;
|
|
@@ -241,8 +337,11 @@ export const operationModel = (args: {
|
|
|
241
337
|
/** Pre-merged/resolved (`mergeParameters` output). */
|
|
242
338
|
parameters: ParameterLike[];
|
|
243
339
|
requestBody?: { content?: Record<string, MediaTypeLike> };
|
|
244
|
-
servers
|
|
340
|
+
/** The operation's effective servers (`effectiveServers` output). */
|
|
341
|
+
servers: ServerLike[];
|
|
245
342
|
schemas: Record<string, SchemaLike>;
|
|
343
|
+
/** The document's `components`, which `$ref`'d examples resolve against. */
|
|
344
|
+
components?: ComponentsLike;
|
|
246
345
|
security: OperationSecurity;
|
|
247
346
|
}): PlaygroundModel => ({
|
|
248
347
|
// First alternative only — the spec's preferred way to authorize, matching
|
|
@@ -252,9 +351,12 @@ export const operationModel = (args: {
|
|
|
252
351
|
return input ? [input] : [];
|
|
253
352
|
}),
|
|
254
353
|
authOptional: args.security.optional,
|
|
255
|
-
body: modelBody(args.requestBody, args.schemas),
|
|
354
|
+
body: modelBody(args.requestBody, args.schemas, args.components),
|
|
256
355
|
method: args.method.toUpperCase(),
|
|
257
|
-
params: modelParams(args.parameters, args.schemas),
|
|
356
|
+
params: modelParams(args.parameters, args.schemas, args.components),
|
|
258
357
|
path: args.path,
|
|
259
|
-
|
|
358
|
+
// Variables resolve to their defaults: the samples and Send need a real URL.
|
|
359
|
+
servers: args.servers.map((server) =>
|
|
360
|
+
withServerDefaults(server.url ?? "", server.variables)
|
|
361
|
+
),
|
|
260
362
|
});
|
|
@@ -18,23 +18,49 @@ class BlumePanelTabs extends HTMLElement {
|
|
|
18
18
|
|
|
19
19
|
const activate = (key: string): void => {
|
|
20
20
|
for (const tab of tabs) {
|
|
21
|
-
tab.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
const selected = tab.dataset.panelTab === key;
|
|
22
|
+
tab.setAttribute("aria-selected", selected ? "true" : "false");
|
|
23
|
+
// Roving tabindex: Tab reaches the selected tab only, the arrow keys
|
|
24
|
+
// move between the rest.
|
|
25
|
+
tab.tabIndex = selected ? 0 : -1;
|
|
25
26
|
}
|
|
26
27
|
for (const panel of panels) {
|
|
27
28
|
panel.classList.toggle("hidden", panel.dataset.panel !== key);
|
|
28
29
|
}
|
|
29
30
|
};
|
|
30
31
|
|
|
31
|
-
for (const tab of tabs) {
|
|
32
|
+
for (const [index, tab] of tabs.entries()) {
|
|
32
33
|
tab.addEventListener("click", () => {
|
|
33
34
|
const key = tab.dataset.panelTab;
|
|
34
35
|
if (key) {
|
|
35
36
|
activate(key);
|
|
36
37
|
}
|
|
37
38
|
});
|
|
39
|
+
// The WAI-ARIA tabs keyboard pattern, matching `<Tabs>`: arrows move and
|
|
40
|
+
// wrap, Home and End jump to the ends, and focus follows selection.
|
|
41
|
+
tab.addEventListener("keydown", (event) => {
|
|
42
|
+
const last = tabs.length - 1;
|
|
43
|
+
// The strip runs right to left under dir="rtl", so the arrows swap.
|
|
44
|
+
const rtl = getComputedStyle(this).direction === "rtl";
|
|
45
|
+
let next: number | undefined;
|
|
46
|
+
if (event.key === (rtl ? "ArrowLeft" : "ArrowRight")) {
|
|
47
|
+
next = index === last ? 0 : index + 1;
|
|
48
|
+
} else if (event.key === (rtl ? "ArrowRight" : "ArrowLeft")) {
|
|
49
|
+
next = index === 0 ? last : index - 1;
|
|
50
|
+
} else if (event.key === "Home") {
|
|
51
|
+
next = 0;
|
|
52
|
+
} else if (event.key === "End") {
|
|
53
|
+
next = last;
|
|
54
|
+
}
|
|
55
|
+
const target = next === undefined ? undefined : tabs[next];
|
|
56
|
+
const key = target?.dataset.panelTab;
|
|
57
|
+
if (!(target && key)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
event.preventDefault();
|
|
61
|
+
activate(key);
|
|
62
|
+
target.focus();
|
|
63
|
+
});
|
|
38
64
|
}
|
|
39
65
|
|
|
40
66
|
if (copy) {
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAPI 3 parameter serialization — `style` and `explode` — for the
|
|
3
|
+
* playground's one request builder (`request.ts`). An array or object value
|
|
4
|
+
* has a different wire shape per style: `tags: ["dog", "cat"]` is
|
|
5
|
+
* `tags=dog&tags=cat` in a query (form, exploded), `dog,cat` in a path
|
|
6
|
+
* (simple), and `filter: { color: "red" }` is `filter[color]=red` as a
|
|
7
|
+
* deepObject. Ships in the client bundle with `request.ts`, so it stays
|
|
8
|
+
* dependency-free.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** Parsed JSON: everything `JSON.parse` can produce. */
|
|
12
|
+
export type JsonValue =
|
|
13
|
+
| string
|
|
14
|
+
| number
|
|
15
|
+
| boolean
|
|
16
|
+
| null
|
|
17
|
+
| JsonValue[]
|
|
18
|
+
| { [key: string]: JsonValue };
|
|
19
|
+
|
|
20
|
+
/** A value lowered for serialization: a scalar, a list's items, or an object's entries. */
|
|
21
|
+
export type StyledValue =
|
|
22
|
+
| { kind: "scalar"; text: string }
|
|
23
|
+
| { kind: "list"; items: string[] }
|
|
24
|
+
| { kind: "object"; entries: [string, string][] };
|
|
25
|
+
|
|
26
|
+
// `typeof` checks live in named predicates (the form the oxlint anti-slop
|
|
27
|
+
// config sanctions).
|
|
28
|
+
const isString = (value: JsonValue): value is string =>
|
|
29
|
+
typeof value === "string";
|
|
30
|
+
|
|
31
|
+
/** Whether a parsed value is a plain JSON object. */
|
|
32
|
+
export const isJsonObject = (
|
|
33
|
+
value: JsonValue
|
|
34
|
+
): value is { [key: string]: JsonValue } =>
|
|
35
|
+
typeof value === "object" && value !== null && !Array.isArray(value);
|
|
36
|
+
|
|
37
|
+
/** One member as wire text: a string verbatim, anything else as its JSON. */
|
|
38
|
+
export const memberText = (value: JsonValue): string =>
|
|
39
|
+
isString(value) ? value : JSON.stringify(value);
|
|
40
|
+
|
|
41
|
+
/** A parsed value, lowered: arrays to items, objects to entries, the rest to text. */
|
|
42
|
+
export const styledValue = (value: JsonValue): StyledValue => {
|
|
43
|
+
if (Array.isArray(value)) {
|
|
44
|
+
return { items: value.map(memberText), kind: "list" };
|
|
45
|
+
}
|
|
46
|
+
if (isJsonObject(value)) {
|
|
47
|
+
return {
|
|
48
|
+
entries: Object.entries(value).map(([key, member]) => [
|
|
49
|
+
key,
|
|
50
|
+
memberText(member),
|
|
51
|
+
]),
|
|
52
|
+
kind: "object",
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return { kind: "scalar", text: memberText(value) };
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* A playground input's text as a styled value. Only an array- or object-typed
|
|
60
|
+
* parameter reads its text as JSON — its prefill is the example's JSON
|
|
61
|
+
* (`["dog","cat"]`) — and only when the text parses to that shape; anything
|
|
62
|
+
* else is sent as the one scalar it reads as.
|
|
63
|
+
*/
|
|
64
|
+
export const parseStyledValue = (text: string, type: string): StyledValue => {
|
|
65
|
+
if (type === "array" || type === "object") {
|
|
66
|
+
let parsed: JsonValue;
|
|
67
|
+
try {
|
|
68
|
+
parsed = JSON.parse(text);
|
|
69
|
+
} catch {
|
|
70
|
+
return { kind: "scalar", text };
|
|
71
|
+
}
|
|
72
|
+
if (type === "array" ? Array.isArray(parsed) : isJsonObject(parsed)) {
|
|
73
|
+
return styledValue(parsed);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return { kind: "scalar", text };
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/** The OpenAPI 3 default style: `form` in a query or cookie, `simple` elsewhere. */
|
|
80
|
+
export const defaultStyle = (location: string): string =>
|
|
81
|
+
location === "query" || location === "cookie" ? "form" : "simple";
|
|
82
|
+
|
|
83
|
+
/** `explode` when the spec leaves it out: true for `form`, false for the rest. */
|
|
84
|
+
export const defaultExplode = (style: string): boolean => style === "form";
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* A list or object joined into one value: exploded object entries as
|
|
88
|
+
* `key=value` separated by `separator`, exploded items by `separator`, and
|
|
89
|
+
* the unexploded forms comma-separated (`a,b`, `key,value,key2,value2`).
|
|
90
|
+
*/
|
|
91
|
+
const joined = (
|
|
92
|
+
value: StyledValue,
|
|
93
|
+
explode: boolean,
|
|
94
|
+
encode: (text: string) => string,
|
|
95
|
+
separator: string
|
|
96
|
+
): string => {
|
|
97
|
+
if (value.kind === "scalar") {
|
|
98
|
+
return encode(value.text);
|
|
99
|
+
}
|
|
100
|
+
if (value.kind === "list") {
|
|
101
|
+
return value.items.map(encode).join(explode ? separator : ",");
|
|
102
|
+
}
|
|
103
|
+
return explode
|
|
104
|
+
? value.entries
|
|
105
|
+
.map(([key, member]) => `${encode(key)}=${encode(member)}`)
|
|
106
|
+
.join(separator)
|
|
107
|
+
: value.entries.flat().map(encode).join(",");
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* A query parameter, or an `application/x-www-form-urlencoded` body field, as
|
|
112
|
+
* its percent-encoded `name=value` pairs in order. Exploded lists repeat the
|
|
113
|
+
* name (`tags=dog&tags=cat`) and exploded objects spread their entries
|
|
114
|
+
* (`color=red&size=L`) whatever the style; unexploded ones join with the
|
|
115
|
+
* style's delimiter — `,` for form, `%20` for spaceDelimited, `|` for
|
|
116
|
+
* pipeDelimited. deepObject nests entries under the name (`filter[color]=red`).
|
|
117
|
+
*/
|
|
118
|
+
export const queryPairs = (
|
|
119
|
+
name: string,
|
|
120
|
+
value: StyledValue,
|
|
121
|
+
style: string,
|
|
122
|
+
explode: boolean
|
|
123
|
+
): string[] => {
|
|
124
|
+
const key = encodeURIComponent(name);
|
|
125
|
+
if (value.kind === "object" && style === "deepObject") {
|
|
126
|
+
return value.entries.map(
|
|
127
|
+
([entry, member]) =>
|
|
128
|
+
`${key}[${encodeURIComponent(entry)}]=${encodeURIComponent(member)}`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
if (value.kind === "list" && explode) {
|
|
132
|
+
return value.items.map((item) => `${key}=${encodeURIComponent(item)}`);
|
|
133
|
+
}
|
|
134
|
+
if (value.kind === "object" && explode) {
|
|
135
|
+
return value.entries.map(
|
|
136
|
+
([entry, member]) =>
|
|
137
|
+
`${encodeURIComponent(entry)}=${encodeURIComponent(member)}`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
let delimiter = ",";
|
|
141
|
+
if (style === "spaceDelimited") {
|
|
142
|
+
delimiter = "%20";
|
|
143
|
+
} else if (style === "pipeDelimited") {
|
|
144
|
+
delimiter = "|";
|
|
145
|
+
}
|
|
146
|
+
const text =
|
|
147
|
+
value.kind === "scalar"
|
|
148
|
+
? encodeURIComponent(value.text)
|
|
149
|
+
: (value.kind === "list" ? value.items : value.entries.flat())
|
|
150
|
+
.map(encodeURIComponent)
|
|
151
|
+
.join(delimiter);
|
|
152
|
+
return [`${key}=${text}`];
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* A path or header parameter's value: `simple` (`dog,cat`), or one of the
|
|
157
|
+
* path-only `label` (`.dog.cat`) and `matrix` (`;tags=dog;tags=cat`) styles.
|
|
158
|
+
* `encode` percent-encodes a path segment; a header passes values through.
|
|
159
|
+
*/
|
|
160
|
+
export const templateValue = (
|
|
161
|
+
name: string,
|
|
162
|
+
value: StyledValue,
|
|
163
|
+
style: string,
|
|
164
|
+
explode: boolean,
|
|
165
|
+
encode: (text: string) => string
|
|
166
|
+
): string => {
|
|
167
|
+
if (style === "label") {
|
|
168
|
+
return `.${joined(value, explode, encode, ".")}`;
|
|
169
|
+
}
|
|
170
|
+
if (style !== "matrix") {
|
|
171
|
+
return joined(value, explode, encode, ",");
|
|
172
|
+
}
|
|
173
|
+
const key = encode(name);
|
|
174
|
+
if (value.kind === "list" && explode) {
|
|
175
|
+
return value.items.map((item) => `;${key}=${encode(item)}`).join("");
|
|
176
|
+
}
|
|
177
|
+
if (value.kind === "object" && explode) {
|
|
178
|
+
return `;${joined(value, true, encode, ";")}`;
|
|
179
|
+
}
|
|
180
|
+
return `;${key}=${joined(value, false, encode, ",")}`;
|
|
181
|
+
};
|
|
@@ -16,8 +16,14 @@ import type {
|
|
|
16
16
|
PlaygroundModel,
|
|
17
17
|
RequestValues,
|
|
18
18
|
} from "./request.ts";
|
|
19
|
-
import {
|
|
20
|
-
|
|
19
|
+
import {
|
|
20
|
+
bodyEncoding,
|
|
21
|
+
buildRequest,
|
|
22
|
+
PROXY_HEADERS_HEADER,
|
|
23
|
+
redactAuth,
|
|
24
|
+
} from "./request.ts";
|
|
25
|
+
import { fetchRefusesMethod, sampleLanguages } from "./snippets.ts";
|
|
26
|
+
import type { RequestSample } from "./snippets.ts";
|
|
21
27
|
import { validateJson } from "./validate-json.ts";
|
|
22
28
|
|
|
23
29
|
/**
|
|
@@ -42,6 +48,16 @@ const COOKIE_MESSAGE =
|
|
|
42
48
|
"send the cookie credential you entered. Copy the sample above and run it " +
|
|
43
49
|
"from a terminal instead.";
|
|
44
50
|
|
|
51
|
+
/**
|
|
52
|
+
* fetch refuses the TRACE method outright, proxied or not: the browser throws
|
|
53
|
+
* before anything is sent, which the send's catch would misdiagnose as the
|
|
54
|
+
* CORS wall or an unreachable host. The cURL and Python samples can send it.
|
|
55
|
+
*/
|
|
56
|
+
const TRACE_MESSAGE =
|
|
57
|
+
"Browsers don't allow a page to send a `TRACE` request, so this panel " +
|
|
58
|
+
"can't send it. Run the cURL or Python sample above from a terminal " +
|
|
59
|
+
"instead.";
|
|
60
|
+
|
|
45
61
|
/**
|
|
46
62
|
* A fetch that fails before any response when the API couldn't be reached at
|
|
47
63
|
* all — a mistyped host, a refused connection, no network — rather than
|
|
@@ -155,6 +171,80 @@ const prettyBody = (text: string): string => {
|
|
|
155
171
|
}
|
|
156
172
|
};
|
|
157
173
|
|
|
174
|
+
/*
|
|
175
|
+
* Remembered credentials are a convenience, never a requirement. With storage
|
|
176
|
+
* blocked (Safari's "Block All Cookies", a sandboxed iframe) even reading the
|
|
177
|
+
* `localStorage` global throws a SecurityError, which would take the whole
|
|
178
|
+
* panel down during init; each access is guarded so the form simply forgets.
|
|
179
|
+
*/
|
|
180
|
+
const readStored = (key: string): string | null => {
|
|
181
|
+
try {
|
|
182
|
+
return localStorage.getItem(key);
|
|
183
|
+
} catch {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const writeStored = (key: string, value: string): void => {
|
|
189
|
+
try {
|
|
190
|
+
localStorage.setItem(key, value);
|
|
191
|
+
} catch {
|
|
192
|
+
// Blocked or full storage: the credentials stay in the form only.
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const removeStored = (key: string): void => {
|
|
197
|
+
try {
|
|
198
|
+
localStorage.removeItem(key);
|
|
199
|
+
} catch {
|
|
200
|
+
// Blocked storage never held the entry in the first place.
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* The headers a live send carries. A `Cookie` header can still arrive via a
|
|
206
|
+
* spec-declared header parameter; the browser would silently drop the
|
|
207
|
+
* forbidden name anyway, so it is stripped rather than earn a console
|
|
208
|
+
* warning. Blume's own proxy — always a path on this site — forwards only the
|
|
209
|
+
* headers named in {@link PROXY_HEADERS_HEADER}, never ones the browser or
|
|
210
|
+
* the platform add on their own; a multipart body's Content-Type is fetch's,
|
|
211
|
+
* written with the boundary, so it is named without being set. An external
|
|
212
|
+
* proxy on another origin gets no such header: it would only be one more name
|
|
213
|
+
* for its preflight to allow.
|
|
214
|
+
*/
|
|
215
|
+
const sendHeaders = (sample: RequestSample, proxy: string) => {
|
|
216
|
+
const headers = { ...sample.headers };
|
|
217
|
+
delete headers.Cookie;
|
|
218
|
+
if (proxy.startsWith("/") && !proxy.startsWith("//")) {
|
|
219
|
+
const names = Object.keys(headers);
|
|
220
|
+
if (sample.formData) {
|
|
221
|
+
names.push("Content-Type");
|
|
222
|
+
}
|
|
223
|
+
headers[PROXY_HEADERS_HEADER] = names.join(", ");
|
|
224
|
+
}
|
|
225
|
+
return headers;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* The body a live send carries: multipart parts as `FormData`, anything else
|
|
230
|
+
* as its text. fetch throws a synchronous TypeError for a GET/HEAD with a
|
|
231
|
+
* body — which the send's catch would mislabel as CORS — so a spec that
|
|
232
|
+
* declares a GET requestBody keeps its samples, but the live send drops it.
|
|
233
|
+
*/
|
|
234
|
+
const sendBody = (sample: RequestSample): string | FormData | undefined => {
|
|
235
|
+
if (sample.method === "GET" || sample.method === "HEAD") {
|
|
236
|
+
return undefined;
|
|
237
|
+
}
|
|
238
|
+
if (!sample.formData) {
|
|
239
|
+
return sample.body;
|
|
240
|
+
}
|
|
241
|
+
const form = new FormData();
|
|
242
|
+
for (const [name, value] of sample.formData) {
|
|
243
|
+
form.append(name, value);
|
|
244
|
+
}
|
|
245
|
+
return form;
|
|
246
|
+
};
|
|
247
|
+
|
|
158
248
|
/** A one-line text element for the response/error regions. */
|
|
159
249
|
const line = (className: string, text: string): HTMLElement => {
|
|
160
250
|
const el = document.createElement("div");
|
|
@@ -302,9 +392,11 @@ export const initPlayground = (root: HTMLElement): void => {
|
|
|
302
392
|
}
|
|
303
393
|
// An emptied editor means "no body" (see `bodyFor`), not invalid JSON —
|
|
304
394
|
// reporting a syntax error there would block a send the request builder is
|
|
305
|
-
// perfectly happy to make.
|
|
395
|
+
// perfectly happy to make. A raw media type (`text/plain`, XML) isn't
|
|
396
|
+
// JSON at all, so it isn't checked as JSON either.
|
|
397
|
+
const raw = bodyEncoding(model.body?.contentType ?? "") === "raw";
|
|
306
398
|
const errors =
|
|
307
|
-
bodyArea.value.trim() === ""
|
|
399
|
+
raw || bodyArea.value.trim() === ""
|
|
308
400
|
? []
|
|
309
401
|
: validateJson(bodyArea.value, model.body?.schema);
|
|
310
402
|
bodyErrors.textContent = "";
|
|
@@ -316,7 +408,7 @@ export const initPlayground = (root: HTMLElement): void => {
|
|
|
316
408
|
|
|
317
409
|
/** Restore remembered credentials into the inputs and re-check the box. */
|
|
318
410
|
const restoreAuth = (): void => {
|
|
319
|
-
const stored =
|
|
411
|
+
const stored = readStored(storageKey);
|
|
320
412
|
if (!stored) {
|
|
321
413
|
return;
|
|
322
414
|
}
|
|
@@ -355,7 +447,7 @@ export const initPlayground = (root: HTMLElement): void => {
|
|
|
355
447
|
}
|
|
356
448
|
} catch {
|
|
357
449
|
// A corrupt entry (older format, manual edit) must not break init.
|
|
358
|
-
|
|
450
|
+
removeStored(storageKey);
|
|
359
451
|
}
|
|
360
452
|
};
|
|
361
453
|
|
|
@@ -374,12 +466,12 @@ export const initPlayground = (root: HTMLElement): void => {
|
|
|
374
466
|
)
|
|
375
467
|
);
|
|
376
468
|
const table = document.createElement("table");
|
|
377
|
-
table.className = "w-full text-
|
|
469
|
+
table.className = "w-full text-start text-xs";
|
|
378
470
|
for (const [name, value] of res.headers) {
|
|
379
471
|
const row = document.createElement("tr");
|
|
380
472
|
const header = document.createElement("th");
|
|
381
473
|
header.setAttribute("scope", "row");
|
|
382
|
-
header.className = "
|
|
474
|
+
header.className = "pe-3 align-top font-medium text-muted-foreground";
|
|
383
475
|
header.textContent = name;
|
|
384
476
|
const cell = document.createElement("td");
|
|
385
477
|
cell.className = "break-all font-mono text-foreground";
|
|
@@ -423,6 +515,11 @@ export const initPlayground = (root: HTMLElement): void => {
|
|
|
423
515
|
if (!response || sending) {
|
|
424
516
|
return;
|
|
425
517
|
}
|
|
518
|
+
if (fetchRefusesMethod(model.method)) {
|
|
519
|
+
response.textContent = "";
|
|
520
|
+
response.append(line(ERROR_TEXT, TRACE_MESSAGE));
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
426
523
|
if (validateBody().length > 0) {
|
|
427
524
|
return;
|
|
428
525
|
}
|
|
@@ -464,11 +561,7 @@ export const initPlayground = (root: HTMLElement): void => {
|
|
|
464
561
|
sample.url
|
|
465
562
|
)}`
|
|
466
563
|
: sample.url;
|
|
467
|
-
|
|
468
|
-
// the browser would silently drop the forbidden name anyway, so strip it
|
|
469
|
-
// rather than earn a console warning.
|
|
470
|
-
const headers = { ...sample.headers };
|
|
471
|
-
delete headers.Cookie;
|
|
564
|
+
const headers = sendHeaders(sample, proxy);
|
|
472
565
|
// fetch surfaces an invalid URL or header value as the same TypeError a
|
|
473
566
|
// CORS rejection produces, and the catch below would misdiagnose it as the
|
|
474
567
|
// CORS wall. Both are validated here, where the real error can be shown,
|
|
@@ -496,13 +589,7 @@ export const initPlayground = (root: HTMLElement): void => {
|
|
|
496
589
|
const start = performance.now();
|
|
497
590
|
try {
|
|
498
591
|
const res = await fetch(url, {
|
|
499
|
-
|
|
500
|
-
// which the catch below would mislabel as CORS. A spec that declares
|
|
501
|
-
// a GET requestBody keeps its samples, but the live send drops it.
|
|
502
|
-
body:
|
|
503
|
-
sample.method === "GET" || sample.method === "HEAD"
|
|
504
|
-
? undefined
|
|
505
|
-
: sample.body,
|
|
592
|
+
body: sendBody(sample),
|
|
506
593
|
headers,
|
|
507
594
|
method: sample.method,
|
|
508
595
|
// A `TimeoutError` DOMException is not a TypeError, so it reads as a
|
|
@@ -548,9 +635,9 @@ export const initPlayground = (root: HTMLElement): void => {
|
|
|
548
635
|
}
|
|
549
636
|
if (remember && target === remember) {
|
|
550
637
|
if (remember.checked) {
|
|
551
|
-
|
|
638
|
+
writeStored(storageKey, JSON.stringify(collectAuth()));
|
|
552
639
|
} else {
|
|
553
|
-
|
|
640
|
+
removeStored(storageKey);
|
|
554
641
|
}
|
|
555
642
|
} else if (
|
|
556
643
|
remember?.checked &&
|
|
@@ -558,7 +645,7 @@ export const initPlayground = (root: HTMLElement): void => {
|
|
|
558
645
|
target.dataset.authUsername !== undefined ||
|
|
559
646
|
target.dataset.authPassword !== undefined)
|
|
560
647
|
) {
|
|
561
|
-
|
|
648
|
+
writeStored(storageKey, JSON.stringify(collectAuth()));
|
|
562
649
|
}
|
|
563
650
|
syncSamples();
|
|
564
651
|
};
|