node-liblzma 2.2.0 → 3.1.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 (54) hide show
  1. package/README.md +446 -766
  2. package/lib/cli/nxz.js +410 -85
  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.d.ts.map +1 -1
  9. package/lib/lzma.inline.d.ts +30 -0
  10. package/lib/lzma.inline.d.ts.map +1 -0
  11. package/lib/lzma.inline.js +68 -0
  12. package/lib/lzma.inline.js.map +1 -0
  13. package/lib/lzma.js +20 -13
  14. package/lib/lzma.js.map +1 -1
  15. package/lib/pool.d.ts.map +1 -1
  16. package/lib/pool.js +2 -1
  17. package/lib/pool.js.map +1 -1
  18. package/lib/wasm/bindings.d.ts +109 -0
  19. package/lib/wasm/bindings.d.ts.map +1 -0
  20. package/lib/wasm/bindings.js +320 -0
  21. package/lib/wasm/bindings.js.map +1 -0
  22. package/lib/wasm/compress.d.ts +32 -0
  23. package/lib/wasm/compress.d.ts.map +1 -0
  24. package/lib/wasm/compress.js +47 -0
  25. package/lib/wasm/compress.js.map +1 -0
  26. package/lib/wasm/decompress.d.ts +32 -0
  27. package/lib/wasm/decompress.d.ts.map +1 -0
  28. package/lib/wasm/decompress.js +45 -0
  29. package/lib/wasm/decompress.js.map +1 -0
  30. package/lib/wasm/index.d.ts +14 -0
  31. package/lib/wasm/index.d.ts.map +1 -0
  32. package/lib/wasm/index.js +18 -0
  33. package/lib/wasm/index.js.map +1 -0
  34. package/lib/wasm/liblzma.inline.d.ts +10 -0
  35. package/lib/wasm/liblzma.inline.d.ts.map +1 -0
  36. package/lib/wasm/liblzma.inline.js +10 -0
  37. package/lib/wasm/liblzma.inline.js.map +1 -0
  38. package/lib/wasm/memory.d.ts +57 -0
  39. package/lib/wasm/memory.d.ts.map +1 -0
  40. package/lib/wasm/memory.js +110 -0
  41. package/lib/wasm/memory.js.map +1 -0
  42. package/lib/wasm/stream.d.ts +35 -0
  43. package/lib/wasm/stream.d.ts.map +1 -0
  44. package/lib/wasm/stream.js +168 -0
  45. package/lib/wasm/stream.js.map +1 -0
  46. package/lib/wasm/types.d.ts +77 -0
  47. package/lib/wasm/types.d.ts.map +1 -0
  48. package/lib/wasm/types.js +55 -0
  49. package/lib/wasm/types.js.map +1 -0
  50. package/lib/wasm/utils.d.ts +62 -0
  51. package/lib/wasm/utils.d.ts.map +1 -0
  52. package/lib/wasm/utils.js +164 -0
  53. package/lib/wasm/utils.js.map +1 -0
  54. package/package.json +31 -8
package/README.md CHANGED
@@ -9,32 +9,49 @@ Node-liblzma
9
9
  [![Node Version](https://img.shields.io/node/v/node-liblzma.svg)](https://nodejs.org)
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
-
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.
12
+ [![codecov](https://codecov.io/gh/oorabona/node-liblzma/graph/badge.svg)](https://codecov.io/gh/oorabona/node-liblzma)
13
+ [![code style: biome](https://img.shields.io/badge/code_style-biome-60a5fa.svg)](https://biomejs.dev)
14
+
15
+ Native Node.js bindings for liblzma XZ/LZMA2 compression with **browser support via WebAssembly**.
16
+
17
+ ## Table of Contents
18
+
19
+ - [Quick Start](#quick-start)
20
+ - [What's New](#whats-new)
21
+ - [v3.0.0 Browser & WASM Support](#v300--browser--wasm-support)
22
+ - [v2.0.0 — TypeScript Modernization](#v200--typescript-modernization)
23
+ - [Browser Usage](#browser-usage)
24
+ - [CLI Tool (nxz)](#cli-tool-nxz)
25
+ - [Ecosystem Packages](#ecosystem-packages)
26
+ - [tar-xz — tar.xz archives](#tar-xz--tarxz-archives)
27
+ - [nxz standalone CLI](#nxz--standalone-cli)
28
+ - [API Reference](#api-reference)
29
+ - [API Comparison with Zlib](#api-comparison-with-zlib)
30
+ - [Options](#options)
31
+ - [Advanced Configuration](#advanced-configuration)
32
+ - [Thread Support](#thread-support)
33
+ - [Progress Monitoring](#progress-monitoring)
34
+ - [Concurrency Control (LZMAPool)](#concurrency-control-with-lzmapool)
35
+ - [File Compression Helpers](#file-compression-helpers)
36
+ - [Error Handling](#error-handling)
37
+ - [Benchmark](#benchmark)
38
+ - [Installation](#installation)
39
+ - [Testing](#testing)
40
+ - [Migration Guide (v1 → v2)](#migration-guide)
41
+ - [Contributing](#contributing)
42
+ - [Troubleshooting](#troubleshooting)
43
+ - [Bugs](#bugs)
44
+ - [Acknowledgements](#acknowledgements)
45
+ - [License](#license)
46
+
47
+ ## What is liblzma/XZ?
48
+
49
+ [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
50
 
34
51
  > Only LZMA2 is supported for compression output.
35
- But the library can open and read any LZMA1 or LZMA2 compressed file.
52
+ > But the library can open and read any LZMA1 or LZMA2 compressed file.
36
53
 
37
- # Quick Start
54
+ ## Quick Start
38
55
 
39
56
  ```bash
40
57
  npm install node-liblzma
@@ -61,7 +78,8 @@ compressor.on('progress', ({ bytesRead, bytesWritten }) => {
61
78
  });
62
79
  ```
63
80
 
64
- **Promise style (with `.then()`):**
81
+ <details>
82
+ <summary><strong>Promise style (with .then())</strong></summary>
65
83
 
66
84
  ```typescript
67
85
  import { xzAsync, unxzAsync } from 'node-liblzma';
@@ -79,7 +97,10 @@ xzAsync(Buffer.from('Hello, World!'))
79
97
  });
80
98
  ```
81
99
 
82
- **Callback style (Node.js traditional):**
100
+ </details>
101
+
102
+ <details>
103
+ <summary><strong>Callback style (Node.js traditional)</strong></summary>
83
104
 
84
105
  ```typescript
85
106
  import { xz, unxz } from 'node-liblzma';
@@ -93,25 +114,144 @@ xz(Buffer.from('Hello, World!'), (err, compressed) => {
93
114
  });
94
115
  ```
95
116
 
117
+ </details>
118
+
96
119
  📖 **Full API documentation**: [oorabona.github.io/node-liblzma](https://oorabona.github.io/node-liblzma/)
97
120
 
98
- # Command Line Interface (nxz)
121
+ ## What's New
122
+
123
+ ### v3.0.0 — Browser & WASM Support
124
+
125
+ > **[Live Demo](https://oorabona.github.io/node-liblzma/demo/)** — Try XZ compression in your browser.
126
+
127
+ - **Browser/WASM support**: Full XZ compression and decompression via WebAssembly
128
+ - Same API as Node.js (`xzAsync`, `unxzAsync`, `createXz`, `createUnxz`)
129
+ - WASM binary: ~52KB gzipped (under 100KB budget)
130
+ - Web Streams API for streaming compression/decompression
131
+ - Zero-config inline mode: `import from 'node-liblzma/inline'`
132
+ - **CLI tool (nxz)**: Portable xz-like command line tool
133
+ - Full xz compatibility: `-z`, `-d`, `-l`, `-k`, `-f`, `-c`, `-o`, `-v`, `-q`
134
+ - Compression presets 0-9 with extreme mode (`-e`)
135
+ - tar.xz archive support: create, list, extract (compatible with system `tar`)
136
+ - Progress display, stdin/stdout piping, benchmarking (`-B`)
137
+ - **tar-xz package**: Create/extract `.tar.xz` archives — Node.js streaming + browser WASM
138
+ - **Progress events**: Monitor compression/decompression in real-time
139
+ - **XZ Utils 5.8.x**: Updated to latest stable version
140
+ - **519 tests**: Comprehensive test suite with 100% code coverage
141
+
142
+ ### v2.0.0 — TypeScript Modernization
143
+
144
+ - **Full TypeScript migration**: Complete rewrite from CoffeeScript
145
+ - **Promise-based APIs**: `xzAsync()` and `unxzAsync()`
146
+ - **Modern tooling**: Vitest, Biome, pnpm, pre-commit hooks
147
+ - **Node.js >= 16** required (updated from >= 12)
148
+
149
+ <details>
150
+ <summary><strong>Legacy (N-API migration)</strong></summary>
151
+
152
+ 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.
153
+
154
+ Tested on: Linux x64, macOS (x64/arm64), Raspberry Pi 2/3/4, Windows.
155
+
156
+ **Prebuilt binaries** are bundled for: Windows x64, Linux x64, macOS x64/arm64.
157
+
158
+ | Flag | Description | Default | Values |
159
+ |------|-------------|---------|--------|
160
+ | `USE_GLOBAL` | Use system liblzma library | `yes` (`no` on Windows) | `yes`, `no` |
161
+ | `RUNTIME_LINK` | Static or shared linking | `shared` | `static`, `shared` |
162
+ | `ENABLE_THREAD_SUPPORT` | Enable thread support | `yes` | `yes`, `no` |
163
+
164
+ If no prebuilt binary matches your platform, `node-gyp` will compile from source automatically.
165
+
166
+ </details>
167
+
168
+ ## Browser Usage
169
+
170
+ > **[Live Demo](https://oorabona.github.io/node-liblzma/demo/)** — Try XZ compression in your browser right now.
171
+
172
+ 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.
173
+
174
+ ### Basic Usage
175
+
176
+ ```typescript
177
+ // Bundlers auto-resolve to WASM in browser, native in Node.js
178
+ import { xzAsync, unxzAsync, isXZ } from 'node-liblzma';
179
+
180
+ // Compress
181
+ const compressed = await xzAsync('Hello, browser!');
182
+
183
+ // Decompress
184
+ const original = await unxzAsync(compressed);
185
+
186
+ // Check if data is XZ-compressed
187
+ if (isXZ(someBuffer)) {
188
+ const data = await unxzAsync(someBuffer);
189
+ }
190
+ ```
191
+
192
+ ### Streaming with Web Streams API
193
+
194
+ ```typescript
195
+ import { createXz, createUnxz } from 'node-liblzma';
196
+
197
+ // Compress a fetch response
198
+ const response = await fetch('/large-file.bin');
199
+ const compressed = response.body.pipeThrough(createXz({ preset: 6 }));
200
+
201
+ // Decompress
202
+ const decompressed = compressedStream.pipeThrough(createUnxz());
203
+ ```
204
+
205
+ ### Import Modes
206
+
207
+ | Import | When to use |
208
+ |--------|-------------|
209
+ | `node-liblzma` | Standard — bundler resolves to WASM (browser) or native (Node.js) |
210
+ | `node-liblzma/wasm` | Explicit WASM usage in Node.js (no native addon needed) |
211
+ | `node-liblzma/inline` | Zero-config — WASM embedded as base64 (no external file to serve) |
212
+
213
+ ```typescript
214
+ // Explicit WASM (works in Node.js too, no native build required)
215
+ import { xzAsync } from 'node-liblzma/wasm';
216
+
217
+ // Inline mode (larger bundle, but no WASM file to configure)
218
+ import { ensureInlineInit, xzAsync } from 'node-liblzma/inline';
219
+ await ensureInlineInit(); // Decodes embedded base64 WASM
220
+ const compressed = await xzAsync(data);
221
+ ```
222
+
223
+ ### Browser Limitations
224
+
225
+ - **No sync APIs**: `xzSync()` / `unxzSync()` throw `LZMAError` in browsers
226
+ - **Presets 0-6 only**: Presets 7-9 require more memory than WASM's 256MB limit
227
+ - **No filesystem**: `xzFile()` / `unxzFile()` are not available
228
+ - **No Node Streams**: Use `createXz()` / `createUnxz()` (Web TransformStream) instead of `Xz` / `Unxz` classes
229
+
230
+ ### Bundle Size
231
+
232
+ | Component | Raw | Gzipped |
233
+ |-----------|-----|---------|
234
+ | liblzma.wasm | ~107KB | ~52KB |
235
+ | Glue code (liblzma.js) | ~6KB | ~2KB |
236
+ | **Total** | **~113KB** | **~54KB** |
237
+
238
+ For detailed browser setup instructions, see [docs/BROWSER.md](docs/BROWSER.md).
239
+
240
+ ## CLI Tool (nxz)
99
241
 
100
242
  This package includes `nxz`, a portable xz-like CLI tool that works on any platform with Node.js.
101
243
 
102
- ## Installation
244
+ ### Installation
103
245
 
104
246
  ```bash
105
247
  # Global installation (recommended for CLI usage)
106
248
  npm install -g node-liblzma
107
- # or
108
- pnpm add -g node-liblzma
109
249
 
110
250
  # Then use directly
111
251
  nxz --help
112
252
  ```
113
253
 
114
- ## Quick Examples
254
+ ### Quick Examples
115
255
 
116
256
  ```bash
117
257
  # Compress a file (creates file.txt.xz, deletes original)
@@ -123,64 +263,76 @@ nxz file.txt.xz
123
263
  # Keep original file (-k)
124
264
  nxz -k file.txt
125
265
 
126
- # Decompress explicitly (-d)
127
- nxz -d archive.xz
128
-
129
266
  # Maximum compression (-9) with extreme mode (-e)
130
267
  nxz -9e large-file.bin
131
268
 
132
269
  # Compress to stdout (-c) for piping
133
270
  nxz -c file.txt > file.txt.xz
134
271
 
135
- # Decompress to stdout
136
- nxz -dc file.txt.xz | grep "pattern"
272
+ # List archive info (-l / -lv for verbose)
273
+ nxz -l file.txt.xz
274
+
275
+ # Benchmark native vs WASM performance (-B)
276
+ nxz -B file.txt
277
+ ```
137
278
 
138
- # Custom output file (-o)
139
- nxz -d archive.xz -o /tmp/output.bin
279
+ ### tar.xz Archives
140
280
 
141
- # List archive info (-l)
142
- nxz -l file.txt.xz
281
+ nxz can create, list, and extract `.tar.xz` archives — auto-detected from file extension:
143
282
 
144
- # Verbose info (-lv)
145
- nxz -lv file.txt.xz
283
+ ```bash
284
+ # Create a tar.xz archive from files/directories
285
+ nxz -T src/ lib/ README.md -o project.tar.xz
146
286
 
147
- # Compress from stdin
148
- cat file.txt | nxz -c > file.txt.xz
287
+ # List archive contents
288
+ nxz -Tl project.tar.xz
149
289
 
150
- # Quiet mode - suppress warnings (-q)
151
- nxz -q file.txt
290
+ # Extract archive to a directory
291
+ nxz -Td project.tar.xz -o output/
292
+
293
+ # Extract with path stripping (like tar --strip-components)
294
+ nxz -Td project.tar.xz --strip=1 -o output/
152
295
  ```
153
296
 
154
- ## All Options
297
+ Archives created by nxz are fully compatible with system `tar -xJf`.
298
+
299
+ ### All Options
155
300
 
156
301
  | Option | Long | Description |
157
302
  |--------|------|-------------|
158
303
  | `-z` | `--compress` | Force compression mode |
159
304
  | `-d` | `--decompress` | Force decompression mode |
160
305
  | `-l` | `--list` | List archive information |
306
+ | `-T` | `--tar` | Treat file as tar.xz archive (auto-detected for .tar.xz/.txz) |
307
+ | `-B` | `--benchmark` | Benchmark native vs WASM performance |
161
308
  | `-k` | `--keep` | Keep original file (don't delete) |
162
309
  | `-f` | `--force` | Overwrite existing output file |
163
310
  | `-c` | `--stdout` | Write to stdout, keep original file |
164
- | `-o` | `--output=FILE` | Write output to specified file |
311
+ | `-o` | `--output=FILE` | Write output to specified file (or directory for tar extract) |
165
312
  | `-v` | `--verbose` | Show progress for large files |
166
313
  | `-q` | `--quiet` | Suppress warning messages |
167
314
  | `-0`..`-9` | | Compression level (default: 6) |
168
315
  | `-e` | `--extreme` | Extreme compression (slower) |
316
+ | | `--strip=N` | Strip N leading path components on tar extract |
169
317
  | `-h` | `--help` | Show help |
170
318
  | `-V` | `--version` | Show version |
171
319
 
172
- ## One-shot Usage (without global install)
320
+ <details>
321
+ <summary><strong>One-shot usage (without global install)</strong></summary>
173
322
 
174
323
  ```bash
175
- # npm/npx
176
- npx --package node-liblzma nxz --help
177
- npx -p node-liblzma nxz file.txt
324
+ # Standalone nxz package (recommended — smaller, faster install)
325
+ npx nxz --help
326
+ pnpm dlx nxz --help
178
327
 
179
- # pnpm
328
+ # Or via the full node-liblzma package
329
+ npx --package node-liblzma nxz --help
180
330
  pnpm dlx --package node-liblzma nxz --help
181
331
  ```
182
332
 
183
- ## Exit Codes
333
+ </details>
334
+
335
+ ### Exit Codes
184
336
 
185
337
  | Code | Meaning |
186
338
  |------|---------|
@@ -188,92 +340,57 @@ pnpm dlx --package node-liblzma nxz --help
188
340
  | 1 | Error (file not found, format error, etc.) |
189
341
  | 130 | Interrupted (SIGINT/Ctrl+C) |
190
342
 
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
343
+ ## Ecosystem Packages
212
344
 
213
- This major release brings the library into 2025 with modern tooling and TypeScript support:
345
+ node-liblzma powers a family of focused packages:
214
346
 
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)
347
+ | Package | Description | Install |
348
+ |---------|-------------|---------|
349
+ | [`node-liblzma`](https://www.npmjs.com/package/node-liblzma) | Core XZ library Node.js native + browser WASM | `npm i node-liblzma` |
350
+ | [`tar-xz`](https://www.npmjs.com/package/tar-xz) | Create/extract .tar.xz archives — Node.js + browser | `npm i tar-xz` |
351
+ | [`nxz`](https://www.npmjs.com/package/nxz) | Standalone CLI `npx nxz file.txt` | `npx nxz` |
223
352
 
224
- ## Legacy (N-API migration)
353
+ ### tar-xz — tar.xz archives
225
354
 
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).
355
+ > **[Live Demo](https://oorabona.github.io/node-liblzma/tar-xz/)** Create and extract tar.xz archives in your browser.
227
356
 
228
- It has been tested and works on:
357
+ A library for working with `.tar.xz` archives, with dual Node.js (streaming) and browser (buffer-based) APIs. This fills the gap left by [node-tar](https://github.com/isaacs/node-tar) which does not support `.tar.xz`.
229
358
 
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)
241
-
242
- * For [MacOS](https://github.com/oorabona/node-liblzma/actions/workflows/ci-macos.yml)
243
-
244
- ## Prebuilt images
359
+ ```typescript
360
+ // Node.js — streaming API
361
+ import { create, extract, list } from 'tar-xz';
245
362
 
246
- Several prebuilt versions are bundled within the package.
363
+ await create({ file: 'archive.tar.xz', cwd: './src', files: ['index.ts', 'utils.ts'] });
364
+ const entries = await list({ file: 'archive.tar.xz' });
365
+ await extract({ file: 'archive.tar.xz', cwd: './output' });
247
366
 
248
- * Windows x86_64
249
- * Linux x86_64
250
- * MacOS x86_64 / Arm64
367
+ // Browser — buffer-based API
368
+ import { createTarXz, extractTarXz, listTarXz } from 'tar-xz';
251
369
 
252
- If your OS/architecture matches, you will use this version which has been compiled using the following default flags:
370
+ const archive = await createTarXz({ files: [{ name: 'hello.txt', content: data }] });
371
+ const files = await extractTarXz(archive);
372
+ const entries = await listTarXz(archive);
373
+ ```
253
374
 
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` |
375
+ ### nxz standalone CLI
259
376
 
260
- If not `node-gyp` will automagically start compiling stuff according to the environment variables set, or the default values above.
377
+ A lightweight wrapper package for running `nxz` without installing the full `node-liblzma`:
261
378
 
262
- If you want to change compilation flags, please read on [here](#installation).
379
+ ```bash
380
+ # No install needed
381
+ npx nxz file.txt # compress
382
+ npx nxz -d file.txt.xz # decompress
383
+ npx nxz -T src/ -o app.tar.xz # create tar.xz archive
263
384
 
264
- # Related projects
385
+ # Or install globally
386
+ npm install -g nxz
387
+ ```
265
388
 
266
- Thanks to the community, there are several choices out there:
389
+ ## API Reference
267
390
 
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.
391
+ ### API Comparison with Zlib
275
392
 
276
- # API comparison
393
+ The API mirrors Node.js Zlib for easy adoption:
277
394
 
278
395
  ```js
279
396
  // CommonJS
@@ -296,9 +413,7 @@ import * as lzma from 'node-liblzma';
296
413
  | - | `xzFile` | `(input, output, [options])` → `Promise<void>` |
297
414
  | - | `unxzFile` | `(input, output, [options])` → `Promise<void>` |
298
415
 
299
- ## Options
300
-
301
- The `options` object accepts the following attributes:
416
+ ### Options
302
417
 
303
418
  | Attribute | Type | Description | Values |
304
419
  |-----------|------|-------------|--------|
@@ -309,15 +424,13 @@ The `options` object accepts the following attributes:
309
424
  | `filters` | array | Filter chain | `filter.LZMA2`, `filter.X86`, `filter.ARM`, etc. |
310
425
  | `chunkSize` | number | Processing chunk size | Default: 64KB |
311
426
 
312
- For further information about each of these flags, see the [XZ SDK documentation](http://7-zip.org/sdk.html).
427
+ For further information, see the [XZ SDK documentation](http://7-zip.org/sdk.html).
313
428
 
314
429
  ## Advanced Configuration
315
430
 
316
431
  ### Thread Support
317
432
 
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:**
433
+ Multi-threaded compression is available when built with `ENABLE_THREAD_SUPPORT=yes` (default).
321
434
 
322
435
  | Value | Behavior |
323
436
  |-------|----------|
@@ -325,26 +438,15 @@ The library supports multi-threaded compression when built with `ENABLE_THREAD_S
325
438
  | `1` | Single-threaded (default) |
326
439
  | `N` | Use exactly N threads |
327
440
 
328
- **Example:**
329
-
330
441
  ```typescript
331
442
  import { createXz, hasThreads } from 'node-liblzma';
332
443
 
333
- // Check if threading is available
334
444
  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 });
445
+ const compressor = createXz({ threads: 0 }); // auto-detect
340
446
  }
341
447
  ```
342
448
 
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
449
+ > **Note**: Threads only apply to compression, not decompression.
348
450
 
349
451
  ### Progress Monitoring
350
452
 
@@ -357,65 +459,52 @@ const compressor = createXz({ preset: 6 });
357
459
 
358
460
  compressor.on('progress', ({ bytesRead, bytesWritten }) => {
359
461
  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`);
462
+ console.log(`Progress: ${bytesRead} in, ${bytesWritten} out (ratio: ${ratio.toFixed(2)})`);
367
463
  });
368
464
 
369
465
  inputStream.pipe(compressor).pipe(outputStream);
370
466
  ```
371
467
 
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`)
468
+ Progress events fire after each chunk is processed. Works with streams, not buffer APIs.
377
469
 
378
- ### Buffer Size Optimization
470
+ ### Concurrency Control with LZMAPool
379
471
 
380
- For optimal performance, the library uses configurable chunk sizes:
472
+ For production environments with high concurrency needs:
381
473
 
382
474
  ```typescript
383
- const stream = createXz({
384
- preset: lzma.preset.DEFAULT,
385
- chunkSize: 256 * 1024 // 256KB chunks (default: 64KB)
386
- });
387
- ```
475
+ import { LZMAPool } from 'node-liblzma';
388
476
 
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)
477
+ const pool = new LZMAPool(10); // Max 10 concurrent operations
394
478
 
395
- ### Memory Usage Limits
479
+ pool.on('metrics', (metrics) => {
480
+ console.log(`Active: ${metrics.active}, Queued: ${metrics.queued}`);
481
+ });
396
482
 
397
- The library enforces a 512MB maximum buffer size to prevent DoS attacks via resource exhaustion. For files larger than 512MB, use streaming APIs:
483
+ const compressed = await pool.compress(buffer);
484
+ const decompressed = await pool.decompress(compressed);
485
+ ```
486
+
487
+ ### File Compression Helpers
398
488
 
399
489
  ```typescript
400
- import { createReadStream, createWriteStream } from 'fs';
401
- import { createXz } from 'node-liblzma';
490
+ import { xzFile, unxzFile } from 'node-liblzma';
402
491
 
403
- createReadStream('large-file.bin')
404
- .pipe(createXz())
405
- .pipe(createWriteStream('large-file.xz'));
492
+ await xzFile('input.txt', 'output.txt.xz');
493
+ await unxzFile('output.txt.xz', 'restored.txt');
494
+
495
+ // With options
496
+ await xzFile('large-file.bin', 'compressed.xz', { preset: 9, threads: 4 });
406
497
  ```
407
498
 
499
+ Handles files > 512MB automatically via streams with lower memory footprint.
500
+
408
501
  ### Error Handling
409
502
 
410
- The library provides typed error classes for better error handling:
503
+ Typed error classes for precise error handling:
411
504
 
412
505
  ```typescript
413
506
  import {
414
- xzAsync,
415
- LZMAError,
416
- LZMAMemoryError,
417
- LZMADataError,
418
- LZMAFormatError
507
+ xzAsync, LZMAError, LZMAMemoryError, LZMADataError, LZMAFormatError
419
508
  } from 'node-liblzma';
420
509
 
421
510
  try {
@@ -427,339 +516,106 @@ try {
427
516
  console.error('Corrupt data:', error.message);
428
517
  } else if (error instanceof LZMAFormatError) {
429
518
  console.error('Invalid format:', error.message);
430
- } else {
431
- console.error('Unknown error:', error);
432
519
  }
433
520
  }
434
521
  ```
435
522
 
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
464
-
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
523
+ **Available error classes**: `LZMAError` (base), `LZMAMemoryError`, `LZMAMemoryLimitError`, `LZMAFormatError`, `LZMAOptionsError`, `LZMADataError`, `LZMABufferError`, `LZMAProgrammingError`.
500
524
 
501
- Simplified API for file-based compression:
525
+ ### Buffer Size Optimization
502
526
 
503
527
  ```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
528
+ const stream = createXz({
529
+ preset: lzma.preset.DEFAULT,
530
+ chunkSize: 256 * 1024 // 256KB chunks (default: 64KB)
516
531
  });
517
532
  ```
518
533
 
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`.
534
+ | File Size | Recommended chunkSize |
535
+ |-----------|-----------------------|
536
+ | < 1MB | 64KB (default) |
537
+ | 1-10MB | 128-256KB |
538
+ | > 10MB | 512KB-1MB |
533
539
 
534
- Why errno instead of JS exceptions?
540
+ Maximum buffer size: 512MB per operation (security limit). For larger files, use streaming APIs.
535
541
 
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.
542
+ ### Async Callback Contract (errno-based)
538
543
 
539
- High-level APIs remain ergonomic:
544
+ The low-level native callback follows an errno-style contract matching liblzma behavior:
540
545
 
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]`).
546
+ - **Signature**: `(errno: number, availInAfter: number, availOutAfter: number)`
547
+ - **Success**: `errno` is `LZMA_OK` or `LZMA_STREAM_END`
548
+ - **Error**: any other `errno` value
543
549
 
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.
550
+ High-level APIs remain ergonomic Promise functions resolve to `Buffer` or reject with `Error`, stream users listen to `error` events.
545
551
 
546
- # Installation
552
+ ## Benchmark
547
553
 
548
- Well, as simple as this one-liner:
554
+ ### Performance Hierarchy
549
555
 
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)
556
+ All three backends use the same liblzma library and produce **identical compression ratios**:
561
557
 
562
- ```sh
563
- pnpm add node-liblzma
564
558
  ```
565
-
566
- If you want to recompile the source, for example to disable threading support in the module, then you have to opt out with:
567
-
568
- ``` bash
569
- ENABLE_THREAD_SUPPORT=no npm install node-liblzma --build-from-source
559
+ System xz > nxz native (C++ addon) > nxz WASM (Emscripten)
560
+ fastest ~1-2x slower ~2-5x slower (decompress)
561
+ ~1x (compress, large files)
570
562
  ```
571
563
 
572
- > Note:
573
- Enabling thread support in the library will __NOT__ work if the LZMA library itself has been built without such support.
564
+ ### Full Comparison (246 KB source code, preset 6)
574
565
 
575
- To build the module, you have the following options:
566
+ | Backend | Compress | Decompress | Size | Environment |
567
+ |---------|----------|------------|------|-------------|
568
+ | **System `xz` 5.8** | 81 ms | 4 ms | 76.7 KB | C binary |
569
+ | **nxz native** | 90 ms | 3.4 ms | 76.7 KB | Node.js + C++ addon |
570
+ | **nxz WASM** | 86 ms | 7.9 ms | 76.7 KB | Node.js + Emscripten |
576
571
 
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
572
+ ### Native vs WASM (nxz -B, preset 6)
580
573
 
581
- ## Using system dev libraries to compile
574
+ | Data | Compress | Decompress | Notes |
575
+ |------|----------|------------|-------|
576
+ | 1 KB text | WASM 2.8x slower | WASM 4.9x slower | Startup overhead dominates |
577
+ | 135 KB binary | ~1:1 | WASM 2x slower | Compression near-parity |
578
+ | 246 KB source | ~1:1 | WASM 2.3x slower | Realistic workload |
579
+ | 1 MB random | ~1:1 | WASM 1.6x slower | Gap narrows with size |
582
580
 
583
- You need to have the development package installed on your system. If you have Debian based distro:
581
+ <details>
582
+ <summary><strong>Running benchmarks</strong></summary>
584
583
 
585
- ```
586
- # apt-get install liblzma-dev
587
- ```
588
-
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.
592
-
593
- Just do:
594
-
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.
600
-
601
- ## Local install of `xz` sources (outside `node-liblzma`)
602
-
603
- So you did install `xz` somewhere outside the module and want the module to use it.
604
-
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).
584
+ ```bash
585
+ # Compare nxz (native) vs system xz across file sizes
586
+ ./scripts/benchmark.sh
587
+ ./scripts/benchmark.sh -s 1,50,200 -p 6,9 # custom sizes/presets
588
+ ./scripts/benchmark.sh -o csv > results.csv # export as CSV/JSON
606
589
 
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
590
+ # Compare native addon vs WASM backend
591
+ nxz --benchmark file.txt
592
+ nxz -B -3 large-file.bin # with preset 3
611
593
  ```
612
594
 
613
- The latest is needed for tests to be run right after.
614
-
615
- Once done, this should suffice:
616
-
617
- ```sh
618
- npm install
619
- ```
595
+ </details>
620
596
 
621
- # Testing
597
+ ### When to Use What
622
598
 
623
- This project maintains **100% code coverage** across all statements, branches, functions, and lines.
599
+ | Scenario | Recommended |
600
+ |----------|-------------|
601
+ | Browser | WASM (only option) |
602
+ | Node.js, performance-critical | Native addon |
603
+ | Node.js, no C++ toolchain available | WASM (`node-liblzma/wasm`) |
604
+ | Cross-platform scripts | nxz CLI |
605
+ | Batch processing many files | System xz |
606
+ | CI/CD with Node.js already installed | nxz CLI |
624
607
 
625
- You can run tests with:
608
+ ## Installation
626
609
 
627
- ```sh
628
- npm test
610
+ ```bash
611
+ npm install node-liblzma
629
612
  # 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)
613
+ pnpm add node-liblzma
746
614
  ```
747
615
 
748
- ### Tooling Updates
749
-
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
616
+ ### System Libraries
755
617
 
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:
618
+ If prebuilt binaries don't match your platform, install system development libraries:
763
619
 
764
620
  ```bash
765
621
  # Debian/Ubuntu
@@ -768,374 +624,198 @@ sudo apt-get install liblzma-dev
768
624
  # macOS
769
625
  brew install xz
770
626
 
771
- # Windows (let node-gyp download and build)
627
+ # Windows (automatic download and build)
772
628
  npm install node-liblzma --build-from-source
773
629
  ```
774
630
 
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:
631
+ ### Build from Source
801
632
 
802
633
  ```bash
634
+ # Force rebuild with default options
803
635
  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
-
835
- **Causes**:
836
- - File is not actually XZ/LZMA compressed
837
- - File is corrupted or incomplete
838
- - Wrong file format (LZMA1 vs LZMA2)
839
636
 
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
637
+ # Disable thread support
871
638
  ENABLE_THREAD_SUPPORT=no npm install node-liblzma --build-from-source
872
639
  ```
873
640
 
874
- ## Performance Issues
875
-
876
- ### Issue: Compression is slow on multi-core systems
641
+ <details>
642
+ <summary><strong>Custom XZ installation</strong></summary>
877
643
 
878
- **Solution**: Enable multi-threaded compression:
879
-
880
- ```typescript
881
- import { xz } from 'node-liblzma';
644
+ If you compiled XZ outside of node-liblzma:
882
645
 
883
- xz(buffer, { threads: 4 }, (err, compressed) => {
884
- // 4 threads used for compression
885
- });
646
+ ```bash
647
+ export CPATH=$HOME/path/to/headers
648
+ export LIBRARY_PATH=$HOME/path/to/lib
649
+ export LD_LIBRARY_PATH=$HOME/path/to/lib:$LD_LIBRARY_PATH
650
+ npm install
886
651
  ```
887
652
 
888
- **Note**: Threads only apply to compression, not decompression.
889
-
890
- ### Issue: High memory usage with concurrent operations
653
+ </details>
891
654
 
892
- **Solution**: Use `LZMAPool` to limit concurrency:
655
+ ## Testing
893
656
 
894
- ```typescript
895
- import { LZMAPool } from 'node-liblzma';
896
-
897
- const pool = new LZMAPool(5); // Limit to 5 concurrent operations
898
-
899
- // Pool automatically queues excess operations
900
- const results = await Promise.all(
901
- largeArray.map(item => pool.compress(item))
902
- );
657
+ ```bash
658
+ pnpm test # Run all 519 tests
659
+ pnpm test:watch # Watch mode
660
+ pnpm test:coverage # Coverage report
661
+ pnpm type-check # TypeScript type checking
903
662
  ```
904
663
 
905
- ## Windows-Specific Issues
664
+ Tests use [Vitest](https://vitest.dev/) with 100% code coverage across statements, branches, functions, and lines.
906
665
 
907
- ### Issue: Build fails on Windows
666
+ ## Migration Guide
908
667
 
909
- **Solutions**:
910
- 1. Install Visual Studio Build Tools:
911
- ```powershell
912
- npm install --global windows-build-tools
913
- ```
668
+ ### v1.x → v2.0
914
669
 
915
- 2. Use the correct Python version:
916
- ```powershell
917
- npm config set python python3
918
- ```
670
+ <details>
671
+ <summary><strong>Breaking changes and new features</strong></summary>
919
672
 
920
- 3. Let the build system download XZ automatically:
921
- ```powershell
922
- npm install node-liblzma --build-from-source
923
- ```
673
+ #### Breaking Changes
924
674
 
925
- ### Issue: "Cannot find module" on Windows
675
+ 1. **Node.js >= 16** required (was >= 12)
676
+ 2. **ESM module format** (`import` instead of `require`)
677
+ 3. **TypeScript source** (CoffeeScript removed)
926
678
 
927
- **Cause**: Path separator issues in Windows
679
+ #### New Features
928
680
 
929
- **Solution**: Use forward slashes or `path.join()`:
930
681
  ```typescript
931
- import { join } from 'path';
932
- await xzFile(join('data', 'input.txt'), join('data', 'output.xz'));
933
- ```
682
+ // Promise-based APIs (recommended)
683
+ const compressed = await xzAsync(buffer);
934
684
 
935
- # Contributing
685
+ // Typed error classes
686
+ import { LZMAMemoryError, LZMADataError } from 'node-liblzma';
936
687
 
937
- We welcome contributions! Here's how to get started.
688
+ // Concurrency control
689
+ const pool = new LZMAPool(10);
690
+ const results = await Promise.all(files.map(f => pool.compress(f)));
938
691
 
939
- ## Development Setup
692
+ // File helpers
693
+ await xzFile('input.txt', 'output.txt.xz');
694
+ ```
940
695
 
941
- 1. **Clone the repository**:
942
- ```bash
943
- git clone https://github.com/oorabona/node-liblzma.git
944
- cd node-liblzma
945
- ```
696
+ #### Tooling Changes
946
697
 
947
- 2. **Install dependencies** (pnpm recommended):
948
- ```bash
949
- pnpm install
950
- # or
951
- npm install
952
- ```
698
+ - **Linter**: Biome (replaces ESLint + Prettier)
699
+ - **Tests**: Vitest (replaces Mocha)
700
+ - **Package Manager**: pnpm recommended
953
701
 
954
- 3. **Build the project**:
955
- ```bash
956
- pnpm build
957
- ```
702
+ </details>
958
703
 
959
- 4. **Run tests**:
960
- ```bash
961
- pnpm test
962
- ```
704
+ ## Contributing
963
705
 
964
- ## Development Workflow
706
+ We welcome contributions! See the full [contributing guidelines](#development-workflow) below.
965
707
 
966
- ### Running Tests
708
+ ### Development Setup
967
709
 
968
710
  ```bash
969
- # Run all tests
711
+ git clone https://github.com/oorabona/node-liblzma.git
712
+ cd node-liblzma
713
+ pnpm install
714
+ pnpm build
970
715
  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
716
  ```
981
717
 
982
- ### Code Quality
983
-
984
- We use [Biome](https://biomejs.dev/) for linting and formatting:
718
+ ### Development Workflow
985
719
 
986
720
  ```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
721
+ pnpm test # Run tests
722
+ pnpm test:watch # Watch mode
723
+ pnpm test:coverage # Coverage report
724
+ pnpm check # Lint + format check (Biome)
725
+ pnpm check:write # Auto-fix lint/format
726
+ pnpm type-check # TypeScript types
998
727
  ```
999
728
 
1000
- ### Type Checking
729
+ We follow [Conventional Commits](https://www.conventionalcommits.org/):
1001
730
 
1002
- ```bash
1003
- pnpm type-check
731
+ ```
732
+ feat: add LZMAPool for concurrency control
733
+ fix: resolve memory leak in FunctionReference
734
+ docs: add migration guide for v2.0
1004
735
  ```
1005
736
 
1006
- ## Code Style
737
+ ### Pull Request Process
1007
738
 
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
739
+ 1. Fork and create a feature branch (`feat/`, `fix/`, `refactor/`, `docs/`)
740
+ 2. Add tests for new functionality (100% coverage required)
741
+ 3. Run `pnpm check:write && pnpm type-check && pnpm test`
742
+ 4. Commit with conventional commits and push
743
+ 5. CI checks run automatically on PR
1012
744
 
1013
- ## Commit Convention
745
+ ## Troubleshooting
1014
746
 
1015
- We follow [Conventional Commits](https://www.conventionalcommits.org/):
747
+ <details>
748
+ <summary><strong>Build issues</strong></summary>
1016
749
 
750
+ **"Cannot find liblzma library"** — Install system dev package:
751
+ ```bash
752
+ sudo apt-get install liblzma-dev # Debian/Ubuntu
753
+ brew install xz # macOS
1017
754
  ```
1018
- <type>(<scope>): <description>
1019
755
 
1020
- [optional body]
1021
-
1022
- [optional footer]
756
+ **"node-gyp rebuild failed"** — Install build tools:
757
+ ```bash
758
+ sudo apt-get install build-essential python3 # Linux
759
+ xcode-select --install # macOS
760
+ npm install --global windows-build-tools # Windows
1023
761
  ```
1024
762
 
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**:
763
+ **"Prebuilt binary not found"** — Build from source:
1035
764
  ```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"
765
+ npm install node-liblzma --build-from-source
1039
766
  ```
1040
767
 
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
- ```
768
+ </details>
1072
769
 
1073
- 7. **Wait for CI checks** to pass (GitHub Actions will run automatically)
770
+ <details>
771
+ <summary><strong>Runtime issues</strong></summary>
1074
772
 
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**:
773
+ **LZMAMemoryError** Input too large for buffer API. Use streaming:
1083
774
  ```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
- });
775
+ createReadStream('large.bin').pipe(createXz()).pipe(createWriteStream('large.xz'));
1096
776
  ```
1097
777
 
1098
- ## Release Process
778
+ **LZMADataError** File is not XZ-compressed or is corrupted. Verify with `file compressed.xz` or `xz -t compressed.xz`.
1099
779
 
1100
- Releases are automated using [@oorabona/release-it-preset](https://github.com/oorabona/release-it-preset):
780
+ **Slow on multi-core** Enable threads: `createXz({ threads: 0 })` (auto-detect cores).
1101
781
 
1102
- ```bash
1103
- # Standard release (patch/minor/major based on commits)
1104
- pnpm release
782
+ **High memory with concurrency** — Use `LZMAPool` to limit simultaneous operations.
1105
783
 
1106
- # Manual changelog editing
1107
- pnpm release:manual
784
+ </details>
1108
785
 
1109
- # Hotfix release
1110
- pnpm release:hotfix
786
+ <details>
787
+ <summary><strong>Windows-specific</strong></summary>
1111
788
 
1112
- # Update changelog only (no release)
1113
- pnpm changelog:update
789
+ **Build fails** Install Visual Studio Build Tools and set Python:
790
+ ```powershell
791
+ npm install --global windows-build-tools
792
+ npm config set python python3
1114
793
  ```
1115
794
 
1116
- **For maintainers only**. Contributors should submit PRs; maintainers handle releases.
795
+ **Path issues** Use `path.join()` instead of hardcoded separators.
1117
796
 
1118
- ## Getting Help
797
+ </details>
1119
798
 
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)
799
+ ## Related Projects
1123
800
 
1124
- ## License
801
+ - [tar-xz](https://www.npmjs.com/package/tar-xz) — Create/extract tar.xz archives (powered by node-liblzma)
802
+ - [nxz](https://www.npmjs.com/package/nxz) — Standalone CLI for XZ compression
803
+ - [lzma-purejs](https://github.com/cscott/lzma-purejs) — Pure JavaScript LZMA implementation
804
+ - [node-xz](https://github.com/robey/node-xz) — Node binding of XZ library
805
+ - [lzma-native](https://github.com/addaleax/lzma-native) — Complete XZ library bindings
1125
806
 
1126
- By contributing, you agree that your contributions will be licensed under [LGPL-3.0+](LICENSE).
807
+ ## Bugs
1127
808
 
1128
- # Bugs
809
+ If you find one, feel free to contribute and post a new [issue](https://github.com/oorabona/node-liblzma/issues)!
810
+ PRs are accepted as well :)
1129
811
 
1130
- If you find one, feel free to contribute and post a new issue!
1131
- PR are accepted as well :)
812
+ If you compile with threads, you may see warnings about `-Wmissing-field-initializers`.
813
+ This is normal and does not prevent threading from being active and working.
1132
814
 
1133
- Kudos goes to [addaleax](https://github.com/addaleax) for helping me out with C++ stuff !
815
+ ## Acknowledgements
1134
816
 
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..
817
+ Kudos to [addaleax](https://github.com/addaleax) for helping out with C++ stuff!
1138
818
 
1139
- # License
819
+ ## License
1140
820
 
1141
- This software is released under [LGPL3.0+](LICENSE)
821
+ This software is released under [LGPL-3.0+](LICENSE).