{"version":3,"sources":["jsdelivr-header.js","/npm/cloudscraper@4.6.0/index.js"],"names":[],"mappings":"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","file":"/npm/cloudscraper@4.6.0/index.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/cloudscraper@4.6.0/index.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","'use strict';\n\nconst requestModule = require('request-promise');\nconst sandbox = require('./lib/sandbox');\nconst decodeEmails = require('./lib/email-decode.js');\nconst { getDefaultHeaders, caseless } = require('./lib/headers');\nconst brotli = require('./lib/brotli');\nconst crypto = require('crypto');\nconst { deprecate } = require('util');\n\nconst {\n  RequestError,\n  CaptchaError,\n  CloudflareError,\n  ParserError\n} = require('./errors');\n\nlet debugging = false;\n\nconst HOST = Symbol('host');\n\nmodule.exports = defaults.call(requestModule);\n\nfunction defaults (params) {\n  // isCloudScraper === !isRequestModule\n  const isRequestModule = this === requestModule;\n\n  let defaultParams = (!isRequestModule && this.defaultParams) || {\n    requester: requestModule,\n    // Cookies should be enabled\n    jar: requestModule.jar(),\n    headers: getDefaultHeaders({ Host: HOST }),\n    // Reduce Cloudflare's timeout to cloudflareMaxTimeout if it is excessive\n    cloudflareMaxTimeout: 30000,\n    // followAllRedirects - follow non-GET HTTP 3xx responses as redirects\n    followAllRedirects: true,\n    // Support only this max challenges in row. If CF returns more, throw an error\n    challengesToSolve: 3,\n    // Remove Cloudflare's email protection\n    decodeEmails: false,\n    // Support gzip encoded responses\n    gzip: true,\n    agentOptions: {\n      // Removes a few problematic TLSv1.0 ciphers to avoid CAPTCHA\n      ciphers: crypto.constants.defaultCipherList + ':!ECDHE+SHA:!AES128-SHA'\n    }\n  };\n\n  // Object.assign requires at least nodejs v4, request only test/supports v6+\n  defaultParams = Object.assign({}, defaultParams, params);\n\n  const cloudscraper = requestModule.defaults\n    .call(this, defaultParams, function (options) {\n      validateRequest(options);\n      return performRequest(options, true);\n    });\n\n  // There's no safety net here, any changes apply to all future requests\n  // that are made with this instance and derived instances.\n  cloudscraper.defaultParams = defaultParams;\n\n  // Ensure this instance gets a copy of our custom defaults function\n  // and afterwards, it will be copied over automatically.\n  if (isRequestModule) {\n    cloudscraper.defaults = defaults;\n  }\n\n  // Expose the debug option\n  Object.defineProperty(cloudscraper, 'debug', {\n    configurable: true,\n    enumerable: true,\n    set (value) {\n      requestModule.debug = debugging = true;\n    },\n    get () {\n      return debugging;\n    }\n  });\n\n  return cloudscraper;\n}\n\nfunction validateRequest (options) {\n  // Prevent overwriting realEncoding in subsequent calls\n  if (!('realEncoding' in options)) {\n    // Can't just do the normal options.encoding || 'utf8'\n    // because null is a valid encoding.\n    if ('encoding' in options) {\n      options.realEncoding = options.encoding;\n    } else {\n      options.realEncoding = 'utf8';\n    }\n  }\n\n  options.encoding = null;\n\n  if (isNaN(options.challengesToSolve)) {\n    throw new TypeError('Expected `challengesToSolve` option to be a number, ' +\n      'got ' + typeof (options.challengesToSolve) + ' instead.');\n  }\n\n  if (isNaN(options.cloudflareMaxTimeout)) {\n    throw new TypeError('Expected `cloudflareMaxTimeout` option to be a number, ' +\n      'got ' + typeof (options.cloudflareMaxTimeout) + ' instead.');\n  }\n\n  if (typeof options.requester !== 'function') {\n    throw new TypeError('Expected `requester` option to be a function, got ' +\n      typeof (options.requester) + ' instead.');\n  }\n}\n\n// This function is wrapped to ensure that we get new options on first call.\n// The options object is reused in subsequent calls when calling it directly.\nfunction performRequest (options, isFirstRequest) {\n  // This should be the default export of either request or request-promise.\n  const requester = options.requester;\n\n  // Note that request is always an instanceof ReadableStream, EventEmitter\n  // If the requester is request-promise, it is also thenable.\n  const request = requester(options);\n\n  // We must define the host header ourselves to preserve case and order.\n  if (request.getHeader('host') === HOST) {\n    request.setHeader('host', request.uri.host);\n  }\n\n  // If the requester is not request-promise, ensure we get a callback.\n  if (typeof request.callback !== 'function') {\n    throw new TypeError('Expected a callback function, got ' +\n        typeof (request.callback) + ' instead.');\n  }\n\n  // We only need the callback from the first request.\n  // The other callbacks can be safely ignored.\n  if (isFirstRequest) {\n    // This should be a user supplied callback or request-promise's callback.\n    // The callback is always wrapped/bound to the request instance.\n    options.callback = request.callback;\n  }\n\n  request.removeAllListeners('error')\n    .once('error', function (error) {\n      onRequestResponse(options, error);\n    });\n\n  request.removeAllListeners('complete')\n    .once('complete', function (response, body) {\n      onRequestResponse(options, null, response, body);\n    });\n\n  // Indicate that this is a cloudscraper request\n  request.cloudscraper = true;\n  return request;\n}\n\n// The argument convention is options first where possible, options\n// always before response, and body always after response.\nfunction onRequestResponse (options, error, response, body) {\n  const callback = options.callback;\n\n  // Encoding is null so body should be a buffer object\n  if (error || !body || !body.toString) {\n    // Pure request error (bad connection, wrong url, etc)\n    return callback(new RequestError(error, options, response));\n  }\n\n  const headers = caseless(response.headers);\n\n  response.responseStartTime = Date.now();\n  response.isCloudflare = /^(cloudflare|sucuri)/i.test('' + headers.server);\n  response.isHTML = /text\\/html/i.test('' + headers['content-type']);\n\n  // If body isn't a buffer, this is a custom response body.\n  if (!Buffer.isBuffer(body)) {\n    return callback(null, response, body);\n  }\n\n  // Decompress brotli compressed responses\n  if (/\\bbr\\b/i.test('' + headers['content-encoding'])) {\n    if (!brotli.isAvailable) {\n      const cause = 'Received a Brotli compressed response. Please install brotli';\n      return callback(new RequestError(cause, options, response));\n    }\n\n    try {\n      response.body = body = brotli.decompress(body);\n    } catch (error) {\n      return callback(new RequestError(error, options, response));\n    }\n\n    // Request doesn't handle brotli and would've failed to parse JSON.\n    if (options.json) {\n      try {\n        response.body = body = JSON.parse(body, response.request._jsonReviver);\n        // If successful, this isn't a challenge.\n        return callback(null, response, body);\n      } catch (error) {\n        // Request's debug will log the failure, no need to duplicate.\n      }\n    }\n  }\n\n  if (response.isCloudflare && response.isHTML) {\n    onCloudflareResponse(options, response, body);\n  } else {\n    onRequestComplete(options, response, body);\n  }\n}\n\nfunction onCloudflareResponse (options, response, body) {\n  const callback = options.callback;\n\n  if (body.length < 1) {\n    // This is a 4xx-5xx Cloudflare response with an empty body.\n    return callback(new CloudflareError(response.statusCode, options, response));\n  }\n\n  const stringBody = body.toString('utf8');\n\n  try {\n    validateResponse(options, response, stringBody);\n  } catch (error) {\n    if (error instanceof CaptchaError && typeof options.onCaptcha === 'function') {\n      // Give users a chance to solve the reCAPTCHA via services such as anti-captcha.com\n      return onCaptcha(options, response, stringBody);\n    }\n\n    return callback(error);\n  }\n\n  const isChallenge = stringBody.indexOf('a = document.getElementById(\\'jschl-answer\\');') !== -1;\n\n  if (isChallenge) {\n    return onChallenge(options, response, stringBody);\n  }\n\n  const isRedirectChallenge = stringBody.indexOf('You are being redirected') !== -1 ||\n    stringBody.indexOf('sucuri_cloudproxy_js') !== -1;\n\n  if (isRedirectChallenge) {\n    return onRedirectChallenge(options, response, stringBody);\n  }\n\n  // 503 status is always a challenge\n  if (response.statusCode === 503) {\n    return onChallenge(options, response, stringBody);\n  }\n\n  // All is good\n  onRequestComplete(options, response, body);\n}\n\nfunction detectRecaptchaVersion (body) {\n  // New version > Dec 2019\n  if (/__cf_chl_captcha_tk__=(.*)/i.test(body)) { // Test for ver2 first, as it also has ver2 fields\n    return 'ver2';\n    // Old version < Dec 2019\n  } else if (body.indexOf('why_captcha') !== -1 || /cdn-cgi\\/l\\/chk_captcha/i.test(body)) {\n    return 'ver1';\n  }\n\n  return false;\n}\n\nfunction validateResponse (options, response, body) {\n  // Finding captcha\n  // Old version < Dec 2019\n  const recaptchaVer = detectRecaptchaVersion(body);\n  if (recaptchaVer) {\n    // Convenience boolean\n    response.isCaptcha = true;\n    throw new CaptchaError('captcha', options, response);\n  }\n\n  // Trying to find '<span class=\"cf-error-code\">1006</span>'\n  const match = body.match(/<\\w+\\s+class=\"cf-error-code\">(.*)<\\/\\w+>/i);\n\n  if (match) {\n    const code = parseInt(match[1]);\n    throw new CloudflareError(code, options, response);\n  }\n\n  return false;\n}\n\nfunction onChallenge (options, response, body) {\n  const callback = options.callback;\n  const uri = response.request.uri;\n  // The query string to send back to Cloudflare\n  const payload = { /* s, jschl_vc, pass, jschl_answer */ };\n\n  let cause;\n  let error;\n\n  if (options.challengesToSolve === 0) {\n    cause = 'Cloudflare challenge loop';\n    error = new CloudflareError(cause, options, response);\n    error.errorType = 4;\n\n    return callback(error);\n  }\n\n  let timeout = parseInt(options.cloudflareTimeout);\n  let match;\n\n  match = body.match(/name=\"(.+?)\" value=\"(.+?)\"/);\n\n  if (match) {\n    const hiddenInputName = match[1];\n    payload[hiddenInputName] = match[2];\n  }\n\n  match = body.match(/name=\"jschl_vc\" value=\"(\\w+)\"/);\n  if (!match) {\n    cause = 'challengeId (jschl_vc) extraction failed';\n    return callback(new ParserError(cause, options, response));\n  }\n\n  payload.jschl_vc = match[1];\n\n  match = body.match(/name=\"pass\" value=\"(.+?)\"/);\n  if (!match) {\n    cause = 'Attribute (pass) value extraction failed';\n    return callback(new ParserError(cause, options, response));\n  }\n\n  payload.pass = match[1];\n\n  match = body.match(/getElementById\\('cf-content'\\)[\\s\\S]+?setTimeout.+?\\r?\\n([\\s\\S]+?a\\.value\\s*=.+?)\\r?\\n(?:[^{<>]*},\\s*(\\d{4,}))?/);\n  if (!match) {\n    cause = 'setTimeout callback extraction failed';\n    return callback(new ParserError(cause, options, response));\n  }\n\n  if (isNaN(timeout)) {\n    if (match[2] !== undefined) {\n      timeout = parseInt(match[2]);\n\n      if (timeout > options.cloudflareMaxTimeout) {\n        if (debugging) {\n          console.warn('Cloudflare\\'s timeout is excessive: ' + (timeout / 1000) + 's');\n        }\n\n        timeout = options.cloudflareMaxTimeout;\n      }\n    } else {\n      cause = 'Failed to parse challenge timeout';\n      return callback(new ParserError(cause, options, response));\n    }\n  }\n\n  // Append a.value so it's always returned from the vm\n  response.challenge = match[1] + '; a.value';\n\n  try {\n    const ctx = new sandbox.Context({ hostname: uri.hostname, body });\n    payload.jschl_answer = sandbox.eval(response.challenge, ctx);\n  } catch (error) {\n    error.message = 'Challenge evaluation failed: ' + error.message;\n    return callback(new ParserError(error, options, response));\n  }\n\n  if (isNaN(payload.jschl_answer)) {\n    cause = 'Challenge answer is not a number';\n    return callback(new ParserError(cause, options, response));\n  }\n\n  // Prevent reusing the headers object to simplify unit testing.\n  options.headers = Object.assign({}, options.headers);\n  // Use the original uri as the referer and to construct the answer uri.\n  options.headers.Referer = uri.href;\n  // Check is form to be submitted via GET or POST\n  match = body.match(/id=\"challenge-form\" action=\"(.+?)\" method=\"(.+?)\"/);\n  if (match && match[2] && match[2] === 'POST') {\n    options.uri = uri.protocol + '//' + uri.host + match[1];\n    // Pass the payload using body form\n    options.form = payload;\n    options.method = 'POST';\n  } else {\n    // Whatever is there, fallback to GET\n    options.uri = uri.protocol + '//' + uri.host + '/cdn-cgi/l/chk_jschl';\n    // Pass the payload using query string\n    options.qs = payload;\n  }\n  // Decrement the number of challenges to solve.\n  options.challengesToSolve -= 1;\n  // baseUrl can't be used in conjunction with an absolute uri\n  if (options.baseUrl !== undefined) {\n    options.baseUrl = undefined;\n  }\n  // Change required by Cloudflate in Jan-Feb 2020\n  options.uri = options.uri.replace(/&amp;/g, '&');\n\n  // Make request with answer after delay.\n  timeout -= Date.now() - response.responseStartTime;\n  setTimeout(performRequest, timeout, options, false);\n}\n\n// Parses the reCAPTCHA form and hands control over to the user\nfunction onCaptcha (options, response, body) {\n  const recaptchaVer = detectRecaptchaVersion(body);\n  const isRecaptchaVer2 = recaptchaVer === 'ver2';\n  const callback = options.callback;\n  // UDF that has the responsibility of returning control back to cloudscraper\n  const handler = options.onCaptcha;\n  // The form data to send back to Cloudflare\n  const payload = { /* r|s, g-re-captcha-response */ };\n\n  let cause;\n  let match;\n\n  match = body.match(/<form(?: [^<>]*)? id=[\"']?challenge-form['\"]?(?: [^<>]*)?>([\\S\\s]*?)<\\/form>/);\n  if (!match) {\n    cause = 'Challenge form extraction failed';\n    return callback(new ParserError(cause, options, response));\n  }\n\n  const form = match[1];\n\n  let siteKey;\n  let rayId; // only for ver 2\n\n  if (isRecaptchaVer2) {\n    match = body.match(/\\sdata-ray=[\"']?([^\\s\"'<>&]+)/);\n    if (!match) {\n      cause = 'Unable to find cloudflare ray id';\n      return callback(new ParserError(cause, options, response));\n    }\n    rayId = match[1];\n  }\n\n  match = body.match(/\\sdata-sitekey=[\"']?([^\\s\"'<>&]+)/);\n  if (match) {\n    siteKey = match[1];\n  } else {\n    const keys = [];\n    const re = /\\/recaptcha\\/api2?\\/(?:fallback|anchor|bframe)\\?(?:[^\\s<>]+&(?:amp;)?)?[Kk]=[\"']?([^\\s\"'<>&]+)/g;\n\n    while ((match = re.exec(body)) !== null) {\n      // Prioritize the explicit fallback siteKey over other matches\n      if (match[0].indexOf('fallback') !== -1) {\n        keys.unshift(match[1]);\n        if (!debugging) break;\n      } else {\n        keys.push(match[1]);\n      }\n    }\n\n    siteKey = keys[0];\n\n    if (!siteKey) {\n      cause = 'Unable to find the reCAPTCHA site key';\n      return callback(new ParserError(cause, options, response));\n    }\n\n    if (debugging) {\n      console.warn('Failed to find data-sitekey, using a fallback:', keys);\n    }\n  }\n\n  // Everything that is needed to solve the reCAPTCHA\n  response.captcha = {\n    siteKey,\n    uri: response.request.uri,\n    form: payload,\n    version: recaptchaVer\n  };\n\n  if (isRecaptchaVer2) {\n    response.rayId = rayId;\n\n    match = body.match(/id=\"challenge-form\" action=\"(.+?)\" method=\"(.+?)\"/);\n    if (!match) {\n      cause = 'Challenge form action and method extraction failed';\n      return callback(new ParserError(cause, options, response));\n    }\n    response.captcha.formMethod = match[2];\n    match = match[1].match(/\\/(.*)/);\n    response.captcha.formActionUri = match[0];\n    payload.id = rayId;\n  }\n\n  Object.defineProperty(response.captcha, 'url', {\n    configurable: true,\n    enumerable: false,\n    get: deprecate(function () {\n      return response.request.uri.href;\n    }, 'captcha.url is deprecated. Please use captcha.uri instead.')\n  });\n\n  // Adding formData\n  match = form.match(/<input(?: [^<>]*)? name=[^<>]+>/g);\n  if (!match) {\n    cause = 'Challenge form is missing inputs';\n    return callback(new ParserError(cause, options, response));\n  }\n\n  const inputs = match;\n  // Only adding inputs that have both a name and value defined\n  for (let name, value, i = 0; i < inputs.length; i++) {\n    name = inputs[i].match(/name=[\"']?([^\\s\"'<>]*)/);\n    if (name) {\n      value = inputs[i].match(/value=[\"']?([^\\s\"'<>]*)/);\n      if (value) {\n        payload[name[1]] = value[1];\n      }\n    }\n  }\n\n  // Sanity check\n  if (!payload.s && !payload.r) {\n    cause = 'Challenge form is missing secret input';\n    return callback(new ParserError(cause, options, response));\n  }\n\n  if (debugging) {\n    console.warn('Captcha:', response.captcha);\n  }\n\n  // The callback used to green light form submission\n  const submit = function (error) {\n    if (error) {\n      // Pass an user defined error back to the original request call\n      return callback(new CaptchaError(error, options, response));\n    }\n\n    onSubmitCaptcha(options, response);\n  };\n\n  // This seems like an okay-ish API (fewer arguments to the handler)\n  response.captcha.submit = submit;\n\n  // We're handing control over to the user now.\n  const thenable = handler(options, response, body);\n  // Handle the case where the user returns a promise\n  if (thenable && typeof thenable.then === 'function') {\n    // eslint-disable-next-line promise/catch-or-return\n    thenable.then(submit, function (error) {\n      if (!error) {\n        // The user broke their promise with a falsy error\n        submit(new Error('Falsy error'));\n      } else {\n        submit(error);\n      }\n    });\n  }\n}\n\nfunction onSubmitCaptcha (options, response) {\n  const callback = options.callback;\n  const uri = response.request.uri;\n  const isRecaptchaVer2 = response.captcha.version === 'ver2';\n\n  if (!response.captcha.form['g-recaptcha-response']) {\n    const cause = 'Form submission without g-recaptcha-response';\n    return callback(new CaptchaError(cause, options, response));\n  }\n\n  if (isRecaptchaVer2) {\n    options.qs = {\n      __cf_chl_captcha_tk__: response.captcha.formActionUri.match(/__cf_chl_captcha_tk__=(.*)/)[1]\n    };\n\n    options.form = response.captcha.form;\n  } else {\n    options.qs = response.captcha.form;\n  }\n\n  options.method = response.captcha.formMethod || 'GET';\n\n  // Prevent reusing the headers object to simplify unit testing.\n  options.headers = Object.assign({}, options.headers);\n  // Use the original uri as the referer and to construct the form action.\n  options.headers.Referer = uri.href;\n  if (isRecaptchaVer2) {\n    options.uri = uri.protocol + '//' + uri.host + response.captcha.formActionUri;\n  } else {\n    options.uri = uri.protocol + '//' + uri.host + '/cdn-cgi/l/chk_captcha';\n  }\n\n  performRequest(options, false);\n}\n\nfunction onRedirectChallenge (options, response, body) {\n  const callback = options.callback;\n  const uri = response.request.uri;\n\n  const match = body.match(/S='([^']+)'/);\n  if (!match) {\n    const cause = 'Cookie code extraction failed';\n    return callback(new ParserError(cause, options, response));\n  }\n\n  const base64EncodedCode = match[1];\n  response.challenge = Buffer.from(base64EncodedCode, 'base64').toString('ascii');\n\n  try {\n    // Evaluate cookie setting code\n    const ctx = new sandbox.Context();\n    sandbox.eval(response.challenge, ctx);\n\n    options.jar.setCookie(ctx.document.cookie, uri.href, { ignoreError: true });\n  } catch (error) {\n    error.message = 'Cookie code evaluation failed: ' + error.message;\n    return callback(new ParserError(error, options, response));\n  }\n\n  options.challengesToSolve -= 1;\n\n  performRequest(options, false);\n}\n\nfunction onRequestComplete (options, response, body) {\n  const callback = options.callback;\n\n  if (typeof options.realEncoding === 'string') {\n    body = body.toString(options.realEncoding);\n    // The resolveWithFullResponse option will resolve with the response\n    // object. This changes the response.body so it is as expected.\n\n    if (response.isHTML && options.decodeEmails) {\n      body = decodeEmails(body);\n    }\n\n    response.body = body;\n  }\n\n  callback(null, response, body);\n}\n"]}