Gathering detailed insights and metrics for brotli-wasm
Gathering detailed insights and metrics for brotli-wasm
Gathering detailed insights and metrics for brotli-wasm
Gathering detailed insights and metrics for brotli-wasm
A reliable compressor and decompressor for Brotli, supporting node & browsers via wasm
npm install brotli-wasm
Module System
Min. Node Version
Typescript Support
Node Version
NPM Version
264 Stars
89 Commits
21 Forks
6 Watching
4 Branches
12 Contributors
Updated on 07 Nov 2024
TypeScript (60.23%)
Rust (25.35%)
JavaScript (14.42%)
Cumulative downloads
Total Downloads
Last day
-1.8%
36,100
Compared to previous day
Last week
4.4%
199,360
Compared to previous week
Last month
29.2%
783,973
Compared to previous month
Last year
35.3%
5,515,813
Compared to previous year
29
Part of HTTP Toolkit: powerful tools for building, testing & debugging HTTP(S)
A reliable compressor and decompressor for Brotli, supporting node & browsers via wasm
Brotli is available in modern Node (12+) but not older Node or browsers. With this package, you can immediately use it everywhere.
This package contains a tiny wrapper around the compress & decompress API of the Rust Brotli crate, compiled to wasm with just enough setup to make it easily usable from JavaScript.
This is battle-tested, in production use in both node & browsers as part of HTTP Toolkit, and includes automated build with node & browser tests to make sure.
npm install brotli-wasm
You should be able to import this directly into Node, as normal, or in a browser using any bundler that supports ES modules & webassembly (e.g. Webpack v4 or v5, Vite, Rollup, and most others).
For each target (node.js, commonjs bundlers & ESM bundlers) this module exports a different WASM file & setup, with a slightly different entrypoint. These entrypoints all expose a consistent default-export API, in addition to some other exports that may vary (e.g. Node exposes the brotli methods synchronously, while browsers always require an await
due to WASM limitations).
In all builds (after waiting for the exported promise in browsers) the module exposes two core methods:
compress(Buffer, [options])
- compresses a buffer using Brotli, returning the compressed buffer. An optional options object can be provided. The only currently supported option is quality
: a number between 1 and 11.decompress(Buffer)
- decompresses a buffer using Brotli, returning the original raw data.For advanced use data-streaming use cases, CompressStream
and DecompressStream
classes for streaming compression are also available. See the tests for example usage.
If you want to support node & browsers with the same code, you can use the await
browser-compatible form with the default export everywhere.
1const brotli = require('brotli-wasm'); 2 3const compressedData = brotli.compress(Buffer.from('some input')); 4const decompressedData = brotli.decompress(compressedData); 5 6console.log(Buffer.from(decompressedData).toString('utf8')); // Prints 'some input'
1import brotliPromise from 'brotli-wasm'; // Import the default export 2 3const brotli = await brotliPromise; // Import is async in browsers due to wasm requirements! 4 5const textEncoder = new TextEncoder(); 6const textDecoder = new TextDecoder(); 7 8const input = 'some input'; 9 10const uncompressedData = textEncoder.encode(input); 11const compressedData = brotli.compress(uncompressedData); 12const decompressedData = brotli.decompress(compressedData); 13 14console.log(textDecoder.decode(decompressedData)); // Prints 'some input'
You can also load it from a CDN like so:
1const brotli = await import("https://unpkg.com/brotli-wasm@3.0.0/index.web.js?module").then(m => m.default);
The package itself has no runtime dependencies, although if you prefer using Buffer
over using TextEncoder/TextDecoder
you may want a browser Buffer polyfill.
If you've installed brotli-wasm
as an NPM package, you can load it from your node_modules
subfolder:
1<!-- index.html --> 2<!DOCTYPE html> 3<html lang="en"> 4 <head></head> 5 <body> 6 <script type="importmap"> 7 { 8 "imports": { 9 "brotli-wasm": "/node_modules/brotli-wasm/index.web.js" 10 } 11 } 12 </script> 13 <script type="module" src="/main.js"></script> 14 </body> 15</html>
1// main.js 2import brotliPromise from 'brotli-wasm'; 3const brotli = await brotliPromise; 4 5const input = 'some input'; 6const uncompressedData = new TextEncoder().encode(input); 7const compressedData = brotli.compress(uncompressedData); 8const decompressedData = brotli.decompress(compressedData); 9console.log(new TextDecoder().decode(decompressedData)); // Prints 'some input'
1import brotliPromise from 'brotli-wasm'; // Import the default export 2 3const brotli = await brotliPromise; // Import is async in browsers due to wasm requirements! 4 5const input = 'some input'; 6 7// Get a stream for your input: 8const inputStream = new ReadableStream({ 9 start(controller) { 10 controller.enqueue(input); 11 controller.close(); 12 } 13}); 14 15// Convert the streaming data to Uint8Arrays, if necessary: 16const textEncoderStream = new TextEncoderStream(); 17 18// You can use whatever stream chunking size you like here, depending on your use case: 19const OUTPUT_SIZE = 100; 20 21// Create a stream to incrementally compress the data as it streams: 22const compressStream = new brotli.CompressStream(); 23const compressionStream = new TransformStream({ 24 transform(chunk, controller) { 25 let resultCode; 26 let inputOffset = 0; 27 28 // Compress this chunk, producing up to OUTPUT_SIZE output bytes at a time, until the 29 // entire input has been compressed. 30 31 do { 32 const input = chunk.slice(inputOffset); 33 const result = compressStream.compress(input, OUTPUT_SIZE); 34 controller.enqueue(result.buf); 35 resultCode = result.code; 36 inputOffset += result.input_offset; 37 } while (resultCode === brotli.BrotliStreamResultCode.NeedsMoreOutput); 38 if (resultCode !== brotli.BrotliStreamResultCode.NeedsMoreInput) { 39 controller.error(`Brotli compression failed when transforming with code ${resultCode}`); 40 } 41 }, 42 flush(controller) { 43 // Once the chunks are finished, flush any remaining data (again in repeated fixed-output 44 // chunks) to finish the stream: 45 let resultCode; 46 do { 47 const result = compressStream.compress(undefined, OUTPUT_SIZE); 48 controller.enqueue(result.buf); 49 resultCode = result.code; 50 } while (resultCode === brotli.BrotliStreamResultCode.NeedsMoreOutput) 51 if (resultCode !== brotli.BrotliStreamResultCode.ResultSuccess) { 52 controller.error(`Brotli compression failed when flushing with code ${resultCode}`); 53 } 54 controller.terminate(); 55 } 56}); 57 58const decompressStream = new brotli.DecompressStream(); 59const decompressionStream = new TransformStream({ 60 transform(chunk, controller) { 61 let resultCode; 62 let inputOffset = 0; 63 64 // Decompress this chunk, producing up to OUTPUT_SIZE output bytes at a time, until the 65 // entire input has been decompressed. 66 67 do { 68 const input = chunk.slice(inputOffset); 69 const result = decompressStream.decompress(input, OUTPUT_SIZE); 70 controller.enqueue(result.buf); 71 resultCode = result.code; 72 inputOffset += result.input_offset; 73 } while (resultCode === brotli.BrotliStreamResultCode.NeedsMoreOutput); 74 if ( 75 resultCode !== brotli.BrotliStreamResultCode.NeedsMoreInput && 76 resultCode !== brotli.BrotliStreamResultCode.ResultSuccess 77 ) { 78 controller.error(`Brotli decompression failed with code ${resultCode}`) 79 } 80 }, 81 flush(controller) { 82 controller.terminate(); 83 } 84}); 85 86const textDecoderStream = new TextDecoderStream(); 87 88let output = ''; 89const outputStream = new WritableStream({ 90 write(chunk) { 91 output += chunk; 92 } 93}); 94 95await inputStream 96 .pipeThrough(textEncoderStream) 97 .pipeThrough(compressionStream) 98 .pipeThrough(decompressionStream) 99 .pipeThrough(textDecoderStream) 100 .pipeTo(outputStream); 101console.log(output); // Prints 'some input'
Note that TransformStream
has become available in all browsers as of mid-2022: https://caniuse.com/mdn-api_transformstream. It's also been available in Node.js (experimentally) since v16.5.0.
This is a simplified demo example - you may well want to tweak the specific stream buffer sizes for compression/decompression to your use case, to reuse buffers, or explore further optimizations if you're interested in these streaming use cases.
There's a few other packages that do similar things, but I found they were all unusable and/or unmaintained:
No vulnerabilities found.
Reason
no dangerous workflow patterns detected
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
dependency not pinned by hash detected -- score normalized to 4
Details
Reason
Found 6/19 approved changesets -- score normalized to 3
Reason
0 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 1
Reason
detected GitHub workflow tokens with excessive permissions
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
branch protection not enabled on development/release branches
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Reason
24 existing vulnerabilities detected
Details
Score
Last Scanned on 2024-11-25
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More