|
@@ -0,0 +1,142 @@
|
|
|
|
|
+'use strict';
|
|
|
|
|
+
|
|
|
|
|
+Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
|
|
+
|
|
|
|
|
+class Store {
|
|
|
|
|
+ constructor(dbName = 'keyval-store', storeName = 'keyval', version = 1) {
|
|
|
|
|
+ this.storeName = storeName;
|
|
|
|
|
+ this._dbName = dbName;
|
|
|
|
|
+ this._storeName = storeName;
|
|
|
|
|
+ this._version = version;
|
|
|
|
|
+ this.id = `dbName:${dbName};;storeName:${storeName}`;
|
|
|
|
|
+ this._init();
|
|
|
|
|
+ }
|
|
|
|
|
+ _init() {
|
|
|
|
|
+ if (this._dbp) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ this._dbp = new Promise((resolve, reject) => {
|
|
|
|
|
+ const openreq = indexedDB.open(this._dbName, this._version);
|
|
|
|
|
+ openreq.onerror = () => reject(openreq.error);
|
|
|
|
|
+ openreq.onsuccess = () => resolve(openreq.result);
|
|
|
|
|
+ // First time setup: create an empty object store
|
|
|
|
|
+ openreq.onupgradeneeded = () => {
|
|
|
|
|
+ openreq.result.createObjectStore(this._storeName);
|
|
|
|
|
+ };
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ _withIDBStore(type, callback) {
|
|
|
|
|
+ this._init();
|
|
|
|
|
+ return this._dbp.then(db => new Promise((resolve, reject) => {
|
|
|
|
|
+ const transaction = db.transaction(this.storeName, type);
|
|
|
|
|
+ transaction.oncomplete = () => resolve();
|
|
|
|
|
+ transaction.onabort = transaction.onerror = () => reject(transaction.error);
|
|
|
|
|
+ callback(transaction.objectStore(this.storeName));
|
|
|
|
|
+ }));
|
|
|
|
|
+ }
|
|
|
|
|
+ _close() {
|
|
|
|
|
+ this._init();
|
|
|
|
|
+ return this._dbp.then(db => {
|
|
|
|
|
+ db.close();
|
|
|
|
|
+ this._dbp = undefined;
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+class Batcher {
|
|
|
|
|
+ constructor(executor) {
|
|
|
|
|
+ this.executor = executor;
|
|
|
|
|
+ this.items = [];
|
|
|
|
|
+ }
|
|
|
|
|
+ async process() {
|
|
|
|
|
+ const toProcess = this.items;
|
|
|
|
|
+ this.items = [];
|
|
|
|
|
+ await this.executor(toProcess.map(({ item }) => item));
|
|
|
|
|
+ toProcess.map(({ onProcessed }) => onProcessed());
|
|
|
|
|
+ if (this.items.length) {
|
|
|
|
|
+ this.ongoing = this.process();
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ this.ongoing = undefined;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ async queue(item) {
|
|
|
|
|
+ const result = new Promise((resolve) => this.items.push({ item, onProcessed: resolve }));
|
|
|
|
|
+ if (!this.ongoing)
|
|
|
|
|
+ this.ongoing = this.process();
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+let store;
|
|
|
|
|
+function getDefaultStore() {
|
|
|
|
|
+ if (!store)
|
|
|
|
|
+ store = new Store();
|
|
|
|
|
+ return store;
|
|
|
|
|
+}
|
|
|
|
|
+function get(key, store = getDefaultStore()) {
|
|
|
|
|
+ let req;
|
|
|
|
|
+ return store._withIDBStore('readwrite', store => {
|
|
|
|
|
+ req = store.get(key);
|
|
|
|
|
+ }).then(() => req.result);
|
|
|
|
|
+}
|
|
|
|
|
+const setBatchers = {};
|
|
|
|
|
+function set(key, value, store = getDefaultStore()) {
|
|
|
|
|
+ if (!setBatchers[store.id]) {
|
|
|
|
|
+ setBatchers[store.id] = new Batcher((items) => store._withIDBStore('readwrite', store => {
|
|
|
|
|
+ for (const item of items) {
|
|
|
|
|
+ store.put(item.value, item.key);
|
|
|
|
|
+ }
|
|
|
|
|
+ }));
|
|
|
|
|
+ }
|
|
|
|
|
+ return setBatchers[store.id].queue({ key, value });
|
|
|
|
|
+}
|
|
|
|
|
+function setBatch(items, store = getDefaultStore()) {
|
|
|
|
|
+ return store._withIDBStore('readwrite', store => {
|
|
|
|
|
+ for (const item of items) {
|
|
|
|
|
+ store.put(item.value, item.key);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+function update(key, updater, store = getDefaultStore()) {
|
|
|
|
|
+ return store._withIDBStore('readwrite', store => {
|
|
|
|
|
+ const req = store.get(key);
|
|
|
|
|
+ req.onsuccess = () => {
|
|
|
|
|
+ store.put(updater(req.result), key);
|
|
|
|
|
+ };
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+function del(key, store = getDefaultStore()) {
|
|
|
|
|
+ return store._withIDBStore('readwrite', store => {
|
|
|
|
|
+ store.delete(key);
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+function clear(store = getDefaultStore()) {
|
|
|
|
|
+ return store._withIDBStore('readwrite', store => {
|
|
|
|
|
+ store.clear();
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+function keys(store = getDefaultStore()) {
|
|
|
|
|
+ const keys = [];
|
|
|
|
|
+ return store._withIDBStore('readwrite', store => {
|
|
|
|
|
+ // This would be store.getAllKeys(), but it isn't supported by Edge or Safari.
|
|
|
|
|
+ // And openKeyCursor isn't supported by Safari.
|
|
|
|
|
+ (store.openKeyCursor || store.openCursor).call(store).onsuccess = function () {
|
|
|
|
|
+ if (!this.result)
|
|
|
|
|
+ return;
|
|
|
|
|
+ keys.push(this.result.key);
|
|
|
|
|
+ this.result.continue();
|
|
|
|
|
+ };
|
|
|
|
|
+ }).then(() => keys);
|
|
|
|
|
+}
|
|
|
|
|
+function close(store = getDefaultStore()) {
|
|
|
|
|
+ return store._close();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+exports.Store = Store;
|
|
|
|
|
+exports.get = get;
|
|
|
|
|
+exports.set = set;
|
|
|
|
|
+exports.setBatch = setBatch;
|
|
|
|
|
+exports.update = update;
|
|
|
|
|
+exports.del = del;
|
|
|
|
|
+exports.clear = clear;
|
|
|
|
|
+exports.keys = keys;
|
|
|
|
|
+exports.close = close;
|