You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					687 lines
				
				17 KiB
			
		
		
			
		
	
	
					687 lines
				
				17 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								'use strict';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Load modules
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const Assert = require('assert');
							 | 
						||
| 
								 | 
							
								const Crypto = require('crypto');
							 | 
						||
| 
								 | 
							
								const Path = require('path');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const DeepEqual = require('./deep-equal');
							 | 
						||
| 
								 | 
							
								const Escape = require('./escape');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Declare internals
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const internals = {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Deep object or array comparison
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.deepEqual = DeepEqual;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Clone object or array
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.clone = function (obj, options = {}, _seen = null) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (typeof obj !== 'object' ||
							 | 
						||
| 
								 | 
							
								        obj === null) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return obj;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const seen = _seen || new Map();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const lookup = seen.get(obj);
							 | 
						||
| 
								 | 
							
								    if (lookup) {
							 | 
						||
| 
								 | 
							
								        return lookup;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let newObj;
							 | 
						||
| 
								 | 
							
								    let cloneDeep = false;
							 | 
						||
| 
								 | 
							
								    const isArray = Array.isArray(obj);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!isArray) {
							 | 
						||
| 
								 | 
							
								        if (Buffer.isBuffer(obj)) {
							 | 
						||
| 
								 | 
							
								            newObj = Buffer.from(obj);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else if (obj instanceof Date) {
							 | 
						||
| 
								 | 
							
								            newObj = new Date(obj.getTime());
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else if (obj instanceof RegExp) {
							 | 
						||
| 
								 | 
							
								            newObj = new RegExp(obj);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else {
							 | 
						||
| 
								 | 
							
								            if (options.prototype !== false) {          // Defaults to true
							 | 
						||
| 
								 | 
							
								                const proto = Object.getPrototypeOf(obj);
							 | 
						||
| 
								 | 
							
								                if (proto &&
							 | 
						||
| 
								 | 
							
								                    proto.isImmutable) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    newObj = obj;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                else {
							 | 
						||
| 
								 | 
							
								                    newObj = Object.create(proto);
							 | 
						||
| 
								 | 
							
								                    cloneDeep = true;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else {
							 | 
						||
| 
								 | 
							
								                newObj = {};
							 | 
						||
| 
								 | 
							
								                cloneDeep = true;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else {
							 | 
						||
| 
								 | 
							
								        newObj = [];
							 | 
						||
| 
								 | 
							
								        cloneDeep = true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    seen.set(obj, newObj);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (cloneDeep) {
							 | 
						||
| 
								 | 
							
								        const keys = internals.keys(obj, options);
							 | 
						||
| 
								 | 
							
								        for (let i = 0; i < keys.length; ++i) {
							 | 
						||
| 
								 | 
							
								            const key = keys[i];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (isArray && key === 'length') {
							 | 
						||
| 
								 | 
							
								                continue;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            const descriptor = Object.getOwnPropertyDescriptor(obj, key);
							 | 
						||
| 
								 | 
							
								            if (descriptor &&
							 | 
						||
| 
								 | 
							
								                (descriptor.get ||
							 | 
						||
| 
								 | 
							
								                    descriptor.set)) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                Object.defineProperty(newObj, key, descriptor);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else {
							 | 
						||
| 
								 | 
							
								                Object.defineProperty(newObj, key, {
							 | 
						||
| 
								 | 
							
								                    enumerable: descriptor ? descriptor.enumerable : true,
							 | 
						||
| 
								 | 
							
								                    writable: true,
							 | 
						||
| 
								 | 
							
								                    configurable: true,
							 | 
						||
| 
								 | 
							
								                    value: exports.clone(obj[key], options, seen)
							 | 
						||
| 
								 | 
							
								                });
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (isArray) {
							 | 
						||
| 
								 | 
							
								            newObj.length = obj.length;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return newObj;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								internals.keys = function (obj, options = {}) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return options.symbols ? Reflect.ownKeys(obj) : Object.getOwnPropertyNames(obj);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Merge all the properties of source into target, source wins in conflict, and by default null and undefined from source are applied
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.merge = function (target, source, isNullOverride /* = true */, isMergeArrays /* = true */) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    exports.assert(target && typeof target === 'object', 'Invalid target value: must be an object');
							 | 
						||
| 
								 | 
							
								    exports.assert(source === null || source === undefined || typeof source === 'object', 'Invalid source value: must be null, undefined, or an object');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!source) {
							 | 
						||
| 
								 | 
							
								        return target;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (Array.isArray(source)) {
							 | 
						||
| 
								 | 
							
								        exports.assert(Array.isArray(target), 'Cannot merge array onto an object');
							 | 
						||
| 
								 | 
							
								        if (isMergeArrays === false) {                                                  // isMergeArrays defaults to true
							 | 
						||
| 
								 | 
							
								            target.length = 0;                                                          // Must not change target assignment
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for (let i = 0; i < source.length; ++i) {
							 | 
						||
| 
								 | 
							
								            target.push(exports.clone(source[i]));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return target;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const keys = internals.keys(source);
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < keys.length; ++i) {
							 | 
						||
| 
								 | 
							
								        const key = keys[i];
							 | 
						||
| 
								 | 
							
								        if (key === '__proto__' ||
							 | 
						||
| 
								 | 
							
								            !Object.prototype.propertyIsEnumerable.call(source, key)) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            continue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        const value = source[key];
							 | 
						||
| 
								 | 
							
								        if (value &&
							 | 
						||
| 
								 | 
							
								            typeof value === 'object') {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (!target[key] ||
							 | 
						||
| 
								 | 
							
								                typeof target[key] !== 'object' ||
							 | 
						||
| 
								 | 
							
								                (Array.isArray(target[key]) !== Array.isArray(value)) ||
							 | 
						||
| 
								 | 
							
								                value instanceof Date ||
							 | 
						||
| 
								 | 
							
								                Buffer.isBuffer(value) ||
							 | 
						||
| 
								 | 
							
								                value instanceof RegExp) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                target[key] = exports.clone(value);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else {
							 | 
						||
| 
								 | 
							
								                exports.merge(target[key], value, isNullOverride, isMergeArrays);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else {
							 | 
						||
| 
								 | 
							
								            if (value !== null &&
							 | 
						||
| 
								 | 
							
								                value !== undefined) {                              // Explicit to preserve empty strings
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                target[key] = value;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else if (isNullOverride !== false) {                    // Defaults to true
							 | 
						||
| 
								 | 
							
								                target[key] = value;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return target;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Apply options to a copy of the defaults
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.applyToDefaults = function (defaults, options, isNullOverride) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    exports.assert(defaults && typeof defaults === 'object', 'Invalid defaults value: must be an object');
							 | 
						||
| 
								 | 
							
								    exports.assert(!options || options === true || typeof options === 'object', 'Invalid options value: must be true, falsy or an object');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!options) {                                                 // If no options, return null
							 | 
						||
| 
								 | 
							
								        return null;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const copy = exports.clone(defaults);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (options === true) {                                         // If options is set to true, use defaults
							 | 
						||
| 
								 | 
							
								        return copy;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return exports.merge(copy, options, isNullOverride === true, false);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Clone an object except for the listed keys which are shallow copied
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.cloneWithShallow = function (source, keys, options) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!source ||
							 | 
						||
| 
								 | 
							
								        typeof source !== 'object') {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return source;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const storage = internals.store(source, keys);    // Move shallow copy items to storage
							 | 
						||
| 
								 | 
							
								    const copy = exports.clone(source, options);      // Deep copy the rest
							 | 
						||
| 
								 | 
							
								    internals.restore(copy, source, storage);         // Shallow copy the stored items and restore
							 | 
						||
| 
								 | 
							
								    return copy;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								internals.store = function (source, keys) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const storage = new Map();
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < keys.length; ++i) {
							 | 
						||
| 
								 | 
							
								        const key = keys[i];
							 | 
						||
| 
								 | 
							
								        const value = exports.reach(source, key);
							 | 
						||
| 
								 | 
							
								        if (typeof value === 'object' ||
							 | 
						||
| 
								 | 
							
								            typeof value === 'function') {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            storage.set(key, value);
							 | 
						||
| 
								 | 
							
								            internals.reachSet(source, key, undefined);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return storage;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								internals.restore = function (copy, source, storage) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for (const [key, value] of storage) {
							 | 
						||
| 
								 | 
							
								        internals.reachSet(copy, key, value);
							 | 
						||
| 
								 | 
							
								        internals.reachSet(source, key, value);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								internals.reachSet = function (obj, key, value) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const path = Array.isArray(key) ? key : key.split('.');
							 | 
						||
| 
								 | 
							
								    let ref = obj;
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < path.length; ++i) {
							 | 
						||
| 
								 | 
							
								        const segment = path[i];
							 | 
						||
| 
								 | 
							
								        if (i + 1 === path.length) {
							 | 
						||
| 
								 | 
							
								            ref[segment] = value;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        ref = ref[segment];
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Apply options to defaults except for the listed keys which are shallow copied from option without merging
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.applyToDefaultsWithShallow = function (defaults, options, keys) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    exports.assert(defaults && typeof defaults === 'object', 'Invalid defaults value: must be an object');
							 | 
						||
| 
								 | 
							
								    exports.assert(!options || options === true || typeof options === 'object', 'Invalid options value: must be true, falsy or an object');
							 | 
						||
| 
								 | 
							
								    exports.assert(keys && Array.isArray(keys), 'Invalid keys');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!options) {                                                 // If no options, return null
							 | 
						||
| 
								 | 
							
								        return null;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const copy = exports.cloneWithShallow(defaults, keys);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (options === true) {                                         // If options is set to true, use defaults
							 | 
						||
| 
								 | 
							
								        return copy;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const storage = internals.store(options, keys);     // Move shallow copy items to storage
							 | 
						||
| 
								 | 
							
								    exports.merge(copy, options, false, false);         // Deep copy the rest
							 | 
						||
| 
								 | 
							
								    internals.restore(copy, options, storage);          // Shallow copy the stored items and restore
							 | 
						||
| 
								 | 
							
								    return copy;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Find the common unique items in two arrays
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.intersect = function (array1, array2, justFirst) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!array1 ||
							 | 
						||
| 
								 | 
							
								        !array2) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return (justFirst ? null : []);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const common = [];
							 | 
						||
| 
								 | 
							
								    const hash = (Array.isArray(array1) ? new Set(array1) : array1);
							 | 
						||
| 
								 | 
							
								    const found = new Set();
							 | 
						||
| 
								 | 
							
								    for (const value of array2) {
							 | 
						||
| 
								 | 
							
								        if (internals.has(hash, value) &&
							 | 
						||
| 
								 | 
							
								            !found.has(value)) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (justFirst) {
							 | 
						||
| 
								 | 
							
								                return value;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            common.push(value);
							 | 
						||
| 
								 | 
							
								            found.add(value);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return (justFirst ? null : common);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								internals.has = function (ref, key) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (typeof ref.has === 'function') {
							 | 
						||
| 
								 | 
							
								        return ref.has(key);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return ref[key] !== undefined;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Test if the reference contains the values
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.contain = function (ref, values, options = {}) {        // options: { deep, once, only, part, symbols }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /*
							 | 
						||
| 
								 | 
							
								        string -> string(s)
							 | 
						||
| 
								 | 
							
								        array -> item(s)
							 | 
						||
| 
								 | 
							
								        object -> key(s)
							 | 
						||
| 
								 | 
							
								        object -> object (key:value)
							 | 
						||
| 
								 | 
							
								    */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let valuePairs = null;
							 | 
						||
| 
								 | 
							
								    if (typeof ref === 'object' &&
							 | 
						||
| 
								 | 
							
								        typeof values === 'object' &&
							 | 
						||
| 
								 | 
							
								        !Array.isArray(ref) &&
							 | 
						||
| 
								 | 
							
								        !Array.isArray(values)) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        valuePairs = values;
							 | 
						||
| 
								 | 
							
								        const symbols = Object.getOwnPropertySymbols(values).filter(Object.prototype.propertyIsEnumerable.bind(values));
							 | 
						||
| 
								 | 
							
								        values = [...Object.keys(values), ...symbols];
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else {
							 | 
						||
| 
								 | 
							
								        values = [].concat(values);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    exports.assert(typeof ref === 'string' || typeof ref === 'object', 'Reference must be string or an object');
							 | 
						||
| 
								 | 
							
								    exports.assert(values.length, 'Values array cannot be empty');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let compare;
							 | 
						||
| 
								 | 
							
								    let compareFlags;
							 | 
						||
| 
								 | 
							
								    if (options.deep) {
							 | 
						||
| 
								 | 
							
								        compare = exports.deepEqual;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        const hasOnly = options.hasOwnProperty('only');
							 | 
						||
| 
								 | 
							
								        const hasPart = options.hasOwnProperty('part');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        compareFlags = {
							 | 
						||
| 
								 | 
							
								            prototype: hasOnly ? options.only : hasPart ? !options.part : false,
							 | 
						||
| 
								 | 
							
								            part: hasOnly ? !options.only : hasPart ? options.part : false
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else {
							 | 
						||
| 
								 | 
							
								        compare = (a, b) => a === b;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let misses = false;
							 | 
						||
| 
								 | 
							
								    const matches = new Array(values.length);
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < matches.length; ++i) {
							 | 
						||
| 
								 | 
							
								        matches[i] = 0;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (typeof ref === 'string') {
							 | 
						||
| 
								 | 
							
								        let pattern = '(';
							 | 
						||
| 
								 | 
							
								        for (let i = 0; i < values.length; ++i) {
							 | 
						||
| 
								 | 
							
								            const value = values[i];
							 | 
						||
| 
								 | 
							
								            exports.assert(typeof value === 'string', 'Cannot compare string reference to non-string value');
							 | 
						||
| 
								 | 
							
								            pattern += (i ? '|' : '') + exports.escapeRegex(value);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        const regex = new RegExp(pattern + ')', 'g');
							 | 
						||
| 
								 | 
							
								        const leftovers = ref.replace(regex, ($0, $1) => {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            const index = values.indexOf($1);
							 | 
						||
| 
								 | 
							
								            ++matches[index];
							 | 
						||
| 
								 | 
							
								            return '';          // Remove from string
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        misses = !!leftovers;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else if (Array.isArray(ref)) {
							 | 
						||
| 
								 | 
							
								        const onlyOnce = !!(options.only && options.once);
							 | 
						||
| 
								 | 
							
								        if (onlyOnce && ref.length !== values.length) {
							 | 
						||
| 
								 | 
							
								            return false;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for (let i = 0; i < ref.length; ++i) {
							 | 
						||
| 
								 | 
							
								            let matched = false;
							 | 
						||
| 
								 | 
							
								            for (let j = 0; j < values.length && matched === false; ++j) {
							 | 
						||
| 
								 | 
							
								                if (!onlyOnce || matches[j] === 0) {
							 | 
						||
| 
								 | 
							
								                    matched = compare(values[j], ref[i], compareFlags) && j;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (matched !== false) {
							 | 
						||
| 
								 | 
							
								                ++matches[matched];
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else {
							 | 
						||
| 
								 | 
							
								                misses = true;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else {
							 | 
						||
| 
								 | 
							
								        const keys = internals.keys(ref, options);
							 | 
						||
| 
								 | 
							
								        for (let i = 0; i < keys.length; ++i) {
							 | 
						||
| 
								 | 
							
								            const key = keys[i];
							 | 
						||
| 
								 | 
							
								            const pos = values.indexOf(key);
							 | 
						||
| 
								 | 
							
								            if (pos !== -1) {
							 | 
						||
| 
								 | 
							
								                if (valuePairs &&
							 | 
						||
| 
								 | 
							
								                    !compare(valuePairs[key], ref[key], compareFlags)) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    return false;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                ++matches[pos];
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else {
							 | 
						||
| 
								 | 
							
								                misses = true;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (options.only) {
							 | 
						||
| 
								 | 
							
								        if (misses || !options.once) {
							 | 
						||
| 
								 | 
							
								            return !misses;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let result = false;
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < matches.length; ++i) {
							 | 
						||
| 
								 | 
							
								        result = result || !!matches[i];
							 | 
						||
| 
								 | 
							
								        if ((options.once && matches[i] > 1) ||
							 | 
						||
| 
								 | 
							
								            (!options.part && !matches[i])) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return false;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return result;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Flatten array
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.flatten = function (array, target) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const result = target || [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < array.length; ++i) {
							 | 
						||
| 
								 | 
							
								        if (Array.isArray(array[i])) {
							 | 
						||
| 
								 | 
							
								            exports.flatten(array[i], result);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else {
							 | 
						||
| 
								 | 
							
								            result.push(array[i]);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return result;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Convert an object key chain string ('a.b.c') to reference (object[a][b][c])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.reach = function (obj, chain, options) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (chain === false ||
							 | 
						||
| 
								 | 
							
								        chain === null ||
							 | 
						||
| 
								 | 
							
								        typeof chain === 'undefined') {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return obj;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    options = options || {};
							 | 
						||
| 
								 | 
							
								    if (typeof options === 'string') {
							 | 
						||
| 
								 | 
							
								        options = { separator: options };
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const isChainArray = Array.isArray(chain);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    exports.assert(!isChainArray || !options.separator, 'Separator option no valid for array-based chain');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const path = isChainArray ? chain : chain.split(options.separator || '.');
							 | 
						||
| 
								 | 
							
								    let ref = obj;
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < path.length; ++i) {
							 | 
						||
| 
								 | 
							
								        let key = path[i];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (Array.isArray(ref)) {
							 | 
						||
| 
								 | 
							
								            const number = Number(key);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (Number.isInteger(number) && number < 0) {
							 | 
						||
| 
								 | 
							
								                key = ref.length + number;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (!ref ||
							 | 
						||
| 
								 | 
							
								            !((typeof ref === 'object' || typeof ref === 'function') && key in ref) ||
							 | 
						||
| 
								 | 
							
								            (typeof ref !== 'object' && options.functions === false)) {         // Only object and function can have properties
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            exports.assert(!options.strict || i + 1 === path.length, 'Missing segment', key, 'in reach path ', chain);
							 | 
						||
| 
								 | 
							
								            exports.assert(typeof ref === 'object' || options.functions === true || typeof ref !== 'function', 'Invalid segment', key, 'in reach path ', chain);
							 | 
						||
| 
								 | 
							
								            ref = options.default;
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        ref = ref[key];
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return ref;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.reachTemplate = function (obj, template, options) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return template.replace(/{([^}]+)}/g, ($0, chain) => {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        const value = exports.reach(obj, chain, options);
							 | 
						||
| 
								 | 
							
								        return (value === undefined || value === null ? '' : value);
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.assert = function (condition, ...args) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (condition) {
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (args.length === 1 && args[0] instanceof Error) {
							 | 
						||
| 
								 | 
							
								        throw args[0];
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const msgs = args
							 | 
						||
| 
								 | 
							
								        .filter((arg) => arg !== '')
							 | 
						||
| 
								 | 
							
								        .map((arg) => {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return typeof arg === 'string' ? arg : arg instanceof Error ? arg.message : exports.stringify(arg);
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    throw new Assert.AssertionError({
							 | 
						||
| 
								 | 
							
								        message: msgs.join(' ') || 'Unknown error',
							 | 
						||
| 
								 | 
							
								        actual: false,
							 | 
						||
| 
								 | 
							
								        expected: true,
							 | 
						||
| 
								 | 
							
								        operator: '==',
							 | 
						||
| 
								 | 
							
								        stackStartFunction: exports.assert
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.Bench = function () {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this.ts = 0;
							 | 
						||
| 
								 | 
							
								    this.reset();
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.Bench.prototype.reset = function () {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this.ts = exports.Bench.now();
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.Bench.prototype.elapsed = function () {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return exports.Bench.now() - this.ts;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.Bench.now = function () {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const ts = process.hrtime();
							 | 
						||
| 
								 | 
							
								    return (ts[0] * 1e3) + (ts[1] / 1e6);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Escape string for Regex construction
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.escapeRegex = function (string) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Escape ^$.*+-?=!:|\/()[]{},
							 | 
						||
| 
								 | 
							
								    return string.replace(/[\^\$\.\*\+\-\?\=\!\:\|\\\/\(\)\[\]\{\}\,]/g, '\\$&');
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Escape attribute value for use in HTTP header
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.escapeHeaderAttribute = function (attribute) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Allowed value characters: !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9, \, "
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    exports.assert(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~\"\\]*$/.test(attribute), 'Bad attribute value (' + attribute + ')');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return attribute.replace(/\\/g, '\\\\').replace(/\"/g, '\\"');                             // Escape quotes and slash
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.escapeHtml = function (string) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return Escape.escapeHtml(string);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.escapeJson = function (string) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return Escape.escapeJson(string);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.once = function (method) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (method._hoekOnce) {
							 | 
						||
| 
								 | 
							
								        return method;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let once = false;
							 | 
						||
| 
								 | 
							
								    const wrapped = function (...args) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (!once) {
							 | 
						||
| 
								 | 
							
								            once = true;
							 | 
						||
| 
								 | 
							
								            method(...args);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    wrapped._hoekOnce = true;
							 | 
						||
| 
								 | 
							
								    return wrapped;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.ignore = function () { };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.uniqueFilename = function (path, extension) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (extension) {
							 | 
						||
| 
								 | 
							
								        extension = extension[0] !== '.' ? '.' + extension : extension;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else {
							 | 
						||
| 
								 | 
							
								        extension = '';
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    path = Path.resolve(path);
							 | 
						||
| 
								 | 
							
								    const name = [Date.now(), process.pid, Crypto.randomBytes(8).toString('hex')].join('-') + extension;
							 | 
						||
| 
								 | 
							
								    return Path.join(path, name);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.stringify = function (...args) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    try {
							 | 
						||
| 
								 | 
							
								        return JSON.stringify.apply(null, args);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    catch (err) {
							 | 
						||
| 
								 | 
							
								        return '[Cannot display object: ' + err.message + ']';
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.wait = function (timeout) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return new Promise((resolve) => setTimeout(resolve, timeout));
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.block = function () {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return new Promise(exports.ignore);
							 | 
						||
| 
								 | 
							
								};
							 |