@stainless-api/docs 0.1.0-beta.6 → 0.1.0-beta.60

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 (115) hide show
  1. package/CHANGELOG.md +476 -0
  2. package/README.md +1 -1
  3. package/eslint-suppressions.json +52 -0
  4. package/locals.d.ts +16 -0
  5. package/package.json +45 -40
  6. package/plugin/assets/languages/csharp.svg +1 -0
  7. package/plugin/buildAlgoliaIndex.ts +32 -7
  8. package/plugin/cms/server.ts +130 -58
  9. package/plugin/cms/sidebar-builder.ts +7 -26
  10. package/plugin/cms/worker.ts +83 -5
  11. package/plugin/components/MethodDescription.tsx +54 -0
  12. package/plugin/components/SDKSelect.astro +7 -87
  13. package/plugin/components/SnippetCode.tsx +53 -8
  14. package/plugin/components/search/SearchAlgolia.astro +14 -26
  15. package/plugin/components/search/SearchIsland.tsx +38 -24
  16. package/plugin/create-playground.shim.tsx +3 -0
  17. package/plugin/generateAPIReferenceLink.ts +2 -2
  18. package/plugin/globalJs/ai-dropdown-options.ts +235 -0
  19. package/plugin/globalJs/code-snippets.ts +15 -8
  20. package/plugin/globalJs/copy.ts +81 -16
  21. package/plugin/globalJs/method-descriptions.ts +33 -0
  22. package/plugin/globalJs/navigation.ts +7 -4
  23. package/plugin/index.ts +179 -35
  24. package/plugin/languages.ts +5 -2
  25. package/plugin/loadPluginConfig.ts +121 -32
  26. package/plugin/middlewareBuilder/stainlessMiddleware.d.ts +1 -1
  27. package/plugin/react/Routing.tsx +208 -104
  28. package/plugin/referencePlaceholderUtils.ts +1 -1
  29. package/plugin/replaceSidebarPlaceholderMiddleware.ts +5 -1
  30. package/plugin/routes/Docs.astro +61 -83
  31. package/plugin/routes/Overview.astro +10 -16
  32. package/plugin/routes/markdown.ts +7 -7
  33. package/plugin/vendor/preview.worker.docs.js +19768 -17702
  34. package/plugin/vendor/templates/go.md +1 -1
  35. package/plugin/vendor/templates/python.md +1 -1
  36. package/resolveSrcFile.ts +10 -0
  37. package/scripts/vendor_deps.ts +5 -5
  38. package/shared/getSharedLogger.ts +15 -0
  39. package/shared/terminalUtils.ts +3 -0
  40. package/src/content.config.ts +9 -0
  41. package/stl-docs/components/AIDropdown.tsx +63 -0
  42. package/stl-docs/components/AiChatIsland.tsx +10 -0
  43. package/stl-docs/components/{content-panel/ContentBreadcrumbs.tsx → ContentBreadcrumbs.tsx} +10 -18
  44. package/stl-docs/components/Head.astro +16 -0
  45. package/stl-docs/components/Header.astro +6 -8
  46. package/stl-docs/components/PageFrame.astro +14 -0
  47. package/stl-docs/components/PageTitle.astro +82 -0
  48. package/stl-docs/components/TableOfContents.astro +34 -0
  49. package/stl-docs/components/ThemeSelect.astro +118 -136
  50. package/stl-docs/components/content-panel/ContentPanel.astro +16 -25
  51. package/stl-docs/components/headers/SplashMobileMenuToggle.astro +17 -1
  52. package/stl-docs/components/headers/StackedHeader.astro +29 -24
  53. package/stl-docs/components/icons/chat-gpt.tsx +17 -0
  54. package/stl-docs/components/icons/claude.tsx +10 -0
  55. package/stl-docs/components/icons/cursor.tsx +10 -0
  56. package/stl-docs/components/icons/gemini.tsx +19 -0
  57. package/stl-docs/components/icons/markdown.tsx +10 -0
  58. package/stl-docs/components/index.ts +1 -0
  59. package/stl-docs/components/mintlify-compat/Accordion.astro +7 -5
  60. package/stl-docs/components/mintlify-compat/AccordionGroup.astro +7 -3
  61. package/stl-docs/components/mintlify-compat/Columns.astro +40 -42
  62. package/stl-docs/components/mintlify-compat/Frame.astro +16 -18
  63. package/stl-docs/components/mintlify-compat/Step.astro +30 -32
  64. package/stl-docs/components/mintlify-compat/Steps.astro +8 -10
  65. package/stl-docs/components/mintlify-compat/callouts/Callout.astro +1 -1
  66. package/stl-docs/components/mintlify-compat/callouts/Check.astro +1 -1
  67. package/stl-docs/components/mintlify-compat/callouts/Danger.astro +1 -1
  68. package/stl-docs/components/mintlify-compat/callouts/Info.astro +1 -1
  69. package/stl-docs/components/mintlify-compat/callouts/Note.astro +1 -1
  70. package/stl-docs/components/mintlify-compat/callouts/Tip.astro +1 -1
  71. package/stl-docs/components/mintlify-compat/callouts/Warning.astro +1 -1
  72. package/stl-docs/components/mintlify-compat/card.css +33 -35
  73. package/stl-docs/components/nav-tabs/NavDropdown.astro +31 -70
  74. package/stl-docs/components/nav-tabs/NavTabs.astro +78 -80
  75. package/stl-docs/components/nav-tabs/SecondaryNavTabs.astro +15 -8
  76. package/stl-docs/components/nav-tabs/buildNavLinks.ts +3 -2
  77. package/stl-docs/components/pagination/HomeLink.astro +10 -0
  78. package/stl-docs/components/pagination/Pagination.astro +175 -0
  79. package/stl-docs/components/pagination/PaginationLinkEmphasized.astro +22 -0
  80. package/stl-docs/components/pagination/PaginationLinkQuiet.astro +13 -0
  81. package/stl-docs/components/pagination/util.ts +71 -0
  82. package/stl-docs/components/scripts.ts +1 -0
  83. package/stl-docs/disableCalloutSyntax.ts +36 -0
  84. package/stl-docs/index.ts +121 -48
  85. package/stl-docs/loadStlDocsConfig.ts +44 -4
  86. package/stl-docs/proseMarkdown/proseMarkdownIntegration.ts +64 -0
  87. package/stl-docs/proseMarkdown/proseMarkdownMiddleware.ts +34 -0
  88. package/stl-docs/proseMarkdown/toMarkdown.ts +158 -0
  89. package/stl-docs/proseSearchIndexing.ts +113 -0
  90. package/stl-docs/tabsMiddleware.ts +11 -3
  91. package/styles/code.css +108 -140
  92. package/styles/fonts.css +32 -17
  93. package/styles/links.css +11 -48
  94. package/styles/method-descriptions.css +36 -0
  95. package/styles/overrides.css +48 -60
  96. package/styles/page.css +92 -52
  97. package/styles/sdk_select.css +9 -7
  98. package/styles/search.css +58 -69
  99. package/styles/sidebar.css +211 -131
  100. package/styles/{variables.css → sl-variables.css} +3 -2
  101. package/styles/stldocs-variables.css +6 -0
  102. package/styles/toc.css +41 -34
  103. package/theme.css +10 -10
  104. package/tsconfig.json +2 -5
  105. package/virtual-module.d.ts +23 -3
  106. package/components/variables.css +0 -135
  107. /package/{plugin/assets → assets}/fonts/geist/OFL.txt +0 -0
  108. /package/{plugin/assets → assets}/fonts/geist/geist-italic-latin-ext.woff2 +0 -0
  109. /package/{plugin/assets → assets}/fonts/geist/geist-italic-latin.woff2 +0 -0
  110. /package/{plugin/assets → assets}/fonts/geist/geist-latin-ext.woff2 +0 -0
  111. /package/{plugin/assets → assets}/fonts/geist/geist-latin.woff2 +0 -0
  112. /package/{plugin/assets → assets}/fonts/geist/geist-mono-italic-latin-ext.woff2 +0 -0
  113. /package/{plugin/assets → assets}/fonts/geist/geist-mono-italic-latin.woff2 +0 -0
  114. /package/{plugin/assets → assets}/fonts/geist/geist-mono-latin-ext.woff2 +0 -0
  115. /package/{plugin/assets → assets}/fonts/geist/geist-mono-latin.woff2 +0 -0
@@ -1,10 +1,11 @@
1
1
  import Markdoc from '@markdoc/markdoc';
2
2
  import Stainless from '@stainless-api/sdk';
3
3
  import { createSDKJSON, parseInputs, transformOAS } from './cms/worker';
4
- import type * as SDKJSON from '~/lib/json-spec-v2/types';
5
- import { Languages } from '@stainless-api/docs-ui/src/routing';
6
- import { buildIndex } from '@stainless-api/docs-ui/src/search/providers/algolia';
4
+ import type * as SDKJSON from '@stainless/sdk-json';
5
+ import { Languages } from '@stainless-api/docs-ui/routing';
6
+ import { buildIndex } from '@stainless-api/docs-search/providers/algolia';
7
7
  import type { VersionUserConfig } from './loadPluginConfig';
8
+ import type { AstroIntegrationLogger } from 'astro';
8
9
 
9
10
  const markdocConfig = {
10
11
  nodes: {
@@ -20,7 +21,31 @@ function renderMarkdown(content?: string) {
20
21
  return Markdoc.renderers.html(transformed);
21
22
  }
22
23
 
23
- export async function buildAlgoliaIndex({ version, apiKey }: { version: VersionUserConfig; apiKey: string }) {
24
+ export async function buildAlgoliaIndex({
25
+ version,
26
+ apiKey,
27
+ logger,
28
+ }: {
29
+ version: VersionUserConfig;
30
+ apiKey: string;
31
+ logger?: AstroIntegrationLogger;
32
+ }) {
33
+ function warnLog(message: string) {
34
+ if (logger) {
35
+ logger.warn(message);
36
+ } else {
37
+ console.warn(message);
38
+ }
39
+ }
40
+
41
+ function infoLog(message: string) {
42
+ if (logger) {
43
+ logger.info(message);
44
+ } else {
45
+ console.log(message);
46
+ }
47
+ }
48
+
24
49
  const client = new Stainless({ apiKey });
25
50
  const configs = await client.projects.configs.retrieve({
26
51
  project: version.stainlessProject,
@@ -42,7 +67,6 @@ export async function buildAlgoliaIndex({ version, apiKey }: { version: VersionU
42
67
  const languages =
43
68
  config.docs?.languages ??
44
69
  (Object.entries(config.targets)
45
- // @ts-expect-error we don't have the actual Stainless config type here
46
70
  .filter(([name, target]) => Languages.includes(name) && !target.skip)
47
71
  .map(([name]) => name) as SDKJSON.SpecLanguage[]);
48
72
 
@@ -50,6 +74,7 @@ export async function buildAlgoliaIndex({ version, apiKey }: { version: VersionU
50
74
  oas: transformedOAS,
51
75
  config,
52
76
  languages,
77
+ version,
53
78
  });
54
79
 
55
80
  const {
@@ -64,9 +89,9 @@ export async function buildAlgoliaIndex({ version, apiKey }: { version: VersionU
64
89
  !indexName && 'PUBLIC_ALGOLIA_INDEX',
65
90
  !algoliaWriteKey && 'PRIVATE_ALGOLIA_WRITE_KEY',
66
91
  ].filter(Boolean);
67
- console.warn(`⚠️ Skipping Algolia indexing due to missing environment variables: ${missing.join(', ')}`);
92
+ warnLog(`Skipping Algolia indexing due to missing environment variables: ${missing.join(', ')}`);
68
93
  return;
69
94
  }
70
95
  await buildIndex(appId, indexName, algoliaWriteKey, sdkJson, renderMarkdown);
71
- console.log('Indexing complete.');
96
+ infoLog('Indexing complete.');
72
97
  }
@@ -1,10 +1,11 @@
1
+ import type { AstroIntegrationLogger } from 'astro';
1
2
  import { createServer, IncomingMessage } from 'http';
2
3
  import { readFile } from 'fs/promises';
3
4
 
4
- import type * as SDKJSON from '~/lib/json-spec-v2/types';
5
+ import type * as SDKJSON from '@stainless/sdk-json';
5
6
  import { createSDKJSON, parseInputs, transformOAS } from './worker';
6
- import { Languages, parseRoute, type DocsLanguage } from '@stainless-api/docs-ui/src/routing';
7
- import Stainless from '@stainless-api/sdk';
7
+ import { Languages, parseRoute, type DocsLanguage } from '@stainless-api/docs-ui/routing';
8
+ import Stainless, { APIError } from '@stainless-api/sdk';
8
9
 
9
10
  import {
10
11
  toStarlightSidebar,
@@ -12,92 +13,150 @@ import {
12
13
  SidebarConfigItemsBuilder,
13
14
  } from './sidebar-builder';
14
15
  import type { VersionUserConfig } from '../loadPluginConfig';
15
-
16
+ import { bold } from '../../shared/terminalUtils';
16
17
  export type InputFilePaths = {
17
18
  oasPath?: string;
18
19
  configPath?: string;
19
20
  };
20
21
 
21
- async function versionInfo(project: string, apiKey: string) {
22
+ async function versionInfo(project: string, apiKey: string): Promise<Record<DocsLanguage, string>> {
22
23
  const data = await fetch(`https://api.stainless.com/api/projects/${project}/package-versions`, {
23
24
  headers: { Authorization: `Bearer ${apiKey}` },
24
25
  });
25
26
 
26
27
  const content = await data.text();
27
- return JSON.parse(content);
28
+ return JSON.parse(content) as Record<DocsLanguage, string>;
29
+ }
30
+
31
+ function redactApiKey(apiKey: string) {
32
+ return apiKey
33
+ .split('')
34
+ .map((char, index) => (index < 10 ? char : '*'))
35
+ .join('');
28
36
  }
29
37
 
38
+ export type Auth = Array<{
39
+ type: 'http_bearer' | 'query' | 'header' | 'oauth2' | 'http_basic' | 'http_digest';
40
+ description?: string;
41
+ name: string;
42
+ title: string;
43
+ header: string | undefined;
44
+ example: string | undefined;
45
+ opts: {
46
+ type: 'string' | 'number' | 'boolean' | 'null' | 'integer';
47
+ nullable: boolean;
48
+ description?: string | undefined;
49
+ example?: unknown;
50
+ default?: unknown;
51
+ read_env?: string | undefined;
52
+ auth?:
53
+ | {
54
+ security_scheme: string;
55
+ role?: 'value' | 'password' | 'username' | 'client_id' | 'client_secret' | undefined;
56
+ }
57
+ | undefined;
58
+ name: string;
59
+ }[];
60
+ }>;
61
+
30
62
  async function loadSpec({
31
63
  apiKey,
32
64
  devPaths,
33
65
  version,
66
+ logger,
34
67
  }: {
35
68
  apiKey: string;
36
69
  devPaths: InputFilePaths;
37
70
  version: VersionUserConfig;
38
- }) {
39
- let oasStr: string;
40
- let configStr: string;
41
- let versions: Record<DocsLanguage, string> | undefined;
42
-
43
- if (devPaths.oasPath && devPaths.configPath) {
44
- [oasStr, configStr] = await Promise.all([
45
- readFile(devPaths.oasPath, 'utf-8'),
46
- readFile(devPaths.configPath, 'utf-8'),
47
- ]);
48
- } else {
49
- const client = new Stainless({ apiKey });
50
- const configs = await client.projects.configs.retrieve({
51
- project: version.stainlessProject,
52
- branch: version.branch,
53
- include: 'openapi',
54
- });
71
+ logger: AstroIntegrationLogger;
72
+ }): Promise<{ data: SDKJSON.Spec; auth: Auth; id: string }> {
73
+ async function unsafeLoad(): Promise<{ data: SDKJSON.Spec; auth: Auth; id: string }> {
74
+ let oasStr: string;
75
+ let configStr: string;
76
+ let versions: Record<DocsLanguage, string> | undefined;
77
+
78
+ if (devPaths.oasPath && devPaths.configPath) {
79
+ [oasStr, configStr] = await Promise.all([
80
+ readFile(devPaths.oasPath, 'utf-8'),
81
+ readFile(devPaths.configPath, 'utf-8'),
82
+ ]);
83
+ } else {
84
+ const client = new Stainless({ apiKey });
85
+ const configs = await client.projects.configs.retrieve({
86
+ project: version.stainlessProject,
87
+ branch: version.branch,
88
+ include: 'openapi',
89
+ });
55
90
 
56
- versions = await versionInfo(version.stainlessProject, apiKey);
91
+ versions = await versionInfo(version.stainlessProject, apiKey);
57
92
 
58
- const configYML = Object.values(configs)[0] as { content: any };
59
- const oasJson = Object.values(configs)[1] as { content: any };
60
- oasStr = oasJson['content'];
61
- configStr = configYML['content'];
62
- }
93
+ const configYML = Object.values(configs)[0] as { content: any };
94
+ const oasJson = Object.values(configs)[1] as { content: any };
95
+ oasStr = oasJson['content'];
96
+ configStr = configYML['content'];
97
+ }
63
98
 
64
- const { oas, config } = await parseInputs({
65
- oas: oasStr,
66
- config: configStr,
67
- });
99
+ const { oas, config } = await parseInputs({
100
+ oas: oasStr,
101
+ config: configStr,
102
+ });
68
103
 
69
- const transformedOAS = await transformOAS({ oas, config });
104
+ const transformedOAS = await transformOAS({ oas, config });
70
105
 
71
- const languages =
72
- config.docs?.languages ??
73
- (Object.entries(config.targets)
74
- // @ts-expect-error we don't have the actual Stainless config type here
75
- .filter(([name, target]) => Languages.includes(name) && !target.skip)
76
- .map(([name]) => name) as SDKJSON.SpecLanguage[]);
106
+ const languages =
107
+ config.docs?.languages ??
108
+ (Object.entries(config.targets)
109
+ .filter(([name, target]) => Languages.includes(name) && !target.skip)
110
+ .map(([name]) => name) as SDKJSON.SpecLanguage[]);
77
111
 
78
- const sdkJson = await createSDKJSON({
79
- oas: transformedOAS,
80
- config,
81
- languages,
82
- });
112
+ const sdkJson = await createSDKJSON({
113
+ oas: transformedOAS,
114
+ config,
115
+ languages,
116
+ version,
117
+ });
83
118
 
84
- if (versions) {
85
- for (const [lang, version] of Object.entries(versions)) {
86
- const meta = sdkJson.metadata[lang as DocsLanguage];
87
- if (meta?.version) meta.version = version;
119
+ if (versions) {
120
+ for (const [lang, version] of Object.entries(versions)) {
121
+ const meta = sdkJson.metadata[lang as DocsLanguage];
122
+ if (meta?.version) meta.version = version;
123
+ }
88
124
  }
125
+
126
+ const id = crypto.randomUUID();
127
+ const opts = Object.entries(config.client_settings.opts).map(([k, v]) => ({ name: k, ...v }));
128
+
129
+ return {
130
+ data: sdkJson,
131
+ auth: sdkJson.security_schemes.map((scheme) => ({
132
+ ...scheme,
133
+ opts: opts.filter((opt) => opt.auth?.security_scheme === scheme.name),
134
+ })),
135
+ id,
136
+ };
89
137
  }
90
138
 
91
- const id = crypto.randomUUID();
139
+ try {
140
+ const result = await unsafeLoad();
141
+ return result;
142
+ } catch (error) {
143
+ logger.error(bold('Failed to fetch API reference information from Stainless:'));
144
+ if (error instanceof APIError && error.status >= 400 && error.status < 500) {
145
+ logger.error(`Requested project slug: "${version.stainlessProject}"`);
146
+ logger.error(`API key: "${redactApiKey(apiKey)}"`);
147
+ logger.error(
148
+ `This error can usually be corrected by re-authenticating with the Stainless. Use the CLI (stl auth login) or verify that the Stainless API key you're using can access the project mentioned above.`,
149
+ );
150
+ } else {
151
+ logger.error(error instanceof Error ? error.message : 'Unknown error');
152
+ }
92
153
 
93
- return {
94
- data: sdkJson,
95
- id,
96
- };
154
+ process.exit(1);
155
+ }
97
156
  }
98
157
 
99
158
  class Spec {
100
- private specPromise: Promise<{ id: string; data: SDKJSON.Spec }>;
159
+ private specPromise: Promise<{ id: string; data: SDKJSON.Spec; auth: Auth }>;
101
160
  private devPaths: InputFilePaths;
102
161
  private apiKey: string;
103
162
  private version: VersionUserConfig;
@@ -107,6 +166,7 @@ class Spec {
107
166
  apiKey: this.apiKey,
108
167
  devPaths: this.devPaths,
109
168
  version: this.version,
169
+ logger: this.logger,
110
170
  });
111
171
  }
112
172
 
@@ -122,21 +182,29 @@ class Spec {
122
182
  return {
123
183
  id: spec.id,
124
184
  data: null,
185
+ auth: null,
125
186
  };
126
187
  }
127
188
 
128
189
  return spec;
129
190
  }
130
191
 
131
- constructor(apiKey: string, version: VersionUserConfig, devPaths: InputFilePaths) {
192
+ constructor(
193
+ apiKey: string,
194
+ version: VersionUserConfig,
195
+ devPaths: InputFilePaths,
196
+ private logger: AstroIntegrationLogger,
197
+ ) {
132
198
  this.specPromise = loadSpec({
133
199
  apiKey,
134
200
  devPaths,
135
201
  version,
202
+ logger,
136
203
  });
137
204
  this.devPaths = devPaths;
138
205
  this.apiKey = apiKey;
139
206
  this.version = version;
207
+ this.logger = logger;
140
208
  }
141
209
  }
142
210
 
@@ -166,14 +234,16 @@ export function startDevServer({
166
234
  devPaths,
167
235
  apiKey,
168
236
  getGeneratedSidebarConfig,
237
+ logger,
169
238
  }: {
170
239
  port: number;
171
240
  version: VersionUserConfig;
172
241
  devPaths: InputFilePaths;
173
242
  apiKey: string;
174
243
  getGeneratedSidebarConfig: (id: number) => GeneratedSidebarConfig | null;
244
+ logger: AstroIntegrationLogger;
175
245
  }) {
176
- const spec = new Spec(apiKey, version, devPaths);
246
+ const spec = new Spec(apiKey, version, devPaths, logger);
177
247
 
178
248
  const server = createServer(async (req, res) => {
179
249
  // Add CORS headers
@@ -250,7 +320,7 @@ export function startDevServer({
250
320
  });
251
321
 
252
322
  server.listen(port, () => {
253
- console.log(`Server is running on port ${port}`);
323
+ logger.debug(`Stainless spec server is running on port: ${port}`);
254
324
  });
255
325
 
256
326
  return {
@@ -266,3 +336,5 @@ export function startDevServer({
266
336
  },
267
337
  };
268
338
  }
339
+
340
+ export type DevSpecServer = Awaited<ReturnType<typeof startDevServer>>;
@@ -1,5 +1,5 @@
1
- import type * as SDKJSON from '~/lib/json-spec-v2/types';
2
- import { generateRoute, walkTree, type DocsLanguage } from '@stainless-api/docs-ui/src/routing';
1
+ import type * as SDKJSON from '@stainless/sdk-json';
2
+ import { generateRoute, walkTree, type DocsLanguage } from '@stainless-api/docs-ui/routing';
3
3
  import type { StarlightRouteData } from '@astrojs/starlight/route-data';
4
4
 
5
5
  function isResourceNonEmpty(resource: SDKJSON.Resource) {
@@ -120,22 +120,12 @@ export type GeneratedSidebarConfig = {
120
120
  };
121
121
 
122
122
  function countKeys(obj?: Record<string, any>) {
123
- let o = obj ?? {};
123
+ const o = obj ?? {};
124
124
  return Object.keys(o).length;
125
125
  }
126
126
 
127
- function getMethodDeclForLanguage(entry: SDKJSON.Method, spec: SDKJSON.Spec, language: DocsLanguage) {
128
- const decls = spec.decls[language] ?? {};
129
- const decl = decls[entry.stainlessPath];
130
- if (decl !== undefined) {
131
- if ('ident' in decl) {
132
- return decl;
133
- }
134
- }
135
- return null;
136
- }
137
-
138
- type MethodDecl = Exclude<ReturnType<typeof getMethodDeclForLanguage>, null>;
127
+ type HasIdent<T> = T extends { ident: unknown } ? T : never;
128
+ type MethodDecl = HasIdent<SDKJSON.LanguageDeclNodes[SDKJSON.SpecLanguage]>;
139
129
 
140
130
  function makeAPIOverviewPage(): UserSidebarAPIOverviewPage {
141
131
  return {
@@ -166,7 +156,7 @@ function pullOutSharedModelsResource(resources: SDKJSON.Resource[]): {
166
156
  }
167
157
 
168
158
  export class SidebarConfigItemsBuilder {
169
- private getMethodDeclForLanguage(entry: SDKJSON.Method) {
159
+ private getMethodDeclForLanguage(entry: SDKJSON.Method): MethodDecl | null {
170
160
  const decls = this.spec.decls[this.language] ?? {};
171
161
  const decl = decls[entry.stainlessPath];
172
162
  if (decl !== undefined) {
@@ -208,13 +198,6 @@ export class SidebarConfigItemsBuilder {
208
198
  };
209
199
  }
210
200
 
211
- private sortByLabel<T extends UserSidebarConfigItem>(items: T[]) {
212
- // sorts in place
213
- items.sort((a, b) => {
214
- return a.label.localeCompare(b.label);
215
- });
216
- }
217
-
218
201
  private generateResourceGroup(resource: SDKJSON.Resource, collapsed: boolean): ReferenceSidebarGroup {
219
202
  const entries: ReferenceSidebarConfigItem[] = [];
220
203
  if (!this.options?.excludeResourceOverviewPages) {
@@ -228,7 +211,6 @@ export class SidebarConfigItemsBuilder {
228
211
  methodPages.push(this.toMethodPage(m, langDecl));
229
212
  }
230
213
  }
231
- this.sortByLabel(methodPages);
232
214
  entries.push(...methodPages);
233
215
 
234
216
  const subresources = Object.values(resource.subresources ?? {});
@@ -238,7 +220,6 @@ export class SidebarConfigItemsBuilder {
238
220
  subresourceGroups.push(this.generateResourceGroup(sub, true));
239
221
  }
240
222
  }
241
- this.sortByLabel(subresourceGroups);
242
223
  entries.push(...subresourceGroups);
243
224
 
244
225
  return {
@@ -253,7 +234,7 @@ export class SidebarConfigItemsBuilder {
253
234
 
254
235
  public generateItems(): ReferenceSidebarConfigItem[] {
255
236
  const resourceMap = this.spec.resources;
256
- let { resources, sharedModelsResource } = pullOutSharedModelsResource(Object.values(resourceMap ?? {}));
237
+ const { resources, sharedModelsResource } = pullOutSharedModelsResource(Object.values(resourceMap ?? {}));
257
238
 
258
239
  const entries: ReferenceSidebarConfigItem[] = resources.filter(isResourceNonEmpty).map((r) => {
259
240
  return this.generateResourceGroup(r, false);
@@ -1,10 +1,11 @@
1
1
  import Worker from 'web-worker';
2
- import { Languages, type DocsLanguage } from '@stainless-api/docs-ui/src/routing';
3
- import type * as SDKJSON from '~/lib/json-spec-v2/types';
2
+ import { Languages, type DocsLanguage } from '@stainless-api/docs-ui/routing';
3
+ import type * as SDKJSON from '@stainless/sdk-json';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { dirname, resolve } from 'node:path';
6
6
  import fs from 'fs/promises';
7
7
  import pathutils from 'path';
8
+ import type { VersionUserConfig } from '../loadPluginConfig';
8
9
 
9
10
  const __filename = fileURLToPath(import.meta.url);
10
11
  const __dirname = dirname(__filename);
@@ -12,7 +13,81 @@ const __dirname = dirname(__filename);
12
13
  const workerPath = resolve(__dirname, '..', 'vendor', 'preview.worker.docs.js');
13
14
 
14
15
  type OpenAPIDocument = Record<string, any>;
15
- type ParsedConfig = Record<string, any>;
16
+ export type ParsedConfig = {
17
+ docs:
18
+ | {
19
+ title?: string | undefined;
20
+ favicon?: string | undefined;
21
+ logo_icon?: string | undefined;
22
+ search?:
23
+ | {
24
+ algolia?:
25
+ | {
26
+ app_id: string;
27
+ index_name: string;
28
+ search_key: string;
29
+ }
30
+ | undefined;
31
+ }
32
+ | undefined;
33
+ description?: string | undefined;
34
+ languages?:
35
+ | ('node' | 'typescript' | 'python' | 'java' | 'kotlin' | 'go' | 'ruby' | 'terraform' | 'http')[]
36
+ | undefined;
37
+ snippets?:
38
+ | {
39
+ exclude_languages?: string[] | undefined;
40
+ }
41
+ | undefined;
42
+ show_security?: boolean | undefined;
43
+ show_readme?: boolean | undefined;
44
+ base_path?: string | undefined;
45
+ navigation?:
46
+ | {
47
+ menubar?:
48
+ | {
49
+ title: string;
50
+ icon?: string;
51
+ variant?: string;
52
+ href?: string | undefined;
53
+ page?: string | undefined;
54
+ }[]
55
+ | undefined;
56
+ sidebar?:
57
+ | {
58
+ title: string;
59
+ icon?: string;
60
+ variant?: string;
61
+ href?: string | undefined;
62
+ page?: string | undefined;
63
+ }[]
64
+ | undefined;
65
+ }
66
+ | undefined;
67
+ pages?: unknown;
68
+ resources?: unknown[] | undefined;
69
+ }
70
+ | undefined;
71
+ targets: Record<string, { skip?: boolean }>;
72
+ client_settings: {
73
+ opts: {
74
+ [x: string]: {
75
+ type: 'string' | 'number' | 'boolean' | 'null' | 'integer';
76
+ nullable: boolean;
77
+ description?: string | undefined;
78
+ example?: unknown;
79
+ default?: unknown;
80
+ read_env?: string | undefined;
81
+ auth?:
82
+ | {
83
+ security_scheme: string;
84
+ role?: 'value' | 'password' | 'username' | 'client_id' | 'client_secret' | undefined;
85
+ }
86
+ | undefined;
87
+ };
88
+ };
89
+ };
90
+ };
16
91
 
17
92
  function runJob({ type, signal, data }: { type: string; signal?: AbortSignal; data: any }) {
18
93
  const stainlessWorker = new Worker(workerPath, {
@@ -22,6 +97,7 @@ function runJob({ type, signal, data }: { type: string; signal?: AbortSignal; da
22
97
 
23
98
  return new Promise<any>((resolve, reject) => {
24
99
  stainlessWorker.addEventListener('error', (e) => {
100
+ e.preventDefault();
25
101
  reject(e);
26
102
  });
27
103
 
@@ -85,10 +161,12 @@ export async function createSDKJSON({
85
161
  oas,
86
162
  config,
87
163
  languages,
164
+ version,
88
165
  }: {
89
166
  oas: OpenAPIDocument;
90
167
  config: ParsedConfig;
91
168
  languages: DocsLanguage[];
169
+ version: VersionUserConfig;
92
170
  }) {
93
171
  const templatePath = resolve(__dirname, '../vendor/templates');
94
172
  const readmeLoader = await Promise.all(
@@ -98,7 +176,7 @@ export async function createSDKJSON({
98
176
  try {
99
177
  const content = await fs.readFile(mdfile);
100
178
  return [language, content.toString()];
101
- } catch (err) {
179
+ } catch {
102
180
  return [language, null];
103
181
  }
104
182
  }),
@@ -113,7 +191,7 @@ export async function createSDKJSON({
113
191
  config,
114
192
  languages,
115
193
  transform: false,
116
- projectName: '',
194
+ projectName: version.stainlessProject,
117
195
  readmeTemplates,
118
196
  },
119
197
  });
@@ -0,0 +1,54 @@
1
+ import { MethodDescriptionProps } from '@stainless-api/docs-ui/components';
2
+ import { useComponents } from '@stainless-api/docs-ui/contexts/use-components';
3
+ import style from '@stainless-api/docs-ui/style';
4
+ import { Button } from '@stainless-api/ui-primitives';
5
+
6
+ function shouldCollapseDescription(description: string) {
7
+ const MIN_CHARS = 400;
8
+ const MIN_LINES = 6;
9
+
10
+ const lineCount = description.split('\n').length;
11
+
12
+ if (description.length >= MIN_CHARS) return true;
13
+ if (lineCount >= MIN_LINES) return true;
14
+
15
+ // Markdown structure often means longer content
16
+ if (/#\s/.test(description)) return true; // has headings
17
+ if (/```/.test(description)) return true; // has code blocks
18
+ if (/^\s*[-*]\s+/m.test(description)) return true; // has lists
19
+
20
+ return false;
21
+ }
22
+
23
+ export function MethodDescription({ description }: MethodDescriptionProps) {
24
+ const { Markdown } = useComponents();
25
+
26
+ if (description) {
27
+ // Attempt to determine if we should make the description collapsible initially
28
+ // or not. If we get this right, there will be no FOUC.
29
+ const collapsible = shouldCollapseDescription(description);
30
+
31
+ return (
32
+ <div className="stl-method-description">
33
+ <div
34
+ className={style.MethodDescription}
35
+ data-stldocs-property-group="method-description"
36
+ data-collapsed={collapsible ? 'true' : 'false'}
37
+ >
38
+ <Markdown content={description} />
39
+ </div>
40
+ <div className="stl-method-description-overflow-wrapper">
41
+ <Button
42
+ type="button"
43
+ data-method-description-toggle
44
+ size="sm"
45
+ variant="ghost"
46
+ hidden={!collapsible}
47
+ >
48
+ Show more
49
+ </Button>
50
+ </div>
51
+ </div>
52
+ );
53
+ }
54
+ }