defuss-brotli 0.2.0 → 0.3.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.
- package/CHANGELOG.md +34 -0
- package/README.md +97 -13
- package/dist/cli.js +37 -0
- package/dist/compressor-core.d.ts +63 -4
- package/dist/compressor-encode.d.ts +16 -0
- package/dist/compressor-slim.d.ts +1 -1
- package/dist/compressor-slim.js +1 -1
- package/dist/compressor.d.ts +1 -1
- package/dist/compressor.js +1 -1
- package/dist/compressor.wasm +0 -0
- package/dist/decompressor-slim.js +1 -1
- package/dist/decompressor.js +1 -1
- package/dist/wasm.d.ts +8 -2
- package/dist/workers.d.ts +27 -0
- package/package.json +6 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `defuss-brotli`. Versions follow [Semantic Versioning](https://semver.org/); before 1.0.0, a minor version may change behavior.
|
|
4
|
+
|
|
5
|
+
## 0.3.0 - 2026-11-09
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- **The default compression quality is now 11 instead of 6**, the default of Node.js zlib (`BROTLI_DEFAULT_QUALITY`) and of the `brotli` CLI. `compress()` and `compressText()` calls that pass no `quality` now return the smallest output and take longer: about 3.8 s instead of 0.15 s for a 5.1 MB SVG (Node.js 26). Pass `{ quality: 6 }` to keep the previous output.
|
|
10
|
+
- The compressor entry points embed the worker script of `compressParallel`: `compressor-slim.js` grows from 4.8 KB to 15.2 KB and `compressor.wasm` by 11.5 KB (raw). The decompressor entry points grow by 29 B.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `compressParallel(input, options)` compresses up to 8 parts of the input at once on workers, each with its own instance of the compressor, and joins them into one standard Brotli stream. At quality 11 it is 2.3 to 4.3 times faster than `compress` from 256 KiB, with output 0.01 to 0.10 % larger (Node.js 26). The output depends only on the input, `quality` and `lgwin`, not on `threads` or the machine. It also exports `BrotliParallelOptions`, `PARALLEL_PARTS` and `MIN_PART_SIZE`.
|
|
15
|
+
- The `defuss-brotli` command (`bunx defuss-brotli compress FILE`, `bunx defuss-brotli decompress FILE.br`) takes the options, defaults, output names and refusals of the `brotli` 1.2.0 CLI. It compresses with `compressParallel`; `--threads=NUM` sets the number of workers. From 256 KiB it compresses 1.2 to 5.3 times faster than `brotli` 1.2.0, depending on the input and runtime. `--large_window`, `--comment`, `--dictionary` and `--concatenated` are not supported.
|
|
16
|
+
- `CHANGELOG.md`, shipped in the package.
|
|
17
|
+
|
|
18
|
+
## 0.2.0 - 2026-11-09
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- `/slim` entry points, `defuss-brotli/compressor/slim` and `defuss-brotli/decompressor/slim`, with the same API as the inline ones. They load the binary from a separate `.wasm` file (`defuss-brotli/compressor.wasm`, `defuss-brotli/decompressor.wasm`): by default from next to the module, or from a URL, a `Response`, bytes or a compiled `WebAssembly.Module` passed to `init()`.
|
|
23
|
+
- `THIRD_PARTY_NOTICES.txt` with the license texts of the code compiled into the `.wasm` files.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- Both modules stream input and output through fixed chunks in WASM memory, so linear memory no longer grows with the payload: it holds 19.6 MiB after decoding 100 MiB, and 69 MiB after compressing it at quality 6.
|
|
28
|
+
- The compressor feeds the encoder one window per call instead of 4 KiB, so qualities 0 and 1 find repeats across chunks (a 28 KiB page at quality 1: 1,189 B instead of 3,853 B); the output equals one encoder call over the whole input at every quality.
|
|
29
|
+
- The decoder is built without the Rust standard library, which brings `defuss-brotli/decompressor/slim` to about 51 KB with Brotli quality 11 (85 KB from jsDelivr).
|
|
30
|
+
- `bun run build`, and with it `npm publish`, finds the pinned build tools without an activated mise.
|
|
31
|
+
|
|
32
|
+
## 0.1.1 to 0.1.3
|
|
33
|
+
|
|
34
|
+
These releases predate this repository's history.
|
package/README.md
CHANGED
|
@@ -9,6 +9,8 @@ A split pure-Rust Brotli package for Node.js + browsers.
|
|
|
9
9
|
- **Pure-Rust WebAssembly** - no native addons
|
|
10
10
|
- **Typed TypeScript API** with full JSDoc
|
|
11
11
|
- **Vitest coverage** in Node and browser
|
|
12
|
+
- **Parallel compression** - `compressParallel()` compresses parts of the input on workers: 2.3 to 4.3 times faster at quality 11 from 256 KiB, with output at most 0.1 % larger
|
|
13
|
+
- **`defuss-brotli` command** with the options of the `brotli` CLI, `bunx defuss-brotli compress FILE`; from 256 KiB it compresses 1.2 to 5.3 times faster than `brotli` 1.2.0
|
|
12
14
|
|
|
13
15
|
## Install
|
|
14
16
|
|
|
@@ -39,6 +41,64 @@ const text = decompressText(compressed);
|
|
|
39
41
|
// returns "Hello, Brotli!"
|
|
40
42
|
```
|
|
41
43
|
|
|
44
|
+
### Command line
|
|
45
|
+
|
|
46
|
+
The package ships a `defuss-brotli` command with the options of the `brotli` CLI, so it can replace `brotli` in build scripts and on machines where only Node.js or Bun is installed:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
bunx defuss-brotli compress dist/app.js # writes dist/app.js.br, keeps dist/app.js
|
|
50
|
+
bunx defuss-brotli decompress dist/app.js.br -o app.js
|
|
51
|
+
bunx defuss-brotli -q 5 -v *.html *.css # brotli's spelling: compress by default
|
|
52
|
+
bunx defuss-brotli -d -c page.html.br | head # -d decompresses, -c writes to stdout
|
|
53
|
+
cat data.json | bunx defuss-brotli > data.json.br
|
|
54
|
+
bunx defuss-brotli -t data.json.br # test integrity, write nothing
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`compress` and `decompress` as the first argument select the mode; without one the command compresses unless `-d` or `-t` is given, as `brotli` does. It compresses with [`compressParallel`](#compressparallel), so inputs above 32 KiB are split into parts that compress on several workers. `npx defuss-brotli` works the same way. The command runs on Node.js when it is on the `PATH` and on Bun otherwise; `bunx --bun defuss-brotli` always uses Bun, which starts faster.
|
|
58
|
+
|
|
59
|
+
| Option | Meaning |
|
|
60
|
+
|---|---|
|
|
61
|
+
| `-0` to `-9`, `-q NUM`, `--quality=NUM`, `-Z`, `--best` | quality 0 to 11, default 11 (`-Z`) |
|
|
62
|
+
| `-w NUM`, `--lgwin=NUM` | window 10 to 24; `0` (default) picks the smallest window that holds the input |
|
|
63
|
+
| `-c`, `--stdout` | write to standard output |
|
|
64
|
+
| `-o FILE`, `--output=FILE` | output file (only with one input) |
|
|
65
|
+
| `-d`, `--decompress` / `-t`, `--test` | decompress / decompress without writing, to check integrity |
|
|
66
|
+
| `-f`, `--force` | overwrite existing output, allow terminal input and output |
|
|
67
|
+
| `-k`, `--keep` / `-j`, `--rm` | keep (default) / delete each source after writing its output |
|
|
68
|
+
| `-s`, `--squash` | when compressing, drop an output that is larger than its source |
|
|
69
|
+
| `-n`, `--no-copy-stat` | do not copy the source's mode and timestamps to the output |
|
|
70
|
+
| `-S SUF`, `--suffix=SUF` | suffix to add or strip, default `.br` |
|
|
71
|
+
| `--threads=NUM` | compress on up to `NUM` workers, default the number of CPUs, at most 8; the output is the same for every `NUM` (not a `brotli` option) |
|
|
72
|
+
| `-v`, `--verbose` / `-h`, `--help` / `-V`, `--version` | per-file sizes and time / usage / version |
|
|
73
|
+
|
|
74
|
+
With no file, or with `-`, it reads standard input and writes standard output. Short flags combine (`-9kf`) and `--` ends the options. Each input is written to `FILE.br` or, when decompressing, to `FILE` without `.br`. The command refuses to overwrite a file without `-f`, to write compressed bytes to a terminal, and to decompress a name that lacks the suffix. It stops at the first file that fails and exits with status 1.
|
|
75
|
+
|
|
76
|
+
VERIFIED (2026-11-09, against brotli 1.2.0 on macOS):
|
|
77
|
+
|
|
78
|
+
- The defaults, output file names and refusals match `brotli`'s. Messages keep its wording, with the error text of Node.js or Bun (`file already exists` where `brotli` prints `File exists`).
|
|
79
|
+
- The default output for `input/page.html` and `input/data.json` is byte-identical to `brotli`'s.
|
|
80
|
+
- On larger inputs, which it compresses in parts, the default output was 0.00 to 0.16 % larger than `brotli`'s: 64 KiB and 1 MiB of SVG, a 64 KiB PNG head, a 2.1 MB PNG and a 5.1 MB SVG.
|
|
81
|
+
|
|
82
|
+
Some `brotli` behavior is not supported or differs:
|
|
83
|
+
|
|
84
|
+
- `--large_window`, `-C`/`--comment`, `-D`/`--dictionary` and `-K`/`--concatenated` fail with a reason, because the WASM encoder and decoder do not implement them.
|
|
85
|
+
- Usage errors print a one-line hint instead of the whole help text.
|
|
86
|
+
- `-t -j` is refused; `brotli` 1.2.0 deletes the file it has just tested.
|
|
87
|
+
- Each input is read into memory whole and each output is written at once, so files must fit in memory; decompressed output is limited to 2^32 − 1 bytes, the wasm32 limit.
|
|
88
|
+
|
|
89
|
+
At the default quality 11, the command compresses 64 KiB as fast as `brotli` 1.2.0 on Bun, and from 256 KiB 2.3 to 5.3 times faster on Bun and 1.2 to 5.1 times faster on Node.js. Smaller files take longer, because it first starts a JavaScript runtime and compiles the WASM. Median of 5 interleaved runs on Apple Silicon (18 cores), startup included, Node.js 26 and Bun 1.4:
|
|
90
|
+
|
|
91
|
+
| Input | `defuss-brotli` on Node.js | on Bun (`bunx --bun`) | `brotli` 1.2.0 |
|
|
92
|
+
|---|---|---|---|
|
|
93
|
+
| 29 KB HTML (`input/page.html`), one part | 164 ms | 89 ms | 55 ms |
|
|
94
|
+
| 64 KiB of SVG, 2 parts | 174 ms | 68 ms | 69 ms |
|
|
95
|
+
| 256 KiB of SVG, 8 parts | 178 ms | 89 ms | 219 ms |
|
|
96
|
+
| 1 MiB of SVG, 8 parts | 320 ms | 301 ms | 879 ms |
|
|
97
|
+
| 2.1 MB PNG, 8 parts | 740 ms | 714 ms | 3.79 s |
|
|
98
|
+
| 5.1 MB SVG, 8 parts | 1.89 s | 1.71 s | 4.00 s |
|
|
99
|
+
|
|
100
|
+
The SVG inputs are the start of the 5.1 MB file. Decompressing the 5.1 MB SVG took 93 ms on Node.js and 55 ms on Bun, against 32 ms for `brotli` (median of 3). Startup (`-V`) took 59 ms on Node.js and 24 ms on Bun.
|
|
101
|
+
|
|
42
102
|
### Inlined or separate `.wasm`
|
|
43
103
|
|
|
44
104
|
Each module comes in two builds with the same API. The default entry points embed the WASM binary as base64, so one JavaScript file is all a runtime or bundler needs. The `/slim` entry points leave the binary in its own `.wasm` file, which HTTP caches independently of your JavaScript, which compiles while it downloads when served as `application/wasm`, and which transfers 21 to 45 % fewer bytes depending on module and transfer encoding (see [Download sizes and budgets](#download-sizes-and-budgets)).
|
|
@@ -86,6 +146,30 @@ With an explicit import, the bundler copies the file and gives it a hashed name,
|
|
|
86
146
|
raw bytes. `TextEncoder` converts to canonical UTF-8 - the standard encoding for
|
|
87
147
|
web text. `compressText` handles this so you don't have to.
|
|
88
148
|
|
|
149
|
+
### `compressParallel`
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import { init, compressParallel } from "defuss-brotli/compressor";
|
|
153
|
+
|
|
154
|
+
await init();
|
|
155
|
+
const compressed = await compressParallel(bytes, { quality: 11, threads: 4 });
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`compressParallel` splits the input into at most 8 parts of at least 32 KiB, compresses each part on a worker with its own instance of the compressor, and joins the parts into one standard Brotli stream on the calling thread. Each part gets the window of input before it as a dictionary, so matches still reach back across parts. This is the multithreaded mode of the `brotli` crate, run on workers instead of threads.
|
|
159
|
+
|
|
160
|
+
- **Output:** it depends only on the input, `quality` and `lgwin`, never on `threads` or the number of CPUs; `threads: 1` compresses the parts one after another on the calling thread and returns the same bytes. Below quality 2, or when the input fits in one part, it returns exactly what `compress` returns. Otherwise the output differs from `compress`'s and is slightly larger.
|
|
161
|
+
- **Workers:** Web Workers from a Blob URL in browsers and Bun, and `worker_threads` in Node.js. Pages with a Content Security Policy must allow `worker-src blob:`. No extra file is needed, because the worker script is embedded in the module. The workers are terminated before the Promise settles. A runtime that cannot start such a worker compresses the parts on the calling thread.
|
|
162
|
+
- **`threads`:** how many workers run at once, default the number of logical CPUs, at most 8. Each worker allocates its own encoder state, as a `compress` call does.
|
|
163
|
+
|
|
164
|
+
VERIFIED on Node.js 26 (Apple Silicon, quality 11, window 22, median of 3 in-process runs, first call):
|
|
165
|
+
|
|
166
|
+
| Input | `compress` | `compressParallel` | Size |
|
|
167
|
+
|---|---|---|---|
|
|
168
|
+
| 256 KiB of SVG | 187 ms | 44 ms | +0.01 % |
|
|
169
|
+
| 1 MiB of SVG | 617 ms | 189 ms | +0.05 % |
|
|
170
|
+
| 2.1 MB PNG | 1.79 s | 0.60 s | +0.10 % |
|
|
171
|
+
| 5.1 MB SVG | 3.82 s | 1.67 s | +0.09 % |
|
|
172
|
+
|
|
89
173
|
### `decompress` vs `decompressText`
|
|
90
174
|
|
|
91
175
|
| Function | Output | Decoding | When to use |
|
|
@@ -100,7 +184,7 @@ valid UTF-8, replacement characters (U+FFFD) are inserted rather than throwing.
|
|
|
100
184
|
|
|
101
185
|
```ts
|
|
102
186
|
interface BrotliCompressOptions {
|
|
103
|
-
quality?: number; // 0-11, default
|
|
187
|
+
quality?: number; // 0-11, default 11
|
|
104
188
|
lgwin?: number; // 10-24, default 22
|
|
105
189
|
}
|
|
106
190
|
```
|
|
@@ -138,6 +222,7 @@ Hard cap on decompressed output to prevent **decompression bombs**. Raise explic
|
|
|
138
222
|
- `quality` must be an integer in **0-11** (the range of the reference Brotli encoder).
|
|
139
223
|
- `lgwin` must be an integer in **10-24** (the window range of the Brotli format, RFC 7932).
|
|
140
224
|
- `maxOutputSize` defaults to **64 MiB** - the WASM module rejects output exceeding this. Values above 2^32 − 1 act as 2^32 − 1.
|
|
225
|
+
- `threads` must be a positive integer. `compressParallel` copies each part and its dictionary (up to one window) to its worker, so it holds up to 8 windows more than the input in JavaScript memory while it runs.
|
|
141
226
|
- Calls stream the input and output through chunks of at most 1 MiB (one window of input when compressing), so WASM memory holds the codec state and two chunks however large the payload; the returned `Uint8Array` lives in JavaScript memory. Truncated input, corrupt input and bytes after the end of the Brotli stream throw.
|
|
142
227
|
|
|
143
228
|
## Why the Split Exports?
|
|
@@ -150,10 +235,10 @@ What a browser downloads for one import, with each file transfer-compressed on i
|
|
|
150
235
|
|
|
151
236
|
| Import | Files | raw | gzip -9 | brotli q4 (jsDelivr) | brotli q11 | zstd -19 |
|
|
152
237
|
|---|---|---:|---:|---:|---:|---:|
|
|
153
|
-
| `defuss-brotli/decompressor/slim` | `decompressor-slim.js` + `decompressor.wasm` | 190,
|
|
154
|
-
| `defuss-brotli/decompressor` | `decompressor.js` | 252,
|
|
155
|
-
| `defuss-brotli/compressor/slim` | `compressor-slim.js` + `compressor.wasm` |
|
|
156
|
-
| `defuss-brotli/compressor` | `compressor.js` | 1,
|
|
238
|
+
| `defuss-brotli/decompressor/slim` | `decompressor-slim.js` + `decompressor.wasm` | 190,289 B | 85,633 B | 85,090 B | 50,828 B | 78,609 B |
|
|
239
|
+
| `defuss-brotli/decompressor` | `decompressor.js` | 252,213 B | 110,006 B | 111,536 B | 92,996 B | 101,435 B |
|
|
240
|
+
| `defuss-brotli/compressor/slim` | `compressor-slim.js` + `compressor.wasm` | 960,020 B | 453,582 B | 510,561 B | 396,565 B | 405,487 B |
|
|
241
|
+
| `defuss-brotli/compressor` | `compressor.js` | 1,275,038 B | 608,203 B | 663,816 B | 499,177 B | 524,736 B |
|
|
157
242
|
|
|
158
243
|
- **gzip -9** and **brotli q4** are what [jsDelivr](https://www.jsdelivr.com/) sends: measured on 2026-11-09, its brotli responses match quality 4 byte for byte and its gzip responses lie within 0.1 % of levels 6 to 9. jsDelivr serves `.wasm` files as `application/wasm` with `access-control-allow-origin: *`, and serves no zstd.
|
|
159
244
|
- **brotli q11** and **zstd -19** are what precompressed static hosting reaches (for example `.br` files next to the assets, or a CDN that compresses at its highest level). Quality 11 is unstable on `compressor.wasm`: passes of `wasm-opt` that change its raw size and its quality-4 size by 0.2 % move it between 340,693 B and 392,638 B, so its budget allows the upper value.
|
|
@@ -294,13 +379,12 @@ Sample output:
|
|
|
294
379
|
|
|
295
380
|
## Defaults
|
|
296
381
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
- `quality = 6`
|
|
382
|
+
- `quality = 11`
|
|
300
383
|
- `lgwin = 22`
|
|
301
384
|
|
|
302
|
-
|
|
303
|
-
|
|
385
|
+
These are the defaults of Node.js zlib (`BROTLI_DEFAULT_QUALITY`, `BROTLI_DEFAULT_WINDOW`), and quality 11 gives the smallest output. Pass a lower `quality` when compression time matters more than size, for example when compressing each response as it is served.
|
|
386
|
+
|
|
387
|
+
The `defuss-brotli` command also uses quality 11 and, like the `brotli` CLI, the smallest window that holds the input.
|
|
304
388
|
|
|
305
389
|
## Build
|
|
306
390
|
|
|
@@ -322,21 +406,21 @@ What happens during build, for each of the two crates:
|
|
|
322
406
|
3. The binary is written as a base64 module next to the wasm-bindgen glue.
|
|
323
407
|
4. `bun build` bundles `src/compressor.ts` or `src/decompressor.ts` with the glue and the base64 module into `dist/`.
|
|
324
408
|
|
|
325
|
-
`
|
|
409
|
+
`bun build` then bundles the command, `src/bin.ts`, into `dist/cli.js`, and `tsc` generates the declaration files.
|
|
326
410
|
|
|
327
411
|
## Test and Benchmark
|
|
328
412
|
|
|
329
413
|
```bash
|
|
330
414
|
make test # Rust unit tests, then Vitest in Node and in headless Chromium
|
|
331
415
|
make coverage # Rust line coverage (fails below 60 %), then the TypeScript coverage table
|
|
332
|
-
make e2e # packs the tarball into clean projects: Node.js, Bun, a bun build app and a Vite app (build + dev) in Chromium
|
|
416
|
+
make e2e # packs the tarball into clean projects: Node.js, Bun, the CLI via bunx, a bun build app and a Vite app (build + dev) in Chromium
|
|
333
417
|
make lint # oxlint, tsc, clippy, rustfmt
|
|
334
418
|
make verify # all of the above, as CI runs it
|
|
335
419
|
make bench # Vitest bench and the 100 MB benchmark in Node, Bun and Chromium, against the perf budgets
|
|
336
420
|
bun run example # quality comparison table
|
|
337
421
|
```
|
|
338
422
|
|
|
339
|
-
`make e2e` writes one result file per consumer to `output/` (`e2e-node.json`, `e2e-bun.json`, `e2e-bun-bundle.json`, `e2e-vite-build.json`, `e2e-vite-dev.json`); `make e2e-runtime`, `make e2e-bun` and `make e2e-vite` run one group.
|
|
423
|
+
`make e2e` writes one result file per consumer to `output/` (`e2e-node.json`, `e2e-bun.json`, `e2e-cli.json`, `e2e-bun-bundle.json`, `e2e-vite-build.json`, `e2e-vite-dev.json`); `make e2e-runtime`, `make e2e-cli`, `make e2e-bun` and `make e2e-vite` run one group.
|
|
340
424
|
|
|
341
425
|
## License
|
|
342
426
|
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var De=Object.defineProperty;var Ne=(e)=>e;function qe(e,t){this[e]=Ne.bind(null,t)}var le=(e,t)=>{for(var r in t)De(e,r,{get:t[r],enumerable:!0,configurable:!0,set:qe.bind(t,r)})};var Z={};le(Z,{Encoder:()=>y,Joiner:()=>k,__wbg_Error_83742b46f01ce22d:()=>Ve,__wbg___wbindgen_throw_6ddd609b62940d55:()=>Ye,__wbg_set_wasm:()=>Qe});var ne={};le(ne,{Decoder:()=>S,__wbg_Error_83742b46f01ce22d:()=>at,__wbg___wbindgen_throw_6ddd609b62940d55:()=>ut,__wbg_set_wasm:()=>mt});import{readFileSync as ue}from"node:fs";import{chmod as gt,readFile as ht,stat as At,unlink as xt,utimes as vt,writeFile as kt}from"node:fs/promises";import{basename as Ut,resolve as Be}from"node:path";import{parseArgs as $e}from"node:util";class b extends Error{}var de=`Usage: defuss-brotli [compress|decompress] [OPTION]... [FILE]...
|
|
3
|
+
Compress (default) or decompress FILEs with Brotli; the options follow the brotli CLI.
|
|
4
|
+
Options:
|
|
5
|
+
-# compression level (0-9)
|
|
6
|
+
-c, --stdout write on standard output
|
|
7
|
+
-d, --decompress decompress (same as the decompress command)
|
|
8
|
+
-f, --force force output file overwrite and terminal input/output
|
|
9
|
+
-h, --help display this help and exit
|
|
10
|
+
-j, --rm remove source file(s)
|
|
11
|
+
-s, --squash remove destination file if larger than source
|
|
12
|
+
-k, --keep keep source file(s) (default)
|
|
13
|
+
-n, --no-copy-stat do not copy source file(s) mode and timestamps
|
|
14
|
+
-o FILE, --output=FILE output file (only if 1 input file)
|
|
15
|
+
-q NUM, --quality=NUM compression level (0-11)
|
|
16
|
+
-t, --test test compressed file integrity
|
|
17
|
+
-v, --verbose verbose mode
|
|
18
|
+
-w NUM, --lgwin=NUM set LZ77 window size (0, 10-24)
|
|
19
|
+
window size = 2**NUM - 16
|
|
20
|
+
0 lets compressor choose the optimal value
|
|
21
|
+
-S SUF, --suffix=SUF output file suffix (default:'.br')
|
|
22
|
+
--threads=NUM compress on up to NUM workers (default: CPUs, at most 8);
|
|
23
|
+
the output is the same for every NUM
|
|
24
|
+
-V, --version display version and exit
|
|
25
|
+
-Z, --best use best compression level (11) (default)
|
|
26
|
+
Simple options could be coalesced, i.e. '-9kf' is equivalent to '-9 -k -f'.
|
|
27
|
+
With no FILE, or when FILE is -, read standard input.
|
|
28
|
+
All arguments after '--' are treated as files.
|
|
29
|
+
Not supported: --large_window, -C/--comment, -D/--dictionary, -K/--concatenated.
|
|
30
|
+
`,_e=Object.fromEntries(Array.from({length:10},(e,t)=>[String(t),{type:"boolean",short:String(t)}])),ze={..._e,stdout:{type:"boolean",short:"c"},decompress:{type:"boolean",short:"d"},force:{type:"boolean",short:"f"},help:{type:"boolean",short:"h"},rm:{type:"boolean",short:"j"},squash:{type:"boolean",short:"s"},keep:{type:"boolean",short:"k"},"no-copy-stat":{type:"boolean",short:"n"},output:{type:"string",short:"o"},quality:{type:"string",short:"q"},test:{type:"boolean",short:"t"},verbose:{type:"boolean",short:"v"},lgwin:{type:"string",short:"w"},suffix:{type:"string",short:"S"},version:{type:"boolean",short:"V"},best:{type:"boolean",short:"Z"},threads:{type:"string"},large_window:{type:"string"},comment:{type:"string",short:"C"},dictionary:{type:"string",short:"D"},concatenated:{type:"boolean",short:"K"}},pe={large_window:"large-window streams are not RFC 7932 Brotli",comment:"the encoder cannot embed stream comments",dictionary:"the encoder and decoder take no custom dictionary",concatenated:"the decoder accepts exactly one stream per input"};function z(e,t,r,n){let o=/^\d+$/.test(t)?Number(t):NaN;if(!r(o))throw new b(`invalid ${e} [${t}]: expected ${n}`);return o}function fe(e){let t=e[0]==="compress"||e[0]==="decompress"?e[0]:void 0,r;try{({tokens:r}=$e({args:t?e.slice(1):e,options:ze,allowPositionals:!0,tokens:!0}))}catch(s){throw new b(s.message.split(/\.\s/)[0])}let n={mode:t??"compress",files:[],stdout:!1,output:void 0,force:!1,remove:!1,squash:!1,copyStat:!0,verbose:!1,quality:11,lgwin:0,suffix:".br",threads:void 0},o=!1,i=!1;for(let s of r){if(s.kind==="positional")n.files.push(s.value);if(s.kind!=="option")continue;let c=s.value??"";if(s.name in pe)throw new b(`${s.rawName} is not supported: ${pe[s.name]}`);if(s.name in _e)n.quality=Number(s.name);switch(s.name){case"quality":n.quality=z("quality",c,(a)=>a<=11,"an integer from 0 to 11");break;case"best":n.quality=11;break;case"lgwin":n.lgwin=z("lgwin",c,(a)=>a===0||a>=10&&a<=24,"0 or an integer from 10 to 24");break;case"decompress":case"test":if(t==="compress")throw new b(`${s.rawName} conflicts with the compress command`);if(s.name==="test")n.mode="test";else if(n.mode!=="test")n.mode="decompress";break;case"stdout":n.stdout=!0;break;case"output":n.output=c;break;case"force":n.force=!0;break;case"rm":n.remove=!0;break;case"keep":n.remove=!1;break;case"squash":n.squash=!0;break;case"no-copy-stat":n.copyStat=!1;break;case"verbose":n.verbose=!0;break;case"threads":n.threads=z("threads",c,(a)=>a>=1,"a positive integer");break;case"suffix":if(!c)throw new b("the suffix must not be empty");n.suffix=c;break;case"help":o=!0;break;case"version":i=!0}}if(o)return{kind:"help"};if(i)return{kind:"version"};if(n.files.length===0)n.files.push("-");if(n.files.filter((s)=>s==="-").length>1)throw new b("standard input (-) can be read only once");if(n.files.length>1&&n.mode!=="test"&&(n.stdout||n.output!==void 0))throw new b("-c and -o take exactly one input file");if(n.mode==="test"&&n.remove)throw new b("-j cannot be combined with -t");return{kind:"run",options:n}}function me(e){let t=10;while(t<24&&2**t-16<e)t++;return t}class y{static __wrap(e){e=e>>>0;let t=Object.create(y.prototype);return t.__wbg_ptr=e,V.register(t,t.__wbg_ptr,t),t}__destroy_into_raw(){let e=this.__wbg_ptr;return this.__wbg_ptr=0,V.unregister(this),e}free(){let e=this.__destroy_into_raw();u.__wbg_encoder_free(e,0)}constructor(e,t,r){try{let s=u.__wbindgen_add_to_stack_pointer(-16);u.encoder_create(s,e,t,r);var n=f().getInt32(s+0,!0),o=f().getInt32(s+4,!0),i=f().getInt32(s+8,!0);if(i)throw R(o);return this.__wbg_ptr=n>>>0,V.register(this,this.__wbg_ptr,this),this}finally{u.__wbindgen_add_to_stack_pointer(16)}}encode(e,t){try{let i=u.__wbindgen_add_to_stack_pointer(-16);u.encoder_encode(i,this.__wbg_ptr,e,t);var r=f().getInt32(i+0,!0),n=f().getInt32(i+4,!0),o=f().getInt32(i+8,!0);if(o)throw R(n);return r>>>0}finally{u.__wbindgen_add_to_stack_pointer(16)}}finished(){return u.encoder_finished(this.__wbg_ptr)!==0}input_capacity(){return u.encoder_input_capacity(this.__wbg_ptr)>>>0}input_ptr(){return u.encoder_input_ptr(this.__wbg_ptr)>>>0}needs_input(){return u.encoder_needs_input(this.__wbg_ptr)!==0}output_ptr(){return u.encoder_output_ptr(this.__wbg_ptr)>>>0}static part(e,t,r,n,o){try{let a=u.__wbindgen_add_to_stack_pointer(-16),p=Xe(o,u.__wbindgen_export),l=ge;u.encoder_part(a,e,t,r,n,p,l);var i=f().getInt32(a+0,!0),s=f().getInt32(a+4,!0),c=f().getInt32(a+8,!0);if(c)throw R(s);return y.__wrap(i)}finally{u.__wbindgen_add_to_stack_pointer(16)}}}if(Symbol.dispose)y.prototype[Symbol.dispose]=y.prototype.free;class k{__destroy_into_raw(){let e=this.__wbg_ptr;return this.__wbg_ptr=0,be.unregister(this),e}free(){let e=this.__destroy_into_raw();u.__wbg_joiner_free(e,0)}constructor(){let e=u.joiner_create();return this.__wbg_ptr=e>>>0,be.register(this,this.__wbg_ptr,this),this}finish(){try{let n=u.__wbindgen_add_to_stack_pointer(-16);u.joiner_finish(n,this.__wbg_ptr);var e=f().getInt32(n+0,!0),t=f().getInt32(n+4,!0),r=f().getInt32(n+8,!0);if(r)throw R(t);return e>>>0}finally{u.__wbindgen_add_to_stack_pointer(16)}}finished(){return u.joiner_finished(this.__wbg_ptr)!==0}input_capacity(){return u.joiner_input_capacity(this.__wbg_ptr)>>>0}input_ptr(){return u.joiner_input_ptr(this.__wbg_ptr)>>>0}join(e){try{let o=u.__wbindgen_add_to_stack_pointer(-16);u.joiner_join(o,this.__wbg_ptr,e);var t=f().getInt32(o+0,!0),r=f().getInt32(o+4,!0),n=f().getInt32(o+8,!0);if(n)throw R(r);return t>>>0}finally{u.__wbindgen_add_to_stack_pointer(16)}}needs_input(){return u.joiner_needs_input(this.__wbg_ptr)!==0}next_part(){u.joiner_next_part(this.__wbg_ptr)}output_ptr(){return u.joiner_output_ptr(this.__wbg_ptr)>>>0}}if(Symbol.dispose)k.prototype[Symbol.dispose]=k.prototype.free;function Ve(e,t){let r=Error(we(e,t));return Ze(r)}function Ye(e,t){throw Error(we(e,t))}var V=typeof FinalizationRegistry>"u"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry((e)=>u.__wbg_encoder_free(e>>>0,1)),be=typeof FinalizationRegistry>"u"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry((e)=>u.__wbg_joiner_free(e>>>0,1));function Ze(e){if(T===w.length)w.push(w.length+1);let t=T;return T=w[t],w[t]=e,t}function He(e){if(e<1028)return;w[e]=T,T=e}var v=null;function f(){if(v===null||v.buffer.detached===!0||v.buffer.detached===void 0&&v.buffer!==u.memory.buffer)v=new DataView(u.memory.buffer);return v}function we(e,t){return e=e>>>0,Ke(e,t)}var E=null;function ye(){if(E===null||E.byteLength===0)E=new Uint8Array(u.memory.buffer);return E}function Ge(e){return w[e]}var w=Array(1024).fill(void 0);w.push(void 0,null,!0,!1);var T=w.length;function Xe(e,t){let r=t(e.length*1,1)>>>0;return ye().set(e,r/1),ge=e.length,r}function R(e){let t=Ge(e);return He(e),t}var j=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0});j.decode();var Je=2146435072,Y=0;function Ke(e,t){if(Y+=t,Y>=Je)j=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0}),j.decode(),Y=t;return j.decode(ye().subarray(e,e+t))}var ge=0,u;function Qe(e){u=e}var he="(()=>{var F=Object.defineProperty;var D=(t)=>t;function O(t,e){this[t]=D.bind(null,e)}var L=(t,e)=>{for(var n in e)F(t,n,{get:e[n],enumerable:!0,configurable:!0,set:O.bind(e,n)})};var R={};L(R,{Encoder:()=>p,Joiner:()=>h,__wbg_Error_83742b46f01ce22d:()=>z,__wbg___wbindgen_throw_6ddd609b62940d55:()=>C,__wbg_set_wasm:()=>Y});class p{static __wrap(t){t=t>>>0;let e=Object.create(p.prototype);return e.__wbg_ptr=t,W.register(e,e.__wbg_ptr,e),e}__destroy_into_raw(){let t=this.__wbg_ptr;return this.__wbg_ptr=0,W.unregister(this),t}free(){let t=this.__destroy_into_raw();r.__wbg_encoder_free(t,0)}constructor(t,e,n){try{let a=r.__wbindgen_add_to_stack_pointer(-16);r.encoder_create(a,t,e,n);var i=_().getInt32(a+0,!0),o=_().getInt32(a+4,!0),s=_().getInt32(a+8,!0);if(s)throw g(o);return this.__wbg_ptr=i>>>0,W.register(this,this.__wbg_ptr,this),this}finally{r.__wbindgen_add_to_stack_pointer(16)}}encode(t,e){try{let s=r.__wbindgen_add_to_stack_pointer(-16);r.encoder_encode(s,this.__wbg_ptr,t,e);var n=_().getInt32(s+0,!0),i=_().getInt32(s+4,!0),o=_().getInt32(s+8,!0);if(o)throw g(i);return n>>>0}finally{r.__wbindgen_add_to_stack_pointer(16)}}finished(){return r.encoder_finished(this.__wbg_ptr)!==0}input_capacity(){return r.encoder_input_capacity(this.__wbg_ptr)>>>0}input_ptr(){return r.encoder_input_ptr(this.__wbg_ptr)>>>0}needs_input(){return r.encoder_needs_input(this.__wbg_ptr)!==0}output_ptr(){return r.encoder_output_ptr(this.__wbg_ptr)>>>0}static part(t,e,n,i,o){try{let d=r.__wbindgen_add_to_stack_pointer(-16),l=H(o,r.__wbindgen_export),b=j;r.encoder_part(d,t,e,n,i,l,b);var s=_().getInt32(d+0,!0),a=_().getInt32(d+4,!0),u=_().getInt32(d+8,!0);if(u)throw g(a);return p.__wrap(s)}finally{r.__wbindgen_add_to_stack_pointer(16)}}}if(Symbol.dispose)p.prototype[Symbol.dispose]=p.prototype.free;class h{__destroy_into_raw(){let t=this.__wbg_ptr;return this.__wbg_ptr=0,M.unregister(this),t}free(){let t=this.__destroy_into_raw();r.__wbg_joiner_free(t,0)}constructor(){let t=r.joiner_create();return this.__wbg_ptr=t>>>0,M.register(this,this.__wbg_ptr,this),this}finish(){try{let i=r.__wbindgen_add_to_stack_pointer(-16);r.joiner_finish(i,this.__wbg_ptr);var t=_().getInt32(i+0,!0),e=_().getInt32(i+4,!0),n=_().getInt32(i+8,!0);if(n)throw g(e);return t>>>0}finally{r.__wbindgen_add_to_stack_pointer(16)}}finished(){return r.joiner_finished(this.__wbg_ptr)!==0}input_capacity(){return r.joiner_input_capacity(this.__wbg_ptr)>>>0}input_ptr(){return r.joiner_input_ptr(this.__wbg_ptr)>>>0}join(t){try{let o=r.__wbindgen_add_to_stack_pointer(-16);r.joiner_join(o,this.__wbg_ptr,t);var e=_().getInt32(o+0,!0),n=_().getInt32(o+4,!0),i=_().getInt32(o+8,!0);if(i)throw g(n);return e>>>0}finally{r.__wbindgen_add_to_stack_pointer(16)}}needs_input(){return r.joiner_needs_input(this.__wbg_ptr)!==0}next_part(){r.joiner_next_part(this.__wbg_ptr)}output_ptr(){return r.joiner_output_ptr(this.__wbg_ptr)>>>0}}if(Symbol.dispose)h.prototype[Symbol.dispose]=h.prototype.free;function z(t,e){let n=Error(x(t,e));return V(n)}function C(t,e){throw Error(x(t,e))}var W=typeof FinalizationRegistry>\"u\"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry((t)=>r.__wbg_encoder_free(t>>>0,1)),M=typeof FinalizationRegistry>\"u\"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry((t)=>r.__wbg_joiner_free(t>>>0,1));function V(t){if(y===c.length)c.push(c.length+1);let e=y;return y=c[e],c[e]=t,e}function N(t){if(t<1028)return;c[t]=y,y=t}var f=null;function _(){if(f===null||f.buffer.detached===!0||f.buffer.detached===void 0&&f.buffer!==r.memory.buffer)f=new DataView(r.memory.buffer);return f}function x(t,e){return t=t>>>0,X(t,e)}var m=null;function I(){if(m===null||m.byteLength===0)m=new Uint8Array(r.memory.buffer);return m}function G(t){return c[t]}var c=Array(1024).fill(void 0);c.push(void 0,null,!0,!1);var y=c.length;function H(t,e){let n=e(t.length*1,1)>>>0;return I().set(t,n/1),j=t.length,n}function g(t){let e=G(t);return N(t),e}var w=new TextDecoder(\"utf-8\",{ignoreBOM:!0,fatal:!0});w.decode();var q=2146435072,v=0;function X(t,e){if(v+=e,v>=q)w=new TextDecoder(\"utf-8\",{ignoreBOM:!0,fatal:!0}),w.decode(),v=e;return w.decode(I().subarray(t,t+e))}var j=0,r;function Y(t){r=t}async function J(t){let n=globalThis.process?.getBuiltinModule?.(\"node:fs/promises\");if(!n)throw Error(`cannot read ${t.href} in this runtime; pass the .wasm bytes or a Response to init() instead`);return n.readFile(t)}async function K(t,e){if(!t.ok)throw Error(`failed to load WASM from ${t.url}: HTTP ${t.status}`);if(typeof WebAssembly.instantiateStreaming===\"function\"&&t.headers.get(\"Content-Type\")===\"application/wasm\")return WebAssembly.instantiateStreaming(t,e);return WebAssembly.instantiate(await t.arrayBuffer(),e)}async function U(t,e,n){let i={[e]:n},o=await t;if(typeof o===\"string\"||o instanceof URL){let a=new URL(o,globalThis.location?.href);o=a.protocol===\"file:\"?await J(a):await fetch(a)}let s;if(o instanceof WebAssembly.Module)s={module:o,instance:await WebAssembly.instantiate(o,i)};else if(o instanceof Response)s=await K(o,i);else s=await WebAssembly.instantiate(o,i);return n.__wbg_set_wasm(s.instance.exports),{memory:s.instance.exports.memory,module:s.module}}function P(t,e){if(t.length===1)return t[0];let n=new Uint8Array(e),i=0;for(let o of t)n.set(o,i),i+=o.length;return n}function B(t,e,n){try{let i=e.input_ptr(),o=e.output_ptr(),s=e.input_capacity(),a=[],u=0,d=0;do{let l=0;if(e.needs_input())l=Math.min(s,n.length-u),new Uint8Array(t.buffer,i,l).set(n.subarray(u,u+l)),u+=l;let b=e.encode(l,u===n.length);if(b>0)a.push(new Uint8Array(t.buffer,o,b).slice());d+=b}while(!e.finished());return P(a,d)}finally{e.free()}}var A=globalThis,k=A.self?void 0:A.process.getBuiltinModule(\"node:worker_threads\").parentPort,S=A.self??k,E;async function T(t){if(\"module\"in t){E=U(t.module,\"./defuss_brotli_compressor_bg.js\",R).then((e)=>e.memory);return}try{let{part:e,total:n,quality:i,lgwin:o,dictionary:s}=t,a=await E,u=B(a,p.part(e.length,n,i,o,s),e);S.postMessage({id:t.id,output:u},[u.buffer])}catch(e){S.postMessage({id:t.id,error:e instanceof Error?e.message:String(e)})}}if(k)k.on(\"message\",(t)=>void T(t));else A.self.onmessage=(t)=>void T(t.data);})();\n";async function et(e){let r=globalThis.process?.getBuiltinModule?.("node:fs/promises");if(!r)throw Error(`cannot read ${e.href} in this runtime; pass the .wasm bytes or a Response to init() instead`);return r.readFile(e)}async function tt(e,t){if(!e.ok)throw Error(`failed to load WASM from ${e.url}: HTTP ${e.status}`);if(typeof WebAssembly.instantiateStreaming==="function"&&e.headers.get("Content-Type")==="application/wasm")return WebAssembly.instantiateStreaming(e,t);return WebAssembly.instantiate(await e.arrayBuffer(),t)}async function B(e,t,r){let n={[t]:r},o=await e;if(typeof o==="string"||o instanceof URL){let s=new URL(o,globalThis.location?.href);o=s.protocol==="file:"?await et(s):await fetch(s)}let i;if(o instanceof WebAssembly.Module)i={module:o,instance:await WebAssembly.instantiate(o,n)};else if(o instanceof Response)i=await tt(o,n);else i=await WebAssembly.instantiate(o,n);return r.__wbg_set_wasm(i.instance.exports),{memory:i.instance.exports.memory,module:i.module}}function U(e,t){if(e.length===1)return e[0];let r=new Uint8Array(t),n=0;for(let o of e)r.set(o,n),n+=o.length;return r}function H(e,t,r){try{let n=t.input_ptr(),o=t.output_ptr(),i=t.input_capacity(),s=[],c=0,a=0;do{let p=0;if(t.needs_input())p=Math.min(i,r.length-c),new Uint8Array(e.buffer,n,p).set(r.subarray(c,c+p)),c+=p;let l=t.encode(p,c===r.length);if(l>0)s.push(new Uint8Array(e.buffer,o,l).slice());a+=l}while(!t.finished());return U(s,a)}finally{t.free()}}function rt(e,t,r){if(typeof Worker==="function"){let s=URL.createObjectURL(new Blob([e],{type:"text/javascript"})),c=new Worker(s);return c.onmessage=(a)=>t(a.data),c.onerror=(a)=>{a.preventDefault(),r(Error(`compressor worker failed: ${a.message}`))},{post:(a,p)=>c.postMessage(a,p),terminate:()=>{c.terminate(),URL.revokeObjectURL(s)}}}let o=globalThis.process?.getBuiltinModule?.("node:worker_threads");if(!o)return null;let i=new o.Worker(e,{eval:!0});return i.on("message",t),i.on("error",r),{post:(s,c)=>i.postMessage(s,c),terminate:()=>void i.terminate()}}function Ae(e,t,r,n){return new Promise((o,i)=>{let s=[],c=[],a=0,p=0,l=!1,d=(h)=>{if(l)return;l=!0;for(let m of c)m.terminate();if(h)i(h);else o(s)},P=(h)=>{if(a===r.length)return;let m=a++,A=r[m];h.post({id:m,...A},[A.part.buffer,A.dictionary.buffer])};for(let h=0;h<n&&!l;h++){let m=null;try{m=rt(e,(A)=>{if("error"in A)return d(Error(A.error));if(s[A.id]=A.output,++p===r.length)d();else P(m)},d)}catch{if(h===0)return o(null);break}if(!m)return o(null);c.push(m),m.post({module:t},[]),P(m)}})}var xe=11,ve=22,G=8,ke=32768,L=null,X=!1,O=null,J=null;function nt(e,t){let r=Math.max(Math.ceil(e/G),ke);if(t<2||e<=r)return[[0,e]];let n=[];for(let o=0;o<e;o+=r)n.push([o,Math.min(o+r,e)]);return n}function ot(e,t){let r=new k;try{let n=r.input_ptr(),o=r.output_ptr(),i=r.input_capacity(),s=[],c=0,a=(p)=>{if(p>0)s.push(new Uint8Array(e.buffer,o,p).slice());c+=p};for(let p of t){r.next_part();for(let l=0;l<p.length;l+=i){let d=p.subarray(l,l+i);new Uint8Array(e.buffer,n,d.length).set(d);let P=d.length;do a(r.join(P)),P=0;while(!r.needs_input())}}while(!r.finished())a(r.finish());return U(s,c)}finally{r.free()}}function it(){let e=globalThis,t=e.navigator?.hardwareConcurrency??e.process?.getBuiltinModule?.("node:os")?.availableParallelism?.();return Math.min(G,t??1)}function K(){if(!X||!O||!J)throw Error("defuss-brotli/compressor is not initialized. Call await init() first.")}function Q(e={}){let t=e.quality??xe,r=e.lgwin??ve;if(!Number.isInteger(t)||t<0||t>11)throw Error("quality must be an integer between 0 and 11");if(!Number.isInteger(r)||r<10||r>24)throw Error("lgwin must be an integer between 10 and 24");return{quality:t,lgwin:r}}function Ue(e){if(!L)L=Promise.resolve().then(()=>B(e(),"./defuss_brotli_compressor_bg.js",Z)).then((t)=>{O=t.memory,J=t.module,X=!0},(t)=>{throw L=null,t});return L}function ee(){return X}function We(e,t={}){K();let{quality:r,lgwin:n}=Q(t);return H(O,new y(e.length,r,n),e)}async function te(e,t={}){K();let{quality:r,lgwin:n}=Q(t),o=t.threads??it();if(!Number.isInteger(o)||o<1)throw Error("threads must be a positive integer");let i=nt(e.length,r);if(i.length===1)return We(e,{quality:r,lgwin:n});let s=2**n-16,c=(l,d)=>new Uint8Array(e.subarray(l,d)),a=i.map(([l,d])=>({total:e.length,quality:r,lgwin:n,part:c(l,d),dictionary:c(Math.max(0,l-s),l)})),p=(o>1?await Ae(he,J,a,Math.min(o,a.length)):null)??a.map((l)=>H(O,y.part(l.part.length,l.total,r,n,l.dictionary),l.part));return ot(O,p)}function Me(e){return Ue(()=>e??new URL("./compressor.wasm",import.meta.url))}class S{__destroy_into_raw(){let e=this.__wbg_ptr;return this.__wbg_ptr=0,Se.unregister(this),e}free(){let e=this.__destroy_into_raw();_.__wbg_decoder_free(e,0)}constructor(e,t){try{let i=_.__wbindgen_add_to_stack_pointer(-16);_.decoder_create(i,e,t);var r=M().getInt32(i+0,!0),n=M().getInt32(i+4,!0),o=M().getInt32(i+8,!0);if(o)throw Pe(n);return this.__wbg_ptr=r>>>0,Se.register(this,this.__wbg_ptr,this),this}finally{_.__wbindgen_add_to_stack_pointer(16)}}decode(e,t){try{let i=_.__wbindgen_add_to_stack_pointer(-16);_.decoder_decode(i,this.__wbg_ptr,e,t);var r=M().getInt32(i+0,!0),n=M().getInt32(i+4,!0),o=M().getInt32(i+8,!0);if(o)throw Pe(n);return r>>>0}finally{_.__wbindgen_add_to_stack_pointer(16)}}finished(){return _.decoder_finished(this.__wbg_ptr)!==0}input_capacity(){return _.decoder_input_capacity(this.__wbg_ptr)>>>0}input_ptr(){return _.decoder_input_ptr(this.__wbg_ptr)>>>0}needs_input(){return _.decoder_needs_input(this.__wbg_ptr)!==0}output_ptr(){return _.decoder_output_ptr(this.__wbg_ptr)>>>0}}if(Symbol.dispose)S.prototype[Symbol.dispose]=S.prototype.free;function at(e,t){let r=Error(Te(e,t));return ct(r)}function ut(e,t){throw Error(Te(e,t))}var Se=typeof FinalizationRegistry>"u"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry((e)=>_.__wbg_decoder_free(e>>>0,1));function ct(e){if(I===g.length)g.push(g.length+1);let t=I;return I=g[t],g[t]=e,t}function lt(e){if(e<1028)return;g[e]=I,I=e}var W=null;function M(){if(W===null||W.buffer.detached===!0||W.buffer.detached===void 0&&W.buffer!==_.memory.buffer)W=new DataView(_.memory.buffer);return W}function Te(e,t){return e=e>>>0,ft(e,t)}var F=null;function pt(){if(F===null||F.byteLength===0)F=new Uint8Array(_.memory.buffer);return F}function dt(e){return g[e]}var g=Array(1024).fill(void 0);g.push(void 0,null,!0,!1);var I=g.length;function Pe(e){let t=dt(e);return lt(e),t}var C=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0});C.decode();var _t=2146435072,re=0;function ft(e,t){if(re+=t,re>=_t)C=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0}),C.decode(),re=t;return C.decode(pt().subarray(e,e+t))}var _;function mt(e){_=e}var Re=67108864,bt=4294967295,D=null,oe=!1;function wt(e,t,r){let n=new S(t.length,r);try{let o=n.input_ptr(),i=n.output_ptr(),s=n.input_capacity(),c=[],a=0,p=0;do{let l=0;if(n.needs_input())l=Math.min(s,t.length-a),new Uint8Array(e.buffer,o,l).set(t.subarray(a,a+l)),a+=l;let d=n.decode(l,a===t.length);if(d>0)c.push(new Uint8Array(e.buffer,i,d).slice());p+=d}while(!n.finished());return U(c,p)}finally{n.free()}}var N=null;function Oe(){if(!oe||!N)throw Error("defuss-brotli/decompressor is not initialized. Call await init() first.")}function Ie(e={}){let t=e.maxOutputSize??Re;if(!Number.isInteger(t)||t<=0)throw Error("maxOutputSize must be a positive integer");return{maxOutputSize:t}}function Ee(e){if(!D)D=Promise.resolve().then(()=>B(e(),"./defuss_brotli_decompressor_bg.js",ne)).then(({memory:t})=>{N=(r,n)=>wt(t,r,n),oe=!0},(t)=>{throw D=null,t});return D}function ie(){return oe}function se(e,t={}){Oe();let{maxOutputSize:r}=Ie(t);return N(e,Math.min(r,bt))}function je(e){return Ee(()=>e??new URL("./decompressor.wasm",import.meta.url))}var Wt=4294967295;function Le(e){if(e<1024)return`${e} B`;let[t,r]=e<1048576?[e/1024,"KiB"]:e<1073741824?[e/1048576,"MiB"]:[e/1073741824,"GiB"];return`${t.toFixed(3)} ${r}`}function ae(e,t){return new Promise((r,n)=>e.write(t,(o)=>o?n(o):r()))}async function Mt(e){let t=[];for await(let r of e)t.push(r);return Buffer.concat(t)}function q(e){let t=e instanceof Error?e.message:String(e);return/^[A-Z]+: (.+?), \w+ '.*'$/.exec(t)?.[1]??t}class x extends Error{}function St(e,t){if(t.stdout||e==="-"&&t.output===void 0)return;if(t.output!==void 0)return t.output;if(t.mode==="compress")return e+t.suffix;if(!e.endsWith(t.suffix)||Ut(e)===t.suffix)throw new x(`input file [${e}] suffix mismatch`);return e.slice(0,-t.suffix.length)}async function Pt(e,t,r){let n=e==="-"?"stdin":e,o=t.mode==="test"?null:St(e,t);if(o&&e!=="-"&&Be(o)===Be(e))throw new x(`input and output are the same file [${e}]`);let i,s;try{i=e==="-"?await Mt(r.stdin):await ht(e),s=e==="-"?void 0:await At(e)}catch(d){throw new x(`failed to open input file [${n}]: ${q(d)}`)}let c=performance.now(),a;if(t.mode==="compress"){if(!ee())await Me(r.wasm.compressor());let d=t.lgwin||me(i.length);a=await te(i,{quality:t.quality,lgwin:d,threads:t.threads})}else{if(!ie())await je(r.wasm.decompressor());try{a=se(i,{maxOutputSize:Wt})}catch(d){throw new x(`corrupt input [${n}]: ${q(d)}`)}}let p=((performance.now()-c)/1000).toFixed(2),l=t.mode==="compress"&&t.squash&&!!o&&a.length>i.length;if(o===void 0)await ae(r.stdout,a);else if(o&&!l){try{await kt(o,a,{flag:t.force?"w":"wx"})}catch(d){throw new x(`failed to open output file [${o}]: ${q(d)}`)}if(t.copyStat&&s)await gt(o,s.mode&4095),await vt(o,s.atimeMs/1000,s.mtimeMs/1000)}if(t.verbose){let d=t.mode==="compress"?"Compressed":"Decompressed";r.stderr.write(`${d} [${n}]: ${Le(i.length)} -> ${Le(a.length)} in ${p} sec
|
|
31
|
+
`)}if(t.remove&&e!=="-"&&!l)await xt(e)}async function Fe(e,t){let r;try{let o=fe(e);if(o.kind==="help")return await ae(t.stdout,de).then(()=>0);if(o.kind==="version")return await ae(t.stdout,`defuss-brotli ${t.version}
|
|
32
|
+
`).then(()=>0);r=o.options}catch(o){if(!(o instanceof b))throw o;return t.stderr.write(`${o.message}
|
|
33
|
+
Use -h for help.
|
|
34
|
+
`),1}let n=r.stdout||r.files.includes("-")&&r.output===void 0;if(!r.force&&r.mode==="compress"&&n&&t.stdout.isTTY)return t.stderr.write(`Use -h help. Use -f to force output to a terminal.
|
|
35
|
+
`),1;if(!r.force&&r.files.includes("-")&&t.stdin.isTTY)return t.stderr.write(`Use -h help. Use -f to force input from a terminal.
|
|
36
|
+
`),1;for(let o of r.files)try{await Pt(o,r,t)}catch(i){if(i.code!=="EPIPE")t.stderr.write(`${i instanceof x?i.message:`failed on [${o}]: ${q(i)}`}
|
|
37
|
+
`);return 1}return 0}var ce=(e)=>new URL(e,import.meta.url),{version:Tt}=JSON.parse(ue(ce("../package.json"),"utf8"));process.stdout.on("error",()=>{});var Rt=await Fe(process.argv.slice(2),{stdin:process.stdin,stdout:process.stdout,stderr:process.stderr,version:Tt,wasm:{compressor:()=>ue(ce("./compressor.wasm")),decompressor:()=>ue(ce("./decompressor.wasm"))}}),Ce=(e)=>new Promise((t)=>e.write("",t));await Promise.all([Ce(process.stdout),Ce(process.stderr)]);process.exit(Rt);
|
|
@@ -16,7 +16,7 @@ export interface BrotliCompressOptions {
|
|
|
16
16
|
* - **5–7** — good balance for web text (HTML/CSS/JS/JSON)
|
|
17
17
|
* - **11** — smallest output, slowest compression
|
|
18
18
|
*
|
|
19
|
-
* @default
|
|
19
|
+
* @default 11
|
|
20
20
|
*/
|
|
21
21
|
quality?: number;
|
|
22
22
|
/**
|
|
@@ -29,10 +29,44 @@ export interface BrotliCompressOptions {
|
|
|
29
29
|
*/
|
|
30
30
|
lgwin?: number;
|
|
31
31
|
}
|
|
32
|
-
/**
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Default compression quality: the smallest output. VERIFIED: the same default as Node.js zlib
|
|
34
|
+
* (`BROTLI_DEFAULT_QUALITY` = 11) and the `brotli` CLI. Pass a lower quality when compression time matters more.
|
|
35
|
+
*/
|
|
36
|
+
export declare const DEFAULT_QUALITY = 11;
|
|
37
|
+
/** Default sliding window exponent, as in Node.js zlib (`BROTLI_DEFAULT_WINDOW` = 22). */
|
|
35
38
|
export declare const DEFAULT_LGWIN = 22;
|
|
39
|
+
/**
|
|
40
|
+
* Options for {@link compressParallel}.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* await compressParallel(data); // quality 11, up to 8 workers
|
|
45
|
+
* await compressParallel(data, { threads: 2 }); // same bytes, two workers
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export interface BrotliParallelOptions extends BrotliCompressOptions {
|
|
49
|
+
/**
|
|
50
|
+
* How many workers compress parts at once, at most {@link PARALLEL_PARTS}; 1 compresses the parts one after
|
|
51
|
+
* another on the calling thread. The output does not depend on it.
|
|
52
|
+
*
|
|
53
|
+
* @default the number of logical CPUs, at most 8
|
|
54
|
+
*/
|
|
55
|
+
threads?: number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* A parallel compression splits its input into at most this many parts of at least {@link MIN_PART_SIZE} bytes.
|
|
59
|
+
* A constant rather than the number of CPUs, so the output is the same on every machine. VERIFIED (native, brotli
|
|
60
|
+
* 8.0.2, quality 11): with 8 threads, 8 parts beat 4 and 16 on a 256 KiB SVG, the 5.1 MB SVG and a 20 MB mix.
|
|
61
|
+
*/
|
|
62
|
+
export declare const PARALLEL_PARTS = 8;
|
|
63
|
+
/** Smallest part. VERIFIED: 32 KiB parts cost a 256 KiB SVG 0.007 % in size, 16 KiB parts of a 29 KB page 1.6 %. */
|
|
64
|
+
export declare const MIN_PART_SIZE: number;
|
|
65
|
+
/**
|
|
66
|
+
* The byte ranges of a parallel compression: one range below quality 2, whose encoders use no dictionary, or when
|
|
67
|
+
* the input fits in one part; otherwise at most {@link PARALLEL_PARTS} of at least {@link MIN_PART_SIZE} bytes.
|
|
68
|
+
*/
|
|
69
|
+
export declare function partRanges(length: number, quality: number): Array<[number, number]>;
|
|
36
70
|
/**
|
|
37
71
|
* Throws if `init()` has not been called yet.
|
|
38
72
|
* Used internally before every compress call.
|
|
@@ -80,6 +114,31 @@ export declare function isReady(): boolean;
|
|
|
80
114
|
* ```
|
|
81
115
|
*/
|
|
82
116
|
export declare function compress(input: Uint8Array, options?: BrotliCompressOptions): Uint8Array;
|
|
117
|
+
/**
|
|
118
|
+
* Compress raw bytes with Brotli on several workers.
|
|
119
|
+
*
|
|
120
|
+
* The input is split into at most {@link PARALLEL_PARTS} parts of at least {@link MIN_PART_SIZE} bytes. Each worker
|
|
121
|
+
* runs its own instance of the compressor and compresses one part with the window before it as dictionary, so
|
|
122
|
+
* matches still reach across parts; the calling thread joins the parts into one standard Brotli stream. The output
|
|
123
|
+
* depends only on the input and the options other than `threads`, and differs slightly from {@link compress}'s
|
|
124
|
+
* when there is more than one part. Below quality 2, or when the input fits in one part, it equals `compress`.
|
|
125
|
+
* Workers start from a Blob URL (browsers, Bun; a Content Security Policy must allow `worker-src blob:`) or, in
|
|
126
|
+
* Node.js, with `worker_threads`, and are terminated before the Promise settles. Where no such worker starts, the
|
|
127
|
+
* parts run one after another on the calling thread, with the same output.
|
|
128
|
+
*
|
|
129
|
+
* @param input Raw bytes to compress. Not modified.
|
|
130
|
+
* @param options Compression tuning and the number of workers — see {@link BrotliParallelOptions}.
|
|
131
|
+
* @returns A Promise of a new `Uint8Array` with the Brotli-compressed output.
|
|
132
|
+
*
|
|
133
|
+
* @throws If `init()` was not called first, or if options are out of range.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```ts
|
|
137
|
+
* await init();
|
|
138
|
+
* const compressed = await compressParallel(bigFileBytes);
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
export declare function compressParallel(input: Uint8Array, options?: BrotliParallelOptions): Promise<Uint8Array>;
|
|
83
142
|
/**
|
|
84
143
|
* Compress a JS string with Brotli.
|
|
85
144
|
*
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** The `Encoder` methods the loop uses, from whichever instance created the encoder. */
|
|
2
|
+
export interface ChunkEncoder {
|
|
3
|
+
input_ptr(): number;
|
|
4
|
+
output_ptr(): number;
|
|
5
|
+
input_capacity(): number;
|
|
6
|
+
needs_input(): boolean;
|
|
7
|
+
encode(len: number, last: boolean): number;
|
|
8
|
+
finished(): boolean;
|
|
9
|
+
free(): void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Encodes `input` through `encoder` and frees it: one window (`2 ** lgwin` bytes) of input at a time is copied into
|
|
13
|
+
* WASM memory and each compressed chunk (at most 1 MiB) straight out of it, so linear memory holds the encoder
|
|
14
|
+
* state and two chunks rather than the whole input and output. The chunks are joined once at the end.
|
|
15
|
+
*/
|
|
16
|
+
export declare function encodeWith(wasm: WebAssembly.Memory, encoder: ChunkEncoder, input: Uint8Array): Uint8Array;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { WasmSource } from "./wasm.js";
|
|
2
|
-
export { DEFAULT_LGWIN, DEFAULT_QUALITY, assertReady, compress, compressText, isReady, normalizeOptions, type BrotliCompressOptions, } from "./compressor-core.js";
|
|
2
|
+
export { DEFAULT_LGWIN, DEFAULT_QUALITY, MIN_PART_SIZE, PARALLEL_PARTS, assertReady, compress, compressParallel, compressText, isReady, normalizeOptions, type BrotliCompressOptions, type BrotliParallelOptions, } from "./compressor-core.js";
|
|
3
3
|
export type { WasmSource } from "./wasm.js";
|
|
4
4
|
/**
|
|
5
5
|
* Load the Brotli compressor WASM module from a separate `.wasm` file. Must be called (and awaited) once before
|
package/dist/compressor-slim.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var P=Object.defineProperty;var k=(e)=>e;function C(e,t){this[e]=k.bind(null,t)}var j=(e,t)=>{for(var r in t)P(e,r,{get:t[r],enumerable:!0,configurable:!0,set:C.bind(t,r)})};var h={};j(h,{Encoder:()=>f,__wbg_Error_83742b46f01ce22d:()=>z,__wbg___wbindgen_throw_6ddd609b62940d55:()=>q,__wbg_set_wasm:()=>X});class f{__destroy_into_raw(){let e=this.__wbg_ptr;return this.__wbg_ptr=0,B.unregister(this),e}free(){let e=this.__destroy_into_raw();o.__wbg_encoder_free(e,0)}constructor(e,t,r){try{let a=o.__wbindgen_add_to_stack_pointer(-16);o.encoder_create(a,e,t,r);var i=l().getInt32(a+0,!0),n=l().getInt32(a+4,!0),s=l().getInt32(a+8,!0);if(s)throw R(n);return this.__wbg_ptr=i>>>0,B.register(this,this.__wbg_ptr,this),this}finally{o.__wbindgen_add_to_stack_pointer(16)}}encode(e,t){try{let s=o.__wbindgen_add_to_stack_pointer(-16);o.encoder_encode(s,this.__wbg_ptr,e,t);var r=l().getInt32(s+0,!0),i=l().getInt32(s+4,!0),n=l().getInt32(s+8,!0);if(n)throw R(i);return r>>>0}finally{o.__wbindgen_add_to_stack_pointer(16)}}finished(){return o.encoder_finished(this.__wbg_ptr)!==0}input_capacity(){return o.encoder_input_capacity(this.__wbg_ptr)>>>0}input_ptr(){return o.encoder_input_ptr(this.__wbg_ptr)>>>0}needs_input(){return o.encoder_needs_input(this.__wbg_ptr)!==0}output_ptr(){return o.encoder_output_ptr(this.__wbg_ptr)>>>0}}if(Symbol.dispose)f.prototype[Symbol.dispose]=f.prototype.free;function z(e,t){let r=Error(T(e,t));return N(r)}function q(e,t){throw Error(T(e,t))}var B=typeof FinalizationRegistry>"u"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry((e)=>o.__wbg_encoder_free(e>>>0,1));function N(e){if(m===u.length)u.push(u.length+1);let t=m;return m=u[t],u[t]=e,t}function G(e){if(e<1028)return;u[e]=m,m=e}var c=null;function l(){if(c===null||c.buffer.detached===!0||c.buffer.detached===void 0&&c.buffer!==o.memory.buffer)c=new DataView(o.memory.buffer);return c}function T(e,t){return e=e>>>0,Q(e,t)}var d=null;function V(){if(d===null||d.byteLength===0)d=new Uint8Array(o.memory.buffer);return d}function Y(e){return u[e]}var u=Array(1024).fill(void 0);u.push(void 0,null,!0,!1);var m=u.length;function R(e){let t=Y(e);return G(e),t}var y=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0});y.decode();var H=2146435072,g=0;function Q(e,t){if(g+=t,g>=H)y=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0}),y.decode(),g=t;return y.decode(V().subarray(e,e+t))}var o;function X(e){o=e}async function J(e){let r=globalThis.process?.getBuiltinModule?.("node:fs/promises");if(!r)throw Error(`cannot read ${e.href} in this runtime; pass the .wasm bytes or a Response to init() instead`);return r.readFile(e)}async function K(e,t){if(!e.ok)throw Error(`failed to load WASM from ${e.url}: HTTP ${e.status}`);if(typeof WebAssembly.instantiateStreaming==="function"&&e.headers.get("Content-Type")==="application/wasm")return(await WebAssembly.instantiateStreaming(e,t)).instance;return(await WebAssembly.instantiate(await e.arrayBuffer(),t)).instance}async function E(e,t,r){let i={[t]:r},n=await e;if(typeof n==="string"||n instanceof URL){let a=new URL(n,globalThis.location?.href);n=a.protocol==="file:"?await J(a):await fetch(a)}let s;if(n instanceof WebAssembly.Module)s=await WebAssembly.instantiate(n,i);else if(n instanceof Response)s=await K(n,i);else s=(await WebAssembly.instantiate(n,i)).instance;return r.__wbg_set_wasm(s.exports),s.exports.memory}function M(e,t){if(e.length===1)return e[0];let r=new Uint8Array(t),i=0;for(let n of e)r.set(n,i),i+=n.length;return r}var I=6,F=22,b=null,A=!1,x=null;function Z(e,t,r,i){let n=new f(t.length,r,i);try{let s=n.input_ptr(),a=n.output_ptr(),D=n.input_capacity(),U=[],_=0,W=0;do{let p=0;if(n.needs_input())p=Math.min(D,t.length-_),new Uint8Array(e.buffer,s,p).set(t.subarray(_,_+p)),_+=p;let w=n.encode(p,_===t.length);if(w>0)U.push(new Uint8Array(e.buffer,a,w).slice());W+=w}while(!n.finished());return M(U,W)}finally{n.free()}}function L(){if(!A||!x)throw Error("defuss-brotli/compressor is not initialized. Call await init() first.")}function O(e={}){let t=e.quality??I,r=e.lgwin??F;if(!Number.isInteger(t)||t<0||t>11)throw Error("quality must be an integer between 0 and 11");if(!Number.isInteger(r)||r<10||r>24)throw Error("lgwin must be an integer between 10 and 24");return{quality:t,lgwin:r}}function S(e){if(!b)b=Promise.resolve().then(()=>E(e(),"./defuss_brotli_compressor_bg.js",h)).then((t)=>{x=t,A=!0},(t)=>{throw b=null,t});return b}function ee(){return A}function v(e,t={}){L();let{quality:r,lgwin:i}=O(t);return Z(x,e,r,i)}function te(e,t={}){return v(new TextEncoder().encode(e),t)}function ae(e){return S(()=>e??new URL("./compressor.wasm",import.meta.url))}export{F as DEFAULT_LGWIN,I as DEFAULT_QUALITY,L as assertReady,v as compress,te as compressText,ae as init,ee as isReady,O as normalizeOptions};
|
|
1
|
+
var X=Object.defineProperty;var Q=(t)=>t;function Z(t,e){this[t]=Q.bind(null,e)}var tt=(t,e)=>{for(var r in e)X(t,r,{get:e[r],enumerable:!0,configurable:!0,set:Z.bind(e,r)})};var T={};tt(T,{Encoder:()=>g,Joiner:()=>h,__wbg_Error_83742b46f01ce22d:()=>et,__wbg___wbindgen_throw_6ddd609b62940d55:()=>rt,__wbg_set_wasm:()=>ut});class g{static __wrap(t){t=t>>>0;let e=Object.create(g.prototype);return e.__wbg_ptr=t,I.register(e,e.__wbg_ptr,e),e}__destroy_into_raw(){let t=this.__wbg_ptr;return this.__wbg_ptr=0,I.unregister(this),t}free(){let t=this.__destroy_into_raw();o.__wbg_encoder_free(t,0)}constructor(t,e,r){try{let a=o.__wbindgen_add_to_stack_pointer(-16);o.encoder_create(a,t,e,r);var i=c().getInt32(a+0,!0),n=c().getInt32(a+4,!0),s=c().getInt32(a+8,!0);if(s)throw W(n);return this.__wbg_ptr=i>>>0,I.register(this,this.__wbg_ptr,this),this}finally{o.__wbindgen_add_to_stack_pointer(16)}}encode(t,e){try{let s=o.__wbindgen_add_to_stack_pointer(-16);o.encoder_encode(s,this.__wbg_ptr,t,e);var r=c().getInt32(s+0,!0),i=c().getInt32(s+4,!0),n=c().getInt32(s+8,!0);if(n)throw W(i);return r>>>0}finally{o.__wbindgen_add_to_stack_pointer(16)}}finished(){return o.encoder_finished(this.__wbg_ptr)!==0}input_capacity(){return o.encoder_input_capacity(this.__wbg_ptr)>>>0}input_ptr(){return o.encoder_input_ptr(this.__wbg_ptr)>>>0}needs_input(){return o.encoder_needs_input(this.__wbg_ptr)!==0}output_ptr(){return o.encoder_output_ptr(this.__wbg_ptr)>>>0}static part(t,e,r,i,n){try{let u=o.__wbindgen_add_to_stack_pointer(-16),p=st(n,o.__wbindgen_export),_=N;o.encoder_part(u,t,e,r,i,p,_);var s=c().getInt32(u+0,!0),a=c().getInt32(u+4,!0),l=c().getInt32(u+8,!0);if(l)throw W(a);return g.__wrap(s)}finally{o.__wbindgen_add_to_stack_pointer(16)}}}if(Symbol.dispose)g.prototype[Symbol.dispose]=g.prototype.free;class h{__destroy_into_raw(){let t=this.__wbg_ptr;return this.__wbg_ptr=0,C.unregister(this),t}free(){let t=this.__destroy_into_raw();o.__wbg_joiner_free(t,0)}constructor(){let t=o.joiner_create();return this.__wbg_ptr=t>>>0,C.register(this,this.__wbg_ptr,this),this}finish(){try{let i=o.__wbindgen_add_to_stack_pointer(-16);o.joiner_finish(i,this.__wbg_ptr);var t=c().getInt32(i+0,!0),e=c().getInt32(i+4,!0),r=c().getInt32(i+8,!0);if(r)throw W(e);return t>>>0}finally{o.__wbindgen_add_to_stack_pointer(16)}}finished(){return o.joiner_finished(this.__wbg_ptr)!==0}input_capacity(){return o.joiner_input_capacity(this.__wbg_ptr)>>>0}input_ptr(){return o.joiner_input_ptr(this.__wbg_ptr)>>>0}join(t){try{let n=o.__wbindgen_add_to_stack_pointer(-16);o.joiner_join(n,this.__wbg_ptr,t);var e=c().getInt32(n+0,!0),r=c().getInt32(n+4,!0),i=c().getInt32(n+8,!0);if(i)throw W(r);return e>>>0}finally{o.__wbindgen_add_to_stack_pointer(16)}}needs_input(){return o.joiner_needs_input(this.__wbg_ptr)!==0}next_part(){o.joiner_next_part(this.__wbg_ptr)}output_ptr(){return o.joiner_output_ptr(this.__wbg_ptr)>>>0}}if(Symbol.dispose)h.prototype[Symbol.dispose]=h.prototype.free;function et(t,e){let r=Error(D(t,e));return nt(r)}function rt(t,e){throw Error(D(t,e))}var I=typeof FinalizationRegistry>"u"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry((t)=>o.__wbg_encoder_free(t>>>0,1)),C=typeof FinalizationRegistry>"u"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry((t)=>o.__wbg_joiner_free(t>>>0,1));function nt(t){if(v===b.length)b.push(b.length+1);let e=v;return v=b[e],b[e]=t,e}function it(t){if(t<1028)return;b[t]=v,v=t}var w=null;function c(){if(w===null||w.buffer.detached===!0||w.buffer.detached===void 0&&w.buffer!==o.memory.buffer)w=new DataView(o.memory.buffer);return w}function D(t,e){return t=t>>>0,_t(t,e)}var k=null;function z(){if(k===null||k.byteLength===0)k=new Uint8Array(o.memory.buffer);return k}function ot(t){return b[t]}var b=Array(1024).fill(void 0);b.push(void 0,null,!0,!1);var v=b.length;function st(t,e){let r=e(t.length*1,1)>>>0;return z().set(t,r/1),N=t.length,r}function W(t){let e=ot(t);return it(t),e}var U=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0});U.decode();var at=2146435072,P=0;function _t(t,e){if(P+=e,P>=at)U=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0}),U.decode(),P=e;return U.decode(z().subarray(t,t+e))}var N=0,o;function ut(t){o=t}var q="(()=>{var F=Object.defineProperty;var D=(t)=>t;function O(t,e){this[t]=D.bind(null,e)}var L=(t,e)=>{for(var n in e)F(t,n,{get:e[n],enumerable:!0,configurable:!0,set:O.bind(e,n)})};var R={};L(R,{Encoder:()=>p,Joiner:()=>h,__wbg_Error_83742b46f01ce22d:()=>z,__wbg___wbindgen_throw_6ddd609b62940d55:()=>C,__wbg_set_wasm:()=>Y});class p{static __wrap(t){t=t>>>0;let e=Object.create(p.prototype);return e.__wbg_ptr=t,W.register(e,e.__wbg_ptr,e),e}__destroy_into_raw(){let t=this.__wbg_ptr;return this.__wbg_ptr=0,W.unregister(this),t}free(){let t=this.__destroy_into_raw();r.__wbg_encoder_free(t,0)}constructor(t,e,n){try{let a=r.__wbindgen_add_to_stack_pointer(-16);r.encoder_create(a,t,e,n);var i=_().getInt32(a+0,!0),o=_().getInt32(a+4,!0),s=_().getInt32(a+8,!0);if(s)throw g(o);return this.__wbg_ptr=i>>>0,W.register(this,this.__wbg_ptr,this),this}finally{r.__wbindgen_add_to_stack_pointer(16)}}encode(t,e){try{let s=r.__wbindgen_add_to_stack_pointer(-16);r.encoder_encode(s,this.__wbg_ptr,t,e);var n=_().getInt32(s+0,!0),i=_().getInt32(s+4,!0),o=_().getInt32(s+8,!0);if(o)throw g(i);return n>>>0}finally{r.__wbindgen_add_to_stack_pointer(16)}}finished(){return r.encoder_finished(this.__wbg_ptr)!==0}input_capacity(){return r.encoder_input_capacity(this.__wbg_ptr)>>>0}input_ptr(){return r.encoder_input_ptr(this.__wbg_ptr)>>>0}needs_input(){return r.encoder_needs_input(this.__wbg_ptr)!==0}output_ptr(){return r.encoder_output_ptr(this.__wbg_ptr)>>>0}static part(t,e,n,i,o){try{let d=r.__wbindgen_add_to_stack_pointer(-16),l=H(o,r.__wbindgen_export),b=j;r.encoder_part(d,t,e,n,i,l,b);var s=_().getInt32(d+0,!0),a=_().getInt32(d+4,!0),u=_().getInt32(d+8,!0);if(u)throw g(a);return p.__wrap(s)}finally{r.__wbindgen_add_to_stack_pointer(16)}}}if(Symbol.dispose)p.prototype[Symbol.dispose]=p.prototype.free;class h{__destroy_into_raw(){let t=this.__wbg_ptr;return this.__wbg_ptr=0,M.unregister(this),t}free(){let t=this.__destroy_into_raw();r.__wbg_joiner_free(t,0)}constructor(){let t=r.joiner_create();return this.__wbg_ptr=t>>>0,M.register(this,this.__wbg_ptr,this),this}finish(){try{let i=r.__wbindgen_add_to_stack_pointer(-16);r.joiner_finish(i,this.__wbg_ptr);var t=_().getInt32(i+0,!0),e=_().getInt32(i+4,!0),n=_().getInt32(i+8,!0);if(n)throw g(e);return t>>>0}finally{r.__wbindgen_add_to_stack_pointer(16)}}finished(){return r.joiner_finished(this.__wbg_ptr)!==0}input_capacity(){return r.joiner_input_capacity(this.__wbg_ptr)>>>0}input_ptr(){return r.joiner_input_ptr(this.__wbg_ptr)>>>0}join(t){try{let o=r.__wbindgen_add_to_stack_pointer(-16);r.joiner_join(o,this.__wbg_ptr,t);var e=_().getInt32(o+0,!0),n=_().getInt32(o+4,!0),i=_().getInt32(o+8,!0);if(i)throw g(n);return e>>>0}finally{r.__wbindgen_add_to_stack_pointer(16)}}needs_input(){return r.joiner_needs_input(this.__wbg_ptr)!==0}next_part(){r.joiner_next_part(this.__wbg_ptr)}output_ptr(){return r.joiner_output_ptr(this.__wbg_ptr)>>>0}}if(Symbol.dispose)h.prototype[Symbol.dispose]=h.prototype.free;function z(t,e){let n=Error(x(t,e));return V(n)}function C(t,e){throw Error(x(t,e))}var W=typeof FinalizationRegistry>\"u\"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry((t)=>r.__wbg_encoder_free(t>>>0,1)),M=typeof FinalizationRegistry>\"u\"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry((t)=>r.__wbg_joiner_free(t>>>0,1));function V(t){if(y===c.length)c.push(c.length+1);let e=y;return y=c[e],c[e]=t,e}function N(t){if(t<1028)return;c[t]=y,y=t}var f=null;function _(){if(f===null||f.buffer.detached===!0||f.buffer.detached===void 0&&f.buffer!==r.memory.buffer)f=new DataView(r.memory.buffer);return f}function x(t,e){return t=t>>>0,X(t,e)}var m=null;function I(){if(m===null||m.byteLength===0)m=new Uint8Array(r.memory.buffer);return m}function G(t){return c[t]}var c=Array(1024).fill(void 0);c.push(void 0,null,!0,!1);var y=c.length;function H(t,e){let n=e(t.length*1,1)>>>0;return I().set(t,n/1),j=t.length,n}function g(t){let e=G(t);return N(t),e}var w=new TextDecoder(\"utf-8\",{ignoreBOM:!0,fatal:!0});w.decode();var q=2146435072,v=0;function X(t,e){if(v+=e,v>=q)w=new TextDecoder(\"utf-8\",{ignoreBOM:!0,fatal:!0}),w.decode(),v=e;return w.decode(I().subarray(t,t+e))}var j=0,r;function Y(t){r=t}async function J(t){let n=globalThis.process?.getBuiltinModule?.(\"node:fs/promises\");if(!n)throw Error(`cannot read ${t.href} in this runtime; pass the .wasm bytes or a Response to init() instead`);return n.readFile(t)}async function K(t,e){if(!t.ok)throw Error(`failed to load WASM from ${t.url}: HTTP ${t.status}`);if(typeof WebAssembly.instantiateStreaming===\"function\"&&t.headers.get(\"Content-Type\")===\"application/wasm\")return WebAssembly.instantiateStreaming(t,e);return WebAssembly.instantiate(await t.arrayBuffer(),e)}async function U(t,e,n){let i={[e]:n},o=await t;if(typeof o===\"string\"||o instanceof URL){let a=new URL(o,globalThis.location?.href);o=a.protocol===\"file:\"?await J(a):await fetch(a)}let s;if(o instanceof WebAssembly.Module)s={module:o,instance:await WebAssembly.instantiate(o,i)};else if(o instanceof Response)s=await K(o,i);else s=await WebAssembly.instantiate(o,i);return n.__wbg_set_wasm(s.instance.exports),{memory:s.instance.exports.memory,module:s.module}}function P(t,e){if(t.length===1)return t[0];let n=new Uint8Array(e),i=0;for(let o of t)n.set(o,i),i+=o.length;return n}function B(t,e,n){try{let i=e.input_ptr(),o=e.output_ptr(),s=e.input_capacity(),a=[],u=0,d=0;do{let l=0;if(e.needs_input())l=Math.min(s,n.length-u),new Uint8Array(t.buffer,i,l).set(n.subarray(u,u+l)),u+=l;let b=e.encode(l,u===n.length);if(b>0)a.push(new Uint8Array(t.buffer,o,b).slice());d+=b}while(!e.finished());return P(a,d)}finally{e.free()}}var A=globalThis,k=A.self?void 0:A.process.getBuiltinModule(\"node:worker_threads\").parentPort,S=A.self??k,E;async function T(t){if(\"module\"in t){E=U(t.module,\"./defuss_brotli_compressor_bg.js\",R).then((e)=>e.memory);return}try{let{part:e,total:n,quality:i,lgwin:o,dictionary:s}=t,a=await E,u=B(a,p.part(e.length,n,i,o,s),e);S.postMessage({id:t.id,output:u},[u.buffer])}catch(e){S.postMessage({id:t.id,error:e instanceof Error?e.message:String(e)})}}if(k)k.on(\"message\",(t)=>void T(t));else A.self.onmessage=(t)=>void T(t.data);})();\n";async function lt(t){let r=globalThis.process?.getBuiltinModule?.("node:fs/promises");if(!r)throw Error(`cannot read ${t.href} in this runtime; pass the .wasm bytes or a Response to init() instead`);return r.readFile(t)}async function pt(t,e){if(!t.ok)throw Error(`failed to load WASM from ${t.url}: HTTP ${t.status}`);if(typeof WebAssembly.instantiateStreaming==="function"&&t.headers.get("Content-Type")==="application/wasm")return WebAssembly.instantiateStreaming(t,e);return WebAssembly.instantiate(await t.arrayBuffer(),e)}async function V(t,e,r){let i={[e]:r},n=await t;if(typeof n==="string"||n instanceof URL){let a=new URL(n,globalThis.location?.href);n=a.protocol==="file:"?await lt(a):await fetch(a)}let s;if(n instanceof WebAssembly.Module)s={module:n,instance:await WebAssembly.instantiate(n,i)};else if(n instanceof Response)s=await pt(n,i);else s=await WebAssembly.instantiate(n,i);return r.__wbg_set_wasm(s.instance.exports),{memory:s.instance.exports.memory,module:s.module}}function R(t,e){if(t.length===1)return t[0];let r=new Uint8Array(e),i=0;for(let n of t)r.set(n,i),i+=n.length;return r}function j(t,e,r){try{let i=e.input_ptr(),n=e.output_ptr(),s=e.input_capacity(),a=[],l=0,u=0;do{let p=0;if(e.needs_input())p=Math.min(s,r.length-l),new Uint8Array(t.buffer,i,p).set(r.subarray(l,l+p)),l+=p;let _=e.encode(p,l===r.length);if(_>0)a.push(new Uint8Array(t.buffer,n,_).slice());u+=_}while(!e.finished());return R(a,u)}finally{e.free()}}function ct(t,e,r){if(typeof Worker==="function"){let a=URL.createObjectURL(new Blob([t],{type:"text/javascript"})),l=new Worker(a);return l.onmessage=(u)=>e(u.data),l.onerror=(u)=>{u.preventDefault(),r(Error(`compressor worker failed: ${u.message}`))},{post:(u,p)=>l.postMessage(u,p),terminate:()=>{l.terminate(),URL.revokeObjectURL(a)}}}let n=globalThis.process?.getBuiltinModule?.("node:worker_threads");if(!n)return null;let s=new n.Worker(t,{eval:!0});return s.on("message",e),s.on("error",r),{post:(a,l)=>s.postMessage(a,l),terminate:()=>void s.terminate()}}function G(t,e,r,i){return new Promise((n,s)=>{let a=[],l=[],u=0,p=0,_=!1,d=(m)=>{if(_)return;_=!0;for(let f of l)f.terminate();if(m)s(m);else n(a)},A=(m)=>{if(u===r.length)return;let f=u++,y=r[f];m.post({id:f,...y},[y.part.buffer,y.dictionary.buffer])};for(let m=0;m<i&&!_;m++){let f=null;try{f=ct(t,(y)=>{if("error"in y)return d(Error(y.error));if(a[y.id]=y.output,++p===r.length)d();else A(f)},d)}catch{if(m===0)return n(null);break}if(!f)return n(null);l.push(f),f.post({module:e},[]),A(f)}})}var H=11,Y=22,E=8,J=32768,M=null,B=!1,x=null,S=null;function dt(t,e){let r=Math.max(Math.ceil(t/E),J);if(e<2||t<=r)return[[0,t]];let i=[];for(let n=0;n<t;n+=r)i.push([n,Math.min(n+r,t)]);return i}function ft(t,e){let r=new h;try{let i=r.input_ptr(),n=r.output_ptr(),s=r.input_capacity(),a=[],l=0,u=(p)=>{if(p>0)a.push(new Uint8Array(t.buffer,n,p).slice());l+=p};for(let p of e){r.next_part();for(let _=0;_<p.length;_+=s){let d=p.subarray(_,_+s);new Uint8Array(t.buffer,i,d.length).set(d);let A=d.length;do u(r.join(A)),A=0;while(!r.needs_input())}}while(!r.finished())u(r.finish());return R(a,l)}finally{r.free()}}function bt(){let t=globalThis,e=t.navigator?.hardwareConcurrency??t.process?.getBuiltinModule?.("node:os")?.availableParallelism?.();return Math.min(E,e??1)}function L(){if(!B||!x||!S)throw Error("defuss-brotli/compressor is not initialized. Call await init() first.")}function O(t={}){let e=t.quality??H,r=t.lgwin??Y;if(!Number.isInteger(e)||e<0||e>11)throw Error("quality must be an integer between 0 and 11");if(!Number.isInteger(r)||r<10||r>24)throw Error("lgwin must be an integer between 10 and 24");return{quality:e,lgwin:r}}function K(t){if(!M)M=Promise.resolve().then(()=>V(t(),"./defuss_brotli_compressor_bg.js",T)).then((e)=>{x=e.memory,S=e.module,B=!0},(e)=>{throw M=null,e});return M}function gt(){return B}function F(t,e={}){L();let{quality:r,lgwin:i}=O(e);return j(x,new g(t.length,r,i),t)}async function mt(t,e={}){L();let{quality:r,lgwin:i}=O(e),n=e.threads??bt();if(!Number.isInteger(n)||n<1)throw Error("threads must be a positive integer");let s=dt(t.length,r);if(s.length===1)return F(t,{quality:r,lgwin:i});let a=2**i-16,l=(_,d)=>new Uint8Array(t.subarray(_,d)),u=s.map(([_,d])=>({total:t.length,quality:r,lgwin:i,part:l(_,d),dictionary:l(Math.max(0,_-a),_)})),p=(n>1?await G(q,S,u,Math.min(n,u.length)):null)??u.map((_)=>j(x,g.part(_.part.length,_.total,r,i,_.dictionary),_.part));return ft(x,p)}function yt(t,e={}){return F(new TextEncoder().encode(t),e)}function Tt(t){return K(()=>t??new URL("./compressor.wasm",import.meta.url))}export{Y as DEFAULT_LGWIN,H as DEFAULT_QUALITY,J as MIN_PART_SIZE,E as PARALLEL_PARTS,L as assertReady,F as compress,mt as compressParallel,yt as compressText,Tt as init,gt as isReady,O as normalizeOptions};
|
package/dist/compressor.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { DEFAULT_LGWIN, DEFAULT_QUALITY, assertReady, compress, compressText, isReady, normalizeOptions, type BrotliCompressOptions, } from "./compressor-core.js";
|
|
1
|
+
export { DEFAULT_LGWIN, DEFAULT_QUALITY, MIN_PART_SIZE, PARALLEL_PARTS, assertReady, compress, compressParallel, compressText, isReady, normalizeOptions, type BrotliCompressOptions, type BrotliParallelOptions, } from "./compressor-core.js";
|
|
2
2
|
/**
|
|
3
3
|
* Load the Brotli compressor WASM module. Must be called (and awaited) once
|
|
4
4
|
* before any `compress` / `compressText` call.
|