function proxify(obj, nonChainableMethodName) {
  if (!isProxyEnabled()) return obj;

  return new Proxy(obj, {
    get: function proxyGetter(target, property) {
      // This check is here because we should not throw errors on Symbol properties
      // such as `Symbol.toStringTag`.
      // The values for which an error should be thrown can be configured using
      // the `config.proxyExcludedKeys` setting.
      if (
        typeof property === 'string' &&
        config.proxyExcludedKeys.indexOf(property) === -1 &&
        !Reflect.has(target, property)
      ) {
        // Special message for invalid property access of non-chainable methods.
        if (nonChainableMethodName) {
          throw Error(
            'Invalid Chai property: ' +
              nonChainableMethodName +
              '.' +
              property +
              '. See docs for proper usage of "' +
              nonChainableMethodName +
              '".'
          );
        }

        // If the property is reasonably close to an existing Chai property,
        // suggest that property to the user. Only suggest properties with a
        // distance less than 4.
        let suggestion = null;
        let suggestionDistance = 4;
        getProperties(target).forEach(function (prop) {
          if (
            // we actually mean to check `Object.prototype` here
            // eslint-disable-next-line no-prototype-builtins
            !Object.prototype.hasOwnProperty(prop) &&
            builtins.indexOf(prop) === -1
          ) {
            let dist = stringDistanceCapped(property, prop, suggestionDistance);
            if (dist < suggestionDistance) {
              suggestion = prop;
              suggestionDistance = dist;
            }
          }
        });

        if (suggestion !== null) {
          throw Error(
            'Invalid Chai property: ' +
              property +
              '. Did you mean "' +
              suggestion +
              '"?'
          );
        } else {
          throw Error('Invalid Chai property: ' + property);
        }
      }

      // Use this proxy getter as the starting point for removing implementation
      // frames from the stack trace of a failed assertion. For property
      // assertions, this prevents the proxy getter from showing up in the stack
      // trace since it's invoked before the property getter. For method and
      // chainable method assertions, this flag will end up getting changed to
      // the method wrapper, which is good since this frame will no longer be in
      // the stack once the method is invoked. Note that Chai builtin assertion
      // properties such as `__flags` are skipped since this is only meant to
      // capture the starting point of an assertion. This step is also skipped
      // if the `lockSsfi` flag is set, thus indicating that this assertion is
      // being called from within another assertion. In that case, the `ssfi`
      // flag is already set to the outer assertion's starting point.
      if (builtins.indexOf(property) === -1 && !flag(target, 'lockSsfi')) {
        flag(target, 'ssfi', proxyGetter);
      }

      return Reflect.get(target, property);
    }
  });
}
### .proxify(object)

Return a proxy of given object that throws an error when a non-existent
property is read. By default, the root cause is assumed to be a misspelled
property, and thus an attempt is made to offer a reasonable suggestion from
the list of existing properties. However, if a nonChainableMethodName is
provided, then the root cause is instead a failure to invoke a non-chainable
method prior to reading the non-existent property.

If proxies are unsupported or disabled via the user's Chai config, then
return object without modification.

@namespace Utils
@template {object} T
@param {T} obj
@param {string} [nonChainableMethodName]
@returns {T}