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

217 lines (204 loc) 6.41 kB
import FDBObjectStore from './FDBObjectStore.js'; import FDBRequest from './FDBRequest.js'; import { AbortError, InvalidStateError, NotFoundError, TransactionInactiveError, } from './lib/errors.js'; import FakeDOMStringList from './lib/FakeDOMStringList.js'; import FakeEvent from './lib/FakeEvent.js'; import FakeEventTarget from './lib/FakeEventTarget.js'; import { queueTask } from './lib/scheduling.js'; // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#transaction class FDBTransaction extends FakeEventTarget { _state = 'active'; _started = false; _rollbackLog = []; _objectStoresCache = new Map(); error = null; onabort = null; oncomplete = null; onerror = null; _requests = []; constructor(storeNames, mode, db) { super(); this._scope = new Set(storeNames); this.mode = mode; this.db = db; this.objectStoreNames = new FakeDOMStringList(...Array.from(this._scope).sort()); } // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-aborting-a-transaction _abort(errName) { for (const f of this._rollbackLog.reverse()) { f(); } if (errName !== null) { const e = new DOMException(undefined, 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); } } } queueTask(() => { const event = new FakeEvent('abort', { bubbles: true, cancelable: false, }); event.eventPath = [this.db]; this.dispatchEvent(event); }); this._state = 'finished'; } 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 objectStore(name) { if (this._state !== 'active') { throw new InvalidStateError(); } const objectStore = this._objectStoresCache.get(name); if (objectStore !== undefined) { return objectStore; } const rawObjectStore = this.db._rawDatabase.rawObjectStores.get(name); if (!this._scope.has(name) || rawObjectStore === undefined) { throw new NotFoundError(); } const objectStore2 = new FDBObjectStore(this, rawObjectStore); this._objectStoresCache.set(name, objectStore2); return objectStore2; } // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-asynchronously-executing-a-request _execRequestAsync(obj) { const source = obj.source; const operation = obj.operation; let request = Object.hasOwn(obj, '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 FDBRequest(); } else { request = new FDBRequest(); request.source = source; request.transaction = source.transaction; } } this._requests.push({ operation, request, }); return request; } _start() { this._started = true; // 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 operation(); } else { let defaultAction; let event; try { const result = operation(); 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, }); } catch (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, }); defaultAction = this._abort.bind(this, err.name); } try { event.eventPath = [this.db, this]; request.dispatchEvent(event); } catch (err) { if (this._state !== 'committing') { this._abort('AbortError'); } throw err; } // Default action of event if (!event.canceled) { if (defaultAction) { defaultAction(); } } } // Give it another chance for new handlers to be set before finishing queueTask(this._start.bind(this)); return; } // Check if transaction complete event needs to be fired if (this._state !== 'finished') { // Either aborted or committed already this._state = 'finished'; if (!this.error) { const event = new FakeEvent('complete'); this.dispatchEvent(event); } } } commit() { if (this._state !== 'active') { throw new InvalidStateError(); } this._state = 'committing'; } toString() { return '[object IDBRequest]'; } } export default FDBTransaction;