node-liblzma 2.2.0 → 3.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/README.md +379 -778
  2. package/lib/cli/nxz.js +176 -84
  3. package/lib/cli/nxz.js.map +1 -1
  4. package/lib/lzma.browser.d.ts +24 -0
  5. package/lib/lzma.browser.d.ts.map +1 -0
  6. package/lib/lzma.browser.js +30 -0
  7. package/lib/lzma.browser.js.map +1 -0
  8. package/lib/lzma.inline.d.ts +30 -0
  9. package/lib/lzma.inline.d.ts.map +1 -0
  10. package/lib/lzma.inline.js +68 -0
  11. package/lib/lzma.inline.js.map +1 -0
  12. package/lib/wasm/bindings.d.ts +109 -0
  13. package/lib/wasm/bindings.d.ts.map +1 -0
  14. package/lib/wasm/bindings.js +307 -0
  15. package/lib/wasm/bindings.js.map +1 -0
  16. package/lib/wasm/compress.d.ts +32 -0
  17. package/lib/wasm/compress.d.ts.map +1 -0
  18. package/lib/wasm/compress.js +47 -0
  19. package/lib/wasm/compress.js.map +1 -0
  20. package/lib/wasm/decompress.d.ts +32 -0
  21. package/lib/wasm/decompress.d.ts.map +1 -0
  22. package/lib/wasm/decompress.js +45 -0
  23. package/lib/wasm/decompress.js.map +1 -0
  24. package/lib/wasm/index.d.ts +14 -0
  25. package/lib/wasm/index.d.ts.map +1 -0
  26. package/lib/wasm/index.js +18 -0
  27. package/lib/wasm/index.js.map +1 -0
  28. package/lib/wasm/liblzma.inline.d.ts +10 -0
  29. package/lib/wasm/liblzma.inline.d.ts.map +1 -0
  30. package/lib/wasm/liblzma.inline.js +10 -0
  31. package/lib/wasm/liblzma.inline.js.map +1 -0
  32. package/lib/wasm/memory.d.ts +57 -0
  33. package/lib/wasm/memory.d.ts.map +1 -0
  34. package/lib/wasm/memory.js +108 -0
  35. package/lib/wasm/memory.js.map +1 -0
  36. package/lib/wasm/stream.d.ts +35 -0
  37. package/lib/wasm/stream.d.ts.map +1 -0
  38. package/lib/wasm/stream.js +164 -0
  39. package/lib/wasm/stream.js.map +1 -0
  40. package/lib/wasm/types.d.ts +77 -0
  41. package/lib/wasm/types.d.ts.map +1 -0
  42. package/lib/wasm/types.js +55 -0
  43. package/lib/wasm/types.js.map +1 -0
  44. package/lib/wasm/utils.d.ts +62 -0
  45. package/lib/wasm/utils.d.ts.map +1 -0
  46. package/lib/wasm/utils.js +162 -0
  47. package/lib/wasm/utils.js.map +1 -0
  48. package/package.json +24 -3
package/README.md CHANGED
@@ -10,31 +10,43 @@ Node-liblzma
10
10
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org/)
11
11
  [![npm provenance](https://img.shields.io/badge/npm-provenance-green.svg)](https://docs.npmjs.com/generating-provenance-statements)
12
12
 
13
- # What is liblzma/XZ ?
14
-
15
- [XZ](https://tukaani.org/xz/xz-file-format.txt) is a container for compressed archives. It is among the best compressors out there according to several benchmarks:
16
-
17
- * [Gzip vs Bzip2 vs LZMA vs XZ vs LZ4 vs LZO](http://pokecraft.first-world.info/wiki/Quick_Benchmark:_Gzip_vs_Bzip2_vs_LZMA_vs_XZ_vs_LZ4_vs_LZO)
18
- * [Large Text Compression Benchmark](http://mattmahoney.net/dc/text.html#2118)
19
- * [Linux Compression Comparison (GZIP vs BZIP2 vs LZMA vs ZIP vs Compress)](http://bashitout.com/2009/08/30/Linux-Compression-Comparison-GZIP-vs-BZIP2-vs-LZMA-vs-ZIP-vs-Compress.html)
20
-
21
- It has a good balance between compression time/ratio and decompression time/memory.
22
-
23
- # About this project
24
-
25
- This project aims towards providing:
26
-
27
- * A quick and easy way to play with XZ compression:
28
- Quick and easy as it conforms to zlib API, so that switching from __zlib/deflate__ to __xz__ might be as easy as a string search/replace in your code editor :smile:
29
-
30
- * Complete integration with XZ sources/binaries:
31
- You can either use system packages or download a specific version and compile it!
32
- See [installation](#installation) below.
13
+ Native Node.js bindings for liblzma — XZ/LZMA2 compression with **browser support via WebAssembly**.
14
+
15
+ ## Table of Contents
16
+
17
+ - [Quick Start](#quick-start)
18
+ - [What's New](#whats-new)
19
+ - [v3.0.0 Browser & WASM Support](#v300--browser--wasm-support)
20
+ - [v2.0.0 — TypeScript Modernization](#v200--typescript-modernization)
21
+ - [Browser Usage](#browser-usage)
22
+ - [CLI Tool (nxz)](#cli-tool-nxz)
23
+ - [API Reference](#api-reference)
24
+ - [API Comparison with Zlib](#api-comparison-with-zlib)
25
+ - [Options](#options)
26
+ - [Advanced Configuration](#advanced-configuration)
27
+ - [Thread Support](#thread-support)
28
+ - [Progress Monitoring](#progress-monitoring)
29
+ - [Concurrency Control (LZMAPool)](#concurrency-control-with-lzmapool)
30
+ - [File Compression Helpers](#file-compression-helpers)
31
+ - [Error Handling](#error-handling)
32
+ - [Benchmark](#benchmark)
33
+ - [Installation](#installation)
34
+ - [Testing](#testing)
35
+ - [Migration Guide (v1 → v2)](#migration-guide)
36
+ - [Contributing](#contributing)
37
+ - [Troubleshooting](#troubleshooting)
38
+ - [Bugs](#bugs)
39
+ - [Acknowledgements](#acknowledgements)
40
+ - [License](#license)
41
+
42
+ ## What is liblzma/XZ?
43
+
44
+ [XZ](https://tukaani.org/xz/xz-file-format.txt) is a container for compressed archives. It offers one of the best compression ratios available, with a good balance between compression time and decompression speed/memory.
33
45
 
34
46
  > Only LZMA2 is supported for compression output.
35
- But the library can open and read any LZMA1 or LZMA2 compressed file.
47
+ > But the library can open and read any LZMA1 or LZMA2 compressed file.
36
48
 
37
- # Quick Start
49
+ ## Quick Start
38
50
 
39
51
  ```bash
40
52
  npm install node-liblzma
@@ -61,7 +73,8 @@ compressor.on('progress', ({ bytesRead, bytesWritten }) => {
61
73
  });
62
74
  ```
63
75
 
64
- **Promise style (with `.then()`):**
76
+ <details>
77
+ <summary><strong>Promise style (with .then())</strong></summary>
65
78
 
66
79
  ```typescript
67
80
  import { xzAsync, unxzAsync } from 'node-liblzma';
@@ -79,7 +92,10 @@ xzAsync(Buffer.from('Hello, World!'))
79
92
  });
80
93
  ```
81
94
 
82
- **Callback style (Node.js traditional):**
95
+ </details>
96
+
97
+ <details>
98
+ <summary><strong>Callback style (Node.js traditional)</strong></summary>
83
99
 
84
100
  ```typescript
85
101
  import { xz, unxz } from 'node-liblzma';
@@ -93,25 +109,142 @@ xz(Buffer.from('Hello, World!'), (err, compressed) => {
93
109
  });
94
110
  ```
95
111
 
112
+ </details>
113
+
96
114
  📖 **Full API documentation**: [oorabona.github.io/node-liblzma](https://oorabona.github.io/node-liblzma/)
97
115
 
98
- # Command Line Interface (nxz)
116
+ ## What's New
117
+
118
+ ### v3.0.0 — Browser & WASM Support
119
+
120
+ > **[Live Demo](https://oorabona.github.io/node-liblzma/demo/)** — Try XZ compression in your browser.
121
+
122
+ - **Browser/WASM support**: Full XZ compression and decompression via WebAssembly
123
+ - Same API as Node.js (`xzAsync`, `unxzAsync`, `createXz`, `createUnxz`)
124
+ - WASM binary: ~52KB gzipped (under 100KB budget)
125
+ - Web Streams API for streaming compression/decompression
126
+ - Zero-config inline mode: `import from 'node-liblzma/inline'`
127
+ - **CLI tool (nxz)**: Portable xz-like command line tool
128
+ - Full xz compatibility: `-z`, `-d`, `-l`, `-k`, `-f`, `-c`, `-o`, `-v`, `-q`
129
+ - Compression presets 0-9 with extreme mode (`-e`)
130
+ - Progress display, stdin/stdout piping, benchmarking (`-B`)
131
+ - **Progress events**: Monitor compression/decompression in real-time
132
+ - **XZ Utils 5.8.x**: Updated to latest stable version
133
+ - **458+ tests**: Comprehensive test suite with 100% code coverage
134
+
135
+ ### v2.0.0 — TypeScript Modernization
136
+
137
+ - **Full TypeScript migration**: Complete rewrite from CoffeeScript
138
+ - **Promise-based APIs**: `xzAsync()` and `unxzAsync()`
139
+ - **Modern tooling**: Vitest, Biome, pnpm, pre-commit hooks
140
+ - **Node.js >= 16** required (updated from >= 12)
141
+
142
+ <details>
143
+ <summary><strong>Legacy (N-API migration)</strong></summary>
144
+
145
+ In previous versions, [N-API](https://nodejs.org/api/n-api.html) replaced [nan](https://github.com/nodejs/nan) as the stable ABI for native modules.
146
+
147
+ Tested on: Linux x64, macOS (x64/arm64), Raspberry Pi 2/3/4, Windows.
148
+
149
+ **Prebuilt binaries** are bundled for: Windows x64, Linux x64, macOS x64/arm64.
150
+
151
+ | Flag | Description | Default | Values |
152
+ |------|-------------|---------|--------|
153
+ | `USE_GLOBAL` | Use system liblzma library | `yes` (`no` on Windows) | `yes`, `no` |
154
+ | `RUNTIME_LINK` | Static or shared linking | `shared` | `static`, `shared` |
155
+ | `ENABLE_THREAD_SUPPORT` | Enable thread support | `yes` | `yes`, `no` |
156
+
157
+ If no prebuilt binary matches your platform, `node-gyp` will compile from source automatically.
158
+
159
+ </details>
160
+
161
+ ## Browser Usage
162
+
163
+ > **[Live Demo](https://oorabona.github.io/node-liblzma/demo/)** — Try XZ compression in your browser right now.
164
+
165
+ node-liblzma v3.0.0+ supports XZ compression in the browser via WebAssembly. The same API works in both Node.js and browsers — bundlers (Vite, Webpack, esbuild) automatically resolve the WASM-backed implementation.
166
+
167
+ ### Basic Usage
168
+
169
+ ```typescript
170
+ // Bundlers auto-resolve to WASM in browser, native in Node.js
171
+ import { xzAsync, unxzAsync, isXZ } from 'node-liblzma';
172
+
173
+ // Compress
174
+ const compressed = await xzAsync('Hello, browser!');
175
+
176
+ // Decompress
177
+ const original = await unxzAsync(compressed);
178
+
179
+ // Check if data is XZ-compressed
180
+ if (isXZ(someBuffer)) {
181
+ const data = await unxzAsync(someBuffer);
182
+ }
183
+ ```
184
+
185
+ ### Streaming with Web Streams API
186
+
187
+ ```typescript
188
+ import { createXz, createUnxz } from 'node-liblzma';
189
+
190
+ // Compress a fetch response
191
+ const response = await fetch('/large-file.bin');
192
+ const compressed = response.body.pipeThrough(createXz({ preset: 6 }));
193
+
194
+ // Decompress
195
+ const decompressed = compressedStream.pipeThrough(createUnxz());
196
+ ```
197
+
198
+ ### Import Modes
199
+
200
+ | Import | When to use |
201
+ |--------|-------------|
202
+ | `node-liblzma` | Standard — bundler resolves to WASM (browser) or native (Node.js) |
203
+ | `node-liblzma/wasm` | Explicit WASM usage in Node.js (no native addon needed) |
204
+ | `node-liblzma/inline` | Zero-config — WASM embedded as base64 (no external file to serve) |
205
+
206
+ ```typescript
207
+ // Explicit WASM (works in Node.js too, no native build required)
208
+ import { xzAsync } from 'node-liblzma/wasm';
209
+
210
+ // Inline mode (larger bundle, but no WASM file to configure)
211
+ import { ensureInlineInit, xzAsync } from 'node-liblzma/inline';
212
+ await ensureInlineInit(); // Decodes embedded base64 WASM
213
+ const compressed = await xzAsync(data);
214
+ ```
215
+
216
+ ### Browser Limitations
217
+
218
+ - **No sync APIs**: `xzSync()` / `unxzSync()` throw `LZMAError` in browsers
219
+ - **Presets 0-6 only**: Presets 7-9 require more memory than WASM's 256MB limit
220
+ - **No filesystem**: `xzFile()` / `unxzFile()` are not available
221
+ - **No Node Streams**: Use `createXz()` / `createUnxz()` (Web TransformStream) instead of `Xz` / `Unxz` classes
222
+
223
+ ### Bundle Size
224
+
225
+ | Component | Raw | Gzipped |
226
+ |-----------|-----|---------|
227
+ | liblzma.wasm | ~107KB | ~52KB |
228
+ | Glue code (liblzma.js) | ~6KB | ~2KB |
229
+ | **Total** | **~113KB** | **~54KB** |
230
+
231
+ For detailed browser setup instructions, see [docs/BROWSER.md](docs/BROWSER.md).
232
+
233
+ ## CLI Tool (nxz)
99
234
 
100
235
  This package includes `nxz`, a portable xz-like CLI tool that works on any platform with Node.js.
101
236
 
102
- ## Installation
237
+ ### Installation
103
238
 
104
239
  ```bash
105
240
  # Global installation (recommended for CLI usage)
106
241
  npm install -g node-liblzma
107
- # or
108
- pnpm add -g node-liblzma
109
242
 
110
243
  # Then use directly
111
244
  nxz --help
112
245
  ```
113
246
 
114
- ## Quick Examples
247
+ ### Quick Examples
115
248
 
116
249
  ```bash
117
250
  # Compress a file (creates file.txt.xz, deletes original)
@@ -123,41 +256,27 @@ nxz file.txt.xz
123
256
  # Keep original file (-k)
124
257
  nxz -k file.txt
125
258
 
126
- # Decompress explicitly (-d)
127
- nxz -d archive.xz
128
-
129
259
  # Maximum compression (-9) with extreme mode (-e)
130
260
  nxz -9e large-file.bin
131
261
 
132
262
  # Compress to stdout (-c) for piping
133
263
  nxz -c file.txt > file.txt.xz
134
264
 
135
- # Decompress to stdout
136
- nxz -dc file.txt.xz | grep "pattern"
137
-
138
- # Custom output file (-o)
139
- nxz -d archive.xz -o /tmp/output.bin
140
-
141
- # List archive info (-l)
265
+ # List archive info (-l / -lv for verbose)
142
266
  nxz -l file.txt.xz
143
267
 
144
- # Verbose info (-lv)
145
- nxz -lv file.txt.xz
146
-
147
- # Compress from stdin
148
- cat file.txt | nxz -c > file.txt.xz
149
-
150
- # Quiet mode - suppress warnings (-q)
151
- nxz -q file.txt
268
+ # Benchmark native vs WASM performance (-B)
269
+ nxz -B file.txt
152
270
  ```
153
271
 
154
- ## All Options
272
+ ### All Options
155
273
 
156
274
  | Option | Long | Description |
157
275
  |--------|------|-------------|
158
276
  | `-z` | `--compress` | Force compression mode |
159
277
  | `-d` | `--decompress` | Force decompression mode |
160
278
  | `-l` | `--list` | List archive information |
279
+ | `-B` | `--benchmark` | Benchmark native vs WASM performance |
161
280
  | `-k` | `--keep` | Keep original file (don't delete) |
162
281
  | `-f` | `--force` | Overwrite existing output file |
163
282
  | `-c` | `--stdout` | Write to stdout, keep original file |
@@ -169,18 +288,20 @@ nxz -q file.txt
169
288
  | `-h` | `--help` | Show help |
170
289
  | `-V` | `--version` | Show version |
171
290
 
172
- ## One-shot Usage (without global install)
291
+ <details>
292
+ <summary><strong>One-shot usage (without global install)</strong></summary>
173
293
 
174
294
  ```bash
175
295
  # npm/npx
176
296
  npx --package node-liblzma nxz --help
177
- npx -p node-liblzma nxz file.txt
178
297
 
179
298
  # pnpm
180
299
  pnpm dlx --package node-liblzma nxz --help
181
300
  ```
182
301
 
183
- ## Exit Codes
302
+ </details>
303
+
304
+ ### Exit Codes
184
305
 
185
306
  | Code | Meaning |
186
307
  |------|---------|
@@ -188,92 +309,11 @@ pnpm dlx --package node-liblzma nxz --help
188
309
  | 1 | Error (file not found, format error, etc.) |
189
310
  | 130 | Interrupted (SIGINT/Ctrl+C) |
190
311
 
191
- # What's new ?
192
-
193
- ## Latest Updates (2026)
194
-
195
- * **CLI Tool (nxz)**: Portable xz-like command line tool included in the package
196
- - Full xz compatibility: `-z`, `-d`, `-l`, `-k`, `-f`, `-c`, `-o`, `-v`, `-q`
197
- - Compression presets 0-9 with extreme mode (`-e`)
198
- - Progress display for large files, stdin/stdout piping
199
- - Works on any platform with Node.js
200
- - See [Command Line Interface](#command-line-interface-nxz) section
201
- * **Progress Events**: Monitor compression/decompression progress with real-time events
202
- ```typescript
203
- const compressor = createXz();
204
- compressor.on('progress', ({ bytesRead, bytesWritten }) => {
205
- console.log(`Read: ${bytesRead}, Written: ${bytesWritten}`);
206
- });
207
- ```
208
- * **API Documentation**: Full TypeDoc documentation with Material theme at [oorabona.github.io/node-liblzma](https://oorabona.github.io/node-liblzma/)
209
- * **XZ Utils 5.8.2**: Updated to latest stable version
210
-
211
- ## Version 2.0 (2025) - Complete Modernization
212
-
213
- This major release brings the library into 2025 with modern tooling and TypeScript support:
214
-
215
- * **Full TypeScript migration**: Complete rewrite from CoffeeScript to TypeScript for better type safety and developer experience
216
- * **Promise-based APIs**: New async functions `xzAsync()` and `unxzAsync()` with Promise support
217
- * **Modern testing**: Migrated from Mocha to Vitest with improved performance and better TypeScript integration
218
- * **Enhanced tooling**:
219
- - [Biome](https://biomejs.dev/) for fast linting and formatting
220
- - Pre-commit hooks with nano-staged and simple-git-hooks
221
- - pnpm as package manager for better dependency management
222
- * **Updated Node.js support**: Requires Node.js >= 16 (updated from >= 12)
223
-
224
- ## Legacy (N-API migration)
225
-
226
- In previous versions, [N-API](https://nodejs.org/api/n-api.html) became the _de facto_ standard to provide stable ABI API for NodeJS Native Modules, replacing [nan](https://github.com/nodejs/nan).
227
-
228
- It has been tested and works on:
229
-
230
- * Linux x64 (Ubuntu)
231
- * OSX (`macos-11`)
232
- * Raspberry Pi 2/3/4 (both on 32-bit and 64-bit architectures)
233
- * Windows (`windows-2019` and `windows-2022` are part of GitHub CI)
234
-
235
- > Notes:
236
- >
237
- > * For [Windows](https://github.com/oorabona/node-liblzma/actions/workflows/ci-windows.yml)
238
- > There is no "global" installation of the LZMA library on the Windows machine provisionned by GitHub, so it is pointless to build with this config
239
- >
240
- * For [Linux](https://github.com/oorabona/node-liblzma/actions/workflows/ci-linux.yml)
312
+ ## API Reference
241
313
 
242
- * For [MacOS](https://github.com/oorabona/node-liblzma/actions/workflows/ci-macos.yml)
314
+ ### API Comparison with Zlib
243
315
 
244
- ## Prebuilt images
245
-
246
- Several prebuilt versions are bundled within the package.
247
-
248
- * Windows x86_64
249
- * Linux x86_64
250
- * MacOS x86_64 / Arm64
251
-
252
- If your OS/architecture matches, you will use this version which has been compiled using the following default flags:
253
-
254
- | Flag | Description | Default | Values |
255
- |------|-------------|---------|--------|
256
- | `USE_GLOBAL` | Use system liblzma library | `yes` (`no` on Windows) | `yes`, `no` |
257
- | `RUNTIME_LINK` | Static or shared linking | `shared` | `static`, `shared` |
258
- | `ENABLE_THREAD_SUPPORT` | Enable thread support | `yes` | `yes`, `no` |
259
-
260
- If not `node-gyp` will automagically start compiling stuff according to the environment variables set, or the default values above.
261
-
262
- If you want to change compilation flags, please read on [here](#installation).
263
-
264
- # Related projects
265
-
266
- Thanks to the community, there are several choices out there:
267
-
268
- * [lzma-purejs](https://github.com/cscott/lzma-purejs)
269
- A pure JavaScript implementation of the algorithm
270
- * [node-xz](https://github.com/robey/node-xz)
271
- Node binding of XZ library
272
- * [lzma-native](https://github.com/addaleax/lzma-native)
273
- A very complete implementation of XZ library bindings
274
- * Others are also available but they fork "xz" process in the background.
275
-
276
- # API comparison
316
+ The API mirrors Node.js Zlib for easy adoption:
277
317
 
278
318
  ```js
279
319
  // CommonJS
@@ -296,9 +336,7 @@ import * as lzma from 'node-liblzma';
296
336
  | - | `xzFile` | `(input, output, [options])` → `Promise<void>` |
297
337
  | - | `unxzFile` | `(input, output, [options])` → `Promise<void>` |
298
338
 
299
- ## Options
300
-
301
- The `options` object accepts the following attributes:
339
+ ### Options
302
340
 
303
341
  | Attribute | Type | Description | Values |
304
342
  |-----------|------|-------------|--------|
@@ -309,15 +347,13 @@ The `options` object accepts the following attributes:
309
347
  | `filters` | array | Filter chain | `filter.LZMA2`, `filter.X86`, `filter.ARM`, etc. |
310
348
  | `chunkSize` | number | Processing chunk size | Default: 64KB |
311
349
 
312
- For further information about each of these flags, see the [XZ SDK documentation](http://7-zip.org/sdk.html).
350
+ For further information, see the [XZ SDK documentation](http://7-zip.org/sdk.html).
313
351
 
314
352
  ## Advanced Configuration
315
353
 
316
354
  ### Thread Support
317
355
 
318
- The library supports multi-threaded compression when built with `ENABLE_THREAD_SUPPORT=yes` (default). Thread support allows parallel compression on multi-core systems, significantly improving performance for large files.
319
-
320
- **Thread values:**
356
+ Multi-threaded compression is available when built with `ENABLE_THREAD_SUPPORT=yes` (default).
321
357
 
322
358
  | Value | Behavior |
323
359
  |-------|----------|
@@ -325,26 +361,15 @@ The library supports multi-threaded compression when built with `ENABLE_THREAD_S
325
361
  | `1` | Single-threaded (default) |
326
362
  | `N` | Use exactly N threads |
327
363
 
328
- **Example:**
329
-
330
364
  ```typescript
331
365
  import { createXz, hasThreads } from 'node-liblzma';
332
366
 
333
- // Check if threading is available
334
367
  if (hasThreads()) {
335
- // Auto-detect: use all CPU cores
336
- const compressor = createXz({ threads: 0 });
337
-
338
- // Or specify exact thread count
339
- const compressor4 = createXz({ threads: 4 });
368
+ const compressor = createXz({ threads: 0 }); // auto-detect
340
369
  }
341
370
  ```
342
371
 
343
- **Important notes:**
344
- - Thread support only applies to **compression**, not decompression
345
- - Requires LZMA library built with pthread support (`ENABLE_THREAD_SUPPORT=yes`)
346
- - Default is `threads: 1` (single-threaded) for predictable behavior
347
- - Check availability: `hasThreads()` returns `true` if multi-threading is supported
372
+ > **Note**: Threads only apply to compression, not decompression.
348
373
 
349
374
  ### Progress Monitoring
350
375
 
@@ -357,65 +382,52 @@ const compressor = createXz({ preset: 6 });
357
382
 
358
383
  compressor.on('progress', ({ bytesRead, bytesWritten }) => {
359
384
  const ratio = bytesWritten / bytesRead;
360
- console.log(`Progress: ${bytesRead} bytes in, ${bytesWritten} bytes out (ratio: ${ratio.toFixed(2)})`);
361
- });
362
-
363
- // Works with both compression and decompression
364
- const decompressor = createUnxz();
365
- decompressor.on('progress', ({ bytesRead, bytesWritten }) => {
366
- console.log(`Decompressing: ${bytesRead} → ${bytesWritten} bytes`);
385
+ console.log(`Progress: ${bytesRead} in, ${bytesWritten} out (ratio: ${ratio.toFixed(2)})`);
367
386
  });
368
387
 
369
388
  inputStream.pipe(compressor).pipe(outputStream);
370
389
  ```
371
390
 
372
- **Notes:**
373
- - Progress events fire after each chunk is processed
374
- - `bytesRead`: Total input bytes processed so far
375
- - `bytesWritten`: Total output bytes produced so far
376
- - Works with streams, not buffer APIs (`xz`/`unxz`)
391
+ Progress events fire after each chunk is processed. Works with streams, not buffer APIs.
377
392
 
378
- ### Buffer Size Optimization
393
+ ### Concurrency Control with LZMAPool
379
394
 
380
- For optimal performance, the library uses configurable chunk sizes:
395
+ For production environments with high concurrency needs:
381
396
 
382
397
  ```typescript
383
- const stream = createXz({
384
- preset: lzma.preset.DEFAULT,
385
- chunkSize: 256 * 1024 // 256KB chunks (default: 64KB)
386
- });
387
- ```
398
+ import { LZMAPool } from 'node-liblzma';
388
399
 
389
- **Recommendations:**
390
- - **Small files (< 1MB)**: Use default 64KB chunks
391
- - **Medium files (1-10MB)**: Use 128-256KB chunks
392
- - **Large files (> 10MB)**: Use 512KB-1MB chunks
393
- - **Maximum buffer size**: 512MB per operation (security limit)
400
+ const pool = new LZMAPool(10); // Max 10 concurrent operations
394
401
 
395
- ### Memory Usage Limits
402
+ pool.on('metrics', (metrics) => {
403
+ console.log(`Active: ${metrics.active}, Queued: ${metrics.queued}`);
404
+ });
396
405
 
397
- The library enforces a 512MB maximum buffer size to prevent DoS attacks via resource exhaustion. For files larger than 512MB, use streaming APIs:
406
+ const compressed = await pool.compress(buffer);
407
+ const decompressed = await pool.decompress(compressed);
408
+ ```
409
+
410
+ ### File Compression Helpers
398
411
 
399
412
  ```typescript
400
- import { createReadStream, createWriteStream } from 'fs';
401
- import { createXz } from 'node-liblzma';
413
+ import { xzFile, unxzFile } from 'node-liblzma';
402
414
 
403
- createReadStream('large-file.bin')
404
- .pipe(createXz())
405
- .pipe(createWriteStream('large-file.xz'));
415
+ await xzFile('input.txt', 'output.txt.xz');
416
+ await unxzFile('output.txt.xz', 'restored.txt');
417
+
418
+ // With options
419
+ await xzFile('large-file.bin', 'compressed.xz', { preset: 9, threads: 4 });
406
420
  ```
407
421
 
422
+ Handles files > 512MB automatically via streams with lower memory footprint.
423
+
408
424
  ### Error Handling
409
425
 
410
- The library provides typed error classes for better error handling:
426
+ Typed error classes for precise error handling:
411
427
 
412
428
  ```typescript
413
429
  import {
414
- xzAsync,
415
- LZMAError,
416
- LZMAMemoryError,
417
- LZMADataError,
418
- LZMAFormatError
430
+ xzAsync, LZMAError, LZMAMemoryError, LZMADataError, LZMAFormatError
419
431
  } from 'node-liblzma';
420
432
 
421
433
  try {
@@ -427,339 +439,106 @@ try {
427
439
  console.error('Corrupt data:', error.message);
428
440
  } else if (error instanceof LZMAFormatError) {
429
441
  console.error('Invalid format:', error.message);
430
- } else {
431
- console.error('Unknown error:', error);
432
442
  }
433
443
  }
434
444
  ```
435
445
 
436
- **Available error classes:**
437
- - `LZMAError` - Base error class
438
- - `LZMAMemoryError` - Memory allocation failed
439
- - `LZMAMemoryLimitError` - Memory limit exceeded
440
- - `LZMAFormatError` - Unrecognized file format
441
- - `LZMAOptionsError` - Invalid compression options
442
- - `LZMADataError` - Corrupt compressed data
443
- - `LZMABufferError` - Buffer size issues
444
- - `LZMAProgrammingError` - Internal errors
445
-
446
- ### Error Recovery
447
-
448
- Streams automatically handle recoverable errors and provide state transition hooks:
449
-
450
- ```typescript
451
- const decompressor = createUnxz();
452
-
453
- decompressor.on('error', (error) => {
454
- console.error('Decompression error:', error.errno, error.message);
455
- // Stream will emit 'close' event after error
456
- });
457
-
458
- decompressor.on('close', () => {
459
- console.log('Stream closed, safe to cleanup');
460
- });
461
- ```
462
-
463
- ### Concurrency Control with LZMAPool
446
+ **Available error classes**: `LZMAError` (base), `LZMAMemoryError`, `LZMAMemoryLimitError`, `LZMAFormatError`, `LZMAOptionsError`, `LZMADataError`, `LZMABufferError`, `LZMAProgrammingError`.
464
447
 
465
- For production environments with high concurrency needs, use `LZMAPool` to limit simultaneous operations:
466
-
467
- ```typescript
468
- import { LZMAPool } from 'node-liblzma';
469
-
470
- const pool = new LZMAPool(10); // Max 10 concurrent operations
471
-
472
- // Monitor pool metrics
473
- pool.on('metrics', (metrics) => {
474
- console.log(`Active: ${metrics.active}, Queued: ${metrics.queued}`);
475
- console.log(`Completed: ${metrics.completed}, Failed: ${metrics.failed}`);
476
- });
477
-
478
- // Compress with automatic queuing
479
- const compressed = await pool.compress(buffer);
480
- const decompressed = await pool.decompress(compressed);
481
-
482
- // Get current metrics
483
- const status = pool.getMetrics();
484
- ```
485
-
486
- **Pool Events:**
487
- - `queue` - Task added to queue
488
- - `start` - Task started processing
489
- - `complete` - Task completed successfully
490
- - `error-task` - Task failed
491
- - `metrics` - Metrics updated (after each state change)
492
-
493
- **Benefits:**
494
- - ✅ Automatic backpressure
495
- - ✅ Prevents resource exhaustion
496
- - ✅ Production-ready monitoring
497
- - ✅ Zero breaking changes (opt-in)
498
-
499
- ### File Compression Helpers
500
-
501
- Simplified API for file-based compression:
448
+ ### Buffer Size Optimization
502
449
 
503
450
  ```typescript
504
- import { xzFile, unxzFile } from 'node-liblzma';
505
-
506
- // Compress a file
507
- await xzFile('input.txt', 'output.txt.xz');
508
-
509
- // Decompress a file
510
- await unxzFile('output.txt.xz', 'restored.txt');
511
-
512
- // With options
513
- await xzFile('large-file.bin', 'compressed.xz', {
514
- preset: 9,
515
- threads: 4
451
+ const stream = createXz({
452
+ preset: lzma.preset.DEFAULT,
453
+ chunkSize: 256 * 1024 // 256KB chunks (default: 64KB)
516
454
  });
517
455
  ```
518
456
 
519
- **Advantages over buffer APIs:**
520
- - ✅ Handles files > 512MB automatically
521
- - Built-in backpressure via streams
522
- - Lower memory footprint
523
- - Simpler API for common use cases
524
-
525
- ## Async callback contract (errno-based)
526
-
527
- The low-level native callback used internally by streams follows an errno-style contract to match liblzma behavior and to avoid mixing exception channels:
528
-
529
- - Signature: `(errno: number, availInAfter: number, availOutAfter: number)`
530
- - Success: `errno` is either `LZMA_OK` or `LZMA_STREAM_END`.
531
- - Recoverable/other conditions: any other `errno` value (for example, `LZMA_BUF_ERROR`, `LZMA_DATA_ERROR`, `LZMA_PROG_ERROR`) indicates an error state.
532
- - Streams emit `onerror` with the numeric `errno` when `errno !== LZMA_OK && errno !== LZMA_STREAM_END`.
533
-
534
- Why errno instead of JS exceptions?
535
-
536
- - The binding mirrors liblzma’s status codes and keeps a single error channel that’s easy to reason about in tight processing loops.
537
- - This avoids throwing across async worker boundaries and keeps cleanup deterministic.
538
-
539
- High-level APIs remain ergonomic:
540
-
541
- - Promise-based functions `xzAsync()`/`unxzAsync()` still resolve to `Buffer` or reject with `Error` as expected.
542
- - Stream users can listen to `error` events, where we map `errno` to a human-friendly message (`messages[errno]`).
543
-
544
- If you prefer Node’s error-first callbacks, you can wrap the APIs and translate `errno` to `Error` objects at your boundaries without changing the native layer.
545
-
546
- # Installation
547
-
548
- Well, as simple as this one-liner:
549
-
550
- ```sh
551
- npm i node-liblzma --save
552
- ```
553
-
554
- --OR--
555
-
556
- ```sh
557
- yarn add node-liblzma
558
- ```
559
-
560
- --OR-- (recommended for development)
457
+ | File Size | Recommended chunkSize |
458
+ |-----------|-----------------------|
459
+ | < 1MB | 64KB (default) |
460
+ | 1-10MB | 128-256KB |
461
+ | > 10MB | 512KB-1MB |
561
462
 
562
- ```sh
563
- pnpm add node-liblzma
564
- ```
463
+ Maximum buffer size: 512MB per operation (security limit). For larger files, use streaming APIs.
565
464
 
566
- If you want to recompile the source, for example to disable threading support in the module, then you have to opt out with:
465
+ ### Async Callback Contract (errno-based)
567
466
 
568
- ``` bash
569
- ENABLE_THREAD_SUPPORT=no npm install node-liblzma --build-from-source
570
- ```
467
+ The low-level native callback follows an errno-style contract matching liblzma behavior:
571
468
 
572
- > Note:
573
- Enabling thread support in the library will __NOT__ work if the LZMA library itself has been built without such support.
469
+ - **Signature**: `(errno: number, availInAfter: number, availOutAfter: number)`
470
+ - **Success**: `errno` is `LZMA_OK` or `LZMA_STREAM_END`
471
+ - **Error**: any other `errno` value
574
472
 
575
- To build the module, you have the following options:
473
+ High-level APIs remain ergonomic Promise functions resolve to `Buffer` or reject with `Error`, stream users listen to `error` events.
576
474
 
577
- 1. Using system development libraries
578
- 2. Ask the build system to download `xz` and build it
579
- 3. Compile `xz` yourself, outside `node-liblzma`, and have it use it after
475
+ ## Benchmark
580
476
 
581
- ## Using system dev libraries to compile
477
+ ### Performance Hierarchy
582
478
 
583
- You need to have the development package installed on your system. If you have Debian based distro:
479
+ All three backends use the same liblzma library and produce **identical compression ratios**:
584
480
 
585
481
  ```
586
- # apt-get install liblzma-dev
482
+ System xz > nxz native (C++ addon) > nxz WASM (Emscripten)
483
+ fastest ~1-2x slower ~2-5x slower (decompress)
484
+ ~1x (compress, large files)
587
485
  ```
588
486
 
589
- ## Automatic download and compilation to statically link `xz`
590
-
591
- If you do not plan on having a local install, you can ask for automatic download and build of whatever version of `xz` you want.
487
+ ### Full Comparison (246 KB source code, preset 6)
592
488
 
593
- Just do:
489
+ | Backend | Compress | Decompress | Size | Environment |
490
+ |---------|----------|------------|------|-------------|
491
+ | **System `xz` 5.8** | 81 ms | 4 ms | 76.7 KB | C binary |
492
+ | **nxz native** | 90 ms | 3.4 ms | 76.7 KB | Node.js + C++ addon |
493
+ | **nxz WASM** | 86 ms | 7.9 ms | 76.7 KB | Node.js + Emscripten |
594
494
 
595
- ```sh
596
- npm install node-liblzma --build-from-source
597
- ```
598
-
599
- When no option is given in the commandline arguments, it will build with default values.
495
+ ### Native vs WASM (nxz -B, preset 6)
600
496
 
601
- ## Local install of `xz` sources (outside `node-liblzma`)
497
+ | Data | Compress | Decompress | Notes |
498
+ |------|----------|------------|-------|
499
+ | 1 KB text | WASM 2.8x slower | WASM 4.9x slower | Startup overhead dominates |
500
+ | 135 KB binary | ~1:1 | WASM 2x slower | Compression near-parity |
501
+ | 246 KB source | ~1:1 | WASM 2.3x slower | Realistic workload |
502
+ | 1 MB random | ~1:1 | WASM 1.6x slower | Gap narrows with size |
602
503
 
603
- So you did install `xz` somewhere outside the module and want the module to use it.
504
+ <details>
505
+ <summary><strong>Running benchmarks</strong></summary>
604
506
 
605
- For that, you need to set the include directory and library directory search paths as GCC [environment variables](https://gcc.gnu.org/onlinedocs/gcc/Environment-Variables.html).
507
+ ```bash
508
+ # Compare nxz (native) vs system xz across file sizes
509
+ ./scripts/benchmark.sh
510
+ ./scripts/benchmark.sh -s 1,50,200 -p 6,9 # custom sizes/presets
511
+ ./scripts/benchmark.sh -o csv > results.csv # export as CSV/JSON
606
512
 
607
- ```sh
608
- export CPATH=$HOME/path/to/headers
609
- export LIBRARY_PATH=$HOME/path/to/lib
610
- export LD_LIBRARY_PATH=$HOME/path/to/lib:$LD_LIBRARY_PATH
513
+ # Compare native addon vs WASM backend
514
+ nxz --benchmark file.txt
515
+ nxz -B -3 large-file.bin # with preset 3
611
516
  ```
612
517
 
613
- The latest is needed for tests to be run right after.
614
-
615
- Once done, this should suffice:
518
+ </details>
616
519
 
617
- ```sh
618
- npm install
619
- ```
520
+ ### When to Use What
620
521
 
621
- # Testing
522
+ | Scenario | Recommended |
523
+ |----------|-------------|
524
+ | Browser | WASM (only option) |
525
+ | Node.js, performance-critical | Native addon |
526
+ | Node.js, no C++ toolchain available | WASM (`node-liblzma/wasm`) |
527
+ | Cross-platform scripts | nxz CLI |
528
+ | Batch processing many files | System xz |
529
+ | CI/CD with Node.js already installed | nxz CLI |
622
530
 
623
- This project maintains **100% code coverage** across all statements, branches, functions, and lines.
624
-
625
- You can run tests with:
531
+ ## Installation
626
532
 
627
- ```sh
628
- npm test
533
+ ```bash
534
+ npm install node-liblzma
629
535
  # or
630
- pnpm test
631
- ```
632
-
633
- It will build and launch the test suite (325+ tests) with [Vitest](https://vitest.dev/) with TypeScript support and coverage reporting.
634
-
635
- Additional testing commands:
636
-
637
- ```sh
638
- # Watch mode for development
639
- pnpm test:watch
640
-
641
- # Coverage report
642
- pnpm test:coverage
643
-
644
- # Type checking
645
- pnpm type-check
646
- ```
647
-
648
- # Usage
649
-
650
- As the API is very close to NodeJS Zlib, you will probably find a good reference
651
- [there](http://www.nodejs.org/api/zlib.html).
652
-
653
- Otherwise examples can be found as part of the test suite, so feel free to use them!
654
- They are written in TypeScript with full type definitions.
655
-
656
- # Migration Guide
657
-
658
- ## Migrating from v1.x to v2.0
659
-
660
- Version 2.0 introduces several breaking changes along with powerful new features.
661
-
662
- ### Breaking Changes
663
-
664
- 1. **Node.js Version Requirement**
665
- ```diff
666
- - Requires Node.js >= 12
667
- + Requires Node.js >= 16
668
- ```
669
-
670
- 2. **ESM Module Format**
671
- ```diff
672
- - CommonJS: var lzma = require('node-liblzma');
673
- + ESM: import * as lzma from 'node-liblzma';
674
- + CommonJS still works via dynamic import
675
- ```
676
-
677
- 3. **TypeScript Migration**
678
- - Source code migrated from CoffeeScript to TypeScript
679
- - Full type definitions included
680
- - Better IDE autocomplete and type safety
681
-
682
- ### New Features You Should Adopt
683
-
684
- 1. **Promise-based APIs** (Recommended for new code)
685
- ```typescript
686
- // Old callback style (still works)
687
- xz(buffer, (err, compressed) => {
688
- if (err) throw err;
689
- // use compressed
690
- });
691
-
692
- // New Promise style
693
- try {
694
- const compressed = await xzAsync(buffer);
695
- // use compressed
696
- } catch (err) {
697
- // handle error
698
- }
699
- ```
700
-
701
- 2. **Typed Error Classes** (Better error handling)
702
- ```typescript
703
- import { LZMAMemoryError, LZMADataError } from 'node-liblzma';
704
-
705
- try {
706
- await unxzAsync(corruptData);
707
- } catch (error) {
708
- if (error instanceof LZMADataError) {
709
- console.error('Corrupt compressed data');
710
- } else if (error instanceof LZMAMemoryError) {
711
- console.error('Out of memory');
712
- }
713
- }
714
- ```
715
-
716
- 3. **Concurrency Control** (For high-throughput applications)
717
- ```typescript
718
- import { LZMAPool } from 'node-liblzma';
719
-
720
- const pool = new LZMAPool(10); // Max 10 concurrent operations
721
-
722
- // Automatic queuing and backpressure
723
- const results = await Promise.all(
724
- files.map(file => pool.compress(file))
725
- );
726
- ```
727
-
728
- 4. **File Helpers** (Simpler file compression)
729
- ```typescript
730
- import { xzFile, unxzFile } from 'node-liblzma';
731
-
732
- // Compress a file (handles streaming automatically)
733
- await xzFile('input.txt', 'output.txt.xz');
734
-
735
- // Decompress a file
736
- await unxzFile('output.txt.xz', 'restored.txt');
737
- ```
738
-
739
- ### Testing Framework Change
740
-
741
- If you maintain tests for code using node-liblzma:
742
-
743
- ```diff
744
- - Mocha test framework
745
- + Vitest test framework (faster, better TypeScript support)
536
+ pnpm add node-liblzma
746
537
  ```
747
538
 
748
- ### Tooling Updates
539
+ ### System Libraries
749
540
 
750
- Development tooling has been modernized:
751
-
752
- - **Linter**: Biome (replaces ESLint + Prettier)
753
- - **Package Manager**: pnpm recommended (npm/yarn still work)
754
- - **Pre-commit Hooks**: nano-staged + simple-git-hooks
755
-
756
- # Troubleshooting
757
-
758
- ## Common Build Issues
759
-
760
- ### Issue: "Cannot find liblzma library"
761
-
762
- **Solution**: Install system development package or let node-gyp download it:
541
+ If prebuilt binaries don't match your platform, install system development libraries:
763
542
 
764
543
  ```bash
765
544
  # Debian/Ubuntu
@@ -768,374 +547,196 @@ sudo apt-get install liblzma-dev
768
547
  # macOS
769
548
  brew install xz
770
549
 
771
- # Windows (let node-gyp download and build)
550
+ # Windows (automatic download and build)
772
551
  npm install node-liblzma --build-from-source
773
552
  ```
774
553
 
775
- ### Issue: "node-gyp rebuild failed"
776
-
777
- **Symptoms**: Build fails with C++ compilation errors
778
-
779
- **Solutions**:
780
- 1. Install build tools:
781
- ```bash
782
- # Ubuntu/Debian
783
- sudo apt-get install build-essential python3
784
-
785
- # macOS (install Xcode Command Line Tools)
786
- xcode-select --install
787
-
788
- # Windows
789
- npm install --global windows-build-tools
790
- ```
791
-
792
- 2. Clear build cache and retry:
793
- ```bash
794
- rm -rf build node_modules
795
- npm install
796
- ```
797
-
798
- ### Issue: "Prebuilt binary not found"
799
-
800
- **Solution**: Your platform might not have prebuilt binaries. Build from source:
554
+ ### Build from Source
801
555
 
802
556
  ```bash
557
+ # Force rebuild with default options
803
558
  npm install node-liblzma --build-from-source
804
- ```
805
-
806
- ## Runtime Issues
807
-
808
- ### Issue: "Memory allocation failed" (LZMAMemoryError)
809
-
810
- **Causes**:
811
- - Input buffer exceeds 512MB limit (security protection)
812
- - System out of memory
813
- - Trying to decompress extremely large archive
814
-
815
- **Solutions**:
816
- 1. For files > 512MB, use streaming APIs:
817
- ```typescript
818
- import { createReadStream, createWriteStream } from 'fs';
819
- import { createXz } from 'node-liblzma';
820
-
821
- createReadStream('large-file.bin')
822
- .pipe(createXz())
823
- .pipe(createWriteStream('large-file.xz'));
824
- ```
825
-
826
- 2. Or use file helpers (automatically handle large files):
827
- ```typescript
828
- await xzFile('large-file.bin', 'large-file.xz');
829
- ```
830
-
831
- ### Issue: "Corrupt compressed data" (LZMADataError)
832
-
833
- **Symptoms**: Decompression fails with `LZMADataError`
834
559
 
835
- **Causes**:
836
- - File is not actually XZ/LZMA compressed
837
- - File is corrupted or incomplete
838
- - Wrong file format (LZMA1 vs LZMA2)
839
-
840
- **Solutions**:
841
- 1. Verify file format:
842
- ```bash
843
- file compressed.xz
844
- # Should show: "XZ compressed data"
845
- ```
846
-
847
- 2. Check file integrity:
848
- ```bash
849
- xz -t compressed.xz
850
- ```
851
-
852
- 3. Handle errors gracefully:
853
- ```typescript
854
- try {
855
- const data = await unxzAsync(buffer);
856
- } catch (error) {
857
- if (error instanceof LZMADataError) {
858
- console.error('Invalid or corrupt XZ file');
859
- }
860
- }
861
- ```
862
-
863
- ### Issue: Thread support warnings during compilation
864
-
865
- **Symptoms**: Compiler warnings about `-Wmissing-field-initializers`
866
-
867
- **Status**: This is normal and does not affect functionality. Thread support still works correctly.
868
-
869
- **Disable thread support** (if warnings are problematic):
870
- ```bash
560
+ # Disable thread support
871
561
  ENABLE_THREAD_SUPPORT=no npm install node-liblzma --build-from-source
872
562
  ```
873
563
 
874
- ## Performance Issues
564
+ <details>
565
+ <summary><strong>Custom XZ installation</strong></summary>
875
566
 
876
- ### Issue: Compression is slow on multi-core systems
567
+ If you compiled XZ outside of node-liblzma:
877
568
 
878
- **Solution**: Enable multi-threaded compression:
879
-
880
- ```typescript
881
- import { xz } from 'node-liblzma';
882
-
883
- xz(buffer, { threads: 4 }, (err, compressed) => {
884
- // 4 threads used for compression
885
- });
569
+ ```bash
570
+ export CPATH=$HOME/path/to/headers
571
+ export LIBRARY_PATH=$HOME/path/to/lib
572
+ export LD_LIBRARY_PATH=$HOME/path/to/lib:$LD_LIBRARY_PATH
573
+ npm install
886
574
  ```
887
575
 
888
- **Note**: Threads only apply to compression, not decompression.
889
-
890
- ### Issue: High memory usage with concurrent operations
891
-
892
- **Solution**: Use `LZMAPool` to limit concurrency:
576
+ </details>
893
577
 
894
- ```typescript
895
- import { LZMAPool } from 'node-liblzma';
896
-
897
- const pool = new LZMAPool(5); // Limit to 5 concurrent operations
578
+ ## Testing
898
579
 
899
- // Pool automatically queues excess operations
900
- const results = await Promise.all(
901
- largeArray.map(item => pool.compress(item))
902
- );
580
+ ```bash
581
+ pnpm test # Run all 458+ tests
582
+ pnpm test:watch # Watch mode
583
+ pnpm test:coverage # Coverage report
584
+ pnpm type-check # TypeScript type checking
903
585
  ```
904
586
 
905
- ## Windows-Specific Issues
587
+ Tests use [Vitest](https://vitest.dev/) with 100% code coverage across statements, branches, functions, and lines.
906
588
 
907
- ### Issue: Build fails on Windows
589
+ ## Migration Guide
908
590
 
909
- **Solutions**:
910
- 1. Install Visual Studio Build Tools:
911
- ```powershell
912
- npm install --global windows-build-tools
913
- ```
591
+ ### v1.x → v2.0
914
592
 
915
- 2. Use the correct Python version:
916
- ```powershell
917
- npm config set python python3
918
- ```
593
+ <details>
594
+ <summary><strong>Breaking changes and new features</strong></summary>
919
595
 
920
- 3. Let the build system download XZ automatically:
921
- ```powershell
922
- npm install node-liblzma --build-from-source
923
- ```
596
+ #### Breaking Changes
924
597
 
925
- ### Issue: "Cannot find module" on Windows
598
+ 1. **Node.js >= 16** required (was >= 12)
599
+ 2. **ESM module format** (`import` instead of `require`)
600
+ 3. **TypeScript source** (CoffeeScript removed)
926
601
 
927
- **Cause**: Path separator issues in Windows
602
+ #### New Features
928
603
 
929
- **Solution**: Use forward slashes or `path.join()`:
930
604
  ```typescript
931
- import { join } from 'path';
932
- await xzFile(join('data', 'input.txt'), join('data', 'output.xz'));
933
- ```
605
+ // Promise-based APIs (recommended)
606
+ const compressed = await xzAsync(buffer);
934
607
 
935
- # Contributing
608
+ // Typed error classes
609
+ import { LZMAMemoryError, LZMADataError } from 'node-liblzma';
936
610
 
937
- We welcome contributions! Here's how to get started.
611
+ // Concurrency control
612
+ const pool = new LZMAPool(10);
613
+ const results = await Promise.all(files.map(f => pool.compress(f)));
938
614
 
939
- ## Development Setup
615
+ // File helpers
616
+ await xzFile('input.txt', 'output.txt.xz');
617
+ ```
940
618
 
941
- 1. **Clone the repository**:
942
- ```bash
943
- git clone https://github.com/oorabona/node-liblzma.git
944
- cd node-liblzma
945
- ```
619
+ #### Tooling Changes
946
620
 
947
- 2. **Install dependencies** (pnpm recommended):
948
- ```bash
949
- pnpm install
950
- # or
951
- npm install
952
- ```
621
+ - **Linter**: Biome (replaces ESLint + Prettier)
622
+ - **Tests**: Vitest (replaces Mocha)
623
+ - **Package Manager**: pnpm recommended
953
624
 
954
- 3. **Build the project**:
955
- ```bash
956
- pnpm build
957
- ```
625
+ </details>
958
626
 
959
- 4. **Run tests**:
960
- ```bash
961
- pnpm test
962
- ```
627
+ ## Contributing
963
628
 
964
- ## Development Workflow
629
+ We welcome contributions! See the full [contributing guidelines](#development-workflow) below.
965
630
 
966
- ### Running Tests
631
+ ### Development Setup
967
632
 
968
633
  ```bash
969
- # Run all tests
634
+ git clone https://github.com/oorabona/node-liblzma.git
635
+ cd node-liblzma
636
+ pnpm install
637
+ pnpm build
970
638
  pnpm test
971
-
972
- # Watch mode (re-run on changes)
973
- pnpm test:watch
974
-
975
- # Coverage report
976
- pnpm test:coverage
977
-
978
- # Interactive UI
979
- pnpm test:ui
980
639
  ```
981
640
 
982
- ### Code Quality
983
-
984
- We use [Biome](https://biomejs.dev/) for linting and formatting:
641
+ ### Development Workflow
985
642
 
986
643
  ```bash
987
- # Check code style
988
- pnpm check
989
-
990
- # Auto-fix issues
991
- pnpm check:write
992
-
993
- # Lint only
994
- pnpm lint
995
-
996
- # Format only
997
- pnpm format:write
644
+ pnpm test # Run tests
645
+ pnpm test:watch # Watch mode
646
+ pnpm test:coverage # Coverage report
647
+ pnpm check # Lint + format check (Biome)
648
+ pnpm check:write # Auto-fix lint/format
649
+ pnpm type-check # TypeScript types
998
650
  ```
999
651
 
1000
- ### Type Checking
652
+ We follow [Conventional Commits](https://www.conventionalcommits.org/):
1001
653
 
1002
- ```bash
1003
- pnpm type-check
654
+ ```
655
+ feat: add LZMAPool for concurrency control
656
+ fix: resolve memory leak in FunctionReference
657
+ docs: add migration guide for v2.0
1004
658
  ```
1005
659
 
1006
- ## Code Style
660
+ ### Pull Request Process
1007
661
 
1008
- - **Linter**: Biome (configured in `biome.json`)
1009
- - **Formatting**: Biome handles both linting and formatting
1010
- - **Pre-commit hooks**: Automatically run via nano-staged + simple-git-hooks
1011
- - **TypeScript**: Strict mode enabled
662
+ 1. Fork and create a feature branch (`feat/`, `fix/`, `refactor/`, `docs/`)
663
+ 2. Add tests for new functionality (100% coverage required)
664
+ 3. Run `pnpm check:write && pnpm type-check && pnpm test`
665
+ 4. Commit with conventional commits and push
666
+ 5. CI checks run automatically on PR
1012
667
 
1013
- ## Commit Convention
668
+ ## Troubleshooting
1014
669
 
1015
- We follow [Conventional Commits](https://www.conventionalcommits.org/):
670
+ <details>
671
+ <summary><strong>Build issues</strong></summary>
1016
672
 
673
+ **"Cannot find liblzma library"** — Install system dev package:
674
+ ```bash
675
+ sudo apt-get install liblzma-dev # Debian/Ubuntu
676
+ brew install xz # macOS
1017
677
  ```
1018
- <type>(<scope>): <description>
1019
678
 
1020
- [optional body]
1021
-
1022
- [optional footer]
679
+ **"node-gyp rebuild failed"** — Install build tools:
680
+ ```bash
681
+ sudo apt-get install build-essential python3 # Linux
682
+ xcode-select --install # macOS
683
+ npm install --global windows-build-tools # Windows
1023
684
  ```
1024
685
 
1025
- **Types**:
1026
- - `feat`: New feature
1027
- - `fix`: Bug fix
1028
- - `docs`: Documentation changes
1029
- - `refactor`: Code refactoring
1030
- - `test`: Test changes
1031
- - `chore`: Build/tooling changes
1032
- - `perf`: Performance improvements
1033
-
1034
- **Examples**:
686
+ **"Prebuilt binary not found"** — Build from source:
1035
687
  ```bash
1036
- git commit -m "feat: add LZMAPool for concurrency control"
1037
- git commit -m "fix: resolve memory leak in FunctionReference"
1038
- git commit -m "docs: add migration guide for v2.0"
688
+ npm install node-liblzma --build-from-source
1039
689
  ```
1040
690
 
1041
- ## Pull Request Process
1042
-
1043
- 1. **Fork the repository** and create a feature branch:
1044
- ```bash
1045
- git checkout -b feat/my-new-feature
1046
- ```
1047
-
1048
- 2. **Make your changes** following code style guidelines
1049
-
1050
- 3. **Add tests** for new functionality:
1051
- - All new code must have 100% test coverage
1052
- - Tests go in `test/` directory
1053
- - Use Vitest testing framework
1054
-
1055
- 4. **Ensure all checks pass**:
1056
- ```bash
1057
- pnpm check:write # Fix code style
1058
- pnpm type-check # Verify TypeScript types
1059
- pnpm test # Run test suite
1060
- ```
1061
-
1062
- 5. **Commit with conventional commits**:
1063
- ```bash
1064
- git add .
1065
- git commit -m "feat: add new feature"
1066
- ```
1067
-
1068
- 6. **Push and create Pull Request**:
1069
- ```bash
1070
- git push origin feat/my-new-feature
1071
- ```
691
+ </details>
1072
692
 
1073
- 7. **Wait for CI checks** to pass (GitHub Actions will run automatically)
693
+ <details>
694
+ <summary><strong>Runtime issues</strong></summary>
1074
695
 
1075
- ## Testing Guidelines
1076
-
1077
- - **Coverage**: Maintain 100% code coverage (statements, branches, functions, lines)
1078
- - **Test files**: Name tests `*.test.ts` in `test/` directory
1079
- - **Structure**: Use `describe` and `it` blocks with clear descriptions
1080
- - **Assertions**: Use Vitest's `expect()` API
1081
-
1082
- **Example test**:
696
+ **LZMAMemoryError** Input too large for buffer API. Use streaming:
1083
697
  ```typescript
1084
- import { describe, it, expect } from 'vitest';
1085
- import { xzAsync, unxzAsync } from '../src/lzma.js';
1086
-
1087
- describe('Compression', () => {
1088
- it('should compress and decompress data', async () => {
1089
- const original = Buffer.from('test data');
1090
- const compressed = await xzAsync(original);
1091
- const decompressed = await unxzAsync(compressed);
1092
-
1093
- expect(decompressed.equals(original)).toBe(true);
1094
- });
1095
- });
698
+ createReadStream('large.bin').pipe(createXz()).pipe(createWriteStream('large.xz'));
1096
699
  ```
1097
700
 
1098
- ## Release Process
701
+ **LZMADataError** File is not XZ-compressed or is corrupted. Verify with `file compressed.xz` or `xz -t compressed.xz`.
1099
702
 
1100
- Releases are automated using [@oorabona/release-it-preset](https://github.com/oorabona/release-it-preset):
703
+ **Slow on multi-core** Enable threads: `createXz({ threads: 0 })` (auto-detect cores).
1101
704
 
1102
- ```bash
1103
- # Standard release (patch/minor/major based on commits)
1104
- pnpm release
705
+ **High memory with concurrency** — Use `LZMAPool` to limit simultaneous operations.
1105
706
 
1106
- # Manual changelog editing
1107
- pnpm release:manual
707
+ </details>
1108
708
 
1109
- # Hotfix release
1110
- pnpm release:hotfix
709
+ <details>
710
+ <summary><strong>Windows-specific</strong></summary>
1111
711
 
1112
- # Update changelog only (no release)
1113
- pnpm changelog:update
712
+ **Build fails** Install Visual Studio Build Tools and set Python:
713
+ ```powershell
714
+ npm install --global windows-build-tools
715
+ npm config set python python3
1114
716
  ```
1115
717
 
1116
- **For maintainers only**. Contributors should submit PRs; maintainers handle releases.
718
+ **Path issues** Use `path.join()` instead of hardcoded separators.
1117
719
 
1118
- ## Getting Help
720
+ </details>
1119
721
 
1120
- - **Questions**: Open a [Discussion](https://github.com/oorabona/node-liblzma/discussions)
1121
- - **Bugs**: Open an [Issue](https://github.com/oorabona/node-liblzma/issues)
1122
- - **Security**: Email security@example.com (do not open public issues)
722
+ ## Related Projects
1123
723
 
1124
- ## License
724
+ - [lzma-purejs](https://github.com/cscott/lzma-purejs) — Pure JavaScript LZMA implementation
725
+ - [node-xz](https://github.com/robey/node-xz) — Node binding of XZ library
726
+ - [lzma-native](https://github.com/addaleax/lzma-native) — Complete XZ library bindings
1125
727
 
1126
- By contributing, you agree that your contributions will be licensed under [LGPL-3.0+](LICENSE).
728
+ ## Bugs
1127
729
 
1128
- # Bugs
730
+ If you find one, feel free to contribute and post a new [issue](https://github.com/oorabona/node-liblzma/issues)!
731
+ PRs are accepted as well :)
1129
732
 
1130
- If you find one, feel free to contribute and post a new issue!
1131
- PR are accepted as well :)
733
+ If you compile with threads, you may see warnings about `-Wmissing-field-initializers`.
734
+ This is normal and does not prevent threading from being active and working.
1132
735
 
1133
- Kudos goes to [addaleax](https://github.com/addaleax) for helping me out with C++ stuff !
736
+ ## Acknowledgements
1134
737
 
1135
- If you compile with threads, you may see a bunch of warnings about `-Wmissing-field-initializers`.
1136
- This is _normal_ and does not prevent threading from being active and working.
1137
- I did not yet figure how to fix this except by masking the warning..
738
+ Kudos to [addaleax](https://github.com/addaleax) for helping out with C++ stuff!
1138
739
 
1139
- # License
740
+ ## License
1140
741
 
1141
- This software is released under [LGPL3.0+](LICENSE)
742
+ This software is released under [LGPL-3.0+](LICENSE).