Skip to content

Commit 1d95e82

Browse files
chore(api): Changed the search api to return raw source (instead of base64 encoding) (#356)
This PR alters the behaviour of the search api (and all apis that depend on it) to return raw source code instead of a base64 encoding. Reasoning: we are decoding it on the client in multiple different places, so it would be beneficial to decode it in a single spot. **Note**: This is a **breaking change** to the API surface. However, since the API surface is still unofficial/unsupported, I will roll this as a patch version change. See #101
1 parent 22d548e commit 1d95e82

File tree

12 files changed

+25
-47
lines changed

12 files changed

+25
-47
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
<!-- @NOTE: this release includes a API change that affects the MCP package (@sourcebot/mcp). On release, bump the MCP package's version and delete this message. -->
11+
1012
### Fixed
1113
- Delete account join request when redeeming an invite. [#352](https://github.com/sourcebot-dev/sourcebot/pull/352)
1214
- Fix issue where a repository would not be included in a search context if the context was created before the repository. [#354](https://github.com/sourcebot-dev/sourcebot/pull/354)
1315

16+
### Changed
17+
- Changed search api (and all apis that depend on it) to return raw source code instead of base64 encoded string. ([356](https://github.com/sourcebot-dev/sourcebot/pull/356)).
18+
19+
1420
## [4.3.0] - 2025-06-11
1521

1622
### Added

packages/mcp/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Changed
11+
- Updated API client to match the latest Sourcebot release. [#356](https://github.com/sourcebot-dev/sourcebot/pull/356)
12+
1013
## [1.0.2] - 2025-05-28
1114

1215
### Changed

packages/mcp/src/index.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { z } from 'zod';
88
import { listRepos, search, getFileSource } from './client.js';
99
import { env, numberSchema } from './env.js';
1010
import { TextContent } from './types.js';
11-
import { base64Decode, isServiceError } from './utils.js';
11+
import { isServiceError } from './utils.js';
1212

1313
// Create MCP server
1414
const server = new McpServer({
@@ -114,8 +114,7 @@ server.tool(
114114

115115
if (includeCodeSnippets) {
116116
const snippets = file.chunks.map(chunk => {
117-
const content = base64Decode(chunk.content);
118-
return `\`\`\`\n${content}\n\`\`\``
117+
return `\`\`\`\n${chunk.content}\n\`\`\``
119118
}).join('\n');
120119
text += `\n\n${snippets}`;
121120
}
@@ -201,7 +200,7 @@ server.tool(
201200

202201
const content: TextContent[] = [{
203202
type: "text",
204-
text: `file: ${fileName}\nrepository: ${repoId}\nlanguage: ${response.language}\nsource:\n${base64Decode(response.source)}`,
203+
text: `file: ${fileName}\nrepository: ${repoId}\nlanguage: ${response.language}\nsource:\n${response.source}`,
205204
}]
206205

207206
return {

packages/mcp/src/utils.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
import { ServiceError } from "./types.js";
22

3-
// From https://developer.mozilla.org/en-US/docs/Glossary/Base64#the_unicode_problem
4-
export const base64Decode = (base64: string): string => {
5-
const binString = atob(base64);
6-
return Buffer.from(Uint8Array.from(binString, (m) => m.codePointAt(0)!).buffer).toString();
7-
}
83

94
export const isServiceError = (data: unknown): data is ServiceError => {
105
return typeof data === 'object' &&

packages/web/src/app/[domain]/browse/[...path]/components/codePreviewPanel.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import { base64Decode, getCodeHostInfoForRepo, unwrapServiceError } from "@/lib/utils";
3+
import { getCodeHostInfoForRepo, unwrapServiceError } from "@/lib/utils";
44
import { useBrowseParams } from "@/app/[domain]/browse/hooks/useBrowseParams";
55
import { useQuery } from "@tanstack/react-query";
66
import { getFileSource } from "@/features/search/fileSourceApi";
@@ -88,7 +88,7 @@ export const CodePreviewPanel = () => {
8888
</div>
8989
<Separator />
9090
<PureCodePreviewPanel
91-
source={base64Decode(fileSourceResponse.source)}
91+
source={fileSourceResponse.source}
9292
language={fileSourceResponse.language}
9393
repoName={repoName}
9494
path={path}

packages/web/src/app/[domain]/search/components/codePreviewPanel/index.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { useDomain } from "@/hooks/useDomain";
77
import { SymbolIcon } from "@radix-ui/react-icons";
88
import { SetStateAction, Dispatch, useMemo } from "react";
99
import { getFileSource } from "@/features/search/fileSourceApi";
10-
import { base64Decode } from "@/lib/utils";
1110
import { unwrapServiceError } from "@/lib/utils";
1211

1312
interface CodePreviewPanelProps {
@@ -41,10 +40,8 @@ export const CodePreviewPanel = ({
4140
}, domain)
4241
),
4342
select: (data) => {
44-
const decodedSource = base64Decode(data.source);
45-
4643
return {
47-
content: decodedSource,
44+
content: data.source,
4845
filepath: previewedFile.fileName.text,
4946
matches: previewedFile.chunks,
5047
link: previewedFile.webUrl,

packages/web/src/app/[domain]/search/components/searchResultsPanel/fileMatch.tsx

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
'use client';
22

3-
import { useCallback, useMemo } from "react";
3+
import { useCallback } from "react";
44
import { SearchResultFile, SearchResultChunk } from "@/features/search/types";
5-
import { base64Decode } from "@/lib/utils";
65
import { LightweightCodeHighlighter } from "@/app/[domain]/components/lightweightCodeHighlighter";
76

87

@@ -17,17 +16,12 @@ export const FileMatch = ({
1716
file,
1817
onOpen: _onOpen,
1918
}: FileMatchProps) => {
20-
21-
const content = useMemo(() => {
22-
return base64Decode(match.content);
23-
}, [match.content]);
24-
2519
const onOpen = useCallback((isCtrlKeyPressed: boolean) => {
2620
const startLineNumber = match.contentStart.lineNumber;
27-
const endLineNumber = content.trimEnd().split('\n').length + startLineNumber - 1;
21+
const endLineNumber = match.content.trimEnd().split('\n').length + startLineNumber - 1;
2822

2923
_onOpen(startLineNumber, endLineNumber, isCtrlKeyPressed);
30-
}, [content, match.contentStart.lineNumber, _onOpen]);
24+
}, [match.content, match.contentStart.lineNumber, _onOpen]);
3125

3226
// If it's just the title, don't show a code preview
3327
if (match.matchRanges.length === 0) {
@@ -57,7 +51,7 @@ export const FileMatch = ({
5751
lineNumbersOffset={match.contentStart.lineNumber}
5852
renderWhitespace={true}
5953
>
60-
{content}
54+
{match.content}
6155
</LightweightCodeHighlighter>
6256
</div>
6357
);

packages/web/src/ee/features/codeNav/components/exploreMenu/referenceList.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { PathHeader } from "@/app/[domain]/components/pathHeader";
55
import { LightweightCodeHighlighter } from "@/app/[domain]/components/lightweightCodeHighlighter";
66
import { FindRelatedSymbolsResponse } from "@/features/codeNav/types";
77
import { RepositoryInfo, SourceRange } from "@/features/search/types";
8-
import { base64Decode } from "@/lib/utils";
98
import { useMemo, useRef } from "react";
109
import useCaptureEvent from "@/hooks/useCaptureEvent";
1110
import { useVirtualizer } from "@tanstack/react-virtual";
@@ -155,10 +154,6 @@ const ReferenceListItem = ({
155154
onClick,
156155
onMouseEnter,
157156
}: ReferenceListItemProps) => {
158-
const decodedLineContent = useMemo(() => {
159-
return base64Decode(lineContent);
160-
}, [lineContent]);
161-
162157
const highlightRanges = useMemo(() => [range], [range]);
163158

164159
return (
@@ -174,7 +169,7 @@ const ReferenceListItem = ({
174169
lineNumbersOffset={range.start.lineNumber}
175170
renderWhitespace={false}
176171
>
177-
{decodedLineContent}
172+
{lineContent}
178173
</LightweightCodeHighlighter>
179174
</div>
180175
)

packages/web/src/ee/features/codeNav/components/symbolHoverPopup/symbolDefinitionPreview.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip
33
import { LightweightCodeHighlighter } from "@/app/[domain]/components/lightweightCodeHighlighter";
44
import { useMemo } from "react";
55
import { SourceRange } from "@/features/search/types";
6-
import { base64Decode } from "@/lib/utils";
76

87
interface SymbolDefinitionPreviewProps {
98
symbolDefinition: {
@@ -21,10 +20,6 @@ export const SymbolDefinitionPreview = ({
2120
const { lineContent, language, range } = symbolDefinition;
2221
const highlightRanges = useMemo(() => [range], [range]);
2322

24-
const decodedLineContent = useMemo(() => {
25-
return base64Decode(lineContent);
26-
}, [lineContent]);
27-
2823
return (
2924
<div className="flex flex-col gap-2 mb-2">
3025
<Tooltip
@@ -55,7 +50,7 @@ export const SymbolDefinitionPreview = ({
5550
lineNumbersOffset={range.start.lineNumber}
5651
renderWhitespace={false}
5752
>
58-
{decodedLineContent}
53+
{lineContent}
5954
</LightweightCodeHighlighter>
6055
</div>
6156
)

packages/web/src/features/agents/review-agent/nodes/fetchFileContent.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { sourcebot_context, sourcebot_pr_payload } from "@/features/agents/review-agent/types";
22
import { getFileSource } from "@/features/search/fileSourceApi";
33
import { fileSourceResponseSchema } from "@/features/search/schemas";
4-
import { base64Decode } from "@/lib/utils";
54
import { isServiceError } from "@/lib/utils";
65
import { env } from "@/env.mjs";
76
import { createLogger } from "@sourcebot/logger";
@@ -24,7 +23,7 @@ export const fetchFileContent = async (pr_payload: sourcebot_pr_payload, filenam
2423
}
2524

2625
const fileSourceResponse = fileSourceResponseSchema.parse(response);
27-
const fileContent = base64Decode(fileSourceResponse.source);
26+
const fileContent = fileSourceResponse.source;
2827

2928
const fileContentContext: sourcebot_context = {
3029
type: "file_content",

packages/web/src/features/search/searchApi.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { SearchRequest, SearchResponse, SourceRange } from "./types";
1010
import { OrgRole, Repo } from "@sourcebot/db";
1111
import * as Sentry from "@sentry/nextjs";
1212
import { sew, withAuth, withOrgMembership } from "@/actions";
13+
import { base64Decode } from "@sourcebot/shared";
1314

1415
// List of supported query prefixes in zoekt.
1516
// @see : https://github.com/sourcebot-dev/zoekt/blob/main/query/parse.go#L417
@@ -264,7 +265,7 @@ export const search = async ({ query, matches, contextLines, whole }: SearchRequ
264265
.filter((chunk) => !chunk.FileName) // Filter out filename chunks.
265266
.map((chunk) => {
266267
return {
267-
content: chunk.Content,
268+
content: base64Decode(chunk.Content),
268269
matchRanges: chunk.Ranges.map((range) => ({
269270
start: {
270271
byteOffset: range.Start.ByteOffset,
@@ -295,7 +296,7 @@ export const search = async ({ query, matches, contextLines, whole }: SearchRequ
295296
}
296297
}),
297298
branches: file.Branches,
298-
content: file.Content,
299+
content: file.Content ? base64Decode(file.Content) : undefined,
299300
}
300301
}).filter((file) => file !== undefined) ?? [];
301302

packages/web/src/lib/utils.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -301,12 +301,6 @@ export const isServiceError = (data: unknown): data is ServiceError => {
301301
'message' in data;
302302
}
303303

304-
// From https://developer.mozilla.org/en-US/docs/Glossary/Base64#the_unicode_problem
305-
export const base64Decode = (base64: string): string => {
306-
const binString = atob(base64);
307-
return Buffer.from(Uint8Array.from(binString, (m) => m.codePointAt(0)!).buffer).toString();
308-
}
309-
310304
// @see: https://stackoverflow.com/a/65959350/23221295
311305
export const isDefined = <T>(arg: T | null | undefined): arg is T extends null | undefined ? never : T => {
312306
return arg !== null && arg !== undefined;

0 commit comments

Comments
 (0)