@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.
Files changed (188) hide show
  1. package/CHANGELOG.md +1102 -0
  2. package/ambient.d.ts +6 -0
  3. package/eslint-suppressions.json +90 -0
  4. package/{eslint.config.js → eslint.config.ts} +0 -2
  5. package/locals.d.ts +17 -0
  6. package/package.json +62 -44
  7. package/playground-virtual-modules.d.ts +96 -0
  8. package/plugin/assets/languages/cli.svg +14 -0
  9. package/plugin/assets/languages/csharp.svg +1 -0
  10. package/plugin/assets/languages/php.svg +4 -0
  11. package/plugin/buildAlgoliaIndex.ts +40 -39
  12. package/plugin/components/MethodDescription.tsx +54 -0
  13. package/plugin/components/RequestBuilder/ParamEditor.tsx +55 -0
  14. package/plugin/components/RequestBuilder/SnippetStainlessIsland.tsx +107 -0
  15. package/plugin/components/RequestBuilder/index.tsx +40 -0
  16. package/plugin/components/RequestBuilder/props.ts +9 -0
  17. package/plugin/components/RequestBuilder/spec-helpers.ts +47 -0
  18. package/plugin/components/RequestBuilder/styles.css +67 -0
  19. package/plugin/components/SDKSelect.astro +18 -111
  20. package/plugin/components/SnippetCode.tsx +112 -70
  21. package/plugin/components/StainlessIslands.tsx +126 -0
  22. package/plugin/components/search/SearchAlgolia.astro +46 -29
  23. package/plugin/components/search/SearchIsland.tsx +61 -37
  24. package/plugin/generateAPIReferenceLink.ts +0 -40
  25. package/plugin/globalJs/ai-dropdown-options.ts +248 -0
  26. package/plugin/globalJs/code-snippets.ts +45 -16
  27. package/plugin/globalJs/copy.ts +115 -27
  28. package/plugin/globalJs/create-playground.shim.ts +3 -0
  29. package/plugin/globalJs/method-descriptions.ts +33 -0
  30. package/plugin/globalJs/navigation.ts +24 -44
  31. package/plugin/globalJs/playground-data.shim.ts +1 -0
  32. package/plugin/globalJs/playground-data.ts +14 -0
  33. package/plugin/globalJs/summary-selection-tweak.ts +29 -0
  34. package/plugin/helpers/generateDocsRoutes.ts +59 -0
  35. package/plugin/helpers/multiSpec.ts +8 -0
  36. package/plugin/index.ts +317 -141
  37. package/plugin/languages.ts +8 -2
  38. package/plugin/loadPluginConfig.ts +284 -109
  39. package/plugin/markdown/highlighter.ts +100 -0
  40. package/plugin/markdown/index.ts +39 -0
  41. package/plugin/middlewareBuilder/stainlessMiddleware.d.ts +3 -1
  42. package/plugin/react/Routing.tsx +98 -263
  43. package/plugin/referencePlaceholderUtils.ts +17 -14
  44. package/plugin/replaceSidebarPlaceholderMiddleware.ts +39 -35
  45. package/plugin/routes/Docs.astro +72 -111
  46. package/plugin/routes/DocsStatic.astro +6 -5
  47. package/plugin/routes/Overview.astro +46 -22
  48. package/plugin/routes/llms.ts +186 -0
  49. package/plugin/routes/markdown.ts +13 -12
  50. package/plugin/{cms → sidebar-utils}/sidebar-builder.ts +84 -69
  51. package/plugin/specs/FileCache.ts +99 -0
  52. package/plugin/specs/fetchSpecSSR.ts +27 -0
  53. package/plugin/specs/generateSpec.ts +112 -0
  54. package/plugin/specs/index.ts +132 -0
  55. package/plugin/specs/inputResolver.ts +148 -0
  56. package/plugin/{cms → specs}/worker.ts +82 -5
  57. package/plugin/vendor/preview.worker.docs.js +27121 -16890
  58. package/plugin/vendor/templates/cli.md +1 -0
  59. package/plugin/vendor/templates/go.md +4 -2
  60. package/plugin/vendor/templates/java.md +5 -1
  61. package/plugin/vendor/templates/kotlin.md +5 -1
  62. package/plugin/vendor/templates/node.md +4 -2
  63. package/plugin/vendor/templates/python.md +4 -2
  64. package/plugin/vendor/templates/ruby.md +4 -2
  65. package/plugin/vendor/templates/terraform.md +1 -1
  66. package/plugin/vendor/templates/typescript.md +3 -1
  67. package/resolveSrcFile.ts +10 -0
  68. package/scripts/vendor_deps.ts +5 -5
  69. package/shared/conditionalIntegration.ts +28 -0
  70. package/shared/getProsePages.ts +41 -0
  71. package/shared/getSharedLogger.ts +15 -0
  72. package/shared/terminalUtils.ts +3 -0
  73. package/shared/virtualModule.ts +46 -1
  74. package/src/content.config.ts +9 -0
  75. package/stl-docs/aiChatExamples.ts +95 -0
  76. package/stl-docs/chat/docs-chat-handler.ts +18 -0
  77. package/stl-docs/chat/hook.ts +215 -0
  78. package/stl-docs/chat/schemas.ts +70 -0
  79. package/stl-docs/chat/stainless-handler/index.ts +126 -0
  80. package/stl-docs/chat/stream-util.ts +16 -0
  81. package/stl-docs/chat/ui/AiChat.module.css +591 -0
  82. package/stl-docs/chat/ui/AiChat.tsx +188 -0
  83. package/stl-docs/chat/ui/Trigger.tsx +154 -0
  84. package/stl-docs/chat/ui/components/ChatControls.tsx +51 -0
  85. package/stl-docs/chat/ui/components/ChatEmpty.tsx +42 -0
  86. package/stl-docs/chat/ui/components/ChatLog.tsx +96 -0
  87. package/stl-docs/chat/ui/components/ChatMessage.tsx +47 -0
  88. package/stl-docs/chat/ui/components/CodeBlock.tsx +33 -0
  89. package/stl-docs/chat/ui/components/MessageFeedback.tsx +109 -0
  90. package/stl-docs/chat/ui/components/Table.tsx +15 -0
  91. package/stl-docs/chat/ui/components/ToolCall.tsx +34 -0
  92. package/stl-docs/chat/ui/components/hljs-github.css +81 -0
  93. package/stl-docs/chat/ui/scroll-manager.ts +86 -0
  94. package/stl-docs/chat/ui/types.ts +45 -0
  95. package/stl-docs/components/AIDropdown.tsx +63 -0
  96. package/stl-docs/components/AiChatIsland.tsx +16 -0
  97. package/stl-docs/components/{content-panel/ContentBreadcrumbs.tsx → ContentBreadcrumbs.tsx} +2 -2
  98. package/stl-docs/components/ContentPanel.astro +9 -0
  99. package/stl-docs/components/Footer.astro +89 -0
  100. package/stl-docs/components/Head.astro +20 -0
  101. package/stl-docs/components/Header.astro +3 -9
  102. package/stl-docs/components/PageFrame.astro +37 -0
  103. package/stl-docs/components/PageSidebar.astro +11 -0
  104. package/stl-docs/components/PageTitle.astro +82 -0
  105. package/stl-docs/components/StainlessLogo.svg +4 -0
  106. package/stl-docs/components/ThemeProvider.astro +36 -0
  107. package/stl-docs/components/ThemeSelect.astro +84 -146
  108. package/stl-docs/components/TwoColumnContent.astro +2 -0
  109. package/stl-docs/components/headers/DefaultHeader.astro +6 -8
  110. package/stl-docs/components/headers/StackedHeader.astro +10 -53
  111. package/stl-docs/components/icons/chat-gpt.tsx +2 -2
  112. package/stl-docs/components/icons/cursor.tsx +10 -0
  113. package/stl-docs/components/icons/gemini.tsx +19 -0
  114. package/stl-docs/components/icons/markdown.tsx +1 -1
  115. package/stl-docs/components/index.ts +1 -0
  116. package/stl-docs/components/mintlify-compat/Accordion.astro +2 -2
  117. package/stl-docs/components/mintlify-compat/AccordionGroup.astro +0 -4
  118. package/stl-docs/components/mintlify-compat/Columns.astro +2 -2
  119. package/stl-docs/components/mintlify-compat/Frame.astro +6 -6
  120. package/stl-docs/components/mintlify-compat/Tab.astro +2 -2
  121. package/stl-docs/components/mintlify-compat/callouts/Callout.astro +2 -2
  122. package/stl-docs/components/mintlify-compat/callouts/Check.astro +0 -4
  123. package/stl-docs/components/mintlify-compat/callouts/Danger.astro +0 -4
  124. package/stl-docs/components/mintlify-compat/callouts/Info.astro +0 -4
  125. package/stl-docs/components/mintlify-compat/callouts/Note.astro +0 -4
  126. package/stl-docs/components/mintlify-compat/callouts/Tip.astro +0 -4
  127. package/stl-docs/components/mintlify-compat/callouts/Warning.astro +0 -4
  128. package/stl-docs/components/mintlify-compat/card.css +4 -4
  129. package/stl-docs/components/mintlify-compat/index.ts +2 -4
  130. package/stl-docs/components/nav-tabs/NavDropdown.astro +38 -77
  131. package/stl-docs/components/nav-tabs/NavTabs.astro +81 -81
  132. package/stl-docs/components/nav-tabs/SecondaryNavTabs.astro +1 -2
  133. package/stl-docs/components/nav-tabs/buildNavLinks.ts +5 -2
  134. package/stl-docs/components/pagination/HomeLink.astro +10 -0
  135. package/stl-docs/components/pagination/Pagination.astro +177 -0
  136. package/stl-docs/components/pagination/PaginationLinkEmphasized.astro +22 -0
  137. package/stl-docs/components/pagination/PaginationLinkQuiet.astro +13 -0
  138. package/stl-docs/components/pagination/util.ts +71 -0
  139. package/stl-docs/components/scripts.ts +1 -0
  140. package/stl-docs/components/sidebars/BaseSidebar.astro +80 -2
  141. package/stl-docs/components/sidebars/SidebarWithComponents.tsx +10 -0
  142. package/stl-docs/components/sidebars/convertAstroSidebarToStl.tsx +62 -0
  143. package/stl-docs/disableCalloutSyntax.ts +36 -0
  144. package/stl-docs/fonts.ts +186 -0
  145. package/stl-docs/index.ts +176 -58
  146. package/stl-docs/loadStlDocsConfig.ts +73 -8
  147. package/stl-docs/proseDocSync.test.ts +74 -0
  148. package/stl-docs/proseDocSync.ts +344 -0
  149. package/stl-docs/proseMarkdown/proseMarkdownIntegration.ts +53 -0
  150. package/stl-docs/proseMarkdown/proseMarkdownMiddleware.ts +41 -0
  151. package/stl-docs/proseMarkdown/toMarkdown.ts +158 -0
  152. package/stl-docs/proseSearchIndexing.ts +218 -0
  153. package/stl-docs/tabsMiddleware.ts +14 -5
  154. package/styles/code.css +53 -49
  155. package/styles/links.css +2 -37
  156. package/styles/method-descriptions.css +36 -0
  157. package/styles/overrides.css +28 -46
  158. package/styles/page.css +228 -38
  159. package/styles/sdk_select.css +9 -6
  160. package/styles/search.css +11 -21
  161. package/styles/sidebar.css +28 -215
  162. package/styles/{variables.css → sl-variables.css} +4 -8
  163. package/styles/stldocs-variables.css +6 -0
  164. package/styles/toc.css +19 -8
  165. package/theme.css +11 -9
  166. package/tsconfig.json +1 -4
  167. package/virtual-module.d.ts +66 -8
  168. package/components/variables.css +0 -112
  169. package/plugin/cms/client.ts +0 -62
  170. package/plugin/cms/server.ts +0 -268
  171. package/plugin/globalJs/ai-dropdown.ts +0 -57
  172. package/stl-docs/components/APIReferenceAIDropdown.tsx +0 -58
  173. package/stl-docs/components/ClientRouterHead.astro +0 -41
  174. package/stl-docs/components/content-panel/ContentPanel.astro +0 -69
  175. package/stl-docs/components/content-panel/ProseAIDropdown.tsx +0 -55
  176. package/stl-docs/components/headers/SplashMobileMenuToggle.astro +0 -49
  177. package/stl-docs/components/mintlify-compat/Step.astro +0 -56
  178. package/stl-docs/components/mintlify-compat/Steps.astro +0 -15
  179. package/styles/fonts.css +0 -68
  180. /package/{plugin/assets → assets}/fonts/geist/OFL.txt +0 -0
  181. /package/{plugin/assets → assets}/fonts/geist/geist-italic-latin-ext.woff2 +0 -0
  182. /package/{plugin/assets → assets}/fonts/geist/geist-italic-latin.woff2 +0 -0
  183. /package/{plugin/assets → assets}/fonts/geist/geist-latin-ext.woff2 +0 -0
  184. /package/{plugin/assets → assets}/fonts/geist/geist-latin.woff2 +0 -0
  185. /package/{plugin/assets → assets}/fonts/geist/geist-mono-italic-latin-ext.woff2 +0 -0
  186. /package/{plugin/assets → assets}/fonts/geist/geist-mono-italic-latin.woff2 +0 -0
  187. /package/{plugin/assets → assets}/fonts/geist/geist-mono-latin-ext.woff2 +0 -0
  188. /package/{plugin/assets → assets}/fonts/geist/geist-mono-latin.woff2 +0 -0
package/plugin/index.ts CHANGED
@@ -2,32 +2,43 @@ import react from '@astrojs/react';
2
2
  import type { StarlightPlugin } from '@astrojs/starlight/types';
3
3
  import type { AstroIntegration } from 'astro';
4
4
  import { config } from 'dotenv';
5
- import getPort from 'get-port';
6
- import { startDevServer } from './cms/server';
7
- import { buildAlgoliaIndex } from './buildAlgoliaIndex';
8
5
  import {
9
6
  getAPIReferencePlaceholderItemFromSidebarConfig,
10
7
  makePlaceholderItems,
11
8
  } from './referencePlaceholderUtils';
12
- import type {
13
- GeneratedSidebarConfig,
14
- ReferenceSidebarConfigGenerateOptions,
15
- ReferenceSidebarConfigItem,
16
- } from './cms/sidebar-builder';
9
+ import {
10
+ SidebarConfigItemsBuilder,
11
+ toStarlightSidebar,
12
+ type GeneratedSidebarConfig,
13
+ type ReferenceSidebarConfigGenerateOptions,
14
+ type ReferenceSidebarConfigItem,
15
+ } from './sidebar-utils/sidebar-builder';
17
16
  import {
18
17
  parseStarlightPluginConfig,
19
18
  type NormalizedStainlessStarlightConfig,
20
19
  type SomeStainlessStarlightUserConfig,
21
- type SpecRetrieverConfig,
22
20
  } from './loadPluginConfig';
23
- import { buildVirtualModuleString } from '../shared/virtualModule';
21
+ import { buildVirtualModuleString, makeAsyncVirtualModPlugin } from '../shared/virtualModule';
22
+ import type * as StlStarlightVirtualModule from 'virtual:stl-starlight-virtual-module';
24
23
  import path from 'path';
25
24
  import fs from 'fs';
25
+ import { getSharedLogger } from '../shared/getSharedLogger';
26
+ import { resolveSrcFile } from '../resolveSrcFile';
27
+ import { mkdir, writeFile } from 'fs/promises';
28
+ import { fileURLToPath } from 'url';
29
+ import prebundleWorkers from 'vite-plugin-prebundle-workers';
30
+ import { SpecLoader, startSpecLoader } from './specs';
31
+
32
+ import type * as ReferenceSidebarsVirtualModule from 'virtual:stl-starlight-reference-sidebars';
33
+ import { generateMissingRouteList } from '@stainless-api/docs-ui/routing';
34
+ import { buildAlgoliaIndex } from './buildAlgoliaIndex';
26
35
 
27
36
  export { generateAPILink } from './generateAPIReferenceLink';
28
37
  export type { ReferenceSidebarConfigItem };
29
38
 
30
- config();
39
+ config({
40
+ quiet: true,
41
+ });
31
42
 
32
43
  let sidebarIdCounter = 0;
33
44
 
@@ -78,150 +89,227 @@ export function generateAPIReferenceItems(
78
89
  return makePlaceholderItems(id);
79
90
  }
80
91
 
81
- function tmpGetCMSServerConfig(specRetrieverConfig: SpecRetrieverConfig) {
82
- if (specRetrieverConfig.kind === 'external_spec_server') {
83
- throw new Error('External spec server is not yet supported');
84
- }
85
- if (specRetrieverConfig.kind === 'local_spec_server_with_files') {
86
- if (!specRetrieverConfig.apiKey) {
87
- throw new Error('API key is required');
88
- }
89
- return {
90
- apiKey: specRetrieverConfig.apiKey,
91
- version: specRetrieverConfig.version,
92
- devPaths: specRetrieverConfig.devPaths,
93
- };
94
- }
95
-
96
- if (specRetrieverConfig.kind === 'local_spec_server_with_remote_files') {
97
- return {
98
- apiKey: specRetrieverConfig.apiKey,
99
- version: specRetrieverConfig.version,
100
- devPaths: {
101
- oasPath: undefined,
102
- configPath: undefined,
103
- },
104
- };
105
- }
106
- throw new Error('Invalid spec retriever config');
107
- }
108
-
109
- async function stlStarlightAstroIntegration(
110
- pluginConfig: NormalizedStainlessStarlightConfig,
111
- ): Promise<AstroIntegration> {
92
+ function stlStarlightAstroIntegration(pluginConfig: NormalizedStainlessStarlightConfig): AstroIntegration {
112
93
  const virtualId = `virtual:stl-starlight-virtual-module`;
113
94
  // The '\0' prefix tells Vite “this is a virtual module” and prevents it from being resolved again.
114
95
  const resolvedId = `\0${virtualId}`;
96
+ let playgroundsBase: string | undefined;
97
+ let buildPlaygrounds: (args: unknown) => Promise<void> | undefined;
98
+ let astroBase = '/';
99
+
100
+ let specLoader: SpecLoader | undefined;
101
+ async function resolveSpecs() {
102
+ if (!specLoader) throw new Error('Expected spec loader to exist');
103
+ const result = await specLoader.specPromise;
104
+ return result.specComposite;
105
+ }
115
106
 
116
- const CMS_PORT = await getPort();
107
+ let building: Promise<void> | undefined;
108
+ let reportError: ((message: string) => void) | null = null;
109
+ let collectedErrors: string[] | null = null;
117
110
 
118
- const { apiKey, version, devPaths } = tmpGetCMSServerConfig(pluginConfig.specRetrieverConfig);
111
+ function startPlaygroundsBuild(playgroundsCachePath: string) {
112
+ if (!pluginConfig.experimentalPlaygrounds) return;
113
+ if (building) return building;
114
+ return (building = (async () => {
115
+ const specComposite = await resolveSpecs();
119
116
 
120
- const cmsServer = await startDevServer({
121
- port: CMS_PORT,
122
- apiKey,
123
- version,
124
- devPaths,
125
- getGeneratedSidebarConfig: (id: number) => {
126
- const config = sidebarConfigs.get(id);
127
- if (!config) {
128
- return null;
117
+ if (specComposite.listUniqueSpecs().length > 1) {
118
+ throw new Error('Multiple specs found. This is not supported for Playgrounds.');
129
119
  }
130
- return config;
131
- },
132
- });
120
+
121
+ const spec = specComposite.listUniqueSpecs()[0]!.data.sdkJson;
122
+ const auth = specComposite.listUniqueSpecs()[0]!.data.auth;
123
+
124
+ const langs = specComposite.getLanguages();
125
+
126
+ return buildPlaygrounds?.({
127
+ spec,
128
+ langs,
129
+ auth,
130
+ playPath: playgroundsCachePath,
131
+ reportError(message: string) {
132
+ (reportError ?? console.error)(message);
133
+ },
134
+ })?.catch(() => {
135
+ console.error('Failed to build playgrounds');
136
+ });
137
+ })());
138
+ }
133
139
 
134
140
  return {
135
141
  name: 'stl-starlight-astro',
136
142
  hooks: {
137
- 'astro:config:setup': async ({ injectRoute, updateConfig, logger, command, config: astroConfig }) => {
143
+ 'astro:config:setup': async ({
144
+ injectRoute,
145
+ updateConfig,
146
+ logger: localLogger,
147
+ command,
148
+ config: astroConfig,
149
+ createCodegenDir,
150
+ }) => {
151
+ const logger = getSharedLogger({ fallback: localLogger });
138
152
  const projectDir = astroConfig.root.pathname;
153
+ astroBase = astroConfig.base;
139
154
 
140
- const middlewareFile = path.join(projectDir, 'middleware.stainless.ts');
155
+ specLoader = await startSpecLoader(pluginConfig, logger, createCodegenDir());
156
+
157
+ reportError = (message: string) => logger.error(message);
158
+
159
+ if (pluginConfig.experimentalPlaygrounds) {
160
+ playgroundsBase = pluginConfig.experimentalPlaygrounds.playgroundsBase;
161
+ }
162
+
163
+ const middlewareFileBase = path.join(projectDir, 'middleware.stainless');
164
+ const middlewareFile = ['.tsx', '.ts']
165
+ .map((ext) => middlewareFileBase + ext)
166
+ .find((f) => fs.existsSync(f));
141
167
 
142
168
  let vmMiddlewareExport = 'export const MIDDLEWARE = {};';
143
- if (fs.existsSync(middlewareFile)) {
144
- logger.info(`Loading middleware from ${middlewareFile}`);
169
+ if (middlewareFile) {
170
+ logger.debug(`Loading middleware from ${middlewareFile}`);
145
171
  vmMiddlewareExport = `export { default as MIDDLEWARE } from '${middlewareFile}';`;
146
172
  }
147
173
 
148
174
  injectRoute({
149
- pattern: `${pluginConfig.basePath}/[...slug].md`,
150
- entrypoint: '@stainless-api/docs/MarkdownRoute',
151
- prerender: command === 'build',
175
+ pattern: `${pluginConfig.basePath}/[...slug]/index.md`,
176
+ entrypoint: resolveSrcFile('/plugin/routes/markdown.ts'),
177
+ prerender: pluginConfig.experimentalPrerender ? command === 'build' : false,
152
178
  });
153
179
 
154
- const astroFile = command === 'build' ? 'DocsStaticRoute' : 'DocsRoute';
180
+ const astroFile = command === 'build' ? 'DocsStatic' : 'Docs';
155
181
  injectRoute({
156
182
  pattern: `${pluginConfig.basePath}/[...slug]`,
157
- // in prod I think this points to @stainless-starlight/components/docs.astro
158
- entrypoint: `@stainless-api/docs/${astroFile}`,
159
- prerender: command === 'build',
183
+ entrypoint: resolveSrcFile(`/plugin/routes/${astroFile}.astro`),
184
+ prerender: pluginConfig.experimentalPrerender ? command === 'build' : false,
160
185
  });
161
186
 
162
187
  injectRoute({
163
188
  pattern: pluginConfig.basePath,
164
- entrypoint: '@stainless-api/docs/OverviewRoute',
165
- prerender: command === 'build',
189
+ entrypoint: resolveSrcFile('/plugin/routes/Overview.astro'),
190
+ prerender: pluginConfig.experimentalPrerender ? command === 'build' : false,
166
191
  });
167
192
 
168
- // Remove starlight callout syntax in favor of our own callout component
169
- // updateConfig always deeply merges arrays so we need to mutate remarkPlugins directly
170
- // in order to remove plugins that starlight added
171
- astroConfig.markdown.remarkPlugins = astroConfig.markdown.remarkPlugins.filter((plugin, i, arr) => {
172
- if (typeof plugin !== 'function') return true;
173
- // remove:
174
- // 1. remarkDirective plugin
175
- if (plugin.name === 'remarkDirective') return false;
176
- // 2. directly followed by remarkAsides plugin (inner function named 'attacher')
177
- if (plugin.name === 'attacher') {
178
- const prev = arr[i - 1];
179
- if (typeof prev === 'function' && prev.name === 'remarkDirective') return false;
180
- }
181
- // 3. remarkDirectivesRestoration plugin
182
- if (plugin.name === 'remarkDirectivesRestoration') return false;
183
- return true;
184
- });
193
+ if (pluginConfig.llmsTxt.enabled) {
194
+ injectRoute({
195
+ pattern: '/llms.txt',
196
+ entrypoint: resolveSrcFile('/plugin/routes/llms.ts'),
197
+ prerender: pluginConfig.experimentalPrerender ? command === 'build' : false,
198
+ });
199
+ } else {
200
+ logger.info('LLMS.txt generation is disabled.');
201
+ }
185
202
 
186
203
  updateConfig({
187
204
  vite: {
188
- ssr: {
189
- noExternal: ['@stainless-api/ui-primitives'],
190
- },
191
- optimizeDeps: { include: ['@stainless-api/ui-primitives'] },
192
205
  plugins: [
206
+ makeAsyncVirtualModPlugin<typeof ReferenceSidebarsVirtualModule>(
207
+ 'virtual:stl-starlight-reference-sidebars',
208
+ async () => {
209
+ // we know specLoader exists here
210
+ const { specComposite } = await specLoader!.specPromise;
211
+
212
+ const sidebars = [...sidebarConfigs.entries()]
213
+ // produce all { id, language } combos with the attached config
214
+ // flattens to one item per language * id combo
215
+ .flatMap(([id, config]) =>
216
+ specComposite.listAllLanguagesAndIncludeSpecs().map((res) => ({
217
+ id,
218
+ config,
219
+ language: res.language,
220
+ spec: res.spec.data.sdkJson,
221
+ })),
222
+ )
223
+ // produce a sidebar for each
224
+ // later we will .find() the sidebar that matches the (id, language)
225
+ .map(({ id, config, language, spec }) => {
226
+ const configItemsBuilder = new SidebarConfigItemsBuilder(
227
+ spec,
228
+ language,
229
+ config.options,
230
+ );
231
+
232
+ let userSidebarConfig = configItemsBuilder.generateItems();
233
+ if (config.transformFn) {
234
+ const transformedSidebarConfig = config.transformFn(userSidebarConfig, language);
235
+ if (transformedSidebarConfig) userSidebarConfig = transformedSidebarConfig;
236
+ }
237
+
238
+ return {
239
+ id,
240
+ language,
241
+ // this has to run multpile times because it depends on the
242
+ // userSidebarConfig (which is per-id) and the language
243
+ entries: toStarlightSidebar({
244
+ basePath: path.posix.join(astroBase, pluginConfig.basePath),
245
+ spec,
246
+ entries: userSidebarConfig,
247
+ currentLanguage: language,
248
+ }),
249
+ };
250
+ });
251
+
252
+ return { sidebars };
253
+ },
254
+ ),
255
+ ...specLoader.vitePlugins,
193
256
  {
194
257
  name: 'stl-starlight-vite',
195
- configureServer(server) {
196
- for (const filePath of Object.values(devPaths)) {
197
- if (!filePath) {
198
- continue;
199
- }
200
- server.watcher.add(filePath);
258
+ buildStart() {
259
+ building = undefined;
260
+ },
261
+ async configureServer(server) {
262
+ if (playgroundsBase) {
263
+ buildPlaygrounds = (await server.ssrLoadModule(playgroundsBase + '/src/build.ts'))
264
+ .buildPlaygrounds;
201
265
  }
202
266
 
203
- server.watcher.on('change', async (changed) => {
204
- if (Object.values(devPaths).includes(changed)) {
205
- logger.info(`${changed} changed, reloading...`);
206
- cmsServer.invalidate();
207
- server.hot.send({
208
- type: 'full-reload',
209
- path: '*',
210
- });
211
- }
212
- });
267
+ // TODO: eventually - re-add support for watching local input changes (eg. reloading when OAS/config files change)
213
268
  },
214
269
  resolveId(id) {
215
270
  if (id === virtualId) {
216
271
  return resolvedId;
217
272
  }
273
+ if (id === 'virtual:stl-playground/unstable-update-language') {
274
+ return resolveSrcFile('plugin/languages.ts');
275
+ }
276
+ if (id === 'virtual:stl-playground/create') {
277
+ return fileURLToPath(
278
+ new URL(
279
+ pluginConfig.experimentalPlaygrounds
280
+ ? path.join(playgroundsBase!, '/src/create.tsx')
281
+ : './globalJs/create-playground.shim.ts',
282
+ import.meta.url,
283
+ ),
284
+ );
285
+ }
286
+ if (id === 'virtual:stl-playground/data') {
287
+ return fileURLToPath(
288
+ new URL(
289
+ pluginConfig.experimentalPlaygrounds
290
+ ? './globalJs/playground-data.ts'
291
+ : './globalJs/playground-data.shim.ts',
292
+ import.meta.url,
293
+ ),
294
+ );
295
+ }
296
+ if (id.startsWith('virtual:stl-playground/')) {
297
+ const result = path.join(
298
+ this.environment.getTopLevelConfig().cacheDir,
299
+ 'stl-playground',
300
+ id.slice('virtual:stl-playground/'.length),
301
+ );
302
+ const config = this.environment.getTopLevelConfig();
303
+ return startPlaygroundsBuild(path.join(config.cacheDir, 'stl-playground'))?.then(
304
+ () => result,
305
+ );
306
+ }
218
307
  },
219
308
  load(id) {
220
309
  if (id === resolvedId) {
221
310
  return [
222
311
  buildVirtualModuleString({
223
- BASE_PATH: pluginConfig.basePath,
224
- CMS_PORT,
312
+ RESOLVED_API_REFERENCE_PATH: path.posix.join(astroConfig.base, pluginConfig.basePath),
225
313
  EXCLUDE_LANGUAGES: pluginConfig.excludeLanguages,
226
314
  DEFAULT_LANGUAGE: pluginConfig.defaultLanguage,
227
315
  BREADCRUMB_CONFIG: pluginConfig.breadcrumbs,
@@ -229,21 +317,88 @@ async function stlStarlightAstroIntegration(
229
317
  HIGHLIGHT_THEMES: pluginConfig.highlighting.themes,
230
318
  CONTENT_PANEL_LAYOUT: pluginConfig.contentPanel.layout,
231
319
  EXPERIMENTAL_COLLAPSIBLE_SNIPPETS: pluginConfig.experimentalCollapsibleSnippets,
320
+ EXPERIMENTAL_COLLAPSIBLE_METHOD_DESCRIPTIONS:
321
+ pluginConfig.experimentalCollapsibleMethodDescriptions,
232
322
  PROPERTY_SETTINGS: pluginConfig.propertySettings,
233
- SEARCH: pluginConfig.search,
234
- INCLUDE_AI_DROPDOWN_OPTIONS: pluginConfig.includeAIDropdownOptions,
235
- }),
323
+ ENABLE_CONTEXT_MENU: pluginConfig.contextMenu,
324
+ EXPERIMENTAL_PLAYGROUNDS: !!pluginConfig.experimentalPlaygrounds,
325
+ EXPERIMENTAL_REQUEST_BUILDER: pluginConfig.experimentalRequestBuilder,
326
+ STAINLESS_PROJECT: pluginConfig.stainlessProject,
327
+ LLMS_TXT_DESCRIPTION: pluginConfig.llmsTxt.description,
328
+ LLMS_TXT_DETAIL_THRESHOLD: pluginConfig.llmsTxt.detailThreshold,
329
+ } satisfies Omit<typeof StlStarlightVirtualModule, 'MIDDLEWARE'>),
236
330
  vmMiddlewareExport,
237
331
  ].join('\n');
238
332
  }
239
333
  },
240
334
  },
335
+ prebundleWorkers({
336
+ include: [
337
+ '**/typescript/runner*',
338
+ '**/typescript/worker*',
339
+ '**/pyright.worker*',
340
+ '**/python/pyodide*',
341
+ ],
342
+ configureEsBuild(_, opts) {
343
+ opts.external ??= [];
344
+ opts.external.push('url');
345
+ opts.loader ??= {};
346
+ opts.loader['.wasm'] = 'dataurl';
347
+ return opts;
348
+ },
349
+ }),
241
350
  ],
242
351
  },
243
352
  });
244
353
  },
245
- 'astro:server:done': async () => {
246
- await cmsServer.destroy();
354
+ 'astro:build:start'({ logger }) {
355
+ collectedErrors = [];
356
+ reportError = (message: string) => {
357
+ logger.error(message);
358
+ collectedErrors!.push(message);
359
+ };
360
+ },
361
+ 'astro:build:done': async ({ dir, logger }) => {
362
+ const dist = fileURLToPath(dir);
363
+ const stainlessDir = path.join(dist, '_stainless');
364
+ await mkdir(stainlessDir, { recursive: true });
365
+ if (collectedErrors) {
366
+ for (const error of collectedErrors) {
367
+ logger.error(error);
368
+ }
369
+ collectedErrors = null;
370
+ }
371
+
372
+ const manifest = {
373
+ astroBase,
374
+ };
375
+ await writeFile(path.join(stainlessDir, 'stl-manifest.json'), JSON.stringify(manifest, null, 2));
376
+
377
+ const specComposite = await resolveSpecs();
378
+
379
+ await buildAlgoliaIndex({
380
+ specComposite,
381
+ logger,
382
+ });
383
+
384
+ // Generate a list of missing API routes to enable graceful handling of unimplemented SDK methods.
385
+ // When users switch languages in the docs, some API methods may not be implemented in the target SDK.
386
+ // Instead of showing a generic 404, we statically generate pages for these routes and mark them
387
+ // in this file so Cloudflare can serve them with a 404 status. These pages display helpful information
388
+ // about the missing method and provide links to SDKs where it is available.
389
+
390
+ // TODO: (multi-spec) support multiple specs
391
+ const spec = specComposite.listUniqueSpecs()[0]!.data.sdkJson;
392
+
393
+ const missingRoutes = generateMissingRouteList({
394
+ spec,
395
+ basePath: path.posix.join(astroBase, pluginConfig.basePath),
396
+ });
397
+ await mkdir(stainlessDir, { recursive: true });
398
+ await writeFile(
399
+ path.join(stainlessDir, 'missing-routes.json'),
400
+ JSON.stringify(missingRoutes, null, 2),
401
+ );
247
402
  },
248
403
  },
249
404
  };
@@ -253,24 +408,33 @@ export function stainlessStarlight(someUserConfig: SomeStainlessStarlightUserCon
253
408
  return {
254
409
  name: 'stl-starlight',
255
410
  hooks: {
256
- 'config:setup': async ({
411
+ 'config:setup': ({
257
412
  addIntegration,
258
413
  updateConfig,
259
414
  addRouteMiddleware,
260
415
  command,
261
416
  config: starlightConfig,
262
417
  astroConfig,
263
- logger,
418
+ logger: localLogger,
264
419
  }) => {
265
420
  if (command !== 'build' && command !== 'dev') {
266
421
  return;
267
422
  }
268
423
 
269
- const configParseResult = parseStarlightPluginConfig(someUserConfig, command);
424
+ const logger = getSharedLogger({ fallback: localLogger });
425
+
426
+ const configParseResult = parseStarlightPluginConfig(someUserConfig, {
427
+ command,
428
+ base: astroConfig.base,
429
+ });
270
430
  if (configParseResult.result === 'error') {
271
- logger.error(configParseResult.message);
431
+ const errorLines = configParseResult.message.split('\n');
432
+ for (const line of errorLines) {
433
+ logger.error(line);
434
+ }
272
435
  process.exit(1);
273
436
  }
437
+
274
438
  const config = configParseResult.config;
275
439
 
276
440
  const isReactLoaded = astroConfig.integrations.find(({ name }) => name === '@astrojs/react');
@@ -279,42 +443,52 @@ export function stainlessStarlight(someUserConfig: SomeStainlessStarlightUserCon
279
443
  addIntegration(react());
280
444
  }
281
445
 
282
- if (
283
- command === 'build' &&
284
- config.specRetrieverConfig.kind === 'local_spec_server_with_remote_files'
285
- ) {
286
- await buildAlgoliaIndex({
287
- version: config.specRetrieverConfig.version,
288
- apiKey: config.specRetrieverConfig.apiKey,
289
- });
290
- }
291
-
292
- addIntegration(await stlStarlightAstroIntegration(config));
446
+ // TODO: (multi-spec) re-add this? not strictly necessary to merge
447
+ // if ('apiKey' in config.specInputs) {
448
+ // if (!config.specInputs.apiKey) {
449
+ // logger.info(`Stainless credentials not loaded`);
450
+ // } else if (config.specInputs.apiKey.source === 'explicit-config') {
451
+ // logger.info(`Stainless credentials loaded from user config`);
452
+ // } else if (config.specInputs.apiKey.source === 'environment-variable') {
453
+ // logger.info('Stainless credentials loaded from `STAINLESS_API_KEY` environment variable');
454
+ // } else if (config.specInputs.apiKey.source === 'cli') {
455
+ // logger.info('Stainless credentials loaded from `stl` CLI');
456
+ // }
457
+ // })
293
458
 
294
459
  if (starlightConfig.sidebar) {
295
460
  // for pagination (https://starlight.astro.build/reference/configuration/#pagination) to work correctly
296
461
  // update the placeholder link to be correct
297
- const item = getAPIReferencePlaceholderItemFromSidebarConfig(starlightConfig.sidebar);
298
- if (item && typeof item === 'object' && 'link' in item) {
299
- item.link = config.basePath;
462
+ for (const placeholder of getAPIReferencePlaceholderItemFromSidebarConfig(
463
+ starlightConfig.sidebar,
464
+ )) {
465
+ if (placeholder && typeof placeholder === 'object' && 'link' in placeholder) {
466
+ placeholder.link = config.basePath;
467
+ }
300
468
  }
301
469
  }
302
470
 
303
- const currentExpressiveCode =
471
+ addIntegration(stlStarlightAstroIntegration(config));
472
+
473
+ const expressiveCodeConfig =
304
474
  typeof starlightConfig.expressiveCode === 'object' ? starlightConfig.expressiveCode : {};
305
- updateConfig({
306
- expressiveCode: {
307
- ...currentExpressiveCode,
308
- themes: [...(currentExpressiveCode.themes || []), 'github-light', 'github-dark'],
309
- },
310
- });
475
+
476
+ const themes = expressiveCodeConfig.themes
477
+ ? expressiveCodeConfig.themes
478
+ : (['github-light', 'github-dark'] satisfies (typeof expressiveCodeConfig)['themes']);
311
479
 
312
480
  updateConfig({
313
481
  sidebar: starlightConfig.sidebar,
482
+ ...(expressiveCodeConfig && {
483
+ expressiveCode: {
484
+ ...expressiveCodeConfig,
485
+ themes,
486
+ },
487
+ }),
314
488
  });
315
489
 
316
490
  addRouteMiddleware({
317
- entrypoint: '@stainless-api/docs/replaceSidebarPlaceholderMiddleware',
491
+ entrypoint: resolveSrcFile('/plugin/replaceSidebarPlaceholderMiddleware.ts'),
318
492
  order: 'post',
319
493
  });
320
494
  },
@@ -323,5 +497,7 @@ export function stainlessStarlight(someUserConfig: SomeStainlessStarlightUserCon
323
497
  }
324
498
 
325
499
  // Additional exports we want for Stainless <-> docs integration.
326
- export { parseStainlessPath } from '@stainless-api/docs-ui/src/routing';
327
- export { renderMarkdown } from '@stainless-api/docs-ui/src/markdown';
500
+ export { parseStainlessPath } from '@stainless-api/docs-ui/routing';
501
+ export { renderMarkdown } from '@stainless-api/docs-ui/markdown';
502
+
503
+ export { resolveSpec } from './specs/inputResolver';
@@ -1,4 +1,4 @@
1
- import type { DocsLanguage } from '@stainless-api/docs-ui/src/routing';
1
+ import type { DocsLanguage } from '@stainless-api/docs-ui/routing';
2
2
  import KotlinIcon from './assets/languages/kotlin.svg';
3
3
  import RubyIcon from './assets/languages/ruby.svg';
4
4
  import TerraformIcon from './assets/languages/terraform.svg';
@@ -7,6 +7,9 @@ import PythonIcon from './assets/languages/python.svg';
7
7
  import JavaIcon from './assets/languages/java.svg';
8
8
  import GoIcon from './assets/languages/go.svg';
9
9
  import CurlIcon from './assets/languages/curl.svg';
10
+ import CSharpIcon from './assets/languages/csharp.svg';
11
+ import PHPIcon from './assets/languages/php.svg';
12
+ import CLIIcon from './assets/languages/cli.svg';
10
13
 
11
14
  export const Languages: Record<
12
15
  DocsLanguage,
@@ -29,6 +32,9 @@ export const Languages: Record<
29
32
  http: { name: 'HTTP', icon: CurlIcon, alt: 'HTTP logo' },
30
33
  terraform: { name: 'Terraform', icon: TerraformIcon, alt: 'Terraform logo' },
31
34
  ruby: { name: 'Ruby', icon: RubyIcon, alt: 'Ruby logo' },
35
+ csharp: { name: 'C#', icon: CSharpIcon, alt: 'C# logo' },
36
+ cli: { name: 'CLI Tool', icon: CLIIcon, alt: 'CLI logo' },
37
+ php: { name: 'PHP', icon: PHPIcon, alt: 'PHP logo' },
32
38
  };
33
39
 
34
40
  export function generatePrefix(basePath: string, language: string) {
@@ -44,7 +50,7 @@ export function applyLanguageToLinks(basePath?: string, defaultLanguage?: string
44
50
  `[data-stldocs-overview],[data-stldocs-method],a.nav-link[href^='${basePath}']`,
45
51
  );
46
52
 
47
- for (var link of links) {
53
+ for (const link of links) {
48
54
  const href = link.getAttribute('href');
49
55
  const prefix = generatePrefix(basePath, language);
50
56
  if (href?.startsWith(basePath) && !href?.startsWith(prefix)) {