Arjun Barrett 4 年之前
父节点
当前提交
abadcbef52
共有 16 个文件被更改,包括 379 次插入41 次删除
  1. 2 1
      .gitignore
  2. 8 10
      .npmignore
  3. 4 0
      CHANGELOG.md
  4. 18 2
      README.md
  5. 10 0
      docs/classes/asyncdeflate.md
  6. 10 0
      docs/classes/asyncgunzip.md
  7. 12 0
      docs/classes/asyncgzip.md
  8. 10 0
      docs/classes/asyncinflate.md
  9. 10 0
      docs/classes/asyncunzlib.md
  10. 10 0
      docs/classes/asynczlib.md
  11. 16 4
      package.json
  12. 41 0
      scripts/buildUMD.ts
  13. 61 21
      src/index.ts
  14. 1 0
      test/3-streams.ts
  15. 0 0
      test/4-multiperf.ts
  16. 166 3
      yarn.lock

+ 2 - 1
.gitignore

@@ -4,4 +4,5 @@ esm/
 .DS_STORE
 # Rust version - available when ready
 rs/
-.DS_STORE
+.DS_STORE
+umd/

+ 8 - 10
.npmignore

@@ -1,10 +1,8 @@
-docs/
-src/
-test/
-.gitignore
-.npmignore
-tsconfig.esm.json
-tsconfig.json
-yarn.lock
-rs/
-scripts/
+*
+!esm/
+!lib/
+!umd/
+!CHANGELOG.md
+!LICENSE
+!package.json
+!README.md

+ 4 - 0
CHANGELOG.md

@@ -1,3 +1,7 @@
+## 0.3.4
+- Fixed rare overflow bug causing corruption
+- Added async stream termination
+- Added UMD bundle
 ## 0.3.0
 - Added support for asynchronous and synchronous streaming
 - Reduced bundle size by autogenerating worker code, even in minified environments

+ 18 - 2
README.md

@@ -45,6 +45,17 @@ If your environment doesn't support ES Modules (e.g. Node.js):
 const fflate = require('fflate');
 ```
 
+If you want to load from a CDN in the browser:
+```html
+<!--
+You should use either UNPKG or jsDelivr (i.e. only one of the following)
+Note that tree shaking is completely unsupported from the CDN
+-->
+<script src="https://unpkg.com/fflate"></script>
+<script src="https://cdn.jsdelivr.net/npm/fflate"></script>
+<!-- Now, the global variable fflate contains the library -->
+```
+
 And use:
 ```js
 // This is an ArrayBuffer of data
@@ -163,7 +174,7 @@ const dcmpStrm = new fflate.Decompress((chunk, final) => {
 });
 ```
 
-You can create multi-file ZIP archives easily as well:
+You can create multi-file ZIP archives easily as well. Note that by default, compression is enabled for all files, which is not useful when ZIPping many PNGs, JPEGs, PDFs, etc. because those formats are already compressed. You should either override the level on a per-file basis or globally to avoid wasting resources.
 ```js
 // Note that the asynchronous version (see below) runs in parallel and
 // is *much* (up to 3x) faster for larger archives.
@@ -179,7 +190,8 @@ const zipped = fflate.zipSync({
   },
   // You can also provide compression options
   'myImageData.bmp': [aMassiveFile, { level: 9, mem: 12 }],
-  'superTinyFile.bin': [new Uint8Array([0]), { level: 0 }]
+  // PNG is pre-compressed; no need to waste time
+  'superTinyFile.png': [aPNGFile, { level: 0 }]
 }, {
   // These options are the defaults for all files, but file-specific
   // options take precedence.
@@ -266,6 +278,10 @@ gzs.push(chunk);
 // be), use synchronous streams.
 console.log(wasCallbackCalled) // false
 
+// To terminate an asynchronous stream's internal worker, call
+// stream.terminate().
+gzs.terminate();
+
 // This is way faster than zipSync because the compression of multiple
 // files runs in parallel. In fact, the fact that it's parallelized
 // makes it faster than most standalone ZIP CLIs. The effect is most

+ 10 - 0
docs/classes/asyncdeflate.md

@@ -15,6 +15,7 @@ Asynchronous streaming DEFLATE compression
 ### Properties
 
 * [ondata](asyncdeflate.md#ondata)
+* [terminate](asyncdeflate.md#terminate)
 
 ### Methods
 
@@ -57,6 +58,15 @@ Name | Type | Description |
 
 The handler to call whenever data is available
 
+___
+
+### terminate
+
+•  **terminate**: [AsyncTerminable](../interfaces/asyncterminable.md)
+
+A method to terminate the stream's internal worker. Subsequent calls to
+push() will silently fail.
+
 ## Methods
 
 ### push

+ 10 - 0
docs/classes/asyncgunzip.md

@@ -15,6 +15,7 @@ Asynchronous streaming GZIP decompression
 ### Properties
 
 * [ondata](asyncgunzip.md#ondata)
+* [terminate](asyncgunzip.md#terminate)
 
 ### Methods
 
@@ -44,6 +45,15 @@ Name | Type | Description |
 
 The handler to call whenever data is available
 
+___
+
+### terminate
+
+•  **terminate**: [AsyncTerminable](../interfaces/asyncterminable.md)
+
+A method to terminate the stream's internal worker. Subsequent calls to
+push() will silently fail.
+
 ## Methods
 
 ### push

+ 12 - 0
docs/classes/asyncgzip.md

@@ -16,6 +16,7 @@ Asynchronous streaming GZIP compression
 ### Properties
 
 * [ondata](asyncgzip.md#ondata)
+* [terminate](asyncgzip.md#terminate)
 
 ### Methods
 
@@ -84,6 +85,17 @@ Name | Type | Description |
 The handler to call whenever data is available
 The handler to call whenever data is available
 
+___
+
+### terminate
+
+•  **terminate**: [AsyncTerminable](../interfaces/asyncterminable.md)
+
+A method to terminate the stream's internal worker. Subsequent calls to
+push() will silently fail.
+A method to terminate the stream's internal worker. Subsequent calls to
+push() will silently fail.
+
 ## Methods
 
 ### push

+ 10 - 0
docs/classes/asyncinflate.md

@@ -15,6 +15,7 @@ Asynchronous streaming DEFLATE decompression
 ### Properties
 
 * [ondata](asyncinflate.md#ondata)
+* [terminate](asyncinflate.md#terminate)
 
 ### Methods
 
@@ -44,6 +45,15 @@ Name | Type | Description |
 
 The handler to call whenever data is available
 
+___
+
+### terminate
+
+•  **terminate**: [AsyncTerminable](../interfaces/asyncterminable.md)
+
+A method to terminate the stream's internal worker. Subsequent calls to
+push() will silently fail.
+
 ## Methods
 
 ### push

+ 10 - 0
docs/classes/asyncunzlib.md

@@ -15,6 +15,7 @@ Asynchronous streaming Zlib decompression
 ### Properties
 
 * [ondata](asyncunzlib.md#ondata)
+* [terminate](asyncunzlib.md#terminate)
 
 ### Methods
 
@@ -44,6 +45,15 @@ Name | Type | Description |
 
 The handler to call whenever data is available
 
+___
+
+### terminate
+
+•  **terminate**: [AsyncTerminable](../interfaces/asyncterminable.md)
+
+A method to terminate the stream's internal worker. Subsequent calls to
+push() will silently fail.
+
 ## Methods
 
 ### push

+ 10 - 0
docs/classes/asynczlib.md

@@ -15,6 +15,7 @@ Asynchronous streaming Zlib compression
 ### Properties
 
 * [ondata](asynczlib.md#ondata)
+* [terminate](asynczlib.md#terminate)
 
 ### Methods
 
@@ -57,6 +58,15 @@ Name | Type | Description |
 
 The handler to call whenever data is available
 
+___
+
+### terminate
+
+•  **terminate**: [AsyncTerminable](../interfaces/asyncterminable.md)
+
+A method to terminate the stream's internal worker. Subsequent calls to
+push() will silently fail.
+
 ## Methods
 
 ### push

+ 16 - 4
package.json

@@ -1,10 +1,12 @@
 {
   "name": "fflate",
-  "version": "0.3.3",
+  "version": "0.3.4",
   "description": "High performance (de)compression in an 8kB package",
   "main": "lib/index.js",
   "module": "esm/index.js",
   "types": "lib/index.d.ts",
+  "unpkg": "umd/index.js",
+  "jsdelivr": "umd/index.js",
   "browser": {
     "./lib/node-worker.js": "./lib/worker.js",
     "./esm/node-worker.js": "./esm/worker.js"
@@ -22,6 +24,7 @@
     "decompression",
     "zlib",
     "pako",
+    "jszip",
     "browser",
     "node.js",
     "tiny",
@@ -30,9 +33,11 @@
     "non-blocking"
   ],
   "scripts": {
-    "build": "yarn build:lib && yarn build:docs && yarn build:post",
-    "build:lib": "tsc && tsc --project tsconfig.esm.json",
-    "build:post": "node -r ts-node/register scripts/rewriteBuilds.ts",
+    "build": "yarn build:lib && yarn build:docs && yarn build:rewrite",
+    "script": "node -r ts-node/register scripts/$SC.ts",
+    "build:lib": "tsc && tsc --project tsconfig.esm.json && yarn build:umd",
+    "build:umd": "SC=buildUMD yarn script",
+    "build:rewrite": "SC=rewriteBuilds yarn script",
     "build:docs": "typedoc --mode library --plugin typedoc-plugin-markdown --hideProjectName --hideBreadcrumbs --readme none --disableSources --excludePrivate --excludeProtected --out docs/ src/index.ts",
     "test": "TS_NODE_PROJECT=test/tsconfig.json uvu -b -r ts-node/register test",
     "prepack": "yarn build && yarn test"
@@ -40,7 +45,14 @@
   "devDependencies": {
     "@types/node": "^14.11.2",
     "@types/pako": "*",
+    "@types/react": "^16.9.55",
+    "@types/react-dom": "^16.9.9",
+    "jszip": "^3.5.0",
     "pako": "*",
+    "preact": "^10.5.5",
+    "react": "^17.0.1",
+    "react-dom": "^17.0.1",
+    "terser": "^5.3.8",
     "tiny-inflate": "*",
     "ts-node": "^9.0.0",
     "typedoc": "^0.17.0-3",

+ 41 - 0
scripts/buildUMD.ts

@@ -0,0 +1,41 @@
+import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
+import { minify, MinifyOptions } from 'terser';
+import { join } from 'path';
+
+const p = (...fns: string[]) => join(__dirname, '..', ...fns);
+
+const src = readFileSync(p('lib', 'index.js'), 'utf8');
+const worker = readFileSync(p('lib', 'worker.js'), 'utf8');
+const nodeWorker = readFileSync(p('lib', 'node-worker.js'), 'utf8');
+
+const opts: MinifyOptions = {
+  mangle: {
+    toplevel: true,
+  },
+  compress: {
+    passes: 5,
+    unsafe: true,
+    pure_getters: true
+  },
+  sourceMap: false
+};
+
+minify(src, opts).then(async out => {
+  const wkrOut = (await minify(worker, opts)).code.replace(
+    /exports.__esModule=!0;/,
+    ''
+  ).replace(/exports\./g, '_f.');
+  const nodeWkrOut = (await minify(nodeWorker, opts)).code.replace(
+    /exports.__esModule=!0;/,
+    ''
+  ).replace(/exports\./g, '_f.').replace(
+    /require/, // intentionally done just once
+    "eval('require')"
+  );
+  const res = "!function(f){typeof module!='undefined'&&typeof exports=='object'?module.exports=f():typeof define!='undefined'&&define.amd?define(['fflate',f]):(typeof self!='undefined'?self:this).fflate=f()}(function(){var _e = {};" +
+    out.code.replace(/require\("\.\/node-worker"\)/,
+    "(typeof module!='undefined'&&typeof exports=='object'?function(_f){" + nodeWkrOut + 'return _f}:function(_f){' + wkrOut + 'return _f})({})'
+  ).replace(/exports\.(.*)=void 0;/, '').replace(/exports\./g, '_e.') + 'return _e})';
+  if (!existsSync(p('umd'))) mkdirSync(p('umd'));
+  writeFileSync(p('umd', 'index.js'), res);
+});

+ 61 - 21
src/index.ts

@@ -68,7 +68,7 @@ const hMap = ((cd: Uint8Array, mb: number, r: 0 | 1) => {
   // index
   let i = 0;
   // u8 "map": index -> # of codes with bit length = index
-  const l = new u8(mb);
+  const l = new u16(mb);
   // length of cd must be 288 (total # of codes)
   for (; i < s; ++i) ++l[cd[i] - 1];
   // u16 "map": index -> minimum code for bit length = index
@@ -196,12 +196,13 @@ const inflt = (dat: Uint8Array, buf?: Uint8Array, st?: InflateState) => {
   } : () => {};
   //  last chunk         bitpos           bytes
   let final = st.f || 0, pos = st.p || 0, bt = st.b || 0, lm = st.l, dm = st.d, lbt = st.m, dbt = st.n;
+  if (final && !lm) return buf;
   // total bits
   const tbts = sl << 3;
   do {
     if (!lm) {
       // BFINAL - this is only 1 when last chunk is next
-      final = bits(dat, pos, 1);
+      st.f = final = bits(dat, pos, 1);
       // type: 0 = no compression, 1 = fixed huffman, 2 = dynamic huffman
       const type = bits(dat, pos + 1, 3);
       pos += 3;
@@ -311,8 +312,8 @@ const inflt = (dat: Uint8Array, buf?: Uint8Array, st?: InflateState) => {
         bt = end;
       }
     }
-    if (lm) final = 1, st.l = lm, st.m = lbt, st.d = dm, st.n = dbt;
-    st.p = pos, st.b = bt;
+    st.l = lm, st.p = pos, st.b = bt;
+    if (lm) final = 1, st.m = lbt, st.d = dm, st.n = dbt;
   } while (!final)
   return bt == buf.length ? buf : slc(buf, 0, bt);
 }
@@ -371,7 +372,7 @@ const hTree = (d: Uint16Array, mb: number) => {
   // symbols that combined have high freq, will start processing i2 (high-freq,
   // non-composite) symbols instead
   // see https://reddit.com/r/photopea/comments/ikekht/uzipjs_questions/
-	while (i1 != s - 1) {
+  while (i1 != s - 1) {
     l = t[t[i0].f < t[i2].f ? i0++ : i2++];
     r = t[i0 != i1 && t[i0].f < t[i2].f ? i0++ : i2++];
     t[i1++] = { s: -1, f: l.f + r.f, l, r };
@@ -493,7 +494,7 @@ const wblk = (dat: Uint8Array, out: Uint8Array, final: number, syms: Uint32Array
   const flen = (bl + 5) << 3;
   const ftlen = clen(lf, flt) + clen(df, fdt) + eb;
   const dtlen = clen(lf, dlt) + clen(df, ddt) + eb + 14 + 3 * nlcc + clen(lcfreq, lct) + (2 * lcfreq[16] + 3 * lcfreq[17] + 7 * lcfreq[18]);
-  if (flen < ftlen && flen < dtlen) return wfblk(out, p, dat.subarray(bs, bs + bl));
+  if (flen <= ftlen && flen <= dtlen) return wfblk(out, p, dat.subarray(bs, bs + bl));
   let lm: Uint16Array, ll: Uint8Array, dm: Uint16Array, dl: Uint8Array;
   wbits(out, p, 1 + (dtlen < ftlen as unknown as number)), p += 2;
   if (dtlen < ftlen) {
@@ -888,7 +889,6 @@ const wom = (ev: MessageEvent<[Record<string, unknown>, string]>) => {
 type CachedWorker = readonly [string, Record<string, unknown>];
 
 const ch: CachedWorker[] = [];
-
 // clone bufs
 const cbfs = (v: Record<string, unknown>) => {
   const tl: ArrayBuffer[] = [];
@@ -953,11 +953,11 @@ const astrm = (strm: CmpDecmpStrm) => {
   return (ev: MessageEvent<[Uint8Array, boolean]>) => strm.push(ev.data[0], ev.data[1]);
 }
 
-type Astrm = { ondata: AsyncFlateStreamHandler; push: (d: Uint8Array, f?: boolean) => void; };
+type Astrm = { ondata: AsyncFlateStreamHandler; push: (d: Uint8Array, f?: boolean) => void; terminate: AsyncTerminable; };
 
 // async stream attach
 const astrmify = <T>(fns: (() => unknown[])[], strm: Astrm, opts: T | 0, init: (ev: MessageEvent<T>) => void, id: number) => {
-  let t = 0;
+  let t: boolean;
   const w = wrkr<T, [Uint8Array, boolean]>(
     fns,
     init,
@@ -965,7 +965,7 @@ const astrmify = <T>(fns: (() => unknown[])[], strm: Astrm, opts: T | 0, init: (
     (err, dat) => {
       if (err) strm.ondata.call(strm, err);
       else {
-        if (dat[1]) t = 1, w.terminate();
+        if (dat[1]) w.terminate();
         strm.ondata.call(strm, err, dat[0], dat[1]);
       }
     }
@@ -974,8 +974,9 @@ const astrmify = <T>(fns: (() => unknown[])[], strm: Astrm, opts: T | 0, init: (
   strm.push = function(d, f) {
     if (t) throw 'stream finished';
     if (!strm.ondata) throw 'no stream handler';
-    w.postMessage([d, f], [d.buffer]);
+    w.postMessage([d, t = f], [d.buffer]);
   };
+  strm.terminate = () => { w.terminate(); };
 }
 
 // read 2 bytes
@@ -1129,6 +1130,12 @@ export class AsyncDeflate {
    */
   // @ts-ignore
   push(chunk: Uint8Array, final?: boolean): void;
+  
+  /**
+   * A method to terminate the stream's internal worker. Subsequent calls to
+   * push() will silently fail.
+   */
+  terminate: AsyncTerminable;
 }
 
 /**
@@ -1182,19 +1189,20 @@ export class Inflate {
   ondata: FlateStreamHandler;
 
   private e(c: Uint8Array) {
+    if (this.d) throw 'stream finished';
+    if (!this.ondata) throw 'no stream handler';
     const l = this.p.length;
     const n = new u8(l + c.length);
     n.set(this.p), n.set(c, l), this.p = n;
   }
 
   private c(c: Uint8Array, final: boolean) {
-    this.s.i = final;
+    this.d = this.s.i = final;
     const bts = this.s.b;
     const dt = inflt(this.p, this.o, this.s);
-    this.ondata(slc(dt, bts, this.s.b), final);
+    this.ondata(slc(dt, bts, this.s.b), final || false);
     this.o = slc(dt, this.s.b - 32768), this.s.b = 32768;
     this.p = slc(this.p, this.s.p >>> 3), this.s.p &= 7;
-    this.d = this.s.f && !this.s.l;
   }
 
   /**
@@ -1203,9 +1211,7 @@ export class Inflate {
    * @param final Whether this is the final chunk
    */
   push(chunk: Uint8Array, final?: boolean) {
-    if (this.d) throw 'stream finished';
-    if (!this.ondata) throw 'no stream handler';
-    this.e(chunk), this.c(chunk, final || false);
+    this.e(chunk), this.c(chunk, final);
   }
 }
 
@@ -1240,6 +1246,12 @@ export class AsyncInflate {
    */
   // @ts-ignore
   push(chunk: Uint8Array, final?: boolean): void;
+
+  /**
+   * A method to terminate the stream's internal worker. Subsequent calls to
+   * push() will silently fail.
+   */
+  terminate: AsyncTerminable;
 }
 
 /**
@@ -1362,6 +1374,12 @@ export class AsyncGzip {
    */
   // @ts-ignore
   push(chunk: Uint8Array, final?: boolean): void;
+
+  /**
+   * A method to terminate the stream's internal worker. Subsequent calls to
+   * push() will silently fail.
+   */
+  terminate: AsyncTerminable;
 }
 
 /**
@@ -1428,7 +1446,7 @@ export class Gunzip {
     (Inflate.prototype as unknown as { e: typeof Inflate.prototype['e'] }).e.call(this, chunk);
     if (this.v) {
       const s = gzs(this.p);
-      if (s >= this.p.length) return;
+      if (s >= this.p.length && !final) return;
       this.p = this.p.subarray(s), this.v = 0;
     }
     if (final) {
@@ -1473,6 +1491,12 @@ export class AsyncGunzip {
    */
   // @ts-ignore
   push(chunk: Uint8Array, final?: boolean): void;
+
+  /**
+   * A method to terminate the stream's internal worker. Subsequent calls to
+   * push() will silently fail.
+   */
+  terminate: AsyncTerminable;
 }
 
 /**
@@ -1593,6 +1617,12 @@ export class AsyncZlib {
    */
   // @ts-ignore
   push(chunk: Uint8Array, final?: boolean): void;
+
+  /**
+   * A method to terminate the stream's internal worker. Subsequent calls to
+   * push() will silently fail.
+   */
+  terminate: AsyncTerminable;
 }
 
 /**
@@ -1656,7 +1686,7 @@ export class Unzlib {
   push(chunk: Uint8Array, final?: boolean) {
     (Inflate.prototype as unknown as { e: typeof Inflate.prototype['e'] }).e.call(this, chunk);
     if (this.v) {
-      if (this.p.length < 2) return;
+      if (this.p.length < 2 && !final) return;
       this.p = this.p.subarray(2), this.v = 0;
     }
     if (final) {
@@ -1701,6 +1731,12 @@ export class AsyncUnzlib {
    */
   // @ts-ignore
   push(chunk: Uint8Array, final?: boolean): void;
+
+  /**
+   * A method to terminate the stream's internal worker. Subsequent calls to
+   * push() will silently fail.
+   */
+  terminate: AsyncTerminable;
 }
 
 /**
@@ -2155,7 +2191,9 @@ export function unzip(data: Uint8Array, cb: UnzipCallback): AsyncTerminable {
   }
   const files: Unzipped = {};
   let e = data.length - 22;
-  while (b4(data, e) != 0x6054B50) --e;
+  for (; b4(data, e) != 0x6054B50; --e) {
+    if (!e || data.length - e > 65558) throw 'invalid zip file';
+  };
   let lft = b2(data, e + 8);
   if (!lft) cb(null, {});
   const c = lft;
@@ -2191,7 +2229,9 @@ export function unzip(data: Uint8Array, cb: UnzipCallback): AsyncTerminable {
 export function unzipSync(data: Uint8Array) {
   const files: Unzipped = {};
   let e = data.length - 22;
-  while (b4(data, e) != 0x6054B50) --e;
+  for (; b4(data, e) != 0x6054B50; --e) {
+    if (!e || data.length - e > 65558) throw 'invalid zip file';
+  };
   const c = b2(data, e + 8);
   if (!c) return {};
   let o = b4(data, e + 16);

+ 1 - 0
test/3-streams.ts

@@ -0,0 +1 @@
+// TODO: Test streams here

+ 0 - 0
test/3-multiperf.ts → test/4-multiperf.ts


+ 166 - 3
yarn.lock

@@ -17,6 +17,26 @@
   resolved "https://registry.yarnpkg.com/@types/pako/-/pako-1.0.1.tgz#33b237f3c9aff44d0f82fe63acffa4a365ef4a61"
   integrity sha512-GdZbRSJ3Cv5fiwT6I0SQ3ckeN2PWNqxd26W9Z2fCK1tGrrasGy4puvNFtnddqH9UJFMQYXxEuuB7B8UK+LLwSg==
 
+"@types/prop-types@*":
+  version "15.7.3"
+  resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
+  integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
+
+"@types/react-dom@^16.9.9":
+  version "16.9.9"
+  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.9.tgz#d2d0a6f720a0206369ccbefff752ba37b9583136"
+  integrity sha512-jE16FNWO3Logq/Lf+yvEAjKzhpST/Eac8EMd1i4dgZdMczfgqC8EjpxwNgEe3SExHYLliabXDh9DEhhqnlXJhg==
+  dependencies:
+    "@types/react" "*"
+
+"@types/react@*", "@types/react@^16.9.55":
+  version "16.9.55"
+  resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.55.tgz#47078587f5bfe028a23b6b46c7b94ac0d436acff"
+  integrity sha512-6KLe6lkILeRwyyy7yG9rULKJ0sXplUsl98MGoCfpteXf9sPWFWWMknDcsvubcpaTdBuxtsLF6HDUwdApZL/xIg==
+  dependencies:
+    "@types/prop-types" "*"
+    csstype "^3.0.2"
+
 arg@^4.1.0:
   version "4.1.3"
   resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
@@ -47,11 +67,26 @@ buffer-from@^1.0.0:
   resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
   integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
 
+commander@^2.20.0:
+  version "2.20.3"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+  integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
 [email protected]:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
   integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
 
+core-util-is@~1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+  integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
+
+csstype@^3.0.2:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.4.tgz#b156d7be03b84ff425c9a0a4b1e5f4da9c5ca888"
+  integrity sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA==
+
 dequal@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/dequal/-/dequal-1.0.1.tgz#dbbf9795ec626e9da8bd68782f4add1d23700d8b"
@@ -110,6 +145,11 @@ highlight.js@^9.18.0:
   resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.3.tgz#a1a0a2028d5e3149e2380f8a865ee8516703d634"
   integrity sha512-zBZAmhSupHIl5sITeMqIJnYCDfAEc3Gdkqj65wC1lpI468MMQeeQkhcIAvk+RylAkxrCcI9xy9piHiXeQ1BdzQ==
 
+immediate@~3.0.5:
+  version "3.0.6"
+  resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
+  integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
+
 inflight@^1.0.4:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
@@ -118,7 +158,7 @@ inflight@^1.0.4:
     once "^1.3.0"
     wrappy "1"
 
-inherits@2:
+inherits@2, inherits@~2.0.3:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
   integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -128,11 +168,21 @@ interpret@^1.0.0:
   resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
   integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==
 
+isarray@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+  integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
+
 jquery@^3.4.1:
   version "3.5.1"
   resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz#d7b4d08e1bfdb86ad2f1a3d039ea17304717abb5"
   integrity sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==
 
+"js-tokens@^3.0.0 || ^4.0.0":
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+  integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
 jsonfile@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
@@ -140,16 +190,40 @@ jsonfile@^4.0.0:
   optionalDependencies:
     graceful-fs "^4.1.6"
 
+jszip@^3.5.0:
+  version "3.5.0"
+  resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.5.0.tgz#b4fd1f368245346658e781fec9675802489e15f6"
+  integrity sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==
+  dependencies:
+    lie "~3.3.0"
+    pako "~1.0.2"
+    readable-stream "~2.3.6"
+    set-immediate-shim "~1.0.1"
+
 kleur@^4.0.3:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.1.tgz#80b49dd7d1afeba41b8dcdf4ecfff9252205fc52"
   integrity sha512-BsNhM6T/yTWFG580CRnYhT3LfUuPK7Hwrm+W2H0G8lK/nogalP5Nsrh/cHjxVVkzl0sFm7z8b8rNcZCfKxeoxA==
 
+lie@~3.3.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a"
+  integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==
+  dependencies:
+    immediate "~3.0.5"
+
 lodash@^4.17.15:
   version "4.17.20"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
   integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
 
+loose-envify@^1.1.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+  integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
+  dependencies:
+    js-tokens "^3.0.0 || ^4.0.0"
+
 lunr@^2.3.8:
   version "2.3.9"
   resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1"
@@ -187,6 +261,11 @@ neo-async@^2.6.0:
   resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
   integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
 
+object-assign@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+  integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
+
 once@^1.3.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@@ -194,7 +273,7 @@ once@^1.3.0:
   dependencies:
     wrappy "1"
 
-pako@*:
+pako@*, pako@~1.0.2:
   version "1.0.11"
   resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
   integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
@@ -209,11 +288,51 @@ path-parse@^1.0.6:
   resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
   integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
 
+preact@^10.5.5:
+  version "10.5.5"
+  resolved "https://registry.yarnpkg.com/preact/-/preact-10.5.5.tgz#c6c172ca751df27483350b8ab622abc12956e997"
+  integrity sha512-5ONLNH1SXMzzbQoExZX4TELemNt+TEDb622xXFNfZngjjM9qtrzseJt+EfiUu4TZ6EJ95X5sE1ES4yqHFSIdhg==
+
+process-nextick-args@~2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
+  integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
+
 progress@^2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
   integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
 
+react-dom@^17.0.1:
+  version "17.0.1"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.1.tgz#1de2560474ec9f0e334285662ede52dbc5426fc6"
+  integrity sha512-6eV150oJZ9U2t9svnsspTMrWNyHc6chX0KzDeAOXftRa8bNeOKTTfCJ7KorIwenkHd2xqVTBTCZd79yk/lx/Ug==
+  dependencies:
+    loose-envify "^1.1.0"
+    object-assign "^4.1.1"
+    scheduler "^0.20.1"
+
+react@^17.0.1:
+  version "17.0.1"
+  resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127"
+  integrity sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w==
+  dependencies:
+    loose-envify "^1.1.0"
+    object-assign "^4.1.1"
+
+readable-stream@~2.3.6:
+  version "2.3.7"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
+  integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
+  dependencies:
+    core-util-is "~1.0.0"
+    inherits "~2.0.3"
+    isarray "~1.0.0"
+    process-nextick-args "~2.0.0"
+    safe-buffer "~5.1.1"
+    string_decoder "~1.1.1"
+    util-deprecate "~1.0.1"
+
 rechoir@^0.6.2:
   version "0.6.2"
   resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
@@ -235,6 +354,24 @@ sade@^1.7.3:
   dependencies:
     mri "^1.1.0"
 
+safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+  integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+scheduler@^0.20.1:
+  version "0.20.1"
+  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.1.tgz#da0b907e24026b01181ecbc75efdc7f27b5a000c"
+  integrity sha512-LKTe+2xNJBNxu/QhHvDR14wUXHRQbVY5ZOYpOGWRzhydZUqrLb2JBvLPY7cAqFmqrWuDED0Mjk7013SZiOz6Bw==
+  dependencies:
+    loose-envify "^1.1.0"
+    object-assign "^4.1.1"
+
+set-immediate-shim@~1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
+  integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=
+
 shelljs@^0.8.3:
   version "0.8.4"
   resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2"
@@ -244,7 +381,7 @@ shelljs@^0.8.3:
     interpret "^1.0.0"
     rechoir "^0.6.2"
 
-source-map-support@^0.5.17:
+source-map-support@^0.5.17, source-map-support@~0.5.19:
   version "0.5.19"
   resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
   integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
@@ -257,6 +394,27 @@ source-map@^0.6.0, source-map@^0.6.1:
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
   integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
 
+source-map@~0.7.2:
+  version "0.7.3"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
+  integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
+
+string_decoder@~1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
+  integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
+  dependencies:
+    safe-buffer "~5.1.0"
+
+terser@^5.3.8:
+  version "5.3.8"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-5.3.8.tgz#991ae8ba21a3d990579b54aa9af11586197a75dd"
+  integrity sha512-zVotuHoIfnYjtlurOouTazciEfL7V38QMAOhGqpXDEg6yT13cF4+fEP9b0rrCEQTn+tT46uxgFsTZzhygk+CzQ==
+  dependencies:
+    commander "^2.20.0"
+    source-map "~0.7.2"
+    source-map-support "~0.5.19"
+
 tiny-inflate@*:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4"
@@ -331,6 +489,11 @@ universalify@^0.1.0:
   resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
   integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
 
+util-deprecate@~1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+  integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
+
 uvu@^0.3.3:
   version "0.3.3"
   resolved "https://registry.yarnpkg.com/uvu/-/uvu-0.3.3.tgz#ad2edd8d2fca50c4dcb39fb2e843f4c15e48ac94"