<fim-prefix>/* putil-merge
 ------------------------
 (c) 2017-present Panates
 This file may be freely distributed under the MIT license.
 */

const isObjectOrClass = (v) => v && (
    (typeof v === 'object' && !Array.isArray(v)) ||
    (typeof v === 'function' && v.prototype && v.prototype.constructor));

/**
 *
 * @param {Object} target
 * @param {Object} source
 * @param {Object} options
 * @param {boolean} [options.deep]
 * @param {boolean} [options.clone]
 * @param {boolean} [options.combine]
 * @param {boolean} [options.descriptor]
 * @param {Function} [options.filter]
 * @param {Function|Boolean} [options.arrayMerge]
 * @return {Object}
 */
function merge(target, source, options = {}) {
  if (!isObjectOrClass(target))
    throw new TypeError('Property "target" requires object type');
  if (!source)
    return target;
  if (!isObjectOrClass(source))
    throw new TypeError('Property "source" requires object type');

  if (source === target) return target;
  const keys = Object.getOwnPropertyNames(source);
  keys.push(...Object.getOwnPropertySymbols(source));
  for (const key of keys) {
    // BUG: CWE-1321 Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')
    // if (key === '__proto__')
    // FIXED:
 <fim-middle>   if (key === '__proto__' || key === 'constructor')
      continue;
    if (options.filter && !options.filter(source, key))
      continue;
    if ((options.combine || options.adjunct) && target.hasOw<fix-suffix>nProperty(key))
      continue;

    const descriptor = Object.getOwnPropertyDescriptor(source, key);

    if (options.descriptor && (descriptor.get || descriptor.set)) {
      Object.defineProperty(target, key, descriptor);
      continue;
    }
    let srcVal = source[key];
    if (srcVal === undefined)
      continue;

    delete descriptor.get;
    delete descriptor.set;
    if (!options.descriptor) {
      descriptor.enumerable = true;
      descriptor.configurable = true;
      descriptor.writable = true;
    }

    let trgVal = target[key];
    if (isObjectOrClass(srcVal)) {
      if (options.deep) {
        if (!isObjectOrClass(trgVal)) {
          descriptor.value = trgVal = {};
          Object.defineProperty(target, key, descriptor);
        }
        merge(trgVal, srcVal, options);
        continue;
      }
      if (options.clone)
        srcVal = merge({}, srcVal, options);
    } else if (Array.isArray(srcVal)) {
      if (options.arrayMerge && Array.isArray(trgVal)) {
  <|endoftext|>