6690 lines
259 KiB
JavaScript
6690 lines
259 KiB
JavaScript
(function (global, factory) {
|
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.zip = {}));
|
|
})(this, (function (exports) { 'use strict';
|
|
|
|
const { Array, Object, String, Number, BigInt, Math, Date, Map, Set, Response, URL, Error, Uint8Array, Uint16Array, Uint32Array, DataView, Blob, Promise, TextEncoder, TextDecoder, document, crypto, btoa, TransformStream, ReadableStream, WritableStream, CompressionStream, DecompressionStream, navigator, Worker } = typeof globalThis !== 'undefined' ? globalThis : this || self;
|
|
|
|
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 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 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 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_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 VERSION_DEFLATE = 0x14;
|
|
const VERSION_ZIP64 = 0x2D;
|
|
const VERSION_AES = 0x33;
|
|
|
|
const DIRECTORY_SIGNATURE = "/";
|
|
|
|
const HEADER_SIZE = 30;
|
|
const HEADER_OFFSET_SIGNATURE = 10;
|
|
const HEADER_OFFSET_COMPRESSED_SIZE = 14;
|
|
const HEADER_OFFSET_UNCOMPRESSED_SIZE = 18;
|
|
|
|
const MAX_DATE = new Date(2107, 11, 31);
|
|
const MIN_DATE = new Date(1980, 0, 1);
|
|
|
|
const UNDEFINED_VALUE = undefined;
|
|
const UNDEFINED_TYPE = "undefined";
|
|
const FUNCTION_TYPE = "function";
|
|
|
|
/*
|
|
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 StreamAdapter {
|
|
|
|
constructor(Codec) {
|
|
return class extends TransformStream {
|
|
constructor(_format, options) {
|
|
const codec = new Codec(options);
|
|
super({
|
|
transform(chunk, controller) {
|
|
controller.enqueue(codec.append(chunk));
|
|
},
|
|
flush(controller) {
|
|
const chunk = codec.flush();
|
|
if (chunk) {
|
|
controller.enqueue(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 MINIMUM_CHUNK_SIZE = 64;
|
|
let maxWorkers = 2;
|
|
try {
|
|
if (typeof navigator != UNDEFINED_TYPE && navigator.hardwareConcurrency) {
|
|
maxWorkers = navigator.hardwareConcurrency;
|
|
}
|
|
// eslint-disable-next-line no-unused-vars
|
|
} catch (_) {
|
|
// ignored
|
|
}
|
|
const DEFAULT_CONFIGURATION = {
|
|
chunkSize: 512 * 1024,
|
|
maxWorkers,
|
|
terminateWorkerTimeout: 5000,
|
|
useWebWorkers: true,
|
|
useCompressionStream: true,
|
|
workerScripts: UNDEFINED_VALUE,
|
|
CompressionStreamNative: typeof CompressionStream != UNDEFINED_TYPE && CompressionStream,
|
|
DecompressionStreamNative: typeof DecompressionStream != UNDEFINED_TYPE && DecompressionStream
|
|
};
|
|
|
|
const config = Object.assign({}, DEFAULT_CONFIGURATION);
|
|
|
|
function getConfiguration() {
|
|
return config;
|
|
}
|
|
|
|
function getChunkSize(config) {
|
|
return Math.max(config.chunkSize, MINIMUM_CHUNK_SIZE);
|
|
}
|
|
|
|
function configure(configuration) {
|
|
const {
|
|
baseURL,
|
|
chunkSize,
|
|
maxWorkers,
|
|
terminateWorkerTimeout,
|
|
useCompressionStream,
|
|
useWebWorkers,
|
|
Deflate,
|
|
Inflate,
|
|
CompressionStream,
|
|
DecompressionStream,
|
|
workerScripts
|
|
} = configuration;
|
|
setIfDefined("baseURL", baseURL);
|
|
setIfDefined("chunkSize", chunkSize);
|
|
setIfDefined("maxWorkers", maxWorkers);
|
|
setIfDefined("terminateWorkerTimeout", terminateWorkerTimeout);
|
|
setIfDefined("useCompressionStream", useCompressionStream);
|
|
setIfDefined("useWebWorkers", useWebWorkers);
|
|
if (Deflate) {
|
|
config.CompressionStream = new StreamAdapter(Deflate);
|
|
}
|
|
if (Inflate) {
|
|
config.DecompressionStream = new StreamAdapter(Inflate);
|
|
}
|
|
setIfDefined("CompressionStream", CompressionStream);
|
|
setIfDefined("DecompressionStream", DecompressionStream);
|
|
if (workerScripts !== UNDEFINED_VALUE) {
|
|
const { deflate, inflate } = workerScripts;
|
|
if (deflate || inflate) {
|
|
if (!config.workerScripts) {
|
|
config.workerScripts = {};
|
|
}
|
|
}
|
|
if (deflate) {
|
|
if (!Array.isArray(deflate)) {
|
|
throw new Error("workerScripts.deflate must be an array");
|
|
}
|
|
config.workerScripts.deflate = deflate;
|
|
}
|
|
if (inflate) {
|
|
if (!Array.isArray(inflate)) {
|
|
throw new Error("workerScripts.inflate must be an array");
|
|
}
|
|
config.workerScripts.inflate = inflate;
|
|
}
|
|
}
|
|
}
|
|
|
|
function setIfDefined(propertyName, propertyValue) {
|
|
if (propertyValue !== UNDEFINED_VALUE) {
|
|
config[propertyName] = propertyValue;
|
|
}
|
|
}
|
|
|
|
function e(e,t={}){const n='const{Array:e,Object:t,Number:n,Math:r,Error:s,Uint8Array:i,Uint16Array:o,Uint32Array:c,Int32Array:f,Map:a,DataView:l,Promise:u,TextEncoder:w,crypto:h,postMessage:d,TransformStream:p,ReadableStream:y,WritableStream:m,CompressionStream:b,DecompressionStream:g}=self,k=void 0,v="undefined",S="function";class z{constructor(e){return class extends p{constructor(t,n){const r=new e(n);super({transform(e,t){t.enqueue(r.append(e))},flush(e){const t=r.flush();t&&e.enqueue(t)}})}}}}const C=[];for(let e=0;256>e;e++){let t=e;for(let e=0;8>e;e++)1&t?t=t>>>1^3988292384:t>>>=1;C[e]=t}class x{constructor(e){this.t=e||-1}append(e){let t=0|this.t;for(let n=0,r=0|e.length;r>n;n++)t=t>>>8^C[255&(t^e[n])];this.t=t}get(){return~this.t}}class A extends p{constructor(){let e;const t=new x;super({transform(e,n){t.append(e),n.enqueue(e)},flush(){const n=new i(4);new l(n.buffer).setUint32(0,t.get()),e.value=n}}),e=this}}const I={concat(e,t){if(0===e.length||0===t.length)return e.concat(t);const n=e[e.length-1],r=I.i(n);return 32===r?e.concat(t):I.o(t,r,0|n,e.slice(0,e.length-1))},l(e){const t=e.length;if(0===t)return 0;const n=e[t-1];return 32*(t-1)+I.i(n)},u(e,t){if(32*e.length<t)return e;const n=(e=e.slice(0,r.ceil(t/32))).length;return t&=31,n>0&&t&&(e[n-1]=I.h(t,e[n-1]&2147483648>>t-1,1)),e},h:(e,t,n)=>32===e?t:(n?0|t:t<<32-e)+1099511627776*e,i:e=>r.round(e/1099511627776)||32,o(e,t,n,r){for(void 0===r&&(r=[]);t>=32;t-=32)r.push(n),n=0;if(0===t)return r.concat(e);for(let s=0;s<e.length;s++)r.push(n|e[s]>>>t),n=e[s]<<32-t;const s=e.length?e[e.length-1]:0,i=I.i(s);return r.push(I.h(t+i&31,t+i>32?n:r.pop(),1)),r}},_={bytes:{p(e){const t=I.l(e)/8,n=new i(t);let r;for(let s=0;t>s;s++)3&s||(r=e[s/4]),n[s]=r>>>24,r<<=8;return n},m(e){const t=[];let n,r=0;for(n=0;n<e.length;n++)r=r<<8|e[n],3&~n||(t.push(r),r=0);return 3&n&&t.push(I.h(8*(3&n),r)),t}}},P=class{constructor(e){const t=this;t.blockSize=512,t.k=[1732584193,4023233417,2562383102,271733878,3285377520],t.v=[1518500249,1859775393,2400959708,3395469782],e?(t.S=e.S.slice(0),t.C=e.C.slice(0),t.A=e.A):t.reset()}reset(){const e=this;return e.S=e.k.slice(0),e.C=[],e.A=0,e}update(e){const t=this;"string"==typeof e&&(e=_.I.m(e));const n=t.C=I.concat(t.C,e),r=t.A,i=t.A=r+I.l(e);if(i>9007199254740991)throw new s("Cannot hash more than 2^53 - 1 bits");const o=new c(n);let f=0;for(let e=t.blockSize+r-(t.blockSize+r&t.blockSize-1);i>=e;e+=t.blockSize)t._(o.subarray(16*f,16*(f+1))),f+=1;return n.splice(0,16*f),t}P(){const e=this;let t=e.C;const n=e.S;t=I.concat(t,[I.h(1,1)]);for(let e=t.length+2;15&e;e++)t.push(0);for(t.push(r.floor(e.A/4294967296)),t.push(0|e.A);t.length;)e._(t.splice(0,16));return e.reset(),n}D(e,t,n,r){return e>19?e>39?e>59?e>79?void 0:t^n^r:t&n|t&r|n&r:t^n^r:t&n|~t&r}V(e,t){return t<<e|t>>>32-e}_(t){const n=this,s=n.S,i=e(80);for(let e=0;16>e;e++)i[e]=t[e];let o=s[0],c=s[1],f=s[2],a=s[3],l=s[4];for(let e=0;79>=e;e++){16>e||(i[e]=n.V(1,i[e-3]^i[e-8]^i[e-14]^i[e-16]));const t=n.V(5,o)+n.D(e,c,f,a)+l+i[e]+n.v[r.floor(e/20)]|0;l=a,a=f,f=n.V(30,c),c=o,o=t}s[0]=s[0]+o|0,s[1]=s[1]+c|0,s[2]=s[2]+f|0,s[3]=s[3]+a|0,s[4]=s[4]+l|0}},D={getRandomValues(e){const t=new c(e.buffer),n=e=>{let t=987654321;const n=4294967295;return()=>(t=36969*(65535&t)+(t>>16)&n,(((t<<16)+(e=18e3*(65535&e)+(e>>16)&n)&n)/4294967296+.5)*(r.random()>.5?1:-1))};for(let s,i=0;i<e.length;i+=4){const e=n(4294967296*(s||r.random()));s=987654071*e(),t[i/4]=4294967296*e()|0}return e}},V={importKey:e=>new V.R(_.bytes.m(e)),B(e,t,n,r){if(n=n||1e4,0>r||0>n)throw new s("invalid params to pbkdf2");const i=1+(r>>5)<<2;let o,c,f,a,u;const w=new ArrayBuffer(i),h=new l(w);let d=0;const p=I;for(t=_.bytes.m(t),u=1;(i||1)>d;u++){for(o=c=e.encrypt(p.concat(t,[u])),f=1;n>f;f++)for(c=e.encrypt(c),a=0;a<c.length;a++)o[a]^=c[a];for(f=0;(i||1)>d&&f<o.length;f++)h.setInt32(d,o[f]),d+=4}return w.slice(0,r/8)},R:class{constructor(e){const t=this,n=t.M=P,r=[[],[]];t.U=[new n,new n];const s=t.U[0].blockSize/32;e.length>s&&(e=(new n).update(e).P());for(let t=0;s>t;t++)r[0][t]=909522486^e[t],r[1][t]=1549556828^e[t];t.U[0].update(r[0]),t.U[1].update(r[1]),t.K=new n(t.U[0])}reset(){const e=this;e.K=new e.M(e.U[0]),e.N=!1}update(e){this.N=!0,this.K.update(e)}digest(){const e=this,t=e.K.P(),n=new e.M(e.U[1]).update(t).P();return e.reset(),n}encrypt(e){if(this.N)throw new s("encrypt on already updated hmac called!");return this.update(e),this.digest(e)}}},R=typeof h!=v&&typeof h.getRandomValues==S,B="Invalid password",E="Invalid signature",M="zipjs-abort-check-password";function U(e){return R?h.getRandomValues(e):D.getRandomValues(e)}const K=16,N={name:"PBKDF2"},O=t.assign({hash:{name:"HMAC"}},N),T=t.assign({iterations:1e3,hash:{name:"SHA-1"}},N),W=["deriveBits"],j=[8,12,16],H=[16,24,32],L=10,F=[0,0,0,0],q=typeof h!=v,G=q&&h.subtle,J=q&&typeof G!=v,Q=_.bytes,X=class{constructor(e){const t=this;t.O=[[[],[],[],[],[]],[[],[],[],[],[]]],t.O[0][0][0]||t.T();const n=t.O[0][4],r=t.O[1],i=e.length;let o,c,f,a=1;if(4!==i&&6!==i&&8!==i)throw new s("invalid aes key size");for(t.v=[c=e.slice(0),f=[]],o=i;4*i+28>o;o++){let e=c[o-1];(o%i===0||8===i&&o%i===4)&&(e=n[e>>>24]<<24^n[e>>16&255]<<16^n[e>>8&255]<<8^n[255&e],o%i===0&&(e=e<<8^e>>>24^a<<24,a=a<<1^283*(a>>7))),c[o]=c[o-i]^e}for(let e=0;o;e++,o--){const t=c[3&e?o:o-4];f[e]=4>=o||4>e?t:r[0][n[t>>>24]]^r[1][n[t>>16&255]]^r[2][n[t>>8&255]]^r[3][n[255&t]]}}encrypt(e){return this.W(e,0)}decrypt(e){return this.W(e,1)}T(){const e=this.O[0],t=this.O[1],n=e[4],r=t[4],s=[],i=[];let o,c,f,a;for(let e=0;256>e;e++)i[(s[e]=e<<1^283*(e>>7))^e]=e;for(let l=o=0;!n[l];l^=c||1,o=i[o]||1){let i=o^o<<1^o<<2^o<<3^o<<4;i=i>>8^255&i^99,n[l]=i,r[i]=l,a=s[f=s[c=s[l]]];let u=16843009*a^65537*f^257*c^16843008*l,w=257*s[i]^16843008*i;for(let n=0;4>n;n++)e[n][l]=w=w<<24^w>>>8,t[n][i]=u=u<<24^u>>>8}for(let n=0;5>n;n++)e[n]=e[n].slice(0),t[n]=t[n].slice(0)}W(e,t){if(4!==e.length)throw new s("invalid aes block size");const n=this.v[t],r=n.length/4-2,i=[0,0,0,0],o=this.O[t],c=o[0],f=o[1],a=o[2],l=o[3],u=o[4];let w,h,d,p=e[0]^n[0],y=e[t?3:1]^n[1],m=e[2]^n[2],b=e[t?1:3]^n[3],g=4;for(let e=0;r>e;e++)w=c[p>>>24]^f[y>>16&255]^a[m>>8&255]^l[255&b]^n[g],h=c[y>>>24]^f[m>>16&255]^a[b>>8&255]^l[255&p]^n[g+1],d=c[m>>>24]^f[b>>16&255]^a[p>>8&255]^l[255&y]^n[g+2],b=c[b>>>24]^f[p>>16&255]^a[y>>8&255]^l[255&m]^n[g+3],g+=4,p=w,y=h,m=d;for(let e=0;4>e;e++)i[t?3&-e:e]=u[p>>>24]<<24^u[y>>16&255]<<16^u[m>>8&255]<<8^u[255&b]^n[g++],w=p,p=y,y=m,m=b,b=w;return i}},Y=class{constructor(e,t){this.j=e,this.H=t,this.L=t}reset(){this.L=this.H}update(e){return this.F(this.j,e,this.L)}q(e){if(255&~(e>>24))e+=1<<24;else{let t=e>>16&255,n=e>>8&255,r=255&e;255===t?(t=0,255===n?(n=0,255===r?r=0:++r):++n):++t,e=0,e+=t<<16,e+=n<<8,e+=r}return e}G(e){0===(e[0]=this.q(e[0]))&&(e[1]=this.q(e[1]))}F(e,t,n){let r;if(!(r=t.length))return[];const s=I.l(t);for(let s=0;r>s;s+=4){this.G(n);const r=e.encrypt(n);t[s]^=r[0],t[s+1]^=r[1],t[s+2]^=r[2],t[s+3]^=r[3]}return I.u(t,s)}},Z=V.R;let $=q&&J&&typeof G.importKey==S,ee=q&&J&&typeof G.deriveBits==S;class te extends p{constructor({password:e,rawPassword:n,signed:r,encryptionStrength:o,checkPasswordOnly:c}){super({start(){t.assign(this,{ready:new u(e=>this.J=e),password:ie(e,n),signed:r,X:o-1,pending:new i})},async transform(e,t){const n=this,{password:r,X:o,J:f,ready:a}=n;r?(await(async(e,t,n,r)=>{const i=await se(e,t,n,ce(r,0,j[t])),o=ce(r,j[t]);if(i[0]!=o[0]||i[1]!=o[1])throw new s(B)})(n,o,r,ce(e,0,j[o]+2)),e=ce(e,j[o]+2),c?t.error(new s(M)):f()):await a;const l=new i(e.length-L-(e.length-L)%K);t.enqueue(re(n,e,l,0,L,!0))},async flush(e){const{signed:t,Y:n,Z:r,pending:o,ready:c}=this;if(r&&n){await c;const f=ce(o,0,o.length-L),a=ce(o,o.length-L);let l=new i;if(f.length){const e=ae(Q,f);r.update(e);const t=n.update(e);l=fe(Q,t)}if(t){const e=ce(fe(Q,r.digest()),0,L);for(let t=0;L>t;t++)if(e[t]!=a[t])throw new s(E)}e.enqueue(l)}}})}}class ne extends p{constructor({password:e,rawPassword:n,encryptionStrength:r}){let s;super({start(){t.assign(this,{ready:new u(e=>this.J=e),password:ie(e,n),X:r-1,pending:new i})},async transform(e,t){const n=this,{password:r,X:s,J:o,ready:c}=n;let f=new i;r?(f=await(async(e,t,n)=>{const r=U(new i(j[t]));return oe(r,await se(e,t,n,r))})(n,s,r),o()):await c;const a=new i(f.length+e.length-e.length%K);a.set(f,0),t.enqueue(re(n,e,a,f.length,0))},async flush(e){const{Y:t,Z:n,pending:r,ready:o}=this;if(n&&t){await o;let c=new i;if(r.length){const e=t.update(ae(Q,r));n.update(e),c=fe(Q,e)}s.signature=fe(Q,n.digest()).slice(0,L),e.enqueue(oe(c,s.signature))}}}),s=this}}function re(e,t,n,r,s,o){const{Y:c,Z:f,pending:a}=e,l=t.length-s;let u;for(a.length&&(t=oe(a,t),n=((e,t)=>{if(t&&t>e.length){const n=e;(e=new i(t)).set(n,0)}return e})(n,l-l%K)),u=0;l-K>=u;u+=K){const e=ae(Q,ce(t,u,u+K));o&&f.update(e);const s=c.update(e);o||f.update(s),n.set(fe(Q,s),u+r)}return e.pending=ce(t,u),n}async function se(n,r,s,o){n.password=null;const c=await(async(e,t,n,r,s)=>{if(!$)return V.importKey(t);try{return await G.importKey("raw",t,n,!1,s)}catch(e){return $=!1,V.importKey(t)}})(0,s,O,0,W),f=await(async(e,t,n)=>{if(!ee)return V.B(t,e.salt,T.iterations,n);try{return await G.deriveBits(e,t,n)}catch(r){return ee=!1,V.B(t,e.salt,T.iterations,n)}})(t.assign({salt:o},T),c,8*(2*H[r]+2)),a=new i(f),l=ae(Q,ce(a,0,H[r])),u=ae(Q,ce(a,H[r],2*H[r])),w=ce(a,2*H[r]);return t.assign(n,{keys:{key:l,$:u,passwordVerification:w},Y:new Y(new X(l),e.from(F)),Z:new Z(u)}),w}function ie(e,t){return t===k?(e=>{if(typeof w==v){const t=new i((e=unescape(encodeURIComponent(e))).length);for(let n=0;n<t.length;n++)t[n]=e.charCodeAt(n);return t}return(new w).encode(e)})(e):t}function oe(e,t){let n=e;return e.length+t.length&&(n=new i(e.length+t.length),n.set(e,0),n.set(t,e.length)),n}function ce(e,t,n){return e.subarray(t,n)}function fe(e,t){return e.p(t)}function ae(e,t){return e.m(t)}class le extends p{constructor({password:e,passwordVerification:n,checkPasswordOnly:r}){super({start(){t.assign(this,{password:e,passwordVerification:n}),de(this,e)},transform(e,t){const n=this;if(n.password){const t=we(n,e.subarray(0,12));if(n.password=null,t[11]!=n.passwordVerification)throw new s(B);e=e.subarray(12)}r?t.error(new s(M)):t.enqueue(we(n,e))}})}}class ue extends p{constructor({password:e,passwordVerification:n}){super({start(){t.assign(this,{password:e,passwordVerification:n}),de(this,e)},transform(e,t){const n=this;let r,s;if(n.password){n.password=null;const t=U(new i(12));t[11]=n.passwordVerification,r=new i(e.length+t.length),r.set(he(n,t),0),s=12}else r=new i(e.length),s=0;r.set(he(n,e),s),t.enqueue(r)}})}}function we(e,t){const n=new i(t.length);for(let r=0;r<t.length;r++)n[r]=ye(e)^t[r],pe(e,n[r]);return n}function he(e,t){const n=new i(t.length);for(let r=0;r<t.length;r++)n[r]=ye(e)^t[r],pe(e,t[r]);return n}function de(e,n){const r=[305419896,591751049,878082192];t.assign(e,{keys:r,ee:new x(r[0]),te:new x(r[2])});for(let t=0;t<n.length;t++)pe(e,n.charCodeAt(t))}function pe(e,t){let[n,s,i]=e.keys;e.ee.append([t]),n=~e.ee.get(),s=be(r.imul(be(s+me(n)),134775813)+1),e.te.append([s>>>24]),i=~e.te.get(),e.keys=[n,s,i]}function ye(e){const t=2|e.keys[2];return me(r.imul(t,1^t)>>>8)}function me(e){return 255&e}function be(e){return 4294967295&e}const ge="deflate-raw";class ke extends p{constructor(e,{chunkSize:t,CompressionStream:n,CompressionStreamNative:r}){super({});const{compressed:s,encrypted:i,useCompressionStream:o,zipCrypto:c,signed:f,level:a}=e,u=this;let w,h,d=super.readable;i&&!c||!f||(w=new A,d=Ce(d,w)),s&&(d=ze(d,o,{level:a,chunkSize:t},r,n)),i&&(c?d=Ce(d,new ue(e)):(h=new ne(e),d=Ce(d,h))),Se(u,d,()=>{let e;i&&!c&&(e=h.signature),i&&!c||!f||(e=new l(w.value.buffer).getUint32(0)),u.signature=e})}}class ve extends p{constructor(e,{chunkSize:t,DecompressionStream:n,DecompressionStreamNative:r}){super({});const{zipCrypto:i,encrypted:o,signed:c,signature:f,compressed:a,useCompressionStream:u}=e;let w,h,d=super.readable;o&&(i?d=Ce(d,new le(e)):(h=new te(e),d=Ce(d,h))),a&&(d=ze(d,u,{chunkSize:t},r,n)),o&&!i||!c||(w=new A,d=Ce(d,w)),Se(this,d,()=>{if((!o||i)&&c){const e=new l(w.value.buffer);if(f!=e.getUint32(0,!1))throw new s(E)}})}}function Se(e,n,r){n=Ce(n,new p({flush:r})),t.defineProperty(e,"readable",{get:()=>n})}function ze(e,t,n,r,s){try{e=Ce(e,new(t&&r?r:s)(ge,n))}catch(r){if(!t)throw r;e=Ce(e,new s(ge,n))}return e}function Ce(e,t){return e.pipeThrough(t)}const xe="data",Ae="close";class Ie extends p{constructor(e,n){super({});const r=this,{codecType:i}=e;let o;i.startsWith("deflate")?o=ke:i.startsWith("inflate")&&(o=ve),r.outputSize=0;let c=0;const f=new o(e,n),a=super.readable,l=new p({transform(e,t){e&&e.length&&(c+=e.length,t.enqueue(e))},flush(){t.assign(r,{inputSize:c})}}),u=new p({transform(t,n){if(t&&t.length&&(n.enqueue(t),r.outputSize+=t.length,e.outputSize&&r.outputSize>e.outputSize))throw new s("Invalid uncompressed size")},flush(){const{signature:e}=f;t.assign(r,{signature:e,inputSize:c})}});t.defineProperty(r,"readable",{get:()=>a.pipeThrough(l).pipeThrough(f).pipeThrough(u)})}}class _e extends p{constructor(e){let t;super({transform:function n(r,s){if(t){const e=new i(t.length+r.length);e.set(t),e.set(r,t.length),r=e,t=null}r.length>e?(s.enqueue(r.slice(0,e)),n(r.slice(e),s)):t=r},flush(e){t&&t.length&&e.enqueue(t)}})}}const Pe=new a,De=new a;let Ve,Re=0,Be=!0;async function Ee(e){try{const{options:t,scripts:r,config:s}=e;if(r&&r.length)try{Be?importScripts.apply(k,r):await Me(r)}catch(e){Be=!1,await Me(r)}self.initCodec&&self.initCodec(),s.CompressionStreamNative=self.CompressionStream,s.DecompressionStreamNative=self.DecompressionStream,self.Deflate&&(s.CompressionStream=new z(self.Deflate)),self.Inflate&&(s.DecompressionStream=new z(self.Inflate));const i={highWaterMark:1},o=e.readable||new y({async pull(e){const t=new u(e=>Pe.set(Re,e));Ue({type:"pull",messageId:Re}),Re=(Re+1)%n.MAX_SAFE_INTEGER;const{value:r,done:s}=await t;e.enqueue(r),s&&e.close()}},i),c=e.writable||new m({async write(e){let t;const r=new u(e=>t=e);De.set(Re,t),Ue({type:xe,value:e,messageId:Re}),Re=(Re+1)%n.MAX_SAFE_INTEGER,await r}},i),f=new Ie(t,s);Ve=new AbortController;const{signal:a}=Ve;await o.pipeThrough(f).pipeThrough(new _e(s.chunkSize)).pipeTo(c,{signal:a,preventClose:!0,preventAbort:!0}),await c.getWriter().close();const{signature:l,inputSize:w,outputSize:h}=f;Ue({type:Ae,result:{signature:l,inputSize:w,outputSize:h}})}catch(e){e.outputSize=0,Ke(e)}}async function Me(e){for(const t of e)await import(t)}function Ue(e){let{value:t}=e;if(t)if(t.length)try{t=new i(t),e.value=t.buffer,d(e,[e.value])}catch(t){d(e)}else d(e);else d(e)}function Ke(e=new s("Unknown error")){const{message:t,stack:n,code:r,name:i,outputSize:o}=e;d({error:{message:t,stack:n,code:r,name:i,outputSize:o}})}addEventListener("message",({data:e})=>{const{type:t,messageId:n,value:r,done:s}=e;try{if("start"==t&&Ee(e),t==xe){const e=Pe.get(n);Pe.delete(n),e({value:new i(r),done:s})}if("ack"==t){const e=De.get(n);De.delete(n),e()}t==Ae&&Ve.abort()}catch(e){Ke(e)}});const Ne=-2;function Oe(t){return Te(t.map(([t,n])=>new e(t).fill(n,0,t)))}function Te(t){return t.reduce((t,n)=>t.concat(e.isArray(n)?Te(n):n),[])}const We=[0,1,2,3].concat(...Oe([[2,4],[2,5],[4,6],[4,7],[8,8],[8,9],[16,10],[16,11],[32,12],[32,13],[64,14],[64,15],[2,0],[1,16],[1,17],[2,18],[2,19],[4,20],[4,21],[8,22],[8,23],[16,24],[16,25],[32,26],[32,27],[64,28],[64,29]]));function je(){const e=this;function t(e,t){let n=0;do{n|=1&e,e>>>=1,n<<=1}while(--t>0);return n>>>1}e.ne=n=>{const s=e.re,i=e.ie.se,o=e.ie.oe;let c,f,a,l=-1;for(n.ce=0,n.fe=573,c=0;o>c;c++)0!==s[2*c]?(n.ae[++n.ce]=l=c,n.le[c]=0):s[2*c+1]=0;for(;2>n.ce;)a=n.ae[++n.ce]=2>l?++l:0,s[2*a]=1,n.le[a]=0,n.ue--,i&&(n.we-=i[2*a+1]);for(e.he=l,c=r.floor(n.ce/2);c>=1;c--)n.de(s,c);a=o;do{c=n.ae[1],n.ae[1]=n.ae[n.ce--],n.de(s,1),f=n.ae[1],n.ae[--n.fe]=c,n.ae[--n.fe]=f,s[2*a]=s[2*c]+s[2*f],n.le[a]=r.max(n.le[c],n.le[f])+1,s[2*c+1]=s[2*f+1]=a,n.ae[1]=a++,n.de(s,1)}while(n.ce>=2);n.ae[--n.fe]=n.ae[1],(t=>{const n=e.re,r=e.ie.se,s=e.ie.pe,i=e.ie.ye,o=e.ie.me;let c,f,a,l,u,w,h=0;for(l=0;15>=l;l++)t.be[l]=0;for(n[2*t.ae[t.fe]+1]=0,c=t.fe+1;573>c;c++)f=t.ae[c],l=n[2*n[2*f+1]+1]+1,l>o&&(l=o,h++),n[2*f+1]=l,f>e.he||(t.be[l]++,u=0,i>f||(u=s[f-i]),w=n[2*f],t.ue+=w*(l+u),r&&(t.we+=w*(r[2*f+1]+u)));if(0!==h){do{for(l=o-1;0===t.be[l];)l--;t.be[l]--,t.be[l+1]+=2,t.be[o]--,h-=2}while(h>0);for(l=o;0!==l;l--)for(f=t.be[l];0!==f;)a=t.ae[--c],a>e.he||(n[2*a+1]!=l&&(t.ue+=(l-n[2*a+1])*n[2*a],n[2*a+1]=l),f--)}})(n),((e,n,r)=>{const s=[];let i,o,c,f=0;for(i=1;15>=i;i++)s[i]=f=f+r[i-1]<<1;for(o=0;n>=o;o++)c=e[2*o+1],0!==c&&(e[2*o]=t(s[c]++,c))})(s,e.he,n.be)}}function He(e,t,n,r,s){const i=this;i.se=e,i.pe=t,i.ye=n,i.oe=r,i.me=s}je.ge=[0,1,2,3,4,5,6,7].concat(...Oe([[2,8],[2,9],[2,10],[2,11],[4,12],[4,13],[4,14],[4,15],[8,16],[8,17],[8,18],[8,19],[16,20],[16,21],[16,22],[16,23],[32,24],[32,25],[32,26],[31,27],[1,28]])),je.ke=[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],je.ve=[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],je.Se=e=>256>e?We[e]:We[256+(e>>>7)],je.ze=[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],je.Ce=[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],je.xe=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],je.Ae=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];const Le=Oe([[144,8],[112,9],[24,7],[8,8]]);He.Ie=Te([12,140,76,204,44,172,108,236,28,156,92,220,60,188,124,252,2,130,66,194,34,162,98,226,18,146,82,210,50,178,114,242,10,138,74,202,42,170,106,234,26,154,90,218,58,186,122,250,6,134,70,198,38,166,102,230,22,150,86,214,54,182,118,246,14,142,78,206,46,174,110,238,30,158,94,222,62,190,126,254,1,129,65,193,33,161,97,225,17,145,81,209,49,177,113,241,9,137,73,201,41,169,105,233,25,153,89,217,57,185,121,249,5,133,69,197,37,165,101,229,21,149,85,213,53,181,117,245,13,141,77,205,45,173,109,237,29,157,93,221,61,189,125,253,19,275,147,403,83,339,211,467,51,307,179,435,115,371,243,499,11,267,139,395,75,331,203,459,43,299,171,427,107,363,235,491,27,283,155,411,91,347,219,475,59,315,187,443,123,379,251,507,7,263,135,391,71,327,199,455,39,295,167,423,103,359,231,487,23,279,151,407,87,343,215,471,55,311,183,439,119,375,247,503,15,271,143,399,79,335,207,463,47,303,175,431,111,367,239,495,31,287,159,415,95,351,223,479,63,319,191,447,127,383,255,511,0,64,32,96,16,80,48,112,8,72,40,104,24,88,56,120,4,68,36,100,20,84,52,116,3,131,67,195,35,163,99,227].map((e,t)=>[e,Le[t]]));const Fe=Oe([[30,5]]);function qe(e,t,n,r,s){const i=this;i._e=e,i.Pe=t,i.De=n,i.Ve=r,i.Re=s}He.Be=Te([0,16,8,24,4,20,12,28,2,18,10,26,6,22,14,30,1,17,9,25,5,21,13,29,3,19,11,27,7,23].map((e,t)=>[e,Fe[t]])),He.Ee=new He(He.Ie,je.ze,257,286,15),He.Me=new He(He.Be,je.Ce,0,30,15),He.Ue=new He(null,je.xe,0,19,7);const Ge=[new qe(0,0,0,0,0),new qe(4,4,8,4,1),new qe(4,5,16,8,1),new qe(4,6,32,32,1),new qe(4,4,16,16,2),new qe(8,16,32,32,2),new qe(8,16,128,128,2),new qe(8,32,128,256,2),new qe(32,128,258,1024,2),new qe(32,258,258,4096,2)],Je=["need dictionary","stream end","","","stream error","data error","","buffer error","",""],Qe=113,Xe=666,Ye=262;function Ze(e,t,n,r){const s=e[2*t],i=e[2*n];return i>s||s==i&&r[t]<=r[n]}function $e(){const e=this;let t,n,s,c,f,a,l,u,w,h,d,p,y,m,b,g,k,v,S,z,C,x,A,I,_,P,D,V,R,B,E,M,U;const K=new je,N=new je,O=new je;let T,W,j,H,L,F;function q(){let t;for(t=0;286>t;t++)E[2*t]=0;for(t=0;30>t;t++)M[2*t]=0;for(t=0;19>t;t++)U[2*t]=0;E[512]=1,e.ue=e.we=0,W=j=0}function G(e,t){let n,r=-1,s=e[1],i=0,o=7,c=4;0===s&&(o=138,c=3),e[2*(t+1)+1]=65535;for(let f=0;t>=f;f++)n=s,s=e[2*(f+1)+1],++i<o&&n==s||(c>i?U[2*n]+=i:0!==n?(n!=r&&U[2*n]++,U[32]++):i>10?U[36]++:U[34]++,i=0,r=n,0===s?(o=138,c=3):n==s?(o=6,c=3):(o=7,c=4))}function J(t){e.Ke[e.pending++]=t}function Q(e){J(255&e),J(e>>>8&255)}function X(e,t){let n;const r=t;F>16-r?(n=e,L|=n<<F&65535,Q(L),L=n>>>16-F,F+=r-16):(L|=e<<F&65535,F+=r)}function Y(e,t){const n=2*e;X(65535&t[n],65535&t[n+1])}function Z(e,t){let n,r,s=-1,i=e[1],o=0,c=7,f=4;for(0===i&&(c=138,f=3),n=0;t>=n;n++)if(r=i,i=e[2*(n+1)+1],++o>=c||r!=i){if(f>o)do{Y(r,U)}while(0!==--o);else 0!==r?(r!=s&&(Y(r,U),o--),Y(16,U),X(o-3,2)):o>10?(Y(18,U),X(o-11,7)):(Y(17,U),X(o-3,3));o=0,s=r,0===i?(c=138,f=3):r==i?(c=6,f=3):(c=7,f=4)}}function $(){16==F?(Q(L),L=0,F=0):8>F||(J(255&L),L>>>=8,F-=8)}function ee(t,n){let s,i,o;if(e.Ne[W]=t,e.Oe[W]=255&n,W++,0===t?E[2*n]++:(j++,t--,E[2*(je.ge[n]+256+1)]++,M[2*je.Se(t)]++),!(8191&W)&&D>2){for(s=8*W,i=C-k,o=0;30>o;o++)s+=M[2*o]*(5+je.Ce[o]);if(s>>>=3,j<r.floor(W/2)&&s<r.floor(i/2))return!0}return W==T-1}function te(t,n){let r,s,i,o,c=0;if(0!==W)do{r=e.Ne[c],s=e.Oe[c],c++,0===r?Y(s,t):(i=je.ge[s],Y(i+256+1,t),o=je.ze[i],0!==o&&(s-=je.ke[i],X(s,o)),r--,i=je.Se(r),Y(i,n),o=je.Ce[i],0!==o&&(r-=je.ve[i],X(r,o)))}while(W>c);Y(256,t),H=t[513]}function ne(){F>8?Q(L):F>0&&J(255&L),L=0,F=0}function re(t,n,r){X(0+(r?1:0),3),((t,n)=>{ne(),H=8,Q(n),Q(~n),e.Ke.set(u.subarray(t,t+n),e.pending),e.pending+=n})(t,n)}function se(n){((t,n,r)=>{let s,i,o=0;D>0?(K.ne(e),N.ne(e),o=(()=>{let t;for(G(E,K.he),G(M,N.he),O.ne(e),t=18;t>=3&&0===U[2*je.Ae[t]+1];t--);return e.ue+=14+3*(t+1),t})(),s=e.ue+3+7>>>3,i=e.we+3+7>>>3,i>s||(s=i)):s=i=n+5,n+4>s||-1==t?i==s?(X(2+(r?1:0),3),te(He.Ie,He.Be)):(X(4+(r?1:0),3),((e,t,n)=>{let r;for(X(e-257,5),X(t-1,5),X(n-4,4),r=0;n>r;r++)X(U[2*je.Ae[r]+1],3);Z(E,e-1),Z(M,t-1)})(K.he+1,N.he+1,o+1),te(E,M)):re(t,n,r),q(),r&&ne()})(0>k?-1:k,C-k,n),k=C,t.Te()}function ie(){let e,n,r,s;do{if(s=w-A-C,0===s&&0===C&&0===A)s=f;else if(-1==s)s--;else if(C>=f+f-Ye){u.set(u.subarray(f,f+f),0),x-=f,C-=f,k-=f,e=y,r=e;do{n=65535&d[--r],d[r]=f>n?0:n-f}while(0!==--e);e=f,r=e;do{n=65535&h[--r],h[r]=f>n?0:n-f}while(0!==--e);s+=f}if(0===t.We)return;e=t.je(u,C+A,s),A+=e,3>A||(p=255&u[C],p=(p<<g^255&u[C+1])&b)}while(Ye>A&&0!==t.We)}function oe(e){let t,n,r=_,s=C,i=I;const o=C>f-Ye?C-(f-Ye):0;let c=B;const a=l,w=C+258;let d=u[s+i-1],p=u[s+i];R>I||(r>>=2),c>A&&(c=A);do{if(t=e,u[t+i]==p&&u[t+i-1]==d&&u[t]==u[s]&&u[++t]==u[s+1]){s+=2,t++;do{}while(u[++s]==u[++t]&&u[++s]==u[++t]&&u[++s]==u[++t]&&u[++s]==u[++t]&&u[++s]==u[++t]&&u[++s]==u[++t]&&u[++s]==u[++t]&&u[++s]==u[++t]&&w>s);if(n=258-(w-s),s=w-258,n>i){if(x=e,i=n,n>=c)break;d=u[s+i-1],p=u[s+i]}}}while((e=65535&h[e&a])>o&&0!==--r);return i>A?A:i}e.le=[],e.be=[],e.ae=[],E=[],M=[],U=[],e.de=(t,n)=>{const r=e.ae,s=r[n];let i=n<<1;for(;i<=e.ce&&(i<e.ce&&Ze(t,r[i+1],r[i],e.le)&&i++,!Ze(t,s,r[i],e.le));)r[n]=r[i],n=i,i<<=1;r[n]=s},e.He=(t,S,x,W,j,G)=>(W||(W=8),j||(j=8),G||(G=0),t.Le=null,-1==S&&(S=6),1>j||j>9||8!=W||9>x||x>15||0>S||S>9||0>G||G>2?Ne:(t.Fe=e,a=x,f=1<<a,l=f-1,m=j+7,y=1<<m,b=y-1,g=r.floor((m+3-1)/3),u=new i(2*f),h=[],d=[],T=1<<j+6,e.Ke=new i(4*T),s=4*T,e.Ne=new o(T),e.Oe=new i(T),D=S,V=G,(t=>(t.qe=t.Ge=0,t.Le=null,e.pending=0,e.Je=0,n=Qe,c=0,K.re=E,K.ie=He.Ee,N.re=M,N.ie=He.Me,O.re=U,O.ie=He.Ue,L=0,F=0,H=8,q(),(()=>{w=2*f,d[y-1]=0;for(let e=0;y-1>e;e++)d[e]=0;P=Ge[D].Pe,R=Ge[D]._e,B=Ge[D].De,_=Ge[D].Ve,C=0,k=0,A=0,v=I=2,z=0,p=0})(),0))(t))),e.Qe=()=>42!=n&&n!=Qe&&n!=Xe?Ne:(e.Oe=null,e.Ne=null,e.Ke=null,d=null,h=null,u=null,e.Fe=null,n==Qe?-3:0),e.Xe=(e,t,n)=>{let r=0;return-1==t&&(t=6),0>t||t>9||0>n||n>2?Ne:(Ge[D].Re!=Ge[t].Re&&0!==e.qe&&(r=e.Ye(1)),D!=t&&(D=t,P=Ge[D].Pe,R=Ge[D]._e,B=Ge[D].De,_=Ge[D].Ve),V=n,r)},e.Ze=(e,t,r)=>{let s,i=r,o=0;if(!t||42!=n)return Ne;if(3>i)return 0;for(i>f-Ye&&(i=f-Ye,o=r-i),u.set(t.subarray(o,o+i),0),C=i,k=i,p=255&u[0],p=(p<<g^255&u[1])&b,s=0;i-3>=s;s++)p=(p<<g^255&u[s+2])&b,h[s&l]=d[p],d[p]=s;return 0},e.Ye=(r,i)=>{let o,w,m,_,R;if(i>4||0>i)return Ne;if(!r.$e||!r.et&&0!==r.We||n==Xe&&4!=i)return r.Le=Je[4],Ne;if(0===r.tt)return r.Le=Je[7],-5;var B;if(t=r,_=c,c=i,42==n&&(w=8+(a-8<<4)<<8,m=(D-1&255)>>1,m>3&&(m=3),w|=m<<6,0!==C&&(w|=32),w+=31-w%31,n=Qe,J((B=w)>>8&255),J(255&B)),0!==e.pending){if(t.Te(),0===t.tt)return c=-1,0}else if(0===t.We&&_>=i&&4!=i)return t.Le=Je[7],-5;if(n==Xe&&0!==t.We)return r.Le=Je[7],-5;if(0!==t.We||0!==A||0!=i&&n!=Xe){switch(R=-1,Ge[D].Re){case 0:R=(e=>{let n,r=65535;for(r>s-5&&(r=s-5);;){if(1>=A){if(ie(),0===A&&0==e)return 0;if(0===A)break}if(C+=A,A=0,n=k+r,(0===C||C>=n)&&(A=C-n,C=n,se(!1),0===t.tt))return 0;if(C-k>=f-Ye&&(se(!1),0===t.tt))return 0}return se(4==e),0===t.tt?4==e?2:0:4==e?3:1})(i);break;case 1:R=(e=>{let n,r=0;for(;;){if(Ye>A){if(ie(),Ye>A&&0==e)return 0;if(0===A)break}if(3>A||(p=(p<<g^255&u[C+2])&b,r=65535&d[p],h[C&l]=d[p],d[p]=C),0===r||(C-r&65535)>f-Ye||2!=V&&(v=oe(r)),3>v)n=ee(0,255&u[C]),A--,C++;else if(n=ee(C-x,v-3),A-=v,v>P||3>A)C+=v,v=0,p=255&u[C],p=(p<<g^255&u[C+1])&b;else{v--;do{C++,p=(p<<g^255&u[C+2])&b,r=65535&d[p],h[C&l]=d[p],d[p]=C}while(0!==--v);C++}if(n&&(se(!1),0===t.tt))return 0}return se(4==e),0===t.tt?4==e?2:0:4==e?3:1})(i);break;case 2:R=(e=>{let n,r,s=0;for(;;){if(Ye>A){if(ie(),Ye>A&&0==e)return 0;if(0===A)break}if(3>A||(p=(p<<g^255&u[C+2])&b,s=65535&d[p],h[C&l]=d[p],d[p]=C),I=v,S=x,v=2,0!==s&&P>I&&f-Ye>=(C-s&65535)&&(2!=V&&(v=oe(s)),5>=v&&(1==V||3==v&&C-x>4096)&&(v=2)),3>I||v>I)if(0!==z){if(n=ee(0,255&u[C-1]),n&&se(!1),C++,A--,0===t.tt)return 0}else z=1,C++,A--;else{r=C+A-3,n=ee(C-1-S,I-3),A-=I-1,I-=2;do{++C>r||(p=(p<<g^255&u[C+2])&b,s=65535&d[p],h[C&l]=d[p],d[p]=C)}while(0!==--I);if(z=0,v=2,C++,n&&(se(!1),0===t.tt))return 0}}return 0!==z&&(n=ee(0,255&u[C-1]),z=0),se(4==e),0===t.tt?4==e?2:0:4==e?3:1})(i)}if(2!=R&&3!=R||(n=Xe),0==R||2==R)return 0===t.tt&&(c=-1),0;if(1==R){if(1==i)X(2,3),Y(256,He.Ie),$(),9>1+H+10-F&&(X(2,3),Y(256,He.Ie),$()),H=7;else if(re(0,0,!1),3==i)for(o=0;y>o;o++)d[o]=0;if(t.Te(),0===t.tt)return c=-1,0}}return 4!=i?0:1}}function et(){const e=this;e.nt=0,e.rt=0,e.We=0,e.qe=0,e.tt=0,e.Ge=0}function tt(e){const t=new et,n=(o=e&&e.chunkSize?e.chunkSize:65536)+5*(r.floor(o/16383)+1);var o;const c=new i(n);let f=e?e.level:-1;void 0===f&&(f=-1),t.He(f),t.$e=c,this.append=(e,r)=>{let o,f,a=0,l=0,u=0;const w=[];if(e.length){t.nt=0,t.et=e,t.We=e.length;do{if(t.rt=0,t.tt=n,o=t.Ye(0),0!=o)throw new s("deflating: "+t.Le);t.rt&&(t.rt==n?w.push(new i(c)):w.push(c.subarray(0,t.rt))),u+=t.rt,r&&t.nt>0&&t.nt!=a&&(r(t.nt),a=t.nt)}while(t.We>0||0===t.tt);return w.length>1?(f=new i(u),w.forEach(e=>{f.set(e,l),l+=e.length})):f=w[0]?new i(w[0]):new i,f}},this.flush=()=>{let e,r,o=0,f=0;const a=[];do{if(t.rt=0,t.tt=n,e=t.Ye(4),1!=e&&0!=e)throw new s("deflating: "+t.Le);n-t.tt>0&&a.push(c.slice(0,t.rt)),f+=t.rt}while(t.We>0||0===t.tt);return t.Qe(),r=new i(f),a.forEach(e=>{r.set(e,o),o+=e.length}),r}}et.prototype={He(e,t){const n=this;return n.Fe=new $e,t||(t=15),n.Fe.He(n,e,t)},Ye(e){const t=this;return t.Fe?t.Fe.Ye(t,e):Ne},Qe(){const e=this;if(!e.Fe)return Ne;const t=e.Fe.Qe();return e.Fe=null,t},Xe(e,t){const n=this;return n.Fe?n.Fe.Xe(n,e,t):Ne},Ze(e,t){const n=this;return n.Fe?n.Fe.Ze(n,e,t):Ne},je(e,t,n){const r=this;let s=r.We;return s>n&&(s=n),0===s?0:(r.We-=s,e.set(r.et.subarray(r.nt,r.nt+s),t),r.nt+=s,r.qe+=s,s)},Te(){const e=this;let t=e.Fe.pending;t>e.tt&&(t=e.tt),0!==t&&(e.$e.set(e.Fe.Ke.subarray(e.Fe.Je,e.Fe.Je+t),e.rt),e.rt+=t,e.Fe.Je+=t,e.Ge+=t,e.tt-=t,e.Fe.pending-=t,0===e.Fe.pending&&(e.Fe.Je=0))}};const nt=-2,rt=-3,st=-5,it=[0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535],ot=[96,7,256,0,8,80,0,8,16,84,8,115,82,7,31,0,8,112,0,8,48,0,9,192,80,7,10,0,8,96,0,8,32,0,9,160,0,8,0,0,8,128,0,8,64,0,9,224,80,7,6,0,8,88,0,8,24,0,9,144,83,7,59,0,8,120,0,8,56,0,9,208,81,7,17,0,8,104,0,8,40,0,9,176,0,8,8,0,8,136,0,8,72,0,9,240,80,7,4,0,8,84,0,8,20,85,8,227,83,7,43,0,8,116,0,8,52,0,9,200,81,7,13,0,8,100,0,8,36,0,9,168,0,8,4,0,8,132,0,8,68,0,9,232,80,7,8,0,8,92,0,8,28,0,9,152,84,7,83,0,8,124,0,8,60,0,9,216,82,7,23,0,8,108,0,8,44,0,9,184,0,8,12,0,8,140,0,8,76,0,9,248,80,7,3,0,8,82,0,8,18,85,8,163,83,7,35,0,8,114,0,8,50,0,9,196,81,7,11,0,8,98,0,8,34,0,9,164,0,8,2,0,8,130,0,8,66,0,9,228,80,7,7,0,8,90,0,8,26,0,9,148,84,7,67,0,8,122,0,8,58,0,9,212,82,7,19,0,8,106,0,8,42,0,9,180,0,8,10,0,8,138,0,8,74,0,9,244,80,7,5,0,8,86,0,8,22,192,8,0,83,7,51,0,8,118,0,8,54,0,9,204,81,7,15,0,8,102,0,8,38,0,9,172,0,8,6,0,8,134,0,8,70,0,9,236,80,7,9,0,8,94,0,8,30,0,9,156,84,7,99,0,8,126,0,8,62,0,9,220,82,7,27,0,8,110,0,8,46,0,9,188,0,8,14,0,8,142,0,8,78,0,9,252,96,7,256,0,8,81,0,8,17,85,8,131,82,7,31,0,8,113,0,8,49,0,9,194,80,7,10,0,8,97,0,8,33,0,9,162,0,8,1,0,8,129,0,8,65,0,9,226,80,7,6,0,8,89,0,8,25,0,9,146,83,7,59,0,8,121,0,8,57,0,9,210,81,7,17,0,8,105,0,8,41,0,9,178,0,8,9,0,8,137,0,8,73,0,9,242,80,7,4,0,8,85,0,8,21,80,8,258,83,7,43,0,8,117,0,8,53,0,9,202,81,7,13,0,8,101,0,8,37,0,9,170,0,8,5,0,8,133,0,8,69,0,9,234,80,7,8,0,8,93,0,8,29,0,9,154,84,7,83,0,8,125,0,8,61,0,9,218,82,7,23,0,8,109,0,8,45,0,9,186,0,8,13,0,8,141,0,8,77,0,9,250,80,7,3,0,8,83,0,8,19,85,8,195,83,7,35,0,8,115,0,8,51,0,9,198,81,7,11,0,8,99,0,8,35,0,9,166,0,8,3,0,8,131,0,8,67,0,9,230,80,7,7,0,8,91,0,8,27,0,9,150,84,7,67,0,8,123,0,8,59,0,9,214,82,7,19,0,8,107,0,8,43,0,9,182,0,8,11,0,8,139,0,8,75,0,9,246,80,7,5,0,8,87,0,8,23,192,8,0,83,7,51,0,8,119,0,8,55,0,9,206,81,7,15,0,8,103,0,8,39,0,9,174,0,8,7,0,8,135,0,8,71,0,9,238,80,7,9,0,8,95,0,8,31,0,9,158,84,7,99,0,8,127,0,8,63,0,9,222,82,7,27,0,8,111,0,8,47,0,9,190,0,8,15,0,8,143,0,8,79,0,9,254,96,7,256,0,8,80,0,8,16,84,8,115,82,7,31,0,8,112,0,8,48,0,9,193,80,7,10,0,8,96,0,8,32,0,9,161,0,8,0,0,8,128,0,8,64,0,9,225,80,7,6,0,8,88,0,8,24,0,9,145,83,7,59,0,8,120,0,8,56,0,9,209,81,7,17,0,8,104,0,8,40,0,9,177,0,8,8,0,8,136,0,8,72,0,9,241,80,7,4,0,8,84,0,8,20,85,8,227,83,7,43,0,8,116,0,8,52,0,9,201,81,7,13,0,8,100,0,8,36,0,9,169,0,8,4,0,8,132,0,8,68,0,9,233,80,7,8,0,8,92,0,8,28,0,9,153,84,7,83,0,8,124,0,8,60,0,9,217,82,7,23,0,8,108,0,8,44,0,9,185,0,8,12,0,8,140,0,8,76,0,9,249,80,7,3,0,8,82,0,8,18,85,8,163,83,7,35,0,8,114,0,8,50,0,9,197,81,7,11,0,8,98,0,8,34,0,9,165,0,8,2,0,8,130,0,8,66,0,9,229,80,7,7,0,8,90,0,8,26,0,9,149,84,7,67,0,8,122,0,8,58,0,9,213,82,7,19,0,8,106,0,8,42,0,9,181,0,8,10,0,8,138,0,8,74,0,9,245,80,7,5,0,8,86,0,8,22,192,8,0,83,7,51,0,8,118,0,8,54,0,9,205,81,7,15,0,8,102,0,8,38,0,9,173,0,8,6,0,8,134,0,8,70,0,9,237,80,7,9,0,8,94,0,8,30,0,9,157,84,7,99,0,8,126,0,8,62,0,9,221,82,7,27,0,8,110,0,8,46,0,9,189,0,8,14,0,8,142,0,8,78,0,9,253,96,7,256,0,8,81,0,8,17,85,8,131,82,7,31,0,8,113,0,8,49,0,9,195,80,7,10,0,8,97,0,8,33,0,9,163,0,8,1,0,8,129,0,8,65,0,9,227,80,7,6,0,8,89,0,8,25,0,9,147,83,7,59,0,8,121,0,8,57,0,9,211,81,7,17,0,8,105,0,8,41,0,9,179,0,8,9,0,8,137,0,8,73,0,9,243,80,7,4,0,8,85,0,8,21,80,8,258,83,7,43,0,8,117,0,8,53,0,9,203,81,7,13,0,8,101,0,8,37,0,9,171,0,8,5,0,8,133,0,8,69,0,9,235,80,7,8,0,8,93,0,8,29,0,9,155,84,7,83,0,8,125,0,8,61,0,9,219,82,7,23,0,8,109,0,8,45,0,9,187,0,8,13,0,8,141,0,8,77,0,9,251,80,7,3,0,8,83,0,8,19,85,8,195,83,7,35,0,8,115,0,8,51,0,9,199,81,7,11,0,8,99,0,8,35,0,9,167,0,8,3,0,8,131,0,8,67,0,9,231,80,7,7,0,8,91,0,8,27,0,9,151,84,7,67,0,8,123,0,8,59,0,9,215,82,7,19,0,8,107,0,8,43,0,9,183,0,8,11,0,8,139,0,8,75,0,9,247,80,7,5,0,8,87,0,8,23,192,8,0,83,7,51,0,8,119,0,8,55,0,9,207,81,7,15,0,8,103,0,8,39,0,9,175,0,8,7,0,8,135,0,8,71,0,9,239,80,7,9,0,8,95,0,8,31,0,9,159,84,7,99,0,8,127,0,8,63,0,9,223,82,7,27,0,8,111,0,8,47,0,9,191,0,8,15,0,8,143,0,8,79,0,9,255],ct=[80,5,1,87,5,257,83,5,17,91,5,4097,81,5,5,89,5,1025,85,5,65,93,5,16385,80,5,3,88,5,513,84,5,33,92,5,8193,82,5,9,90,5,2049,86,5,129,192,5,24577,80,5,2,87,5,385,83,5,25,91,5,6145,81,5,7,89,5,1537,85,5,97,93,5,24577,80,5,4,88,5,769,84,5,49,92,5,12289,82,5,13,90,5,3073,86,5,193,192,5,24577],ft=[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,0,0],at=[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,112,112],lt=[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],ut=[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];function wt(){let e,t,n,r,s,i;function o(e,t,o,c,f,a,l,u,w,h,d){let p,y,m,b,g,k,v,S,z,C,x,A,I,_,P;C=0,g=o;do{n[e[t+C]]++,C++,g--}while(0!==g);if(n[0]==o)return l[0]=-1,u[0]=0,0;for(S=u[0],k=1;15>=k&&0===n[k];k++);for(v=k,k>S&&(S=k),g=15;0!==g&&0===n[g];g--);for(m=g,S>g&&(S=g),u[0]=S,_=1<<k;g>k;k++,_<<=1)if(0>(_-=n[k]))return rt;if(0>(_-=n[g]))return rt;for(n[g]+=_,i[1]=k=0,C=1,I=2;0!==--g;)i[I]=k+=n[C],I++,C++;g=0,C=0;do{0!==(k=e[t+C])&&(d[i[k]++]=g),C++}while(++g<o);for(o=i[m],i[0]=g=0,C=0,b=-1,A=-S,s[0]=0,x=0,P=0;m>=v;v++)for(p=n[v];0!==p--;){for(;v>A+S;){if(b++,A+=S,P=m-A,P=P>S?S:P,(y=1<<(k=v-A))>p+1&&(y-=p+1,I=v,P>k))for(;++k<P&&(y<<=1)>n[++I];)y-=n[I];if(P=1<<k,h[0]+P>1440)return rt;s[b]=x=h[0],h[0]+=P,0!==b?(i[b]=g,r[0]=k,r[1]=S,k=g>>>A-S,r[2]=x-s[b-1]-k,w.set(r,3*(s[b-1]+k))):l[0]=x}for(r[1]=v-A,o>C?d[C]<c?(r[0]=256>d[C]?0:96,r[2]=d[C++]):(r[0]=a[d[C]-c]+16+64,r[2]=f[d[C++]-c]):r[0]=192,y=1<<v-A,k=g>>>A;P>k;k+=y)w.set(r,3*(x+k));for(k=1<<v-1;0!==(g&k);k>>>=1)g^=k;for(g^=k,z=(1<<A)-1;(g&z)!=i[b];)b--,A-=S,z=(1<<A)-1}return 0!==_&&1!=m?st:0}function c(o){let c;for(e||(e=[],t=[],n=new f(16),r=[],s=new f(15),i=new f(16)),t.length<o&&(t=[]),c=0;o>c;c++)t[c]=0;for(c=0;16>c;c++)n[c]=0;for(c=0;3>c;c++)r[c]=0;s.set(n.subarray(0,15),0),i.set(n.subarray(0,16),0)}this.st=(n,r,s,i,f)=>{let a;return c(19),e[0]=0,a=o(n,0,19,19,null,null,s,r,i,e,t),a==rt?f.Le="oversubscribed dynamic bit lengths tree":a!=st&&0!==r[0]||(f.Le="incomplete dynamic bit lengths tree",a=rt),a},this.it=(n,r,s,i,f,a,l,u,w)=>{let h;return c(288),e[0]=0,h=o(s,0,n,257,ft,at,a,i,u,e,t),0!=h||0===i[0]?(h==rt?w.Le="oversubscribed literal/length tree":-4!=h&&(w.Le="incomplete literal/length tree",h=rt),h):(c(288),h=o(s,n,r,0,lt,ut,l,f,u,e,t),0!=h||0===f[0]&&n>257?(h==rt?w.Le="oversubscribed distance tree":h==st?(w.Le="incomplete distance tree",h=rt):-4!=h&&(w.Le="empty distance tree with lengths",h=rt),h):0)}}function ht(){const e=this;let t,n,r,s,i=0,o=0,c=0,f=0,a=0,l=0,u=0,w=0,h=0,d=0;function p(e,t,n,r,s,i,o,c){let f,a,l,u,w,h,d,p,y,m,b,g,k,v,S,z;d=c.nt,p=c.We,w=o.ot,h=o.ct,y=o.write,m=y<o.read?o.read-y-1:o.end-y,b=it[e],g=it[t];do{for(;20>h;)p--,w|=(255&c.ft(d++))<<h,h+=8;if(f=w&b,a=n,l=r,z=3*(l+f),0!==(u=a[z]))for(;;){if(w>>=a[z+1],h-=a[z+1],16&u){for(u&=15,k=a[z+2]+(w&it[u]),w>>=u,h-=u;15>h;)p--,w|=(255&c.ft(d++))<<h,h+=8;for(f=w&g,a=s,l=i,z=3*(l+f),u=a[z];;){if(w>>=a[z+1],h-=a[z+1],16&u){for(u&=15;u>h;)p--,w|=(255&c.ft(d++))<<h,h+=8;if(v=a[z+2]+(w&it[u]),w>>=u,h-=u,m-=k,v>y){S=y-v;do{S+=o.end}while(0>S);if(u=o.end-S,k>u){if(k-=u,y-S>0&&u>y-S)do{o.lt[y++]=o.lt[S++]}while(0!==--u);else o.lt.set(o.lt.subarray(S,S+u),y),y+=u,S+=u,u=0;S=0}}else S=y-v,y-S>0&&2>y-S?(o.lt[y++]=o.lt[S++],o.lt[y++]=o.lt[S++],k-=2):(o.lt.set(o.lt.subarray(S,S+2),y),y+=2,S+=2,k-=2);if(y-S>0&&k>y-S)do{o.lt[y++]=o.lt[S++]}while(0!==--k);else o.lt.set(o.lt.subarray(S,S+k),y),y+=k,S+=k,k=0;break}if(64&u)return c.Le="invalid distance code",k=c.We-p,k=k>h>>3?h>>3:k,p+=k,d-=k,h-=k<<3,o.ot=w,o.ct=h,c.We=p,c.qe+=d-c.nt,c.nt=d,o.write=y,rt;f+=a[z+2],f+=w&it[u],z=3*(l+f),u=a[z]}break}if(64&u)return 32&u?(k=c.We-p,k=k>h>>3?h>>3:k,p+=k,d-=k,h-=k<<3,o.ot=w,o.ct=h,c.We=p,c.qe+=d-c.nt,c.nt=d,o.write=y,1):(c.Le="invalid literal/length code",k=c.We-p,k=k>h>>3?h>>3:k,p+=k,d-=k,h-=k<<3,o.ot=w,o.ct=h,c.We=p,c.qe+=d-c.nt,c.nt=d,o.write=y,rt);if(f+=a[z+2],f+=w&it[u],z=3*(l+f),0===(u=a[z])){w>>=a[z+1],h-=a[z+1],o.lt[y++]=a[z+2],m--;break}}else w>>=a[z+1],h-=a[z+1],o.lt[y++]=a[z+2],m--}while(m>=258&&p>=10);return k=c.We-p,k=k>h>>3?h>>3:k,p+=k,d-=k,h-=k<<3,o.ot=w,o.ct=h,c.We=p,c.qe+=d-c.nt,c.nt=d,o.write=y,0}e.init=(e,i,o,c,f,a)=>{t=0,u=e,w=i,r=o,h=c,s=f,d=a,n=null},e.ut=(e,y,m)=>{let b,g,k,v,S,z,C,x=0,A=0,I=0;for(I=y.nt,v=y.We,x=e.ot,A=e.ct,S=e.write,z=S<e.read?e.read-S-1:e.end-S;;)switch(t){case 0:if(z>=258&&v>=10&&(e.ot=x,e.ct=A,y.We=v,y.qe+=I-y.nt,y.nt=I,e.write=S,m=p(u,w,r,h,s,d,e,y),I=y.nt,v=y.We,x=e.ot,A=e.ct,S=e.write,z=S<e.read?e.read-S-1:e.end-S,0!=m)){t=1==m?7:9;break}c=u,n=r,o=h,t=1;case 1:for(b=c;b>A;){if(0===v)return e.ot=x,e.ct=A,y.We=v,y.qe+=I-y.nt,y.nt=I,e.write=S,e.wt(y,m);m=0,v--,x|=(255&y.ft(I++))<<A,A+=8}if(g=3*(o+(x&it[b])),x>>>=n[g+1],A-=n[g+1],k=n[g],0===k){f=n[g+2],t=6;break}if(16&k){a=15&k,i=n[g+2],t=2;break}if(!(64&k)){c=k,o=g/3+n[g+2];break}if(32&k){t=7;break}return t=9,y.Le="invalid literal/length code",m=rt,e.ot=x,e.ct=A,y.We=v,y.qe+=I-y.nt,y.nt=I,e.write=S,e.wt(y,m);case 2:for(b=a;b>A;){if(0===v)return e.ot=x,e.ct=A,y.We=v,y.qe+=I-y.nt,y.nt=I,e.write=S,e.wt(y,m);m=0,v--,x|=(255&y.ft(I++))<<A,A+=8}i+=x&it[b],x>>=b,A-=b,c=w,n=s,o=d,t=3;case 3:for(b=c;b>A;){if(0===v)return e.ot=x,e.ct=A,y.We=v,y.qe+=I-y.nt,y.nt=I,e.write=S,e.wt(y,m);m=0,v--,x|=(255&y.ft(I++))<<A,A+=8}if(g=3*(o+(x&it[b])),x>>=n[g+1],A-=n[g+1],k=n[g],16&k){a=15&k,l=n[g+2],t=4;break}if(!(64&k)){c=k,o=g/3+n[g+2];break}return t=9,y.Le="invalid distance code",m=rt,e.ot=x,e.ct=A,y.We=v,y.qe+=I-y.nt,y.nt=I,e.write=S,e.wt(y,m);case 4:for(b=a;b>A;){if(0===v)return e.ot=x,e.ct=A,y.We=v,y.qe+=I-y.nt,y.nt=I,e.write=S,e.wt(y,m);m=0,v--,x|=(255&y.ft(I++))<<A,A+=8}l+=x&it[b],x>>=b,A-=b,t=5;case 5:for(C=S-l;0>C;)C+=e.end;for(;0!==i;){if(0===z&&(S==e.end&&0!==e.read&&(S=0,z=S<e.read?e.read-S-1:e.end-S),0===z&&(e.write=S,m=e.wt(y,m),S=e.write,z=S<e.read?e.read-S-1:e.end-S,S==e.end&&0!==e.read&&(S=0,z=S<e.read?e.read-S-1:e.end-S),0===z)))return e.ot=x,e.ct=A,y.We=v,y.qe+=I-y.nt,y.nt=I,e.write=S,e.wt(y,m);e.lt[S++]=e.lt[C++],z--,C==e.end&&(C=0),i--}t=0;break;case 6:if(0===z&&(S==e.end&&0!==e.read&&(S=0,z=S<e.read?e.read-S-1:e.end-S),0===z&&(e.write=S,m=e.wt(y,m),S=e.write,z=S<e.read?e.read-S-1:e.end-S,S==e.end&&0!==e.read&&(S=0,z=S<e.read?e.read-S-1:e.end-S),0===z)))return e.ot=x,e.ct=A,y.We=v,y.qe+=I-y.nt,y.nt=I,e.write=S,e.wt(y,m);m=0,e.lt[S++]=f,z--,t=0;break;case 7:if(A>7&&(A-=8,v++,I--),e.write=S,m=e.wt(y,m),S=e.write,z=S<e.read?e.read-S-1:e.end-S,e.read!=e.write)return e.ot=x,e.ct=A,y.We=v,y.qe+=I-y.nt,y.nt=I,e.write=S,e.wt(y,m);t=8;case 8:return m=1,e.ot=x,e.ct=A,y.We=v,y.qe+=I-y.nt,y.nt=I,e.write=S,e.wt(y,m);case 9:return m=rt,e.ot=x,e.ct=A,y.We=v,y.qe+=I-y.nt,y.nt=I,e.write=S,e.wt(y,m);default:return m=nt,e.ot=x,e.ct=A,y.We=v,y.qe+=I-y.nt,y.nt=I,e.write=S,e.wt(y,m)}},e.ht=()=>{}}wt.dt=(e,t,n,r)=>(e[0]=9,t[0]=5,n[0]=ot,r[0]=ct,0);const dt=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];function pt(e,t){const n=this;let r,s=0,o=0,c=0,a=0;const l=[0],u=[0],w=new ht;let h=0,d=new f(4320);const p=new wt;n.ct=0,n.ot=0,n.lt=new i(t),n.end=t,n.read=0,n.write=0,n.reset=(e,t)=>{t&&(t[0]=0),6==s&&w.ht(e),s=0,n.ct=0,n.ot=0,n.read=n.write=0},n.reset(e,null),n.wt=(e,t)=>{let r,s,i;return s=e.rt,i=n.read,r=(i>n.write?n.end:n.write)-i,r>e.tt&&(r=e.tt),0!==r&&t==st&&(t=0),e.tt-=r,e.Ge+=r,e.$e.set(n.lt.subarray(i,i+r),s),s+=r,i+=r,i==n.end&&(i=0,n.write==n.end&&(n.write=0),r=n.write-i,r>e.tt&&(r=e.tt),0!==r&&t==st&&(t=0),e.tt-=r,e.Ge+=r,e.$e.set(n.lt.subarray(i,i+r),s),s+=r,i+=r),e.rt=s,n.read=i,t},n.ut=(e,t)=>{let i,f,y,m,b,g,k,v;for(m=e.nt,b=e.We,f=n.ot,y=n.ct,g=n.write,k=g<n.read?n.read-g-1:n.end-g;;){let S,z,C,x,A,I,_,P;switch(s){case 0:for(;3>y;){if(0===b)return n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);t=0,b--,f|=(255&e.ft(m++))<<y,y+=8}switch(i=7&f,h=1&i,i>>>1){case 0:f>>>=3,y-=3,i=7&y,f>>>=i,y-=i,s=1;break;case 1:S=[],z=[],C=[[]],x=[[]],wt.dt(S,z,C,x),w.init(S[0],z[0],C[0],0,x[0],0),f>>>=3,y-=3,s=6;break;case 2:f>>>=3,y-=3,s=3;break;case 3:return f>>>=3,y-=3,s=9,e.Le="invalid block type",t=rt,n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t)}break;case 1:for(;32>y;){if(0===b)return n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);t=0,b--,f|=(255&e.ft(m++))<<y,y+=8}if((~f>>>16&65535)!=(65535&f))return s=9,e.Le="invalid stored block lengths",t=rt,n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);o=65535&f,f=y=0,s=0!==o?2:0!==h?7:0;break;case 2:if(0===b)return n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);if(0===k&&(g==n.end&&0!==n.read&&(g=0,k=g<n.read?n.read-g-1:n.end-g),0===k&&(n.write=g,t=n.wt(e,t),g=n.write,k=g<n.read?n.read-g-1:n.end-g,g==n.end&&0!==n.read&&(g=0,k=g<n.read?n.read-g-1:n.end-g),0===k)))return n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);if(t=0,i=o,i>b&&(i=b),i>k&&(i=k),n.lt.set(e.je(m,i),g),m+=i,b-=i,g+=i,k-=i,0!==(o-=i))break;s=0!==h?7:0;break;case 3:for(;14>y;){if(0===b)return n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);t=0,b--,f|=(255&e.ft(m++))<<y,y+=8}if(c=i=16383&f,(31&i)>29||(i>>5&31)>29)return s=9,e.Le="too many length or distance symbols",t=rt,n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);if(i=258+(31&i)+(i>>5&31),!r||r.length<i)r=[];else for(v=0;i>v;v++)r[v]=0;f>>>=14,y-=14,a=0,s=4;case 4:for(;4+(c>>>10)>a;){for(;3>y;){if(0===b)return n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);t=0,b--,f|=(255&e.ft(m++))<<y,y+=8}r[dt[a++]]=7&f,f>>>=3,y-=3}for(;19>a;)r[dt[a++]]=0;if(l[0]=7,i=p.st(r,l,u,d,e),0!=i)return(t=i)==rt&&(r=null,s=9),n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);a=0,s=5;case 5:for(;i=c,258+(31&i)+(i>>5&31)>a;){let o,w;for(i=l[0];i>y;){if(0===b)return n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);t=0,b--,f|=(255&e.ft(m++))<<y,y+=8}if(i=d[3*(u[0]+(f&it[i]))+1],w=d[3*(u[0]+(f&it[i]))+2],16>w)f>>>=i,y-=i,r[a++]=w;else{for(v=18==w?7:w-14,o=18==w?11:3;i+v>y;){if(0===b)return n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);t=0,b--,f|=(255&e.ft(m++))<<y,y+=8}if(f>>>=i,y-=i,o+=f&it[v],f>>>=v,y-=v,v=a,i=c,v+o>258+(31&i)+(i>>5&31)||16==w&&1>v)return r=null,s=9,e.Le="invalid bit length repeat",t=rt,n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);w=16==w?r[v-1]:0;do{r[v++]=w}while(0!==--o);a=v}}if(u[0]=-1,A=[],I=[],_=[],P=[],A[0]=9,I[0]=6,i=c,i=p.it(257+(31&i),1+(i>>5&31),r,A,I,_,P,d,e),0!=i)return i==rt&&(r=null,s=9),t=i,n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);w.init(A[0],I[0],d,_[0],d,P[0]),s=6;case 6:if(n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,1!=(t=w.ut(n,e,t)))return n.wt(e,t);if(t=0,w.ht(e),m=e.nt,b=e.We,f=n.ot,y=n.ct,g=n.write,k=g<n.read?n.read-g-1:n.end-g,0===h){s=0;break}s=7;case 7:if(n.write=g,t=n.wt(e,t),g=n.write,k=g<n.read?n.read-g-1:n.end-g,n.read!=n.write)return n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);s=8;case 8:return t=1,n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);case 9:return t=rt,n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);default:return t=nt,n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t)}}},n.ht=e=>{n.reset(e,null),n.lt=null,d=null},n.yt=(e,t,r)=>{n.lt.set(e.subarray(t,t+r),0),n.read=n.write=r},n.bt=()=>1==s?1:0}const yt=13,mt=[0,0,255,255];function bt(){const e=this;function t(e){return e&&e.gt?(e.qe=e.Ge=0,e.Le=null,e.gt.mode=7,e.gt.kt.reset(e,null),0):nt}e.mode=0,e.method=0,e.vt=[0],e.St=0,e.marker=0,e.zt=0,e.Ct=t=>(e.kt&&e.kt.ht(t),e.kt=null,0),e.xt=(n,r)=>(n.Le=null,e.kt=null,8>r||r>15?(e.Ct(n),nt):(e.zt=r,n.gt.kt=new pt(n,1<<r),t(n),0)),e.At=(e,t)=>{let n,r;if(!e||!e.gt||!e.et)return nt;const s=e.gt;for(t=4==t?st:0,n=st;;)switch(s.mode){case 0:if(0===e.We)return n;if(n=t,e.We--,e.qe++,8!=(15&(s.method=e.ft(e.nt++)))){s.mode=yt,e.Le="unknown compression method",s.marker=5;break}if(8+(s.method>>4)>s.zt){s.mode=yt,e.Le="invalid win size",s.marker=5;break}s.mode=1;case 1:if(0===e.We)return n;if(n=t,e.We--,e.qe++,r=255&e.ft(e.nt++),((s.method<<8)+r)%31!=0){s.mode=yt,e.Le="incorrect header check",s.marker=5;break}if(!(32&r)){s.mode=7;break}s.mode=2;case 2:if(0===e.We)return n;n=t,e.We--,e.qe++,s.St=(255&e.ft(e.nt++))<<24&4278190080,s.mode=3;case 3:if(0===e.We)return n;n=t,e.We--,e.qe++,s.St+=(255&e.ft(e.nt++))<<16&16711680,s.mode=4;case 4:if(0===e.We)return n;n=t,e.We--,e.qe++,s.St+=(255&e.ft(e.nt++))<<8&65280,s.mode=5;case 5:return 0===e.We?n:(n=t,e.We--,e.qe++,s.St+=255&e.ft(e.nt++),s.mode=6,2);case 6:return s.mode=yt,e.Le="need dictionary",s.marker=0,nt;case 7:if(n=s.kt.ut(e,n),n==rt){s.mode=yt,s.marker=0;break}if(0==n&&(n=t),1!=n)return n;n=t,s.kt.reset(e,s.vt),s.mode=12;case 12:return e.We=0,1;case yt:return rt;default:return nt}},e.It=(e,t,n)=>{let r=0,s=n;if(!e||!e.gt||6!=e.gt.mode)return nt;const i=e.gt;return s<1<<i.zt||(s=(1<<i.zt)-1,r=n-s),i.kt.yt(t,r,s),i.mode=7,0},e._t=e=>{let n,r,s,i,o;if(!e||!e.gt)return nt;const c=e.gt;if(c.mode!=yt&&(c.mode=yt,c.marker=0),0===(n=e.We))return st;for(r=e.nt,s=c.marker;0!==n&&4>s;)e.ft(r)==mt[s]?s++:s=0!==e.ft(r)?0:4-s,r++,n--;return e.qe+=r-e.nt,e.nt=r,e.We=n,c.marker=s,4!=s?rt:(i=e.qe,o=e.Ge,t(e),e.qe=i,e.Ge=o,c.mode=7,0)},e.Pt=e=>e&&e.gt&&e.gt.kt?e.gt.kt.bt():nt}function gt(){}function kt(e){const t=new gt,n=e&&e.chunkSize?r.floor(2*e.chunkSize):131072,o=new i(n);let c=!1;t.xt(),t.$e=o,this.append=(e,r)=>{const f=[];let a,l,u=0,w=0,h=0;if(0!==e.length){t.nt=0,t.et=e,t.We=e.length;do{if(t.rt=0,t.tt=n,0!==t.We||c||(t.nt=0,c=!0),a=t.At(0),c&&a===st){if(0!==t.We)throw new s("inflating: bad input")}else if(0!==a&&1!==a)throw new s("inflating: "+t.Le);if((c||1===a)&&t.We===e.length)throw new s("inflating: bad input");t.rt&&(t.rt===n?f.push(new i(o)):f.push(o.subarray(0,t.rt))),h+=t.rt,r&&t.nt>0&&t.nt!=u&&(r(t.nt),u=t.nt)}while(t.We>0||0===t.tt);return f.length>1?(l=new i(h),f.forEach(e=>{l.set(e,w),w+=e.length})):l=f[0]?new i(f[0]):new i,l}},this.flush=()=>{t.Ct()}}gt.prototype={xt(e){const t=this;return t.gt=new bt,e||(e=15),t.gt.xt(t,e)},At(e){const t=this;return t.gt?t.gt.At(t,e):nt},Ct(){const e=this;if(!e.gt)return nt;const t=e.gt.Ct(e);return e.gt=null,t},_t(){const e=this;return e.gt?e.gt._t(e):nt},It(e,t){const n=this;return n.gt?n.gt.It(n,e,t):nt},ft(e){return this.et[e]},je(e,t){return this.et.subarray(e,e+t)}},self.initCodec=()=>{self.Deflate=tt,self.Inflate=kt};\n',r=()=>t.useDataURI?"data:text/javascript,"+encodeURIComponent(n):URL.createObjectURL(new Blob([n],{type:"text/javascript"}));e({workerScripts:{inflate:[r],deflate:[r]}});}
|
|
|
|
/*
|
|
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() {
|
|
return "application/octet-stream";
|
|
}
|
|
|
|
/* eslint-disable no-prototype-builtins */
|
|
|
|
|
|
function initShimAsyncCodec(library, options = {}, registerDataHandler) {
|
|
return {
|
|
Deflate: createCodecClass(library.Deflate, options.deflate, registerDataHandler),
|
|
Inflate: createCodecClass(library.Inflate, options.inflate, registerDataHandler)
|
|
};
|
|
}
|
|
|
|
function objectHasOwn(object, propertyName) {
|
|
return typeof Object.hasOwn === FUNCTION_TYPE ?
|
|
Object.hasOwn(object, propertyName) :
|
|
object.hasOwnProperty(propertyName);
|
|
}
|
|
|
|
function createCodecClass(constructor, constructorOptions, registerDataHandler) {
|
|
return class {
|
|
|
|
constructor(options) {
|
|
const codecAdapter = this;
|
|
const onData = data => {
|
|
if (codecAdapter.pendingData) {
|
|
const previousPendingData = codecAdapter.pendingData;
|
|
codecAdapter.pendingData = new Uint8Array(previousPendingData.length + data.length);
|
|
const { pendingData } = codecAdapter;
|
|
pendingData.set(previousPendingData, 0);
|
|
pendingData.set(data, previousPendingData.length);
|
|
} else {
|
|
codecAdapter.pendingData = new Uint8Array(data);
|
|
}
|
|
};
|
|
if (objectHasOwn(options, "level") && options.level === UNDEFINED_VALUE) {
|
|
delete options.level;
|
|
}
|
|
codecAdapter.codec = new constructor(Object.assign({}, constructorOptions, options));
|
|
registerDataHandler(codecAdapter.codec, onData);
|
|
}
|
|
append(data) {
|
|
this.codec.push(data);
|
|
return getResponse(this);
|
|
}
|
|
flush() {
|
|
this.codec.push(new Uint8Array(), true);
|
|
return getResponse(this);
|
|
}
|
|
};
|
|
|
|
function getResponse(codec) {
|
|
if (codec.pendingData) {
|
|
const output = codec.pendingData;
|
|
codec.pendingData = null;
|
|
return output;
|
|
} else {
|
|
return new Uint8Array();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
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 = [];
|
|
for (let i = 0; i < 256; i++) {
|
|
let t = i;
|
|
for (let j = 0; j < 8; j++) {
|
|
if (t & 1) {
|
|
t = (t >>> 1) ^ 0xEDB88320;
|
|
} else {
|
|
t = t >>> 1;
|
|
}
|
|
}
|
|
table[i] = t;
|
|
}
|
|
|
|
class Crc32 {
|
|
|
|
constructor(crc) {
|
|
this.crc = crc || -1;
|
|
}
|
|
|
|
append(data) {
|
|
let crc = this.crc | 0;
|
|
for (let offset = 0, length = data.length | 0; offset < length; offset++) {
|
|
crc = (crc >>> 8) ^ table[(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) {
|
|
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;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Random values
|
|
* @namespace
|
|
*/
|
|
const random = {
|
|
/**
|
|
* Generate random words with pure js, cryptographically not as strong & safe as native implementation.
|
|
* @param {TypedArray} typedArray The array to fill.
|
|
* @return {TypedArray} The random values.
|
|
*/
|
|
getRandomValues(typedArray) {
|
|
const words = new Uint32Array(typedArray.buffer);
|
|
const r = (m_w) => {
|
|
let m_z = 0x3ade68b1;
|
|
const mask = 0xffffffff;
|
|
return function () {
|
|
m_z = (0x9069 * (m_z & 0xFFFF) + (m_z >> 0x10)) & mask;
|
|
m_w = (0x4650 * (m_w & 0xFFFF) + (m_w >> 0x10)) & mask;
|
|
const result = ((((m_z << 0x10) + m_w) & mask) / 0x100000000) + .5;
|
|
return result * (Math.random() > .5 ? 1 : -1);
|
|
};
|
|
};
|
|
for (let i = 0, rcache; i < typedArray.length; i += 4) {
|
|
const _r = r((rcache || Math.random()) * 0x100000000);
|
|
rcache = _r() * 0x3ade67b7;
|
|
words[i / 4] = (_r() * 0x100000000) | 0;
|
|
}
|
|
return typedArray;
|
|
}
|
|
};
|
|
|
|
/** @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";
|
|
|
|
function getRandomValues(array) {
|
|
if (GET_RANDOM_VALUES_SUPPORTED) {
|
|
return crypto.getRandomValues(array);
|
|
} else {
|
|
return random.getRandomValues(array);
|
|
}
|
|
}
|
|
|
|
/*
|
|
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];
|
|
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, signed, encryptionStrength, checkPasswordOnly }) {
|
|
super({
|
|
start() {
|
|
Object.assign(this, {
|
|
ready: new Promise(resolve => this.resolveReady = resolve),
|
|
password: encodePassword(password, rawPassword),
|
|
signed,
|
|
strength: encryptionStrength - 1,
|
|
pending: new Uint8Array()
|
|
});
|
|
},
|
|
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 {
|
|
signed,
|
|
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 = new Uint8Array();
|
|
if (chunkToDecrypt.length) {
|
|
const encryptedChunk = toBits(codecBytes, chunkToDecrypt);
|
|
hmac.update(encryptedChunk);
|
|
const decryptedChunk = ctr.update(encryptedChunk);
|
|
decryptedChunkArray = fromBits(codecBytes, decryptedChunk);
|
|
}
|
|
if (signed) {
|
|
const signature = subarray(fromBits(codecBytes, hmac.digest()), 0, SIGNATURE_LENGTH);
|
|
for (let indexSignature = 0; indexSignature < SIGNATURE_LENGTH; indexSignature++) {
|
|
if (signature[indexSignature] != originalSignature[indexSignature]) {
|
|
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() {
|
|
Object.assign(this, {
|
|
ready: new Promise(resolve => this.resolveReady = resolve),
|
|
password: encodePassword(password, rawPassword),
|
|
strength: encryptionStrength - 1,
|
|
pending: new Uint8Array()
|
|
});
|
|
},
|
|
async transform(chunk, controller) {
|
|
const aesCrypto = this;
|
|
const {
|
|
password,
|
|
strength,
|
|
resolveReady,
|
|
ready
|
|
} = aesCrypto;
|
|
let preamble = new Uint8Array();
|
|
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 = new Uint8Array();
|
|
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 append(aesCrypto, input, output, paddingStart, paddingEnd, verifySignature) {
|
|
const {
|
|
ctr,
|
|
hmac,
|
|
pending
|
|
} = aesCrypto;
|
|
const inputLength = input.length - paddingEnd;
|
|
if (pending.length) {
|
|
input = concat(pending, input);
|
|
output = expand(output, 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);
|
|
// eslint-disable-next-line no-unused-vars
|
|
} 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);
|
|
// eslint-disable-next-line no-unused-vars
|
|
} 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, passwordVerification, checkPasswordOnly }) {
|
|
super({
|
|
start() {
|
|
Object.assign(this, {
|
|
password,
|
|
passwordVerification
|
|
});
|
|
createKeys(this, password);
|
|
},
|
|
transform(chunk, controller) {
|
|
const zipCrypto = this;
|
|
if (zipCrypto.password) {
|
|
const decryptedHeader = decrypt(zipCrypto, chunk.subarray(0, HEADER_LENGTH));
|
|
zipCrypto.password = null;
|
|
if (decryptedHeader[HEADER_LENGTH - 1] != zipCrypto.passwordVerification) {
|
|
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, passwordVerification }) {
|
|
super({
|
|
start() {
|
|
Object.assign(this, {
|
|
password,
|
|
passwordVerification
|
|
});
|
|
createKeys(this, password);
|
|
},
|
|
transform(chunk, controller) {
|
|
const zipCrypto = this;
|
|
let output;
|
|
let offset;
|
|
if (zipCrypto.password) {
|
|
zipCrypto.password = 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 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) {
|
|
const keys = [0x12345678, 0x23456789, 0x34567890];
|
|
Object.assign(target, {
|
|
keys,
|
|
crcKey0: new Crc32(keys[0]),
|
|
crcKey2: new Crc32(keys[2])
|
|
});
|
|
for (let index = 0; index < password.length; index++) {
|
|
updateKeys(target, password.charCodeAt(index));
|
|
}
|
|
}
|
|
|
|
function updateKeys(target, byte) {
|
|
let [key0, key1, key2] = target.keys;
|
|
target.crcKey0.append([byte]);
|
|
key0 = ~target.crcKey0.get();
|
|
key1 = getInt32(Math.imul(getInt32(key1 + getInt8(key0)), 134775813) + 1);
|
|
target.crcKey2.append([key1 >>> 24]);
|
|
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) 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_INVALID_UNCOMPRESSED_SIZE = "Invalid uncompressed size";
|
|
const COMPRESSION_FORMAT = "deflate-raw";
|
|
|
|
class DeflateStream extends TransformStream {
|
|
|
|
constructor(options, { chunkSize, CompressionStream, CompressionStreamNative }) {
|
|
super({});
|
|
const { compressed, encrypted, useCompressionStream, zipCrypto, signed, level } = options;
|
|
const stream = this;
|
|
let crc32Stream, encryptionStream;
|
|
let readable = super.readable;
|
|
if ((!encrypted || zipCrypto) && signed) {
|
|
crc32Stream = new Crc32Stream();
|
|
readable = pipeThrough(readable, crc32Stream);
|
|
}
|
|
if (compressed) {
|
|
readable = pipeThroughCommpressionStream(readable, useCompressionStream, { level, chunkSize }, CompressionStreamNative, 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 = new DataView(crc32Stream.value.buffer).getUint32(0);
|
|
}
|
|
stream.signature = signature;
|
|
});
|
|
}
|
|
}
|
|
|
|
class InflateStream extends TransformStream {
|
|
|
|
constructor(options, { chunkSize, DecompressionStream, DecompressionStreamNative }) {
|
|
super({});
|
|
const { zipCrypto, encrypted, signed, signature, compressed, useCompressionStream } = 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 }, DecompressionStreamNative, DecompressionStream);
|
|
}
|
|
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, CodecStreamNative, CodecStream) {
|
|
try {
|
|
const CompressionStream = useCompressionStream && CodecStreamNative ? CodecStreamNative : CodecStream;
|
|
readable = pipeThrough(readable, new CompressionStream(COMPRESSION_FORMAT, options));
|
|
// eslint-disable-next-line no-unused-vars
|
|
} catch (error) {
|
|
if (useCompressionStream) {
|
|
readable = pipeThrough(readable, new CodecStream(COMPRESSION_FORMAT, options));
|
|
} else {
|
|
throw error;
|
|
}
|
|
}
|
|
return readable;
|
|
}
|
|
|
|
function pipeThrough(readable, transformStream) {
|
|
return readable.pipeThrough(transformStream);
|
|
}
|
|
|
|
/*
|
|
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 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 && 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;
|
|
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;
|
|
}
|
|
if (chunk.length > chunkSize) {
|
|
controller.enqueue(chunk.slice(0, chunkSize));
|
|
transform(chunk.slice(chunkSize), controller);
|
|
} else {
|
|
pendingChunk = 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.
|
|
*/
|
|
|
|
|
|
let WEB_WORKERS_SUPPORTED = typeof Worker != UNDEFINED_TYPE;
|
|
|
|
class CodecWorker {
|
|
|
|
constructor(workerData, { readable, writable }, { options, config, streamOptions, useWebWorkers, transferStreams, scripts }, onTaskFinished) {
|
|
const { signal } = streamOptions;
|
|
Object.assign(workerData, {
|
|
busy: true,
|
|
readable: readable
|
|
.pipeThrough(new ChunkStream(config.chunkSize))
|
|
.pipeThrough(new ProgressWatcherStream(streamOptions), { signal }),
|
|
writable,
|
|
options: Object.assign({}, options),
|
|
scripts,
|
|
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() {
|
|
const { resolveTerminated } = workerData;
|
|
if (resolveTerminated) {
|
|
workerData.resolveTerminated = null;
|
|
workerData.terminated = true;
|
|
workerData.worker.terminate();
|
|
resolveTerminated();
|
|
}
|
|
workerData.busy = false;
|
|
onTaskFinished(workerData);
|
|
}
|
|
});
|
|
return (useWebWorkers && WEB_WORKERS_SUPPORTED ? 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);
|
|
// eslint-disable-next-line no-unused-vars
|
|
} catch (_) {
|
|
// ignored
|
|
}
|
|
}
|
|
|
|
function createWorkerInterface(workerData, config) {
|
|
return {
|
|
run: () => runWorker$1(workerData, config)
|
|
};
|
|
}
|
|
|
|
function createWebWorkerInterface(workerData, config) {
|
|
const { baseURL, chunkSize } = config;
|
|
if (!workerData.interface) {
|
|
let worker;
|
|
try {
|
|
worker = getWebWorker(workerData.scripts[0], baseURL, workerData);
|
|
// eslint-disable-next-line no-unused-vars
|
|
} catch (_) {
|
|
WEB_WORKERS_SUPPORTED = false;
|
|
return createWorkerInterface(workerData, config);
|
|
}
|
|
Object.assign(workerData, {
|
|
worker,
|
|
interface: {
|
|
run: () => runWebWorker(workerData, { chunkSize })
|
|
}
|
|
});
|
|
}
|
|
return workerData.interface;
|
|
}
|
|
|
|
async function runWorker$1({ options, readable, writable, onTaskFinished }, config) {
|
|
let codecStream;
|
|
try {
|
|
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, scripts } = workerData;
|
|
const { writable, closed } = watchClosedStream(workerData.writable);
|
|
const streamsTransferred = sendMessage({
|
|
type: MESSAGE_START,
|
|
scripts: scripts.slice(1),
|
|
options,
|
|
config,
|
|
readable,
|
|
writable
|
|
}, workerData);
|
|
if (!streamsTransferred) {
|
|
Object.assign(workerData, {
|
|
reader: readable.getReader(),
|
|
writer: writable.getWriter()
|
|
});
|
|
}
|
|
const resultValue = await result;
|
|
if (!streamsTransferred) {
|
|
await writable.getWriter().close();
|
|
}
|
|
await closed;
|
|
return resultValue;
|
|
}
|
|
|
|
function watchClosedStream(writableSource) {
|
|
let resolveStreamClosed;
|
|
const closed = new Promise(resolve => resolveStreamClosed = resolve);
|
|
const writable = new WritableStream({
|
|
async write(chunk) {
|
|
const writer = writableSource.getWriter();
|
|
await writer.ready;
|
|
await writer.write(chunk);
|
|
writer.releaseLock();
|
|
},
|
|
close() {
|
|
resolveStreamClosed();
|
|
},
|
|
abort(reason) {
|
|
const writer = writableSource.getWriter();
|
|
return writer.abort(reason);
|
|
}
|
|
});
|
|
return { writable, closed };
|
|
}
|
|
|
|
let classicWorkersSupported = true;
|
|
let transferStreamsSupported = true;
|
|
|
|
function getWebWorker(url, baseURL, workerData) {
|
|
const workerOptions = { type: "module" };
|
|
let scriptUrl, worker;
|
|
if (typeof url == FUNCTION_TYPE) {
|
|
url = url();
|
|
}
|
|
try {
|
|
scriptUrl = new URL(url, baseURL);
|
|
// eslint-disable-next-line no-unused-vars
|
|
} catch (_) {
|
|
scriptUrl = url;
|
|
}
|
|
if (classicWorkersSupported) {
|
|
try {
|
|
worker = new Worker(scriptUrl);
|
|
// eslint-disable-next-line no-unused-vars
|
|
} catch (_) {
|
|
classicWorkersSupported = false;
|
|
worker = new Worker(scriptUrl, workerOptions);
|
|
}
|
|
} else {
|
|
worker = new Worker(scriptUrl, workerOptions);
|
|
}
|
|
worker.addEventListener(MESSAGE_EVENT_TYPE, event => onMessage(event, workerData));
|
|
return worker;
|
|
}
|
|
|
|
function sendMessage(message, { worker, writer, onTaskFinished, transferStreams }) {
|
|
try {
|
|
const { value, readable, writable } = message;
|
|
const transferables = [];
|
|
if (value) {
|
|
if (value.byteLength < value.buffer.byteLength) {
|
|
message.value = value.buffer.slice(0, value.byteLength);
|
|
}
|
|
else {
|
|
message.value = value.buffer;
|
|
}
|
|
transferables.push(message.value);
|
|
}
|
|
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;
|
|
// eslint-disable-next-line no-unused-vars
|
|
} catch (_) {
|
|
transferStreamsSupported = false;
|
|
message.readable = message.writable = null;
|
|
worker.postMessage(message);
|
|
}
|
|
} else {
|
|
worker.postMessage(message);
|
|
}
|
|
} catch (error) {
|
|
if (writer) {
|
|
writer.releaseLock();
|
|
}
|
|
onTaskFinished();
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async function onMessage({ data }, workerData) {
|
|
const { type, value, messageId, result, error } = data;
|
|
const { reader, writer, resolveResult, rejectResult, onTaskFinished } = workerData;
|
|
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();
|
|
sendMessage({ type: MESSAGE_DATA, value, done, messageId }, workerData);
|
|
}
|
|
if (type == MESSAGE_DATA) {
|
|
await writer.ready;
|
|
await writer.write(new Uint8Array(value));
|
|
sendMessage({ type: MESSAGE_ACK_DATA, messageId }, workerData);
|
|
}
|
|
if (type == MESSAGE_CLOSE) {
|
|
close(null, result);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
sendMessage({ type: MESSAGE_CLOSE, messageId }, workerData);
|
|
close(error);
|
|
}
|
|
|
|
function close(error, result) {
|
|
if (error) {
|
|
rejectResult(error);
|
|
} else {
|
|
resolveResult(result);
|
|
}
|
|
if (writer) {
|
|
writer.releaseLock();
|
|
}
|
|
onTaskFinished();
|
|
}
|
|
}
|
|
|
|
/*
|
|
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.
|
|
*/
|
|
|
|
|
|
let pool = [];
|
|
const pendingRequests = [];
|
|
|
|
let indexWorker = 0;
|
|
|
|
async function runWorker(stream, workerOptions) {
|
|
const { options, config } = workerOptions;
|
|
const { transferStreams, useWebWorkers, useCompressionStream, codecType, compressed, signed, encrypted } = options;
|
|
const { workerScripts, 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.scripts = workerOptions.useWebWorkers && workerScripts ? workerScripts[codecType] : [];
|
|
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 }));
|
|
}
|
|
}
|
|
|
|
function onTaskFinished(workerData) {
|
|
if (pendingRequests.length) {
|
|
const [{ resolve, stream, workerOptions }] = pendingRequests.splice(0, 1);
|
|
resolve(new CodecWorker(workerData, stream, workerOptions, onTaskFinished));
|
|
} else if (workerData.worker) {
|
|
clearTerminateTimeout(workerData);
|
|
terminateWorker(workerData, workerOptions);
|
|
} else {
|
|
pool = pool.filter(data => data != workerData);
|
|
}
|
|
}
|
|
}
|
|
|
|
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();
|
|
// eslint-disable-next-line no-unused-vars
|
|
} 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) 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_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_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 PROPERTY_NAME_WRITABLE = "writable";
|
|
|
|
class Stream {
|
|
|
|
constructor() {
|
|
this.size = 0;
|
|
}
|
|
|
|
init() {
|
|
this.initialized = true;
|
|
}
|
|
}
|
|
|
|
class Reader extends Stream {
|
|
|
|
get readable() {
|
|
const reader = this;
|
|
const { chunkSize = DEFAULT_CHUNK_SIZE } = reader;
|
|
const readable = new ReadableStream({
|
|
start() {
|
|
this.chunkOffset = 0;
|
|
},
|
|
async pull(controller) {
|
|
const { offset = 0, size, diskNumberStart } = readable;
|
|
const { chunkOffset } = this;
|
|
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 {
|
|
this.chunkOffset += chunkSize;
|
|
}
|
|
}
|
|
});
|
|
return readable;
|
|
}
|
|
}
|
|
|
|
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 = 0;
|
|
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) {
|
|
if (dataString.length > 2) {
|
|
writer.data += btoa(dataString);
|
|
} else {
|
|
writer.pending += dataString;
|
|
}
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
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
|
|
} = options;
|
|
options = Object.assign({}, options);
|
|
delete options.preventHeadRequest;
|
|
delete options.useRangeHeader;
|
|
delete options.forceRangeRequests;
|
|
delete options.combineSizeEocd;
|
|
delete options.useXHR;
|
|
Object.assign(httpReader, {
|
|
url,
|
|
options,
|
|
preventHeadRequest,
|
|
useRangeHeader,
|
|
forceRangeRequests,
|
|
combineSizeEocd
|
|
});
|
|
}
|
|
|
|
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));
|
|
if (!forceRangeRequests && response.headers.get(HTTP_HEADER_ACCEPT_RANGES) != HTTP_RANGE_UNIT) {
|
|
throw new Error(ERR_HTTP_RANGE);
|
|
} else {
|
|
if (combineSizeEocd) {
|
|
httpReader.eocdCache = new Uint8Array(await response.arrayBuffer());
|
|
}
|
|
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) {
|
|
return new Uint8Array();
|
|
} 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);
|
|
}
|
|
return new Uint8Array(await response.arrayBuffer());
|
|
}
|
|
} 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());
|
|
if (!httpReader.size) {
|
|
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) {
|
|
httpReader.size = Number(contentLength);
|
|
} else {
|
|
await getRequestData(httpReader, httpReader.options);
|
|
}
|
|
}
|
|
}
|
|
|
|
async function sendFetchRequest(method, { options, url }, headers) {
|
|
const response = await fetch(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 ? 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 = {}) {
|
|
options.useRangeHeader = true;
|
|
super(url, options);
|
|
}
|
|
}
|
|
|
|
|
|
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 {
|
|
|
|
init(initSize = 0) {
|
|
Object.assign(this, {
|
|
offset: 0,
|
|
array: new Uint8Array(initSize)
|
|
});
|
|
super.init();
|
|
}
|
|
|
|
writeUint8Array(array) {
|
|
const writer = this;
|
|
if (writer.offset + array.length > writer.array.length) {
|
|
const previousArray = writer.array;
|
|
writer.array = new Uint8Array(previousArray.length + array.length);
|
|
writer.array.set(previousArray);
|
|
}
|
|
writer.array.set(array, writer.offset);
|
|
writer.offset += array.length;
|
|
}
|
|
|
|
getData() {
|
|
return this.array;
|
|
}
|
|
}
|
|
|
|
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 diskReader.init();
|
|
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 = new Uint8Array();
|
|
}
|
|
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;
|
|
await this.write(chunk.subarray(availableSize));
|
|
} else {
|
|
await writeChunk(chunk);
|
|
}
|
|
},
|
|
async close() {
|
|
await diskWriter.ready;
|
|
await closeDisk();
|
|
}
|
|
});
|
|
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.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,
|
|
maxSize: Infinity
|
|
});
|
|
}
|
|
return writer;
|
|
}
|
|
}
|
|
|
|
function isHttpFamily(url) {
|
|
const { baseURL } = getConfiguration();
|
|
const { protocol } = new URL(url, baseURL);
|
|
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);
|
|
}
|
|
|
|
const SplitZipReader = SplitDataReader;
|
|
const SplitZipWriter = SplitDataWriter;
|
|
|
|
/*
|
|
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).decode(value);
|
|
}
|
|
}
|
|
|
|
/*
|
|
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 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_ATTRIBUTE = "internalFileAttribute";
|
|
const PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES = "internalFileAttributes";
|
|
const PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTE = "externalFileAttribute";
|
|
const PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTES = "externalFileAttributes";
|
|
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_NAMES = [
|
|
PROPERTY_NAME_FILENAME, PROPERTY_NAME_RAW_FILENAME, PROPERTY_NAME_COMPRESSED_SIZE, PROPERTY_NAME_UNCOMPRESSED_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_OFFSET, PROPERTY_NAME_DISK_NUMBER_START,
|
|
PROPERTY_NAME_DISK_NUMBER_START, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTE, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES,
|
|
PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTE, PROPERTY_NAME_EXTERNAL_FILE_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,
|
|
"bitFlag", "filenameUTF8", "commentUTF8", "rawExtraField", "extraFieldZip64", "extraFieldUnicodePath", "extraFieldUnicodeComment",
|
|
"extraFieldAES", "extraFieldNTFS", "extraFieldExtendedTimestamp"];
|
|
|
|
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_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_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";
|
|
|
|
/*
|
|
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_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 = "Compression method not supported";
|
|
const ERR_SPLIT_ZIP_FILE = "Split zip file";
|
|
const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
|
|
const CHARSET_UTF8 = "utf-8";
|
|
const CHARSET_CP437 = "cp437";
|
|
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: []
|
|
});
|
|
}
|
|
|
|
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);
|
|
}
|
|
reader.chunkSize = getChunkSize(config);
|
|
const endOfDirectoryInfo = await seekSignature(reader, END_OF_CENTRAL_DIR_SIGNATURE, reader.size, END_OF_CENTRAL_DIR_LENGTH, MAX_16_BITS * 16);
|
|
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);
|
|
}
|
|
}
|
|
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;
|
|
let lastDiskNumber = getUint16(endOfDirectoryView, 4);
|
|
const expectedLastDiskNumber = reader.lastDiskNumber || 0;
|
|
let diskNumber = getUint16(endOfDirectoryView, 6);
|
|
let filesLength = getUint16(endOfDirectoryView, 8);
|
|
let prependedDataLength = 0;
|
|
let startOffset = 0;
|
|
if (directoryDataOffset == MAX_32_BITS || directoryDataLength == MAX_32_BITS || filesLength == MAX_16_BITS || diskNumber == MAX_16_BITS) {
|
|
const endOfDirectoryLocatorArray = await readUint8Array(reader, endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH, ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH);
|
|
const endOfDirectoryLocatorView = getDataView$1(endOfDirectoryLocatorArray);
|
|
if (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;
|
|
if (getUint32(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE && directoryDataOffset != expectedDirectoryDataOffset) {
|
|
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 (getUint32(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
|
|
throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
|
|
}
|
|
if (lastDiskNumber == MAX_16_BITS) {
|
|
lastDiskNumber = getUint32(endOfDirectoryView, 16);
|
|
}
|
|
if (diskNumber == MAX_16_BITS) {
|
|
diskNumber = getUint32(endOfDirectoryView, 20);
|
|
}
|
|
if (filesLength == MAX_16_BITS) {
|
|
filesLength = getBigUint64(endOfDirectoryView, 32);
|
|
}
|
|
if (directoryDataLength == MAX_32_BITS) {
|
|
directoryDataLength = getBigUint64(endOfDirectoryView, 40);
|
|
}
|
|
directoryDataOffset -= directoryDataLength;
|
|
}
|
|
}
|
|
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) {
|
|
const expectedDirectoryDataOffset = endOfDirectoryInfo.offset - directoryDataLength;
|
|
if (getUint32(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE && directoryDataOffset != expectedDirectoryDataOffset) {
|
|
const originalDirectoryDataOffset = directoryDataOffset;
|
|
directoryDataOffset = expectedDirectoryDataOffset;
|
|
if (directoryDataOffset > originalDirectoryDataOffset) {
|
|
prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
|
|
}
|
|
directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength, diskNumber);
|
|
directoryView = getDataView$1(directoryArray);
|
|
}
|
|
}
|
|
const expectedDirectoryDataLength = endOfDirectoryInfo.offset - directoryDataOffset - (reader.lastDiskOffset || 0);
|
|
if (directoryDataLength != expectedDirectoryDataLength && expectedDirectoryDataLength >= 0) {
|
|
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);
|
|
}
|
|
const filenameEncoding = getOptionValue$1(zipReader, options, OPTION_FILENAME_ENCODING);
|
|
const commentEncoding = getOptionValue$1(zipReader, options, OPTION_COMMENT_ENCODING);
|
|
for (let indexFile = 0; indexFile < filesLength; indexFile++) {
|
|
const fileEntry = new ZipEntry$1(reader, config, zipReader.options);
|
|
if (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 + 46;
|
|
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 directory =
|
|
(msDosCompatible && ((getUint8(directoryView, offset + 38) & FILE_ATTR_MSDOS_DIR_MASK) == FILE_ATTR_MSDOS_DIR_MASK)) ||
|
|
(unixCompatible && (((externalFileAttributes >> 16) & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR)) ||
|
|
(rawFilename.length && rawFilename[rawFilename.length - 1] == DIRECTORY_SIGNATURE.charCodeAt(0));
|
|
const executable = (unixCompatible && (((externalFileAttributes >> 16) & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0));
|
|
const offsetFileEntry = getUint32(directoryView, offset + 42) + prependedDataLength;
|
|
Object.assign(fileEntry, {
|
|
versionMadeBy,
|
|
msDosCompatible,
|
|
compressedSize: 0,
|
|
uncompressedSize: 0,
|
|
commentLength,
|
|
directory,
|
|
offset: offsetFileEntry,
|
|
diskNumberStart: getUint16(directoryView, offset + 34),
|
|
internalFileAttributes: getUint16(directoryView, offset + 36),
|
|
externalFileAttributes,
|
|
rawFilename,
|
|
filenameUTF8,
|
|
commentUTF8,
|
|
rawExtraField: directoryArray.subarray(extraFieldOffset, commentOffset),
|
|
executable
|
|
});
|
|
fileEntry.internalFileAttribute = fileEntry.internalFileAttributes;
|
|
fileEntry.externalFileAttribute = fileEntry.externalFileAttributes;
|
|
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, {
|
|
rawComment,
|
|
filename,
|
|
comment,
|
|
directory: directory || filename.endsWith(DIRECTORY_SIGNATURE)
|
|
});
|
|
startOffset = Math.max(offsetFileEntry, startOffset);
|
|
readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
|
|
fileEntry.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));
|
|
// eslint-disable-next-line no-unused-vars
|
|
} catch (_) {
|
|
// ignored
|
|
}
|
|
}
|
|
yield entry;
|
|
}
|
|
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) : new Uint8Array();
|
|
}
|
|
zipReader.comment = commentLength ? await readUint8Array(reader, commentOffset + END_OF_CENTRAL_DIR_LENGTH, commentLength) : new Uint8Array();
|
|
if (extractAppendedData) {
|
|
zipReader.appendedData = appendedDataOffset < reader.size ? await readUint8Array(reader, appendedDataOffset, reader.size - appendedDataOffset) : new Uint8Array();
|
|
}
|
|
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) {
|
|
value.getData(writable);
|
|
return readable;
|
|
}
|
|
})()
|
|
};
|
|
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,
|
|
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);
|
|
}
|
|
}
|
|
if ((compressionMethod != COMPRESSION_METHOD_STORE && compressionMethod != COMPRESSION_METHOD_DEFLATE) && !passThrough) {
|
|
throw new Error(ERR_UNSUPPORTED_COMPRESSION);
|
|
}
|
|
if (getUint32(dataView, 0) != LOCAL_FILE_HEADER_SIGNATURE) {
|
|
throw new Error(ERR_LOCAL_FILE_HEADER_NOT_FOUND);
|
|
}
|
|
readCommonHeader(localDirectory, dataView, 4);
|
|
const {
|
|
extraFieldLength,
|
|
filenameLength,
|
|
lastAccessDate,
|
|
creationDate
|
|
} = localDirectory;
|
|
localDirectory.rawExtraField = extraFieldLength ?
|
|
await readUint8Array(reader, offset + HEADER_SIZE + filenameLength, extraFieldLength, diskNumberStart) :
|
|
new Uint8Array();
|
|
readCommonFooter(zipEntry, localDirectory, dataView, 4, true);
|
|
Object.assign(fileEntry, { lastAccessDate, 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.readable;
|
|
Object.assign(readable, {
|
|
diskNumberStart,
|
|
offset: dataOffset,
|
|
size
|
|
});
|
|
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 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) & 0xFF) : ((signature >>> 24) & 0xFF)),
|
|
outputSize: uncompressedSize,
|
|
signature,
|
|
compressed: compressionMethod != 0 && !passThrough,
|
|
encrypted: zipEntry.encrypted && !passThrough,
|
|
useWebWorkers: getOptionValue$1(zipEntry, options, OPTION_USE_WEB_WORKERS),
|
|
useCompressionStream: getOptionValue$1(zipEntry, options, OPTION_USE_COMPRESSION_STREAM),
|
|
transferStreams: getOptionValue$1(zipEntry, options, OPTION_TRANSFER_STREAMS),
|
|
checkPasswordOnly
|
|
},
|
|
config,
|
|
streamOptions: { signal, size, onstart, onprogress, onend }
|
|
};
|
|
if (checkOverlappingEntry) {
|
|
await detectOverlappingEntry({
|
|
reader,
|
|
fileEntry,
|
|
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;
|
|
}
|
|
// eslint-disable-next-line no-unused-vars
|
|
} 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) {
|
|
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 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);
|
|
for (let indexMissingProperty = 0, offset = 0; indexMissingProperty < missingProperties.length; indexMissingProperty++) {
|
|
const [propertyName, max] = missingProperties[indexMissingProperty];
|
|
if (directory[propertyName] == max) {
|
|
const extraction = ZIP64_EXTRACTION[max];
|
|
directory[propertyName] = extraFieldZip64[propertyName] = extraction.getValue(extraFieldView, offset);
|
|
offset += extraction.bytes;
|
|
} else if (extraFieldZip64[propertyName]) {
|
|
throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
|
|
}
|
|
}
|
|
}
|
|
|
|
function readExtraFieldUnicode(extraFieldUnicode, propertyName, rawPropertyName, directory, fileEntry) {
|
|
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 + "UTF8"] = 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;
|
|
}
|
|
// eslint-disable-next-line no-unused-vars
|
|
} 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);
|
|
}
|
|
// eslint-disable-next-line no-unused-vars
|
|
} catch (_) {
|
|
// ignored
|
|
}
|
|
}
|
|
|
|
function readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory, localDirectory) {
|
|
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 * 1000);
|
|
const rawPropertyName = timeRawProperties[indexProperty];
|
|
extraFieldExtendedTimestamp[rawPropertyName] = time;
|
|
}
|
|
offset += 4;
|
|
});
|
|
}
|
|
|
|
async function detectOverlappingEntry({
|
|
reader,
|
|
fileEntry,
|
|
offset,
|
|
diskNumberStart,
|
|
signature,
|
|
compressedSize,
|
|
uncompressedSize,
|
|
dataOffset,
|
|
dataDescriptor,
|
|
extraFieldZip64,
|
|
readRanges
|
|
}) {
|
|
let diskOffset = 0;
|
|
if (diskNumberStart) {
|
|
for (let indexReader = 0; indexReader < diskNumberStart; indexReader++) {
|
|
const diskReader = reader.readers[indexReader];
|
|
diskOffset += diskReader.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 = 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 otherRange of readRanges) {
|
|
if (otherRange.fileEntry != fileEntry && range.start >= otherRange.start && range.start < otherRange.end) {
|
|
const error = new Error(ERR_OVERLAPPING_ENTRY);
|
|
error.overlappingEntry = otherRange.fileEntry;
|
|
throw error;
|
|
}
|
|
}
|
|
readRanges.push(range);
|
|
}
|
|
|
|
async function seekSignature(reader, signature, startOffset, minimumBytes, maximumLength) {
|
|
const signatureArray = new Uint8Array(4);
|
|
const signatureView = getDataView$1(signatureArray);
|
|
setUint32$1(signatureView, 0, signature);
|
|
const maximumBytes = minimumBytes + maximumLength;
|
|
return (await seek(minimumBytes)) || await seek(Math.min(maximumBytes, startOffset));
|
|
|
|
async function seek(length) {
|
|
const offset = startOffset - length;
|
|
const bytes = await readUint8Array(reader, offset, length);
|
|
for (let indexByte = bytes.length - minimumBytes; indexByte >= 0; indexByte--) {
|
|
if (bytes[indexByte] == signatureArray[0] && bytes[indexByte + 1] == signatureArray[1] &&
|
|
bytes[indexByte + 2] == signatureArray[2] && bytes[indexByte + 3] == signatureArray[3]) {
|
|
return {
|
|
offset: offset + indexByte,
|
|
buffer: bytes.slice(indexByte, indexByte + minimumBytes).buffer
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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 & 0x0000ffff;
|
|
try {
|
|
return new Date(1980 + ((date & 0xFE00) >> 9), ((date & 0x01E0) >> 5) - 1, date & 0x001F, (time & 0xF800) >> 11, (time & 0x07E0) >> 5, (time & 0x001F) * 2, 0);
|
|
// eslint-disable-next-line no-unused-vars
|
|
} 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 setUint32$1(view, offset, value) {
|
|
view.setUint32(offset, value, true);
|
|
}
|
|
|
|
function getDataView$1(array) {
|
|
return new DataView(array.buffer);
|
|
}
|
|
|
|
/*
|
|
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_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_FORMAT = "Zip64 is not supported (make sure 'keepOrder' is set to 'true')";
|
|
const ERR_UNDEFINED_UNCOMPRESSED_SIZE = "Undefined uncompressed size";
|
|
const ERR_ZIP_NOT_EMPTY = "Zip file not empty";
|
|
|
|
const EXTRAFIELD_DATA_AES = new Uint8Array([0x07, 0x00, 0x02, 0x00, 0x41, 0x45, 0x03, 0x00, 0x00]);
|
|
|
|
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 &&
|
|
writer.maxSize !== UNDEFINED_VALUE && writer.maxSize > 0 && writer.maxSize !== Infinity;
|
|
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],
|
|
pendingEntriesSize: 0,
|
|
pendingAddFileCalls: new Set(),
|
|
bufferedWrites: 0
|
|
});
|
|
}
|
|
|
|
async prependZip(reader) {
|
|
if (this.filenames.size) {
|
|
throw new Error(ERR_ZIP_NOT_EMPTY);
|
|
}
|
|
reader = new GenericReader(reader);
|
|
const zipReader = new ZipReader(reader.readable);
|
|
const entries = await zipReader.getEntries();
|
|
await zipReader.close();
|
|
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,
|
|
compressionMethod,
|
|
lastModDate,
|
|
lastAccessDate,
|
|
creationDate,
|
|
rawFilename,
|
|
bitFlag,
|
|
encrypted,
|
|
uncompressedSize,
|
|
compressedSize,
|
|
diskOffset,
|
|
diskNumber,
|
|
zip64
|
|
} = entry;
|
|
let {
|
|
rawExtraFieldZip64,
|
|
rawExtraFieldAES,
|
|
rawExtraFieldExtendedTimestamp,
|
|
rawExtraFieldNTFS,
|
|
rawExtraField
|
|
} = entry;
|
|
const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
|
|
rawExtraFieldZip64 = rawExtraFieldZip64 || new Uint8Array();
|
|
rawExtraFieldAES = rawExtraFieldAES || new Uint8Array();
|
|
rawExtraFieldExtendedTimestamp = rawExtraFieldExtendedTimestamp || new Uint8Array();
|
|
rawExtraFieldNTFS = rawExtraFieldNTFS || new Uint8Array();
|
|
rawExtraField = rawExtraField || new Uint8Array();
|
|
const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraField);
|
|
const zip64UncompressedSize = zip64 && uncompressedSize > MAX_32_BITS;
|
|
const zip64CompressedSize = zip64 && compressedSize > MAX_32_BITS;
|
|
const {
|
|
headerArray,
|
|
headerView
|
|
} = getHeaderArrayData({
|
|
version,
|
|
bitFlag: getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod),
|
|
compressionMethod,
|
|
uncompressedSize,
|
|
compressedSize,
|
|
lastModDate,
|
|
rawFilename,
|
|
zip64CompressedSize,
|
|
zip64UncompressedSize,
|
|
extraFieldLength
|
|
});
|
|
Object.assign(entry, {
|
|
zip64UncompressedSize,
|
|
zip64CompressedSize,
|
|
zip64Offset: zip64 && this.offset - diskOffset > MAX_32_BITS,
|
|
zip64DiskNumberStart: zip64 && diskNumber > MAX_16_BITS,
|
|
rawExtraFieldZip64,
|
|
rawExtraFieldAES,
|
|
rawExtraFieldExtendedTimestamp,
|
|
rawExtraFieldNTFS,
|
|
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;
|
|
const {
|
|
pendingAddFileCalls,
|
|
config
|
|
} = zipWriter;
|
|
if (workers < config.maxWorkers) {
|
|
workers++;
|
|
} else {
|
|
await new Promise(resolve => pendingEntries.push(resolve));
|
|
}
|
|
let promiseAddFile;
|
|
try {
|
|
name = name.trim();
|
|
if (zipWriter.filenames.has(name)) {
|
|
throw new Error(ERR_DUPLICATED_NAME);
|
|
}
|
|
zipWriter.filenames.add(name);
|
|
promiseAddFile = addFile(zipWriter, name, reader, options);
|
|
pendingAddFileCalls.add(promiseAddFile);
|
|
return await promiseAddFile;
|
|
} catch (error) {
|
|
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 = new Uint8Array(), options = {}) {
|
|
const zipWriter = this;
|
|
const { pendingAddFileCalls, writer } = this;
|
|
const { writable } = writer;
|
|
while (pendingAddFileCalls.size) {
|
|
await Promise.allSettled(Array.from(pendingAddFileCalls));
|
|
}
|
|
await closeFile(this, 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);
|
|
}
|
|
|
|
transform(path) {
|
|
const { readable, writable } = new TransformStream({
|
|
flush: () => { this.zipWriter.close(); }
|
|
});
|
|
this.zipWriter.add(path, readable);
|
|
return { readable: this.readable, writable };
|
|
}
|
|
|
|
writable(path) {
|
|
const { readable, writable } = new TransformStream();
|
|
this.zipWriter.add(path, readable);
|
|
return writable;
|
|
}
|
|
|
|
close(comment = undefined, options = {}) {
|
|
return this.zipWriter.close(comment, options);
|
|
}
|
|
}
|
|
|
|
async function addFile(zipWriter, name, reader, options) {
|
|
name = name.trim();
|
|
const msDosCompatible = getOptionValue(zipWriter, options, PROPERTY_NAME_MS_DOS_COMPATIBLE);
|
|
const versionMadeBy = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION_MADE_BY, msDosCompatible ? 20 : 768);
|
|
const executable = getOptionValue(zipWriter, options, PROPERTY_NAME_EXECUTABLE);
|
|
if (versionMadeBy > MAX_16_BITS) {
|
|
throw new Error(ERR_INVALID_VERSION);
|
|
}
|
|
let externalFileAttributes = getOptionValue(zipWriter, options, PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTES, 0);
|
|
if (externalFileAttributes === 0) {
|
|
externalFileAttributes = getOptionValue(zipWriter, options, PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTE, 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;
|
|
}
|
|
}
|
|
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);
|
|
let internalFileAttributes = getOptionValue(zipWriter, options, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES, 0);
|
|
if (internalFileAttributes === 0) {
|
|
internalFileAttributes = getOptionValue(zipWriter, options, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTE, 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 level = getOptionValue(zipWriter, options, OPTION_LEVEL);
|
|
const useWebWorkers = getOptionValue(zipWriter, options, OPTION_USE_WEB_WORKERS);
|
|
const bufferedWrite = getOptionValue(zipWriter, options, OPTION_BUFFERED_WRITE);
|
|
const dataDescriptorSignature = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR_SIGNATURE, false);
|
|
const signal = getOptionValue(zipWriter, options, OPTION_SIGNAL);
|
|
const useUnicodeFileNames = getOptionValue(zipWriter, options, OPTION_USE_UNICODE_FILE_NAMES, true);
|
|
const useCompressionStream = getOptionValue(zipWriter, options, OPTION_USE_COMPRESSION_STREAM);
|
|
const compressionMethod = getOptionValue(zipWriter, options, PROPERTY_NAME_COMPRESSION_METHOD);
|
|
let dataDescriptor = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR);
|
|
if (bufferedWrite && dataDescriptor === UNDEFINED_VALUE) {
|
|
dataDescriptor = false;
|
|
}
|
|
if (dataDescriptor === UNDEFINED_VALUE || zipCrypto) {
|
|
dataDescriptor = true;
|
|
}
|
|
let 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 = new Uint8Array();
|
|
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);
|
|
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);
|
|
}
|
|
arraySet(rawExtraField, new Uint16Array([type]), offset);
|
|
arraySet(rawExtraField, new Uint16Array([getLength(data)]), offset + 2);
|
|
arraySet(rawExtraField, data, offset + 4);
|
|
offset += 4 + getLength(data);
|
|
});
|
|
}
|
|
let maximumCompressedSize = 0;
|
|
let maximumEntrySize = 0;
|
|
let uncompressedSize = 0;
|
|
if (passThrough) {
|
|
uncompressedSize = options[PROPERTY_NAME_UNCOMPRESSED_SIZE];
|
|
if (uncompressedSize === UNDEFINED_VALUE) {
|
|
throw new Error(ERR_UNDEFINED_UNCOMPRESSED_SIZE);
|
|
}
|
|
}
|
|
const zip64Enabled = zip64 === true;
|
|
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);
|
|
}
|
|
} else {
|
|
options.uncompressedSize = uncompressedSize;
|
|
maximumCompressedSize = getMaximumCompressedSize(uncompressedSize);
|
|
}
|
|
}
|
|
const { diskOffset, diskNumber, maxSize } = zipWriter.writer;
|
|
const zip64UncompressedSize = zip64Enabled || uncompressedSize > MAX_32_BITS;
|
|
const zip64CompressedSize = zip64Enabled || maximumCompressedSize > MAX_32_BITS;
|
|
const zip64Offset = zip64Enabled || zipWriter.offset + zipWriter.pendingEntriesSize - diskOffset > MAX_32_BITS;
|
|
const supportZip64SplitFile = getOptionValue(zipWriter, options, OPTION_SUPPORT_ZIP64_SPLIT_FILE, true);
|
|
const zip64DiskNumberStart = (supportZip64SplitFile && zip64Enabled) || diskNumber + Math.ceil(zipWriter.pendingEntriesSize / maxSize) > MAX_16_BITS;
|
|
if (zip64Offset || zip64UncompressedSize || zip64CompressedSize || zip64DiskNumberStart) {
|
|
if (zip64 === false || !keepOrder) {
|
|
throw new Error(ERR_UNSUPPORTED_FORMAT);
|
|
} else {
|
|
zip64 = true;
|
|
}
|
|
}
|
|
zip64 = zip64 || false;
|
|
const encrypted = getOptionValue(zipWriter, options, PROPERTY_NAME_ENCRYPTED);
|
|
options = Object.assign({}, options, {
|
|
rawFilename,
|
|
rawComment,
|
|
version,
|
|
versionMadeBy,
|
|
lastModDate,
|
|
lastAccessDate,
|
|
creationDate,
|
|
rawExtraField,
|
|
zip64,
|
|
zip64UncompressedSize,
|
|
zip64CompressedSize,
|
|
zip64Offset,
|
|
zip64DiskNumberStart,
|
|
password,
|
|
rawPassword,
|
|
level: !useCompressionStream && (zipWriter.config.CompressionStream === UNDEFINED_VALUE && zipWriter.config.CompressionStreamNative === UNDEFINED_VALUE) ? 0 : level,
|
|
useWebWorkers,
|
|
encryptionStrength,
|
|
extendedTimestamp,
|
|
zipCrypto,
|
|
bufferedWrite,
|
|
keepOrder,
|
|
useUnicodeFileNames,
|
|
dataDescriptor,
|
|
dataDescriptorSignature,
|
|
signal,
|
|
msDosCompatible,
|
|
internalFileAttribute: internalFileAttributes,
|
|
internalFileAttributes,
|
|
externalFileAttribute: externalFileAttributes,
|
|
externalFileAttributes,
|
|
useCompressionStream,
|
|
passThrough,
|
|
encrypted: Boolean((password && getLength(password)) || (rawPassword && getLength(rawPassword))) || (passThrough && encrypted),
|
|
signature: options[PROPERTY_NAME_SIGNATURE],
|
|
compressionMethod,
|
|
uncompressedSize,
|
|
offset: zipWriter.offset - diskOffset,
|
|
diskNumberStart: diskNumber
|
|
});
|
|
const headerInfo = getHeaderInfo(options);
|
|
const dataDescriptorInfo = getDataDescriptorInfo(options);
|
|
const metadataSize = getLength(headerInfo.localHeaderArray, dataDescriptorInfo.dataDescriptorArray);
|
|
maximumEntrySize = metadataSize + maximumCompressedSize;
|
|
if (zipWriter.options[OPTION_USDZ]) {
|
|
maximumEntrySize += maximumEntrySize + 64;
|
|
}
|
|
zipWriter.pendingEntriesSize += maximumEntrySize;
|
|
let fileEntry;
|
|
try {
|
|
fileEntry = await getFileEntry(zipWriter, name, reader, { headerInfo, dataDescriptorInfo, metadataSize }, options);
|
|
} finally {
|
|
zipWriter.pendingEntriesSize -= maximumEntrySize;
|
|
}
|
|
Object.assign(fileEntry, { name, comment, extraField });
|
|
return new Entry(fileEntry);
|
|
}
|
|
|
|
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 = Array.from(files.values()).pop();
|
|
let fileEntry = {};
|
|
let bufferedWrite;
|
|
let releaseLockWriter;
|
|
let releaseLockCurrentFileEntry;
|
|
let writingBufferedEntryData;
|
|
let writingEntryData;
|
|
let fileWriter;
|
|
let blobPromise;
|
|
files.set(name, fileEntry);
|
|
try {
|
|
let lockPreviousFileEntry;
|
|
if (keepOrder) {
|
|
lockPreviousFileEntry = previousFileEntry && previousFileEntry.lock;
|
|
requestLockCurrentFileEntry();
|
|
}
|
|
if ((options.bufferedWrite || zipWriter.writerLocked || (zipWriter.bufferedWrites && keepOrder) || !dataDescriptor) && !usdz) {
|
|
fileWriter = new TransformStream();
|
|
fileWriter.size = 0;
|
|
bufferedWrite = true;
|
|
zipWriter.bufferedWrites++;
|
|
await initStream(writer);
|
|
} else {
|
|
fileWriter = writer;
|
|
await requestLockWriter();
|
|
}
|
|
await initStream(fileWriter);
|
|
const { writable, 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) {
|
|
appendExtraFieldUSDZ(entryInfo, zipWriter.offset - diskOffset);
|
|
}
|
|
const {
|
|
localHeaderView,
|
|
localHeaderArray
|
|
} = headerInfo;
|
|
if (!bufferedWrite) {
|
|
await lockPreviousFileEntry;
|
|
await skipDiskIfNeeded(writable);
|
|
}
|
|
const { diskNumber } = writer;
|
|
writingEntryData = true;
|
|
fileEntry.diskNumberStart = diskNumber;
|
|
if (bufferedWrite) {
|
|
blobPromise = new Response(fileWriter.readable).blob();
|
|
} else {
|
|
await writeData(fileWriter, localHeaderArray);
|
|
}
|
|
fileEntry = await createFileEntry(reader, fileWriter, fileEntry, entryInfo, zipWriter.config, options);
|
|
const { zip64 } = fileEntry;
|
|
writingEntryData = false;
|
|
files.set(name, fileEntry);
|
|
fileEntry.filename = name;
|
|
if (bufferedWrite) {
|
|
const [blob] = await Promise.all([blobPromise, fileWriter.writable.getWriter().close(), lockPreviousFileEntry]);
|
|
await requestLockWriter();
|
|
writingBufferedEntryData = true;
|
|
fileEntry.diskNumberStart = writer.diskNumber;
|
|
fileEntry.offset = zipWriter.offset - writer.diskOffset;
|
|
if (zip64) {
|
|
updateZip64ExtraField(fileEntry);
|
|
}
|
|
updateLocalHeader(fileEntry, localHeaderView, options);
|
|
await skipDiskIfNeeded(writable);
|
|
await writeData(writer, localHeaderArray);
|
|
await blob.stream().pipeTo(writable, { preventClose: true, preventAbort: true, signal });
|
|
writer.size += fileWriter.size;
|
|
writingBufferedEntryData = false;
|
|
} else {
|
|
fileEntry.offset = zipWriter.offset - diskOffset;
|
|
if (zip64) {
|
|
updateZip64ExtraField(fileEntry);
|
|
}
|
|
}
|
|
if (fileEntry.offset > MAX_32_BITS && !zip64) {
|
|
throw new Error(ERR_UNSUPPORTED_FORMAT);
|
|
}
|
|
zipWriter.offset += fileEntry.size;
|
|
return fileEntry;
|
|
} catch (error) {
|
|
if ((bufferedWrite && writingBufferedEntryData) || (!bufferedWrite && writingEntryData)) {
|
|
zipWriter.hasCorruptedEntries = true;
|
|
if (error) {
|
|
try {
|
|
error.corruptedEntry = true;
|
|
// eslint-disable-next-line no-unused-vars
|
|
} catch (_) {
|
|
// ignored
|
|
}
|
|
}
|
|
if (bufferedWrite) {
|
|
zipWriter.offset += fileWriter.size;
|
|
} else {
|
|
zipWriter.offset = fileWriter.size;
|
|
}
|
|
}
|
|
files.delete(name);
|
|
throw error;
|
|
} finally {
|
|
if (bufferedWrite) {
|
|
zipWriter.bufferedWrites--;
|
|
}
|
|
if (releaseLockCurrentFileEntry) {
|
|
releaseLockCurrentFileEntry();
|
|
}
|
|
if (releaseLockWriter) {
|
|
releaseLockWriter();
|
|
}
|
|
}
|
|
|
|
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(writable) {
|
|
if (getLength(headerInfo.localHeaderArray) > writer.availableSize) {
|
|
writer.availableSize = 0;
|
|
await writeData(writable, new Uint8Array());
|
|
}
|
|
}
|
|
}
|
|
|
|
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,
|
|
rawExtraFieldAES
|
|
} = headerInfo;
|
|
const { dataDescriptorArray } = dataDescriptorInfo;
|
|
const {
|
|
rawFilename,
|
|
lastAccessDate,
|
|
creationDate,
|
|
password,
|
|
rawPassword,
|
|
level,
|
|
zip64,
|
|
zip64UncompressedSize,
|
|
zip64CompressedSize,
|
|
zip64Offset,
|
|
zip64DiskNumberStart,
|
|
zipCrypto,
|
|
dataDescriptor,
|
|
directory,
|
|
executable,
|
|
versionMadeBy,
|
|
rawComment,
|
|
rawExtraField,
|
|
useWebWorkers,
|
|
onstart,
|
|
onprogress,
|
|
onend,
|
|
signal,
|
|
encryptionStrength,
|
|
extendedTimestamp,
|
|
msDosCompatible,
|
|
internalFileAttributes,
|
|
externalFileAttributes,
|
|
useCompressionStream,
|
|
passThrough
|
|
} = options;
|
|
const fileEntry = {
|
|
lock,
|
|
versionMadeBy,
|
|
zip64,
|
|
directory: Boolean(directory),
|
|
executable: Boolean(executable),
|
|
filenameUTF8: true,
|
|
rawFilename,
|
|
commentUTF8: true,
|
|
rawComment,
|
|
rawExtraFieldZip64,
|
|
localExtraFieldZip64Length,
|
|
rawExtraFieldExtendedTimestamp,
|
|
rawExtraFieldNTFS,
|
|
rawExtraFieldAES,
|
|
rawExtraField,
|
|
extendedTimestamp,
|
|
msDosCompatible,
|
|
internalFileAttributes,
|
|
externalFileAttributes,
|
|
diskNumberStart
|
|
};
|
|
let {
|
|
signature,
|
|
uncompressedSize
|
|
} = options;
|
|
let compressedSize = 0;
|
|
if (!passThrough) {
|
|
uncompressedSize = 0;
|
|
}
|
|
const { writable } = writer;
|
|
if (reader) {
|
|
reader.chunkSize = getChunkSize(config);
|
|
const readable = reader.readable;
|
|
const size = reader.size;
|
|
const workerOptions = {
|
|
options: {
|
|
codecType: CODEC_DEFLATE,
|
|
level,
|
|
rawPassword,
|
|
password,
|
|
encryptionStrength,
|
|
zipCrypto: encrypted && zipCrypto,
|
|
passwordVerification: encrypted && zipCrypto && (rawLastModDate >> 8) & 0xFF,
|
|
signed: !passThrough,
|
|
compressed: compressed && !passThrough,
|
|
encrypted: encrypted && !passThrough,
|
|
useWebWorkers,
|
|
useCompressionStream,
|
|
transferStreams: false
|
|
},
|
|
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;
|
|
}
|
|
} 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,
|
|
zip64Offset,
|
|
zip64DiskNumberStart
|
|
});
|
|
return fileEntry;
|
|
}
|
|
|
|
function getHeaderInfo(options) {
|
|
const {
|
|
rawFilename,
|
|
lastModDate,
|
|
lastAccessDate,
|
|
creationDate,
|
|
level,
|
|
zip64,
|
|
zipCrypto,
|
|
useUnicodeFileNames,
|
|
dataDescriptor,
|
|
directory,
|
|
rawExtraField,
|
|
encryptionStrength,
|
|
extendedTimestamp,
|
|
passThrough,
|
|
encrypted,
|
|
zip64UncompressedSize,
|
|
zip64CompressedSize,
|
|
zip64Offset,
|
|
zip64DiskNumberStart,
|
|
uncompressedSize,
|
|
offset,
|
|
diskNumberStart
|
|
} = options;
|
|
let { version, compressionMethod } = options;
|
|
const compressed = !directory && (level > 0 || (level === UNDEFINED_VALUE && compressionMethod !== 0));
|
|
let rawExtraFieldZip64;
|
|
const uncompressedFile = passThrough || !compressed;
|
|
const zip64ExtraFieldComplete = zip64 && (options.bufferedWrite || ((!zip64UncompressedSize && !zip64CompressedSize) || uncompressedFile));
|
|
if (zip64) {
|
|
let rawExtraFieldZip64Length = 4;
|
|
if (zip64UncompressedSize) {
|
|
rawExtraFieldZip64Length += 8;
|
|
}
|
|
if (zip64CompressedSize) {
|
|
rawExtraFieldZip64Length += 8;
|
|
}
|
|
if (zip64Offset) {
|
|
rawExtraFieldZip64Length += 8;
|
|
}
|
|
if (zip64DiskNumberStart) {
|
|
rawExtraFieldZip64Length += 4;
|
|
}
|
|
rawExtraFieldZip64 = new Uint8Array(rawExtraFieldZip64Length);
|
|
const rawExtraFieldZip64View = getDataView(rawExtraFieldZip64);
|
|
setUint16(rawExtraFieldZip64View, 0, EXTRAFIELD_TYPE_ZIP64);
|
|
setUint16(rawExtraFieldZip64View, 2, getLength(rawExtraFieldZip64) - 4);
|
|
if (zip64ExtraFieldComplete) {
|
|
const rawExtraFieldZip64View = getDataView(rawExtraFieldZip64);
|
|
let rawExtraFieldZip64Offset = 4;
|
|
if (zip64UncompressedSize) {
|
|
setBigUint64(rawExtraFieldZip64View, rawExtraFieldZip64Offset, BigInt(uncompressedSize));
|
|
rawExtraFieldZip64Offset += 8;
|
|
}
|
|
if (zip64CompressedSize && uncompressedFile) {
|
|
setBigUint64(rawExtraFieldZip64View, rawExtraFieldZip64Offset, BigInt(uncompressedSize));
|
|
rawExtraFieldZip64Offset += 8;
|
|
}
|
|
if (zip64Offset) {
|
|
setBigUint64(rawExtraFieldZip64View, rawExtraFieldZip64Offset, BigInt(offset));
|
|
rawExtraFieldZip64Offset += 8;
|
|
}
|
|
if (zip64DiskNumberStart) {
|
|
setUint32(rawExtraFieldZip64View, rawExtraFieldZip64Offset, diskNumberStart);
|
|
rawExtraFieldZip64Offset += 4;
|
|
}
|
|
}
|
|
} else {
|
|
rawExtraFieldZip64 = new Uint8Array();
|
|
}
|
|
let rawExtraFieldAES;
|
|
if (encrypted && !zipCrypto) {
|
|
rawExtraFieldAES = new Uint8Array(getLength(EXTRAFIELD_DATA_AES) + 2);
|
|
const extraFieldAESView = getDataView(rawExtraFieldAES);
|
|
setUint16(extraFieldAESView, 0, EXTRAFIELD_TYPE_AES);
|
|
arraySet(rawExtraFieldAES, EXTRAFIELD_DATA_AES, 2);
|
|
setUint8(extraFieldAESView, 8, encryptionStrength);
|
|
} else {
|
|
rawExtraFieldAES = new Uint8Array();
|
|
}
|
|
let rawExtraFieldNTFS;
|
|
let rawExtraFieldExtendedTimestamp;
|
|
let extraFieldExtendedTimestampFlag;
|
|
if (extendedTimestamp) {
|
|
rawExtraFieldExtendedTimestamp = new Uint8Array(9 + (lastAccessDate ? 4 : 0) + (creationDate ? 4 : 0));
|
|
const extraFieldExtendedTimestampView = getDataView(rawExtraFieldExtendedTimestamp);
|
|
setUint16(extraFieldExtendedTimestampView, 0, EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
|
|
setUint16(extraFieldExtendedTimestampView, 2, getLength(rawExtraFieldExtendedTimestamp) - 4);
|
|
extraFieldExtendedTimestampFlag = 0x1 + (lastAccessDate ? 0x2 : 0) + (creationDate ? 0x4 : 0);
|
|
setUint8(extraFieldExtendedTimestampView, 4, extraFieldExtendedTimestampFlag);
|
|
let offset = 5;
|
|
setUint32(extraFieldExtendedTimestampView, offset, Math.floor(lastModDate.getTime() / 1000));
|
|
offset += 4;
|
|
if (lastAccessDate) {
|
|
setUint32(extraFieldExtendedTimestampView, offset, Math.floor(lastAccessDate.getTime() / 1000));
|
|
offset += 4;
|
|
}
|
|
if (creationDate) {
|
|
setUint32(extraFieldExtendedTimestampView, offset, Math.floor(creationDate.getTime() / 1000));
|
|
}
|
|
try {
|
|
rawExtraFieldNTFS = new Uint8Array(36);
|
|
const extraFieldNTFSView = getDataView(rawExtraFieldNTFS);
|
|
const lastModTimeNTFS = getTimeNTFS(lastModDate);
|
|
setUint16(extraFieldNTFSView, 0, EXTRAFIELD_TYPE_NTFS);
|
|
setUint16(extraFieldNTFSView, 2, 32);
|
|
setUint16(extraFieldNTFSView, 8, EXTRAFIELD_TYPE_NTFS_TAG1);
|
|
setUint16(extraFieldNTFSView, 10, 24);
|
|
setBigUint64(extraFieldNTFSView, 12, lastModTimeNTFS);
|
|
setBigUint64(extraFieldNTFSView, 20, getTimeNTFS(lastAccessDate) || lastModTimeNTFS);
|
|
setBigUint64(extraFieldNTFSView, 28, getTimeNTFS(creationDate) || lastModTimeNTFS);
|
|
// eslint-disable-next-line no-unused-vars
|
|
} catch (_) {
|
|
rawExtraFieldNTFS = new Uint8Array();
|
|
}
|
|
} else {
|
|
rawExtraFieldNTFS = rawExtraFieldExtendedTimestamp = new Uint8Array();
|
|
}
|
|
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 = zip64ExtraFieldComplete ? getLength(rawExtraFieldZip64) : 0;
|
|
const extraFieldLength = localExtraFieldZip64Length + getLength(rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraField);
|
|
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
|
|
});
|
|
let localHeaderOffset = HEADER_SIZE;
|
|
const localHeaderArray = new Uint8Array(localHeaderOffset + getLength(rawFilename) + extraFieldLength);
|
|
const localHeaderView = getDataView(localHeaderArray);
|
|
setUint32(localHeaderView, 0, LOCAL_FILE_HEADER_SIGNATURE);
|
|
arraySet(localHeaderArray, headerArray, 4);
|
|
arraySet(localHeaderArray, rawFilename, localHeaderOffset);
|
|
localHeaderOffset += getLength(rawFilename);
|
|
if (zip64ExtraFieldComplete) {
|
|
arraySet(localHeaderArray, rawExtraFieldZip64, localHeaderOffset);
|
|
}
|
|
localHeaderOffset += localExtraFieldZip64Length;
|
|
arraySet(localHeaderArray, rawExtraFieldAES, localHeaderOffset);
|
|
localHeaderOffset += getLength(rawExtraFieldAES);
|
|
arraySet(localHeaderArray, rawExtraFieldExtendedTimestamp, localHeaderOffset);
|
|
localHeaderOffset += getLength(rawExtraFieldExtendedTimestamp);
|
|
arraySet(localHeaderArray, rawExtraFieldNTFS, localHeaderOffset);
|
|
localHeaderOffset += getLength(rawExtraFieldNTFS);
|
|
arraySet(localHeaderArray, rawExtraField, localHeaderOffset);
|
|
if (dataDescriptor) {
|
|
setUint32(localHeaderView, HEADER_OFFSET_COMPRESSED_SIZE + 4, 0);
|
|
setUint32(localHeaderView, HEADER_OFFSET_UNCOMPRESSED_SIZE + 4, 0);
|
|
}
|
|
return {
|
|
localHeaderArray,
|
|
localHeaderView,
|
|
headerArray,
|
|
headerView,
|
|
lastModDate,
|
|
rawLastModDate,
|
|
encrypted,
|
|
compressed,
|
|
version,
|
|
compressionMethod,
|
|
extraFieldExtendedTimestampFlag,
|
|
rawExtraFieldZip64,
|
|
localExtraFieldZip64Length,
|
|
rawExtraFieldExtendedTimestamp,
|
|
rawExtraFieldNTFS,
|
|
rawExtraFieldAES,
|
|
extraFieldLength
|
|
};
|
|
}
|
|
|
|
function appendExtraFieldUSDZ(entryInfo, zipWriterOffset) {
|
|
const { headerInfo } = entryInfo;
|
|
let { localHeaderArray, extraFieldLength } = headerInfo;
|
|
let localHeaderArrayView = getDataView(localHeaderArray);
|
|
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 - 2);
|
|
const previousLocalHeaderArray = localHeaderArray;
|
|
headerInfo.localHeaderArray = localHeaderArray = new Uint8Array(getLength(previousLocalHeaderArray) + extraBytesLength);
|
|
arraySet(localHeaderArray, previousLocalHeaderArray);
|
|
arraySet(localHeaderArray, rawExtraFieldUSDZ, getLength(previousLocalHeaderArray));
|
|
localHeaderArrayView = getDataView(localHeaderArray);
|
|
setUint16(localHeaderArrayView, 28, extraFieldLength + extraBytesLength);
|
|
entryInfo.metadataSize += extraBytesLength;
|
|
}
|
|
|
|
function getDataDescriptorInfo({
|
|
zip64,
|
|
dataDescriptor,
|
|
dataDescriptorSignature
|
|
}) {
|
|
let dataDescriptorArray = new Uint8Array();
|
|
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,
|
|
offset,
|
|
diskNumberStart,
|
|
zip64UncompressedSize,
|
|
zip64CompressedSize,
|
|
zip64Offset,
|
|
zip64DiskNumberStart
|
|
}, localHeaderView, { dataDescriptor }) {
|
|
if (!dataDescriptor) {
|
|
if (!encrypted) {
|
|
setUint32(localHeaderView, HEADER_OFFSET_SIGNATURE + 4, signature);
|
|
}
|
|
if (!zip64) {
|
|
setUint32(localHeaderView, HEADER_OFFSET_COMPRESSED_SIZE + 4, compressedSize);
|
|
setUint32(localHeaderView, HEADER_OFFSET_UNCOMPRESSED_SIZE + 4, uncompressedSize);
|
|
}
|
|
}
|
|
if (zip64) {
|
|
if (localExtraFieldZip64Length) {
|
|
let localHeaderOffset = HEADER_SIZE + getLength(rawFilename) + 4;
|
|
if (zip64UncompressedSize) {
|
|
setBigUint64(localHeaderView, localHeaderOffset, BigInt(uncompressedSize));
|
|
localHeaderOffset += 8;
|
|
}
|
|
if (zip64CompressedSize) {
|
|
setBigUint64(localHeaderView, localHeaderOffset, BigInt(compressedSize));
|
|
localHeaderOffset += 8;
|
|
}
|
|
if (zip64Offset) {
|
|
setBigUint64(localHeaderView, localHeaderOffset, BigInt(offset));
|
|
localHeaderOffset += 8;
|
|
}
|
|
if (zip64DiskNumberStart) {
|
|
setUint32(localHeaderView, localHeaderOffset, diskNumberStart);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function updateZip64ExtraField({
|
|
compressedSize,
|
|
uncompressedSize,
|
|
offset,
|
|
diskNumberStart,
|
|
zip64UncompressedSize,
|
|
zip64CompressedSize,
|
|
zip64Offset,
|
|
zip64DiskNumberStart,
|
|
rawExtraFieldZip64
|
|
}) {
|
|
const rawExtraFieldZip64View = getDataView(rawExtraFieldZip64);
|
|
let rawExtraFieldZip64Offset = 4;
|
|
if (zip64UncompressedSize) {
|
|
setBigUint64(rawExtraFieldZip64View, rawExtraFieldZip64Offset, BigInt(uncompressedSize));
|
|
rawExtraFieldZip64Offset += 8;
|
|
}
|
|
if (zip64CompressedSize) {
|
|
setBigUint64(rawExtraFieldZip64View, rawExtraFieldZip64Offset, BigInt(compressedSize));
|
|
rawExtraFieldZip64Offset += 8;
|
|
}
|
|
if (zip64Offset) {
|
|
setBigUint64(rawExtraFieldZip64View, rawExtraFieldZip64Offset, BigInt(offset));
|
|
rawExtraFieldZip64Offset += 8;
|
|
}
|
|
if (zip64DiskNumberStart) {
|
|
setUint32(rawExtraFieldZip64View, rawExtraFieldZip64Offset, diskNumberStart);
|
|
}
|
|
}
|
|
|
|
async function closeFile(zipWriter, comment, options) {
|
|
const { files, writer } = zipWriter;
|
|
const { diskOffset } = writer;
|
|
let { diskNumber } = writer;
|
|
let offset = 0;
|
|
let directoryDataLength = 0;
|
|
let directoryOffset = zipWriter.offset - diskOffset;
|
|
let filesLength = files.size;
|
|
for (const [, fileEntry] of files) {
|
|
const {
|
|
rawFilename,
|
|
rawExtraFieldZip64,
|
|
rawExtraFieldAES,
|
|
rawComment,
|
|
rawExtraFieldNTFS,
|
|
rawExtraField,
|
|
extendedTimestamp,
|
|
extraFieldExtendedTimestampFlag,
|
|
lastModDate
|
|
} = fileEntry;
|
|
let rawExtraFieldTimestamp;
|
|
if (extendedTimestamp) {
|
|
rawExtraFieldTimestamp = new Uint8Array(9);
|
|
const extraFieldExtendedTimestampView = getDataView(rawExtraFieldTimestamp);
|
|
setUint16(extraFieldExtendedTimestampView, 0, EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
|
|
setUint16(extraFieldExtendedTimestampView, 2, 5);
|
|
setUint8(extraFieldExtendedTimestampView, 4, extraFieldExtendedTimestampFlag);
|
|
setUint32(extraFieldExtendedTimestampView, 5, Math.floor(lastModDate.getTime() / 1000));
|
|
} else {
|
|
rawExtraFieldTimestamp = new Uint8Array();
|
|
}
|
|
fileEntry.rawExtraFieldExtendedTimestamp = rawExtraFieldTimestamp;
|
|
directoryDataLength += 46 +
|
|
getLength(
|
|
rawFilename,
|
|
rawComment,
|
|
rawExtraFieldZip64,
|
|
rawExtraFieldAES,
|
|
rawExtraFieldNTFS,
|
|
rawExtraFieldTimestamp,
|
|
rawExtraField);
|
|
}
|
|
const directoryArray = new Uint8Array(directoryDataLength);
|
|
const directoryView = getDataView(directoryArray);
|
|
await initStream(writer);
|
|
let directoryDiskOffset = 0;
|
|
for (const [indexFileEntry, fileEntry] of Array.from(files.values()).entries()) {
|
|
const {
|
|
offset: fileEntryOffset,
|
|
rawFilename,
|
|
rawExtraFieldZip64,
|
|
rawExtraFieldAES,
|
|
rawExtraFieldExtendedTimestamp,
|
|
rawExtraFieldNTFS,
|
|
rawExtraField,
|
|
rawComment,
|
|
versionMadeBy,
|
|
headerArray,
|
|
headerView,
|
|
zip64,
|
|
zip64UncompressedSize,
|
|
zip64CompressedSize,
|
|
zip64DiskNumberStart,
|
|
zip64Offset,
|
|
internalFileAttributes,
|
|
externalFileAttributes,
|
|
diskNumberStart,
|
|
uncompressedSize,
|
|
compressedSize
|
|
} = fileEntry;
|
|
const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraField);
|
|
setUint32(directoryView, offset, CENTRAL_FILE_HEADER_SIGNATURE);
|
|
setUint16(directoryView, offset + 4, versionMadeBy);
|
|
if (!zip64UncompressedSize) {
|
|
setUint32(headerView, HEADER_OFFSET_UNCOMPRESSED_SIZE, uncompressedSize);
|
|
}
|
|
if (!zip64CompressedSize) {
|
|
setUint32(headerView, HEADER_OFFSET_COMPRESSED_SIZE, compressedSize);
|
|
}
|
|
arraySet(directoryArray, headerArray, offset + 6);
|
|
let directoryOffset = offset + HEADER_SIZE;
|
|
setUint16(directoryView, directoryOffset, extraFieldLength);
|
|
directoryOffset += 2;
|
|
setUint16(directoryView, directoryOffset, getLength(rawComment));
|
|
directoryOffset += 2;
|
|
setUint16(directoryView, directoryOffset, zip64 && zip64DiskNumberStart ? MAX_16_BITS : diskNumberStart);
|
|
directoryOffset += 2;
|
|
setUint16(directoryView, directoryOffset, internalFileAttributes);
|
|
directoryOffset += 2;
|
|
if (externalFileAttributes) {
|
|
setUint32(directoryView, directoryOffset, externalFileAttributes);
|
|
}
|
|
directoryOffset += 4;
|
|
setUint32(directoryView, directoryOffset, zip64 && zip64Offset ? MAX_32_BITS : fileEntryOffset);
|
|
directoryOffset += 4;
|
|
arraySet(directoryArray, rawFilename, directoryOffset);
|
|
directoryOffset += getLength(rawFilename);
|
|
arraySet(directoryArray, rawExtraFieldZip64, directoryOffset);
|
|
directoryOffset += getLength(rawExtraFieldZip64);
|
|
arraySet(directoryArray, rawExtraFieldAES, directoryOffset);
|
|
directoryOffset += getLength(rawExtraFieldAES);
|
|
arraySet(directoryArray, rawExtraFieldExtendedTimestamp, directoryOffset);
|
|
directoryOffset += getLength(rawExtraFieldExtendedTimestamp);
|
|
arraySet(directoryArray, rawExtraFieldNTFS, directoryOffset);
|
|
directoryOffset += getLength(rawExtraFieldNTFS);
|
|
arraySet(directoryArray, rawExtraField, directoryOffset);
|
|
directoryOffset += getLength(rawExtraField);
|
|
arraySet(directoryArray, rawComment, directoryOffset);
|
|
if (offset - directoryDiskOffset > writer.availableSize) {
|
|
writer.availableSize = 0;
|
|
await writeData(writer, directoryArray.slice(directoryDiskOffset, offset));
|
|
directoryDiskOffset = offset;
|
|
}
|
|
offset = directoryOffset;
|
|
if (options.onprogress) {
|
|
try {
|
|
await options.onprogress(indexFileEntry + 1, files.size, new Entry(fileEntry));
|
|
// eslint-disable-next-line no-unused-vars
|
|
} catch (_) {
|
|
// ignored
|
|
}
|
|
}
|
|
}
|
|
await writeData(writer, directoryDiskOffset ? directoryArray.slice(directoryDiskOffset) : directoryArray);
|
|
let lastDiskNumber = writer.diskNumber;
|
|
const { availableSize } = writer;
|
|
if (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 endOfdirectoryArray = new Uint8Array(zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH);
|
|
const endOfdirectoryView = getDataView(endOfdirectoryArray);
|
|
offset = 0;
|
|
if (zip64) {
|
|
setUint32(endOfdirectoryView, 0, ZIP64_END_OF_CENTRAL_DIR_SIGNATURE);
|
|
setBigUint64(endOfdirectoryView, 4, BigInt(44));
|
|
setUint16(endOfdirectoryView, 12, 45);
|
|
setUint16(endOfdirectoryView, 14, 45);
|
|
setUint32(endOfdirectoryView, 16, lastDiskNumber);
|
|
setUint32(endOfdirectoryView, 20, diskNumber);
|
|
setBigUint64(endOfdirectoryView, 24, BigInt(filesLength));
|
|
setBigUint64(endOfdirectoryView, 32, BigInt(filesLength));
|
|
setBigUint64(endOfdirectoryView, 40, BigInt(directoryDataLength));
|
|
setBigUint64(endOfdirectoryView, 48, BigInt(directoryOffset));
|
|
setUint32(endOfdirectoryView, 56, ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE);
|
|
setBigUint64(endOfdirectoryView, 64, BigInt(directoryOffset) + BigInt(directoryDataLength));
|
|
setUint32(endOfdirectoryView, 72, 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;
|
|
offset += ZIP64_END_OF_CENTRAL_DIR_LENGTH + ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH;
|
|
}
|
|
setUint32(endOfdirectoryView, offset, END_OF_CENTRAL_DIR_SIGNATURE);
|
|
setUint16(endOfdirectoryView, offset + 4, lastDiskNumber);
|
|
setUint16(endOfdirectoryView, offset + 6, diskNumber);
|
|
setUint16(endOfdirectoryView, offset + 8, filesLength);
|
|
setUint16(endOfdirectoryView, offset + 10, filesLength);
|
|
setUint32(endOfdirectoryView, offset + 12, directoryDataLength);
|
|
setUint32(endOfdirectoryView, offset + 16, directoryOffset);
|
|
const commentLength = getLength(comment);
|
|
if (commentLength) {
|
|
if (commentLength <= MAX_16_BITS) {
|
|
setUint16(endOfdirectoryView, offset + 20, commentLength);
|
|
} else {
|
|
throw new Error(ERR_INVALID_COMMENT);
|
|
}
|
|
}
|
|
await writeData(writer, endOfdirectoryArray);
|
|
if (commentLength) {
|
|
await writeData(writer, comment);
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
function getTimeNTFS(date) {
|
|
if (date) {
|
|
return ((BigInt(date.getTime()) + BigInt(11644473600000)) * BigInt(10000));
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
function getLength(...arrayLikes) {
|
|
let result = 0;
|
|
arrayLikes.forEach(arrayLike => arrayLike && (result += arrayLike.length));
|
|
return result;
|
|
}
|
|
|
|
function getHeaderArrayData({
|
|
version,
|
|
bitFlag,
|
|
compressionMethod,
|
|
uncompressedSize,
|
|
compressedSize,
|
|
lastModDate,
|
|
rawFilename,
|
|
zip64CompressedSize,
|
|
zip64UncompressedSize,
|
|
extraFieldLength
|
|
}) {
|
|
const headerArray = new Uint8Array(HEADER_SIZE - 4);
|
|
const headerView = getDataView(headerArray);
|
|
setUint16(headerView, 0, version);
|
|
setUint16(headerView, 2, bitFlag);
|
|
setUint16(headerView, 4, compressionMethod);
|
|
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());
|
|
const rawLastModDate = dateArray[0];
|
|
setUint32(headerView, 6, rawLastModDate);
|
|
if (zip64CompressedSize || compressedSize !== UNDEFINED_VALUE) {
|
|
setUint32(headerView, HEADER_OFFSET_COMPRESSED_SIZE, zip64CompressedSize ? MAX_32_BITS : compressedSize);
|
|
}
|
|
if (zip64UncompressedSize || uncompressedSize !== UNDEFINED_VALUE) {
|
|
setUint32(headerView, HEADER_OFFSET_UNCOMPRESSED_SIZE, zip64UncompressedSize ? MAX_32_BITS : uncompressedSize);
|
|
}
|
|
setUint16(headerView, 22, getLength(rawFilename));
|
|
setUint16(headerView, 24, 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.
|
|
*/
|
|
|
|
|
|
class ZipEntry {
|
|
|
|
constructor(fs, name, params, parent) {
|
|
const zipEntry = this;
|
|
if (fs.root && parent && parent.getChildByName(name)) {
|
|
throw new Error("Entry filename already exists");
|
|
}
|
|
if (!params) {
|
|
params = {};
|
|
}
|
|
Object.assign(zipEntry, {
|
|
fs,
|
|
name,
|
|
data: params.data,
|
|
options: params.options,
|
|
id: fs.entries.length,
|
|
parent,
|
|
children: [],
|
|
uncompressedSize: params.uncompressedSize || 0,
|
|
passThrough: params.passThrough
|
|
});
|
|
fs.entries.push(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("Entry filename already 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 = new 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 this.data.encrypted;
|
|
}
|
|
|
|
async checkPassword(password, options = {}) {
|
|
const zipEntry = this;
|
|
if (zipEntry.isPasswordProtected()) {
|
|
options.password = password;
|
|
options.checkPasswordOnly = true;
|
|
try {
|
|
await zipEntry.data.getData(null, options);
|
|
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);
|
|
}
|
|
|
|
getArrayBuffer(options) {
|
|
return this.data.arrayBuffer(options);
|
|
}
|
|
|
|
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: function () { return { 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);
|
|
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: function () { return { readable }; },
|
|
options
|
|
});
|
|
}
|
|
|
|
addFileSystemEntry(fileSystemEntry, options = {}) {
|
|
return addFileSystemHandle(this, fileSystemEntry, options);
|
|
}
|
|
|
|
addFileSystemHandle(handle, options = {}) {
|
|
return addFileSystemHandle(this, handle, options);
|
|
}
|
|
|
|
addFile(file, 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;
|
|
}
|
|
|
|
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, pathIndex) => {
|
|
const previousParent = parent;
|
|
parent = parent.getChildByName(pathPart);
|
|
if (!parent) {
|
|
parent = new ZipDirectoryEntry(this.fs, pathPart, { data: pathIndex == path.length - 1 ? entry : 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
|
|
}));
|
|
}
|
|
} catch (error) {
|
|
try {
|
|
error.cause = {
|
|
entry
|
|
};
|
|
// eslint-disable-next-line no-unused-vars
|
|
} catch (_) {
|
|
// ignored
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
return importedEntries;
|
|
}
|
|
|
|
async exportZip(writer, options) {
|
|
const zipEntry = this;
|
|
if (options.bufferedWrite === UNDEFINED_VALUE) {
|
|
options.bufferedWrite = true;
|
|
}
|
|
await Promise.all([initReaders(zipEntry, options.readerOptions), initStream(writer)]);
|
|
const zipWriter = new ZipWriter(writer, options);
|
|
await exportZip(zipWriter, zipEntry, getTotalSize([zipEntry], "uncompressedSize"), options);
|
|
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);
|
|
this.entries[entry.id] = null;
|
|
}
|
|
|
|
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) {
|
|
if (destination.getChildByName(entry.name)) {
|
|
throw new Error("Entry filename already exists");
|
|
}
|
|
detach(entry);
|
|
entry.parent = destination;
|
|
destination.children.push(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]);
|
|
}
|
|
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) {
|
|
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);
|
|
}
|
|
|
|
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;
|
|
entries.forEach(process);
|
|
return size;
|
|
|
|
function process(entry) {
|
|
size += entry[propertyName];
|
|
if (entry.children) {
|
|
entry.children.forEach(process);
|
|
}
|
|
}
|
|
}
|
|
|
|
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({}, zipBlobReader.options, options));
|
|
zipBlobReader.data = data;
|
|
zipBlobReader.blobReader = new BlobReader(data);
|
|
super.init();
|
|
}
|
|
|
|
readUint8Array(index, length) {
|
|
return this.blobReader.readUint8Array(index, length);
|
|
}
|
|
};
|
|
}
|
|
|
|
async function initReaders(entry, options) {
|
|
if (entry.children.length) {
|
|
await Promise.all(entry.children.map(async child => {
|
|
if (child.directory) {
|
|
await initReaders(child, options);
|
|
} else {
|
|
const reader = child.reader = new child.Reader(child.data, options);
|
|
try {
|
|
await initStream(reader);
|
|
} catch (error) {
|
|
try {
|
|
error.entryId = child.id;
|
|
error.cause = {
|
|
entry: child
|
|
};
|
|
// eslint-disable-next-line no-unused-vars
|
|
} catch (_) {
|
|
// ignored
|
|
}
|
|
throw error;
|
|
}
|
|
child.uncompressedSize = reader.size;
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
|
|
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) {
|
|
const selectedEntry = entry;
|
|
const entryOffsets = new Map();
|
|
await process(zipWriter, entry);
|
|
|
|
async function process(zipWriter, entry) {
|
|
await exportChild();
|
|
|
|
async function exportChild() {
|
|
if (options.bufferedWrite) {
|
|
await Promise.allSettled(entry.children.map(processChild));
|
|
} 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, child.reader, 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);
|
|
// eslint-disable-next-line no-unused-vars
|
|
} 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);
|
|
}
|
|
}
|
|
return addedEntries;
|
|
}
|
|
}
|
|
|
|
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.root = new ZipDirectoryEntry(fs);
|
|
}
|
|
|
|
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.
|
|
*/
|
|
|
|
|
|
let baseURL;
|
|
try {
|
|
baseURL = (typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('zip-fs.js', document.baseURI).href));
|
|
// eslint-disable-next-line no-unused-vars
|
|
} catch (_) {
|
|
// ignored
|
|
}
|
|
configure({ baseURL });
|
|
e(configure);
|
|
|
|
exports.BlobReader = BlobReader;
|
|
exports.BlobWriter = BlobWriter;
|
|
exports.Data64URIReader = Data64URIReader;
|
|
exports.Data64URIWriter = Data64URIWriter;
|
|
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_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_UNCOMPRESSED_SIZE = ERR_UNDEFINED_UNCOMPRESSED_SIZE;
|
|
exports.ERR_UNSUPPORTED_COMPRESSION = ERR_UNSUPPORTED_COMPRESSION;
|
|
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.GenericReader = GenericReader;
|
|
exports.GenericWriter = GenericWriter;
|
|
exports.HttpRangeReader = HttpRangeReader;
|
|
exports.HttpReader = HttpReader;
|
|
exports.Reader = Reader;
|
|
exports.SplitDataReader = SplitDataReader;
|
|
exports.SplitDataWriter = SplitDataWriter;
|
|
exports.SplitZipReader = SplitZipReader;
|
|
exports.SplitZipWriter = SplitZipWriter;
|
|
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.fs = fs;
|
|
exports.getMimeType = getMimeType;
|
|
exports.initShimAsyncCodec = initShimAsyncCodec;
|
|
exports.initStream = initStream;
|
|
exports.readUint8Array = readUint8Array;
|
|
exports.terminateWorkers = terminateWorkers;
|
|
|
|
}));
|