@stainless-api/docs 0.1.0-beta.10 → 0.1.0-beta.100

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