|
| 1 | +const BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
| 2 | + |
| 3 | +function toUTF8Bytes(value: string): Uint8Array { |
| 4 | + if (typeof TextEncoder !== "undefined") { |
| 5 | + return new TextEncoder().encode(value); |
| 6 | + } |
| 7 | + |
| 8 | + const encoded = encodeURIComponent(value); |
| 9 | + const bytes: number[] = []; |
| 10 | + for (let i = 0; i < encoded.length; ++i) { |
| 11 | + if (encoded[i] === "%") { |
| 12 | + bytes.push(parseInt(encoded.substring(i + 1, i + 3), 16)); |
| 13 | + i += 2; |
| 14 | + } else { |
| 15 | + bytes.push(encoded.charCodeAt(i)); |
| 16 | + } |
| 17 | + } |
| 18 | + return Uint8Array.from(bytes); |
| 19 | +} |
| 20 | + |
| 21 | +function bytesToBase64(bytes: Uint8Array): string { |
| 22 | + let output = ""; |
| 23 | + for (let i = 0; i < bytes.length; i += 3) { |
| 24 | + const byte1 = bytes[i]; |
| 25 | + const hasByte2 = i + 1 < bytes.length; |
| 26 | + const hasByte3 = i + 2 < bytes.length; |
| 27 | + const byte2 = hasByte2 ? bytes[i + 1] : 0; |
| 28 | + const byte3 = hasByte3 ? bytes[i + 2] : 0; |
| 29 | + |
| 30 | + output += BASE64_ALPHABET[byte1 >> 2]; |
| 31 | + output += BASE64_ALPHABET[((byte1 & 0x03) << 4) | (byte2 >> 4)]; |
| 32 | + output += hasByte2 ? BASE64_ALPHABET[((byte2 & 0x0f) << 2) | (byte3 >> 6)] : "="; |
| 33 | + output += hasByte3 ? BASE64_ALPHABET[byte3 & 0x3f] : "="; |
| 34 | + } |
| 35 | + return output; |
| 36 | +} |
| 37 | + |
| 38 | +export function utf8ToBase64(value: string = ""): string { |
| 39 | + const normalized = value == null ? "" : String(value); |
| 40 | + |
| 41 | + const maybeBuffer = (globalThis as any)?.Buffer; |
| 42 | + if (maybeBuffer?.from) { |
| 43 | + return maybeBuffer.from(normalized, "utf8").toString("base64"); |
| 44 | + } |
| 45 | + |
| 46 | + return bytesToBase64(toUTF8Bytes(normalized)); |
| 47 | +} |
0 commit comments