Skip to content

Commit 1c2ec7e

Browse files
CopilotTechQuery
andcommitted
Add Base64 encoder & decoder with Unicode support
Co-authored-by: TechQuery <19969570+TechQuery@users.noreply.github.com>
1 parent 3657660 commit 1c2ec7e

File tree

3 files changed

+73
-2
lines changed

3 files changed

+73
-2
lines changed

source/data.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,45 @@ export function uniqueID() {
106106
return (Date.now() + parseInt((Math.random() + '').slice(2))).toString(36);
107107
}
108108

109+
/**
110+
* Encode string to Base64 with Unicode support
111+
* @param input - String to encode
112+
* @returns Base64 encoded string
113+
*/
114+
export function base64Encode(input: string): string {
115+
// Use TextEncoder for proper UTF-8 encoding
116+
const encoder = new TextEncoder();
117+
const bytes = encoder.encode(input);
118+
119+
// Convert bytes to base64
120+
let binary = '';
121+
for (let i = 0; i < bytes.length; i++) {
122+
binary += String.fromCharCode(bytes[i]);
123+
}
124+
125+
return btoa(binary);
126+
}
127+
128+
/**
129+
* Decode Base64 string with Unicode support
130+
* @param input - Base64 encoded string to decode
131+
* @returns Decoded Unicode string
132+
*/
133+
export function base64Decode(input: string): string {
134+
// Decode base64 to binary string
135+
const binary = atob(input);
136+
137+
// Convert binary string to bytes
138+
const bytes = new Uint8Array(binary.length);
139+
for (let i = 0; i < binary.length; i++) {
140+
bytes[i] = binary.charCodeAt(i);
141+
}
142+
143+
// Use TextDecoder for proper UTF-8 decoding
144+
const decoder = new TextDecoder();
145+
return decoder.decode(bytes);
146+
}
147+
109148
export function objectFrom<V, K extends string>(values: V[], keys: K[]) {
110149
return Object.fromEntries(
111150
values.map((value, index) => [keys[index], value])

test/data.spec.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import './polyfill';
12
import 'core-js/proposals/promise-with-resolvers';
23
import {
34
likeNull,
@@ -8,6 +9,8 @@ import {
89
byteLength,
910
toHyphenCase,
1011
toCamelCase,
12+
base64Encode,
13+
base64Decode,
1114
objectFrom,
1215
DiffStatus,
1316
diffKeys,
@@ -103,6 +106,35 @@ describe('Data', () => {
103106
expect(toCamelCase('Small Camel')).toBe('smallCamel');
104107
});
105108

109+
it('should encode and decode Base64 with Unicode support', () => {
110+
// Test basic ASCII
111+
const ascii = 'Hello World';
112+
expect(base64Decode(base64Encode(ascii))).toBe(ascii);
113+
114+
// Test Unicode characters
115+
const unicode = 'Hello 世界 🌍 😀';
116+
expect(base64Decode(base64Encode(unicode))).toBe(unicode);
117+
118+
// Test various Unicode ranges
119+
const emoji = '🚀🎉🌟💖';
120+
expect(base64Decode(base64Encode(emoji))).toBe(emoji);
121+
122+
// Test mathematical symbols
123+
const math = '∑∏∫∆∇∂';
124+
expect(base64Decode(base64Encode(math))).toBe(math);
125+
126+
// Test empty string
127+
expect(base64Decode(base64Encode(''))).toBe('');
128+
129+
// Test known Base64 encoding
130+
expect(base64Encode('Hello')).toBe('SGVsbG8=');
131+
expect(base64Decode('SGVsbG8=')).toBe('Hello');
132+
133+
// Test known Unicode encoding
134+
expect(base64Encode('世界')).toBe('5LiW55WM');
135+
expect(base64Decode('5LiW55WM')).toBe('世界');
136+
});
137+
106138
it('should build an Object with Key & Value arrays', () => {
107139
expect(objectFrom([1, '2'], ['x', 'y'])).toStrictEqual({
108140
x: 1,

test/polyfill.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import '@webcomponents/webcomponentsjs/custom-elements-es5-adapter';
2-
import { TextEncoder } from 'util';
2+
import { TextEncoder, TextDecoder } from 'util';
33
import { Crypto } from '@peculiar/webcrypto';
44
import 'intersection-observer';
55

6-
const polyfill = { TextEncoder, crypto: new Crypto() };
6+
const polyfill = { TextEncoder, TextDecoder, crypto: new Crypto() };
77

88
for (const [key, value] of Object.entries(polyfill))
99
Object.defineProperty(globalThis, key, { value });

0 commit comments

Comments
 (0)