9829 lines
354 KiB
JavaScript
9829 lines
354 KiB
JavaScript
'use strict';
|
|
|
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
/*
|
|
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();
|
|
|
|
/*
|
|
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 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);
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
const t=[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],r=[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],f=[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],o=[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],n=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],c=new Uint8Array(288);c.fill(8,0,144),c.fill(9,144,256),c.fill(7,256,280),c.fill(8,280,288);const e=new Uint8Array(30).fill(5);function s(t){const r=new Uint16Array(16);for(const f of t)r[f]++;r[0]=0;const f=new Uint16Array(17);for(let t=1;15>=t;t++)f[t+1]=f[t]+r[t];const o=new Uint16Array(t.length);for(let r=0;r<t.length;r++)t[r]&&(o[f[t[r]]++]=r);return {t:r,symbols:o}}const a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";function i(i){i({workerURI:i=>{const v="text/javascript",m=(a=>{let i=0,v=0,m=0,u=new Uint8Array(1024),j=0,R=0;for(;!R;){R=x(1);const t=x(2);if(0==t)V();else if(1==t)X(s(c),s(e));else {if(2!=t)throw Error("invalid deflate block type");X(...K());}}return u.subarray(0,j);function b(){if(i>=a.length)throw Error("unexpected end of deflate data");return a[i++]}function x(t){for(;t>m;)v|=b()<<m,m+=8;const r=v&(1<<t)-1;return v>>>=t,m-=t,r}function V(){v=0,m=0;const t=b()|b()<<8;i+=2,l(j+t);for(let r=0;t>r;r++)u[j++]=b();}function X(n,c){let e=L(n);for(;256!=e;){if(256>e)l(j+1),u[j++]=e;else {const n=e-257,s=t[n]+x(r[n]),a=L(c),i=f[a]+x(o[a]);l(j+s);const v=j-i;for(let t=0;s>t;t++)u[j++]=u[v+t];}e=L(n);}}function K(){const t=x(5)+257,r=x(5)+1,f=x(4)+4,o=new Uint8Array(19);for(let t=0;f>t;t++)o[n[t]]=x(3);const c=s(o),e=new Uint8Array(t+r);let a=0;for(;a<e.length;){const t=L(c);if(16>t)e[a++]=t;else if(16==t){const t=e[a-1];let r=x(2)+3;for(;r--;)e[a++]=t;}else a+=17==t?x(3)+3:x(7)+11;}return [s(e.subarray(0,t)),s(e.subarray(t))]}function L(t){const{t:r,symbols:f}=t;let o=0,n=0,c=0;for(let t=1;15>=t;t++){o|=x(1);const e=r[t];if(e>o-n)return f[c+(o-n)];c+=e,n=n+e<<1,o<<=1;}throw Error("invalid huffman code")}function l(t){if(u.length<t){let r=2*u.length;for(;t>r;)r*=2;const f=new Uint8Array(r);f.set(u.subarray(0,j)),u=f;}}})((t=>{const r=(t=(t+"").replace(/[^A-Za-z0-9+/=]/g,"")).length,f=[];for(let o=0;r>o;o+=4){const r=a.indexOf(t[o])<<18|a.indexOf(t[o+1])<<12|(63&a.indexOf(t[o+2]))<<6|63&a.indexOf(t[o+3]);f.push(r>>16&255),"="!==t[o+2]&&f.push(r>>8&255),"="!==t[o+3]&&f.push(255&r);}return new Uint8Array(f)})("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"));if(i){const t=new Blob([m],{type:v});return URL.createObjectURL(t)}return "data:"+v+";base64,"+(t=>{let r="";const f=t.length;let o=0;for(;f>o+2;o+=3){const f=t[o]<<16|t[o+1]<<8|t[o+2];r+=a[f>>18&63]+a[f>>12&63]+a[f>>6&63]+a[63&f];}const n=f-o;if(1===n){const f=t[o]<<16;r+=a[f>>18&63]+a[f>>12&63]+"==";}else if(2===n){const f=t[o]<<16|t[o+1]<<8;r+=a[f>>18&63]+a[f>>12&63]+a[f>>6&63]+"=";}return r})(m)}});}
|
|
|
|
/*
|
|
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$1 = [[], [], [], [], [], [], [], []];
|
|
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$1[0][n] = t;
|
|
}
|
|
for (let n = 0; n < 256; n++) {
|
|
for (let k = 1; k < 8; k++) {
|
|
const previous = T$1[k - 1][n];
|
|
T$1[k][n] = (previous >>> 8) ^ T$1[0][previous & 0xFF];
|
|
}
|
|
}
|
|
const [T0, T1, T2, T3, T4, T5, T6, T7] = T$1;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/*
|
|
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.
|
|
*/
|
|
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/*
|
|
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.
|
|
*/
|
|
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
// 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!");
|
|
}
|
|
}
|
|
};
|
|
|
|
/*
|
|
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 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";
|
|
|
|
function getRandomValues(array) {
|
|
if (GET_RANDOM_VALUES_SUPPORTED) {
|
|
return crypto.getRandomValues(array);
|
|
} else {
|
|
throw new Error(ERR_UNSUPPORTED_CRYPTO_API);
|
|
}
|
|
}
|
|
|
|
/*
|
|
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 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;
|
|
}
|
|
}
|
|
|
|
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$1(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$1(encrypt, strength, password, salt);
|
|
return concat(salt, passwordVerification);
|
|
}
|
|
|
|
async function createKeys$1(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);
|
|
}
|
|
|
|
/*
|
|
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 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);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/*
|
|
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 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);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
});
|
|
}
|
|
|
|
/*
|
|
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 DEFAULT_CHUNK_SIZE$1 = 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";
|
|
|
|
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$1;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
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 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 = () => { };
|
|
|
|
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$1(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$1({ 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$1(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$1(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$1(workerData);
|
|
close(error);
|
|
}
|
|
}
|
|
|
|
function close(error, result) {
|
|
if (stale()) {
|
|
return;
|
|
}
|
|
if (error) {
|
|
rejectResult(error);
|
|
} else {
|
|
resolveResult(result);
|
|
}
|
|
if (writer) {
|
|
writer.releaseLock();
|
|
}
|
|
onTaskFinished();
|
|
}
|
|
}
|
|
|
|
/*
|
|
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.
|
|
*/
|
|
|
|
|
|
let pool = [];
|
|
const pendingRequests = [];
|
|
let starvationTimeout;
|
|
let starvationDelay;
|
|
|
|
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();
|
|
}));
|
|
}
|
|
|
|
/*
|
|
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 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);
|
|
}
|
|
|
|
/*
|
|
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;
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
/*
|
|
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.
|
|
*/
|
|
|
|
|
|
function decodeText(value, encoding) {
|
|
if (encoding && encoding.trim().toLowerCase() == "cp437") {
|
|
return decodeCP437(value);
|
|
} else {
|
|
return new TextDecoder(encoding, { ignoreBOM: true }).decode(value);
|
|
}
|
|
}
|
|
|
|
/*
|
|
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]);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
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";
|
|
|
|
/*
|
|
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 ERR_BAD_FORMAT = "File format is not recognized";
|
|
const ERR_EOCDR_NOT_FOUND = "End of central directory not found";
|
|
const ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND = "End of Zip64 central directory locator not found";
|
|
const ERR_CENTRAL_DIRECTORY_NOT_FOUND = "Central directory header not found";
|
|
const ERR_LOCAL_FILE_HEADER_NOT_FOUND = "Local file header not found";
|
|
const ERR_EXTRAFIELD_ZIP64_NOT_FOUND = "Zip64 extra field not found";
|
|
const ERR_ENCRYPTED = "File contains encrypted entry";
|
|
const ERR_UNSUPPORTED_ENCRYPTION = "Encryption method not supported";
|
|
const ERR_UNSUPPORTED_COMPRESSION$1 = "Compression method not supported";
|
|
const ERR_SPLIT_ZIP_FILE = "Split zip file";
|
|
const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
|
|
const ERR_AMBIGUOUS_ARCHIVE = "Ambiguous archive";
|
|
const CHARSET_UTF8 = "utf-8";
|
|
const PROPERTY_NAME_UTF8_SUFFIX = "UTF8";
|
|
const CHARSET_CP437 = "cp437";
|
|
const BITFLAG_AMBIGUITY_MASK = BITFLAG_ENCRYPTED | BITFLAG_DATA_DESCRIPTOR | BITFLAG_LANG_ENCODING_FLAG;
|
|
const ZIP64_PROPERTIES = [
|
|
[PROPERTY_NAME_UNCOMPRESSED_SIZE, MAX_32_BITS],
|
|
[PROPERTY_NAME_COMPRESSED_SIZE, MAX_32_BITS],
|
|
[PROPERTY_NAME_OFFSET, MAX_32_BITS],
|
|
[PROPERTY_NAME_DISK_NUMBER_START, MAX_16_BITS]
|
|
];
|
|
const ZIP64_EXTRACTION = {
|
|
[MAX_16_BITS]: {
|
|
getValue: getUint32,
|
|
bytes: 4
|
|
},
|
|
[MAX_32_BITS]: {
|
|
getValue: getBigUint64,
|
|
bytes: 8
|
|
}
|
|
};
|
|
|
|
class ZipReader {
|
|
|
|
constructor(reader, options = {}) {
|
|
Object.assign(this, {
|
|
reader: new GenericReader(reader),
|
|
options,
|
|
config: getConfiguration(),
|
|
readRanges: new Map()
|
|
});
|
|
}
|
|
|
|
async* getEntriesGenerator(options = {}) {
|
|
const zipReader = this;
|
|
let { reader } = zipReader;
|
|
const { config } = zipReader;
|
|
await initStream(reader);
|
|
if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
|
|
reader = new BlobReader(await new Response(reader.readable).blob());
|
|
await initStream(reader);
|
|
}
|
|
if (reader.size < END_OF_CENTRAL_DIR_LENGTH) {
|
|
throw new Error(ERR_BAD_FORMAT);
|
|
}
|
|
const strictness = getStrictness(getOptionValue$1(zipReader, options, OPTION_STRICTNESS), getOptionValue$1(zipReader, options, OPTION_CHECK_AMBIGUITY));
|
|
const checkAmbiguity = strictness == STRICTNESS_STRICT;
|
|
const rejectAmbiguousEndOfDirectory = strictness != STRICTNESS_TOLERANT;
|
|
const maxAppendedDataSize = getMaxAppendedDataSize(getOptionValue$1(zipReader, options, OPTION_MAX_APPENDED_DATA_SIZE), strictness);
|
|
const { endOfDirectoryInfo, endOfDirectoryReachingEndCount } = await findEndOfCentralDirectory(reader, rejectAmbiguousEndOfDirectory, maxAppendedDataSize);
|
|
if (!endOfDirectoryInfo) {
|
|
const signatureArray = await readUint8Array(reader, 0, 4);
|
|
const signatureView = getDataView$1(signatureArray);
|
|
if (getUint32(signatureView) == SPLIT_ZIP_FILE_SIGNATURE) {
|
|
throw new Error(ERR_SPLIT_ZIP_FILE);
|
|
} else {
|
|
throw new Error(ERR_EOCDR_NOT_FOUND);
|
|
}
|
|
}
|
|
if (rejectAmbiguousEndOfDirectory && endOfDirectoryReachingEndCount > 1) {
|
|
throwAmbiguousArchive("multiple end of central directory records");
|
|
}
|
|
const endOfDirectoryView = getDataView$1(endOfDirectoryInfo);
|
|
let directoryDataLength = getUint32(endOfDirectoryView, 12);
|
|
let directoryDataOffset = getUint32(endOfDirectoryView, 16);
|
|
const commentOffset = endOfDirectoryInfo.offset;
|
|
const commentLength = getUint16(endOfDirectoryView, 20);
|
|
const appendedDataOffset = commentOffset + END_OF_CENTRAL_DIR_LENGTH + commentLength;
|
|
if (reader.size - appendedDataOffset > maxAppendedDataSize) {
|
|
throwAmbiguousArchive("appended data");
|
|
}
|
|
let lastDiskNumber = getUint16(endOfDirectoryView, 4);
|
|
const expectedLastDiskNumber = reader.lastDiskNumber || 0;
|
|
let diskNumber = getUint16(endOfDirectoryView, 6);
|
|
let filesLength = getUint16(endOfDirectoryView, 10);
|
|
let prependedDataLength = 0;
|
|
let startOffset;
|
|
let zip64EndOfDirectory;
|
|
if (directoryDataOffset == MAX_32_BITS || directoryDataLength == MAX_32_BITS || filesLength == MAX_16_BITS || diskNumber == MAX_16_BITS) {
|
|
const endOfDirectoryLocatorArray = endOfDirectoryInfo.offset >= ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH ?
|
|
await readUint8Array(reader, endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH, ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH) :
|
|
EMPTY_UINT8_ARRAY;
|
|
const endOfDirectoryLocatorView = getDataView$1(endOfDirectoryLocatorArray);
|
|
if (endOfDirectoryLocatorArray.length == ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH &&
|
|
getUint32(endOfDirectoryLocatorView, 0) == ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE) {
|
|
directoryDataOffset = getBigUint64(endOfDirectoryLocatorView, 8);
|
|
let endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH, -1);
|
|
let endOfDirectoryView = getDataView$1(endOfDirectoryArray);
|
|
const expectedDirectoryDataOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH - ZIP64_END_OF_CENTRAL_DIR_LENGTH - (reader.lastDiskOffset || 0);
|
|
if ((endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) &&
|
|
directoryDataOffset != expectedDirectoryDataOffset && expectedDirectoryDataOffset >= 0) {
|
|
const originalDirectoryDataOffset = directoryDataOffset;
|
|
directoryDataOffset = expectedDirectoryDataOffset;
|
|
if (directoryDataOffset > originalDirectoryDataOffset) {
|
|
prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
|
|
}
|
|
endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH, -1);
|
|
endOfDirectoryView = getDataView$1(endOfDirectoryArray);
|
|
}
|
|
if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
|
|
throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
|
|
}
|
|
zip64EndOfDirectory = true;
|
|
if (lastDiskNumber == MAX_16_BITS) {
|
|
lastDiskNumber = getUint32(endOfDirectoryView, 16);
|
|
} else if (checkAmbiguity && lastDiskNumber != getUint32(endOfDirectoryView, 16)) {
|
|
throwAmbiguousArchive("mismatched zip64 end of central directory record");
|
|
}
|
|
if (diskNumber == MAX_16_BITS) {
|
|
diskNumber = getUint32(endOfDirectoryView, 20);
|
|
} else if (checkAmbiguity && diskNumber != getUint32(endOfDirectoryView, 20)) {
|
|
throwAmbiguousArchive("mismatched zip64 end of central directory record");
|
|
}
|
|
if (filesLength == MAX_16_BITS) {
|
|
filesLength = getBigUint64(endOfDirectoryView, 32);
|
|
} else if (checkAmbiguity && filesLength != getBigUint64(endOfDirectoryView, 32)) {
|
|
throwAmbiguousArchive("mismatched zip64 end of central directory record");
|
|
}
|
|
if (directoryDataLength == MAX_32_BITS) {
|
|
directoryDataLength = getBigUint64(endOfDirectoryView, 40);
|
|
} else if (checkAmbiguity && directoryDataLength != getBigUint64(endOfDirectoryView, 40)) {
|
|
throwAmbiguousArchive("mismatched zip64 end of central directory record");
|
|
}
|
|
directoryDataOffset = getBigUint64(endOfDirectoryView, 48) + prependedDataLength;
|
|
}
|
|
}
|
|
const declaredDirectoryDataLength = directoryDataLength;
|
|
const centralDirectoryEndOffset = endOfDirectoryInfo.offset -
|
|
(zip64EndOfDirectory ? ZIP64_END_OF_CENTRAL_DIR_LENGTH + ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH : 0);
|
|
if (directoryDataOffset >= reader.size) {
|
|
prependedDataLength = reader.size - directoryDataOffset - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
|
|
directoryDataOffset = reader.size - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
|
|
}
|
|
if (expectedLastDiskNumber != lastDiskNumber) {
|
|
throw new Error(ERR_SPLIT_ZIP_FILE);
|
|
}
|
|
if (directoryDataOffset < 0) {
|
|
throw new Error(ERR_BAD_FORMAT);
|
|
}
|
|
let offset = 0;
|
|
let directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength, diskNumber);
|
|
let directoryView = getDataView$1(directoryArray);
|
|
if (directoryDataLength) {
|
|
if (directoryArray.length < 4) {
|
|
throw new Error(ERR_BAD_FORMAT);
|
|
}
|
|
const expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength - (reader.lastDiskOffset || 0);
|
|
if (directoryDataOffset != expectedDirectoryDataOffset && diskNumber == lastDiskNumber) {
|
|
const storedPointsAtDirectory = getUint32(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE;
|
|
let reconcile = !storedPointsAtDirectory;
|
|
if (!reconcile && expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
|
|
const expectedSignatureArray = await readUint8Array(reader, expectedDirectoryDataOffset, 4, diskNumber);
|
|
reconcile = getUint32(getDataView$1(expectedSignatureArray), 0) == CENTRAL_FILE_HEADER_SIGNATURE;
|
|
}
|
|
if (reconcile) {
|
|
const originalDirectoryDataOffset = directoryDataOffset;
|
|
directoryDataOffset = expectedDirectoryDataOffset;
|
|
if (directoryDataOffset > originalDirectoryDataOffset) {
|
|
prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
|
|
}
|
|
directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength, diskNumber);
|
|
directoryView = getDataView$1(directoryArray);
|
|
}
|
|
}
|
|
}
|
|
const expectedDirectoryDataLength = centralDirectoryEndOffset - directoryDataOffset - (reader.lastDiskOffset || 0);
|
|
if (directoryDataLength != expectedDirectoryDataLength && expectedDirectoryDataLength >= 0 && diskNumber == lastDiskNumber) {
|
|
directoryDataLength = expectedDirectoryDataLength;
|
|
directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength, diskNumber);
|
|
directoryView = getDataView$1(directoryArray);
|
|
}
|
|
if (directoryDataOffset < 0 || directoryDataOffset >= reader.size) {
|
|
throw new Error(ERR_BAD_FORMAT);
|
|
}
|
|
startOffset = directoryDataOffset;
|
|
const filenameEncoding = getOptionValue$1(zipReader, options, OPTION_FILENAME_ENCODING);
|
|
const commentEncoding = getOptionValue$1(zipReader, options, OPTION_COMMENT_ENCODING);
|
|
const filenames = checkAmbiguity ? new Set() : UNDEFINED_VALUE;
|
|
let duplicateFilename;
|
|
for (let indexFile = 0; indexFile < filesLength; indexFile++) {
|
|
const fileEntry = new ZipEntry$1(reader, config, zipReader.options);
|
|
if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
|
|
throw new Error(ERR_CENTRAL_DIRECTORY_NOT_FOUND);
|
|
}
|
|
readCommonHeader(fileEntry, directoryView, offset + 6);
|
|
const languageEncodingFlag = Boolean(fileEntry.bitFlag.languageEncodingFlag);
|
|
const filenameOffset = offset + CENTRAL_FILE_HEADER_LENGTH;
|
|
const extraFieldOffset = filenameOffset + fileEntry.filenameLength;
|
|
const commentOffset = extraFieldOffset + fileEntry.extraFieldLength;
|
|
const versionMadeBy = getUint16(directoryView, offset + 4);
|
|
const msDosCompatible = versionMadeBy >> 8 == 0;
|
|
const unixCompatible = versionMadeBy >> 8 == 3;
|
|
const rawFilename = directoryArray.subarray(filenameOffset, extraFieldOffset);
|
|
const commentLength = getUint16(directoryView, offset + 32);
|
|
const endOffset = commentOffset + commentLength;
|
|
const rawComment = directoryArray.subarray(commentOffset, endOffset);
|
|
const filenameUTF8 = languageEncodingFlag;
|
|
const commentUTF8 = languageEncodingFlag;
|
|
const externalFileAttributes = getUint32(directoryView, offset + 38);
|
|
const msdosAttributesRaw = externalFileAttributes & MAX_8_BITS;
|
|
const msdosAttributes = {
|
|
readOnly: Boolean(msdosAttributesRaw & FILE_ATTR_MSDOS_READONLY_MASK),
|
|
hidden: Boolean(msdosAttributesRaw & FILE_ATTR_MSDOS_HIDDEN_MASK),
|
|
system: Boolean(msdosAttributesRaw & FILE_ATTR_MSDOS_SYSTEM_MASK),
|
|
directory: Boolean(msdosAttributesRaw & FILE_ATTR_MSDOS_DIR_MASK),
|
|
archive: Boolean(msdosAttributesRaw & FILE_ATTR_MSDOS_ARCHIVE_MASK)
|
|
};
|
|
const offsetFileEntry = getUint32(directoryView, offset + 42);
|
|
const decode = getOptionValue$1(zipReader, options, OPTION_DECODE_TEXT) || decodeText;
|
|
const rawFilenameEncoding = filenameUTF8 ? CHARSET_UTF8 : filenameEncoding || CHARSET_CP437;
|
|
const rawCommentEncoding = commentUTF8 ? CHARSET_UTF8 : commentEncoding || CHARSET_CP437;
|
|
let filename = decode(rawFilename, rawFilenameEncoding);
|
|
if (filename === UNDEFINED_VALUE) {
|
|
filename = decodeText(rawFilename, rawFilenameEncoding);
|
|
}
|
|
let comment = decode(rawComment, rawCommentEncoding);
|
|
if (comment === UNDEFINED_VALUE) {
|
|
comment = decodeText(rawComment, rawCommentEncoding);
|
|
}
|
|
Object.assign(fileEntry, {
|
|
index: indexFile,
|
|
versionMadeBy,
|
|
msDosCompatible,
|
|
compressedSize: 0,
|
|
uncompressedSize: 0,
|
|
commentLength,
|
|
offset: offsetFileEntry,
|
|
diskNumberStart: getUint16(directoryView, offset + 34),
|
|
internalFileAttributes: getUint16(directoryView, offset + 36),
|
|
externalFileAttributes,
|
|
msdosAttributesRaw,
|
|
msdosAttributes,
|
|
rawFilename,
|
|
filenameUTF8,
|
|
commentUTF8,
|
|
rawExtraField: directoryArray.subarray(extraFieldOffset, commentOffset),
|
|
rawComment,
|
|
filename,
|
|
comment
|
|
});
|
|
readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
|
|
fileEntry.offset += prependedDataLength;
|
|
startOffset = Math.min(fileEntry.offset, startOffset);
|
|
if (checkAmbiguity) {
|
|
if (filenames.has(fileEntry.filename)) {
|
|
duplicateFilename = true;
|
|
}
|
|
filenames.add(fileEntry.filename);
|
|
}
|
|
const unixExternalUpper = (fileEntry.externalFileAttributes >> 16) & MAX_16_BITS;
|
|
if (fileEntry.unixMode === UNDEFINED_VALUE && (unixExternalUpper & (FILE_ATTR_UNIX_DEFAULT_MASK | FILE_ATTR_UNIX_EXECUTABLE_MASK | FILE_ATTR_UNIX_TYPE_DIR)) != 0) {
|
|
fileEntry.unixMode = unixExternalUpper;
|
|
}
|
|
const setuid = Boolean(fileEntry.unixMode & FILE_ATTR_UNIX_SETUID_MASK);
|
|
const setgid = Boolean(fileEntry.unixMode & FILE_ATTR_UNIX_SETGID_MASK);
|
|
const sticky = Boolean(fileEntry.unixMode & FILE_ATTR_UNIX_STICKY_MASK);
|
|
const executable = (fileEntry.unixMode !== UNDEFINED_VALUE)
|
|
? ((fileEntry.unixMode & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)
|
|
: (unixCompatible && ((unixExternalUpper & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0));
|
|
const modeIsDir = fileEntry.unixMode !== UNDEFINED_VALUE && ((fileEntry.unixMode & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
|
|
const upperIsDir = ((unixExternalUpper & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
|
|
Object.assign(fileEntry, {
|
|
setuid,
|
|
setgid,
|
|
sticky,
|
|
unixExternalUpper,
|
|
internalFileAttribute: fileEntry.internalFileAttributes,
|
|
externalFileAttribute: fileEntry.externalFileAttributes,
|
|
executable,
|
|
directory: modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || (fileEntry.filename.endsWith(DIRECTORY_SIGNATURE) && !fileEntry.uncompressedSize),
|
|
zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
|
|
});
|
|
const entry = new Entry(fileEntry);
|
|
entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader.readRanges, options);
|
|
entry.arrayBuffer = async options => {
|
|
const writer = new TransformStream();
|
|
const [arrayBuffer] = await Promise.all([
|
|
new Response(writer.readable).arrayBuffer(),
|
|
fileEntry.getData(writer, entry, zipReader.readRanges, options)]);
|
|
return arrayBuffer;
|
|
};
|
|
offset = endOffset;
|
|
const { onprogress } = options;
|
|
if (onprogress) {
|
|
try {
|
|
await onprogress(indexFile + 1, filesLength, new Entry(fileEntry));
|
|
} catch {
|
|
// ignored
|
|
}
|
|
}
|
|
yield entry;
|
|
}
|
|
if (checkAmbiguity && offset != declaredDirectoryDataLength) {
|
|
throwAmbiguousArchive("trailing central directory data");
|
|
}
|
|
if (duplicateFilename) {
|
|
throwAmbiguousArchive("duplicate filename");
|
|
}
|
|
if (checkAmbiguity && (prependedDataLength || (filesLength && startOffset > 0))) {
|
|
throwAmbiguousArchive("prepended data");
|
|
}
|
|
const extractPrependedData = getOptionValue$1(zipReader, options, OPTION_EXTRACT_PREPENDED_DATA);
|
|
const extractAppendedData = getOptionValue$1(zipReader, options, OPTION_EXTRACT_APPENDED_DATA);
|
|
if (extractPrependedData) {
|
|
zipReader.prependedData = startOffset > 0 ? await readUint8Array(reader, 0, startOffset) : EMPTY_UINT8_ARRAY;
|
|
}
|
|
zipReader.comment = commentLength ? await readUint8Array(reader, commentOffset + END_OF_CENTRAL_DIR_LENGTH, commentLength) : EMPTY_UINT8_ARRAY;
|
|
if (extractAppendedData) {
|
|
zipReader.appendedData = appendedDataOffset < reader.size ? await readUint8Array(reader, appendedDataOffset, reader.size - appendedDataOffset) : EMPTY_UINT8_ARRAY;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
async getEntries(options = {}) {
|
|
const entries = [];
|
|
for await (const entry of this.getEntriesGenerator(options)) {
|
|
entries.push(entry);
|
|
}
|
|
return entries;
|
|
}
|
|
|
|
async close() {
|
|
}
|
|
}
|
|
|
|
class ZipReaderStream {
|
|
|
|
constructor(options = {}) {
|
|
const { readable, writable } = new TransformStream();
|
|
const gen = new ZipReader(readable, options).getEntriesGenerator();
|
|
this.readable = new ReadableStream({
|
|
async pull(controller) {
|
|
const { done, value } = await gen.next();
|
|
if (done)
|
|
return controller.close();
|
|
const chunk = {
|
|
...value,
|
|
readable: (function () {
|
|
const { readable, writable } = new TransformStream();
|
|
if (value.getData) {
|
|
getData();
|
|
return readable;
|
|
}
|
|
|
|
async function getData() {
|
|
try {
|
|
await value.getData(writable);
|
|
} catch (error) {
|
|
try {
|
|
await writable.abort(error);
|
|
} catch {
|
|
// ignored
|
|
}
|
|
}
|
|
}
|
|
})()
|
|
};
|
|
delete chunk.getData;
|
|
controller.enqueue(chunk);
|
|
}
|
|
});
|
|
this.writable = writable;
|
|
}
|
|
}
|
|
|
|
let ZipEntry$1 = class ZipEntry {
|
|
|
|
constructor(reader, config, options) {
|
|
Object.assign(this, {
|
|
reader,
|
|
config,
|
|
options
|
|
});
|
|
}
|
|
|
|
async getData(writer, fileEntry, readRanges, options = {}) {
|
|
const zipEntry = this;
|
|
const {
|
|
reader,
|
|
index,
|
|
offset,
|
|
diskNumberStart,
|
|
extraFieldAES,
|
|
extraFieldZip64,
|
|
compressionMethod,
|
|
config,
|
|
bitFlag,
|
|
signature,
|
|
rawLastModDate,
|
|
uncompressedSize,
|
|
compressedSize
|
|
} = zipEntry;
|
|
const {
|
|
dataDescriptor
|
|
} = bitFlag;
|
|
const localDirectory = fileEntry.localDirectory = {};
|
|
const dataArray = await readUint8Array(reader, offset, HEADER_SIZE, diskNumberStart);
|
|
const dataView = getDataView$1(dataArray);
|
|
let password = getOptionValue$1(zipEntry, options, OPTION_PASSWORD);
|
|
let rawPassword = getOptionValue$1(zipEntry, options, OPTION_RAW_PASSWORD);
|
|
const passThrough = getOptionValue$1(zipEntry, options, OPTION_PASS_THROUGH);
|
|
password = password && password.length && password;
|
|
rawPassword = rawPassword && rawPassword.length && rawPassword;
|
|
if (extraFieldAES) {
|
|
if (extraFieldAES.originalCompressionMethod != COMPRESSION_METHOD_AES) {
|
|
throw new Error(ERR_UNSUPPORTED_COMPRESSION$1);
|
|
}
|
|
}
|
|
if ((compressionMethod != COMPRESSION_METHOD_STORE && compressionMethod != COMPRESSION_METHOD_DEFLATE && compressionMethod != COMPRESSION_METHOD_DEFLATE_64) && !passThrough) {
|
|
throw new Error(ERR_UNSUPPORTED_COMPRESSION$1);
|
|
}
|
|
if (dataArray.length < HEADER_SIZE || getUint32(dataView, 0) != LOCAL_FILE_HEADER_SIGNATURE) {
|
|
throw new Error(ERR_LOCAL_FILE_HEADER_NOT_FOUND);
|
|
}
|
|
readCommonHeader(localDirectory, dataView, 4);
|
|
const {
|
|
extraFieldLength,
|
|
filenameLength
|
|
} = localDirectory;
|
|
const checkAmbiguity = getStrictness(getOptionValue$1(zipEntry, options, OPTION_STRICTNESS), getOptionValue$1(zipEntry, options, OPTION_CHECK_AMBIGUITY)) == STRICTNESS_STRICT;
|
|
let rawLocalFilename = EMPTY_UINT8_ARRAY;
|
|
if (checkAmbiguity && (filenameLength || extraFieldLength)) {
|
|
const trailingDataArray = await readUint8Array(reader, offset + HEADER_SIZE, filenameLength + extraFieldLength, diskNumberStart);
|
|
rawLocalFilename = trailingDataArray.subarray(0, filenameLength);
|
|
localDirectory.rawExtraField = trailingDataArray.subarray(filenameLength);
|
|
} else {
|
|
localDirectory.rawExtraField = extraFieldLength ?
|
|
await readUint8Array(reader, offset + HEADER_SIZE + filenameLength, extraFieldLength, diskNumberStart) :
|
|
EMPTY_UINT8_ARRAY;
|
|
}
|
|
readCommonFooter(zipEntry, localDirectory, dataView, 4, true);
|
|
if (checkAmbiguity) {
|
|
checkLocalDirectory(zipEntry, localDirectory, rawLocalFilename);
|
|
}
|
|
const { lastAccessDate, creationDate } = localDirectory;
|
|
if (lastAccessDate) {
|
|
fileEntry.lastAccessDate = lastAccessDate;
|
|
}
|
|
if (creationDate) {
|
|
fileEntry.creationDate = creationDate;
|
|
}
|
|
const encrypted = zipEntry.encrypted && localDirectory.encrypted && !passThrough;
|
|
const zipCrypto = encrypted && !extraFieldAES;
|
|
if (!passThrough) {
|
|
fileEntry.zipCrypto = zipCrypto;
|
|
}
|
|
if (encrypted) {
|
|
if (!zipCrypto && extraFieldAES.strength === UNDEFINED_VALUE) {
|
|
throw new Error(ERR_UNSUPPORTED_ENCRYPTION);
|
|
} else if (!password && !rawPassword) {
|
|
throw new Error(ERR_ENCRYPTED);
|
|
}
|
|
}
|
|
const dataOffset = offset + HEADER_SIZE + filenameLength + extraFieldLength;
|
|
const size = compressedSize;
|
|
const readable = reader.createReadable({ offset: dataOffset, size, diskNumberStart, chunkSize: getChunkSize(config) });
|
|
const signal = getOptionValue$1(zipEntry, options, OPTION_SIGNAL);
|
|
const checkPasswordOnly = getOptionValue$1(zipEntry, options, OPTION_CHECK_PASSWORD_ONLY);
|
|
let checkOverlappingEntry = getOptionValue$1(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY);
|
|
const checkOverlappingEntryOnly = getOptionValue$1(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY_ONLY);
|
|
if (checkOverlappingEntryOnly) {
|
|
checkOverlappingEntry = true;
|
|
}
|
|
const { onstart, onprogress, onend } = options;
|
|
const deflate64 = compressionMethod == COMPRESSION_METHOD_DEFLATE_64;
|
|
let useCompressionStream = getOptionValue$1(zipEntry, options, OPTION_USE_COMPRESSION_STREAM);
|
|
if (deflate64) {
|
|
useCompressionStream = false;
|
|
}
|
|
const workerOptions = {
|
|
options: {
|
|
codecType: CODEC_INFLATE,
|
|
password,
|
|
rawPassword,
|
|
zipCrypto,
|
|
encryptionStrength: extraFieldAES && extraFieldAES.strength,
|
|
signed: getOptionValue$1(zipEntry, options, OPTION_CHECK_SIGNATURE) && !passThrough,
|
|
passwordVerification: zipCrypto && (dataDescriptor ? ((rawLastModDate >>> 8) & MAX_8_BITS) : ((signature >>> 24) & MAX_8_BITS)),
|
|
outputSize: passThrough ? compressedSize : uncompressedSize,
|
|
signature,
|
|
compressed: compressionMethod != 0 && !passThrough,
|
|
encrypted,
|
|
useWebWorkers: getOptionValue$1(zipEntry, options, OPTION_USE_WEB_WORKERS),
|
|
useCompressionStream,
|
|
transferStreams: getOptionValue$1(zipEntry, options, OPTION_TRANSFER_STREAMS),
|
|
deflate64,
|
|
checkPasswordOnly
|
|
},
|
|
config,
|
|
streamOptions: { signal, size, onstart, onprogress, onend }
|
|
};
|
|
if (checkOverlappingEntry) {
|
|
await detectOverlappingEntry({
|
|
reader,
|
|
fileEntry,
|
|
index,
|
|
offset,
|
|
diskNumberStart,
|
|
signature,
|
|
compressedSize,
|
|
uncompressedSize,
|
|
dataOffset,
|
|
dataDescriptor: dataDescriptor || localDirectory.bitFlag.dataDescriptor,
|
|
extraFieldZip64: extraFieldZip64 || localDirectory.extraFieldZip64,
|
|
readRanges
|
|
});
|
|
}
|
|
let writable;
|
|
try {
|
|
if (!checkOverlappingEntryOnly) {
|
|
if (checkPasswordOnly) {
|
|
writer = new WritableStream();
|
|
}
|
|
writer = new GenericWriter(writer);
|
|
await initStream(writer, passThrough ? compressedSize : uncompressedSize);
|
|
({ writable } = writer);
|
|
const { outputSize } = await runWorker({ readable, writable }, workerOptions);
|
|
writer.size += outputSize;
|
|
if (outputSize != (passThrough ? compressedSize : uncompressedSize)) {
|
|
throw new Error(ERR_INVALID_UNCOMPRESSED_SIZE);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
if (error.outputSize !== UNDEFINED_VALUE) {
|
|
writer.size += error.outputSize;
|
|
}
|
|
if (!checkPasswordOnly || error.message != ERR_ABORT_CHECK_PASSWORD) {
|
|
throw error;
|
|
}
|
|
} finally {
|
|
const preventClose = getOptionValue$1(zipEntry, options, OPTION_PREVENT_CLOSE);
|
|
if (!preventClose && writable && !writable.locked) {
|
|
await writable.getWriter().close();
|
|
}
|
|
}
|
|
return checkPasswordOnly || checkOverlappingEntryOnly ? UNDEFINED_VALUE : writer.getData ? writer.getData() : writable;
|
|
}
|
|
};
|
|
|
|
function readCommonHeader(directory, dataView, offset) {
|
|
const rawBitFlag = directory.rawBitFlag = getUint16(dataView, offset + 2);
|
|
const encrypted = (rawBitFlag & BITFLAG_ENCRYPTED) == BITFLAG_ENCRYPTED;
|
|
const rawLastModDate = getUint32(dataView, offset + 6);
|
|
Object.assign(directory, {
|
|
encrypted,
|
|
version: getUint16(dataView, offset),
|
|
bitFlag: {
|
|
level: (rawBitFlag & BITFLAG_LEVEL) >> 1,
|
|
dataDescriptor: (rawBitFlag & BITFLAG_DATA_DESCRIPTOR) == BITFLAG_DATA_DESCRIPTOR,
|
|
languageEncodingFlag: (rawBitFlag & BITFLAG_LANG_ENCODING_FLAG) == BITFLAG_LANG_ENCODING_FLAG
|
|
},
|
|
rawLastModDate,
|
|
lastModDate: getDate(rawLastModDate),
|
|
filenameLength: getUint16(dataView, offset + 22),
|
|
extraFieldLength: getUint16(dataView, offset + 24)
|
|
});
|
|
}
|
|
|
|
function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory) {
|
|
const { rawExtraField } = directory;
|
|
const extraField = directory.extraField = new Map();
|
|
const rawExtraFieldView = getDataView$1(new Uint8Array(rawExtraField));
|
|
let offsetExtraField = 0;
|
|
try {
|
|
while (offsetExtraField < rawExtraField.length) {
|
|
const type = getUint16(rawExtraFieldView, offsetExtraField);
|
|
const size = getUint16(rawExtraFieldView, offsetExtraField + 2);
|
|
extraField.set(type, {
|
|
type,
|
|
data: rawExtraField.slice(offsetExtraField + 4, offsetExtraField + 4 + size)
|
|
});
|
|
offsetExtraField += 4 + size;
|
|
}
|
|
} catch {
|
|
// ignored
|
|
}
|
|
const compressionMethod = getUint16(dataView, offset + 4);
|
|
Object.assign(directory, {
|
|
signature: getUint32(dataView, offset + HEADER_OFFSET_SIGNATURE),
|
|
compressedSize: getUint32(dataView, offset + HEADER_OFFSET_COMPRESSED_SIZE),
|
|
uncompressedSize: getUint32(dataView, offset + HEADER_OFFSET_UNCOMPRESSED_SIZE)
|
|
});
|
|
const extraFieldZip64 = extraField.get(EXTRAFIELD_TYPE_ZIP64);
|
|
if (extraFieldZip64) {
|
|
readExtraFieldZip64(extraFieldZip64, directory);
|
|
directory.extraFieldZip64 = extraFieldZip64;
|
|
}
|
|
const extraFieldUnicodePath = extraField.get(EXTRAFIELD_TYPE_UNICODE_PATH);
|
|
if (extraFieldUnicodePath) {
|
|
readExtraFieldUnicode(extraFieldUnicodePath, PROPERTY_NAME_FILENAME, PROPERTY_NAME_RAW_FILENAME, directory, fileEntry);
|
|
directory.extraFieldUnicodePath = extraFieldUnicodePath;
|
|
}
|
|
const extraFieldUnicodeComment = extraField.get(EXTRAFIELD_TYPE_UNICODE_COMMENT);
|
|
if (extraFieldUnicodeComment) {
|
|
readExtraFieldUnicode(extraFieldUnicodeComment, PROPERTY_NAME_COMMENT, PROPERTY_NAME_RAW_COMMENT, directory, fileEntry);
|
|
directory.extraFieldUnicodeComment = extraFieldUnicodeComment;
|
|
}
|
|
const extraFieldAES = extraField.get(EXTRAFIELD_TYPE_AES);
|
|
if (extraFieldAES && extraFieldAES.data.length >= 7) {
|
|
readExtraFieldAES(extraFieldAES, directory, compressionMethod);
|
|
directory.extraFieldAES = extraFieldAES;
|
|
} else {
|
|
directory.compressionMethod = compressionMethod;
|
|
}
|
|
const extraFieldNTFS = extraField.get(EXTRAFIELD_TYPE_NTFS);
|
|
if (extraFieldNTFS) {
|
|
readExtraFieldNTFS(extraFieldNTFS, directory);
|
|
directory.extraFieldNTFS = extraFieldNTFS;
|
|
}
|
|
const extraFieldUnix = extraField.get(EXTRAFIELD_TYPE_UNIX);
|
|
if (extraFieldUnix) {
|
|
readExtraFieldUnix(extraFieldUnix, directory, false);
|
|
directory.extraFieldUnix = extraFieldUnix;
|
|
} else {
|
|
const extraFieldInfoZip = extraField.get(EXTRAFIELD_TYPE_INFOZIP);
|
|
if (extraFieldInfoZip) {
|
|
readExtraFieldUnix(extraFieldInfoZip, directory, true);
|
|
directory.extraFieldInfoZip = extraFieldInfoZip;
|
|
}
|
|
}
|
|
const extraFieldExtendedTimestamp = extraField.get(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
|
|
if (extraFieldExtendedTimestamp) {
|
|
readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory, localDirectory);
|
|
directory.extraFieldExtendedTimestamp = extraFieldExtendedTimestamp;
|
|
}
|
|
const extraFieldUSDZ = extraField.get(EXTRAFIELD_TYPE_USDZ);
|
|
if (extraFieldUSDZ) {
|
|
directory.extraFieldUSDZ = extraFieldUSDZ;
|
|
}
|
|
}
|
|
|
|
function readExtraFieldZip64(extraFieldZip64, directory) {
|
|
directory.zip64 = true;
|
|
const extraFieldView = getDataView$1(extraFieldZip64.data);
|
|
const missingProperties = ZIP64_PROPERTIES.filter(([propertyName, max]) => directory[propertyName] == max);
|
|
const requiredLength = missingProperties.reduce((length, [, max]) => length + ZIP64_EXTRACTION[max].bytes, 0);
|
|
if (extraFieldZip64.data.length < requiredLength) {
|
|
throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
|
|
}
|
|
for (let indexMissingProperty = 0, offset = 0; indexMissingProperty < missingProperties.length; indexMissingProperty++) {
|
|
const [propertyName, max] = missingProperties[indexMissingProperty];
|
|
const extraction = ZIP64_EXTRACTION[max];
|
|
directory[propertyName] = extraFieldZip64[propertyName] = extraction.getValue(extraFieldView, offset);
|
|
offset += extraction.bytes;
|
|
}
|
|
}
|
|
|
|
function readExtraFieldUnicode(extraFieldUnicode, propertyName, rawPropertyName, directory, fileEntry) {
|
|
if (extraFieldUnicode.data.length < 5) {
|
|
extraFieldUnicode.valid = false;
|
|
return;
|
|
}
|
|
const extraFieldView = getDataView$1(extraFieldUnicode.data);
|
|
const crc32 = new Crc32();
|
|
crc32.append(fileEntry[rawPropertyName]);
|
|
const dataViewSignature = getDataView$1(new Uint8Array(4));
|
|
dataViewSignature.setUint32(0, crc32.get(), true);
|
|
const signature = getUint32(extraFieldView, 1);
|
|
Object.assign(extraFieldUnicode, {
|
|
version: getUint8(extraFieldView, 0),
|
|
[propertyName]: decodeText(extraFieldUnicode.data.subarray(5)),
|
|
valid: !fileEntry.bitFlag.languageEncodingFlag && signature == getUint32(dataViewSignature, 0)
|
|
});
|
|
if (extraFieldUnicode.valid) {
|
|
directory[propertyName] = extraFieldUnicode[propertyName];
|
|
directory[propertyName + PROPERTY_NAME_UTF8_SUFFIX] = true;
|
|
}
|
|
}
|
|
|
|
function readExtraFieldAES(extraFieldAES, directory, compressionMethod) {
|
|
const extraFieldView = getDataView$1(extraFieldAES.data);
|
|
const strength = getUint8(extraFieldView, 4);
|
|
Object.assign(extraFieldAES, {
|
|
vendorVersion: getUint8(extraFieldView, 0),
|
|
vendorId: getUint8(extraFieldView, 2),
|
|
strength,
|
|
originalCompressionMethod: compressionMethod,
|
|
compressionMethod: getUint16(extraFieldView, 5)
|
|
});
|
|
directory.compressionMethod = extraFieldAES.compressionMethod;
|
|
}
|
|
|
|
function readExtraFieldNTFS(extraFieldNTFS, directory) {
|
|
const extraFieldView = getDataView$1(extraFieldNTFS.data);
|
|
let offsetExtraField = 4;
|
|
let tag1Data;
|
|
try {
|
|
while (offsetExtraField < extraFieldNTFS.data.length && !tag1Data) {
|
|
const tagValue = getUint16(extraFieldView, offsetExtraField);
|
|
const attributeSize = getUint16(extraFieldView, offsetExtraField + 2);
|
|
if (tagValue == EXTRAFIELD_TYPE_NTFS_TAG1) {
|
|
tag1Data = extraFieldNTFS.data.slice(offsetExtraField + 4, offsetExtraField + 4 + attributeSize);
|
|
}
|
|
offsetExtraField += 4 + attributeSize;
|
|
}
|
|
} catch {
|
|
// ignored
|
|
}
|
|
try {
|
|
if (tag1Data && tag1Data.length == 24) {
|
|
const tag1View = getDataView$1(tag1Data);
|
|
const rawLastModDate = tag1View.getBigUint64(0, true);
|
|
const rawLastAccessDate = tag1View.getBigUint64(8, true);
|
|
const rawCreationDate = tag1View.getBigUint64(16, true);
|
|
Object.assign(extraFieldNTFS, {
|
|
rawLastModDate,
|
|
rawLastAccessDate,
|
|
rawCreationDate
|
|
});
|
|
const lastModDate = getDateNTFS(rawLastModDate);
|
|
const lastAccessDate = getDateNTFS(rawLastAccessDate);
|
|
const creationDate = getDateNTFS(rawCreationDate);
|
|
const extraFieldData = { lastModDate, lastAccessDate, creationDate };
|
|
Object.assign(extraFieldNTFS, extraFieldData);
|
|
Object.assign(directory, extraFieldData);
|
|
}
|
|
} catch {
|
|
// ignored
|
|
}
|
|
}
|
|
|
|
function readExtraFieldUnix(extraField, directory, isInfoZip) {
|
|
try {
|
|
const view = getDataView$1(new Uint8Array(extraField.data));
|
|
let uid, gid;
|
|
if (isInfoZip) {
|
|
let offset = 0;
|
|
const version = getUint8(view, offset++);
|
|
const uidSize = getUint8(view, offset++);
|
|
uid = unpackUnixId(extraField.data.subarray(offset, offset + uidSize));
|
|
offset += uidSize;
|
|
const gidSize = getUint8(view, offset++);
|
|
gid = unpackUnixId(extraField.data.subarray(offset, offset + gidSize));
|
|
Object.assign(extraField, { version, uid, gid });
|
|
} else if (extraField.data.length >= 4) {
|
|
uid = getUint16(view, 0);
|
|
gid = getUint16(view, 2);
|
|
Object.assign(extraField, { uid, gid });
|
|
}
|
|
if (uid !== UNDEFINED_VALUE) {
|
|
directory.uid = uid;
|
|
}
|
|
if (gid !== UNDEFINED_VALUE) {
|
|
directory.gid = gid;
|
|
}
|
|
} catch {
|
|
// ignored
|
|
}
|
|
}
|
|
|
|
function unpackUnixId(bytes) {
|
|
const buffer = new Uint8Array(4);
|
|
buffer.set(bytes, 0);
|
|
const view = new DataView(buffer.buffer, buffer.byteOffset, 4);
|
|
return view.getUint32(0, true);
|
|
}
|
|
|
|
function readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory, localDirectory) {
|
|
if (!extraFieldExtendedTimestamp.data.length) {
|
|
return;
|
|
}
|
|
const extraFieldView = getDataView$1(extraFieldExtendedTimestamp.data);
|
|
const flags = getUint8(extraFieldView, 0);
|
|
const timeProperties = [];
|
|
const timeRawProperties = [];
|
|
if (localDirectory) {
|
|
if ((flags & 0x1) == 0x1) {
|
|
timeProperties.push(PROPERTY_NAME_LAST_MODIFICATION_DATE);
|
|
timeRawProperties.push(PROPERTY_NAME_RAW_LAST_MODIFICATION_DATE);
|
|
}
|
|
if ((flags & 0x2) == 0x2) {
|
|
timeProperties.push(PROPERTY_NAME_LAST_ACCESS_DATE);
|
|
timeRawProperties.push(PROPERTY_NAME_RAW_LAST_ACCESS_DATE);
|
|
}
|
|
if ((flags & 0x4) == 0x4) {
|
|
timeProperties.push(PROPERTY_NAME_CREATION_DATE);
|
|
timeRawProperties.push(PROPERTY_NAME_RAW_CREATION_DATE);
|
|
}
|
|
} else if (extraFieldExtendedTimestamp.data.length >= 5) {
|
|
timeProperties.push(PROPERTY_NAME_LAST_MODIFICATION_DATE);
|
|
timeRawProperties.push(PROPERTY_NAME_RAW_LAST_MODIFICATION_DATE);
|
|
}
|
|
let offset = 1;
|
|
timeProperties.forEach((propertyName, indexProperty) => {
|
|
if (extraFieldExtendedTimestamp.data.length >= offset + 4) {
|
|
const time = getUint32(extraFieldView, offset);
|
|
directory[propertyName] = extraFieldExtendedTimestamp[propertyName] = new Date((time | 0) * 1000);
|
|
const rawPropertyName = timeRawProperties[indexProperty];
|
|
extraFieldExtendedTimestamp[rawPropertyName] = time;
|
|
}
|
|
offset += 4;
|
|
});
|
|
}
|
|
|
|
async function detectOverlappingEntry({
|
|
reader,
|
|
fileEntry,
|
|
index,
|
|
offset,
|
|
diskNumberStart,
|
|
signature,
|
|
compressedSize,
|
|
uncompressedSize,
|
|
dataOffset,
|
|
dataDescriptor,
|
|
extraFieldZip64,
|
|
readRanges
|
|
}) {
|
|
let diskOffset = 0;
|
|
if (diskNumberStart && reader.readers) {
|
|
for (let indexReader = 0; indexReader < Math.min(diskNumberStart, reader.readers.length); indexReader++) {
|
|
diskOffset += reader.readers[indexReader].size;
|
|
}
|
|
}
|
|
let dataDescriptorLength = 0;
|
|
if (dataDescriptor) {
|
|
if (extraFieldZip64) {
|
|
dataDescriptorLength = DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH;
|
|
} else {
|
|
dataDescriptorLength = DATA_DESCRIPTOR_RECORD_LENGTH;
|
|
}
|
|
}
|
|
if (dataDescriptorLength) {
|
|
const dataDescriptorArray = await readUint8Array(reader, dataOffset + compressedSize, dataDescriptorLength + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH, diskNumberStart);
|
|
const dataDescriptorSignature = dataDescriptorArray.length == dataDescriptorLength + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH &&
|
|
getUint32(getDataView$1(dataDescriptorArray), 0) == DATA_DESCRIPTOR_RECORD_SIGNATURE;
|
|
if (dataDescriptorSignature) {
|
|
const readSignature = getUint32(getDataView$1(dataDescriptorArray), 4);
|
|
let readCompressedSize;
|
|
let readUncompressedSize;
|
|
if (extraFieldZip64) {
|
|
readCompressedSize = getBigUint64(getDataView$1(dataDescriptorArray), 8);
|
|
readUncompressedSize = getBigUint64(getDataView$1(dataDescriptorArray), 16);
|
|
} else {
|
|
readCompressedSize = getUint32(getDataView$1(dataDescriptorArray), 8);
|
|
readUncompressedSize = getUint32(getDataView$1(dataDescriptorArray), 12);
|
|
}
|
|
const matchSignature = (fileEntry.encrypted && !fileEntry.zipCrypto) || readSignature == signature;
|
|
if (matchSignature &&
|
|
readCompressedSize == compressedSize &&
|
|
readUncompressedSize == uncompressedSize) {
|
|
dataDescriptorLength += DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
|
|
}
|
|
}
|
|
}
|
|
const range = {
|
|
start: diskOffset + offset,
|
|
end: diskOffset + dataOffset + compressedSize + dataDescriptorLength,
|
|
fileEntry
|
|
};
|
|
for (const [otherIndex, otherRange] of readRanges) {
|
|
if (otherIndex != index && range.start < otherRange.end && otherRange.start < range.end) {
|
|
const error = new Error(ERR_OVERLAPPING_ENTRY);
|
|
error.overlappingEntry = otherRange.fileEntry;
|
|
throw error;
|
|
}
|
|
}
|
|
readRanges.set(index, range);
|
|
}
|
|
|
|
function getStrictness(strictness, checkAmbiguity) {
|
|
if (strictness === UNDEFINED_VALUE) {
|
|
return checkAmbiguity ? STRICTNESS_STRICT : STRICTNESS_BALANCED;
|
|
}
|
|
return strictness;
|
|
}
|
|
|
|
function getMaxAppendedDataSize(maxAppendedDataSize, strictness) {
|
|
if (maxAppendedDataSize !== UNDEFINED_VALUE) {
|
|
return maxAppendedDataSize;
|
|
}
|
|
if (strictness == STRICTNESS_STRICT) {
|
|
return 0;
|
|
}
|
|
if (strictness == STRICTNESS_TOLERANT) {
|
|
return Infinity;
|
|
}
|
|
return MAX_16_BITS;
|
|
}
|
|
|
|
const MAX_END_OF_CENTRAL_DIR_PROBES = 64;
|
|
|
|
const CENTRAL_DIRECTORY_UNREACHABLE = 0;
|
|
const CENTRAL_DIRECTORY_PLAUSIBLE = 1;
|
|
const CENTRAL_DIRECTORY_REACHABLE = 2;
|
|
|
|
async function findEndOfCentralDirectory(reader, rejectAmbiguous, maxAppendedDataSize) {
|
|
const { size } = reader;
|
|
const anchoredLength = Math.min(size, END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS);
|
|
const remoteProbeBudget = { count: MAX_END_OF_CENTRAL_DIR_PROBES };
|
|
let endOfDirectoryInfo;
|
|
let plausibleEndOfDirectoryInfo;
|
|
let endOfDirectoryReachingEndCount = 0;
|
|
for await (const [anchoredView, anchoredOffset, anchoredArray, indexByte, offset] of scanEndOfCentralDirectory(reader, anchoredLength)) {
|
|
const commentLength = getUint16(anchoredView, indexByte + 20);
|
|
if (offset + END_OF_CENTRAL_DIR_LENGTH + commentLength == size) {
|
|
const reachability = await getCentralDirectoryReachability(reader, anchoredView, anchoredOffset, indexByte, offset, size, remoteProbeBudget);
|
|
if (reachability == CENTRAL_DIRECTORY_REACHABLE) {
|
|
if (!endOfDirectoryInfo) {
|
|
endOfDirectoryInfo = getEndOfCentralDirectoryInfo(anchoredArray, indexByte, offset);
|
|
}
|
|
endOfDirectoryReachingEndCount++;
|
|
if (!rejectAmbiguous || endOfDirectoryReachingEndCount > 1) {
|
|
break;
|
|
}
|
|
} else if (reachability == CENTRAL_DIRECTORY_PLAUSIBLE && !plausibleEndOfDirectoryInfo) {
|
|
plausibleEndOfDirectoryInfo = getEndOfCentralDirectoryInfo(anchoredArray, indexByte, offset);
|
|
}
|
|
}
|
|
}
|
|
if (!endOfDirectoryInfo) {
|
|
endOfDirectoryInfo = plausibleEndOfDirectoryInfo;
|
|
}
|
|
if (!endOfDirectoryInfo) {
|
|
endOfDirectoryInfo = await seekEndOfCentralDirectory(reader, maxAppendedDataSize, remoteProbeBudget);
|
|
}
|
|
return { endOfDirectoryInfo, endOfDirectoryReachingEndCount };
|
|
}
|
|
|
|
async function seekEndOfCentralDirectory(reader, maxAppendedDataSize, remoteProbeBudget) {
|
|
const { size } = reader;
|
|
const searchLength = Math.min(size, maxAppendedDataSize == Infinity ? size :
|
|
END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS + maxAppendedDataSize);
|
|
let firstSignatureInfo, plausibleInfo;
|
|
for await (const [searchView, searchOffset, searchArray, indexByte, offset] of scanEndOfCentralDirectory(reader, searchLength)) {
|
|
const record = getEndOfCentralDirectoryInfo(searchArray, indexByte, offset);
|
|
if (!firstSignatureInfo) {
|
|
firstSignatureInfo = record;
|
|
}
|
|
const reachability = await getCentralDirectoryReachability(reader, searchView, searchOffset, indexByte, offset, size, remoteProbeBudget);
|
|
if (reachability == CENTRAL_DIRECTORY_REACHABLE) {
|
|
return record;
|
|
}
|
|
if (reachability == CENTRAL_DIRECTORY_PLAUSIBLE && !plausibleInfo) {
|
|
plausibleInfo = record;
|
|
}
|
|
}
|
|
return plausibleInfo || firstSignatureInfo;
|
|
}
|
|
|
|
async function* scanEndOfCentralDirectory(reader, scanLength) {
|
|
const scanOffset = reader.size - scanLength;
|
|
const scanArray = await readUint8Array(reader, scanOffset, scanLength);
|
|
const scanView = getDataView$1(scanArray);
|
|
for (let indexByte = scanArray.length - END_OF_CENTRAL_DIR_LENGTH; indexByte >= 0; indexByte--) {
|
|
if (getUint32(scanView, indexByte) == END_OF_CENTRAL_DIR_SIGNATURE) {
|
|
yield [scanView, scanOffset, scanArray, indexByte, scanOffset + indexByte];
|
|
}
|
|
}
|
|
}
|
|
|
|
function getEndOfCentralDirectoryInfo(scanArray, indexByte, offset) {
|
|
return { offset, buffer: scanArray.slice(indexByte, indexByte + END_OF_CENTRAL_DIR_LENGTH).buffer };
|
|
}
|
|
|
|
async function getCentralDirectoryReachability(reader, view, anchoredOffset, indexByte, offset, size, remoteProbeBudget) {
|
|
const filesLength = getUint16(view, indexByte + 10);
|
|
const directoryDataLength = getUint32(view, indexByte + 12);
|
|
const directoryDataOffset = getUint32(view, indexByte + 16);
|
|
if (filesLength == MAX_16_BITS || directoryDataLength == MAX_32_BITS || directoryDataOffset == MAX_32_BITS) {
|
|
const locatorSignature = await readSignature(reader, view, anchoredOffset, offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH, size, remoteProbeBudget);
|
|
return locatorSignature == ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE ? CENTRAL_DIRECTORY_REACHABLE : CENTRAL_DIRECTORY_UNREACHABLE;
|
|
}
|
|
if (!filesLength && !directoryDataLength) {
|
|
return CENTRAL_DIRECTORY_PLAUSIBLE;
|
|
}
|
|
for (const centralDirectoryOffset of [offset - directoryDataLength, directoryDataOffset]) {
|
|
if (await readSignature(reader, view, anchoredOffset, centralDirectoryOffset, size, remoteProbeBudget) == CENTRAL_FILE_HEADER_SIGNATURE) {
|
|
return CENTRAL_DIRECTORY_REACHABLE;
|
|
}
|
|
}
|
|
return CENTRAL_DIRECTORY_UNREACHABLE;
|
|
}
|
|
|
|
async function readSignature(reader, view, anchoredOffset, signatureOffset, size, remoteProbeBudget) {
|
|
if (signatureOffset < 0 || signatureOffset + 4 > size) {
|
|
return UNDEFINED_VALUE;
|
|
}
|
|
if (signatureOffset >= anchoredOffset) {
|
|
return getUint32(view, signatureOffset - anchoredOffset);
|
|
}
|
|
if (remoteProbeBudget.count > 0) {
|
|
remoteProbeBudget.count--;
|
|
const signatureArray = await readUint8Array(reader, signatureOffset, 4);
|
|
return getUint32(getDataView$1(signatureArray), 0);
|
|
}
|
|
return UNDEFINED_VALUE;
|
|
}
|
|
|
|
function checkLocalDirectory(zipEntry, localDirectory, rawLocalFilename) {
|
|
const { rawFilename } = zipEntry;
|
|
if (rawLocalFilename.length != rawFilename.length ||
|
|
rawLocalFilename.some((byteValue, indexByte) => byteValue != rawFilename[indexByte])) {
|
|
throwAmbiguousArchive("mismatched local file header (filename)");
|
|
}
|
|
if ((localDirectory.rawBitFlag & BITFLAG_AMBIGUITY_MASK) != (zipEntry.rawBitFlag & BITFLAG_AMBIGUITY_MASK)) {
|
|
throwAmbiguousArchive("mismatched local file header (general purpose bit flag)");
|
|
}
|
|
if (localDirectory.compressionMethod != zipEntry.compressionMethod) {
|
|
throwAmbiguousArchive("mismatched local file header (compression method)");
|
|
}
|
|
if (!localDirectory.bitFlag.dataDescriptor &&
|
|
(localDirectory.signature || localDirectory.compressedSize || localDirectory.uncompressedSize) &&
|
|
(localDirectory.signature != zipEntry.signature ||
|
|
localDirectory.compressedSize != zipEntry.compressedSize ||
|
|
localDirectory.uncompressedSize != zipEntry.uncompressedSize)) {
|
|
throwAmbiguousArchive("mismatched local file header (signature or sizes)");
|
|
}
|
|
}
|
|
|
|
function throwAmbiguousArchive(reason) {
|
|
const error = new Error(ERR_AMBIGUOUS_ARCHIVE);
|
|
error.reason = reason;
|
|
throw error;
|
|
}
|
|
|
|
function getOptionValue$1(zipReader, options, name) {
|
|
return options[name] === UNDEFINED_VALUE ? zipReader.options[name] : options[name];
|
|
}
|
|
|
|
function getDate(timeRaw) {
|
|
const date = (timeRaw & 0xffff0000) >> 16, time = timeRaw & MAX_16_BITS;
|
|
try {
|
|
return new Date(1980 + ((date & 0xFE00) >> 9), ((date & 0x01E0) >> 5) - 1, date & 0x001F, (time & 0xF800) >> 11, (time & 0x07E0) >> 5, (time & 0x001F) * 2, 0);
|
|
} catch {
|
|
// ignored
|
|
}
|
|
}
|
|
|
|
function getDateNTFS(timeRaw) {
|
|
return new Date((Number((timeRaw / BigInt(10000)) - BigInt(11644473600000))));
|
|
}
|
|
|
|
function getUint8(view, offset) {
|
|
return view.getUint8(offset);
|
|
}
|
|
|
|
function getUint16(view, offset) {
|
|
return view.getUint16(offset, true);
|
|
}
|
|
|
|
function getUint32(view, offset) {
|
|
return view.getUint32(offset, true);
|
|
}
|
|
|
|
function getBigUint64(view, offset) {
|
|
return Number(view.getBigUint64(offset, true));
|
|
}
|
|
|
|
function getDataView$1(array) {
|
|
return new DataView(array.buffer, array.byteOffset, array.byteLength);
|
|
}
|
|
|
|
/*
|
|
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 ERR_DUPLICATED_NAME = "File already exists";
|
|
const ERR_INVALID_COMMENT = "Zip file comment exceeds 64KB";
|
|
const ERR_INVALID_ENTRY_COMMENT = "File entry comment exceeds 64KB";
|
|
const ERR_INVALID_ENTRY_NAME = "File entry name exceeds 64KB";
|
|
const ERR_INVALID_VERSION = "Version exceeds 65535";
|
|
const ERR_INVALID_ENCRYPTION_STRENGTH = "The strength must equal 1, 2, or 3";
|
|
const ERR_INVALID_EXTRAFIELD_TYPE = "Extra field type exceeds 65535";
|
|
const ERR_INVALID_EXTRAFIELD_DATA = "Extra field data exceeds 64KB";
|
|
const ERR_UNSUPPORTED_COMPRESSION = "Compression method not supported";
|
|
const MIN_UNIX_TIME = -2147483648;
|
|
const MAX_UNIX_TIME = 2147483647;
|
|
const ERR_UNSUPPORTED_FORMAT = "Zip64 is not supported (set the 'zip64' option to 'true')";
|
|
const ERR_UNDEFINED_UNCOMPRESSED_SIZE = "Undefined uncompressed size";
|
|
const ERR_UNDEFINED_READER = "Undefined reader";
|
|
const ERR_ZIP_NOT_EMPTY = "Zip file not empty";
|
|
const ERR_INVALID_UID = "Invalid uid (must be integer 0..2^32-1)";
|
|
const ERR_INVALID_GID = "Invalid gid (must be integer 0..2^32-1)";
|
|
const ERR_INVALID_UNIX_MODE = "Invalid UNIX mode (must be integer 0..65535)";
|
|
const ERR_INVALID_UNIX_EXTRA_FIELD_TYPE = "Invalid unixExtraFieldType (must be 'infozip' or 'unix')";
|
|
const ERR_INVALID_UNIX_ID_SIZE = "uid/gid must be 0..65535 for unixExtraFieldType 'unix' (use 'infozip' for larger ids)";
|
|
const ERR_INVALID_MSDOS_ATTRIBUTES = "Invalid msdosAttributesRaw (must be integer 0..255)";
|
|
const ERR_INVALID_MSDOS_DATA = "Invalid msdosAttributes (must be an object with boolean flags)";
|
|
|
|
const EXTRAFIELD_DATA_AES = new Uint8Array([0x07, 0x00, 0x02, 0x00, 0x41, 0x45, 0x03, 0x00, 0x00]);
|
|
const INFOZIP_EXTRA_FIELD_TYPE = "infozip";
|
|
const UNIX_EXTRA_FIELD_TYPE = "unix";
|
|
|
|
let workers = 0;
|
|
const pendingEntries = [];
|
|
|
|
class ZipWriter {
|
|
|
|
constructor(writer, options = {}) {
|
|
writer = new GenericWriter(writer);
|
|
const addSplitZipSignature =
|
|
writer.availableSize !== UNDEFINED_VALUE && writer.availableSize > 0 && writer.availableSize !== INFINITY_VALUE &&
|
|
writer.maxSize !== UNDEFINED_VALUE && writer.maxSize > 0 && writer.maxSize !== INFINITY_VALUE;
|
|
Object.assign(this, {
|
|
writer,
|
|
addSplitZipSignature,
|
|
options,
|
|
config: getConfiguration(),
|
|
files: new Map(),
|
|
filenames: new Set(),
|
|
offset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? writer.size || writer.writable.size || 0 : options[OPTION_OFFSET],
|
|
initialOffset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? 0 : options[OPTION_OFFSET] - (writer.size || writer.writable.size || 0),
|
|
pendingAddFileCalls: new Set(),
|
|
bufferedWrites: 0,
|
|
lastFileEntry: UNDEFINED_VALUE
|
|
});
|
|
}
|
|
|
|
async prependZip(reader) {
|
|
if (this.filenames.size) {
|
|
throw new Error(ERR_ZIP_NOT_EMPTY);
|
|
}
|
|
reader = new GenericReader(reader);
|
|
await initStream(reader);
|
|
const zipReader = new ZipReader(reader.readable);
|
|
const entries = await zipReader.getEntries();
|
|
await zipReader.close();
|
|
await initStream(this.writer);
|
|
await reader.readable.pipeTo(this.writer.writable, { preventClose: true, preventAbort: true });
|
|
this.writer.size = this.offset = reader.size;
|
|
this.filenames = new Set(entries.map(entry => entry.filename));
|
|
this.files = new Map(entries.map(entry => {
|
|
const {
|
|
version,
|
|
rawLastModDate,
|
|
lastAccessDate,
|
|
creationDate,
|
|
rawFilename,
|
|
bitFlag,
|
|
encrypted,
|
|
uncompressedSize,
|
|
compressedSize,
|
|
diskOffset,
|
|
diskNumber,
|
|
zip64
|
|
} = entry;
|
|
let {
|
|
compressionMethod,
|
|
rawExtraFieldZip64,
|
|
rawExtraFieldAES,
|
|
rawExtraFieldExtendedTimestamp,
|
|
rawExtraFieldNTFS,
|
|
rawExtraFieldUnix,
|
|
rawExtraField,
|
|
} = entry;
|
|
const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
|
|
rawExtraFieldZip64 = rawExtraFieldZip64 || EMPTY_UINT8_ARRAY;
|
|
rawExtraFieldAES = rawExtraFieldAES || EMPTY_UINT8_ARRAY;
|
|
rawExtraFieldExtendedTimestamp = rawExtraFieldExtendedTimestamp || EMPTY_UINT8_ARRAY;
|
|
rawExtraFieldNTFS = rawExtraFieldNTFS || EMPTY_UINT8_ARRAY;
|
|
rawExtraFieldUnix = rawExtraFieldUnix || EMPTY_UINT8_ARRAY;
|
|
rawExtraField = rawExtraField || EMPTY_UINT8_ARRAY;
|
|
if (entry.extraFieldAES) {
|
|
compressionMethod = COMPRESSION_METHOD_AES;
|
|
}
|
|
const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField);
|
|
const zip64UncompressedSize = zip64 && uncompressedSize >= MAX_32_BITS;
|
|
const zip64CompressedSize = zip64 && compressedSize >= MAX_32_BITS;
|
|
const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
|
|
const {
|
|
headerArray,
|
|
headerView
|
|
} = getHeaderArrayData({
|
|
version,
|
|
bitFlag: bitFlagValue,
|
|
compressionMethod,
|
|
uncompressedSize,
|
|
compressedSize,
|
|
rawLastModDate,
|
|
rawFilename,
|
|
zip64CompressedSize,
|
|
zip64UncompressedSize,
|
|
extraFieldLength
|
|
});
|
|
const { signature } = entry;
|
|
if (signature !== UNDEFINED_VALUE) {
|
|
setUint32(headerView, HEADER_OFFSET_SIGNATURE, signature);
|
|
}
|
|
Object.assign(entry, {
|
|
zip64UncompressedSize,
|
|
zip64CompressedSize,
|
|
zip64Offset: zip64 && this.offset - diskOffset >= MAX_32_BITS,
|
|
zip64DiskNumberStart: zip64 && diskNumber >= MAX_16_BITS,
|
|
rawExtraFieldZip64,
|
|
rawExtraFieldAES,
|
|
rawExtraFieldExtendedTimestamp,
|
|
rawExtraFieldNTFS,
|
|
rawExtraFieldUnix,
|
|
rawExtraField,
|
|
extendedTimestamp: rawExtraFieldExtendedTimestamp.length > 0 || rawExtraFieldNTFS.length > 0,
|
|
extraFieldExtendedTimestampFlag: 0x1 + (lastAccessDate ? 0x2 : 0) + (creationDate ? 0x4 : 0),
|
|
headerArray,
|
|
headerView
|
|
});
|
|
return [entry.filename, entry];
|
|
}));
|
|
}
|
|
|
|
async add(name = "", reader, options = {}) {
|
|
const zipWriter = this;
|
|
options = Object.assign({}, options);
|
|
const {
|
|
pendingAddFileCalls,
|
|
config
|
|
} = zipWriter;
|
|
if (workers < config.maxWorkers) {
|
|
workers++;
|
|
} else {
|
|
await new Promise(resolve => pendingEntries.push(resolve));
|
|
}
|
|
let promiseAddFile;
|
|
let nameAdded;
|
|
try {
|
|
name = name.trim();
|
|
if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
|
|
name += DIRECTORY_SIGNATURE;
|
|
}
|
|
if (zipWriter.filenames.has(name)) {
|
|
throw new Error(ERR_DUPLICATED_NAME);
|
|
}
|
|
zipWriter.filenames.add(name);
|
|
nameAdded = true;
|
|
promiseAddFile = addFile(zipWriter, name, reader, options);
|
|
pendingAddFileCalls.add(promiseAddFile);
|
|
return await promiseAddFile;
|
|
} catch (error) {
|
|
if (nameAdded) {
|
|
zipWriter.filenames.delete(name);
|
|
}
|
|
throw error;
|
|
} finally {
|
|
pendingAddFileCalls.delete(promiseAddFile);
|
|
const pendingEntry = pendingEntries.shift();
|
|
if (pendingEntry) {
|
|
pendingEntry();
|
|
} else {
|
|
workers--;
|
|
}
|
|
}
|
|
}
|
|
|
|
remove(entry) {
|
|
const { filenames, files } = this;
|
|
if (typeof entry == "string") {
|
|
entry = files.get(entry);
|
|
}
|
|
if (entry && entry.filename !== UNDEFINED_VALUE) {
|
|
const { filename } = entry;
|
|
if (filenames.has(filename) && files.has(filename)) {
|
|
filenames.delete(filename);
|
|
files.delete(filename);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
async close(comment = EMPTY_UINT8_ARRAY, options = {}) {
|
|
const zipWriter = this;
|
|
const { pendingAddFileCalls, writer } = this;
|
|
const { writable } = writer;
|
|
if (getLength(comment) > MAX_16_BITS) {
|
|
throw new Error(ERR_INVALID_COMMENT);
|
|
}
|
|
while (pendingAddFileCalls.size) {
|
|
await Promise.allSettled(Array.from(pendingAddFileCalls));
|
|
}
|
|
await closeFile(zipWriter, comment, options);
|
|
const preventClose = getOptionValue(zipWriter, options, OPTION_PREVENT_CLOSE);
|
|
if (!preventClose) {
|
|
await writable.getWriter().close();
|
|
}
|
|
return writer.getData ? writer.getData() : writable;
|
|
}
|
|
}
|
|
|
|
class ZipWriterStream {
|
|
|
|
constructor(options = {}) {
|
|
const { readable, writable } = new TransformStream();
|
|
this.readable = readable;
|
|
this.zipWriter = new ZipWriter(writable, options);
|
|
this.pendingAddFileCalls = new Set();
|
|
}
|
|
|
|
transform(path) {
|
|
const zipWriter = this.zipWriter;
|
|
let streamController;
|
|
const { readable, writable } = new TransformStream({
|
|
start(controller) {
|
|
streamController = controller;
|
|
},
|
|
flush: () => void closeArchive()
|
|
});
|
|
watchAddFileCall(this, this.zipWriter.add(path, readable), error => streamController.error(error));
|
|
return { readable: this.readable, writable };
|
|
|
|
async function closeArchive() {
|
|
try {
|
|
await zipWriter.close();
|
|
} catch (error) {
|
|
try {
|
|
await zipWriter.writer.writable.abort(error);
|
|
} catch {
|
|
// ignored
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
writable(path) {
|
|
let streamController;
|
|
const { readable, writable } = new TransformStream({
|
|
start(controller) {
|
|
streamController = controller;
|
|
}
|
|
});
|
|
watchAddFileCall(this, this.zipWriter.add(path, readable), error => streamController.error(error));
|
|
return writable;
|
|
}
|
|
|
|
async close(comment = UNDEFINED_VALUE, options = {}) {
|
|
await Promise.all(Array.from(this.pendingAddFileCalls));
|
|
return this.zipWriter.close(comment, options);
|
|
}
|
|
}
|
|
|
|
function watchAddFileCall(zipWriterStream, promiseAddFile, onerror) {
|
|
zipWriterStream.pendingAddFileCalls.add(promiseAddFile);
|
|
promiseAddFile.catch(error => {
|
|
try {
|
|
onerror(error);
|
|
} catch {
|
|
// ignored
|
|
}
|
|
});
|
|
}
|
|
|
|
async function addFile(zipWriter, name, reader, options) {
|
|
const attributesInfo = resolveAttributes(zipWriter, name, options);
|
|
({ name } = attributesInfo);
|
|
const metadataInfo = resolveMetadata(zipWriter, name, options);
|
|
const { comment } = metadataInfo;
|
|
const extraField = options[PROPERTY_NAME_EXTRA_FIELD];
|
|
zipWriter.files.set(name, UNDEFINED_VALUE);
|
|
let fileEntry;
|
|
try {
|
|
const sizesInfo = await resolveSizes(zipWriter, reader, metadataInfo, options);
|
|
({ reader } = sizesInfo);
|
|
const { diskOffset, diskNumber } = zipWriter.writer;
|
|
options = Object.assign({}, options, attributesInfo.resolvedOptions, metadataInfo.resolvedOptions, sizesInfo.resolvedOptions, {
|
|
internalFileAttribute: metadataInfo.resolvedOptions.internalFileAttributes,
|
|
externalFileAttribute: attributesInfo.resolvedOptions.externalFileAttributes,
|
|
signature: options[PROPERTY_NAME_SIGNATURE],
|
|
offset: zipWriter.offset - diskOffset,
|
|
diskNumberStart: diskNumber
|
|
});
|
|
const headerInfo = getHeaderInfo(options);
|
|
const dataDescriptorInfo = getDataDescriptorInfo(options);
|
|
const metadataSize = getLength(headerInfo.localHeaderArray, dataDescriptorInfo.dataDescriptorArray);
|
|
fileEntry = await getFileEntry(zipWriter, name, reader, { headerInfo, dataDescriptorInfo, metadataSize }, options);
|
|
} catch (error) {
|
|
zipWriter.files.delete(name);
|
|
throw error;
|
|
}
|
|
Object.assign(fileEntry, { name, comment, extraField });
|
|
return new Entry(fileEntry);
|
|
}
|
|
|
|
function resolveAttributes(zipWriter, name, options) {
|
|
name = name.trim();
|
|
let msDosCompatible = getOptionValue(zipWriter, options, PROPERTY_NAME_MS_DOS_COMPATIBLE);
|
|
let versionMadeBy = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION_MADE_BY, msDosCompatible ? 20 : 768);
|
|
const executable = getOptionValue(zipWriter, options, PROPERTY_NAME_EXECUTABLE);
|
|
const uid = getOptionValue(zipWriter, options, PROPERTY_NAME_UID);
|
|
const gid = getOptionValue(zipWriter, options, PROPERTY_NAME_GID);
|
|
let unixMode = getOptionValue(zipWriter, options, PROPERTY_NAME_UNIX_MODE);
|
|
let unixExtraFieldType = getOptionValue(zipWriter, options, OPTION_UNIX_EXTRA_FIELD_TYPE);
|
|
let setuid = getOptionValue(zipWriter, options, PROPERTY_NAME_SETUID);
|
|
let setgid = getOptionValue(zipWriter, options, PROPERTY_NAME_SETGID);
|
|
let sticky = getOptionValue(zipWriter, options, PROPERTY_NAME_STICKY);
|
|
if (uid !== UNDEFINED_VALUE && (uid < 0 || uid > MAX_32_BITS)) {
|
|
throw new Error(ERR_INVALID_UID);
|
|
}
|
|
if (gid !== UNDEFINED_VALUE && (gid < 0 || gid > MAX_32_BITS)) {
|
|
throw new Error(ERR_INVALID_GID);
|
|
}
|
|
if (unixMode !== UNDEFINED_VALUE && (unixMode < 0 || unixMode > MAX_16_BITS)) {
|
|
throw new Error(ERR_INVALID_UNIX_MODE);
|
|
}
|
|
if (unixExtraFieldType !== UNDEFINED_VALUE && unixExtraFieldType !== INFOZIP_EXTRA_FIELD_TYPE && unixExtraFieldType !== UNIX_EXTRA_FIELD_TYPE) {
|
|
throw new Error(ERR_INVALID_UNIX_EXTRA_FIELD_TYPE);
|
|
}
|
|
if (unixExtraFieldType === UNIX_EXTRA_FIELD_TYPE &&
|
|
((uid !== UNDEFINED_VALUE && uid > MAX_16_BITS) || (gid !== UNDEFINED_VALUE && gid > MAX_16_BITS))) {
|
|
throw new Error(ERR_INVALID_UNIX_ID_SIZE);
|
|
}
|
|
if (unixExtraFieldType === UNDEFINED_VALUE && (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE)) {
|
|
unixExtraFieldType = INFOZIP_EXTRA_FIELD_TYPE;
|
|
}
|
|
let msdosAttributesRaw = getOptionValue(zipWriter, options, PROPERTY_NAME_MSDOS_ATTRIBUTES_RAW);
|
|
let msdosAttributes = getOptionValue(zipWriter, options, PROPERTY_NAME_MSDOS_ATTRIBUTES);
|
|
const hasUnixMetadata = uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE || unixMode !== UNDEFINED_VALUE || unixExtraFieldType;
|
|
const hasMsDosProvided = msdosAttributesRaw !== UNDEFINED_VALUE || msdosAttributes !== UNDEFINED_VALUE;
|
|
if (hasUnixMetadata) {
|
|
msDosCompatible = false;
|
|
versionMadeBy = (versionMadeBy & MAX_16_BITS) | (3 << 8);
|
|
} else if (hasMsDosProvided) {
|
|
msDosCompatible = true;
|
|
versionMadeBy = (versionMadeBy & MAX_8_BITS);
|
|
}
|
|
if (msdosAttributesRaw !== UNDEFINED_VALUE && (msdosAttributesRaw < 0 || msdosAttributesRaw > MAX_8_BITS)) {
|
|
throw new Error(ERR_INVALID_MSDOS_ATTRIBUTES);
|
|
}
|
|
if (msdosAttributes && typeof msdosAttributes !== OBJECT_TYPE) {
|
|
throw new Error(ERR_INVALID_MSDOS_DATA);
|
|
}
|
|
if (versionMadeBy > MAX_16_BITS) {
|
|
throw new Error(ERR_INVALID_VERSION);
|
|
}
|
|
let externalFileAttributes = getOptionValue(zipWriter, options, PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTES, 0);
|
|
if (!options[PROPERTY_NAME_DIRECTORY] && name.endsWith(DIRECTORY_SIGNATURE)) {
|
|
options[PROPERTY_NAME_DIRECTORY] = true;
|
|
}
|
|
const directory = getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY);
|
|
if (directory) {
|
|
if (!name.endsWith(DIRECTORY_SIGNATURE)) {
|
|
name += DIRECTORY_SIGNATURE;
|
|
}
|
|
if (externalFileAttributes === 0) {
|
|
externalFileAttributes = FILE_ATTR_MSDOS_DIR_MASK;
|
|
if (!msDosCompatible) {
|
|
externalFileAttributes |= (FILE_ATTR_UNIX_TYPE_DIR | FILE_ATTR_UNIX_EXECUTABLE_MASK | FILE_ATTR_UNIX_DEFAULT_MASK) << 16;
|
|
}
|
|
}
|
|
} else if (!msDosCompatible && externalFileAttributes === 0) {
|
|
if (executable) {
|
|
externalFileAttributes = (FILE_ATTR_UNIX_EXECUTABLE_MASK | FILE_ATTR_UNIX_DEFAULT_MASK) << 16;
|
|
} else {
|
|
externalFileAttributes = FILE_ATTR_UNIX_DEFAULT_MASK << 16;
|
|
}
|
|
}
|
|
let unixExternalUpper;
|
|
if (!msDosCompatible) {
|
|
unixExternalUpper = (externalFileAttributes >> 16) & MAX_16_BITS;
|
|
unixMode = unixMode === UNDEFINED_VALUE ? unixExternalUpper : (unixMode & MAX_16_BITS);
|
|
if (setuid) {
|
|
unixMode |= FILE_ATTR_UNIX_SETUID_MASK;
|
|
} else {
|
|
setuid = Boolean(unixMode & FILE_ATTR_UNIX_SETUID_MASK);
|
|
}
|
|
if (setgid) {
|
|
unixMode |= FILE_ATTR_UNIX_SETGID_MASK;
|
|
} else {
|
|
setgid = Boolean(unixMode & FILE_ATTR_UNIX_SETGID_MASK);
|
|
}
|
|
if (sticky) {
|
|
unixMode |= FILE_ATTR_UNIX_STICKY_MASK;
|
|
} else {
|
|
sticky = Boolean(unixMode & FILE_ATTR_UNIX_STICKY_MASK);
|
|
}
|
|
if (directory) {
|
|
unixMode |= FILE_ATTR_UNIX_TYPE_DIR;
|
|
}
|
|
externalFileAttributes = ((unixMode & MAX_16_BITS) << 16) | (externalFileAttributes & MAX_8_BITS);
|
|
}
|
|
({ msdosAttributesRaw, msdosAttributes } = normalizeMsdosAttributes(msdosAttributesRaw, msdosAttributes));
|
|
if (hasMsDosProvided) {
|
|
externalFileAttributes = (externalFileAttributes & MAX_32_BITS) | (msdosAttributesRaw & MAX_8_BITS);
|
|
}
|
|
return {
|
|
name,
|
|
resolvedOptions: {
|
|
versionMadeBy,
|
|
msDosCompatible,
|
|
externalFileAttributes,
|
|
unixExternalUpper,
|
|
uid,
|
|
gid,
|
|
unixMode,
|
|
unixExtraFieldType,
|
|
setuid,
|
|
setgid,
|
|
sticky,
|
|
msdosAttributesRaw,
|
|
msdosAttributes
|
|
}
|
|
};
|
|
}
|
|
|
|
function resolveMetadata(zipWriter, name, options) {
|
|
const encode = getOptionValue(zipWriter, options, OPTION_ENCODE_TEXT, encodeText);
|
|
let rawFilename = encode(name);
|
|
if (rawFilename === UNDEFINED_VALUE) {
|
|
rawFilename = encodeText(name);
|
|
}
|
|
if (getLength(rawFilename) > MAX_16_BITS) {
|
|
throw new Error(ERR_INVALID_ENTRY_NAME);
|
|
}
|
|
const comment = options[PROPERTY_NAME_COMMENT] || "";
|
|
let rawComment = encode(comment);
|
|
if (rawComment === UNDEFINED_VALUE) {
|
|
rawComment = encodeText(comment);
|
|
}
|
|
if (getLength(rawComment) > MAX_16_BITS) {
|
|
throw new Error(ERR_INVALID_ENTRY_COMMENT);
|
|
}
|
|
const version = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION, VERSION_DEFLATE);
|
|
if (version > MAX_16_BITS) {
|
|
throw new Error(ERR_INVALID_VERSION);
|
|
}
|
|
const lastModDate = getOptionValue(zipWriter, options, PROPERTY_NAME_LAST_MODIFICATION_DATE, new Date());
|
|
const lastAccessDate = getOptionValue(zipWriter, options, PROPERTY_NAME_LAST_ACCESS_DATE);
|
|
const creationDate = getOptionValue(zipWriter, options, PROPERTY_NAME_CREATION_DATE);
|
|
const internalFileAttributes = getOptionValue(zipWriter, options, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES, 0);
|
|
const passThrough = getOptionValue(zipWriter, options, OPTION_PASS_THROUGH);
|
|
let password, rawPassword;
|
|
if (!passThrough) {
|
|
password = getOptionValue(zipWriter, options, OPTION_PASSWORD);
|
|
rawPassword = getOptionValue(zipWriter, options, OPTION_RAW_PASSWORD);
|
|
}
|
|
const encryptionStrength = getOptionValue(zipWriter, options, OPTION_ENCRYPTION_STRENGTH, 3);
|
|
const zipCrypto = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIPCRYPTO);
|
|
const extendedTimestamp = getOptionValue(zipWriter, options, OPTION_EXTENDED_TIMESTAMP, true);
|
|
const keepOrder = getOptionValue(zipWriter, options, OPTION_KEEP_ORDER, true);
|
|
const useWebWorkers = getOptionValue(zipWriter, options, OPTION_USE_WEB_WORKERS);
|
|
const transferStreams = getOptionValue(zipWriter, options, OPTION_TRANSFER_STREAMS, true);
|
|
const bufferedWrite = getOptionValue(zipWriter, options, OPTION_BUFFERED_WRITE);
|
|
const createTempStream = getOptionValue(zipWriter, options, OPTION_CREATE_TEMP_STREAM);
|
|
const dataDescriptorSignature = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR_SIGNATURE, true);
|
|
const signal = getOptionValue(zipWriter, options, OPTION_SIGNAL);
|
|
const useUnicodeFileNames = getOptionValue(zipWriter, options, OPTION_USE_UNICODE_FILE_NAMES, true);
|
|
const compressionMethod = getOptionValue(zipWriter, options, PROPERTY_NAME_COMPRESSION_METHOD);
|
|
if (!passThrough && compressionMethod !== UNDEFINED_VALUE &&
|
|
compressionMethod !== COMPRESSION_METHOD_STORE && compressionMethod !== COMPRESSION_METHOD_DEFLATE) {
|
|
throw new Error(ERR_UNSUPPORTED_COMPRESSION);
|
|
}
|
|
let level = getOptionValue(zipWriter, options, OPTION_LEVEL);
|
|
let useCompressionStream = getOptionValue(zipWriter, options, OPTION_USE_COMPRESSION_STREAM);
|
|
let dataDescriptor = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR);
|
|
if (bufferedWrite && dataDescriptor === UNDEFINED_VALUE) {
|
|
dataDescriptor = false;
|
|
}
|
|
if (dataDescriptor === UNDEFINED_VALUE || zipCrypto) {
|
|
dataDescriptor = true;
|
|
}
|
|
if (level !== UNDEFINED_VALUE && level != 6) {
|
|
useCompressionStream = false;
|
|
}
|
|
if (!useCompressionStream && (zipWriter.config.CompressionStream === UNDEFINED_VALUE && zipWriter.config.CompressionStreamZlib === UNDEFINED_VALUE)) {
|
|
level = 0;
|
|
}
|
|
const zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
|
|
if (!zipCrypto && (password !== UNDEFINED_VALUE || rawPassword !== UNDEFINED_VALUE) && !(encryptionStrength >= 1 && encryptionStrength <= 3)) {
|
|
throw new Error(ERR_INVALID_ENCRYPTION_STRENGTH);
|
|
}
|
|
let rawExtraField = EMPTY_UINT8_ARRAY;
|
|
const extraField = options[PROPERTY_NAME_EXTRA_FIELD];
|
|
if (extraField) {
|
|
let extraFieldSize = 0;
|
|
let offset = 0;
|
|
extraField.forEach(data => extraFieldSize += 4 + getLength(data));
|
|
rawExtraField = new Uint8Array(extraFieldSize);
|
|
const rawExtraFieldView = getDataView(rawExtraField);
|
|
extraField.forEach((data, type) => {
|
|
if (type > MAX_16_BITS) {
|
|
throw new Error(ERR_INVALID_EXTRAFIELD_TYPE);
|
|
}
|
|
if (getLength(data) > MAX_16_BITS) {
|
|
throw new Error(ERR_INVALID_EXTRAFIELD_DATA);
|
|
}
|
|
setUint16(rawExtraFieldView, offset, type);
|
|
setUint16(rawExtraFieldView, offset + 2, getLength(data));
|
|
arraySet(rawExtraField, data, offset + 4);
|
|
offset += 4 + getLength(data);
|
|
});
|
|
}
|
|
return {
|
|
comment,
|
|
resolvedOptions: {
|
|
rawFilename,
|
|
rawComment,
|
|
version,
|
|
lastModDate,
|
|
lastAccessDate,
|
|
creationDate,
|
|
internalFileAttributes,
|
|
passThrough,
|
|
password,
|
|
rawPassword,
|
|
encryptionStrength,
|
|
zipCrypto,
|
|
extendedTimestamp,
|
|
keepOrder,
|
|
useWebWorkers,
|
|
transferStreams,
|
|
bufferedWrite,
|
|
createTempStream,
|
|
dataDescriptorSignature,
|
|
signal,
|
|
useUnicodeFileNames,
|
|
compressionMethod,
|
|
level,
|
|
useCompressionStream,
|
|
dataDescriptor,
|
|
zip64,
|
|
rawExtraField
|
|
}
|
|
};
|
|
}
|
|
|
|
async function resolveSizes(zipWriter, reader, { resolvedOptions: metadata }, options) {
|
|
const { passThrough, zipCrypto, password, rawPassword, encryptionStrength } = metadata;
|
|
let { dataDescriptor, zip64, level, compressionMethod } = metadata;
|
|
let maximumCompressedSize = 0;
|
|
let uncompressedSize = 0;
|
|
if (passThrough) {
|
|
if (!reader) {
|
|
throw new Error(ERR_UNDEFINED_READER);
|
|
}
|
|
uncompressedSize = options[PROPERTY_NAME_UNCOMPRESSED_SIZE];
|
|
if (uncompressedSize === UNDEFINED_VALUE) {
|
|
throw new Error(ERR_UNDEFINED_UNCOMPRESSED_SIZE);
|
|
}
|
|
}
|
|
const zip64Enabled = zip64 === true;
|
|
const encrypted = getOptionValue(zipWriter, options, PROPERTY_NAME_ENCRYPTED);
|
|
const encryptedEntry = Boolean(reader) && (Boolean((password && getLength(password)) || (rawPassword && getLength(rawPassword))) || (passThrough && encrypted));
|
|
if (!reader) {
|
|
level = 0;
|
|
compressionMethod = COMPRESSION_METHOD_STORE;
|
|
}
|
|
const encryptionOverhead = encryptedEntry ? (zipCrypto ? 12 : 16 + encryptionStrength * 4) : 0;
|
|
if (reader) {
|
|
reader = new GenericReader(reader);
|
|
await initStream(reader);
|
|
if (!passThrough) {
|
|
if (reader.size === UNDEFINED_VALUE) {
|
|
dataDescriptor = true;
|
|
if (zip64 || zip64 === UNDEFINED_VALUE) {
|
|
zip64 = true;
|
|
uncompressedSize = maximumCompressedSize = MAX_32_BITS + 1;
|
|
}
|
|
} else {
|
|
options.uncompressedSize = uncompressedSize = reader.size;
|
|
maximumCompressedSize = getMaximumCompressedSize(uncompressedSize) + encryptionOverhead;
|
|
}
|
|
} else {
|
|
options.uncompressedSize = uncompressedSize;
|
|
maximumCompressedSize = getMaximumCompressedSize(uncompressedSize) + encryptionOverhead;
|
|
}
|
|
}
|
|
const zip64UncompressedSize = zip64Enabled || uncompressedSize >= MAX_32_BITS;
|
|
const zip64CompressedSize = zip64Enabled || maximumCompressedSize >= MAX_32_BITS;
|
|
if (zip64UncompressedSize || zip64CompressedSize) {
|
|
if (zip64 === false) {
|
|
throw new Error(ERR_UNSUPPORTED_FORMAT);
|
|
} else {
|
|
zip64 = true;
|
|
}
|
|
}
|
|
zip64 = zip64 || false;
|
|
return {
|
|
reader,
|
|
resolvedOptions: {
|
|
dataDescriptor,
|
|
zip64,
|
|
zip64UncompressedSize,
|
|
zip64CompressedSize,
|
|
uncompressedSize,
|
|
level,
|
|
compressionMethod,
|
|
encrypted: encryptedEntry
|
|
}
|
|
};
|
|
}
|
|
|
|
async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
|
|
const {
|
|
files,
|
|
writer
|
|
} = zipWriter;
|
|
const {
|
|
keepOrder,
|
|
dataDescriptor,
|
|
signal
|
|
} = options;
|
|
const {
|
|
headerInfo
|
|
} = entryInfo;
|
|
const usdz = zipWriter.options[OPTION_USDZ];
|
|
const previousFileEntry = zipWriter.lastFileEntry;
|
|
let fileEntry = {};
|
|
let bufferedWrite;
|
|
let releaseLockWriter;
|
|
let releaseLockCurrentFileEntry;
|
|
let writingBufferedEntryData;
|
|
let writingEntryData;
|
|
let writerSizeBeforeEntry;
|
|
let flushedBufferedSize = 0;
|
|
let fileWriter;
|
|
files.set(name, fileEntry);
|
|
zipWriter.lastFileEntry = fileEntry;
|
|
try {
|
|
let lockPreviousFileEntry;
|
|
if (keepOrder) {
|
|
lockPreviousFileEntry = previousFileEntry && previousFileEntry.lock;
|
|
requestLockCurrentFileEntry();
|
|
}
|
|
if (options.bufferedWrite || !keepOrder || zipWriter.writerLocked || zipWriter.bufferedWrites || !dataDescriptor) {
|
|
bufferedWrite = true;
|
|
zipWriter.bufferedWrites++;
|
|
if (options.createTempStream) {
|
|
fileWriter = await options.createTempStream();
|
|
} else {
|
|
fileWriter = new TransformStream(UNDEFINED_VALUE, UNDEFINED_VALUE, { highWaterMark: INFINITY_VALUE });
|
|
}
|
|
fileWriter.size = 0;
|
|
await initStream(writer);
|
|
} else {
|
|
fileWriter = writer;
|
|
await requestLockWriter();
|
|
}
|
|
await initStream(fileWriter);
|
|
const { diskOffset } = writer;
|
|
if (zipWriter.addSplitZipSignature) {
|
|
delete zipWriter.addSplitZipSignature;
|
|
const signatureArray = new Uint8Array(4);
|
|
const signatureArrayView = getDataView(signatureArray);
|
|
setUint32(signatureArrayView, 0, SPLIT_ZIP_FILE_SIGNATURE);
|
|
await writeData(writer, signatureArray);
|
|
zipWriter.offset += 4;
|
|
}
|
|
if (usdz && !bufferedWrite) {
|
|
appendExtraFieldUSDZ(entryInfo, zipWriter.offset - diskOffset);
|
|
}
|
|
const { localHeaderArray } = headerInfo;
|
|
if (!bufferedWrite) {
|
|
await lockPreviousFileEntry;
|
|
await skipDiskIfNeeded();
|
|
}
|
|
const diskNumberStart = writer.diskNumber;
|
|
const entryOffset = getSegmentOffset(zipWriter, writer);
|
|
fileEntry.diskNumberStart = diskNumberStart;
|
|
if (!bufferedWrite) {
|
|
writingEntryData = true;
|
|
writerSizeBeforeEntry = writer.size;
|
|
await writeData(fileWriter, localHeaderArray);
|
|
}
|
|
fileEntry = await createFileEntry(reader, fileWriter, fileEntry, entryInfo, zipWriter.config, options);
|
|
if (!bufferedWrite) {
|
|
writingEntryData = false;
|
|
}
|
|
files.set(name, fileEntry);
|
|
fileEntry.filename = name;
|
|
if (bufferedWrite) {
|
|
await Promise.all([fileWriter.writable.getWriter().close(), lockPreviousFileEntry]);
|
|
await requestLockWriter();
|
|
writingBufferedEntryData = true;
|
|
writerSizeBeforeEntry = writer.size;
|
|
await skipDiskIfNeeded();
|
|
fileEntry.diskNumberStart = writer.diskNumber;
|
|
fileEntry.offset = getSegmentOffset(zipWriter, writer);
|
|
if (usdz) {
|
|
const previousMetadataSize = entryInfo.metadataSize;
|
|
appendExtraFieldUSDZ(entryInfo, zipWriter.offset - writer.diskOffset);
|
|
fileEntry.size += entryInfo.metadataSize - previousMetadataSize;
|
|
}
|
|
updateLocalHeader(fileEntry, headerInfo.localHeaderView, options);
|
|
await writeData(writer, headerInfo.localHeaderArray);
|
|
await flushBufferedData(fileWriter.readable, writer, signal, chunkLength => flushedBufferedSize += chunkLength);
|
|
writer.size += fileWriter.size;
|
|
writingBufferedEntryData = false;
|
|
} else {
|
|
fileEntry.diskNumberStart = diskNumberStart;
|
|
fileEntry.offset = entryOffset;
|
|
}
|
|
zipWriter.offset += fileEntry.size;
|
|
return fileEntry;
|
|
} catch (error) {
|
|
if (writingBufferedEntryData || writingEntryData) {
|
|
zipWriter.hasCorruptedEntries = true;
|
|
if (error) {
|
|
try {
|
|
error.corruptedEntry = true;
|
|
} catch {
|
|
// ignored
|
|
}
|
|
}
|
|
zipWriter.offset += writer.size - writerSizeBeforeEntry;
|
|
if (bufferedWrite) {
|
|
zipWriter.offset += flushedBufferedSize;
|
|
}
|
|
}
|
|
files.delete(name);
|
|
throw error;
|
|
} finally {
|
|
if (bufferedWrite) {
|
|
zipWriter.bufferedWrites--;
|
|
}
|
|
if (releaseLockCurrentFileEntry) {
|
|
releaseLockCurrentFileEntry();
|
|
}
|
|
if (releaseLockWriter) {
|
|
releaseLockWriter();
|
|
}
|
|
if (bufferedWrite && fileWriter && fileWriter.dispose) {
|
|
try {
|
|
await fileWriter.dispose();
|
|
} catch {
|
|
// ignored
|
|
}
|
|
}
|
|
}
|
|
|
|
function requestLockCurrentFileEntry() {
|
|
fileEntry.lock = new Promise(resolve => releaseLockCurrentFileEntry = resolve);
|
|
}
|
|
|
|
async function requestLockWriter() {
|
|
zipWriter.writerLocked = true;
|
|
const { lockWriter } = zipWriter;
|
|
zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
|
|
zipWriter.writerLocked = false;
|
|
resolve();
|
|
});
|
|
await lockWriter;
|
|
}
|
|
|
|
async function skipDiskIfNeeded() {
|
|
if (getLength(headerInfo.localHeaderArray) > writer.availableSize) {
|
|
writer.availableSize = 0;
|
|
await writeData(writer, EMPTY_UINT8_ARRAY);
|
|
}
|
|
}
|
|
}
|
|
|
|
async function createFileEntry(reader, writer, { diskNumberStart, lock }, entryInfo, config, options) {
|
|
const {
|
|
headerInfo,
|
|
dataDescriptorInfo,
|
|
metadataSize
|
|
} = entryInfo;
|
|
const {
|
|
headerArray,
|
|
headerView,
|
|
lastModDate,
|
|
rawLastModDate,
|
|
encrypted,
|
|
compressed,
|
|
version,
|
|
compressionMethod,
|
|
rawExtraFieldZip64,
|
|
localExtraFieldZip64Length,
|
|
rawExtraFieldExtendedTimestamp,
|
|
extraFieldExtendedTimestampFlag,
|
|
rawExtraFieldNTFS,
|
|
rawExtraFieldUnix,
|
|
rawExtraFieldAES,
|
|
} = headerInfo;
|
|
const { dataDescriptorArray } = dataDescriptorInfo;
|
|
const {
|
|
rawFilename,
|
|
lastAccessDate,
|
|
creationDate,
|
|
password,
|
|
rawPassword,
|
|
level,
|
|
zip64,
|
|
zip64UncompressedSize,
|
|
zip64CompressedSize,
|
|
zipCrypto,
|
|
dataDescriptor,
|
|
directory,
|
|
executable,
|
|
versionMadeBy,
|
|
rawComment,
|
|
rawExtraField,
|
|
useWebWorkers,
|
|
transferStreams,
|
|
onstart,
|
|
onprogress,
|
|
onend,
|
|
signal,
|
|
encryptionStrength,
|
|
extendedTimestamp,
|
|
msDosCompatible,
|
|
internalFileAttributes,
|
|
externalFileAttributes,
|
|
uid,
|
|
gid,
|
|
unixMode,
|
|
setuid,
|
|
setgid,
|
|
sticky,
|
|
unixExternalUpper,
|
|
msdosAttributesRaw,
|
|
msdosAttributes,
|
|
useCompressionStream,
|
|
passThrough
|
|
} = options;
|
|
const fileEntry = {
|
|
lock,
|
|
versionMadeBy,
|
|
zip64,
|
|
directory: Boolean(directory),
|
|
executable: Boolean(executable),
|
|
filenameUTF8: true,
|
|
rawFilename,
|
|
commentUTF8: true,
|
|
rawComment,
|
|
rawExtraFieldZip64,
|
|
localExtraFieldZip64Length,
|
|
rawExtraFieldExtendedTimestamp,
|
|
rawExtraFieldNTFS,
|
|
rawExtraFieldUnix,
|
|
rawExtraFieldAES,
|
|
rawExtraField,
|
|
extendedTimestamp,
|
|
msDosCompatible,
|
|
internalFileAttributes,
|
|
externalFileAttributes,
|
|
diskNumberStart,
|
|
uid,
|
|
gid,
|
|
unixMode,
|
|
setuid,
|
|
setgid,
|
|
sticky,
|
|
unixExternalUpper,
|
|
msdosAttributesRaw,
|
|
msdosAttributes
|
|
};
|
|
let {
|
|
signature,
|
|
uncompressedSize
|
|
} = options;
|
|
let compressedSize = 0;
|
|
if (!passThrough) {
|
|
uncompressedSize = 0;
|
|
}
|
|
const { writable } = writer;
|
|
if (reader) {
|
|
const readable = reader.createReadable ? reader.createReadable({ chunkSize: getChunkSize(config) }) : reader.readable;
|
|
const size = reader.size;
|
|
const workerOptions = {
|
|
options: {
|
|
codecType: CODEC_DEFLATE,
|
|
level,
|
|
rawPassword,
|
|
password,
|
|
encryptionStrength,
|
|
zipCrypto: encrypted && zipCrypto,
|
|
passwordVerification: encrypted && zipCrypto && (rawLastModDate >> 8) & MAX_8_BITS,
|
|
signed: !passThrough,
|
|
compressed: compressed && !passThrough,
|
|
encrypted: encrypted && !passThrough,
|
|
useWebWorkers,
|
|
useCompressionStream,
|
|
transferStreams
|
|
},
|
|
config,
|
|
streamOptions: { signal, size, onstart, onprogress, onend }
|
|
};
|
|
try {
|
|
const result = await runWorker({ readable, writable }, workerOptions);
|
|
compressedSize = result.outputSize;
|
|
writer.size += compressedSize;
|
|
if (!passThrough) {
|
|
uncompressedSize = result.inputSize;
|
|
signature = result.signature;
|
|
}
|
|
if ((!zip64CompressedSize && compressedSize >= MAX_32_BITS) ||
|
|
(!zip64UncompressedSize && uncompressedSize >= MAX_32_BITS)) {
|
|
throw new Error(ERR_UNSUPPORTED_FORMAT);
|
|
}
|
|
} catch (error) {
|
|
if (error.outputSize !== UNDEFINED_VALUE) {
|
|
writer.size += error.outputSize;
|
|
}
|
|
throw error;
|
|
}
|
|
|
|
}
|
|
setEntryInfo({
|
|
signature,
|
|
compressedSize,
|
|
uncompressedSize,
|
|
headerInfo,
|
|
dataDescriptorInfo
|
|
}, options);
|
|
if (dataDescriptor) {
|
|
await writeData(writer, dataDescriptorArray);
|
|
}
|
|
Object.assign(fileEntry, {
|
|
uncompressedSize,
|
|
compressedSize,
|
|
lastModDate,
|
|
rawLastModDate,
|
|
creationDate,
|
|
lastAccessDate,
|
|
encrypted,
|
|
zipCrypto,
|
|
size: metadataSize + compressedSize,
|
|
compressionMethod,
|
|
version,
|
|
headerArray,
|
|
headerView,
|
|
signature,
|
|
extraFieldExtendedTimestampFlag,
|
|
zip64UncompressedSize,
|
|
zip64CompressedSize
|
|
});
|
|
return fileEntry;
|
|
}
|
|
|
|
function getHeaderInfo(options) {
|
|
const {
|
|
rawFilename,
|
|
lastModDate,
|
|
lastAccessDate,
|
|
creationDate,
|
|
level,
|
|
zip64,
|
|
zipCrypto,
|
|
useUnicodeFileNames,
|
|
dataDescriptor,
|
|
directory,
|
|
rawExtraField,
|
|
encryptionStrength,
|
|
extendedTimestamp,
|
|
passThrough,
|
|
encrypted,
|
|
zip64UncompressedSize,
|
|
zip64CompressedSize,
|
|
uncompressedSize
|
|
} = options;
|
|
let { version, compressionMethod } = options;
|
|
const compressed = !directory && (compressionMethod === UNDEFINED_VALUE
|
|
? (level === UNDEFINED_VALUE || level > 0)
|
|
: compressionMethod !== COMPRESSION_METHOD_STORE);
|
|
let rawLocalExtraFieldZip64;
|
|
const uncompressedFile = passThrough || !compressed;
|
|
const zip64ExtraFieldComplete = zip64 && (options.bufferedWrite || !dataDescriptor || ((!zip64UncompressedSize && !zip64CompressedSize) || uncompressedFile));
|
|
const writeLocalExtraFieldZip64 = zip64ExtraFieldComplete || (zip64 && dataDescriptor && (zip64UncompressedSize || zip64CompressedSize));
|
|
if (zip64 && (zip64UncompressedSize || zip64CompressedSize)) {
|
|
const length = 4 + 16;
|
|
const extraFieldZip64 = createRecordWriter(length);
|
|
extraFieldZip64.uint16(EXTRAFIELD_TYPE_ZIP64);
|
|
extraFieldZip64.uint16(length - 4);
|
|
rawLocalExtraFieldZip64 = extraFieldZip64.array;
|
|
if (zip64ExtraFieldComplete) {
|
|
extraFieldZip64.uint64(uncompressedSize);
|
|
if (uncompressedFile) {
|
|
const encryptionOverhead = encrypted ? (zipCrypto ? 12 : 16 + encryptionStrength * 4) : 0;
|
|
extraFieldZip64.uint64(passThrough ? 0 : uncompressedSize + encryptionOverhead);
|
|
}
|
|
}
|
|
} else {
|
|
rawLocalExtraFieldZip64 = EMPTY_UINT8_ARRAY;
|
|
}
|
|
let rawExtraFieldAES;
|
|
if (encrypted && !zipCrypto) {
|
|
const extraFieldAES = createRecordWriter(getLength(EXTRAFIELD_DATA_AES) + 2);
|
|
extraFieldAES.uint16(EXTRAFIELD_TYPE_AES);
|
|
extraFieldAES.bytes(EXTRAFIELD_DATA_AES);
|
|
rawExtraFieldAES = extraFieldAES.array;
|
|
rawExtraFieldAES[8] = encryptionStrength;
|
|
} else {
|
|
rawExtraFieldAES = EMPTY_UINT8_ARRAY;
|
|
}
|
|
let rawExtraFieldNTFS;
|
|
let rawExtraFieldExtendedTimestamp;
|
|
let extraFieldExtendedTimestampFlag;
|
|
if (extendedTimestamp) {
|
|
const lastModTimeUnix = getTimeUnix(lastModDate);
|
|
if (inUnixTimeRange(lastModTimeUnix)) {
|
|
const extraFieldTimestampLength = 9 + (lastAccessDate ? 4 : 0) + (creationDate ? 4 : 0);
|
|
const extraFieldTimestamp = createRecordWriter(extraFieldTimestampLength);
|
|
extraFieldExtendedTimestampFlag = 0x1 + (lastAccessDate ? 0x2 : 0) + (creationDate ? 0x4 : 0);
|
|
extraFieldTimestamp.uint16(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
|
|
extraFieldTimestamp.uint16(extraFieldTimestampLength - 4);
|
|
extraFieldTimestamp.uint8(extraFieldExtendedTimestampFlag);
|
|
extraFieldTimestamp.uint32(lastModTimeUnix);
|
|
if (lastAccessDate) {
|
|
extraFieldTimestamp.uint32(clampUnixTime(getTimeUnix(lastAccessDate)));
|
|
}
|
|
if (creationDate) {
|
|
extraFieldTimestamp.uint32(clampUnixTime(getTimeUnix(creationDate)));
|
|
}
|
|
rawExtraFieldExtendedTimestamp = extraFieldTimestamp.array;
|
|
} else {
|
|
rawExtraFieldExtendedTimestamp = EMPTY_UINT8_ARRAY;
|
|
}
|
|
try {
|
|
const lastModTimeNTFS = getTimeNTFS(lastModDate);
|
|
const extraFieldNTFS = createRecordWriter(36);
|
|
extraFieldNTFS.uint16(EXTRAFIELD_TYPE_NTFS);
|
|
extraFieldNTFS.uint16(32);
|
|
extraFieldNTFS.skip(4);
|
|
extraFieldNTFS.uint16(EXTRAFIELD_TYPE_NTFS_TAG1);
|
|
extraFieldNTFS.uint16(24);
|
|
extraFieldNTFS.uint64(lastModTimeNTFS);
|
|
extraFieldNTFS.uint64(getTimeNTFS(lastAccessDate) || lastModTimeNTFS);
|
|
extraFieldNTFS.uint64(getTimeNTFS(creationDate) || lastModTimeNTFS);
|
|
rawExtraFieldNTFS = extraFieldNTFS.array;
|
|
} catch {
|
|
rawExtraFieldNTFS = EMPTY_UINT8_ARRAY;
|
|
}
|
|
} else {
|
|
rawExtraFieldNTFS = rawExtraFieldExtendedTimestamp = EMPTY_UINT8_ARRAY;
|
|
}
|
|
let rawExtraFieldUnix;
|
|
try {
|
|
const { uid, gid, unixExtraFieldType } = options;
|
|
if (unixExtraFieldType == INFOZIP_EXTRA_FIELD_TYPE && (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE)) {
|
|
const uidBytes = packUnixId(uid);
|
|
const gidBytes = packUnixId(gid);
|
|
const payloadLength = 3 + uidBytes.length + gidBytes.length;
|
|
const extraFieldUnix = createRecordWriter(4 + payloadLength);
|
|
extraFieldUnix.uint16(EXTRAFIELD_TYPE_INFOZIP);
|
|
extraFieldUnix.uint16(payloadLength);
|
|
extraFieldUnix.uint8(1);
|
|
extraFieldUnix.uint8(uidBytes.length);
|
|
extraFieldUnix.bytes(uidBytes);
|
|
extraFieldUnix.uint8(gidBytes.length);
|
|
extraFieldUnix.bytes(gidBytes);
|
|
rawExtraFieldUnix = extraFieldUnix.array;
|
|
} else if (unixExtraFieldType == UNIX_EXTRA_FIELD_TYPE && (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE)) {
|
|
const extraFieldUnix = createRecordWriter(8);
|
|
extraFieldUnix.uint16(EXTRAFIELD_TYPE_UNIX);
|
|
extraFieldUnix.uint16(4);
|
|
extraFieldUnix.uint16((uid === UNDEFINED_VALUE ? 0 : uid) & MAX_16_BITS);
|
|
extraFieldUnix.uint16((gid === UNDEFINED_VALUE ? 0 : gid) & MAX_16_BITS);
|
|
rawExtraFieldUnix = extraFieldUnix.array;
|
|
} else {
|
|
rawExtraFieldUnix = EMPTY_UINT8_ARRAY;
|
|
}
|
|
} catch {
|
|
rawExtraFieldUnix = EMPTY_UINT8_ARRAY;
|
|
}
|
|
if (compressionMethod === UNDEFINED_VALUE) {
|
|
compressionMethod = compressed ? COMPRESSION_METHOD_DEFLATE : COMPRESSION_METHOD_STORE;
|
|
}
|
|
if (zip64) {
|
|
version = version > VERSION_ZIP64 ? version : VERSION_ZIP64;
|
|
}
|
|
if (encrypted && !zipCrypto) {
|
|
version = version > VERSION_AES ? version : VERSION_AES;
|
|
rawExtraFieldAES[9] = compressionMethod;
|
|
compressionMethod = COMPRESSION_METHOD_AES;
|
|
}
|
|
const localExtraFieldZip64Length = writeLocalExtraFieldZip64 ? getLength(rawLocalExtraFieldZip64) : 0;
|
|
const extraFieldLength = localExtraFieldZip64Length + getLength(rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField);
|
|
if (extraFieldLength > MAX_16_BITS) {
|
|
throw new Error(ERR_INVALID_EXTRAFIELD_DATA);
|
|
}
|
|
const {
|
|
headerArray,
|
|
headerView,
|
|
rawLastModDate
|
|
} = getHeaderArrayData({
|
|
version,
|
|
bitFlag: getBitFlag(level, useUnicodeFileNames, dataDescriptor, encrypted, compressionMethod),
|
|
compressionMethod,
|
|
uncompressedSize,
|
|
lastModDate: lastModDate < MIN_DATE ? MIN_DATE : lastModDate > MAX_DATE ? MAX_DATE : lastModDate,
|
|
rawFilename,
|
|
zip64CompressedSize,
|
|
zip64UncompressedSize,
|
|
extraFieldLength
|
|
});
|
|
const localHeader = createRecordWriter(HEADER_SIZE + getLength(rawFilename) + extraFieldLength);
|
|
const localHeaderArray = localHeader.array;
|
|
const localHeaderView = getDataView(localHeaderArray);
|
|
localHeader.uint32(LOCAL_FILE_HEADER_SIGNATURE);
|
|
localHeader.bytes(headerArray);
|
|
localHeader.bytes(rawFilename);
|
|
if (writeLocalExtraFieldZip64) {
|
|
localHeader.bytes(rawLocalExtraFieldZip64);
|
|
}
|
|
localHeader.bytes(rawExtraFieldAES);
|
|
localHeader.bytes(rawExtraFieldExtendedTimestamp);
|
|
localHeader.bytes(rawExtraFieldNTFS);
|
|
localHeader.bytes(rawExtraFieldUnix);
|
|
localHeader.bytes(rawExtraField);
|
|
if (dataDescriptor) {
|
|
if (!zip64CompressedSize) {
|
|
setUint32(localHeaderView, HEADER_OFFSET_COMPRESSED_SIZE + LOCAL_HEADER_COMMON_OFFSET, 0);
|
|
}
|
|
if (!zip64UncompressedSize) {
|
|
setUint32(localHeaderView, HEADER_OFFSET_UNCOMPRESSED_SIZE + LOCAL_HEADER_COMMON_OFFSET, 0);
|
|
}
|
|
}
|
|
return {
|
|
localHeaderArray,
|
|
localHeaderView,
|
|
headerArray,
|
|
headerView,
|
|
lastModDate,
|
|
rawLastModDate,
|
|
encrypted,
|
|
compressed,
|
|
version,
|
|
compressionMethod,
|
|
extraFieldExtendedTimestampFlag,
|
|
rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
|
|
localExtraFieldZip64Length,
|
|
rawExtraFieldExtendedTimestamp,
|
|
rawExtraFieldNTFS,
|
|
rawExtraFieldUnix,
|
|
rawExtraFieldAES,
|
|
extraFieldLength
|
|
};
|
|
}
|
|
|
|
function appendExtraFieldUSDZ(entryInfo, zipWriterOffset) {
|
|
const { headerInfo } = entryInfo;
|
|
let { localHeaderArray, extraFieldLength } = headerInfo;
|
|
let extraBytesLength = 64 - ((zipWriterOffset + getLength(localHeaderArray)) % 64);
|
|
if (extraBytesLength < 4) {
|
|
extraBytesLength += 64;
|
|
}
|
|
const rawExtraFieldUSDZ = new Uint8Array(extraBytesLength);
|
|
const extraFieldUSDZView = getDataView(rawExtraFieldUSDZ);
|
|
setUint16(extraFieldUSDZView, 0, EXTRAFIELD_TYPE_USDZ);
|
|
setUint16(extraFieldUSDZView, 2, extraBytesLength - 4);
|
|
const previousLocalHeaderArray = localHeaderArray;
|
|
headerInfo.localHeaderArray = localHeaderArray = new Uint8Array(getLength(previousLocalHeaderArray) + extraBytesLength);
|
|
arraySet(localHeaderArray, previousLocalHeaderArray);
|
|
arraySet(localHeaderArray, rawExtraFieldUSDZ, getLength(previousLocalHeaderArray));
|
|
const localHeaderArrayView = getDataView(localHeaderArray);
|
|
setUint16(localHeaderArrayView, 28, extraFieldLength + extraBytesLength);
|
|
headerInfo.localHeaderView = localHeaderArrayView;
|
|
entryInfo.metadataSize += extraBytesLength;
|
|
}
|
|
|
|
function packUnixId(id) {
|
|
if (id === UNDEFINED_VALUE) {
|
|
return EMPTY_UINT8_ARRAY;
|
|
} else {
|
|
const dataArray = new Uint8Array(4);
|
|
const dataView = getDataView(dataArray);
|
|
dataView.setUint32(0, id, true);
|
|
let length = 4;
|
|
while (length > 1 && dataArray[length - 1] === 0) {
|
|
length--;
|
|
}
|
|
return dataArray.subarray(0, length);
|
|
}
|
|
}
|
|
|
|
function normalizeMsdosAttributes(msdosAttributesRaw, msdosAttributes) {
|
|
if (msdosAttributesRaw !== UNDEFINED_VALUE) {
|
|
msdosAttributesRaw = msdosAttributesRaw & MAX_8_BITS;
|
|
} else if (msdosAttributes !== UNDEFINED_VALUE) {
|
|
const { readOnly, hidden, system, directory: msdDir, archive } = msdosAttributes;
|
|
let raw = 0;
|
|
if (readOnly) raw |= FILE_ATTR_MSDOS_READONLY_MASK;
|
|
if (hidden) raw |= FILE_ATTR_MSDOS_HIDDEN_MASK;
|
|
if (system) raw |= FILE_ATTR_MSDOS_SYSTEM_MASK;
|
|
if (msdDir) raw |= FILE_ATTR_MSDOS_DIR_MASK;
|
|
if (archive) raw |= FILE_ATTR_MSDOS_ARCHIVE_MASK;
|
|
msdosAttributesRaw = raw & MAX_8_BITS;
|
|
}
|
|
if (msdosAttributes === UNDEFINED_VALUE) {
|
|
msdosAttributes = {
|
|
readOnly: Boolean(msdosAttributesRaw & FILE_ATTR_MSDOS_READONLY_MASK),
|
|
hidden: Boolean(msdosAttributesRaw & FILE_ATTR_MSDOS_HIDDEN_MASK),
|
|
system: Boolean(msdosAttributesRaw & FILE_ATTR_MSDOS_SYSTEM_MASK),
|
|
directory: Boolean(msdosAttributesRaw & FILE_ATTR_MSDOS_DIR_MASK),
|
|
archive: Boolean(msdosAttributesRaw & FILE_ATTR_MSDOS_ARCHIVE_MASK)
|
|
};
|
|
}
|
|
return { msdosAttributesRaw, msdosAttributes };
|
|
}
|
|
|
|
function getDataDescriptorInfo({
|
|
zip64,
|
|
dataDescriptor,
|
|
dataDescriptorSignature
|
|
}) {
|
|
let dataDescriptorArray = EMPTY_UINT8_ARRAY;
|
|
let dataDescriptorView, dataDescriptorOffset = 0;
|
|
let dataDescriptorLength = zip64 ? DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH : DATA_DESCRIPTOR_RECORD_LENGTH;
|
|
if (dataDescriptorSignature) {
|
|
dataDescriptorLength += DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
|
|
}
|
|
if (dataDescriptor) {
|
|
dataDescriptorArray = new Uint8Array(dataDescriptorLength);
|
|
dataDescriptorView = getDataView(dataDescriptorArray);
|
|
if (dataDescriptorSignature) {
|
|
dataDescriptorOffset = DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
|
|
setUint32(dataDescriptorView, 0, DATA_DESCRIPTOR_RECORD_SIGNATURE);
|
|
}
|
|
}
|
|
return {
|
|
dataDescriptorArray,
|
|
dataDescriptorView,
|
|
dataDescriptorOffset
|
|
};
|
|
}
|
|
|
|
function setEntryInfo({
|
|
signature,
|
|
compressedSize,
|
|
uncompressedSize,
|
|
headerInfo,
|
|
dataDescriptorInfo
|
|
}, {
|
|
zip64,
|
|
zipCrypto,
|
|
dataDescriptor
|
|
}) {
|
|
const {
|
|
headerView,
|
|
encrypted
|
|
} = headerInfo;
|
|
const {
|
|
dataDescriptorView,
|
|
dataDescriptorOffset
|
|
} = dataDescriptorInfo;
|
|
if ((!encrypted || zipCrypto) && signature !== UNDEFINED_VALUE) {
|
|
setUint32(headerView, HEADER_OFFSET_SIGNATURE, signature);
|
|
if (dataDescriptor) {
|
|
setUint32(dataDescriptorView, dataDescriptorOffset, signature);
|
|
}
|
|
}
|
|
if (zip64) {
|
|
if (dataDescriptor) {
|
|
setBigUint64(dataDescriptorView, dataDescriptorOffset + 4, BigInt(compressedSize));
|
|
setBigUint64(dataDescriptorView, dataDescriptorOffset + 12, BigInt(uncompressedSize));
|
|
}
|
|
} else {
|
|
setUint32(headerView, HEADER_OFFSET_COMPRESSED_SIZE, compressedSize);
|
|
setUint32(headerView, HEADER_OFFSET_UNCOMPRESSED_SIZE, uncompressedSize);
|
|
if (dataDescriptor) {
|
|
setUint32(dataDescriptorView, dataDescriptorOffset + 4, compressedSize);
|
|
setUint32(dataDescriptorView, dataDescriptorOffset + 8, uncompressedSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
function updateLocalHeader({
|
|
rawFilename,
|
|
encrypted,
|
|
zip64,
|
|
localExtraFieldZip64Length,
|
|
signature,
|
|
compressedSize,
|
|
uncompressedSize,
|
|
zip64UncompressedSize,
|
|
zip64CompressedSize
|
|
}, localHeaderView, { dataDescriptor }) {
|
|
if (!dataDescriptor) {
|
|
if (!encrypted) {
|
|
setUint32(localHeaderView, HEADER_OFFSET_SIGNATURE + LOCAL_HEADER_COMMON_OFFSET, signature);
|
|
}
|
|
if (!zip64CompressedSize) {
|
|
setUint32(localHeaderView, HEADER_OFFSET_COMPRESSED_SIZE + LOCAL_HEADER_COMMON_OFFSET, compressedSize);
|
|
}
|
|
if (!zip64UncompressedSize) {
|
|
setUint32(localHeaderView, HEADER_OFFSET_UNCOMPRESSED_SIZE + LOCAL_HEADER_COMMON_OFFSET, uncompressedSize);
|
|
}
|
|
}
|
|
if (zip64 && localExtraFieldZip64Length) {
|
|
const localHeaderOffset = HEADER_SIZE + getLength(rawFilename) + 4;
|
|
setBigUint64(localHeaderView, localHeaderOffset, BigInt(uncompressedSize));
|
|
setBigUint64(localHeaderView, localHeaderOffset + 8, BigInt(compressedSize));
|
|
}
|
|
}
|
|
|
|
|
|
async function closeFile(zipWriter, comment, options) {
|
|
const directoryDataLength = createDirectoryRecords(zipWriter.files);
|
|
const { cdStartDiskNumber, cdStartDiskOffset } = await writeDirectoryRecords(zipWriter, directoryDataLength, options);
|
|
await writeEndOfDirectoryRecord(zipWriter, comment, options, { cdStartDiskNumber, cdStartDiskOffset, directoryDataLength });
|
|
}
|
|
|
|
function createDirectoryRecords(files) {
|
|
let directoryDataLength = 0;
|
|
for (const [, fileEntry] of files) {
|
|
const {
|
|
rawFilename,
|
|
rawExtraFieldAES,
|
|
rawComment,
|
|
rawExtraFieldNTFS,
|
|
rawExtraFieldUnix,
|
|
rawExtraField,
|
|
extendedTimestamp,
|
|
extraFieldExtendedTimestampFlag,
|
|
lastModDate,
|
|
zip64UncompressedSize,
|
|
zip64CompressedSize,
|
|
uncompressedSize,
|
|
compressedSize
|
|
} = fileEntry;
|
|
const zip64Offset = fileEntry.offset >= MAX_32_BITS;
|
|
const zip64DiskNumberStart = fileEntry.diskNumberStart >= MAX_16_BITS;
|
|
let rawExtraFieldZip64;
|
|
if (zip64Offset || zip64DiskNumberStart || zip64UncompressedSize || zip64CompressedSize) {
|
|
const length = 4 + (zip64UncompressedSize ? 8 : 0) + (zip64CompressedSize ? 8 : 0) + (zip64Offset ? 8 : 0) + (zip64DiskNumberStart ? 4 : 0);
|
|
const extraFieldZip64 = createRecordWriter(length);
|
|
extraFieldZip64.uint16(EXTRAFIELD_TYPE_ZIP64);
|
|
extraFieldZip64.uint16(length - 4);
|
|
if (zip64UncompressedSize) {
|
|
extraFieldZip64.uint64(uncompressedSize);
|
|
}
|
|
if (zip64CompressedSize) {
|
|
extraFieldZip64.uint64(compressedSize);
|
|
}
|
|
if (zip64Offset) {
|
|
extraFieldZip64.uint64(fileEntry.offset);
|
|
}
|
|
if (zip64DiskNumberStart) {
|
|
extraFieldZip64.uint32(fileEntry.diskNumberStart);
|
|
}
|
|
rawExtraFieldZip64 = extraFieldZip64.array;
|
|
} else {
|
|
rawExtraFieldZip64 = EMPTY_UINT8_ARRAY;
|
|
}
|
|
fileEntry.rawExtraFieldZip64 = rawExtraFieldZip64;
|
|
fileEntry.zip64Offset = zip64Offset;
|
|
fileEntry.zip64DiskNumberStart = zip64DiskNumberStart;
|
|
let rawExtraFieldTimestamp;
|
|
const lastModTimeUnix = getTimeUnix(lastModDate);
|
|
if (extendedTimestamp && inUnixTimeRange(lastModTimeUnix)) {
|
|
const extraFieldTimestamp = createRecordWriter(9);
|
|
extraFieldTimestamp.uint16(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
|
|
extraFieldTimestamp.uint16(5);
|
|
extraFieldTimestamp.uint8(extraFieldExtendedTimestampFlag);
|
|
extraFieldTimestamp.uint32(lastModTimeUnix);
|
|
rawExtraFieldTimestamp = extraFieldTimestamp.array;
|
|
} else {
|
|
rawExtraFieldTimestamp = EMPTY_UINT8_ARRAY;
|
|
}
|
|
fileEntry.rawExtraFieldExtendedTimestamp = rawExtraFieldTimestamp;
|
|
const extraFieldLength = getLength(
|
|
rawExtraFieldZip64,
|
|
rawExtraFieldAES,
|
|
rawExtraFieldNTFS,
|
|
rawExtraFieldUnix,
|
|
rawExtraFieldTimestamp,
|
|
rawExtraField);
|
|
if (extraFieldLength > MAX_16_BITS) {
|
|
throw new Error(ERR_INVALID_EXTRAFIELD_DATA);
|
|
}
|
|
directoryDataLength += CENTRAL_FILE_HEADER_LENGTH + getLength(rawFilename, rawComment) + extraFieldLength;
|
|
}
|
|
return directoryDataLength;
|
|
}
|
|
|
|
async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
|
|
const { files, writer } = zipWriter;
|
|
const directoryArray = new Uint8Array(directoryDataLength);
|
|
await initStream(writer);
|
|
let offset = 0;
|
|
let directoryDiskOffset = 0;
|
|
let cdStartDiskNumber = writer.diskNumber;
|
|
let cdStartDiskOffset = writer.diskOffset;
|
|
for (const [indexFileEntry, fileEntry] of Array.from(files.values()).entries()) {
|
|
const {
|
|
offset: fileEntryOffset,
|
|
rawFilename,
|
|
rawExtraFieldZip64,
|
|
rawExtraFieldAES,
|
|
rawExtraFieldExtendedTimestamp,
|
|
rawExtraFieldNTFS,
|
|
rawExtraFieldUnix,
|
|
rawExtraField,
|
|
rawComment,
|
|
versionMadeBy,
|
|
headerArray,
|
|
headerView,
|
|
zip64UncompressedSize,
|
|
zip64CompressedSize,
|
|
zip64DiskNumberStart,
|
|
zip64Offset,
|
|
internalFileAttributes,
|
|
externalFileAttributes,
|
|
diskNumberStart,
|
|
uncompressedSize,
|
|
compressedSize
|
|
} = fileEntry;
|
|
const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField);
|
|
const directoryRecordLength = CENTRAL_FILE_HEADER_LENGTH + getLength(rawFilename, rawComment) + extraFieldLength;
|
|
if (offset + directoryRecordLength - directoryDiskOffset > writer.availableSize) {
|
|
await writeData(writer, directoryArray.slice(directoryDiskOffset, offset));
|
|
directoryDiskOffset = offset;
|
|
writer.availableSize = 0;
|
|
await writeData(writer, EMPTY_UINT8_ARRAY);
|
|
}
|
|
if (indexFileEntry == 0) {
|
|
cdStartDiskNumber = writer.diskNumber;
|
|
cdStartDiskOffset = writer.diskOffset;
|
|
}
|
|
if (!zip64UncompressedSize) {
|
|
setUint32(headerView, HEADER_OFFSET_UNCOMPRESSED_SIZE, uncompressedSize);
|
|
}
|
|
if (!zip64CompressedSize) {
|
|
setUint32(headerView, HEADER_OFFSET_COMPRESSED_SIZE, compressedSize);
|
|
}
|
|
if ((zip64Offset || zip64DiskNumberStart) && fileEntry.version < VERSION_ZIP64) {
|
|
setUint16(headerView, HEADER_OFFSET_VERSION, VERSION_ZIP64);
|
|
}
|
|
const directoryRecord = createRecordWriter(directoryRecordLength);
|
|
directoryRecord.uint32(CENTRAL_FILE_HEADER_SIGNATURE);
|
|
directoryRecord.uint16(versionMadeBy);
|
|
directoryRecord.bytes(headerArray.subarray(0, HEADER_SIZE - 4 - 2));
|
|
directoryRecord.uint16(extraFieldLength);
|
|
directoryRecord.uint16(getLength(rawComment));
|
|
directoryRecord.uint16(zip64DiskNumberStart ? MAX_16_BITS : diskNumberStart);
|
|
directoryRecord.uint16(internalFileAttributes);
|
|
directoryRecord.uint32(externalFileAttributes);
|
|
directoryRecord.uint32(zip64Offset ? MAX_32_BITS : fileEntryOffset);
|
|
directoryRecord.bytes(rawFilename);
|
|
directoryRecord.bytes(rawExtraFieldZip64);
|
|
directoryRecord.bytes(rawExtraFieldAES);
|
|
directoryRecord.bytes(rawExtraFieldExtendedTimestamp);
|
|
directoryRecord.bytes(rawExtraFieldNTFS);
|
|
directoryRecord.bytes(rawExtraFieldUnix);
|
|
directoryRecord.bytes(rawExtraField);
|
|
directoryRecord.bytes(rawComment);
|
|
arraySet(directoryArray, directoryRecord.array, offset);
|
|
offset += directoryRecordLength;
|
|
if (options.onprogress) {
|
|
try {
|
|
await options.onprogress(indexFileEntry + 1, files.size, new Entry(fileEntry));
|
|
} catch {
|
|
// ignored
|
|
}
|
|
}
|
|
}
|
|
await writeData(writer, directoryDiskOffset ? directoryArray.slice(directoryDiskOffset) : directoryArray);
|
|
return { cdStartDiskNumber, cdStartDiskOffset };
|
|
}
|
|
|
|
async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
|
|
const { writer } = zipWriter;
|
|
const { cdStartDiskNumber, cdStartDiskOffset } = cdInfo;
|
|
let { directoryDataLength } = cdInfo;
|
|
let filesLength = zipWriter.files.size;
|
|
let diskNumber = cdStartDiskNumber;
|
|
let directoryOffset = zipWriter.offset - cdStartDiskOffset - (cdStartDiskNumber ? zipWriter.initialOffset : 0);
|
|
let lastDiskNumber = writer.diskNumber;
|
|
if (writer.availableSize < END_OF_CENTRAL_DIR_LENGTH) {
|
|
lastDiskNumber++;
|
|
}
|
|
let zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
|
|
if (directoryOffset >= MAX_32_BITS || directoryDataLength >= MAX_32_BITS || filesLength >= MAX_16_BITS || lastDiskNumber >= MAX_16_BITS) {
|
|
if (zip64 === false) {
|
|
throw new Error(ERR_UNSUPPORTED_FORMAT);
|
|
} else {
|
|
zip64 = true;
|
|
}
|
|
}
|
|
const commentLength = getLength(comment);
|
|
if (commentLength > MAX_16_BITS) {
|
|
throw new Error(ERR_INVALID_COMMENT);
|
|
}
|
|
const endOfdirectoryRecord = createRecordWriter(zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH);
|
|
if (getLength(endOfdirectoryRecord.array) + commentLength > writer.availableSize) {
|
|
writer.availableSize = 0;
|
|
await writeData(writer, EMPTY_UINT8_ARRAY);
|
|
}
|
|
lastDiskNumber = writer.diskNumber;
|
|
if (zip64) {
|
|
endOfdirectoryRecord.uint32(ZIP64_END_OF_CENTRAL_DIR_SIGNATURE);
|
|
endOfdirectoryRecord.uint64(44);
|
|
endOfdirectoryRecord.uint16(45);
|
|
endOfdirectoryRecord.uint16(45);
|
|
endOfdirectoryRecord.uint32(lastDiskNumber);
|
|
endOfdirectoryRecord.uint32(diskNumber);
|
|
endOfdirectoryRecord.uint64(filesLength);
|
|
endOfdirectoryRecord.uint64(filesLength);
|
|
endOfdirectoryRecord.uint64(directoryDataLength);
|
|
endOfdirectoryRecord.uint64(directoryOffset);
|
|
endOfdirectoryRecord.uint32(ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE);
|
|
endOfdirectoryRecord.uint32(lastDiskNumber);
|
|
endOfdirectoryRecord.uint64(BigInt(zipWriter.offset) + BigInt(directoryDataLength) - BigInt(writer.diskOffset) - BigInt(writer.diskNumber ? zipWriter.initialOffset : 0));
|
|
endOfdirectoryRecord.uint32(lastDiskNumber + 1);
|
|
const supportZip64SplitFile = getOptionValue(zipWriter, options, OPTION_SUPPORT_ZIP64_SPLIT_FILE, true);
|
|
if (supportZip64SplitFile) {
|
|
lastDiskNumber = MAX_16_BITS;
|
|
diskNumber = MAX_16_BITS;
|
|
}
|
|
filesLength = MAX_16_BITS;
|
|
directoryOffset = MAX_32_BITS;
|
|
directoryDataLength = MAX_32_BITS;
|
|
}
|
|
endOfdirectoryRecord.uint32(END_OF_CENTRAL_DIR_SIGNATURE);
|
|
endOfdirectoryRecord.uint16(lastDiskNumber);
|
|
endOfdirectoryRecord.uint16(diskNumber);
|
|
endOfdirectoryRecord.uint16(filesLength);
|
|
endOfdirectoryRecord.uint16(filesLength);
|
|
endOfdirectoryRecord.uint32(directoryDataLength);
|
|
endOfdirectoryRecord.uint32(directoryOffset);
|
|
endOfdirectoryRecord.uint16(commentLength);
|
|
await writeData(writer, endOfdirectoryRecord.array);
|
|
if (commentLength) {
|
|
await writeData(writer, comment);
|
|
}
|
|
}
|
|
|
|
function createRecordWriter(length) {
|
|
const array = new Uint8Array(length);
|
|
const view = getDataView(array);
|
|
let offset = 0;
|
|
return {
|
|
array,
|
|
uint8: value => { setUint8(view, offset, value); offset += 1; },
|
|
uint16: value => { setUint16(view, offset, value); offset += 2; },
|
|
uint32: value => { setUint32(view, offset, value); offset += 4; },
|
|
uint64: value => { setBigUint64(view, offset, BigInt(value)); offset += 8; },
|
|
bytes: value => { arraySet(array, value, offset); offset += getLength(value); },
|
|
skip: count => offset += count
|
|
};
|
|
}
|
|
|
|
function getSegmentOffset(zipWriter, writer) {
|
|
return zipWriter.offset - writer.diskOffset - (writer.diskNumber ? zipWriter.initialOffset : 0);
|
|
}
|
|
|
|
async function writeData(writer, array) {
|
|
const { writable } = writer;
|
|
const streamWriter = writable.getWriter();
|
|
try {
|
|
await streamWriter.ready;
|
|
writer.size += getLength(array);
|
|
await streamWriter.write(array);
|
|
} finally {
|
|
streamWriter.releaseLock();
|
|
}
|
|
}
|
|
|
|
async function flushBufferedData(readable, writer, signal, onChunkWritten) {
|
|
const streamWriter = writer.writable.getWriter();
|
|
try {
|
|
await readable.pipeTo(new WritableStream({
|
|
async write(chunk) {
|
|
await streamWriter.ready;
|
|
await streamWriter.write(chunk);
|
|
onChunkWritten(getLength(chunk));
|
|
}
|
|
}), { preventClose: true, preventAbort: true, signal });
|
|
} finally {
|
|
streamWriter.releaseLock();
|
|
}
|
|
}
|
|
|
|
function getTimeNTFS(date) {
|
|
if (date) {
|
|
return ((BigInt(date.getTime()) + BigInt(11644473600000)) * BigInt(10000));
|
|
}
|
|
}
|
|
|
|
function getTimeUnix(date) {
|
|
return Math.floor(date.getTime() / 1000);
|
|
}
|
|
|
|
function inUnixTimeRange(timeUnix) {
|
|
return timeUnix >= MIN_UNIX_TIME && timeUnix <= MAX_UNIX_TIME;
|
|
}
|
|
|
|
function clampUnixTime(timeUnix) {
|
|
return Math.min(MAX_UNIX_TIME, Math.max(MIN_UNIX_TIME, timeUnix));
|
|
}
|
|
|
|
function getOptionValue(zipWriter, options, name, defaultValue) {
|
|
const result = options[name] === UNDEFINED_VALUE ? zipWriter.options[name] : options[name];
|
|
return result === UNDEFINED_VALUE ? defaultValue : result;
|
|
}
|
|
|
|
function getMaximumCompressedSize(uncompressedSize) {
|
|
return uncompressedSize + (5 * (Math.floor(uncompressedSize / 16383) + 1));
|
|
}
|
|
|
|
function setUint8(view, offset, value) {
|
|
view.setUint8(offset, value);
|
|
}
|
|
|
|
function setUint16(view, offset, value) {
|
|
view.setUint16(offset, value, true);
|
|
}
|
|
|
|
function setUint32(view, offset, value) {
|
|
view.setUint32(offset, value, true);
|
|
}
|
|
|
|
function setBigUint64(view, offset, value) {
|
|
view.setBigUint64(offset, value, true);
|
|
}
|
|
|
|
function arraySet(array, typedArray, offset) {
|
|
array.set(typedArray, offset);
|
|
}
|
|
|
|
function getDataView(array) {
|
|
return new DataView(array.buffer, array.byteOffset, array.byteLength);
|
|
}
|
|
|
|
function getLength(...arrayLikes) {
|
|
let result = 0;
|
|
arrayLikes.forEach(arrayLike => arrayLike && (result += arrayLike.length));
|
|
return result;
|
|
}
|
|
|
|
function getHeaderArrayData({
|
|
version,
|
|
bitFlag,
|
|
compressionMethod,
|
|
uncompressedSize,
|
|
compressedSize,
|
|
lastModDate,
|
|
rawLastModDate,
|
|
rawFilename,
|
|
zip64CompressedSize,
|
|
zip64UncompressedSize,
|
|
extraFieldLength
|
|
}) {
|
|
const headerRecord = createRecordWriter(HEADER_SIZE - 4);
|
|
const headerArray = headerRecord.array;
|
|
const headerView = getDataView(headerArray);
|
|
headerRecord.uint16(version);
|
|
headerRecord.uint16(bitFlag);
|
|
headerRecord.uint16(compressionMethod);
|
|
if (rawLastModDate === UNDEFINED_VALUE) {
|
|
const dateArray = new Uint32Array(1);
|
|
const dateView = getDataView(dateArray);
|
|
setUint16(dateView, 0, (((lastModDate.getHours() << 6) | lastModDate.getMinutes()) << 5) | lastModDate.getSeconds() / 2);
|
|
setUint16(dateView, 2, ((((lastModDate.getFullYear() - 1980) << 4) | (lastModDate.getMonth() + 1)) << 5) | lastModDate.getDate());
|
|
rawLastModDate = dateArray[0];
|
|
}
|
|
headerRecord.uint32(rawLastModDate);
|
|
headerRecord.skip(4);
|
|
if (zip64CompressedSize || compressedSize !== UNDEFINED_VALUE) {
|
|
headerRecord.uint32(zip64CompressedSize ? MAX_32_BITS : compressedSize);
|
|
} else {
|
|
headerRecord.skip(4);
|
|
}
|
|
if (zip64UncompressedSize || uncompressedSize !== UNDEFINED_VALUE) {
|
|
headerRecord.uint32(zip64UncompressedSize ? MAX_32_BITS : uncompressedSize);
|
|
} else {
|
|
headerRecord.skip(4);
|
|
}
|
|
headerRecord.uint16(getLength(rawFilename));
|
|
headerRecord.uint16(extraFieldLength);
|
|
return {
|
|
headerArray,
|
|
headerView,
|
|
rawLastModDate
|
|
};
|
|
}
|
|
|
|
function getBitFlag(level, useUnicodeFileNames, dataDescriptor, encrypted, compressionMethod) {
|
|
let bitFlag = 0;
|
|
if (useUnicodeFileNames) {
|
|
bitFlag = bitFlag | BITFLAG_LANG_ENCODING_FLAG;
|
|
}
|
|
if (dataDescriptor) {
|
|
bitFlag = bitFlag | BITFLAG_DATA_DESCRIPTOR;
|
|
}
|
|
if (compressionMethod == COMPRESSION_METHOD_DEFLATE || compressionMethod == COMPRESSION_METHOD_DEFLATE_64) {
|
|
if (level >= 0 && level <= 3) {
|
|
bitFlag = bitFlag | BITFLAG_LEVEL_SUPER_FAST_MASK;
|
|
}
|
|
if (level > 3 && level <= 5) {
|
|
bitFlag = bitFlag | BITFLAG_LEVEL_FAST_MASK;
|
|
}
|
|
if (level == 9) {
|
|
bitFlag = bitFlag | BITFLAG_LEVEL_MAX_MASK;
|
|
}
|
|
}
|
|
if (encrypted) {
|
|
bitFlag = bitFlag | BITFLAG_ENCRYPTED;
|
|
}
|
|
return bitFlag;
|
|
}
|
|
|
|
/*
|
|
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.
|
|
*/
|
|
|
|
|
|
function getMimeType$1() {
|
|
return "application/octet-stream";
|
|
}
|
|
|
|
/*
|
|
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 DEFAULT_THRESHOLD$2 = 1024 * 1024;
|
|
const DEFAULT_DIRECTORY_NAME$1 = ".zip.js-temp";
|
|
|
|
function createOPFSTempStream(options = {}) {
|
|
const {
|
|
thresholdBytes = DEFAULT_THRESHOLD$2,
|
|
directoryName = DEFAULT_DIRECTORY_NAME$1,
|
|
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 };
|
|
};
|
|
}
|
|
|
|
/*
|
|
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 DEFAULT_THRESHOLD$1 = 1024 * 1024;
|
|
|
|
function createBlobTempStream(options = {}) {
|
|
const {
|
|
thresholdBytes = DEFAULT_THRESHOLD$1
|
|
} = 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 };
|
|
};
|
|
}
|
|
|
|
/*
|
|
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 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 };
|
|
};
|
|
}
|
|
|
|
/*
|
|
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.
|
|
*/
|
|
|
|
|
|
try {
|
|
configure({ baseURI: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-native.cjs', document.baseURI).href)) });
|
|
} catch {
|
|
// ignored
|
|
}
|
|
|
|
var{Uint8Array:x,Uint16Array:g,Int32Array:R,TransformStream:H,Math:z,Error:L,Array:k}=globalThis,pe=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],Z=new x(0),qe=new g(0),de=[];for(let e=0;e<6;e++)de.push(e,0==e?8:4);de.push(0,1);var Se=[];for(let e=0;e<14;e++)Se.push(e,0==e?4:2);var Ee=new g([0,1,2,3,4,6,8,12,16,24,32,48,64,96,128,192,256,384,512,768,1024,1536,2048,3072,4096,6144,8192,12288,16384,24576]),ge=new g([0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224,0]);function M(e,t,n,r,i){if(0==i)return;let f=e instanceof x?e:new x(e.buffer,e.byteOffset,e.byteLength),_=n instanceof x?n.subarray(r,r+i):new x(n.buffer,n.byteOffset+r,i);f.set(_,t);}function Ve(e,t,n){0!=n&&(e instanceof x?e:new x(e.buffer,e.byteOffset,e.byteLength)).fill(0,t,t+n);}function je(){return {next_in:Z,next_in_index:0,avail_in:0,total_in:0,next_out:Z,next_out_index:0,avail_out:0,total_out:0,msg:"",t:0,i:0,_:0,l:void 0}}function Je(e,t){let n=1<<t;return {o:e,u:new x(n),h:n,v:t,k:0,m:0,T:0,p:0}}function te(e){let t=[];for(let n=0;n<e.length;n+=2){let r=e[n],i=e[n+1];for(let e=0;e<i;e++)t.push(r);}return new g(t)}var ne=class{constructor(e,t){this.M=e,this.C=t,this.Z=0;}},re=class{constructor(e,t,n,r,i){this.W=e,this.q=t,this.S=n,this.L=r,this.$=i;}};function D_(e){return Q_[e<-6||e>2?9:2-e]||""}function we(e,t){try{e.msg=D_(t);}catch(n){e.msg="zlib error "+String(t)+" ("+n+")";}return t}function $e(e,t){let n=e>>>0,r=0;for(let e=0;e<t;e++)r=r<<1|1&n,n>>>=1;return r}function T(e,t){e.D[e.A++]=t;}function Ae(e,t){T(e,255&t),T(e,t>>>8&255);}function e_(e,t,n){let r=255&n,i=65535&t,f=e.I+e.H;return e.D[f]=255&i,e.D[f+1]=i>>>8&255,e.D[f+2]=r,e.H+=3,i=i-1&65535,e.N[__[r]+ie+1].j++,e.U[y_(i)].j++,e.H==e.R}function De(e,t){let n=255&t,r=e.I+e.H;return e.D[r]=0,e.D[r+1]=0,e.D[r+2]=n,e.H+=3,e.N[n].j++,e.H==e.R}function ye(e){return e.h-ae}function y_(e){return e<256?A_[e]:A_[256+(e>>7)]}function v_(e){let t=Ce+7,n=1<<t,r=(1<<t)-1,i=z.floor((t+v-1)/v),f=1<<8+Ce;return {...Je(e,15),o:e,Y:42,P:0,B:void 0,F:32767,G:t,O:n,V:r,X:i,J:new g(32768),K:new g(n),ee:f,D:new x(32768),te:0,ne:32768,A:0,re:0,ie:0,fe:0,_e:0,le:0,oe:-2,ue:0,ae:0,ce:0,se:0,he:0,de:0,we:0,be:0,ve:0,ke:0,ge:0,me:0,xe:0,Te:0,ye:new R(2*Te+1),pe:new x(2*Te+1),ze:new g(be+1),H:0,R:0,Me:Z,I:0,Ce:0,Ze:0,We:8,qe:32768,Se:0,Le:0,$e:0,N:new k(fe).fill(0).map(()=>Q()),U:new k(2*me+1).fill(0).map(()=>Q()),De:new k(2*oe+1).fill(0).map(()=>Q()),Ae:w_(),Ie:w_(),He:w_()}}function I_(e){let t=[];for(let n=0;n<e.length;n+=2){let r=e[n],i=e[n+1],f=Q();f.Qe=r,f.je=i,t.push(f);}return t}function Q(){return {j:0,Qe:0,Ne:0,je:0}}function w_(){return new ne([],dn(null,Z,0,0,0))}function dn(e,t,n,r,i){return new re(e,t,n,r,i)}function $_(){let e=new k(288).fill(0);for(let t=0;t<=143;t++)e[t]=8;for(let t=144;t<=255;t++)e[t]=9;for(let t=256;t<=279;t++)e[t]=7;for(let t=280;t<=287;t++)e[t]=8;return e}function k_(e){let{code:t,length:n}=mn(e),r=new g(2*e.length),i=0;for(let f=0;f<e.length;f++){let e=n[f]||0,_=t[f]||0;r[i++]=e?$e(_,e):0,r[i++]=e;}return new g(r)}function et(e,t,n){let r=0;for(let n=0;n<e.length;n++){let i=t[n]?1<<t[n]:1,f=e[n]+i-1;f>r&&(r=f);}r<n&&(r=n);let i=new x(r+1);for(let n=0;n<=r;n++)for(let r=0;r<e.length;r++){let f=t[r]?1<<t[r]:1,_=e[r];if(n>=_&&n<=_+f-1){i[n]=r;break}}let f=0;for(let n=0;n<e.length-1;n++){let r=t[n]?1<<t[n]:1,i=e[n]+r-1;i>f&&(f=i);}return i[f]=e.length-1,i}function _t(e,t){let n=0;for(let r=0;r<e.length;r++){let i=t[r]?1<<t[r]:1,f=e[r]+i-1;f>n&&(n=f);}let r=new x(n+1);for(let i=0;i<=n;i++)for(let n=0;n<e.length;n++){let f=t[n]?1<<t[n]:1,_=e[n];if(i>=_&&i<=_+f-1){r[i]=n;break}}return r}function tt(e){let t=new x(512),n=e.length-1;for(let r=0;r<256;r++)t[r]=r<=n?e[r]:e[n];for(let r=256;r<=n;r++){let n=r>>7;t[256+(n>255?255:n)]=e[r];}for(let e=257;e<512;e++)0==t[e]&&(t[e]=t[e-1]);return t}function mn(e){let t=z.max(...e),n=new k(t+1).fill(0);for(let t of e)t>0&&n[t]++;let r=new k(e.length).fill(0),i=new k(t+1).fill(0),f=0;for(let e=1;e<=t;e++)f=f+n[e-1]<<1,i[e]=f;for(let t=0;t<e.length;t++){let n=e[t];0!=n&&(r[t]=i[n]++);}return {code:r,length:e}}var Ce=8,v=3,ee=258,ae=ee+v+1,nt=4096,Ue=16,He=ee,bn=29,ie=256,Te=ie+1+bn,me=30,oe=19,fe=2*Te+1,be=15,rt=9,at=255,it=32,ot=4,ve=256,t_=16,n_=17,r_=18,ft=0,N_=1,lt=2,$=-1,Q_=["need dictionary","stream end","","file error","stream error","data error","insufficient memory","buffer error",""],a_=te(de),i_=te(Se),Be=new g(19);Be[16]=2,Be[17]=3,Be[18]=7;var hn=k_($_()),sn=k_(new k(30).fill(5)),Fe=I_(hn),R_=I_(sn),__=et(ge,a_,ee),A_=tt(_t(Ee,i_));function he(e,t,n){if(void 0===t||void 0===n)return 1;let r=65535&e,i=e>>>16&65535,f=0;for(;n>0;){let e=n>2e3?2e3:n;n-=e;do{r=r+t[f++]|0,i=i+r|0;}while(--e);r%=65521,i%=65521;}return (i<<16|r)>>>0}var Ze=[[],[],[],[],[],[],[],[]];for(let e=0;e<256;e++){let t=e;for(let e=0;e<8;e++)t=1&t?3988292384^t>>>1:t>>>1;Ze[0][e]=t;}for(let e=0;e<256;e++)for(let t=1;t<8;t++){let n=Ze[t-1][e];Ze[t][e]=n>>>8^Ze[0][255&n];}var[ut,xn,pn,Sn,En,gn,Tn,wn]=Ze;function W(e=0,t,n){if(!t)return 0;void 0===n&&(n=t.length);let r=0|~e,i=0;if((n=z.min(n,t.length))>=8){let e=new DataView(t.buffer,t.byteOffset,n),f=n-8;for(;i<=f;i+=8){let t=r^e.getInt32(i,true),n=e.getInt32(i+4,true);r=wn[255&t]^Tn[t>>>8&255]^gn[t>>>16&255]^En[t>>>24&255]^Sn[255&n]^pn[n>>>8&255]^xn[n>>>16&255]^ut[n>>>24&255];}}for(;i<n;i++)r=r>>>8^ut[255&(r^t[i])];return (4294967295^r)>>>0}function xt(e){16==e.p?(Ae(e,e.T),e.T=0,e.p=0):e.p>=8&&(T(e,e.T),e.T>>=8,e.p-=8);}function pt(e){e.p>8?Ae(e,e.T):e.p>0&&T(e,e.T),e.Ce=1+(e.p-1&7),e.T=0,e.p=0;}function An(e,t,n){let r,i,f=[],_=0;for(r=1;r<=be;r++)_=_+n[r-1]<<1,f[r]=_;for(i=0;i<=t;i++){let t=e[i].je;0!=t&&(e[i].Qe=$e(f[t]++,t));}}function C(e,t,n){e.p>Ue-n?(e.T=65535&(e.T|t<<e.p),Ae(e,e.T),e.T=t>>Ue-e.p&65535,e.p+=n-Ue):(e.T=65535&(e.T|t<<e.p),e.p+=n);}function St(e){for(let t=0;t<e.N.length;t++)e.N[t].j=0;for(let t=0;t<e.U.length;t++)e.U[t].j=0;for(let t=0;t<e.De.length;t++)e.De[t].j=0;e.N[ve].j=1,e.ie=e.fe=0,e.H=e._e=0;}function Et(e){if(e.N&&e.N.length>=fe)for(let t=0;t<fe;t++)e.N[t]=Q();else {e.N=[];for(let t=0;t<fe;t++)e.N.push(Q());}if(e.U&&e.U.length>=2*me+1)for(let t=0;t<2*me+1;t++)e.U[t]=Q();else {e.U=[];for(let t=0;t<2*me+1;t++)e.U.push(Q());}if(e.De&&e.De.length>=2*oe+1)for(let t=0;t<2*oe+1;t++)e.De[t]=Q();else {e.De=[];for(let t=0;t<2*oe+1;t++)e.De.push(Q());}e.Ae=new ne(e.N,new re(Fe,a_,ie+1,Te,be)),e.Ie=new ne(e.U,new re(R_,i_,0,me,be)),e.He=new ne(e.De,new re(null,Be,0,oe,7)),e.T=0,e.p=0,e.Ce=0,St(e);}var se=1;function Dn(e,t,n){return n=e.ye[se],e.ye[se]=e.ye[e.Le--],z_(e,t,se),n}function mt(e,t,n,r){return e[t].j<e[n].j||e[t].j==e[n].j&&r[t]<=r[n]}function z_(e,t,n){let r=e.ye[n],i=n<<1;for(;i<=e.Le&&(i<e.Le&&mt(t,e.ye[i+1],e.ye[i],e.pe)&&i++,!mt(t,r,e.ye[i],e.pe));)e.ye[n]=e.ye[i],n=i,i<<=1;e.ye[n]=r;}function yn(e,t){let n,r,i,f,_,l,o=t.M,u=t.Z,a=t.C.W,c=t.C.q,s=t.C.S,h=t.C.$,d=0;for(f=0;f<=be;f++)e.ze[f]=0;for(o[e.ye[e.$e]].je=0,n=e.$e+1;n<fe;n++)r=e.ye[n],f=o[o[r].Ne].je+1,f>h&&(f=h,d++),o[r].je=f,!(r>u)&&(e.ze[f]++,_=0,r>=s&&(_=c[r-s]),l=o[r].j,e.ie+=l*(f+_),a&&(e.fe+=l*(a[r].je+_)));if(0!=d){do{for(f=h-1;0==e.ze[f];)f--;e.ze[f]--,e.ze[f+1]+=2,e.ze[h]--,d-=2;}while(d>0);for(f=h;0!=f;f--)for(r=e.ze[f];0!=r;)i=e.ye[--n],!(i>u)&&(o[i].je!=f&&(e.ie+=(f-o[i].je)*o[i].j,o[i].je=f),r--);}}function L_(e,t){let n,r,i,f=t.M,_=t.C.W,l=t.C.L,o=-1;for(e.Le=0,e.$e=fe,n=0;n<l;n++)0!=f[n].j?(e.ye[++e.Le]=o=n,e.pe[n]=0):f[n].je=0;for(;e.Le<2;)i=e.ye[++e.Le]=o<2?++o:0,f[i].j=1,e.pe[i]=0,e.ie--,_&&(e.fe-=_[i].je);for(t.Z=o,n=z.floor(e.Le/2);n>=1;n--)z_(e,f,n);i=l;do{n=Dn(e,f,n),r=e.ye[se],e.ye[--e.$e]=n,e.ye[--e.$e]=r,f[i].j=f[n].j+f[r].j,e.pe[i]=(e.pe[n]>=e.pe[r]?e.pe[n]:e.pe[r])+1,f[n].Ne=f[r].Ne=i,e.ye[se]=i++,z_(e,f,se);}while(e.Le>=2);e.ye[--e.$e]=e.ye[se],yn(e,t),An(f,t.Z,e.ze);}function bt(e,t,n){let r,i,f=-1,_=t[0].je,l=0,o=7,u=4;for(0==_&&(o=138,u=3),t[n+1].je=65535,r=0;r<=n;r++)i=_,_=t[r+1].je,!(++l<o&&i==_)&&(l<u?e.De[i].j+=l:0!=i?(i!=f&&e.De[i].j++,e.De[t_].j++):l<=10?e.De[n_].j++:e.De[r_].j++,l=0,f=i,0==_?(o=138,u=3):i==_?(o=6,u=3):(o=7,u=4));}function ht(e,t,n){let r,i=-1,f=t[0].je,_=0,l=7,o=4;0==f&&(l=138,o=3);for(let u=0;u<=n;u++)if(r=f,f=t[u+1].je,!(++_<l&&r==f)){if(_<o)do{C(e,e.De[r].Qe,e.De[r].je);}while(0!=--_);else 0!=r?(r!=i&&(C(e,e.De[r].Qe,e.De[r].je),_--),C(e,e.De[t_].Qe,e.De[t_].je),C(e,_-3,2)):_<=10?(C(e,e.De[n_].Qe,e.De[n_].je),C(e,_-3,3)):(C(e,e.De[r_].Qe,e.De[r_].je),C(e,_-11,7));_=0,i=r,0==f?(l=138,o=3):r==f?(l=6,o=3):(l=7,o=4);}}function vn(e){let t;for(bt(e,e.N,e.Ae.Z),bt(e,e.U,e.Ie.Z),L_(e,e.He),t=oe-1;t>=3&&0==e.De[pe[t]].je;t--);return e.ie+=3*(t+1)+5+5+4,t}function In(e,t,n,r){let i;for(C(e,t-257,5),C(e,n-1,5),C(e,r-4,4),i=0;i<r;i++)C(e,e.De[pe[i]].je,3);ht(e,e.N,t-1),ht(e,e.U,n-1);}function Pe(e,t,n,r,i=0){C(e,(ft<<1)+r,3),pt(e),Ae(e,n),Ae(e,~n),n&&t&&M(e.D,e.A,t,i,n),e.A+=n;}function gt(e){xt(e);}function Tt(e){C(e,N_<<1,3),C(e,Fe[ve].Qe,Fe[ve].je),xt(e);}function st(e,t,n){let r,i,f,_,l=0;if(0!=e.H)do{r=255&e.Me[l],r+=(255&e.Me[l+1])<<8,i=e.Me[l+2],l+=3,0==r?C(e,t[i].Qe,t[i].je):(f=__[i],C(e,t[f+ie+1].Qe,t[f+ie+1].je),_=a_[f],0!=_&&(i-=ge[f],C(e,i,_)),r--,f=y_(r),C(e,n[f].Qe,n[f].je),_=i_[f],0!=_&&(r-=Ee[f],C(e,r,_)));}while(l<e.H);C(e,t[ve].Qe,t[ve].je);}function kn(e){let t,n=4093624447;for(t=0;t<=31;t++,n>>=1)if(1&n&&0!=e.N[t].j)return 0;if(0!=e.N[9].j||0!=e.N[10].j||0!=e.N[13].j)return 1;for(t=32;t<ie;t++)if(0!=e.N[t].j)return 1;return 0}function wt(e,t,n,r,i=0){let f,_,l=0;e.ve>0?(2==e.o.t&&(e.o.t=kn(e)),L_(e,e.Ae),L_(e,e.Ie),l=vn(e),f=e.ie+3+7>>3,_=e.fe+3+7>>3,(_<=f||4==e.ke)&&(f=_)):f=_=n+5,n+4<=f&&t?Pe(e,t,n,r,i):_==f?(C(e,(N_<<1)+r,3),st(e,Fe,R_)):(C(e,(lt<<1)+r,3),In(e,e.Ae.Z+1,e.Ie.Z+1,l+1),st(e,e.N,e.U)),St(e),r&&pt(e);}function vt(){let e=je();return e.l=v_(e),e}var Ye=[{Ue:Lt,Re:0,Ye:0,Ee:0,Pe:0},{Ue:U_,Re:4,Ye:4,Ee:8,Pe:4},{Ue:U_,Re:4,Ye:5,Ee:16,Pe:8},{Ue:U_,Re:4,Ye:6,Ee:32,Pe:32},{Ue:Ne,Re:4,Ye:4,Ee:16,Pe:16},{Ue:Ne,Re:8,Ye:16,Ee:32,Pe:32},{Ue:Ne,Re:8,Ye:16,Ee:128,Pe:128},{Ue:Ne,Re:8,Ye:32,Ee:128,Pe:256},{Ue:Ne,Re:32,Ye:128,Ee:258,Pe:1024},{Ue:Ne,Re:32,Ye:258,Ee:258,Pe:4096}];function At(e){return 2*e-(e>4?9:0)}function l_(e,t,n){return ((t<<e.X^n)&e.V)>>>0}function u_(e,t){e.be=l_(e,e.be,e.u[t+(v-1)]);let n=e.J[t&e.F]=e.K[e.be];return e.K[e.be]=t,n}function It(e){e.K[e.O-1]=0,Ve(e.K,0,(e.O-1)*e.K.BYTES_PER_ELEMENT);}function Un(e){let t,n,r=e.h;for(t=e.O;t>0;)t--,n=e.K[t],e.K[t]=n>=r?n-r:0;for(t=r;t>0;)t--,n=e.J[t],e.J[t]=n>=r?n-r:0;}function H_(e,t,n,r){let i=e.avail_in;return i>r&&(i=r),0==i?0:(e.avail_in-=i,M(t,n,e.next_in,e.next_in_index,i),1==e.l.P?e.i=he(e.i,new x(t.buffer,t.byteOffset+n,i),i):2==e.l.P&&(e.i=W(e.i,new x(t.buffer,t.byteOffset+n,i),i)),e.next_in_index+=i,e.total_in+=i,i)}function c_(e){let t,n,r=e.h;do{if(n=e.qe-e.ce-e.ae,0==n&&0==e.ae&&0==e.ce?n=r:-1==n&&n--,e.ae>=r+ye(e)&&(M(e.u,0,e.u,r,r-n),e.Se-=r,e.ae-=r,e.ue-=r,e.le>e.ae&&(e.le=e.ae),Un(e),n+=r),0==e.o.avail_in)break;if(t=H_(e.o,e.u,e.ae+e.ce,n),e.ce+=t,e.ce+e.le>=v){let t=e.ae-e.le;for(e.be=e.u[t],e.be=l_(e,e.be,e.u[t+1]);e.le&&(e.be=l_(e,e.be,e.u[t+v-1]),e.J[t&e.F]=e.K[e.be],e.K[e.be]=t,t++,e.le--,!(e.ce+e.le<v)););}}while(e.ce<ae&&0!=e.o.avail_in);if(e.k<e.qe){let t,n=e.ae+e.ce;e.k<n?(t=e.qe-n,t>He&&(t=He),Ve(e.u,n,t),e.k=n+t):e.k<n+He&&(t=n+He-e.k,t>e.qe-e.k&&(t=e.qe-e.k),Ve(e.u,e.k,t),e.k+=t);}}function kt(e,t,n=8,r=15,i=Ce,f=0){let _=1;if(!e)return -2;if(e.msg="",-1==t&&(t=6),r<0){if(_=0,r<-15)return -2;r=-r;}else r>15&&(_=2,r-=16);if(i<1||i>rt||8!=n||r<8||r>15||t<0||t>9||f<0||f>4||8==r&&1!=_)return -2;8==r&&(r=9);let l=v_(e);return l?(e.l=l,l.o=e,l.Y=42,l.P=_,l.B=void 0,l.v=r,l.h=1<<l.v,l.F=l.h-1,l.G=i+7,l.O=1<<l.G,l.V=l.O-1,l.X=(l.G+v-1)/v,l.u=new x(2*l.h),l.J=new g(l.h),l.K=new g(l.O),l.k=0,l.ee=1<<i+6,l.D=new x(l.ee*ot),l.ne=4*l.ee,l.u&&l.J&&l.K&&l.D?(l.Me=l.D.subarray(l.ee),l.I=l.te+l.ee,l.R=3*(l.ee-1),l.ve=t,l.ke=f,l.We=n,Fn(e)):(l.Y=666,e.msg=D_(-4),P_(e),-4)):-4}function Z_(e){if(null==e)return true;let t=e.l;return !t||t.o!=e||42!=t.Y&&57!=t.Y&&69!=t.Y&&73!=t.Y&&91!=t.Y&&103!=t.Y&&113!=t.Y&&666!=t.Y}function Hn(e){let t;return Z_(e)?-2:(e.total_in=e.total_out=0,e.msg="",e.t=2,t=e.l,t.A=0,t.re=t.te,t.P<0&&(t.P=-t.P),t.Y=2==t.P?57:42,e.i=2==t.P?W(0):he(0),t.oe=-2,Et(t),0)}function Bn(e){e.qe=2*e.h,It(e),e.Te=Ye[e.ve].Ye,e.ge=Ye[e.ve].Re,e.me=Ye[e.ve].Ee,e.xe=Ye[e.ve].Pe,e.ae=0,e.ue=0,e.ce=0,e.le=0,e.se=e.he=v-1,e.we=0,e.be=0;}function Fn(e){let t=Hn(e);return 0==t&&Bn(e.l),t}function Me(e,t){T(e,t>>8),T(e,255&t);}function q(e){let t,n=e.l;gt(n),t=n.A,t>e.avail_out&&(t=e.avail_out),0!=t&&(M(e.next_out,e.next_out_index,n.D,n.re,t),e.next_out_index+=t,n.re+=t,e.total_out+=t,e.avail_out-=t,n.A-=t,0==n.A&&(n.re=n.te));}function Ie(e,t){let n=e.l;n.B&&n.B.Be&&(e.i=W(e.i,new x(n.D.buffer,n.te+t,n.A-t),n.A-t));}function Nt(e,t){let n,r=e.l;if(Z_(e)||t>5||t<0)return we(e,-2);if(!e.next_out||0!=e.avail_in&&!e.next_in||666==r.Y&&4!=t)return we(e,-2);if(0==e.avail_out)return we(e,-5);if(n=r.oe,r.oe=t,0!=r.A){if(q(e),0==e.avail_out)return r.oe=$,0}else if(0==e.avail_in&&At(t)<=At(n)&&4!=t)return we(e,-5);if(666==r.Y&&0!=e.avail_in)return we(e,-5);if(42==r.Y&&0==r.P&&(r.Y=113),42==r.Y){let t,n=8+(r.v-8<<4)<<8;if(t=r.ke>=2||r.ve<2?0:r.ve<6?1:6==r.ve?2:3,n|=t<<6,0!=r.ae&&(n|=it),n+=31-n%31,Me(r,n),0!=r.ae&&(Me(r,e.i>>16),Me(r,65535&e.i)),e.i=1,r.Y=113,q(e),0!=r.A)return r.oe=$,0}if(57==r.Y)if(e.i=W(0),T(r,31),T(r,139),T(r,8),r.B)T(r,(r.B.Fe?1:0)+(r.B.Be?2:0)+(null==r.B.Ge?0:4)+(null==r.B.Oe?0:8)+(null==r.B.Ve?0:16)),T(r,255&r.B.Xe),T(r,r.B.Xe>>>8&255),T(r,r.B.Xe>>>16&255),T(r,r.B.Xe>>>24&255),T(r,9==r.ve?2:r.ke>=2||r.ve<2?4:0),T(r,255&r.B.Je),null!=r.B.Ge&&(T(r,255&r.B.Ke),T(r,r.B.Ke>>>8&255)),r.B.Be&&(e.i=W(e.i,r.D,r.A)),r.Ze=0,r.Y=69;else if(T(r,0),T(r,0),T(r,0),T(r,0),T(r,0),T(r,9==r.ve?2:r.ke>=2||r.ve<2?4:0),T(r,at),r.Y=113,q(e),0!=r.A)return r.oe=$,0;if(69==r.Y){if(r.B&&null!=r.B.Ge){let t=r.A,n=(65535&r.B.Ke)-r.Ze;for(;r.A+n>r.ne;){let i=r.ne-r.A;if(M(r.D,r.A,r.B.Ge,r.Ze,i),r.A=r.ne,Ie(e,t),r.Ze+=i,q(e),0!=r.A)return r.oe=$,0;t=0,n-=i;}M(r.D,r.A,r.B.Ge,r.Ze,n),r.A+=n,Ie(e,t),r.Ze=0;}r.Y=73;}if(73==r.Y){if(r.B&&r.B.Oe&&r.B.Oe.length){let t,n=r.A;do{if(r.A==r.ne){if(Ie(e,n),q(e),0!=r.A)return r.oe=$,0;n=0;}t=r.B.Oe[r.Ze++],T(r,t);}while(0!=t);Ie(e,n),r.Ze=0;}r.Y=91;}if(91==r.Y){if(r.B&&r.B.Ve&&r.B.Ve.length){let t,n=r.A;do{if(r.A==r.ne){if(Ie(e,n),q(e),0!=r.A)return r.oe=$,0;n=0;}t=r.B.Ve[r.Ze++],T(r,t);}while(0!=t);Ie(e,n);}r.Y=103;}if(103==r.Y){if(r.B&&r.B.Be){if(r.A+2>r.ne&&(q(e),0!=r.A))return r.oe=$,0;T(r,255&e.i),T(r,e.i>>>8&255),e.i=W(0);}if(r.Y=113,q(e),0!=r.A)return r.oe=$,0}if(0!=e.avail_in||0!=r.ce||0!=t&&666!=r.Y){let n=0==r.ve?Lt(r,t):2==r.ke?Pn(r,t):3==r.ke?Zn(r,t):Ye[r.ve].Ue(r,t);if((2==n||3==n)&&(r.Y=666),0==n||2==n)return 0==e.avail_out&&(r.oe=$),0;if(1==n&&(1==t?Tt(r):5!=t&&(Pe(r,null,0,0),3==t&&(It(r),0==r.ce&&(r.ae=0,r.ue=0,r.le=0))),q(e),0==e.avail_out))return r.oe=$,0}return 4!=t?0:r.P<=0?1:(2==r.P?(T(r,255&e.i),T(r,e.i>>>8&255),T(r,e.i>>>16&255),T(r,e.i>>>24&255),T(r,255&e.total_in),T(r,e.total_in>>>8&255),T(r,e.total_in>>>16&255),T(r,e.total_in>>>24&255)):(Me(r,e.i>>>16&65535),Me(r,65535&e.i)),q(e),r.P>0&&(r.P=-r.P),0!=r.A?0:1)}function P_(e){if(Z_(e))return -2;let t=e.l,n=t.Y;return t.u=Z,t.J=qe,t.K=qe,t.D=Z,t.Me=Z,t.ye=new R(0),t.pe=Z,t.ze=qe,t.N.length=0,t.U.length=0,t.De.length=0,t.B=void 0,t.te=0,t.re=0,t.I=0,113==n?-3:0}function Rt(e,t){let n,r,i=e.xe,f=e.ae,_=e.he,l=e.me,o=e.ae>ye(e)?e.ae-ye(e):0,u=e.J,a=e.F,c=e.u,s=e.ce,h=ee<s?ee:s,d=c[f],w=c[f+1],b=c[f+_-1],v=c[f+_];_>=e.ge&&(i>>=2),l>s&&(l=s);do{if(n=t,c[n+_]!=v||c[n+_-1]!=b||c[n]!=d||c[n+1]!=w)continue;let i=2;for(;i<h&&c[f+i]==c[n+i];)i++;if(r=i,r>_){if(e.Se=t,_=r,r>=l)break;b=c[f+_-1],v=c[f+_];}}while((t=u[t&a])>o&&0!=--i);return _<=s?_:s}function zt(e,t){wt(e,e.u,e.ae-e.ue,t,e.ue),e.ue=e.ae,q(e.o);}function j(e,t){return zt(e,t?1:0),0==e.o.avail_out?t?2:0:null}var Dt=65535;function ke(e,t){return e<t?e:t}function Lt(e,t){let n,r,i,f=ke(e.ne-5,e.h),_=0,l=e.o.avail_in;do{if(n=Dt,i=e.p+42>>3,e.o.avail_out<i||(i=e.o.avail_out-i,r=e.ae-e.ue,n>r+e.o.avail_in&&(n=r+e.o.avail_in),n>i&&(n=i),n<f&&(0==n&&4!=t||0==t||n!=r+e.o.avail_in)))break;_=4==t&&n==r+e.o.avail_in?1:0,Pe(e,null,0,_),e.D[e.A-4]=n,e.D[e.A-3]=n>>8,e.D[e.A-2]=~n,e.D[e.A-1]=~n>>8,q(e.o),r&&(r>n&&(r=n),M(e.o.next_out,e.o.next_out_index,e.u,e.ue,r),e.o.next_out_index+=r,e.o.avail_out-=r,e.o.total_out+=r,e.ue+=r,n-=r),n&&(H_(e.o,e.o.next_out,e.o.next_out_index,n),e.o.next_out_index+=n,e.o.avail_out-=n,e.o.total_out+=n);}while(0==_);if(l-=e.o.avail_in,l){if(l>=e.h){e._e=2;let t=e.o.next_in_index-e.h;M(e.u,0,e.o.next_in,t,e.h),e.ae=e.h,e.le=e.ae;}else e.qe-e.ae<=l&&(e.ae-=e.h,M(e.u,0,e.u,e.h,e.ae),e._e<2&&e._e++,e.le>e.ae&&(e.le=e.ae)),M(e.u,e.ae,e.o.next_in,e.o.next_in_index-l,l),e.ae+=l,e.le+=ke(l,e.h-e.le);e.ue=e.ae;}return e.k<e.ae&&(e.k=e.ae),_?(e.Ce=8,3):0!=t&&4!=t&&0==e.o.avail_in&&e.ae==e.ue?1:(i=e.qe-e.ae,e.o.avail_in>i&&e.ue>=e.h&&(e.ue-=e.h,e.ae-=e.h,M(e.u,0,e.u,e.h,e.ae),e._e<2&&e._e++,i+=e.h,e.le>e.ae&&(e.le=e.ae)),i>e.o.avail_in&&(i=e.o.avail_in),i&&(H_(e.o,e.u,e.ae,i),e.ae+=i,e.le+=ke(i,e.h-e.le)),e.k<e.ae&&(e.k=e.ae),i=e.p+42>>3,i=ke(e.ne-i,Dt),f=ke(i,e.h),r=e.ae-e.ue,(r>=f||(r||4==t)&&0!=t&&0==e.o.avail_in&&r<=i)&&(n=ke(r,i),_=4==t&&0==e.o.avail_in&&n==r?1:0,Pe(e,e.u,n,_,e.ue),e.ue+=n,q(e.o)),_&&(e.Ce=8),_?2:0)}function U_(e,t){let n,r=false;for(;;){if(e.ce<ae){if(c_(e),e.ce<ae&&0==t)return 0;if(0==e.ce)break}if(n=0,e.ce>=v&&(n=u_(e,e.ae)),0!=n&&e.ae-n<=ye(e)&&(e.se=Rt(e,n)),e.se>=v)if(e.ae,e.Se,e.se,r=e_(e,e.ae-e.Se,e.se-v),e.ce-=e.se,e.se<=e.Te&&e.ce>=v){e.se--;do{e.ae++,n=u_(e,e.ae);}while(0!=--e.se);e.ae++;}else e.ae+=e.se,e.se=0,e.be=e.u[e.ae],e.be=l_(e,e.be,e.u[e.ae+1]);else r=De(e,e.u[e.ae]),e.ce--,e.ae++;if(r){let t=j(e,false);if(null!=t)return t}}if(e.le=e.ae<v-1?e.ae:v-1,4==t){return j(e,true)??3}if(e.H){let t=j(e,false);if(null!=t)return t}return 1}function Ne(e,t){let n,r=false;for(;;){if(e.ce<ae){if(c_(e),e.ce<ae&&0==t)return 0;if(0==e.ce)break}if(n=0,e.ce>=v&&(n=u_(e,e.ae)),e.he=e.se,e.de=e.Se,e.se=v-1,0!=n&&e.he<e.Te&&e.ae-n<=ye(e)&&(e.se=Rt(e,n),e.se<=5&&(1==e.ke||e.se==v&&e.ae-e.Se>nt)&&(e.se=v-1)),e.he>=v&&e.se<=e.he){let t=e.ae+e.ce-v;e.ae,e.de,e.he,r=e_(e,e.ae-1-e.de,e.he-v),e.ce-=e.he-1,e.he-=2;do{++e.ae<=t&&(n=u_(e,e.ae));}while(0!=--e.he);if(e.we=0,e.se=v-1,e.ae++,r){let t=j(e,false);if(null!=t)return t}}else if(e.we){if(r=De(e,e.u[e.ae-1]),r&&zt(e,0),e.ae++,e.ce--,0==e.o.avail_out)return 0}else e.we=1,e.ae++,e.ce--;}if(e.we&&(r=De(e,e.u[e.ae-1]),e.we=0),e.le=e.ae<v-1?e.ae:v-1,4==t){return j(e,true)??3}if(e.H){let t=j(e,false);if(null!=t)return t}return 1}function Zn(e,t){let n,r,i,f;for(;;){if(e.ce<=ee){if(c_(e),e.ce<=ee&&0==t)return 0;if(0==e.ce)break}if(e.se=0,e.ce>=v&&e.ae>0&&(i=e.ae-1,r=e.u[i],r==++i&&r==++i&&r==++i)){f=e.ae+ee;do{}while(r==++i&&r==++i&&r==++i&&r==++i&&r==++i&&r==++i&&r==++i&&r==++i&&i<f);e.se=ee-(f-i),e.se>e.ce&&(e.se=e.ce);}if(e.se>=v?(e.ae,e.ae,e.se,n=e_(e,1,e.se-v),e.ce-=e.se,e.ae+=e.se,e.se=0):(n=De(e,e.u[e.ae]),e.ce--,e.ae++),n){let t=j(e,false);if(null!=t)return t}}if(e.le=0,4==t){return j(e,true)??3}if(e.H){let t=j(e,false);if(null!=t)return t}return 1}function Pn(e,t){let n=false;for(;;){if(0==e.ce&&(c_(e),0==e.ce)){if(0==t)return 0;break}if(e.se=0,n=De(e,e.u[e.ae]),e.ce--,e.ae++,n){let t=j(e,false);if(null!=t)return t}}if(e.le=0,4==t){return j(e,true)??3}if(e.H){let t=j(e,false);if(null!=t)return t}return 1}var ue=852,d_=592,m_=594,Ot=Ee.map(e=>e+1),Ct=ge.subarray(0,-1).map(e=>e+3),Mn=[16,1,73,1,200,1],Yn=[144,1,72,1,78,1],Ut=Se.map(qt),Ht=Se.map(Vt);Ut.push(64,2),Ht.push(142,2);var Bt=de.slice(0,-2).map(qt),Ft=de.slice(0,-2).map(Vt);Bt.push(...Mn),Ft.push(...Yn);var Zt=new g([...Ct,258,0,0]),Pt=new g([...Ct,3,0,0]),Mt=te(Bt),Yt=te(Ft),Xt=new g([...Ot,0,0]),Wt=new g([...Ot,32769,49153]),Gt=te(Ut),Kt=te(Ht);function qt(e,t){return t%2?e:e+16}function Vt(e,t){return t%2?e:e+128}function Jt(e,t){let n,r=e.l,i=e.next_in_index,f=e.next_out_index,_=e.next_in,l=e.next_out,o=r.u,u=r.T>>>0,a=r.p>>>0,c=r.et,s=r.tt,h=(1<<r.nt)-1,d=(1<<r.rt)-1,w=r.h>>>0,b=r.k>>>0,v=r.m>>>0,k=r.it,g=f-(t-e.avail_out),m=f+(e.avail_out-257),x=i+(e.avail_in-5),T=0,y=0,p=0,z=0;e:do{for(;a<15;){if(!(i<_.length))break e;u+=_[i++]<<a,a+=8;}n=c[u&h];t:for(;;){if(p=n>>>16&255,u>>>=p,a-=p,p=n>>>24,0==p){l[f++]=65535&n;break}if(16&p){if(T=65535&n,p&=15,p){for(;a<p;){if(!(i<_.length)){r.ft=16200;break e}u+=_[i++]<<a,a+=8;}T+=u&(1<<p)-1,u>>>=p,a-=p;}for(;a<15;){if(!(i<_.length)){r.ft=16200;break e}u+=_[i++]<<a,a+=8;}n=s[u&d];n:for(;;){if(p=n>>>16&255,u>>>=p,a-=p,p=n>>>24,16&p){if(y=65535&n,p&=15,p){for(;a<p;){if(!(i<_.length)){r.ft=16200;break e}u+=_[i++]<<a,a+=8;}y+=u&(1<<p)-1,u>>>=p,a-=p;}let t=T,c=f-g;if(y>c){let n=y-c;if(n>b&&k){e.msg="invalid distance too far back",r.ft=16209;break e}if(0==v){if(z=w-n,!(n<t)){for(let e=0;e<t;++e)l[f++]=o[z++];continue e}for(let e=0;e<n;++e)l[f++]=o[z++];t-=n,z=f-y;}else if(v<n){z=w+v-n;let e=n-v;if(!(e<t)){for(let e=0;e<t;++e)l[f++]=o[z++];continue e}for(let t=0;t<e;++t)l[f++]=o[z++];if(t-=e,z=0,v<t){for(let e=0;e<v;++e)l[f++]=o[z++];t-=v,z=f-y;}}else {if(z=v-n,!(n<t)){for(let e=0;e<t;++e)l[f++]=o[z++];continue e}for(let e=0;e<n;++e)l[f++]=o[z++];t-=n,z=f-y;}for(;t>2;)l[f++]=l[z++],l[f++]=l[z++],l[f++]=l[z++],t-=3;t&&(l[f++]=l[z++],t>1&&(l[f++]=l[z++]));}else {for(z=f-y;t>2;)l[f++]=l[z++],l[f++]=l[z++],l[f++]=l[z++],t-=3;t&&(l[f++]=l[z++],t>1&&(l[f++]=l[z++]));}break}if(64&p){e.msg="invalid distance code",r.ft=16209;break e}n=s[(65535&n)+(u&(1<<p)-1)];continue n}break}if(64&p){if(32&p){r.ft=16191;break e}e.msg="invalid literal/length code",r.ft=16209;break e}n=c[(65535&n)+(u&(1<<p)-1)];continue t}}while(i<x&&f<m);let M=a>>3;i-=M,a-=M<<3,u&=(1<<a)-1,e.next_in_index=i,e.next_out_index=f,e.avail_in=i<x?x-i+5:5-(i-x),e.avail_out=f<m?m-f+257:257-(f-m),r.T=u>>>0,r.p=a>>>0;}var Xn=new R(0);function M_(e,t){let n=Xn,r=t?ue+m_:ue+d_;return {...Je(e,0),o:e,ft:16180,_t:false,P:0,lt:false,ot:0,ut:0,ct:0,st:0,u:Z,ht:0,dt:0,Ge:0,et:n,tt:n,nt:0,rt:0,wt:0,bt:0,vt:0,kt:0,gt:n,xt:new g(320),Tt:new g(288),yt:new R(r),zt:0,it:true,Mt:0,Ct:0,Zt:t}}function We(e,t,n){return e<<24|t<<16|n}function b_(e=0,t=0,n=0){return We(e,t,n)}function h_(e=1){return We(64,e,0)}function Qt(e=0){return We(96,e,0)}function Y_(e){return (255&e)<<24|(e>>8&255)<<16|(e>>16&255)<<8|e>>24&255}var Le=15,Gn={Zt:false,Wt:Zt,qt:Mt,St:Xt,Lt:Gt,$t:20,Dt:257,At:0,It:d_,Ht:false,Qt:true},Kn={Zt:true,Wt:Pt,qt:Yt,St:Wt,Lt:Kt,$t:19,Dt:256,At:-1,It:m_,Ht:true,Qt:false};function Oe(e,t,n,r,i,f,_,l){let o,u,a,c,s,h,d,w,b,v,k,m,x,T,y,p,z,M,C,Z=new g(Le+1),W=new g(Le+1),q=l?Kn:Gn;for(o=0;o<=Le;o++)Z[o]=0;for(u=0;u<n;u++)Z[t[u]]++;for(s=i.jt,c=Le;c>=1&&0==Z[c];c--);if(s>c&&(s=c),0==c)return q.Qt?(y=h_(1),r.jt[0]=y,r.jt[1]=y,i.jt=1,0):-1;for(a=1;a<c&&0==Z[a];a++);for(s<a&&(s=a),w=1,o=1;o<=Le;o++)if(w<<=1,w-=Z[o],w<0)return -1;if(w>0&&(0==e||1!=c))return -1;for(W[1]=0,o=1;o<Le;o++)W[o+1]=W[o]+Z[o];for(u=0;u<n;u++)0!=t[u]&&(f[W[t[u]]++]=u);switch(e){case 0:z=M=f,C=q.$t;break;case 1:z=q.Wt,M=q.qt,C=q.Dt;break;default:z=q.St,M=q.Lt,C=q.At;}if(v=0,u=0,o=a,p=_.jt,h=s,d=0,x=-1,b=1<<s,T=b-1,1==e&&(q.Ht?b>=ue:b>ue)||2==e&&(q.Ht?b>=q.It:b>q.It))return 1;for(;;){y=qn(f,u,o,d,e,z,M,C,q.Zt),k=1<<o-d,m=1<<h,a=m;do{m-=k;let e=(v>>d)+m;r.jt[p+e]=y;}while(0!=m);for(k=1<<o-1;v&k;)k>>=1;if(0!=k?(v&=k-1,v+=k):v=0,u++,0==--Z[o]){if(o==c)break;o=t[f[u]];}if(o>s&&(v&T)!=x){for(0==d&&(d=s),p+=1<<h,h=o-d,w=1<<h;h+d<c&&(w-=Z[h+d],!(w<=0));)h++,w<<=1;if(b+=1<<h,1==e&&(q.Ht?b>=ue:b>ue)||2==e&&(q.Ht?b>=q.It:b>q.It))return 1;x=v&T,r.jt[_.jt+x]=We(h,s,p-_.jt);}}if(0!=v)for(y=h_(o-d);0!=v;){for(0!=d&&(v&T)!=x&&(d=0,o=s,p=_.jt,h=s,y=h_(o)),r.jt[p+(v>>d)]=y,k=1<<o-1;v&k;)k>>=1;0!=k?(v&=k-1,v+=k):v=0;}return _.jt+=b,i.jt=s,0}function qn(e,t,n,r,i,f,_,l,o){let u;if(o?e[t]<l:e[t]+1<l)u=b_(0,n-r,e[t]);else if(o?e[t]>l:e[t]>=l)if(o&&1==i){let i=e[t]-257;u=b_(_[i],n-r,f[i]);}else {let i=o?e[t]:e[t]-l;u=b_(_[i],n-r,f[i]);}else u=Qt(n-r);return u}var p_=new R(0),Qn={Nt:true,Ut:new R(544),Rt:p_,Yt:p_},$n={Nt:true,Ut:new R(544),Rt:p_,Yt:p_};function $t(){let e=je();return e.l=M_(e,false),e}function Ge(e){let t;return !(e&&(t=e.l,!(!t||t.o!=e||t.Zt&&(t.ft<16191||t.ft>16209)||!t.Zt&&(t.ft<16180||t.ft>16211))))}function er(e){let t;return Ge(e)?-2:(t=e.l,e.total_in=e.total_out=t.st=0,e.msg="",t.P&&(e.i=1&t.P),t.ft=t.Zt?16191:16180,t._t=false,t.lt=false,t.ot=-1,t.ut=t.Zt?65536:32768,delete t.B,t.T=0,t.p=0,t.et=t.yt,t.tt=t.yt,t.gt=t.yt,t.it=true,t.Mt=-1,0)}function _r(e){let t;return Ge(e)?-2:(t=e.l,t.h=0,t.k=0,t.m=0,er(e))}function tr(e,t){let n,r;if(Ge(e))return -2;if(r=e.l,t<0){if(t<-16)return -2;n=0,r.Zt=-16==t,t=-t;}else n=5+(t>>4),r.Zt=false,t<48&&(t&=15);let i=r.Zt?16:15;return t&&(t<8||t>i)?-2:(r.u.length>0&&r.v!=t&&(r.u=Z),r.P=n,r.v=t,_r(e))}function en(e,t){let n,r;if(!e)return -2;e.msg="";let i=-16==t;return r=M_(e,i),e.l=r,r.o=e,r.ft=i?16191:16180,n=tr(e,t),n}function nr(e){let t=e.Zt?$n:Qn,n={jt:0};if(t.Nt){let r,i,f;for(r=0;r<144;)e.xt[r++]=8;for(;r<256;)e.xt[r++]=9;for(;r<280;)e.xt[r++]=7;for(;r<288;)e.xt[r++]=8;t.Ut.fill(0),f=t.Ut,t.Rt=f,i=9;let _={jt:f},l={jt:i},o={jt:0};for(Oe(1,e.xt,288,_,l,e.Tt,o,e.Zt),f=_.jt,i=l.jt,e.zt=o.jt,r=0;r<32;)e.xt[r++]=5;i=5;let u=o.jt,a={jt:f},c={jt:i};n.jt=u,Oe(2,e.xt,32,a,c,e.Tt,n,e.Zt),t.Yt=f.slice(u),t.Nt=false;}e.et=t.Rt,e.nt=9,e.tt=t.Yt,e.rt=5,e.zt=n.jt;}function rr(e,t,n){let r=e.l;if(!(r.u&&0!=r.u.length||(r.u=new x(1<<r.v),r.u)))return 1;if(0==r.h&&(r.h=1<<r.v,r.m=0,r.k=0),n>=r.h)M(r.u,0,t,t.length-r.h,r.h),r.m=0,r.k=r.h;else {let e=r.h-r.m;e>n&&(e=n),M(r.u,r.m,t,t.length-n,e),(n-=e)?(M(r.u,0,t,t.length-n,n),r.m=n,r.k=r.h):(r.m+=e,r.m==r.h&&(r.m=0),r.k<r.h&&(r.k+=e));}return 0}var S_=class extends L{constructor(){super("Need more input");}};function _n(e,t){let n,r,i,f,_,l,o,u,a,c,s,h,d,w,b,v,k,g=new x(4);if(Ge(e)||!e.next_out||!e.next_in&&0!=e.avail_in)return -2;l=0,u=0,o=0,a=0,r=Z,i=0,f=Z,_=0,n=e.l,16191==n.ft&&(n.ft=16192),z(),c=l,s=o,k=0;try{for(;;)switch(n.ft){case 16180:if(0==n.P){n.ft=16192;break}if(L(16),2&n.P&&35615==u){0==n.v&&(n.v=15),n.ct=W(0),n.ct=y(n.ct,u),q(),n.ft=16181;break}if(n.B&&(n.B.Et=-1),!(1&n.P)||(($(8)<<8)+(u>>8))%31){e.msg="incorrect header check",n.ft=16209;break}if(8!=$(4)){e.msg="unknown compression method",n.ft=16209;break}if(D(4),v=$(4)+8,0==n.v&&(n.v=v),v>15||v>n.v){e.msg="invalid window size",n.ft=16209;break}n.ut=1<<v,n.ot=0,e.i=n.ct=he(0),n.ft=512&u?16189:16191,q();break;case 16181:if(L(16),n.ot=u,8!=(255&n.ot)){e.msg="unknown compression method",n.ft=16209;break}if(57344&n.ot){e.msg="unknown header flags set",n.ft=16209;break}n.B&&(n.B.Fe=u>>8&1),512&n.ot&&4&n.P&&(n.ct=y(n.ct,u)),q(),n.ft=16182;case 16182:L(32),n.B&&(n.B.Xe=u),512&n.ot&&4&n.P&&(n.ct=p(n.ct,u)),q(),n.ft=16183;case 16183:L(16),n.B&&(n.B.Pt=255&u,n.B.Je=u>>8),512&n.ot&&4&n.P&&(n.ct=y(n.ct,u)),q(),n.ft=16184;case 16184:1024&n.ot?(L(16),n.ht=u,n.B&&(n.B.Ke=u),512&n.ot&&4&n.P&&(n.ct=y(n.ct,u)),q()):n.B&&(n.B.Ge=Z),n.ft=16185;case 16185:if(1024&n.ot&&(h=n.ht,h>l&&(h=l),h&&(n.B&&n.B.Ge&&n.B.Bt&&(v=n.B.Ke-n.ht)<n.B.Bt&&M(n.B.Ge,v,r,i,h),512&n.ot&&4&n.P&&(n.ct=W(n.ct,r.subarray(i,i+h),h)),l-=h,i+=h,n.ht-=h),n.ht))return m();n.ht=0,n.ft=16186;case 16186:if(2048&n.ot){if(0==l)return m();h=0;do{v=r[i+h++],n.B&&n.B.Ft&&n.ht<n.B.Ft&&(n.B.Oe[n.ht++]=v);}while(v&&h<l);if(512&n.ot&&4&n.P&&(n.ct=W(n.ct,r.subarray(i,i+h),h)),l-=h,i+=h,v)return m()}else n.B&&(n.B.Oe=Z);n.ht=0,n.ft=16187;case 16187:if(4096&n.ot){if(0==l)return m();h=0;do{v=r[i+h++],n.B&&n.B.Gt&&n.ht<n.B.Gt&&(n.B.Ve[n.ht++]=v);}while(v&&h<l);if(512&n.ot&&4&n.P&&(n.ct=W(n.ct,r.subarray(i,i+h),h)),l-=h,i+=h,v)return m()}else n.B&&(n.B.Ve=Z);n.ft=16188;case 16188:if(512&n.ot){if(L(16),4&n.P&&u!=(65535&n.ct)){e.msg="header crc mismatch",n.ft=16209;break}q();}n.B&&(n.B.Be=n.ot>>9&1,n.B.Et=1),e.i=n.ct=W(0),n.ft=16191;break;case 16189:L(32),e.i=n.ct=Y_(u),q(),n.ft=16190;case 16190:if(!n.lt)return C(),2;e.i=n.ct=he(0),n.ft=16191;case 16191:if(5==t||6==t)return m();case 16192:if(n._t){A(),n.ft=16206;break}switch(L(3),n._t=!!$(1),D(1),$(2)){case 0:n.ft=16193;break;case 1:if(nr(n),n.ft=16199,6==t)return D(2),m();break;case 2:n.ft=16196;break;case 3:e.msg="invalid block type",n.ft=16209;}D(2);break;case 16193:if(A(),L(32),(65535&u)!=(u>>>16^65535)){e.msg="invalid stored block lengths",n.ft=16209;break}if(n.ht=65535&u,q(),n.ft=16194,6==t)return m();case 16194:n.ft=16195;case 16195:if(h=n.ht,h){if(h>l&&(h=l),h>o&&(h=o),0==h)return m();M(f,_,r,i,h),l-=h,i+=h,o-=h,_+=h,n.ht-=h;break}n.ft=16191;break;case 16196:if(L(14),n.bt=$(5)+257,D(5),n.vt=$(5)+1,D(5),n.wt=$(4)+4,D(4),n.bt>286||!n.Zt&&n.vt>30){e.msg=n.Zt?"too many length":"too many length or distance symbols",n.ft=16209;break}n.kt=0,n.ft=16197;case 16197:for(;n.kt<n.wt;)L(3),n.xt[pe[n.kt++]]=$(3),D(3);for(;n.kt<19;)n.xt[pe[n.kt++]]=0;n.gt=n.yt,n.et=n.tt=n.gt,n.nt=7;let c={jt:n.gt},g={jt:n.nt},x={jt:0};if(k=Oe(0,n.xt,19,c,g,n.Tt,x,n.Zt),n.gt=c.jt,n.nt=g.jt,k){e.msg="invalid code lengths set",n.ft=16209;break}n.kt=0,n.ft=16198;case 16198:for(;n.kt<n.bt+n.vt;){for(;w=n.et[$(n.nt)],!((w>>>16&255)<=a);)S();if((65535&w)<16)D(w>>>16&255),n.xt[n.kt++]=65535&w;else {if(16==(65535&w)){if(L(2+(w>>>16&255)),D(w>>>16&255),0==n.kt){e.msg="invalid bit length repeat",n.ft=16209;break}v=n.xt[n.kt-1],h=3+$(2),D(2);}else 17==(65535&w)?(L(3+(w>>>16&255)),D(w>>>16&255),v=0,h=3+$(3),D(3)):(L(7+(w>>>16&255)),D(w>>>16&255),v=0,h=11+$(7),D(7));if(n.kt+h>n.bt+n.vt){e.msg="invalid bit length repeat",n.ft=16209;break}for(;h--;)n.xt[n.kt++]=v;}}if(16209==n.ft)break;if(0==n.xt[256]){e.msg="invalid code -- missing end-of-block",n.ft=16209;break}n.gt=n.yt,n.nt=9;let I={jt:n.gt},H={jt:n.nt},Q={jt:0};k=Oe(1,n.xt,n.bt,I,H,n.Tt,Q,n.Zt),n.gt=I.jt,n.nt=H.jt;let j=Q.jt;if(n.et=n.gt.slice(0,j),k){e.msg="invalid literal/lengths set",n.ft=16209;break}n.rt=6;let N=n.xt.subarray(n.bt,n.bt+n.vt),U={jt:n.gt},R={jt:n.rt},Y={jt:j};if(k=Oe(2,N,n.vt,U,R,n.Tt,Y,n.Zt),n.gt=U.jt,n.rt=R.jt,n.tt=n.gt.slice(j),k){e.msg="invalid distances set",n.ft=16209;break}if(n.ft=16199,6==t)return m();case 16199:n.ft=16200;case 16200:if(!n.Zt&&l>=6&&o>=258){C(),Jt(e,s),z(),16191==n.ft&&(n.Mt=-1);break}for(n.Mt=0;w=n.et[$(n.nt)],!((w>>>16&255)<=a);)S();if(w>>>24&&!(w>>>24&240)){for(b=w;w=n.et[(65535&b)+($((b>>>16&255)+(b>>>24))>>(b>>>16&255))],!((b>>>16&255)+(w>>>16&255)<=a);)S();D(b>>>16&255),n.Mt+=b>>>16&255;}if(D(w>>>16&255),n.Mt+=w>>>16&255,n.ht=65535&w,!(w>>>24)){n.ft=16205;break}if(w>>>24&32){n.Mt=-1,n.ft=16191;break}if(w>>>24&64){e.msg="invalid literal/length code",n.ft=16209;break}n.Ge=w>>>24&(n.Zt?31:15),n.ft=16201;case 16201:n.Ge&&(L(n.Ge),n.ht+=$(n.Ge),D(n.Ge),n.Mt+=n.Ge),n.Ct=n.ht,n.ft=16202;case 16202:for(;w=n.tt[$(n.rt)],!((w>>>16&255)<=a);)S();if(!(w>>>24&240)){for(b=w;w=n.tt[(65535&b)+($((b>>>16&255)+(b>>>24))>>(b>>>16&255))],!((b>>>16&255)+(w>>>16&255)<=a);)S();D(b>>>16&255),n.Mt+=b>>>16&255;}if(D(w>>>16&255),n.Mt+=w>>>16&255,w>>>24&64){e.msg="invalid distance code",n.ft=16209;break}n.dt=65535&w,n.Ge=w>>>24&15,n.ft=16203;case 16203:n.Ge&&(L(n.Ge),n.dt+=$(n.Ge),D(n.Ge),n.Mt+=n.Ge),n.ft=16204;case 16204:if(0==o)return m();if(h=s-o,n.dt>h){if(h=n.dt-h,h>n.k&&n.it){e.msg="invalid distance too far back",n.ft=16209;break}h>n.m?(h-=n.m,d=n.h-h):d=n.m-h,h>n.ht&&(h=n.ht),h>o&&(h=o);for(let e=0;e<h;++e)f[_]=255&n.u[d],++_,++d;}else {d=_-n.dt,h=n.ht,h>o&&(h=o);for(let e=0;e<h;++e)f[_]=f[d],++_,++d;}h>o&&(h=o),o-=h,n.ht-=h,0==n.ht&&(n.ft=16200);break;case 16205:if(0==o)return m();f[_++]=n.ht,o--,n.ft=16200;break;case 16206:if(n.P){if(L(32),s-=o,e.total_out+=s,n.st+=s,4&n.P&&s){let t=f.subarray(_-s,_);e.i=n.ct=T(n.ct,t,s);}if(s=o,4&n.P&&(n.ot?u:Y_(u)>>>0)!=n.ct){e.msg="incorrect data check",n.ft=16209;break}q();}n.ft=16207;case 16207:if(n.P&&n.ot){if(L(32),4&n.P&&u!=(4294967295&n.st)){e.msg="incorrect length check",n.ft=16209;break}q();}n.ft=16208;case 16208:return k=1,m();case 16209:return k=-3,m();case 16210:return -4;default:return -2}}catch(e){if(e instanceof S_)return m();throw e}function m(){if(C(),n.h||s!=e.avail_out&&n.ft<16209&&(n.Zt?n.ft<16208:n.ft<16206)||4!=t){let t=s-e.avail_out;if(rr(e,e.next_out.subarray(e.next_out_index-t,e.next_out_index),t))return n.ft=16210,-4}return c-=e.avail_in,s-=e.avail_out,e.total_in+=c,e.total_out+=s,n.st+=s,4&n.P&&s&&(e.i=n.ct=T(n.ct,e.next_out.subarray(e.next_out_index-s,e.next_out_index),s)),e.t=n.p+(n._t?64:0)+(16191==n.ft?128:0)+(16199==n.ft||16194==n.ft?256:0),(0==c&&0==s&&0==k||4==t&&0==k)&&(k=-5),k}function T(e,t,r){return n.ot?W(e,t,r):he(e,t,r)}function y(e,t){return g[0]=255&t,g[1]=t>>>8&255,W(e,g,2)>>>0}function p(e,t){return g[0]=255&t,g[1]=t>>>8&255,g[2]=t>>>16&255,g[3]=t>>>24&255,W(e,g,4)>>>0}function z(){f=e.next_out,_=e.next_out_index,o=e.avail_out,r=e.next_in,i=e.next_in_index,l=e.avail_in,u=n.T,a=n.p;}function C(){e.next_out=f,e.next_out_index=_,e.avail_out=o,e.next_in=r,e.next_in_index=i,e.avail_in=l,n.T=u,n.p=a;}function q(){u=0,a=0;}function S(){if(0==l)throw new S_;l--,u+=(255&r[i])<<a,i++,u>>>=0,a+=8;}function L(e){for(;a<e;)S();}function $(e){return u&(1<<e)-1}function D(e){u>>>=e,a-=e;}function A(){u>>>=7&a,a-=7&a;}}function tn(e){return Ge(e)?-2:0}var X_=65536,ar=32768,W_=class{constructor(e=16,t=X_){this.Ot=[],this.Vt=e;for(let n=0;n<z.min(e,4);n++)this.Ot.push(new x(t));}acquire(e=X_){for(let t=this.Ot.length-1;t>=0;t--){let n=this.Ot[t];if(n.length>=e)return this.Ot.splice(t,1),n}return new x(e)}release(e){this.Ot.length<this.Vt&&this.Ot.push(e);}};function nn(e){let t=new W_(32,X_),n=null;function r(e){try{t.release(e);}catch{}}return new H({start(){},transform(i,f){if(!n){let t=e.Xt(),r=e.Jt(t);if(0!=r&&0!=r)throw new L("init failed: "+r);n={o:t};}let _=n.o,l=0;for(;l<i.length;){let n=z.min(i.length-l,ar),o=i.subarray(l,l+n);for(_.next_in=o,_.next_in_index=0,_.avail_in=o.length;_.avail_in>0;){let n=t.acquire(),i=false;try{_.next_out=n,_.next_out_index=0,_.avail_out=n.length;let r=e.Kt(_,0),l=n.length-_.avail_out;if(l>0){let e=!1,r={en:n.subarray(0,l),release:()=>{e||(e=!0,t.release(n));}};i=!0,f.enqueue(r);}if(0!=r&&1!=r)throw new L("process error: "+r)}finally{i||r(n);}}l+=n;}},flush(i){if(!n)return;let f=n.o;for(;;){let n=t.acquire(),_=false;try{f.next_out=n,f.next_out_index=0,f.avail_out=n.length;let r=e.Kt(f,4),l=n.length-f.avail_out;if(l>0){let e=!1,r={en:n.subarray(0,l),release:()=>{e||(e=!0,t.release(n));}};_=!0,i.enqueue(r);}if(1==r)break;if(0!=r)throw new L("finalization error: "+r)}finally{_||r(n);}}let _=e.tn(f);if(0!=_&&0!=_)throw new L("end failed: "+_)}})}function rn(){return new H({start(){},transform(e,t){try{t.enqueue(e.en.slice(0));}finally{e.release();}},flush(){}})}function ir(e="deflate",t){let n="gzip"==e?31:"deflate-raw"==e?-15:15,r=t&&"number"==typeof t.level?t.level:-1;return nn({Xt:()=>vt(),Jt:e=>kt(e,r,8,n,8,0),Kt:Nt,tn:P_})}function or(e="deflate"){let t="gzip"==e?31:"deflate-raw"==e?-15:"deflate64-raw"==e?-16:15;return nn({Xt:()=>$t(),Jt:e=>en(e,t),Kt:_n,tn:tn})}var E_=class{constructor(e="deflate",t){let n=ir(e,t);this.writable=n.writable,this.readable=n.readable.pipeThrough(rn());}},g_=class{constructor(e="deflate"){let t=or(e);this.writable=t.writable,this.readable=t.readable.pipeThrough(rn());}};
|
|
|
|
/*
|
|
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.
|
|
*/
|
|
|
|
|
|
configure({
|
|
workerURI: "./core/web-worker-native.js",
|
|
wasmURI: null,
|
|
CompressionStreamZlib: E_,
|
|
DecompressionStreamZlib: g_
|
|
});
|
|
|
|
/*
|
|
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 ERR_ENTRY_EXISTS = "Entry filename already exists";
|
|
const ERR_READABLE_CONSUMED = "Readable stream already consumed";
|
|
|
|
class ZipEntry {
|
|
|
|
constructor(fs, name, params, parent) {
|
|
const zipEntry = this;
|
|
if (fs.root && parent && parent.getChildByName(name)) {
|
|
throw new Error(ERR_ENTRY_EXISTS);
|
|
}
|
|
if (!params) {
|
|
params = {};
|
|
}
|
|
Object.assign(zipEntry, {
|
|
fs,
|
|
name,
|
|
data: params.data,
|
|
options: params.options,
|
|
id: fs.entryIdCounter++,
|
|
parent,
|
|
children: [],
|
|
uncompressedSize: params.uncompressedSize || 0,
|
|
passThrough: params.passThrough
|
|
});
|
|
if (parent || !fs.root) {
|
|
fs.entries[zipEntry.id] = zipEntry;
|
|
}
|
|
if (parent) {
|
|
zipEntry.parent.children.push(zipEntry);
|
|
}
|
|
}
|
|
|
|
moveTo(target) {
|
|
// deprecated
|
|
const zipEntry = this;
|
|
zipEntry.fs.move(zipEntry, target);
|
|
}
|
|
|
|
getFullname() {
|
|
return this.getRelativeName();
|
|
}
|
|
|
|
getRelativeName(ancestor = this.fs.root) {
|
|
const zipEntry = this;
|
|
let relativeName = zipEntry.name;
|
|
let entry = zipEntry.parent;
|
|
while (entry && entry != ancestor) {
|
|
relativeName = (entry.name ? entry.name + "/" : "") + relativeName;
|
|
entry = entry.parent;
|
|
}
|
|
return relativeName;
|
|
}
|
|
|
|
isDescendantOf(ancestor) {
|
|
let entry = this.parent;
|
|
while (entry && entry.id != ancestor.id) {
|
|
entry = entry.parent;
|
|
}
|
|
return Boolean(entry);
|
|
}
|
|
|
|
rename(name) {
|
|
const parent = this.parent;
|
|
if (parent && parent.getChildByName(name)) {
|
|
throw new Error(ERR_ENTRY_EXISTS);
|
|
} else {
|
|
this.name = name;
|
|
}
|
|
}
|
|
}
|
|
|
|
class ZipFileEntry extends ZipEntry {
|
|
|
|
constructor(fs, name, params, parent) {
|
|
super(fs, name, params, parent);
|
|
const zipEntry = this;
|
|
zipEntry.Reader = params.Reader;
|
|
zipEntry.Writer = params.Writer;
|
|
if (params.getData) {
|
|
zipEntry.getData = params.getData;
|
|
}
|
|
}
|
|
|
|
clone() {
|
|
return new ZipFileEntry(this.fs, this.name, this);
|
|
}
|
|
|
|
async getData(writer, options = {}) {
|
|
const zipEntry = this;
|
|
if (!writer || (writer.constructor == zipEntry.Writer && zipEntry.data)) {
|
|
return zipEntry.data;
|
|
} else {
|
|
const reader = zipEntry.reader = createReader(zipEntry.Reader, zipEntry.data, options);
|
|
const uncompressedSize = zipEntry.data ? zipEntry.data.uncompressedSize : reader.size;
|
|
await Promise.all([initStream(reader), initStream(writer, uncompressedSize)]);
|
|
const { readable } = reader;
|
|
zipEntry.uncompressedSize = reader.size;
|
|
await readable.pipeTo(writer.writable);
|
|
return writer.getData ? writer.getData() : writer.writable;
|
|
}
|
|
}
|
|
|
|
isPasswordProtected() {
|
|
return Boolean(this.data && this.data.encrypted);
|
|
}
|
|
|
|
async checkPassword(password, options = {}) {
|
|
const zipEntry = this;
|
|
if (zipEntry.isPasswordProtected()) {
|
|
try {
|
|
await zipEntry.data.getData(null, Object.assign({}, options, {
|
|
password,
|
|
checkPasswordOnly: true
|
|
}));
|
|
return true;
|
|
} catch (error) {
|
|
if (error.message == ERR_INVALID_PASSWORD) {
|
|
return false;
|
|
} else {
|
|
throw error;
|
|
}
|
|
}
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
getText(encoding, options) {
|
|
return this.getData(new TextWriter(encoding), options);
|
|
}
|
|
|
|
getBlob(mimeType, options) {
|
|
return this.getData(new BlobWriter(mimeType), options);
|
|
}
|
|
|
|
getData64URI(mimeType, options) {
|
|
return this.getData(new Data64URIWriter(mimeType), options);
|
|
}
|
|
|
|
getUint8Array(options) {
|
|
return this.getData(new Uint8ArrayWriter(), options);
|
|
}
|
|
|
|
getWritable(writable = new WritableStream(), options) {
|
|
return this.getData({ writable }, options);
|
|
}
|
|
|
|
async getArrayBuffer(options) {
|
|
const array = await this.getUint8Array(options);
|
|
return array.byteOffset || array.byteLength != array.buffer.byteLength
|
|
? array.buffer.slice(array.byteOffset, array.byteOffset + array.byteLength)
|
|
: array.buffer;
|
|
}
|
|
|
|
replaceBlob(blob) {
|
|
Object.assign(this, {
|
|
data: blob,
|
|
Reader: BlobReader,
|
|
Writer: BlobWriter,
|
|
reader: null
|
|
});
|
|
}
|
|
|
|
replaceText(text) {
|
|
Object.assign(this, {
|
|
data: text,
|
|
Reader: TextReader,
|
|
Writer: TextWriter,
|
|
reader: null
|
|
});
|
|
}
|
|
|
|
replaceData64URI(dataURI) {
|
|
Object.assign(this, {
|
|
data: dataURI,
|
|
Reader: Data64URIReader,
|
|
Writer: Data64URIWriter,
|
|
reader: null
|
|
});
|
|
}
|
|
|
|
replaceUint8Array(array) {
|
|
Object.assign(this, {
|
|
data: array,
|
|
Reader: Uint8ArrayReader,
|
|
Writer: Uint8ArrayWriter,
|
|
reader: null
|
|
});
|
|
}
|
|
|
|
replaceReadable(readable) {
|
|
Object.assign(this, {
|
|
data: null,
|
|
Reader: getReadableReader(readable),
|
|
Writer: null,
|
|
reader: null
|
|
});
|
|
}
|
|
}
|
|
|
|
class ZipDirectoryEntry extends ZipEntry {
|
|
|
|
constructor(fs, name, params, parent) {
|
|
super(fs, name, params, parent);
|
|
this.directory = true;
|
|
}
|
|
|
|
clone(deepClone) {
|
|
const zipEntry = this;
|
|
const clonedEntry = new ZipDirectoryEntry(zipEntry.fs, zipEntry.name, zipEntry);
|
|
if (deepClone) {
|
|
clonedEntry.children = zipEntry.children.map(child => {
|
|
const childClone = child.clone(deepClone);
|
|
childClone.parent = clonedEntry;
|
|
return childClone;
|
|
});
|
|
}
|
|
return clonedEntry;
|
|
}
|
|
|
|
addDirectory(name, options) {
|
|
return addChild(this, name, { options }, true);
|
|
}
|
|
|
|
addText(name, text, options = {}) {
|
|
return addChild(this, name, {
|
|
data: text,
|
|
Reader: TextReader,
|
|
Writer: TextWriter,
|
|
options,
|
|
uncompressedSize: text.length
|
|
});
|
|
}
|
|
|
|
addBlob(name, blob, options = {}) {
|
|
return addChild(this, name, {
|
|
data: blob,
|
|
Reader: BlobReader,
|
|
Writer: BlobWriter,
|
|
options,
|
|
uncompressedSize: blob.size
|
|
});
|
|
}
|
|
|
|
addData64URI(name, dataURI, options = {}) {
|
|
let dataEnd = dataURI.length;
|
|
while (dataURI.charAt(dataEnd - 1) == "=") {
|
|
dataEnd--;
|
|
}
|
|
const dataStart = dataURI.indexOf(",") + 1;
|
|
return addChild(this, name, {
|
|
data: dataURI,
|
|
Reader: Data64URIReader,
|
|
Writer: Data64URIWriter,
|
|
options,
|
|
uncompressedSize: Math.floor((dataEnd - dataStart) * 0.75)
|
|
});
|
|
}
|
|
|
|
addUint8Array(name, array, options = {}) {
|
|
return addChild(this, name, {
|
|
data: array,
|
|
Reader: Uint8ArrayReader,
|
|
Writer: Uint8ArrayWriter,
|
|
options,
|
|
uncompressedSize: array.length
|
|
});
|
|
}
|
|
|
|
addHttpContent(name, url, options = {}) {
|
|
return addChild(this, name, {
|
|
data: url,
|
|
Reader: class extends HttpReader {
|
|
constructor(url) {
|
|
super(url, options);
|
|
}
|
|
},
|
|
options
|
|
});
|
|
}
|
|
|
|
addReadable(name, readable, options = {}) {
|
|
return addChild(this, name, {
|
|
Reader: getReadableReader(readable),
|
|
options
|
|
});
|
|
}
|
|
|
|
addFileSystemEntry(fileSystemEntry, options = {}) {
|
|
return addFileSystemHandle(this, fileSystemEntry, options);
|
|
}
|
|
|
|
addFileSystemHandle(handle, options = {}) {
|
|
return addFileSystemHandle(this, handle, options);
|
|
}
|
|
|
|
addFile(file, options = {}) {
|
|
options = Object.assign({}, options);
|
|
if (!options.lastModDate) {
|
|
options.lastModDate = new Date(file.lastModified);
|
|
}
|
|
return addChild(this, file.name, {
|
|
data: file,
|
|
Reader: function () {
|
|
const readable = file.stream();
|
|
const size = file.size;
|
|
return { readable, size };
|
|
},
|
|
options,
|
|
uncompressedSize: file.size
|
|
});
|
|
}
|
|
|
|
addData(name, params) {
|
|
return addChild(this, name, params);
|
|
}
|
|
|
|
importBlob(blob, options) {
|
|
return this.importZip(new BlobReader(blob), options);
|
|
}
|
|
|
|
importData64URI(dataURI, options) {
|
|
return this.importZip(new Data64URIReader(dataURI), options);
|
|
}
|
|
|
|
importUint8Array(array, options) {
|
|
return this.importZip(new Uint8ArrayReader(array), options);
|
|
}
|
|
|
|
importHttpContent(url, options) {
|
|
return this.importZip(new HttpReader(url, options), options);
|
|
}
|
|
|
|
importReadable(readable, options) {
|
|
return this.importZip({ readable }, options);
|
|
}
|
|
|
|
exportBlob(options = {}) {
|
|
return this.exportZip(new BlobWriter(options.mimeType || "application/zip"), options);
|
|
}
|
|
|
|
exportData64URI(options = {}) {
|
|
return this.exportZip(new Data64URIWriter(options.mimeType || "application/zip"), options);
|
|
}
|
|
|
|
exportUint8Array(options = {}) {
|
|
return this.exportZip(new Uint8ArrayWriter(), options);
|
|
}
|
|
|
|
async exportWritable(writable = new WritableStream(), options = {}) {
|
|
await this.exportZip({ writable }, options);
|
|
return writable;
|
|
}
|
|
|
|
exportFileSystemHandle(handle, options = {}) {
|
|
return exportFileSystemHandle(this, handle, options);
|
|
}
|
|
|
|
async importZip(reader, options = {}) {
|
|
await initStream(reader);
|
|
const zipReader = new ZipReader(reader, options);
|
|
const importedEntries = [];
|
|
const entries = await zipReader.getEntries();
|
|
for (const entry of entries) {
|
|
let parent = this;
|
|
try {
|
|
const path = entry.filename.split("/");
|
|
const name = path.pop();
|
|
path.forEach(pathPart => {
|
|
const previousParent = parent;
|
|
parent = parent.getChildByName(pathPart);
|
|
if (parent) {
|
|
if (!parent.directory) {
|
|
throw new Error(ERR_ENTRY_EXISTS);
|
|
}
|
|
} else {
|
|
parent = new ZipDirectoryEntry(this.fs, pathPart, { data: null }, previousParent);
|
|
importedEntries.push(parent);
|
|
}
|
|
});
|
|
if (!entry.directory) {
|
|
importedEntries.push(addChild(parent, name, {
|
|
data: entry,
|
|
Reader: getZipBlobReader(Object.assign({}, options)),
|
|
uncompressedSize: entry.uncompressedSize,
|
|
passThrough: options.passThrough
|
|
}));
|
|
} else {
|
|
let directoryEntry = parent;
|
|
if (name) {
|
|
directoryEntry = parent.getChildByName(name);
|
|
if (directoryEntry) {
|
|
if (!directoryEntry.directory) {
|
|
throw new Error(ERR_ENTRY_EXISTS);
|
|
}
|
|
} else {
|
|
directoryEntry = new ZipDirectoryEntry(this.fs, name, { data: null }, parent);
|
|
importedEntries.push(directoryEntry);
|
|
}
|
|
}
|
|
if (directoryEntry != this && !directoryEntry.data) {
|
|
directoryEntry.data = entry;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
try {
|
|
error.cause = {
|
|
entry
|
|
};
|
|
} catch {
|
|
// ignored
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
return importedEntries;
|
|
}
|
|
|
|
async exportZip(writer, options = {}) {
|
|
const zipEntry = this;
|
|
options = Object.assign({}, options);
|
|
if (options.bufferedWrite === UNDEFINED_VALUE) {
|
|
options.bufferedWrite = true;
|
|
}
|
|
const [readers] = await Promise.all([initReaders(zipEntry, options.readerOptions), initStream(writer)]);
|
|
const zipWriter = new ZipWriter(writer, options);
|
|
await exportZip(zipWriter, zipEntry, getTotalSize([zipEntry], "uncompressedSize"), options, readers);
|
|
await zipWriter.close();
|
|
return writer.getData ? writer.getData() : writer.writable;
|
|
}
|
|
|
|
getChildByName(name) {
|
|
const children = this.children;
|
|
for (let childIndex = 0; childIndex < children.length; childIndex++) {
|
|
const child = children[childIndex];
|
|
if (child.name == name) {
|
|
return child;
|
|
}
|
|
}
|
|
}
|
|
|
|
isPasswordProtected() {
|
|
const children = this.children;
|
|
for (let childIndex = 0; childIndex < children.length; childIndex++) {
|
|
const child = children[childIndex];
|
|
if (child.isPasswordProtected()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
async checkPassword(password, options = {}) {
|
|
const children = this.children;
|
|
const result = await Promise.all(children.map(child => child.checkPassword(password, options)));
|
|
return !result.includes(false);
|
|
}
|
|
}
|
|
|
|
|
|
class FS {
|
|
|
|
constructor() {
|
|
resetFS(this);
|
|
}
|
|
|
|
get children() {
|
|
return this.root.children;
|
|
}
|
|
|
|
remove(entry) {
|
|
detach(entry);
|
|
const removedEntries = [entry];
|
|
while (removedEntries.length) {
|
|
const removedEntry = removedEntries.pop();
|
|
this.entries[removedEntry.id] = null;
|
|
for (const child of removedEntry.children) {
|
|
removedEntries.push(child);
|
|
}
|
|
}
|
|
entry.parent = UNDEFINED_VALUE;
|
|
}
|
|
|
|
move(entry, destination) {
|
|
if (entry == this.root) {
|
|
throw new Error("Root directory cannot be moved");
|
|
} else {
|
|
if (destination.directory) {
|
|
if (!destination.isDescendantOf(entry)) {
|
|
if (entry != destination) {
|
|
const existingChild = destination.getChildByName(entry.name);
|
|
if (existingChild) {
|
|
if (existingChild != entry) {
|
|
throw new Error(ERR_ENTRY_EXISTS);
|
|
}
|
|
} else {
|
|
detach(entry);
|
|
entry.parent = destination;
|
|
destination.children.push(entry);
|
|
registerEntries(this, entry);
|
|
}
|
|
}
|
|
} else {
|
|
throw new Error("Entry is a ancestor of target entry");
|
|
}
|
|
} else {
|
|
throw new Error("Target entry is not a directory");
|
|
}
|
|
}
|
|
}
|
|
|
|
find(fullname) {
|
|
const path = fullname.split("/");
|
|
let node = this.root;
|
|
for (let index = 0; node && index < path.length; index++) {
|
|
node = node.getChildByName(path[index]);
|
|
}
|
|
if (!node) {
|
|
node = this.entries.find(entry => entry && (entry == this.root || entry.isDescendantOf(this.root)) &&
|
|
entry.getRelativeName() == fullname);
|
|
}
|
|
return node;
|
|
}
|
|
|
|
getById(id) {
|
|
return this.entries[id];
|
|
}
|
|
|
|
getChildByName(name) {
|
|
return this.root.getChildByName(name);
|
|
}
|
|
|
|
addDirectory(name, options) {
|
|
return this.root.addDirectory(name, options);
|
|
}
|
|
|
|
addText(name, text, options) {
|
|
return this.root.addText(name, text, options);
|
|
}
|
|
|
|
addBlob(name, blob, options) {
|
|
return this.root.addBlob(name, blob, options);
|
|
}
|
|
|
|
addData64URI(name, dataURI, options) {
|
|
return this.root.addData64URI(name, dataURI, options);
|
|
}
|
|
|
|
addUint8Array(name, array, options) {
|
|
return this.root.addUint8Array(name, array, options);
|
|
}
|
|
|
|
addHttpContent(name, url, options) {
|
|
return this.root.addHttpContent(name, url, options);
|
|
}
|
|
|
|
addReadable(name, readable, options) {
|
|
return this.root.addReadable(name, readable, options);
|
|
}
|
|
|
|
addFileSystemEntry(fileSystemEntry, options) {
|
|
return this.root.addFileSystemEntry(fileSystemEntry, options);
|
|
}
|
|
|
|
addFileSystemHandle(handle, options) {
|
|
return this.root.addFileSystemHandle(handle, options);
|
|
}
|
|
|
|
addFile(file, options) {
|
|
return this.root.addFile(file, options);
|
|
}
|
|
|
|
addData(name, params) {
|
|
return this.root.addData(name, params);
|
|
}
|
|
|
|
importBlob(blob, options) {
|
|
resetFS(this);
|
|
return this.root.importBlob(blob, options);
|
|
}
|
|
|
|
importData64URI(dataURI, options) {
|
|
resetFS(this);
|
|
return this.root.importData64URI(dataURI, options);
|
|
}
|
|
|
|
importUint8Array(array, options) {
|
|
resetFS(this);
|
|
return this.root.importUint8Array(array, options);
|
|
}
|
|
|
|
importHttpContent(url, options) {
|
|
resetFS(this);
|
|
return this.root.importHttpContent(url, options);
|
|
}
|
|
|
|
importReadable(readable, options) {
|
|
resetFS(this);
|
|
return this.root.importReadable(readable, options);
|
|
}
|
|
|
|
importZip(reader, options) {
|
|
resetFS(this);
|
|
return this.root.importZip(reader, options);
|
|
}
|
|
|
|
exportBlob(options) {
|
|
return this.root.exportBlob(options);
|
|
}
|
|
|
|
exportData64URI(options) {
|
|
return this.root.exportData64URI(options);
|
|
}
|
|
|
|
exportUint8Array(options) {
|
|
return this.root.exportUint8Array(options);
|
|
}
|
|
|
|
exportWritable(writable, options) {
|
|
return this.root.exportWritable(writable, options);
|
|
}
|
|
|
|
exportFileSystemHandle(handle, options) {
|
|
return this.root.exportFileSystemHandle(handle, options);
|
|
}
|
|
|
|
exportZip(writer, options) {
|
|
return this.root.exportZip(writer, options);
|
|
}
|
|
|
|
isPasswordProtected() {
|
|
return this.root.isPasswordProtected();
|
|
}
|
|
|
|
checkPassword(password, options) {
|
|
return this.root.checkPassword(password, options);
|
|
}
|
|
}
|
|
|
|
const fs = { FS, ZipDirectoryEntry, ZipFileEntry };
|
|
|
|
function getTotalSize(entries, propertyName) {
|
|
let size = 0;
|
|
const pendingEntries = Array.from(entries);
|
|
while (pendingEntries.length) {
|
|
const entry = pendingEntries.pop();
|
|
size += entry[propertyName] || 0;
|
|
for (const child of entry.children) {
|
|
pendingEntries.push(child);
|
|
}
|
|
}
|
|
return size;
|
|
}
|
|
|
|
function getReadableReader(readable) {
|
|
let consumed;
|
|
return function () {
|
|
if (consumed) {
|
|
throw new Error(ERR_READABLE_CONSUMED);
|
|
}
|
|
consumed = true;
|
|
return { readable };
|
|
};
|
|
}
|
|
|
|
function getZipBlobReader(options) {
|
|
return class extends Reader {
|
|
|
|
constructor(entry, options = {}) {
|
|
super();
|
|
this.entry = entry;
|
|
this.options = options;
|
|
}
|
|
|
|
async init() {
|
|
const zipBlobReader = this;
|
|
zipBlobReader.size = zipBlobReader.entry.uncompressedSize;
|
|
const data = await zipBlobReader.entry.getData(new BlobWriter(), Object.assign({}, options, zipBlobReader.options));
|
|
zipBlobReader.data = data;
|
|
zipBlobReader.blobReader = new BlobReader(data);
|
|
super.init();
|
|
}
|
|
|
|
readUint8Array(index, length) {
|
|
return this.blobReader.readUint8Array(index, length);
|
|
}
|
|
};
|
|
}
|
|
|
|
function createReader(Reader, data, options) {
|
|
return Reader.prototype ? new Reader(data, options) : Reader(data, options);
|
|
}
|
|
|
|
async function initReaders(entry, options) {
|
|
const fileEntries = [];
|
|
const pendingEntries = [entry];
|
|
const readers = new Map();
|
|
while (pendingEntries.length) {
|
|
const pendingEntry = pendingEntries.pop();
|
|
for (const child of pendingEntry.children) {
|
|
if (child.directory) {
|
|
pendingEntries.push(child);
|
|
} else {
|
|
fileEntries.push(child);
|
|
}
|
|
}
|
|
}
|
|
await Promise.all(fileEntries.map(async child => {
|
|
const reader = child.reader = createReader(child.Reader, child.data, options);
|
|
readers.set(child, reader);
|
|
try {
|
|
await initStream(reader);
|
|
} catch (error) {
|
|
try {
|
|
error.entryId = child.id;
|
|
error.cause = {
|
|
entry: child
|
|
};
|
|
} catch {
|
|
// ignored
|
|
}
|
|
throw error;
|
|
}
|
|
if (reader.size !== UNDEFINED_VALUE) {
|
|
child.uncompressedSize = reader.size;
|
|
}
|
|
}));
|
|
return readers;
|
|
}
|
|
|
|
function detach(entry) {
|
|
if (entry.parent) {
|
|
const children = entry.parent.children;
|
|
children.forEach((child, index) => {
|
|
if (child.id == entry.id) {
|
|
children.splice(index, 1);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
async function exportZip(zipWriter, entry, totalSize, options, readers) {
|
|
const selectedEntry = entry;
|
|
const entryOffsets = new Map();
|
|
await process(zipWriter, entry);
|
|
|
|
async function process(zipWriter, entry) {
|
|
await exportChild();
|
|
|
|
async function exportChild() {
|
|
if (options.bufferedWrite) {
|
|
const results = await Promise.allSettled(entry.children.map(processChild));
|
|
const errorResult = results.find(result => result.status == "rejected");
|
|
if (errorResult) {
|
|
throw errorResult.reason;
|
|
}
|
|
} else {
|
|
for (const child of entry.children) {
|
|
await processChild(child);
|
|
}
|
|
}
|
|
}
|
|
|
|
async function processChild(child) {
|
|
const name = options.relativePath ? child.getRelativeName(selectedEntry) : child.getFullname();
|
|
const childOptions = child.options || {};
|
|
let zipEntryOptions = {};
|
|
if (child.data instanceof Entry) {
|
|
const {
|
|
externalFileAttributes,
|
|
versionMadeBy,
|
|
comment,
|
|
lastModDate,
|
|
creationDate,
|
|
lastAccessDate,
|
|
uncompressedSize,
|
|
encrypted,
|
|
zipCrypto,
|
|
signature,
|
|
compressionMethod,
|
|
extraFieldAES
|
|
} = child.data;
|
|
zipEntryOptions = {
|
|
externalFileAttributes,
|
|
versionMadeBy,
|
|
comment,
|
|
lastModDate,
|
|
creationDate,
|
|
lastAccessDate
|
|
};
|
|
if (child.passThrough) {
|
|
let level, encryptionStrength;
|
|
if (compressionMethod === 0) {
|
|
level = 0;
|
|
}
|
|
if (extraFieldAES) {
|
|
encryptionStrength = extraFieldAES.strength;
|
|
}
|
|
zipEntryOptions = Object.assign(zipEntryOptions, {
|
|
passThrough: true,
|
|
encrypted,
|
|
zipCrypto,
|
|
signature,
|
|
uncompressedSize,
|
|
level,
|
|
encryptionStrength,
|
|
compressionMethod
|
|
});
|
|
}
|
|
}
|
|
await zipWriter.add(name, readers.get(child), Object.assign({}, options, zipEntryOptions, childOptions, {
|
|
directory: child.directory,
|
|
onprogress: async indexProgress => {
|
|
if (options.onprogress) {
|
|
entryOffsets.set(name, indexProgress);
|
|
try {
|
|
await options.onprogress(Array.from(entryOffsets.values()).reduce((previousValue, currentValue) => previousValue + currentValue), totalSize);
|
|
} catch {
|
|
// ignored
|
|
}
|
|
}
|
|
}
|
|
}));
|
|
await process(zipWriter, child);
|
|
}
|
|
}
|
|
}
|
|
|
|
function addFileSystemHandle(zipEntry, handle, options) {
|
|
return addFile(zipEntry, handle, []);
|
|
|
|
async function addFile(parentEntry, handle, addedEntries) {
|
|
if (handle) {
|
|
try {
|
|
if (handle.isFile || handle.isDirectory) {
|
|
handle = await transformToFileSystemhandle(handle);
|
|
}
|
|
if (handle.kind == "file") {
|
|
const file = await handle.getFile();
|
|
addedEntries.push(
|
|
parentEntry.addData(file.name, {
|
|
Reader: function () {
|
|
const readable = file.stream();
|
|
const size = file.size;
|
|
return { readable, size };
|
|
},
|
|
options: Object.assign({}, { lastModDate: new Date(file.lastModified) }, options),
|
|
uncompressedSize: file.size
|
|
})
|
|
);
|
|
} else if (handle.kind == "directory") {
|
|
const directoryEntry = parentEntry.addDirectory(handle.name);
|
|
addedEntries.push(directoryEntry);
|
|
for await (const childHandle of handle.values()) {
|
|
await addFile(directoryEntry, childHandle, addedEntries);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
const message = error.message + (handle ? " (" + handle.name + ")" : "");
|
|
throw new Error(message, { cause: error });
|
|
}
|
|
}
|
|
return addedEntries;
|
|
}
|
|
}
|
|
|
|
async function exportFileSystemHandle(zipEntry, directoryHandle, options) {
|
|
const totalSize = getTotalSize([zipEntry], "uncompressedSize");
|
|
const writtenSizes = new Map();
|
|
await exportChildren(zipEntry, directoryHandle);
|
|
return directoryHandle;
|
|
|
|
async function exportChildren(entry, parentHandle) {
|
|
if (options.concurrent) {
|
|
const results = await Promise.allSettled(entry.children.map(child => exportChild(child, parentHandle)));
|
|
const failedResult = results.find(result => result.status == "rejected");
|
|
if (failedResult) {
|
|
throw failedResult.reason;
|
|
}
|
|
} else {
|
|
for (const child of entry.children) {
|
|
await exportChild(child, parentHandle);
|
|
}
|
|
}
|
|
}
|
|
|
|
async function exportChild(child, parentHandle) {
|
|
try {
|
|
if (child.directory) {
|
|
const childDirectoryHandle = await parentHandle.getDirectoryHandle(child.name, { create: true });
|
|
await exportChildren(child, childDirectoryHandle);
|
|
} else {
|
|
const fileHandle = await parentHandle.getFileHandle(child.name, { create: true });
|
|
const writable = await fileHandle.createWritable();
|
|
await child.getData({ writable }, Object.assign({}, options, {
|
|
onprogress: async progress => {
|
|
if (options.onprogress) {
|
|
writtenSizes.set(child.id, progress);
|
|
try {
|
|
await options.onprogress(Array.from(writtenSizes.values()).reduce((previousValue, currentValue) => previousValue + currentValue, 0), totalSize);
|
|
} catch {
|
|
// ignored
|
|
}
|
|
}
|
|
}
|
|
}));
|
|
}
|
|
} catch (error) {
|
|
throw new Error(error.message + (child ? " (" + child.name + ")" : ""), { cause: error });
|
|
}
|
|
}
|
|
}
|
|
|
|
async function transformToFileSystemhandle(entry) {
|
|
const handle = {
|
|
name: entry.name
|
|
};
|
|
if (entry.isFile) {
|
|
handle.kind = "file";
|
|
handle.getFile = () =>
|
|
new Promise((resolve, reject) => entry.file(resolve, reject));
|
|
}
|
|
if (entry.isDirectory) {
|
|
handle.kind = "directory";
|
|
const handles = await transformToFileSystemhandles(entry);
|
|
handle.values = () => handles;
|
|
}
|
|
return handle;
|
|
}
|
|
|
|
async function transformToFileSystemhandles(entry) {
|
|
const entries = [];
|
|
function readEntries(directoryReader, resolve, reject) {
|
|
directoryReader.readEntries(async (entriesPart) => {
|
|
if (!entriesPart.length) {
|
|
resolve(entries);
|
|
} else {
|
|
for (const entry of entriesPart) {
|
|
entries.push(await transformToFileSystemhandle(entry));
|
|
}
|
|
readEntries(directoryReader, resolve, reject);
|
|
}
|
|
}, reject);
|
|
}
|
|
await new Promise((resolve, reject) =>
|
|
readEntries(entry.createReader(), resolve, reject)
|
|
);
|
|
return {
|
|
[Symbol.iterator]() {
|
|
let entryIndex = 0;
|
|
return {
|
|
next() {
|
|
const result = {
|
|
value: entries[entryIndex],
|
|
done: entryIndex == entries.length
|
|
};
|
|
entryIndex++;
|
|
return result;
|
|
}
|
|
};
|
|
}
|
|
};
|
|
}
|
|
|
|
function resetFS(fs) {
|
|
fs.entries = [];
|
|
fs.entryIdCounter = 0;
|
|
fs.root = new ZipDirectoryEntry(fs);
|
|
}
|
|
|
|
function registerEntries(fs, entry) {
|
|
const pendingEntries = [entry];
|
|
while (pendingEntries.length) {
|
|
const pendingEntry = pendingEntries.pop();
|
|
fs.entries[pendingEntry.id] = pendingEntry;
|
|
for (const child of pendingEntry.children) {
|
|
pendingEntries.push(child);
|
|
}
|
|
}
|
|
}
|
|
|
|
function addChild(parent, name, params, directory) {
|
|
if (parent.directory) {
|
|
return directory ? new ZipDirectoryEntry(parent.fs, name, params, parent) : new ZipFileEntry(parent.fs, name, params, parent);
|
|
} else {
|
|
throw new Error("Parent entry is not a directory");
|
|
}
|
|
}
|
|
|
|
/*
|
|
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 table = {
|
|
"application": {
|
|
"andrew-inset": "ez",
|
|
"annodex": "anx",
|
|
"atom+xml": "atom",
|
|
"atomcat+xml": "atomcat",
|
|
"atomserv+xml": "atomsrv",
|
|
"bbolin": "lin",
|
|
"cu-seeme": "cu",
|
|
"davmount+xml": "davmount",
|
|
"dsptype": "tsp",
|
|
"ecmascript": [
|
|
"es",
|
|
"ecma"
|
|
],
|
|
"futuresplash": "spl",
|
|
"hta": "hta",
|
|
"java-archive": "jar",
|
|
"java-serialized-object": "ser",
|
|
"java-vm": "class",
|
|
"m3g": "m3g",
|
|
"mac-binhex40": "hqx",
|
|
"mathematica": [
|
|
"nb",
|
|
"ma",
|
|
"mb"
|
|
],
|
|
"msaccess": "mdb",
|
|
"msword": [
|
|
"doc",
|
|
"dot",
|
|
"wiz"
|
|
],
|
|
"mxf": "mxf",
|
|
"oda": "oda",
|
|
"ogg": "ogx",
|
|
"pdf": "pdf",
|
|
"pgp-keys": "key",
|
|
"pgp-signature": [
|
|
"asc",
|
|
"sig"
|
|
],
|
|
"pics-rules": "prf",
|
|
"postscript": [
|
|
"ps",
|
|
"ai",
|
|
"eps",
|
|
"epsi",
|
|
"epsf",
|
|
"eps2",
|
|
"eps3"
|
|
],
|
|
"rar": "rar",
|
|
"rdf+xml": "rdf",
|
|
"rss+xml": "rss",
|
|
"rtf": "rtf",
|
|
"xhtml+xml": [
|
|
"xhtml",
|
|
"xht"
|
|
],
|
|
"xml": [
|
|
"xml",
|
|
"xsl",
|
|
"xsd",
|
|
"xpdl"
|
|
],
|
|
"xspf+xml": "xspf",
|
|
"zip": "zip",
|
|
"vnd.android.package-archive": "apk",
|
|
"vnd.cinderella": "cdy",
|
|
"vnd.google-earth.kml+xml": "kml",
|
|
"vnd.google-earth.kmz": "kmz",
|
|
"vnd.mozilla.xul+xml": "xul",
|
|
"vnd.ms-excel": [
|
|
"xls",
|
|
"xlb",
|
|
"xlt",
|
|
"xlm",
|
|
"xla",
|
|
"xlc",
|
|
"xlw"
|
|
],
|
|
"vnd.ms-pki.seccat": "cat",
|
|
"vnd.ms-pki.stl": "stl",
|
|
"vnd.ms-powerpoint": [
|
|
"ppt",
|
|
"pps",
|
|
"pot",
|
|
"ppa",
|
|
"pwz"
|
|
],
|
|
"vnd.oasis.opendocument.chart": "odc",
|
|
"vnd.oasis.opendocument.database": "odb",
|
|
"vnd.oasis.opendocument.formula": "odf",
|
|
"vnd.oasis.opendocument.graphics": "odg",
|
|
"vnd.oasis.opendocument.graphics-template": "otg",
|
|
"vnd.oasis.opendocument.image": "odi",
|
|
"vnd.oasis.opendocument.presentation": "odp",
|
|
"vnd.oasis.opendocument.presentation-template": "otp",
|
|
"vnd.oasis.opendocument.spreadsheet": "ods",
|
|
"vnd.oasis.opendocument.spreadsheet-template": "ots",
|
|
"vnd.oasis.opendocument.text": "odt",
|
|
"vnd.oasis.opendocument.text-master": [
|
|
"odm",
|
|
"otm"
|
|
],
|
|
"vnd.oasis.opendocument.text-template": "ott",
|
|
"vnd.oasis.opendocument.text-web": "oth",
|
|
"vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlsx",
|
|
"vnd.openxmlformats-officedocument.spreadsheetml.template": "xltx",
|
|
"vnd.openxmlformats-officedocument.presentationml.presentation": "pptx",
|
|
"vnd.openxmlformats-officedocument.presentationml.slideshow": "ppsx",
|
|
"vnd.openxmlformats-officedocument.presentationml.template": "potx",
|
|
"vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
|
|
"vnd.openxmlformats-officedocument.wordprocessingml.template": "dotx",
|
|
"vnd.smaf": "mmf",
|
|
"vnd.stardivision.calc": "sdc",
|
|
"vnd.stardivision.chart": "sds",
|
|
"vnd.stardivision.draw": "sda",
|
|
"vnd.stardivision.impress": "sdd",
|
|
"vnd.stardivision.math": [
|
|
"sdf",
|
|
"smf"
|
|
],
|
|
"vnd.stardivision.writer": [
|
|
"sdw",
|
|
"vor"
|
|
],
|
|
"vnd.stardivision.writer-global": "sgl",
|
|
"vnd.sun.xml.calc": "sxc",
|
|
"vnd.sun.xml.calc.template": "stc",
|
|
"vnd.sun.xml.draw": "sxd",
|
|
"vnd.sun.xml.draw.template": "std",
|
|
"vnd.sun.xml.impress": "sxi",
|
|
"vnd.sun.xml.impress.template": "sti",
|
|
"vnd.sun.xml.math": "sxm",
|
|
"vnd.sun.xml.writer": "sxw",
|
|
"vnd.sun.xml.writer.global": "sxg",
|
|
"vnd.sun.xml.writer.template": "stw",
|
|
"vnd.symbian.install": [
|
|
"sis",
|
|
"sisx"
|
|
],
|
|
"vnd.visio": [
|
|
"vsd",
|
|
"vst",
|
|
"vss",
|
|
"vsw",
|
|
"vsdx",
|
|
"vssx",
|
|
"vstx",
|
|
"vssm",
|
|
"vstm"
|
|
],
|
|
"vnd.wap.wbxml": "wbxml",
|
|
"vnd.wap.wmlc": "wmlc",
|
|
"vnd.wap.wmlscriptc": "wmlsc",
|
|
"vnd.wordperfect": "wpd",
|
|
"vnd.wordperfect5.1": "wp5",
|
|
"x-123": "wk",
|
|
"x-7z-compressed": "7z",
|
|
"x-abiword": "abw",
|
|
"x-apple-diskimage": "dmg",
|
|
"x-bcpio": "bcpio",
|
|
"x-bittorrent": "torrent",
|
|
"x-cbr": [
|
|
"cbr",
|
|
"cba",
|
|
"cbt",
|
|
"cb7"
|
|
],
|
|
"x-cbz": "cbz",
|
|
"x-cdf": [
|
|
"cdf",
|
|
"cda"
|
|
],
|
|
"x-cdlink": "vcd",
|
|
"x-chess-pgn": "pgn",
|
|
"x-cpio": "cpio",
|
|
"x-csh": "csh",
|
|
"x-director": [
|
|
"dir",
|
|
"dxr",
|
|
"cst",
|
|
"cct",
|
|
"cxt",
|
|
"w3d",
|
|
"fgd",
|
|
"swa"
|
|
],
|
|
"x-dms": "dms",
|
|
"x-doom": "wad",
|
|
"x-dvi": "dvi",
|
|
"x-httpd-eruby": "rhtml",
|
|
"x-freemind": "mm",
|
|
"x-gnumeric": "gnumeric",
|
|
"x-go-sgf": "sgf",
|
|
"x-graphing-calculator": "gcf",
|
|
"x-gtar": [
|
|
"gtar",
|
|
"taz"
|
|
],
|
|
"x-hdf": "hdf",
|
|
"x-httpd-php": [
|
|
"phtml",
|
|
"pht",
|
|
"php"
|
|
],
|
|
"x-httpd-php-source": "phps",
|
|
"x-httpd-php3": "php3",
|
|
"x-httpd-php3-preprocessed": "php3p",
|
|
"x-httpd-php4": "php4",
|
|
"x-httpd-php5": "php5",
|
|
"x-ica": "ica",
|
|
"x-info": "info",
|
|
"x-internet-signup": [
|
|
"ins",
|
|
"isp"
|
|
],
|
|
"x-iphone": "iii",
|
|
"x-iso9660-image": "iso",
|
|
"x-java-jnlp-file": "jnlp",
|
|
"x-jmol": "jmz",
|
|
"x-killustrator": "kil",
|
|
"x-latex": "latex",
|
|
"x-lyx": "lyx",
|
|
"x-lzx": "lzx",
|
|
"x-maker": [
|
|
"frm",
|
|
"fb",
|
|
"fbdoc"
|
|
],
|
|
"x-ms-wmd": "wmd",
|
|
"x-msdos-program": [
|
|
"com",
|
|
"exe",
|
|
"bat",
|
|
"dll"
|
|
],
|
|
"x-netcdf": [
|
|
"nc"
|
|
],
|
|
"x-ns-proxy-autoconfig": [
|
|
"pac",
|
|
"dat"
|
|
],
|
|
"x-nwc": "nwc",
|
|
"x-object": "o",
|
|
"x-oz-application": "oza",
|
|
"x-pkcs7-certreqresp": "p7r",
|
|
"x-python-code": [
|
|
"pyc",
|
|
"pyo"
|
|
],
|
|
"x-qgis": [
|
|
"qgs",
|
|
"shp",
|
|
"shx"
|
|
],
|
|
"x-quicktimeplayer": "qtl",
|
|
"x-redhat-package-manager": [
|
|
"rpm",
|
|
"rpa"
|
|
],
|
|
"x-ruby": "rb",
|
|
"x-sh": "sh",
|
|
"x-shar": "shar",
|
|
"x-shockwave-flash": [
|
|
"swf",
|
|
"swfl"
|
|
],
|
|
"x-silverlight": "scr",
|
|
"x-stuffit": "sit",
|
|
"x-sv4cpio": "sv4cpio",
|
|
"x-sv4crc": "sv4crc",
|
|
"x-tar": "tar",
|
|
"x-tex-gf": "gf",
|
|
"x-tex-pk": "pk",
|
|
"x-texinfo": [
|
|
"texinfo",
|
|
"texi"
|
|
],
|
|
"x-trash": [
|
|
"~",
|
|
"%",
|
|
"bak",
|
|
"old",
|
|
"sik"
|
|
],
|
|
"x-ustar": "ustar",
|
|
"x-wais-source": "src",
|
|
"x-wingz": "wz",
|
|
"x-x509-ca-cert": [
|
|
"crt",
|
|
"der",
|
|
"cer"
|
|
],
|
|
"x-xcf": "xcf",
|
|
"x-xfig": "fig",
|
|
"x-xpinstall": "xpi",
|
|
"applixware": "aw",
|
|
"atomsvc+xml": "atomsvc",
|
|
"ccxml+xml": "ccxml",
|
|
"cdmi-capability": "cdmia",
|
|
"cdmi-container": "cdmic",
|
|
"cdmi-domain": "cdmid",
|
|
"cdmi-object": "cdmio",
|
|
"cdmi-queue": "cdmiq",
|
|
"docbook+xml": "dbk",
|
|
"dssc+der": "dssc",
|
|
"dssc+xml": "xdssc",
|
|
"emma+xml": "emma",
|
|
"epub+zip": "epub",
|
|
"exi": "exi",
|
|
"font-tdpfr": "pfr",
|
|
"gml+xml": "gml",
|
|
"gpx+xml": "gpx",
|
|
"gxf": "gxf",
|
|
"hyperstudio": "stk",
|
|
"inkml+xml": [
|
|
"ink",
|
|
"inkml"
|
|
],
|
|
"ipfix": "ipfix",
|
|
"jsonml+json": "jsonml",
|
|
"lost+xml": "lostxml",
|
|
"mads+xml": "mads",
|
|
"marc": "mrc",
|
|
"marcxml+xml": "mrcx",
|
|
"mathml+xml": [
|
|
"mathml",
|
|
"mml"
|
|
],
|
|
"mbox": "mbox",
|
|
"mediaservercontrol+xml": "mscml",
|
|
"metalink+xml": "metalink",
|
|
"metalink4+xml": "meta4",
|
|
"mets+xml": "mets",
|
|
"mods+xml": "mods",
|
|
"mp21": [
|
|
"m21",
|
|
"mp21"
|
|
],
|
|
"mp4": "mp4s",
|
|
"oebps-package+xml": "opf",
|
|
"omdoc+xml": "omdoc",
|
|
"onenote": [
|
|
"onetoc",
|
|
"onetoc2",
|
|
"onetmp",
|
|
"onepkg"
|
|
],
|
|
"oxps": "oxps",
|
|
"patch-ops-error+xml": "xer",
|
|
"pgp-encrypted": "pgp",
|
|
"pkcs10": "p10",
|
|
"pkcs7-mime": [
|
|
"p7m",
|
|
"p7c"
|
|
],
|
|
"pkcs7-signature": "p7s",
|
|
"pkcs8": "p8",
|
|
"pkix-attr-cert": "ac",
|
|
"pkix-crl": "crl",
|
|
"pkix-pkipath": "pkipath",
|
|
"pkixcmp": "pki",
|
|
"pls+xml": "pls",
|
|
"prs.cww": "cww",
|
|
"pskc+xml": "pskcxml",
|
|
"reginfo+xml": "rif",
|
|
"relax-ng-compact-syntax": "rnc",
|
|
"resource-lists+xml": "rl",
|
|
"resource-lists-diff+xml": "rld",
|
|
"rls-services+xml": "rs",
|
|
"rpki-ghostbusters": "gbr",
|
|
"rpki-manifest": "mft",
|
|
"rpki-roa": "roa",
|
|
"rsd+xml": "rsd",
|
|
"sbml+xml": "sbml",
|
|
"scvp-cv-request": "scq",
|
|
"scvp-cv-response": "scs",
|
|
"scvp-vp-request": "spq",
|
|
"scvp-vp-response": "spp",
|
|
"sdp": "sdp",
|
|
"set-payment-initiation": "setpay",
|
|
"set-registration-initiation": "setreg",
|
|
"shf+xml": "shf",
|
|
"sparql-query": "rq",
|
|
"sparql-results+xml": "srx",
|
|
"srgs": "gram",
|
|
"srgs+xml": "grxml",
|
|
"sru+xml": "sru",
|
|
"ssdl+xml": "ssdl",
|
|
"ssml+xml": "ssml",
|
|
"tei+xml": [
|
|
"tei",
|
|
"teicorpus"
|
|
],
|
|
"thraud+xml": "tfi",
|
|
"timestamped-data": "tsd",
|
|
"vnd.3gpp.pic-bw-large": "plb",
|
|
"vnd.3gpp.pic-bw-small": "psb",
|
|
"vnd.3gpp.pic-bw-var": "pvb",
|
|
"vnd.3gpp2.tcap": "tcap",
|
|
"vnd.3m.post-it-notes": "pwn",
|
|
"vnd.accpac.simply.aso": "aso",
|
|
"vnd.accpac.simply.imp": "imp",
|
|
"vnd.acucobol": "acu",
|
|
"vnd.acucorp": [
|
|
"atc",
|
|
"acutc"
|
|
],
|
|
"vnd.adobe.air-application-installer-package+zip": "air",
|
|
"vnd.adobe.formscentral.fcdt": "fcdt",
|
|
"vnd.adobe.fxp": [
|
|
"fxp",
|
|
"fxpl"
|
|
],
|
|
"vnd.adobe.xdp+xml": "xdp",
|
|
"vnd.adobe.xfdf": "xfdf",
|
|
"vnd.ahead.space": "ahead",
|
|
"vnd.airzip.filesecure.azf": "azf",
|
|
"vnd.airzip.filesecure.azs": "azs",
|
|
"vnd.amazon.ebook": "azw",
|
|
"vnd.americandynamics.acc": "acc",
|
|
"vnd.amiga.ami": "ami",
|
|
"vnd.anser-web-certificate-issue-initiation": "cii",
|
|
"vnd.anser-web-funds-transfer-initiation": "fti",
|
|
"vnd.antix.game-component": "atx",
|
|
"vnd.apple.installer+xml": "mpkg",
|
|
"vnd.apple.mpegurl": "m3u8",
|
|
"vnd.aristanetworks.swi": "swi",
|
|
"vnd.astraea-software.iota": "iota",
|
|
"vnd.audiograph": "aep",
|
|
"vnd.blueice.multipass": "mpm",
|
|
"vnd.bmi": "bmi",
|
|
"vnd.businessobjects": "rep",
|
|
"vnd.chemdraw+xml": "cdxml",
|
|
"vnd.chipnuts.karaoke-mmd": "mmd",
|
|
"vnd.claymore": "cla",
|
|
"vnd.cloanto.rp9": "rp9",
|
|
"vnd.clonk.c4group": [
|
|
"c4g",
|
|
"c4d",
|
|
"c4f",
|
|
"c4p",
|
|
"c4u"
|
|
],
|
|
"vnd.cluetrust.cartomobile-config": "c11amc",
|
|
"vnd.cluetrust.cartomobile-config-pkg": "c11amz",
|
|
"vnd.commonspace": "csp",
|
|
"vnd.contact.cmsg": "cdbcmsg",
|
|
"vnd.cosmocaller": "cmc",
|
|
"vnd.crick.clicker": "clkx",
|
|
"vnd.crick.clicker.keyboard": "clkk",
|
|
"vnd.crick.clicker.palette": "clkp",
|
|
"vnd.crick.clicker.template": "clkt",
|
|
"vnd.crick.clicker.wordbank": "clkw",
|
|
"vnd.criticaltools.wbs+xml": "wbs",
|
|
"vnd.ctc-posml": "pml",
|
|
"vnd.cups-ppd": "ppd",
|
|
"vnd.curl.car": "car",
|
|
"vnd.curl.pcurl": "pcurl",
|
|
"vnd.dart": "dart",
|
|
"vnd.data-vision.rdz": "rdz",
|
|
"vnd.dece.data": [
|
|
"uvf",
|
|
"uvvf",
|
|
"uvd",
|
|
"uvvd"
|
|
],
|
|
"vnd.dece.ttml+xml": [
|
|
"uvt",
|
|
"uvvt"
|
|
],
|
|
"vnd.dece.unspecified": [
|
|
"uvx",
|
|
"uvvx"
|
|
],
|
|
"vnd.dece.zip": [
|
|
"uvz",
|
|
"uvvz"
|
|
],
|
|
"vnd.denovo.fcselayout-link": "fe_launch",
|
|
"vnd.dna": "dna",
|
|
"vnd.dolby.mlp": "mlp",
|
|
"vnd.dpgraph": "dpg",
|
|
"vnd.dreamfactory": "dfac",
|
|
"vnd.ds-keypoint": "kpxx",
|
|
"vnd.dvb.ait": "ait",
|
|
"vnd.dvb.service": "svc",
|
|
"vnd.dynageo": "geo",
|
|
"vnd.ecowin.chart": "mag",
|
|
"vnd.enliven": "nml",
|
|
"vnd.epson.esf": "esf",
|
|
"vnd.epson.msf": "msf",
|
|
"vnd.epson.quickanime": "qam",
|
|
"vnd.epson.salt": "slt",
|
|
"vnd.epson.ssf": "ssf",
|
|
"vnd.eszigno3+xml": [
|
|
"es3",
|
|
"et3"
|
|
],
|
|
"vnd.ezpix-album": "ez2",
|
|
"vnd.ezpix-package": "ez3",
|
|
"vnd.fdf": "fdf",
|
|
"vnd.fdsn.mseed": "mseed",
|
|
"vnd.fdsn.seed": [
|
|
"seed",
|
|
"dataless"
|
|
],
|
|
"vnd.flographit": "gph",
|
|
"vnd.fluxtime.clip": "ftc",
|
|
"vnd.framemaker": [
|
|
"fm",
|
|
"frame",
|
|
"maker",
|
|
"book"
|
|
],
|
|
"vnd.frogans.fnc": "fnc",
|
|
"vnd.frogans.ltf": "ltf",
|
|
"vnd.fsc.weblaunch": "fsc",
|
|
"vnd.fujitsu.oasys": "oas",
|
|
"vnd.fujitsu.oasys2": "oa2",
|
|
"vnd.fujitsu.oasys3": "oa3",
|
|
"vnd.fujitsu.oasysgp": "fg5",
|
|
"vnd.fujitsu.oasysprs": "bh2",
|
|
"vnd.fujixerox.ddd": "ddd",
|
|
"vnd.fujixerox.docuworks": "xdw",
|
|
"vnd.fujixerox.docuworks.binder": "xbd",
|
|
"vnd.fuzzysheet": "fzs",
|
|
"vnd.genomatix.tuxedo": "txd",
|
|
"vnd.geogebra.file": "ggb",
|
|
"vnd.geogebra.tool": "ggt",
|
|
"vnd.geometry-explorer": [
|
|
"gex",
|
|
"gre"
|
|
],
|
|
"vnd.geonext": "gxt",
|
|
"vnd.geoplan": "g2w",
|
|
"vnd.geospace": "g3w",
|
|
"vnd.gmx": "gmx",
|
|
"vnd.grafeq": [
|
|
"gqf",
|
|
"gqs"
|
|
],
|
|
"vnd.groove-account": "gac",
|
|
"vnd.groove-help": "ghf",
|
|
"vnd.groove-identity-message": "gim",
|
|
"vnd.groove-injector": "grv",
|
|
"vnd.groove-tool-message": "gtm",
|
|
"vnd.groove-tool-template": "tpl",
|
|
"vnd.groove-vcard": "vcg",
|
|
"vnd.hal+xml": "hal",
|
|
"vnd.handheld-entertainment+xml": "zmm",
|
|
"vnd.hbci": "hbci",
|
|
"vnd.hhe.lesson-player": "les",
|
|
"vnd.hp-hpgl": "hpgl",
|
|
"vnd.hp-hpid": "hpid",
|
|
"vnd.hp-hps": "hps",
|
|
"vnd.hp-jlyt": "jlt",
|
|
"vnd.hp-pcl": "pcl",
|
|
"vnd.hp-pclxl": "pclxl",
|
|
"vnd.hydrostatix.sof-data": "sfd-hdstx",
|
|
"vnd.ibm.minipay": "mpy",
|
|
"vnd.ibm.modcap": [
|
|
"afp",
|
|
"listafp",
|
|
"list3820"
|
|
],
|
|
"vnd.ibm.rights-management": "irm",
|
|
"vnd.ibm.secure-container": "sc",
|
|
"vnd.iccprofile": [
|
|
"icc",
|
|
"icm"
|
|
],
|
|
"vnd.igloader": "igl",
|
|
"vnd.immervision-ivp": "ivp",
|
|
"vnd.immervision-ivu": "ivu",
|
|
"vnd.insors.igm": "igm",
|
|
"vnd.intercon.formnet": [
|
|
"xpw",
|
|
"xpx"
|
|
],
|
|
"vnd.intergeo": "i2g",
|
|
"vnd.intu.qbo": "qbo",
|
|
"vnd.intu.qfx": "qfx",
|
|
"vnd.ipunplugged.rcprofile": "rcprofile",
|
|
"vnd.irepository.package+xml": "irp",
|
|
"vnd.is-xpr": "xpr",
|
|
"vnd.isac.fcs": "fcs",
|
|
"vnd.jam": "jam",
|
|
"vnd.jcp.javame.midlet-rms": "rms",
|
|
"vnd.jisp": "jisp",
|
|
"vnd.joost.joda-archive": "joda",
|
|
"vnd.kahootz": [
|
|
"ktz",
|
|
"ktr"
|
|
],
|
|
"vnd.kde.karbon": "karbon",
|
|
"vnd.kde.kchart": "chrt",
|
|
"vnd.kde.kformula": "kfo",
|
|
"vnd.kde.kivio": "flw",
|
|
"vnd.kde.kontour": "kon",
|
|
"vnd.kde.kpresenter": [
|
|
"kpr",
|
|
"kpt"
|
|
],
|
|
"vnd.kde.kspread": "ksp",
|
|
"vnd.kde.kword": [
|
|
"kwd",
|
|
"kwt"
|
|
],
|
|
"vnd.kenameaapp": "htke",
|
|
"vnd.kidspiration": "kia",
|
|
"vnd.kinar": [
|
|
"kne",
|
|
"knp"
|
|
],
|
|
"vnd.koan": [
|
|
"skp",
|
|
"skd",
|
|
"skt",
|
|
"skm"
|
|
],
|
|
"vnd.kodak-descriptor": "sse",
|
|
"vnd.las.las+xml": "lasxml",
|
|
"vnd.llamagraphics.life-balance.desktop": "lbd",
|
|
"vnd.llamagraphics.life-balance.exchange+xml": "lbe",
|
|
"vnd.lotus-1-2-3": "123",
|
|
"vnd.lotus-approach": "apr",
|
|
"vnd.lotus-freelance": "pre",
|
|
"vnd.lotus-notes": "nsf",
|
|
"vnd.lotus-organizer": "org",
|
|
"vnd.lotus-screencam": "scm",
|
|
"vnd.lotus-wordpro": "lwp",
|
|
"vnd.macports.portpkg": "portpkg",
|
|
"vnd.mcd": "mcd",
|
|
"vnd.medcalcdata": "mc1",
|
|
"vnd.mediastation.cdkey": "cdkey",
|
|
"vnd.mfer": "mwf",
|
|
"vnd.mfmp": "mfm",
|
|
"vnd.micrografx.flo": "flo",
|
|
"vnd.micrografx.igx": "igx",
|
|
"vnd.mif": "mif",
|
|
"vnd.mobius.daf": "daf",
|
|
"vnd.mobius.dis": "dis",
|
|
"vnd.mobius.mbk": "mbk",
|
|
"vnd.mobius.mqy": "mqy",
|
|
"vnd.mobius.msl": "msl",
|
|
"vnd.mobius.plc": "plc",
|
|
"vnd.mobius.txf": "txf",
|
|
"vnd.mophun.application": "mpn",
|
|
"vnd.mophun.certificate": "mpc",
|
|
"vnd.ms-artgalry": "cil",
|
|
"vnd.ms-cab-compressed": "cab",
|
|
"vnd.ms-excel.addin.macroenabled.12": "xlam",
|
|
"vnd.ms-excel.sheet.binary.macroenabled.12": "xlsb",
|
|
"vnd.ms-excel.sheet.macroenabled.12": "xlsm",
|
|
"vnd.ms-excel.template.macroenabled.12": "xltm",
|
|
"vnd.ms-fontobject": "eot",
|
|
"vnd.ms-htmlhelp": "chm",
|
|
"vnd.ms-ims": "ims",
|
|
"vnd.ms-lrm": "lrm",
|
|
"vnd.ms-officetheme": "thmx",
|
|
"vnd.ms-powerpoint.addin.macroenabled.12": "ppam",
|
|
"vnd.ms-powerpoint.presentation.macroenabled.12": "pptm",
|
|
"vnd.ms-powerpoint.slide.macroenabled.12": "sldm",
|
|
"vnd.ms-powerpoint.slideshow.macroenabled.12": "ppsm",
|
|
"vnd.ms-powerpoint.template.macroenabled.12": "potm",
|
|
"vnd.ms-project": [
|
|
"mpp",
|
|
"mpt"
|
|
],
|
|
"vnd.ms-word.document.macroenabled.12": "docm",
|
|
"vnd.ms-word.template.macroenabled.12": "dotm",
|
|
"vnd.ms-works": [
|
|
"wps",
|
|
"wks",
|
|
"wcm",
|
|
"wdb"
|
|
],
|
|
"vnd.ms-wpl": "wpl",
|
|
"vnd.ms-xpsdocument": "xps",
|
|
"vnd.mseq": "mseq",
|
|
"vnd.musician": "mus",
|
|
"vnd.muvee.style": "msty",
|
|
"vnd.mynfc": "taglet",
|
|
"vnd.neurolanguage.nlu": "nlu",
|
|
"vnd.nitf": [
|
|
"ntf",
|
|
"nitf"
|
|
],
|
|
"vnd.noblenet-directory": "nnd",
|
|
"vnd.noblenet-sealer": "nns",
|
|
"vnd.noblenet-web": "nnw",
|
|
"vnd.nokia.n-gage.data": "ngdat",
|
|
"vnd.nokia.n-gage.symbian.install": "n-gage",
|
|
"vnd.nokia.radio-preset": "rpst",
|
|
"vnd.nokia.radio-presets": "rpss",
|
|
"vnd.novadigm.edm": "edm",
|
|
"vnd.novadigm.edx": "edx",
|
|
"vnd.novadigm.ext": "ext",
|
|
"vnd.oasis.opendocument.chart-template": "otc",
|
|
"vnd.oasis.opendocument.formula-template": "odft",
|
|
"vnd.oasis.opendocument.image-template": "oti",
|
|
"vnd.olpc-sugar": "xo",
|
|
"vnd.oma.dd2+xml": "dd2",
|
|
"vnd.openofficeorg.extension": "oxt",
|
|
"vnd.openxmlformats-officedocument.presentationml.slide": "sldx",
|
|
"vnd.osgeo.mapguide.package": "mgp",
|
|
"vnd.osgi.dp": "dp",
|
|
"vnd.osgi.subsystem": "esa",
|
|
"vnd.palm": [
|
|
"pdb",
|
|
"pqa",
|
|
"oprc"
|
|
],
|
|
"vnd.pawaafile": "paw",
|
|
"vnd.pg.format": "str",
|
|
"vnd.pg.osasli": "ei6",
|
|
"vnd.picsel": "efif",
|
|
"vnd.pmi.widget": "wg",
|
|
"vnd.pocketlearn": "plf",
|
|
"vnd.powerbuilder6": "pbd",
|
|
"vnd.previewsystems.box": "box",
|
|
"vnd.proteus.magazine": "mgz",
|
|
"vnd.publishare-delta-tree": "qps",
|
|
"vnd.pvi.ptid1": "ptid",
|
|
"vnd.quark.quarkxpress": [
|
|
"qxd",
|
|
"qxt",
|
|
"qwd",
|
|
"qwt",
|
|
"qxl",
|
|
"qxb"
|
|
],
|
|
"vnd.realvnc.bed": "bed",
|
|
"vnd.recordare.musicxml": "mxl",
|
|
"vnd.recordare.musicxml+xml": "musicxml",
|
|
"vnd.rig.cryptonote": "cryptonote",
|
|
"vnd.rn-realmedia": "rm",
|
|
"vnd.rn-realmedia-vbr": "rmvb",
|
|
"vnd.route66.link66+xml": "link66",
|
|
"vnd.sailingtracker.track": "st",
|
|
"vnd.seemail": "see",
|
|
"vnd.sema": "sema",
|
|
"vnd.semd": "semd",
|
|
"vnd.semf": "semf",
|
|
"vnd.shana.informed.formdata": "ifm",
|
|
"vnd.shana.informed.formtemplate": "itp",
|
|
"vnd.shana.informed.interchange": "iif",
|
|
"vnd.shana.informed.package": "ipk",
|
|
"vnd.simtech-mindmapper": [
|
|
"twd",
|
|
"twds"
|
|
],
|
|
"vnd.smart.teacher": "teacher",
|
|
"vnd.solent.sdkm+xml": [
|
|
"sdkm",
|
|
"sdkd"
|
|
],
|
|
"vnd.spotfire.dxp": "dxp",
|
|
"vnd.spotfire.sfs": "sfs",
|
|
"vnd.stepmania.package": "smzip",
|
|
"vnd.stepmania.stepchart": "sm",
|
|
"vnd.sus-calendar": [
|
|
"sus",
|
|
"susp"
|
|
],
|
|
"vnd.svd": "svd",
|
|
"vnd.syncml+xml": "xsm",
|
|
"vnd.syncml.dm+wbxml": "bdm",
|
|
"vnd.syncml.dm+xml": "xdm",
|
|
"vnd.tao.intent-module-archive": "tao",
|
|
"vnd.tcpdump.pcap": [
|
|
"pcap",
|
|
"cap",
|
|
"dmp"
|
|
],
|
|
"vnd.tmobile-livetv": "tmo",
|
|
"vnd.trid.tpt": "tpt",
|
|
"vnd.triscape.mxs": "mxs",
|
|
"vnd.trueapp": "tra",
|
|
"vnd.ufdl": [
|
|
"ufd",
|
|
"ufdl"
|
|
],
|
|
"vnd.uiq.theme": "utz",
|
|
"vnd.umajin": "umj",
|
|
"vnd.unity": "unityweb",
|
|
"vnd.uoml+xml": "uoml",
|
|
"vnd.vcx": "vcx",
|
|
"vnd.visionary": "vis",
|
|
"vnd.vsf": "vsf",
|
|
"vnd.webturbo": "wtb",
|
|
"vnd.wolfram.player": "nbp",
|
|
"vnd.wqd": "wqd",
|
|
"vnd.wt.stf": "stf",
|
|
"vnd.xara": "xar",
|
|
"vnd.xfdl": "xfdl",
|
|
"vnd.yamaha.hv-dic": "hvd",
|
|
"vnd.yamaha.hv-script": "hvs",
|
|
"vnd.yamaha.hv-voice": "hvp",
|
|
"vnd.yamaha.openscoreformat": "osf",
|
|
"vnd.yamaha.openscoreformat.osfpvg+xml": "osfpvg",
|
|
"vnd.yamaha.smaf-audio": "saf",
|
|
"vnd.yamaha.smaf-phrase": "spf",
|
|
"vnd.yellowriver-custom-menu": "cmp",
|
|
"vnd.zul": [
|
|
"zir",
|
|
"zirz"
|
|
],
|
|
"vnd.zzazz.deck+xml": "zaz",
|
|
"voicexml+xml": "vxml",
|
|
"widget": "wgt",
|
|
"winhlp": "hlp",
|
|
"wsdl+xml": "wsdl",
|
|
"wspolicy+xml": "wspolicy",
|
|
"x-ace-compressed": "ace",
|
|
"x-authorware-bin": [
|
|
"aab",
|
|
"x32",
|
|
"u32",
|
|
"vox"
|
|
],
|
|
"x-authorware-map": "aam",
|
|
"x-authorware-seg": "aas",
|
|
"x-blorb": [
|
|
"blb",
|
|
"blorb"
|
|
],
|
|
"x-bzip": "bz",
|
|
"x-bzip2": [
|
|
"bz2",
|
|
"boz"
|
|
],
|
|
"x-cfs-compressed": "cfs",
|
|
"x-chat": "chat",
|
|
"x-conference": "nsc",
|
|
"x-dgc-compressed": "dgc",
|
|
"x-dtbncx+xml": "ncx",
|
|
"x-dtbook+xml": "dtb",
|
|
"x-dtbresource+xml": "res",
|
|
"x-eva": "eva",
|
|
"x-font-bdf": "bdf",
|
|
"x-font-ghostscript": "gsf",
|
|
"x-font-linux-psf": "psf",
|
|
"x-font-pcf": "pcf",
|
|
"x-font-snf": "snf",
|
|
"x-font-ttf": [
|
|
"ttf",
|
|
"ttc"
|
|
],
|
|
"x-font-type1": [
|
|
"pfa",
|
|
"pfb",
|
|
"pfm",
|
|
"afm"
|
|
],
|
|
"x-freearc": "arc",
|
|
"x-gca-compressed": "gca",
|
|
"x-glulx": "ulx",
|
|
"x-gramps-xml": "gramps",
|
|
"x-install-instructions": "install",
|
|
"x-lzh-compressed": [
|
|
"lzh",
|
|
"lha"
|
|
],
|
|
"x-mie": "mie",
|
|
"x-mobipocket-ebook": [
|
|
"prc",
|
|
"mobi"
|
|
],
|
|
"x-ms-application": "application",
|
|
"x-ms-shortcut": "lnk",
|
|
"x-ms-xbap": "xbap",
|
|
"x-msbinder": "obd",
|
|
"x-mscardfile": "crd",
|
|
"x-msclip": "clp",
|
|
"x-ms-installer": "msi",
|
|
"x-msmediaview": [
|
|
"mvb",
|
|
"m13",
|
|
"m14"
|
|
],
|
|
"x-msmetafile": [
|
|
"wmf",
|
|
"wmz",
|
|
"emf",
|
|
"emz"
|
|
],
|
|
"x-msmoney": "mny",
|
|
"x-mspublisher": "pub",
|
|
"x-msschedule": "scd",
|
|
"x-msterminal": "trm",
|
|
"x-mswrite": "wri",
|
|
"x-nzb": "nzb",
|
|
"x-pkcs12": [
|
|
"p12",
|
|
"pfx"
|
|
],
|
|
"x-pkcs7-certificates": [
|
|
"p7b",
|
|
"spc"
|
|
],
|
|
"x-research-info-systems": "ris",
|
|
"x-silverlight-app": "xap",
|
|
"x-sql": "sql",
|
|
"x-stuffitx": "sitx",
|
|
"x-subrip": "srt",
|
|
"x-t3vm-image": "t3",
|
|
"x-tex-tfm": "tfm",
|
|
"x-tgif": "obj",
|
|
"x-xliff+xml": "xlf",
|
|
"x-xz": "xz",
|
|
"x-zmachine": [
|
|
"z1",
|
|
"z2",
|
|
"z3",
|
|
"z4",
|
|
"z5",
|
|
"z6",
|
|
"z7",
|
|
"z8"
|
|
],
|
|
"xaml+xml": "xaml",
|
|
"xcap-diff+xml": "xdf",
|
|
"xenc+xml": "xenc",
|
|
"xml-dtd": "dtd",
|
|
"xop+xml": "xop",
|
|
"xproc+xml": "xpl",
|
|
"xslt+xml": "xslt",
|
|
"xv+xml": [
|
|
"mxml",
|
|
"xhvml",
|
|
"xvml",
|
|
"xvm"
|
|
],
|
|
"yang": "yang",
|
|
"yin+xml": "yin",
|
|
"envoy": "evy",
|
|
"fractals": "fif",
|
|
"internet-property-stream": "acx",
|
|
"olescript": "axs",
|
|
"vnd.ms-outlook": "msg",
|
|
"vnd.ms-pkicertstore": "sst",
|
|
"x-compress": "z",
|
|
"x-perfmon": [
|
|
"pma",
|
|
"pmc",
|
|
"pmr",
|
|
"pmw"
|
|
],
|
|
"ynd.ms-pkipko": "pko",
|
|
"gzip": [
|
|
"gz",
|
|
"tgz"
|
|
],
|
|
"smil+xml": [
|
|
"smi",
|
|
"smil"
|
|
],
|
|
"vnd.debian.binary-package": [
|
|
"deb",
|
|
"udeb"
|
|
],
|
|
"vnd.hzn-3d-crossword": "x3d",
|
|
"vnd.sqlite3": [
|
|
"db",
|
|
"sqlite",
|
|
"sqlite3",
|
|
"db-wal",
|
|
"sqlite-wal",
|
|
"db-shm",
|
|
"sqlite-shm"
|
|
],
|
|
"vnd.wap.sic": "sic",
|
|
"vnd.wap.slc": "slc",
|
|
"x-krita": [
|
|
"kra",
|
|
"krz"
|
|
],
|
|
"x-perl": [
|
|
"pm",
|
|
"pl"
|
|
],
|
|
"yaml": [
|
|
"yaml",
|
|
"yml"
|
|
]
|
|
},
|
|
"audio": {
|
|
"amr": "amr",
|
|
"amr-wb": "awb",
|
|
"annodex": "axa",
|
|
"basic": [
|
|
"au",
|
|
"snd"
|
|
],
|
|
"flac": "flac",
|
|
"midi": [
|
|
"mid",
|
|
"midi",
|
|
"kar",
|
|
"rmi"
|
|
],
|
|
"mpeg": [
|
|
"mpga",
|
|
"mpega",
|
|
"mp3",
|
|
"m4a",
|
|
"mp2a",
|
|
"m2a",
|
|
"m3a"
|
|
],
|
|
"mpegurl": "m3u",
|
|
"ogg": [
|
|
"oga",
|
|
"ogg",
|
|
"spx"
|
|
],
|
|
"prs.sid": "sid",
|
|
"x-aiff": "aifc",
|
|
"x-gsm": "gsm",
|
|
"x-ms-wma": "wma",
|
|
"x-ms-wax": "wax",
|
|
"x-pn-realaudio": "ram",
|
|
"x-realaudio": "ra",
|
|
"x-sd2": "sd2",
|
|
"adpcm": "adp",
|
|
"mp4": "mp4a",
|
|
"s3m": "s3m",
|
|
"silk": "sil",
|
|
"vnd.dece.audio": [
|
|
"uva",
|
|
"uvva"
|
|
],
|
|
"vnd.digital-winds": "eol",
|
|
"vnd.dra": "dra",
|
|
"vnd.dts": "dts",
|
|
"vnd.dts.hd": "dtshd",
|
|
"vnd.lucent.voice": "lvp",
|
|
"vnd.ms-playready.media.pya": "pya",
|
|
"vnd.nuera.ecelp4800": "ecelp4800",
|
|
"vnd.nuera.ecelp7470": "ecelp7470",
|
|
"vnd.nuera.ecelp9600": "ecelp9600",
|
|
"vnd.rip": "rip",
|
|
"webm": "weba",
|
|
"x-caf": "caf",
|
|
"x-matroska": "mka",
|
|
"x-pn-realaudio-plugin": "rmp",
|
|
"xm": "xm",
|
|
"aac": "aac",
|
|
"aiff": [
|
|
"aiff",
|
|
"aif",
|
|
"aff"
|
|
],
|
|
"opus": "opus",
|
|
"wav": "wav"
|
|
},
|
|
"chemical": {
|
|
"x-alchemy": "alc",
|
|
"x-cache": [
|
|
"cac",
|
|
"cache"
|
|
],
|
|
"x-cache-csf": "csf",
|
|
"x-cactvs-binary": [
|
|
"cbin",
|
|
"cascii",
|
|
"ctab"
|
|
],
|
|
"x-cdx": "cdx",
|
|
"x-chem3d": "c3d",
|
|
"x-cif": "cif",
|
|
"x-cmdf": "cmdf",
|
|
"x-cml": "cml",
|
|
"x-compass": "cpa",
|
|
"x-crossfire": "bsd",
|
|
"x-csml": [
|
|
"csml",
|
|
"csm"
|
|
],
|
|
"x-ctx": "ctx",
|
|
"x-cxf": [
|
|
"cxf",
|
|
"cef"
|
|
],
|
|
"x-embl-dl-nucleotide": [
|
|
"emb",
|
|
"embl"
|
|
],
|
|
"x-gamess-input": [
|
|
"inp",
|
|
"gam",
|
|
"gamin"
|
|
],
|
|
"x-gaussian-checkpoint": [
|
|
"fch",
|
|
"fchk"
|
|
],
|
|
"x-gaussian-cube": "cub",
|
|
"x-gaussian-input": [
|
|
"gau",
|
|
"gjc",
|
|
"gjf"
|
|
],
|
|
"x-gaussian-log": "gal",
|
|
"x-gcg8-sequence": "gcg",
|
|
"x-genbank": "gen",
|
|
"x-hin": "hin",
|
|
"x-isostar": [
|
|
"istr",
|
|
"ist"
|
|
],
|
|
"x-jcamp-dx": [
|
|
"jdx",
|
|
"dx"
|
|
],
|
|
"x-kinemage": "kin",
|
|
"x-macmolecule": "mcm",
|
|
"x-macromodel-input": "mmod",
|
|
"x-mdl-molfile": "mol",
|
|
"x-mdl-rdfile": "rd",
|
|
"x-mdl-rxnfile": "rxn",
|
|
"x-mdl-sdfile": "sd",
|
|
"x-mdl-tgf": "tgf",
|
|
"x-mmcif": "mcif",
|
|
"x-mol2": "mol2",
|
|
"x-molconn-Z": "b",
|
|
"x-mopac-graph": "gpt",
|
|
"x-mopac-input": [
|
|
"mop",
|
|
"mopcrt",
|
|
"zmt"
|
|
],
|
|
"x-mopac-out": "moo",
|
|
"x-ncbi-asn1": "asn",
|
|
"x-ncbi-asn1-ascii": [
|
|
"prt",
|
|
"ent"
|
|
],
|
|
"x-ncbi-asn1-binary": "val",
|
|
"x-rosdal": "ros",
|
|
"x-swissprot": "sw",
|
|
"x-vamas-iso14976": "vms",
|
|
"x-vmd": "vmd",
|
|
"x-xtel": "xtel",
|
|
"x-xyz": "xyz"
|
|
},
|
|
"font": {
|
|
"otf": "otf",
|
|
"woff": "woff",
|
|
"woff2": "woff2"
|
|
},
|
|
"image": {
|
|
"gif": "gif",
|
|
"ief": "ief",
|
|
"jpeg": [
|
|
"jpeg",
|
|
"jpg",
|
|
"jpe",
|
|
"jfif",
|
|
"jfif-tbnl",
|
|
"jif"
|
|
],
|
|
"pcx": "pcx",
|
|
"png": "png",
|
|
"svg+xml": [
|
|
"svg",
|
|
"svgz"
|
|
],
|
|
"tiff": [
|
|
"tiff",
|
|
"tif"
|
|
],
|
|
"vnd.djvu": [
|
|
"djvu",
|
|
"djv"
|
|
],
|
|
"vnd.wap.wbmp": "wbmp",
|
|
"x-canon-cr2": "cr2",
|
|
"x-canon-crw": "crw",
|
|
"x-cmu-raster": "ras",
|
|
"x-coreldraw": "cdr",
|
|
"x-coreldrawpattern": "pat",
|
|
"x-coreldrawtemplate": "cdt",
|
|
"x-corelphotopaint": "cpt",
|
|
"x-epson-erf": "erf",
|
|
"x-icon": "ico",
|
|
"x-jg": "art",
|
|
"x-jng": "jng",
|
|
"x-nikon-nef": "nef",
|
|
"x-olympus-orf": "orf",
|
|
"x-portable-anymap": "pnm",
|
|
"x-portable-bitmap": "pbm",
|
|
"x-portable-graymap": "pgm",
|
|
"x-portable-pixmap": "ppm",
|
|
"x-rgb": "rgb",
|
|
"x-xbitmap": "xbm",
|
|
"x-xpixmap": "xpm",
|
|
"x-xwindowdump": "xwd",
|
|
"bmp": "bmp",
|
|
"cgm": "cgm",
|
|
"g3fax": "g3",
|
|
"ktx": "ktx",
|
|
"prs.btif": "btif",
|
|
"sgi": "sgi",
|
|
"vnd.dece.graphic": [
|
|
"uvi",
|
|
"uvvi",
|
|
"uvg",
|
|
"uvvg"
|
|
],
|
|
"vnd.dwg": "dwg",
|
|
"vnd.dxf": "dxf",
|
|
"vnd.fastbidsheet": "fbs",
|
|
"vnd.fpx": "fpx",
|
|
"vnd.fst": "fst",
|
|
"vnd.fujixerox.edmics-mmr": "mmr",
|
|
"vnd.fujixerox.edmics-rlc": "rlc",
|
|
"vnd.ms-modi": "mdi",
|
|
"vnd.ms-photo": "wdp",
|
|
"vnd.net-fpx": "npx",
|
|
"vnd.xiff": "xif",
|
|
"webp": "webp",
|
|
"x-3ds": "3ds",
|
|
"x-cmx": "cmx",
|
|
"x-freehand": [
|
|
"fh",
|
|
"fhc",
|
|
"fh4",
|
|
"fh5",
|
|
"fh7"
|
|
],
|
|
"x-pict": [
|
|
"pic",
|
|
"pct"
|
|
],
|
|
"x-tga": "tga",
|
|
"cis-cod": "cod",
|
|
"avif": [
|
|
"avif",
|
|
"avifs"
|
|
],
|
|
"heic": [
|
|
"heif",
|
|
"heic"
|
|
],
|
|
"pjpeg": [
|
|
"pjpg"
|
|
],
|
|
"vnd.adobe.photoshop": "psd",
|
|
"x-adobe-dng": "dng",
|
|
"x-fuji-raf": "raf",
|
|
"x-icns": "icns",
|
|
"x-kodak-dcr": "dcr",
|
|
"x-kodak-k25": "k25",
|
|
"x-kodak-kdc": "kdc",
|
|
"x-minolta-mrw": "mrw",
|
|
"x-panasonic-raw": [
|
|
"raw",
|
|
"rw2",
|
|
"rwl"
|
|
],
|
|
"x-pentax-pef": [
|
|
"pef",
|
|
"ptx"
|
|
],
|
|
"x-sigma-x3f": "x3f",
|
|
"x-sony-arw": "arw",
|
|
"x-sony-sr2": "sr2",
|
|
"x-sony-srf": "srf"
|
|
},
|
|
"message": {
|
|
"rfc822": [
|
|
"eml",
|
|
"mime",
|
|
"mht",
|
|
"mhtml",
|
|
"nws"
|
|
]
|
|
},
|
|
"model": {
|
|
"iges": [
|
|
"igs",
|
|
"iges"
|
|
],
|
|
"mesh": [
|
|
"msh",
|
|
"mesh",
|
|
"silo"
|
|
],
|
|
"vrml": [
|
|
"wrl",
|
|
"vrml"
|
|
],
|
|
"x3d+vrml": [
|
|
"x3dv",
|
|
"x3dvz"
|
|
],
|
|
"x3d+xml": "x3dz",
|
|
"x3d+binary": [
|
|
"x3db",
|
|
"x3dbz"
|
|
],
|
|
"vnd.collada+xml": "dae",
|
|
"vnd.dwf": "dwf",
|
|
"vnd.gdl": "gdl",
|
|
"vnd.gtw": "gtw",
|
|
"vnd.mts": "mts",
|
|
"vnd.usdz+zip": "usdz",
|
|
"vnd.vtu": "vtu"
|
|
},
|
|
"text": {
|
|
"cache-manifest": [
|
|
"manifest",
|
|
"appcache"
|
|
],
|
|
"calendar": [
|
|
"ics",
|
|
"icz",
|
|
"ifb"
|
|
],
|
|
"css": "css",
|
|
"csv": "csv",
|
|
"h323": "323",
|
|
"html": [
|
|
"html",
|
|
"htm",
|
|
"shtml",
|
|
"stm"
|
|
],
|
|
"iuls": "uls",
|
|
"plain": [
|
|
"txt",
|
|
"text",
|
|
"brf",
|
|
"conf",
|
|
"def",
|
|
"list",
|
|
"log",
|
|
"in",
|
|
"bas",
|
|
"diff",
|
|
"ksh"
|
|
],
|
|
"richtext": "rtx",
|
|
"scriptlet": [
|
|
"sct",
|
|
"wsc"
|
|
],
|
|
"texmacs": "tm",
|
|
"tab-separated-values": "tsv",
|
|
"vnd.sun.j2me.app-descriptor": "jad",
|
|
"vnd.wap.wml": "wml",
|
|
"vnd.wap.wmlscript": "wmls",
|
|
"x-bibtex": "bib",
|
|
"x-boo": "boo",
|
|
"x-c++hdr": [
|
|
"h++",
|
|
"hpp",
|
|
"hxx",
|
|
"hh"
|
|
],
|
|
"x-c++src": [
|
|
"c++",
|
|
"cpp",
|
|
"cxx",
|
|
"cc"
|
|
],
|
|
"x-component": "htc",
|
|
"x-dsrc": "d",
|
|
"x-diff": "patch",
|
|
"x-haskell": "hs",
|
|
"x-java": "java",
|
|
"x-literate-haskell": "lhs",
|
|
"x-moc": "moc",
|
|
"x-pascal": [
|
|
"p",
|
|
"pas",
|
|
"pp",
|
|
"inc"
|
|
],
|
|
"x-pcs-gcd": "gcd",
|
|
"x-python": "py",
|
|
"x-scala": "scala",
|
|
"x-setext": "etx",
|
|
"x-tcl": [
|
|
"tcl",
|
|
"tk"
|
|
],
|
|
"x-tex": [
|
|
"tex",
|
|
"ltx",
|
|
"sty",
|
|
"cls"
|
|
],
|
|
"x-vcalendar": "vcs",
|
|
"x-vcard": "vcf",
|
|
"n3": "n3",
|
|
"prs.lines.tag": "dsc",
|
|
"sgml": [
|
|
"sgml",
|
|
"sgm"
|
|
],
|
|
"troff": [
|
|
"t",
|
|
"tr",
|
|
"roff",
|
|
"man",
|
|
"me",
|
|
"ms"
|
|
],
|
|
"turtle": "ttl",
|
|
"uri-list": [
|
|
"uri",
|
|
"uris",
|
|
"urls"
|
|
],
|
|
"vcard": "vcard",
|
|
"vnd.curl": "curl",
|
|
"vnd.curl.dcurl": "dcurl",
|
|
"vnd.curl.scurl": "scurl",
|
|
"vnd.curl.mcurl": "mcurl",
|
|
"vnd.dvb.subtitle": "sub",
|
|
"vnd.fly": "fly",
|
|
"vnd.fmi.flexstor": "flx",
|
|
"vnd.graphviz": "gv",
|
|
"vnd.in3d.3dml": "3dml",
|
|
"vnd.in3d.spot": "spot",
|
|
"x-asm": [
|
|
"s",
|
|
"asm"
|
|
],
|
|
"x-c": [
|
|
"c",
|
|
"h",
|
|
"dic"
|
|
],
|
|
"x-fortran": [
|
|
"f",
|
|
"for",
|
|
"f77",
|
|
"f90"
|
|
],
|
|
"x-opml": "opml",
|
|
"x-nfo": "nfo",
|
|
"x-sfv": "sfv",
|
|
"x-uuencode": "uu",
|
|
"webviewhtml": "htt",
|
|
"javascript": "js",
|
|
"json": "json",
|
|
"markdown": [
|
|
"md",
|
|
"markdown",
|
|
"mdown",
|
|
"markdn"
|
|
],
|
|
"vnd.wap.si": "si",
|
|
"vnd.wap.sl": "sl"
|
|
},
|
|
"video": {
|
|
"3gpp": "3gp",
|
|
"annodex": "axv",
|
|
"dl": "dl",
|
|
"dv": [
|
|
"dif",
|
|
"dv"
|
|
],
|
|
"fli": "fli",
|
|
"gl": "gl",
|
|
"mpeg": [
|
|
"mpeg",
|
|
"mpg",
|
|
"mpe",
|
|
"m1v",
|
|
"m2v",
|
|
"mp2",
|
|
"mpa",
|
|
"mpv2"
|
|
],
|
|
"mp4": [
|
|
"mp4",
|
|
"mp4v",
|
|
"mpg4"
|
|
],
|
|
"quicktime": [
|
|
"qt",
|
|
"mov"
|
|
],
|
|
"ogg": "ogv",
|
|
"vnd.mpegurl": [
|
|
"mxu",
|
|
"m4u"
|
|
],
|
|
"x-flv": "flv",
|
|
"x-la-asf": [
|
|
"lsf",
|
|
"lsx"
|
|
],
|
|
"x-mng": "mng",
|
|
"x-ms-asf": [
|
|
"asf",
|
|
"asx",
|
|
"asr"
|
|
],
|
|
"x-ms-wm": "wm",
|
|
"x-ms-wmv": "wmv",
|
|
"x-ms-wmx": "wmx",
|
|
"x-ms-wvx": "wvx",
|
|
"x-msvideo": "avi",
|
|
"x-sgi-movie": "movie",
|
|
"x-matroska": [
|
|
"mpv",
|
|
"mkv",
|
|
"mk3d",
|
|
"mks"
|
|
],
|
|
"3gpp2": "3g2",
|
|
"h261": "h261",
|
|
"h263": "h263",
|
|
"h264": "h264",
|
|
"jpeg": "jpgv",
|
|
"jpm": [
|
|
"jpm",
|
|
"jpgm"
|
|
],
|
|
"mj2": [
|
|
"mj2",
|
|
"mjp2"
|
|
],
|
|
"vnd.dece.hd": [
|
|
"uvh",
|
|
"uvvh"
|
|
],
|
|
"vnd.dece.mobile": [
|
|
"uvm",
|
|
"uvvm"
|
|
],
|
|
"vnd.dece.pd": [
|
|
"uvp",
|
|
"uvvp"
|
|
],
|
|
"vnd.dece.sd": [
|
|
"uvs",
|
|
"uvvs"
|
|
],
|
|
"vnd.dece.video": [
|
|
"uvv",
|
|
"uvvv"
|
|
],
|
|
"vnd.dvb.file": "dvb",
|
|
"vnd.fvt": "fvt",
|
|
"vnd.ms-playready.media.pyv": "pyv",
|
|
"vnd.uvvu.mp4": [
|
|
"uvu",
|
|
"uvvu"
|
|
],
|
|
"vnd.vivo": "viv",
|
|
"webm": "webm",
|
|
"x-f4v": "f4v",
|
|
"x-m4v": "m4v",
|
|
"x-ms-vob": "vob",
|
|
"x-smv": "smv",
|
|
"mp2t": "ts"
|
|
},
|
|
"x-conference": {
|
|
"x-cooltalk": "ice"
|
|
},
|
|
"x-world": {
|
|
"x-vrml": [
|
|
"vrm",
|
|
"flr",
|
|
"wrz",
|
|
"xaf",
|
|
"xof"
|
|
]
|
|
}
|
|
};
|
|
|
|
let mimeTypes;
|
|
|
|
function getMimeType(filename) {
|
|
return filename && getMimeTypes()[filename.split(".").pop().toLowerCase()] || getMimeType$1();
|
|
}
|
|
|
|
function getMimeTypes() {
|
|
if (!mimeTypes) {
|
|
mimeTypes = {};
|
|
for (const type of Object.keys(table)) {
|
|
for (const subtype of Object.keys(table[type])) {
|
|
const value = table[type][subtype];
|
|
if (typeof value == "string") {
|
|
mimeTypes[value] = type + "/" + subtype;
|
|
} else {
|
|
for (let indexMimeType = 0; indexMimeType < value.length; indexMimeType++) {
|
|
mimeTypes[value[indexMimeType]] = type + "/" + subtype;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return mimeTypes;
|
|
}
|
|
|
|
/*
|
|
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.
|
|
*/
|
|
|
|
|
|
i(configure);
|
|
|
|
exports.BlobReader = BlobReader;
|
|
exports.BlobWriter = BlobWriter;
|
|
exports.Data64URIReader = Data64URIReader;
|
|
exports.Data64URIWriter = Data64URIWriter;
|
|
exports.ERR_AMBIGUOUS_ARCHIVE = ERR_AMBIGUOUS_ARCHIVE;
|
|
exports.ERR_BAD_FORMAT = ERR_BAD_FORMAT;
|
|
exports.ERR_CENTRAL_DIRECTORY_NOT_FOUND = ERR_CENTRAL_DIRECTORY_NOT_FOUND;
|
|
exports.ERR_DUPLICATED_NAME = ERR_DUPLICATED_NAME;
|
|
exports.ERR_ENCRYPTED = ERR_ENCRYPTED;
|
|
exports.ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND = ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND;
|
|
exports.ERR_EOCDR_NOT_FOUND = ERR_EOCDR_NOT_FOUND;
|
|
exports.ERR_EXTRAFIELD_ZIP64_NOT_FOUND = ERR_EXTRAFIELD_ZIP64_NOT_FOUND;
|
|
exports.ERR_HTTP_RANGE = ERR_HTTP_RANGE;
|
|
exports.ERR_INVALID_COMMENT = ERR_INVALID_COMMENT;
|
|
exports.ERR_INVALID_COMPRESSED_DATA = ERR_INVALID_COMPRESSED_DATA;
|
|
exports.ERR_INVALID_ENCRYPTION_STRENGTH = ERR_INVALID_ENCRYPTION_STRENGTH;
|
|
exports.ERR_INVALID_ENTRY_COMMENT = ERR_INVALID_ENTRY_COMMENT;
|
|
exports.ERR_INVALID_ENTRY_NAME = ERR_INVALID_ENTRY_NAME;
|
|
exports.ERR_INVALID_EXTRAFIELD_DATA = ERR_INVALID_EXTRAFIELD_DATA;
|
|
exports.ERR_INVALID_EXTRAFIELD_TYPE = ERR_INVALID_EXTRAFIELD_TYPE;
|
|
exports.ERR_INVALID_PASSWORD = ERR_INVALID_PASSWORD;
|
|
exports.ERR_INVALID_SIGNATURE = ERR_INVALID_SIGNATURE;
|
|
exports.ERR_INVALID_UNCOMPRESSED_SIZE = ERR_INVALID_UNCOMPRESSED_SIZE;
|
|
exports.ERR_INVALID_VERSION = ERR_INVALID_VERSION;
|
|
exports.ERR_ITERATOR_COMPLETED_TOO_SOON = ERR_ITERATOR_COMPLETED_TOO_SOON;
|
|
exports.ERR_LOCAL_FILE_HEADER_NOT_FOUND = ERR_LOCAL_FILE_HEADER_NOT_FOUND;
|
|
exports.ERR_OVERLAPPING_ENTRY = ERR_OVERLAPPING_ENTRY;
|
|
exports.ERR_SPLIT_ZIP_FILE = ERR_SPLIT_ZIP_FILE;
|
|
exports.ERR_UNDEFINED_READER = ERR_UNDEFINED_READER;
|
|
exports.ERR_UNDEFINED_UNCOMPRESSED_SIZE = ERR_UNDEFINED_UNCOMPRESSED_SIZE;
|
|
exports.ERR_UNSUPPORTED_COMPRESSION = ERR_UNSUPPORTED_COMPRESSION$1;
|
|
exports.ERR_UNSUPPORTED_ENCRYPTION = ERR_UNSUPPORTED_ENCRYPTION;
|
|
exports.ERR_UNSUPPORTED_FORMAT = ERR_UNSUPPORTED_FORMAT;
|
|
exports.ERR_WRITER_NOT_INITIALIZED = ERR_WRITER_NOT_INITIALIZED;
|
|
exports.ERR_ZIP_NOT_EMPTY = ERR_ZIP_NOT_EMPTY;
|
|
exports.HttpRangeReader = HttpRangeReader;
|
|
exports.HttpReader = HttpReader;
|
|
exports.Reader = Reader;
|
|
exports.SplitDataReader = SplitDataReader;
|
|
exports.SplitDataWriter = SplitDataWriter;
|
|
exports.TextReader = TextReader;
|
|
exports.TextWriter = TextWriter;
|
|
exports.Uint8ArrayReader = Uint8ArrayReader;
|
|
exports.Uint8ArrayWriter = Uint8ArrayWriter;
|
|
exports.Writer = Writer;
|
|
exports.ZipReader = ZipReader;
|
|
exports.ZipReaderStream = ZipReaderStream;
|
|
exports.ZipWriter = ZipWriter;
|
|
exports.ZipWriterStream = ZipWriterStream;
|
|
exports.configure = configure;
|
|
exports.createBlobTempStream = createBlobTempStream;
|
|
exports.createOPFSTempStream = createOPFSTempStream;
|
|
exports.createSyncAccessHandleTempStream = createSyncAccessHandleTempStream;
|
|
exports.fs = fs;
|
|
exports.getMimeType = getMimeType;
|
|
exports.terminateWorkers = terminateWorkers;
|