@stainless-api/docs 0.1.0-beta.11 → 0.1.0-beta.110

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 (149) hide show
  1. package/CHANGELOG.md +919 -0
  2. package/eslint-suppressions.json +27 -0
  3. package/locals.d.ts +17 -0
  4. package/package.json +50 -41
  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 -111
  18. package/plugin/components/SnippetCode.tsx +110 -68
  19. package/plugin/components/StainlessIslands.tsx +126 -0
  20. package/plugin/components/search/SearchAlgolia.astro +46 -29
  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 +304 -138
  34. package/plugin/languages.ts +8 -2
  35. package/plugin/loadPluginConfig.ts +251 -107
  36. package/plugin/middlewareBuilder/stainlessMiddleware.d.ts +3 -1
  37. package/plugin/react/Routing.tsx +212 -141
  38. package/plugin/referencePlaceholderUtils.ts +18 -15
  39. package/plugin/replaceSidebarPlaceholderMiddleware.ts +38 -34
  40. package/plugin/routes/Docs.astro +70 -111
  41. package/plugin/routes/DocsStatic.astro +6 -5
  42. package/plugin/routes/Overview.astro +45 -21
  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 +27303 -18260
  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 +5 -1
  55. package/plugin/vendor/templates/kotlin.md +5 -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/conditionalIntegration.ts +28 -0
  64. package/shared/getProsePages.ts +41 -0
  65. package/shared/getSharedLogger.ts +15 -0
  66. package/shared/terminalUtils.ts +3 -0
  67. package/shared/virtualModule.ts +54 -1
  68. package/src/content.config.ts +9 -0
  69. package/stl-docs/components/AIDropdown.tsx +63 -0
  70. package/stl-docs/components/AiChatIsland.tsx +14 -0
  71. package/stl-docs/components/{content-panel/ContentBreadcrumbs.tsx → ContentBreadcrumbs.tsx} +2 -2
  72. package/stl-docs/components/Footer.astro +89 -0
  73. package/stl-docs/components/Head.astro +20 -0
  74. package/stl-docs/components/Header.astro +3 -10
  75. package/stl-docs/components/PageFrame.astro +34 -0
  76. package/stl-docs/components/PageSidebar.astro +11 -0
  77. package/stl-docs/components/PageTitle.astro +82 -0
  78. package/stl-docs/components/StainlessLogo.svg +4 -0
  79. package/stl-docs/components/TableOfContents.astro +34 -0
  80. package/stl-docs/components/ThemeProvider.astro +36 -0
  81. package/stl-docs/components/ThemeSelect.astro +84 -146
  82. package/stl-docs/components/TwoColumnContent.astro +2 -0
  83. package/stl-docs/components/content-panel/ContentPanel.astro +4 -64
  84. package/stl-docs/components/headers/DefaultHeader.astro +4 -6
  85. package/stl-docs/components/headers/StackedHeader.astro +8 -51
  86. package/stl-docs/components/icons/chat-gpt.tsx +2 -2
  87. package/stl-docs/components/icons/cursor.tsx +10 -0
  88. package/stl-docs/components/icons/gemini.tsx +19 -0
  89. package/stl-docs/components/icons/markdown.tsx +1 -1
  90. package/stl-docs/components/index.ts +1 -0
  91. package/stl-docs/components/mintlify-compat/Frame.astro +4 -4
  92. package/stl-docs/components/mintlify-compat/card.css +4 -4
  93. package/stl-docs/components/mintlify-compat/index.ts +2 -4
  94. package/stl-docs/components/nav-tabs/NavDropdown.astro +31 -75
  95. package/stl-docs/components/nav-tabs/NavTabs.astro +79 -81
  96. package/stl-docs/components/nav-tabs/SecondaryNavTabs.astro +15 -7
  97. package/stl-docs/components/nav-tabs/buildNavLinks.ts +3 -2
  98. package/stl-docs/components/pagination/HomeLink.astro +10 -0
  99. package/stl-docs/components/pagination/Pagination.astro +177 -0
  100. package/stl-docs/components/pagination/PaginationLinkEmphasized.astro +22 -0
  101. package/stl-docs/components/pagination/PaginationLinkQuiet.astro +13 -0
  102. package/stl-docs/components/pagination/util.ts +71 -0
  103. package/stl-docs/components/scripts.ts +1 -0
  104. package/stl-docs/components/sidebars/BaseSidebar.astro +79 -2
  105. package/stl-docs/components/sidebars/SidebarWithComponents.tsx +10 -0
  106. package/stl-docs/components/sidebars/convertAstroSidebarToStl.tsx +62 -0
  107. package/stl-docs/disableCalloutSyntax.ts +36 -0
  108. package/stl-docs/fonts.ts +186 -0
  109. package/stl-docs/index.ts +171 -51
  110. package/stl-docs/loadStlDocsConfig.ts +64 -8
  111. package/stl-docs/proseDocSync.ts +314 -0
  112. package/stl-docs/proseMarkdown/proseMarkdownIntegration.ts +53 -0
  113. package/stl-docs/proseMarkdown/proseMarkdownMiddleware.ts +41 -0
  114. package/stl-docs/proseMarkdown/toMarkdown.ts +158 -0
  115. package/stl-docs/proseSearchIndexing.ts +222 -0
  116. package/stl-docs/tabsMiddleware.ts +13 -4
  117. package/styles/code.css +53 -49
  118. package/styles/links.css +2 -37
  119. package/styles/method-descriptions.css +36 -0
  120. package/styles/overrides.css +28 -46
  121. package/styles/page.css +230 -52
  122. package/styles/sdk_select.css +9 -6
  123. package/styles/search.css +11 -21
  124. package/styles/sidebar.css +25 -211
  125. package/styles/{variables.css → sl-variables.css} +4 -8
  126. package/styles/stldocs-variables.css +6 -0
  127. package/styles/toc.css +19 -8
  128. package/theme.css +11 -9
  129. package/tsconfig.json +1 -4
  130. package/virtual-module.d.ts +65 -8
  131. package/components/variables.css +0 -112
  132. package/plugin/cms/client.ts +0 -62
  133. package/plugin/cms/server.ts +0 -268
  134. package/plugin/globalJs/ai-dropdown.ts +0 -57
  135. package/stl-docs/components/APIReferenceAIDropdown.tsx +0 -58
  136. package/stl-docs/components/content-panel/ProseAIDropdown.tsx +0 -55
  137. package/stl-docs/components/headers/SplashMobileMenuToggle.astro +0 -49
  138. package/stl-docs/components/mintlify-compat/Step.astro +0 -56
  139. package/stl-docs/components/mintlify-compat/Steps.astro +0 -15
  140. package/styles/fonts.css +0 -68
  141. /package/{plugin/assets → assets}/fonts/geist/OFL.txt +0 -0
  142. /package/{plugin/assets → assets}/fonts/geist/geist-italic-latin-ext.woff2 +0 -0
  143. /package/{plugin/assets → assets}/fonts/geist/geist-italic-latin.woff2 +0 -0
  144. /package/{plugin/assets → assets}/fonts/geist/geist-latin-ext.woff2 +0 -0
  145. /package/{plugin/assets → assets}/fonts/geist/geist-latin.woff2 +0 -0
  146. /package/{plugin/assets → assets}/fonts/geist/geist-mono-italic-latin-ext.woff2 +0 -0
  147. /package/{plugin/assets → assets}/fonts/geist/geist-mono-italic-latin.woff2 +0 -0
  148. /package/{plugin/assets → assets}/fonts/geist/geist-mono-latin-ext.woff2 +0 -0
  149. /package/{plugin/assets → assets}/fonts/geist/geist-mono-latin.woff2 +0 -0
@@ -1,79 +1,78 @@
1
1
  ---
2
- import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
3
- import { cmsClient } from "../cms/client";
2
+ import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
3
+ import { getReadmeContent, buildPageNavigation, RenderSpec, astroMarkdownRender } from '../react/Routing';
4
+ import { getResourceFromSpec } from '@stainless-api/docs-ui/utils';
5
+ import { parseRoute, parseStainlessPath } from '@stainless-api/docs-ui/routing';
4
6
  import {
5
- getReadmeContent,
6
- generateDocsRoutes,
7
- buildPageNavigation,
8
- RenderSpec,
9
- astroMarkdownRender,
10
- } from "../react/Routing";
11
- import { getResourceFromSpec } from "@stainless-api/docs-ui/src/utils";
12
- import { BASE_PATH, CONTENT_PANEL_LAYOUT,MIDDLEWARE } from "virtual:stl-starlight-virtual-module";
7
+ CONTENT_PANEL_LAYOUT,
8
+ MIDDLEWARE,
9
+ EXPERIMENTAL_REQUEST_BUILDER,
10
+ RESOLVED_API_REFERENCE_PATH,
11
+ } from 'virtual:stl-starlight-virtual-module';
12
+ import { generateDocsRoutes } from '../helpers/generateDocsRoutes';
13
+ import { StainlessIslands } from '../components/StainlessIslands';
14
+ import { getSDKJSONInSSR } from '../specs/fetchSpecSSR';
13
15
 
16
+ const language = parseRoute(RESOLVED_API_REFERENCE_PATH, Astro.url.pathname)?.language;
14
17
 
15
- const spec = await cmsClient.getSpec();
16
- const routes = generateDocsRoutes(spec);
18
+ const spec = await getSDKJSONInSSR(language);
17
19
 
18
- const route = routes.find((r) => r.params.slug === Astro.params.slug);
20
+ export type Props = Awaited<ReturnType<typeof generateDocsRoutes>>[number]['props'] | Record<string, never>;
19
21
 
20
- if (!route) {
21
- throw new Error("No such route");
22
- }
22
+ const props =
23
+ 'language' in Astro.props
24
+ ? // In prod, we pass the props down to this page
25
+ Astro.props
26
+ : // In the dev server, we skip the DocsStatic wrapper so we need to find the props ourselves
27
+ generateDocsRoutes(spec).find((r) => r.params.slug === Astro.params.slug)?.props;
28
+ if (!props) throw new Error(`Could not find a route for slug '${Astro.params.slug}'`);
29
+
30
+ // PageTitle override will skip rendering the default Starlight title
31
+ Astro.locals._stlStarlightPage = {
32
+ skipRenderingStarlightTitle: props.kind === 'readme' ? false : true,
33
+ };
23
34
 
24
- const readmeContent = await getReadmeContent(spec, route.props.language);
25
- const readme =
26
- route.props.kind === "readme"
27
- ? await astroMarkdownRender(readmeContent ?? "")
28
- : null;
35
+ const readmeContent = await getReadmeContent(spec, props.language);
36
+ const readme = props.kind === 'readme' ? await astroMarkdownRender(readmeContent ?? '') : null;
29
37
 
30
- const resource = route.props.stainlessPath
31
- ? getResourceFromSpec(route.props.stainlessPath, spec)
32
- : null;
38
+ const resource = props.stainlessPath ? getResourceFromSpec(props.stainlessPath, spec) : null;
33
39
 
34
40
  const pageNav =
35
- route.props.kind === "resource" && resource
41
+ props.kind === 'resource' && resource
36
42
  ? buildPageNavigation(resource)
37
- : route.props.kind === "readme" && readme?.metadata
43
+ : props.kind === 'readme' && readme?.metadata
38
44
  ? readme?.metadata.headings
39
45
  : [];
40
46
 
41
- const props = route.props;
47
+ Astro.locals.language = props.language;
42
48
 
49
+ // Use first heading in README as page title
43
50
  if (readme) {
44
51
  const repo = spec.metadata?.[props.language]?.code_url;
45
- readme.code = readme.code.replace(
46
- /<a href="(?!(?:https?:\/\/|\/\/))([^"]+)"/g,
47
- `<a href="${repo}/$1"`);
48
- props.title = readme.metadata.headings[0].text ?? "Overview";
52
+ readme.code = readme.code.replace(/<a href="(?!(?:https?:\/\/|\/\/))([^"]+)"/g, `<a href="${repo}/$1"`);
53
+ props.title = readme.metadata.headings[0]!.text ?? 'Overview';
49
54
  }
50
55
 
56
+ // use summary for the page title of method pages
57
+ if (props.kind === 'http_method' && props.stainlessPath) {
58
+ const parsed = parseStainlessPath(props.stainlessPath);
59
+ const resource = getResourceFromSpec(props.stainlessPath, spec);
60
+ if (parsed?.method && resource?.methods[parsed.method]) {
61
+ const method = resource.methods[parsed.method];
62
+ props.title = method?.summary ?? method?.title ?? props.title;
63
+ }
64
+ }
51
65
  ---
52
66
 
53
-
54
67
  <StarlightPage
55
68
  headings={pageNav}
56
69
  frontmatter={{
57
- title: props?.title,
58
- head: [
59
- {
60
- tag: "link",
61
- attrs: {
62
- rel: "alternate",
63
- type: "text/markdown",
64
- href: `${BASE_PATH}/${Astro.params.slug}.md`
65
- }
66
- }
67
- ],
70
+ title: props.title,
68
71
  pagefind: false,
69
- tableOfContents: ["resource", "readme"].includes(props.kind)
70
- ? { maxHeadingLevel: 6 }
71
- : false,
72
- // @ts-ignore
73
- stainlessStarlight: {
74
- basePath: BASE_PATH,
75
- ...props,
76
- },
72
+ tableOfContents:
73
+ props.kind === 'readme' || (props.kind === 'resource' && props.language !== 'terraform')
74
+ ? { maxHeadingLevel: 6 }
75
+ : false,
77
76
  }}
78
77
  >
79
78
  {
@@ -88,76 +87,36 @@ if (readme) {
88
87
  contentPanelLayout={CONTENT_PANEL_LAYOUT}
89
88
  transformRequestSnippet={MIDDLEWARE.transformRequestSnippet}
90
89
  />
91
-
92
- <style is:inline>
93
- #stldocs-snippet-title {
94
- display: flex;
95
- gap: 5px;
96
- }
97
-
98
- .stldocs-snippet-code:not(.stldocs-snippet-response .stldocs-snippet-code) {
99
- padding: 0 !important;
100
-
101
- .astro-code {
102
- padding: 1rem;
103
- }
104
- }
105
-
106
-
107
- [data-has-sidebar]:not([data-has-toc]) .sl-container {
108
- max-width: 1428px;
109
- }
110
-
111
- .content-panel:nth-of-type(1) {
112
- display: none;
113
- }
114
- .content-panel:nth-of-type(2) .stl-page-nav-container {
115
- display: none;
116
- }
117
-
118
- .content-panel:nth-of-type(2) .stldocs-root .stl-page-nav-container {
119
- display: flex;
120
- }
121
- </style>
122
90
  </div>
123
91
  ) : (
124
92
  <>
125
93
  <Fragment set:html={readme?.code} />
126
- <style is:inline>
127
- .sl-markdown-content h1:first-of-type {
128
- display: none;
129
- }
130
-
131
- .sl-markdown-content img {
132
- display: inline-block;
133
- vertical-align: text-bottom;
134
- }
135
-
136
- .sl-markdown-content .octicon {
137
- margin-right: 0.2rem;
138
- overflow: visible !important;
139
- -webkit-mask: var(--oct-icon) no-repeat;
140
- mask: var(--oct-icon) no-repeat;
141
- -webkit-mask-size: 100% 100%;
142
- mask-size: 100% 100%;
143
- background-color: currentColor;
144
- color: inherit;
145
- display: inline-block;
146
- vertical-align: text-bottom;
147
- width: 1em;
148
- height: 1em;
149
- }
150
-
151
- .sl-markdown-content code {
152
- white-space: pre-wrap;
153
- }
154
- </style>
94
+ <style
95
+ is:inline
96
+ set:text={`
97
+ .sl-markdown-content h1:first-of-type {
98
+ display: none;
99
+ }
100
+
101
+ .sl-markdown-content img {
102
+ display: inline-block;
103
+ vertical-align: text-bottom;
104
+ }
105
+
106
+ .sl-markdown-content code {
107
+ white-space: pre-wrap;
108
+ }
109
+ `}
110
+ />
155
111
  </>
156
112
  )
157
113
  }
158
114
  </StarlightPage>
159
115
 
116
+ {EXPERIMENTAL_REQUEST_BUILDER && <StainlessIslands client:load />}
117
+
160
118
  <script src="../globalJs/navigation.ts"></script>
161
119
  <script src="../globalJs/copy.ts"></script>
162
120
  <script src="../globalJs/tooltip.ts"></script>
163
121
  <script src="../globalJs/code-snippets.ts"></script>
122
+ <script src="../globalJs/method-descriptions.ts"></script>
@@ -1,14 +1,15 @@
1
1
  ---
2
2
  import type { GetStaticPaths } from 'astro';
3
3
  import Docs from './Docs.astro';
4
- import { cmsClient } from '../cms/client';
5
- import { generateDocsRoutes } from '../react/Routing';
4
+ import { generateAllDocsRoutes } from '../helpers/generateDocsRoutes';
6
5
 
7
6
  export const getStaticPaths = (async () => {
8
- const spec = await cmsClient.getSpec();
9
- const routes = generateDocsRoutes(spec);
7
+ const routes = await generateAllDocsRoutes();
10
8
  return routes;
11
9
  }) satisfies GetStaticPaths;
10
+
11
+ export type Props = Awaited<ReturnType<typeof getStaticPaths>>[number]['props'];
12
+ const props = Astro.props;
12
13
  ---
13
14
 
14
- <Docs />
15
+ <Docs {...props} />
@@ -1,17 +1,36 @@
1
1
  ---
2
2
  import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
3
- import { BASE_PATH, EXCLUDE_LANGUAGES } from 'virtual:stl-starlight-virtual-module';
4
- import { cmsClient } from '../cms/client';
5
- import type { DocsLanguage } from '@stainless-api/docs-ui/src/routing';
3
+ import { EXCLUDE_LANGUAGES } from 'virtual:stl-starlight-virtual-module';
6
4
  import { RenderLibraries, RenderSpecOverview, type SpecMetadata } from '../react/Routing';
5
+ import { getSDKJSONInSSR } from '../specs/fetchSpecSSR';
6
+ import { api } from 'virtual:stainless-apis-manifest';
7
7
 
8
- const spec = await cmsClient.getSpec();
8
+ const spec = await getSDKJSONInSSR('http');
9
9
 
10
- const languages: DocsLanguage[] = spec.docs!.languages ?? ['http'];
11
- const metadata = languages
12
- .filter((language) => !['http', 'terraform'].includes(language) && spec.metadata[language])
13
- .filter((language) => !EXCLUDE_LANGUAGES.includes(language))
14
- .map((language) => [language, spec.metadata[language]]) as SpecMetadata;
10
+ const langTargets = api.languages
11
+ .map((l) => l.language)
12
+ .filter((l) => !EXCLUDE_LANGUAGES.includes(l))
13
+ .filter((l) => l !== 'http');
14
+
15
+ const langsWithSpecs = await Promise.all(
16
+ langTargets.map(async (language) => {
17
+ const spec = await getSDKJSONInSSR(language);
18
+ return {
19
+ language,
20
+ spec,
21
+ };
22
+ }),
23
+ );
24
+
25
+ const metadata: SpecMetadata = langsWithSpecs.map(({ language, spec }) => [
26
+ language,
27
+ spec.metadata[language]!,
28
+ ]);
29
+
30
+ // PageTitle override will skip rendering the default Starlight title
31
+ Astro.locals._stlStarlightPage = {
32
+ hasMarkdownRoute: false,
33
+ };
15
34
  ---
16
35
 
17
36
  <StarlightPage
@@ -19,22 +38,26 @@ const metadata = languages
19
38
  title: 'API Reference',
20
39
  pagefind: false,
21
40
  tableOfContents: false,
22
- stainlessStarlight: {
23
- basePath: BASE_PATH,
24
- language: 'http',
25
- },
26
41
  }}
27
42
  >
28
- <h3>Libraries</h3>
43
+ <div class="stl-overview">
44
+ {
45
+ metadata.length > 0 && (
46
+ <>
47
+ <h3>Libraries</h3>
29
48
 
30
- <div class="stldocs-root language-blocks stl-ui-not-prose not-content">
31
- <RenderLibraries metadata={metadata} />
32
- </div>
49
+ <div class="stldocs-root language-blocks stl-ui-not-prose not-content">
50
+ <RenderLibraries metadata={metadata} />
51
+ </div>
52
+ </>
53
+ )
54
+ }
33
55
 
34
- <h3>API Overview</h3>
56
+ <h3>API Overview</h3>
35
57
 
36
- <div class="stldocs-root api-overview stl-ui-not-prose">
37
- <RenderSpecOverview spec={spec} language="http" />
58
+ <div class="stldocs-root api-overview stl-ui-not-prose">
59
+ <RenderSpecOverview spec={spec} language="http" />
60
+ </div>
38
61
  </div>
39
62
  </StarlightPage>
40
63
 
@@ -46,7 +69,8 @@ const metadata = languages
46
69
  margin-top: 2rem;
47
70
 
48
71
  .stldocs-language-block {
49
- width: 350px;
72
+ width: 0;
73
+ flex: 1 1 350px;
50
74
  }
51
75
  }
52
76
 
@@ -1,13 +1,14 @@
1
1
  import type { APIRoute, GetStaticPaths } from 'astro';
2
- import { cmsClient } from '../cms/client';
3
- import { generateDocsRoutes, getReadmeContent } from '../react/Routing';
4
- import { getResourceFromSpec } from '@stainless-api/docs-ui/src/utils';
2
+ import { getReadmeContent } from '../react/Routing';
3
+ import { getResourceFromSpec } from '@stainless-api/docs-ui/utils';
5
4
 
6
- import { renderMarkdown } from '@stainless-api/docs-ui/src/markdown';
5
+ import { renderMarkdown } from '@stainless-api/docs-ui/markdown';
7
6
 
8
- import { parseStainlessPath, type DocsLanguage } from '@stainless-api/docs-ui/src/routing';
9
- import type { EnvironmentType } from '@stainless-api/docs-ui/src/markdown/utils';
7
+ import { parseStainlessPath, type DocsLanguage } from '@stainless-api/docs-ui/routing';
8
+ import type { EnvironmentType } from '@stainless-api/docs-ui/markdown/utils';
10
9
  import { PROPERTY_SETTINGS, MIDDLEWARE } from 'virtual:stl-starlight-virtual-module';
10
+ import { generateAllDocsRoutes } from '../helpers/generateDocsRoutes';
11
+ import { getSDKJSONInSSR } from '../specs/fetchSpecSSR';
11
12
 
12
13
  type RouteProps = {
13
14
  stainlessPath: string;
@@ -16,17 +17,17 @@ type RouteProps = {
16
17
  };
17
18
 
18
19
  export const getStaticPaths = (async () => {
19
- const spec = await cmsClient.getSpec();
20
- return generateDocsRoutes(spec);
20
+ const paths = await generateAllDocsRoutes();
21
+ return paths;
21
22
  }) satisfies GetStaticPaths;
22
23
 
23
24
  export const GET: APIRoute<RouteProps> = async ({ props }) => {
24
- const spec = await cmsClient.getSpec();
25
+ const spec = await getSDKJSONInSSR(props.language);
25
26
 
26
27
  if (props.kind === 'readme') {
27
28
  const readmeContent = await getReadmeContent(spec, props.language);
28
29
  return new Response(readmeContent, {
29
- headers: { 'Content-Type': 'text/markdown' },
30
+ headers: { 'Content-Type': 'text/plain' },
30
31
  });
31
32
  }
32
33
 
@@ -47,11 +48,11 @@ export const GET: APIRoute<RouteProps> = async ({ props }) => {
47
48
  },
48
49
  };
49
50
 
50
- const target = props.kind === 'http_method' && parsed?.method ? resource.methods[parsed.method] : resource;
51
+ const target = props.kind === 'http_method' && parsed?.method ? resource.methods[parsed.method]! : resource;
51
52
  const output = renderMarkdown(env, target);
52
53
 
53
54
  return new Response(output, {
54
- headers: { 'Content-Type': 'text/markdown' },
55
+ headers: { 'Content-Type': 'text/plain' },
55
56
  });
56
57
  };
57
58
 
@@ -1,7 +1,20 @@
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
+ function makeMethodOrResourceKey(entry: SDKJSON.Method | SDKJSON.Resource): string {
6
+ if (entry.kind === 'http_method') {
7
+ if (entry.endpoint && entry.endpoint !== '') {
8
+ return entry.endpoint;
9
+ }
10
+ return entry.stainlessPath;
11
+ }
12
+ if (entry.kind === 'resource') {
13
+ return entry.stainlessPath;
14
+ }
15
+ throw new Error(`Unknown entry kind ${JSON.stringify(entry)}`);
16
+ }
17
+
5
18
  function isResourceNonEmpty(resource: SDKJSON.Resource) {
6
19
  return (
7
20
  Object.keys(resource.methods ?? {}).length > 0 ||
@@ -119,23 +132,12 @@ export type GeneratedSidebarConfig = {
119
132
  ) => ReferenceSidebarConfigItem[] | void;
120
133
  };
121
134
 
122
- function countKeys(obj?: Record<string, any>) {
123
- let o = obj ?? {};
124
- return Object.keys(o).length;
125
- }
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;
135
+ function countKeys(obj?: Record<string, unknown>) {
136
+ return Object.keys(obj ?? {}).length;
136
137
  }
137
138
 
138
- type MethodDecl = Exclude<ReturnType<typeof getMethodDeclForLanguage>, null>;
139
+ type HasIdent<T> = T extends { ident: unknown } ? T : never;
140
+ type MethodDecl = HasIdent<SDKJSON.LanguageDeclNodes[SDKJSON.SpecLanguage]>;
139
141
 
140
142
  function makeAPIOverviewPage(): UserSidebarAPIOverviewPage {
141
143
  return {
@@ -166,7 +168,7 @@ function pullOutSharedModelsResource(resources: SDKJSON.Resource[]): {
166
168
  }
167
169
 
168
170
  export class SidebarConfigItemsBuilder {
169
- private getMethodDeclForLanguage(entry: SDKJSON.Method) {
171
+ private getMethodDeclForLanguage(entry: SDKJSON.Method): MethodDecl | null {
170
172
  const decls = this.spec.decls[this.language] ?? {};
171
173
  const decl = decls[entry.stainlessPath];
172
174
  if (decl !== undefined) {
@@ -177,11 +179,15 @@ export class SidebarConfigItemsBuilder {
177
179
  return null;
178
180
  }
179
181
 
182
+ private isWebhookResource(resource: SDKJSON.Resource): boolean {
183
+ return resource.stainlessPath === '(resource) webhooks';
184
+ }
185
+
180
186
  private toResourceOverviewPage(entry: SDKJSON.Resource): UserSidebarResourceOverviewPage {
181
187
  return {
182
188
  kind: 'resource_overview_page',
183
- label: 'Overview',
184
- key: entry.configRef,
189
+ label: this.isWebhookResource(entry) ? 'Events' : 'Overview',
190
+ key: makeMethodOrResourceKey(entry),
185
191
  badge: undefined,
186
192
  metadata: {
187
193
  subResourceCount: countKeys(entry.subresources),
@@ -195,7 +201,7 @@ export class SidebarConfigItemsBuilder {
195
201
  return {
196
202
  kind: 'method_page',
197
203
  label: entry.title,
198
- key: entry.endpoint,
204
+ key: makeMethodOrResourceKey(entry),
199
205
  badge: undefined,
200
206
  metadata: {
201
207
  deprecated: Boolean(entry.deprecated),
@@ -208,16 +214,10 @@ export class SidebarConfigItemsBuilder {
208
214
  };
209
215
  }
210
216
 
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
217
  private generateResourceGroup(resource: SDKJSON.Resource, collapsed: boolean): ReferenceSidebarGroup {
219
218
  const entries: ReferenceSidebarConfigItem[] = [];
220
- if (!this.options?.excludeResourceOverviewPages) {
219
+ // even if we aren't generating resource overview pages, we want to generate them for webhooks
220
+ if (!this.options?.excludeResourceOverviewPages || this.isWebhookResource(resource)) {
221
221
  entries.push(this.toResourceOverviewPage(resource));
222
222
  }
223
223
  const methods = Object.values(resource.methods ?? {});
@@ -228,7 +228,6 @@ export class SidebarConfigItemsBuilder {
228
228
  methodPages.push(this.toMethodPage(m, langDecl));
229
229
  }
230
230
  }
231
- this.sortByLabel(methodPages);
232
231
  entries.push(...methodPages);
233
232
 
234
233
  const subresources = Object.values(resource.subresources ?? {});
@@ -238,14 +237,13 @@ export class SidebarConfigItemsBuilder {
238
237
  subresourceGroups.push(this.generateResourceGroup(sub, true));
239
238
  }
240
239
  }
241
- this.sortByLabel(subresourceGroups);
242
240
  entries.push(...subresourceGroups);
243
241
 
244
242
  return {
245
243
  kind: 'group',
246
244
  badge: undefined,
247
245
  label: resource.title,
248
- resourceGroupKey: resource.configRef,
246
+ resourceGroupKey: makeMethodOrResourceKey(resource),
249
247
  entries,
250
248
  collapsed,
251
249
  };
@@ -253,7 +251,7 @@ export class SidebarConfigItemsBuilder {
253
251
 
254
252
  public generateItems(): ReferenceSidebarConfigItem[] {
255
253
  const resourceMap = this.spec.resources;
256
- let { resources, sharedModelsResource } = pullOutSharedModelsResource(Object.values(resourceMap ?? {}));
254
+ const { resources, sharedModelsResource } = pullOutSharedModelsResource(Object.values(resourceMap ?? {}));
257
255
 
258
256
  const entries: ReferenceSidebarConfigItem[] = resources.filter(isResourceNonEmpty).map((r) => {
259
257
  return this.generateResourceGroup(r, false);
@@ -276,35 +274,24 @@ export class SidebarConfigItemsBuilder {
276
274
  ) {}
277
275
  }
278
276
 
279
- export function walkSidebarConfigItems(
280
- sidebar: ReferenceSidebarConfigItem[],
281
- fn: (item: ReferenceSidebarConfigItem) => void,
282
- ) {
283
- for (const item of sidebar) {
284
- fn(item);
285
- if (item.kind === 'group') {
286
- walkSidebarConfigItems(item.entries, fn);
287
- }
288
- }
289
- }
290
-
291
277
  type SidebarEntry = StarlightRouteData['sidebar'][number];
292
278
 
293
279
  // This allows us to be a bit more forgiving to the user.
294
280
  // As long as they don't modify the key, we can still find the item.
281
+
295
282
  function getResourceOrMethod(spec: SDKJSON.Spec, endpointOrConfigRef: string) {
296
283
  for (const entry of walkTree(spec, false)) {
297
- if (entry.data.kind === 'resource' && entry.data.configRef === endpointOrConfigRef) {
298
- return entry;
284
+ if (entry.data.kind === 'model') {
285
+ continue;
299
286
  }
300
- if (entry.data.kind === 'http_method' && entry.data.endpoint === endpointOrConfigRef) {
287
+ if (makeMethodOrResourceKey(entry.data) === endpointOrConfigRef) {
301
288
  return entry;
302
289
  }
303
290
  }
304
291
  return null;
305
292
  }
306
293
 
307
- export function forceGenerateRoute({
294
+ function forceGenerateRoute({
308
295
  basePath,
309
296
  stainlessPath,
310
297
  language,
@@ -325,19 +312,17 @@ export type BuildSidebarParams = {
325
312
  currentSlug: string;
326
313
  };
327
314
 
328
- type ToStarlightSidebarParams = BuildSidebarParams & {
315
+ type ToStarlightSidebarParams = {
316
+ basePath: string;
329
317
  spec: SDKJSON.Spec;
330
318
  entries: ReferenceSidebarConfigItem[];
331
- currentStainlessPath: string;
332
319
  currentLanguage: DocsLanguage;
333
320
  };
334
321
 
335
322
  export function toStarlightSidebar({
336
323
  basePath,
337
- currentSlug,
338
324
  spec,
339
325
  entries,
340
- currentStainlessPath,
341
326
  currentLanguage,
342
327
  }: ToStarlightSidebarParams): SidebarEntry[] {
343
328
  const starlightEntries: SidebarEntry[] = [];
@@ -350,7 +335,7 @@ export function toStarlightSidebar({
350
335
  type: 'link',
351
336
  href: readmeSlug,
352
337
  label: entry.label,
353
- isCurrent: currentSlug === readmeSlug,
338
+ isCurrent: false,
354
339
  badge: entry.badge,
355
340
  attrs: {
356
341
  'data-stldocs-overview': 'readme',
@@ -369,14 +354,12 @@ export function toStarlightSidebar({
369
354
  language: currentLanguage,
370
355
  });
371
356
 
372
- const isCurrent = resourceOrMethod.data.stainlessPath === currentStainlessPath;
373
-
374
357
  if (resourceOrMethod.data.kind === 'http_method') {
375
358
  starlightEntries.push({
376
359
  type: 'link',
377
360
  href: route,
378
361
  label: entry.label,
379
- isCurrent,
362
+ isCurrent: false,
380
363
  badge: entry.badge,
381
364
  attrs: {
382
365
  'data-stldocs-method': resourceOrMethod.data.httpMethod,
@@ -387,7 +370,7 @@ export function toStarlightSidebar({
387
370
  type: 'link',
388
371
  href: route,
389
372
  label: entry.label,
390
- isCurrent,
373
+ isCurrent: false,
391
374
  badge: entry.badge,
392
375
  attrs: {
393
376
  // TODO: @Ryan: is data.name unique? This is what we used before so I'm not changing, but I am curious.
@@ -398,14 +381,20 @@ export function toStarlightSidebar({
398
381
  throw new Error(`Unknown entry kind ${JSON.stringify(entry)}`);
399
382
  }
400
383
  } else if (entry.kind === 'group') {
384
+ // Skip pushing the group if if the resource it represents is not available in the current language.
385
+ // This occurs when SDK generation for the current language is skipped in the Stainless config for that resource.
386
+ if (entry.resourceGroupKey) {
387
+ const resourceOrMethod = getResourceOrMethod(spec, entry.resourceGroupKey);
388
+ if (resourceOrMethod?.data?.kind === 'resource' && !resourceOrMethod?.data?.[currentLanguage]) {
389
+ continue;
390
+ }
391
+ }
401
392
  starlightEntries.push({
402
393
  type: 'group',
403
394
  label: entry.label,
404
395
  entries: toStarlightSidebar({
405
396
  basePath,
406
- currentSlug,
407
397
  spec,
408
- currentStainlessPath,
409
398
  entries: entry.entries,
410
399
  currentLanguage,
411
400
  }),