@ngx-toolkit/cache
Version:
Angular cache with Universal support
175 lines • 26 kB
JavaScript
import 'reflect-metadata/Reflect';
import { getCacheManager } from './cache.instance';
export const METADATA_KEY_CACHE_DEFAULTS = '_cache_defaults';
export const METADATA_KEY_CACHE_KEYS = '_cache_keys';
export const METADATA_KEY_CACHE_VALUE = '_cache_value';
/**
* Allows the configuration of defaults for `CacheResult`, `CachePut`, `CacheRemove`, and `CacheRemoveAll` at the class level.
* Without the method level annotations this annotation has no effect.
*
* @param cacheName
*/
export function CacheDefaults(cacheName) {
return (target) => {
Reflect.defineMetadata(METADATA_KEY_CACHE_DEFAULTS, cacheName, target);
};
}
/**
* When a method annotated with `CacheResult` is invoked a cache key will be generated
* and *Cache.get(key)* is called before the annotated method actually executes.
* If a value is found in the cache it is returned and the annotated method is never actually executed.
* If no value is found the annotated method is invoked and the returned value is stored in the cache with the generated key.
*
* @param params (Optional) {cacheName?: string}
*/
export function CacheResult(params) {
params = getDefaultParams(params);
return (target, propertyKey, descriptor) => {
const originalMethod = descriptor.value;
descriptor.value = function (...args) {
const cache = getCache(target, params);
const cacheKey = getCacheKey(target, propertyKey, args);
// Find cache value
let result = cache.get(cacheKey);
// Call function & save function if no cache value
if (result === undefined) {
// Call function & save result
result = originalMethod.apply(this, args);
cache.put(cacheKey, result);
}
return result;
};
return descriptor;
};
}
/**
* Marks a method argument as part of the cache key.
* If no arguments are marked all arguments are used.
* The exception is for a method annotated with `CachePut` where the `CacheValue` parameter is never included in the key.
*/
export function CacheKey() {
return (target, propertyKey, parameterIndex) => {
const indices = Reflect.getMetadata(`${METADATA_KEY_CACHE_KEYS}_${propertyKey.toString()}`, target, propertyKey) || [];
indices.push(parameterIndex);
Reflect.defineMetadata(`${METADATA_KEY_CACHE_KEYS}_${propertyKey.toString()}`, indices, target, propertyKey);
};
}
/**
* When a method annotated with `CachePut` is invoked a cache key will be generated
* and *Cache.put(key, value)* will be invoked on the specified cache storing the value marked with `CacheValue`.
*
* @param params (Optional) {cacheName?: string, afterInvocation: boolean = true}
*/
export function CachePut(params) {
params = getDefaultParams(params);
return (target, propertyKey, descriptor) => {
const originalMethod = descriptor.value;
descriptor.value = function (...args) {
const cache = getCache(target, params);
const indexValue = Reflect.getMetadata(`${METADATA_KEY_CACHE_VALUE}_${propertyKey.toString()}`, target, propertyKey);
const cacheKey = getCacheKey(target, propertyKey, args, indexValue);
if (!params.afterInvocation && indexValue && indexValue >= 0 && indexValue < args.length) {
cache.put(cacheKey, args[indexValue]);
}
const result = originalMethod.apply(this, args);
if (params.afterInvocation && indexValue && indexValue >= 0 && indexValue < args.length) {
cache.put(cacheKey, args[indexValue]);
}
return result;
};
return descriptor;
};
}
/**
* Marks the parameter to be cached for a method annotated with `CachePut`.
*/
export function CacheValue() {
return (target, propertyKey, parameterIndex) => {
Reflect.defineMetadata(`${METADATA_KEY_CACHE_VALUE}_${propertyKey.toString()}`, parameterIndex, target, propertyKey);
};
}
/**
* When a method annotated with `CacheRemove` is invoked a cache key will be generated
* and *Cache.remove(key)* will be invoked on the specified cache.
* The default behavior is to call *Cache.evict(key)* after the annotated method is invoked,
* this behavior can be changed by setting *`afterInvocation`* to false in which case *Cache.evict(key)*
* will be called before the annotated method is invoked.
*
* @param params (Optional) {cacheName?: string, afterInvocation: boolean = true}
*/
export function CacheRemove(params) {
params = getDefaultParams(params);
return (target, propertyKey, descriptor) => {
const originalMethod = descriptor.value;
descriptor.value = function (...args) {
const cache = getCache(target, params);
const cacheKey = getCacheKey(target, propertyKey, args);
if (!params.afterInvocation) {
cache.evict(cacheKey);
}
const result = originalMethod.apply(this, args);
if (params.afterInvocation) {
cache.evict(cacheKey);
}
return result;
};
return descriptor;
};
}
/**
* When a method annotated with `CacheRemoveAll` is invoked all elements in the specified cache will be removed via the *Cache.clear()* method.
* The default behavior is to call *Cache.clear()* after the annotated method is invoked,
* this behavior can be changed by setting *`afterInvocation`* to false in which case *Cache.clear()* will be called before the annotated method is invoked.
*
* @param params (Optional) {cacheName?: string, afterInvocation: boolean = true}
*/
export function CacheRemoveAll(params) {
params = getDefaultParams(params);
return (target, propertyKey, descriptor) => {
const originalMethod = descriptor.value;
descriptor.value = function (...args) {
const cache = getCache(target, params);
if (!params.afterInvocation) {
cache.clear();
}
const result = originalMethod.apply(this, args);
if (params.afterInvocation) {
cache.clear();
}
return result;
};
return descriptor;
};
}
function getDefaultParams(cacheParams) {
return Object.assign({
afterInvocation: true
}, cacheParams || {});
}
function getCache(target, params) {
if (!params.cacheName) {
params.cacheName = Reflect.getMetadata(METADATA_KEY_CACHE_DEFAULTS, target.constructor) || '';
}
const cache = getCacheManager().getCache(params.cacheName);
if (!cache) {
throw new Error(`Cache '${params.cacheName}' not found for ${target.constructor.name}`);
}
return cache;
}
function getCacheKey(target, propertyKey, args, cacheValueIndex = -1) {
if (!args) {
args = [];
}
const indices = Reflect.getMetadata(`${METADATA_KEY_CACHE_KEYS}_${propertyKey.toString()}`, target, propertyKey);
if (indices) {
args = args.filter((value, index) => indices.indexOf(index) !== -1 && cacheValueIndex !== index);
}
else if (cacheValueIndex !== -1) {
args = args.filter((value, index) => cacheValueIndex !== index);
}
if (args.length === 0) {
throw new Error(`Couldn't generate key without params for '${propertyKey.toString()}' method of ${target.constructor.name}`);
}
return args.map(a => (JSON.stringify(a) || a.toString())).join('|');
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FjaGUuZGVjb3JhdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vcHJvamVjdHMvY2FjaGUvc3JjL2xpYi9jYWNoZS5kZWNvcmF0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTywwQkFBMEIsQ0FBQztBQUNsQyxPQUFPLEVBQUMsZUFBZSxFQUFDLE1BQU0sa0JBQWtCLENBQUM7QUFHakQsTUFBTSxDQUFDLE1BQU0sMkJBQTJCLEdBQUcsaUJBQWlCLENBQUM7QUFDN0QsTUFBTSxDQUFDLE1BQU0sdUJBQXVCLEdBQUcsYUFBYSxDQUFDO0FBQ3JELE1BQU0sQ0FBQyxNQUFNLHdCQUF3QixHQUFHLGNBQWMsQ0FBQztBQVV2RDs7Ozs7R0FLRztBQUNILE1BQU0sVUFBVSxhQUFhLENBQUMsU0FBaUI7SUFDN0MsT0FBTyxDQUFDLE1BQWdCLEVBQVEsRUFBRTtRQUNoQyxPQUFPLENBQUMsY0FBYyxDQUFDLDJCQUEyQixFQUFFLFNBQVMsRUFBRSxNQUFNLENBQUMsQ0FBQztJQUN6RSxDQUFDLENBQUM7QUFDSixDQUFDO0FBRUQ7Ozs7Ozs7R0FPRztBQUNILE1BQU0sVUFBVSxXQUFXLENBQUMsTUFBb0I7SUFDOUMsTUFBTSxHQUFHLGdCQUFnQixDQUFjLE1BQU0sQ0FBQyxDQUFDO0lBRS9DLE9BQU8sQ0FBQyxNQUFjLEVBQUUsV0FBNEIsRUFBRSxVQUF3QyxFQUFnQyxFQUFFO1FBQzlILE1BQU0sY0FBYyxHQUFHLFVBQVUsQ0FBQyxLQUFLLENBQUM7UUFDeEMsVUFBVSxDQUFDLEtBQUssR0FBRyxVQUFTLEdBQUcsSUFBVztZQUN4QyxNQUFNLEtBQUssR0FBVSxRQUFRLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1lBQzlDLE1BQU0sUUFBUSxHQUFXLFdBQVcsQ0FBQyxNQUFNLEVBQUUsV0FBVyxFQUFFLElBQUksQ0FBQyxDQUFDO1lBRWhFLG1CQUFtQjtZQUNuQixJQUFJLE1BQU0sR0FBUSxLQUFLLENB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bmltcG9ydCB7Z2V0Q2FjaGVNYW5hZ2VyfSBmcm9tICcuL2NhY2hlLmluc3RhbmNlJztcbmltcG9ydCB7Q2FjaGV9IGZyb20gJy4vY2FjaGUubW9kZWwnO1xuXG5leHBvcnQgY29uc3QgTUVUQURBVEFfS0VZX0NBQ0hFX0RFRkFVTFRTID0gJ19jYWNoZV9kZWZhdWx0cyc7XG5leHBvcnQgY29uc3QgTUVUQURBVEFfS0VZX0NBQ0hFX0tFWVMgPSAnX2NhY2hlX2tleXMnO1xuZXhwb3J0IGNvbnN0IE1FVEFEQVRBX0tFWV9DQUNIRV9WQUxVRSA9ICdfY2FjaGVfdmFsdWUnO1xuXG5leHBvcnQgaW50ZXJmYWNlIENhY2hlUGFyYW1zIHtcbiAgY2FjaGVOYW1lPzogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIENhY2hlUGFyYW1zSW52b2MgZXh0ZW5kcyBDYWNoZVBhcmFtcyB7XG4gIGFmdGVySW52b2NhdGlvbj86IGJvb2xlYW47XG59XG5cbi8qKlxuICogQWxsb3dzIHRoZSBjb25maWd1cmF0aW9uIG9mIGRlZmF1bHRzIGZvciBgQ2FjaGVSZXN1bHRgLCBgQ2FjaGVQdXRgLCBgQ2FjaGVSZW1vdmVgLCBhbmQgYENhY2hlUmVtb3ZlQWxsYCBhdCB0aGUgY2xhc3MgbGV2ZWwuXG4gKiBXaXRob3V0IHRoZSBtZXRob2QgbGV2ZWwgYW5ub3RhdGlvbnMgdGhpcyBhbm5vdGF0aW9uIGhhcyBubyBlZmZlY3QuXG4gKlxuICogQHBhcmFtIGNhY2hlTmFtZVxuICovXG5leHBvcnQgZnVuY3Rpb24gQ2FjaGVEZWZhdWx0cyhjYWNoZU5hbWU6IHN0cmluZyk6IENsYXNzRGVjb3JhdG9yIHtcbiAgcmV0dXJuICh0YXJnZXQ6IEZ1bmN0aW9uKTogdm9pZCA9PiB7XG4gICAgUmVmbGVjdC5kZWZpbmVNZXRhZGF0YShNRVRBREFUQV9LRVlfQ0FDSEVfREVGQVVMVFMsIGNhY2hlTmFtZSwgdGFyZ2V0KTtcbiAgfTtcbn1cblxuLyoqXG4gKiBXaGVuIGEgbWV0aG9kIGFubm90YXRlZCB3aXRoIGBDYWNoZVJlc3VsdGAgaXMgaW52b2tlZCBhIGNhY2hlIGtleSB3aWxsIGJlIGdlbmVyYXRlZFxuICogYW5kICpDYWNoZS5nZXQoa2V5KSogaXMgY2FsbGVkIGJlZm9yZSB0aGUgYW5ub3RhdGVkIG1ldGhvZCBhY3R1YWxseSBleGVjdXRlcy5cbiAqIElmIGEgdmFsdWUgaXMgZm91bmQgaW4gdGhlIGNhY2hlIGl0IGlzIHJldHVybmVkIGFuZCB0aGUgYW5ub3RhdGVkIG1ldGhvZCBpcyBuZXZlciBhY3R1YWxseSBleGVjdXRlZC5cbiAqIElmIG5vIHZhbHVlIGlzIGZvdW5kIHRoZSBhbm5vdGF0ZWQgbWV0aG9kIGlzIGludm9rZWQgYW5kIHRoZSByZXR1cm5lZCB2YWx1ZSBpcyBzdG9yZWQgaW4gdGhlIGNhY2hlIHdpdGggdGhlIGdlbmVyYXRlZCBrZXkuXG4gKlxuICogQHBhcmFtIHBhcmFtcyAoT3B0aW9uYWwpIHtjYWNoZU5hbWU/OiBzdHJpbmd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBDYWNoZVJlc3VsdChwYXJhbXM/OiBDYWNoZVBhcmFtcyk6IE1ldGhvZERlY29yYXRvciB7XG4gIHBhcmFtcyA9IGdldERlZmF1bHRQYXJhbXM8Q2FjaGVQYXJhbXM+KHBhcmFtcyk7XG5cbiAgcmV0dXJuICh0YXJnZXQ6IE9iamVjdCwgcHJvcGVydHlLZXk6IHN0cmluZyB8IHN5bWJvbCwgZGVzY3JpcHRvcjogVHlwZWRQcm9wZXJ0eURlc2NyaXB0b3I8YW55Pik6IFR5cGVkUHJvcGVydHlEZXNjcmlwdG9yPGFueT4gPT4ge1xuICAgIGNvbnN0IG9yaWdpbmFsTWV0aG9kID0gZGVzY3JpcHRvci52YWx1ZTtcbiAgICBkZXNjcmlwdG9yLnZhbHVlID0gZnVuY3Rpb24oLi4uYXJnczogYW55W10pIHtcbiAgICAgIGNvbnN0IGNhY2hlOiBDYWNoZSA9IGdldENhY2hlKHRhcmdldCwgcGFyYW1zKTtcbiAgICAgIGNvbnN0IGNhY2hlS2V5OiBzdHJpbmcgPSBnZXRDYWNoZUtleSh0YXJnZXQsIHByb3BlcnR5S2V5LCBhcmdzKTtcblxuICAgICAgLy8gRmluZCBjYWNoZSB2YWx1ZVxuICAgICAgbGV0IHJlc3VsdDogYW55ID0gY2FjaGUuZ2V0KGNhY2hlS2V5KTtcblxuICAgICAgLy8gQ2FsbCBmdW5jdGlvbiAmIHNhdmUgZnVuY3Rpb24gaWYgbm8gY2FjaGUgdmFsdWVcbiAgICAgIGlmIChyZXN1bHQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAvLyBDYWxsIGZ1bmN0aW9uICYgc2F2ZSByZXN1bHRcbiAgICAgICAgcmVzdWx0ID0gb3JpZ2luYWxNZXRob2QuYXBwbHkodGhpcywgYXJncyk7XG4gICAgICAgIGNhY2hlLnB1dChjYWNoZUtleSwgcmVzdWx0KTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9O1xuICAgIHJldHVybiBkZXNjcmlwdG9yO1xuICB9O1xufVxuXG4vKipcbiAqIE1hcmtzIGEgbWV0aG9kIGFyZ3VtZW50IGFzIHBhcnQgb2YgdGhlIGNhY2hlIGtleS5cbiAqIElmIG5vIGFyZ3VtZW50cyBhcmUgbWFya2VkIGFsbCBhcmd1bWVudHMgYXJlIHVzZWQuXG4gKiBUaGUgZXhjZXB0aW9uIGlzIGZvciBhIG1ldGhvZCBhbm5vdGF0ZWQgd2l0aCBgQ2FjaGVQdXRgIHdoZXJlIHRoZSBgQ2FjaGVWYWx1ZWAgcGFyYW1ldGVyIGlzIG5ldmVyIGluY2x1ZGVkIGluIHRoZSBrZXkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBDYWNoZUtleSgpOiBQYXJhbWV0ZXJEZWNvcmF0b3Ige1xuICByZXR1cm4gKHRhcmdldDogT2JqZWN0LCBwcm9wZXJ0eUtleTogc3RyaW5nIHwgc3ltYm9sLCBwYXJhbWV0ZXJJbmRleDogbnVtYmVyKSA9PiB7XG4gICAgY29uc3QgaW5kaWNlcyA9IFJlZmxlY3QuZ2V0TWV0YWRhdGEoYCR7TUVUQURBVEFfS0VZX0NBQ0hFX0tFWVN9XyR7cHJvcGVydHlLZXkudG9TdHJpbmcoKX1gLCB0YXJnZXQsIHByb3BlcnR5S2V5KSB8fCBbXTtcbiAgICBpbmRpY2VzLnB1c2gocGFyYW1ldGVySW5kZXgpO1xuICAgIFJlZmxlY3QuZGVmaW5lTWV0YWRhdGEoYCR7TUVUQURBVEFfS0VZX0NBQ0hFX0tFWVN9XyR7cHJvcGVydHlLZXkudG9TdHJpbmcoKX1gLCBpbmRpY2VzLCB0YXJnZXQsIHByb3BlcnR5S2V5KTtcbiAgfTtcbn1cblxuLyoqXG4gKiBXaGVuIGEgbWV0aG9kIGFubm90YXRlZCB3aXRoIGBDYWNoZVB1dGAgaXMgaW52b2tlZCBhIGNhY2hlIGtleSB3aWxsIGJlIGdlbmVyYXRlZFxuICogYW5kICpDYWNoZS5wdXQoa2V5LCB2YWx1ZSkqIHdpbGwgYmUgaW52b2tlZCBvbiB0aGUgc3BlY2lmaWVkIGNhY2hlIHN0b3JpbmcgdGhlIHZhbHVlIG1hcmtlZCB3aXRoIGBDYWNoZVZhbHVlYC5cbiAqXG4gKiBAcGFyYW0gcGFyYW1zIChPcHRpb25hbCkge2NhY2hlTmFtZT86IHN0cmluZywgYWZ0ZXJJbnZvY2F0aW9uOiBib29sZWFuID0gdHJ1ZX1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIENhY2hlUHV0KHBhcmFtcz86IENhY2hlUGFyYW1zSW52b2MpOiBNZXRob2REZWNvcmF0b3Ige1xuICBwYXJhbXMgPSBnZXREZWZhdWx0UGFyYW1zPENhY2hlUGFyYW1zSW52b2M+KHBhcmFtcyk7XG5cbiAgcmV0dXJuICh0YXJnZXQ6IE9iamVjdCwgcHJvcGVydHlLZXk6IHN0cmluZyB8IHN5bWJvbCwgZGVzY3JpcHRvcjogVHlwZWRQcm9wZXJ0eURlc2NyaXB0b3I8YW55Pik6IFR5cGVkUHJvcGVydHlEZXNjcmlwdG9yPGFueT4gPT4ge1xuICAgIGNvbnN0IG9yaWdpbmFsTWV0aG9kID0gZGVzY3JpcHRvci52YWx1ZTtcbiAgICBkZXNjcmlwdG9yLnZhbHVlID0gZnVuY3Rpb24oLi4uYXJnczogYW55W10pIHtcbiAgICAgIGNvbnN0IGNhY2hlOiBDYWNoZSA9IGdldENhY2hlKHRhcmdldCwgcGFyYW1zKTtcbiAgICAgIGNvbnN0IGluZGV4VmFsdWU6IG51bWJlciA9IFJlZmxlY3QuZ2V0TWV0YWRhdGEoYCR7TUVUQURBVEFfS0VZX0NBQ0hFX1ZBTFVFfV8ke3Byb3BlcnR5S2V5LnRvU3RyaW5nKCl9YCwgdGFyZ2V0LCBwcm9wZXJ0eUtleSk7XG4gICAgICBjb25zdCBjYWNoZUtleTogc3RyaW5nID0gZ2V0Q2FjaGVLZXkodGFyZ2V0LCBwcm9wZXJ0eUtleSwgYXJncywgaW5kZXhWYWx1ZSk7XG5cbiAgICAgIGlmICghcGFyYW1zLmFmdGVySW52b2NhdGlvbiAmJiBpbmRleFZhbHVlICYmIGluZGV4VmFsdWUgPj0gMCAmJiBpbmRleFZhbHVlIDwgYXJncy5sZW5ndGgpIHtcbiAgICAgICAgY2FjaGUucHV0KGNhY2hlS2V5LCBhcmdzW2luZGV4VmFsdWVdKTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgcmVzdWx0ID0gb3JpZ2luYWxNZXRob2QuYXBwbHkodGhpcywgYXJncyk7XG5cbiAgICAgIGlmIChwYXJhbXMuYWZ0ZXJJbnZvY2F0aW9uICYmIGluZGV4VmFsdWUgJiYgaW5kZXhWYWx1ZSA+PSAwICYmIGluZGV4VmFsdWUgPCBhcmdzLmxlbmd0aCkge1xuICAgICAgICBjYWNoZS5wdXQoY2FjaGVLZXksIGFyZ3NbaW5kZXhWYWx1ZV0pO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH07XG4gICAgcmV0dXJuIGRlc2NyaXB0b3I7XG4gIH07XG59XG5cbi8qKlxuICogTWFya3MgdGhlIHBhcmFtZXRlciB0byBiZSBjYWNoZWQgZm9yIGEgbWV0aG9kIGFubm90YXRlZCB3aXRoIGBDYWNoZVB1dGAuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBDYWNoZVZhbHVlKCk6IFBhcmFtZXRlckRlY29yYXRvciB7XG4gIHJldHVybiAodGFyZ2V0OiBPYmplY3QsIHByb3BlcnR5S2V5OiBzdHJpbmcgfCBzeW1ib2wsIHBhcmFtZXRlckluZGV4OiBudW1iZXIpID0+IHtcbiAgICBSZWZsZWN0LmRlZmluZU1ldGFkYXRhKGAke01FVEFEQVRBX0tFWV9DQUNIRV9WQUxVRX1fJHtwcm9wZXJ0eUtleS50b1N0cmluZygpfWAsIHBhcmFtZXRlckluZGV4LCB0YXJnZXQsIHByb3BlcnR5S2V5KTtcbiAgfTtcbn1cblxuLyoqXG4gKiBXaGVuIGEgbWV0aG9kIGFubm90YXRlZCB3aXRoIGBDYWNoZVJlbW92ZWAgaXMgaW52b2tlZCBhIGNhY2hlIGtleSB3aWxsIGJlIGdlbmVyYXRlZFxuICogYW5kICpDYWNoZS5yZW1vdmUoa2V5KSogd2lsbCBiZSBpbnZva2VkIG9uIHRoZSBzcGVjaWZpZWQgY2FjaGUuXG4gKiBUaGUgZGVmYXVsdCBiZWhhdmlvciBpcyB0byBjYWxsICpDYWNoZS5ldmljdChrZXkpKiBhZnRlciB0aGUgYW5ub3RhdGVkIG1ldGhvZCBpcyBpbnZva2VkLFxuICogdGhpcyBiZWhhdmlvciBjYW4gYmUgY2hhbmdlZCBieSBzZXR0aW5nICpgYWZ0ZXJJbnZvY2F0aW9uYCogdG8gZmFsc2UgaW4gd2hpY2ggY2FzZSAqQ2FjaGUuZXZpY3Qoa2V5KSpcbiAqIHdpbGwgYmUgY2FsbGVkIGJlZm9yZSB0aGUgYW5ub3RhdGVkIG1ldGhvZCBpcyBpbnZva2VkLlxuICpcbiAqIEBwYXJhbSBwYXJhbXMgKE9wdGlvbmFsKSB7Y2FjaGVOYW1lPzogc3RyaW5nLCBhZnRlckludm9jYXRpb246IGJvb2xlYW4gPSB0cnVlfVxuICovXG5leHBvcnQgZnVuY3Rpb24gQ2FjaGVSZW1vdmUocGFyYW1zPzogQ2FjaGVQYXJhbXNJbnZvYyk6IE1ldGhvZERlY29yYXRvciB7XG4gIHBhcmFtcyA9IGdldERlZmF1bHRQYXJhbXM8Q2FjaGVQYXJhbXNJbnZvYz4ocGFyYW1zKTtcblxuICByZXR1cm4gKHRhcmdldDogT2JqZWN0LCBwcm9wZXJ0eUtleTogc3RyaW5nIHwgc3ltYm9sLCBkZXNjcmlwdG9yOiBUeXBlZFByb3BlcnR5RGVzY3JpcHRvcjxhbnk+KTogVHlwZWRQcm9wZXJ0eURlc2NyaXB0b3I8YW55PiA9PiB7XG4gICAgY29uc3Qgb3JpZ2luYWxNZXRob2QgPSBkZXNjcmlwdG9yLnZhbHVlO1xuICAgIGRlc2NyaXB0b3IudmFsdWUgPSBmdW5jdGlvbiguLi5hcmdzOiBhbnlbXSkge1xuICAgICAgY29uc3QgY2FjaGU6IENhY2hlID0gZ2V0Q2FjaGUodGFyZ2V0LCBwYXJhbXMpO1xuICAgICAgY29uc3QgY2FjaGVLZXk6IHN0cmluZyA9IGdldENhY2hlS2V5KHRhcmdldCwgcHJvcGVydHlLZXksIGFyZ3MpO1xuXG4gICAgICBpZiAoIXBhcmFtcy5hZnRlckludm9jYXRpb24pIHtcbiAgICAgICAgY2FjaGUuZXZpY3QoY2FjaGVLZXkpO1xuICAgICAgfVxuXG4gICAgICBjb25zdCByZXN1bHQ6IGFueSA9IG9yaWdpbmFsTWV0aG9kLmFwcGx5KHRoaXMsIGFyZ3MpO1xuXG5cbiAgICAgIGlmIChwYXJhbXMuYWZ0ZXJJbnZvY2F0aW9uKSB7XG4gICAgICAgIGNhY2hlLmV2aWN0KGNhY2hlS2V5KTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9O1xuICAgIHJldHVybiBkZXNjcmlwdG9yO1xuICB9O1xufVxuXG4vKipcbiAqIFdoZW4gYSBtZXRob2QgYW5ub3RhdGVkIHdpdGggYENhY2hlUmVtb3ZlQWxsYCBpcyBpbnZva2VkIGFsbCBlbGVtZW50cyBpbiB0aGUgc3BlY2lmaWVkIGNhY2hlIHdpbGwgYmUgcmVtb3ZlZCB2aWEgdGhlICpDYWNoZS5jbGVhcigpKiBtZXRob2QuXG4gKiBUaGUgZGVmYXVsdCBiZWhhdmlvciBpcyB0byBjYWxsICpDYWNoZS5jbGVhcigpKiBhZnRlciB0aGUgYW5ub3RhdGVkIG1ldGhvZCBpcyBpbnZva2VkLFxuICogdGhpcyBiZWhhdmlvciBjYW4gYmUgY2hhbmdlZCBieSBzZXR0aW5nICpgYWZ0ZXJJbnZvY2F0aW9uYCogdG8gZmFsc2UgaW4gd2hpY2ggY2FzZSAqQ2FjaGUuY2xlYXIoKSogd2lsbCBiZSBjYWxsZWQgYmVmb3JlIHRoZSBhbm5vdGF0ZWQgbWV0aG9kIGlzIGludm9rZWQuXG4gKlxuICogQHBhcmFtIHBhcmFtcyAoT3B0aW9uYWwpIHtjYWNoZU5hbWU/OiBzdHJpbmcsIGFmdGVySW52b2NhdGlvbjogYm9vbGVhbiA9IHRydWV9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBDYWNoZVJlbW92ZUFsbChwYXJhbXM/OiBDYWNoZVBhcmFtc0ludm9jKTogTWV0aG9kRGVjb3JhdG9yIHtcbiAgcGFyYW1zID0gZ2V0RGVmYXVsdFBhcmFtczxDYWNoZVBhcmFtc0ludm9jPihwYXJhbXMpO1xuXG4gIHJldHVybiAodGFyZ2V0OiBPYmplY3QsIHByb3BlcnR5S2V5OiBzdHJpbmcgfCBzeW1ib2wsIGRlc2NyaXB0b3I6IFR5cGVkUHJvcGVydHlEZXNjcmlwdG9yPGFueT4pOiBUeXBlZFByb3BlcnR5RGVzY3JpcHRvcjxhbnk+ID0+IHtcbiAgICBjb25zdCBvcmlnaW5hbE1ldGhvZCA9IGRlc2NyaXB0b3IudmFsdWU7XG4gICAgZGVzY3JpcHRvci52YWx1ZSA9IGZ1bmN0aW9uKC4uLmFyZ3M6IGFueVtdKSB7XG4gICAgICBjb25zdCBjYWNoZTogQ2FjaGUgPSBnZXRDYWNoZSh0YXJnZXQsIHBhcmFtcyk7XG4gICAgICBpZiAoIXBhcmFtcy5hZnRlckludm9jYXRpb24pIHtcbiAgICAgICAgY2FjaGUuY2xlYXIoKTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgcmVzdWx0OiBhbnkgPSBvcmlnaW5hbE1ldGhvZC5hcHBseSh0aGlzLCBhcmdzKTtcblxuICAgICAgaWYgKHBhcmFtcy5hZnRlckludm9jYXRpb24pIHtcbiAgICAgICAgY2FjaGUuY2xlYXIoKTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9O1xuICAgIHJldHVybiBkZXNjcmlwdG9yO1xuICB9O1xufVxuXG5mdW5jdGlvbiBnZXREZWZhdWx0UGFyYW1zPFQ+KGNhY2hlUGFyYW1zOiBDYWNoZVBhcmFtcyk6IHsgYWZ0ZXJJbnZvY2F0aW9uOiBib29sZWFuIH0gJiBDYWNoZVBhcmFtcyB7XG4gIHJldHVybiBPYmplY3QuYXNzaWduKHtcbiAgICBhZnRlckludm9jYXRpb246IHRydWVcbiAgfSwgY2FjaGVQYXJhbXMgfHwge30pO1xufVxuXG5mdW5jdGlvbiBnZXRDYWNoZSh0YXJnZXQ6IE9iamVjdCwgcGFyYW1zOiBDYWNoZVBhcmFtcyk6IENhY2hlIHtcbiAgaWYgKCFwYXJhbXMuY2FjaGVOYW1lKSB7XG4gICAgcGFyYW1zLmNhY2hlTmFtZSA9IFJlZmxlY3QuZ2V0TWV0YWRhdGEoTUVUQURBVEFfS0VZX0NBQ0hFX0RFRkFVTFRTLCB0YXJnZXQuY29uc3RydWN0b3IpIHx8ICcnO1xuICB9XG5cbiAgY29uc3QgY2FjaGU6IENhY2hlID0gZ2V0Q2FjaGVNYW5hZ2VyKCkuZ2V0Q2FjaGUocGFyYW1zLmNhY2hlTmFtZSk7XG4gIGlmICghY2FjaGUpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYENhY2hlICcke3BhcmFtcy5jYWNoZU5hbWV9JyBub3QgZm91bmQgZm9yICR7dGFyZ2V0LmNvbnN0cnVjdG9yLm5hbWV9YCk7XG4gIH1cbiAgcmV0dXJuIGNhY2hlO1xufVxuXG5mdW5jdGlvbiBnZXRDYWNoZUtleSh0YXJnZXQ6IE9iamVjdCwgcHJvcGVydHlLZXk6IHN0cmluZyB8IHN5bWJvbCwgYXJnczogYW55W10sIGNhY2hlVmFsdWVJbmRleCA9IC0xKTogc3RyaW5nIHtcbiAgaWYgKCFhcmdzKSB7XG4gICAgYXJncyA9IFtdO1xuICB9XG5cbiAgY29uc3QgaW5kaWNlczogbnVtYmVyW10gPSBSZWZsZWN0LmdldE1ldGFkYXRhKGAke01FVEFEQVRBX0tFWV9DQUNIRV9LRVlTfV8ke3Byb3BlcnR5S2V5LnRvU3RyaW5nKCl9YCwgdGFyZ2V0LCBwcm9wZXJ0eUtleSk7XG4gIGlmIChpbmRpY2VzKSB7XG4gICAgYXJncyA9IGFyZ3MuZmlsdGVyKCh2YWx1ZTogYW55LCBpbmRleDogbnVtYmVyKSA9PiBpbmRpY2VzLmluZGV4T2YoaW5kZXgpICE9PSAtMSAmJiBjYWNoZVZhbHVlSW5kZXggIT09IGluZGV4KTtcbiAgfSBlbHNlIGlmIChjYWNoZVZhbHVlSW5kZXggIT09IC0xKSB7XG4gICAgYXJncyA9IGFyZ3MuZmlsdGVyKCh2YWx1ZTogYW55LCBpbmRleDogbnVtYmVyKSA9PiBjYWNoZVZhbHVlSW5kZXggIT09IGluZGV4KTtcbiAgfVxuXG4gIGlmIChhcmdzLmxlbmd0aCA9PT0gMCkge1xuICAgIHRocm93IG5ldyBFcnJvcihgQ291bGRuJ3QgZ2VuZXJhdGUga2V5IHdpdGhvdXQgcGFyYW1zIGZvciAnJHtwcm9wZXJ0eUtleS50b1N0cmluZygpfScgbWV0aG9kIG9mICR7dGFyZ2V0LmNvbnN0cnVjdG9yLm5hbWV9YCk7XG4gIH1cblxuICByZXR1cm4gYXJncy5tYXAoYSA9PiAoSlNPTi5zdHJpbmdpZnkoYSkgfHwgYS50b1N0cmluZygpKSkuam9pbignfCcpO1xufVxuIl19