{"id":19738272,"url":"https://github.com/httptoolkit/brotli-wasm","last_synced_at":"2025-05-14T19:09:32.455Z","repository":{"id":40364579,"uuid":"369537548","full_name":"httptoolkit/brotli-wasm","owner":"httptoolkit","description":"A reliable compressor and decompressor for Brotli, supporting node \u0026 browsers via wasm","archived":false,"fork":false,"pushed_at":"2025-02-17T16:31:18.000Z","size":524,"stargazers_count":284,"open_issues_count":15,"forks_count":25,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-05-14T16:57:20.980Z","etag":null,"topics":["brotli","compression","wasm"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/httptoolkit.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-05-21T13:03:59.000Z","updated_at":"2025-05-14T13:16:54.000Z","dependencies_parsed_at":"2024-06-03T19:00:51.140Z","dependency_job_id":"67a5e4dd-dc19-4fdf-90a3-06f7261bd542","html_url":"https://github.com/httptoolkit/brotli-wasm","commit_stats":{"total_commits":53,"total_committers":9,"mean_commits":5.888888888888889,"dds":0.4528301886792453,"last_synced_commit":"b5c095da32a2c646b6e8c1771d83f437c1777e35"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/httptoolkit%2Fbrotli-wasm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/httptoolkit%2Fbrotli-wasm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/httptoolkit%2Fbrotli-wasm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/httptoolkit%2Fbrotli-wasm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/httptoolkit","download_url":"https://codeload.github.com/httptoolkit/brotli-wasm/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254209859,"owners_count":22032897,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["brotli","compression","wasm"],"created_at":"2024-11-12T01:13:44.798Z","updated_at":"2025-05-14T19:09:32.323Z","avatar_url":"https://github.com/httptoolkit.png","language":"TypeScript","readme":"# brotli-wasm [![Build Status](https://github.com/httptoolkit/brotli-wasm/workflows/CI/badge.svg)](https://github.com/httptoolkit/brotli-wasm/actions) [![Available on NPM](https://img.shields.io/npm/v/brotli-wasm.svg)](https://npmjs.com/package/brotli-wasm)\n\n\u003e _Part of [HTTP Toolkit](https://httptoolkit.tech): powerful tools for building, testing \u0026 debugging HTTP(S)_\n\n**A reliable compressor and decompressor for Brotli, supporting node \u0026 browsers via wasm**\n\nBrotli is available in modern Node (12+) but not older Node or browsers. With this package, you can immediately use it everywhere.\n\nThis package contains a tiny wrapper around the compress \u0026 decompress API of the Rust [Brotli crate](https://crates.io/crates/brotli), compiled to wasm with just enough setup to make it easily usable from JavaScript.\n\nThis is battle-tested, in production use in both node \u0026 browsers as part of [HTTP Toolkit](https://httptoolkit.tech/), and includes automated build with node \u0026 browser tests to make sure.\n\n## Getting started\n\n```\nnpm install brotli-wasm\n```\n\nYou should be able to import this directly into Node, as normal, or in a browser using any bundler that supports ES modules \u0026 webassembly (e.g. Webpack v4 or v5, Vite, Rollup, and most others).\n\nFor each target (node.js, commonjs bundlers \u0026 ESM bundlers) this module exports a different WASM file \u0026 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).\n\nIn all builds (after waiting for the exported promise in browsers) the module exposes two core methods:\n\n* `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.\n* `decompress(Buffer)` - decompresses a buffer using Brotli, returning the original raw data.\n\nFor advanced use data-streaming use cases, `CompressStream` and `DecompressStream` classes for streaming compression are also available. See [the tests](https://github.com/httptoolkit/brotli-wasm/blob/main/test/brotli.spec.ts) for example usage.\n\n### Usage\n\nIf you want to support node \u0026 browsers with the same code, you can use the `await` browser-compatible form with the default export everywhere.\n\n#### In node.js:\n\n```javascript\nconst brotli = require('brotli-wasm');\n\nconst compressedData = brotli.compress(Buffer.from('some input'));\nconst decompressedData = brotli.decompress(compressedData);\n\nconsole.log(Buffer.from(decompressedData).toString('utf8')); // Prints 'some input'\n```\n\n#### In browsers:\n\n```javascript\nimport brotliPromise from 'brotli-wasm'; // Import the default export\n\nconst brotli = await brotliPromise; // Import is async in browsers due to wasm requirements!\n\nconst textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder();\n\nconst input = 'some input';\n\nconst uncompressedData = textEncoder.encode(input);\nconst compressedData = brotli.compress(uncompressedData);\nconst decompressedData = brotli.decompress(compressedData);\n\nconsole.log(textDecoder.decode(decompressedData)); // Prints 'some input'\n```\n\nYou can also load it from a CDN like so:\n```javascript\nconst brotli = await import(\"https://unpkg.com/brotli-wasm@3.0.0/index.web.js?module\").then(m =\u003e m.default);\n```\n\nThe package itself has no runtime dependencies, although if you prefer using `Buffer` over using `TextEncoder/TextDecoder` you may want a [browser Buffer polyfill](https://www.npmjs.com/package/browserify-zlib).\n\n##### Using an importmap\n\nIf you've installed `brotli-wasm` as an NPM package, you can load it from your `node_modules` subfolder:\n\n```html\n\u003c!-- index.html --\u003e\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n    \u003chead\u003e\u003c/head\u003e\n    \u003cbody\u003e\n        \u003cscript type=\"importmap\"\u003e\n            {\n                \"imports\": {\n                    \"brotli-wasm\": \"/node_modules/brotli-wasm/index.web.js\"\n                }\n            }\n        \u003c/script\u003e\n        \u003cscript type=\"module\" src=\"/main.js\"\u003e\u003c/script\u003e\n    \u003c/body\u003e\n\u003c/html\u003e\n```\n\n```javascript\n// main.js\nimport brotliPromise from 'brotli-wasm';\nconst brotli = await brotliPromise;\n\nconst input = 'some input';\nconst uncompressedData = new TextEncoder().encode(input);\nconst compressedData = brotli.compress(uncompressedData);\nconst decompressedData = brotli.decompress(compressedData);\nconsole.log(new TextDecoder().decode(decompressedData)); // Prints 'some input'\n```\n\n#### In browser with streams:\n\n```javascript\nimport brotliPromise from 'brotli-wasm'; // Import the default export\n\nconst brotli = await brotliPromise; // Import is async in browsers due to wasm requirements!\n\nconst input = 'some input';\n\n// Get a stream for your input:\nconst inputStream = new ReadableStream({\n    start(controller) {\n        controller.enqueue(input);\n        controller.close();\n    }\n});\n\n// Convert the streaming data to Uint8Arrays, if necessary:\nconst textEncoderStream = new TextEncoderStream();\n\n// You can use whatever stream chunking size you like here, depending on your use case:\nconst OUTPUT_SIZE = 100;\n\n// Create a stream to incrementally compress the data as it streams:\nconst compressStream = new brotli.CompressStream();\nconst compressionStream = new TransformStream({\n    transform(chunk, controller) {\n        let resultCode;\n        let inputOffset = 0;\n\n        // Compress this chunk, producing up to OUTPUT_SIZE output bytes at a time, until the\n        // entire input has been compressed.\n\n        do {\n            const input = chunk.slice(inputOffset);\n            const result = compressStream.compress(input, OUTPUT_SIZE);\n            controller.enqueue(result.buf);\n            resultCode = result.code;\n            inputOffset += result.input_offset;\n        } while (resultCode === brotli.BrotliStreamResultCode.NeedsMoreOutput);\n        if (resultCode !== brotli.BrotliStreamResultCode.NeedsMoreInput) {\n            controller.error(`Brotli compression failed when transforming with code ${resultCode}`);\n        }\n    },\n    flush(controller) {\n        // Once the chunks are finished, flush any remaining data (again in repeated fixed-output\n        // chunks) to finish the stream:\n        let resultCode;\n        do {\n            const result = compressStream.compress(undefined, OUTPUT_SIZE);\n            controller.enqueue(result.buf);\n            resultCode = result.code;\n        } while (resultCode === brotli.BrotliStreamResultCode.NeedsMoreOutput)\n        if (resultCode !== brotli.BrotliStreamResultCode.ResultSuccess) {\n            controller.error(`Brotli compression failed when flushing with code ${resultCode}`);\n        }\n        controller.terminate();\n    }\n});\n\nconst decompressStream = new brotli.DecompressStream();\nconst decompressionStream = new TransformStream({\n    transform(chunk, controller) {\n        let resultCode;\n        let inputOffset = 0;\n\n        // Decompress this chunk, producing up to OUTPUT_SIZE output bytes at a time, until the\n        // entire input has been decompressed.\n\n        do {\n            const input = chunk.slice(inputOffset);\n            const result = decompressStream.decompress(input, OUTPUT_SIZE);\n            controller.enqueue(result.buf);\n            resultCode = result.code;\n            inputOffset += result.input_offset;\n        } while (resultCode === brotli.BrotliStreamResultCode.NeedsMoreOutput);\n        if (\n            resultCode !== brotli.BrotliStreamResultCode.NeedsMoreInput \u0026\u0026\n            resultCode !== brotli.BrotliStreamResultCode.ResultSuccess\n        ) {\n            controller.error(`Brotli decompression failed with code ${resultCode}`)\n        }\n    },\n    flush(controller) {\n        controller.terminate();\n    }\n});\n\nconst textDecoderStream = new TextDecoderStream();\n\nlet output = '';\nconst outputStream = new WritableStream({\n    write(chunk) {\n        output += chunk;\n    }\n});\n\nawait inputStream\n    .pipeThrough(textEncoderStream)\n    .pipeThrough(compressionStream)\n    .pipeThrough(decompressionStream)\n    .pipeThrough(textDecoderStream)\n    .pipeTo(outputStream);\nconsole.log(output); // Prints 'some input'\n```\n\nNote 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.\n\nThis 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.\n\n## Alternatives\n\nThere's a few other packages that do similar things, but I found they were all unusable and/or unmaintained:\n\n* [brotli-dec-wasm](https://www.npmjs.com/package/brotli-dec-wasm) - decompressor only, compiled from Rust just like this package, actively maintained, but no compressor available (by design). **If you only need decompression, this package is a good choice**.\n* [Brotli.js](https://www.npmjs.com/package/brotli) - hand-written JS decompressor that seems to work OK for most cases, but it crashes for some edge cases and the emscripten build of the compressor doesn't work in browsers at all. Last updated in 2017.\n* [wasm-brotli](https://www.npmjs.com/package/wasm-brotli) - Compiled from Rust like this package, includes decompressor \u0026 compressor, but requires a custom async wrapper for Webpack v4 usage and isn't usable at all in Webpack v5. Last updated in 2019.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhttptoolkit%2Fbrotli-wasm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhttptoolkit%2Fbrotli-wasm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhttptoolkit%2Fbrotli-wasm/lists"}