@stainless-api/docs 0.1.0-beta.87 → 0.1.0-beta.89

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 (37) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/eslint-suppressions.json +2 -22
  3. package/package.json +5 -7
  4. package/playground-virtual-modules.d.ts +96 -0
  5. package/plugin/buildAlgoliaIndex.ts +9 -7
  6. package/plugin/components/SDKSelect.astro +19 -17
  7. package/plugin/components/search/SearchIsland.tsx +4 -4
  8. package/plugin/globalJs/copy.ts +5 -2
  9. package/plugin/globalJs/navigation.ts +3 -3
  10. package/plugin/helpers/getDocsLanguages.ts +9 -0
  11. package/plugin/index.ts +112 -136
  12. package/plugin/loadPluginConfig.ts +36 -52
  13. package/plugin/react/Routing.tsx +5 -5
  14. package/plugin/referencePlaceholderUtils.ts +17 -14
  15. package/plugin/replaceSidebarPlaceholderMiddleware.ts +38 -31
  16. package/plugin/routes/Docs.astro +2 -2
  17. package/plugin/routes/DocsStatic.astro +2 -2
  18. package/plugin/routes/Overview.astro +2 -2
  19. package/plugin/routes/markdown.ts +3 -3
  20. package/plugin/{cms → sidebar-utils}/sidebar-builder.ts +10 -18
  21. package/plugin/specs/fetchSpecSSR.ts +21 -0
  22. package/plugin/specs/generateSpec.ts +50 -0
  23. package/plugin/specs/index.ts +238 -0
  24. package/shared/virtualModule.ts +54 -1
  25. package/stl-docs/components/Head.astro +4 -4
  26. package/stl-docs/components/sidebars/convertAstroSidebarToStl.tsx +32 -16
  27. package/stl-docs/fonts.ts +172 -0
  28. package/stl-docs/index.ts +7 -4
  29. package/stl-docs/loadStlDocsConfig.ts +4 -1
  30. package/stl-docs/proseMarkdown/proseMarkdownMiddleware.ts +3 -1
  31. package/theme.css +0 -1
  32. package/virtual-module.d.ts +23 -100
  33. package/plugin/cms/client.ts +0 -62
  34. package/plugin/cms/generate-spec.ts +0 -112
  35. package/plugin/cms/server.ts +0 -364
  36. package/styles/fonts.css +0 -83
  37. /package/plugin/{cms → specs}/worker.ts +0 -0
package/plugin/index.ts CHANGED
@@ -1,27 +1,26 @@
1
1
  import react from '@astrojs/react';
2
2
  import type { StarlightPlugin } from '@astrojs/starlight/types';
3
- import type { AstroIntegration, AstroIntegrationLogger } from 'astro';
3
+ import type { AstroIntegration } from 'astro';
4
4
  import type { BundledTheme } from 'shiki';
5
5
  import { config } from 'dotenv';
6
- import getPort from 'get-port';
7
- import { DevSpecServer, startDevServer, type SpecResp } from './cms/server';
8
6
  import { buildAlgoliaIndex } from './buildAlgoliaIndex';
9
7
  import {
10
8
  getAPIReferencePlaceholderItemFromSidebarConfig,
11
9
  makePlaceholderItems,
12
10
  } from './referencePlaceholderUtils';
13
- import type {
14
- GeneratedSidebarConfig,
15
- ReferenceSidebarConfigGenerateOptions,
16
- ReferenceSidebarConfigItem,
17
- } 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';
18
18
  import {
19
19
  parseStarlightPluginConfig,
20
20
  type NormalizedStainlessStarlightConfig,
21
21
  type SomeStainlessStarlightUserConfig,
22
- type SpecRetrieverConfig,
23
22
  } from './loadPluginConfig';
24
- import { buildVirtualModuleString } from '../shared/virtualModule';
23
+ import { buildVirtualModuleString, makeAsyncVirtualModPlugin } from '../shared/virtualModule';
25
24
  import type * as StlStarlightVirtualModule from 'virtual:stl-starlight-virtual-module';
26
25
  import path from 'path';
27
26
  import fs from 'fs';
@@ -30,6 +29,10 @@ import { resolveSrcFile } from '../resolveSrcFile';
30
29
  import { mkdir, writeFile } from 'fs/promises';
31
30
  import { fileURLToPath } from 'url';
32
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 { getDocsLanguages } from './helpers/getDocsLanguages';
33
36
  import { generateMissingRouteList } from '@stainless-api/docs-ui/routing';
34
37
 
35
38
  export { generateAPILink } from './generateAPIReferenceLink';
@@ -88,37 +91,8 @@ export function generateAPIReferenceItems(
88
91
  return makePlaceholderItems(id);
89
92
  }
90
93
 
91
- function tmpGetCMSServerConfig(specRetrieverConfig: SpecRetrieverConfig) {
92
- if (specRetrieverConfig.kind === 'external_spec_server') {
93
- throw new Error('External spec server is not yet supported');
94
- }
95
- if (specRetrieverConfig.kind === 'local_spec_server_with_files') {
96
- if (!specRetrieverConfig.apiKey) {
97
- throw new Error('API key is required');
98
- }
99
- return {
100
- apiKey: specRetrieverConfig.apiKey,
101
- version: specRetrieverConfig.version,
102
- devPaths: specRetrieverConfig.devPaths,
103
- };
104
- }
105
-
106
- if (specRetrieverConfig.kind === 'local_spec_server_with_remote_files') {
107
- return {
108
- apiKey: specRetrieverConfig.apiKey,
109
- version: specRetrieverConfig.version,
110
- devPaths: {
111
- oasPath: undefined,
112
- configPath: undefined,
113
- },
114
- };
115
- }
116
- throw new Error('Invalid spec retriever config');
117
- }
118
-
119
94
  async function stlStarlightAstroIntegration(
120
95
  pluginConfig: NormalizedStainlessStarlightConfig,
121
- stlStarlightPluginLogger: AstroIntegrationLogger,
122
96
  ): Promise<AstroIntegration> {
123
97
  const virtualId = `virtual:stl-starlight-virtual-module`;
124
98
  // The '\0' prefix tells Vite “this is a virtual module” and prevents it from being resolved again.
@@ -127,11 +101,15 @@ async function stlStarlightAstroIntegration(
127
101
  let buildPlaygrounds;
128
102
  let astroBase = '/';
129
103
 
130
- const CMS_PORT = await getPort();
131
-
132
- const { apiKey, version, devPaths } = tmpGetCMSServerConfig(pluginConfig.specRetrieverConfig);
133
-
134
- let cmsServer: DevSpecServer | undefined;
104
+ let specLoader: SpecLoader | undefined;
105
+ async function getSpec() {
106
+ if (!specLoader) throw new Error('Expected spec loader to exist');
107
+ const result = await specLoader.specPromise;
108
+ return {
109
+ spec: result.spec.data,
110
+ auth: result.spec.auth,
111
+ };
112
+ }
135
113
 
136
114
  let building: Promise<void> | undefined;
137
115
  let reportError: ((message: string) => void) | null = null;
@@ -141,17 +119,9 @@ async function stlStarlightAstroIntegration(
141
119
  if (!pluginConfig.experimentalPlaygrounds) return;
142
120
  if (building) return building;
143
121
  return (building = (async () => {
144
- const { data: spec, auth } = (await (
145
- await fetch(`http://localhost:${CMS_PORT}/retrieve_spec`, {
146
- method: 'POST',
147
- body: JSON.stringify({}),
148
- })
149
- ).json()) as SpecResp;
150
- if (!spec || !auth) throw new Error('expected spec');
151
-
152
- const langs = (spec.docs?.languages ?? ['http']).filter(
153
- (lang) => !pluginConfig.excludeLanguages?.includes(lang),
154
- );
122
+ const { spec, auth } = await getSpec();
123
+
124
+ const langs = getDocsLanguages(spec, pluginConfig.excludeLanguages);
155
125
 
156
126
  await buildPlaygrounds!({
157
127
  spec,
@@ -180,28 +150,13 @@ async function stlStarlightAstroIntegration(
180
150
  const projectDir = astroConfig.root.pathname;
181
151
  astroBase = astroConfig.base;
182
152
 
183
- const codegenDir = createCodegenDir().pathname;
184
-
185
- let specCacheDirectory: string | null = null;
186
- if (command === 'dev') {
187
- specCacheDirectory = path.join(codegenDir, 'spec_cache');
188
- fs.mkdirSync(specCacheDirectory, { recursive: true });
189
- }
190
-
191
- cmsServer = startDevServer({
192
- port: CMS_PORT,
193
- apiKey: apiKey.value,
194
- version,
195
- devPaths,
196
- logger: stlStarlightPluginLogger,
197
- specCacheDirectory,
198
- getGeneratedSidebarConfig: (id: number) => {
199
- const config = sidebarConfigs.get(id);
200
- if (!config) {
201
- return null;
202
- }
203
- return config;
204
- },
153
+ specLoader = await startSpecLoader(pluginConfig, logger, createCodegenDir());
154
+ specLoader.specPromise.then((specResult) => {
155
+ if (specResult.result === 'generated') {
156
+ logger.info(`Generated new SDKJSON`);
157
+ } else if (specResult.result === 'exists') {
158
+ logger.info(`Loaded SDKJSON`);
159
+ }
205
160
  });
206
161
 
207
162
  reportError = (message: string) => logger.error(message);
@@ -240,6 +195,55 @@ async function stlStarlightAstroIntegration(
240
195
  updateConfig({
241
196
  vite: {
242
197
  plugins: [
198
+ makeAsyncVirtualModPlugin<typeof ReferenceSidebarsVirtualModule>(
199
+ 'virtual:stl-starlight-reference-sidebars',
200
+ async () => {
201
+ const { spec } = await getSpec();
202
+ const languages = getDocsLanguages(spec, pluginConfig.excludeLanguages);
203
+
204
+ const sidebars = [...sidebarConfigs.entries()]
205
+ // produce all { id, language } combos with the attached config
206
+ // flattens to one item per language * id combo
207
+ .flatMap(([id, config]) =>
208
+ languages.map((language) => ({
209
+ id,
210
+ config,
211
+ language,
212
+ })),
213
+ )
214
+ // produce a sidebar for each
215
+ // later we will .find() the sidebar that matches the (id, language)
216
+ .map(({ id, config, language }) => {
217
+ const configItemsBuilder = new SidebarConfigItemsBuilder(
218
+ spec,
219
+ language,
220
+ config.options,
221
+ );
222
+
223
+ let userSidebarConfig = configItemsBuilder.generateItems();
224
+ if (config.transformFn) {
225
+ const transformedSidebarConfig = config.transformFn(userSidebarConfig, language);
226
+ if (transformedSidebarConfig) userSidebarConfig = transformedSidebarConfig;
227
+ }
228
+
229
+ return {
230
+ id,
231
+ language,
232
+ // this has to run multpile times because it depends on the
233
+ // userSidebarConfig (which is per-id) and the language
234
+ entries: toStarlightSidebar({
235
+ basePath: path.posix.join(astroBase, pluginConfig.basePath), // TODO, is this right?
236
+ spec,
237
+ entries: userSidebarConfig,
238
+ currentLanguage: language,
239
+ }),
240
+ };
241
+ });
242
+
243
+ return { sidebars };
244
+ },
245
+ ),
246
+ specLoader.vitePlugin,
243
247
  {
244
248
  name: 'stl-starlight-vite',
245
249
  buildStart() {
@@ -251,23 +255,7 @@ async function stlStarlightAstroIntegration(
251
255
  .buildPlaygrounds;
252
256
  }
253
257
 
254
- for (const filePath of Object.values(devPaths)) {
255
- if (!filePath) {
256
- continue;
257
- }
258
- server.watcher.add(filePath);
259
- }
260
-
261
- server.watcher.on('change', async (changed) => {
262
- if (Object.values(devPaths).includes(changed)) {
263
- logger.info(`${changed} changed, reloading...`);
264
- cmsServer?.invalidate();
265
- server.hot.send({
266
- type: 'full-reload',
267
- path: '*',
268
- });
269
- }
270
- });
258
+ // TODO: eventually - re-add support for watching local input changes (eg. reloading when OAS/config files change)
271
259
  },
272
260
  resolveId(id) {
273
261
  if (id === virtualId) {
@@ -312,9 +300,8 @@ async function stlStarlightAstroIntegration(
312
300
  if (id === resolvedId) {
313
301
  return [
314
302
  buildVirtualModuleString({
315
- BASE_PATH: path.posix.join(astroConfig.base, pluginConfig.basePath),
316
- CMS_PORT,
317
- EXCLUDE_LANGUAGES: ['php', ...pluginConfig.excludeLanguages],
303
+ RESOLVED_API_REFERENCE_PATH: path.posix.join(astroConfig.base, pluginConfig.basePath),
304
+ EXCLUDE_LANGUAGES: pluginConfig.excludeLanguages,
318
305
  DEFAULT_LANGUAGE: pluginConfig.defaultLanguage,
319
306
  BREADCRUMB_CONFIG: pluginConfig.breadcrumbs,
320
307
  EXPAND_RESOURCES: pluginConfig.expandResources,
@@ -327,7 +314,7 @@ async function stlStarlightAstroIntegration(
327
314
  ENABLE_CONTEXT_MENU: pluginConfig.contextMenu,
328
315
  EXPERIMENTAL_PLAYGROUNDS: !!pluginConfig.experimentalPlaygrounds,
329
316
  EXPERIMENTAL_REQUEST_BUILDER: pluginConfig.experimentalRequestBuilder,
330
- STAINLESS_PROJECT: version.stainlessProject,
317
+ STAINLESS_PROJECT: pluginConfig.specInputs.project,
331
318
  } satisfies Omit<typeof StlStarlightVirtualModule, 'MIDDLEWARE'>),
332
319
  vmMiddlewareExport,
333
320
  ].join('\n');
@@ -353,9 +340,6 @@ async function stlStarlightAstroIntegration(
353
340
  },
354
341
  });
355
342
  },
356
- 'astro:server:done': async () => {
357
- await cmsServer?.destroy();
358
- },
359
343
  'astro:build:start'({ logger }) {
360
344
  collectedErrors = [];
361
345
  reportError = (message: string) => {
@@ -384,25 +368,16 @@ async function stlStarlightAstroIntegration(
384
368
  // Instead of showing a generic 404, we statically generate pages for these routes and mark them
385
369
  // in this file so Cloudflare can serve them with a 404 status. These pages display helpful information
386
370
  // about the missing method and provide links to SDKs where it is available.
387
- const { data: spec } = (await (
388
- await fetch(`http://localhost:${CMS_PORT}/retrieve_spec`, {
389
- method: 'POST',
390
- body: JSON.stringify({}),
391
- })
392
- ).json()) as SpecResp;
393
- if (spec) {
394
- const missingRoutes = await generateMissingRouteList({
395
- spec,
396
- basePath: path.posix.join(astroBase, pluginConfig.basePath),
397
- });
398
- await mkdir(stainlessDir, { recursive: true });
399
- await writeFile(
400
- path.join(stainlessDir, 'missing-routes.json'),
401
- JSON.stringify(missingRoutes, null, 2),
402
- );
403
- } else {
404
- console.log('Could not retrieve spec for missing routes check');
405
- }
371
+ const { spec } = await getSpec();
372
+ const missingRoutes = generateMissingRouteList({
373
+ spec,
374
+ basePath: path.posix.join(astroBase, pluginConfig.basePath),
375
+ });
376
+ await mkdir(stainlessDir, { recursive: true });
377
+ await writeFile(
378
+ path.join(stainlessDir, 'missing-routes.json'),
379
+ JSON.stringify(missingRoutes, null, 2),
380
+ );
406
381
  },
407
382
  },
408
383
  };
@@ -444,40 +419,41 @@ export function stainlessStarlight(someUserConfig: SomeStainlessStarlightUserCon
444
419
  addIntegration(react());
445
420
  }
446
421
 
447
- if ('apiKey' in config.specRetrieverConfig) {
448
- if (!config.specRetrieverConfig.apiKey) {
422
+ if ('apiKey' in config.specInputs) {
423
+ if (!config.specInputs.apiKey) {
449
424
  logger.info(`Stainless credentials not loaded`);
450
- } else if (config.specRetrieverConfig.apiKey.source === 'explicit-config') {
425
+ } else if (config.specInputs.apiKey.source === 'explicit-config') {
451
426
  logger.info(`Stainless credentials loaded from user config`);
452
- } else if (config.specRetrieverConfig.apiKey.source === 'environment-variable') {
427
+ } else if (config.specInputs.apiKey.source === 'environment-variable') {
453
428
  logger.info('Stainless credentials loaded from `STAINLESS_API_KEY` environment variable');
454
- } else if (config.specRetrieverConfig.apiKey.source === 'cli') {
429
+ } else if (config.specInputs.apiKey.source === 'cli') {
455
430
  logger.info('Stainless credentials loaded from `stl` CLI');
456
431
  }
457
432
  }
458
433
 
459
- if (
460
- command === 'build' &&
461
- config.specRetrieverConfig.kind === 'local_spec_server_with_remote_files'
462
- ) {
434
+ if (command === 'build' && config.specInputs.kind === 'stainless_api_inputs') {
463
435
  await buildAlgoliaIndex({
464
- version: config.specRetrieverConfig.version,
465
- apiKey: config.specRetrieverConfig.apiKey.value,
436
+ stainlessProject: config.specInputs.project,
437
+ branch: config.specInputs.branch,
438
+ apiKey: config.specInputs.apiKey.value,
466
439
  logger,
467
440
  });
468
441
  }
469
442
 
470
- addIntegration(await stlStarlightAstroIntegration(config, logger));
471
-
472
443
  if (starlightConfig.sidebar) {
473
444
  // for pagination (https://starlight.astro.build/reference/configuration/#pagination) to work correctly
474
445
  // update the placeholder link to be correct
475
- const item = getAPIReferencePlaceholderItemFromSidebarConfig(starlightConfig.sidebar);
476
- if (item && typeof item === 'object' && 'link' in item) {
477
- item.link = config.basePath;
446
+ for (const placeholder of getAPIReferencePlaceholderItemFromSidebarConfig(
447
+ starlightConfig.sidebar,
448
+ )) {
449
+ if (placeholder && typeof placeholder === 'object' && 'link' in placeholder) {
450
+ placeholder.link = config.basePath;
451
+ }
478
452
  }
479
453
  }
480
454
 
455
+ addIntegration(await stlStarlightAstroIntegration(config));
456
+
481
457
  const expressiveCodeConfig =
482
458
  typeof starlightConfig.expressiveCode === 'object' ? starlightConfig.expressiveCode : {};
483
459
 
@@ -5,7 +5,6 @@ import { existsSync, readFileSync } from 'fs';
5
5
  import type { CreateShikiHighlighterOptions } from '@astrojs/markdown-remark';
6
6
  import type { DocsLanguage } from '@stainless-api/docs-ui/routing';
7
7
  import type { PropertySettingsType } from '@stainless-api/docs-ui/contexts';
8
- import type { InputFilePaths } from '../plugin/cms/server';
9
8
  import { bold } from '../shared/terminalUtils';
10
9
 
11
10
  export type AstroCommand = 'dev' | 'build' | 'preview' | 'sync';
@@ -158,7 +157,7 @@ function resolvePath(inputPath: string) {
158
157
  return path.resolve(process.cwd(), inputPath);
159
158
  }
160
159
 
161
- function getLocalFilePaths(command: AstroCommand): InputFilePaths | null {
160
+ function getLocalFilePaths(command: AstroCommand) {
162
161
  if (command !== 'dev') {
163
162
  return null;
164
163
  }
@@ -185,26 +184,6 @@ export type LoadedApiKey = {
185
184
  source: ApiKeySource;
186
185
  };
187
186
 
188
- export type SpecRetrieverConfig =
189
- | {
190
- kind: 'external_spec_server';
191
- specServerUrl: string;
192
- stainlessProject: null;
193
- }
194
- | {
195
- kind: 'local_spec_server_with_files';
196
- stainlessProject: string;
197
- devPaths: InputFilePaths;
198
- apiKey: LoadedApiKey | null;
199
- version: VersionUserConfig;
200
- }
201
- | {
202
- kind: 'local_spec_server_with_remote_files';
203
- stainlessProject: string;
204
- apiKey: LoadedApiKey;
205
- version: VersionUserConfig;
206
- };
207
-
208
187
  function parseAuthJson(authJsonStr: string) {
209
188
  let json: unknown;
210
189
  try {
@@ -252,10 +231,31 @@ function loadApiKey(configValue: string | undefined): LoadedApiKey | null {
252
231
  return { value: accessToken, source: 'cli' };
253
232
  }
254
233
 
234
+ export type SDKJSONInputs =
235
+ | {
236
+ kind: 'file_inputs';
237
+ project: string;
238
+ oasPath: string;
239
+ configPath: string;
240
+ }
241
+ | {
242
+ kind: 'stainless_api_inputs';
243
+ project: string;
244
+ branch: string;
245
+ apiKey: LoadedApiKey;
246
+ }
247
+ | {
248
+ kind: 'string_inputs';
249
+ oasStr: string;
250
+ configStr: string;
251
+ project: string;
252
+ };
253
+
255
254
  function normalizeConfig(partial: SomeStainlessStarlightUserConfig, command: AstroCommand) {
256
255
  const configWithDefaults = {
257
256
  basePath: partial.basePath ?? '/api',
258
- excludeLanguages: partial.excludeLanguages ?? [],
257
+ // TODO: why are we excluding php; do we need to
258
+ excludeLanguages: ['php' as const, ...(partial.excludeLanguages ?? [])],
259
259
  defaultLanguage: partial.defaultLanguage ?? 'http',
260
260
  breadcrumbs: {
261
261
  includeCurrentPage: partial.breadcrumbs?.includeCurrentPage ?? false,
@@ -285,38 +285,23 @@ function normalizeConfig(partial: SomeStainlessStarlightUserConfig, command: Ast
285
285
  experimentalPrerender: partial.experimentalPrerender ?? true,
286
286
  };
287
287
 
288
- function getSpecRetrieverConfig(): SpecRetrieverConfig {
289
- if ('externalSpecServerUrl' in partial) {
290
- return {
291
- kind: 'external_spec_server',
292
- specServerUrl: partial.externalSpecServerUrl,
293
- stainlessProject: null,
294
- };
295
- }
296
-
288
+ function getSpecInputs(): SDKJSONInputs {
297
289
  if (!('stainlessProject' in partial)) {
298
290
  throw new Error('You must provide a stainlessProject when using Stainless Starlight');
299
291
  }
300
292
 
301
- const apiKey = loadApiKey(partial.apiKey);
302
-
303
- const version = {
304
- stainlessProject: partial.stainlessProject,
305
- branch: partial.versions?.[0]?.branch ?? 'main',
306
- version: partial.versions?.[0]?.version ?? 'v1',
307
- };
308
-
309
293
  const localFilePaths = getLocalFilePaths(command);
310
294
  if (localFilePaths) {
311
295
  return {
312
- kind: 'local_spec_server_with_files',
313
- devPaths: localFilePaths,
314
- stainlessProject: partial.stainlessProject,
315
- apiKey,
316
- version,
296
+ kind: 'file_inputs',
297
+ project: partial.stainlessProject,
298
+ oasPath: localFilePaths.oasPath,
299
+ configPath: localFilePaths.configPath,
317
300
  };
318
301
  }
319
302
 
303
+ const apiKey = loadApiKey(partial.apiKey);
304
+
320
305
  if (!apiKey) {
321
306
  throw new Error(
322
307
  [
@@ -329,20 +314,19 @@ function normalizeConfig(partial: SomeStainlessStarlightUserConfig, command: Ast
329
314
  ].join('\n'),
330
315
  );
331
316
  }
332
-
333
317
  return {
334
- kind: 'local_spec_server_with_remote_files',
318
+ kind: 'stainless_api_inputs',
319
+ project: partial.stainlessProject,
320
+ branch: partial.versions?.[0]?.branch ?? 'main',
335
321
  apiKey,
336
- stainlessProject: partial.stainlessProject,
337
- version,
338
322
  };
339
323
  }
340
324
 
341
- const specRetrieverConfig = getSpecRetrieverConfig();
325
+ const specInputs = getSpecInputs();
342
326
 
343
327
  return {
344
328
  ...configWithDefaults,
345
- specRetrieverConfig,
329
+ specInputs,
346
330
  };
347
331
  }
348
332
 
@@ -354,7 +338,7 @@ export type NormalizedStainlessStarlightConfig = ReturnType<typeof normalizeConf
354
338
 
355
339
  We've tried to avoid any config values being optional/undefined. To accomplish this:
356
340
  - Any optional config values should have their defaults set here: eg. basePath defaults to /api
357
- - If a field is only used in certain contexts, we make each context a discriminated union (see SpecRetrieverConfig)
341
+ - If a field is only used in certain contexts, we make each context a discriminated union (see SDKJSONInputs)
358
342
  - We prefer empty arrays over undefined/null
359
343
  */
360
344
  export function parseStarlightPluginConfig(partial: SomeStainlessStarlightUserConfig, command: AstroCommand) {
@@ -43,7 +43,7 @@ import {
43
43
  import { Dropdown } from '@stainless-api/docs/components';
44
44
 
45
45
  import {
46
- BASE_PATH,
46
+ RESOLVED_API_REFERENCE_PATH,
47
47
  EXCLUDE_LANGUAGES,
48
48
  EXPAND_RESOURCES,
49
49
  HIGHLIGHT_THEMES,
@@ -282,7 +282,7 @@ export function RenderLibraries({ metadata }: { metadata: SpecMetadata }) {
282
282
  language={language}
283
283
  version={data.version || ''}
284
284
  install={data.install || ''}
285
- links={{ repo: data.repo_url || '#', docs: `${BASE_PATH}/${language}` }}
285
+ links={{ repo: data.repo_url || '#', docs: `${RESOLVED_API_REFERENCE_PATH}/${language}` }}
286
286
  />
287
287
  ))}
288
288
  </ComponentProvider>
@@ -295,7 +295,7 @@ export function RenderSpecOverview({ spec, language }: { spec: SDKJSON.Spec; lan
295
295
  return (
296
296
  <DocsProvider spec={spec} language={language ?? 'node'}>
297
297
  <ComponentProvider components={componentOverrides}>
298
- <NavigationProvider basePath={BASE_PATH}>
298
+ <NavigationProvider basePath={RESOLVED_API_REFERENCE_PATH}>
299
299
  <MarkdownProvider render={renderMarkdown} highlight={highlight}>
300
300
  <div className={style.Overview}>
301
301
  {resources
@@ -351,7 +351,7 @@ export function RenderSpec({
351
351
  }}
352
352
  >
353
353
  <ComponentProvider components={componentOverrides}>
354
- <NavigationProvider basePath={BASE_PATH} selectedPath={path}>
354
+ <NavigationProvider basePath={RESOLVED_API_REFERENCE_PATH} selectedPath={path}>
355
355
  <MarkdownProvider render={renderMarkdown} highlight={highlight}>
356
356
  {
357
357
  <div className="stldocs-root stl-ui-not-prose">
@@ -359,7 +359,7 @@ export function RenderSpec({
359
359
  <SDKBreadcrumbs
360
360
  spec={spec as SDKJSON.Spec}
361
361
  currentPath={currentPath}
362
- basePath={BASE_PATH}
362
+ basePath={RESOLVED_API_REFERENCE_PATH}
363
363
  config={BREADCRUMB_CONFIG}
364
364
  />
365
365
  {ENABLE_CONTEXT_MENU && <AIDropdown />}
@@ -21,27 +21,30 @@ export function makePlaceholderItems(id: number) {
21
21
 
22
22
  type StarlightConfig = Parameters<typeof starlight>[0];
23
23
 
24
- type SidebarConfigEntry = Exclude<StarlightConfig['sidebar'], undefined>[number];
24
+ export type SidebarConfigEntry = Exclude<StarlightConfig['sidebar'], undefined>[number];
25
25
 
26
26
  export function getAPIReferencePlaceholderItemFromSidebarConfig(
27
27
  sidebar: SidebarConfigEntry[],
28
- ): SidebarConfigEntry | null {
29
- for (const item of sidebar) {
30
- if (typeof item === 'string') {
31
- continue;
32
- }
33
- if ('items' in item) {
34
- const found = getAPIReferencePlaceholderItemFromSidebarConfig(item.items);
35
- if (found) {
36
- return found;
28
+ ): SidebarConfigEntry[] {
29
+ const items: SidebarConfigEntry[] = [];
30
+
31
+ function recursiveGetPlaceholderItems(entries: SidebarConfigEntry[]) {
32
+ for (const item of entries) {
33
+ if (typeof item === 'string') {
34
+ continue;
35
+ }
36
+ if ('attrs' in item && item.attrs?.about === INTERNAL_REFERENCE_ENTRY_MARKER) {
37
+ items.push(item);
38
+ }
39
+ if ('items' in item) {
40
+ recursiveGetPlaceholderItems(item.items);
37
41
  }
38
- }
39
- if ('attrs' in item && item.attrs?.about === INTERNAL_REFERENCE_ENTRY_MARKER) {
40
- return item;
41
42
  }
42
43
  }
43
44
 
44
- return null;
45
+ recursiveGetPlaceholderItems(sidebar);
46
+
47
+ return items;
45
48
  }
46
49
 
47
50
  type SidebarEntry = StarlightRouteData['sidebar'][number];