@stainless-api/docs 1.0.0-beta.141 → 1.0.0-beta.142

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,19 @@
1
1
  # @stainless-api/docs
2
2
 
3
+ ## 1.0.0-beta.142
4
+
5
+ ### Minor Changes
6
+
7
+ - 28bb924: updated API for SDKJSON
8
+
9
+ ### Patch Changes
10
+
11
+ - 65c8fa0: Uses CSS for sidebar http icons instead of inline svg
12
+ - Updated dependencies [65c8fa0]
13
+ - Updated dependencies [28bb924]
14
+ - @stainless-api/docs-ui@1.0.0-beta.98
15
+ - @stainless-api/docs-search@1.0.0-beta.52
16
+
3
17
  ## 1.0.0-beta.141
4
18
 
5
19
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stainless-api/docs",
3
- "version": "1.0.0-beta.141",
3
+ "version": "1.0.0-beta.142",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -76,8 +76,8 @@
76
76
  "remend": "^1.3.0",
77
77
  "shiki": "^4.0.2",
78
78
  "unified": "^11.0.5",
79
- "@stainless-api/docs-search": "1.0.0-beta.51",
80
- "@stainless-api/docs-ui": "1.0.0-beta.97",
79
+ "@stainless-api/docs-search": "1.0.0-beta.52",
80
+ "@stainless-api/docs-ui": "1.0.0-beta.98",
81
81
  "@stainless-api/ui-primitives": "1.0.0-beta.55",
82
82
  "@stainless/sdk-json": "^0.1.0-beta.11"
83
83
  },
@@ -92,7 +92,7 @@
92
92
  "react": "^19.2.6",
93
93
  "react-dom": "^19.2.6",
94
94
  "typescript": "6.0.3",
95
- "vite": "^7.3.2",
95
+ "vite": "^7.3.3",
96
96
  "vitest": "^4.1.5",
97
97
  "zod": "^4.4.3",
98
98
  "@stainless/eslint-config": "0.1.0-beta.2"
package/plugin/index.ts CHANGED
@@ -39,6 +39,7 @@ import { buildAlgoliaIndex } from './buildAlgoliaIndex';
39
39
  import { flatSpecsList, loadAllSpecs, LoadedSpecs } from './specs/utils';
40
40
 
41
41
  export { generateAPILink } from './generateAPIReferenceLink';
42
+ export { makeFileSystemSDKJSONFilesLoader } from './specs/makeFileSystemSDKJSONFilesLoader';
42
43
  export type { ReferenceSidebarConfigItem };
43
44
 
44
45
  config({
@@ -124,7 +125,7 @@ function stlStarlightAstroIntegration(pluginConfig: NormalizedStainlessStarlight
124
125
  astroBase = astroConfig.base;
125
126
 
126
127
  specsPromise = loadAllSpecs(
127
- pluginConfig.loadSpecs({
128
+ pluginConfig.loadSDKJSONFiles({
128
129
  stainlessProject: pluginConfig.stainlessProject,
129
130
  branch: pluginConfig.branch,
130
131
  apiKey: pluginConfig.apiKey?.value ?? null,
@@ -6,8 +6,8 @@ import type { CreateShikiHighlighterOptions } from '@astrojs/markdown-remark';
6
6
  import type { DocsLanguage } from '@stainless-api/docs-ui/routing';
7
7
  import type { PropertySettingsType } from '@stainless-api/docs-ui/contexts';
8
8
 
9
- import { defaultSpecLoader } from './specs/defaultSpecLoader';
10
- import { SpecLoaderFn } from './specs/utils';
9
+ import { defaultSpecLoader } from './specs/defaultSDKJSONLoader';
10
+ import { SDKJSONFilesLoaderFn as SDKJSONFilesLoaderFn } from './specs/utils';
11
11
 
12
12
  type ApiKeySource = 'explicit-config' | 'environment-variable' | 'cli';
13
13
 
@@ -54,7 +54,7 @@ export type StainlessStarlightUserConfig = {
54
54
  /**
55
55
  * Optional function to provide your own loader for API reference data.
56
56
  */
57
- loadSpecs?: SpecLoaderFn;
57
+ loadSDKJSONFiles?: SDKJSONFilesLoaderFn;
58
58
 
59
59
  /**
60
60
  * Optional list of versions to render in the API reference.
@@ -286,7 +286,7 @@ function normalizeConfig(partial: SomeStainlessStarlightUserConfig, astroOptions
286
286
  detailThreshold: partial.llmsTxt?.detailThreshold ?? 2000,
287
287
  },
288
288
  apiKey: loadApiKey(partial.apiKey),
289
- loadSpecs: partial.loadSpecs ?? defaultSpecLoader,
289
+ loadSDKJSONFiles: partial.loadSDKJSONFiles ?? defaultSpecLoader,
290
290
  branch: partial.versions?.[0]?.branch ?? 'main',
291
291
  };
292
292
 
@@ -1,5 +1,5 @@
1
1
  import path from 'path';
2
- import type { SpecLoaderFn, SpecLoaderParams } from './utils';
2
+ import type { SDKJSONFilesLoaderFn, SDKJSONFilesLoaderParams } from './utils';
3
3
  import Stainless, { APIError } from '@stainless-api/sdk';
4
4
  import { DocsLanguage } from '@stainless-api/docs-ui/routing';
5
5
  import { bold } from '../../shared/terminalUtils';
@@ -44,7 +44,7 @@ function redactApiKey(apiKey: string) {
44
44
  .join('');
45
45
  }
46
46
 
47
- async function loadInputs({ apiKey, logger, stainlessProject, branch }: SpecLoaderParams) {
47
+ async function loadInputs({ apiKey, logger, stainlessProject, branch }: SDKJSONFilesLoaderParams) {
48
48
  const localFilePaths = getLocalFilePaths();
49
49
 
50
50
  if (localFilePaths) {
@@ -133,7 +133,7 @@ async function cleanupDirectory(directory: string, filesToKeep: string[]) {
133
133
  };
134
134
  }
135
135
 
136
- export const defaultSpecLoader: SpecLoaderFn = async (params) => {
136
+ export const defaultSpecLoader: SDKJSONFilesLoaderFn = async (params) => {
137
137
  const { createCodegenDir } = params;
138
138
 
139
139
  const inputs = await loadInputs(params);
@@ -163,7 +163,7 @@ export const defaultSpecLoader: SpecLoaderFn = async (params) => {
163
163
  ];
164
164
  }
165
165
 
166
- const result = await generateSpecFromStrings({
166
+ const { sdkJson, languages } = await generateSpecFromStrings({
167
167
  oasStr: inputs.oasStr,
168
168
  configStr: inputs.configStr,
169
169
  languageOverrides: {
@@ -174,7 +174,7 @@ export const defaultSpecLoader: SpecLoaderFn = async (params) => {
174
174
  stainlessProject: params.stainlessProject,
175
175
  });
176
176
 
177
- await writeFile(filePath, JSON.stringify(result), 'utf8');
177
+ await writeFile(filePath, JSON.stringify(sdkJson), 'utf8');
178
178
  params.logger.info(`Generated: ${fileName}`);
179
179
 
180
180
  const { deletedCount } = await cleanupDirectory(specsDirectory, [fileName]);
@@ -185,8 +185,8 @@ export const defaultSpecLoader: SpecLoaderFn = async (params) => {
185
185
  return [
186
186
  {
187
187
  filePath,
188
- languages: result.languages.filter((language) => language !== 'sql' && language !== 'openapi'),
189
- sdkJson: result.sdkJson,
188
+ languages: languages.filter((language) => language !== 'sql' && language !== 'openapi'),
189
+ sdkJson,
190
190
  },
191
191
  ];
192
192
  };
@@ -1,20 +1,10 @@
1
1
  import { readFile } from 'fs/promises';
2
2
 
3
3
  import { api } from 'virtual:stainless-apis-manifest';
4
- import type { SpecWithAuth } from '@stainless/sdk-json/spec';
4
+ import { Spec } from '@stainless/sdk-json';
5
5
  import { DocsLanguage } from '@stainless-api/docs-ui/routing';
6
6
 
7
- const cachedSpecWithAuth: Record<string, SpecWithAuth> = {};
8
-
9
- async function getSpecWithAuthInSSR(filePath: string) {
10
- if (cachedSpecWithAuth[filePath]) {
11
- return cachedSpecWithAuth[filePath];
12
- }
13
- const specStr = await readFile(filePath, 'utf8');
14
- const json = JSON.parse(specStr) as SpecWithAuth;
15
- cachedSpecWithAuth[filePath] = json;
16
- return json;
17
- }
7
+ const cachedSpecs: Record<string, Spec> = {};
18
8
 
19
9
  export async function getSDKJSONInSSR(language: DocsLanguage) {
20
10
  const filePath = api.languages.find((l) => l.language === language)?.sdkJSONFilePath;
@@ -22,6 +12,12 @@ export async function getSDKJSONInSSR(language: DocsLanguage) {
22
12
  throw new Error(`No SDK JSON file path for language: ${language}`);
23
13
  }
24
14
 
25
- const specWithAuth = await getSpecWithAuthInSSR(filePath);
26
- return specWithAuth.sdkJson;
15
+ if (cachedSpecs[filePath]) {
16
+ return cachedSpecs[filePath];
17
+ }
18
+ const specStr = await readFile(filePath, 'utf8');
19
+ const json = JSON.parse(specStr) as Spec;
20
+ cachedSpecs[filePath] = json;
21
+
22
+ return json;
27
23
  }
@@ -0,0 +1,63 @@
1
+ import { Spec, SpecLanguage } from '@stainless/sdk-json';
2
+ import { SDKJSONFilesLoaderFn, SDKJSONFilesLoaderParams } from './utils';
3
+ import { mkdir, readFile, writeFile } from 'fs/promises';
4
+ import path from 'path';
5
+
6
+ interface GenerateSDKJSONOptions {
7
+ /** Raw OpenAPI spec contents (JSON or YAML). */
8
+ spec: string;
9
+ /** Raw Stainless config contents (YAML). */
10
+ config: string;
11
+ /** Language to build the SDK JSON spec for. */
12
+ language: SpecLanguage;
13
+ }
14
+
15
+ type GenerateSDKJSONFn<T> = (options: GenerateSDKJSONOptions) => Promise<T>;
16
+
17
+ type FileSystemSpecLoaderParams<T> = {
18
+ specPath: string;
19
+ configFilePath: string;
20
+ generateSDKJSON: GenerateSDKJSONFn<T>;
21
+ languages: SpecLanguage[];
22
+ };
23
+
24
+ export function makeFileSystemSDKJSONFilesLoader<T>({
25
+ specPath,
26
+ configFilePath,
27
+ generateSDKJSON,
28
+ languages,
29
+ }: FileSystemSpecLoaderParams<T>): SDKJSONFilesLoaderFn {
30
+ return async function fileSystemSpecLoader(opts: SDKJSONFilesLoaderParams) {
31
+ const { createCodegenDir, logger } = opts;
32
+ const [spec, config] = await Promise.all([readFile(specPath, 'utf8'), readFile(configFilePath, 'utf8')]);
33
+
34
+ const specsDirectory = path.join(createCodegenDir().pathname, 'fsSpecLoaderSpecs');
35
+ await mkdir(specsDirectory, { recursive: true });
36
+
37
+ const r = languages.map(async (language) => {
38
+ // type casting here is a little weird
39
+ // it prevents type errors from slightly incompatible SDKJSON types (since generateSDKJSON comes from the user)
40
+ const sdkJson = (await generateSDKJSON({
41
+ spec,
42
+ config,
43
+ language,
44
+ })) as unknown as Spec;
45
+
46
+ const filePath = path.join(specsDirectory, `${language}.json`);
47
+
48
+ await writeFile(filePath, JSON.stringify(sdkJson));
49
+
50
+ logger.info(`Loaded SDKJSON for ${language} to ${filePath}`);
51
+
52
+ return {
53
+ filePath: filePath,
54
+ languages: [language],
55
+ sdkJson,
56
+ };
57
+ });
58
+
59
+ const results = await Promise.all(r);
60
+
61
+ return results;
62
+ };
63
+ }
@@ -5,7 +5,7 @@ import { AstroIntegrationLogger } from 'astro';
5
5
 
6
6
  type PossibleLanguage = NonNullable<NonNullable<NonNullable<Spec['docs']>['languages']>[number]>;
7
7
 
8
- export type SpecLoaderParams = {
8
+ export type SDKJSONFilesLoaderParams = {
9
9
  /**
10
10
  * The slug of your Stainless project.
11
11
  */
@@ -32,7 +32,7 @@ export type SpecLoaderParams = {
32
32
  createCodegenDir: () => URL;
33
33
  };
34
34
 
35
- type SpecLoaderResult = {
35
+ type SDKJSONFilesLoaderResult = {
36
36
  /**
37
37
  * The file path to the loaded spec. The spec MUST be written to a path on disk.
38
38
  * If you are not sure where to place it, create a directory in .astro using the `createCodegenDir` function passed in the parameters of the spec loader function.
@@ -49,7 +49,7 @@ type SpecLoaderResult = {
49
49
  sdkJson?: Spec;
50
50
  };
51
51
 
52
- export type SpecLoaderFn = (opts: SpecLoaderParams) => Promise<SpecLoaderResult[]>;
52
+ export type SDKJSONFilesLoaderFn = (opts: SDKJSONFilesLoaderParams) => Promise<SDKJSONFilesLoaderResult[]>;
53
53
 
54
54
  async function readSpecFromFile(filePath: string) {
55
55
  const txt = await readFile(filePath, 'utf8');
@@ -57,7 +57,7 @@ async function readSpecFromFile(filePath: string) {
57
57
  return json;
58
58
  }
59
59
 
60
- export async function loadAllSpecs(specLoaderResultsPromise: Promise<SpecLoaderResult[]>) {
60
+ export async function loadAllSpecs(specLoaderResultsPromise: Promise<SDKJSONFilesLoaderResult[]>) {
61
61
  const specLoaderResults = await specLoaderResultsPromise;
62
62
  const specs = await Promise.all(
63
63
  specLoaderResults.map(async (result) => {
@@ -1,9 +1,7 @@
1
1
  import { StlSidebarEntry } from '@stainless-api/docs-ui/components';
2
2
  import { SidebarEntry } from '../pagination/util';
3
3
  import { ReactNode } from 'react';
4
- import { Badge, getHttpMethod } from '@stainless-api/ui-primitives';
5
- import { FunctionIcon } from '@stainless-api/ui-primitives/icons';
6
- import { BracesIcon } from 'lucide-react';
4
+ import { getHttpMethod } from '@stainless-api/ui-primitives';
7
5
 
8
6
  function getIcon(entry: SidebarEntry): ReactNode | undefined {
9
7
  if (entry.type !== 'link') {
@@ -11,25 +9,34 @@ function getIcon(entry: SidebarEntry): ReactNode | undefined {
11
9
  }
12
10
  const methodAttr = entry.attrs['data-stldocs-method'];
13
11
  const httpMethod = getHttpMethod(methodAttr);
12
+ const classes = `stl-ui-badge stl-ui-badge--size-sm stl-sidebar-icon`;
13
+
14
14
  if (httpMethod) {
15
- return <Badge.HTTP method={httpMethod} iconOnly size="sm" />;
15
+ const methodClass = `stl-ui-badge--http-${httpMethod.toLowerCase()}`;
16
+ return (
17
+ <span className={`${classes} stl-ui-badge--http ${methodClass}`} role="img" aria-label={httpMethod} />
18
+ );
16
19
  }
17
20
 
18
21
  // special handling for the webhooks resource overview page
19
22
  if (entry.attrs['data-stldocs-overview'] === 'webhooks') {
20
23
  return (
21
- <Badge size="sm" icon={<BracesIcon />} intent="info">
22
- {''}
23
- </Badge>
24
+ <span
25
+ className={`${classes} stl-ui-badge--intent-info stl-sidebar-icon--braces`}
26
+ role="img"
27
+ aria-label="Webhook"
28
+ />
24
29
  );
25
30
  }
26
31
 
27
32
  // Support empty string as method to show generic "Function" badge
28
33
  else if (methodAttr === '') {
29
34
  return (
30
- <Badge size="sm" icon={<FunctionIcon />} intent="info">
31
- {''}
32
- </Badge>
35
+ <span
36
+ className={`${classes} stl-ui-badge--intent-info stl-sidebar-icon--function`}
37
+ role="img"
38
+ aria-label="Method"
39
+ />
33
40
  );
34
41
  }
35
42
  return undefined;