@stainless-api/docs 0.1.0-beta.123 → 0.1.0-beta.125
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 +18 -0
- package/package.json +10 -8
- package/plugin/react/Routing.tsx +2 -2
- package/stl-docs/proseDocSync.test.ts +74 -0
- package/stl-docs/proseDocSync.ts +33 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @stainless-api/docs
|
|
2
2
|
|
|
3
|
+
## 0.1.0-beta.125
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 975ba51: cap prose indexing batches to 30MB
|
|
8
|
+
- Updated dependencies [005419c]
|
|
9
|
+
- @stainless-api/docs-ui@0.1.0-beta.89
|
|
10
|
+
- @stainless-api/docs-search@0.1.0-beta.42
|
|
11
|
+
|
|
12
|
+
## 0.1.0-beta.124
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- 152c864: don’t try to load github themes unconditionally
|
|
17
|
+
- Updated dependencies [af5a5d7]
|
|
18
|
+
- @stainless-api/docs-ui@0.1.0-beta.88
|
|
19
|
+
- @stainless-api/docs-search@0.1.0-beta.41
|
|
20
|
+
|
|
3
21
|
## 0.1.0-beta.123
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stainless-api/docs",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.125",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"node": ">=22.12.0"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"@astrojs/starlight": ">=0.
|
|
38
|
-
"astro": ">=
|
|
37
|
+
"@astrojs/starlight": ">=0.38.0",
|
|
38
|
+
"astro": ">=6.0.0",
|
|
39
39
|
"react": ">=19.0.0",
|
|
40
40
|
"react-dom": ">=19.0.0",
|
|
41
41
|
"vite": ">=7.3.1"
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"clsx": "^2.1.1",
|
|
50
50
|
"dotenv": "17.3.1",
|
|
51
51
|
"lucide-react": "^0.577.0",
|
|
52
|
-
"marked": "^17.0.
|
|
52
|
+
"marked": "^17.0.5",
|
|
53
53
|
"node-html-parser": "^7.1.0",
|
|
54
54
|
"rehype-parse": "^9.0.1",
|
|
55
55
|
"rehype-remark": "^10.0.1",
|
|
@@ -60,9 +60,9 @@
|
|
|
60
60
|
"unified": "^11.0.5",
|
|
61
61
|
"vite-plugin-prebundle-workers": "^0.2.0",
|
|
62
62
|
"web-worker": "^1.5.0",
|
|
63
|
-
"yaml": "^2.8.
|
|
64
|
-
"@stainless-api/docs-search": "0.1.0-beta.
|
|
65
|
-
"@stainless-api/docs-ui": "0.1.0-beta.
|
|
63
|
+
"yaml": "^2.8.3",
|
|
64
|
+
"@stainless-api/docs-search": "0.1.0-beta.42",
|
|
65
|
+
"@stainless-api/docs-ui": "0.1.0-beta.89",
|
|
66
66
|
"@stainless-api/ui-primitives": "0.1.0-beta.50"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
"tsx": "^4.21.0",
|
|
77
77
|
"typescript": "5.9.3",
|
|
78
78
|
"vite": "^7.3.1",
|
|
79
|
+
"vitest": "^4.1.2",
|
|
79
80
|
"zod": "^4.3.6",
|
|
80
81
|
"@stainless/eslint-config": "0.1.0-beta.1",
|
|
81
82
|
"@stainless/sdk-json": "^0.1.0-beta.9"
|
|
@@ -84,6 +85,7 @@
|
|
|
84
85
|
"vendor-deps": "tsx scripts/vendor_deps.ts",
|
|
85
86
|
"lint": "eslint --flag unstable_native_nodejs_ts_config . --max-warnings 0",
|
|
86
87
|
"sync": "astro sync",
|
|
87
|
-
"check:types": "astro check"
|
|
88
|
+
"check:types": "astro check",
|
|
89
|
+
"test": "vitest run"
|
|
88
90
|
}
|
|
89
91
|
}
|
package/plugin/react/Routing.tsx
CHANGED
|
@@ -425,8 +425,8 @@ async function astroHighlight() {
|
|
|
425
425
|
|
|
426
426
|
astroShikiHighlighter = createHighlighter({
|
|
427
427
|
themes: [
|
|
428
|
-
'github-
|
|
429
|
-
'github-
|
|
428
|
+
HIGHLIGHT_THEMES?.dark ?? 'github-dark',
|
|
429
|
+
HIGHLIGHT_THEMES?.light ?? 'github-light',
|
|
430
430
|
{
|
|
431
431
|
name: 'stainless-docs-json',
|
|
432
432
|
colors: {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { batchBySize, MAX_BATCH_BYTES, MAX_BATCH_DOCS } from './proseDocSync';
|
|
3
|
+
|
|
4
|
+
function makeDoc(docId: string, sizeBytes: number) {
|
|
5
|
+
return {
|
|
6
|
+
docId,
|
|
7
|
+
content: Buffer.alloc(sizeBytes, 'x'),
|
|
8
|
+
sha256: 'fake',
|
|
9
|
+
source: `/${docId}`,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
describe('batchBySize', () => {
|
|
14
|
+
it('returns empty array for no docs', () => {
|
|
15
|
+
expect(batchBySize([])).toEqual([]);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('puts all docs in one batch when under both limits', () => {
|
|
19
|
+
const docs = Array.from({ length: 5 }, (_, i) => makeDoc(`doc-${i}`, 100));
|
|
20
|
+
const batches = batchBySize(docs);
|
|
21
|
+
expect(batches).toHaveLength(1);
|
|
22
|
+
expect(batches[0]).toHaveLength(5);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('splits at MAX_BATCH_DOCS', () => {
|
|
26
|
+
const docs = Array.from({ length: MAX_BATCH_DOCS + 10 }, (_, i) => makeDoc(`doc-${i}`, 10));
|
|
27
|
+
const batches = batchBySize(docs);
|
|
28
|
+
expect(batches).toHaveLength(2);
|
|
29
|
+
expect(batches[0]).toHaveLength(MAX_BATCH_DOCS);
|
|
30
|
+
expect(batches[1]).toHaveLength(10);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('splits when cumulative size exceeds MAX_BATCH_BYTES', () => {
|
|
34
|
+
const docSize = 10 * 1024 * 1024; // 10MB each
|
|
35
|
+
// 4 docs = 40MB > 30MB limit, so should split into [3, 1]
|
|
36
|
+
const docs = Array.from({ length: 4 }, (_, i) => makeDoc(`doc-${i}`, docSize));
|
|
37
|
+
const batches = batchBySize(docs);
|
|
38
|
+
expect(batches).toHaveLength(2);
|
|
39
|
+
expect(batches[0]).toHaveLength(3);
|
|
40
|
+
expect(batches[1]).toHaveLength(1);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('handles a single doc larger than MAX_BATCH_BYTES', () => {
|
|
44
|
+
const docs = [makeDoc('huge', MAX_BATCH_BYTES + 1)];
|
|
45
|
+
const batches = batchBySize(docs);
|
|
46
|
+
// Still goes into a batch on its own — we can't split a single doc
|
|
47
|
+
expect(batches).toHaveLength(1);
|
|
48
|
+
expect(batches[0]).toHaveLength(1);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('byte limit takes precedence over doc count when hit first', () => {
|
|
52
|
+
// 2 docs of 20MB each = 40MB > 30MB limit, well under 100 doc limit
|
|
53
|
+
const docs = [makeDoc('a', 20 * 1024 * 1024), makeDoc('b', 20 * 1024 * 1024)];
|
|
54
|
+
const batches = batchBySize(docs);
|
|
55
|
+
expect(batches).toHaveLength(2);
|
|
56
|
+
expect(batches[0]).toHaveLength(1);
|
|
57
|
+
expect(batches[1]).toHaveLength(1);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('creates multiple batches for many large docs', () => {
|
|
61
|
+
const docSize = 8 * 1024 * 1024; // 8MB each
|
|
62
|
+
// 10 docs * 8MB = 80MB, should split into batches of ~3 (24MB each)
|
|
63
|
+
const docs = Array.from({ length: 10 }, (_, i) => makeDoc(`doc-${i}`, docSize));
|
|
64
|
+
const batches = batchBySize(docs);
|
|
65
|
+
// Every batch should be <= MAX_BATCH_BYTES
|
|
66
|
+
for (const batch of batches) {
|
|
67
|
+
const totalBytes = batch.reduce((sum, d) => sum + d.content.byteLength, 0);
|
|
68
|
+
expect(totalBytes).toBeLessThanOrEqual(MAX_BATCH_BYTES);
|
|
69
|
+
}
|
|
70
|
+
// All docs should be accounted for
|
|
71
|
+
const totalDocs = batches.reduce((sum, b) => sum + b.length, 0);
|
|
72
|
+
expect(totalDocs).toBe(10);
|
|
73
|
+
});
|
|
74
|
+
});
|
package/stl-docs/proseDocSync.ts
CHANGED
|
@@ -9,6 +9,10 @@ import { NormalizedStainlessDocsConfig } from './loadStlDocsConfig';
|
|
|
9
9
|
|
|
10
10
|
const DOCS_API_BASE_URL = 'https://api.stainlessapi.com';
|
|
11
11
|
|
|
12
|
+
export const MAX_BATCH_DOCS = 100;
|
|
13
|
+
// Cloud Run has a 32MB request body limit; leave headroom for JSON envelope overhead
|
|
14
|
+
export const MAX_BATCH_BYTES = 30 * 1024 * 1024;
|
|
15
|
+
|
|
12
16
|
// ─── API client ──────────────────────────────────────────────────────
|
|
13
17
|
|
|
14
18
|
async function docsApiRequest(
|
|
@@ -96,6 +100,33 @@ function diffManifests(
|
|
|
96
100
|
return { toPut, toDelete };
|
|
97
101
|
}
|
|
98
102
|
|
|
103
|
+
// ─── Batching ────────────────────────────────────────────────────────
|
|
104
|
+
|
|
105
|
+
export function batchBySize(docs: (LocalDoc & { docId: string })[]): (LocalDoc & { docId: string })[][] {
|
|
106
|
+
const batches: (LocalDoc & { docId: string })[][] = [];
|
|
107
|
+
let current: (LocalDoc & { docId: string })[] = [];
|
|
108
|
+
let currentBytes = 0;
|
|
109
|
+
|
|
110
|
+
for (const doc of docs) {
|
|
111
|
+
const docBytes = doc.content.byteLength;
|
|
112
|
+
|
|
113
|
+
if (
|
|
114
|
+
current.length > 0 &&
|
|
115
|
+
(current.length >= MAX_BATCH_DOCS || currentBytes + docBytes > MAX_BATCH_BYTES)
|
|
116
|
+
) {
|
|
117
|
+
batches.push(current);
|
|
118
|
+
current = [];
|
|
119
|
+
currentBytes = 0;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
current.push(doc);
|
|
123
|
+
currentBytes += docBytes;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (current.length > 0) batches.push(current);
|
|
127
|
+
return batches;
|
|
128
|
+
}
|
|
129
|
+
|
|
99
130
|
// ─── Import & delete ────────────────────────────────────────────────
|
|
100
131
|
|
|
101
132
|
type ImportJobResult = { succeeded: number; failed: number; errors: string[] };
|
|
@@ -108,10 +139,9 @@ async function importDocuments(
|
|
|
108
139
|
logger: AstroIntegrationLogger,
|
|
109
140
|
): Promise<ImportJobResult> {
|
|
110
141
|
const totals: ImportJobResult = { succeeded: 0, failed: 0, errors: [] };
|
|
142
|
+
const batches = batchBySize(docs);
|
|
111
143
|
|
|
112
|
-
for (
|
|
113
|
-
const batch = docs.slice(i, i + 100);
|
|
114
|
-
|
|
144
|
+
for (const batch of batches) {
|
|
115
145
|
let response: Response;
|
|
116
146
|
try {
|
|
117
147
|
response = await docsApiRequest('POST', `/api/docs-sites/${docsSiteId}/documents/import`, apiKey, {
|