@heyo-sh/create-heyo-docs 1.0.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/deployments/cloudflare/astro/package.json +2 -1
  2. package/deployments/cloudflare/astro/src/env.d.ts +4 -1
  3. package/deployments/cloudflare/astro/src/pages/heyo-docs-internal/ai-chat.ts +9 -7
  4. package/deployments/cloudflare/next/app/heyo-docs-internal/ai-chat/route.ts +9 -7
  5. package/deployments/cloudflare/next/cloudflare-env.d.ts +4 -1
  6. package/deployments/cloudflare/react-router/app/cloudflare-env.d.ts +4 -1
  7. package/deployments/cloudflare/react-router/app/routes/ai-chat.ts +9 -8
  8. package/deployments/cloudflare/react-router/package.json +2 -1
  9. package/deployments/vercel/astro/package.json +1 -0
  10. package/dist/index.js +24 -4
  11. package/dist/scaffold.d.ts.map +1 -1
  12. package/dist/utils.d.ts.map +1 -1
  13. package/package.json +1 -1
  14. package/templates/astro/content/index.mdx +8 -0
  15. package/templates/astro/heyo-docs.config.ts +4 -48
  16. package/templates/astro/src/components/docs-app.tsx +30 -130
  17. package/templates/astro/src/heyo-docs-icons.tsx +3 -1
  18. package/templates/astro/src/layouts/docs-layout.astro +10 -436
  19. package/templates/astro/src/lib/docs.ts +3 -2
  20. package/templates/astro/src/lib/openapi-request.ts +6 -146
  21. package/templates/astro/src/pages/[...slug].astro +7 -15
  22. package/templates/astro/src/pages/[...slug].md.ts +4 -1
  23. package/templates/astro/src/pages/heyo-docs-internal/ai-chat.ts +5 -7
  24. package/templates/astro/src/pages/heyo-docs-internal/openapi-request.ts +6 -0
  25. package/templates/astro/src/pages/index.astro +18 -9
  26. package/templates/astro/src/pages/llms-full.txt.ts +1 -1
  27. package/templates/astro/src/pages/llms.txt.ts +1 -1
  28. package/templates/astro/src/pages/rss.xml.ts +1 -1
  29. package/templates/astro/src/pages/sitemap.xml.ts +2 -2
  30. package/templates/astro/src/virtual.d.ts +5 -5
  31. package/templates/next/app/[[...slug]]/page.tsx +10 -9
  32. package/templates/next/app/components/docs-app.tsx +28 -120
  33. package/templates/next/app/heyo-docs-icons.tsx +1 -1
  34. package/templates/next/app/heyo-docs-internal/ai-chat/route.ts +6 -7
  35. package/templates/next/app/heyo-docs-internal/markdown/[[...slug]]/route.ts +1 -1
  36. package/templates/next/app/layout.tsx +25 -444
  37. package/templates/next/app/lib/docs.ts +3 -3
  38. package/templates/next/app/lib/openapi-request.ts +4 -136
  39. package/templates/next/app/llms-full.txt/route.ts +1 -1
  40. package/templates/next/app/llms.txt/route.ts +1 -1
  41. package/templates/next/app/rss.xml/route.ts +1 -1
  42. package/templates/next/app/sitemap.xml/route.ts +1 -1
  43. package/templates/next/content/index.mdx +8 -0
  44. package/templates/next/heyo-docs.config.ts +3 -47
  45. package/templates/next/next.config.ts +7 -1
  46. package/templates/react-router/app/heyo-docs-icons.tsx +3 -1
  47. package/templates/react-router/app/root.tsx +28 -465
  48. package/templates/react-router/app/routes/ai-chat.ts +4 -8
  49. package/templates/react-router/app/routes/docs.tsx +22 -63
  50. package/templates/react-router/app/routes/home.tsx +6 -1
  51. package/templates/react-router/app/routes/llms-full.ts +1 -1
  52. package/templates/react-router/app/routes/llms.ts +1 -1
  53. package/templates/react-router/app/routes/markdown.ts +4 -1
  54. package/templates/react-router/app/routes/openapi-request.ts +11 -144
  55. package/templates/react-router/app/routes/rss.ts +1 -1
  56. package/templates/react-router/app/routes/sitemap.ts +5 -8
  57. package/templates/react-router/app/virtual.d.ts +5 -5
  58. package/templates/react-router/content/index.mdx +8 -0
  59. package/templates/react-router/heyo-docs.config.ts +4 -48
  60. package/templates/astro/content/api-overview.mdx +0 -20
  61. package/templates/astro/content/assets/local-architecture.svg +0 -22
  62. package/templates/astro/content/assets/quick-reference.txt +0 -5
  63. package/templates/astro/content/changelog.mdx +0 -38
  64. package/templates/astro/content/configuration.mdx +0 -117
  65. package/templates/astro/content/deploying.mdx +0 -70
  66. package/templates/astro/content/essentials/callouts.mdx +0 -34
  67. package/templates/astro/content/essentials/code-blocks.mdx +0 -43
  68. package/templates/astro/content/essentials/lists.mdx +0 -36
  69. package/templates/astro/content/essentials/tables.mdx +0 -30
  70. package/templates/astro/content/essentials/tabs.mdx +0 -40
  71. package/templates/astro/content/essentials/text-styling.mdx +0 -43
  72. package/templates/astro/content/getting-started.mdx +0 -16
  73. package/templates/astro/content/mdx-components.mdx +0 -44
  74. package/templates/astro/content/openapi-petstore.json +0 -160
  75. package/templates/astro/content/openapi.json +0 -278
  76. package/templates/astro/content/quickstart.mdx +0 -60
  77. package/templates/astro/src/components/theme-provider.tsx +0 -106
  78. package/templates/astro/src/lib/seo.ts +0 -221
  79. package/templates/astro/src/lib/theme.ts +0 -74
  80. package/templates/next/app/components/theme-provider.tsx +0 -93
  81. package/templates/next/app/lib/seo.ts +0 -189
  82. package/templates/next/app/lib/theme.ts +0 -61
  83. package/templates/next/content/api-overview.mdx +0 -20
  84. package/templates/next/content/assets/local-architecture.svg +0 -22
  85. package/templates/next/content/assets/quick-reference.txt +0 -5
  86. package/templates/next/content/changelog.mdx +0 -38
  87. package/templates/next/content/configuration.mdx +0 -117
  88. package/templates/next/content/deploying.mdx +0 -70
  89. package/templates/next/content/essentials/callouts.mdx +0 -34
  90. package/templates/next/content/essentials/code-blocks.mdx +0 -43
  91. package/templates/next/content/essentials/lists.mdx +0 -36
  92. package/templates/next/content/essentials/tables.mdx +0 -30
  93. package/templates/next/content/essentials/tabs.mdx +0 -40
  94. package/templates/next/content/essentials/text-styling.mdx +0 -43
  95. package/templates/next/content/getting-started.mdx +0 -16
  96. package/templates/next/content/mdx-components.mdx +0 -41
  97. package/templates/next/content/openapi-petstore.json +0 -160
  98. package/templates/next/content/openapi.json +0 -278
  99. package/templates/next/content/quickstart.mdx +0 -60
  100. package/templates/react-router/app/components/theme-provider.tsx +0 -106
  101. package/templates/react-router/app/lib/theme.ts +0 -74
  102. package/templates/react-router/content/api-overview.mdx +0 -20
  103. package/templates/react-router/content/assets/local-architecture.svg +0 -22
  104. package/templates/react-router/content/assets/quick-reference.txt +0 -5
  105. package/templates/react-router/content/changelog.mdx +0 -38
  106. package/templates/react-router/content/configuration.mdx +0 -117
  107. package/templates/react-router/content/deploying.mdx +0 -70
  108. package/templates/react-router/content/essentials/callouts.mdx +0 -34
  109. package/templates/react-router/content/essentials/code-blocks.mdx +0 -43
  110. package/templates/react-router/content/essentials/lists.mdx +0 -36
  111. package/templates/react-router/content/essentials/tables.mdx +0 -30
  112. package/templates/react-router/content/essentials/tabs.mdx +0 -40
  113. package/templates/react-router/content/essentials/text-styling.mdx +0 -43
  114. package/templates/react-router/content/getting-started.mdx +0 -16
  115. package/templates/react-router/content/mdx-components.mdx +0 -293
  116. package/templates/react-router/content/openapi-petstore.json +0 -160
  117. package/templates/react-router/content/openapi.json +0 -278
  118. package/templates/react-router/content/quickstart.mdx +0 -60
@@ -1,278 +0,0 @@
1
- {
2
- "openapi": "3.1.0",
3
- "info": {
4
- "title": "Planet API",
5
- "version": "1.0.0",
6
- "description": "An example API used by the Heyo Docs React Router demo."
7
- },
8
- "servers": [
9
- {
10
- "url": "https://api.example.com/v1",
11
- "description": "Example production API"
12
- }
13
- ],
14
- "security": [
15
- {
16
- "BearerAuth": []
17
- }
18
- ],
19
- "paths": {
20
- "/organizations/{organizationId}/planets": {
21
- "parameters": [
22
- {
23
- "name": "organizationId",
24
- "in": "path",
25
- "required": true,
26
- "description": "The organization that owns the planets.",
27
- "schema": {
28
- "type": "string",
29
- "example": "org_123"
30
- }
31
- }
32
- ],
33
- "get": {
34
- "operationId": "listPlanets",
35
- "summary": "List planets",
36
- "description": "Returns the planets that belong to an organization.",
37
- "tags": ["Planets"],
38
- "parameters": [
39
- {
40
- "name": "limit",
41
- "in": "query",
42
- "description": "Maximum number of records to return.",
43
- "schema": {
44
- "type": "integer",
45
- "default": 20
46
- }
47
- }
48
- ],
49
- "responses": {
50
- "200": {
51
- "description": "A page of planets.",
52
- "content": {
53
- "application/json": {
54
- "schema": {
55
- "type": "array",
56
- "items": {
57
- "$ref": "#/components/schemas/Planet"
58
- }
59
- }
60
- }
61
- }
62
- }
63
- }
64
- },
65
- "post": {
66
- "operationId": "createPlanet",
67
- "summary": "Create a planet",
68
- "description": "Creates a planet inside an organization.",
69
- "tags": ["Planets"],
70
- "requestBody": {
71
- "required": true,
72
- "description": "The planet to create.",
73
- "content": {
74
- "application/json": {
75
- "schema": {
76
- "$ref": "#/components/schemas/PlanetInput"
77
- }
78
- }
79
- }
80
- },
81
- "responses": {
82
- "201": {
83
- "description": "The newly created planet.",
84
- "content": {
85
- "application/json": {
86
- "schema": {
87
- "$ref": "#/components/schemas/Planet"
88
- }
89
- }
90
- }
91
- }
92
- }
93
- }
94
- },
95
- "/organizations/{organizationId}/planets/{planetId}": {
96
- "parameters": [
97
- {
98
- "name": "organizationId",
99
- "in": "path",
100
- "required": true,
101
- "description": "The organization that owns the planet.",
102
- "schema": {
103
- "type": "string",
104
- "example": "org_123"
105
- }
106
- },
107
- {
108
- "name": "planetId",
109
- "in": "path",
110
- "required": true,
111
- "description": "The planet identifier.",
112
- "schema": {
113
- "type": "string",
114
- "example": "planet_earth"
115
- }
116
- }
117
- ],
118
- "get": {
119
- "operationId": "getPlanet",
120
- "summary": "Get a planet",
121
- "description": "Returns one planet by ID.",
122
- "tags": ["Planets"],
123
- "responses": {
124
- "200": {
125
- "description": "The requested planet.",
126
- "content": {
127
- "application/json": {
128
- "schema": {
129
- "$ref": "#/components/schemas/Planet"
130
- }
131
- }
132
- }
133
- },
134
- "404": {
135
- "description": "Planet not found."
136
- }
137
- }
138
- },
139
- "put": {
140
- "operationId": "replacePlanet",
141
- "summary": "Replace a planet",
142
- "description": "Replaces all editable planet values.",
143
- "tags": ["Planets"],
144
- "requestBody": {
145
- "required": true,
146
- "content": {
147
- "application/json": {
148
- "schema": {
149
- "$ref": "#/components/schemas/PlanetInput"
150
- }
151
- }
152
- }
153
- },
154
- "responses": {
155
- "200": {
156
- "description": "The updated planet.",
157
- "content": {
158
- "application/json": {
159
- "schema": {
160
- "$ref": "#/components/schemas/Planet"
161
- }
162
- }
163
- }
164
- }
165
- }
166
- },
167
- "patch": {
168
- "operationId": "updatePlanet",
169
- "summary": "Update a planet",
170
- "description": "Updates one or more planet fields.",
171
- "tags": ["Planets"],
172
- "requestBody": {
173
- "required": true,
174
- "content": {
175
- "application/json": {
176
- "schema": {
177
- "$ref": "#/components/schemas/PlanetPatch"
178
- }
179
- }
180
- }
181
- },
182
- "responses": {
183
- "200": {
184
- "description": "The updated planet.",
185
- "content": {
186
- "application/json": {
187
- "schema": {
188
- "$ref": "#/components/schemas/Planet"
189
- }
190
- }
191
- }
192
- }
193
- }
194
- },
195
- "delete": {
196
- "operationId": "deletePlanet",
197
- "summary": "Delete a planet",
198
- "description": "Permanently removes a planet from an organization.",
199
- "tags": ["Planets"],
200
- "parameters": [
201
- {
202
- "name": "hardDelete",
203
- "in": "query",
204
- "description": "Also remove historical records.",
205
- "schema": {
206
- "type": "boolean",
207
- "default": false
208
- }
209
- }
210
- ],
211
- "responses": {
212
- "204": {
213
- "description": "Planet deleted."
214
- },
215
- "404": {
216
- "description": "Planet not found."
217
- }
218
- }
219
- }
220
- }
221
- },
222
- "components": {
223
- "securitySchemes": {
224
- "BearerAuth": {
225
- "type": "http",
226
- "scheme": "bearer",
227
- "bearerFormat": "JWT"
228
- }
229
- },
230
- "schemas": {
231
- "Planet": {
232
- "type": "object",
233
- "required": ["id", "name", "classification"],
234
- "properties": {
235
- "id": {
236
- "type": "string",
237
- "example": "planet_earth"
238
- },
239
- "name": {
240
- "type": "string",
241
- "example": "Earth"
242
- },
243
- "classification": {
244
- "type": "string",
245
- "enum": ["terrestrial", "gas-giant", "ice-giant"]
246
- }
247
- }
248
- },
249
- "PlanetInput": {
250
- "type": "object",
251
- "required": ["name", "classification"],
252
- "properties": {
253
- "name": {
254
- "type": "string",
255
- "example": "Mars"
256
- },
257
- "classification": {
258
- "type": "string",
259
- "enum": ["terrestrial", "gas-giant", "ice-giant"]
260
- }
261
- }
262
- },
263
- "PlanetPatch": {
264
- "type": "object",
265
- "properties": {
266
- "name": {
267
- "type": "string",
268
- "example": "New Mars"
269
- },
270
- "classification": {
271
- "type": "string",
272
- "enum": ["terrestrial", "gas-giant", "ice-giant"]
273
- }
274
- }
275
- }
276
- }
277
- }
278
- }
@@ -1,60 +0,0 @@
1
- ---
2
- title: Quickstart
3
- description: Create, run, and understand your Heyo Docs project.
4
- ---
5
-
6
- # Quickstart
7
-
8
- A new Heyo Docs project is a complete documentation site: MDX files provide the
9
- content, `heyo-docs.config.ts` defines the site model, and your selected
10
- framework renders the finished experience.
11
-
12
- ## Create a project
13
-
14
- Use the creator with the package manager you already use. It asks for a project
15
- name, framework, deployment target, theme, and package manager, then creates a
16
- ready-to-run application.
17
-
18
- <CodeBlockGroup defaultValue="bun" variant="line">
19
- <CodeBlock language="bash" title="bun">
20
- {"bun create @heyo-sh/heyo-docs"}
21
- </CodeBlock>
22
- <CodeBlock language="bash" title="npm">
23
- {"npm create @heyo-sh/heyo-docs@latest"}
24
- </CodeBlock>
25
- <CodeBlock language="bash" title="pnpm">
26
- {"pnpm create @heyo-sh/heyo-docs"}
27
- </CodeBlock>
28
- <CodeBlock language="bash" title="yarn">
29
- {"yarn dlx @heyo-sh/create-heyo-docs"}
30
- </CodeBlock>
31
- </CodeBlockGroup>
32
-
33
- When the command finishes, move into the new directory and run the development
34
- command printed by the creator. The local server rebuilds the documentation
35
- when you change either content or configuration.
36
-
37
- ## How the pieces fit together
38
-
39
- 1. **Content** lives in the `content` directory. Every `.mdx` file becomes a
40
- documentation route.
41
- 2. **Configuration** in `heyo-docs.config.ts` gives the site its name, theme,
42
- navigation order, and public metadata.
43
- 3. **The framework app** loads the same model during development and production
44
- builds, so the sidebar, search, routes, and adjacent-page links stay in sync.
45
-
46
- Create a new file such as `content/guides/deployment.mdx` to add the
47
- `/guides/deployment` route. Add that path to a navigation section when the
48
- page should appear in the sidebar.
49
-
50
- ## Make your first change
51
-
52
- Update the title and description in the frontmatter of `content/index.mdx`,
53
- then refresh the browser. Use [Basic Configuration](/configuration) to adjust
54
- the site model and [Text Styling](/essentials/text-styling) for writing the
55
- page itself.
56
-
57
- <Callout type="tip" title="One source of truth">
58
- Keep navigation in <code>heyo-docs.config.ts</code> and page-specific details
59
- in MDX frontmatter. The build validates both before publishing.
60
- </Callout>
@@ -1,106 +0,0 @@
1
- import {
2
- createContext,
3
- useContext,
4
- useEffect,
5
- useState,
6
- type ReactNode,
7
- } from "react";
8
-
9
- import {
10
- applyTheme,
11
- DEFAULT_THEME,
12
- getStoredTheme,
13
- type ResolvedTheme,
14
- THEME_STORAGE_KEY,
15
- type Theme,
16
- } from "../lib/theme";
17
-
18
- type ThemeProviderProps = {
19
- children: ReactNode;
20
- defaultTheme?: Theme;
21
- storageKey?: string;
22
- };
23
-
24
- type ThemeProviderState = {
25
- theme: Theme;
26
- resolvedTheme: ResolvedTheme;
27
- mounted: boolean;
28
- setTheme: (theme: Theme) => void;
29
- };
30
-
31
- const ThemeProviderContext = createContext<ThemeProviderState | undefined>(
32
- undefined,
33
- );
34
-
35
- export function ThemeProvider({
36
- children,
37
- defaultTheme = DEFAULT_THEME,
38
- storageKey = THEME_STORAGE_KEY,
39
- }: ThemeProviderProps) {
40
- const [theme, setThemeState] = useState<Theme>(defaultTheme);
41
- const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>(
42
- defaultTheme === "system" ? "light" : defaultTheme,
43
- );
44
- const [mounted, setMounted] = useState(false);
45
-
46
- useEffect(() => {
47
- const syncTheme = () => {
48
- const nextTheme = getStoredTheme(storageKey, defaultTheme);
49
-
50
- setThemeState(nextTheme);
51
- setResolvedTheme(applyTheme(nextTheme));
52
- };
53
-
54
- syncTheme();
55
-
56
- const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
57
- const handleSystemThemeChange = () => {
58
- const nextTheme = getStoredTheme(storageKey, defaultTheme);
59
- setResolvedTheme(applyTheme(nextTheme));
60
- };
61
- const handleStorage = (event: StorageEvent) => {
62
- if (event.key !== null && event.key !== storageKey) return;
63
- syncTheme();
64
- };
65
-
66
- mediaQuery.addEventListener("change", handleSystemThemeChange);
67
- window.addEventListener("storage", handleStorage);
68
- setMounted(true);
69
-
70
- return () => {
71
- mediaQuery.removeEventListener("change", handleSystemThemeChange);
72
- window.removeEventListener("storage", handleStorage);
73
- };
74
- }, [defaultTheme, storageKey]);
75
-
76
- const value = {
77
- theme,
78
- resolvedTheme,
79
- mounted,
80
- setTheme: (nextTheme: Theme) => {
81
- try {
82
- window.localStorage.setItem(storageKey, nextTheme);
83
- } catch {
84
- // The requested preference still applies when storage is unavailable.
85
- }
86
-
87
- setThemeState(nextTheme);
88
- setResolvedTheme(applyTheme(nextTheme));
89
- },
90
- };
91
-
92
- return (
93
- <ThemeProviderContext.Provider value={value}>
94
- {children}
95
- </ThemeProviderContext.Provider>
96
- );
97
- }
98
-
99
- export function useTheme() {
100
- const context = useContext(ThemeProviderContext);
101
-
102
- if (context === undefined)
103
- throw new Error("useTheme must be used within a ThemeProvider");
104
-
105
- return context;
106
- }
@@ -1,221 +0,0 @@
1
- import {
2
- navigationGroupContainsPath,
3
- navigationPages,
4
- navigationSectionPathForPath,
5
- type ChangelogGroupConfig,
6
- type DocsPage,
7
- type HeyoDocsConfig,
8
- type NavigationGroup,
9
- type OpenApiEndpoint,
10
- } from "@heyo-sh/heyo-docs";
11
-
12
- interface DocsSeoInput {
13
- config: HeyoDocsConfig;
14
- pathname: string;
15
- page?: DocsPage;
16
- endpoint?: OpenApiEndpoint;
17
- changelogGroup?: ChangelogGroupConfig;
18
- navigation?: NavigationGroup[];
19
- }
20
-
21
- function canonicalFor(
22
- config: HeyoDocsConfig,
23
- pathname: string,
24
- page?: DocsPage,
25
- ): string | undefined {
26
- return (
27
- page?.seo.canonical ??
28
- (config.siteUrl ? `${config.siteUrl}${pathname}` : undefined)
29
- );
30
- }
31
-
32
- function website(config: HeyoDocsConfig) {
33
- return {
34
- "@type": "WebSite",
35
- name: config.title,
36
- ...(config.siteUrl ? { url: config.siteUrl } : {}),
37
- };
38
- }
39
-
40
- interface BreadcrumbItem {
41
- label: string;
42
- href?: string;
43
- }
44
-
45
- function breadcrumbItems(
46
- navigation: NavigationGroup[] | undefined,
47
- pathname: string,
48
- title: string,
49
- changelogGroup: ChangelogGroupConfig | undefined,
50
- ): BreadcrumbItem[] {
51
- if (changelogGroup) return [{ label: changelogGroup.group, href: pathname }];
52
-
53
- const group = navigation?.find((candidate) =>
54
- navigationGroupContainsPath(candidate, pathname),
55
- );
56
- const sections = group
57
- ? navigationSectionPathForPath(group.sections, pathname)
58
- : undefined;
59
-
60
- if (!group) return [{ label: title, href: pathname }];
61
-
62
- const groupHref = navigationPages(group.sections)[0]?.slug;
63
- return [
64
- { label: group.group, href: groupHref },
65
- ...(sections ?? []).map((section) => ({
66
- label: section.section,
67
- href: navigationPages([section])[0]?.slug,
68
- })),
69
- { label: title, href: pathname },
70
- ];
71
- }
72
-
73
- function absoluteUrl(siteUrl: string | undefined, href: string | undefined) {
74
- if (!siteUrl || !href) return undefined;
75
- if (/^https?:\/\//.test(href)) return href;
76
- return new URL(href, `${siteUrl}/`).toString();
77
- }
78
-
79
- function breadcrumbs(
80
- config: HeyoDocsConfig,
81
- canonical: string | undefined,
82
- title: string,
83
- navigation: NavigationGroup[] | undefined,
84
- pathname: string,
85
- changelogGroup: ChangelogGroupConfig | undefined,
86
- ) {
87
- const items = breadcrumbItems(navigation, pathname, title, changelogGroup);
88
-
89
- return {
90
- "@context": "https://schema.org",
91
- "@type": "BreadcrumbList",
92
- itemListElement: [
93
- { label: config.title, href: config.siteUrl },
94
- ...items,
95
- ].map((item, index, allItems) => {
96
- const itemUrl =
97
- index === allItems.length - 1
98
- ? canonical
99
- : absoluteUrl(config.siteUrl, item.href);
100
- return {
101
- "@type": "ListItem",
102
- position: index + 1,
103
- name: item.label,
104
- ...(itemUrl ? { item: itemUrl } : {}),
105
- };
106
- }),
107
- };
108
- }
109
-
110
- function endpointUrlTemplate(endpoint: OpenApiEndpoint) {
111
- const server = endpoint.servers[0]?.replace(/\/$/, "");
112
- return server ? `${server}${endpoint.path}` : endpoint.path;
113
- }
114
-
115
- function structuredData({
116
- changelogGroup,
117
- config,
118
- endpoint,
119
- page,
120
- canonical,
121
- description,
122
- title,
123
- }: Omit<DocsSeoInput, "pathname"> & {
124
- canonical?: string;
125
- description: string;
126
- title: string;
127
- }) {
128
- if (endpoint) {
129
- const entryPoint = {
130
- "@type": "EntryPoint",
131
- name:
132
- endpoint.operationId ??
133
- `${endpoint.method.toUpperCase()} ${endpoint.path}`,
134
- httpMethod: endpoint.method.toUpperCase(),
135
- urlTemplate: endpointUrlTemplate(endpoint),
136
- ...(endpoint.requestBody?.contentType
137
- ? { encodingType: endpoint.requestBody.contentType }
138
- : {}),
139
- ...(endpoint.responses[0]?.contentType
140
- ? { contentType: endpoint.responses[0].contentType }
141
- : {}),
142
- };
143
- return {
144
- "@context": "https://schema.org",
145
- "@type": "APIReference",
146
- headline: title,
147
- description,
148
- ...(canonical ? { url: canonical } : {}),
149
- isPartOf: website(config),
150
- mainEntity: entryPoint,
151
- };
152
- }
153
-
154
- if (page && changelogGroup) {
155
- return {
156
- "@context": "https://schema.org",
157
- "@type": "CollectionPage",
158
- headline: title,
159
- description,
160
- ...(canonical ? { url: canonical } : {}),
161
- isPartOf: website(config),
162
- ...(page.changelogUpdates?.length
163
- ? {
164
- hasPart: page.changelogUpdates.map((update) => ({
165
- "@type": "TechArticle",
166
- headline: update.label,
167
- ...(update.tags.length
168
- ? { keywords: update.tags.join(", ") }
169
- : {}),
170
- ...(canonical ? { url: `${canonical}#${update.id}` } : {}),
171
- })),
172
- }
173
- : {}),
174
- };
175
- }
176
-
177
- return {
178
- "@context": "https://schema.org",
179
- "@type": "TechArticle",
180
- headline: title,
181
- description,
182
- ...(canonical ? { url: canonical } : {}),
183
- isPartOf: website(config),
184
- };
185
- }
186
-
187
- export function docsSeo(input: DocsSeoInput) {
188
- const { changelogGroup, config, endpoint, navigation, page, pathname } =
189
- input;
190
- const title = page?.seo.title ?? `${endpoint!.title} | ${config.title}`;
191
- const description =
192
- page?.seo.description ??
193
- endpoint?.description ??
194
- `${endpoint!.method.toUpperCase()} ${endpoint!.path} API endpoint.`;
195
- const canonical = canonicalFor(config, pathname, page);
196
-
197
- return {
198
- title,
199
- description,
200
- canonical,
201
- structuredData: [
202
- structuredData({
203
- changelogGroup,
204
- config,
205
- endpoint,
206
- page,
207
- canonical,
208
- description,
209
- title,
210
- }),
211
- breadcrumbs(
212
- config,
213
- canonical,
214
- page?.title ?? endpoint!.title,
215
- navigation,
216
- pathname,
217
- changelogGroup,
218
- ),
219
- ],
220
- };
221
- }