simple-zstd 1.4.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/.github/workflows/ci.yml +46 -0
  2. package/.github/workflows/release.yml +45 -0
  3. package/.prettierignore +5 -0
  4. package/.prettierrc +8 -0
  5. package/.release-it.json +28 -0
  6. package/CHANGELOG.md +26 -0
  7. package/README.md +690 -47
  8. package/dist/src/buffer-writable.d.ts +12 -0
  9. package/dist/src/buffer-writable.js +41 -0
  10. package/dist/src/index.d.ts +49 -0
  11. package/dist/src/index.js +430 -0
  12. package/dist/src/peek-transform.d.ts +16 -0
  13. package/dist/src/peek-transform.js +145 -0
  14. package/dist/src/process-duplex.d.ts +11 -0
  15. package/dist/src/process-duplex.js +157 -0
  16. package/dist/src/process-queue.d.ts +8 -0
  17. package/dist/src/process-queue.js +94 -0
  18. package/dist/src/types.d.ts +34 -0
  19. package/dist/src/types.js +3 -0
  20. package/eslint.config.js +49 -0
  21. package/package.json +32 -16
  22. package/src/buffer-writable.ts +30 -0
  23. package/src/index.ts +472 -0
  24. package/src/is-zst.d.ts +5 -0
  25. package/src/peek-transform.ts +153 -0
  26. package/src/process-duplex.ts +164 -0
  27. package/src/process-queue.ts +97 -0
  28. package/src/types.ts +35 -0
  29. package/tsconfig.json +110 -0
  30. package/.eslintrc.js +0 -18
  31. package/.nyc_output/4b36a1ef-a01d-4de7-a4be-e966f315cbd7.json +0 -1
  32. package/.nyc_output/5d73987b-f188-488b-8441-66c67bb19076.json +0 -1
  33. package/.nyc_output/processinfo/4b36a1ef-a01d-4de7-a4be-e966f315cbd7.json +0 -1
  34. package/.nyc_output/processinfo/5d73987b-f188-488b-8441-66c67bb19076.json +0 -1
  35. package/.nyc_output/processinfo/index.json +0 -1
  36. package/.travis.yml +0 -9
  37. package/coverage/base.css +0 -224
  38. package/coverage/block-navigation.js +0 -87
  39. package/coverage/buffer-writable.js.html +0 -154
  40. package/coverage/favicon.png +0 -0
  41. package/coverage/index.html +0 -146
  42. package/coverage/index.js.html +0 -841
  43. package/coverage/oven.js.html +0 -235
  44. package/coverage/prettify.css +0 -1
  45. package/coverage/prettify.js +0 -2
  46. package/coverage/sort-arrow-sprite.png +0 -0
  47. package/coverage/sorter.js +0 -196
  48. package/index.js +0 -68
package/README.md CHANGED
@@ -1,87 +1,730 @@
1
1
  # simple-zstd
2
2
 
3
3
  [![Build Status](https://travis-ci.org/Stieneee/simple-zstd.svg?branch=master)](https://travis-ci.org/Stieneee/simple-zstd)
4
- [![Package Size Size](https://badgen.net/badge/packagephobia/install/simple-zstd)](https://packagephobia.now.sh/result?p=simple-zstd)
5
4
  [![License](https://badgen.net/badge/license/MIT/blue)](https://choosealicense.com/licenses/mit/)
6
5
 
7
- Node.js interface to system installed zstandard (zstd).
6
+ Node.js interface to system-installed Zstandard (zstd) with TypeScript support.
8
7
 
9
- ## Why Another ZSTD Package
8
+ ## Overview
10
9
 
11
- Other packages were either using out-of-date ZSTD versions or depended on native C bindings that required a compilation step during installation.
12
- A package was needed that would cleanly work with [pkg](https://www.npmjs.com/package/pkg).
10
+ simple-zstd is a lightweight wrapper around the system-installed zstd binary, inspired by simple-git's approach of wrapping system binaries rather than building against native libraries. This provides a more stable and portable solution at the cost of requiring zstd to be installed on the system.
13
11
 
14
- ## Dependencies
12
+ ### Features
15
13
 
16
- ZSTD
14
+ - **TypeScript Support**: Full TypeScript definitions and modern ES modules
15
+ - **Multiple Interfaces**: Static functions, buffer methods, and class-based API with process pooling
16
+ - **Promise-Based**: All operations return promises for modern async/await patterns
17
+ - **Stream & Buffer**: Support for both streaming and buffer-based compression/decompression
18
+ - **Smart Decompression**: Automatic detection and passthrough of non-compressed data
19
+ - **Dictionary Support**: Use compression dictionaries via Buffer or file path
20
+ - **Process Pooling**: Pre-spawn child processes for latency-sensitive applications
21
+ - **Node.js 18+**: Built on modern Node.js features
17
22
 
18
- Example:
23
+ ## Requirements
19
24
 
20
- `sudo apt install zstd`
25
+ - **Node.js**: >= 18.0.0
26
+ - **zstd**: Must be installed and available on system PATH
21
27
 
22
28
  ## Installation
23
29
 
24
- `npm i simple-zstd`
30
+ ### Install zstd
31
+
32
+ **Ubuntu/Debian:**
33
+
34
+ ```bash
35
+ sudo apt install zstd
36
+ ```
37
+
38
+ **macOS:**
39
+
40
+ ```bash
41
+ brew install zstd
42
+ ```
43
+
44
+ **Windows:**
45
+
46
+ ```bash
47
+ choco install zstd
48
+ # or download from: https://github.com/facebook/zstd/releases
49
+ ```
50
+
51
+ ### Install simple-zstd
52
+
53
+ ```bash
54
+ npm install simple-zstd
55
+ ```
56
+
57
+ ## API Reference
58
+
59
+ ### Types
60
+
61
+ ```typescript
62
+ import { Duplex } from "node:stream";
63
+ import { SpawnOptions } from "node:child_process";
64
+ import { DuplexOptions } from "node:stream";
65
+
66
+ interface ZSTDOpts {
67
+ dictionary?: Buffer | { path: string }; // Compression dictionary
68
+ zstdOptions?: string[]; // CLI args to pass to zstd (e.g., ['--ultra'])
69
+ spawnOptions?: SpawnOptions; // Node.js child_process spawn options
70
+ streamOptions?: DuplexOptions; // Node.js stream options
71
+ }
72
+
73
+ interface PoolOpts {
74
+ compressQueueSize?: number; // Number of pre-spawned compression processes
75
+ decompressQueueSize?: number; // Number of pre-spawned decompression processes
76
+ compressQueue?: {
77
+ compLevel?: number;
78
+ dictionary?: Buffer | { path: string };
79
+ zstdOptions?: string[];
80
+ spawnOptions?: SpawnOptions;
81
+ streamOptions?: DuplexOptions;
82
+ };
83
+ decompressQueue?: {
84
+ dictionary?: Buffer | { path: string };
85
+ zstdOptions?: string[];
86
+ spawnOptions?: SpawnOptions;
87
+ streamOptions?: DuplexOptions;
88
+ };
89
+ }
90
+ ```
91
+
92
+ ### Static Functions
93
+
94
+ ```typescript
95
+ // Compression
96
+ compress(compLevel: number, opts?: ZSTDOpts): Promise<Duplex>
97
+ compressBuffer(buffer: Buffer, compLevel: number, opts?: ZSTDOpts): Promise<Buffer>
98
+
99
+ // Decompression (with automatic passthrough for non-compressed data)
100
+ decompress(opts?: ZSTDOpts): Promise<Duplex>
101
+ decompressBuffer(buffer: Buffer, opts?: ZSTDOpts): Promise<Buffer>
102
+ ```
103
+
104
+ ### Class: SimpleZSTD
105
+
106
+ The `SimpleZSTD` class provides process pooling for better performance when performing many compression/decompression operations.
107
+
108
+ ```typescript
109
+ class SimpleZSTD {
110
+ // Static factory method (recommended)
111
+ static create(poolOptions?: PoolOpts): Promise<SimpleZSTD>;
112
+
113
+ // Instance methods
114
+ compress(compLevel?: number): Promise<Duplex>;
115
+ compressBuffer(buffer: Buffer, compLevel?: number): Promise<Buffer>;
116
+ decompress(): Promise<Duplex>;
117
+ decompressBuffer(buffer: Buffer): Promise<Buffer>;
118
+ destroy(): void;
119
+
120
+ // Statistics
121
+ get queueStats(): {
122
+ compress: { hits: number; misses: number };
123
+ decompress: { hits: number; misses: number };
124
+ };
125
+ }
126
+ ```
127
+
128
+ **Note:** Use the static `create()` method instead of the constructor. The constructor is private to ensure proper async initialization.
25
129
 
26
130
  ## Usage
27
131
 
28
- simple-zstd exposes a stream interfaces for compression and decompression.
29
- The underlying child process is destroyed with the stream.
132
+ ### Example 1: Stream Interface (TypeScript)
133
+
134
+ ```typescript
135
+ import fs from "node:fs";
136
+ import { pipeline } from "node:stream/promises";
137
+ import { compress, decompress } from "simple-zstd";
138
+
139
+ async function copyFile() {
140
+ const c = await compress(3); // Compression level 3
141
+ const d = await decompress();
142
+
143
+ await pipeline(
144
+ fs.createReadStream("example.txt"),
145
+ c,
146
+ d,
147
+ fs.createWriteStream("example_copy.txt")
148
+ );
149
+
150
+ console.log("File compressed and decompressed!");
151
+ }
152
+
153
+ copyFile().catch(console.error);
154
+ ```
155
+
156
+ ### Example 2: Buffer Interface (TypeScript)
157
+
158
+ ```typescript
159
+ import { compressBuffer, decompressBuffer } from "simple-zstd";
160
+
161
+ async function processBuffer() {
162
+ const buffer = Buffer.from("this is a test");
163
+
164
+ // Compress with level 3
165
+ const compressed = await compressBuffer(buffer, 3);
166
+ console.log(
167
+ `Original: ${buffer.length} bytes, Compressed: ${compressed.length} bytes`
168
+ );
169
+
170
+ // Decompress
171
+ const decompressed = await decompressBuffer(compressed);
172
+ console.log(decompressed.toString()); // "this is a test"
173
+ }
174
+
175
+ processBuffer().catch(console.error);
176
+ ```
177
+
178
+ ### Example 3: CommonJS Usage
179
+
180
+ ```javascript
181
+ const fs = require("fs");
182
+ const { pipeline } = require("node:stream/promises");
183
+ const { compress, decompress } = require("simple-zstd");
184
+
185
+ async function copyFile() {
186
+ const c = await compress(3);
187
+ const d = await decompress();
188
+
189
+ await pipeline(
190
+ fs.createReadStream("example.txt"),
191
+ c,
192
+ d,
193
+ fs.createWriteStream("example_copy.txt")
194
+ );
195
+
196
+ console.log("File compressed and decompressed!");
197
+ }
198
+
199
+ copyFile().catch(console.error);
200
+ ```
201
+
202
+ ### Example 4: Class Interface with Process Pooling
203
+
204
+ The `SimpleZSTD` class pre-spawns zstd processes for lower latency. This is ideal for high-throughput scenarios.
205
+
206
+ ```typescript
207
+ import fs from "node:fs";
208
+ import { pipeline } from "node:stream/promises";
209
+ import { SimpleZSTD } from "simple-zstd";
210
+
211
+ async function processMultipleFiles() {
212
+ // Create instance with process pools using static factory method
213
+ const zstd = await SimpleZSTD.create({
214
+ compressQueueSize: 2, // Pre-spawn 2 compression processes
215
+ decompressQueueSize: 2, // Pre-spawn 2 decompression processes
216
+ compressQueue: {
217
+ compLevel: 3, // Default compression level for pool
218
+ },
219
+ });
220
+
221
+ try {
222
+ // Process first file with pool default (level 3)
223
+ const c1 = await zstd.compress();
224
+ const d1 = await zstd.decompress();
225
+
226
+ await pipeline(
227
+ fs.createReadStream("file1.txt"),
228
+ c1,
229
+ d1,
230
+ fs.createWriteStream("file1_copy.txt")
231
+ );
232
+
233
+ console.log("File 1 processed!");
234
+
235
+ // Process second file with custom compression level (bypasses pool)
236
+ const c2 = await zstd.compress(19); // Override with level 19
237
+ const d2 = await zstd.decompress();
238
+
239
+ await pipeline(
240
+ fs.createReadStream("file2.txt"),
241
+ c2,
242
+ d2,
243
+ fs.createWriteStream("file2_copy.txt")
244
+ );
245
+
246
+ console.log("File 2 processed!");
247
+
248
+ // Check pool statistics
249
+ console.log("Pool stats:", zstd.queueStats);
250
+ // Example output: { compress: { hits: 1, misses: 1 }, decompress: { hits: 2, misses: 0 } }
251
+ // Note: compress shows 1 miss because we used custom level for file2
252
+ } finally {
253
+ // Clean up all child processes
254
+ zstd.destroy();
255
+ }
256
+ }
257
+
258
+ processMultipleFiles().catch(console.error);
259
+ ```
260
+
261
+ ### Example 5: Using Compression Dictionaries
262
+
263
+ ```typescript
264
+ import fs from "node:fs";
265
+ import { compressBuffer, decompressBuffer, SimpleZSTD } from "simple-zstd";
266
+
267
+ // Static functions with dictionaries
268
+ async function useDictionaryStatic() {
269
+ const dictionary = fs.readFileSync("my-dictionary.zstd");
270
+ const data = Buffer.from("Sample text to compress");
271
+
272
+ // Compress with dictionary
273
+ const compressed = await compressBuffer(data, 3, { dictionary });
274
+
275
+ // Decompress with same dictionary
276
+ const decompressed = await decompressBuffer(compressed, { dictionary });
277
+
278
+ console.log(decompressed.toString()); // "Sample text to compress"
279
+ }
280
+
281
+ // Class with dictionaries (supports Buffer or file path)
282
+ async function useDictionaryClass() {
283
+ const dictionary = fs.readFileSync("my-dictionary.zstd");
284
+
285
+ const zstd = await SimpleZSTD.create({
286
+ compressQueue: {
287
+ compLevel: 3,
288
+ dictionary, // Can be Buffer or { path: '/path/to/dict' }
289
+ },
290
+ decompressQueue: {
291
+ dictionary, // Same dictionary for decompression
292
+ },
293
+ });
294
+
295
+ try {
296
+ const data = Buffer.from("Sample text to compress");
297
+ const compressed = await zstd.compressBuffer(data);
298
+ const decompressed = await zstd.decompressBuffer(compressed);
299
+ console.log(decompressed.toString()); // "Sample text to compress"
300
+ } finally {
301
+ zstd.destroy();
302
+ }
303
+ }
304
+
305
+ useDictionaryStatic().catch(console.error);
306
+ ```
307
+
308
+ **Dictionary Caching:** When using dictionary Buffers with static functions, simple-zstd automatically caches the temporary dictionary files using SHA-256 hashing. This means:
309
+ - ✅ Multiple calls with the **same dictionary Buffer** reuse the same temp file
310
+ - ✅ No performance penalty for repeated operations with dictionaries
311
+ - ✅ Automatic cleanup when the dictionary is no longer in use
312
+ - ✅ **Fixes exponential slowdown** when compressing thousands of items with dictionaries
313
+
314
+ ```typescript
315
+ const dict = fs.readFileSync('my-dict.zstd');
316
+
317
+ // These 1000 operations will only create ONE temp file total
318
+ for (let i = 0; i < 1000; i++) {
319
+ await compressBuffer(data[i], 3, { dictionary: dict });
320
+ }
321
+ // Temp file is automatically cleaned up when no longer referenced
322
+ ```
323
+
324
+ ### Example 6: Smart Decompression (Auto-detect)
325
+
326
+ The decompression functions automatically detect if data is zstd-compressed and pass through uncompressed data unchanged.
327
+
328
+ ```typescript
329
+ import { decompressBuffer } from "simple-zstd";
330
+
331
+ async function smartDecompress() {
332
+ const plainText = Buffer.from("not compressed");
333
+ const result = await decompressBuffer(plainText);
334
+
335
+ // Non-compressed data passes through unchanged
336
+ console.log(result.toString()); // "not compressed"
337
+ }
338
+
339
+ smartDecompress().catch(console.error);
340
+ ```
341
+
342
+ ## Advanced Options
343
+
344
+ ### Custom zstd Options
345
+
346
+ Pass any command-line option to the zstd process via `zstdOptions`:
347
+
348
+ ```typescript
349
+ import { compress } from "simple-zstd";
350
+
351
+ // Use ultra compression (level 22)
352
+ const stream = await compress(22, {
353
+ zstdOptions: ["--ultra"],
354
+ });
355
+
356
+ // Multiple options
357
+ const stream2 = await compress(19, {
358
+ zstdOptions: ["--ultra", "--long"],
359
+ });
360
+ ```
361
+
362
+ ### Spawn Options
363
+
364
+ Control the child process spawn behavior:
365
+
366
+ ```typescript
367
+ import { compress } from "simple-zstd";
368
+
369
+ const stream = await compress(3, {
370
+ spawnOptions: {
371
+ cwd: "/custom/working/directory",
372
+ env: { ...process.env, CUSTOM_VAR: "value" },
373
+ },
374
+ });
375
+ ```
376
+
377
+ ### Stream Options
378
+
379
+ Customize the Duplex stream behavior:
380
+
381
+ ```typescript
382
+ import { compress } from "simple-zstd";
383
+
384
+ const stream = await compress(3, {
385
+ streamOptions: {
386
+ highWaterMark: 64 * 1024, // 64KB buffer
387
+ },
388
+ });
389
+ ```
390
+
391
+ ### Stream Events
392
+
393
+ All compression and decompression streams emit the following events:
394
+
395
+ ```typescript
396
+ import { compress } from "simple-zstd";
397
+
398
+ const stream = await compress(3);
399
+
400
+ // Standard Duplex stream events
401
+ stream.on("data", (chunk: Buffer) => {
402
+ console.log("Received chunk:", chunk.length, "bytes");
403
+ });
404
+
405
+ stream.on("end", () => {
406
+ console.log("Stream finished");
407
+ });
408
+
409
+ stream.on("error", (err: Error) => {
410
+ console.error("Stream error:", err);
411
+ });
412
+
413
+ // zstd-specific events
414
+ stream.on("stderr", (message: string) => {
415
+ // zstd process stderr output
416
+ console.warn("zstd stderr:", message);
417
+ });
418
+
419
+ stream.on("exit", (code: number, signal: NodeJS.Signals | null) => {
420
+ // zstd process exit event
421
+ console.log("zstd process exited with code:", code);
422
+ });
423
+ ```
424
+
425
+ **Event Reference:**
426
+
427
+ - `data` - Emitted when compressed/decompressed data is available
428
+ - `end` - Emitted when the stream has finished processing
429
+ - `error` - Emitted on stream errors or if zstd exits with non-zero code
430
+ - `stderr` - Emitted when the zstd process writes to stderr (warnings, debug info)
431
+ - `exit` - Emitted when the underlying zstd process exits
432
+
433
+ ## Debugging
434
+
435
+ Enable debug output using the `DEBUG` environment variable:
436
+
437
+ ```bash
438
+ # Debug simple-zstd operations
439
+ DEBUG=SimpleZSTD node app.js
440
+
441
+ # Debug process queue
442
+ DEBUG=SimpleZSTDQueue node app.js
443
+
444
+ # Debug both
445
+ DEBUG=SimpleZSTD,SimpleZSTDQueue node app.js
446
+ ```
447
+
448
+ ## Migrating to v2
30
449
 
31
- stream = ZSTDCompress(lvl)
32
- lvl - ZSTD compression level
450
+ Version 2.0 is a complete rewrite with TypeScript support and a modernized API. Here's what you need to know:
33
451
 
34
- stream = ZSTDDecompress()
452
+ ### Breaking Changes
35
453
 
36
- stream = ZSTDDecompressMaybe()
454
+ #### 1. Node.js Version Requirement
37
455
 
38
- ### Example
456
+ **v1:** No explicit requirement
457
+ **v2:** Requires Node.js >= 18.0.0
458
+
459
+ ```bash
460
+ # Check your Node version
461
+ node --version # Should be v18.0.0 or higher
462
+ ```
463
+
464
+ #### 2. Function Names Changed
465
+
466
+ | v1 Function | v2 Function |
467
+ | ----------------------- | -------------------------------------------- |
468
+ | `ZSTDCompress(level)` | `compress(level, opts?)` |
469
+ | `ZSTDDecompress()` | `decompress(opts?)` |
470
+ | `ZSTDDecompressMaybe()` | `decompress(opts?)` _(built-in auto-detect)_ |
471
+
472
+ **v1 Code:**
39
473
 
40
474
  ```javascript
41
- const fs = require('fs');
42
- const {ZSTDCompress, ZSTDDecompress} = require('simple-zstd');
475
+ const { ZSTDCompress, ZSTDDecompress } = require("simple-zstd");
476
+
477
+ const compressStream = ZSTDCompress(3);
478
+ const decompressStream = ZSTDDecompress();
479
+ ```
480
+
481
+ **v2 Code:**
482
+
483
+ ```javascript
484
+ const { compress, decompress } = require("simple-zstd");
485
+
486
+ const compressStream = await compress(3);
487
+ const decompressStream = await decompress();
488
+ ```
489
+
490
+ #### 3. All Functions Now Return Promises
43
491
 
44
- // ZSTDCompress(compressionLevel, streamOptions)
45
- // ZSTDDecompress(streamOptions)
492
+ **v1:** Functions returned streams synchronously
493
+ **v2:** Functions return `Promise<Duplex>` and must be awaited
46
494
 
47
- fs.createReadStream('example.txt')
495
+ **v1 Code:**
496
+
497
+ ```javascript
498
+ fs.createReadStream("file.txt")
48
499
  .pipe(ZSTDCompress(3))
49
500
  .pipe(ZSTDDecompress())
50
- .pipe(fs.createWriteStream('example_copy.txt'))
51
- .on('error', (err) => {
52
- //..
53
- })
54
- .on('finish', () => {
55
- console.log('Copy Complete!');
56
- })
501
+ .pipe(fs.createWriteStream("output.txt"));
502
+ ```
57
503
 
58
- // -> Copy Complete
504
+ **v2 Code:**
505
+
506
+ ```javascript
507
+ const c = await compress(3);
508
+ const d = await decompress();
509
+
510
+ await pipeline(
511
+ fs.createReadStream("file.txt"),
512
+ c,
513
+ d,
514
+ fs.createWriteStream("output.txt")
515
+ );
59
516
  ```
60
517
 
61
- ### Decompress Maybe
518
+ #### 4. "Maybe" Functionality Now Built-in
519
+
520
+ **v1:** Had separate `ZSTDDecompressMaybe()` function
521
+ **v2:** All decompression functions auto-detect and pass through non-compressed data
62
522
 
63
- A maybe variant of decompress will pass-through a non-zst stream while decompressing a zst stream.
523
+ **v1 Code:**
64
524
 
65
525
  ```javascript
66
- const fs = require('fs');
67
- const {ZSTDDecompressMaybe} = require('simple-zstd');
526
+ const { ZSTDDecompressMaybe } = require("simple-zstd");
527
+
528
+ stream.pipe(ZSTDDecompressMaybe()).pipe(output);
529
+ ```
530
+
531
+ **v2 Code:**
532
+
533
+ ```javascript
534
+ const { decompress } = require("simple-zstd");
535
+
536
+ const d = await decompress(); // Automatically detects compressed data
537
+ pipeline(stream, d, output);
538
+ ```
539
+
540
+ #### 5. Options Structure Changed
541
+
542
+ **v1:** Limited options as separate parameters
543
+ **v2:** Unified options object with TypeScript types
544
+
545
+ **v1 Code:**
546
+
547
+ ```javascript
548
+ // v1 had limited customization
549
+ ZSTDCompress(3, streamOptions);
550
+ ```
551
+
552
+ **v2 Code:**
553
+
554
+ ```javascript
555
+ await compress(3, {
556
+ dictionary: Buffer.from("..."),
557
+ zstdOptions: ["--ultra"],
558
+ spawnOptions: { cwd: "/tmp" },
559
+ streamOptions: { highWaterMark: 64 * 1024 },
560
+ });
561
+ ```
562
+
563
+ ### New Features in v2
564
+
565
+ #### TypeScript Support
566
+
567
+ ```typescript
568
+ import { compress, decompress, SimpleZSTD } from "simple-zstd";
569
+ import type { ZSTDOpts, PoolOpts } from "simple-zstd";
570
+ ```
571
+
572
+ #### Buffer Interface
573
+
574
+ New convenience methods for working with buffers directly:
575
+
576
+ ```typescript
577
+ import { compressBuffer, decompressBuffer } from "simple-zstd";
578
+
579
+ const compressed = await compressBuffer(Buffer.from("data"), 3);
580
+ const decompressed = await decompressBuffer(compressed);
581
+ ```
582
+
583
+ #### Process Pooling with SimpleZSTD Class
584
+
585
+ Pre-spawn processes for better performance with async factory method:
586
+
587
+ ```typescript
588
+ import { SimpleZSTD } from "simple-zstd";
589
+
590
+ const zstd = await SimpleZSTD.create({
591
+ compressQueueSize: 2,
592
+ decompressQueueSize: 2,
593
+ compressQueue: {
594
+ compLevel: 3,
595
+ dictionary: Buffer.from("..."), // Optional
596
+ },
597
+ });
68
598
 
69
- // ZSTDDecompressMaybe(spawnOptions, streamOptions, zstdOptions)
599
+ // Use pooled processes
600
+ const stream = await zstd.compress();
70
601
 
71
- fs.createReadStream('example.txt')
72
- // .pipe(ZSTDCompress(3))
73
- .pipe(ZSTDDecompressMaybe())
74
- .pipe(fs.createWriteStream('example_copy.txt'))
75
- .on('error', (err) => {
76
- //..
77
- })
78
- .on('finish', () => {
79
- console.log('Copy Complete!');
80
- })
602
+ // Or override compression level for specific operations
603
+ const stream2 = await zstd.compress(19);
81
604
 
82
- // -> Copy Complete
605
+ // Clean up when done
606
+ zstd.destroy();
83
607
  ```
84
608
 
609
+ #### Dictionary Support
610
+
611
+ Full support for compression dictionaries:
612
+
613
+ ```typescript
614
+ const dictionary = fs.readFileSync("dict.zstd");
615
+
616
+ await compress(3, { dictionary });
617
+ await compressBuffer(data, 3, { dictionary });
618
+ ```
619
+
620
+ ### Migration Examples
621
+
622
+ #### Simple Stream Compression
623
+
624
+ **v1:**
625
+
626
+ ```javascript
627
+ const { ZSTDCompress, ZSTDDecompress } = require("simple-zstd");
628
+
629
+ fs.createReadStream("input.txt")
630
+ .pipe(ZSTDCompress(3))
631
+ .pipe(fs.createWriteStream("output.zst"));
632
+ ```
633
+
634
+ **v2:**
635
+
636
+ ```javascript
637
+ const { compress } = require("simple-zstd");
638
+ const { pipeline } = require("node:stream/promises");
639
+
640
+ const c = await compress(3);
641
+ await pipeline(
642
+ fs.createReadStream("input.txt"),
643
+ c,
644
+ fs.createWriteStream("output.zst")
645
+ );
646
+ ```
647
+
648
+ #### Error Handling
649
+
650
+ **v1:**
651
+
652
+ ```javascript
653
+ ZSTDCompress(3).on("error", (err) => console.error(err));
654
+ ```
655
+
656
+ **v2:**
657
+
658
+ ```javascript
659
+ try {
660
+ const c = await compress(3);
661
+ c.on("error", (err) => console.error(err));
662
+ } catch (err) {
663
+ console.error("Failed to create stream:", err);
664
+ }
665
+ ```
666
+
667
+ #### With Custom Options
668
+
669
+ **v1:**
670
+
671
+ ```javascript
672
+ // Limited options in v1
673
+ const stream = ZSTDCompress(3, { highWaterMark: 64 * 1024 });
674
+ ```
675
+
676
+ **v2:**
677
+
678
+ ```javascript
679
+ const stream = await compress(3, {
680
+ streamOptions: { highWaterMark: 64 * 1024 },
681
+ zstdOptions: ["--ultra"],
682
+ });
683
+ ```
684
+
685
+ ### Upgrade Checklist
686
+
687
+ - [ ] Update Node.js to >= 18.0.0
688
+ - [ ] Replace `ZSTDCompress` with `compress`
689
+ - [ ] Replace `ZSTDDecompress` with `decompress`
690
+ - [ ] Replace `ZSTDDecompressMaybe` with `decompress` (same function)
691
+ - [ ] Add `await` to all compression/decompression calls
692
+ - [ ] Update imports to use new function names
693
+ - [ ] Consider using buffer methods (`compressBuffer`/`decompressBuffer`) for simpler use cases
694
+ - [ ] Consider using `SimpleZSTD` class for high-throughput scenarios
695
+ - [ ] Update error handling for async/await pattern
696
+ - [ ] Update tests to handle promises
697
+
698
+ ## Performance Benchmarks
699
+
700
+ This package has been benchmarked against other zstd packages.
701
+ At this time is appears to be the fastest package for processing large files.
702
+
703
+ [Benchmark Tests](https://github.com/Stieneee/node-compression-test)
704
+
705
+ ## Performance Considerations
706
+
707
+ This package spawns a child process for each compression or decompression operation. While this provides excellent performance for large files, child process creation overhead can become a bottleneck when processing many small files rapidly.
708
+
709
+ **Solution:** Use the `SimpleZSTD` class with process pooling for high-throughput scenarios:
710
+
711
+ ```typescript
712
+ const zstd = await SimpleZSTD.create({
713
+ compressQueueSize: 4, // Pre-spawn 4 compression processes
714
+ decompressQueueSize: 4, // Pre-spawn 4 decompression processes
715
+ });
716
+
717
+ // Reuse pooled processes for multiple operations
718
+ for (const file of files) {
719
+ const stream = await zstd.compress();
720
+ // ... process file ...
721
+ }
722
+
723
+ zstd.destroy(); // Clean up when done
724
+ ```
725
+
726
+ Process pooling significantly reduces latency by reusing existing child processes instead of spawning new ones for each operation.
727
+
85
728
  ## Contributing
86
729
 
87
730
  Pull requests are welcome.
@@ -90,7 +733,7 @@ Pull requests are welcome.
90
733
 
91
734
  MIT License
92
735
 
93
- Copyright (c) 2020 Tyler Stiene
736
+ Copyright (c) 2025 Tyler Stiene
94
737
 
95
738
  Permission is hereby granted, free of charge, to any person obtaining a copy
96
739
  of this software and associated documentation files (the "Software"), to deal