gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated (d9297f3d -> 035b3fda)


From: gnunet
Subject: [taler-wallet-core] branch master updated (d9297f3d -> 035b3fda)
Date: Wed, 20 Nov 2019 20:02:55 +0100

This is an automated email from the git hooks/post-receive script.

dold pushed a change to branch master
in repository wallet-core.

    from d9297f3d work on CLI
     new faedf697 idb-bridge: fix typo in rollback
     new 553da649 WIP: simplify DB queries and error handling
     new 7974a762 no more commander.js
     new 035b3fda version upgrade and formatting

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 package.json                              |    7 +-
 packages/idb-bridge/.vscode/settings.json |    3 +-
 packages/idb-bridge/package.json          |    4 +-
 packages/idb-bridge/src/MemoryBackend.ts  |    2 +-
 packages/idb-bridge/src/tree/b+tree.ts    |    4 +-
 packages/idb-bridge/yarn.lock             |    8 +-
 src/dbTypes.ts                            |   90 +-
 src/headless/clk.ts                       |   64 +-
 src/headless/taler-wallet-cli.ts          |  108 +-
 src/helpers.ts                            |   14 +
 src/logging.ts                            |  351 -------
 src/query.ts                              | 1122 +++++----------------
 src/wallet.ts                             | 1566 +++++++++++++++--------------
 src/walletTypes.ts                        |   58 +-
 src/webex/messages.ts                     |   16 -
 src/webex/pages/error.html                |   18 -
 src/webex/pages/error.tsx                 |  129 ---
 src/webex/pages/logs.html                 |   27 -
 src/webex/pages/logs.tsx                  |   86 --
 src/webex/renderHtml.tsx                  |    6 +-
 src/webex/wxApi.ts                        |   32 -
 src/webex/wxBackend.ts                    |   42 -
 tsconfig.json                             |    3 -
 webpack.config.js                         |    2 -
 yarn.lock                                 | 1289 ++++++++++++++----------
 25 files changed, 2066 insertions(+), 2985 deletions(-)
 delete mode 100644 src/logging.ts
 delete mode 100644 src/webex/pages/error.html
 delete mode 100644 src/webex/pages/error.tsx
 delete mode 100644 src/webex/pages/logs.html
 delete mode 100644 src/webex/pages/logs.tsx

diff --git a/package.json b/package.json
index 8e7a5b35..980274e0 100644
--- a/package.json
+++ b/package.json
@@ -46,7 +46,7 @@
     "react": "^16.8.5",
     "react-dom": "^16.8.5",
     "structured-clone": "^0.2.2",
-    "terser-webpack-plugin": "^1.2.3",
+    "terser-webpack-plugin": "^2.2.1",
     "through2": "3.0.1",
     "tslint": "^5.19.0",
     "typedoc": "^0.15.0",
@@ -60,11 +60,10 @@
     "webpack-merge": "^4.2.2"
   },
   "dependencies": {
-    "@types/chrome": "^0.0.88",
+    "@types/chrome": "^0.0.91",
     "@types/urijs": "^1.19.3",
     "axios": "^0.19.0",
-    "commander": "^3.0.1",
-    "idb-bridge": "^0.0.10",
+    "idb-bridge": "^0.0.11",
     "qrcode-generator": "^1.4.3",
     "source-map-support": "^0.5.12",
     "urijs": "^1.18.10"
diff --git a/packages/idb-bridge/.vscode/settings.json 
b/packages/idb-bridge/.vscode/settings.json
index ff30c446..ec71f9aa 100644
--- a/packages/idb-bridge/.vscode/settings.json
+++ b/packages/idb-bridge/.vscode/settings.json
@@ -1,3 +1,4 @@
 {
-    "editor.tabSize": 2
+    "editor.tabSize": 2,
+    "typescript.tsdk": "node_modules/typescript/lib"
 }
\ No newline at end of file
diff --git a/packages/idb-bridge/package.json b/packages/idb-bridge/package.json
index f0072a6b..7ac39726 100644
--- a/packages/idb-bridge/package.json
+++ b/packages/idb-bridge/package.json
@@ -1,6 +1,6 @@
 {
   "name": "idb-bridge",
-  "version": "0.0.10",
+  "version": "0.0.11",
   "description": "IndexedDB implementation that uses SQLite3 as storage",
   "main": "./build/index.js",
   "types": "./build/index.d.ts",
@@ -13,6 +13,6 @@
   },
   "devDependencies": {
     "ava": "2.3.0",
-    "typescript": "^3.4.5"
+    "typescript": "^3.7.0"
   }
 }
diff --git a/packages/idb-bridge/src/MemoryBackend.ts 
b/packages/idb-bridge/src/MemoryBackend.ts
index 99582a1b..bb13a022 100644
--- a/packages/idb-bridge/src/MemoryBackend.ts
+++ b/packages/idb-bridge/src/MemoryBackend.ts
@@ -1445,7 +1445,7 @@ export class MemoryBackend implements Backend {
       objectStore.modifiedKeyGenerator = undefined;
       objectStore.modifiedIndexes = {};
 
-      for (const indexName in Object.keys(
+      for (const indexName of Object.keys(
         db.committedSchema.objectStores[objectStoreName].indexes,
       )) {
         const index = objectStore.committedIndexes[indexName];
diff --git a/packages/idb-bridge/src/tree/b+tree.ts 
b/packages/idb-bridge/src/tree/b+tree.ts
index 01187cdc..783c6b04 100644
--- a/packages/idb-bridge/src/tree/b+tree.ts
+++ b/packages/idb-bridge/src/tree/b+tree.ts
@@ -823,7 +823,7 @@ if (Symbol && Symbol.iterator) // iterator is equivalent to 
entries()
 (BTree as any).prototype.setRange = BTree.prototype.setPairs;
 (BTree as any).prototype.add = BTree.prototype.set;
 
-function iterator<T>(next: () => {done:boolean,value?:T} = (() => ({ 
done:true, value:undefined }))): IterableIterator<T> {
+function iterator<T>(next: () => {done?:boolean,value?:T} = (() => ({ 
done:true, value:undefined }))): IterableIterator<T> {
   var result: any = { next };
   if (Symbol && Symbol.iterator)
     result[Symbol.iterator] = function() { return this; };
@@ -1348,4 +1348,4 @@ function check(fact: boolean, ...args: any[]) {
 }
 
 /** A BTree frozen in the empty state. */
-export const EmptyBTree = (() => { let t = new BTree(); t.freeze(); return t; 
})();
\ No newline at end of file
+export const EmptyBTree = (() => { let t = new BTree(); t.freeze(); return t; 
})();
diff --git a/packages/idb-bridge/yarn.lock b/packages/idb-bridge/yarn.lock
index 8e59a7f8..9a5daad3 100644
--- a/packages/idb-bridge/yarn.lock
+++ b/packages/idb-bridge/yarn.lock
@@ -2534,10 +2534,10 @@ typedarray-to-buffer@^3.1.5:
   dependencies:
     is-typedarray "^1.0.0"
 
-typescript@^3.4.5:
-  version "3.5.3"
-  resolved 
"https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977";
-  integrity 
sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
+typescript@^3.7.0:
+  version "3.7.2"
+  resolved 
"https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb";
+  integrity 
sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ==
 
 uid2@0.0.3:
   version "0.0.3"
diff --git a/src/dbTypes.ts b/src/dbTypes.ts
index 28893b8e..0d54069e 100644
--- a/src/dbTypes.ts
+++ b/src/dbTypes.ts
@@ -36,6 +36,7 @@ import {
 } from "./talerTypes";
 
 import { Index, Store } from "./query";
+import { Timestamp, OperationError } from "./walletTypes";
 
 /**
  * Current database version, should be incremented
@@ -310,13 +311,10 @@ export class DenominationRecord {
 }
 
 /**
- * Exchange record as stored in the wallet's database.
+ * Details about the exchange that we only know after
+ * querying /keys and /wire.
  */
-export interface ExchangeRecord {
-  /**
-   * Base url of the exchange.
-   */
-  baseUrl: string;
+export interface ExchangeDetails {
   /**
    * Master public key of the exchange.
    */
@@ -331,22 +329,60 @@ export interface ExchangeRecord {
    */
   currency: string;
 
+  /**
+   * Last observed protocol version.
+   */
+  protocolVersion: string;
+
   /**
    * Timestamp for last update.
    */
-  lastUpdateTime: number;
+  lastUpdateTime: Timestamp;
+}
+
+export enum ExchangeUpdateStatus {
+  NONE = "none",
+  FETCH_KEYS = "fetch_keys",
+  FETCH_WIRE = "fetch_wire",
+}
+
+export interface ExchangeBankAccount {
+  url: string;
+}
 
+export interface ExchangeWireInfo {
+  feesForType: { [wireMethod: string]: WireFee[] };
+  accounts: ExchangeBankAccount[];
+}
+
+/**
+ * Exchange record as stored in the wallet's database.
+ */
+export interface ExchangeRecord {
   /**
-   * When did we actually use this exchange last (in milliseconds).  If we
-   * never used the exchange for anything but just updated its info, this is
-   * set to 0.  (Currently only updated when reserves are created.)
+   * Base url of the exchange.
    */
-  lastUsedTime: number;
+  baseUrl: string;
 
   /**
-   * Last observed protocol version.
+   * Details, once known.
    */
-  protocolVersion?: string;
+  details: ExchangeDetails | undefined;
+
+  /**
+   * Mapping from wire method type to the wire fee.
+   */
+  wireInfo: ExchangeWireInfo | undefined;
+
+  /**
+   * Time when the update to the exchange has been started or
+   * undefined if no update is in progress.
+   */
+  updateStarted: Timestamp | undefined;
+
+  updateStatus: ExchangeUpdateStatus;
+
+  lastError?: OperationError;
 }
 
 /**
@@ -554,21 +590,6 @@ export class ProposalDownloadRecord {
   static checked: (obj: any) => ProposalDownloadRecord;
 }
 
-/**
- * Wire fees for an exchange.
- */
-export interface ExchangeWireFeesRecord {
-  /**
-   * Base URL of the exchange.
-   */
-  exchangeBaseUrl: string;
-
-  /**
-   * Mapping from wire method type to the wire fee.
-   */
-  feesForType: { [wireMethod: string]: WireFee[] };
-}
-
 /**
  * Status of a tip we got from a merchant.
  */
@@ -931,12 +952,6 @@ export namespace Stores {
     constructor() {
       super("exchanges", { keyPath: "baseUrl" });
     }
-
-    pubKeyIndex = new Index<string, ExchangeRecord>(
-      this,
-      "pubKeyIndex",
-      "masterPublicKey",
-    );
   }
 
   class CoinsStore extends Store<CoinRecord> {
@@ -1034,12 +1049,6 @@ export namespace Stores {
     }
   }
 
-  class ExchangeWireFeesStore extends Store<ExchangeWireFeesRecord> {
-    constructor() {
-      super("exchangeWireFees", { keyPath: "exchangeBaseUrl" });
-    }
-  }
-
   class ReservesStore extends Store<ReserveRecord> {
     constructor() {
       super("reserves", { keyPath: "reserve_pub" });
@@ -1094,7 +1103,6 @@ export namespace Stores {
   export const config = new ConfigStore();
   export const currencies = new CurrenciesStore();
   export const denominations = new DenominationsStore();
-  export const exchangeWireFees = new ExchangeWireFeesStore();
   export const exchanges = new ExchangeStore();
   export const precoins = new Store<PreCoinRecord>("precoins", {
     keyPath: "coinPub",
diff --git a/src/headless/clk.ts b/src/headless/clk.ts
index 642a1bef..f66d609e 100644
--- a/src/headless/clk.ts
+++ b/src/headless/clk.ts
@@ -20,7 +20,6 @@
 import process = require("process");
 import path = require("path");
 import readline = require("readline");
-import { symlinkSync } from "fs";
 
 class Converter<T> {}
 
@@ -54,6 +53,7 @@ interface ArgumentDef {
   name: string;
   conv: Converter<any>;
   args: ArgumentArgs<any>;
+  required: boolean;
 }
 
 interface SubcommandDef {
@@ -181,7 +181,7 @@ export class CommandGroup<GN extends keyof any, TG> {
     return this as any;
   }
 
-  argument<N extends keyof any, V>(
+  requiredArgument<N extends keyof any, V>(
     name: N,
     conv: Converter<V>,
     args: ArgumentArgs<V> = {},
@@ -190,6 +190,22 @@ export class CommandGroup<GN extends keyof any, TG> {
       args: args,
       conv: conv,
       name: name as string,
+      required: true,
+    };
+    this.arguments.push(argDef);
+    return this as any;
+  }
+
+  maybeArgument<N extends keyof any, V>(
+    name: N,
+    conv: Converter<V>,
+    args: ArgumentArgs<V> = {},
+  ): CommandGroup<GN, TG & SubRecord<GN, N, V | undefined>> {
+    const argDef: ArgumentDef = {
+      args: args,
+      conv: conv,
+      name: name as string,
+      required: false,
     };
     this.arguments.push(argDef);
     return this as any;
@@ -401,10 +417,25 @@ export class CommandGroup<GN extends keyof any, TG> {
           process.exit(-1);
           throw Error("not reached");
         }
+        myArgs[d.name] = unparsedArgs[i];
         posArgIndex++;
       }
     }
 
+    for (let i = posArgIndex; i < this.arguments.length; i++) {
+      const d = this.arguments[i];
+      const n = this.name ?? progname;
+      if (d.required) {
+        if (d.args.default !== undefined) {
+          myArgs[d.name] = d.args.default;
+        } else {
+          console.error(`error: missing positional argument '${d.name}' for 
${n}`);
+          process.exit(-1);
+          throw Error("not reached");
+        }
+      }
+    }
+
     for (let option of this.options) {
       if (option.isFlag == false && option.required == true) {
         if (!foundOptions[option.name]) {
@@ -433,9 +464,7 @@ export class CommandGroup<GN extends keyof any, TG> {
         unparsedArgs.slice(i + 1),
         parsedArgs,
       );
-    }
-
-    if (this.myAction) {
+    } else if (this.myAction) {
       this.myAction(parsedArgs);
     } else {
       this.printHelp(progname, parents);
@@ -513,18 +542,35 @@ export class Program<PN extends keyof any, T> {
   }
 
   /**
-   * Add a positional argument to the program.
+   * Add a required positional argument to the program.
    */
-  argument<N extends keyof any, V>(
+  requiredArgument<N extends keyof any, V>(
     name: N,
     conv: Converter<V>,
     args: ArgumentArgs<V> = {},
   ): Program<N, T & SubRecord<PN, N, V>> {
-    this.mainCommand.argument(name, conv, args);
+    this.mainCommand.requiredArgument(name, conv, args);
+    return this as any;
+  }
+
+  /**
+   * Add an optional argument to the program.
+   */
+  maybeArgument<N extends keyof any, V>(
+    name: N,
+    conv: Converter<V>,
+    args: ArgumentArgs<V> = {},
+  ): Program<N, T & SubRecord<PN, N, V | undefined>> {
+    this.mainCommand.maybeArgument(name, conv, args);
     return this as any;
   }
 }
 
+export type GetArgType<T> =
+  T extends Program<any, infer AT> ? AT :
+  T extends CommandGroup<any, infer AT> ? AT :
+  any;
+
 export function program<PN extends keyof any>(
   argKey: PN,
   args: ProgramArgs = {},
@@ -532,6 +578,8 @@ export function program<PN extends keyof any>(
   return new Program(argKey as string, args);
 }
 
+
+
 export function prompt(question: string): Promise<string> {
   const stdinReadline = readline.createInterface({
     input: process.stdin,
diff --git a/src/headless/taler-wallet-cli.ts b/src/headless/taler-wallet-cli.ts
index 41f68319..06235d0b 100644
--- a/src/headless/taler-wallet-cli.ts
+++ b/src/headless/taler-wallet-cli.ts
@@ -97,6 +97,28 @@ const walletCli = clk
     help: "Enable verbose output.",
   });
 
+type WalletCliArgsType = clk.GetArgType<typeof walletCli>;
+
+async function withWallet<T>(
+  walletCliArgs: WalletCliArgsType,
+  f: (w: Wallet) => Promise<T>,
+): Promise<T> {
+  applyVerbose(walletCliArgs.wallet.verbose);
+  const wallet = await getDefaultNodeWallet({
+    persistentStoragePath: walletDbPath,
+  });
+  try {
+    await wallet.fillDefaults();
+    const ret = await f(wallet);
+    return ret;
+  } catch (e) {
+    console.error("caught exception:", e);
+    process.exit(1);
+  } finally {
+    wallet.stop();
+  }
+}
+
 walletCli
   .subcommand("testPayCmd", "test-pay", { help: "create contract and pay" })
   .requiredOption("amount", ["-a", "--amount"], clk.STRING)
@@ -135,15 +157,11 @@ walletCli
 walletCli
   .subcommand("", "balance", { help: "Show wallet balance." })
   .action(async args => {
-    applyVerbose(args.wallet.verbose);
     console.log("balance command called");
-    const wallet = await getDefaultNodeWallet({
-      persistentStoragePath: walletDbPath,
+    withWallet(args, async (wallet) => {
+      const balance = await wallet.getBalances();
+      console.log(JSON.stringify(balance, undefined, 2));
     });
-    console.log("got wallet");
-    const balance = await wallet.getBalances();
-    console.log(JSON.stringify(balance, undefined, 2));
-    process.exit(0);
   });
 
 walletCli
@@ -153,29 +171,19 @@ walletCli
   .requiredOption("limit", ["--limit"], clk.STRING)
   .requiredOption("contEvt", ["--continue-with"], clk.STRING)
   .action(async args => {
-    applyVerbose(args.wallet.verbose);
-    console.log("history command called");
-    const wallet = await getDefaultNodeWallet({
-      persistentStoragePath: walletDbPath,
+    withWallet(args, async (wallet) => {
+      const history = await wallet.getHistory();
+      console.log(JSON.stringify(history, undefined, 2));
     });
-    console.log("got wallet");
-    const history = await wallet.getHistory();
-    console.log(JSON.stringify(history, undefined, 2));
-    process.exit(0);
   });
 
 walletCli
   .subcommand("", "pending", { help: "Show pending operations." })
   .action(async args => {
-    applyVerbose(args.wallet.verbose);
-    console.log("history command called");
-    const wallet = await getDefaultNodeWallet({
-      persistentStoragePath: walletDbPath,
+    withWallet(args, async (wallet) => {
+      const pending = await wallet.getPendingOperations();
+      console.log(JSON.stringify(pending, undefined, 2));
     });
-    console.log("got wallet");
-    const pending = await wallet.getPendingOperations();
-    console.log(JSON.stringify(pending, undefined, 2));
-    process.exit(0);
   });
 
 async function asyncSleep(milliSeconds: number): Promise<void> {
@@ -184,6 +192,16 @@ async function asyncSleep(milliSeconds: number): 
Promise<void> {
   });
 }
 
+walletCli
+  .subcommand("runPendingOpt", "run-pending", {
+    help: "Run pending operations."
+  })
+  .action(async (args) => {
+    withWallet(args, async (wallet) => {
+      await wallet.processPending();
+    });
+  });
+
 walletCli
   .subcommand("testMerchantQrcodeCmd", "test-merchant-qrcode")
   .requiredOption("amount", ["-a", "--amount"], clk.STRING, {
@@ -279,7 +297,7 @@ walletCli
 
 walletCli
   .subcommand("withdrawUriCmd", "withdraw-uri")
-  .argument("withdrawUri", clk.STRING)
+  .requiredArgument("withdrawUri", clk.STRING)
   .action(async args => {
     applyVerbose(args.wallet.verbose);
     const cmdArgs = args.withdrawUriCmd;
@@ -318,7 +336,7 @@ walletCli
 
 walletCli
   .subcommand("tipUriCmd", "tip-uri")
-  .argument("uri", clk.STRING)
+  .requiredArgument("uri", clk.STRING)
   .action(async args => {
     applyVerbose(args.wallet.verbose);
     const tipUri = args.tipUriCmd.uri;
@@ -334,7 +352,7 @@ walletCli
 
 walletCli
   .subcommand("refundUriCmd", "refund-uri")
-  .argument("uri", clk.STRING)
+  .requiredArgument("uri", clk.STRING)
   .action(async args => {
     applyVerbose(args.wallet.verbose);
     const refundUri = args.refundUriCmd.uri;
@@ -346,20 +364,38 @@ walletCli
     wallet.stop();
   });
 
-const exchangesCli = walletCli
-  .subcommand("exchangesCmd", "exchanges", {
-    help: "Manage exchanges."
-  });
-
-exchangesCli.subcommand("exchangesListCmd", "list", {
-  help: "List known exchanges."
+const exchangesCli = walletCli.subcommand("exchangesCmd", "exchanges", {
+  help: "Manage exchanges.",
 });
 
-exchangesCli.subcommand("exchangesListCmd", "update");
+exchangesCli
+  .subcommand("exchangesListCmd", "list", {
+    help: "List known exchanges.",
+  })
+  .action(async args => {
+    console.log("Listing exchanges ...");
+    withWallet(args, async (wallet) => {
+      const exchanges = await wallet.getExchanges();
+      console.log("exchanges", exchanges);
+    });
+  });
+
+exchangesCli
+  .subcommand("exchangesUpdateCmd", "update", {
+    help: "Update or add an exchange by base URL.",
+  })
+  .requiredArgument("url", clk.STRING, {
+    help: "Base URL of the exchange.",
+  })
+  .action(async args => {
+    withWallet(args, async (wallet) => {
+      const res = await 
wallet.updateExchangeFromUrl(args.exchangesUpdateCmd.url);
+    });
+  });
 
 walletCli
   .subcommand("payUriCmd", "pay-uri")
-  .argument("url", clk.STRING)
+  .requiredArgument("url", clk.STRING)
   .flag("autoYes", ["-y", "--yes"])
   .action(async args => {
     applyVerbose(args.wallet.verbose);
@@ -374,7 +410,7 @@ walletCli
   });
 
 const testCli = walletCli.subcommand("testingArgs", "testing", {
-  help: "Subcommands for testing GNU Taler deployments."
+  help: "Subcommands for testing GNU Taler deployments.",
 });
 
 testCli
diff --git a/src/helpers.ts b/src/helpers.ts
index 7cd17649..a063db16 100644
--- a/src/helpers.ts
+++ b/src/helpers.ts
@@ -25,6 +25,7 @@ import { AmountJson } from "./amounts";
 import * as Amounts from "./amounts";
 
 import URI = require("urijs");
+import { Timestamp } from "./walletTypes";
 
 /**
  * Show an amount in a form suitable for the user.
@@ -125,6 +126,19 @@ export function getTalerStampSec(stamp: string): number | 
null {
   return parseInt(m[1], 10);
 }
 
+/**
+ * Extract a timestamp from a Taler timestamp string.
+ */
+export function extractTalerStamp(stamp: string): Timestamp | undefined {
+  const m = stamp.match(/\/?Date\(([0-9]*)\)\/?/);
+  if (!m || !m[1]) {
+    return undefined;
+  }
+  return {
+    t_ms: parseInt(m[1], 10) * 1000,
+  };
+}
+
 /**
  * Check if a timestamp is in the right format.
  */
diff --git a/src/logging.ts b/src/logging.ts
deleted file mode 100644
index 4e7b60b9..00000000
--- a/src/logging.ts
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- This file is part of TALER
- (C) 2016 Inria
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * Configurable logging.  Allows to log persistently to a database.
- */
-
-import {
-  QueryRoot,
-  Store,
-} from "./query";
-import { openPromise } from "./promiseUtils";
-
-/**
- * Supported log levels.
- */
-export type Level = "error" | "debug" | "info" | "warn";
-
-// Right now, our debug/info/warn/debug loggers just use the console based
-// loggers.  This might change in the future.
-
-function makeInfo() {
-  return console.info.bind(console, "%o");
-}
-
-function makeWarn() {
-  return console.warn.bind(console, "%o");
-}
-
-function makeError() {
-  return console.error.bind(console, "%o");
-}
-
-function makeDebug() {
-  return console.log.bind(console, "%o");
-}
-
-/**
- * Log a message using the configurable logger.
- */
-export async function log(msg: string, level: Level = "info"): Promise<void> {
-  const ci = getCallInfo(2);
-  return record(level, msg, undefined, ci.file, ci.line, ci.column);
-}
-
-function getCallInfo(level: number) {
-  // see https://github.com/v8/v8/wiki/Stack-Trace-API
-  const stack = Error().stack;
-  if (!stack) {
-    return unknownFrame;
-  }
-  const lines = stack.split("\n");
-  return parseStackLine(lines[level + 1]);
-}
-
-interface Frame {
-  column?: number;
-  file?: string;
-  line?: number;
-  method?: string;
-}
-
-const unknownFrame: Frame = {
-  column: 0,
-  file: "(unknown)",
-  line: 0,
-  method: "(unknown)",
-};
-
-/**
- * Adapted from https://github.com/errwischt/stacktrace-parser.
- */
-function parseStackLine(stackLine: string): Frame {
-  // tslint:disable-next-line:max-line-length
-  const chrome = /^\s*at (?:(?:(?:Anonymous function)?|((?:\[object 
object\])?\S+(?: \[as \S+\])?)) 
)?\(?((?:file|http|https):.*?):(\d+)(?::(\d+))?\)?\s*$/i;
-  const gecko = /^(?:\s*([^@]*)(?:\((.*?)\))?@)?(\S.*?):(\d+)(?::(\d+))?\s*$/i;
-  const node  = /^\s*at (?:((?:\[object object\])?\S+(?: \[as \S+\])?) 
)?\(?(.*?):(\d+)(?::(\d+))?\)?\s*$/i;
-  let parts;
-
-  parts = gecko.exec(stackLine);
-  if (parts) {
-    const f: Frame = {
-        column: parts[5] ? +parts[5] : undefined,
-        file: parts[3],
-        line: +parts[4],
-        method: parts[1] || "(unknown)",
-    };
-    return f;
-  }
-
-  parts = chrome.exec(stackLine);
-  if (parts) {
-    const f: Frame = {
-        column: parts[4] ? +parts[4] : undefined,
-        file: parts[2],
-        line: +parts[3],
-        method: parts[1] || "(unknown)",
-    };
-    return f;
-  }
-
-  parts = node.exec(stackLine);
-  if (parts) {
-    const f: Frame = {
-        column: parts[4] ? +parts[4] : undefined,
-        file: parts[2],
-        line: +parts[3],
-        method: parts[1] || "(unknown)",
-    };
-    return f;
-  }
-
-  return unknownFrame;
-}
-
-
-let db: IDBDatabase|undefined;
-
-/**
- * A structured log entry as stored in the database.
- */
-export interface LogEntry {
-  /**
-   * Soure code column where the error occured.
-   */
-  col?: number;
-  /**
-   * Additional detail for the log statement.
-   */
-  detail?: string;
-  /**
-   * Id of the log entry, used as primary
-   * key for the database.
-   */
-  id?: number;
-  /**
-   * Log level, see [[Level}}.
-   */
-  level: string;
-  /**
-   * Line where the log was created from.
-   */
-  line?: number;
-  /**
-   * The actual log message.
-   */
-  msg: string;
-  /**
-   * The source file where the log enctry
-   * was created from.
-   */
-  source?: string;
-  /**
-   * Time when the log entry was created.
-   */
-  timestamp: number;
-}
-
-/**
- * Get all logs.  Only use for debugging, since this returns all logs ever made
- * at once without pagination.
- */
-export async function getLogs(): Promise<LogEntry[]> {
-  if (!db) {
-    db = await openLoggingDb();
-  }
-  return await new QueryRoot(db).iter(logsStore).toArray();
-}
-
-/**
- * The barrier ensures that only one DB write is scheduled against the log db
- * at the same time, so that the DB can stay responsive.  This is a bit of a
- * design problem with IndexedDB, it doesn't guarantee fairness.
- */
-let barrier: any;
-
-/**
- * Record an exeption in the log.
- */
-export async function recordException(msg: string, e: any): Promise<void> {
-  let stack: string|undefined;
-  let frame: Frame|undefined;
-  try {
-    stack = e.stack;
-    if (stack) {
-      const lines = stack.split("\n");
-      frame = parseStackLine(lines[1]);
-    }
-  } catch (e) {
-    // ignore
-  }
-  if (!frame) {
-    frame = unknownFrame;
-  }
-  return record("error", e.toString(), stack, frame.file, frame.line, 
frame.column);
-}
-
-
-/**
- * Cache for reports.  Also used when something is so broken that we can't even
- * access the database.
- */
-const reportCache: { [reportId: string]: any } = {};
-
-
-/**
- * Get a UUID that does not use cryptographically secure randomness.
- * Formatted as RFC4122 version 4 UUID.
- */
-function getInsecureUuid() {
-  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c: string) 
=> {
-    const r = Math.random() * 16 | 0;
-    const v = c === "x" ? r : (r & 0x3 | 0x8);
-    return v.toString(16);
-  });
-}
-
-
-/**
- * Store a report and return a unique identifier to retrieve it later.
- */
-export async function storeReport(report: any): Promise<string> {
-  const uid = getInsecureUuid();
-  reportCache[uid] = report;
-  return uid;
-}
-
-
-/**
- * Retrieve a report by its unique identifier.
- */
-export async function getReport(reportUid: string): Promise<any> {
-  return reportCache[reportUid];
-}
-
-
-/**
- * Record a log entry in the database.
- */
-export async function record(level: Level,
-                             msg: string,
-                             detail?: string,
-                             source?: string,
-                             line?: number,
-                             col?: number): Promise<void> {
-  if (typeof indexedDB === "undefined") {
-    console.log("can't access DB for logging in this context");
-    console.log("log was", { level, msg, detail, source, line, col });
-    return;
-  }
-
-  let myBarrier: any;
-
-  if (barrier) {
-    const p = barrier.promise;
-    myBarrier = barrier = openPromise();
-    await p;
-  } else {
-    myBarrier = barrier = openPromise();
-  }
-
-  try {
-    if (!db) {
-      db = await openLoggingDb();
-    }
-
-    const count = await new QueryRoot(db).count(logsStore);
-
-    if (count > 1000) {
-      await new QueryRoot(db).deleteIf(logsStore, (e, i) => (i < 200));
-    }
-
-    const entry: LogEntry = {
-      col,
-      detail,
-      level,
-      line,
-      msg,
-      source,
-      timestamp: new Date().getTime(),
-    };
-    await new QueryRoot(db).put(logsStore, entry);
-  } finally {
-    await Promise.resolve().then(() => myBarrier.resolve());
-  }
-}
-
-const loggingDbVersion = 2;
-
-const logsStore: Store<LogEntry> = new Store<LogEntry>("logs");
-
-/**
- * Get a handle to the IndexedDB used to store
- * logs.
- */
-export function openLoggingDb(): Promise<IDBDatabase> {
-  return new Promise<IDBDatabase>((resolve, reject) => {
-    const req = indexedDB.open("taler-logging", loggingDbVersion);
-    req.onerror = (e) => {
-      reject(e);
-    };
-    req.onsuccess = (e) => {
-      resolve(req.result);
-    };
-    req.onupgradeneeded = (e) => {
-      const resDb = req.result;
-      if (e.oldVersion !== 0) {
-        try {
-          resDb.deleteObjectStore("logs");
-        } catch (e) {
-          console.error(e);
-        }
-      }
-      resDb.createObjectStore("logs", { keyPath: "id", autoIncrement: true });
-      resDb.createObjectStore("reports", { keyPath: "uid", autoIncrement: 
false });
-    };
-  });
-}
-
-/**
- * Log a message at severity info.
- */
-export const info = makeInfo();
-
-/**
- * Log a message at severity debug.
- */
-export const debug = makeDebug();
-
-/**
- * Log a message at severity warn.
- */
-export const warn = makeWarn();
-
-/**
- * Log a message at severity error.
- */
-export const error = makeError();
diff --git a/src/query.ts b/src/query.ts
index 7c939046..f510da55 100644
--- a/src/query.ts
+++ b/src/query.ts
@@ -1,3 +1,5 @@
+import { openPromise } from "./promiseUtils";
+
 /*
  This file is part of TALER
  (C) 2016 GNUnet e.V.
@@ -20,9 +22,6 @@
  * @author Florian Dold
  */
 
- import { openPromise } from "./promiseUtils";
-import { join } from "path";
-
 /**
  * Result of an inner join.
  */
@@ -63,928 +62,343 @@ export interface IndexOptions {
   multiEntry?: boolean;
 }
 
-/**
- * Definition of an index.
- */
-export class Index<S extends IDBValidKey, T> {
-  /**
-   * Name of the store that this index is associated with.
-   */
-  storeName: string;
-
-  /**
-   * Options to use for the index.
-   */
-  options: IndexOptions;
-
-  constructor(
-    s: Store<T>,
-    public indexName: string,
-    public keyPath: string | string[],
-    options?: IndexOptions,
-  ) {
-    const defaultOptions = {
-      multiEntry: false,
+function requestToPromise(req: IDBRequest): Promise<any> {
+  return new Promise((resolve, reject) => {
+    req.onsuccess = () => {
+      resolve(req.result);
     };
-    this.options = { ...defaultOptions, ...(options || {}) };
-    this.storeName = s.name;
-  }
-
-  /**
-   * We want to have the key type parameter in use somewhere,
-   * because otherwise the compiler complains.  In iterIndex the
-   * key type is pretty useful.
-   */
-  protected _dummyKey: S | undefined;
+    req.onerror = () => {
+      reject(req.error);
+    };
+  });
 }
 
-/**
- * Stream that can be filtered, reduced or joined
- * with indices.
- */
-export interface QueryStream<T> {
-  /**
-   * Join the current query with values from an index.
-   * The left side of the join is extracted via a function from the stream's
-   * result, the right side of the join is the key of the index.
-   */
-  indexJoin<S, I extends IDBValidKey>(
-    index: Index<I, S>,
-    keyFn: (obj: T) => I,
-  ): QueryStream<JoinResult<T, S>>;
-  /**
-   * Join the current query with values from an index, and keep values in the
-   * current stream that don't have a match.  The left side of the join is
-   * extracted via a function from the stream's result, the right side of the
-   * join is the key of the index.
-   */
-  indexJoinLeft<S, I extends IDBValidKey>(
-    index: Index<I, S>,
-    keyFn: (obj: T) => I,
-  ): QueryStream<JoinLeftResult<T, S>>;
-  /**
-   * Join the current query with values from another object store.
-   * The left side of the join is extracted via a function over the current 
query,
-   * the right side of the join is the key of the object store.
-   */
-  keyJoin<S, I extends IDBValidKey>(
-    store: Store<S>,
-    keyFn: (obj: T) => I,
-  ): QueryStream<JoinResult<T, S>>;
-
-  /**
-   * Only keep elements in the result stream for which the predicate returns
-   * true.
-   */
-  filter(f: (x: T) => boolean): QueryStream<T>;
-
-  /**
-   * Fold the stream, resulting in a single value.
-   */
-  fold<S>(f: (v: T, acc: S) => S, start: S): Promise<S>;
-
-  /**
-   * Execute a function for every value of the stream, for the
-   * side-effects of the function.
-   */
-  forEach(f: (v: T) => void): Promise<void>;
-
-  /**
-   * Map each element of the stream using a function, resulting in another
-   * stream of a different type.
-   */
-  map<S>(f: (x: T) => S): QueryStream<S>;
-
-  /**
-   * Map each element of the stream to a potentially empty array, and collect
-   * the result in a stream of the flattened arrays.
-   */
-  flatMap<S>(f: (x: T) => S[]): QueryStream<S>;
-
-  /**
-   * Collect the stream into an array and return a promise for it.
-   */
-  toArray(): Promise<T[]>;
-
-  /**
-   * Get the first value of the stream.
-   */
-  first(): QueryValue<T>;
-
-  /**
-   * Run the query without returning a result.
-   * Useful for queries with side effects.
-   */
-  run(): Promise<void>;
+export function oneShotGet<T>(
+  db: IDBDatabase,
+  store: Store<T>,
+  key: any,
+): Promise<T | undefined> {
+  const tx = db.transaction([store.name], "readonly");
+  const req = tx.objectStore(store.name).get(key);
+  return requestToPromise(req);
 }
 
-/**
- * Query result that consists of at most one value.
- */
-export interface QueryValue<T> {
-  /**
-   * Apply a function to a query value.
-   */
-  map<S>(f: (x: T) => S): QueryValue<S>;
-  /**
-   * Conditionally execute either of two queries based
-   * on a property of this query value.
-   *
-   * Useful to properly implement complex queries within a transaction (as
-   * opposed to just computing the conditional and then executing either
-   * branch).  This is necessary since IndexedDB does not allow long-lived
-   * transactions.
-   */
-  cond<R>(
-    f: (x: T) => boolean,
-    onTrue: (r: QueryRoot) => R,
-    onFalse: (r: QueryRoot) => R,
-  ): Promise<void>;
+export function oneShotGetIndexed<S extends IDBValidKey, T>(
+  db: IDBDatabase,
+  index: Index<S, T>,
+  key: any,
+): Promise<T | undefined> {
+  const tx = db.transaction([index.storeName], "readonly");
+  const req = tx
+    .objectStore(index.storeName)
+    .index(index.indexName)
+    .get(key);
+  return requestToPromise(req);
 }
 
-abstract class BaseQueryValue<T> implements QueryValue<T> {
-  constructor(public root: QueryRoot) {}
-
-  map<S>(f: (x: T) => S): QueryValue<S> {
-    return new MapQueryValue<T, S>(this, f);
-  }
+export function oneShotPut<T>(
+  db: IDBDatabase,
+  store: Store<T>,
+  value: T,
+  key?: any,
+): Promise<any> {
+  const tx = db.transaction([store.name], "readwrite");
+  const req = tx.objectStore(store.name).put(value, key);
+  return requestToPromise(req);
+}
 
-  cond<R>(
-    f: (x: T) => boolean,
-    onTrue: (r: QueryRoot) => R,
-    onFalse: (r: QueryRoot) => R,
-  ): Promise<void> {
-    return new Promise<void>((resolve, reject) => {
-      this.subscribeOne((v, tx) => {
-        if (f(v)) {
-          onTrue(new QueryRoot(this.root.db));
+function applyMutation<T>(
+  req: IDBRequest,
+  f: (x: T) => T | undefined,
+): Promise<void> {
+  return new Promise((resolve, reject) => {
+    req.onsuccess = () => {
+      const cursor = req.result;
+      if (cursor) {
+        const val = cursor.value();
+        const modVal = f(val);
+        if (modVal !== undefined && modVal !== null) {
+          const req2: IDBRequest = cursor.update(modVal);
+          req2.onerror = () => {
+            reject(req2.error);
+          };
+          req2.onsuccess = () => {
+            cursor.continue();
+          };
         } else {
-          onFalse(new QueryRoot(this.root.db));
+          cursor.continue();
         }
-      });
-      resolve();
-    });
-  }
-
-  abstract subscribeOne(f: SubscribeOneFn): void;
-}
-
-class FirstQueryValue<T> extends BaseQueryValue<T> {
-  private gotValue = false;
-  private s: QueryStreamBase<T>;
-
-  constructor(stream: QueryStreamBase<T>) {
-    super(stream.root);
-    this.s = stream;
-  }
-
-  subscribeOne(f: SubscribeOneFn): void {
-    this.s.subscribe((isDone, value, tx) => {
-      if (this.gotValue) {
-        return;
-      }
-      if (isDone) {
-        f(undefined, tx);
       } else {
-        f(value, tx);
+        resolve();
       }
-      this.gotValue = true;
-    });
-  }
+    };
+    req.onerror = () => {
+      reject(req.error);
+    };
+  });
 }
 
-class MapQueryValue<T, S> extends BaseQueryValue<S> {
-  constructor(private v: BaseQueryValue<T>, private mapFn: (x: T) => S) {
-    super(v.root);
-  }
-
-  subscribeOne(f: SubscribeOneFn): void {
-    this.v.subscribeOne((v, tx) => this.mapFn(v));
-  }
+export function oneShotMutate<T>(
+  db: IDBDatabase,
+  store: Store<T>,
+  key: any,
+  f: (x: T) => T | undefined,
+): Promise<void> {
+  const tx = db.transaction([store.name], "readwrite");
+  const req = tx.objectStore(store.name).openCursor(key);
+  return applyMutation(req, f);
 }
 
-/**
- * Exception that should be thrown by client code to abort a transaction.
- */
-export const AbortTransaction = Symbol("abort_transaction");
-
-abstract class QueryStreamBase<T> implements QueryStream<T> {
-  abstract subscribe(
-    f: (isDone: boolean, value: any, tx: IDBTransaction) => void,
-  ): void;
-  constructor(public root: QueryRoot) {}
-
-  first(): QueryValue<T> {
-    return new FirstQueryValue(this);
-  }
-
-  flatMap<S>(f: (x: T) => S[]): QueryStream<S> {
-    return new QueryStreamFlatMap<T, S>(this, f);
-  }
+type CursorResult<T> = CursorEmptyResult<T> | CursorValueResult<T>;
 
-  map<S>(f: (x: T) => S): QueryStream<S> {
-    return new QueryStreamMap(this, f);
-  }
-
-  indexJoin<S, I extends IDBValidKey>(
-    index: Index<I, S>,
-    keyFn: (obj: T) => I,
-  ): QueryStream<JoinResult<T, S>> {
-    this.root.addStoreAccess(index.storeName, false);
-    return new QueryStreamIndexJoin<T, S>(
-      this,
-      index.storeName,
-      index.indexName,
-      keyFn,
-    );
-  }
-
-  indexJoinLeft<S, I extends IDBValidKey>(
-    index: Index<I, S>,
-    keyFn: (obj: T) => I,
-  ): QueryStream<JoinLeftResult<T, S>> {
-    this.root.addStoreAccess(index.storeName, false);
-    return new QueryStreamIndexJoinLeft<T, S>(
-      this,
-      index.storeName,
-      index.indexName,
-      keyFn,
-    );
-  }
-
-  keyJoin<S, I extends IDBValidKey>(
-    store: Store<S>,
-    keyFn: (obj: T) => I,
-  ): QueryStream<JoinResult<T, S>> {
-    this.root.addStoreAccess(store.name, false);
-    return new QueryStreamKeyJoin<T, S>(this, store.name, keyFn);
-  }
-
-  filter(f: (x: any) => boolean): QueryStream<T> {
-    return new QueryStreamFilter(this, f);
-  }
-
-  toArray(): Promise<T[]> {
-    const { resolve, promise } = openPromise<T[]>();
-    const values: T[] = [];
-
-    this.subscribe((isDone, value) => {
-      if (isDone) {
-        resolve(values);
-        return;
-      }
-      values.push(value);
-    });
-
-    return Promise.resolve()
-      .then(() => this.root.finish())
-      .then(() => promise);
-  }
-
-  fold<A>(f: (x: T, acc: A) => A, init: A): Promise<A> {
-    const { resolve, promise } = openPromise<A>();
-    let acc = init;
-
-    this.subscribe((isDone, value) => {
-      if (isDone) {
-        resolve(acc);
-        return;
-      }
-      acc = f(value, acc);
-    });
-
-    return Promise.resolve()
-      .then(() => this.root.finish())
-      .then(() => promise);
-  }
-
-  forEach(f: (x: T) => void): Promise<void> {
-    const { resolve, promise } = openPromise<void>();
-
-    this.subscribe((isDone, value) => {
-      if (isDone) {
-        resolve();
-        return;
-      }
-      f(value);
-    });
-
-    return Promise.resolve()
-      .then(() => this.root.finish())
-      .then(() => promise);
-  }
-
-  run(): Promise<void> {
-    const { resolve, promise } = openPromise<void>();
-
-    this.subscribe((isDone, value) => {
-      if (isDone) {
-        resolve();
-        return;
-      }
-    });
-
-    return Promise.resolve()
-      .then(() => this.root.finish())
-      .then(() => promise);
-  }
+interface CursorEmptyResult<T> {
+  hasValue: false;
 }
 
-type FilterFn = (e: any) => boolean;
-type SubscribeFn = (done: boolean, value: any, tx: IDBTransaction) => void;
-type SubscribeOneFn = (value: any, tx: IDBTransaction) => void;
-
-class QueryStreamFilter<T> extends QueryStreamBase<T> {
-  constructor(public s: QueryStreamBase<T>, public filterFn: FilterFn) {
-    super(s.root);
-  }
-
-  subscribe(f: SubscribeFn) {
-    this.s.subscribe((isDone, value, tx) => {
-      if (isDone) {
-        f(true, undefined, tx);
-        return;
-      }
-      if (this.filterFn(value)) {
-        f(false, value, tx);
-      }
-    });
-  }
+interface CursorValueResult<T> {
+  hasValue: true;
+  value: T;
 }
 
-class QueryStreamFlatMap<T, S> extends QueryStreamBase<S> {
-  constructor(public s: QueryStreamBase<T>, public flatMapFn: (v: T) => S[]) {
-    super(s.root);
-  }
-
-  subscribe(f: SubscribeFn) {
-    this.s.subscribe((isDone, value, tx) => {
-      if (isDone) {
-        f(true, undefined, tx);
-        return;
+class ResultStream<T> {
+  private currentPromise: Promise<void>;
+  private gotCursorEnd: boolean = false;
+  private awaitingResult: boolean = false;
+
+  constructor(private req: IDBRequest) {
+    this.awaitingResult = true;
+    let p = openPromise<void>();
+    this.currentPromise = p.promise;
+    req.onsuccess = () => {
+      if (!this.awaitingResult) {
+        throw Error("BUG: invariant violated");
       }
-      const values = this.flatMapFn(value);
-      for (const v in values) {
-        f(false, v, tx);
+      const cursor = req.result;
+      if (cursor) {
+        this.awaitingResult = false;
+        p.resolve();
+        p = openPromise<void>();
+        this.currentPromise = p.promise;
+      } else {
+        this.gotCursorEnd = true;
+        p.resolve();
       }
-    });
-  }
-}
-
-class QueryStreamMap<S, T> extends QueryStreamBase<T> {
-  constructor(public s: QueryStreamBase<S>, public mapFn: (v: S) => T) {
-    super(s.root);
+    };
+    req.onerror = () => {
+      p.reject(req.error);
+    };
   }
 
-  subscribe(f: SubscribeFn) {
-    this.s.subscribe((isDone, value, tx) => {
-      if (isDone) {
-        f(true, undefined, tx);
-        return;
+  async toArray(): Promise<T[]> {
+    const arr: T[] = [];
+    while (true) {
+      const x = await this.next();
+      if (x.hasValue) {
+        arr.push(x.value);
+      } else {
+        break;
       }
-      const mappedValue = this.mapFn(value);
-      f(false, mappedValue, tx);
-    });
-  }
-}
-
-class QueryStreamIndexJoin<T, S> extends QueryStreamBase<JoinResult<T, S>> {
-  constructor(
-    public s: QueryStreamBase<T>,
-    public storeName: string,
-    public indexName: string,
-    public key: any,
-  ) {
-    super(s.root);
+    }
+    return arr;
   }
 
-  subscribe(f: SubscribeFn) {
-    this.s.subscribe((isDone, value, tx) => {
-      if (isDone) {
-        f(true, undefined, tx);
-        return;
+  async map<R>(f: (x: T) => R): Promise<R[]> {
+    const arr: R[] = [];
+    while (true) {
+      const x = await this.next();
+      if (x.hasValue) {
+        arr.push(f(x.value));
+      } else {
+        break;
       }
-      const joinKey = this.key(value);
-      const s = tx.objectStore(this.storeName).index(this.indexName);
-      const req = s.openCursor(IDBKeyRange.only(joinKey));
-      req.onsuccess = () => {
-        const cursor = req.result;
-        if (cursor) {
-          f(false, { left: value, right: cursor.value }, tx);
-          cursor.continue();
-        }
-      };
-    });
-  }
-}
-
-class QueryStreamIndexJoinLeft<T, S> extends QueryStreamBase<
-  JoinLeftResult<T, S>
-> {
-  constructor(
-    public s: QueryStreamBase<T>,
-    public storeName: string,
-    public indexName: string,
-    public key: any,
-  ) {
-    super(s.root);
+    }
+    return arr;
   }
 
-  subscribe(f: SubscribeFn) {
-    this.s.subscribe((isDone, value, tx) => {
-      if (isDone) {
-        f(true, undefined, tx);
-        return;
+  async forEach(f: (x: T) => void): Promise<void> {
+    while (true) {
+      const x = await this.next();
+      if (x.hasValue) {
+        f(x.value);
+      } else {
+        break;
       }
-      const s = tx.objectStore(this.storeName).index(this.indexName);
-      const req = s.openCursor(IDBKeyRange.only(this.key(value)));
-      let gotMatch = false;
-      req.onsuccess = () => {
-        const cursor = req.result;
-        if (cursor) {
-          gotMatch = true;
-          f(false, { left: value, right: cursor.value }, tx);
-          cursor.continue();
-        } else {
-          if (!gotMatch) {
-            f(false, { left: value }, tx);
-          }
-        }
-      };
-    });
-  }
-}
-
-class QueryStreamKeyJoin<T, S> extends QueryStreamBase<JoinResult<T, S>> {
-  constructor(
-    public s: QueryStreamBase<T>,
-    public storeName: string,
-    public key: any,
-  ) {
-    super(s.root);
+    }
   }
 
-  subscribe(f: SubscribeFn) {
-    this.s.subscribe((isDone, value, tx) => {
-      if (isDone) {
-        f(true, undefined, tx);
-        return;
-      }
-      const s = tx.objectStore(this.storeName);
-      const req = s.openCursor(IDBKeyRange.only(this.key(value)));
-      req.onsuccess = () => {
-        const cursor = req.result;
-        if (cursor) {
-          f(false, { left: value, right: cursor.value }, tx);
-          cursor.continue();
-        } else {
-          f(true, undefined, tx);
+  async filter(f: (x: T) => boolean): Promise<T[]> {
+    const arr: T[] = [];
+    while (true) {
+      const x = await this.next();
+      if (x.hasValue) {
+        if (f(x.value)) {
+          arr.push(x.value);
         }
-      };
-    });
-  }
-}
-
-class IterQueryStream<T> extends QueryStreamBase<T> {
-  private storeName: string;
-  private options: any;
-  private subscribers: SubscribeFn[];
-
-  constructor(qr: QueryRoot, storeName: string, options: any) {
-    super(qr);
-    this.options = options;
-    this.storeName = storeName;
-    this.subscribers = [];
-
-    const doIt = (tx: IDBTransaction) => {
-      const { indexName = void 0, only = void 0 } = this.options;
-      let s: any;
-      if (indexName !== void 0) {
-        s = tx.objectStore(this.storeName).index(this.options.indexName);
       } else {
-        s = tx.objectStore(this.storeName);
-      }
-      let kr: IDBKeyRange | undefined;
-      if (only !== undefined) {
-        kr = IDBKeyRange.only(this.options.only);
+        break;
       }
-      const req = s.openCursor(kr);
-      req.onsuccess = () => {
-        const cursor: IDBCursorWithValue = req.result;
-        if (cursor) {
-          for (const f of this.subscribers) {
-            f(false, cursor.value, tx);
-          }
-          cursor.continue();
-        } else {
-          for (const f of this.subscribers) {
-            f(true, undefined, tx);
-          }
-        }
-      };
-    };
-
-    this.root.addWork(doIt);
+    }
+    return arr;
   }
 
-  subscribe(f: SubscribeFn) {
-    this.subscribers.push(f);
+  async next(): Promise<CursorResult<T>> {
+    if (this.gotCursorEnd) {
+      return { hasValue: false };
+    }
+    if (!this.awaitingResult) {
+      const cursor = this.req.result;
+      if (!cursor) {
+        throw Error("assertion failed");
+      }
+      this.awaitingResult = true;
+      cursor.continue();
+    }
+    await this.currentPromise;
+    if (this.gotCursorEnd) {
+      return { hasValue: false };
+    }
+    const cursor = this.req.result;
+    if (!cursor) {
+      throw Error("assertion failed");
+    }
+    return { hasValue: true, value: cursor.value };
   }
 }
 
-/**
- * Root wrapper around an IndexedDB for queries with a fluent interface.
- */
-export class QueryRoot {
-  private work: Array<(t: IDBTransaction) => void> = [];
-  private stores: Set<string> = new Set();
-  private kickoffPromise: Promise<void>;
-
-  /**
-   * Some operations is a write operation,
-   * and we need to do a "readwrite" transaction/
-   */
-  private hasWrite: boolean;
-
-  private finishScheduled: boolean;
-
-  private finished: boolean = false;
-
-  private keys: { [keyName: string]: IDBValidKey } = {};
-
-  constructor(public db: IDBDatabase) {}
-
-  /**
-   * Get a named key that was created during the query.
-   */
-  key(keyName: string): IDBValidKey | undefined {
-    return this.keys[keyName];
-  }
-
-  private checkFinished() {
-    if (this.finished) {
-      throw Error("Can't add work to query after it was started");
-    }
-  }
+export function oneShotIter<T>(
+  db: IDBDatabase,
+  store: Store<T>,
+): ResultStream<T> {
+  const tx = db.transaction([store.name], "readonly");
+  const req = tx.objectStore(store.name).openCursor();
+  return new ResultStream<T>(req);
+}
 
-  /**
-   * Get a stream of all objects in the store.
-   */
-  iter<T>(store: Store<T>): QueryStream<T> {
-    this.checkFinished();
-    this.stores.add(store.name);
-    this.scheduleFinish();
-    return new IterQueryStream<T>(this, store.name, {});
-  }
+export function oneShotIterIndex<S extends IDBValidKey, T>(
+  db: IDBDatabase,
+  index: Index<S, T>,
+  query?: any,
+): ResultStream<T> {
+  const tx = db.transaction([index.storeName], "readonly");
+  const req = tx
+    .objectStore(index.storeName)
+    .index(index.indexName)
+    .openCursor(query);
+  return new ResultStream<T>(req);
+}
 
-  /**
-   * Count the number of objects in a store.
-   */
-  count<T>(store: Store<T>): Promise<number> {
-    this.checkFinished();
-    const { resolve, promise } = openPromise<number>();
-
-    const doCount = (tx: IDBTransaction) => {
-      const s = tx.objectStore(store.name);
-      const req = s.count();
-      req.onsuccess = () => {
-        resolve(req.result);
-      };
-    };
+class TransactionHandle {
+  constructor(private tx: IDBTransaction) {}
 
-    this.addWork(doCount, store.name, false);
-    return Promise.resolve()
-      .then(() => this.finish())
-      .then(() => promise);
+  put<T>(store: Store<T>, value: T, key?: any): Promise<any> {
+    const req = this.tx.objectStore(store.name).put(value, key);
+    return requestToPromise(req);
   }
 
-  /**
-   * Delete all objects in a store that match a predicate.
-   */
-  deleteIf<T>(
-    store: Store<T>,
-    predicate: (x: T, n: number) => boolean,
-  ): QueryRoot {
-    this.checkFinished();
-    const doDeleteIf = (tx: IDBTransaction) => {
-      const s = tx.objectStore(store.name);
-      const req = s.openCursor();
-      let n = 0;
-      req.onsuccess = () => {
-        const cursor: IDBCursorWithValue | null = req.result;
-        if (cursor) {
-          if (predicate(cursor.value, n++)) {
-            cursor.delete();
-          }
-          cursor.continue();
-        }
-      };
-    };
-    this.addWork(doDeleteIf, store.name, true);
-    return this;
+  add<T>(store: Store<T>, value: T, key?: any): Promise<any> {
+    const req = this.tx.objectStore(store.name).add(value, key);
+    return requestToPromise(req);
   }
 
-  iterIndex<S extends IDBValidKey, T>(
-    index: Index<S, T>,
-    only?: S,
-  ): QueryStream<T> {
-    this.checkFinished();
-    this.stores.add(index.storeName);
-    this.scheduleFinish();
-    return new IterQueryStream<T>(this, index.storeName, {
-      indexName: index.indexName,
-      only,
-    });
+  get<T>(store: Store<T>, key: any): Promise<T | undefined> {
+    const req = this.tx.objectStore(store.name).get(key);
+    return requestToPromise(req);
   }
 
-  /**
-   * Put an object into the given object store.
-   * Overrides if an existing object with the same key exists
-   * in the store.
-   */
-  put<T>(store: Store<T>, val: T, keyName?: string): QueryRoot {
-    this.checkFinished();
-    const doPut = (tx: IDBTransaction) => {
-      const req = tx.objectStore(store.name).put(val);
-      if (keyName) {
-        req.onsuccess = () => {
-          this.keys[keyName] = req.result;
-        };
-      }
-    };
-    this.scheduleFinish();
-    this.addWork(doPut, store.name, true);
-    return this;
+  iter<T>(store: Store<T>, key?: any): ResultStream<T> {
+    const req = this.tx.objectStore(store.name).openCursor(key);
+    return new ResultStream<T>(req);
   }
 
-  /**
-   * Put an object into a store or return an existing record.
-   */
-  putOrGetExisting<T>(store: Store<T>, val: T, key: IDBValidKey): Promise<T> {
-    this.checkFinished();
-    const { resolve, promise } = openPromise<T>();
-    const doPutOrGet = (tx: IDBTransaction) => {
-      const objstore = tx.objectStore(store.name);
-      const req = objstore.get(key);
-      req.onsuccess = () => {
-        if (req.result !== undefined) {
-          resolve(req.result);
-        } else {
-          const req2 = objstore.add(val);
-          req2.onsuccess = () => {
-            resolve(val);
-          };
-        }
-      };
-    };
-    this.scheduleFinish();
-    this.addWork(doPutOrGet, store.name, true);
-    return promise;
+  delete<T>(store: Store<T>, key: any): Promise<void> {
+    const req = this.tx.objectStore(store.name).delete(key);
+    return requestToPromise(req);
   }
 
-  putWithResult<T>(store: Store<T>, val: T): Promise<IDBValidKey> {
-    this.checkFinished();
-    const { resolve, promise } = openPromise<IDBValidKey>();
-    const doPutWithResult = (tx: IDBTransaction) => {
-      const req = tx.objectStore(store.name).put(val);
-      req.onsuccess = () => {
-        resolve(req.result);
-      };
-      this.scheduleFinish();
-    };
-    this.addWork(doPutWithResult, store.name, true);
-    return Promise.resolve()
-      .then(() => this.finish())
-      .then(() => promise);
+  mutate<T>(store: Store<T>, key: any, f: (x: T) => T | undefined) {
+    const req = this.tx.objectStore(store.name).openCursor(key);
+    return applyMutation(req, f);
   }
+}
 
-  /**
-   * Update objects inside a transaction.
-   *
-   * If the mutation function throws AbortTransaction, the whole transaction 
will be aborted.
-   * If the mutation function returns undefined or null, no modification will 
be made.
-   */
-  mutate<T>(store: Store<T>, key: any, f: (v: T) => T | undefined): QueryRoot {
-    this.checkFinished();
-    const doPut = (tx: IDBTransaction) => {
-      const req = tx.objectStore(store.name).openCursor(IDBKeyRange.only(key));
-      req.onsuccess = () => {
-        const cursor = req.result;
-        if (cursor) {
-          const value = cursor.value;
-          let modifiedValue: T | undefined;
-          try {
-            modifiedValue = f(value);
-          } catch (e) {
-            if (e === AbortTransaction) {
-              tx.abort();
-              return;
-            }
-            throw e;
-          }
-          if (modifiedValue !== undefined && modifiedValue !== null) {
-            cursor.update(modifiedValue);
-          }
-          cursor.continue();
-        }
-      };
+export function runWithWriteTransaction<T>(
+  db: IDBDatabase,
+  stores: Store<any>[],
+  f: (t: TransactionHandle) => Promise<T>,
+): Promise<T> {
+  return new Promise((resolve, reject) => {
+    const storeName = stores.map(x => x.name);
+    const tx = db.transaction(storeName, "readwrite");
+    let funResult: any = undefined;
+    let gotFunResult: boolean = false;
+    tx.onerror = () => {
+      console.error("error in transaction:", tx.error);
+      reject(tx.error);
     };
-    this.scheduleFinish();
-    this.addWork(doPut, store.name, true);
-    return this;
-  }
-
-  /**
-   * Add all object from an iterable to the given object store.
-   */
-  putAll<T>(store: Store<T>, iterable: T[]): QueryRoot {
-    this.checkFinished();
-    const doPutAll = (tx: IDBTransaction) => {
-      for (const obj of iterable) {
-        tx.objectStore(store.name).put(obj);
+    tx.oncomplete = () => {
+      // This is a fatal error: The transaction completed *before*
+      // the transaction function returned.  Likely, the transaction
+      // function waited on a promise that is *not* resolved in the
+      // microtask queue, thus triggering the auto-commit behavior.
+      // Unfortunately, the auto-commit behavior of IDB can't be switched
+      // of.  There are some proposals to add this functionality in the future.
+      if (!gotFunResult) {
+        const msg =
+          "BUG: transaction closed before transaction function returned";
+        console.error(msg);
+        reject(Error(msg));
       }
+      resolve(funResult);
     };
-    this.scheduleFinish();
-    this.addWork(doPutAll, store.name, true);
-    return this;
-  }
-
-  /**
-   * Add an object to the given object store.
-   * Fails if the object's key is already present
-   * in the object store.
-   */
-  add<T>(store: Store<T>, val: T): QueryRoot {
-    this.checkFinished();
-    const doAdd = (tx: IDBTransaction) => {
-      tx.objectStore(store.name).add(val);
-    };
-    this.scheduleFinish();
-    this.addWork(doAdd, store.name, true);
-    return this;
-  }
-
-  /**
-   * Get one object from a store by its key.
-   */
-  get<T>(store: Store<T>, key: any): Promise<T | undefined> {
-    this.checkFinished();
-    if (key === void 0) {
-      throw Error("key must not be undefined");
-    }
-
-    const { resolve, promise } = openPromise<T | undefined>();
-
-    const doGet = (tx: IDBTransaction) => {
-      const req = tx.objectStore(store.name).get(key);
-      req.onsuccess = () => {
-        resolve(req.result);
-      };
+    tx.onabort = () => {
+      console.error("aborted transaction");
+      reject(AbortTransaction);
     };
+    const th = new TransactionHandle(tx);
+    const resP = f(th);
+    resP.then(result => {
+      gotFunResult = true;
+      funResult = result;
+    });
+  });
+}
 
-    this.addWork(doGet, store.name, false);
-    return Promise.resolve()
-      .then(() => this.finish())
-      .then(() => promise);
-  }
-
-  /**
-   * Get get objects from a store by their keys.
-   * If no object for a key exists, the resulting position in the array
-   * contains 'undefined'.
-   */
-  getMany<T>(store: Store<T>, keys: any[]): Promise<T[]> {
-    this.checkFinished();
-
-    const { resolve, promise } = openPromise<T[]>();
-    const results: T[] = [];
-
-    const doGetMany = (tx: IDBTransaction) => {
-      for (const key of keys) {
-        if (key === void 0) {
-          throw Error("key must not be undefined");
-        }
-        const req = tx.objectStore(store.name).get(key);
-        req.onsuccess = () => {
-          results.push(req.result);
-          if (results.length === keys.length) {
-            resolve(results);
-          }
-        };
-      }
-    };
-
-    this.addWork(doGetMany, store.name, false);
-    return Promise.resolve()
-      .then(() => this.finish())
-      .then(() => promise);
-  }
-
+/**
+ * Definition of an index.
+ */
+export class Index<S extends IDBValidKey, T> {
   /**
-   * Get one object from a store by its key.
+   * Name of the store that this index is associated with.
    */
-  getIndexed<I extends IDBValidKey, T>(
-    index: Index<I, T>,
-    key: I,
-  ): Promise<T | undefined> {
-    this.checkFinished();
-    if (key === void 0) {
-      throw Error("key must not be undefined");
-    }
-
-    const { resolve, promise } = openPromise<T | undefined>();
-
-    const doGetIndexed = (tx: IDBTransaction) => {
-      const req = tx
-        .objectStore(index.storeName)
-        .index(index.indexName)
-        .get(key);
-      req.onsuccess = () => {
-        resolve(req.result);
-      };
-    };
-
-    this.addWork(doGetIndexed, index.storeName, false);
-    return Promise.resolve()
-      .then(() => this.finish())
-      .then(() => promise);
-  }
-
-  private scheduleFinish() {
-    if (!this.finishScheduled) {
-      Promise.resolve().then(() => this.finish());
-      this.finishScheduled = true;
-    }
-  }
+  storeName: string;
 
   /**
-   * Finish the query, and start the query in the first place if necessary.
+   * Options to use for the index.
    */
-  finish(): Promise<void> {
-    if (this.kickoffPromise) {
-      return this.kickoffPromise;
-    }
-    this.kickoffPromise = new Promise<void>((resolve, reject) => {
-      // At this point, we can't add any more work
-      this.finished = true;
-      if (this.work.length === 0) {
-        resolve();
-        return;
-      }
-      const mode = this.hasWrite ? "readwrite" : "readonly";
-      const tx = this.db.transaction(Array.from(this.stores), mode);
-      tx.oncomplete = () => {
-        resolve();
-      };
-      tx.onabort = () => {
-        console.warn(
-          `aborted ${mode} transaction on stores [${[...this.stores]}]`,
-        );
-        reject(Error("transaction aborted"));
-      };
-      tx.onerror = e => {
-        console.warn(`error in transaction`, (e.target as any).error);
-      };
-      for (const w of this.work) {
-        w(tx);
-      }
-    });
-    return this.kickoffPromise;
-  }
+  options: IndexOptions;
 
-  /**
-   * Delete an object by from the given object store.
-   */
-  delete<T>(store: Store<T>, key: any): QueryRoot {
-    this.checkFinished();
-    const doDelete = (tx: IDBTransaction) => {
-      tx.objectStore(store.name).delete(key);
+  constructor(
+    s: Store<T>,
+    public indexName: string,
+    public keyPath: string | string[],
+    options?: IndexOptions,
+  ) {
+    const defaultOptions = {
+      multiEntry: false,
     };
-    this.scheduleFinish();
-    this.addWork(doDelete, store.name, true);
-    return this;
+    this.options = { ...defaultOptions, ...(options || {}) };
+    this.storeName = s.name;
   }
 
   /**
-   * Low-level function to add a task to the internal work queue.
+   * We want to have the key type parameter in use somewhere,
+   * because otherwise the compiler complains.  In iterIndex the
+   * key type is pretty useful.
    */
-  addWork(
-    workFn: (t: IDBTransaction) => void,
-    storeName?: string,
-    isWrite?: boolean,
-  ) {
-    this.work.push(workFn);
-    if (storeName) {
-      this.addStoreAccess(storeName, isWrite);
-    }
-  }
-
-  addStoreAccess(storeName: string, isWrite?: boolean) {
-    if (storeName) {
-      this.stores.add(storeName);
-    }
-    if (isWrite) {
-      this.hasWrite = true;
-    }
-  }
+  protected _dummyKey: S | undefined;
 }
+
+/**
+ * Exception that should be thrown by client code to abort a transaction.
+ */
+export const AbortTransaction = Symbol("abort_transaction");
diff --git a/src/wallet.ts b/src/wallet.ts
index f5219c45..71e058fd 100644
--- a/src/wallet.ts
+++ b/src/wallet.ts
@@ -29,14 +29,19 @@ import {
   canonicalizeBaseUrl,
   getTalerStampSec,
   strcmp,
+  extractTalerStamp,
 } from "./helpers";
 import { HttpRequestLibrary, RequestException } from "./http";
 import * as LibtoolVersion from "./libtoolVersion";
 import {
   AbortTransaction,
-  JoinLeftResult,
-  JoinResult,
-  QueryRoot,
+  oneShotPut,
+  oneShotGet,
+  runWithWriteTransaction,
+  oneShotIter,
+  oneShotIterIndex,
+  oneShotGetIndexed,
+  oneShotMutate,
 } from "./query";
 import { TimerGroup } from "./timer";
 
@@ -63,6 +68,8 @@ import {
   TipRecord,
   WireFee,
   WithdrawalRecord,
+  ExchangeDetails,
+  ExchangeUpdateStatus,
 } from "./dbTypes";
 import {
   Auditor,
@@ -110,6 +117,8 @@ import {
   PendingOperationInfo,
   PendingOperationsResponse,
   HistoryQuery,
+  getTimestampNow,
+  OperationError,
 } from "./walletTypes";
 import { openPromise } from "./promiseUtils";
 import {
@@ -338,6 +347,18 @@ interface CoinsForPaymentArgs {
   wireMethod: string;
 }
 
+/**
+ * This error is thrown when an
+ */
+class OperationFailedAndReportedError extends Error {
+  constructor(public reason: Error) {
+    super("Reported failed operation: " + reason.message);
+
+    // Set the prototype explicitly.
+    Object.setPrototypeOf(this, OperationFailedAndReportedError.prototype);
+  }
+}
+
 /**
  * The platform-independent wallet implementation.
  */
@@ -372,10 +393,6 @@ export class Wallet {
    */
   private runningOperations: Set<string> = new Set();
 
-  q(): QueryRoot {
-    return new QueryRoot(this.db);
-  }
-
   constructor(
     db: IDBDatabase,
     http: HttpRequestLibrary,
@@ -389,31 +406,55 @@ export class Wallet {
     this.notifier = notifier;
     this.cryptoApi = new CryptoApi(cryptoWorkerFactory);
     this.timerGroup = new TimerGroup();
+  }
+
+  public async processPending(): Promise<void> {
+    const exchangeBaseUrlList = await oneShotIter(
+      this.db,
+      Stores.exchanges,
+    ).map(x => x.baseUrl);
 
-    const init = async () => {
-      await this.fillDefaults().catch(e => console.log(e));
+    for (let exchangeBaseUrl of exchangeBaseUrlList) {
+      await this.updateExchangeFromUrl(exchangeBaseUrl);
+    }
+  }
+
+  /**
+   * Start processing pending operations asynchronously.
+   */
+  public start() {
+    const work = async () => {
       await this.collectGarbage().catch(e => console.log(e));
       this.updateExchanges();
       this.resumePendingFromDb();
       this.timerGroup.every(1000 * 60 * 15, () => this.updateExchanges());
     };
-
-    init();
+    work();
   }
 
-  private async fillDefaults() {
-    const onTrue = (r: QueryRoot) => {};
-    const onFalse = (r: QueryRoot) => {
-      Wallet.enableTracing && console.log("applying defaults");
-      r.put(Stores.config, { key: "currencyDefaultsApplied", value: true })
-        .putAll(Stores.currencies, builtinCurrencies)
-        .finish();
-    };
-    await this.q()
-      .iter(Stores.config)
-      .filter(x => x.key === "currencyDefaultsApplied")
-      .first()
-      .cond(x => x && x.value, onTrue, onFalse);
+  /**
+   * Insert the hard-coded defaults for exchanges, coins and
+   * auditors into the database, unless these defaults have
+   * already been applied.
+   */
+  async fillDefaults() {
+    await runWithWriteTransaction(
+      this.db,
+      [Stores.config, Stores.currencies],
+      async tx => {
+        let applied = false;
+        await tx.iter(Stores.config).forEach(x => {
+          if (x.key == "currencyDefaultsApplied" && x.value == true) {
+            applied = true;
+          }
+        });
+        if (!applied) {
+          for (let c of builtinCurrencies) {
+            await tx.put(Stores.currencies, c);
+          }
+        }
+      },
+    );
   }
 
   private startOperation(operationId: string) {
@@ -429,12 +470,11 @@ export class Wallet {
   }
 
   async updateExchanges(): Promise<void> {
-    const exchangesUrls = await this.q()
-      .iter(Stores.exchanges)
-      .map(e => e.baseUrl)
-      .toArray();
+    const exchangeUrls = await oneShotIter(this.db, Stores.exchanges).map(
+      e => e.baseUrl,
+    );
 
-    for (const url of exchangesUrls) {
+    for (const url of exchangeUrls) {
       this.updateExchangeFromUrl(url).catch(e => {
         console.error("updating exchange failed", e);
       });
@@ -448,69 +488,60 @@ export class Wallet {
   private resumePendingFromDb(): void {
     Wallet.enableTracing && console.log("resuming pending operations from db");
 
-    this.q()
-      .iter(Stores.reserves)
-      .forEach(reserve => {
-        Wallet.enableTracing &&
-          console.log("resuming reserve", reserve.reserve_pub);
-        this.processReserve(reserve.reserve_pub);
-      });
+    oneShotIter(this.db, Stores.reserves).forEach(reserve => {
+      Wallet.enableTracing &&
+        console.log("resuming reserve", reserve.reserve_pub);
+      this.processReserve(reserve.reserve_pub);
+    });
 
-    this.q()
-      .iter(Stores.precoins)
-      .forEach(preCoin => {
-        Wallet.enableTracing && console.log("resuming precoin");
-        this.processPreCoin(preCoin.coinPub);
-      });
+    oneShotIter(this.db, Stores.precoins).forEach(preCoin => {
+      Wallet.enableTracing && console.log("resuming precoin");
+      this.processPreCoin(preCoin.coinPub);
+    });
 
-    this.q()
-      .iter(Stores.refresh)
-      .forEach((r: RefreshSessionRecord) => {
-        this.continueRefreshSession(r);
-      });
+    oneShotIter(this.db, Stores.refresh).forEach((r: RefreshSessionRecord) => {
+      this.continueRefreshSession(r);
+    });
 
-    this.q()
-      .iter(Stores.coinsReturns)
-      .forEach((r: CoinsReturnRecord) => {
+    oneShotIter(this.db, Stores.coinsReturns).forEach(
+      (r: CoinsReturnRecord) => {
         this.depositReturnedCoins(r);
-      });
-
-    // FIXME: optimize via index
-    this.q()
-      .iter(Stores.coins)
-      .forEach((c: CoinRecord) => {
-        if (c.status === CoinStatus.Dirty) {
-          Wallet.enableTracing &&
-            console.log("resuming pending refresh for coin", c);
-          this.refresh(c.coinPub);
-        }
-      });
+      },
+    );
   }
 
   private async getCoinsForReturn(
     exchangeBaseUrl: string,
     amount: AmountJson,
   ): Promise<CoinWithDenom[] | undefined> {
-    const exchange = await this.q().get(Stores.exchanges, exchangeBaseUrl);
+    const exchange = await oneShotGet(
+      this.db,
+      Stores.exchanges,
+      exchangeBaseUrl,
+    );
     if (!exchange) {
       throw Error(`Exchange ${exchangeBaseUrl} not known to the wallet`);
     }
 
-    const coins: CoinRecord[] = await this.q()
-      .iterIndex(Stores.coins.exchangeBaseUrlIndex, exchange.baseUrl)
-      .toArray();
+    const coins: CoinRecord[] = await oneShotIterIndex(
+      this.db,
+      Stores.coins.exchangeBaseUrlIndex,
+      exchange.baseUrl,
+    ).toArray();
 
     if (!coins || !coins.length) {
       return [];
     }
 
-    const denoms = await this.q()
-      .iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl)
-      .toArray();
+    const denoms = await oneShotIterIndex(
+      this.db,
+      Stores.denominations.exchangeBaseUrlIndex,
+      exchange.baseUrl,
+    ).toArray();
 
     // Denomination of the first coin, we assume that all other
     // coins have the same currency
-    const firstDenom = await this.q().get(Stores.denominations, [
+    const firstDenom = await oneShotGet(this.db, Stores.denominations, [
       exchange.baseUrl,
       coins[0].denomPub,
     ]);
@@ -521,7 +552,7 @@ export class Wallet {
 
     const cds: CoinWithDenom[] = [];
     for (const coin of coins) {
-      const denom = await this.q().get(Stores.denominations, [
+      const denom = await oneShotGet(this.db, Stores.denominations, [
         exchange.baseUrl,
         coin.denomPub,
       ]);
@@ -572,16 +603,22 @@ export class Wallet {
 
     let remainingAmount = paymentAmount;
 
-    const exchanges = await this.q()
-      .iter(Stores.exchanges)
-      .toArray();
+    const exchanges = await oneShotIter(this.db, Stores.exchanges).toArray();
 
     for (const exchange of exchanges) {
       let isOkay: boolean = false;
+      const exchangeDetails = exchange.details;
+      if (!exchangeDetails) {
+        continue;
+      }
+      const exchangeFees = exchange.wireInfo;
+      if (!exchangeFees) {
+        continue;
+      }
 
       // is the exchange explicitly allowed?
       for (const allowedExchange of allowedExchanges) {
-        if (allowedExchange.master_pub === exchange.masterPublicKey) {
+        if (allowedExchange.master_pub === exchangeDetails.masterPublicKey) {
           isOkay = true;
           break;
         }
@@ -590,7 +627,7 @@ export class Wallet {
       // is the exchange allowed because of one of its auditors?
       if (!isOkay) {
         for (const allowedAuditor of allowedAuditors) {
-          for (const auditor of exchange.auditors) {
+          for (const auditor of exchangeDetails.auditors) {
             if (auditor.auditor_pub === allowedAuditor.auditor_pub) {
               isOkay = true;
               break;
@@ -606,20 +643,25 @@ export class Wallet {
         continue;
       }
 
-      const coins: CoinRecord[] = await this.q()
-        .iterIndex(Stores.coins.exchangeBaseUrlIndex, exchange.baseUrl)
-        .toArray();
+      const coins = await oneShotIterIndex(
+        this.db,
+        Stores.coins.exchangeBaseUrlIndex,
+        exchange.baseUrl,
+      ).toArray();
+
+      const denoms = await oneShotIterIndex(
+        this.db,
+        Stores.denominations.exchangeBaseUrlIndex,
+        exchange.baseUrl,
+      ).toArray();
 
-      const denoms = await this.q()
-        .iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl)
-        .toArray();
       if (!coins || coins.length === 0) {
         continue;
       }
 
       // Denomination of the first coin, we assume that all other
       // coins have the same currency
-      const firstDenom = await this.q().get(Stores.denominations, [
+      const firstDenom = await oneShotGet(this.db, Stores.denominations, [
         exchange.baseUrl,
         coins[0].denomPub,
       ]);
@@ -629,7 +671,7 @@ export class Wallet {
       const currency = firstDenom.value.currency;
       const cds: CoinWithDenom[] = [];
       for (const coin of coins) {
-        const denom = await this.q().get(Stores.denominations, [
+        const denom = await oneShotGet(this.db, Stores.denominations, [
           exchange.baseUrl,
           coin.denomPub,
         ]);
@@ -651,18 +693,9 @@ export class Wallet {
         cds.push({ coin, denom });
       }
 
-      const fees = await this.q().get(
-        Stores.exchangeWireFees,
-        exchange.baseUrl,
-      );
-      if (!fees) {
-        console.error("no fees found for exchange", exchange);
-        continue;
-      }
-
       let totalFees = Amounts.getZero(currency);
       let wireFee: AmountJson | undefined;
-      for (const fee of fees.feesForType[wireMethod] || []) {
+      for (const fee of exchangeFees.feesForType[wireMethod] || []) {
         if (fee.startStamp <= wireFeeTime && fee.endStamp >= wireFeeTime) {
           wireFee = fee.wireFee;
           break;
@@ -723,10 +756,17 @@ export class Wallet {
       timestamp_refund: 0,
     };
 
-    await this.q()
-      .put(Stores.purchases, t)
-      .putAll(Stores.coins, payCoinInfo.updatedCoins)
-      .finish();
+    await runWithWriteTransaction(
+      this.db,
+      [Stores.coins, Stores.purchases],
+      async tx => {
+        await tx.put(Stores.purchases, t);
+        for (let c of payCoinInfo.updatedCoins) {
+          await tx.put(Stores.coins, c);
+        }
+      },
+    );
+
     this.badge.showNotification();
     this.notifier.notify();
     return t;
@@ -773,7 +813,8 @@ export class Wallet {
 
     console.log("proposal", proposal);
 
-    const differentPurchase = await this.q().getIndexed(
+    const differentPurchase = await oneShotGetIndexed(
+      this.db,
       Stores.purchases.fulfillmentUrlIndex,
       proposal.contractTerms.fulfillment_url,
     );
@@ -805,7 +846,8 @@ export class Wallet {
     }
 
     // First check if we already payed for it.
-    const purchase = await this.q().get(
+    const purchase = await oneShotGet(
+      this.db,
       Stores.purchases,
       proposal.contractTermsHash,
     );
@@ -890,7 +932,8 @@ export class Wallet {
    *  downloaded in the context of a session ID.
    */
   async downloadProposal(url: string, sessionId?: string): Promise<number> {
-    const oldProposal = await this.q().getIndexed(
+    const oldProposal = await oneShotGetIndexed(
+      this.db,
       Stores.proposals.urlIndex,
       url,
     );
@@ -924,7 +967,7 @@ export class Wallet {
       downloadSessionId: sessionId,
     };
 
-    const id = await this.q().putWithResult(Stores.proposals, proposalRecord);
+    const id = await oneShotPut(this.db, Stores.proposals, proposalRecord);
     this.notifier.notify();
     if (typeof id !== "number") {
       throw Error("db schema wrong");
@@ -934,19 +977,17 @@ export class Wallet {
 
   async refundFailedPay(proposalId: number) {
     console.log(`refunding failed payment with proposal id ${proposalId}`);
-    const proposal: ProposalDownloadRecord | undefined = await this.q().get(
-      Stores.proposals,
-      proposalId,
-    );
-
+    const proposal = await oneShotGet(this.db, Stores.proposals, proposalId);
     if (!proposal) {
       throw Error(`proposal with id ${proposalId} not found`);
     }
 
-    const purchase = await this.q().get(
+    const purchase = await oneShotGet(
+      this.db,
       Stores.purchases,
       proposal.contractTermsHash,
     );
+
     if (!purchase) {
       throw Error("purchase not found for proposal");
     }
@@ -960,7 +1001,11 @@ export class Wallet {
     contractTermsHash: string,
     sessionId: string | undefined,
   ): Promise<ConfirmPayResult> {
-    const purchase = await this.q().get(Stores.purchases, contractTermsHash);
+    const purchase = await oneShotGet(
+      this.db,
+      Stores.purchases,
+      contractTermsHash,
+    );
     if (!purchase) {
       throw Error("Purchase not found: " + contractTermsHash);
     }
@@ -998,7 +1043,7 @@ export class Wallet {
     purchase.finished = true;
     const modifiedCoins: CoinRecord[] = [];
     for (const pc of purchase.payReq.coins) {
-      const c = await this.q().get<CoinRecord>(Stores.coins, pc.coin_pub);
+      const c = await oneShotGet(this.db, Stores.coins, pc.coin_pub);
       if (!c) {
         console.error("coin not found");
         throw Error("coin used in payment not found");
@@ -1007,10 +1052,17 @@ export class Wallet {
       modifiedCoins.push(c);
     }
 
-    await this.q()
-      .putAll(Stores.coins, modifiedCoins)
-      .put(Stores.purchases, purchase)
-      .finish();
+    await runWithWriteTransaction(
+      this.db,
+      [Stores.coins, Stores.purchases],
+      async tx => {
+        for (let c of modifiedCoins) {
+          tx.put(Stores.coins, c);
+        }
+        tx.put(Stores.purchases, purchase);
+      },
+    );
+
     for (const c of purchase.payReq.coins) {
       this.refresh(c.coin_pub);
     }
@@ -1031,9 +1083,7 @@ export class Wallet {
    */
   async refreshDirtyCoins(): Promise<{ numRefreshed: number }> {
     let n = 0;
-    const coins = await this.q()
-      .iter(Stores.coins)
-      .toArray();
+    const coins = await oneShotIter(this.db, Stores.coins).toArray();
     for (let coin of coins) {
       if (coin.status == CoinStatus.Dirty) {
         try {
@@ -1059,10 +1109,7 @@ export class Wallet {
       console.log(
         `executing confirmPay with proposalId ${proposalId} and 
sessionIdOverride ${sessionIdOverride}`,
       );
-    const proposal: ProposalDownloadRecord | undefined = await this.q().get(
-      Stores.proposals,
-      proposalId,
-    );
+    const proposal = await oneShotGet(this.db, Stores.proposals, proposalId);
 
     if (!proposal) {
       throw Error(`proposal with id ${proposalId} not found`);
@@ -1070,7 +1117,8 @@ export class Wallet {
 
     const sessionId = sessionIdOverride || proposal.downloadSessionId;
 
-    let purchase = await this.q().get(
+    let purchase = await oneShotGet(
+      this.db,
       Stores.purchases,
       proposal.contractTermsHash,
     );
@@ -1145,7 +1193,13 @@ export class Wallet {
       return;
     }
     const coinKeys = sp.payCoinInfo.updatedCoins.map(x => x.coinPub);
-    const coins = await this.q().getMany(Stores.coins, coinKeys);
+    const coins: CoinRecord[] = [];
+    for (let coinKey of coinKeys) {
+      const cc = await oneShotGet(this.db, Stores.coins, coinKey);
+      if (cc) {
+        coins.push(cc);
+      }
+    }
     for (let i = 0; i < coins.length; i++) {
       const specCoin = sp.payCoinInfo.originalCoins[i];
       const currentCoin = coins[i];
@@ -1164,13 +1218,10 @@ export class Wallet {
   }
 
   /**
-   * Send reserve details 
+   * Send reserve details
    */
   private async sendReserveInfoToBank(reservePub: string) {
-    const reserve = await this.q().get<ReserveRecord>(
-      Stores.reserves,
-      reservePub,
-    );
+    const reserve = await oneShotGet(this.db, Stores.reserves, reservePub);
     if (!reserve) {
       throw Error("reserve not in db");
     }
@@ -1191,7 +1242,7 @@ export class Wallet {
     }
 
     if (status.transfer_done) {
-      await this.q().mutate(Stores.reserves, reservePub, r => {
+      await oneShotMutate(this.db, Stores.reserves, reservePub, r => {
         r.timestamp_confirmed = now;
         return r;
       });
@@ -1207,7 +1258,7 @@ export class Wallet {
         console.log("bank error response", e);
         throw e;
       }
-      await this.q().mutate(Stores.reserves, reservePub, r => {
+      await oneShotMutate(this.db, Stores.reserves, reservePub, r => {
         r.timestamp_reserve_info_posted = now;
         return r;
       });
@@ -1238,10 +1289,7 @@ export class Wallet {
       // Sometimes though, we want to try again faster.
       let maxTimeout = 3000 * 60;
       try {
-        const reserve = await this.q().get<ReserveRecord>(
-          Stores.reserves,
-          reservePub,
-        );
+        const reserve = await oneShotGet(this.db, Stores.reserves, reservePub);
         if (!reserve) {
           isHardError = true;
           throw Error("reserve not in db");
@@ -1302,7 +1350,7 @@ export class Wallet {
     const op = openPromise<void>();
 
     const processPreCoinInternal = async (retryDelayMs: number = 200) => {
-      const preCoin = await this.q().get(Stores.precoins, preCoinPub);
+      const preCoin = await oneShotGet(this.db, Stores.precoins, preCoinPub);
       if (!preCoin) {
         console.log("processPreCoin: preCoinPub not found");
         return;
@@ -1325,7 +1373,8 @@ export class Wallet {
       this.processPreCoinConcurrent++;
 
       try {
-        const exchange = await this.q().get(
+        const exchange = await oneShotGet(
+          this.db,
           Stores.exchanges,
           preCoin.exchangeBaseUrl,
         );
@@ -1333,7 +1382,7 @@ export class Wallet {
           console.error("db inconsistent: exchange for precoin not found");
           return;
         }
-        const denom = await this.q().get(Stores.denominations, [
+        const denom = await oneShotGet(this.db, Stores.denominations, [
           preCoin.exchangeBaseUrl,
           preCoin.denomPub,
         ]);
@@ -1358,11 +1407,15 @@ export class Wallet {
           return r;
         };
 
-        await this.q()
-          .mutate(Stores.reserves, preCoin.reservePub, mutateReserve)
-          .delete(Stores.precoins, coin.coinPub)
-          .add(Stores.coins, coin)
-          .finish();
+        await runWithWriteTransaction(
+          this.db,
+          [Stores.reserves, Stores.precoins, Stores.coins],
+          async tx => {
+            await tx.mutate(Stores.reserves, preCoin.reservePub, 
mutateReserve);
+            await tx.delete(Stores.precoins, coin.coinPub);
+            await tx.add(Stores.coins, coin);
+          },
+        );
 
         this.badge.showNotification();
 
@@ -1402,20 +1455,6 @@ export class Wallet {
     }
   }
 
-  /**
-   * Update the timestamp of when an exchange was used.
-   */
-  async updateExchangeUsedTime(exchangeBaseUrl: string): Promise<void> {
-    const now = new Date().getTime();
-    const update = (r: ExchangeRecord) => {
-      r.lastUsedTime = now;
-      return r;
-    };
-    await this.q()
-      .mutate(Stores.exchanges, exchangeBaseUrl, update)
-      .finish();
-  }
-
   /**
    * Create a reserve, but do not flag it as confirmed yet.
    *
@@ -1451,38 +1490,46 @@ export class Wallet {
       const rec = {
         paytoUri: senderWire,
       };
-      await this.q()
-        .put(Stores.senderWires, rec)
-        .finish();
+      await oneShotPut(this.db, Stores.senderWires, rec);
     }
 
-    await this.updateExchangeUsedTime(req.exchange);
     const exchangeInfo = await this.updateExchangeFromUrl(req.exchange);
+    const exchangeDetails = exchangeInfo.details;
+    if (!exchangeDetails) {
+      throw Error("exchange not updated");
+    }
     const { isAudited, isTrusted } = await this.getExchangeTrust(exchangeInfo);
-    let currencyRecord = await this.q().get(
+    let currencyRecord = await oneShotGet(
+      this.db,
       Stores.currencies,
-      exchangeInfo.currency,
+      exchangeDetails.currency,
     );
     if (!currencyRecord) {
       currencyRecord = {
         auditors: [],
         exchanges: [],
         fractionalDigits: 2,
-        name: exchangeInfo.currency,
+        name: exchangeDetails.currency,
       };
     }
 
     if (!isAudited && !isTrusted) {
       currencyRecord.exchanges.push({
         baseUrl: req.exchange,
-        exchangePub: exchangeInfo.masterPublicKey,
+        exchangePub: exchangeDetails.masterPublicKey,
       });
     }
 
-    await this.q()
-      .put(Stores.currencies, currencyRecord)
-      .put(Stores.reserves, reserveRecord)
-      .finish();
+    const cr: CurrencyRecord = currencyRecord;
+
+    runWithWriteTransaction(
+      this.db,
+      [Stores.currencies, Stores.reserves],
+      async tx => {
+        await tx.put(Stores.currencies, cr);
+        await tx.put(Stores.reserves, reserveRecord);
+      },
+    );
 
     if (req.bankWithdrawStatusUrl) {
       this.processReserve(keypair.pub);
@@ -1506,17 +1553,13 @@ export class Wallet {
    */
   async confirmReserve(req: ConfirmReserveRequest): Promise<void> {
     const now = new Date().getTime();
-    const reserve: ReserveRecord | undefined = await this.q().get<
-      ReserveRecord
-    >(Stores.reserves, req.reservePub);
+    const reserve = await oneShotGet(this.db, Stores.reserves, req.reservePub);
     if (!reserve) {
       console.error("Unable to confirm reserve, not found in DB");
       return;
     }
     reserve.timestamp_confirmed = now;
-    await this.q()
-      .put(Stores.reserves, reserve)
-      .finish();
+    await oneShotPut(this.db, Stores.reserves, reserve);
     this.notifier.notify();
 
     this.processReserve(reserve.reserve_pub);
@@ -1589,22 +1632,33 @@ export class Wallet {
       reservePub: reserve.reserve_pub,
       withdrawalAmount: Amounts.toString(withdrawAmount),
       startTimestamp: stampMsNow,
-    }
+    };
 
-    const preCoinRecords: PreCoinRecord[] = await 
Promise.all(denomsForWithdraw.map(async denom => {
-      return await this.cryptoApi.createPreCoin(denom, reserve);
-    }));
+    const preCoinRecords: PreCoinRecord[] = await Promise.all(
+      denomsForWithdraw.map(async denom => {
+        return await this.cryptoApi.createPreCoin(denom, reserve);
+      }),
+    );
 
-    const totalCoinValue = Amounts.sum(denomsForWithdraw.map(x => 
x.value)).amount
-    const totalCoinWithdrawFee = Amounts.sum(denomsForWithdraw.map(x => 
x.feeWithdraw)).amount
-    const totalWithdrawAmount = Amounts.add(totalCoinValue, 
totalCoinWithdrawFee).amount
+    const totalCoinValue = Amounts.sum(denomsForWithdraw.map(x => x.value))
+      .amount;
+    const totalCoinWithdrawFee = Amounts.sum(
+      denomsForWithdraw.map(x => x.feeWithdraw),
+    ).amount;
+    const totalWithdrawAmount = Amounts.add(
+      totalCoinValue,
+      totalCoinWithdrawFee,
+    ).amount;
 
     function mutateReserve(r: ReserveRecord): ReserveRecord {
       const currentAmount = r.current_amount;
       if (!currentAmount) {
         throw Error("can't withdraw when amount is unknown");
       }
-      r.precoin_amount = Amounts.add(r.precoin_amount, 
totalWithdrawAmount).amount;
+      r.precoin_amount = Amounts.add(
+        r.precoin_amount,
+        totalWithdrawAmount,
+      ).amount;
       const result = Amounts.sub(currentAmount, totalWithdrawAmount);
       if (result.saturated) {
         console.error("can't create precoins, saturated");
@@ -1623,11 +1677,17 @@ export class Wallet {
     // This will fail and throw an exception if the remaining amount in the
     // reserve is too low to create a pre-coin.
     try {
-      await this.q()
-        .putAll(Stores.precoins, preCoinRecords)
-        .put(Stores.withdrawals, withdrawalRecord)
-        .mutate(Stores.reserves, reserve.reserve_pub, mutateReserve)
-        .finish();
+      await runWithWriteTransaction(
+        this.db,
+        [Stores.precoins, Stores.withdrawals, Stores.reserves],
+        async tx => {
+          for (let pcr of preCoinRecords) {
+            await tx.put(Stores.precoins, pcr);
+          }
+          await tx.mutate(Stores.reserves, reserve.reserve_pub, mutateReserve);
+          await tx.put(Stores.withdrawals, withdrawalRecord);
+        },
+      );
     } catch (e) {
       return;
     }
@@ -1642,10 +1702,7 @@ export class Wallet {
    * by quering the reserve's exchange.
    */
   private async updateReserve(reservePub: string): Promise<ReserveRecord> {
-    const reserve = await this.q().get<ReserveRecord>(
-      Stores.reserves,
-      reservePub,
-    );
+    const reserve = await oneShotGet(this.db, Stores.reserves, reservePub);
     if (!reserve) {
       throw Error("reserve not in db");
     }
@@ -1669,44 +1726,24 @@ export class Wallet {
       throw Error();
     }
     reserve.current_amount = Amounts.parseOrThrow(reserveInfo.balance);
-    await this.q()
-      .put(Stores.reserves, reserve)
-      .finish();
+    await oneShotPut(this.db, Stores.reserves, reserve);
     this.notifier.notify();
     return reserve;
   }
 
-  /**
-   * Get the wire information for the exchange with the given base URL.
-   */
-  async getWireInfo(exchangeBaseUrl: string): Promise<ExchangeWireJson> {
-    exchangeBaseUrl = canonicalizeBaseUrl(exchangeBaseUrl);
-    const reqUrl = new URI("wire")
-      .absoluteTo(exchangeBaseUrl)
-      .addQuery("cacheBreaker", WALLET_CACHE_BREAKER_CLIENT_VERSION);
-    const resp = await this.http.get(reqUrl.href());
-
-    if (resp.status !== 200) {
-      throw Error("/wire request failed");
-    }
-
-    const wiJson = resp.responseJson;
-    if (!wiJson) {
-      throw Error("/wire response malformed");
-    }
-
-    return ExchangeWireJson.checked(wiJson);
-  }
-
-  async getPossibleDenoms(exchangeBaseUrl: string) {
-    return this.q()
-      .iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchangeBaseUrl)
-      .filter(
-        d =>
-          d.status === DenominationStatus.Unverified ||
-          d.status === DenominationStatus.VerifiedGood,
-      )
-      .toArray();
+  async getPossibleDenoms(
+    exchangeBaseUrl: string,
+  ): Promise<DenominationRecord[]> {
+    return await oneShotIterIndex(
+      this.db,
+      Stores.denominations.exchangeBaseUrlIndex,
+      exchangeBaseUrl,
+    ).filter(d => {
+      return (
+        d.status === DenominationStatus.Unverified ||
+        d.status === DenominationStatus.VerifiedGood
+      );
+    });
   }
 
   /**
@@ -1718,19 +1755,21 @@ export class Wallet {
   async getVerifiedSmallestWithdrawAmount(
     exchangeBaseUrl: string,
   ): Promise<AmountJson> {
-    const exchange = await this.q().get(Stores.exchanges, exchangeBaseUrl);
+    const exchange = await oneShotGet(
+      this.db,
+      Stores.exchanges,
+      exchangeBaseUrl,
+    );
     if (!exchange) {
       throw Error(`exchange ${exchangeBaseUrl} not found`);
     }
+    const exchangeDetails = exchange.details;
+    if (!exchangeDetails) {
+      throw Error(`exchange ${exchangeBaseUrl} details not available`);
+    }
+
+    const possibleDenoms = await this.getPossibleDenoms(exchange.baseUrl);
 
-    const possibleDenoms = await this.q()
-      .iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl)
-      .filter(
-        d =>
-          d.status === DenominationStatus.Unverified ||
-          d.status === DenominationStatus.VerifiedGood,
-      )
-      .toArray();
     possibleDenoms.sort((d1, d2) => {
       const a1 = Amounts.add(d1.feeWithdraw, d1.value).amount;
       const a2 = Amounts.add(d2.feeWithdraw, d2.value).amount;
@@ -1743,21 +1782,19 @@ export class Wallet {
       }
       const valid = await this.cryptoApi.isValidDenom(
         denom,
-        exchange.masterPublicKey,
+        exchangeDetails.masterPublicKey,
       );
       if (!valid) {
         denom.status = DenominationStatus.VerifiedBad;
       } else {
         denom.status = DenominationStatus.VerifiedGood;
       }
-      await this.q()
-        .put(Stores.denominations, denom)
-        .finish();
+      await oneShotPut(this.db, Stores.denominations, denom);
       if (valid) {
         return Amounts.add(denom.feeWithdraw, denom.value).amount;
       }
     }
-    return Amounts.getZero(exchange.currency);
+    return Amounts.getZero(exchangeDetails.currency);
   }
 
   /**
@@ -1771,19 +1808,20 @@ export class Wallet {
     exchangeBaseUrl: string,
     amount: AmountJson,
   ): Promise<DenominationRecord[]> {
-    const exchange = await this.q().get(Stores.exchanges, exchangeBaseUrl);
+    const exchange = await oneShotGet(
+      this.db,
+      Stores.exchanges,
+      exchangeBaseUrl,
+    );
     if (!exchange) {
       throw Error(`exchange ${exchangeBaseUrl} not found`);
     }
+    const exchangeDetails = exchange.details;
+    if (!exchangeDetails) {
+      throw Error(`exchange ${exchangeBaseUrl} details not available`);
+    }
 
-    const possibleDenoms = await this.q()
-      .iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl)
-      .filter(
-        d =>
-          d.status === DenominationStatus.Unverified ||
-          d.status === DenominationStatus.VerifiedGood,
-      )
-      .toArray();
+    const possibleDenoms = await this.getPossibleDenoms(exchange.baseUrl);
 
     let allValid = false;
 
@@ -1797,7 +1835,7 @@ export class Wallet {
         if (denom.status === DenominationStatus.Unverified) {
           const valid = await this.cryptoApi.isValidDenom(
             denom,
-            exchange.masterPublicKey,
+            exchangeDetails.masterPublicKey,
           );
           if (!valid) {
             denom.status = DenominationStatus.VerifiedBad;
@@ -1806,9 +1844,7 @@ export class Wallet {
             denom.status = DenominationStatus.VerifiedGood;
             nextPossibleDenoms.push(denom);
           }
-          await this.q()
-            .put(Stores.denominations, denom)
-            .finish();
+          await oneShotPut(this.db, Stores.denominations, denom);
         } else {
           nextPossibleDenoms.push(denom);
         }
@@ -1826,19 +1862,24 @@ export class Wallet {
   ): Promise<{ isTrusted: boolean; isAudited: boolean }> {
     let isTrusted = false;
     let isAudited = false;
-    const currencyRecord = await this.q().get(
+    const exchangeDetails = exchangeInfo.details;
+    if (!exchangeDetails) {
+      throw Error(`exchange ${exchangeInfo.baseUrl} details not available`);
+    }
+    const currencyRecord = await oneShotGet(
+      this.db,
       Stores.currencies,
-      exchangeInfo.currency,
+      exchangeDetails.currency,
     );
     if (currencyRecord) {
       for (const trustedExchange of currencyRecord.exchanges) {
-        if (trustedExchange.exchangePub === exchangeInfo.masterPublicKey) {
+        if (trustedExchange.exchangePub === exchangeDetails.masterPublicKey) {
           isTrusted = true;
           break;
         }
       }
       for (const trustedAuditor of currencyRecord.auditors) {
-        for (const exchangeAuditor of exchangeInfo.auditors) {
+        for (const exchangeAuditor of exchangeDetails.auditors) {
           if (trustedAuditor.auditorPub === exchangeAuditor.auditor_pub) {
             isAudited = true;
             break;
@@ -1872,6 +1913,16 @@ export class Wallet {
     amount: AmountJson,
   ): Promise<ReserveCreationInfo> {
     const exchangeInfo = await this.updateExchangeFromUrl(baseUrl);
+    const exchangeDetails = exchangeInfo.details;
+    if (!exchangeDetails) {
+      throw Error(`exchange ${exchangeInfo.baseUrl} details not available`);
+    }
+    const exchangeWireInfo = exchangeInfo.wireInfo;
+    if (!exchangeWireInfo) {
+      throw Error(
+        `exchange ${exchangeInfo.baseUrl} wire details not available`,
+      );
+    }
 
     const selectedDenoms = await this.getVerifiedWithdrawDenomList(
       baseUrl,
@@ -1887,16 +1938,8 @@ export class Wallet {
       )
       .reduce((a, b) => Amounts.add(a, b).amount);
 
-    const wireInfo = await this.getWireInfo(baseUrl);
-
-    const wireFees = await this.q().get(Stores.exchangeWireFees, baseUrl);
-    if (!wireFees) {
-      // should never happen unless DB is inconsistent
-      throw Error(`no wire fees found for exchange ${baseUrl}`);
-    }
-
     const exchangeWireAccounts: string[] = [];
-    for (let account of wireInfo.accounts) {
+    for (let account of exchangeWireInfo.accounts) {
       exchangeWireAccounts.push(account.url);
     }
 
@@ -1910,14 +1953,15 @@ export class Wallet {
       }
     }
 
-    const possibleDenoms =
-      (await this.q()
-        .iterIndex(Stores.denominations.exchangeBaseUrlIndex, baseUrl)
-        .filter(d => d.isOffered)
-        .toArray()) || [];
+    const possibleDenoms = await oneShotIterIndex(
+      this.db,
+      Stores.denominations.exchangeBaseUrlIndex,
+      baseUrl,
+    ).filter(d => d.isOffered);
 
     const trustedAuditorPubs = [];
-    const currencyRecord = await this.q().get<CurrencyRecord>(
+    const currencyRecord = await oneShotGet(
+      this.db,
       Stores.currencies,
       amount.currency,
     );
@@ -1928,10 +1972,10 @@ export class Wallet {
     }
 
     let versionMatch;
-    if (exchangeInfo.protocolVersion) {
+    if (exchangeDetails.protocolVersion) {
       versionMatch = LibtoolVersion.compare(
         WALLET_PROTOCOL_VERSION,
-        exchangeInfo.protocolVersion,
+        exchangeDetails.protocolVersion,
       );
 
       if (
@@ -1940,10 +1984,10 @@ export class Wallet {
         versionMatch.currentCmp === -1
       ) {
         console.warn(
-          `wallet version ${WALLET_PROTOCOL_VERSION} might be outdated 
(exchange has ${exchangeInfo.protocolVersion}), checking for updates`,
+          `wallet version ${WALLET_PROTOCOL_VERSION} might be outdated 
(exchange has ${exchangeDetails.protocolVersion}), checking for updates`,
         );
         if (isFirefox()) {
-          console.log("skipping update check on Firefox")
+          console.log("skipping update check on Firefox");
         } else {
           chrome.runtime.requestUpdateCheck((status, details) => {
             console.log("update check status:", status);
@@ -1956,7 +2000,7 @@ export class Wallet {
       earliestDepositExpiration,
       exchangeInfo,
       exchangeWireAccounts,
-      exchangeVersion: exchangeInfo.protocolVersion || "unknown",
+      exchangeVersion: exchangeDetails.protocolVersion || "unknown",
       isAudited,
       isTrusted,
       numOfferedDenoms: possibleDenoms.length,
@@ -1965,7 +2009,7 @@ export class Wallet {
       trustedAuditorPubs,
       versionMatch,
       walletVersion: WALLET_PROTOCOL_VERSION,
-      wireFees,
+      wireFees: exchangeWireInfo,
       withdrawFee: acc,
     };
     return ret;
@@ -1975,8 +2019,19 @@ export class Wallet {
     exchangeBaseUrl: string,
     supportedTargetTypes: string[],
   ): Promise<string> {
-    const wireInfo = await this.getWireInfo(exchangeBaseUrl);
-    for (let account of wireInfo.accounts) {
+    const exchangeRecord = await oneShotGet(
+      this.db,
+      Stores.exchanges,
+      exchangeBaseUrl,
+    );
+    if (!exchangeRecord) {
+      throw Error(`Exchange '${exchangeBaseUrl}' not found.`);
+    }
+    const exchangeWireInfo = exchangeRecord.wireInfo;
+    if (!exchangeWireInfo) {
+      throw Error(`Exchange wire info for '${exchangeBaseUrl}' not found.`);
+    }
+    for (let account of exchangeWireInfo.accounts) {
       const paytoUri = new URI(account.url);
       if (supportedTargetTypes.includes(paytoUri.authority())) {
         return account.url;
@@ -1986,233 +2041,180 @@ export class Wallet {
   }
 
   /**
-   * Update or add exchange DB entry by fetching the /keys information.
+   * Update or add exchange DB entry by fetching the /keys and /wire 
information.
    * Optionally link the reserve entry to the new or existing
    * exchange entry in then DB.
    */
-  async updateExchangeFromUrl(baseUrl: string): Promise<ExchangeRecord> {
-    baseUrl = canonicalizeBaseUrl(baseUrl);
-    const keysUrl = new URI("keys")
-      .absoluteTo(baseUrl)
-      .addQuery("cacheBreaker", WALLET_CACHE_BREAKER_CLIENT_VERSION);
-    const keysResp = await this.http.get(keysUrl.href());
-    if (keysResp.status !== 200) {
-      throw Error("/keys request failed");
-    }
-    const exchangeKeysJson = KeysJson.checked(keysResp.responseJson);
-    const exchangeWire = await this.getWireInfo(baseUrl);
-    return this.updateExchangeFromJson(baseUrl, exchangeKeysJson, 
exchangeWire);
-  }
-
-  private async suspendCoins(exchangeInfo: ExchangeRecord): Promise<void> {
-    const resultSuspendedCoins = await this.q()
-      .iterIndex(Stores.coins.exchangeBaseUrlIndex, exchangeInfo.baseUrl)
-      .indexJoinLeft(
-        Stores.denominations.exchangeBaseUrlIndex,
-        e => e.exchangeBaseUrl,
-      )
-      .fold(
-        (
-          cd: JoinLeftResult<CoinRecord, DenominationRecord>,
-          suspendedCoins: CoinRecord[],
-        ) => {
-          if (!cd.right || !cd.right.isOffered) {
-            return Array.prototype.concat(suspendedCoins, [cd.left]);
-          }
-          return Array.prototype.concat(suspendedCoins);
-        },
-        [],
-      );
-
-    const q = this.q();
-    resultSuspendedCoins.map((c: CoinRecord) => {
-      Wallet.enableTracing && console.log("suspending coin", c);
-      c.suspended = true;
-      q.put(Stores.coins, c);
-      this.badge.showNotification();
-      this.notifier.notify();
-    });
-    await q.finish();
-  }
-
-  private async updateExchangeFromJson(
+  async updateExchangeFromUrl(
     baseUrl: string,
-    exchangeKeysJson: KeysJson,
-    wireMethodDetails: ExchangeWireJson,
+    force: boolean = false,
   ): Promise<ExchangeRecord> {
-    // FIXME: all this should probably be commited atomically
-    const updateTimeSec = getTalerStampSec(exchangeKeysJson.list_issue_date);
-    if (updateTimeSec === null) {
-      throw Error("invalid update time");
-    }
-
-    if (exchangeKeysJson.denoms.length === 0) {
-      throw Error("exchange doesn't offer any denominations");
-    }
-
-    const r = await this.q().get<ExchangeRecord>(Stores.exchanges, baseUrl);
-
-    let exchangeInfo: ExchangeRecord;
+    const now = getTimestampNow();
+    baseUrl = canonicalizeBaseUrl(baseUrl);
 
+    const r = await oneShotGet(this.db, Stores.exchanges, baseUrl);
     if (!r) {
-      exchangeInfo = {
-        auditors: exchangeKeysJson.auditors,
-        baseUrl,
-        currency: Amounts.parseOrThrow(exchangeKeysJson.denoms[0].value)
-          .currency,
-        lastUpdateTime: updateTimeSec,
-        lastUsedTime: 0,
-        masterPublicKey: exchangeKeysJson.master_public_key,
+      const newExchangeRecord: ExchangeRecord = {
+        baseUrl: baseUrl,
+        details: undefined,
+        wireInfo: undefined,
+        updateStatus: ExchangeUpdateStatus.FETCH_KEYS,
+        updateStarted: now,
       };
-      Wallet.enableTracing && console.log("making fresh exchange");
+      await oneShotPut(this.db, Stores.exchanges, newExchangeRecord);
     } else {
-      if (updateTimeSec < r.lastUpdateTime) {
-        Wallet.enableTracing && console.log("outdated /keys, not updating");
-        return r;
-      }
-      exchangeInfo = r;
-      exchangeInfo.lastUpdateTime = updateTimeSec;
-      Wallet.enableTracing && console.log("updating old exchange");
-    }
-
-    const updatedExchangeInfo = await this.updateExchangeInfo(
-      exchangeInfo,
-      exchangeKeysJson,
-    );
-    await this.suspendCoins(updatedExchangeInfo);
-    updatedExchangeInfo.protocolVersion = exchangeKeysJson.version;
-
-    await this.q()
-      .put(Stores.exchanges, updatedExchangeInfo)
-      .finish();
-
-    let oldWireFees = await this.q().get(Stores.exchangeWireFees, baseUrl);
-    if (!oldWireFees) {
-      oldWireFees = {
-        exchangeBaseUrl: baseUrl,
-        feesForType: {},
-      };
-    }
-
-    for (const paytoTargetType in wireMethodDetails.fees) {
-      let latestFeeStamp = 0;
-      const newFeeDetails = wireMethodDetails.fees[paytoTargetType];
-      const oldFeeDetails = oldWireFees.feesForType[paytoTargetType] || [];
-      oldWireFees.feesForType[paytoTargetType] = oldFeeDetails;
-      for (const oldFee of oldFeeDetails) {
-        if (oldFee.endStamp > latestFeeStamp) {
-          latestFeeStamp = oldFee.endStamp;
-        }
-      }
-      for (const fee of newFeeDetails) {
-        const start = getTalerStampSec(fee.start_date);
-        if (start === null) {
-          console.error("invalid start stamp in fee", fee);
-          continue;
-        }
-        if (start < latestFeeStamp) {
-          continue;
-        }
-        const end = getTalerStampSec(fee.end_date);
-        if (end === null) {
-          console.error("invalid end stamp in fee", fee);
-          continue;
+      runWithWriteTransaction(this.db, [Stores.exchanges], async t => {
+        const rec = await t.get(Stores.exchanges, baseUrl);
+        if (!rec) {
+          return;
         }
-        const wf: WireFee = {
-          closingFee: Amounts.parseOrThrow(fee.closing_fee),
-          endStamp: end,
-          sig: fee.sig,
-          startStamp: start,
-          wireFee: Amounts.parseOrThrow(fee.wire_fee),
-        };
-        const valid: boolean = await this.cryptoApi.isValidWireFee(
-          paytoTargetType,
-          wf,
-          exchangeInfo.masterPublicKey,
-        );
-        if (!valid) {
-          console.error("fee signature invalid", fee);
-          throw Error("fee signature invalid");
+        if (rec.updateStatus != ExchangeUpdateStatus.NONE && !force) {
+          return;
         }
-        oldFeeDetails.push(wf);
-      }
+        rec.updateStarted = now;
+        rec.updateStatus = ExchangeUpdateStatus.FETCH_KEYS;
+        t.put(Stores.exchanges, rec);
+      });
     }
 
-    await this.q().put(Stores.exchangeWireFees, oldWireFees);
+    await this.updateExchangeWithKeys(baseUrl);
+    await this.updateExchangeWithWireInfo(baseUrl);
 
-    if (exchangeKeysJson.payback) {
-      for (const payback of exchangeKeysJson.payback) {
-        const denom = await this.q().getIndexed(
-          Stores.denominations.denomPubHashIndex,
-          payback.h_denom_pub,
-        );
-        if (!denom) {
-          continue;
-        }
-        Wallet.enableTracing && console.log(`cashing back denom`, denom);
-        const coins = await this.q()
-          .iterIndex(Stores.coins.denomPubIndex, denom.denomPub)
-          .toArray();
-        for (const coin of coins) {
-          this.payback(coin.coinPub);
-        }
-      }
+    const updatedExchange = await oneShotGet(
+      this.db,
+      Stores.exchanges,
+      baseUrl,
+    );
+
+    if (!updatedExchange) {
+      // This should practically never happen
+      throw Error("exchange not found");
     }
+    return updatedExchange;
+  }
 
-    return updatedExchangeInfo;
+  private async setExchangeError(
+    baseUrl: string,
+    err: OperationError,
+  ): Promise<void> {
+    const mut = (exchange: ExchangeRecord) => {
+      exchange.lastError = err;
+      return exchange;
+    };
+    await oneShotMutate(this.db, Stores.exchanges, baseUrl, mut);
   }
 
-  private async updateExchangeInfo(
-    exchangeInfo: ExchangeRecord,
-    newKeys: KeysJson,
-  ): Promise<ExchangeRecord> {
-    if (exchangeInfo.masterPublicKey !== newKeys.master_public_key) {
-      throw Error("public keys do not match");
+  /**
+   * Fetch the exchange's /keys and update our database accordingly.
+   *
+   * Exceptions thrown in this method must be caught and reported
+   * in the pending operations.
+   */
+  private async updateExchangeWithKeys(baseUrl: string): Promise<void> {
+    const existingExchangeRecord = await oneShotGet(
+      this.db,
+      Stores.exchanges,
+      baseUrl,
+    );
+
+    if (
+      existingExchangeRecord?.updateStatus != ExchangeUpdateStatus.FETCH_KEYS
+    ) {
+      return;
+    }
+    const keysUrl = new URI("keys")
+      .absoluteTo(baseUrl)
+      .addQuery("cacheBreaker", WALLET_CACHE_BREAKER_CLIENT_VERSION);
+    let keysResp;
+    try {
+      keysResp = await this.http.get(keysUrl.href());
+    } catch (e) {
+      await this.setExchangeError(baseUrl, {
+        type: "network",
+        details: {},
+        message: `Fetching keys failed: ${e.message}`,
+      });
+      throw e;
+    }
+    let exchangeKeysJson: KeysJson;
+    try {
+      exchangeKeysJson = KeysJson.checked(keysResp.responseJson);
+    } catch (e) {
+      await this.setExchangeError(baseUrl, {
+        type: "protocol-violation",
+        details: {},
+        message: `Parsing /keys response failed: ${e.message}`,
+      });
+      throw e;
     }
 
-    const existingDenoms: {
-      [denomPub: string]: DenominationRecord;
-    } = await this.q()
-      .iterIndex(
-        Stores.denominations.exchangeBaseUrlIndex,
-        exchangeInfo.baseUrl,
-      )
-      .fold(
-        (x: DenominationRecord, acc: typeof existingDenoms) => (
-          (acc[x.denomPub] = x), acc
-        ),
-        {},
-      );
+    const lastUpdateTimestamp = extractTalerStamp(
+      exchangeKeysJson.list_issue_date,
+    );
+    if (!lastUpdateTimestamp) {
+      const m = `Parsing /keys response failed: invalid list_issue_date.`;
+      await this.setExchangeError(baseUrl, {
+        type: "protocol-violation",
+        details: {},
+        message: m,
+      });
+      throw Error(m);
+    }
 
-    const newDenoms: typeof existingDenoms = {};
-    const newAndUnseenDenoms: typeof existingDenoms = {};
+    if (exchangeKeysJson.denoms.length === 0) {
+      const m = "exchange doesn't offer any denominations";
+      await this.setExchangeError(baseUrl, {
+        type: "protocol-violation",
+        details: {},
+        message: m,
+      });
+      throw Error(m);
+    }
 
-    for (const d of newKeys.denoms) {
-      const dr = await this.denominationRecordFromKeys(exchangeInfo.baseUrl, 
d);
-      if (!(d.denom_pub in existingDenoms)) {
-        newAndUnseenDenoms[dr.denomPub] = dr;
-      }
-      newDenoms[dr.denomPub] = dr;
+    const protocolVersion = exchangeKeysJson.version;
+    if (!protocolVersion) {
+      const m = "outdate exchange, no version in /keys response";
+      await this.setExchangeError(baseUrl, {
+        type: "protocol-violation",
+        details: {},
+        message: m,
+      });
+      throw Error(m);
     }
 
-    for (const oldDenomPub in existingDenoms) {
-      if (!(oldDenomPub in newDenoms)) {
-        const d = existingDenoms[oldDenomPub];
-        d.isOffered = false;
+    const currency = Amounts.parseOrThrow(exchangeKeysJson.denoms[0].value)
+      .currency;
+
+    const mutExchangeRecord = (r: ExchangeRecord) => {
+      if (r.updateStatus != ExchangeUpdateStatus.FETCH_KEYS) {
+        console.log("not updating, wrong state (concurrent modification?)");
+        return undefined;
       }
-    }
+      r.details = {
+        currency,
+        protocolVersion,
+        lastUpdateTime: lastUpdateTimestamp,
+        masterPublicKey: exchangeKeysJson.master_public_key,
+        auditors: exchangeKeysJson.auditors,
+      };
+      r.updateStatus = ExchangeUpdateStatus.FETCH_WIRE;
+      r.lastError = undefined;
+      return r;
+    };
+  }
 
-    await this.q()
-      .putAll(
-        Stores.denominations,
-        Object.keys(newAndUnseenDenoms).map(d => newAndUnseenDenoms[d]),
-      )
-      .putAll(
-        Stores.denominations,
-        Object.keys(existingDenoms).map(d => existingDenoms[d]),
-      )
-      .finish();
-    return exchangeInfo;
+  private async updateExchangeWithWireInfo(exchangeBaseUrl: string) {
+    exchangeBaseUrl = canonicalizeBaseUrl(exchangeBaseUrl);
+    const reqUrl = new URI("wire")
+      .absoluteTo(exchangeBaseUrl)
+      .addQuery("cacheBreaker", WALLET_CACHE_BREAKER_CLIENT_VERSION);
+    const resp = await this.http.get(reqUrl.href());
+
+    const wiJson = resp.responseJson;
+    if (!wiJson) {
+      throw Error("/wire response malformed");
+    }
+    const wireInfo = ExchangeWireJson.checked(wiJson);
   }
 
   /**
@@ -2253,127 +2255,114 @@ export class Wallet {
       entryEx[field] = Amounts.add(entryEx[field], amount).amount;
     }
 
-    function collectBalances(c: CoinRecord, balance: WalletBalance) {
-      if (c.suspended) {
-        return balance;
-      }
-      if (c.status === CoinStatus.Fresh) {
-        addTo(balance, "available", c.currentAmount, c.exchangeBaseUrl);
-        return balance;
-      }
-      if (c.status === CoinStatus.Dirty) {
-        addTo(balance, "pendingIncoming", c.currentAmount, c.exchangeBaseUrl);
-        addTo(balance, "pendingIncomingDirty", c.currentAmount, 
c.exchangeBaseUrl);
-        return balance;
-      }
-      return balance;
-    }
-
-    function collectPendingWithdraw(r: ReserveRecord, balance: WalletBalance) {
-      if (!r.timestamp_confirmed) {
-        return balance;
-      }
-      let amount = Amounts.getZero(r.requested_amount.currency);
-      /*
-      let amount = r.current_amount;
-      if (!amount) {
-        amount = r.requested_amount;
-      }
-      */
-      amount = Amounts.add(amount, r.precoin_amount).amount;
-      if (Amounts.cmp(smallestWithdraw[r.exchange_base_url], amount) < 0) {
-        addTo(balance, "pendingIncoming", amount, r.exchange_base_url);
-        addTo(balance, "pendingIncomingWithdraw", amount, r.exchange_base_url);
-      }
-      return balance;
-    }
-
-    function collectPaybacks(r: ReserveRecord, balance: WalletBalance) {
-      if (!r.hasPayback) {
-        return balance;
-      }
-      if (
-        Amounts.cmp(smallestWithdraw[r.exchange_base_url], r.current_amount!) <
-        0
-      ) {
-        addTo(balance, "paybackAmount", r.current_amount!, 
r.exchange_base_url);
-      }
-      return balance;
-    }
+    const balanceStore = {
+      byCurrency: {},
+      byExchange: {},
+    };
 
-    function collectPendingRefresh(
-      r: RefreshSessionRecord,
-      balance: WalletBalance,
-    ) {
-      // Don't count finished refreshes, since the refresh already resulted
-      // in coins being added to the wallet.
-      if (r.finished) {
-        return balance;
-      }
-      addTo(balance, "pendingIncoming", r.valueOutput, r.exchangeBaseUrl);
-      addTo(balance, "pendingIncomingRefresh", r.valueOutput, 
r.exchangeBaseUrl);
+    await runWithWriteTransaction(
+      this.db,
+      [Stores.coins, Stores.refresh, Stores.reserves, Stores.purchases],
+      async tx => {
+        await tx.iter(Stores.coins).forEach(c => {
+          if (c.suspended) {
+            return;
+          }
+          if (c.status === CoinStatus.Fresh) {
+            addTo(
+              balanceStore,
+              "available",
+              c.currentAmount,
+              c.exchangeBaseUrl,
+            );
+          }
+          if (c.status === CoinStatus.Dirty) {
+            addTo(
+              balanceStore,
+              "pendingIncoming",
+              c.currentAmount,
+              c.exchangeBaseUrl,
+            );
+            addTo(
+              balanceStore,
+              "pendingIncomingDirty",
+              c.currentAmount,
+              c.exchangeBaseUrl,
+            );
+          }
+        });
+        await tx.iter(Stores.refresh).forEach(r => {
+          // Don't count finished refreshes, since the refresh already resulted
+          // in coins being added to the wallet.
+          if (r.finished) {
+            return;
+          }
+          addTo(
+            balanceStore,
+            "pendingIncoming",
+            r.valueOutput,
+            r.exchangeBaseUrl,
+          );
+          addTo(
+            balanceStore,
+            "pendingIncomingRefresh",
+            r.valueOutput,
+            r.exchangeBaseUrl,
+          );
+        });
 
-      return balance;
-    }
+        await tx.iter(Stores.reserves).forEach(r => {
+          if (!r.timestamp_confirmed) {
+            return;
+          }
+          let amount = Amounts.getZero(r.requested_amount.currency);
+          amount = Amounts.add(amount, r.precoin_amount).amount;
+          addTo(balanceStore, "pendingIncoming", amount, r.exchange_base_url);
+          addTo(
+            balanceStore,
+            "pendingIncomingWithdraw",
+            amount,
+            r.exchange_base_url,
+          );
+        });
 
-    function collectPayments(t: PurchaseRecord, balance: WalletBalance) {
-      if (t.finished) {
-        return balance;
-      }
-      for (const c of t.payReq.coins) {
-        addTo(
-          balance,
-          "pendingPayment",
-          Amounts.parseOrThrow(c.contribution),
-          c.exchange_url,
-        );
-      }
-      return balance;
-    }
+        await tx.iter(Stores.reserves).forEach(r => {
+          if (!r.hasPayback) {
+            return;
+          }
+          addTo(
+            balanceStore,
+            "paybackAmount",
+            r.current_amount!,
+            r.exchange_base_url,
+          );
+          return balanceStore;
+        });
 
-    function collectSmallestWithdraw(
-      e: JoinResult<ExchangeRecord, DenominationRecord>,
-      sw: any,
-    ) {
-      let min = sw[e.left.baseUrl];
-      const v = Amounts.add(e.right.value, e.right.feeWithdraw).amount;
-      if (!min) {
-        min = v;
-      } else if (Amounts.cmp(v, min) < 0) {
-        min = v;
-      }
-      sw[e.left.baseUrl] = min;
-      return sw;
-    }
+        await tx.iter(Stores.purchases).forEach(t => {
+          if (t.finished) {
+            return;
+          }
+          for (const c of t.payReq.coins) {
+            addTo(
+              balanceStore,
+              "pendingPayment",
+              Amounts.parseOrThrow(c.contribution),
+              c.exchange_url,
+            );
+          }
+        });
+      },
+    );
 
-    const balanceStore = {
-      byCurrency: {},
-      byExchange: {},
-    };
-    // Mapping from exchange pub to smallest
-    // possible amount we can withdraw
-    let smallestWithdraw: { [baseUrl: string]: AmountJson } = {};
-
-    smallestWithdraw = await this.q()
-      .iter(Stores.exchanges)
-      .indexJoin(Stores.denominations.exchangeBaseUrlIndex, x => x.baseUrl)
-      .fold(collectSmallestWithdraw, {});
-
-    const tx = this.q();
-    tx.iter(Stores.coins).fold(collectBalances, balanceStore);
-    tx.iter(Stores.refresh).fold(collectPendingRefresh, balanceStore);
-    tx.iter(Stores.reserves).fold(collectPendingWithdraw, balanceStore);
-    tx.iter(Stores.reserves).fold(collectPaybacks, balanceStore);
-    tx.iter(Stores.purchases).fold(collectPayments, balanceStore);
-    await tx.finish();
-    Wallet.enableTracing && console.log("computed balances:", balanceStore)
+    Wallet.enableTracing && console.log("computed balances:", balanceStore);
     return balanceStore;
   }
 
   async createRefreshSession(
     oldCoinPub: string,
   ): Promise<RefreshSessionRecord | undefined> {
-    const coin = await this.q().get<CoinRecord>(Stores.coins, oldCoinPub);
+    const coin = await oneShotGet(this.db, Stores.coins, oldCoinPub);
 
     if (!coin) {
       throw Error("coin not found");
@@ -2389,7 +2378,7 @@ export class Wallet {
       throw Error("db inconsistent");
     }
 
-    const oldDenom = await this.q().get(Stores.denominations, [
+    const oldDenom = await oneShotGet(this.db, Stores.denominations, [
       exchange.baseUrl,
       coin.denomPub,
     ]);
@@ -2398,9 +2387,11 @@ export class Wallet {
       throw Error("db inconsistent");
     }
 
-    const availableDenoms: DenominationRecord[] = await this.q()
-      .iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl)
-      .toArray();
+    const availableDenoms: DenominationRecord[] = await oneShotIterIndex(
+      this.db,
+      Stores.denominations.exchangeBaseUrlIndex,
+      exchange.baseUrl,
+    ).toArray();
 
     const availableAmount = Amounts.sub(coin.currentAmount, 
oldDenom.feeRefresh)
       .amount;
@@ -2421,7 +2412,7 @@ export class Wallet {
           )} too small`,
         );
       coin.status = CoinStatus.Useless;
-      await this.q().put(Stores.coins, coin);
+      await oneShotPut(this.db, Stores.coins, coin);
       this.notifier.notify();
       return undefined;
     }
@@ -2445,16 +2436,20 @@ export class Wallet {
       return c;
     }
 
+    let key;
+
     // Store refresh session and subtract refreshed amount from
     // coin in the same transaction.
-    const query = this.q();
-    query
-      .put(Stores.refresh, refreshSession, "refreshKey")
-      .mutate(Stores.coins, coin.coinPub, mutateCoin);
-    await query.finish();
+    await runWithWriteTransaction(
+      this.db,
+      [Stores.refresh, Stores.coins],
+      async tx => {
+        key = await tx.put(Stores.refresh, refreshSession);
+        await tx.mutate(Stores.coins, coin.coinPub, mutateCoin);
+      },
+    );
     this.notifier.notify();
 
-    const key = query.key("refreshKey");
     if (!key || typeof key !== "number") {
       throw Error("insert failed");
     }
@@ -2466,18 +2461,23 @@ export class Wallet {
 
   async refresh(oldCoinPub: string): Promise<void> {
     const refreshImpl = async () => {
-      const oldRefreshSessions = await this.q()
-        .iter(Stores.refresh)
-        .toArray();
+      const oldRefreshSessions = await oneShotIter(
+        this.db,
+        Stores.refresh,
+      ).toArray();
       for (const session of oldRefreshSessions) {
         if (session.finished) {
           continue;
         }
         Wallet.enableTracing &&
-          console.log("waiting for unfinished old refresh session for", 
oldCoinPub, session);
+          console.log(
+            "waiting for unfinished old refresh session for",
+            oldCoinPub,
+            session,
+          );
         await this.continueRefreshSession(session);
       }
-      const coin = await this.q().get(Stores.coins, oldCoinPub);
+      const coin = await oneShotGet(this.db, Stores.coins, oldCoinPub);
       if (!coin) {
         console.warn("can't refresh, coin not in database");
         return;
@@ -2486,7 +2486,11 @@ export class Wallet {
         coin.status === CoinStatus.Useless ||
         coin.status === CoinStatus.Fresh
       ) {
-        Wallet.enableTracing && console.log("not refreshing due to coin 
status", CoinStatus[coin.status])
+        Wallet.enableTracing &&
+          console.log(
+            "not refreshing due to coin status",
+            CoinStatus[coin.status],
+          );
         return;
       }
       const refreshSession = await this.createRefreshSession(oldCoinPub);
@@ -2520,10 +2524,7 @@ export class Wallet {
     }
     if (typeof refreshSession.norevealIndex !== "number") {
       await this.refreshMelt(refreshSession);
-      const r = await this.q().get<RefreshSessionRecord>(
-        Stores.refresh,
-        refreshSession.id,
-      );
+      const r = await oneShotGet(this.db, Stores.refresh, refreshSession.id);
       if (!r) {
         throw Error("refresh session does not exist anymore");
       }
@@ -2539,10 +2540,12 @@ export class Wallet {
       return;
     }
 
-    const coin = await this.q().get<CoinRecord>(
+    const coin = await oneShotGet(
+      this.db,
       Stores.coins,
       refreshSession.meltCoinPub,
     );
+
     if (!coin) {
       console.error("can't melt coin, it does not exist");
       return;
@@ -2579,9 +2582,8 @@ export class Wallet {
 
     refreshSession.norevealIndex = norevealIndex;
 
-    await this.q()
-      .put(Stores.refresh, refreshSession)
-      .finish();
+    await oneShotPut(this.db, Stores.refresh, refreshSession);
+
     this.notifier.notify();
   }
 
@@ -2598,7 +2600,8 @@ export class Wallet {
       throw Error("refresh index error");
     }
 
-    const meltCoinRecord = await this.q().get(
+    const meltCoinRecord = await oneShotGet(
+      this.db,
       Stores.coins,
       refreshSession.meltCoinPub,
     );
@@ -2657,10 +2660,7 @@ export class Wallet {
       return;
     }
 
-    const exchange = await this.q().get<ExchangeRecord>(
-      Stores.exchanges,
-      refreshSession.exchangeBaseUrl,
-    );
+    const exchange = await this.findExchange(refreshSession.exchangeBaseUrl);
     if (!exchange) {
       console.error(`exchange ${refreshSession.exchangeBaseUrl} not found`);
       return;
@@ -2669,7 +2669,7 @@ export class Wallet {
     const coins: CoinRecord[] = [];
 
     for (let i = 0; i < respJson.ev_sigs.length; i++) {
-      const denom = await this.q().get(Stores.denominations, [
+      const denom = await oneShotGet(this.db, Stores.denominations, [
         refreshSession.exchangeBaseUrl,
         refreshSession.newDenoms[i],
       ]);
@@ -2702,17 +2702,31 @@ export class Wallet {
 
     refreshSession.finished = true;
 
-    await this.q()
-      .putAll(Stores.coins, coins)
-      .put(Stores.refresh, refreshSession)
-      .finish();
+    await runWithWriteTransaction(
+      this.db,
+      [Stores.coins, Stores.refresh],
+      async tx => {
+        for (let coin of coins) {
+          await tx.put(Stores.coins, coin);
+        }
+        await tx.put(Stores.refresh, refreshSession);
+      },
+    );
     this.notifier.notify();
   }
 
+  async findExchange(
+    exchangeBaseUrl: string,
+  ): Promise<ExchangeRecord | undefined> {
+    return await oneShotGet(this.db, Stores.exchanges, exchangeBaseUrl);
+  }
+
   /**
    * Retrive the full event history for this wallet.
    */
-  async getHistory(historyQuery?: HistoryQuery): Promise<{ history: 
HistoryRecord[] }> {
+  async getHistory(
+    historyQuery?: HistoryQuery,
+  ): Promise<{ history: HistoryRecord[] }> {
     const history: HistoryRecord[] = [];
 
     // FIXME: do pagination instead of generating the full history
@@ -2721,9 +2735,7 @@ export class Wallet {
     // This works as timestamps are guaranteed to be monotonically
     // increasing even
 
-    const proposals = await this.q()
-      .iter<ProposalDownloadRecord>(Stores.proposals)
-      .toArray();
+    const proposals = await oneShotIter(this.db, Stores.proposals).toArray();
     for (const p of proposals) {
       history.push({
         detail: {
@@ -2735,7 +2747,10 @@ export class Wallet {
       });
     }
 
-    const withdrawals = await 
this.q().iter<WithdrawalRecord>(Stores.withdrawals).toArray()
+    const withdrawals = await oneShotIter(
+      this.db,
+      Stores.withdrawals,
+    ).toArray();
     for (const w of withdrawals) {
       history.push({
         detail: {
@@ -2746,9 +2761,7 @@ export class Wallet {
       });
     }
 
-    const purchases = await this.q()
-      .iter<PurchaseRecord>(Stores.purchases)
-      .toArray();
+    const purchases = await oneShotIter(this.db, Stores.purchases).toArray();
     for (const p of purchases) {
       history.push({
         detail: {
@@ -2787,9 +2800,8 @@ export class Wallet {
       }
     }
 
-    const reserves: ReserveRecord[] = await this.q()
-      .iter<ReserveRecord>(Stores.reserves)
-      .toArray();
+    const reserves = await oneShotIter(this.db, Stores.reserves).toArray();
+
     for (const r of reserves) {
       history.push({
         detail: {
@@ -2813,9 +2825,7 @@ export class Wallet {
       }
     }
 
-    const tips: TipRecord[] = await this.q()
-      .iter<TipRecord>(Stores.tips)
-      .toArray();
+    const tips: TipRecord[] = await oneShotIter(this.db, 
Stores.tips).toArray();
     for (const tip of tips) {
       history.push({
         detail: {
@@ -2835,78 +2845,97 @@ export class Wallet {
   }
 
   async getPendingOperations(): Promise<PendingOperationsResponse> {
+    const pendingOperations: PendingOperationInfo[] = [];
+    const exchanges = await this.getExchanges();
+    for (let e of exchanges) {
+      switch (e.updateStatus) {
+        case ExchangeUpdateStatus.NONE:
+          if (!e.details) {
+            pendingOperations.push({
+              type: "bug",
+              message:
+                "Exchange record does not have details, but no update in 
progress.",
+              details: {
+                exchangeBaseUrl: e.baseUrl,
+              },
+            });
+          }
+          break;
+        case ExchangeUpdateStatus.FETCH_KEYS:
+          pendingOperations.push({
+            type: "exchange-update",
+            stage: "fetch-keys",
+            exchangeBaseUrl: e.baseUrl,
+          });
+          break;
+        case ExchangeUpdateStatus.FETCH_WIRE:
+          pendingOperations.push({
+            type: "exchange-update",
+            stage: "fetch-wire",
+            exchangeBaseUrl: e.baseUrl,
+          });
+          break;
+      }
+    }
     return {
-      pendingOperations: []
+      pendingOperations,
     };
   }
 
   async getDenoms(exchangeUrl: string): Promise<DenominationRecord[]> {
-    const denoms = await this.q()
-      .iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchangeUrl)
-      .toArray();
+    const denoms = await oneShotIterIndex(
+      this.db,
+      Stores.denominations.exchangeBaseUrlIndex,
+      exchangeUrl,
+    ).toArray();
     return denoms;
   }
 
   async getProposal(
     proposalId: number,
   ): Promise<ProposalDownloadRecord | undefined> {
-    const proposal = await this.q().get(Stores.proposals, proposalId);
+    const proposal = await oneShotGet(this.db, Stores.proposals, proposalId);
     return proposal;
   }
 
   async getExchanges(): Promise<ExchangeRecord[]> {
-    return this.q()
-      .iter<ExchangeRecord>(Stores.exchanges)
-      .toArray();
+    return await oneShotIter(this.db, Stores.exchanges).toArray();
   }
 
   async getCurrencies(): Promise<CurrencyRecord[]> {
-    return this.q()
-      .iter<CurrencyRecord>(Stores.currencies)
-      .toArray();
+    return await oneShotIter(this.db, Stores.currencies).toArray();
   }
 
   async updateCurrency(currencyRecord: CurrencyRecord): Promise<void> {
     Wallet.enableTracing && console.log("updating currency to", 
currencyRecord);
-    await this.q()
-      .put(Stores.currencies, currencyRecord)
-      .finish();
+    await oneShotPut(this.db, Stores.currencies, currencyRecord);
     this.notifier.notify();
   }
 
   async getReserves(exchangeBaseUrl: string): Promise<ReserveRecord[]> {
-    return this.q()
-      .iter<ReserveRecord>(Stores.reserves)
-      .filter((r: ReserveRecord) => r.exchange_base_url === exchangeBaseUrl)
-      .toArray();
+    return await oneShotIter(this.db, Stores.reserves).filter(
+      r => r.exchange_base_url === exchangeBaseUrl,
+    );
   }
 
   async getCoins(exchangeBaseUrl: string): Promise<CoinRecord[]> {
-    return this.q()
-      .iter<CoinRecord>(Stores.coins)
-      .filter((c: CoinRecord) => c.exchangeBaseUrl === exchangeBaseUrl)
-      .toArray();
+    return await oneShotIter(this.db, Stores.coins).filter(
+      c => c.exchangeBaseUrl === exchangeBaseUrl,
+    );
   }
 
   async getPreCoins(exchangeBaseUrl: string): Promise<PreCoinRecord[]> {
-    return this.q()
-      .iter<PreCoinRecord>(Stores.precoins)
-      .filter((c: PreCoinRecord) => c.exchangeBaseUrl === exchangeBaseUrl)
-      .toArray();
+    return await oneShotIter(this.db, Stores.precoins).filter(
+      c => c.exchangeBaseUrl === exchangeBaseUrl,
+    );
   }
 
-  async hashContract(contract: ContractTerms): Promise<string> {
+  private async hashContract(contract: ContractTerms): Promise<string> {
     return this.cryptoApi.hashString(canonicalJson(contract));
   }
 
-  async getCurrencyRecord(
-    currency: string,
-  ): Promise<CurrencyRecord | undefined> {
-    return this.q().get(Stores.currencies, currency);
-  }
-
   async payback(coinPub: string): Promise<void> {
-    let coin = await this.q().get(Stores.coins, coinPub);
+    let coin = await oneShotGet(this.db, Stores.coins, coinPub);
     if (!coin) {
       throw Error(`Coin ${coinPub} not found, can't request payback`);
     }
@@ -2914,7 +2943,7 @@ export class Wallet {
     if (!reservePub) {
       throw Error(`Can't request payback for a refreshed coin`);
     }
-    const reserve = await this.q().get(Stores.reserves, reservePub);
+    const reserve = await oneShotGet(this.db, Stores.reserves, reservePub);
     if (!reserve) {
       throw Error(`Reserve of coin ${coinPub} not found`);
     }
@@ -2932,9 +2961,14 @@ export class Wallet {
     // technically we might update reserve status before we get the response
     // from the reserve for the payback request.
     reserve.hasPayback = true;
-    await this.q()
-      .put(Stores.coins, coin)
-      .put(Stores.reserves, reserve);
+    await runWithWriteTransaction(
+      this.db,
+      [Stores.coins, Stores.reserves],
+      async tx => {
+        await tx.put(Stores.coins, coin!!);
+        await tx.put(Stores.reserves, reserve);
+      },
+    );
     this.notifier.notify();
 
     const paybackRequest = await this.cryptoApi.createPaybackRequest(coin);
@@ -2947,17 +2981,17 @@ export class Wallet {
     if (paybackConfirmation.reserve_pub !== coin.reservePub) {
       throw Error(`Coin's reserve doesn't match reserve on payback`);
     }
-    coin = await this.q().get(Stores.coins, coinPub);
+    coin = await oneShotGet(this.db, Stores.coins, coinPub);
     if (!coin) {
       throw Error(`Coin ${coinPub} not found, can't confirm payback`);
     }
     coin.status = CoinStatus.PaybackDone;
-    await this.q().put(Stores.coins, coin);
+    await oneShotPut(this.db, Stores.coins, coin);
     this.notifier.notify();
     await this.updateReserve(reservePub!);
   }
 
-  async denominationRecordFromKeys(
+  private async denominationRecordFromKeys(
     exchangeBaseUrl: string,
     denomIn: Denomination,
   ): Promise<DenominationRecord> {
@@ -2983,20 +3017,19 @@ export class Wallet {
   }
 
   async withdrawPaybackReserve(reservePub: string): Promise<void> {
-    const reserve = await this.q().get(Stores.reserves, reservePub);
+    const reserve = await oneShotGet(this.db, Stores.reserves, reservePub);
     if (!reserve) {
       throw Error(`Reserve ${reservePub} does not exist`);
     }
     reserve.hasPayback = false;
-    await this.q().put(Stores.reserves, reserve);
+    await oneShotPut(this.db, Stores.reserves, reserve);
     this.depleteReserve(reserve);
   }
 
   async getPaybackReserves(): Promise<ReserveRecord[]> {
-    return await this.q()
-      .iter(Stores.reserves)
-      .filter(r => r.hasPayback)
-      .toArray();
+    return await oneShotIter(this.db, Stores.reserves).filter(
+      r => r.hasPayback,
+    );
   }
 
   /**
@@ -3009,13 +3042,16 @@ export class Wallet {
 
   async getSenderWireInfos(): Promise<SenderWireInfos> {
     const m: { [url: string]: Set<string> } = {};
-    await this.q()
-      .iter(Stores.exchangeWireFees)
-      .map(x => {
-        const s = (m[x.exchangeBaseUrl] = m[x.exchangeBaseUrl] || new Set());
-        Object.keys(x.feesForType).map(k => s.add(k));
-      })
-      .run();
+
+    await oneShotIter(this.db, Stores.exchanges).forEach(x => {
+      const wi = x.wireInfo;
+      if (!wi) {
+        return;
+      }
+      const s = (m[x.baseUrl] = m[x.baseUrl] || new Set());
+      Object.keys(wi.feesForType).map(k => s.add(k));
+    });
+
     Wallet.enableTracing && console.log(m);
     const exchangeWireTypes: { [url: string]: string[] } = {};
     Object.keys(m).map(e => {
@@ -3023,12 +3059,10 @@ export class Wallet {
     });
 
     const senderWiresSet: Set<string> = new Set();
-    await this.q()
-      .iter(Stores.senderWires)
-      .map(x => {
-        senderWiresSet.add(x.paytoUri);
-      })
-      .run();
+    await oneShotIter(this.db, Stores.senderWires).forEach(x => {
+      senderWiresSet.add(x.paytoUri);
+    });
+
     const senderWires: string[] = Array.from(senderWiresSet);
 
     return {
@@ -3049,11 +3083,15 @@ export class Wallet {
       return;
     }
     const stampSecNow = Math.floor(new Date().getTime() / 1000);
-    const exchange = await this.q().get(Stores.exchanges, req.exchange);
+    const exchange = await this.findExchange(req.exchange);
     if (!exchange) {
       console.error(`Exchange ${req.exchange} not known to the wallet`);
       return;
     }
+    const exchangeDetails = exchange.details;
+    if (!exchangeDetails) {
+      throw Error("exchange information needs to be updated first.");
+    }
     Wallet.enableTracing && console.log("selecting coins for return:", req);
     const cds = await this.getCoinsForReturn(req.exchange, req.amount);
     Wallet.enableTracing && console.log(cds);
@@ -3073,7 +3111,7 @@ export class Wallet {
       amount: Amounts.toString(req.amount),
       auditors: [],
       exchanges: [
-        { master_pub: exchange.masterPublicKey, url: exchange.baseUrl },
+        { master_pub: exchangeDetails.masterPublicKey, url: exchange.baseUrl },
       ],
       extra: {},
       fulfillment_url: "",
@@ -3114,10 +3152,16 @@ export class Wallet {
       wire: req.senderWire,
     };
 
-    await this.q()
-      .put(Stores.coinsReturns, coinsReturnRecord)
-      .putAll(Stores.coins, payCoinInfo.updatedCoins)
-      .finish();
+    await runWithWriteTransaction(
+      this.db,
+      [Stores.coinsReturns, Stores.coins],
+      async tx => {
+        await tx.put(Stores.coinsReturns, coinsReturnRecord);
+        for (let c of payCoinInfo.updatedCoins) {
+          await tx.put(Stores.coins, c);
+        }
+      },
+    );
     this.badge.showNotification();
     this.notifier.notify();
 
@@ -3167,7 +3211,8 @@ export class Wallet {
       // FIXME: verify signature
 
       // For every successful deposit, we replace the old record with an 
updated one
-      const currentCrr = await this.q().get(
+      const currentCrr = await oneShotGet(
+        this.db,
         Stores.coinsReturns,
         coinsReturnRecord.contractTermsHash,
       );
@@ -3180,7 +3225,7 @@ export class Wallet {
           nc.depositedSig = respJson.sig;
         }
       }
-      await this.q().put(Stores.coinsReturns, currentCrr);
+      await oneShotPut(this.db, Stores.coinsReturns, currentCrr);
       this.notifier.notify();
     }
   }
@@ -3220,9 +3265,7 @@ export class Wallet {
     const hc = refundResponse.h_contract_terms;
 
     // Add the refund permissions to the purchase within a DB transaction
-    await this.q()
-      .mutate(Stores.purchases, hc, f)
-      .finish();
+    await oneShotMutate(this.db, Stores.purchases, hc, f);
     this.notifier.notify();
 
     await this.submitRefunds(hc);
@@ -3257,7 +3300,11 @@ export class Wallet {
   }
 
   private async submitRefunds(contractTermsHash: string): Promise<void> {
-    const purchase = await this.q().get(Stores.purchases, contractTermsHash);
+    const purchase = await oneShotGet(
+      this.db,
+      Stores.purchases,
+      contractTermsHash,
+    );
     if (!purchase) {
       console.error(
         "not submitting refunds, contract terms not found:",
@@ -3320,10 +3367,18 @@ export class Wallet {
         return c;
       };
 
-      await this.q()
-        .mutate(Stores.purchases, contractTermsHash, transformPurchase)
-        .mutate(Stores.coins, perm.coin_pub, transformCoin)
-        .finish();
+      await runWithWriteTransaction(
+        this.db,
+        [Stores.purchases, Stores.coins],
+        async tx => {
+          await tx.mutate(
+            Stores.purchases,
+            contractTermsHash,
+            transformPurchase,
+          );
+          await tx.mutate(Stores.coins, perm.coin_pub, transformCoin);
+        },
+      );
       this.refresh(perm.coin_pub);
     }
 
@@ -3334,7 +3389,7 @@ export class Wallet {
   async getPurchase(
     contractTermsHash: string,
   ): Promise<PurchaseRecord | undefined> {
-    return this.q().get(Stores.purchases, contractTermsHash);
+    return oneShotGet(this.db, Stores.purchases, contractTermsHash);
   }
 
   async getFullRefundFees(
@@ -3343,7 +3398,8 @@ export class Wallet {
     if (refundPermissions.length === 0) {
       throw Error("no refunds given");
     }
-    const coin0 = await this.q().get(
+    const coin0 = await oneShotGet(
+      this.db,
       Stores.coins,
       refundPermissions[0].coin_pub,
     );
@@ -3354,18 +3410,18 @@ export class Wallet {
       Amounts.parseOrThrow(refundPermissions[0].refund_amount).currency,
     );
 
-    const denoms = await this.q()
-      .iterIndex(
-        Stores.denominations.exchangeBaseUrlIndex,
-        coin0.exchangeBaseUrl,
-      )
-      .toArray();
+    const denoms = await oneShotIterIndex(
+      this.db,
+      Stores.denominations.exchangeBaseUrlIndex,
+      coin0.exchangeBaseUrl,
+    ).toArray();
+
     for (const rp of refundPermissions) {
-      const coin = await this.q().get(Stores.coins, rp.coin_pub);
+      const coin = await oneShotGet(this.db, Stores.coins, rp.coin_pub);
       if (!coin) {
         throw Error("coin not found");
       }
-      const denom = await this.q().get(Stores.denominations, [
+      const denom = await oneShotGet(this.db, Stores.denominations, [
         coin0.exchangeBaseUrl,
         coin.denomPub,
       ]);
@@ -3407,19 +3463,16 @@ export class Wallet {
     tipId: string,
     merchantOrigin: string,
   ): Promise<void> {
-    let tipRecord = await this.q().get(Stores.tips, [tipId, merchantOrigin]);
+    let tipRecord = await oneShotGet(this.db, Stores.tips, [
+      tipId,
+      merchantOrigin,
+    ]);
     if (!tipRecord) {
       throw Error("tip not in database");
     }
 
     tipRecord.accepted = true;
-
-    // Create one transactional query, within this transaction
-    // both the tip will be marked as accepted and coins
-    // already withdrawn will be untainted.
-    await this.q()
-      .put(Stores.tips, tipRecord)
-      .finish();
+    await oneShotPut(this.db, Stores.tips, tipRecord);
 
     if (tipRecord.pickedUp) {
       console.log("tip already picked up");
@@ -3437,7 +3490,7 @@ export class Wallet {
       );
       const coinPubs: string[] = planchets.map(x => x.coinPub);
 
-      await this.q().mutate(Stores.tips, [tipId, merchantOrigin], r => {
+      await oneShotMutate(this.db, Stores.tips, [tipId, merchantOrigin], r => {
         if (!r.planchets) {
           r.planchets = planchets;
           r.coinPubs = coinPubs;
@@ -3448,7 +3501,7 @@ export class Wallet {
       this.notifier.notify();
     }
 
-    tipRecord = await this.q().get(Stores.tips, [tipId, merchantOrigin]);
+    tipRecord = await oneShotGet(this.db, Stores.tips, [tipId, 
merchantOrigin]);
     if (!tipRecord) {
       throw Error("tip not in database");
     }
@@ -3497,15 +3550,14 @@ export class Wallet {
         reservePub: response.reserve_pub,
         withdrawSig: response.reserve_sigs[i].reserve_sig,
       };
-      await this.q().put(Stores.precoins, preCoin);
+      await oneShotPut(this.db, Stores.precoins, preCoin);
       await this.processPreCoin(preCoin.coinPub);
     }
 
     tipRecord.pickedUp = true;
 
-    await this.q()
-      .put(Stores.tips, tipRecord)
-      .finish();
+    await oneShotPut(this.db, Stores.tips, tipRecord);
+
     this.notifier.notify();
     this.badge.showNotification();
     return;
@@ -3529,10 +3581,11 @@ export class Wallet {
 
     let amount = Amounts.parseOrThrow(tipPickupStatus.amount);
 
-    let tipRecord = await this.q().get(Stores.tips, [
+    let tipRecord = await oneShotGet(this.db, Stores.tips, [
       res.tipId,
       res.merchantOrigin,
     ]);
+
     if (!tipRecord) {
       const withdrawDetails = await this.getWithdrawDetailsForAmount(
         tipPickupStatus.exchange_url,
@@ -3558,7 +3611,7 @@ export class Wallet {
           withdrawDetails.withdrawFee,
         ).amount,
       };
-      await this.q().put(Stores.tips, tipRecord);
+      await oneShotPut(this.db, Stores.tips, tipRecord);
     }
 
     const tipStatus: TipStatus = {
@@ -3578,7 +3631,11 @@ export class Wallet {
   }
 
   async abortFailedPayment(contractTermsHash: string): Promise<void> {
-    const purchase = await this.q().get(Stores.purchases, contractTermsHash);
+    const purchase = await oneShotGet(
+      this.db,
+      Stores.purchases,
+      contractTermsHash,
+    );
     if (!purchase) {
       throw Error("Purchase not found, unable to abort with refund");
     }
@@ -3595,7 +3652,7 @@ export class Wallet {
     // From now on, we can't retry payment anymore,
     // so mark this in the DB in case the /pay abort
     // does not complete on the first try.
-    await this.q().put(Stores.purchases, purchase);
+    await oneShotPut(this.db, Stores.purchases, purchase);
 
     let resp;
 
@@ -3616,15 +3673,14 @@ export class Wallet {
     const refundResponse = MerchantRefundResponse.checked(resp.responseJson);
     await this.acceptRefundResponse(refundResponse);
 
-    const markAbortDone = (p: PurchaseRecord) => {
+    await runWithWriteTransaction(this.db, [Stores.purchases], async tx => {
+      const p = await tx.get(Stores.purchases, purchase.contractTermsHash);
+      if (!p) {
+        return;
+      }
       p.abortDone = true;
-      return p;
-    };
-    await this.q().mutate(
-      Stores.purchases,
-      purchase.contractTermsHash,
-      markAbortDone,
-    );
+      await tx.put(Stores.purchases, p);
+    });
   }
 
   /**
@@ -3684,16 +3740,16 @@ export class Wallet {
   }
 
   async getPurchaseDetails(hc: string): Promise<PurchaseDetails> {
-    const purchase = await this.q().get(Stores.purchases, hc);
+    const purchase = await oneShotGet(this.db, Stores.purchases, hc);
     if (!purchase) {
       throw Error("unknown purchase");
     }
     const refundsDoneAmounts = Object.values(purchase.refundsDone).map(x =>
       Amounts.parseOrThrow(x.refund_amount),
     );
-    const refundsPendingAmounts = Object.values(purchase.refundsPending).map(
-      x => Amounts.parseOrThrow(x.refund_amount),
-    );
+    const refundsPendingAmounts = Object.values(
+      purchase.refundsPending,
+    ).map(x => Amounts.parseOrThrow(x.refund_amount));
     const totalRefundAmount = Amounts.sum([
       ...refundsDoneAmounts,
       ...refundsPendingAmounts,
diff --git a/src/walletTypes.ts b/src/walletTypes.ts
index e632cd38..b227ca81 100644
--- a/src/walletTypes.ts
+++ b/src/walletTypes.ts
@@ -34,8 +34,7 @@ import {
   CoinRecord,
   DenominationRecord,
   ExchangeRecord,
-  ExchangeWireFeesRecord,
-  TipRecord,
+  ExchangeWireInfo,
 } from "./dbTypes";
 import { CoinPaySig, ContractTerms, PayReq } from "./talerTypes";
 
@@ -98,7 +97,7 @@ export interface ReserveCreationInfo {
   /**
    * Wire fees from the exchange.
    */
-  wireFees: ExchangeWireFeesRecord;
+  wireFees: ExchangeWireInfo;
 
   /**
    * Does the wallet know about an auditor for
@@ -475,7 +474,6 @@ export interface PreparePayResultError {
   error: string;
 }
 
-
 export interface PreparePayResultPaid {
   status: "paid";
   contractTerms: ContractTerms;
@@ -517,18 +515,40 @@ export interface WalletDiagnostics {
 }
 
 export interface PendingWithdrawOperation {
-  type: "withdraw"
+  type: "withdraw";
 }
 
 export interface PendingRefreshOperation {
-  type: "refresh"
+  type: "refresh";
 }
 
 export interface PendingPayOperation {
-  type: "pay"
+  type: "pay";
+}
+
+export interface OperationError {
+  type: string;
+  message: string;
+  details: any;
+}
+
+export interface PendingExchangeUpdateOperation {
+  type: "exchange-update";
+  stage: string;
+  exchangeBaseUrl: string;
+  lastError?: OperationError;
+}
+
+export interface PendingBugOperation {
+  type: "bug";
+  message: string;
+  details: any;
 }
 
-export type PendingOperationInfo = PendingWithdrawOperation
+export type PendingOperationInfo =
+  | PendingWithdrawOperation
+  | PendingBugOperation
+  | PendingExchangeUpdateOperation;
 
 export interface PendingOperationsResponse {
   pendingOperations: PendingOperationInfo[];
@@ -541,4 +561,24 @@ export interface HistoryQuery {
    * Level 1: All events.
    */
   level: number;
-}
\ No newline at end of file
+}
+
+export interface Timestamp {
+  /**
+   * Timestamp in milliseconds.
+   */
+  t_ms: number;
+}
+
+export interface Duration {
+  /**
+   * Duration in milliseconds.
+   */
+  d_ms: number;
+}
+
+export function getTimestampNow(): Timestamp {
+  return {
+    t_ms: new Date().getTime(),
+  };
+}
diff --git a/src/webex/messages.ts b/src/webex/messages.ts
index 78a1a1fd..3f6e5cc4 100644
--- a/src/webex/messages.ts
+++ b/src/webex/messages.ts
@@ -73,14 +73,6 @@ export interface MessageMap {
     request: { baseUrl: string };
     response: dbTypes.ExchangeRecord;
   };
-  "currency-info": {
-    request: { name: string };
-    response: dbTypes.CurrencyRecord;
-  };
-  "hash-contract": {
-    request: { contract: object };
-    response: string;
-  };
   "reserve-creation-info": {
     request: { baseUrl: string; amount: AmountJson };
     response: walletTypes.ReserveCreationInfo;
@@ -145,14 +137,6 @@ export interface MessageMap {
     request: {};
     response: void;
   };
-  "log-and-display-error": {
-    request: any;
-    response: void;
-  };
-  "get-report": {
-    request: { reportUid: string };
-    response: void;
-  };
   "get-purchase-details": {
     request: { contractTermsHash: string };
     response: walletTypes.PurchaseDetails;
diff --git a/src/webex/pages/error.html b/src/webex/pages/error.html
deleted file mode 100644
index 51a8fd73..00000000
--- a/src/webex/pages/error.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!DOCTYPE html>
-<html>
-
-<head>
-  <meta charset="UTF-8">
-  <title>Taler Wallet: Error Occured</title>
-
-  <link rel="stylesheet" type="text/css" href="../style/wallet.css">
-
-  <link rel="icon" href="/img/icon.png">
-
-  <script src="/dist/page-common-bundle.js"></script>
-  <script src="/dist/error-bundle.js"></script>
-
-  <body>
-    <div id="container"></div>
-  </body>
-</html>
diff --git a/src/webex/pages/error.tsx b/src/webex/pages/error.tsx
deleted file mode 100644
index dee8ce44..00000000
--- a/src/webex/pages/error.tsx
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- This file is part of TALER
- (C) 2015-2016 GNUnet e.V.
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-
-/**
- * Page shown to the user to confirm creation
- * of a reserve, usually requested by the bank.
- *
- * @author Florian Dold
- */
-
-
-import * as React from "react";
-import * as ReactDOM from "react-dom";
-import URI = require("urijs");
-
-import * as wxApi from "../wxApi";
-
-import { Collapsible } from "../renderHtml";
-
-interface ErrorProps {
-  report: any;
-}
-
-class ErrorView extends React.Component<ErrorProps, { }> {
-  render(): JSX.Element {
-    const report = this.props.report;
-    if (!report) {
-      return (
-        <div id="main">
-          <h1>Error Report Not Found</h1>
-          <p>This page is supposed to display an error reported by the GNU 
Taler wallet,
-              but the corresponding error report can't be found.</p>
-          <p>Maybe the error occured before the browser was restarted or the 
wallet was reloaded.</p>
-        </div>
-      );
-    }
-    try {
-      switch (report.name) {
-        case "pay-post-failed": {
-          const summary = report.contractTerms.summary || 
report.contractTerms.order_id;
-          return (
-            <div id="main">
-              <h1>Failed to send payment</h1>
-              <p>
-                Failed to send payment for <strong>{summary}</strong>{" "}
-                to merchant 
<strong>{report.contractTerms.merchant.name}</strong>.
-              </p>
-              <p>
-                You can <a 
href={report.contractTerms.fulfillment_url}>retry</a> the payment.{" "}
-                If this problem persists, please contact the mechant with the 
error details below.
-              </p>
-              <Collapsible initiallyCollapsed={true} title="Error Details">
-                <pre>
-                  {JSON.stringify(report, null, " ")}
-                </pre>
-              </Collapsible>
-            </div>
-          );
-        }
-        default:
-          return (
-            <div id="main">
-              <h1>Unknown Error</h1>
-              The GNU Taler wallet reported an unknown error.  Here are the 
details:
-              <pre>
-                {JSON.stringify(report, null, " ")}
-              </pre>
-            </div>
-          );
-      }
-    } catch (e) {
-        return (
-          <div id="main">
-            <h1>Error</h1>
-            The GNU Taler wallet reported an error.  Here are the details:
-            <pre>
-              {JSON.stringify(report, null, " ")}
-            </pre>
-            A detailed error report could not be generated:
-            <pre>
-              {e.toString()}
-            </pre>
-          </div>
-        );
-    }
-  }
-}
-
-async function main() {
-  const url = new URI(document.location.href);
-  const query: any = URI.parseQuery(url.query());
-
-  const container = document.getElementById("container");
-  if (!container) {
-    console.error("fatal: can't mount component, countainer missing");
-    return;
-  }
-
-  // report that we'll render, either looked up from the
-  // logging module or synthesized here for fixed/fatal errors
-  let report;
-
-  const reportUid: string = query.reportUid;
-  if (!reportUid) {
-    report = {
-      name: "missing-error",
-    };
-  } else {
-    report = await wxApi.getReport(reportUid);
-  }
-
-  ReactDOM.render(<ErrorView report={report} />, container);
-}
-
-document.addEventListener("DOMContentLoaded", () => main());
diff --git a/src/webex/pages/logs.html b/src/webex/pages/logs.html
deleted file mode 100644
index 9545269e..00000000
--- a/src/webex/pages/logs.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE html>
-<html>
-
-<head>
-  <meta charset="UTF-8">
-  <title>Taler Wallet: Logs</title>
-
-  <link rel="stylesheet" type="text/css" href="../style/wallet.css">
-
-  <link rel="icon" href="/img/icon.png">
-
-  <script src="/dist/page-common-bundle.js"></script>
-  <script src="/dist/logs-bundle.js"></script>
-
-  <style>
-    .tree-item {
-            margin: 2em;
-            border-radius: 5px;
-            border: 1px solid gray;
-            padding: 1em;
-    }
-  </style>
-
-  <body>
-    <div id="container"></div>
-  </body>
-</html>
diff --git a/src/webex/pages/logs.tsx b/src/webex/pages/logs.tsx
deleted file mode 100644
index c4fe670a..00000000
--- a/src/webex/pages/logs.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- This file is part of TALER
- (C) 2016 Inria
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * Show wallet logs.
- *
- * @author Florian Dold
- */
-
-import {
-  LogEntry,
-  getLogs,
-} from "../../logging";
-
-import * as React from "react";
-import * as ReactDOM from "react-dom";
-
-interface LogViewProps {
-  log: LogEntry;
-}
-
-class LogView extends React.Component<LogViewProps, {}> {
-  render(): JSX.Element {
-    const e = this.props.log;
-    return (
-      <div className="tree-item">
-        <ul>
-          <li>level: {e.level}</li>
-          <li>msg: {e.msg}</li>
-          <li>id: {e.id || "unknown"}</li>
-          <li>file: {e.source || "(unknown)"}</li>
-          <li>line: {e.line || "(unknown)"}</li>
-          <li>col: {e.col || "(unknown)"}</li>
-          {(e.detail ? <li> detail: <pre>{e.detail}</pre></li> : [])}
-        </ul>
-      </div>
-    );
-  }
-}
-
-interface LogsState {
-  logs: LogEntry[]|undefined;
-}
-
-class Logs extends React.Component<{}, LogsState> {
-  constructor(props: {}) {
-    super({});
-    this.update();
-    this.state = {} as any;
-  }
-
-  async update() {
-    const logs = await getLogs();
-    this.setState({logs});
-  }
-
-  render(): JSX.Element {
-    const logs = this.state.logs;
-    if (!logs) {
-      return <span>...</span>;
-    }
-    return (
-      <div className="tree-item">
-        Logs:
-        {logs.map((e) => <LogView log={e} />)}
-      </div>
-    );
-  }
-}
-
-document.addEventListener("DOMContentLoaded", () => {
-  ReactDOM.render(<Logs />, document.getElementById("container")!);
-});
diff --git a/src/webex/renderHtml.tsx b/src/webex/renderHtml.tsx
index e2f82105..f2cccfba 100644
--- a/src/webex/renderHtml.tsx
+++ b/src/webex/renderHtml.tsx
@@ -137,12 +137,12 @@ function AuditorDetailsView(props: {
       </p>
     );
   }
-  if (rci.exchangeInfo.auditors.length === 0) {
+  if ((rci.exchangeInfo.details?.auditors ?? []).length === 0) {
     return <p>The exchange is not audited by any auditors.</p>;
   }
   return (
     <div>
-      {rci.exchangeInfo.auditors.map(a => (
+      {(rci.exchangeInfo.details?.auditors ?? []).map(a => (
         <div>
           <h3>Auditor {a.auditor_url}</h3>
           <p>
@@ -231,7 +231,7 @@ function FeeDetailsView(props: {
     <div>
       <h3>Overview</h3>
       <p>
-        Public key: <ExpanderText text={rci.exchangeInfo.masterPublicKey} />
+        Public key: <ExpanderText 
text={rci.exchangeInfo.details?.masterPublicKey ?? "??"} />
       </p>
       <p>
         {i18n.str`Withdrawal fees:`} {withdrawFee}
diff --git a/src/webex/wxApi.ts b/src/webex/wxApi.ts
index 39c31ca5..a5067213 100644
--- a/src/webex/wxApi.ts
+++ b/src/webex/wxApi.ts
@@ -123,13 +123,6 @@ export function getCurrencies(): Promise<CurrencyRecord[]> 
{
 }
 
 
-/**
- * Get information about a specific currency.
- */
-export function getCurrency(name: string): Promise<CurrencyRecord|null> {
-  return callBackend("currency-info", {name});
-}
-
 
 /**
  * Get information about a specific exchange.
@@ -225,12 +218,6 @@ export function submitPay(contractTermsHash: string, 
sessionId: string | undefin
   return callBackend("submit-pay", { contractTermsHash, sessionId });
 }
 
-/**
- * Hash a contract.  Throws if its not a valid contract.
- */
-export function hashContract(contract: object): Promise<string> {
-  return callBackend("hash-contract", { contract });
-}
 
 /**
  * Mark a reserve as confirmed.
@@ -284,25 +271,6 @@ export function returnCoins(args: { amount: AmountJson, 
exchange: string, sender
 }
 
 
-/**
- * Record an error report and display it in a tabl.
- *
- * If sameTab is set, the error report will be opened in the current tab,
- * otherwise in a new tab.
- */
-export function logAndDisplayError(args: any): Promise<void> {
-  return callBackend("log-and-display-error", args);
-}
-
-/**
- * Get an error report from the logging database for the
- * given report UID.
- */
-export function getReport(reportUid: string): Promise<any> {
-  return callBackend("get-report", { reportUid });
-}
-
-
 /**
  * Look up a purchase in the wallet database from
  * the contract terms hash.
diff --git a/src/webex/wxBackend.ts b/src/webex/wxBackend.ts
index 16cd2a78..f4decbc6 100644
--- a/src/webex/wxBackend.ts
+++ b/src/webex/wxBackend.ts
@@ -24,7 +24,6 @@
  * Imports.
  */
 import { BrowserHttpLib } from "../http";
-import * as logging from "../logging";
 import { AmountJson } from "../amounts";
 import {
   ConfirmReserveRequest,
@@ -138,22 +137,6 @@ async function handleMessage(
       }
       return needsWallet().updateExchangeFromUrl(detail.baseUrl);
     }
-    case "currency-info": {
-      if (!detail.name) {
-        return Promise.resolve({ error: "name missing" });
-      }
-      return needsWallet().getCurrencyRecord(detail.name);
-    }
-    case "hash-contract": {
-      if (!detail.contract) {
-        return Promise.resolve({ error: "contract missing" });
-      }
-      return needsWallet()
-        .hashContract(detail.contract)
-        .then(hash => {
-          return hash;
-        });
-    }
     case "reserve-creation-info": {
       if (!detail.baseUrl || typeof detail.baseUrl !== "string") {
         return Promise.resolve({ error: "bad url" });
@@ -243,20 +226,6 @@ async function handleMessage(
       };
       return resp;
     }
-    case "log-and-display-error":
-      logging.storeReport(detail).then(reportUid => {
-        const url = chrome.extension.getURL(
-          `/src/webex/pages/error.html?reportUid=${reportUid}`,
-        );
-        if (detail.sameTab && sender && sender.tab && sender.tab.id) {
-          chrome.tabs.update(detail.tabId, { url });
-        } else {
-          chrome.tabs.create({ url });
-        }
-      });
-      return;
-    case "get-report":
-      return logging.getReport(detail.reportUid);
     case "get-purchase-details": {
       const contractTermsHash = detail.contractTermsHash;
       if (!contractTermsHash) {
@@ -574,17 +543,6 @@ export async function wxMain() {
     chrome.runtime.reload();
   });
 
-  window.onerror = (m, source, lineno, colno, error) => {
-    logging.record(
-      "error",
-      "".concat(m as any, error as any),
-      undefined,
-      source || "(unknown)",
-      lineno || 0,
-      colno || 0,
-    );
-  };
-
   chrome.tabs.query({}, tabs => {
     console.log("got tabs", tabs);
     for (const tab of tabs) {
diff --git a/tsconfig.json b/tsconfig.json
index e190e14b..25087b60 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -53,7 +53,6 @@
     "src/index.ts",
     "src/libtoolVersion-test.ts",
     "src/libtoolVersion.ts",
-    "src/logging.ts",
     "src/promiseUtils.ts",
     "src/query.ts",
     "src/talerTypes.ts",
@@ -72,8 +71,6 @@
     "src/webex/pages/add-auditor.tsx",
     "src/webex/pages/auditors.tsx",
     "src/webex/pages/benchmark.tsx",
-    "src/webex/pages/error.tsx",
-    "src/webex/pages/logs.tsx",
     "src/webex/pages/pay.tsx",
     "src/webex/pages/payback.tsx",
     "src/webex/pages/popup.tsx",
diff --git a/webpack.config.js b/webpack.config.js
index 09645588..ab893eac 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -80,8 +80,6 @@ module.exports = function (env) {
       "pay": "./src/webex/pages/pay.tsx",
       "withdraw": "./src/webex/pages/withdraw.tsx",
       "welcome": "./src/webex/pages/welcome.tsx",
-      "error": "./src/webex/pages/error.tsx",
-      "logs": "./src/webex/pages/logs.tsx",
       "payback": "./src/webex/pages/payback.tsx",
       "popup": "./src/webex/pages/popup.tsx",
       "reset-required": "./src/webex/pages/reset-required.tsx",
diff --git a/yarn.lock b/yarn.lock
index 2e7ec95c..4c9012d4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -34,18 +34,18 @@
     "@babel/highlight" "^7.0.0"
 
 "@babel/core@^7.6.0":
-  version "7.6.2"
-  resolved 
"https://registry.yarnpkg.com/@babel/core/-/core-7.6.2.tgz#069a776e8d5e9eefff76236bc8845566bd31dd91";
-  integrity 
sha512-l8zto/fuoZIbncm+01p8zPSDZu/VuuJhAfA7d/AbzM09WR7iVhavvfNDYCNpo1VvLk6E6xgAoP9P+/EMJHuRkQ==
+  version "7.7.2"
+  resolved 
"https://registry.yarnpkg.com/@babel/core/-/core-7.7.2.tgz#ea5b99693bcfc058116f42fa1dd54da412b29d91";
+  integrity 
sha512-eeD7VEZKfhK1KUXGiyPFettgF3m513f8FoBSWiQ1xTvl1RAopLs42Wp9+Ze911I6H0N9lNqJMDgoZT7gHsipeQ==
   dependencies:
     "@babel/code-frame" "^7.5.5"
-    "@babel/generator" "^7.6.2"
-    "@babel/helpers" "^7.6.2"
-    "@babel/parser" "^7.6.2"
-    "@babel/template" "^7.6.0"
-    "@babel/traverse" "^7.6.2"
-    "@babel/types" "^7.6.0"
-    convert-source-map "^1.1.0"
+    "@babel/generator" "^7.7.2"
+    "@babel/helpers" "^7.7.0"
+    "@babel/parser" "^7.7.2"
+    "@babel/template" "^7.7.0"
+    "@babel/traverse" "^7.7.2"
+    "@babel/types" "^7.7.2"
+    convert-source-map "^1.7.0"
     debug "^4.1.0"
     json5 "^2.1.0"
     lodash "^4.17.13"
@@ -53,67 +53,64 @@
     semver "^5.4.1"
     source-map "^0.5.0"
 
-"@babel/generator@^7.0.0", "@babel/generator@^7.4.0", 
"@babel/generator@^7.5.5":
-  version "7.5.5"
-  resolved 
"https://registry.yarnpkg.com/@babel/generator/-/generator-7.5.5.tgz#873a7f936a3c89491b43536d12245b626664e3cf";
-  integrity 
sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==
+"@babel/generator@^7.0.0", "@babel/generator@^7.4.0", 
"@babel/generator@^7.6.0", "@babel/generator@^7.7.2":
+  version "7.7.2"
+  resolved 
"https://registry.yarnpkg.com/@babel/generator/-/generator-7.7.2.tgz#2f4852d04131a5e17ea4f6645488b5da66ebf3af";
+  integrity 
sha512-WthSArvAjYLz4TcbKOi88me+KmDJdKSlfwwN8CnUYn9jBkzhq0ZEPuBfkAWIvjJ3AdEV1Cf/+eSQTnp3IDJKlQ==
   dependencies:
-    "@babel/types" "^7.5.5"
+    "@babel/types" "^7.7.2"
     jsesc "^2.5.1"
     lodash "^4.17.13"
     source-map "^0.5.0"
-    trim-right "^1.0.1"
 
-"@babel/generator@^7.6.0", "@babel/generator@^7.6.2":
-  version "7.6.2"
-  resolved 
"https://registry.yarnpkg.com/@babel/generator/-/generator-7.6.2.tgz#dac8a3c2df118334c2a29ff3446da1636a8f8c03";
-  integrity 
sha512-j8iHaIW4gGPnViaIHI7e9t/Hl8qLjERI6DcV9kEpAIDJsAOrcnXqRS7t+QbhL76pwbtqP+QCQLL0z1CyVmtjjQ==
+"@babel/helper-annotate-as-pure@^7.7.0":
+  version "7.7.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.7.0.tgz#efc54032d43891fe267679e63f6860aa7dbf4a5e";
+  integrity 
sha512-k50CQxMlYTYo+GGyUGFwpxKVtxVJi9yh61sXZji3zYHccK9RYliZGSTOgci85T+r+0VFN2nWbGM04PIqwfrpMg==
   dependencies:
-    "@babel/types" "^7.6.0"
-    jsesc "^2.5.1"
-    lodash "^4.17.13"
-    source-map "^0.5.0"
+    "@babel/types" "^7.7.0"
 
-"@babel/helper-annotate-as-pure@^7.0.0":
-  version "7.0.0"
-  resolved 
"https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz#323d39dd0b50e10c7c06ca7d7638e6864d8c5c32";
-  integrity 
sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==
+"@babel/helper-create-regexp-features-plugin@^7.7.0":
+  version "7.7.2"
+  resolved 
"https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.7.2.tgz#6f20443778c8fce2af2ff4206284afc0ced65db6";
+  integrity 
sha512-pAil/ZixjTlrzNpjx+l/C/wJk002Wo7XbbZ8oujH/AoJ3Juv0iN/UTcPUHXKMFLqsfS0Hy6Aow8M31brUYBlQQ==
   dependencies:
-    "@babel/types" "^7.0.0"
+    "@babel/helper-regex" "^7.4.4"
+    regexpu-core "^4.6.0"
 
-"@babel/helper-function-name@^7.1.0":
-  version "7.1.0"
-  resolved 
"https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53";
-  integrity 
sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==
+"@babel/helper-function-name@^7.7.0":
+  version "7.7.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.7.0.tgz#44a5ad151cfff8ed2599c91682dda2ec2c8430a3";
+  integrity 
sha512-tDsJgMUAP00Ugv8O2aGEua5I2apkaQO7lBGUq1ocwN3G23JE5Dcq0uh3GvFTChPa4b40AWiAsLvCZOA2rdnQ7Q==
   dependencies:
-    "@babel/helper-get-function-arity" "^7.0.0"
-    "@babel/template" "^7.1.0"
-    "@babel/types" "^7.0.0"
+    "@babel/helper-get-function-arity" "^7.7.0"
+    "@babel/template" "^7.7.0"
+    "@babel/types" "^7.7.0"
 
-"@babel/helper-get-function-arity@^7.0.0":
-  version "7.0.0"
-  resolved 
"https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3";
-  integrity 
sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==
+"@babel/helper-get-function-arity@^7.7.0":
+  version "7.7.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.0.tgz#c604886bc97287a1d1398092bc666bc3d7d7aa2d";
+  integrity 
sha512-tLdojOTz4vWcEnHWHCuPN5P85JLZWbm5Fx5ZsMEMPhF3Uoe3O7awrbM2nQ04bDOUToH/2tH/ezKEOR8zEYzqyw==
   dependencies:
-    "@babel/types" "^7.0.0"
+    "@babel/types" "^7.7.0"
 
-"@babel/helper-module-imports@^7.0.0":
-  version "7.0.0"
-  resolved 
"https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz#96081b7111e486da4d2cd971ad1a4fe216cc2e3d";
-  integrity 
sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==
+"@babel/helper-module-imports@^7.7.0":
+  version "7.7.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.7.0.tgz#99c095889466e5f7b6d66d98dffc58baaf42654d";
+  integrity 
sha512-Dv3hLKIC1jyfTkClvyEkYP2OlkzNvWs5+Q8WgPbxM5LMeorons7iPP91JM+DU7tRbhqA1ZeooPaMFvQrn23RHw==
   dependencies:
-    "@babel/types" "^7.0.0"
+    "@babel/types" "^7.7.0"
 
-"@babel/helper-module-transforms@^7.4.4":
-  version "7.5.5"
-  resolved 
"https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.5.5.tgz#f84ff8a09038dcbca1fd4355661a500937165b4a";
-  integrity 
sha512-jBeCvETKuJqeiaCdyaheF40aXnnU1+wkSiUs/IQg3tB85up1LyL8x77ClY8qJpuRJUcXQo+ZtdNESmZl4j56Pw==
-  dependencies:
-    "@babel/helper-module-imports" "^7.0.0"
-    "@babel/helper-simple-access" "^7.1.0"
-    "@babel/helper-split-export-declaration" "^7.4.4"
-    "@babel/template" "^7.4.4"
-    "@babel/types" "^7.5.5"
+"@babel/helper-module-transforms@^7.7.0":
+  version "7.7.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.7.0.tgz#154a69f0c5b8fd4d39e49750ff7ac4faa3f36786";
+  integrity 
sha512-rXEefBuheUYQyX4WjV19tuknrJFwyKw0HgzRwbkyTbB+Dshlq7eqkWbyjzToLrMZk/5wKVKdWFluiAsVkHXvuQ==
+  dependencies:
+    "@babel/helper-module-imports" "^7.7.0"
+    "@babel/helper-simple-access" "^7.7.0"
+    "@babel/helper-split-export-declaration" "^7.7.0"
+    "@babel/template" "^7.7.0"
+    "@babel/types" "^7.7.0"
     lodash "^4.17.13"
 
 "@babel/helper-plugin-utils@^7.0.0":
@@ -128,50 +125,50 @@
   dependencies:
     lodash "^4.17.13"
 
-"@babel/helper-remap-async-to-generator@^7.1.0":
-  version "7.1.0"
-  resolved 
"https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz#361d80821b6f38da75bd3f0785ece20a88c5fe7f";
-  integrity 
sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg==
+"@babel/helper-remap-async-to-generator@^7.7.0":
+  version "7.7.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.7.0.tgz#4d69ec653e8bff5bce62f5d33fc1508f223c75a7";
+  integrity 
sha512-pHx7RN8X0UNHPB/fnuDnRXVZ316ZigkO8y8D835JlZ2SSdFKb6yH9MIYRU4fy/KPe5sPHDFOPvf8QLdbAGGiyw==
   dependencies:
-    "@babel/helper-annotate-as-pure" "^7.0.0"
-    "@babel/helper-wrap-function" "^7.1.0"
-    "@babel/template" "^7.1.0"
-    "@babel/traverse" "^7.1.0"
-    "@babel/types" "^7.0.0"
+    "@babel/helper-annotate-as-pure" "^7.7.0"
+    "@babel/helper-wrap-function" "^7.7.0"
+    "@babel/template" "^7.7.0"
+    "@babel/traverse" "^7.7.0"
+    "@babel/types" "^7.7.0"
 
-"@babel/helper-simple-access@^7.1.0":
-  version "7.1.0"
-  resolved 
"https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz#65eeb954c8c245beaa4e859da6188f39d71e585c";
-  integrity 
sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==
+"@babel/helper-simple-access@^7.7.0":
+  version "7.7.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.7.0.tgz#97a8b6c52105d76031b86237dc1852b44837243d";
+  integrity 
sha512-AJ7IZD7Eem3zZRuj5JtzFAptBw7pMlS3y8Qv09vaBWoFsle0d1kAn5Wq6Q9MyBXITPOKnxwkZKoAm4bopmv26g==
   dependencies:
-    "@babel/template" "^7.1.0"
-    "@babel/types" "^7.0.0"
+    "@babel/template" "^7.7.0"
+    "@babel/types" "^7.7.0"
 
-"@babel/helper-split-export-declaration@^7.4.4":
-  version "7.4.4"
-  resolved 
"https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677";
-  integrity 
sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==
+"@babel/helper-split-export-declaration@^7.7.0":
+  version "7.7.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.0.tgz#1365e74ea6c614deeb56ebffabd71006a0eb2300";
+  integrity 
sha512-HgYSI8rH08neWlAH3CcdkFg9qX9YsZysZI5GD8LjhQib/mM0jGOZOVkoUiiV2Hu978fRtjtsGsW6w0pKHUWtqA==
   dependencies:
-    "@babel/types" "^7.4.4"
+    "@babel/types" "^7.7.0"
 
-"@babel/helper-wrap-function@^7.1.0":
-  version "7.2.0"
-  resolved 
"https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz#c4e0012445769e2815b55296ead43a958549f6fa";
-  integrity 
sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ==
+"@babel/helper-wrap-function@^7.7.0":
+  version "7.7.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.7.0.tgz#15af3d3e98f8417a60554acbb6c14e75e0b33b74";
+  integrity 
sha512-sd4QjeMgQqzshSjecZjOp8uKfUtnpmCyQhKQrVJBBgeHAB/0FPi33h3AbVlVp07qQtMD4QgYSzaMI7VwncNK/w==
   dependencies:
-    "@babel/helper-function-name" "^7.1.0"
-    "@babel/template" "^7.1.0"
-    "@babel/traverse" "^7.1.0"
-    "@babel/types" "^7.2.0"
+    "@babel/helper-function-name" "^7.7.0"
+    "@babel/template" "^7.7.0"
+    "@babel/traverse" "^7.7.0"
+    "@babel/types" "^7.7.0"
 
-"@babel/helpers@^7.6.2":
-  version "7.6.2"
-  resolved 
"https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.6.2.tgz#681ffe489ea4dcc55f23ce469e58e59c1c045153";
-  integrity 
sha512-3/bAUL8zZxYs1cdX2ilEE0WobqbCmKWr/889lf2SS0PpDcpEIY8pb1CCyz0pEcX3pEb+MCbks1jIokz2xLtGTA==
+"@babel/helpers@^7.7.0":
+  version "7.7.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.7.0.tgz#359bb5ac3b4726f7c1fde0ec75f64b3f4275d60b";
+  integrity 
sha512-VnNwL4YOhbejHb7x/b5F39Zdg5vIQpUUNzJwx0ww1EcVRt41bbGRZWhAURrfY32T5zTT3qwNOQFWpn+P0i0a2g==
   dependencies:
-    "@babel/template" "^7.6.0"
-    "@babel/traverse" "^7.6.2"
-    "@babel/types" "^7.6.0"
+    "@babel/template" "^7.7.0"
+    "@babel/traverse" "^7.7.0"
+    "@babel/types" "^7.7.0"
 
 "@babel/highlight@^7.0.0":
   version "7.5.0"
@@ -182,29 +179,24 @@
     esutils "^2.0.2"
     js-tokens "^4.0.0"
 
-"@babel/parser@^7.0.0", "@babel/parser@^7.4.3", "@babel/parser@^7.4.4", 
"@babel/parser@^7.5.5":
-  version "7.5.5"
-  resolved 
"https://registry.yarnpkg.com/@babel/parser/-/parser-7.5.5.tgz#02f077ac8817d3df4a832ef59de67565e71cca4b";
-  integrity 
sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==
-
-"@babel/parser@^7.6.0", "@babel/parser@^7.6.2":
-  version "7.6.2"
-  resolved 
"https://registry.yarnpkg.com/@babel/parser/-/parser-7.6.2.tgz#205e9c95e16ba3b8b96090677a67c9d6075b70a1";
-  integrity 
sha512-mdFqWrSPCmikBoaBYMuBulzTIKuXVPtEISFbRRVNwMWpCms/hmE2kRq0bblUHaNRKrjRlmVbx1sDHmjmRgD2Xg==
+"@babel/parser@^7.0.0", "@babel/parser@^7.4.3", "@babel/parser@^7.7.0", 
"@babel/parser@^7.7.2":
+  version "7.7.3"
+  resolved 
"https://registry.yarnpkg.com/@babel/parser/-/parser-7.7.3.tgz#5fad457c2529de476a248f75b0f090b3060af043";
+  integrity 
sha512-bqv+iCo9i+uLVbI0ILzKkvMorqxouI+GbV13ivcARXn9NNEabi2IEz912IgNpT/60BNXac5dgcfjb94NjsF33A==
 
 "@babel/plugin-proposal-async-generator-functions@^7.2.0":
-  version "7.2.0"
-  resolved 
"https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz#b289b306669dce4ad20b0252889a15768c9d417e";
-  integrity 
sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ==
+  version "7.7.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.7.0.tgz#83ef2d6044496b4c15d8b4904e2219e6dccc6971";
+  integrity 
sha512-ot/EZVvf3mXtZq0Pd0+tSOfGWMizqmOohXmNZg6LNFjHOV+wOPv7BvVYh8oPR8LhpIP3ye8nNooKL50YRWxpYA==
   dependencies:
     "@babel/helper-plugin-utils" "^7.0.0"
-    "@babel/helper-remap-async-to-generator" "^7.1.0"
+    "@babel/helper-remap-async-to-generator" "^7.7.0"
     "@babel/plugin-syntax-async-generators" "^7.2.0"
 
 "@babel/plugin-proposal-dynamic-import@^7.5.0":
-  version "7.5.0"
-  resolved 
"https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.5.0.tgz#e532202db4838723691b10a67b8ce509e397c506";
-  integrity 
sha512-x/iMjggsKTFHYC6g11PL7Qy58IK8H5zqfm9e6hu4z1iH2IRyAp9u9dL80zA6R76yFovETFLKz2VJIC2iIPBuFw==
+  version "7.7.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.7.0.tgz#dc02a8bad8d653fb59daf085516fa416edd2aa7f";
+  integrity 
sha512-7poL3Xi+QFPC7sGAzEIbXUyYzGJwbc2+gSD0AkiC5k52kH2cqHdqxm5hNFfLW3cRSTcx9bN0Fl7/6zWcLLnKAQ==
   dependencies:
     "@babel/helper-plugin-utils" "^7.0.0"
     "@babel/plugin-syntax-dynamic-import" "^7.2.0"
@@ -239,85 +231,51 @@
     "@babel/helper-plugin-utils" "^7.0.0"
 
 "@babel/plugin-transform-dotall-regex@^7.4.4":
-  version "7.4.4"
-  resolved 
"https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz#361a148bc951444312c69446d76ed1ea8e4450c3";
-  integrity 
sha512-P05YEhRc2h53lZDjRPk/OektxCVevFzZs2Gfjd545Wde3k+yFDbXORgl2e0xpbq8mLcKJ7Idss4fAg0zORN/zg==
+  version "7.7.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.7.0.tgz#c5c9ecacab3a5e0c11db6981610f0c32fd698b3b";
+  integrity 
sha512-3QQlF7hSBnSuM1hQ0pS3pmAbWLax/uGNCbPBND9y+oJ4Y776jsyujG2k0Sn2Aj2a0QwVOiOFL5QVPA7spjvzSA==
   dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.7.0"
     "@babel/helper-plugin-utils" "^7.0.0"
-    "@babel/helper-regex" "^7.4.4"
-    regexpu-core "^4.5.4"
 
 "@babel/plugin-transform-modules-commonjs@^7.5.0":
-  version "7.5.0"
-  resolved 
"https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.5.0.tgz#425127e6045231360858eeaa47a71d75eded7a74";
-  integrity 
sha512-xmHq0B+ytyrWJvQTc5OWAC4ii6Dhr0s22STOoydokG51JjWhyYo5mRPXoi+ZmtHQhZZwuXNN+GG5jy5UZZJxIQ==
+  version "7.7.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.7.0.tgz#3e5ffb4fd8c947feede69cbe24c9554ab4113fe3";
+  integrity 
sha512-KEMyWNNWnjOom8vR/1+d+Ocz/mILZG/eyHHO06OuBQ2aNhxT62fr4y6fGOplRx+CxCSp3IFwesL8WdINfY/3kg==
   dependencies:
-    "@babel/helper-module-transforms" "^7.4.4"
+    "@babel/helper-module-transforms" "^7.7.0"
     "@babel/helper-plugin-utils" "^7.0.0"
-    "@babel/helper-simple-access" "^7.1.0"
+    "@babel/helper-simple-access" "^7.7.0"
     babel-plugin-dynamic-import-node "^2.3.0"
 
-"@babel/template@^7.1.0", "@babel/template@^7.4.0", "@babel/template@^7.4.4":
-  version "7.4.4"
-  resolved 
"https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237";
-  integrity 
sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==
+"@babel/template@^7.4.0", "@babel/template@^7.7.0":
+  version "7.7.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/template/-/template-7.7.0.tgz#4fadc1b8e734d97f56de39c77de76f2562e597d0";
+  integrity 
sha512-OKcwSYOW1mhWbnTBgQY5lvg1Fxg+VyfQGjcBduZFljfc044J5iDlnDSfhQ867O17XHiSCxYHUxHg2b7ryitbUQ==
   dependencies:
     "@babel/code-frame" "^7.0.0"
-    "@babel/parser" "^7.4.4"
-    "@babel/types" "^7.4.4"
+    "@babel/parser" "^7.7.0"
+    "@babel/types" "^7.7.0"
 
-"@babel/template@^7.6.0":
-  version "7.6.0"
-  resolved 
"https://registry.yarnpkg.com/@babel/template/-/template-7.6.0.tgz#7f0159c7f5012230dad64cca42ec9bdb5c9536e6";
-  integrity 
sha512-5AEH2EXD8euCk446b7edmgFdub/qfH1SN6Nii3+fyXP807QRx9Q73A2N5hNwRRslC2H9sNzaFhsPubkS4L8oNQ==
-  dependencies:
-    "@babel/code-frame" "^7.0.0"
-    "@babel/parser" "^7.6.0"
-    "@babel/types" "^7.6.0"
-
-"@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3":
-  version "7.5.5"
-  resolved 
"https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.5.5.tgz#f664f8f368ed32988cd648da9f72d5ca70f165bb";
-  integrity 
sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==
+"@babel/traverse@^7.4.3", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.2":
+  version "7.7.2"
+  resolved 
"https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.7.2.tgz#ef0a65e07a2f3c550967366b3d9b62a2dcbeae09";
+  integrity 
sha512-TM01cXib2+rgIZrGJOLaHV/iZUAxf4A0dt5auY6KNZ+cm6aschuJGqKJM3ROTt3raPUdIDk9siAufIFEleRwtw==
   dependencies:
     "@babel/code-frame" "^7.5.5"
-    "@babel/generator" "^7.5.5"
-    "@babel/helper-function-name" "^7.1.0"
-    "@babel/helper-split-export-declaration" "^7.4.4"
-    "@babel/parser" "^7.5.5"
-    "@babel/types" "^7.5.5"
+    "@babel/generator" "^7.7.2"
+    "@babel/helper-function-name" "^7.7.0"
+    "@babel/helper-split-export-declaration" "^7.7.0"
+    "@babel/parser" "^7.7.2"
+    "@babel/types" "^7.7.2"
     debug "^4.1.0"
     globals "^11.1.0"
     lodash "^4.17.13"
 
-"@babel/traverse@^7.6.2":
-  version "7.6.2"
-  resolved 
"https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.6.2.tgz#b0e2bfd401d339ce0e6c05690206d1e11502ce2c";
-  integrity 
sha512-8fRE76xNwNttVEF2TwxJDGBLWthUkHWSldmfuBzVRmEDWOtu4XdINTgN7TDWzuLg4bbeIMLvfMFD9we5YcWkRQ==
-  dependencies:
-    "@babel/code-frame" "^7.5.5"
-    "@babel/generator" "^7.6.2"
-    "@babel/helper-function-name" "^7.1.0"
-    "@babel/helper-split-export-declaration" "^7.4.4"
-    "@babel/parser" "^7.6.2"
-    "@babel/types" "^7.6.0"
-    debug "^4.1.0"
-    globals "^11.1.0"
-    lodash "^4.17.13"
-
-"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.4.0", 
"@babel/types@^7.4.4", "@babel/types@^7.5.5":
-  version "7.5.5"
-  resolved 
"https://registry.yarnpkg.com/@babel/types/-/types-7.5.5.tgz#97b9f728e182785909aa4ab56264f090a028d18a";
-  integrity 
sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==
-  dependencies:
-    esutils "^2.0.2"
-    lodash "^4.17.13"
-    to-fast-properties "^2.0.0"
-
-"@babel/types@^7.6.0":
-  version "7.6.1"
-  resolved 
"https://registry.yarnpkg.com/@babel/types/-/types-7.6.1.tgz#53abf3308add3ac2a2884d539151c57c4b3ac648";
-  integrity 
sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g==
+"@babel/types@^7.4.0", "@babel/types@^7.7.0", "@babel/types@^7.7.2":
+  version "7.7.2"
+  resolved 
"https://registry.yarnpkg.com/@babel/types/-/types-7.7.2.tgz#550b82e5571dcd174af576e23f0adba7ffc683f7";
+  integrity 
sha512-YTf6PXoh3+eZgRCBzzP25Bugd2ngmpQVrk7kXX0i5N9BO7TFBtIgZYs7WtxtOGs8e6A4ZI7ECkbBCEHeXocvOA==
   dependencies:
     esutils "^2.0.2"
     lodash "^4.17.13"
@@ -330,25 +288,25 @@
   dependencies:
     arrify "^1.0.1"
 
-"@nodelib/fs.scandir@2.1.2":
-  version "2.1.2"
-  resolved 
"https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.2.tgz#1f981cd5b83e85cfdeb386fc693d4baab392fa54";
-  integrity 
sha512-wrIBsjA5pl13f0RN4Zx4FNWmU71lv03meGKnqRUoCyan17s4V3WL92f3w3AIuWbNnpcrQyFBU5qMavJoB8d27w==
+"@nodelib/fs.scandir@2.1.3":
+  version "2.1.3"
+  resolved 
"https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b";
+  integrity 
sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==
   dependencies:
-    "@nodelib/fs.stat" "2.0.2"
+    "@nodelib/fs.stat" "2.0.3"
     run-parallel "^1.1.9"
 
-"@nodelib/fs.stat@2.0.2", "@nodelib/fs.stat@^2.0.1":
-  version "2.0.2"
-  resolved 
"https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.2.tgz#2762aea8fe78ea256860182dcb52d61ee4b8fda6";
-  integrity 
sha512-z8+wGWV2dgUhLqrtRYa03yDx4HWMvXKi1z8g3m2JyxAx8F7xk74asqPk5LAETjqDSGLFML/6CDl0+yFunSYicw==
+"@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2":
+  version "2.0.3"
+  resolved 
"https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3";
+  integrity 
sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==
 
-"@nodelib/fs.walk@^1.2.1":
-  version "1.2.3"
-  resolved 
"https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.3.tgz#a555dc256acaf00c62b0db29529028dd4d4cb141";
-  integrity 
sha512-l6t8xEhfK9Sa4YO5mIRdau7XSOADfmh3jCr0evNHdY+HNkW6xuQhgMH7D73VV6WpZOagrW0UludvMTiifiwTfA==
+"@nodelib/fs.walk@^1.2.3":
+  version "1.2.4"
+  resolved 
"https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976";
+  integrity 
sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==
   dependencies:
-    "@nodelib/fs.scandir" "2.1.2"
+    "@nodelib/fs.scandir" "2.1.3"
     fastq "^1.6.0"
 
 "@sindresorhus/is@^0.14.0":
@@ -363,13 +321,18 @@
   dependencies:
     defer-to-connect "^1.0.1"
 
-"@types/chrome@^0.0.88":
-  version "0.0.88"
-  resolved 
"https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.88.tgz#0041a101d69f78008910927c5b3299a00d8660db";
-  integrity 
sha512-JBsIrBZ2adJhlXvJ+1j0xLbcfOfwee/WAg7Lp2NE+Wf3m0vXMJFWv/PPjqNk5ZUXDeY/qDxPHe+PUjxnl8HWFg==
+"@types/chrome@^0.0.91":
+  version "0.0.91"
+  resolved 
"https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.91.tgz#4b3996f55f344057e6a677c8366aa98080c6e380";
+  integrity 
sha512-vNvo9lJkp1AvViWrUwe1bxhoMwr5dRZWlgr1DTuaNkz97LsG56lDX1sceWeZir2gRACJ5vdHtoRdVAvm8C75Ug==
   dependencies:
     "@types/filesystem" "*"
 
+"@types/color-name@^1.1.1":
+  version "1.1.1"
+  resolved 
"https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0";
+  integrity 
sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
+
 "@types/events@*":
   version "3.0.0"
   resolved 
"https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7";
@@ -402,39 +365,39 @@
   integrity 
sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
 
 "@types/node@*":
-  version "12.7.2"
-  resolved 
"https://registry.yarnpkg.com/@types/node/-/node-12.7.2.tgz#c4e63af5e8823ce9cc3f0b34f7b998c2171f0c44";
-  integrity 
sha512-dyYO+f6ihZEtNPDcWNR1fkoTDf3zAK3lAABDze3mz6POyIercH0lEUawUFXlG8xaQZmm1yEBON/4TsYv/laDYg==
+  version "12.12.11"
+  resolved 
"https://registry.yarnpkg.com/@types/node/-/node-12.12.11.tgz#bec2961975888d964196bf0016a2f984d793d3ce";
+  integrity 
sha512-O+x6uIpa6oMNTkPuHDa9MhMMehlxLAd5QcOvKRjAFsBVpeFWTOPnXbDvILvFgFFZfQ1xh1EZi1FbXxUix+zpsQ==
 
 "@types/node@^11.12.0":
-  version "11.13.19"
-  resolved 
"https://registry.yarnpkg.com/@types/node/-/node-11.13.19.tgz#c7bd7009aa850464775d8a814e18138d633cd676";
-  integrity 
sha512-tLRDU1hmcWamtgRT2iVRdraAQVGFQGgtcqracSo9XyMN1VeZLSVGb8RJJxVqab7UGbijoUijGPVFMjmqzyZIUw==
+  version "11.15.2"
+  resolved 
"https://registry.yarnpkg.com/@types/node/-/node-11.15.2.tgz#998b9cacc5f26e441d8396340818fde8e08aada5";
+  integrity 
sha512-BqCU9uIFkUH9Sgo2uLYbmIiFB1T+VBiM8AI/El3LIAI5KzwtckeSG+3WOYZr9aMoX4UIvRFBWBeSaOu6hFue2Q==
 
 "@types/prop-types@*":
-  version "15.7.1"
-  resolved 
"https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.1.tgz#f1a11e7babb0c3cad68100be381d1e064c68f1f6";
-  integrity 
sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg==
+  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.0.0":
-  version "16.9.0"
-  resolved 
"https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.0.tgz#ba6ddb00bf5de700b0eb91daa452081ffccbfdea";
-  integrity 
sha512-OL2lk7LYGjxn4b0efW3Pvf2KBVP0y1v3wip1Bp7nA79NkOpElH98q3WdCEdDj93b2b0zaeBG9DvriuKjIK5xDA==
+  version "16.9.4"
+  resolved 
"https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.4.tgz#0b58df09a60961dcb77f62d4f1832427513420df";
+  integrity 
sha512-fya9xteU/n90tda0s+FtN5Ym4tbgxpq/hb/Af24dvs6uYnYn+fspaxw5USlw0R8apDNwxsqumdRoCoKitckQqw==
   dependencies:
     "@types/react" "*"
 
 "@types/react@*", "@types/react@^16.4.0":
-  version "16.9.2"
-  resolved 
"https://registry.yarnpkg.com/@types/react/-/react-16.9.2.tgz#6d1765431a1ad1877979013906731aae373de268";
-  integrity 
sha512-jYP2LWwlh+FTqGd9v7ynUKZzjj98T8x7Yclz479QdRhHfuW9yQ+0jjnD31eXSXutmBpppj5PYNLYLRfnZJvcfg==
+  version "16.9.11"
+  resolved 
"https://registry.yarnpkg.com/@types/react/-/react-16.9.11.tgz#70e0b7ad79058a7842f25ccf2999807076ada120";
+  integrity 
sha512-UBT4GZ3PokTXSWmdgC/GeCGEJXE5ofWyibCcecRLUVN2ZBpXQGVgQGtG2foS7CrTKFKlQVVswLvf7Js6XA/CVQ==
   dependencies:
     "@types/prop-types" "*"
     csstype "^2.2.0"
 
 "@types/urijs@^1.19.3":
-  version "1.19.3"
-  resolved 
"https://registry.yarnpkg.com/@types/urijs/-/urijs-1.19.3.tgz#ed90d38baf3eff1627544ad6ed3bcdeb79ef3533";
-  integrity 
sha512-L5tP2dEIV+OMVEVRhf8PCFMNMyO5ZBodrXpEqnGczky60lcv8l5Kl9Yi4J1yxhSVfHUe+Pr2nXJfDM+rUYNs3w==
+  version "1.19.4"
+  resolved 
"https://registry.yarnpkg.com/@types/urijs/-/urijs-1.19.4.tgz#29c4a694d4842d7f95e359a26223fc1865f1ab13";
+  integrity 
sha512-uHUvuLfy4YkRHL4UH8J8oRsINhdEHd9ymag7KJZVT94CjAmY1njoUzhazJsZjwfy+IpWKQKGVyXCwzhZvg73Fg==
 
 "@webassemblyjs/ast@1.8.5":
   version "1.8.5"
@@ -615,6 +578,14 @@ acorn@^6.0.7, acorn@^6.2.1:
   resolved 
"https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e";
   integrity 
sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==
 
+aggregate-error@^3.0.0:
+  version "3.0.1"
+  resolved 
"https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.0.1.tgz#db2fe7246e536f40d9b5442a39e117d7dd6a24e0";
+  integrity 
sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==
+  dependencies:
+    clean-stack "^2.0.0"
+    indent-string "^4.0.0"
+
 ajv-errors@^1.0.0:
   version "1.0.1"
   resolved 
"https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d";
@@ -650,11 +621,11 @@ ansi-colors@^1.0.1:
     ansi-wrap "^0.1.0"
 
 ansi-escapes@^4.2.1:
-  version "4.2.1"
-  resolved 
"https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.2.1.tgz#4dccdb846c3eee10f6d64dea66273eab90c37228";
-  integrity 
sha512-Cg3ymMAdN10wOk/VYfLV7KCQyv7EDirJ64500sU7n9UlmioEtDuU5Gd+hj73hXSU/ex7tHJSssmyftDdkMLO8Q==
+  version "4.3.0"
+  resolved 
"https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.0.tgz#a4ce2b33d6b214b7950d8595c212f12ac9cc569d";
+  integrity 
sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==
   dependencies:
-    type-fest "^0.5.2"
+    type-fest "^0.8.1"
 
 ansi-gray@^0.1.1:
   version "0.1.1"
@@ -678,6 +649,11 @@ ansi-regex@^4.1.0:
   resolved 
"https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997";
   integrity 
sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
 
+ansi-regex@^5.0.0:
+  version "5.0.0"
+  resolved 
"https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75";
+  integrity 
sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
+
 ansi-styles@^3.2.0, ansi-styles@^3.2.1:
   version "3.2.1"
   resolved 
"https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d";
@@ -686,10 +662,11 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1:
     color-convert "^1.9.0"
 
 ansi-styles@^4.1.0:
-  version "4.1.0"
-  resolved 
"https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.1.0.tgz#d3ba8047b818293eaaa7978321dd61bff9842cfc";
-  integrity 
sha512-Qts4KCLKG+waHc9C4m07weIY8qyeixoS0h6RnbsNVD6Fw+pEZGW3vTyObL3WXpE09Mq4Oi7/lBEyLmOiLtlYWQ==
+  version "4.2.0"
+  resolved 
"https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.0.tgz#5681f0dcf7ae5880a7841d8831c4724ed9cc0172";
+  integrity 
sha512-7kFQgnEaMdRtwf6uSfUnVr9gSGC7faurn+J/Mv90/W+iTtN0405/nLdopfMWwchyxhbGYl6TC4Sccn9TUkGAgg==
   dependencies:
+    "@types/color-name" "^1.1.1"
     color-convert "^2.0.1"
 
 ansi-wrap@0.1.0, ansi-wrap@^0.1.0:
@@ -710,10 +687,10 @@ anymatch@^2.0.0:
     micromatch "^3.1.4"
     normalize-path "^2.1.1"
 
-anymatch@^3.0.1:
-  version "3.0.3"
-  resolved 
"https://registry.yarnpkg.com/anymatch/-/anymatch-3.0.3.tgz#2fb624fe0e84bccab00afee3d0006ed310f22f09";
-  integrity 
sha512-c6IvoeBECQlMVuYUjSwimnhmztImpErfxJzWZhIQinIvQWoGOnB0dLIgifbPHQt5heS6mNlaZG16f06H3C8t1g==
+anymatch@~3.1.1:
+  version "3.1.1"
+  resolved 
"https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142";
+  integrity 
sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==
   dependencies:
     normalize-path "^3.0.0"
     picomatch "^2.0.4"
@@ -1166,9 +1143,9 @@ bl@^3.0.0:
     readable-stream "^3.0.1"
 
 bluebird@^3.5.5:
-  version "3.5.5"
-  resolved 
"https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f";
-  integrity 
sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==
+  version "3.7.1"
+  resolved 
"https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.1.tgz#df70e302b471d7473489acf26a93d63b53f874de";
+  integrity 
sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==
 
 blueimp-md5@^2.10.0:
   version "2.12.0"
@@ -1234,7 +1211,7 @@ braces@^2.3.1, braces@^2.3.2:
     split-string "^3.0.2"
     to-regex "^3.0.1"
 
-braces@^3.0.1, braces@^3.0.2:
+braces@^3.0.1, braces@~3.0.2:
   version "3.0.2"
   resolved 
"https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107";
   integrity 
sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
@@ -1326,18 +1303,18 @@ buffer-xor@^1.0.3:
   integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=
 
 buffer@^4.3.0:
-  version "4.9.1"
-  resolved 
"https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298";
-  integrity sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=
+  version "4.9.2"
+  resolved 
"https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8";
+  integrity 
sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==
   dependencies:
     base64-js "^1.0.2"
     ieee754 "^1.1.4"
     isarray "^1.0.0"
 
 buffer@^5.1.0:
-  version "5.4.0"
-  resolved 
"https://registry.yarnpkg.com/buffer/-/buffer-5.4.0.tgz#33294f5c1f26e08461e528b69fa06de3c45cbd8c";
-  integrity 
sha512-Xpgy0IwHK2N01ncykXTy6FpCWuM+CJSHoPVBLyNqyrWxsedpLvwsYUhf0ME3WRFNUhos0dMamz9cOS/xRDtU5g==
+  version "5.4.3"
+  resolved 
"https://registry.yarnpkg.com/buffer/-/buffer-5.4.3.tgz#3fbc9c69eb713d323e3fc1a895eee0710c072115";
+  integrity 
sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==
   dependencies:
     base64-js "^1.0.2"
     ieee754 "^1.1.4"
@@ -1378,6 +1355,30 @@ cacache@^12.0.2:
     unique-filename "^1.1.1"
     y18n "^4.0.0"
 
+cacache@^13.0.1:
+  version "13.0.1"
+  resolved 
"https://registry.yarnpkg.com/cacache/-/cacache-13.0.1.tgz#a8000c21697089082f85287a1aec6e382024a71c";
+  integrity 
sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w==
+  dependencies:
+    chownr "^1.1.2"
+    figgy-pudding "^3.5.1"
+    fs-minipass "^2.0.0"
+    glob "^7.1.4"
+    graceful-fs "^4.2.2"
+    infer-owner "^1.0.4"
+    lru-cache "^5.1.1"
+    minipass "^3.0.0"
+    minipass-collect "^1.0.2"
+    minipass-flush "^1.0.5"
+    minipass-pipeline "^1.2.2"
+    mkdirp "^0.5.1"
+    move-concurrently "^1.0.1"
+    p-map "^3.0.0"
+    promise-inflight "^1.0.1"
+    rimraf "^2.7.1"
+    ssri "^7.0.0"
+    unique-filename "^1.1.1"
+
 cache-base@^1.0.1:
   version "1.0.1"
   resolved 
"https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2";
@@ -1470,9 +1471,9 @@ check-types@^8.0.3:
   integrity 
sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ==
 
 chokidar@^2.0.0, chokidar@^2.0.2:
-  version "2.1.6"
-  resolved 
"https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.6.tgz#b6cad653a929e244ce8a834244164d241fa954c5";
-  integrity 
sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==
+  version "2.1.8"
+  resolved 
"https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917";
+  integrity 
sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==
   dependencies:
     anymatch "^2.0.0"
     async-each "^1.0.1"
@@ -1489,24 +1490,24 @@ chokidar@^2.0.0, chokidar@^2.0.2:
     fsevents "^1.2.7"
 
 chokidar@^3.0.2:
-  version "3.0.2"
-  resolved 
"https://registry.yarnpkg.com/chokidar/-/chokidar-3.0.2.tgz#0d1cd6d04eb2df0327446188cd13736a3367d681";
-  integrity 
sha512-c4PR2egjNjI1um6bamCQ6bUNPDiyofNQruHvKgHQ4gDUP/ITSVSzNsiI5OWtHOsX323i5ha/kk4YmOZ1Ktg7KA==
-  dependencies:
-    anymatch "^3.0.1"
-    braces "^3.0.2"
-    glob-parent "^5.0.0"
-    is-binary-path "^2.1.0"
-    is-glob "^4.0.1"
-    normalize-path "^3.0.0"
-    readdirp "^3.1.1"
+  version "3.3.0"
+  resolved 
"https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6";
+  integrity 
sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==
+  dependencies:
+    anymatch "~3.1.1"
+    braces "~3.0.2"
+    glob-parent "~5.1.0"
+    is-binary-path "~2.1.0"
+    is-glob "~4.0.1"
+    normalize-path "~3.0.0"
+    readdirp "~3.2.0"
   optionalDependencies:
-    fsevents "^2.0.6"
+    fsevents "~2.1.1"
 
-chownr@^1.1.1:
-  version "1.1.2"
-  resolved 
"https://registry.yarnpkg.com/chownr/-/chownr-1.1.2.tgz#a18f1e0b269c8a6a5d3c86eb298beb14c3dd7bf6";
-  integrity 
sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==
+chownr@^1.1.1, chownr@^1.1.2:
+  version "1.1.3"
+  resolved 
"https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142";
+  integrity 
sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==
 
 chrome-trace-event@^1.0.2:
   version "1.0.2"
@@ -1548,7 +1549,7 @@ class-utils@^0.3.5:
     isobject "^3.0.0"
     static-extend "^0.1.1"
 
-clean-stack@^2.2.0:
+clean-stack@^2.0.0, clean-stack@^2.2.0:
   version "2.2.0"
   resolved 
"https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b";
   integrity 
sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
@@ -1702,15 +1703,10 @@ color-support@^1.1.3:
   resolved 
"https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2";
   integrity 
sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
 
-commander@^2.12.1, commander@^2.18.0, commander@^2.20.0, commander@~2.20.0:
-  version "2.20.0"
-  resolved 
"https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422";
-  integrity 
sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
-
-commander@^3.0.1:
-  version "3.0.1"
-  resolved 
"https://registry.yarnpkg.com/commander/-/commander-3.0.1.tgz#4595aec3530525e671fb6f85fb173df8ff8bf57a";
-  integrity 
sha512-UNgvDd+csKdc9GD4zjtkHKQbT8Aspt2jCBqNSPp53vAS0L1tS9sXB2TCEOPHJ7kt9bN/niWkYj8T3RQSoMXdSQ==
+commander@^2.12.1, commander@^2.18.0, commander@^2.20.0, commander@~2.20.3:
+  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==
 
 common-path-prefix@^1.0.0:
   version "1.0.0"
@@ -1782,11 +1778,9 @@ configstore@^4.0.0:
     xdg-basedir "^3.0.0"
 
 console-browserify@^1.1.0:
-  version "1.1.0"
-  resolved 
"https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10";
-  integrity sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=
-  dependencies:
-    date-now "^0.1.4"
+  version "1.2.0"
+  resolved 
"https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336";
+  integrity 
sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==
 
 console-control-strings@^1.0.0, console-control-strings@~1.1.0:
   version "1.1.0"
@@ -1810,10 +1804,10 @@ content-type@~1.0.4:
   resolved 
"https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b";
   integrity 
sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
 
-convert-source-map@^1.1.0, convert-source-map@^1.5.0, 
convert-source-map@^1.6.0:
-  version "1.6.0"
-  resolved 
"https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20";
-  integrity 
sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==
+convert-source-map@^1.5.0, convert-source-map@^1.6.0, 
convert-source-map@^1.7.0:
+  version "1.7.0"
+  resolved 
"https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442";
+  integrity 
sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
   dependencies:
     safe-buffer "~5.1.1"
 
@@ -1858,9 +1852,9 @@ copy-props@^2.0.1:
     is-plain-object "^2.0.1"
 
 core-js@^2.0.0:
-  version "2.6.9"
-  resolved 
"https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2";
-  integrity 
sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==
+  version "2.6.10"
+  resolved 
"https://registry.yarnpkg.com/core-js/-/core-js-2.6.10.tgz#8a5b8391f8cc7013da703411ce5b585706300d7f";
+  integrity 
sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==
 
 core-util-is@~1.0.0:
   version "1.0.2"
@@ -1975,9 +1969,9 @@ crypto-random-string@^1.0.0:
   integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=
 
 csstype@^2.2.0:
-  version "2.6.6"
-  resolved 
"https://registry.yarnpkg.com/csstype/-/csstype-2.6.6.tgz#c34f8226a94bbb10c32cc0d714afdf942291fc41";
-  integrity 
sha512-RpFbQGUE74iyPgvr46U9t1xoQBM8T4BL8SxrN66Le2xYAPSaDJJKeztV3awugusb3g3G9iL8StmkBBXhcbbXhg==
+  version "2.6.7"
+  resolved 
"https://registry.yarnpkg.com/csstype/-/csstype-2.6.7.tgz#20b0024c20b6718f4eda3853a1f5a1cce7f5e4a5";
+  integrity 
sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ==
 
 currently-unhandled@^0.4.1:
   version "0.4.1"
@@ -1986,12 +1980,12 @@ currently-unhandled@^0.4.1:
   dependencies:
     array-find-index "^1.0.1"
 
-cyclist@~0.2.2:
-  version "0.2.2"
-  resolved 
"https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640";
-  integrity sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=
+cyclist@^1.0.1:
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9";
+  integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=
 
-d@1:
+d@1, d@^1.0.1:
   version "1.0.1"
   resolved 
"https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a";
   integrity 
sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==
@@ -1999,11 +1993,6 @@ d@1:
     es5-ext "^0.10.50"
     type "^1.0.1"
 
-date-now@^0.1.4:
-  version "0.1.4"
-  resolved 
"https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b";
-  integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=
-
 date-time@^2.1.0:
   version "2.1.0"
   resolved 
"https://registry.yarnpkg.com/date-time/-/date-time-2.1.0.tgz#0286d1b4c769633b3ca13e1e62558d2dbdc2eba2";
@@ -2065,9 +2054,16 @@ decompress-response@^3.3.0:
     mimic-response "^1.0.0"
 
 deep-equal@^1.0.0:
-  version "1.0.1"
-  resolved 
"https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5";
-  integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=
+  version "1.1.1"
+  resolved 
"https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a";
+  integrity 
sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==
+  dependencies:
+    is-arguments "^1.0.4"
+    is-date-object "^1.0.1"
+    is-regex "^1.0.4"
+    object-is "^1.0.1"
+    object-keys "^1.1.1"
+    regexp.prototype.flags "^1.2.0"
 
 deep-extend@^0.6.0:
   version "0.6.0"
@@ -2101,9 +2097,9 @@ defaults@^1.0.3:
     clone "^1.0.2"
 
 defer-to-connect@^1.0.1:
-  version "1.0.2"
-  resolved 
"https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.0.2.tgz#4bae758a314b034ae33902b5aac25a8dd6a8633e";
-  integrity 
sha512-k09hcQcTDY+cwgiwa6PYKLm3jlagNzQ+RSvhjzESOGOx+MNOuXkxTfEvPrO1IOQ81tArCFYQgi631clB70RpQw==
+  version "1.1.0"
+  resolved 
"https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.0.tgz#b41bd7efa8508cef13f8456975f7a278c72833fd";
+  integrity 
sha512-WE2sZoctWm/v4smfCAdjYbrfS55JiMRdlY9ZubFhsYbteCK9+BvAx4YV7nPjYM6ZnX5BcoVKwfmyx9sIFTgQMQ==
 
 define-properties@^1.1.2:
   version "1.1.3"
@@ -2158,9 +2154,9 @@ depd@~1.1.2:
   integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
 
 des.js@^1.0.0:
-  version "1.0.0"
-  resolved 
"https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc";
-  integrity sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843";
+  integrity 
sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==
   dependencies:
     inherits "^2.0.1"
     minimalistic-assert "^1.0.0"
@@ -2180,10 +2176,10 @@ detect-libc@^1.0.2:
   resolved 
"https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b";
   integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
 
-diff@^3.2.0:
-  version "3.5.0"
-  resolved 
"https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12";
-  integrity 
sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
+diff@^4.0.1:
+  version "4.0.1"
+  resolved 
"https://registry.yarnpkg.com/diff/-/diff-4.0.1.tgz#0c667cb467ebbb5cea7f14f135cc2dba7780a8ff";
+  integrity 
sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==
 
 diffie-hellman@^5.0.0:
   version "5.0.3"
@@ -2214,9 +2210,9 @@ dot-prop@^4.1.0:
     is-obj "^1.0.0"
 
 dot-prop@^5.1.0:
-  version "5.1.0"
-  resolved 
"https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.1.0.tgz#bdd8c986a77b83e3fca524e53786df916cabbd8a";
-  integrity 
sha512-n1oC6NBF+KM9oVXtjmen4Yo7HyAVWV2UUl50dCYJdw2924K6dX9bf9TTTWaKtYlRn0FEtxG27KS80ayVLixxJA==
+  version "5.2.0"
+  resolved 
"https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb";
+  integrity 
sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==
   dependencies:
     is-obj "^2.0.0"
 
@@ -2254,14 +2250,14 @@ ee-first@1.1.1:
   integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
 
 ejs@^2.6.1:
-  version "2.6.2"
-  resolved 
"https://registry.yarnpkg.com/ejs/-/ejs-2.6.2.tgz#3a32c63d1cd16d11266cd4703b14fec4e74ab4f6";
-  integrity 
sha512-PcW2a0tyTuPHz3tWyYqtK6r1fZ3gp+3Sop8Ph+ZYN81Ob5rwmbHEzaqs10N3BEsaGTkh/ooniXK+WwszGlc2+Q==
+  version "2.7.4"
+  resolved 
"https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba";
+  integrity 
sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==
 
 elliptic@^6.0.0:
-  version "6.5.0"
-  resolved 
"https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.0.tgz#2b8ed4c891b7de3200e14412a5b8248c7af505ca";
-  integrity 
sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg==
+  version "6.5.1"
+  resolved 
"https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.1.tgz#c380f5f909bf1b9b4428d028cd18d3b0efd6b52b";
+  integrity 
sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==
   dependencies:
     bn.js "^4.4.0"
     brorand "^1.0.1"
@@ -2312,13 +2308,13 @@ encoding@^0.1.12:
     iconv-lite "~0.4.13"
 
 end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1:
-  version "1.4.1"
-  resolved 
"https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43";
-  integrity 
sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==
+  version "1.4.4"
+  resolved 
"https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0";
+  integrity 
sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
   dependencies:
     once "^1.4.0"
 
-enhanced-resolve@4.1.0, enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0:
+enhanced-resolve@4.1.0:
   version "4.1.0"
   resolved 
"https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f";
   integrity 
sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==
@@ -2327,6 +2323,15 @@ enhanced-resolve@4.1.0, enhanced-resolve@^4.0.0, 
enhanced-resolve@^4.1.0:
     memory-fs "^0.4.0"
     tapable "^1.0.0"
 
+enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0:
+  version "4.1.1"
+  resolved 
"https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz#2937e2b8066cd0fe7ce0990a98f0d71a35189f66";
+  integrity 
sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==
+  dependencies:
+    graceful-fs "^4.1.2"
+    memory-fs "^0.5.0"
+    tapable "^1.0.0"
+
 equal-length@^1.0.0:
   version "1.0.1"
   resolved 
"https://registry.yarnpkg.com/equal-length/-/equal-length-1.0.1.tgz#21ca112d48ab24b4e1e7ffc0e5339d31fdfc274c";
@@ -2346,14 +2351,14 @@ error-ex@^1.2.0, error-ex@^1.3.1:
   dependencies:
     is-arrayish "^0.2.1"
 
-es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@~0.10.14:
-  version "0.10.50"
-  resolved 
"https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.50.tgz#6d0e23a0abdb27018e5ac4fd09b412bc5517a778";
-  integrity 
sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw==
+es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50:
+  version "0.10.52"
+  resolved 
"https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.52.tgz#bb21777e919a04263736ded120a9d665f10ea63f";
+  integrity 
sha512-bWCbE9fbpYQY4CU6hJbJ1vSz70EClMlDgJ7BmwI+zEJhxrwjesZRPglGJlsZhu0334U3hI+gaspwksH9IGD6ag==
   dependencies:
     es6-iterator "~2.0.3"
-    es6-symbol "~3.1.1"
-    next-tick "^1.0.0"
+    es6-symbol "~3.1.2"
+    next-tick "~1.0.0"
 
 es6-error@^4.0.1:
   version "4.1.1"
@@ -2369,13 +2374,13 @@ es6-iterator@^2.0.1, es6-iterator@^2.0.3, 
es6-iterator@~2.0.3:
     es5-ext "^0.10.35"
     es6-symbol "^3.1.1"
 
-es6-symbol@^3.1.1, es6-symbol@~3.1.1:
-  version "3.1.1"
-  resolved 
"https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77";
-  integrity sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=
+es6-symbol@^3.1.1, es6-symbol@~3.1.2:
+  version "3.1.3"
+  resolved 
"https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18";
+  integrity 
sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==
   dependencies:
-    d "1"
-    es5-ext "~0.10.14"
+    d "^1.0.1"
+    ext "^1.1.2"
 
 es6-weak-map@^2.0.1:
   version "2.0.3"
@@ -2554,6 +2559,13 @@ express@^4.16.3:
     utils-merge "1.0.1"
     vary "~1.1.2"
 
+ext@^1.1.2:
+  version "1.2.0"
+  resolved 
"https://registry.yarnpkg.com/ext/-/ext-1.2.0.tgz#8dd8d2dd21bcced3045be09621fa0cbf73908ba4";
+  integrity 
sha512-0ccUQK/9e3NreLFg6K6np8aPyRgwycx+oFGtfx1dSp7Wj00Ozw9r05FgBRlzjf2XBM7LAzwgLyDscRrtSU91hA==
+  dependencies:
+    type "^2.0.0"
+
 extend-shallow@^2.0.1:
   version "2.0.1"
   resolved 
"https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f";
@@ -2609,15 +2621,14 @@ fast-diff@^1.1.2:
   integrity 
sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
 
 fast-glob@^3.0.3:
-  version "3.0.4"
-  resolved 
"https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.0.4.tgz#d484a41005cb6faeb399b951fd1bd70ddaebb602";
-  integrity 
sha512-wkIbV6qg37xTJwqSsdnIphL1e+LaGz4AIQqr00mIubMaEhv1/HEmJ0uuCGZRNRUkZZmOB5mJKO0ZUTVq+SxMQg==
+  version "3.1.0"
+  resolved 
"https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.1.0.tgz#77375a7e3e6f6fc9b18f061cddd28b8d1eec75ae";
+  integrity 
sha512-TrUz3THiq2Vy3bjfQUB2wNyPdGBeGmdjbzzBLhfHN4YFurYptCKwGq/TfiRavbGywFRzY6U2CdmQ1zmsY5yYaw==
   dependencies:
-    "@nodelib/fs.stat" "^2.0.1"
-    "@nodelib/fs.walk" "^1.2.1"
-    glob-parent "^5.0.0"
-    is-glob "^4.0.1"
-    merge2 "^1.2.3"
+    "@nodelib/fs.stat" "^2.0.2"
+    "@nodelib/fs.walk" "^1.2.3"
+    glob-parent "^5.1.0"
+    merge2 "^1.3.0"
     micromatch "^4.0.2"
 
 fast-json-stable-stringify@^2.0.0:
@@ -2638,9 +2649,9 @@ figgy-pudding@^3.5.1:
   integrity 
sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==
 
 figures@^3.0.0:
-  version "3.0.0"
-  resolved 
"https://registry.yarnpkg.com/figures/-/figures-3.0.0.tgz#756275c964646163cc6f9197c7a0295dbfd04de9";
-  integrity 
sha512-HKri+WoWoUgr83pehn/SIgLOMZ9nAWC6dcGj26RY2R4F50u4+RTUz0RCrUlOV3nKRAICW1UGzyb+kcX2qK1S/g==
+  version "3.1.0"
+  resolved 
"https://registry.yarnpkg.com/figures/-/figures-3.1.0.tgz#4b198dd07d8d71530642864af2d45dd9e459c4ec";
+  integrity 
sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==
   dependencies:
     escape-string-regexp "^1.0.5"
 
@@ -2688,6 +2699,15 @@ find-cache-dir@^2.1.0:
     make-dir "^2.0.0"
     pkg-dir "^3.0.0"
 
+find-cache-dir@^3.0.0:
+  version "3.1.0"
+  resolved 
"https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.1.0.tgz#9935894999debef4cf9f677fdf646d002c4cdecb";
+  integrity 
sha512-zw+EFiNBNPgI2NTrKkDd1xd7q0cs6wr/iWnr/oUkI0yF9K9GqQ+riIt4aiyFaaqpaWbxPrJXHI+QvmNUQbX+0Q==
+  dependencies:
+    commondir "^1.0.1"
+    make-dir "^3.0.0"
+    pkg-dir "^4.1.0"
+
 find-up@^1.0.0:
   version "1.1.2"
   resolved 
"https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f";
@@ -2829,11 +2849,18 @@ fs-extra@^8.1.0:
     universalify "^0.1.0"
 
 fs-minipass@^1.2.5:
-  version "1.2.6"
-  resolved 
"https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07";
-  integrity 
sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ==
+  version "1.2.7"
+  resolved 
"https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7";
+  integrity 
sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==
+  dependencies:
+    minipass "^2.6.0"
+
+fs-minipass@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.0.0.tgz#a6415edab02fae4b9e9230bc87ee2e4472003cd1";
+  integrity 
sha512-40Qz+LFXmd9tzYVnnBmZvFfvAADfUA14TXPK1s7IfElJTIZ97rA8w4Kin7Wt5JBrC3ShnnFJO/5vPjPEeJIq9A==
   dependencies:
-    minipass "^2.2.1"
+    minipass "^3.0.0"
 
 fs-mkdirp-stream@^1.0.0:
   version "1.0.0"
@@ -2866,10 +2893,10 @@ fsevents@^1.2.7:
     nan "^2.12.1"
     node-pre-gyp "^0.12.0"
 
-fsevents@^2.0.6:
-  version "2.0.7"
-  resolved 
"https://registry.yarnpkg.com/fsevents/-/fsevents-2.0.7.tgz#382c9b443c6cbac4c57187cdda23aa3bf1ccfc2a";
-  integrity 
sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==
+fsevents@~2.1.1:
+  version "2.1.2"
+  resolved 
"https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805";
+  integrity 
sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==
 
 function-bind@^1.1.1:
   version "1.1.1"
@@ -2939,7 +2966,7 @@ gettext-parser@2.0.0:
     encoding "^0.1.12"
     safe-buffer "^5.1.2"
 
-gettext-parser@^1.3.0:
+gettext-parser@^1.4.0:
   version "1.4.0"
   resolved 
"https://registry.yarnpkg.com/gettext-parser/-/gettext-parser-1.4.0.tgz#f8baf34a292f03d5e42f02df099d301f167a7ace";
   integrity 
sha512-sedZYLHlHeBop/gZ1jdg59hlUEcpcZJofLq2JFwJT1zTqAU3l2wFv6IsuwFHGqbiT9DWzMUW4/em2+hspnmMMA==
@@ -2948,11 +2975,11 @@ gettext-parser@^1.3.0:
     safe-buffer "^5.1.1"
 
 gettext-to-messageformat@^0.3.0:
-  version "0.3.0"
-  resolved 
"https://registry.yarnpkg.com/gettext-to-messageformat/-/gettext-to-messageformat-0.3.0.tgz#6b9f9e2f554825a03463ce6669bbb94c42fc4023";
-  integrity 
sha512-HlEGFECqAavbOYJTo1I2qh8IqWetAenixaH/AbdIuNTY0easvzrPn+yYUHy63GEjx9pXoLx2nCJcDcE27LrB2g==
+  version "0.3.1"
+  resolved 
"https://registry.yarnpkg.com/gettext-to-messageformat/-/gettext-to-messageformat-0.3.1.tgz#c303ff9e31a5c781b14629b13bddb27b9ffca1f8";
+  integrity 
sha512-UyqIL3Ul4NryU95Wome/qtlcuVIqgEWVIFw0zi7Lv14ACLXfaVDCbrjZ7o+3BZ7u+4NS1mP/2O1eXZoHCoas8g==
   dependencies:
-    gettext-parser "^1.3.0"
+    gettext-parser "^1.4.0"
 
 glob-parent@^3.1.0:
   version "3.1.0"
@@ -2962,10 +2989,10 @@ glob-parent@^3.1.0:
     is-glob "^3.1.0"
     path-dirname "^1.0.0"
 
-glob-parent@^5.0.0:
-  version "5.0.0"
-  resolved 
"https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954";
-  integrity 
sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==
+glob-parent@^5.1.0, glob-parent@~5.1.0:
+  version "5.1.0"
+  resolved 
"https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2";
+  integrity 
sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==
   dependencies:
     is-glob "^4.0.1"
 
@@ -2998,9 +3025,9 @@ glob-watcher@^5.0.3:
     object.defaults "^1.1.0"
 
 glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4:
-  version "7.1.4"
-  resolved 
"https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255";
-  integrity 
sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==
+  version "7.1.6"
+  resolved 
"https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6";
+  integrity 
sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
   dependencies:
     fs.realpath "^1.0.0"
     inflight "^1.0.4"
@@ -3106,10 +3133,10 @@ got@^9.6.0:
     to-readable-stream "^1.0.0"
     url-parse-lax "^3.0.0"
 
-graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, 
graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0:
-  version "4.2.2"
-  resolved 
"https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02";
-  integrity 
sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==
+graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, 
graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2:
+  version "4.2.3"
+  resolved 
"https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423";
+  integrity 
sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==
 
 gulp-cli@^2.2.0:
   version "2.2.0"
@@ -3165,9 +3192,9 @@ gulp-rename@^1.2.2:
   integrity 
sha512-swzbIGb/arEoFK89tPY58vg3Ok1bw+d35PfUNwWqdo7KM4jkmuGA78JiDNqR+JeZFaeeHnRg9N7aihX3YPmsyg==
 
 gulp-tar@^3.0.0:
-  version "3.0.0"
-  resolved 
"https://registry.yarnpkg.com/gulp-tar/-/gulp-tar-3.0.0.tgz#05746ce4e1f67d0798475bdc2f2f95a9c661b30b";
-  integrity 
sha512-vgUeodtzF5QY4i74QEYHUVZN9YCY3OOTZhMWYHC3J76T1n/lMltJYupbNI1UZyYin7rwdLjV+dZDiN2w6Pjc6Q==
+  version "3.1.0"
+  resolved 
"https://registry.yarnpkg.com/gulp-tar/-/gulp-tar-3.1.0.tgz#2e474619b2820eddf5cf48cec8aac08eaee3bf20";
+  integrity 
sha512-+Y3ntrDIBBVoxqlAnb0STUdVNx+xceH6YIFXvYMA48avJiwa+a81bFbJ/loz47zSF8nflc15ON0YLX4YgMLAvw==
   dependencies:
     archiver "^3.1.1"
     plugin-error "^1.0.1"
@@ -3175,9 +3202,9 @@ gulp-tar@^3.0.0:
     vinyl "^2.1.0"
 
 gulp-zip@^5.0.0:
-  version "5.0.0"
-  resolved 
"https://registry.yarnpkg.com/gulp-zip/-/gulp-zip-5.0.0.tgz#ba1af3247e7acc4dc01a72a77c568654cfb1dd1f";
-  integrity 
sha512-oR3t8kn+ccHkSyRcBV5kBLPXrhqTh5d6wBAR7r7wqjNQNBhYvOwPedCwlAaGcNl1qSeXNDn6qOk1Qyxvx9Wrow==
+  version "5.0.1"
+  resolved 
"https://registry.yarnpkg.com/gulp-zip/-/gulp-zip-5.0.1.tgz#0dba5094901b0d91bcc8b53b3f6ff797c0f2002d";
+  integrity 
sha512-M/IWLh9RvOpuofDZkgDirtiyz9J3yIqnDOJ3muzk2D/XnZ1ruqPlPLRIpXnl/aZU+xXwKPdOIxjRzkUcVEQyZQ==
   dependencies:
     get-stream "^5.1.0"
     plugin-error "^1.0.1"
@@ -3210,10 +3237,10 @@ gzip-size@^5.0.0:
     duplexer "^0.1.1"
     pify "^4.0.1"
 
-handlebars@^4.1.2:
-  version "4.1.2"
-  resolved 
"https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.2.tgz#b6b37c1ced0306b221e094fc7aca3ec23b131b67";
-  integrity 
sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==
+handlebars@^4.1.2, handlebars@^4.5.1:
+  version "4.5.3"
+  resolved 
"https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.3.tgz#5cf75bd8714f7605713511a56be7c349becb0482";
+  integrity 
sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==
   dependencies:
     neo-async "^2.6.0"
     optimist "^0.6.1"
@@ -3232,9 +3259,9 @@ has-flag@^4.0.0:
   integrity 
sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
 
 has-symbols@^1.0.0:
-  version "1.0.0"
-  resolved 
"https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44";
-  integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8";
+  integrity 
sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
 
 has-unicode@^2.0.0:
   version "2.0.1"
@@ -3277,6 +3304,13 @@ has-yarn@^2.1.0:
   resolved 
"https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77";
   integrity 
sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==
 
+has@^1.0.1:
+  version "1.0.3"
+  resolved 
"https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796";
+  integrity 
sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
+  dependencies:
+    function-bind "^1.1.1"
+
 hash-base@^3.0.0:
   version "3.0.4"
   resolved 
"https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918";
@@ -3301,17 +3335,17 @@ hasha@^3.0.0:
     is-stream "^1.0.1"
 
 hasha@^5.0.0:
-  version "5.0.0"
-  resolved 
"https://registry.yarnpkg.com/hasha/-/hasha-5.0.0.tgz#fdc3785caea03df29535fc8adb512c3d3a709004";
-  integrity 
sha512-PqWdhnQhq6tqD32hZv+l1e5mJHNSudjnaAzgAHfkGiU0ABN6lmbZF8abJIulQHbZ7oiHhP8yL6O910ICMc+5pw==
+  version "5.1.0"
+  resolved 
"https://registry.yarnpkg.com/hasha/-/hasha-5.1.0.tgz#dd05ccdfcfe7dab626247ce2a58efe461922f4ca";
+  integrity 
sha512-OFPDWmzPN1l7atOV1TgBVmNtBxaIysToK6Ve9DK+vT6pYuklw/nPNT+HJbZi0KDcI6vWB+9tgvZ5YD7fA3CXcA==
   dependencies:
-    is-stream "^1.1.0"
-    type-fest "^0.3.0"
+    is-stream "^2.0.0"
+    type-fest "^0.8.0"
 
-highlight.js@^9.15.8:
-  version "9.15.10"
-  resolved 
"https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.15.10.tgz#7b18ed75c90348c045eef9ed08ca1319a2219ad2";
-  integrity 
sha512-RoV7OkQm0T3os3Dd2VHLNMoaoDVx77Wygln3n9l5YV172XonWG6rgQD3XnF/BuFFZw9A0TJgmMSO8FEWQgvcXw==
+highlight.js@^9.16.2:
+  version "9.16.2"
+  resolved 
"https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.16.2.tgz#68368d039ffe1c6211bcc07e483daf95de3e403e";
+  integrity 
sha512-feMUrVLZvjy0oC7FVJQcSQRqbBq9kwqnYE4+Kj9ZjbHh3g+BisiPgF49NyQbVLNdrL/qqZr3Ca9yOKwgn2i/tw==
 
 hmac-drbg@^1.0.0:
   version "1.0.1"
@@ -3335,9 +3369,9 @@ hoopy@^0.1.4:
   integrity 
sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==
 
 hosted-git-info@^2.1.4:
-  version "2.8.4"
-  resolved 
"https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.4.tgz#44119abaf4bc64692a16ace34700fed9c03e2546";
-  integrity 
sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==
+  version "2.8.5"
+  resolved 
"https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c";
+  integrity 
sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==
 
 http-cache-semantics@^4.0.0:
   version "4.0.3"
@@ -3378,10 +3412,10 @@ iconv-lite@0.4.24, iconv-lite@^0.4.4, 
iconv-lite@~0.4.13:
   dependencies:
     safer-buffer ">= 2.1.2 < 3"
 
-idb-bridge@^0.0.10:
-  version "0.0.10"
-  resolved 
"https://registry.yarnpkg.com/idb-bridge/-/idb-bridge-0.0.10.tgz#69d59550dc722f6bf62cb98a4d4a2f2b9e66653c";
-  integrity 
sha512-AE8YipDGjnS+APSlupcPNCNslCAErRwfUgJ8aNZigmVIr3xzI0YPGWr6NC6UI6ofpQ1DczUlaKqA86m1R/COGQ==
+idb-bridge@^0.0.11:
+  version "0.0.11"
+  resolved 
"https://registry.yarnpkg.com/idb-bridge/-/idb-bridge-0.0.11.tgz#ba2fbd24b7e6f7f4de8333ed12b0912e64dda308";
+  integrity 
sha512-fLlHce/WwT6eD3sc54gsfvM5fZqrhAPwBNH4uU/y6D0C1+0higH7OgC5/wploMhkmNYkQID3BMNZvSUBr0leSQ==
 
 ieee754@^1.1.4:
   version "1.1.13"
@@ -3399,9 +3433,9 @@ ignore-by-default@^1.0.0:
   integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk=
 
 ignore-walk@^3.0.1:
-  version "3.0.1"
-  resolved 
"https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8";
-  integrity 
sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==
+  version "3.0.3"
+  resolved 
"https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37";
+  integrity 
sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==
   dependencies:
     minimatch "^3.0.4"
 
@@ -3446,7 +3480,7 @@ indent-string@^4.0.0:
   resolved 
"https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251";
   integrity 
sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
 
-infer-owner@^1.0.3:
+infer-owner@^1.0.3, infer-owner@^1.0.4:
   version "1.0.4"
   resolved 
"https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467";
   integrity 
sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==
@@ -3526,6 +3560,11 @@ is-accessor-descriptor@^1.0.0:
   dependencies:
     kind-of "^6.0.0"
 
+is-arguments@^1.0.4:
+  version "1.0.4"
+  resolved 
"https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3";
+  integrity 
sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==
+
 is-arrayish@^0.2.1:
   version "0.2.1"
   resolved 
"https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d";
@@ -3538,7 +3577,7 @@ is-binary-path@^1.0.0:
   dependencies:
     binary-extensions "^1.0.0"
 
-is-binary-path@^2.1.0:
+is-binary-path@~2.1.0:
   version "2.1.0"
   resolved 
"https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09";
   integrity 
sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
@@ -3551,9 +3590,9 @@ is-buffer@^1.1.5:
   integrity 
sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
 
 is-buffer@^2.0.2:
-  version "2.0.3"
-  resolved 
"https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725";
-  integrity 
sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==
+  version "2.0.4"
+  resolved 
"https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623";
+  integrity 
sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==
 
 is-ci@^2.0.0:
   version "2.0.0"
@@ -3576,6 +3615,11 @@ is-data-descriptor@^1.0.0:
   dependencies:
     kind-of "^6.0.0"
 
+is-date-object@^1.0.1:
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16";
+  integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=
+
 is-descriptor@^0.1.0:
   version "0.1.6"
   resolved 
"https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca";
@@ -3640,7 +3684,7 @@ is-glob@^3.1.0:
   dependencies:
     is-extglob "^2.1.0"
 
-is-glob@^4.0.0, is-glob@^4.0.1:
+is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
   version "4.0.1"
   resolved 
"https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc";
   integrity 
sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
@@ -3747,6 +3791,13 @@ is-promise@^2.1.0:
   resolved 
"https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa";
   integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=
 
+is-regex@^1.0.4:
+  version "1.0.4"
+  resolved 
"https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491";
+  integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=
+  dependencies:
+    has "^1.0.1"
+
 is-relative@^1.0.0:
   version "1.0.0"
   resolved 
"https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d";
@@ -3759,6 +3810,11 @@ is-stream@^1.0.1, is-stream@^1.1.0:
   resolved 
"https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44";
   integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
 
+is-stream@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3";
+  integrity 
sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==
+
 is-typedarray@^1.0.0:
   version "1.0.0"
   resolved 
"https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a";
@@ -3885,6 +3941,14 @@ jed@^1.1.1:
   resolved 
"https://registry.yarnpkg.com/jed/-/jed-1.1.1.tgz#7a549bbd9ffe1585b0cd0a191e203055bee574b4";
   integrity sha1-elSbvZ/+FYWwzQoZHiAwVb7ldLQ=
 
+jest-worker@^24.9.0:
+  version "24.9.0"
+  resolved 
"https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5";
+  integrity 
sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==
+  dependencies:
+    merge-stream "^2.0.0"
+    supports-color "^6.1.0"
+
 jquery@^3.4.1:
   version "3.4.1"
   resolved 
"https://registry.yarnpkg.com/jquery/-/jquery-3.4.1.tgz#714f1f8d9dde4bdfa55764ba37ef214630d80ef2";
@@ -3946,9 +4010,9 @@ json5@^1.0.1:
     minimist "^1.2.0"
 
 json5@^2.1.0:
-  version "2.1.0"
-  resolved 
"https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850";
-  integrity 
sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==
+  version "2.1.1"
+  resolved 
"https://registry.yarnpkg.com/json5/-/json5-2.1.1.tgz#81b6cb04e9ba496f1c7005d07b4368a2638f90b6";
+  integrity 
sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==
   dependencies:
     minimist "^1.2.0"
 
@@ -4202,9 +4266,9 @@ loud-rejection@^1.0.0:
     signal-exit "^3.0.0"
 
 loud-rejection@^2.1.0:
-  version "2.1.0"
-  resolved 
"https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-2.1.0.tgz#4020547ddbc39ed711c8434326df9fc7d2395355";
-  integrity 
sha512-g/6MQxUXYHeVqZ4PGpPL1fS1fOvlXoi7bay0pizmjAd/3JhyXwxzwrnr74yzdmhuerlslbRJ3x7IOXzFz0cE5w==
+  version "2.2.0"
+  resolved 
"https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-2.2.0.tgz#4255eb6e9c74045b0edc021fa7397ab655a8517c";
+  integrity 
sha512-S0FayMXku80toa5sZ6Ro4C+s+EtFDCsyJNG/AzFMfX3AxD5Si4dZsgzm/kKnbOxHl5Cv8jBlno8+3XYIh2pNjQ==
   dependencies:
     currently-unhandled "^0.4.1"
     signal-exit "^3.0.2"
@@ -4234,10 +4298,10 @@ lru-cache@^5.1.1:
   dependencies:
     yallist "^3.0.2"
 
-lunr@^2.3.6:
-  version "2.3.6"
-  resolved 
"https://registry.yarnpkg.com/lunr/-/lunr-2.3.6.tgz#f278beee7ffd56ad86e6e478ce02ab2b98c78dd5";
-  integrity 
sha512-swStvEyDqQ85MGpABCMBclZcLI/pBIlu8FFDtmX197+oEgKloJ67QnB+Tidh0340HmLMs39c4GrkPY3cmkXp6Q==
+lunr@^2.3.8:
+  version "2.3.8"
+  resolved 
"https://registry.yarnpkg.com/lunr/-/lunr-2.3.8.tgz#a8b89c31f30b5a044b97d2d28e2da191b6ba2072";
+  integrity 
sha512-oxMeX/Y35PNFuZoHp+jUj5OSEmLCaIH4KTFJh7a93cHBoFmpw2IoPs22VIz7vyO2YUnx2Tn9dzIwO2P/4quIRg==
 
 make-dir@^1.0.0:
   version "1.3.0"
@@ -4323,9 +4387,9 @@ matchdep@^2.0.0:
     stack-trace "0.0.10"
 
 matcher@^2.0.0:
-  version "2.0.0"
-  resolved 
"https://registry.yarnpkg.com/matcher/-/matcher-2.0.0.tgz#85fe38d97670dbd2a46590cf099401e2ffb4755c";
-  integrity 
sha512-nlmfSlgHBFx36j/Pl/KQPbIaqE8Zf0TqmSMjsuddHDg6PMSVgmyW9HpkLs0o0M1n2GIZ/S2BZBLIww/xjhiGng==
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/matcher/-/matcher-2.1.0.tgz#64e1041c15b993e23b786f93320a7474bf833c28";
+  integrity 
sha512-o+nZr+vtJtgPNklyeUKkkH42OsK8WAfdgaJE2FNxcjLPg+5QbeEoT6vRj8Xq/iv18JlQ9cmKsEu0b94ixWf1YQ==
   dependencies:
     escape-string-regexp "^2.0.0"
 
@@ -4379,6 +4443,14 @@ memory-fs@^0.4.0, memory-fs@^0.4.1:
     errno "^0.1.3"
     readable-stream "^2.0.1"
 
+memory-fs@^0.5.0:
+  version "0.5.0"
+  resolved 
"https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c";
+  integrity 
sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==
+  dependencies:
+    errno "^0.1.3"
+    readable-stream "^2.0.1"
+
 meow@^5.0.0:
   version "5.0.0"
   resolved 
"https://registry.yarnpkg.com/meow/-/meow-5.0.0.tgz#dfc73d63a9afc714a5e371760eb5c88b91078aa4";
@@ -4406,10 +4478,15 @@ merge-source-map@^1.1.0:
   dependencies:
     source-map "^0.6.1"
 
-merge2@^1.2.3:
-  version "1.2.4"
-  resolved 
"https://registry.yarnpkg.com/merge2/-/merge2-1.2.4.tgz#c9269589e6885a60cf80605d9522d4b67ca646e3";
-  integrity 
sha512-FYE8xI+6pjFOhokZu0We3S5NKCirLbCzSh2Usf3qEyr4X8U+0jNg9P8RZ4qz+V2UoECLVwSyzU3LxXBaLGtD3A==
+merge-stream@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60";
+  integrity 
sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
+
+merge2@^1.2.3, merge2@^1.3.0:
+  version "1.3.0"
+  resolved 
"https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81";
+  integrity 
sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==
 
 methods@~1.1.2:
   version "1.1.2"
@@ -4451,17 +4528,17 @@ miller-rabin@^4.0.0:
     bn.js "^4.0.0"
     brorand "^1.0.1"
 
-mime-db@1.40.0:
-  version "1.40.0"
-  resolved 
"https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32";
-  integrity 
sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==
+mime-db@1.42.0:
+  version "1.42.0"
+  resolved 
"https://registry.yarnpkg.com/mime-db/-/mime-db-1.42.0.tgz#3e252907b4c7adb906597b4b65636272cf9e7bac";
+  integrity 
sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==
 
 mime-types@~2.1.24:
-  version "2.1.24"
-  resolved 
"https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81";
-  integrity 
sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==
+  version "2.1.25"
+  resolved 
"https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.25.tgz#39772d46621f93e2a80a856c53b86a62156a6437";
+  integrity 
sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==
   dependencies:
-    mime-db "1.40.0"
+    mime-db "1.42.0"
 
 mime@1.6.0:
   version "1.6.0"
@@ -4523,20 +4600,48 @@ minimist@~0.0.1:
   resolved 
"https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf";
   integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=
 
-minipass@^2.2.1, minipass@^2.3.5:
-  version "2.3.5"
-  resolved 
"https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848";
-  integrity 
sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==
+minipass-collect@^1.0.2:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617";
+  integrity 
sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==
+  dependencies:
+    minipass "^3.0.0"
+
+minipass-flush@^1.0.5:
+  version "1.0.5"
+  resolved 
"https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373";
+  integrity 
sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==
+  dependencies:
+    minipass "^3.0.0"
+
+minipass-pipeline@^1.2.2:
+  version "1.2.2"
+  resolved 
"https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.2.tgz#3dcb6bb4a546e32969c7ad710f2c79a86abba93a";
+  integrity 
sha512-3JS5A2DKhD2g0Gg8x3yamO0pj7YeKGwVlDS90pF++kxptwx/F+B//roxf9SqYil5tQo65bijy+dAuAFZmYOouA==
+  dependencies:
+    minipass "^3.0.0"
+
+minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0:
+  version "2.9.0"
+  resolved 
"https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6";
+  integrity 
sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==
   dependencies:
     safe-buffer "^5.1.2"
     yallist "^3.0.0"
 
+minipass@^3.0.0, minipass@^3.1.1:
+  version "3.1.1"
+  resolved 
"https://registry.yarnpkg.com/minipass/-/minipass-3.1.1.tgz#7607ce778472a185ad6d89082aa2070f79cedcd5";
+  integrity 
sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w==
+  dependencies:
+    yallist "^4.0.0"
+
 minizlib@^1.2.1:
-  version "1.2.1"
-  resolved 
"https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614";
-  integrity 
sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==
+  version "1.3.3"
+  resolved 
"https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d";
+  integrity 
sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==
   dependencies:
-    minipass "^2.2.1"
+    minipass "^2.9.0"
 
 mississippi@^3.0.0:
   version "3.0.0"
@@ -4652,7 +4757,7 @@ nested-error-stacks@^2.0.0:
   resolved 
"https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61";
   integrity 
sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==
 
-next-tick@^1.0.0:
+next-tick@~1.0.0:
   version "1.0.0"
   resolved 
"https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c";
   integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
@@ -4732,15 +4837,15 @@ normalize-path@^2.1.1:
   dependencies:
     remove-trailing-separator "^1.0.1"
 
-normalize-path@^3.0.0:
+normalize-path@^3.0.0, normalize-path@~3.0.0:
   version "3.0.0"
   resolved 
"https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65";
   integrity 
sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
 
 normalize-url@^4.1.0:
-  version "4.3.0"
-  resolved 
"https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.3.0.tgz#9c49e10fc1876aeb76dba88bf1b2b5d9fa57b2ee";
-  integrity 
sha512-0NLtR71o4k6GLP+mr6Ty34c5GA6CMoEsncKJxvQd8NzPxaHRJNnb5gZE8R1XF4CPIS7QPHLJ74IFszwtNVAHVQ==
+  version "4.5.0"
+  resolved 
"https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129";
+  integrity 
sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
 
 now-and-later@^2.0.0:
   version "2.0.1"
@@ -4755,9 +4860,9 @@ npm-bundled@^1.0.1:
   integrity 
sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==
 
 npm-packlist@^1.1.6:
-  version "1.4.4"
-  resolved 
"https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.4.tgz#866224233850ac534b63d1a6e76050092b5d2f44";
-  integrity 
sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw==
+  version "1.4.6"
+  resolved 
"https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.6.tgz#53ba3ed11f8523079f1457376dd379ee4ea42ff4";
+  integrity 
sha512-u65uQdb+qwtGvEJh/DgQgW1Xg7sqeNbmxYyrvlNznaVTjV3E5P6F/EFjM+BVHXl7JJlsdG8A64M0XI8FI/IOlg==
   dependencies:
     ignore-walk "^3.0.1"
     npm-bundled "^1.0.1"
@@ -4829,7 +4934,12 @@ object-copy@^0.1.0:
     define-property "^0.2.5"
     kind-of "^3.0.3"
 
-object-keys@^1.0.11, object-keys@^1.0.12:
+object-is@^1.0.1:
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/object-is/-/object-is-1.0.1.tgz#0aa60ec9989a0b3ed795cf4d06f62cf1ad6539b6";
+  integrity sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=
+
+object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1:
   version "1.1.1"
   resolved 
"https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e";
   integrity 
sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
@@ -5051,6 +5161,13 @@ p-map@^2.0.0:
   resolved 
"https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175";
   integrity 
sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==
 
+p-map@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d";
+  integrity 
sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==
+  dependencies:
+    aggregate-error "^3.0.0"
+
 p-try@^1.0.0:
   version "1.0.0"
   resolved 
"https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3";
@@ -5097,18 +5214,18 @@ pako@~1.0.5:
   integrity 
sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==
 
 parallel-transform@^1.1.0:
-  version "1.1.0"
-  resolved 
"https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06";
-  integrity sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=
+  version "1.2.0"
+  resolved 
"https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc";
+  integrity 
sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==
   dependencies:
-    cyclist "~0.2.2"
+    cyclist "^1.0.1"
     inherits "^2.0.3"
     readable-stream "^2.1.5"
 
 parse-asn1@^5.0.0:
-  version "5.1.4"
-  resolved 
"https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.4.tgz#37f6628f823fbdeb2273b4d540434a22f3ef1fcc";
-  integrity 
sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==
+  version "5.1.5"
+  resolved 
"https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz#003271343da58dc94cace494faef3d2147ecea0e";
+  integrity 
sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==
   dependencies:
     asn1.js "^4.0.0"
     browserify-aes "^1.0.0"
@@ -5263,9 +5380,9 @@ pbkdf2@^3.0.3:
     sha.js "^2.4.8"
 
 picomatch@^2.0.4, picomatch@^2.0.5:
-  version "2.0.7"
-  resolved 
"https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6";
-  integrity 
sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==
+  version "2.1.1"
+  resolved 
"https://registry.yarnpkg.com/picomatch/-/picomatch-2.1.1.tgz#ecdfbea7704adb5fe6fb47f9866c4c0e15e905c5";
+  integrity 
sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA==
 
 pify@^2.0.0:
   version "2.3.0"
@@ -5309,7 +5426,7 @@ pkg-dir@^3.0.0:
   dependencies:
     find-up "^3.0.0"
 
-pkg-dir@^4.2.0:
+pkg-dir@^4.1.0, pkg-dir@^4.2.0:
   version "4.2.0"
   resolved 
"https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3";
   integrity 
sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
@@ -5360,9 +5477,9 @@ prepend-http@^2.0.0:
   integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
 
 prettier@^1.18.2:
-  version "1.18.2"
-  resolved 
"https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea";
-  integrity 
sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==
+  version "1.19.1"
+  resolved 
"https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb";
+  integrity 
sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
 
 pretty-hrtime@^1.0.0:
   version "1.0.3"
@@ -5483,9 +5600,9 @@ punycode@^2.1.0:
   integrity 
sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
 
 qrcode-generator@^1.4.3:
-  version "1.4.3"
-  resolved 
"https://registry.yarnpkg.com/qrcode-generator/-/qrcode-generator-1.4.3.tgz#4876e8f280e65b6c94615f4c19c484f6b964b199";
-  integrity 
sha512-++rVRvMRq5BlHfmAafl8a4ppUntzUxCCUTT2t0siUgqKwdnqRzY8IH6f6WSX5dZUhD2Ul5/MIKuTJddflwrGzw==
+  version "1.4.4"
+  resolved 
"https://registry.yarnpkg.com/qrcode-generator/-/qrcode-generator-1.4.4.tgz#63f771224854759329a99048806a53ed278740e7";
+  integrity 
sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw==
 
 qs@6.7.0:
   version "6.7.0"
@@ -5548,24 +5665,24 @@ rc@^1.2.7, rc@^1.2.8:
     strip-json-comments "~2.0.1"
 
 react-dom@^16.8.5:
-  version "16.9.0"
-  resolved 
"https://registry.yarnpkg.com/react-dom/-/react-dom-16.9.0.tgz#5e65527a5e26f22ae3701131bcccaee9fb0d3962";
-  integrity 
sha512-YFT2rxO9hM70ewk9jq0y6sQk8cL02xm4+IzYBz75CQGlClQQ1Bxq0nhHF6OtSbit+AIahujJgb/CPRibFkMNJQ==
+  version "16.12.0"
+  resolved 
"https://registry.yarnpkg.com/react-dom/-/react-dom-16.12.0.tgz#0da4b714b8d13c2038c9396b54a92baea633fe11";
+  integrity 
sha512-LMxFfAGrcS3kETtQaCkTKjMiifahaMySFDn71fZUNpPHZQEzmk/GiAeIT8JSOrHB23fnuCOMruL2a8NYlw+8Gw==
   dependencies:
     loose-envify "^1.1.0"
     object-assign "^4.1.1"
     prop-types "^15.6.2"
-    scheduler "^0.15.0"
+    scheduler "^0.18.0"
 
 react-is@^16.8.1:
-  version "16.9.0"
-  resolved 
"https://registry.yarnpkg.com/react-is/-/react-is-16.9.0.tgz#21ca9561399aad0ff1a7701c01683e8ca981edcb";
-  integrity 
sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==
+  version "16.12.0"
+  resolved 
"https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c";
+  integrity 
sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==
 
 react@^16.8.5:
-  version "16.9.0"
-  resolved 
"https://registry.yarnpkg.com/react/-/react-16.9.0.tgz#40ba2f9af13bc1a38d75dbf2f4359a5185c4f7aa";
-  integrity 
sha512-+7LQnFBwkiw+BobzOF6N//BdoNw0ouwmSJTEm9cglOOmsg/TMiFHZLe2sEoN5M7LgJTj9oHH0gxklfnQe66S1w==
+  version "16.12.0"
+  resolved 
"https://registry.yarnpkg.com/react/-/react-16.12.0.tgz#0c0a9c6a142429e3614834d5a778e18aa78a0b83";
+  integrity 
sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA==
   dependencies:
     loose-envify "^1.1.0"
     object-assign "^4.1.1"
@@ -5644,10 +5761,10 @@ readdirp@^2.2.1:
     micromatch "^3.1.10"
     readable-stream "^2.0.2"
 
-readdirp@^3.1.1:
-  version "3.1.2"
-  resolved 
"https://registry.yarnpkg.com/readdirp/-/readdirp-3.1.2.tgz#fa85d2d14d4289920e4671dead96431add2ee78a";
-  integrity 
sha512-8rhl0xs2cxfVsqzreYCvs8EwBfn/DhVdqtoLmw19uI3SC5avYX9teCurlErfpPXGmYtMHReGaP2RsLnFvz/lnw==
+readdirp@~3.2.0:
+  version "3.2.0"
+  resolved 
"https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839";
+  integrity 
sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==
   dependencies:
     picomatch "^2.0.4"
 
@@ -5686,10 +5803,17 @@ regex-not@^1.0.0, regex-not@^1.0.2:
     extend-shallow "^3.0.2"
     safe-regex "^1.1.0"
 
-regexpu-core@^4.5.4:
-  version "4.5.5"
-  resolved 
"https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.5.5.tgz#aaffe61c2af58269b3e516b61a73790376326411";
-  integrity 
sha512-FpI67+ky9J+cDizQUJlIlNZFKual/lUkFr1AG6zOCpwZ9cLrg8UUVakyUQJD7fCDIe9Z2nwTQJNPyonatNmDFQ==
+regexp.prototype.flags@^1.2.0:
+  version "1.2.0"
+  resolved 
"https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz#6b30724e306a27833eeb171b66ac8890ba37e41c";
+  integrity 
sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA==
+  dependencies:
+    define-properties "^1.1.2"
+
+regexpu-core@^4.6.0:
+  version "4.6.0"
+  resolved 
"https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.6.0.tgz#2037c18b327cfce8a6fea2a4ec441f2432afb8b6";
+  integrity 
sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==
   dependencies:
     regenerate "^1.4.0"
     regenerate-unicode-properties "^8.1.0"
@@ -5714,9 +5838,9 @@ registry-url@^5.0.0:
     rc "^1.2.8"
 
 regjsgen@^0.5.0:
-  version "0.5.0"
-  resolved 
"https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.0.tgz#a7634dc08f89209c2049adda3525711fb97265dd";
-  integrity 
sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==
+  version "0.5.1"
+  resolved 
"https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c";
+  integrity 
sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==
 
 regjsparser@^0.6.0:
   version "0.6.0"
@@ -5887,7 +6011,7 @@ reusify@^1.0.0:
   resolved 
"https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76";
   integrity 
sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
 
-rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3:
+rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@^2.7.1:
   version "2.7.1"
   resolved 
"https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec";
   integrity 
sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
@@ -5941,10 +6065,10 @@ sax@^1.2.4:
   resolved 
"https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9";
   integrity 
sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
 
-scheduler@^0.15.0:
-  version "0.15.0"
-  resolved 
"https://registry.yarnpkg.com/scheduler/-/scheduler-0.15.0.tgz#6bfcf80ff850b280fed4aeecc6513bc0b4f17f8e";
-  integrity 
sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg==
+scheduler@^0.18.0:
+  version "0.18.0"
+  resolved 
"https://registry.yarnpkg.com/scheduler/-/scheduler-0.18.0.tgz#5901ad6659bc1d8f3fdaf36eb7a67b0d6746b1c4";
+  integrity 
sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ==
   dependencies:
     loose-envify "^1.1.0"
     object-assign "^4.1.1"
@@ -5958,6 +6082,14 @@ schema-utils@^1.0.0:
     ajv-errors "^1.0.0"
     ajv-keywords "^3.1.0"
 
+schema-utils@^2.5.0:
+  version "2.5.0"
+  resolved 
"https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.5.0.tgz#8f254f618d402cc80257486213c8970edfd7c22f";
+  integrity 
sha512-32ISrwW2scPXHUSusP8qMg5dLUawKkyV+/qIEV9JdXKx+rsM6mi8vZY8khg2M69Qom16rtroWXD3Ybtiws38gQ==
+  dependencies:
+    ajv "^6.10.2"
+    ajv-keywords "^3.4.1"
+
 semver-diff@^2.0.0:
   version "2.1.0"
   resolved 
"https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36";
@@ -6007,9 +6139,14 @@ serialize-error@^2.1.0:
   integrity sha1-ULZ51WNc34Rme9yOWa9OW4HV9go=
 
 serialize-javascript@^1.7.0:
-  version "1.7.0"
-  resolved 
"https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.7.0.tgz#d6e0dfb2a3832a8c94468e6eb1db97e55a192a65";
-  integrity 
sha512-ke8UG8ulpFOxO8f8gRYabHQe/ZntKlcig2Mp+8+URDP1D8vJZ0KUt7LYo07q25Z/+JVSgpr/cui9PIp5H6/+nA==
+  version "1.9.1"
+  resolved 
"https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.9.1.tgz#cfc200aef77b600c47da9bb8149c943e798c2fdb";
+  integrity 
sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==
+
+serialize-javascript@^2.1.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.0.tgz#9310276819efd0eb128258bb341957f6eb2fc570";
+  integrity 
sha512-a/mxFfU00QT88umAJQsNWOnUKckhNCqOl028N48e7wFmo2/EHpTo9Wso+iJJCMrQnmFvcjto5RJdAHEvVhcyUQ==
 
 serve-static@1.14.1:
   version "1.14.1"
@@ -6141,9 +6278,9 @@ source-map-resolve@^0.5.0:
     urix "^0.1.0"
 
 source-map-support@^0.5.12, source-map-support@^0.5.13, 
source-map-support@^0.5.3, source-map-support@~0.5.12:
-  version "0.5.13"
-  resolved 
"https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932";
-  integrity 
sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==
+  version "0.5.16"
+  resolved 
"https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042";
+  integrity 
sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==
   dependencies:
     buffer-from "^1.0.0"
     source-map "^0.6.0"
@@ -6169,9 +6306,9 @@ sparkles@^1.0.0:
   integrity 
sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==
 
 spawn-wrap@^1.4.2:
-  version "1.4.2"
-  resolved 
"https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-1.4.2.tgz#cff58e73a8224617b6561abdc32586ea0c82248c";
-  integrity 
sha512-vMwR3OmmDhnxCVxM8M+xO/FtIp6Ju/mNaDfCMMW7FDcLRTPFWUswec4LXJHTJE2hwTI9O0YBfygu4DalFl7Ylg==
+  version "1.4.3"
+  resolved 
"https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-1.4.3.tgz#81b7670e170cca247d80bf5faf0cfb713bdcf848";
+  integrity 
sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==
   dependencies:
     foreground-child "^1.5.6"
     mkdirp "^0.5.0"
@@ -6225,6 +6362,14 @@ ssri@^6.0.1:
   dependencies:
     figgy-pudding "^3.5.1"
 
+ssri@^7.0.0:
+  version "7.1.0"
+  resolved 
"https://registry.yarnpkg.com/ssri/-/ssri-7.1.0.tgz#92c241bf6de82365b5c7fb4bd76e975522e1294d";
+  integrity 
sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g==
+  dependencies:
+    figgy-pudding "^3.5.1"
+    minipass "^3.1.1"
+
 stack-trace@0.0.10:
   version "0.0.10"
   resolved 
"https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0";
@@ -6319,13 +6464,13 @@ string-width@^3.0.0, string-width@^3.1.0:
     strip-ansi "^5.1.0"
 
 string-width@^4.1.0:
-  version "4.1.0"
-  resolved 
"https://registry.yarnpkg.com/string-width/-/string-width-4.1.0.tgz#ba846d1daa97c3c596155308063e075ed1c99aff";
-  integrity 
sha512-NrX+1dVVh+6Y9dnQ19pR0pP4FiEIlUvdTGn8pw6CKTNq5sgib2nIhmUNT5TAmhWmvKr3WcxBcP3E8nWezuipuQ==
+  version "4.2.0"
+  resolved 
"https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5";
+  integrity 
sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==
   dependencies:
     emoji-regex "^8.0.0"
     is-fullwidth-code-point "^3.0.0"
-    strip-ansi "^5.2.0"
+    strip-ansi "^6.0.0"
 
 string_decoder@^1.0.0, string_decoder@^1.1.1:
   version "1.3.0"
@@ -6362,6 +6507,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
   dependencies:
     ansi-regex "^4.1.0"
 
+strip-ansi@^6.0.0:
+  version "6.0.0"
+  resolved 
"https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532";
+  integrity 
sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==
+  dependencies:
+    ansi-regex "^5.0.0"
+
 strip-bom-buf@^2.0.0:
   version "2.0.0"
   resolved 
"https://registry.yarnpkg.com/strip-bom-buf/-/strip-bom-buf-2.0.0.tgz#ff9c223937f8e7154b77e9de9bde094186885c15";
@@ -6427,9 +6579,9 @@ supports-color@^5.3.0:
     has-flag "^3.0.0"
 
 supports-color@^7.0.0:
-  version "7.0.0"
-  resolved 
"https://registry.yarnpkg.com/supports-color/-/supports-color-7.0.0.tgz#f2392c50ab35bb3cae7beebf24d254a19f880c06";
-  integrity 
sha512-WRt32iTpYEZWYOpcetGm0NPeSvaebccx7hhS/5M6sAiqnhedtFCHFxkjzZlJvFNCPowiKSFGiZk5USQDFy83vQ==
+  version "7.1.0"
+  resolved 
"https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1";
+  integrity 
sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==
   dependencies:
     has-flag "^4.0.0"
 
@@ -6463,13 +6615,13 @@ tar-stream@^2.1.0:
     readable-stream "^3.1.1"
 
 tar@^4:
-  version "4.4.10"
-  resolved 
"https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1";
-  integrity 
sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==
+  version "4.4.13"
+  resolved 
"https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525";
+  integrity 
sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==
   dependencies:
     chownr "^1.1.1"
     fs-minipass "^1.2.5"
-    minipass "^2.3.5"
+    minipass "^2.8.6"
     minizlib "^1.2.1"
     mkdirp "^0.5.0"
     safe-buffer "^5.1.2"
@@ -6482,7 +6634,7 @@ term-size@^1.2.0:
   dependencies:
     execa "^0.7.0"
 
-terser-webpack-plugin@^1.2.3, terser-webpack-plugin@^1.4.1:
+terser-webpack-plugin@^1.4.1:
   version "1.4.1"
   resolved 
"https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz#61b18e40eaee5be97e771cdbb10ed1280888c2b4";
   integrity 
sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg==
@@ -6497,10 +6649,24 @@ terser-webpack-plugin@^1.2.3, 
terser-webpack-plugin@^1.4.1:
     webpack-sources "^1.4.0"
     worker-farm "^1.7.0"
 
-terser@^4.1.2:
-  version "4.2.0"
-  resolved 
"https://registry.yarnpkg.com/terser/-/terser-4.2.0.tgz#4b1b5f4424b426a7a47e80d6aae45e0d7979aef0";
-  integrity 
sha512-6lPt7lZdZ/13icQJp8XasFOwZjFJkxFFIb/N1fhYEQNoNI3Ilo3KABZ9OocZvZoB39r6SiIk/0+v/bt8nZoSeA==
+terser-webpack-plugin@^2.2.1:
+  version "2.2.1"
+  resolved 
"https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-2.2.1.tgz#5569e6c7d8be79e5e43d6da23acc3b6ba77d22bd";
+  integrity 
sha512-jwdauV5Al7zopR6OAYvIIRcxXCSvLjZjr7uZE8l2tIWb/ryrGN48sJftqGf5k9z09tWhajx53ldp0XPI080YnA==
+  dependencies:
+    cacache "^13.0.1"
+    find-cache-dir "^3.0.0"
+    jest-worker "^24.9.0"
+    schema-utils "^2.5.0"
+    serialize-javascript "^2.1.0"
+    source-map "^0.6.1"
+    terser "^4.3.9"
+    webpack-sources "^1.4.3"
+
+terser@^4.1.2, terser@^4.3.9:
+  version "4.4.0"
+  resolved 
"https://registry.yarnpkg.com/terser/-/terser-4.4.0.tgz#22c46b4817cf4c9565434bfe6ad47336af259ac3";
+  integrity 
sha512-oDG16n2WKm27JO8h4y/w3iqBGAOSCtq7k8dRmrn4Wf9NouL0b2WpMHGChFGZq4nFAQy1FsNJrVQHfurXOSTmOA==
   dependencies:
     commander "^2.20.0"
     source-map "~0.6.1"
@@ -6649,15 +6815,15 @@ tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0:
   integrity 
sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
 
 tslint@^5.19.0:
-  version "5.19.0"
-  resolved 
"https://registry.yarnpkg.com/tslint/-/tslint-5.19.0.tgz#a2cbd4a7699386da823f6b499b8394d6c47bb968";
-  integrity 
sha512-1LwwtBxfRJZnUvoS9c0uj8XQtAnyhWr9KlNvDIdB+oXyT+VpsOAaEhEgKi1HrZ8rq0ki/AAnbGSv4KM6/AfVZw==
+  version "5.20.1"
+  resolved 
"https://registry.yarnpkg.com/tslint/-/tslint-5.20.1.tgz#e401e8aeda0152bc44dd07e614034f3f80c67b7d";
+  integrity 
sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==
   dependencies:
     "@babel/code-frame" "^7.0.0"
     builtin-modules "^1.1.1"
     chalk "^2.3.0"
     commander "^2.12.1"
-    diff "^3.2.0"
+    diff "^4.0.1"
     glob "^7.1.1"
     js-yaml "^3.13.1"
     minimatch "^3.0.4"
@@ -6684,10 +6850,10 @@ type-fest@^0.3.0:
   resolved 
"https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1";
   integrity 
sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==
 
-type-fest@^0.5.2:
-  version "0.5.2"
-  resolved 
"https://registry.yarnpkg.com/type-fest/-/type-fest-0.5.2.tgz#d6ef42a0356c6cd45f49485c3b6281fc148e48a2";
-  integrity 
sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw==
+type-fest@^0.8.0, type-fest@^0.8.1:
+  version "0.8.1"
+  resolved 
"https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d";
+  integrity 
sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
 
 type-is@~1.6.17, type-is@~1.6.18:
   version "1.6.18"
@@ -6698,9 +6864,14 @@ type-is@~1.6.17, type-is@~1.6.18:
     mime-types "~2.1.24"
 
 type@^1.0.1:
-  version "1.0.3"
-  resolved 
"https://registry.yarnpkg.com/type/-/type-1.0.3.tgz#16f5d39f27a2d28d86e48f8981859e9d3296c179";
-  integrity 
sha512-51IMtNfVcee8+9GJvj0spSuFcZHe9vSib6Xtgsny1Km9ugyz2mbS08I3rsUIRYgJohFRFU1160sgRodYz378Hg==
+  version "1.2.0"
+  resolved 
"https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0";
+  integrity 
sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==
+
+type@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/type/-/type-2.0.0.tgz#5f16ff6ef2eb44f260494dae271033b29c09a9c3";
+  integrity 
sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==
 
 typedarray-to-buffer@^3.1.5:
   version "3.1.5"
@@ -6714,49 +6885,44 @@ typedarray@^0.0.6:
   resolved 
"https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777";
   integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
 
-typedoc-default-themes@^0.6.0:
-  version "0.6.0"
-  resolved 
"https://registry.yarnpkg.com/typedoc-default-themes/-/typedoc-default-themes-0.6.0.tgz#7e73bf54dd9e11550dd0fb576d5176b758f8f8b5";
-  integrity 
sha512-MdTROOojxod78CEv22rIA69o7crMPLnVZPefuDLt/WepXqJwgiSu8Xxq+H36x0Jj3YGc7lOglI2vPJ2GhoOybw==
+typedoc-default-themes@^0.6.1:
+  version "0.6.1"
+  resolved 
"https://registry.yarnpkg.com/typedoc-default-themes/-/typedoc-default-themes-0.6.1.tgz#e2e471188983df995f4f9df49f713044fced6802";
+  integrity 
sha512-z5AWKqQDz7igl9WkUuafx8cEm4MPVQGMpbWE+3lwVOaq+U4UoLKBMnpFQWh/4fqQ3bGysXpOstMxy2OOzHezyw==
   dependencies:
     backbone "^1.4.0"
     jquery "^3.4.1"
-    lunr "^2.3.6"
+    lunr "^2.3.8"
     underscore "^1.9.1"
 
 typedoc@^0.15.0:
-  version "0.15.0"
-  resolved 
"https://registry.yarnpkg.com/typedoc/-/typedoc-0.15.0.tgz#21eaf4db41cf2797bad027a74f2a75cd08ae0c2d";
-  integrity 
sha512-NOtfq5Tis4EFt+J2ozhVq9RCeUnfEYMFKoU6nCXCXUULJz1UQynOM+yH3TkfZCPLzigbqB0tQYGVlktUWweKlw==
+  version "0.15.2"
+  resolved 
"https://registry.yarnpkg.com/typedoc/-/typedoc-0.15.2.tgz#f0f79abd12e8b9785f96ce205e2dadf282c32cf4";
+  integrity 
sha512-K2nFEtyDQTVdXOzYtECw3TwuT3lM91Zc0dzGSLuor5R8qzZbwqBoCw7xYGVBow6+mEZAvKGznLFsl7FzG+wAgQ==
   dependencies:
     "@types/minimatch" "3.0.3"
     fs-extra "^8.1.0"
-    handlebars "^4.1.2"
-    highlight.js "^9.15.8"
+    handlebars "^4.5.1"
+    highlight.js "^9.16.2"
     lodash "^4.17.15"
     marked "^0.7.0"
     minimatch "^3.0.0"
     progress "^2.0.3"
     shelljs "^0.8.3"
-    typedoc-default-themes "^0.6.0"
-    typescript "3.5.x"
-
-typescript@3.5.x:
-  version "3.5.3"
-  resolved 
"https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977";
-  integrity 
sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
+    typedoc-default-themes "^0.6.1"
+    typescript "3.7.x"
 
-typescript@^3.7.2:
+typescript@3.7.x, typescript@^3.7.2:
   version "3.7.2"
   resolved 
"https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb";
   integrity 
sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ==
 
 uglify-js@^3.0.27, uglify-js@^3.1.4:
-  version "3.6.0"
-  resolved 
"https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.0.tgz#704681345c53a8b2079fb6cec294b05ead242ff5";
-  integrity 
sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==
+  version "3.6.9"
+  resolved 
"https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.9.tgz#85d353edb6ddfb62a9d798f36e91792249320611";
+  integrity 
sha512-pcnnhaoG6RtrvHJ1dFncAe8Od6Nuy30oaJ82ts6//sGSXOP5UjBMEthiProjXmMNHOfd93sqlkztifFMcb+4yw==
   dependencies:
-    commander "~2.20.0"
+    commander "~2.20.3"
     source-map "~0.6.1"
 
 uid2@0.0.3:
@@ -6884,9 +7050,9 @@ unset-value@^1.0.0:
     isobject "^3.0.0"
 
 upath@^1.1.1:
-  version "1.1.2"
-  resolved 
"https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068";
-  integrity 
sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==
+  version "1.2.0"
+  resolved 
"https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894";
+  integrity 
sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==
 
 update-notifier@^3.0.1:
   version "3.0.1"
@@ -6914,9 +7080,9 @@ uri-js@^4.2.2:
     punycode "^2.1.0"
 
 urijs@^1.18.10:
-  version "1.19.1"
-  resolved 
"https://registry.yarnpkg.com/urijs/-/urijs-1.19.1.tgz#5b0ff530c0cbde8386f6342235ba5ca6e995d25a";
-  integrity 
sha512-xVrGVi94ueCJNrBSTjWqjvtgvl3cyOTThp2zaMaFNGp3F542TR6sM3f2o8RqZl+AwteClSVmoCyt0ka4RjQOQg==
+  version "1.19.2"
+  resolved 
"https://registry.yarnpkg.com/urijs/-/urijs-1.19.2.tgz#f9be09f00c4c5134b7cb3cf475c1dd394526265a";
+  integrity 
sha512-s/UIq9ap4JPZ7H1EB5ULo/aOUbWqfDi7FKzMC2Nz+0Si8GiT1rIEaprt8hy3Vy2Ex2aJPpOQv4P4DuOZ+K1c6w==
 
 urix@^0.1.0:
   version "0.1.0"
@@ -7051,9 +7217,9 @@ vinyl@^2.0.0, vinyl@^2.1.0, vinyl@^2.2.0:
     replace-ext "^1.0.0"
 
 vm-browserify@^1.0.1:
-  version "1.1.0"
-  resolved 
"https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.0.tgz#bd76d6a23323e2ca8ffa12028dc04559c75f9019";
-  integrity 
sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==
+  version "1.1.2"
+  resolved 
"https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0";
+  integrity 
sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
 
 watchpack@^1.6.0:
   version "1.6.0"
@@ -7072,9 +7238,9 @@ wcwidth@^1.0.1:
     defaults "^1.0.3"
 
 webpack-bundle-analyzer@^3.0.2:
-  version "3.4.1"
-  resolved 
"https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.4.1.tgz#430544c7ba1631baccf673475ca8300cb74a3c47";
-  integrity 
sha512-Bs8D/1zF+17lhqj2OYmzi7HEVYqEVxu7lCO9Ff8BwajenOU0vAwEoV8e4ICCPNZAcqR1PCR/7o2SkW+cnCmF0A==
+  version "3.6.0"
+  resolved 
"https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.6.0.tgz#39b3a8f829ca044682bc6f9e011c95deb554aefd";
+  integrity 
sha512-orUfvVYEfBMDXgEKAKVvab5iQ2wXneIEorGNsyuOyVYpjYrI7CUOhhXNDd3huMwQ3vNNWWlGP+hzflMFYNzi2g==
   dependencies:
     acorn "^6.0.7"
     acorn-walk "^6.1.1"
@@ -7091,9 +7257,9 @@ webpack-bundle-analyzer@^3.0.2:
     ws "^6.0.0"
 
 webpack-cli@^3.1.0:
-  version "3.3.7"
-  resolved 
"https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.7.tgz#77c8580dd8e92f69d635e0238eaf9d9c15759a91";
-  integrity 
sha512-OhTUCttAsr+IZSMVwGROGRHvT+QAs8H6/mHIl4SvhAwYywjiylYjpwybGx7WQ9Hkb45FhjtsymkwiRRbGJ1SZQ==
+  version "3.3.10"
+  resolved 
"https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.10.tgz#17b279267e9b4fb549023fae170da8e6e766da13";
+  integrity 
sha512-u1dgND9+MXaEt74sJR4PR7qkPxXUSQ0RXYq8x1L6Jg1MYVEmGPrH6Ah6C4arD4r0J1P5HKjRqpab36k0eIzPqg==
   dependencies:
     chalk "2.4.2"
     cross-spawn "6.0.5"
@@ -7124,7 +7290,7 @@ webpack-merge@^4.2.2:
   dependencies:
     lodash "^4.17.15"
 
-webpack-sources@^1.4.0, webpack-sources@^1.4.1:
+webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3:
   version "1.4.3"
   resolved 
"https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933";
   integrity 
sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==
@@ -7133,9 +7299,9 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1:
     source-map "~0.6.1"
 
 webpack@^4.39.3:
-  version "4.39.3"
-  resolved 
"https://registry.yarnpkg.com/webpack/-/webpack-4.39.3.tgz#a02179d1032156b713b6ec2da7e0df9d037def50";
-  integrity 
sha512-BXSI9M211JyCVc3JxHWDpze85CvjC842EvpRsVTc/d15YJGlox7GIDd38kJgWrb3ZluyvIjgenbLDMBQPDcxYQ==
+  version "4.41.2"
+  resolved 
"https://registry.yarnpkg.com/webpack/-/webpack-4.41.2.tgz#c34ec76daa3a8468c9b61a50336d8e3303dce74e";
+  integrity 
sha512-Zhw69edTGfbz9/8JJoyRQ/pq8FYUoY0diOXqW0T6yhgdhCv6wr0hra5DwwWexNRns2Z2+gsnrNcbe9hbGBgk/A==
   dependencies:
     "@webassemblyjs/ast" "1.8.5"
     "@webassemblyjs/helper-module-context" "1.8.5"
@@ -7241,9 +7407,9 @@ write-file-atomic@^2.0.0, write-file-atomic@^2.4.2:
     signal-exit "^3.0.2"
 
 write-file-atomic@^3.0.0:
-  version "3.0.0"
-  resolved 
"https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.0.tgz#1b64dbbf77cb58fd09056963d63e62667ab4fb21";
-  integrity 
sha512-EIgkf60l2oWsffja2Sf2AL384dx328c0B+cIYPTQq5q2rOYuDV00/iPFBOUiDKKwKMOhkymH8AidPaRvzfxY+Q==
+  version "3.0.1"
+  resolved 
"https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.1.tgz#558328352e673b5bb192cf86500d60b230667d4b";
+  integrity 
sha512-JPStrIyyVJ6oCSz/691fAjFtefZ6q+fP6tm+OS4Qw6o+TGQxNp1ziY2PgS+X/m0V8OWhZiO/m4xSj+Pr4RrZvw==
   dependencies:
     imurmurhash "^0.1.4"
     is-typedarray "^1.0.0"
@@ -7283,9 +7449,14 @@ yallist@^2.1.2:
   integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
 
 yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3:
-  version "3.0.3"
-  resolved 
"https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9";
-  integrity 
sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==
+  version "3.1.1"
+  resolved 
"https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd";
+  integrity 
sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
+
+yallist@^4.0.0:
+  version "4.0.0"
+  resolved 
"https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72";
+  integrity 
sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
 
 yargs-parser@^10.0.0:
   version "10.1.0"

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]