gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-wallet-webex] branch master updated (65eb8b96 -> bf5


From: gnunet
Subject: [GNUnet-SVN] [taler-wallet-webex] branch master updated (65eb8b96 -> bf52d4b8)
Date: Tue, 25 Jun 2019 14:31:55 +0200

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

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

    from 65eb8b96 bump version
     new 2ee9431f idb wip
     new a4e4125c idb: tests working
     new 859a9e72 idb: more tests working
     new c84361d3 idb: memory backend fixes
     new b3fc710d idb: make unique cursor work
     new df561923 idb: extend test case
     new bf52d4b8 idb: make test pass

The 7 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:
 packages/idb-bridge/.prettierrc                    |    6 +
 packages/idb-bridge/README.md                      |   18 +
 packages/idb-bridge/package.json                   |   19 +
 packages/idb-bridge/src/BridgeIDBCursor.ts         |  345 ++
 .../idb-bridge/src/BridgeIDBCursorWithValue.ts     |   47 +
 packages/idb-bridge/src/BridgeIDBDatabase.ts       |  240 ++
 packages/idb-bridge/src/BridgeIDBFactory.ts        |  203 ++
 packages/idb-bridge/src/BridgeIDBIndex.ts          |  315 ++
 packages/idb-bridge/src/BridgeIDBKeyRange.ts       |  133 +
 packages/idb-bridge/src/BridgeIDBObjectStore.ts    |  445 +++
 packages/idb-bridge/src/BridgeIDBOpenDBRequest.ts  |   36 +
 packages/idb-bridge/src/BridgeIDBRequest.ts        |   86 +
 packages/idb-bridge/src/BridgeIDBTransaction.ts    |  333 ++
 .../idb-bridge/src/BridgeIDBVersionChangeEvent.ts  |   41 +
 packages/idb-bridge/src/MemoryBackend.test.ts      |  237 ++
 packages/idb-bridge/src/MemoryBackend.ts           | 1156 ++++++
 packages/idb-bridge/src/backend-interface.ts       |  174 +
 packages/idb-bridge/src/tree/b+tree.ts             | 1351 +++++++
 packages/idb-bridge/src/tree/interfaces.ts         |  329 ++
 packages/idb-bridge/src/util/FakeEvent.ts          |   80 +
 packages/idb-bridge/src/util/FakeEventTarget.ts    |  185 +
 packages/idb-bridge/src/util/canInjectKey.ts       |   34 +
 packages/idb-bridge/src/util/cmp.ts                |  108 +
 packages/idb-bridge/src/util/deepEquals.ts         |   75 +
 packages/idb-bridge/src/util/enforceRange.ts       |   18 +
 packages/idb-bridge/src/util/errors.ts             |  120 +
 packages/idb-bridge/src/util/extractKey.ts         |   55 +
 packages/idb-bridge/src/util/fakeDOMStringList.ts  |   37 +
 packages/idb-bridge/src/util/getIndexKeys.test.ts  |   24 +
 packages/idb-bridge/src/util/getIndexKeys.ts       |   28 +
 packages/idb-bridge/src/util/injectKey.ts          |   48 +
 .../idb-bridge/src/util/makeStoreKeyValue.test.ts  |   42 +
 packages/idb-bridge/src/util/makeStoreKeyValue.ts  |  104 +
 packages/idb-bridge/src/util/openPromise.ts        |   22 +
 packages/idb-bridge/src/util/queueTask.ts          |   21 +
 packages/idb-bridge/src/util/structuredClone.ts    |   15 +
 packages/idb-bridge/src/util/types.ts              |   73 +
 packages/idb-bridge/src/util/validateKeyPath.ts    |   77 +
 packages/idb-bridge/src/util/valueToKey.ts         |   70 +
 packages/idb-bridge/tsconfig.json                  |   15 +
 packages/idb-bridge/yarn.lock                      | 3841 ++++++++++++++++++++
 41 files changed, 10606 insertions(+)
 create mode 100644 packages/idb-bridge/.prettierrc
 create mode 100644 packages/idb-bridge/README.md
 create mode 100644 packages/idb-bridge/package.json
 create mode 100644 packages/idb-bridge/src/BridgeIDBCursor.ts
 create mode 100644 packages/idb-bridge/src/BridgeIDBCursorWithValue.ts
 create mode 100644 packages/idb-bridge/src/BridgeIDBDatabase.ts
 create mode 100644 packages/idb-bridge/src/BridgeIDBFactory.ts
 create mode 100644 packages/idb-bridge/src/BridgeIDBIndex.ts
 create mode 100644 packages/idb-bridge/src/BridgeIDBKeyRange.ts
 create mode 100644 packages/idb-bridge/src/BridgeIDBObjectStore.ts
 create mode 100644 packages/idb-bridge/src/BridgeIDBOpenDBRequest.ts
 create mode 100644 packages/idb-bridge/src/BridgeIDBRequest.ts
 create mode 100644 packages/idb-bridge/src/BridgeIDBTransaction.ts
 create mode 100644 packages/idb-bridge/src/BridgeIDBVersionChangeEvent.ts
 create mode 100644 packages/idb-bridge/src/MemoryBackend.test.ts
 create mode 100644 packages/idb-bridge/src/MemoryBackend.ts
 create mode 100644 packages/idb-bridge/src/backend-interface.ts
 create mode 100644 packages/idb-bridge/src/tree/b+tree.ts
 create mode 100644 packages/idb-bridge/src/tree/interfaces.ts
 create mode 100644 packages/idb-bridge/src/util/FakeEvent.ts
 create mode 100644 packages/idb-bridge/src/util/FakeEventTarget.ts
 create mode 100644 packages/idb-bridge/src/util/canInjectKey.ts
 create mode 100644 packages/idb-bridge/src/util/cmp.ts
 create mode 100644 packages/idb-bridge/src/util/deepEquals.ts
 create mode 100644 packages/idb-bridge/src/util/enforceRange.ts
 create mode 100644 packages/idb-bridge/src/util/errors.ts
 create mode 100644 packages/idb-bridge/src/util/extractKey.ts
 create mode 100644 packages/idb-bridge/src/util/fakeDOMStringList.ts
 create mode 100644 packages/idb-bridge/src/util/getIndexKeys.test.ts
 create mode 100644 packages/idb-bridge/src/util/getIndexKeys.ts
 create mode 100644 packages/idb-bridge/src/util/injectKey.ts
 create mode 100644 packages/idb-bridge/src/util/makeStoreKeyValue.test.ts
 create mode 100644 packages/idb-bridge/src/util/makeStoreKeyValue.ts
 create mode 100644 packages/idb-bridge/src/util/openPromise.ts
 create mode 100644 packages/idb-bridge/src/util/queueTask.ts
 create mode 100644 packages/idb-bridge/src/util/structuredClone.ts
 create mode 100644 packages/idb-bridge/src/util/types.ts
 create mode 100644 packages/idb-bridge/src/util/validateKeyPath.ts
 create mode 100644 packages/idb-bridge/src/util/valueToKey.ts
 create mode 100644 packages/idb-bridge/tsconfig.json
 create mode 100644 packages/idb-bridge/yarn.lock

diff --git a/packages/idb-bridge/.prettierrc b/packages/idb-bridge/.prettierrc
new file mode 100644
index 00000000..50063da9
--- /dev/null
+++ b/packages/idb-bridge/.prettierrc
@@ -0,0 +1,6 @@
+{
+  "trailingComma": "all",
+  "tabWidth": 2,
+  "semi": true,
+  "singleQuote": false
+}
\ No newline at end of file
diff --git a/packages/idb-bridge/README.md b/packages/idb-bridge/README.md
new file mode 100644
index 00000000..9298f292
--- /dev/null
+++ b/packages/idb-bridge/README.md
@@ -0,0 +1,18 @@
+# idb-bridge
+
+The `idb-bridge` package implements the IndexedDB API with multiple backends.
+
+Currently available backends are:
+ * sqlite: A SQLite3 database.  Can be backed by a file or in memory.
+ * memdb: An unoptimized in-memory storage backend.  Useful for environments
+   that do not have sqlite.
+
+## Known Issues
+
+IndexedDB assumes that after a database has been opened, the set of object 
stores and indices does not change,
+even when there is no transaction active.  We cannot guarantee this with 
SQLite.
+
+## Acknowledgements
+
+This library is based on the fakeIndexedDB library
+(https://github.com/dumbmatter/fakeIndexedDB).
\ No newline at end of file
diff --git a/packages/idb-bridge/package.json b/packages/idb-bridge/package.json
new file mode 100644
index 00000000..52396bfe
--- /dev/null
+++ b/packages/idb-bridge/package.json
@@ -0,0 +1,19 @@
+{
+  "name": "idb-bridge",
+  "version": "0.0.1",
+  "description": "IndexedDB implementation that uses SQLite3 as storage",
+  "main": "index.js",
+  "author": "Florian Dold",
+  "license": "AGPL-3.0-or-later",
+  "private": false,
+  "dependencies": {
+    "sqlite3": "^4.0.8"
+  },
+  "scripts": {
+    "test": "tsc && ava"
+  },
+  "devDependencies": {
+    "ava": "2.1.0",
+    "typescript": "^3.4.5"
+  }
+}
diff --git a/packages/idb-bridge/src/BridgeIDBCursor.ts 
b/packages/idb-bridge/src/BridgeIDBCursor.ts
new file mode 100644
index 00000000..ed5aa3e8
--- /dev/null
+++ b/packages/idb-bridge/src/BridgeIDBCursor.ts
@@ -0,0 +1,345 @@
+/*
+ Copyright 2019 Florian Dold
+ Copyright 2017 Jeremy Scheff
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ or implied. See the License for the specific language governing
+ permissions and limitations under the License.
+ */
+
+import BridgeIDBKeyRange from "./BridgeIDBKeyRange";
+import BridgeIDBObjectStore from "./BridgeIDBObjectStore";
+import BridgeIDBRequest from "./BridgeIDBRequest";
+import compareKeys from "./util/cmp";
+import {
+  DataError,
+  InvalidAccessError,
+  InvalidStateError,
+  ReadOnlyError,
+  TransactionInactiveError,
+} from "./util/errors";
+import extractKey from "./util/extractKey";
+import structuredClone from "./util/structuredClone";
+import {
+  CursorRange,
+  CursorSource,
+  Key,
+  Value,
+  BridgeIDBCursorDirection,
+} from "./util/types";
+import valueToKey from "./util/valueToKey";
+import {
+  RecordGetRequest,
+  ResultLevel,
+  Backend,
+  DatabaseTransaction,
+  RecordStoreRequest,
+} from "./backend-interface";
+
+/**
+ * http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#cursor
+ */
+class BridgeIDBCursor {
+  _request: BridgeIDBRequest | undefined;
+
+  private _gotValue: boolean = false;
+  private _range: CursorRange;
+  private _indexPosition = undefined; // Key of previously returned record
+  private _objectStorePosition = undefined;
+  private _keyOnly: boolean;
+
+  private _source: CursorSource;
+  private _direction: BridgeIDBCursorDirection;
+  private _key = undefined;
+  private _primaryKey: Key | undefined = undefined;
+  private _indexName: string | undefined;
+  private _objectStoreName: string;
+
+  protected _value: Value = undefined;
+
+  constructor(
+    source: CursorSource,
+    objectStoreName: string,
+    indexName: string | undefined,
+    range: CursorRange,
+    direction: BridgeIDBCursorDirection,
+    request: BridgeIDBRequest,
+    keyOnly: boolean,
+  ) {
+    this._indexName = indexName;
+    this._objectStoreName = objectStoreName;
+    this._range = range;
+    this._source = source;
+    this._direction = direction;
+    this._request = request;
+    this._keyOnly = keyOnly;
+  }
+
+  get _effectiveObjectStore(): BridgeIDBObjectStore {
+    if (this.source instanceof BridgeIDBObjectStore) {
+      return this.source;
+    }
+    return this.source.objectStore;
+  }
+
+  get _backend(): Backend {
+    return this._source._backend;
+  }
+
+  // Read only properties
+  get source() {
+    return this._source;
+  }
+  set source(val) {
+    /* For babel */
+  }
+
+  get direction() {
+    return this._direction;
+  }
+  set direction(val) {
+    /* For babel */
+  }
+
+  get key() {
+    return this._key;
+  }
+  set key(val) {
+    /* For babel */
+  }
+
+  get primaryKey() {
+    return this._primaryKey;
+  }
+  set primaryKey(val) {
+    /* For babel */
+  }
+
+  /**
+   * https://w3c.github.io/IndexedDB/#iterate-a-cursor
+   */
+  async _iterate(key?: Key, primaryKey?: Key): Promise<any> {
+    const recordGetRequest: RecordGetRequest = {
+      direction: this.direction,
+      indexName: this._indexName,
+      lastIndexPosition: this._indexPosition,
+      lastObjectStorePosition: this._objectStorePosition,
+      limit: 1,
+      range: this._range,
+      objectStoreName: this._objectStoreName,
+      advanceIndexKey: key,
+      advancePrimaryKey: primaryKey,
+      resultLevel: this._keyOnly ? ResultLevel.OnlyKeys : ResultLevel.Full,
+    };
+
+    const { btx } = this.source._confirmActiveTransaction();
+
+    let response = await this._backend.getRecords(btx, recordGetRequest);
+
+    if (response.count === 0) {
+      console.log("cursor is returning empty result");
+      return null;
+    }
+
+    if (response.count !== 1) {
+      throw Error("invariant failed");
+    }
+
+    console.log("request is:", JSON.stringify(recordGetRequest));
+    console.log("get response is:", JSON.stringify(response));
+
+    if (this._indexName !== undefined) {
+      this._key = response.indexKeys![0];
+    } else {
+      this._key = response.primaryKeys![0];
+    }
+
+    this._primaryKey = response.primaryKeys![0];
+
+    if (!this._keyOnly) {
+      this._value = response.values![0];
+    }
+
+    this._gotValue = true;
+    this._objectStorePosition = structuredClone(response.primaryKeys![0]);
+    if (response.indexKeys !== undefined && response.indexKeys.length > 0) {
+      this._indexPosition = structuredClone(response.indexKeys[0]);
+    }
+
+    return this;
+  }
+
+  // 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBCursor-update-IDBRequest-any-value
+  public update(value: Value) {
+    if (value === undefined) {
+      throw new TypeError();
+    }
+
+    const transaction = this._effectiveObjectStore.transaction;
+
+    if (transaction._state !== "active") {
+      throw new TransactionInactiveError();
+    }
+
+    if (transaction.mode === "readonly") {
+      throw new ReadOnlyError();
+    }
+
+    if (this._effectiveObjectStore._deleted) {
+      throw new InvalidStateError();
+    }
+
+    if (
+      !(this.source instanceof BridgeIDBObjectStore) &&
+      this.source._deleted
+    ) {
+      throw new InvalidStateError();
+    }
+
+    if (!this._gotValue || !this.hasOwnProperty("value")) {
+      throw new InvalidStateError();
+    }
+
+    const storeReq: RecordStoreRequest = {
+      overwrite: true,
+      key: this._primaryKey,
+      value: value,
+      objectStoreName: this._objectStoreName,
+    };
+
+    const operation = async () => {
+      const { btx } = this.source._confirmActiveTransaction();
+      this._backend.storeRecord(btx, storeReq);
+    };
+    return transaction._execRequestAsync({
+      operation,
+      source: this,
+    });
+  }
+
+  /**
+   * 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBCursor-advance-void-unsigned-long-count
+   */
+  public advance(count: number) {
+    throw Error("not implemented");
+  }
+
+  /**
+   * 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBCursor-continue-void-any-key
+   */
+  public continue(key?: Key) {
+    const transaction = this._effectiveObjectStore.transaction;
+
+    if (transaction._state !== "active") {
+      throw new TransactionInactiveError();
+    }
+
+    if (this._effectiveObjectStore._deleted) {
+      throw new InvalidStateError();
+    }
+    if (
+      !(this.source instanceof BridgeIDBObjectStore) &&
+      this.source._deleted
+    ) {
+      throw new InvalidStateError();
+    }
+
+    if (!this._gotValue) {
+      throw new InvalidStateError();
+    }
+
+    if (key !== undefined) {
+      key = valueToKey(key);
+      let lastKey =
+        this._indexName === undefined
+          ? this._objectStorePosition
+          : this._indexPosition;
+
+      const cmpResult = compareKeys(key, lastKey);
+
+      if (
+        (cmpResult <= 0 &&
+          (this.direction === "next" || this.direction === "nextunique")) ||
+        (cmpResult >= 0 &&
+          (this.direction === "prev" || this.direction === "prevunique"))
+      ) {
+        throw new DataError();
+      }
+    }
+
+    if (this._request) {
+      this._request.readyState = "pending";
+    }
+
+    const operation = async () => {
+      return this._iterate(key);
+    };
+
+    transaction._execRequestAsync({
+      operation,
+      request: this._request,
+      source: this.source,
+    });
+
+    this._gotValue = false;
+  }
+
+  // https://w3c.github.io/IndexedDB/#dom-idbcursor-continueprimarykey
+  public continuePrimaryKey(key: Key, primaryKey: Key) {
+    throw Error("not implemented");
+  }
+
+  public delete() {
+    const transaction = this._effectiveObjectStore.transaction;
+
+    if (transaction._state !== "active") {
+      throw new TransactionInactiveError();
+    }
+
+    if (transaction.mode === "readonly") {
+      throw new ReadOnlyError();
+    }
+
+    if (this._effectiveObjectStore._deleted) {
+      throw new InvalidStateError();
+    }
+    if (
+      !(this.source instanceof BridgeIDBObjectStore) &&
+      this.source._deleted
+    ) {
+      throw new InvalidStateError();
+    }
+
+    if (!this._gotValue || !this.hasOwnProperty("value")) {
+      throw new InvalidStateError();
+    }
+
+    const operation = async () => {
+      const { btx } = this.source._confirmActiveTransaction();
+      this._backend.deleteRecord(
+        btx,
+        this._objectStoreName,
+        BridgeIDBKeyRange._valueToKeyRange(this._primaryKey),
+      );
+    };
+
+    return transaction._execRequestAsync({
+      operation,
+      source: this,
+    });
+  }
+
+  public toString() {
+    return "[object IDBCursor]";
+  }
+}
+
+export default BridgeIDBCursor;
diff --git a/packages/idb-bridge/src/BridgeIDBCursorWithValue.ts 
b/packages/idb-bridge/src/BridgeIDBCursorWithValue.ts
new file mode 100644
index 00000000..b2f23147
--- /dev/null
+++ b/packages/idb-bridge/src/BridgeIDBCursorWithValue.ts
@@ -0,0 +1,47 @@
+/*
+ Copyright 2017 Jeremy Scheff
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ or implied. See the License for the specific language governing
+ permissions and limitations under the License.
+ */
+
+import BridgeIDBCursor from "./BridgeIDBCursor";
+import {
+    CursorRange,
+    CursorSource,
+    BridgeIDBCursorDirection,
+    Value,
+} from "./util/types";
+
+class BridgeIDBCursorWithValue extends BridgeIDBCursor {
+
+    get value(): Value {
+        return this._value;
+    }
+    
+    constructor(
+        source: CursorSource,
+        objectStoreName: string,
+        indexName: string | undefined,
+        range: CursorRange,
+        direction: BridgeIDBCursorDirection,
+        request?: any,
+    ) {
+        super(source, objectStoreName, indexName, range, direction, request, 
false);
+    }
+
+    public toString() {
+        return "[object IDBCursorWithValue]";
+    }
+}
+
+export default BridgeIDBCursorWithValue;
diff --git a/packages/idb-bridge/src/BridgeIDBDatabase.ts 
b/packages/idb-bridge/src/BridgeIDBDatabase.ts
new file mode 100644
index 00000000..bc2e8acc
--- /dev/null
+++ b/packages/idb-bridge/src/BridgeIDBDatabase.ts
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2017 Jeremy Scheff
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+import BridgeIDBTransaction from "./BridgeIDBTransaction";
+import {
+  ConstraintError,
+  InvalidAccessError,
+  InvalidStateError,
+  NotFoundError,
+  TransactionInactiveError,
+} from "./util/errors";
+import fakeDOMStringList from "./util/fakeDOMStringList";
+import FakeEventTarget from "./util/FakeEventTarget";
+import { FakeDOMStringList, KeyPath, TransactionMode } from "./util/types";
+import validateKeyPath from "./util/validateKeyPath";
+import queueTask from "./util/queueTask";
+import {
+  Backend,
+  DatabaseConnection,
+  Schema,
+  DatabaseTransaction,
+} from "./backend-interface";
+
+/**
+ * Ensure that an active version change transaction is currently running.
+ */
+const confirmActiveVersionchangeTransaction = (database: BridgeIDBDatabase) => 
{
+  if (!database._runningVersionchangeTransaction) {
+    throw new InvalidStateError();
+  }
+
+  // Find the latest versionchange transaction
+  const transactions = database._transactions.filter(
+    (tx: BridgeIDBTransaction) => {
+      return tx.mode === "versionchange";
+    },
+  );
+  const transaction = transactions[transactions.length - 1];
+
+  if (!transaction || transaction._state === "finished") {
+    throw new InvalidStateError();
+  }
+
+  if (transaction._state !== "active") {
+    throw new TransactionInactiveError();
+  }
+
+  return transaction;
+};
+
+
+// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#database-interface
+class BridgeIDBDatabase extends FakeEventTarget {
+  _closePending = false;
+  _closed = false;
+  _runningVersionchangeTransaction = false;
+  _transactions: Array<BridgeIDBTransaction> = [];
+
+  _backendConnection: DatabaseConnection;
+  _backend: Backend;
+
+  _schema: Schema;
+
+  get name(): string {
+    return this._schema.databaseName;
+  }
+
+  get version(): number {
+    return this._schema.databaseVersion;
+  }
+
+  get objectStoreNames(): FakeDOMStringList {
+    return fakeDOMStringList(Object.keys(this._schema.objectStores)).sort();
+  }
+
+  /**
+   * http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#database-closing-steps
+   */
+  _closeConnection() {
+    this._closePending = true;
+
+    const transactionsComplete = this._transactions.every(
+      (transaction: BridgeIDBTransaction) => {
+        return transaction._state === "finished";
+      },
+    );
+
+    if (transactionsComplete) {
+      this._closed = true;
+      this._backend.close(this._backendConnection);
+    } else {
+      queueTask(() => {
+        this._closeConnection();
+      });
+    }
+  }
+
+  constructor(backend: Backend, backendConnection: DatabaseConnection) {
+    super();
+
+    this._schema = backend.getSchema(backendConnection);
+
+    this._backend = backend;
+    this._backendConnection = backendConnection;
+  }
+
+  // http://w3c.github.io/IndexedDB/#dom-idbdatabase-createobjectstore
+  public createObjectStore(
+    name: string,
+    options: { autoIncrement?: boolean; keyPath?: KeyPath } | null = {},
+  ) {
+    if (name === undefined) {
+      throw new TypeError();
+    }
+    const transaction = confirmActiveVersionchangeTransaction(this);
+    const backendTx = transaction._backendTransaction;
+    if (!backendTx) {
+      throw Error("invariant violated");
+    }
+
+    const keyPath =
+      options !== null && options.keyPath !== undefined
+        ? options.keyPath
+        : null;
+    const autoIncrement =
+      options !== null && options.autoIncrement !== undefined
+        ? options.autoIncrement
+        : false;
+
+    if (keyPath !== null) {
+      validateKeyPath(keyPath);
+    }
+
+    if (Object.keys(this._schema.objectStores).includes(name)) {
+      throw new ConstraintError();
+    }
+
+    if (autoIncrement && (keyPath === "" || Array.isArray(keyPath))) {
+      throw new InvalidAccessError();
+    }
+
+    transaction._backend.createObjectStore(backendTx, name, keyPath, 
autoIncrement);
+
+    this._schema = this._backend.getSchema(this._backendConnection);
+
+    return transaction.objectStore(name);
+  }
+
+  public deleteObjectStore(name: string): void {
+    if (name === undefined) {
+      throw new TypeError();
+    }
+    const transaction = confirmActiveVersionchangeTransaction(this);
+    transaction._objectStoresCache.delete(name);
+  }
+
+  public _internalTransaction(
+    storeNames: string | string[],
+    mode?: TransactionMode,
+    backendTransaction?: DatabaseTransaction,
+  ): BridgeIDBTransaction {
+    mode = mode !== undefined ? mode : "readonly";
+    if (
+      mode !== "readonly" &&
+      mode !== "readwrite" &&
+      mode !== "versionchange"
+    ) {
+      throw new TypeError("Invalid mode: " + mode);
+    }
+
+    const hasActiveVersionchange = this._transactions.some(
+      (transaction: BridgeIDBTransaction) => {
+        return (
+          transaction._state === "active" &&
+          transaction.mode === "versionchange" &&
+          transaction.db === this
+        );
+      },
+    );
+    if (hasActiveVersionchange) {
+      throw new InvalidStateError();
+    }
+
+    if (this._closePending) {
+      throw new InvalidStateError();
+    }
+
+    if (!Array.isArray(storeNames)) {
+      storeNames = [storeNames];
+    }
+    if (storeNames.length === 0 && mode !== "versionchange") {
+      throw new InvalidAccessError();
+    }
+    for (const storeName of storeNames) {
+      if (this.objectStoreNames.indexOf(storeName) < 0) {
+        throw new NotFoundError(
+          "No objectStore named " + storeName + " in this database",
+        );
+      }
+    }
+
+    const tx = new BridgeIDBTransaction(storeNames, mode, this, 
backendTransaction);
+    this._transactions.push(tx);
+    queueTask(() => tx._start());
+    return tx;
+  }
+
+  public transaction(
+    storeNames: string | string[],
+    mode?: TransactionMode,
+  ): BridgeIDBTransaction {
+    if (mode === "versionchange") {
+      throw new TypeError("Invalid mode: " + mode);
+    }
+    return this._internalTransaction(storeNames, mode);
+  }
+
+  public close() {
+    this._closeConnection();
+  }
+
+  public toString() {
+    return "[object IDBDatabase]";
+  }
+}
+
+export default BridgeIDBDatabase;
diff --git a/packages/idb-bridge/src/BridgeIDBFactory.ts 
b/packages/idb-bridge/src/BridgeIDBFactory.ts
new file mode 100644
index 00000000..ad02be46
--- /dev/null
+++ b/packages/idb-bridge/src/BridgeIDBFactory.ts
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2019 Florian Dold
+ * Copyright 2017 Jeremy Scheff
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+import BridgeIDBDatabase from "./BridgeIDBDatabase";
+import BridgeIDBOpenDBRequest from "./BridgeIDBOpenDBRequest";
+import BridgeIDBVersionChangeEvent from "./BridgeIDBVersionChangeEvent";
+import compareKeys from "./util/cmp";
+import enforceRange from "./util/enforceRange";
+import { AbortError, VersionError } from "./util/errors";
+import FakeEvent from "./util/FakeEvent";
+import { Backend, DatabaseConnection } from "./backend-interface";
+import queueTask from "./util/queueTask";
+
+type DatabaseList = Array<{ name: string; version: number }>;
+
+class BridgeIDBFactory {
+  public cmp = compareKeys;
+  private backend: Backend;
+  private connections: BridgeIDBDatabase[] = [];
+  static enableTracing: boolean = true;
+
+  public constructor(backend: Backend) {
+    this.backend = backend;
+  }
+
+  // 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBFactory-deleteDatabase-IDBOpenDBRequest-DOMString-name
+  public deleteDatabase(name: string): BridgeIDBOpenDBRequest {
+    const request = new BridgeIDBOpenDBRequest();
+    request.source = null;
+
+    queueTask(async () => {
+      const databases = await this.backend.getDatabases();
+      const dbInfo = databases.find((x) => x.name == name);
+      if (!dbInfo) {
+        // Database already doesn't exist, success!
+        const event = new BridgeIDBVersionChangeEvent("success", {
+          newVersion: null,
+          oldVersion: 0,
+        });
+        request.dispatchEvent(event);
+        return;
+      }
+      const oldVersion = dbInfo.version;
+
+      try {
+        const dbconn = await this.backend.connectDatabase(name);
+        const backendTransaction = await 
this.backend.enterVersionChange(dbconn, 0);
+        await this.backend.deleteDatabase(backendTransaction, name);
+        await this.backend.commit(backendTransaction);
+        await this.backend.close(dbconn);
+
+        request.result = undefined;
+        request.readyState = "done";
+
+        const event2 = new BridgeIDBVersionChangeEvent("success", {
+          newVersion: null,
+          oldVersion,
+        });
+        request.dispatchEvent(event2);
+      } catch (err) {
+        request.error = new Error();
+        request.error.name = err.name;
+        request.readyState = "done";
+
+        const event = new FakeEvent("error", {
+          bubbles: true,
+          cancelable: true,
+        });
+        event.eventPath = [];
+        request.dispatchEvent(event);
+      }
+    });
+
+    return request;
+  }
+
+  // tslint:disable-next-line max-line-length
+  // 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBFactory-open-IDBOpenDBRequest-DOMString-name-unsigned-long-long-version
+  public open(name: string, version?: number) {
+    if (arguments.length > 1 && version !== undefined) {
+      // Based on spec, not sure why "MAX_SAFE_INTEGER" instead of "unsigned 
long long", but it's needed to pass
+      // tests
+      version = enforceRange(version, "MAX_SAFE_INTEGER");
+    }
+    if (version === 0) {
+      throw new TypeError();
+    }
+
+    const request = new BridgeIDBOpenDBRequest();
+
+    queueTask(async () => {
+      let dbconn: DatabaseConnection;
+      try {
+        dbconn = await this.backend.connectDatabase(name);
+      } catch (err) {
+        request._finishWithError(err);
+        return;
+      }
+
+      const schema = this.backend.getSchema(dbconn);
+      const existingVersion = schema.databaseVersion;
+
+      if (version === undefined) {
+        version = existingVersion !== 0 ? existingVersion : 1;
+      }
+
+      const requestedVersion = version;
+
+      if (existingVersion > requestedVersion) {
+        request._finishWithError(new VersionError());
+        return;
+      }
+
+      const db = new BridgeIDBDatabase(this.backend, dbconn);
+
+      if (existingVersion < requestedVersion) {
+        // 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-running-a-versionchange-transaction
+
+        for (const otherConn of this.connections) {
+          const event = new BridgeIDBVersionChangeEvent("versionchange", {
+            newVersion: version,
+            oldVersion: existingVersion,
+          });
+          otherConn.dispatchEvent(event);
+        }
+
+        if (this._anyOpen()) {
+          const event = new BridgeIDBVersionChangeEvent("blocked", {
+            newVersion: version,
+            oldVersion: existingVersion,
+          });
+          request.dispatchEvent(event);
+        }
+
+        const backendTransaction = await 
this.backend.enterVersionChange(dbconn, requestedVersion);
+        db._runningVersionchangeTransaction = true;
+
+        const transaction = db._internalTransaction(
+          [],
+          "versionchange",
+          backendTransaction,
+        );
+        const event = new BridgeIDBVersionChangeEvent("upgradeneeded", {
+          newVersion: version,
+          oldVersion: existingVersion,
+        });
+
+        request.result = db;
+        request.readyState = "done";
+        request.transaction = transaction;
+        request.dispatchEvent(event);
+
+        await transaction._waitDone();
+
+        // We don't explicitly exit the versionchange transaction,
+        // since this is already done by the BridgeIDBTransaction.
+        db._runningVersionchangeTransaction = false;
+
+        const event2 = new FakeEvent("success", {
+          bubbles: false,
+          cancelable: false,
+        });
+        event2.eventPath = [request];
+
+        request.dispatchEvent(event2);
+      }
+
+      this.connections.push(db);
+      return db;
+    });
+
+    return request;
+  }
+
+  // https://w3c.github.io/IndexedDB/#dom-idbfactory-databases
+  public databases(): Promise<DatabaseList> {
+    return this.backend.getDatabases();
+  }
+
+  public toString(): string {
+    return "[object IDBFactory]";
+  }
+
+  private _anyOpen(): boolean {
+    return this.connections.some(c => !c._closed && !c._closePending);
+  }
+}
+
+export default BridgeIDBFactory;
diff --git a/packages/idb-bridge/src/BridgeIDBIndex.ts 
b/packages/idb-bridge/src/BridgeIDBIndex.ts
new file mode 100644
index 00000000..8179be83
--- /dev/null
+++ b/packages/idb-bridge/src/BridgeIDBIndex.ts
@@ -0,0 +1,315 @@
+/*
+ Copyright 2017 Jeremy Scheff
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ or implied. See the License for the specific language governing
+ permissions and limitations under the License.
+ */
+
+import BridgeIDBCursor from "./BridgeIDBCursor";
+import BridgeIDBCursorWithValue from "./BridgeIDBCursorWithValue";
+import BridgeIDBKeyRange from "./BridgeIDBKeyRange";
+import BridgeIDBObjectStore from "./BridgeIDBObjectStore";
+import BridgeIDBRequest from "./BridgeIDBRequest";
+import {
+  ConstraintError,
+  InvalidStateError,
+  TransactionInactiveError,
+} from "./util/errors";
+import { BridgeIDBCursorDirection, Key, KeyPath } from "./util/types";
+import valueToKey from "./util/valueToKey";
+import BridgeIDBTransaction from "./BridgeIDBTransaction";
+import {
+  Schema,
+  Backend,
+  DatabaseTransaction,
+  RecordGetRequest,
+  ResultLevel,
+} from "./backend-interface";
+
+const confirmActiveTransaction = (
+  index: BridgeIDBIndex,
+): BridgeIDBTransaction => {
+  if (index._deleted || index.objectStore._deleted) {
+    throw new InvalidStateError();
+  }
+
+  if (index.objectStore.transaction._state !== "active") {
+    throw new TransactionInactiveError();
+  }
+
+  return index.objectStore.transaction;
+};
+
+// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#idl-def-IDBIndex
+class BridgeIDBIndex {
+  objectStore: BridgeIDBObjectStore;
+
+  get _schema(): Schema {
+    return this.objectStore.transaction.db._schema;
+  }
+
+  get keyPath(): KeyPath {
+    return this._schema.indexes[this._name].keyPath;
+  }
+
+  get multiEntry(): boolean {
+    return this._schema.indexes[this._name].multiEntry;
+  }
+
+  get unique(): boolean {
+    return this._schema.indexes[this._name].unique;
+  }
+
+  get _backend(): Backend {
+    return this.objectStore._backend;
+  }
+
+  _confirmActiveTransaction(): { btx: DatabaseTransaction } {
+    return this.objectStore._confirmActiveTransaction();
+  }
+
+  private _name: string;
+
+  public _deleted: boolean = false;
+
+  constructor(objectStore: BridgeIDBObjectStore, name: string) {
+    this._name = name;
+    this.objectStore = objectStore;
+  }
+
+  get name() {
+    return this._name;
+  }
+
+  // https://w3c.github.io/IndexedDB/#dom-idbindex-name
+  set name(name: any) {
+    const transaction = this.objectStore.transaction;
+
+    if (!transaction.db._runningVersionchangeTransaction) {
+      throw new InvalidStateError();
+    }
+
+    if (transaction._state !== "active") {
+      throw new TransactionInactiveError();
+    }
+
+    const { btx } = this._confirmActiveTransaction();
+
+    const oldName = this._name;
+    const newName = String(name);
+
+    if (newName === oldName) {
+      return;
+    }
+
+    this._backend.renameIndex(btx, oldName, newName);
+
+    if (this.objectStore.indexNames.indexOf(name) >= 0) {
+      throw new ConstraintError();
+    }
+  }
+
+  // tslint:disable-next-line max-line-length
+  // 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBIndex-openCursor-IDBRequest-any-range-IDBCursorDirection-direction
+  public openCursor(
+    range?: BridgeIDBKeyRange | Key | null | undefined,
+    direction: BridgeIDBCursorDirection = "next",
+  ) {
+    confirmActiveTransaction(this);
+
+    if (range === null) {
+      range = undefined;
+    }
+    if (range !== undefined && !(range instanceof BridgeIDBKeyRange)) {
+      range = BridgeIDBKeyRange.only(valueToKey(range));
+    }
+
+    const request = new BridgeIDBRequest();
+    request.source = this;
+    request.transaction = this.objectStore.transaction;
+
+    const cursor = new BridgeIDBCursorWithValue(
+      this,
+      this.objectStore.name,
+      this._name,
+      range,
+      direction,
+      request,
+    );
+
+    const operation = async () => {
+      return cursor._iterate();
+    };
+
+    return this.objectStore.transaction._execRequestAsync({
+      operation,
+      request,
+      source: this,
+    });
+  }
+
+  // tslint:disable-next-line max-line-length
+  // 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBIndex-openKeyCursor-IDBRequest-any-range-IDBCursorDirection-direction
+  public openKeyCursor(
+    range?: BridgeIDBKeyRange | Key | null | undefined,
+    direction: BridgeIDBCursorDirection = "next",
+  ) {
+    confirmActiveTransaction(this);
+
+    if (range === null) {
+      range = undefined;
+    }
+    if (range !== undefined && !(range instanceof BridgeIDBKeyRange)) {
+      range = BridgeIDBKeyRange.only(valueToKey(range));
+    }
+
+    const request = new BridgeIDBRequest();
+    request.source = this;
+    request.transaction = this.objectStore.transaction;
+
+    const cursor = new BridgeIDBCursor(
+      this,
+      this.objectStore.name,
+      this._name,
+      range,
+      direction,
+      request,
+      true,
+    );
+
+    return this.objectStore.transaction._execRequestAsync({
+      operation: cursor._iterate.bind(cursor),
+      request,
+      source: this,
+    });
+  }
+
+  public get(key: BridgeIDBKeyRange | Key) {
+    confirmActiveTransaction(this);
+
+    if (!(key instanceof BridgeIDBKeyRange)) {
+      key = BridgeIDBKeyRange._valueToKeyRange(key);
+    }
+
+    const getReq: RecordGetRequest = {
+      direction: "next",
+      indexName: this._name,
+      limit: 1,
+      range: key,
+      objectStoreName: this.objectStore._name,
+      resultLevel: ResultLevel.Full,
+    };
+
+    const operation = async () => {
+      const { btx } = this._confirmActiveTransaction();
+      const result = await this._backend.getRecords(btx, getReq);
+      if (result.count == 0) {
+        return undefined;
+      }
+      const values = result.values;
+      if (!values) {
+        throw Error("invariant violated");
+      }
+      return values[0];
+    };
+
+    return this.objectStore.transaction._execRequestAsync({
+      operation,
+      source: this,
+    });
+  }
+
+  // http://w3c.github.io/IndexedDB/#dom-idbindex-getall
+  public getAll(query?: BridgeIDBKeyRange | Key, count?: number) {
+    throw Error("not implemented");
+  }
+
+  // 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBIndex-getKey-IDBRequest-any-key
+  public getKey(key: BridgeIDBKeyRange | Key) {
+    confirmActiveTransaction(this);
+
+    if (!(key instanceof BridgeIDBKeyRange)) {
+      key = BridgeIDBKeyRange._valueToKeyRange(key);
+    }
+
+    const getReq: RecordGetRequest = {
+      direction: "next",
+      indexName: this._name,
+      limit: 1,
+      range: key,
+      objectStoreName: this.objectStore._name,
+      resultLevel: ResultLevel.OnlyKeys,
+    };
+
+    const operation = async () => {
+      const { btx } = this._confirmActiveTransaction();
+      const result = await this._backend.getRecords(btx, getReq);
+      if (result.count == 0) {
+        return undefined;
+      }
+      const primaryKeys = result.primaryKeys;
+      if (!primaryKeys) {
+        throw Error("invariant violated");
+      }
+      return primaryKeys[0];
+    };
+
+    return this.objectStore.transaction._execRequestAsync({
+      operation,
+      source: this,
+    });
+  }
+
+  // http://w3c.github.io/IndexedDB/#dom-idbindex-getallkeys
+  public getAllKeys(query?: BridgeIDBKeyRange | Key, count?: number) {
+    throw Error("not implemented");
+  }
+
+  // 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBIndex-count-IDBRequest-any-key
+  public count(key: BridgeIDBKeyRange | Key | null | undefined) {
+    confirmActiveTransaction(this);
+
+    if (key === null) {
+      key = undefined;
+    }
+    if (key !== undefined && !(key instanceof BridgeIDBKeyRange)) {
+      key = BridgeIDBKeyRange.only(valueToKey(key));
+    }
+
+    const getReq: RecordGetRequest = {
+      direction: "next",
+      indexName: this._name,
+      limit: 1,
+      range: key,
+      objectStoreName: this.objectStore._name,
+      resultLevel: ResultLevel.OnlyCount,
+    };
+
+    const operation = async () => {
+      const { btx } = this._confirmActiveTransaction();
+      const result = await this._backend.getRecords(btx, getReq);
+      return result.count;
+    };
+
+    return this.objectStore.transaction._execRequestAsync({
+      operation,
+      source: this,
+    });
+
+  }
+
+  public toString() {
+    return "[object IDBIndex]";
+  }
+}
+
+export default BridgeIDBIndex;
diff --git a/packages/idb-bridge/src/BridgeIDBKeyRange.ts 
b/packages/idb-bridge/src/BridgeIDBKeyRange.ts
new file mode 100644
index 00000000..2b32c7e0
--- /dev/null
+++ b/packages/idb-bridge/src/BridgeIDBKeyRange.ts
@@ -0,0 +1,133 @@
+/*
+  Copyright 2019 Florian Dold
+  Copyright 2017 Jeremy Scheff
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+  or implied. See the License for the specific language governing
+  permissions and limitations under the License.
+ */
+
+import compareKeys from "./util/cmp";
+import { DataError } from "./util/errors";
+import { Key } from "./util/types";
+import valueToKey from "./util/valueToKey";
+
+// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#range-concept
+class BridgeIDBKeyRange {
+  public static only(value: Key) {
+    if (arguments.length === 0) {
+      throw new TypeError();
+    }
+    value = valueToKey(value);
+    return new BridgeIDBKeyRange(value, value, false, false);
+  }
+
+  static lowerBound(lower: Key, open: boolean = false) {
+    if (arguments.length === 0) {
+      throw new TypeError();
+    }
+    lower = valueToKey(lower);
+    return new BridgeIDBKeyRange(lower, undefined, open, true);
+  }
+
+  static upperBound(upper: Key, open: boolean = false) {
+    if (arguments.length === 0) {
+      throw new TypeError();
+    }
+    upper = valueToKey(upper);
+    return new BridgeIDBKeyRange(undefined, upper, true, open);
+  }
+
+  static bound(
+    lower: Key,
+    upper: Key,
+    lowerOpen: boolean = false,
+    upperOpen: boolean = false,
+  ) {
+    if (arguments.length < 2) {
+      throw new TypeError();
+    }
+
+    const cmpResult = compareKeys(lower, upper);
+    if (cmpResult === 1 || (cmpResult === 0 && (lowerOpen || upperOpen))) {
+      throw new DataError();
+    }
+
+    lower = valueToKey(lower);
+    upper = valueToKey(upper);
+    return new BridgeIDBKeyRange(lower, upper, lowerOpen, upperOpen);
+  }
+
+  readonly lower: Key | undefined;
+  readonly upper: Key | undefined;
+  readonly lowerOpen: boolean;
+  readonly upperOpen: boolean;
+
+  constructor(
+    lower: Key | undefined,
+    upper: Key | undefined,
+    lowerOpen: boolean,
+    upperOpen: boolean,
+  ) {
+    this.lower = lower;
+    this.upper = upper;
+    this.lowerOpen = lowerOpen;
+    this.upperOpen = upperOpen;
+  }
+
+  // https://w3c.github.io/IndexedDB/#dom-idbkeyrange-includes
+  includes(key: Key) {
+    if (arguments.length === 0) {
+      throw new TypeError();
+    }
+    key = valueToKey(key);
+
+    if (this.lower !== undefined) {
+      const cmpResult = compareKeys(this.lower, key);
+
+      if (cmpResult === 1 || (cmpResult === 0 && this.lowerOpen)) {
+        return false;
+      }
+    }
+    if (this.upper !== undefined) {
+      const cmpResult = compareKeys(this.upper, key);
+
+      if (cmpResult === -1 || (cmpResult === 0 && this.upperOpen)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  toString() {
+    return "[object IDBKeyRange]";
+  }
+
+  static _valueToKeyRange(value: any, nullDisallowedFlag: boolean = false) {
+    if (value instanceof BridgeIDBKeyRange) {
+        return value;
+    }
+
+    if (value === null || value === undefined) {
+        if (nullDisallowedFlag) {
+            throw new DataError();
+        }
+        return new BridgeIDBKeyRange(undefined, undefined, false, false);
+    }
+
+    const key = valueToKey(value);
+
+    return BridgeIDBKeyRange.only(key);
+  }
+
+}
+
+export default BridgeIDBKeyRange;
diff --git a/packages/idb-bridge/src/BridgeIDBObjectStore.ts 
b/packages/idb-bridge/src/BridgeIDBObjectStore.ts
new file mode 100644
index 00000000..eca4c198
--- /dev/null
+++ b/packages/idb-bridge/src/BridgeIDBObjectStore.ts
@@ -0,0 +1,445 @@
+/*
+ Copyright 2017 Jeremy Scheff
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ or implied. See the License for the specific language governing
+ permissions and limitations under the License.
+ */
+
+import BridgeIDBCursor from "./BridgeIDBCursor";
+import BridgeIDBCursorWithValue from "./BridgeIDBCursorWithValue";
+import BridgeIDBIndex from "./BridgeIDBIndex";
+import BridgeIDBKeyRange from "./BridgeIDBKeyRange";
+import BridgeIDBRequest from "./BridgeIDBRequest";
+import BridgeIDBTransaction from "./BridgeIDBTransaction";
+
+import {
+  ConstraintError,
+  DataError,
+  InvalidAccessError,
+  InvalidStateError,
+  NotFoundError,
+  ReadOnlyError,
+  TransactionInactiveError,
+} from "./util/errors";
+import extractKey from "./util/extractKey";
+import fakeDOMStringList from "./util/fakeDOMStringList";
+import structuredClone from "./util/structuredClone";
+import {
+  FakeDOMStringList,
+  BridgeIDBCursorDirection,
+  Key,
+  KeyPath,
+  Value,
+} from "./util/types";
+import validateKeyPath from "./util/validateKeyPath";
+import valueToKey from "./util/valueToKey";
+import {
+  DatabaseTransaction,
+  RecordGetRequest,
+  ResultLevel,
+} from "./backend-interface";
+import BridgeIDBFactory from "./BridgeIDBFactory";
+
+
+// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#object-store
+class BridgeIDBObjectStore {
+  _indexesCache: Map<string, BridgeIDBIndex> = new Map();
+
+  transaction: BridgeIDBTransaction;
+
+  get autoIncrement(): boolean {
+    return this._schema.objectStores[this._name].autoIncrement;
+  }
+
+  get indexNames(): FakeDOMStringList {
+    return 
fakeDOMStringList(this._schema.objectStores[this._name].indexes).sort();
+  }
+
+  get keyPath(): KeyPath | null {
+    return this._schema.objectStores[this._name].keyPath;
+  }
+
+  _name: string;
+
+  get _schema() {
+    return this.transaction.db._schema;
+  }
+
+  _deleted: boolean = false;
+
+  constructor(transaction: BridgeIDBTransaction, name: string) {
+    this._name = name;
+    this.transaction = transaction;
+  }
+
+  get name() {
+    return this._name;
+  }
+
+  get _backend() {
+    return this.transaction.db._backend;
+  }
+
+  get _backendConnection() {
+    return this.transaction.db._backendConnection;
+  }
+
+  _confirmActiveTransaction(): { btx: DatabaseTransaction } {
+    const btx = this.transaction._backendTransaction;
+    if (!btx) {
+      throw new InvalidStateError();
+    }
+    return { btx };
+  }
+
+  // http://w3c.github.io/IndexedDB/#dom-idbobjectstore-name
+  set name(newName: any) {
+    const transaction = this.transaction;
+
+    if (!transaction.db._runningVersionchangeTransaction) {
+      throw new InvalidStateError();
+    }
+
+    let { btx } = this._confirmActiveTransaction();
+
+
+    newName = String(newName);
+
+    const oldName = this._name;
+
+    if (newName === oldName) {
+      return;
+    }
+
+    this._backend.renameObjectStore(btx, oldName, newName);
+    this.transaction.db._schema = 
this._backend.getSchema(this._backendConnection);
+  }
+
+  public _store(value: Value, key: Key | undefined, overwrite: boolean) {
+    if (BridgeIDBFactory.enableTracing) {
+      console.log(`TRACE: IDBObjectStore._store`);
+    }
+    if (this.transaction.mode === "readonly") {
+      throw new ReadOnlyError();
+    }
+    const operation = async () => {
+      const { btx } = this._confirmActiveTransaction();
+      return this._backend.storeRecord(btx, {
+        objectStoreName: this._name,
+        key: key,
+        value: value,
+        overwrite,
+      });
+    };
+
+    return this.transaction._execRequestAsync({ operation, source: this });
+  }
+
+  public put(value: Value, key?: Key) {
+    if (arguments.length === 0) {
+      throw new TypeError();
+    }
+    return this._store(value, key, true);
+  }
+
+  public add(value: Value, key?: Key) {
+    if (arguments.length === 0) {
+      throw new TypeError();
+    }
+    return this._store(value, key, false);
+  }
+
+  public delete(key: Key) {
+    if (arguments.length === 0) {
+      throw new TypeError();
+    }
+
+    if (this.transaction.mode === "readonly") {
+      throw new ReadOnlyError();
+    }
+
+    if (!(key instanceof BridgeIDBKeyRange)) {
+      key = valueToKey(key);
+    }
+
+    const operation = async () => {
+      const { btx } = this._confirmActiveTransaction();
+      return this._backend.deleteRecord(btx, this._name, key);
+    }
+      
+    return this.transaction._execRequestAsync({
+      operation,
+      source: this,
+    });
+  }
+
+  public get(key?: BridgeIDBKeyRange | Key) {
+    if (arguments.length === 0) {
+      throw new TypeError();
+    }
+
+    if (!(key instanceof BridgeIDBKeyRange)) {
+      key = valueToKey(key);
+    }
+
+    const recordRequest: RecordGetRequest = {
+      objectStoreName: this._name,
+      indexName: undefined,
+      lastIndexPosition: undefined,
+      lastObjectStorePosition: undefined,
+      direction: "next",
+      limit: 1,
+      resultLevel: ResultLevel.Full,
+      range: key,
+    };
+
+    const operation = async () => {
+      const { btx } = this._confirmActiveTransaction();
+      const result = await this._backend.getRecords(
+        btx,
+        recordRequest,
+      );
+      if (result.count == 0) {
+        return undefined;
+      }
+      const values = result.values;
+      if (!values) {
+        throw Error("invariant violated");
+      }
+      return values[0];
+    };
+
+    return this.transaction._execRequestAsync({
+      operation,
+      source: this,
+    });
+  }
+
+  // http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getall
+  public getAll(query?: BridgeIDBKeyRange | Key, count?: number) {
+    throw Error("not implemented");
+  }
+
+  // http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getkey
+  public getKey(key?: BridgeIDBKeyRange | Key) {
+    throw Error("not implemented");
+  }
+
+  // http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getallkeys
+  public getAllKeys(query?: BridgeIDBKeyRange | Key, count?: number) {
+    throw Error("not implemented");
+  }
+
+  public clear() {
+    throw Error("not implemented");
+  }
+
+  public openCursor(
+    range?: BridgeIDBKeyRange | Key,
+    direction: BridgeIDBCursorDirection = "next",
+  ) {
+
+    if (range === null) {
+      range = undefined;
+    }
+    if (range !== undefined && !(range instanceof BridgeIDBKeyRange)) {
+      range = BridgeIDBKeyRange.only(valueToKey(range));
+    }
+
+    const request = new BridgeIDBRequest();
+    request.source = this;
+    request.transaction = this.transaction;
+
+    const cursor = new BridgeIDBCursorWithValue(
+      this,
+      this._name,
+      undefined,
+      range,
+      direction,
+      request,
+    );
+
+    return this.transaction._execRequestAsync({
+      operation: () => cursor._iterate(),
+      request,
+      source: this,
+    });
+  }
+
+  public openKeyCursor(
+    range?: BridgeIDBKeyRange | Key,
+    direction?: BridgeIDBCursorDirection,
+  ) {
+    if (range === null) {
+      range = undefined;
+    }
+    if (range !== undefined && !(range instanceof BridgeIDBKeyRange)) {
+      range = BridgeIDBKeyRange.only(valueToKey(range));
+    }
+
+    if (!direction) {
+      direction = "next";
+    }
+
+    const request = new BridgeIDBRequest();
+    request.source = this;
+    request.transaction = this.transaction;
+
+    const cursor = new BridgeIDBCursor(
+      this,
+      this._name,
+      undefined,
+      range,
+      direction,
+      request,
+      true,
+    );
+
+    return this.transaction._execRequestAsync({
+      operation: cursor._iterate.bind(cursor),
+      request,
+      source: this,
+    });
+  }
+
+  // tslint:disable-next-line max-line-length
+  // 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBObjectStore-createIndex-IDBIndex-DOMString-name-DOMString-sequence-DOMString--keyPath-IDBIndexParameters-optionalParameters
+  public createIndex(
+    indexName: string,
+    keyPath: KeyPath,
+    optionalParameters: { multiEntry?: boolean; unique?: boolean } = {},
+  ) {
+    if (arguments.length < 2) {
+      throw new TypeError();
+    }
+
+    if (!this.transaction.db._runningVersionchangeTransaction) {
+      throw new InvalidStateError();
+    }
+
+    const { btx } = this._confirmActiveTransaction();
+
+    const multiEntry =
+      optionalParameters.multiEntry !== undefined
+        ? optionalParameters.multiEntry
+        : false;
+    const unique =
+      optionalParameters.unique !== undefined
+        ? optionalParameters.unique
+        : false;
+
+    if (this.transaction.mode !== "versionchange") {
+      throw new InvalidStateError();
+    }
+
+    if (this.indexNames.indexOf(indexName) >= 0) {
+      throw new ConstraintError();
+    }
+
+    validateKeyPath(keyPath);
+
+    if (Array.isArray(keyPath) && multiEntry) {
+      throw new InvalidAccessError();
+    }
+
+    this._backend.createIndex(
+      btx,
+      indexName,
+      this._name,
+      keyPath,
+      multiEntry,
+      unique,
+    );
+
+    return new BridgeIDBIndex(this, indexName);
+  }
+
+  // https://w3c.github.io/IndexedDB/#dom-idbobjectstore-index
+  public index(name: string) {
+    if (arguments.length === 0) {
+      throw new TypeError();
+    }
+
+    if (this.transaction._state === "finished") {
+      throw new InvalidStateError();
+    }
+
+    const index = this._indexesCache.get(name);
+    if (index !== undefined) {
+      return index;
+    }
+
+    return new BridgeIDBIndex(this, name);
+  }
+
+  public deleteIndex(name: string) {
+    if (arguments.length === 0) {
+      throw new TypeError();
+    }
+
+    if (this.transaction.mode !== "versionchange") {
+      throw new InvalidStateError();
+    }
+
+    if (!this.transaction.db._runningVersionchangeTransaction) {
+      throw new InvalidStateError();
+    }
+
+    const { btx } = this._confirmActiveTransaction();
+
+    const index = this._indexesCache.get(name);
+    if (index !== undefined) {
+      index._deleted = true;
+    }
+
+    this._backend.deleteIndex(btx, name);
+  }
+
+  // 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBObjectStore-count-IDBRequest-any-key
+  public count(key?: Key | BridgeIDBKeyRange) {
+
+    if (key === null) {
+      key = undefined;
+    }
+    if (key !== undefined && !(key instanceof BridgeIDBKeyRange)) {
+      key = BridgeIDBKeyRange.only(valueToKey(key));
+    }
+
+    const recordGetRequest: RecordGetRequest = {
+      direction: "next",
+      indexName: undefined,
+      lastIndexPosition: undefined,
+      limit: -1,
+      objectStoreName: this._name,
+      lastObjectStorePosition: undefined,
+      range: key,
+      resultLevel: ResultLevel.OnlyCount,
+    };
+
+    const operation = async () => {
+      const { btx } = this._confirmActiveTransaction();
+      const result = await this._backend.getRecords(
+        btx,
+        recordGetRequest,
+      );
+      return result.count;
+    };
+
+    return this.transaction._execRequestAsync({ operation, source: this });
+  }
+
+  public toString() {
+    return "[object IDBObjectStore]";
+  }
+}
+
+export default BridgeIDBObjectStore;
diff --git a/packages/idb-bridge/src/BridgeIDBOpenDBRequest.ts 
b/packages/idb-bridge/src/BridgeIDBOpenDBRequest.ts
new file mode 100644
index 00000000..7b636193
--- /dev/null
+++ b/packages/idb-bridge/src/BridgeIDBOpenDBRequest.ts
@@ -0,0 +1,36 @@
+/*
+  Copyright 2019 Florian Dold
+  Copyright 2017 Jeremy Scheff
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+  or implied. See the License for the specific language governing
+  permissions and limitations under the License.
+*/
+
+import BridgeIDBRequest from "./BridgeIDBRequest";
+import { EventCallback } from "./util/types";
+
+class BridgeIDBOpenDBRequest extends BridgeIDBRequest {
+    public onupgradeneeded: EventCallback | null = null;
+    public onblocked: EventCallback | null = null;
+
+    constructor() {
+      super();
+      // https://www.w3.org/TR/IndexedDB/#open-requests
+      this.source = null;
+    }
+
+    public toString() {
+        return "[object IDBOpenDBRequest]";
+    }
+}
+
+export default BridgeIDBOpenDBRequest;
diff --git a/packages/idb-bridge/src/BridgeIDBRequest.ts 
b/packages/idb-bridge/src/BridgeIDBRequest.ts
new file mode 100644
index 00000000..cd009285
--- /dev/null
+++ b/packages/idb-bridge/src/BridgeIDBRequest.ts
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2017 Jeremy Scheff
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+import BridgeFDBCursor from "./BridgeIDBCursor";
+import BridgeIDBIndex from "./BridgeIDBIndex";
+import BridgeIDBObjectStore from "./BridgeIDBObjectStore";
+import BridgeIDBTransaction from "./BridgeIDBTransaction";
+import { InvalidStateError } from "./util/errors";
+import FakeEventTarget from "./util/FakeEventTarget";
+import { EventCallback } from "./util/types";
+import FakeEvent from "./util/FakeEvent";
+
+class BridgeIDBRequest extends FakeEventTarget {
+  _result: any = null;
+  _error: Error | null | undefined = null;
+  source: BridgeFDBCursor | BridgeIDBIndex | BridgeIDBObjectStore | null = 
null;
+  transaction: BridgeIDBTransaction | null = null;
+  readyState: "done" | "pending" = "pending";
+  onsuccess: EventCallback | null = null;
+  onerror: EventCallback | null = null;
+
+  get error() {
+    if (this.readyState === "pending") {
+      throw new InvalidStateError();
+    }
+    return this._error;
+  }
+
+  set error(value: any) {
+    this._error = value;
+  }
+
+  get result() {
+    if (this.readyState === "pending") {
+      throw new InvalidStateError();
+    }
+    return this._result;
+  }
+
+  set result(value: any) {
+    this._result = value;
+  }
+
+  toString() {
+    return "[object IDBRequest]";
+  }
+
+  _finishWithError(err: Error) {
+    this.result = undefined;
+    this.readyState = "done";
+
+    this.error = new Error(err.message);
+    this.error.name = err.name;
+
+    const event = new FakeEvent("error", {
+      bubbles: true,
+      cancelable: true,
+    });
+    event.eventPath = [];
+    this.dispatchEvent(event);
+  }
+
+  _finishWithResult(result: any) {
+    this.result = result;
+    this.readyState = "done";
+
+    const event = new FakeEvent("success");
+    event.eventPath = [];
+    this.dispatchEvent(event);
+  }
+}
+
+export default BridgeIDBRequest;
diff --git a/packages/idb-bridge/src/BridgeIDBTransaction.ts 
b/packages/idb-bridge/src/BridgeIDBTransaction.ts
new file mode 100644
index 00000000..a9f0201d
--- /dev/null
+++ b/packages/idb-bridge/src/BridgeIDBTransaction.ts
@@ -0,0 +1,333 @@
+import BridgeIDBDatabase from "./BridgeIDBDatabase";
+import BridgeIDBObjectStore from "./BridgeIDBObjectStore";
+import BridgeIDBRequest from "./BridgeIDBRequest";
+import {
+  AbortError,
+  InvalidStateError,
+  NotFoundError,
+  TransactionInactiveError,
+} from "./util/errors";
+import fakeDOMStringList from "./util/fakeDOMStringList";
+import FakeEvent from "./util/FakeEvent";
+import FakeEventTarget from "./util/FakeEventTarget";
+import {
+  EventCallback,
+  FakeDOMStringList,
+  RequestObj,
+  TransactionMode,
+} from "./util/types";
+import queueTask from "./util/queueTask";
+import openPromise from "./util/openPromise";
+import { DatabaseTransaction, Backend } from "./backend-interface";
+import { array } from "prop-types";
+import BridgeIDBFactory from "./BridgeIDBFactory";
+
+// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#transaction
+class BridgeIDBTransaction extends FakeEventTarget {
+  public _state: "active" | "inactive" | "committing" | "finished" = "active";
+  public _started = false;
+  public _objectStoresCache: Map<string, BridgeIDBObjectStore> = new Map();
+
+  public _backendTransaction?: DatabaseTransaction;
+
+  public objectStoreNames: FakeDOMStringList;
+  public mode: TransactionMode;
+  public db: BridgeIDBDatabase;
+  public error: Error | null = null;
+  public onabort: EventCallback | null = null;
+  public oncomplete: EventCallback | null = null;
+  public onerror: EventCallback | null = null;
+
+  private _waitPromise: Promise<void>;
+  private _resolveWait: () => void;
+
+  public _scope: Set<string>;
+  private _requests: Array<{
+    operation: () => void;
+    request: BridgeIDBRequest;
+  }> = [];
+
+  get _backend(): Backend {
+    return this.db._backend;
+  }
+
+  constructor(
+    storeNames: string[],
+    mode: TransactionMode,
+    db: BridgeIDBDatabase,
+    backendTransaction?: DatabaseTransaction,
+  ) {
+    super();
+
+    const myOpenPromise = openPromise<void>();
+    this._waitPromise = myOpenPromise.promise;
+    this._resolveWait = myOpenPromise.resolve;
+
+    this._scope = new Set(storeNames);
+    this._backendTransaction = backendTransaction;
+    this.mode = mode;
+    this.db = db;
+    this.objectStoreNames = fakeDOMStringList(Array.from(this._scope).sort());
+
+    this.db._transactions.push(this);
+  }
+
+  // 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-aborting-a-transaction
+  async _abort(errName: string | null) {
+    this._state = "finished";
+
+    if (errName !== null) {
+      const e = new Error();
+      e.name = errName;
+      this.error = e;
+    }
+
+    // Should this directly remove from _requests?
+    for (const { request } of this._requests) {
+      if (request.readyState !== "done") {
+        request.readyState = "done"; // This will cancel execution of this 
request's operation
+        if (request.source) {
+          request.result = undefined;
+          request.error = new AbortError();
+
+          const event = new FakeEvent("error", {
+            bubbles: true,
+            cancelable: true,
+          });
+          event.eventPath = [this.db, this];
+          request.dispatchEvent(event);
+        }
+      }
+    }
+
+    // Only roll back if we actually executed the scheduled operations.
+    const maybeBtx = this._backendTransaction;
+    if (maybeBtx) {
+      await this._backend.rollback(maybeBtx);
+    }
+
+    queueTask(() => {
+      const event = new FakeEvent("abort", {
+        bubbles: true,
+        cancelable: false,
+      });
+      event.eventPath = [this.db];
+      this.dispatchEvent(event);
+    });
+  }
+
+  public abort() {
+    if (this._state === "committing" || this._state === "finished") {
+      throw new InvalidStateError();
+    }
+    this._state = "active";
+
+    this._abort(null);
+  }
+
+  // http://w3c.github.io/IndexedDB/#dom-idbtransaction-objectstore
+  public objectStore(name: string) {
+    if (this._state !== "active") {
+      throw new InvalidStateError();
+    }
+
+    const objectStore = this._objectStoresCache.get(name);
+    if (objectStore !== undefined) {
+      return objectStore;
+    }
+
+    return new BridgeIDBObjectStore(this, name);
+  }
+
+  // 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-asynchronously-executing-a-request
+  public _execRequestAsync(obj: RequestObj) {
+    const source = obj.source;
+    const operation = obj.operation;
+    let request = obj.hasOwnProperty("request") ? obj.request : null;
+
+    if (this._state !== "active") {
+      throw new TransactionInactiveError();
+    }
+
+    // Request should only be passed for cursors
+    if (!request) {
+      if (!source) {
+        // Special requests like indexes that just need to run some code
+        request = new BridgeIDBRequest();
+      } else {
+        request = new BridgeIDBRequest();
+        request.source = source;
+        request.transaction = (source as any).transaction;
+      }
+    }
+
+    this._requests.push({
+      operation,
+      request,
+    });
+
+    return request;
+  }
+
+  /**
+   * Actually execute the scheduled work for this transaction.
+   */
+  public async _start() {
+    if (BridgeIDBFactory.enableTracing) {
+      console.log(`TRACE: IDBTransaction._start, ${this._requests.length} 
queued`);
+    }
+    this._started = true;
+
+    console.log("beginning transaction");
+
+    if (!this._backendTransaction) {
+      this._backendTransaction = await this._backend.beginTransaction(
+        this.db._backendConnection,
+        Array.from(this._scope),
+        this.mode,
+      );
+    }
+
+    console.log("beginTransaction completed");
+
+    // Remove from request queue - cursor ones will be added back if necessary 
by cursor.continue and such
+    let operation;
+    let request;
+    while (this._requests.length > 0) {
+      const r = this._requests.shift();
+
+      // This should only be false if transaction was aborted
+      if (r && r.request.readyState !== "done") {
+        request = r.request;
+        operation = r.operation;
+        break;
+      }
+    }
+
+    if (request && operation) {
+      if (!request.source) {
+        // Special requests like indexes that just need to run some code, with 
error handling already built into
+        // operation
+        console.log("running operation without source");
+        await operation();
+      } else {
+        console.log("running operation with source");
+        let event;
+        try {
+          const result = await operation();
+          if (BridgeIDBFactory.enableTracing) {
+            console.log("TRACE: tx operation finished with success");
+          }
+          request.readyState = "done";
+          request.result = result;
+          request.error = undefined;
+
+          // 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-fire-a-success-event
+          if (this._state === "inactive") {
+            this._state = "active";
+          }
+          event = new FakeEvent("success", {
+            bubbles: false,
+            cancelable: false,
+          });
+
+          try {
+            event.eventPath = [request, this, this.db];
+            request.dispatchEvent(event);
+          } catch (err) {
+            if (this._state !== "committing") {
+              this._abort("AbortError");
+            }
+            throw err;
+          }
+        } catch (err) {
+          if (BridgeIDBFactory.enableTracing) {
+            console.log("TRACING: error during operation: ", err);
+          }
+          request.readyState = "done";
+          request.result = undefined;
+          request.error = err;
+
+          // 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-fire-an-error-event
+          if (this._state === "inactive") {
+            this._state = "active";
+          }
+          event = new FakeEvent("error", {
+            bubbles: true,
+            cancelable: true,
+          });
+
+          try {
+            event.eventPath = [this.db, this];
+            request.dispatchEvent(event);
+          } catch (err) {
+            if (this._state !== "committing") {
+              this._abort("AbortError");
+            }
+            throw err;
+          }
+          if (!event.canceled) {
+            this._abort(err.name);
+          }
+        }
+      }
+
+      // On to the next one
+      if (this._requests.length > 0) {
+        this._start();
+      } else {
+        // Give it another chance for new handlers to be set before finishing
+        queueTask(() => this._start());
+      }
+      return;
+    }
+
+    if (this._state !== "finished" && this._state !== "committing") {
+      if (BridgeIDBFactory.enableTracing) {
+        console.log("finishing transaction");
+      }
+
+      this._state = "committing";
+
+      await this._backend.commit(this._backendTransaction);
+
+      this._state = "finished";
+
+      if (!this.error) {
+        if (BridgeIDBFactory.enableTracing) {
+          console.log("dispatching 'complete' event");
+        }
+        const event = new FakeEvent("complete");
+        event.eventPath = [this, this.db];
+        this.dispatchEvent(event);
+      }
+
+      const idx = this.db._transactions.indexOf(this);
+      if (idx < 0) {
+        throw Error("invariant failed");
+      }
+      this.db._transactions.splice(idx, 1);
+
+      this._resolveWait();
+    }
+  }
+
+  public commit() {
+    if (this._state !== "active") {
+      throw new InvalidStateError();
+    }
+
+    this._state = "committing";
+    // We now just wait for auto-commit ...
+  }
+
+  public toString() {
+    return "[object IDBRequest]";
+  }
+
+  _waitDone(): Promise<void> {
+    return this._waitPromise;
+  }
+}
+
+export default BridgeIDBTransaction;
diff --git a/packages/idb-bridge/src/BridgeIDBVersionChangeEvent.ts 
b/packages/idb-bridge/src/BridgeIDBVersionChangeEvent.ts
new file mode 100644
index 00000000..6fc63ee3
--- /dev/null
+++ b/packages/idb-bridge/src/BridgeIDBVersionChangeEvent.ts
@@ -0,0 +1,41 @@
+/*
+  Copyright 2019 Florian Dold
+  Copyright 2017 Jeremy Scheff
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+  or implied. See the License for the specific language governing
+  permissions and limitations under the License.
+ */
+
+import FakeEvent from "./util/FakeEvent";
+
+class BridgeIDBVersionChangeEvent extends FakeEvent {
+    public newVersion: number | null;
+    public oldVersion: number;
+
+    constructor(
+        type: "blocked" | "success" | "upgradeneeded" | "versionchange",
+        parameters: { newVersion?: number | null; oldVersion?: number } = {},
+    ) {
+        super(type);
+
+        this.newVersion =
+            parameters.newVersion !== undefined ? parameters.newVersion : null;
+        this.oldVersion =
+            parameters.oldVersion !== undefined ? parameters.oldVersion : 0;
+    }
+
+    public toString() {
+        return "[object IDBVersionChangeEvent]";
+    }
+}
+
+export default BridgeIDBVersionChangeEvent;
diff --git a/packages/idb-bridge/src/MemoryBackend.test.ts 
b/packages/idb-bridge/src/MemoryBackend.test.ts
new file mode 100644
index 00000000..c21c2d06
--- /dev/null
+++ b/packages/idb-bridge/src/MemoryBackend.test.ts
@@ -0,0 +1,237 @@
+import test from "ava";
+import MemoryBackend from "./MemoryBackend";
+import BridgeIDBFactory from "./BridgeIDBFactory";
+import BridgeIDBRequest from "./BridgeIDBRequest";
+import BridgeIDBDatabase from "./BridgeIDBDatabase";
+import BridgeIDBTransaction from "./BridgeIDBTransaction";
+import BridgeIDBKeyRange from "./BridgeIDBKeyRange";
+import BridgeIDBCursorWithValue from "./BridgeIDBCursorWithValue";
+
+function promiseFromRequest(request: BridgeIDBRequest): Promise<any> {
+  return new Promise((resolve, reject) => {
+    request.onsuccess = () => {
+      resolve(request.result);
+    };
+    request.onerror = () => {
+      reject(request.error);
+    };
+  });
+}
+
+function promiseFromTransaction(
+  transaction: BridgeIDBTransaction,
+): Promise<any> {
+  return new Promise((resolve, reject) => {
+    console.log("attaching event handlers");
+    transaction.oncomplete = () => {
+      console.log("oncomplete was called from promise");
+      resolve();
+    };
+    transaction.onerror = () => {
+      reject();
+    };
+  });
+}
+
+test("Spec: Example 1 Part 1", async t => {
+  const backend = new MemoryBackend();
+  const idb = new BridgeIDBFactory(backend);
+
+  const request = idb.open("library");
+  request.onupgradeneeded = () => {
+    const db = request.result;
+    const store = db.createObjectStore("books", { keyPath: "isbn" });
+    const titleIndex = store.createIndex("by_title", "title", { unique: true 
});
+    const authorIndex = store.createIndex("by_author", "author");
+
+    // Populate with initial data.
+    store.put({ title: "Quarry Memories", author: "Fred", isbn: 123456 });
+    store.put({ title: "Water Buffaloes", author: "Fred", isbn: 234567 });
+    store.put({ title: "Bedrock Nights", author: "Barney", isbn: 345678 });
+  };
+
+  await promiseFromRequest(request);
+  t.pass();
+});
+
+test("Spec: Example 1 Part 2", async t => {
+  const backend = new MemoryBackend();
+  const idb = new BridgeIDBFactory(backend);
+
+  const request = idb.open("library");
+  request.onupgradeneeded = () => {
+    const db = request.result;
+    const store = db.createObjectStore("books", { keyPath: "isbn" });
+    const titleIndex = store.createIndex("by_title", "title", { unique: true 
});
+    const authorIndex = store.createIndex("by_author", "author");
+  };
+
+  const db: BridgeIDBDatabase = await promiseFromRequest(request);
+
+  t.is(db.name, "library");
+
+  const tx = db.transaction("books", "readwrite");
+  tx.oncomplete = () => {
+    console.log("oncomplete called");
+  };
+
+  const store = tx.objectStore("books");
+
+  store.put({ title: "Quarry Memories", author: "Fred", isbn: 123456 });
+  store.put({ title: "Water Buffaloes", author: "Fred", isbn: 234567 });
+  store.put({ title: "Bedrock Nights", author: "Barney", isbn: 345678 });
+
+  await promiseFromTransaction(tx);
+
+  t.pass();
+});
+
+test("Spec: Example 1 Part 3", async t => {
+  const backend = new MemoryBackend();
+  const idb = new BridgeIDBFactory(backend);
+
+  const request = idb.open("library");
+  request.onupgradeneeded = () => {
+    const db = request.result;
+    const store = db.createObjectStore("books", { keyPath: "isbn" });
+    const titleIndex = store.createIndex("by_title", "title", { unique: true 
});
+    const authorIndex = store.createIndex("by_author", "author");
+  };
+
+  const db: BridgeIDBDatabase = await promiseFromRequest(request);
+
+  t.is(db.name, "library");
+
+  const tx = db.transaction("books", "readwrite");
+
+  const store = tx.objectStore("books");
+
+  store.put({ title: "Bedrock Nights", author: "Barney", isbn: 345678 });
+  store.put({ title: "Quarry Memories", author: "Fred", isbn: 123456 });
+  store.put({ title: "Water Buffaloes", author: "Fred", isbn: 234567 });
+
+  await promiseFromTransaction(tx);
+
+  const tx2 = db.transaction("books", "readonly");
+  const store2 = tx2.objectStore("books");
+  var index2 = store2.index("by_title");
+  const request2 = index2.get("Bedrock Nights");
+  const result2: any = await promiseFromRequest(request2);
+
+  t.is(result2.author, "Barney");
+
+  const tx3 = db.transaction(["books"], "readonly");
+  const store3 = tx3.objectStore("books");
+  const index3 = store3.index("by_author");
+  const request3 = index3.openCursor(BridgeIDBKeyRange.only("Fred"));
+
+  await promiseFromRequest(request3);
+
+  let cursor: BridgeIDBCursorWithValue;
+  cursor = request3.result as BridgeIDBCursorWithValue;
+  t.is(cursor.value.author, "Fred");
+  t.is(cursor.value.isbn, 123456);
+
+  cursor.continue();
+
+  await promiseFromRequest(request3);
+
+  cursor = request3.result as BridgeIDBCursorWithValue;
+  t.is(cursor.value.author, "Fred");
+  t.is(cursor.value.isbn, 234567);
+
+  await promiseFromTransaction(tx3);
+
+  const tx4 = db.transaction("books", "readonly");
+  const store4 = tx4.objectStore("books");
+  const request4 = store4.openCursor();
+
+  await promiseFromRequest(request4);
+
+  cursor = request4.result;
+  t.is(cursor.value.isbn, 123456);
+
+  cursor.continue();
+
+  await promiseFromRequest(request4);
+
+  cursor = request4.result;
+  t.is(cursor.value.isbn, 234567);
+
+  cursor.continue();
+
+  await promiseFromRequest(request4);
+
+  cursor = request4.result;
+  t.is(cursor.value.isbn, 345678);
+
+  cursor.continue();
+  await promiseFromRequest(request4);
+
+  cursor = request4.result;
+
+  t.is(cursor, null);
+
+  const tx5 = db.transaction("books", "readonly");
+  const store5 = tx5.objectStore("books");
+  const index5 = store5.index("by_author");
+
+  const request5 = index5.openCursor(null, "next");
+
+  await promiseFromRequest(request5);
+  cursor = request5.result;
+  t.is(cursor.value.author, "Barney");
+  cursor.continue();
+
+  await promiseFromRequest(request5);
+  cursor = request5.result;
+  t.is(cursor.value.author, "Fred");
+  cursor.continue();
+
+  await promiseFromRequest(request5);
+  cursor = request5.result;
+  t.is(cursor.value.author, "Fred");
+  cursor.continue();
+
+  await promiseFromRequest(request5);
+  cursor = request5.result;
+  t.is(cursor, null);
+
+  const request6 = index5.openCursor(null, "nextunique");
+
+  await promiseFromRequest(request6);
+  cursor = request6.result;
+  t.is(cursor.value.author, "Barney");
+  cursor.continue();
+
+  await promiseFromRequest(request6);
+  cursor = request6.result;
+  t.is(cursor.value.author, "Fred");
+  t.is(cursor.value.isbn, 123456);
+  cursor.continue();
+
+  await promiseFromRequest(request6);
+  cursor = request6.result;
+  t.is(cursor, null);
+
+
+  const request7 = index5.openCursor(null, "prevunique");
+  await promiseFromRequest(request7);
+  cursor = request7.result;
+  t.is(cursor.value.author, "Fred");
+  t.is(cursor.value.isbn, 234567);
+  cursor.continue();
+
+  await promiseFromRequest(request7);
+  cursor = request7.result;
+  t.is(cursor.value.author, "Barney");
+  cursor.continue();
+
+  await promiseFromRequest(request7);
+  cursor = request7.result;
+  t.is(cursor, null);
+
+  db.close();
+
+  t.pass();
+});
diff --git a/packages/idb-bridge/src/MemoryBackend.ts 
b/packages/idb-bridge/src/MemoryBackend.ts
new file mode 100644
index 00000000..bd9b8996
--- /dev/null
+++ b/packages/idb-bridge/src/MemoryBackend.ts
@@ -0,0 +1,1156 @@
+import {
+  Backend,
+  DatabaseConnection,
+  DatabaseTransaction,
+  Schema,
+  RecordStoreRequest,
+  IndexProperties,
+  RecordGetRequest,
+  RecordGetResponse,
+  ResultLevel,
+} from "./backend-interface";
+import structuredClone from "./util/structuredClone";
+import {
+  InvalidStateError,
+  InvalidAccessError,
+  ConstraintError,
+} from "./util/errors";
+import BTree, { ISortedMap, ISortedMapF } from "./tree/b+tree";
+import BridgeIDBFactory from "./BridgeIDBFactory";
+import compareKeys from "./util/cmp";
+import extractKey from "./util/extractKey";
+import { Key, Value, KeyPath } from "./util/types";
+import { StoreKeyResult, makeStoreKeyValue } from "./util/makeStoreKeyValue";
+import getIndexKeys from "./util/getIndexKeys";
+import openPromise from "./util/openPromise";
+import BridgeIDBKeyRange from "./BridgeIDBKeyRange";
+import { resetWarningCache } from "prop-types";
+
+enum TransactionLevel {
+  Disconnected = 0,
+  Connected = 1,
+  Read = 2,
+  Write = 3,
+  VersionChange = 4,
+}
+
+interface ObjectStore {
+  originalName: string;
+  modifiedName: string | undefined;
+  originalData: ISortedMapF<Key, ObjectStoreRecord>;
+  modifiedData: ISortedMapF<Key, ObjectStoreRecord> | undefined;
+  deleted: boolean;
+  originalKeyGenerator: number;
+  modifiedKeyGenerator: number | undefined;
+}
+
+interface Index {
+  originalName: string;
+  modifiedName: string | undefined;
+  originalData: ISortedMapF<Key, IndexRecord>;
+  modifiedData: ISortedMapF<Key, IndexRecord> | undefined;
+  deleted: boolean;
+}
+
+interface Database {
+  committedObjectStores: { [name: string]: ObjectStore };
+  modifiedObjectStores: { [name: string]: ObjectStore };
+  committedIndexes: { [name: string]: Index };
+  modifiedIndexes: { [name: string]: Index };
+  committedSchema: Schema;
+  /**
+   * Was the transaction deleted during the running transaction?
+   */
+  deleted: boolean;
+
+  txLevel: TransactionLevel;
+
+  connectionCookie: string | undefined;
+}
+
+interface Connection {
+  dbName: string;
+
+  modifiedSchema: Schema | undefined;
+
+  /**
+   * Has the underlying database been deleted?
+   */
+  deleted: boolean;
+
+  /**
+   * Map from the effective name of an object store during
+   * the transaction to the real name.
+   */
+  objectStoreMap: { [currentName: string]: ObjectStore };
+  indexMap: { [currentName: string]: Index };
+}
+
+interface IndexRecord {
+  indexKey: Key;
+  primaryKeys: Key[];
+}
+
+interface ObjectStoreRecord {
+  primaryKey: Key;
+  value: Value;
+}
+
+class AsyncCondition {
+  _waitPromise: Promise<void>;
+  _resolveWaitPromise: () => void;
+  constructor() {
+    const op = openPromise<void>();
+    this._waitPromise = op.promise;
+    this._resolveWaitPromise = op.resolve;
+  }
+
+  wait(): Promise<void> {
+    return this._waitPromise;
+  }
+
+  trigger(): void {
+    this._resolveWaitPromise();
+    const op = openPromise<void>();
+    this._waitPromise = op.promise;
+    this._resolveWaitPromise = op.resolve;
+  }
+}
+
+function nextStoreKey<T>(
+  forward: boolean,
+  data: ISortedMapF<Key, ObjectStoreRecord>,
+  k: Key | undefined,
+) {
+  if (k === undefined || k === null) {
+    return undefined;
+  }
+  const res = forward ? data.nextHigherPair(k) : data.nextLowerPair(k);
+  if (!res) {
+    return undefined;
+  }
+  return res[1].primaryKey;
+}
+
+function furthestKey(
+  forward: boolean,
+  key1: Key | undefined,
+  key2: Key | undefined,
+) {
+  if (key1 === undefined) {
+    return key2;
+  }
+  if (key2 === undefined) {
+    return key1;
+  }
+  const cmpResult = compareKeys(key1, key2);
+  if (cmpResult === 0) {
+    // Same result
+    return key1;
+  }
+  if (forward && cmpResult === 1) {
+    return key1;
+  }
+  if (forward && cmpResult === -1) {
+    return key2;
+  }
+  if (!forward && cmpResult === 1) {
+    return key2;
+  }
+  if (!forward && cmpResult === -1) {
+    return key1;
+  }
+}
+
+/**
+ * Primitive in-memory backend.
+ */
+export class MemoryBackend implements Backend {
+  databases: { [name: string]: Database } = {};
+
+  connectionIdCounter = 1;
+
+  transactionIdCounter = 1;
+
+  /**
+   * Connections by connection cookie.
+   */
+  connections: { [name: string]: Connection } = {};
+
+  /**
+   * Connections by transaction (!!) cookie.  In this implementation,
+   * at most one transaction can run at the same time per connection.
+   */
+  connectionsByTransaction: { [tx: string]: Connection } = {};
+
+  /**
+   * Condition that is triggered whenever a client disconnects.
+   */
+  disconnectCond: AsyncCondition = new AsyncCondition();
+
+  /**
+   * Conditation that is triggered whenever a transaction finishes.
+   */
+  transactionDoneCond: AsyncCondition = new AsyncCondition();
+
+  enableTracing: boolean = true;
+
+  async getDatabases(): Promise<{ name: string; version: number }[]> {
+    if (this.enableTracing) {
+      console.log("TRACING: getDatabase");
+    }
+    const dbList = [];
+    for (const name in this.databases) {
+      dbList.push({
+        name,
+        version: this.databases[name].committedSchema.databaseVersion,
+      });
+    }
+    return dbList;
+  }
+
+  async deleteDatabase(tx: DatabaseTransaction, name: string): Promise<void> {
+    if (this.enableTracing) {
+      console.log("TRACING: deleteDatabase");
+    }
+    const myConn = this.connectionsByTransaction[tx.transactionCookie];
+    if (!myConn) {
+      throw Error("no connection associated with transaction");
+    }
+    const myDb = this.databases[name];
+    if (!myDb) {
+      throw Error("db not found");
+    }
+    if (myDb.committedSchema.databaseName !== name) {
+      throw Error("name does not match");
+    }
+    if (myDb.txLevel < TransactionLevel.VersionChange) {
+      throw new InvalidStateError();
+    }
+    if (myDb.connectionCookie !== tx.transactionCookie) {
+      throw new InvalidAccessError();
+    }
+    myDb.deleted = true;
+  }
+
+  async connectDatabase(name: string): Promise<DatabaseConnection> {
+    if (this.enableTracing) {
+      console.log(`TRACING: connectDatabase(${name})`);
+    }
+    const connectionId = this.connectionIdCounter++;
+    const connectionCookie = `connection-${connectionId}`;
+
+    let database = this.databases[name];
+    if (!database) {
+      const schema: Schema = {
+        databaseName: name,
+        indexes: {},
+        databaseVersion: 0,
+        objectStores: {},
+      };
+      database = {
+        committedSchema: schema,
+        deleted: false,
+        modifiedIndexes: {},
+        committedIndexes: {},
+        committedObjectStores: {},
+        modifiedObjectStores: {},
+        txLevel: TransactionLevel.Disconnected,
+        connectionCookie: undefined,
+      };
+      this.databases[name] = database;
+    }
+
+    while (database.txLevel !== TransactionLevel.Disconnected) {
+      await this.disconnectCond.wait();
+    }
+
+    database.txLevel = TransactionLevel.Connected;
+    database.connectionCookie = connectionCookie;
+
+    const myConn: Connection = {
+      dbName: name,
+      deleted: false,
+      indexMap: Object.assign({}, database.committedIndexes),
+      objectStoreMap: Object.assign({}, database.committedObjectStores),
+      modifiedSchema: structuredClone(database.committedSchema),
+    };
+
+    this.connections[connectionCookie] = myConn;
+
+    return { connectionCookie };
+  }
+
+  async beginTransaction(
+    conn: DatabaseConnection,
+    objectStores: string[],
+    mode: import("./util/types").TransactionMode,
+  ): Promise<DatabaseTransaction> {
+    if (this.enableTracing) {
+      console.log(`TRACING: beginTransaction`);
+    }
+    const transactionCookie = `tx-${this.transactionIdCounter++}`;
+    const myConn = this.connections[conn.connectionCookie];
+    if (!myConn) {
+      throw Error("connection not found");
+    }
+    const myDb = this.databases[myConn.dbName];
+    if (!myDb) {
+      throw Error("db not found");
+    }
+
+    while (myDb.txLevel !== TransactionLevel.Connected) {
+      if (this.enableTracing) {
+        console.log(`TRACING: beginTransaction -- waiting for others to 
close`);
+      }
+      await this.transactionDoneCond.wait();
+    }
+
+    if (mode === "readonly") {
+      myDb.txLevel = TransactionLevel.Read;
+    } else if (mode === "readwrite") {
+      myDb.txLevel = TransactionLevel.Write;
+    } else {
+      throw Error("unsupported transaction mode");
+    }
+
+    this.connectionsByTransaction[transactionCookie] = myConn;
+
+    return { transactionCookie };
+  }
+
+  async enterVersionChange(
+    conn: DatabaseConnection,
+    newVersion: number,
+  ): Promise<DatabaseTransaction> {
+    if (this.enableTracing) {
+      console.log(`TRACING: enterVersionChange`);
+    }
+    const transactionCookie = `tx-vc-${this.transactionIdCounter++}`;
+    const myConn = this.connections[conn.connectionCookie];
+    if (!myConn) {
+      throw Error("connection not found");
+    }
+    const myDb = this.databases[myConn.dbName];
+    if (!myDb) {
+      throw Error("db not found");
+    }
+
+    while (myDb.txLevel !== TransactionLevel.Connected) {
+      await this.transactionDoneCond.wait();
+    }
+
+    myDb.txLevel = TransactionLevel.VersionChange;
+
+    this.connectionsByTransaction[transactionCookie] = myConn;
+
+    return { transactionCookie };
+  }
+
+  async close(conn: DatabaseConnection): Promise<void> {
+    if (this.enableTracing) {
+      console.log(`TRACING: close`);
+    }
+    const myConn = this.connections[conn.connectionCookie];
+    if (!myConn) {
+      throw Error("connection not found - already closed?");
+    }
+    if (!myConn.deleted) {
+      const myDb = this.databases[myConn.dbName];
+      if (myDb.txLevel != TransactionLevel.Connected) {
+        throw Error("invalid state");
+      }
+      myDb.txLevel = TransactionLevel.Disconnected;
+    }
+    delete this.connections[conn.connectionCookie];
+    this.disconnectCond.trigger();
+  }
+
+  getSchema(dbConn: DatabaseConnection): Schema {
+    if (this.enableTracing) {
+      console.log(`TRACING: getSchema`);
+    }
+    const myConn = this.connections[dbConn.connectionCookie];
+    if (!myConn) {
+      throw Error("unknown connection");
+    }
+    const db = this.databases[myConn.dbName];
+    if (!db) {
+      throw Error("db not found");
+    }
+    if (myConn.modifiedSchema) {
+      return myConn.modifiedSchema;
+    }
+    return db.committedSchema;
+  }
+
+  renameIndex(
+    btx: DatabaseTransaction,
+    oldName: string,
+    newName: string,
+  ): void {
+    if (this.enableTracing) {
+      console.log(`TRACING: renameIndex(?, ${oldName}, ${newName})`);
+    }
+    const myConn = this.connectionsByTransaction[btx.transactionCookie];
+    if (!myConn) {
+      throw Error("unknown connection");
+    }
+    const db = this.databases[myConn.dbName];
+    if (!db) {
+      throw Error("db not found");
+    }
+    if (db.txLevel < TransactionLevel.VersionChange) {
+      throw Error("only allowed in versionchange transaction");
+    }
+    let schema = myConn.modifiedSchema;
+    if (!schema) {
+      throw Error();
+    }
+    if (schema.indexes[newName]) {
+      throw new Error("new index name already used");
+    }
+    if (!schema.indexes[oldName]) {
+      throw new Error("new index name already used");
+    }
+    const index: Index = myConn.indexMap[oldName];
+    if (!index) {
+      throw Error("old index missing in connection's index map");
+    }
+    schema.indexes[newName] = schema.indexes[newName];
+    delete schema.indexes[oldName];
+    for (const storeName in schema.objectStores) {
+      const store = schema.objectStores[storeName];
+      store.indexes = store.indexes.map(x => {
+        if (x == oldName) {
+          return newName;
+        } else {
+          return x;
+        }
+      });
+    }
+    myConn.indexMap[newName] = index;
+    delete myConn.indexMap[oldName];
+    index.modifiedName = newName;
+  }
+
+  deleteIndex(btx: DatabaseTransaction, indexName: string): void {
+    if (this.enableTracing) {
+      console.log(`TRACING: deleteIndex(${indexName})`);
+    }
+    const myConn = this.connections[btx.transactionCookie];
+    if (!myConn) {
+      throw Error("unknown connection");
+    }
+    const db = this.databases[myConn.dbName];
+    if (!db) {
+      throw Error("db not found");
+    }
+    if (db.txLevel < TransactionLevel.VersionChange) {
+      throw Error("only allowed in versionchange transaction");
+    }
+    let schema = myConn.modifiedSchema;
+    if (!schema) {
+      throw Error();
+    }
+    if (!schema.indexes[indexName]) {
+      throw new Error("index does not exist");
+    }
+    const index: Index = myConn.indexMap[indexName];
+    if (!index) {
+      throw Error("old index missing in connection's index map");
+    }
+    index.deleted = true;
+    delete schema.indexes[indexName];
+    delete myConn.indexMap[indexName];
+    for (const storeName in schema.objectStores) {
+      const store = schema.objectStores[storeName];
+      store.indexes = store.indexes.filter(x => {
+        return x !== indexName;
+      });
+    }
+  }
+
+  deleteObjectStore(btx: DatabaseTransaction, name: string): void {
+    if (this.enableTracing) {
+      console.log(`TRACING: deleteObjectStore(${name})`);
+    }
+    const myConn = this.connections[btx.transactionCookie];
+    if (!myConn) {
+      throw Error("unknown connection");
+    }
+    const db = this.databases[myConn.dbName];
+    if (!db) {
+      throw Error("db not found");
+    }
+    if (db.txLevel < TransactionLevel.VersionChange) {
+      throw Error("only allowed in versionchange transaction");
+    }
+    const schema = myConn.modifiedSchema;
+    if (!schema) {
+      throw Error();
+    }
+    const objectStoreProperties = schema.objectStores[name];
+    if (!objectStoreProperties) {
+      throw Error("object store not found");
+    }
+    const objectStore = myConn.objectStoreMap[name];
+    if (!objectStore) {
+      throw Error("object store not found in map");
+    }
+    const indexNames = objectStoreProperties.indexes;
+    for (const indexName of indexNames) {
+      this.deleteIndex(btx, indexName);
+    }
+
+    objectStore.deleted = true;
+    delete myConn.objectStoreMap[name];
+    delete schema.objectStores[name];
+  }
+
+  renameObjectStore(
+    btx: DatabaseTransaction,
+    oldName: string,
+    newName: string,
+  ): void {
+    if (this.enableTracing) {
+      console.log(`TRACING: renameObjectStore(?, ${oldName}, ${newName})`);
+    }
+
+    const myConn = this.connections[btx.transactionCookie];
+    if (!myConn) {
+      throw Error("unknown connection");
+    }
+    const db = this.databases[myConn.dbName];
+    if (!db) {
+      throw Error("db not found");
+    }
+    if (db.txLevel < TransactionLevel.VersionChange) {
+      throw Error("only allowed in versionchange transaction");
+    }
+    const schema = myConn.modifiedSchema;
+    if (!schema) {
+      throw Error();
+    }
+    if (!schema.objectStores[oldName]) {
+      throw Error("object store not found");
+    }
+    if (schema.objectStores[newName]) {
+      throw Error("new object store already exists");
+    }
+    const objectStore = myConn.objectStoreMap[oldName];
+    if (!objectStore) {
+      throw Error("object store not found in map");
+    }
+    objectStore.modifiedName = newName;
+    schema.objectStores[newName] = schema.objectStores[oldName];
+    delete schema.objectStores[oldName];
+    delete myConn.objectStoreMap[oldName];
+    myConn.objectStoreMap[newName] = objectStore;
+  }
+
+  createObjectStore(
+    btx: DatabaseTransaction,
+    name: string,
+    keyPath: string | string[] | null,
+    autoIncrement: boolean,
+  ): void {
+    if (this.enableTracing) {
+      console.log(
+        `TRACING: createObjectStore(${btx.transactionCookie}, ${name})`,
+      );
+    }
+    const myConn = this.connectionsByTransaction[btx.transactionCookie];
+    if (!myConn) {
+      throw Error("unknown connection");
+    }
+    const db = this.databases[myConn.dbName];
+    if (!db) {
+      throw Error("db not found");
+    }
+    if (db.txLevel < TransactionLevel.VersionChange) {
+      throw Error("only allowed in versionchange transaction");
+    }
+    const newObjectStore: ObjectStore = {
+      deleted: false,
+      modifiedName: undefined,
+      originalName: name,
+      modifiedData: undefined,
+      originalData: new BTree([], compareKeys),
+      modifiedKeyGenerator: undefined,
+      originalKeyGenerator: 1,
+    };
+    const schema = myConn.modifiedSchema;
+    if (!schema) {
+      throw Error("no schema for versionchange tx");
+    }
+    schema.objectStores[name] = {
+      autoIncrement,
+      keyPath,
+      indexes: [],
+    };
+    myConn.objectStoreMap[name] = newObjectStore;
+    db.modifiedObjectStores[name] = newObjectStore;
+  }
+
+  createIndex(
+    btx: DatabaseTransaction,
+    indexName: string,
+    objectStoreName: string,
+    keyPath: import("./util/types").KeyPath,
+    multiEntry: boolean,
+    unique: boolean,
+  ): void {
+    if (this.enableTracing) {
+      console.log(`TRACING: createIndex(${indexName})`);
+    }
+    const myConn = this.connectionsByTransaction[btx.transactionCookie];
+    if (!myConn) {
+      throw Error("unknown connection");
+    }
+    const db = this.databases[myConn.dbName];
+    if (!db) {
+      throw Error("db not found");
+    }
+    if (db.txLevel < TransactionLevel.VersionChange) {
+      throw Error("only allowed in versionchange transaction");
+    }
+    const indexProperties: IndexProperties = {
+      keyPath,
+      multiEntry,
+      unique,
+    };
+    const newIndex: Index = {
+      deleted: false,
+      modifiedData: undefined,
+      modifiedName: undefined,
+      originalData: new BTree([], compareKeys),
+      originalName: indexName,
+    };
+    myConn.indexMap[indexName] = newIndex;
+    db.modifiedIndexes[indexName] = newIndex;
+    const schema = myConn.modifiedSchema;
+    if (!schema) {
+      throw Error("no schema in versionchange tx");
+    }
+    const objectStoreProperties = schema.objectStores[objectStoreName];
+    if (!objectStoreProperties) {
+      throw Error("object store not found");
+    }
+    objectStoreProperties.indexes.push(indexName);
+    schema.indexes[indexName] = indexProperties;
+
+    const objectStore = myConn.objectStoreMap[objectStoreName];
+    if (!objectStore) {
+      throw Error("object store does not exist");
+    }
+
+    const storeData = objectStore.modifiedData || objectStore.originalData;
+
+    storeData.forEach((v, k) => {
+      this.insertIntoIndex(newIndex, k, v.value, indexProperties);
+    });
+  }
+
+  async deleteRecord(
+    btx: DatabaseTransaction,
+    objectStoreName: string,
+    range: import("./BridgeIDBKeyRange").default,
+  ): Promise<void> {
+    if (this.enableTracing) {
+      console.log(`TRACING: deleteRecord`);
+    }
+    const myConn = this.connectionsByTransaction[btx.transactionCookie];
+    if (!myConn) {
+      throw Error("unknown connection");
+    }
+    const db = this.databases[myConn.dbName];
+    if (!db) {
+      throw Error("db not found");
+    }
+    if (db.txLevel < TransactionLevel.Write) {
+      throw Error("only allowed in write transaction");
+    }
+    throw Error("not implemented");
+  }
+
+  async getRecords(
+    btx: DatabaseTransaction,
+    req: RecordGetRequest,
+  ): Promise<RecordGetResponse> {
+    if (this.enableTracing) {
+      console.log(`TRACING: getRecords`);
+      console.log("query", req);
+    }
+    const myConn = this.connectionsByTransaction[btx.transactionCookie];
+    if (!myConn) {
+      throw Error("unknown connection");
+    }
+    const db = this.databases[myConn.dbName];
+    if (!db) {
+      throw Error("db not found");
+    }
+    if (db.txLevel < TransactionLevel.Read) {
+      throw Error("only allowed while running a transaction");
+    }
+    const objectStore = myConn.objectStoreMap[req.objectStoreName];
+    if (!objectStore) {
+      throw Error("object store not found");
+    }
+
+    let range;
+    if (req.range == null || req.range === undefined) {
+      range = new BridgeIDBKeyRange(undefined, undefined, true, true);
+    } else {
+      range = req.range;
+    }
+
+    let numResults = 0;
+    let indexKeys: Key[] = [];
+    let primaryKeys: Key[] = [];
+    let values: Value[] = [];
+
+    const forward: boolean =
+      req.direction === "next" || req.direction === "nextunique";
+    const unique: boolean =
+      req.direction === "prevunique" || req.direction === "nextunique";
+
+    const storeData = objectStore.modifiedData || objectStore.originalData;
+
+    const haveIndex = req.indexName !== undefined;
+
+    if (haveIndex) {
+      const index = myConn.indexMap[req.indexName!];
+      const indexData = index.modifiedData || index.originalData;
+      let indexPos = req.lastIndexPosition;
+
+      if (indexPos === undefined) {
+        // First time we iterate!  So start at the beginning (lower/upper)
+        // of our allowed range.
+        indexPos = forward ? range.lower : range.upper;
+      }
+
+      let primaryPos = req.lastObjectStorePosition;
+
+      // We might have to advance the index key further!
+      if (req.advanceIndexKey !== undefined) {
+        const compareResult = compareKeys(req.advanceIndexKey, indexPos);
+        if ((forward && compareResult > 0) || (!forward && compareResult > 0)) 
{
+          indexPos = req.advanceIndexKey;
+        } else if (compareResult == 0 && req.advancePrimaryKey !== undefined) {
+          // index keys are the same, so advance the primary key
+          if (primaryPos === undefined) {
+            primaryPos = req.advancePrimaryKey;
+          } else {
+            const primCompareResult = compareKeys(
+              req.advancePrimaryKey,
+              primaryPos,
+            );
+            if (
+              (forward && primCompareResult > 0) ||
+              (!forward && primCompareResult < 0)
+            ) {
+              primaryPos = req.advancePrimaryKey;
+            }
+          }
+        }
+      }
+
+      if (indexPos === undefined || indexPos === null) {
+        indexPos = forward ? indexData.minKey() : indexData.maxKey();
+      }
+
+      let indexEntry: IndexRecord | undefined;
+      indexEntry = indexData.get(indexPos);
+      if (!indexEntry) {
+        const res = indexData.nextHigherPair(indexPos);
+        if (res) {
+          indexEntry = res[1];
+        }
+      }
+
+      let primkeySubPos = 0;
+
+      // Sort out the case where the index key is the same, so we have
+      // to get the prev/next primary key
+      if (
+        indexEntry !== undefined &&
+        req.lastIndexPosition !== undefined &&
+        compareKeys(indexEntry.indexKey, req.lastIndexPosition) === 0
+      ) {
+        let pos = forward ? 0 : indexEntry.primaryKeys.length - 1;
+        console.log("number of primary keys", indexEntry.primaryKeys.length);
+        console.log("start pos is", pos);
+        // Advance past the lastObjectStorePosition
+        do {
+          const cmpResult = compareKeys(
+            req.lastObjectStorePosition,
+            indexEntry.primaryKeys[pos],
+          );
+          console.log("cmp result is", cmpResult);
+          if ((forward && cmpResult < 0) || (!forward && cmpResult > 0)) {
+            break;
+          }
+          pos += forward ? 1 : -1;
+          console.log("now pos is", pos);
+        } while (pos >= 0 && pos < indexEntry.primaryKeys.length);
+
+        // Make sure we're at least at advancedPrimaryPos
+        while (
+          primaryPos !== undefined &&
+          pos >= 0 &&
+          pos < indexEntry.primaryKeys.length
+        ) {
+          const cmpResult = compareKeys(
+            primaryPos,
+            indexEntry.primaryKeys[pos],
+          );
+          if ((forward && cmpResult <= 0) || (!forward && cmpResult >= 0)) {
+            break;
+          }
+          pos += forward ? 1 : -1;
+        }
+        primkeySubPos = pos;
+      } else if (indexEntry !== undefined) {
+        primkeySubPos = forward ? 0 : indexEntry.primaryKeys.length - 1;
+      }
+
+      console.log("subPos=", primkeySubPos);
+      console.log("indexPos=", indexPos);
+
+      while (1) {
+        if (req.limit != 0 && numResults == req.limit) {
+          break;
+        }
+        if (indexPos === undefined) {
+          break;
+        }
+        if (!range.includes(indexPos)) {
+          break;
+        }
+        if (indexEntry === undefined) {
+          break;
+        }
+        if (
+          primkeySubPos < 0 ||
+          primkeySubPos >= indexEntry.primaryKeys.length
+        ) {
+          const res = forward
+            ? indexData.nextHigherPair(indexPos)
+            : indexData.nextLowerPair(indexPos);
+          if (res) {
+            indexPos = res[1].indexKey;
+            indexEntry = res[1];
+            primkeySubPos = forward ? 0 : indexEntry.primaryKeys.length - 1;
+          } else {
+            break;
+          }
+        }
+
+        // Skip repeated index keys if unique results are requested.
+        let skip = false;
+        if (unique) {
+          if (
+            indexKeys.length > 0 &&
+            compareKeys(
+              indexEntry.indexKey,
+              indexKeys[indexKeys.length - 1],
+            ) === 0
+          ) {
+            skip = true;
+          }
+          if (
+            req.lastIndexPosition !== undefined &&
+            compareKeys(indexPos, req.lastIndexPosition) === 0
+          ) {
+            skip = true;
+          }
+        }
+        if (!skip) {
+          console.log(`not skipping!, subPos=${primkeySubPos}`);
+          indexKeys.push(indexEntry.indexKey);
+          primaryKeys.push(indexEntry.primaryKeys[primkeySubPos]);
+          numResults++;
+        } else {
+          console.log("skipping!");
+        }
+        primkeySubPos += forward ? 1 : -1;
+      }
+
+      // Now we can collect the values based on the primary keys,
+      // if requested.
+      if (req.resultLevel === ResultLevel.Full) {
+        for (let i = 0; i < numResults; i++) {
+          console.log("getting value for index", i);
+          console.log("with key", primaryKeys[i]);
+          const result = storeData.get(primaryKeys[i]);
+          if (!result) {
+            throw Error("invariant violated");
+          }
+          values.push(result);
+        }
+      }
+    } else {
+      // only based on object store, no index involved, phew!
+      let storePos = req.lastObjectStorePosition;
+      if (storePos === undefined) {
+        storePos = forward ? range.lower : range.upper;
+      }
+
+      if (req.advanceIndexKey !== undefined) {
+        throw Error("unsupported request");
+      }
+
+      storePos = furthestKey(forward, req.advancePrimaryKey, storePos);
+
+      if (storePos !== null && storePos !== undefined) {
+        // Advance store position if we are either still at the last returned
+        // store key, or if we are currently not on a key.
+        const storeEntry = storeData.get(storePos);
+        console.log("store entry:", storeEntry);
+        if (
+          !storeEntry ||
+          (req.lastObjectStorePosition !== undefined &&
+            compareKeys(req.lastObjectStorePosition, storePos) === 0)
+        ) {
+          storePos = storeData.nextHigherKey(storePos);
+        }
+      } else {
+        storePos = forward ? storeData.minKey() : storeData.maxKey();
+        console.log("setting starting store store pos to", storePos);
+      }
+
+      while (1) {
+        if (req.limit != 0 && numResults == req.limit) {
+          break;
+        }
+        if (storePos === null || storePos === undefined) {
+          break;
+        }
+        if (!range.includes(storePos)) {
+          break;
+        }
+
+        const res = storeData.get(storePos);
+
+        if (res === undefined) {
+          break;
+        }
+
+        if (req.resultLevel >= ResultLevel.OnlyKeys) {
+          primaryKeys.push(structuredClone(storePos));
+        }
+
+        if (req.resultLevel >= ResultLevel.Full) {
+          values.push(res);
+        }
+
+        numResults++;
+        storePos = nextStoreKey(forward, storeData, storePos);
+      }
+    }
+    if (this.enableTracing) {
+      console.log(`TRACING: getRecords got ${numResults} results`);
+    }
+    return {
+      count: numResults,
+      indexKeys:
+        req.resultLevel >= ResultLevel.OnlyKeys && haveIndex
+          ? indexKeys
+          : undefined,
+      primaryKeys:
+        req.resultLevel >= ResultLevel.OnlyKeys ? primaryKeys : undefined,
+      values: req.resultLevel >= ResultLevel.Full ? values : undefined,
+    };
+  }
+
+  async storeRecord(
+    btx: DatabaseTransaction,
+    storeReq: RecordStoreRequest,
+  ): Promise<void> {
+    if (this.enableTracing) {
+      console.log(`TRACING: storeRecord`);
+    }
+    const myConn = this.connectionsByTransaction[btx.transactionCookie];
+    if (!myConn) {
+      throw Error("unknown connection");
+    }
+    const db = this.databases[myConn.dbName];
+    if (!db) {
+      throw Error("db not found");
+    }
+    if (db.txLevel < TransactionLevel.Write) {
+      throw Error("only allowed while running a transaction");
+    }
+    const schema = myConn.modifiedSchema
+      ? myConn.modifiedSchema
+      : db.committedSchema;
+
+    const objectStore = myConn.objectStoreMap[storeReq.objectStoreName];
+
+    const storeKeyResult: StoreKeyResult = makeStoreKeyValue(
+      storeReq.value,
+      storeReq.key,
+      objectStore.modifiedKeyGenerator || objectStore.originalKeyGenerator,
+      schema.objectStores[storeReq.objectStoreName].autoIncrement,
+      schema.objectStores[storeReq.objectStoreName].keyPath,
+    );
+    let key = storeKeyResult.key;
+    let value = storeKeyResult.value;
+    objectStore.modifiedKeyGenerator = storeKeyResult.updatedKeyGenerator;
+
+    if (!objectStore.modifiedData) {
+      objectStore.modifiedData = objectStore.originalData;
+    }
+    const modifiedData = objectStore.modifiedData;
+    const hasKey = modifiedData.has(key);
+    if (hasKey && !storeReq.overwrite) {
+      throw Error("refusing to overwrite");
+    }
+
+    objectStore.modifiedData = modifiedData.with(key, value, true);
+
+    for (const indexName of schema.objectStores[storeReq.objectStoreName]
+      .indexes) {
+      const index = myConn.indexMap[indexName];
+      if (!index) {
+        throw Error("index referenced by object store does not exist");
+      }
+      const indexProperties = schema.indexes[indexName];
+      this.insertIntoIndex(index, key, value, indexProperties);
+    }
+  }
+
+  private insertIntoIndex(
+    index: Index,
+    primaryKey: Key,
+    value: Value,
+    indexProperties: IndexProperties,
+  ): void {
+    if (this.enableTracing) {
+      console.log(
+        `insertIntoIndex(${index.modifiedName || index.originalName})`,
+      );
+    }
+    let indexData = index.modifiedData || index.originalData;
+    const indexKeys = getIndexKeys(
+      value,
+      indexProperties.keyPath,
+      indexProperties.multiEntry,
+    );
+    for (const indexKey of indexKeys) {
+      const existingRecord = indexData.get(indexKey);
+      if (existingRecord) {
+        if (indexProperties.unique) {
+          throw new ConstraintError();
+        } else {
+          const newIndexRecord = {
+            indexKey: indexKey,
+            primaryKeys: [primaryKey]
+              .concat(existingRecord.primaryKeys)
+              .sort(compareKeys),
+          };
+          index.modifiedData = indexData.with(indexKey, newIndexRecord, true);
+        }
+      } else {
+        const newIndexRecord: IndexRecord = {
+          indexKey: indexKey,
+          primaryKeys: [primaryKey],
+        };
+        index.modifiedData = indexData.with(indexKey, newIndexRecord, true);
+      }
+    }
+  }
+
+  async rollback(btx: DatabaseTransaction): Promise<void> {
+    if (this.enableTracing) {
+      console.log(`TRACING: rollback`);
+    }
+    const myConn = this.connectionsByTransaction[btx.transactionCookie];
+    if (!myConn) {
+      throw Error("unknown connection");
+    }
+    const db = this.databases[myConn.dbName];
+    if (!db) {
+      throw Error("db not found");
+    }
+    if (db.txLevel < TransactionLevel.Read) {
+      throw Error("only allowed while running a transaction");
+    }
+    db.modifiedIndexes = {};
+    db.modifiedObjectStores = {};
+    db.txLevel = TransactionLevel.Connected;
+    myConn.modifiedSchema = structuredClone(db.committedSchema);
+    myConn.indexMap = Object.assign({}, db.committedIndexes);
+    myConn.objectStoreMap = Object.assign({}, db.committedObjectStores);
+    for (const indexName in db.committedIndexes) {
+      const index = db.committedIndexes[indexName];
+      index.deleted = false;
+      index.modifiedData = undefined;
+      index.modifiedName = undefined;
+    }
+    for (const objectStoreName in db.committedObjectStores) {
+      const objectStore = db.committedObjectStores[objectStoreName];
+      objectStore.deleted = false;
+      objectStore.modifiedData = undefined;
+      objectStore.modifiedName = undefined;
+      objectStore.modifiedKeyGenerator = undefined;
+    }
+    delete this.connectionsByTransaction[btx.transactionCookie];
+    this.transactionDoneCond.trigger();
+  }
+
+  async commit(btx: DatabaseTransaction): Promise<void> {
+    if (this.enableTracing) {
+      console.log(`TRACING: commit`);
+    }
+    const myConn = this.connectionsByTransaction[btx.transactionCookie];
+    if (!myConn) {
+      throw Error("unknown connection");
+    }
+    const db = this.databases[myConn.dbName];
+    if (!db) {
+      throw Error("db not found");
+    }
+    if (db.txLevel < TransactionLevel.Read) {
+      throw Error("only allowed while running a transaction");
+    }
+
+    db.committedSchema = myConn.modifiedSchema || db.committedSchema;
+    db.txLevel = TransactionLevel.Connected;
+
+    db.committedIndexes = {};
+    db.committedObjectStores = {};
+    db.modifiedIndexes = {};
+    db.committedObjectStores = {};
+
+    for (const indexName in myConn.indexMap) {
+      const index = myConn.indexMap[indexName];
+      index.deleted = false;
+      index.originalData = index.modifiedData || index.originalData;
+      index.originalName = index.modifiedName || index.originalName;
+      db.committedIndexes[indexName] = index;
+    }
+
+    for (const objectStoreName in myConn.objectStoreMap) {
+      const objectStore = myConn.objectStoreMap[objectStoreName];
+      objectStore.deleted = false;
+      objectStore.originalData =
+        objectStore.modifiedData || objectStore.originalData;
+      objectStore.originalName =
+        objectStore.modifiedName || objectStore.originalName;
+      if (objectStore.modifiedKeyGenerator !== undefined) {
+        objectStore.originalKeyGenerator = objectStore.modifiedKeyGenerator;
+      }
+      db.committedObjectStores[objectStoreName] = objectStore;
+    }
+
+    myConn.indexMap = Object.assign({}, db.committedIndexes);
+    myConn.objectStoreMap = Object.assign({}, db.committedObjectStores);
+
+    delete this.connectionsByTransaction[btx.transactionCookie];
+    this.transactionDoneCond.trigger();
+  }
+}
+
+export default MemoryBackend;
diff --git a/packages/idb-bridge/src/backend-interface.ts 
b/packages/idb-bridge/src/backend-interface.ts
new file mode 100644
index 00000000..ab093d9c
--- /dev/null
+++ b/packages/idb-bridge/src/backend-interface.ts
@@ -0,0 +1,174 @@
+import {
+  TransactionMode,
+  Value,
+  BridgeIDBCursorDirection,
+  Key,
+  KeyPath,
+  BridgeIDBDatabaseInfo,
+} from "./util/types";
+import BridgeIDBKeyRange from "./BridgeIDBKeyRange";
+
+export interface ObjectStoreProperties {
+  keyPath: KeyPath | null;
+  autoIncrement: boolean;
+  indexes: string[];
+}
+
+export interface IndexProperties {
+  keyPath: KeyPath;
+  multiEntry: boolean;
+  unique: boolean;
+}
+
+export interface Schema {
+  databaseName: string;
+  databaseVersion: number;
+  objectStores: { [name: string]: ObjectStoreProperties };
+  indexes: { [name: string]: IndexProperties };
+}
+
+export interface DatabaseConnection {
+  connectionCookie: string;
+}
+
+export interface DatabaseTransaction {
+  transactionCookie: string;
+}
+
+export enum ResultLevel {
+  OnlyCount,
+  OnlyKeys,
+  Full,
+}
+
+export interface RecordGetRequest {
+  direction: BridgeIDBCursorDirection;
+  objectStoreName: string;
+  indexName: string | undefined;
+  /**
+   * The range of keys to return.
+   * If indexName is defined, the range refers to the index keys.
+   * Otherwise it refers to the object store keys.
+   */
+  range: BridgeIDBKeyRange | undefined;
+  /**
+   * Last cursor position in terms of the index key.
+   * Can only be specified if indexName is defined and
+   * lastObjectStorePosition is defined.
+   * 
+   * Must either be undefined or within range.
+   */
+  lastIndexPosition?: Key;
+  /**
+   * Last position in terms of the object store key.
+   */
+  lastObjectStorePosition?: Key;
+  /**
+   * If specified, the index key of the results must be
+   * greater or equal to advanceIndexKey.
+   * 
+   * Only applicable if indexName is specified.
+   */
+  advanceIndexKey?: Key;
+  /**
+   * If specified, the primary key of the results must be greater
+   * or equal to advancePrimaryKey.
+   */
+  advancePrimaryKey?: Key;
+  /**
+   * Maximum number of resuts to return.
+   * If -1, return all available results
+   */
+  limit: number;
+  resultLevel: ResultLevel;
+}
+
+export interface RecordGetResponse {
+  values: Value[] | undefined;
+  indexKeys: Key[] | undefined;
+  primaryKeys: Key[] | undefined;
+  count: number;
+}
+
+export interface RecordStoreRequest {
+  objectStoreName: string;
+  value: Value;
+  key: Key | undefined;
+  overwrite: boolean;
+}
+
+export interface Backend {
+  getDatabases(): Promise<BridgeIDBDatabaseInfo[]>;
+
+  connectDatabase(name: string): Promise<DatabaseConnection>;
+
+  beginTransaction(
+    conn: DatabaseConnection,
+    objectStores: string[],
+    mode: TransactionMode,
+  ): Promise<DatabaseTransaction>;
+
+  enterVersionChange(
+    conn: DatabaseConnection,
+    newVersion: number,
+  ): Promise<DatabaseTransaction>;
+
+  /**
+   * Even though the standard interface for indexedDB doesn't require
+   * the client to run deleteDatabase in a version transaction, there is
+   * implicitly one running. 
+   */
+  deleteDatabase(btx: DatabaseTransaction, name: string): Promise<void>;
+
+  close(db: DatabaseConnection): Promise<void>;
+
+  getSchema(db: DatabaseConnection): Schema;
+
+  renameIndex(btx: DatabaseTransaction, oldName: string, newName: string): 
void;
+
+  deleteIndex(btx: DatabaseTransaction, indexName: string): void;
+
+  rollback(btx: DatabaseTransaction): Promise<void>;
+
+  commit(btx: DatabaseTransaction): Promise<void>;
+
+  deleteObjectStore(btx: DatabaseTransaction, name: string): void;
+
+  createObjectStore(
+    btx: DatabaseTransaction,
+    name: string,
+    keyPath: string | string[] | null,
+    autoIncrement: boolean,
+  ): void;
+
+  renameObjectStore(
+    btx: DatabaseTransaction,
+    oldName: string,
+    newName: string,
+  ): void;
+
+  createIndex(
+    btx: DatabaseTransaction,
+    indexName: string,
+    objectStoreName: string,
+    keyPath: KeyPath,
+    multiEntry: boolean,
+    unique: boolean,
+  ): void;
+
+  deleteRecord(
+    btx: DatabaseTransaction,
+    objectStoreName: string,
+    range: BridgeIDBKeyRange,
+  ): Promise<void>;
+
+  getRecords(
+    btx: DatabaseTransaction,
+    req: RecordGetRequest,
+  ): Promise<RecordGetResponse>;
+
+  storeRecord(
+    btx: DatabaseTransaction,
+    storeReq: RecordStoreRequest,
+  ): Promise<void>;
+}
diff --git a/packages/idb-bridge/src/tree/b+tree.ts 
b/packages/idb-bridge/src/tree/b+tree.ts
new file mode 100644
index 00000000..01187cdc
--- /dev/null
+++ b/packages/idb-bridge/src/tree/b+tree.ts
@@ -0,0 +1,1351 @@
+/*
+Copyright (c) 2018 David Piepgrass
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+SPDX-License-Identifier: MIT
+*/
+
+// Original repository: https://github.com/qwertie/btree-typescript
+
+
+import { ISortedMap, ISortedMapF } from './interfaces';
+export {
+  ISetSource, ISetSink, ISet, ISetF, ISortedSetSource, ISortedSet, ISortedSetF,
+  IMapSource, IMapSink, IMap, IMapF, ISortedMapSource, ISortedMap, ISortedMapF
+} from './interfaces';
+
+export type EditRangeResult<V,R=number> = {value?:V, break?:R, 
delete?:boolean};
+
+type index = number;
+
+// Informative microbenchmarks & stuff:
+// http://www.jayconrod.com/posts/52/a-tour-of-v8-object-representation (very 
educational)
+// 
https://blog.mozilla.org/luke/2012/10/02/optimizing-javascript-variable-access/ 
(local vars are faster than properties)
+// 
http://benediktmeurer.de/2017/12/13/an-introduction-to-speculative-optimization-in-v8/
 (other stuff)
+// https://jsperf.com/js-in-operator-vs-alternatives (avoid 'in' operator; 
`.p!==undefined` faster than `hasOwnProperty('p')` in all browsers)
+// https://jsperf.com/instanceof-vs-typeof-vs-constructor-vs-member (speed of 
type tests varies wildly across browsers)
+// https://jsperf.com/detecting-arrays-new (a.constructor===Array is best 
across browsers, assuming a is an object)
+// https://jsperf.com/shallow-cloning-methods (a constructor is faster than 
Object.create; hand-written clone faster than Object.assign)
+// https://jsperf.com/ways-to-fill-an-array (slice-and-replace is fastest)
+// https://jsperf.com/math-min-max-vs-ternary-vs-if (Math.min/max is slow on 
Edge)
+// https://jsperf.com/array-vs-property-access-speed (v.x/v.y is faster than 
a[0]/a[1] in major browsers IF hidden class is constant)
+// https://jsperf.com/detect-not-null-or-undefined (`x==null` slightly slower 
than `x===null||x===undefined` on all browsers)
+// Overall, microbenchmarks suggest Firefox is the fastest browser for 
JavaScript and Edge is the slowest.
+// Lessons from 
https://v8project.blogspot.com/2017/09/elements-kinds-in-v8.html:
+//   - Avoid holes in arrays. Avoid `new Array(N)`, it will be "holey" 
permanently.
+//   - Don't read outside bounds of an array (it scans prototype chain).
+//   - Small integer arrays are stored differently from doubles
+//   - Adding non-numbers to an array deoptimizes it permanently into a 
general array
+//   - Objects can be used like arrays (e.g. have length property) but are 
slower
+//   - V8 source (NewElementsCapacity in src/objects.h): arrays grow by 50% + 
16 elements
+
+/** Compares two numbers, strings, arrays of numbers/strings, Dates,
+ *  or objects that have a valueOf() method returning a number or string. 
+ *  Optimized for numbers. Returns 1 if a>b, -1 if a<b, and 0 if a===b.
+ */
+export function defaultComparator(a: any, b: any) {
+  var c = a - b;
+  if (c === c) return c; // a & b are number
+  // General case (c is NaN): string / arrays / Date / incomparable things
+  if (a) a = a.valueOf();
+  if (b) b = b.valueOf();
+  return a < b ? -1 : a > b ? 1 : a == b ? 0 : c;   
+};
+
+/**
+ * A reasonably fast collection of key-value pairs with a powerful API. 
+ * Largely compatible with the standard Map. BTree is a B+ tree data structure,
+ * so the collection is sorted by key.
+ * 
+ * B+ trees tend to use memory more efficiently than hashtables such as the
+ * standard Map, especially when the collection contains a large number of 
+ * items. However, maintaining the sort order makes them modestly slower: 
+ * O(log size) rather than O(1). This B+ tree implementation supports O(1)
+ * fast cloning. It also supports freeze(), which can be used to ensure that
+ * a BTree is not changed accidentally.
+ * 
+ * Confusingly, the ES6 Map.forEach(c) method calls c(value,key) instead of
+ * c(key,value), in contrast to other methods such as set() and entries()
+ * which put the key first. I can only assume that the order was reversed on 
+ * the theory that users would usually want to examine values and ignore keys.
+ * BTree's forEach() therefore works the same way, but a second method 
+ * `.forEachPair((key,value)=>{...})` is provided which sends you the key
+ * first and the value second; this method is slightly faster because it is 
+ * the "native" for-each method for this class.
+ * 
+ * Out of the box, BTree supports keys that are numbers, strings, arrays of 
+ * numbers/strings, Date, and objects that have a valueOf() method returning a 
+ * number or string. Other data types, such as arrays of Date or custom
+ * objects, require a custom comparator, which you must pass as the second 
+ * argument to the constructor (the first argument is an optional list of 
+ * initial items). Symbols cannot be used as keys because they are unordered
+ * (one Symbol is never "greater" or "less" than another).
+ * 
+ * @example
+ * Given a {name: string, age: number} object, you can create a tree sorted by
+ * name and then by age like this:
+ *   
+ *     var tree = new BTree(undefined, (a, b) => {
+ *       if (a.name > b.name)
+ *         return 1; // Return a number >0 when a > b
+ *       else if (a.name < b.name)
+ *         return -1; // Return a number <0 when a < b
+ *       else // names are equal (or incomparable)
+ *         return a.age - b.age; // Return >0 when a.age > b.age
+ *     });
+ * 
+ *     tree.set({name:"Bill", age:17}, "happy");
+ *     tree.set({name:"Fran", age:40}, "busy & stressed");
+ *     tree.set({name:"Bill", age:55}, "recently laid off");
+ *     tree.forEachPair((k, v) => {
+ *       console.log(`Name: ${k.name} Age: ${k.age} Status: ${v}`);
+ *     });
+ * 
+ * @description
+ * The "range" methods (`forEach, forRange, editRange`) will return the number
+ * of elements that were scanned. In addition, the callback can return 
{break:R}
+ * to stop early and return R from the outer function.
+ * 
+ * - TODO: Test performance of preallocating values array at max size
+ * - TODO: Add fast initialization when a sorted array is provided to 
constructor
+ * 
+ * For more documentation see https://github.com/qwertie/btree-typescript
+ *
+ * Are you a C# developer? You might like the similar data structures I made 
for C#: 
+ * BDictionary, BList, etc. See http://core.loyc.net/collections/
+ * 
+ * @author David Piepgrass
+ */
+export default class BTree<K=any, V=any> implements ISortedMapF<K,V>, 
ISortedMap<K,V>
+{
+  private _root: BNode<K, V> = EmptyLeaf as BNode<K,V>;
+  _size: number = 0;
+  _maxNodeSize: number;
+  _compare: (a:K, b:K) => number;
+  
+  /**
+   * Initializes an empty B+ tree.
+   * @param compare Custom function to compare pairs of elements in the tree.
+   *   This is not required for numbers, strings and arrays of numbers/strings.
+   * @param entries A set of key-value pairs to initialize the tree
+   * @param maxNodeSize Branching factor (maximum items or children per node)
+   *   Must be in range 4..256. If undefined or <4 then default is used; if 
>256 then 256.
+   */
+  public constructor(entries?: [K,V][], compare?: (a: K, b: K) => number, 
maxNodeSize?: number) {
+    this._maxNodeSize = maxNodeSize! >= 4 ? Math.min(maxNodeSize!, 256) : 32;
+    this._compare = compare || defaultComparator;
+    if (entries)
+      this.setPairs(entries);
+  }
+  
+  // ES6 Map<K,V> methods ///////////////////////////////////////////////////
+
+  /** Gets the number of key-value pairs in the tree. */
+  get size() { return this._size; }
+  /** Gets the number of key-value pairs in the tree. */
+  get length() { return this._size; }
+  /** Returns true iff the tree contains no key-value pairs. */
+  get isEmpty() { return this._size === 0; }
+
+  /** Releases the tree so that its size is 0. */
+  clear() {
+    this._root = EmptyLeaf as BNode<K,V>;
+    this._size = 0;
+  }
+
+  forEach(callback: (v:V, k:K, tree:BTree<K,V>) => void, thisArg?: any): 
number;
+
+  /** Runs a function for each key-value pair, in order from smallest to 
+   *  largest key. For compatibility with ES6 Map, the argument order to
+   *  the callback is backwards: value first, then key. Call forEachPair 
+   *  instead to receive the key as the first argument.
+   * @param thisArg If provided, this parameter is assigned as the `this`
+   *        value for each callback.
+   * @returns the number of values that were sent to the callback,
+   *        or the R value if the callback returned {break:R}. */
+  forEach<R=number>(callback: (v:V, k:K, tree:BTree<K,V>) => {break?:R}|void, 
thisArg?: any): R|number {
+    if (thisArg !== undefined)
+      callback = callback.bind(thisArg);
+    return this.forEachPair((k, v) => callback(v, k, this));
+  }
+
+  /** Runs a function for each key-value pair, in order from smallest to 
+   *  largest key. The callback can return {break:R} (where R is any value
+   *  except undefined) to stop immediately and return R from forEachPair.
+   * @param onFound A function that is called for each key-value pair. This 
+   *        function can return {break:R} to stop early with result R.
+   *        The reason that you must return {break:R} instead of simply R 
+   *        itself is for consistency with editRange(), which allows 
+   *        multiple actions, not just breaking.
+   * @param initialCounter This is the value of the third argument of 
+   *        `onFound` the first time it is called. The counter increases 
+   *        by one each time `onFound` is called. Default value: 0
+   * @returns the number of pairs sent to the callback (plus initialCounter,
+   *        if you provided one). If the callback returned {break:R} then
+   *        the R value is returned instead. */
+  forEachPair<R=number>(callback: (k:K, v:V, counter:number) => 
{break?:R}|void, initialCounter?: number): R|number {
+    var low = this.minKey(), high = this.maxKey();
+    return this.forRange(low!, high!, true, callback, initialCounter);
+  }
+
+  /**
+   * Finds a pair in the tree and returns the associated value.
+   * @param defaultValue a value to return if the key was not found.
+   * @returns the value, or defaultValue if the key was not found.
+   * @description Computational complexity: O(log size)
+   */
+  get(key: K, defaultValue?: V): V | undefined {
+    return this._root.get(key, defaultValue, this);
+  }
+  
+  /**
+   * Adds or overwrites a key-value pair in the B+ tree.
+   * @param key the key is used to determine the sort order of
+   *        data in the tree.
+   * @param value data to associate with the key (optional)
+   * @param overwrite Whether to overwrite an existing key-value pair 
+   *        (default: true). If this is false and there is an existing
+   *        key-value pair then this method has no effect.
+   * @returns true if a new key-value pair was added.
+   * @description Computational complexity: O(log size)
+   * Note: when overwriting a previous entry, the key is updated
+   * as well as the value. This has no effect unless the new key
+   * has data that does not affect its sort order.
+   */
+  set(key: K, value: V, overwrite?: boolean): boolean { 
+    if (this._root.isShared)
+      this._root = this._root.clone();
+    var result = this._root.set(key, value, overwrite, this);
+    if (result === true || result === false)
+      return result;
+    // Root node has split, so create a new root node.
+    this._root = new BNodeInternal<K,V>([this._root, result]);
+    return true;
+  }
+
+  /**
+   * Returns true if the key exists in the B+ tree, false if not.
+   * Use get() for best performance; use has() if you need to
+   * distinguish between "undefined value" and "key not present".
+   * @param key Key to detect
+   * @description Computational complexity: O(log size)
+   */
+  has(key: K): boolean { 
+    return this.forRange(key, key, true, undefined) !== 0;
+  }
+
+  /**
+   * Removes a single key-value pair from the B+ tree.
+   * @param key Key to find
+   * @returns true if a pair was found and removed, false otherwise.
+   * @description Computational complexity: O(log size)
+   */
+  delete(key: K): boolean {
+    return this.editRange(key, key, true, DeleteRange) !== 0;
+  }
+
+  // Clone-mutators /////////////////////////////////////////////////////////
+
+  /** Returns a copy of the tree with the specified key set (the value is 
undefined). */
+  with(key: K): BTree<K,V|undefined>;
+  /** Returns a copy of the tree with the specified key-value pair set. */
+  with<V2>(key: K, value: V2, overwrite?: boolean): BTree<K,V|V2>;
+  with<V2>(key: K, value?: V2, overwrite?: boolean): BTree<K,V|V2|undefined> {
+    let nu = this.clone() as BTree<K,V|V2|undefined>;
+    return nu.set(key, value, overwrite) || overwrite ? nu : this;
+  }
+
+  /** Returns a copy of the tree with the specified key-value pairs set. */
+  withPairs<V2>(pairs: [K,V|V2][], overwrite: boolean): BTree<K,V|V2> {
+    let nu = this.clone() as BTree<K,V|V2>;
+    return nu.setPairs(pairs, overwrite) !== 0 || overwrite ? nu : this;
+  }
+
+  /** Returns a copy of the tree with the specified keys present. 
+   *  @param keys The keys to add. If a key is already present in the tree,
+   *         neither the existing key nor the existing value is modified.
+   *  @param returnThisIfUnchanged if true, returns this if all keys already 
+   *  existed. Performance note: due to the architecture of this class, all
+   *  node(s) leading to existing keys are cloned even if the collection is
+   *  ultimately unchanged.
+  */
+  withKeys(keys: K[], returnThisIfUnchanged?: boolean): BTree<K,V|undefined> {
+    let nu = this.clone() as BTree<K,V|undefined>, changed = false;
+    for (var i = 0; i < keys.length; i++)
+      changed = nu.set(keys[i], undefined, false) || changed;
+    return returnThisIfUnchanged && !changed ? this : nu;
+  }
+
+  /** Returns a copy of the tree with the specified key removed. 
+   * @param returnThisIfUnchanged if true, returns this if the key didn't 
exist.
+   *  Performance note: due to the architecture of this class, node(s) leading
+   *  to where the key would have been stored are cloned even when the key
+   *  turns out not to exist and the collection is unchanged.
+   */
+  without(key: K, returnThisIfUnchanged?: boolean): BTree<K,V> {
+    return this.withoutRange(key, key, true, returnThisIfUnchanged);
+  }
+
+  /** Returns a copy of the tree with the specified keys removed.
+   * @param returnThisIfUnchanged if true, returns this if none of the keys
+   *  existed. Performance note: due to the architecture of this class,
+   *  node(s) leading to where the key would have been stored are cloned
+   *  even when the key turns out not to exist.
+   */
+  withoutKeys(keys: K[], returnThisIfUnchanged?: boolean): BTree<K,V> {
+    let nu = this.clone();
+    return nu.deleteKeys(keys) || !returnThisIfUnchanged ? nu : this;
+  }
+
+  /** Returns a copy of the tree with the specified range of keys removed. */
+  withoutRange(low: K, high: K, includeHigh: boolean, returnThisIfUnchanged?: 
boolean): BTree<K,V> {
+    let nu = this.clone();
+    if (nu.deleteRange(low, high, includeHigh) === 0 && returnThisIfUnchanged)
+      return this;
+    return nu;
+  }
+
+  /** Returns a copy of the tree with pairs removed whenever the callback 
+   *  function returns false. `where()` is a synonym for this method. */
+  filter(callback: (k:K,v:V,counter:number) => boolean, 
returnThisIfUnchanged?: boolean): BTree<K,V> {
+    var nu = this.greedyClone();
+    var del: any;
+    nu.editAll((k,v,i) => {
+      if (!callback(k, v, i)) return del = Delete;
+    });
+    if (!del && returnThisIfUnchanged)
+      return this;
+    return nu;
+  }
+
+  /** Returns a copy of the tree with all values altered by a callback 
function. */
+  mapValues<R>(callback: (v:V,k:K,counter:number) => R): BTree<K,R> {
+    var tmp = {} as {value:R};
+    var nu = this.greedyClone();
+    nu.editAll((k,v,i) => {
+      return tmp.value = callback(v, k, i), tmp as any;
+    });
+    return nu as any as BTree<K,R>;
+  }
+
+  /** Performs a reduce operation like the `reduce` method of `Array`. 
+   *  It is used to combine all pairs into a single value, or perform 
+   *  conversions. `reduce` is best understood by example. For example,
+   *  `tree.reduce((P, pair) => P * pair[0], 1)` multiplies all keys 
+   *  together. It means "start with P=1, and for each pair multiply 
+   *  it by the key in pair[0]". Another example would be converting 
+   *  the tree to a Map (in this example, note that M.set returns M):
+   *  
+   *  var M = tree.reduce((M, pair) => M.set(pair[0],pair[1]), new Map())
+   *  
+   *  **Note**: the same array is sent to the callback on every iteration.
+   */
+  reduce<R>(callback: 
(previous:R,currentPair:[K,V],counter:number,tree:BTree<K,V>) => R, 
initialValue: R): R;
+  reduce<R>(callback: 
(previous:R|undefined,currentPair:[K,V],counter:number,tree:BTree<K,V>) => R): 
R|undefined;
+  reduce<R>(callback: 
(previous:R|undefined,currentPair:[K,V],counter:number,tree:BTree<K,V>) => R, 
initialValue?: R): R|undefined {
+    let i = 0, p = initialValue;
+    var it = this.entries(this.minKey(), ReusedArray), next;
+    while (!(next = it.next()).done)
+      p = callback(p, next.value, i++, this);
+    return p;
+  }
+
+  // Iterator methods ///////////////////////////////////////////////////////
+
+  /** Returns an iterator that provides items in order (ascending order if
+   *  the collection's comparator uses ascending order, as is the default.)
+   *  @param lowestKey First key to be iterated, or undefined to start at
+   *         minKey(). If the specified key doesn't exist then iteration
+   *         starts at the next higher key (according to the comparator).
+   *  @param reusedArray Optional array used repeatedly to store key-value
+   *         pairs, to avoid creating a new array on every iteration.
+   */
+  entries(lowestKey?: K, reusedArray?: (K|V)[]): IterableIterator<[K,V]> {
+    var info = this.findPath(lowestKey);
+    if (info === undefined) return iterator<[K,V]>();
+    var {nodequeue, nodeindex, leaf} = info;
+    var state = reusedArray !== undefined ? 1 : 0;
+    var i = (lowestKey === undefined ? -1 : leaf.indexOf(lowestKey, 0, 
this._compare) - 1);
+
+    return iterator<[K,V]>(() => {
+      jump: for (;;) {
+        switch(state) {
+          case 0:
+            if (++i < leaf.keys.length)
+              return {done: false, value: [leaf.keys[i], leaf.values[i]]};
+            state = 2;
+            continue;
+          case 1:
+            if (++i < leaf.keys.length) {
+              reusedArray![0] = leaf.keys[i], reusedArray![1] = leaf.values[i];
+              return {done: false, value: reusedArray as [K,V]};
+            }
+            state = 2;
+          case 2:
+            // Advance to the next leaf node
+            for (var level = -1;;) {
+              if (++level >= nodequeue.length) {
+                state = 3; continue jump;
+              }
+              if (++nodeindex[level] < nodequeue[level].length)
+                break;
+            }
+            for (; level > 0; level--) {
+              nodequeue[level-1] = (nodequeue[level][nodeindex[level]] as 
BNodeInternal<K,V>).children;
+              nodeindex[level-1] = 0;
+            }
+            leaf = nodequeue[0][nodeindex[0]];
+            i = -1;
+            state = reusedArray !== undefined ? 1 : 0;
+            continue;
+          case 3:
+            return {done: true, value: undefined};
+        }
+      }
+    });
+  }
+
+  /** Returns an iterator that provides items in reversed order.
+   *  @param highestKey Key at which to start iterating, or undefined to 
+   *         start at minKey(). If the specified key doesn't exist then 
iteration
+   *         starts at the next lower key (according to the comparator).
+   *  @param reusedArray Optional array used repeatedly to store key-value
+   *         pairs, to avoid creating a new array on every iteration.
+   *  @param skipHighest Iff this flag is true and the highestKey exists in the
+   *         collection, the pair matching highestKey is skipped, not iterated.
+   */
+  entriesReversed(highestKey?: K, reusedArray?: (K|V)[], skipHighest?: 
boolean): IterableIterator<[K,V]> {
+    if ((highestKey = highestKey || this.maxKey()) === undefined)
+      return iterator<[K,V]>(); // collection is empty
+    var {nodequeue,nodeindex,leaf} = this.findPath(highestKey) || 
this.findPath(this.maxKey())!;
+    check(!nodequeue[0] || leaf === nodequeue[0][nodeindex[0]], "wat!");
+    var i = leaf.indexOf(highestKey, 0, this._compare);
+    if (!(skipHighest || this._compare(leaf.keys[i], highestKey) > 0))
+      i++;
+    var state = reusedArray !== undefined ? 1 : 0;
+
+    return iterator<[K,V]>(() => {
+      jump: for (;;) {
+        switch(state) {
+          case 0:
+            if (--i >= 0)
+              return {done: false, value: [leaf.keys[i], leaf.values[i]]};
+            state = 2;
+            continue;
+          case 1:
+            if (--i >= 0) {
+              reusedArray![0] = leaf.keys[i], reusedArray![1] = leaf.values[i];
+              return {done: false, value: reusedArray as [K,V]};
+            }
+            state = 2;
+          case 2:
+            // Advance to the next leaf node
+            for (var level = -1;;) {
+              if (++level >= nodequeue.length) {
+                state = 3; continue jump;
+              }
+              if (--nodeindex[level] >= 0)
+                break;
+            }
+            for (; level > 0; level--) {
+              nodequeue[level-1] = (nodequeue[level][nodeindex[level]] as 
BNodeInternal<K,V>).children;
+              nodeindex[level-1] = nodequeue[level-1].length-1;
+            }
+            leaf = nodequeue[0][nodeindex[0]];
+            i = leaf.keys.length;
+            state = reusedArray !== undefined ? 1 : 0;
+            continue;
+          case 3:
+            return {done: true, value: undefined};
+        }
+      }
+    });
+  }
+
+  /* Used by entries() and entriesReversed() to prepare to start iterating.
+   * It develops a "node queue" for each non-leaf level of the tree.
+   * Levels are numbered "bottom-up" so that level 0 is a list of leaf 
+   * nodes from a low-level non-leaf node. The queue at a given level L
+   * consists of nodequeue[L] which is the children of a BNodeInternal, 
+   * and nodeindex[L], the current index within that child list, such
+   * such that nodequeue[L-1] === nodequeue[L][nodeindex[L]].children.
+   * (However inside this function the order is reversed.)
+   */
+  private findPath(key?: K): { nodequeue: BNode<K,V>[][], nodeindex: number[], 
leaf: BNode<K,V> } | undefined
+  {
+    var nextnode = this._root;
+    var nodequeue: BNode<K,V>[][], nodeindex: number[];
+
+    if (nextnode.isLeaf) {
+      nodequeue = EmptyArray, nodeindex = EmptyArray; // avoid allocations
+    } else {
+      nodequeue = [], nodeindex = [];
+      for (var d = 0; !nextnode.isLeaf; d++) {
+        nodequeue[d] = (nextnode as BNodeInternal<K,V>).children;
+        nodeindex[d] = key === undefined ? 0 : nextnode.indexOf(key, 0, 
this._compare);
+        if (nodeindex[d] >= nodequeue[d].length)
+          return; // first key > maxKey()
+        nextnode = nodequeue[d][nodeindex[d]];
+      }
+      nodequeue.reverse();
+      nodeindex.reverse();
+    }
+    return {nodequeue, nodeindex, leaf:nextnode};
+  }
+
+  /** Returns a new iterator for iterating the keys of each pair in ascending 
order. 
+   *  @param firstKey: Minimum key to include in the output. */
+  keys(firstKey?: K): IterableIterator<K> {
+    var it = this.entries(firstKey, ReusedArray);
+    return iterator<K>(() => {
+      var n: IteratorResult<any> = it.next();
+      if (n.value) n.value = n.value[0];
+      return n;
+    });
+  }
+  
+  /** Returns a new iterator for iterating the values of each pair in order by 
key. 
+   *  @param firstKey: Minimum key whose associated value is included in the 
output. */
+  values(firstKey?: K): IterableIterator<V> {
+    var it = this.entries(firstKey, ReusedArray);
+    return iterator<V>(() => {
+      var n: IteratorResult<any> = it.next();
+      if (n.value) n.value = n.value[1];
+      return n;
+    });
+  }
+
+  // Additional methods /////////////////////////////////////////////////////
+
+  /** Returns the maximum number of children/values before nodes will split. */
+  get maxNodeSize() {
+    return this._maxNodeSize;
+  }
+
+  /** Gets the lowest key in the tree. Complexity: O(log size) */
+  minKey(): K | undefined { return this._root.minKey(); }
+  
+  /** Gets the highest key in the tree. Complexity: O(1) */
+  maxKey(): K | undefined { return this._root.maxKey(); }
+
+  /** Quickly clones the tree by marking the root node as shared. 
+   *  Both copies remain editable. When you modify either copy, any
+   *  nodes that are shared (or potentially shared) between the two
+   *  copies are cloned so that the changes do not affect other copies.
+   *  This is known as copy-on-write behavior, or "lazy copying". */
+  clone(): BTree<K,V> {
+    this._root.isShared = true;
+    var result = new BTree<K,V>(undefined, this._compare, this._maxNodeSize);
+    result._root = this._root;
+    result._size = this._size;
+    return result;
+  }
+
+  /** Performs a greedy clone, immediately duplicating any nodes that are 
+   *  not currently marked as shared, in order to avoid marking any nodes
+   *  as shared.
+   *  @param force Clone all nodes, even shared ones.
+   */
+  greedyClone(force?: boolean): BTree<K,V> {
+    var result = new BTree<K,V>(undefined, this._compare, this._maxNodeSize);
+    result._root = this._root.greedyClone(force);
+    result._size = this._size;
+    return result;
+  }
+
+  /** Gets an array filled with the contents of the tree, sorted by key */
+  toArray(maxLength: number = 0x7FFFFFFF): [K,V][] {
+    let min = this.minKey(), max = this.maxKey();
+    if (min !== undefined)
+      return this.getRange(min, max!, true, maxLength)
+    return [];
+  }
+
+  /** Gets an array of all keys, sorted */
+  keysArray() {
+    var results: K[] = [];
+    this._root.forRange(this.minKey()!, this.maxKey()!, true, false, this, 0, 
+      (k,v) => { results.push(k); });
+    return results;
+  }
+  
+  /** Gets an array of all values, sorted by key */
+  valuesArray() {
+    var results: V[] = [];
+    this._root.forRange(this.minKey()!, this.maxKey()!, true, false, this, 0,
+      (k,v) => { results.push(v); });
+    return results;
+  }
+
+  /** Gets a string representing the tree's data based on toArray(). */
+  toString() {
+    return this.toArray().toString();
+  }
+
+  /** Stores a key-value pair only if the key doesn't already exist in the 
tree. 
+   * @returns true if a new key was added
+  */
+  setIfNotPresent(key: K, value: V): boolean {
+    return this.set(key, value, false);
+  }
+
+  /** Returns the next pair whose key is larger than the specified key (or 
undefined if there is none) */
+  nextHigherPair(key: K): [K,V]|undefined {
+    var it = this.entries(key, ReusedArray);
+    var r = it.next();
+    if (!r.done && this._compare(r.value[0], key) <= 0)
+      r = it.next();
+    return r.value;
+  }
+  
+  /** Returns the next key larger than the specified key (or undefined if 
there is none) */
+  nextHigherKey(key: K): K|undefined {
+    var p = this.nextHigherPair(key);
+    return p ? p[0] : p;
+  }
+
+  /** Returns the next pair whose key is smaller than the specified key (or 
undefined if there is none) */
+  nextLowerPair(key: K): [K,V]|undefined {
+    var it = this.entriesReversed(key, ReusedArray, true);
+    return it.next().value;
+  }
+  
+  /** Returns the next key smaller than the specified key (or undefined if 
there is none) */
+  nextLowerKey(key: K): K|undefined {
+    var p = this.nextLowerPair(key);
+    return p ? p[0] : p;
+  }
+
+  /** Edits the value associated with a key in the tree, if it already exists. 
+   * @returns true if the key existed, false if not.
+  */
+  changeIfPresent(key: K, value: V): boolean { 
+    return this.editRange(key, key, true, (k,v) => ({value})) !== 0;
+  }
+
+  /**
+   * Builds an array of pairs from the specified range of keys, sorted by key.
+   * Each returned pair is also an array: pair[0] is the key, pair[1] is the 
value.
+   * @param low The first key in the array will be greater than or equal to 
`low`.
+   * @param high This method returns when a key larger than this is reached.
+   * @param includeHigh If the `high` key is present, its pair will be included
+   *        in the output if and only if this parameter is true. Note: if the
+   *        `low` key is present, it is always included in the output.
+   * @param maxLength Length limit. getRange will stop scanning the tree when 
+   *                  the array reaches this size.
+   * @description Computational complexity: O(result.length + log size)
+   */
+  getRange(low: K, high: K, includeHigh?: boolean, maxLength: number = 
0x3FFFFFF): [K,V][] {
+    var results: [K,V][] = [];
+    this._root.forRange(low, high, includeHigh, false, this, 0, (k,v) => {
+      results.push([k,v])
+      return results.length > maxLength ? Break : undefined;
+    });
+    return results;
+  }
+
+  /** Adds all pairs from a list of key-value pairs.
+   * @param pairs Pairs to add to this tree. If there are duplicate keys, 
+   *        later pairs currently overwrite earlier ones (e.g. [[0,1],[0,7]] 
+   *        associates 0 with 7.)
+   * @param overwrite Whether to overwrite pairs that already exist (if false,
+   *        pairs[i] is ignored when the key pairs[i][0] already exists.)
+   * @returns The number of pairs added to the collection.
+   * @description Computational complexity: O(pairs.length * log(size + 
pairs.length))
+   */
+  setPairs(pairs: [K,V][], overwrite?: boolean): number {
+    var added = 0;
+    for (var i = 0; i < pairs.length; i++)
+      if (this.set(pairs[i][0], pairs[i][1], overwrite))
+        added++;
+    return added;
+  }
+
+  forRange(low: K, high: K, includeHigh: boolean, onFound?: 
(k:K,v:V,counter:number) => void, initialCounter?: number): number;
+
+  /**
+   * Scans the specified range of keys, in ascending order by key.
+   * Note: the callback `onFound` must not insert or remove items in the
+   * collection. Doing so may cause incorrect data to be sent to the 
+   * callback afterward.
+   * @param low The first key scanned will be greater than or equal to `low`.
+   * @param high Scanning stops when a key larger than this is reached.
+   * @param includeHigh If the `high` key is present, `onFound` is called for
+   *        that final pair if and only if this parameter is true.
+   * @param onFound A function that is called for each key-value pair. This 
+   *        function can return {break:R} to stop early with result R.
+   * @param initialCounter Initial third argument of onFound. This value 
+   *        increases by one each time `onFound` is called. Default: 0
+   * @returns The number of values found, or R if the callback returned 
+   *        `{break:R}` to stop early.
+   * @description Computational complexity: O(number of items scanned + log 
size)
+   */
+  forRange<R=number>(low: K, high: K, includeHigh: boolean, onFound?: 
(k:K,v:V,counter:number) => {break?:R}|void, initialCounter?: number): R|number 
{
+    var r = this._root.forRange(low, high, includeHigh, false, this, 
initialCounter || 0, onFound);
+    return typeof r === "number" ? r : r.break!;
+  }
+
+  /**
+   * Scans and potentially modifies values for a subsequence of keys.
+   * Note: the callback `onFound` should ideally be a pure function. 
+   *   Specfically, it must not insert items, call clone(), or change 
+   *   the collection except via return value; out-of-band editing may
+   *   cause an exception or may cause incorrect data to be sent to
+   *   the callback (duplicate or missed items). It must not cause a 
+   *   clone() of the collection, otherwise the clone could be modified
+   *   by changes requested by the callback.
+   * @param low The first key scanned will be greater than or equal to `low`.
+   * @param high Scanning stops when a key larger than this is reached.
+   * @param includeHigh If the `high` key is present, `onFound` is called for
+   *        that final pair if and only if this parameter is true.
+   * @param onFound A function that is called for each key-value pair. This 
+   *        function can return `{value:v}` to change the value associated 
+   *        with the current key, `{delete:true}` to delete the current pair,
+   *        `{break:R}` to stop early with result R, or it can return nothing
+   *        (undefined or {}) to cause no effect and continue iterating.
+   *        `{break:R}` can be combined with one of the other two commands.
+   *        The third argument `counter` is the number of items iterated 
+   *        previously; it equals 0 when `onFound` is called the first time.
+   * @returns The number of values scanned, or R if the callback returned 
+   *        `{break:R}` to stop early.
+   * @description 
+   *   Computational complexity: O(number of items scanned + log size)
+   *   Note: if the tree has been cloned with clone(), any shared
+   *   nodes are copied before `onFound` is called. This takes O(n) time 
+   *   where n is proportional to the amount of shared data scanned.
+   */
+  editRange<R=V>(low: K, high: K, includeHigh: boolean, onFound: 
(k:K,v:V,counter:number) => EditRangeResult<V,R>|void, initialCounter?: 
number): R|number {
+    var root = this._root;
+    if (root.isShared)
+      this._root = root = root.clone();
+    try {
+      var r = root.forRange(low, high, includeHigh, true, this, initialCounter 
|| 0, onFound);
+      return typeof r === "number" ? r : r.break!;
+    } finally {
+      while (root.keys.length <= 1 && !root.isLeaf)
+        this._root = root = root.keys.length === 0 ? EmptyLeaf :
+                    (root as any as BNodeInternal<K,V>).children[0];
+    }
+  }
+
+  /** Same as `editRange` except that the callback is called for all pairs. */
+  editAll<R=V>(onFound: (k:K,v:V,counter:number) => EditRangeResult<V,R>|void, 
initialCounter?: number): R|number {
+    return this.editRange(this.minKey()!, this.maxKey()!, true, onFound, 
initialCounter);
+  }
+
+  /**
+   * Removes a range of key-value pairs from the B+ tree.
+   * @param low The first key scanned will be greater than or equal to `low`.
+   * @param high Scanning stops when a key larger than this is reached.
+   * @param includeHigh Specifies whether the `high` key, if present, is 
deleted.
+   * @returns The number of key-value pairs that were deleted.
+   * @description Computational complexity: O(log size + number of items 
deleted)
+   */
+  deleteRange(low: K, high: K, includeHigh: boolean): number {
+    return this.editRange(low, high, includeHigh, DeleteRange);
+  }
+
+  /** Deletes a series of keys from the collection. */
+  deleteKeys(keys: K[]): number {
+    for (var i = 0, r = 0; i < keys.length; i++)
+      if (this.delete(keys[i]))
+        r++;
+    return r;
+  }
+
+  /** Gets the height of the tree: the number of internal nodes between the 
+   *  BTree object and its leaf nodes (zero if there are no internal nodes). */
+  get height(): number {
+    for (var node = this._root, h = -1; node != null; h++)
+      node = (node as any).children;
+    return h;
+  }
+
+  /** Makes the object read-only to ensure it is not accidentally modified.
+   *  Freezing does not have to be permanent; unfreeze() reverses the effect.
+   *  This is accomplished by replacing mutator functions with a function 
+   *  that throws an Error. Compared to using a property (e.g. this.isFrozen) 
+   *  this implementation gives better performance in non-frozen BTrees.
+   */
+  freeze() {
+    var t = this as any;
+    // Note: all other mutators ultimately call set() or editRange() 
+    //       so we don't need to override those others.
+    t.clear = t.set = t.editRange = function() {
+      throw new Error("Attempted to modify a frozen BTree");
+    };
+  }
+
+  /** Ensures mutations are allowed, reversing the effect of freeze(). */
+  unfreeze() {
+    delete this.clear;
+    delete this.set;
+    delete this.editRange;
+  }
+
+  /** Returns true if the tree appears to be frozen. */
+  get isFrozen() {
+    return this.hasOwnProperty('editRange');
+  }
+
+  /** Scans the tree for signs of serious bugs (e.g. this.size doesn't match
+   *  number of elements, internal nodes not caching max element properly...)
+   *  Computational complexity: O(number of nodes), i.e. O(size). This method
+   *  skips the most expensive test - whether all keys are sorted - but it
+   *  does check that maxKey() of the children of internal nodes are sorted. */
+  checkValid() {
+    var size = this._root.checkValid(0, this);
+    check(size === this.size, "size mismatch: counted ", size, "but stored", 
this.size);
+  }
+}
+
+declare const Symbol: any;
+if (Symbol && Symbol.iterator) // iterator is equivalent to entries()
+  (BTree as any).prototype[Symbol.iterator] = BTree.prototype.entries;
+(BTree as any).prototype.where = BTree.prototype.filter;
+(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> {
+  var result: any = { next };
+  if (Symbol && Symbol.iterator)
+    result[Symbol.iterator] = function() { return this; };
+  return result;
+}
+  
+
+/** Leaf node / base class. **************************************************/
+class BNode<K,V> {
+  // If this is an internal node, _keys[i] is the highest key in children[i].
+  keys: K[];
+  values: V[];
+  isShared: true | undefined;
+  get isLeaf() { return (this as any).children === undefined; }
+  
+  constructor(keys: K[] = [], values?: V[]) {
+    this.keys = keys;
+    this.values = values || undefVals as any[];
+    this.isShared = undefined;
+  }
+
+  // Shared methods /////////////////////////////////////////////////////////
+
+  maxKey() {
+    return this.keys[this.keys.length-1];
+  }
+
+  // If key not found, returns i^failXor where i is the insertion index.
+  // Callers that don't care whether there was a match will set failXor=0.
+  indexOf(key: K, failXor: number, cmp: (a:K, b:K) => number): index {
+    // TODO: benchmark multiple search strategies
+    const keys = this.keys;
+    var lo = 0, hi = keys.length, mid = hi >> 1;
+    while(lo < hi) {
+      var c = cmp(keys[mid], key);
+      if (c < 0)
+        lo = mid + 1;
+      else if (c > 0) // key < keys[mid]
+        hi = mid;
+      else if (c === 0)
+        return mid;
+      else {
+        // c is NaN or otherwise invalid
+        if (key === key) // at least the search key is not NaN
+          return keys.length;
+        else
+          throw new Error("BTree: NaN was used as a key");
+      }
+      mid = (lo + hi) >> 1;
+    }
+    return mid ^ failXor;
+
+    // Unrolled version: benchmarks show same speed, not worth using
+    /*var i = 1, c: number = 0, sum = 0;
+    if (keys.length >= 4) {
+      i = 3;
+      if (keys.length >= 8) {
+        i = 7;
+        if (keys.length >= 16) {
+          i = 15;
+          if (keys.length >= 32) {
+            i = 31;
+            if (keys.length >= 64) {
+              i = 127;
+              i += (c = i < keys.length ? cmp(keys[i], key) : 1) < 0 ? 64 : 
-64;
+              sum += c;
+              i += (c = i < keys.length ? cmp(keys[i], key) : 1) < 0 ? 32 : 
-32;
+              sum += c;
+            }
+            i += (c = i < keys.length ? cmp(keys[i], key) : 1) < 0 ? 16 : -16;
+            sum += c;
+          }
+          i += (c = i < keys.length ? cmp(keys[i], key) : 1) < 0 ? 8 : -8;
+          sum += c;
+        }
+        i += (c = i < keys.length ? cmp(keys[i], key) : 1) < 0 ? 4 : -4;
+        sum += c;
+      }
+      i += (c = i < keys.length ? cmp(keys[i], key) : 1) < 0 ? 2 : -2;
+      sum += c;
+    }
+    i += (c = i < keys.length ? cmp(keys[i], key) : 1) < 0 ? 1 : -1;
+    c = i < keys.length ? cmp(keys[i], key) : 1;
+    sum += c;
+    if (c < 0) {
+      ++i;
+      c = i < keys.length ? cmp(keys[i], key) : 1;
+      sum += c;
+    }
+    if (sum !== sum) {
+      if (key === key) // at least the search key is not NaN
+        return keys.length ^ failXor;
+      else
+        throw new Error("BTree: NaN was used as a key");
+    }
+    return c === 0 ? i : i ^ failXor;*/
+  }
+
+  // Leaf Node: misc //////////////////////////////////////////////////////////
+
+  minKey() {
+    return this.keys[0];
+  }
+
+  clone(): BNode<K,V> {
+    var v = this.values;
+    return new BNode<K,V>(this.keys.slice(0), v === undefVals ? v : 
v.slice(0));
+  }
+
+  greedyClone(force?: boolean): BNode<K,V> {
+    return this.isShared && !force ? this : this.clone();
+  }
+
+  get(key: K, defaultValue: V|undefined, tree: BTree<K,V>): V|undefined {
+    var i = this.indexOf(key, -1, tree._compare);
+    return i < 0 ? defaultValue : this.values[i];
+  }
+
+  checkValid(depth: number, tree: BTree<K,V>): number {
+    var kL = this.keys.length, vL = this.values.length;
+    check(this.values === undefVals ? kL <= vL : kL === vL,
+      "keys/values length mismatch: depth", depth, "with lengths", kL, vL);
+    // Note: we don't check for "node too small" because sometimes a node
+    // can legitimately have size 1. This occurs if there is a batch 
+    // deletion, leaving a node of size 1, and the siblings are full so
+    // it can't be merged with adjacent nodes. However, the parent will
+    // verify that the average node size is at least half of the maximum.
+    check(depth == 0 || kL > 0, "empty leaf at depth", depth);
+    return kL;
+  }
+
+  // Leaf Node: set & node splitting //////////////////////////////////////////
+
+  set(key: K, value: V, overwrite: boolean|undefined, tree: BTree<K,V>): 
boolean|BNode<K,V> { 
+    var i = this.indexOf(key, -1, tree._compare);
+    if (i < 0) {
+      // key does not exist yet
+      i = ~i;
+      tree._size++;
+      
+      if (this.keys.length < tree._maxNodeSize) {
+        return this.insertInLeaf(i, key, value, tree);
+      } else {
+        // This leaf node is full and must split
+        var newRightSibling = this.splitOffRightSide(), target: BNode<K,V> = 
this;
+        if (i > this.keys.length) {
+          i -= this.keys.length;
+          target = newRightSibling;
+        }
+        target.insertInLeaf(i, key, value, tree);
+        return newRightSibling;
+      }
+    } else {
+      // Key already exists
+      if (overwrite !== false) {
+        if (value !== undefined)
+          this.reifyValues();
+        // usually this is a no-op, but some users may wish to edit the key
+        this.keys[i] = key;
+        this.values[i] = value;
+      }
+      return false;
+    }
+  }
+
+  reifyValues() {
+    if (this.values === undefVals)
+      return this.values = this.values.slice(0, this.keys.length);
+    return this.values;
+  }
+
+  insertInLeaf(i: index, key: K, value: V, tree: BTree<K,V>) {
+    this.keys.splice(i, 0, key);
+    if (this.values === undefVals) {
+      while (undefVals.length < tree._maxNodeSize)
+        undefVals.push(undefined);
+      if (value === undefined) {
+        return true;
+      } else {
+        this.values = undefVals.slice(0, this.keys.length - 1);
+      }
+    }
+    this.values.splice(i, 0, value);
+    return true;
+  }
+  
+  takeFromRight(rhs: BNode<K,V>) {
+    // Reminder: parent node must update its copy of key for this node
+    // assert: neither node is shared
+    // assert rhs.keys.length > (maxNodeSize/2 && this.keys.length<maxNodeSize)
+    var v = this.values;
+    if (rhs.values === undefVals) {
+      if (v !== undefVals)
+        v.push(undefined as any);
+    } else {
+      v = this.reifyValues();
+      v.push(rhs.values.shift()!);
+    }
+    this.keys.push(rhs.keys.shift()!);
+  }
+
+  takeFromLeft(lhs: BNode<K,V>) {
+    // Reminder: parent node must update its copy of key for this node
+    // assert: neither node is shared
+    // assert rhs.keys.length > (maxNodeSize/2 && this.keys.length<maxNodeSize)
+    var v = this.values;
+    if (lhs.values === undefVals) {
+      if (v !== undefVals)
+        v.unshift(undefined as any);
+    } else {
+      v = this.reifyValues();
+      v.unshift(lhs.values.pop()!);
+    }
+    this.keys.unshift(lhs.keys.pop()!);
+  }
+
+  splitOffRightSide(): BNode<K,V> {
+    // Reminder: parent node must update its copy of key for this node
+    var half = this.keys.length >> 1, keys = this.keys.splice(half);
+    var values = this.values === undefVals ? undefVals : 
this.values.splice(half);
+    return new BNode<K,V>(keys, values);
+  }
+
+  // Leaf Node: scanning & deletions //////////////////////////////////////////
+
+  forRange<R>(low: K, high: K, includeHigh: boolean|undefined, editMode: 
boolean, tree: BTree<K,V>, count: number,
+              onFound?: (k:K, v:V, counter:number) => 
EditRangeResult<V,R>|void): EditRangeResult<V,R>|number {
+    var cmp = tree._compare;
+    var iLow, iHigh;
+    if (high === low) {
+      if (!includeHigh)
+        return count;
+      iHigh = (iLow = this.indexOf(low, -1, cmp)) + 1;
+      if (iLow < 0)
+        return count;
+    } else {
+      iLow = this.indexOf(low, 0, cmp);
+      iHigh = this.indexOf(high, -1, cmp);
+      if (iHigh < 0)
+        iHigh = ~iHigh;
+      else if (includeHigh === true)
+        iHigh++;
+    }
+    var keys = this.keys, values = this.values;
+    if (onFound !== undefined) {
+      for(var i = iLow; i < iHigh; i++) {
+        var key = keys[i];
+        var result = onFound(key, values[i], count++);
+        if (result !== undefined) {
+          if (editMode === true) {
+            if (key !== keys[i] || this.isShared === true)
+              throw new Error("BTree illegally changed or cloned in 
editRange");
+            if (result.delete) {
+              this.keys.splice(i, 1);
+              if (this.values !== undefVals)
+                this.values.splice(i, 1);
+              tree._size--;
+              i--;
+              iHigh--;
+            } else if (result.hasOwnProperty('value')) {
+              values![i] = result.value!;
+            }
+          }
+          if (result.break !== undefined)
+            return result;
+        }
+      }
+    } else
+      count += iHigh - iLow;
+    return count;
+  }
+
+  /** Adds entire contents of right-hand sibling (rhs is left unchanged) */
+  mergeSibling(rhs: BNode<K,V>, _: number) {
+    this.keys.push.apply(this.keys, rhs.keys);
+    if (this.values === undefVals) {
+      if (rhs.values === undefVals)
+        return;
+      this.values = this.values.slice(0, this.keys.length);
+    }
+    this.values.push.apply(this.values, rhs.reifyValues());
+  }
+}
+
+/** Internal node (non-leaf node) ********************************************/
+class BNodeInternal<K,V> extends BNode<K,V> {
+  // Note: conventionally B+ trees have one fewer key than the number of 
+  // children, but I find it easier to keep the array lengths equal: each
+  // keys[i] caches the value of children[i].maxKey().
+  children: BNode<K,V>[];
+
+  constructor(children: BNode<K,V>[], keys?: K[]) {
+    if (!keys) {
+      keys = [];
+      for (var i = 0; i < children.length; i++)
+        keys[i] = children[i].maxKey();
+    }
+    super(keys);
+    this.children = children;
+  }
+
+  clone(): BNode<K,V> {
+    var children = this.children.slice(0);
+    for (var i = 0; i < children.length; i++)
+      children[i].isShared = true;
+    return new BNodeInternal<K,V>(children, this.keys.slice(0));
+  }
+
+  greedyClone(force?: boolean): BNode<K,V> {
+    if (this.isShared && !force)
+      return this;
+    var nu = new BNodeInternal<K,V>(this.children.slice(0), 
this.keys.slice(0));
+    for (var i = 0; i < nu.children.length; i++)
+      nu.children[i] = nu.children[i].greedyClone();
+    return nu;
+  }
+
+  minKey() {
+    return this.children[0].minKey();
+  }
+
+  get(key: K, defaultValue: V|undefined, tree: BTree<K,V>): V|undefined {
+    var i = this.indexOf(key, 0, tree._compare), children = this.children;
+    return i < children.length ? children[i].get(key, defaultValue, tree) : 
undefined;
+  }
+
+  checkValid(depth: number, tree: BTree<K,V>) : number {
+    var kL = this.keys.length, cL = this.children.length;
+    check(kL === cL, "keys/children length mismatch: depth", depth, "lengths", 
kL, cL);
+    check(kL > 1, "internal node has length", kL, "at depth", depth);
+    var size = 0, c = this.children, k = this.keys, childSize = 0;
+    for (var i = 0; i < cL; i++) {
+      size += c[i].checkValid(depth + 1, tree);
+      childSize += c[i].keys.length;
+      check(size >= childSize, "wtf"); // no way this will ever fail
+      check(i === 0 || c[i-1].constructor === c[i].constructor, "type 
mismatch");
+      if (c[i].maxKey() != k[i])
+        check(false, "keys[", i, "] =", k[i], "is wrong, should be ", 
c[i].maxKey(), "at depth", depth);
+      if (!(i === 0 || tree._compare(k[i-1], k[i]) < 0))
+        check(false, "sort violation at depth", depth, "index", i, "keys", 
k[i-1], k[i]);
+    }
+    var toofew = childSize < (tree.maxNodeSize >> 1)*cL;
+    if (toofew || childSize > tree.maxNodeSize*cL)
+      check(false, toofew ? "too few" : "too many", "children (", childSize, 
size, ") at depth", depth, ", maxNodeSize:", tree.maxNodeSize, 
"children.length:", cL);
+    return size;
+  }
+
+  // Internal Node: set & node splitting //////////////////////////////////////
+
+  set(key: K, value: V, overwrite: boolean|undefined, tree: BTree<K,V>): 
boolean|BNodeInternal<K,V> {
+    var c = this.children, max = tree._maxNodeSize, cmp = tree._compare;
+    var i = Math.min(this.indexOf(key, 0, cmp), c.length - 1), child = c[i];
+    
+    if (child.isShared)
+      c[i] = child = child.clone();
+    if (child.keys.length >= max) {
+      // child is full; inserting anything else will cause a split.
+      // Shifting an item to the left or right sibling may avoid a split.
+      // We can do a shift if the adjacent node is not full and if the
+      // current key can still be placed in the same node after the shift.
+      var other: BNode<K,V>;
+      if (i > 0 && (other = c[i-1]).keys.length < max && cmp(child.keys[0], 
key) < 0) {
+        if (other.isShared)
+          c[i-1] = other = other.clone();
+        other.takeFromRight(child);
+        this.keys[i-1] = other.maxKey();
+      } else if ((other = c[i+1]) !== undefined && other.keys.length < max && 
cmp(child.maxKey(), key) < 0) {
+        if (other.isShared)
+          c[i+1] = other = other.clone();
+        other.takeFromLeft(child);
+        this.keys[i] = c[i].maxKey();
+      }
+    }
+
+    var result = child.set(key, value, overwrite, tree);
+    if (result === false)
+      return false;
+    this.keys[i] = child.maxKey();
+    if (result === true)
+      return true;
+
+    // The child has split and `result` is a new right child... does it fit?
+    if (this.keys.length < max) { // yes
+      this.insert(i+1, result);
+      return true;
+    } else { // no, we must split also
+      var newRightSibling = this.splitOffRightSide(), target: 
BNodeInternal<K,V> = this;
+      if (cmp(result.maxKey(), this.maxKey()) > 0) {
+        target = newRightSibling;
+        i -= this.keys.length;
+      }
+      target.insert(i+1, result);
+      return newRightSibling;
+    }
+  }
+
+  insert(i: index, child: BNode<K,V>) {
+    this.children.splice(i, 0, child);
+    this.keys.splice(i, 0, child.maxKey());
+  }
+
+  splitOffRightSide() {
+    var half = this.children.length >> 1;
+    return new BNodeInternal<K,V>(this.children.splice(half), 
this.keys.splice(half));
+  }
+
+  takeFromRight(rhs: BNode<K,V>) {
+    // Reminder: parent node must update its copy of key for this node
+    // assert: neither node is shared
+    // assert rhs.keys.length > (maxNodeSize/2 && this.keys.length<maxNodeSize)
+    this.keys.push(rhs.keys.shift()!);
+    this.children.push((rhs as BNodeInternal<K,V>).children.shift()!);
+  }
+
+  takeFromLeft(lhs: BNode<K,V>) {
+    // Reminder: parent node must update its copy of key for this node
+    // assert: neither node is shared
+    // assert rhs.keys.length > (maxNodeSize/2 && this.keys.length<maxNodeSize)
+    this.keys.unshift(lhs.keys.pop()!);
+    this.children.unshift((lhs as BNodeInternal<K,V>).children.pop()!);
+  }
+
+  // Internal Node: scanning & deletions //////////////////////////////////////
+
+  forRange<R>(low: K, high: K, includeHigh: boolean|undefined, editMode: 
boolean, tree: BTree<K,V>, count: number,
+    onFound?: (k:K, v:V, counter:number) => EditRangeResult<V,R>|void): 
EditRangeResult<V,R>|number
+  {
+    var cmp = tree._compare;
+    var iLow = this.indexOf(low, 0, cmp), i = iLow;
+    var iHigh = Math.min(high === low ? iLow : this.indexOf(high, 0, cmp), 
this.keys.length-1);
+    var keys = this.keys, children = this.children;
+    if (!editMode) {
+      // Simple case
+      for(; i <= iHigh; i++) {
+        var result = children[i].forRange(low, high, includeHigh, editMode, 
tree, count, onFound);
+        if (typeof result !== 'number')
+          return result;
+        count = result;
+      }
+    } else if (i <= iHigh) {
+      try {
+        for(; i <= iHigh; i++) {
+          if (children[i].isShared)
+            children[i] = children[i].clone();
+          var result = children[i].forRange(low, high, includeHigh, editMode, 
tree, count, onFound);
+          keys[i] = children[i].maxKey();
+          if (typeof result !== 'number')
+            return result;
+          count = result;
+        }
+      } finally {
+        // Deletions may have occurred, so look for opportunities to merge 
nodes.
+        var half = tree._maxNodeSize >> 1;
+        if (iLow > 0)
+          iLow--;
+        for(i = iHigh; i >= iLow; i--) {
+          if (children[i].keys.length <= half)
+            this.tryMerge(i, tree._maxNodeSize);
+        }
+        // Are we completely empty?
+        if (children[0].keys.length === 0) {
+          check(children.length === 1 && keys.length === 1, "emptiness bug");
+          children.shift();
+          keys.shift();
+        }
+      }
+    }
+    return count;
+  }
+
+  /** Merges child i with child i+1 if their combined size is not too large */
+  tryMerge(i: index, maxSize: number): boolean {
+    var children = this.children;
+    if (i >= 0 && i + 1 < children.length) {
+      if (children[i].keys.length + children[i+1].keys.length <= maxSize) {
+        if (children[i].isShared) // cloned already UNLESS i is outside scan 
range
+          children[i] = children[i].clone();
+        children[i].mergeSibling(children[i+1], maxSize);
+        children.splice(i + 1, 1);
+        this.keys.splice(i + 1, 1);
+        this.keys[i] = children[i].maxKey();
+        return true;
+      }
+    }
+    return false;
+  }
+
+  mergeSibling(rhs: BNode<K,V>, maxNodeSize: number) {
+    // assert !this.isShared;
+    var oldLength = this.keys.length;
+    this.keys.push.apply(this.keys, rhs.keys);
+    this.children.push.apply(this.children, (rhs as any as 
BNodeInternal<K,V>).children);
+    // If our children are themselves almost empty due to a mass-delete,
+    // they may need to be merged too (but only the oldLength-1 and its
+    // right sibling should need this).
+    this.tryMerge(oldLength-1, maxNodeSize);
+  }
+}
+
+// Optimization: this array of `undefined`s is used instead of a normal
+// array of values in nodes where `undefined` is the only value.
+// Its length is extended to max node size on first use; since it can
+// be shared between trees with different maximums, its length can only
+// increase, never decrease. Its type should be undefined[] but strangely
+// TypeScript won't allow the comparison V[] === undefined[]. To prevent
+// users from making this array too large, BTree has a maximum node size.
+var undefVals: any[] = [];
+
+const Delete = {delete: true}, DeleteRange = () => Delete;
+const Break = {break: true};
+const EmptyLeaf = (function() { 
+  var n = new BNode<any,any>(); n.isShared = true; return n;
+})();
+const EmptyArray: any[] = [];
+const ReusedArray: any[] = []; // assumed thread-local
+
+function check(fact: boolean, ...args: any[]) {
+  if (!fact) {
+    args.unshift('B+ tree '); // at beginning of message
+    throw new Error(args.join(' '));
+  }
+}
+
+/** A BTree frozen in the empty state. */
+export const EmptyBTree = (() => { let t = new BTree(); t.freeze(); return t; 
})();
\ No newline at end of file
diff --git a/packages/idb-bridge/src/tree/interfaces.ts 
b/packages/idb-bridge/src/tree/interfaces.ts
new file mode 100644
index 00000000..6bd0cdf5
--- /dev/null
+++ b/packages/idb-bridge/src/tree/interfaces.ts
@@ -0,0 +1,329 @@
+/*
+Copyright (c) 2018 David Piepgrass
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+SPDX-License-Identifier: MIT
+*/
+
+// Original repository: https://github.com/qwertie/btree-typescript
+
+
+/** Read-only set interface (subinterface of IMapSource<K,any>).
+ *  The word "set" usually means that each item in the collection is unique
+ *  (appears only once, based on a definition of equality used by the 
+ *  collection.) Objects conforming to this interface aren't guaranteed not 
+ *  to contain duplicates, but as an example, BTree<K,V> implements this 
+ *  interface and does not allow duplicates. */
+export interface ISetSource<K=any>
+{
+  /** Returns the number of key/value pairs in the map object. */
+  size: number;
+  /** Returns a boolean asserting whether the key exists in the map object or 
not. */
+  has(key: K): boolean;
+  /** Returns a new iterator for iterating the items in the set (the order is 
implementation-dependent). */
+  keys(): IterableIterator<K>;
+}
+
+/** Read-only map interface (i.e. a source of key-value pairs). */
+export interface IMapSource<K=any, V=any> extends ISetSource<K>
+{
+  /** Returns the number of key/value pairs in the map object. */
+  size: number;
+  /** Returns the value associated to the key, or undefined if there is none. 
*/
+  get(key: K): V|undefined;
+  /** Returns a boolean asserting whether the key exists in the map object or 
not. */
+  has(key: K): boolean;
+  /** Calls callbackFn once for each key-value pair present in the map object.
+   *  The ES6 Map class sends the value to the callback before the key, so
+   *  this interface must do likewise. */
+  forEach(callbackFn: (v:V, k:K, map:IMapSource<K,V>) => void, thisArg?: any): 
void;
+  
+  /** Returns an iterator that provides all key-value pairs from the 
collection (as arrays of length 2). */
+  entries(): IterableIterator<[K,V]>;
+  /** Returns a new iterator for iterating the keys of each pair. */
+  keys(): IterableIterator<K>;
+  /** Returns a new iterator for iterating the values of each pair. */
+  values(): IterableIterator<V>;
+  // TypeScript compiler decided Symbol.iterator has type 'any'
+  //[Symbol.iterator](): IterableIterator<[K,V]>;
+}
+
+/** Write-only set interface (the set cannot be queried, but items can be 
added to it.) 
+ *  @description Note: BTree<K,V> does not officially implement this interface,
+ *               but BTree<K> can be used as an instance of ISetSink<K>. */
+export interface ISetSink<K=any>
+{
+  /** Adds the specified item to the set, if it was not in the set already. */
+  add(key: K): any;
+  /** Returns true if an element in the map object existed and has been 
+   *  removed, or false if the element did not exist. */
+  delete(key: K): boolean;
+  /** Removes everything so that the set is empty. */
+  clear(): void;
+}
+
+/** Write-only map interface (i.e. a drain into which key-value pairs can be 
"sunk") */
+export interface IMapSink<K=any, V=any>
+{
+  /** Returns true if an element in the map object existed and has been 
+   *  removed, or false if the element did not exist. */
+  delete(key: K): boolean;
+  /** Sets the value for the key in the map object (the return value is 
+   *  boolean in BTree but Map returns the Map itself.) */
+  set(key: K, value: V): any;
+  /** Removes all key/value pairs from the IMap object. */
+  clear(): void;
+}
+
+/** Set interface.
+ *  @description Note: BTree<K,V> does not officially implement this interface,
+ *               but BTree<K> can be used as an instance of ISet<K>. */
+export interface ISet<K=any> extends ISetSource<K>, ISetSink<K> { }
+
+/** An interface compatible with ES6 Map and BTree. This interface does not
+ *  describe the complete interface of either class, but merely the common 
+ *  interface shared by both. */
+export interface IMap<K=any, V=any> extends IMapSource<K, V>, IMapSink<K, V> { 
}
+
+/** An data source that provides read-only access to a set of items called
+ *  "keys" in sorted order. This is a subinterface of ISortedMapSource. */
+export interface ISortedSetSource<K=any> extends ISetSource<K>
+{
+  /** Gets the lowest key in the collection. */
+  minKey(): K | undefined;
+  /** Gets the highest key in the collection. */
+  maxKey(): K | undefined;
+  /** Returns the next key larger than the specified key (or undefined if 
there is none) */
+  nextHigherKey(key: K): K|undefined;
+  /** Returns the next key smaller than the specified key (or undefined if 
there is none) */
+  nextLowerKey(key: K): K|undefined;
+  /** Calls `callback` on the specified range of keys, in ascending order by 
key.
+   * @param low The first key scanned will be greater than or equal to `low`.
+   * @param high Scanning stops when a key larger than this is reached.
+   * @param includeHigh If the `high` key is present in the map, `onFound` is 
called 
+   *        for that final pair if and only if this parameter is true.
+   * @param onFound A function that is called for each key pair. Because this
+   *        is a subinterface of ISortedMapSource, if there is a value 
+   *        associated with the key, it is passed as the second parameter.
+   * @param initialCounter Initial third argument of `onFound`. This value 
+   *        increases by one each time `onFound` is called. Default: 0
+   * @returns Number of pairs found and the number of times `onFound` was 
called.
+   */
+  forRange(low: K, high: K, includeHigh: boolean, onFound?: 
(k:K,v:any,counter:number) => void, initialCounter?: number): number;
+  /** Returns a new iterator for iterating the keys of each pair in ascending 
order. 
+   *  @param firstKey: Minimum key to include in the output. */
+  keys(firstKey?: K): IterableIterator<K>;
+}
+
+/** An data source that provides read-only access to items in sorted order. */
+export interface ISortedMapSource<K=any, V=any> extends IMapSource<K, V>, 
ISortedSetSource<K>
+{
+  /** Returns the next pair whose key is larger than the specified key (or 
undefined if there is none) */
+  nextHigherPair(key: K): [K,V]|undefined;
+  /** Returns the next pair whose key is smaller than the specified key (or 
undefined if there is none) */
+  nextLowerPair(key: K): [K,V]|undefined;
+  /** Builds an array of pairs from the specified range of keys, sorted by key.
+   * Each returned pair is also an array: pair[0] is the key, pair[1] is the 
value.
+   * @param low The first key in the array will be greater than or equal to 
`low`.
+   * @param high This method returns when a key larger than this is reached.
+   * @param includeHigh If the `high` key is present in the map, its pair will 
be
+   *        included in the output if and only if this parameter is true. 
Note: 
+   *        if the `low` key is present, it is always included in the output.
+   * @param maxLength Maximum length of the returned array (default: unlimited)
+   * @description Computational complexity: O(result.length + log size)
+   */
+  getRange(low: K, high: K, includeHigh?: boolean, maxLength?: number): 
[K,V][];
+  /** Calls `callback` on the specified range of keys, in ascending order by 
key.
+   * @param low The first key scanned will be greater than or equal to `low`.
+   * @param high Scanning stops when a key larger than this is reached.
+   * @param includeHigh If the `high` key is present in the map, `onFound` is 
called 
+   *        for that final pair if and only if this parameter is true.
+   * @param onFound A function that is called for each key-value pair.
+   * @param initialCounter Initial third argument of onFound. This value 
+   *        increases by one each time `onFound` is called. Default: 0
+   * @returns Number of pairs found and the number of times `callback` was 
called.
+   */
+  forRange(low: K, high: K, includeHigh: boolean, onFound?: 
(k:K,v:V,counter:number) => void, initialCounter?: number): number;
+  /** Returns an iterator that provides items in order by key.
+   *  @param firstKey: Minimum key to include in the output. */
+  entries(firstKey?: K): IterableIterator<[K,V]>;
+  /** Returns a new iterator for iterating the keys of each pair in ascending 
order. 
+   *  @param firstKey: Minimum key to include in the output. */
+  keys(firstKey?: K): IterableIterator<K>;
+  /** Returns a new iterator for iterating the values of each pair in order by 
key. 
+   *  @param firstKey: Minimum key whose associated value is included in the 
output. */
+  values(firstKey?: K): IterableIterator<V>;
+  
+  // This method should logically be in IMapSource but is not supported by ES6 
Map
+  /** Performs a reduce operation like the `reduce` method of `Array`. 
+   *  It is used to combine all pairs into a single value, or perform 
conversions. */
+  reduce<R>(callback: 
(previous:R,currentPair:[K,V],counter:number,tree:IMapF<K,V>) => R, 
initialValue: R): R;
+  /** Performs a reduce operation like the `reduce` method of `Array`. 
+   *  It is used to combine all pairs into a single value, or perform 
conversions. */
+  reduce<R>(callback: 
(previous:R|undefined,currentPair:[K,V],counter:number,tree:IMapF<K,V>) => R): 
R|undefined;
+}
+
+/** An interface for a set of keys (the combination of ISortedSetSource<K> and 
ISetSink<K>) */
+export interface ISortedSet<K=any> extends ISortedSetSource<K>, ISetSink<K> { }
+
+/** An interface for a sorted map (dictionary), 
+ *  not including functional/persistent methods. */
+export interface ISortedMap<K=any, V=any> extends IMap<K,V>, 
ISortedMapSource<K, V>
+{
+  // All of the following methods should be in IMap but are left out of IMap
+  // so that IMap is compatible with ES6 Map.
+
+  /** Adds or overwrites a key-value pair in the sorted map.
+   * @param key the key is used to determine the sort order of data in the 
tree.
+   * @param value data to associate with the key
+   * @param overwrite Whether to overwrite an existing key-value pair 
+   *        (default: true). If this is false and there is an existing
+   *        key-value pair then the call to this method has no effect.
+   * @returns true if a new key-value pair was added, false if the key 
+   *        already existed. */
+  set(key: K, value: V, overwrite?: boolean): boolean;
+  /** Adds all pairs from a list of key-value pairs.
+   * @param pairs Pairs to add to this tree. If there are duplicate keys, 
+   *        later pairs currently overwrite earlier ones (e.g. [[0,1],[0,7]] 
+   *        associates 0 with 7.)
+   * @param overwrite Whether to overwrite pairs that already exist (if false,
+   *        pairs[i] is ignored when the key pairs[i][0] already exists.)
+   * @returns The number of pairs added to the collection.
+   */
+  setPairs(pairs: [K,V][], overwrite?: boolean): number;
+  /** Deletes a series of keys from the collection. */
+  deleteKeys(keys: K[]): number;
+  /** Removes a range of key-value pairs from the B+ tree.
+   * @param low The first key deleted will be greater than or equal to `low`.
+   * @param high Deleting stops when a key larger than this is reached.
+   * @param includeHigh Specifies whether the `high` key, if present, is 
deleted.
+   * @returns The number of key-value pairs that were deleted. */
+  deleteRange(low: K, high: K, includeHigh: boolean): number;
+
+  // TypeScript requires these methods of ISortedMapSource to be repeated
+  entries(firstKey?: K): IterableIterator<[K,V]>;
+  keys(firstKey?: K): IterableIterator<K>;
+  values(firstKey?: K): IterableIterator<V>;
+}
+
+/** An interface for a functional set, in which the set object could be 
read-only 
+ *  but new versions of the set can be created by calling "with" or "without" 
+ *  methods to add or remove keys. This is a subinterface of IMapF<K,V>,
+ *  so the items in the set may be referred to as "keys". */
+export interface ISetF<K=any> extends ISetSource<K> {
+  /** Returns a copy of the set with the specified key included. 
+   *  @description You might wonder why this method accepts only one key 
+   *  instead of `...keys: K[]`. The reason is that the derived interface
+   *  IMapF expects the second parameter to be a value. Therefore
+   *  withKeys() is provided to set multiple keys at once. */
+  with(key: K): ISetF<K>;
+  /** Returns a copy of the set with the specified key removed. */
+  without(key: K): ISetF<K>;
+  /** Returns a copy of the tree with all the keys in the specified array 
present.
+   *  @param keys The keys to add.
+   *  @param returnThisIfUnchanged If true, the method returns `this` when
+   *         all of the keys are already present in the collection. The 
+   *         default value may be true or false depending on the concrete
+   *         implementation of the interface (in BTree, the default is false.) 
*/
+  withKeys(keys: K[], returnThisIfUnchanged?: boolean): ISetF<K>;
+  /** Returns a copy of the tree with all the keys in the specified array 
removed. */
+  withoutKeys(keys: K[], returnThisIfUnchanged?: boolean): ISetF<K>;
+  /** Returns a copy of the tree with items removed whenever the callback 
+   *  function returns false.
+   *  @param callback A function to call for each item in the set.
+   *         The second parameter to `callback` exists because ISetF
+   *         is a subinterface of IMapF. If the object is a map, v
+   *         is the value associated with the key, otherwise v could be
+   *         undefined or another copy of the third parameter (counter). */
+  filter(callback: (k:K,v:any,counter:number) => boolean, 
returnThisIfUnchanged?: boolean): ISetF<K>;
+}
+
+/** An interface for a functional map, in which the map object could be 
read-only
+ *  but new versions of the map can be created by calling "with" or "without" 
+ *  methods to add or remove keys or key-value pairs. 
+ */
+export interface IMapF<K=any, V=any> extends IMapSource<K, V>, ISetF<K> {
+  /** Returns a copy of the tree with the specified key set (the value is 
undefined). */
+  with(key: K): IMapF<K,V|undefined>;
+  /** Returns a copy of the tree with the specified key-value pair set. */
+  with<V2>(key: K, value: V2, overwrite?: boolean): IMapF<K,V|V2>;
+  /** Returns a copy of the tree with the specified key-value pairs set. */
+  withPairs<V2>(pairs: [K,V|V2][], overwrite: boolean): IMapF<K,V|V2>;
+  /** Returns a copy of the tree with all the keys in the specified array 
present.
+   *  @param keys The keys to add. If a key is already present in the tree,
+   *         neither the existing key nor the existing value is modified. 
+   *  @param returnThisIfUnchanged If true, the method returns `this` when
+   *         all of the keys are already present in the collection. The 
+   *         default value may be true or false depending on the concrete
+   *         implementation of the interface (in BTree, the default is false.) 
*/
+  withKeys(keys: K[], returnThisIfUnchanged?: boolean): IMapF<K,V|undefined>;
+  /** Returns a copy of the tree with all values altered by a callback 
function. */
+  mapValues<R>(callback: (v:V,k:K,counter:number) => R): IMapF<K,R>;
+  /** Performs a reduce operation like the `reduce` method of `Array`. 
+   *  It is used to combine all pairs into a single value, or perform 
conversions. */
+  reduce<R>(callback: 
(previous:R,currentPair:[K,V],counter:number,tree:IMapF<K,V>) => R, 
initialValue: R): R;
+  /** Performs a reduce operation like the `reduce` method of `Array`. 
+   *  It is used to combine all pairs into a single value, or perform 
conversions. */
+  reduce<R>(callback: 
(previous:R|undefined,currentPair:[K,V],counter:number,tree:IMapF<K,V>) => R): 
R|undefined;
+
+  // Update return types in ISetF
+  without(key: K): IMapF<K,V>;
+  withoutKeys(keys: K[], returnThisIfUnchanged?: boolean): IMapF<K,V>;
+  /** Returns a copy of the tree with pairs removed whenever the callback 
+   *  function returns false. */
+  filter(callback: (k:K,v:V,counter:number) => boolean, 
returnThisIfUnchanged?: boolean): IMapF<K,V>;
+}
+
+/** An interface for a functional sorted set: a functional set in which the 
+ *  keys (items) are sorted. This is a subinterface of ISortedMapF. */
+export interface ISortedSetF<K=any> extends ISetF<K>, ISortedSetSource<K>
+{
+  // TypeScript requires this method of ISortedSetSource to be repeated
+  keys(firstKey?: K): IterableIterator<K>;
+}
+
+export interface ISortedMapF<K=any,V=any> extends ISortedSetF<K>, IMapF<K,V>, 
ISortedMapSource<K,V>
+{
+  /** Returns a copy of the tree with the specified range of keys removed. */
+  withoutRange(low: K, high: K, includeHigh: boolean, returnThisIfUnchanged?: 
boolean): ISortedMapF<K,V>;
+
+  // TypeScript requires these methods of ISortedSetF and ISortedMapSource to 
be repeated
+  entries(firstKey?: K): IterableIterator<[K,V]>;
+  keys(firstKey?: K): IterableIterator<K>;
+  values(firstKey?: K): IterableIterator<V>;
+  forRange(low: K, high: K, includeHigh: boolean, onFound?: 
(k:K,v:V,counter:number) => void, initialCounter?: number): number;
+
+  // Update the return value of methods from base interfaces
+  with(key: K): ISortedMapF<K,V|undefined>;
+  with<V2>(key: K, value: V2, overwrite?: boolean): ISortedMapF<K,V|V2>;
+  withKeys(keys: K[], returnThisIfUnchanged?: boolean): 
ISortedMapF<K,V|undefined>;
+  withPairs<V2>(pairs: [K,V|V2][], overwrite: boolean): ISortedMapF<K,V|V2>;
+  mapValues<R>(callback: (v:V,k:K,counter:number) => R): ISortedMapF<K,R>;
+  without(key: K): ISortedMapF<K,V>;
+  withoutKeys(keys: K[], returnThisIfUnchanged?: boolean): ISortedMapF<K,V>;
+  filter(callback: (k:K,v:any,counter:number) => boolean, 
returnThisIfUnchanged?: boolean): ISortedMapF<K,V>;
+}
+
+export interface ISortedMapConstructor<K,V> {
+    new (entries?: [K,V][], compare?: (a: K, b: K) => number): ISortedMap<K,V>;
+}
+export interface ISortedMapFConstructor<K,V> {
+    new (entries?: [K,V][], compare?: (a: K, b: K) => number): 
ISortedMapF<K,V>;
+}
\ No newline at end of file
diff --git a/packages/idb-bridge/src/util/FakeEvent.ts 
b/packages/idb-bridge/src/util/FakeEvent.ts
new file mode 100644
index 00000000..bf17f7e7
--- /dev/null
+++ b/packages/idb-bridge/src/util/FakeEvent.ts
@@ -0,0 +1,80 @@
+/*
+ Copyright 2017 Jeremy Scheff
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ or implied. See the License for the specific language governing
+ permissions and limitations under the License.
+*/
+
+
+import FakeEventTarget from "./FakeEventTarget";
+import { EventType } from "./types";
+
+class Event {
+    public eventPath: FakeEventTarget[] = [];
+    public type: EventType;
+
+    public readonly NONE = 0;
+    public readonly CAPTURING_PHASE = 1;
+    public readonly AT_TARGET = 2;
+    public readonly BUBBLING_PHASE = 3;
+
+    // Flags
+    public propagationStopped = false;
+    public immediatePropagationStopped = false;
+    public canceled = false;
+    public initialized = true;
+    public dispatched = false;
+
+    public target: FakeEventTarget | null = null;
+    public currentTarget: FakeEventTarget | null = null;
+
+    public eventPhase: 0 | 1 | 2 | 3 = 0;
+
+    public defaultPrevented = false;
+
+    public isTrusted = false;
+    public timeStamp = Date.now();
+
+    public bubbles: boolean;
+    public cancelable: boolean;
+
+    constructor(
+        type: EventType,
+        eventInitDict: { bubbles?: boolean; cancelable?: boolean } = {},
+    ) {
+        this.type = type;
+
+        this.bubbles =
+            eventInitDict.bubbles !== undefined ? eventInitDict.bubbles : 
false;
+        this.cancelable =
+            eventInitDict.cancelable !== undefined
+                ? eventInitDict.cancelable
+                : false;
+    }
+
+    public preventDefault() {
+        if (this.cancelable) {
+            this.canceled = true;
+        }
+    }
+
+    public stopPropagation() {
+        this.propagationStopped = true;
+    }
+
+    public stopImmediatePropagation() {
+        this.propagationStopped = true;
+        this.immediatePropagationStopped = true;
+    }
+}
+
+export default Event;
diff --git a/packages/idb-bridge/src/util/FakeEventTarget.ts 
b/packages/idb-bridge/src/util/FakeEventTarget.ts
new file mode 100644
index 00000000..f20432df
--- /dev/null
+++ b/packages/idb-bridge/src/util/FakeEventTarget.ts
@@ -0,0 +1,185 @@
+/*
+ Copyright 2017 Jeremy Scheff
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ or implied. See the License for the specific language governing
+ permissions and limitations under the License.
+ */
+
+import { InvalidStateError } from "./errors";
+import FakeEvent from "./FakeEvent";
+import { EventCallback, EventType } from "./types";
+
+type EventTypeProp =
+  | "onabort"
+  | "onblocked"
+  | "oncomplete"
+  | "onerror"
+  | "onsuccess"
+  | "onupgradeneeded"
+  | "onversionchange";
+
+interface Listener {
+  callback: EventCallback;
+  capture: boolean;
+  type: EventType;
+}
+
+const stopped = (event: FakeEvent, listener: Listener) => {
+  return (
+    event.immediatePropagationStopped ||
+    (event.eventPhase === event.CAPTURING_PHASE &&
+      listener.capture === false) ||
+    (event.eventPhase === event.BUBBLING_PHASE && listener.capture === true)
+  );
+};
+
+// http://www.w3.org/TR/dom/#concept-event-listener-invoke
+const invokeEventListeners = (event: FakeEvent, obj: FakeEventTarget) => {
+  event.currentTarget = obj;
+
+  // The callback might cause obj.listeners to mutate as we traverse it.
+  // Take a copy of the array so that nothing sneaks in and we don't lose
+  // our place.
+  for (const listener of obj.listeners.slice()) {
+    if (event.type !== listener.type || stopped(event, listener)) {
+      continue;
+    }
+
+    console.log(`invoking ${event.type} event listener`, listener);
+    // @ts-ignore
+    listener.callback.call(event.currentTarget, event);
+  }
+
+  const typeToProp: { [key in EventType]: EventTypeProp } = {
+    abort: "onabort",
+    blocked: "onblocked",
+    complete: "oncomplete",
+    error: "onerror",
+    success: "onsuccess",
+    upgradeneeded: "onupgradeneeded",
+    versionchange: "onversionchange",
+  };
+  const prop = typeToProp[event.type];
+  if (prop === undefined) {
+    throw new Error(`Unknown event type: "${event.type}"`);
+  }
+
+  const callback = event.currentTarget[prop];
+  if (callback) {
+    const listener = {
+      callback,
+      capture: false,
+      type: event.type,
+    };
+    if (!stopped(event, listener)) {
+      console.log(`invoking on${event.type} event listener`, listener);
+      // @ts-ignore
+      listener.callback.call(event.currentTarget, event);
+    }
+  }
+};
+
+abstract class FakeEventTarget {
+  public readonly listeners: Listener[] = [];
+
+  // These will be overridden in individual subclasses and made not readonly
+  public readonly onabort: EventCallback | null | undefined;
+  public readonly onblocked: EventCallback | null | undefined;
+  public readonly oncomplete: EventCallback | null | undefined;
+  public readonly onerror: EventCallback | null | undefined;
+  public readonly onsuccess: EventCallback | null | undefined;
+  public readonly onupgradeneeded: EventCallback | null | undefined;
+  public readonly onversionchange: EventCallback | null | undefined;
+
+  static enableTracing: boolean = true;
+
+  public addEventListener(
+    type: EventType,
+    callback: EventCallback,
+    capture = false,
+  ) {
+    this.listeners.push({
+      callback,
+      capture,
+      type,
+    });
+  }
+
+  public removeEventListener(
+    type: EventType,
+    callback: EventCallback,
+    capture = false,
+  ) {
+    const i = this.listeners.findIndex(listener => {
+      return (
+        listener.type === type &&
+        listener.callback === callback &&
+        listener.capture === capture
+      );
+    });
+
+    this.listeners.splice(i, 1);
+  }
+
+  // http://www.w3.org/TR/dom/#dispatching-events
+  public dispatchEvent(event: FakeEvent) {
+    if (event.dispatched || !event.initialized) {
+      throw new InvalidStateError("The object is in an invalid state.");
+    }
+    event.isTrusted = false;
+
+    event.dispatched = true;
+    event.target = this;
+    // NOT SURE WHEN THIS SHOULD BE SET        event.eventPath = [];
+
+    event.eventPhase = event.CAPTURING_PHASE;
+    if (FakeEventTarget.enableTracing) {
+      console.log(
+        `dispatching '${event.type}' event along path with 
${event.eventPath.length} elements`,
+      );
+    }
+    for (const obj of event.eventPath) {
+      if (!event.propagationStopped) {
+        invokeEventListeners(event, obj);
+      }
+    }
+
+    event.eventPhase = event.AT_TARGET;
+    if (!event.propagationStopped) {
+      invokeEventListeners(event, event.target);
+    }
+
+    if (event.bubbles) {
+      event.eventPath.reverse();
+      event.eventPhase = event.BUBBLING_PHASE;
+      if (event.eventPath.length === 0 && event.type === "error") {
+        console.error("Unhandled error event: ", event.target);
+      }
+      for (const obj of event.eventPath) {
+        if (!event.propagationStopped) {
+          invokeEventListeners(event, obj);
+        }
+      }
+    }
+
+    event.dispatched = false;
+    event.eventPhase = event.NONE;
+    event.currentTarget = null;
+
+    if (event.canceled) {
+      return false;
+    }
+    return true;
+  }
+}
+
+export default FakeEventTarget;
diff --git a/packages/idb-bridge/src/util/canInjectKey.ts 
b/packages/idb-bridge/src/util/canInjectKey.ts
new file mode 100644
index 00000000..c6c9c24a
--- /dev/null
+++ b/packages/idb-bridge/src/util/canInjectKey.ts
@@ -0,0 +1,34 @@
+import { KeyPath, Value } from "./types";
+
+// 
http://w3c.github.io/IndexedDB/#check-that-a-key-could-be-injected-into-a-value
+const canInjectKey = (keyPath: KeyPath, value: Value) => {
+  if (Array.isArray(keyPath)) {
+    // tslint:disable-next-line max-line-length
+    throw new Error(
+      "The key paths used in this section are always strings and never 
sequences, since it is not possible to create a object store which has a key 
generator and also has a key path that is a sequence.",
+    );
+  }
+
+  const identifiers = keyPath.split(".");
+  if (identifiers.length === 0) {
+    throw new Error("Assert: identifiers is not empty");
+  }
+  identifiers.pop();
+
+  for (const identifier of identifiers) {
+    if (typeof value !== "object" && !Array.isArray(value)) {
+      return false;
+    }
+
+    const hop = value.hasOwnProperty(identifier);
+    if (!hop) {
+      return true;
+    }
+
+    value = value[identifier];
+  }
+
+  return typeof value === "object" || Array.isArray(value);
+};
+
+export default canInjectKey;
diff --git a/packages/idb-bridge/src/util/cmp.ts 
b/packages/idb-bridge/src/util/cmp.ts
new file mode 100644
index 00000000..9d0dc99a
--- /dev/null
+++ b/packages/idb-bridge/src/util/cmp.ts
@@ -0,0 +1,108 @@
+/*
+ Copyright 2017 Jeremy Scheff
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ or implied. See the License for the specific language governing
+ permissions and limitations under the License.
+ */
+
+import { DataError } from "./errors";
+import valueToKey from "./valueToKey";
+
+const getType = (x: any) => {
+    if (typeof x === "number") {
+        return "Number";
+    }
+    if (x instanceof Date) {
+        return "Date";
+    }
+    if (Array.isArray(x)) {
+        return "Array";
+    }
+    if (typeof x === "string") {
+        return "String";
+    }
+    if (x instanceof ArrayBuffer) {
+        return "Binary";
+    }
+
+    throw new DataError();
+};
+
+// https://w3c.github.io/IndexedDB/#compare-two-keys
+const compareKeys = (first: any, second: any): -1 | 0 | 1 => {
+    if (second === undefined) {
+        throw new TypeError();
+    }
+
+    first = valueToKey(first);
+    second = valueToKey(second);
+
+    const t1 = getType(first);
+    const t2 = getType(second);
+
+    if (t1 !== t2) {
+        if (t1 === "Array") {
+            return 1;
+        }
+        if (
+            t1 === "Binary" &&
+            (t2 === "String" || t2 === "Date" || t2 === "Number")
+        ) {
+            return 1;
+        }
+        if (t1 === "String" && (t2 === "Date" || t2 === "Number")) {
+            return 1;
+        }
+        if (t1 === "Date" && t2 === "Number") {
+            return 1;
+        }
+        return -1;
+    }
+
+    if (t1 === "Binary") {
+        first = new Uint8Array(first);
+        second = new Uint8Array(second);
+    }
+
+    if (t1 === "Array" || t1 === "Binary") {
+        const length = Math.min(first.length, second.length);
+        for (let i = 0; i < length; i++) {
+            const result = compareKeys(first[i], second[i]);
+
+            if (result !== 0) {
+                return result;
+            }
+        }
+
+        if (first.length > second.length) {
+            return 1;
+        }
+        if (first.length < second.length) {
+            return -1;
+        }
+        return 0;
+    }
+
+    if (t1 === "Date") {
+        if (first.getTime() === second.getTime()) {
+            return 0;
+        }
+    } else {
+        if (first === second) {
+            return 0;
+        }
+    }
+
+    return first > second ? 1 : -1;
+};
+
+export default compareKeys;
diff --git a/packages/idb-bridge/src/util/deepEquals.ts 
b/packages/idb-bridge/src/util/deepEquals.ts
new file mode 100644
index 00000000..8d05ad21
--- /dev/null
+++ b/packages/idb-bridge/src/util/deepEquals.ts
@@ -0,0 +1,75 @@
+/*
+Copyright (c) 2017 Evgeny Poberezkin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+
+
+
+const isArray = Array.isArray;
+const keyList = Object.keys;
+const hasProp = Object.prototype.hasOwnProperty;
+
+function deepEquals(a: any, b: any): boolean {
+  if (a === b) return true;
+
+  if (a && b && typeof a == "object" && typeof b == "object") {
+    const arrA = isArray(a);
+    const arrB = isArray(b);
+    let i;
+    let length;
+    let key;
+
+    if (arrA && arrB) {
+      length = a.length;
+      if (length != b.length) return false;
+      for (i = length; i-- !== 0; ) if (!deepEquals(a[i], b[i])) return false;
+      return true;
+    }
+
+    if (arrA != arrB) return false;
+
+    const dateA = a instanceof Date;
+    const dateB = b instanceof Date;
+    if (dateA != dateB) return false;
+    if (dateA && dateB) return a.getTime() == b.getTime();
+
+    const regexpA = a instanceof RegExp;
+    const regexpB = b instanceof RegExp;
+    if (regexpA != regexpB) return false;
+    if (regexpA && regexpB) return a.toString() == b.toString();
+
+    const keys = keyList(a);
+    length = keys.length;
+
+    if (length !== keyList(b).length) return false;
+
+    for (i = length; i-- !== 0; ) if (!hasProp.call(b, keys[i])) return false;
+
+    for (i = length; i-- !== 0; ) {
+      key = keys[i];
+      if (!deepEquals(a[key], b[key])) return false;
+    }
+
+    return true;
+  }
+
+  return a !== a && b !== b;
+}
diff --git a/packages/idb-bridge/src/util/enforceRange.ts 
b/packages/idb-bridge/src/util/enforceRange.ts
new file mode 100644
index 00000000..0cf3b6c8
--- /dev/null
+++ b/packages/idb-bridge/src/util/enforceRange.ts
@@ -0,0 +1,18 @@
+// https://heycam.github.io/webidl/#EnforceRange
+
+const enforceRange = (
+    num: number,
+    type: "MAX_SAFE_INTEGER" | "unsigned long",
+) => {
+    const min = 0;
+    const max = type === "unsigned long" ? 4294967295 : 9007199254740991;
+
+    if (isNaN(num) || num < min || num > max) {
+        throw new TypeError();
+    }
+    if (num >= 0) {
+        return Math.floor(num);
+    }
+};
+
+export default enforceRange;
diff --git a/packages/idb-bridge/src/util/errors.ts 
b/packages/idb-bridge/src/util/errors.ts
new file mode 100644
index 00000000..bbf9498c
--- /dev/null
+++ b/packages/idb-bridge/src/util/errors.ts
@@ -0,0 +1,120 @@
+/*
+ Copyright 2017 Jeremy Scheff
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ or implied. See the License for the specific language governing
+ permissions and limitations under the License.
+ */
+
+
+/* tslint:disable: max-classes-per-file max-line-length */
+
+const messages = {
+    AbortError:
+        "A request was aborted, for example through a call to 
IDBTransaction.abort.",
+    ConstraintError:
+        "A mutation operation in the transaction failed because a constraint 
was not satisfied. For example, an object such as an object store or index 
already exists and a request attempted to create a new one.",
+    DataCloneError:
+        "The data being stored could not be cloned by the internal structured 
cloning algorithm.",
+    DataError: "Data provided to an operation does not meet requirements.",
+    InvalidAccessError:
+        "An invalid operation was performed on an object. For example 
transaction creation attempt was made, but an empty scope was provided.",
+    InvalidStateError:
+        "An operation was called on an object on which it is not allowed or at 
a time when it is not allowed. Also occurs if a request is made on a source 
object that has been deleted or removed. Use TransactionInactiveError or 
ReadOnlyError when possible, as they are more specific variations of 
InvalidStateError.",
+    NotFoundError:
+        "The operation failed because the requested database object could not 
be found. For example, an object store did not exist but was being opened.",
+    ReadOnlyError:
+        'The mutating operation was attempted in a "readonly" transaction.',
+    TransactionInactiveError:
+        "A request was placed against a transaction which is currently not 
active, or which is finished.",
+    VersionError:
+        "An attempt was made to open a database using a lower version than the 
existing version.",
+};
+
+export class AbortError extends Error {
+    constructor(message = messages.AbortError) {
+        super();
+        this.name = "AbortError";
+        this.message = message;
+    }
+}
+
+export class ConstraintError extends Error {
+    constructor(message = messages.ConstraintError) {
+        super();
+        this.name = "ConstraintError";
+        this.message = message;
+    }
+}
+
+export class DataCloneError extends Error {
+    constructor(message = messages.DataCloneError) {
+        super();
+        this.name = "DataCloneError";
+        this.message = message;
+    }
+}
+
+export class DataError extends Error {
+    constructor(message = messages.DataError) {
+        super();
+        this.name = "DataError";
+        this.message = message;
+    }
+}
+
+export class InvalidAccessError extends Error {
+    constructor(message = messages.InvalidAccessError) {
+        super();
+        this.name = "InvalidAccessError";
+        this.message = message;
+    }
+}
+
+export class InvalidStateError extends Error {
+    constructor(message = messages.InvalidStateError) {
+        super();
+        this.name = "InvalidStateError";
+        this.message = message;
+    }
+}
+
+export class NotFoundError extends Error {
+    constructor(message = messages.NotFoundError) {
+        super();
+        this.name = "NotFoundError";
+        this.message = message;
+    }
+}
+
+export class ReadOnlyError extends Error {
+    constructor(message = messages.ReadOnlyError) {
+        super();
+        this.name = "ReadOnlyError";
+        this.message = message;
+    }
+}
+
+export class TransactionInactiveError extends Error {
+    constructor(message = messages.TransactionInactiveError) {
+        super();
+        this.name = "TransactionInactiveError";
+        this.message = message;
+    }
+}
+
+export class VersionError extends Error {
+    constructor(message = messages.VersionError) {
+        super();
+        this.name = "VersionError";
+        this.message = message;
+    }
+}
diff --git a/packages/idb-bridge/src/util/extractKey.ts 
b/packages/idb-bridge/src/util/extractKey.ts
new file mode 100644
index 00000000..fd14c5a6
--- /dev/null
+++ b/packages/idb-bridge/src/util/extractKey.ts
@@ -0,0 +1,55 @@
+import { Key, KeyPath, Value } from "./types";
+import valueToKey from "./valueToKey";
+
+// 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-extracting-a-key-from-a-value-using-a-key-path
+const extractKey = (keyPath: KeyPath, value: Value) => {
+  if (Array.isArray(keyPath)) {
+    const result: Key[] = [];
+
+    for (let item of keyPath) {
+      // This doesn't make sense to me based on the spec, but it is needed to 
pass the W3C KeyPath tests (see same
+      // comment in validateKeyPath)
+      if (
+        item !== undefined &&
+        item !== null &&
+        typeof item !== "string" &&
+        (item as any).toString
+      ) {
+        item = (item as any).toString();
+      }
+      result.push(valueToKey(extractKey(item, value)));
+    }
+
+    return result;
+  }
+
+  if (keyPath === "") {
+    return value;
+  }
+
+  let remainingKeyPath: string | null = keyPath;
+  let object = value;
+
+  while (remainingKeyPath !== null) {
+    let identifier;
+
+    const i = remainingKeyPath.indexOf(".");
+    if (i >= 0) {
+      identifier = remainingKeyPath.slice(0, i);
+      remainingKeyPath = remainingKeyPath.slice(i + 1);
+    } else {
+      identifier = remainingKeyPath;
+      remainingKeyPath = null;
+    }
+
+    if (!object.hasOwnProperty(identifier)) {
+      return;
+    }
+
+    object = object[identifier];
+  }
+
+  return object;
+};
+
+export default extractKey;
diff --git a/packages/idb-bridge/src/util/fakeDOMStringList.ts 
b/packages/idb-bridge/src/util/fakeDOMStringList.ts
new file mode 100644
index 00000000..5add1758
--- /dev/null
+++ b/packages/idb-bridge/src/util/fakeDOMStringList.ts
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2017 Jeremy Scheff
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+import { FakeDOMStringList } from "./types";
+
+// Would be nicer to sublcass Array, but I'd have to sacrifice Node 4 support 
to do that.
+
+const fakeDOMStringList = (arr: string[]): FakeDOMStringList => {
+  const arr2 = arr.slice();
+
+  Object.defineProperty(arr2, "contains", {
+    // tslint:disable-next-line object-literal-shorthand
+    value: (value: string) => arr2.indexOf(value) >= 0,
+  });
+
+  Object.defineProperty(arr2, "item", {
+    // tslint:disable-next-line object-literal-shorthand
+    value: (i: number) => arr2[i],
+  });
+
+  return arr2 as FakeDOMStringList;
+};
+
+export default fakeDOMStringList;
diff --git a/packages/idb-bridge/src/util/getIndexKeys.test.ts 
b/packages/idb-bridge/src/util/getIndexKeys.test.ts
new file mode 100644
index 00000000..e1bc9dd0
--- /dev/null
+++ b/packages/idb-bridge/src/util/getIndexKeys.test.ts
@@ -0,0 +1,24 @@
+import test from "ava";
+import { getIndexKeys } from "./getIndexKeys";
+
+test("basics", (t) => {
+  t.deepEqual(getIndexKeys({foo: 42}, "foo", false), [42]);
+  t.deepEqual(getIndexKeys({foo: {bar: 42}}, "foo.bar", false), [42]);
+  t.deepEqual(getIndexKeys({foo: [42, 43]}, "foo.0", false), [42]);
+  t.deepEqual(getIndexKeys({foo: [42, 43]}, "foo.1", false), [43]);
+
+  t.deepEqual(getIndexKeys([1, 2, 3], "", false), [[1, 2, 3]]);
+
+  t.throws(() => {
+    getIndexKeys({foo: 42}, "foo.bar", false);
+  });
+
+  t.deepEqual(getIndexKeys({foo: 42}, "foo", true), [42]);
+  t.deepEqual(getIndexKeys({foo: 42, bar: 10}, ["foo", "bar"], true), [42, 
10]);
+  t.deepEqual(getIndexKeys({foo: 42, bar: 10}, ["foo", "bar"], false), [[42, 
10]]);
+  t.deepEqual(getIndexKeys({foo: 42, bar: 10}, ["foo", "bar", "spam"], true), 
[42, 10]);
+
+  t.throws(() => {
+    getIndexKeys({foo: 42, bar: 10}, ["foo", "bar", "spam"], false);
+  });
+});
diff --git a/packages/idb-bridge/src/util/getIndexKeys.ts 
b/packages/idb-bridge/src/util/getIndexKeys.ts
new file mode 100644
index 00000000..416cf9ea
--- /dev/null
+++ b/packages/idb-bridge/src/util/getIndexKeys.ts
@@ -0,0 +1,28 @@
+import { Key, Value, KeyPath } from "./types";
+import extractKey from "./extractKey";
+import valueToKey from "./valueToKey";
+
+export function getIndexKeys(
+  value: Value,
+  keyPath: KeyPath,
+  multiEntry: boolean,
+): Key[] {
+  if (multiEntry && Array.isArray(keyPath)) {
+    const keys = [];
+    for (const subkeyPath of keyPath) {
+      const key = extractKey(subkeyPath, value);
+      try {
+        const k = valueToKey(key);
+        keys.push(k);
+      } catch {
+        // Ignore invalid subkeys
+      }
+    }
+    return keys;
+  } else {
+    let key = extractKey(keyPath, value);
+    return [valueToKey(key)];
+  }
+}
+
+export default getIndexKeys;
diff --git a/packages/idb-bridge/src/util/injectKey.ts 
b/packages/idb-bridge/src/util/injectKey.ts
new file mode 100644
index 00000000..89d3a01d
--- /dev/null
+++ b/packages/idb-bridge/src/util/injectKey.ts
@@ -0,0 +1,48 @@
+import { KeyPath, Value, Key } from "./types";
+import canInjectKey from "./canInjectKey";
+import { DataError } from "./errors";
+import structuredClone from "./structuredClone";
+
+export function injectKey(keyPath: KeyPath, value: Value, key: Key): Value {
+  if (Array.isArray(keyPath)) {
+    // tslint:disable-next-line max-line-length
+    throw new Error(
+      "The key paths used in this section are always strings and never 
sequences, since it is not possible to create a object store which has a key 
generator and also has a key path that is a sequence.",
+    );
+  }
+
+  const identifiers = keyPath.split(".");
+  if (identifiers.length === 0) {
+    throw new Error("Assert: identifiers is not empty");
+  }
+
+  const lastIdentifier = identifiers.pop();
+
+  if (lastIdentifier === null || lastIdentifier === undefined) {
+    throw Error();
+  }
+
+  for (const identifier of identifiers) {
+    if (typeof value !== "object" && !Array.isArray(value)) {
+      return false;
+    }
+
+    const hop = value.hasOwnProperty(identifier);
+    if (!hop) {
+      return true;
+    }
+
+    value = value[identifier];
+  }
+
+  if (!(typeof value === "object" || Array.isArray(value))) {
+    throw new Error("can't inject key");
+  }
+
+  const newValue = structuredClone(value);
+  newValue[lastIdentifier] = structuredClone(key);
+
+  return newValue;
+}
+
+export default injectKey;
\ No newline at end of file
diff --git a/packages/idb-bridge/src/util/makeStoreKeyValue.test.ts 
b/packages/idb-bridge/src/util/makeStoreKeyValue.test.ts
new file mode 100644
index 00000000..7820875c
--- /dev/null
+++ b/packages/idb-bridge/src/util/makeStoreKeyValue.test.ts
@@ -0,0 +1,42 @@
+import test from 'ava';
+import { makeStoreKeyValue } from "./makeStoreKeyValue";
+
+test("basics", (t) => {
+  let result;
+
+  result = makeStoreKeyValue({ name: "Florian" }, undefined, 42, true, "id");
+  t.is(result.updatedKeyGenerator, 43);
+  t.is(result.key, 42);
+  t.is(result.value.name, "Florian");
+  t.is(result.value.id, 42);
+
+  result = makeStoreKeyValue({ name: "Florian", id: 10 }, undefined, 5, true, 
"id");
+  t.is(result.updatedKeyGenerator, 11);
+  t.is(result.key, 10);
+  t.is(result.value.name, "Florian");
+  t.is(result.value.id, 10);
+
+  result = makeStoreKeyValue({ name: "Florian", id: 5 }, undefined, 10, true, 
"id");
+  t.is(result.updatedKeyGenerator, 10);
+  t.is(result.key, 5);
+  t.is(result.value.name, "Florian");
+  t.is(result.value.id, 5);
+
+  result = makeStoreKeyValue({ name: "Florian", id: "foo" }, undefined, 10, 
true, "id");
+  t.is(result.updatedKeyGenerator, 10);
+  t.is(result.key, "foo");
+  t.is(result.value.name, "Florian");
+  t.is(result.value.id, "foo");
+
+  result = makeStoreKeyValue({ name: "Florian" }, "foo", 10, true, null);
+  t.is(result.updatedKeyGenerator, 10);
+  t.is(result.key, "foo");
+  t.is(result.value.name, "Florian");
+  t.is(result.value.id, undefined);
+
+  result = makeStoreKeyValue({ name: "Florian" }, undefined, 10, true, null);
+  t.is(result.updatedKeyGenerator, 11);
+  t.is(result.key, 10);
+  t.is(result.value.name, "Florian");
+  t.is(result.value.id, undefined);
+});
diff --git a/packages/idb-bridge/src/util/makeStoreKeyValue.ts 
b/packages/idb-bridge/src/util/makeStoreKeyValue.ts
new file mode 100644
index 00000000..4f45e0d8
--- /dev/null
+++ b/packages/idb-bridge/src/util/makeStoreKeyValue.ts
@@ -0,0 +1,104 @@
+import { Value, Key, KeyPath } from "./types";
+import extractKey from "./extractKey";
+import { DataError } from "./errors";
+import valueToKey from "./valueToKey";
+import structuredClone from "./structuredClone";
+import injectKey from "./injectKey";
+
+export interface StoreKeyResult {
+  updatedKeyGenerator: number;
+  key: Key;
+  value: Value;
+}
+
+export function makeStoreKeyValue(
+  value: Value,
+  key: Key | undefined,
+  currentKeyGenerator: number,
+  autoIncrement: boolean,
+  keyPath: KeyPath | null,
+): StoreKeyResult {
+  const haveKey = key !== undefined && key !== null;
+  const haveKeyPath = keyPath !== null && keyPath !== undefined;
+
+  // This models a decision table on (haveKey, haveKeyPath, autoIncrement)
+
+  value = structuredClone(value);
+
+  if (haveKey) {
+    if (haveKeyPath) {
+      // (yes, yes, no)
+      // (yes, yes, yes)
+      throw new DataError();
+    } else {
+      if (autoIncrement) {
+        // (yes, no, yes)
+        key = valueToKey(key)!;
+        let updatedKeyGenerator: number;
+        if (typeof key !== "number") {
+          updatedKeyGenerator = currentKeyGenerator;
+        } else {
+          updatedKeyGenerator = key;
+        }
+        return {
+          key: key!,
+          value: value,
+          updatedKeyGenerator,
+        };
+      } else {
+        // (yes, no, no)
+        throw new DataError();
+      }
+    }
+  } else {
+    if (haveKeyPath) {
+      if (autoIncrement) {
+        // (no, yes, yes)
+
+        let updatedKeyGenerator: number;
+        const maybeInlineKey = extractKey(keyPath!, value);
+        if (maybeInlineKey === undefined) {
+          value = injectKey(keyPath!, value, currentKeyGenerator);
+          key = currentKeyGenerator;
+          updatedKeyGenerator = currentKeyGenerator + 1;
+        } else if (typeof maybeInlineKey === "number") {
+          key = maybeInlineKey;
+          if (maybeInlineKey >= currentKeyGenerator) {
+            updatedKeyGenerator = maybeInlineKey + 1;
+          } else {
+            updatedKeyGenerator = currentKeyGenerator;
+          }
+        } else {
+          key = maybeInlineKey;
+          updatedKeyGenerator = currentKeyGenerator;
+        }
+        return {
+          key: key,
+          value: value,
+          updatedKeyGenerator,
+        }
+      } else {
+        // (no, yes, no)
+        key = extractKey(keyPath!, value);
+        key = valueToKey(key);
+        return {
+          key: key!,
+          value: value,
+          updatedKeyGenerator: currentKeyGenerator,
+        };
+      }
+    } else {
+      if (autoIncrement) {
+        // (no, no, yes)
+        return {
+          key: currentKeyGenerator,
+          value: value,
+          updatedKeyGenerator: currentKeyGenerator + 1,
+        }
+      } else {
+        // (no, no, no)
+        throw new DataError();
+      }
+    }
+  }
+}
diff --git a/packages/idb-bridge/src/util/openPromise.ts 
b/packages/idb-bridge/src/util/openPromise.ts
new file mode 100644
index 00000000..3f6da81b
--- /dev/null
+++ b/packages/idb-bridge/src/util/openPromise.ts
@@ -0,0 +1,22 @@
+function openPromise<T>(): {
+  promise: Promise<T>;
+  resolve: (v?: T | PromiseLike<T>) => void;
+  reject: (err?: any) => void;
+} {
+  let resolve;
+  let reject;
+  const promise = new Promise<T>((resolve2, reject2) => {
+    resolve = resolve2;
+    reject = reject2;
+  });
+  if (!resolve) {
+    throw Error("broken invariant");
+  }
+  if (!reject) {
+    throw Error("broken invariant");
+  }
+
+  return { promise, resolve, reject };
+}
+
+export default openPromise;
diff --git a/packages/idb-bridge/src/util/queueTask.ts 
b/packages/idb-bridge/src/util/queueTask.ts
new file mode 100644
index 00000000..7d59c226
--- /dev/null
+++ b/packages/idb-bridge/src/util/queueTask.ts
@@ -0,0 +1,21 @@
+/*
+  Copyright 2019 Florian Dold
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+  or implied. See the License for the specific language governing
+  permissions and limitations under the License.
+ */
+
+ export function queueTask(fn: () => void) {
+   setImmediate(fn);
+ }
+
+ export default queueTask;
\ No newline at end of file
diff --git a/packages/idb-bridge/src/util/structuredClone.ts 
b/packages/idb-bridge/src/util/structuredClone.ts
new file mode 100644
index 00000000..8b6b6131
--- /dev/null
+++ b/packages/idb-bridge/src/util/structuredClone.ts
@@ -0,0 +1,15 @@
+
+function structuredCloneImpl(val: any, visited: WeakMap<any, boolean>): any {
+  // FIXME: replace with real implementation!
+  return JSON.parse(JSON.stringify(val));
+}
+
+/**
+ * Structured clone for IndexedDB.
+ */
+export function structuredClone(val: any): any {
+  const visited: WeakMap<any, boolean> = new WeakMap<any, boolean>();
+  return structuredCloneImpl(val, visited);
+}
+
+export default structuredClone;
\ No newline at end of file
diff --git a/packages/idb-bridge/src/util/types.ts 
b/packages/idb-bridge/src/util/types.ts
new file mode 100644
index 00000000..7aea22ab
--- /dev/null
+++ b/packages/idb-bridge/src/util/types.ts
@@ -0,0 +1,73 @@
+/*
+ Copyright 2017 Jeremy Scheff
+ Copyright 2019 Florian Dold
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ or implied. See the License for the specific language governing
+ permissions and limitations under the License.
+*/
+
+import BridgeIDBRequest from "../BridgeIDBRequest";
+import BridgeIDBKeyRange from "../BridgeIDBKeyRange";
+import BridgeIDBIndex from "../BridgeIDBIndex";
+import BridgeIBObjectStore from "../BridgeIDBObjectStore";
+
+interface EventInCallback extends Event {
+  target: any;
+  error: Error | null;
+}
+
+export type EventCallback = (event: EventInCallback) => void;
+
+export type EventType =
+  | "abort"
+  | "blocked"
+  | "complete"
+  | "error"
+  | "success"
+  | "upgradeneeded"
+  | "versionchange";
+
+export type CursorSource = BridgeIDBIndex | BridgeIBObjectStore;
+
+
+export interface FakeDOMStringList extends Array<string> {
+  contains: (value: string) => boolean;
+  item: (i: number) => string | undefined;
+}
+
+export type BridgeIDBCursorDirection = "next" | "nextunique" | "prev" | 
"prevunique";
+
+export type KeyPath = string | string[];
+
+export type Key = any;
+
+export type CursorRange = Key | BridgeIDBKeyRange | undefined;
+
+export type Value = any;
+
+export interface Record {
+  key: Key;
+  value: Key | Value; // For indexes, will be Key. For object stores, will be 
Value.
+}
+
+export type TransactionMode = "readonly" | "readwrite" | "versionchange";
+
+export interface BridgeIDBDatabaseInfo {
+  name: string;
+  version: number
+};
+
+export interface RequestObj {
+  operation: () => Promise<any>;
+  request?: BridgeIDBRequest | undefined;
+  source?: any;
+}
diff --git a/packages/idb-bridge/src/util/validateKeyPath.ts 
b/packages/idb-bridge/src/util/validateKeyPath.ts
new file mode 100644
index 00000000..18552a5d
--- /dev/null
+++ b/packages/idb-bridge/src/util/validateKeyPath.ts
@@ -0,0 +1,77 @@
+/*
+ Copyright 2017 Jeremy Scheff
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ or implied. See the License for the specific language governing
+ permissions and limitations under the License.
+*/
+
+import { KeyPath } from "./types";
+
+// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-valid-key-path
+const validateKeyPath = (keyPath: KeyPath, parent?: "array" | "string") => {
+    // This doesn't make sense to me based on the spec, but it is needed to 
pass the W3C KeyPath tests (see same
+    // comment in extractKey)
+    if (
+        keyPath !== undefined &&
+        keyPath !== null &&
+        typeof keyPath !== "string" &&
+        keyPath.toString &&
+        (parent === "array" || !Array.isArray(keyPath))
+    ) {
+        keyPath = keyPath.toString();
+    }
+
+    if (typeof keyPath === "string") {
+        if (keyPath === "" && parent !== "string") {
+            return;
+        }
+        try {
+            // https://mathiasbynens.be/demo/javascript-identifier-regex for 
ECMAScript 5.1 / Unicode v7.0.0, with
+            // reserved words at beginning removed
+            // tslint:disable-next-line max-line-length
+            const validIdentifierRegex = 
/^(?:[\$A-Z_a-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0
 [...]
+            if (keyPath.length >= 1 && validIdentifierRegex.test(keyPath)) {
+                return;
+            }
+        } catch (err) {
+            throw new SyntaxError(err.message);
+        }
+        if (keyPath.indexOf(" ") >= 0) {
+            throw new SyntaxError(
+                "The keypath argument contains an invalid key path (no spaces 
allowed).",
+            );
+        }
+    }
+
+    if (Array.isArray(keyPath) && keyPath.length > 0) {
+        if (parent) {
+            // No nested arrays
+            throw new SyntaxError(
+                "The keypath argument contains an invalid key path (nested 
arrays).",
+            );
+        }
+        for (const part of keyPath) {
+            validateKeyPath(part, "array");
+        }
+        return;
+    } else if (typeof keyPath === "string" && keyPath.indexOf(".") >= 0) {
+        keyPath = keyPath.split(".");
+        for (const part of keyPath) {
+            validateKeyPath(part, "string");
+        }
+        return;
+    }
+
+    throw new SyntaxError();
+};
+
+export default validateKeyPath;
diff --git a/packages/idb-bridge/src/util/valueToKey.ts 
b/packages/idb-bridge/src/util/valueToKey.ts
new file mode 100644
index 00000000..85c8c409
--- /dev/null
+++ b/packages/idb-bridge/src/util/valueToKey.ts
@@ -0,0 +1,70 @@
+/*
+ Copyright 2017 Jeremy Scheff
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ or implied. See the License for the specific language governing
+ permissions and limitations under the License.
+ */
+
+import { DataError } from "./errors";
+import { Key } from "./types";
+
+// https://w3c.github.io/IndexedDB/#convert-a-value-to-a-input
+function valueToKey(input: any, seen?: Set<object>): Key | Key[] {
+  if (typeof input === "number") {
+    if (isNaN(input)) {
+      throw new DataError();
+    }
+    return input;
+  } else if (input instanceof Date) {
+    const ms = input.valueOf();
+    if (isNaN(ms)) {
+      throw new DataError();
+    }
+    return new Date(ms);
+  } else if (typeof input === "string") {
+    return input;
+  } else if (
+    input instanceof ArrayBuffer ||
+    (typeof ArrayBuffer !== "undefined" &&
+      ArrayBuffer.isView &&
+      ArrayBuffer.isView(input))
+  ) {
+    if (input instanceof ArrayBuffer) {
+      return new Uint8Array(input).buffer;
+    }
+    return new Uint8Array(input.buffer).buffer;
+  } else if (Array.isArray(input)) {
+    if (seen === undefined) {
+      seen = new Set();
+    } else if (seen.has(input)) {
+      throw new DataError();
+    }
+    seen.add(input);
+
+    const keys = [];
+    for (let i = 0; i < input.length; i++) {
+      const hop = input.hasOwnProperty(i);
+      if (!hop) {
+        throw new DataError();
+      }
+      const entry = input[i];
+      const key = valueToKey(entry, seen);
+      keys.push(key);
+    }
+    return keys;
+  } else {
+    
+    throw new DataError();
+  }
+}
+
+export default valueToKey;
diff --git a/packages/idb-bridge/tsconfig.json 
b/packages/idb-bridge/tsconfig.json
new file mode 100644
index 00000000..1b9102e4
--- /dev/null
+++ b/packages/idb-bridge/tsconfig.json
@@ -0,0 +1,15 @@
+{
+    "compilerOptions": {
+        "lib": ["es6"],
+        "module": "commonjs",
+        "target": "es5",
+        "noImplicitAny": true,
+        "sourceMap": false,
+        "outDir": "build",
+        "noEmitOnError": true,
+        "strict": true,
+        "incremental": true,
+        "sourceMap": true
+    },
+    "include": ["src/**/*"]
+}
diff --git a/packages/idb-bridge/yarn.lock b/packages/idb-bridge/yarn.lock
new file mode 100644
index 00000000..0977944f
--- /dev/null
+++ b/packages/idb-bridge/yarn.lock
@@ -0,0 +1,3841 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@ava/babel-plugin-throws-helper@^3.0.0":
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/@ava/babel-plugin-throws-helper/-/babel-plugin-throws-helper-3.0.0.tgz#2c933ec22da0c4ce1fc5369f2b95452c70420586";
+  integrity 
sha512-mN9UolOs4WX09QkheU1ELkVy2WPnwonlO3XMdN8JF8fQqRVgVTR21xDbvEOUsbwz6Zwjq7ji9yzyjuXqDPalxg==
+
+"@ava/babel-preset-stage-4@^3.0.0":
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/@ava/babel-preset-stage-4/-/babel-preset-stage-4-3.0.0.tgz#32c46b22b640d1ba0c6e38ae4abd58efab965558";
+  integrity 
sha512-uI5UBx++UsckkfnbF0HH6jvTIvM4r/Kxt1ROO2YXKu5H15sScAtxUIAHiUVbPIw24zPqz/PlF3xxlIDuyFzlQw==
+  dependencies:
+    "@babel/plugin-proposal-async-generator-functions" "^7.2.0"
+    "@babel/plugin-proposal-optional-catch-binding" "^7.2.0"
+    "@babel/plugin-transform-dotall-regex" "^7.4.3"
+    "@babel/plugin-transform-modules-commonjs" "^7.4.3"
+
+"@ava/babel-preset-transform-test-files@^5.0.0":
+  version "5.0.0"
+  resolved 
"https://registry.yarnpkg.com/@ava/babel-preset-transform-test-files/-/babel-preset-transform-test-files-5.0.0.tgz#e06fc762069511e597531cc1120e22216aac6981";
+  integrity 
sha512-rqgyQwkT0+j2JzYP51dOv80u33rzAvjBtXRzUON+7+6u26mjoudRXci2+1s18rat8r4uOlZfbzm114YS6pwmYw==
+  dependencies:
+    "@ava/babel-plugin-throws-helper" "^3.0.0"
+    babel-plugin-espower "^3.0.1"
+
+"@babel/code-frame@^7.0.0":
+  version "7.0.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8";
+  integrity 
sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==
+  dependencies:
+    "@babel/highlight" "^7.0.0"
+
+"@babel/core@^7.4.5":
+  version "7.4.5"
+  resolved 
"https://registry.yarnpkg.com/@babel/core/-/core-7.4.5.tgz#081f97e8ffca65a9b4b0fdc7e274e703f000c06a";
+  integrity 
sha512-OvjIh6aqXtlsA8ujtGKfC7LYWksYSX8yQcM8Ay3LuvVeQ63lcOKgoZWVqcpFwkd29aYU9rVx7jxhfhiEDV9MZA==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    "@babel/generator" "^7.4.4"
+    "@babel/helpers" "^7.4.4"
+    "@babel/parser" "^7.4.5"
+    "@babel/template" "^7.4.4"
+    "@babel/traverse" "^7.4.5"
+    "@babel/types" "^7.4.4"
+    convert-source-map "^1.1.0"
+    debug "^4.1.0"
+    json5 "^2.1.0"
+    lodash "^4.17.11"
+    resolve "^1.3.2"
+    semver "^5.4.1"
+    source-map "^0.5.0"
+
+"@babel/generator@^7.0.0", "@babel/generator@^7.4.4":
+  version "7.4.4"
+  resolved 
"https://registry.yarnpkg.com/@babel/generator/-/generator-7.4.4.tgz#174a215eb843fc392c7edcaabeaa873de6e8f041";
+  integrity 
sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==
+  dependencies:
+    "@babel/types" "^7.4.4"
+    jsesc "^2.5.1"
+    lodash "^4.17.11"
+    source-map "^0.5.0"
+    trim-right "^1.0.1"
+
+"@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==
+  dependencies:
+    "@babel/types" "^7.0.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==
+  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.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==
+  dependencies:
+    "@babel/types" "^7.0.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==
+  dependencies:
+    "@babel/types" "^7.0.0"
+
+"@babel/helper-module-transforms@^7.4.4":
+  version "7.4.4"
+  resolved 
"https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.4.4.tgz#96115ea42a2f139e619e98ed46df6019b94414b8";
+  integrity 
sha512-3Z1yp8TVQf+B4ynN7WoHPKS8EkdTbgAEy0nU0rs/1Kw4pDgmvYH3rz3aI11KgxKCba2cn7N+tqzV1mY2HMN96w==
+  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.4.4"
+    lodash "^4.17.11"
+
+"@babel/helper-plugin-utils@^7.0.0":
+  version "7.0.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250";
+  integrity 
sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==
+
+"@babel/helper-regex@^7.4.4":
+  version "7.4.4"
+  resolved 
"https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.4.4.tgz#a47e02bc91fb259d2e6727c2a30013e3ac13c4a2";
+  integrity 
sha512-Y5nuB/kESmR3tKjU8Nkn1wMGEx1tjJX076HBMeL3XLQCu6vA/YRzuTW0bbb+qRnXvQGn+d6Rx953yffl8vEy7Q==
+  dependencies:
+    lodash "^4.17.11"
+
+"@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==
+  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-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==
+  dependencies:
+    "@babel/template" "^7.1.0"
+    "@babel/types" "^7.0.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==
+  dependencies:
+    "@babel/types" "^7.4.4"
+
+"@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==
+  dependencies:
+    "@babel/helper-function-name" "^7.1.0"
+    "@babel/template" "^7.1.0"
+    "@babel/traverse" "^7.1.0"
+    "@babel/types" "^7.2.0"
+
+"@babel/helpers@^7.4.4":
+  version "7.4.4"
+  resolved 
"https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.4.4.tgz#868b0ef59c1dd4e78744562d5ce1b59c89f2f2a5";
+  integrity 
sha512-igczbR/0SeuPR8RFfC7tGrbdTbFL3QTvH6D+Z6zNxnTe//GyqmtHmDkzrqDmyZ3eSwPqB/LhyKoU5DXsp+Vp2A==
+  dependencies:
+    "@babel/template" "^7.4.4"
+    "@babel/traverse" "^7.4.4"
+    "@babel/types" "^7.4.4"
+
+"@babel/highlight@^7.0.0":
+  version "7.0.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4";
+  integrity 
sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==
+  dependencies:
+    chalk "^2.0.0"
+    esutils "^2.0.2"
+    js-tokens "^4.0.0"
+
+"@babel/parser@^7.0.0", "@babel/parser@^7.4.4", "@babel/parser@^7.4.5":
+  version "7.4.5"
+  resolved 
"https://registry.yarnpkg.com/@babel/parser/-/parser-7.4.5.tgz#04af8d5d5a2b044a2a1bffacc1e5e6673544e872";
+  integrity 
sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==
+
+"@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==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-remap-async-to-generator" "^7.1.0"
+    "@babel/plugin-syntax-async-generators" "^7.2.0"
+
+"@babel/plugin-proposal-optional-catch-binding@^7.2.0":
+  version "7.2.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz#135d81edb68a081e55e56ec48541ece8065c38f5";
+  integrity 
sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-optional-catch-binding" "^7.2.0"
+
+"@babel/plugin-syntax-async-generators@^7.2.0":
+  version "7.2.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz#69e1f0db34c6f5a0cf7e2b3323bf159a76c8cb7f";
+  integrity 
sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-object-rest-spread@^7.2.0":
+  version "7.2.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz#3b7a3e733510c57e820b9142a6579ac8b0dfad2e";
+  integrity 
sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-optional-catch-binding@^7.2.0":
+  version "7.2.0"
+  resolved 
"https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz#a94013d6eda8908dfe6a477e7f9eda85656ecf5c";
+  integrity 
sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-dotall-regex@^7.4.3":
+  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==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-regex" "^7.4.4"
+    regexpu-core "^4.5.4"
+
+"@babel/plugin-transform-modules-commonjs@^7.4.3":
+  version "7.4.4"
+  resolved 
"https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.4.4.tgz#0bef4713d30f1d78c2e59b3d6db40e60192cac1e";
+  integrity 
sha512-4sfBOJt58sEo9a2BQXnZq+Q3ZTSAUXyK3E30o36BOGnJ+tvJ6YSxF0PG6kERvbeISgProodWuI9UVG3/FMY6iw==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.4.4"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-simple-access" "^7.1.0"
+
+"@babel/template@^7.1.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==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    "@babel/parser" "^7.4.4"
+    "@babel/types" "^7.4.4"
+
+"@babel/traverse@^7.1.0", "@babel/traverse@^7.4.4", "@babel/traverse@^7.4.5":
+  version "7.4.5"
+  resolved 
"https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.4.5.tgz#4e92d1728fd2f1897dafdd321efbff92156c3216";
+  integrity 
sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    "@babel/generator" "^7.4.4"
+    "@babel/helper-function-name" "^7.1.0"
+    "@babel/helper-split-export-declaration" "^7.4.4"
+    "@babel/parser" "^7.4.5"
+    "@babel/types" "^7.4.4"
+    debug "^4.1.0"
+    globals "^11.1.0"
+    lodash "^4.17.11"
+
+"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.4.4":
+  version "7.4.4"
+  resolved 
"https://registry.yarnpkg.com/@babel/types/-/types-7.4.4.tgz#8db9e9a629bb7c29370009b4b779ed93fe57d5f0";
+  integrity 
sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==
+  dependencies:
+    esutils "^2.0.2"
+    lodash "^4.17.11"
+    to-fast-properties "^2.0.0"
+
+"@concordance/react@^2.0.0":
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/@concordance/react/-/react-2.0.0.tgz#aef913f27474c53731f4fd79cc2f54897de90fde";
+  integrity 
sha512-huLSkUuM2/P+U0uy2WwlKuixMsTODD8p4JVQBI4VKeopkiN0C7M3N9XYVawb4M+4spN5RrO/eLhk7KoQX6nsfA==
+  dependencies:
+    arrify "^1.0.1"
+
+"@mrmlnc/readdir-enhanced@^2.2.1":
+  version "2.2.1"
+  resolved 
"https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde";
+  integrity 
sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==
+  dependencies:
+    call-me-maybe "^1.0.1"
+    glob-to-regexp "^0.3.0"
+
+"@nodelib/fs.stat@^1.1.2":
+  version "1.1.3"
+  resolved 
"https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b";
+  integrity 
sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==
+
+"@sindresorhus/is@^0.14.0":
+  version "0.14.0"
+  resolved 
"https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea";
+  integrity 
sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==
+
+"@szmarczak/http-timer@^1.1.2":
+  version "1.1.2"
+  resolved 
"https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421";
+  integrity 
sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==
+  dependencies:
+    defer-to-connect "^1.0.1"
+
+"@types/events@*":
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7";
+  integrity 
sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
+
+"@types/glob@^7.1.1":
+  version "7.1.1"
+  resolved 
"https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575";
+  integrity 
sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==
+  dependencies:
+    "@types/events" "*"
+    "@types/minimatch" "*"
+    "@types/node" "*"
+
+"@types/minimatch@*":
+  version "3.0.3"
+  resolved 
"https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d";
+  integrity 
sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
+
+"@types/node@*":
+  version "12.0.8"
+  resolved 
"https://registry.yarnpkg.com/@types/node/-/node-12.0.8.tgz#551466be11b2adc3f3d47156758f610bd9f6b1d8";
+  integrity 
sha512-b8bbUOTwzIY3V5vDTY1fIJ+ePKDUBqt2hC2woVGotdQQhG/2Sh62HOKHrT7ab+VerXAcPyAiTEipPu/FsreUtg==
+
+abbrev@1:
+  version "1.1.1"
+  resolved 
"https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8";
+  integrity 
sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
+
+ajv@^6.5.5:
+  version "6.10.0"
+  resolved 
"https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1";
+  integrity 
sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==
+  dependencies:
+    fast-deep-equal "^2.0.1"
+    fast-json-stable-stringify "^2.0.0"
+    json-schema-traverse "^0.4.1"
+    uri-js "^4.2.2"
+
+ansi-align@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb";
+  integrity 
sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==
+  dependencies:
+    string-width "^3.0.0"
+
+ansi-escapes@^4.1.0:
+  version "4.2.0"
+  resolved 
"https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.2.0.tgz#c38600259cefba178ee3f7166c5ea3a5dd2e88fc";
+  integrity 
sha512-0+VX4uhi8m3aNbzoqKmkAVOEj6uQzcUHXoFPkKjhZPTpGRUBqVh930KbB6PS4zIyDZccphlLIYlu8nsjFzkXwg==
+  dependencies:
+    type-fest "^0.5.2"
+
+ansi-regex@^2.0.0:
+  version "2.1.1"
+  resolved 
"https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df";
+  integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
+
+ansi-regex@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998";
+  integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
+
+ansi-regex@^4.1.0:
+  version "4.1.0"
+  resolved 
"https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997";
+  integrity 
sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
+
+ansi-styles@^3.2.1:
+  version "3.2.1"
+  resolved 
"https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d";
+  integrity 
sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+  dependencies:
+    color-convert "^1.9.0"
+
+ansi-styles@^4.0.0:
+  version "4.0.0"
+  resolved 
"https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.0.0.tgz#f6b84e8fc97ea7add7a53b7530ef28f3fde0e048";
+  integrity 
sha512-8zjUtFJ3db/QoPXuuEMloS2AUf79/yeyttJ7Abr3hteopJu9HK8vsgGviGUMq+zyA6cZZO6gAyZoMTF6TgaEjA==
+  dependencies:
+    color-convert "^2.0.0"
+
+anymatch@^3.0.1:
+  version "3.0.2"
+  resolved 
"https://registry.yarnpkg.com/anymatch/-/anymatch-3.0.2.tgz#ddb3a8495d44875423af7b919aace11e91732a41";
+  integrity 
sha512-rUe9SxpRQlVg4EM8It7JMNWWYHAirTPpbTuvaSKybb5IejNgWB3PGBBX9rrPKDx2pM/p3Wh+7+ASaWRyyAbxmQ==
+  dependencies:
+    normalize-path "^3.0.0"
+    picomatch "^2.0.4"
+
+aproba@^1.0.3:
+  version "1.2.0"
+  resolved 
"https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a";
+  integrity 
sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
+
+are-we-there-yet@~1.1.2:
+  version "1.1.5"
+  resolved 
"https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21";
+  integrity 
sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==
+  dependencies:
+    delegates "^1.0.0"
+    readable-stream "^2.0.6"
+
+argparse@^1.0.7:
+  version "1.0.10"
+  resolved 
"https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911";
+  integrity 
sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
+  dependencies:
+    sprintf-js "~1.0.2"
+
+arr-diff@^4.0.0:
+  version "4.0.0"
+  resolved 
"https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520";
+  integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=
+
+arr-flatten@^1.1.0:
+  version "1.1.0"
+  resolved 
"https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1";
+  integrity 
sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==
+
+arr-union@^3.1.0:
+  version "3.1.0"
+  resolved 
"https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4";
+  integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
+
+array-find-index@^1.0.1:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1";
+  integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=
+
+array-union@^1.0.1, array-union@^1.0.2:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39";
+  integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=
+  dependencies:
+    array-uniq "^1.0.1"
+
+array-union@^2.1.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d";
+  integrity 
sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
+
+array-uniq@^1.0.1:
+  version "1.0.3"
+  resolved 
"https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6";
+  integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=
+
+array-uniq@^2.1.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/array-uniq/-/array-uniq-2.1.0.tgz#46603d5e28e79bfd02b046fcc1d77c6820bd8e98";
+  integrity 
sha512-bdHxtev7FN6+MXI1YFW0Q8mQ8dTJc2S8AMfju+ZR77pbg2yAdVyDlwkaUI7Har0LyOMRFPHrJ9lYdyjZZswdlQ==
+
+array-unique@^0.3.2:
+  version "0.3.2"
+  resolved 
"https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428";
+  integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
+
+arrify@^1.0.1:
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d";
+  integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
+
+arrify@^2.0.1:
+  version "2.0.1"
+  resolved 
"https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa";
+  integrity 
sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==
+
+asn1@~0.2.3:
+  version "0.2.4"
+  resolved 
"https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136";
+  integrity 
sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
+  dependencies:
+    safer-buffer "~2.1.0"
+
+assert-plus@1.0.0, assert-plus@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525";
+  integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
+
+assign-symbols@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367";
+  integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
+
+async-each@^1.0.3:
+  version "1.0.3"
+  resolved 
"https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf";
+  integrity 
sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
+
+asynckit@^0.4.0:
+  version "0.4.0"
+  resolved 
"https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79";
+  integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
+
+atob@^2.1.1:
+  version "2.1.2"
+  resolved 
"https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9";
+  integrity 
sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
+
+ava@2.1.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/ava/-/ava-2.1.0.tgz#158c5cdf48d1556caf0a9fdf96b61af9e566c153";
+  integrity 
sha512-IaS+l1KfYtFpJlDZXrNG0M6SGr/DUvwJmTiaTaW2lMdEezCAJc5J/hNAQWIKigkmxIOURYT29atD/qyp+zq+wg==
+  dependencies:
+    "@ava/babel-preset-stage-4" "^3.0.0"
+    "@ava/babel-preset-transform-test-files" "^5.0.0"
+    "@babel/core" "^7.4.5"
+    "@babel/generator" "^7.4.4"
+    "@babel/plugin-syntax-async-generators" "^7.2.0"
+    "@babel/plugin-syntax-object-rest-spread" "^7.2.0"
+    "@babel/plugin-syntax-optional-catch-binding" "^7.2.0"
+    "@concordance/react" "^2.0.0"
+    ansi-escapes "^4.1.0"
+    ansi-styles "^4.0.0"
+    arr-flatten "^1.1.0"
+    array-union "^2.1.0"
+    array-uniq "^2.1.0"
+    arrify "^2.0.1"
+    bluebird "^3.5.5"
+    chalk "^2.4.2"
+    chokidar "^3.0.1"
+    chunkd "^1.0.0"
+    ci-parallel-vars "^1.0.0"
+    clean-stack "^2.1.0"
+    clean-yaml-object "^0.1.0"
+    cli-cursor "^3.0.0"
+    cli-truncate "^1.1.0"
+    code-excerpt "^2.1.1"
+    common-path-prefix "^1.0.0"
+    concordance "^4.0.0"
+    convert-source-map "^1.6.0"
+    currently-unhandled "^0.4.1"
+    debug "^4.1.1"
+    del "^4.1.1"
+    dot-prop "^5.0.1"
+    emittery "^0.4.1"
+    empower-core "^1.2.0"
+    equal-length "^1.0.0"
+    escape-string-regexp "^2.0.0"
+    esm "^3.2.25"
+    figures "^3.0.0"
+    find-up "^4.0.0"
+    get-port "^5.0.0"
+    globby "^9.2.0"
+    ignore-by-default "^1.0.0"
+    import-local "^2.0.0"
+    indent-string "^4.0.0"
+    is-ci "^2.0.0"
+    is-error "^2.2.2"
+    is-observable "^2.0.0"
+    is-plain-object "^3.0.0"
+    is-promise "^2.1.0"
+    lodash "^4.17.11"
+    loud-rejection "^2.1.0"
+    make-dir "^3.0.0"
+    matcher "^2.0.0"
+    md5-hex "^3.0.0"
+    meow "^5.0.0"
+    micromatch "^4.0.2"
+    ms "^2.1.2"
+    observable-to-promise "^1.0.0"
+    ora "^3.4.0"
+    package-hash "^4.0.0"
+    pkg-conf "^3.1.0"
+    plur "^3.1.1"
+    pretty-ms "^5.0.0"
+    require-precompiled "^0.1.0"
+    resolve-cwd "^3.0.0"
+    slash "^3.0.0"
+    source-map-support "^0.5.12"
+    stack-utils "^1.0.2"
+    strip-ansi "^5.2.0"
+    strip-bom-buf "^2.0.0"
+    supertap "^1.0.0"
+    supports-color "^6.1.0"
+    trim-off-newlines "^1.0.1"
+    trim-right "^1.0.1"
+    unique-temp-dir "^1.0.0"
+    update-notifier "^3.0.0"
+    write-file-atomic "^3.0.0"
+
+aws-sign2@~0.7.0:
+  version "0.7.0"
+  resolved 
"https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8";
+  integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
+
+aws4@^1.8.0:
+  version "1.8.0"
+  resolved 
"https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f";
+  integrity 
sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==
+
+babel-plugin-espower@^3.0.1:
+  version "3.0.1"
+  resolved 
"https://registry.yarnpkg.com/babel-plugin-espower/-/babel-plugin-espower-3.0.1.tgz#180db17126f88e754105b8b5216d21e520a6bd4e";
+  integrity 
sha512-Ms49U7VIAtQ/TtcqRbD6UBmJBUCSxiC3+zPc+eGqxKUIFO1lTshyEDRUjhoAbd2rWfwYf3cZ62oXozrd8W6J0A==
+  dependencies:
+    "@babel/generator" "^7.0.0"
+    "@babel/parser" "^7.0.0"
+    call-matcher "^1.0.0"
+    core-js "^2.0.0"
+    espower-location-detector "^1.0.0"
+    espurify "^1.6.0"
+    estraverse "^4.1.1"
+
+balanced-match@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767";
+  integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+
+base@^0.11.1:
+  version "0.11.2"
+  resolved 
"https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f";
+  integrity 
sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==
+  dependencies:
+    cache-base "^1.0.1"
+    class-utils "^0.3.5"
+    component-emitter "^1.2.1"
+    define-property "^1.0.0"
+    isobject "^3.0.1"
+    mixin-deep "^1.2.0"
+    pascalcase "^0.1.1"
+
+bcrypt-pbkdf@^1.0.0:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e";
+  integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
+  dependencies:
+    tweetnacl "^0.14.3"
+
+binary-extensions@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c";
+  integrity 
sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==
+
+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==
+
+boxen@^3.0.0:
+  version "3.2.0"
+  resolved 
"https://registry.yarnpkg.com/boxen/-/boxen-3.2.0.tgz#fbdff0de93636ab4450886b6ff45b92d098f45eb";
+  integrity 
sha512-cU4J/+NodM3IHdSL2yN8bqYqnmlBTidDR4RC7nJs61ZmtGz8VZzM3HLQX0zY5mrSmPtR3xWwsq2jOUQqFZN8+A==
+  dependencies:
+    ansi-align "^3.0.0"
+    camelcase "^5.3.1"
+    chalk "^2.4.2"
+    cli-boxes "^2.2.0"
+    string-width "^3.0.0"
+    term-size "^1.2.0"
+    type-fest "^0.3.0"
+    widest-line "^2.0.0"
+
+brace-expansion@^1.1.7:
+  version "1.1.11"
+  resolved 
"https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd";
+  integrity 
sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+  dependencies:
+    balanced-match "^1.0.0"
+    concat-map "0.0.1"
+
+braces@^2.3.1:
+  version "2.3.2"
+  resolved 
"https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729";
+  integrity 
sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==
+  dependencies:
+    arr-flatten "^1.1.0"
+    array-unique "^0.3.2"
+    extend-shallow "^2.0.1"
+    fill-range "^4.0.0"
+    isobject "^3.0.1"
+    repeat-element "^1.1.2"
+    snapdragon "^0.8.1"
+    snapdragon-node "^2.0.1"
+    split-string "^3.0.2"
+    to-regex "^3.0.1"
+
+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==
+  dependencies:
+    fill-range "^7.0.1"
+
+buffer-from@^1.0.0:
+  version "1.1.1"
+  resolved 
"https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef";
+  integrity 
sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
+
+cache-base@^1.0.1:
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2";
+  integrity 
sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==
+  dependencies:
+    collection-visit "^1.0.0"
+    component-emitter "^1.2.1"
+    get-value "^2.0.6"
+    has-value "^1.0.0"
+    isobject "^3.0.1"
+    set-value "^2.0.0"
+    to-object-path "^0.3.0"
+    union-value "^1.0.0"
+    unset-value "^1.0.0"
+
+cacheable-request@^6.0.0:
+  version "6.1.0"
+  resolved 
"https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912";
+  integrity 
sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==
+  dependencies:
+    clone-response "^1.0.2"
+    get-stream "^5.1.0"
+    http-cache-semantics "^4.0.0"
+    keyv "^3.0.0"
+    lowercase-keys "^2.0.0"
+    normalize-url "^4.1.0"
+    responselike "^1.0.2"
+
+call-matcher@^1.0.0:
+  version "1.1.0"
+  resolved 
"https://registry.yarnpkg.com/call-matcher/-/call-matcher-1.1.0.tgz#23b2c1bc7a8394c8be28609d77ddbd5786680432";
+  integrity 
sha512-IoQLeNwwf9KTNbtSA7aEBb1yfDbdnzwjCetjkC8io5oGeOmK2CBNdg0xr+tadRYKO0p7uQyZzvon0kXlZbvGrw==
+  dependencies:
+    core-js "^2.0.0"
+    deep-equal "^1.0.0"
+    espurify "^1.6.0"
+    estraverse "^4.0.0"
+
+call-me-maybe@^1.0.1:
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b";
+  integrity sha1-JtII6onje1y95gJQoV8DHBak1ms=
+
+call-signature@0.0.2:
+  version "0.0.2"
+  resolved 
"https://registry.yarnpkg.com/call-signature/-/call-signature-0.0.2.tgz#a84abc825a55ef4cb2b028bd74e205a65b9a4996";
+  integrity sha1-qEq8glpV70yysCi9dOIFpluaSZY=
+
+camelcase-keys@^4.0.0:
+  version "4.2.0"
+  resolved 
"https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77";
+  integrity sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=
+  dependencies:
+    camelcase "^4.1.0"
+    map-obj "^2.0.0"
+    quick-lru "^1.0.0"
+
+camelcase@^4.1.0:
+  version "4.1.0"
+  resolved 
"https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd";
+  integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=
+
+camelcase@^5.3.1:
+  version "5.3.1"
+  resolved 
"https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320";
+  integrity 
sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
+
+caseless@~0.12.0:
+  version "0.12.0"
+  resolved 
"https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc";
+  integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
+
+chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2:
+  version "2.4.2"
+  resolved 
"https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424";
+  integrity 
sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+  dependencies:
+    ansi-styles "^3.2.1"
+    escape-string-regexp "^1.0.5"
+    supports-color "^5.3.0"
+
+chokidar@^3.0.1:
+  version "3.0.1"
+  resolved 
"https://registry.yarnpkg.com/chokidar/-/chokidar-3.0.1.tgz#98fe9aa476c55d9aea7841d6325ffdb30e95b40c";
+  integrity 
sha512-2ww34sJWehnbpV0Q4k4V5Hh7juo7po6z7LUWkcIQnSGN1lHOL8GGtLtfwabKvLFQw/hbSUQ0u6V7OgGYgBzlkQ==
+  dependencies:
+    anymatch "^3.0.1"
+    async-each "^1.0.3"
+    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.0.2"
+  optionalDependencies:
+    fsevents "^2.0.6"
+
+chownr@^1.1.1:
+  version "1.1.1"
+  resolved 
"https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494";
+  integrity 
sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==
+
+chunkd@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/chunkd/-/chunkd-1.0.0.tgz#4ead4a3704bcce510c4bb4d4a8be30c557836dd1";
+  integrity 
sha512-xx3Pb5VF9QaqCotolyZ1ywFBgyuJmu6+9dLiqBxgelEse9Xsr3yUlpoX3O4Oh11M00GT2kYMsRByTKIMJW2Lkg==
+
+ci-info@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46";
+  integrity 
sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
+
+ci-parallel-vars@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/ci-parallel-vars/-/ci-parallel-vars-1.0.0.tgz#af97729ed1c7381911ca37bcea263d62638701b3";
+  integrity 
sha512-u6dx20FBXm+apMi+5x7UVm6EH7BL1gc4XrcnQewjcB7HWRcor/V5qWc3RG2HwpgDJ26gIi2DSEu3B7sXynAw/g==
+
+class-utils@^0.3.5:
+  version "0.3.6"
+  resolved 
"https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463";
+  integrity 
sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==
+  dependencies:
+    arr-union "^3.1.0"
+    define-property "^0.2.5"
+    isobject "^3.0.0"
+    static-extend "^0.1.1"
+
+clean-stack@^2.1.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.1.0.tgz#9e7fec7f3f8340a2ab4f127c80273085e8fbbdd0";
+  integrity 
sha512-uQWrpRm+iZZUCAp7ZZJQbd4Za9I3AjR/3YTjmcnAtkauaIm/T5CT6U8zVI6e60T6OANqBFAzuR9/HB3NzuZCRA==
+
+clean-yaml-object@^0.1.0:
+  version "0.1.0"
+  resolved 
"https://registry.yarnpkg.com/clean-yaml-object/-/clean-yaml-object-0.1.0.tgz#63fb110dc2ce1a84dc21f6d9334876d010ae8b68";
+  integrity sha1-Y/sRDcLOGoTcIfbZM0h20BCui2g=
+
+cli-boxes@^2.2.0:
+  version "2.2.0"
+  resolved 
"https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.0.tgz#538ecae8f9c6ca508e3c3c95b453fe93cb4c168d";
+  integrity 
sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==
+
+cli-cursor@^2.1.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5";
+  integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=
+  dependencies:
+    restore-cursor "^2.0.0"
+
+cli-cursor@^3.0.0:
+  version "3.1.0"
+  resolved 
"https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307";
+  integrity 
sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
+  dependencies:
+    restore-cursor "^3.1.0"
+
+cli-spinners@^2.0.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.1.0.tgz#22c34b4d51f573240885b201efda4e4ec9fff3c7";
+  integrity 
sha512-8B00fJOEh1HPrx4fo5eW16XmE1PcL1tGpGrxy63CXGP9nHdPBN63X75hA1zhvQuhVztJWLqV58Roj2qlNM7cAA==
+
+cli-truncate@^1.1.0:
+  version "1.1.0"
+  resolved 
"https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-1.1.0.tgz#2b2dfd83c53cfd3572b87fc4d430a808afb04086";
+  integrity 
sha512-bAtZo0u82gCfaAGfSNxUdTI9mNyza7D8w4CVCcaOsy7sgwDzvx6ekr6cuWJqY3UGzgnQ1+4wgENup5eIhgxEYA==
+  dependencies:
+    slice-ansi "^1.0.0"
+    string-width "^2.0.0"
+
+clone-response@^1.0.2:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b";
+  integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=
+  dependencies:
+    mimic-response "^1.0.0"
+
+clone@^1.0.2:
+  version "1.0.4"
+  resolved 
"https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e";
+  integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
+
+code-excerpt@^2.1.1:
+  version "2.1.1"
+  resolved 
"https://registry.yarnpkg.com/code-excerpt/-/code-excerpt-2.1.1.tgz#5fe3057bfbb71a5f300f659ef2cc0a47651ba77c";
+  integrity 
sha512-tJLhH3EpFm/1x7heIW0hemXJTUU5EWl2V0EIX558jp05Mt1U6DVryCgkp3l37cxqs+DNbNgxG43SkwJXpQ14Jw==
+  dependencies:
+    convert-to-spaces "^1.0.1"
+
+code-point-at@^1.0.0:
+  version "1.1.0"
+  resolved 
"https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77";
+  integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
+
+collection-visit@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0";
+  integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=
+  dependencies:
+    map-visit "^1.0.0"
+    object-visit "^1.0.0"
+
+color-convert@^1.9.0:
+  version "1.9.3"
+  resolved 
"https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8";
+  integrity 
sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+  dependencies:
+    color-name "1.1.3"
+
+color-convert@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.0.tgz#9851ac61cc0d3898a8a3088650d5bf447bf69d97";
+  integrity 
sha512-hzTicsCJIHdxih9+2aLR1tNGZX5qSJGRHDPVwSY26tVrEf55XNajLOBWz2UuWSIergszA09/bqnOiHyqx9fxQg==
+  dependencies:
+    color-name "~1.1.4"
+
+color-name@1.1.3:
+  version "1.1.3"
+  resolved 
"https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25";
+  integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
+
+color-name@~1.1.4:
+  version "1.1.4"
+  resolved 
"https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2";
+  integrity 
sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+combined-stream@^1.0.6, combined-stream@~1.0.6:
+  version "1.0.8"
+  resolved 
"https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f";
+  integrity 
sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+  dependencies:
+    delayed-stream "~1.0.0"
+
+common-path-prefix@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-1.0.0.tgz#cd52f6f0712e0baab97d6f9732874f22f47752c0";
+  integrity sha1-zVL28HEuC6q5fW+XModPIvR3UsA=
+
+component-emitter@^1.2.1:
+  version "1.3.0"
+  resolved 
"https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0";
+  integrity 
sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
+
+concat-map@0.0.1:
+  version "0.0.1"
+  resolved 
"https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b";
+  integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+concordance@^4.0.0:
+  version "4.0.0"
+  resolved 
"https://registry.yarnpkg.com/concordance/-/concordance-4.0.0.tgz#5932fdee397d129bdbc3a1885fbe69839b1b7e15";
+  integrity 
sha512-l0RFuB8RLfCS0Pt2Id39/oCPykE01pyxgAFypWTlaGRgvLkZrtczZ8atEHpTeEIW+zYWXTBuA9cCSeEOScxReQ==
+  dependencies:
+    date-time "^2.1.0"
+    esutils "^2.0.2"
+    fast-diff "^1.1.2"
+    js-string-escape "^1.0.1"
+    lodash.clonedeep "^4.5.0"
+    lodash.flattendeep "^4.4.0"
+    lodash.islength "^4.0.1"
+    lodash.merge "^4.6.1"
+    md5-hex "^2.0.0"
+    semver "^5.5.1"
+    well-known-symbols "^2.0.0"
+
+configstore@^4.0.0:
+  version "4.0.0"
+  resolved 
"https://registry.yarnpkg.com/configstore/-/configstore-4.0.0.tgz#5933311e95d3687efb592c528b922d9262d227e7";
+  integrity 
sha512-CmquAXFBocrzaSM8mtGPMM/HiWmyIpr4CcJl/rgY2uCObZ/S7cKU0silxslqJejl+t/T9HS8E0PUNQD81JGUEQ==
+  dependencies:
+    dot-prop "^4.1.0"
+    graceful-fs "^4.1.2"
+    make-dir "^1.0.0"
+    unique-string "^1.0.0"
+    write-file-atomic "^2.0.0"
+    xdg-basedir "^3.0.0"
+
+console-control-strings@^1.0.0, console-control-strings@~1.1.0:
+  version "1.1.0"
+  resolved 
"https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e";
+  integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
+
+convert-source-map@^1.1.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==
+  dependencies:
+    safe-buffer "~5.1.1"
+
+convert-to-spaces@^1.0.1:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/convert-to-spaces/-/convert-to-spaces-1.0.2.tgz#7e3e48bbe6d997b1417ddca2868204b4d3d85715";
+  integrity sha1-fj5Iu+bZl7FBfdyihoIEtNPYVxU=
+
+copy-descriptor@^0.1.0:
+  version "0.1.1"
+  resolved 
"https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d";
+  integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
+
+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==
+
+core-util-is@1.0.2, core-util-is@~1.0.0:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7";
+  integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
+
+cross-spawn@^5.0.1:
+  version "5.1.0"
+  resolved 
"https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449";
+  integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=
+  dependencies:
+    lru-cache "^4.0.1"
+    shebang-command "^1.2.0"
+    which "^1.2.9"
+
+crypto-random-string@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e";
+  integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=
+
+currently-unhandled@^0.4.1:
+  version "0.4.1"
+  resolved 
"https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea";
+  integrity sha1-mI3zP+qxke95mmE2nddsF635V+o=
+  dependencies:
+    array-find-index "^1.0.1"
+
+dashdash@^1.12.0:
+  version "1.14.1"
+  resolved 
"https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0";
+  integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
+  dependencies:
+    assert-plus "^1.0.0"
+
+date-time@^2.1.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/date-time/-/date-time-2.1.0.tgz#0286d1b4c769633b3ca13e1e62558d2dbdc2eba2";
+  integrity 
sha512-/9+C44X7lot0IeiyfgJmETtRMhBidBYM2QFFIkGa0U1k+hSyY87Nw7PY3eDqpvCBm7I3WCSfPeZskW/YYq6m4g==
+  dependencies:
+    time-zone "^1.0.0"
+
+debug@^2.2.0, debug@^2.3.3:
+  version "2.6.9"
+  resolved 
"https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f";
+  integrity 
sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+  dependencies:
+    ms "2.0.0"
+
+debug@^3.2.6:
+  version "3.2.6"
+  resolved 
"https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b";
+  integrity 
sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
+  dependencies:
+    ms "^2.1.1"
+
+debug@^4.1.0, debug@^4.1.1:
+  version "4.1.1"
+  resolved 
"https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791";
+  integrity 
sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
+  dependencies:
+    ms "^2.1.1"
+
+decamelize-keys@^1.0.0:
+  version "1.1.0"
+  resolved 
"https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9";
+  integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=
+  dependencies:
+    decamelize "^1.1.0"
+    map-obj "^1.0.0"
+
+decamelize@^1.1.0:
+  version "1.2.0"
+  resolved 
"https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290";
+  integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
+
+decode-uri-component@^0.2.0:
+  version "0.2.0"
+  resolved 
"https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545";
+  integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
+
+decompress-response@^3.3.0:
+  version "3.3.0"
+  resolved 
"https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3";
+  integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=
+  dependencies:
+    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=
+
+deep-extend@^0.6.0:
+  version "0.6.0"
+  resolved 
"https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac";
+  integrity 
sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
+
+defaults@^1.0.3:
+  version "1.0.3"
+  resolved 
"https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d";
+  integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=
+  dependencies:
+    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==
+
+define-property@^0.2.5:
+  version "0.2.5"
+  resolved 
"https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116";
+  integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=
+  dependencies:
+    is-descriptor "^0.1.0"
+
+define-property@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6";
+  integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY=
+  dependencies:
+    is-descriptor "^1.0.0"
+
+define-property@^2.0.2:
+  version "2.0.2"
+  resolved 
"https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d";
+  integrity 
sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==
+  dependencies:
+    is-descriptor "^1.0.2"
+    isobject "^3.0.1"
+
+del@^4.1.1:
+  version "4.1.1"
+  resolved 
"https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4";
+  integrity 
sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==
+  dependencies:
+    "@types/glob" "^7.1.1"
+    globby "^6.1.0"
+    is-path-cwd "^2.0.0"
+    is-path-in-cwd "^2.0.0"
+    p-map "^2.0.0"
+    pify "^4.0.1"
+    rimraf "^2.6.3"
+
+delayed-stream@~1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619";
+  integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
+
+delegates@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a";
+  integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
+
+detect-libc@^1.0.2:
+  version "1.0.3"
+  resolved 
"https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b";
+  integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
+
+dir-glob@^2.2.2:
+  version "2.2.2"
+  resolved 
"https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4";
+  integrity 
sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==
+  dependencies:
+    path-type "^3.0.0"
+
+dot-prop@^4.1.0:
+  version "4.2.0"
+  resolved 
"https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57";
+  integrity 
sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==
+  dependencies:
+    is-obj "^1.0.0"
+
+dot-prop@^5.0.1:
+  version "5.1.0"
+  resolved 
"https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.1.0.tgz#bdd8c986a77b83e3fca524e53786df916cabbd8a";
+  integrity 
sha512-n1oC6NBF+KM9oVXtjmen4Yo7HyAVWV2UUl50dCYJdw2924K6dX9bf9TTTWaKtYlRn0FEtxG27KS80ayVLixxJA==
+  dependencies:
+    is-obj "^2.0.0"
+
+duplexer3@^0.1.4:
+  version "0.1.4"
+  resolved 
"https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2";
+  integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
+
+ecc-jsbn@~0.1.1:
+  version "0.1.2"
+  resolved 
"https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9";
+  integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
+  dependencies:
+    jsbn "~0.1.0"
+    safer-buffer "^2.1.0"
+
+emittery@^0.4.1:
+  version "0.4.1"
+  resolved 
"https://registry.yarnpkg.com/emittery/-/emittery-0.4.1.tgz#abe9d3297389ba424ac87e53d1c701962ce7433d";
+  integrity 
sha512-r4eRSeStEGf6M5SKdrQhhLK5bOwOBxQhIE3YSTnZE3GpKiLfnnhE+tPtrJE79+eDJgm39BM6LSoI8SCx4HbwlQ==
+
+emoji-regex@^7.0.1:
+  version "7.0.3"
+  resolved 
"https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156";
+  integrity 
sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
+
+empower-core@^1.2.0:
+  version "1.2.0"
+  resolved 
"https://registry.yarnpkg.com/empower-core/-/empower-core-1.2.0.tgz#ce3fb2484d5187fa29c23fba8344b0b2fdf5601c";
+  integrity 
sha512-g6+K6Geyc1o6FdXs9HwrXleCFan7d66G5xSCfSF7x1mJDCes6t0om9lFQG3zOrzh3Bkb/45N0cZ5Gqsf7YrzGQ==
+  dependencies:
+    call-signature "0.0.2"
+    core-js "^2.0.0"
+
+end-of-stream@^1.1.0:
+  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==
+  dependencies:
+    once "^1.4.0"
+
+equal-length@^1.0.0:
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/equal-length/-/equal-length-1.0.1.tgz#21ca112d48ab24b4e1e7ffc0e5339d31fdfc274c";
+  integrity sha1-IcoRLUirJLTh5//A5TOdMf38J0w=
+
+error-ex@^1.3.1:
+  version "1.3.2"
+  resolved 
"https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf";
+  integrity 
sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
+  dependencies:
+    is-arrayish "^0.2.1"
+
+es6-error@^4.0.1:
+  version "4.1.1"
+  resolved 
"https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d";
+  integrity 
sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
+
+escape-string-regexp@^1.0.5:
+  version "1.0.5"
+  resolved 
"https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4";
+  integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
+
+escape-string-regexp@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344";
+  integrity 
sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
+
+esm@^3.2.25:
+  version "3.2.25"
+  resolved 
"https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10";
+  integrity 
sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==
+
+espower-location-detector@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/espower-location-detector/-/espower-location-detector-1.0.0.tgz#a17b7ecc59d30e179e2bef73fb4137704cb331b5";
+  integrity sha1-oXt+zFnTDheeK+9z+0E3cEyzMbU=
+  dependencies:
+    is-url "^1.2.1"
+    path-is-absolute "^1.0.0"
+    source-map "^0.5.0"
+    xtend "^4.0.0"
+
+esprima@^4.0.0:
+  version "4.0.1"
+  resolved 
"https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71";
+  integrity 
sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+
+espurify@^1.6.0:
+  version "1.8.1"
+  resolved 
"https://registry.yarnpkg.com/espurify/-/espurify-1.8.1.tgz#5746c6c1ab42d302de10bd1d5bf7f0e8c0515056";
+  integrity 
sha512-ZDko6eY/o+D/gHCWyHTU85mKDgYcS4FJj7S+YD6WIInm7GQ6AnOjmcL4+buFV/JOztVLELi/7MmuGU5NHta0Mg==
+  dependencies:
+    core-js "^2.0.0"
+
+estraverse@^4.0.0, estraverse@^4.1.1:
+  version "4.2.0"
+  resolved 
"https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13";
+  integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=
+
+esutils@^2.0.2:
+  version "2.0.2"
+  resolved 
"https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b";
+  integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=
+
+execa@^0.7.0:
+  version "0.7.0"
+  resolved 
"https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777";
+  integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=
+  dependencies:
+    cross-spawn "^5.0.1"
+    get-stream "^3.0.0"
+    is-stream "^1.1.0"
+    npm-run-path "^2.0.0"
+    p-finally "^1.0.0"
+    signal-exit "^3.0.0"
+    strip-eof "^1.0.0"
+
+expand-brackets@^2.1.4:
+  version "2.1.4"
+  resolved 
"https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622";
+  integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI=
+  dependencies:
+    debug "^2.3.3"
+    define-property "^0.2.5"
+    extend-shallow "^2.0.1"
+    posix-character-classes "^0.1.0"
+    regex-not "^1.0.0"
+    snapdragon "^0.8.1"
+    to-regex "^3.0.1"
+
+extend-shallow@^2.0.1:
+  version "2.0.1"
+  resolved 
"https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f";
+  integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=
+  dependencies:
+    is-extendable "^0.1.0"
+
+extend-shallow@^3.0.0, extend-shallow@^3.0.2:
+  version "3.0.2"
+  resolved 
"https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8";
+  integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=
+  dependencies:
+    assign-symbols "^1.0.0"
+    is-extendable "^1.0.1"
+
+extend@~3.0.2:
+  version "3.0.2"
+  resolved 
"https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa";
+  integrity 
sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
+
+extglob@^2.0.4:
+  version "2.0.4"
+  resolved 
"https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543";
+  integrity 
sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==
+  dependencies:
+    array-unique "^0.3.2"
+    define-property "^1.0.0"
+    expand-brackets "^2.1.4"
+    extend-shallow "^2.0.1"
+    fragment-cache "^0.2.1"
+    regex-not "^1.0.0"
+    snapdragon "^0.8.1"
+    to-regex "^3.0.1"
+
+extsprintf@1.3.0:
+  version "1.3.0"
+  resolved 
"https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05";
+  integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
+
+extsprintf@^1.2.0:
+  version "1.4.0"
+  resolved 
"https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f";
+  integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
+
+fast-deep-equal@^2.0.1:
+  version "2.0.1"
+  resolved 
"https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49";
+  integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
+
+fast-diff@^1.1.2:
+  version "1.2.0"
+  resolved 
"https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03";
+  integrity 
sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
+
+fast-glob@^2.2.6:
+  version "2.2.7"
+  resolved 
"https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d";
+  integrity 
sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==
+  dependencies:
+    "@mrmlnc/readdir-enhanced" "^2.2.1"
+    "@nodelib/fs.stat" "^1.1.2"
+    glob-parent "^3.1.0"
+    is-glob "^4.0.0"
+    merge2 "^1.2.3"
+    micromatch "^3.1.10"
+
+fast-json-stable-stringify@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2";
+  integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I=
+
+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==
+  dependencies:
+    escape-string-regexp "^1.0.5"
+
+fill-range@^4.0.0:
+  version "4.0.0"
+  resolved 
"https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7";
+  integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=
+  dependencies:
+    extend-shallow "^2.0.1"
+    is-number "^3.0.0"
+    repeat-string "^1.6.1"
+    to-regex-range "^2.1.0"
+
+fill-range@^7.0.1:
+  version "7.0.1"
+  resolved 
"https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40";
+  integrity 
sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
+  dependencies:
+    to-regex-range "^5.0.1"
+
+find-up@^2.0.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7";
+  integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c=
+  dependencies:
+    locate-path "^2.0.0"
+
+find-up@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73";
+  integrity 
sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
+  dependencies:
+    locate-path "^3.0.0"
+
+find-up@^4.0.0:
+  version "4.1.0"
+  resolved 
"https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19";
+  integrity 
sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
+  dependencies:
+    locate-path "^5.0.0"
+    path-exists "^4.0.0"
+
+for-in@^1.0.2:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80";
+  integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=
+
+forever-agent@~0.6.1:
+  version "0.6.1"
+  resolved 
"https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91";
+  integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
+
+form-data@~2.3.2:
+  version "2.3.3"
+  resolved 
"https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6";
+  integrity 
sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
+  dependencies:
+    asynckit "^0.4.0"
+    combined-stream "^1.0.6"
+    mime-types "^2.1.12"
+
+fragment-cache@^0.2.1:
+  version "0.2.1"
+  resolved 
"https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19";
+  integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=
+  dependencies:
+    map-cache "^0.2.2"
+
+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==
+  dependencies:
+    minipass "^2.2.1"
+
+fs.realpath@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f";
+  integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+fsevents@^2.0.6:
+  version "2.0.7"
+  resolved 
"https://registry.yarnpkg.com/fsevents/-/fsevents-2.0.7.tgz#382c9b443c6cbac4c57187cdda23aa3bf1ccfc2a";
+  integrity 
sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==
+
+gauge@~2.7.3:
+  version "2.7.4"
+  resolved 
"https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7";
+  integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=
+  dependencies:
+    aproba "^1.0.3"
+    console-control-strings "^1.0.0"
+    has-unicode "^2.0.0"
+    object-assign "^4.1.0"
+    signal-exit "^3.0.0"
+    string-width "^1.0.1"
+    strip-ansi "^3.0.1"
+    wide-align "^1.1.0"
+
+get-port@^5.0.0:
+  version "5.0.0"
+  resolved 
"https://registry.yarnpkg.com/get-port/-/get-port-5.0.0.tgz#aa22b6b86fd926dd7884de3e23332c9f70c031a6";
+  integrity 
sha512-imzMU0FjsZqNa6BqOjbbW6w5BivHIuQKopjpPqcnx0AVHJQKCxK1O+Ab3OrVXhrekqfVMjwA9ZYu062R+KcIsQ==
+  dependencies:
+    type-fest "^0.3.0"
+
+get-stream@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14";
+  integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=
+
+get-stream@^4.1.0:
+  version "4.1.0"
+  resolved 
"https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5";
+  integrity 
sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
+  dependencies:
+    pump "^3.0.0"
+
+get-stream@^5.1.0:
+  version "5.1.0"
+  resolved 
"https://registry.yarnpkg.com/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9";
+  integrity 
sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==
+  dependencies:
+    pump "^3.0.0"
+
+get-value@^2.0.3, get-value@^2.0.6:
+  version "2.0.6"
+  resolved 
"https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28";
+  integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=
+
+getpass@^0.1.1:
+  version "0.1.7"
+  resolved 
"https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa";
+  integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
+  dependencies:
+    assert-plus "^1.0.0"
+
+glob-parent@^3.1.0:
+  version "3.1.0"
+  resolved 
"https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae";
+  integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=
+  dependencies:
+    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==
+  dependencies:
+    is-glob "^4.0.1"
+
+glob-to-regexp@^0.3.0:
+  version "0.3.0"
+  resolved 
"https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab";
+  integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=
+
+glob@^7.0.3, glob@^7.1.3:
+  version "7.1.4"
+  resolved 
"https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255";
+  integrity 
sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^3.0.4"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
+global-dirs@^0.1.0:
+  version "0.1.1"
+  resolved 
"https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445";
+  integrity sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=
+  dependencies:
+    ini "^1.3.4"
+
+globals@^11.1.0:
+  version "11.12.0"
+  resolved 
"https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e";
+  integrity 
sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
+
+globby@^6.1.0:
+  version "6.1.0"
+  resolved 
"https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c";
+  integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=
+  dependencies:
+    array-union "^1.0.1"
+    glob "^7.0.3"
+    object-assign "^4.0.1"
+    pify "^2.0.0"
+    pinkie-promise "^2.0.0"
+
+globby@^9.2.0:
+  version "9.2.0"
+  resolved 
"https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d";
+  integrity 
sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==
+  dependencies:
+    "@types/glob" "^7.1.1"
+    array-union "^1.0.2"
+    dir-glob "^2.2.2"
+    fast-glob "^2.2.6"
+    glob "^7.1.3"
+    ignore "^4.0.3"
+    pify "^4.0.1"
+    slash "^2.0.0"
+
+got@^9.6.0:
+  version "9.6.0"
+  resolved 
"https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85";
+  integrity 
sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==
+  dependencies:
+    "@sindresorhus/is" "^0.14.0"
+    "@szmarczak/http-timer" "^1.1.2"
+    cacheable-request "^6.0.0"
+    decompress-response "^3.3.0"
+    duplexer3 "^0.1.4"
+    get-stream "^4.1.0"
+    lowercase-keys "^1.0.1"
+    mimic-response "^1.0.1"
+    p-cancelable "^1.0.0"
+    to-readable-stream "^1.0.0"
+    url-parse-lax "^3.0.0"
+
+graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2:
+  version "4.1.15"
+  resolved 
"https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00";
+  integrity 
sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
+
+har-schema@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92";
+  integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
+
+har-validator@~5.1.0:
+  version "5.1.3"
+  resolved 
"https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080";
+  integrity 
sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
+  dependencies:
+    ajv "^6.5.5"
+    har-schema "^2.0.0"
+
+has-flag@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd";
+  integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
+
+has-unicode@^2.0.0:
+  version "2.0.1"
+  resolved 
"https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9";
+  integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
+
+has-value@^0.3.1:
+  version "0.3.1"
+  resolved 
"https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f";
+  integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=
+  dependencies:
+    get-value "^2.0.3"
+    has-values "^0.1.4"
+    isobject "^2.0.0"
+
+has-value@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177";
+  integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=
+  dependencies:
+    get-value "^2.0.6"
+    has-values "^1.0.0"
+    isobject "^3.0.0"
+
+has-values@^0.1.4:
+  version "0.1.4"
+  resolved 
"https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771";
+  integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E=
+
+has-values@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f";
+  integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=
+  dependencies:
+    is-number "^3.0.0"
+    kind-of "^4.0.0"
+
+has-yarn@^2.1.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77";
+  integrity 
sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==
+
+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==
+  dependencies:
+    is-stream "^1.1.0"
+    type-fest "^0.3.0"
+
+hosted-git-info@^2.1.4:
+  version "2.7.1"
+  resolved 
"https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047";
+  integrity 
sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==
+
+http-cache-semantics@^4.0.0:
+  version "4.0.3"
+  resolved 
"https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz#495704773277eeef6e43f9ab2c2c7d259dda25c5";
+  integrity 
sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew==
+
+http-signature@~1.2.0:
+  version "1.2.0"
+  resolved 
"https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1";
+  integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
+  dependencies:
+    assert-plus "^1.0.0"
+    jsprim "^1.2.2"
+    sshpk "^1.7.0"
+
+iconv-lite@^0.4.4:
+  version "0.4.24"
+  resolved 
"https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b";
+  integrity 
sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
+  dependencies:
+    safer-buffer ">= 2.1.2 < 3"
+
+ignore-by-default@^1.0.0:
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09";
+  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==
+  dependencies:
+    minimatch "^3.0.4"
+
+ignore@^4.0.3:
+  version "4.0.6"
+  resolved 
"https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc";
+  integrity 
sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
+
+import-lazy@^2.1.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43";
+  integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=
+
+import-local@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d";
+  integrity 
sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==
+  dependencies:
+    pkg-dir "^3.0.0"
+    resolve-cwd "^2.0.0"
+
+imurmurhash@^0.1.4:
+  version "0.1.4"
+  resolved 
"https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea";
+  integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
+
+indent-string@^3.0.0, indent-string@^3.2.0:
+  version "3.2.0"
+  resolved 
"https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289";
+  integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=
+
+indent-string@^4.0.0:
+  version "4.0.0"
+  resolved 
"https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251";
+  integrity 
sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
+
+inflight@^1.0.4:
+  version "1.0.6"
+  resolved 
"https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9";
+  integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+  dependencies:
+    once "^1.3.0"
+    wrappy "1"
+
+inherits@2, inherits@~2.0.3:
+  version "2.0.4"
+  resolved 
"https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c";
+  integrity 
sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+ini@^1.3.4, ini@~1.3.0:
+  version "1.3.5"
+  resolved 
"https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927";
+  integrity 
sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
+
+irregular-plurals@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-2.0.0.tgz#39d40f05b00f656d0b7fa471230dd3b714af2872";
+  integrity 
sha512-Y75zBYLkh0lJ9qxeHlMjQ7bSbyiSqNW/UOPWDmzC7cXskL1hekSITh1Oc6JV0XCWWZ9DE8VYSB71xocLk3gmGw==
+
+is-accessor-descriptor@^0.1.6:
+  version "0.1.6"
+  resolved 
"https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6";
+  integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=
+  dependencies:
+    kind-of "^3.0.2"
+
+is-accessor-descriptor@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656";
+  integrity 
sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==
+  dependencies:
+    kind-of "^6.0.0"
+
+is-arrayish@^0.2.1:
+  version "0.2.1"
+  resolved 
"https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d";
+  integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
+
+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==
+  dependencies:
+    binary-extensions "^2.0.0"
+
+is-buffer@^1.1.5:
+  version "1.1.6"
+  resolved 
"https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be";
+  integrity 
sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
+
+is-ci@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c";
+  integrity 
sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==
+  dependencies:
+    ci-info "^2.0.0"
+
+is-data-descriptor@^0.1.4:
+  version "0.1.4"
+  resolved 
"https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56";
+  integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=
+  dependencies:
+    kind-of "^3.0.2"
+
+is-data-descriptor@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7";
+  integrity 
sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==
+  dependencies:
+    kind-of "^6.0.0"
+
+is-descriptor@^0.1.0:
+  version "0.1.6"
+  resolved 
"https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca";
+  integrity 
sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==
+  dependencies:
+    is-accessor-descriptor "^0.1.6"
+    is-data-descriptor "^0.1.4"
+    kind-of "^5.0.0"
+
+is-descriptor@^1.0.0, is-descriptor@^1.0.2:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec";
+  integrity 
sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==
+  dependencies:
+    is-accessor-descriptor "^1.0.0"
+    is-data-descriptor "^1.0.0"
+    kind-of "^6.0.2"
+
+is-error@^2.2.2:
+  version "2.2.2"
+  resolved 
"https://registry.yarnpkg.com/is-error/-/is-error-2.2.2.tgz#c10ade187b3c93510c5470a5567833ee25649843";
+  integrity 
sha512-IOQqts/aHWbiisY5DuPJQ0gcbvaLFCa7fBa9xoLfxBZvQ+ZI/Zh9xoI7Gk+G64N0FdK4AbibytHht2tWgpJWLg==
+
+is-extendable@^0.1.0, is-extendable@^0.1.1:
+  version "0.1.1"
+  resolved 
"https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89";
+  integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=
+
+is-extendable@^1.0.1:
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4";
+  integrity 
sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==
+  dependencies:
+    is-plain-object "^2.0.4"
+
+is-extglob@^2.1.0, is-extglob@^2.1.1:
+  version "2.1.1"
+  resolved 
"https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2";
+  integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
+
+is-fullwidth-code-point@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb";
+  integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
+  dependencies:
+    number-is-nan "^1.0.0"
+
+is-fullwidth-code-point@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f";
+  integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
+
+is-glob@^3.1.0:
+  version "3.1.0"
+  resolved 
"https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a";
+  integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=
+  dependencies:
+    is-extglob "^2.1.0"
+
+is-glob@^4.0.0, 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==
+  dependencies:
+    is-extglob "^2.1.1"
+
+is-installed-globally@^0.1.0:
+  version "0.1.0"
+  resolved 
"https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80";
+  integrity sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=
+  dependencies:
+    global-dirs "^0.1.0"
+    is-path-inside "^1.0.0"
+
+is-npm@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/is-npm/-/is-npm-3.0.0.tgz#ec9147bfb629c43f494cf67936a961edec7e8053";
+  integrity 
sha512-wsigDr1Kkschp2opC4G3yA6r9EgVA6NjRpWzIi9axXqeIaAATPRJc4uLujXe3Nd9uO8KoDyA4MD6aZSeXTADhA==
+
+is-number@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195";
+  integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=
+  dependencies:
+    kind-of "^3.0.2"
+
+is-number@^7.0.0:
+  version "7.0.0"
+  resolved 
"https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b";
+  integrity 
sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
+is-obj@^1.0.0:
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f";
+  integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8=
+
+is-obj@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982";
+  integrity 
sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==
+
+is-observable@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/is-observable/-/is-observable-2.0.0.tgz#327af1e8cdea9cd717f95911b87c5d34301721a6";
+  integrity 
sha512-fhBZv3eFKUbyHXZ1oHujdo2tZ+CNbdpdzzlENgCGZUC8keoGxUew2jYFLYcUB4qo7LDD03o4KK11m/QYD7kEjg==
+
+is-path-cwd@^2.0.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.1.0.tgz#2e0c7e463ff5b7a0eb60852d851a6809347a124c";
+  integrity 
sha512-Sc5j3/YnM8tDeyCsVeKlm/0p95075DyLmDEIkSgQ7mXkrOX+uTCtmQFm0CYzVyJwcCCmO3k8qfJt17SxQwB5Zw==
+
+is-path-in-cwd@^2.0.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb";
+  integrity 
sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==
+  dependencies:
+    is-path-inside "^2.1.0"
+
+is-path-inside@^1.0.0:
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036";
+  integrity sha1-jvW33lBDej/cprToZe96pVy0gDY=
+  dependencies:
+    path-is-inside "^1.0.1"
+
+is-path-inside@^2.1.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2";
+  integrity 
sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==
+  dependencies:
+    path-is-inside "^1.0.2"
+
+is-plain-obj@^1.1.0:
+  version "1.1.0"
+  resolved 
"https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e";
+  integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4=
+
+is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
+  version "2.0.4"
+  resolved 
"https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677";
+  integrity 
sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
+  dependencies:
+    isobject "^3.0.1"
+
+is-plain-object@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.0.tgz#47bfc5da1b5d50d64110806c199359482e75a928";
+  integrity 
sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==
+  dependencies:
+    isobject "^4.0.0"
+
+is-promise@^2.1.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa";
+  integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=
+
+is-stream@^1.1.0:
+  version "1.1.0"
+  resolved 
"https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44";
+  integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
+
+is-typedarray@^1.0.0, is-typedarray@~1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a";
+  integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
+
+is-url@^1.2.1:
+  version "1.2.4"
+  resolved 
"https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52";
+  integrity 
sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==
+
+is-utf8@^0.2.1:
+  version "0.2.1"
+  resolved 
"https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72";
+  integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=
+
+is-windows@^1.0.2:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d";
+  integrity 
sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
+
+is-yarn-global@^0.3.0:
+  version "0.3.0"
+  resolved 
"https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232";
+  integrity 
sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==
+
+isarray@1.0.0, isarray@~1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11";
+  integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
+
+isexe@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10";
+  integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
+
+isobject@^2.0.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89";
+  integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=
+  dependencies:
+    isarray "1.0.0"
+
+isobject@^3.0.0, isobject@^3.0.1:
+  version "3.0.1"
+  resolved 
"https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df";
+  integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
+
+isobject@^4.0.0:
+  version "4.0.0"
+  resolved 
"https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0";
+  integrity 
sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==
+
+isstream@~0.1.2:
+  version "0.1.2"
+  resolved 
"https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a";
+  integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
+
+js-string-escape@^1.0.1:
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef";
+  integrity sha1-4mJbrbwNZ8dTPp7cEGjFh65BN+8=
+
+js-tokens@^4.0.0:
+  version "4.0.0"
+  resolved 
"https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499";
+  integrity 
sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+js-yaml@^3.10.0:
+  version "3.13.1"
+  resolved 
"https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847";
+  integrity 
sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
+  dependencies:
+    argparse "^1.0.7"
+    esprima "^4.0.0"
+
+jsbn@~0.1.0:
+  version "0.1.1"
+  resolved 
"https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513";
+  integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
+
+jsesc@^2.5.1:
+  version "2.5.2"
+  resolved 
"https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4";
+  integrity 
sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
+
+jsesc@~0.5.0:
+  version "0.5.0"
+  resolved 
"https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d";
+  integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=
+
+json-buffer@3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898";
+  integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=
+
+json-parse-better-errors@^1.0.1:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9";
+  integrity 
sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
+
+json-schema-traverse@^0.4.1:
+  version "0.4.1"
+  resolved 
"https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660";
+  integrity 
sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-schema@0.2.3:
+  version "0.2.3"
+  resolved 
"https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13";
+  integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
+
+json-stringify-safe@~5.0.1:
+  version "5.0.1"
+  resolved 
"https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb";
+  integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
+
+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==
+  dependencies:
+    minimist "^1.2.0"
+
+jsprim@^1.2.2:
+  version "1.4.1"
+  resolved 
"https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2";
+  integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
+  dependencies:
+    assert-plus "1.0.0"
+    extsprintf "1.3.0"
+    json-schema "0.2.3"
+    verror "1.10.0"
+
+keyv@^3.0.0:
+  version "3.1.0"
+  resolved 
"https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9";
+  integrity 
sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==
+  dependencies:
+    json-buffer "3.0.0"
+
+kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
+  version "3.2.2"
+  resolved 
"https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64";
+  integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=
+  dependencies:
+    is-buffer "^1.1.5"
+
+kind-of@^4.0.0:
+  version "4.0.0"
+  resolved 
"https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57";
+  integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc=
+  dependencies:
+    is-buffer "^1.1.5"
+
+kind-of@^5.0.0:
+  version "5.1.0"
+  resolved 
"https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d";
+  integrity 
sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
+
+kind-of@^6.0.0, kind-of@^6.0.2:
+  version "6.0.2"
+  resolved 
"https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051";
+  integrity 
sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==
+
+latest-version@^5.0.0:
+  version "5.1.0"
+  resolved 
"https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face";
+  integrity 
sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==
+  dependencies:
+    package-json "^6.3.0"
+
+load-json-file@^4.0.0:
+  version "4.0.0"
+  resolved 
"https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b";
+  integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs=
+  dependencies:
+    graceful-fs "^4.1.2"
+    parse-json "^4.0.0"
+    pify "^3.0.0"
+    strip-bom "^3.0.0"
+
+load-json-file@^5.2.0:
+  version "5.3.0"
+  resolved 
"https://registry.yarnpkg.com/load-json-file/-/load-json-file-5.3.0.tgz#4d3c1e01fa1c03ea78a60ac7af932c9ce53403f3";
+  integrity 
sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==
+  dependencies:
+    graceful-fs "^4.1.15"
+    parse-json "^4.0.0"
+    pify "^4.0.1"
+    strip-bom "^3.0.0"
+    type-fest "^0.3.0"
+
+locate-path@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e";
+  integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=
+  dependencies:
+    p-locate "^2.0.0"
+    path-exists "^3.0.0"
+
+locate-path@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e";
+  integrity 
sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
+  dependencies:
+    p-locate "^3.0.0"
+    path-exists "^3.0.0"
+
+locate-path@^5.0.0:
+  version "5.0.0"
+  resolved 
"https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0";
+  integrity 
sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
+  dependencies:
+    p-locate "^4.1.0"
+
+lodash.clonedeep@^4.5.0:
+  version "4.5.0"
+  resolved 
"https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef";
+  integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
+
+lodash.flattendeep@^4.4.0:
+  version "4.4.0"
+  resolved 
"https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2";
+  integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=
+
+lodash.islength@^4.0.1:
+  version "4.0.1"
+  resolved 
"https://registry.yarnpkg.com/lodash.islength/-/lodash.islength-4.0.1.tgz#4e9868d452575d750affd358c979543dc20ed577";
+  integrity sha1-Tpho1FJXXXUK/9NYyXlUPcIO1Xc=
+
+lodash.merge@^4.6.1:
+  version "4.6.1"
+  resolved 
"https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54";
+  integrity 
sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==
+
+lodash@^4.17.11:
+  version "4.17.11"
+  resolved 
"https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d";
+  integrity 
sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
+
+log-symbols@^2.2.0:
+  version "2.2.0"
+  resolved 
"https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a";
+  integrity 
sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==
+  dependencies:
+    chalk "^2.0.1"
+
+loud-rejection@^1.0.0:
+  version "1.6.0"
+  resolved 
"https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f";
+  integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=
+  dependencies:
+    currently-unhandled "^0.4.1"
+    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==
+  dependencies:
+    currently-unhandled "^0.4.1"
+    signal-exit "^3.0.2"
+
+lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f";
+  integrity 
sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==
+
+lowercase-keys@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479";
+  integrity 
sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
+
+lru-cache@^4.0.1:
+  version "4.1.5"
+  resolved 
"https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd";
+  integrity 
sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
+  dependencies:
+    pseudomap "^1.0.2"
+    yallist "^2.1.2"
+
+make-dir@^1.0.0:
+  version "1.3.0"
+  resolved 
"https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c";
+  integrity 
sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==
+  dependencies:
+    pify "^3.0.0"
+
+make-dir@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/make-dir/-/make-dir-3.0.0.tgz#1b5f39f6b9270ed33f9f054c5c0f84304989f801";
+  integrity 
sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==
+  dependencies:
+    semver "^6.0.0"
+
+map-cache@^0.2.2:
+  version "0.2.2"
+  resolved 
"https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf";
+  integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=
+
+map-obj@^1.0.0:
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d";
+  integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=
+
+map-obj@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9";
+  integrity sha1-plzSkIepJZi4eRJXpSPgISIqwfk=
+
+map-visit@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f";
+  integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=
+  dependencies:
+    object-visit "^1.0.0"
+
+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==
+  dependencies:
+    escape-string-regexp "^2.0.0"
+
+md5-hex@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/md5-hex/-/md5-hex-2.0.0.tgz#d0588e9f1c74954492ecd24ac0ac6ce997d92e33";
+  integrity sha1-0FiOnxx0lUSS7NJKwKxs6ZfZLjM=
+  dependencies:
+    md5-o-matic "^0.1.1"
+
+md5-hex@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/md5-hex/-/md5-hex-3.0.0.tgz#96cf5c62cedea41e04124b9473ef7481db6de5fb";
+  integrity 
sha512-uA+EX5IV1r5lKBJecwTSec3k6xl4ziBUZihRiOpOHCeHjKA0ai6+eImamXQy/cI3Qep5mQgFTeJld9tcwdBNFw==
+  dependencies:
+    md5-o-matic "^0.1.1"
+
+md5-o-matic@^0.1.1:
+  version "0.1.1"
+  resolved 
"https://registry.yarnpkg.com/md5-o-matic/-/md5-o-matic-0.1.1.tgz#822bccd65e117c514fab176b25945d54100a03c3";
+  integrity sha1-givM1l4RfFFPqxdrJZRdVBAKA8M=
+
+meow@^5.0.0:
+  version "5.0.0"
+  resolved 
"https://registry.yarnpkg.com/meow/-/meow-5.0.0.tgz#dfc73d63a9afc714a5e371760eb5c88b91078aa4";
+  integrity 
sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==
+  dependencies:
+    camelcase-keys "^4.0.0"
+    decamelize-keys "^1.0.0"
+    loud-rejection "^1.0.0"
+    minimist-options "^3.0.1"
+    normalize-package-data "^2.3.4"
+    read-pkg-up "^3.0.0"
+    redent "^2.0.0"
+    trim-newlines "^2.0.0"
+    yargs-parser "^10.0.0"
+
+merge2@^1.2.3:
+  version "1.2.3"
+  resolved 
"https://registry.yarnpkg.com/merge2/-/merge2-1.2.3.tgz#7ee99dbd69bb6481689253f018488a1b902b0ed5";
+  integrity 
sha512-gdUU1Fwj5ep4kplwcmftruWofEFt6lfpkkr3h860CXbAB9c3hGb55EOL2ali0Td5oebvW0E1+3Sr+Ur7XfKpRA==
+
+micromatch@^3.1.10:
+  version "3.1.10"
+  resolved 
"https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23";
+  integrity 
sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==
+  dependencies:
+    arr-diff "^4.0.0"
+    array-unique "^0.3.2"
+    braces "^2.3.1"
+    define-property "^2.0.2"
+    extend-shallow "^3.0.2"
+    extglob "^2.0.4"
+    fragment-cache "^0.2.1"
+    kind-of "^6.0.2"
+    nanomatch "^1.2.9"
+    object.pick "^1.3.0"
+    regex-not "^1.0.0"
+    snapdragon "^0.8.1"
+    to-regex "^3.0.2"
+
+micromatch@^4.0.2:
+  version "4.0.2"
+  resolved 
"https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259";
+  integrity 
sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==
+  dependencies:
+    braces "^3.0.1"
+    picomatch "^2.0.5"
+
+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-types@^2.1.12, mime-types@~2.1.19:
+  version "2.1.24"
+  resolved 
"https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81";
+  integrity 
sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==
+  dependencies:
+    mime-db "1.40.0"
+
+mimic-fn@^1.0.0:
+  version "1.2.0"
+  resolved 
"https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022";
+  integrity 
sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
+
+mimic-fn@^2.1.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b";
+  integrity 
sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
+
+mimic-response@^1.0.0, mimic-response@^1.0.1:
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b";
+  integrity 
sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
+
+minimatch@^3.0.4:
+  version "3.0.4"
+  resolved 
"https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083";
+  integrity 
sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+  dependencies:
+    brace-expansion "^1.1.7"
+
+minimist-options@^3.0.1:
+  version "3.0.2"
+  resolved 
"https://registry.yarnpkg.com/minimist-options/-/minimist-options-3.0.2.tgz#fba4c8191339e13ecf4d61beb03f070103f3d954";
+  integrity 
sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==
+  dependencies:
+    arrify "^1.0.1"
+    is-plain-obj "^1.1.0"
+
+minimist@0.0.8:
+  version "0.0.8"
+  resolved 
"https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d";
+  integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
+
+minimist@^1.2.0:
+  version "1.2.0"
+  resolved 
"https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284";
+  integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
+
+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==
+  dependencies:
+    safe-buffer "^5.1.2"
+    yallist "^3.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==
+  dependencies:
+    minipass "^2.2.1"
+
+mixin-deep@^1.2.0:
+  version "1.3.1"
+  resolved 
"https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe";
+  integrity 
sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==
+  dependencies:
+    for-in "^1.0.2"
+    is-extendable "^1.0.1"
+
+mkdirp@^0.5.0, mkdirp@^0.5.1:
+  version "0.5.1"
+  resolved 
"https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903";
+  integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
+  dependencies:
+    minimist "0.0.8"
+
+ms@2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8";
+  integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
+
+ms@^2.1.1, ms@^2.1.2:
+  version "2.1.2"
+  resolved 
"https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009";
+  integrity 
sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+nan@^2.12.1:
+  version "2.14.0"
+  resolved 
"https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c";
+  integrity 
sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
+
+nanomatch@^1.2.9:
+  version "1.2.13"
+  resolved 
"https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119";
+  integrity 
sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==
+  dependencies:
+    arr-diff "^4.0.0"
+    array-unique "^0.3.2"
+    define-property "^2.0.2"
+    extend-shallow "^3.0.2"
+    fragment-cache "^0.2.1"
+    is-windows "^1.0.2"
+    kind-of "^6.0.2"
+    object.pick "^1.3.0"
+    regex-not "^1.0.0"
+    snapdragon "^0.8.1"
+    to-regex "^3.0.1"
+
+needle@^2.2.1:
+  version "2.4.0"
+  resolved 
"https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c";
+  integrity 
sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==
+  dependencies:
+    debug "^3.2.6"
+    iconv-lite "^0.4.4"
+    sax "^1.2.4"
+
+node-pre-gyp@^0.11.0:
+  version "0.11.0"
+  resolved 
"https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz#db1f33215272f692cd38f03238e3e9b47c5dd054";
+  integrity 
sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==
+  dependencies:
+    detect-libc "^1.0.2"
+    mkdirp "^0.5.1"
+    needle "^2.2.1"
+    nopt "^4.0.1"
+    npm-packlist "^1.1.6"
+    npmlog "^4.0.2"
+    rc "^1.2.7"
+    rimraf "^2.6.1"
+    semver "^5.3.0"
+    tar "^4"
+
+nopt@^4.0.1:
+  version "4.0.1"
+  resolved 
"https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d";
+  integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=
+  dependencies:
+    abbrev "1"
+    osenv "^0.1.4"
+
+normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
+  version "2.5.0"
+  resolved 
"https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8";
+  integrity 
sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
+  dependencies:
+    hosted-git-info "^2.1.4"
+    resolve "^1.10.0"
+    semver "2 || 3 || 4 || 5"
+    validate-npm-package-license "^3.0.1"
+
+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==
+
+npm-bundled@^1.0.1:
+  version "1.0.6"
+  resolved 
"https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd";
+  integrity 
sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==
+
+npm-packlist@^1.1.6:
+  version "1.4.1"
+  resolved 
"https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.1.tgz#19064cdf988da80ea3cee45533879d90192bbfbc";
+  integrity 
sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==
+  dependencies:
+    ignore-walk "^3.0.1"
+    npm-bundled "^1.0.1"
+
+npm-run-path@^2.0.0:
+  version "2.0.2"
+  resolved 
"https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f";
+  integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=
+  dependencies:
+    path-key "^2.0.0"
+
+npmlog@^4.0.2:
+  version "4.1.2"
+  resolved 
"https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b";
+  integrity 
sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
+  dependencies:
+    are-we-there-yet "~1.1.2"
+    console-control-strings "~1.1.0"
+    gauge "~2.7.3"
+    set-blocking "~2.0.0"
+
+number-is-nan@^1.0.0:
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d";
+  integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
+
+oauth-sign@~0.9.0:
+  version "0.9.0"
+  resolved 
"https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455";
+  integrity 
sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
+
+object-assign@^4.0.1, object-assign@^4.1.0:
+  version "4.1.1"
+  resolved 
"https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863";
+  integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
+
+object-copy@^0.1.0:
+  version "0.1.0"
+  resolved 
"https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c";
+  integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw=
+  dependencies:
+    copy-descriptor "^0.1.0"
+    define-property "^0.2.5"
+    kind-of "^3.0.3"
+
+object-visit@^1.0.0:
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb";
+  integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=
+  dependencies:
+    isobject "^3.0.0"
+
+object.pick@^1.3.0:
+  version "1.3.0"
+  resolved 
"https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747";
+  integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=
+  dependencies:
+    isobject "^3.0.1"
+
+observable-to-promise@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/observable-to-promise/-/observable-to-promise-1.0.0.tgz#37e136f16a15385ac063411ada0e1202bfff58f4";
+  integrity 
sha512-cqnGUrNsE6vdVDTPAX9/WeVzwy/z37vdxupdQXU8vgTXRFH72KCZiZga8aca2ulRPIeem8W3vW9rQHBwfIl2WA==
+  dependencies:
+    is-observable "^2.0.0"
+    symbol-observable "^1.0.4"
+
+once@^1.3.0, once@^1.3.1, once@^1.4.0:
+  version "1.4.0"
+  resolved 
"https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1";
+  integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+  dependencies:
+    wrappy "1"
+
+onetime@^2.0.0:
+  version "2.0.1"
+  resolved 
"https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4";
+  integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=
+  dependencies:
+    mimic-fn "^1.0.0"
+
+onetime@^5.1.0:
+  version "5.1.0"
+  resolved 
"https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5";
+  integrity 
sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==
+  dependencies:
+    mimic-fn "^2.1.0"
+
+ora@^3.4.0:
+  version "3.4.0"
+  resolved 
"https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318";
+  integrity 
sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==
+  dependencies:
+    chalk "^2.4.2"
+    cli-cursor "^2.1.0"
+    cli-spinners "^2.0.0"
+    log-symbols "^2.2.0"
+    strip-ansi "^5.2.0"
+    wcwidth "^1.0.1"
+
+os-homedir@^1.0.0:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3";
+  integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
+
+os-tmpdir@^1.0.0, os-tmpdir@^1.0.1:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274";
+  integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
+
+osenv@^0.1.4:
+  version "0.1.5"
+  resolved 
"https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410";
+  integrity 
sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==
+  dependencies:
+    os-homedir "^1.0.0"
+    os-tmpdir "^1.0.0"
+
+p-cancelable@^1.0.0:
+  version "1.1.0"
+  resolved 
"https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc";
+  integrity 
sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==
+
+p-finally@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae";
+  integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
+
+p-limit@^1.1.0:
+  version "1.3.0"
+  resolved 
"https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8";
+  integrity 
sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==
+  dependencies:
+    p-try "^1.0.0"
+
+p-limit@^2.0.0, p-limit@^2.2.0:
+  version "2.2.0"
+  resolved 
"https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.0.tgz#417c9941e6027a9abcba5092dd2904e255b5fbc2";
+  integrity 
sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==
+  dependencies:
+    p-try "^2.0.0"
+
+p-locate@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43";
+  integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=
+  dependencies:
+    p-limit "^1.1.0"
+
+p-locate@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4";
+  integrity 
sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
+  dependencies:
+    p-limit "^2.0.0"
+
+p-locate@^4.1.0:
+  version "4.1.0"
+  resolved 
"https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07";
+  integrity 
sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
+  dependencies:
+    p-limit "^2.2.0"
+
+p-map@^2.0.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175";
+  integrity 
sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==
+
+p-try@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3";
+  integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=
+
+p-try@^2.0.0:
+  version "2.2.0"
+  resolved 
"https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6";
+  integrity 
sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+
+package-hash@^4.0.0:
+  version "4.0.0"
+  resolved 
"https://registry.yarnpkg.com/package-hash/-/package-hash-4.0.0.tgz#3537f654665ec3cc38827387fc904c163c54f506";
+  integrity 
sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==
+  dependencies:
+    graceful-fs "^4.1.15"
+    hasha "^5.0.0"
+    lodash.flattendeep "^4.4.0"
+    release-zalgo "^1.0.0"
+
+package-json@^6.3.0:
+  version "6.4.0"
+  resolved 
"https://registry.yarnpkg.com/package-json/-/package-json-6.4.0.tgz#4f626976604f4a9a41723ce1792b204a60b1b61e";
+  integrity 
sha512-bd1T8OBG7hcvMd9c/udgv6u5v9wISP3Oyl9Cm7Weop8EFwrtcQDnS2sb6zhwqus2WslSr5wSTIPiTTpxxmPm7Q==
+  dependencies:
+    got "^9.6.0"
+    registry-auth-token "^3.4.0"
+    registry-url "^5.0.0"
+    semver "^6.1.1"
+
+parse-json@^4.0.0:
+  version "4.0.0"
+  resolved 
"https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0";
+  integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=
+  dependencies:
+    error-ex "^1.3.1"
+    json-parse-better-errors "^1.0.1"
+
+parse-ms@^2.1.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d";
+  integrity 
sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==
+
+pascalcase@^0.1.1:
+  version "0.1.1"
+  resolved 
"https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14";
+  integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=
+
+path-dirname@^1.0.0:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0";
+  integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=
+
+path-exists@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515";
+  integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
+
+path-exists@^4.0.0:
+  version "4.0.0"
+  resolved 
"https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3";
+  integrity 
sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
+
+path-is-absolute@^1.0.0:
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f";
+  integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+path-is-inside@^1.0.1, path-is-inside@^1.0.2:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53";
+  integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
+
+path-key@^2.0.0:
+  version "2.0.1"
+  resolved 
"https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40";
+  integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
+
+path-parse@^1.0.6:
+  version "1.0.6"
+  resolved 
"https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c";
+  integrity 
sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
+
+path-type@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f";
+  integrity 
sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==
+  dependencies:
+    pify "^3.0.0"
+
+performance-now@^2.1.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b";
+  integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
+
+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==
+
+pify@^2.0.0:
+  version "2.3.0"
+  resolved 
"https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c";
+  integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
+
+pify@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176";
+  integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
+
+pify@^4.0.1:
+  version "4.0.1"
+  resolved 
"https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231";
+  integrity 
sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
+
+pinkie-promise@^2.0.0:
+  version "2.0.1"
+  resolved 
"https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa";
+  integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o=
+  dependencies:
+    pinkie "^2.0.0"
+
+pinkie@^2.0.0:
+  version "2.0.4"
+  resolved 
"https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870";
+  integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
+
+pkg-conf@^3.1.0:
+  version "3.1.0"
+  resolved 
"https://registry.yarnpkg.com/pkg-conf/-/pkg-conf-3.1.0.tgz#d9f9c75ea1bae0e77938cde045b276dac7cc69ae";
+  integrity 
sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ==
+  dependencies:
+    find-up "^3.0.0"
+    load-json-file "^5.2.0"
+
+pkg-dir@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3";
+  integrity 
sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==
+  dependencies:
+    find-up "^3.0.0"
+
+plur@^3.1.1:
+  version "3.1.1"
+  resolved 
"https://registry.yarnpkg.com/plur/-/plur-3.1.1.tgz#60267967866a8d811504fe58f2faaba237546a5b";
+  integrity 
sha512-t1Ax8KUvV3FFII8ltczPn2tJdjqbd1sIzu6t4JL7nQ3EyeL/lTrj5PWKb06ic5/6XYDr65rQ4uzQEGN70/6X5w==
+  dependencies:
+    irregular-plurals "^2.0.0"
+
+posix-character-classes@^0.1.0:
+  version "0.1.1"
+  resolved 
"https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab";
+  integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
+
+prepend-http@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897";
+  integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
+
+pretty-ms@^5.0.0:
+  version "5.0.0"
+  resolved 
"https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-5.0.0.tgz#6133a8f55804b208e4728f6aa7bf01085e951e24";
+  integrity 
sha512-94VRYjL9k33RzfKiGokPBPpsmloBYSf5Ri+Pq19zlsEcUKFob+admeXr5eFDRuPjFmEOcjJvPGdillYOJyvZ7Q==
+  dependencies:
+    parse-ms "^2.1.0"
+
+process-nextick-args@~2.0.0:
+  version "2.0.1"
+  resolved 
"https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2";
+  integrity 
sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
+
+pseudomap@^1.0.2:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3";
+  integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
+
+psl@^1.1.24:
+  version "1.1.31"
+  resolved 
"https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184";
+  integrity 
sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==
+
+pump@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64";
+  integrity 
sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
+  dependencies:
+    end-of-stream "^1.1.0"
+    once "^1.3.1"
+
+punycode@^1.4.1:
+  version "1.4.1"
+  resolved 
"https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e";
+  integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
+
+punycode@^2.1.0:
+  version "2.1.1"
+  resolved 
"https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec";
+  integrity 
sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+
+qs@~6.5.2:
+  version "6.5.2"
+  resolved 
"https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36";
+  integrity 
sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
+
+quick-lru@^1.0.0:
+  version "1.1.0"
+  resolved 
"https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8";
+  integrity sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=
+
+rc@^1.1.6, rc@^1.2.7, rc@^1.2.8:
+  version "1.2.8"
+  resolved 
"https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed";
+  integrity 
sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
+  dependencies:
+    deep-extend "^0.6.0"
+    ini "~1.3.0"
+    minimist "^1.2.0"
+    strip-json-comments "~2.0.1"
+
+read-pkg-up@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07";
+  integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=
+  dependencies:
+    find-up "^2.0.0"
+    read-pkg "^3.0.0"
+
+read-pkg@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389";
+  integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=
+  dependencies:
+    load-json-file "^4.0.0"
+    normalize-package-data "^2.3.2"
+    path-type "^3.0.0"
+
+readable-stream@^2.0.6:
+  version "2.3.6"
+  resolved 
"https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf";
+  integrity 
sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
+  dependencies:
+    core-util-is "~1.0.0"
+    inherits "~2.0.3"
+    isarray "~1.0.0"
+    process-nextick-args "~2.0.0"
+    safe-buffer "~5.1.1"
+    string_decoder "~1.1.1"
+    util-deprecate "~1.0.1"
+
+readdirp@^3.0.2:
+  version "3.0.2"
+  resolved 
"https://registry.yarnpkg.com/readdirp/-/readdirp-3.0.2.tgz#cba63348e9e42fc1bd334b1d2ef895b6a043cbd6";
+  integrity 
sha512-LbyJYv48eywrhOlScq16H/VkCiGKGPC2TpOdZCJ7QXnYEjn3NN/Oblh8QEU3vqfSRBB7OGvh5x45NKiVeNujIQ==
+  dependencies:
+    picomatch "^2.0.4"
+
+redent@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/redent/-/redent-2.0.0.tgz#c1b2007b42d57eb1389079b3c8333639d5e1ccaa";
+  integrity sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=
+  dependencies:
+    indent-string "^3.0.0"
+    strip-indent "^2.0.0"
+
+regenerate-unicode-properties@^8.0.2:
+  version "8.1.0"
+  resolved 
"https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e";
+  integrity 
sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==
+  dependencies:
+    regenerate "^1.4.0"
+
+regenerate@^1.4.0:
+  version "1.4.0"
+  resolved 
"https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11";
+  integrity 
sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==
+
+regex-not@^1.0.0, regex-not@^1.0.2:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c";
+  integrity 
sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==
+  dependencies:
+    extend-shallow "^3.0.2"
+    safe-regex "^1.1.0"
+
+regexpu-core@^4.5.4:
+  version "4.5.4"
+  resolved 
"https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.5.4.tgz#080d9d02289aa87fe1667a4f5136bc98a6aebaae";
+  integrity 
sha512-BtizvGtFQKGPUcTy56o3nk1bGRp4SZOTYrDtGNlqCQufptV5IkkLN6Emw+yunAJjzf+C9FQFtvq7IoA3+oMYHQ==
+  dependencies:
+    regenerate "^1.4.0"
+    regenerate-unicode-properties "^8.0.2"
+    regjsgen "^0.5.0"
+    regjsparser "^0.6.0"
+    unicode-match-property-ecmascript "^1.0.4"
+    unicode-match-property-value-ecmascript "^1.1.0"
+
+registry-auth-token@^3.4.0:
+  version "3.4.0"
+  resolved 
"https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.4.0.tgz#d7446815433f5d5ed6431cd5dca21048f66b397e";
+  integrity 
sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==
+  dependencies:
+    rc "^1.1.6"
+    safe-buffer "^5.0.1"
+
+registry-url@^5.0.0:
+  version "5.1.0"
+  resolved 
"https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009";
+  integrity 
sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==
+  dependencies:
+    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==
+
+regjsparser@^0.6.0:
+  version "0.6.0"
+  resolved 
"https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.0.tgz#f1e6ae8b7da2bae96c99399b868cd6c933a2ba9c";
+  integrity 
sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==
+  dependencies:
+    jsesc "~0.5.0"
+
+release-zalgo@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/release-zalgo/-/release-zalgo-1.0.0.tgz#09700b7e5074329739330e535c5a90fb67851730";
+  integrity sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=
+  dependencies:
+    es6-error "^4.0.1"
+
+repeat-element@^1.1.2:
+  version "1.1.3"
+  resolved 
"https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce";
+  integrity 
sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==
+
+repeat-string@^1.6.1:
+  version "1.6.1"
+  resolved 
"https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637";
+  integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
+
+request@^2.87.0:
+  version "2.88.0"
+  resolved 
"https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef";
+  integrity 
sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
+  dependencies:
+    aws-sign2 "~0.7.0"
+    aws4 "^1.8.0"
+    caseless "~0.12.0"
+    combined-stream "~1.0.6"
+    extend "~3.0.2"
+    forever-agent "~0.6.1"
+    form-data "~2.3.2"
+    har-validator "~5.1.0"
+    http-signature "~1.2.0"
+    is-typedarray "~1.0.0"
+    isstream "~0.1.2"
+    json-stringify-safe "~5.0.1"
+    mime-types "~2.1.19"
+    oauth-sign "~0.9.0"
+    performance-now "^2.1.0"
+    qs "~6.5.2"
+    safe-buffer "^5.1.2"
+    tough-cookie "~2.4.3"
+    tunnel-agent "^0.6.0"
+    uuid "^3.3.2"
+
+require-precompiled@^0.1.0:
+  version "0.1.0"
+  resolved 
"https://registry.yarnpkg.com/require-precompiled/-/require-precompiled-0.1.0.tgz#5a1b52eb70ebed43eb982e974c85ab59571e56fa";
+  integrity sha1-WhtS63Dr7UPrmC6XTIWrWVceVvo=
+
+resolve-cwd@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a";
+  integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=
+  dependencies:
+    resolve-from "^3.0.0"
+
+resolve-cwd@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d";
+  integrity 
sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==
+  dependencies:
+    resolve-from "^5.0.0"
+
+resolve-from@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748";
+  integrity sha1-six699nWiBvItuZTM17rywoYh0g=
+
+resolve-from@^5.0.0:
+  version "5.0.0"
+  resolved 
"https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69";
+  integrity 
sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
+
+resolve-url@^0.2.1:
+  version "0.2.1"
+  resolved 
"https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a";
+  integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
+
+resolve@^1.10.0, resolve@^1.3.2:
+  version "1.11.0"
+  resolved 
"https://registry.yarnpkg.com/resolve/-/resolve-1.11.0.tgz#4014870ba296176b86343d50b60f3b50609ce232";
+  integrity 
sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==
+  dependencies:
+    path-parse "^1.0.6"
+
+responselike@^1.0.2:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7";
+  integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=
+  dependencies:
+    lowercase-keys "^1.0.0"
+
+restore-cursor@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf";
+  integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368=
+  dependencies:
+    onetime "^2.0.0"
+    signal-exit "^3.0.2"
+
+restore-cursor@^3.1.0:
+  version "3.1.0"
+  resolved 
"https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e";
+  integrity 
sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==
+  dependencies:
+    onetime "^5.1.0"
+    signal-exit "^3.0.2"
+
+ret@~0.1.10:
+  version "0.1.15"
+  resolved 
"https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc";
+  integrity 
sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
+
+rimraf@^2.6.1, rimraf@^2.6.3:
+  version "2.6.3"
+  resolved 
"https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab";
+  integrity 
sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
+  dependencies:
+    glob "^7.1.3"
+
+safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+  version "5.1.2"
+  resolved 
"https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d";
+  integrity 
sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+safe-regex@^1.1.0:
+  version "1.1.0"
+  resolved 
"https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e";
+  integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4=
+  dependencies:
+    ret "~0.1.10"
+
+"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, 
safer-buffer@~2.1.0:
+  version "2.1.2"
+  resolved 
"https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a";
+  integrity 
sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+sax@^1.2.4:
+  version "1.2.4"
+  resolved 
"https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9";
+  integrity 
sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
+
+semver-diff@^2.0.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36";
+  integrity sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=
+  dependencies:
+    semver "^5.0.3"
+
+"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.3.0, semver@^5.4.1, 
semver@^5.5.1:
+  version "5.7.0"
+  resolved 
"https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b";
+  integrity 
sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==
+
+semver@^6.0.0, semver@^6.1.1:
+  version "6.1.1"
+  resolved 
"https://registry.yarnpkg.com/semver/-/semver-6.1.1.tgz#53f53da9b30b2103cd4f15eab3a18ecbcb210c9b";
+  integrity 
sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==
+
+serialize-error@^2.1.0:
+  version "2.1.0"
+  resolved 
"https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a";
+  integrity sha1-ULZ51WNc34Rme9yOWa9OW4HV9go=
+
+set-blocking@~2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7";
+  integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
+
+set-value@^0.4.3:
+  version "0.4.3"
+  resolved 
"https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1";
+  integrity sha1-fbCPnT0i3H945Trzw79GZuzfzPE=
+  dependencies:
+    extend-shallow "^2.0.1"
+    is-extendable "^0.1.1"
+    is-plain-object "^2.0.1"
+    to-object-path "^0.3.0"
+
+set-value@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274";
+  integrity 
sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==
+  dependencies:
+    extend-shallow "^2.0.1"
+    is-extendable "^0.1.1"
+    is-plain-object "^2.0.3"
+    split-string "^3.0.1"
+
+shebang-command@^1.2.0:
+  version "1.2.0"
+  resolved 
"https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea";
+  integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
+  dependencies:
+    shebang-regex "^1.0.0"
+
+shebang-regex@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3";
+  integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
+
+signal-exit@^3.0.0, signal-exit@^3.0.2:
+  version "3.0.2"
+  resolved 
"https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d";
+  integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
+
+slash@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44";
+  integrity 
sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==
+
+slash@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634";
+  integrity 
sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
+
+slice-ansi@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d";
+  integrity 
sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==
+  dependencies:
+    is-fullwidth-code-point "^2.0.0"
+
+snapdragon-node@^2.0.1:
+  version "2.1.1"
+  resolved 
"https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b";
+  integrity 
sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==
+  dependencies:
+    define-property "^1.0.0"
+    isobject "^3.0.0"
+    snapdragon-util "^3.0.1"
+
+snapdragon-util@^3.0.1:
+  version "3.0.1"
+  resolved 
"https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2";
+  integrity 
sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==
+  dependencies:
+    kind-of "^3.2.0"
+
+snapdragon@^0.8.1:
+  version "0.8.2"
+  resolved 
"https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d";
+  integrity 
sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==
+  dependencies:
+    base "^0.11.1"
+    debug "^2.2.0"
+    define-property "^0.2.5"
+    extend-shallow "^2.0.1"
+    map-cache "^0.2.2"
+    source-map "^0.5.6"
+    source-map-resolve "^0.5.0"
+    use "^3.1.0"
+
+source-map-resolve@^0.5.0:
+  version "0.5.2"
+  resolved 
"https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259";
+  integrity 
sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==
+  dependencies:
+    atob "^2.1.1"
+    decode-uri-component "^0.2.0"
+    resolve-url "^0.2.1"
+    source-map-url "^0.4.0"
+    urix "^0.1.0"
+
+source-map-support@^0.5.12:
+  version "0.5.12"
+  resolved 
"https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599";
+  integrity 
sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==
+  dependencies:
+    buffer-from "^1.0.0"
+    source-map "^0.6.0"
+
+source-map-url@^0.4.0:
+  version "0.4.0"
+  resolved 
"https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3";
+  integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
+
+source-map@^0.5.0, source-map@^0.5.6:
+  version "0.5.7"
+  resolved 
"https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc";
+  integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
+
+source-map@^0.6.0:
+  version "0.6.1"
+  resolved 
"https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263";
+  integrity 
sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+spdx-correct@^3.0.0:
+  version "3.1.0"
+  resolved 
"https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4";
+  integrity 
sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==
+  dependencies:
+    spdx-expression-parse "^3.0.0"
+    spdx-license-ids "^3.0.0"
+
+spdx-exceptions@^2.1.0:
+  version "2.2.0"
+  resolved 
"https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977";
+  integrity 
sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==
+
+spdx-expression-parse@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0";
+  integrity 
sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==
+  dependencies:
+    spdx-exceptions "^2.1.0"
+    spdx-license-ids "^3.0.0"
+
+spdx-license-ids@^3.0.0:
+  version "3.0.4"
+  resolved 
"https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz#75ecd1a88de8c184ef015eafb51b5b48bfd11bb1";
+  integrity 
sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==
+
+split-string@^3.0.1, split-string@^3.0.2:
+  version "3.1.0"
+  resolved 
"https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2";
+  integrity 
sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==
+  dependencies:
+    extend-shallow "^3.0.0"
+
+sprintf-js@~1.0.2:
+  version "1.0.3"
+  resolved 
"https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c";
+  integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
+
+sqlite3@^4.0.8:
+  version "4.0.8"
+  resolved 
"https://registry.yarnpkg.com/sqlite3/-/sqlite3-4.0.8.tgz#81ee60d54befaa52f5421fe6337050bd43d4bb95";
+  integrity 
sha512-kgwHu4j10KhpCHtx//dejd/tVQot7jc3sw+Sn0vMuKOw0X00Ckyg9VceKgzPyGmmz+zEoYue9tOLriWTvYy0ww==
+  dependencies:
+    nan "^2.12.1"
+    node-pre-gyp "^0.11.0"
+    request "^2.87.0"
+
+sshpk@^1.7.0:
+  version "1.16.1"
+  resolved 
"https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877";
+  integrity 
sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
+  dependencies:
+    asn1 "~0.2.3"
+    assert-plus "^1.0.0"
+    bcrypt-pbkdf "^1.0.0"
+    dashdash "^1.12.0"
+    ecc-jsbn "~0.1.1"
+    getpass "^0.1.1"
+    jsbn "~0.1.0"
+    safer-buffer "^2.0.2"
+    tweetnacl "~0.14.0"
+
+stack-utils@^1.0.2:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8";
+  integrity 
sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==
+
+static-extend@^0.1.1:
+  version "0.1.2"
+  resolved 
"https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6";
+  integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=
+  dependencies:
+    define-property "^0.2.5"
+    object-copy "^0.1.0"
+
+string-width@^1.0.1:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3";
+  integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
+  dependencies:
+    code-point-at "^1.0.0"
+    is-fullwidth-code-point "^1.0.0"
+    strip-ansi "^3.0.0"
+
+"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1:
+  version "2.1.1"
+  resolved 
"https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e";
+  integrity 
sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
+  dependencies:
+    is-fullwidth-code-point "^2.0.0"
+    strip-ansi "^4.0.0"
+
+string-width@^3.0.0:
+  version "3.1.0"
+  resolved 
"https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961";
+  integrity 
sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==
+  dependencies:
+    emoji-regex "^7.0.1"
+    is-fullwidth-code-point "^2.0.0"
+    strip-ansi "^5.1.0"
+
+string_decoder@~1.1.1:
+  version "1.1.1"
+  resolved 
"https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8";
+  integrity 
sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
+  dependencies:
+    safe-buffer "~5.1.0"
+
+strip-ansi@^3.0.0, strip-ansi@^3.0.1:
+  version "3.0.1"
+  resolved 
"https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf";
+  integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
+  dependencies:
+    ansi-regex "^2.0.0"
+
+strip-ansi@^4.0.0:
+  version "4.0.0"
+  resolved 
"https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f";
+  integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
+  dependencies:
+    ansi-regex "^3.0.0"
+
+strip-ansi@^5.1.0, strip-ansi@^5.2.0:
+  version "5.2.0"
+  resolved 
"https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae";
+  integrity 
sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
+  dependencies:
+    ansi-regex "^4.1.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";
+  integrity 
sha512-gLFNHucd6gzb8jMsl5QmZ3QgnUJmp7qn4uUSHNwEXumAp7YizoGYw19ZUVfuq4aBOQUtyn2k8X/CwzWB73W2lQ==
+  dependencies:
+    is-utf8 "^0.2.1"
+
+strip-bom@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3";
+  integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=
+
+strip-eof@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf";
+  integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
+
+strip-indent@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68";
+  integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=
+
+strip-json-comments@~2.0.1:
+  version "2.0.1"
+  resolved 
"https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a";
+  integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
+
+supertap@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/supertap/-/supertap-1.0.0.tgz#bd9751c7fafd68c68cf8222a29892206a119fa9e";
+  integrity 
sha512-HZJ3geIMPgVwKk2VsmO5YHqnnJYl6bV5A9JW2uzqV43WmpgliNEYbuvukfor7URpaqpxuw3CfZ3ONdVbZjCgIA==
+  dependencies:
+    arrify "^1.0.1"
+    indent-string "^3.2.0"
+    js-yaml "^3.10.0"
+    serialize-error "^2.1.0"
+    strip-ansi "^4.0.0"
+
+supports-color@^5.3.0:
+  version "5.5.0"
+  resolved 
"https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f";
+  integrity 
sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+  dependencies:
+    has-flag "^3.0.0"
+
+supports-color@^6.1.0:
+  version "6.1.0"
+  resolved 
"https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3";
+  integrity 
sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==
+  dependencies:
+    has-flag "^3.0.0"
+
+symbol-observable@^1.0.4:
+  version "1.2.0"
+  resolved 
"https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804";
+  integrity 
sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
+
+tar@^4:
+  version "4.4.10"
+  resolved 
"https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1";
+  integrity 
sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==
+  dependencies:
+    chownr "^1.1.1"
+    fs-minipass "^1.2.5"
+    minipass "^2.3.5"
+    minizlib "^1.2.1"
+    mkdirp "^0.5.0"
+    safe-buffer "^5.1.2"
+    yallist "^3.0.3"
+
+term-size@^1.2.0:
+  version "1.2.0"
+  resolved 
"https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69";
+  integrity sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=
+  dependencies:
+    execa "^0.7.0"
+
+time-zone@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/time-zone/-/time-zone-1.0.0.tgz#99c5bf55958966af6d06d83bdf3800dc82faec5d";
+  integrity sha1-mcW/VZWJZq9tBtg73zgA3IL67F0=
+
+to-fast-properties@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e";
+  integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
+
+to-object-path@^0.3.0:
+  version "0.3.0"
+  resolved 
"https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af";
+  integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=
+  dependencies:
+    kind-of "^3.0.2"
+
+to-readable-stream@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771";
+  integrity 
sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==
+
+to-regex-range@^2.1.0:
+  version "2.1.1"
+  resolved 
"https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38";
+  integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=
+  dependencies:
+    is-number "^3.0.0"
+    repeat-string "^1.6.1"
+
+to-regex-range@^5.0.1:
+  version "5.0.1"
+  resolved 
"https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4";
+  integrity 
sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+  dependencies:
+    is-number "^7.0.0"
+
+to-regex@^3.0.1, to-regex@^3.0.2:
+  version "3.0.2"
+  resolved 
"https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce";
+  integrity 
sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==
+  dependencies:
+    define-property "^2.0.2"
+    extend-shallow "^3.0.2"
+    regex-not "^1.0.2"
+    safe-regex "^1.1.0"
+
+tough-cookie@~2.4.3:
+  version "2.4.3"
+  resolved 
"https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781";
+  integrity 
sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==
+  dependencies:
+    psl "^1.1.24"
+    punycode "^1.4.1"
+
+trim-newlines@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20";
+  integrity sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=
+
+trim-off-newlines@^1.0.1:
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3";
+  integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM=
+
+trim-right@^1.0.1:
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003";
+  integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=
+
+tunnel-agent@^0.6.0:
+  version "0.6.0"
+  resolved 
"https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd";
+  integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
+  dependencies:
+    safe-buffer "^5.0.1"
+
+tweetnacl@^0.14.3, tweetnacl@~0.14.0:
+  version "0.14.5"
+  resolved 
"https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64";
+  integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
+
+type-fest@^0.3.0:
+  version "0.3.1"
+  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==
+
+typedarray-to-buffer@^3.1.5:
+  version "3.1.5"
+  resolved 
"https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080";
+  integrity 
sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==
+  dependencies:
+    is-typedarray "^1.0.0"
+
+typescript@^3.4.5:
+  version "3.4.5"
+  resolved 
"https://registry.yarnpkg.com/typescript/-/typescript-3.4.5.tgz#2d2618d10bb566572b8d7aad5180d84257d70a99";
+  integrity 
sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==
+
+uid2@0.0.3:
+  version "0.0.3"
+  resolved 
"https://registry.yarnpkg.com/uid2/-/uid2-0.0.3.tgz#483126e11774df2f71b8b639dcd799c376162b82";
+  integrity sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I=
+
+unicode-canonical-property-names-ecmascript@^1.0.4:
+  version "1.0.4"
+  resolved 
"https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818";
+  integrity 
sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==
+
+unicode-match-property-ecmascript@^1.0.4:
+  version "1.0.4"
+  resolved 
"https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c";
+  integrity 
sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==
+  dependencies:
+    unicode-canonical-property-names-ecmascript "^1.0.4"
+    unicode-property-aliases-ecmascript "^1.0.4"
+
+unicode-match-property-value-ecmascript@^1.1.0:
+  version "1.1.0"
+  resolved 
"https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277";
+  integrity 
sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==
+
+unicode-property-aliases-ecmascript@^1.0.4:
+  version "1.0.5"
+  resolved 
"https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57";
+  integrity 
sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==
+
+union-value@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4";
+  integrity sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=
+  dependencies:
+    arr-union "^3.1.0"
+    get-value "^2.0.6"
+    is-extendable "^0.1.1"
+    set-value "^0.4.3"
+
+unique-string@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a";
+  integrity sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=
+  dependencies:
+    crypto-random-string "^1.0.0"
+
+unique-temp-dir@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/unique-temp-dir/-/unique-temp-dir-1.0.0.tgz#6dce95b2681ca003eebfb304a415f9cbabcc5385";
+  integrity sha1-bc6VsmgcoAPuv7MEpBX5y6vMU4U=
+  dependencies:
+    mkdirp "^0.5.1"
+    os-tmpdir "^1.0.1"
+    uid2 "0.0.3"
+
+unset-value@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559";
+  integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=
+  dependencies:
+    has-value "^0.3.1"
+    isobject "^3.0.0"
+
+update-notifier@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/update-notifier/-/update-notifier-3.0.0.tgz#e9bbf8f0f5b7a2ce6666ca46334fdb29492e8fab";
+  integrity 
sha512-6Xe3oF2bvuoj4YECUc52yxVs94yWrxwqHbzyveDktTS1WhnlTRpNcQMxUshcB7nRVGi1jEXiqL5cW1S5WSyzKg==
+  dependencies:
+    boxen "^3.0.0"
+    chalk "^2.0.1"
+    configstore "^4.0.0"
+    has-yarn "^2.1.0"
+    import-lazy "^2.1.0"
+    is-ci "^2.0.0"
+    is-installed-globally "^0.1.0"
+    is-npm "^3.0.0"
+    is-yarn-global "^0.3.0"
+    latest-version "^5.0.0"
+    semver-diff "^2.0.0"
+    xdg-basedir "^3.0.0"
+
+uri-js@^4.2.2:
+  version "4.2.2"
+  resolved 
"https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0";
+  integrity 
sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
+  dependencies:
+    punycode "^2.1.0"
+
+urix@^0.1.0:
+  version "0.1.0"
+  resolved 
"https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72";
+  integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
+
+url-parse-lax@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c";
+  integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=
+  dependencies:
+    prepend-http "^2.0.0"
+
+use@^3.1.0:
+  version "3.1.1"
+  resolved 
"https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f";
+  integrity 
sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
+
+util-deprecate@~1.0.1:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf";
+  integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
+
+uuid@^3.3.2:
+  version "3.3.2"
+  resolved 
"https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131";
+  integrity 
sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
+
+validate-npm-package-license@^3.0.1:
+  version "3.0.4"
+  resolved 
"https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a";
+  integrity 
sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
+  dependencies:
+    spdx-correct "^3.0.0"
+    spdx-expression-parse "^3.0.0"
+
+verror@1.10.0:
+  version "1.10.0"
+  resolved 
"https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400";
+  integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
+  dependencies:
+    assert-plus "^1.0.0"
+    core-util-is "1.0.2"
+    extsprintf "^1.2.0"
+
+wcwidth@^1.0.1:
+  version "1.0.1"
+  resolved 
"https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8";
+  integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=
+  dependencies:
+    defaults "^1.0.3"
+
+well-known-symbols@^2.0.0:
+  version "2.0.0"
+  resolved 
"https://registry.yarnpkg.com/well-known-symbols/-/well-known-symbols-2.0.0.tgz#e9c7c07dbd132b7b84212c8174391ec1f9871ba5";
+  integrity 
sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==
+
+which@^1.2.9:
+  version "1.3.1"
+  resolved 
"https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a";
+  integrity 
sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
+  dependencies:
+    isexe "^2.0.0"
+
+wide-align@^1.1.0:
+  version "1.1.3"
+  resolved 
"https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457";
+  integrity 
sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==
+  dependencies:
+    string-width "^1.0.2 || 2"
+
+widest-line@^2.0.0:
+  version "2.0.1"
+  resolved 
"https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc";
+  integrity 
sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==
+  dependencies:
+    string-width "^2.1.1"
+
+wrappy@1:
+  version "1.0.2"
+  resolved 
"https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f";
+  integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
+
+write-file-atomic@^2.0.0:
+  version "2.4.3"
+  resolved 
"https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481";
+  integrity 
sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==
+  dependencies:
+    graceful-fs "^4.1.11"
+    imurmurhash "^0.1.4"
+    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==
+  dependencies:
+    imurmurhash "^0.1.4"
+    is-typedarray "^1.0.0"
+    signal-exit "^3.0.2"
+    typedarray-to-buffer "^3.1.5"
+
+xdg-basedir@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4";
+  integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=
+
+xtend@^4.0.0:
+  version "4.0.1"
+  resolved 
"https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af";
+  integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68=
+
+yallist@^2.1.2:
+  version "2.1.2"
+  resolved 
"https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52";
+  integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
+
+yallist@^3.0.0, 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==
+
+yargs-parser@^10.0.0:
+  version "10.1.0"
+  resolved 
"https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8";
+  integrity 
sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==
+  dependencies:
+    camelcase "^4.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]