@stainless-api/docs 0.1.0-beta.51 → 0.1.0-beta.53

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,25 @@
1
1
  # @stainless-api/docs
2
2
 
3
+ ## 0.1.0-beta.53
4
+
5
+ ### Minor Changes
6
+
7
+ - 2b0fec9: Added prose indexing for search
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [2b0fec9]
12
+ - @stainless-api/docs-ui@0.1.0-beta.44
13
+
14
+ ## 0.1.0-beta.52
15
+
16
+ ### Patch Changes
17
+
18
+ - 72c7604: include default themes in stainlessStarlight plugin
19
+ - Updated dependencies [8a32bbb]
20
+ - @stainless-api/ui-primitives@0.1.0-beta.32
21
+ - @stainless-api/docs-ui@0.1.0-beta.43
22
+
3
23
  ## 0.1.0-beta.51
4
24
 
5
25
  ### Patch Changes
@@ -44,4 +44,4 @@
44
44
  "count": 1
45
45
  }
46
46
  }
47
- }
47
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stainless-api/docs",
3
- "version": "0.1.0-beta.51",
3
+ "version": "0.1.0-beta.53",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -47,12 +47,12 @@
47
47
  "remark-gfm": "^4.0.1",
48
48
  "remark-github-alerts": "^0.1.1",
49
49
  "remark-stringify": "^11.0.0",
50
- "shiki": "^3.18.0",
50
+ "shiki": "^3.19.0",
51
51
  "unified": "^11.0.5",
52
52
  "web-worker": "^1.5.0",
53
53
  "yaml": "^2.8.2",
54
- "@stainless-api/ui-primitives": "0.1.0-beta.31",
55
- "@stainless-api/docs-ui": "0.1.0-beta.42"
54
+ "@stainless-api/ui-primitives": "0.1.0-beta.32",
55
+ "@stainless-api/docs-ui": "0.1.0-beta.44"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@astrojs/check": "^0.9.6",
@@ -60,14 +60,14 @@
60
60
  "@types/node": "24.10.1",
61
61
  "@types/react": "^19.2.7",
62
62
  "@types/react-dom": "^19.2.3",
63
- "react": "^19.2.0",
64
- "react-dom": "^19.2.0",
63
+ "react": "^19.2.1",
64
+ "react-dom": "^19.2.1",
65
65
  "tsx": "^4.21.0",
66
66
  "typescript": "5.9.3",
67
67
  "vite": "^6.4.1",
68
68
  "zod": "^4.1.13",
69
- "@stainless/eslint-config": "0.1.0-beta.0",
70
- "@stainless/sdk-json": "^0.1.0-beta.0"
69
+ "@stainless/sdk-json": "^0.1.0-beta.0",
70
+ "@stainless/eslint-config": "0.1.0-beta.0"
71
71
  },
72
72
  "scripts": {
73
73
  "vendor-deps": "tsx scripts/vendor_deps.ts",
package/plugin/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import react from '@astrojs/react';
2
2
  import type { StarlightPlugin } from '@astrojs/starlight/types';
3
3
  import type { AstroIntegration, AstroIntegrationLogger } from 'astro';
4
+ import type { BundledTheme } from 'shiki';
4
5
  import { config } from 'dotenv';
5
6
  import getPort from 'get-port';
6
7
  import { startDevServer } from './cms/server';
@@ -310,8 +311,21 @@ export function stainlessStarlight(someUserConfig: SomeStainlessStarlightUserCon
310
311
  }
311
312
  }
312
313
 
314
+ const expressiveCodeConfig =
315
+ typeof starlightConfig.expressiveCode === 'object' ? starlightConfig.expressiveCode : {};
316
+
317
+ const themes = expressiveCodeConfig.themes
318
+ ? (expressiveCodeConfig.themes as BundledTheme[])
319
+ : (['github-light', 'github-dark'] as BundledTheme[]);
320
+
313
321
  updateConfig({
314
322
  sidebar: starlightConfig.sidebar,
323
+ ...(expressiveCodeConfig && {
324
+ expressiveCode: {
325
+ ...expressiveCodeConfig,
326
+ themes,
327
+ },
328
+ }),
315
329
  });
316
330
 
317
331
  addRouteMiddleware({
package/stl-docs/index.ts CHANGED
@@ -22,6 +22,7 @@ import type * as StlDocsVirtualModule from 'virtual:stl-docs-virtual-module';
22
22
  import { resolveSrcFile } from '../resolveSrcFile';
23
23
  import { stainlessDocsMarkdownRenderer } from './proseMarkdown/proseMarkdownIntegration';
24
24
  import { setSharedLogger } from '../shared/getSharedLogger';
25
+ import { stainlessDocsProseIndexing } from './proseSearchIndexing';
25
26
 
26
27
  export * from '../plugin';
27
28
 
@@ -242,5 +243,6 @@ export function stainlessDocs(config: StainlessDocsUserConfig) {
242
243
  stainlessDocsStarlightIntegration(normalizedConfig),
243
244
  stainlessDocsIntegration(normalizedConfig, apiReferenceBasePath),
244
245
  stainlessDocsMarkdownRenderer({ enabled: normalizedConfig.enableProseMarkdownRendering }),
246
+ stainlessDocsProseIndexing(),
245
247
  ];
246
248
  }
@@ -0,0 +1,113 @@
1
+ import type { AstroIntegration } from 'astro';
2
+ import { readFile } from 'fs/promises';
3
+ import { getSharedLogger } from '../shared/getSharedLogger';
4
+ import { bold } from '../shared/terminalUtils';
5
+ import { buildProseIndex } from '@stainless-api/docs-ui/search/providers/algolia';
6
+ import * as cheerio from 'cheerio';
7
+
8
+ function chunkByWords(content: string, chunkSize: number = 30000, chunkOverlap: number = 10) {
9
+ if (Buffer.byteLength(content) < chunkSize) return [content];
10
+
11
+ const words = content.split(/\s+/);
12
+ const chunks: string[] = [];
13
+
14
+ let currentChunk: string[] = [];
15
+ let currentSize = 0;
16
+
17
+ for (const word of words) {
18
+ const wordSize = Buffer.byteLength(word + ' ', 'utf-8');
19
+
20
+ if (currentSize + wordSize > chunkSize && currentChunk.length > 0) {
21
+ chunks.push(currentChunk.join(' '));
22
+
23
+ const overlapStart = Math.max(0, currentChunk.length - chunkOverlap);
24
+ currentChunk = currentChunk.slice(overlapStart);
25
+ currentSize = Buffer.byteLength(currentChunk.join(' '), 'utf-8');
26
+ }
27
+
28
+ currentChunk.push(word);
29
+ currentSize += wordSize;
30
+ }
31
+
32
+ if (currentChunk.length > 0) {
33
+ chunks.push(currentChunk.join(' '));
34
+ }
35
+
36
+ return chunks;
37
+ }
38
+
39
+ export function* indexHTML(content: string, root: string, pattern: string) {
40
+ const $ = cheerio.load(content);
41
+ const matches = $(root).find(pattern);
42
+
43
+ for (const match of matches) {
44
+ const rawText = $(match).text().trim();
45
+ const chunks = chunkByWords(rawText);
46
+ const chunkId = crypto.randomUUID();
47
+
48
+ for (const [chunkN, content] of chunks.entries()) {
49
+ yield {
50
+ id: $(match).attr('id'),
51
+ tag: match.tagName.toLowerCase(),
52
+ content,
53
+ chunk: {
54
+ id: chunkId,
55
+ index: chunkN,
56
+ total: chunks.length,
57
+ },
58
+ };
59
+ }
60
+ }
61
+ }
62
+
63
+ const root = 'main';
64
+ const pattern = 'h1, h2, h3, h4, h5, h6, p, li';
65
+
66
+ export function stainlessDocsProseIndexing(): AstroIntegration {
67
+ return {
68
+ name: 'stl-docs-prose-indexing',
69
+ hooks: {
70
+ 'astro:build:done': async ({ assets, logger: localLogger, dir }) => {
71
+ const logger = getSharedLogger({ fallback: localLogger });
72
+ const outputBasePath = dir.pathname;
73
+
74
+ const {
75
+ PUBLIC_ALGOLIA_APP_ID: appId,
76
+ PUBLIC_ALGOLIA_INDEX: indexName,
77
+ PRIVATE_ALGOLIA_WRITE_KEY: algoliaWriteKey,
78
+ } = process.env;
79
+
80
+ if (!appId || !indexName || !algoliaWriteKey) {
81
+ logger.info('Skipping algolia indexing due to missing environment variables');
82
+ return;
83
+ }
84
+
85
+ const starlightPagePatterns = ['/[...slug]'];
86
+ const pagesToRender = Array.from(assets.entries())
87
+ .filter(([k]) => starlightPagePatterns.includes(k))
88
+ .map(([, v]) => v)
89
+ .flat()
90
+ .map((v) => v.pathname);
91
+
92
+ logger.info(bold(`Indexing ${pagesToRender.length} prose pages for search`));
93
+
94
+ const objects = [];
95
+ for (const absHtmlPath of pagesToRender) {
96
+ const content = await readFile(absHtmlPath, 'utf-8');
97
+ const idx = indexHTML(content, root, pattern);
98
+ for (const entry of idx)
99
+ objects.push({
100
+ ...entry,
101
+ source: absHtmlPath.slice(outputBasePath.length),
102
+ });
103
+ }
104
+
105
+ try {
106
+ await buildProseIndex(appId, `${indexName}-prose`, algoliaWriteKey, objects);
107
+ } catch (err) {
108
+ logger.error(`Failed to index prose content: ${err}`);
109
+ }
110
+ },
111
+ },
112
+ };
113
+ }