chain-able
Version:
interfaces that describe their intentions.
325 lines (300 loc) • 29.3 kB
JavaScript
/* eslint complexity: "OFF" */
var MethodChain = require('./MethodChain')
var ChainedMapBase = require('./ChainedMapBase')
var dopemerge = require('./deps/dopemerge')
var isFunction = require('./deps/is/function')
var isUndefined = require('./deps/is/undefined')
var isTrue = require('./deps/is/true')
var isMapish = require('./deps/is/mapish')
var ObjectKeys = require('./deps/util/keys')
var SHORTHANDS_KEY = require('./deps/meta/shorthands')
var ENV_DEVELOPMENT = require('./deps/env/dev')
var ENV_DEBUG = require('./deps/env/debug')
var ON_EXISTING_KEY = 'onExisting'
var ON_VALUE_KEY = 'onValue'
var MERGER_KEY = 'merger'
var MERGER_OPTIONS_KEY = 'opts'
var OBJ_KEY = 'obj'
/**
* @since 1.0.0
* @type {Map}
* @extends {ChainedMapBase}
* @member MergeChain
* @memberOf Chainable
*
* @types MergeChain
* @tests MergeChain
* @see deps/dopemerge
*
* {@link https://sourcemaking.com/design_patterns/visitor visitor-pattern}
*
* @TODO consider just making this a function,
* because 80/20 onValue merger & onExisting
* are rarely used & are easily overridable with .merge
*/
var MergeChain = (function (ChainedMapBase) {
function MergeChain(parent) {
ChainedMapBase.call(this, parent)
/* prettier-ignore */
this
.extend([ON_EXISTING_KEY, ON_VALUE_KEY, OBJ_KEY])
.set(ON_VALUE_KEY, function () { return true; })
.set(MERGER_KEY, dopemerge)
}
if ( ChainedMapBase ) MergeChain.__proto__ = ChainedMapBase;
MergeChain.prototype = Object.create( ChainedMapBase && ChainedMapBase.prototype );
MergeChain.prototype.constructor = MergeChain;
/**
* @desc options for merging with dopemerge
* @modifies this.merger | this.opts
*
* @memberOf MergeChain
* @since 1.0.2
* @param {Object | Function} opts when object: options for the merger. when function: is the merger
* @return {MergeChain} @chainable
* @see dopemerge
*
* @example
* {
* stringToArray: true,
* boolToArray: false,
* boolAsRight: true,
* ignoreTypes: ['null', 'undefined', 'NaN'],
* debug: false,
* }
*
* @example
* .merger(require('lodash.mergewith')())
*/
MergeChain.init = function init (parent) {
return new MergeChain(parent)
};
MergeChain.prototype.merger = function merger (opts) {
if (isFunction(opts)) { return this.set(MERGER_KEY, opts) }
return this.set(MERGER_OPTIONS_KEY, opts)
};
// [v] messes comments on conditional brace style
/* prettier-ignore */
/**
* @desc merges object in, goes through all keys, checks cbs, dopemerges
*
* @since 1.0.0
*
* @param {Object} [obj2=undefined] object to merge in, defaults to this.get('obj')
* @return {MergeChain} @chainable
*
* @see ChainedMap
* @TODO issue here if we extend without shorthands &
* we want to merge existing values... :s
*
*
* @example
*
* const chain = new Chain()
* chain.merge({canada: {eh: true}})
* chain.merge({canada: {arr: [0, {'1': 2}], eh: {again: true}}})
* chain.entries()
* //=> {canada:{ eh: {again: true}, arr: [0, {'1': 2}] }}
*/
MergeChain.prototype.merge = function merge (obj2) {
var this$1 = this;
// better uglifying
var parent = this.parent
var get = function (key) { return this$1.get(key); }
var onExisting = get(ON_EXISTING_KEY)
var onValue = get(ON_VALUE_KEY)
var opts = get(MERGER_OPTIONS_KEY)
var obj = obj2 || get(OBJ_KEY)
var merger = get(MERGER_KEY)
var shorthands = parent.meta ? parent.meta(SHORTHANDS_KEY) : {}
var keys = ObjectKeys(obj)
// @@debugger
/* istanbul ignore next: devs */
if (ENV_DEVELOPMENT) {
if (!obj) {
console.log({onExisting: onExisting, opts: opts, obj: obj, merger: merger, shorthands: shorthands, keys: keys, parent: parent})
throw new Error('must provide an object to merge')
}
}
/**
* @private
*
* since this would be slower
* if I want to not have a speedy default when using .onExisting
* should @note to use .extend
* when using chains without a class & doing .merge (edge-case)
*
* @param {Primitive} key key (shorthands[key] or just key)
* @param {*} value obj[key]
* @return {void}
*
* @TODO could use .eq here
* @TODO if (isMapish(obj)) obj = obj.entries()
*
* @example
* var obj = {key: 1}
*
* MergeChain.init(obj).merge({key: ['value']})
*
* // goes to this internal scoped function
* handleExisting('key', ['value'])
* // if there is .onValue or .onExisting, use them, default deepmerge
*
* obj
* //=> {key: [1, 'value']}
*
*/
var handleExisting = function (key, value) {
/**
* @desc when fn is a full method, not an extended shorthand
* @since 0.5.0
*
* @param {Primitive} keyToSet key we chose to set
* @param {*} valueToSet value we chose to set (merged, existing, new)
* @return {Parent | Chain | *} .set or [keyToSet] return
*
* @example
*
* MergeChain.init(new Chain().extend(['eh']))
*
* //isFunction: true => call parent[keyToSet](valueToSet)
* setChosen('eh', 1)
* //=> parent
* parent.get('eh')
* //=> 1
*
* //=>isFunction: false => parent.set(keyToSet, valueToSet)
* setChosen('oh', 1)
* //=> parent //<- unless .set is overriden
* parent.get('oh')
* //=> 1
*/
var setChosen = function (keyToSet, valueToSet) { return (isFunction(parent[key])
? parent[keyToSet](valueToSet)
: parent.set(keyToSet, valueToSet)); }
/**
* check if it's shorthanded
* -> check if it has a value already
*/
if (isTrue(parent.has(key))) {
// get that value
var existing = parent.get(key)
/**
* if we have onExisting, call it
* else default to dopemerge
*/
if (isUndefined(onExisting)) {
/* istanbul ignore next: devs */
if (ENV_DEBUG) {
console.log(
'parent has: no onExisting',
{existing: existing, [key]: value}
)
}
setChosen(key, merger(existing, value, opts))
}
else {
/* istanbul ignore next: devs */
if (ENV_DEBUG) {
console.log(
'parent has: has onExisting',
{existing: existing, onExisting: onExisting, [key]: value}
)
}
/**
* maybe we should not even have `.onExisting`
* since we can just override merge method...
* and then client can just use a custom merger...
*
* could add and remove subscriber but that's overhead and
* tricky here, because if we set a value that was just set...
*/
setChosen(key, onExisting(existing, value, opts))
}
}
else {
/* istanbul ignore next: devs */
if (ENV_DEBUG) {
console.log('parent does not have', {[key]: value})
}
setChosen(key, value)
}
}
for (var k = 0, len = keys.length; k < len; k++) {
// key to the current property in the data being merged
var key = keys[k]
// we have our value, no we can change the key if needed for shorthands
var value = obj[key]
// @NOTE: when shorthands is an object, key is the method it should call
if (!isUndefined(shorthands[key]) && shorthands[key] !== key) {
/* istanbul ignore next: devs */
if (ENV_DEBUG) {
console.log(
'had a shorthand with a diff key than the object (likely @alias)',
{shorthandMethod: shorthands[key], key: key, value: value}
)
}
key = shorthands[key]
}
// method for the key
var method = parent[key]
/* istanbul ignore next: sourcemaps trigger istanbul here incorrectly */
// use onValue when set
if (!onValue(value, key, this$1)) {
/* istanbul ignore next: devs */
if (ENV_DEBUG) {
console.log('had onValue, was false, ignored', {onValue: onValue, key: key, value: value})
}
continue
}
// when property itself is a Chainable
else if (isMapish(method)) {
/* istanbul ignore next: devs */
if (ENV_DEBUG) {
console.log('has method or shorthand')
}
parent[key].merge(value)
}
// we have a method or shorthand
else if (method) {
/* istanbul ignore next: devs */
if (ENV_DEBUG) {
console.log('has method or shorthand', {method: method, key: key, value: value})
}
handleExisting(key, value)
}
// default to .set on the store
else {
/* istanbul ignore next: devs */
if (ENV_DEBUG) {
console.log('went to default', {method: method, key: key, value: value})
}
parent.set(key, value)
}
}
return parent
};
return MergeChain;
}(ChainedMapBase));
/**
* @memberOf MergeChain
* @method onExisting
* @since 0.9.0
* @example
* const {Chain, MergeChain} = require('chain-able')
*
* const chain = new Chain().set('str', 'stringy')
*
* MergeChain.init(chain)
* .onExisting((a, b) => a + b)
* .merge({str: '+'})
*
* chain.get('str')
* //=> 'stringy+'
*/
module.exports = MergeChain
// @TODO re-enable this later
// module.exports = new MethodChain(MergeChain.prototype)
// .methods(['onExisting', 'onValue', 'obj'])
// .build(MergeChain)
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWVyZ2VDaGFpbi5qcyIsInNvdXJjZXMiOlsiTWVyZ2VDaGFpbi5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKiBlc2xpbnQgY29tcGxleGl0eTogXCJPRkZcIiAqL1xuY29uc3QgTWV0aG9kQ2hhaW4gPSByZXF1aXJlKCcuL01ldGhvZENoYWluJylcbmNvbnN0IENoYWluZWRNYXBCYXNlID0gcmVxdWlyZSgnLi9DaGFpbmVkTWFwQmFzZScpXG5jb25zdCBkb3BlbWVyZ2UgPSByZXF1aXJlKCcuL2RlcHMvZG9wZW1lcmdlJylcbmNvbnN0IGlzRnVuY3Rpb24gPSByZXF1aXJlKCcuL2RlcHMvaXMvZnVuY3Rpb24nKVxuY29uc3QgaXNVbmRlZmluZWQgPSByZXF1aXJlKCcuL2RlcHMvaXMvdW5kZWZpbmVkJylcbmNvbnN0IGlzVHJ1ZSA9IHJlcXVpcmUoJy4vZGVwcy9pcy90cnVlJylcbmNvbnN0IGlzTWFwaXNoID0gcmVxdWlyZSgnLi9kZXBzL2lzL21hcGlzaCcpXG5jb25zdCBPYmplY3RLZXlzID0gcmVxdWlyZSgnLi9kZXBzL3V0aWwva2V5cycpXG5jb25zdCBTSE9SVEhBTkRTX0tFWSA9IHJlcXVpcmUoJy4vZGVwcy9tZXRhL3Nob3J0aGFuZHMnKVxuY29uc3QgRU5WX0RFVkVMT1BNRU5UID0gcmVxdWlyZSgnLi9kZXBzL2Vudi9kZXYnKVxuY29uc3QgRU5WX0RFQlVHID0gcmVxdWlyZSgnLi9kZXBzL2Vudi9kZWJ1ZycpXG5cbmNvbnN0IE9OX0VYSVNUSU5HX0tFWSA9ICdvbkV4aXN0aW5nJ1xuY29uc3QgT05fVkFMVUVfS0VZID0gJ29uVmFsdWUnXG5jb25zdCBNRVJHRVJfS0VZID0gJ21lcmdlcidcbmNvbnN0IE1FUkdFUl9PUFRJT05TX0tFWSA9ICdvcHRzJ1xuY29uc3QgT0JKX0tFWSA9ICdvYmonXG5cbi8qKlxuICogQHNpbmNlIDEuMC4wXG4gKiBAdHlwZSB7TWFwfVxuICogQGV4dGVuZHMge0NoYWluZWRNYXBCYXNlfVxuICogQG1lbWJlciBNZXJnZUNoYWluXG4gKiBAbWVtYmVyT2YgQ2hhaW5hYmxlXG4gKlxuICogQHR5cGVzIE1lcmdlQ2hhaW5cbiAqIEB0ZXN0cyBNZXJnZUNoYWluXG4gKiBAc2VlIGRlcHMvZG9wZW1lcmdlXG4gKlxuICoge0BsaW5rIGh0dHBzOi8vc291cmNlbWFraW5nLmNvbS9kZXNpZ25fcGF0dGVybnMvdmlzaXRvciB2aXNpdG9yLXBhdHRlcm59XG4gKlxuICogQFRPRE8gY29uc2lkZXIganVzdCBtYWtpbmcgdGhpcyBhIGZ1bmN0aW9uLFxuICogICAgICAgYmVjYXVzZSA4MC8yMCBvblZhbHVlIG1lcmdlciAmIG9uRXhpc3RpbmdcbiAqICAgICAgIGFyZSByYXJlbHkgdXNlZCAmIGFyZSBlYXNpbHkgb3ZlcnJpZGFibGUgd2l0aCAubWVyZ2VcbiAqL1xuY2xhc3MgTWVyZ2VDaGFpbiBleHRlbmRzIENoYWluZWRNYXBCYXNlIHtcbiAgLyoqXG4gICAqIEBzdGF0aWNcbiAgICogQHBhcmFtICB7Q2hhaW5hYmxlIHwgUGFyZW50VHlwZX0gcGFyZW50IFBhcmVudFR5cGUgcmVxdWlyZWQsIGZvciBtZXJnaW5nXG4gICAqIEByZXR1cm4ge01lcmdlQ2hhaW59IEBjaGFpbmFibGVcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICpcbiAgICogICAgbGV0IG1hcCA9IG5ldyBNYXAoKVxuICAgKiAgICBtYXAuc2V0KCdlaCcsIDEpXG4gICAqICAgIG1hcC5zZXQoJ2NvbycsICdvbycpXG4gICAqXG4gICAqICAgIE1lcmdlQ2hhaW4uaW5pdChtYXApLm1lcmdlKHtlaDogMn0pXG4gICAqICAgIGNvbnNvbGUuZGlyKG1hcClcbiAgICogICAgLy89PiBNYXAgeyAnZWgnID0+IDIsICdjb28nID0+ICdvbycgfVxuICAgKlxuICAgKi9cbiAgc3RhdGljIGluaXQocGFyZW50KSB7XG4gICAgcmV0dXJuIG5ldyBNZXJnZUNoYWluKHBhcmVudClcbiAgfVxuXG4gIC8qKlxuICAgKiBAaW5oZXJpdGRvY1xuICAgKi9cbiAgY29uc3RydWN0b3IocGFyZW50KSB7XG4gICAgc3VwZXIocGFyZW50KVxuXG4gICAgLyogcHJldHRpZXItaWdub3JlICovXG4gICAgdGhpc1xuICAgICAgLmV4dGVuZChbT05fRVhJU1RJTkdfS0VZLCBPTl9WQUxVRV9LRVksIE9CSl9LRVldKVxuICAgICAgLnNldChPTl9WQUxVRV9LRVksICgpID0+IHRydWUpXG4gICAgICAuc2V0KE1FUkdFUl9LRVksIGRvcGVtZXJnZSlcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzYyBvcHRpb25zIGZvciBtZXJnaW5nIHdpdGggZG9wZW1lcmdlXG4gICAqICAgICAgIEBtb2RpZmllcyB0aGlzLm1lcmdlciB8IHRoaXMub3B0c1xuICAgKlxuICAgKiBAbWVtYmVyT2YgTWVyZ2VDaGFpblxuICAgKiBAc2luY2UgMS4wLjJcbiAgICogQHBhcmFtICB7T2JqZWN0IHwgRnVuY3Rpb259IG9wdHMgd2hlbiBvYmplY3Q6IG9wdGlvbnMgZm9yIHRoZSBtZXJnZXIuIHdoZW4gZnVuY3Rpb246IGlzIHRoZSBtZXJnZXJcbiAgICogQHJldHVybiB7TWVyZ2VDaGFpbn0gQGNoYWluYWJsZVxuICAgKiBAc2VlIGRvcGVtZXJnZVxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiAgIHtcbiAgICogICAgIHN0cmluZ1RvQXJyYXk6IHRydWUsXG4gICAqICAgICBib29sVG9BcnJheTogZmFsc2UsXG4gICAqICAgICBib29sQXNSaWdodDogdHJ1ZSxcbiAgICogICAgIGlnbm9yZVR5cGVzOiBbJ251bGwnLCAndW5kZWZpbmVkJywgJ05hTiddLFxuICAgKiAgICAgZGVidWc6IGZhbHNlLFxuICAgKiAgIH1cbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogICAgLm1lcmdlcihyZXF1aXJlKCdsb2Rhc2gubWVyZ2V3aXRoJykoKSlcbiAgICovXG4gIG1lcmdlcihvcHRzKSB7XG4gICAgaWYgKGlzRnVuY3Rpb24ob3B0cykpIHJldHVybiB0aGlzLnNldChNRVJHRVJfS0VZLCBvcHRzKVxuICAgIHJldHVybiB0aGlzLnNldChNRVJHRVJfT1BUSU9OU19LRVksIG9wdHMpXG4gIH1cblxuICAvLyBbdl0gbWVzc2VzIGNvbW1lbnRzIG9uIGNvbmRpdGlvbmFsIGJyYWNlIHN0eWxlXG4gIC8qIHByZXR0aWVyLWlnbm9yZSAqL1xuICAvKipcbiAgICogQGRlc2MgbWVyZ2VzIG9iamVjdCBpbiwgZ29lcyB0aHJvdWdoIGFsbCBrZXlzLCBjaGVja3MgY2JzLCBkb3BlbWVyZ2VzXG4gICAqXG4gICAqIEBzaW5jZSAxLjAuMFxuICAgKlxuICAgKiBAcGFyYW0gIHtPYmplY3R9IFtvYmoyPXVuZGVmaW5lZF0gb2JqZWN0IHRvIG1lcmdlIGluLCBkZWZhdWx0cyB0byB0aGlzLmdldCgnb2JqJylcbiAgICogQHJldHVybiB7TWVyZ2VDaGFpbn0gQGNoYWluYWJsZVxuICAgKlxuICAgKiBAc2VlIENoYWluZWRNYXBcbiAgICogQFRPRE8gaXNzdWUgaGVyZSBpZiB3ZSBleHRlbmQgd2l0aG91dCBzaG9ydGhhbmRzICZcbiAgICogICAgICAgd2Ugd2FudCB0byBtZXJnZSBleGlzdGluZyB2YWx1ZXMuLi4gOnNcbiAgICpcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICpcbiAgICogIGNvbnN0IGNoYWluID0gbmV3IENoYWluKClcbiAgICogIGNoYWluLm1lcmdlKHtjYW5hZGE6IHtlaDogdHJ1ZX19KVxuICAgKiAgY2hhaW4ubWVyZ2Uoe2NhbmFkYToge2FycjogWzAsIHsnMSc6IDJ9XSwgZWg6IHthZ2FpbjogdHJ1ZX19fSlcbiAgICogIGNoYWluLmVudHJpZXMoKVxuICAgKiAgLy89PiB7Y2FuYWRhOnsgZWg6IHthZ2FpbjogdHJ1ZX0sIGFycjogWzAsIHsnMSc6IDJ9XSB9fVxuICAgKi9cbiAgbWVyZ2Uob2JqMikge1xuICAgIC8vIGJldHRlciB1Z2xpZnlpbmdcbiAgICBjb25zdCBwYXJlbnQgPSB0aGlzLnBhcmVudFxuICAgIGNvbnN0IGdldCA9IGtleSA9PiB0aGlzLmdldChrZXkpXG5cbiAgICBjb25zdCBvbkV4aXN0aW5nID0gZ2V0KE9OX0VYSVNUSU5HX0tFWSlcbiAgICBjb25zdCBvblZhbHVlID0gZ2V0KE9OX1ZBTFVFX0tFWSlcbiAgICBjb25zdCBvcHRzID0gZ2V0KE1FUkdFUl9PUFRJT05TX0tFWSlcbiAgICBjb25zdCBvYmogPSBvYmoyIHx8IGdldChPQkpfS0VZKVxuICAgIGNvbnN0IG1lcmdlciA9IGdldChNRVJHRVJfS0VZKVxuICAgIGNvbnN0IHNob3J0aGFuZHMgPSBwYXJlbnQubWV0YSA/IHBhcmVudC5tZXRhKFNIT1JUSEFORFNfS0VZKSA6IHt9XG4gICAgY29uc3Qga2V5cyA9IE9iamVjdEtleXMob2JqKVxuXG4gICAgLy8gQEBkZWJ1Z2dlclxuXG4gICAgLyogaXN0YW5idWwgaWdub3JlIG5leHQ6IGRldnMgKi9cbiAgICBpZiAoRU5WX0RFVkVMT1BNRU5UKSB7XG4gICAgICBpZiAoIW9iaikge1xuICAgICAgICBjb25zb2xlLmxvZyh7b25FeGlzdGluZywgb3B0cywgb2JqLCBtZXJnZXIsIHNob3J0aGFuZHMsIGtleXMsIHBhcmVudH0pXG4gICAgICAgIHRocm93IG5ldyBFcnJvcignbXVzdCBwcm92aWRlIGFuIG9iamVjdCB0byBtZXJnZScpXG4gICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQHByaXZhdGVcbiAgICAgKlxuICAgICAqIHNpbmNlIHRoaXMgd291bGQgYmUgc2xvd2VyXG4gICAgICogaWYgSSB3YW50IHRvIG5vdCBoYXZlIGEgc3BlZWR5IGRlZmF1bHQgd2hlbiB1c2luZyAub25FeGlzdGluZ1xuICAgICAqIHNob3VsZCBAbm90ZSB0byB1c2UgLmV4dGVuZFxuICAgICAqIHdoZW4gdXNpbmcgY2hhaW5zIHdpdGhvdXQgYSBjbGFzcyAmIGRvaW5nIC5tZXJnZSAoZWRnZS1jYXNlKVxuICAgICAqXG4gICAgICogQHBhcmFtICB7UHJpbWl0aXZlfSBrZXkga2V5IChzaG9ydGhhbmRzW2tleV0gb3IganVzdCBrZXkpXG4gICAgICogQHBhcmFtICB7Kn0gdmFsdWUgb2JqW2tleV1cbiAgICAgKiBAcmV0dXJuIHt2b2lkfVxuICAgICAqXG4gICAgICogQFRPRE8gY291bGQgdXNlIC5lcSBoZXJlXG4gICAgICogQFRPRE8gaWYgKGlzTWFwaXNoKG9iaikpIG9iaiA9IG9iai5lbnRyaWVzKClcbiAgICAgKlxuICAgICAqIEBleGFtcGxlXG4gICAgICogIHZhciBvYmogPSB7a2V5OiAxfVxuICAgICAqXG4gICAgICogIE1lcmdlQ2hhaW4uaW5pdChvYmopLm1lcmdlKHtrZXk6IFsndmFsdWUnXX0pXG4gICAgICpcbiAgICAgKiAgLy8gZ29lcyB0byB0aGlzIGludGVybmFsIHNjb3BlZCBmdW5jdGlvblxuICAgICAqICBoYW5kbGVFeGlzdGluZygna2V5JywgWyd2YWx1ZSddKVxuICAgICAqICAvLyBpZiB0aGVyZSBpcyAub25WYWx1ZSBvciAub25FeGlzdGluZywgdXNlIHRoZW0sIGRlZmF1bHQgZGVlcG1lcmdlXG4gICAgICpcbiAgICAgKiAgb2JqXG4gICAgICogIC8vPT4ge2tleTogWzEsICd2YWx1ZSddfVxuICAgICAqXG4gICAgICovXG4gICAgY29uc3QgaGFuZGxlRXhpc3RpbmcgPSAoa2V5LCB2YWx1ZSkgPT4ge1xuICAgICAgLyoqXG4gICAgICAgKiBAZGVzYyB3aGVuIGZuIGlzIGEgZnVsbCBtZXRob2QsIG5vdCBhbiBleHRlbmRlZCBzaG9ydGhhbmRcbiAgICAgICAqIEBzaW5jZSAwLjUuMFxuICAgICAgICpcbiAgICAgICAqIEBwYXJhbSB7UHJpbWl0aXZlfSBrZXlUb1NldCBrZXkgd2UgY2hvc2UgdG8gc2V0XG4gICAgICAgKiBAcGFyYW0geyp9IHZhbHVlVG9TZXQgdmFsdWUgd2UgY2hvc2UgdG8gc2V0IChtZXJnZWQsIGV4aXN0aW5nLCBuZXcpXG4gICAgICAgKiBAcmV0dXJuIHtQYXJlbnQgfCBDaGFpbiB8ICp9IC5zZXQgb3IgW2tleVRvU2V0XSByZXR1cm5cbiAgICAgICAqXG4gICAgICAgKiBAZXhhbXBsZVxuICAgICAgICpcbiAgICAgICAqICAgIE1lcmdlQ2hhaW4uaW5pdChuZXcgQ2hhaW4oKS5leHRlbmQoWydlaCddKSlcbiAgICAgICAqXG4gICAgICAgKiAgICAvL2lzRnVuY3Rpb246IHRydWUgPT4gY2FsbCBwYXJlbnRba2V5VG9TZXRdKHZhbHVlVG9TZXQpXG4gICAgICAgKiAgICBzZXRDaG9zZW4oJ2VoJywgMSlcbiAgICAgICAqICAgIC8vPT4gcGFyZW50XG4gICAgICAgKiAgICBwYXJlbnQuZ2V0KCdlaCcpXG4gICAgICAgKiAgICAvLz0+IDFcbiAgICAgICAqXG4gICAgICAgKiAgICAvLz0+aXNGdW5jdGlvbjogZmFsc2UgPT4gcGFyZW50LnNldChrZXlUb1NldCwgdmFsdWVUb1NldClcbiAgICAgICAqICAgIHNldENob3Nlbignb2gnLCAxKVxuICAgICAgICogICAgLy89PiBwYXJlbnQgLy88LSB1bmxlc3MgLnNldCBpcyBvdmVycmlkZW5cbiAgICAgICAqICAgIHBhcmVudC5nZXQoJ29oJylcbiAgICAgICAqICAgIC8vPT4gMVxuICAgICAgICovXG4gICAgICBjb25zdCBzZXRDaG9zZW4gPSAoa2V5VG9TZXQsIHZhbHVlVG9TZXQpID0+XG4gICAgICAgIChpc0Z1bmN0aW9uKHBhcmVudFtrZXldKVxuICAgICAgICAgID8gcGFyZW50W2tleVRvU2V0XSh2YWx1ZVRvU2V0KVxuICAgICAgICAgIDogcGFyZW50LnNldChrZXlUb1NldCwgdmFsdWVUb1NldCkpXG5cbiAgICAgIC8qKlxuICAgICAgICogY2hlY2sgaWYgaXQncyBzaG9ydGhhbmRlZFxuICAgICAgICogLT4gY2hlY2sgaWYgaXQgaGFzIGEgdmFsdWUgYWxyZWFkeVxuICAgICAgICovXG4gICAgICBpZiAoaXNUcnVlKHBhcmVudC5oYXMoa2V5KSkpIHtcbiAgICAgICAgLy8gZ2V0IHRoYXQgdmFsdWVcbiAgICAgICAgY29uc3QgZXhpc3RpbmcgPSBwYXJlbnQuZ2V0KGtleSlcblxuICAgICAgICAvKipcbiAgICAgICAgICogaWYgd2UgaGF2ZSBvbkV4aXN0aW5nLCBjYWxsIGl0XG4gICAgICAgICAqIGVsc2UgZGVmYXVsdCB0byBkb3BlbWVyZ2VcbiAgICAgICAgICovXG4gICAgICAgIGlmIChpc1VuZGVmaW5lZChvbkV4aXN0aW5nKSkge1xuICAgICAgICAgIC8qIGlzdGFuYnVsIGlnbm9yZSBuZXh0OiBkZXZzICovXG4gICAgICAgICAgaWYgKEVOVl9ERUJVRykge1xuICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICdwYXJlbnQgaGFzOiBubyBvbkV4aXN0aW5nJyxcbiAgICAgICAgICAgICAge2V4aXN0aW5nLCBba2V5XTogdmFsdWV9XG4gICAgICAgICAgICApXG4gICAgICAgICAgfVxuICAgICAgICAgIHNldENob3NlbihrZXksIG1lcmdlcihleGlzdGluZywgdmFsdWUsIG9wdHMpKVxuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgIC8qIGlzdGFuYnVsIGlnbm9yZSBuZXh0OiBkZXZzICovXG4gICAgICAgICAgaWYgKEVOVl9ERUJVRykge1xuICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICdwYXJlbnQgaGFzOiBoYXMgb25FeGlzdGluZycsXG4gICAgICAgICAgICAgIHtleGlzdGluZywgb25FeGlzdGluZywgW2tleV06IHZhbHVlfVxuICAgICAgICAgICAgKVxuICAgICAgICAgIH1cbiAgICAgICAgICAvKipcbiAgICAgICAgICAgKiBtYXliZSB3ZSBzaG91bGQgbm90IGV2ZW4gaGF2ZSBgLm9uRXhpc3RpbmdgXG4gICAgICAgICAgICogc2luY2Ugd2UgY2FuIGp1c3Qgb3ZlcnJpZGUgbWVyZ2UgbWV0aG9kLi4uXG4gICAgICAgICAgICogYW5kIHRoZW4gY2xpZW50IGNhbiBqdXN0IHVzZSBhIGN1c3RvbSBtZXJnZXIuLi5cbiAgICAgICAgICAgKlxuICAgICAgICAgICAqIGNvdWxkIGFkZCBhbmQgcmVtb3ZlIHN1YnNjcmliZXIgYnV0IHRoYXQncyBvdmVyaGVhZCBhbmRcbiAgICAgICAgICAgKiB0cmlja3kgaGVyZSwgYmVjYXVzZSBpZiB3ZSBzZXQgYSB2YWx1ZSB0aGF0IHdhcyBqdXN0IHNldC4uLlxuICAgICAgICAgICAqL1xuICAgICAgICAgIHNldENob3NlbihrZXksIG9uRXhpc3RpbmcoZXhpc3RpbmcsIHZhbHVlLCBvcHRzKSlcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgZWxzZSB7XG4gICAgICAgIC8qIGlzdGFuYnVsIGlnbm9yZSBuZXh0OiBkZXZzICovXG4gICAgICAgIGlmIChFTlZfREVCVUcpIHtcbiAgICAgICAgICBjb25zb2xlLmxvZygncGFyZW50IGRvZXMgbm90IGhhdmUnLCB7W2tleV06IHZhbHVlfSlcbiAgICAgICAgfVxuICAgICAgICBzZXRDaG9zZW4oa2V5LCB2YWx1ZSlcbiAgICAgIH1cbiAgICB9XG5cbiAgICBmb3IgKGxldCBrID0gMCwgbGVuID0ga2V5cy5sZW5ndGg7IGsgPCBsZW47IGsrKykge1xuICAgICAgLy8ga2V5IHRvIHRoZSBjdXJyZW50IHByb3BlcnR5IGluIHRoZSBkYXRhIGJlaW5nIG1lcmdlZFxuICAgICAgbGV0IGtleSA9IGtleXNba11cblxuICAgICAgLy8gd2UgaGF2ZSBvdXIgdmFsdWUsIG5vIHdlIGNhbiBjaGFuZ2UgdGhlIGtleSBpZiBuZWVkZWQgZm9yIHNob3J0aGFuZHNcbiAgICAgIGNvbnN0IHZhbHVlID0gb2JqW2tleV1cblxuICAgICAgLy8gQE5PVEU6IHdoZW4gc2hvcnRoYW5kcyBpcyBhbiBvYmplY3QsIGtleSBpcyB0aGUgbWV0aG9kIGl0IHNob3VsZCBjYWxsXG4gICAgICBpZiAoIWlzVW5kZWZpbmVkKHNob3J0aGFuZHNba2V5XSkgJiYgc2hvcnRoYW5kc1trZXldICE9PSBrZXkpIHtcbiAgICAgICAgLyogaXN0YW5idWwgaWdub3JlIG5leHQ6IGRldnMgKi9cbiAgICAgICAgaWYgKEVOVl9ERUJVRykge1xuICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgJ2hhZCBhIHNob3J0aGFuZCB3aXRoIGEgZGlmZiBrZXkgdGhhbiB0aGUgb2JqZWN0IChsaWtlbHkgQGFsaWFzKScsXG4gICAgICAgICAgICB7c2hvcnRoYW5kTWV0aG9kOiBzaG9ydGhhbmRzW2tleV0sIGtleSwgdmFsdWV9XG4gICAgICAgICAgKVxuICAgICAgICB9XG4gICAgICAgIGtleSA9IHNob3J0aGFuZHNba2V5XVxuICAgICAgfVxuXG4gICAgICAvLyBtZXRob2QgZm9yIHRoZSBrZXlcbiAgICAgIGNvbnN0IG1ldGhvZCA9IHBhcmVudFtrZXldXG5cbiAgICAgIC8qIGlzdGFuYnVsIGlnbm9yZSBuZXh0OiBzb3VyY2VtYXBzIHRyaWdnZXIgaXN0YW5idWwgaGVyZSBpbmNvcnJlY3RseSAqL1xuICAgICAgLy8gdXNlIG9uVmFsdWUgd2hlbiBzZXRcbiAgICAgIGlmICghb25WYWx1ZSh2YWx1ZSwga2V5LCB0aGlzKSkge1xuICAgICAgICAvKiBpc3RhbmJ1bCBpZ25vcmUgbmV4dDogZGV2cyAqL1xuICAgICAgICBpZiAoRU5WX0RFQlVHKSB7XG4gICAgICAgICAgY29uc29sZS5sb2coJ2hhZCBvblZhbHVlLCB3YXMgZmFsc2UsIGlnbm9yZWQnLCB7b25WYWx1ZSwga2V5LCB2YWx1ZX0pXG4gICAgICAgIH1cbiAgICAgICAgY29udGludWVcbiAgICAgIH1cbiAgICAgIC8vIHdoZW4gcHJvcGVydHkgaXRzZWxmIGlzIGEgQ2hhaW5hYmxlXG4gICAgICBlbHNlIGlmIChpc01hcGlzaChtZXRob2QpKSB7XG4gICAgICAgIC8qIGlzdGFuYnVsIGlnbm9yZSBuZXh0OiBkZXZzICovXG4gICAgICAgIGlmIChFTlZfREVCVUcpIHtcbiAgICAgICAgICBjb25zb2xlLmxvZygnaGFzIG1ldGhvZCBvciBzaG9ydGhhbmQnKVxuICAgICAgICB9XG4gICAgICAgIHBhcmVudFtrZXldLm1lcmdlKHZhbHVlKVxuICAgICAgfVxuICAgICAgLy8gd2UgaGF2ZSBhIG1ldGhvZCBvciBzaG9ydGhhbmRcbiAgICAgIGVsc2UgaWYgKG1ldGhvZCkge1xuICAgICAgICAvKiBpc3RhbmJ1bCBpZ25vcmUgbmV4dDogZGV2cyAqL1xuICAgICAgICBpZiAoRU5WX0RFQlVHKSB7XG4gICAgICAgICAgY29uc29sZS5sb2coJ2hhcyBtZXRob2Qgb3Igc2hvcnRoYW5kJywge21ldGhvZCwga2V5LCB2YWx1ZX0pXG4gICAgICAgIH1cbiAgICAgICAgaGFuZGxlRXhpc3Rpbmcoa2V5LCB2YWx1ZSlcbiAgICAgIH1cbiAgICAgIC8vIGRlZmF1bHQgdG8gLnNldCBvbiB0aGUgc3RvcmVcbiAgICAgIGVsc2Uge1xuICAgICAgICAvKiBpc3RhbmJ1bCBpZ25vcmUgbmV4dDogZGV2cyAqL1xuICAgICAgICBpZiAoRU5WX0RFQlVHKSB7XG4gICAgICAgICAgY29uc29sZS5sb2coJ3dlbnQgdG8gZGVmYXVsdCcsIHttZXRob2QsIGtleSwgdmFsdWV9KVxuICAgICAgICB9XG4gICAgICAgIHBhcmVudC5zZXQoa2V5LCB2YWx1ZSlcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gcGFyZW50XG4gIH1cbn1cblxuLyoqXG4gKiBAbWVtYmVyT2YgTWVyZ2VDaGFpblxuICogQG1ldGhvZCBvbkV4aXN0aW5nXG4gKiBAc2luY2UgMC45LjBcbiAqIEBleGFtcGxlXG4gKiAgICBjb25zdCB7Q2hhaW4sIE1lcmdlQ2hhaW59ID0gcmVxdWlyZSgnY2hhaW4tYWJsZScpXG4gKlxuICogICAgY29uc3QgY2hhaW4gPSBuZXcgQ2hhaW4oKS5zZXQoJ3N0cicsICdzdHJpbmd5JylcbiAqXG4gKiAgICBNZXJnZUNoYWluLmluaXQoY2hhaW4pXG4gKiAgICAgIC5vbkV4aXN0aW5nKChhLCBiKSA9PiBhICsgYilcbiAqICAgICAgLm1lcmdlKHtzdHI6ICcrJ30pXG4gKlxuICogICAgY2hhaW4uZ2V0KCdzdHInKVxuICogICAgLy89PiAnc3RyaW5neSsnXG4gKi9cblxubW9kdWxlLmV4cG9ydHMgPSBNZXJnZUNoYWluXG5cbi8vIEBUT0RPIHJlLWVuYWJsZSB0aGlzIGxhdGVyXG4vLyBtb2R1bGUuZXhwb3J0cyA9IG5ldyBNZXRob2RDaGFpbihNZXJnZUNoYWluLnByb3RvdHlwZSlcbi8vICAgLm1ldGhvZHMoWydvbkV4aXN0aW5nJywgJ29uVmFsdWUnLCAnb2JqJ10pXG4vLyAgIC5idWlsZChNZXJnZUNoYWluKVxuIl0sIm5hbWVzIjpbImNvbnN0Iiwic3VwZXIiLCJ0aGlzIiwibGV0Il0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBQSxHQUFLLENBQUMsV0FBVyxHQUFHLE9BQU8sQ0FBQyxlQUFlLENBQUM7QUFDNUNBLEdBQUssQ0FBQyxjQUFjLEdBQUcsT0FBTyxDQUFDLGtCQUFrQixDQUFDO0FBQ2xEQSxHQUFLLENBQUMsU0FBUyxHQUFHLE9BQU8sQ0FBQyxrQkFBa0IsQ0FBQztBQUM3Q0EsR0FBSyxDQUFDLFVBQVUsR0FBRyxPQUFPLENBQUMsb0JBQW9CLENBQUM7QUFDaERBLEdBQUssQ0FBQyxXQUFXLEdBQUcsT0FBTyxDQUFDLHFCQUFxQixDQUFDO0FBQ2xEQSxHQUFLLENBQUMsTUFBTSxHQUFHLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQztBQUN4Q0EsR0FBSyxDQUFDLFFBQVEsR0FBRyxPQUFPLENBQUMsa0JBQWtCLENBQUM7QUFDNUNBLEdBQUssQ0FBQyxVQUFVLEdBQUcsT0FBTyxDQUFDLGtCQUFrQixDQUFDO0FBQzlDQSxHQUFLLENBQUMsY0FBYyxHQUFHLE9BQU8sQ0FBQyx3QkFBd0IsQ0FBQztBQUN4REEsR0FBSyxDQUFDLGVBQWUsR0FBRyxPQUFPLENBQUMsZ0JBQWdCLENBQUM7QUFDakRBLEdBQUssQ0FBQyxTQUFTLEdBQUcsT0FBTyxDQUFDLGtCQUFrQixDQUFDOztBQUU3Q0EsR0FBSyxDQUFDLGVBQWUsR0FBRyxZQUFZO0FBQ3BDQSxHQUFLLENBQUMsWUFBWSxHQUFHLFNBQVM7QUFDOUJBLEdBQUssQ0FBQyxVQUFVLEdBQUcsUUFBUTtBQUMzQkEsR0FBSyxDQUFDLGtCQUFrQixHQUFHLE1BQU07QUFDakNBLEdBQUssQ0FBQyxPQUFPLEdBQUcsS0FBSzs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQW1CckIsSUFBTSxVQUFV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