@stainless-api/docs 0.1.0-beta.13 → 0.1.0-beta.130
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1102 -0
- package/ambient.d.ts +6 -0
- package/eslint-suppressions.json +90 -0
- package/{eslint.config.js → eslint.config.ts} +0 -2
- package/locals.d.ts +17 -0
- package/package.json +62 -44
- package/playground-virtual-modules.d.ts +96 -0
- package/plugin/assets/languages/cli.svg +14 -0
- package/plugin/assets/languages/csharp.svg +1 -0
- package/plugin/assets/languages/php.svg +4 -0
- package/plugin/buildAlgoliaIndex.ts +40 -39
- package/plugin/components/MethodDescription.tsx +54 -0
- package/plugin/components/RequestBuilder/ParamEditor.tsx +55 -0
- package/plugin/components/RequestBuilder/SnippetStainlessIsland.tsx +107 -0
- package/plugin/components/RequestBuilder/index.tsx +40 -0
- package/plugin/components/RequestBuilder/props.ts +9 -0
- package/plugin/components/RequestBuilder/spec-helpers.ts +47 -0
- package/plugin/components/RequestBuilder/styles.css +67 -0
- package/plugin/components/SDKSelect.astro +18 -111
- package/plugin/components/SnippetCode.tsx +112 -70
- package/plugin/components/StainlessIslands.tsx +126 -0
- package/plugin/components/search/SearchAlgolia.astro +46 -29
- package/plugin/components/search/SearchIsland.tsx +61 -37
- package/plugin/generateAPIReferenceLink.ts +0 -40
- package/plugin/globalJs/ai-dropdown-options.ts +248 -0
- package/plugin/globalJs/code-snippets.ts +45 -16
- package/plugin/globalJs/copy.ts +115 -27
- package/plugin/globalJs/create-playground.shim.ts +3 -0
- package/plugin/globalJs/method-descriptions.ts +33 -0
- package/plugin/globalJs/navigation.ts +24 -44
- package/plugin/globalJs/playground-data.shim.ts +1 -0
- package/plugin/globalJs/playground-data.ts +14 -0
- package/plugin/globalJs/summary-selection-tweak.ts +29 -0
- package/plugin/helpers/generateDocsRoutes.ts +59 -0
- package/plugin/helpers/multiSpec.ts +8 -0
- package/plugin/index.ts +317 -141
- package/plugin/languages.ts +8 -2
- package/plugin/loadPluginConfig.ts +284 -109
- package/plugin/markdown/highlighter.ts +100 -0
- package/plugin/markdown/index.ts +39 -0
- package/plugin/middlewareBuilder/stainlessMiddleware.d.ts +3 -1
- package/plugin/react/Routing.tsx +98 -263
- package/plugin/referencePlaceholderUtils.ts +17 -14
- package/plugin/replaceSidebarPlaceholderMiddleware.ts +39 -35
- package/plugin/routes/Docs.astro +72 -111
- package/plugin/routes/DocsStatic.astro +6 -5
- package/plugin/routes/Overview.astro +46 -22
- package/plugin/routes/llms.ts +186 -0
- package/plugin/routes/markdown.ts +13 -12
- package/plugin/{cms → sidebar-utils}/sidebar-builder.ts +84 -69
- package/plugin/specs/FileCache.ts +99 -0
- package/plugin/specs/fetchSpecSSR.ts +27 -0
- package/plugin/specs/generateSpec.ts +112 -0
- package/plugin/specs/index.ts +132 -0
- package/plugin/specs/inputResolver.ts +148 -0
- package/plugin/{cms → specs}/worker.ts +82 -5
- package/plugin/vendor/preview.worker.docs.js +27121 -16890
- package/plugin/vendor/templates/cli.md +1 -0
- package/plugin/vendor/templates/go.md +4 -2
- package/plugin/vendor/templates/java.md +5 -1
- package/plugin/vendor/templates/kotlin.md +5 -1
- package/plugin/vendor/templates/node.md +4 -2
- package/plugin/vendor/templates/python.md +4 -2
- package/plugin/vendor/templates/ruby.md +4 -2
- package/plugin/vendor/templates/terraform.md +1 -1
- package/plugin/vendor/templates/typescript.md +3 -1
- package/resolveSrcFile.ts +10 -0
- package/scripts/vendor_deps.ts +5 -5
- package/shared/conditionalIntegration.ts +28 -0
- package/shared/getProsePages.ts +41 -0
- package/shared/getSharedLogger.ts +15 -0
- package/shared/terminalUtils.ts +3 -0
- package/shared/virtualModule.ts +46 -1
- package/src/content.config.ts +9 -0
- package/stl-docs/aiChatExamples.ts +95 -0
- package/stl-docs/chat/docs-chat-handler.ts +18 -0
- package/stl-docs/chat/hook.ts +215 -0
- package/stl-docs/chat/schemas.ts +70 -0
- package/stl-docs/chat/stainless-handler/index.ts +126 -0
- package/stl-docs/chat/stream-util.ts +16 -0
- package/stl-docs/chat/ui/AiChat.module.css +591 -0
- package/stl-docs/chat/ui/AiChat.tsx +188 -0
- package/stl-docs/chat/ui/Trigger.tsx +154 -0
- package/stl-docs/chat/ui/components/ChatControls.tsx +51 -0
- package/stl-docs/chat/ui/components/ChatEmpty.tsx +42 -0
- package/stl-docs/chat/ui/components/ChatLog.tsx +96 -0
- package/stl-docs/chat/ui/components/ChatMessage.tsx +47 -0
- package/stl-docs/chat/ui/components/CodeBlock.tsx +33 -0
- package/stl-docs/chat/ui/components/MessageFeedback.tsx +109 -0
- package/stl-docs/chat/ui/components/Table.tsx +15 -0
- package/stl-docs/chat/ui/components/ToolCall.tsx +34 -0
- package/stl-docs/chat/ui/components/hljs-github.css +81 -0
- package/stl-docs/chat/ui/scroll-manager.ts +86 -0
- package/stl-docs/chat/ui/types.ts +45 -0
- package/stl-docs/components/AIDropdown.tsx +63 -0
- package/stl-docs/components/AiChatIsland.tsx +16 -0
- package/stl-docs/components/{content-panel/ContentBreadcrumbs.tsx → ContentBreadcrumbs.tsx} +2 -2
- package/stl-docs/components/ContentPanel.astro +9 -0
- package/stl-docs/components/Footer.astro +89 -0
- package/stl-docs/components/Head.astro +20 -0
- package/stl-docs/components/Header.astro +3 -9
- package/stl-docs/components/PageFrame.astro +37 -0
- package/stl-docs/components/PageSidebar.astro +11 -0
- package/stl-docs/components/PageTitle.astro +82 -0
- package/stl-docs/components/StainlessLogo.svg +4 -0
- package/stl-docs/components/ThemeProvider.astro +36 -0
- package/stl-docs/components/ThemeSelect.astro +84 -146
- package/stl-docs/components/TwoColumnContent.astro +2 -0
- package/stl-docs/components/headers/DefaultHeader.astro +6 -8
- package/stl-docs/components/headers/StackedHeader.astro +10 -53
- package/stl-docs/components/icons/chat-gpt.tsx +2 -2
- package/stl-docs/components/icons/cursor.tsx +10 -0
- package/stl-docs/components/icons/gemini.tsx +19 -0
- package/stl-docs/components/icons/markdown.tsx +1 -1
- package/stl-docs/components/index.ts +1 -0
- package/stl-docs/components/mintlify-compat/Accordion.astro +2 -2
- package/stl-docs/components/mintlify-compat/AccordionGroup.astro +0 -4
- package/stl-docs/components/mintlify-compat/Columns.astro +2 -2
- package/stl-docs/components/mintlify-compat/Frame.astro +6 -6
- package/stl-docs/components/mintlify-compat/Tab.astro +2 -2
- package/stl-docs/components/mintlify-compat/callouts/Callout.astro +2 -2
- package/stl-docs/components/mintlify-compat/callouts/Check.astro +0 -4
- package/stl-docs/components/mintlify-compat/callouts/Danger.astro +0 -4
- package/stl-docs/components/mintlify-compat/callouts/Info.astro +0 -4
- package/stl-docs/components/mintlify-compat/callouts/Note.astro +0 -4
- package/stl-docs/components/mintlify-compat/callouts/Tip.astro +0 -4
- package/stl-docs/components/mintlify-compat/callouts/Warning.astro +0 -4
- package/stl-docs/components/mintlify-compat/card.css +4 -4
- package/stl-docs/components/mintlify-compat/index.ts +2 -4
- package/stl-docs/components/nav-tabs/NavDropdown.astro +38 -77
- package/stl-docs/components/nav-tabs/NavTabs.astro +81 -81
- package/stl-docs/components/nav-tabs/SecondaryNavTabs.astro +1 -2
- package/stl-docs/components/nav-tabs/buildNavLinks.ts +5 -2
- package/stl-docs/components/pagination/HomeLink.astro +10 -0
- package/stl-docs/components/pagination/Pagination.astro +177 -0
- package/stl-docs/components/pagination/PaginationLinkEmphasized.astro +22 -0
- package/stl-docs/components/pagination/PaginationLinkQuiet.astro +13 -0
- package/stl-docs/components/pagination/util.ts +71 -0
- package/stl-docs/components/scripts.ts +1 -0
- package/stl-docs/components/sidebars/BaseSidebar.astro +80 -2
- package/stl-docs/components/sidebars/SidebarWithComponents.tsx +10 -0
- package/stl-docs/components/sidebars/convertAstroSidebarToStl.tsx +62 -0
- package/stl-docs/disableCalloutSyntax.ts +36 -0
- package/stl-docs/fonts.ts +186 -0
- package/stl-docs/index.ts +176 -58
- package/stl-docs/loadStlDocsConfig.ts +73 -8
- package/stl-docs/proseDocSync.test.ts +74 -0
- package/stl-docs/proseDocSync.ts +344 -0
- package/stl-docs/proseMarkdown/proseMarkdownIntegration.ts +53 -0
- package/stl-docs/proseMarkdown/proseMarkdownMiddleware.ts +41 -0
- package/stl-docs/proseMarkdown/toMarkdown.ts +158 -0
- package/stl-docs/proseSearchIndexing.ts +218 -0
- package/stl-docs/tabsMiddleware.ts +14 -5
- package/styles/code.css +53 -49
- package/styles/links.css +2 -37
- package/styles/method-descriptions.css +36 -0
- package/styles/overrides.css +28 -46
- package/styles/page.css +228 -38
- package/styles/sdk_select.css +9 -6
- package/styles/search.css +11 -21
- package/styles/sidebar.css +28 -215
- package/styles/{variables.css → sl-variables.css} +4 -8
- package/styles/stldocs-variables.css +6 -0
- package/styles/toc.css +19 -8
- package/theme.css +11 -9
- package/tsconfig.json +1 -4
- package/virtual-module.d.ts +66 -8
- package/components/variables.css +0 -112
- package/plugin/cms/client.ts +0 -62
- package/plugin/cms/server.ts +0 -268
- package/plugin/globalJs/ai-dropdown.ts +0 -57
- package/stl-docs/components/APIReferenceAIDropdown.tsx +0 -58
- package/stl-docs/components/ClientRouterHead.astro +0 -41
- package/stl-docs/components/content-panel/ContentPanel.astro +0 -69
- package/stl-docs/components/content-panel/ProseAIDropdown.tsx +0 -55
- package/stl-docs/components/headers/SplashMobileMenuToggle.astro +0 -49
- package/stl-docs/components/mintlify-compat/Step.astro +0 -56
- package/stl-docs/components/mintlify-compat/Steps.astro +0 -15
- package/styles/fonts.css +0 -68
- /package/{plugin/assets → assets}/fonts/geist/OFL.txt +0 -0
- /package/{plugin/assets → assets}/fonts/geist/geist-italic-latin-ext.woff2 +0 -0
- /package/{plugin/assets → assets}/fonts/geist/geist-italic-latin.woff2 +0 -0
- /package/{plugin/assets → assets}/fonts/geist/geist-latin-ext.woff2 +0 -0
- /package/{plugin/assets → assets}/fonts/geist/geist-latin.woff2 +0 -0
- /package/{plugin/assets → assets}/fonts/geist/geist-mono-italic-latin-ext.woff2 +0 -0
- /package/{plugin/assets → assets}/fonts/geist/geist-mono-italic-latin.woff2 +0 -0
- /package/{plugin/assets → assets}/fonts/geist/geist-mono-latin-ext.woff2 +0 -0
- /package/{plugin/assets → assets}/fonts/geist/geist-mono-latin.woff2 +0 -0
package/stl-docs/index.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import starlight from '@astrojs/starlight';
|
|
2
2
|
import react from '@astrojs/react';
|
|
3
|
-
import {
|
|
3
|
+
import type { StarlightPlugin } from '@astrojs/starlight/types';
|
|
4
|
+
import { disableCalloutSyntaxStarlightPlugin } from './disableCalloutSyntax';
|
|
4
5
|
|
|
5
6
|
import type { AstroIntegration } from 'astro';
|
|
6
7
|
|
|
7
8
|
import { normalizeRedirects, type NormalizedRedirectConfig } from './redirects';
|
|
8
|
-
import
|
|
9
|
+
import path from 'node:path';
|
|
9
10
|
import { mkdirSync, writeFileSync } from 'fs';
|
|
10
11
|
import {
|
|
11
12
|
parseStlDocsConfig,
|
|
@@ -16,11 +17,21 @@ import {
|
|
|
16
17
|
type StarlightSidebarConfig,
|
|
17
18
|
} from './loadStlDocsConfig';
|
|
18
19
|
import { buildVirtualModuleString } from '../shared/virtualModule';
|
|
19
|
-
|
|
20
|
-
import
|
|
20
|
+
import type * as StlDocsVirtualModule from 'virtual:stl-docs-virtual-module';
|
|
21
|
+
import { resolveSrcFile } from '../resolveSrcFile';
|
|
22
|
+
import { stainlessDocsMarkdownRenderer } from './proseMarkdown/proseMarkdownIntegration';
|
|
23
|
+
import { getSharedLogger, setSharedLogger } from '../shared/getSharedLogger';
|
|
24
|
+
import { stainlessDocsVectorProseIndexing } from './proseDocSync';
|
|
25
|
+
import { stainlessDocsAlgoliaProseIndexing } from './proseSearchIndexing';
|
|
26
|
+
import { stainlessStarlight } from '../plugin';
|
|
27
|
+
import { getFontRoles, flattenFonts } from './fonts';
|
|
28
|
+
import conditionalIntegration from '../shared/conditionalIntegration';
|
|
29
|
+
import generateExamplesPlugin from './aiChatExamples';
|
|
21
30
|
|
|
22
31
|
export * from '../plugin';
|
|
23
32
|
|
|
33
|
+
const COMPONENTS_FOLDER = '/stl-docs/components';
|
|
34
|
+
|
|
24
35
|
function stainlessDocsStarlightIntegration(config: NormalizedStainlessDocsConfig) {
|
|
25
36
|
// We transform our tabs into a Starlight sidebar
|
|
26
37
|
// This gives them all the built-in features of Starlight (eg. auto-generated entries by directory)
|
|
@@ -40,22 +51,48 @@ function stainlessDocsStarlightIntegration(config: NormalizedStainlessDocsConfig
|
|
|
40
51
|
}
|
|
41
52
|
|
|
42
53
|
type ComponentOverrides = StarlightConfigDefined['components'];
|
|
43
|
-
const plugins = [...config.starlightCompat.plugins];
|
|
44
|
-
|
|
45
54
|
const componentOverrides: ComponentOverrides = {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
55
|
+
Head: resolveSrcFile(COMPONENTS_FOLDER, './Head.astro'),
|
|
56
|
+
|
|
57
|
+
PageFrame: resolveSrcFile(COMPONENTS_FOLDER, './PageFrame.astro'),
|
|
58
|
+
TwoColumnContent: resolveSrcFile(COMPONENTS_FOLDER, './TwoColumnContent.astro'),
|
|
59
|
+
|
|
60
|
+
Header: resolveSrcFile(COMPONENTS_FOLDER, './Header.astro'),
|
|
61
|
+
ThemeProvider: resolveSrcFile(COMPONENTS_FOLDER, './ThemeProvider.astro'),
|
|
62
|
+
ThemeSelect: resolveSrcFile(COMPONENTS_FOLDER, './ThemeSelect.astro'),
|
|
63
|
+
|
|
64
|
+
Sidebar: resolveSrcFile(COMPONENTS_FOLDER, './sidebars/BaseSidebar.astro'),
|
|
65
|
+
ContentPanel: resolveSrcFile(COMPONENTS_FOLDER, './ContentPanel.astro'),
|
|
66
|
+
PageTitle: resolveSrcFile(COMPONENTS_FOLDER, './PageTitle.astro'),
|
|
67
|
+
PageSidebar: resolveSrcFile(COMPONENTS_FOLDER, './PageSidebar.astro'),
|
|
68
|
+
TableOfContents: resolveSrcFile(COMPONENTS_FOLDER, './TableOfContents.astro'),
|
|
69
|
+
|
|
70
|
+
Footer: resolveSrcFile(COMPONENTS_FOLDER, './Footer.astro'),
|
|
71
|
+
Pagination: resolveSrcFile(COMPONENTS_FOLDER, './pagination/Pagination.astro'),
|
|
51
72
|
};
|
|
52
73
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
74
|
+
const plugins: StarlightPlugin[] = [
|
|
75
|
+
// Disable starlight callout syntax in favor of our own component
|
|
76
|
+
disableCalloutSyntaxStarlightPlugin,
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
if (config.apiReference) {
|
|
80
|
+
plugins.push(
|
|
81
|
+
stainlessStarlight({
|
|
82
|
+
...config.apiReference,
|
|
83
|
+
contextMenu: config.contextMenu,
|
|
84
|
+
experimentalPrerender:
|
|
85
|
+
config.apiReference.experimentalPrerender === undefined
|
|
86
|
+
? config.starlightPassThrough.prerender
|
|
87
|
+
: config.apiReference.experimentalPrerender,
|
|
88
|
+
}),
|
|
89
|
+
);
|
|
90
|
+
componentOverrides.Sidebar = resolveSrcFile(COMPONENTS_FOLDER, './sidebars/SDKSelectSidebar.astro');
|
|
91
|
+
componentOverrides.Search = resolveSrcFile('/plugin/components/search/Search.astro');
|
|
57
92
|
}
|
|
58
93
|
|
|
94
|
+
plugins.push(...config.starlightCompat.plugins, ...config.plugins.map((p) => p(config)));
|
|
95
|
+
|
|
59
96
|
// TODO: re-add once we figure out what to do with the client router
|
|
60
97
|
// if (config.enableClientRouter) {
|
|
61
98
|
// // logger.info(`Client router is enabled`);
|
|
@@ -64,8 +101,11 @@ function stainlessDocsStarlightIntegration(config: NormalizedStainlessDocsConfig
|
|
|
64
101
|
// // logger.info(`Client router is disabled`);
|
|
65
102
|
// }
|
|
66
103
|
|
|
104
|
+
const userExpressiveCode = typeof config.expressiveCode === 'object' ? config.expressiveCode : {};
|
|
105
|
+
|
|
67
106
|
return starlight({
|
|
68
107
|
...config.starlightPassThrough,
|
|
108
|
+
pagefind: config.starlightCompat.pagefind,
|
|
69
109
|
sidebar,
|
|
70
110
|
components: {
|
|
71
111
|
...componentOverrides,
|
|
@@ -87,68 +127,110 @@ function stainlessDocsStarlightIntegration(config: NormalizedStainlessDocsConfig
|
|
|
87
127
|
setupNavLinksInitial();
|
|
88
128
|
`,
|
|
89
129
|
},
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
130
|
+
],
|
|
131
|
+
routeMiddleware: [
|
|
132
|
+
...config.starlightCompat.routeMiddleware,
|
|
133
|
+
resolveSrcFile('/stl-docs/tabsMiddleware.ts'),
|
|
134
|
+
],
|
|
135
|
+
customCss: [resolveSrcFile('/theme.css'), ...config.customCss],
|
|
136
|
+
|
|
137
|
+
expressiveCode: {
|
|
138
|
+
...userExpressiveCode,
|
|
139
|
+
themes: userExpressiveCode.themes ?? ['github-light', 'github-dark'],
|
|
140
|
+
styleOverrides: {
|
|
141
|
+
...userExpressiveCode.styleOverrides,
|
|
142
|
+
textMarkers: {
|
|
143
|
+
insBackground: 'var(--stl-color-green-muted-background)',
|
|
144
|
+
insBorderColor: 'var(--stl-color-green-border)',
|
|
145
|
+
insDiffIndicatorColor: 'var(--stl-color-green-foreground-reduced)',
|
|
146
|
+
|
|
147
|
+
delBackground: 'var(--stl-color-red-muted-background)',
|
|
148
|
+
delBorderColor: 'var(--stl-color-red-border)',
|
|
149
|
+
delDiffIndicatorColor: 'var(--stl-color-red-foreground-reduced)',
|
|
150
|
+
|
|
151
|
+
markBackground: 'var(--stl-color-blue-muted-background)',
|
|
152
|
+
markBorderColor: 'var(--stl-color-blue-border)',
|
|
153
|
+
...userExpressiveCode.styleOverrides?.textMarkers,
|
|
100
154
|
},
|
|
101
155
|
},
|
|
102
|
-
|
|
103
|
-
routeMiddleware: [...config.starlightCompat.routeMiddleware, '@stainless-api/docs/tabsMiddleware'],
|
|
104
|
-
customCss: ['@stainless-api/docs/theme', ...config.customCss],
|
|
156
|
+
},
|
|
105
157
|
plugins,
|
|
106
158
|
});
|
|
107
159
|
}
|
|
108
160
|
|
|
109
|
-
function stainlessDocsIntegration(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
161
|
+
function stainlessDocsIntegration(
|
|
162
|
+
config: NormalizedStainlessDocsConfig,
|
|
163
|
+
apiReferenceBasePath: string | null,
|
|
164
|
+
): AstroIntegration {
|
|
113
165
|
let redirects: NormalizedRedirectConfig | null = null;
|
|
114
166
|
|
|
115
167
|
return {
|
|
116
|
-
name: 'stl-docs-
|
|
168
|
+
name: 'stl-docs-astro',
|
|
117
169
|
hooks: {
|
|
118
|
-
'astro:config:setup': ({ updateConfig, command, config: astroConfig }) => {
|
|
119
|
-
|
|
120
|
-
//
|
|
170
|
+
'astro:config:setup': async ({ updateConfig, command, config: astroConfig, logger: localLogger }) => {
|
|
171
|
+
const logger = getSharedLogger({ fallback: localLogger });
|
|
172
|
+
// we only handle redirects for builds
|
|
173
|
+
// in dev, Astro handles them for us
|
|
121
174
|
if (command === 'build' && astroConfig.redirects) {
|
|
122
175
|
redirects = normalizeRedirects(astroConfig.redirects);
|
|
123
176
|
}
|
|
124
177
|
|
|
178
|
+
const base = astroConfig.base ?? '/';
|
|
179
|
+
const withBase = (link: string) =>
|
|
180
|
+
/^([a-z][a-z0-9+.-]*:|\/\/)/.test(link) ? link : path.posix.join(base, link);
|
|
181
|
+
|
|
182
|
+
const virtualModules = new Map(
|
|
183
|
+
Object.entries({
|
|
184
|
+
'virtual:stl-docs-virtual-module': buildVirtualModuleString({
|
|
185
|
+
TABS: config.tabs.map((tab) => ({ ...tab, link: withBase(tab.link) })),
|
|
186
|
+
SPLIT_TABS_ENABLED: config.splitTabsEnabled,
|
|
187
|
+
HEADER_LINKS: config.header.links.map((link) => ({ ...link, link: withBase(link.link) })),
|
|
188
|
+
HEADER_LAYOUT: config.header.layout,
|
|
189
|
+
ENABLE_CLIENT_ROUTER: config.enableClientRouter,
|
|
190
|
+
API_REFERENCE_BASE_PATH: apiReferenceBasePath ?? '/api',
|
|
191
|
+
ENABLE_PROSE_MARKDOWN_RENDERING: config.enableProseMarkdownRendering,
|
|
192
|
+
ENABLE_CONTEXT_MENU: config.contextMenu, // TODO: do not duplicate this between both virtual modules
|
|
193
|
+
RENDER_PAGE_DESCRIPTIONS: config.renderPageDescriptions,
|
|
194
|
+
FONTS: getFontRoles(config.fonts),
|
|
195
|
+
LINK_GROUP_TITLES_TO_OVERVIEW_PAGES: config.linkGroupTitlesToOverviewPages,
|
|
196
|
+
RENDER_CREDITS: config.credits,
|
|
197
|
+
SITE_TITLE: config.siteTitle,
|
|
198
|
+
ENABLE_AI_CHAT: !!config.aiChat,
|
|
199
|
+
} satisfies typeof StlDocsVirtualModule),
|
|
200
|
+
}),
|
|
201
|
+
);
|
|
202
|
+
|
|
125
203
|
updateConfig({
|
|
204
|
+
fonts: [...flattenFonts(config.fonts), ...(astroConfig?.fonts ?? [])],
|
|
126
205
|
vite: {
|
|
127
|
-
|
|
128
|
-
|
|
206
|
+
define: {
|
|
207
|
+
__STLDOCS_HAS_API_REFERENCE__: !!config.apiReference,
|
|
208
|
+
__STLDOCS_ENABLE_AI_CHAT__: !!config.aiChat,
|
|
129
209
|
},
|
|
130
|
-
optimizeDeps: { include: ['@stainless-api/ui-primitives'] },
|
|
131
210
|
plugins: [
|
|
132
211
|
{
|
|
133
|
-
name: 'stl-docs-
|
|
212
|
+
name: 'stl-docs-virtual-modules',
|
|
134
213
|
resolveId(id) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
214
|
+
// The '\0' prefix tells Vite “this is a virtual module” and prevents it from being resolved again.
|
|
215
|
+
if (virtualModules.has(id)) return `\0${id}`;
|
|
138
216
|
},
|
|
139
217
|
load(id) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
TABS: config.tabs,
|
|
143
|
-
SPLIT_TABS_ENABLED: config.splitTabsEnabled,
|
|
144
|
-
HEADER_LINKS: config.header.links,
|
|
145
|
-
HEADER_LAYOUT: config.header.layout,
|
|
146
|
-
ENABLE_CLIENT_ROUTER: config.enableClientRouter,
|
|
147
|
-
INCLUDE_AI_DROPDOWN_OPTIONS: config.includeAIDropdownOptions,
|
|
148
|
-
});
|
|
149
|
-
}
|
|
218
|
+
const bare = id.replace(/^\0/, '');
|
|
219
|
+
if (virtualModules.has(bare)) return virtualModules.get(bare);
|
|
150
220
|
},
|
|
151
221
|
},
|
|
222
|
+
// Separate plugin for the examples because it has async resolution; not a simple string
|
|
223
|
+
// like the above plugins
|
|
224
|
+
...(config.aiChat
|
|
225
|
+
? [
|
|
226
|
+
await generateExamplesPlugin({
|
|
227
|
+
projectName: config.apiReference?.stainlessProject ?? undefined,
|
|
228
|
+
logger,
|
|
229
|
+
exampleOverrides:
|
|
230
|
+
typeof config.aiChat === 'object' ? config.aiChat.exampleOverrides : undefined,
|
|
231
|
+
}),
|
|
232
|
+
]
|
|
233
|
+
: []),
|
|
152
234
|
],
|
|
153
235
|
},
|
|
154
236
|
build: {
|
|
@@ -159,9 +241,9 @@ function stainlessDocsIntegration(config: NormalizedStainlessDocsConfig): AstroI
|
|
|
159
241
|
},
|
|
160
242
|
'astro:build:done': ({ dir }) => {
|
|
161
243
|
if (redirects !== null) {
|
|
162
|
-
const stainlessDir = join(dir.pathname, '_stainless');
|
|
163
|
-
mkdirSync(stainlessDir);
|
|
164
|
-
const outputPath = join(stainlessDir, 'redirects.json');
|
|
244
|
+
const stainlessDir = path.join(dir.pathname, '_stainless');
|
|
245
|
+
mkdirSync(stainlessDir, { recursive: true });
|
|
246
|
+
const outputPath = path.join(stainlessDir, 'redirects.json');
|
|
165
247
|
writeFileSync(outputPath, JSON.stringify(redirects, null, 2), {
|
|
166
248
|
encoding: 'utf-8',
|
|
167
249
|
});
|
|
@@ -171,7 +253,18 @@ function stainlessDocsIntegration(config: NormalizedStainlessDocsConfig): AstroI
|
|
|
171
253
|
};
|
|
172
254
|
}
|
|
173
255
|
|
|
174
|
-
|
|
256
|
+
function sharedLoggerIntegration(): AstroIntegration {
|
|
257
|
+
return {
|
|
258
|
+
name: 'stainless',
|
|
259
|
+
hooks: {
|
|
260
|
+
'astro:config:setup': ({ logger }) => {
|
|
261
|
+
setSharedLogger(logger);
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function stainlessDocs(config: StainlessDocsUserConfig): AstroIntegration[] {
|
|
175
268
|
const normalizedConfigResult = parseStlDocsConfig(config);
|
|
176
269
|
if (normalizedConfigResult.result === 'error') {
|
|
177
270
|
// TODO: would be good to use the astro logger somehow
|
|
@@ -180,9 +273,34 @@ export function stainlessDocs(config: StainlessDocsUserConfig) {
|
|
|
180
273
|
}
|
|
181
274
|
const normalizedConfig = normalizedConfigResult.config;
|
|
182
275
|
|
|
276
|
+
// TODO: need to refactor this, but this allows us to get the base path for the API reference _if_ it exists
|
|
277
|
+
// if it doesn't exist, the value of basePath is null.
|
|
278
|
+
// the stl-starlight virtual module has base path, but it's not available when there's no API reference
|
|
279
|
+
const hasApiReference = normalizedConfig.apiReference !== null;
|
|
280
|
+
let apiReferenceBasePath: string | null = null;
|
|
281
|
+
if (hasApiReference) {
|
|
282
|
+
apiReferenceBasePath = normalizedConfig.apiReference?.basePath ?? '/api';
|
|
283
|
+
}
|
|
284
|
+
|
|
183
285
|
return [
|
|
286
|
+
sharedLoggerIntegration(), // this **must** be first so it can set the shared logger used by our other integrations
|
|
184
287
|
react(),
|
|
185
288
|
stainlessDocsStarlightIntegration(normalizedConfig),
|
|
186
|
-
stainlessDocsIntegration(normalizedConfig),
|
|
289
|
+
stainlessDocsIntegration(normalizedConfig, apiReferenceBasePath),
|
|
290
|
+
conditionalIntegration({
|
|
291
|
+
condition: !config.experimental?.disableProseMarkdownRendering,
|
|
292
|
+
integration: stainlessDocsMarkdownRenderer({ apiReferenceBasePath }),
|
|
293
|
+
reason: 'disabled by experimental config "disableProseMarkdownRendering"',
|
|
294
|
+
}),
|
|
295
|
+
conditionalIntegration({
|
|
296
|
+
condition: !config.experimental?.disableStainlessProseIndexing,
|
|
297
|
+
integration: stainlessDocsAlgoliaProseIndexing({ apiReferenceBasePath }),
|
|
298
|
+
reason: 'disabled by experimental config "disableStainlessProseIndexing"',
|
|
299
|
+
}),
|
|
300
|
+
conditionalIntegration({
|
|
301
|
+
condition: !config.experimental?.disableStainlessProseIndexing,
|
|
302
|
+
integration: stainlessDocsVectorProseIndexing(normalizedConfig, apiReferenceBasePath),
|
|
303
|
+
reason: 'disabled by experimental config "disableStainlessProseIndexing"',
|
|
304
|
+
}),
|
|
187
305
|
];
|
|
188
306
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { StainlessStarlightUserConfig } from '../plugin/loadPluginConfig';
|
|
2
|
-
import type { StarlightUserConfig } from '@astrojs/starlight/types';
|
|
2
|
+
import type { StarlightPlugin, StarlightUserConfig } from '@astrojs/starlight/types';
|
|
3
3
|
import type { ButtonVariant } from '@stainless-api/ui-primitives';
|
|
4
4
|
import type { AnchorHTMLAttributes } from 'react';
|
|
5
5
|
import type starlight from '@astrojs/starlight';
|
|
6
|
+
import { normalizeFonts, type StlDocsFontConfig } from './fonts';
|
|
7
|
+
import type { ExamplePromptResponse } from './aiChatExamples';
|
|
6
8
|
|
|
7
9
|
type StarlightConfig = Parameters<typeof starlight>[0];
|
|
8
10
|
|
|
@@ -32,20 +34,22 @@ type PassThroughStarlightConfigOptions = Pick<
|
|
|
32
34
|
| 'lastUpdated'
|
|
33
35
|
| 'pagination'
|
|
34
36
|
| 'sidebar'
|
|
37
|
+
| 'expressiveCode'
|
|
35
38
|
>;
|
|
36
39
|
|
|
37
40
|
type ExperimentalStarlightCompatibilityConfig = Pick<
|
|
38
41
|
StarlightConfigDefined,
|
|
39
|
-
'components' | 'routeMiddleware' | 'plugins'
|
|
42
|
+
'components' | 'routeMiddleware' | 'plugins' | 'prerender' | 'pagefind'
|
|
40
43
|
>;
|
|
41
44
|
|
|
42
|
-
type Tabs = {
|
|
45
|
+
export type Tabs = {
|
|
43
46
|
label: string;
|
|
44
47
|
link: string;
|
|
45
48
|
sidebar?: SidebarEntry[];
|
|
46
49
|
/**
|
|
47
50
|
* Whether to hide the tab in the tab bar.
|
|
48
|
-
*
|
|
51
|
+
*
|
|
52
|
+
* @default false
|
|
49
53
|
*/
|
|
50
54
|
hidden?: boolean;
|
|
51
55
|
}[];
|
|
@@ -64,10 +68,44 @@ export type StainlessDocsUserConfig = {
|
|
|
64
68
|
layout?: HeaderLayout;
|
|
65
69
|
links?: HeaderLink[];
|
|
66
70
|
};
|
|
71
|
+
disableCredits?: boolean;
|
|
72
|
+
fonts?: StlDocsFontConfig;
|
|
67
73
|
experimental?: {
|
|
68
74
|
starlightCompat?: ExperimentalStarlightCompatibilityConfig;
|
|
69
75
|
enableClientRouter?: boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Disable markdown rendering for prose content. Only disable this if it is causing issues.
|
|
78
|
+
*
|
|
79
|
+
* @default false
|
|
80
|
+
*/
|
|
81
|
+
disableProseMarkdownRendering?: boolean;
|
|
82
|
+
disableStainlessProseIndexing?: boolean;
|
|
83
|
+
aiChat?: { exampleOverrides?: ExamplePromptResponse } | true;
|
|
84
|
+
/**
|
|
85
|
+
* Whether to link group titles to overview pages. Note: overview pages must already be present in the sidebar for this to work.
|
|
86
|
+
*
|
|
87
|
+
* @default false
|
|
88
|
+
*/
|
|
89
|
+
linkGroupTitlesToOverviewPages?: boolean;
|
|
70
90
|
};
|
|
91
|
+
/**
|
|
92
|
+
* Whether to show the context menu with options like "Copy as Markdown" and "Open in ChatGPT".
|
|
93
|
+
*
|
|
94
|
+
* @default true
|
|
95
|
+
*/
|
|
96
|
+
contextMenu?: boolean;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Whether to render page descriptions in prose page headers
|
|
100
|
+
*
|
|
101
|
+
* @default true
|
|
102
|
+
*/
|
|
103
|
+
renderPageDescriptions?: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Stainless Docs plugins.
|
|
106
|
+
* Each plugin is a function that receives the normalized config and returns a Starlight plugin.
|
|
107
|
+
*/
|
|
108
|
+
plugins?: ((config: Exclude<NormalizedStainlessDocsConfig, 'plugins'>) => StarlightPlugin)[];
|
|
71
109
|
} & PassThroughStarlightConfigOptions;
|
|
72
110
|
|
|
73
111
|
type HeaderLayout = 'default' | 'stacked';
|
|
@@ -99,6 +137,19 @@ function normalizeRouteMiddleware(userConfig: StainlessDocsUserConfig) {
|
|
|
99
137
|
return entry;
|
|
100
138
|
}
|
|
101
139
|
|
|
140
|
+
function normalizeSiteTitle(userConfig: StainlessDocsUserConfig) {
|
|
141
|
+
if (typeof userConfig.title === 'string') {
|
|
142
|
+
return userConfig.title;
|
|
143
|
+
}
|
|
144
|
+
if (typeof userConfig.title === 'object') {
|
|
145
|
+
const firstValue = Object.values(userConfig.title)[0];
|
|
146
|
+
if (typeof firstValue === 'string') {
|
|
147
|
+
return firstValue;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
throw new Error('Site title provided in config is not valid.');
|
|
151
|
+
}
|
|
152
|
+
|
|
102
153
|
function normalizeConfig(userConfig: StainlessDocsUserConfig) {
|
|
103
154
|
const splitTabsEnabled = areSplitTabsEnabled(userConfig);
|
|
104
155
|
|
|
@@ -112,6 +163,7 @@ function normalizeConfig(userConfig: StainlessDocsUserConfig) {
|
|
|
112
163
|
layout: userConfig.header?.layout ?? 'default',
|
|
113
164
|
links: userConfig.header?.links ?? [],
|
|
114
165
|
},
|
|
166
|
+
fonts: normalizeFonts(userConfig.fonts),
|
|
115
167
|
starlightPassThrough: {
|
|
116
168
|
tableOfContents: userConfig.tableOfContents,
|
|
117
169
|
titleDelimiter: userConfig.titleDelimiter,
|
|
@@ -125,16 +177,29 @@ function normalizeConfig(userConfig: StainlessDocsUserConfig) {
|
|
|
125
177
|
locales: userConfig.locales,
|
|
126
178
|
lastUpdated: userConfig.lastUpdated,
|
|
127
179
|
pagination: userConfig.pagination,
|
|
180
|
+
prerender: userConfig.experimental?.starlightCompat?.prerender ?? true,
|
|
128
181
|
},
|
|
129
182
|
starlightCompat: {
|
|
130
183
|
components: userConfig.experimental?.starlightCompat?.components ?? {},
|
|
131
184
|
plugins: userConfig.experimental?.starlightCompat?.plugins ?? [],
|
|
185
|
+
pagefind: userConfig.experimental?.starlightCompat?.pagefind ?? true,
|
|
132
186
|
routeMiddleware: normalizeRouteMiddleware(userConfig),
|
|
133
187
|
},
|
|
134
188
|
enableClientRouter: userConfig.experimental?.enableClientRouter ?? false,
|
|
135
189
|
apiReference: userConfig.apiReference ?? null,
|
|
136
190
|
sidebar: userConfig.sidebar,
|
|
137
|
-
|
|
191
|
+
enableStainlessProseIndexing:
|
|
192
|
+
userConfig.experimental?.disableStainlessProseIndexing === true ? false : true,
|
|
193
|
+
enableProseMarkdownRendering:
|
|
194
|
+
userConfig.experimental?.disableProseMarkdownRendering === true ? false : true,
|
|
195
|
+
contextMenu: userConfig.contextMenu ?? true,
|
|
196
|
+
expressiveCode: userConfig.expressiveCode,
|
|
197
|
+
renderPageDescriptions: userConfig.renderPageDescriptions ?? true,
|
|
198
|
+
plugins: userConfig.plugins ?? [],
|
|
199
|
+
aiChat: userConfig.experimental?.aiChat,
|
|
200
|
+
linkGroupTitlesToOverviewPages: userConfig.experimental?.linkGroupTitlesToOverviewPages ?? false,
|
|
201
|
+
credits: !userConfig.disableCredits,
|
|
202
|
+
siteTitle: normalizeSiteTitle(userConfig),
|
|
138
203
|
};
|
|
139
204
|
|
|
140
205
|
return configWithDefaults;
|
|
@@ -143,12 +208,12 @@ function normalizeConfig(userConfig: StainlessDocsUserConfig) {
|
|
|
143
208
|
export type NormalizedStainlessDocsConfig = ReturnType<typeof normalizeConfig>;
|
|
144
209
|
|
|
145
210
|
/*
|
|
146
|
-
The goal of the code in this file is to take a user's config and normalize it.
|
|
211
|
+
The goal of the code in this file is to take a user's config and normalize it.
|
|
147
212
|
Specifically: we want a single complete config format used throughout the internals of the plugin.
|
|
148
213
|
|
|
149
214
|
We've tried to avoid any config values being optional/undefined. To accomplish this:
|
|
150
|
-
- Any optional config values should have their defaults set here: eg. basePath defaults to /api
|
|
151
|
-
- If a field is only used in certain contexts, we make each context a discriminated union (see
|
|
215
|
+
- Any optional config values should have their defaults set here: eg. basePath defaults to /api
|
|
216
|
+
- If a field is only used in certain contexts, we make each context a discriminated union (see SDKJSONInputs)
|
|
152
217
|
- We prefer empty arrays over undefined/null
|
|
153
218
|
*/
|
|
154
219
|
export function parseStlDocsConfig(userConfig: StainlessDocsUserConfig) {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { batchBySize, MAX_BATCH_BYTES, MAX_BATCH_DOCS } from './proseDocSync';
|
|
3
|
+
|
|
4
|
+
function makeDoc(docId: string, sizeBytes: number) {
|
|
5
|
+
return {
|
|
6
|
+
docId,
|
|
7
|
+
content: Buffer.alloc(sizeBytes, 'x'),
|
|
8
|
+
sha256: 'fake',
|
|
9
|
+
source: `/${docId}`,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
describe('batchBySize', () => {
|
|
14
|
+
it('returns empty array for no docs', () => {
|
|
15
|
+
expect(batchBySize([])).toEqual([]);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('puts all docs in one batch when under both limits', () => {
|
|
19
|
+
const docs = Array.from({ length: 5 }, (_, i) => makeDoc(`doc-${i}`, 100));
|
|
20
|
+
const batches = batchBySize(docs);
|
|
21
|
+
expect(batches).toHaveLength(1);
|
|
22
|
+
expect(batches[0]).toHaveLength(5);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('splits at MAX_BATCH_DOCS', () => {
|
|
26
|
+
const docs = Array.from({ length: MAX_BATCH_DOCS + 10 }, (_, i) => makeDoc(`doc-${i}`, 10));
|
|
27
|
+
const batches = batchBySize(docs);
|
|
28
|
+
expect(batches).toHaveLength(2);
|
|
29
|
+
expect(batches[0]).toHaveLength(MAX_BATCH_DOCS);
|
|
30
|
+
expect(batches[1]).toHaveLength(10);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('splits when cumulative size exceeds MAX_BATCH_BYTES', () => {
|
|
34
|
+
const docSize = 10 * 1024 * 1024; // 10MB each
|
|
35
|
+
// 4 docs = 40MB > 30MB limit, so should split into [3, 1]
|
|
36
|
+
const docs = Array.from({ length: 4 }, (_, i) => makeDoc(`doc-${i}`, docSize));
|
|
37
|
+
const batches = batchBySize(docs);
|
|
38
|
+
expect(batches).toHaveLength(2);
|
|
39
|
+
expect(batches[0]).toHaveLength(3);
|
|
40
|
+
expect(batches[1]).toHaveLength(1);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('handles a single doc larger than MAX_BATCH_BYTES', () => {
|
|
44
|
+
const docs = [makeDoc('huge', MAX_BATCH_BYTES + 1)];
|
|
45
|
+
const batches = batchBySize(docs);
|
|
46
|
+
// Still goes into a batch on its own — we can't split a single doc
|
|
47
|
+
expect(batches).toHaveLength(1);
|
|
48
|
+
expect(batches[0]).toHaveLength(1);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('byte limit takes precedence over doc count when hit first', () => {
|
|
52
|
+
// 2 docs of 20MB each = 40MB > 30MB limit, well under 100 doc limit
|
|
53
|
+
const docs = [makeDoc('a', 20 * 1024 * 1024), makeDoc('b', 20 * 1024 * 1024)];
|
|
54
|
+
const batches = batchBySize(docs);
|
|
55
|
+
expect(batches).toHaveLength(2);
|
|
56
|
+
expect(batches[0]).toHaveLength(1);
|
|
57
|
+
expect(batches[1]).toHaveLength(1);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('creates multiple batches for many large docs', () => {
|
|
61
|
+
const docSize = 8 * 1024 * 1024; // 8MB each
|
|
62
|
+
// 10 docs * 8MB = 80MB, should split into batches of ~3 (24MB each)
|
|
63
|
+
const docs = Array.from({ length: 10 }, (_, i) => makeDoc(`doc-${i}`, docSize));
|
|
64
|
+
const batches = batchBySize(docs);
|
|
65
|
+
// Every batch should be <= MAX_BATCH_BYTES
|
|
66
|
+
for (const batch of batches) {
|
|
67
|
+
const totalBytes = batch.reduce((sum, d) => sum + d.content.byteLength, 0);
|
|
68
|
+
expect(totalBytes).toBeLessThanOrEqual(MAX_BATCH_BYTES);
|
|
69
|
+
}
|
|
70
|
+
// All docs should be accounted for
|
|
71
|
+
const totalDocs = batches.reduce((sum, b) => sum + b.length, 0);
|
|
72
|
+
expect(totalDocs).toBe(10);
|
|
73
|
+
});
|
|
74
|
+
});
|