analytics-node
Advanced tools
Comparing version 0.6.0 to 1.0.0
0.6.0 / 2014-02-19 | ||
================== | ||
* add group() call | ||
0.6.0 - February 19, 2014 | ||
------------------------- | ||
* add group method |
232
lib/index.js
var assert = require('assert'); | ||
var debug = require('debug')('analytics-node'); | ||
var noop = function(){}; | ||
var request = require('superagent'); | ||
var type = require('component-type'); | ||
var join = require('join-component'); | ||
var uid = require('uid'); | ||
var version = require('../package.json').version; | ||
var _ = require('underscore'), | ||
events = require('events'), | ||
Client = require('./client'); | ||
global.setImmediate = global.setImmediate || process.nextTick.bind(process); | ||
/** | ||
* Expose an `Analytics` client. | ||
*/ | ||
module.exports = Analytics; | ||
/** | ||
* Keep a default client for use directly via its api methods. | ||
* Initialize a new `Analytics` with your Segment project's `writeKey` and an | ||
* optional dictionary of `options`. | ||
* | ||
* @param {String} writeKey | ||
* @param {Object} options (optional) | ||
* @property {Number} flushAt (default: 20) | ||
* @property {Number} flushAfter (default: 10000) | ||
* @property {String} host (default: 'https://api.segment.io') | ||
*/ | ||
var defaultClient = new Client(); | ||
var api = ['init', 'track', 'identify', 'flush', 'alias', 'group']; | ||
function Analytics(writeKey, options){ | ||
if (!(this instanceof Analytics)) return new Analytics(writeKey, options); | ||
assert(writeKey, "You must pass your Segment project's write key."); | ||
options = options || {}; | ||
this.queue = []; | ||
this.writeKey = writeKey; | ||
this.host = options.host || 'https://api.segment.io'; | ||
this.flushAt = Math.max(options.flushAt, 1) || 20; | ||
this.flushAfter = options.flushAfter || 10000; | ||
} | ||
/** | ||
* Add the default client's public API methods to the module | ||
* Send an identify `message`. | ||
* | ||
* @param {Object} message | ||
* @param {Function} fn (optional) | ||
* @return {Analytics} | ||
*/ | ||
_.each(api, function (method) { | ||
exports[method] = function () { | ||
return defaultClient[method].apply(defaultClient, arguments); | ||
}; | ||
}); | ||
Analytics.prototype.identify = function(message, fn){ | ||
validate(message); | ||
assert(message.anonymousId || message.userId, 'You must pass either an "anonymousId" or a "userId".'); | ||
this.enqueue('identify', message, fn); | ||
return this; | ||
}; | ||
/** | ||
* Add our eventEmitter calls for the client. | ||
* @param {Function} fn [description] | ||
* Send a group `message`. | ||
* | ||
* @param {Object} message | ||
* @param {Function} fn (optional) | ||
* @return {Analytics} | ||
*/ | ||
_.each(events.EventEmitter.prototype, function (fn, key) { | ||
exports[key] = function () { | ||
defaultClient[key].apply(defaultClient, arguments); | ||
Analytics.prototype.group = function(message, fn){ | ||
validate(message); | ||
assert(message.anonymousId || message.userId, 'You must pass either an "anonymousId" or a "userId".'); | ||
assert(message.groupId, 'You must pass a "groupId".'); | ||
this.enqueue('group', message, fn); | ||
return this; | ||
}; | ||
/** | ||
* Send a track `message`. | ||
* | ||
* @param {Object} message | ||
* @param {Function} fn (optional) | ||
* @return {Analytics} | ||
*/ | ||
Analytics.prototype.track = function(message, fn){ | ||
validate(message); | ||
assert(message.anonymousId || message.userId, 'You must pass either an "anonymousId" or a "userId".'); | ||
assert(message.event, 'You must pass an "event".'); | ||
this.enqueue('track', message, fn); | ||
return this; | ||
}; | ||
/** | ||
* Send a page `message`. | ||
* | ||
* @param {Object} message | ||
* @param {Function} fn (optional) | ||
* @return {Analytics} | ||
*/ | ||
Analytics.prototype.page = function(message, fn){ | ||
validate(message); | ||
assert(message.anonymousId || message.userId, 'You must pass either an "anonymousId" or a "userId".'); | ||
this.enqueue('page', message, fn); | ||
return this; | ||
}; | ||
/** | ||
* Send an alias `message`. | ||
* | ||
* @param {Object} message | ||
* @param {Function} fn (optional) | ||
* @return {Analytics} | ||
*/ | ||
Analytics.prototype.alias = function(message, fn){ | ||
validate(message); | ||
assert(message.userId, 'You must pass a "userId".'); | ||
assert(message.previousId, 'You must pass a "previousId".'); | ||
this.enqueue('alias', message, fn); | ||
return this; | ||
}; | ||
/** | ||
* Flush the current queue and callback `fn(err, batch)`. | ||
* | ||
* @param {Function} fn (optional) | ||
* @return {Analytics} | ||
*/ | ||
Analytics.prototype.flush = function(fn){ | ||
fn = fn || noop; | ||
if (!this.queue.length) return setImmediate(fn); | ||
var items = this.queue.splice(0, this.flushAt); | ||
var fns = items.map(function(_){ return _.callback; }); | ||
var batch = items.map(function(_){ return _.message; }); | ||
var context = { library: { name: 'analytics-node', version: version }}; | ||
var data = { | ||
batch: batch, | ||
timestamp: new Date(), | ||
context: context, | ||
messageId: uid(8) | ||
}; | ||
}); | ||
exports.defaults = require('./defaults'); | ||
exports.triggers = require('./triggers'); | ||
exports.Client = Client; | ||
debug('flush: %o', data); | ||
request | ||
.post(this.host + '/v1/batch') | ||
.auth(this.writeKey, '') | ||
.send(data) | ||
.end(function(err, res){ | ||
err = err || error(res); | ||
fns.push(fn); | ||
fns.forEach(function(fn){ fn(err, data); }); | ||
debug('flushed: %o', data); | ||
}); | ||
}; | ||
/** | ||
* Add a `message` of type `type` to the queue and check whether it should be | ||
* flushed. | ||
* | ||
* @param {String} type | ||
* @param {Object} message | ||
* @param {Functino} fn (optional) | ||
* @api private | ||
*/ | ||
Analytics.prototype.enqueue = function(type, message, fn){ | ||
fn = fn || noop; | ||
message.type = type; | ||
if (!message.timestamp) message.timestamp = new Date(); | ||
debug('%s: %o', type, message); | ||
this.queue.push({ | ||
message: message, | ||
callback: fn | ||
}); | ||
if (this.queue.length >= this.flushAt) this.flush(); | ||
if (this.timer) clearTimeout(this.timer); | ||
if (this.flushAfter) this.timer = setTimeout(this.flush.bind(this), this.flushAfter); | ||
}; | ||
/** | ||
* Validation rules. | ||
*/ | ||
var rules = { | ||
anonymousId: ['string', 'number'], | ||
category: 'string', | ||
context: 'object', | ||
event: 'string', | ||
groupId: ['string', 'number'], | ||
integrations: 'object', | ||
name: 'string', | ||
previousId: ['string', 'number'], | ||
timestamp: 'date', | ||
userId: ['string', 'number'] | ||
}; | ||
/** | ||
* Validate an options `obj`. | ||
* | ||
* @param {Object} obj | ||
*/ | ||
function validate(obj){ | ||
assert('object' == type(obj), 'You must pass a message object.'); | ||
for (var key in rules) { | ||
var val = obj[key]; | ||
if (!val) continue; | ||
var exp = rules[key]; | ||
exp = ('array' === type(exp) ? exp : [exp]); | ||
var a = 'object' == exp ? 'an' : 'a'; | ||
assert(exp.some(function(e){ return type(val) === e; }), '"' + key + '" must be ' + a + ' ' + join(exp, 'or') + '.'); | ||
} | ||
}; | ||
/** | ||
* Get an error from a `res`. | ||
* | ||
* @param {Object} res | ||
* @return {String} | ||
*/ | ||
function error(res){ | ||
if (!res.error) return; | ||
var body = res.body; | ||
var msg = body.error && body.error.message | ||
|| res.status + ' ' + res.text; | ||
return new Error(msg); | ||
} |
{ | ||
"name": "analytics-node", | ||
"version": "0.6.0", | ||
"repository": "git://github.com/segmentio/analytics-node", | ||
"version": "1.0.0", | ||
"description": "The hassle-free way to integrate analytics into any node application.", | ||
"repository": "git://github.com/segmentio/analytics-node", | ||
"keywords": [ | ||
@@ -14,18 +14,20 @@ "analytics", | ||
], | ||
"authors": "Ilya Volodarsky <ilya@segment.io>, Calvin French-Owen <calvin@segment.io>", | ||
"engines": { | ||
"node": ">= 0.8.x" | ||
}, | ||
"main": "./lib", | ||
"main": "lib/index.js", | ||
"dependencies": { | ||
"underscore": "1.4.3", | ||
"request": "2.12.0" | ||
"component-type": "~1.0.0", | ||
"join-component": "~1.0.0", | ||
"superagent": "~0.16.0", | ||
"debug": "~0.7.4", | ||
"uid": "0.0.2" | ||
}, | ||
"devDependencies": { | ||
"mocha": "1.8.1", | ||
"should": "1.2.1" | ||
"express": "~3.4.8" | ||
}, | ||
"engines": { | ||
"node": ">= 0.8.x" | ||
}, | ||
"scripts": { | ||
"test": "make test" | ||
} | ||
} | ||
} |
@@ -1,8 +0,7 @@ | ||
analytics-node | ||
============== | ||
[![Build Status](https://travis-ci.org/segmentio/analytics-node.png)](https://travis-ci.org/segmentio/analytics-node) | ||
#analytics-node | ||
[![Build Status](https://api.travis-ci.org/segmentio/analytics-node.svg?branch=master)](https://travis-ci.org/segmentio/analytics-node) | ||
analytics-node is a node.js client for [Segment.io](https://segment.io) | ||
A node.js client for [Segment.io](https://segment.io) — The hassle-free way to integrate analytics into any application. | ||
@@ -13,22 +12,17 @@ ## Documentation | ||
## License (MIT) | ||
## License | ||
WWWWWW||WWWWWW | ||
W W W||W W W | ||
|| | ||
( OO )__________ | ||
/ | \ | ||
/o o| MIT \ | ||
\___/||_||__||_|| * | ||
|| || || || | ||
_||_|| _||_|| | ||
(__|__|(__|__| | ||
``` | ||
WWWWWW||WWWWWW | ||
W W W||W W W | ||
|| | ||
( OO )__________ | ||
/ | \ | ||
/o o| MIT \ | ||
\___/||_||__||_|| * | ||
|| || || || | ||
_||_|| _||_|| | ||
(__|__|(__|__| | ||
``` | ||
Copyright © 2013 Segment.io Inc. \<friends@segment.io\> | ||
(The MIT License) | ||
Copyright (c) 2013 Segment.io Inc. <friends@segment.io> | ||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||
@@ -39,5 +33,1 @@ | ||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/segmentio/analytics-node/trend.png)](https://bitdeli.com/free "Bitdeli Badge") | ||
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
1
17048
5
10
493
32
2
1
+ Addedcomponent-type@~1.0.0
+ Addeddebug@~0.7.4
+ Addedjoin-component@~1.0.0
+ Addedsuperagent@~0.16.0
+ Addeduid@0.0.2
+ Addedcomponent-type@1.0.0(transitive)
+ Addedcookiejar@1.3.0(transitive)
+ Addeddebug@0.7.4(transitive)
+ Addedemitter-component@1.0.0(transitive)
+ Addedformidable@1.0.14(transitive)
+ Addedjoin-component@1.0.0(transitive)
+ Addedmethods@0.0.1(transitive)
+ Addedmime@1.2.5(transitive)
+ Addedqs@0.6.5(transitive)
+ Addedreduce-component@1.0.1(transitive)
+ Addedsuperagent@0.16.0(transitive)
+ Addeduid@0.0.2(transitive)
- Removedrequest@2.12.0
- Removedunderscore@1.4.3
- Removedrequest@2.12.0(transitive)
- Removedunderscore@1.4.3(transitive)