Add existing to tracked
This commit is contained in:
+30
@@ -0,0 +1,30 @@
|
||||
# .github/workflows/publish.yml
|
||||
|
||||
name: Publish
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
publish_jsr:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: npx jsr publish
|
||||
|
||||
publish_npm:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# Setup .npmrc file to publish to npm
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '24'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
- run: npm ci
|
||||
- run: npm publish
|
||||
+284
@@ -0,0 +1,284 @@
|
||||
# Benchmarks
|
||||
|
||||
A fair, reproducible comparison of **@zip.js/zip.js** against
|
||||
[jszip](https://github.com/Stuk/jszip), [fflate](https://github.com/101arrowz/fflate)
|
||||
and [archiver](https://github.com/archiverjs/node-archiver) on a set of realistic
|
||||
workloads.
|
||||
|
||||
The numbers below are honest: zip.js wins clearly on some workloads and loses on
|
||||
others. The goal is to show *where* each library is the right tool, and to give you a
|
||||
harness you can re-run on your own hardware — the results are machine-specific and you
|
||||
should not trust anyone's benchmark (including this one) without reproducing it.
|
||||
|
||||
> **TL;DR** — For compressing large or multiple entries, zip.js is the fastest option
|
||||
> in the field, and it is the only one that parallelizes compression across CPU cores
|
||||
> **without spawning a single Web Worker** — it lets the platform's native
|
||||
> `CompressionStream` run on the threadpool while you simply issue concurrent `add()`
|
||||
> calls. It also streams arbitrarily large files at a flat, low memory ceiling. For
|
||||
> deflating thousands of tiny buffers in one shot, **fflate** remains the throughput
|
||||
> and footprint champion. Against native tooling, zip.js on a modern runtime is a bit
|
||||
> faster *and* tighter than 7-Zip's fast mode on single-file streaming; 7-Zip keeps a
|
||||
> real edge only on thousands of tiny files.
|
||||
|
||||
## Environment
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Machine | Apple M2, 8 cores (4 performance + 4 efficiency), 16 GB RAM |
|
||||
| OS | macOS 26.5.1 (arm64) |
|
||||
| Runtime | Node.js v24.12.0 |
|
||||
| zip.js | 2.8.29 |
|
||||
| jszip | 3.10.1 |
|
||||
| fflate | 0.8.3 |
|
||||
| archiver | 8.0.0 |
|
||||
|
||||
## Method
|
||||
|
||||
- **Isolation.** Each `(library, operation, workload)` combination runs in its own
|
||||
freshly-spawned Node process under `/usr/bin/time -l`, so there is no cross-library
|
||||
GC or heap contamination and peak memory is a true per-library figure.
|
||||
- **Timing.** `performance.now()` around the measured operation only. Every combination
|
||||
runs **3 times**; the table reports the **median**.
|
||||
- **Memory.** Peak resident set size (RSS) reported by `/usr/bin/time -l`. The "peak"
|
||||
column is the delta over an empty-process baseline (~42 MB), i.e. the memory
|
||||
attributable to the work.
|
||||
- **Fair work.** All libraries compress at **DEFLATE level 6**. Output sizes are shown
|
||||
so you can confirm each library did equivalent work. The corpus is generated from a
|
||||
seeded PRNG, so every library sees byte-for-byte identical input.
|
||||
- **zip.js modes.** zip.js is measured both **single-threaded** (apples-to-apples with
|
||||
the single-threaded libraries) and with its **Web Worker** pool, so the worker
|
||||
overhead is never hidden.
|
||||
|
||||
## Compression — single process, single thread
|
||||
|
||||
Level-6 DEFLATE, one entry (or one batch) compressed in the main thread. This is the
|
||||
apples-to-apples comparison against the single-threaded libraries.
|
||||
|
||||
| Workload | @zip.js/zip.js | jszip | fflate | archiver |
|
||||
|---|--:|--:|--:|--:|
|
||||
| Compressible text (20 MB) | **795 ms** | 1770 ms | 953 ms | 720 ms |
|
||||
| Incompressible data (20 MB) | 446 ms | 883 ms | **306 ms** | 366 ms |
|
||||
| Already-compressed media (20 MB) | 452 ms | 886 ms | **298 ms** | 358 ms |
|
||||
| 5,000 files × ~2 KB | 845 ms | 887 ms | **287 ms** | 419 ms |
|
||||
|
||||
Peak memory for the same runs (Δ over baseline):
|
||||
|
||||
| Workload | @zip.js/zip.js | jszip | fflate | archiver |
|
||||
|---|--:|--:|--:|--:|
|
||||
| Compressible text (20 MB) | 92 MB | 65 MB | **60 MB** | 82 MB |
|
||||
| Incompressible data (20 MB) | 141 MB | 93 MB | 106 MB | **83 MB** |
|
||||
| Already-compressed media (20 MB) | 142 MB | 94 MB | 106 MB | **83 MB** |
|
||||
| 5,000 files × ~2 KB | 266 MB | 270 MB | **102 MB** | 118 MB |
|
||||
|
||||
zip.js is fastest on compressible text and competitive with archiver elsewhere, at
|
||||
roughly comparable compressed sizes. **fflate** is the clear winner on raw throughput
|
||||
and footprint for incompressible data and for large numbers of tiny files — if that is
|
||||
your workload, use fflate.
|
||||
|
||||
## Parallelism & codec backends — 8 files × 8 MB, single process
|
||||
|
||||
This is the headline. The same 64 MB of compressible entries, compressed several ways
|
||||
in a **plain Node process** (no worker threads unless noted). zip.js can select its
|
||||
codec backend at runtime — native `CompressionStream`, the bundled WebAssembly zlib, or
|
||||
a pure-JavaScript zlib port — and it can issue `add()` calls concurrently.
|
||||
|
||||
| Configuration | Median time | vs jszip |
|
||||
|---|--:|--:|
|
||||
| **zip.js — `CompressionStream`, concurrent `add()`** | **657 ms** | **8.8×** |
|
||||
| fflate — async (its own worker pool) | 729 ms | 7.9× |
|
||||
| archiver — Node zlib (libuv threadpool) | 2266 ms | 2.5× |
|
||||
| zip.js — `CompressionStream`, sequential | 2453 ms | 2.3× |
|
||||
| fflate — `zipSync` (single thread) | 3103 ms | 1.9× |
|
||||
| zip.js — WASM zlib | 4176 ms | 1.4× |
|
||||
| zip.js — pure-JS zlib | 4709 ms | 1.2× |
|
||||
| jszip (pako, single thread) | 5758 ms | 1.0× |
|
||||
|
||||
**The point:** zip.js with the native `CompressionStream` goes from **2453 ms
|
||||
sequential to 657 ms with concurrent `add()` — a 3.7× speedup — using no Web Workers at
|
||||
all.** The native codec runs on the platform's threadpool, so independent entries
|
||||
compress on multiple cores while your code stays on the main thread. That makes it the
|
||||
fastest configuration measured, narrowly ahead of fflate's dedicated worker pool.
|
||||
|
||||
One honest caveat, visible in the table: **only the native `CompressionStream` backend
|
||||
parallelizes this way.** The WASM and pure-JS backends run synchronously on the main
|
||||
thread, so concurrent `add()` does not speed them up (4176 ms and 4709 ms whether
|
||||
sequential or "parallel"). Use those backends when a native `CompressionStream` is
|
||||
unavailable or when you need byte-identical zlib output; use the native backend when you
|
||||
want this parallelism.
|
||||
|
||||
There is a second way to land on the WASM backend by accident: because
|
||||
`CompressionStream` exposes no level control, requesting any non-default compression
|
||||
level (e.g. `{ level: 5 }`) makes zip.js fall back to the WASM zlib codec — which, per
|
||||
the table, does not parallelize via concurrent `add()`. Keep the default level to keep
|
||||
the native-backend parallelism, or pair a custom level with Web Workers (below).
|
||||
|
||||
### Parallelism is runtime-dependent
|
||||
|
||||
The table above is measured on **Node**, and its "no Web Workers needed" result does
|
||||
**not** hold on every runtime: concurrent `add()` only spreads across cores if the
|
||||
runtime runs `CompressionStream` off the main thread. Same 8 × 8 MB workload, level 6,
|
||||
median of 3:
|
||||
|
||||
| Runtime | sequential | concurrent `add()` | concurrent `add()` + `useWebWorkers` |
|
||||
|---|--:|--:|--:|
|
||||
| Node.js | 2.94 s | **0.74 s** | 0.74 s |
|
||||
| Bun | 1.80 s | **0.36 s** | 0.46 s |
|
||||
| Deno | 1.84 s | 1.82 s | **0.47 s** |
|
||||
|
||||
- **Node and Bun** back `CompressionStream` with a threadpool, so concurrent `add()`
|
||||
alone parallelizes — no Web Workers needed (Bun is fastest here).
|
||||
- **Deno** runs `CompressionStream` on the isolate thread, so concurrent `add()` alone
|
||||
gives no speedup (1.82 s ≈ its 1.84 s sequential). Set `useWebWorkers: true` and it
|
||||
parallelizes properly (0.47 s), landing right beside the others.
|
||||
- **Browsers vary by engine.** Safari/WebKit runs `CompressionStream` on the main thread
|
||||
(serial, like Deno), so use `useWebWorkers: true` there. Chromium implements it
|
||||
separately and may behave differently — check a given browser by compressing several
|
||||
large buffers through `CompressionStream` sequentially versus concurrently and comparing
|
||||
the wall time.
|
||||
|
||||
**Rule of thumb:** on Node and Bun, concurrent `add()` is enough; on Deno and
|
||||
Safari/WebKit, also set `useWebWorkers: true`. Web Workers are the portable way to get
|
||||
this parallelism on any runtime — and the only way once you use a non-default level
|
||||
(which switches to the WASM codec).
|
||||
|
||||
## Decompression
|
||||
|
||||
Level-6 archives, read back and fully materialized. archiver has no unzip API, so it is
|
||||
excluded.
|
||||
|
||||
| Workload | @zip.js/zip.js | jszip | fflate |
|
||||
|---|--:|--:|--:|
|
||||
| Compressible text (20 MB) | **65 ms** | 143 ms | 82 ms |
|
||||
| 5,000 files × ~2 KB | 565 ms | 453 ms | **89 ms** |
|
||||
|
||||
zip.js has the fastest large-stream decompression. On thousands of tiny entries the
|
||||
per-entry setup cost dominates and **fflate is dramatically faster and lighter** — again
|
||||
the right tool when you are unpacking many small files.
|
||||
|
||||
## Streaming a large file — 256 MB, disk → zip → disk
|
||||
|
||||
The input is streamed from disk and the archive is streamed back to disk; neither is
|
||||
ever fully held in memory (for the libraries that support it).
|
||||
|
||||
| Library | Median time | Peak memory |
|
||||
|---|--:|--:|
|
||||
| archiver | 9183 ms | 111 MB |
|
||||
| zip.js (workers) | 9893 ms | **99 MB** |
|
||||
| zip.js (1 thread) | 10110 ms | **99 MB** |
|
||||
| fflate | 10613 ms | 87 MB |
|
||||
| jszip | 22358 ms | 527 MB |
|
||||
|
||||
zip.js, fflate and archiver all hold memory **flat** while streaming — zip.js peaks at
|
||||
~99 MB regardless of the 256 MB input, thanks to real backpressure through the
|
||||
compression pipeline. **jszip buffers the entire file** and needs ~527 MB, at more than
|
||||
twice the wall-clock time. If you process files that do not fit comfortably in memory,
|
||||
avoid jszip.
|
||||
|
||||
## Against native tooling — 7-Zip
|
||||
|
||||
How far is zip.js from a native archiver? [`benchmarks/bench-7z.js`](benchmarks/bench-7z.js)
|
||||
compares it against the `7zz` CLI (7-Zip 25.01), disk-to-disk on both sides, under Deno
|
||||
2.9.3 and Bun 1.3.14 (both runtimes agree within noise; Deno numbers shown). 7-Zip
|
||||
timings include process spawn (~ms).
|
||||
|
||||
One comparison is impossible to make perfectly fair: **no 7-Zip preset runs zlib's
|
||||
algorithm.** `-mx=5+` is a near-optimal parser (much slower, smaller output) and
|
||||
`-mx=1..4` a greedy one (faster, larger output) — they bracket zlib level 6. So the
|
||||
table carries two anchors: `-mx=6` as the *ratio* anchor and `-mx=1` as the *speed*
|
||||
anchor. On this machine the `-mx` ladder on the 256 MB file reads: `-mx=1/3` → 5.9 s,
|
||||
`-mx=5/6` → 34 s, `-mx=7` → 85 s — the 6× jump at `-mx=5` is the switch from greedy
|
||||
matching to optimal parsing, and it cannot be hidden by threads (one file = one deflate
|
||||
stream = one core).
|
||||
|
||||
| Workload (compress) | zip.js (workers) | 7-Zip `-mx=6` (mt) | 7-Zip `-mx=1` (mt) |
|
||||
|---|--:|--:|--:|
|
||||
| Compressible text (20 MB) | **426 ms** / 6.2 MB | 2606 ms / 5.8 MB | 470 ms / 6.8 MB |
|
||||
| Incompressible data (20 MB) | **342 ms** | 410 ms | 372 ms |
|
||||
| 5,000 files × ~2 KB | 780 ms / 5.5 MB | 199 ms / 4.9 MB | **168 ms** / 5.0 MB |
|
||||
| Large file, disk-to-disk (256 MB) | **5.5 s** / 79.5 MB | 33.4 s / 73.6 MB | 5.9 s / 86.7 MB |
|
||||
|
||||
| Workload (decompress) | zip.js (workers) | 7-Zip (mt) |
|
||||
|---|--:|--:|
|
||||
| Compressible text (20 MB) | **69 ms** | 88 ms |
|
||||
| 5,000 files × ~2 KB | 976 ms | **471 ms** |
|
||||
|
||||
- **On single-file streaming, zip.js strictly dominates 7-Zip's speed tier**: a bit
|
||||
faster *and* ~9 % smaller output, because zlib-6's lazy matching is a better
|
||||
speed/ratio point than 7-Zip's greedy fast mode. What `-mx=6` buys for its 6× time is
|
||||
~7 % more compression — a trade zip.js cannot make, but also one most workloads don't
|
||||
want.
|
||||
- **7-Zip legitimately dominates many small files** (~4× on compress, ~2× on
|
||||
decompress): its per-entry cost is near zero, while zip.js pays per-entry
|
||||
orchestration (worker round trips, header writes, one output stream per extracted
|
||||
file). Same lesson as the fflate rows above — tiny-entry workloads are zip.js's cost
|
||||
center, and the codec backend is irrelevant there.
|
||||
|
||||
## The runtime's zlib decides zip.js throughput
|
||||
|
||||
On bulk data zip.js is a thin wrapper around the platform's `CompressionStream` — it
|
||||
adds ~7 % over the raw encoder on the 256 MB stream. That means throughput is decided
|
||||
by **which zlib the runtime vendors**, and they differ a lot. Same 256 MB compressible
|
||||
file, one thread, level-6-class output everywhere:
|
||||
|
||||
| Encoder | Time | Output |
|
||||
|---|--:|--:|
|
||||
| zlib-ng — Deno & Bun `CompressionStream` | **4.8–5.1 s** | 79.4 MB |
|
||||
| Chromium zlib — Node `CompressionStream` / `node:zlib` | 8.9 s | 78.3 MB |
|
||||
| classic zlib — Apple `gzip -6` | 12.5 s | 78.9 MB |
|
||||
| classic zlib compiled to WASM — zip.js `useCompressionStream: false` | ~16 s | 78.9 MB |
|
||||
| (reference) 7-Zip `-mx=6` | 33.5 s | 73.6 MB |
|
||||
|
||||
Verified in the runtimes' sources: Deno builds `flate2` with vendored
|
||||
**zlib-ng** (`__vendored_zlib_ng` default feature), Bun vendors **zlib-ng 2.3.3**
|
||||
(SIMD CRC/adler/match kernels, NEON on arm64) behind `node:zlib` and
|
||||
`CompressionStream`, and Node vendors **Chromium's zlib fork** (SIMD checksums and
|
||||
hash sliding, but a match finder much closer to classic zlib).
|
||||
|
||||
Consequences worth knowing:
|
||||
|
||||
- **The same zip.js code runs ~1.75× faster on Deno/Bun than on Node** for bulk
|
||||
compression. A "zip.js is fast/slow" measurement is often really a statement about
|
||||
the host's zlib — and the Node tables above are the *pessimistic* end.
|
||||
- **The WASM backend is the portability floor, not a peer**: classic zlib plus ~30 %
|
||||
WebAssembly overhead ≈ 3× slower than native on compress. Decompression barely
|
||||
suffers (inflate is cheap — within ~10 % of native). It still lands mid-curve
|
||||
against 7-Zip: 16 s / 78.9 MB sits between `-mx=1` (5.9 s / 86.7 MB) and `-mx=6`
|
||||
(33.5 s / 73.6 MB).
|
||||
- Output sizes across the zlib family are interchangeable (78–79 MB): it is one
|
||||
algorithm at four levels of implementation tuning. zip.js inherits whichever the
|
||||
host provides — including future upgrades, for free.
|
||||
|
||||
## When to pick which
|
||||
|
||||
- **Choose zip.js** for the fastest compression of large or multiple entries
|
||||
(parallelism with no Web Workers), the fastest large-stream decompression, flat
|
||||
low-memory streaming of huge files, and the broadest ZIP feature set in one library —
|
||||
AES & ZipCrypto encryption, Zip64, split/multi-volume archives, and an optional Web
|
||||
Worker pool.
|
||||
- **Choose fflate** when you deflate thousands of tiny buffers in a single call and want
|
||||
the smallest memory footprint and the highest raw synchronous throughput.
|
||||
- **archiver** is a solid streaming compressor on Node but cannot read archives.
|
||||
- **jszip** is convenient but the slowest here and buffers whole files in memory.
|
||||
|
||||
## Reproduce
|
||||
|
||||
The harness lives in [`benchmarks/`](benchmarks/). It has no ties to the machine above;
|
||||
run it on yours.
|
||||
|
||||
```sh
|
||||
cd benchmarks
|
||||
npm install # jszip, fflate, archiver (zip.js is used from the repo)
|
||||
npm run corpus # generate the deterministic datasets under .corpus/
|
||||
node bench.js # the head-to-head tables (compress / decompress / disk streaming)
|
||||
node bench-backends.js # the parallelism & codec-backend matrix
|
||||
deno run -A bench-7z.js # zip.js vs the 7zz CLI (also: bun bench-7z.js)
|
||||
```
|
||||
|
||||
`bench-7z.js` needs the 7-Zip CLI (`brew install sevenzip`) and runs under Deno or Bun;
|
||||
set `ZIPJS_BACKEND=wasm` to measure the WebAssembly codec instead of the native
|
||||
`CompressionStream`, and `SKIP_HUGE=1` to skip the 256 MB combo.
|
||||
|
||||
Both scripts write JSON and a human-readable log to `benchmarks/results/`. Set
|
||||
`RUNS=<n>` to change the number of repetitions (default 3). The datasets are generated
|
||||
from a seeded PRNG (`benchmarks/lib/corpus.js`), so every run — and every library —
|
||||
sees identical bytes.
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2023, Gildas Lormeau
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
# Introduction
|
||||
|
||||
zip.js is a JavaScript open-source library (BSD-3-Clause license) for
|
||||
compressing and decompressing zip files. It has been designed to handle large amounts
|
||||
of data. It supports notably multi-core compression, native compression with
|
||||
compression streams, archives larger than 4GB with Zip64, split zip files, data
|
||||
encryption, and Deflate64 decompression.
|
||||
|
||||
# Demo
|
||||
|
||||
See https://gildas-lormeau.github.io/zip-manager
|
||||
|
||||
# Documentation
|
||||
|
||||
See here for more info: https://gildas-lormeau.github.io/zip.js/
|
||||
|
||||
# Examples
|
||||
|
||||
## Hello world
|
||||
|
||||
```js
|
||||
import {
|
||||
BlobReader,
|
||||
BlobWriter,
|
||||
TextReader,
|
||||
TextWriter,
|
||||
ZipReader,
|
||||
ZipWriter
|
||||
} from "@zip.js/zip.js";
|
||||
// "jsr:@zip-js/zip-js" for Deno
|
||||
|
||||
// ----
|
||||
// Write the zip file
|
||||
// ----
|
||||
|
||||
// Creates a BlobWriter object where the zip content will be written.
|
||||
const zipFileWriter = new BlobWriter();
|
||||
// Creates a TextReader object storing the text of the entry to add in the zip
|
||||
// (i.e. "Hello world!").
|
||||
const helloWorldReader = new TextReader("Hello world!");
|
||||
|
||||
// Creates a ZipWriter object writing data via `zipFileWriter`, adds the entry
|
||||
// "hello.txt" containing the text "Hello world!" via `helloWorldReader`, and
|
||||
// closes the writer.
|
||||
const zipWriter = new ZipWriter(zipFileWriter);
|
||||
await zipWriter.add("hello.txt", helloWorldReader);
|
||||
await zipWriter.close();
|
||||
|
||||
// Retrieves the Blob object containing the zip content into `zipFileBlob`. It
|
||||
// is also returned by zipWriter.close() for more convenience.
|
||||
const zipFileBlob = await zipFileWriter.getData();
|
||||
|
||||
// ----
|
||||
// Read the zip file
|
||||
// ----
|
||||
|
||||
// Creates a BlobReader object used to read `zipFileBlob`.
|
||||
const zipFileReader = new BlobReader(zipFileBlob);
|
||||
// Creates a TextWriter object where the content of the first entry in the zip
|
||||
// will be written.
|
||||
const helloWorldWriter = new TextWriter();
|
||||
|
||||
// Creates a ZipReader object reading the zip content via `zipFileReader`,
|
||||
// retrieves metadata (name, dates, etc.) of the first entry, retrieves its
|
||||
// content via `helloWorldWriter`, and closes the reader.
|
||||
const zipReader = new ZipReader(zipFileReader);
|
||||
const firstEntry = (await zipReader.getEntries()).shift();
|
||||
const helloWorldText = await firstEntry.getData(helloWorldWriter);
|
||||
await zipReader.close();
|
||||
|
||||
// Displays "Hello world!".
|
||||
console.log(helloWorldText);
|
||||
```
|
||||
|
||||
Run the code on JSFiddle: https://jsfiddle.net/tm9fhvab/
|
||||
|
||||
## Hello world with Streams
|
||||
|
||||
```js
|
||||
import {
|
||||
BlobReader,
|
||||
ZipReader,
|
||||
ZipWriter
|
||||
} from "@zip-js/zip-js";
|
||||
// Prefix "@zip-js/zip-js" with "jsr:" for Deno
|
||||
|
||||
// ----
|
||||
// Write the zip file
|
||||
// ----
|
||||
|
||||
// Creates a TransformStream object, the zip content will be written in the
|
||||
// `writable` property.
|
||||
const zipFileStream = new TransformStream();
|
||||
// Creates a Promise object resolved to the zip content returned as a Blob
|
||||
// object retrieved from `zipFileStream.readable`.
|
||||
const zipFileBlobPromise = new Response(zipFileStream.readable).blob();
|
||||
// Creates a ReadableStream object storing the text of the entry to add in the
|
||||
// zip (i.e. "Hello world!").
|
||||
const helloWorldReadable = new Blob(["Hello world!"]).stream();
|
||||
|
||||
// Creates a ZipWriter object writing data into `zipFileStream.writable`, adds
|
||||
// the entry "hello.txt" containing the text "Hello world!" retrieved from
|
||||
// `helloWorldReadable`, and closes the writer.
|
||||
const zipWriter = new ZipWriter(zipFileStream.writable);
|
||||
await zipWriter.add("hello.txt", helloWorldReadable);
|
||||
await zipWriter.close();
|
||||
|
||||
// Retrieves the Blob object containing the zip content into `zipFileBlob`.
|
||||
const zipFileBlob = await zipFileBlobPromise;
|
||||
|
||||
// ----
|
||||
// Read the zip file
|
||||
// ----
|
||||
|
||||
// Creates a BlobReader object used to read `zipFileBlob`.
|
||||
const zipFileReader = new BlobReader(zipFileBlob);
|
||||
// Creates a TransformStream object, the content of the first entry in the zip
|
||||
// will be written in the `writable` property.
|
||||
const helloWorldStream = new TransformStream();
|
||||
// Creates a Promise object resolved to the content of the first entry returned
|
||||
// as text from `helloWorldStream.readable`.
|
||||
const helloWorldTextPromise = new Response(helloWorldStream.readable).text();
|
||||
|
||||
// Creates a ZipReader object reading the zip content via `zipFileReader`,
|
||||
// retrieves metadata (name, dates, etc.) of the first entry, retrieves its
|
||||
// content into `helloWorldStream.writable`, and closes the reader.
|
||||
const zipReader = new ZipReader(zipFileReader);
|
||||
const firstEntry = (await zipReader.getEntries()).shift();
|
||||
await firstEntry.getData(helloWorldStream.writable);
|
||||
await zipReader.close();
|
||||
|
||||
// Displays "Hello world!".
|
||||
const helloWorldText = await helloWorldTextPromise;
|
||||
console.log(helloWorldText);
|
||||
```
|
||||
|
||||
Run the code on JSFiddle: https://jsfiddle.net/aw3d6f4o/
|
||||
|
||||
## Adding concurrently multiple entries in a zip file
|
||||
|
||||
```js
|
||||
import {
|
||||
BlobWriter,
|
||||
HttpReader,
|
||||
TextReader,
|
||||
ZipWriter,
|
||||
} from "@zip-js/zip-js";
|
||||
// Prefix "@zip-js/zip-js" with "jsr:" for Deno
|
||||
|
||||
const README_URL = "https://unpkg.com/@zip.js/zip.js/README.md";
|
||||
getZipFileBlob()
|
||||
.then(downloadFile);
|
||||
|
||||
async function getZipFileBlob() {
|
||||
const zipWriter = new ZipWriter(new BlobWriter("application/zip"));
|
||||
await Promise.all([
|
||||
zipWriter.add("hello.txt", new TextReader("Hello world!")),
|
||||
zipWriter.add("README.md", new HttpReader(README_URL)),
|
||||
]);
|
||||
return zipWriter.close();
|
||||
}
|
||||
|
||||
function downloadFile(blob) {
|
||||
document.body.appendChild(Object.assign(document.createElement("a"), {
|
||||
download: "hello.zip",
|
||||
href: URL.createObjectURL(blob),
|
||||
textContent: "Download zip file",
|
||||
}));
|
||||
}
|
||||
```
|
||||
|
||||
Run the code on Plunker: https://plnkr.co/edit/4sVljNIpqSUE9HCA?preview
|
||||
|
||||
## Tests
|
||||
|
||||
See https://github.com/gildas-lormeau/zip.js/tree/master/tests/all
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "@zip-js/zip-js",
|
||||
"version": "2.8.34",
|
||||
"exports": {
|
||||
".": "./index.js"
|
||||
},
|
||||
"lint": {
|
||||
"exclude": [
|
||||
"dist/*.js",
|
||||
"*-inline.js",
|
||||
"tests/",
|
||||
"benchmarks/",
|
||||
"**/*.cjs",
|
||||
"**/*.min.js"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"rollup*",
|
||||
"tsconfig.json",
|
||||
".vscode",
|
||||
".eslintrc.json",
|
||||
"benchmarks",
|
||||
"docs"
|
||||
]
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
# Prebuilt zip.js bundles
|
||||
|
||||
**Note**: These bundles are not ES module compatible (you cannot `import` them directly). Use `index.js` at the project root or one of the `zip-*.js` files in [`/lib`](../lib) (e.g. `/lib/zip-core.js`) for ESM usage.
|
||||
|
||||
Contents:
|
||||
|
||||
- `zip.js` / `zip.min.js`: Full `ZipWriter` / `ZipReader` bundles with embedded Web Worker code and WASM.
|
||||
- `zip-fs.js` / `zip-fs.min.js`: Full `ZipWriter` / `ZipReader` plus virtual file system (`fs`), with embedded Web Worker code and WASM.
|
||||
- `zip-core.js` / `zip-core.min.js`: Minimal `ZipWriter` / `ZipReader`.
|
||||
- `zip-fs-core.js` / `zip-fs-core.min.js`: Minimal `ZipWriter` / `ZipReader` plus virtual file system (`fs`).
|
||||
|
||||
Online builder:
|
||||
|
||||
Alternatively, you can build your own version of zip.js with the online builder, see https://gildas-lormeau.github.io/zip.js/builder.
|
||||
|
||||
Notes:
|
||||
- Files suffixed with `-native` rely on a pure JavaScript implementation of Compression Streams instead of a WASM module.
|
||||
- `zip-legacy.min.js` is the equivalent of `zip.min.js` before the version `2.8`, it relies on a JavaScript implementation of Compression Streams (if used) but only in web workers. Alternatively `zip-native.min.js` includes the Compression Streams implementation in the main environment and the web workers.
|
||||
+7109
File diff suppressed because it is too large
Load Diff
+1
File diff suppressed because one or more lines are too long
+7465
File diff suppressed because it is too large
Load Diff
+1
File diff suppressed because one or more lines are too long
+9836
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+10150
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+7142
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
BIN
Binary file not shown.
+7180
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+7494
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+54
@@ -0,0 +1,54 @@
|
||||
import js from "@eslint/js";
|
||||
|
||||
export default [
|
||||
{
|
||||
ignores: [
|
||||
"**/node_modules/",
|
||||
".git/",
|
||||
"dist/",
|
||||
"**/*-inline.js",
|
||||
"lib/core/web-worker-inline-native.js",
|
||||
"lib/core/web-worker-inline-wasm.js",
|
||||
"lib/core/streams/zlib-js/zlib-streams.min.js",
|
||||
"tests/vendor/*.js",
|
||||
"index.cjs",
|
||||
"index.min.js",
|
||||
"index-native.cjs",
|
||||
"index-native.min.js"
|
||||
]
|
||||
},
|
||||
js.configs.recommended,
|
||||
{
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: "module",
|
||||
globals: {
|
||||
console: "readonly"
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
"indent": [
|
||||
"error",
|
||||
"tab",
|
||||
{
|
||||
"SwitchCase": 1
|
||||
}
|
||||
],
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"unix"
|
||||
],
|
||||
"quotes": [
|
||||
"error",
|
||||
"double"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"no-console": [
|
||||
"warn"
|
||||
]
|
||||
}
|
||||
}
|
||||
];
|
||||
+9828
File diff suppressed because one or more lines are too long
+31
@@ -0,0 +1,31 @@
|
||||
/// <reference types="./index.d.ts" />
|
||||
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
export * from "./lib/zip-fs-native.js";
|
||||
+1
File diff suppressed because one or more lines are too long
+10142
File diff suppressed because one or more lines are too long
+2853
File diff suppressed because it is too large
Load Diff
+2853
File diff suppressed because it is too large
Load Diff
+31
@@ -0,0 +1,31 @@
|
||||
/// <reference types="./index.d.ts" />
|
||||
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
export * from "./lib/zip-fs-wasm.js";
|
||||
+1
File diff suppressed because one or more lines are too long
+168
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global setTimeout, clearTimeout */
|
||||
|
||||
import { UNDEFINED_VALUE } from "./constants.js";
|
||||
import {
|
||||
CODEC_INFLATE,
|
||||
CODEC_DEFLATE,
|
||||
ERR_INVALID_SIGNATURE,
|
||||
ERR_INVALID_UNCOMPRESSED_SIZE,
|
||||
ERR_INVALID_PASSWORD,
|
||||
ERR_INVALID_COMPRESSED_DATA,
|
||||
ERR_ABORT_CHECK_PASSWORD
|
||||
} from "./streams/codec-stream.js";
|
||||
import { CodecWorker } from "./codec-worker.js";
|
||||
|
||||
let pool = [];
|
||||
const pendingRequests = [];
|
||||
let starvationTimeout;
|
||||
let starvationDelay;
|
||||
|
||||
export {
|
||||
runWorker,
|
||||
terminateWorkers,
|
||||
CODEC_DEFLATE,
|
||||
CODEC_INFLATE,
|
||||
ERR_INVALID_SIGNATURE,
|
||||
ERR_INVALID_UNCOMPRESSED_SIZE,
|
||||
ERR_INVALID_PASSWORD,
|
||||
ERR_INVALID_COMPRESSED_DATA,
|
||||
ERR_ABORT_CHECK_PASSWORD
|
||||
};
|
||||
|
||||
let indexWorker = 0;
|
||||
|
||||
async function runWorker(stream, workerOptions) {
|
||||
const { options, config } = workerOptions;
|
||||
const { transferStreams, useWebWorkers, useCompressionStream, compressed, signed, encrypted } = options;
|
||||
const { workerURI, maxWorkers } = config;
|
||||
workerOptions.transferStreams = transferStreams || transferStreams === UNDEFINED_VALUE;
|
||||
const streamCopy = !compressed && !signed && !encrypted && !workerOptions.transferStreams;
|
||||
workerOptions.useWebWorkers = !streamCopy && (useWebWorkers || (useWebWorkers === UNDEFINED_VALUE && config.useWebWorkers));
|
||||
workerOptions.workerURI = workerOptions.useWebWorkers && workerURI ? workerURI : UNDEFINED_VALUE;
|
||||
options.useCompressionStream = useCompressionStream || (useCompressionStream === UNDEFINED_VALUE && config.useCompressionStream);
|
||||
return (await getWorker()).run();
|
||||
|
||||
// deno-lint-ignore require-await
|
||||
async function getWorker() {
|
||||
const workerData = pool.find(workerData => !workerData.busy);
|
||||
if (workerData) {
|
||||
clearTerminateTimeout(workerData);
|
||||
return new CodecWorker(workerData, stream, workerOptions, onTaskFinished);
|
||||
} else if (pool.length < maxWorkers) {
|
||||
const workerData = { indexWorker };
|
||||
indexWorker++;
|
||||
pool.push(workerData);
|
||||
return new CodecWorker(workerData, stream, workerOptions, onTaskFinished);
|
||||
} else {
|
||||
return new Promise(resolve => {
|
||||
pendingRequests.push({ resolve, stream, workerOptions });
|
||||
starvationDelay = config.workerStarvationTimeout;
|
||||
armStarvationTimeout();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function onTaskFinished(workerData) {
|
||||
clearStarvationTimeout();
|
||||
if (pendingRequests.length) {
|
||||
const [{ resolve, stream, workerOptions }] = pendingRequests.splice(0, 1);
|
||||
resolve(new CodecWorker(workerData, stream, workerOptions, onTaskFinished));
|
||||
armStarvationTimeout();
|
||||
} else if (workerData.worker) {
|
||||
clearTerminateTimeout(workerData);
|
||||
terminateWorker(workerData, workerOptions);
|
||||
} else {
|
||||
pool = pool.filter(data => data != workerData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function armStarvationTimeout() {
|
||||
if (!starvationTimeout && pendingRequests.length && Number.isFinite(starvationDelay) && starvationDelay >= 0) {
|
||||
starvationTimeout = setTimeout(onWorkerStarvation, starvationDelay);
|
||||
}
|
||||
}
|
||||
|
||||
function clearStarvationTimeout() {
|
||||
if (starvationTimeout) {
|
||||
clearTimeout(starvationTimeout);
|
||||
starvationTimeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
function onWorkerStarvation() {
|
||||
starvationTimeout = null;
|
||||
if (pendingRequests.length) {
|
||||
const [{ resolve, stream, workerOptions }] = pendingRequests.splice(0, 1);
|
||||
const inlineWorkerOptions = Object.assign({}, workerOptions, { useWebWorkers: false, workerURI: UNDEFINED_VALUE });
|
||||
resolve(new CodecWorker({}, stream, inlineWorkerOptions, onInlineTaskFinished));
|
||||
armStarvationTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
function onInlineTaskFinished() {
|
||||
clearStarvationTimeout();
|
||||
armStarvationTimeout();
|
||||
}
|
||||
|
||||
function terminateWorker(workerData, workerOptions) {
|
||||
const { config } = workerOptions;
|
||||
const { terminateWorkerTimeout } = config;
|
||||
if (Number.isFinite(terminateWorkerTimeout) && terminateWorkerTimeout >= 0) {
|
||||
if (workerData.terminated) {
|
||||
workerData.terminated = false;
|
||||
} else {
|
||||
workerData.terminateTimeout = setTimeout(async () => {
|
||||
pool = pool.filter(data => data != workerData);
|
||||
try {
|
||||
await workerData.terminate();
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
}, terminateWorkerTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clearTerminateTimeout(workerData) {
|
||||
const { terminateTimeout } = workerData;
|
||||
if (terminateTimeout) {
|
||||
clearTimeout(terminateTimeout);
|
||||
workerData.terminateTimeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
async function terminateWorkers() {
|
||||
await Promise.allSettled(pool.map(workerData => {
|
||||
clearTerminateTimeout(workerData);
|
||||
return workerData.terminate();
|
||||
}));
|
||||
}
|
||||
+487
@@ -0,0 +1,487 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global Worker, URL, TransformStream, AbortController, structuredClone, DOMException */
|
||||
|
||||
import {
|
||||
UNDEFINED_VALUE,
|
||||
UNDEFINED_TYPE,
|
||||
FUNCTION_TYPE
|
||||
} from "./constants.js";
|
||||
import { getChunkSize } from "./configuration.js";
|
||||
import {
|
||||
CODEC_DEFLATE,
|
||||
CodecStream,
|
||||
ChunkStream,
|
||||
MESSAGE_EVENT_TYPE,
|
||||
MESSAGE_START,
|
||||
MESSAGE_PULL,
|
||||
MESSAGE_DATA,
|
||||
MESSAGE_ACK_DATA,
|
||||
MESSAGE_CLOSE
|
||||
} from "./streams/codec-stream.js";
|
||||
|
||||
const MODULE_WORKER_OPTIONS = { type: "module" };
|
||||
const ERROR_EVENT_TYPE = "error";
|
||||
const MESSAGE_ERROR_EVENT_TYPE = "messageerror";
|
||||
|
||||
let webWorkerSupported, webWorkerSource, webWorkerURI, webWorkerOptions;
|
||||
let transferStreamsSupported = true;
|
||||
try {
|
||||
transferStreamsSupported = typeof structuredClone == FUNCTION_TYPE && structuredClone(new DOMException("", "AbortError")).code !== UNDEFINED_VALUE;
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
let initModule = () => { };
|
||||
|
||||
export {
|
||||
CodecWorker,
|
||||
configureWorker
|
||||
};
|
||||
|
||||
function configureWorker({ initModule: initModuleFunction }) {
|
||||
initModule = initModuleFunction;
|
||||
}
|
||||
|
||||
class CodecWorker {
|
||||
|
||||
constructor(workerData, { readable, writable }, { options, config, streamOptions, useWebWorkers, transferStreams, workerURI }, onTaskFinished) {
|
||||
const { signal } = streamOptions;
|
||||
Object.assign(workerData, {
|
||||
busy: true,
|
||||
generation: (workerData.generation || 0) + 1,
|
||||
readable: readable
|
||||
.pipeThrough(new ChunkStream(getChunkSize(config)))
|
||||
.pipeThrough(new ProgressWatcherStream(streamOptions), { signal }),
|
||||
writable,
|
||||
options: Object.assign({}, options),
|
||||
workerURI,
|
||||
transferStreams,
|
||||
terminate() {
|
||||
return new Promise(resolve => {
|
||||
const { worker, busy } = workerData;
|
||||
if (worker) {
|
||||
if (busy) {
|
||||
workerData.resolveTerminated = resolve;
|
||||
} else {
|
||||
worker.terminate();
|
||||
resolve();
|
||||
}
|
||||
workerData.interface = null;
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
},
|
||||
onTaskFinished() {
|
||||
if (workerData.busy) {
|
||||
const { resolveTerminated } = workerData;
|
||||
if (resolveTerminated) {
|
||||
workerData.resolveTerminated = null;
|
||||
workerData.terminated = true;
|
||||
workerData.worker.terminate();
|
||||
resolveTerminated();
|
||||
}
|
||||
workerData.busy = false;
|
||||
onTaskFinished(workerData);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (webWorkerSupported === UNDEFINED_VALUE) {
|
||||
// deno-lint-ignore valid-typeof
|
||||
webWorkerSupported = typeof Worker != UNDEFINED_TYPE;
|
||||
}
|
||||
return (useWebWorkers && webWorkerSupported ? createWebWorkerInterface : createWorkerInterface)(workerData, config);
|
||||
}
|
||||
}
|
||||
|
||||
class ProgressWatcherStream extends TransformStream {
|
||||
|
||||
constructor({ onstart, onprogress, size, onend }) {
|
||||
let chunkOffset = 0;
|
||||
super({
|
||||
async start() {
|
||||
if (onstart) {
|
||||
await callHandler(onstart, size);
|
||||
}
|
||||
},
|
||||
async transform(chunk, controller) {
|
||||
chunkOffset += chunk.length;
|
||||
if (onprogress) {
|
||||
await callHandler(onprogress, chunkOffset, size);
|
||||
}
|
||||
controller.enqueue(chunk);
|
||||
},
|
||||
async flush() {
|
||||
if (onend) {
|
||||
await callHandler(onend, chunkOffset);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function callHandler(handler, ...parameters) {
|
||||
try {
|
||||
await handler(...parameters);
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
function createWorkerInterface(workerData, config) {
|
||||
return {
|
||||
run: () => runWorker(workerData, config)
|
||||
};
|
||||
}
|
||||
|
||||
function createWebWorkerInterface(workerData, config) {
|
||||
const { baseURI, chunkSize } = config;
|
||||
let { wasmURI } = config;
|
||||
|
||||
if (!workerData.interface) {
|
||||
// deno-lint-ignore valid-typeof
|
||||
if (typeof wasmURI == FUNCTION_TYPE) {
|
||||
wasmURI = wasmURI();
|
||||
}
|
||||
let worker;
|
||||
try {
|
||||
worker = getWebWorker(workerData.workerURI, baseURI, workerData);
|
||||
} catch {
|
||||
webWorkerSupported = false;
|
||||
return createWorkerInterface(workerData, config);
|
||||
}
|
||||
Object.assign(workerData, {
|
||||
worker,
|
||||
terminated: false,
|
||||
interface: {
|
||||
run: () => runWebWorker(workerData, { chunkSize, wasmURI, baseURI })
|
||||
}
|
||||
});
|
||||
}
|
||||
return workerData.interface;
|
||||
}
|
||||
|
||||
async function runWorker({ options, readable, writable, onTaskFinished }, config) {
|
||||
let codecStream;
|
||||
try {
|
||||
if (!options.useCompressionStream) {
|
||||
try {
|
||||
await initModule(config);
|
||||
} catch {
|
||||
const ZlibStream = options.codecType.startsWith(CODEC_DEFLATE) ?
|
||||
config.CompressionStreamZlib :
|
||||
config.DecompressionStreamZlib;
|
||||
if (!ZlibStream || ZlibStream.requiresModule) {
|
||||
options.useCompressionStream = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
codecStream = new CodecStream(options, config);
|
||||
await readable.pipeThrough(codecStream).pipeTo(writable, { preventClose: true, preventAbort: true });
|
||||
const {
|
||||
signature,
|
||||
inputSize,
|
||||
outputSize
|
||||
} = codecStream;
|
||||
return {
|
||||
signature,
|
||||
inputSize,
|
||||
outputSize
|
||||
};
|
||||
} catch (error) {
|
||||
if (codecStream) {
|
||||
error.outputSize = codecStream.outputSize;
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
onTaskFinished();
|
||||
}
|
||||
}
|
||||
|
||||
async function runWebWorker(workerData, config) {
|
||||
let resolveResult, rejectResult;
|
||||
const result = new Promise((resolve, reject) => {
|
||||
resolveResult = resolve;
|
||||
rejectResult = reject;
|
||||
});
|
||||
Object.assign(workerData, {
|
||||
reader: null,
|
||||
writer: null,
|
||||
resolveResult,
|
||||
rejectResult,
|
||||
result
|
||||
});
|
||||
const { readable, options } = workerData;
|
||||
const { writable, closed, abortPipe } = watchClosedStream(workerData.writable);
|
||||
let streamsTransferred;
|
||||
try {
|
||||
streamsTransferred = sendMessage({
|
||||
type: MESSAGE_START,
|
||||
options,
|
||||
config,
|
||||
readable,
|
||||
writable
|
||||
}, workerData);
|
||||
} catch (error) {
|
||||
abortPipe();
|
||||
try {
|
||||
await closed;
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
workerData.onTaskFinished();
|
||||
throw error;
|
||||
}
|
||||
if (!streamsTransferred) {
|
||||
Object.assign(workerData, {
|
||||
reader: readable.getReader(),
|
||||
writer: writable.getWriter()
|
||||
});
|
||||
}
|
||||
try {
|
||||
const resultValue = await result;
|
||||
await closeWritable();
|
||||
await closed;
|
||||
return resultValue;
|
||||
} catch (error) {
|
||||
await closeWritable();
|
||||
abortPipe();
|
||||
try {
|
||||
await closed;
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
async function closeWritable() {
|
||||
if (!streamsTransferred && !writable.locked) {
|
||||
try {
|
||||
await writable.getWriter().close();
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function watchClosedStream(writableSource) {
|
||||
const abortController = new AbortController();
|
||||
const { writable, readable } = new TransformStream();
|
||||
const closed = readable.pipeTo(writableSource, { preventClose: true, preventAbort: true, signal: abortController.signal });
|
||||
closed.catch(() => { });
|
||||
return { writable, closed, abortPipe: () => abortController.abort() };
|
||||
}
|
||||
|
||||
function terminateWorker(workerData) {
|
||||
const { worker } = workerData;
|
||||
if (worker) {
|
||||
try {
|
||||
worker.terminate();
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
workerData.interface = null;
|
||||
}
|
||||
|
||||
function getWebWorker(url, baseURI, workerData, isModuleType, useBlobURI = true) {
|
||||
let worker, resolvedURI, resolvedOptions;
|
||||
if (webWorkerURI === UNDEFINED_VALUE || webWorkerSource !== url) {
|
||||
// deno-lint-ignore valid-typeof
|
||||
const isFunctionURI = typeof url == FUNCTION_TYPE;
|
||||
if (isFunctionURI) {
|
||||
resolvedURI = url(useBlobURI);
|
||||
} else {
|
||||
resolvedURI = url;
|
||||
}
|
||||
const isDataURI = resolvedURI.startsWith("data:");
|
||||
const isBlobURI = resolvedURI.startsWith("blob:");
|
||||
if (isDataURI || isBlobURI) {
|
||||
if (isModuleType === UNDEFINED_VALUE) {
|
||||
isModuleType = false;
|
||||
}
|
||||
if (isModuleType) {
|
||||
resolvedOptions = MODULE_WORKER_OPTIONS;
|
||||
}
|
||||
try {
|
||||
worker = new Worker(resolvedURI, resolvedOptions);
|
||||
} catch (error) {
|
||||
if (isBlobURI) {
|
||||
try {
|
||||
URL.revokeObjectURL(resolvedURI);
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
if (isFunctionURI && isBlobURI) {
|
||||
return getWebWorker(url, baseURI, workerData, isModuleType, false);
|
||||
} else if (!isModuleType) {
|
||||
return getWebWorker(url, baseURI, workerData, true, false);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isModuleType === UNDEFINED_VALUE) {
|
||||
isModuleType = true;
|
||||
}
|
||||
if (isModuleType) {
|
||||
resolvedOptions = MODULE_WORKER_OPTIONS;
|
||||
}
|
||||
try {
|
||||
resolvedURI = new URL(resolvedURI, baseURI);
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
try {
|
||||
worker = new Worker(resolvedURI, resolvedOptions);
|
||||
} catch (error) {
|
||||
if (!isModuleType) {
|
||||
return getWebWorker(url, baseURI, workerData, false, useBlobURI);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
webWorkerSource = url;
|
||||
webWorkerURI = resolvedURI;
|
||||
webWorkerOptions = resolvedOptions;
|
||||
} else {
|
||||
worker = new Worker(webWorkerURI, webWorkerOptions);
|
||||
}
|
||||
worker.addEventListener(MESSAGE_EVENT_TYPE, event => onMessage(event, workerData));
|
||||
worker.addEventListener(ERROR_EVENT_TYPE, event => onWorkerError(event, workerData));
|
||||
worker.addEventListener(MESSAGE_ERROR_EVENT_TYPE, event => onWorkerError(event, workerData));
|
||||
return worker;
|
||||
}
|
||||
|
||||
function onWorkerError(event, workerData) {
|
||||
if (event.preventDefault) {
|
||||
event.preventDefault();
|
||||
}
|
||||
const { rejectResult, writer, onTaskFinished } = workerData;
|
||||
terminateWorker(workerData);
|
||||
if (rejectResult) {
|
||||
rejectResult(event.error || new Error(event.message || ERROR_EVENT_TYPE));
|
||||
if (writer) {
|
||||
writer.releaseLock();
|
||||
}
|
||||
onTaskFinished();
|
||||
}
|
||||
}
|
||||
|
||||
function sendMessage(message, { worker, writer, transferStreams }) {
|
||||
try {
|
||||
const { value, readable, writable } = message;
|
||||
const transferables = [];
|
||||
if (value) {
|
||||
message.value = value.byteOffset || value.byteLength != value.buffer.byteLength ? new Uint8Array(value) : value;
|
||||
transferables.push(message.value.buffer);
|
||||
}
|
||||
if (transferStreams && transferStreamsSupported) {
|
||||
if (readable) {
|
||||
transferables.push(readable);
|
||||
}
|
||||
if (writable) {
|
||||
transferables.push(writable);
|
||||
}
|
||||
} else {
|
||||
message.readable = message.writable = null;
|
||||
}
|
||||
if (transferables.length) {
|
||||
try {
|
||||
worker.postMessage(message, transferables);
|
||||
return true;
|
||||
} catch {
|
||||
transferStreamsSupported = false;
|
||||
message.readable = message.writable = null;
|
||||
worker.postMessage(message);
|
||||
}
|
||||
} else {
|
||||
worker.postMessage(message);
|
||||
}
|
||||
} catch (error) {
|
||||
if (writer) {
|
||||
writer.releaseLock();
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function onMessage({ data }, workerData) {
|
||||
const { type, value, messageId, result, error } = data;
|
||||
const { reader, writer, resolveResult, rejectResult, onTaskFinished, generation } = workerData;
|
||||
const stale = () => workerData.generation != generation;
|
||||
try {
|
||||
if (error) {
|
||||
const { message, stack, code, name, outputSize } = error;
|
||||
const responseError = new Error(message);
|
||||
Object.assign(responseError, { stack, code, name, outputSize });
|
||||
close(responseError);
|
||||
} else {
|
||||
if (type == MESSAGE_PULL) {
|
||||
const { value, done } = await reader.read();
|
||||
if (!stale()) {
|
||||
sendMessage({ type: MESSAGE_DATA, value, done, messageId }, workerData);
|
||||
}
|
||||
}
|
||||
if (type == MESSAGE_DATA) {
|
||||
await writer.ready;
|
||||
await writer.write(new Uint8Array(value));
|
||||
if (!stale()) {
|
||||
sendMessage({ type: MESSAGE_ACK_DATA, messageId }, workerData);
|
||||
}
|
||||
}
|
||||
if (type == MESSAGE_CLOSE) {
|
||||
close(null, result);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (!stale()) {
|
||||
terminateWorker(workerData);
|
||||
close(error);
|
||||
}
|
||||
}
|
||||
|
||||
function close(error, result) {
|
||||
if (stale()) {
|
||||
return;
|
||||
}
|
||||
if (error) {
|
||||
rejectResult(error);
|
||||
} else {
|
||||
resolveResult(result);
|
||||
}
|
||||
if (writer) {
|
||||
writer.releaseLock();
|
||||
}
|
||||
onTaskFinished();
|
||||
}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global navigator, CompressionStream, DecompressionStream */
|
||||
|
||||
import {
|
||||
UNDEFINED_VALUE,
|
||||
UNDEFINED_TYPE
|
||||
} from "./constants.js";
|
||||
|
||||
const MINIMUM_CHUNK_SIZE = 64;
|
||||
let maxWorkers = 2;
|
||||
try {
|
||||
if (typeof navigator != UNDEFINED_TYPE && navigator.hardwareConcurrency) {
|
||||
maxWorkers = navigator.hardwareConcurrency;
|
||||
}
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
const DEFAULT_CONFIGURATION = {
|
||||
workerURI: "./core/web-worker-wasm.js",
|
||||
wasmURI: "./core/streams/zlib-wasm/zlib-streams.wasm",
|
||||
chunkSize: 64 * 1024,
|
||||
maxWorkers,
|
||||
terminateWorkerTimeout: 5000,
|
||||
workerStarvationTimeout: 5000,
|
||||
useWebWorkers: true,
|
||||
useCompressionStream: true,
|
||||
CompressionStream: typeof CompressionStream != UNDEFINED_TYPE && CompressionStream,
|
||||
DecompressionStream: typeof DecompressionStream != UNDEFINED_TYPE && DecompressionStream
|
||||
};
|
||||
|
||||
const CONFIGURABLE_PROPERTY_NAMES = [
|
||||
"baseURI",
|
||||
"wasmURI",
|
||||
"workerURI",
|
||||
"chunkSize",
|
||||
"maxWorkers",
|
||||
"terminateWorkerTimeout",
|
||||
"workerStarvationTimeout",
|
||||
"useCompressionStream",
|
||||
"useWebWorkers",
|
||||
"CompressionStream",
|
||||
"DecompressionStream",
|
||||
"CompressionStreamZlib",
|
||||
"DecompressionStreamZlib"
|
||||
];
|
||||
|
||||
const config = Object.assign({}, DEFAULT_CONFIGURATION);
|
||||
|
||||
export {
|
||||
configure,
|
||||
getConfiguration,
|
||||
getChunkSize
|
||||
};
|
||||
|
||||
function getConfiguration() {
|
||||
return config;
|
||||
}
|
||||
|
||||
function getChunkSize(config) {
|
||||
return Math.max(config.chunkSize, MINIMUM_CHUNK_SIZE);
|
||||
}
|
||||
|
||||
function configure(configuration) {
|
||||
for (const propertyName of CONFIGURABLE_PROPERTY_NAMES) {
|
||||
const propertyValue = configuration[propertyName];
|
||||
if (propertyValue !== UNDEFINED_VALUE) {
|
||||
config[propertyName] = propertyValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
Copyright (c) 2022 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
const MAX_32_BITS = 0xffffffff;
|
||||
const MAX_16_BITS = 0xffff;
|
||||
const MAX_8_BITS = 0xff;
|
||||
const COMPRESSION_METHOD_DEFLATE = 0x08;
|
||||
const COMPRESSION_METHOD_DEFLATE_64 = 0x09;
|
||||
const COMPRESSION_METHOD_STORE = 0x00;
|
||||
const COMPRESSION_METHOD_AES = 0x63;
|
||||
|
||||
const LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50;
|
||||
const SPLIT_ZIP_FILE_SIGNATURE = 0x08074b50;
|
||||
const DATA_DESCRIPTOR_RECORD_SIGNATURE = SPLIT_ZIP_FILE_SIGNATURE;
|
||||
const CENTRAL_FILE_HEADER_SIGNATURE = 0x02014b50;
|
||||
const END_OF_CENTRAL_DIR_SIGNATURE = 0x06054b50;
|
||||
const ZIP64_END_OF_CENTRAL_DIR_SIGNATURE = 0x06064b50;
|
||||
const ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE = 0x07064b50;
|
||||
const CENTRAL_FILE_HEADER_LENGTH = 46;
|
||||
const END_OF_CENTRAL_DIR_LENGTH = 22;
|
||||
const ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH = 20;
|
||||
const ZIP64_END_OF_CENTRAL_DIR_LENGTH = 56;
|
||||
const ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH = END_OF_CENTRAL_DIR_LENGTH + ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH + ZIP64_END_OF_CENTRAL_DIR_LENGTH;
|
||||
|
||||
const DATA_DESCRIPTOR_RECORD_LENGTH = 12;
|
||||
const DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH = 20;
|
||||
const DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH = 4;
|
||||
|
||||
const EXTRAFIELD_TYPE_ZIP64 = 0x0001;
|
||||
const EXTRAFIELD_TYPE_AES = 0x9901;
|
||||
const EXTRAFIELD_TYPE_NTFS = 0x000a;
|
||||
const EXTRAFIELD_TYPE_NTFS_TAG1 = 0x0001;
|
||||
const EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP = 0x5455;
|
||||
const EXTRAFIELD_TYPE_UNICODE_PATH = 0x7075;
|
||||
const EXTRAFIELD_TYPE_UNICODE_COMMENT = 0x6375;
|
||||
const EXTRAFIELD_TYPE_USDZ = 0x1986;
|
||||
const EXTRAFIELD_TYPE_INFOZIP = 0x7875;
|
||||
const EXTRAFIELD_TYPE_UNIX = 0x7855;
|
||||
|
||||
const BITFLAG_ENCRYPTED = 0b1;
|
||||
const BITFLAG_LEVEL = 0b0110;
|
||||
const BITFLAG_LEVEL_MAX_MASK = 0b010;
|
||||
const BITFLAG_LEVEL_FAST_MASK = 0b100;
|
||||
const BITFLAG_LEVEL_SUPER_FAST_MASK = 0b110;
|
||||
const BITFLAG_DATA_DESCRIPTOR = 0b1000;
|
||||
const BITFLAG_LANG_ENCODING_FLAG = 0b100000000000;
|
||||
const FILE_ATTR_MSDOS_DIR_MASK = 0b10000;
|
||||
const FILE_ATTR_MSDOS_READONLY_MASK = 0x01;
|
||||
const FILE_ATTR_MSDOS_HIDDEN_MASK = 0x02;
|
||||
const FILE_ATTR_MSDOS_SYSTEM_MASK = 0x04;
|
||||
const FILE_ATTR_MSDOS_ARCHIVE_MASK = 0x20;
|
||||
const FILE_ATTR_UNIX_TYPE_MASK = 0o170000;
|
||||
const FILE_ATTR_UNIX_TYPE_DIR = 0o040000;
|
||||
const FILE_ATTR_UNIX_EXECUTABLE_MASK = 0o111;
|
||||
const FILE_ATTR_UNIX_DEFAULT_MASK = 0o644;
|
||||
const FILE_ATTR_UNIX_SETUID_MASK = 0o4000;
|
||||
const FILE_ATTR_UNIX_SETGID_MASK = 0o2000;
|
||||
const FILE_ATTR_UNIX_STICKY_MASK = 0o1000;
|
||||
|
||||
const VERSION_DEFLATE = 0x14;
|
||||
const VERSION_ZIP64 = 0x2D;
|
||||
const VERSION_AES = 0x33;
|
||||
|
||||
const DIRECTORY_SIGNATURE = "/";
|
||||
|
||||
const HEADER_SIZE = 30;
|
||||
const HEADER_OFFSET_VERSION = 0;
|
||||
const HEADER_OFFSET_SIGNATURE = 10;
|
||||
const HEADER_OFFSET_COMPRESSED_SIZE = 14;
|
||||
const HEADER_OFFSET_UNCOMPRESSED_SIZE = 18;
|
||||
const LOCAL_HEADER_COMMON_OFFSET = 4;
|
||||
|
||||
const MAX_DATE = new Date(2107, 11, 31);
|
||||
const MIN_DATE = new Date(1980, 0, 1);
|
||||
|
||||
const UNDEFINED_VALUE = undefined;
|
||||
const INFINITY_VALUE = Infinity;
|
||||
const UNDEFINED_TYPE = "undefined";
|
||||
const FUNCTION_TYPE = "function";
|
||||
const OBJECT_TYPE = "object";
|
||||
|
||||
const EMPTY_UINT8_ARRAY = new Uint8Array();
|
||||
|
||||
export {
|
||||
EMPTY_UINT8_ARRAY,
|
||||
MAX_32_BITS,
|
||||
MAX_16_BITS,
|
||||
MAX_8_BITS,
|
||||
COMPRESSION_METHOD_DEFLATE,
|
||||
COMPRESSION_METHOD_DEFLATE_64,
|
||||
COMPRESSION_METHOD_STORE,
|
||||
COMPRESSION_METHOD_AES,
|
||||
SPLIT_ZIP_FILE_SIGNATURE,
|
||||
LOCAL_FILE_HEADER_SIGNATURE,
|
||||
DATA_DESCRIPTOR_RECORD_SIGNATURE,
|
||||
CENTRAL_FILE_HEADER_SIGNATURE,
|
||||
CENTRAL_FILE_HEADER_LENGTH,
|
||||
END_OF_CENTRAL_DIR_SIGNATURE,
|
||||
ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE,
|
||||
ZIP64_END_OF_CENTRAL_DIR_SIGNATURE,
|
||||
DATA_DESCRIPTOR_RECORD_LENGTH,
|
||||
DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH,
|
||||
DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH,
|
||||
EXTRAFIELD_TYPE_ZIP64,
|
||||
EXTRAFIELD_TYPE_AES,
|
||||
EXTRAFIELD_TYPE_NTFS,
|
||||
EXTRAFIELD_TYPE_NTFS_TAG1,
|
||||
EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP,
|
||||
EXTRAFIELD_TYPE_UNICODE_PATH,
|
||||
EXTRAFIELD_TYPE_UNICODE_COMMENT,
|
||||
EXTRAFIELD_TYPE_USDZ,
|
||||
EXTRAFIELD_TYPE_INFOZIP,
|
||||
EXTRAFIELD_TYPE_UNIX,
|
||||
END_OF_CENTRAL_DIR_LENGTH,
|
||||
ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH,
|
||||
ZIP64_END_OF_CENTRAL_DIR_LENGTH,
|
||||
ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH,
|
||||
BITFLAG_ENCRYPTED,
|
||||
BITFLAG_LEVEL,
|
||||
BITFLAG_LEVEL_MAX_MASK,
|
||||
BITFLAG_LEVEL_FAST_MASK,
|
||||
BITFLAG_LEVEL_SUPER_FAST_MASK,
|
||||
BITFLAG_DATA_DESCRIPTOR,
|
||||
BITFLAG_LANG_ENCODING_FLAG,
|
||||
FILE_ATTR_MSDOS_DIR_MASK,
|
||||
FILE_ATTR_MSDOS_READONLY_MASK,
|
||||
FILE_ATTR_MSDOS_HIDDEN_MASK,
|
||||
FILE_ATTR_MSDOS_SYSTEM_MASK,
|
||||
FILE_ATTR_MSDOS_ARCHIVE_MASK,
|
||||
FILE_ATTR_UNIX_TYPE_MASK,
|
||||
FILE_ATTR_UNIX_TYPE_DIR,
|
||||
FILE_ATTR_UNIX_EXECUTABLE_MASK,
|
||||
FILE_ATTR_UNIX_DEFAULT_MASK,
|
||||
FILE_ATTR_UNIX_SETUID_MASK,
|
||||
FILE_ATTR_UNIX_SETGID_MASK,
|
||||
FILE_ATTR_UNIX_STICKY_MASK,
|
||||
VERSION_DEFLATE,
|
||||
VERSION_ZIP64,
|
||||
VERSION_AES,
|
||||
HEADER_SIZE,
|
||||
HEADER_OFFSET_VERSION,
|
||||
HEADER_OFFSET_SIGNATURE,
|
||||
HEADER_OFFSET_COMPRESSED_SIZE,
|
||||
HEADER_OFFSET_UNCOMPRESSED_SIZE,
|
||||
LOCAL_HEADER_COMMON_OFFSET,
|
||||
DIRECTORY_SIGNATURE,
|
||||
MIN_DATE,
|
||||
MAX_DATE,
|
||||
UNDEFINED_VALUE,
|
||||
INFINITY_VALUE,
|
||||
UNDEFINED_TYPE,
|
||||
FUNCTION_TYPE,
|
||||
OBJECT_TYPE
|
||||
};
|
||||
+819
@@ -0,0 +1,819 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global Blob, atob, btoa, XMLHttpRequest, URL, fetch, ReadableStream, WritableStream, FileReader, TransformStream, Response */
|
||||
// deno-lint-ignore-file no-this-alias
|
||||
|
||||
import {
|
||||
UNDEFINED_VALUE,
|
||||
INFINITY_VALUE,
|
||||
FUNCTION_TYPE,
|
||||
END_OF_CENTRAL_DIR_LENGTH,
|
||||
EMPTY_UINT8_ARRAY
|
||||
} from "./constants.js";
|
||||
import { getConfiguration } from "./configuration.js";
|
||||
|
||||
const ERR_HTTP_STATUS = "HTTP error ";
|
||||
const ERR_HTTP_RANGE = "HTTP Range not supported";
|
||||
const ERR_ITERATOR_COMPLETED_TOO_SOON = "Writer iterator completed too soon";
|
||||
const ERR_WRITER_NOT_INITIALIZED = "Writer not initialized";
|
||||
|
||||
const CONTENT_TYPE_TEXT_PLAIN = "text/plain";
|
||||
const HTTP_HEADER_CONTENT_LENGTH = "Content-Length";
|
||||
const HTTP_HEADER_CONTENT_ENCODING = "Content-Encoding";
|
||||
const HTTP_HEADER_CONTENT_RANGE = "Content-Range";
|
||||
const HTTP_HEADER_ACCEPT_RANGES = "Accept-Ranges";
|
||||
const HTTP_HEADER_RANGE = "Range";
|
||||
const HTTP_HEADER_CONTENT_TYPE = "Content-Type";
|
||||
const HTTP_METHOD_HEAD = "HEAD";
|
||||
const HTTP_METHOD_GET = "GET";
|
||||
const HTTP_RANGE_UNIT = "bytes";
|
||||
const DEFAULT_CHUNK_SIZE = 64 * 1024;
|
||||
const DEFAULT_BUFFER_SIZE = 256 * 1024;
|
||||
|
||||
const PROPERTY_NAME_WRITABLE = "writable";
|
||||
|
||||
class Stream {
|
||||
|
||||
constructor() {
|
||||
this.size = 0;
|
||||
}
|
||||
|
||||
init() {
|
||||
this.initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
class Reader extends Stream {
|
||||
|
||||
get readable() {
|
||||
return this.createReadable();
|
||||
}
|
||||
|
||||
createReadable({ offset = 0, size, diskNumberStart, chunkSize = DEFAULT_CHUNK_SIZE } = {}) {
|
||||
const reader = this;
|
||||
let chunkOffset = 0;
|
||||
return new ReadableStream({
|
||||
async pull(controller) {
|
||||
const dataSize = size === UNDEFINED_VALUE ? chunkSize : Math.min(chunkSize, size - chunkOffset);
|
||||
const data = await readUint8Array(reader, offset + chunkOffset, dataSize, diskNumberStart);
|
||||
controller.enqueue(data);
|
||||
if ((chunkOffset + chunkSize > size) || (size === UNDEFINED_VALUE && !data.length && dataSize)) {
|
||||
controller.close();
|
||||
} else {
|
||||
chunkOffset += chunkSize;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class Writer extends Stream {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const writer = this;
|
||||
const writable = new WritableStream({
|
||||
write(chunk) {
|
||||
if (!writer.initialized) {
|
||||
throw new Error(ERR_WRITER_NOT_INITIALIZED);
|
||||
}
|
||||
return writer.writeUint8Array(chunk);
|
||||
}
|
||||
});
|
||||
Object.defineProperty(writer, PROPERTY_NAME_WRITABLE, {
|
||||
get() {
|
||||
return writable;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
writeUint8Array() {
|
||||
// abstract
|
||||
}
|
||||
}
|
||||
|
||||
class Data64URIReader extends Reader {
|
||||
|
||||
constructor(dataURI) {
|
||||
super();
|
||||
let dataEnd = dataURI.length;
|
||||
while (dataURI.charAt(dataEnd - 1) == "=") {
|
||||
dataEnd--;
|
||||
}
|
||||
const dataStart = dataURI.indexOf(",") + 1;
|
||||
Object.assign(this, {
|
||||
dataURI,
|
||||
dataStart,
|
||||
size: Math.floor((dataEnd - dataStart) * 0.75)
|
||||
});
|
||||
}
|
||||
|
||||
readUint8Array(offset, length) {
|
||||
const {
|
||||
dataStart,
|
||||
dataURI
|
||||
} = this;
|
||||
const dataArray = new Uint8Array(length);
|
||||
const start = Math.floor(offset / 3) * 4;
|
||||
const bytes = atob(dataURI.substring(start + dataStart, Math.ceil((offset + length) / 3) * 4 + dataStart));
|
||||
const delta = offset - Math.floor(start / 4) * 3;
|
||||
let effectiveLength = 0;
|
||||
for (let indexByte = delta; indexByte < delta + length && indexByte < bytes.length; indexByte++) {
|
||||
dataArray[indexByte - delta] = bytes.charCodeAt(indexByte);
|
||||
effectiveLength++;
|
||||
}
|
||||
if (effectiveLength < dataArray.length) {
|
||||
return dataArray.subarray(0, effectiveLength);
|
||||
} else {
|
||||
return dataArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Data64URIWriter extends Writer {
|
||||
|
||||
constructor(contentType) {
|
||||
super();
|
||||
Object.assign(this, {
|
||||
data: "data:" + (contentType || "") + ";base64,",
|
||||
pending: []
|
||||
});
|
||||
}
|
||||
|
||||
writeUint8Array(array) {
|
||||
const writer = this;
|
||||
let indexArray;
|
||||
let dataString = writer.pending;
|
||||
const delta = writer.pending.length;
|
||||
writer.pending = "";
|
||||
for (indexArray = 0; indexArray < (Math.floor((delta + array.length) / 3) * 3) - delta; indexArray++) {
|
||||
dataString += String.fromCharCode(array[indexArray]);
|
||||
}
|
||||
for (; indexArray < array.length; indexArray++) {
|
||||
writer.pending += String.fromCharCode(array[indexArray]);
|
||||
}
|
||||
if (dataString.length > 2) {
|
||||
writer.data += btoa(dataString);
|
||||
} else {
|
||||
writer.pending = dataString + writer.pending;
|
||||
}
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this.data + btoa(this.pending);
|
||||
}
|
||||
}
|
||||
|
||||
class BlobReader extends Reader {
|
||||
|
||||
constructor(blob) {
|
||||
super();
|
||||
Object.assign(this, {
|
||||
blob,
|
||||
size: blob.size
|
||||
});
|
||||
}
|
||||
|
||||
async readUint8Array(offset, length) {
|
||||
const reader = this;
|
||||
const offsetEnd = offset + length;
|
||||
const blob = offset || offsetEnd < reader.size ? reader.blob.slice(offset, offsetEnd) : reader.blob;
|
||||
let arrayBuffer = await blob.arrayBuffer();
|
||||
if (arrayBuffer.byteLength > length) {
|
||||
arrayBuffer = arrayBuffer.slice(offset, offsetEnd);
|
||||
}
|
||||
return new Uint8Array(arrayBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
class BlobWriter extends Stream {
|
||||
|
||||
constructor(contentType) {
|
||||
super();
|
||||
const writer = this;
|
||||
const transformStream = new TransformStream();
|
||||
const headers = [];
|
||||
if (contentType) {
|
||||
headers.push([HTTP_HEADER_CONTENT_TYPE, contentType]);
|
||||
}
|
||||
Object.defineProperty(writer, PROPERTY_NAME_WRITABLE, {
|
||||
get() {
|
||||
return transformStream.writable;
|
||||
}
|
||||
});
|
||||
writer.blob = new Response(transformStream.readable, { headers }).blob();
|
||||
writer.blob.catch(() => { });
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this.blob;
|
||||
}
|
||||
}
|
||||
|
||||
class TextReader extends BlobReader {
|
||||
|
||||
constructor(text) {
|
||||
super(new Blob([text], { type: CONTENT_TYPE_TEXT_PLAIN }));
|
||||
}
|
||||
}
|
||||
|
||||
class TextWriter extends BlobWriter {
|
||||
|
||||
constructor(encoding) {
|
||||
super(encoding);
|
||||
Object.assign(this, {
|
||||
encoding,
|
||||
utf8: !encoding || encoding.toLowerCase() == "utf-8"
|
||||
});
|
||||
}
|
||||
|
||||
async getData() {
|
||||
const {
|
||||
encoding,
|
||||
utf8
|
||||
} = this;
|
||||
const blob = await super.getData();
|
||||
if (blob.text && utf8) {
|
||||
return blob.text();
|
||||
} else {
|
||||
const reader = new FileReader();
|
||||
return new Promise((resolve, reject) => {
|
||||
Object.assign(reader, {
|
||||
onload: ({ target }) => resolve(target.result),
|
||||
onerror: () => reject(reader.error)
|
||||
});
|
||||
reader.readAsText(blob, encoding);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FetchReader extends Reader {
|
||||
|
||||
constructor(url, options) {
|
||||
super();
|
||||
createHttpReader(this, url, options);
|
||||
}
|
||||
|
||||
async init() {
|
||||
await initHttpReader(this, sendFetchRequest, getFetchRequestData);
|
||||
super.init();
|
||||
}
|
||||
|
||||
readUint8Array(index, length) {
|
||||
return readUint8ArrayHttpReader(this, index, length, sendFetchRequest, getFetchRequestData);
|
||||
}
|
||||
}
|
||||
|
||||
class XHRReader extends Reader {
|
||||
|
||||
constructor(url, options) {
|
||||
super();
|
||||
createHttpReader(this, url, options);
|
||||
}
|
||||
|
||||
async init() {
|
||||
await initHttpReader(this, sendXMLHttpRequest, getXMLHttpRequestData);
|
||||
super.init();
|
||||
}
|
||||
|
||||
readUint8Array(index, length) {
|
||||
return readUint8ArrayHttpReader(this, index, length, sendXMLHttpRequest, getXMLHttpRequestData);
|
||||
}
|
||||
}
|
||||
|
||||
function createHttpReader(httpReader, url, options) {
|
||||
const {
|
||||
preventHeadRequest,
|
||||
useRangeHeader,
|
||||
forceRangeRequests,
|
||||
combineSizeEocd,
|
||||
fetch
|
||||
} = options;
|
||||
options = Object.assign({}, options);
|
||||
delete options.preventHeadRequest;
|
||||
delete options.useRangeHeader;
|
||||
delete options.forceRangeRequests;
|
||||
delete options.combineSizeEocd;
|
||||
delete options.useXHR;
|
||||
delete options.fetch;
|
||||
Object.assign(httpReader, {
|
||||
url,
|
||||
options,
|
||||
preventHeadRequest,
|
||||
useRangeHeader,
|
||||
forceRangeRequests,
|
||||
combineSizeEocd,
|
||||
fetch
|
||||
});
|
||||
}
|
||||
|
||||
async function initHttpReader(httpReader, sendRequest, getRequestData) {
|
||||
const {
|
||||
url,
|
||||
preventHeadRequest,
|
||||
useRangeHeader,
|
||||
forceRangeRequests,
|
||||
combineSizeEocd
|
||||
} = httpReader;
|
||||
if (isHttpFamily(url) && (useRangeHeader || forceRangeRequests) && (typeof preventHeadRequest == "undefined" || preventHeadRequest)) {
|
||||
const response = await sendRequest(HTTP_METHOD_GET, httpReader, getRangeHeaders(httpReader, combineSizeEocd ? -END_OF_CENTRAL_DIR_LENGTH : undefined));
|
||||
const acceptRanges = response.headers.get(HTTP_HEADER_ACCEPT_RANGES);
|
||||
if (!forceRangeRequests && (!acceptRanges || acceptRanges.toLowerCase() != HTTP_RANGE_UNIT)) {
|
||||
throw new Error(ERR_HTTP_RANGE);
|
||||
} else {
|
||||
if (combineSizeEocd) {
|
||||
const eocdCache = new Uint8Array(await response.arrayBuffer());
|
||||
if (response.status == 206 && eocdCache.length == END_OF_CENTRAL_DIR_LENGTH) {
|
||||
httpReader.eocdCache = eocdCache;
|
||||
}
|
||||
}
|
||||
let contentSize;
|
||||
const contentRangeHeader = response.headers.get(HTTP_HEADER_CONTENT_RANGE);
|
||||
if (contentRangeHeader) {
|
||||
const splitHeader = contentRangeHeader.trim().split(/\s*\/\s*/);
|
||||
if (splitHeader.length) {
|
||||
const headerValue = splitHeader[1];
|
||||
if (headerValue && headerValue != "*") {
|
||||
contentSize = Number(headerValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (contentSize === UNDEFINED_VALUE) {
|
||||
await getContentLength(httpReader, sendRequest, getRequestData);
|
||||
} else {
|
||||
httpReader.size = contentSize;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await getContentLength(httpReader, sendRequest, getRequestData);
|
||||
}
|
||||
}
|
||||
|
||||
async function readUint8ArrayHttpReader(httpReader, index, length, sendRequest, getRequestData) {
|
||||
const {
|
||||
useRangeHeader,
|
||||
forceRangeRequests,
|
||||
eocdCache,
|
||||
size,
|
||||
options
|
||||
} = httpReader;
|
||||
if (useRangeHeader || forceRangeRequests) {
|
||||
if (eocdCache && index == size - END_OF_CENTRAL_DIR_LENGTH && length == END_OF_CENTRAL_DIR_LENGTH) {
|
||||
return eocdCache;
|
||||
}
|
||||
if (index >= size || length === 0) {
|
||||
return EMPTY_UINT8_ARRAY;
|
||||
} else {
|
||||
if (index + length > size) {
|
||||
length = size - index;
|
||||
}
|
||||
const response = await sendRequest(HTTP_METHOD_GET, httpReader, getRangeHeaders(httpReader, index, length));
|
||||
if (response.status != 206) {
|
||||
throw new Error(ERR_HTTP_RANGE);
|
||||
}
|
||||
const contentRangeHeader = response.headers.get(HTTP_HEADER_CONTENT_RANGE);
|
||||
if (contentRangeHeader) {
|
||||
const rangeStart = Number(contentRangeHeader.trim().split(/[\s-]+/)[1]);
|
||||
if (!Number.isNaN(rangeStart) && rangeStart != index) {
|
||||
throw new Error(ERR_HTTP_RANGE);
|
||||
}
|
||||
}
|
||||
const data = new Uint8Array(await response.arrayBuffer());
|
||||
if (data.length != length) {
|
||||
throw new Error(ERR_HTTP_RANGE);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
} else {
|
||||
const { data } = httpReader;
|
||||
if (!data) {
|
||||
await getRequestData(httpReader, options);
|
||||
}
|
||||
return new Uint8Array(httpReader.data.subarray(index, index + length));
|
||||
}
|
||||
}
|
||||
|
||||
function getRangeHeaders(httpReader, index = 0, length = 1) {
|
||||
return Object.assign({}, getHeaders(httpReader), { [HTTP_HEADER_RANGE]: HTTP_RANGE_UNIT + "=" + (index < 0 ? index : index + "-" + (index + length - 1)) });
|
||||
}
|
||||
|
||||
function getHeaders({ options }) {
|
||||
const { headers } = options;
|
||||
if (headers) {
|
||||
if (Symbol.iterator in headers) {
|
||||
return Object.fromEntries(headers);
|
||||
} else {
|
||||
return headers;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getFetchRequestData(httpReader) {
|
||||
await getRequestData(httpReader, sendFetchRequest);
|
||||
}
|
||||
|
||||
async function getXMLHttpRequestData(httpReader) {
|
||||
await getRequestData(httpReader, sendXMLHttpRequest);
|
||||
}
|
||||
|
||||
async function getRequestData(httpReader, sendRequest) {
|
||||
const response = await sendRequest(HTTP_METHOD_GET, httpReader, getHeaders(httpReader));
|
||||
httpReader.data = new Uint8Array(await response.arrayBuffer());
|
||||
httpReader.size = httpReader.data.length;
|
||||
}
|
||||
|
||||
async function getContentLength(httpReader, sendRequest, getRequestData) {
|
||||
if (httpReader.preventHeadRequest) {
|
||||
await getRequestData(httpReader, httpReader.options);
|
||||
} else {
|
||||
const response = await sendRequest(HTTP_METHOD_HEAD, httpReader, getHeaders(httpReader));
|
||||
const contentLength = response.headers.get(HTTP_HEADER_CONTENT_LENGTH);
|
||||
if (contentLength && !response.headers.get(HTTP_HEADER_CONTENT_ENCODING)) {
|
||||
httpReader.size = Number(contentLength);
|
||||
} else {
|
||||
await getRequestData(httpReader, httpReader.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function sendFetchRequest(method, { fetch: fetchFunction = fetch, options, url }, headers) {
|
||||
const response = await fetchFunction(url, Object.assign({}, options, { method, headers }));
|
||||
if (response.status < 400) {
|
||||
return response;
|
||||
} else {
|
||||
throw response.status == 416 ? new Error(ERR_HTTP_RANGE) : new Error(ERR_HTTP_STATUS + (response.statusText || response.status));
|
||||
}
|
||||
}
|
||||
|
||||
function sendXMLHttpRequest(method, { url }, headers) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = new XMLHttpRequest();
|
||||
request.addEventListener("load", () => {
|
||||
if (request.status < 400) {
|
||||
const headers = [];
|
||||
request.getAllResponseHeaders().trim().split(/[\r\n]+/).forEach(header => {
|
||||
const splitHeader = header.trim().split(/\s*:\s*/);
|
||||
splitHeader[0] = splitHeader[0].trim().replace(/^[a-z]|-[a-z]/g, value => value.toUpperCase());
|
||||
headers.push(splitHeader);
|
||||
});
|
||||
resolve({
|
||||
status: request.status,
|
||||
arrayBuffer: () => request.response,
|
||||
headers: new Map(headers)
|
||||
});
|
||||
} else {
|
||||
reject(request.status == 416 ? new Error(ERR_HTTP_RANGE) : new Error(ERR_HTTP_STATUS + (request.statusText || request.status)));
|
||||
}
|
||||
}, false);
|
||||
request.addEventListener("error", event => reject(event.detail ? event.detail.error : new Error("Network error")), false);
|
||||
request.open(method, url);
|
||||
if (headers) {
|
||||
for (const entry of Object.entries(headers)) {
|
||||
request.setRequestHeader(entry[0], entry[1]);
|
||||
}
|
||||
}
|
||||
request.responseType = "arraybuffer";
|
||||
request.send();
|
||||
});
|
||||
}
|
||||
|
||||
class HttpReader extends Reader {
|
||||
|
||||
constructor(url, options = {}) {
|
||||
super();
|
||||
Object.assign(this, {
|
||||
url,
|
||||
reader: options.useXHR && !options.fetch ? new XHRReader(url, options) : new FetchReader(url, options)
|
||||
});
|
||||
}
|
||||
|
||||
set size(value) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
get size() {
|
||||
return this.reader.size;
|
||||
}
|
||||
|
||||
async init() {
|
||||
await this.reader.init();
|
||||
super.init();
|
||||
}
|
||||
|
||||
readUint8Array(index, length) {
|
||||
return this.reader.readUint8Array(index, length);
|
||||
}
|
||||
}
|
||||
|
||||
class HttpRangeReader extends HttpReader {
|
||||
|
||||
constructor(url, options = {}) {
|
||||
super(url, Object.assign({}, options, { useRangeHeader: true }));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Uint8ArrayReader extends Reader {
|
||||
|
||||
constructor(array) {
|
||||
super();
|
||||
array = new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
|
||||
Object.assign(this, {
|
||||
array,
|
||||
size: array.length
|
||||
});
|
||||
}
|
||||
|
||||
readUint8Array(index, length) {
|
||||
return this.array.slice(index, index + length);
|
||||
}
|
||||
}
|
||||
|
||||
class Uint8ArrayWriter extends Writer {
|
||||
|
||||
constructor(defaultBufferSize) {
|
||||
super();
|
||||
this.defaultBufferSize = defaultBufferSize || DEFAULT_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
init(initSize = 0) {
|
||||
Object.assign(this, {
|
||||
offset: 0,
|
||||
array: new Uint8Array(initSize > 0 ? initSize : this.defaultBufferSize)
|
||||
});
|
||||
super.init();
|
||||
}
|
||||
|
||||
writeUint8Array(array) {
|
||||
const writer = this;
|
||||
const requiredLength = writer.offset + array.length;
|
||||
if (requiredLength > writer.array.length) {
|
||||
let newLength = writer.array.length ? writer.array.length * 2 : writer.defaultBufferSize;
|
||||
while (newLength < requiredLength) {
|
||||
newLength *= 2;
|
||||
}
|
||||
const previousArray = writer.array;
|
||||
writer.array = new Uint8Array(newLength);
|
||||
writer.array.set(previousArray);
|
||||
}
|
||||
writer.array.set(array, writer.offset);
|
||||
writer.offset += array.length;
|
||||
}
|
||||
|
||||
getData() {
|
||||
if (this.offset === this.array.length) {
|
||||
return this.array;
|
||||
} else {
|
||||
return this.array.slice(0, this.offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SplitDataReader extends Reader {
|
||||
|
||||
constructor(readers) {
|
||||
super();
|
||||
this.readers = readers;
|
||||
}
|
||||
|
||||
async init() {
|
||||
const reader = this;
|
||||
const { readers } = reader;
|
||||
reader.lastDiskNumber = 0;
|
||||
reader.lastDiskOffset = 0;
|
||||
await Promise.all(readers.map(async (diskReader, indexDiskReader) => {
|
||||
await initStream(diskReader);
|
||||
if (indexDiskReader != readers.length - 1) {
|
||||
reader.lastDiskOffset += diskReader.size;
|
||||
}
|
||||
reader.size += diskReader.size;
|
||||
}));
|
||||
super.init();
|
||||
}
|
||||
|
||||
async readUint8Array(offset, length, diskNumber = 0) {
|
||||
const reader = this;
|
||||
const { readers } = this;
|
||||
let result;
|
||||
let currentDiskNumber = diskNumber;
|
||||
if (currentDiskNumber == -1) {
|
||||
currentDiskNumber = readers.length - 1;
|
||||
}
|
||||
let currentReaderOffset = offset;
|
||||
while (readers[currentDiskNumber] && currentReaderOffset >= readers[currentDiskNumber].size) {
|
||||
currentReaderOffset -= readers[currentDiskNumber].size;
|
||||
currentDiskNumber++;
|
||||
}
|
||||
const currentReader = readers[currentDiskNumber];
|
||||
if (currentReader) {
|
||||
const currentReaderSize = currentReader.size;
|
||||
if (currentReaderOffset + length <= currentReaderSize) {
|
||||
result = await readUint8Array(currentReader, currentReaderOffset, length);
|
||||
} else {
|
||||
const chunkLength = currentReaderSize - currentReaderOffset;
|
||||
result = new Uint8Array(length);
|
||||
const firstPart = await readUint8Array(currentReader, currentReaderOffset, chunkLength);
|
||||
result.set(firstPart, 0);
|
||||
const secondPart = await reader.readUint8Array(offset + chunkLength, length - chunkLength, diskNumber);
|
||||
result.set(secondPart, chunkLength);
|
||||
if (firstPart.length + secondPart.length < length) {
|
||||
result = result.subarray(0, firstPart.length + secondPart.length);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = EMPTY_UINT8_ARRAY;
|
||||
}
|
||||
reader.lastDiskNumber = Math.max(currentDiskNumber, reader.lastDiskNumber);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
class SplitDataWriter extends Stream {
|
||||
|
||||
constructor(writerGenerator, maxSize = 4294967295) {
|
||||
super();
|
||||
const writer = this;
|
||||
Object.assign(writer, {
|
||||
diskNumber: 0,
|
||||
diskOffset: 0,
|
||||
size: 0,
|
||||
maxSize,
|
||||
availableSize: maxSize
|
||||
});
|
||||
let diskSourceWriter, diskWritable, diskWriter;
|
||||
const writable = new WritableStream({
|
||||
async write(chunk) {
|
||||
const { availableSize } = writer;
|
||||
if (!diskWriter) {
|
||||
const { value, done } = await writerGenerator.next();
|
||||
if (done && !value) {
|
||||
throw new Error(ERR_ITERATOR_COMPLETED_TOO_SOON);
|
||||
} else {
|
||||
diskSourceWriter = value;
|
||||
diskSourceWriter.size = 0;
|
||||
if (diskSourceWriter.maxSize) {
|
||||
writer.maxSize = diskSourceWriter.maxSize;
|
||||
}
|
||||
writer.availableSize = writer.maxSize;
|
||||
await initStream(diskSourceWriter);
|
||||
diskWritable = value.writable;
|
||||
diskWriter = diskWritable.getWriter();
|
||||
}
|
||||
await this.write(chunk);
|
||||
} else if (chunk.length >= availableSize) {
|
||||
await writeChunk(chunk.subarray(0, availableSize));
|
||||
await closeDisk();
|
||||
writer.diskOffset += diskSourceWriter.size;
|
||||
writer.diskNumber++;
|
||||
diskWriter = null;
|
||||
writer.availableSize = writer.maxSize;
|
||||
if (chunk.length > availableSize) {
|
||||
await this.write(chunk.subarray(availableSize));
|
||||
}
|
||||
} else {
|
||||
await writeChunk(chunk);
|
||||
}
|
||||
},
|
||||
async close() {
|
||||
if (diskWriter) {
|
||||
await diskWriter.ready;
|
||||
await closeDisk();
|
||||
}
|
||||
},
|
||||
async abort(reason) {
|
||||
if (diskWriter) {
|
||||
await diskWriter.abort(reason);
|
||||
}
|
||||
}
|
||||
});
|
||||
Object.defineProperty(writer, PROPERTY_NAME_WRITABLE, {
|
||||
get() {
|
||||
return writable;
|
||||
}
|
||||
});
|
||||
|
||||
async function writeChunk(chunk) {
|
||||
const chunkLength = chunk.length;
|
||||
if (chunkLength) {
|
||||
await diskWriter.ready;
|
||||
await diskWriter.write(chunk);
|
||||
diskSourceWriter.size += chunkLength;
|
||||
writer.availableSize -= chunkLength;
|
||||
}
|
||||
}
|
||||
|
||||
async function closeDisk() {
|
||||
await diskWriter.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GenericReader {
|
||||
|
||||
constructor(reader) {
|
||||
if (Array.isArray(reader)) {
|
||||
reader = new SplitDataReader(reader);
|
||||
}
|
||||
if (reader instanceof ReadableStream) {
|
||||
reader = {
|
||||
readable: reader
|
||||
};
|
||||
}
|
||||
return reader;
|
||||
}
|
||||
}
|
||||
|
||||
class GenericWriter {
|
||||
|
||||
constructor(writer) {
|
||||
if (writer.writable === UNDEFINED_VALUE && typeof writer.next == FUNCTION_TYPE) {
|
||||
writer = new SplitDataWriter(writer);
|
||||
}
|
||||
if (writer instanceof WritableStream) {
|
||||
writer = {
|
||||
writable: writer
|
||||
};
|
||||
}
|
||||
if (writer.size === UNDEFINED_VALUE) {
|
||||
writer.size = 0;
|
||||
}
|
||||
if (!(writer instanceof SplitDataWriter)) {
|
||||
Object.assign(writer, {
|
||||
diskNumber: 0,
|
||||
diskOffset: 0,
|
||||
availableSize: INFINITY_VALUE,
|
||||
maxSize: INFINITY_VALUE
|
||||
});
|
||||
}
|
||||
return writer;
|
||||
}
|
||||
}
|
||||
|
||||
function isHttpFamily(url) {
|
||||
const { baseURI } = getConfiguration();
|
||||
const { protocol } = new URL(url, baseURI);
|
||||
return protocol == "http:" || protocol == "https:";
|
||||
}
|
||||
|
||||
async function initStream(stream, initSize) {
|
||||
if (stream.init && !stream.initialized) {
|
||||
await stream.init(initSize);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
function readUint8Array(reader, offset, size, diskNumber) {
|
||||
return reader.readUint8Array(offset, size, diskNumber);
|
||||
}
|
||||
|
||||
export {
|
||||
initStream,
|
||||
GenericReader,
|
||||
GenericWriter,
|
||||
readUint8Array,
|
||||
Reader,
|
||||
Writer,
|
||||
TextReader,
|
||||
TextWriter,
|
||||
Data64URIReader,
|
||||
Data64URIWriter,
|
||||
BlobReader,
|
||||
BlobWriter,
|
||||
Uint8ArrayReader,
|
||||
Uint8ArrayWriter,
|
||||
HttpReader,
|
||||
HttpRangeReader,
|
||||
SplitDataReader,
|
||||
SplitDataWriter,
|
||||
ERR_HTTP_RANGE,
|
||||
ERR_ITERATOR_COMPLETED_TOO_SOON,
|
||||
ERR_WRITER_NOT_INITIALIZED
|
||||
};
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
Copyright (c) 2022 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
const OPTION_FILENAME_ENCODING = "filenameEncoding";
|
||||
const OPTION_COMMENT_ENCODING = "commentEncoding";
|
||||
const OPTION_DECODE_TEXT = "decodeText";
|
||||
const OPTION_EXTRACT_PREPENDED_DATA = "extractPrependedData";
|
||||
const OPTION_EXTRACT_APPENDED_DATA = "extractAppendedData";
|
||||
const OPTION_PASSWORD = "password";
|
||||
const OPTION_RAW_PASSWORD = "rawPassword";
|
||||
const OPTION_PASS_THROUGH = "passThrough";
|
||||
const OPTION_SIGNAL = "signal";
|
||||
const OPTION_CHECK_PASSWORD_ONLY = "checkPasswordOnly";
|
||||
const OPTION_CHECK_OVERLAPPING_ENTRY_ONLY = "checkOverlappingEntryOnly";
|
||||
const OPTION_CHECK_OVERLAPPING_ENTRY = "checkOverlappingEntry";
|
||||
const OPTION_CHECK_AMBIGUITY = "checkAmbiguity";
|
||||
const OPTION_CHECK_SIGNATURE = "checkSignature";
|
||||
const OPTION_USE_WEB_WORKERS = "useWebWorkers";
|
||||
const OPTION_USE_COMPRESSION_STREAM = "useCompressionStream";
|
||||
const OPTION_TRANSFER_STREAMS = "transferStreams";
|
||||
const OPTION_PREVENT_CLOSE = "preventClose";
|
||||
const OPTION_ENCRYPTION_STRENGTH = "encryptionStrength";
|
||||
const OPTION_EXTENDED_TIMESTAMP = "extendedTimestamp";
|
||||
const OPTION_KEEP_ORDER = "keepOrder";
|
||||
const OPTION_LEVEL = "level";
|
||||
const OPTION_BUFFERED_WRITE = "bufferedWrite";
|
||||
const OPTION_CREATE_TEMP_STREAM = "createTempStream";
|
||||
const OPTION_DATA_DESCRIPTOR_SIGNATURE = "dataDescriptorSignature";
|
||||
const OPTION_USE_UNICODE_FILE_NAMES = "useUnicodeFileNames";
|
||||
const OPTION_DATA_DESCRIPTOR = "dataDescriptor";
|
||||
const OPTION_SUPPORT_ZIP64_SPLIT_FILE = "supportZip64SplitFile";
|
||||
const OPTION_ENCODE_TEXT = "encodeText";
|
||||
const OPTION_OFFSET = "offset";
|
||||
const OPTION_USDZ = "usdz";
|
||||
const OPTION_UNIX_EXTRA_FIELD_TYPE = "unixExtraFieldType";
|
||||
const OPTION_STRICTNESS = "strictness";
|
||||
const OPTION_MAX_APPENDED_DATA_SIZE = "maxAppendedDataSize";
|
||||
const STRICTNESS_STRICT = "strict";
|
||||
const STRICTNESS_BALANCED = "balanced";
|
||||
const STRICTNESS_TOLERANT = "tolerant";
|
||||
|
||||
export {
|
||||
OPTION_FILENAME_ENCODING,
|
||||
OPTION_COMMENT_ENCODING,
|
||||
OPTION_DECODE_TEXT,
|
||||
OPTION_EXTRACT_PREPENDED_DATA,
|
||||
OPTION_EXTRACT_APPENDED_DATA,
|
||||
OPTION_PASSWORD,
|
||||
OPTION_RAW_PASSWORD,
|
||||
OPTION_PASS_THROUGH,
|
||||
OPTION_SIGNAL,
|
||||
OPTION_CHECK_PASSWORD_ONLY,
|
||||
OPTION_CHECK_OVERLAPPING_ENTRY_ONLY,
|
||||
OPTION_CHECK_OVERLAPPING_ENTRY,
|
||||
OPTION_CHECK_AMBIGUITY,
|
||||
OPTION_CHECK_SIGNATURE,
|
||||
OPTION_USE_WEB_WORKERS,
|
||||
OPTION_USE_COMPRESSION_STREAM,
|
||||
OPTION_TRANSFER_STREAMS,
|
||||
OPTION_PREVENT_CLOSE,
|
||||
OPTION_ENCRYPTION_STRENGTH,
|
||||
OPTION_EXTENDED_TIMESTAMP,
|
||||
OPTION_KEEP_ORDER,
|
||||
OPTION_LEVEL,
|
||||
OPTION_BUFFERED_WRITE,
|
||||
OPTION_CREATE_TEMP_STREAM,
|
||||
OPTION_DATA_DESCRIPTOR_SIGNATURE,
|
||||
OPTION_USE_UNICODE_FILE_NAMES,
|
||||
OPTION_DATA_DESCRIPTOR,
|
||||
OPTION_SUPPORT_ZIP64_SPLIT_FILE,
|
||||
OPTION_ENCODE_TEXT,
|
||||
OPTION_OFFSET,
|
||||
OPTION_USDZ,
|
||||
OPTION_UNIX_EXTRA_FIELD_TYPE,
|
||||
OPTION_STRICTNESS,
|
||||
OPTION_MAX_APPENDED_DATA_SIZE,
|
||||
STRICTNESS_STRICT,
|
||||
STRICTNESS_BALANCED,
|
||||
STRICTNESS_TOLERANT
|
||||
};
|
||||
+322
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
Copyright (c) 2022 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global crypto, TransformStream */
|
||||
// deno-lint-ignore-file no-this-alias
|
||||
|
||||
import { UNDEFINED_VALUE, UNDEFINED_TYPE, FUNCTION_TYPE, EMPTY_UINT8_ARRAY } from "../constants.js";
|
||||
import { encodeText } from "./../util/encode-text.js";
|
||||
import {
|
||||
cipher,
|
||||
codec,
|
||||
misc,
|
||||
mode
|
||||
} from "./codecs/sjcl.js";
|
||||
import {
|
||||
ERR_INVALID_PASSWORD,
|
||||
ERR_INVALID_SIGNATURE,
|
||||
ERR_ABORT_CHECK_PASSWORD,
|
||||
getRandomValues
|
||||
} from "./common-crypto.js";
|
||||
|
||||
const BLOCK_LENGTH = 16;
|
||||
const RAW_FORMAT = "raw";
|
||||
const PBKDF2_ALGORITHM = { name: "PBKDF2" };
|
||||
const HASH_ALGORITHM = { name: "HMAC" };
|
||||
const HASH_FUNCTION = "SHA-1";
|
||||
const BASE_KEY_ALGORITHM = Object.assign({ hash: HASH_ALGORITHM }, PBKDF2_ALGORITHM);
|
||||
const DERIVED_BITS_ALGORITHM = Object.assign({ iterations: 1000, hash: { name: HASH_FUNCTION } }, PBKDF2_ALGORITHM);
|
||||
const DERIVED_BITS_USAGE = ["deriveBits"];
|
||||
const SALT_LENGTH = [8, 12, 16];
|
||||
const KEY_LENGTH = [16, 24, 32];
|
||||
const SIGNATURE_LENGTH = 10;
|
||||
const COUNTER_DEFAULT_VALUE = [0, 0, 0, 0];
|
||||
// deno-lint-ignore valid-typeof
|
||||
const CRYPTO_API_SUPPORTED = typeof crypto != UNDEFINED_TYPE;
|
||||
const subtle = CRYPTO_API_SUPPORTED && crypto.subtle;
|
||||
const SUBTLE_API_SUPPORTED = CRYPTO_API_SUPPORTED && typeof subtle != UNDEFINED_TYPE;
|
||||
const codecBytes = codec.bytes;
|
||||
const Aes = cipher.aes;
|
||||
const CtrGladman = mode.ctrGladman;
|
||||
const HmacSha1 = misc.hmacSha1;
|
||||
|
||||
let IMPORT_KEY_SUPPORTED = CRYPTO_API_SUPPORTED && SUBTLE_API_SUPPORTED && typeof subtle.importKey == FUNCTION_TYPE;
|
||||
let DERIVE_BITS_SUPPORTED = CRYPTO_API_SUPPORTED && SUBTLE_API_SUPPORTED && typeof subtle.deriveBits == FUNCTION_TYPE;
|
||||
|
||||
class AESDecryptionStream extends TransformStream {
|
||||
|
||||
constructor({ password, rawPassword, encryptionStrength, checkPasswordOnly }) {
|
||||
super({
|
||||
start() {
|
||||
initAesCrypto(this, password, rawPassword, encryptionStrength);
|
||||
},
|
||||
async transform(chunk, controller) {
|
||||
const aesCrypto = this;
|
||||
const {
|
||||
password,
|
||||
strength,
|
||||
resolveReady,
|
||||
ready
|
||||
} = aesCrypto;
|
||||
if (password) {
|
||||
await createDecryptionKeys(aesCrypto, strength, password, subarray(chunk, 0, SALT_LENGTH[strength] + 2));
|
||||
chunk = subarray(chunk, SALT_LENGTH[strength] + 2);
|
||||
if (checkPasswordOnly) {
|
||||
controller.error(new Error(ERR_ABORT_CHECK_PASSWORD));
|
||||
} else {
|
||||
resolveReady();
|
||||
}
|
||||
} else {
|
||||
await ready;
|
||||
}
|
||||
const output = new Uint8Array(chunk.length - SIGNATURE_LENGTH - ((chunk.length - SIGNATURE_LENGTH) % BLOCK_LENGTH));
|
||||
controller.enqueue(append(aesCrypto, chunk, output, 0, SIGNATURE_LENGTH, true));
|
||||
},
|
||||
async flush(controller) {
|
||||
const {
|
||||
ctr,
|
||||
hmac,
|
||||
pending,
|
||||
ready
|
||||
} = this;
|
||||
if (hmac && ctr) {
|
||||
await ready;
|
||||
const chunkToDecrypt = subarray(pending, 0, pending.length - SIGNATURE_LENGTH);
|
||||
const originalSignature = subarray(pending, pending.length - SIGNATURE_LENGTH);
|
||||
let decryptedChunkArray = EMPTY_UINT8_ARRAY;
|
||||
if (chunkToDecrypt.length) {
|
||||
const encryptedChunk = toBits(codecBytes, chunkToDecrypt);
|
||||
hmac.update(encryptedChunk);
|
||||
const decryptedChunk = ctr.update(encryptedChunk);
|
||||
decryptedChunkArray = fromBits(codecBytes, decryptedChunk);
|
||||
}
|
||||
const signature = subarray(fromBits(codecBytes, hmac.digest()), 0, SIGNATURE_LENGTH);
|
||||
let invalidSignature = pending.length < SIGNATURE_LENGTH ? 1 : 0;
|
||||
for (let indexSignature = 0; indexSignature < SIGNATURE_LENGTH; indexSignature++) {
|
||||
invalidSignature |= signature[indexSignature] ^ originalSignature[indexSignature];
|
||||
}
|
||||
if (invalidSignature) {
|
||||
throw new Error(ERR_INVALID_SIGNATURE);
|
||||
}
|
||||
controller.enqueue(decryptedChunkArray);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class AESEncryptionStream extends TransformStream {
|
||||
|
||||
constructor({ password, rawPassword, encryptionStrength }) {
|
||||
// deno-lint-ignore prefer-const
|
||||
let stream;
|
||||
super({
|
||||
start() {
|
||||
initAesCrypto(this, password, rawPassword, encryptionStrength);
|
||||
},
|
||||
async transform(chunk, controller) {
|
||||
const aesCrypto = this;
|
||||
const {
|
||||
password,
|
||||
strength,
|
||||
resolveReady,
|
||||
ready
|
||||
} = aesCrypto;
|
||||
let preamble = EMPTY_UINT8_ARRAY;
|
||||
if (password) {
|
||||
preamble = await createEncryptionKeys(aesCrypto, strength, password);
|
||||
resolveReady();
|
||||
} else {
|
||||
await ready;
|
||||
}
|
||||
const output = new Uint8Array(preamble.length + chunk.length - (chunk.length % BLOCK_LENGTH));
|
||||
output.set(preamble, 0);
|
||||
controller.enqueue(append(aesCrypto, chunk, output, preamble.length, 0));
|
||||
},
|
||||
async flush(controller) {
|
||||
const {
|
||||
ctr,
|
||||
hmac,
|
||||
pending,
|
||||
ready
|
||||
} = this;
|
||||
if (hmac && ctr) {
|
||||
await ready;
|
||||
let encryptedChunkArray = EMPTY_UINT8_ARRAY;
|
||||
if (pending.length) {
|
||||
const encryptedChunk = ctr.update(toBits(codecBytes, pending));
|
||||
hmac.update(encryptedChunk);
|
||||
encryptedChunkArray = fromBits(codecBytes, encryptedChunk);
|
||||
}
|
||||
stream.signature = fromBits(codecBytes, hmac.digest()).slice(0, SIGNATURE_LENGTH);
|
||||
controller.enqueue(concat(encryptedChunkArray, stream.signature));
|
||||
}
|
||||
}
|
||||
});
|
||||
stream = this;
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
AESDecryptionStream,
|
||||
AESEncryptionStream
|
||||
};
|
||||
|
||||
function initAesCrypto(aesCrypto, password, rawPassword, encryptionStrength) {
|
||||
Object.assign(aesCrypto, {
|
||||
ready: new Promise(resolve => aesCrypto.resolveReady = resolve),
|
||||
password: encodePassword(password, rawPassword),
|
||||
strength: encryptionStrength - 1,
|
||||
pending: EMPTY_UINT8_ARRAY
|
||||
});
|
||||
}
|
||||
|
||||
function append(aesCrypto, input, output, paddingStart, paddingEnd, verifySignature) {
|
||||
const {
|
||||
ctr,
|
||||
hmac,
|
||||
pending
|
||||
} = aesCrypto;
|
||||
if (pending.length) {
|
||||
input = concat(pending, input);
|
||||
}
|
||||
const inputLength = input.length - paddingEnd;
|
||||
output = expand(output, paddingStart + (inputLength - (inputLength % BLOCK_LENGTH)));
|
||||
let offset;
|
||||
for (offset = 0; offset <= inputLength - BLOCK_LENGTH; offset += BLOCK_LENGTH) {
|
||||
const inputChunk = toBits(codecBytes, subarray(input, offset, offset + BLOCK_LENGTH));
|
||||
if (verifySignature) {
|
||||
hmac.update(inputChunk);
|
||||
}
|
||||
const outputChunk = ctr.update(inputChunk);
|
||||
if (!verifySignature) {
|
||||
hmac.update(outputChunk);
|
||||
}
|
||||
output.set(fromBits(codecBytes, outputChunk), offset + paddingStart);
|
||||
}
|
||||
aesCrypto.pending = subarray(input, offset);
|
||||
return output;
|
||||
}
|
||||
|
||||
async function createDecryptionKeys(decrypt, strength, password, preamble) {
|
||||
const passwordVerificationKey = await createKeys(decrypt, strength, password, subarray(preamble, 0, SALT_LENGTH[strength]));
|
||||
const passwordVerification = subarray(preamble, SALT_LENGTH[strength]);
|
||||
if (passwordVerificationKey[0] != passwordVerification[0] || passwordVerificationKey[1] != passwordVerification[1]) {
|
||||
throw new Error(ERR_INVALID_PASSWORD);
|
||||
}
|
||||
}
|
||||
|
||||
async function createEncryptionKeys(encrypt, strength, password) {
|
||||
const salt = getRandomValues(new Uint8Array(SALT_LENGTH[strength]));
|
||||
const passwordVerification = await createKeys(encrypt, strength, password, salt);
|
||||
return concat(salt, passwordVerification);
|
||||
}
|
||||
|
||||
async function createKeys(aesCrypto, strength, password, salt) {
|
||||
aesCrypto.password = null;
|
||||
const baseKey = await importKey(RAW_FORMAT, password, BASE_KEY_ALGORITHM, false, DERIVED_BITS_USAGE);
|
||||
const derivedBits = await deriveBits(Object.assign({ salt }, DERIVED_BITS_ALGORITHM), baseKey, 8 * ((KEY_LENGTH[strength] * 2) + 2));
|
||||
const compositeKey = new Uint8Array(derivedBits);
|
||||
const key = toBits(codecBytes, subarray(compositeKey, 0, KEY_LENGTH[strength]));
|
||||
const authentication = toBits(codecBytes, subarray(compositeKey, KEY_LENGTH[strength], KEY_LENGTH[strength] * 2));
|
||||
const passwordVerification = subarray(compositeKey, KEY_LENGTH[strength] * 2);
|
||||
Object.assign(aesCrypto, {
|
||||
keys: {
|
||||
key,
|
||||
authentication,
|
||||
passwordVerification
|
||||
},
|
||||
ctr: new CtrGladman(new Aes(key), Array.from(COUNTER_DEFAULT_VALUE)),
|
||||
hmac: new HmacSha1(authentication)
|
||||
});
|
||||
return passwordVerification;
|
||||
}
|
||||
|
||||
async function importKey(format, password, algorithm, extractable, keyUsages) {
|
||||
if (IMPORT_KEY_SUPPORTED) {
|
||||
try {
|
||||
return await subtle.importKey(format, password, algorithm, extractable, keyUsages);
|
||||
} catch {
|
||||
IMPORT_KEY_SUPPORTED = false;
|
||||
return misc.importKey(password);
|
||||
}
|
||||
} else {
|
||||
return misc.importKey(password);
|
||||
}
|
||||
}
|
||||
|
||||
async function deriveBits(algorithm, baseKey, length) {
|
||||
if (DERIVE_BITS_SUPPORTED) {
|
||||
try {
|
||||
return await subtle.deriveBits(algorithm, baseKey, length);
|
||||
} catch {
|
||||
DERIVE_BITS_SUPPORTED = false;
|
||||
return misc.pbkdf2(baseKey, algorithm.salt, DERIVED_BITS_ALGORITHM.iterations, length);
|
||||
}
|
||||
} else {
|
||||
return misc.pbkdf2(baseKey, algorithm.salt, DERIVED_BITS_ALGORITHM.iterations, length);
|
||||
}
|
||||
}
|
||||
|
||||
function encodePassword(password, rawPassword) {
|
||||
if (rawPassword === UNDEFINED_VALUE) {
|
||||
return encodeText(password);
|
||||
} else {
|
||||
return rawPassword;
|
||||
}
|
||||
}
|
||||
|
||||
function concat(leftArray, rightArray) {
|
||||
let array = leftArray;
|
||||
if (leftArray.length + rightArray.length) {
|
||||
array = new Uint8Array(leftArray.length + rightArray.length);
|
||||
array.set(leftArray, 0);
|
||||
array.set(rightArray, leftArray.length);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
function expand(inputArray, length) {
|
||||
if (length && length > inputArray.length) {
|
||||
const array = inputArray;
|
||||
inputArray = new Uint8Array(length);
|
||||
inputArray.set(array, 0);
|
||||
}
|
||||
return inputArray;
|
||||
}
|
||||
|
||||
function subarray(array, begin, end) {
|
||||
return array.subarray(begin, end);
|
||||
}
|
||||
|
||||
function fromBits(codecBytes, chunk) {
|
||||
return codecBytes.fromBits(chunk);
|
||||
}
|
||||
function toBits(codecBytes, chunk) {
|
||||
return codecBytes.toBits(chunk);
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
Copyright (c) 2022 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is based on JZlib 1.0.2 ymnk, JCraft,Inc.
|
||||
* JZlib is based on zlib-1.1.3, so all credit should go authors
|
||||
* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
|
||||
* and contributors of zlib.
|
||||
*/
|
||||
|
||||
/* global TransformStream */
|
||||
// deno-lint-ignore-file no-this-alias
|
||||
|
||||
import { UNDEFINED_VALUE } from "../constants.js";
|
||||
import {
|
||||
ERR_INVALID_PASSWORD,
|
||||
ERR_INVALID_SIGNATURE,
|
||||
ERR_INVALID_UNCOMPRESSED_SIZE,
|
||||
ERR_INVALID_COMPRESSED_DATA,
|
||||
ERR_ABORT_CHECK_PASSWORD,
|
||||
InflateStream,
|
||||
DeflateStream
|
||||
} from "./zip-entry-stream.js";
|
||||
|
||||
const DEFAULT_CHUNK_SIZE = 64 * 1024;
|
||||
const MESSAGE_EVENT_TYPE = "message";
|
||||
const MESSAGE_START = "start";
|
||||
const MESSAGE_PULL = "pull";
|
||||
const MESSAGE_DATA = "data";
|
||||
const MESSAGE_ACK_DATA = "ack";
|
||||
const MESSAGE_CLOSE = "close";
|
||||
const CODEC_DEFLATE = "deflate";
|
||||
const CODEC_INFLATE = "inflate";
|
||||
|
||||
export {
|
||||
CODEC_DEFLATE,
|
||||
CODEC_INFLATE,
|
||||
MESSAGE_EVENT_TYPE,
|
||||
MESSAGE_START,
|
||||
MESSAGE_PULL,
|
||||
MESSAGE_DATA,
|
||||
MESSAGE_ACK_DATA,
|
||||
MESSAGE_CLOSE,
|
||||
ERR_INVALID_PASSWORD,
|
||||
ERR_INVALID_SIGNATURE,
|
||||
ERR_INVALID_UNCOMPRESSED_SIZE,
|
||||
ERR_INVALID_COMPRESSED_DATA,
|
||||
ERR_ABORT_CHECK_PASSWORD,
|
||||
CodecStream,
|
||||
ChunkStream
|
||||
};
|
||||
|
||||
class CodecStream extends TransformStream {
|
||||
|
||||
constructor(options, config) {
|
||||
super({});
|
||||
const codec = this;
|
||||
const { codecType } = options;
|
||||
let Stream;
|
||||
if (codecType.startsWith(CODEC_DEFLATE)) {
|
||||
Stream = DeflateStream;
|
||||
} else if (codecType.startsWith(CODEC_INFLATE)) {
|
||||
Stream = InflateStream;
|
||||
}
|
||||
codec.outputSize = 0;
|
||||
let inputSize = 0;
|
||||
const stream = new Stream(options, config);
|
||||
const readable = super.readable;
|
||||
const inputSizeStream = new TransformStream({
|
||||
transform(chunk, controller) {
|
||||
if (chunk && chunk.length) {
|
||||
inputSize += chunk.length;
|
||||
controller.enqueue(chunk);
|
||||
}
|
||||
},
|
||||
flush() {
|
||||
Object.assign(codec, {
|
||||
inputSize
|
||||
});
|
||||
}
|
||||
});
|
||||
const outputSizeStream = new TransformStream({
|
||||
transform(chunk, controller) {
|
||||
if (chunk && chunk.length) {
|
||||
controller.enqueue(chunk);
|
||||
codec.outputSize += chunk.length;
|
||||
if (options.outputSize !== UNDEFINED_VALUE && codec.outputSize > options.outputSize) {
|
||||
throw new Error(ERR_INVALID_UNCOMPRESSED_SIZE);
|
||||
}
|
||||
}
|
||||
},
|
||||
flush() {
|
||||
const { signature } = stream;
|
||||
Object.assign(codec, {
|
||||
signature,
|
||||
inputSize
|
||||
});
|
||||
}
|
||||
});
|
||||
Object.defineProperty(codec, "readable", {
|
||||
get() {
|
||||
return readable.pipeThrough(inputSizeStream).pipeThrough(stream).pipeThrough(outputSizeStream);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ChunkStream extends TransformStream {
|
||||
|
||||
constructor(chunkSize) {
|
||||
let pendingChunk;
|
||||
if (!(chunkSize >= 1)) {
|
||||
chunkSize = DEFAULT_CHUNK_SIZE;
|
||||
}
|
||||
super({
|
||||
transform,
|
||||
flush(controller) {
|
||||
if (pendingChunk && pendingChunk.length) {
|
||||
controller.enqueue(pendingChunk);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function transform(chunk, controller) {
|
||||
if (pendingChunk) {
|
||||
const newChunk = new Uint8Array(pendingChunk.length + chunk.length);
|
||||
newChunk.set(pendingChunk);
|
||||
newChunk.set(chunk, pendingChunk.length);
|
||||
chunk = newChunk;
|
||||
pendingChunk = null;
|
||||
}
|
||||
let offset = 0;
|
||||
while (chunk.length - offset > chunkSize) {
|
||||
controller.enqueue(chunk.slice(offset, offset + chunkSize));
|
||||
offset += chunkSize;
|
||||
}
|
||||
pendingChunk = offset ? chunk.slice(offset) : chunk;
|
||||
}
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
Copyright (c) 2022 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// Slicing-by-8 CRC-32 (Intel / zlib). The eight 256-entry tables let the inner loop
|
||||
// consume 8 bytes per iteration with a shorter dependency chain, ~4x the byte-at-a-time
|
||||
// rate (measured ~320 -> ~1400 MB/s on 64KB chunks).
|
||||
//
|
||||
// Every table MUST stay a PACKED_SMI array: build with array literals (not `new Array(n)`,
|
||||
// which is HOLEY) and store the signed int32 XOR result (no `>>> 0`). An unsigned or holey
|
||||
// table becomes a V8 FixedDoubleArray whose every hot-loop lookup unboxes a double (~1.6x
|
||||
// slower). Signedness is irrelevant to the result — the reads mask/shift it and the final
|
||||
// `~crc` normalizes it. Do NOT reintroduce `>>> 0` here or switch to `new Array(256)`.
|
||||
const T = [[], [], [], [], [], [], [], []];
|
||||
for (let n = 0; n < 256; n++) {
|
||||
let t = n;
|
||||
for (let j = 0; j < 8; j++) {
|
||||
t = (t & 1) ? (t >>> 1) ^ 0xEDB88320 : t >>> 1;
|
||||
}
|
||||
T[0][n] = t;
|
||||
}
|
||||
for (let n = 0; n < 256; n++) {
|
||||
for (let k = 1; k < 8; k++) {
|
||||
const previous = T[k - 1][n];
|
||||
T[k][n] = (previous >>> 8) ^ T[0][previous & 0xFF];
|
||||
}
|
||||
}
|
||||
const [T0, T1, T2, T3, T4, T5, T6, T7] = T;
|
||||
|
||||
class Crc32 {
|
||||
|
||||
constructor(crc) {
|
||||
this.crc = crc || -1;
|
||||
}
|
||||
|
||||
append(data) {
|
||||
let crc = this.crc | 0;
|
||||
const length = data.length | 0;
|
||||
let offset = 0;
|
||||
// Process 8 bytes per iteration over the typed-array body. DataView.getInt32(le)
|
||||
// reads an unaligned little-endian word as a signed int32 (no double boxing), so no
|
||||
// alignment or endianness handling is needed; data.buffer guards non-typed inputs.
|
||||
if (length >= 8 && data.buffer) {
|
||||
const view = new DataView(data.buffer, data.byteOffset, length);
|
||||
const end = length - 8;
|
||||
for (; offset <= end; offset += 8) {
|
||||
const a = crc ^ view.getInt32(offset, true);
|
||||
const b = view.getInt32(offset + 4, true);
|
||||
crc = T7[a & 0xFF] ^ T6[(a >>> 8) & 0xFF] ^ T5[(a >>> 16) & 0xFF] ^ T4[(a >>> 24) & 0xFF] ^
|
||||
T3[b & 0xFF] ^ T2[(b >>> 8) & 0xFF] ^ T1[(b >>> 16) & 0xFF] ^ T0[(b >>> 24) & 0xFF];
|
||||
}
|
||||
}
|
||||
// Remaining tail (and non-typed inputs) byte-at-a-time with the base table.
|
||||
for (; offset < length; offset++) {
|
||||
crc = (crc >>> 8) ^ T0[(crc ^ data[offset]) & 0xFF];
|
||||
}
|
||||
this.crc = crc;
|
||||
}
|
||||
|
||||
get() {
|
||||
return ~this.crc;
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
Crc32
|
||||
};
|
||||
+795
@@ -0,0 +1,795 @@
|
||||
// Derived from https://github.com/xqdoo00o/jszip/blob/master/lib/sjcl.js and https://github.com/bitwiseshiftleft/sjcl
|
||||
|
||||
// deno-lint-ignore-file no-this-alias
|
||||
|
||||
/*
|
||||
* SJCL is open. You can use, modify and redistribute it under a BSD
|
||||
* license or under the GNU GPL, version 2.0.
|
||||
*/
|
||||
|
||||
/** @fileOverview Javascript cryptography implementation.
|
||||
*
|
||||
* Crush to remove comments, shorten variable names and
|
||||
* generally reduce transmission size.
|
||||
*
|
||||
* @author Emily Stark
|
||||
* @author Mike Hamburg
|
||||
* @author Dan Boneh
|
||||
*/
|
||||
|
||||
/*jslint indent: 2, bitwise: false, nomen: false, plusplus: false, white: false, regexp: false */
|
||||
|
||||
/** @fileOverview Arrays of bits, encoded as arrays of Numbers.
|
||||
*
|
||||
* @author Emily Stark
|
||||
* @author Mike Hamburg
|
||||
* @author Dan Boneh
|
||||
*/
|
||||
|
||||
/**
|
||||
* Arrays of bits, encoded as arrays of Numbers.
|
||||
* @namespace
|
||||
* @description
|
||||
* <p>
|
||||
* These objects are the currency accepted by SJCL's crypto functions.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Most of our crypto primitives operate on arrays of 4-byte words internally,
|
||||
* but many of them can take arguments that are not a multiple of 4 bytes.
|
||||
* This library encodes arrays of bits (whose size need not be a multiple of 8
|
||||
* bits) as arrays of 32-bit words. The bits are packed, big-endian, into an
|
||||
* array of words, 32 bits at a time. Since the words are double-precision
|
||||
* floating point numbers, they fit some extra data. We use this (in a private,
|
||||
* possibly-changing manner) to encode the number of bits actually present
|
||||
* in the last word of the array.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Because bitwise ops clear this out-of-band data, these arrays can be passed
|
||||
* to ciphers like AES which want arrays of words.
|
||||
* </p>
|
||||
*/
|
||||
const bitArray = {
|
||||
/**
|
||||
* Concatenate two bit arrays.
|
||||
* @param {bitArray} a1 The first array.
|
||||
* @param {bitArray} a2 The second array.
|
||||
* @return {bitArray} The concatenation of a1 and a2.
|
||||
*/
|
||||
concat(a1, a2) {
|
||||
if (a1.length === 0 || a2.length === 0) {
|
||||
return a1.concat(a2);
|
||||
}
|
||||
|
||||
const last = a1[a1.length - 1], shift = bitArray.getPartial(last);
|
||||
if (shift === 32) {
|
||||
return a1.concat(a2);
|
||||
} else {
|
||||
return bitArray._shiftRight(a2, shift, last | 0, a1.slice(0, a1.length - 1));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Find the length of an array of bits.
|
||||
* @param {bitArray} a The array.
|
||||
* @return {Number} The length of a, in bits.
|
||||
*/
|
||||
bitLength(a) {
|
||||
const l = a.length;
|
||||
if (l === 0) {
|
||||
return 0;
|
||||
}
|
||||
const x = a[l - 1];
|
||||
return (l - 1) * 32 + bitArray.getPartial(x);
|
||||
},
|
||||
|
||||
/**
|
||||
* Truncate an array.
|
||||
* @param {bitArray} a The array.
|
||||
* @param {Number} len The length to truncate to, in bits.
|
||||
* @return {bitArray} A new array, truncated to len bits.
|
||||
*/
|
||||
clamp(a, len) {
|
||||
if (a.length * 32 < len) {
|
||||
return a;
|
||||
}
|
||||
a = a.slice(0, Math.ceil(len / 32));
|
||||
const l = a.length;
|
||||
len = len & 31;
|
||||
if (l > 0 && len) {
|
||||
a[l - 1] = bitArray.partial(len, a[l - 1] & 0x80000000 >> (len - 1), 1);
|
||||
}
|
||||
return a;
|
||||
},
|
||||
|
||||
/**
|
||||
* Make a partial word for a bit array.
|
||||
* @param {Number} len The number of bits in the word.
|
||||
* @param {Number} x The bits.
|
||||
* @param {Number} [_end=0] Pass 1 if x has already been shifted to the high side.
|
||||
* @return {Number} The partial word.
|
||||
*/
|
||||
partial(len, x, _end) {
|
||||
if (len === 32) {
|
||||
return x;
|
||||
}
|
||||
return (_end ? x | 0 : x << (32 - len)) + len * 0x10000000000;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the number of bits used by a partial word.
|
||||
* @param {Number} x The partial word.
|
||||
* @return {Number} The number of bits used by the partial word.
|
||||
*/
|
||||
getPartial(x) {
|
||||
return Math.round(x / 0x10000000000) || 32;
|
||||
},
|
||||
|
||||
/** Shift an array right.
|
||||
* @param {bitArray} a The array to shift.
|
||||
* @param {Number} shift The number of bits to shift.
|
||||
* @param {Number} [carry=0] A byte to carry in
|
||||
* @param {bitArray} [out=[]] An array to prepend to the output.
|
||||
* @private
|
||||
*/
|
||||
_shiftRight(a, shift, carry, out) {
|
||||
if (out === undefined) {
|
||||
out = [];
|
||||
}
|
||||
|
||||
for (; shift >= 32; shift -= 32) {
|
||||
out.push(carry);
|
||||
carry = 0;
|
||||
}
|
||||
if (shift === 0) {
|
||||
return out.concat(a);
|
||||
}
|
||||
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
out.push(carry | a[i] >>> shift);
|
||||
carry = a[i] << (32 - shift);
|
||||
}
|
||||
const last2 = a.length ? a[a.length - 1] : 0;
|
||||
const shift2 = bitArray.getPartial(last2);
|
||||
out.push(bitArray.partial(shift + shift2 & 31, (shift + shift2 > 32) ? carry : out.pop(), 1));
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
/** @fileOverview Bit array codec implementations.
|
||||
*
|
||||
* @author Emily Stark
|
||||
* @author Mike Hamburg
|
||||
* @author Dan Boneh
|
||||
*/
|
||||
|
||||
/**
|
||||
* Arrays of bytes
|
||||
* @namespace
|
||||
*/
|
||||
const codec = {
|
||||
bytes: {
|
||||
/** Convert from a bitArray to an array of bytes. */
|
||||
fromBits(arr) {
|
||||
const bl = bitArray.bitLength(arr);
|
||||
const byteLength = bl / 8;
|
||||
const out = new Uint8Array(byteLength);
|
||||
let tmp;
|
||||
for (let i = 0; i < byteLength; i++) {
|
||||
if ((i & 3) === 0) {
|
||||
tmp = arr[i / 4];
|
||||
}
|
||||
out[i] = tmp >>> 24;
|
||||
tmp <<= 8;
|
||||
}
|
||||
return out;
|
||||
},
|
||||
/** Convert from an array of bytes to a bitArray. */
|
||||
toBits(bytes) {
|
||||
const out = [];
|
||||
let i;
|
||||
let tmp = 0;
|
||||
for (i = 0; i < bytes.length; i++) {
|
||||
tmp = tmp << 8 | bytes[i];
|
||||
if ((i & 3) === 3) {
|
||||
out.push(tmp);
|
||||
tmp = 0;
|
||||
}
|
||||
}
|
||||
if (i & 3) {
|
||||
out.push(bitArray.partial(8 * (i & 3), tmp));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const hash = {};
|
||||
|
||||
/**
|
||||
* Context for a SHA-1 operation in progress.
|
||||
* @constructor
|
||||
*/
|
||||
hash.sha1 = class {
|
||||
constructor(hash) {
|
||||
const sha1 = this;
|
||||
/**
|
||||
* The hash's block size, in bits.
|
||||
* @constant
|
||||
*/
|
||||
sha1.blockSize = 512;
|
||||
/**
|
||||
* The SHA-1 initialization vector.
|
||||
* @private
|
||||
*/
|
||||
sha1._init = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0];
|
||||
/**
|
||||
* The SHA-1 hash key.
|
||||
* @private
|
||||
*/
|
||||
sha1._key = [0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6];
|
||||
if (hash) {
|
||||
sha1._h = hash._h.slice(0);
|
||||
sha1._buffer = hash._buffer.slice(0);
|
||||
sha1._length = hash._length;
|
||||
} else {
|
||||
sha1.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the hash state.
|
||||
* @return this
|
||||
*/
|
||||
reset() {
|
||||
const sha1 = this;
|
||||
sha1._h = sha1._init.slice(0);
|
||||
sha1._buffer = [];
|
||||
sha1._length = 0;
|
||||
return sha1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Input several words to the hash.
|
||||
* @param {bitArray|String} data the data to hash.
|
||||
* @return this
|
||||
*/
|
||||
update(data) {
|
||||
const sha1 = this;
|
||||
if (typeof data === "string") {
|
||||
data = codec.utf8String.toBits(data);
|
||||
}
|
||||
const b = sha1._buffer = bitArray.concat(sha1._buffer, data);
|
||||
const ol = sha1._length;
|
||||
const nl = sha1._length = ol + bitArray.bitLength(data);
|
||||
if (nl > 9007199254740991) {
|
||||
throw new Error("Cannot hash more than 2^53 - 1 bits");
|
||||
}
|
||||
const c = new Uint32Array(b);
|
||||
let j = 0;
|
||||
for (let i = sha1.blockSize + ol - ((sha1.blockSize + ol) & (sha1.blockSize - 1)); i <= nl;
|
||||
i += sha1.blockSize) {
|
||||
sha1._block(c.subarray(16 * j, 16 * (j + 1)));
|
||||
j += 1;
|
||||
}
|
||||
b.splice(0, 16 * j);
|
||||
return sha1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete hashing and output the hash value.
|
||||
* @return {bitArray} The hash value, an array of 5 big-endian words. TODO
|
||||
*/
|
||||
finalize() {
|
||||
const sha1 = this;
|
||||
let b = sha1._buffer;
|
||||
const h = sha1._h;
|
||||
|
||||
// Round out and push the buffer
|
||||
b = bitArray.concat(b, [bitArray.partial(1, 1)]);
|
||||
// Round out the buffer to a multiple of 16 words, less the 2 length words.
|
||||
for (let i = b.length + 2; i & 15; i++) {
|
||||
b.push(0);
|
||||
}
|
||||
|
||||
// append the length
|
||||
b.push(Math.floor(sha1._length / 0x100000000));
|
||||
b.push(sha1._length | 0);
|
||||
|
||||
while (b.length) {
|
||||
sha1._block(b.splice(0, 16));
|
||||
}
|
||||
|
||||
sha1.reset();
|
||||
return h;
|
||||
}
|
||||
|
||||
/**
|
||||
* The SHA-1 logical functions f(0), f(1), ..., f(79).
|
||||
* @private
|
||||
*/
|
||||
_f(t, b, c, d) {
|
||||
if (t <= 19) {
|
||||
return (b & c) | (~b & d);
|
||||
} else if (t <= 39) {
|
||||
return b ^ c ^ d;
|
||||
} else if (t <= 59) {
|
||||
return (b & c) | (b & d) | (c & d);
|
||||
} else if (t <= 79) {
|
||||
return b ^ c ^ d;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Circular left-shift operator.
|
||||
* @private
|
||||
*/
|
||||
_S(n, x) {
|
||||
return (x << n) | (x >>> 32 - n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform one cycle of SHA-1.
|
||||
* @param {Uint32Array|bitArray} words one block of words.
|
||||
* @private
|
||||
*/
|
||||
_block(words) {
|
||||
const sha1 = this;
|
||||
const h = sha1._h;
|
||||
// When words is passed to _block, it has 16 elements. SHA1 _block
|
||||
// function extends words with new elements (at the end there are 80 elements).
|
||||
// The problem is that if we use Uint32Array instead of Array,
|
||||
// the length of Uint32Array cannot be changed. Thus, we replace words with a
|
||||
// normal Array here.
|
||||
const w = Array(80); // do not use Uint32Array here as the instantiation is slower
|
||||
for (let j = 0; j < 16; j++) {
|
||||
w[j] = words[j];
|
||||
}
|
||||
|
||||
let a = h[0];
|
||||
let b = h[1];
|
||||
let c = h[2];
|
||||
let d = h[3];
|
||||
let e = h[4];
|
||||
|
||||
for (let t = 0; t <= 79; t++) {
|
||||
if (t >= 16) {
|
||||
w[t] = sha1._S(1, w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16]);
|
||||
}
|
||||
const tmp = (sha1._S(5, a) + sha1._f(t, b, c, d) + e + w[t] +
|
||||
sha1._key[Math.floor(t / 20)]) | 0;
|
||||
e = d;
|
||||
d = c;
|
||||
c = sha1._S(30, b);
|
||||
b = a;
|
||||
a = tmp;
|
||||
}
|
||||
|
||||
h[0] = (h[0] + a) | 0;
|
||||
h[1] = (h[1] + b) | 0;
|
||||
h[2] = (h[2] + c) | 0;
|
||||
h[3] = (h[3] + d) | 0;
|
||||
h[4] = (h[4] + e) | 0;
|
||||
}
|
||||
};
|
||||
|
||||
/** @fileOverview Low-level AES implementation.
|
||||
*
|
||||
* This file contains a low-level implementation of AES, optimized for
|
||||
* size and for efficiency on several browsers. It is based on
|
||||
* OpenSSL's aes_core.c, a public-domain implementation by Vincent
|
||||
* Rijmen, Antoon Bosselaers and Paulo Barreto.
|
||||
*
|
||||
* An older version of this implementation is available in the public
|
||||
* domain, but this one is (c) Emily Stark, Mike Hamburg, Dan Boneh,
|
||||
* Stanford University 2008-2010 and BSD-licensed for liability
|
||||
* reasons.
|
||||
*
|
||||
* @author Emily Stark
|
||||
* @author Mike Hamburg
|
||||
* @author Dan Boneh
|
||||
*/
|
||||
|
||||
const cipher = {};
|
||||
|
||||
/**
|
||||
* Schedule out an AES key for both encryption and decryption. This
|
||||
* is a low-level class. Use a cipher mode to do bulk encryption.
|
||||
*
|
||||
* @constructor
|
||||
* @param {Array} key The key as an array of 4, 6 or 8 words.
|
||||
*/
|
||||
cipher.aes = class {
|
||||
constructor(key) {
|
||||
/**
|
||||
* The expanded S-box and inverse S-box tables. These will be computed
|
||||
* on the client so that we don't have to send them down the wire.
|
||||
*
|
||||
* There are two tables, _tables[0] is for encryption and
|
||||
* _tables[1] is for decryption.
|
||||
*
|
||||
* The first 4 sub-tables are the expanded S-box with MixColumns. The
|
||||
* last (_tables[01][4]) is the S-box itself.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
const aes = this;
|
||||
aes._tables = [[[], [], [], [], []], [[], [], [], [], []]];
|
||||
|
||||
if (!aes._tables[0][0][0]) {
|
||||
aes._precompute();
|
||||
}
|
||||
|
||||
const sbox = aes._tables[0][4];
|
||||
const decTable = aes._tables[1];
|
||||
const keyLen = key.length;
|
||||
|
||||
let i, encKey, decKey, rcon = 1;
|
||||
|
||||
if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) {
|
||||
throw new Error("invalid aes key size");
|
||||
}
|
||||
|
||||
aes._key = [encKey = key.slice(0), decKey = []];
|
||||
|
||||
// schedule encryption keys
|
||||
for (i = keyLen; i < 4 * keyLen + 28; i++) {
|
||||
let tmp = encKey[i - 1];
|
||||
|
||||
// apply sbox
|
||||
if (i % keyLen === 0 || (keyLen === 8 && i % keyLen === 4)) {
|
||||
tmp = sbox[tmp >>> 24] << 24 ^ sbox[tmp >> 16 & 255] << 16 ^ sbox[tmp >> 8 & 255] << 8 ^ sbox[tmp & 255];
|
||||
|
||||
// shift rows and add rcon
|
||||
if (i % keyLen === 0) {
|
||||
tmp = tmp << 8 ^ tmp >>> 24 ^ rcon << 24;
|
||||
rcon = rcon << 1 ^ (rcon >> 7) * 283;
|
||||
}
|
||||
}
|
||||
|
||||
encKey[i] = encKey[i - keyLen] ^ tmp;
|
||||
}
|
||||
|
||||
// schedule decryption keys
|
||||
for (let j = 0; i; j++, i--) {
|
||||
const tmp = encKey[j & 3 ? i : i - 4];
|
||||
if (i <= 4 || j < 4) {
|
||||
decKey[j] = tmp;
|
||||
} else {
|
||||
decKey[j] = decTable[0][sbox[tmp >>> 24]] ^
|
||||
decTable[1][sbox[tmp >> 16 & 255]] ^
|
||||
decTable[2][sbox[tmp >> 8 & 255]] ^
|
||||
decTable[3][sbox[tmp & 255]];
|
||||
}
|
||||
}
|
||||
}
|
||||
// public
|
||||
/* Something like this might appear here eventually
|
||||
name: "AES",
|
||||
blockSize: 4,
|
||||
keySizes: [4,6,8],
|
||||
*/
|
||||
|
||||
/**
|
||||
* Encrypt an array of 4 big-endian words.
|
||||
* @param {Array} data The plaintext.
|
||||
* @return {Array} The ciphertext.
|
||||
*/
|
||||
encrypt(data) {
|
||||
return this._crypt(data, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt an array of 4 big-endian words.
|
||||
* @param {Array} data The ciphertext.
|
||||
* @return {Array} The plaintext.
|
||||
*/
|
||||
decrypt(data) {
|
||||
return this._crypt(data, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand the S-box tables.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_precompute() {
|
||||
const encTable = this._tables[0];
|
||||
const decTable = this._tables[1];
|
||||
const sbox = encTable[4];
|
||||
const sboxInv = decTable[4];
|
||||
const d = [];
|
||||
const th = [];
|
||||
let xInv, x2, x4, x8;
|
||||
|
||||
// Compute double and third tables
|
||||
for (let i = 0; i < 256; i++) {
|
||||
th[(d[i] = i << 1 ^ (i >> 7) * 283) ^ i] = i;
|
||||
}
|
||||
|
||||
for (let x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) {
|
||||
// Compute sbox
|
||||
let s = xInv ^ xInv << 1 ^ xInv << 2 ^ xInv << 3 ^ xInv << 4;
|
||||
s = s >> 8 ^ s & 255 ^ 99;
|
||||
sbox[x] = s;
|
||||
sboxInv[s] = x;
|
||||
|
||||
// Compute MixColumns
|
||||
x8 = d[x4 = d[x2 = d[x]]];
|
||||
let tDec = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100;
|
||||
let tEnc = d[s] * 0x101 ^ s * 0x1010100;
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
encTable[i][x] = tEnc = tEnc << 24 ^ tEnc >>> 8;
|
||||
decTable[i][s] = tDec = tDec << 24 ^ tDec >>> 8;
|
||||
}
|
||||
}
|
||||
|
||||
// Compactify. Considerable speedup on Firefox.
|
||||
for (let i = 0; i < 5; i++) {
|
||||
encTable[i] = encTable[i].slice(0);
|
||||
decTable[i] = decTable[i].slice(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encryption and decryption core.
|
||||
* @param {Array} input Four words to be encrypted or decrypted.
|
||||
* @param dir The direction, 0 for encrypt and 1 for decrypt.
|
||||
* @return {Array} The four encrypted or decrypted words.
|
||||
* @private
|
||||
*/
|
||||
_crypt(input, dir) {
|
||||
if (input.length !== 4) {
|
||||
throw new Error("invalid aes block size");
|
||||
}
|
||||
|
||||
const key = this._key[dir];
|
||||
|
||||
const nInnerRounds = key.length / 4 - 2;
|
||||
const out = [0, 0, 0, 0];
|
||||
const table = this._tables[dir];
|
||||
|
||||
// load up the tables
|
||||
const t0 = table[0];
|
||||
const t1 = table[1];
|
||||
const t2 = table[2];
|
||||
const t3 = table[3];
|
||||
const sbox = table[4];
|
||||
|
||||
// state variables a,b,c,d are loaded with pre-whitened data
|
||||
let a = input[0] ^ key[0];
|
||||
let b = input[dir ? 3 : 1] ^ key[1];
|
||||
let c = input[2] ^ key[2];
|
||||
let d = input[dir ? 1 : 3] ^ key[3];
|
||||
let kIndex = 4;
|
||||
let a2, b2, c2;
|
||||
|
||||
// Inner rounds. Cribbed from OpenSSL.
|
||||
for (let i = 0; i < nInnerRounds; i++) {
|
||||
a2 = t0[a >>> 24] ^ t1[b >> 16 & 255] ^ t2[c >> 8 & 255] ^ t3[d & 255] ^ key[kIndex];
|
||||
b2 = t0[b >>> 24] ^ t1[c >> 16 & 255] ^ t2[d >> 8 & 255] ^ t3[a & 255] ^ key[kIndex + 1];
|
||||
c2 = t0[c >>> 24] ^ t1[d >> 16 & 255] ^ t2[a >> 8 & 255] ^ t3[b & 255] ^ key[kIndex + 2];
|
||||
d = t0[d >>> 24] ^ t1[a >> 16 & 255] ^ t2[b >> 8 & 255] ^ t3[c & 255] ^ key[kIndex + 3];
|
||||
kIndex += 4;
|
||||
a = a2; b = b2; c = c2;
|
||||
}
|
||||
|
||||
// Last round.
|
||||
for (let i = 0; i < 4; i++) {
|
||||
out[dir ? 3 & -i : i] =
|
||||
sbox[a >>> 24] << 24 ^
|
||||
sbox[b >> 16 & 255] << 16 ^
|
||||
sbox[c >> 8 & 255] << 8 ^
|
||||
sbox[d & 255] ^
|
||||
key[kIndex++];
|
||||
a2 = a; a = b; b = c; c = d; d = a2;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
/** @fileOverview CTR mode implementation.
|
||||
*
|
||||
* Special thanks to Roy Nicholson for pointing out a bug in our
|
||||
* implementation.
|
||||
*
|
||||
* @author Emily Stark
|
||||
* @author Mike Hamburg
|
||||
* @author Dan Boneh
|
||||
*/
|
||||
|
||||
/** Brian Gladman's CTR Mode.
|
||||
* @constructor
|
||||
* @param {Object} _prf The aes instance to generate key.
|
||||
* @param {bitArray} _iv The iv for ctr mode, it must be 128 bits.
|
||||
*/
|
||||
|
||||
const mode = {};
|
||||
|
||||
/**
|
||||
* Brian Gladman's CTR Mode.
|
||||
* @namespace
|
||||
*/
|
||||
mode.ctrGladman = class {
|
||||
constructor(prf, iv) {
|
||||
this._prf = prf;
|
||||
this._initIv = iv;
|
||||
this._iv = iv;
|
||||
}
|
||||
|
||||
reset() {
|
||||
this._iv = this._initIv;
|
||||
}
|
||||
|
||||
/** Input some data to calculate.
|
||||
* @param {bitArray} data the data to process, it must be intergral multiple of 128 bits unless it's the last.
|
||||
*/
|
||||
update(data) {
|
||||
return this.calculate(this._prf, data, this._iv);
|
||||
}
|
||||
|
||||
incWord(word) {
|
||||
if (((word >> 24) & 0xff) === 0xff) { //overflow
|
||||
let b1 = (word >> 16) & 0xff;
|
||||
let b2 = (word >> 8) & 0xff;
|
||||
let b3 = word & 0xff;
|
||||
|
||||
if (b1 === 0xff) { // overflow b1
|
||||
b1 = 0;
|
||||
if (b2 === 0xff) {
|
||||
b2 = 0;
|
||||
if (b3 === 0xff) {
|
||||
b3 = 0;
|
||||
} else {
|
||||
++b3;
|
||||
}
|
||||
} else {
|
||||
++b2;
|
||||
}
|
||||
} else {
|
||||
++b1;
|
||||
}
|
||||
|
||||
word = 0;
|
||||
word += (b1 << 16);
|
||||
word += (b2 << 8);
|
||||
word += b3;
|
||||
} else {
|
||||
word += (0x01 << 24);
|
||||
}
|
||||
return word;
|
||||
}
|
||||
|
||||
incCounter(counter) {
|
||||
if ((counter[0] = this.incWord(counter[0])) === 0) {
|
||||
// encr_data in fileenc.c from Dr Brian Gladman's counts only with DWORD j < 8
|
||||
counter[1] = this.incWord(counter[1]);
|
||||
}
|
||||
}
|
||||
|
||||
calculate(prf, data, iv) {
|
||||
let l;
|
||||
if (!(l = data.length)) {
|
||||
return [];
|
||||
}
|
||||
const bl = bitArray.bitLength(data);
|
||||
for (let i = 0; i < l; i += 4) {
|
||||
this.incCounter(iv);
|
||||
const e = prf.encrypt(iv);
|
||||
data[i] ^= e[0];
|
||||
data[i + 1] ^= e[1];
|
||||
data[i + 2] ^= e[2];
|
||||
data[i + 3] ^= e[3];
|
||||
}
|
||||
return bitArray.clamp(data, bl);
|
||||
}
|
||||
};
|
||||
|
||||
const misc = {
|
||||
importKey(password) {
|
||||
return new misc.hmacSha1(codec.bytes.toBits(password));
|
||||
},
|
||||
pbkdf2(prf, salt, count, length) {
|
||||
count = count || 10000;
|
||||
if (length < 0 || count < 0) {
|
||||
throw new Error("invalid params to pbkdf2");
|
||||
}
|
||||
const byteLength = ((length >> 5) + 1) << 2;
|
||||
let u, ui, i, j, k;
|
||||
const arrayBuffer = new ArrayBuffer(byteLength);
|
||||
const out = new DataView(arrayBuffer);
|
||||
let outLength = 0;
|
||||
const b = bitArray;
|
||||
salt = codec.bytes.toBits(salt);
|
||||
for (k = 1; outLength < (byteLength || 1); k++) {
|
||||
u = ui = prf.encrypt(b.concat(salt, [k]));
|
||||
for (i = 1; i < count; i++) {
|
||||
ui = prf.encrypt(ui);
|
||||
for (j = 0; j < ui.length; j++) {
|
||||
u[j] ^= ui[j];
|
||||
}
|
||||
}
|
||||
for (i = 0; outLength < (byteLength || 1) && i < u.length; i++) {
|
||||
out.setInt32(outLength, u[i]);
|
||||
outLength += 4;
|
||||
}
|
||||
}
|
||||
return arrayBuffer.slice(0, length / 8);
|
||||
}
|
||||
};
|
||||
|
||||
/** @fileOverview HMAC implementation.
|
||||
*
|
||||
* @author Emily Stark
|
||||
* @author Mike Hamburg
|
||||
* @author Dan Boneh
|
||||
*/
|
||||
|
||||
/** HMAC with the specified hash function.
|
||||
* @constructor
|
||||
* @param {bitArray} key the key for HMAC.
|
||||
* @param {Object} [Hash=hash.sha1] The hash function to use.
|
||||
*/
|
||||
misc.hmacSha1 = class {
|
||||
|
||||
constructor(key) {
|
||||
const hmac = this;
|
||||
const Hash = hmac._hash = hash.sha1;
|
||||
const exKey = [[], []];
|
||||
hmac._baseHash = [new Hash(), new Hash()];
|
||||
const bs = hmac._baseHash[0].blockSize / 32;
|
||||
|
||||
if (key.length > bs) {
|
||||
key = new Hash().update(key).finalize();
|
||||
}
|
||||
|
||||
for (let i = 0; i < bs; i++) {
|
||||
exKey[0][i] = key[i] ^ 0x36363636;
|
||||
exKey[1][i] = key[i] ^ 0x5C5C5C5C;
|
||||
}
|
||||
|
||||
hmac._baseHash[0].update(exKey[0]);
|
||||
hmac._baseHash[1].update(exKey[1]);
|
||||
hmac._resultHash = new Hash(hmac._baseHash[0]);
|
||||
}
|
||||
reset() {
|
||||
const hmac = this;
|
||||
hmac._resultHash = new hmac._hash(hmac._baseHash[0]);
|
||||
hmac._updated = false;
|
||||
}
|
||||
|
||||
update(data) {
|
||||
const hmac = this;
|
||||
hmac._updated = true;
|
||||
hmac._resultHash.update(data);
|
||||
}
|
||||
|
||||
digest() {
|
||||
const hmac = this;
|
||||
const w = hmac._resultHash.finalize();
|
||||
const result = new (hmac._hash)(hmac._baseHash[1]).update(w).finalize();
|
||||
|
||||
hmac.reset();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
encrypt(data) {
|
||||
if (!this._updated) {
|
||||
this.update(data);
|
||||
return this.digest(data);
|
||||
} else {
|
||||
throw new Error("encrypt on already updated hmac called!");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
cipher,
|
||||
codec,
|
||||
misc,
|
||||
mode
|
||||
};
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
Copyright (c) 2022 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global crypto */
|
||||
|
||||
import { FUNCTION_TYPE, UNDEFINED_TYPE } from "../constants.js";
|
||||
|
||||
const GET_RANDOM_VALUES_SUPPORTED = typeof crypto != UNDEFINED_TYPE && typeof crypto.getRandomValues == FUNCTION_TYPE;
|
||||
|
||||
const ERR_INVALID_PASSWORD = "Invalid password";
|
||||
const ERR_INVALID_SIGNATURE = "Invalid signature";
|
||||
const ERR_ABORT_CHECK_PASSWORD = "zipjs-abort-check-password";
|
||||
const ERR_UNSUPPORTED_CRYPTO_API = "Crypto API not supported";
|
||||
|
||||
export {
|
||||
getRandomValues,
|
||||
ERR_INVALID_PASSWORD,
|
||||
ERR_INVALID_SIGNATURE,
|
||||
ERR_ABORT_CHECK_PASSWORD
|
||||
};
|
||||
|
||||
function getRandomValues(array) {
|
||||
if (GET_RANDOM_VALUES_SUPPORTED) {
|
||||
return crypto.getRandomValues(array);
|
||||
} else {
|
||||
throw new Error(ERR_UNSUPPORTED_CRYPTO_API);
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright (c) 2022 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global TransformStream */
|
||||
|
||||
import { Crc32 } from "./codecs/crc32.js";
|
||||
|
||||
class Crc32Stream extends TransformStream {
|
||||
|
||||
constructor() {
|
||||
// deno-lint-ignore prefer-const
|
||||
let stream;
|
||||
const crc32 = new Crc32();
|
||||
super({
|
||||
transform(chunk, controller) {
|
||||
crc32.append(chunk);
|
||||
controller.enqueue(chunk);
|
||||
},
|
||||
flush() {
|
||||
const value = new Uint8Array(4);
|
||||
const dataView = new DataView(value.buffer);
|
||||
dataView.setUint32(0, crc32.get());
|
||||
stream.value = value;
|
||||
}
|
||||
});
|
||||
stream = this;
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
Crc32Stream
|
||||
};
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
Copyright (c) 2022 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global TransformStream */
|
||||
// deno-lint-ignore-file no-this-alias
|
||||
|
||||
import { Crc32 } from "./codecs/crc32.js";
|
||||
import {
|
||||
ERR_INVALID_PASSWORD,
|
||||
ERR_ABORT_CHECK_PASSWORD,
|
||||
getRandomValues
|
||||
} from "./common-crypto.js";
|
||||
|
||||
const HEADER_LENGTH = 12;
|
||||
|
||||
class ZipCryptoDecryptionStream extends TransformStream {
|
||||
|
||||
constructor({ password, rawPassword, passwordVerification, checkPasswordOnly }) {
|
||||
super({
|
||||
start() {
|
||||
initZipCrypto(this, password, rawPassword, passwordVerification);
|
||||
},
|
||||
transform(chunk, controller) {
|
||||
const zipCrypto = this;
|
||||
if (zipCrypto.password || zipCrypto.rawPassword) {
|
||||
const decryptedHeader = decrypt(zipCrypto, chunk.subarray(0, HEADER_LENGTH));
|
||||
zipCrypto.password = zipCrypto.rawPassword = null;
|
||||
if ((decryptedHeader.at(-1) ^ zipCrypto.passwordVerification) != 0) {
|
||||
throw new Error(ERR_INVALID_PASSWORD);
|
||||
}
|
||||
chunk = chunk.subarray(HEADER_LENGTH);
|
||||
}
|
||||
if (checkPasswordOnly) {
|
||||
controller.error(new Error(ERR_ABORT_CHECK_PASSWORD));
|
||||
} else {
|
||||
controller.enqueue(decrypt(zipCrypto, chunk));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ZipCryptoEncryptionStream extends TransformStream {
|
||||
|
||||
constructor({ password, rawPassword, passwordVerification }) {
|
||||
super({
|
||||
start() {
|
||||
initZipCrypto(this, password, rawPassword, passwordVerification);
|
||||
},
|
||||
transform(chunk, controller) {
|
||||
const zipCrypto = this;
|
||||
let output;
|
||||
let offset;
|
||||
if (zipCrypto.password || zipCrypto.rawPassword) {
|
||||
zipCrypto.password = zipCrypto.rawPassword = null;
|
||||
const header = getRandomValues(new Uint8Array(HEADER_LENGTH));
|
||||
header[HEADER_LENGTH - 1] = zipCrypto.passwordVerification;
|
||||
output = new Uint8Array(chunk.length + header.length);
|
||||
output.set(encrypt(zipCrypto, header), 0);
|
||||
offset = HEADER_LENGTH;
|
||||
} else {
|
||||
output = new Uint8Array(chunk.length);
|
||||
offset = 0;
|
||||
}
|
||||
output.set(encrypt(zipCrypto, chunk), offset);
|
||||
controller.enqueue(output);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
ZipCryptoDecryptionStream,
|
||||
ZipCryptoEncryptionStream
|
||||
};
|
||||
|
||||
function initZipCrypto(zipCrypto, password, rawPassword, passwordVerification) {
|
||||
Object.assign(zipCrypto, {
|
||||
password,
|
||||
rawPassword,
|
||||
passwordVerification
|
||||
});
|
||||
createKeys(zipCrypto, password, rawPassword);
|
||||
}
|
||||
|
||||
function decrypt(target, input) {
|
||||
const output = new Uint8Array(input.length);
|
||||
for (let index = 0; index < input.length; index++) {
|
||||
output[index] = getByte(target) ^ input[index];
|
||||
updateKeys(target, output[index]);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
function encrypt(target, input) {
|
||||
const output = new Uint8Array(input.length);
|
||||
for (let index = 0; index < input.length; index++) {
|
||||
output[index] = getByte(target) ^ input[index];
|
||||
updateKeys(target, input[index]);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
function createKeys(target, password, rawPassword) {
|
||||
const keys = [0x12345678, 0x23456789, 0x34567890];
|
||||
Object.assign(target, {
|
||||
keys,
|
||||
crcKey0: new Crc32(keys[0]),
|
||||
crcKey2: new Crc32(keys[2])
|
||||
});
|
||||
if (rawPassword) {
|
||||
for (let index = 0; index < rawPassword.length; index++) {
|
||||
updateKeys(target, rawPassword[index]);
|
||||
}
|
||||
} else {
|
||||
for (let index = 0; index < password.length; index++) {
|
||||
updateKeys(target, password.charCodeAt(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateKeys(target, byte) {
|
||||
let [, key1] = target.keys;
|
||||
target.crcKey0.append([byte]);
|
||||
const key0 = ~target.crcKey0.get();
|
||||
key1 = getInt32(Math.imul(getInt32(key1 + getInt8(key0)), 134775813) + 1);
|
||||
target.crcKey2.append([key1 >>> 24]);
|
||||
const key2 = ~target.crcKey2.get();
|
||||
target.keys = [key0, key1, key2];
|
||||
}
|
||||
|
||||
function getByte(target) {
|
||||
const temp = target.keys[2] | 2;
|
||||
return getInt8(Math.imul(temp, (temp ^ 1)) >>> 8);
|
||||
}
|
||||
|
||||
function getInt8(number) {
|
||||
return number & 0xFF;
|
||||
}
|
||||
|
||||
function getInt32(number) {
|
||||
return number & 0xFFFFFFFF;
|
||||
}
|
||||
+295
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global TransformStream, ReadableStream */
|
||||
// deno-lint-ignore-file no-this-alias
|
||||
|
||||
import { Crc32Stream } from "./crc32-stream.js";
|
||||
import {
|
||||
AESEncryptionStream,
|
||||
AESDecryptionStream
|
||||
} from "./aes-crypto-stream.js";
|
||||
import {
|
||||
ZipCryptoEncryptionStream,
|
||||
ZipCryptoDecryptionStream
|
||||
} from "./zip-crypto-stream.js";
|
||||
import {
|
||||
ERR_INVALID_PASSWORD,
|
||||
ERR_INVALID_SIGNATURE,
|
||||
ERR_ABORT_CHECK_PASSWORD
|
||||
} from "./common-crypto.js";
|
||||
|
||||
const ERR_INVALID_UNCOMPRESSED_SIZE = "Invalid uncompressed size";
|
||||
const ERR_INVALID_COMPRESSED_DATA = "Invalid compressed data";
|
||||
const FORMAT_DEFLATE_RAW = "deflate-raw";
|
||||
const FORMAT_DEFLATE64_RAW = "deflate64-raw";
|
||||
const FORMAT_GZIP = "gzip";
|
||||
const GZIP_HEADER_LENGTH = 10;
|
||||
const GZIP_TRAILER_LENGTH = 8;
|
||||
|
||||
class DeflateStream extends TransformStream {
|
||||
|
||||
constructor(options, { chunkSize, CompressionStreamZlib, CompressionStream }) {
|
||||
super({});
|
||||
const { compressed, encrypted, useCompressionStream, zipCrypto, signed, level, deflate64 } = options;
|
||||
const stream = this;
|
||||
let crc32Stream, encryptionStream, gzipCrc32Stream;
|
||||
let readable = super.readable;
|
||||
const useGzipCrc32 = signed && compressed && !deflate64 && (!encrypted || zipCrypto) &&
|
||||
Boolean(useCompressionStream && CompressionStream);
|
||||
if ((!encrypted || zipCrypto) && signed && !useGzipCrc32) {
|
||||
crc32Stream = new Crc32Stream();
|
||||
readable = pipeThrough(readable, crc32Stream);
|
||||
}
|
||||
if (compressed) {
|
||||
if (useGzipCrc32) {
|
||||
gzipCrc32Stream = new GzipToRawDeflateStream();
|
||||
readable = pipeThroughBackpressured(readable, new CompressionStream(FORMAT_GZIP));
|
||||
readable = pipeThrough(readable, gzipCrc32Stream);
|
||||
} else {
|
||||
readable = pipeThroughCommpressionStream(readable, useCompressionStream, { level, chunkSize }, CompressionStream, CompressionStreamZlib, CompressionStream);
|
||||
}
|
||||
}
|
||||
if (encrypted) {
|
||||
if (zipCrypto) {
|
||||
readable = pipeThrough(readable, new ZipCryptoEncryptionStream(options));
|
||||
} else {
|
||||
encryptionStream = new AESEncryptionStream(options);
|
||||
readable = pipeThrough(readable, encryptionStream);
|
||||
}
|
||||
}
|
||||
setReadable(stream, readable, () => {
|
||||
let signature;
|
||||
if (encrypted && !zipCrypto) {
|
||||
signature = encryptionStream.signature;
|
||||
}
|
||||
if ((!encrypted || zipCrypto) && signed) {
|
||||
signature = useGzipCrc32 ? gzipCrc32Stream.signature : new DataView(crc32Stream.value.buffer).getUint32(0);
|
||||
}
|
||||
stream.signature = signature;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class GzipToRawDeflateStream extends TransformStream {
|
||||
|
||||
constructor() {
|
||||
// deno-lint-ignore prefer-const
|
||||
let stream;
|
||||
let headerLeft = GZIP_HEADER_LENGTH;
|
||||
let tail = new Uint8Array(0);
|
||||
super({
|
||||
transform(chunk, controller) {
|
||||
if (headerLeft) {
|
||||
const dropped = Math.min(headerLeft, chunk.length);
|
||||
headerLeft -= dropped;
|
||||
chunk = chunk.subarray(dropped);
|
||||
if (!chunk.length) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const available = tail.length + chunk.length;
|
||||
if (available <= GZIP_TRAILER_LENGTH) {
|
||||
const pending = new Uint8Array(available);
|
||||
pending.set(tail);
|
||||
pending.set(chunk, tail.length);
|
||||
tail = pending;
|
||||
return;
|
||||
}
|
||||
const emitLength = available - GZIP_TRAILER_LENGTH;
|
||||
const output = new Uint8Array(emitLength);
|
||||
const fromTail = Math.min(emitLength, tail.length);
|
||||
output.set(tail.subarray(0, fromTail), 0);
|
||||
if (emitLength > fromTail) {
|
||||
output.set(chunk.subarray(0, emitLength - fromTail), fromTail);
|
||||
}
|
||||
controller.enqueue(output);
|
||||
const nextTail = new Uint8Array(GZIP_TRAILER_LENGTH);
|
||||
const tailRemaining = tail.length - fromTail;
|
||||
if (tailRemaining) {
|
||||
nextTail.set(tail.subarray(fromTail), 0);
|
||||
}
|
||||
nextTail.set(chunk.subarray(emitLength - fromTail), tailRemaining);
|
||||
tail = nextTail;
|
||||
},
|
||||
flush() {
|
||||
const dataView = new DataView(tail.buffer, tail.byteOffset, tail.byteLength);
|
||||
stream.signature = dataView.getUint32(0, true);
|
||||
stream.uncompressedSize = dataView.getUint32(4, true);
|
||||
}
|
||||
});
|
||||
stream = this;
|
||||
}
|
||||
}
|
||||
|
||||
class InflateStream extends TransformStream {
|
||||
|
||||
constructor(options, { chunkSize, DecompressionStreamZlib, DecompressionStream }) {
|
||||
super({});
|
||||
const { zipCrypto, encrypted, signed, signature, compressed, useCompressionStream, deflate64 } = options;
|
||||
let crc32Stream, decryptionStream;
|
||||
let readable = super.readable;
|
||||
if (encrypted) {
|
||||
if (zipCrypto) {
|
||||
readable = pipeThrough(readable, new ZipCryptoDecryptionStream(options));
|
||||
} else {
|
||||
decryptionStream = new AESDecryptionStream(options);
|
||||
readable = pipeThrough(readable, decryptionStream);
|
||||
}
|
||||
}
|
||||
if (compressed) {
|
||||
readable = pipeThroughCommpressionStream(readable, useCompressionStream, { chunkSize, deflate64 }, DecompressionStream, DecompressionStreamZlib, DecompressionStream);
|
||||
readable = mapInflateStreamError(readable);
|
||||
}
|
||||
if ((!encrypted || zipCrypto) && signed) {
|
||||
crc32Stream = new Crc32Stream();
|
||||
readable = pipeThrough(readable, crc32Stream);
|
||||
}
|
||||
setReadable(this, readable, () => {
|
||||
if ((!encrypted || zipCrypto) && signed) {
|
||||
const dataViewSignature = new DataView(crc32Stream.value.buffer);
|
||||
if (signature != dataViewSignature.getUint32(0, false)) {
|
||||
throw new Error(ERR_INVALID_SIGNATURE);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
DeflateStream,
|
||||
InflateStream,
|
||||
ERR_INVALID_PASSWORD,
|
||||
ERR_INVALID_SIGNATURE,
|
||||
ERR_INVALID_UNCOMPRESSED_SIZE,
|
||||
ERR_INVALID_COMPRESSED_DATA,
|
||||
ERR_ABORT_CHECK_PASSWORD
|
||||
};
|
||||
|
||||
function setReadable(stream, readable, flush) {
|
||||
readable = pipeThrough(readable, new TransformStream({ flush }));
|
||||
Object.defineProperty(stream, "readable", {
|
||||
get() {
|
||||
return readable;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function pipeThroughCommpressionStream(readable, useCompressionStream, options, CompressionStreamNative, CompressionStreamZlib, CompressionStream) {
|
||||
const Stream = useCompressionStream && CompressionStreamNative ? CompressionStreamNative : CompressionStreamZlib || CompressionStream;
|
||||
const format = options.deflate64 ? FORMAT_DEFLATE64_RAW : FORMAT_DEFLATE_RAW;
|
||||
let codecStream;
|
||||
try {
|
||||
codecStream = new Stream(format, options);
|
||||
} catch (error) {
|
||||
if (useCompressionStream) {
|
||||
if (CompressionStreamZlib) {
|
||||
codecStream = new CompressionStreamZlib(format, options);
|
||||
} else if (CompressionStream) {
|
||||
codecStream = new CompressionStream(format, options);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return pipeThroughBackpressured(readable, codecStream);
|
||||
}
|
||||
|
||||
function pipeThrough(readable, transformStream) {
|
||||
return readable.pipeThrough(transformStream);
|
||||
}
|
||||
|
||||
function pipeThroughBackpressured(readable, transformStream) {
|
||||
const writer = transformStream.writable.getWriter();
|
||||
const reader = readable.getReader();
|
||||
pump();
|
||||
return transformStream.readable;
|
||||
|
||||
async function pump() {
|
||||
try {
|
||||
for (; ;) {
|
||||
await writer.ready;
|
||||
const result = await reader.read();
|
||||
if (result.done) {
|
||||
await writer.close();
|
||||
break;
|
||||
}
|
||||
await writer.write(result.value);
|
||||
}
|
||||
} catch (error) {
|
||||
await abort(writer, error);
|
||||
await cancel(reader, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function abort(writer, error) {
|
||||
try {
|
||||
await writer.abort(error);
|
||||
} catch {
|
||||
// ignored: the writable may already be errored/closed
|
||||
}
|
||||
}
|
||||
|
||||
async function cancel(reader, error) {
|
||||
try {
|
||||
await reader.cancel(error);
|
||||
} catch {
|
||||
// ignored: the readable may already be errored/closed
|
||||
}
|
||||
}
|
||||
|
||||
function mapInflateStreamError(readable) {
|
||||
const reader = readable.getReader();
|
||||
return new ReadableStream({
|
||||
async pull(controller) {
|
||||
let result;
|
||||
try {
|
||||
result = await reader.read();
|
||||
} catch (error) {
|
||||
if (error && error.message) {
|
||||
throw error;
|
||||
}
|
||||
const mappedError = new Error(ERR_INVALID_COMPRESSED_DATA);
|
||||
mappedError.cause = error;
|
||||
throw mappedError;
|
||||
}
|
||||
const { value, done } = result;
|
||||
if (done) {
|
||||
controller.close();
|
||||
} else {
|
||||
controller.enqueue(value);
|
||||
}
|
||||
},
|
||||
cancel(reason) {
|
||||
return reader.cancel(reason);
|
||||
}
|
||||
});
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
See https://github.com/gildas-lormeau/zlib-streams-ts/tree/main/dist
|
||||
+1
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
||||
See https://github.com/gildas-lormeau/zlib-streams/tree/main/dist
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global WebAssembly, URL, fetch, atob */
|
||||
|
||||
import { setWasmExports } from "./zlib-streams.js";
|
||||
|
||||
let initializedModule = false;
|
||||
|
||||
async function initModule(wasmURI, { baseURI }) {
|
||||
if (!initializedModule) {
|
||||
let arrayBuffer, uri;
|
||||
try {
|
||||
try {
|
||||
uri = new URL(wasmURI, baseURI);
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
const response = await fetch(uri);
|
||||
arrayBuffer = await response.arrayBuffer();
|
||||
} catch (error) {
|
||||
if (wasmURI.startsWith("data:application/wasm;base64,")) {
|
||||
arrayBuffer = arrayBufferFromDataURI(wasmURI);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
const wasmInstance = await WebAssembly.instantiate(arrayBuffer);
|
||||
setWasmExports(wasmInstance.instance.exports);
|
||||
initializedModule = true;
|
||||
}
|
||||
}
|
||||
|
||||
function resetWasmModule() {
|
||||
initializedModule = false;
|
||||
}
|
||||
|
||||
function arrayBufferFromDataURI(dataURI) {
|
||||
const base64 = dataURI.split(",")[1];
|
||||
const binary = atob(base64);
|
||||
const len = binary.length;
|
||||
const bytes = new Uint8Array(len);
|
||||
for (let i = 0; i < len; ++i) {
|
||||
bytes[i] = binary.charCodeAt(i);
|
||||
}
|
||||
return bytes.buffer;
|
||||
}
|
||||
|
||||
export {
|
||||
CompressionStreamZlib,
|
||||
DecompressionStreamZlib
|
||||
} from "./zlib-streams.js";
|
||||
export {
|
||||
initModule,
|
||||
resetWasmModule
|
||||
};
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global TransformStream */
|
||||
|
||||
let wasm, malloc, free, memory;
|
||||
|
||||
export function setWasmExports(wasmAPI) {
|
||||
wasm = wasmAPI;
|
||||
({ malloc, free, memory } = wasm);
|
||||
if (typeof malloc !== "function" || typeof free !== "function" || !memory) {
|
||||
wasm = malloc = free = memory = null;
|
||||
throw new Error("Invalid WASM module");
|
||||
}
|
||||
}
|
||||
|
||||
function _make(isCompress, type, options = {}) {
|
||||
const level = (typeof options.level === "number") ? options.level : -1;
|
||||
const outBufferSize = (typeof options.outBuffer === "number") ? options.outBuffer : 64 * 1024;
|
||||
const inBufferSize = (typeof options.inBufferSize === "number") ? options.inBufferSize : 64 * 1024;
|
||||
|
||||
return new TransformStream({
|
||||
start() {
|
||||
try {
|
||||
let result;
|
||||
this.out = malloc(outBufferSize);
|
||||
this.in = malloc(inBufferSize);
|
||||
this.inBufferSize = inBufferSize;
|
||||
if (!this.out || !this.in) {
|
||||
throw new Error("allocation failed");
|
||||
}
|
||||
this._scratch = new Uint8Array(outBufferSize);
|
||||
if (isCompress) {
|
||||
this._process = wasm.deflate_process;
|
||||
this._last_consumed = wasm.deflate_last_consumed;
|
||||
this._end = wasm.deflate_end;
|
||||
this.streamHandle = wasm.deflate_new();
|
||||
if (type === "gzip") {
|
||||
result = wasm.deflate_init_gzip(this.streamHandle, level);
|
||||
} else if (type === "deflate-raw") {
|
||||
result = wasm.deflate_init_raw(this.streamHandle, level);
|
||||
} else {
|
||||
result = wasm.deflate_init(this.streamHandle, level);
|
||||
}
|
||||
} else {
|
||||
if (type === "deflate64-raw") {
|
||||
this._process = wasm.inflate9_process;
|
||||
this._last_consumed = wasm.inflate9_last_consumed;
|
||||
this._end = wasm.inflate9_end;
|
||||
this.streamHandle = wasm.inflate9_new();
|
||||
result = wasm.inflate9_init_raw(this.streamHandle);
|
||||
} else {
|
||||
this._process = wasm.inflate_process;
|
||||
this._last_consumed = wasm.inflate_last_consumed;
|
||||
this._end = wasm.inflate_end;
|
||||
this.streamHandle = wasm.inflate_new();
|
||||
if (type === "deflate-raw") {
|
||||
result = wasm.inflate_init_raw(this.streamHandle);
|
||||
} else if (type === "gzip") {
|
||||
result = wasm.inflate_init_gzip(this.streamHandle);
|
||||
} else {
|
||||
result = wasm.inflate_init(this.streamHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result !== 0) {
|
||||
throw new Error("init failed:" + result);
|
||||
}
|
||||
} catch (error) {
|
||||
disposeStream(this);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
transform(chunk, controller) {
|
||||
try {
|
||||
const buffer = chunk;
|
||||
const heap = new Uint8Array(memory.buffer);
|
||||
const process = this._process;
|
||||
const last_consumed = this._last_consumed;
|
||||
const out = this.out;
|
||||
const scratch = this._scratch;
|
||||
let offset = 0;
|
||||
while (offset < buffer.length) {
|
||||
const toRead = Math.min(buffer.length - offset, 32 * 1024);
|
||||
if (!this.in || this.inBufferSize < toRead) {
|
||||
if (this.in && free) {
|
||||
free(this.in);
|
||||
this.in = 0;
|
||||
}
|
||||
this.in = malloc(toRead);
|
||||
this.inBufferSize = toRead;
|
||||
if (!this.in) {
|
||||
throw new Error("allocation failed");
|
||||
}
|
||||
}
|
||||
heap.set(buffer.subarray(offset, offset + toRead), this.in);
|
||||
const result = process(this.streamHandle, this.in, toRead, out, outBufferSize, 0);
|
||||
const prod = result & 0x00ffffff;
|
||||
if (prod) {
|
||||
scratch.set(heap.subarray(out, out + prod), 0);
|
||||
controller.enqueue(scratch.slice(0, prod));
|
||||
}
|
||||
if (!isCompress) {
|
||||
const code = (result >> 24) & 0xff;
|
||||
const signedCode = (code & 0x80) ? code - 256 : code;
|
||||
if (signedCode < 0) {
|
||||
throw new Error("process error:" + signedCode);
|
||||
}
|
||||
}
|
||||
const consumed = last_consumed(this.streamHandle);
|
||||
if (consumed === 0) {
|
||||
break;
|
||||
}
|
||||
offset += consumed;
|
||||
}
|
||||
} catch (error) {
|
||||
disposeStream(this);
|
||||
controller.error(error);
|
||||
}
|
||||
},
|
||||
flush(controller) {
|
||||
try {
|
||||
const heap = new Uint8Array(memory.buffer);
|
||||
const process = this._process;
|
||||
const out = this.out;
|
||||
const scratch = this._scratch;
|
||||
while (true) {
|
||||
const result = process(this.streamHandle, 0, 0, out, outBufferSize, 4);
|
||||
const produced = result & 0x00ffffff;
|
||||
const code = (result >> 24) & 0xff;
|
||||
if (!isCompress) {
|
||||
const signedCode = (code & 0x80) ? code - 256 : code;
|
||||
if (signedCode < 0) {
|
||||
throw new Error("process error:" + signedCode);
|
||||
}
|
||||
}
|
||||
if (produced) {
|
||||
scratch.set(heap.subarray(out, out + produced), 0);
|
||||
controller.enqueue(scratch.slice(0, produced));
|
||||
}
|
||||
if (code === 1 || produced === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
controller.error(error);
|
||||
} finally {
|
||||
const result = disposeStream(this);
|
||||
if (result !== 0) {
|
||||
controller.error(new Error("end error:" + result));
|
||||
}
|
||||
}
|
||||
},
|
||||
cancel() {
|
||||
// release the stream handle and buffers when the pipeline is aborted,
|
||||
// they would be leaked in the process-lifetime wasm heap otherwise
|
||||
disposeStream(this);
|
||||
}
|
||||
});
|
||||
|
||||
function disposeStream(state) {
|
||||
let endResult = 0;
|
||||
if (state.streamHandle && state._end) {
|
||||
endResult = state._end(state.streamHandle);
|
||||
}
|
||||
state.streamHandle = 0;
|
||||
if (state.in && free) {
|
||||
free(state.in);
|
||||
}
|
||||
state.in = 0;
|
||||
if (state.out && free) {
|
||||
free(state.out);
|
||||
}
|
||||
state.out = 0;
|
||||
return endResult;
|
||||
}
|
||||
}
|
||||
|
||||
export class CompressionStreamZlib {
|
||||
constructor(type = "deflate", options) {
|
||||
return _make(true, type, options);
|
||||
}
|
||||
}
|
||||
export class DecompressionStreamZlib {
|
||||
constructor(type = "deflate", options) {
|
||||
return _make(false, type, options);
|
||||
}
|
||||
}
|
||||
// These codecs are backed by the WASM module; they are unusable until setWasmExports() has run.
|
||||
// The worker uses this flag to know it must fall back to the native CompressionStream when the
|
||||
// module fails to load, rather than discarding a self-contained codec supplied through config.
|
||||
CompressionStreamZlib.requiresModule = true;
|
||||
DecompressionStreamZlib.requiresModule = true;
|
||||
Generated
Vendored
Executable
BIN
Binary file not shown.
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
const BASE64_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
export function base64Decode(b64) {
|
||||
b64 = String(b64).replace(/[^A-Za-z0-9+/=]/g, "");
|
||||
const len = b64.length;
|
||||
const out = [];
|
||||
for (let i = 0; i < len; i += 4) {
|
||||
const a = BASE64_TABLE.indexOf(b64[i]);
|
||||
const b = BASE64_TABLE.indexOf(b64[i + 1]);
|
||||
const c = BASE64_TABLE.indexOf(b64[i + 2]);
|
||||
const d = BASE64_TABLE.indexOf(b64[i + 3]);
|
||||
const n = (a << 18) | (b << 12) | ((c & 63) << 6) | (d & 63);
|
||||
out.push((n >> 16) & 0xff);
|
||||
if (b64[i + 2] !== "=") {
|
||||
out.push((n >> 8) & 0xff);
|
||||
}
|
||||
if (b64[i + 3] !== "=") {
|
||||
out.push(n & 0xff);
|
||||
}
|
||||
}
|
||||
return new Uint8Array(out);
|
||||
}
|
||||
|
||||
export function base64Encode(bytes) {
|
||||
let out = "";
|
||||
const len = bytes.length;
|
||||
let i = 0;
|
||||
for (; i + 2 < len; i += 3) {
|
||||
const n = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
|
||||
out += BASE64_TABLE[(n >> 18) & 63] + BASE64_TABLE[(n >> 12) & 63] + BASE64_TABLE[(n >> 6) & 63] + BASE64_TABLE[n & 63];
|
||||
}
|
||||
const rem = len - i;
|
||||
if (rem === 1) {
|
||||
const n = bytes[i] << 16;
|
||||
out += BASE64_TABLE[(n >> 18) & 63] + BASE64_TABLE[(n >> 12) & 63] + "==";
|
||||
} else if (rem === 2) {
|
||||
const n = (bytes[i] << 16) | (bytes[i + 1] << 8);
|
||||
out += BASE64_TABLE[(n >> 18) & 63] + BASE64_TABLE[(n >> 12) & 63] + BASE64_TABLE[(n >> 6) & 63] + "=";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global Response, ReadableStream, WritableStream, TransformStream */
|
||||
|
||||
export {
|
||||
createBlobTempStream
|
||||
};
|
||||
|
||||
const DEFAULT_THRESHOLD = 1024 * 1024;
|
||||
|
||||
function createBlobTempStream(options = {}) {
|
||||
const {
|
||||
thresholdBytes = DEFAULT_THRESHOLD
|
||||
} = options;
|
||||
return function () {
|
||||
const memoryChunks = [];
|
||||
let bufferedSize = 0;
|
||||
let spilled = false;
|
||||
let blobWriter, blobPromise, blobReader;
|
||||
|
||||
async function spillToBlob() {
|
||||
const transformStream = new TransformStream();
|
||||
blobPromise = new Response(transformStream.readable).blob();
|
||||
blobWriter = transformStream.writable.getWriter();
|
||||
spilled = true;
|
||||
for (const chunk of memoryChunks) {
|
||||
await blobWriter.write(chunk);
|
||||
}
|
||||
memoryChunks.length = 0;
|
||||
}
|
||||
|
||||
const writable = new WritableStream({
|
||||
async write(chunk) {
|
||||
if (spilled) {
|
||||
await blobWriter.write(chunk);
|
||||
} else {
|
||||
memoryChunks.push(chunk);
|
||||
bufferedSize += chunk.length;
|
||||
if (bufferedSize > thresholdBytes) {
|
||||
await spillToBlob();
|
||||
}
|
||||
}
|
||||
},
|
||||
async close() {
|
||||
if (blobWriter) {
|
||||
await blobWriter.close();
|
||||
blobWriter = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let memoryIndex = 0;
|
||||
const readable = new ReadableStream({
|
||||
async pull(controller) {
|
||||
if (spilled) {
|
||||
if (!blobReader) {
|
||||
const blob = await blobPromise;
|
||||
blobReader = blob.stream().getReader();
|
||||
}
|
||||
const { value, done } = await blobReader.read();
|
||||
if (done) {
|
||||
controller.close();
|
||||
} else {
|
||||
controller.enqueue(value);
|
||||
}
|
||||
} else if (memoryIndex < memoryChunks.length) {
|
||||
controller.enqueue(memoryChunks[memoryIndex++]);
|
||||
} else {
|
||||
controller.close();
|
||||
}
|
||||
},
|
||||
async cancel(reason) {
|
||||
if (blobReader) {
|
||||
await blobReader.cancel(reason);
|
||||
}
|
||||
}
|
||||
}, { highWaterMark: 0 });
|
||||
async function dispose() {
|
||||
if (blobWriter) {
|
||||
try {
|
||||
await blobWriter.abort();
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
blobWriter = null;
|
||||
}
|
||||
if (blobPromise) {
|
||||
blobPromise.catch(() => {
|
||||
// ignored
|
||||
});
|
||||
blobPromise = null;
|
||||
}
|
||||
memoryChunks.length = 0;
|
||||
}
|
||||
|
||||
return { writable, readable, dispose };
|
||||
};
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright (c) 2022 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global TextDecoder */
|
||||
|
||||
const CP437 = "\0☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼ !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~⌂ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ ".split("");
|
||||
const VALID_CP437 = CP437.length == 256;
|
||||
|
||||
export {
|
||||
decodeCP437
|
||||
};
|
||||
|
||||
function decodeCP437(stringValue) {
|
||||
if (VALID_CP437) {
|
||||
let result = "";
|
||||
for (let indexCharacter = 0; indexCharacter < stringValue.length; indexCharacter++) {
|
||||
result += CP437[stringValue[indexCharacter]];
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
return new TextDecoder().decode(stringValue);
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Copyright (c) 2022 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global TextDecoder */
|
||||
|
||||
import { decodeCP437 } from "./decode-cp437.js";
|
||||
|
||||
export {
|
||||
decodeText
|
||||
};
|
||||
|
||||
function decodeText(value, encoding) {
|
||||
if (encoding && encoding.trim().toLowerCase() == "cp437") {
|
||||
return decodeCP437(value);
|
||||
} else {
|
||||
return new TextDecoder(encoding, { ignoreBOM: true }).decode(value);
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright (c) 2022 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
export {
|
||||
getMimeType
|
||||
};
|
||||
|
||||
function getMimeType() {
|
||||
return "application/octet-stream";
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright (c) 2022 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global TextEncoder */
|
||||
|
||||
import { UNDEFINED_TYPE } from "../constants.js";
|
||||
|
||||
export {
|
||||
encodeText
|
||||
};
|
||||
|
||||
function encodeText(value) {
|
||||
// deno-lint-ignore valid-typeof
|
||||
if (typeof TextEncoder == UNDEFINED_TYPE) {
|
||||
value = unescape(encodeURIComponent(value));
|
||||
const result = new Uint8Array(value.length);
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
result[i] = value.charCodeAt(i);
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
return new TextEncoder().encode(value);
|
||||
}
|
||||
}
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
const MAX_CODE_LENGTH = 15;
|
||||
const END_OF_BLOCK = 256;
|
||||
const LENGTH_BASES = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258];
|
||||
const LENGTH_EXTRA_BITS = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0];
|
||||
const DISTANCE_BASES = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577];
|
||||
const DISTANCE_EXTRA_BITS = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13];
|
||||
const CODE_LENGTH_CODE_ORDER = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
|
||||
const FIXED_LITERAL_LENGTHS = new Uint8Array(288);
|
||||
FIXED_LITERAL_LENGTHS.fill(8, 0, 144);
|
||||
FIXED_LITERAL_LENGTHS.fill(9, 144, 256);
|
||||
FIXED_LITERAL_LENGTHS.fill(7, 256, 280);
|
||||
FIXED_LITERAL_LENGTHS.fill(8, 280, 288);
|
||||
const FIXED_DISTANCE_LENGTHS = new Uint8Array(30).fill(5);
|
||||
|
||||
export function inflateRaw(input) {
|
||||
let inputIndex = 0;
|
||||
let bitBuffer = 0;
|
||||
let bitCount = 0;
|
||||
let output = new Uint8Array(1024);
|
||||
let outputLength = 0;
|
||||
let lastBlock = 0;
|
||||
while (!lastBlock) {
|
||||
lastBlock = readBits(1);
|
||||
const blockType = readBits(2);
|
||||
if (blockType == 0) {
|
||||
copyStoredBlock();
|
||||
} else if (blockType == 1) {
|
||||
inflateBlock(buildHuffmanTable(FIXED_LITERAL_LENGTHS), buildHuffmanTable(FIXED_DISTANCE_LENGTHS));
|
||||
} else if (blockType == 2) {
|
||||
inflateBlock(...readDynamicTables());
|
||||
} else {
|
||||
throw new Error("invalid deflate block type");
|
||||
}
|
||||
}
|
||||
return output.subarray(0, outputLength);
|
||||
|
||||
function readByte() {
|
||||
if (inputIndex >= input.length) {
|
||||
throw new Error("unexpected end of deflate data");
|
||||
}
|
||||
return input[inputIndex++];
|
||||
}
|
||||
|
||||
function readBits(count) {
|
||||
while (bitCount < count) {
|
||||
bitBuffer |= readByte() << bitCount;
|
||||
bitCount += 8;
|
||||
}
|
||||
const value = bitBuffer & ((1 << count) - 1);
|
||||
bitBuffer >>>= count;
|
||||
bitCount -= count;
|
||||
return value;
|
||||
}
|
||||
|
||||
function copyStoredBlock() {
|
||||
bitBuffer = 0;
|
||||
bitCount = 0;
|
||||
const length = readByte() | (readByte() << 8);
|
||||
inputIndex += 2;
|
||||
ensureOutput(outputLength + length);
|
||||
for (let indexByte = 0; indexByte < length; indexByte++) {
|
||||
output[outputLength++] = readByte();
|
||||
}
|
||||
}
|
||||
|
||||
function inflateBlock(literalTable, distanceTable) {
|
||||
let symbol = decodeSymbol(literalTable);
|
||||
while (symbol != END_OF_BLOCK) {
|
||||
if (symbol < END_OF_BLOCK) {
|
||||
ensureOutput(outputLength + 1);
|
||||
output[outputLength++] = symbol;
|
||||
} else {
|
||||
const lengthIndex = symbol - 257;
|
||||
const length = LENGTH_BASES[lengthIndex] + readBits(LENGTH_EXTRA_BITS[lengthIndex]);
|
||||
const distanceIndex = decodeSymbol(distanceTable);
|
||||
const distance = DISTANCE_BASES[distanceIndex] + readBits(DISTANCE_EXTRA_BITS[distanceIndex]);
|
||||
ensureOutput(outputLength + length);
|
||||
const copyStart = outputLength - distance;
|
||||
for (let indexByte = 0; indexByte < length; indexByte++) {
|
||||
output[outputLength++] = output[copyStart + indexByte];
|
||||
}
|
||||
}
|
||||
symbol = decodeSymbol(literalTable);
|
||||
}
|
||||
}
|
||||
|
||||
function readDynamicTables() {
|
||||
const literalLengthCount = readBits(5) + 257;
|
||||
const distanceLengthCount = readBits(5) + 1;
|
||||
const codeLengthCount = readBits(4) + 4;
|
||||
const codeLengthLengths = new Uint8Array(19);
|
||||
for (let indexCode = 0; indexCode < codeLengthCount; indexCode++) {
|
||||
codeLengthLengths[CODE_LENGTH_CODE_ORDER[indexCode]] = readBits(3);
|
||||
}
|
||||
const codeLengthTable = buildHuffmanTable(codeLengthLengths);
|
||||
const lengths = new Uint8Array(literalLengthCount + distanceLengthCount);
|
||||
let indexLength = 0;
|
||||
while (indexLength < lengths.length) {
|
||||
const symbol = decodeSymbol(codeLengthTable);
|
||||
if (symbol < 16) {
|
||||
lengths[indexLength++] = symbol;
|
||||
} else if (symbol == 16) {
|
||||
const previousLength = lengths[indexLength - 1];
|
||||
let repeatCount = readBits(2) + 3;
|
||||
while (repeatCount--) {
|
||||
lengths[indexLength++] = previousLength;
|
||||
}
|
||||
} else {
|
||||
const repeatCount = symbol == 17 ? readBits(3) + 3 : readBits(7) + 11;
|
||||
indexLength += repeatCount;
|
||||
}
|
||||
}
|
||||
return [
|
||||
buildHuffmanTable(lengths.subarray(0, literalLengthCount)),
|
||||
buildHuffmanTable(lengths.subarray(literalLengthCount))
|
||||
];
|
||||
}
|
||||
|
||||
function decodeSymbol(table) {
|
||||
const { lengthCounts, symbols } = table;
|
||||
let code = 0;
|
||||
let first = 0;
|
||||
let index = 0;
|
||||
for (let length = 1; length <= MAX_CODE_LENGTH; length++) {
|
||||
code |= readBits(1);
|
||||
const count = lengthCounts[length];
|
||||
if (code - first < count) {
|
||||
return symbols[index + (code - first)];
|
||||
}
|
||||
index += count;
|
||||
first = (first + count) << 1;
|
||||
code <<= 1;
|
||||
}
|
||||
throw new Error("invalid huffman code");
|
||||
}
|
||||
|
||||
function ensureOutput(length) {
|
||||
if (output.length < length) {
|
||||
let newLength = output.length * 2;
|
||||
while (newLength < length) {
|
||||
newLength *= 2;
|
||||
}
|
||||
const newOutput = new Uint8Array(newLength);
|
||||
newOutput.set(output.subarray(0, outputLength));
|
||||
output = newOutput;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function buildHuffmanTable(codeLengths) {
|
||||
const lengthCounts = new Uint16Array(MAX_CODE_LENGTH + 1);
|
||||
for (const length of codeLengths) {
|
||||
lengthCounts[length]++;
|
||||
}
|
||||
lengthCounts[0] = 0;
|
||||
const offsets = new Uint16Array(MAX_CODE_LENGTH + 2);
|
||||
for (let length = 1; length <= MAX_CODE_LENGTH; length++) {
|
||||
offsets[length + 1] = offsets[length] + lengthCounts[length];
|
||||
}
|
||||
const symbols = new Uint16Array(codeLengths.length);
|
||||
for (let symbol = 0; symbol < codeLengths.length; symbol++) {
|
||||
if (codeLengths[symbol]) {
|
||||
symbols[offsets[codeLengths[symbol]]++] = symbol;
|
||||
}
|
||||
}
|
||||
return { lengthCounts, symbols };
|
||||
}
|
||||
+1641
File diff suppressed because it is too large
Load Diff
+139
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global navigator, crypto, ReadableStream, WritableStream */
|
||||
|
||||
export {
|
||||
createOPFSTempStream
|
||||
};
|
||||
|
||||
const DEFAULT_THRESHOLD = 1024 * 1024;
|
||||
const DEFAULT_DIRECTORY_NAME = ".zip.js-temp";
|
||||
|
||||
function createOPFSTempStream(options = {}) {
|
||||
const {
|
||||
thresholdBytes = DEFAULT_THRESHOLD,
|
||||
directoryName = DEFAULT_DIRECTORY_NAME,
|
||||
getDirectory = () => navigator.storage.getDirectory()
|
||||
} = options;
|
||||
let directoryHandlePromise;
|
||||
function getTempDirectory() {
|
||||
if (!directoryHandlePromise) {
|
||||
directoryHandlePromise = Promise.resolve(getDirectory())
|
||||
.then(root => root.getDirectoryHandle(directoryName, { create: true }));
|
||||
}
|
||||
return directoryHandlePromise;
|
||||
}
|
||||
return function () {
|
||||
const memoryChunks = [];
|
||||
let bufferedSize = 0;
|
||||
let spilled = false;
|
||||
let fileName, fileHandle, fileWriter, fileReader;
|
||||
|
||||
async function spillToFile() {
|
||||
const directoryHandle = await getTempDirectory();
|
||||
fileName = crypto.randomUUID();
|
||||
fileHandle = await directoryHandle.getFileHandle(fileName, { create: true });
|
||||
fileWriter = (await fileHandle.createWritable()).getWriter();
|
||||
spilled = true;
|
||||
for (const chunk of memoryChunks) {
|
||||
await fileWriter.write(chunk);
|
||||
}
|
||||
memoryChunks.length = 0;
|
||||
}
|
||||
|
||||
const writable = new WritableStream({
|
||||
async write(chunk) {
|
||||
if (spilled) {
|
||||
await fileWriter.write(chunk);
|
||||
} else {
|
||||
memoryChunks.push(chunk);
|
||||
bufferedSize += chunk.length;
|
||||
if (bufferedSize > thresholdBytes) {
|
||||
await spillToFile();
|
||||
}
|
||||
}
|
||||
},
|
||||
async close() {
|
||||
if (fileWriter) {
|
||||
await fileWriter.close();
|
||||
fileWriter = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let memoryIndex = 0;
|
||||
const readable = new ReadableStream({
|
||||
async pull(controller) {
|
||||
if (spilled) {
|
||||
if (!fileReader) {
|
||||
const file = await fileHandle.getFile();
|
||||
fileReader = file.stream().getReader();
|
||||
}
|
||||
const { value, done } = await fileReader.read();
|
||||
if (done) {
|
||||
controller.close();
|
||||
} else {
|
||||
controller.enqueue(value);
|
||||
}
|
||||
} else if (memoryIndex < memoryChunks.length) {
|
||||
controller.enqueue(memoryChunks[memoryIndex++]);
|
||||
} else {
|
||||
controller.close();
|
||||
}
|
||||
},
|
||||
async cancel(reason) {
|
||||
if (fileReader) {
|
||||
await fileReader.cancel(reason);
|
||||
}
|
||||
}
|
||||
}, { highWaterMark: 0 });
|
||||
async function dispose() {
|
||||
if (fileWriter) {
|
||||
try {
|
||||
await fileWriter.close();
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
fileWriter = null;
|
||||
}
|
||||
if (fileName) {
|
||||
try {
|
||||
const directoryHandle = await getTempDirectory();
|
||||
await directoryHandle.removeEntry(fileName);
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
fileHandle = fileName = null;
|
||||
}
|
||||
memoryChunks.length = 0;
|
||||
}
|
||||
|
||||
return { writable, readable, dispose };
|
||||
};
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global navigator, crypto, FileSystemFileHandle, ReadableStream, WritableStream */
|
||||
|
||||
export {
|
||||
createSyncAccessHandleTempStream
|
||||
};
|
||||
|
||||
const DEFAULT_THRESHOLD = 1024 * 1024;
|
||||
const DEFAULT_DIRECTORY_NAME = ".zip.js-temp";
|
||||
const READ_CHUNK_SIZE = 512 * 1024;
|
||||
const ERR_UNSUPPORTED_CONTEXT = "createSyncAccessHandle is only available in dedicated workers";
|
||||
|
||||
function createSyncAccessHandleTempStream(options = {}) {
|
||||
const {
|
||||
thresholdBytes = DEFAULT_THRESHOLD,
|
||||
directoryName = DEFAULT_DIRECTORY_NAME,
|
||||
getDirectory
|
||||
} = options;
|
||||
if (!getDirectory &&
|
||||
(typeof FileSystemFileHandle == "undefined" || !FileSystemFileHandle.prototype.createSyncAccessHandle)) {
|
||||
throw new Error(ERR_UNSUPPORTED_CONTEXT);
|
||||
}
|
||||
const getRootDirectory = getDirectory || (() => navigator.storage.getDirectory());
|
||||
let directoryHandlePromise;
|
||||
function getTempDirectory() {
|
||||
if (!directoryHandlePromise) {
|
||||
directoryHandlePromise = Promise.resolve(getRootDirectory())
|
||||
.then(root => root.getDirectoryHandle(directoryName, { create: true }));
|
||||
}
|
||||
return directoryHandlePromise;
|
||||
}
|
||||
return function () {
|
||||
const memoryChunks = [];
|
||||
let bufferedSize = 0;
|
||||
let spilled = false;
|
||||
let fileName, accessHandle;
|
||||
let writeOffset = 0;
|
||||
let readOffset = 0;
|
||||
|
||||
async function spillToFile() {
|
||||
const directoryHandle = await getTempDirectory();
|
||||
fileName = crypto.randomUUID();
|
||||
const fileHandle = await directoryHandle.getFileHandle(fileName, { create: true });
|
||||
accessHandle = await fileHandle.createSyncAccessHandle();
|
||||
spilled = true;
|
||||
for (const chunk of memoryChunks) {
|
||||
accessHandle.write(chunk, { at: writeOffset });
|
||||
writeOffset += chunk.length;
|
||||
}
|
||||
memoryChunks.length = 0;
|
||||
}
|
||||
|
||||
const writable = new WritableStream({
|
||||
async write(chunk) {
|
||||
if (spilled) {
|
||||
accessHandle.write(chunk, { at: writeOffset });
|
||||
writeOffset += chunk.length;
|
||||
} else {
|
||||
memoryChunks.push(chunk);
|
||||
bufferedSize += chunk.length;
|
||||
if (bufferedSize > thresholdBytes) {
|
||||
await spillToFile();
|
||||
}
|
||||
}
|
||||
},
|
||||
close() {
|
||||
if (accessHandle) {
|
||||
accessHandle.flush();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let memoryIndex = 0;
|
||||
const readable = new ReadableStream({
|
||||
pull(controller) {
|
||||
if (spilled) {
|
||||
const remaining = writeOffset - readOffset;
|
||||
if (remaining <= 0) {
|
||||
controller.close();
|
||||
return;
|
||||
}
|
||||
const buffer = new Uint8Array(Math.min(READ_CHUNK_SIZE, remaining));
|
||||
const read = accessHandle.read(buffer, { at: readOffset });
|
||||
if (read) {
|
||||
readOffset += read;
|
||||
controller.enqueue(buffer.subarray(0, read));
|
||||
} else {
|
||||
controller.close();
|
||||
}
|
||||
} else if (memoryIndex < memoryChunks.length) {
|
||||
controller.enqueue(memoryChunks[memoryIndex++]);
|
||||
} else {
|
||||
controller.close();
|
||||
}
|
||||
}
|
||||
}, { highWaterMark: 0 });
|
||||
async function dispose() {
|
||||
if (accessHandle) {
|
||||
try {
|
||||
accessHandle.close();
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
accessHandle = null;
|
||||
}
|
||||
if (fileName) {
|
||||
try {
|
||||
const directoryHandle = await getTempDirectory();
|
||||
await directoryHandle.removeEntry(fileName);
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
fileName = null;
|
||||
}
|
||||
memoryChunks.length = 0;
|
||||
}
|
||||
|
||||
return { writable, readable, dispose };
|
||||
};
|
||||
}
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global self, addEventListener, postMessage, ReadableStream, WritableStream, AbortController */
|
||||
|
||||
import {
|
||||
CodecStream,
|
||||
ChunkStream,
|
||||
MESSAGE_EVENT_TYPE,
|
||||
MESSAGE_START,
|
||||
MESSAGE_PULL,
|
||||
MESSAGE_DATA,
|
||||
MESSAGE_ACK_DATA,
|
||||
MESSAGE_CLOSE
|
||||
} from "./streams/codec-stream.js";
|
||||
import { getChunkSize } from "./configuration.js";
|
||||
|
||||
const pendingPullMessages = new Map();
|
||||
const pendingDataMessages = new Map();
|
||||
|
||||
let abortController, messageId = 0;
|
||||
|
||||
addEventListener(MESSAGE_EVENT_TYPE, ({ data }) => {
|
||||
const { type, messageId, value, done } = data;
|
||||
try {
|
||||
if (type == MESSAGE_START) {
|
||||
init(data);
|
||||
}
|
||||
if (type == MESSAGE_DATA) {
|
||||
const resolve = pendingPullMessages.get(messageId);
|
||||
pendingPullMessages.delete(messageId);
|
||||
resolve({ value: new Uint8Array(value), done });
|
||||
}
|
||||
if (type == MESSAGE_ACK_DATA) {
|
||||
const resolve = pendingDataMessages.get(messageId);
|
||||
pendingDataMessages.delete(messageId);
|
||||
resolve();
|
||||
}
|
||||
if (type == MESSAGE_CLOSE) {
|
||||
abortController.abort();
|
||||
}
|
||||
} catch (error) {
|
||||
sendErrorMessage(error);
|
||||
}
|
||||
});
|
||||
|
||||
async function init(message) {
|
||||
let codecStream, writable;
|
||||
try {
|
||||
const { options, config } = message;
|
||||
if (!options.useCompressionStream) {
|
||||
try {
|
||||
await self.initModule(message.config);
|
||||
} catch {
|
||||
options.useCompressionStream = true;
|
||||
}
|
||||
}
|
||||
config.CompressionStream = self.CompressionStream;
|
||||
config.DecompressionStream = self.DecompressionStream;
|
||||
const strategy = { highWaterMark: 1 };
|
||||
const readable = message.readable || new ReadableStream({
|
||||
async pull(controller) {
|
||||
const result = new Promise(resolve => pendingPullMessages.set(messageId, resolve));
|
||||
sendMessage({ type: MESSAGE_PULL, messageId });
|
||||
messageId = (messageId + 1) % Number.MAX_SAFE_INTEGER;
|
||||
const { value, done } = await result;
|
||||
controller.enqueue(value);
|
||||
if (done) {
|
||||
controller.close();
|
||||
}
|
||||
}
|
||||
}, strategy);
|
||||
writable = message.writable || new WritableStream({
|
||||
async write(value) {
|
||||
let resolveAckData;
|
||||
const ackData = new Promise(resolve => resolveAckData = resolve);
|
||||
pendingDataMessages.set(messageId, resolveAckData);
|
||||
sendMessage({ type: MESSAGE_DATA, value, messageId });
|
||||
messageId = (messageId + 1) % Number.MAX_SAFE_INTEGER;
|
||||
await ackData;
|
||||
}
|
||||
}, strategy);
|
||||
codecStream = new CodecStream(options, config);
|
||||
abortController = new AbortController();
|
||||
const { signal } = abortController;
|
||||
await readable
|
||||
.pipeThrough(codecStream)
|
||||
.pipeThrough(new ChunkStream(getChunkSize(config)))
|
||||
.pipeTo(writable, { signal, preventClose: true, preventAbort: true });
|
||||
await writable.getWriter().close();
|
||||
const {
|
||||
signature,
|
||||
inputSize,
|
||||
outputSize
|
||||
} = codecStream;
|
||||
sendMessage({
|
||||
type: MESSAGE_CLOSE,
|
||||
result: {
|
||||
signature,
|
||||
inputSize,
|
||||
outputSize
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
error.outputSize = codecStream ? codecStream.outputSize : 0;
|
||||
if (writable && !writable.locked) {
|
||||
try {
|
||||
await writable.getWriter().close();
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
sendErrorMessage(error);
|
||||
}
|
||||
}
|
||||
|
||||
function sendMessage(message) {
|
||||
let { value } = message;
|
||||
if (value) {
|
||||
if (value.length) {
|
||||
try {
|
||||
value = new Uint8Array(value);
|
||||
message.value = value.buffer;
|
||||
postMessage(message, [message.value]);
|
||||
} catch {
|
||||
postMessage(message);
|
||||
}
|
||||
} else {
|
||||
postMessage(message);
|
||||
}
|
||||
} else {
|
||||
postMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
function sendErrorMessage(error = new Error("Unknown error")) {
|
||||
const { message, stack, code, name, outputSize } = error;
|
||||
postMessage({ error: { message, stack, code, name, outputSize } });
|
||||
}
|
||||
+1
File diff suppressed because one or more lines are too long
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global Blob, URL */
|
||||
/* eslint-disable quotes */
|
||||
import { inflateRaw } from "./util/inflate.js";
|
||||
import { base64Decode, base64Encode } from "./util/base64.js";
|
||||
|
||||
export function configureWebWorker(configure) {
|
||||
const code = `__workerCode__`;
|
||||
configure({
|
||||
workerURI: (useBlobURI) => {
|
||||
const type = "text/javascript";
|
||||
const source = inflateRaw(base64Decode(code));
|
||||
if (useBlobURI) {
|
||||
const blob = new Blob([source], { type });
|
||||
return URL.createObjectURL(blob);
|
||||
} else {
|
||||
return "data:" + type + ";base64," + base64Encode(source);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global Blob, URL */
|
||||
/* eslint-disable quotes */
|
||||
|
||||
export function configureWebWorker(configure) {
|
||||
const code = `__workerCode__`;
|
||||
|
||||
configure({
|
||||
workerURI: (useBlobURI) => {
|
||||
const type = "text/javascript";
|
||||
if (useBlobURI) {
|
||||
const blob = new Blob([code], { type });
|
||||
return URL.createObjectURL(blob);
|
||||
} else {
|
||||
return "data:" + type + "," + encodeURIComponent(code);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
+1
File diff suppressed because one or more lines are too long
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global self */
|
||||
|
||||
import "./web-worker-base.js";
|
||||
import {
|
||||
CompressionStreamZlib,
|
||||
DecompressionStreamZlib,
|
||||
} from "./streams/zlib-js/zlib-streams.min.js";
|
||||
|
||||
self.initModule = config => {
|
||||
config.CompressionStreamZlib = CompressionStreamZlib;
|
||||
config.DecompressionStreamZlib = DecompressionStreamZlib;
|
||||
};
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* global self */
|
||||
|
||||
import "./web-worker-base.js";
|
||||
import {
|
||||
CompressionStreamZlib,
|
||||
DecompressionStreamZlib,
|
||||
initModule as init
|
||||
} from "./streams/zlib-wasm/zlib-streams-loader.js";
|
||||
|
||||
self.initModule = async config => {
|
||||
try {
|
||||
const result = await init(config.wasmURI, config);
|
||||
config.CompressionStreamZlib = CompressionStreamZlib;
|
||||
config.DecompressionStreamZlib = DecompressionStreamZlib;
|
||||
return result;
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
};
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
const PROPERTY_NAME_FILENAME = "filename";
|
||||
const PROPERTY_NAME_RAW_FILENAME = "rawFilename";
|
||||
const PROPERTY_NAME_COMMENT = "comment";
|
||||
const PROPERTY_NAME_RAW_COMMENT = "rawComment";
|
||||
const PROPERTY_NAME_UNCOMPRESSED_SIZE = "uncompressedSize";
|
||||
const PROPERTY_NAME_COMPRESSED_SIZE = "compressedSize";
|
||||
const PROPERTY_NAME_OFFSET = "offset";
|
||||
const PROPERTY_NAME_DISK_NUMBER_START = "diskNumberStart";
|
||||
const PROPERTY_NAME_LAST_MODIFICATION_DATE = "lastModDate";
|
||||
const PROPERTY_NAME_RAW_LAST_MODIFICATION_DATE = "rawLastModDate";
|
||||
const PROPERTY_NAME_LAST_ACCESS_DATE = "lastAccessDate";
|
||||
const PROPERTY_NAME_RAW_LAST_ACCESS_DATE = "rawLastAccessDate";
|
||||
const PROPERTY_NAME_CREATION_DATE = "creationDate";
|
||||
const PROPERTY_NAME_RAW_CREATION_DATE = "rawCreationDate";
|
||||
const PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES = "internalFileAttributes";
|
||||
const PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTES = "externalFileAttributes";
|
||||
const PROPERTY_NAME_MSDOS_ATTRIBUTES_RAW = "msdosAttributesRaw";
|
||||
const PROPERTY_NAME_MSDOS_ATTRIBUTES = "msdosAttributes";
|
||||
const PROPERTY_NAME_MS_DOS_COMPATIBLE = "msDosCompatible";
|
||||
const PROPERTY_NAME_ZIP64 = "zip64";
|
||||
const PROPERTY_NAME_ENCRYPTED = "encrypted";
|
||||
const PROPERTY_NAME_VERSION = "version";
|
||||
const PROPERTY_NAME_VERSION_MADE_BY = "versionMadeBy";
|
||||
const PROPERTY_NAME_ZIPCRYPTO = "zipCrypto";
|
||||
const PROPERTY_NAME_DIRECTORY = "directory";
|
||||
const PROPERTY_NAME_EXECUTABLE = "executable";
|
||||
const PROPERTY_NAME_COMPRESSION_METHOD = "compressionMethod";
|
||||
const PROPERTY_NAME_SIGNATURE = "signature";
|
||||
const PROPERTY_NAME_EXTRA_FIELD = "extraField";
|
||||
const PROPERTY_NAME_EXTRA_FIELD_INFOZIP = "extraFieldInfoZip";
|
||||
const PROPERTY_NAME_EXTRA_FIELD_UNIX = "extraFieldUnix";
|
||||
const PROPERTY_NAME_UID = "uid";
|
||||
const PROPERTY_NAME_GID = "gid";
|
||||
const PROPERTY_NAME_UNIX_MODE = "unixMode";
|
||||
const PROPERTY_NAME_SETUID = "setuid";
|
||||
const PROPERTY_NAME_SETGID = "setgid";
|
||||
const PROPERTY_NAME_STICKY = "sticky";
|
||||
const PROPERTY_NAME_BITFLAG = "bitFlag";
|
||||
const PROPERTY_NAME_FILENAME_UTF8 = "filenameUTF8";
|
||||
const PROPERTY_NAME_COMMENT_UTF8 = "commentUTF8";
|
||||
const PROPERTY_NAME_RAW_EXTRA_FIELD = "rawExtraField";
|
||||
const PROPERTY_NAME_EXTRA_FIELD_ZIP64 = "extraFieldZip64";
|
||||
const PROPERTY_NAME_EXTRA_FIELD_UNICODE_PATH = "extraFieldUnicodePath";
|
||||
const PROPERTY_NAME_EXTRA_FIELD_UNICODE_COMMENT = "extraFieldUnicodeComment";
|
||||
const PROPERTY_NAME_EXTRA_FIELD_AES = "extraFieldAES";
|
||||
const PROPERTY_NAME_EXTRA_FIELD_NTFS = "extraFieldNTFS";
|
||||
const PROPERTY_NAME_EXTRA_FIELD_EXTENDED_TIMESTAMP = "extraFieldExtendedTimestamp";
|
||||
|
||||
const PROPERTY_NAMES = [
|
||||
PROPERTY_NAME_FILENAME,
|
||||
PROPERTY_NAME_RAW_FILENAME,
|
||||
PROPERTY_NAME_UNCOMPRESSED_SIZE,
|
||||
PROPERTY_NAME_COMPRESSED_SIZE,
|
||||
PROPERTY_NAME_LAST_MODIFICATION_DATE,
|
||||
PROPERTY_NAME_RAW_LAST_MODIFICATION_DATE,
|
||||
PROPERTY_NAME_COMMENT,
|
||||
PROPERTY_NAME_RAW_COMMENT,
|
||||
PROPERTY_NAME_LAST_ACCESS_DATE,
|
||||
PROPERTY_NAME_CREATION_DATE,
|
||||
PROPERTY_NAME_RAW_CREATION_DATE,
|
||||
PROPERTY_NAME_OFFSET,
|
||||
PROPERTY_NAME_DISK_NUMBER_START,
|
||||
PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES,
|
||||
PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTES,
|
||||
PROPERTY_NAME_MSDOS_ATTRIBUTES_RAW,
|
||||
PROPERTY_NAME_MSDOS_ATTRIBUTES,
|
||||
PROPERTY_NAME_MS_DOS_COMPATIBLE,
|
||||
PROPERTY_NAME_ZIP64,
|
||||
PROPERTY_NAME_ENCRYPTED,
|
||||
PROPERTY_NAME_VERSION,
|
||||
PROPERTY_NAME_VERSION_MADE_BY,
|
||||
PROPERTY_NAME_ZIPCRYPTO,
|
||||
PROPERTY_NAME_DIRECTORY,
|
||||
PROPERTY_NAME_EXECUTABLE,
|
||||
PROPERTY_NAME_COMPRESSION_METHOD,
|
||||
PROPERTY_NAME_SIGNATURE,
|
||||
PROPERTY_NAME_EXTRA_FIELD,
|
||||
PROPERTY_NAME_EXTRA_FIELD_UNIX,
|
||||
PROPERTY_NAME_EXTRA_FIELD_INFOZIP,
|
||||
PROPERTY_NAME_UID,
|
||||
PROPERTY_NAME_GID,
|
||||
PROPERTY_NAME_UNIX_MODE,
|
||||
PROPERTY_NAME_SETUID,
|
||||
PROPERTY_NAME_SETGID,
|
||||
PROPERTY_NAME_STICKY,
|
||||
PROPERTY_NAME_BITFLAG,
|
||||
PROPERTY_NAME_FILENAME_UTF8,
|
||||
PROPERTY_NAME_COMMENT_UTF8,
|
||||
PROPERTY_NAME_RAW_EXTRA_FIELD,
|
||||
PROPERTY_NAME_EXTRA_FIELD_ZIP64,
|
||||
PROPERTY_NAME_EXTRA_FIELD_UNICODE_PATH,
|
||||
PROPERTY_NAME_EXTRA_FIELD_UNICODE_COMMENT,
|
||||
PROPERTY_NAME_EXTRA_FIELD_AES,
|
||||
PROPERTY_NAME_EXTRA_FIELD_NTFS,
|
||||
PROPERTY_NAME_EXTRA_FIELD_EXTENDED_TIMESTAMP
|
||||
];
|
||||
|
||||
class Entry {
|
||||
|
||||
constructor(data) {
|
||||
PROPERTY_NAMES.forEach(name => this[name] = data[name]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export {
|
||||
PROPERTY_NAME_FILENAME,
|
||||
PROPERTY_NAME_RAW_FILENAME,
|
||||
PROPERTY_NAME_COMMENT,
|
||||
PROPERTY_NAME_RAW_COMMENT,
|
||||
PROPERTY_NAME_UNCOMPRESSED_SIZE,
|
||||
PROPERTY_NAME_COMPRESSED_SIZE,
|
||||
PROPERTY_NAME_OFFSET,
|
||||
PROPERTY_NAME_DISK_NUMBER_START,
|
||||
PROPERTY_NAME_LAST_MODIFICATION_DATE,
|
||||
PROPERTY_NAME_RAW_LAST_MODIFICATION_DATE,
|
||||
PROPERTY_NAME_LAST_ACCESS_DATE,
|
||||
PROPERTY_NAME_RAW_LAST_ACCESS_DATE,
|
||||
PROPERTY_NAME_CREATION_DATE,
|
||||
PROPERTY_NAME_RAW_CREATION_DATE,
|
||||
PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES,
|
||||
PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTES,
|
||||
PROPERTY_NAME_MSDOS_ATTRIBUTES_RAW,
|
||||
PROPERTY_NAME_MSDOS_ATTRIBUTES,
|
||||
PROPERTY_NAME_MS_DOS_COMPATIBLE,
|
||||
PROPERTY_NAME_ZIP64,
|
||||
PROPERTY_NAME_ENCRYPTED,
|
||||
PROPERTY_NAME_VERSION,
|
||||
PROPERTY_NAME_VERSION_MADE_BY,
|
||||
PROPERTY_NAME_ZIPCRYPTO,
|
||||
PROPERTY_NAME_DIRECTORY,
|
||||
PROPERTY_NAME_EXECUTABLE,
|
||||
PROPERTY_NAME_COMPRESSION_METHOD,
|
||||
PROPERTY_NAME_SIGNATURE,
|
||||
PROPERTY_NAME_EXTRA_FIELD,
|
||||
PROPERTY_NAME_GID,
|
||||
PROPERTY_NAME_UID,
|
||||
PROPERTY_NAME_UNIX_MODE,
|
||||
PROPERTY_NAME_SETUID,
|
||||
PROPERTY_NAME_SETGID,
|
||||
PROPERTY_NAME_STICKY,
|
||||
Entry
|
||||
};
|
||||
+1045
File diff suppressed because it is too large
Load Diff
+1236
File diff suppressed because it is too large
Load Diff
+1952
File diff suppressed because it is too large
Load Diff
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* eslint-disable quotes */
|
||||
|
||||
import { inflateRaw } from "./util/inflate.js";
|
||||
import { base64Decode, base64Encode } from "./util/base64.js";
|
||||
|
||||
export function configureZlibModule(configure) {
|
||||
const data = `__wasmBinary__`;
|
||||
let dataURI;
|
||||
configure({
|
||||
wasmURI: () => {
|
||||
if (!dataURI) {
|
||||
dataURI = "data:application/wasm;base64," + base64Encode(inflateRaw(base64Decode(data)));
|
||||
}
|
||||
return dataURI;
|
||||
}
|
||||
});
|
||||
}
|
||||
+1
File diff suppressed because one or more lines are too long
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import { configure } from "./core/configuration.js";
|
||||
|
||||
try {
|
||||
configure({ baseURI: import.meta.url });
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
|
||||
export * from "./zip-core-reader.js";
|
||||
export * from "./zip-core-writer.js";
|
||||
export {
|
||||
Reader,
|
||||
Writer,
|
||||
TextReader,
|
||||
TextWriter,
|
||||
Data64URIReader,
|
||||
Data64URIWriter,
|
||||
BlobReader,
|
||||
BlobWriter,
|
||||
HttpReader,
|
||||
HttpRangeReader,
|
||||
Uint8ArrayWriter,
|
||||
Uint8ArrayReader,
|
||||
SplitDataReader,
|
||||
SplitDataWriter,
|
||||
ERR_HTTP_RANGE,
|
||||
ERR_ITERATOR_COMPLETED_TOO_SOON,
|
||||
ERR_WRITER_NOT_INITIALIZED
|
||||
} from "./core/io.js";
|
||||
export {
|
||||
getMimeType
|
||||
} from "./core/util/default-mime-type.js";
|
||||
export {
|
||||
configure
|
||||
} from "./core/configuration.js";
|
||||
export {
|
||||
createOPFSTempStream
|
||||
} from "./core/util/opfs-temp-stream.js";
|
||||
export {
|
||||
createBlobTempStream
|
||||
} from "./core/util/blob-temp-stream.js";
|
||||
export {
|
||||
createSyncAccessHandleTempStream
|
||||
} from "./core/util/sync-access-handle-temp-stream.js";
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
export * from "./zip-core-base.js";
|
||||
export * from "./zip-module-native.js";
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
export {
|
||||
ZipReader,
|
||||
ZipReaderStream,
|
||||
ERR_BAD_FORMAT,
|
||||
ERR_EOCDR_NOT_FOUND,
|
||||
ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND,
|
||||
ERR_CENTRAL_DIRECTORY_NOT_FOUND,
|
||||
ERR_LOCAL_FILE_HEADER_NOT_FOUND,
|
||||
ERR_EXTRAFIELD_ZIP64_NOT_FOUND,
|
||||
ERR_ENCRYPTED,
|
||||
ERR_UNSUPPORTED_ENCRYPTION,
|
||||
ERR_UNSUPPORTED_COMPRESSION,
|
||||
ERR_INVALID_SIGNATURE,
|
||||
ERR_INVALID_UNCOMPRESSED_SIZE,
|
||||
ERR_INVALID_PASSWORD,
|
||||
ERR_INVALID_COMPRESSED_DATA,
|
||||
ERR_SPLIT_ZIP_FILE,
|
||||
ERR_OVERLAPPING_ENTRY,
|
||||
ERR_AMBIGUOUS_ARCHIVE
|
||||
} from "./core/zip-reader.js";
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
export * from "./zip-core-base.js";
|
||||
export * from "./zip-module-wasm.js";
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
export {
|
||||
ZipWriter,
|
||||
ZipWriterStream,
|
||||
ERR_DUPLICATED_NAME,
|
||||
ERR_INVALID_COMMENT,
|
||||
ERR_INVALID_ENTRY_NAME,
|
||||
ERR_INVALID_ENTRY_COMMENT,
|
||||
ERR_INVALID_VERSION,
|
||||
ERR_INVALID_EXTRAFIELD_TYPE,
|
||||
ERR_INVALID_EXTRAFIELD_DATA,
|
||||
ERR_INVALID_ENCRYPTION_STRENGTH,
|
||||
ERR_UNSUPPORTED_FORMAT,
|
||||
ERR_UNDEFINED_UNCOMPRESSED_SIZE,
|
||||
ERR_UNDEFINED_READER,
|
||||
ERR_ZIP_NOT_EMPTY
|
||||
} from "./core/zip-writer.js";
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
export * from "./zip-core-base.js";
|
||||
export { terminateWorkers } from "./core/codec-pool.js";
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
export * from "./zip-core-native.js";
|
||||
export * from "./core/zip-fs.js";
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
export * from "./zip-core-wasm.js";
|
||||
export * from "./core/zip-fs.js";
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import { configure } from "./core/configuration.js";
|
||||
import { configureWebWorker } from "./core/web-worker-inline-native.js";
|
||||
|
||||
configureWebWorker(configure);
|
||||
|
||||
export * from "./zip-fs-core-native.js";
|
||||
export { getMimeType } from "./core/util/mime-type.js";
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import { configure } from "./core/configuration.js";
|
||||
import { configureWebWorker } from "./core/web-worker-inline-wasm.js";
|
||||
|
||||
configureWebWorker(configure);
|
||||
|
||||
export * from "./zip-fs-core-wasm.js";
|
||||
export { getMimeType } from "./core/util/mime-type.js";
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import { configure } from "./core/configuration.js";
|
||||
import { configureWebWorker } from "./core/web-worker-inline-native.js";
|
||||
|
||||
configureWebWorker(configure);
|
||||
|
||||
export * from "./zip-core-base.js";
|
||||
export { terminateWorkers } from "./core/codec-pool.js";
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import { configure } from "./core/configuration.js";
|
||||
import { CompressionStreamZlib, DecompressionStreamZlib } from "./core/streams/zlib-js/zlib-streams.min.js";
|
||||
|
||||
configure({
|
||||
workerURI: "./core/web-worker-native.js",
|
||||
wasmURI: null,
|
||||
CompressionStreamZlib,
|
||||
DecompressionStreamZlib
|
||||
});
|
||||
|
||||
export { terminateWorkers } from "./core/codec-pool.js";
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import { FUNCTION_TYPE } from "./core/constants.js";
|
||||
import { configure } from "./core/configuration.js";
|
||||
import { configureZlibModule } from "./core/zlib-streams-inline.js";
|
||||
import { configureWorker } from "./core/codec-worker.js";
|
||||
import { terminateWorkers } from "./core/codec-pool.js";
|
||||
import { initModule, resetWasmModule, CompressionStreamZlib, DecompressionStreamZlib } from "./core/streams/zlib-wasm/zlib-streams-loader.js";
|
||||
|
||||
let modulePromise;
|
||||
|
||||
configureZlibModule(configure);
|
||||
configureWorker({
|
||||
initModule: config => {
|
||||
if (!modulePromise) {
|
||||
let { wasmURI } = config;
|
||||
// deno-lint-ignore valid-typeof
|
||||
if (typeof wasmURI == FUNCTION_TYPE) {
|
||||
wasmURI = wasmURI();
|
||||
}
|
||||
modulePromise = initModule(wasmURI, config);
|
||||
|
||||
}
|
||||
return modulePromise;
|
||||
}
|
||||
});
|
||||
configure({
|
||||
CompressionStreamZlib,
|
||||
DecompressionStreamZlib
|
||||
});
|
||||
|
||||
export { terminateWorkersAndModule as terminateWorkers };
|
||||
|
||||
function terminateWorkersAndModule() {
|
||||
modulePromise = null;
|
||||
terminateWorkers();
|
||||
resetWasmModule();
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import { configure } from "./core/configuration.js";
|
||||
import { configureWebWorker } from "./core/web-worker-inline-native.js";
|
||||
|
||||
configureWebWorker(configure);
|
||||
|
||||
export * from "./zip-core-native.js";
|
||||
export * from "./zip-module-native.js";
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import { configure } from "./core/configuration.js";
|
||||
import { configureWebWorker } from "./core/web-worker-inline-wasm.js";
|
||||
|
||||
configureWebWorker(configure);
|
||||
|
||||
export * from "./zip-core-wasm.js";
|
||||
export * from "./zip-module-wasm.js";
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"name": "@zip.js/zip.js",
|
||||
"description": "A JavaScript library to zip and unzip files in the browser, Deno and Node.js",
|
||||
"author": "Gildas Lormeau",
|
||||
"license": "BSD-3-Clause",
|
||||
"version": "2.8.34",
|
||||
"type": "module",
|
||||
"keywords": [
|
||||
"zip",
|
||||
"unzip",
|
||||
"browser",
|
||||
"web",
|
||||
"aes-256",
|
||||
"aes-192",
|
||||
"aes-128",
|
||||
"zip-crypto",
|
||||
"encryption",
|
||||
"zip64",
|
||||
"web-streams",
|
||||
"compression-streams",
|
||||
"web-crypto",
|
||||
"web-workers",
|
||||
"deno",
|
||||
"node.js",
|
||||
"multi-core",
|
||||
"split-zip",
|
||||
"usdz",
|
||||
"deflate64",
|
||||
"wasm"
|
||||
],
|
||||
"engines": {
|
||||
"deno": ">=1.0.0",
|
||||
"node": ">=18.0.0",
|
||||
"bun": ">=0.7.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npx rollup -c",
|
||||
"build-dev": "npx rollup -c rollup.config.dev.js",
|
||||
"build-doc-api": "npx typedoc",
|
||||
"build-doc-markdown": "npx typedoc --plugin typedoc-plugin-markdown --out ./docs",
|
||||
"test-deno": "cd ./tests && deno test --allow-read ./deno-runner.js",
|
||||
"test-node": "cd ./tests && node ./node-runner.js",
|
||||
"test-bun": "cd ./tests && bun test ./bun-runner.js --timeout 30000",
|
||||
"test-deno-dist": "./tests/dist-runner.sh deno",
|
||||
"test-node-dist": "./tests/dist-runner.sh node",
|
||||
"test-bun-dist": "./tests/dist-runner.sh bun",
|
||||
"test-types": "tsc -p ./tests/types/tsconfig.json",
|
||||
"test-firefox": "firefox http://localhost:8081/tests/ & npx http-server -p=8081 & wait $!",
|
||||
"test-chrome": "google-chrome http://localhost:8081/tests/ & npx http-server -p=8081 & wait $!"
|
||||
},
|
||||
"main": "./index.cjs",
|
||||
"module": "./index.js",
|
||||
"types": "./index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
"./dist/zip-web-worker.js": "./dist/zip-web-worker.js",
|
||||
"./dist/zip-web-worker-native.js": "./dist/zip-web-worker-native.js",
|
||||
"./dist/zip-module.wasm": "./dist/zip-module.wasm",
|
||||
".": {
|
||||
"react-native": {
|
||||
"types": "./index.d.cts",
|
||||
"default": "./index-native.cjs"
|
||||
},
|
||||
"import": {
|
||||
"types": "./index.d.ts",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./index.d.cts",
|
||||
"default": "./index.cjs"
|
||||
}
|
||||
},
|
||||
"./index-native.js": {
|
||||
"import": {
|
||||
"types": "./index.d.ts",
|
||||
"default": "./index-native.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./index.d.cts",
|
||||
"default": "./index-native.cjs"
|
||||
}
|
||||
},
|
||||
"./index.cjs": {
|
||||
"types": "./index.d.cts",
|
||||
"default": "./index.cjs"
|
||||
},
|
||||
"./index-native.cjs": {
|
||||
"types": "./index.d.cts",
|
||||
"default": "./index-native.cjs"
|
||||
},
|
||||
"./index.min.js": {
|
||||
"import": {
|
||||
"types": "./index.d.ts",
|
||||
"default": "./index.min.js"
|
||||
}
|
||||
},
|
||||
"./index-native.min.js": {
|
||||
"import": {
|
||||
"types": "./index.d.ts",
|
||||
"default": "./index-native.min.js"
|
||||
}
|
||||
},
|
||||
"./lib/zip-core-reader.js": {
|
||||
"import": {
|
||||
"types": "./index.d.ts",
|
||||
"default": "./lib/zip-core-reader.js"
|
||||
}
|
||||
},
|
||||
"./lib/zip-core-writer.js": {
|
||||
"import": {
|
||||
"types": "./index.d.ts",
|
||||
"default": "./lib/zip-core-writer.js"
|
||||
}
|
||||
},
|
||||
"./lib/zip-fs.js": {
|
||||
"import": {
|
||||
"types": "./index.d.ts",
|
||||
"default": "./lib/zip-fs-wasm.js"
|
||||
}
|
||||
},
|
||||
"./lib/zip-fs-native.js": {
|
||||
"import": {
|
||||
"types": "./index.d.ts",
|
||||
"default": "./lib/zip-fs-native.js"
|
||||
}
|
||||
},
|
||||
"./lib/zip-core.js": {
|
||||
"import": {
|
||||
"types": "./index.d.ts",
|
||||
"default": "./lib/zip-core-wasm.js"
|
||||
}
|
||||
},
|
||||
"./lib/zip-core-native.js": {
|
||||
"import": {
|
||||
"types": "./index.d.ts",
|
||||
"default": "./lib/zip-core-native.js"
|
||||
}
|
||||
},
|
||||
"./lib/zip-fs-core.js": {
|
||||
"import": {
|
||||
"types": "./index.d.ts",
|
||||
"default": "./lib/zip-fs-core-wasm.js"
|
||||
}
|
||||
},
|
||||
"./lib/zip-fs-core-native.js": {
|
||||
"import": {
|
||||
"types": "./index.d.ts",
|
||||
"default": "./lib/zip-fs-core-native.js"
|
||||
}
|
||||
},
|
||||
"./lib/zip.js": {
|
||||
"import": {
|
||||
"types": "./index.d.ts",
|
||||
"default": "./lib/zip-wasm.js"
|
||||
}
|
||||
},
|
||||
"./lib/zip-native.js": {
|
||||
"import": {
|
||||
"types": "./index.d.ts",
|
||||
"default": "./lib/zip-native.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/gildas-lormeau/zip.js.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/gildas-lormeau/zip.js/issues"
|
||||
},
|
||||
"homepage": "https://gildas-lormeau.github.io/zip.js",
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@rollup/plugin-replace": "^6.0.3",
|
||||
"@rollup/plugin-terser": "^1.0.0",
|
||||
"eslint": "^10.7.0",
|
||||
"http-server": "^14.1.1",
|
||||
"rollup": "^4.62.2",
|
||||
"typedoc": "^0.28.20",
|
||||
"typedoc-plugin-markdown": "^4.12.0"
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import ts from "typescript";
|
||||
import { domprops } from "./node_modules/terser/tools/domprops.js";
|
||||
|
||||
const ROOT = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const WORKER_BOUNDARY_PROPERTY_NAMES = ["codecType", "config", "salt", "iterations", "keys", "password", "rawPassword", "encryptionStrength", "encrypted", "signed", "signature", "compressed", "level", "zipCrypto", "passwordVerification", "deflate64", "pull", "enqueue", "close", "messageId", "chunkSize", "useCompressionStream", "preventAbort", "preventClose", "checkPasswordOnly", "inputSize", "outputSize", "inBufferSize", "outBuffer", "wasmURI", "malloc", "free", "inflate_new", "inflate_init_raw", "inflate_init_gzip", "inflate_init", "inflate_process", "inflate_last_consumed", "inflate_end", "inflate9_new", "inflate9_init_raw", "inflate9_process", "inflate9_last_consumed", "inflate9_end", "deflate_new", "deflate_init_raw", "deflate_init_gzip", "deflate_init", "deflate_process", "deflate_last_consumed", "deflate_end"];
|
||||
|
||||
const AUDITED_KEEP_PROPERTY_NAMES = ["instance", "zip"];
|
||||
|
||||
export {
|
||||
WORKER_BOUNDARY_PROPERTY_NAMES,
|
||||
AUDITED_KEEP_PROPERTY_NAMES,
|
||||
collectDeclarationNames,
|
||||
getReservedPropertyNames
|
||||
};
|
||||
|
||||
function getReservedPropertyNames() {
|
||||
const names = collectDeclarationNames(path.join(ROOT, "index.d.ts"));
|
||||
for (const name of WORKER_BOUNDARY_PROPERTY_NAMES) {
|
||||
names.add(name);
|
||||
}
|
||||
for (const name of AUDITED_KEEP_PROPERTY_NAMES) {
|
||||
names.add(name);
|
||||
}
|
||||
for (const name of domprops) {
|
||||
names.add(name);
|
||||
}
|
||||
return [...names];
|
||||
}
|
||||
|
||||
function collectDeclarationNames(filePath) {
|
||||
const names = new Set();
|
||||
const source = ts.createSourceFile(filePath, readFileSync(filePath, "utf8"), ts.ScriptTarget.Latest, true);
|
||||
visit(source);
|
||||
return names;
|
||||
|
||||
function visit(node) {
|
||||
if ((ts.isPropertySignature(node) || ts.isMethodSignature(node) || ts.isPropertyDeclaration(node) ||
|
||||
ts.isMethodDeclaration(node) || ts.isGetAccessorDeclaration(node) || ts.isSetAccessorDeclaration(node) ||
|
||||
ts.isEnumMember(node) || ts.isParameter(node) || ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node) ||
|
||||
ts.isFunctionDeclaration(node) || ts.isTypeAliasDeclaration(node) || ts.isVariableDeclaration(node)) &&
|
||||
node.name && (ts.isIdentifier(node.name) || ts.isStringLiteralLike(node.name))) {
|
||||
names.add(node.name.text);
|
||||
}
|
||||
ts.forEachChild(node, visit);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user