Repositorio del curso CCOM4030 el semestre B91 del proyecto Artesanías con el Instituto de Cultura

indexeddb.js 35KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097
  1. import isIndexedDBValid from '../utils/isIndexedDBValid';
  2. import createBlob from '../utils/createBlob';
  3. import idb from '../utils/idb';
  4. import Promise from '../utils/promise';
  5. import executeCallback from '../utils/executeCallback';
  6. import executeTwoCallbacks from '../utils/executeTwoCallbacks';
  7. import normalizeKey from '../utils/normalizeKey';
  8. import getCallback from '../utils/getCallback';
  9. // Some code originally from async_storage.js in
  10. // [Gaia](https://github.com/mozilla-b2g/gaia).
  11. const DETECT_BLOB_SUPPORT_STORE = 'local-forage-detect-blob-support';
  12. let supportsBlobs;
  13. const dbContexts = {};
  14. const toString = Object.prototype.toString;
  15. // Transaction Modes
  16. const READ_ONLY = 'readonly';
  17. const READ_WRITE = 'readwrite';
  18. // Transform a binary string to an array buffer, because otherwise
  19. // weird stuff happens when you try to work with the binary string directly.
  20. // It is known.
  21. // From http://stackoverflow.com/questions/14967647/ (continues on next line)
  22. // encode-decode-image-with-base64-breaks-image (2013-04-21)
  23. function _binStringToArrayBuffer(bin) {
  24. var length = bin.length;
  25. var buf = new ArrayBuffer(length);
  26. var arr = new Uint8Array(buf);
  27. for (var i = 0; i < length; i++) {
  28. arr[i] = bin.charCodeAt(i);
  29. }
  30. return buf;
  31. }
  32. //
  33. // Blobs are not supported in all versions of IndexedDB, notably
  34. // Chrome <37 and Android <5. In those versions, storing a blob will throw.
  35. //
  36. // Various other blob bugs exist in Chrome v37-42 (inclusive).
  37. // Detecting them is expensive and confusing to users, and Chrome 37-42
  38. // is at very low usage worldwide, so we do a hacky userAgent check instead.
  39. //
  40. // content-type bug: https://code.google.com/p/chromium/issues/detail?id=408120
  41. // 404 bug: https://code.google.com/p/chromium/issues/detail?id=447916
  42. // FileReader bug: https://code.google.com/p/chromium/issues/detail?id=447836
  43. //
  44. // Code borrowed from PouchDB. See:
  45. // https://github.com/pouchdb/pouchdb/blob/master/packages/node_modules/pouchdb-adapter-idb/src/blobSupport.js
  46. //
  47. function _checkBlobSupportWithoutCaching(idb) {
  48. return new Promise(function(resolve) {
  49. var txn = idb.transaction(DETECT_BLOB_SUPPORT_STORE, READ_WRITE);
  50. var blob = createBlob(['']);
  51. txn.objectStore(DETECT_BLOB_SUPPORT_STORE).put(blob, 'key');
  52. txn.onabort = function(e) {
  53. // If the transaction aborts now its due to not being able to
  54. // write to the database, likely due to the disk being full
  55. e.preventDefault();
  56. e.stopPropagation();
  57. resolve(false);
  58. };
  59. txn.oncomplete = function() {
  60. var matchedChrome = navigator.userAgent.match(/Chrome\/(\d+)/);
  61. var matchedEdge = navigator.userAgent.match(/Edge\//);
  62. // MS Edge pretends to be Chrome 42:
  63. // https://msdn.microsoft.com/en-us/library/hh869301%28v=vs.85%29.aspx
  64. resolve(
  65. matchedEdge ||
  66. !matchedChrome ||
  67. parseInt(matchedChrome[1], 10) >= 43
  68. );
  69. };
  70. }).catch(function() {
  71. return false; // error, so assume unsupported
  72. });
  73. }
  74. function _checkBlobSupport(idb) {
  75. if (typeof supportsBlobs === 'boolean') {
  76. return Promise.resolve(supportsBlobs);
  77. }
  78. return _checkBlobSupportWithoutCaching(idb).then(function(value) {
  79. supportsBlobs = value;
  80. return supportsBlobs;
  81. });
  82. }
  83. function _deferReadiness(dbInfo) {
  84. var dbContext = dbContexts[dbInfo.name];
  85. // Create a deferred object representing the current database operation.
  86. var deferredOperation = {};
  87. deferredOperation.promise = new Promise(function(resolve, reject) {
  88. deferredOperation.resolve = resolve;
  89. deferredOperation.reject = reject;
  90. });
  91. // Enqueue the deferred operation.
  92. dbContext.deferredOperations.push(deferredOperation);
  93. // Chain its promise to the database readiness.
  94. if (!dbContext.dbReady) {
  95. dbContext.dbReady = deferredOperation.promise;
  96. } else {
  97. dbContext.dbReady = dbContext.dbReady.then(function() {
  98. return deferredOperation.promise;
  99. });
  100. }
  101. }
  102. function _advanceReadiness(dbInfo) {
  103. var dbContext = dbContexts[dbInfo.name];
  104. // Dequeue a deferred operation.
  105. var deferredOperation = dbContext.deferredOperations.pop();
  106. // Resolve its promise (which is part of the database readiness
  107. // chain of promises).
  108. if (deferredOperation) {
  109. deferredOperation.resolve();
  110. return deferredOperation.promise;
  111. }
  112. }
  113. function _rejectReadiness(dbInfo, err) {
  114. var dbContext = dbContexts[dbInfo.name];
  115. // Dequeue a deferred operation.
  116. var deferredOperation = dbContext.deferredOperations.pop();
  117. // Reject its promise (which is part of the database readiness
  118. // chain of promises).
  119. if (deferredOperation) {
  120. deferredOperation.reject(err);
  121. return deferredOperation.promise;
  122. }
  123. }
  124. function _getConnection(dbInfo, upgradeNeeded) {
  125. return new Promise(function(resolve, reject) {
  126. dbContexts[dbInfo.name] = dbContexts[dbInfo.name] || createDbContext();
  127. if (dbInfo.db) {
  128. if (upgradeNeeded) {
  129. _deferReadiness(dbInfo);
  130. dbInfo.db.close();
  131. } else {
  132. return resolve(dbInfo.db);
  133. }
  134. }
  135. var dbArgs = [dbInfo.name];
  136. if (upgradeNeeded) {
  137. dbArgs.push(dbInfo.version);
  138. }
  139. var openreq = idb.open.apply(idb, dbArgs);
  140. if (upgradeNeeded) {
  141. openreq.onupgradeneeded = function(e) {
  142. var db = openreq.result;
  143. try {
  144. db.createObjectStore(dbInfo.storeName);
  145. if (e.oldVersion <= 1) {
  146. // Added when support for blob shims was added
  147. db.createObjectStore(DETECT_BLOB_SUPPORT_STORE);
  148. }
  149. } catch (ex) {
  150. if (ex.name === 'ConstraintError') {
  151. console.warn(
  152. 'The database "' +
  153. dbInfo.name +
  154. '"' +
  155. ' has been upgraded from version ' +
  156. e.oldVersion +
  157. ' to version ' +
  158. e.newVersion +
  159. ', but the storage "' +
  160. dbInfo.storeName +
  161. '" already exists.'
  162. );
  163. } else {
  164. throw ex;
  165. }
  166. }
  167. };
  168. }
  169. openreq.onerror = function(e) {
  170. e.preventDefault();
  171. reject(openreq.error);
  172. };
  173. openreq.onsuccess = function() {
  174. resolve(openreq.result);
  175. _advanceReadiness(dbInfo);
  176. };
  177. });
  178. }
  179. function _getOriginalConnection(dbInfo) {
  180. return _getConnection(dbInfo, false);
  181. }
  182. function _getUpgradedConnection(dbInfo) {
  183. return _getConnection(dbInfo, true);
  184. }
  185. function _isUpgradeNeeded(dbInfo, defaultVersion) {
  186. if (!dbInfo.db) {
  187. return true;
  188. }
  189. var isNewStore = !dbInfo.db.objectStoreNames.contains(dbInfo.storeName);
  190. var isDowngrade = dbInfo.version < dbInfo.db.version;
  191. var isUpgrade = dbInfo.version > dbInfo.db.version;
  192. if (isDowngrade) {
  193. // If the version is not the default one
  194. // then warn for impossible downgrade.
  195. if (dbInfo.version !== defaultVersion) {
  196. console.warn(
  197. 'The database "' +
  198. dbInfo.name +
  199. '"' +
  200. " can't be downgraded from version " +
  201. dbInfo.db.version +
  202. ' to version ' +
  203. dbInfo.version +
  204. '.'
  205. );
  206. }
  207. // Align the versions to prevent errors.
  208. dbInfo.version = dbInfo.db.version;
  209. }
  210. if (isUpgrade || isNewStore) {
  211. // If the store is new then increment the version (if needed).
  212. // This will trigger an "upgradeneeded" event which is required
  213. // for creating a store.
  214. if (isNewStore) {
  215. var incVersion = dbInfo.db.version + 1;
  216. if (incVersion > dbInfo.version) {
  217. dbInfo.version = incVersion;
  218. }
  219. }
  220. return true;
  221. }
  222. return false;
  223. }
  224. // encode a blob for indexeddb engines that don't support blobs
  225. function _encodeBlob(blob) {
  226. return new Promise(function(resolve, reject) {
  227. var reader = new FileReader();
  228. reader.onerror = reject;
  229. reader.onloadend = function(e) {
  230. var base64 = btoa(e.target.result || '');
  231. resolve({
  232. __local_forage_encoded_blob: true,
  233. data: base64,
  234. type: blob.type
  235. });
  236. };
  237. reader.readAsBinaryString(blob);
  238. });
  239. }
  240. // decode an encoded blob
  241. function _decodeBlob(encodedBlob) {
  242. var arrayBuff = _binStringToArrayBuffer(atob(encodedBlob.data));
  243. return createBlob([arrayBuff], { type: encodedBlob.type });
  244. }
  245. // is this one of our fancy encoded blobs?
  246. function _isEncodedBlob(value) {
  247. return value && value.__local_forage_encoded_blob;
  248. }
  249. // Specialize the default `ready()` function by making it dependent
  250. // on the current database operations. Thus, the driver will be actually
  251. // ready when it's been initialized (default) *and* there are no pending
  252. // operations on the database (initiated by some other instances).
  253. function _fullyReady(callback) {
  254. var self = this;
  255. var promise = self._initReady().then(function() {
  256. var dbContext = dbContexts[self._dbInfo.name];
  257. if (dbContext && dbContext.dbReady) {
  258. return dbContext.dbReady;
  259. }
  260. });
  261. executeTwoCallbacks(promise, callback, callback);
  262. return promise;
  263. }
  264. // Try to establish a new db connection to replace the
  265. // current one which is broken (i.e. experiencing
  266. // InvalidStateError while creating a transaction).
  267. function _tryReconnect(dbInfo) {
  268. _deferReadiness(dbInfo);
  269. var dbContext = dbContexts[dbInfo.name];
  270. var forages = dbContext.forages;
  271. for (var i = 0; i < forages.length; i++) {
  272. const forage = forages[i];
  273. if (forage._dbInfo.db) {
  274. forage._dbInfo.db.close();
  275. forage._dbInfo.db = null;
  276. }
  277. }
  278. dbInfo.db = null;
  279. return _getOriginalConnection(dbInfo)
  280. .then(db => {
  281. dbInfo.db = db;
  282. if (_isUpgradeNeeded(dbInfo)) {
  283. // Reopen the database for upgrading.
  284. return _getUpgradedConnection(dbInfo);
  285. }
  286. return db;
  287. })
  288. .then(db => {
  289. // store the latest db reference
  290. // in case the db was upgraded
  291. dbInfo.db = dbContext.db = db;
  292. for (var i = 0; i < forages.length; i++) {
  293. forages[i]._dbInfo.db = db;
  294. }
  295. })
  296. .catch(err => {
  297. _rejectReadiness(dbInfo, err);
  298. throw err;
  299. });
  300. }
  301. // FF doesn't like Promises (micro-tasks) and IDDB store operations,
  302. // so we have to do it with callbacks
  303. function createTransaction(dbInfo, mode, callback, retries) {
  304. if (retries === undefined) {
  305. retries = 1;
  306. }
  307. try {
  308. var tx = dbInfo.db.transaction(dbInfo.storeName, mode);
  309. callback(null, tx);
  310. } catch (err) {
  311. if (
  312. retries > 0 &&
  313. (!dbInfo.db ||
  314. err.name === 'InvalidStateError' ||
  315. err.name === 'NotFoundError')
  316. ) {
  317. return Promise.resolve()
  318. .then(() => {
  319. if (
  320. !dbInfo.db ||
  321. (err.name === 'NotFoundError' &&
  322. !dbInfo.db.objectStoreNames.contains(
  323. dbInfo.storeName
  324. ) &&
  325. dbInfo.version <= dbInfo.db.version)
  326. ) {
  327. // increase the db version, to create the new ObjectStore
  328. if (dbInfo.db) {
  329. dbInfo.version = dbInfo.db.version + 1;
  330. }
  331. // Reopen the database for upgrading.
  332. return _getUpgradedConnection(dbInfo);
  333. }
  334. })
  335. .then(() => {
  336. return _tryReconnect(dbInfo).then(function() {
  337. createTransaction(dbInfo, mode, callback, retries - 1);
  338. });
  339. })
  340. .catch(callback);
  341. }
  342. callback(err);
  343. }
  344. }
  345. function createDbContext() {
  346. return {
  347. // Running localForages sharing a database.
  348. forages: [],
  349. // Shared database.
  350. db: null,
  351. // Database readiness (promise).
  352. dbReady: null,
  353. // Deferred operations on the database.
  354. deferredOperations: []
  355. };
  356. }
  357. // Open the IndexedDB database (automatically creates one if one didn't
  358. // previously exist), using any options set in the config.
  359. function _initStorage(options) {
  360. var self = this;
  361. var dbInfo = {
  362. db: null
  363. };
  364. if (options) {
  365. for (var i in options) {
  366. dbInfo[i] = options[i];
  367. }
  368. }
  369. // Get the current context of the database;
  370. var dbContext = dbContexts[dbInfo.name];
  371. // ...or create a new context.
  372. if (!dbContext) {
  373. dbContext = createDbContext();
  374. // Register the new context in the global container.
  375. dbContexts[dbInfo.name] = dbContext;
  376. }
  377. // Register itself as a running localForage in the current context.
  378. dbContext.forages.push(self);
  379. // Replace the default `ready()` function with the specialized one.
  380. if (!self._initReady) {
  381. self._initReady = self.ready;
  382. self.ready = _fullyReady;
  383. }
  384. // Create an array of initialization states of the related localForages.
  385. var initPromises = [];
  386. function ignoreErrors() {
  387. // Don't handle errors here,
  388. // just makes sure related localForages aren't pending.
  389. return Promise.resolve();
  390. }
  391. for (var j = 0; j < dbContext.forages.length; j++) {
  392. var forage = dbContext.forages[j];
  393. if (forage !== self) {
  394. // Don't wait for itself...
  395. initPromises.push(forage._initReady().catch(ignoreErrors));
  396. }
  397. }
  398. // Take a snapshot of the related localForages.
  399. var forages = dbContext.forages.slice(0);
  400. // Initialize the connection process only when
  401. // all the related localForages aren't pending.
  402. return Promise.all(initPromises)
  403. .then(function() {
  404. dbInfo.db = dbContext.db;
  405. // Get the connection or open a new one without upgrade.
  406. return _getOriginalConnection(dbInfo);
  407. })
  408. .then(function(db) {
  409. dbInfo.db = db;
  410. if (_isUpgradeNeeded(dbInfo, self._defaultConfig.version)) {
  411. // Reopen the database for upgrading.
  412. return _getUpgradedConnection(dbInfo);
  413. }
  414. return db;
  415. })
  416. .then(function(db) {
  417. dbInfo.db = dbContext.db = db;
  418. self._dbInfo = dbInfo;
  419. // Share the final connection amongst related localForages.
  420. for (var k = 0; k < forages.length; k++) {
  421. var forage = forages[k];
  422. if (forage !== self) {
  423. // Self is already up-to-date.
  424. forage._dbInfo.db = dbInfo.db;
  425. forage._dbInfo.version = dbInfo.version;
  426. }
  427. }
  428. });
  429. }
  430. function getItem(key, callback) {
  431. var self = this;
  432. key = normalizeKey(key);
  433. var promise = new Promise(function(resolve, reject) {
  434. self
  435. .ready()
  436. .then(function() {
  437. createTransaction(self._dbInfo, READ_ONLY, function(
  438. err,
  439. transaction
  440. ) {
  441. if (err) {
  442. return reject(err);
  443. }
  444. try {
  445. var store = transaction.objectStore(
  446. self._dbInfo.storeName
  447. );
  448. var req = store.get(key);
  449. req.onsuccess = function() {
  450. var value = req.result;
  451. if (value === undefined) {
  452. value = null;
  453. }
  454. if (_isEncodedBlob(value)) {
  455. value = _decodeBlob(value);
  456. }
  457. resolve(value);
  458. };
  459. req.onerror = function() {
  460. reject(req.error);
  461. };
  462. } catch (e) {
  463. reject(e);
  464. }
  465. });
  466. })
  467. .catch(reject);
  468. });
  469. executeCallback(promise, callback);
  470. return promise;
  471. }
  472. // Iterate over all items stored in database.
  473. function iterate(iterator, callback) {
  474. var self = this;
  475. var promise = new Promise(function(resolve, reject) {
  476. self
  477. .ready()
  478. .then(function() {
  479. createTransaction(self._dbInfo, READ_ONLY, function(
  480. err,
  481. transaction
  482. ) {
  483. if (err) {
  484. return reject(err);
  485. }
  486. try {
  487. var store = transaction.objectStore(
  488. self._dbInfo.storeName
  489. );
  490. var req = store.openCursor();
  491. var iterationNumber = 1;
  492. req.onsuccess = function() {
  493. var cursor = req.result;
  494. if (cursor) {
  495. var value = cursor.value;
  496. if (_isEncodedBlob(value)) {
  497. value = _decodeBlob(value);
  498. }
  499. var result = iterator(
  500. value,
  501. cursor.key,
  502. iterationNumber++
  503. );
  504. // when the iterator callback returns any
  505. // (non-`undefined`) value, then we stop
  506. // the iteration immediately
  507. if (result !== void 0) {
  508. resolve(result);
  509. } else {
  510. cursor.continue();
  511. }
  512. } else {
  513. resolve();
  514. }
  515. };
  516. req.onerror = function() {
  517. reject(req.error);
  518. };
  519. } catch (e) {
  520. reject(e);
  521. }
  522. });
  523. })
  524. .catch(reject);
  525. });
  526. executeCallback(promise, callback);
  527. return promise;
  528. }
  529. function setItem(key, value, callback) {
  530. var self = this;
  531. key = normalizeKey(key);
  532. var promise = new Promise(function(resolve, reject) {
  533. var dbInfo;
  534. self
  535. .ready()
  536. .then(function() {
  537. dbInfo = self._dbInfo;
  538. if (toString.call(value) === '[object Blob]') {
  539. return _checkBlobSupport(dbInfo.db).then(function(
  540. blobSupport
  541. ) {
  542. if (blobSupport) {
  543. return value;
  544. }
  545. return _encodeBlob(value);
  546. });
  547. }
  548. return value;
  549. })
  550. .then(function(value) {
  551. createTransaction(self._dbInfo, READ_WRITE, function(
  552. err,
  553. transaction
  554. ) {
  555. if (err) {
  556. return reject(err);
  557. }
  558. try {
  559. var store = transaction.objectStore(
  560. self._dbInfo.storeName
  561. );
  562. // The reason we don't _save_ null is because IE 10 does
  563. // not support saving the `null` type in IndexedDB. How
  564. // ironic, given the bug below!
  565. // See: https://github.com/mozilla/localForage/issues/161
  566. if (value === null) {
  567. value = undefined;
  568. }
  569. var req = store.put(value, key);
  570. transaction.oncomplete = function() {
  571. // Cast to undefined so the value passed to
  572. // callback/promise is the same as what one would get out
  573. // of `getItem()` later. This leads to some weirdness
  574. // (setItem('foo', undefined) will return `null`), but
  575. // it's not my fault localStorage is our baseline and that
  576. // it's weird.
  577. if (value === undefined) {
  578. value = null;
  579. }
  580. resolve(value);
  581. };
  582. transaction.onabort = transaction.onerror = function() {
  583. var err = req.error
  584. ? req.error
  585. : req.transaction.error;
  586. reject(err);
  587. };
  588. } catch (e) {
  589. reject(e);
  590. }
  591. });
  592. })
  593. .catch(reject);
  594. });
  595. executeCallback(promise, callback);
  596. return promise;
  597. }
  598. function removeItem(key, callback) {
  599. var self = this;
  600. key = normalizeKey(key);
  601. var promise = new Promise(function(resolve, reject) {
  602. self
  603. .ready()
  604. .then(function() {
  605. createTransaction(self._dbInfo, READ_WRITE, function(
  606. err,
  607. transaction
  608. ) {
  609. if (err) {
  610. return reject(err);
  611. }
  612. try {
  613. var store = transaction.objectStore(
  614. self._dbInfo.storeName
  615. );
  616. // We use a Grunt task to make this safe for IE and some
  617. // versions of Android (including those used by Cordova).
  618. // Normally IE won't like `.delete()` and will insist on
  619. // using `['delete']()`, but we have a build step that
  620. // fixes this for us now.
  621. var req = store.delete(key);
  622. transaction.oncomplete = function() {
  623. resolve();
  624. };
  625. transaction.onerror = function() {
  626. reject(req.error);
  627. };
  628. // The request will be also be aborted if we've exceeded our storage
  629. // space.
  630. transaction.onabort = function() {
  631. var err = req.error
  632. ? req.error
  633. : req.transaction.error;
  634. reject(err);
  635. };
  636. } catch (e) {
  637. reject(e);
  638. }
  639. });
  640. })
  641. .catch(reject);
  642. });
  643. executeCallback(promise, callback);
  644. return promise;
  645. }
  646. function clear(callback) {
  647. var self = this;
  648. var promise = new Promise(function(resolve, reject) {
  649. self
  650. .ready()
  651. .then(function() {
  652. createTransaction(self._dbInfo, READ_WRITE, function(
  653. err,
  654. transaction
  655. ) {
  656. if (err) {
  657. return reject(err);
  658. }
  659. try {
  660. var store = transaction.objectStore(
  661. self._dbInfo.storeName
  662. );
  663. var req = store.clear();
  664. transaction.oncomplete = function() {
  665. resolve();
  666. };
  667. transaction.onabort = transaction.onerror = function() {
  668. var err = req.error
  669. ? req.error
  670. : req.transaction.error;
  671. reject(err);
  672. };
  673. } catch (e) {
  674. reject(e);
  675. }
  676. });
  677. })
  678. .catch(reject);
  679. });
  680. executeCallback(promise, callback);
  681. return promise;
  682. }
  683. function length(callback) {
  684. var self = this;
  685. var promise = new Promise(function(resolve, reject) {
  686. self
  687. .ready()
  688. .then(function() {
  689. createTransaction(self._dbInfo, READ_ONLY, function(
  690. err,
  691. transaction
  692. ) {
  693. if (err) {
  694. return reject(err);
  695. }
  696. try {
  697. var store = transaction.objectStore(
  698. self._dbInfo.storeName
  699. );
  700. var req = store.count();
  701. req.onsuccess = function() {
  702. resolve(req.result);
  703. };
  704. req.onerror = function() {
  705. reject(req.error);
  706. };
  707. } catch (e) {
  708. reject(e);
  709. }
  710. });
  711. })
  712. .catch(reject);
  713. });
  714. executeCallback(promise, callback);
  715. return promise;
  716. }
  717. function key(n, callback) {
  718. var self = this;
  719. var promise = new Promise(function(resolve, reject) {
  720. if (n < 0) {
  721. resolve(null);
  722. return;
  723. }
  724. self
  725. .ready()
  726. .then(function() {
  727. createTransaction(self._dbInfo, READ_ONLY, function(
  728. err,
  729. transaction
  730. ) {
  731. if (err) {
  732. return reject(err);
  733. }
  734. try {
  735. var store = transaction.objectStore(
  736. self._dbInfo.storeName
  737. );
  738. var advanced = false;
  739. var req = store.openKeyCursor();
  740. req.onsuccess = function() {
  741. var cursor = req.result;
  742. if (!cursor) {
  743. // this means there weren't enough keys
  744. resolve(null);
  745. return;
  746. }
  747. if (n === 0) {
  748. // We have the first key, return it if that's what they
  749. // wanted.
  750. resolve(cursor.key);
  751. } else {
  752. if (!advanced) {
  753. // Otherwise, ask the cursor to skip ahead n
  754. // records.
  755. advanced = true;
  756. cursor.advance(n);
  757. } else {
  758. // When we get here, we've got the nth key.
  759. resolve(cursor.key);
  760. }
  761. }
  762. };
  763. req.onerror = function() {
  764. reject(req.error);
  765. };
  766. } catch (e) {
  767. reject(e);
  768. }
  769. });
  770. })
  771. .catch(reject);
  772. });
  773. executeCallback(promise, callback);
  774. return promise;
  775. }
  776. function keys(callback) {
  777. var self = this;
  778. var promise = new Promise(function(resolve, reject) {
  779. self
  780. .ready()
  781. .then(function() {
  782. createTransaction(self._dbInfo, READ_ONLY, function(
  783. err,
  784. transaction
  785. ) {
  786. if (err) {
  787. return reject(err);
  788. }
  789. try {
  790. var store = transaction.objectStore(
  791. self._dbInfo.storeName
  792. );
  793. var req = store.openKeyCursor();
  794. var keys = [];
  795. req.onsuccess = function() {
  796. var cursor = req.result;
  797. if (!cursor) {
  798. resolve(keys);
  799. return;
  800. }
  801. keys.push(cursor.key);
  802. cursor.continue();
  803. };
  804. req.onerror = function() {
  805. reject(req.error);
  806. };
  807. } catch (e) {
  808. reject(e);
  809. }
  810. });
  811. })
  812. .catch(reject);
  813. });
  814. executeCallback(promise, callback);
  815. return promise;
  816. }
  817. function dropInstance(options, callback) {
  818. callback = getCallback.apply(this, arguments);
  819. var currentConfig = this.config();
  820. options = (typeof options !== 'function' && options) || {};
  821. if (!options.name) {
  822. options.name = options.name || currentConfig.name;
  823. options.storeName = options.storeName || currentConfig.storeName;
  824. }
  825. var self = this;
  826. var promise;
  827. if (!options.name) {
  828. promise = Promise.reject('Invalid arguments');
  829. } else {
  830. const isCurrentDb =
  831. options.name === currentConfig.name && self._dbInfo.db;
  832. const dbPromise = isCurrentDb
  833. ? Promise.resolve(self._dbInfo.db)
  834. : _getOriginalConnection(options).then(db => {
  835. const dbContext = dbContexts[options.name];
  836. const forages = dbContext.forages;
  837. dbContext.db = db;
  838. for (var i = 0; i < forages.length; i++) {
  839. forages[i]._dbInfo.db = db;
  840. }
  841. return db;
  842. });
  843. if (!options.storeName) {
  844. promise = dbPromise.then(db => {
  845. _deferReadiness(options);
  846. const dbContext = dbContexts[options.name];
  847. const forages = dbContext.forages;
  848. db.close();
  849. for (var i = 0; i < forages.length; i++) {
  850. const forage = forages[i];
  851. forage._dbInfo.db = null;
  852. }
  853. const dropDBPromise = new Promise((resolve, reject) => {
  854. var req = idb.deleteDatabase(options.name);
  855. req.onerror = req.onblocked = err => {
  856. const db = req.result;
  857. if (db) {
  858. db.close();
  859. }
  860. reject(err);
  861. };
  862. req.onsuccess = () => {
  863. const db = req.result;
  864. if (db) {
  865. db.close();
  866. }
  867. resolve(db);
  868. };
  869. });
  870. return dropDBPromise
  871. .then(db => {
  872. dbContext.db = db;
  873. for (var i = 0; i < forages.length; i++) {
  874. const forage = forages[i];
  875. _advanceReadiness(forage._dbInfo);
  876. }
  877. })
  878. .catch(err => {
  879. (
  880. _rejectReadiness(options, err) || Promise.resolve()
  881. ).catch(() => {});
  882. throw err;
  883. });
  884. });
  885. } else {
  886. promise = dbPromise.then(db => {
  887. if (!db.objectStoreNames.contains(options.storeName)) {
  888. return;
  889. }
  890. const newVersion = db.version + 1;
  891. _deferReadiness(options);
  892. const dbContext = dbContexts[options.name];
  893. const forages = dbContext.forages;
  894. db.close();
  895. for (let i = 0; i < forages.length; i++) {
  896. const forage = forages[i];
  897. forage._dbInfo.db = null;
  898. forage._dbInfo.version = newVersion;
  899. }
  900. const dropObjectPromise = new Promise((resolve, reject) => {
  901. const req = idb.open(options.name, newVersion);
  902. req.onerror = err => {
  903. const db = req.result;
  904. db.close();
  905. reject(err);
  906. };
  907. req.onupgradeneeded = () => {
  908. var db = req.result;
  909. db.deleteObjectStore(options.storeName);
  910. };
  911. req.onsuccess = () => {
  912. const db = req.result;
  913. db.close();
  914. resolve(db);
  915. };
  916. });
  917. return dropObjectPromise
  918. .then(db => {
  919. dbContext.db = db;
  920. for (let j = 0; j < forages.length; j++) {
  921. const forage = forages[j];
  922. forage._dbInfo.db = db;
  923. _advanceReadiness(forage._dbInfo);
  924. }
  925. })
  926. .catch(err => {
  927. (
  928. _rejectReadiness(options, err) || Promise.resolve()
  929. ).catch(() => {});
  930. throw err;
  931. });
  932. });
  933. }
  934. }
  935. executeCallback(promise, callback);
  936. return promise;
  937. }
  938. var asyncStorage = {
  939. _driver: 'asyncStorage',
  940. _initStorage: _initStorage,
  941. _support: isIndexedDBValid(),
  942. iterate: iterate,
  943. getItem: getItem,
  944. setItem: setItem,
  945. removeItem: removeItem,
  946. clear: clear,
  947. length: length,
  948. key: key,
  949. keys: keys,
  950. dropInstance: dropInstance
  951. };
  952. export default asyncStorage;