UNPKG

@react-native-firebase/app

Version:

A well tested, feature rich Firebase implementation for React Native, supporting iOS & Android. Individual module support for Admob, Analytics, Auth, Crash Reporting, Cloud Firestore, Database, Dynamic Links, Functions, Messaging (FCM), Remote Config, Sto

257 lines (248 loc) 8.49 kB
import FDBDatabase from './FDBDatabase.js'; import FDBOpenDBRequest from './FDBOpenDBRequest.js'; import FDBVersionChangeEvent from './FDBVersionChangeEvent.js'; import cmp from './lib/cmp.js'; import Database from './lib/Database.js'; import enforceRange from './lib/enforceRange.js'; import { AbortError, VersionError } from './lib/errors.js'; import FakeEvent from './lib/FakeEvent.js'; import { queueTask } from './lib/scheduling.js'; const waitForOthersClosedDelete = (databases, name, openDatabases, cb) => { const anyOpen = openDatabases.some(openDatabase2 => { return !openDatabase2._closed && !openDatabase2._closePending; }); if (anyOpen) { queueTask(() => waitForOthersClosedDelete(databases, name, openDatabases, cb)); return; } databases.delete(name); cb(null); }; // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-deleting-a-database const deleteDatabase = (databases, name, request, cb) => { try { const db = databases.get(name); if (db === undefined) { cb(null); return; } db.deletePending = true; const openDatabases = db.connections.filter(connection => { return !connection._closed && !connection._closePending; }); for (const openDatabase2 of openDatabases) { if (!openDatabase2._closePending) { const event = new FDBVersionChangeEvent('versionchange', { newVersion: null, oldVersion: db.version, }); openDatabase2.dispatchEvent(event); } } const anyOpen = openDatabases.some(openDatabase3 => { return !openDatabase3._closed && !openDatabase3._closePending; }); if (request && anyOpen) { const event = new FDBVersionChangeEvent('blocked', { newVersion: null, oldVersion: db.version, }); request.dispatchEvent(event); } waitForOthersClosedDelete(databases, name, openDatabases, cb); } catch (err) { cb(err); } }; // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-running-a-versionchange-transaction const runVersionchangeTransaction = (connection, version, request, cb) => { connection._runningVersionchangeTransaction = true; const oldVersion = connection.version; const openDatabases = connection._rawDatabase.connections.filter(otherDatabase => { return connection !== otherDatabase; }); for (const openDatabase2 of openDatabases) { if (!openDatabase2._closed && !openDatabase2._closePending) { const event = new FDBVersionChangeEvent('versionchange', { newVersion: version, oldVersion, }); openDatabase2.dispatchEvent(event); } } const anyOpen = openDatabases.some(openDatabase3 => { return !openDatabase3._closed && !openDatabase3._closePending; }); if (anyOpen) { const event = new FDBVersionChangeEvent('blocked', { newVersion: version, oldVersion, }); request.dispatchEvent(event); } const waitForOthersClosed = () => { const anyOpen2 = openDatabases.some(openDatabase2 => { return !openDatabase2._closed && !openDatabase2._closePending; }); if (anyOpen2) { queueTask(waitForOthersClosed); return; } // Set the version of database to version. This change is considered part of the transaction, and so if the // transaction is aborted, this change is reverted. connection._rawDatabase.version = version; connection.version = version; // Get rid of this setImmediate? const transaction = connection.transaction(connection.objectStoreNames, 'versionchange'); request.result = connection; request.readyState = 'done'; request.transaction = transaction; transaction._rollbackLog.push(() => { connection._rawDatabase.version = oldVersion; connection.version = oldVersion; }); const event = new FDBVersionChangeEvent('upgradeneeded', { newVersion: version, oldVersion, }); request.dispatchEvent(event); transaction.addEventListener('error', () => { connection._runningVersionchangeTransaction = false; // throw arguments[0].target.error; // console.log("error in versionchange transaction - not sure if anything needs to be done here", e.target.error.name); }); transaction.addEventListener('abort', () => { connection._runningVersionchangeTransaction = false; request.transaction = null; queueTask(() => { cb(new AbortError()); }); }); transaction.addEventListener('complete', () => { connection._runningVersionchangeTransaction = false; request.transaction = null; // Let other complete event handlers run before continuing queueTask(() => { if (connection._closePending) { cb(new AbortError()); } else { cb(null); } }); }); }; waitForOthersClosed(); }; // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-opening-a-database const openDatabase = (databases, name, version, request, cb) => { let db = databases.get(name); if (db === undefined) { db = new Database(name, 0); databases.set(name, db); } if (version === undefined) { version = db.version !== 0 ? db.version : 1; } if (db.version > version) { return cb(new VersionError()); } const connection = new FDBDatabase(db); if (db.version < version) { runVersionchangeTransaction(connection, version, request, err => { if (err) { // DO THIS HERE: ensure that connection is closed by running the steps for closing a database connection before these // steps are aborted. return cb(err); } cb(null, connection); }); } else { cb(null, connection); } }; class FDBFactory { cmp = cmp; _databases = new Map(); // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBFactory-deleteDatabase-IDBOpenDBRequest-DOMString-name deleteDatabase(name) { const request = new FDBOpenDBRequest(); request.source = null; queueTask(() => { const db = this._databases.get(name); const oldVersion = db !== undefined ? db.version : 0; deleteDatabase(this._databases, name, request, err => { if (err) { request.error = new DOMException(err.message, err.name); request.readyState = 'done'; const event = new FakeEvent('error', { bubbles: true, cancelable: true, }); event.eventPath = []; request.dispatchEvent(event); return; } request.result = undefined; request.readyState = 'done'; const event2 = new FDBVersionChangeEvent('success', { newVersion: null, oldVersion, }); request.dispatchEvent(event2); }); }); return request; } // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBFactory-open-IDBOpenDBRequest-DOMString-name-unsigned-long-long-version open(name, version) { 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 FDBOpenDBRequest(); request.source = null; queueTask(() => { openDatabase(this._databases, name, version, request, (err, connection) => { if (err) { request.result = undefined; request.readyState = 'done'; request.error = new DOMException(err.message, err.name); const event = new FakeEvent('error', { bubbles: true, cancelable: true, }); event.eventPath = []; request.dispatchEvent(event); return; } request.result = connection; request.readyState = 'done'; const event2 = new FakeEvent('success'); event2.eventPath = []; request.dispatchEvent(event2); }); }); return request; } // https://w3c.github.io/IndexedDB/#dom-idbfactory-databases databases() { return new Promise(resolve => { const result = []; for (const [name, database] of this._databases) { result.push({ name, version: database.version, }); } resolve(result); }); } toString() { return '[object IDBFactory]'; } } export default FDBFactory;