@stainless-api/docs 0.1.0-beta.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.
- package/.env.example +1 -0
- package/CHANGELOG.md +13 -0
- package/README.md +11 -0
- package/components/variables.css +139 -0
- package/eslint.config.js +10 -0
- package/package.json +74 -0
- package/plugin/assets/fonts/geist/OFL.txt +93 -0
- package/plugin/assets/fonts/geist/geist-italic-latin-ext.woff2 +0 -0
- package/plugin/assets/fonts/geist/geist-italic-latin.woff2 +0 -0
- package/plugin/assets/fonts/geist/geist-latin-ext.woff2 +0 -0
- package/plugin/assets/fonts/geist/geist-latin.woff2 +0 -0
- package/plugin/assets/fonts/geist/geist-mono-italic-latin-ext.woff2 +0 -0
- package/plugin/assets/fonts/geist/geist-mono-italic-latin.woff2 +0 -0
- package/plugin/assets/fonts/geist/geist-mono-latin-ext.woff2 +0 -0
- package/plugin/assets/fonts/geist/geist-mono-latin.woff2 +0 -0
- package/plugin/assets/languages/curl.svg +10 -0
- package/plugin/assets/languages/go.svg +4 -0
- package/plugin/assets/languages/java.svg +7 -0
- package/plugin/assets/languages/kotlin.svg +10 -0
- package/plugin/assets/languages/powershell.svg +3 -0
- package/plugin/assets/languages/python.svg +19 -0
- package/plugin/assets/languages/ruby.svg +125 -0
- package/plugin/assets/languages/terraform.svg +5 -0
- package/plugin/assets/languages/typescript.svg +11 -0
- package/plugin/assets/stainless-logo-dark.png +0 -0
- package/plugin/assets/stainless-logo.png +0 -0
- package/plugin/buildAlgoliaIndex.ts +72 -0
- package/plugin/cms/client.ts +62 -0
- package/plugin/cms/server.ts +268 -0
- package/plugin/cms/sidebar-builder.ts +420 -0
- package/plugin/cms/worker.ts +122 -0
- package/plugin/components/SDKSelect.astro +154 -0
- package/plugin/components/SnippetCode.tsx +212 -0
- package/plugin/components/search/Search.astro +6 -0
- package/plugin/components/search/SearchAlgolia.astro +87 -0
- package/plugin/components/search/SearchIsland.tsx +100 -0
- package/plugin/generateAPIReferenceLink.ts +71 -0
- package/plugin/globalJs/ai-dropdown.ts +57 -0
- package/plugin/globalJs/code-snippets.ts +87 -0
- package/plugin/globalJs/copy.ts +37 -0
- package/plugin/globalJs/navigation.ts +81 -0
- package/plugin/globalJs/tooltip.ts +32 -0
- package/plugin/helpers/getPageLoadEvent.ts +8 -0
- package/plugin/index.ts +308 -0
- package/plugin/languages.ts +67 -0
- package/plugin/loadPluginConfig.ts +273 -0
- package/plugin/middlewareBuilder/stainlessMiddleware.d.ts +5 -0
- package/plugin/middlewareBuilder/stlStarlightMiddleware.ts +5 -0
- package/plugin/react/Routing.tsx +435 -0
- package/plugin/referencePlaceholderUtils.ts +82 -0
- package/plugin/replaceSidebarPlaceholderMiddleware.ts +50 -0
- package/plugin/routes/Docs.astro +171 -0
- package/plugin/routes/DocsStatic.astro +14 -0
- package/plugin/routes/Overview.astro +67 -0
- package/plugin/routes/markdown.ts +58 -0
- package/plugin/vendor/preview.worker.docs.js +21657 -0
- package/plugin/vendor/templates/go.md +314 -0
- package/plugin/vendor/templates/java.md +87 -0
- package/plugin/vendor/templates/kotlin.md +87 -0
- package/plugin/vendor/templates/node.md +233 -0
- package/plugin/vendor/templates/python.md +249 -0
- package/plugin/vendor/templates/ruby.md +145 -0
- package/plugin/vendor/templates/terraform.md +60 -0
- package/plugin/vendor/templates/typescript.md +317 -0
- package/scripts/vendor_deps.ts +50 -0
- package/shared/virtualModule.ts +7 -0
- package/stl-docs/components/APIReferenceAIDropdown.tsx +86 -0
- package/stl-docs/components/ClientRouterHead.astro +41 -0
- package/stl-docs/components/Header.astro +91 -0
- package/stl-docs/components/Sidebar.astro +11 -0
- package/stl-docs/components/ThemeSelect.astro +225 -0
- package/stl-docs/components/content-panel/ContentBreadcrumbs.tsx +84 -0
- package/stl-docs/components/content-panel/ContentPanel.astro +72 -0
- package/stl-docs/components/content-panel/ProseAIDropdown.tsx +64 -0
- package/stl-docs/components/headers/DefaultHeader.astro +36 -0
- package/stl-docs/components/headers/HeaderLinks.astro +16 -0
- package/stl-docs/components/headers/SplashMobileMenuToggle.astro +49 -0
- package/stl-docs/components/headers/StackedHeader.astro +75 -0
- package/stl-docs/components/mintlify-compat/Accordion.astro +46 -0
- package/stl-docs/components/mintlify-compat/AccordionGroup.astro +25 -0
- package/stl-docs/components/mintlify-compat/Card.tsx +32 -0
- package/stl-docs/components/mintlify-compat/Columns.astro +66 -0
- package/stl-docs/components/mintlify-compat/Frame.astro +37 -0
- package/stl-docs/components/mintlify-compat/Step.astro +58 -0
- package/stl-docs/components/mintlify-compat/Steps.astro +17 -0
- package/stl-docs/components/mintlify-compat/Tab.astro +13 -0
- package/stl-docs/components/mintlify-compat/Tabs.astro +7 -0
- package/stl-docs/components/mintlify-compat/callouts/Callout.astro +7 -0
- package/stl-docs/components/mintlify-compat/callouts/Check.astro +7 -0
- package/stl-docs/components/mintlify-compat/callouts/Danger.astro +7 -0
- package/stl-docs/components/mintlify-compat/callouts/Info.astro +7 -0
- package/stl-docs/components/mintlify-compat/callouts/Note.astro +7 -0
- package/stl-docs/components/mintlify-compat/callouts/Tip.astro +7 -0
- package/stl-docs/components/mintlify-compat/callouts/Warning.astro +7 -0
- package/stl-docs/components/mintlify-compat/callouts/index.ts +9 -0
- package/stl-docs/components/mintlify-compat/card.css +44 -0
- package/stl-docs/components/mintlify-compat/index.ts +15 -0
- package/stl-docs/components/nav-tabs/NavDropdown.astro +106 -0
- package/stl-docs/components/nav-tabs/NavTabs.astro +165 -0
- package/stl-docs/components/nav-tabs/SecondaryNavTabs.astro +62 -0
- package/stl-docs/components/nav-tabs/buildNavLinks.ts +14 -0
- package/stl-docs/index.ts +174 -0
- package/stl-docs/loadStlDocsConfig.ts +160 -0
- package/stl-docs/redirects.ts +33 -0
- package/stl-docs/tabsMiddleware.ts +183 -0
- package/styles/code.css +189 -0
- package/styles/fonts.css +68 -0
- package/styles/links.css +51 -0
- package/styles/mintlify-compat.css +1 -0
- package/styles/overrides.css +79 -0
- package/styles/page.css +76 -0
- package/styles/sdk_select.css +11 -0
- package/styles/search.css +85 -0
- package/styles/sidebar.css +168 -0
- package/styles/toc.css +42 -0
- package/styles/variables.css +18 -0
- package/theme.css +15 -0
- package/tsconfig.json +18 -0
- package/virtual-module.d.ts +43 -0
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { marked } from 'marked';
|
|
3
|
+
import hljs from 'highlight.js';
|
|
4
|
+
import { createMarkdownProcessor, type MarkdownProcessor } from '@astrojs/markdown-remark';
|
|
5
|
+
import remarkGfmAlerts from 'remark-github-alerts';
|
|
6
|
+
|
|
7
|
+
import type { MarkdownHeading } from 'astro';
|
|
8
|
+
import type { StarlightRouteData } from '@astrojs/starlight/route-data';
|
|
9
|
+
import type * as SDKJSON from '~/lib/json-spec-v2/types';
|
|
10
|
+
import type { DocsLanguage } from '@stainless-api/docs-ui/src/routing';
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
generateRouteList,
|
|
14
|
+
generateRoute,
|
|
15
|
+
parseStainlessPath,
|
|
16
|
+
walkTree,
|
|
17
|
+
SupportedLanguageSyntaxes,
|
|
18
|
+
getLanguageSnippet,
|
|
19
|
+
} from '@stainless-api/docs-ui/src/routing';
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
DocsProvider,
|
|
23
|
+
MarkdownProvider,
|
|
24
|
+
NavigationProvider,
|
|
25
|
+
useSpec,
|
|
26
|
+
type ContentPanelLayout,
|
|
27
|
+
} from '@stainless-api/docs-ui/src/contexts';
|
|
28
|
+
|
|
29
|
+
import { flatResources, getResourceFromSpec } from '@stainless-api/docs-ui/src/utils';
|
|
30
|
+
|
|
31
|
+
import {
|
|
32
|
+
SDKMethod,
|
|
33
|
+
SDKResource,
|
|
34
|
+
type SDKRequestTitleProps,
|
|
35
|
+
SDKBreadcrumbs,
|
|
36
|
+
Dropdown,
|
|
37
|
+
DropdownTrigger,
|
|
38
|
+
DropdownMenu,
|
|
39
|
+
DropdownItem,
|
|
40
|
+
SDKIcon,
|
|
41
|
+
SDKOverview,
|
|
42
|
+
SDKLanguageBlock,
|
|
43
|
+
} from '@stainless-api/docs-ui/src/components';
|
|
44
|
+
import {
|
|
45
|
+
BASE_PATH,
|
|
46
|
+
EXCLUDE_LANGUAGES,
|
|
47
|
+
EXPAND_RESOURCES,
|
|
48
|
+
HIGHLIGHT_THEMES,
|
|
49
|
+
BREADCRUMB_CONFIG,
|
|
50
|
+
PROPERTY_SETTINGS,
|
|
51
|
+
} from 'virtual:stl-starlight-virtual-module';
|
|
52
|
+
import style from '@stainless-api/docs-ui/src/style';
|
|
53
|
+
import { createHighlighter, type BundledLanguage, type BundledTheme, type HighlighterGeneric } from 'shiki';
|
|
54
|
+
import { SnippetCode, SnippetContainer, SnippetRequestContainer } from '../components/SnippetCode';
|
|
55
|
+
import clsx from 'clsx';
|
|
56
|
+
import type { StlStarlightMiddleware } from '../middlewareBuilder/stainlessMiddleware';
|
|
57
|
+
import { ComponentProvider } from '@stainless-api/docs-ui/src/contexts/component';
|
|
58
|
+
import { APIReferenceAIDropdown } from '../../stl-docs/components/APIReferenceAIDropdown';
|
|
59
|
+
|
|
60
|
+
export function generateDocsRoutes(spec: SDKJSON.Spec) {
|
|
61
|
+
const paths = generateRouteList({
|
|
62
|
+
spec,
|
|
63
|
+
excludeLanguages: EXCLUDE_LANGUAGES as DocsLanguage[],
|
|
64
|
+
});
|
|
65
|
+
const readmes = Object.entries(spec.readme)
|
|
66
|
+
.filter(([language]) => language !== 'http')
|
|
67
|
+
.filter(([language]) => !EXCLUDE_LANGUAGES.includes(language as DocsLanguage))
|
|
68
|
+
.map(([language]) => ({
|
|
69
|
+
slug: language,
|
|
70
|
+
stainlessPath: null,
|
|
71
|
+
language: language as DocsLanguage,
|
|
72
|
+
title: 'Readme',
|
|
73
|
+
kind: 'readme',
|
|
74
|
+
}));
|
|
75
|
+
|
|
76
|
+
return [...paths, ...readmes].map(({ slug, stainlessPath, language, title, kind }) => {
|
|
77
|
+
return {
|
|
78
|
+
params: { slug },
|
|
79
|
+
props: { stainlessPath, language, title, kind },
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function isResourceNonEmpty(resource: SDKJSON.Resource) {
|
|
85
|
+
return (
|
|
86
|
+
Object.keys(resource.methods ?? {}).length > 0 ||
|
|
87
|
+
Object.keys(resource.subresources ?? {}).length > 0 ||
|
|
88
|
+
Object.keys(resource.models ?? {}).length > 0
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
type SidebarEntry = StarlightRouteData['sidebar'][number];
|
|
93
|
+
|
|
94
|
+
export function buildSidebar(
|
|
95
|
+
basePath: string,
|
|
96
|
+
language: DocsLanguage,
|
|
97
|
+
current: string,
|
|
98
|
+
spec: SDKJSON.Spec,
|
|
99
|
+
resources?: Record<string, SDKJSON.Resource>,
|
|
100
|
+
nested?: boolean,
|
|
101
|
+
): StarlightRouteData['sidebar'] {
|
|
102
|
+
const totalRoutes = Array.from(walkTree(spec, false)).filter(
|
|
103
|
+
(item) => item.data.kind === 'http_method',
|
|
104
|
+
).length;
|
|
105
|
+
|
|
106
|
+
return Object.values(resources ?? spec.resources ?? [])
|
|
107
|
+
.filter((resource) => isResourceNonEmpty(resource))
|
|
108
|
+
.sort((a, b) => (a.name.startsWith('$') === b.name.startsWith('$') ? 0 : a.name.startsWith('$') ? 1 : -1))
|
|
109
|
+
.map((resource) => {
|
|
110
|
+
const subs = buildSidebar(basePath, language, current, spec, resource.subresources, true);
|
|
111
|
+
|
|
112
|
+
const overview: SidebarEntry = {
|
|
113
|
+
type: 'link',
|
|
114
|
+
isCurrent: current === resource.stainlessPath,
|
|
115
|
+
attrs: { 'data-stldocs-overview': resource.name },
|
|
116
|
+
label: 'Overview',
|
|
117
|
+
href: generateRoute(basePath, language, resource.stainlessPath) ?? basePath,
|
|
118
|
+
badge: undefined,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const meths: SidebarEntry[] = Object.values(resource.methods ?? [])
|
|
122
|
+
.filter((method) => spec.decls?.[language]?.[method.stainlessPath])
|
|
123
|
+
.toSorted((first, second) => first.name.localeCompare(second.name))
|
|
124
|
+
.map((method) => ({
|
|
125
|
+
type: 'link',
|
|
126
|
+
isCurrent: current === method.stainlessPath,
|
|
127
|
+
attrs: { 'data-stldocs-method': method.httpMethod },
|
|
128
|
+
label: method.summary ?? method.name,
|
|
129
|
+
href: generateRoute(basePath, language, method.stainlessPath) ?? basePath,
|
|
130
|
+
badge: undefined,
|
|
131
|
+
}));
|
|
132
|
+
|
|
133
|
+
const shouldExpand = EXPAND_RESOURCES ?? totalRoutes < 20;
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
type: 'group',
|
|
137
|
+
label: resource.title,
|
|
138
|
+
badge: undefined,
|
|
139
|
+
collapsed: !shouldExpand || nested === true,
|
|
140
|
+
entries: [...(resources ? [] : [overview]), ...meths, ...subs],
|
|
141
|
+
};
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function buildPageNavigation(resource: SDKJSON.Resource, depth: number = 2): MarkdownHeading[] {
|
|
146
|
+
const output: MarkdownHeading[] = [{ depth, slug: resource.stainlessPath, text: resource.title }];
|
|
147
|
+
|
|
148
|
+
const subs = Object.values(resource.subresources ?? {}).flatMap((sub) =>
|
|
149
|
+
buildPageNavigation(sub, depth + 1),
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
return [...output, ...subs];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function renderMarkdown(content: string) {
|
|
156
|
+
return marked.parse(content, { gfm: true }) as string;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function highlight(content: string, language?: string) {
|
|
160
|
+
if (language === 'json') return hljs.highlight(content, { language }).value;
|
|
161
|
+
const highlighter = await astroHighlight();
|
|
162
|
+
return highlighter.codeToHtml(content, {
|
|
163
|
+
lang: language ?? 'javascript',
|
|
164
|
+
themes: HIGHLIGHT_THEMES || {},
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function SDKSelectReactComponent({
|
|
169
|
+
selected,
|
|
170
|
+
languages,
|
|
171
|
+
id,
|
|
172
|
+
className,
|
|
173
|
+
}: {
|
|
174
|
+
selected: DocsLanguage;
|
|
175
|
+
languages: DocsLanguage[];
|
|
176
|
+
id: string;
|
|
177
|
+
className?: string;
|
|
178
|
+
}) {
|
|
179
|
+
return (
|
|
180
|
+
<Dropdown id={id} data-current-value={selected} className={className}>
|
|
181
|
+
<DropdownTrigger
|
|
182
|
+
className="dropdown-toggle stldocs-button-tertiary"
|
|
183
|
+
type="button"
|
|
184
|
+
id="stldocs-snippet-title-button"
|
|
185
|
+
aria-expanded="false"
|
|
186
|
+
withChevron
|
|
187
|
+
>
|
|
188
|
+
<SDKIcon language={getLanguageSnippet(selected)} size={16} />
|
|
189
|
+
<span className={clsx('stl-snippet-dropdown-button-text', selected)}>{selected}</span>
|
|
190
|
+
</DropdownTrigger>
|
|
191
|
+
<DropdownMenu
|
|
192
|
+
className="dropdown-menu stl-sdk-select-dropdown-menu"
|
|
193
|
+
position="below"
|
|
194
|
+
aria-labelledby="stldocs-snippet-title-button"
|
|
195
|
+
>
|
|
196
|
+
{languages.map((item) => (
|
|
197
|
+
<DropdownItem key={item} value={item} selected={item === selected}>
|
|
198
|
+
<div>
|
|
199
|
+
<SDKIcon language={getLanguageSnippet(item)} size={16} />
|
|
200
|
+
<span className={clsx('stl-snippet-dropdown-button-text', item)}>{item}</span>
|
|
201
|
+
</div>
|
|
202
|
+
</DropdownItem>
|
|
203
|
+
))}
|
|
204
|
+
</DropdownMenu>
|
|
205
|
+
</Dropdown>
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function SDKRequestTitle({ snippetLanguage }: SDKRequestTitleProps) {
|
|
210
|
+
const spec = useSpec();
|
|
211
|
+
|
|
212
|
+
const selected = snippetLanguage.split('.').at(0) as DocsLanguage;
|
|
213
|
+
const languages = (spec?.docs?.languages ?? ['http']).filter((lang) => !EXCLUDE_LANGUAGES.includes(lang));
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<SDKSelectReactComponent
|
|
217
|
+
selected={selected || 'http'}
|
|
218
|
+
languages={languages}
|
|
219
|
+
id="stldocs-snippet-select"
|
|
220
|
+
className="stl-sdk-select"
|
|
221
|
+
/>
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export type SpecMetadata = [
|
|
226
|
+
'http' | 'node' | 'python' | 'go' | 'typescript' | 'terraform' | 'ruby' | 'java' | 'kotlin',
|
|
227
|
+
{
|
|
228
|
+
repo_url?: string;
|
|
229
|
+
code_url?: string;
|
|
230
|
+
package_title?: string;
|
|
231
|
+
version?: string;
|
|
232
|
+
install?: string;
|
|
233
|
+
},
|
|
234
|
+
][];
|
|
235
|
+
|
|
236
|
+
export function RenderLibraries({ metadata }: { metadata: SpecMetadata }) {
|
|
237
|
+
return (
|
|
238
|
+
<ComponentProvider components={{}}>
|
|
239
|
+
{metadata.map(([language, data]) => (
|
|
240
|
+
<SDKLanguageBlock
|
|
241
|
+
language={language}
|
|
242
|
+
version={data.version || ''}
|
|
243
|
+
install={data.install || ''}
|
|
244
|
+
links={{ repo: data.repo_url || '#', docs: `${BASE_PATH}/${language}` }}
|
|
245
|
+
/>
|
|
246
|
+
))}
|
|
247
|
+
</ComponentProvider>
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export function RenderSpecOverview({ spec, language }: { spec: SDKJSON.Spec; language: DocsLanguage }) {
|
|
252
|
+
const resources = React.useMemo(() => flatResources(spec.resources, []), [spec]);
|
|
253
|
+
|
|
254
|
+
return (
|
|
255
|
+
<DocsProvider spec={spec} language={language ?? 'node'}>
|
|
256
|
+
<ComponentProvider
|
|
257
|
+
components={{
|
|
258
|
+
SDKRequestTitle,
|
|
259
|
+
SnippetCode,
|
|
260
|
+
SnippetContainer,
|
|
261
|
+
SnippetRequestContainer,
|
|
262
|
+
}}
|
|
263
|
+
>
|
|
264
|
+
<NavigationProvider basePath={BASE_PATH}>
|
|
265
|
+
<MarkdownProvider render={renderMarkdown} highlight={highlight}>
|
|
266
|
+
<div className={style.Overview}>
|
|
267
|
+
{resources
|
|
268
|
+
.filter(({ resource }) => !resource.name.startsWith('$'))
|
|
269
|
+
.map(({ resource, parents }) => (
|
|
270
|
+
<SDKResource
|
|
271
|
+
key={resource.stainlessPath}
|
|
272
|
+
resource={resource}
|
|
273
|
+
parents={parents}
|
|
274
|
+
showModels={false}
|
|
275
|
+
/>
|
|
276
|
+
))}
|
|
277
|
+
</div>
|
|
278
|
+
</MarkdownProvider>
|
|
279
|
+
</NavigationProvider>
|
|
280
|
+
</ComponentProvider>
|
|
281
|
+
</DocsProvider>
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export function RenderSpec({
|
|
286
|
+
spec,
|
|
287
|
+
kind,
|
|
288
|
+
path,
|
|
289
|
+
language,
|
|
290
|
+
currentPath,
|
|
291
|
+
contentPanelLayout = 'double-pane',
|
|
292
|
+
transformRequestSnippet,
|
|
293
|
+
}: {
|
|
294
|
+
spec: SDKJSON.Spec;
|
|
295
|
+
kind: string;
|
|
296
|
+
path: string;
|
|
297
|
+
language: DocsLanguage;
|
|
298
|
+
currentPath: string;
|
|
299
|
+
contentPanelLayout?: ContentPanelLayout;
|
|
300
|
+
transformRequestSnippet?: StlStarlightMiddleware['transformRequestSnippet'];
|
|
301
|
+
}) {
|
|
302
|
+
const parsed = parseStainlessPath(path);
|
|
303
|
+
const resource = getResourceFromSpec(path, spec);
|
|
304
|
+
|
|
305
|
+
if (!resource || !parsed) return null;
|
|
306
|
+
|
|
307
|
+
return (
|
|
308
|
+
<DocsProvider
|
|
309
|
+
spec={spec as SDKJSON.Spec}
|
|
310
|
+
language={language ?? 'node'}
|
|
311
|
+
settings={{
|
|
312
|
+
contentPanelLayout,
|
|
313
|
+
properties: PROPERTY_SETTINGS,
|
|
314
|
+
}}
|
|
315
|
+
>
|
|
316
|
+
<ComponentProvider
|
|
317
|
+
components={{
|
|
318
|
+
SDKRequestTitle,
|
|
319
|
+
SnippetCode,
|
|
320
|
+
SnippetContainer,
|
|
321
|
+
SnippetRequestContainer,
|
|
322
|
+
}}
|
|
323
|
+
>
|
|
324
|
+
<NavigationProvider basePath={BASE_PATH} selectedPath={path}>
|
|
325
|
+
<MarkdownProvider render={renderMarkdown} highlight={highlight}>
|
|
326
|
+
{kind === 'http_method' ? (
|
|
327
|
+
<div className="stldocs-root stl-ui-not-prose">
|
|
328
|
+
<div className="stl-page-nav-container">
|
|
329
|
+
<SDKBreadcrumbs
|
|
330
|
+
spec={spec as SDKJSON.Spec}
|
|
331
|
+
currentPath={currentPath}
|
|
332
|
+
basePath={BASE_PATH}
|
|
333
|
+
config={BREADCRUMB_CONFIG}
|
|
334
|
+
/>
|
|
335
|
+
<APIReferenceAIDropdown />
|
|
336
|
+
</div>
|
|
337
|
+
<SDKMethod
|
|
338
|
+
method={resource.methods[parsed.method]}
|
|
339
|
+
transformRequestSnippet={transformRequestSnippet}
|
|
340
|
+
/>
|
|
341
|
+
</div>
|
|
342
|
+
) : (
|
|
343
|
+
<div className="stldocs-root stl-ui-not-prose">
|
|
344
|
+
<div className="stl-page-nav-container">
|
|
345
|
+
<SDKBreadcrumbs
|
|
346
|
+
spec={spec as SDKJSON.Spec}
|
|
347
|
+
currentPath={currentPath}
|
|
348
|
+
basePath={BASE_PATH}
|
|
349
|
+
config={BREADCRUMB_CONFIG}
|
|
350
|
+
/>
|
|
351
|
+
<APIReferenceAIDropdown />
|
|
352
|
+
</div>
|
|
353
|
+
<SDKOverview resource={resource} />
|
|
354
|
+
</div>
|
|
355
|
+
)}
|
|
356
|
+
</MarkdownProvider>
|
|
357
|
+
</NavigationProvider>
|
|
358
|
+
</ComponentProvider>
|
|
359
|
+
</DocsProvider>
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export function RenderMethod({ path }: { path: string }) {
|
|
364
|
+
const spec = useSpec();
|
|
365
|
+
if (!spec) return null;
|
|
366
|
+
|
|
367
|
+
const parsed = parseStainlessPath(path);
|
|
368
|
+
const resource = getResourceFromSpec(path, spec);
|
|
369
|
+
if (!resource || !parsed) return null;
|
|
370
|
+
|
|
371
|
+
const meth = resource.methods[parsed.method];
|
|
372
|
+
return <SDKMethod method={meth} />;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export async function getReadmeContent(spec: SDKJSON.Spec, language: DocsLanguage) {
|
|
376
|
+
const repoUrl = spec.metadata?.[language]?.repo_url;
|
|
377
|
+
|
|
378
|
+
try {
|
|
379
|
+
if (repoUrl) {
|
|
380
|
+
const rawUrl = repoUrl.replace('www.github.com', 'raw.githubusercontent.com');
|
|
381
|
+
|
|
382
|
+
const response = await fetch(`${rawUrl}/refs/heads/main/README.md`);
|
|
383
|
+
if (response && response.ok) return response.text();
|
|
384
|
+
}
|
|
385
|
+
} catch {
|
|
386
|
+
// ignore
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return spec.readme[language];
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Astro's markdown processor is a singleton
|
|
393
|
+
// Need to cache it instead of instanting per request
|
|
394
|
+
let astroMarkdownProcessor: MarkdownProcessor;
|
|
395
|
+
async function astroMarkdown() {
|
|
396
|
+
if (!astroMarkdownProcessor) {
|
|
397
|
+
astroMarkdownProcessor = await createMarkdownProcessor({
|
|
398
|
+
gfm: true,
|
|
399
|
+
remarkPlugins: [remarkGfmAlerts],
|
|
400
|
+
shikiConfig: {
|
|
401
|
+
themes: HIGHLIGHT_THEMES,
|
|
402
|
+
},
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return astroMarkdownProcessor;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
let astroShikiHighlighter: HighlighterGeneric<BundledLanguage, BundledTheme>;
|
|
410
|
+
async function astroHighlight() {
|
|
411
|
+
if (!astroShikiHighlighter) {
|
|
412
|
+
astroShikiHighlighter = await createHighlighter({
|
|
413
|
+
themes: ['github-light', 'github-dark'],
|
|
414
|
+
langs: SupportedLanguageSyntaxes,
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return astroShikiHighlighter;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export async function astroMarkdownRender(content: string) {
|
|
422
|
+
const md = await astroMarkdown();
|
|
423
|
+
const output = await md.render(content);
|
|
424
|
+
|
|
425
|
+
// Map GFM callouts to the closest Starlight equivalent
|
|
426
|
+
output.code = output.code
|
|
427
|
+
.replaceAll('markdown-alert-caution', 'markdown-alert-danger')
|
|
428
|
+
.replaceAll('markdown-alert-warning', 'markdown-alert-caution')
|
|
429
|
+
.replaceAll('markdown-alert-important', 'markdown-alert-caution')
|
|
430
|
+
.replaceAll('markdown-alert-title', 'starlight-aside__title')
|
|
431
|
+
.replaceAll('markdown-alert-', 'starlight-aside--')
|
|
432
|
+
.replaceAll('markdown-alert', 'starlight-aside');
|
|
433
|
+
|
|
434
|
+
return output;
|
|
435
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { StarlightRouteData } from '@astrojs/starlight/route-data';
|
|
2
|
+
import type starlight from '@astrojs/starlight';
|
|
3
|
+
|
|
4
|
+
// file is required because of build errors when importing from index.ts in the replaceSidebarPlaceholderMiddleware.ts file
|
|
5
|
+
const INTERNAL_REFERENCE_ENTRY_MARKER = 'STL_STARLIGHT_API_REFERENCE_ITEMS_PLACEHOLDER';
|
|
6
|
+
|
|
7
|
+
export function makePlaceholderItems(id: number) {
|
|
8
|
+
return [
|
|
9
|
+
{
|
|
10
|
+
label: 'API Reference',
|
|
11
|
+
link: '/', // this gets replaced during plugin initialization
|
|
12
|
+
attrs: {
|
|
13
|
+
// we hack-ily use/abuse the about attribute to identify the placeholder item
|
|
14
|
+
// using "about" was sort of a random choice, but it seems good enough
|
|
15
|
+
about: INTERNAL_REFERENCE_ENTRY_MARKER,
|
|
16
|
+
'data-stldocs-sidebar-id': id,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type StarlightConfig = Parameters<typeof starlight>[0];
|
|
23
|
+
|
|
24
|
+
type SidebarConfigEntry = Exclude<StarlightConfig['sidebar'], undefined>[number];
|
|
25
|
+
|
|
26
|
+
export function getAPIReferencePlaceholderItemFromSidebarConfig(
|
|
27
|
+
sidebar: SidebarConfigEntry[],
|
|
28
|
+
): SidebarConfigEntry | null {
|
|
29
|
+
for (const item of sidebar) {
|
|
30
|
+
if (typeof item === 'string') {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if ('items' in item) {
|
|
34
|
+
const found = getAPIReferencePlaceholderItemFromSidebarConfig(item.items);
|
|
35
|
+
if (found) {
|
|
36
|
+
return found;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if ('attrs' in item && item.attrs?.about === INTERNAL_REFERENCE_ENTRY_MARKER) {
|
|
40
|
+
return item;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
type SidebarEntry = StarlightRouteData['sidebar'][number];
|
|
48
|
+
|
|
49
|
+
type PlaceholderItemResult = {
|
|
50
|
+
index: number;
|
|
51
|
+
group: SidebarEntry[];
|
|
52
|
+
placeholder: SidebarEntry;
|
|
53
|
+
sidebarId: number;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
function recursiveGetPlaceholderItems(
|
|
57
|
+
sidebar: SidebarEntry[],
|
|
58
|
+
items: PlaceholderItemResult[],
|
|
59
|
+
): PlaceholderItemResult[] {
|
|
60
|
+
for (let i = 0; i < sidebar.length; i++) {
|
|
61
|
+
const entry = sidebar[i];
|
|
62
|
+
if ('attrs' in entry && entry.attrs?.about === INTERNAL_REFERENCE_ENTRY_MARKER) {
|
|
63
|
+
items.push({
|
|
64
|
+
index: i,
|
|
65
|
+
group: sidebar,
|
|
66
|
+
placeholder: entry,
|
|
67
|
+
sidebarId: entry.attrs?.['data-stldocs-sidebar-id'],
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
if (entry.type === 'group') {
|
|
71
|
+
const foundEntries = getAPIReferencePlaceholderItems(entry.entries);
|
|
72
|
+
if (foundEntries) {
|
|
73
|
+
items.push(...foundEntries);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return items;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function getAPIReferencePlaceholderItems(sidebar: SidebarEntry[]): PlaceholderItemResult[] {
|
|
81
|
+
return recursiveGetPlaceholderItems(sidebar, []);
|
|
82
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { defineRouteMiddleware } from '@astrojs/starlight/route-data';
|
|
2
|
+
|
|
3
|
+
import { cmsClient } from './cms/client';
|
|
4
|
+
import { BASE_PATH } from 'virtual:stl-starlight-virtual-module';
|
|
5
|
+
import { getAPIReferencePlaceholderItems } from './referencePlaceholderUtils';
|
|
6
|
+
import { getMethodFromSDKJSON, recursiveReplacePlaceholderItems } from './generateAPIReferenceLink';
|
|
7
|
+
import { forceGenerateRoute } from './cms/sidebar-builder';
|
|
8
|
+
import { parseRoute } from '@stainless-api/docs-ui/src/routing';
|
|
9
|
+
|
|
10
|
+
// this fn is loaded in the plugin via addRouteMiddleware
|
|
11
|
+
|
|
12
|
+
export const onRequest = defineRouteMiddleware(async (context) => {
|
|
13
|
+
// if using content collection schema, use: context.locals.starlightRoute.entry.data.stainlessStarlight
|
|
14
|
+
// this worked without collections but relied on hijacking starlightRoute: context.props.frontmatter.stainlessStarlight
|
|
15
|
+
|
|
16
|
+
const slug = `/${context.locals.starlightRoute.id}`; // same as .slug but not deprecated
|
|
17
|
+
|
|
18
|
+
const apiReferencePlaceholderItems = getAPIReferencePlaceholderItems(context.locals.starlightRoute.sidebar);
|
|
19
|
+
|
|
20
|
+
const spec = await cmsClient.getSpec();
|
|
21
|
+
|
|
22
|
+
const { language, stainlessPath } = parseRoute(BASE_PATH, slug);
|
|
23
|
+
|
|
24
|
+
// This is probably temporary, but it fills in functionality needed for Mintlify imports
|
|
25
|
+
recursiveReplacePlaceholderItems(context.locals.starlightRoute.sidebar, (entry, { endpoint, label }) => {
|
|
26
|
+
const method = getMethodFromSDKJSON(spec, endpoint);
|
|
27
|
+
|
|
28
|
+
const route = forceGenerateRoute({
|
|
29
|
+
basePath: BASE_PATH,
|
|
30
|
+
stainlessPath: method.stainlessPath,
|
|
31
|
+
language,
|
|
32
|
+
});
|
|
33
|
+
entry.href = route;
|
|
34
|
+
entry.isCurrent = method.stainlessPath === stainlessPath;
|
|
35
|
+
if (!label) {
|
|
36
|
+
entry.label = method.summary ?? method.name;
|
|
37
|
+
}
|
|
38
|
+
entry.attrs['data-stldocs-method'] = method.httpMethod;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
for (const item of apiReferencePlaceholderItems) {
|
|
42
|
+
const entries = await cmsClient.buildSidebar({
|
|
43
|
+
basePath: BASE_PATH,
|
|
44
|
+
currentSlug: slug,
|
|
45
|
+
sidebarId: item.sidebarId,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
item.group.splice(item.index, 1, ...entries);
|
|
49
|
+
}
|
|
50
|
+
});
|