diff --git a/patches/localforage+1.10.0.patch b/patches/localforage+1.10.0.patch index 491c5f6e3..ac3c2878f 100644 --- a/patches/localforage+1.10.0.patch +++ b/patches/localforage+1.10.0.patch @@ -1,750 +1,785 @@ diff --git a/node_modules/localforage/dist/localforage.js b/node_modules/localforage/dist/localforage.js -index 4e3295b..9049388 100644 +index 4e3295b..c92a702 100644 --- a/node_modules/localforage/dist/localforage.js +++ b/node_modules/localforage/dist/localforage.js -@@ -1125,6 +1125,172 @@ function removeItem(key, callback) { +@@ -1125,6 +1125,177 @@ function removeItem(key, callback) { return promise; } +function getMultipleItems(keys, synchronizationKey) { + var self = this; + + var normalizedKeys = keys.map(normalizeKey); + var normalizedSynchronizationKey = normalizeKey(synchronizationKey); + + var promise = new Promise$1(function (resolve, reject) { + self.ready().then(function () { + createTransaction(self._dbInfo, READ_WRITE, function (err, transaction) { + if (err) { + return reject(err); + } + + try { + var store = transaction.objectStore(self._dbInfo.storeName); + + var allKeys = [].concat(normalizedKeys, [normalizedSynchronizationKey]); + var result = {}; + var values = {}; + + var placeRequest = function placeRequest(keysArray, index) { + var key = keysArray[index]; + var req = store.get(key); + + req.onsuccess = function () { + var value = req.result; + if (value === undefined) { + value = null; + } + + if (_isEncodedBlob(value)) { + value = _decodeBlob(value); + } + + if (key === normalizedSynchronizationKey) { + result['synchronizationValue'] = value; + } else { + values[key] = value; + } + + if (index < keysArray.length - 1) { + placeRequest(keysArray, index + 1); + return; + } + + result['values'] = values; + resolve(result); + }; + + req.onerror = function () { + reject(req.err); + }; + }; + + placeRequest(allKeys, 0); + } catch (e) { + reject(e); + } + }); + })["catch"](reject); + }); + + return promise; +} + ++function getSetMultipleItemsRaceConditionErrorMessage() { ++ return 'Another thread completed transaction'; ++} ++ +function setMultipleItems(input, synchronizationKey, expectedSynchronizationValue, newSynchronizationValue, forceWrite) { + var self = this; + var normalizedSynchronizationKey = normalizeKey(synchronizationKey); + + var promise = new Promise$1(function (resolve, reject) { + var dbInfo; + self.ready().then(function () { + dbInfo = self._dbInfo; + var inputArray = Object.entries(input); + return Promise$1.all(inputArray.map(function (_ref) { + var key = _ref[0], + value = _ref[1]; + + var normalizedKey = normalizeKey(key); + if (value === null) { + value = undefined; + } + + if (!toString.call(value) === '[object Blob]') { + return [normalizedKey, value]; + } + + return _checkBlobSupport(dbInfo.db).then(function (blobSupport) { + if (blobSupport) { + return [normalizedKey, value]; + } + return [normalizedKey, _encodeBlob(value)]; + }); + })); + }).then(function (inputArray) { + createTransaction(self._dbInfo, READ_WRITE, function (err, transaction) { + if (err) { + reject(err); + } + + try { + var store = transaction.objectStore(self._dbInfo.storeName); + + var req = store.get(normalizedSynchronizationKey); + + req.onsuccess = function () { + var value = req.result; + if (value === undefined) { + value = null; + } + if (value !== expectedSynchronizationValue && !forceWrite) { -+ reject('Another thread completed transaction'); ++ var errorMessage = getSetMultipleItemsRaceConditionErrorMessage(); ++ reject(new Error(errorMessage)); + return; + } + + for (var _iterator = inputArray, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref2; + + if (_isArray) { + if (_i >= _iterator.length) break; + _ref2 = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) break; + _ref2 = _i.value; + } + + var _ref3 = _ref2, + key = _ref3[0], + value = _ref3[1]; + + var writeRequest = store.put(value, key); + + writeRequest.onerror = function () { + reject(writeRequest.err); + }; + } + + var synchronizationValueWriteRequest = store.put(newSynchronizationValue, normalizedSynchronizationKey); + + synchronizationValueWriteRequest.onerror = function () { + reject(synchronizationValueWriteRequest.err); + }; + }; + + req.onerror = function () { + reject(req.err); + }; + + transaction.oncomplete = function () { + resolve(); + }; + + transaction.onabort = transaction.onerror = function () { + reject(transaction.error); + }; + } catch (e) { + reject(e); + } + }); + })["catch"](reject); + }); + + return promise; +} + function clear(callback) { var self = this; -@@ -1437,6 +1603,8 @@ var asyncStorage = { +@@ -1437,6 +1608,9 @@ var asyncStorage = { getItem: getItem, setItem: setItem, removeItem: removeItem, + getMultipleItems: getMultipleItems, + setMultipleItems: setMultipleItems, ++ getSetMultipleItemsRaceConditionErrorMessage: getSetMultipleItemsRaceConditionErrorMessage, clear: clear, length: length, key: key, -@@ -2100,6 +2268,12 @@ var webSQLStorage = { +@@ -2100,6 +2274,15 @@ var webSQLStorage = { getItem: getItem$1, setItem: setItem$1, removeItem: removeItem$1, + getMultipleItems: function getMultipleItems() { + throw "Method unsupported for driver."; + }, + setMultipleItems: function setMultipleItems() { + throw "Method unsupported for driver."; ++ }, ++ getSetMultipleItemsRaceConditionErrorMessage: function getSetMultipleItemsRaceConditionErrorMessage() { ++ throw "Method unsupported for driver."; + }, clear: clear$1, length: length$1, key: key$1, -@@ -2422,6 +2596,12 @@ var localStorageWrapper = { +@@ -2422,6 +2605,15 @@ var localStorageWrapper = { getItem: getItem$2, setItem: setItem$2, removeItem: removeItem$2, + getMultipleItems: function getMultipleItems() { + throw "Method unsupported for driver."; + }, + setMultipleItems: function setMultipleItems() { + throw "Method unsupported for driver."; ++ }, ++ getSetMultipleItemsRaceConditionErrorMessage: function getSetMultipleItemsRaceConditionErrorMessage() { ++ throw "Method unsupported for driver."; + }, clear: clear$2, length: length$2, key: key$2, -@@ -2466,7 +2646,7 @@ var DefaultDriverOrder = [DefaultDrivers.INDEXEDDB._driver, DefaultDrivers.WEBSQ +@@ -2466,7 +2658,7 @@ var DefaultDriverOrder = [DefaultDrivers.INDEXEDDB._driver, DefaultDrivers.WEBSQ var OptionalDriverMethods = ['dropInstance']; -var LibraryMethods = ['clear', 'getItem', 'iterate', 'key', 'keys', 'length', 'removeItem', 'setItem'].concat(OptionalDriverMethods); -+var LibraryMethods = ['clear', 'getItem', 'iterate', 'key', 'keys', 'length', 'removeItem', 'setItem', 'setMultipleItems', 'getMultipleItems'].concat(OptionalDriverMethods); ++var LibraryMethods = ['clear', 'getItem', 'iterate', 'key', 'keys', 'length', 'removeItem', 'setItem', 'setMultipleItems', 'getSetMultipleItemsRaceConditionErrorMessage', 'getMultipleItems'].concat(OptionalDriverMethods); var DefaultConfig = { description: '', -@@ -2621,8 +2801,8 @@ var LocalForage = function () { +@@ -2621,8 +2813,8 @@ var LocalForage = function () { }; }; - for (var _i = 0, _len = OptionalDriverMethods.length; _i < _len; _i++) { - var optionalDriverMethod = OptionalDriverMethods[_i]; + for (var _i2 = 0, _len = OptionalDriverMethods.length; _i2 < _len; _i2++) { + var optionalDriverMethod = OptionalDriverMethods[_i2]; if (!driverObject[optionalDriverMethod]) { driverObject[optionalDriverMethod] = methodNotImplementedFactory(optionalDriverMethod); } diff --git a/node_modules/localforage/dist/localforage.min.js b/node_modules/localforage/dist/localforage.min.js -index 7403f8f..195b535 100644 +index 7403f8f..22478ac 100644 --- a/node_modules/localforage/dist/localforage.min.js +++ b/node_modules/localforage/dist/localforage.min.js @@ -4,4 +4,4 @@ https://localforage.github.io/localForage (c) 2013-2017 Mozilla, Apache License 2.0 */ -!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.localforage=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c||a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g=43)}}).catch(function(){return!1})}function n(a){return"boolean"==typeof xa?va.resolve(xa):m(a).then(function(a){return xa=a})}function o(a){var b=ya[a.name],c={};c.promise=new va(function(a,b){c.resolve=a,c.reject=b}),b.deferredOperations.push(c),b.dbReady?b.dbReady=b.dbReady.then(function(){return c.promise}):b.dbReady=c.promise}function p(a){var b=ya[a.name],c=b.deferredOperations.pop();if(c)return c.resolve(),c.promise}function q(a,b){var c=ya[a.name],d=c.deferredOperations.pop();if(d)return d.reject(b),d.promise}function r(a,b){return new va(function(c,d){if(ya[a.name]=ya[a.name]||B(),a.db){if(!b)return c(a.db);o(a),a.db.close()}var e=[a.name];b&&e.push(a.version);var f=ua.open.apply(ua,e);b&&(f.onupgradeneeded=function(b){var c=f.result;try{c.createObjectStore(a.storeName),b.oldVersion<=1&&c.createObjectStore(wa)}catch(c){if("ConstraintError"!==c.name)throw c;console.warn('The database "'+a.name+'" has been upgraded from version '+b.oldVersion+" to version "+b.newVersion+', but the storage "'+a.storeName+'" already exists.')}}),f.onerror=function(a){a.preventDefault(),d(f.error)},f.onsuccess=function(){var b=f.result;b.onversionchange=function(a){a.target.close()},c(b),p(a)}})}function s(a){return r(a,!1)}function t(a){return r(a,!0)}function u(a,b){if(!a.db)return!0;var c=!a.db.objectStoreNames.contains(a.storeName),d=a.versiona.db.version;if(d&&(a.version!==b&&console.warn('The database "'+a.name+"\" can't be downgraded from version "+a.db.version+" to version "+a.version+"."),a.version=a.db.version),e||c){if(c){var f=a.db.version+1;f>a.version&&(a.version=f)}return!0}return!1}function v(a){return new va(function(b,c){var d=new FileReader;d.onerror=c,d.onloadend=function(c){var d=btoa(c.target.result||"");b({__local_forage_encoded_blob:!0,data:d,type:a.type})},d.readAsBinaryString(a)})}function w(a){return g([l(atob(a.data))],{type:a.type})}function x(a){return a&&a.__local_forage_encoded_blob}function y(a){var b=this,c=b._initReady().then(function(){var a=ya[b._dbInfo.name];if(a&&a.dbReady)return a.dbReady});return i(c,a,a),c}function z(a){o(a);for(var b=ya[a.name],c=b.forages,d=0;d0&&(!a.db||"InvalidStateError"===e.name||"NotFoundError"===e.name))return va.resolve().then(function(){if(!a.db||"NotFoundError"===e.name&&!a.db.objectStoreNames.contains(a.storeName)&&a.version<=a.db.version)return a.db&&(a.version=a.db.version+1),t(a)}).then(function(){return z(a).then(function(){A(a,b,c,d-1)})}).catch(c);c(e)}}function B(){return{forages:[],db:null,dbReady:null,deferredOperations:[]}}function C(a){function b(){return va.resolve()}var c=this,d={db:null};if(a)for(var e in a)d[e]=a[e];var f=ya[d.name];f||(f=B(),ya[d.name]=f),f.forages.push(c),c._initReady||(c._initReady=c.ready,c.ready=y);for(var g=[],h=0;h>4,k[i++]=(15&d)<<4|e>>2,k[i++]=(3&e)<<6|63&f;return j}function O(a){var b,c=new Uint8Array(a),d="";for(b=0;b>2],d+=Da[(3&c[b])<<4|c[b+1]>>4],d+=Da[(15&c[b+1])<<2|c[b+2]>>6],d+=Da[63&c[b+2]];return c.length%3==2?d=d.substring(0,d.length-1)+"=":c.length%3==1&&(d=d.substring(0,d.length-2)+"=="),d}function P(a,b){var c="";if(a&&(c=Ua.call(a)),a&&("[object ArrayBuffer]"===c||a.buffer&&"[object ArrayBuffer]"===Ua.call(a.buffer))){var d,e=Ga;a instanceof ArrayBuffer?(d=a,e+=Ia):(d=a.buffer,"[object Int8Array]"===c?e+=Ka:"[object Uint8Array]"===c?e+=La:"[object Uint8ClampedArray]"===c?e+=Ma:"[object Int16Array]"===c?e+=Na:"[object Uint16Array]"===c?e+=Pa:"[object Int32Array]"===c?e+=Oa:"[object Uint32Array]"===c?e+=Qa:"[object Float32Array]"===c?e+=Ra:"[object Float64Array]"===c?e+=Sa:b(new Error("Failed to get type for BinaryArray"))),b(e+O(d))}else if("[object Blob]"===c){var f=new FileReader;f.onload=function(){var c=Ea+a.type+"~"+O(this.result);b(Ga+Ja+c)},f.readAsArrayBuffer(a)}else try{b(JSON.stringify(a))}catch(c){console.error("Couldn't convert value into a JSON string: ",a),b(null,c)}}function Q(a){if(a.substring(0,Ha)!==Ga)return JSON.parse(a);var b,c=a.substring(Ta),d=a.substring(Ha,Ta);if(d===Ja&&Fa.test(c)){var e=c.match(Fa);b=e[1],c=c.substring(e[0].length)}var f=N(c);switch(d){case Ia:return f;case Ja:return g([f],{type:b});case Ka:return new Int8Array(f);case La:return new Uint8Array(f);case Ma:return new Uint8ClampedArray(f);case Na:return new Int16Array(f);case Pa:return new Uint16Array(f);case Oa:return new Int32Array(f);case Qa:return new Uint32Array(f);case Ra:return new Float32Array(f);case Sa:return new Float64Array(f);default:throw new Error("Unkown type: "+d)}}function R(a,b,c,d){a.executeSql("CREATE TABLE IF NOT EXISTS "+b.storeName+" (id INTEGER PRIMARY KEY, key unique, value)",[],c,d)}function S(a){var b=this,c={db:null};if(a)for(var d in a)c[d]="string"!=typeof a[d]?a[d].toString():a[d];var e=new va(function(a,d){try{c.db=openDatabase(c.name,String(c.version),c.description,c.size)}catch(a){return d(a)}c.db.transaction(function(e){R(e,c,function(){b._dbInfo=c,a()},function(a,b){d(b)})},d)});return c.serializer=Va,e}function T(a,b,c,d,e,f){a.executeSql(c,d,e,function(a,g){g.code===g.SYNTAX_ERR?a.executeSql("SELECT name FROM sqlite_master WHERE type='table' AND name = ?",[b.storeName],function(a,h){h.rows.length?f(a,g):R(a,b,function(){a.executeSql(c,d,e,f)},f)},f):f(a,g)},f)}function U(a,b){var c=this;a=j(a);var d=new va(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){T(c,e,"SELECT * FROM "+e.storeName+" WHERE key = ? LIMIT 1",[a],function(a,c){var d=c.rows.length?c.rows.item(0).value:null;d&&(d=e.serializer.deserialize(d)),b(d)},function(a,b){d(b)})})}).catch(d)});return h(d,b),d}function V(a,b){var c=this,d=new va(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){T(c,e,"SELECT * FROM "+e.storeName,[],function(c,d){for(var f=d.rows,g=f.length,h=0;h0)return void f(W.apply(e,[a,h,c,d-1]));g(b)}})})}).catch(g)});return h(f,c),f}function X(a,b,c){return W.apply(this,[a,b,c,1])}function Y(a,b){var c=this;a=j(a);var d=new va(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){T(c,e,"DELETE FROM "+e.storeName+" WHERE key = ?",[a],function(){b()},function(a,b){d(b)})})}).catch(d)});return h(d,b),d}function Z(a){var b=this,c=new va(function(a,c){b.ready().then(function(){var d=b._dbInfo;d.db.transaction(function(b){T(b,d,"DELETE FROM "+d.storeName,[],function(){a()},function(a,b){c(b)})})}).catch(c)});return h(c,a),c}function $(a){var b=this,c=new va(function(a,c){b.ready().then(function(){var d=b._dbInfo;d.db.transaction(function(b){T(b,d,"SELECT COUNT(key) as c FROM "+d.storeName,[],function(b,c){var d=c.rows.item(0).c;a(d)},function(a,b){c(b)})})}).catch(c)});return h(c,a),c}function _(a,b){var c=this,d=new va(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){T(c,e,"SELECT key FROM "+e.storeName+" WHERE id = ? LIMIT 1",[a+1],function(a,c){var d=c.rows.length?c.rows.item(0).key:null;b(d)},function(a,b){d(b)})})}).catch(d)});return h(d,b),d}function aa(a){var b=this,c=new va(function(a,c){b.ready().then(function(){var d=b._dbInfo;d.db.transaction(function(b){T(b,d,"SELECT key FROM "+d.storeName,[],function(b,c){for(var d=[],e=0;e '__WebKitDatabaseInfoTable__'",[],function(c,d){for(var e=[],f=0;f0}function ha(a){var b=this,c={};if(a)for(var d in a)c[d]=a[d];return c.keyPrefix=ea(a,b._defaultConfig),ga()?(b._dbInfo=c,c.serializer=Va,va.resolve()):va.reject()}function ia(a){var b=this,c=b.ready().then(function(){for(var a=b._dbInfo.keyPrefix,c=localStorage.length-1;c>=0;c--){var d=localStorage.key(c);0===d.indexOf(a)&&localStorage.removeItem(d)}});return h(c,a),c}function ja(a,b){var c=this;a=j(a);var d=c.ready().then(function(){var b=c._dbInfo,d=localStorage.getItem(b.keyPrefix+a);return d&&(d=b.serializer.deserialize(d)),d});return h(d,b),d}function ka(a,b){var c=this,d=c.ready().then(function(){for(var b=c._dbInfo,d=b.keyPrefix,e=d.length,f=localStorage.length,g=1,h=0;h=0;b--){var c=localStorage.key(b);0===c.indexOf(a)&&localStorage.removeItem(c)}}):va.reject("Invalid arguments"),h(d,b),d}function ra(a,b){a[b]=function(){var c=arguments;return a.ready().then(function(){return a[b].apply(a,c)})}}function sa(){for(var a=1;a=43)}}).catch(function(){return!1})}function n(a){return"boolean"==typeof za?xa.resolve(za):m(a).then(function(a){return za=a})}function o(a){var b=Aa[a.name],c={};c.promise=new xa(function(a,b){c.resolve=a,c.reject=b}),b.deferredOperations.push(c),b.dbReady?b.dbReady=b.dbReady.then(function(){return c.promise}):b.dbReady=c.promise}function p(a){var b=Aa[a.name],c=b.deferredOperations.pop();if(c)return c.resolve(),c.promise}function q(a,b){var c=Aa[a.name],d=c.deferredOperations.pop();if(d)return d.reject(b),d.promise}function r(a,b){return new xa(function(c,d){if(Aa[a.name]=Aa[a.name]||B(),a.db){if(!b)return c(a.db);o(a),a.db.close()}var e=[a.name];b&&e.push(a.version);var f=wa.open.apply(wa,e);b&&(f.onupgradeneeded=function(b){var c=f.result;try{c.createObjectStore(a.storeName),b.oldVersion<=1&&c.createObjectStore(ya)}catch(c){if("ConstraintError"!==c.name)throw c;console.warn('The database "'+a.name+'" has been upgraded from version '+b.oldVersion+" to version "+b.newVersion+', but the storage "'+a.storeName+'" already exists.')}}),f.onerror=function(a){a.preventDefault(),d(f.error)},f.onsuccess=function(){var b=f.result;b.onversionchange=function(a){a.target.close()},c(b),p(a)}})}function s(a){return r(a,!1)}function t(a){return r(a,!0)}function u(a,b){if(!a.db)return!0;var c=!a.db.objectStoreNames.contains(a.storeName),d=a.versiona.db.version;if(d&&(a.version!==b&&console.warn('The database "'+a.name+"\" can't be downgraded from version "+a.db.version+" to version "+a.version+"."),a.version=a.db.version),e||c){if(c){var f=a.db.version+1;f>a.version&&(a.version=f)}return!0}return!1}function v(a){return new xa(function(b,c){var d=new FileReader;d.onerror=c,d.onloadend=function(c){var d=btoa(c.target.result||"");b({__local_forage_encoded_blob:!0,data:d,type:a.type})},d.readAsBinaryString(a)})}function w(a){return g([l(atob(a.data))],{type:a.type})}function x(a){return a&&a.__local_forage_encoded_blob}function y(a){var b=this,c=b._initReady().then(function(){var a=Aa[b._dbInfo.name];if(a&&a.dbReady)return a.dbReady});return i(c,a,a),c}function z(a){o(a);for(var b=Aa[a.name],c=b.forages,d=0;d0&&(!a.db||"InvalidStateError"===e.name||"NotFoundError"===e.name))return xa.resolve().then(function(){if(!a.db||"NotFoundError"===e.name&&!a.db.objectStoreNames.contains(a.storeName)&&a.version<=a.db.version)return a.db&&(a.version=a.db.version+1),t(a)}).then(function(){return z(a).then(function(){A(a,b,c,d-1)})}).catch(c);c(e)}}function B(){return{forages:[],db:null,dbReady:null,deferredOperations:[]}}function C(a){function b(){return xa.resolve()}var c=this,d={db:null};if(a)for(var e in a)d[e]=a[e];var f=Aa[d.name];f||(f=B(),Aa[d.name]=f),f.forages.push(c),c._initReady||(c._initReady=c.ready,c.ready=y);for(var g=[],h=0;h=f.length)break;m=f[j++]}else{if(j=f.next(),j.done)break;m=j.value}var n=m,o=n[0],b=n[1],p=k.put(b,o);p.onerror=function(){h(p.err)}}var q=k.put(d,g);q.onerror=function(){h(q.err)}},l.onerror=function(){h(l.err)},j.oncomplete=function(){b()},j.onabort=j.onerror=function(){h(j.error)}}catch(a){h(a)}})}).catch(h)})}function J(a){var b=this,c=new xa(function(a,c){b.ready().then(function(){A(b._dbInfo,Da,function(d,e){if(d)return c(d);try{var f=e.objectStore(b._dbInfo.storeName),g=f.clear();e.oncomplete=function(){a()},e.onabort=e.onerror=function(){var a=g.error?g.error:g.transaction.error;c(a)}}catch(a){c(a)}})}).catch(c)});return h(c,a),c}function K(a){var b=this,c=new xa(function(a,c){b.ready().then(function(){A(b._dbInfo,Ca,function(d,e){if(d)return c(d);try{var f=e.objectStore(b._dbInfo.storeName),g=f.count();g.onsuccess=function(){a(g.result)},g.onerror=function(){c(g.error)}}catch(a){c(a)}})}).catch(c)});return h(c,a),c}function L(a,b){var c=this,d=new xa(function(b,d){if(a<0)return void b(null);c.ready().then(function(){A(c._dbInfo,Ca,function(e,f){if(e)return d(e);try{var g=f.objectStore(c._dbInfo.storeName),h=!1,i=g.openKeyCursor();i.onsuccess=function(){var c=i.result;if(!c)return void b(null);0===a?b(c.key):h?b(c.key):(h=!0,c.advance(a))},i.onerror=function(){d(i.error)}}catch(a){d(a)}})}).catch(d)});return h(d,b),d}function M(a){var b=this,c=new xa(function(a,c){b.ready().then(function(){A(b._dbInfo,Ca,function(d,e){if(d)return c(d);try{var f=e.objectStore(b._dbInfo.storeName),g=f.openKeyCursor(),h=[];g.onsuccess=function(){var b=g.result;if(!b)return void a(h);h.push(b.key),b.continue()},g.onerror=function(){c(g.error)}}catch(a){c(a)}})}).catch(c)});return h(c,a),c}function N(a,b){b=k.apply(this,arguments);var c=this.config();a="function"!=typeof a&&a||{},a.name||(a.name=a.name||c.name,a.storeName=a.storeName||c.storeName);var d,e=this;if(a.name){var f=a.name===c.name&&e._dbInfo.db,g=f?xa.resolve(e._dbInfo.db):s(a).then(function(b){var c=Aa[a.name],d=c.forages;c.db=b;for(var e=0;e>4,k[i++]=(15&d)<<4|e>>2,k[i++]=(3&e)<<6|63&f;return j}function Q(a){var b,c=new Uint8Array(a),d="";for(b=0;b>2],d+=Fa[(3&c[b])<<4|c[b+1]>>4],d+=Fa[(15&c[b+1])<<2|c[b+2]>>6],d+=Fa[63&c[b+2]];return c.length%3==2?d=d.substring(0,d.length-1)+"=":c.length%3==1&&(d=d.substring(0,d.length-2)+"=="),d}function R(a,b){var c="";if(a&&(c=Wa.call(a)),a&&("[object ArrayBuffer]"===c||a.buffer&&"[object ArrayBuffer]"===Wa.call(a.buffer))){var d,e=Ia;a instanceof ArrayBuffer?(d=a,e+=Ka):(d=a.buffer,"[object Int8Array]"===c?e+=Ma:"[object Uint8Array]"===c?e+=Na:"[object Uint8ClampedArray]"===c?e+=Oa:"[object Int16Array]"===c?e+=Pa:"[object Uint16Array]"===c?e+=Ra:"[object Int32Array]"===c?e+=Qa:"[object Uint32Array]"===c?e+=Sa:"[object Float32Array]"===c?e+=Ta:"[object Float64Array]"===c?e+=Ua:b(new Error("Failed to get type for BinaryArray"))),b(e+Q(d))}else if("[object Blob]"===c){var f=new FileReader;f.onload=function(){var c=Ga+a.type+"~"+Q(this.result);b(Ia+La+c)},f.readAsArrayBuffer(a)}else try{b(JSON.stringify(a))}catch(c){console.error("Couldn't convert value into a JSON string: ",a),b(null,c)}}function S(a){if(a.substring(0,Ja)!==Ia)return JSON.parse(a);var b,c=a.substring(Va),d=a.substring(Ja,Va);if(d===La&&Ha.test(c)){var e=c.match(Ha);b=e[1],c=c.substring(e[0].length)}var f=P(c);switch(d){case Ka:return f;case La:return g([f],{type:b});case Ma:return new Int8Array(f);case Na:return new Uint8Array(f);case Oa:return new Uint8ClampedArray(f);case Pa:return new Int16Array(f);case Ra:return new Uint16Array(f);case Qa:return new Int32Array(f);case Sa:return new Uint32Array(f);case Ta:return new Float32Array(f);case Ua:return new Float64Array(f);default:throw new Error("Unkown type: "+d)}}function T(a,b,c,d){a.executeSql("CREATE TABLE IF NOT EXISTS "+b.storeName+" (id INTEGER PRIMARY KEY, key unique, value)",[],c,d)}function U(a){var b=this,c={db:null};if(a)for(var d in a)c[d]="string"!=typeof a[d]?a[d].toString():a[d];var e=new xa(function(a,d){try{c.db=openDatabase(c.name,String(c.version),c.description,c.size)}catch(a){return d(a)}c.db.transaction(function(e){T(e,c,function(){b._dbInfo=c,a()},function(a,b){d(b)})},d)});return c.serializer=Xa,e}function V(a,b,c,d,e,f){a.executeSql(c,d,e,function(a,g){g.code===g.SYNTAX_ERR?a.executeSql("SELECT name FROM sqlite_master WHERE type='table' AND name = ?",[b.storeName],function(a,h){h.rows.length?f(a,g):T(a,b,function(){a.executeSql(c,d,e,f)},f)},f):f(a,g)},f)}function W(a,b){var c=this;a=j(a);var d=new xa(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){V(c,e,"SELECT * FROM "+e.storeName+" WHERE key = ? LIMIT 1",[a],function(a,c){var d=c.rows.length?c.rows.item(0).value:null;d&&(d=e.serializer.deserialize(d)),b(d)},function(a,b){d(b)})})}).catch(d)});return h(d,b),d}function X(a,b){var c=this,d=new xa(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){V(c,e,"SELECT * FROM "+e.storeName,[],function(c,d){for(var f=d.rows,g=f.length,h=0;h0)return void f(Y.apply(e,[a,h,c,d-1]));g(b)}})})}).catch(g)});return h(f,c),f}function Z(a,b,c){return Y.apply(this,[a,b,c,1])}function $(a,b){var c=this;a=j(a);var d=new xa(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){V(c,e,"DELETE FROM "+e.storeName+" WHERE key = ?",[a],function(){b()},function(a,b){d(b)})})}).catch(d)});return h(d,b),d}function _(a){var b=this,c=new xa(function(a,c){b.ready().then(function(){var d=b._dbInfo;d.db.transaction(function(b){V(b,d,"DELETE FROM "+d.storeName,[],function(){a()},function(a,b){c(b)})})}).catch(c)});return h(c,a),c}function aa(a){var b=this,c=new xa(function(a,c){b.ready().then(function(){var d=b._dbInfo;d.db.transaction(function(b){V(b,d,"SELECT COUNT(key) as c FROM "+d.storeName,[],function(b,c){var d=c.rows.item(0).c;a(d)},function(a,b){c(b)})})}).catch(c)});return h(c,a),c}function ba(a,b){var c=this,d=new xa(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){V(c,e,"SELECT key FROM "+e.storeName+" WHERE id = ? LIMIT 1",[a+1],function(a,c){var d=c.rows.length?c.rows.item(0).key:null;b(d)},function(a,b){d(b)})})}).catch(d)});return h(d,b),d}function ca(a){var b=this,c=new xa(function(a,c){b.ready().then(function(){var d=b._dbInfo;d.db.transaction(function(b){V(b,d,"SELECT key FROM "+d.storeName,[],function(b,c){for(var d=[],e=0;e '__WebKitDatabaseInfoTable__'",[],function(c,d){for(var e=[],f=0;f0}function ja(a){var b=this,c={};if(a)for(var d in a)c[d]=a[d];return c.keyPrefix=ga(a,b._defaultConfig),ia()?(b._dbInfo=c,c.serializer=Xa,xa.resolve()):xa.reject()}function ka(a){var b=this,c=b.ready().then(function(){for(var a=b._dbInfo.keyPrefix,c=localStorage.length-1;c>=0;c--){var d=localStorage.key(c);0===d.indexOf(a)&&localStorage.removeItem(d)}});return h(c,a),c}function la(a,b){var c=this;a=j(a);var d=c.ready().then(function(){var b=c._dbInfo,d=localStorage.getItem(b.keyPrefix+a);return d&&(d=b.serializer.deserialize(d)),d});return h(d,b),d}function ma(a,b){var c=this,d=c.ready().then(function(){for(var b=c._dbInfo,d=b.keyPrefix,e=d.length,f=localStorage.length,g=1,h=0;h=0;b--){var c=localStorage.key(b);0===c.indexOf(a)&&localStorage.removeItem(c)}}):xa.reject("Invalid arguments"),h(d,b),d}function ta(a,b){a[b]=function(){var c=arguments;return a.ready().then(function(){return a[b].apply(a,c)})}}function ua(){for(var a=1;a=43)}}).catch(function(){return!1})}function n(a){return"boolean"==typeof Aa?ya.resolve(Aa):m(a).then(function(a){return Aa=a})}function o(a){var b=Ba[a.name],c={};c.promise=new ya(function(a,b){c.resolve=a,c.reject=b}),b.deferredOperations.push(c),b.dbReady?b.dbReady=b.dbReady.then(function(){return c.promise}):b.dbReady=c.promise}function p(a){var b=Ba[a.name],c=b.deferredOperations.pop();if(c)return c.resolve(),c.promise}function q(a,b){var c=Ba[a.name],d=c.deferredOperations.pop();if(d)return d.reject(b),d.promise}function r(a,b){return new ya(function(c,d){if(Ba[a.name]=Ba[a.name]||B(),a.db){if(!b)return c(a.db);o(a),a.db.close()}var e=[a.name];b&&e.push(a.version);var f=xa.open.apply(xa,e);b&&(f.onupgradeneeded=function(b){var c=f.result;try{c.createObjectStore(a.storeName),b.oldVersion<=1&&c.createObjectStore(za)}catch(c){if("ConstraintError"!==c.name)throw c;console.warn('The database "'+a.name+'" has been upgraded from version '+b.oldVersion+" to version "+b.newVersion+', but the storage "'+a.storeName+'" already exists.')}}),f.onerror=function(a){a.preventDefault(),d(f.error)},f.onsuccess=function(){var b=f.result;b.onversionchange=function(a){a.target.close()},c(b),p(a)}})}function s(a){return r(a,!1)}function t(a){return r(a,!0)}function u(a,b){if(!a.db)return!0;var c=!a.db.objectStoreNames.contains(a.storeName),d=a.versiona.db.version;if(d&&(a.version!==b&&console.warn('The database "'+a.name+"\" can't be downgraded from version "+a.db.version+" to version "+a.version+"."),a.version=a.db.version),e||c){if(c){var f=a.db.version+1;f>a.version&&(a.version=f)}return!0}return!1}function v(a){return new ya(function(b,c){var d=new FileReader;d.onerror=c,d.onloadend=function(c){var d=btoa(c.target.result||"");b({__local_forage_encoded_blob:!0,data:d,type:a.type})},d.readAsBinaryString(a)})}function w(a){return g([l(atob(a.data))],{type:a.type})}function x(a){return a&&a.__local_forage_encoded_blob}function y(a){var b=this,c=b._initReady().then(function(){var a=Ba[b._dbInfo.name];if(a&&a.dbReady)return a.dbReady});return i(c,a,a),c}function z(a){o(a);for(var b=Ba[a.name],c=b.forages,d=0;d0&&(!a.db||"InvalidStateError"===e.name||"NotFoundError"===e.name))return ya.resolve().then(function(){if(!a.db||"NotFoundError"===e.name&&!a.db.objectStoreNames.contains(a.storeName)&&a.version<=a.db.version)return a.db&&(a.version=a.db.version+1),t(a)}).then(function(){return z(a).then(function(){A(a,b,c,d-1)})}).catch(c);c(e)}}function B(){return{forages:[],db:null,dbReady:null,deferredOperations:[]}}function C(a){function b(){return ya.resolve()}var c=this,d={db:null};if(a)for(var e in a)d[e]=a[e];var f=Ba[d.name];f||(f=B(),Ba[d.name]=f),f.forages.push(c),c._initReady||(c._initReady=c.ready,c.ready=y);for(var g=[],h=0;h=i.length)break;n=i[m++]}else{if(m=i.next(),m.done)break;n=m.value}var o=n,p=o[0],b=o[1],q=k.put(b,p);q.onerror=function(){h(q.err)}}var r=k.put(d,g);r.onerror=function(){h(r.err)}},l.onerror=function(){h(l.err)},j.oncomplete=function(){b()},j.onabort=j.onerror=function(){h(j.error)}}catch(a){h(a)}})}).catch(h)})}function K(a){var b=this,c=new ya(function(a,c){b.ready().then(function(){A(b._dbInfo,Ea,function(d,e){if(d)return c(d);try{var f=e.objectStore(b._dbInfo.storeName),g=f.clear();e.oncomplete=function(){a()},e.onabort=e.onerror=function(){var a=g.error?g.error:g.transaction.error;c(a)}}catch(a){c(a)}})}).catch(c)});return h(c,a),c}function L(a){var b=this,c=new ya(function(a,c){b.ready().then(function(){A(b._dbInfo,Da,function(d,e){if(d)return c(d);try{var f=e.objectStore(b._dbInfo.storeName),g=f.count();g.onsuccess=function(){a(g.result)},g.onerror=function(){c(g.error)}}catch(a){c(a)}})}).catch(c)});return h(c,a),c}function M(a,b){var c=this,d=new ya(function(b,d){if(a<0)return void b(null);c.ready().then(function(){A(c._dbInfo,Da,function(e,f){if(e)return d(e);try{var g=f.objectStore(c._dbInfo.storeName),h=!1,i=g.openKeyCursor();i.onsuccess=function(){var c=i.result;if(!c)return void b(null);0===a?b(c.key):h?b(c.key):(h=!0,c.advance(a))},i.onerror=function(){d(i.error)}}catch(a){d(a)}})}).catch(d)});return h(d,b),d}function N(a){var b=this,c=new ya(function(a,c){b.ready().then(function(){A(b._dbInfo,Da,function(d,e){if(d)return c(d);try{var f=e.objectStore(b._dbInfo.storeName),g=f.openKeyCursor(),h=[];g.onsuccess=function(){var b=g.result;if(!b)return void a(h);h.push(b.key),b.continue()},g.onerror=function(){c(g.error)}}catch(a){c(a)}})}).catch(c)});return h(c,a),c}function O(a,b){b=k.apply(this,arguments);var c=this.config();a="function"!=typeof a&&a||{},a.name||(a.name=a.name||c.name,a.storeName=a.storeName||c.storeName);var d,e=this;if(a.name){var f=a.name===c.name&&e._dbInfo.db,g=f?ya.resolve(e._dbInfo.db):s(a).then(function(b){var c=Ba[a.name],d=c.forages;c.db=b;for(var e=0;e>4,k[i++]=(15&d)<<4|e>>2,k[i++]=(3&e)<<6|63&f;return j}function R(a){var b,c=new Uint8Array(a),d="";for(b=0;b>2],d+=Ga[(3&c[b])<<4|c[b+1]>>4],d+=Ga[(15&c[b+1])<<2|c[b+2]>>6],d+=Ga[63&c[b+2]];return c.length%3==2?d=d.substring(0,d.length-1)+"=":c.length%3==1&&(d=d.substring(0,d.length-2)+"=="),d}function S(a,b){var c="";if(a&&(c=Xa.call(a)),a&&("[object ArrayBuffer]"===c||a.buffer&&"[object ArrayBuffer]"===Xa.call(a.buffer))){var d,e=Ja;a instanceof ArrayBuffer?(d=a,e+=La):(d=a.buffer,"[object Int8Array]"===c?e+=Na:"[object Uint8Array]"===c?e+=Oa:"[object Uint8ClampedArray]"===c?e+=Pa:"[object Int16Array]"===c?e+=Qa:"[object Uint16Array]"===c?e+=Sa:"[object Int32Array]"===c?e+=Ra:"[object Uint32Array]"===c?e+=Ta:"[object Float32Array]"===c?e+=Ua:"[object Float64Array]"===c?e+=Va:b(new Error("Failed to get type for BinaryArray"))),b(e+R(d))}else if("[object Blob]"===c){var f=new FileReader;f.onload=function(){var c=Ha+a.type+"~"+R(this.result);b(Ja+Ma+c)},f.readAsArrayBuffer(a)}else try{b(JSON.stringify(a))}catch(c){console.error("Couldn't convert value into a JSON string: ",a),b(null,c)}}function T(a){if(a.substring(0,Ka)!==Ja)return JSON.parse(a);var b,c=a.substring(Wa),d=a.substring(Ka,Wa);if(d===Ma&&Ia.test(c)){var e=c.match(Ia);b=e[1],c=c.substring(e[0].length)}var f=Q(c);switch(d){case La:return f;case Ma:return g([f],{type:b});case Na:return new Int8Array(f);case Oa:return new Uint8Array(f);case Pa:return new Uint8ClampedArray(f);case Qa:return new Int16Array(f);case Sa:return new Uint16Array(f);case Ra:return new Int32Array(f);case Ta:return new Uint32Array(f);case Ua:return new Float32Array(f);case Va:return new Float64Array(f);default:throw new Error("Unkown type: "+d)}}function U(a,b,c,d){a.executeSql("CREATE TABLE IF NOT EXISTS "+b.storeName+" (id INTEGER PRIMARY KEY, key unique, value)",[],c,d)}function V(a){var b=this,c={db:null};if(a)for(var d in a)c[d]="string"!=typeof a[d]?a[d].toString():a[d];var e=new ya(function(a,d){try{c.db=openDatabase(c.name,String(c.version),c.description,c.size)}catch(a){return d(a)}c.db.transaction(function(e){U(e,c,function(){b._dbInfo=c,a()},function(a,b){d(b)})},d)});return c.serializer=Ya,e}function W(a,b,c,d,e,f){a.executeSql(c,d,e,function(a,g){g.code===g.SYNTAX_ERR?a.executeSql("SELECT name FROM sqlite_master WHERE type='table' AND name = ?",[b.storeName],function(a,h){h.rows.length?f(a,g):U(a,b,function(){a.executeSql(c,d,e,f)},f)},f):f(a,g)},f)}function X(a,b){var c=this;a=j(a);var d=new ya(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){W(c,e,"SELECT * FROM "+e.storeName+" WHERE key = ? LIMIT 1",[a],function(a,c){var d=c.rows.length?c.rows.item(0).value:null;d&&(d=e.serializer.deserialize(d)),b(d)},function(a,b){d(b)})})}).catch(d)});return h(d,b),d}function Y(a,b){var c=this,d=new ya(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){W(c,e,"SELECT * FROM "+e.storeName,[],function(c,d){for(var f=d.rows,g=f.length,h=0;h0)return void f(Z.apply(e,[a,h,c,d-1]));g(b)}})})}).catch(g)});return h(f,c),f}function $(a,b,c){return Z.apply(this,[a,b,c,1])}function _(a,b){var c=this;a=j(a);var d=new ya(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){W(c,e,"DELETE FROM "+e.storeName+" WHERE key = ?",[a],function(){b()},function(a,b){d(b)})})}).catch(d)});return h(d,b),d}function aa(a){var b=this,c=new ya(function(a,c){b.ready().then(function(){var d=b._dbInfo;d.db.transaction(function(b){W(b,d,"DELETE FROM "+d.storeName,[],function(){a()},function(a,b){c(b)})})}).catch(c)});return h(c,a),c}function ba(a){var b=this,c=new ya(function(a,c){b.ready().then(function(){var d=b._dbInfo;d.db.transaction(function(b){W(b,d,"SELECT COUNT(key) as c FROM "+d.storeName,[],function(b,c){var d=c.rows.item(0).c;a(d)},function(a,b){c(b)})})}).catch(c)});return h(c,a),c}function ca(a,b){var c=this,d=new ya(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){W(c,e,"SELECT key FROM "+e.storeName+" WHERE id = ? LIMIT 1",[a+1],function(a,c){var d=c.rows.length?c.rows.item(0).key:null;b(d)},function(a,b){d(b)})})}).catch(d)});return h(d,b),d}function da(a){var b=this,c=new ya(function(a,c){b.ready().then(function(){var d=b._dbInfo;d.db.transaction(function(b){W(b,d,"SELECT key FROM "+d.storeName,[],function(b,c){for(var d=[],e=0;e '__WebKitDatabaseInfoTable__'",[],function(c,d){for(var e=[],f=0;f0}function ka(a){var b=this,c={};if(a)for(var d in a)c[d]=a[d];return c.keyPrefix=ha(a,b._defaultConfig),ja()?(b._dbInfo=c,c.serializer=Ya,ya.resolve()):ya.reject()}function la(a){var b=this,c=b.ready().then(function(){for(var a=b._dbInfo.keyPrefix,c=localStorage.length-1;c>=0;c--){var d=localStorage.key(c);0===d.indexOf(a)&&localStorage.removeItem(d)}});return h(c,a),c}function ma(a,b){var c=this;a=j(a);var d=c.ready().then(function(){var b=c._dbInfo,d=localStorage.getItem(b.keyPrefix+a);return d&&(d=b.serializer.deserialize(d)),d});return h(d,b),d}function na(a,b){var c=this,d=c.ready().then(function(){for(var b=c._dbInfo,d=b.keyPrefix,e=d.length,f=localStorage.length,g=1,h=0;h=0;b--){var c=localStorage.key(b);0===c.indexOf(a)&&localStorage.removeItem(c)}}):ya.reject("Invalid arguments"),h(d,b),d}function ua(a,b){a[b]=function(){var c=arguments;return a.ready().then(function(){return a[b].apply(a,c)})}}function va(){for(var a=1;a= _iterator.length) break; + _ref2 = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) break; + _ref2 = _i.value; + } + + var _ref3 = _ref2, + key = _ref3[0], + value = _ref3[1]; + + var writeRequest = store.put(value, key); + + writeRequest.onerror = function () { + reject(writeRequest.err); + }; + } + + var synchronizationValueWriteRequest = store.put(newSynchronizationValue, normalizedSynchronizationKey); + + synchronizationValueWriteRequest.onerror = function () { + reject(synchronizationValueWriteRequest.err); + }; + }; + + req.onerror = function () { + reject(req.err); + }; + + transaction.oncomplete = function () { + resolve(); + }; + + transaction.onabort = transaction.onerror = function () { + reject(transaction.error); + }; + } catch (e) { + reject(e); + } + }); + })["catch"](reject); + }); + + return promise; +} + function clear(callback) { var self = this; -@@ -1101,6 +1267,8 @@ var asyncStorage = { +@@ -1101,6 +1272,9 @@ var asyncStorage = { getItem: getItem, setItem: setItem, removeItem: removeItem, + getMultipleItems: getMultipleItems, + setMultipleItems: setMultipleItems, ++ getSetMultipleItemsRaceConditionErrorMessage: getSetMultipleItemsRaceConditionErrorMessage, clear: clear, length: length, key: key, -@@ -1764,6 +1932,12 @@ var webSQLStorage = { +@@ -1764,6 +1938,15 @@ var webSQLStorage = { getItem: getItem$1, setItem: setItem$1, removeItem: removeItem$1, + getMultipleItems: function getMultipleItems() { + throw "Method unsupported for driver."; + }, + setMultipleItems: function setMultipleItems() { + throw "Method unsupported for driver."; ++ }, ++ getSetMultipleItemsRaceConditionErrorMessage: function getSetMultipleItemsRaceConditionErrorMessage() { ++ throw "Method unsupported for driver."; + }, clear: clear$1, length: length$1, key: key$1, -@@ -2086,6 +2260,12 @@ var localStorageWrapper = { +@@ -2086,6 +2269,15 @@ var localStorageWrapper = { getItem: getItem$2, setItem: setItem$2, removeItem: removeItem$2, + getMultipleItems: function getMultipleItems() { + throw "Method unsupported for driver."; + }, + setMultipleItems: function setMultipleItems() { + throw "Method unsupported for driver."; ++ }, ++ getSetMultipleItemsRaceConditionErrorMessage: function getSetMultipleItemsRaceConditionErrorMessage() { ++ throw "Method unsupported for driver."; + }, clear: clear$2, length: length$2, key: key$2, -@@ -2130,7 +2310,7 @@ var DefaultDriverOrder = [DefaultDrivers.INDEXEDDB._driver, DefaultDrivers.WEBSQ +@@ -2130,7 +2322,7 @@ var DefaultDriverOrder = [DefaultDrivers.INDEXEDDB._driver, DefaultDrivers.WEBSQ var OptionalDriverMethods = ['dropInstance']; -var LibraryMethods = ['clear', 'getItem', 'iterate', 'key', 'keys', 'length', 'removeItem', 'setItem'].concat(OptionalDriverMethods); -+var LibraryMethods = ['clear', 'getItem', 'iterate', 'key', 'keys', 'length', 'removeItem', 'setItem', 'setMultipleItems', 'getMultipleItems'].concat(OptionalDriverMethods); ++var LibraryMethods = ['clear', 'getItem', 'iterate', 'key', 'keys', 'length', 'removeItem', 'setItem', 'setMultipleItems', 'getSetMultipleItemsRaceConditionErrorMessage', 'getMultipleItems'].concat(OptionalDriverMethods); var DefaultConfig = { description: '', -@@ -2285,8 +2465,8 @@ var LocalForage = function () { +@@ -2285,8 +2477,8 @@ var LocalForage = function () { }; }; - for (var _i = 0, _len = OptionalDriverMethods.length; _i < _len; _i++) { - var optionalDriverMethod = OptionalDriverMethods[_i]; + for (var _i2 = 0, _len = OptionalDriverMethods.length; _i2 < _len; _i2++) { + var optionalDriverMethod = OptionalDriverMethods[_i2]; if (!driverObject[optionalDriverMethod]) { driverObject[optionalDriverMethod] = methodNotImplementedFactory(optionalDriverMethod); } diff --git a/node_modules/localforage/dist/localforage.nopromises.min.js b/node_modules/localforage/dist/localforage.nopromises.min.js -index a4d5d02..f4ca5f0 100644 +index a4d5d02..90e9e78 100644 --- a/node_modules/localforage/dist/localforage.nopromises.min.js +++ b/node_modules/localforage/dist/localforage.nopromises.min.js @@ -4,4 +4,4 @@ https://localforage.github.io/localForage (c) 2013-2017 Mozilla, Apache License 2.0 */ -!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.localforage=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c||a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g=43)}}).catch(function(){return!1})}function n(a){return"boolean"==typeof xa?va.resolve(xa):m(a).then(function(a){return xa=a})}function o(a){var b=ya[a.name],c={};c.promise=new va(function(a,b){c.resolve=a,c.reject=b}),b.deferredOperations.push(c),b.dbReady?b.dbReady=b.dbReady.then(function(){return c.promise}):b.dbReady=c.promise}function p(a){var b=ya[a.name],c=b.deferredOperations.pop();if(c)return c.resolve(),c.promise}function q(a,b){var c=ya[a.name],d=c.deferredOperations.pop();if(d)return d.reject(b),d.promise}function r(a,b){return new va(function(c,d){if(ya[a.name]=ya[a.name]||B(),a.db){if(!b)return c(a.db);o(a),a.db.close()}var e=[a.name];b&&e.push(a.version);var f=ua.open.apply(ua,e);b&&(f.onupgradeneeded=function(b){var c=f.result;try{c.createObjectStore(a.storeName),b.oldVersion<=1&&c.createObjectStore(wa)}catch(c){if("ConstraintError"!==c.name)throw c;console.warn('The database "'+a.name+'" has been upgraded from version '+b.oldVersion+" to version "+b.newVersion+', but the storage "'+a.storeName+'" already exists.')}}),f.onerror=function(a){a.preventDefault(),d(f.error)},f.onsuccess=function(){var b=f.result;b.onversionchange=function(a){a.target.close()},c(b),p(a)}})}function s(a){return r(a,!1)}function t(a){return r(a,!0)}function u(a,b){if(!a.db)return!0;var c=!a.db.objectStoreNames.contains(a.storeName),d=a.versiona.db.version;if(d&&(a.version!==b&&console.warn('The database "'+a.name+"\" can't be downgraded from version "+a.db.version+" to version "+a.version+"."),a.version=a.db.version),e||c){if(c){var f=a.db.version+1;f>a.version&&(a.version=f)}return!0}return!1}function v(a){return new va(function(b,c){var d=new FileReader;d.onerror=c,d.onloadend=function(c){var d=btoa(c.target.result||"");b({__local_forage_encoded_blob:!0,data:d,type:a.type})},d.readAsBinaryString(a)})}function w(a){return g([l(atob(a.data))],{type:a.type})}function x(a){return a&&a.__local_forage_encoded_blob}function y(a){var b=this,c=b._initReady().then(function(){var a=ya[b._dbInfo.name];if(a&&a.dbReady)return a.dbReady});return i(c,a,a),c}function z(a){o(a);for(var b=ya[a.name],c=b.forages,d=0;d0&&(!a.db||"InvalidStateError"===e.name||"NotFoundError"===e.name))return va.resolve().then(function(){if(!a.db||"NotFoundError"===e.name&&!a.db.objectStoreNames.contains(a.storeName)&&a.version<=a.db.version)return a.db&&(a.version=a.db.version+1),t(a)}).then(function(){return z(a).then(function(){A(a,b,c,d-1)})}).catch(c);c(e)}}function B(){return{forages:[],db:null,dbReady:null,deferredOperations:[]}}function C(a){function b(){return va.resolve()}var c=this,d={db:null};if(a)for(var e in a)d[e]=a[e];var f=ya[d.name];f||(f=B(),ya[d.name]=f),f.forages.push(c),c._initReady||(c._initReady=c.ready,c.ready=y);for(var g=[],h=0;h>4,k[i++]=(15&d)<<4|e>>2,k[i++]=(3&e)<<6|63&f;return j}function O(a){var b,c=new Uint8Array(a),d="";for(b=0;b>2],d+=Da[(3&c[b])<<4|c[b+1]>>4],d+=Da[(15&c[b+1])<<2|c[b+2]>>6],d+=Da[63&c[b+2]];return c.length%3==2?d=d.substring(0,d.length-1)+"=":c.length%3==1&&(d=d.substring(0,d.length-2)+"=="),d}function P(a,b){var c="";if(a&&(c=Ua.call(a)),a&&("[object ArrayBuffer]"===c||a.buffer&&"[object ArrayBuffer]"===Ua.call(a.buffer))){var d,e=Ga;a instanceof ArrayBuffer?(d=a,e+=Ia):(d=a.buffer,"[object Int8Array]"===c?e+=Ka:"[object Uint8Array]"===c?e+=La:"[object Uint8ClampedArray]"===c?e+=Ma:"[object Int16Array]"===c?e+=Na:"[object Uint16Array]"===c?e+=Pa:"[object Int32Array]"===c?e+=Oa:"[object Uint32Array]"===c?e+=Qa:"[object Float32Array]"===c?e+=Ra:"[object Float64Array]"===c?e+=Sa:b(new Error("Failed to get type for BinaryArray"))),b(e+O(d))}else if("[object Blob]"===c){var f=new FileReader;f.onload=function(){var c=Ea+a.type+"~"+O(this.result);b(Ga+Ja+c)},f.readAsArrayBuffer(a)}else try{b(JSON.stringify(a))}catch(c){console.error("Couldn't convert value into a JSON string: ",a),b(null,c)}}function Q(a){if(a.substring(0,Ha)!==Ga)return JSON.parse(a);var b,c=a.substring(Ta),d=a.substring(Ha,Ta);if(d===Ja&&Fa.test(c)){var e=c.match(Fa);b=e[1],c=c.substring(e[0].length)}var f=N(c);switch(d){case Ia:return f;case Ja:return g([f],{type:b});case Ka:return new Int8Array(f);case La:return new Uint8Array(f);case Ma:return new Uint8ClampedArray(f);case Na:return new Int16Array(f);case Pa:return new Uint16Array(f);case Oa:return new Int32Array(f);case Qa:return new Uint32Array(f);case Ra:return new Float32Array(f);case Sa:return new Float64Array(f);default:throw new Error("Unkown type: "+d)}}function R(a,b,c,d){a.executeSql("CREATE TABLE IF NOT EXISTS "+b.storeName+" (id INTEGER PRIMARY KEY, key unique, value)",[],c,d)}function S(a){var b=this,c={db:null};if(a)for(var d in a)c[d]="string"!=typeof a[d]?a[d].toString():a[d];var e=new va(function(a,d){try{c.db=openDatabase(c.name,String(c.version),c.description,c.size)}catch(a){return d(a)}c.db.transaction(function(e){R(e,c,function(){b._dbInfo=c,a()},function(a,b){d(b)})},d)});return c.serializer=Va,e}function T(a,b,c,d,e,f){a.executeSql(c,d,e,function(a,g){g.code===g.SYNTAX_ERR?a.executeSql("SELECT name FROM sqlite_master WHERE type='table' AND name = ?",[b.storeName],function(a,h){h.rows.length?f(a,g):R(a,b,function(){a.executeSql(c,d,e,f)},f)},f):f(a,g)},f)}function U(a,b){var c=this;a=j(a);var d=new va(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){T(c,e,"SELECT * FROM "+e.storeName+" WHERE key = ? LIMIT 1",[a],function(a,c){var d=c.rows.length?c.rows.item(0).value:null;d&&(d=e.serializer.deserialize(d)),b(d)},function(a,b){d(b)})})}).catch(d)});return h(d,b),d}function V(a,b){var c=this,d=new va(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){T(c,e,"SELECT * FROM "+e.storeName,[],function(c,d){for(var f=d.rows,g=f.length,h=0;h0)return void f(W.apply(e,[a,h,c,d-1]));g(b)}})})}).catch(g)});return h(f,c),f}function X(a,b,c){return W.apply(this,[a,b,c,1])}function Y(a,b){var c=this;a=j(a);var d=new va(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){T(c,e,"DELETE FROM "+e.storeName+" WHERE key = ?",[a],function(){b()},function(a,b){d(b)})})}).catch(d)});return h(d,b),d}function Z(a){var b=this,c=new va(function(a,c){b.ready().then(function(){var d=b._dbInfo;d.db.transaction(function(b){T(b,d,"DELETE FROM "+d.storeName,[],function(){a()},function(a,b){c(b)})})}).catch(c)});return h(c,a),c}function $(a){var b=this,c=new va(function(a,c){b.ready().then(function(){var d=b._dbInfo;d.db.transaction(function(b){T(b,d,"SELECT COUNT(key) as c FROM "+d.storeName,[],function(b,c){var d=c.rows.item(0).c;a(d)},function(a,b){c(b)})})}).catch(c)});return h(c,a),c}function _(a,b){var c=this,d=new va(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){T(c,e,"SELECT key FROM "+e.storeName+" WHERE id = ? LIMIT 1",[a+1],function(a,c){var d=c.rows.length?c.rows.item(0).key:null;b(d)},function(a,b){d(b)})})}).catch(d)});return h(d,b),d}function aa(a){var b=this,c=new va(function(a,c){b.ready().then(function(){var d=b._dbInfo;d.db.transaction(function(b){T(b,d,"SELECT key FROM "+d.storeName,[],function(b,c){for(var d=[],e=0;e '__WebKitDatabaseInfoTable__'",[],function(c,d){for(var e=[],f=0;f0}function ha(a){var b=this,c={};if(a)for(var d in a)c[d]=a[d];return c.keyPrefix=ea(a,b._defaultConfig),ga()?(b._dbInfo=c,c.serializer=Va,va.resolve()):va.reject()}function ia(a){var b=this,c=b.ready().then(function(){for(var a=b._dbInfo.keyPrefix,c=localStorage.length-1;c>=0;c--){var d=localStorage.key(c);0===d.indexOf(a)&&localStorage.removeItem(d)}});return h(c,a),c}function ja(a,b){var c=this;a=j(a);var d=c.ready().then(function(){var b=c._dbInfo,d=localStorage.getItem(b.keyPrefix+a);return d&&(d=b.serializer.deserialize(d)),d});return h(d,b),d}function ka(a,b){var c=this,d=c.ready().then(function(){for(var b=c._dbInfo,d=b.keyPrefix,e=d.length,f=localStorage.length,g=1,h=0;h=0;b--){var c=localStorage.key(b);0===c.indexOf(a)&&localStorage.removeItem(c)}}):va.reject("Invalid arguments"),h(d,b),d}function ra(a,b){a[b]=function(){var c=arguments;return a.ready().then(function(){return a[b].apply(a,c)})}}function sa(){for(var a=1;a=43)}}).catch(function(){return!1})}function n(a){return"boolean"==typeof za?xa.resolve(za):m(a).then(function(a){return za=a})}function o(a){var b=Aa[a.name],c={};c.promise=new xa(function(a,b){c.resolve=a,c.reject=b}),b.deferredOperations.push(c),b.dbReady?b.dbReady=b.dbReady.then(function(){return c.promise}):b.dbReady=c.promise}function p(a){var b=Aa[a.name],c=b.deferredOperations.pop();if(c)return c.resolve(),c.promise}function q(a,b){var c=Aa[a.name],d=c.deferredOperations.pop();if(d)return d.reject(b),d.promise}function r(a,b){return new xa(function(c,d){if(Aa[a.name]=Aa[a.name]||B(),a.db){if(!b)return c(a.db);o(a),a.db.close()}var e=[a.name];b&&e.push(a.version);var f=wa.open.apply(wa,e);b&&(f.onupgradeneeded=function(b){var c=f.result;try{c.createObjectStore(a.storeName),b.oldVersion<=1&&c.createObjectStore(ya)}catch(c){if("ConstraintError"!==c.name)throw c;console.warn('The database "'+a.name+'" has been upgraded from version '+b.oldVersion+" to version "+b.newVersion+', but the storage "'+a.storeName+'" already exists.')}}),f.onerror=function(a){a.preventDefault(),d(f.error)},f.onsuccess=function(){var b=f.result;b.onversionchange=function(a){a.target.close()},c(b),p(a)}})}function s(a){return r(a,!1)}function t(a){return r(a,!0)}function u(a,b){if(!a.db)return!0;var c=!a.db.objectStoreNames.contains(a.storeName),d=a.versiona.db.version;if(d&&(a.version!==b&&console.warn('The database "'+a.name+"\" can't be downgraded from version "+a.db.version+" to version "+a.version+"."),a.version=a.db.version),e||c){if(c){var f=a.db.version+1;f>a.version&&(a.version=f)}return!0}return!1}function v(a){return new xa(function(b,c){var d=new FileReader;d.onerror=c,d.onloadend=function(c){var d=btoa(c.target.result||"");b({__local_forage_encoded_blob:!0,data:d,type:a.type})},d.readAsBinaryString(a)})}function w(a){return g([l(atob(a.data))],{type:a.type})}function x(a){return a&&a.__local_forage_encoded_blob}function y(a){var b=this,c=b._initReady().then(function(){var a=Aa[b._dbInfo.name];if(a&&a.dbReady)return a.dbReady});return i(c,a,a),c}function z(a){o(a);for(var b=Aa[a.name],c=b.forages,d=0;d0&&(!a.db||"InvalidStateError"===e.name||"NotFoundError"===e.name))return xa.resolve().then(function(){if(!a.db||"NotFoundError"===e.name&&!a.db.objectStoreNames.contains(a.storeName)&&a.version<=a.db.version)return a.db&&(a.version=a.db.version+1),t(a)}).then(function(){return z(a).then(function(){A(a,b,c,d-1)})}).catch(c);c(e)}}function B(){return{forages:[],db:null,dbReady:null,deferredOperations:[]}}function C(a){function b(){return xa.resolve()}var c=this,d={db:null};if(a)for(var e in a)d[e]=a[e];var f=Aa[d.name];f||(f=B(),Aa[d.name]=f),f.forages.push(c),c._initReady||(c._initReady=c.ready,c.ready=y);for(var g=[],h=0;h=f.length)break;m=f[j++]}else{if(j=f.next(),j.done)break;m=j.value}var n=m,o=n[0],b=n[1],p=k.put(b,o);p.onerror=function(){h(p.err)}}var q=k.put(d,g);q.onerror=function(){h(q.err)}},l.onerror=function(){h(l.err)},j.oncomplete=function(){b()},j.onabort=j.onerror=function(){h(j.error)}}catch(a){h(a)}})}).catch(h)})}function J(a){var b=this,c=new xa(function(a,c){b.ready().then(function(){A(b._dbInfo,Da,function(d,e){if(d)return c(d);try{var f=e.objectStore(b._dbInfo.storeName),g=f.clear();e.oncomplete=function(){a()},e.onabort=e.onerror=function(){var a=g.error?g.error:g.transaction.error;c(a)}}catch(a){c(a)}})}).catch(c)});return h(c,a),c}function K(a){var b=this,c=new xa(function(a,c){b.ready().then(function(){A(b._dbInfo,Ca,function(d,e){if(d)return c(d);try{var f=e.objectStore(b._dbInfo.storeName),g=f.count();g.onsuccess=function(){a(g.result)},g.onerror=function(){c(g.error)}}catch(a){c(a)}})}).catch(c)});return h(c,a),c}function L(a,b){var c=this,d=new xa(function(b,d){if(a<0)return void b(null);c.ready().then(function(){A(c._dbInfo,Ca,function(e,f){if(e)return d(e);try{var g=f.objectStore(c._dbInfo.storeName),h=!1,i=g.openKeyCursor();i.onsuccess=function(){var c=i.result;if(!c)return void b(null);0===a?b(c.key):h?b(c.key):(h=!0,c.advance(a))},i.onerror=function(){d(i.error)}}catch(a){d(a)}})}).catch(d)});return h(d,b),d}function M(a){var b=this,c=new xa(function(a,c){b.ready().then(function(){A(b._dbInfo,Ca,function(d,e){if(d)return c(d);try{var f=e.objectStore(b._dbInfo.storeName),g=f.openKeyCursor(),h=[];g.onsuccess=function(){var b=g.result;if(!b)return void a(h);h.push(b.key),b.continue()},g.onerror=function(){c(g.error)}}catch(a){c(a)}})}).catch(c)});return h(c,a),c}function N(a,b){b=k.apply(this,arguments);var c=this.config();a="function"!=typeof a&&a||{},a.name||(a.name=a.name||c.name,a.storeName=a.storeName||c.storeName);var d,e=this;if(a.name){var f=a.name===c.name&&e._dbInfo.db,g=f?xa.resolve(e._dbInfo.db):s(a).then(function(b){var c=Aa[a.name],d=c.forages;c.db=b;for(var e=0;e>4,k[i++]=(15&d)<<4|e>>2,k[i++]=(3&e)<<6|63&f;return j}function Q(a){var b,c=new Uint8Array(a),d="";for(b=0;b>2],d+=Fa[(3&c[b])<<4|c[b+1]>>4],d+=Fa[(15&c[b+1])<<2|c[b+2]>>6],d+=Fa[63&c[b+2]];return c.length%3==2?d=d.substring(0,d.length-1)+"=":c.length%3==1&&(d=d.substring(0,d.length-2)+"=="),d}function R(a,b){var c="";if(a&&(c=Wa.call(a)),a&&("[object ArrayBuffer]"===c||a.buffer&&"[object ArrayBuffer]"===Wa.call(a.buffer))){var d,e=Ia;a instanceof ArrayBuffer?(d=a,e+=Ka):(d=a.buffer,"[object Int8Array]"===c?e+=Ma:"[object Uint8Array]"===c?e+=Na:"[object Uint8ClampedArray]"===c?e+=Oa:"[object Int16Array]"===c?e+=Pa:"[object Uint16Array]"===c?e+=Ra:"[object Int32Array]"===c?e+=Qa:"[object Uint32Array]"===c?e+=Sa:"[object Float32Array]"===c?e+=Ta:"[object Float64Array]"===c?e+=Ua:b(new Error("Failed to get type for BinaryArray"))),b(e+Q(d))}else if("[object Blob]"===c){var f=new FileReader;f.onload=function(){var c=Ga+a.type+"~"+Q(this.result);b(Ia+La+c)},f.readAsArrayBuffer(a)}else try{b(JSON.stringify(a))}catch(c){console.error("Couldn't convert value into a JSON string: ",a),b(null,c)}}function S(a){if(a.substring(0,Ja)!==Ia)return JSON.parse(a);var b,c=a.substring(Va),d=a.substring(Ja,Va);if(d===La&&Ha.test(c)){var e=c.match(Ha);b=e[1],c=c.substring(e[0].length)}var f=P(c);switch(d){case Ka:return f;case La:return g([f],{type:b});case Ma:return new Int8Array(f);case Na:return new Uint8Array(f);case Oa:return new Uint8ClampedArray(f);case Pa:return new Int16Array(f);case Ra:return new Uint16Array(f);case Qa:return new Int32Array(f);case Sa:return new Uint32Array(f);case Ta:return new Float32Array(f);case Ua:return new Float64Array(f);default:throw new Error("Unkown type: "+d)}}function T(a,b,c,d){a.executeSql("CREATE TABLE IF NOT EXISTS "+b.storeName+" (id INTEGER PRIMARY KEY, key unique, value)",[],c,d)}function U(a){var b=this,c={db:null};if(a)for(var d in a)c[d]="string"!=typeof a[d]?a[d].toString():a[d];var e=new xa(function(a,d){try{c.db=openDatabase(c.name,String(c.version),c.description,c.size)}catch(a){return d(a)}c.db.transaction(function(e){T(e,c,function(){b._dbInfo=c,a()},function(a,b){d(b)})},d)});return c.serializer=Xa,e}function V(a,b,c,d,e,f){a.executeSql(c,d,e,function(a,g){g.code===g.SYNTAX_ERR?a.executeSql("SELECT name FROM sqlite_master WHERE type='table' AND name = ?",[b.storeName],function(a,h){h.rows.length?f(a,g):T(a,b,function(){a.executeSql(c,d,e,f)},f)},f):f(a,g)},f)}function W(a,b){var c=this;a=j(a);var d=new xa(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){V(c,e,"SELECT * FROM "+e.storeName+" WHERE key = ? LIMIT 1",[a],function(a,c){var d=c.rows.length?c.rows.item(0).value:null;d&&(d=e.serializer.deserialize(d)),b(d)},function(a,b){d(b)})})}).catch(d)});return h(d,b),d}function X(a,b){var c=this,d=new xa(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){V(c,e,"SELECT * FROM "+e.storeName,[],function(c,d){for(var f=d.rows,g=f.length,h=0;h0)return void f(Y.apply(e,[a,h,c,d-1]));g(b)}})})}).catch(g)});return h(f,c),f}function Z(a,b,c){return Y.apply(this,[a,b,c,1])}function $(a,b){var c=this;a=j(a);var d=new xa(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){V(c,e,"DELETE FROM "+e.storeName+" WHERE key = ?",[a],function(){b()},function(a,b){d(b)})})}).catch(d)});return h(d,b),d}function _(a){var b=this,c=new xa(function(a,c){b.ready().then(function(){var d=b._dbInfo;d.db.transaction(function(b){V(b,d,"DELETE FROM "+d.storeName,[],function(){a()},function(a,b){c(b)})})}).catch(c)});return h(c,a),c}function aa(a){var b=this,c=new xa(function(a,c){b.ready().then(function(){var d=b._dbInfo;d.db.transaction(function(b){V(b,d,"SELECT COUNT(key) as c FROM "+d.storeName,[],function(b,c){var d=c.rows.item(0).c;a(d)},function(a,b){c(b)})})}).catch(c)});return h(c,a),c}function ba(a,b){var c=this,d=new xa(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){V(c,e,"SELECT key FROM "+e.storeName+" WHERE id = ? LIMIT 1",[a+1],function(a,c){var d=c.rows.length?c.rows.item(0).key:null;b(d)},function(a,b){d(b)})})}).catch(d)});return h(d,b),d}function ca(a){var b=this,c=new xa(function(a,c){b.ready().then(function(){var d=b._dbInfo;d.db.transaction(function(b){V(b,d,"SELECT key FROM "+d.storeName,[],function(b,c){for(var d=[],e=0;e '__WebKitDatabaseInfoTable__'",[],function(c,d){for(var e=[],f=0;f0}function ja(a){var b=this,c={};if(a)for(var d in a)c[d]=a[d];return c.keyPrefix=ga(a,b._defaultConfig),ia()?(b._dbInfo=c,c.serializer=Xa,xa.resolve()):xa.reject()}function ka(a){var b=this,c=b.ready().then(function(){for(var a=b._dbInfo.keyPrefix,c=localStorage.length-1;c>=0;c--){var d=localStorage.key(c);0===d.indexOf(a)&&localStorage.removeItem(d)}});return h(c,a),c}function la(a,b){var c=this;a=j(a);var d=c.ready().then(function(){var b=c._dbInfo,d=localStorage.getItem(b.keyPrefix+a);return d&&(d=b.serializer.deserialize(d)),d});return h(d,b),d}function ma(a,b){var c=this,d=c.ready().then(function(){for(var b=c._dbInfo,d=b.keyPrefix,e=d.length,f=localStorage.length,g=1,h=0;h=0;b--){var c=localStorage.key(b);0===c.indexOf(a)&&localStorage.removeItem(c)}}):xa.reject("Invalid arguments"),h(d,b),d}function ta(a,b){a[b]=function(){var c=arguments;return a.ready().then(function(){return a[b].apply(a,c)})}}function ua(){for(var a=1;a=43)}}).catch(function(){return!1})}function n(a){return"boolean"==typeof Aa?ya.resolve(Aa):m(a).then(function(a){return Aa=a})}function o(a){var b=Ba[a.name],c={};c.promise=new ya(function(a,b){c.resolve=a,c.reject=b}),b.deferredOperations.push(c),b.dbReady?b.dbReady=b.dbReady.then(function(){return c.promise}):b.dbReady=c.promise}function p(a){var b=Ba[a.name],c=b.deferredOperations.pop();if(c)return c.resolve(),c.promise}function q(a,b){var c=Ba[a.name],d=c.deferredOperations.pop();if(d)return d.reject(b),d.promise}function r(a,b){return new ya(function(c,d){if(Ba[a.name]=Ba[a.name]||B(),a.db){if(!b)return c(a.db);o(a),a.db.close()}var e=[a.name];b&&e.push(a.version);var f=xa.open.apply(xa,e);b&&(f.onupgradeneeded=function(b){var c=f.result;try{c.createObjectStore(a.storeName),b.oldVersion<=1&&c.createObjectStore(za)}catch(c){if("ConstraintError"!==c.name)throw c;console.warn('The database "'+a.name+'" has been upgraded from version '+b.oldVersion+" to version "+b.newVersion+', but the storage "'+a.storeName+'" already exists.')}}),f.onerror=function(a){a.preventDefault(),d(f.error)},f.onsuccess=function(){var b=f.result;b.onversionchange=function(a){a.target.close()},c(b),p(a)}})}function s(a){return r(a,!1)}function t(a){return r(a,!0)}function u(a,b){if(!a.db)return!0;var c=!a.db.objectStoreNames.contains(a.storeName),d=a.versiona.db.version;if(d&&(a.version!==b&&console.warn('The database "'+a.name+"\" can't be downgraded from version "+a.db.version+" to version "+a.version+"."),a.version=a.db.version),e||c){if(c){var f=a.db.version+1;f>a.version&&(a.version=f)}return!0}return!1}function v(a){return new ya(function(b,c){var d=new FileReader;d.onerror=c,d.onloadend=function(c){var d=btoa(c.target.result||"");b({__local_forage_encoded_blob:!0,data:d,type:a.type})},d.readAsBinaryString(a)})}function w(a){return g([l(atob(a.data))],{type:a.type})}function x(a){return a&&a.__local_forage_encoded_blob}function y(a){var b=this,c=b._initReady().then(function(){var a=Ba[b._dbInfo.name];if(a&&a.dbReady)return a.dbReady});return i(c,a,a),c}function z(a){o(a);for(var b=Ba[a.name],c=b.forages,d=0;d0&&(!a.db||"InvalidStateError"===e.name||"NotFoundError"===e.name))return ya.resolve().then(function(){if(!a.db||"NotFoundError"===e.name&&!a.db.objectStoreNames.contains(a.storeName)&&a.version<=a.db.version)return a.db&&(a.version=a.db.version+1),t(a)}).then(function(){return z(a).then(function(){A(a,b,c,d-1)})}).catch(c);c(e)}}function B(){return{forages:[],db:null,dbReady:null,deferredOperations:[]}}function C(a){function b(){return ya.resolve()}var c=this,d={db:null};if(a)for(var e in a)d[e]=a[e];var f=Ba[d.name];f||(f=B(),Ba[d.name]=f),f.forages.push(c),c._initReady||(c._initReady=c.ready,c.ready=y);for(var g=[],h=0;h=i.length)break;n=i[m++]}else{if(m=i.next(),m.done)break;n=m.value}var o=n,p=o[0],b=o[1],q=k.put(b,p);q.onerror=function(){h(q.err)}}var r=k.put(d,g);r.onerror=function(){h(r.err)}},l.onerror=function(){h(l.err)},j.oncomplete=function(){b()},j.onabort=j.onerror=function(){h(j.error)}}catch(a){h(a)}})}).catch(h)})}function K(a){var b=this,c=new ya(function(a,c){b.ready().then(function(){A(b._dbInfo,Ea,function(d,e){if(d)return c(d);try{var f=e.objectStore(b._dbInfo.storeName),g=f.clear();e.oncomplete=function(){a()},e.onabort=e.onerror=function(){var a=g.error?g.error:g.transaction.error;c(a)}}catch(a){c(a)}})}).catch(c)});return h(c,a),c}function L(a){var b=this,c=new ya(function(a,c){b.ready().then(function(){A(b._dbInfo,Da,function(d,e){if(d)return c(d);try{var f=e.objectStore(b._dbInfo.storeName),g=f.count();g.onsuccess=function(){a(g.result)},g.onerror=function(){c(g.error)}}catch(a){c(a)}})}).catch(c)});return h(c,a),c}function M(a,b){var c=this,d=new ya(function(b,d){if(a<0)return void b(null);c.ready().then(function(){A(c._dbInfo,Da,function(e,f){if(e)return d(e);try{var g=f.objectStore(c._dbInfo.storeName),h=!1,i=g.openKeyCursor();i.onsuccess=function(){var c=i.result;if(!c)return void b(null);0===a?b(c.key):h?b(c.key):(h=!0,c.advance(a))},i.onerror=function(){d(i.error)}}catch(a){d(a)}})}).catch(d)});return h(d,b),d}function N(a){var b=this,c=new ya(function(a,c){b.ready().then(function(){A(b._dbInfo,Da,function(d,e){if(d)return c(d);try{var f=e.objectStore(b._dbInfo.storeName),g=f.openKeyCursor(),h=[];g.onsuccess=function(){var b=g.result;if(!b)return void a(h);h.push(b.key),b.continue()},g.onerror=function(){c(g.error)}}catch(a){c(a)}})}).catch(c)});return h(c,a),c}function O(a,b){b=k.apply(this,arguments);var c=this.config();a="function"!=typeof a&&a||{},a.name||(a.name=a.name||c.name,a.storeName=a.storeName||c.storeName);var d,e=this;if(a.name){var f=a.name===c.name&&e._dbInfo.db,g=f?ya.resolve(e._dbInfo.db):s(a).then(function(b){var c=Ba[a.name],d=c.forages;c.db=b;for(var e=0;e>4,k[i++]=(15&d)<<4|e>>2,k[i++]=(3&e)<<6|63&f;return j}function R(a){var b,c=new Uint8Array(a),d="";for(b=0;b>2],d+=Ga[(3&c[b])<<4|c[b+1]>>4],d+=Ga[(15&c[b+1])<<2|c[b+2]>>6],d+=Ga[63&c[b+2]];return c.length%3==2?d=d.substring(0,d.length-1)+"=":c.length%3==1&&(d=d.substring(0,d.length-2)+"=="),d}function S(a,b){var c="";if(a&&(c=Xa.call(a)),a&&("[object ArrayBuffer]"===c||a.buffer&&"[object ArrayBuffer]"===Xa.call(a.buffer))){var d,e=Ja;a instanceof ArrayBuffer?(d=a,e+=La):(d=a.buffer,"[object Int8Array]"===c?e+=Na:"[object Uint8Array]"===c?e+=Oa:"[object Uint8ClampedArray]"===c?e+=Pa:"[object Int16Array]"===c?e+=Qa:"[object Uint16Array]"===c?e+=Sa:"[object Int32Array]"===c?e+=Ra:"[object Uint32Array]"===c?e+=Ta:"[object Float32Array]"===c?e+=Ua:"[object Float64Array]"===c?e+=Va:b(new Error("Failed to get type for BinaryArray"))),b(e+R(d))}else if("[object Blob]"===c){var f=new FileReader;f.onload=function(){var c=Ha+a.type+"~"+R(this.result);b(Ja+Ma+c)},f.readAsArrayBuffer(a)}else try{b(JSON.stringify(a))}catch(c){console.error("Couldn't convert value into a JSON string: ",a),b(null,c)}}function T(a){if(a.substring(0,Ka)!==Ja)return JSON.parse(a);var b,c=a.substring(Wa),d=a.substring(Ka,Wa);if(d===Ma&&Ia.test(c)){var e=c.match(Ia);b=e[1],c=c.substring(e[0].length)}var f=Q(c);switch(d){case La:return f;case Ma:return g([f],{type:b});case Na:return new Int8Array(f);case Oa:return new Uint8Array(f);case Pa:return new Uint8ClampedArray(f);case Qa:return new Int16Array(f);case Sa:return new Uint16Array(f);case Ra:return new Int32Array(f);case Ta:return new Uint32Array(f);case Ua:return new Float32Array(f);case Va:return new Float64Array(f);default:throw new Error("Unkown type: "+d)}}function U(a,b,c,d){a.executeSql("CREATE TABLE IF NOT EXISTS "+b.storeName+" (id INTEGER PRIMARY KEY, key unique, value)",[],c,d)}function V(a){var b=this,c={db:null};if(a)for(var d in a)c[d]="string"!=typeof a[d]?a[d].toString():a[d];var e=new ya(function(a,d){try{c.db=openDatabase(c.name,String(c.version),c.description,c.size)}catch(a){return d(a)}c.db.transaction(function(e){U(e,c,function(){b._dbInfo=c,a()},function(a,b){d(b)})},d)});return c.serializer=Ya,e}function W(a,b,c,d,e,f){a.executeSql(c,d,e,function(a,g){g.code===g.SYNTAX_ERR?a.executeSql("SELECT name FROM sqlite_master WHERE type='table' AND name = ?",[b.storeName],function(a,h){h.rows.length?f(a,g):U(a,b,function(){a.executeSql(c,d,e,f)},f)},f):f(a,g)},f)}function X(a,b){var c=this;a=j(a);var d=new ya(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){W(c,e,"SELECT * FROM "+e.storeName+" WHERE key = ? LIMIT 1",[a],function(a,c){var d=c.rows.length?c.rows.item(0).value:null;d&&(d=e.serializer.deserialize(d)),b(d)},function(a,b){d(b)})})}).catch(d)});return h(d,b),d}function Y(a,b){var c=this,d=new ya(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){W(c,e,"SELECT * FROM "+e.storeName,[],function(c,d){for(var f=d.rows,g=f.length,h=0;h0)return void f(Z.apply(e,[a,h,c,d-1]));g(b)}})})}).catch(g)});return h(f,c),f}function $(a,b,c){return Z.apply(this,[a,b,c,1])}function _(a,b){var c=this;a=j(a);var d=new ya(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){W(c,e,"DELETE FROM "+e.storeName+" WHERE key = ?",[a],function(){b()},function(a,b){d(b)})})}).catch(d)});return h(d,b),d}function aa(a){var b=this,c=new ya(function(a,c){b.ready().then(function(){var d=b._dbInfo;d.db.transaction(function(b){W(b,d,"DELETE FROM "+d.storeName,[],function(){a()},function(a,b){c(b)})})}).catch(c)});return h(c,a),c}function ba(a){var b=this,c=new ya(function(a,c){b.ready().then(function(){var d=b._dbInfo;d.db.transaction(function(b){W(b,d,"SELECT COUNT(key) as c FROM "+d.storeName,[],function(b,c){var d=c.rows.item(0).c;a(d)},function(a,b){c(b)})})}).catch(c)});return h(c,a),c}function ca(a,b){var c=this,d=new ya(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){W(c,e,"SELECT key FROM "+e.storeName+" WHERE id = ? LIMIT 1",[a+1],function(a,c){var d=c.rows.length?c.rows.item(0).key:null;b(d)},function(a,b){d(b)})})}).catch(d)});return h(d,b),d}function da(a){var b=this,c=new ya(function(a,c){b.ready().then(function(){var d=b._dbInfo;d.db.transaction(function(b){W(b,d,"SELECT key FROM "+d.storeName,[],function(b,c){for(var d=[],e=0;e '__WebKitDatabaseInfoTable__'",[],function(c,d){for(var e=[],f=0;f0}function ka(a){var b=this,c={};if(a)for(var d in a)c[d]=a[d];return c.keyPrefix=ha(a,b._defaultConfig),ja()?(b._dbInfo=c,c.serializer=Ya,ya.resolve()):ya.reject()}function la(a){var b=this,c=b.ready().then(function(){for(var a=b._dbInfo.keyPrefix,c=localStorage.length-1;c>=0;c--){var d=localStorage.key(c);0===d.indexOf(a)&&localStorage.removeItem(d)}});return h(c,a),c}function ma(a,b){var c=this;a=j(a);var d=c.ready().then(function(){var b=c._dbInfo,d=localStorage.getItem(b.keyPrefix+a);return d&&(d=b.serializer.deserialize(d)),d});return h(d,b),d}function na(a,b){var c=this,d=c.ready().then(function(){for(var b=c._dbInfo,d=b.keyPrefix,e=d.length,f=localStorage.length,g=1,h=0;h=0;b--){var c=localStorage.key(b);0===c.indexOf(a)&&localStorage.removeItem(c)}}):ya.reject("Invalid arguments"),h(d,b),d}function ua(a,b){a[b]=function(){var c=arguments;return a.ready().then(function(){return a[b].apply(a,c)})}}function va(){for(var a=1;a { throw "Method unsupported for driver."; }, + setMultipleItems: () => { throw "Method unsupported for driver."; }, ++ getSetMultipleItemsRaceConditionErrorMessage: () => {throw "Method unsupported for driver.";}, clear: clear, length: length, key: key, diff --git a/node_modules/localforage/src/drivers/websql.js b/node_modules/localforage/src/drivers/websql.js -index 257d819..4fa2410 100644 +index 257d819..1209150 100644 --- a/node_modules/localforage/src/drivers/websql.js +++ b/node_modules/localforage/src/drivers/websql.js -@@ -600,6 +600,8 @@ var webSQLStorage = { +@@ -600,6 +600,9 @@ var webSQLStorage = { getItem: getItem, setItem: setItem, removeItem: removeItem, + getMultipleItems: () => { throw "Method unsupported for driver."; }, + setMultipleItems: () => { throw "Method unsupported for driver."; }, ++ getSetMultipleItemsRaceConditionErrorMessage: () => {throw "Method unsupported for driver.";}, clear: clear, length: length, key: key, diff --git a/node_modules/localforage/src/localforage.js b/node_modules/localforage/src/localforage.js -index 8d3393d..847e5e5 100644 +index 8d3393d..2b3e3a8 100644 --- a/node_modules/localforage/src/localforage.js +++ b/node_modules/localforage/src/localforage.js -@@ -36,7 +36,9 @@ const LibraryMethods = [ +@@ -36,7 +36,10 @@ const LibraryMethods = [ 'keys', 'length', 'removeItem', - 'setItem' + 'setItem', + 'setMultipleItems', ++ 'getSetMultipleItemsRaceConditionErrorMessage', + 'getMultipleItems' ].concat(OptionalDriverMethods); const DefaultConfig = { diff --git a/node_modules/localforage/typings/localforage.d.ts b/node_modules/localforage/typings/localforage.d.ts -index 7fb8f58..850f6d6 100644 +index 7fb8f58..2c5284f 100644 --- a/node_modules/localforage/typings/localforage.d.ts +++ b/node_modules/localforage/typings/localforage.d.ts -@@ -17,8 +17,12 @@ interface LocalForageOptions extends LocalForageDbInstanceOptions { +@@ -17,8 +17,14 @@ interface LocalForageOptions extends LocalForageDbInstanceOptions { interface LocalForageDbMethodsCore { getItem(key: string, callback?: (err: any, value: T | null) => void): Promise; + getMultipleItems(keys: ReadonlyArray, synchronizationKey: string): Promise>; + setItem(key: string, value: T, callback?: (err: any, value: T) => void): Promise; + setMultipleItems(input: Readonly<{[key: string]: unknown}>, synchronizationKey: string, expectedSynchronizationValue: ?string, newSynchronizationValue: string, forceWrite: boolean): Promise; ++ ++ getSetMultipleItemsRaceConditionErrorMessage(): string; + removeItem(key: string, callback?: (err: any) => void): Promise; clear(callback?: (err: any) => void): Promise; diff --git a/web/flow-typed/npm/localforage_v1.5.x.js b/web/flow-typed/npm/localforage_v1.5.x.js index 2337a65e2..ab1c876d2 100644 --- a/web/flow-typed/npm/localforage_v1.5.x.js +++ b/web/flow-typed/npm/localforage_v1.5.x.js @@ -1,107 +1,109 @@ // flow-typed signature: 925af5b2fdcaf03745a5f974ecaa0ce9 // flow-typed version: c6154227d1/localforage_v1.5.x/flow_>=v0.104.x type PartialConfig = { driver?: string | Array, name?: string, size?: number, storeName?: string, version?: string, description?: string, ... }; type Driver = { _driver: string, _initStorage(config: PartialConfig): void, getItem( key: string, successCallback?: (err?: Error, value?: T) => mixed, ): ?Promise, setItem( key: string, value: T, successCallback?: (err?: Error, value?: T) => mixed, ): ?Promise, removeItem( key: string, successCallback?: (err?: Error) => mixed, ): ?Promise, clear(successCallback?: ?(numberOfKeys: number) => mixed): ?Promise, length(successCallback?: (numberOfKeys: number) => mixed): ?Promise, key( keyIndex: number, successCallback?: (keyName: string) => mixed, ): ?Promise, keys( successCallback?: (keyNames: Array) => mixed, ): ?Promise>, getMultipleItems( keys: $ReadOnlyArray<$Keys>, synchronizationKey: string ): Promise<{+values: T, +synchronizationValue: ?string}>, setMultipleItems( input: {+[string]: mixed}, synchronizationKey: string, expectedSynchronizationValue: ?string, newSynchronizationValue: string, forceWrite: boolean ): Promise, + getSetMultipleItemsRaceConditionErrorMessage(): string, ... }; type localforageInstance = { INDEXEDDB: 'asyncStorage', WEBSQL: 'webSQLStorage', LOCALSTORAGE: 'localStorageWrapper', getItem( key: string, successCallback?: (err?: Error, value?: T) => mixed, ): Promise, setItem( key: string, value: T, successCallback?: (err?: Error, value?: T) => mixed, ): Promise, removeItem( key: string, successCallback?: (err?: Error) => mixed, ): Promise, clear(successCallback?: ?(numberOfKeys: number) => mixed): Promise, length(successCallback?: (numberOfKeys: number) => mixed): Promise, key( keyIndex: number, successCallback?: (keyName: string) => mixed, ): Promise, keys( successCallback?: (keyNames: Array) => mixed, ): Promise>, getMultipleItems( keys: $ReadOnlyArray<$Keys>, synchronizationKey: string ): Promise<{+values: T, +synchronizationValue: ?string}>, setMultipleItems( input: {+[string]: mixed}, synchronizationKey: string, expectedSynchronizationValue: ?string, newSynchronizationValue: string, forceWrite: boolean ): Promise, + getSetMultipleItemsRaceConditionErrorMessage(): string, iterate( iteratorCallback: (value: T, key: string, iterationNumber: number) => mixed, successCallback?: (result: void | [string, T]) => mixed, ): Promise, setDriver(driverNames: string | Array): void, config(config?: PartialConfig): boolean | PartialConfig, defineDriver(driver: Driver): void, driver(): string, ready(): Promise, supports(driverName: string): boolean, createInstance(config?: PartialConfig): localforageInstance, dropInstance(config?: PartialConfig): Promise, ... }; declare module 'localforage' { declare module.exports: localforageInstance; } diff --git a/web/push-notif/notif-crypto-utils.js b/web/push-notif/notif-crypto-utils.js index ac1dc4e5c..87237cc27 100644 --- a/web/push-notif/notif-crypto-utils.js +++ b/web/push-notif/notif-crypto-utils.js @@ -1,1264 +1,1271 @@ // @flow import olm from '@commapp/olm'; import type { EncryptResult } from '@commapp/olm'; import invariant from 'invariant'; import localforage from 'localforage'; import uuid from 'uuid'; import { olmEncryptedMessageTypes, type NotificationsOlmDataType, type PickledOLMAccount, } from 'lib/types/crypto-types.js'; import type { PlainTextWebNotification, EncryptedWebNotification, SenderDeviceDescriptor, } from 'lib/types/notif-types.js'; import { getCookieIDFromCookie } from 'lib/utils/cookie-utils.js'; import { getMessageForException } from 'lib/utils/errors.js'; import { promiseAll } from 'lib/utils/promises.js'; import { assertWithValidator } from 'lib/utils/validation-utils.js'; import { fetchAuthMetadata, getNotifsInboundKeysForDeviceID, } from './services-client.js'; import { type EncryptedData, decryptData, encryptData, importJWKKey, exportKeyToJWK, generateCryptoKey, encryptedAESDataValidator, extendedCryptoKeyValidator, } from '../crypto/aes-gcm-crypto-utils.js'; import { initOlm } from '../olm/olm-utils.js'; import { NOTIFICATIONS_OLM_DATA_CONTENT, NOTIFICATIONS_OLM_DATA_ENCRYPTION_KEY, } from '../shared-worker/utils/constants.js'; import { isDesktopSafari } from '../shared-worker/utils/db-utils.js'; export type WebNotifDecryptionError = { +id: string, +error: string, +displayErrorMessage?: boolean, }; export type WebNotifsServiceUtilsData = { +olmWasmPath: string, +staffCanSee: boolean, }; export type NotificationAccountWithPicklingKey = { +notificationAccount: olm.Account, +picklingKey: string, +synchronizationValue: ?string, +accountEncryptionKey?: CryptoKey, }; type DecryptionResult = { +newPendingSessionUpdate: string, +newUpdateCreationTimestamp: number, +decryptedNotification: T, }; export const WEB_NOTIFS_SERVICE_UTILS_KEY = 'webNotifsServiceUtils'; const SESSION_UPDATE_MAX_PENDING_TIME = 10 * 1000; const INDEXED_DB_KEYSERVER_PREFIX = 'keyserver'; const INDEXED_DB_KEY_SEPARATOR = ':'; const INDEXED_DB_DEVICE_PREFIX = 'device'; const INDEXED_DB_NOTIFS_SYNC_KEY = 'notifsSyncKey'; // This constant is only used to migrate the existing notifications // session with production keyserver to new IndexedDB key format. This // migration will fire when user updates the app. It will also fire // on dev env provided old keyserver set up is used. Developers willing // to use new keyserver set up must log out before updating the app. // Do not introduce new usages of this constant in the code!!! const ASHOAT_KEYSERVER_ID_USED_ONLY_FOR_MIGRATION_FROM_LEGACY_NOTIF_STORAGE = '256'; const INDEXED_DB_UNREAD_COUNT_SUFFIX = 'unreadCount'; const INDEXED_DB_NOTIFS_ACCOUNT_KEY = 'notificationAccount'; const INDEXED_DB_NOTIFS_ACCOUNT_ENCRYPTION_KEY_DB_LABEL = 'notificationAccountEncryptionKey'; async function deserializeEncryptedData( encryptedData: EncryptedData, encryptionKey: CryptoKey, ): Promise { const serializedData = await decryptData(encryptedData, encryptionKey); const data: T = JSON.parse(new TextDecoder().decode(serializedData)); return data; } async function deserializeEncryptedDataOptional( encryptedData: ?EncryptedData, encryptionKey: ?CryptoKey, ): Promise { if (!encryptedData || !encryptionKey) { return undefined; } return deserializeEncryptedData(encryptedData, encryptionKey); } async function serializeUnencryptedData( data: T, encryptionKey: CryptoKey, ): Promise { const dataAsString = JSON.stringify(data); invariant( dataAsString, 'Attempt to serialize null or undefined is forbidden', ); return await encryptData( new TextEncoder().encode(dataAsString), encryptionKey, ); } async function validateCryptoKey( cryptoKey: CryptoKey | SubtleCrypto$JsonWebKey, ): Promise { if (!isDesktopSafari) { return ((cryptoKey: any): CryptoKey); } return await importJWKKey(((cryptoKey: any): SubtleCrypto$JsonWebKey)); } async function validateCryptoKeyOptional( cryptoKey: ?CryptoKey | ?SubtleCrypto$JsonWebKey, ): Promise { if (!cryptoKey) { return undefined; } return validateCryptoKey(cryptoKey); } async function getCryptoKeyPersistentForm( cryptoKey: CryptoKey, ): Promise { if (!isDesktopSafari) { return cryptoKey; } // Safari doesn't support structured clone algorithm in service // worker context so we have to store CryptoKey as JSON return await exportKeyToJWK(cryptoKey); } async function getNotifsAccountWithOlmData( senderDeviceDescriptor: SenderDeviceDescriptor, ): Promise<{ +encryptedOlmData: ?EncryptedData, +encryptionKey: ?CryptoKey, +olmDataKey: string, +encryptionKeyDBLabel: string, +encryptedOlmAccount: ?EncryptedData, +accountEncryptionKey: ?CryptoKey, +synchronizationValue: ?string, }> { let olmDataKey; let olmDataEncryptionKeyDBLabel; const { keyserverID, senderDeviceID } = senderDeviceDescriptor; if (keyserverID) { const olmDBKeys = await getNotifsOlmSessionDBKeys(keyserverID); const { olmDataKey: fetchedOlmDataKey, encryptionKeyDBKey } = olmDBKeys; olmDataKey = fetchedOlmDataKey; olmDataEncryptionKeyDBLabel = encryptionKeyDBKey; } else { invariant( senderDeviceID, 'keyserverID or SenderDeviceID must be present to decrypt a notif', ); olmDataKey = getOlmDataKeyForDeviceID(senderDeviceID); olmDataEncryptionKeyDBLabel = getOlmEncryptionKeyDBLabelForDeviceID(senderDeviceID); } const queryResult = await localforage.getMultipleItems<{ notificationAccount: ?EncryptedData, notificationAccountEncryptionKey: ?CryptoKey, synchronizationValue: ?number, [string]: ?EncryptedData | ?CryptoKey | ?SubtleCrypto$JsonWebKey, }>( [ INDEXED_DB_NOTIFS_ACCOUNT_KEY, INDEXED_DB_NOTIFS_ACCOUNT_ENCRYPTION_KEY_DB_LABEL, olmDataEncryptionKeyDBLabel, olmDataKey, ], INDEXED_DB_NOTIFS_SYNC_KEY, ); const { values: { notificationAccount, notificationAccountEncryptionKey, [olmDataKey]: maybeEncryptedOlmData, [olmDataEncryptionKeyDBLabel]: maybeOlmDataEncryptionKey, }, synchronizationValue, } = queryResult; const encryptedOlmData: ?EncryptedData = maybeEncryptedOlmData ? assertWithValidator(maybeEncryptedOlmData, encryptedAESDataValidator) : undefined; const olmDataEncryptionKey: ?CryptoKey | ?SubtleCrypto$JsonWebKey = maybeOlmDataEncryptionKey ? assertWithValidator( maybeOlmDataEncryptionKey, extendedCryptoKeyValidator, ) : undefined; const [encryptionKey, accountEncryptionKey] = await Promise.all([ validateCryptoKeyOptional(olmDataEncryptionKey), validateCryptoKeyOptional(notificationAccountEncryptionKey), ]); return { encryptedOlmData, encryptionKey, encryptionKeyDBLabel: olmDataEncryptionKeyDBLabel, encryptedOlmAccount: notificationAccount, olmDataKey, accountEncryptionKey, synchronizationValue, }; } async function persistNotifsAccountWithOlmData(input: { +olmDataKey?: string, +olmEncryptionKeyDBLabel?: string, +olmData?: ?NotificationsOlmDataType, +encryptionKey?: ?CryptoKey, +accountEncryptionKey?: ?CryptoKey, +accountWithPicklingKey?: PickledOLMAccount, +synchronizationValue: ?string, +forceWrite: boolean, }): Promise { const { olmData, olmDataKey, accountEncryptionKey, accountWithPicklingKey, encryptionKey, synchronizationValue, olmEncryptionKeyDBLabel, forceWrite, } = input; const shouldPersistOlmData = olmDataKey && olmData && (encryptionKey || olmEncryptionKeyDBLabel); const shouldPersistAccount = !!accountWithPicklingKey; if (!shouldPersistOlmData && !shouldPersistAccount) { return; } const serializationPromises: { [string]: Promise, } = {}; if (olmDataKey && olmData && encryptionKey) { serializationPromises[olmDataKey] = serializeUnencryptedData( olmData, encryptionKey, ); } else if (olmData && olmDataKey && olmEncryptionKeyDBLabel) { const newEncryptionKey = await generateCryptoKey({ extractable: isDesktopSafari, }); serializationPromises[olmDataKey] = serializeUnencryptedData( olmData, newEncryptionKey, ); serializationPromises[olmEncryptionKeyDBLabel] = getCryptoKeyPersistentForm(newEncryptionKey); } if (accountWithPicklingKey && accountEncryptionKey) { serializationPromises[INDEXED_DB_NOTIFS_ACCOUNT_KEY] = serializeUnencryptedData( accountWithPicklingKey, accountEncryptionKey, ); } else if (accountWithPicklingKey) { const newEncryptionKey = await generateCryptoKey({ extractable: isDesktopSafari, }); serializationPromises[INDEXED_DB_NOTIFS_ACCOUNT_KEY] = serializeUnencryptedData( accountWithPicklingKey, newEncryptionKey, ); serializationPromises[INDEXED_DB_NOTIFS_ACCOUNT_ENCRYPTION_KEY_DB_LABEL] = getCryptoKeyPersistentForm(newEncryptionKey); } const setMultipleItemsInput = await promiseAll(serializationPromises); const newSynchronizationValue = uuid.v4(); try { await localforage.setMultipleItems( setMultipleItemsInput, INDEXED_DB_NOTIFS_SYNC_KEY, synchronizationValue, newSynchronizationValue, forceWrite, ); } catch (e) { + if ( + !e.message?.includes( + localforage.getSetMultipleItemsRaceConditionErrorMessage(), + ) + ) { + throw e; + } // likely shared worker persisted its own data console.log(e); } } async function decryptWebNotification( encryptedNotification: EncryptedWebNotification, ): Promise { const { id, encryptedPayload, type: messageType, ...rest } = encryptedNotification; const senderDeviceDescriptor: SenderDeviceDescriptor = rest; const utilsData = await localforage.getItem( WEB_NOTIFS_SERVICE_UTILS_KEY, ); if (!utilsData) { return { id, error: 'Necessary data not found in IndexedDB' }; } const { olmWasmPath, staffCanSee } = (utilsData: WebNotifsServiceUtilsData); let notifsAccountWithOlmData; try { notifsAccountWithOlmData = await getNotifsAccountWithOlmData( senderDeviceDescriptor, ); } catch (e) { return { id, error: e.message, displayErrorMessage: staffCanSee, }; } const { encryptionKey, encryptedOlmData, olmDataKey, encryptionKeyDBLabel: olmEncryptionKeyDBLabel, accountEncryptionKey, encryptedOlmAccount, synchronizationValue, } = notifsAccountWithOlmData; try { const [notificationsOlmData, accountWithPicklingKey] = await Promise.all([ deserializeEncryptedDataOptional( encryptedOlmData, encryptionKey, ), deserializeEncryptedDataOptional( encryptedOlmAccount, accountEncryptionKey, ), olm.init({ locateFile: () => olmWasmPath }), ]); let decryptedNotification; let updatedOlmData; let updatedNotifsAccount; const { senderDeviceID, keyserverID } = senderDeviceDescriptor; if (keyserverID) { invariant( notificationsOlmData && encryptionKey, 'Received encrypted notification but keyserver olm session was not created', ); const { decryptedNotification: resultDecryptedNotification, updatedOlmData: resultUpdatedOlmData, } = await commonDecrypt( notificationsOlmData, encryptedPayload, ); decryptedNotification = resultDecryptedNotification; updatedOlmData = resultUpdatedOlmData; const { unreadCount } = decryptedNotification; invariant(keyserverID, 'Keyserver ID must be set to update badge counts'); await Promise.all([ persistNotifsAccountWithOlmData({ olmDataKey, olmData: updatedOlmData, olmEncryptionKeyDBLabel, encryptionKey, forceWrite: false, synchronizationValue, }), updateNotifsUnreadCountStorage({ [keyserverID]: unreadCount, }), ]); return { id, ...decryptedNotification }; } else { invariant( senderDeviceID, 'keyserverID or SenderDeviceID must be present to decrypt a notif', ); invariant( accountWithPicklingKey, 'Received encrypted notification but notifs olm account not created', ); const { decryptedNotification: resultDecryptedNotification, updatedOlmData: resultUpdatedOlmData, updatedNotifsAccount: resultUpdatedNotifsAccount, } = await commonPeerDecrypt( senderDeviceID, notificationsOlmData, accountWithPicklingKey, messageType, encryptedPayload, ); decryptedNotification = resultDecryptedNotification; updatedOlmData = resultUpdatedOlmData; updatedNotifsAccount = resultUpdatedNotifsAccount; await persistNotifsAccountWithOlmData({ accountWithPicklingKey: updatedNotifsAccount, accountEncryptionKey, encryptionKey, olmData: updatedOlmData, olmDataKey, olmEncryptionKeyDBLabel, synchronizationValue, forceWrite: false, }); return { id, ...decryptedNotification }; } } catch (e) { return { id, error: e.message, displayErrorMessage: staffCanSee, }; } } async function decryptDesktopNotification( encryptedPayload: string, messageType: string, staffCanSee: boolean, senderDeviceDescriptor: SenderDeviceDescriptor, ): Promise<{ +[string]: mixed }> { const { keyserverID, senderDeviceID } = senderDeviceDescriptor; let notifsAccountWithOlmData; try { [notifsAccountWithOlmData] = await Promise.all([ getNotifsAccountWithOlmData(senderDeviceDescriptor), initOlm(), ]); } catch (e) { return { error: e.message, displayErrorMessage: staffCanSee, }; } const { encryptionKey, encryptedOlmData, olmDataKey, encryptionKeyDBLabel: olmEncryptionKeyDBLabel, accountEncryptionKey, encryptedOlmAccount, synchronizationValue, } = notifsAccountWithOlmData; try { const [notificationsOlmData, accountWithPicklingKey] = await Promise.all([ deserializeEncryptedDataOptional( encryptedOlmData, encryptionKey, ), deserializeEncryptedDataOptional( encryptedOlmAccount, accountEncryptionKey, ), ]); if (keyserverID) { invariant( notificationsOlmData && encryptionKey, 'Received encrypted notification but keyserver olm session was not created', ); const { decryptedNotification, updatedOlmData } = await commonDecrypt<{ +[string]: mixed, }>(notificationsOlmData, encryptedPayload); const updatedOlmDataPersistencePromise = persistNotifsAccountWithOlmData({ olmDataKey, olmData: updatedOlmData, olmEncryptionKeyDBLabel, encryptionKey, forceWrite: false, synchronizationValue, }); // iOS notifications require that unread count is set under // `badge` key. Since MacOS notifications are created by the // same function the unread count is also set under `badge` key const { badge } = decryptedNotification; if (typeof badge === 'number') { await Promise.all([ updateNotifsUnreadCountStorage({ [(keyserverID: string)]: badge }), updatedOlmDataPersistencePromise, ]); return decryptedNotification; } const { unreadCount } = decryptedNotification; if (typeof unreadCount === 'number') { await Promise.all([ updateNotifsUnreadCountStorage({ [(keyserverID: string)]: unreadCount, }), updatedOlmDataPersistencePromise, ]); } return decryptedNotification; } else { invariant( senderDeviceID, 'keyserverID or SenderDeviceID must be present to decrypt a notif', ); invariant( accountWithPicklingKey, 'Received encrypted notification but notifs olm account not created', ); const { decryptedNotification, updatedOlmData, updatedNotifsAccount } = await commonPeerDecrypt<{ +[string]: mixed, }>( senderDeviceID, notificationsOlmData, accountWithPicklingKey, messageType, encryptedPayload, ); await persistNotifsAccountWithOlmData({ accountWithPicklingKey: updatedNotifsAccount, accountEncryptionKey, encryptionKey, olmData: updatedOlmData, olmDataKey, olmEncryptionKeyDBLabel, synchronizationValue, forceWrite: false, }); return decryptedNotification; } } catch (e) { return { error: e.message, staffCanSee, }; } } async function commonDecrypt( notificationsOlmData: NotificationsOlmDataType, encryptedPayload: string, ): Promise<{ +decryptedNotification: T, +updatedOlmData: NotificationsOlmDataType, }> { const { mainSession, picklingKey, pendingSessionUpdate, updateCreationTimestamp, } = notificationsOlmData; let updatedOlmData: NotificationsOlmDataType; let decryptedNotification: T; const shouldUpdateMainSession = Date.now() - updateCreationTimestamp > SESSION_UPDATE_MAX_PENDING_TIME; const decryptionWithPendingSessionResult = decryptWithPendingSession( pendingSessionUpdate, picklingKey, encryptedPayload, ); if (decryptionWithPendingSessionResult.decryptedNotification) { const { decryptedNotification: notifDecryptedWithPendingSession, newPendingSessionUpdate, newUpdateCreationTimestamp, } = decryptionWithPendingSessionResult; decryptedNotification = notifDecryptedWithPendingSession; updatedOlmData = { mainSession: shouldUpdateMainSession ? pendingSessionUpdate : mainSession, pendingSessionUpdate: newPendingSessionUpdate, updateCreationTimestamp: newUpdateCreationTimestamp, picklingKey, }; } else { const { newUpdateCreationTimestamp, decryptedNotification: notifDecryptedWithMainSession, } = decryptWithSession(mainSession, picklingKey, encryptedPayload); decryptedNotification = notifDecryptedWithMainSession; updatedOlmData = { mainSession: mainSession, pendingSessionUpdate, updateCreationTimestamp: newUpdateCreationTimestamp, picklingKey, }; } return { decryptedNotification, updatedOlmData }; } async function commonPeerDecrypt( senderDeviceID: string, notificationsOlmData: ?NotificationsOlmDataType, notificationAccount: PickledOLMAccount, messageType: string, encryptedPayload: string, ): Promise<{ +decryptedNotification: T, +updatedOlmData?: NotificationsOlmDataType, +updatedNotifsAccount?: PickledOLMAccount, }> { if ( messageType !== olmEncryptedMessageTypes.PREKEY.toString() && messageType !== olmEncryptedMessageTypes.TEXT.toString() ) { throw new Error( `Received message of invalid type from device: ${senderDeviceID}`, ); } let isSenderChainEmpty = true; let hasReceivedMessage = false; const sessionExists = !!notificationsOlmData; if (notificationsOlmData) { const session = new olm.Session(); session.unpickle( notificationsOlmData.picklingKey, notificationsOlmData.pendingSessionUpdate, ); isSenderChainEmpty = session.is_sender_chain_empty(); hasReceivedMessage = session.has_received_message(); } // regular message const isRegularMessage = !!notificationsOlmData && messageType === olmEncryptedMessageTypes.TEXT.toString(); const isRegularPrekeyMessage = !!notificationsOlmData && messageType === olmEncryptedMessageTypes.PREKEY.toString() && isSenderChainEmpty && hasReceivedMessage; if (!!notificationsOlmData && (isRegularMessage || isRegularPrekeyMessage)) { return await commonDecrypt(notificationsOlmData, encryptedPayload); } // At this point we either face race condition or session reset attempt or // session initialization attempt. For each of this scenario new inbound // session must be created in order to decrypt message const authMetadata = await fetchAuthMetadata(); const notifInboundKeys = await getNotifsInboundKeysForDeviceID( senderDeviceID, authMetadata, ); const account = new olm.Account(); const session = new olm.Session(); account.unpickle( notificationAccount.picklingKey, notificationAccount.pickledAccount, ); if (notifInboundKeys.error) { throw new Error(notifInboundKeys.error); } invariant( notifInboundKeys.curve25519, 'curve25519 must be present in notifs inbound keys', ); session.create_inbound_from( account, notifInboundKeys.curve25519, encryptedPayload, ); const decryptedNotification: T = JSON.parse( session.decrypt(Number(messageType), encryptedPayload), ); // session reset attempt or session initialization - handled the same const sessionResetAttempt = sessionExists && !isSenderChainEmpty && hasReceivedMessage; // race condition const raceCondition = sessionExists && !isSenderChainEmpty && !hasReceivedMessage; const { deviceID: ourDeviceID } = authMetadata; invariant(ourDeviceID, 'Session creation attempt but no device id'); const thisDeviceWinsRaceCondition = ourDeviceID > senderDeviceID; if ( !sessionExists || sessionResetAttempt || (raceCondition && !thisDeviceWinsRaceCondition) ) { const pickledOlmSession = session.pickle(notificationAccount.picklingKey); const updatedOlmData = { mainSession: pickledOlmSession, pendingSessionUpdate: pickledOlmSession, updateCreationTimestamp: Date.now(), picklingKey: notificationAccount.picklingKey, }; const updatedNotifsAccount = { pickledAccount: account.pickle(notificationAccount.picklingKey), picklingKey: notificationAccount.picklingKey, }; return { decryptedNotification, updatedOlmData, updatedNotifsAccount, }; } // If there is a race condition but we win device id comparison // we return object that carries decrypted data but won't persist // any session state return { decryptedNotification }; } function decryptWithSession( pickledSession: string, picklingKey: string, encryptedPayload: string, ): DecryptionResult { const session = new olm.Session(); session.unpickle(picklingKey, pickledSession); const decryptedNotification: T = JSON.parse( session.decrypt(olmEncryptedMessageTypes.TEXT, encryptedPayload), ); const newPendingSessionUpdate = session.pickle(picklingKey); const newUpdateCreationTimestamp = Date.now(); return { decryptedNotification, newUpdateCreationTimestamp, newPendingSessionUpdate, }; } function decryptWithPendingSession( pendingSessionUpdate: string, picklingKey: string, encryptedPayload: string, ): DecryptionResult | { +error: string } { try { const { decryptedNotification, newPendingSessionUpdate, newUpdateCreationTimestamp, } = decryptWithSession( pendingSessionUpdate, picklingKey, encryptedPayload, ); return { newPendingSessionUpdate, newUpdateCreationTimestamp, decryptedNotification, }; } catch (e) { return { error: e.message }; } } async function encryptNotification( payload: string, deviceID: string, ): Promise { const olmDataKey = getOlmDataKeyForDeviceID(deviceID); const olmEncryptionKeyDBLabel = getOlmEncryptionKeyDBLabelForDeviceID(deviceID); let encryptedOlmData, encryptionKey, synchronizationValue; try { const { values: { [olmDataKey]: fetchedEncryptedOlmData, [olmEncryptionKeyDBLabel]: fetchedEncryptionKey, }, synchronizationValue: fetchedSynchronizationValue, } = await localforage.getMultipleItems<{ +[string]: ?EncryptedData | ?CryptoKey | ?SubtleCrypto$JsonWebKey, }>([olmDataKey, olmEncryptionKeyDBLabel], INDEXED_DB_NOTIFS_SYNC_KEY); encryptedOlmData = fetchedEncryptedOlmData; encryptionKey = fetchedEncryptionKey; synchronizationValue = fetchedSynchronizationValue; } catch (e) { throw new Error( `Failed to fetch olm session from IndexedDB for device: ${deviceID}. Details: ${ getMessageForException(e) ?? '' }`, ); } if (!encryptionKey || !encryptedOlmData) { throw new Error(`Session with device: ${deviceID} not initialized.`); } const validatedEncryptedOlmData = assertWithValidator( encryptedOlmData, encryptedAESDataValidator, ); const validatedEncryptionKey = await validateCryptoKey( assertWithValidator(encryptionKey, extendedCryptoKeyValidator), ); let encryptedNotification; try { encryptedNotification = await encryptNotificationWithOlmSession( payload, validatedEncryptedOlmData, olmDataKey, validatedEncryptionKey, synchronizationValue, ); } catch (e) { throw new Error( `Failed encrypt notification for device: ${deviceID}. Details: ${ getMessageForException(e) ?? '' }`, ); } return encryptedNotification; } async function encryptNotificationWithOlmSession( payload: string, encryptedOlmData: EncryptedData, olmDataKey: string, encryptionKey: CryptoKey, synchronizationValue: ?string, ): Promise { const serializedOlmData = await decryptData(encryptedOlmData, encryptionKey); const { mainSession, picklingKey, pendingSessionUpdate, updateCreationTimestamp, }: NotificationsOlmDataType = JSON.parse( new TextDecoder().decode(serializedOlmData), ); const session = new olm.Session(); session.unpickle(picklingKey, pendingSessionUpdate); const encryptedNotification = session.encrypt(payload); const newPendingSessionUpdate = session.pickle(picklingKey); const updatedOlmData: NotificationsOlmDataType = { mainSession, pendingSessionUpdate: newPendingSessionUpdate, picklingKey, updateCreationTimestamp, }; const updatedEncryptedSession = await encryptData( new TextEncoder().encode(JSON.stringify(updatedOlmData)), encryptionKey, ); const newSynchronizationValue = uuid.v4(); await localforage.setMultipleItems( { [olmDataKey]: updatedEncryptedSession }, INDEXED_DB_NOTIFS_SYNC_KEY, synchronizationValue, newSynchronizationValue, // This method (encryptNotification) is expected to be called // exclusively from the shared worker which must always win race // condition against push notifications service-worker. true, ); return encryptedNotification; } // notifications account manipulation async function getNotifsCryptoAccount(): Promise { const { values: { [INDEXED_DB_NOTIFS_ACCOUNT_KEY]: encryptedNotifsAccount, [INDEXED_DB_NOTIFS_ACCOUNT_ENCRYPTION_KEY_DB_LABEL]: notifsAccountEncryptionKey, }, synchronizationValue, } = await localforage.getMultipleItems<{ +notificationAccount: ?EncryptedData, +notificationAccountEncryptionKey: ?CryptoKey | ?SubtleCrypto$JsonWebKey, }>( [ INDEXED_DB_NOTIFS_ACCOUNT_KEY, INDEXED_DB_NOTIFS_ACCOUNT_ENCRYPTION_KEY_DB_LABEL, ], INDEXED_DB_NOTIFS_SYNC_KEY, ); if (!encryptedNotifsAccount || !notifsAccountEncryptionKey) { throw new Error( 'Attempt to retrieve notifs olm account but account not created.', ); } const validatedNotifsAccountEncryptionKey = await validateCryptoKey( notifsAccountEncryptionKey, ); const pickledOLMAccount = await deserializeEncryptedData( encryptedNotifsAccount, validatedNotifsAccountEncryptionKey, ); const { pickledAccount, picklingKey } = pickledOLMAccount; const notificationAccount = new olm.Account(); notificationAccount.unpickle(picklingKey, pickledAccount); return { notificationAccount, picklingKey, synchronizationValue, accountEncryptionKey: validatedNotifsAccountEncryptionKey, }; } async function retrieveEncryptionKey( encryptionKeyDBLabel: string, ): Promise { if (!isDesktopSafari) { return await localforage.getItem(encryptionKeyDBLabel); } // Safari doesn't support structured clone algorithm in service // worker context so we have to store CryptoKey as JSON const persistedCryptoKey = await localforage.getItem(encryptionKeyDBLabel); if (!persistedCryptoKey) { return null; } return await importJWKKey(persistedCryptoKey); } async function persistEncryptionKey( encryptionKeyDBLabel: string, encryptionKey: CryptoKey, ): Promise { let cryptoKeyPersistentForm; if (isDesktopSafari) { // Safari doesn't support structured clone algorithm in service // worker context so we have to store CryptoKey as JSON cryptoKeyPersistentForm = await exportKeyToJWK(encryptionKey); } else { cryptoKeyPersistentForm = encryptionKey; } await localforage.setItem(encryptionKeyDBLabel, cryptoKeyPersistentForm); } async function getNotifsOlmSessionDBKeys(keyserverID?: string): Promise<{ +olmDataKey: string, +encryptionKeyDBKey: string, }> { const olmDataKeyForKeyserverPrefix = getOlmDataKeyForCookie( undefined, keyserverID, ); const olmEncryptionKeyDBLabelForKeyserverPrefix = getOlmEncryptionKeyDBLabelForCookie(undefined, keyserverID); const dbKeys = await localforage.keys(); const olmDataKeys = sortOlmDBKeysArray( dbKeys.filter(key => key.startsWith(olmDataKeyForKeyserverPrefix)), ); const encryptionKeyDBLabels = sortOlmDBKeysArray( dbKeys.filter(key => key.startsWith(olmEncryptionKeyDBLabelForKeyserverPrefix), ), ); if (olmDataKeys.length === 0 || encryptionKeyDBLabels.length === 0) { throw new Error( 'Received encrypted notification but olm session was not created', ); } const latestDataKey = olmDataKeys[olmDataKeys.length - 1]; const latestEncryptionKeyDBKey = encryptionKeyDBLabels[encryptionKeyDBLabels.length - 1]; const latestDataCookieID = getCookieIDFromOlmDBKey(latestDataKey); const latestEncryptionKeyCookieID = getCookieIDFromOlmDBKey( latestEncryptionKeyDBKey, ); if (latestDataCookieID !== latestEncryptionKeyCookieID) { throw new Error( 'Olm sessions and their encryption keys out of sync. Latest cookie ' + `id for olm sessions ${latestDataCookieID}. Latest cookie ` + `id for olm session encryption keys ${latestEncryptionKeyCookieID}`, ); } const olmDBKeys = { olmDataKey: latestDataKey, encryptionKeyDBKey: latestEncryptionKeyDBKey, }; const keysToDelete: $ReadOnlyArray = [ ...olmDataKeys.slice(0, olmDataKeys.length - 1), ...encryptionKeyDBLabels.slice(0, encryptionKeyDBLabels.length - 1), ]; await Promise.all(keysToDelete.map(key => localforage.removeItem(key))); return olmDBKeys; } function getOlmDataKeyForCookie(cookie: ?string, keyserverID?: string): string { let olmDataKeyBase; if (keyserverID) { olmDataKeyBase = [ INDEXED_DB_KEYSERVER_PREFIX, keyserverID, NOTIFICATIONS_OLM_DATA_CONTENT, ].join(INDEXED_DB_KEY_SEPARATOR); } else { olmDataKeyBase = NOTIFICATIONS_OLM_DATA_CONTENT; } if (!cookie) { return olmDataKeyBase; } const cookieID = getCookieIDFromCookie(cookie); return [olmDataKeyBase, cookieID].join(INDEXED_DB_KEY_SEPARATOR); } function getOlmDataKeyForDeviceID(deviceID: string): string { return [ INDEXED_DB_DEVICE_PREFIX, deviceID, NOTIFICATIONS_OLM_DATA_CONTENT, ].join(INDEXED_DB_KEY_SEPARATOR); } function getOlmEncryptionKeyDBLabelForCookie( cookie: ?string, keyserverID?: string, ): string { let olmEncryptionKeyDBLabelBase; if (keyserverID) { olmEncryptionKeyDBLabelBase = [ INDEXED_DB_KEYSERVER_PREFIX, keyserverID, NOTIFICATIONS_OLM_DATA_ENCRYPTION_KEY, ].join(INDEXED_DB_KEY_SEPARATOR); } else { olmEncryptionKeyDBLabelBase = NOTIFICATIONS_OLM_DATA_ENCRYPTION_KEY; } if (!cookie) { return olmEncryptionKeyDBLabelBase; } const cookieID = getCookieIDFromCookie(cookie); return [olmEncryptionKeyDBLabelBase, cookieID].join(INDEXED_DB_KEY_SEPARATOR); } function getOlmEncryptionKeyDBLabelForDeviceID(deviceID: string): string { return [ INDEXED_DB_DEVICE_PREFIX, deviceID, NOTIFICATIONS_OLM_DATA_ENCRYPTION_KEY, ].join(INDEXED_DB_KEY_SEPARATOR); } function getCookieIDFromOlmDBKey(olmDBKey: string): string | '0' { // Olm DB keys comply to one of the following formats: // KEYSERVER::(OLM_CONTENT | OLM_ENCRYPTION_KEY): // or legacy (OLM_CONTENT | OLM_ENCRYPTION_KEY):. // Legacy format may be used in case a new version of the web app // is running on a old desktop version that uses legacy key format. const cookieID = olmDBKey.split(INDEXED_DB_KEY_SEPARATOR).slice(-1)[0]; return cookieID ?? '0'; } function sortOlmDBKeysArray( olmDBKeysArray: $ReadOnlyArray, ): $ReadOnlyArray { return olmDBKeysArray .map(key => ({ cookieID: Number(getCookieIDFromOlmDBKey(key)), key, })) .sort( ({ cookieID: cookieID1 }, { cookieID: cookieID2 }) => cookieID1 - cookieID2, ) .map(({ key }) => key); } async function migrateLegacyOlmNotificationsSessions() { const keyValuePairsToInsert: { [key: string]: EncryptedData | CryptoKey } = {}; const keysToDelete = []; await localforage.iterate((value: EncryptedData | CryptoKey, key) => { let keyToInsert; if (key.startsWith(NOTIFICATIONS_OLM_DATA_CONTENT)) { const cookieID = getCookieIDFromOlmDBKey(key); keyToInsert = getOlmDataKeyForCookie( cookieID, ASHOAT_KEYSERVER_ID_USED_ONLY_FOR_MIGRATION_FROM_LEGACY_NOTIF_STORAGE, ); } else if (key.startsWith(NOTIFICATIONS_OLM_DATA_ENCRYPTION_KEY)) { const cookieID = getCookieIDFromOlmDBKey(key); keyToInsert = getOlmEncryptionKeyDBLabelForCookie( cookieID, ASHOAT_KEYSERVER_ID_USED_ONLY_FOR_MIGRATION_FROM_LEGACY_NOTIF_STORAGE, ); } else { return undefined; } keyValuePairsToInsert[keyToInsert] = value; keysToDelete.push(key); return undefined; }); const insertionPromises = Object.entries(keyValuePairsToInsert).map( ([key, value]) => (async () => { await localforage.setItem(key, value); })(), ); const deletionPromises = keysToDelete.map(key => (async () => await localforage.removeItem(key))(), ); await Promise.all([...insertionPromises, ...deletionPromises]); } // Multiple keyserver unread count utilities function getKeyserverUnreadCountKey(keyserverID: string) { return [ INDEXED_DB_KEYSERVER_PREFIX, keyserverID, INDEXED_DB_UNREAD_COUNT_SUFFIX, ].join(INDEXED_DB_KEY_SEPARATOR); } async function updateNotifsUnreadCountStorage(perKeyserverUnreadCount: { +[keyserverID: string]: number, }) { const unreadCountUpdatePromises: Array> = Object.entries( perKeyserverUnreadCount, ).map(([keyserverID, unreadCount]) => { const keyserverUnreadCountKey = getKeyserverUnreadCountKey(keyserverID); return localforage.setItem(keyserverUnreadCountKey, unreadCount); }); await Promise.all(unreadCountUpdatePromises); } async function queryNotifsUnreadCountStorage( keyserverIDs: $ReadOnlyArray, ): Promise<{ +[keyserverID: string]: ?number, }> { const queryUnreadCountPromises: Array> = keyserverIDs.map(async keyserverID => { const keyserverUnreadCountKey = getKeyserverUnreadCountKey(keyserverID); const unreadCount = await localforage.getItem( keyserverUnreadCountKey, ); return [keyserverID, unreadCount]; }); const queriedUnreadCounts: $ReadOnlyArray<[string, ?number]> = await Promise.all(queryUnreadCountPromises); return Object.fromEntries(queriedUnreadCounts); } export { decryptWebNotification, decryptDesktopNotification, encryptNotification, getOlmDataKeyForCookie, getOlmEncryptionKeyDBLabelForCookie, getOlmDataKeyForDeviceID, getOlmEncryptionKeyDBLabelForDeviceID, migrateLegacyOlmNotificationsSessions, updateNotifsUnreadCountStorage, queryNotifsUnreadCountStorage, getNotifsCryptoAccount, persistEncryptionKey, retrieveEncryptionKey, persistNotifsAccountWithOlmData, };