@stainless-api/docs 0.1.0-beta.13 → 0.1.0-beta.131

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 +1110 -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
@@ -1,15 +1,24 @@
1
1
  import path from 'path';
2
+ import { homedir } from 'os';
3
+ import { existsSync, readFileSync } from 'fs';
2
4
 
3
5
  import type { CreateShikiHighlighterOptions } from '@astrojs/markdown-remark';
4
- import type { DocsLanguage } from '@stainless-api/docs-ui/src/routing';
5
- import type { PropertySettingsType } from '@stainless-api/docs-ui/src/contexts';
6
- import type { InputFilePaths } from '../plugin/cms/server';
6
+ import type { DocsLanguage } from '@stainless-api/docs-ui/routing';
7
+ import type { PropertySettingsType } from '@stainless-api/docs-ui/contexts';
8
+ import { bold } from '../shared/terminalUtils';
9
+ import { resolveSpec, SpecInputResolver } from './specs/inputResolver';
10
+ import { specCache, SpecCacheResult } from './specs/generateSpec';
11
+
12
+ export type LanguageGenerateQuery = {
13
+ mode: 'exclude' | 'only';
14
+ list: DocsLanguage[];
15
+ };
7
16
 
8
- export type AstroCommand = 'dev' | 'build' | 'preview' | 'sync';
17
+ type AstroCommand = 'dev' | 'build' | 'preview' | 'sync';
9
18
 
10
- export type ContentLayout = 'double-pane' | 'single-pane';
19
+ type ContentLayout = 'double-pane' | 'single-pane';
11
20
 
12
- export type VersionUserConfig = {
21
+ type VersionUserConfig = {
13
22
  version: string;
14
23
  stainlessProject: string;
15
24
  branch: string;
@@ -18,7 +27,7 @@ export type VersionUserConfig = {
18
27
  type BreadcrumbUserConfig = {
19
28
  /**
20
29
  * Include the current page in the breadcrumb list.
21
- * Defaults to `false`.
30
+ * Default: `false`
22
31
  */
23
32
  includeCurrentPage?: boolean;
24
33
  };
@@ -26,7 +35,12 @@ type BreadcrumbUserConfig = {
26
35
  export type StainlessStarlightUserConfig = {
27
36
  /**
28
37
  * Optional api key for Stainless API.
29
- * If not provided, it will look for the STAINLESS_API_KEY environment variable.
38
+ * If not provided, we will handle Stainless auth via the `stl` CLI or look for the STAINLESS_API_KEY environment variable.
39
+ * Precedence:
40
+ * 1. Explicity `apiKey` option provided
41
+ * 2. `STAINLESS_API_KEY` environment variable
42
+ * 3. Login status from the `stl` CLI
43
+ * 4. Error (no auth found)
30
44
  */
31
45
  apiKey?: string;
32
46
 
@@ -35,6 +49,19 @@ export type StainlessStarlightUserConfig = {
35
49
  */
36
50
  stainlessProject: string;
37
51
 
52
+ /**
53
+ * Powerful configuration options for customized use cases
54
+ */
55
+ advanced?: {
56
+ /**
57
+ *
58
+ * More advanced replacement for versions, basePath, and excludeLanguages.
59
+ */
60
+ overrideSpecs?: {
61
+ specs: SpecInputResolver[];
62
+ };
63
+ };
64
+
38
65
  /**
39
66
  * Optional list of versions to render in the API reference.
40
67
  */
@@ -42,8 +69,8 @@ export type StainlessStarlightUserConfig = {
42
69
 
43
70
  /**
44
71
  * Optional mount point for API reference docs.
45
- * Defaults to `/api`.
46
72
  * Example: `/my-api` → docs available at `/my-api/…`.
73
+ * @default `/api`
47
74
  */
48
75
  basePath?: string;
49
76
 
@@ -55,8 +82,8 @@ export type StainlessStarlightUserConfig = {
55
82
 
56
83
  /**
57
84
  * Optional language to treat as the default when the user hasn't selected one.
58
- * Defaults to `"http"` when none is provided.
59
- * Example: `"python"`
85
+ * Example: `'python'`
86
+ * @default 'http'
60
87
  */
61
88
  defaultLanguage?: DocsLanguage;
62
89
 
@@ -90,7 +117,7 @@ export type StainlessStarlightUserConfig = {
90
117
  contentPanel?: {
91
118
  /**
92
119
  * Optional layout for the content panel.
93
- * Defaults to `"double-pane"`.
120
+ * @default 'double-pane'
94
121
  */
95
122
  layout?: ContentLayout;
96
123
  };
@@ -100,78 +127,261 @@ export type StainlessStarlightUserConfig = {
100
127
  */
101
128
  propertySettings?: PropertySettingsType;
102
129
 
103
- /**
104
- * Options to control the documentation site's search functionality
105
- */
106
- search?: {
107
- /**
108
- * When set to `true`, the enableAISearch` setting turns on support for
109
- * LLM-based conversations with the API documentation
110
- */
111
- enableAISearch?: boolean;
112
- };
113
-
114
130
  /**
115
131
  * Enable experimental collapsible code snippets. Snippets will be collapsed by default for
116
132
  * single-pane and mobile layouts.
117
- * Defaults to `false`.
133
+ *
134
+ * @default false
118
135
  */
119
136
  experimentalCollapsibleSnippets?: boolean;
120
137
 
121
138
  /**
122
- * When set to `true`, a dropdown button will be included on all pages to allow users to
123
- * view or send markdown to an LLM.
124
- * Defaults to `false`.
139
+ * Enable experimental collapsible method descriptions. Method descriptions will be
140
+ * collapsed if their content exceeds a certain length.
141
+ *
142
+ * @default false
125
143
  */
126
- includeAIDropdownOptions?: boolean;
127
- };
144
+ experimentalCollapsibleMethodDescriptions?: boolean;
128
145
 
129
- export type ExternalSpecServerUserConfig = Omit<StainlessStarlightUserConfig, 'stainlessProject'> & {
130
- externalSpecServerUrl: string;
146
+ /**
147
+ * Whether to show the context menu with options like "Copy as Markdown" and "Open in ChatGPT".
148
+ *
149
+ * @default true
150
+ */
151
+
152
+ contextMenu?: boolean;
153
+
154
+ /**
155
+ * When set to `import playgrounds from '@stainless-api/playgrounds'`, code snippets will have a "Play" button,
156
+ * allowing users to edit and run code snippets in their browser.
157
+ */
158
+ experimentalPlaygrounds?: { playgroundsBase: string };
159
+
160
+ /** When set to true, enables the experimental request builder interface for testing API endpoints. */
161
+ experimentalRequestBuilder?: boolean;
162
+
163
+ /** Whether to prerender the api reference pages.
164
+ *
165
+ * @default true
166
+ */
167
+ experimentalPrerender?: boolean;
168
+
169
+ /**
170
+ * Configuration for the generated `/llms.txt` file.
171
+ */
172
+ llmsTxt?: {
173
+ /**
174
+ * Whether to disable the generated `/llms.txt` file.
175
+ *
176
+ * @default false
177
+ */
178
+ disabled?: boolean;
179
+ /**
180
+ * A short description of the site, used as the blockquote summary at the top of the file.
181
+ * Falls back to the top-level Starlight `description` field if not set.
182
+ */
183
+ description?: string;
184
+ /**
185
+ * The maximum number of total routes (prose + API reference) at which the file switches
186
+ * from compact mode (resources only) to detailed mode (resources and
187
+ * methods).
188
+ *
189
+ * @default 2000
190
+ */
191
+ detailThreshold?: number;
192
+ };
131
193
  };
132
194
 
133
- export type SomeStainlessStarlightUserConfig = StainlessStarlightUserConfig | ExternalSpecServerUserConfig;
195
+ // TODO: eventually? re-add support for external spec servers
196
+ // export type ExternalSpecServerUserConfig = Omit<StainlessStarlightUserConfig, 'stainlessProject'> & {
197
+ // externalSpecServerUrl: string;
198
+ // };
199
+
200
+ export type SomeStainlessStarlightUserConfig = StainlessStarlightUserConfig;
134
201
 
135
202
  function resolvePath(inputPath: string) {
136
203
  return path.resolve(process.cwd(), inputPath);
137
204
  }
138
205
 
139
- function getLocalFilePaths(command: AstroCommand): InputFilePaths | null {
206
+ function getLocalFilePaths(command: AstroCommand) {
140
207
  if (command !== 'dev') {
141
208
  return null;
142
209
  }
143
- if (!process.env.OPENAPI_PATH || !process.env.STAINLESS_SPEC_PATH) {
210
+
211
+ // eslint-disable-next-line turbo/no-undeclared-env-vars
212
+ const oasPath = process.env.OPENAPI_PATH;
213
+ // eslint-disable-next-line turbo/no-undeclared-env-vars
214
+ const configPath = process.env.STAINLESS_CONFIG_PATH;
215
+
216
+ if (!oasPath || !configPath) {
144
217
  return null;
145
218
  }
219
+
146
220
  return {
147
- oasPath: resolvePath(process.env.OPENAPI_PATH),
148
- configPath: resolvePath(process.env.STAINLESS_SPEC_PATH),
221
+ oasPath: resolvePath(oasPath),
222
+ configPath: resolvePath(configPath),
149
223
  };
150
224
  }
151
225
 
152
- export type SpecRetrieverConfig =
153
- | {
154
- kind: 'external_spec_server';
155
- specServerUrl: string;
156
- stainlessProject: null;
226
+ type ApiKeySource = 'explicit-config' | 'environment-variable' | 'cli';
227
+
228
+ export type LoadedApiKey = {
229
+ value: string;
230
+ source: ApiKeySource;
231
+ };
232
+
233
+ function parseAuthJson(authJsonStr: string) {
234
+ let json: unknown;
235
+ try {
236
+ json = JSON.parse(authJsonStr);
237
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
238
+ } catch (_error) {
239
+ return null;
240
+ }
241
+
242
+ if (typeof json !== 'object' || json === null) {
243
+ return null;
244
+ }
245
+ if (!('access_token' in json)) {
246
+ return null;
247
+ }
248
+ const accessToken = json['access_token'];
249
+ if (typeof accessToken !== 'string') {
250
+ return null;
251
+ }
252
+ return accessToken;
253
+ }
254
+
255
+ function loadApiKey(configValue: string | undefined): LoadedApiKey | null {
256
+ if (typeof configValue === 'string') {
257
+ return { value: configValue, source: 'explicit-config' };
258
+ }
259
+ if (process.env.STAINLESS_API_KEY) {
260
+ return { value: process.env.STAINLESS_API_KEY, source: 'environment-variable' };
261
+ }
262
+
263
+ const homeDirPath = homedir();
264
+
265
+ const authJsonPath = path.join(homeDirPath, '.config', 'stainless', 'auth.json');
266
+
267
+ if (!existsSync(authJsonPath)) {
268
+ return null;
269
+ }
270
+
271
+ const authJsonStr = readFileSync(authJsonPath, 'utf-8');
272
+ const accessToken = parseAuthJson(authJsonStr);
273
+ if (!accessToken) {
274
+ return null;
275
+ }
276
+
277
+ return { value: accessToken, source: 'cli' };
278
+ }
279
+
280
+ /** @public but discouraged - used by cloudflare via relative import */
281
+ export function forceLoadStainlessCredentials(): LoadedApiKey {
282
+ const v = loadApiKey(undefined);
283
+ if (!v) {
284
+ throw new Error(`Failed to load Stainless credentials.`);
285
+ }
286
+ return v;
287
+ }
288
+
289
+ type AstroOptions = {
290
+ command: AstroCommand;
291
+ base: string;
292
+ };
293
+
294
+ type ResolvedAPIConfigEntry = {
295
+ loadSpecs: () => Promise<SpecCacheResult[]>;
296
+ };
297
+
298
+ function makeSimpleAPIConfig(
299
+ partial: SomeStainlessStarlightUserConfig,
300
+ astroOptions: AstroOptions,
301
+ ): ResolvedAPIConfigEntry {
302
+ if (!('stainlessProject' in partial)) {
303
+ throw new Error('You must provide a stainlessProject when using Stainless Starlight');
304
+ }
305
+
306
+ const excludedLanguages = partial.excludeLanguages ?? [];
307
+
308
+ const apiKey = loadApiKey(partial.apiKey);
309
+
310
+ function getResolver() {
311
+ const localFilePaths = getLocalFilePaths(astroOptions.command);
312
+ if (localFilePaths) {
313
+ return resolveSpec.fromFiles({
314
+ oasPath: localFilePaths.oasPath,
315
+ configPath: localFilePaths.configPath,
316
+ languageOverrides: {
317
+ mode: 'exclude',
318
+ list: excludedLanguages,
319
+ },
320
+ stainlessProject: partial.stainlessProject,
321
+ });
157
322
  }
158
- | {
159
- kind: 'local_spec_server_with_files';
160
- stainlessProject: string;
161
- devPaths: InputFilePaths;
162
- apiKey: string | null;
163
- version: VersionUserConfig;
323
+
324
+ if (!apiKey) {
325
+ throw new Error(
326
+ [
327
+ bold(
328
+ 'No Stainless credentials found. Please choose one of the following options to authenticate with Stainless:',
329
+ ),
330
+ '- Run `stl auth login` to authenticate via the Stainless CLI',
331
+ '- Provide a Stainless API key via the `STAINLESS_API_KEY` environment variable (eg. in a .env file)',
332
+ '- Set the `apiKey` option in the Stainless Docs config',
333
+ ].join('\n'),
334
+ );
164
335
  }
165
- | {
166
- kind: 'local_spec_server_with_remote_files';
167
- stainlessProject: string;
168
- apiKey: string;
169
- version: VersionUserConfig;
336
+ return resolveSpec.fromStainlessApi({
337
+ stainlessProject: partial.stainlessProject,
338
+ branch: partial.versions?.[0]?.branch ?? 'main',
339
+ apiKey: apiKey,
340
+ languageOverrides: {
341
+ mode: 'exclude',
342
+ list: excludedLanguages,
343
+ },
344
+ });
345
+ }
346
+
347
+ const resolver = getResolver();
348
+
349
+ return {
350
+ loadSpecs: async function loadSpecs() {
351
+ const inputs = await resolver.resolve({ apiKey });
352
+
353
+ const result = await specCache.get(inputs);
354
+
355
+ return [result];
356
+ },
357
+ };
358
+ }
359
+
360
+ function loadAPIConfig(
361
+ partial: SomeStainlessStarlightUserConfig,
362
+ astroOptions: AstroOptions,
363
+ ): ResolvedAPIConfigEntry {
364
+ if (partial.advanced?.overrideSpecs) {
365
+ const overrides = partial.advanced.overrideSpecs;
366
+ const apiKey = loadApiKey(partial.apiKey);
367
+ return {
368
+ loadSpecs: async function loadSpecs() {
369
+ const specsPromises = overrides.specs.map(async (spec) => {
370
+ const inputs = await spec.resolve({ apiKey });
371
+ return await specCache.get(inputs);
372
+ });
373
+
374
+ return await Promise.all(specsPromises);
375
+ },
170
376
  };
377
+ }
378
+ return makeSimpleAPIConfig(partial, astroOptions);
379
+ }
171
380
 
172
- function normalizeConfig(partial: SomeStainlessStarlightUserConfig, command: AstroCommand) {
381
+ function normalizeConfig(partial: SomeStainlessStarlightUserConfig, astroOptions: AstroOptions) {
173
382
  const configWithDefaults = {
174
383
  basePath: partial.basePath ?? '/api',
384
+ astroBase: astroOptions.base,
175
385
  excludeLanguages: partial.excludeLanguages ?? [],
176
386
  defaultLanguage: partial.defaultLanguage ?? 'http',
177
387
  breadcrumbs: {
@@ -188,86 +398,51 @@ function normalizeConfig(partial: SomeStainlessStarlightUserConfig, command: Ast
188
398
  layout: partial.contentPanel?.layout ?? 'double-pane',
189
399
  },
190
400
  experimentalCollapsibleSnippets: partial.experimentalCollapsibleSnippets ?? false,
401
+ experimentalCollapsibleMethodDescriptions: partial.experimentalCollapsibleMethodDescriptions ?? false,
191
402
  propertySettings: {
192
403
  types: partial.propertySettings?.types ?? 'rich',
193
404
  collapseDescription: partial.propertySettings?.collapseDescription ?? true,
405
+ showTitle: partial.propertySettings?.showTitle ?? false,
194
406
  expandDepth: partial.propertySettings?.expandDepth ?? 0,
195
407
  includeModelProperties: partial.propertySettings?.includeModelProperties ?? true,
196
408
  },
197
- search: {
198
- enableAISearch: partial.search?.enableAISearch ?? false,
409
+ contextMenu: partial.contextMenu ?? true,
410
+ experimentalPlaygrounds: partial.experimentalPlaygrounds ?? undefined,
411
+ experimentalRequestBuilder: partial.experimentalRequestBuilder ?? false,
412
+ experimentalPrerender: partial.experimentalPrerender ?? true,
413
+ stainlessProject: partial.stainlessProject,
414
+ llmsTxt: {
415
+ enabled: partial.llmsTxt?.disabled ?? true,
416
+ description: partial.llmsTxt?.description ?? null,
417
+ detailThreshold: partial.llmsTxt?.detailThreshold ?? 2000,
199
418
  },
200
- includeAIDropdownOptions: partial.includeAIDropdownOptions ?? false,
201
419
  };
202
420
 
203
- function getSpecRetrieverConfig(): SpecRetrieverConfig {
204
- if ('externalSpecServerUrl' in partial) {
205
- return {
206
- kind: 'external_spec_server',
207
- specServerUrl: partial.externalSpecServerUrl,
208
- stainlessProject: null,
209
- };
210
- }
211
-
212
- if (!('stainlessProject' in partial)) {
213
- throw new Error('You must provide a stainlessProject when using Stainless Starlight');
214
- }
215
-
216
- const apiKey = partial.apiKey ?? process.env.STAINLESS_API_KEY ?? null;
217
-
218
- const version = {
219
- stainlessProject: partial.stainlessProject,
220
- branch: partial.versions?.[0]?.branch ?? 'main',
221
- version: partial.versions?.[0]?.version ?? 'v1',
222
- };
223
-
224
- const localFilePaths = getLocalFilePaths(command);
225
- if (localFilePaths) {
226
- return {
227
- kind: 'local_spec_server_with_files',
228
- devPaths: localFilePaths,
229
- stainlessProject: partial.stainlessProject,
230
- apiKey,
231
- version,
232
- };
233
- }
234
-
235
- if (!apiKey) {
236
- throw new Error(
237
- 'Please provide a Stainless API key via the STAINLESS_API_KEY environment variable or the apiKey option in the Stainless Starlight config.',
238
- );
239
- }
240
-
241
- return {
242
- kind: 'local_spec_server_with_remote_files',
243
- apiKey,
244
- stainlessProject: partial.stainlessProject,
245
- version,
246
- };
247
- }
248
-
249
- const specRetrieverConfig = getSpecRetrieverConfig();
421
+ const api = loadAPIConfig(partial, astroOptions);
250
422
 
251
423
  return {
252
424
  ...configWithDefaults,
253
- specRetrieverConfig,
425
+ api,
254
426
  };
255
427
  }
256
428
 
257
429
  export type NormalizedStainlessStarlightConfig = ReturnType<typeof normalizeConfig>;
258
430
 
259
431
  /*
260
- The goal of the code in this file is to take a user's config and normalize it.
432
+ The goal of the code in this file is to take a user's config and normalize it.
261
433
  Specifically: we want a single complete config format used throughout the internals of the plugin.
262
434
 
263
435
  We've tried to avoid any config values being optional/undefined. To accomplish this:
264
- - Any optional config values should have their defaults set here: eg. basePath defaults to /api
265
- - If a field is only used in certain contexts, we make each context a discriminated union (see SpecRetrieverConfig)
436
+ - Any optional config values should have their defaults set here: eg. basePath defaults to /api
437
+ - If a field is only used in certain contexts, we make each context a discriminated union (see SDKJSONInputs)
266
438
  - We prefer empty arrays over undefined/null
267
439
  */
268
- export function parseStarlightPluginConfig(partial: SomeStainlessStarlightUserConfig, command: AstroCommand) {
440
+ export function parseStarlightPluginConfig(
441
+ partial: SomeStainlessStarlightUserConfig,
442
+ astroOptions: AstroOptions,
443
+ ) {
269
444
  try {
270
- const config = normalizeConfig(partial, command);
445
+ const config = normalizeConfig(partial, astroOptions);
271
446
  return {
272
447
  result: 'success' as const,
273
448
  config,
@@ -0,0 +1,100 @@
1
+ import {
2
+ createHighlighter,
3
+ type HighlighterGeneric,
4
+ type ThemeInput,
5
+ type BundledTheme,
6
+ type BundledLanguage,
7
+ } from 'shiki';
8
+ import { HIGHLIGHT_THEMES } from 'virtual:stl-starlight-virtual-module';
9
+ import { SupportedLanguageSyntaxes } from '@stainless-api/docs-ui/routing';
10
+ import type { CreateShikiHighlighterOptions } from '@astrojs/markdown-remark';
11
+
12
+ const STAINLESS_DOCS_JSON_THEME = {
13
+ name: 'stainless-docs-json',
14
+ colors: {
15
+ 'editor.background': 'var(--stl-color-background)',
16
+ 'editor.foreground': 'var(--stl-color-foreground)',
17
+ },
18
+
19
+ tokenColors: [
20
+ {
21
+ scope: ['comment', 'punctuation.definition.comment'],
22
+ settings: { foreground: 'var(--stl-color-foreground-muted)' },
23
+ },
24
+ // numbers, booleans, null
25
+ {
26
+ scope: ['constant.numeric', 'constant.language'],
27
+ settings: { foreground: 'var(--stl-color-orange-foreground)' },
28
+ },
29
+ // strings
30
+ {
31
+ scope: ['string', 'string.quoted', 'string.template'],
32
+ settings: { foreground: 'var(--stl-color-green-foreground)' },
33
+ },
34
+ // Keys, brackets
35
+ {
36
+ scope: ['support.type', 'meta'],
37
+ settings: { foreground: 'var(--stl-color-foreground)' },
38
+ },
39
+ // brackets
40
+ {
41
+ scope: ['meta'],
42
+ settings: { foreground: 'var(--stl-color-foreground-muted)' },
43
+ },
44
+ // built-in types
45
+ {
46
+ scope: ['support.type.builtin'],
47
+ settings: { foreground: 'var(--stl-color-purple-foreground)' },
48
+ },
49
+ ],
50
+ } satisfies ThemeInput;
51
+
52
+ // singleton
53
+ let astroShikiHighlighter:
54
+ | HighlighterGeneric<BundledLanguage, BundledTheme>
55
+ | Promise<HighlighterGeneric<BundledLanguage, BundledTheme>>
56
+ | null = null;
57
+ async function getAstroHighlighter() {
58
+ if (astroShikiHighlighter) {
59
+ return astroShikiHighlighter;
60
+ }
61
+
62
+ astroShikiHighlighter = createHighlighter({
63
+ themes: [
64
+ HIGHLIGHT_THEMES?.dark ?? 'github-dark',
65
+ HIGHLIGHT_THEMES?.light ?? 'github-light',
66
+ STAINLESS_DOCS_JSON_THEME,
67
+ ],
68
+ langs: SupportedLanguageSyntaxes,
69
+ });
70
+
71
+ return astroShikiHighlighter;
72
+ }
73
+
74
+ function runHighlight({
75
+ highlighter,
76
+ content,
77
+ language,
78
+ themes,
79
+ }: {
80
+ highlighter: HighlighterGeneric<BundledLanguage, BundledTheme>;
81
+ content: string;
82
+ language?: string;
83
+ themes?: CreateShikiHighlighterOptions['themes'] | Record<string, 'stainless-docs-json'>;
84
+ }) {
85
+ return highlighter.codeToHtml(content, {
86
+ lang: language ?? 'javascript',
87
+ themes:
88
+ // Default to user-provided themes except in case of json
89
+ themes ??
90
+ (language === 'JSON'
91
+ ? { light: 'stainless-docs-json', dark: 'stainless-docs-json' }
92
+ : HIGHLIGHT_THEMES) ??
93
+ {},
94
+ });
95
+ }
96
+
97
+ export async function highlight(content: string, language?: string) {
98
+ const highlighter = await getAstroHighlighter();
99
+ return runHighlight({ highlighter, content, language });
100
+ }
@@ -0,0 +1,39 @@
1
+ import { createMarkdownProcessor, type MarkdownProcessor } from '@astrojs/markdown-remark';
2
+ import remarkGfmAlerts from 'remark-github-alerts';
3
+ import { HIGHLIGHT_THEMES } from 'virtual:stl-starlight-virtual-module';
4
+
5
+ // singleton
6
+ let astroMarkdownProcessor: MarkdownProcessor;
7
+ async function getAstroMarkdown() {
8
+ if (!astroMarkdownProcessor) {
9
+ astroMarkdownProcessor = await createMarkdownProcessor({
10
+ gfm: true,
11
+ remarkPlugins: [remarkGfmAlerts],
12
+ shikiConfig: { themes: HIGHLIGHT_THEMES },
13
+ });
14
+ }
15
+
16
+ return astroMarkdownProcessor;
17
+ }
18
+
19
+ export async function astroMarkdownRender(content: string) {
20
+ const md = await getAstroMarkdown();
21
+ const output = await md.render(content);
22
+
23
+ // Map GFM callouts to the closest Starlight equivalent
24
+ // TODO: this sucks; we should be rendering via ui-primitives callouts instead of ugly theme-incompatible starlight ones
25
+ output.code = output.code
26
+ .replaceAll('markdown-alert-caution', 'markdown-alert-danger')
27
+ .replaceAll('markdown-alert-warning', 'markdown-alert-caution')
28
+ .replaceAll('markdown-alert-important', 'markdown-alert-caution')
29
+ .replaceAll('markdown-alert-title', 'starlight-aside__title')
30
+ .replaceAll('markdown-alert-', 'starlight-aside--')
31
+ .replaceAll('markdown-alert', 'starlight-aside');
32
+
33
+ return output;
34
+ }
35
+
36
+ export async function astroMarkdownRenderText(content: string) {
37
+ const result = await astroMarkdownRender(content);
38
+ return result.code;
39
+ }
@@ -1,5 +1,7 @@
1
- import type { TransformRequestSnippetFn } from '@stainless-api/docs-ui/src/components/sdk';
1
+ import type { TransformRequestSnippetFn } from '@stainless-api/docs-ui/components/sdk';
2
+ import type { AppComponents } from '@stainless-api/docs-ui/contexts/component';
2
3
 
3
4
  export type StlStarlightMiddleware = {
4
5
  transformRequestSnippet?: TransformRequestSnippetFn;
6
+ componentOverrides?: Partial<AppComponents>;
5
7
  };