Arjun Barrett 4 лет назад
Родитель
Сommit
3442262c83

+ 14 - 1
README.md

@@ -54,12 +54,25 @@ If you want to load from a CDN in the browser:
 <!--
 <!--
 You should use either UNPKG or jsDelivr (i.e. only one of the following)
 You should use either UNPKG or jsDelivr (i.e. only one of the following)
 Note that tree shaking is completely unsupported from the CDN
 Note that tree shaking is completely unsupported from the CDN
+You may also want to specify the version, e.g. with [email protected]
 -->
 -->
-<script src="https://unpkg.com/fflate"></script>
+<script src="https://unpkg.com/fflate/umd/index.js"></script>
 <script src="https://cdn.jsdelivr.net/npm/fflate/umd/index.js"></script>
 <script src="https://cdn.jsdelivr.net/npm/fflate/umd/index.js"></script>
 <!-- Now, the global variable fflate contains the library -->
 <!-- Now, the global variable fflate contains the library -->
 ```
 ```
 
 
+If your environment doesn't support bundling:
+```js
+// Again, try to import just what you need
+
+// For the browser:
+import * as fflate from 'fflate/esm/browser.js';
+// If for some reason the standard ESM import fails on Node:
+import * as fflate from 'fflate/esm/index.mjs';
+```
+
+If you see `require('worker_threads')` in any code bundled for the browser, your bundler probably didn't resolve the `browser` field of `package.json`. You can enable it (e.g. [for Rollup](https://github.com/rollup/plugins/tree/master/packages/node-resolve#browser)) or you can manually import the ESM version at `fflate/esm/browser.js`. 
+
 And use:
 And use:
 ```js
 ```js
 // This is an ArrayBuffer of data
 // This is an ArrayBuffer of data

+ 34 - 0
docs/README.md

@@ -10,13 +10,19 @@
 * [AsyncGzip](classes/asyncgzip.md)
 * [AsyncGzip](classes/asyncgzip.md)
 * [AsyncInflate](classes/asyncinflate.md)
 * [AsyncInflate](classes/asyncinflate.md)
 * [AsyncUnzlib](classes/asyncunzlib.md)
 * [AsyncUnzlib](classes/asyncunzlib.md)
+* [AsyncZipDeflate](classes/asynczipdeflate.md)
 * [AsyncZlib](classes/asynczlib.md)
 * [AsyncZlib](classes/asynczlib.md)
+* [DecodeUTF8](classes/decodeutf8.md)
 * [Decompress](classes/decompress.md)
 * [Decompress](classes/decompress.md)
 * [Deflate](classes/deflate.md)
 * [Deflate](classes/deflate.md)
+* [EncodeUTF8](classes/encodeutf8.md)
 * [Gunzip](classes/gunzip.md)
 * [Gunzip](classes/gunzip.md)
 * [Gzip](classes/gzip.md)
 * [Gzip](classes/gzip.md)
 * [Inflate](classes/inflate.md)
 * [Inflate](classes/inflate.md)
 * [Unzlib](classes/unzlib.md)
 * [Unzlib](classes/unzlib.md)
+* [Zip](classes/zip.md)
+* [ZipDeflate](classes/zipdeflate.md)
+* [ZipPassThrough](classes/zippassthrough.md)
 * [Zlib](classes/zlib.md)
 * [Zlib](classes/zlib.md)
 
 
 ### Interfaces
 ### Interfaces
@@ -34,6 +40,8 @@
 * [DeflateOptions](interfaces/deflateoptions.md)
 * [DeflateOptions](interfaces/deflateoptions.md)
 * [GzipOptions](interfaces/gzipoptions.md)
 * [GzipOptions](interfaces/gzipoptions.md)
 * [Unzipped](interfaces/unzipped.md)
 * [Unzipped](interfaces/unzipped.md)
+* [ZipAttributes](interfaces/zipattributes.md)
+* [ZipInputFile](interfaces/zipinputfile.md)
 * [ZipOptions](interfaces/zipoptions.md)
 * [ZipOptions](interfaces/zipoptions.md)
 * [Zippable](interfaces/zippable.md)
 * [Zippable](interfaces/zippable.md)
 * [ZlibOptions](interfaces/zliboptions.md)
 * [ZlibOptions](interfaces/zliboptions.md)
@@ -44,7 +52,9 @@
 * [AsyncZippableFile](README.md#asynczippablefile)
 * [AsyncZippableFile](README.md#asynczippablefile)
 * [FlateCallback](README.md#flatecallback)
 * [FlateCallback](README.md#flatecallback)
 * [FlateStreamHandler](README.md#flatestreamhandler)
 * [FlateStreamHandler](README.md#flatestreamhandler)
+* [StringStreamHandler](README.md#stringstreamhandler)
 * [UnzipCallback](README.md#unzipcallback)
 * [UnzipCallback](README.md#unzipcallback)
+* [ZipProgressHandler](README.md#zipprogresshandler)
 * [ZippableFile](README.md#zippablefile)
 * [ZippableFile](README.md#zippablefile)
 
 
 ### Functions
 ### Functions
@@ -118,6 +128,18 @@ Handler for data (de)compression streams
 
 
 ___
 ___
 
 
+### StringStreamHandler
+
+Ƭ  **StringStreamHandler**: (data: string,final: boolean) => void
+
+Handler for string generation streams
+
+**`param`** The string output from the stream processor
+
+**`param`** Whether this is the final block
+
+___
+
 ### UnzipCallback
 ### UnzipCallback
 
 
 Ƭ  **UnzipCallback**: (err: Error \| string,data: [Unzipped](interfaces/unzipped.md)) => void
 Ƭ  **UnzipCallback**: (err: Error \| string,data: [Unzipped](interfaces/unzipped.md)) => void
@@ -130,6 +152,18 @@ Callback for asynchronous ZIP decompression
 
 
 ___
 ___
 
 
+### ZipProgressHandler
+
+Ƭ  **ZipProgressHandler**: (bytesRead: number,bytesOut: number) => void
+
+Callback for ZIP compression progress
+
+**`param`** Any error that occurred
+
+**`param`** The decompressed ZIP archive
+
+___
+
 ### ZippableFile
 ### ZippableFile
 
 
 Ƭ  **ZippableFile**: Uint8Array \| []
 Ƭ  **ZippableFile**: Uint8Array \| []

+ 155 - 0
docs/classes/asynczipdeflate.md

@@ -0,0 +1,155 @@
+# Class: AsyncZipDeflate
+
+Asynchronous streaming DEFLATE compression for ZIP archives
+
+## Hierarchy
+
+* **AsyncZipDeflate**
+
+## Implements
+
+* [ZipInputFile](../interfaces/zipinputfile.md)
+
+## Index
+
+### Constructors
+
+* [constructor](asynczipdeflate.md#constructor)
+
+### Properties
+
+* [attrs](asynczipdeflate.md#attrs)
+* [compression](asynczipdeflate.md#compression)
+* [crc](asynczipdeflate.md#crc)
+* [filename](asynczipdeflate.md#filename)
+* [flag](asynczipdeflate.md#flag)
+* [ondata](asynczipdeflate.md#ondata)
+* [os](asynczipdeflate.md#os)
+* [size](asynczipdeflate.md#size)
+* [terminate](asynczipdeflate.md#terminate)
+
+### Methods
+
+* [process](asynczipdeflate.md#process)
+* [push](asynczipdeflate.md#push)
+
+## Constructors
+
+### constructor
+
+\+ **new AsyncZipDeflate**(`filename`: string, `opts`: [DeflateOptions](../interfaces/deflateoptions.md)): [AsyncZipDeflate](asynczipdeflate.md)
+
+Creates a DEFLATE stream that can be added to ZIP archives
+
+#### Parameters:
+
+Name | Type | Default value | Description |
+------ | ------ | ------ | ------ |
+`filename` | string | - | The filename to associate with this data stream |
+`opts` | [DeflateOptions](../interfaces/deflateoptions.md) | {} | The compression options  |
+
+**Returns:** [AsyncZipDeflate](asynczipdeflate.md)
+
+## Properties
+
+### attrs
+
+• `Optional` **attrs**: number
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[attrs](../interfaces/zipinputfile.md#attrs)*
+
+___
+
+### compression
+
+•  **compression**: number
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[compression](../interfaces/zipinputfile.md#compression)*
+
+___
+
+### crc
+
+•  **crc**: number
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[crc](../interfaces/zipinputfile.md#crc)*
+
+___
+
+### filename
+
+•  **filename**: string
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[filename](../interfaces/zipinputfile.md#filename)*
+
+___
+
+### flag
+
+•  **flag**: 0 \| 1 \| 2 \| 3
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[flag](../interfaces/zipinputfile.md#flag)*
+
+___
+
+### ondata
+
+•  **ondata**: [AsyncFlateStreamHandler](../README.md#asyncflatestreamhandler)
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[ondata](../interfaces/zipinputfile.md#ondata)*
+
+___
+
+### os
+
+• `Optional` **os**: number
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[os](../interfaces/zipinputfile.md#os)*
+
+___
+
+### size
+
+•  **size**: number
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[size](../interfaces/zipinputfile.md#size)*
+
+___
+
+### terminate
+
+•  **terminate**: [AsyncTerminable](../interfaces/asyncterminable.md)
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[terminate](../interfaces/zipinputfile.md#terminate)*
+
+## Methods
+
+### process
+
+▸ **process**(`chunk`: Uint8Array, `final`: boolean): void
+
+#### Parameters:
+
+Name | Type |
+------ | ------ |
+`chunk` | Uint8Array |
+`final` | boolean |
+
+**Returns:** void
+
+___
+
+### push
+
+▸ **push**(`chunk`: Uint8Array, `final?`: boolean): void
+
+Pushes a chunk to be deflated
+
+#### Parameters:
+
+Name | Type | Description |
+------ | ------ | ------ |
+`chunk` | Uint8Array | The chunk to push |
+`final?` | boolean | Whether this is the last chunk  |
+
+**Returns:** void

+ 62 - 0
docs/classes/decodeutf8.md

@@ -0,0 +1,62 @@
+# Class: DecodeUTF8
+
+Streaming UTF-8 decoding
+
+## Hierarchy
+
+* **DecodeUTF8**
+
+## Index
+
+### Constructors
+
+* [constructor](decodeutf8.md#constructor)
+
+### Properties
+
+* [ondata](decodeutf8.md#ondata)
+
+### Methods
+
+* [push](decodeutf8.md#push)
+
+## Constructors
+
+### constructor
+
+\+ **new DecodeUTF8**(`handler?`: [StringStreamHandler](../README.md#stringstreamhandler)): [DecodeUTF8](decodeutf8.md)
+
+Creates a UTF-8 decoding stream
+
+#### Parameters:
+
+Name | Type |
+------ | ------ |
+`handler?` | [StringStreamHandler](../README.md#stringstreamhandler) |
+
+**Returns:** [DecodeUTF8](decodeutf8.md)
+
+## Properties
+
+### ondata
+
+•  **ondata**: [StringStreamHandler](../README.md#stringstreamhandler)
+
+The handler to call whenever data is available
+
+## Methods
+
+### push
+
+▸ **push**(`chunk`: Uint8Array, `final?`: boolean): void
+
+Pushes a chunk to be decoded from UTF-8 binary
+
+#### Parameters:
+
+Name | Type | Description |
+------ | ------ | ------ |
+`chunk` | Uint8Array | The chunk to push |
+`final?` | boolean | Whether this is the last chunk  |
+
+**Returns:** void

+ 62 - 0
docs/classes/encodeutf8.md

@@ -0,0 +1,62 @@
+# Class: EncodeUTF8
+
+Streaming UTF-8 encoding
+
+## Hierarchy
+
+* **EncodeUTF8**
+
+## Index
+
+### Constructors
+
+* [constructor](encodeutf8.md#constructor)
+
+### Properties
+
+* [ondata](encodeutf8.md#ondata)
+
+### Methods
+
+* [push](encodeutf8.md#push)
+
+## Constructors
+
+### constructor
+
+\+ **new EncodeUTF8**(`handler?`: [FlateStreamHandler](../README.md#flatestreamhandler)): [EncodeUTF8](encodeutf8.md)
+
+Creates a UTF-8 decoding stream
+
+#### Parameters:
+
+Name | Type |
+------ | ------ |
+`handler?` | [FlateStreamHandler](../README.md#flatestreamhandler) |
+
+**Returns:** [EncodeUTF8](encodeutf8.md)
+
+## Properties
+
+### ondata
+
+•  **ondata**: [FlateStreamHandler](../README.md#flatestreamhandler)
+
+The handler to call whenever data is available
+
+## Methods
+
+### push
+
+▸ **push**(`chunk`: string, `final?`: boolean): void
+
+Pushes a chunk to be encoded to UTF-8
+
+#### Parameters:
+
+Name | Type | Description |
+------ | ------ | ------ |
+`chunk` | string | The string data to push |
+`final?` | boolean | Whether this is the last chunk  |
+
+**Returns:** void

+ 80 - 0
docs/classes/zip.md

@@ -0,0 +1,80 @@
+# Class: Zip
+
+A zippable archive to which files can incrementally be added
+
+## Hierarchy
+
+* **Zip**
+
+## Index
+
+### Constructors
+
+* [constructor](zip.md#constructor)
+
+### Properties
+
+* [ondata](zip.md#ondata)
+
+### Methods
+
+* [add](zip.md#add)
+* [end](zip.md#end)
+* [terminate](zip.md#terminate)
+
+## Constructors
+
+### constructor
+
+\+ **new Zip**(): [Zip](zip.md)
+
+Creates an empty ZIP archive to which files can be added
+
+**Returns:** [Zip](zip.md)
+
+## Properties
+
+### ondata
+
+•  **ondata**: [AsyncFlateStreamHandler](../README.md#asyncflatestreamhandler)
+
+The handler to call whenever data is available
+
+## Methods
+
+### add
+
+▸ **add**(`file`: [ZipInputFile](../interfaces/zipinputfile.md)): void
+
+Adds a file to the ZIP archive
+
+#### Parameters:
+
+Name | Type | Description |
+------ | ------ | ------ |
+`file` | [ZipInputFile](../interfaces/zipinputfile.md) | The file stream to add  |
+
+**Returns:** void
+
+___
+
+### end
+
+▸ **end**(): void
+
+Ends the process of adding files and prepares to emit the final chunks.
+This *must* be called after adding all desired files for the resulting
+ZIP file to work properly.
+
+**Returns:** void
+
+___
+
+### terminate
+
+▸ **terminate**(): void
+
+A method to terminate any internal workers used by the stream. Subsequent
+calls to add() will silently fail.
+
+**Returns:** void

+ 147 - 0
docs/classes/zipdeflate.md

@@ -0,0 +1,147 @@
+# Class: ZipDeflate
+
+Streaming DEFLATE compression for ZIP archives. Prefer using AsyncZipDeflate
+for better performance
+
+## Hierarchy
+
+* **ZipDeflate**
+
+## Implements
+
+* [ZipInputFile](../interfaces/zipinputfile.md)
+
+## Index
+
+### Constructors
+
+* [constructor](zipdeflate.md#constructor)
+
+### Properties
+
+* [attrs](zipdeflate.md#attrs)
+* [compression](zipdeflate.md#compression)
+* [crc](zipdeflate.md#crc)
+* [filename](zipdeflate.md#filename)
+* [flag](zipdeflate.md#flag)
+* [ondata](zipdeflate.md#ondata)
+* [os](zipdeflate.md#os)
+* [size](zipdeflate.md#size)
+
+### Methods
+
+* [process](zipdeflate.md#process)
+* [push](zipdeflate.md#push)
+
+## Constructors
+
+### constructor
+
+\+ **new ZipDeflate**(`filename`: string, `opts`: [DeflateOptions](../interfaces/deflateoptions.md)): [ZipDeflate](zipdeflate.md)
+
+Creates a DEFLATE stream that can be added to ZIP archives
+
+#### Parameters:
+
+Name | Type | Default value | Description |
+------ | ------ | ------ | ------ |
+`filename` | string | - | The filename to associate with this data stream |
+`opts` | [DeflateOptions](../interfaces/deflateoptions.md) | {} | The compression options  |
+
+**Returns:** [ZipDeflate](zipdeflate.md)
+
+## Properties
+
+### attrs
+
+• `Optional` **attrs**: number
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[attrs](../interfaces/zipinputfile.md#attrs)*
+
+___
+
+### compression
+
+•  **compression**: number
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[compression](../interfaces/zipinputfile.md#compression)*
+
+___
+
+### crc
+
+•  **crc**: number
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[crc](../interfaces/zipinputfile.md#crc)*
+
+___
+
+### filename
+
+•  **filename**: string
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[filename](../interfaces/zipinputfile.md#filename)*
+
+___
+
+### flag
+
+•  **flag**: 0 \| 1 \| 2 \| 3
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[flag](../interfaces/zipinputfile.md#flag)*
+
+___
+
+### ondata
+
+•  **ondata**: [AsyncFlateStreamHandler](../README.md#asyncflatestreamhandler)
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[ondata](../interfaces/zipinputfile.md#ondata)*
+
+___
+
+### os
+
+• `Optional` **os**: number
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[os](../interfaces/zipinputfile.md#os)*
+
+___
+
+### size
+
+•  **size**: number
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[size](../interfaces/zipinputfile.md#size)*
+
+## Methods
+
+### process
+
+▸ **process**(`chunk`: Uint8Array, `final`: boolean): void
+
+#### Parameters:
+
+Name | Type |
+------ | ------ |
+`chunk` | Uint8Array |
+`final` | boolean |
+
+**Returns:** void
+
+___
+
+### push
+
+▸ **push**(`chunk`: Uint8Array, `final?`: boolean): void
+
+Pushes a chunk to be deflated
+
+#### Parameters:
+
+Name | Type | Description |
+------ | ------ | ------ |
+`chunk` | Uint8Array | The chunk to push |
+`final?` | boolean | Whether this is the last chunk  |
+
+**Returns:** void

+ 113 - 0
docs/classes/zippassthrough.md

@@ -0,0 +1,113 @@
+# Class: ZipPassThrough
+
+A pass-through stream to keep data uncompressed in a ZIP archive.
+
+## Hierarchy
+
+* **ZipPassThrough**
+
+## Implements
+
+* [ZipInputFile](../interfaces/zipinputfile.md)
+
+## Index
+
+### Constructors
+
+* [constructor](zippassthrough.md#constructor)
+
+### Properties
+
+* [attrs](zippassthrough.md#attrs)
+* [crc](zippassthrough.md#crc)
+* [filename](zippassthrough.md#filename)
+* [ondata](zippassthrough.md#ondata)
+* [os](zippassthrough.md#os)
+* [size](zippassthrough.md#size)
+
+### Methods
+
+* [push](zippassthrough.md#push)
+
+## Constructors
+
+### constructor
+
+\+ **new ZipPassThrough**(`filename`: string): [ZipPassThrough](zippassthrough.md)
+
+Creates a pass-through stream that can be added to ZIP archives
+
+#### Parameters:
+
+Name | Type | Description |
+------ | ------ | ------ |
+`filename` | string | The filename to associate with this data stream  |
+
+**Returns:** [ZipPassThrough](zippassthrough.md)
+
+## Properties
+
+### attrs
+
+• `Optional` **attrs**: number
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[attrs](../interfaces/zipinputfile.md#attrs)*
+
+___
+
+### crc
+
+•  **crc**: number
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[crc](../interfaces/zipinputfile.md#crc)*
+
+___
+
+### filename
+
+•  **filename**: string
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[filename](../interfaces/zipinputfile.md#filename)*
+
+___
+
+### ondata
+
+•  **ondata**: [AsyncFlateStreamHandler](../README.md#asyncflatestreamhandler)
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[ondata](../interfaces/zipinputfile.md#ondata)*
+
+___
+
+### os
+
+• `Optional` **os**: number
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[os](../interfaces/zipinputfile.md#os)*
+
+___
+
+### size
+
+•  **size**: number
+
+*Implementation of [ZipInputFile](../interfaces/zipinputfile.md).[size](../interfaces/zipinputfile.md#size)*
+
+## Methods
+
+### push
+
+▸ **push**(`chunk`: Uint8Array, `final?`: boolean): void
+
+Pushes a chunk to be added. If you are subclassing this with a custom
+compression algorithm, note that you must push data from the source
+file only, pre-compression.
+
+#### Parameters:
+
+Name | Type | Description |
+------ | ------ | ------ |
+`chunk` | Uint8Array | The chunk to push |
+`final?` | boolean | Whether this is the last chunk  |
+
+**Returns:** void

+ 1 - 1
docs/interfaces/asyncgzipoptions.md

@@ -89,4 +89,4 @@ ___
 *Inherited from [GzipOptions](gzipoptions.md).[mtime](gzipoptions.md#mtime)*
 *Inherited from [GzipOptions](gzipoptions.md).[mtime](gzipoptions.md#mtime)*
 
 
 When the file was last modified. Defaults to the current time.
 When the file was last modified. Defaults to the current time.
-If you're using GZIP, set this to 0 to avoid revealing a modification date entirely.
+Set this to 0 to avoid revealing a modification date entirely.

+ 52 - 1
docs/interfaces/asynczipoptions.md

@@ -6,7 +6,7 @@ Options for asynchronously creating a ZIP archive
 
 
 * [AsyncDeflateOptions](asyncdeflateoptions.md)
 * [AsyncDeflateOptions](asyncdeflateoptions.md)
 
 
-* {}
+* [ZipAttributes](zipattributes.md)
 
 
   ↳ **AsyncZipOptions**
   ↳ **AsyncZipOptions**
 
 
@@ -14,12 +14,41 @@ Options for asynchronously creating a ZIP archive
 
 
 ### Properties
 ### Properties
 
 
+* [attrs](asynczipoptions.md#attrs)
 * [consume](asynczipoptions.md#consume)
 * [consume](asynczipoptions.md#consume)
 * [level](asynczipoptions.md#level)
 * [level](asynczipoptions.md#level)
 * [mem](asynczipoptions.md#mem)
 * [mem](asynczipoptions.md#mem)
+* [mtime](asynczipoptions.md#mtime)
+* [os](asynczipoptions.md#os)
 
 
 ## Properties
 ## Properties
 
 
+### attrs
+
+• `Optional` **attrs**: number
+
+*Inherited from [ZipAttributes](zipattributes.md).[attrs](zipattributes.md#attrs)*
+
+The file's attributes. These are traditionally somewhat complicated
+and platform-dependent, so using them is scarcely necessary. However,
+here is a representation of what this is, bit by bit:
+
+`TTTTugtrwxrwxrwx0000000000ADVSHR`
+
+T = file type (rarely useful)
+
+u = setuid, g = setgid, t = sticky
+
+rwx = user permissions, rwx = group permissions, rwx = other permissions
+
+0000000000 = unused
+
+A = archive, D = directory, V = volume label, S = system file, H = hidden, R = read-only
+
+If you want to set the Unix permissions, for instance, just bit shift by 16, e.g. 0644 << 16
+
+___
+
 ### consume
 ### consume
 
 
 • `Optional` **consume**: boolean
 • `Optional` **consume**: boolean
@@ -66,3 +95,25 @@ It is recommended not to lower the value below 4, since that tends to hurt perfo
 In addition, values above 8 tend to help very little on most data and can even hurt performance.
 In addition, values above 8 tend to help very little on most data and can even hurt performance.
 
 
 The default value is automatically determined based on the size of the input data.
 The default value is automatically determined based on the size of the input data.
+
+___
+
+### mtime
+
+• `Optional` **mtime**: GzipOptions[\"mtime\"]
+
+*Inherited from [ZipAttributes](zipattributes.md).[mtime](zipattributes.md#mtime)*
+
+When the file was last modified. Defaults to the current time.
+
+___
+
+### os
+
+• `Optional` **os**: number
+
+*Inherited from [ZipAttributes](zipattributes.md).[os](zipattributes.md#os)*
+
+The operating system of origin for this file. The value is defined
+by PKZIP's APPNOTE.txt, section 4.4.2.2. For example, 0 (the default)
+is MS/DOS, 3 is UNIX, 19 is macOS.

+ 6 - 2
docs/interfaces/asynczippable.md

@@ -4,6 +4,10 @@ The complete directory structure of an asynchronously ZIPpable archive
 
 
 ## Hierarchy
 ## Hierarchy
 
 
-* {}
+* **AsyncZippable**
 
 
-  ↳ **AsyncZippable**
+## Indexable
+
+▪ [path: string]: [AsyncZippable](asynczippable.md) \| [AsyncZippableFile](../README.md#asynczippablefile)
+
+The complete directory structure of an asynchronously ZIPpable archive

+ 1 - 1
docs/interfaces/gzipoptions.md

@@ -73,4 +73,4 @@ ___
 • `Optional` **mtime**: Date \| string \| number
 • `Optional` **mtime**: Date \| string \| number
 
 
 When the file was last modified. Defaults to the current time.
 When the file was last modified. Defaults to the current time.
-If you're using GZIP, set this to 0 to avoid revealing a modification date entirely.
+Set this to 0 to avoid revealing a modification date entirely.

+ 7 - 2
docs/interfaces/unzipped.md

@@ -5,6 +5,11 @@ and the file is the value
 
 
 ## Hierarchy
 ## Hierarchy
 
 
-* {}
+* **Unzipped**
 
 
-  ↳ **Unzipped**
+## Indexable
+
+▪ [path: string]: Uint8Array
+
+An unzipped archive. The full path of each file is used as the key,
+and the file is the value

+ 63 - 0
docs/interfaces/zipattributes.md

@@ -0,0 +1,63 @@
+# Interface: ZipAttributes
+
+Attributes for files added to a ZIP archive object
+
+## Hierarchy
+
+* **ZipAttributes**
+
+  ↳ [ZipOptions](zipoptions.md)
+
+  ↳ [AsyncZipOptions](asynczipoptions.md)
+
+  ↳ [ZipInputFile](zipinputfile.md)
+
+## Index
+
+### Properties
+
+* [attrs](zipattributes.md#attrs)
+* [mtime](zipattributes.md#mtime)
+* [os](zipattributes.md#os)
+
+## Properties
+
+### attrs
+
+• `Optional` **attrs**: number
+
+The file's attributes. These are traditionally somewhat complicated
+and platform-dependent, so using them is scarcely necessary. However,
+here is a representation of what this is, bit by bit:
+
+`TTTTugtrwxrwxrwx0000000000ADVSHR`
+
+T = file type (rarely useful)
+
+u = setuid, g = setgid, t = sticky
+
+rwx = user permissions, rwx = group permissions, rwx = other permissions
+
+0000000000 = unused
+
+A = archive, D = directory, V = volume label, S = system file, H = hidden, R = read-only
+
+If you want to set the Unix permissions, for instance, just bit shift by 16, e.g. 0644 << 16
+
+___
+
+### mtime
+
+• `Optional` **mtime**: GzipOptions[\"mtime\"]
+
+When the file was last modified. Defaults to the current time.
+
+___
+
+### os
+
+• `Optional` **os**: number
+
+The operating system of origin for this file. The value is defined
+by PKZIP's APPNOTE.txt, section 4.4.2.2. For example, 0 (the default)
+is MS/DOS, 3 is UNIX, 19 is macOS.

+ 164 - 0
docs/interfaces/zipinputfile.md

@@ -0,0 +1,164 @@
+# Interface: ZipInputFile
+
+A stream that can be used to create a file in a ZIP archive
+
+## Hierarchy
+
+* [ZipAttributes](zipattributes.md)
+
+  ↳ **ZipInputFile**
+
+## Implemented by
+
+* [AsyncZipDeflate](../classes/asynczipdeflate.md)
+* [ZipDeflate](../classes/zipdeflate.md)
+* [ZipPassThrough](../classes/zippassthrough.md)
+
+## Index
+
+### Properties
+
+* [attrs](zipinputfile.md#attrs)
+* [compression](zipinputfile.md#compression)
+* [crc](zipinputfile.md#crc)
+* [filename](zipinputfile.md#filename)
+* [flag](zipinputfile.md#flag)
+* [mtime](zipinputfile.md#mtime)
+* [ondata](zipinputfile.md#ondata)
+* [os](zipinputfile.md#os)
+* [size](zipinputfile.md#size)
+* [terminate](zipinputfile.md#terminate)
+
+## Properties
+
+### attrs
+
+• `Optional` **attrs**: number
+
+*Inherited from [ZipAttributes](zipattributes.md).[attrs](zipattributes.md#attrs)*
+
+The file's attributes. These are traditionally somewhat complicated
+and platform-dependent, so using them is scarcely necessary. However,
+here is a representation of what this is, bit by bit:
+
+`TTTTugtrwxrwxrwx0000000000ADVSHR`
+
+T = file type (rarely useful)
+
+u = setuid, g = setgid, t = sticky
+
+rwx = user permissions, rwx = group permissions, rwx = other permissions
+
+0000000000 = unused
+
+A = archive, D = directory, V = volume label, S = system file, H = hidden, R = read-only
+
+If you want to set the Unix permissions, for instance, just bit shift by 16, e.g. 0644 << 16
+
+___
+
+### compression
+
+• `Optional` **compression**: number
+
+The compression format for the data stream. This number is determined by
+the spec in PKZIP's APPNOTE.txt, section 4.4.5. For example, 0 = no
+compression, 8 = deflate, 14 = LZMA
+
+___
+
+### crc
+
+•  **crc**: number
+
+A CRC of the original file contents. This attribute may be invalid after
+the file is added to the ZIP archive; it must be correct only before the
+stream completes.
+
+If you don't want to have to generate this yourself, consider extending the
+ZipPassThrough class and overriding its process() method, or using one of
+ZipDeflate or AsyncZipDeflate
+
+___
+
+### filename
+
+•  **filename**: string
+
+The filename to associate with the data provided to this stream. If you
+want a file in a subdirectory, use forward slashes as a separator (e.g.
+`directory/filename.ext`). This will still work on Windows.
+
+___
+
+### flag
+
+• `Optional` **flag**: 0 \| 1 \| 2 \| 3
+
+Bits 1 and 2 of the general purpose bit flag, specified in PKZIP's
+APPNOTE.txt, section 4.4.4. This is unlikely to be necessary.
+
+___
+
+### mtime
+
+• `Optional` **mtime**: GzipOptions[\"mtime\"]
+
+*Inherited from [ZipAttributes](zipattributes.md).[mtime](zipattributes.md#mtime)*
+
+When the file was last modified. Defaults to the current time.
+
+___
+
+### ondata
+
+• `Optional` **ondata**: [AsyncFlateStreamHandler](../README.md#asyncflatestreamhandler)
+
+The handler to be called when data is added. After passing this stream to
+the ZIP file object, this handler will always be defined. To call it:
+
+`stream.ondata(error, chunk, final)`
+
+error = any error that occurred (null if there was no error)
+
+chunk = a Uint8Array of the data that was added (null if there was an
+error)
+
+final = boolean, whether this is the final chunk in the stream
+
+___
+
+### os
+
+• `Optional` **os**: number
+
+*Inherited from [ZipAttributes](zipattributes.md).[os](zipattributes.md#os)*
+
+The operating system of origin for this file. The value is defined
+by PKZIP's APPNOTE.txt, section 4.4.2.2. For example, 0 (the default)
+is MS/DOS, 3 is UNIX, 19 is macOS.
+
+___
+
+### size
+
+•  **size**: number
+
+The size of the file in bytes. This attribute may be invalid after
+the file is added to the ZIP archive; it must be correct only before the
+stream completes.
+
+If you don't want to have to compute this yourself, consider extending the
+ZipPassThrough class and overriding its process() method, or using one of
+ZipDeflate or AsyncZipDeflate
+
+___
+
+### terminate
+
+• `Optional` **terminate**: [AsyncTerminable](asyncterminable.md)
+
+A method called when the stream is no longer needed, for clean-up
+purposes. This will not always be called after the stream completes,
+so, you may wish to call this.terminate() after the final chunk is
+processed if you have clean-up logic.

+ 52 - 1
docs/interfaces/zipoptions.md

@@ -6,7 +6,7 @@ Options for creating a ZIP archive
 
 
 * [DeflateOptions](deflateoptions.md)
 * [DeflateOptions](deflateoptions.md)
 
 
-* {}
+* [ZipAttributes](zipattributes.md)
 
 
   ↳ **ZipOptions**
   ↳ **ZipOptions**
 
 
@@ -14,11 +14,40 @@ Options for creating a ZIP archive
 
 
 ### Properties
 ### Properties
 
 
+* [attrs](zipoptions.md#attrs)
 * [level](zipoptions.md#level)
 * [level](zipoptions.md#level)
 * [mem](zipoptions.md#mem)
 * [mem](zipoptions.md#mem)
+* [mtime](zipoptions.md#mtime)
+* [os](zipoptions.md#os)
 
 
 ## Properties
 ## Properties
 
 
+### attrs
+
+• `Optional` **attrs**: number
+
+*Inherited from [ZipAttributes](zipattributes.md).[attrs](zipattributes.md#attrs)*
+
+The file's attributes. These are traditionally somewhat complicated
+and platform-dependent, so using them is scarcely necessary. However,
+here is a representation of what this is, bit by bit:
+
+`TTTTugtrwxrwxrwx0000000000ADVSHR`
+
+T = file type (rarely useful)
+
+u = setuid, g = setgid, t = sticky
+
+rwx = user permissions, rwx = group permissions, rwx = other permissions
+
+0000000000 = unused
+
+A = archive, D = directory, V = volume label, S = system file, H = hidden, R = read-only
+
+If you want to set the Unix permissions, for instance, just bit shift by 16, e.g. 0644 << 16
+
+___
+
 ### level
 ### level
 
 
 • `Optional` **level**: 0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9
 • `Optional` **level**: 0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9
@@ -54,3 +83,25 @@ It is recommended not to lower the value below 4, since that tends to hurt perfo
 In addition, values above 8 tend to help very little on most data and can even hurt performance.
 In addition, values above 8 tend to help very little on most data and can even hurt performance.
 
 
 The default value is automatically determined based on the size of the input data.
 The default value is automatically determined based on the size of the input data.
+
+___
+
+### mtime
+
+• `Optional` **mtime**: GzipOptions[\"mtime\"]
+
+*Inherited from [ZipAttributes](zipattributes.md).[mtime](zipattributes.md#mtime)*
+
+When the file was last modified. Defaults to the current time.
+
+___
+
+### os
+
+• `Optional` **os**: number
+
+*Inherited from [ZipAttributes](zipattributes.md).[os](zipattributes.md#os)*
+
+The operating system of origin for this file. The value is defined
+by PKZIP's APPNOTE.txt, section 4.4.2.2. For example, 0 (the default)
+is MS/DOS, 3 is UNIX, 19 is macOS.

+ 6 - 2
docs/interfaces/zippable.md

@@ -4,6 +4,10 @@ The complete directory structure of a ZIPpable archive
 
 
 ## Hierarchy
 ## Hierarchy
 
 
-* {}
+* **Zippable**
 
 
-  ↳ **Zippable**
+## Indexable
+
+▪ [path: string]: [Zippable](zippable.md) \| [ZippableFile](../README.md#zippablefile)
+
+The complete directory structure of a ZIPpable archive

+ 10 - 0
package.json

@@ -11,6 +11,13 @@
     "./lib/node-worker.js": "./lib/worker.js",
     "./lib/node-worker.js": "./lib/worker.js",
     "./esm/index.mjs": "./esm/browser.js"
     "./esm/index.mjs": "./esm/browser.js"
   },
   },
+  "exports": {
+    ".": {
+      "node": "./esm/index.mjs",
+      "require": "./lib/index.js",
+      "default": "./esm/browser.js"
+    }
+  },
   "targets": {
   "targets": {
     "main": false,
     "main": false,
     "module": false,
     "module": false,
@@ -82,5 +89,8 @@
     "react": "preact/compat",
     "react": "preact/compat",
     "react-dom": "preact/compat",
     "react-dom": "preact/compat",
     "react-dom/test-utils": "preact/test-utils"
     "react-dom/test-utils": "preact/test-utils"
+  },
+  "dependencies": {
+    "@msgpack/msgpack": "^2.3.0"
   }
   }
 }
 }

+ 608 - 89
src/index.ts

@@ -544,7 +544,7 @@ const et = /*#__PURE__*/new u8(0);
 // compresses data into a raw DEFLATE buffer
 // compresses data into a raw DEFLATE buffer
 const dflt = (dat: Uint8Array, lvl: number, plvl: number, pre: number, post: number, lst: 0 | 1) => {
 const dflt = (dat: Uint8Array, lvl: number, plvl: number, pre: number, post: number, lst: 0 | 1) => {
   const s = dat.length;
   const s = dat.length;
-  const o = new u8(pre + s + 5 * (1 + Math.floor(s / 7000)) + post);
+  const o = new u8(pre + s + 5 * (1 + Math.ceil(s / 7000)) + post);
   // writing to this writes to the output buffer
   // writing to this writes to the output buffer
   const w = o.subarray(pre, o.length - post);
   const w = o.subarray(pre, o.length - post);
   let pos = 0;
   let pos = 0;
@@ -649,7 +649,7 @@ const dflt = (dat: Uint8Array, lvl: number, plvl: number, pre: number, post: num
     }
     }
     pos = wblk(dat, w, lst, syms, lf, df, eb, li, bs, i - bs, pos);
     pos = wblk(dat, w, lst, syms, lf, df, eb, li, bs, i - bs, pos);
     // this is the easiest way to avoid needing to maintain state
     // this is the easiest way to avoid needing to maintain state
-    if (!lst) pos = wfblk(w, pos, et);
+    if (!lst && pos & 7) pos = wfblk(w, pos + 1, et);
   }
   }
   return slc(o, 0, pre + shft(pos) + post);
   return slc(o, 0, pre + shft(pos) + post);
 }
 }
@@ -742,7 +742,7 @@ export interface DeflateOptions {
 export interface GzipOptions extends DeflateOptions {
 export interface GzipOptions extends DeflateOptions {
   /**
   /**
    * When the file was last modified. Defaults to the current time.
    * When the file was last modified. Defaults to the current time.
-   * If you're using GZIP, set this to 0 to avoid revealing a modification date entirely.
+   * Set this to 0 to avoid revealing a modification date entirely.
    */
    */
   mtime?: Date | string | number;
   mtime?: Date | string | number;
   /**
   /**
@@ -840,11 +840,11 @@ const dopt = (dat: Uint8Array, opt: DeflateOptions, pre: number, post: number, s
   dflt(dat, opt.level == null ? 6 : opt.level, opt.mem == null ? Math.ceil(Math.max(8, Math.min(13, Math.log(dat.length))) * 1.5) : (12 + opt.mem), pre, post, !st as unknown as 0 | 1);
   dflt(dat, opt.level == null ? 6 : opt.level, opt.mem == null ? Math.ceil(Math.max(8, Math.min(13, Math.log(dat.length))) * 1.5) : (12 + opt.mem), pre, post, !st as unknown as 0 | 1);
 
 
 // Walmart object spread
 // Walmart object spread
-const mrg = <T extends {}>(a: T, b: T) => {
-  const o = {} as T;
+const mrg = <A, B>(a: A, b: B) => {
+  const o = {} as Record<string, unknown>;
   for (const k in a) o[k] = a[k];
   for (const k in a) o[k] = a[k];
   for (const k in b) o[k] = b[k];
   for (const k in b) o[k] = b[k];
-  return o;
+  return o as A & B;
 }
 }
 
 
 // worker clone
 // worker clone
@@ -1886,15 +1886,53 @@ export function decompressSync(data: Uint8Array, out?: Uint8Array) {
       : unzlibSync(data, out);
       : unzlibSync(data, out);
 }
 }
 
 
+/**
+ * Attributes for files added to a ZIP archive object
+ */
+export interface ZipAttributes {
+  /**
+   * The operating system of origin for this file. The value is defined
+   * by PKZIP's APPNOTE.txt, section 4.4.2.2. For example, 0 (the default)
+   * is MS/DOS, 3 is UNIX, 19 is macOS.
+   */
+  os?: number;
+
+  /**
+   * The file's attributes. These are traditionally somewhat complicated
+   * and platform-dependent, so using them is scarcely necessary. However,
+   * here is a representation of what this is, bit by bit:
+   * 
+   * `TTTTugtrwxrwxrwx0000000000ADVSHR`
+   * 
+   * T = file type (rarely useful)
+   * 
+   * u = setuid, g = setgid, t = sticky
+   * 
+   * rwx = user permissions, rwx = group permissions, rwx = other permissions
+   * 
+   * 0000000000 = unused
+   * 
+   * A = archive, D = directory, V = volume label, S = system file, H = hidden, R = read-only
+   * 
+   * If you want to set the Unix permissions, for instance, just bit shift by 16, e.g. 0644 << 16
+   */
+  attrs?: number;
+
+  /**
+   * When the file was last modified. Defaults to the current time.
+   */
+  mtime?: GzipOptions['mtime'];
+}
+
 /**
 /**
  * Options for creating a ZIP archive
  * Options for creating a ZIP archive
  */
  */
-export interface ZipOptions extends DeflateOptions, Pick<GzipOptions, 'mtime'> {}
+export interface ZipOptions extends DeflateOptions, ZipAttributes {}
 
 
 /**
 /**
  * Options for asynchronously creating a ZIP archive
  * Options for asynchronously creating a ZIP archive
  */
  */
-export interface AsyncZipOptions extends AsyncDeflateOptions, Pick<AsyncGzipOptions, 'mtime'> {}
+export interface AsyncZipOptions extends AsyncDeflateOptions, ZipAttributes {}
 
 
 /**
 /**
  * Options for asynchronously expanding a ZIP archive
  * Options for asynchronously expanding a ZIP archive
@@ -1914,18 +1952,31 @@ export type AsyncZippableFile = Uint8Array | [Uint8Array, AsyncZipOptions];
 /**
 /**
  * The complete directory structure of a ZIPpable archive
  * The complete directory structure of a ZIPpable archive
  */
  */
-export interface Zippable extends Record<string, Zippable | ZippableFile> {}
+export interface Zippable {
+  [path: string]: Zippable | ZippableFile;
+}
 
 
 /**
 /**
  * The complete directory structure of an asynchronously ZIPpable archive
  * The complete directory structure of an asynchronously ZIPpable archive
  */
  */
-export interface AsyncZippable extends Record<string, AsyncZippable | AsyncZippableFile> {}
+export interface AsyncZippable {
+  [path: string]: AsyncZippable | AsyncZippableFile;
+}
 
 
 /**
 /**
  * An unzipped archive. The full path of each file is used as the key,
  * An unzipped archive. The full path of each file is used as the key,
  * and the file is the value
  * and the file is the value
  */
  */
-export interface Unzipped extends Record<string, Uint8Array> {}
+export interface Unzipped {
+  [path: string]: Uint8Array
+}
+
+/**
+ * Handler for string generation streams
+ * @param data The string output from the stream processor
+ * @param final Whether this is the final block
+ */
+export type StringStreamHandler = (data: string, final: boolean) => void;
 
 
 /**
 /**
  * Callback for asynchronous ZIP decompression
  * Callback for asynchronous ZIP decompression
@@ -1934,6 +1985,13 @@ export interface Unzipped extends Record<string, Uint8Array> {}
  */
  */
 export type UnzipCallback = (err: Error | string, data: Unzipped) => void;
 export type UnzipCallback = (err: Error | string, data: Unzipped) => void;
 
 
+/**
+ * Callback for ZIP compression progress
+ * @param err Any error that occurred
+ * @param data The decompressed ZIP archive
+ */
+export type ZipProgressHandler = (bytesRead: number, bytesOut: number) => void;
+
 // flattened Zippable
 // flattened Zippable
 type FlatZippable<A extends boolean> = Record<string, [Uint8Array, (A extends true ? AsyncZipOptions : ZipOptions)]>;
 type FlatZippable<A extends boolean> = Record<string, [Uint8Array, (A extends true ? AsyncZipOptions : ZipOptions)]>;
 
 
@@ -1947,6 +2005,102 @@ const fltn = <A extends boolean>(d: A extends true ? AsyncZippable : Zippable, p
   }
   }
 }
 }
 
 
+// text encoder
+const te = typeof TextEncoder != 'undefined' && new TextEncoder();
+// text decoder
+const td = typeof TextDecoder != 'undefined' && new TextDecoder();
+// text decoder stream
+let tds = 0;
+try {
+  td.decode(et, { stream: true });
+  tds = 1;
+} catch(e) {}
+
+// decode UTF8
+const dutf8 = (d: Uint8Array) => {
+  for (let r = '', i = 0;;) {
+    let c = d[i++];
+    const eb = ((c > 127) as unknown as number) + ((c > 223) as unknown as number) + ((c > 239) as unknown as number);
+    if (i + eb > d.length) return [r, d.slice(i - 1)] as const;
+    if (!eb) r += String.fromCharCode(c)
+    else if (eb == 3) {
+      c = ((c & 15) << 18 | (d[i++] & 63) << 12 | (d[i++] & 63) << 6 | (d[i++] & 63)) - 65536,
+      r += String.fromCharCode(55296 | (c >> 10), 56320 | (c & 1023));
+    } else if (eb & 1) r += String.fromCharCode((c & 31) << 6 | (d[i++] & 63));
+    else r += String.fromCharCode((c & 15) << 12 | (d[i++] & 63) << 6 | (d[i++] & 63));
+  }
+}
+
+/**
+ * Streaming UTF-8 decoding
+ */
+export class DecodeUTF8 {
+  private p: Uint8Array;
+  private t: TextDecoder;
+  /**
+   * Creates a UTF-8 decoding stream
+   * @param opts The compression options
+   * @param cb The callback to call whenever data is deflated
+   */
+  constructor(handler?: StringStreamHandler) {
+    this.ondata = handler;
+    if (tds) this.t = new TextDecoder();
+    else this.p = et;
+  }
+
+  /**
+   * Pushes a chunk to be decoded from UTF-8 binary
+   * @param chunk The chunk to push
+   * @param final Whether this is the last chunk
+   */
+  push(chunk: Uint8Array, final?: boolean) {
+    if (!this.ondata) throw 'no callback';
+    if (!final) final = false;
+    if (this.t) return this.ondata(this.t.decode(chunk, { stream: !final }), final);
+    const dat = new u8(this.p.length + chunk.length);
+    dat.set(this.p);
+    dat.set(chunk, this.p.length);
+    const [ch, np] = dutf8(dat);
+    if (final && np.length) throw 'invalid utf-8 data';
+    this.p = np;
+    this.ondata(ch, final);
+  }
+
+  /**
+   * The handler to call whenever data is available
+   */
+  ondata: StringStreamHandler;
+}
+
+/**
+ * Streaming UTF-8 encoding
+ */
+export class EncodeUTF8 {
+  /**
+   * Creates a UTF-8 decoding stream
+   * @param opts The compression options
+   * @param cb The callback to call whenever data is deflated
+   */
+  constructor(handler?: FlateStreamHandler) {
+    this.ondata = handler;
+  }
+
+  /**
+   * Pushes a chunk to be encoded to UTF-8
+   * @param chunk The string data to push
+   * @param final Whether this is the last chunk
+   */
+  push(chunk: string, final?: boolean) {
+    if (!this.ondata) throw 'no callback';
+    this.ondata(strToU8(chunk), final || false);
+  }
+
+  /**
+   * The handler to call whenever data is available
+   */
+  ondata: FlateStreamHandler;
+}
+
 /**
 /**
  * Converts a string into a Uint8Array for use with compression/decompression methods
  * Converts a string into a Uint8Array for use with compression/decompression methods
  * @param str The string to encode
  * @param str The string to encode
@@ -1955,9 +2109,14 @@ const fltn = <A extends boolean>(d: A extends true ? AsyncZippable : Zippable, p
  * @returns The string encoded in UTF-8/Latin-1 binary
  * @returns The string encoded in UTF-8/Latin-1 binary
  */
  */
 export function strToU8(str: string, latin1?: boolean): Uint8Array {
 export function strToU8(str: string, latin1?: boolean): Uint8Array {
+  if (latin1) {
+    const ar = new u8(str.length);
+    for (let i = 0; i < str.length; ++i) ar[i] = str.charCodeAt(i);
+    return ar;
+  }
+  if (te) return te.encode(str);
   const l = str.length;
   const l = str.length;
-  if (!latin1 && typeof TextEncoder != 'undefined') return new TextEncoder().encode(str);
-  let ar = new u8(str.length + (str.length >>> 1));
+  let ar = new u8(str.length + (str.length >> 1));
   let ai = 0;
   let ai = 0;
   const w = (v: number) => { ar[ai++] = v; };
   const w = (v: number) => { ar[ai++] = v; };
   for (let i = 0; i < l; ++i) {
   for (let i = 0; i < l; ++i) {
@@ -1985,20 +2144,22 @@ export function strToU8(str: string, latin1?: boolean): Uint8Array {
  * @returns The original UTF-8/Latin-1 string
  * @returns The original UTF-8/Latin-1 string
  */
  */
 export function strFromU8(dat: Uint8Array, latin1?: boolean) {
 export function strFromU8(dat: Uint8Array, latin1?: boolean) {
-  let r = '';
-  if (!latin1 && typeof TextDecoder != 'undefined') return new TextDecoder().decode(dat);
-  for (let i = 0; i < dat.length;) {
-    let c = dat[i++];
-    if (c < 128 || latin1) r += String.fromCharCode(c);
-    else if (c < 224) r += String.fromCharCode((c & 31) << 6 | (dat[i++] & 63));
-    else if (c < 240) r += String.fromCharCode((c & 15) << 12 | (dat[i++] & 63) << 6 | (dat[i++] & 63));
-    else
-      c = ((c & 15) << 18 | (dat[i++] & 63) << 12 | (dat[i++] & 63) << 6 | (dat[i++] & 63)) - 65536,
-      r += String.fromCharCode(55296 | (c >> 10), 56320 | (c & 1023));
-  }
-  return r;
+  if (latin1) {
+    let r = '';
+    for (let i = 0; i < dat.length; i += 16384)
+      r += String.fromCharCode.apply(null, dat.subarray(i, i + 16384));
+    return r;
+  } else if (td) return td.decode(dat)
+  else {
+    const [out, ext] = dutf8(dat);
+    if (ext.length) throw 'invalid utf-8 data';
+    return out;
+  } 
 };
 };
 
 
+// deflate bit flag
+const dbf = (l: number) => l == 1 ? 3 : l < 6 ? 2 : l == 9 ? 1 : 0;
+
 // skip local zip header
 // skip local zip header
 const slzh = (d: Uint8Array, b: number) => b + 30 + b2(d, b + 26) + b2(d, b + 28);
 const slzh = (d: Uint8Array, b: number) => b + 30 + b2(d, b + 26) + b2(d, b + 28);
 
 
@@ -2015,26 +2176,43 @@ const z64e = (d: Uint8Array, b: number) => {
   return [b4(d, b + 12), b4(d, b + 4), b4(d, b + 20)] as const;
   return [b4(d, b + 12), b4(d, b + 4), b4(d, b + 20)] as const;
 }
 }
 
 
+// zip header file
+type ZHF = Omit<ZipInputFile, 'terminate' | 'ondata' | 'filename'>;
+
+
 // write zip header
 // write zip header
-const wzh = (d: Uint8Array, b: number, c: number, cmp: Uint8Array, su: number, fn: Uint8Array, u: boolean, o: ZipOptions, ce: number | null, t: number) => {
-  const fl = fn.length, l = cmp.length;
+const wzh = (d: Uint8Array, b: number, f: ZHF, fn: Uint8Array, u: boolean, c?: number, ce?: number) => {
+  const fl = fn.length;
   wbytes(d, b, ce != null ? 0x2014B50 : 0x4034B50), b += 4;
   wbytes(d, b, ce != null ? 0x2014B50 : 0x4034B50), b += 4;
-  if (ce != null) d[b] = 20, b += 2;
+  if (ce != null) d[b++] = 20, d[b++] = f.os;
   d[b] = 20, b += 2; // spec compliance? what's that?
   d[b] = 20, b += 2; // spec compliance? what's that?
-  d[b++] = (t == 8 && (o.level == 1 ? 6 : o.level < 6 ? 4 : o.level == 9 ? 2 : 0)), d[b++] = u && 8;
-  d[b] = t, b += 2;
-  const dt = new Date(o.mtime || Date.now()), y = dt.getFullYear() - 1980;
+  d[b++] = (f.flag << 1) | (c == null && 8), d[b++] = u && 8;
+  d[b++] = f.compression & 255, d[b++] = f.compression >> 8;
+  const dt = new Date(f.mtime == null ? Date.now() : f.mtime), y = dt.getFullYear() - 1980;
   if (y < 0 || y > 119) throw 'date not in range 1980-2099';
   if (y < 0 || y > 119) throw 'date not in range 1980-2099';
-  wbytes(d, b, ((y << 24) * 2) | ((dt.getMonth() + 1) << 21) | (dt.getDate() << 16) | (dt.getHours() << 11) | (dt.getMinutes() << 5) | (dt.getSeconds() >>> 1));
-  b += 4;
-  wbytes(d, b, c);
-  wbytes(d, b + 4, l);
-  wbytes(d, b + 8, su);
-  wbytes(d, b + 12, fl), b += 16; // skip extra field, comment
-  if (ce != null) wbytes(d, b += 10, ce), b += 4;
+  wbytes(d, b, ((y << 24) * 2) | ((dt.getMonth() + 1) << 21) | (dt.getDate() << 16) | (dt.getHours() << 11) | (dt.getMinutes() << 5) | (dt.getSeconds() >>> 1)), b += 4;
+  if (c != null) {
+    wbytes(d, b, f.crc);
+    wbytes(d, b + 4, c);
+    wbytes(d, b + 8, f.size);
+  }
+  wbytes(d, b + 12, fl), b += 16;
+  if (ce != null) {
+    wbytes(d, b + 6, f.attrs);
+    wbytes(d, b + 10, ce), b += 14;
+  }
   d.set(fn, b);
   d.set(fn, b);
-  b += fl;
-  if (ce == null) d.set(cmp, b);
+  return b + fl;
+}
+
+// create zip data descriptor
+const czdd = (f: Pick<ZipInputFile, 'size' | 'crc'>, c: number) => {
+  const d = new u8(16);
+  wbytes(d, 0, 0x8074B50)
+  wbytes(d, 4, f.crc);
+  wbytes(d, 8, c);
+  wbytes(d, 12, f.size);
+  return d;
 }
 }
 
 
 // write zip footer (end of central directory)
 // write zip footer (end of central directory)
@@ -2046,30 +2224,367 @@ const wzf = (o: Uint8Array, b: number, c: number, d: number, e: number) => {
   wbytes(o, b + 16, e);
   wbytes(o, b + 16, e);
 }
 }
 
 
-// internal zip data
-type AsyncZipDat = {
+/**
+ * A stream that can be used to create a file in a ZIP archive
+ */
+export interface ZipInputFile extends ZipAttributes {
+  /**
+   * The filename to associate with the data provided to this stream. If you
+   * want a file in a subdirectory, use forward slashes as a separator (e.g.
+   * `directory/filename.ext`). This will still work on Windows.
+   */
+  filename: string;
+
+  /**
+   * The size of the file in bytes. This attribute may be invalid after
+   * the file is added to the ZIP archive; it must be correct only before the
+   * stream completes.
+   * 
+   * If you don't want to have to compute this yourself, consider extending the
+   * ZipPassThrough class and overriding its process() method, or using one of
+   * ZipDeflate or AsyncZipDeflate
+   */
+  size: number;
+
+  /**
+   * A CRC of the original file contents. This attribute may be invalid after
+   * the file is added to the ZIP archive; it must be correct only before the
+   * stream completes.
+   * 
+   * If you don't want to have to generate this yourself, consider extending the
+   * ZipPassThrough class and overriding its process() method, or using one of
+   * ZipDeflate or AsyncZipDeflate
+   */
+  crc: number;
+
+  /**
+   * The compression format for the data stream. This number is determined by
+   * the spec in PKZIP's APPNOTE.txt, section 4.4.5. For example, 0 = no
+   * compression, 8 = deflate, 14 = LZMA
+   */
+  compression?: number;
+
+  /**
+   * Bits 1 and 2 of the general purpose bit flag, specified in PKZIP's
+   * APPNOTE.txt, section 4.4.4. This is unlikely to be necessary.
+   */
+  flag?: 0 | 1 | 2 | 3;
+
+  /**
+   * The handler to be called when data is added. After passing this stream to
+   * the ZIP file object, this handler will always be defined. To call it:
+   * 
+   * `stream.ondata(error, chunk, final)`
+   * 
+   * error = any error that occurred (null if there was no error)
+   * 
+   * chunk = a Uint8Array of the data that was added (null if there was an
+   * error)
+   * 
+   * final = boolean, whether this is the final chunk in the stream
+   */
+  ondata?: AsyncFlateStreamHandler;
+  
+  /**
+   * A method called when the stream is no longer needed, for clean-up
+   * purposes. This will not always be called after the stream completes,
+   * so, you may wish to call this.terminate() after the final chunk is
+   * processed if you have clean-up logic.
+   */
+  terminate?: AsyncTerminable;
+}
+
+type AsyncZipDat = ZHF & {
   // compressed data
   // compressed data
-  d: Uint8Array;
-  // uncompressed length
-  m: number;
-  // type (0 = uncompressed, 8 = DEFLATE)
-  t: number;
-  // filename as Uint8Array
-  n: Uint8Array;
-  // Unicode filename
+  c: Uint8Array;
+  // filename
+  f: Uint8Array;
+  // unicode
   u: boolean;
   u: boolean;
-  // CRC32
-  c: number;
-  // zip options
-  p: ZipOptions;
 };
 };
 
 
 type ZipDat = AsyncZipDat & {
 type ZipDat = AsyncZipDat & {
-  // total offset
+  // offset
   o: number;
   o: number;
 }
 }
 
 
-// TODO: Support streams as ZIP input
+/**
+ * A pass-through stream to keep data uncompressed in a ZIP archive.
+ */
+export class ZipPassThrough implements ZipInputFile {
+  filename: string;
+  crc: number;
+  size: number;
+  os?: number;
+  attrs?: number;
+  ondata: AsyncFlateStreamHandler;
+  private c: CRCV;
+
+  /**
+   * Creates a pass-through stream that can be added to ZIP archives
+   * @param filename The filename to associate with this data stream
+   */
+  constructor(filename: string) {
+    this.filename = filename;
+    this.c = crc();
+    this.size = 0;
+  }
+
+  /**
+   * Processes a chunk and pushes to the output stream. You can override this
+   * method in a subclass for custom behavior, but by default this passes
+   * the data through. You must call this.ondata(err, chunk, final) at some
+   * point in this method.
+   * @param chunk The chunk to process
+   * @param final Whether this is the last chunk
+   */
+  protected process(chunk: Uint8Array, final: boolean) {
+    this.ondata(null, chunk, final);
+  }
+
+  /**
+   * Pushes a chunk to be added. If you are subclassing this with a custom
+   * compression algorithm, note that you must push data from the source
+   * file only, pre-compression.
+   * @param chunk The chunk to push
+   * @param final Whether this is the last chunk
+   */
+  push(chunk: Uint8Array, final?: boolean) {
+    if (!(this as ZipInputFile).ondata) throw 'no callback - add to ZIP archive before pushing';
+    this.c.p(chunk);
+    this.size += chunk.length;
+    if (final) this.crc = this.c.d();
+    this.process(chunk, final || false);
+  }
+}
+
+// I don't extend because TypeScript extension adds 1kB of runtime bloat
+
+/**
+ * Streaming DEFLATE compression for ZIP archives. Prefer using AsyncZipDeflate
+ * for better performance
+ */
+export class ZipDeflate implements ZipInputFile {
+  filename: string;
+  crc: number;
+  size: number;
+  compression: number;
+  flag: 0 | 1 | 2 | 3;
+  os?: number;
+  attrs?: number;
+  ondata: AsyncFlateStreamHandler;
+  private d: Deflate;
+
+  /**
+   * Creates a DEFLATE stream that can be added to ZIP archives
+   * @param filename The filename to associate with this data stream
+   * @param opts The compression options
+   */
+  constructor(filename: string, opts: DeflateOptions = {}) {
+    ZipPassThrough.call(this, filename);
+    this.d = new Deflate(opts, (dat, final) => {
+      this.ondata(null, dat, final);
+    });
+    this.compression = 8;
+    this.flag = dbf(opts.level);
+  }
+  
+  process(chunk: Uint8Array, final: boolean) {
+    try {
+      this.d.push(chunk, final);
+    } catch(e) {
+      this.ondata(e, null, final);
+    }
+  }
+
+  /**
+   * Pushes a chunk to be deflated
+   * @param chunk The chunk to push
+   * @param final Whether this is the last chunk
+   */
+  push(chunk: Uint8Array, final?: boolean) {
+    ZipPassThrough.prototype.push.call(this, chunk, final);
+  }
+}
+
+/**
+ * Asynchronous streaming DEFLATE compression for ZIP archives
+ */
+export class AsyncZipDeflate implements ZipInputFile {
+  filename: string;
+  crc: number;
+  size: number;
+  compression: number;
+  flag: 0 | 1 | 2 | 3;
+  os?: number;
+  attrs?: number;
+  ondata: AsyncFlateStreamHandler;
+  private d: AsyncDeflate;
+  terminate: AsyncTerminable;
+
+  /**
+   * Creates a DEFLATE stream that can be added to ZIP archives
+   * @param filename The filename to associate with this data stream
+   * @param opts The compression options
+   */
+  constructor(filename: string, opts: DeflateOptions = {}) {
+    ZipPassThrough.call(this, filename);
+    this.d = new AsyncDeflate(opts, (err, dat, final) => {
+      this.ondata(err, dat, final);
+    });
+    this.compression = 8;
+    this.flag = dbf(opts.level);
+    this.terminate = this.d.terminate;
+  }
+  
+  process(chunk: Uint8Array, final: boolean) {
+    this.d.push(chunk, final);
+  }
+
+  /**
+   * Pushes a chunk to be deflated
+   * @param chunk The chunk to push
+   * @param final Whether this is the last chunk
+   */
+  push(chunk: Uint8Array, final?: boolean) {
+    ZipPassThrough.prototype.push.call(this, chunk, final);
+  }
+}
+
+type ZIFE = {
+  // compressed size
+  c: number;
+  // filename
+  f: Uint8Array;
+  // unicode
+  u: boolean;
+  // byte offset
+  b: number;
+  // header offset
+  h: number;
+  // terminator
+  t: () => void;
+  // turn
+  r: () => void;
+};
+
+type ZipInternalFile = ZHF & ZIFE;
+
+/**
+ * A zippable archive to which files can incrementally be added
+ */
+export class Zip {
+  private u: ZipInternalFile[];
+  private d: number;
+  /**
+   * Creates an empty ZIP archive to which files can be added
+   */
+  constructor() {
+    this.u = [];
+    this.d = 1;
+  }
+  /**
+   * Adds a file to the ZIP archive
+   * @param file The file stream to add
+   */
+  add(file: ZipInputFile) {
+    if (this.d & 2) throw 'stream finished';
+    const f = strToU8(file.filename), fl = f.length, u = fl != file.filename.length, hl = fl + 30;
+    if (fl > 65535) throw 'filename too long';
+    const header = new u8(hl);
+    wzh(header, 0, file, f, u);
+    let chks: Uint8Array[] = [header];
+    const pAll = () => {
+      for (const chk of chks) this.ondata(null, chk, false);
+      chks = [];
+    };
+    let tr = this.d;
+    this.d = 0;
+    const ind = this.u.length;
+    const uf = mrg(file, {
+      f,
+      u,
+      t: () => { 
+        if (file.terminate) file.terminate();
+      },
+      r: () => {
+        pAll();
+        if (tr) {
+          const nxt = this.u[ind + 1];
+          if (nxt) nxt.r();
+          else this.d = 1;
+        }
+        tr = 1;
+      }
+    } as ZIFE);
+    let cl = 0;
+    file.ondata = (err, dat, final) => {
+      if (err) {
+        this.ondata(err, dat, final);
+        this.terminate();
+      } else {
+        cl += dat.length;
+        chks.push(dat);
+        if (final) {
+          chks.push(czdd(file, cl));
+          uf.c = cl, uf.b = hl + cl + 16, uf.crc = file.crc, uf.size = file.size;
+          if (tr) uf.r();
+          tr = 1;
+        } else if (tr) pAll();
+      }
+    }
+    this.u.push(uf);
+  }
+
+  /**
+   * Ends the process of adding files and prepares to emit the final chunks.
+   * This *must* be called after adding all desired files for the resulting
+   * ZIP file to work properly.
+   */
+  end() {
+    if (this.d & 2) {
+      if (this.d & 1) throw 'stream finishing';
+      throw 'stream finished';
+    }
+    if (this.d) this.e();
+    else this.u.push({
+      r: () => {
+        if (!(this.d & 1)) return;
+        this.u.splice(-1, 1);
+        this.e();
+      },
+      t: () => {}
+    } as unknown as ZipInternalFile);
+    this.d = 3;
+  }
+
+  private e() {
+    let bt = 0, l = 0, tl = 0;
+    for (const f of this.u) tl += 46 + f.f.length;
+    const out = new u8(tl + 22);
+    for (const f of this.u) {
+      wzh(out, bt, f, f.f, f.u, f.c, l);
+      bt += 46 + f.f.length, l += f.b;
+    }
+    wzf(out, bt, this.u.length, tl, l)
+    this.ondata(null, out, true);
+    this.d = 2;
+  }
+
+  /**
+   * A method to terminate any internal workers used by the stream. Subsequent
+   * calls to add() will silently fail.
+   */
+  terminate() {
+    for (const f of this.u) f.t();
+    this.d = 2;
+  }
+
+  /**
+   * The handler to call whenever data is available
+   */
+  ondata: AsyncFlateStreamHandler;
+}
 
 
 /**
 /**
  * Asynchronously creates a ZIP file
  * Asynchronously creates a ZIP file
@@ -2104,8 +2619,11 @@ export function zip(data: AsyncZippable, opts: AsyncZipOptions | FlateCallback,
     for (let i = 0; i < slft; ++i) {
     for (let i = 0; i < slft; ++i) {
       const f = files[i];
       const f = files[i];
       try {
       try {
-        wzh(out, tot, f.c, f.d, f.m, f.n, f.u, f.p, null, f.t);
-        wzh(out, o, f.c, f.d, f.m, f.n, f.u, f.p, tot, f.t), o += 46 + f.n.length, tot += 30 + f.n.length + f.d.length;
+        const l = f.c.length;
+        wzh(out, tot, f, f.f, f.u, l);
+        const loc = tot + 30 + f.f.length;
+        out.set(f.c, loc);
+        wzh(out, o, f, f.f, f.u, l, tot), o += 46 + f.f.length, tot = loc + l;
       } catch(e) {
       } catch(e) {
         return cb(e, null);
         return cb(e, null);
       }
       }
@@ -2118,33 +2636,32 @@ export function zip(data: AsyncZippable, opts: AsyncZipOptions | FlateCallback,
   for (let i = 0; i < slft; ++i) {
   for (let i = 0; i < slft; ++i) {
     const fn = k[i];
     const fn = k[i];
     const [file, p] = r[fn];
     const [file, p] = r[fn];
-    const c = crc(), m = file.length;
+    const c = crc(), size = file.length;
     c.p(file);
     c.p(file);
-    const n = strToU8(fn), s = n.length;
-    const t = p.level == 0 ? 0 : 8;
+    const f = strToU8(fn), s = f.length;
+    const compression = p.level == 0 ? 0 : 8;
     const cbl: FlateCallback = (e, d) => {
     const cbl: FlateCallback = (e, d) => {
       if (e) {
       if (e) {
         tAll();
         tAll();
         cb(e, null);
         cb(e, null);
       } else {
       } else {
         const l = d.length;
         const l = d.length;
-        files[i] = {
-          t,
-          d,
-          m,
-          c: c.d(),
-          u: fn.length != l,
-          n,
-          p
-        };
+        files[i] = mrg(p, {
+          size,
+          crc: c.d(),
+          c: d,
+          f,
+          u: s != fn.length,
+          compression
+        });
         o += 30 + s + l;
         o += 30 + s + l;
         tot += 76 + 2 * s + l;
         tot += 76 + 2 * s + l;
         if (!--lft) cbf();
         if (!--lft) cbf();
       }
       }
     }
     }
-    if (n.length > 65535) cbl('filename too long', null);
-    if (!t) cbl(null, file);
-    else if (m < 160000) {
+    if (s > 65535) cbl('filename too long', null);
+    if (!compression) cbl(null, file);
+    else if (size < 160000) {
       try {
       try {
         cbl(null, deflateSync(file, p));
         cbl(null, deflateSync(file, p));
       } catch(e) {
       } catch(e) {
@@ -2170,30 +2687,29 @@ export function zipSync(data: Zippable, opts: ZipOptions = {}) {
   let tot = 0;
   let tot = 0;
   for (const fn in r) {
   for (const fn in r) {
     const [file, p] = r[fn];
     const [file, p] = r[fn];
-    const t = p.level == 0 ? 0 : 8;
-    const n = strToU8(fn), s = n.length;
-    if (n.length > 65535) throw 'filename too long';
-    const d = t ? deflateSync(file, p) : file, l = d.length;
+    const compression = p.level == 0 ? 0 : 8;
+    const f = strToU8(fn), s = f.length;
+    if (s > 65535) throw 'filename too long';
+    const d = compression ? deflateSync(file, p) : file, l = d.length;
     const c = crc();
     const c = crc();
     c.p(file);
     c.p(file);
-    files.push({
-      t,
-      d,
-      m: file.length,
-      c: c.d(),
-      u: fn.length != s,
-      n,
+    files.push(mrg(p, {
+      size: file.length,
+      crc: c.d(),
+      c: d,
+      f,
+      u: s != fn.length,
       o,
       o,
-      p
-    });
+      compression
+    }));
     o += 30 + s + l;
     o += 30 + s + l;
     tot += 76 + 2 * s + l;
     tot += 76 + 2 * s + l;
   }
   }
   const out = new u8(tot + 22), oe = o, cdl = tot - o;
   const out = new u8(tot + 22), oe = o, cdl = tot - o;
   for (let i = 0; i < files.length; ++i) {
   for (let i = 0; i < files.length; ++i) {
     const f = files[i];
     const f = files[i];
-    wzh(out, f.o, f.c, f.d, f.m, f.n, f.u, f.p, null, f.t);
-    wzh(out, o, f.c, f.d, f.m, f.n, f.u, f.p, f.o, f.t), o += 46 + f.n.length;
+    wzh(out, f.o, f, f.f, f.u, f.c.length);
+    wzh(out, o, f, f.f, f.u, f.c.length, f.o), o += 46 + f.f.length;
   }
   }
   wzf(out, o, files.length, cdl, oe);
   wzf(out, o, files.length, cdl, oe);
   return out;
   return out;
@@ -2226,7 +2742,10 @@ export function unzip(data: Uint8Array, cb: UnzipCallback): AsyncTerminable {
   const z = o == 4294967295;
   const z = o == 4294967295;
   if (z) {
   if (z) {
     e = b4(data, e - 12);
     e = b4(data, e - 12);
-    if (b4(data, e) != 0x6064B50) throw 'invalid zip file';
+    if (b4(data, e) != 0x6064B50) {
+      cb('invalid zip file', null);
+      return;
+    }
     c = lft = b4(data, e + 32);
     c = lft = b4(data, e + 32);
     o = b4(data, e + 48);
     o = b4(data, e + 48);
   }
   }

+ 1 - 1
src/node-worker.ts

@@ -3,7 +3,7 @@ let Worker: typeof import('worker_threads').Worker;
 const workerAdd = ";var __w=require('worker_threads');__w.parentPort.on('message',function(m){onmessage({data:m})}),postMessage=function(m,t){__w.parentPort.postMessage(m,t)},close=process.exit;self=global";
 const workerAdd = ";var __w=require('worker_threads');__w.parentPort.on('message',function(m){onmessage({data:m})}),postMessage=function(m,t){__w.parentPort.postMessage(m,t)},close=process.exit;self=global";
 
 
 try {
 try {
-  Worker = require('worker_threads').Worker;
+  Worker = /*#__PURE__*/require('worker_threads').Worker;
 } catch(e) {
 } catch(e) {
 }
 }
 export default Worker ? <T>(c: string, _: number, msg: unknown, transfer: ArrayBuffer[], cb: (err: Error, msg: T) => void) => {
 export default Worker ? <T>(c: string, _: number, msg: unknown, transfer: ArrayBuffer[], cb: (err: Error, msg: T) => void) => {

+ 5 - 0
yarn.lock

@@ -1540,6 +1540,11 @@
     "@material/feature-targeting" "^5.1.0"
     "@material/feature-targeting" "^5.1.0"
     "@material/theme" "^5.1.0"
     "@material/theme" "^5.1.0"
 
 
+"@msgpack/msgpack@^2.3.0":
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/@msgpack/msgpack/-/msgpack-2.3.0.tgz#a9043b920837b2dd63482e7bf6b8345813e9816b"
+  integrity sha512-xxRejzNpiVQ2lzxMG/yo2ocfZSk+cKo2THq54AimaubMucg66DpQm9Yj7ESMr/l2EqDkmF2Dx4r0F/cbsitAaw==
+
 "@nodelib/[email protected]":
 "@nodelib/[email protected]":
   version "2.1.3"
   version "2.1.3"
   resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b"
   resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b"