blume 2.0.2 → 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 +65 -0
- package/dist/cli/{chunk-6k4ftwze.js → chunk-1d7ve1dm.js} +1 -1
- package/dist/cli/{chunk-zp79m0ts.js → chunk-2eytanqx.js} +2 -2
- package/dist/cli/{chunk-8g8ytmgx.js → chunk-2hsdwb9n.js} +19 -19
- package/dist/cli/{chunk-8g8ytmgx.js.map → chunk-2hsdwb9n.js.map} +1 -1
- package/dist/cli/{chunk-g698a744.js → chunk-2z928egk.js} +5 -5
- package/dist/cli/{chunk-ppzjqwx2.js → chunk-35d4wj9f.js} +17 -18
- package/dist/cli/{chunk-ppzjqwx2.js.map → chunk-35d4wj9f.js.map} +3 -3
- package/dist/cli/{chunk-mqc662a6.js → chunk-364znk6q.js} +2 -2
- package/dist/cli/{chunk-gs7r695n.js → chunk-3em5wd2y.js} +21 -8
- package/dist/cli/{chunk-gs7r695n.js.map → chunk-3em5wd2y.js.map} +3 -3
- package/dist/cli/{chunk-k7pj68a8.js → chunk-5f86nr5m.js} +15 -15
- package/dist/cli/{chunk-hr8ne106.js → chunk-5m5nmvyq.js} +68 -43
- package/dist/cli/chunk-5m5nmvyq.js.map +13 -0
- package/dist/cli/{chunk-91ws1n6j.js → chunk-5xvm6tfj.js} +14 -14
- package/dist/cli/{chunk-w4bxdvsa.js → chunk-6dsbexzp.js} +14 -14
- package/dist/cli/{chunk-00gs3wqs.js → chunk-8ktnccpt.js} +1 -1
- package/dist/cli/{chunk-j85scx15.js → chunk-9t7a85s3.js} +130 -44
- package/dist/cli/chunk-9t7a85s3.js.map +10 -0
- package/dist/cli/{chunk-bbnwccaz.js → chunk-a58773jm.js} +2 -2
- package/dist/cli/{chunk-nfcyttvj.js → chunk-acanzt5p.js} +9 -9
- package/dist/cli/{chunk-nfcyttvj.js.map → chunk-acanzt5p.js.map} +1 -1
- package/dist/cli/{chunk-wdrt2k2v.js → chunk-akbpwfxc.js} +90 -26
- package/dist/cli/chunk-akbpwfxc.js.map +10 -0
- package/dist/cli/{chunk-sqw4ekg1.js → chunk-b07cmahc.js} +2 -2
- package/dist/cli/{chunk-xh43dwgw.js → chunk-c8chx29p.js} +42 -29
- package/dist/cli/{chunk-xh43dwgw.js.map → chunk-c8chx29p.js.map} +9 -9
- package/dist/cli/{chunk-7mbqtmgb.js → chunk-crgn1q09.js} +21 -12
- 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-4e9b9ra6.js → chunk-ey84smr6.js} +3 -3
- package/dist/cli/{chunk-d1v5rhy0.js → chunk-g4hq16wv.js} +13 -13
- package/dist/cli/{chunk-273ygyr4.js → chunk-ga0pf4aj.js} +7 -7
- package/dist/cli/{chunk-273ygyr4.js.map → chunk-ga0pf4aj.js.map} +3 -3
- package/dist/cli/{chunk-pbg5a4s3.js → chunk-hm3vjy5s.js} +64 -45
- package/dist/cli/chunk-hm3vjy5s.js.map +19 -0
- package/dist/cli/{chunk-5r8g91qn.js → chunk-j85vccga.js} +306 -107
- package/dist/cli/chunk-j85vccga.js.map +36 -0
- package/dist/cli/{chunk-n1yg3tj3.js → chunk-p3v96n38.js} +6 -6
- package/dist/cli/{chunk-n1yg3tj3.js.map → chunk-p3v96n38.js.map} +3 -3
- package/dist/cli/{chunk-7vtckvaw.js → chunk-p73c0m7w.js} +14 -14
- package/dist/cli/{chunk-7vtckvaw.js.map → chunk-p73c0m7w.js.map} +1 -1
- package/dist/cli/{chunk-qkb5a8sa.js → chunk-pehfxfta.js} +3 -3
- package/dist/cli/{chunk-v6ya5kcb.js → chunk-pv29h0wf.js} +1223 -978
- package/dist/cli/{chunk-v6ya5kcb.js.map → chunk-pv29h0wf.js.map} +47 -46
- package/dist/cli/{chunk-6dtt0zfn.js → chunk-q58y5e6a.js} +10 -10
- package/dist/cli/{chunk-6dtt0zfn.js.map → chunk-q58y5e6a.js.map} +3 -3
- package/dist/cli/{chunk-fsmrqk8a.js → chunk-r20tn01b.js} +1 -1
- package/dist/cli/{chunk-bfwp9vp6.js → chunk-r9rcc4w7.js} +7 -7
- package/dist/cli/{chunk-h7k3nq3v.js → chunk-tkacnehg.js} +2 -2
- package/dist/cli/{chunk-h2ez8dzb.js → chunk-tzmab476.js} +4 -4
- package/dist/cli/{chunk-3yce002v.js → chunk-vg9r4eb9.js} +6 -6
- package/dist/cli/{chunk-3yce002v.js.map → chunk-vg9r4eb9.js.map} +4 -4
- package/dist/cli/{chunk-hqp2ajnh.js → chunk-wrr3j9w9.js} +16 -17
- package/dist/cli/{chunk-hqp2ajnh.js.map → chunk-wrr3j9w9.js.map} +7 -7
- package/dist/cli/{chunk-ddndchfr.js → chunk-yfyb25rh.js} +38 -26
- package/dist/cli/chunk-yfyb25rh.js.map +14 -0
- package/dist/cli/index.js +20 -18
- package/dist/cli/index.js.map +3 -3
- 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 +9 -7
- package/dist/types/ai/skills.d.ts +4 -1
- package/dist/types/ai/tar.d.ts +1 -3
- package/dist/types/analytics/index.d.ts +2 -0
- package/dist/types/analytics/one-dollar-stats.d.ts +48 -0
- package/dist/types/analytics/schema.d.ts +14 -0
- package/dist/types/astro/integration.d.ts +3 -2
- package/dist/types/core/base-path.d.ts +21 -7
- package/dist/types/core/config-input.d.ts +9 -7
- package/dist/types/core/directive-diagnostics.d.ts +12 -0
- package/dist/types/core/heading-markers.d.ts +5 -7
- package/dist/types/core/i18n.d.ts +2 -0
- package/dist/types/core/last-modified.d.ts +10 -0
- package/dist/types/core/meta.d.ts +8 -0
- package/dist/types/core/schema.d.ts +7 -0
- package/dist/types/core/sources/lower.d.ts +29 -16
- package/dist/types/core/sources/normalize.d.ts +13 -1
- package/dist/types/core/sources/watch.d.ts +12 -5
- package/dist/types/core/standard-schema.d.ts +5 -0
- package/dist/types/deploy/artifacts.d.ts +6 -4
- package/dist/types/deploy/headers.d.ts +5 -0
- package/dist/types/deploy/platforms/types.d.ts +8 -0
- package/dist/types/deploy/redirects.d.ts +24 -13
- 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/docs/02-deployment.mdx +4 -2
- package/docs/08-faq.mdx +1 -1
- package/docs/advanced/custom-pages.mdx +3 -3
- package/docs/cli/audit.mdx +17 -1
- package/docs/cli/evals.mdx +3 -3
- package/docs/cli/translate.mdx +3 -3
- package/docs/cli/version.mdx +1 -1
- package/docs/configuration/analytics.mdx +20 -1
- package/docs/configuration/customization.mdx +2 -0
- package/docs/configuration/search.mdx +1 -1
- package/docs/content/components.mdx +1 -1
- package/docs/content/frontmatter.mdx +1 -1
- package/docs/content/i18n.mdx +2 -0
- package/docs/content/index.mdx +4 -2
- package/docs/content/islands.mdx +1 -1
- package/docs/content/meta.mdx +3 -1
- package/docs/content/sources.mdx +1 -5
- package/docs/content/syntax.mdx +14 -0
- package/docs/content/versioning.mdx +1 -1
- package/docs/discoverability/agent-discovery.mdx +1 -1
- package/docs/discoverability/index.mdx +2 -1
- package/docs/discoverability/markdown.mdx +2 -2
- package/docs/discoverability/open-graph.mdx +5 -3
- package/docs/discoverability/rss.mdx +1 -1
- package/docs/references/asyncapi.mdx +1 -1
- package/docs/references/graphql.mdx +1 -1
- package/docs/references/openapi.mdx +5 -3
- package/package.json +1 -1
- package/skills/blume-migrate/references/fumadocs.md +1 -1
- package/skills/blume-migrate/references/nextra.md +1 -1
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +7 -2
- package/src/ai/agent-readability.ts +2 -2
- package/src/ai/ai-catalog.ts +6 -2
- package/src/ai/api/handlers.ts +2 -2
- package/src/ai/api/spec.ts +2 -2
- package/src/ai/api-catalog.ts +6 -2
- package/src/ai/changelog-markdown.ts +2 -2
- package/src/ai/link-headers.ts +14 -5
- package/src/ai/llms.ts +18 -7
- package/src/ai/mcp/discovery.ts +2 -2
- package/src/ai/mcp/query.ts +2 -2
- package/src/ai/mcp/server.ts +2 -2
- package/src/ai/openapi-components.ts +22 -6
- package/src/ai/relative-links.ts +42 -26
- package/src/ai/serializers.ts +2 -1
- package/src/ai/skills.ts +14 -1
- package/src/ai/tar.ts +139 -12
- package/src/analytics/head.ts +4 -0
- package/src/analytics/index.ts +5 -0
- package/src/analytics/one-dollar-stats.ts +86 -0
- package/src/analytics/schema.ts +2 -0
- package/src/astro/generate.ts +1 -1
- 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 +81 -37
- package/src/audit/checks/links.ts +8 -1
- package/src/audit/graph.ts +3 -1
- package/src/audit/run.ts +12 -9
- package/src/audit/snapshot.ts +5 -0
- package/src/cli/args.ts +32 -0
- package/src/cli/commands/audit.ts +4 -1
- package/src/cli/commands/doctor.ts +3 -1
- package/src/cli/commands/eval.ts +19 -11
- package/src/cli/commands/translate.ts +9 -12
- package/src/cli/commands/validate.ts +3 -1
- package/src/cli/dev-lock.ts +157 -37
- package/src/cli/report-format.ts +11 -6
- package/src/components/colors.ts +19 -0
- package/src/components/content/Badge.astro +5 -3
- package/src/components/content/Component.astro +2 -2
- package/src/components/content/Tab.astro +0 -1
- package/src/components/content/Tabs.astro +4 -0
- package/src/components/content/badge-color.ts +5 -2
- package/src/components/content/base-href.ts +13 -36
- package/src/components/islands/assistant.tsx +21 -4
- package/src/components/islands/base-path.ts +47 -10
- package/src/components/islands/hooks.ts +35 -19
- package/src/components/islands/webmcp.ts +4 -2
- package/src/components/layout/Breadcrumbs.astro +5 -2
- package/src/components/layout/DiscoveryLinks.astro +9 -5
- package/src/components/layout/Header.astro +2 -2
- package/src/components/layout/LanguageSwitcher.astro +2 -2
- package/src/components/layout/NavSelector.astro +2 -2
- package/src/components/layout/NavTabMenu.astro +3 -3
- package/src/components/layout/NavTree.astro +16 -6
- package/src/components/layout/PageActions.astro +4 -3
- package/src/components/layout/PageLayout.astro +7 -6
- package/src/components/layout/Pagination.astro +3 -3
- package/src/components/layout/RootLayout.astro +5 -5
- package/src/components/layout/Search.astro +33 -13
- package/src/components/layout/VersionBanner.astro +2 -2
- package/src/components/layout/analytics-client.ts +12 -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/Operation.astro +2 -0
- package/src/components/openapi/Playground.astro +4 -4
- package/src/components/openapi/RequestPanel.astro +5 -2
- package/src/components/openapi/SchemaTable.astro +7 -0
- package/src/components/openapi/async.ts +38 -6
- package/src/components/openapi/helpers.ts +19 -9
- package/src/components/openapi/message-composer.ts +6 -1
- package/src/components/openapi/message.ts +17 -2
- package/src/components/openapi/operation-model.ts +81 -11
- package/src/components/openapi/panel.ts +4 -2
- package/src/components/openapi/param-style.ts +181 -0
- package/src/components/openapi/playground-client.ts +73 -16
- package/src/components/openapi/request.ts +190 -26
- package/src/components/openapi/schema-tree.ts +30 -24
- package/src/components/openapi/snippets.ts +85 -6
- package/src/components/openapi/ws-client.ts +18 -2
- package/src/core/base-path.ts +62 -16
- package/src/core/config-input.ts +9 -7
- package/src/core/diagnostics.ts +2 -0
- package/src/core/directive-diagnostics.ts +99 -0
- package/src/core/frontmatter.ts +21 -18
- package/src/core/heading-markers.ts +5 -18
- package/src/core/i18n.ts +31 -3
- package/src/core/last-modified.ts +25 -3
- package/src/core/locale-links.ts +5 -1
- package/src/core/meta.ts +33 -19
- package/src/core/navigation.ts +28 -8
- package/src/core/project-graph.ts +27 -5
- package/src/core/schema.ts +24 -4
- package/src/core/sources/contentful-rich-text.ts +27 -20
- package/src/core/sources/filesystem.ts +20 -2
- package/src/core/sources/github-releases.ts +25 -6
- package/src/core/sources/lexical.ts +23 -18
- package/src/core/sources/lower.ts +201 -34
- package/src/core/sources/mdx-remote.ts +51 -16
- package/src/core/sources/normalize.ts +325 -90
- package/src/core/sources/notion.ts +52 -25
- package/src/core/sources/obsidian.ts +23 -6
- package/src/core/sources/portable-text.ts +38 -21
- package/src/core/sources/strapi-blocks.ts +20 -16
- package/src/core/sources/watch.ts +20 -7
- package/src/core/standard-schema.ts +10 -6
- package/src/core/version-cut.ts +52 -15
- package/src/deploy/artifacts.ts +27 -6
- package/src/deploy/cloudflare-negotiation.ts +4 -12
- package/src/deploy/headers.ts +8 -4
- package/src/deploy/node-headers.ts +1 -1
- package/src/deploy/platforms/cloudflare.ts +12 -3
- package/src/deploy/platforms/netlify.ts +1 -0
- package/src/deploy/platforms/node.ts +1 -0
- package/src/deploy/platforms/static.ts +1 -0
- package/src/deploy/platforms/types.ts +8 -0
- package/src/deploy/platforms/vercel.ts +1 -0
- package/src/deploy/redirects.ts +54 -18
- package/src/deploy/robots.ts +2 -2
- package/src/deploy/rss.ts +4 -3
- package/src/deploy/sitemap.ts +7 -5
- package/src/markdown/base-links.ts +34 -36
- package/src/markdown/directives.ts +242 -36
- package/src/markdown/features.ts +17 -0
- package/src/markdown/index.ts +7 -6
- package/src/markdown/mdast.ts +5 -2
- package/src/markdown/relative-links.ts +12 -4
- package/src/og/card.ts +129 -5
- package/src/og/derive.ts +41 -31
- package/src/og/index.ts +1 -0
- package/src/openapi/asyncapi.ts +4 -2
- package/src/openapi/model.ts +22 -14
- package/src/openapi/proxy.ts +63 -10
- package/src/openapi/render-mdx.ts +41 -2
- package/src/registry/eject.ts +181 -24
- package/src/search/adapters/version-scope.ts +30 -0
- package/src/search/documents.ts +67 -26
- package/src/search/popular.ts +2 -1
- package/src/seo/jsonld.ts +7 -3
- package/src/translate/meta.ts +68 -24
- package/src/translate/run.ts +3 -3
- package/src/translate/validate.ts +4 -1
- package/src/translate/work-list.ts +56 -17
- package/dist/cli/chunk-5r8g91qn.js.map +0 -34
- package/dist/cli/chunk-7mbqtmgb.js.map +0 -10
- package/dist/cli/chunk-ddndchfr.js.map +0 -14
- package/dist/cli/chunk-esh98wmb.js +0 -23
- package/dist/cli/chunk-esh98wmb.js.map +0 -10
- package/dist/cli/chunk-hr8ne106.js.map +0 -13
- package/dist/cli/chunk-j85scx15.js.map +0 -10
- package/dist/cli/chunk-pbg5a4s3.js.map +0 -19
- package/dist/cli/chunk-wdrt2k2v.js.map +0 -10
- /package/dist/cli/{chunk-6k4ftwze.js.map → chunk-1d7ve1dm.js.map} +0 -0
- /package/dist/cli/{chunk-zp79m0ts.js.map → chunk-2eytanqx.js.map} +0 -0
- /package/dist/cli/{chunk-g698a744.js.map → chunk-2z928egk.js.map} +0 -0
- /package/dist/cli/{chunk-mqc662a6.js.map → chunk-364znk6q.js.map} +0 -0
- /package/dist/cli/{chunk-k7pj68a8.js.map → chunk-5f86nr5m.js.map} +0 -0
- /package/dist/cli/{chunk-91ws1n6j.js.map → chunk-5xvm6tfj.js.map} +0 -0
- /package/dist/cli/{chunk-w4bxdvsa.js.map → chunk-6dsbexzp.js.map} +0 -0
- /package/dist/cli/{chunk-00gs3wqs.js.map → chunk-8ktnccpt.js.map} +0 -0
- /package/dist/cli/{chunk-bbnwccaz.js.map → chunk-a58773jm.js.map} +0 -0
- /package/dist/cli/{chunk-sqw4ekg1.js.map → chunk-b07cmahc.js.map} +0 -0
- /package/dist/cli/{chunk-4e9b9ra6.js.map → chunk-ey84smr6.js.map} +0 -0
- /package/dist/cli/{chunk-d1v5rhy0.js.map → chunk-g4hq16wv.js.map} +0 -0
- /package/dist/cli/{chunk-qkb5a8sa.js.map → chunk-pehfxfta.js.map} +0 -0
- /package/dist/cli/{chunk-fsmrqk8a.js.map → chunk-r20tn01b.js.map} +0 -0
- /package/dist/cli/{chunk-bfwp9vp6.js.map → chunk-r9rcc4w7.js.map} +0 -0
- /package/dist/cli/{chunk-h7k3nq3v.js.map → chunk-tkacnehg.js.map} +0 -0
- /package/dist/cli/{chunk-h2ez8dzb.js.map → chunk-tzmab476.js.map} +0 -0
|
@@ -6,14 +6,21 @@ import {
|
|
|
6
6
|
resolveSchema,
|
|
7
7
|
toJson,
|
|
8
8
|
} from "./helpers.ts";
|
|
9
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
ComponentsLike,
|
|
11
|
+
ParameterLike,
|
|
12
|
+
SchemaLike,
|
|
13
|
+
SpecValue,
|
|
14
|
+
} from "./helpers.ts";
|
|
10
15
|
import {
|
|
11
16
|
declaredTypes,
|
|
12
17
|
inputValue,
|
|
13
18
|
scalarType,
|
|
14
19
|
validationSchema,
|
|
15
20
|
} from "./playground-schema.ts";
|
|
21
|
+
import { bodyEncoding } from "./request.ts";
|
|
16
22
|
import type {
|
|
23
|
+
ParamSerialization,
|
|
17
24
|
PlaygroundAuthInput,
|
|
18
25
|
PlaygroundBody,
|
|
19
26
|
PlaygroundBodyField,
|
|
@@ -34,6 +41,7 @@ interface MediaTypeLike {
|
|
|
34
41
|
schema?: SchemaLike;
|
|
35
42
|
example?: SpecValue;
|
|
36
43
|
examples?: SpecValue;
|
|
44
|
+
encoding?: SpecValue;
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
/** A server entry: its URL template and the variables that fill it. */
|
|
@@ -70,6 +78,45 @@ const isExampleObject = (
|
|
|
70
78
|
): value is Record<string, SpecValue> =>
|
|
71
79
|
typeof value === "object" && value !== null && !Array.isArray(value);
|
|
72
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
|
+
|
|
73
120
|
/**
|
|
74
121
|
* Playground inputs for the operation's parameters. Cookie params are skipped
|
|
75
122
|
* (not supported in v1 — browsers won't let a page set arbitrary cookies).
|
|
@@ -80,7 +127,8 @@ const isExampleObject = (
|
|
|
80
127
|
*/
|
|
81
128
|
const modelParams = (
|
|
82
129
|
parameters: ParameterLike[],
|
|
83
|
-
schemas: Record<string, SchemaLike
|
|
130
|
+
schemas: Record<string, SchemaLike>,
|
|
131
|
+
components: ComponentsLike | undefined
|
|
84
132
|
): PlaygroundParam[] => {
|
|
85
133
|
const params: PlaygroundParam[] = [];
|
|
86
134
|
for (const param of parameters) {
|
|
@@ -102,9 +150,11 @@ const modelParams = (
|
|
|
102
150
|
type: scalarType(param.schema, schemas),
|
|
103
151
|
value: required
|
|
104
152
|
? inputValue(
|
|
105
|
-
declaredExample(param) ??
|
|
153
|
+
declaredExample(param, components) ??
|
|
154
|
+
exampleValue(param.schema, schemas)
|
|
106
155
|
)
|
|
107
156
|
: "",
|
|
157
|
+
...serialization(param),
|
|
108
158
|
});
|
|
109
159
|
}
|
|
110
160
|
return params;
|
|
@@ -165,10 +215,16 @@ const bodyFields = (
|
|
|
165
215
|
return fields;
|
|
166
216
|
};
|
|
167
217
|
|
|
168
|
-
/**
|
|
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
|
+
*/
|
|
169
224
|
const modelBody = (
|
|
170
225
|
requestBody: { content?: Record<string, MediaTypeLike> } | undefined,
|
|
171
|
-
schemas: Record<string, SchemaLike
|
|
226
|
+
schemas: Record<string, SchemaLike>,
|
|
227
|
+
components: ComponentsLike | undefined
|
|
172
228
|
): PlaygroundBody | undefined => {
|
|
173
229
|
const media = jsonContentType(requestBody?.content);
|
|
174
230
|
if (!media) {
|
|
@@ -176,13 +232,25 @@ const modelBody = (
|
|
|
176
232
|
}
|
|
177
233
|
const [contentType, mediaType] = media;
|
|
178
234
|
const exampleData =
|
|
179
|
-
declaredExample(mediaType) ??
|
|
180
|
-
|
|
235
|
+
declaredExample(mediaType, components) ??
|
|
236
|
+
exampleValue(mediaType.schema, schemas);
|
|
237
|
+
const encoding = bodyEncoding(contentType);
|
|
238
|
+
const raw = encoding === "raw";
|
|
239
|
+
const body: PlaygroundBody = {
|
|
181
240
|
contentType,
|
|
182
|
-
example:
|
|
183
|
-
|
|
241
|
+
example:
|
|
242
|
+
raw && isString(exampleData) ? exampleData : (toJson(exampleData) ?? ""),
|
|
243
|
+
fields: raw
|
|
244
|
+
? undefined
|
|
245
|
+
: bodyFields(mediaType.schema, schemas, exampleData),
|
|
184
246
|
schema: validationSchema(mediaType.schema, schemas),
|
|
185
247
|
};
|
|
248
|
+
const fieldEncoding =
|
|
249
|
+
encoding === "form" ? formEncoding(mediaType.encoding) : undefined;
|
|
250
|
+
if (fieldEncoding) {
|
|
251
|
+
body.encoding = fieldEncoding;
|
|
252
|
+
}
|
|
253
|
+
return body;
|
|
186
254
|
};
|
|
187
255
|
|
|
188
256
|
const AUTHORIZATION_HEADER = { in: "header", name: "Authorization" } as const;
|
|
@@ -272,6 +340,8 @@ export const operationModel = (args: {
|
|
|
272
340
|
/** The operation's effective servers (`effectiveServers` output). */
|
|
273
341
|
servers: ServerLike[];
|
|
274
342
|
schemas: Record<string, SchemaLike>;
|
|
343
|
+
/** The document's `components`, which `$ref`'d examples resolve against. */
|
|
344
|
+
components?: ComponentsLike;
|
|
275
345
|
security: OperationSecurity;
|
|
276
346
|
}): PlaygroundModel => ({
|
|
277
347
|
// First alternative only — the spec's preferred way to authorize, matching
|
|
@@ -281,9 +351,9 @@ export const operationModel = (args: {
|
|
|
281
351
|
return input ? [input] : [];
|
|
282
352
|
}),
|
|
283
353
|
authOptional: args.security.optional,
|
|
284
|
-
body: modelBody(args.requestBody, args.schemas),
|
|
354
|
+
body: modelBody(args.requestBody, args.schemas, args.components),
|
|
285
355
|
method: args.method.toUpperCase(),
|
|
286
|
-
params: modelParams(args.parameters, args.schemas),
|
|
356
|
+
params: modelParams(args.parameters, args.schemas, args.components),
|
|
287
357
|
path: args.path,
|
|
288
358
|
// Variables resolve to their defaults: the samples and Send need a real URL.
|
|
289
359
|
servers: args.servers.map((server) =>
|
|
@@ -40,10 +40,12 @@ class BlumePanelTabs extends HTMLElement {
|
|
|
40
40
|
// wrap, Home and End jump to the ends, and focus follows selection.
|
|
41
41
|
tab.addEventListener("keydown", (event) => {
|
|
42
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";
|
|
43
45
|
let next: number | undefined;
|
|
44
|
-
if (event.key === "ArrowRight") {
|
|
46
|
+
if (event.key === (rtl ? "ArrowLeft" : "ArrowRight")) {
|
|
45
47
|
next = index === last ? 0 : index + 1;
|
|
46
|
-
} else if (event.key === "ArrowLeft") {
|
|
48
|
+
} else if (event.key === (rtl ? "ArrowRight" : "ArrowLeft")) {
|
|
47
49
|
next = index === 0 ? last : index - 1;
|
|
48
50
|
} else if (event.key === "Home") {
|
|
49
51
|
next = 0;
|
|
@@ -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
|
|
@@ -185,6 +201,50 @@ const removeStored = (key: string): void => {
|
|
|
185
201
|
}
|
|
186
202
|
};
|
|
187
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
|
+
|
|
188
248
|
/** A one-line text element for the response/error regions. */
|
|
189
249
|
const line = (className: string, text: string): HTMLElement => {
|
|
190
250
|
const el = document.createElement("div");
|
|
@@ -332,9 +392,11 @@ export const initPlayground = (root: HTMLElement): void => {
|
|
|
332
392
|
}
|
|
333
393
|
// An emptied editor means "no body" (see `bodyFor`), not invalid JSON —
|
|
334
394
|
// reporting a syntax error there would block a send the request builder is
|
|
335
|
-
// 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";
|
|
336
398
|
const errors =
|
|
337
|
-
bodyArea.value.trim() === ""
|
|
399
|
+
raw || bodyArea.value.trim() === ""
|
|
338
400
|
? []
|
|
339
401
|
: validateJson(bodyArea.value, model.body?.schema);
|
|
340
402
|
bodyErrors.textContent = "";
|
|
@@ -453,6 +515,11 @@ export const initPlayground = (root: HTMLElement): void => {
|
|
|
453
515
|
if (!response || sending) {
|
|
454
516
|
return;
|
|
455
517
|
}
|
|
518
|
+
if (fetchRefusesMethod(model.method)) {
|
|
519
|
+
response.textContent = "";
|
|
520
|
+
response.append(line(ERROR_TEXT, TRACE_MESSAGE));
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
456
523
|
if (validateBody().length > 0) {
|
|
457
524
|
return;
|
|
458
525
|
}
|
|
@@ -494,11 +561,7 @@ export const initPlayground = (root: HTMLElement): void => {
|
|
|
494
561
|
sample.url
|
|
495
562
|
)}`
|
|
496
563
|
: sample.url;
|
|
497
|
-
|
|
498
|
-
// the browser would silently drop the forbidden name anyway, so strip it
|
|
499
|
-
// rather than earn a console warning.
|
|
500
|
-
const headers = { ...sample.headers };
|
|
501
|
-
delete headers.Cookie;
|
|
564
|
+
const headers = sendHeaders(sample, proxy);
|
|
502
565
|
// fetch surfaces an invalid URL or header value as the same TypeError a
|
|
503
566
|
// CORS rejection produces, and the catch below would misdiagnose it as the
|
|
504
567
|
// CORS wall. Both are validated here, where the real error can be shown,
|
|
@@ -526,13 +589,7 @@ export const initPlayground = (root: HTMLElement): void => {
|
|
|
526
589
|
const start = performance.now();
|
|
527
590
|
try {
|
|
528
591
|
const res = await fetch(url, {
|
|
529
|
-
|
|
530
|
-
// which the catch below would mislabel as CORS. A spec that declares
|
|
531
|
-
// a GET requestBody keeps its samples, but the live send drops it.
|
|
532
|
-
body:
|
|
533
|
-
sample.method === "GET" || sample.method === "HEAD"
|
|
534
|
-
? undefined
|
|
535
|
-
: sample.body,
|
|
592
|
+
body: sendBody(sample),
|
|
536
593
|
headers,
|
|
537
594
|
method: sample.method,
|
|
538
595
|
// A `TimeoutError` DOMException is not a TypeError, so it reads as a
|