Skip to content

Commit 37398fe

Browse files
[getsentry/action-github-commit] Auto commit
1 parent c0acd2b commit 37398fe

File tree

6 files changed

+91
-89
lines changed

6 files changed

+91
-89
lines changed

app/api/[transport]/route.ts

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,40 @@
1-
import {createMcpHandler} from "mcp-handler";
2-
import {z} from "zod";
1+
import {createMcpHandler} from 'mcp-handler';
2+
import {z} from 'zod';
33

4-
import {formatMatchAsBlock, searchIndex} from "../search/searchIndex";
5-
import {readDocContent} from "../../shared/docs-utils";
4+
import {readDocContent} from '../../shared/docs-utils';
5+
import {formatMatchAsBlock, searchIndex} from '../search/searchIndex';
66

77
const handler = createMcpHandler(
8-
(server) => {
8+
server => {
99
server.tool(
10-
"search_docs",
11-
"Search the precomputed markdown index and return matching documentation entry points.",
10+
'search_docs',
11+
'Search the precomputed markdown index and return matching documentation entry points.',
1212
{
1313
query: z.string().min(1),
1414
limit: z.number().int().min(1).max(25).default(5),
1515
},
1616
async ({query, limit}) => {
1717
const matches = await searchIndex(query, limit);
1818
const contentText = matches.length
19-
? matches.map(formatMatchAsBlock).join("\n\n")
20-
: "No matches found.";
19+
? matches.map(formatMatchAsBlock).join('\n\n')
20+
: 'No matches found.';
2121

2222
return {
23-
content: [{type: "text", text: contentText}],
23+
content: [{type: 'text', text: contentText}],
2424
};
2525
}
2626
);
2727

2828
server.tool(
29-
"get_doc",
30-
"Fetch raw markdown from the documentation exports. Reads local files when available, otherwise fetches from DOCS_PUBLIC_BASE.",
29+
'get_doc',
30+
'Fetch raw markdown from the documentation exports. Reads local files when available, otherwise fetches from DOCS_PUBLIC_BASE.',
3131
{
3232
path: z.string().min(1),
3333
},
3434
async ({path}) => {
3535
const content = await readDocContent(path);
3636
return {
37-
content: [{type: "text", text: content}],
37+
content: [{type: 'text', text: content}],
3838
};
3939
}
4040
);
@@ -43,15 +43,15 @@ const handler = createMcpHandler(
4343
// Optional server options
4444
},
4545
{
46-
basePath: "/api",
46+
basePath: '/api',
4747
maxDuration: 60,
4848
verboseLogs: false,
4949
}
5050
);
5151

5252
function normalizeRequest(request: Request): Request {
5353
const url = new URL(request.url);
54-
if (url.pathname.endsWith("/") && url.pathname.length > 1) {
54+
if (url.pathname.endsWith('/') && url.pathname.length > 1) {
5555
url.pathname = url.pathname.slice(0, -1);
5656
}
5757

@@ -60,7 +60,7 @@ function normalizeRequest(request: Request): Request {
6060
headers: request.headers,
6161
body: request.body,
6262
// @ts-ignore - duplex is needed for streaming
63-
duplex: "half",
63+
duplex: 'half',
6464
});
6565
}
6666

@@ -69,4 +69,4 @@ function wrappedHandler(request: Request) {
6969
return handler(normalizedRequest);
7070
}
7171

72-
export {wrappedHandler as GET, wrappedHandler as POST, wrappedHandler as DELETE};
72+
export {wrappedHandler as GET, wrappedHandler as POST, wrappedHandler as DELETE};

app/api/search/route.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
import {NextRequest, NextResponse} from "next/server";
1+
import {NextRequest, NextResponse} from 'next/server';
22

3-
import {mapMatchToResponse, searchIndex} from "./searchIndex";
3+
import {mapMatchToResponse, searchIndex} from './searchIndex';
44

5-
export const runtime = "nodejs";
5+
export const runtime = 'nodejs';
66

77
export async function GET(request: NextRequest) {
88
const {searchParams} = new URL(request.url);
9-
const query = searchParams.get("q") ?? "";
10-
const limitParam = searchParams.get("limit");
9+
const query = searchParams.get('q') ?? '';
10+
const limitParam = searchParams.get('limit');
1111
const limit = limitParam ? Math.min(25, Math.max(1, Number(limitParam))) : 10;
1212

1313
try {
@@ -25,9 +25,9 @@ export async function GET(request: NextRequest) {
2525
{
2626
query,
2727
limit,
28-
error: error instanceof Error ? error.message : "Unknown error",
28+
error: error instanceof Error ? error.message : 'Unknown error',
2929
},
3030
{status: 500}
3131
);
3232
}
33-
}
33+
}

app/api/search/searchIndex.ts

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import {promises as fs} from "node:fs";
2-
import path from "node:path";
1+
import {promises as fs} from 'node:fs';
2+
import path from 'node:path';
33

4-
import {buildDocUrl} from "../../shared/docs-utils";
4+
import {buildDocUrl} from '../../shared/docs-utils';
55

6-
const SEARCH_INDEX_PATH = path.join(process.cwd(), "public", "search-index.json");
6+
const SEARCH_INDEX_PATH = path.join(process.cwd(), 'public', 'search-index.json');
77

88
type RawSearchIndexEntry = {
99
content: string;
@@ -39,7 +39,7 @@ type CachedEntry = RawSearchIndexEntry & {
3939
let searchIndexPromise: Promise<CachedEntry[]> | null = null;
4040

4141
async function loadSearchIndexInternal(): Promise<CachedEntry[]> {
42-
const raw = await fs.readFile(SEARCH_INDEX_PATH, "utf8");
42+
const raw = await fs.readFile(SEARCH_INDEX_PATH, 'utf8');
4343
const parsed = JSON.parse(raw) as SearchIndexFile;
4444
return parsed.entries.map(entry => ({
4545
...entry,
@@ -162,50 +162,50 @@ export async function searchIndex(query: string, limit: number): Promise<SearchM
162162
}
163163

164164
function getInstallBias(entry: CachedEntry): number {
165-
const segments = entry.pathLower.split("/");
166-
const fileName = segments[segments.length - 1] ?? "";
167-
const baseName = fileName.replace(/\.md$/, "");
165+
const segments = entry.pathLower.split('/');
166+
const fileName = segments[segments.length - 1] ?? '';
167+
const baseName = fileName.replace(/\.md$/, '');
168168

169169
let bias = 0;
170170

171171
// Top-level platform doc like "platforms/react.md"
172-
if (segments[0] === "platforms" && segments.length === 2) {
172+
if (segments[0] === 'platforms' && segments.length === 2) {
173173
bias += 40;
174174
}
175175

176176
// JavaScript guide root doc like "platforms/javascript/guides/react.md"
177177
if (
178-
segments[0] === "platforms" &&
179-
segments[1] === "javascript" &&
180-
segments[2] === "guides" &&
178+
segments[0] === 'platforms' &&
179+
segments[1] === 'javascript' &&
180+
segments[2] === 'guides' &&
181181
segments.length === 4
182182
) {
183183
bias += 50;
184184
}
185185

186186
// Files under an install directory get a boost
187-
if (segments.includes("install")) {
187+
if (segments.includes('install')) {
188188
bias += 20;
189189
}
190190

191191
// Common install filenames get additional weight
192-
if (["install", "installation", "setup", "getting-started"].includes(baseName)) {
192+
if (['install', 'installation', 'setup', 'getting-started'].includes(baseName)) {
193193
bias += 25;
194194
}
195195

196196
return bias;
197197
}
198198

199199
export function formatMatchAsBlock(match: SearchMatch): string {
200-
const header = `# ${match.hierarchy.join(" > ")}`;
200+
const header = `# ${match.hierarchy.join(' > ')}`;
201201
const link = `[${match.title}](${match.path})`;
202202
const lines = [header, link];
203203

204204
if (match.snippet) {
205205
lines.push(match.snippet);
206206
}
207207

208-
return lines.join("\n");
208+
return lines.join('\n');
209209
}
210210

211211
export function mapMatchToResponse(match: SearchMatch) {
@@ -219,4 +219,4 @@ export function mapMatchToResponse(match: SearchMatch) {
219219
score: match.score,
220220
matchedTokens: match.matchedTokens,
221221
};
222-
}
222+
}

app/scripts/generate-search-index.mjs

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
11
#!/usr/bin/env node
2-
import {promises as fs} from "node:fs";
3-
import path from "node:path";
2+
import {promises as fs} from 'node:fs';
3+
import path from 'node:path';
44

5-
const MD_EXPORTS_ROOT = path.join(process.cwd(), "public", "md-exports");
6-
const OUTPUT_PATH = path.join(process.cwd(), "public", "search-index.json");
5+
const MD_EXPORTS_ROOT = path.join(process.cwd(), 'public', 'md-exports');
6+
const OUTPUT_PATH = path.join(process.cwd(), 'public', 'search-index.json');
77
const MAX_CONTENT_LENGTH = 4000;
88

99
const SEGMENT_LABELS = new Map(
1010
Object.entries({
11-
platforms: "Platform",
12-
javascript: "JavaScript",
13-
typescript: "TypeScript",
14-
python: "Python",
15-
android: "Android",
16-
apple: "Apple",
17-
react: "React",
18-
"react-native": "React Native",
19-
node: "Node",
20-
nodejs: "Node.js",
21-
dotnet: ".NET",
22-
php: "PHP",
23-
java: "Java",
24-
swift: "Swift",
25-
kotlin: "Kotlin",
26-
guides: "Guides",
27-
install: "Install",
28-
tracing: "Tracing",
29-
performance: "Performance",
11+
platforms: 'Platform',
12+
javascript: 'JavaScript',
13+
typescript: 'TypeScript',
14+
python: 'Python',
15+
android: 'Android',
16+
apple: 'Apple',
17+
react: 'React',
18+
'react-native': 'React Native',
19+
node: 'Node',
20+
nodejs: 'Node.js',
21+
dotnet: '.NET',
22+
php: 'PHP',
23+
java: 'Java',
24+
swift: 'Swift',
25+
kotlin: 'Kotlin',
26+
guides: 'Guides',
27+
install: 'Install',
28+
tracing: 'Tracing',
29+
performance: 'Performance',
3030
})
3131
);
3232

@@ -40,7 +40,7 @@ function titleCase(segment) {
4040
.split(/[-_]/)
4141
.filter(Boolean)
4242
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
43-
.join(" ");
43+
.join(' ');
4444
}
4545

4646
async function* walkMarkdownFiles(root) {
@@ -59,7 +59,7 @@ async function* walkMarkdownFiles(root) {
5959
continue;
6060
}
6161

62-
if (dirent.isFile() && dirent.name.endsWith(".md")) {
62+
if (dirent.isFile() && dirent.name.endsWith('.md')) {
6363
yield fullPath;
6464
}
6565
}
@@ -69,17 +69,17 @@ async function* walkMarkdownFiles(root) {
6969
function extractTitle(content) {
7070
const lines = content.split(/\r?\n/);
7171
for (const line of lines) {
72-
if (line.startsWith("#")) {
73-
return line.replace(/^#+\s*/, "").trim() || null;
72+
if (line.startsWith('#')) {
73+
return line.replace(/^#+\s*/, '').trim() || null;
7474
}
7575
}
7676
return null;
7777
}
7878

7979
function buildHierarchy(relativePath) {
80-
const segments = relativePath.split("/");
81-
const fileName = segments.pop() || "";
82-
const baseName = fileName.replace(/\.md$/i, "");
80+
const segments = relativePath.split('/');
81+
const fileName = segments.pop() || '';
82+
const baseName = fileName.replace(/\.md$/i, '');
8383
const pathSegments = [...segments, baseName];
8484
const labels = pathSegments.map(titleCase).filter(Boolean);
8585
return labels;
@@ -94,16 +94,16 @@ function toSummary(content) {
9494
}
9595
return trimmed.slice(0, 200);
9696
}
97-
return "";
97+
return '';
9898
}
9999

100100
async function main() {
101101
const entries = [];
102102
for await (const filePath of walkMarkdownFiles(MD_EXPORTS_ROOT)) {
103-
const relativePath = path.relative(MD_EXPORTS_ROOT, filePath).replace(/\\/g, "/");
104-
const content = await fs.readFile(filePath, "utf8");
103+
const relativePath = path.relative(MD_EXPORTS_ROOT, filePath).replace(/\\/g, '/');
104+
const content = await fs.readFile(filePath, 'utf8');
105105
const truncatedContent = content.slice(0, MAX_CONTENT_LENGTH);
106-
const title = extractTitle(content) ?? titleCase(path.basename(filePath, ".md"));
106+
const title = extractTitle(content) ?? titleCase(path.basename(filePath, '.md'));
107107
const hierarchy = buildHierarchy(relativePath);
108108
const summary = toSummary(truncatedContent);
109109

@@ -124,11 +124,11 @@ async function main() {
124124
entries,
125125
};
126126

127-
await fs.writeFile(OUTPUT_PATH, JSON.stringify(payload, null, 2), "utf8");
127+
await fs.writeFile(OUTPUT_PATH, JSON.stringify(payload, null, 2), 'utf8');
128128
console.log(`Generated ${entries.length} entries in ${OUTPUT_PATH}`);
129129
}
130130

131131
main().catch(error => {
132132
console.error(error);
133133
process.exitCode = 1;
134-
});
134+
});

0 commit comments

Comments
 (0)