diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4bef7351..71a57fb2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,17 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](http://keepachangelog.com/)
 and this project adheres to [Semantic Versioning](http://semver.org/).
 
-## [1.8.0] - 2018-02-25
+## [2.0.0] - 2019-03-14
+### Added
+- Support for multiple instances of PxEnforcer (for multi px-app in same web app)
+
+### Refactored
+- Major parts of the code to inject an instance of PxLogger and PX config.
+
+### Changed
+- Changed PxClient.submitActivities() signature to receive a config object.
+
+## [1.8.0] - 2019-02-25
 ### Added
 - Support for testing blocking flow in monitor mode
 
diff --git a/README.md b/README.md
index 2798d94a..91e18a20 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
 [PerimeterX](http://www.perimeterx.com) Shared base for NodeJS enforcers
 =============================================================
 
-> Latest stable version: [v1.8.0](https://www.npmjs.com/package/perimeterx-node-core)
+> Latest stable version: [v2.0.0](https://www.npmjs.com/package/perimeterx-node-core)
 
 This is a shared base implementation for PerimeterX Express enforcer and future NodeJS enforcers. For a fully functioning implementation example, see the [Node-Express enforcer](https://github.com/PerimeterX/perimeterx-node-express/) implementation.
 
@@ -28,9 +28,9 @@ Table of Contents
 ### <a name="basic-usage"></a> Basic Usage Example
 To integrate this module into an enforcer, users should initialize the enforcer.
 ```javascript
-function initPXModule(params) {
+function initPXModule(params, client) {
     params.moduleVersion = '<your module version>';
-    enforcer = new PxEnforcer(params);
+    enforcer = new PxEnforcer(params, client);
     //if dynamic configurations is configured
     if (enforcer.config.conf.DYNAMIC_CONFIGURATIONS) {
         setInterval(enforcer.config.confManager.loadData.bind(enforcer.config.confManager), enforcer.config.conf.CONFIGURATION_LOAD_INTERVAL);
@@ -64,9 +64,9 @@ Extend the `PxClient` class to send activities to PerimeterX.
 ```javascript
 const PxClient = require('perimeterx-node-core').PxClient;
 class MyClient extends PxClient {
-    init() {
+    init(config) {
         setInterval(() => {
-            this.submitActivities();
+            this.submitActivities(config);
         }, 1000);
     }
 }
@@ -77,7 +77,7 @@ Make sure to pass the client instance when initializing the enforcer.
 ```javascript
 function initPXModule(params) {
     params.moduleVersion = '<your module version>';
-    let pxClient = new MyClient();
+    const pxClient = new MyClient();
     enforcer = new PxEnforcer(params, pxClient);
     //if dynamic configurations is configured
     if (enforcer.config.conf.DYNAMIC_CONFIGURATIONS) {
diff --git a/index.js b/index.js
index cc807570..c04c1402 100644
--- a/index.js
+++ b/index.js
@@ -26,5 +26,4 @@
 module.exports = {
     PxEnforcer: require('./lib/pxenforcer'),
     PxClient: require('./lib/pxclient'),
-    request: require('./lib/request')
 };
\ No newline at end of file
diff --git a/lib/configloader.js b/lib/configloader.js
index 008bfc6a..01e8e129 100644
--- a/lib/configloader.js
+++ b/lib/configloader.js
@@ -1,10 +1,10 @@
-
-const logger = require('./pxlogger');
+const request = require('./request');
 
 class ConfigLoader {
 
-    constructor(config, pxClient) {
-        this.config = config;
+    constructor(pxConfig, pxClient) {
+        this.pxConfig = pxConfig;
+        this.config = pxConfig.conf;
         this.pxClient = pxClient;
     }
 
@@ -13,18 +13,17 @@ class ConfigLoader {
     }
 
     loadData() {
-        const request = require('./request');
         const checksum = this.config.checksum;
         const callData = {
             'url': `https://${this.config.CONFIGURATIONS_HOST}${this.config.CONFIGURATIONS_URI + (checksum ? `?checksum=${checksum}` : '')}`,
             'headers': {Authorization: 'Bearer ' + this.config.AUTH_TOKEN},
             'timeout': this.config.API_TIMEOUT_MS
         };
-        request.get(callData, (error, response) => {
+        request.get(callData, this.config, (error, response) => {
             if (error || !response || !(response.statusCode === 200 || response.statusCode === 204)) {
-                logger.error(`Failed to get configurations: ${error}`);
+                this.config.logger.error(`Failed to get configurations: ${error}`);
                 if (!checksum) { //no configuration loaded and we can't get configuration - disable module
-                    logger.debug('Failed to pull initial config, switching module to disable until remote configuration found');
+                    this.config.logger.debug('Failed to pull initial config, switching module to disable until remote configuration found');
                     this.config.ENABLE_MODULE = false;
                 }
                 return;
@@ -33,7 +32,7 @@ class ConfigLoader {
             // new configuration available
             if (response.statusCode === 200) {
                 const body = JSON.parse(response.body.toString());
-                logger.debug(`Found new configuration - checksum: ${body.checksum}, new configuration: ${JSON.stringify(body)}`);
+                this.config.logger.debug(`Found new configuration - checksum: ${body.checksum}, new configuration: ${JSON.stringify(body)}`);
                 this.config.checksum = body.checksum;
                 this.config.COOKIE_SECRET_KEY = body.cookieKey;
                 this.config.PX_APP_ID = body.appId;
@@ -47,7 +46,7 @@ class ConfigLoader {
                 this.config.MODULE_MODE = body.moduleMode === 'blocking' ? this.config.MONITOR_MODE.BLOCK : this.config.MONITOR_MODE.MONITOR;
                 this.config.FIRST_PARTY_ENABLED = body.firstPartyEnabled;
                 this.config.FIRST_PARTY_XHR_ENABLED = body.firstPartyXhrEnabled;
-                this.pxClient.sendEnforcerTelemetry('remote_config');
+                this.pxClient.sendEnforcerTelemetry('remote_config', this.config);
             }
         });
     }
diff --git a/lib/cookie/cookieV1.js b/lib/cookie/cookieV1.js
index fef789f0..a65fbe05 100644
--- a/lib/cookie/cookieV1.js
+++ b/lib/cookie/cookieV1.js
@@ -5,10 +5,9 @@ const Payload = require('../pxpayload');
 class CookieV1 extends Payload {
 
     constructor(ctx, config) {
-        super();
+        super(config);
         this.pxCookie = ctx.cookies['_px'];
-        this.pxConfig = config;
-        this.pxContext = ctx;
+        this.ctx = ctx;
         this.cookieSecret = config.COOKIE_SECRET_KEY;
     }
 
@@ -33,10 +32,10 @@ class CookieV1 extends Payload {
         const baseHmacStr = '' + this.getTime() + this.decodedCookie.s.a + this.getScore() + this.getUuid() + this.getVid();
 
         // hmac string with IP - for backward support
-        const hmacWithIp = baseHmacStr + this.pxContext.ip + this.pxContext.userAgent;
+        const hmacWithIp = baseHmacStr + this.ctx.ip + this.ctx.userAgent;
 
         // hmac string without IP
-        const hmacWithoutIp = baseHmacStr + this.pxContext.userAgent;
+        const hmacWithoutIp = baseHmacStr + this.ctx.userAgent;
 
         return this.isHmacValid(hmacWithoutIp, this.getHmac()) || this.isHmacValid(hmacWithIp, this.getHmac());
     }
diff --git a/lib/cookie/cookieV3.js b/lib/cookie/cookieV3.js
index cb705034..bdcfc003 100644
--- a/lib/cookie/cookieV3.js
+++ b/lib/cookie/cookieV3.js
@@ -4,13 +4,12 @@ const Payload = require('../pxpayload');
 
 class CookieV3 extends Payload {
     constructor(ctx, config) {
-        super();
+        super(config);
         let [hash, ...cookie] = ctx.cookies['_px3'].split(':');
         cookie = cookie.join(':');
         this.pxCookie = cookie;
         this.cookieHash = hash;
-        this.pxConfig = config;
-        this.pxContext = ctx;
+        this.ctx = ctx;
         this.cookieSecret = config.COOKIE_SECRET_KEY;
     }
 
@@ -31,7 +30,7 @@ class CookieV3 extends Payload {
     }
 
     isSecure() {
-        const hmacStr = this.pxCookie + this.pxContext.userAgent;
+        const hmacStr = this.pxCookie + this.ctx.userAgent;
         return this.isHmacValid(hmacStr, this.getHmac());
     }
 }
diff --git a/lib/cookie/tokenV1.js b/lib/cookie/tokenV1.js
index f3598288..78bf1b12 100644
--- a/lib/cookie/tokenV1.js
+++ b/lib/cookie/tokenV1.js
@@ -5,10 +5,9 @@ const Payload = require('../pxpayload');
 class TokenV1 extends Payload {
 
     constructor(ctx, config, token) {
-        super();
+        super(config);
         this.pxCookie = token;
-        this.pxConfig = config;
-        this.pxContext = ctx;
+        this.ctx = ctx;
         this.cookieSecret = config.COOKIE_SECRET_KEY;
     }
 
@@ -33,7 +32,7 @@ class TokenV1 extends Payload {
         const baseHmacStr = '' + this.getTime() + this.decodedCookie.s.a + this.getScore() + this.getUuid() + this.getVid();
 
         // hmac string with IP - for backward support
-        const hmacWithIp = baseHmacStr + this.pxContext.ip;
+        const hmacWithIp = baseHmacStr + this.ctx.ip;
 
         // hmac string without IP
         const hmacWithoutIp = baseHmacStr;
diff --git a/lib/cookie/tokenV3.js b/lib/cookie/tokenV3.js
index 8e3b71d6..13ba0346 100644
--- a/lib/cookie/tokenV3.js
+++ b/lib/cookie/tokenV3.js
@@ -4,13 +4,12 @@ const Payload = require('../pxpayload');
 
 class TokenV3 extends Payload {
     constructor(ctx, config, token) {
-        super();
+        super(config);
         let [hash, ...cookie] = token.split(':');
         cookie = cookie.join(':');
         this.pxCookie = cookie;
         this.cookieHash = hash;
-        this.pxConfig = config;
-        this.pxContext = ctx;
+        this.ctx = ctx;
         this.cookieSecret = config.COOKIE_SECRET_KEY;
     }
 
diff --git a/lib/pxapi.js b/lib/pxapi.js
index 37c75da1..d20b9307 100644
--- a/lib/pxapi.js
+++ b/lib/pxapi.js
@@ -1,28 +1,25 @@
 'use strict';
 const pxUtil = require('./pxutil');
 const pxHttpc = require('./pxhttpc');
-const pxLogger = require('./pxlogger');
-const pxConfig = require('./pxconfig');
 
 exports.evalByServerCall = evalByServerCall;
 
 /**
  * callServer - call the perimeterx api server to receive a score for a given user.
  *
- * @param {Object} pxCtx - current request context
+ * @param {Object} ctx - current request context
  * @param {Function} callback - callback function.
  */
-function callServer(pxCtx, callback) {
-    const config = pxConfig.conf;
-    const ip = pxCtx.ip;
-    const fullUrl = pxCtx.fullUrl;
-    const vid = pxCtx.vid || '';
-    const pxhd = pxCtx.pxhd || '';
-    const vidSource = pxCtx.vidSource || '';
-    const uuid = pxCtx.uuid || '';
-    const uri = pxCtx.uri || '/';
-    const headers = pxUtil.formatHeaders(pxCtx.headers);
-    const httpVersion = pxCtx.httpVersion;
+function callServer(ctx, config, callback) {
+    const ip = ctx.ip;
+    const fullUrl = ctx.fullUrl;
+    const vid = ctx.vid || '';
+    const pxhd = ctx.pxhd || '';
+    const vidSource = ctx.vidSource || '';
+    const uuid = ctx.uuid || '';
+    const uri = ctx.uri || '/';
+    const headers = pxUtil.formatHeaders(ctx.headers, config.SENSITIVE_HEADERS);
+    const httpVersion = ctx.httpVersion;
     const riskMode = config.MODULE_MODE === config.MONITOR_MODE.MONITOR ? 'monitor' : 'active_blocking';
 
     const data = {
@@ -34,22 +31,22 @@ function callServer(pxCtx, callback) {
             firstParty: config.FIRST_PARTY_ENABLED
         },
         additional: {
-            s2s_call_reason: pxCtx.s2sCallReason,
+            s2s_call_reason: ctx.s2sCallReason,
             http_version: httpVersion,
-            http_method: pxCtx.httpMethod,
+            http_method: ctx.httpMethod,
             risk_mode: riskMode,
             module_version: config.MODULE_VERSION,
-            cookie_origin: pxCtx.cookieOrigin,
-            request_cookie_names: pxCtx.requestCookieNames
+            cookie_origin: ctx.cookieOrigin,
+            request_cookie_names: ctx.requestCookieNames
         }
     };
 
-    if (pxCtx.s2sCallReason === 'cookie_decryption_failed') {
-        data.additional.px_orig_cookie = pxCtx.getCookie(); //No need strigify, already a string
+    if (ctx.s2sCallReason === 'cookie_decryption_failed') {
+        data.additional.px_orig_cookie = ctx.getCookie(); //No need strigify, already a string
     }
 
-    if (pxCtx.s2sCallReason === 'cookie_expired' || pxCtx.s2sCallReason === 'cookie_validation_failed') {
-        data.additional.px_cookie = JSON.stringify(pxCtx.decodedCookie);
+    if (ctx.s2sCallReason === 'cookie_expired' || ctx.s2sCallReason === 'cookie_validation_failed') {
+        data.additional.px_cookie = JSON.stringify(ctx.decodedCookie);
     }
 
     pxUtil.prepareCustomParams(config, data.additional);
@@ -74,59 +71,57 @@ function callServer(pxCtx, callback) {
     if (pxhd && data.additional.s2s_call_reason === 'no_cookie') {
         data.additional.s2s_call_reason = 'no_cookie_w_vid';
     }
-    if (pxCtx.originalUuid) {
-        data.additional['original_uuid'] = pxCtx.originalUuid;
+    if (ctx.originalUuid) {
+        data.additional['original_uuid'] = ctx.originalUuid;
     }
 
-    if (pxCtx.originalTokenError) {
-        data.additional['original_token_error'] = pxCtx.originalTokenError;
+    if (ctx.originalTokenError) {
+        data.additional['original_token_error'] = ctx.originalTokenError;
     }
 
-    if (pxCtx.originalToken) {
-        data.additional['original_token'] = pxCtx.originalToken;
+    if (ctx.originalToken) {
+        data.additional['original_token'] = ctx.originalToken;
     }
 
-    if (pxCtx.decodedOriginalToken) {
-        data.additional['px_decoded_original_token'] = pxCtx.decodedOriginalToken;
+    if (ctx.decodedOriginalToken) {
+        data.additional['px_decoded_original_token'] = ctx.decodedOriginalToken;
     }
 
-    if(pxCtx.hmac) {
-        data.additional['px_cookie_hmac'] = pxCtx.hmac;
+    if(ctx.hmac) {
+        data.additional['px_cookie_hmac'] = ctx.hmac;
     }
 
-    pxCtx.hasMadeServerCall = true;
-    return pxHttpc.callServer(data, reqHeaders, config.SERVER_TO_SERVER_API_URI, 'query', callback);
+    ctx.hasMadeServerCall = true;
+    return pxHttpc.callServer(data, reqHeaders, config.SERVER_TO_SERVER_API_URI, 'query', config, callback);
 }
 
 /**
  * evalByServerCall - main server to server function, execute a server call for score and process its value to make blocking decisions.
  * '
- * @param {Object} pxCtx - current request context.
+ * @param {Object} ctx - current request context.
  * @param {Function} callback - callback function.
  */
-function evalByServerCall(pxCtx, callback) {
-
-    const config = pxConfig.conf;
-    if (!pxCtx.ip || !pxCtx.headers) {
-        pxLogger.error('perimeterx score evaluation failed. bad parameters.');
+function evalByServerCall(ctx, config, callback) {
+    if (!ctx.ip || !ctx.headers) {
+        config.logger.error('perimeterx score evaluation failed. bad parameters.');
         return callback(config.SCORE_EVALUATE_ACTION.UNEXPECTED_RESULT);
     }
-    pxLogger.debug(`Evaluating Risk API request, call reason: ${pxCtx.s2sCallReason}`);
-    callServer(pxCtx, (err, res) => {
+    config.logger.debug(`Evaluating Risk API request, call reason: ${ctx.s2sCallReason}`);
+    callServer(ctx, config, (err, res) => {
         if (err) {
             if (err === 'timeout') {
-                pxCtx.passReason = config.PASS_REASON.S2S_TIMEOUT;
+                ctx.passReason = config.PASS_REASON.S2S_TIMEOUT;
                 return callback(config.SCORE_EVALUATE_ACTION.S2S_TIMEOUT_PASS);
             }
-            pxLogger.error(`Unexpected exception while evaluating Risk cookie. ${err}`);
-            pxCtx.passReason = pxConfig.conf.PASS_REASON.REQUEST_FAILED;
+            config.logger.error(`Unexpected exception while evaluating Risk cookie. ${err}`);
+            ctx.passReason = config.PASS_REASON.REQUEST_FAILED;
             return callback(config.SCORE_EVALUATE_ACTION.UNEXPECTED_RESULT);
         }
-        pxCtx.pxhd = res.pxhd;
-        const action = isBadRiskScore(res, pxCtx);
+        ctx.pxhd = res.pxhd;
+        const action = isBadRiskScore(res, ctx, config);
         /* score response invalid - pass traffic */
         if (action === -1) {
-            pxLogger.error('perimeterx server query response is invalid');
+            config.logger.error('perimeterx server query response is invalid');
             return callback(config.SCORE_EVALUATE_ACTION.UNEXPECTED_RESULT);
         }
 
@@ -137,7 +132,7 @@ function evalByServerCall(pxCtx, callback) {
 
         /* score crossed threshold - block traffic */
         if (action === 0) {
-            pxCtx.uuid = res.uuid || '';
+            ctx.uuid = res.uuid || '';
             return callback(config.SCORE_EVALUATE_ACTION.BAD_SCORE);
         }
 
@@ -150,7 +145,7 @@ function evalByServerCall(pxCtx, callback) {
  * isBadRiskScore - processing response score and return a block indicator.
  *
  * @param {object} res - perimeterx response object.
- * @param {object} pxCtx - current request context.
+ * @param {object} ctx - current request context.
  *
  * @return {Number} indicator to the validity of the cookie.
  *                   -1 response object is not valid
@@ -158,23 +153,22 @@ function evalByServerCall(pxCtx, callback) {
  *                   1 response valid with good score
  *
  */
-function isBadRiskScore(res, pxCtx) {
+function isBadRiskScore(res, ctx, config) {
     if (!res || !pxUtil.verifyDefined(res.score) || !res.action) {
-        pxCtx.passReason = pxConfig.conf.PASS_REASON.INVALID_RESPONSE;
+        ctx.passReason = config.PASS_REASON.INVALID_RESPONSE;
         return -1;
     }
     const score = res.score;
-    pxCtx.score = score;
-    pxCtx.uuid = res.uuid;
-    if (score >= pxConfig.conf.BLOCKING_SCORE) {
-        pxCtx.blockAction = res.action;
+    ctx.score = score;
+    ctx.uuid = res.uuid;
+    if (score >= config.BLOCKING_SCORE) {
+        ctx.blockAction = res.action;
         if (res.action === 'j' && res.action_data && res.action_data.body) {
-            pxCtx.blockActionData = res.action_data.body;
+            ctx.blockActionData = res.action_data.body;
         }
         return 0;
     } else {
-        pxCtx.passReason =  pxConfig.conf.PASS_REASON.S2S;
+        ctx.passReason =  config.PASS_REASON.S2S;
         return 1;
     }
 }
-
diff --git a/lib/pxclient.js b/lib/pxclient.js
index fff950ec..a8bbc884 100644
--- a/lib/pxclient.js
+++ b/lib/pxclient.js
@@ -1,10 +1,7 @@
 'use strict';
-const pxLogger = require('./pxlogger');
-const pxConfig = require('./pxconfig');
 const pxUtil = require('./pxutil');
 const pxHttpc = require('./pxhttpc');
 const os = require('os');
-
 class PxClient {
     constructor() {
         this.activitiesBuffer = [];
@@ -19,32 +16,32 @@ class PxClient {
      *
      * @param {string} activityType - name of the activity
      * @param {object} details - activities details in key-val format
-     * @param {object} pxCtx - request context
+     * @param {object} ctx - request context
+     * @param {object} config - perimeterx config
      */
-    sendToPerimeterX(activityType, details, pxCtx) {
-        const config = pxConfig.conf;
+    sendToPerimeterX(activityType, details, ctx, config) {
         if (activityType === 'page_requested' && !config.SEND_PAGE_ACTIVITIES) {
             return;
         }
 
-        details['cookie_origin'] = pxCtx.cookieOrigin;
+        details['cookie_origin'] = ctx.cookieOrigin;
         details['module_version'] = config.MODULE_VERSION;
-        if (pxCtx.blockAction && activityType === 'block') {
-            details['block_action'] = pxCtx.blockAction;
+        if (ctx.blockAction && activityType === 'block') {
+            details['block_action'] = ctx.blockAction;
         }
 
         const pxData = {};
         pxData.type = activityType;
-        pxData.headers = pxCtx.headers;
+        pxData.headers = ctx.headers;
         pxData.timestamp = Date.now();
-        pxData.socket_ip = pxCtx.ip;
+        pxData.socket_ip = ctx.ip;
         pxData.px_app_id = config.PX_APP_ID;
-        pxData.url = pxCtx.fullUrl;
-        if (pxCtx.vid) {
-            pxData.vid = pxCtx.vid;
+        pxData.url = ctx.fullUrl;
+        if (ctx.vid) {
+            pxData.vid = ctx.vid;
         }
-        if (pxCtx.pxhd && (activityType === 'page_requested') || activityType === 'block') {
-            pxData.pxhd = pxCtx.pxhd;
+        if (ctx.pxhd && (activityType === 'page_requested') || activityType === 'block') {
+            pxData.pxhd = ctx.pxhd;
             pxUtil.prepareCustomParams(config, details);
         }
         pxData.details = details;
@@ -52,8 +49,7 @@ class PxClient {
         this.activitiesBuffer.push(pxData);
     }
 
-    sendEnforcerTelemetry(updateReason) {
-        const config = pxConfig.conf;
+    sendEnforcerTelemetry(updateReason, config) {
         const headers = {
             'Authorization': 'Bearer ' + config.AUTH_TOKEN,
             'Content-Type': 'application/json'
@@ -73,31 +69,31 @@ class PxClient {
         pxData.px_app_id = config.PX_APP_ID;
         pxData.details = details;
 
-        pxLogger.debug('Sending telemetry activity to perimeterx servers');
+        config.logger.debug('Sending telemetry activity to perimeterx servers');
 
-        this.callServer(pxData, pxConfig.conf.TELEMETRY_URI, headers);
+        this.callServer(pxData, config.TELEMETRY_URI, headers, config);
     }
 
     /**
      * submitActivities - flash activities buffer and send to px servers for processing
      *
      */
-    submitActivities(cb) {
+    submitActivities(config, cb) {
         if (this.activitiesBuffer.length > 0) {
             const headers = {
                 'Content-Type': 'application/json'
             };
 
-            pxLogger.debug('Sending activities to perimeterx servers');
+            config.logger.debug('Sending activities to perimeterx servers');
 
             const tempActivities = this.activitiesBuffer.concat(); //duplicate
             this.activitiesBuffer.splice(0, tempActivities.length);
-            this.callServer(tempActivities, pxConfig.conf.SERVER_COLLECT_URI, headers, cb);
+            this.callServer(tempActivities, config.SERVER_COLLECT_URI, headers, config, cb);
         }
     }
 
-    callServer(data, path, headers, cb) {
-        pxHttpc.callServer(data, headers, path, 'activities');
+    callServer(data, path, headers, config, cb) {
+        pxHttpc.callServer(data, headers, path, 'activities', config);
         if (cb) {
             cb();
         }
diff --git a/lib/pxconfig.js b/lib/pxconfig.js
index e01bbb4c..b26fb4c8 100644
--- a/lib/pxconfig.js
+++ b/lib/pxconfig.js
@@ -1,165 +1,44 @@
 'use strict';
-const ConfigLoader = require('./configloader');
 const TestModeRequestHandler = require('./pxtesthandler');
 const HttpsProxyAgent = require('https-proxy-agent');
-let config = {};
-let configLoader = null;
-const PX_DEFAULT = {};
-const PX_INTERNAL = {};
-
-PX_INTERNAL.MODULE_VERSION = 'NodeJS Module';
-/* internal configurations */
-PX_INTERNAL.SERVER_HOST = 'sapi.perimeterx.net';
-PX_INTERNAL.COLLECTOR_HOST = 'collector.perimeterx.net';
-PX_INTERNAL.CAPTCHA_HOST = 'captcha.px-cdn.net';
-PX_INTERNAL.CLIENT_HOST = 'client.perimeterx.net';
-PX_INTERNAL.CONFIGURATIONS_HOST = 'px-conf.perimeterx.net';
-PX_INTERNAL.SERVER_TO_SERVER_API_URI = '/api/v3/risk';
-PX_INTERNAL.SERVER_CAPTCHA_URI = '/api/v2/risk/captcha';
-PX_INTERNAL.SERVER_COLLECT_URI = '/api/v1/collector/s2s';
-PX_INTERNAL.CONFIGURATIONS_URI = '/api/v1/enforcer';
-PX_INTERNAL.TELEMETRY_URI = '/api/v2/risk/telemetry';
-PX_INTERNAL.ENFORCER_TRUE_IP_HEADER = 'x-px-enforcer-true-ip';
-PX_INTERNAL.FIRST_PARTY_HEADER = 'x-px-first-party';
-PX_INTERNAL.FORWARDED_FOR_HEADER = 'x-forwarded-for';
-PX_INTERNAL.FIRST_PARTY_VENDOR_PATH = '/init.js';
-PX_INTERNAL.FIRST_PARTY_XHR_PATH = '/xhr';
-PX_INTERNAL.FIRST_PARTY_CAPTCHA_PATH = '/captcha';
-PX_INTERNAL.EMPTY_GIF_B64 = 'R0lGODlhAQABAPAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==';
-
-// Cookie encryption configurations
-PX_INTERNAL.COOKIE_ENCRYPTION = true;
-PX_INTERNAL.CE_KEYLEN = 32;
-PX_INTERNAL.CE_IVLEN = 16;
-PX_INTERNAL.CE_ITERATIONS = 1000;
-PX_INTERNAL.CE_DIGEST = 'sha256';
-PX_INTERNAL.CE_ALGO = 'aes-256-cbc';
-
-PX_INTERNAL.STATIC_FILES_EXT = ['.css', '.bmp', '.tif', '.ttf', '.docx', '.woff2', '.js', '.pict', '.tiff', '.eot', '.xlsx', '.jpg', '.csv', '.eps', '.woff', '.xls', '.jpeg', '.doc', '.ejs', '.otf', '.pptx', '.gif', '.pdf', '.swf', '.svg', '.ps', '.ico', '.pls', '.midi', '.svgz', '.class', '.png', '.ppt', '.mid', 'webp', '.jar'];
-
-/* actions */
-PX_INTERNAL.SCORE_EVALUATE_ACTION = {
-    SPECIAL_TOKEN: -6,
-    SENSITIVE_ROUTE: -5,
-    UNEXPECTED_RESULT: -4,
-    NO_COOKIE: -3,
-    COOKIE_INVALID: -2,
-    COOKIE_EXPIRED: -1,
-
-    S2S_PASS_TRAFFIC: 11,
-    COOKIE_PASS_TRAFFIC: 10,
-    S2S_TIMEOUT_PASS: 9,
-    COOKIE_BLOCK_TRAFFIC: -10,
-    S2S_BLOCK_TRAFFIC: -11,
-    CAPTCHA_BLOCK_TRAFFIC: -12,
-    CHALLENGE_BLOCK_TRAFFIC: -13,
-
-    CAPTCHA_PASS: 0,
-    CAPTCHA_BLOCK: 1,
-
-    GOOD_SCORE: 1,
-    BAD_SCORE: 0
-};
-
-PX_INTERNAL.PASS_REASON = {
-    CAPTCHA_TIMEOUT: 'captcha_timeout',
-    CAPTCHA: 'captcha',
-    COOKIE: 'cookie',
-    S2S: 's2s',
-    S2S_TIMEOUT: 's2s_timeout',
-    MONITOR_MODE: 'monitor_mode',
-    INVALID_RESPONSE: 'invalid_response',
-    REQUEST_FAILED: 'request_failed'
-};
-
-PX_INTERNAL.MONITOR_MODE = {
-    MONITOR: 0,
-    BLOCK: 1
-};
+class PxConfig {
+    constructor(params, logger) {
+        this.PX_INTERNAL = pxInternalConfig();
+        this.PX_DEFAULT = pxDefaultConfig(this.PX_INTERNAL);
+        this.config = this.mergeDefaults(params);
+        this.logger = logger;
+        this.config.logger = this.logger;
 
-/* to be defined by the initiating user or dynamically via configuration service */
-PX_DEFAULT.PX_APP_ID = 'PX_APP_ID';
-PX_DEFAULT.ENABLE_MODULE = true;
-PX_DEFAULT.API_TIMEOUT_MS = 1000;
-PX_DEFAULT.BLOCKING_SCORE = 100;
-PX_DEFAULT.COOKIE_SECRET_KEY = 'cookie_secret_key';
-PX_DEFAULT.AUTH_TOKEN = 'auth_token';
-PX_DEFAULT.IP_HEADERS = [];
-PX_DEFAULT.BLOCK_HTML = 'BLOCK';
-PX_DEFAULT.SENSITIVE_HEADERS = ['cookie', 'cookies'];
-PX_DEFAULT.PROXY_URL = '';
-PX_DEFAULT.SEND_PAGE_ACTIVITIES = true;
-PX_DEFAULT.DEBUG_MODE = false;
-PX_DEFAULT.CUSTOM_REQUEST_HANDLER = '';
-PX_DEFAULT.MAX_BUFFER_LEN = 30;
-PX_DEFAULT.GET_USER_IP = '';
-PX_DEFAULT.CSS_REF = [];
-PX_DEFAULT.JS_REF = [];
-PX_DEFAULT.CUSTOM_LOGO = '';
-PX_DEFAULT.LOGO_VISIBILITY = 'hidden';
-PX_DEFAULT.SENSITIVE_ROUTES = [];
-PX_DEFAULT.WHITELIST_ROUTES = [];
-PX_DEFAULT.DYNAMIC_CONFIGURATIONS = false;
-PX_DEFAULT.CONFIGURATION_LOAD_INTERVAL = 5000;
-PX_DEFAULT.MODULE_MODE = PX_INTERNAL.MONITOR_MODE.MONITOR;
-PX_DEFAULT.ADDITIONAL_ACTIVITY_HANDLER = '';
-PX_DEFAULT.ENRICH_CUSTOM_PARAMETERS = '';
-PX_DEFAULT.FIRST_PARTY_ENABLED = true;
-PX_DEFAULT.FIRST_PARTY_XHR_ENABLED = true;
-PX_DEFAULT.TESTING_MODE = false;
-PX_DEFAULT.WHITELIST_EXT = [];
-PX_DEFAULT.BYPASS_MONITOR_HEADER = '';
+        this.config.WHITELIST_EXT = [...this.PX_INTERNAL.STATIC_FILES_EXT, ...this.PX_DEFAULT.WHITELIST_EXT];
 
-class PxConfig {
-    static init(params, pxClient) {
-        config = PxConfig.mergeDefaults(params);
-        config.WHITELIST_EXT = [...PX_INTERNAL.STATIC_FILES_EXT, ...PX_DEFAULT.WHITELIST_EXT];
-        if (PX_DEFAULT.TESTING_MODE) {
-            PX_DEFAULT.CUSTOM_REQUEST_HANDLER = TestModeRequestHandler.testModeRequestHandler;
-        }
-        if (config.PROXY_URL) {
-            config.agent = new HttpsProxyAgent(config.PROXY_URL);
+        if (this.PX_DEFAULT.TESTING_MODE) {
+            this.PX_DEFAULT.CUSTOM_REQUEST_HANDLER = TestModeRequestHandler.testModeRequestHandler;
         }
-        pxClient.sendEnforcerTelemetry('initial_config');
-        if (PX_DEFAULT.DYNAMIC_CONFIGURATIONS) {
-            configLoader = new ConfigLoader(config, pxClient);
-            configLoader.init();
+
+        if (this.config.PROXY_URL) {
+            this.config.agent = new HttpsProxyAgent(this._config.PROXY_URL);
         }
+
+        this.configLoader = null;
     }
 
-    static mergeDefaults(params) {
-        PX_DEFAULT.ENABLE_MODULE = this.configurationsOverriding(PX_DEFAULT, params, 'ENABLE_MODULE', 'enableModule');
-        PX_DEFAULT.PX_APP_ID = this.configurationsOverriding(PX_DEFAULT, params, 'PX_APP_ID', 'pxAppId');
-        //Set SEVER_HOST if app_id is set.
-        PX_INTERNAL.SERVER_HOST = PX_DEFAULT.PX_APP_ID !== 'PX_APP_ID' ? PX_INTERNAL.SERVER_HOST = 'sapi-' + PX_DEFAULT.PX_APP_ID.toLowerCase() + '.perimeterx.net' : 'sapi.perimeterx.net';
-        PX_INTERNAL.COLLECTOR_HOST = PX_DEFAULT.PX_APP_ID !== 'PX_APP_ID' ? PX_INTERNAL.COLLECTOR_HOST = 'collector-' + PX_DEFAULT.PX_APP_ID.toLowerCase() + '.perimeterx.net' : 'collector.perimeterx.net';
-        PX_DEFAULT.COOKIE_SECRET_KEY = this.configurationsOverriding(PX_DEFAULT, params, 'COOKIE_SECRET_KEY', 'cookieSecretKey');
-        PX_DEFAULT.AUTH_TOKEN = this.configurationsOverriding(PX_DEFAULT, params, 'AUTH_TOKEN', 'authToken');
-        PX_DEFAULT.PROXY_URL = this.configurationsOverriding(PX_DEFAULT, params, 'PROXY_URL', 'proxy');
-        PX_DEFAULT.API_TIMEOUT_MS = this.configurationsOverriding(PX_DEFAULT, params, 'API_TIMEOUT_MS', 'apiTimeoutMS');
-        PX_DEFAULT.CUSTOM_REQUEST_HANDLER = this.configurationsOverriding(PX_DEFAULT, params, 'CUSTOM_REQUEST_HANDLER', 'customRequestHandler');
-        PX_DEFAULT.GET_USER_IP = this.configurationsOverriding(PX_DEFAULT, params, 'GET_USER_IP', 'getUserIp');
-        PX_DEFAULT.BLOCKING_SCORE = this.configurationsOverriding(PX_DEFAULT, params, 'BLOCKING_SCORE', 'blockingScore');
-        PX_DEFAULT.IP_HEADERS = this.configurationsOverriding(PX_DEFAULT, params, 'IP_HEADERS', 'ipHeaders');
-        PX_DEFAULT.SEND_PAGE_ACTIVITIES = this.configurationsOverriding(PX_DEFAULT, params, 'SEND_PAGE_ACTIVITIES', 'sendPageActivities');
-        PX_DEFAULT.SENSITIVE_HEADERS = this.configurationsOverriding(PX_DEFAULT, params, 'SENSITIVE_HEADERS', 'sensitiveHeaders');
-        PX_DEFAULT.DEBUG_MODE = this.configurationsOverriding(PX_DEFAULT, params, 'DEBUG_MODE', 'debugMode');
-        PX_DEFAULT.MAX_BUFFER_LEN = this.configurationsOverriding(PX_DEFAULT, params, 'MAX_BUFFER_LEN', 'maxBufferLength');
-        PX_DEFAULT.JS_REF = this.configurationsOverriding(PX_DEFAULT, params, 'JS_REF', 'jsRef');
-        PX_DEFAULT.CSS_REF = this.configurationsOverriding(PX_DEFAULT, params, 'CSS_REF', 'cssRef');
-        PX_DEFAULT.CUSTOM_LOGO = this.configurationsOverriding(PX_DEFAULT, params, 'CUSTOM_LOGO', 'customLogo');
-        PX_DEFAULT.SENSITIVE_ROUTES = this.configurationsOverriding(PX_DEFAULT, params, 'SENSITIVE_ROUTES', 'sensitiveRoutes');
-        PX_DEFAULT.WHITELIST_ROUTES = this.configurationsOverriding(PX_DEFAULT, params, 'WHITELIST_ROUTES', 'whitelistRoutes');
-        PX_DEFAULT.DYNAMIC_CONFIGURATIONS = this.configurationsOverriding(PX_DEFAULT, params, 'DYNAMIC_CONFIGURATIONS', 'dynamicConfigurations');
-        PX_DEFAULT.MODULE_MODE = this.configurationsOverriding(PX_DEFAULT, params, 'MODULE_MODE', 'moduleMode');
-        PX_DEFAULT.FIRST_PARTY_ENABLED = this.configurationsOverriding(PX_DEFAULT, params, 'FIRST_PARTY_ENABLED', 'firstPartyEnabled');
-        PX_INTERNAL.MODULE_VERSION = this.configurationsOverriding(PX_INTERNAL, params, 'MODULE_VERSION', 'moduleVersion');
-        PX_DEFAULT.ADDITIONAL_ACTIVITY_HANDLER = this.configurationsOverriding(PX_DEFAULT, params, 'ADDITIONAL_ACTIVITY_HANDLER', 'additionalActivityHandler');
-        PX_DEFAULT.ENRICH_CUSTOM_PARAMETERS = this.configurationsOverriding(PX_DEFAULT, params, 'ENRICH_CUSTOM_PARAMETERS', 'enrichCustomParameters');
-        PX_DEFAULT.TESTING_MODE = this.configurationsOverriding(PX_DEFAULT, params, 'TESTING_MODE', 'testingMode');
-        PX_DEFAULT.WHITELIST_EXT = this.configurationsOverriding(PX_DEFAULT, params, 'WHITELIST_EXT', 'whitelistExt');
-        PX_DEFAULT.BYPASS_MONITOR_HEADER = this.configurationsOverriding(PX_DEFAULT, params, 'BYPASS_MONITOR_HEADER', 'bypassMonitorHeader');
-        return Object.assign(PX_DEFAULT, PX_INTERNAL);
+    mergeDefaults(params) {
+        const configKeyMapping = [['ENABLE_MODULE', 'enableModule'], ['PX_APP_ID', 'pxAppId'], ['COOKIE_SECRET_KEY', 'cookieSecretKey'], ['AUTH_TOKEN', 'authToken'], ['PROXY_URL', 'proxy'], 
+            ['API_TIMEOUT_MS', 'apiTimeoutMS'], ['CUSTOM_REQUEST_HANDLER', 'customRequestHandler'], ['GET_USER_IP', 'getUserIp'], ['BLOCKING_SCORE', 'blockingScore'], ['IP_HEADERS', 'ipHeaders'], 
+            ['SEND_PAGE_ACTIVITIES', 'sendPageActivities'], ['SENSITIVE_HEADERS', 'sensitiveHeaders'], ['DEBUG_MODE', 'debugMode'], ['MAX_BUFFER_LEN', 'maxBufferLength'], ['JS_REF', 'jsRef'], 
+            ['CSS_REF', 'cssRef'], ['CUSTOM_LOGO', 'customLogo'], ['SENSITIVE_ROUTES', 'sensitiveRoutes'], ['WHITELIST_ROUTES', 'whitelistRoutes'], ['DYNAMIC_CONFIGURATIONS', 'dynamicConfigurations'], 
+            ['MODULE_MODE', 'moduleMode'], ['FIRST_PARTY_ENABLED', 'firstPartyEnabled'], ['ADDITIONAL_ACTIVITY_HANDLER', 'additionalActivityHandler'], ['ENRICH_CUSTOM_PARAMETERS', 'enrichCustomParameters'], 
+            ['TESTING_MODE', 'testingMode'], ['WHITELIST_EXT', 'whitelistExt'], ['BYPASS_MONITOR_HEADER', 'bypassMonitorHeader']];
+
+        configKeyMapping.forEach(([targetKey, sourceKey]) => {
+            this.PX_DEFAULT[targetKey] = PxConfig.configurationsOverriding(this.PX_DEFAULT, params, targetKey, sourceKey);
+        });
+
+        this.PX_INTERNAL.SERVER_HOST = this.PX_DEFAULT.PX_APP_ID !== 'PX_APP_ID' ? this.PX_INTERNAL.SERVER_HOST = 'sapi-' + this.PX_DEFAULT.PX_APP_ID.toLowerCase() + '.perimeterx.net' : 'sapi.perimeterx.net';
+        this.PX_INTERNAL.COLLECTOR_HOST = this.PX_DEFAULT.PX_APP_ID !== 'PX_APP_ID' ? this.PX_INTERNAL.COLLECTOR_HOST = 'collector-' + this.PX_DEFAULT.PX_APP_ID.toLowerCase() + '.perimeterx.net' : 'collector.perimeterx.net';
+        this.PX_INTERNAL.MODULE_VERSION = PxConfig.configurationsOverriding(this.PX_INTERNAL, params, 'MODULE_VERSION', 'moduleVersion');
+
+        return Object.assign(this.PX_DEFAULT, this.PX_INTERNAL);
     }
 
     static configurationsOverriding(conf, params, defaultName, userInput) {
@@ -184,14 +63,122 @@ class PxConfig {
         return params[userInput];
     }
 
-    static get conf() {
-        return config;
+    get conf() {
+        return this.config;
     }
 
-    static get confManager() {
-        return configLoader;
+    get confManager() {
+        return this.configLoader;
     }
 }
 
-module.exports = PxConfig;
+function pxInternalConfig() {
+    return {
+        MODULE_VERSION: 'NodeJS Module',
+        SERVER_HOST: 'sapi.perimeterx.net',
+        COLLECTOR_HOST: 'collector.perimeterx.net',
+        CAPTCHA_HOST: 'captcha.px-cdn.net',
+        CLIENT_HOST: 'client.perimeterx.net',
+        CONFIGURATIONS_HOST: 'px-conf.perimeterx.net',
+        SERVER_TO_SERVER_API_URI: '/api/v3/risk',
+        SERVER_CAPTCHA_URI: '/api/v2/risk/captcha',
+        SERVER_COLLECT_URI: '/api/v1/collector/s2s',
+        CONFIGURATIONS_URI: '/api/v1/enforcer',
+        TELEMETRY_URI: '/api/v2/risk/telemetry',
+        ENFORCER_TRUE_IP_HEADER: 'x-px-enforcer-true-ip',
+        FIRST_PARTY_HEADER: 'x-px-first-party',
+        FORWARDED_FOR_HEADER: 'x-forwarded-for',
+        FIRST_PARTY_VENDOR_PATH: '/init.js',
+        FIRST_PARTY_XHR_PATH: '/xhr',
+        FIRST_PARTY_CAPTCHA_PATH: '/captcha',
+        EMPTY_GIF_B64: 'R0lGODlhAQABAPAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
+        // Cookie encryption configurations
+        COOKIE_ENCRYPTION: true,
+        CE_KEYLEN: 32,
+        CE_IVLEN: 16,
+        CE_ITERATIONS: 1000,
+        CE_DIGEST: 'sha256',
+        CE_ALGO: 'aes-256-cbc',
+        
+        STATIC_FILES_EXT: ['.css', '.bmp', '.tif', '.ttf', '.docx', '.woff2', '.js', '.pict', '.tiff', '.eot', '.xlsx', '.jpg', '.csv', '.eps', '.woff', '.xls', '.jpeg', '.doc', '.ejs', '.otf', '.pptx', '.gif', '.pdf', '.swf', '.svg', '.ps', '.ico', '.pls', '.midi', '.svgz', '.class', '.png', '.ppt', '.mid', 'webp', '.jar'],
+        
+        /* actions */
+        SCORE_EVALUATE_ACTION: {
+            SPECIAL_TOKEN: -6,
+            SENSITIVE_ROUTE: -5,
+            UNEXPECTED_RESULT: -4,
+            NO_COOKIE: -3,
+            COOKIE_INVALID: -2,
+            COOKIE_EXPIRED: -1,
+        
+            S2S_PASS_TRAFFIC: 11,
+            COOKIE_PASS_TRAFFIC: 10,
+            S2S_TIMEOUT_PASS: 9,
+            COOKIE_BLOCK_TRAFFIC: -10,
+            S2S_BLOCK_TRAFFIC: -11,
+            CAPTCHA_BLOCK_TRAFFIC: -12,
+            CHALLENGE_BLOCK_TRAFFIC: -13,
+        
+            CAPTCHA_PASS: 0,
+            CAPTCHA_BLOCK: 1,
+        
+            GOOD_SCORE: 1,
+            BAD_SCORE: 0
+        },
+        
+        PASS_REASON: {
+            CAPTCHA_TIMEOUT: 'captcha_timeout',
+            CAPTCHA: 'captcha',
+            COOKIE: 'cookie',
+            S2S: 's2s',
+            S2S_TIMEOUT: 's2s_timeout',
+            MONITOR_MODE: 'monitor_mode',
+            INVALID_RESPONSE: 'invalid_response',
+            REQUEST_FAILED: 'request_failed'
+        },
+        
+        MONITOR_MODE: {
+            MONITOR: 0,
+            BLOCK: 1
+        },
+    };
+}
+
+/* to be defined by the initiating user or dynamically via configuration service */
+function pxDefaultConfig(PX_INTERNAL) {
+    return {
+        PX_APP_ID: 'PX_APP_ID',
+        ENABLE_MODULE: true,
+        API_TIMEOUT_MS: 1000,
+        BLOCKING_SCORE: 100,
+        COOKIE_SECRET_KEY: 'cookie_secret_key',
+        AUTH_TOKEN: 'auth_token',
+        IP_HEADERS: [],
+        BLOCK_HTML: 'BLOCK',
+        SENSITIVE_HEADERS: ['cookie', 'cookies'],
+        PROXY_URL: '',
+        SEND_PAGE_ACTIVITIES: true,
+        DEBUG_MODE: false,
+        CUSTOM_REQUEST_HANDLER: '',
+        MAX_BUFFER_LEN: 30,
+        GET_USER_IP: '',
+        CSS_REF: [],
+        JS_REF: [],
+        CUSTOM_LOGO: '',
+        LOGO_VISIBILITY: 'hidden',
+        SENSITIVE_ROUTES: [],
+        WHITELIST_ROUTES: [],
+        DYNAMIC_CONFIGURATIONS: false,
+        CONFIGURATION_LOAD_INTERVAL: 5000,
+        MODULE_MODE: PX_INTERNAL.MONITOR_MODE.MONITOR,
+        ADDITIONAL_ACTIVITY_HANDLER: '',
+        ENRICH_CUSTOM_PARAMETERS: '',
+        FIRST_PARTY_ENABLED: true,
+        FIRST_PARTY_XHR_ENABLED: true,
+        TESTING_MODE: false,
+        WHITELIST_EXT: [],
+        BYPASS_MONITOR_HEADER: '',
+    };
+}
 
+module.exports = PxConfig;
\ No newline at end of file
diff --git a/lib/pxcontext.js b/lib/pxcontext.js
index dc2eb8d4..84f15f9d 100644
--- a/lib/pxcontext.js
+++ b/lib/pxcontext.js
@@ -1,74 +1,48 @@
-const net = require('net');
-const pxLogger = require('./pxlogger');
 const pxUtil = require('./pxutil');
 
 class PxContext {
-
-    constructor(config, request) {
-        const userAgent = request.get('user-agent') || request.get('User-Agent') || 'none';
+    constructor(config, req) {
+        const userAgent = req.get('user-agent') || req.get('User-Agent') || 'none';
         const mobileSdkHeader = 'x-px-authorization';
         const mobileSdkOriginalTokenHeader = 'x-px-original-token';
         const vidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
 
         this.cookies = {};
         this.score = 0;
-        this.ip = PxContext.extractIP(config, request);
+        this.ip = pxUtil.extractIP(config, req);
 
-        this.headers = pxUtil.filterSensitiveHeaders(request.headers);
-        this.hostname = request.hostname || request.get('host');
+        this.headers = pxUtil.filterSensitiveHeaders(req.headers, config.SENSITIVE_HEADERS);
+        this.hostname = req.hostname || req.get('host');
         this.userAgent = userAgent;
-        this.uri = request.originalUrl || '/';
-        this.fullUrl = request.protocol + '://' + request.get('host') + request.originalUrl;
-        this.httpVersion = request.httpVersion || '';
-        this.httpMethod = request.method || '';
+        this.uri = req.originalUrl || '/';
+        this.fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl;
+        this.httpVersion = req.httpVersion || '';
+        this.httpMethod = req.method || '';
         this.sensitiveRoute = this.isSpecialRoute(config.SENSITIVE_ROUTES, this.uri);
         this.whitelistRoute = this.isSpecialRoute(config.WHITELIST_ROUTES, this.uri);
         this.cookieOrigin = 'cookie';
         const mobileHeader = this.headers[mobileSdkHeader];
         if (mobileHeader !== undefined) {
             this.cookieOrigin = 'header';
-            pxLogger.debug('Mobile SDK token detected');
+            config.logger.debug('Mobile SDK token detected');
             this.originalToken = this.headers[mobileSdkOriginalTokenHeader];
             const tokenObject = this.getTokenObject(mobileHeader);
             this.cookies[tokenObject.key] = tokenObject.value;
         } else {
-            this.requestCookieNames = Object.keys(request.cookies);
-            Object.keys(request.cookies).forEach(key => {
+            this.requestCookieNames = Object.keys(req.cookies);
+            Object.keys(req.cookies).forEach(key => {
                 if (key.match(/^_px\d?$/)) {
-                    this.cookies[key] = request.cookies[key];
+                    this.cookies[key] = req.cookies[key];
                 } else if (key === '_pxhd') {
-                    this.pxhd = request.cookies[key];
-                } else if ((key === '_pxvid' || key === 'pxvid') && vidRegex.test(request.cookies[key])) {
-                    this.vid = request.cookies[key];
+                    this.pxhd = req.cookies[key];
+                } else if ((key === '_pxvid' || key === 'pxvid') && vidRegex.test(req.cookies[key])) {
+                    this.vid = req.cookies[key];
                     this.vidSource = 'vid_cookie';
                 }
             });
         }
     }
 
-    static extractIP(config, request) {
-        let ip;
-        if (Array.isArray(config.IP_HEADERS)) {
-            config.IP_HEADERS.some(ipHeader => {
-                try {
-                    const headerValue = request.get(ipHeader);
-                    if (headerValue) {
-                        ip = headerValue;
-                        return true;
-                    }
-                } catch (e) {
-                    pxLogger.debug('Failed to use IP_HEADERS from config.');
-                }
-            });
-        } else {
-            ip = typeof config.GET_USER_IP === 'function' && config.GET_USER_IP(request);
-        }
-        if (ip && net.isIP(ip) > 0) {
-            return ip;
-        }
-        return request.ip;
-    }
-
     getCookie() {
         return this.cookies['_px3'] ? this.cookies['_px3'] : this.cookies['_px'];
     }
diff --git a/lib/pxcookie.js b/lib/pxcookie.js
index ea790c9f..c50d32a7 100644
--- a/lib/pxcookie.js
+++ b/lib/pxcookie.js
@@ -1,6 +1,4 @@
 'use strict';
-const pxConfig = require('./pxconfig');
-const pxLogger = require('./pxlogger');
 const CookieV1 = require('./cookie/cookieV1');
 const CookieV3 = require('./cookie/cookieV3');
 const TokenV1 = require('./cookie/tokenV1');
@@ -12,86 +10,84 @@ exports.evalCookie = evalCookie;
 /**
  * evalCookie - main cookie evaluation function. dectypt, decode and verify cookie content. if score.
  *
- * @param {Object} pxCtx - current request context.
+ * @param {Object} ctx - current request context.
  *
  * @return {Number} evaluation results, derived from configured enum on PX_DEFAULT.COOKIE_EVAL. possible values: (NO_COOKIE, COOKIE_INVALID, COOKIE_EXPIRED, UNEXPECTED_RESULT, BAD_SCORE, GOOD_SCORE).
  *
  */
-function evalCookie(pxCtx) {
-
-    const config = pxConfig.conf;
-    const pxCookie = pxCtx.getCookie();
+function evalCookie(ctx, config) {
+    const pxCookie = ctx.getCookie();
 
     try {
-        if (Object.keys(pxCtx.cookies).length === 0) {
-            pxLogger.debug('Cookie is missing');
-            pxCtx.s2sCallReason = 'no_cookie';
+        if (Object.keys(ctx.cookies).length === 0) {
+            config.logger.debug('Cookie is missing');
+            ctx.s2sCallReason = 'no_cookie';
             return config.SCORE_EVALUATE_ACTION.NO_COOKIE;
         }
 
         if (!config.COOKIE_SECRET_KEY) {
-            pxLogger.debug('No cookie key found, pause cookie evaluation');
-            pxCtx.s2sCallReason = 'no_cookie_key';
+            config.logger.debug('No cookie key found, pause cookie evaluation');
+            ctx.s2sCallReason = 'no_cookie_key';
             return config.SCORE_EVALUATE_ACTION.UNEXPECTED_RESULT;
         }
 
         // Mobile SDK traffic
-        if (pxCookie && pxCtx.cookieOrigin === 'header') {
+        if (pxCookie && ctx.cookieOrigin === 'header') {
             if (pxCookie.match(/^\d+$/)) {
-                pxCtx.s2sCallReason = `mobile_error_${pxCookie}`;
-                if (pxCtx.originalToken) {
-                    originalTokenValidator.evalCookie(pxCtx, config);
+                ctx.s2sCallReason = `mobile_error_${pxCookie}`;
+                if (ctx.originalToken) {
+                    originalTokenValidator.evalCookie(ctx, config);
                 }
                 return config.SCORE_EVALUATE_ACTION.SPECIAL_TOKEN;
             }
         }
 
-        const cookie = pxCookieFactory(pxCtx, config);
-        pxLogger.debug(`Cookie ${getCookieVersion(pxCtx)} found, Evaluating`);
+        const cookie = pxCookieFactory(ctx, config);
+        config.logger.debug(`Cookie ${getCookieVersion(ctx)} found, Evaluating`);
         if (!cookie.deserialize()) {
-            pxCtx.s2sCallReason = 'cookie_decryption_failed';
-            pxCtx.px_orig_cookie = getPxCookieFromContext(pxCtx);
-            pxLogger.debug(`Cookie decryption failed, value: ${pxCtx.px_orig_cookie}`);
+            ctx.s2sCallReason = 'cookie_decryption_failed';
+            ctx.px_orig_cookie = getPxCookieFromContext(ctx);
+            config.logger.debug(`Cookie decryption failed, value: ${ctx.px_orig_cookie}`);
             return config.SCORE_EVALUATE_ACTION.COOKIE_INVALID;
         }
 
-        pxCtx.decodedCookie = cookie.decodedCookie;
-        pxCtx.score = cookie.getScore();
-        pxCtx.vid = cookie.getVid();
-        pxCtx.vidSource = 'risk_cookie';
-        pxCtx.uuid = cookie.getUuid();
-        pxCtx.hmac = cookie.getHmac();
-        pxCtx.blockAction = cookie.getBlockAction();
+        ctx.decodedCookie = cookie.decodedCookie;
+        ctx.score = cookie.getScore();
+        ctx.vid = cookie.getVid();
+        ctx.vidSource = 'risk_cookie';
+        ctx.uuid = cookie.getUuid();
+        ctx.hmac = cookie.getHmac();
+        ctx.blockAction = cookie.getBlockAction();
 
         if (cookie.isExpired()) {
-            pxLogger.debug(`Cookie TTL is expired, value: ${JSON.stringify(cookie.decodedCookie)}, age: ${Date.now() - cookie.getTime()}`);
-            pxCtx.s2sCallReason = 'cookie_expired';
+            config.logger.debug(`Cookie TTL is expired, value: ${JSON.stringify(cookie.decodedCookie)}, age: ${Date.now() - cookie.getTime()}`);
+            ctx.s2sCallReason = 'cookie_expired';
             return config.SCORE_EVALUATE_ACTION.COOKIE_EXPIRED;
         }
 
         if (cookie.isHighScore()) {
-            pxLogger.debug(`Cookie evaluation ended successfully, risk score: ${cookie.getScore()}`);
+            config.logger.debug(`Cookie evaluation ended successfully, risk score: ${cookie.getScore()}`);
             return config.SCORE_EVALUATE_ACTION.BAD_SCORE;
         }
 
         if (!cookie.isSecure()) {
-            pxLogger.debug(`Cookie HMAC validation failed, value: ${JSON.stringify(cookie.decodedCookie)} user-agent: ${pxCtx.userAgent}`);
-            pxCtx.s2sCallReason = 'cookie_validation_failed';
+            config.logger.debug(`Cookie HMAC validation failed, value: ${JSON.stringify(cookie.decodedCookie)} user-agent: ${ctx.userAgent}`);
+            ctx.s2sCallReason = 'cookie_validation_failed';
             return config.SCORE_EVALUATE_ACTION.COOKIE_INVALID;
         }
 
-        if (pxCtx.sensitiveRoute) {
-            pxLogger.debug(`Sensitive route match, sending Risk API. path: ${pxCtx.uri}`);
-            pxCtx.s2sCallReason = 'sensitive_route';
+        if (ctx.sensitiveRoute) {
+            config.logger.debug(`Sensitive route match, sending Risk API. path: ${ctx.uri}`);
+            ctx.s2sCallReason = 'sensitive_route';
             return config.SCORE_EVALUATE_ACTION.SENSITIVE_ROUTE;
         }
 
-        pxCtx.passReason = config.PASS_REASON.COOKIE;
-        pxLogger.debug(`Cookie evaluation ended successfully, risk score: ${cookie.getScore()}`);
+        ctx.passReason = config.PASS_REASON.COOKIE;
+        config.logger.debug(`Cookie evaluation ended successfully, risk score: ${cookie.getScore()}`);
         return config.SCORE_EVALUATE_ACTION.GOOD_SCORE;
     } catch (e) {
-        pxLogger.error('Error while evaluating perimeterx cookie: ' + e.message);
-        pxCtx.s2sCallReason = 'cookie_decryption_failed';
+        config.logger.error('Error while evaluating perimeterx cookie: ' + e.message);
+        ctx.s2sCallReason = 'cookie_decryption_failed';
         return config.SCORE_EVALUATE_ACTION.UNEXPECTED_RESULT;
     }
 }
@@ -99,20 +95,20 @@ function evalCookie(pxCtx) {
 /**
  * Factory method for creating PX Cookie object according to cookie version and type found on the request
  */
-function pxCookieFactory(pxCtx, pxConfig) {
-    if (pxCtx.cookieOrigin == 'cookie') {
-        return (pxCtx.cookies['_px3'] ? new CookieV3(pxCtx, pxConfig) : new CookieV1(pxCtx, pxConfig));
+function pxCookieFactory(ctx, config) {
+    if (ctx.cookieOrigin == 'cookie') {
+        return (ctx.cookies['_px3'] ? new CookieV3(ctx, config, config.logger) : new CookieV1(ctx, config, config.logger));
     } else {
-        return (pxCtx.cookies['_px3'] ? new TokenV3(pxCtx, pxConfig, pxCtx.cookies['_px3']) : new TokenV1(pxCtx, pxConfig, pxCtx.cookies['_px']));
+        return (ctx.cookies['_px3'] ? new TokenV3(ctx, config, ctx.cookies['_px3'], config.logger) : new TokenV1(ctx, config, ctx.cookies['_px'], config.logger));
     }
 }
 
-function getCookieVersion(pxCtx) {
-    return pxCtx.cookies['_px3'] ? 'V3' : 'V1';
+function getCookieVersion(ctx) {
+    return ctx.cookies['_px3'] ? 'V3' : 'V1';
 }
 
-function getPxCookieFromContext(pxCtx) {
-    if (Object.keys(pxCtx.cookies).length) {
-        return pxCtx.cookies['_px3'] ? pxCtx.cookies['_px3'] : pxCtx.cookies['_px'];
+function getPxCookieFromContext(ctx) {
+    if (Object.keys(ctx.cookies).length) {
+        return ctx.cookies['_px3'] ? ctx.cookies['_px3'] : ctx.cookies['_px'];
     }
 }
diff --git a/lib/pxenforcer.js b/lib/pxenforcer.js
index e2903a62..954123c3 100644
--- a/lib/pxenforcer.js
+++ b/lib/pxenforcer.js
@@ -1,141 +1,148 @@
 'use strict';
 
+const mu = require('mu2');
 const PxClient = require('./pxclient');
-const pxLogger = require('./pxlogger');
 const PxContext = require('./pxcontext');
 const PxConfig = require('./pxconfig');
-const mu = require('mu2');
 const pxProxy = require('./pxproxy');
 const pxUtil = require('./pxutil');
 const pxApi = require('./pxapi');
 const pxCookie = require('./pxcookie');
+const PxLogger = require('./pxlogger');
+const ConfigLoader = require('./configloader');
 
 class PxEnforcer {
-
     constructor(params, client) {
-        if (client) {
-            this.pxClient = client;
-        } else {
-            this.pxClient = new PxClient();
+        this.logger = new PxLogger();
+
+        this.pxConfig = new PxConfig(params, this.logger);
+        this._config = this.pxConfig.conf;
+
+        this.logger.init(this.pxConfig);
+
+        this.pxClient = (client) ? client : new PxClient();
+        this.pxClient.init(this._config);
+        this.pxClient.sendEnforcerTelemetry('initial_config', this);
+        if (this._config.DYNAMIC_CONFIGURATIONS) {
+            this.config.configLoader = new ConfigLoader(this.pxConfig, this.pxClient);
+            this.config.configLoader.init();
         }
-        this.pxClient.init();
-        PxConfig.init(params, this.pxClient);
-        this.reversePrefix = PxConfig.conf.PX_APP_ID.substring(2);
+
+        this.reversePrefix = this.pxConfig.conf.PX_APP_ID.substring(2);
     }
 
     enforce(req, res, cb) {
-        const pxConfig = PxConfig.conf;
         const requestUrl = req.originalUrl;
-        pxLogger.debug('Starting request verification');
+        this.logger.debug('Starting request verification');
 
-        if (requestUrl.startsWith(`/${this.reversePrefix}${pxConfig.FIRST_PARTY_VENDOR_PATH}`)) {
+        if (requestUrl.startsWith(`/${this.reversePrefix}${this._config.FIRST_PARTY_VENDOR_PATH}`)) {
             // reverse proxy client
-            return pxProxy.getClient(req, pxConfig, PxContext.extractIP(pxConfig, req), cb);
+            return pxProxy.getClient(req, this._config, pxUtil.extractIP(this._config, req), cb);
         }
 
-        if (requestUrl.startsWith(`/${this.reversePrefix}${pxConfig.FIRST_PARTY_XHR_PATH}`)) {
+        if (requestUrl.startsWith(`/${this.reversePrefix}${this._config.FIRST_PARTY_XHR_PATH}`)) {
             //reverse proxy xhr
-            return pxProxy.sendXHR(req, pxConfig, PxContext.extractIP(pxConfig, req), this.reversePrefix, cb);
+            return pxProxy.sendXHR(req, this._config, pxUtil.extractIP(this._config, req), this.reversePrefix, cb);
         }
 
-        if (requestUrl.startsWith(`/${this.reversePrefix}${pxConfig.FIRST_PARTY_CAPTCHA_PATH}`)) {
+        if (requestUrl.startsWith(`/${this.reversePrefix}${this._config.FIRST_PARTY_CAPTCHA_PATH}`)) {
             // reverse proxy captcha
-            return pxProxy.getCaptcha(req, pxConfig, PxContext.extractIP(pxConfig, req), this.reversePrefix, cb);
+            return pxProxy.getCaptcha(req, this._config, pxUtil.extractIP(this._config, req), this.reversePrefix, cb);
         }
 
-        if (!pxConfig.ENABLE_MODULE || pxUtil.checkForStatic(req, pxConfig.STATIC_FILES_EXT)) {
-            pxLogger.debug('Request will not be verified, module is disabled');
+        if (!this._config.ENABLE_MODULE || pxUtil.checkForStatic(req, this._config.STATIC_FILES_EXT)) {
+            this.logger.debug('Request will not be verified, module is disabled');
             return cb();
         }
 
         try {
-            const pxCtx = new PxContext(pxConfig, req);
-            pxLogger.debug('Request context created successfully');
-            pxCtx.collectorUrl = `https://collector-${pxConfig.PX_APP_ID}.perimeterx.net`;
+            const ctx = new PxContext(this._config, req, this.logger);
+            this.logger.debug('Request context created successfully');
+            ctx.collectorUrl = `https://collector-${this._config.PX_APP_ID}.perimeterx.net`;
 
-            if (pxCtx.whitelistRoute) {
-                pxLogger.debug(`Whitelist route match: ${pxCtx.uri}`);
+            if (ctx.whitelistRoute) {
+                this.logger.debug(`Whitelist route match: ${ctx.uri}`);
                 return cb();
             }
 
-            this.verifyUserScore(pxCtx, pxConfig, () => {
-                this.handleVerification(pxCtx, pxConfig, req, res, cb);
+            this.verifyUserScore(ctx, () => {
+                this.handleVerification(ctx, req, res, cb);
             });
         } catch (err) {
             return cb();
         }
     }
 
-    verifyUserScore(pxCtx, pxConfig, callback) {
+    verifyUserScore(ctx, callback) {
         const startRiskRtt = Date.now();
-        pxCtx.riskRtt = 0;
+        ctx.riskRtt = 0;
 
         try {
-            if (!pxCtx.ip || !pxCtx.uri) {
-                pxLogger.error('perimeterx score evaluation failed. bad parameters.');
-                return callback(pxConfig.SCORE_EVALUATE_ACTION.COOKIE_PASS_TRAFFIC);
+            if (!ctx.ip || !ctx.uri) {
+                this.logger.error('perimeterx score evaluation failed. bad parameters.');
+                return callback(this._config.SCORE_EVALUATE_ACTION.COOKIE_PASS_TRAFFIC);
             }
 
-            const action = pxCookie.evalCookie(pxCtx);
+            const action = pxCookie.evalCookie(ctx, this._config);
             /* score did not cross threshold - pass traffic */
-            if (action === pxConfig.SCORE_EVALUATE_ACTION.GOOD_SCORE) {
-                return callback(pxConfig.SCORE_EVALUATE_ACTION.COOKIE_PASS_TRAFFIC);
+            if (action === this._config.SCORE_EVALUATE_ACTION.GOOD_SCORE) {
+                return callback(this._config.SCORE_EVALUATE_ACTION.COOKIE_PASS_TRAFFIC);
             }
 
             /* score crossed threshold - block traffic */
-            if (action === pxConfig.SCORE_EVALUATE_ACTION.BAD_SCORE) {
-                pxCtx.blockReason = 'cookie_high_score';
-                return callback(pxConfig.SCORE_EVALUATE_ACTION.COOKIE_BLOCK_TRAFFIC);
+            if (action === this._config.SCORE_EVALUATE_ACTION.BAD_SCORE) {
+                ctx.blockReason = 'cookie_high_score';
+                return callback(this._config.SCORE_EVALUATE_ACTION.COOKIE_BLOCK_TRAFFIC);
             }
 
             /* when no fallback to s2s call if cookie does not exist or failed on evaluation */
-            pxApi.evalByServerCall(pxCtx, (action) => {
-                pxCtx.riskRtt = Date.now() - startRiskRtt;
+            pxApi.evalByServerCall(ctx, this._config, (action) => {
+                ctx.riskRtt = Date.now() - startRiskRtt;
 
-                if (action === pxConfig.SCORE_EVALUATE_ACTION.UNEXPECTED_RESULT) {
-                    pxLogger.error('perimeterx score evaluation failed. unexpected error. passing traffic');
-                    return callback(pxConfig.SCORE_EVALUATE_ACTION.S2S_PASS_TRAFFIC);
+                if (action === this._config.SCORE_EVALUATE_ACTION.UNEXPECTED_RESULT) {
+                    this.logger.error('perimeterx score evaluation failed. unexpected error. passing traffic');
+                    return callback(this._config.SCORE_EVALUATE_ACTION.S2S_PASS_TRAFFIC);
                 }
 
-                pxLogger.debug(`Risk API response returned successfully, risk score: ${pxCtx.score}, round_trip_time: ${pxCtx.riskRtt}ms`);
+                this.logger.debug(`Risk API response returned successfully, risk score: ${ctx.score}, round_trip_time: ${ctx.riskRtt}ms`);
 
-                if (action === pxConfig.SCORE_EVALUATE_ACTION.GOOD_SCORE) {
-                    pxLogger.debug(`Risk score is lower than blocking score. score: ${pxCtx.score} blocking score: ${pxConfig.BLOCKING_SCORE}`);
-                    return callback(pxConfig.SCORE_EVALUATE_ACTION.S2S_PASS_TRAFFIC);
+                if (action === this._config.SCORE_EVALUATE_ACTION.GOOD_SCORE) {
+                    this.logger.debug(`Risk score is lower than blocking score. score: ${ctx.score} blocking score: ${this._config.BLOCKING_SCORE}`);
+                    return callback(this._config.SCORE_EVALUATE_ACTION.S2S_PASS_TRAFFIC);
                 }
 
-                if (action === pxConfig.SCORE_EVALUATE_ACTION.BAD_SCORE) {
-                    pxLogger.debug(`Risk score is higher or equal to blocking score. score: ${pxCtx.score} blocking score: ${pxConfig.BLOCKING_SCORE}`);
-                    switch (pxCtx.blockAction) {
+                if (action === this._config.SCORE_EVALUATE_ACTION.BAD_SCORE) {
+                    this.logger.debug(`Risk score is higher or equal to blocking score. score: ${ctx.score} blocking score: ${this._config.BLOCKING_SCORE}`);
+                    switch (ctx.blockAction) {
                         case 'j':
-                            pxCtx.blockReason = 'challenge';
+                            ctx.blockReason = 'challenge';
                             break;
                         case 'r':
-                            pxCtx.blockReason = 'exceeded_rate_limit';
+                            ctx.blockReason = 'exceeded_rate_limit';
                             break;
                         default:
-                            pxCtx.blockReason = 's2s_high_score';
+                            ctx.blockReason = 's2s_high_score';
                     }
-                    return callback(pxConfig.SCORE_EVALUATE_ACTION.S2S_BLOCK_TRAFFIC);
+                    return callback(this._config.SCORE_EVALUATE_ACTION.S2S_BLOCK_TRAFFIC);
                 }
 
-                if(action === pxConfig.SCORE_EVALUATE_ACTION.S2S_TIMEOUT_PASS) {
-                    pxLogger.debug(`Risk API timed out , round_trip_time: ${pxCtx.riskRtt}ms`);
-                    return callback(pxConfig.SCORE_EVALUATE_ACTION.S2S_TIMEOUT_PASS);
+                if(action === this._config.SCORE_EVALUATE_ACTION.S2S_TIMEOUT_PASS) {
+                    this.logger.debug(`Risk API timed out , round_trip_time: ${ctx.riskRtt}ms`);
+                    return callback(this._config.SCORE_EVALUATE_ACTION.S2S_TIMEOUT_PASS);
                 }
             });
         } catch (e) {
-            pxLogger.error('perimeterx score evaluation failed. unexpected error. ' + e.message);
-            pxCtx.riskRtt = Date.now() - startRiskRtt;
-            return callback(pxConfig.SCORE_EVALUATE_ACTION.S2S_PASS_TRAFFIC);
+            this.logger.error('perimeterx score evaluation failed. unexpected error. ' + e.message);
+            ctx.riskRtt = Date.now() - startRiskRtt;
+            return callback(this._config.SCORE_EVALUATE_ACTION.S2S_PASS_TRAFFIC);
         }
     }
 
-    handleVerification(pxCtx, pxConfig, req, res, cb) {
-        const verified = pxCtx.score < pxConfig.BLOCKING_SCORE;
+    handleVerification(ctx, req, res, cb) {
+        const verified = ctx.score < this._config.BLOCKING_SCORE;
         if (res) {
             const setCookie = res.getHeader('Set-Cookie') ? res.getHeader('Set-Cookie') : '';
-            const pxhdCookie = pxCtx.pxhd ? '_pxhd=' + pxCtx.pxhd : '';
+            const pxhdCookie = ctx.pxhd ? '_pxhd=' + ctx.pxhd : '';
             const setCookieModified = [setCookie, pxhdCookie].filter(Boolean);
             if (setCookieModified.length > 0) {
                 res.setHeader('Set-Cookie', setCookieModified);
@@ -143,24 +150,24 @@ class PxEnforcer {
         }
         // Handle async activities
         if (verified) {
-            this.pxPass(pxCtx);
+            this.pxPass(ctx);
         } else {
-            this.pxBlock(pxCtx, pxConfig);
+            this.pxBlock(ctx);
         }
 
         // check for additional activity handler
-        if (pxConfig.ADDITIONAL_ACTIVITY_HANDLER) {
-            pxConfig.ADDITIONAL_ACTIVITY_HANDLER(pxCtx, pxConfig);
+        if (this._config.ADDITIONAL_ACTIVITY_HANDLER) {
+            this._config.ADDITIONAL_ACTIVITY_HANDLER(ctx, this._config);
         }
 
-        if (pxConfig.CUSTOM_REQUEST_HANDLER) {
+        if (this._config.CUSTOM_REQUEST_HANDLER) {
             if (res) {
-                pxConfig.CUSTOM_REQUEST_HANDLER(pxCtx, pxConfig, req, res);
+                this._config.CUSTOM_REQUEST_HANDLER(ctx, this._config, req, res);
                 if (res.headersSent) {
                     return;
                 }
             } else {
-                const result = pxConfig.CUSTOM_REQUEST_HANDLER(pxCtx, pxConfig, req);
+                const result = this._config.CUSTOM_REQUEST_HANDLER(ctx, this._config, req);
                 if (result) {
                     return cb(null, result);
                 }
@@ -168,22 +175,23 @@ class PxEnforcer {
         }
 
         // If verified, pass the request here
-        const shouldBypassMonitor = pxConfig.BYPASS_MONITOR_HEADER && req.headers[pxConfig.BYPASS_MONITOR_HEADER] === '1';
-        if (verified || (pxConfig.MODULE_MODE === pxConfig.MONITOR_MODE.MONITOR && !shouldBypassMonitor)) {
+        const shouldBypassMonitor = this._config.BYPASS_MONITOR_HEADER && req.headers[this._config.BYPASS_MONITOR_HEADER] === '1';
+        if (verified || (this._config.MODULE_MODE === this._config.MONITOR_MODE.MONITOR && !shouldBypassMonitor)) {
             return cb();
         }
 
         const acceptHeaderValue = req.headers['accept'] || req.headers['content-type'];
-        const isJsonResponse = acceptHeaderValue && acceptHeaderValue.split(',').find((value) => value.toLowerCase() === 'application/json') && pxCtx.cookieOrigin === 'cookie' && pxCtx.blockAction !== 'r';
+        const isJsonResponse = acceptHeaderValue && acceptHeaderValue.split(',').find((value) => value.toLowerCase() === 'application/json') && ctx.cookieOrigin === 'cookie' && ctx.blockAction !== 'r';
 
-        pxLogger.debug(`Enforcing action:  ${pxUtil.parseAction(pxCtx.blockAction)} page is served ${isJsonResponse ? 'using advanced protection mode' : ''}`);
-        this.generateResponse(pxCtx, pxConfig, isJsonResponse, function (responseObject) {
+        this.logger.debug(`Enforcing action:  ${pxUtil.parseAction(ctx.blockAction)} page is served ${isJsonResponse ? 'using advanced protection mode' : ''}`);
+        const config = this._config;
+        this.generateResponse(ctx, isJsonResponse, function (responseObject) {
             const response = {
                 status: '403',
                 statusDescription: 'Forbidden'
             };
 
-            if (pxCtx.blockAction === 'r') {
+            if (ctx.blockAction === 'r') {
                 response.status = '429';
                 response.statusDescription = 'Too Many Requests';
             }
@@ -205,79 +213,80 @@ class PxEnforcer {
             response.header = {key: 'Content-Type', value: 'text/html'};
             response.body = responseObject;
 
-            if (pxCtx.cookieOrigin !== 'cookie') {
+            if (ctx.cookieOrigin !== 'cookie') {
                 response.header = {key: 'Content-Type', value: 'application/json'};
                 response.body = {
-                    action: pxUtil.parseAction(pxCtx.blockAction),
-                    uuid: pxCtx.uuid,
-                    vid: pxCtx.vid,
-                    appId: pxConfig.PX_APP_ID,
+                    action: pxUtil.parseAction(ctx.blockAction),
+                    uuid: ctx.uuid,
+                    vid: ctx.vid,
+                    appId: config.PX_APP_ID,
                     page: new Buffer(responseObject).toString('base64'),
-                    collectorUrl: pxCtx.collectorUrl
+                    collectorUrl: ctx.collectorUrl
                 };
             }
             cb(null, response);
         });
     }
-
+    
     get config() {
-        return PxConfig;
+        return this.pxConfig;
     }
 
     /**
      * pxPass - pass handler, sends page_requested activity and passes the request using next()
-     * @param {Object} pxCtx - current request context.
+     * @param {Object} ctx - current request context.
+     * @param {Object} config - perimeterx config.
      */
-    pxPass(pxCtx) {
+    pxPass(ctx) {
         const details = {
-            'px_cookie': pxCtx.decodedCookie,
-            'client_uuid': pxCtx.uuid,
-            'pass_reason': pxCtx.passReason,
-            'risk_rtt': pxCtx.riskRtt,
-            'module_version': PxConfig.conf.MODULE_VERSION
+            'px_cookie': ctx.decodedCookie,
+            'client_uuid': ctx.uuid,
+            'pass_reason': ctx.passReason,
+            'risk_rtt': ctx.riskRtt,
+            'module_version': this.pxConfig.conf.MODULE_VERSION
         };
-        pxLogger.debug('Sending page requested activity');
-        this.pxClient.sendToPerimeterX('page_requested', details, pxCtx);
+        this.logger.debug('Sending page requested activity');
+        this.pxClient.sendToPerimeterX('page_requested', details, ctx, this._config);
     }
 
     /**
      * pxBlock - block handler, send blocking activity to px and render the block html back to screen
      *
-     * @param pxConfig
-     * @param {Object} pxCtx - current request context.
+     * @param {Object} ctx - current request context.
+     * @param config
      */
-    pxBlock(pxCtx, pxConfig) {
+    pxBlock(ctx) {
         const details = {
-            block_reason: pxCtx.blockReason,
-            client_uuid: pxCtx.uuid,
+            block_reason: ctx.blockReason,
+            client_uuid: ctx.uuid,
             block_module: 'px-node-express',
-            block_score: pxCtx.score,
-            module_version: PxConfig.conf.MODULE_VERSION,
-            simulated_block: pxConfig.MODULE_MODE === pxConfig.MONITOR_MODE.MONITOR
+            block_score: ctx.score,
+            module_version: this.pxConfig.conf.MODULE_VERSION,
+            simulated_block: this._config.MODULE_MODE === this._config.MONITOR_MODE.MONITOR
         };
 
-        pxLogger.debug(`Sending block activity`);
-        this.pxClient.sendToPerimeterX('block', details, pxCtx);
+        this.logger.debug(`Sending block activity`);
+        this.pxClient.sendToPerimeterX('block', details, ctx, this._config);
     }
 
     /**
-     * generateResponse - genarating HTML string from pxContext and pxConfig in case
+     * generateResponse - genarating HTML string from ctx in case
      * action was to block the request
      *
-     * @param {Object} pxContext - current request context.
-     * @param {Object} pxConfig - current Px configs
+     * @param {Object} ctx - current request context.
+     * @param {Object} config - current Px configs
      * @param {Function} cb - send the generated html value.
      */
-    generateResponse(pxContext, pxConfig, jsonResponse, cb) {
+    generateResponse(ctx, jsonResponse, cb) {
         let template;
-        switch (pxContext.blockAction) {
+        switch (ctx.blockAction) {
             case 'j':
-                return cb(pxContext.blockActionData);
+                return cb(ctx.blockActionData);
             case 'r':
                 return this.compileMustache('ratelimit', {}, cb);
         }
 
-        const props = this.getProps(pxContext, pxConfig, template);
+        const props = this.getProps(ctx, template);
         if (jsonResponse) {
             return cb(props);
         } else {
@@ -285,30 +294,30 @@ class PxEnforcer {
         }
     }
 
-    getProps(pxContext, pxConfig) {
-        let jsClientSrc = `//${pxConfig.CLIENT_HOST}/${pxConfig.PX_APP_ID}/main.min.js`;
-        let captchaSrc = `//${pxConfig.CAPTCHA_HOST}/${pxConfig.PX_APP_ID}/captcha.js?a=${pxContext.blockAction}&u=${pxContext.uuid}&v=${pxContext.vid || ''}&m=${pxContext.isMobile() ? '1' :'0'}`;
-        let hostUrl = pxContext.collectorUrl;
+    getProps(ctx) {
+        let jsClientSrc = `//${this._config.CLIENT_HOST}/${this._config.PX_APP_ID}/main.min.js`;
+        let captchaSrc = `//${this._config.CAPTCHA_HOST}/${this._config.PX_APP_ID}/captcha.js?a=${ctx.blockAction}&u=${ctx.uuid}&v=${ctx.vid || ''}&m=${ctx.isMobile() ? '1' :'0'}`;
+        let hostUrl = ctx.collectorUrl;
 
-        if (pxConfig.FIRST_PARTY_ENABLED && !pxContext.isMobile()) {
-            const prefix = pxConfig.PX_APP_ID.substring(2);
-            jsClientSrc = `/${prefix}${pxConfig.FIRST_PARTY_VENDOR_PATH}`;
-            captchaSrc = `/${prefix}${pxConfig.FIRST_PARTY_CAPTCHA_PATH}/captcha.js?a=${pxContext.blockAction}&u=${pxContext.uuid}&v=${pxContext.vid || ''}&m=${pxContext.isMobile() ? '1' :'0'}`;
-            hostUrl = `/${prefix}${pxConfig.FIRST_PARTY_XHR_PATH}`;
+        if (this._config.FIRST_PARTY_ENABLED && !ctx.isMobile()) {
+            const prefix = this._config.PX_APP_ID.substring(2);
+            jsClientSrc = `/${prefix}${this._config.FIRST_PARTY_VENDOR_PATH}`;
+            captchaSrc = `/${prefix}${this._config.FIRST_PARTY_CAPTCHA_PATH}/captcha.js?a=${ctx.blockAction}&u=${ctx.uuid}&v=${ctx.vid || ''}&m=${ctx.isMobile() ? '1' :'0'}`;
+            hostUrl = `/${prefix}${this._config.FIRST_PARTY_XHR_PATH}`;
         }
 
         return {
-            refId: pxContext.uuid,
-            appId: pxConfig.PX_APP_ID,
-            vid: pxContext.vid,
-            uuid: pxContext.uuid,
-            customLogo: pxConfig.CUSTOM_LOGO,
-            cssRef: pxConfig.CSS_REF,
-            jsRef: pxConfig.JS_REF,
-            logoVisibility: pxConfig.CUSTOM_LOGO ? 'visible' : 'hidden',
+            refId: ctx.uuid,
+            appId: this._config.PX_APP_ID,
+            vid: ctx.vid,
+            uuid: ctx.uuid,
+            customLogo: this._config.CUSTOM_LOGO,
+            cssRef: this._config.CSS_REF,
+            jsRef: this._config.JS_REF,
+            logoVisibility: this._config.CUSTOM_LOGO ? 'visible' : 'hidden',
             hostUrl: hostUrl,
             jsClientSrc: jsClientSrc,
-            firstPartyEnabled: pxConfig.FIRST_PARTY_ENABLED,
+            firstPartyEnabled: this._config.FIRST_PARTY_ENABLED,
             blockScript: captchaSrc
         };
     }
diff --git a/lib/pxhttpc.js b/lib/pxhttpc.js
index b87426d3..f0d1c4cb 100644
--- a/lib/pxhttpc.js
+++ b/lib/pxhttpc.js
@@ -1,7 +1,6 @@
 'use strict';
+
 const request = require('./request');
-const pxConfig = require('./pxconfig');
-const pxLogger = require('./pxlogger');
 
 module.exports = {
     callServer
@@ -16,9 +15,8 @@ module.exports = {
  * @param {string} callType - indication for a query or activities sending
  * @param {Function} callback - callback function.
  */
-function callServer(data, headers, uri, callType, callback) {
-    callback = callback || ((err) => { err && pxLogger.debug(`callServer default callback. Error: ${err}`); });
-    const config = pxConfig.conf;
+function callServer(data, headers, uri, callType, config, callback) {
+    callback = callback || ((err) => { err && config.logger.debug(`callServer default callback. Error: ${err}`); });
     const callData = {
         'url': `https://${config.SERVER_HOST}${uri}`,
         'data': JSON.stringify(data),
@@ -28,7 +26,7 @@ function callServer(data, headers, uri, callType, callback) {
     callData.timeout = callType === 'query' ? config.API_TIMEOUT_MS : config.ACTIVITIES_TIMEOUT;
 
     try {
-        request.post(callData, function (err, response) {
+        request.post(callData, config, function (err, response) {
             let data;
             if (err) {
                 if (err == 'Error: Timeout has been reached.') {
diff --git a/lib/pxlogger.js b/lib/pxlogger.js
index bf7e6aaf..eff1ddb6 100644
--- a/lib/pxlogger.js
+++ b/lib/pxlogger.js
@@ -3,22 +3,28 @@
  * Version 1.0 Published 12 May 2016
  */
 
-module.exports = {
-    error: error,
-    debug: debug
-};
+class PxLogger {
+    constructor() {
+        this.debugMode = false;
+        this.appId = '';
+    }
 
-function debug(msg) {
-    const pxConfig = require('./pxconfig');
-    if (pxConfig.conf.DEBUG_MODE && msg) {
-        console.info(`[PerimeterX - DEBUG][${pxConfig.conf.PX_APP_ID}] - ${msg}`);
+    init(pxConfig) {
+        this.debugMode = pxConfig.conf.DEBUG_MODE;
+        this.appId = pxConfig.conf.PX_APP_ID;
     }
-}
 
-function error(msg) {
-    const pxConfig = require('./pxconfig');
-    if (typeof msg === 'string') {
-        console.error(new Error(`[PerimeterX - ERROR][${pxConfig.conf.PX_APP_ID}] - ${msg}`).stack);
+    debug(msg) {
+        if (this.debugMode && msg) {
+            console.info(`[PerimeterX - DEBUG][${this.appId}] - ${msg}`);
+        }
+    }
+    
+    error(msg) {
+        if (typeof msg === 'string') {
+            console.error(new Error(`[PerimeterX - ERROR][${this.appId}] - ${msg}`).stack);
+        }
     }
 }
 
+module.exports = PxLogger;
diff --git a/lib/pxoriginaltoken.js b/lib/pxoriginaltoken.js
index b3792491..b38a76f4 100644
--- a/lib/pxoriginaltoken.js
+++ b/lib/pxoriginaltoken.js
@@ -1,33 +1,32 @@
 const TokenV3 = require('./cookie/tokenV3');
 const TokenV1 = require('./cookie/tokenV1');
-const pxLogger = require('./pxlogger');
 
-function evalCookie(pxCtx, pxConfig) {
+function evalCookie(ctx, config) {
     try {
-        const noVersionOriginalToken = pxCtx.originalToken;
-        const cookie = (pxCtx.cookies['_px3'] ? new TokenV3(pxCtx, pxConfig, noVersionOriginalToken) : new TokenV1(pxCtx, pxConfig, noVersionOriginalToken));
-        pxLogger.debug('Original token found, Evaluating');
+        const noVersionOriginalToken = ctx.originalToken;
+        const cookie = (ctx.cookies['_px3'] ? new TokenV3(ctx, config, noVersionOriginalToken) : new TokenV1(ctx, config, noVersionOriginalToken));
+        config.logger.debug('Original token found, Evaluating');
 
         if (!cookie.deserialize()) {
-            pxLogger.debug(`Original token decryption failed, value: ${pxCtx.originalToken}`);
-            pxCtx.originalTokenError = 'decryption_failed';
+            config.logger.debug(`Original token decryption failed, value: ${ctx.originalToken}`);
+            ctx.originalTokenError = 'decryption_failed';
             return;
         }
 
-        pxCtx.decodedOriginalToken = cookie.decodedCookie;
-        pxCtx.vid = cookie.getVid();
-        pxCtx.originalUuid = cookie.getUuid();
+        ctx.decodedOriginalToken = cookie.decodedCookie;
+        ctx.vid = cookie.getVid();
+        ctx.originalUuid = cookie.getUuid();
 
         if (!cookie.isSecure()) {
-            pxLogger.debug(`Original token HMAC validation failed, value: ${JSON.stringify(cookie.decodedCookie)} user-agent: ${pxCtx.userAgent}`);
-            pxCtx.originalTokenError = 'validation_failed';
+            config.logger.debug(`Original token HMAC validation failed, value: ${JSON.stringify(cookie.decodedCookie)} user-agent: ${ctx.userAgent}`);
+            ctx.originalTokenError = 'validation_failed';
             return;
         }
 
         return;
     } catch (e) {
-        pxLogger.error(`Error while evaluating perimeterx original token: ${e.message}`);
-        pxCtx.originalTokenError = 'decryption_failed';
+        config.logger.error(`Error while evaluating perimeterx original token: ${e.message}`);
+        ctx.originalTokenError = 'decryption_failed';
         return;
     }
 }
diff --git a/lib/pxpayload.js b/lib/pxpayload.js
index 96ca6998..2d89e8da 100644
--- a/lib/pxpayload.js
+++ b/lib/pxpayload.js
@@ -1,11 +1,9 @@
 'use strict';
 
-const pxLogger = require('./pxlogger');
 const crypto = require('crypto');
-
 class Payload {
 
-    constructor() {
+    constructor(config) {
         //cookie string
         this.pxCookie = '';
 
@@ -13,10 +11,10 @@ class Payload {
         this.decodedCookie = '';
 
         //PerimeterX configuration object
-        this.pxConfig = {};
+        this.config = config;
 
         //PerimeterX context
-        this.pxContext = {};
+        this.ctx = {};
 
         //cookie secret string
         this.cookieSecret = '';
@@ -27,7 +25,7 @@ class Payload {
      * @returns {boolean}
      */
     isHighScore() {
-        return this.getScore() >= this.pxConfig.BLOCKING_SCORE;
+        return this.getScore() >= this.config.BLOCKING_SCORE;
     }
 
     /**
@@ -69,7 +67,7 @@ class Payload {
             return true;
         }
         let cookie;
-        if (this.pxConfig.COOKIE_ENCRYPTION) {
+        if (this.config.COOKIE_ENCRYPTION) {
             cookie = this.decrypt();
         } else {
             cookie = this.decode();
@@ -90,47 +88,47 @@ class Payload {
         try {
             const data = this.pxCookie.split(':');
             if (data.length !== 3) {
-                pxLogger.debug('invalid cookie format - wrong number of parts');
+                this.config.logger.debug('invalid cookie format - wrong number of parts');
                 return '';
             }
             const iterations = Number(data[1]);
             const encryptedCookie = data[2];
             /* iterations value is not a number */
             if (!iterations) {
-                pxLogger.debug('invalid cookie format - iterations value is not a number');
+                this.config.logger.debug('invalid cookie format - iterations value is not a number');
                 return '';
             }
 
             /* iterations value is not in the legit range */
             if (iterations > 5000 || iterations < 500) {
-                pxLogger.debug('invalid cookie format - iterations out of bounds');
+                this.config.logger.debug('invalid cookie format - iterations out of bounds');
                 return '';
             }
 
             /* salt value is not as expected */
             if (!data[0] || typeof data[0] !== 'string' || data[0].length > 100) {
-                pxLogger.debug('invalid cookie format - invalid salt value');
+                this.config.logger.debug('invalid cookie format - invalid salt value');
                 return '';
             }
 
             /* cookie value is not as expected */
             if (!encryptedCookie || typeof encryptedCookie !== 'string') {
-                pxLogger.debug('invalid cookie format - no cookie value');
+                this.config.logger.debug('invalid cookie format - no cookie value');
                 return '';
             }
 
             const salt = new Buffer(data[0], 'base64');
-            const derivation = crypto.pbkdf2Sync(this.cookieSecret, salt, iterations, this.pxConfig.CE_KEYLEN + this.pxConfig.CE_IVLEN, this.pxConfig.CE_DIGEST);
-            const key = derivation.slice(0, this.pxConfig.CE_KEYLEN);
-            const iv = derivation.slice(this.pxConfig.CE_KEYLEN);
+            const derivation = crypto.pbkdf2Sync(this.cookieSecret, salt, iterations, this.config.CE_KEYLEN + this.config.CE_IVLEN, this.config.CE_DIGEST);
+            const key = derivation.slice(0, this.config.CE_KEYLEN);
+            const iv = derivation.slice(this.config.CE_KEYLEN);
 
-            const cipher = crypto.createDecipheriv(this.pxConfig.CE_ALGO, key, iv);
+            const cipher = crypto.createDecipheriv(this.config.CE_ALGO, key, iv);
             let decrypted = cipher.update(encryptedCookie, 'base64', 'utf8');
             decrypted += cipher.final('utf8');
 
             return JSON.parse(decrypted);
         } catch (err) {
-            pxLogger.error('Error while decrypting Perimeterx cookie: ' + err.message);
+            this.config.logger.error('Error while decrypting Perimeterx cookie: ' + err.message);
             return '';
         }
     }
@@ -145,14 +143,14 @@ class Payload {
 
     isHmacValid(hmacStr, cookieHmac) {
         try {
-            const hmac = crypto.createHmac(this.pxConfig.CE_DIGEST, this.cookieSecret);
+            const hmac = crypto.createHmac(this.config.CE_DIGEST, this.cookieSecret);
             hmac.setEncoding('hex');
             hmac.write(hmacStr);
             hmac.end();
             const h = hmac.read();
             return h === cookieHmac;
         } catch (err) {
-            pxLogger.error('Error while validating Perimeterx cookie: ' + err.stack);
+            this.config.logger.error('Error while validating Perimeterx cookie: ' + err.stack);
         }
     }
 }
diff --git a/lib/pxproxy.js b/lib/pxproxy.js
index 98d3055c..64d25759 100644
--- a/lib/pxproxy.js
+++ b/lib/pxproxy.js
@@ -1,5 +1,4 @@
 const request = require('./request');
-const pxLogger = require('./pxlogger');
 const pxUtil = require('./pxutil');
 const rawBody = require('raw-body');
 const contentType = require('content-type');
@@ -8,7 +7,7 @@ const contentType = require('content-type');
  * getClient - process the proxy request to get the captcha script file from PX servers.
  *
  * @param {object} req - the request object.
- * @param {object} pxConfig - the PerimeterX config object.
+ * @param {object} config - the PerimeterX config object.
  * @param {string} ip - the ip that initiated the call.
  * @param {string} reversePrefix - the prefix of the xhr request.
  * @param {function} cb - the callback function to call at the end of the process.
@@ -16,9 +15,9 @@ const contentType = require('content-type');
  * @return {function} the callback function passed as param with the server response (captcha file) or error.
  *
  */
-function getCaptcha(req, pxConfig, ip, reversePrefix, cb) {
+function getCaptcha(req, config, ip, reversePrefix, cb) {
     let res = {};
-    if (!pxConfig.FIRST_PARTY_ENABLED) {
+    if (!config.FIRST_PARTY_ENABLED) {
         res = {
             status: 200,
             header: {key: 'Content-Type', value:'application/javascript'},
@@ -26,22 +25,23 @@ function getCaptcha(req, pxConfig, ip, reversePrefix, cb) {
         };
         return cb(null, res);
     } else {
-        const searchMask = `/${reversePrefix}${pxConfig.FIRST_PARTY_CAPTCHA_PATH}`;
+        const searchMask = `/${reversePrefix}${config.FIRST_PARTY_CAPTCHA_PATH}`;
         const regEx = new RegExp(searchMask, 'ig');
-        const pxRequestUri =  `/${pxConfig.PX_APP_ID}${req.originalUrl.replace(regEx, '')}`;
-        pxLogger.debug(`Forwarding request from ${req.originalUrl} to xhr at ${pxConfig.CAPTCHA_HOST}${pxRequestUri}`);
+        const pxRequestUri =  `/${config.PX_APP_ID}${req.originalUrl.replace(regEx, '')}`;
+        config.logger.debug(`Forwarding request from ${req.originalUrl} to xhr at ${config.CAPTCHA_HOST}${pxRequestUri}`);
         const callData = {
-            url: `https://${pxConfig.CAPTCHA_HOST}${pxRequestUri}`,
-            headers: pxUtil.filterSensitiveHeaders(req.headers),
-            timeout: pxConfig.API_TIMEOUT_MS
+            url: `https://${config.CAPTCHA_HOST}${pxRequestUri}`,
+            headers: pxUtil.filterSensitiveHeaders(req.headers, config.SENSITIVE_HEADERS),
+            timeout: config.API_TIMEOUT_MS
         };
-        callData.headers['host'] = pxConfig.CAPTCHA_HOST;
-        callData.headers[pxConfig.ENFORCER_TRUE_IP_HEADER] = ip;
-        callData.headers[pxConfig.FIRST_PARTY_HEADER] = 1;
-        request.get(callData, (error, response) => {
+        callData.headers['host'] = config.CAPTCHA_HOST;
+        callData.headers[config.ENFORCER_TRUE_IP_HEADER] = ip;
+        callData.headers[config.FIRST_PARTY_HEADER] = 1;
+        request.get(callData, config, (error, response) => {
             if (error || !response) {
-                pxLogger.error(`Error while fetching first party captcha: ${error}`);
+                config.logger.error(`Error while fetching first party captcha: ${error}`);
             }
+
             response = response || {statusCode: 200, headers: {}};
             res = {
                 status: response.statusCode,
@@ -58,16 +58,16 @@ function getCaptcha(req, pxConfig, ip, reversePrefix, cb) {
  * getClient - process the proxy request to get the client file from PX servers.
  *
  * @param {object} req - the request object.
- * @param {object} pxConfig - the PerimeterX config object.
+ * @param {object} config - the PerimeterX config object.
  * @param {string} ip - the ip that initiated the call.
  * @param {function} cb - the callback function to call at the end of the process.
  *
  * @return {function} the callback function passed as param with the server response (client file) or error.
  *
  */
-function getClient(req, pxConfig, ip, cb) {
+function getClient(req, config, ip, cb) {
     let res = {};
-    if (!pxConfig.FIRST_PARTY_ENABLED) {
+    if (!config.FIRST_PARTY_ENABLED) {
         res = {
             status: 200,
             header: {key: 'Content-Type', value:'application/javascript'},
@@ -75,19 +75,19 @@ function getClient(req, pxConfig, ip, cb) {
         };
         return cb(null, res);
     } else {
-        const clientRequestUri = `/${pxConfig.PX_APP_ID}/main.min.js`;
-        pxLogger.debug(`Forwarding request from ${req.originalUrl.toLowerCase()} to client at ${pxConfig.CLIENT_HOST}${clientRequestUri}`);
+        const clientRequestUri = `/${config.PX_APP_ID}/main.min.js`;
+        config.logger.debug(`Forwarding request from ${req.originalUrl.toLowerCase()} to client at ${config.CLIENT_HOST}${clientRequestUri}`);
         const callData = {
-            url: `https://${pxConfig.CLIENT_HOST}${clientRequestUri}`,
-            headers: pxUtil.filterSensitiveHeaders(req.headers),
-            timeout: pxConfig.API_TIMEOUT_MS
+            url: `https://${config.CLIENT_HOST}${clientRequestUri}`,
+            headers: pxUtil.filterSensitiveHeaders(req.headers, config.SENSITIVE_HEADERS),
+            timeout: config.API_TIMEOUT_MS
         };
-        callData.headers['host'] = pxConfig.CLIENT_HOST;
-        callData.headers[pxConfig.ENFORCER_TRUE_IP_HEADER] = ip;
-        callData.headers[pxConfig.FIRST_PARTY_HEADER] = 1;
-        request.get(callData, (error, response) => {
+        callData.headers['host'] = config.CLIENT_HOST;
+        callData.headers[config.ENFORCER_TRUE_IP_HEADER] = ip;
+        callData.headers[config.FIRST_PARTY_HEADER] = 1;
+        request.get(callData, config, (error, response) => {
             if (error || !response) {
-                pxLogger.error(`Error while fetching first party client: ${error}`);
+                config.logger.error(`Error while fetching first party client: ${error}`);
             }
 
             response = response || {statusCode: 200, headers: {}};
@@ -106,7 +106,7 @@ function getClient(req, pxConfig, ip, cb) {
  * sendXHR - process the proxy request for sending activities to PX servers.
  *
  * @param {object} req - the request object.
- * @param {object} pxConfig - the PerimeterX config object.
+ * @param {object} config - the PerimeterX config object.
  * @param {string} ip - the ip that initiated the call.
  * @param {string} reversePrefix - the prefix of the xhr request.
  * @param {function} cb - the callback function to call at the end of the process.
@@ -114,15 +114,15 @@ function getClient(req, pxConfig, ip, cb) {
  * @return {function} the callback function passed as param with the server response or error.
  *
  */
-function sendXHR(req, pxConfig, ip, reversePrefix, cb) {
+function sendXHR(req, config, ip, reversePrefix, cb) {
     let res = {};
     let vid;
-    if (!pxConfig.FIRST_PARTY_ENABLED || !pxConfig.FIRST_PARTY_XHR_ENABLED) {
+    if (!config.FIRST_PARTY_ENABLED || !config.FIRST_PARTY_XHR_ENABLED) {
         if (req.originalUrl.toLowerCase().includes('gif')) {
             res = {
                 status: 200,
                 header: {key: 'Content-Type', value:'image/gif'},
-                body: Buffer.from(pxConfig.EMPTY_GIF_B64, 'base64')
+                body: Buffer.from(config.EMPTY_GIF_B64, 'base64')
             };
         } else {
             res = {
@@ -135,21 +135,21 @@ function sendXHR(req, pxConfig, ip, reversePrefix, cb) {
     }
 
     // handle proxy
-    parseBody(req).then(() => {
-        const searchMask = `/${reversePrefix}${pxConfig.FIRST_PARTY_XHR_PATH}`;
+    parseBody(req, config.logger).then(() => {
+        const searchMask = `/${reversePrefix}${config.FIRST_PARTY_XHR_PATH}`;
         const regEx = new RegExp(searchMask, 'ig');
         const pxRequestUri = req.originalUrl.replace(regEx, '');
-        pxLogger.debug(`Forwarding request from ${req.originalUrl} to xhr at ${pxConfig.COLLECTOR_HOST}${pxRequestUri}`);
+        config.logger.debug(`Forwarding request from ${req.originalUrl} to xhr at ${config.COLLECTOR_HOST}${pxRequestUri}`);
 
         const callData = {
-            url: `https://${pxConfig.COLLECTOR_HOST}${pxRequestUri}`,
-            headers: pxUtil.generateProxyHeaders(req.headers, req.ip),
-            timeout: pxConfig.API_TIMEOUT_MS
+            url: `https://${config.COLLECTOR_HOST}${pxRequestUri}`,
+            headers: pxUtil.generateProxyHeaders(req.headers, req.ip, config.SENSITIVE_HEADERS, config.FORWARDED_FOR_HEADER),
+            timeout: config.API_TIMEOUT_MS
         };
 
-        callData.headers['host'] = pxConfig.COLLECTOR_HOST;
-        callData.headers[pxConfig.ENFORCER_TRUE_IP_HEADER] = ip;
-        callData.headers[pxConfig.FIRST_PARTY_HEADER] = 1;
+        callData.headers['host'] = config.COLLECTOR_HOST;
+        callData.headers[config.ENFORCER_TRUE_IP_HEADER] = ip;
+        callData.headers[config.FIRST_PARTY_HEADER] = 1;
         if (req.rawBody) {
             callData.headers['content-length'] = Buffer.byteLength(req.rawBody); // makes sure the length of the body is correct, as the body might go trough different body parsers.
             callData.data = req.rawBody;
@@ -173,7 +173,7 @@ function sendXHR(req, pxConfig, ip, reversePrefix, cb) {
         }
 
         if (req.method === 'POST') {
-            request.post(callData, (error, response) => {
+            request.post(callData, config, (error, response) => {
                 if (error || !response || response.statusCode >= 400) {
                     if (req.originalUrl.toLowerCase().includes('/beacon')) {
                         res = {
@@ -197,13 +197,13 @@ function sendXHR(req, pxConfig, ip, reversePrefix, cb) {
                 return cb(null, res);
             });
         } else if (req.method === 'GET') {
-            request.get(callData, (error, response) => {
+            request.get(callData, config, (error, response) => {
                 if (error || !response || response.statusCode >= 400) {
                     if (req.originalUrl.toLowerCase().includes('.gif')) {
                         res = {
                             status: 200,
                             header: {key: 'Content-Type', value:'image/gif'},
-                            body: Buffer.from(pxConfig.EMPTY_GIF_B64, 'base64')
+                            body: Buffer.from(config.EMPTY_GIF_B64, 'base64')
                         };
                     } else {
                         res = {
@@ -225,7 +225,7 @@ function sendXHR(req, pxConfig, ip, reversePrefix, cb) {
     });
 }
 
-function parseBody(req) {
+function parseBody(req, pxLogger) {
     return new Promise((resolve) => {
         try {
             if (req.method == 'GET') {
diff --git a/lib/pxtesthandler.js b/lib/pxtesthandler.js
index fe1065a9..9ff1b41c 100644
--- a/lib/pxtesthandler.js
+++ b/lib/pxtesthandler.js
@@ -2,37 +2,37 @@
 module.exports = {
     testModeRequestHandler: customRequestHandler};
 
-function customRequestHandler(pxCtx, pxconfig, req, res) {
+function customRequestHandler(ctx, config, req, res) {
     const result = {
-        px_cookies : pxCtx.cookies,
-        uuid: pxCtx.uuid,
-        vid: pxCtx.vid,
-        ip: pxCtx.ip,
-        full_url: pxCtx.fullUrl,
-        score: pxCtx.score,
-        px_cookie_hmac : pxCtx.hmac,
-        block_action: pxCtx.blockAction,
-        http_method: pxCtx.httpMethod,
-        hostname: pxCtx.hostname,
-        headers: pxCtx.headers,
-        user_agent: pxCtx.userAgent,
-        uri: pxCtx.uri,
-        is_made_s2s_api_call: pxCtx.hasMadeServerCall || false,
-        sensitive_route: pxCtx.sensitiveRoute,
-        sensitive_routes_list: pxconfig.SENSITIVE_ROUTES,
-        whitelist_routes: pxconfig.WHITELIST_ROUTES,
-        decoded_px_cookie: pxCtx.decodedCookie,
-        cookie_origin: pxCtx.cookieOrigin,
-        http_version: pxCtx.httpVersion,
-        s2s_call_reason: pxCtx.s2sCallReason || 'none',
-        block_reason: pxCtx.blockReason || 'none',
-        module_mode: pxconfig.MODULE_MODE
+        px_cookies : ctx.cookies,
+        uuid: ctx.uuid,
+        vid: ctx.vid,
+        ip: ctx.ip,
+        full_url: ctx.fullUrl,
+        score: ctx.score,
+        px_cookie_hmac : ctx.hmac,
+        block_action: ctx.blockAction,
+        http_method: ctx.httpMethod,
+        hostname: ctx.hostname,
+        headers: ctx.headers,
+        user_agent: ctx.userAgent,
+        uri: ctx.uri,
+        is_made_s2s_api_call: ctx.hasMadeServerCall || false,
+        sensitive_route: ctx.sensitiveRoute,
+        sensitive_routes_list: config.SENSITIVE_ROUTES,
+        whitelist_routes: config.WHITELIST_ROUTES,
+        decoded_px_cookie: ctx.decodedCookie,
+        cookie_origin: ctx.cookieOrigin,
+        http_version: ctx.httpVersion,
+        s2s_call_reason: ctx.s2sCallReason || 'none',
+        block_reason: ctx.blockReason || 'none',
+        module_mode: config.MODULE_MODE
     };
-    if (pxCtx.originalUuid) {
-        result['original_uuid'] = pxCtx.originalUuid;
+    if (ctx.originalUuid) {
+        result['original_uuid'] = ctx.originalUuid;
     }
-    if (pxCtx.originalTokenError) {
-        result['original_token_error'] = pxCtx.originalTokenError;
+    if (ctx.originalTokenError) {
+        result['original_token_error'] = ctx.originalTokenError;
     }
 
     res.json(result);
diff --git a/lib/pxutil.js b/lib/pxutil.js
index 8a68a126..598cf889 100644
--- a/lib/pxutil.js
+++ b/lib/pxutil.js
@@ -1,5 +1,7 @@
 'use strict';
 
+const net = require('net');
+
 /**
  * PerimeterX (http://www.perimeterx.com) NodeJS-Express SDK
  * Version 1.0 Published 12 May 2016
@@ -11,8 +13,7 @@
  * @param {Object} headers - request headers in key value format.
  * @return {Array} request headers an array format.
  */
-function formatHeaders(headers) {
-    const pxConfig = require('./pxconfig').conf;
+function formatHeaders(headers, sensitiveHeaders) {
     const retval = [];
     try {
         if (!headers || typeof headers !== 'object' || Object.keys(headers).length === 0) {
@@ -20,7 +21,7 @@ function formatHeaders(headers) {
         }
 
         for (const header in headers) {
-            if (header && headers[header] && pxConfig.SENSITIVE_HEADERS.indexOf(header) === -1) {
+            if (header && headers[header] && sensitiveHeaders.indexOf(header) === -1) {
                 retval.push({name: header, value: headers[header]});
             }
         }
@@ -55,12 +56,9 @@ function checkForStatic(req, exts) {
  *
  * @return {object} filtered headers.
  */
-function filterSensitiveHeaders(headers) {
+function filterSensitiveHeaders(headers, sensitiveKeys) {
     try {
-        const pxConfig = require('./pxconfig').conf;
         const retval = {};
-
-        const sensitiveKeys = pxConfig.SENSITIVE_HEADERS;
         for (const key in headers) {
             if (sensitiveKeys.findIndex(item => key.toLowerCase() === item.toLowerCase()) === -1) {
                 retval[key] = headers[key];
@@ -72,15 +70,14 @@ function filterSensitiveHeaders(headers) {
     }
 }
 
-function generateProxyHeaders(headers, ip) {
+function generateProxyHeaders(headers, ip, sensitiveHeaders, forwardedForHeader) {
     try {
-        const pxConfig = require('./pxconfig').conf;
-        const filteredHeaders = filterSensitiveHeaders(headers);
-        const xffHeader = Object.keys(filteredHeaders).find(item => item.toLowerCase() === pxConfig.FORWARDED_FOR_HEADER);
+        const filteredHeaders = filterSensitiveHeaders(headers, sensitiveHeaders);
+        const xffHeader = Object.keys(filteredHeaders).find(item => item.toLowerCase() === forwardedForHeader);
         if (xffHeader) {
             filteredHeaders[xffHeader] += `, ${ip}`;
         } else {
-            filteredHeaders[pxConfig.FORWARDED_FOR_HEADER] = ip;
+            filteredHeaders[forwardedForHeader] = ip;
         }
         return filteredHeaders;
     } catch(e) {
@@ -165,6 +162,29 @@ function prepareCustomParams(config, dict) {
     }
 }
 
+function extractIP(config, req) {
+    let ip;
+    if (Array.isArray(config.IP_HEADERS)) {
+        config.IP_HEADERS.some(ipHeader => {
+            try {
+                const headerValue = req.get(ipHeader);
+                if (headerValue) {
+                    ip = headerValue;
+                    return true;
+                }
+            } catch (e) {
+                config.logger.debug('Failed to use IP_HEADERS from config.');
+            }
+        });
+    } else {
+        ip = typeof config.GET_USER_IP === 'function' && config.GET_USER_IP(req);
+    }
+    if (ip && net.isIP(ip) > 0) {
+        return ip;
+    }
+    return req.ip;
+}
+
 module.exports = {
     formatHeaders,
     filterSensitiveHeaders,
@@ -174,4 +194,5 @@ module.exports = {
     parseAction,
     generateProxyHeaders,
     prepareCustomParams,
+    extractIP
 };
diff --git a/lib/request.js b/lib/request.js
index d0a8a5f6..d33d1449 100644
--- a/lib/request.js
+++ b/lib/request.js
@@ -1,35 +1,21 @@
-const pxLogger = require('./pxlogger');
 const p = require('agent-phin').unpromisified;
-const pxConfig = require('./pxconfig');
 const https = require('https');
 const keepAliveAgent = new https.Agent({ keepAlive: true });
 
-const request = {
-    get: (options, cb) => {
-        options.method = 'GET';
-        return makeRequest(options, cb);
-    },
-    post: (options, cb) => {
-        options.method = 'POST';
-        if (!options.headers['content-type'] && !options.headers['Content-Type']) {
-            options.headers['content-type'] = 'application/json';
-        }
-        return makeRequest(options, cb);
-    }
+exports.get = (options, config, cb) => {
+    options.method = 'GET';
+    return makeRequest(options, config, cb);
 };
 
-function makeRequest(options, cb) {
-    if (pxConfig.conf.agent) {
-        options.agent = pxConfig.conf.agent;
-    } else {
-        options.agent = keepAliveAgent;
-    }
-
-    try {
-        p(options, cb);
-    } catch (e) {
-        pxLogger.error(`Error making request: ${e.message}`);
+exports.post = (options, config, cb) => {
+    options.method = 'POST';
+    if (!options.headers['content-type'] && !options.headers['Content-Type']) {
+        options.headers['content-type'] = 'application/json';
     }
-}
+    return makeRequest(options, config, cb);
+};
 
-module.exports = request;
\ No newline at end of file
+function makeRequest(options, config, cb) {
+    options.agent = config.agent || keepAliveAgent;
+    p(options, cb);
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..23c596c1
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,1290 @@
+{
+  "name": "perimeterx-node-core",
+  "version": "1.7.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@babel/code-frame": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz",
+      "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==",
+      "dev": true,
+      "requires": {
+        "@babel/highlight": "^7.0.0"
+      }
+    },
+    "@babel/highlight": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz",
+      "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==",
+      "dev": true,
+      "requires": {
+        "chalk": "^2.0.0",
+        "esutils": "^2.0.2",
+        "js-tokens": "^4.0.0"
+      }
+    },
+    "acorn": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.0.tgz",
+      "integrity": "sha512-MW/FjM+IvU9CgBzjO3UIPCE2pyEwUsoFl+VGdczOPEdxfGFjuKny/gN54mOuX7Qxmb9Rg9MCn2oKiSUeW+pjrw==",
+      "dev": true
+    },
+    "acorn-jsx": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz",
+      "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==",
+      "dev": true
+    },
+    "agent-base": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
+      "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
+      "requires": {
+        "es6-promisify": "^5.0.0"
+      }
+    },
+    "agent-phin": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/agent-phin/-/agent-phin-1.0.3.tgz",
+      "integrity": "sha512-p9aDxReY6SxaVVXYmidCdPFtz+EsRfVodiL/rbIyht9c4kjkUe+RtdMJr5eEuJKCfh3iKKwB6Ntf2UP2E0zL6w==",
+      "requires": {
+        "centra": "^2.2.1"
+      }
+    },
+    "ajv": {
+      "version": "6.9.1",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz",
+      "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==",
+      "dev": true,
+      "requires": {
+        "fast-deep-equal": "^2.0.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      }
+    },
+    "ansi-escapes": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
+      "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
+      "dev": true
+    },
+    "ansi-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+      "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+      "dev": true
+    },
+    "ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "dev": true,
+      "requires": {
+        "color-convert": "^1.9.0"
+      }
+    },
+    "argparse": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+      "dev": true,
+      "requires": {
+        "sprintf-js": "~1.0.2"
+      }
+    },
+    "astral-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
+      "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+      "dev": true
+    },
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+      "dev": true
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "requires": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "browser-stdout": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+      "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+      "dev": true
+    },
+    "bytes": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+      "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
+    },
+    "callsites": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz",
+      "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==",
+      "dev": true
+    },
+    "centra": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/centra/-/centra-2.2.2.tgz",
+      "integrity": "sha512-7M9pOa0afioJy+h3+RvaK9BuxtJdo4/cRXoBPoClUcX1YLlaFMIxQlN/Sp4GoDEp2w17qeQ74NDuMdXPaNLToA=="
+    },
+    "chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      }
+    },
+    "chardet": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+      "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+      "dev": true
+    },
+    "circular-json": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
+      "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
+      "dev": true
+    },
+    "cli-cursor": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+      "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+      "dev": true,
+      "requires": {
+        "restore-cursor": "^2.0.0"
+      }
+    },
+    "cli-width": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
+      "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
+      "dev": true
+    },
+    "color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dev": true,
+      "requires": {
+        "color-name": "1.1.3"
+      }
+    },
+    "color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+      "dev": true
+    },
+    "commander": {
+      "version": "2.15.1",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
+      "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
+      "dev": true
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+      "dev": true
+    },
+    "content-type": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
+    },
+    "cross-spawn": {
+      "version": "6.0.5",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+      "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+      "dev": true,
+      "requires": {
+        "nice-try": "^1.0.4",
+        "path-key": "^2.0.1",
+        "semver": "^5.5.0",
+        "shebang-command": "^1.2.0",
+        "which": "^1.2.9"
+      }
+    },
+    "debug": {
+      "version": "3.2.6",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+      "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+      "requires": {
+        "ms": "^2.1.1"
+      }
+    },
+    "deep-is": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+      "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+      "dev": true
+    },
+    "depd": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+      "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+    },
+    "diff": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
+      "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
+      "dev": true
+    },
+    "doctrine": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+      "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+      "dev": true,
+      "requires": {
+        "esutils": "^2.0.2"
+      }
+    },
+    "emoji-regex": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+      "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+      "dev": true
+    },
+    "es6-promise": {
+      "version": "4.2.5",
+      "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz",
+      "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg=="
+    },
+    "es6-promisify": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
+      "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
+      "requires": {
+        "es6-promise": "^4.0.3"
+      }
+    },
+    "escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+      "dev": true
+    },
+    "eslint": {
+      "version": "5.13.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.13.0.tgz",
+      "integrity": "sha512-nqD5WQMisciZC5EHZowejLKQjWGuFS5c70fxqSKlnDME+oz9zmE8KTlX+lHSg+/5wsC/kf9Q9eMkC8qS3oM2fg==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.0.0",
+        "ajv": "^6.5.3",
+        "chalk": "^2.1.0",
+        "cross-spawn": "^6.0.5",
+        "debug": "^4.0.1",
+        "doctrine": "^2.1.0",
+        "eslint-scope": "^4.0.0",
+        "eslint-utils": "^1.3.1",
+        "eslint-visitor-keys": "^1.0.0",
+        "espree": "^5.0.0",
+        "esquery": "^1.0.1",
+        "esutils": "^2.0.2",
+        "file-entry-cache": "^2.0.0",
+        "functional-red-black-tree": "^1.0.1",
+        "glob": "^7.1.2",
+        "globals": "^11.7.0",
+        "ignore": "^4.0.6",
+        "import-fresh": "^3.0.0",
+        "imurmurhash": "^0.1.4",
+        "inquirer": "^6.1.0",
+        "js-yaml": "^3.12.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "levn": "^0.3.0",
+        "lodash": "^4.17.5",
+        "minimatch": "^3.0.4",
+        "mkdirp": "^0.5.1",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.8.2",
+        "path-is-inside": "^1.0.2",
+        "progress": "^2.0.0",
+        "regexpp": "^2.0.1",
+        "semver": "^5.5.1",
+        "strip-ansi": "^4.0.0",
+        "strip-json-comments": "^2.0.1",
+        "table": "^5.0.2",
+        "text-table": "^0.2.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        }
+      }
+    },
+    "eslint-scope": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz",
+      "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==",
+      "dev": true,
+      "requires": {
+        "esrecurse": "^4.1.0",
+        "estraverse": "^4.1.1"
+      }
+    },
+    "eslint-utils": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz",
+      "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==",
+      "dev": true
+    },
+    "eslint-visitor-keys": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
+      "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==",
+      "dev": true
+    },
+    "espree": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.0.tgz",
+      "integrity": "sha512-1MpUfwsdS9MMoN7ZXqAr9e9UKdVHDcvrJpyx7mm1WuQlx/ygErEQBzgi5Nh5qBHIoYweprhtMkTCb9GhcAIcsA==",
+      "dev": true,
+      "requires": {
+        "acorn": "^6.0.2",
+        "acorn-jsx": "^5.0.0",
+        "eslint-visitor-keys": "^1.0.0"
+      }
+    },
+    "esprima": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+      "dev": true
+    },
+    "esquery": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
+      "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
+      "dev": true,
+      "requires": {
+        "estraverse": "^4.0.0"
+      }
+    },
+    "esrecurse": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
+      "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
+      "dev": true,
+      "requires": {
+        "estraverse": "^4.1.0"
+      }
+    },
+    "estraverse": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
+      "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
+      "dev": true
+    },
+    "esutils": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
+      "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
+      "dev": true
+    },
+    "external-editor": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz",
+      "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==",
+      "dev": true,
+      "requires": {
+        "chardet": "^0.7.0",
+        "iconv-lite": "^0.4.24",
+        "tmp": "^0.0.33"
+      },
+      "dependencies": {
+        "iconv-lite": {
+          "version": "0.4.24",
+          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+          "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+          "dev": true,
+          "requires": {
+            "safer-buffer": ">= 2.1.2 < 3"
+          }
+        }
+      }
+    },
+    "fast-deep-equal": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+      "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+      "dev": true
+    },
+    "fast-json-stable-stringify": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
+      "dev": true
+    },
+    "fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+      "dev": true
+    },
+    "figures": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+      "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+      "dev": true,
+      "requires": {
+        "escape-string-regexp": "^1.0.5"
+      }
+    },
+    "file-entry-cache": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
+      "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=",
+      "dev": true,
+      "requires": {
+        "flat-cache": "^1.2.1",
+        "object-assign": "^4.0.1"
+      }
+    },
+    "flat-cache": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz",
+      "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==",
+      "dev": true,
+      "requires": {
+        "circular-json": "^0.3.1",
+        "graceful-fs": "^4.1.2",
+        "rimraf": "~2.6.2",
+        "write": "^0.2.1"
+      }
+    },
+    "formatio": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz",
+      "integrity": "sha1-87IWfZBoxGmKjVH092CjmlTYGOs=",
+      "dev": true,
+      "requires": {
+        "samsam": "1.x"
+      }
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+      "dev": true
+    },
+    "functional-red-black-tree": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+      "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+      "dev": true
+    },
+    "glob": {
+      "version": "7.1.3",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+      "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+      "dev": true,
+      "requires": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      }
+    },
+    "globals": {
+      "version": "11.10.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-11.10.0.tgz",
+      "integrity": "sha512-0GZF1RiPKU97IHUO5TORo9w1PwrH/NBPl+fS7oMLdaTRiYmYbwK4NWoZWrAdd0/abG9R2BU+OiwyQpTpE6pdfQ==",
+      "dev": true
+    },
+    "graceful-fs": {
+      "version": "4.1.15",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
+      "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
+      "dev": true
+    },
+    "growl": {
+      "version": "1.10.5",
+      "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+      "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+      "dev": true
+    },
+    "has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+      "dev": true
+    },
+    "he": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
+      "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
+      "dev": true
+    },
+    "http-errors": {
+      "version": "1.6.3",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+      "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
+      "requires": {
+        "depd": "~1.1.2",
+        "inherits": "2.0.3",
+        "setprototypeof": "1.1.0",
+        "statuses": ">= 1.4.0 < 2"
+      }
+    },
+    "https-proxy-agent": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz",
+      "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==",
+      "requires": {
+        "agent-base": "^4.1.0",
+        "debug": "^3.1.0"
+      }
+    },
+    "iconv-lite": {
+      "version": "0.4.23",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
+      "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
+      "requires": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      }
+    },
+    "ignore": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+      "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+      "dev": true
+    },
+    "import-fresh": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz",
+      "integrity": "sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ==",
+      "dev": true,
+      "requires": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      }
+    },
+    "imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+      "dev": true
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "dev": true,
+      "requires": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+    },
+    "inquirer": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.2.tgz",
+      "integrity": "sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA==",
+      "dev": true,
+      "requires": {
+        "ansi-escapes": "^3.2.0",
+        "chalk": "^2.4.2",
+        "cli-cursor": "^2.1.0",
+        "cli-width": "^2.0.0",
+        "external-editor": "^3.0.3",
+        "figures": "^2.0.0",
+        "lodash": "^4.17.11",
+        "mute-stream": "0.0.7",
+        "run-async": "^2.2.0",
+        "rxjs": "^6.4.0",
+        "string-width": "^2.1.0",
+        "strip-ansi": "^5.0.0",
+        "through": "^2.3.6"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
+          "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
+          "dev": true
+        },
+        "strip-ansi": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz",
+          "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^4.0.0"
+          }
+        }
+      }
+    },
+    "is-fullwidth-code-point": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+      "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+      "dev": true
+    },
+    "is-promise": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
+      "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
+      "dev": true
+    },
+    "isarray": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+      "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+      "dev": true
+    },
+    "isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+      "dev": true
+    },
+    "js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "dev": true
+    },
+    "js-yaml": {
+      "version": "3.12.1",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz",
+      "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==",
+      "dev": true,
+      "requires": {
+        "argparse": "^1.0.7",
+        "esprima": "^4.0.0"
+      }
+    },
+    "json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true
+    },
+    "json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+      "dev": true
+    },
+    "levn": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+      "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "~1.1.2",
+        "type-check": "~0.3.2"
+      }
+    },
+    "lodash": {
+      "version": "4.17.11",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+      "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
+      "dev": true
+    },
+    "lolex": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.6.0.tgz",
+      "integrity": "sha1-OpoCg0UqR9dDnnJzG54H1zhuSfY=",
+      "dev": true
+    },
+    "mimic-fn": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
+      "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
+      "dev": true
+    },
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "dev": true,
+      "requires": {
+        "brace-expansion": "^1.1.7"
+      }
+    },
+    "minimist": {
+      "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+      "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+      "dev": true
+    },
+    "mkdirp": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+      "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+      "dev": true,
+      "requires": {
+        "minimist": "0.0.8"
+      }
+    },
+    "mocha": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz",
+      "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==",
+      "dev": true,
+      "requires": {
+        "browser-stdout": "1.3.1",
+        "commander": "2.15.1",
+        "debug": "3.1.0",
+        "diff": "3.5.0",
+        "escape-string-regexp": "1.0.5",
+        "glob": "7.1.2",
+        "growl": "1.10.5",
+        "he": "1.1.1",
+        "minimatch": "3.0.4",
+        "mkdirp": "0.5.1",
+        "supports-color": "5.4.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "glob": {
+          "version": "7.1.2",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
+          "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+          "dev": true,
+          "requires": {
+            "fs.realpath": "^1.0.0",
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "^3.0.4",
+            "once": "^1.3.0",
+            "path-is-absolute": "^1.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "5.4.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
+          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
+      }
+    },
+    "ms": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+      "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+    },
+    "mu2": {
+      "version": "0.5.21",
+      "resolved": "https://registry.npmjs.org/mu2/-/mu2-0.5.21.tgz",
+      "integrity": "sha1-iIqPD9kOsc/anbgUdvbhmcyeWNM="
+    },
+    "mute-stream": {
+      "version": "0.0.7",
+      "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
+      "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
+      "dev": true
+    },
+    "native-promise-only": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz",
+      "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE=",
+      "dev": true
+    },
+    "natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+      "dev": true
+    },
+    "nice-try": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+      "dev": true
+    },
+    "object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+      "dev": true
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "dev": true,
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "onetime": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+      "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+      "dev": true,
+      "requires": {
+        "mimic-fn": "^1.0.0"
+      }
+    },
+    "optionator": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
+      "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
+      "dev": true,
+      "requires": {
+        "deep-is": "~0.1.3",
+        "fast-levenshtein": "~2.0.4",
+        "levn": "~0.3.0",
+        "prelude-ls": "~1.1.2",
+        "type-check": "~0.3.2",
+        "wordwrap": "~1.0.0"
+      }
+    },
+    "os-tmpdir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+      "dev": true
+    },
+    "parent-module": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.0.tgz",
+      "integrity": "sha512-8Mf5juOMmiE4FcmzYc4IaiS9L3+9paz2KOiXzkRviCP6aDmN49Hz6EMWz0lGNp9pX80GvvAuLADtyGfW/Em3TA==",
+      "dev": true,
+      "requires": {
+        "callsites": "^3.0.0"
+      }
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+      "dev": true
+    },
+    "path-is-inside": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+      "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
+      "dev": true
+    },
+    "path-key": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+      "dev": true
+    },
+    "path-to-regexp": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
+      "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
+      "dev": true,
+      "requires": {
+        "isarray": "0.0.1"
+      }
+    },
+    "prelude-ls": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+      "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+      "dev": true
+    },
+    "progress": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+      "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+      "dev": true
+    },
+    "punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+      "dev": true
+    },
+    "raw-body": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
+      "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
+      "requires": {
+        "bytes": "3.0.0",
+        "http-errors": "1.6.3",
+        "iconv-lite": "0.4.23",
+        "unpipe": "1.0.0"
+      }
+    },
+    "regexpp": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
+      "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
+      "dev": true
+    },
+    "resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true
+    },
+    "restore-cursor": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+      "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+      "dev": true,
+      "requires": {
+        "onetime": "^2.0.0",
+        "signal-exit": "^3.0.2"
+      }
+    },
+    "rewire": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/rewire/-/rewire-2.5.2.tgz",
+      "integrity": "sha1-ZCfee3/u+n02QBUH62SlOFvFjcc=",
+      "dev": true
+    },
+    "rimraf": {
+      "version": "2.6.3",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+      "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+      "dev": true,
+      "requires": {
+        "glob": "^7.1.3"
+      }
+    },
+    "run-async": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
+      "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
+      "dev": true,
+      "requires": {
+        "is-promise": "^2.1.0"
+      }
+    },
+    "rxjs": {
+      "version": "6.4.0",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz",
+      "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==",
+      "dev": true,
+      "requires": {
+        "tslib": "^1.9.0"
+      }
+    },
+    "safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+    },
+    "samsam": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz",
+      "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==",
+      "dev": true
+    },
+    "semver": {
+      "version": "5.6.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
+      "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
+      "dev": true
+    },
+    "setprototypeof": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+      "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
+    },
+    "shebang-command": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+      "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+      "dev": true,
+      "requires": {
+        "shebang-regex": "^1.0.0"
+      }
+    },
+    "shebang-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+      "dev": true
+    },
+    "should": {
+      "version": "8.4.0",
+      "resolved": "https://registry.npmjs.org/should/-/should-8.4.0.tgz",
+      "integrity": "sha1-XmCInT5kS73Tl6MM00+tKPz5C8A=",
+      "dev": true,
+      "requires": {
+        "should-equal": "0.8.0",
+        "should-format": "0.3.2",
+        "should-type": "0.2.0"
+      }
+    },
+    "should-equal": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-0.8.0.tgz",
+      "integrity": "sha1-o/BXMv9FusG3ukEvhAiFaBlkEpk=",
+      "dev": true,
+      "requires": {
+        "should-type": "0.2.0"
+      }
+    },
+    "should-format": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/should-format/-/should-format-0.3.2.tgz",
+      "integrity": "sha1-pZgx4Bot3uFJkRvHFIvlyAMZ4f8=",
+      "dev": true,
+      "requires": {
+        "should-type": "0.2.0"
+      }
+    },
+    "should-type": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/should-type/-/should-type-0.2.0.tgz",
+      "integrity": "sha1-ZwfvlVKdmJ3MCY/gdTqx+RNrt/Y=",
+      "dev": true
+    },
+    "signal-exit": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+      "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+      "dev": true
+    },
+    "sinon": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/sinon/-/sinon-2.4.1.tgz",
+      "integrity": "sha512-vFTrO9Wt0ECffDYIPSP/E5bBugt0UjcBQOfQUMh66xzkyPEnhl/vM2LRZi2ajuTdkH07sA6DzrM6KvdvGIH8xw==",
+      "dev": true,
+      "requires": {
+        "diff": "^3.1.0",
+        "formatio": "1.2.0",
+        "lolex": "^1.6.0",
+        "native-promise-only": "^0.8.1",
+        "path-to-regexp": "^1.7.0",
+        "samsam": "^1.1.3",
+        "text-encoding": "0.6.4",
+        "type-detect": "^4.0.0"
+      }
+    },
+    "slice-ansi": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
+      "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^3.2.0",
+        "astral-regex": "^1.0.0",
+        "is-fullwidth-code-point": "^2.0.0"
+      }
+    },
+    "sprintf-js": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+      "dev": true
+    },
+    "statuses": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+      "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
+    },
+    "string-width": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+      "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+      "dev": true,
+      "requires": {
+        "is-fullwidth-code-point": "^2.0.0",
+        "strip-ansi": "^4.0.0"
+      }
+    },
+    "strip-ansi": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+      "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+      "dev": true,
+      "requires": {
+        "ansi-regex": "^3.0.0"
+      }
+    },
+    "strip-json-comments": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+      "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+      "dev": true
+    },
+    "supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "dev": true,
+      "requires": {
+        "has-flag": "^3.0.0"
+      }
+    },
+    "table": {
+      "version": "5.2.3",
+      "resolved": "https://registry.npmjs.org/table/-/table-5.2.3.tgz",
+      "integrity": "sha512-N2RsDAMvDLvYwFcwbPyF3VmVSSkuF+G1e+8inhBLtHpvwXGw4QRPEZhihQNeEN0i1up6/f6ObCJXNdlRG3YVyQ==",
+      "dev": true,
+      "requires": {
+        "ajv": "^6.9.1",
+        "lodash": "^4.17.11",
+        "slice-ansi": "^2.1.0",
+        "string-width": "^3.0.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
+          "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
+          "dev": true
+        },
+        "string-width": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.0.0.tgz",
+          "integrity": "sha512-rr8CUxBbvOZDUvc5lNIJ+OC1nPVpz+Siw9VBtUjB9b6jZehZLFt0JMCZzShFHIsI8cbhm0EsNIfWJMFV3cu3Ew==",
+          "dev": true,
+          "requires": {
+            "emoji-regex": "^7.0.1",
+            "is-fullwidth-code-point": "^2.0.0",
+            "strip-ansi": "^5.0.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz",
+          "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^4.0.0"
+          }
+        }
+      }
+    },
+    "text-encoding": {
+      "version": "0.6.4",
+      "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz",
+      "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=",
+      "dev": true
+    },
+    "text-table": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+      "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+      "dev": true
+    },
+    "through": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+      "dev": true
+    },
+    "tmp": {
+      "version": "0.0.33",
+      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+      "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+      "dev": true,
+      "requires": {
+        "os-tmpdir": "~1.0.2"
+      }
+    },
+    "tslib": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
+      "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
+      "dev": true
+    },
+    "type-check": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+      "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "~1.1.2"
+      }
+    },
+    "type-detect": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+      "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+      "dev": true
+    },
+    "unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+    },
+    "uri-js": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
+      "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+      "dev": true,
+      "requires": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "which": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+      "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+      "dev": true,
+      "requires": {
+        "isexe": "^2.0.0"
+      }
+    },
+    "wordwrap": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+      "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+      "dev": true
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+      "dev": true
+    },
+    "write": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
+      "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=",
+      "dev": true,
+      "requires": {
+        "mkdirp": "^0.5.1"
+      }
+    }
+  }
+}
diff --git a/package.json b/package.json
index c21260f5..6c10448f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "perimeterx-node-core",
-  "version": "1.8.0",
+  "version": "2.0.0",
   "description": "PerimeterX NodeJS shared core for various applications to monitor and block traffic according to PerimeterX risk score",
   "main": "index.js",
   "scripts": {
diff --git a/test/pxapi.test.js b/test/pxapi.test.js
index 896482bd..695d8a6e 100644
--- a/test/pxapi.test.js
+++ b/test/pxapi.test.js
@@ -6,11 +6,14 @@ const rewire = require('rewire');
 const pxhttpc = require('../lib/pxhttpc');
 const pxapi = rewire('../lib/pxapi');
 const originalTokenValidator = require('../lib/pxoriginaltoken');
-const PxClient = rewire('../lib/pxclient');
+const PxConfig = require('../lib/pxconfig');
+const PxLogger = require('../lib/pxlogger');
 
 describe('PX API - pxapi.js', () => {
     let params;
     let config;
+    let pxConfig;
+    let logger;
     let stub;
 
     beforeEach(() => {
@@ -27,10 +30,11 @@ describe('PX API - pxapi.js', () => {
             moduleMode: 1,
         };
 
-        const pxconfig = require('../lib/pxconfig');
-        pxconfig.init(params, new PxClient());
-        config = pxconfig.mergeDefaults(params);
-        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+        logger = new PxLogger();
+        pxConfig = new PxConfig(params, logger);
+        logger.init(pxConfig);
+        config = pxConfig.conf;
+        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
             return callback(data);
         });
     });
@@ -45,8 +49,8 @@ describe('PX API - pxapi.js', () => {
         //Using rewire to get callServer function
         const pxApiCallServerFunc = pxapi.__get__('callServer');
 
-        // Prepare pxCtx
-        const pxCtx = {
+        // Prepare ctx
+        const ctx = {
             ip: '1.2.3.4',
             fullUrl: 'stub',
             vid: 'stub',
@@ -61,88 +65,88 @@ describe('PX API - pxapi.js', () => {
             }
         };
 
-        pxApiCallServerFunc(pxCtx, data => {
+        pxApiCallServerFunc(ctx, config, data => {
             data.additional.px_orig_cookie.should.equal('abc');
             done();
         });
     });
 
-    it('token v3 - should add originalUuid, vid and decodedOriginalToken to pxCtx when original token decryption succeeds', (done) => {
-        const pxCtx = {
+    it('token v3 - should add originalUuid, vid and decodedOriginalToken to ctx when original token decryption succeeds', (done) => {
+        const ctx = {
             cookies: {
                 _px3: 'aaaa'
             },
             originalToken: '68a1bf96ab3af2e0683a377d332b125dda3e195ee56cf3ce4d61b99cd0860dc6:xTMRZvJnzxM=:1000:0pjajaPCjssb2HjG2436zyFXIvIEbE87nFBrHEQPDRT7fqiQ5RA05+njsLUVpOtdJjLvWNNAlSG70DW2wqWM5VmF9UR420/wxPkx6Ebyz/L9q7Mxk5fcdF8p+dGcMc3uD7Qh8y3WiPSN389cXhfKfMttUABQYvRpOxo7rMC+ngpHEVYg+lfBZCliHB1PZKLy'
         };
 
-        originalTokenValidator.evalCookie(pxCtx, config);
-        pxCtx.originalUuid.should.equal('09ade30a-f08b-11e7-8c3f-9a214cf093ae');
-        pxCtx.vid.should.equal('0290edec-f08b-11e7-8c3f-9a214cf093ae');
-        JSON.stringify(pxCtx.decodedOriginalToken).should.equal('{"a":"c","s":0,"u":"09ade30a-f08b-11e7-8c3f-9a214cf093ae","t":1830515445000,"v":"0290edec-f08b-11e7-8c3f-9a214cf093ae"}');
+        originalTokenValidator.evalCookie(ctx, config);
+        ctx.originalUuid.should.equal('09ade30a-f08b-11e7-8c3f-9a214cf093ae');
+        ctx.vid.should.equal('0290edec-f08b-11e7-8c3f-9a214cf093ae');
+        JSON.stringify(ctx.decodedOriginalToken).should.equal('{"a":"c","s":0,"u":"09ade30a-f08b-11e7-8c3f-9a214cf093ae","t":1830515445000,"v":"0290edec-f08b-11e7-8c3f-9a214cf093ae"}');
         done();
     });
     it('token v3 - should set originalTokenError to decryption_failed on original token decryption fail', (done) => {
-        const pxCtx = {
+        const ctx = {
             cookies: {
                 _px3: 'aaaa'
             },
             originalToken: 'aaaaa:bbbbb:cccc:ddddd'
         };
 
-        originalTokenValidator.evalCookie(pxCtx, config);
-        pxCtx.originalTokenError.should.equal('decryption_failed');
+        originalTokenValidator.evalCookie(ctx, config);
+        ctx.originalTokenError.should.equal('decryption_failed');
         done();
     });
     it('token v3 - should set originalTokenError to validation_failed on original token validation fail', (done) => {
-        const pxCtx = {
+        const ctx = {
             cookies: {
                 _px3: 'aaaa'
             },
             originalToken: '68a1bf96ab3af2e0683a377d332b125dda3e195ee56cf3ce4d61b99cd0860dc:xTMRZvJnzxM=:1000:0pjajaPCjssb2HjG2436zyFXIvIEbE87nFBrHEQPDRT7fqiQ5RA05+njsLUVpOtdJjLvWNNAlSG70DW2wqWM5VmF9UR420/wxPkx6Ebyz/L9q7Mxk5fcdF8p+dGcMc3uD7Qh8y3WiPSN389cXhfKfMttUABQYvRpOxo7rMC+ngpHEVYg+lfBZCliHB1PZKLy'
         };
 
-        originalTokenValidator.evalCookie(pxCtx, config);
-        pxCtx.originalUuid.should.equal('09ade30a-f08b-11e7-8c3f-9a214cf093ae');
-        pxCtx.vid.should.equal('0290edec-f08b-11e7-8c3f-9a214cf093ae');
-        pxCtx.originalTokenError.should.equal('validation_failed');
+        originalTokenValidator.evalCookie(ctx, config);
+        ctx.originalUuid.should.equal('09ade30a-f08b-11e7-8c3f-9a214cf093ae');
+        ctx.vid.should.equal('0290edec-f08b-11e7-8c3f-9a214cf093ae');
+        ctx.originalTokenError.should.equal('validation_failed');
         done();
     });
-    it('token v1 - should add originalUuid, vid and decodedOriginalToken to pxCtx when original token decryption succeeds', (done) => {
-        const pxCtx = {
+    it('token v1 - should add originalUuid, vid and decodedOriginalToken to ctx when original token decryption succeeds', (done) => {
+        const ctx = {
             cookies: {
                 _px: 'aaaa'
             },
             originalToken: 'Gy9z3mQPYNE=:1000:I7A44BXmO5IlgqhXLM5Mmuq4/jESNgse51Zj/l4bpkAaymDQzcrUMHBofVQ8Q9IYfon3bVQn7gHA124xunjlSlPMlj133wuFBzt7r/yJKpcTEex5WBxynCQAXXx8tymeO1gWXLmPchrV93ysxPl/AeV2/ofVN3YzUR/0PQbXB2fzxkPc5bMPdxLMJCrgLtR4msoMGvg9qaiufMFDWWzah1kvUq1Kvrlk3UQm0y6UU1j6GoLHkTSnDBTg3GexETotOoUkM5FYMPZm8TxK0as+mg=='
         };
 
-        originalTokenValidator.evalCookie(pxCtx, config);
-        pxCtx.originalUuid.should.equal('09ade30a-f08b-11e7-8c3f-9a214cf093ae');
-        pxCtx.vid.should.equal('0290edec-f08b-11e7-8c3f-9a214cf093ae');
-        JSON.stringify(pxCtx.decodedOriginalToken).should.equal('{"h":"aa2341380b7c67ee0ed5c2f7d4facf03847d7dcb4540aab021654361d3dcade4","s":{"a":0,"b":0},"u":"09ade30a-f08b-11e7-8c3f-9a214cf093ae","t":1830515445000,"v":"0290edec-f08b-11e7-8c3f-9a214cf093ae"}');
+        originalTokenValidator.evalCookie(ctx, config);
+        ctx.originalUuid.should.equal('09ade30a-f08b-11e7-8c3f-9a214cf093ae');
+        ctx.vid.should.equal('0290edec-f08b-11e7-8c3f-9a214cf093ae');
+        JSON.stringify(ctx.decodedOriginalToken).should.equal('{"h":"aa2341380b7c67ee0ed5c2f7d4facf03847d7dcb4540aab021654361d3dcade4","s":{"a":0,"b":0},"u":"09ade30a-f08b-11e7-8c3f-9a214cf093ae","t":1830515445000,"v":"0290edec-f08b-11e7-8c3f-9a214cf093ae"}');
         done();
     });
     it('token v1 - should set originalTokenError to decryption_failed on original token decryption fail', (done) => {
-        const pxCtx = {
+        const ctx = {
             cookies: {
                 _px: 'aaaa'
             },
             originalToken: 'aaaaa:bbbbb:cccc:ddddd'
         };
 
-        originalTokenValidator.evalCookie(pxCtx, config);
-        pxCtx.originalTokenError.should.equal('decryption_failed');
+        originalTokenValidator.evalCookie(ctx, config);
+        ctx.originalTokenError.should.equal('decryption_failed');
         done();
     });
     it('should fail with exception and set originalTokenError to decryption_failed', (done) => {
-        const pxCtx = {
+        const ctx = {
             cookies: {
                 _px: 'aaaaa'
             },
             originalToken: ''
         };
 
-        originalTokenValidator.evalCookie(pxCtx, config);
-        pxCtx.originalTokenError.should.equal('decryption_failed');
+        originalTokenValidator.evalCookie(ctx, config);
+        ctx.originalTokenError.should.equal('decryption_failed');
         done();
     });
 });
\ No newline at end of file
diff --git a/test/pxconfig.test.js b/test/pxconfig.test.js
index 9f46d538..361964e5 100644
--- a/test/pxconfig.test.js
+++ b/test/pxconfig.test.js
@@ -2,11 +2,12 @@
 
 const should = require('should');
 const rewire = require('rewire');
-const PxClient = rewire('../lib/pxclient');
+const PxConfig = require('../lib/pxconfig');
+const PxLogger = require('../lib/pxlogger');
 
 describe('PX Configurations - pxconfig.js', () => {
-    let pxconfig;
     let params;
+    const logger = new PxLogger();
 
     beforeEach(() => {
         params = {
@@ -21,21 +22,20 @@ describe('PX Configurations - pxconfig.js', () => {
             customRequestHandler: null
         };
 
-        pxconfig = require('../lib/pxconfig');
     });
 
     it('should set baseUrl to sapi-<appid>.perimeterx.net', (done) => {
         params.pxAppId = 'PXJWbMQarF';
-        pxconfig.init(params, new PxClient());
-        const conf = pxconfig.conf;
+        const pxConfig = new PxConfig(params, logger);
+        const conf = pxConfig.conf;
         conf.SERVER_HOST.should.be.exactly(`sapi-${params.pxAppId.toLowerCase()}.perimeterx.net`);
         done();
     });
 
     it('blocking score should be 80', (done) => {
         params.blockingScore = 80;
-        pxconfig.init(params, new PxClient());
-        const conf = pxconfig.conf;
+        const pxConfig = new PxConfig(params, logger);
+        const conf = pxConfig.conf;
         conf.BLOCKING_SCORE.should.be.exactly(80);
         done();
     });
@@ -45,8 +45,8 @@ describe('PX Configurations - pxconfig.js', () => {
             return '1.2.3.4';
         };
 
-        pxconfig.init(params, new PxClient());
-        const conf = pxconfig.conf;
+        const pxConfig = new PxConfig(params, logger);
+        const conf = pxConfig.conf;
         conf.GET_USER_IP().should.be.exactly('1.2.3.4');
         done();
     });
@@ -56,51 +56,51 @@ describe('PX Configurations - pxconfig.js', () => {
             return 'Blocked';
         };
 
-        pxconfig.init(params, new PxClient());
-        const conf = pxconfig.conf;
+        const pxConfig = new PxConfig(params, logger);
+        const conf = pxConfig.conf;
         conf.CUSTOM_REQUEST_HANDLER().should.be.exactly('Blocked');
         done();
     });
 
     it('should set enableModule to false', () => {
         params.enableModule = false;
-        pxconfig.init(params, new PxClient());
-        const conf = pxconfig.conf;
+        const pxConfig = new PxConfig(params, logger);
+        const conf = pxConfig.conf;
         conf.ENABLE_MODULE.should.be.exactly(false);
     });
 
     it('should set sendPageActivities to false', () => {
         params.sendPageActivities = false;
-        pxconfig.init(params, new PxClient());
-        const conf = pxconfig.conf;
+        const pxConfig = new PxConfig(params, logger);
+        const conf = pxConfig.conf;
         conf.SEND_PAGE_ACTIVITIES.should.be.exactly(false);
     });
 
     it('should set debugMode to true', () => {
         params.sendPageActivities = true;
-        pxconfig.init(params, new PxClient());
-        const conf = pxconfig.conf;
+        const pxConfig = new PxConfig(params, logger);
+        const conf = pxConfig.conf;
         conf.DEBUG_MODE.should.be.exactly(true);
     });
 
     it('customLogo should be overridden', () => {
         params.customLogo = 'http://www.google.com/logo.jpg';
-        pxconfig.init(params, new PxClient());
-        const conf = pxconfig.conf;
+        const pxConfig = new PxConfig(params, logger);
+        const conf = pxConfig.conf;
         conf.CUSTOM_LOGO.should.be.exactly('http://www.google.com/logo.jpg');
     });
 
     it('jsRef should be overridden', () => {
         params.jsRef = ['http://www.google.com/script.js'];
-        pxconfig.init(params, new PxClient());
-        const conf = pxconfig.conf;
+        const pxConfig = new PxConfig(params, logger);
+        const conf = pxConfig.conf;
         conf.JS_REF[0].should.equal('http://www.google.com/script.js');
     });
 
     it('cssRef should be overridden', () => {
         params.cssRef = ['http://www.google.com/stylesheet.css'];
-        pxconfig.init(params, new PxClient());
-        const conf = pxconfig.conf;
+        const pxConfig = new PxConfig(params, logger);
+        const conf = pxConfig.conf;
         conf.CSS_REF[0].should.equal('http://www.google.com/stylesheet.css');
     });
 });
\ No newline at end of file
diff --git a/test/pxenforcer.test.js b/test/pxenforcer.test.js
index 0698159f..c0a057e7 100644
--- a/test/pxenforcer.test.js
+++ b/test/pxenforcer.test.js
@@ -9,7 +9,7 @@ const PxClient = rewire('../lib/pxclient');
 const PxEnforcer = require('../lib/pxenforcer');
 
 describe('PX Enforcer - pxenforcer.js', () => {
-    let params, enforcer, req, stub;
+    let params, enforcer, req, stub, pxClient;
 
     beforeEach(() => {
         params = {
@@ -36,6 +36,8 @@ describe('PX Enforcer - pxenforcer.js', () => {
         req.get = (key) => {
             return req.headers[key] || '';
         };
+
+        pxClient = new PxClient();
     });
 
     afterEach(() => {
@@ -43,11 +45,11 @@ describe('PX Enforcer - pxenforcer.js', () => {
     });
 
     it('enforces a call in a disabled module', (done) => {
-        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
             return callback ? callback(null, data) : '';
         });
         params.enableModule = false;
-        enforcer = new PxEnforcer(params, new PxClient());
+        enforcer = new PxEnforcer(params, pxClient);
         enforcer.enforce(req, null, (response) => {
 
             (response === undefined).should.equal(true);
@@ -56,10 +58,10 @@ describe('PX Enforcer - pxenforcer.js', () => {
     });
 
     it('enforces a call in an enabled module', (done) => {
-        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
             return callback ? callback(null, data) : '';
         });
-        enforcer = new PxEnforcer(params, new PxClient());
+        enforcer = new PxEnforcer(params, pxClient);
         enforcer.enforce(req, null, (response) => {
 
             (response === undefined).should.equal(true);
@@ -68,14 +70,14 @@ describe('PX Enforcer - pxenforcer.js', () => {
     });
 
     it('uses first party to get client', (done) => {
-        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
             return callback ? callback(null, data) : '';
         });
-        const reqStub = sinon.stub(request, 'get').callsFake((data, callback) => {
+        const reqStub = sinon.stub(request, 'get').callsFake((data, config, callback) => {
             callback(null, {headers: {'x-px-johnny': '1'}, body: 'hello buddy', proxy:''});
         });
         req.originalUrl = '/_APP_ID/init.js';
-        enforcer = new PxEnforcer(params, new PxClient());
+        enforcer = new PxEnforcer(params, pxClient);
         enforcer.enforce(req, null, (error, response) => {
             (response === undefined).should.equal(false);
             response.body.should.equal('hello buddy');
@@ -86,16 +88,16 @@ describe('PX Enforcer - pxenforcer.js', () => {
     });
 
     it('uses first party for xhr post request', (done) => {
-        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
             return callback ? callback(null, data) : '';
         });
-        const reqStub = sinon.stub(request, 'post').callsFake((data, callback) => {
+        const reqStub = sinon.stub(request, 'post').callsFake((data, config, callback) => {
             callback(null, {headers: {'x-px-johnny': '1'}, body:'hello buddy'});
         });
         req.originalUrl = '/_APP_ID/xhr/something';
         req.method = 'POST';
         req.body = 'test';
-        enforcer = new PxEnforcer(params, new PxClient());
+        enforcer = new PxEnforcer(params, pxClient);
         enforcer.enforce(req, null, (error, response) => {
             (response === undefined).should.equal(false);
             response.body.should.equal('hello buddy');
@@ -106,16 +108,16 @@ describe('PX Enforcer - pxenforcer.js', () => {
     });
 
     it('uses first party for xhr get request', (done) => {
-        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
             return callback ? callback(null, data) : '';
         });
-        const reqStub = sinon.stub(request, 'get').callsFake((data, callback) => {
+        const reqStub = sinon.stub(request, 'get').callsFake((data, config, callback) => {
             callback(null, {headers: {'x-px-johnny': '1'}, body: 'hello buddy'});
         });
         req.originalUrl = '/_APP_ID/xhr/something';
         req.method = 'GET';
         req.body = 'test';
-        enforcer = new PxEnforcer(params, new PxClient());
+        enforcer = new PxEnforcer(params, pxClient);
         enforcer.enforce(req, null, (error, response) => {
             (response === undefined).should.equal(false);
             response.body.should.equal('hello buddy');
@@ -126,17 +128,17 @@ describe('PX Enforcer - pxenforcer.js', () => {
     });
 
     it('uses first party with pxvid cookie', (done) => {
-        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
             return callback ? callback(null, data) : '';
         });
-        const reqStub = sinon.stub(request, 'post').callsFake((data, callback) => {
+        const reqStub = sinon.stub(request, 'post').callsFake((data, config, callback) => {
             callback(null, {headers: {'x-px-johnny': '1'}, body:'hello buddy'});
         });
         req.originalUrl = '/_APP_ID/xhr/something';
         req.method = 'POST';
         req.cookies['_pxvid'] = 'abab-123';
         req.body = 'test';
-        enforcer = new PxEnforcer(params, new PxClient());
+        enforcer = new PxEnforcer(params, pxClient);
         enforcer.enforce(req, null, (error, response) => {
             (response === undefined).should.equal(false);
             response.body.should.equal('hello buddy');
@@ -147,16 +149,16 @@ describe('PX Enforcer - pxenforcer.js', () => {
     });
 
     it('uses first party for xhr and passed trough bodyParser', (done) => {
-        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
             return callback ? callback(null, data) : '';
         });
-        const reqStub = sinon.stub(request, 'post').callsFake((data, callback) => {
+        const reqStub = sinon.stub(request, 'post').callsFake((data, config, callback) => {
             callback(null, {headers: {'x-px-johnny': '1'}, body:'hello buddy'});
         });
         req.originalUrl = '/_APP_ID/xhr/something';
         req.method = 'POST';
         req.body = {key: 'value', anotherKey: 'anotherValue'};
-        enforcer = new PxEnforcer(params, new PxClient());
+        enforcer = new PxEnforcer(params, pxClient);
         enforcer.enforce(req, null, (error, response) => {
             (response === undefined).should.equal(false);
             response.body.should.equal('hello buddy');
@@ -167,7 +169,7 @@ describe('PX Enforcer - pxenforcer.js', () => {
     });
 
     it('should not use first party paths if originated from mobile', (done) => {
-        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
             data.score = 100;
             data.action = 'b';
             return callback ? callback(null, data) : '';
@@ -176,13 +178,13 @@ describe('PX Enforcer - pxenforcer.js', () => {
             moduleMode: 1,
             firstPartyEnabled: true
         }, params);
-        const reqStub = sinon.stub(request, 'post').callsFake((data, callback) => {
+        const reqStub = sinon.stub(request, 'post').callsFake((data, config, callback) => {
             callback(null, {headers: {'x-px-johnny': '1'}, body:'hello buddy'});
         });
         req.headers = {'x-px-authorization': '3:some-fake-cookie'};
         req.method = 'POST';
         req.body = {key: 'value', anotherKey: 'anotherValue'};
-        enforcer = new PxEnforcer(curParams, new PxClient());
+        enforcer = new PxEnforcer(curParams, pxClient);
         enforcer.enforce(req, null, (error, response) => {
             (response === undefined).should.equal(false);
             response.body.action.should.equal('block');
@@ -191,7 +193,7 @@ describe('PX Enforcer - pxenforcer.js', () => {
         });
     });
     it('should bypass monitor mode by header', (done) => {
-        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
             data.score = 100;
             data.action = 'b';
             return callback ? callback(null, data) : '';
@@ -208,7 +210,7 @@ describe('PX Enforcer - pxenforcer.js', () => {
         });
         req.method = 'POST';
         req.body = {key: 'value', anotherKey: 'anotherValue'};
-        enforcer = new PxEnforcer(curParams, new PxClient());
+        enforcer = new PxEnforcer(curParams, pxClient);
         enforcer.enforce(req, null, (error, response) => {
             (response === undefined).should.equal(false);
             (response.body.indexOf('Please verify you are a human') > -1).should.equal(true);
@@ -217,7 +219,7 @@ describe('PX Enforcer - pxenforcer.js', () => {
         });
     });
     it('should ignore bypass monitor mode by header', (done) => {
-        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
             data.score = 100;
             data.action = 'b';
             return callback ? callback(null, data) : '';
@@ -234,7 +236,7 @@ describe('PX Enforcer - pxenforcer.js', () => {
         });
         req.method = 'POST';
         req.body = {key: 'value', anotherKey: 'anotherValue'};
-        enforcer = new PxEnforcer(curParams, new PxClient());
+        enforcer = new PxEnforcer(curParams, pxClient);
         enforcer.enforce(req, null, (error, response) => {
             (response === undefined).should.equal(true);
             reqStub.restore();
@@ -242,7 +244,7 @@ describe('PX Enforcer - pxenforcer.js', () => {
         });
     });
     it('should ignore bypass monitor header as its not present', (done) => {
-        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
             data.score = 100;
             data.action = 'b';
             return callback ? callback(null, data) : '';
@@ -256,7 +258,7 @@ describe('PX Enforcer - pxenforcer.js', () => {
         });
         req.method = 'POST';
         req.body = {key: 'value', anotherKey: 'anotherValue'};
-        enforcer = new PxEnforcer(curParams, new PxClient());
+        enforcer = new PxEnforcer(curParams, pxClient);
         enforcer.enforce(req, null, (error, response) => {
             (response === undefined).should.equal(true);
             reqStub.restore();
@@ -264,7 +266,7 @@ describe('PX Enforcer - pxenforcer.js', () => {
         });
     });
     it('should ignore bypass monitor header as cookie is valid', (done) => {
-        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+        stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
             data.score = 0;
             data.action = 'b';
             return callback ? callback(null, data) : '';
@@ -281,7 +283,7 @@ describe('PX Enforcer - pxenforcer.js', () => {
         });
         req.method = 'POST';
         req.body = {key: 'value', anotherKey: 'anotherValue'};
-        enforcer = new PxEnforcer(curParams, new PxClient());
+        enforcer = new PxEnforcer(curParams, pxClient);
         enforcer.enforce(req, null, (error, response) => {
             (response === undefined).should.equal(true);
             reqStub.restore();
@@ -289,4 +291,3 @@ describe('PX Enforcer - pxenforcer.js', () => {
         });
     });
 });
-
diff --git a/test/pxutils.test.js b/test/pxutils.test.js
index 82205f9a..85b4d7b1 100644
--- a/test/pxutils.test.js
+++ b/test/pxutils.test.js
@@ -3,10 +3,11 @@
 const should = require('should');
 const rewire = require('rewire');
 const pxutil = require('../lib/pxutil');
-const PxClient = rewire('../lib/pxclient');
+const PxConfig = require('../lib/pxconfig');
+const PxLogger = require('../lib/pxlogger');
 
 describe('PX Utils - pxutils.js', () => {
-    let pxconfig;
+    let pxConfig;
     let params;
 
     beforeEach(() => {
@@ -22,12 +23,12 @@ describe('PX Utils - pxutils.js', () => {
             enrichCustomParameters: enrichCustomParameters
         };
 
-        pxconfig = require('../lib/pxconfig');
-        pxconfig.init(params, new PxClient());
+        const logger = new PxLogger();
+        pxConfig = new PxConfig(params, logger);
     });
 
     it('should generate headers array from headers object', (done) => {
-        const formattedHeaders = pxutil.formatHeaders({K: 'v'});
+        const formattedHeaders = pxutil.formatHeaders({K: 'v'}, pxConfig.conf.SENSITIVE_HEADERS);
         (Object.prototype.toString.call(formattedHeaders)).should.be.exactly('[object Array]');
         formattedHeaders[0]['name'].should.be.exactly('K');
         formattedHeaders[0]['value'].should.be.exactly('v');
@@ -36,7 +37,7 @@ describe('PX Utils - pxutils.js', () => {
 
     it('should receive custom params function and custom params object and add only 2 of them', (done) => {
         const dict = {};
-        pxutil.prepareCustomParams(pxconfig.conf, dict);
+        pxutil.prepareCustomParams(pxConfig.conf, dict);
         dict['custom_param1'].should.be.exactly('1');
         dict['custom_param2'].should.be.exactly('2');
         dict['custom_param10'].should.be.exactly('10');
