wip library to store cold objects in s3, warm objects on disk, and hot objects in memory
nodejs
typescript
1import { gzip, gunzip, createGzip, createGunzip } from 'node:zlib';
2import { promisify } from 'node:util';
3import type { Transform } from 'node:stream';
4
5const gzipAsync = promisify(gzip);
6const gunzipAsync = promisify(gunzip);
7
8/**
9 * Compress data using gzip.
10 *
11 * @param data - Data to compress
12 * @returns Compressed data as Uint8Array
13 *
14 * @remarks
15 * Uses Node.js zlib with default compression level (6).
16 * Compression is transparent to the user - data is automatically decompressed on retrieval.
17 *
18 * @example
19 * ```typescript
20 * const original = new TextEncoder().encode('Hello, world!');
21 * const compressed = await compress(original);
22 * console.log(`Compressed from ${original.length} to ${compressed.length} bytes`);
23 * ```
24 */
25export async function compress(data: Uint8Array): Promise<Uint8Array> {
26 const buffer = Buffer.from(data);
27 const compressed = await gzipAsync(buffer);
28 return new Uint8Array(compressed);
29}
30
31/**
32 * Decompress gzip-compressed data.
33 *
34 * @param data - Compressed data
35 * @returns Decompressed data as Uint8Array
36 * @throws Error if data is not valid gzip format
37 *
38 * @remarks
39 * Automatically validates gzip magic bytes (0x1f 0x8b) before decompression.
40 *
41 * @example
42 * ```typescript
43 * const decompressed = await decompress(compressedData);
44 * const text = new TextDecoder().decode(decompressed);
45 * ```
46 */
47export async function decompress(data: Uint8Array): Promise<Uint8Array> {
48 // Validate gzip magic bytes
49 if (data.length < 2 || data[0] !== 0x1f || data[1] !== 0x8b) {
50 throw new Error('Invalid gzip data: missing magic bytes');
51 }
52
53 const buffer = Buffer.from(data);
54 const decompressed = await gunzipAsync(buffer);
55 return new Uint8Array(decompressed);
56}
57
58/**
59 * Check if data appears to be gzip-compressed by inspecting magic bytes.
60 *
61 * @param data - Data to check
62 * @returns true if data starts with gzip magic bytes (0x1f 0x8b)
63 *
64 * @remarks
65 * This is a quick check that doesn't decompress the data.
66 * Useful for detecting already-compressed data to avoid double compression.
67 *
68 * @example
69 * ```typescript
70 * if (isGzipped(data)) {
71 * console.log('Already compressed, skipping compression');
72 * } else {
73 * data = await compress(data);
74 * }
75 * ```
76 */
77export function isGzipped(data: Uint8Array): boolean {
78 return data.length >= 2 && data[0] === 0x1f && data[1] === 0x8b;
79}
80
81/**
82 * Create a gzip compression transform stream.
83 *
84 * @returns A transform stream that compresses data passing through it
85 *
86 * @remarks
87 * Use this for streaming compression of large files.
88 * Pipe data through this stream to compress it on-the-fly.
89 *
90 * @example
91 * ```typescript
92 * const compressStream = createCompressStream();
93 * sourceStream.pipe(compressStream).pipe(destinationStream);
94 * ```
95 */
96export function createCompressStream(): Transform {
97 return createGzip();
98}
99
100/**
101 * Create a gzip decompression transform stream.
102 *
103 * @returns A transform stream that decompresses data passing through it
104 *
105 * @remarks
106 * Use this for streaming decompression of large files.
107 * Pipe compressed data through this stream to decompress it on-the-fly.
108 *
109 * @example
110 * ```typescript
111 * const decompressStream = createDecompressStream();
112 * compressedStream.pipe(decompressStream).pipe(destinationStream);
113 * ```
114 */
115export function createDecompressStream(): Transform {
116 return createGunzip();
117}