vocs 2.0.17 → 2.1.2
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/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/globals.d.ts +16 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/config.d.ts +28 -0
- package/dist/internal/config.d.ts.map +1 -1
- package/dist/internal/config.js +10 -2
- package/dist/internal/config.js.map +1 -1
- package/dist/internal/llms.d.ts +21 -2
- package/dist/internal/llms.d.ts.map +1 -1
- package/dist/internal/llms.js +41 -1
- package/dist/internal/llms.js.map +1 -1
- package/dist/internal/markdown-negotiation.d.ts +36 -0
- package/dist/internal/markdown-negotiation.d.ts.map +1 -0
- package/dist/internal/markdown-negotiation.js +81 -0
- package/dist/internal/markdown-negotiation.js.map +1 -0
- package/dist/internal/markdown.d.ts.map +1 -1
- package/dist/internal/markdown.js +5 -0
- package/dist/internal/markdown.js.map +1 -1
- package/dist/internal/openapi/anchors.d.ts +25 -0
- package/dist/internal/openapi/anchors.d.ts.map +1 -0
- package/dist/internal/openapi/anchors.js +37 -0
- package/dist/internal/openapi/anchors.js.map +1 -0
- package/dist/internal/openapi/app.d.ts +89 -0
- package/dist/internal/openapi/app.d.ts.map +1 -0
- package/dist/internal/openapi/app.js +62 -0
- package/dist/internal/openapi/app.js.map +1 -0
- package/dist/internal/openapi/index.d.ts +8 -0
- package/dist/internal/openapi/index.d.ts.map +1 -0
- package/dist/internal/openapi/index.js +5 -0
- package/dist/internal/openapi/index.js.map +1 -0
- package/dist/internal/openapi/markdown.d.ts +42 -0
- package/dist/internal/openapi/markdown.d.ts.map +1 -0
- package/dist/internal/openapi/markdown.js +235 -0
- package/dist/internal/openapi/markdown.js.map +1 -0
- package/dist/internal/openapi/openapi.d.ts +187 -0
- package/dist/internal/openapi/openapi.d.ts.map +1 -0
- package/dist/internal/openapi/openapi.js +44 -0
- package/dist/internal/openapi/openapi.js.map +1 -0
- package/dist/internal/openapi/openrpc.d.ts +90 -0
- package/dist/internal/openapi/openrpc.d.ts.map +1 -0
- package/dist/internal/openapi/openrpc.js +213 -0
- package/dist/internal/openapi/openrpc.js.map +1 -0
- package/dist/internal/openapi/parser.d.ts +181 -0
- package/dist/internal/openapi/parser.d.ts.map +1 -0
- package/dist/internal/openapi/parser.js +329 -0
- package/dist/internal/openapi/parser.js.map +1 -0
- package/dist/internal/openapi/registry.d.ts +36 -0
- package/dist/internal/openapi/registry.d.ts.map +1 -0
- package/dist/internal/openapi/registry.js +79 -0
- package/dist/internal/openapi/registry.js.map +1 -0
- package/dist/internal/openapi/sample.d.ts +115 -0
- package/dist/internal/openapi/sample.d.ts.map +1 -0
- package/dist/internal/openapi/sample.js +434 -0
- package/dist/internal/openapi/sample.js.map +1 -0
- package/dist/internal/openapi/search.d.ts +19 -0
- package/dist/internal/openapi/search.d.ts.map +1 -0
- package/dist/internal/openapi/search.js +98 -0
- package/dist/internal/openapi/search.js.map +1 -0
- package/dist/internal/openapi/sidebar.d.ts +30 -0
- package/dist/internal/openapi/sidebar.d.ts.map +1 -0
- package/dist/internal/openapi/sidebar.js +67 -0
- package/dist/internal/openapi/sidebar.js.map +1 -0
- package/dist/internal/openapi/union.d.ts +36 -0
- package/dist/internal/openapi/union.d.ts.map +1 -0
- package/dist/internal/openapi/union.js +69 -0
- package/dist/internal/openapi/union.js.map +1 -0
- package/dist/internal/search.d.ts.map +1 -1
- package/dist/internal/search.js +20 -0
- package/dist/internal/search.js.map +1 -1
- package/dist/internal/vite-plugins.d.ts +12 -0
- package/dist/internal/vite-plugins.d.ts.map +1 -1
- package/dist/internal/vite-plugins.js +102 -11
- package/dist/internal/vite-plugins.js.map +1 -1
- package/dist/react/Badge.d.ts +2 -3
- package/dist/react/Badge.d.ts.map +1 -1
- package/dist/react/Layout.client.d.ts.map +1 -1
- package/dist/react/Layout.client.js +2 -2
- package/dist/react/Layout.client.js.map +1 -1
- package/dist/react/OpenApi.d.ts +6 -0
- package/dist/react/OpenApi.d.ts.map +1 -0
- package/dist/react/OpenApi.js +6 -0
- package/dist/react/OpenApi.js.map +1 -0
- package/dist/react/internal/CodeToHtml.client.d.ts +53 -2
- package/dist/react/internal/CodeToHtml.client.d.ts.map +1 -1
- package/dist/react/internal/CodeToHtml.client.js +154 -21
- package/dist/react/internal/CodeToHtml.client.js.map +1 -1
- package/dist/react/internal/Sidebar.d.ts.map +1 -1
- package/dist/react/internal/Sidebar.js +99 -2
- package/dist/react/internal/Sidebar.js.map +1 -1
- package/dist/react/internal/openapi/CodeSample.client.d.ts +21 -0
- package/dist/react/internal/openapi/CodeSample.client.d.ts.map +1 -0
- package/dist/react/internal/openapi/CodeSample.client.js +134 -0
- package/dist/react/internal/openapi/CodeSample.client.js.map +1 -0
- package/dist/react/internal/openapi/CollapsibleChildren.client.d.ts +17 -0
- package/dist/react/internal/openapi/CollapsibleChildren.client.d.ts.map +1 -0
- package/dist/react/internal/openapi/CollapsibleChildren.client.js +18 -0
- package/dist/react/internal/openapi/CollapsibleChildren.client.js.map +1 -0
- package/dist/react/internal/openapi/Disclosure.client.d.ts +28 -0
- package/dist/react/internal/openapi/Disclosure.client.d.ts.map +1 -0
- package/dist/react/internal/openapi/Disclosure.client.js +38 -0
- package/dist/react/internal/openapi/Disclosure.client.js.map +1 -0
- package/dist/react/internal/openapi/Endpoints.d.ts +26 -0
- package/dist/react/internal/openapi/Endpoints.d.ts.map +1 -0
- package/dist/react/internal/openapi/Endpoints.js +33 -0
- package/dist/react/internal/openapi/Endpoints.js.map +1 -0
- package/dist/react/internal/openapi/EndpointsView.d.ts +24 -0
- package/dist/react/internal/openapi/EndpointsView.d.ts.map +1 -0
- package/dist/react/internal/openapi/EndpointsView.js +26 -0
- package/dist/react/internal/openapi/EndpointsView.js.map +1 -0
- package/dist/react/internal/openapi/EnumValues.client.d.ts +14 -0
- package/dist/react/internal/openapi/EnumValues.client.d.ts.map +1 -0
- package/dist/react/internal/openapi/EnumValues.client.js +20 -0
- package/dist/react/internal/openapi/EnumValues.client.js.map +1 -0
- package/dist/react/internal/openapi/HeadingAnchor.d.ts +15 -0
- package/dist/react/internal/openapi/HeadingAnchor.d.ts.map +1 -0
- package/dist/react/internal/openapi/HeadingAnchor.js +12 -0
- package/dist/react/internal/openapi/HeadingAnchor.js.map +1 -0
- package/dist/react/internal/openapi/OpenApiPage.d.ts +79 -0
- package/dist/react/internal/openapi/OpenApiPage.d.ts.map +1 -0
- package/dist/react/internal/openapi/OpenApiPage.js +72 -0
- package/dist/react/internal/openapi/OpenApiPage.js.map +1 -0
- package/dist/react/internal/openapi/Operation.d.ts +25 -0
- package/dist/react/internal/openapi/Operation.d.ts.map +1 -0
- package/dist/react/internal/openapi/Operation.js +101 -0
- package/dist/react/internal/openapi/Operation.js.map +1 -0
- package/dist/react/internal/openapi/Playground.client.d.ts +33 -0
- package/dist/react/internal/openapi/Playground.client.d.ts.map +1 -0
- package/dist/react/internal/openapi/Playground.client.js +170 -0
- package/dist/react/internal/openapi/Playground.client.js.map +1 -0
- package/dist/react/internal/openapi/PropertyExample.client.d.ts +17 -0
- package/dist/react/internal/openapi/PropertyExample.client.d.ts.map +1 -0
- package/dist/react/internal/openapi/PropertyExample.client.js +21 -0
- package/dist/react/internal/openapi/PropertyExample.client.js.map +1 -0
- package/dist/react/internal/openapi/Reference.d.ts +55 -0
- package/dist/react/internal/openapi/Reference.d.ts.map +1 -0
- package/dist/react/internal/openapi/Reference.js +42 -0
- package/dist/react/internal/openapi/Reference.js.map +1 -0
- package/dist/react/internal/openapi/Schema.d.ts +110 -0
- package/dist/react/internal/openapi/Schema.d.ts.map +1 -0
- package/dist/react/internal/openapi/Schema.js +239 -0
- package/dist/react/internal/openapi/Schema.js.map +1 -0
- package/dist/react/internal/openapi/SchemaUnion.client.d.ts +25 -0
- package/dist/react/internal/openapi/SchemaUnion.client.d.ts.map +1 -0
- package/dist/react/internal/openapi/SchemaUnion.client.js +48 -0
- package/dist/react/internal/openapi/SchemaUnion.client.js.map +1 -0
- package/dist/react/internal/openapi/anchor-navigation.client.d.ts +47 -0
- package/dist/react/internal/openapi/anchor-navigation.client.d.ts.map +1 -0
- package/dist/react/internal/openapi/anchor-navigation.client.js +120 -0
- package/dist/react/internal/openapi/anchor-navigation.client.js.map +1 -0
- package/dist/react/internal/openapi/auth.d.ts +28 -0
- package/dist/react/internal/openapi/auth.d.ts.map +1 -0
- package/dist/react/internal/openapi/auth.js +75 -0
- package/dist/react/internal/openapi/auth.js.map +1 -0
- package/dist/react/useLayout.d.ts +2 -0
- package/dist/react/useLayout.d.ts.map +1 -1
- package/dist/react/useLayout.js +2 -0
- package/dist/react/useLayout.js.map +1 -1
- package/dist/server/handlers.d.ts +1 -1
- package/dist/server/handlers.d.ts.map +1 -1
- package/dist/server/handlers.js +26 -5
- package/dist/server/handlers.js.map +1 -1
- package/dist/server/og-assets.d.ts +5 -0
- package/dist/server/og-assets.d.ts.map +1 -0
- package/dist/server/og-assets.js +11 -0
- package/dist/server/og-assets.js.map +1 -0
- package/dist/server/openapi/assets.d.ts +33 -0
- package/dist/server/openapi/assets.d.ts.map +1 -0
- package/dist/server/openapi/assets.generated.d.ts +9 -0
- package/dist/server/openapi/assets.generated.d.ts.map +1 -0
- package/dist/server/openapi/assets.generated.js +1091 -0
- package/dist/server/openapi/assets.generated.js.map +1 -0
- package/dist/server/openapi/assets.js +32 -0
- package/dist/server/openapi/assets.js.map +1 -0
- package/dist/server/openapi/handler.d.ts +103 -0
- package/dist/server/openapi/handler.d.ts.map +1 -0
- package/dist/server/openapi/handler.js +198 -0
- package/dist/server/openapi/handler.js.map +1 -0
- package/dist/server/openapi/handler.test.d.ts +2 -0
- package/dist/server/openapi/handler.test.d.ts.map +1 -0
- package/dist/server/openapi/handler.test.js +203 -0
- package/dist/server/openapi/handler.test.js.map +1 -0
- package/dist/server/openapi/html.d.ts +16 -0
- package/dist/server/openapi/html.d.ts.map +1 -0
- package/dist/server/openapi/html.js +75 -0
- package/dist/server/openapi/html.js.map +1 -0
- package/dist/server/openapi/pages.d.ts +33 -0
- package/dist/server/openapi/pages.d.ts.map +1 -0
- package/dist/server/openapi/pages.js +130 -0
- package/dist/server/openapi/pages.js.map +1 -0
- package/dist/server/openapi/pages.test.d.ts +2 -0
- package/dist/server/openapi/pages.test.d.ts.map +1 -0
- package/dist/server/openapi/pages.test.js +94 -0
- package/dist/server/openapi/pages.test.js.map +1 -0
- package/dist/server/openapi/state.d.ts +42 -0
- package/dist/server/openapi/state.d.ts.map +1 -0
- package/dist/server/openapi/state.js +101 -0
- package/dist/server/openapi/state.js.map +1 -0
- package/dist/styles/index.css +16 -0
- package/dist/styles/markdown.css +9 -7
- package/dist/styles/openapi-playground.css +80 -0
- package/dist/styles/openapi.css +660 -0
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +1 -0
- package/dist/vite.js.map +1 -1
- package/dist/waku/internal/middleware/md-router.d.ts +0 -4
- package/dist/waku/internal/middleware/md-router.d.ts.map +1 -1
- package/dist/waku/internal/middleware/md-router.js +3 -48
- package/dist/waku/internal/middleware/md-router.js.map +1 -1
- package/dist/waku/internal/patches/adapters/vercel-build-enhancer.js +1 -1
- package/dist/waku/internal/patches/adapters/vercel-build-enhancer.js.map +1 -1
- package/dist/waku/internal/patches/router.d.ts.map +1 -1
- package/dist/waku/internal/patches/router.js +114 -1
- package/dist/waku/internal/patches/router.js.map +1 -1
- package/package.json +5 -1
- package/src/config.ts +1 -0
- package/src/globals.d.ts +16 -0
- package/src/index.ts +1 -0
- package/src/internal/config.ts +40 -1
- package/src/internal/llms.ts +51 -1
- package/src/internal/markdown-negotiation.test.ts +42 -0
- package/src/internal/markdown-negotiation.ts +95 -0
- package/src/internal/markdown.ts +5 -0
- package/src/internal/openapi/anchors.ts +44 -0
- package/src/internal/openapi/app.ts +127 -0
- package/src/internal/openapi/index.ts +24 -0
- package/src/internal/openapi/markdown.test.ts +115 -0
- package/src/internal/openapi/markdown.ts +275 -0
- package/src/internal/openapi/openapi.ts +212 -0
- package/src/internal/openapi/openrpc.test.ts +239 -0
- package/src/internal/openapi/openrpc.ts +295 -0
- package/src/internal/openapi/parser.test.ts +203 -0
- package/src/internal/openapi/parser.ts +613 -0
- package/src/internal/openapi/registry.test.ts +89 -0
- package/src/internal/openapi/registry.ts +89 -0
- package/src/internal/openapi/sample.test.ts +283 -0
- package/src/internal/openapi/sample.ts +562 -0
- package/src/internal/openapi/search.test.ts +62 -0
- package/src/internal/openapi/search.ts +108 -0
- package/src/internal/openapi/sidebar.test.ts +131 -0
- package/src/internal/openapi/sidebar.ts +94 -0
- package/src/internal/openapi/union.test.ts +51 -0
- package/src/internal/openapi/union.ts +74 -0
- package/src/internal/search.ts +20 -0
- package/src/internal/test/virtual-config.stub.ts +14 -0
- package/src/internal/vite-plugins.ts +106 -11
- package/src/openapi-app/App.tsx +64 -0
- package/src/openapi-app/blocks.tsx +33 -0
- package/src/openapi-app/client.tsx +25 -0
- package/src/openapi-app/links.test.ts +84 -0
- package/src/openapi-app/links.ts +66 -0
- package/src/openapi-app/payload.ts +20 -0
- package/src/openapi-app/virtual/config.ts +7 -0
- package/src/openapi-app/virtual/group-icons.ts +2 -0
- package/src/openapi-app/virtual/langs.ts +6 -0
- package/src/openapi-app/virtual/openapi.ts +10 -0
- package/src/openapi-app/virtual/search-index.ts +21 -0
- package/src/openapi-app/virtual/slots.ts +4 -0
- package/src/openapi-app/virtual/user-styles.ts +2 -0
- package/src/openapi-app/waku.tsx +154 -0
- package/src/react/Badge.tsx +2 -3
- package/src/react/Layout.client.tsx +17 -4
- package/src/react/OpenApi.tsx +5 -0
- package/src/react/internal/CodeToHtml.client.tsx +283 -22
- package/src/react/internal/Sidebar.tsx +126 -22
- package/src/react/internal/openapi/CodeSample.client.tsx +294 -0
- package/src/react/internal/openapi/CollapsibleChildren.client.tsx +41 -0
- package/src/react/internal/openapi/Disclosure.client.tsx +67 -0
- package/src/react/internal/openapi/Endpoints.tsx +58 -0
- package/src/react/internal/openapi/EndpointsView.tsx +76 -0
- package/src/react/internal/openapi/EnumValues.client.tsx +49 -0
- package/src/react/internal/openapi/HeadingAnchor.tsx +28 -0
- package/src/react/internal/openapi/OpenApiPage.tsx +173 -0
- package/src/react/internal/openapi/Operation.test.tsx +101 -0
- package/src/react/internal/openapi/Operation.tsx +335 -0
- package/src/react/internal/openapi/Playground.client.tsx +234 -0
- package/src/react/internal/openapi/PropertyExample.client.tsx +55 -0
- package/src/react/internal/openapi/Reference.tsx +120 -0
- package/src/react/internal/openapi/Schema.tsx +467 -0
- package/src/react/internal/openapi/SchemaUnion.client.tsx +123 -0
- package/src/react/internal/openapi/anchor-navigation.client.ts +154 -0
- package/src/react/internal/openapi/auth.ts +69 -0
- package/src/react/useLayout.ts +4 -0
- package/src/server/handlers.ts +31 -6
- package/src/server/og-assets.ts +14 -0
- package/src/server/openapi/assets.generated.ts +1093 -0
- package/src/server/openapi/assets.ts +57 -0
- package/src/server/openapi/handler.test.ts +244 -0
- package/src/server/openapi/handler.ts +277 -0
- package/src/server/openapi/html.ts +84 -0
- package/src/server/openapi/pages.test.ts +111 -0
- package/src/server/openapi/pages.ts +153 -0
- package/src/server/openapi/state.ts +136 -0
- package/src/styles/index.css +16 -0
- package/src/styles/markdown.css +9 -7
- package/src/styles/openapi-playground.css +80 -0
- package/src/styles/openapi.css +660 -0
- package/src/vite.ts +1 -0
- package/src/waku/internal/middleware/md-router.ts +8 -52
- package/src/waku/internal/patches/adapters/vercel-build-enhancer.ts +1 -1
- package/src/waku/internal/patches/router.ts +131 -1
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { Config } from '../config.js'
|
|
2
|
+
import type { SidebarItem } from '../sidebar.js'
|
|
3
|
+
import { type Ir, parse } from './parser.js'
|
|
4
|
+
import * as Sidebar from './sidebar.js'
|
|
5
|
+
|
|
6
|
+
type Specs = Record<string, Ir>
|
|
7
|
+
|
|
8
|
+
let cache: Specs | null = null
|
|
9
|
+
let building: Promise<Specs> | null = null
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Parses all configured OpenAPI specs into IRs, memoized across plugins and
|
|
13
|
+
* Vite environments. Call {@link invalidate} to force a re-parse (e.g. when a
|
|
14
|
+
* local spec file changes).
|
|
15
|
+
*/
|
|
16
|
+
export async function build(config: Config): Promise<Specs> {
|
|
17
|
+
if (cache) return cache
|
|
18
|
+
if (building) return building
|
|
19
|
+
|
|
20
|
+
building = (async () => {
|
|
21
|
+
const next: Specs = {}
|
|
22
|
+
for (const entry of config.openapi ?? [])
|
|
23
|
+
next[entry.path] = await parse(entry, { rootDir: config.rootDir })
|
|
24
|
+
cache = next
|
|
25
|
+
building = null
|
|
26
|
+
return next
|
|
27
|
+
})()
|
|
28
|
+
|
|
29
|
+
return building
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Returns the cached specs, or `null` if not yet built. */
|
|
33
|
+
export function peek(): Specs | null {
|
|
34
|
+
return cache
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Clears the cache so the next {@link build} re-parses. */
|
|
38
|
+
export function invalidate(): void {
|
|
39
|
+
cache = null
|
|
40
|
+
building = null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Returns the OpenAPI-generated sidebars keyed by mount path, derived from the
|
|
45
|
+
* cached IRs. Empty if specs haven't been built yet or none are configured.
|
|
46
|
+
*/
|
|
47
|
+
export function sidebars(
|
|
48
|
+
config?: Config,
|
|
49
|
+
): Record<string, { backLink: boolean; items: SidebarItem<true>[] }> {
|
|
50
|
+
const result: Record<string, { backLink: boolean; items: SidebarItem<true>[] }> = {}
|
|
51
|
+
if (!cache) return result
|
|
52
|
+
for (const [path, ir] of Object.entries(cache)) {
|
|
53
|
+
const collapsed = config?.openapi?.find((entry) => entry.path === path)?.sidebar?.collapsed
|
|
54
|
+
result[path] = { backLink: true, items: Sidebar.toSidebar(ir, { collapsed }) }
|
|
55
|
+
}
|
|
56
|
+
return result
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Merges OpenAPI-generated sidebars into a user's `sidebar` config.
|
|
61
|
+
*
|
|
62
|
+
* Returns the original config unchanged when no OpenAPI sidebars exist.
|
|
63
|
+
* Array-form sidebars are converted to path-keyed form (under `/`) so the
|
|
64
|
+
* OpenAPI section can be scoped to its own mount path.
|
|
65
|
+
*
|
|
66
|
+
* Per-entry `sidebar.top` / `sidebar.bottom` items (links to consumer guide
|
|
67
|
+
* pages mounted under the same path) are prepended/appended around the
|
|
68
|
+
* generated items. Never mutates inputs.
|
|
69
|
+
*/
|
|
70
|
+
export function mergeSidebar(config: Config): Config {
|
|
71
|
+
const generated = sidebars(config)
|
|
72
|
+
if (Object.keys(generated).length === 0) return config
|
|
73
|
+
|
|
74
|
+
const userSidebar = config.sidebar
|
|
75
|
+
|
|
76
|
+
let merged: Record<string, unknown>
|
|
77
|
+
if (!userSidebar) merged = {}
|
|
78
|
+
else if (Array.isArray(userSidebar)) merged = { '/': userSidebar }
|
|
79
|
+
else merged = { ...userSidebar }
|
|
80
|
+
|
|
81
|
+
for (const [path, sidebar] of Object.entries(generated)) {
|
|
82
|
+
const entry = config.openapi?.find((candidate) => candidate.path === path)
|
|
83
|
+
const top = entry?.sidebar?.top ?? []
|
|
84
|
+
const bottom = entry?.sidebar?.bottom ?? []
|
|
85
|
+
merged[path] = { ...sidebar, items: [...top, ...sidebar.items, ...bottom] }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return { ...config, sidebar: merged as Config['sidebar'] }
|
|
89
|
+
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import type { IrOperation } from './parser.js'
|
|
3
|
+
import { codeSamples, harRequest, responseSamples } from './sample.js'
|
|
4
|
+
|
|
5
|
+
const operation: IrOperation = {
|
|
6
|
+
id: 'createpet',
|
|
7
|
+
method: 'POST',
|
|
8
|
+
path: '/pets/{petId}',
|
|
9
|
+
parameters: [
|
|
10
|
+
{ name: 'petId', in: 'path', required: true, schema: { type: 'string' } },
|
|
11
|
+
{ name: 'limit', in: 'query', required: true, schema: { type: 'integer' } },
|
|
12
|
+
{ name: 'dryRun', in: 'query', schema: { type: 'boolean' } },
|
|
13
|
+
],
|
|
14
|
+
requestBody: {
|
|
15
|
+
content: [
|
|
16
|
+
{
|
|
17
|
+
mediaType: 'application/json',
|
|
18
|
+
schema: {
|
|
19
|
+
type: 'object',
|
|
20
|
+
properties: { name: { type: 'string' }, age: { type: 'integer' } },
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
responses: [
|
|
26
|
+
{
|
|
27
|
+
status: '200',
|
|
28
|
+
description: 'A pet',
|
|
29
|
+
content: [
|
|
30
|
+
{
|
|
31
|
+
mediaType: 'application/json',
|
|
32
|
+
schema: { type: 'object', properties: { id: { type: 'string' } } },
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
headers: [],
|
|
36
|
+
},
|
|
37
|
+
{ status: '404', description: 'Not found', content: [], headers: [] },
|
|
38
|
+
],
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe('harRequest', () => {
|
|
42
|
+
test('fills path params, required query/body, and resolves the server', () => {
|
|
43
|
+
const request = harRequest(operation, 'https://api.example.com/')
|
|
44
|
+
expect(request.method).toBe('POST')
|
|
45
|
+
expect(request.url).toBe('https://api.example.com/pets/string')
|
|
46
|
+
// All non-deprecated query params are included (required and optional).
|
|
47
|
+
expect(request.queryString).toEqual([
|
|
48
|
+
{ name: 'limit', value: '0' },
|
|
49
|
+
{ name: 'dryRun', value: 'true' },
|
|
50
|
+
])
|
|
51
|
+
expect(request.postData?.mimeType).toBe('application/json')
|
|
52
|
+
expect(JSON.parse(request.postData?.text ?? '{}')).toEqual({ name: 'string', age: 0 })
|
|
53
|
+
expect(request.headers).toContainEqual({ name: 'Content-Type', value: 'application/json' })
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
describe('codeSamples', () => {
|
|
58
|
+
test('generates cURL and TypeScript fetch snippets', () => {
|
|
59
|
+
const samples = codeSamples(operation, 'https://api.example.com')
|
|
60
|
+
expect(samples.map((sample) => sample.id)).toEqual(['shell/curl', 'js/fetch'])
|
|
61
|
+
const curl = samples.find((sample) => sample.id === 'shell/curl')
|
|
62
|
+
expect(curl?.lang).toBe('bash')
|
|
63
|
+
expect(curl?.code).toContain('curl')
|
|
64
|
+
expect(curl?.code).toContain('https://api.example.com/pets/string')
|
|
65
|
+
const ts = samples.find((sample) => sample.id === 'js/fetch')
|
|
66
|
+
expect(ts?.label).toBe('TypeScript')
|
|
67
|
+
expect(ts?.lang).toBe('ts')
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('anchors query parameters to their schema rows per line', () => {
|
|
71
|
+
const samples = codeSamples(operation, 'https://api.example.com')
|
|
72
|
+
const curl = samples.find((sample) => sample.id === 'shell/curl')
|
|
73
|
+
// Each query parameter is on its own line, so the whole line is anchored.
|
|
74
|
+
const lineIds = (curl?.lineAnchors ?? []).filter(Boolean)
|
|
75
|
+
expect(lineIds).toContain('createpet-limit')
|
|
76
|
+
expect(lineIds).toContain('createpet-dryrun')
|
|
77
|
+
// The anchored line indices correspond to the `?limit`/`&dryRun` lines.
|
|
78
|
+
const lines = curl?.display.split('\n') ?? []
|
|
79
|
+
curl?.lineAnchors.forEach((id, index) => {
|
|
80
|
+
if (id) expect(lines[index]?.trim()).toMatch(/^'?[?&]/)
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test('color-codes query parameter names and values', () => {
|
|
85
|
+
const samples = codeSamples(operation, 'https://api.example.com')
|
|
86
|
+
const curl = samples.find((sample) => sample.id === 'shell/curl')
|
|
87
|
+
const ranges = curl?.colorRanges ?? []
|
|
88
|
+
// Each query param contributes a `key` (name) and a `value` range.
|
|
89
|
+
const display = curl?.display ?? ''
|
|
90
|
+
const keys = ranges.filter((r) => r.kind === 'key').map((r) => display.slice(...r.range))
|
|
91
|
+
const values = ranges.filter((r) => r.kind === 'value').map((r) => display.slice(...r.range))
|
|
92
|
+
// The key range includes the leading `?`/`&` separator so it's colored too.
|
|
93
|
+
expect(keys).toContain('?limit')
|
|
94
|
+
expect(keys).toContain('&dryRun')
|
|
95
|
+
expect(values).toContain('0')
|
|
96
|
+
expect(values).toContain('true')
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
test('places each query parameter on its own line', () => {
|
|
100
|
+
const samples = codeSamples(operation, 'https://api.example.com')
|
|
101
|
+
const curl = samples.find((sample) => sample.id === 'shell/curl')
|
|
102
|
+
// Copy text stays single-line/valid; the display variant is broken up.
|
|
103
|
+
expect(curl?.code).toContain('?limit=0&dryRun=true')
|
|
104
|
+
expect(curl?.display).toContain('\n ?limit=0')
|
|
105
|
+
expect(curl?.display).toContain('\n &dryRun=true')
|
|
106
|
+
const ts = samples.find((sample) => sample.id === 'js/fetch')
|
|
107
|
+
expect(ts?.display).toContain("'?limit=0'")
|
|
108
|
+
expect(ts?.display).toContain("'&dryRun=true'")
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
test('collapses query parameters beyond the first 2', () => {
|
|
112
|
+
const many: IrOperation = {
|
|
113
|
+
...operation,
|
|
114
|
+
parameters: [
|
|
115
|
+
{ name: 'a', in: 'query', schema: { type: 'string' } },
|
|
116
|
+
{ name: 'b', in: 'query', schema: { type: 'string' } },
|
|
117
|
+
{ name: 'c', in: 'query', schema: { type: 'string' } },
|
|
118
|
+
{ name: 'd', in: 'query', schema: { type: 'string' } },
|
|
119
|
+
{ name: 'e', in: 'query', schema: { type: 'string' } },
|
|
120
|
+
],
|
|
121
|
+
}
|
|
122
|
+
const curl = codeSamples(many).find((sample) => sample.id === 'shell/curl')
|
|
123
|
+
expect(curl?.hiddenQueryCount).toBe(3)
|
|
124
|
+
// Collapsed variant has the first 2 query params, full has all 5.
|
|
125
|
+
expect(curl?.collapsed?.display).toContain('?a=')
|
|
126
|
+
expect(curl?.collapsed?.display).toContain('&b=')
|
|
127
|
+
expect(curl?.collapsed?.display).not.toContain('&c=')
|
|
128
|
+
expect(curl?.display).toContain('&e=')
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
test('does not collapse 2 or fewer query parameters', () => {
|
|
132
|
+
const curl = codeSamples(operation).find((sample) => sample.id === 'shell/curl')
|
|
133
|
+
expect(curl?.collapsed).toBeUndefined()
|
|
134
|
+
expect(curl?.hiddenQueryCount).toBeUndefined()
|
|
135
|
+
})
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
describe('responseSamples', () => {
|
|
139
|
+
test('builds JSON samples only for responses with a JSON schema/example', () => {
|
|
140
|
+
const samples = responseSamples(operation)
|
|
141
|
+
expect(samples.map((sample) => sample.status)).toEqual(['200'])
|
|
142
|
+
expect(samples[0]?.lang).toBe('json')
|
|
143
|
+
expect(JSON.parse(samples[0]?.code ?? '{}')).toEqual({ id: 'string' })
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
test('marks synthesized placeholder values via offset ranges', () => {
|
|
147
|
+
const samples = responseSamples(operation)
|
|
148
|
+
const sample = samples[0]
|
|
149
|
+
expect(sample?.placeholders).toHaveLength(1)
|
|
150
|
+
const [start, end] = sample?.placeholders[0] ?? [0, 0]
|
|
151
|
+
expect(sample?.code.slice(start, end)).toBe('"string"')
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
test('authored examples have no placeholders', () => {
|
|
155
|
+
const samples = responseSamples({
|
|
156
|
+
...operation,
|
|
157
|
+
responses: [
|
|
158
|
+
{
|
|
159
|
+
status: '200',
|
|
160
|
+
description: 'A pet',
|
|
161
|
+
content: [{ mediaType: 'application/json', example: { id: 'abc' } }],
|
|
162
|
+
headers: [],
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
})
|
|
166
|
+
expect(samples[0]?.placeholders).toEqual([])
|
|
167
|
+
expect(JSON.parse(samples[0]?.code ?? '{}')).toEqual({ id: 'abc' })
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
test('exposes a status-scoped id base for anchor resolution', () => {
|
|
171
|
+
const samples = responseSamples(operation)
|
|
172
|
+
expect(samples[0]?.idBase).toBe('createpet-response-200')
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
test('records per-line schema paths for nested objects and arrays', () => {
|
|
176
|
+
const samples = responseSamples({
|
|
177
|
+
...operation,
|
|
178
|
+
responses: [
|
|
179
|
+
{
|
|
180
|
+
status: '200',
|
|
181
|
+
description: 'ok',
|
|
182
|
+
content: [
|
|
183
|
+
{
|
|
184
|
+
mediaType: 'application/json',
|
|
185
|
+
schema: {
|
|
186
|
+
type: 'object',
|
|
187
|
+
properties: {
|
|
188
|
+
data: {
|
|
189
|
+
type: 'object',
|
|
190
|
+
properties: {
|
|
191
|
+
items: {
|
|
192
|
+
type: 'array',
|
|
193
|
+
items: { type: 'object', properties: { id: { type: 'string' } } },
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
headers: [],
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
})
|
|
205
|
+
const sample = samples[0]
|
|
206
|
+
const lines = sample?.code.split('\n') ?? []
|
|
207
|
+
const paths = sample?.linePaths ?? []
|
|
208
|
+
// Find the line for the nested `id` and assert its recorded path.
|
|
209
|
+
const idLine = lines.findIndex((line) => line.includes('"id"'))
|
|
210
|
+
expect(paths[idLine]).toEqual(['data', 'items', 'id'])
|
|
211
|
+
// The `data` opening line maps to `['data']`.
|
|
212
|
+
const dataLine = lines.findIndex((line) => line.includes('"data"'))
|
|
213
|
+
expect(paths[dataLine]).toEqual(['data'])
|
|
214
|
+
// Closing braces carry no path.
|
|
215
|
+
const closingLine = lines.length - 1
|
|
216
|
+
expect(paths[closingLine]).toBeUndefined()
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
test('qualifies union variant paths so they match the variant picker rows', () => {
|
|
220
|
+
const samples = responseSamples({
|
|
221
|
+
...operation,
|
|
222
|
+
responses: [
|
|
223
|
+
{
|
|
224
|
+
status: '200',
|
|
225
|
+
description: 'ok',
|
|
226
|
+
content: [
|
|
227
|
+
{
|
|
228
|
+
mediaType: 'application/json',
|
|
229
|
+
schema: {
|
|
230
|
+
type: 'object',
|
|
231
|
+
properties: {
|
|
232
|
+
data: {
|
|
233
|
+
type: 'array',
|
|
234
|
+
items: {
|
|
235
|
+
anyOf: [
|
|
236
|
+
{
|
|
237
|
+
title: 'Transfer',
|
|
238
|
+
type: 'object',
|
|
239
|
+
properties: { amount: { type: 'string' } },
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
title: 'Mint',
|
|
243
|
+
type: 'object',
|
|
244
|
+
properties: { supply: { type: 'string' } },
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
],
|
|
253
|
+
headers: [],
|
|
254
|
+
},
|
|
255
|
+
],
|
|
256
|
+
})
|
|
257
|
+
const sample = samples[0]
|
|
258
|
+
const lines = sample?.code.split('\n') ?? []
|
|
259
|
+
const paths = sample?.linePaths ?? []
|
|
260
|
+
// The sampled variant (index 0) qualifies its children with `variant-0`.
|
|
261
|
+
const amountLine = lines.findIndex((line) => line.includes('"amount"'))
|
|
262
|
+
expect(paths[amountLine]).toEqual(['data', 'variant-0', 'amount'])
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
test('authored examples still record per-line schema paths', () => {
|
|
266
|
+
const samples = responseSamples({
|
|
267
|
+
...operation,
|
|
268
|
+
responses: [
|
|
269
|
+
{
|
|
270
|
+
status: '200',
|
|
271
|
+
description: 'ok',
|
|
272
|
+
content: [{ mediaType: 'application/json', example: { id: 'abc', nested: { x: 1 } } }],
|
|
273
|
+
headers: [],
|
|
274
|
+
},
|
|
275
|
+
],
|
|
276
|
+
})
|
|
277
|
+
const sample = samples[0]
|
|
278
|
+
const lines = sample?.code.split('\n') ?? []
|
|
279
|
+
const paths = sample?.linePaths ?? []
|
|
280
|
+
const xLine = lines.findIndex((line) => line.includes('"x"'))
|
|
281
|
+
expect(paths[xLine]).toEqual(['nested', 'x'])
|
|
282
|
+
})
|
|
283
|
+
})
|