UNPKG

expired-storage

Version:

Micro JS lib that provide local & session storage with expiration time.

350 lines (284 loc) 12.2 kB
(function(root) { "use strict"; /** * ExpiredStorage * A class to manage local storage with expiration time (like cookies). * * @param storage: Optional storage base class to use (must implement the 'localStorage' API). If not defined, 'localStorage' will be used as a default. **/ function ExpiredStorage(storage) { // set default storage to localStorage storage = storage || (typeof localStorage !== "undefined" ? localStorage : null); // sanity check1: make sure we have a valid storage class to use. if (!storage) { throw new Error("ExpiredStorage: No storage base class provided and 'localStorage' is undefined! Please provide a valid base storage class."); } // sanity check2: make sure the storage class provide the required API. if (!(storage.setItem && storage.getItem && storage.removeItem && storage.clear)) { throw new Error("ExpiredStorage: Storage class don't support one or more of the required API functions: getItem, setItem, removeItem or clear."); } // set storage class this._storage = storage; } /** * Define expired storage prototype. */ ExpiredStorage.prototype = { // base storage class to use, must implement the localStorage API. _storage: null, // prefix to use when storing items timestamp _expiration_key_prefix: "__expired_storage_ts__", /** * Get current timestamp in seconds. **/ getTimestamp: function() { return Math.floor(((new Date).getTime()) / 1000); }, /** * Set item. * @param key: Item key to set (string). * @param value: Value to store (string). * @param expiration: Expiration time, in seconds. If not provided, will not set expiration time. * @param return: Storage.setItem() return code. **/ setItem: function(key, value, expiration) { // set item var ret = this._storage.setItem(key, value); // set expiration timestamp (only if defined) if (expiration) { this.updateExpiration(key, expiration); } // return set value return value return ret; }, /** * Get item. * @param key: Item key to get (string). * @return: Stored value, or undefined if not set / expired. */ getItem: function(key) { // if expired remove item and return null if (this.isExpired(key)) { this.removeItem(key); return null; } // try to fetch and return item value return this._storage.getItem(key); }, /** * Get item + metadata such as time left and if expired. * Even if item expired, will not remove it. * @param key: Item key to get (string). * @return: Dictionary with: {value, timeLeft, isExpired} */ peek: function(key) { // get value and time left var ret = { value: this._storage.getItem(key), timeLeft: this.getTimeLeft(key), }; // set if expired ret.isExpired = ret.timeLeft !== null && ret.timeLeft <= 0; // return data return ret; }, /** * Get item time left to live. * @param key: Item key to get (string). * @return: Time left to expire (in seconds), or null if don't have expiration date. */ getTimeLeft: function(key) { // try to fetch expiration time for key var expireTime = parseInt(this._storage.getItem(this._expiration_key_prefix + key)); // if got expiration time return how much left to live if (expireTime && !isNaN(expireTime)) { return expireTime - this.getTimestamp(); } // if don't have expiration time return null return null; }, /** * Return if an item is expired (don't remove it, even if expired). * @param key: Item key to check (string). * @return: True if expired, False otherwise. */ isExpired: function(key) { // get time left for item var timeLeft = this.getTimeLeft(key); // return if expired return timeLeft !== null && timeLeft <= 0; }, /** * Update expiration time for an item (note: doesn't validate that the item is set). * @param key: Item key to update expiration for (string). * @param expiration: New expiration time in seconds to set. * @return: Storage.setItem() return code for setting new expiration. **/ updateExpiration: function(key, expiration) { return this._storage.setItem(this._expiration_key_prefix + key, this.getTimestamp() + expiration); }, /** * Remove an item. * @param key: Item key to remove (string). * @return: Storage.removeItem() return code. */ removeItem: function(key) { // remove the item itself and its expiration time var ret = this._storage.removeItem(key); this._storage.removeItem(this._expiration_key_prefix + key); // return optional return code return ret; }, /** * Set a json serializable value. This basically calls JSON.stringify on 'val' before setting it. * @param key: Item key to set (string). * @param value: Value to store (object, will be stringified). * @param expiration: Expiration time, in seconds. If not provided, will not set expiration time. * @param return: Storage.setItem() return code. **/ setJson: function(key, val, expiration) { // special case - make sure not undefined, because it would just write "undefined" and crash on reading. if (val === undefined) { throw new Error("Cannot set undefined value as JSON!"); } // set stringified value return this.setItem(key, JSON.stringify(val), expiration); }, /** * Get a json serializable value. This basically calls JSON.parse on the returned value. * @param key: Item key to get (string). * @return: Stored value, or undefined if not set / expired. **/ getJson: function(key) { // get value var val = this.getItem(key); // if null, return null if (val === null) { return null; } // parse and return value return JSON.parse(val); }, /** * Get all keys in storage, not including internal keys used to store expiration. * @param: includeExpired: if true, will also include expired keys. * @return: Array with keys. */ keys: function(includeExpired) { // create list to return var ret = []; // iterate over storage keys to find all non-expiration keys var that = this; this._iterKeys(function(storageKey) { // if its not a timestamp key, skip it if (storageKey.indexOf(that._expiration_key_prefix) !== 0) { // add to return list, but only if including expired keys or if not expired yet if (includeExpired || !that.isExpired(storageKey)) { ret.push(storageKey); } } }); // return keys return ret; }, /** * Iterate all keys in storage class. * @param callback: Function to call for every key, with a single param: key. */ _iterKeys: function(callback) { // first check if storage define a 'keys()' function. if it does, use it if (typeof this._storage.keys === "function") { var keys = this._storage.keys(); for (var i = 0; i < keys.length; ++i) { callback(keys[i]); } } // if not supported try to use object.keys else if (typeof Object === "function" && Object.keys) { var keys = Object.keys(this._storage); for (var i = 0; i < keys.length; ++i) { callback(keys[i]); } } // if not supported try to use iteration via length else if (this._storage.length !== undefined && typeof this._storage.key === "function") { // first build keys array, so this function will be delete-safe (eg if callback remove keys it won't cause problems due to index change) var keys = []; for (var i = 0, len = this._storage.length; i < len; ++i) { keys.push(this._storage.key(i)); } // now actually iterate keys for (var i = 0; i < keys.length; ++i) { callback(keys[i]); } } // if both methods above didn't work, iterate on all keys in storage class hoping for the best.. else { for (var storageKey in this._storage) { callback(storageKey); } } }, /** * Clear the entire storage and all keys in it. */ clear: function() { this._storage.clear(); }, /** * Clear expired keys. * If you never call this function, expired keys will remain until you try to get them / reset a new value. * * @param return: List of removed keys due to expiration. */ clearExpired: function() { // return list var ret = []; // get current timestamp var timestamp = this.getTimestamp(); // iterate over storage keys to find all counters var that = this; this._iterKeys(function(storageKey) { // if its not a timestamp key, skip it if (storageKey.indexOf(that._expiration_key_prefix) === 0) { // get item key var itemKey = storageKey.substr(that._expiration_key_prefix.length); // if expired remove it + the item if (that.isExpired(itemKey)) { that.removeItem(itemKey); ret.push(itemKey); } } }); // return list with removed keys return ret; }, }; // add some api params ExpiredStorage.version = "1.0.2"; ExpiredStorage.author = "Ronen Ness"; ExpiredStorage.gitUrl = "https://github.com/RonenNess/ExpiredStorage"; // AMD support. if (typeof define === "function" && define.amd) { define(function() { "use strict"; return ExpiredStorage; }); } // support in CommonJS with module object else if (typeof module === "object" && module.exports) { module.exports = ExpiredStorage; } // support in CommonJS without 'module' object else if (typeof exports === "object" && exports) { exports = ExpiredStorage; } // add to root (only if not previously defined) else if (root && !root.ExpiredStorage) { root.ExpiredStorage = ExpiredStorage; } // also return the API object return ExpiredStorage; })(this);