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

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,12 @@
1
1
  # @stainless-api/docs
2
2
 
3
+ ## 1.0.0-beta.143
4
+
5
+ ### Minor Changes
6
+
7
+ - d358f02: Bugfix
8
+ - d358f02: Bug fixes + new API to override package versions etc.
9
+
3
10
  ## 1.0.0-beta.142
4
11
 
5
12
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stainless-api/docs",
3
- "version": "1.0.0-beta.142",
3
+ "version": "1.0.0-beta.143",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
package/plugin/index.ts CHANGED
@@ -39,7 +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
+ export { fileSystemSDKJSONLoader } from './specs/fileSystemSDKJSONLoader';
43
43
  export type { ReferenceSidebarConfigItem };
44
44
 
45
45
  config({
@@ -6,7 +6,7 @@ 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/defaultSDKJSONLoader';
9
+ import { defaultSDKJSONLoader } from './specs/defaultSDKJSONLoader';
10
10
  import { SDKJSONFilesLoaderFn as SDKJSONFilesLoaderFn } from './specs/utils';
11
11
 
12
12
  type ApiKeySource = 'explicit-config' | 'environment-variable' | 'cli';
@@ -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
- loadSDKJSONFiles: partial.loadSDKJSONFiles ?? defaultSpecLoader,
289
+ loadSDKJSONFiles: partial.loadSDKJSONFiles ?? defaultSDKJSONLoader,
290
290
  branch: partial.versions?.[0]?.branch ?? 'main',
291
291
  };
292
292
 
@@ -128,7 +128,6 @@ export type SpecMetadata = [
128
128
  {
129
129
  repo_url?: string;
130
130
  code_url?: string;
131
- package_title?: string;
132
131
  version?: string;
133
132
  install?: string;
134
133
  },
@@ -1,11 +1,12 @@
1
1
  import path from 'path';
2
2
  import type { SDKJSONFilesLoaderFn, SDKJSONFilesLoaderParams } from './utils';
3
+ import { sdkJSONCacheReaderWriter } from './utils';
3
4
  import Stainless, { APIError } from '@stainless-api/sdk';
4
- import { DocsLanguage } from '@stainless-api/docs-ui/routing';
5
+ import { DocsLanguage, isSupportedLanguage } from '@stainless-api/docs-ui/routing';
5
6
  import { bold } from '../../shared/terminalUtils';
6
- import { mkdir, readdir, readFile, rm, writeFile } from 'fs/promises';
7
+ import { mkdir, readdir, readFile, rm } from 'fs/promises';
7
8
  import { generateSpecFromStrings, previewWorkerCode } from '@stainless/sdk-json/spec';
8
- import { Spec, SpecLanguage } from '@stainless/sdk-json';
9
+ import { Spec } from '@stainless/sdk-json';
9
10
  import crypto from 'crypto';
10
11
 
11
12
  function resolvePath(inputPath: string) {
@@ -115,13 +116,11 @@ async function loadInputs({ apiKey, logger, stainlessProject, branch }: SDKJSONF
115
116
  }
116
117
  }
117
118
 
118
- async function maybeLoadJSONFile<T>(filePath: string): Promise<T | null> {
119
- try {
120
- const fileContents = await readFile(filePath, 'utf8');
121
- return JSON.parse(fileContents) as T;
122
- } catch {
123
- return null;
119
+ function getLanguagesFromSDKJSON(spec: Spec) {
120
+ if (spec.docs?.languages) {
121
+ return spec.docs.languages;
124
122
  }
123
+ return Object.keys(spec.decls).filter(isSupportedLanguage);
125
124
  }
126
125
 
127
126
  async function cleanupDirectory(directory: string, filesToKeep: string[]) {
@@ -133,12 +132,12 @@ async function cleanupDirectory(directory: string, filesToKeep: string[]) {
133
132
  };
134
133
  }
135
134
 
136
- export const defaultSpecLoader: SDKJSONFilesLoaderFn = async (params) => {
135
+ export const defaultSDKJSONLoader: SDKJSONFilesLoaderFn = async (params) => {
137
136
  const { createCodegenDir } = params;
138
137
 
139
138
  const inputs = await loadInputs(params);
140
139
 
141
- const specsDirectory = path.join(createCodegenDir().pathname, 'specs2');
140
+ const specsDirectory = path.join(createCodegenDir().pathname, 'sdk_json_cache');
142
141
  await mkdir(specsDirectory, { recursive: true });
143
142
 
144
143
  const fileName =
@@ -150,20 +149,28 @@ export const defaultSpecLoader: SDKJSONFilesLoaderFn = async (params) => {
150
149
 
151
150
  const filePath = path.join(specsDirectory, fileName);
152
151
 
153
- const cachedSpec = await maybeLoadJSONFile<{ languages: SpecLanguage[]; sdkJson: Spec }>(filePath);
152
+ const cachedSpec = await (async () => {
153
+ try {
154
+ return await sdkJSONCacheReaderWriter.readFile(filePath);
155
+ } catch {
156
+ return null;
157
+ }
158
+ })();
159
+
154
160
  // skip generation since we already have a cached spec
155
161
  if (cachedSpec) {
162
+ const languages = getLanguagesFromSDKJSON(cachedSpec);
156
163
  params.logger.info(`Loaded cached spec: ${fileName}`);
157
164
  return [
158
165
  {
159
166
  filePath,
160
- languages: cachedSpec.languages,
161
- sdkJson: cachedSpec.sdkJson,
167
+ languages,
168
+ sdkJson: cachedSpec,
162
169
  },
163
170
  ];
164
171
  }
165
172
 
166
- const { sdkJson, languages } = await generateSpecFromStrings({
173
+ const { sdkJson } = await generateSpecFromStrings({
167
174
  oasStr: inputs.oasStr,
168
175
  configStr: inputs.configStr,
169
176
  languageOverrides: {
@@ -174,9 +181,11 @@ export const defaultSpecLoader: SDKJSONFilesLoaderFn = async (params) => {
174
181
  stainlessProject: params.stainlessProject,
175
182
  });
176
183
 
177
- await writeFile(filePath, JSON.stringify(sdkJson), 'utf8');
178
- params.logger.info(`Generated: ${fileName}`);
184
+ const languages = getLanguagesFromSDKJSON(sdkJson);
179
185
 
186
+ await sdkJSONCacheReaderWriter.writeFile(filePath, sdkJson);
187
+
188
+ params.logger.info(`Generated: ${fileName}`);
180
189
  const { deletedCount } = await cleanupDirectory(specsDirectory, [fileName]);
181
190
  if (deletedCount > 0) {
182
191
  params.logger.info(`Cleaned up ${deletedCount} unused spec file(s)`);
@@ -185,7 +194,7 @@ export const defaultSpecLoader: SDKJSONFilesLoaderFn = async (params) => {
185
194
  return [
186
195
  {
187
196
  filePath,
188
- languages: languages.filter((language) => language !== 'sql' && language !== 'openapi'),
197
+ languages,
189
198
  sdkJson,
190
199
  },
191
200
  ];
@@ -0,0 +1,126 @@
1
+ import { Spec, SpecLanguage } from '@stainless/sdk-json';
2
+ import { SDKJSONFilesLoaderFn, SDKJSONFilesLoaderParams, sdkJSONCacheReaderWriter } from './utils';
3
+ import { mkdir, readFile } from 'fs/promises';
4
+ import path from 'path';
5
+ import { generateSpecFromStrings } from '@stainless/sdk-json/spec';
6
+
7
+ interface GenerateSDKJSONOptions {
8
+ /** Raw OpenAPI spec contents (JSON or YAML). */
9
+ spec: string;
10
+ /** Raw Stainless config contents (YAML). */
11
+ config: string;
12
+ /** Language to build the SDK JSON spec for. */
13
+ language: SpecLanguage;
14
+ /** The name of the project. */
15
+ stainlessProject: string;
16
+ }
17
+
18
+ type GenerateSDKJSONFn<T> = (options: GenerateSDKJSONOptions) => Promise<{ spec: T }>;
19
+
20
+ type NonHttpSpecLanguage = Exclude<SpecLanguage, 'http'>;
21
+
22
+ type LibraryInformationOverride = Partial<{
23
+ [key in NonHttpSpecLanguage]: {
24
+ repo_url?: string;
25
+ code_url?: string;
26
+ version?: string;
27
+ install?: string;
28
+ };
29
+ }>;
30
+
31
+ type FileSystemSpecLoaderParams<T> = {
32
+ /**
33
+ * The path to the OpenAPI spec file.
34
+ *
35
+ */
36
+ specPath: string;
37
+ /**
38
+ * The path to your Stainless config file.
39
+ */
40
+ configFilePath: string;
41
+ /**
42
+ * The function used to generate your SDKJSON.
43
+ */
44
+ generateSDKJSON?: GenerateSDKJSONFn<T>;
45
+ /**
46
+ * The languages for which you want to render documentation.
47
+ */
48
+ languages: SpecLanguage[];
49
+ /**
50
+ * A key:value map of languages to overrides. Used to manually set things like the install command or SDK version.
51
+ */
52
+ override?: LibraryInformationOverride;
53
+ };
54
+
55
+ const defaultGenerateSDKJSON: GenerateSDKJSONFn<Spec> = async ({
56
+ config,
57
+ spec,
58
+ language,
59
+ stainlessProject,
60
+ }) => {
61
+ const { sdkJson } = await generateSpecFromStrings({
62
+ oasStr: spec,
63
+ configStr: config,
64
+ languageOverrides: {
65
+ mode: 'only',
66
+ list: [language],
67
+ },
68
+ versionInfo: null,
69
+ stainlessProject,
70
+ });
71
+
72
+ return { spec: sdkJson };
73
+ };
74
+
75
+ export function fileSystemSDKJSONLoader<T>({
76
+ specPath,
77
+ configFilePath,
78
+ generateSDKJSON,
79
+ languages,
80
+ override,
81
+ }: FileSystemSpecLoaderParams<T>): SDKJSONFilesLoaderFn {
82
+ const generateFn = generateSDKJSON ?? defaultGenerateSDKJSON;
83
+ return async function fileSystemSpecLoader(opts: SDKJSONFilesLoaderParams) {
84
+ const { createCodegenDir, logger } = opts;
85
+ const [spec, config] = await Promise.all([readFile(specPath, 'utf8'), readFile(configFilePath, 'utf8')]);
86
+
87
+ const specsDirectory = path.join(createCodegenDir().pathname, 'fs_spec_loader_specs');
88
+ await mkdir(specsDirectory, { recursive: true });
89
+
90
+ const r = languages.map(async (language) => {
91
+ const generateResult = await generateFn({
92
+ spec,
93
+ config,
94
+ language,
95
+ stainlessProject: opts.stainlessProject,
96
+ });
97
+
98
+ // type casting here is a little weird
99
+ // it prevents type errors from slightly incompatible SDKJSON types (since generateSDKJSON comes from the user)
100
+ const sdkJson = generateResult.spec as unknown as Spec;
101
+
102
+ if (override && language !== 'http' && override[language]) {
103
+ sdkJson.metadata[language] = {
104
+ ...sdkJson.metadata[language],
105
+ ...override[language],
106
+ };
107
+ }
108
+
109
+ const filePath = path.join(specsDirectory, `${language}.json`);
110
+
111
+ await sdkJSONCacheReaderWriter.writeFile(filePath, sdkJson);
112
+
113
+ logger.info(`Loaded SDKJSON for ${language} to ${filePath}`);
114
+
115
+ return {
116
+ filePath: filePath,
117
+ languages: [language],
118
+ sdkJson,
119
+ };
120
+ });
121
+
122
+ const results = await Promise.all(r);
123
+
124
+ return results;
125
+ };
126
+ }
@@ -1,5 +1,5 @@
1
1
  import { Spec } from '@stainless/sdk-json';
2
- import { readFile } from 'fs/promises';
2
+ import { readFile, writeFile } from 'fs/promises';
3
3
  import { DocsLanguage } from '@stainless-api/docs-ui/routing';
4
4
  import { AstroIntegrationLogger } from 'astro';
5
5
 
@@ -84,3 +84,17 @@ export function flatSpecsList(specs: LoadedSpecs) {
84
84
  )
85
85
  .flat();
86
86
  }
87
+
88
+ function typedReaderWriter<T>() {
89
+ return {
90
+ async readFile(filePath: string) {
91
+ const fileContents = await readFile(filePath, 'utf8');
92
+ return JSON.parse(fileContents) as T;
93
+ },
94
+ async writeFile(filePath: string, data: T) {
95
+ await writeFile(filePath, JSON.stringify(data), 'utf8');
96
+ },
97
+ };
98
+ }
99
+
100
+ export const sdkJSONCacheReaderWriter = typedReaderWriter<Spec>();
@@ -1,63 +0,0 @@
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
- }