angular-persistence
Version:
A library to handle persistence for Angular 2 applications.
320 lines • 13.1 kB
JavaScript
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