UNPKG

truffle

Version:

Truffle - Simple development framework for Ethereum

1,397 lines (1,226 loc) 61.1 kB
#!/usr/bin/env node exports.id = 9509; exports.ids = [9509]; exports.modules = { /***/ 88054: /***/ (function(module, __unused_webpack_exports, __webpack_require__) { "use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; const events_1 = __webpack_require__(82361); const debug_1 = __importDefault(__webpack_require__(15158)); const promisify_1 = __importDefault(__webpack_require__(96304)); const debug = debug_1.default('agent-base'); function isAgent(v) { return Boolean(v) && typeof v.addRequest === 'function'; } function isSecureEndpoint() { const { stack } = new Error(); if (typeof stack !== 'string') return false; return stack.split('\n').some(l => l.indexOf('(https.js:') !== -1 || l.indexOf('node:https:') !== -1); } function createAgent(callback, opts) { return new createAgent.Agent(callback, opts); } (function (createAgent) { /** * Base `http.Agent` implementation. * No pooling/keep-alive is implemented by default. * * @param {Function} callback * @api public */ class Agent extends events_1.EventEmitter { constructor(callback, _opts) { super(); let opts = _opts; if (typeof callback === 'function') { this.callback = callback; } else if (callback) { opts = callback; } // Timeout for the socket to be returned from the callback this.timeout = null; if (opts && typeof opts.timeout === 'number') { this.timeout = opts.timeout; } // These aren't actually used by `agent-base`, but are required // for the TypeScript definition files in `@types/node` :/ this.maxFreeSockets = 1; this.maxSockets = 1; this.maxTotalSockets = Infinity; this.sockets = {}; this.freeSockets = {}; this.requests = {}; this.options = {}; } get defaultPort() { if (typeof this.explicitDefaultPort === 'number') { return this.explicitDefaultPort; } return isSecureEndpoint() ? 443 : 80; } set defaultPort(v) { this.explicitDefaultPort = v; } get protocol() { if (typeof this.explicitProtocol === 'string') { return this.explicitProtocol; } return isSecureEndpoint() ? 'https:' : 'http:'; } set protocol(v) { this.explicitProtocol = v; } callback(req, opts, fn) { throw new Error('"agent-base" has no default implementation, you must subclass and override `callback()`'); } /** * Called by node-core's "_http_client.js" module when creating * a new HTTP request with this Agent instance. * * @api public */ addRequest(req, _opts) { const opts = Object.assign({}, _opts); if (typeof opts.secureEndpoint !== 'boolean') { opts.secureEndpoint = isSecureEndpoint(); } if (opts.host == null) { opts.host = 'localhost'; } if (opts.port == null) { opts.port = opts.secureEndpoint ? 443 : 80; } if (opts.protocol == null) { opts.protocol = opts.secureEndpoint ? 'https:' : 'http:'; } if (opts.host && opts.path) { // If both a `host` and `path` are specified then it's most // likely the result of a `url.parse()` call... we need to // remove the `path` portion so that `net.connect()` doesn't // attempt to open that as a unix socket file. delete opts.path; } delete opts.agent; delete opts.hostname; delete opts._defaultAgent; delete opts.defaultPort; delete opts.createConnection; // Hint to use "Connection: close" // XXX: non-documented `http` module API :( req._last = true; req.shouldKeepAlive = false; let timedOut = false; let timeoutId = null; const timeoutMs = opts.timeout || this.timeout; const onerror = (err) => { if (req._hadError) return; req.emit('error', err); // For Safety. Some additional errors might fire later on // and we need to make sure we don't double-fire the error event. req._hadError = true; }; const ontimeout = () => { timeoutId = null; timedOut = true; const err = new Error(`A "socket" was not created for HTTP request before ${timeoutMs}ms`); err.code = 'ETIMEOUT'; onerror(err); }; const callbackError = (err) => { if (timedOut) return; if (timeoutId !== null) { clearTimeout(timeoutId); timeoutId = null; } onerror(err); }; const onsocket = (socket) => { if (timedOut) return; if (timeoutId != null) { clearTimeout(timeoutId); timeoutId = null; } if (isAgent(socket)) { // `socket` is actually an `http.Agent` instance, so // relinquish responsibility for this `req` to the Agent // from here on debug('Callback returned another Agent instance %o', socket.constructor.name); socket.addRequest(req, opts); return; } if (socket) { socket.once('free', () => { this.freeSocket(socket, opts); }); req.onSocket(socket); return; } const err = new Error(`no Duplex stream was returned to agent-base for \`${req.method} ${req.path}\``); onerror(err); }; if (typeof this.callback !== 'function') { onerror(new Error('`callback` is not defined')); return; } if (!this.promisifiedCallback) { if (this.callback.length >= 3) { debug('Converting legacy callback function to promise'); this.promisifiedCallback = promisify_1.default(this.callback); } else { this.promisifiedCallback = this.callback; } } if (typeof timeoutMs === 'number' && timeoutMs > 0) { timeoutId = setTimeout(ontimeout, timeoutMs); } if ('port' in opts && typeof opts.port !== 'number') { opts.port = Number(opts.port); } try { debug('Resolving socket for %o request: %o', opts.protocol, `${req.method} ${req.path}`); Promise.resolve(this.promisifiedCallback(req, opts)).then(onsocket, callbackError); } catch (err) { Promise.reject(err).catch(callbackError); } } freeSocket(socket, opts) { debug('Freeing socket %o %o', socket.constructor.name, opts); socket.destroy(); } destroy() { debug('Destroying agent %o', this.constructor.name); } } createAgent.Agent = Agent; // So that `instanceof` works correctly createAgent.prototype = createAgent.Agent.prototype; })(createAgent || (createAgent = {})); module.exports = createAgent; //# sourceMappingURL=index.js.map /***/ }), /***/ 96304: /***/ ((__unused_webpack_module, exports) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); function promisify(fn) { return function (req, opts) { return new Promise((resolve, reject) => { fn.call(this, req, opts, (err, rtn) => { if (err) { reject(err); } else { resolve(rtn); } }); }); }; } exports["default"] = promisify; //# sourceMappingURL=promisify.js.map /***/ }), /***/ 39146: /***/ (function(__unused_webpack_module, exports, __webpack_require__) { "use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); const net_1 = __importDefault(__webpack_require__(41808)); const tls_1 = __importDefault(__webpack_require__(24404)); const url_1 = __importDefault(__webpack_require__(57310)); const assert_1 = __importDefault(__webpack_require__(39491)); const debug_1 = __importDefault(__webpack_require__(15158)); const agent_base_1 = __webpack_require__(88054); const parse_proxy_response_1 = __importDefault(__webpack_require__(59829)); const debug = debug_1.default('https-proxy-agent:agent'); /** * The `HttpsProxyAgent` implements an HTTP Agent subclass that connects to * the specified "HTTP(s) proxy server" in order to proxy HTTPS requests. * * Outgoing HTTP requests are first tunneled through the proxy server using the * `CONNECT` HTTP request method to establish a connection to the proxy server, * and then the proxy server connects to the destination target and issues the * HTTP request from the proxy server. * * `https:` requests have their socket connection upgraded to TLS once * the connection to the proxy server has been established. * * @api public */ class HttpsProxyAgent extends agent_base_1.Agent { constructor(_opts) { let opts; if (typeof _opts === 'string') { opts = url_1.default.parse(_opts); } else { opts = _opts; } if (!opts) { throw new Error('an HTTP(S) proxy server `host` and `port` must be specified!'); } debug('creating new HttpsProxyAgent instance: %o', opts); super(opts); const proxy = Object.assign({}, opts); // If `true`, then connect to the proxy server over TLS. // Defaults to `false`. this.secureProxy = opts.secureProxy || isHTTPS(proxy.protocol); // Prefer `hostname` over `host`, and set the `port` if needed. proxy.host = proxy.hostname || proxy.host; if (typeof proxy.port === 'string') { proxy.port = parseInt(proxy.port, 10); } if (!proxy.port && proxy.host) { proxy.port = this.secureProxy ? 443 : 80; } // ALPN is supported by Node.js >= v5. // attempt to negotiate http/1.1 for proxy servers that support http/2 if (this.secureProxy && !('ALPNProtocols' in proxy)) { proxy.ALPNProtocols = ['http 1.1']; } if (proxy.host && proxy.path) { // If both a `host` and `path` are specified then it's most likely // the result of a `url.parse()` call... we need to remove the // `path` portion so that `net.connect()` doesn't attempt to open // that as a Unix socket file. delete proxy.path; delete proxy.pathname; } this.proxy = proxy; } /** * Called when the node-core HTTP client library is creating a * new HTTP request. * * @api protected */ callback(req, opts) { return __awaiter(this, void 0, void 0, function* () { const { proxy, secureProxy } = this; // Create a socket connection to the proxy server. let socket; if (secureProxy) { debug('Creating `tls.Socket`: %o', proxy); socket = tls_1.default.connect(proxy); } else { debug('Creating `net.Socket`: %o', proxy); socket = net_1.default.connect(proxy); } const headers = Object.assign({}, proxy.headers); const hostname = `${opts.host}:${opts.port}`; let payload = `CONNECT ${hostname} HTTP/1.1\r\n`; // Inject the `Proxy-Authorization` header if necessary. if (proxy.auth) { headers['Proxy-Authorization'] = `Basic ${Buffer.from(proxy.auth).toString('base64')}`; } // The `Host` header should only include the port // number when it is not the default port. let { host, port, secureEndpoint } = opts; if (!isDefaultPort(port, secureEndpoint)) { host += `:${port}`; } headers.Host = host; headers.Connection = 'close'; for (const name of Object.keys(headers)) { payload += `${name}: ${headers[name]}\r\n`; } const proxyResponsePromise = parse_proxy_response_1.default(socket); socket.write(`${payload}\r\n`); const { statusCode, buffered } = yield proxyResponsePromise; if (statusCode === 200) { req.once('socket', resume); if (opts.secureEndpoint) { const servername = opts.servername || opts.host; if (!servername) { throw new Error('Could not determine "servername"'); } // The proxy is connecting to a TLS server, so upgrade // this socket connection to a TLS connection. debug('Upgrading socket connection to TLS'); return tls_1.default.connect(Object.assign(Object.assign({}, omit(opts, 'host', 'hostname', 'path', 'port')), { socket, servername })); } return socket; } // Some other status code that's not 200... need to re-play the HTTP // header "data" events onto the socket once the HTTP machinery is // attached so that the node core `http` can parse and handle the // error status code. // Close the original socket, and a new "fake" socket is returned // instead, so that the proxy doesn't get the HTTP request // written to it (which may contain `Authorization` headers or other // sensitive data). // // See: https://hackerone.com/reports/541502 socket.destroy(); const fakeSocket = new net_1.default.Socket(); fakeSocket.readable = true; // Need to wait for the "socket" event to re-play the "data" events. req.once('socket', (s) => { debug('replaying proxy buffer for failed request'); assert_1.default(s.listenerCount('data') > 0); // Replay the "buffered" Buffer onto the fake `socket`, since at // this point the HTTP module machinery has been hooked up for // the user. s.push(buffered); s.push(null); }); return fakeSocket; }); } } exports["default"] = HttpsProxyAgent; function resume(socket) { socket.resume(); } function isDefaultPort(port, secure) { return Boolean((!secure && port === 80) || (secure && port === 443)); } function isHTTPS(protocol) { return typeof protocol === 'string' ? /^https:?$/i.test(protocol) : false; } function omit(obj, ...keys) { const ret = {}; let key; for (key in obj) { if (!keys.includes(key)) { ret[key] = obj[key]; } } return ret; } //# sourceMappingURL=agent.js.map /***/ }), /***/ 26018: /***/ (function(module, __unused_webpack_exports, __webpack_require__) { "use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; const agent_1 = __importDefault(__webpack_require__(39146)); function createHttpsProxyAgent(opts) { return new agent_1.default(opts); } (function (createHttpsProxyAgent) { createHttpsProxyAgent.HttpsProxyAgent = agent_1.default; createHttpsProxyAgent.prototype = agent_1.default.prototype; })(createHttpsProxyAgent || (createHttpsProxyAgent = {})); module.exports = createHttpsProxyAgent; //# sourceMappingURL=index.js.map /***/ }), /***/ 59829: /***/ (function(__unused_webpack_module, exports, __webpack_require__) { "use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); const debug_1 = __importDefault(__webpack_require__(15158)); const debug = debug_1.default('https-proxy-agent:parse-proxy-response'); function parseProxyResponse(socket) { return new Promise((resolve, reject) => { // we need to buffer any HTTP traffic that happens with the proxy before we get // the CONNECT response, so that if the response is anything other than an "200" // response code, then we can re-play the "data" events on the socket once the // HTTP parser is hooked up... let buffersLength = 0; const buffers = []; function read() { const b = socket.read(); if (b) ondata(b); else socket.once('readable', read); } function cleanup() { socket.removeListener('end', onend); socket.removeListener('error', onerror); socket.removeListener('close', onclose); socket.removeListener('readable', read); } function onclose(err) { debug('onclose had error %o', err); } function onend() { debug('onend'); } function onerror(err) { cleanup(); debug('onerror %o', err); reject(err); } function ondata(b) { buffers.push(b); buffersLength += b.length; const buffered = Buffer.concat(buffers, buffersLength); const endOfHeaders = buffered.indexOf('\r\n\r\n'); if (endOfHeaders === -1) { // keep buffering debug('have not received end of HTTP headers yet...'); read(); return; } const firstLine = buffered.toString('ascii', 0, buffered.indexOf('\r\n')); const statusCode = +firstLine.split(' ')[1]; debug('got proxy server response: %o', firstLine); resolve({ statusCode, buffered }); } socket.on('error', onerror); socket.on('close', onclose); socket.on('end', onend); read(); }); } exports["default"] = parseProxyResponse; //# sourceMappingURL=parse-proxy-response.js.map /***/ }), /***/ 84403: /***/ ((__unused_webpack_module, exports, __webpack_require__) => { /** * Group profile methods. Learn more: https://help.mixpanel.com/hc/en-us/articles/360025333632 */ const {ProfileHelpers} = __webpack_require__(99145); class MixpanelGroups extends ProfileHelpers() { constructor(mp_instance) { super(); this.mixpanel = mp_instance; this.endpoint = '/groups'; } /** groups.set_once(group_key, group_id, prop, to, modifiers, callback) --- The same as groups.set, but adds a property value to a group only if it has not been set before. */ set_once(group_key, group_id, prop, to, modifiers, callback) { const identifiers = {$group_key: group_key, $group_id: group_id}; this._set(prop, to, modifiers, callback, {identifiers, set_once: true}); } /** groups.set(group_key, group_id, prop, to, modifiers, callback) --- set properties on a group profile usage: mixpanel.groups.set('company', 'Acme Inc.', '$name', 'Acme Inc.'); mixpanel.groups.set('company', 'Acme Inc.', { 'Industry': 'widgets', '$name': 'Acme Inc.', }); */ set(group_key, group_id, prop, to, modifiers, callback) { const identifiers = {$group_key: group_key, $group_id: group_id}; this._set(prop, to, modifiers, callback, {identifiers}); } /** groups.delete_group(group_key, group_id, modifiers, callback) --- delete a group profile permanently usage: mixpanel.groups.delete_group('company', 'Acme Inc.'); */ delete_group(group_key, group_id, modifiers, callback) { const identifiers = {$group_key: group_key, $group_id: group_id}; this._delete_profile({identifiers, modifiers, callback}); } /** groups.remove(group_key, group_id, data, modifiers, callback) --- remove a value from a list-valued group profile property. usage: mixpanel.groups.remove('company', 'Acme Inc.', {'products': 'anvil'}); mixpanel.groups.remove('company', 'Acme Inc.', { 'products': 'anvil', 'customer segments': 'coyotes' }); */ remove(group_key, group_id, data, modifiers, callback) { const identifiers = {$group_key: group_key, $group_id: group_id}; this._remove({identifiers, data, modifiers, callback}); } /** groups.union(group_key, group_id, data, modifiers, callback) --- merge value(s) into a list-valued group profile property. usage: mixpanel.groups.union('company', 'Acme Inc.', {'products': 'anvil'}); mixpanel.groups.union('company', 'Acme Inc.', {'products': ['anvil'], 'customer segments': ['coyotes']}); */ union(group_key, group_id, data, modifiers, callback) { const identifiers = {$group_key: group_key, $group_id: group_id}; this._union({identifiers, data, modifiers, callback}) } /** groups.unset(group_key, group_id, prop, modifiers, callback) --- delete a property on a group profile usage: mixpanel.groups.unset('company', 'Acme Inc.', 'products'); mixpanel.groups.unset('company', 'Acme Inc.', ['products', 'customer segments']); */ unset(group_key, group_id, prop, modifiers, callback) { const identifiers = {$group_key: group_key, $group_id: group_id}; this._unset({identifiers, prop, modifiers, callback}) } } exports.MixpanelGroups = MixpanelGroups; /***/ }), /***/ 19509: /***/ ((module, __unused_webpack_exports, __webpack_require__) => { /* Heavily inspired by the original js library copyright Mixpanel, Inc. (http://mixpanel.com/) Copyright (c) 2012 Carl Sverre Released under the MIT license. */ const querystring = __webpack_require__(63477); const Buffer = (__webpack_require__(14300).Buffer); const http = __webpack_require__(13685); const https = __webpack_require__(95687); const HttpsProxyAgent = __webpack_require__(26018); const url = __webpack_require__(57310); const packageInfo = __webpack_require__(69000) const {async_all, ensure_timestamp} = __webpack_require__(28524); const {MixpanelGroups} = __webpack_require__(84403); const {MixpanelPeople} = __webpack_require__(4623); const DEFAULT_CONFIG = { test: false, debug: false, verbose: false, host: 'api.mixpanel.com', protocol: 'https', path: '', keepAlive: true, // set this to true to automatically geolocate based on the client's ip. // e.g., when running under electron geolocate: false, }; var create_client = function(token, config) { if (!token) { throw new Error("The Mixpanel Client needs a Mixpanel token: `init(token)`"); } const metrics = { token, config: {...DEFAULT_CONFIG}, }; const {keepAlive} = metrics.config; // mixpanel constants const MAX_BATCH_SIZE = 50; const REQUEST_LIBS = {http, https}; const REQUEST_AGENTS = { http: new http.Agent({keepAlive}), https: new https.Agent({keepAlive}), }; const proxyPath = process.env.HTTPS_PROXY || process.env.HTTP_PROXY; const proxyAgent = proxyPath ? new HttpsProxyAgent(Object.assign(url.parse(proxyPath), { keepAlive, })) : null; /** * sends an async GET or POST request to mixpanel * for batch processes data must be send in the body of a POST * @param {object} options * @param {string} options.endpoint * @param {object} options.data the data to send in the request * @param {string} [options.method] e.g. `get` or `post`, defaults to `get` * @param {function} callback called on request completion or error */ metrics.send_request = function(options, callback) { callback = callback || function() {}; let content = Buffer.from(JSON.stringify(options.data)).toString('base64'); const endpoint = options.endpoint; const method = (options.method || 'GET').toUpperCase(); let query_params = { 'ip': metrics.config.geolocate ? 1 : 0, 'verbose': metrics.config.verbose ? 1 : 0 }; const key = metrics.config.key; const secret = metrics.config.secret; const request_lib = REQUEST_LIBS[metrics.config.protocol]; let request_options = { host: metrics.config.host, port: metrics.config.port, headers: {}, method: method }; let request; if (!request_lib) { throw new Error( "Mixpanel Initialization Error: Unsupported protocol " + metrics.config.protocol + ". " + "Supported protocols are: " + Object.keys(REQUEST_LIBS) ); } if (method === 'POST') { content = 'data=' + content; request_options.headers['Content-Type'] = 'application/x-www-form-urlencoded'; request_options.headers['Content-Length'] = Buffer.byteLength(content); } else if (method === 'GET') { query_params.data = content; } // add auth params if (secret) { if (request_lib !== https) { throw new Error("Must use HTTPS if authenticating with API Secret"); } const encoded = Buffer.from(secret + ':').toString('base64'); request_options.headers['Authorization'] = 'Basic ' + encoded; } else if (key) { query_params.api_key = key; } else if (endpoint === '/import') { throw new Error("The Mixpanel Client needs a Mixpanel API Secret when importing old events: `init(token, { secret: ... })`"); } request_options.agent = proxyAgent || REQUEST_AGENTS[metrics.config.protocol]; if (metrics.config.test) { query_params.test = 1; } request_options.path = metrics.config.path + endpoint + "?" + querystring.stringify(query_params); request = request_lib.request(request_options, function(res) { var data = ""; res.on('data', function(chunk) { data += chunk; }); res.on('end', function() { var e; if (metrics.config.verbose) { try { var result = JSON.parse(data); if(result.status != 1) { e = new Error("Mixpanel Server Error: " + result.error); } } catch(ex) { e = new Error("Could not parse response from Mixpanel"); } } else { e = (data !== '1') ? new Error("Mixpanel Server Error: " + data) : undefined; } callback(e); }); }); request.on('error', function(e) { if (metrics.config.debug) { console.log("Got Error: " + e.message); } callback(e); }); if (method === 'POST') { request.write(content); } request.end(); }; /** * Send an event to Mixpanel, using the specified endpoint (e.g., track/import) * @param {string} endpoint - API endpoint name * @param {string} event - event name * @param {object} properties - event properties * @param {Function} [callback] - callback for request completion/error */ metrics.send_event_request = function(endpoint, event, properties, callback) { properties.token = metrics.token; properties.mp_lib = "node"; properties.$lib_version = packageInfo.version; var data = { event: event, properties: properties }; if (metrics.config.debug) { console.log("Sending the following event to Mixpanel:\n", data); } metrics.send_request({ method: "GET", endpoint: endpoint, data: data }, callback); }; /** * breaks array into equal-sized chunks, with the last chunk being the remainder * @param {Array} arr * @param {number} size * @returns {Array} */ var chunk = function(arr, size) { var chunks = [], i = 0, total = arr.length; while (i < total) { chunks.push(arr.slice(i, i += size)); } return chunks; }; /** * sends events in batches * @param {object} options * @param {[{}]} options.event_list array of event objects * @param {string} options.endpoint e.g. `/track` or `/import` * @param {number} [options.max_concurrent_requests] limits concurrent async requests over the network * @param {number} [options.max_batch_size] limits number of events sent to mixpanel per request * @param {Function} [callback] callback receives array of errors if any * */ var send_batch_requests = function(options, callback) { var event_list = options.event_list, endpoint = options.endpoint, max_batch_size = options.max_batch_size ? Math.min(MAX_BATCH_SIZE, options.max_batch_size) : MAX_BATCH_SIZE, // to maintain original intention of max_batch_size; if max_batch_size is greater than 50, we assume the user is trying to set max_concurrent_requests max_concurrent_requests = options.max_concurrent_requests || (options.max_batch_size > MAX_BATCH_SIZE && Math.ceil(options.max_batch_size / MAX_BATCH_SIZE)), event_batches = chunk(event_list, max_batch_size), request_batches = max_concurrent_requests ? chunk(event_batches, max_concurrent_requests) : [event_batches], total_event_batches = event_batches.length, total_request_batches = request_batches.length; /** * sends a batch of events to mixpanel through http api * @param {Array} batch * @param {Function} cb */ function send_event_batch(batch, cb) { if (batch.length > 0) { batch = batch.map(function (event) { var properties = event.properties; if (endpoint === '/import' || event.properties.time) { // usually there will be a time property, but not required for `/track` endpoint event.properties.time = ensure_timestamp(event.properties.time); } event.properties.token = event.properties.token || metrics.token; return event; }); // must be a POST metrics.send_request({ method: "POST", endpoint: endpoint, data: batch }, cb); } } /** * Asynchronously sends batches of requests * @param {number} index */ function send_next_request_batch(index) { var request_batch = request_batches[index], cb = function (errors, results) { index += 1; if (index === total_request_batches) { callback && callback(errors, results); } else { send_next_request_batch(index); } }; async_all(request_batch, send_event_batch, cb); } // init recursive function send_next_request_batch(0); if (metrics.config.debug) { console.log( "Sending " + event_list.length + " events to Mixpanel in " + total_event_batches + " batches of events and " + total_request_batches + " batches of requests" ); } }; /** track(event, properties, callback) --- this function sends an event to mixpanel. event:string the event name properties:object additional event properties to send callback:function(err:Error) callback is called when the request is finished or an error occurs */ metrics.track = function(event, properties, callback) { if (!properties || typeof properties === "function") { callback = properties; properties = {}; } // time is optional for `track` if (properties.time) { properties.time = ensure_timestamp(properties.time); } metrics.send_event_request("/track", event, properties, callback); }; /** * send a batch of events to mixpanel `track` endpoint: this should only be used if events are less than 5 days old * @param {Array} event_list array of event objects to track * @param {object} [options] * @param {number} [options.max_concurrent_requests] number of concurrent http requests that can be made to mixpanel * @param {number} [options.max_batch_size] number of events that can be sent to mixpanel per request * @param {Function} [callback] callback receives array of errors if any */ metrics.track_batch = function(event_list, options, callback) { options = options || {}; if (typeof options === 'function') { callback = options; options = {}; } var batch_options = { event_list: event_list, endpoint: "/track", max_concurrent_requests: options.max_concurrent_requests, max_batch_size: options.max_batch_size }; send_batch_requests(batch_options, callback); }; /** import(event, time, properties, callback) --- This function sends an event to mixpanel using the import endpoint. The time argument should be either a Date or Number, and should signify the time the event occurred. It is highly recommended that you specify the distinct_id property for each event you import, otherwise the events will be tied to the IP address of the sending machine. For more information look at: https://mixpanel.com/docs/api-documentation/importing-events-older-than-31-days event:string the event name time:date|number the time of the event properties:object additional event properties to send callback:function(err:Error) callback is called when the request is finished or an error occurs */ metrics.import = function(event, time, properties, callback) { if (!properties || typeof properties === "function") { callback = properties; properties = {}; } properties.time = ensure_timestamp(time); metrics.send_event_request("/import", event, properties, callback); }; /** import_batch(event_list, options, callback) --- This function sends a list of events to mixpanel using the import endpoint. The format of the event array should be: [ { "event": "event name", "properties": { "time": new Date(), // Number or Date; required for each event "key": "val", ... } }, { "event": "event name", "properties": { "time": new Date() // Number or Date; required for each event } }, ... ] See import() for further information about the import endpoint. Options: max_batch_size: the maximum number of events to be transmitted over the network simultaneously. useful for capping bandwidth usage. max_concurrent_requests: the maximum number of concurrent http requests that can be made to mixpanel; also useful for capping bandwidth. N.B.: the Mixpanel API only accepts 50 events per request, so regardless of max_batch_size, larger lists of events will be chunked further into groups of 50. event_list:array list of event names and properties options:object optional batch configuration callback:function(error_list:array) callback is called when the request is finished or an error occurs */ metrics.import_batch = function(event_list, options, callback) { var batch_options; if (typeof(options) === "function" || !options) { callback = options; options = {}; } batch_options = { event_list: event_list, endpoint: "/import", max_concurrent_requests: options.max_concurrent_requests, max_batch_size: options.max_batch_size }; send_batch_requests(batch_options, callback); }; /** alias(distinct_id, alias) --- This function creates an alias for distinct_id For more information look at: https://mixpanel.com/docs/integration-libraries/using-mixpanel-alias distinct_id:string the current identifier alias:string the future alias */ metrics.alias = function(distinct_id, alias, callback) { var properties = { distinct_id: distinct_id, alias: alias }; metrics.track('$create_alias', properties, callback); }; metrics.groups = new MixpanelGroups(metrics); metrics.people = new MixpanelPeople(metrics); /** set_config(config) --- Modifies the mixpanel config config:object an object with properties to override in the mixpanel client config */ metrics.set_config = function(config) { Object.assign(metrics.config, config); if (config.host) { // Split host into host and port const [host, port] = config.host.split(':'); metrics.config.host = host; if (port) { metrics.config.port = Number(port); } } }; if (config) { metrics.set_config(config); } return metrics; }; // module exporting module.exports = { Client: function(token) { console.warn("The function `Client(token)` is deprecated. It is now called `init(token)`."); return create_client(token); }, init: create_client }; /***/ }), /***/ 4623: /***/ ((__unused_webpack_module, exports, __webpack_require__) => { const {merge_modifiers, ProfileHelpers} = __webpack_require__(99145); class MixpanelPeople extends ProfileHelpers() { constructor(mp_instance) { super(); this.mixpanel = mp_instance; this.endpoint = '/engage'; } /** people.set_once(distinct_id, prop, to, modifiers, callback) --- The same as people.set but in the words of mixpanel: mixpanel.people.set_once " This method allows you to set a user attribute, only if it is not currently set. It can be called multiple times safely, so is perfect for storing things like the first date you saw a user, or the referrer that brought them to your website for the first time. " */ set_once(distinct_id, prop, to, modifiers, callback) { const identifiers = {$distinct_id: distinct_id}; this._set(prop, to, modifiers, callback, {identifiers, set_once: true}); } /** people.set(distinct_id, prop, to, modifiers, callback) --- set properties on an user record in engage usage: mixpanel.people.set('bob', 'gender', 'm'); mixpanel.people.set('joe', { 'company': 'acme', 'plan': 'premium' }); */ set(distinct_id, prop, to, modifiers, callback) { const identifiers = {$distinct_id: distinct_id}; this._set(prop, to, modifiers, callback, {identifiers}); } /** people.increment(distinct_id, prop, by, modifiers, callback) --- increment/decrement properties on an user record in engage usage: mixpanel.people.increment('bob', 'page_views', 1); // or, for convenience, if you're just incrementing a counter by 1, you can // simply do mixpanel.people.increment('bob', 'page_views'); // to decrement a counter, pass a negative number mixpanel.people.increment('bob', 'credits_left', -1); // like mixpanel.people.set(), you can increment multiple properties at once: mixpanel.people.increment('bob', { counter1: 1, counter2: 3, counter3: -2 }); */ increment(distinct_id, prop, by, modifiers, callback) { // TODO extract to ProfileHelpers var $add = {}; if (typeof(prop) === 'object') { if (typeof(by) === 'object') { callback = modifiers; modifiers = by; } else { callback = by; } for (const [key, val] of Object.entries(prop)) { if (isNaN(parseFloat(val))) { if (this.mixpanel.config.debug) { console.error("Invalid increment value passed to mixpanel.people.increment - must be a number"); console.error("Passed " + key + ":" + val); } } else { $add[key] = val; } }; } else { if (typeof(by) === 'number' || !by) { by = by || 1; $add[prop] = by; if (typeof(modifiers) === 'function') { callback = modifiers; } } else if (typeof(by) === 'function') { callback = by; $add[prop] = 1; } else { callback = modifiers; modifiers = (typeof(by) === 'object') ? by : {}; $add[prop] = 1; } } var data = { '$add': $add, '$token': this.mixpanel.token, '$distinct_id': distinct_id }; data = merge_modifiers(data, modifiers); if (this.mixpanel.config.debug) { console.log("Sending the following data to Mixpanel (Engage):"); console.log(data); } this.mixpanel.send_request({ method: "GET", endpoint: "/engage", data: data }, callback); } /** people.append(distinct_id, prop, value, modifiers, callback) --- Append a value to a list-valued people analytics property. usage: // append a value to a list, creating it if needed mixpanel.people.append('bob', 'pages_visited', 'homepage'); // like mixpanel.people.set(), you can append multiple properties at once: mixpanel.people.append('bob', { list1: 'bob', list2: 123 }); */ append(distinct_id, prop, value, modifiers, callback) { // TODO extract to ProfileHelpers var $append = {}; if (typeof(prop) === 'object') { if (typeof(value) === 'object') { callback = modifiers; modifiers = value; } else { callback = value; } Object.keys(prop).forEach(function(key) { $append[key] = prop[key]; }); } else { $append[prop] = value; if (typeof(modifiers) === 'function') { callback = modifiers; } } var data = { '$append': $append, '$token': this.mixpanel.token, '$distinct_id': distinct_id }; data = merge_modifiers(data, modifiers); if (this.mixpanel.config.debug) { console.log("Sending the following data to Mixpanel (Engage):"); console.log(data); } this.mixpanel.send_request({ method: "GET", endpoint: "/engage", data: data }, callback); } /** people.track_charge(distinct_id, amount, properties, modifiers, callback) --- Record that you have charged the current user a certain amount of money. usage: // charge a user $29.99 mixpanel.people.track_charge('bob', 29.99); // charge a user $19 on the 1st of february mixpanel.people.track_charge('bob', 19, { '$time': new Date('feb 1 2012') }); */ track_charge(distinct_id, amount, properties, modifiers, callback) { if (typeof(properties) === 'function' || !properties) { callback = properties || function() {}; properties = {}; } else { if (typeof(modifiers) === 'function' || !modifiers) { callback = modifiers || function() {}; if (properties.$ignore_time || properties.hasOwnProperty("$ip")) { modifiers = {}; Object.keys(properties).forEach(function(key) { modifiers[key] = properties[key]; delete properties[key]; }); } } } if (typeof(amount) !== 'number') { amount = parseFloat(amount); if (isNaN(amount)) { console.error("Invalid value passed to mixpanel.people.track_charge - must be a number"); return; } } properties.$amount = amount; if (properties.hasOwnProperty('$time')) { var time = properties.$time; if (Object.prototype.toString.call(time) === '[object Date]') { properties.$time = time.toISOString(); } } var data = { '$append': { '$transactions': properties }, '$token': this.mixpanel.token, '$distinct_id': distinct_id }; data = merge_modifiers(data, modifiers); if (this.mixpanel.config.debug) { console.log("Sending the following data to Mixpanel (Engage):"); console.log(data); } this.mixpanel.send_request({ method: "GET", endpoint: "/engage", data: data }, callback); } /** people.clear_charges(distinct_id, modifiers, callback) --- Clear all the current user's transactions. usage: mixpanel.people.clear_charges('bob'); */ clear_charges(distinct_id, modifiers, callback) { var data = { '$set': { '$transactions': [] }, '$token': this.mixpanel.token, '$distinct_id': distinct_id }; if (typeof(modifiers) === 'function') { callback = modifiers; } data = merge_modifiers(data, modifiers); if (this.mixpanel.config.debug) { console.log("Clearing this user's charges:", distinct_id); } this.mixpanel.send_request({ method: "GET", endpoint: "/engage", data: data }, callback); } /** people.delete_user(distinct_id, modifiers, callback) --- delete an user record in engage usage: