index.js
function I(obj) {
  function isObject(v) {
    return {}.toString.call(v) === '[object Object]';
  }
  function isArray(v) {
    return {}.toString.call(v) === '[object Array]';
  }
  function isFunc(v) {
    return {}.toString.call(v) === '[object Function]';
  }
  function assertA(v, msg) {
    if (!isArray(v)) throw new Error(msg);
  }
  function assertAO(v, msg) {
    if (!isArray(v) && !isObject(v)) throw new Error(msg);
  }
  function _get(o, p) {
    var key = isFunc(p[0]) ? o.findIndex(p[0]) : p[0];
    return p.length ? _get(o[key], p.slice(1)) : o;
  }
  function _set(o, p, v) {
    var key = isFunc(p[0]) ? o.findIndex(p[0]) : p[0];
    // eslint-disable-next-line no-param-reassign
    if (p.length === 1) o[key] = v;else _set(o[key], p.slice(1), v);
  }
  function _copy(o, path) {
    var p = [].concat(toConsumableArray(path));
    var n = p.splice(0, 1)[0];
    if (isFunc(n)) n = o.findIndex(n);
    if (n !== undefined) {
      if (o[n] === undefined) throw new Error('Path not found');
      return isArray(o) ? [].concat(toConsumableArray(o.slice(0, n)), [_copy(o[n], p)], toConsumableArray(o.slice(n + 1))) : _extends({}, o, defineProperty({}, n, _copy(o[n], p)));
    }
    return isArray(o) ? [].concat(toConsumableArray(o)) : _extends({}, o);
  }
  function _diff(a, b) {
    var path = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
    var acc = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;

    function log(p, msg, value) {
      acc.push(value ? { path: p, msg: msg, value: value } : { path: p, msg: msg });
    }
    function strPath(p, key) {
      var p2 = key !== undefined ? [].concat(toConsumableArray(p), [key]) : p;
      return p2.length ? p2.join('.') : '(root)';
    }
    if (isArray(a) && isArray(b)) {
      if (a !== b) log(strPath(path), 'different reference');
      var deletions = {};
      b.forEach(function (v, k) {
        if (a.indexOf(v) === -1) deletions[k] = v;
      });
      a.forEach(function (v, k) {
        var bk = b.indexOf(v);
        if (bk === -1) {
          if (deletions[k] !== undefined) {
            log(strPath(path, k), 'replace', v);
            delete deletions[k];
          } else {
            log(strPath(path, k), 'add', v);
          }
        } else if (bk !== k && !Object.keys(deletions)) log(strPath(path, k), 'index changed from ' + bk, v);
      });
      Object.keys(deletions).forEach(function (k) {
        return log(strPath(path, k), 'delete');
      });
    } else if (isObject(a) && isObject(b)) {
      if (a !== b) log(strPath(path), 'different reference');
      Object.keys(b).forEach(function (k) {
        if (a[k] === undefined) log(strPath(path, k), 'delete', b[k]);
      });
      Object.keys(a).forEach(function (k) {
        if (b[k] === undefined) log(strPath(path, k), 'add', a[k]);else if (a[k] !== b[k]) _diff(a[k], b[k], path.concat([k]), acc);
      });
    } else if ((typeof a === 'undefined' ? 'undefined' : _typeof(a)) === 'object' || (typeof b === 'undefined' ? 'undefined' : _typeof(b)) === 'object') log(strPath(path), 'different types!');else if (a !== b) log(strPath(path), 'change', a);
  }

  assertAO(obj, 'obj must be an object or array');
  return {
    result: obj,
    get: function get$$1(path) {
      assertA(path, 'path must be an array');
      return _get(this.result, path);
    },
    set: function set$$1(value, path) {
      assertA(path, 'path must be an array');
      if (this.copy(path.slice(0, path.length - 1))) {
        _set(this.result, path, value);
      }
      return this;
    },
    push: function push(value) {
      var path = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];

      assertA(path, 'path must be an array');
      if (this.copy(path)) {
        if (path.length) _set(this.result, path, [].concat(toConsumableArray(_get(this.result, path)), [value]));else this.result.push(value);
      }
      return this;
    },
    delete: function _delete(path) {
      assertA(path, 'path must be an array');
      var parentPath = path.slice(0, path.length - 1);
      var last = path.slice(path.length - 1, path.length)[0];
      if (this.copy(parentPath)) {
        var o = parentPath.length ? _get(this.result, parentPath) : this.result;
        if (isArray(o)) {
          if (isFunc(last)) last = o.findIndex(last);
          if (parentPath.length) {
            _set(this.result, parentPath, [].concat(toConsumableArray(o.slice(0, last)), toConsumableArray(o.slice(last + 1))));
          } else this.result = [].concat(toConsumableArray(o.slice(0, last)), toConsumableArray(o.slice(last + 1)));
        } else delete o[last];
      }
      return this;
    },
    copy: function copy(path) {
      assertA(path, 'path must be an array');
      try {
        this.result = _copy(this.result, path);
      } catch (e) {
        console.warn(e);
        return false;
      }
      return this;
    },
    diff: function diff(other) {
      var acc = [];
      _diff(this.result, other, [], acc);
      return acc;
    }
  };
}
function I(obj) {
  function isObject(v) {
    return {}.toString.call(v) === '[object Object]';
  }
  function isArray(v) {
    return {}.toString.call(v) === '[object Array]';
  }
  function isFunc(v) {
    return {}.toString.call(v) === '[object Function]';
  }
  function assertA(v, msg) {
    if (!isArray(v)) throw new Error(msg);
  }
  function assertAO(v, msg) {
    if (!isArray(v) && !isObject(v)) throw new Error(msg);
  }
  function _get(o, p) {
    var key = isFunc(p[0]) ? o.findIndex(p[0]) : p[0];
    return p.length ? _get(o[key], p.slice(1)) : o;
  }
  function _set(o, p, v) {
    var key = isFunc(p[0]) ? o.findIndex(p[0]) : p[0];
    // eslint-disable-next-line no-param-reassign
    if (p.length === 1) o[key] = v;else _set(o[key], p.slice(1), v);
  }
  function _copy(o, path) {
    var p = [].concat(toConsumableArray(path));
    var n = p.splice(0, 1)[0];
    if (isFunc(n)) n = o.findIndex(n);
    if (n !== undefined) {
      if (o[n] === undefined) throw new Error('Path not found');
      return isArray(o) ? [].concat(toConsumableArray(o.slice(0, n)), [_copy(o[n], p)], toConsumableArray(o.slice(n + 1))) : _extends({}, o, defineProperty({}, n, _copy(o[n], p)));
    }
    return isArray(o) ? [].concat(toConsumableArray(o)) : _extends({}, o);
  }
  function _diff(a, b) {
    var path = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
    var acc = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;

    function log(p, msg, value) {
      acc.push(value ? { path: p, msg: msg, value: value } : { path: p, msg: msg });
    }
    function strPath(p, key) {
      var p2 = key !== undefined ? [].concat(toConsumableArray(p), [key]) : p;
      return p2.length ? p2.join('.') : '(root)';
    }
    if (isArray(a) && isArray(b)) {
      if (a !== b) log(strPath(path), 'different reference');
      var deletions = {};
      b.forEach(function (v, k) {
        if (a.indexOf(v) === -1) deletions[k] = v;
      });
      a.forEach(function (v, k) {
        var bk = b.indexOf(v);
        if (bk === -1) {
          if (deletions[k] !== undefined) {
            log(strPath(path, k), 'replace', v);
            delete deletions[k];
          } else {
            log(strPath(path, k), 'add', v);
          }
        } else if (bk !== k && !Object.keys(deletions)) log(strPath(path, k), 'index changed from ' + bk, v);
      });
      Object.keys(deletions).forEach(function (k) {
        return log(strPath(path, k), 'delete');
      });
    } else if (isObject(a) && isObject(b)) {
      if (a !== b) log(strPath(path), 'different reference');
      Object.keys(b).forEach(function (k) {
        if (a[k] === undefined) log(strPath(path, k), 'delete', b[k]);
      });
      Object.keys(a).forEach(function (k) {
        if (b[k] === undefined) log(strPath(path, k), 'add', a[k]);else if (a[k] !== b[k]) _diff(a[k], b[k], path.concat([k]), acc);
      });
    } else if ((typeof a === 'undefined' ? 'undefined' : _typeof(a)) === 'object' || (typeof b === 'undefined' ? 'undefined' : _typeof(b)) === 'object') log(strPath(path), 'different types!');else if (a !== b) log(strPath(path), 'change', a);
  }

  assertAO(obj, 'obj must be an object or array');
  return {
    result: obj,
    get: function get$$1(path) {
      assertA(path, 'path must be an array');
      return _get(this.result, path);
    },
    set: function set$$1(value, path) {
      assertA(path, 'path must be an array');
      if (this.copy(path.slice(0, path.length - 1))) {
        _set(this.result, path, value);
      }
      return this;
    },
    push: function push(value) {
      var path = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];

      assertA(path, 'path must be an array');
      if (this.copy(path)) {
        if (path.length) _set(this.result, path, [].concat(toConsumableArray(_get(this.result, path)), [value]));else this.result.push(value);
      }
      return this;
    },
    delete: function _delete(path) {
      assertA(path, 'path must be an array');
      var parentPath = path.slice(0, path.length - 1);
      var last = path.slice(path.length - 1, path.length)[0];
      if (this.copy(parentPath)) {
        var o = parentPath.length ? _get(this.result, parentPath) : this.result;
        if (isArray(o)) {
          if (isFunc(last)) last = o.findIndex(last);
          if (parentPath.length) {
            _set(this.result, parentPath, [].concat(toConsumableArray(o.slice(0, last)), toConsumableArray(o.slice(last + 1))));
          } else this.result = [].concat(toConsumableArray(o.slice(0, last)), toConsumableArray(o.slice(last + 1)));
        } else delete o[last];
      }
      return this;
    },
    copy: function copy(path) {
      assertA(path, 'path must be an array');
      try {
        this.result = _copy(this.result, path);
      } catch (e) {
        console.warn(e);
        return false;
      }
      return this;
    },
    diff: function diff(other) {
      var acc = [];
      _diff(this.result, other, [], acc);
      return acc;
    }
  };
}
/* eslint-disable no-console