All files / utilities compression.ts

100% Statements 19/19
100% Branches 0/0
100% Functions 5/5
100% Lines 17/17

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72                5x 5x           36x               24x   24x   24x 24x     24x               12x 12x   12x 505x     12x                 24x   24x                   12x   12x    
/**
 * Gzip + base64 codec for storing text content in VS Code settings values.
 *
 * Uses the web-standard CompressionStream/DecompressionStream and btoa/atob
 * globals, which are available in both the desktop (Node >= 18) and browser
 * extension hosts — no Node-specific imports.
 */
 
const encoder = new TextEncoder();
const decoder = new TextDecoder();
 
/**
 * Reads a stream to completion and returns its bytes.
 */
async function streamToBytes(stream: ReadableStream<Uint8Array>): Promise<Uint8Array> {
	return new Uint8Array(await new Response(stream).arrayBuffer());
}
 
/**
 * Encodes bytes as base64. btoa takes a binary string, so the input is
 * chunked to avoid exceeding the argument-spread call stack limit.
 */
function bytesToBase64(bytes: Uint8Array): string {
	const chunkSize = 0x8000;
 
	let binary = '';
 
	for (let i = 0; i < bytes.length; i += chunkSize) {
		binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize));
	}
 
	return btoa(binary);
}
 
/**
 * Decodes a base64 string into bytes.
 * @throws When the input is not valid base64.
 */
function base64ToBytes(base64: string): Uint8Array<ArrayBuffer> {
	const binary = atob(base64);
	const bytes = new Uint8Array(binary.length);
 
	for (let i = 0; i < binary.length; i++) {
		bytes[i] = binary.charCodeAt(i);
	}
 
	return bytes;
}
 
/**
 * Compresses a text with gzip and encodes the result as base64.
 * @param text The text to compress.
 * @returns The gzip-compressed content as a base64 string.
 */
export async function compressToBase64(text: string): Promise<string> {
	const stream = new Blob([encoder.encode(text)]).stream().pipeThrough(new CompressionStream('gzip'));
 
	return bytesToBase64(await streamToBytes(stream));
}
 
/**
 * Decodes a base64 string and decompresses its gzip content back into text.
 * @param base64 A base64 string holding gzip-compressed content.
 * @returns The decompressed text.
 * @throws When the input is not valid base64 or not valid gzip data.
 */
export async function decompressFromBase64(base64: string): Promise<string> {
	const stream = new Blob([base64ToBytes(base64)]).stream().pipeThrough(new DecompressionStream('gzip'));
 
	return decoder.decode(await streamToBytes(stream));
}