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 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 6
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,260 B | 85,609 B | 85,064 B | 50,801 B | 78,590 B |
154
- | `defuss-brotli/decompressor` | `decompressor.js` | 252,184 B | 109,982 B | 111,509 B | 92,957 B | 101,421 B |
155
- | `defuss-brotli/compressor/slim` | `compressor-slim.js` + `compressor.wasm` | 938,084 B | 447,231 B | 503,917 B | 394,522 B | 400,635 B |
156
- | `defuss-brotli/compressor` | `compressor.js` | 1,249,264 B | 599,668 B | 655,950 B | 500,472 B | 520,192 B |
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
- For typical web text (JSON, Markdown, HTML, CSS, JS):
298
-
299
- - `quality = 6`
382
+ - `quality = 11`
300
383
  - `lgwin = 22`
301
384
 
302
- That is a sane point on the speed/ratio curve. Crank `quality` higher if you
303
- need smaller output and can afford more CPU time.
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
- `tsc` then generates the declaration files.
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 6
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
- /** Default compression quality. Good balance of speed and ratio for web text. */
33
- export declare const DEFAULT_QUALITY = 6;
34
- /** Default sliding window exponent. Covers most web-text repetition patterns. */
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
@@ -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};
@@ -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.