Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions crates/native-sidecar/tests/builtin_conformance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ const BUILTIN_CONFORMANCE_CASES: &[&str] = &[
"events",
"stream",
"buffer",
"global_base64",
"url",
"stdlib_polyfill",
"web_streams",
Expand Down Expand Up @@ -3834,6 +3835,45 @@ fn buffer_concat_truncation_matches_host_node() {
run_isolated_builtin_conformance_test("buffer-concat-truncation");
}

fn global_base64_conformance_matches_host_node() {
assert_conformance(
"global_base64",
r#"
function describeError(callback) {
try {
callback();
return { threw: false };
} catch (error) {
return {
threw: true,
name: error?.name ?? null,
code: error?.code ?? null,
message: error?.message ?? null,
};
}
}

console.log(JSON.stringify({
atobText: atob("aGVsbG8="),
atobUnpaddedSingleByte: atob("YQ"),
atobUnpaddedTwoBytes: atob("YWI"),
atobWhitespace: atob(" YQ== \n"),
atobNumberCoercion: atob(1234),
btoaText: btoa("hello"),
btoaNumberCoercion: btoa(1234),
invalidAtob: describeError(() => atob("%%%")),
invalidShortAtob: describeError(() => atob("Y")),
invalidPaddingAtob: describeError(() => atob("AA=A")),
invalidPartialPaddingAtob: describeError(() => atob("YQ=")),
invalidOnlyPaddingAtob: describeError(() => atob("==")),
invalidUrlSafeDashAtob: describeError(() => atob("AA-A")),
invalidUrlSafeUnderscoreAtob: describeError(() => atob("AA_A")),
invalidBtoa: describeError(() => btoa("✓")),
}));
"#,
);
}

fn mkdtemp_sync_collision_safe_matches_host_node_impl() {
let cwd = temp_dir("mkdtemp-sync-collision-safe");
let entrypoint = cwd.join("entry.mjs");
Expand Down Expand Up @@ -4874,6 +4914,7 @@ fn run_named_case(case_name: &str) {
"events" => events_conformance_matches_host_node(),
"stream" => stream_conformance_matches_host_node(),
"buffer" => buffer_conformance_matches_host_node(),
"global_base64" => global_base64_conformance_matches_host_node(),
"url" => url_conformance_matches_host_node(),
"stdlib_polyfill" => stdlib_polyfill_conformance_matches_host_node(),
"web_streams" => web_streams_conformance_matches_host_node(),
Expand Down
37 changes: 32 additions & 5 deletions packages/build-tools/bridge-src/builtins/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1141,6 +1141,9 @@ function setupGlobals() {
g.Event = Event;
g.CustomEvent = CustomEvent;
g.EventTarget = EventTarget;
if (typeof g.DOMException === "undefined") {
g.DOMException = SandboxDOMException;
}
if (typeof g.Buffer === "undefined") {
g.Buffer = Buffer3;
}
Expand All @@ -1161,9 +1164,36 @@ function setupGlobals() {
installBuiltinUtilFormatWithOptions(builtinUtilModule);
if (typeof g.atob === "undefined" || typeof g.btoa === "undefined") {
const base64 = require_base64_js();
const createInvalidCharacterError = (message = "Invalid character") => {
const error = new g.DOMException(message, "InvalidCharacterError");
if (error.code === 0) error.code = 5;
return error;
};
if (typeof g.atob === "undefined") {
g.atob = (value) => {
const bytes = base64.toByteArray(String(value));
// WHATWG forgiving-base64 decode accepts ASCII whitespace and
// unpadded input, but rejects the base64url alphabet. base64-js
// implements RFC 4648 section 4 instead, so normalize the input and
// reject URL-safe characters before handing it over.
const input = String(value).replace(/[\t\n\f\r ]+/g, "");
const hasPadding = input.includes("=");
if (/[^A-Za-z0-9+/=]/.test(input) || /={3,}/.test(input) || /=[^=]/.test(input)) {
throw createInvalidCharacterError();
}
const remainder = input.length % 4;
if (remainder === 1) {
throw createInvalidCharacterError("The string to be decoded is not correctly encoded.");
}
if (hasPadding && remainder !== 0) {
throw createInvalidCharacterError();
}
const normalizedInput = !hasPadding && remainder === 2 ? `${input}==` : !hasPadding && remainder === 3 ? `${input}=` : input;
let bytes = new Uint8Array(0);
try {
bytes = base64.toByteArray(normalizedInput);
} catch {
throw createInvalidCharacterError();
}
let decoded = "";
for (const byte of bytes) {
decoded += String.fromCharCode(byte);
Expand All @@ -1178,7 +1208,7 @@ function setupGlobals() {
for (let index = 0; index < input.length; index += 1) {
const code = input.charCodeAt(index);
if (code > 255) {
throw new TypeError("Invalid character");
throw createInvalidCharacterError();
}
bytes[index] = code;
}
Expand All @@ -1195,9 +1225,6 @@ function setupGlobals() {
if (typeof g.CryptoKey === "undefined") {
g.CryptoKey = SandboxCryptoKey;
}
if (typeof g.DOMException === "undefined") {
g.DOMException = SandboxDOMException;
}
if (typeof g.crypto === "undefined") {
g.crypto = builtinCryptoModule;
} else {
Expand Down