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,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared user-agent lists and content negotiation used to decide whether a
|
|
3
|
+
* request for a documentation route should be served as Markdown instead of
|
|
4
|
+
* HTML. Consumed by the Waku `.md` router middleware and the standalone
|
|
5
|
+
* `Handler.openApi` server so both honor the same rules.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** AI crawlers/agents that should receive Markdown. */
|
|
9
|
+
export const aiUserAgents = [
|
|
10
|
+
'GPTBot',
|
|
11
|
+
'OAI-SearchBot',
|
|
12
|
+
'ChatGPT-User',
|
|
13
|
+
'ChatGPT-User/2.0',
|
|
14
|
+
'anthropic-ai',
|
|
15
|
+
'ClaudeBot',
|
|
16
|
+
'claude-web',
|
|
17
|
+
'PerplexityBot',
|
|
18
|
+
'Perplexity-User',
|
|
19
|
+
'Google-Extended',
|
|
20
|
+
'FacebookBot',
|
|
21
|
+
'meta-externalagent',
|
|
22
|
+
'Bytespider',
|
|
23
|
+
'cohere-ai',
|
|
24
|
+
'AI2Bot',
|
|
25
|
+
'CCBot',
|
|
26
|
+
'Diffbot',
|
|
27
|
+
'omgili',
|
|
28
|
+
'Timpibot',
|
|
29
|
+
'MistralAI-User',
|
|
30
|
+
'GoogleAgent-Mariner',
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
/** Search engine crawlers that should always receive HTML. */
|
|
34
|
+
export const searchEngineUserAgents = [
|
|
35
|
+
'Googlebot',
|
|
36
|
+
'Bingbot',
|
|
37
|
+
'Amazonbot',
|
|
38
|
+
'Applebot',
|
|
39
|
+
'Applebot-Extended',
|
|
40
|
+
'DuckAssistBot',
|
|
41
|
+
'YouBot',
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
/** Terminal HTTP clients that should receive Markdown. */
|
|
45
|
+
export const terminalUserAgents = ['curl/', 'Wget/', 'HTTPie/', 'httpie-go/', 'xh/']
|
|
46
|
+
|
|
47
|
+
/** Link-preview/Open Graph bots that must always receive HTML (for meta tags). */
|
|
48
|
+
export const ogBotUserAgents = [
|
|
49
|
+
'Discordbot',
|
|
50
|
+
'Embedly',
|
|
51
|
+
'Facebot',
|
|
52
|
+
'Iframely',
|
|
53
|
+
'LinkedInBot',
|
|
54
|
+
'Pinterestbot',
|
|
55
|
+
'Slackbot',
|
|
56
|
+
'Slurp',
|
|
57
|
+
'TelegramBot',
|
|
58
|
+
'Twitterbot',
|
|
59
|
+
'WhatsApp',
|
|
60
|
+
'facebookexternalhit',
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Decides whether a route request should be served as Markdown.
|
|
65
|
+
*
|
|
66
|
+
* Resolution order (matches the `.md` router middleware):
|
|
67
|
+
* 1. Open Graph bots never get Markdown (they need HTML meta tags).
|
|
68
|
+
* 2. An explicit `.md` suffix always serves Markdown.
|
|
69
|
+
* 3. Search engines otherwise always get HTML.
|
|
70
|
+
* 4. AI agents, terminal clients, or `Accept: text/markdown` get Markdown.
|
|
71
|
+
* 5. Everything else gets HTML.
|
|
72
|
+
*/
|
|
73
|
+
export function prefersMarkdown(options: prefersMarkdown.Options): boolean {
|
|
74
|
+
const { pathname, userAgent = '', accept = '' } = options
|
|
75
|
+
|
|
76
|
+
if (ogBotUserAgents.some((agent) => userAgent.includes(agent))) return false
|
|
77
|
+
if (pathname.endsWith('.md')) return true
|
|
78
|
+
if (searchEngineUserAgents.some((agent) => userAgent.includes(agent))) return false
|
|
79
|
+
|
|
80
|
+
const isAiAgent = aiUserAgents.some((agent) => userAgent.includes(agent))
|
|
81
|
+
const isTerminal = terminalUserAgents.some((agent) => userAgent.includes(agent))
|
|
82
|
+
const acceptsMarkdown = accept.includes('text/markdown')
|
|
83
|
+
return isAiAgent || isTerminal || acceptsMarkdown
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export declare namespace prefersMarkdown {
|
|
87
|
+
type Options = {
|
|
88
|
+
/** Request pathname (used to detect an explicit `.md` suffix). */
|
|
89
|
+
pathname: string
|
|
90
|
+
/** Request `User-Agent` header. */
|
|
91
|
+
userAgent?: string | undefined
|
|
92
|
+
/** Request `Accept` header. */
|
|
93
|
+
accept?: string | undefined
|
|
94
|
+
}
|
|
95
|
+
}
|
package/src/internal/markdown.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type * as Hast from 'hast'
|
|
2
2
|
import type * as Mdast from 'mdast'
|
|
3
|
+
import rehypeSlug from 'rehype-slug'
|
|
3
4
|
import rehypeStringify from 'rehype-stringify'
|
|
4
5
|
import remarkGfm from 'remark-gfm'
|
|
5
6
|
import remarkParse from 'remark-parse'
|
|
@@ -160,6 +161,10 @@ const processor = unified()
|
|
|
160
161
|
.use(remarkGfm)
|
|
161
162
|
.use(remarkGitHubCallouts)
|
|
162
163
|
.use(remarkRehype, { allowDangerousHtml: true })
|
|
164
|
+
// Add `id`s to headings so the outline (which scans `[data-v-content]
|
|
165
|
+
// :is(h2..h6)[id]`) and heading anchors work on guide/trait pages and on
|
|
166
|
+
// rendered spec descriptions, matching the MDX pipeline.
|
|
167
|
+
.use(rehypeSlug)
|
|
163
168
|
.use(rehypeVocsAttrs)
|
|
164
169
|
.use(rehypeTableWrapper)
|
|
165
170
|
.use(rehypeCalloutIcons)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared id/path helpers that keep the left-hand schema documentation rows and
|
|
3
|
+
* the right-hand response example lines pointing at the same anchor ids. Both
|
|
4
|
+
* sides derive an element id from a schema "path" (the chain of property names
|
|
5
|
+
* leading to a value, e.g. `['data', 'items', 'id']`) plus an `idBase` that
|
|
6
|
+
* scopes the path to a specific operation section (a request body or a single
|
|
7
|
+
* response status).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** A chain of property names identifying a value within a schema. */
|
|
11
|
+
export type SchemaPath = readonly string[]
|
|
12
|
+
|
|
13
|
+
/** Slugifies a path segment / title into a URL-safe id fragment. */
|
|
14
|
+
export function slug(value: string): string {
|
|
15
|
+
const result = value
|
|
16
|
+
.toLowerCase()
|
|
17
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
18
|
+
.replace(/^-+|-+$/g, '')
|
|
19
|
+
return result || 'value'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Id base for an operation's request body schema rows. */
|
|
23
|
+
export function requestBodyIdBase(operationId: string): string {
|
|
24
|
+
return `${operationId}-request-body`
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Id base for a single response status's schema rows. */
|
|
28
|
+
export function responseIdBase(operationId: string, status: string): string {
|
|
29
|
+
return `${operationId}-response-${slug(status)}`
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Qualifies an id base by media type when a section exposes more than one media
|
|
34
|
+
* type, so colliding paths across media types resolve to distinct ids. With a
|
|
35
|
+
* single media type the base is returned unchanged (shorter, stable ids).
|
|
36
|
+
*/
|
|
37
|
+
export function mediaIdBase(base: string, mediaType: string, mediaCount: number): string {
|
|
38
|
+
return mediaCount > 1 ? `${base}-${slug(mediaType)}` : base
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The element id for a schema property at `path` within `idBase`. */
|
|
42
|
+
export function schemaPropertyId(idBase: string, path: SchemaPath): string {
|
|
43
|
+
return `${idBase}-${slug(path.join('.'))}`
|
|
44
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the prebuilt OpenAPI app (the `Handler.openApi` server
|
|
3
|
+
* handler and the static browser bundle it serves).
|
|
4
|
+
*
|
|
5
|
+
* This module is intentionally free of Node and DOM dependencies so it can be
|
|
6
|
+
* imported by both the server handler and the client entry.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Config } from '../config.js'
|
|
10
|
+
import type { SidebarItem } from '../sidebar.js'
|
|
11
|
+
import { normalizePath } from './openapi.js'
|
|
12
|
+
import type { Ir } from './parser.js'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A block of rendered page content. Guide/override pages are compiled on the
|
|
16
|
+
* server into an ordered list of blocks so embedded `<OpenApi.Endpoints />`
|
|
17
|
+
* components can be re-hydrated as real React on the client (rather than
|
|
18
|
+
* inlined as static HTML).
|
|
19
|
+
*/
|
|
20
|
+
export type PageBlock =
|
|
21
|
+
| { type: 'html'; html: string }
|
|
22
|
+
| { type: 'endpoints'; path?: string | undefined }
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A compiled consumer-authored page, keyed by its section-relative route.
|
|
26
|
+
*
|
|
27
|
+
* - `path === '/'` overrides the landing/intro content.
|
|
28
|
+
* - `path === '/<group>'` overrides a category header.
|
|
29
|
+
* - any other `path` is a standalone guide page.
|
|
30
|
+
*/
|
|
31
|
+
export type CompiledPage = {
|
|
32
|
+
/** Section-relative route (normalized, leading slash, no trailing slash). */
|
|
33
|
+
path: string
|
|
34
|
+
/** Document title (frontmatter `title` or first `# heading`). */
|
|
35
|
+
title?: string | undefined
|
|
36
|
+
/** Subtitle Markdown rendered below the title on guide pages. */
|
|
37
|
+
description?: string | undefined
|
|
38
|
+
/**
|
|
39
|
+
* Render `title`/`description` as an on-page header above the content. Set for
|
|
40
|
+
* trait pages (`x-traitTag`), whose title comes from the tag name and subtitle
|
|
41
|
+
* from `x-subtitle` (their Markdown body has no heading). Authored guide pages
|
|
42
|
+
* leave this unset because their MDX owns its own headings.
|
|
43
|
+
*/
|
|
44
|
+
header?: boolean | undefined
|
|
45
|
+
/** Ordered content blocks. */
|
|
46
|
+
blocks: PageBlock[]
|
|
47
|
+
/**
|
|
48
|
+
* Raw authored Markdown (frontmatter + ESM import/export lines stripped), used
|
|
49
|
+
* to serve the page's `.md` / agent-facing version. Absent for trait pages
|
|
50
|
+
* compiled from a description that is already the body.
|
|
51
|
+
*/
|
|
52
|
+
markdown?: string | undefined
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The JSON payload embedded in the standalone HTML shell and consumed by the
|
|
57
|
+
* browser bundle to render the reference entirely client-side.
|
|
58
|
+
*/
|
|
59
|
+
export type Payload = {
|
|
60
|
+
/** Parsed OpenAPI IR. */
|
|
61
|
+
ir: Ir
|
|
62
|
+
/** Document title (spec `info.title`). */
|
|
63
|
+
title: string
|
|
64
|
+
/** Computed sidebar (extras + generated items). */
|
|
65
|
+
sidebar: SidebarItem<true>[]
|
|
66
|
+
/** Compiled override/guide pages. */
|
|
67
|
+
pages: CompiledPage[]
|
|
68
|
+
/**
|
|
69
|
+
* Serialized Vocs config that backs `virtual:vocs/config` in the browser
|
|
70
|
+
* bundle, so the prebuilt app renders the real Vocs layout/chrome. Functions
|
|
71
|
+
* are serialized via {@link file://../config-serializer.ts config-serializer};
|
|
72
|
+
* the client deserializes them on load.
|
|
73
|
+
*/
|
|
74
|
+
config: Config
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Joins a section base path with a section-relative sub-path. */
|
|
78
|
+
export function join(base: string, sub: string): string {
|
|
79
|
+
const b = base === '/' ? '' : base
|
|
80
|
+
if (!sub || sub === '/') return b || '/'
|
|
81
|
+
return `${b}${sub.startsWith('/') ? sub : `/${sub}`}`
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The set of known section routes for a payload: the intro (the spec `path`),
|
|
86
|
+
* one per category, and one per compiled page.
|
|
87
|
+
*/
|
|
88
|
+
export function knownRoutes(payload: Payload): string[] {
|
|
89
|
+
const base = payload.ir.path || '/'
|
|
90
|
+
const routes = new Set<string>()
|
|
91
|
+
routes.add(base === '/' ? '/' : normalizePath(base))
|
|
92
|
+
for (const group of payload.ir.groups) routes.add(join(base, `/${group.id}`))
|
|
93
|
+
for (const page of payload.pages) routes.add(join(base, page.path))
|
|
94
|
+
return [...routes]
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Infers the host mount prefix from a request pathname: the longest known
|
|
99
|
+
* section route that is a path-suffix of `pathname` is stripped; whatever
|
|
100
|
+
* precedes it is the mount (e.g. `/docs/pets` with route `/pets` → `/docs`).
|
|
101
|
+
* Falls back to treating the whole pathname as the mount (the intro page).
|
|
102
|
+
*/
|
|
103
|
+
export function inferMount(pathname: string, routes: string[]): string {
|
|
104
|
+
const path = stripTrailingSlash(pathname)
|
|
105
|
+
let mount = ''
|
|
106
|
+
let best = -1
|
|
107
|
+
for (const route of routes) {
|
|
108
|
+
if (route === '/') continue
|
|
109
|
+
if (path === route || path.endsWith(route)) {
|
|
110
|
+
const prefix = path.slice(0, path.length - route.length)
|
|
111
|
+
if (route.length > best && (prefix === '' || prefix.startsWith('/'))) {
|
|
112
|
+
best = route.length
|
|
113
|
+
mount = prefix
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// No group/page route matched: we're on the intro/landing page, so the whole
|
|
118
|
+
// pathname is the mount. A root mount (`/`) has no prefix, though — return ''
|
|
119
|
+
// so callers build `/group` rather than `//group` (a protocol-relative URL the
|
|
120
|
+
// browser reads as `http://group/`).
|
|
121
|
+
if (best === -1) return path === '/' ? '' : path
|
|
122
|
+
return mount
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function stripTrailingSlash(value: string): string {
|
|
126
|
+
return value.length > 1 && value.endsWith('/') ? value.slice(0, -1) : value
|
|
127
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type { SchemaPath } from './anchors.js'
|
|
2
|
+
export {
|
|
3
|
+
mediaIdBase,
|
|
4
|
+
requestBodyIdBase,
|
|
5
|
+
responseIdBase,
|
|
6
|
+
schemaPropertyId,
|
|
7
|
+
slug,
|
|
8
|
+
} from './anchors.js'
|
|
9
|
+
export * from './openapi.js'
|
|
10
|
+
export type {
|
|
11
|
+
Ir,
|
|
12
|
+
IrBody,
|
|
13
|
+
IrGroup,
|
|
14
|
+
IrMediaType,
|
|
15
|
+
IrOperation,
|
|
16
|
+
IrParameter,
|
|
17
|
+
IrResponse,
|
|
18
|
+
IrSecurityScheme,
|
|
19
|
+
IrServer,
|
|
20
|
+
Method,
|
|
21
|
+
} from './parser.js'
|
|
22
|
+
export { methods, parse } from './parser.js'
|
|
23
|
+
export type { CodeSample, ResponseSample } from './sample.js'
|
|
24
|
+
export { codeSamples, harRequest, responseSamples } from './sample.js'
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import { fromIr } from './markdown.js'
|
|
3
|
+
import type { Ir } from './parser.js'
|
|
4
|
+
|
|
5
|
+
const ir: Ir = {
|
|
6
|
+
path: '/api',
|
|
7
|
+
client: { url: 'https://example.com/openapi.json' },
|
|
8
|
+
info: { title: 'Petstore', version: '1.0.0', description: 'The **best** pet API.' },
|
|
9
|
+
servers: [{ url: 'https://example.com', description: 'Production' }],
|
|
10
|
+
securitySchemes: {},
|
|
11
|
+
traits: [],
|
|
12
|
+
groups: [
|
|
13
|
+
{
|
|
14
|
+
id: 'pets',
|
|
15
|
+
name: 'Pets',
|
|
16
|
+
description: 'Manage pets.',
|
|
17
|
+
operations: [
|
|
18
|
+
{
|
|
19
|
+
id: 'listpets',
|
|
20
|
+
method: 'GET',
|
|
21
|
+
path: '/pets',
|
|
22
|
+
summary: 'List pets',
|
|
23
|
+
description: 'Returns all pets.',
|
|
24
|
+
parameters: [
|
|
25
|
+
{ name: 'limit', in: 'query', description: 'Max items', schema: { type: 'integer' } },
|
|
26
|
+
{ name: 'cursor', in: 'query', deprecated: true },
|
|
27
|
+
],
|
|
28
|
+
responses: [
|
|
29
|
+
{
|
|
30
|
+
status: '200',
|
|
31
|
+
description: 'A list of pets',
|
|
32
|
+
headers: [
|
|
33
|
+
{ name: 'X-Total', schema: { type: 'integer' }, description: 'Total count' },
|
|
34
|
+
],
|
|
35
|
+
content: [
|
|
36
|
+
{
|
|
37
|
+
mediaType: 'application/json',
|
|
38
|
+
schema: {
|
|
39
|
+
type: 'object',
|
|
40
|
+
required: ['data'],
|
|
41
|
+
properties: {
|
|
42
|
+
data: { type: 'array', items: { type: 'object' }, description: 'Pets.' },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: 'createpet',
|
|
52
|
+
method: 'POST',
|
|
53
|
+
path: '/pets',
|
|
54
|
+
summary: 'Create pet',
|
|
55
|
+
parameters: [],
|
|
56
|
+
requestBody: {
|
|
57
|
+
required: true,
|
|
58
|
+
content: [
|
|
59
|
+
{
|
|
60
|
+
mediaType: 'application/json',
|
|
61
|
+
schema: {
|
|
62
|
+
type: 'object',
|
|
63
|
+
required: ['name'],
|
|
64
|
+
properties: { name: { type: 'string', description: 'Pet name.' } },
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
responses: [{ status: '201', description: 'Created', content: [], headers: [] }],
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
describe('fromIr', () => {
|
|
77
|
+
const pages = fromIr(ir)
|
|
78
|
+
|
|
79
|
+
test('emits an overview page and one page per category', () => {
|
|
80
|
+
expect(pages.map((page) => ({ path: page.path, title: page.title }))).toEqual([
|
|
81
|
+
{ path: '/api', title: 'Petstore' },
|
|
82
|
+
{ path: '/api/pets', title: 'Pets' },
|
|
83
|
+
])
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('overview lists every category with links to its operations', () => {
|
|
87
|
+
const overview = pages.find((page) => page.path === '/api')
|
|
88
|
+
expect(overview?.content).toContain('# Petstore')
|
|
89
|
+
expect(overview?.content).toContain('Version: `1.0.0`')
|
|
90
|
+
expect(overview?.content).toContain('## Servers')
|
|
91
|
+
expect(overview?.content).toContain('## Endpoints')
|
|
92
|
+
expect(overview?.content).toContain('### Pets')
|
|
93
|
+
expect(overview?.content).toContain('- [`GET /pets`](/api/pets#listpets): List pets')
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
test('category page renders params, request body, responses and samples', () => {
|
|
97
|
+
const group = pages.find((page) => page.path === '/api/pets')
|
|
98
|
+
const content = group?.content ?? ''
|
|
99
|
+
expect(content).toContain('## List pets')
|
|
100
|
+
expect(content).toContain('`GET /pets`')
|
|
101
|
+
expect(content).toContain('### Query parameters')
|
|
102
|
+
expect(content).toContain('- `limit` `integer`: Max items')
|
|
103
|
+
// Deprecated parameters are excluded.
|
|
104
|
+
expect(content).not.toContain('cursor')
|
|
105
|
+
expect(content).toContain('### Responses')
|
|
106
|
+
expect(content).toContain('#### `200`: A list of pets')
|
|
107
|
+
expect(content).toContain('- `X-Total` `integer`: Total count')
|
|
108
|
+
expect(content).toContain('- `data` `object[]` _(required)_: Pets.')
|
|
109
|
+
expect(content).toContain('### Request body (required) (`application/json`)')
|
|
110
|
+
expect(content).toContain('- `name` `string` _(required)_: Pet name.')
|
|
111
|
+
expect(content).toContain('### Example request')
|
|
112
|
+
expect(content).toContain('```bash')
|
|
113
|
+
expect(content).toContain('curl')
|
|
114
|
+
})
|
|
115
|
+
})
|