Skip to content

Commit 138e7fb

Browse files
feat: replace btoa with toBase64Url for encoding in drawMermaidImage (#9505)
Co-authored-by: Christian Bromann <git@bromann.dev>
1 parent f0ff0ff commit 138e7fb

File tree

4 files changed

+23
-10
lines changed

4 files changed

+23
-10
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@langchain/core": patch
3+
---
4+
5+
feat: replace btoa with toBase64Url for encoding in drawMermaidImage

libs/langchain-core/src/runnables/graph_mermaid.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Edge, Node } from "./types.js";
2+
import { toBase64Url } from "./utils.js";
23

34
function _escapeNodeLabel(nodeLabel: string): string {
45
// Escapes the node label for Mermaid syntax.
@@ -201,8 +202,8 @@ export async function drawMermaidImage(
201202
let backgroundColor = config?.backgroundColor ?? "white";
202203
const imageType = config?.imageType ?? "png";
203204

204-
// Use btoa for compatibility, assume ASCII
205-
const mermaidSyntaxEncoded = btoa(mermaidSyntax);
205+
const mermaidSyntaxEncoded = toBase64Url(mermaidSyntax);
206+
206207
// Check if the background color is a hexadecimal color code using regex
207208
if (backgroundColor !== undefined) {
208209
const hexColorPattern = /^#(?:[0-9a-fA-F]{3}){1,2}$/;

libs/langchain-core/src/runnables/tests/graph_mermaid.test.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
type MockedFunction,
99
} from "vitest";
1010
import { drawMermaidImage } from "../graph_mermaid.js";
11+
import { toBase64Url } from "../utils.js";
1112

1213
// Mock global fetch
1314
const mockFetch = vi.fn() as MockedFunction<typeof fetch>;
@@ -36,7 +37,7 @@ describe("drawMermaidImage", () => {
3637
expect(mockFetch).toHaveBeenCalledTimes(1);
3738

3839
// Check the URL construction
39-
const expectedEncodedSyntax = btoa(mermaidSyntax);
40+
const expectedEncodedSyntax = toBase64Url(mermaidSyntax);
4041
const expectedUrl = `https://mermaid.ink/img/${expectedEncodedSyntax}?bgColor=!white&type=png`;
4142
expect(mockFetch).toHaveBeenCalledWith(expectedUrl);
4243
});
@@ -54,7 +55,7 @@ describe("drawMermaidImage", () => {
5455
});
5556

5657
expect(result).toBe(mockBlob);
57-
const expectedEncodedSyntax = btoa(mermaidSyntax);
58+
const expectedEncodedSyntax = toBase64Url(mermaidSyntax);
5859
const expectedUrl = `https://mermaid.ink/img/${expectedEncodedSyntax}?bgColor=!white&type=jpeg`;
5960
expect(mockFetch).toHaveBeenCalledWith(expectedUrl);
6061
});
@@ -72,7 +73,7 @@ describe("drawMermaidImage", () => {
7273
});
7374

7475
expect(result).toBe(mockBlob);
75-
const expectedEncodedSyntax = btoa(mermaidSyntax);
76+
const expectedEncodedSyntax = toBase64Url(mermaidSyntax);
7677
const expectedUrl = `https://mermaid.ink/img/${expectedEncodedSyntax}?bgColor=!white&type=webp`;
7778
expect(mockFetch).toHaveBeenCalledWith(expectedUrl);
7879
});
@@ -90,7 +91,7 @@ describe("drawMermaidImage", () => {
9091
});
9192

9293
expect(result).toBe(mockBlob);
93-
const expectedEncodedSyntax = btoa(mermaidSyntax);
94+
const expectedEncodedSyntax = toBase64Url(mermaidSyntax);
9495
const expectedUrl = `https://mermaid.ink/img/${expectedEncodedSyntax}?bgColor=#FF5733&type=png`;
9596
expect(mockFetch).toHaveBeenCalledWith(expectedUrl);
9697
});
@@ -108,7 +109,7 @@ describe("drawMermaidImage", () => {
108109
});
109110

110111
expect(result).toBe(mockBlob);
111-
const expectedEncodedSyntax = btoa(mermaidSyntax);
112+
const expectedEncodedSyntax = toBase64Url(mermaidSyntax);
112113
const expectedUrl = `https://mermaid.ink/img/${expectedEncodedSyntax}?bgColor=#FFF&type=png`;
113114
expect(mockFetch).toHaveBeenCalledWith(expectedUrl);
114115
});
@@ -126,7 +127,7 @@ describe("drawMermaidImage", () => {
126127
});
127128

128129
expect(result).toBe(mockBlob);
129-
const expectedEncodedSyntax = btoa(mermaidSyntax);
130+
const expectedEncodedSyntax = toBase64Url(mermaidSyntax);
130131
const expectedUrl = `https://mermaid.ink/img/${expectedEncodedSyntax}?bgColor=!transparent&type=png`;
131132
expect(mockFetch).toHaveBeenCalledWith(expectedUrl);
132133
});
@@ -176,7 +177,7 @@ describe("drawMermaidImage", () => {
176177
const result = await drawMermaidImage(complexSyntax);
177178

178179
expect(result).toBe(mockBlob);
179-
const expectedEncodedSyntax = btoa(complexSyntax);
180+
const expectedEncodedSyntax = toBase64Url(complexSyntax);
180181
const expectedUrl = `https://mermaid.ink/img/${expectedEncodedSyntax}?bgColor=!white&type=png`;
181182
expect(mockFetch).toHaveBeenCalledWith(expectedUrl);
182183
});
@@ -194,7 +195,7 @@ describe("drawMermaidImage", () => {
194195
});
195196

196197
expect(result).toBe(mockBlob);
197-
const expectedEncodedSyntax = btoa(mermaidSyntax);
198+
const expectedEncodedSyntax = toBase64Url(mermaidSyntax);
198199
const expectedUrl = `https://mermaid.ink/img/${expectedEncodedSyntax}?bgColor=!white&type=png`;
199200
expect(mockFetch).toHaveBeenCalledWith(expectedUrl);
200201
});

libs/langchain-core/src/runnables/utils.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,9 @@ export class _RootEventFilter {
7474
return include;
7575
}
7676
}
77+
78+
export const toBase64Url = (str: string): string => {
79+
// Use btoa for compatibility, assume ASCII
80+
const encoded = btoa(str);
81+
return encoded.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
82+
};

0 commit comments

Comments
 (0)