@stainless-api/docs 0.1.0-beta.18 → 0.1.0-beta.19

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @stainless-api/docs
2
2
 
3
+ ## 0.1.0-beta.19
4
+
5
+ ### Patch Changes
6
+
7
+ - 15c003d: fix react peer dependencies
8
+ - Updated dependencies [15c003d]
9
+ - @stainless-api/docs-ui@0.1.0-beta.15
10
+ - @stainless-api/ui-primitives@0.1.0-beta.15
11
+
3
12
  ## 0.1.0-beta.18
4
13
 
5
14
  ### Patch Changes
@@ -0,0 +1,47 @@
1
+ {
2
+ "plugin/buildAlgoliaIndex.ts": {
3
+ "@typescript-eslint/no-explicit-any": {
4
+ "count": 2
5
+ }
6
+ },
7
+ "plugin/cms/client.ts": {
8
+ "@typescript-eslint/no-explicit-any": {
9
+ "count": 1
10
+ }
11
+ },
12
+ "plugin/cms/server.ts": {
13
+ "@typescript-eslint/no-explicit-any": {
14
+ "count": 4
15
+ }
16
+ },
17
+ "plugin/cms/sidebar-builder.ts": {
18
+ "@typescript-eslint/no-explicit-any": {
19
+ "count": 1
20
+ }
21
+ },
22
+ "plugin/cms/worker.ts": {
23
+ "@typescript-eslint/no-explicit-any": {
24
+ "count": 4
25
+ }
26
+ },
27
+ "plugin/components/SnippetCode.tsx": {
28
+ "@typescript-eslint/no-explicit-any": {
29
+ "count": 1
30
+ }
31
+ },
32
+ "plugin/index.ts": {
33
+ "@typescript-eslint/no-explicit-any": {
34
+ "count": 1
35
+ }
36
+ },
37
+ "plugin/languages.ts": {
38
+ "@typescript-eslint/no-explicit-any": {
39
+ "count": 1
40
+ }
41
+ },
42
+ "shared/virtualModule.ts": {
43
+ "@typescript-eslint/no-explicit-any": {
44
+ "count": 1
45
+ }
46
+ }
47
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stainless-api/docs",
3
- "version": "0.1.0-beta.18",
3
+ "version": "0.1.0-beta.19",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -24,7 +24,10 @@
24
24
  },
25
25
  "peerDependencies": {
26
26
  "@astrojs/starlight": ">=0.36.1",
27
- "astro": ">=5.15.3"
27
+ "astro": ">=5.15.3",
28
+ "vite": ">=6.2.1",
29
+ "react": ">=19.0.0",
30
+ "react-dom": ">=19.0.0"
28
31
  },
29
32
  "dependencies": {
30
33
  "@astrojs/markdown-remark": "^6.3.2",
@@ -37,8 +40,6 @@
37
40
  "highlight.js": "^11.11.1",
38
41
  "lucide-react": "^0.544.0",
39
42
  "marked": "^16.0.0",
40
- "react": "^19.2.0",
41
- "react-dom": "^19.2.0",
42
43
  "remark-github-alerts": "^0.1.1",
43
44
  "shiki": "^3.9.2",
44
45
  "web-worker": "^1.5.0",
@@ -49,21 +50,28 @@
49
50
  "remark-gfm": "^4.0.1",
50
51
  "remark-stringify": "^11.0.0",
51
52
  "unified": "^11.0.5",
52
- "@stainless-api/docs-ui": "0.1.0-beta.14",
53
- "@stainless-api/ui-primitives": "0.1.0-beta.14"
53
+ "@stainless-api/docs-ui": "0.1.0-beta.15",
54
+ "@stainless-api/ui-primitives": "0.1.0-beta.15"
54
55
  },
55
56
  "devDependencies": {
57
+ "@astrojs/check": "^0.9.5",
56
58
  "@markdoc/markdoc": "^0.5.2",
57
59
  "@types/node": "^24.4.0",
58
60
  "@types/react": "^19.2.2",
59
61
  "@types/react-dom": "^19.2.2",
62
+ "react": "^19.2.0",
63
+ "react-dom": "^19.2.0",
60
64
  "tsx": "^4.20.3",
65
+ "typescript": "5.9.3",
66
+ "vite": "^6.3.6",
61
67
  "zod": "^4.0.0",
68
+ "@stainless/sdk-json": "^0.0.0",
62
69
  "@stainless/eslint-config": "0.0.0"
63
70
  },
64
71
  "scripts": {
65
72
  "vendor-deps": "pnpm tsx scripts/vendor_deps.ts",
66
73
  "lint": "eslint . --max-warnings 0",
67
- "sync": "astro sync"
74
+ "sync": "astro sync",
75
+ "check:types": "astro check"
68
76
  }
69
77
  }
@@ -1,7 +1,7 @@
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';
4
+ import type * as SDKJSON from '@stainless/sdk-json';
5
5
  import { Languages } from '@stainless-api/docs-ui/src/routing';
6
6
  import { buildIndex } from '@stainless-api/docs-ui/src/search/providers/algolia';
7
7
  import type { VersionUserConfig } from './loadPluginConfig';
@@ -2,7 +2,7 @@ import type { AstroIntegrationLogger } from 'astro';
2
2
  import { createServer, IncomingMessage } from 'http';
3
3
  import { readFile } from 'fs/promises';
4
4
 
5
- import type * as SDKJSON from '~/lib/json-spec-v2/types';
5
+ import type * as SDKJSON from '@stainless/sdk-json';
6
6
  import { createSDKJSON, parseInputs, transformOAS } from './worker';
7
7
  import { Languages, parseRoute, type DocsLanguage } from '@stainless-api/docs-ui/src/routing';
8
8
  import Stainless, { APIError } from '@stainless-api/sdk';
@@ -20,7 +20,7 @@ export type InputFilePaths = {
20
20
  configPath?: string;
21
21
  };
22
22
 
23
- async function versionInfo(project: string, apiKey: string) {
23
+ async function versionInfo(project: string, apiKey: string): Promise<Record<DocsLanguage, string>> {
24
24
  const data = await fetch(`https://api.stainless.com/api/projects/${project}/package-versions`, {
25
25
  headers: { Authorization: `Bearer ${apiKey}` },
26
26
  });
@@ -1,4 +1,4 @@
1
- import type * as SDKJSON from '~/lib/json-spec-v2/types';
1
+ import type * as SDKJSON from '@stainless/sdk-json';
2
2
  import { generateRoute, walkTree, type DocsLanguage } from '@stainless-api/docs-ui/src/routing';
3
3
  import type { StarlightRouteData } from '@astrojs/starlight/route-data';
4
4
 
@@ -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) {
@@ -244,7 +234,7 @@ export class SidebarConfigItemsBuilder {
244
234
 
245
235
  public generateItems(): ReferenceSidebarConfigItem[] {
246
236
  const resourceMap = this.spec.resources;
247
- let { resources, sharedModelsResource } = pullOutSharedModelsResource(Object.values(resourceMap ?? {}));
237
+ const { resources, sharedModelsResource } = pullOutSharedModelsResource(Object.values(resourceMap ?? {}));
248
238
 
249
239
  const entries: ReferenceSidebarConfigItem[] = resources.filter(isResourceNonEmpty).map((r) => {
250
240
  return this.generateResourceGroup(r, false);
@@ -1,6 +1,6 @@
1
1
  import Worker from 'web-worker';
2
2
  import { Languages, type DocsLanguage } from '@stainless-api/docs-ui/src/routing';
3
- import type * as SDKJSON from '~/lib/json-spec-v2/types';
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';
@@ -98,7 +98,7 @@ export async function createSDKJSON({
98
98
  try {
99
99
  const content = await fs.readFile(mdfile);
100
100
  return [language, content.toString()];
101
- } catch (err) {
101
+ } catch {
102
102
  return [language, null];
103
103
  }
104
104
  }),
@@ -54,7 +54,7 @@ function wrapFirstNSpaces($line: cheerio.Cheerio<any>, n: number) {
54
54
  const m = inner.match(new RegExp(`^( {1,${n}})`));
55
55
  if (!m) return;
56
56
 
57
- const lead = m[1];
57
+ const lead = m[1]!;
58
58
  $firstSpan.html(`<span class="leading-ws">${lead}</span>${inner.slice(lead.length)}`);
59
59
  }
60
60
 
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { BASE_PATH, SEARCH } from 'virtual:stl-starlight-virtual-module';
2
+ import { BASE_PATH, HIGHLIGHT_THEMES, SEARCH } from 'virtual:stl-starlight-virtual-module';
3
3
  import { parseRoute, generateRoute } from '@stainless-api/docs-ui/src/routing';
4
4
  import { SearchModal } from '@stainless-api/docs-ui/src/search/index';
5
5
  import { ChatModal } from '@stainless-api/docs-ui/src/components/chat';
@@ -9,6 +9,7 @@ import type { BundledLanguage, BundledTheme, HighlighterGeneric } from 'shiki';
9
9
 
10
10
  import {
11
11
  DocsProvider,
12
+ type MarkdownContext,
12
13
  MarkdownProvider,
13
14
  NavigationProvider,
14
15
  SearchProvider,
@@ -27,7 +28,7 @@ async function getHighlighter() {
27
28
  return $$highlighter;
28
29
  }
29
30
 
30
- async function createMarkdownRenderer() {
31
+ async function createMarkdownRenderer(): Promise<MarkdownContext> {
31
32
  const highlighter = await getHighlighter();
32
33
  const markdocConfig: Markdoc.Config = {
33
34
  nodes: {
@@ -66,16 +67,25 @@ async function createMarkdownRenderer() {
66
67
  },
67
68
  };
68
69
 
69
- return (content: string) => {
70
- const ast = Markdoc.parse(content);
71
- const transformed = Markdoc.transform(ast, markdocConfig);
72
- return Markdoc.renderers.html(transformed);
70
+ return {
71
+ render: (content: string) => {
72
+ const ast = Markdoc.parse(content);
73
+ const transformed = Markdoc.transform(ast, markdocConfig);
74
+ return Markdoc.renderers.html(transformed);
75
+ },
76
+ highlight: (content: string, language: string) => {
77
+ return highlighter.codeToHtml(content, {
78
+ lang: language ?? 'javascript',
79
+ themes: HIGHLIGHT_THEMES || {},
80
+ });
81
+ },
73
82
  };
74
83
  }
75
84
 
76
85
  export function DocsSearch({ settings, currentPath }: { settings: SearchSettings; currentPath: string }) {
77
- const renderMarkdown = React.use(createMarkdownRenderer());
86
+ const markdownRenderer = React.use(createMarkdownRenderer());
78
87
  const { stainlessPath, language } = parseRoute(BASE_PATH, currentPath);
88
+ // eslint-disable-next-line turbo/no-undeclared-env-vars
79
89
  const pageFind = import.meta.env.DEV ? undefined : '/pagefind/pagefind.js';
80
90
 
81
91
  function handleSelect(path: string) {
@@ -86,7 +96,7 @@ export function DocsSearch({ settings, currentPath }: { settings: SearchSettings
86
96
  return (
87
97
  <DocsProvider spec={null} language={language}>
88
98
  <NavigationProvider basePath="/" selectedPath={stainlessPath}>
89
- <MarkdownProvider render={renderMarkdown}>
99
+ <MarkdownProvider {...markdownRenderer}>
90
100
  <SearchProvider onSelect={handleSelect} pageFind={pageFind} settings={settings}>
91
101
  <div className="stldocs-root">
92
102
  <SearchModal id="stldocs-search" />
@@ -1,7 +1,7 @@
1
1
  // This is probably temporary, but it fills in functionality needed for Mintlify imports
2
2
 
3
3
  import type { StarlightRouteData } from '@astrojs/starlight/route-data';
4
- import type * as SDKJSON from '~/lib/json-spec-v2/types';
4
+ import type * as SDKJSON from '@stainless/sdk-json';
5
5
  import { walkTree } from '@stainless-api/docs-ui/src/routing';
6
6
 
7
7
  const INTERNAL_REFERENCE_ENTRY_MARKER = 'STL_STARLIGHT_API_REFERENCE_METHOD_LINK_PLACEHOLDER';
package/plugin/index.ts CHANGED
@@ -122,7 +122,7 @@ async function stlStarlightAstroIntegration(
122
122
 
123
123
  const { apiKey, version, devPaths } = tmpGetCMSServerConfig(pluginConfig.specRetrieverConfig);
124
124
 
125
- const cmsServer = await startDevServer({
125
+ const cmsServer = startDevServer({
126
126
  port: CMS_PORT,
127
127
  apiKey: apiKey.value,
128
128
  version,
@@ -225,6 +225,8 @@ async function stlStarlightAstroIntegration(
225
225
  EXPERIMENTAL_COLLAPSIBLE_SNIPPETS: pluginConfig.experimentalCollapsibleSnippets,
226
226
  PROPERTY_SETTINGS: pluginConfig.propertySettings,
227
227
  SEARCH: pluginConfig.search,
228
+ // @ts-expect-error internal prop
229
+ ENABLE_CONTEXT_MENU: pluginConfig._contextMenu,
228
230
  }),
229
231
  vmMiddlewareExport,
230
232
  ].join('\n');
@@ -44,7 +44,7 @@ export function applyLanguageToLinks(basePath?: string, defaultLanguage?: string
44
44
  `[data-stldocs-overview],[data-stldocs-method],a.nav-link[href^='${basePath}']`,
45
45
  );
46
46
 
47
- for (var link of links) {
47
+ for (const link of links) {
48
48
  const href = link.getAttribute('href');
49
49
  const prefix = generatePrefix(basePath, language);
50
50
  if (href?.startsWith(basePath) && !href?.startsWith(prefix)) {
@@ -299,7 +299,9 @@ function normalizeConfig(partial: SomeStainlessStarlightUserConfig, command: Ast
299
299
  if (!apiKey) {
300
300
  throw new Error(
301
301
  [
302
- bold('No Stainless credentials found. Please choose one of the following options to authenticate with Stainless:'),
302
+ bold(
303
+ 'No Stainless credentials found. Please choose one of the following options to authenticate with Stainless:',
304
+ ),
303
305
  '- Run `stl auth login` to authenticate via the Stainless CLI',
304
306
  '- Provide a Stainless API key via the `STAINLESS_API_KEY` environment variable (eg. in a .env file)',
305
307
  '- Set the `apiKey` option in the Stainless Docs config',
@@ -6,7 +6,7 @@ import remarkGfmAlerts from 'remark-github-alerts';
6
6
 
7
7
  import type { MarkdownHeading } from 'astro';
8
8
  import type { StarlightRouteData } from '@astrojs/starlight/route-data';
9
- import type * as SDKJSON from '~/lib/json-spec-v2/types';
9
+ import type * as SDKJSON from '@stainless/sdk-json';
10
10
  import { LanguageNames, type DocsLanguage } from '@stainless-api/docs-ui/src/routing';
11
11
 
12
12
  import {
@@ -48,9 +48,8 @@ import {
48
48
  HIGHLIGHT_THEMES,
49
49
  BREADCRUMB_CONFIG,
50
50
  PROPERTY_SETTINGS,
51
+ ENABLE_CONTEXT_MENU,
51
52
  } from 'virtual:stl-starlight-virtual-module';
52
- // TODO (eventually): this file should really probably only be importing from the starlight virtual module
53
- import { ENABLE_CONTEXT_MENU } from 'virtual:stl-docs-virtual-module';
54
53
  import style from '@stainless-api/docs-ui/src/style';
55
54
  import { createHighlighter, type BundledLanguage, type BundledTheme, type HighlighterGeneric } from 'shiki';
56
55
  import { SnippetCode, SnippetContainer, SnippetRequestContainer } from '../components/SnippetCode';
@@ -302,7 +301,10 @@ export function RenderSpec({
302
301
  const parsed = parseStainlessPath(path);
303
302
  const resource = getResourceFromSpec(path, spec);
304
303
 
305
- if (!resource || !parsed) return null;
304
+ if (!resource || !parsed) {
305
+ console.warn(`Could not find resource or parsed path for '${path}'`);
306
+ return null;
307
+ }
306
308
 
307
309
  return (
308
310
  <DocsProvider
@@ -336,7 +338,7 @@ export function RenderSpec({
336
338
  </div>
337
339
  {kind === 'http_method' ? (
338
340
  <SDKMethod
339
- method={resource.methods[parsed.method]}
341
+ method={resource.methods[parsed.method!]!}
340
342
  transformRequestSnippet={transformRequestSnippet}
341
343
  />
342
344
  ) : (
@@ -357,10 +359,14 @@ export function RenderMethod({ path }: { path: string }) {
357
359
 
358
360
  const parsed = parseStainlessPath(path);
359
361
  const resource = getResourceFromSpec(path, spec);
360
- if (!resource || !parsed) return null;
361
362
 
362
- const meth = resource.methods[parsed.method];
363
- return <SDKMethod method={meth} />;
363
+ if (!resource || !parsed) {
364
+ console.warn(`Could not find resource or parsed path for '${path}'`);
365
+ return null;
366
+ }
367
+
368
+ const method = resource.methods[parsed.method!]!;
369
+ return <SDKMethod method={method} />;
364
370
  }
365
371
 
366
372
  export async function getReadmeContent(spec: SDKJSON.Spec, language: DocsLanguage) {
@@ -58,7 +58,7 @@ function recursiveGetPlaceholderItems(
58
58
  items: PlaceholderItemResult[],
59
59
  ): PlaceholderItemResult[] {
60
60
  for (let i = 0; i < sidebar.length; i++) {
61
- const entry = sidebar[i];
61
+ const entry = sidebar[i]!;
62
62
  if ('attrs' in entry && entry.attrs?.about === INTERNAL_REFERENCE_ENTRY_MARKER) {
63
63
  items.push({
64
64
  index: i,
@@ -1,16 +1,15 @@
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 { cmsClient } from '../cms/client';
4
4
  import {
5
5
  getReadmeContent,
6
6
  generateDocsRoutes,
7
7
  buildPageNavigation,
8
8
  RenderSpec,
9
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";
13
-
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';
14
13
 
15
14
  const spec = await cmsClient.getSpec();
16
15
  const routes = generateDocsRoutes(spec);
@@ -18,29 +17,24 @@ const routes = generateDocsRoutes(spec);
18
17
  const route = routes.find((r) => r.params.slug === Astro.params.slug);
19
18
 
20
19
  if (!route) {
21
- throw new Error("No such route");
20
+ throw new Error(`Could not find a route for slug '${Astro.params.slug}'`);
22
21
  }
23
22
 
24
23
  // PageTitle override will skip rendering the default Starlight title
25
24
  // @ts-expect-error - _stlStarlightPage isn't typed yet
26
25
  Astro.locals._stlStarlightPage = {
27
26
  skipRenderingStarlightTitle: route.props.kind === 'readme' ? false : true,
28
- }
27
+ };
29
28
 
30
29
  const readmeContent = await getReadmeContent(spec, route.props.language);
31
- const readme =
32
- route.props.kind === "readme"
33
- ? await astroMarkdownRender(readmeContent ?? "")
34
- : null;
30
+ const readme = route.props.kind === 'readme' ? await astroMarkdownRender(readmeContent ?? '') : null;
35
31
 
36
- const resource = route.props.stainlessPath
37
- ? getResourceFromSpec(route.props.stainlessPath, spec)
38
- : null;
32
+ const resource = route.props.stainlessPath ? getResourceFromSpec(route.props.stainlessPath, spec) : null;
39
33
 
40
34
  const pageNav =
41
- route.props.kind === "resource" && resource
35
+ route.props.kind === 'resource' && resource
42
36
  ? buildPageNavigation(resource)
43
- : route.props.kind === "readme" && readme?.metadata
37
+ : route.props.kind === 'readme' && readme?.metadata
44
38
  ? readme?.metadata.headings
45
39
  : [];
46
40
 
@@ -48,33 +42,27 @@ const props = route.props;
48
42
 
49
43
  if (readme) {
50
44
  const repo = spec.metadata?.[props.language]?.code_url;
51
- readme.code = readme.code.replace(
52
- /<a href="(?!(?:https?:\/\/|\/\/))([^"]+)"/g,
53
- `<a href="${repo}/$1"`);
54
- props.title = readme.metadata.headings[0].text ?? "Overview";
45
+ readme.code = readme.code.replace(/<a href="(?!(?:https?:\/\/|\/\/))([^"]+)"/g, `<a href="${repo}/$1"`);
46
+ props.title = readme.metadata.headings[0]!.text ?? 'Overview';
55
47
  }
56
-
57
48
  ---
58
49
 
59
-
60
50
  <StarlightPage
61
51
  headings={pageNav}
62
52
  frontmatter={{
63
53
  title: props?.title,
64
54
  head: [
65
55
  {
66
- tag: "link",
56
+ tag: 'link',
67
57
  attrs: {
68
- rel: "alternate",
69
- type: "text/markdown",
70
- href: `${BASE_PATH}/${Astro.params.slug}.md`
71
- }
72
- }
58
+ rel: 'alternate',
59
+ type: 'text/markdown',
60
+ href: `${BASE_PATH}/${Astro.params.slug}.md`,
61
+ },
62
+ },
73
63
  ],
74
64
  pagefind: false,
75
- tableOfContents: ["resource", "readme"].includes(props.kind)
76
- ? { maxHeadingLevel: 6 }
77
- : false,
65
+ tableOfContents: ['resource', 'readme'].includes(props.kind) ? { maxHeadingLevel: 6 } : false,
78
66
  }}
79
67
  >
80
68
  {
@@ -90,7 +78,9 @@ if (readme) {
90
78
  transformRequestSnippet={MIDDLEWARE.transformRequestSnippet}
91
79
  />
92
80
 
93
- <style is:inline>
81
+ <style
82
+ is:inline
83
+ set:text={`
94
84
  #stldocs-snippet-title {
95
85
  display: flex;
96
86
  gap: 5px;
@@ -104,45 +94,47 @@ if (readme) {
104
94
  }
105
95
  }
106
96
 
107
-
108
97
  [data-has-sidebar]:not([data-has-toc]) .sl-container {
109
98
  max-width: 1428px;
110
99
  }
111
-
112
- </style>
100
+ `}
101
+ />
113
102
  </div>
114
103
  ) : (
115
104
  <>
116
105
  <Fragment set:html={readme?.code} />
117
- <style is:inline>
118
- .sl-markdown-content h1:first-of-type {
119
- display: none;
120
- }
106
+ <style
107
+ is:inline
108
+ set:text={`
109
+ .sl-markdown-content h1:first-of-type {
110
+ display: none;
111
+ }
121
112
 
122
- .sl-markdown-content img {
123
- display: inline-block;
124
- vertical-align: text-bottom;
125
- }
113
+ .sl-markdown-content img {
114
+ display: inline-block;
115
+ vertical-align: text-bottom;
116
+ }
126
117
 
127
- .sl-markdown-content .octicon {
128
- margin-right: 0.2rem;
129
- overflow: visible !important;
130
- -webkit-mask: var(--oct-icon) no-repeat;
131
- mask: var(--oct-icon) no-repeat;
132
- -webkit-mask-size: 100% 100%;
133
- mask-size: 100% 100%;
134
- background-color: currentColor;
135
- color: inherit;
136
- display: inline-block;
137
- vertical-align: text-bottom;
138
- width: 1em;
139
- height: 1em;
140
- }
118
+ .sl-markdown-content .octicon {
119
+ margin-right: 0.2rem;
120
+ overflow: visible !important;
121
+ -webkit-mask: var(--oct-icon) no-repeat;
122
+ mask: var(--oct-icon) no-repeat;
123
+ -webkit-mask-size: 100% 100%;
124
+ mask-size: 100% 100%;
125
+ background-color: currentColor;
126
+ color: inherit;
127
+ display: inline-block;
128
+ vertical-align: text-bottom;
129
+ width: 1em;
130
+ height: 1em;
131
+ }
141
132
 
142
- .sl-markdown-content code {
143
- white-space: pre-wrap;
144
- }
145
- </style>
133
+ .sl-markdown-content code {
134
+ white-space: pre-wrap;
135
+ }
136
+ `}
137
+ />
146
138
  </>
147
139
  )
148
140
  }
@@ -8,10 +8,10 @@ import { RenderLibraries, RenderSpecOverview, type SpecMetadata } from '../react
8
8
  const spec = await cmsClient.getSpec();
9
9
 
10
10
  const languages: DocsLanguage[] = spec.docs!.languages ?? ['http'];
11
- const metadata = languages
11
+ const metadata: SpecMetadata = languages
12
12
  .filter((language) => !['http', 'terraform'].includes(language) && spec.metadata[language])
13
13
  .filter((language) => !EXCLUDE_LANGUAGES.includes(language))
14
- .map((language) => [language, spec.metadata[language]]) as SpecMetadata;
14
+ .map<SpecMetadata[number]>((language) => [language, spec.metadata[language]!]);
15
15
 
16
16
  // PageTitle override will skip rendering the default Starlight title
17
17
  // @ts-expect-error - _stlStarlightPage isn't typed yet
@@ -47,7 +47,7 @@ export const GET: APIRoute<RouteProps> = async ({ props }) => {
47
47
  },
48
48
  };
49
49
 
50
- const target = props.kind === 'http_method' && parsed?.method ? resource.methods[parsed.method] : resource;
50
+ const target = props.kind === 'http_method' && parsed?.method ? resource.methods[parsed.method]! : resource;
51
51
  const output = renderMarkdown(env, target);
52
52
 
53
53
  return new Response(output, {
@@ -1,9 +1,10 @@
1
1
  ---
2
- import { ENABLE_CONTEXT_MENU, ENABLE_PROSE_MARKDOWN_RENDERING } from 'virtual:stl-docs-virtual-module';
2
+ import { ENABLE_PROSE_MARKDOWN_RENDERING } from 'virtual:stl-docs-virtual-module';
3
3
  import type { StarlightRouteData } from '@astrojs/starlight/route-data';
4
4
  import Default from '@astrojs/starlight/components/PageTitle.astro';
5
5
  import { ContentBreadcrumbs } from './ContentBreadcrumbs';
6
6
  import { AIDropdown } from './AIDropdown';
7
+ import { ENABLE_CONTEXT_MENU } from 'virtual:stl-starlight-virtual-module';
7
8
 
8
9
  function sidebarHasEntry(sidebarEntry: StarlightRouteData['sidebar'], currentPath?: string) {
9
10
  if (!currentPath) return false;
@@ -59,7 +59,7 @@ const options = [
59
59
  <Dropdown className="theme-select-container" data-theme-select>
60
60
  <DropdownTrigger withChevron>
61
61
  <span class="stl-dropdown-icon">
62
- {options.map(({ value, label, icon }) => <Fragment set:html={icon} />)}
62
+ {options.map(({ icon }) => <Fragment set:html={icon} />)}
63
63
  </span>
64
64
  </DropdownTrigger>
65
65
  <DropdownMenu role="listbox">
@@ -182,7 +182,7 @@ const options = [
182
182
  applyTheme(event.newDocument, loadTheme());
183
183
  });
184
184
 
185
- document.addEventListener(getPageLoadEvent(), (e) => {
185
+ document.addEventListener(getPageLoadEvent(), () => {
186
186
  setupThemeMode(document);
187
187
  });
188
188
  </script>
@@ -1,12 +1,12 @@
1
1
  ---
2
- import LanguageSelect from "virtual:starlight/components/LanguageSelect";
3
- import Search from "virtual:starlight/components/Search";
4
- import SiteTitle from "virtual:starlight/components/SiteTitle";
2
+ import LanguageSelect from 'virtual:starlight/components/LanguageSelect';
3
+ import Search from 'virtual:starlight/components/Search';
4
+ import SiteTitle from 'virtual:starlight/components/SiteTitle';
5
5
  import HeaderLinks from './HeaderLinks.astro';
6
- import { TABS } from "virtual:stl-docs-virtual-module";
7
- import ThemeSelect from "virtual:starlight/components/ThemeSelect";
8
- import SecondaryNavTabs from "../nav-tabs/SecondaryNavTabs.astro";
9
- import SplashMobileMenuToggle from "./SplashMobileMenuToggle.astro";
6
+ import { TABS } from 'virtual:stl-docs-virtual-module';
7
+ import ThemeSelect from 'virtual:starlight/components/ThemeSelect';
8
+ import SecondaryNavTabs from '../nav-tabs/SecondaryNavTabs.astro';
9
+ import SplashMobileMenuToggle from './SplashMobileMenuToggle.astro';
10
10
 
11
11
  interface Props {
12
12
  shouldRenderSearch?: boolean;
@@ -38,14 +38,14 @@ const { hasSidebar } = Astro.locals.starlightRoute;
38
38
  <SecondaryNavTabs />
39
39
 
40
40
  <style is:inline>
41
- .stl-top-container {
42
- display: flex;
43
- flex-direction: row;
44
- align-items: center;
45
- justify-content: end;
46
- gap: 0.5rem;
47
- width: 100%;
48
- }
41
+ .stl-top-container {
42
+ display: flex;
43
+ flex-direction: row;
44
+ align-items: center;
45
+ justify-content: end;
46
+ gap: 0.5rem;
47
+ width: 100%;
48
+ }
49
49
  @media (min-width: 50rem) {
50
50
  :root {
51
51
  --sl-nav-height: 80px;
@@ -64,12 +64,17 @@ const { hasSidebar } = Astro.locals.starlightRoute;
64
64
  }
65
65
  </style>
66
66
 
67
- {TABS.length === 0 && (
68
- <style is:inline>
69
- @media (min-width: 50rem) {
70
- :root {
71
- --sl-nav-height: 56px;
72
- }
73
- }
74
- </style>
75
- )}
67
+ {
68
+ TABS.length === 0 && (
69
+ <style
70
+ is:inline
71
+ set:text={`
72
+ @media (min-width: 50rem) {
73
+ :root {
74
+ --sl-nav-height: 56px;
75
+ }
76
+ }
77
+ `}
78
+ />
79
+ )
80
+ }
@@ -17,7 +17,7 @@ const BUTTON_ID = 'nav-dropdown-button';
17
17
 
18
18
  const navLinks = buildNavLinks(Astro.locals.starlightRoute);
19
19
 
20
- const buttonText = (navLinks.find((item) => item.active) ?? navLinks[0]).label;
20
+ const buttonText = (navLinks.find((item) => item.active) ?? navLinks[0]!).label;
21
21
  ---
22
22
 
23
23
  <div class="stldocs-root nav-dropdown-root">
@@ -1,9 +1,10 @@
1
1
  import type { StarlightRouteData } from '@astrojs/starlight/route-data';
2
2
  import { TABS } from 'virtual:stl-docs-virtual-module';
3
+ import type { StarlightRouteWithStlDocs } from '../../tabsMiddleware';
3
4
 
4
5
  export function buildNavLinks(starlightRoute: StarlightRouteData) {
5
- // TODO: specify the type of Astro.locals.starlightRoute._stlDocs
6
- const activeTabIndex = starlightRoute._stlDocs?.activeTabIndex;
6
+ const routeData: StarlightRouteWithStlDocs = starlightRoute;
7
+ const activeTabIndex = routeData?._stlDocs?.activeTabIndex;
7
8
 
8
9
  const navLinks = TABS.map((item, index) => ({
9
10
  ...item,
package/stl-docs/index.ts CHANGED
@@ -7,7 +7,7 @@ import { disableCalloutSyntaxStarlightPlugin } from './disableCalloutSyntax';
7
7
  import type { AstroIntegration } from 'astro';
8
8
 
9
9
  import { normalizeRedirects, type NormalizedRedirectConfig } from './redirects';
10
- import path, { join } from 'path';
10
+ import { join } from 'path';
11
11
  import { mkdirSync, writeFileSync } from 'fs';
12
12
  import {
13
13
  parseStlDocsConfig,
@@ -62,7 +62,13 @@ function stainlessDocsStarlightIntegration(config: NormalizedStainlessDocsConfig
62
62
  ];
63
63
 
64
64
  if (config.apiReference !== null) {
65
- plugins.push(stainlessStarlight(config.apiReference));
65
+ plugins.push(
66
+ stainlessStarlight({
67
+ ...config.apiReference,
68
+ // @ts-expect-error internal prop
69
+ _contextMenu: config.contextMenu,
70
+ }),
71
+ );
66
72
  componentOverrides.Sidebar = resolveSrcFile(COMPONENTS_FOLDER, './sidebars/SDKSelectSidebar.astro');
67
73
  componentOverrides.Search = resolveSrcFile('/plugin/components/search/Search.astro');
68
74
  }
@@ -165,7 +171,6 @@ function stainlessDocsIntegration(
165
171
  ENABLE_CLIENT_ROUTER: config.enableClientRouter,
166
172
  API_REFERENCE_BASE_PATH: apiReferenceBasePath,
167
173
  ENABLE_PROSE_MARKDOWN_RENDERING: config.enableProseMarkdownRendering,
168
- ENABLE_CONTEXT_MENU: config.contextMenu,
169
174
  });
170
175
  }
171
176
  },
@@ -4,6 +4,7 @@ import { API_REFERENCE_BASE_PATH } from 'virtual:stl-docs-virtual-module';
4
4
 
5
5
  // this is only run in `astro dev` for rendering prose content as Markdown on the fly.
6
6
  export const onRequest = defineMiddleware(async (context, next) => {
7
+ // eslint-disable-next-line turbo/no-undeclared-env-vars
7
8
  if (!import.meta.env.DEV) {
8
9
  return next();
9
10
  }
@@ -58,7 +58,7 @@ function getTabIndexForSlug(
58
58
  match: 'exact' | 'prefix';
59
59
  } | null {
60
60
  // ↓ exact match eg. slug = "/blog" and there is a link containing "/blog"
61
- let tab = linksByTab.get(slug);
61
+ const tab = linksByTab.get(slug)!;
62
62
  if (typeof tab === 'string') {
63
63
  return {
64
64
  match: 'exact',
@@ -88,13 +88,19 @@ function getNonSplitLinksByTab() {
88
88
  const linksByTab = new Map<string, string>();
89
89
 
90
90
  for (let i = 0; i < TABS.length; i++) {
91
- const tab = TABS[i];
91
+ const tab = TABS[i]!;
92
92
  linksByTab.set(tab.link, String(i));
93
93
  }
94
94
 
95
95
  return linksByTab;
96
96
  }
97
97
 
98
+ export interface StarlightRouteWithStlDocs extends StarlightRouteData {
99
+ _stlDocs?: {
100
+ activeTabIndex: number;
101
+ };
102
+ }
103
+
98
104
  export const onRequest = defineRouteMiddleware(async (context) => {
99
105
  // if using content collection schema, use: context.locals.starlightRoute.entry.data.stainlessStarlight
100
106
  // this worked without collections but relied on hijacking starlightRoute: context.props.frontmatter.stainlessStarlight
@@ -143,7 +149,8 @@ export const onRequest = defineRouteMiddleware(async (context) => {
143
149
  }
144
150
 
145
151
  // We store the active tab index so we can use it in our nav tabs component
146
- context.locals.starlightRoute._stlDocs = {
152
+ const routeData: StarlightRouteWithStlDocs = context.locals.starlightRoute;
153
+ routeData._stlDocs = {
147
154
  activeTabIndex: activeTabIndex.index,
148
155
  };
149
156
 
package/styles/fonts.css CHANGED
@@ -5,7 +5,9 @@
5
5
  font-display: swap;
6
6
  font-weight: 100 900;
7
7
  src: url(../plugin/assets/fonts/geist/geist-latin.woff2) format('woff2-variations');
8
- unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
8
+ unicode-range:
9
+ U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
10
+ U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
9
11
  }
10
12
  @font-face {
11
13
  font-family: 'Geist';
@@ -13,7 +15,9 @@
13
15
  font-display: swap;
14
16
  font-weight: 100 900;
15
17
  src: url(../plugin/assets/fonts/geist/geist-italic-latin.woff2) format('woff2-variations');
16
- unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
18
+ unicode-range:
19
+ U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
20
+ U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
17
21
  }
18
22
  @font-face {
19
23
  font-family: 'Geist';
@@ -21,7 +25,9 @@
21
25
  font-display: swap;
22
26
  font-weight: 100 900;
23
27
  src: url(../plugin/assets/fonts/geist/geist-latin-ext.woff2) format('woff2-variations');
24
- unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
28
+ unicode-range:
29
+ U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
30
+ U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
25
31
  }
26
32
  @font-face {
27
33
  font-family: 'Geist';
@@ -29,10 +35,11 @@
29
35
  font-display: swap;
30
36
  font-weight: 100 900;
31
37
  src: url(../plugin/assets/fonts/geist/geist-italic-latin-ext.woff2) format('woff2-variations');
32
- unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
38
+ unicode-range:
39
+ U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
40
+ U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
33
41
  }
34
42
 
35
-
36
43
  /* Geist Mono */
37
44
  @font-face {
38
45
  font-family: 'Geist Mono';
@@ -40,7 +47,9 @@
40
47
  font-display: swap;
41
48
  font-weight: 100 900;
42
49
  src: url(../plugin/assets/fonts/geist/geist-mono-latin.woff2) format('woff2-variations');
43
- unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
50
+ unicode-range:
51
+ U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
52
+ U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
44
53
  }
45
54
  @font-face {
46
55
  font-family: 'Geist Mono';
@@ -48,7 +57,9 @@
48
57
  font-display: swap;
49
58
  font-weight: 100 900;
50
59
  src: url(../plugin/assets/fonts/geist/geist-mono-italic-latin.woff2) format('woff2-variations');
51
- unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
60
+ unicode-range:
61
+ U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
62
+ U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
52
63
  }
53
64
  @font-face {
54
65
  font-family: 'Geist Mono';
@@ -56,7 +67,9 @@
56
67
  font-display: swap;
57
68
  font-weight: 100 900;
58
69
  src: url(../plugin/assets/fonts/geist/geist-mono-latin-ext.woff2) format('woff2-variations');
59
- unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
70
+ unicode-range:
71
+ U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
72
+ U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
60
73
  }
61
74
  @font-face {
62
75
  font-family: 'Geist Mono';
@@ -64,5 +77,7 @@
64
77
  font-display: swap;
65
78
  font-weight: 100 900;
66
79
  src: url(../plugin/assets/fonts/geist/geist-mono-italic-latin-ext.woff2) format('woff2-variations');
67
- unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
80
+ unicode-range:
81
+ U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
82
+ U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
68
83
  }
@@ -65,7 +65,8 @@
65
65
  }
66
66
  .stl-sdk-select .stldocs-dropdown-trigger {
67
67
  /* dropdown should match link padding, accounting for 1px border */
68
- padding: calc(var(--stl-sidebar-item-padding-block) - 1px) calc(var(--stl-sidebar-item-padding-inline) - 1px);
68
+ padding: calc(var(--stl-sidebar-item-padding-block) - 1px)
69
+ calc(var(--stl-sidebar-item-padding-inline) - 1px);
69
70
  }
70
71
  li a {
71
72
  color: var(--stl-ui-foreground-secondary);
package/tsconfig.json CHANGED
@@ -7,11 +7,8 @@
7
7
  ".stl-docs/**/*",
8
8
  "./plugin/**/*"
9
9
  ],
10
- "exclude": ["dist"],
10
+ "exclude": ["dist", "**/preview.worker.docs.js"],
11
11
  "compilerOptions": {
12
- "paths": {
13
- "~/*": ["../../../stainless/legacy-dir-root/*"]
14
- },
15
12
  "jsx": "react-jsx",
16
13
  "jsxImportSource": "react"
17
14
  }
@@ -18,6 +18,7 @@ declare module 'virtual:stl-starlight-virtual-module' {
18
18
  export const PROPERTY_SETTINGS: PropertySettingsType;
19
19
  export const MIDDLEWARE: StlStarlightMiddleware;
20
20
  export const SEARCH: StainlessStarlightUserConfig['search'];
21
+ export const ENABLE_CONTEXT_MENU: boolean;
21
22
  }
22
23
 
23
24
  declare module 'virtual:stl-docs-virtual-module' {
@@ -42,5 +43,4 @@ declare module 'virtual:stl-docs-virtual-module' {
42
43
  export const ENABLE_CLIENT_ROUTER: boolean;
43
44
  export const API_REFERENCE_BASE_PATH: string | null;
44
45
  export const ENABLE_PROSE_MARKDOWN_RENDERING: boolean;
45
- export const ENABLE_CONTEXT_MENU: boolean;
46
46
  }