// Based on https://github.com/waitingsong/base64/blob/master/src/lib/from_buffer.ts // prevent `Unsupported environment: `window.btoa` or `Buffer` should be supported.` const lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('') export function toBase64(input: string | ArrayBufferLike | ArrayBufferView | Uint8Array): string { if (typeof input === 'string') { return fromUint8Array(new TextEncoder().encode(input)) } if (input instanceof Uint8Array) { return fromUint8Array(input) } return fromUint8Array(new Uint8Array(ArrayBuffer.isView(input) ? input.buffer : input)) } export function toBase64url( input: string | ArrayBufferLike | ArrayBufferView | Uint8Array, ): string { return (typeof input === 'string' ? input : toBase64(input)) .replace(/=/g, '') .replace(/\+/g, '-') .replace(/\//g, '_') } function fromUint8Array(input: Uint8Array): string { const extraBytes = input.byteLength % 3 // if we have 1 byte left, pad 2 bytes const length = input.byteLength - extraBytes let result = '' // go through the array every three bytes, we'll deal with trailing stuff later for (let index = 0; index < length; index += 3) { result += tripletToBase64((input[index] << 16) | ((input[index + 1] << 8) | input[index + 2])) } // pad the end with zeros, but make sure to not forget the extra bytes if (extraBytes === 1) { const tmp = input[length] result += lookup[tmp >> 2] + lookup[(tmp << 4) & 0x3f] + '==' } else if (extraBytes === 2) { const tmp = (input[length] << 8) | input[length + 1] result += lookup[tmp >> 10] + lookup[(tmp >> 4) & 0x3f] + lookup[(tmp << 2) & 0x3f] + '=' } return result } function tripletToBase64(pos: number): string { return ( lookup[(pos >> 18) & 0x3f] + lookup[(pos >> 12) & 0x3f] + lookup[(pos >> 6) & 0x3f] + lookup[pos & 0x3f] ) }