Skip to content

Commit 02c85be

Browse files
goffrieConvex, Inc.
authored andcommitted
Enable --js-base-64 V8 flag (#42731)
This enables: - `Uint8Array.fromBase64` - `Uint8Array.fromHex` - `Uint8Array.prototype.setFromBase64` - `Uint8Array.prototype.setFromHex` - `Uint8Array.prototype.toBase64` - `Uint8Array.prototype.toHex` GitOrigin-RevId: bb258d9938c75ae35af80804bf0f2a051846d46f
1 parent c340d5e commit 02c85be

File tree

3 files changed

+105
-28
lines changed

3 files changed

+105
-28
lines changed

crates/isolate/src/client.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,7 @@ pub fn initialize_v8() {
586586
// tell V8 it can use up to 2MiB of stack space itself. The
587587
// default is 1MiB. Note that the flag is in KiB (https://github.com/v8/v8/blob/master/src/flags/flag-definitions.h#L1594).
588588
"--stack-size=2048".to_string(),
589+
"--js-base-64".to_string(),
589590
];
590591
if let Ok(flags) = env::var("ISOLATE_V8_FLAGS") {
591592
argv.extend(
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
Copied from https://github.com/microsoft/TypeScript/blob/6f4fb0145845db22791188c4d38e3a1a0edfd449/src/lib/esnext.typedarrays.d.ts
3+
Remove this when we update to a TypeScript version including https://github.com/microsoft/TypeScript/pull/61696
4+
5+
Copyright (c) 2025 Microsoft Corporation
6+
7+
Licensed under the Apache License, Version 2.0 (the "License");
8+
you may not use this file except in compliance with the License.
9+
You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
*/
19+
20+
interface Uint8Array<_TArrayBuffer extends ArrayBufferLike = ArrayBufferLike> {
21+
/**
22+
* Converts the `Uint8Array` to a base64-encoded string.
23+
* @param options If provided, sets the alphabet and padding behavior used.
24+
* @returns A base64-encoded string.
25+
*/
26+
toBase64(options?: {
27+
alphabet?: "base64" | "base64url" | undefined;
28+
omitPadding?: boolean | undefined;
29+
}): string;
30+
31+
/**
32+
* Sets the `Uint8Array` from a base64-encoded string.
33+
* @param string The base64-encoded string.
34+
* @param options If provided, specifies the alphabet and handling of the last chunk.
35+
* @returns An object containing the number of bytes read and written.
36+
* @throws {SyntaxError} If the input string contains characters outside the specified alphabet, or if the last
37+
* chunk is inconsistent with the `lastChunkHandling` option.
38+
*/
39+
setFromBase64(
40+
string: string,
41+
options?: {
42+
alphabet?: "base64" | "base64url" | undefined;
43+
lastChunkHandling?:
44+
| "loose"
45+
| "strict"
46+
| "stop-before-partial"
47+
| undefined;
48+
},
49+
): {
50+
read: number;
51+
written: number;
52+
};
53+
54+
/**
55+
* Converts the `Uint8Array` to a base16-encoded string.
56+
* @returns A base16-encoded string.
57+
*/
58+
toHex(): string;
59+
60+
/**
61+
* Sets the `Uint8Array` from a base16-encoded string.
62+
* @param string The base16-encoded string.
63+
* @returns An object containing the number of bytes read and written.
64+
*/
65+
setFromHex(string: string): {
66+
read: number;
67+
written: number;
68+
};
69+
}
70+
71+
interface Uint8ArrayConstructor {
72+
/**
73+
* Creates a new `Uint8Array` from a base64-encoded string.
74+
* @param string The base64-encoded string.
75+
* @param options If provided, specifies the alphabet and handling of the last chunk.
76+
* @returns A new `Uint8Array` instance.
77+
* @throws {SyntaxError} If the input string contains characters outside the specified alphabet, or if the last
78+
* chunk is inconsistent with the `lastChunkHandling` option.
79+
*/
80+
fromBase64(
81+
string: string,
82+
options?: {
83+
alphabet?: "base64" | "base64url" | undefined;
84+
lastChunkHandling?:
85+
| "loose"
86+
| "strict"
87+
| "stop-before-partial"
88+
| undefined;
89+
},
90+
): Uint8Array<ArrayBuffer>;
91+
92+
/**
93+
* Creates a new `Uint8Array` from a base16-encoded string.
94+
* @returns A new `Uint8Array` instance.
95+
*/
96+
fromHex(string: string): Uint8Array<ArrayBuffer>;
97+
}

npm-packages/udf-tests/convex/js_builtins/crypto.ts

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -468,11 +468,7 @@ async function importRsaPkcs8() {
468468
pemHeader.length,
469469
keyFile.length - pemFooter.length,
470470
);
471-
const binaryDerString = atob(pemContents);
472-
const binaryDer = new Uint8Array(binaryDerString.length);
473-
for (let i = 0; i < binaryDerString.length; i++) {
474-
binaryDer[i] = binaryDerString.charCodeAt(i);
475-
}
471+
const binaryDer = Uint8Array.fromBase64(pemContents);
476472

477473
const key = await crypto.subtle.importKey(
478474
"pkcs8",
@@ -514,11 +510,7 @@ async function importRsaSpki() {
514510
pemHeader.length,
515511
keyFile.length - pemFooter.length,
516512
);
517-
const binaryDerString = atob(pemContents);
518-
const binaryDer = new Uint8Array(binaryDerString.length);
519-
for (let i = 0; i < binaryDerString.length; i++) {
520-
binaryDer[i] = binaryDerString.charCodeAt(i);
521-
}
513+
const binaryDer = Uint8Array.fromBase64(pemContents);
522514

523515
const key = await crypto.subtle.importKey(
524516
"spki",
@@ -755,11 +747,7 @@ async function importNonInteroperableRsaPkcs8() {
755747
pemHeader.length,
756748
keyFile.length - pemFooter.length,
757749
);
758-
const binaryDerString = atob(pemContents);
759-
const binaryDer = new Uint8Array(binaryDerString.length);
760-
for (let i = 0; i < binaryDerString.length; i++) {
761-
binaryDer[i] = binaryDerString.charCodeAt(i);
762-
}
750+
const binaryDer = Uint8Array.fromBase64(pemContents);
763751

764752
await expect(
765753
crypto.subtle.importKey(
@@ -1745,9 +1733,7 @@ async function testHMACSign() {
17451733
cryptoKey,
17461734
new Uint8Array(8),
17471735
);
1748-
const actualBase64String = btoa(
1749-
String.fromCharCode(...new Uint8Array(actual)),
1750-
);
1736+
const actualBase64String = new Uint8Array(actual).toBase64();
17511737
// This value is from running the above code in a browser
17521738
const expected = "SdJ6jecfYHT1LzY/Vh03WauHRbzWZeVjDFL4ietJNCw=";
17531739
assert.strictEqual(actualBase64String, expected);
@@ -1771,12 +1757,7 @@ async function testHMACVerify() {
17711757
["verify"],
17721758
);
17731759

1774-
// Convert the base64 signature back to Uint8Array
1775-
const signature = new Uint8Array(
1776-
atob(base64Signature)
1777-
.split("")
1778-
.map((c) => c.charCodeAt(0)),
1779-
);
1760+
const signature = Uint8Array.fromBase64(base64Signature);
17801761

17811762
const isVerified = await subtle.verify(
17821763
{ name: "HMAC" },
@@ -1810,9 +1791,7 @@ async function testHMACSignAlternativeSyntax() {
18101791
await subtle.importKey("raw", key.buffer, importParams, false, ["sign"]);
18111792

18121793
const actual = await subtle.sign("hmac", cryptoKey, new Uint8Array(8));
1813-
const actualBase64String = btoa(
1814-
String.fromCharCode(...new Uint8Array(actual)),
1815-
);
1794+
const actualBase64String = new Uint8Array(actual).toBase64();
18161795
// This value is from running the above code in a browser
18171796
const expected = "SdJ6jecfYHT1LzY/Vh03WauHRbzWZeVjDFL4ietJNCw=";
18181797
assert.strictEqual(actualBase64String, expected);
@@ -1914,7 +1893,7 @@ async function testDigest() {
19141893
"SHA-256",
19151894
new TextEncoder().encode("hello"),
19161895
);
1917-
const digestBase64 = btoa(String.fromCharCode(...new Uint8Array(digest)));
1896+
const digestBase64 = new Uint8Array(digest).toBase64();
19181897
assert.strictEqual(
19191898
digestBase64,
19201899
"LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=",

0 commit comments

Comments
 (0)