
Security News
Ruby's Bundler 4.0.18 Extends Cooldown to bundle lock and bundle cache
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.
node-liblzma
Advanced tools
XZ is a container for compressed archives. It is among the best compressors out there according to several benchmarks:
It has a good balance between compression time/ratio and decompression time/memory.
This project aims towards providing:
A quick and easy way to play with XZ compression: 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:
Complete integration with XZ sources/binaries: You can either use system packages or download a specific version and compile it! See installation below.
Only LZMA2 is supported for compression output. But the library can open and read any LZMA1 or LZMA2 compressed file.
npm install node-liblzma
import { xzAsync, unxzAsync, createXz, createUnxz } from 'node-liblzma';
// Simple: Compress a buffer
const compressed = await xzAsync(Buffer.from('Hello, World!'));
const decompressed = await unxzAsync(compressed);
// Streaming: Compress a file
import { createReadStream, createWriteStream } from 'fs';
createReadStream('input.txt')
.pipe(createXz())
.pipe(createWriteStream('output.xz'));
// With progress monitoring
const compressor = createXz();
compressor.on('progress', ({ bytesRead, bytesWritten }) => {
console.log(`${bytesRead} bytes in โ ${bytesWritten} bytes out`);
});
Promise style (with .then()):
import { xzAsync, unxzAsync } from 'node-liblzma';
xzAsync(Buffer.from('Hello, World!'))
.then(compressed => {
console.log('Compressed size:', compressed.length);
return unxzAsync(compressed);
})
.then(decompressed => {
console.log('Decompressed:', decompressed.toString());
})
.catch(err => {
console.error('Compression failed:', err);
});
Callback style (Node.js traditional):
import { xz, unxz } from 'node-liblzma';
xz(Buffer.from('Hello, World!'), (err, compressed) => {
if (err) throw err;
unxz(compressed, (err, decompressed) => {
if (err) throw err;
console.log('Decompressed:', decompressed.toString());
});
});
๐ Full API documentation: oorabona.github.io/node-liblzma
This package includes nxz, a portable xz-like CLI tool that works on any platform with Node.js.
# Global installation (recommended for CLI usage)
npm install -g node-liblzma
# or
pnpm add -g node-liblzma
# Then use directly
nxz --help
# Compress a file (creates file.txt.xz, deletes original)
nxz file.txt
# Decompress (auto-detected from .xz extension)
nxz file.txt.xz
# Keep original file (-k)
nxz -k file.txt
# Decompress explicitly (-d)
nxz -d archive.xz
# Maximum compression (-9) with extreme mode (-e)
nxz -9e large-file.bin
# Compress to stdout (-c) for piping
nxz -c file.txt > file.txt.xz
# Decompress to stdout
nxz -dc file.txt.xz | grep "pattern"
# Custom output file (-o)
nxz -d archive.xz -o /tmp/output.bin
# List archive info (-l)
nxz -l file.txt.xz
# Verbose info (-lv)
nxz -lv file.txt.xz
# Compress from stdin
cat file.txt | nxz -c > file.txt.xz
# Quiet mode - suppress warnings (-q)
nxz -q file.txt
| Option | Long | Description |
|---|---|---|
-z | --compress | Force compression mode |
-d | --decompress | Force decompression mode |
-l | --list | List archive information |
-k | --keep | Keep original file (don't delete) |
-f | --force | Overwrite existing output file |
-c | --stdout | Write to stdout, keep original file |
-o | --output=FILE | Write output to specified file |
-v | --verbose | Show progress for large files |
-q | --quiet | Suppress warning messages |
-0..-9 | Compression level (default: 6) | |
-e | --extreme | Extreme compression (slower) |
-h | --help | Show help |
-V | --version | Show version |
# npm/npx
npx --package node-liblzma nxz --help
npx -p node-liblzma nxz file.txt
# pnpm
pnpm dlx --package node-liblzma nxz --help
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Error (file not found, format error, etc.) |
| 130 | Interrupted (SIGINT/Ctrl+C) |
-z, -d, -l, -k, -f, -c, -o, -v, -q-e)const compressor = createXz();
compressor.on('progress', ({ bytesRead, bytesWritten }) => {
console.log(`Read: ${bytesRead}, Written: ${bytesWritten}`);
});
This major release brings the library into 2025 with modern tooling and TypeScript support:
xzAsync() and unxzAsync() with Promise supportIn previous versions, N-API became the de facto standard to provide stable ABI API for NodeJS Native Modules, replacing nan.
It has been tested and works on:
macos-11)windows-2019 and windows-2022 are part of GitHub CI)Notes:
- For Windows 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
Several prebuilt versions are bundled within the package.
If your OS/architecture matches, you will use this version which has been compiled using the following default flags:
| Flag | Description | Default | Values |
|---|---|---|---|
USE_GLOBAL | Use system liblzma library | yes (no on Windows) | yes, no |
RUNTIME_LINK | Static or shared linking | shared | static, shared |
ENABLE_THREAD_SUPPORT | Enable thread support | yes | yes, no |
If not node-gyp will automagically start compiling stuff according to the environment variables set, or the default values above.
If you want to change compilation flags, please read on here.
Thanks to the community, there are several choices out there:
// CommonJS
var lzma = require('node-liblzma');
// TypeScript / ES6 modules
import * as lzma from 'node-liblzma';
| Zlib | node-liblzma | Arguments |
|---|---|---|
createGzip | createXz | ([options]) |
createGunzip | createUnxz | ([options]) |
gzip | xz | (buf, [options], callback) |
gunzip | unxz | (buf, [options], callback) |
gzipSync | xzSync | (buf, [options]) |
gunzipSync | unxzSync | (buf, [options]) |
| - | xzAsync | (buf, [options]) โ Promise<Buffer> |
| - | unxzAsync | (buf, [options]) โ Promise<Buffer> |
| - | xzFile | (input, output, [options]) โ Promise<void> |
| - | unxzFile | (input, output, [options]) โ Promise<void> |
The options object accepts the following attributes:
| Attribute | Type | Description | Values |
|---|---|---|---|
check | number | Integrity check | check.NONE, check.CRC32, check.CRC64, check.SHA256 |
preset | number | Compression level (0-9) | preset.DEFAULT (6), preset.EXTREME |
mode | number | Compression mode | mode.FAST, mode.NORMAL |
threads | number | Thread count | 0 = auto (all cores), 1 = single-threaded, N = N threads |
filters | array | Filter chain | filter.LZMA2, filter.X86, filter.ARM, etc. |
chunkSize | number | Processing chunk size | Default: 64KB |
For further information about each of these flags, see the XZ SDK documentation.
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.
Thread values:
| Value | Behavior |
|---|---|
0 | Auto-detect: use all available CPU cores |
1 | Single-threaded (default) |
N | Use exactly N threads |
Example:
import { createXz, hasThreads } from 'node-liblzma';
// Check if threading is available
if (hasThreads()) {
// Auto-detect: use all CPU cores
const compressor = createXz({ threads: 0 });
// Or specify exact thread count
const compressor4 = createXz({ threads: 4 });
}
Important notes:
ENABLE_THREAD_SUPPORT=yes)threads: 1 (single-threaded) for predictable behaviorhasThreads() returns true if multi-threading is supportedTrack compression and decompression progress in real-time:
import { createXz, createUnxz } from 'node-liblzma';
const compressor = createXz({ preset: 6 });
compressor.on('progress', ({ bytesRead, bytesWritten }) => {
const ratio = bytesWritten / bytesRead;
console.log(`Progress: ${bytesRead} bytes in, ${bytesWritten} bytes out (ratio: ${ratio.toFixed(2)})`);
});
// Works with both compression and decompression
const decompressor = createUnxz();
decompressor.on('progress', ({ bytesRead, bytesWritten }) => {
console.log(`Decompressing: ${bytesRead} โ ${bytesWritten} bytes`);
});
inputStream.pipe(compressor).pipe(outputStream);
Notes:
bytesRead: Total input bytes processed so farbytesWritten: Total output bytes produced so farxz/unxz)For optimal performance, the library uses configurable chunk sizes:
const stream = createXz({
preset: lzma.preset.DEFAULT,
chunkSize: 256 * 1024 // 256KB chunks (default: 64KB)
});
Recommendations:
The library enforces a 512MB maximum buffer size to prevent DoS attacks via resource exhaustion. For files larger than 512MB, use streaming APIs:
import { createReadStream, createWriteStream } from 'fs';
import { createXz } from 'node-liblzma';
createReadStream('large-file.bin')
.pipe(createXz())
.pipe(createWriteStream('large-file.xz'));
The library provides typed error classes for better error handling:
import {
xzAsync,
LZMAError,
LZMAMemoryError,
LZMADataError,
LZMAFormatError
} from 'node-liblzma';
try {
const compressed = await xzAsync(buffer);
} catch (error) {
if (error instanceof LZMAMemoryError) {
console.error('Out of memory:', error.message);
} else if (error instanceof LZMADataError) {
console.error('Corrupt data:', error.message);
} else if (error instanceof LZMAFormatError) {
console.error('Invalid format:', error.message);
} else {
console.error('Unknown error:', error);
}
}
Available error classes:
LZMAError - Base error classLZMAMemoryError - Memory allocation failedLZMAMemoryLimitError - Memory limit exceededLZMAFormatError - Unrecognized file formatLZMAOptionsError - Invalid compression optionsLZMADataError - Corrupt compressed dataLZMABufferError - Buffer size issuesLZMAProgrammingError - Internal errorsStreams automatically handle recoverable errors and provide state transition hooks:
const decompressor = createUnxz();
decompressor.on('error', (error) => {
console.error('Decompression error:', error.errno, error.message);
// Stream will emit 'close' event after error
});
decompressor.on('close', () => {
console.log('Stream closed, safe to cleanup');
});
For production environments with high concurrency needs, use LZMAPool to limit simultaneous operations:
import { LZMAPool } from 'node-liblzma';
const pool = new LZMAPool(10); // Max 10 concurrent operations
// Monitor pool metrics
pool.on('metrics', (metrics) => {
console.log(`Active: ${metrics.active}, Queued: ${metrics.queued}`);
console.log(`Completed: ${metrics.completed}, Failed: ${metrics.failed}`);
});
// Compress with automatic queuing
const compressed = await pool.compress(buffer);
const decompressed = await pool.decompress(compressed);
// Get current metrics
const status = pool.getMetrics();
Pool Events:
queue - Task added to queuestart - Task started processingcomplete - Task completed successfullyerror-task - Task failedmetrics - Metrics updated (after each state change)Benefits:
Simplified API for file-based compression:
import { xzFile, unxzFile } from 'node-liblzma';
// Compress a file
await xzFile('input.txt', 'output.txt.xz');
// Decompress a file
await unxzFile('output.txt.xz', 'restored.txt');
// With options
await xzFile('large-file.bin', 'compressed.xz', {
preset: 9,
threads: 4
});
Advantages over buffer APIs:
The low-level native callback used internally by streams follows an errno-style contract to match liblzma behavior and to avoid mixing exception channels:
(errno: number, availInAfter: number, availOutAfter: number)errno is either LZMA_OK or LZMA_STREAM_END.errno value (for example, LZMA_BUF_ERROR, LZMA_DATA_ERROR, LZMA_PROG_ERROR) indicates an error state.onerror with the numeric errno when errno !== LZMA_OK && errno !== LZMA_STREAM_END.Why errno instead of JS exceptions?
High-level APIs remain ergonomic:
xzAsync()/unxzAsync() still resolve to Buffer or reject with Error as expected.error events, where we map errno to a human-friendly message (messages[errno]).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.
Well, as simple as this one-liner:
npm i node-liblzma --save
--OR--
yarn add node-liblzma
--OR-- (recommended for development)
pnpm add node-liblzma
If you want to recompile the source, for example to disable threading support in the module, then you have to opt out with:
ENABLE_THREAD_SUPPORT=no npm install node-liblzma --build-from-source
Note: Enabling thread support in the library will NOT work if the LZMA library itself has been built without such support.
To build the module, you have the following options:
xz and build itxz yourself, outside node-liblzma, and have it use it afterYou need to have the development package installed on your system. If you have Debian based distro:
# apt-get install liblzma-dev
xzIf you do not plan on having a local install, you can ask for automatic download and build of whatever version of xz you want.
Just do:
npm install node-liblzma --build-from-source
When no option is given in the commandline arguments, it will build with default values.
xz sources (outside node-liblzma)So you did install xz somewhere outside the module and want the module to use it.
For that, you need to set the include directory and library directory search paths as GCC environment variables.
export CPATH=$HOME/path/to/headers
export LIBRARY_PATH=$HOME/path/to/lib
export LD_LIBRARY_PATH=$HOME/path/to/lib:$LD_LIBRARY_PATH
The latest is needed for tests to be run right after.
Once done, this should suffice:
npm install
This project maintains 100% code coverage across all statements, branches, functions, and lines.
You can run tests with:
npm test
# or
pnpm test
It will build and launch the test suite (325+ tests) with Vitest with TypeScript support and coverage reporting.
Additional testing commands:
# Watch mode for development
pnpm test:watch
# Coverage report
pnpm test:coverage
# Type checking
pnpm type-check
As the API is very close to NodeJS Zlib, you will probably find a good reference there.
Otherwise examples can be found as part of the test suite, so feel free to use them! They are written in TypeScript with full type definitions.
Version 2.0 introduces several breaking changes along with powerful new features.
Node.js Version Requirement
- Requires Node.js >= 12
+ Requires Node.js >= 16
ESM Module Format
- CommonJS: var lzma = require('node-liblzma');
+ ESM: import * as lzma from 'node-liblzma';
+ CommonJS still works via dynamic import
TypeScript Migration
Promise-based APIs (Recommended for new code)
// Old callback style (still works)
xz(buffer, (err, compressed) => {
if (err) throw err;
// use compressed
});
// New Promise style
try {
const compressed = await xzAsync(buffer);
// use compressed
} catch (err) {
// handle error
}
Typed Error Classes (Better error handling)
import { LZMAMemoryError, LZMADataError } from 'node-liblzma';
try {
await unxzAsync(corruptData);
} catch (error) {
if (error instanceof LZMADataError) {
console.error('Corrupt compressed data');
} else if (error instanceof LZMAMemoryError) {
console.error('Out of memory');
}
}
Concurrency Control (For high-throughput applications)
import { LZMAPool } from 'node-liblzma';
const pool = new LZMAPool(10); // Max 10 concurrent operations
// Automatic queuing and backpressure
const results = await Promise.all(
files.map(file => pool.compress(file))
);
File Helpers (Simpler file compression)
import { xzFile, unxzFile } from 'node-liblzma';
// Compress a file (handles streaming automatically)
await xzFile('input.txt', 'output.txt.xz');
// Decompress a file
await unxzFile('output.txt.xz', 'restored.txt');
If you maintain tests for code using node-liblzma:
- Mocha test framework
+ Vitest test framework (faster, better TypeScript support)
Development tooling has been modernized:
Solution: Install system development package or let node-gyp download it:
# Debian/Ubuntu
sudo apt-get install liblzma-dev
# macOS
brew install xz
# Windows (let node-gyp download and build)
npm install node-liblzma --build-from-source
Symptoms: Build fails with C++ compilation errors
Solutions:
Install build tools:
# Ubuntu/Debian
sudo apt-get install build-essential python3
# macOS (install Xcode Command Line Tools)
xcode-select --install
# Windows
npm install --global windows-build-tools
Clear build cache and retry:
rm -rf build node_modules
npm install
Solution: Your platform might not have prebuilt binaries. Build from source:
npm install node-liblzma --build-from-source
Causes:
Solutions:
For files > 512MB, use streaming APIs:
import { createReadStream, createWriteStream } from 'fs';
import { createXz } from 'node-liblzma';
createReadStream('large-file.bin')
.pipe(createXz())
.pipe(createWriteStream('large-file.xz'));
Or use file helpers (automatically handle large files):
await xzFile('large-file.bin', 'large-file.xz');
Symptoms: Decompression fails with LZMADataError
Causes:
Solutions:
Verify file format:
file compressed.xz
# Should show: "XZ compressed data"
Check file integrity:
xz -t compressed.xz
Handle errors gracefully:
try {
const data = await unxzAsync(buffer);
} catch (error) {
if (error instanceof LZMADataError) {
console.error('Invalid or corrupt XZ file');
}
}
Symptoms: Compiler warnings about -Wmissing-field-initializers
Status: This is normal and does not affect functionality. Thread support still works correctly.
Disable thread support (if warnings are problematic):
ENABLE_THREAD_SUPPORT=no npm install node-liblzma --build-from-source
Solution: Enable multi-threaded compression:
import { xz } from 'node-liblzma';
xz(buffer, { threads: 4 }, (err, compressed) => {
// 4 threads used for compression
});
Note: Threads only apply to compression, not decompression.
Solution: Use LZMAPool to limit concurrency:
import { LZMAPool } from 'node-liblzma';
const pool = new LZMAPool(5); // Limit to 5 concurrent operations
// Pool automatically queues excess operations
const results = await Promise.all(
largeArray.map(item => pool.compress(item))
);
Solutions:
Install Visual Studio Build Tools:
npm install --global windows-build-tools
Use the correct Python version:
npm config set python python3
Let the build system download XZ automatically:
npm install node-liblzma --build-from-source
Cause: Path separator issues in Windows
Solution: Use forward slashes or path.join():
import { join } from 'path';
await xzFile(join('data', 'input.txt'), join('data', 'output.xz'));
We welcome contributions! Here's how to get started.
Clone the repository:
git clone https://github.com/oorabona/node-liblzma.git
cd node-liblzma
Install dependencies (pnpm recommended):
pnpm install
# or
npm install
Build the project:
pnpm build
Run tests:
pnpm test
# Run all tests
pnpm test
# Watch mode (re-run on changes)
pnpm test:watch
# Coverage report
pnpm test:coverage
# Interactive UI
pnpm test:ui
We use Biome for linting and formatting:
# Check code style
pnpm check
# Auto-fix issues
pnpm check:write
# Lint only
pnpm lint
# Format only
pnpm format:write
pnpm type-check
biome.json)We follow Conventional Commits:
<type>(<scope>): <description>
[optional body]
[optional footer]
Types:
feat: New featurefix: Bug fixdocs: Documentation changesrefactor: Code refactoringtest: Test changeschore: Build/tooling changesperf: Performance improvementsExamples:
git commit -m "feat: add LZMAPool for concurrency control"
git commit -m "fix: resolve memory leak in FunctionReference"
git commit -m "docs: add migration guide for v2.0"
Fork the repository and create a feature branch:
git checkout -b feat/my-new-feature
Make your changes following code style guidelines
Add tests for new functionality:
test/ directoryEnsure all checks pass:
pnpm check:write # Fix code style
pnpm type-check # Verify TypeScript types
pnpm test # Run test suite
Commit with conventional commits:
git add .
git commit -m "feat: add new feature"
Push and create Pull Request:
git push origin feat/my-new-feature
Wait for CI checks to pass (GitHub Actions will run automatically)
*.test.ts in test/ directorydescribe and it blocks with clear descriptionsexpect() APIExample test:
import { describe, it, expect } from 'vitest';
import { xzAsync, unxzAsync } from '../src/lzma.js';
describe('Compression', () => {
it('should compress and decompress data', async () => {
const original = Buffer.from('test data');
const compressed = await xzAsync(original);
const decompressed = await unxzAsync(compressed);
expect(decompressed.equals(original)).toBe(true);
});
});
Releases are automated using @oorabona/release-it-preset:
# Standard release (patch/minor/major based on commits)
pnpm release
# Manual changelog editing
pnpm release:manual
# Hotfix release
pnpm release:hotfix
# Update changelog only (no release)
pnpm changelog:update
For maintainers only. Contributors should submit PRs; maintainers handle releases.
By contributing, you agree that your contributions will be licensed under LGPL-3.0+.
If you find one, feel free to contribute and post a new issue! PR are accepted as well :)
Kudos goes to addaleax for helping me out with C++ stuff !
If you compile with threads, you may see a bunch of warnings about -Wmissing-field-initializers.
This is normal and does not prevent threading from being active and working.
I did not yet figure how to fix this except by masking the warning..
This software is released under LGPL3.0+
FAQs
Native Node.js bindings for liblzma (XZ/LZMA2). Streaming, buffer and async APIs with browser support via WebAssembly. zlib-like API, TypeScript-first, prebuilt binaries for Linux/macOS/Windows.
The npm package node-liblzma receives a total of 895,294 weekly downloads. As such, node-liblzma popularity was classified as popular.
We found that node-liblzma demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.ย It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.

Security News
During a UK cyber test, a Mythos 5 agent used sockpuppets, social engineering, and prompt injection to try to get a maintainer to merge malware.

Company News
Socket is now in the AWS Security Hub Extended plan. Adopt it through AWS, apply committed spend, and block malicious open source packages.