UNPKG

angular-persistence

Version:

A library to handle persistence for Angular 2 applications.

320 lines 13.1 kB
import { Injectable, EventEmitter } from '@angular/core'; import { CacheImpl } from './persistence.cache-impl'; import { SubStorage } from './storage/storage.sub_storage'; import { StorageFactory } from './storage/storage.factory'; import { StorageType } from '../constants/persistence.storage_type'; /** * Service used to persist application wide storage. Iterms may be obtained from the Service * itself or used through proxies. This framework also supports an immutable flag which will * instruct the service that the objects stored within should not have any side-effects when * objects on the outside are changed. * * Note on immutability: Only clonable objects will be saved when the immutable flag is * set. This framework will do a deep clone of the objects in question, but items such * as functions will not be preserved. Also, immutability is slower. If you have objects * that are well controlled with a single component, it is suggested that you don't save your * item as immutable. * * @export * \@class PersistenceService * * @author Scott O'Bryan * \@since 1.0 */ export var PersistenceService = (function () { function PersistenceService() { this._emitter = new EventEmitter(); this._storage = StorageFactory.getStorage(); } /** * Returns a hot observable that can be used to monitor changes to this framework over * time. Subscribing to this observable has the potential of causing memory leaks, * so each subscriber is expected to unsubscribe when notifications are no longer * needed. * * results. If not provided, all changes will be * returned. * * to this framework over time. * @param {?=} config * @return {?} */ PersistenceService.prototype.changes = function (config) { if (config === void 0) { config = {}; } var /** @type {?} */ observable = this._emitter.asObservable(); // apply the key filter if (config.key) { observable = observable.filter(function (val) { return val.key === config.key; }); } // apply the type filter if (config.type) { observable = observable.filter(function (val) { return val.type === config.type; }); } return observable; }; /** * Returns an object from storage. If the object was stored with the immutable flag * set, then the object returned will not have any side-effects into the stored model * until it is set again. * * @param {?} key * @param {?=} type * @return {?} */ PersistenceService.prototype.get = function (key, type) { if (type === void 0) { type = StorageType.MEMORY; } var /** @type {?} */ storage = this._getStorage(type); var /** @type {?} */ value = storage.get(key); // the value here will actually be an object with some metadata attached. This // is done to handle immutable and some other things. if (value) { var /** @type {?} */ currDate = Date.now(); // if we have a value, we need to check to see if its expired. if (value.expireAfter && value.created + value.expireAfter < currDate) { storage.remove(key); this._emitter.emit({ key: key, type: type }); return undefined; } // handle the oneUse configuration if (value.oneUse) { storage.remove(key); this._emitter.emit({ key: key, type: type }); return value.data; } // if maxAge then we need to update the expires tag if (value.timeout) { if (value.lastAccessed + value.timeout < currDate) { storage.remove(key); this._emitter.emit({ key: key, type: type }); return undefined; } else { value.lastAccessed = currDate; storage.set(key, value); } } return value.data; } return undefined; }; /** * Puts an object into storage, replacing any item that may be in there. By default, * the object is stored as-is, which means that when other areas of code get the * object, they can mutate it. * * As immutable storage is slower, and the reconstituted logic may be * missing functions or metadata, it is recommended to use this only * if you need to ensure the integrity of the stored object on each set * as might be the case if you make use of the "change" emitter. * * @param {?} key * @param {?} value * @param {?=} config * @return {?} */ PersistenceService.prototype.set = function (key, value, config) { if (config === void 0) { config = {}; } if (!config.type) { config.type = StorageType.MEMORY; } if (!value === undefined) { this.remove(key); this._emitter.emit({ key: key, type: config.type }); return true; } var /** @type {?} */ storage = this._getStorage(config.type); var /** @type {?} */ currDate = Date.now(); var /** @type {?} */ success = storage.set(key, { data: value, expireAfter: config.expireAfter, timeout: config.timeout, oneUse: config.oneUse ? true : false, created: currDate, lastAccessed: currDate }); // happens if the info object or storage object cannot be saved. // Ensure we have cleaned up. if (success) { // this seems kind of wierd, but if we are using an immutable // storage type, we want the emitter this._emitter.emit({ key: key, type: config.type }); } else { storage.remove(key); } return success; }; /** * Clears a value stored in the service for the given type. * * @param {?} key * @param {?=} type * @return {?} */ PersistenceService.prototype.remove = function (key, type) { if (type === void 0) { type = StorageType.MEMORY; } var /** @type {?} */ storage = this._getStorage(type); var /** @type {?} */ currentItem = this.get(key, type); if (currentItem !== undefined) { storage.remove(key); this._emitter.emit({ key: key, type: type }); } return currentItem; }; /** * Clears all stored items for a particular storage type. * * @param {?=} type * @return {?} */ PersistenceService.prototype.removeAll = function (type) { var _this = this; if (type === void 0) { type = StorageType.MEMORY; } var /** @type {?} */ keys = this._getStorage(type).keys(); this._getStorage(type).removeAll(); keys.forEach(function (key) { return _this._emitter.emit({ key: key, type: type }); }); }; /** * Cleans up any expired objects in the cache. * * @param {?=} type * @return {?} */ PersistenceService.prototype.clean = function (type) { if (type === void 0) { type = StorageType.MEMORY; } var /** @type {?} */ storage = this._getStorage(type); var /** @type {?} */ keys = storage.keys(); var /** @type {?} */ currDate = Date.now(); for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) { var key = keys_1[_i]; var /** @type {?} */ item = storage.get(key); // if we have a value, we need to check to see if its expired. if (item && ((item.expireAfter && item.created + item.expireAfter < currDate || item.timeout && item.lastAccessed + item.timeout < currDate))) { this.remove(key); } } }; /** * Create a property on the object that is bound to this stored value. This method * requires ES5 compatibility and the property will have special rules associated * with it. The name of the property will be "key", and the value stored in the * configured storage will be prefix + key. * * @template T the type of property * @param {?} obj * @param {?} propName * @param {?} key * @param {?=} config * @return {?} */ PersistenceService.prototype.defineProperty = function (obj, propName, key, config) { var _this = this; if (config === void 0) { config = {}; } var /** @type {?} */ type = config.type || StorageType.MEMORY; Object.defineProperty(obj, propName, { enumerable: true, configurable: true, get: function () { return _this.get(key, type); }, set: function (val) { _this.set(key, val, config); } }); }; /** * Returns a facade that makes things a bit easier when interacting with the service. * The facade will use the prefix in order to isolate they keystore. If no prefix is * defined, the keystore will be mapped as usual with the keys working as-is in the * storage. * * @param {?} namespace * @param {?=} config the config for the facade * @return {?} a PersistenceFacade object representing this store */ PersistenceService.prototype.createContainer = function (namespace, config) { if (config === void 0) { config = {}; } var /** @type {?} */ thisService = this; var /** @type {?} */ myConfig = { oneUse: config.oneUse, expireAfter: config.expireAfter, timeout: config.timeout, type: config.type || StorageType.MEMORY }; // Return a substorage of the service so the full config can be used. return new SubStorage(namespace, { get: function (key) { return thisService.get(key, myConfig.type); }, set: function (key, value) { return thisService.set(key, value, myConfig); }, remove: function (key) { return thisService.remove(key, myConfig.type); }, removeAll: function () { return thisService.removeAll(); } }, true); }; /** * Returns a cache proxy that makes interacting with this service a little bit easier. The * proxy returned will have a set key, a generic loader, and a consistent set of config * parameters. Please note that the "expires" property of the config might have unforseen * side-effects to the cache in that if the expires setting is already passed, the cache will * no longer cache values until a new proxy is created. * * @param {?} key they key for the item in the persistence layer * @param {?} loader the function to load the intiial value. Must return either a value or * an Observable of that value. If an observable is returned, it will be * converted into a single by this method and returned to the subscriber. * @param {?=} config optional config object used to "set" the value if it has not already * been loaded. If a "type" is not specified, memory storage will be * used. * * @return {?} a CacheProxy that can be used to interact with this cache. */ PersistenceService.prototype.createCache = function (key, loader, config) { if (config === void 0) { config = {}; } // for safety ensure that oneUse is not present. It shouldn't be, but sometimes // typescript doesn't always catch errors var /** @type {?} */ myConfig = { type: config.type || StorageType.MEMORY, expireAfter: config.expireAfter, timeout: config.timeout }; return new CacheImpl(key, loader, this, myConfig); }; /** * @param {?} type * @return {?} */ PersistenceService.prototype._getStorage = function (type) { return this._storage.of(type); }; /** * @param {?} maxAge * @return {?} */ PersistenceService.prototype._calculateExpires = function (maxAge) { return maxAge ? Date.now() + maxAge : undefined; }; PersistenceService.decorators = [ { type: Injectable }, ]; /** @nocollapse */ PersistenceService.ctorParameters = function () { return []; }; return PersistenceService; }()); function PersistenceService_tsickle_Closure_declarations() { /** @type {?} */ PersistenceService.decorators; /** * @nocollapse * @type {?} */ PersistenceService.ctorParameters; /** @type {?} */ PersistenceService.prototype._emitter; /** @type {?} */ PersistenceService.prototype._storage; } //# sourceMappingURL=persistence.service.js.map