"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.ScrapingConnector = void 0;

var _superagent = _interopRequireDefault(require("superagent"));

var cj = _interopRequireWildcard(require("cookiejar"));

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj["default"] = obj; return newObj; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

/**
 * Cookie-rewriting details:
 *
 * ScrapingConnector uses a cookie-jar (through superagent) to
 * handle sessions on traditional HTML backends. This lets
 * us make authenticated request client- *and* server-side,
 * for Desktop, etc.
 *
 * To maintain sessions when running server-side, ScrapingConnector
 * supports cookie re-writing. Eg:
 *
 *  1) Imagine you are running shop.com (the real backend) and
 *     upwa-shop.com (a server-side proxy using this Connector).
 *
 *  2) A browser requests a resource from the proxy, which
 *     forwards a request to shop.com. The shop.com response
 *     contains the header "set-cookie: session=1; domain=shop.com"
 *
 *  3) Before responding to the browser, this connector
 *     rewrites the cookie domain to proxy-shop.com, so that
 *     it will be included on the subsequent request from the
 *     browser to the proxy.
 */

/**
 * A base class for screen-scraping connectors.
 *
 * ScrapingConnector does three things:
 *
 *  1) Allows `window` to be injected, so we can swap it for JSDOM
 *     and run the Connector outside of a browser.
 *
 *  2) Supports cookie-based sessions server-side, using a
 *     cookie-jar and domain-name rewriting.
 *
 *  3) Lets us get/set default headers for requests.
 *
 */
var ScrapingConnector =
/*#__PURE__*/
function () {
  /**
   * @param {Window} window A browser-native window or JSDOM implementation.
   * @param {Object.<string, string>} cookieDomainRewrites the domains to rewrite
   *   on any cookies received from the backend, eg.`{"shop.com": "proxy-shop.com"}`
   */
  function ScrapingConnector(_ref) {
    var window = _ref.window,
        cookieDomainRewrites = _ref.cookieDomainRewrites;

    _classCallCheck(this, ScrapingConnector);

    this.window = window;
    this.cookieDomainRewrites = cookieDomainRewrites || {};
    this.agent = _superagent["default"].agent();
    this.cookieJarSnapshot = null;
  }
  /**
   * Return a Document constructed from a superagent HTTP response. Use
   * to parse content out of the DOM.
   *
   * @param {HTTPResponse} res a superagent http response, expected to be HTML.
   * @returns {Document}
   */


  _createClass(ScrapingConnector, [{
    key: "buildDocument",
    value: function buildDocument(res) {
      var doc = this.window.document.implementation.createHTMLDocument();
      doc.documentElement.innerHTML = res.text;
      return doc;
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "getDefaultHeaders",
    value: function getDefaultHeaders() {
      var headers = this.agent._defaults.filter(function (defaultCall) {
        return defaultCall.fn === 'set';
      }).map(function (header) {
        return _defineProperty({}, header.arguments[0], header.arguments[1]);
      });

      return Object.assign({}, headers);
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "setDefaultHeaders",
    value: function setDefaultHeaders(headers) {
      var _this = this;

      Object.keys(headers).forEach(function (k) {
        _this.agent.set(k, headers[k]);
      });
    }
  }, {
    key: "inBrowser",
    value: function inBrowser() {
      return typeof window !== 'undefined';
    }
    /**
     * Get all *new or changed* cookies in the Connector's cookie jar,
     * since creation or the last call to `setCookies()`.
     *
     * Each returned cookie can be set directly as a header on an HTTP
     * response in the "set-cookie" header.
     *
     * This is only for use *outside* a browser. In the browser, cookies
     * are handled automatically.
     *
     * Example return value:
     *
     *    `["a=b; domain=example.com; path=/", "c=d; domain=example2.com; path=/"]`
     *
     * @returns {string[]}
     */

  }, {
    key: "getCookies",
    value: function getCookies() {
      if (!this.inBrowser()) {
        var rewrites = this.cookieDomainRewrites || {};

        var rewriter = function rewriter(cookie) {
          return ScrapingConnector.rewriteCookieDomain(rewrites, cookie);
        };

        var snapshot = this.cookieJarSnapshot || new cj.CookieJar();
        return ScrapingConnector.diffCookieJars(snapshot, this.agent.jar).map(rewriter).map(function (cookie) {
          return cookie.toString();
        });
      } else {
        return [];
      }
    }
    /**
     * Set the initial cookies on the connector.
     *
     * The `cookies` parameter must be in the format for the `Cookie` header
     * taken directly from an HTTP request, eg: `"a=b; c=d"`
     *
     * @param {String} cookies
     */

  }, {
    key: "setCookies",
    value: function setCookies(cookies) {
      var _this2 = this;

      if (!this.inBrowser()) {
        cookies = cookies || '';
        cookies.split('; ').filter(function (x) {
          return x !== '';
        }).forEach(function (c) {
          return _this2.agent.jar.setCookies(c);
        });
        this.cookieJarSnapshot = ScrapingConnector.cloneCookieJar(this.agent.jar);
      }
    }
  }], [{
    key: "cloneCookie",
    value: function cloneCookie(cookie) {
      return Object.assign(new cj.Cookie(), cookie);
    }
  }, {
    key: "cloneCookieJar",
    value: function cloneCookieJar(jar) {
      var clone = new cj.CookieJar();
      jar.getCookies(cj.CookieAccessInfo.All).forEach(function (c) {
        clone.setCookie(ScrapingConnector.cloneCookie(c));
      });
      return clone;
    }
    /**
     * Returns the cookies that are new or changed in `newJar`.
     *
     * @param oldJar {CookieJar}
     * @param newJar {CookieJar}
     * @returns {Array<Cookie>}
     */

  }, {
    key: "diffCookieJars",
    value: function diffCookieJars(oldJar, newJar) {
      var oldCookies = oldJar.getCookies(cj.CookieAccessInfo.All).map(function (c) {
        return c.toString();
      });
      return newJar.getCookies(cj.CookieAccessInfo.All).filter(function (c) {
        return oldCookies.indexOf(c.toString()) < 0;
      });
    }
    /**
     * Clone `cookie` replace `originalDomain` with `proxyDomain` if the
     * original cookie had an explicit domain set.
     *
     * This is for server-side usage, eg. when running the IM as a proxy
     * on a different domain than the original ecommerce backend.
     *
     * @param {Object<String, String>} rewrites
     * @param {Array<Cookie>} cookie
     * @returns {Array<Cookie>}
     */

  }, {
    key: "rewriteCookieDomain",
    value: function rewriteCookieDomain(rewrites, cookie) {
      var clone = ScrapingConnector.cloneCookie(cookie);
      clone.domain = rewrites[clone.domain] || clone.domain;
      return clone;
    }
  }]);

  return ScrapingConnector;
}();

exports.ScrapingConnector = ScrapingConnector;