UNPKG

@ima/core

Version:

IMA.js framework for isomorphic javascript application

189 lines (188 loc) 6.43 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "CacheImpl", { enumerable: true, get: function() { return CacheImpl; } }); const _Cache = require("./Cache"); const _CacheEntry = require("./CacheEntry"); class CacheImpl extends _Cache.Cache { /** * Pre-compiled regex for escaping </script tags in serialized output. * This avoids recompiling the regex on every serialize() call. */ static SCRIPT_TAG_REGEX = /<\/script/gi; _cache; _factory; _Helper; _ttl; _enabled; /** * Initializes the cache. * * @param cacheStorage The cache entry storage to use. * @param factory Which create new instance of cache entry. * @param Helper The IMA.js helper methods. * @param config The cache configuration. */ constructor(cacheStorage, factory, Helper, { ttl = 30000, enabled = false }){ super(); this._cache = cacheStorage; this._factory = factory; /** * Tha IMA.js helper methods. */ this._Helper = Helper; /** * Default cache entry time to live in milliseconds. */ this._ttl = ttl; /** * Flag signalling whether the cache is currently enabled. */ this._enabled = enabled; } /** * @inheritDoc */ clear() { this._cache.clear(); } /** * @inheritDoc */ has(key) { if (!this._enabled || !this._cache.has(key)) { return false; } const cacheEntry = this._cache.get(key); if (cacheEntry && !cacheEntry.isExpired()) { return true; } this.delete(key); return false; } /** * @inheritDoc */ get(key) { if (this.has(key)) { const cacheEntryItem = this._cache.get(key); const value = cacheEntryItem.getValue(); return this._clone(value); } return null; } /** * @inheritDoc */ set(key, value, ttl = 0) { if (!this._enabled) { return; } const cacheEntry = this._factory.createCacheEntry(this._clone(value), ttl || this._ttl); this._cache.set(key, cacheEntry); } /** * @inheritDoc */ delete(key) { this._cache.delete(key); } /** * @inheritDoc */ disable() { this._enabled = false; this.clear(); } /** * @inheritDoc */ enable() { this._enabled = true; } /** * @inheritDoc */ serialize() { // Use Object.create(null) to avoid prototype chain overhead const dataToSerialize = Object.create(null); for (const key of this._cache.keys()){ const currentValue = this._cache.get(key); if (currentValue instanceof _CacheEntry.CacheEntry) { const serializeEntry = currentValue.serialize(); if (serializeEntry.ttl === Infinity) { serializeEntry.ttl = 'Infinity'; } if ($Debug) { if (!this._canSerializeValue(serializeEntry.value)) { throw new Error(`ima.core.cache.CacheImpl:serialize An ` + `attempt to serialize ` + `${serializeEntry.toString()}, stored ` + `using the key ${key}, was made, but the value ` + `cannot be serialized. Remove this entry from ` + `the cache or change its type so that can be ` + `serialized using JSON.stringify().`); } } dataToSerialize[key] = serializeEntry; } } const serialized = JSON.stringify(dataToSerialize); // Use pre-compiled regex and only replace if needed return serialized.includes('</script') ? serialized.replace(CacheImpl.SCRIPT_TAG_REGEX, '<\\/script') : serialized; } /** * @inheritDoc */ deserialize(serializedData) { for(const key in serializedData){ const cacheEntryItem = serializedData[key]; const ttl = cacheEntryItem.ttl === 'Infinity' ? Infinity : cacheEntryItem.ttl; this.set(key, cacheEntryItem.value, ttl); } } /** * Tests whether the provided value can be serialized into JSON. * * @param value The value to test whether or not it can be serialized. * @return `true` if the provided value can be serialized into JSON, * `false` otherwise. */ _canSerializeValue(value) { // Early exit for primitives const valueType = typeof value; if (value === null || value === undefined || valueType === 'string' || valueType === 'number' || valueType === 'boolean') { return true; } // Check for non-serializable types if (value instanceof Date || value instanceof RegExp || value instanceof Promise || valueType === 'function') { console.warn('The provided value is not serializable: ', value); return false; } // Handle arrays if (value.constructor === Array) { for (const element of value){ if (!this._canSerializeValue(element)) { console.warn('The provided array is not serializable: ', value); return false; } } return true; } // Handle objects if (valueType === 'object') { for (const propertyName of Object.keys(value)){ if (!this._canSerializeValue(value[propertyName])) { console.warn('The provided object is not serializable due to the ' + 'following property: ', propertyName, value); return false; } } } return true; } /** * Attempts to clone the provided value, if possible. Values that cannot be * cloned (e.g. promises) will be simply returned. * * @param value The value to clone. * @return The created clone, or the provided value if the value cannot be * cloned. */ _clone(value) { // Early exit for null and primitives if (value == null) { return value; } const valueType = typeof value; // Only clone objects (arrays, plain objects, etc.) if (valueType === 'object' && !(value instanceof Promise)) { return this._Helper.clone(value); } return value; } } //# sourceMappingURL=CacheImpl.js.map