truffle
Version:
Truffle - Simple development framework for Ethereum
1,397 lines (1,226 loc) • 61.1 kB
JavaScript
#!/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: