UNPKG

@livelybone/storage

Version:

A module for localStorage, support custom solution of quota exceeded. When localStorage is not supported by browser, it will be degrading to use Cookie or Map

502 lines (446 loc) 12.1 kB
/** * Bundle of @livelybone/storage * Generated: 2020-06-03 * Version: 1.7.2 * License: MIT * Author: 2631541504@qq.com */ import { singletonObj } from '@livelybone/singleton'; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } /** * @typedef { undefined|null|String|Number|Boolean|Object|Array } StorageValue * */ /** * @desc JSON stringify * @param { StorageValue } val * */ function stringifyJSON(val) { if (typeof val === 'number') { if (isNaN(val)) return 'NaN'; if (!isFinite(val)) return 'Infinity'; } return JSON.stringify(val); } /** * @desc JSON parse * @param { String } val * */ function parseJSON(val) { try { if (val === 'undefined') return undefined; if (val === 'NaN') return NaN; if (val === 'Infinity') return Infinity; return JSON.parse(val); } catch (e) { return val; } } function isStorageExceeded(err) { return err.code === 22 || err.name.toLowerCase().indexOf('quota') >= 0; } function storageAvailable() { try { var storage = window.localStorage; var x = 'storage-available'; storage.setItem(x, x); var isAvailable = storage.getItem(x) === x; storage.removeItem(x); return isAvailable; } catch (e) { // If the error is QUOTA_EXCEEDED_ERROR // and length of localStorage is greater than 0 // return true return isStorageExceeded(e) && window.localStorage.length > 0; } } function cookieAvailable() { try { return document.cookie || true; } catch (e) { return false; } } var utils = /*#__PURE__*/Object.freeze({ stringifyJSON: stringifyJSON, parseJSON: parseJSON, isStorageExceeded: isStorageExceeded, storageAvailable: storageAvailable, cookieAvailable: cookieAvailable }); var Cookie = /*#__PURE__*/function () { function Cookie() { _classCallCheck(this, Cookie); } _createClass(Cookie, null, [{ key: "keys", value: function keys() { return document.cookie.match(/[^\s=;]+(?==)/g) || []; } }, { key: "values", value: function values() { return Cookie.keys().map(function (key) { return Cookie.get(key); }); } }, { key: "entries", value: function entries() { return Cookie.keys().map(function (key) { return [key, Cookie.get(key)]; }); } }, { key: "forEach", value: function forEach(callback) { Cookie.keys().forEach(function (key) { return callback(Cookie.get(key), key, Cookie); }); } /** * @param { String } key * @param { StorageValue } val * @param { String|Number } [maxAgeMs] * @param { String } [domain] * @param { String } [secure] * @param { String } [path] * */ }, { key: "set", value: function set(key, val) { var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, _ref$maxAgeMs = _ref.maxAgeMs, maxAgeMs = _ref$maxAgeMs === void 0 ? 'Infinity' : _ref$maxAgeMs, domain = _ref.domain, secure = _ref.secure, path = _ref.path; var value = encodeURIComponent(stringifyJSON(val)); var cookie = "".concat(key, "=").concat(value); var isSessionLevel = !maxAgeMs; if (!isSessionLevel) { var exp = new Date(); // Expired after {ms}ms var ms = !isFinite(+maxAgeMs) ? 365 * 24 * 60 * 60 * 1000 : +maxAgeMs; exp.setTime(exp.getTime() + ms); var expires = exp.toUTCString(); // set expires, path, domain and secure of cookie cookie += ";expires=".concat(expires); } cookie += ";path=".concat(path || '/'); if (domain) cookie += ";domain=".concat(domain); if (secure) cookie += ";secure=".concat(secure ? 'secure' : ''); document.cookie = cookie; } }, { key: "get", value: function get(key) { var reg = new RegExp("(^| )".concat(key, "=([^;]*)(;|$)")); var arr = document.cookie.match(reg); if (arr) { return parseJSON(decodeURIComponent(arr[2])); } return null; } }, { key: "has", value: function has(key) { return Cookie.keys().some(function (k) { return k === key; }); } }, { key: "delete", value: function _delete(key) { var has = Cookie.has(key); if (has) { Cookie.set(key, Cookie.get(key), { maxAgeMs: -1 }); return true; } return false; } }, { key: "clear", value: function clear() { Cookie.keys().forEach(function (key) { Cookie.delete(key); }); } }, { key: "size", get: function get() { return Cookie.keys().length; } }]); return Cookie; }(); var LocalStorage = /*#__PURE__*/function () { function LocalStorage() { _classCallCheck(this, LocalStorage); } _createClass(LocalStorage, null, [{ key: "keys", value: function keys() { return Object.keys(localStorage); } }, { key: "values", value: function values() { return LocalStorage.keys().map(function (key) { return LocalStorage.get(key); }); } }, { key: "entries", value: function entries() { return LocalStorage.keys().map(function (key) { return [key, LocalStorage.get(key)]; }); } }, { key: "forEach", value: function forEach(callback) { LocalStorage.keys().forEach(function (key) { return callback(LocalStorage.get(key), key, LocalStorage); }); } }, { key: "set", value: function set(key, val) { var value = stringifyJSON(val); localStorage.setItem(key, value); } }, { key: "get", value: function get(key) { return parseJSON(localStorage.getItem(key)); } }, { key: "has", value: function has(key) { return localStorage.getItem(key) !== null; } }, { key: "delete", value: function _delete(key) { var has = LocalStorage.has(key); if (has) { localStorage.removeItem(key); return true; } return false; } }, { key: "clear", value: function clear() { localStorage.clear(); } }, { key: "addHandler", value: function addHandler(storageHandler) { if (window.addEventListener) { var handler = function handler(e) { storageHandler({ key: e.key, oldValue: parseJSON(e.oldValue), newValue: parseJSON(e.newValue), event: e }); }; window.addEventListener('storage', handler); return handler; } return null; } }, { key: "removeHandler", value: function removeHandler(handlers) { if (window.removeEventListener) { var remove = function remove(handler) { if (handler instanceof Function) window.removeEventListener('storage', handler); }; if (handlers instanceof Array) { handlers.forEach(remove); } else { remove(handlers); } } } }, { key: "size", get: function get() { return LocalStorage.keys().length; } }]); return LocalStorage; }(); /** * @class Storage * */ var Storage = /*#__PURE__*/function () { /** * @typedef { Function } ExceededCallback * @param { Error } error * @param { Array } params params[0] => key; params[1] => value * @param { Storage } storage * */ /** * @param { Boolean } useCookie Use cookie or not * @param { ExceededCallback } [exceededCallback] Callback of QUOTA_EXCEEDED_ERROR, * */ function Storage(useCookie, exceededCallback) { _classCallCheck(this, Storage); _defineProperty(this, "storage", void 0); _defineProperty(this, "exceededCallback", void 0); var getStorage = function getStorage() { if (storageAvailable()) { return LocalStorage; } if (typeof window !== 'undefined') { console.warn(new Error("(Storage) The Object localStorage isn't supported in your client," + ' methods `addHandler` and `removeHandler` will do nothing when you call it')); } // Fallback to use Cookie if useCookie is true if (useCookie) { var $cookieAvailable = cookieAvailable(); if ($cookieAvailable) { return Cookie; } } // Fallback to use Map return new Map(); }; this.storage = getStorage(); if (exceededCallback) this.exceededCallback = exceededCallback; } _createClass(Storage, [{ key: "keys", value: function keys() { return this.storage.keys(); } }, { key: "values", value: function values() { return this.storage.values(); } }, { key: "entries", value: function entries() { return this.storage.entries(); } }, { key: "forEach", value: function forEach(callback) { this.storage.forEach(callback); } }, { key: "set", value: function set(key, val) { try { this.storage.set(key, val); } catch (e) { if (isStorageExceeded(e) && this.exceededCallback) { this.exceededCallback(e, [key, val], this); } else throw e; } } }, { key: "get", value: function get(key) { return this.storage.get(key); } }, { key: "has", value: function has(key) { return this.storage.has(key); } }, { key: "delete", value: function _delete(key) { return this.storage.delete(key); } }, { key: "clear", value: function clear() { this.storage.clear(); } }, { key: "addHandler", value: function addHandler(storageHandler) { if (this.storage.addHandler) { return this.storage.addHandler(storageHandler); } return null; } }, { key: "removeHandler", value: function removeHandler(handlers) { if (this.storage.removeHandler) this.storage.removeHandler(handlers); } }, { key: "size", get: function get() { return this.storage.size; } }]); return Storage; }(); var StorageItem = /*#__PURE__*/function () { function StorageItem(key, isCookie) { _classCallCheck(this, StorageItem); _defineProperty(this, "key", ''); _defineProperty(this, "storage", void 0); this.key = key; this.storage = isCookie ? Cookie : singletonObj('storage', function () { return new Storage(StorageItem.StorageOptions.useCookie, StorageItem.StorageOptions.exceededCallback); }); } _createClass(StorageItem, [{ key: "set", value: function set(val) { this.storage.set(this.key, val); } }, { key: "get", value: function get() { return this.storage.get(this.key); } }, { key: "del", value: function del() { return this.storage.delete(this.key); } }]); return StorageItem; }(); _defineProperty(StorageItem, "StorageOptions", { useCookie: true }); export { Cookie, LocalStorage, Storage, StorageItem, utils as StorageUtils };