define(["exports", "core/pubsubhub", "deps/regenerator"], function (exports, _pubsubhub) {
  "use strict";

  Object.defineProperty(exports, "__esModule", {
    value: true
  });
  exports.biblioDB = exports.name = undefined;

  var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
    return typeof obj;
  } : function (obj) {
    return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
  };

  function _asyncToGenerator(fn) {
    return function () {
      var gen = fn.apply(this, arguments);
      return new Promise(function (resolve, reject) {
        function step(key, arg) {
          try {
            var info = gen[key](arg);
            var value = info.value;
          } catch (error) {
            reject(error);
            return;
          }

          if (info.done) {
            resolve(value);
          } else {
            return Promise.resolve(value).then(function (value) {
              step("next", value);
            }, function (err) {
              step("throw", err);
            });
          }
        }

        return step("next");
      });
    };
  }

  var name = exports.name = "core/biblio-db";

  var ALLOWED_TYPES = new Set(["alias", "reference"]);
  // Database initialization, tracked by "readyPromise"
  var readyPromise = new Promise(function (resolve, reject) {
    var request = void 0;
    try {
      request = window.indexedDB.open("respec-biblio2", 12);
    } catch (err) {
      return reject(err);
    }
    request.onerror = function () {
      reject(new DOMException(request.error.message, request.error.name));
    };
    request.onsuccess = function () {
      resolve(request.result);
    };
    request.onupgradeneeded = _asyncToGenerator(regeneratorRuntime.mark(function _callee() {
      var db, promisesToCreateSchema;
      return regeneratorRuntime.wrap(function _callee$(_context) {
        while (1) {
          switch (_context.prev = _context.next) {
            case 0:
              db = request.result;

              Array.from(db.objectStoreNames).map(function (storeName) {
                return db.deleteObjectStore(storeName);
              });
              promisesToCreateSchema = [new Promise(function (resolve, reject) {
                try {
                  var store = db.createObjectStore("alias", { keyPath: "id" });
                  store.createIndex("aliasOf", "aliasOf", { unique: false });
                  store.transaction.oncomplete = resolve;
                  store.transaction.onerror = reject;
                } catch (err) {
                  reject(err);
                }
              }), new Promise(function (resolve, reject) {
                try {
                  var transaction = db.createObjectStore("reference", {
                    keyPath: "id"
                  }).transaction;
                  transaction.oncomplete = resolve;
                  transaction.onerror = reject;
                } catch (err) {
                  reject(err);
                }
              })];
              _context.prev = 3;
              _context.next = 6;
              return Promise.all(promisesToCreateSchema);

            case 6:
              resolve();
              _context.next = 12;
              break;

            case 9:
              _context.prev = 9;
              _context.t0 = _context["catch"](3);

              reject(_context.t0);

            case 12:
            case "end":
              return _context.stop();
          }
        }
      }, _callee, undefined, [[3, 9]]);
    }));
  });

  var biblioDB = exports.biblioDB = {
    get ready() {
      return readyPromise;
    },
    /**
     * Finds either a reference or an alias.
     * If it's an alias, it resolves it.
     *
     * @param {String} id The reference or alias to look for.
     * @return {Object?} The reference or null.
     */
    find: function () {
      var _ref2 = _asyncToGenerator(regeneratorRuntime.mark(function _callee2(id) {
        return regeneratorRuntime.wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                _context2.next = 2;
                return this.isAlias(id);

              case 2:
                if (!_context2.sent) {
                  _context2.next = 6;
                  break;
                }

                _context2.next = 5;
                return this.resolveAlias(id);

              case 5:
                id = _context2.sent;

              case 6:
                return _context2.abrupt("return", this.get("reference", id));

              case 7:
              case "end":
                return _context2.stop();
            }
          }
        }, _callee2, this);
      }));

      function find(_x) {
        return _ref2.apply(this, arguments);
      }

      return find;
    }(),

    /**
     * Checks if the database has an id for a given type.
     *
     * @param {String} type One of the ALLOWED_TYPES.
     * @param {String} id The reference to find.
     * @return {Boolean} True if it has it, false otherwise.
     */
    has: function () {
      var _ref3 = _asyncToGenerator(regeneratorRuntime.mark(function _callee3(type, id) {
        var db;
        return regeneratorRuntime.wrap(function _callee3$(_context3) {
          while (1) {
            switch (_context3.prev = _context3.next) {
              case 0:
                if (ALLOWED_TYPES.has(type)) {
                  _context3.next = 2;
                  break;
                }

                throw new TypeError("Invalid type: " + type);

              case 2:
                if (id) {
                  _context3.next = 4;
                  break;
                }

                throw new TypeError("id is required");

              case 4:
                _context3.next = 6;
                return this.ready;

              case 6:
                db = _context3.sent;
                return _context3.abrupt("return", new Promise(function (resolve, reject) {
                  var objectStore = db.transaction([type], "readonly").objectStore(type);
                  var range = IDBKeyRange.only(id);
                  var request = objectStore.openCursor(range);
                  request.onsuccess = function () {
                    resolve(!!request.result);
                  };
                  request.onerror = function () {
                    reject(new DOMException(request.error.message, request.error.name));
                  };
                }));

              case 8:
              case "end":
                return _context3.stop();
            }
          }
        }, _callee3, this);
      }));

      function has(_x2, _x3) {
        return _ref3.apply(this, arguments);
      }

      return has;
    }(),

    /**
     * Checks if a given id is an alias.
     *
     * @param {String} id The reference to check.
     * @return {Promise<Boolean>} Resolves with true if found.
     */
    isAlias: function () {
      var _ref4 = _asyncToGenerator(regeneratorRuntime.mark(function _callee4(id) {
        var db;
        return regeneratorRuntime.wrap(function _callee4$(_context4) {
          while (1) {
            switch (_context4.prev = _context4.next) {
              case 0:
                if (id) {
                  _context4.next = 2;
                  break;
                }

                throw new TypeError("id is required");

              case 2:
                _context4.next = 4;
                return this.ready;

              case 4:
                db = _context4.sent;
                return _context4.abrupt("return", new Promise(function (resolve, reject) {
                  var objectStore = db.transaction(["alias"], "readonly").objectStore("alias");
                  var range = IDBKeyRange.only(id);
                  var request = objectStore.openCursor(range);
                  request.onsuccess = function () {
                    resolve(!!request.result);
                  };
                  request.onerror = function () {
                    reject(new DOMException(request.error.message, request.error.name));
                  };
                }));

              case 6:
              case "end":
                return _context4.stop();
            }
          }
        }, _callee4, this);
      }));

      function isAlias(_x4) {
        return _ref4.apply(this, arguments);
      }

      return isAlias;
    }(),

    /**
     * Resolves an alias to its corresponding reference id.
     *
     * @param {String} id The id of the alias to look up.
     * @return {Promise<String>} The id of the resolved reference.
     */
    resolveAlias: function () {
      var _ref5 = _asyncToGenerator(regeneratorRuntime.mark(function _callee5(id) {
        var db;
        return regeneratorRuntime.wrap(function _callee5$(_context5) {
          while (1) {
            switch (_context5.prev = _context5.next) {
              case 0:
                if (id) {
                  _context5.next = 2;
                  break;
                }

                throw new TypeError("id is required");

              case 2:
                _context5.next = 4;
                return this.ready;

              case 4:
                db = _context5.sent;
                return _context5.abrupt("return", new Promise(function (resolve, reject) {
                  var objectStore = db.transaction("alias", "readonly").objectStore("alias");
                  var range = IDBKeyRange.only(id);
                  var request = objectStore.openCursor(range);
                  request.onsuccess = function () {
                    if (request.result === null) {
                      return resolve(null);
                    }
                    resolve(request.result.value.aliasOf);
                  };
                  request.onerror = function () {
                    reject(new DOMException(request.error.message, request.error.name));
                  };
                }));

              case 6:
              case "end":
                return _context5.stop();
            }
          }
        }, _callee5, this);
      }));

      function resolveAlias(_x5) {
        return _ref5.apply(this, arguments);
      }

      return resolveAlias;
    }(),

    /**
     * Get a reference or alias out of the database.
     *
     * @param {String} type The type as per ALLOWED_TYPES.
     * @param {[type]} id The id for what to look up.
     * @return {Promise<Object?>} Resolves with the retrieved object, or null.
     */
    get: function () {
      var _ref6 = _asyncToGenerator(regeneratorRuntime.mark(function _callee6(type, id) {
        var db;
        return regeneratorRuntime.wrap(function _callee6$(_context6) {
          while (1) {
            switch (_context6.prev = _context6.next) {
              case 0:
                if (ALLOWED_TYPES.has(type)) {
                  _context6.next = 2;
                  break;
                }

                throw new TypeError("Invalid type: " + type);

              case 2:
                if (id) {
                  _context6.next = 4;
                  break;
                }

                throw new TypeError("id is required");

              case 4:
                _context6.next = 6;
                return this.ready;

              case 6:
                db = _context6.sent;
                return _context6.abrupt("return", new Promise(function (resolve, reject) {
                  var objectStore = db.transaction([type], "readonly").objectStore(type);
                  var range = IDBKeyRange.only(id);
                  var request = objectStore.openCursor(range);
                  request.onsuccess = function () {
                    if (request.result === null) {
                      return resolve(null);
                    }
                    resolve(request.result.value);
                  };
                  request.onerror = function () {
                    reject(new DOMException(request.error.message, request.error.name));
                  };
                }));

              case 8:
              case "end":
                return _context6.stop();
            }
          }
        }, _callee6, this);
      }));

      function get(_x6, _x7) {
        return _ref6.apply(this, arguments);
      }

      return get;
    }(),

    /**
     * Adds references and aliases to database. This is usually the data from
     * Specref's output (parsed JSON).
     *
     * @param {Object} data An object that contains references and aliases.
     */
    addAll: function () {
      var _ref7 = _asyncToGenerator(regeneratorRuntime.mark(function _callee7(data) {
        var _this = this;

        var aliasesAndRefs, promisesToAdd;
        return regeneratorRuntime.wrap(function _callee7$(_context7) {
          while (1) {
            switch (_context7.prev = _context7.next) {
              case 0:
                if (data) {
                  _context7.next = 2;
                  break;
                }

                return _context7.abrupt("return");

              case 2:
                aliasesAndRefs = {
                  alias: new Set(),
                  reference: new Set()
                };

                Object.keys(data).filter(function (key) {
                  if (typeof data[key] === "string") {
                    var msg = "Legacy SpecRef entries are not supported: `[[" + key + "]]`. ";
                    msg += "Please update it to the new format at [specref repo](https://github.com/tobie/specref/)";
                    (0, _pubsubhub.pub)("error", msg);
                    return false;
                  }
                  return true;
                }).map(function (id) {
                  return Object.assign({ id: id }, data[id]);
                }).reduce(function (collector, obj) {
                  if (obj.aliasOf) {
                    collector.alias.add(obj);
                  } else {
                    collector.reference.add(obj);
                  }
                  return collector;
                }, aliasesAndRefs);
                promisesToAdd = Object.keys(aliasesAndRefs).map(function (type) {
                  return Array.from(aliasesAndRefs[type]).map(function (details) {
                    return _this.add(type, details);
                  });
                }).reduce(function (collector, promises) {
                  return collector.concat(promises);
                }, []);
                _context7.next = 7;
                return Promise.all(promisesToAdd);

              case 7:
              case "end":
                return _context7.stop();
            }
          }
        }, _callee7, this);
      }));

      function addAll(_x8) {
        return _ref7.apply(this, arguments);
      }

      return addAll;
    }(),

    /**
     * Adds a reference or alias to the database.
     *
     * @param {String} type The type as per ALLOWED_TYPES.
     * @param {String} details The object to store.
     */
    add: function () {
      var _ref8 = _asyncToGenerator(regeneratorRuntime.mark(function _callee8(type, details) {
        var db, isInDB;
        return regeneratorRuntime.wrap(function _callee8$(_context8) {
          while (1) {
            switch (_context8.prev = _context8.next) {
              case 0:
                if (ALLOWED_TYPES.has(type)) {
                  _context8.next = 2;
                  break;
                }

                throw new TypeError("Invalid type: " + type);

              case 2:
                if (!((typeof details === "undefined" ? "undefined" : _typeof(details)) !== "object")) {
                  _context8.next = 4;
                  break;
                }

                throw new TypeError("details should be an object");

              case 4:
                if (!(type === "alias" && !details.hasOwnProperty("aliasOf"))) {
                  _context8.next = 6;
                  break;
                }

                throw new TypeError("Invalid alias object.");

              case 6:
                _context8.next = 8;
                return this.ready;

              case 8:
                db = _context8.sent;
                _context8.next = 11;
                return this.has(type, details.id);

              case 11:
                isInDB = _context8.sent;
                return _context8.abrupt("return", new Promise(function (resolve, reject) {
                  var store = db.transaction([type], "readwrite").objectStore(type);
                  // update or add, depending of already having it in db
                  var request = isInDB ? store.put(details) : store.add(details);
                  request.onsuccess = resolve;
                  request.onerror = function () {
                    reject(new DOMException(request.error.message, request.error.name));
                  };
                }));

              case 13:
              case "end":
                return _context8.stop();
            }
          }
        }, _callee8, this);
      }));

      function add(_x9, _x10) {
        return _ref8.apply(this, arguments);
      }

      return add;
    }(),

    /**
     * Closes the underlying database.
     *
     * @return {Promise} Resolves after database closes.
     */
    close: function () {
      var _ref9 = _asyncToGenerator(regeneratorRuntime.mark(function _callee9() {
        var db;
        return regeneratorRuntime.wrap(function _callee9$(_context9) {
          while (1) {
            switch (_context9.prev = _context9.next) {
              case 0:
                _context9.next = 2;
                return this.ready;

              case 2:
                db = _context9.sent;

                db.close();

              case 4:
              case "end":
                return _context9.stop();
            }
          }
        }, _callee9, this);
      }));

      function close() {
        return _ref9.apply(this, arguments);
      }

      return close;
    }()
  };
});
//# sourceMappingURL=biblio-db.js.map