UNPKG

idb-managed

Version:

Easy APIs for IndexedDB, with DB manager to manage local DBs. Based on idb.

340 lines (294 loc) 9.64 kB
/** * @file Codes in this file are based on https://raw.githubusercontent.com/jakearchibald/idb/v2.1.3/lib/idb.js, modified to check whether some db properties exist before proxy methods execute. */ function toArray(arr) { return Array.prototype.slice.call(arr); } function promisifyRequest(request) { return new Promise(function(resolve, reject) { request.onsuccess = function() { resolve(request.result); }; request.onerror = function() { reject(request.error); }; }); } function promisifyRequestCall(obj, method, args) { var request; var p = new Promise(function(resolve, reject) { request = obj[method].apply(obj, args); promisifyRequest(request).then(resolve, reject); }); p.request = request; return p; } function promisifyCursorRequestCall(obj, method, args) { var p = promisifyRequestCall(obj, method, args); return p.then(function(value) { if (!value) return; return new Cursor(value, p.request); }); } function proxyProperties(ProxyClass, targetProp, properties) { properties.forEach(function(prop) { Object.defineProperty(ProxyClass.prototype, prop, { get: function() { return this[targetProp][prop]; }, set: function(val) { this[targetProp][prop] = val; } }); }); } function proxyRequestMethods(ProxyClass, targetProp, Constructor, properties) { properties.forEach(function(prop) { if (!(prop in Constructor.prototype)) return; ProxyClass.prototype[prop] = function() { return promisifyRequestCall(this[targetProp], prop, arguments); }; }); } function proxyMethods(ProxyClass, targetProp, Constructor, properties) { properties.forEach(function(prop) { if (!(prop in Constructor.prototype)) return; ProxyClass.prototype[prop] = function() { return this[targetProp][prop].apply(this[targetProp], arguments); }; }); } function proxyCursorRequestMethods( ProxyClass, targetProp, Constructor, properties ) { properties.forEach(function(prop) { if (!(prop in Constructor.prototype)) return; ProxyClass.prototype[prop] = function() { return promisifyCursorRequestCall( this[targetProp], prop, arguments ); }; }); } function Index(index) { this._index = index; } function Cursor(cursor, request) { this._cursor = cursor; this._request = request; } function ObjectStore(store) { this._store = store; } ObjectStore.prototype.createIndex = function() { return new Index(this._store.createIndex.apply(this._store, arguments)); }; ObjectStore.prototype.index = function() { return new Index(this._store.index.apply(this._store, arguments)); }; function Transaction(idbTransaction) { this._tx = idbTransaction; this.complete = new Promise(function(resolve, reject) { idbTransaction.oncomplete = function() { resolve(); }; idbTransaction.onerror = function() { reject(idbTransaction.error); }; idbTransaction.onabort = function() { reject(idbTransaction.error); }; }); } Transaction.prototype.objectStore = function() { return new ObjectStore(this._tx.objectStore.apply(this._tx, arguments)); }; function UpgradeDB(db, oldVersion, transaction) { this._db = db; this.oldVersion = oldVersion; this.transaction = new Transaction(transaction); } UpgradeDB.prototype.createObjectStore = function() { return new ObjectStore( this._db.createObjectStore.apply(this._db, arguments) ); }; function DB(db) { this._db = db; } DB.prototype.transaction = function() { return new Transaction(this._db.transaction.apply(this._db, arguments)); }; var exp; function idbIsSupported() { try { [ 'IDBIndex', 'IDBCursor', 'IDBObjectStore', 'IDBTransaction', 'IDBDatabase' ].forEach(function(property) { if (window && (!window[property] || !window.hasOwnProperty(property))) { throw new Error(property); } }); return true; } catch (e) { return false; } } if (idbIsSupported()) { proxyProperties(Index, '_index', [ 'name', 'keyPath', 'multiEntry', 'unique' ]); proxyRequestMethods(Index, '_index', IDBIndex, [ 'get', 'getKey', 'getAll', 'getAllKeys', 'count' ]); proxyCursorRequestMethods(Index, '_index', IDBIndex, [ 'openCursor', 'openKeyCursor' ]); proxyProperties(Cursor, '_cursor', [ 'direction', 'key', 'primaryKey', 'value' ]); proxyRequestMethods(Cursor, '_cursor', IDBCursor, ['update', 'delete']); // proxy 'next' methods ['advance', 'continue', 'continuePrimaryKey'].forEach(function(methodName) { if (!(methodName in IDBCursor.prototype)) return; Cursor.prototype[methodName] = function() { var cursor = this; var args = arguments; return Promise.resolve().then(function() { cursor._cursor[methodName].apply(cursor._cursor, args); return promisifyRequest(cursor._request).then(function(value) { if (!value) return; return new Cursor(value, cursor._request); }); }); }; }); proxyProperties(ObjectStore, '_store', [ 'name', 'keyPath', 'indexNames', 'autoIncrement' ]); proxyRequestMethods(ObjectStore, '_store', IDBObjectStore, [ 'put', 'add', 'delete', 'clear', 'get', 'getAll', 'getKey', 'getAllKeys', 'count' ]); proxyCursorRequestMethods(ObjectStore, '_store', IDBObjectStore, [ 'openCursor', 'openKeyCursor' ]); proxyMethods(ObjectStore, '_store', IDBObjectStore, ['deleteIndex']); proxyProperties(Transaction, '_tx', ['objectStoreNames', 'mode']); proxyMethods(Transaction, '_tx', IDBTransaction, ['abort']); proxyProperties(UpgradeDB, '_db', ['name', 'version', 'objectStoreNames']); proxyMethods(UpgradeDB, '_db', IDBDatabase, ['deleteObjectStore', 'close']); proxyProperties(DB, '_db', ['name', 'version', 'objectStoreNames']); proxyMethods(DB, '_db', IDBDatabase, ['close']); // Add cursor iterators // TODO: remove this once browsers do the right thing with promises ['openCursor', 'openKeyCursor'].forEach(function(funcName) { [ObjectStore, Index].forEach(function(Constructor) { // Don't create iterateKeyCursor if openKeyCursor doesn't exist. if (!(funcName in Constructor.prototype)) return; Constructor.prototype[ funcName.replace('open', 'iterate') ] = function() { var args = toArray(arguments); var callback = args[args.length - 1]; var nativeObject = this._store || this._index; var request = nativeObject[funcName].apply( nativeObject, args.slice(0, -1) ); request.onsuccess = function() { callback(request.result); }; }; }); }); // polyfill getAll [Index, ObjectStore].forEach(function(Constructor) { if (Constructor.prototype.getAll) return; Constructor.prototype.getAll = function(query, count) { var instance = this; var items = []; return new Promise(function(resolve) { instance.iterateCursor(query, function(cursor) { if (!cursor) { resolve(items); return; } items.push(cursor.value); if (count !== undefined && items.length == count) { resolve(items); return; } cursor.continue(); }); }); }; }); exp = { open: function(name, version, upgradeCallback) { var p = promisifyRequestCall(indexedDB, 'open', [name, version]); var request = p.request; if (request) { request.onupgradeneeded = function(event) { if (upgradeCallback) { var db = new UpgradeDB( request.result, event.oldVersion, request.transaction ) db.transaction.complete.catch(function() {}); upgradeCallback(db); } }; } return p.then(function(db) { return new DB(db); }); }, delete: function(name) { return promisifyRequestCall(indexedDB, 'deleteDatabase', [name]); } }; } else { var errorMsg = 'indexedDB is not supported'; exp = { open: function() { return Promise.reject(new Error(errorMsg)); }, delete: function() { return Promise.reject(new Error(errorMsg)); } }; } module.exports = exp;