|
1 | | -type Uint8ArrayWithBase64 = typeof Uint8Array & { |
2 | | - fromBase64?: (str: string) => Uint8Array; |
3 | | -}; |
4 | | - |
5 | | -type Uint8ArrayInstanceWithBase64 = Uint8Array & { |
6 | | - toBase64?: () => string; |
7 | | -}; |
8 | | - |
9 | | -type BufferLike = { |
10 | | - from( |
11 | | - input: string, |
12 | | - encoding: 'base64' | 'utf-8', |
13 | | - ): { toString(encoding: 'utf-8' | 'base64'): string }; |
14 | | -}; |
15 | | - |
16 | | -const NodeBuffer = (globalThis as any).Buffer as BufferLike | undefined; |
17 | | -const uint8ArrayPrototype = |
18 | | - Uint8Array.prototype as Uint8ArrayInstanceWithBase64; |
19 | | - |
20 | | -const textDecoder = new TextDecoder('utf-8'); |
21 | | -const textEncoder = new TextEncoder(); |
22 | | - |
23 | | -/** |
24 | | - * Decode base64 string. |
25 | | - * @private |
26 | | - */ |
27 | | -export const decodeBase64: (str: string) => string = (() => { |
28 | | - // 1) Node.js (fast path) |
29 | | - if (typeof NodeBuffer?.from === 'function') { |
30 | | - return (str: string) => NodeBuffer.from(str, 'base64').toString('utf-8'); |
31 | | - } |
32 | | - |
33 | | - // 2) Modern Web / some runtimes |
34 | | - if (typeof (Uint8Array as Uint8ArrayWithBase64).fromBase64 === 'function') { |
35 | | - return (str: string) => |
36 | | - textDecoder.decode((Uint8Array as Uint8ArrayWithBase64).fromBase64!(str)); |
37 | | - } |
38 | | - |
39 | | - // 3) Browser fallback |
40 | | - return (str: string) => { |
41 | | - const binary = atob(str); |
42 | | - return textDecoder.decode( |
43 | | - Uint8Array.from(binary, (char) => char.charCodeAt(0)), |
44 | | - ); |
45 | | - }; |
46 | | -})(); |
47 | | - |
48 | | -/** |
49 | | - * Encode string to base64. |
50 | | - * @private |
51 | | - */ |
52 | | -export const encodeBase64: (str: string) => string = (() => { |
53 | | - // 1) Node.js (fast path) |
54 | | - if (typeof NodeBuffer?.from === 'function') { |
55 | | - return (str: string) => NodeBuffer.from(str, 'utf-8').toString('base64'); |
| 1 | +export const base64 = (() => { |
| 2 | + if (typeof Buffer !== 'undefined') { |
| 3 | + return { |
| 4 | + encode: (str: string) => Buffer.from(str, 'utf-8').toString('base64'), |
| 5 | + decode: (str: string) => Buffer.from(str, 'base64').toString('utf-8'), |
| 6 | + }; |
56 | 7 | } |
57 | 8 |
|
58 | | - // 2) Modern Web / some runtimes |
59 | | - if (typeof uint8ArrayPrototype.toBase64 === 'function') { |
60 | | - return (str: string) => |
61 | | - (textEncoder.encode(str) as Uint8ArrayInstanceWithBase64).toBase64!(); |
62 | | - } |
63 | | - |
64 | | - // 3) Browser fallback |
65 | | - return (str: string) => { |
66 | | - const bytes = textEncoder.encode(str); |
67 | | - let binary = ''; |
68 | | - |
69 | | - for (let i = 0; i < bytes.length; i++) { |
70 | | - binary += String.fromCharCode(bytes[i]); |
71 | | - } |
72 | | - |
73 | | - return btoa(binary); |
| 9 | + const textEncoder = new TextEncoder(); |
| 10 | + const textDecoder = new TextDecoder(); |
| 11 | + |
| 12 | + return { |
| 13 | + encode: (str: string) => { |
| 14 | + const bytes = textEncoder.encode(str); |
| 15 | + return bytes.toBase64(); |
| 16 | + }, |
| 17 | + decode: (str: string) => { |
| 18 | + const bytes = Uint8Array.fromBase64(str); |
| 19 | + return textDecoder.decode(bytes); |
| 20 | + }, |
74 | 21 | }; |
75 | 22 | })(); |
0 commit comments