Index: frontend/node_modules/throat/LICENSE
===================================================================
--- frontend/node_modules/throat/LICENSE	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/throat/LICENSE	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,19 @@
+Copyright (c) 2013 Forbes Lindesay
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
Index: frontend/node_modules/throat/README.md
===================================================================
--- frontend/node_modules/throat/README.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/throat/README.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,65 @@
+# throat
+
+Throttle the parallelism of an asynchronous, promise returning, function / functions. This has special utility when you set the concurrency to `1`. That way you get a mutually exclusive lock.
+
+[Professionally supported throat is now available](https://tidelift.com/subscription/pkg/npm-throat?utm_source=npm-throat&utm_medium=referral&utm_campaign=readme)
+
+[![Build Status](https://img.shields.io/github/actions/workflow/status/ForbesLindesay/throat/test.yml?branch=master&style=for-the-badge)](https://github.com/ForbesLindesay/throat/actions/workflows/test.yml?query=branch%3Amaster)
+[![Coveralls github branch](https://img.shields.io/coveralls/github/ForbesLindesay/throat/master?color=brightgreen&style=for-the-badge)](https://coveralls.io/github/ForbesLindesay/throat)
+[![Rolling Versions](https://img.shields.io/badge/Rolling%20Versions-Enabled-brightgreen?style=for-the-badge)](https://rollingversions.com/ForbesLindesay/throat)
+[![NPM version](https://img.shields.io/npm/v/throat?style=for-the-badge)](https://www.npmjs.com/package/throat)
+
+## Installation
+
+    npm install throat
+
+## API
+
+### throat(concurrency)
+
+This returns a function that acts a bit like a lock (exactly as a lock if concurrency is 1).
+
+Example, only 2 of the following functions will execute at any one time:
+
+```js
+const throat = require('throat')(2);
+
+const resA = throat(async () => {
+  /* async stuff... */
+});
+const resB = throat(async () => {
+  /* async stuff... */
+});
+const resC = throat(async () => {
+  /* async stuff... */
+});
+const resD = throat(async () => {
+  /* async stuff... */
+});
+const resE = throat(async () => {
+  /* async stuff... */
+});
+```
+
+### throat(concurrency, worker)
+
+This returns a function that is an exact copy of `worker` except that it will only execute up to `concurrency` times in parallel before further requests are queued:
+
+```js
+const throat = require('throat');
+
+const input = ['fileA.txt', 'fileB.txt', 'fileC.txt', 'fileD.txt'];
+const data = Promise.all(
+  input.map(throat(2, (fileName) => readFile(fileName)))
+);
+```
+
+Only 2 files will be read at a time, sometimes limiting parallelism in this way can improve scalability.
+
+## Security contact information
+
+To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
+
+## License
+
+MIT
Index: frontend/node_modules/throat/index.d.ts
===================================================================
--- frontend/node_modules/throat/index.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/throat/index.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,32 @@
+/**
+ * Throttle the given function to only run `size` times in parallel.
+ * Extra calls will be queued until one of the earlier calls completes.
+ */
+declare function throat<TResult, TArgs extends any[]>(
+  size: number,
+  fn: (...args: TArgs) => Promise<TResult>
+): (...args: TArgs) => Promise<TResult>;
+
+/**
+ * Throttle the given function to only run `size` times in parallel.
+ * Extra calls will be queued until one of the earlier calls completes.
+ */
+declare function throat<TResult, TArgs extends any[]>(
+  fn: (...args: TArgs) => Promise<TResult>,
+  size: number
+): (...args: TArgs) => Promise<TResult>;
+
+/**
+ * Create a throttle that only allows `size` calls in parallel.
+ * Extra calls will be queued until one of the earlier calls completes.
+ *
+ * To create an exclusive lock, just use a `size` of `1`.
+ */
+declare function throat(
+  size: number
+): <TResult, TArgs extends any[] = []>(
+  fn: (...args: TArgs) => Promise<TResult>,
+  ...args: TArgs
+) => Promise<TResult>;
+export default throat;
+
Index: frontend/node_modules/throat/index.js
===================================================================
--- frontend/node_modules/throat/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/throat/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,145 @@
+'use strict';
+
+function throatInternal(size) {
+  var queue = new Queue();
+  var s = size | 0;
+
+  function run(fn, self, args) {
+    if ((s | 0) !== 0) {
+      s = (s | 0) - 1;
+      return new Promise(function (resolve) {
+        resolve(fn.apply(self, args));
+      }).then(onFulfill, onReject);
+    }
+    return new Promise(function (resolve) {
+      queue.push(new Delayed(resolve, fn, self, args));
+    }).then(runDelayed);
+  }
+  function runDelayed(d) {
+    try {
+      return Promise.resolve(d.fn.apply(d.self, d.args)).then(
+        onFulfill,
+        onReject
+      );
+    } catch (ex) {
+      onReject(ex);
+    }
+  }
+  function onFulfill(result) {
+    release();
+    return result;
+  }
+  function onReject(error) {
+    release();
+    throw error;
+  }
+  function release() {
+    var next = queue.shift();
+    if (next) {
+      next.resolve(next);
+    } else {
+      s = (s | 0) + 1;
+    }
+  }
+
+  return run;
+}
+
+function earlyBound(size, fn) {
+  const run = throatInternal(size | 0);
+  return function () {
+    var args = new Array(arguments.length);
+    for (var i = 0; i < arguments.length; i++) {
+      args[i] = arguments[i];
+    }
+    return run(fn, this, args);
+  };
+}
+function lateBound(size) {
+  const run = throatInternal(size | 0);
+  return function (fn) {
+    if (typeof fn !== 'function') {
+      throw new TypeError(
+        'Expected throat fn to be a function but got ' + typeof fn
+      );
+    }
+    var args = new Array(arguments.length - 1);
+    for (var i = 1; i < arguments.length; i++) {
+      args[i - 1] = arguments[i];
+    }
+    return run(fn, this, args);
+  };
+}
+module.exports = function throat(size, fn) {
+  if (typeof size === 'function') {
+    var temp = fn;
+    fn = size;
+    size = temp;
+  }
+  if (typeof size !== 'number') {
+    throw new TypeError(
+      'Expected throat size to be a number but got ' + typeof size
+    );
+  }
+  if (fn !== undefined && typeof fn !== 'function') {
+    throw new TypeError(
+      'Expected throat fn to be a function but got ' + typeof fn
+    );
+  }
+  if (typeof fn === 'function') {
+    return earlyBound(size | 0, fn);
+  } else {
+    return lateBound(size | 0);
+  }
+};
+
+module.exports.default = module.exports;
+
+function Delayed(resolve, fn, self, args) {
+  this.resolve = resolve;
+  this.fn = fn;
+  this.self = self || null;
+  this.args = args;
+}
+
+var blockSize = 64;
+function Queue() {
+  this._s1 = [];
+  this._s2 = [];
+  this._shiftBlock = this._pushBlock = new Array(blockSize);
+  this._pushIndex = 0;
+  this._shiftIndex = 0;
+}
+
+Queue.prototype.push = function (value) {
+  if (this._pushIndex === blockSize) {
+    this._pushIndex = 0;
+    this._s1[this._s1.length] = this._pushBlock = new Array(blockSize);
+  }
+  this._pushBlock[this._pushIndex++] = value;
+};
+
+Queue.prototype.shift = function () {
+  if (this._shiftIndex === blockSize) {
+    var s2 = this._s2;
+    if (s2.length === 0) {
+      var s1 = this._s1;
+      if (s1.length === 0) {
+        return undefined;
+      }
+      this._s1 = s2;
+      s2 = this._s2 = s1.reverse();
+    }
+    this._shiftIndex = 0;
+    this._shiftBlock = s2.pop();
+  }
+  if (
+    this._pushBlock === this._shiftBlock &&
+    this._pushIndex === this._shiftIndex
+  ) {
+    return undefined;
+  }
+  var result = this._shiftBlock[this._shiftIndex];
+  this._shiftBlock[this._shiftIndex++] = null;
+  return result;
+};
Index: frontend/node_modules/throat/index.js.flow
===================================================================
--- frontend/node_modules/throat/index.js.flow	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/throat/index.js.flow	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,7 @@
+// @flow
+
+declare function throat<TResult, TFn: (...args: Array<any>) => Promise<TResult>>(size: number, fn: TFn): TFn;
+declare function throat<TResult, TFn: (...args: Array<any>) => Promise<TResult>>(fn: TFn, size: number): TFn;
+declare function throat(size: number): <TResult>(fn: () => Promise<TResult>) => Promise<TResult>;
+
+module.exports = throat;
Index: frontend/node_modules/throat/package.json
===================================================================
--- frontend/node_modules/throat/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/throat/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,45 @@
+{
+  "name": "throat",
+  "version": "6.0.2",
+  "description": "Throttle the parallelism of an asynchronous (promise returning) function / functions",
+  "keywords": [
+    "promise",
+    "aplus",
+    "then",
+    "throttle",
+    "concurrency",
+    "parallelism",
+    "limit"
+  ],
+  "files": [
+    "index.d.ts",
+    "index.js",
+    "index.js.flow"
+  ],
+  "devDependencies": {
+    "coveralls": "^3.0.0",
+    "istanbul": "^0.4.5",
+    "jest": "^22.1.4",
+    "promise": "^8.0.0",
+    "sauce-test": "^1.0.0",
+    "test-result": "^2.0.0",
+    "testit": "^3.1.0",
+    "typescript": "^3.4.5"
+  },
+  "jest": {
+    "testEnvironment": "node"
+  },
+  "scripts": {
+    "tsc": "tsc --noEmit",
+    "test": "node test/index.js && npm run test:types",
+    "test:types": "jest",
+    "coverage": "istanbul cover test/index.js",
+    "coveralls": "npm run coverage && cat ./coverage/lcov.info | coveralls"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/ForbesLindesay/throat.git"
+  },
+  "author": "ForbesLindesay",
+  "license": "MIT"
+}
