Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

honeybadger-js

Package Overview
Dependencies
Maintainers
1
Versions
43
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

honeybadger-js - npm Package Compare versions

Comparing version 0.3.1 to 0.4.0

examples/browserify/.npmignore

4

bower.json
{
"name": "honeybadger-js",
"version": "0.3.1",
"name": "honeybadger",
"version": "0.4.0",
"homepage": "https://github.com/honeybadger-io/honeybadger-js",

@@ -5,0 +5,0 @@ "authors": [

/*
honeybadger.js v0.3.1
honeybadger.js v0.4.0
A JavaScript Notifier for Honeybadger

@@ -8,623 +8,534 @@ https://github.com/honeybadger-io/honeybadger-js

*/
(function(window) {
// Generated by CoffeeScript 1.9.3
var helpers;
(function (root, builder) {
// Read default configuration from script tag if available.
var scriptConfig = {};
(function() {
var tags = document.getElementsByTagName("script");
var tag = tags[tags.length - 1];
if (!tag) { return; }
var attrs = tag.attributes;
var value;
for (i = 0, len = attrs.length; i < len; i++) {
if (/data-(\w+)$/.test(attrs[i].nodeName)) {
value = attrs[i].nodeValue;
if (value === 'false') { value = false; }
scriptConfig[RegExp.$1] = value;
}
}
})();
helpers = {};
// Build the singleton factory. The factory can be accessed through
// singleton.factory() to instantiate a new instance.
var factory = function(){
var f = builder();
var singleton = f(scriptConfig);
singleton.factory = f;
return singleton;
};
helpers.String = function(obj, fallback) {
if ((obj == null) && (fallback != null)) {
return String(fallback);
// UMD (Universal Module Definition)
// See https://github.com/umdjs/umd
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], factory);
} else if (typeof module === 'object' && module.exports) {
// Browserfy. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Browserfy/Node.
module.exports = factory();
} else {
// Browser globals (root is window).
root.Honeybadger = factory();
}
if (obj == null) {
return null;
}(this, function () {
var VERSION = '0.4.0',
NOTIFIER = {
name: 'honeybadger.js',
url: 'https://github.com/honeybadger-io/honeybadger-js',
version: VERSION,
language: 'javascript'
};
// Used to control initial setup across clients.
var loaded = false,
installed = false;
// Used to prevent reporting duplicate errors across instances.
var currentErr,
currentPayload;
// Utilities.
function merge(obj1, obj2) {
var obj3 = {};
for (k in obj1) { obj3[k] = obj1[k]; }
for (k in obj2) { obj3[k] = obj2[k]; }
return obj3;
}
return String(obj);
};
// Generated by CoffeeScript 1.9.3
var Configuration;
Configuration = (function() {
Configuration.defaults = {
api_key: null,
host: 'api.honeybadger.io',
ssl: true,
project_root: window.location.protocol + '//' + window.location.host,
environment: 'production',
component: null,
action: null,
disabled: false,
onerror: false,
debug: false,
timeout: false
};
function currentErrIs(err) {
if (!currentErr) { return false; }
if (currentErr.name !== err.name) { return false; }
if (currentErr.message !== err.message) { return false; }
if (currentErr.stack !== err.stack) { return false; }
return true;
}
function Configuration(options) {
var k, ref, v;
if (options == null) {
options = {};
function cgiData() {
var data = {};
data['HTTP_USER_AGENT'] = navigator.userAgent;
if (document.referrer.match(/\S/)) {
data['HTTP_REFERER'] = document.referrer;
}
ref = this.constructor.defaults;
for (k in ref) {
v = ref[k];
this[k] = v;
return data;
}
function stackTrace(err) {
// From TraceKit: Opera 10 *destroys* its stacktrace property if you try to
// access the stack property first.
return err.stacktrace || err.stack || undefined
}
function generateStackTrace(err) {
var stack;
var maxStackSize = 10;
if (err && (stack = stackTrace(err))) {
return {stack: stack, generator: undefined};
}
for (k in options) {
v = options[k];
this[k] = v;
try {
throw new Error('');
} catch(e) {
if (stack = stackTrace(e)) {
return {stack: stack, generator: 'throw'};
}
}
stack = ['<call-stack>'];
var curr = arguments.callee;
while (curr && stack.length < maxStackSize) {
if (/function(?:\s+([\w$]+))+\s*\(/.test(curr.toString())) {
stack.push(RegExp.$1 || '<anonymous>');
} else {
stack.push('<anonymous>');
}
try {
curr = curr.caller;
} catch (e) {
break;
}
}
return {stack: stack.join('\n'), generator: 'walk'};
}
Configuration.prototype.reset = function() {
var k, ref, v;
ref = this.constructor.defaults;
for (k in ref) {
v = ref[k];
this[k] = v;
function checkHandlers(handlers, err) {
var handler, i, len;
for (i = 0, len = handlers.length; i < len; i++) {
handler = handlers[i];
if (handler(err) === false) {
return true;
}
}
return this;
};
return false;
}
return Configuration;
function request(url) {
// Use XHR when available.
try {
// Inspired by https://gist.github.com/Xeoncross/7663273
x = new(this.XMLHttpRequest || ActiveXObject)('MSXML2.XMLHTTP.3.0');
x.open('GET', url, true);
x.send();
return;
} catch(e) {
log('Error encountered during XHR request (will retry): ' + e);
}
})();
// Generated by CoffeeScript 1.9.3
var Notice,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
// Fall back to Image transport.
img = new Image();
img.src = url;
}
Notice = (function() {
function Notice(opts, config) {
var k, ref, ref1, v;
if (opts == null) {
opts = {};
// Client factory.
var factory = (function(opts) {
var defaultProps = [];
var queue = [];
var self = {
context: {},
beforeNotifyHandlers: []
}
if (config == null) {
config = Honeybadger.configuration;
if (opts instanceof Object) {
for (k in opts) { self[k] = opts[k]; }
}
this._sanitize = bind(this._sanitize, this);
this.log = opts.log || function() {};
this.stack = helpers.String(opts.stack);
this.generator = helpers.String(opts.generator);
this["class"] = helpers.String(opts.name, 'Error');
this.message = helpers.String(opts.message, 'No message provided');
this.source = null;
this.url = helpers.String(document.URL);
this.project_root = helpers.String(config.project_root);
this.environment = helpers.String(config.environment);
this.component = helpers.String(opts.component || config.component);
this.action = helpers.String(opts.action || config.action);
this.cgi_data = this._cgiData();
this.fingerprint = helpers.String(opts.fingerprint);
this.context = {};
ref = Honeybadger.context;
for (k in ref) {
v = ref[k];
this.context[k] = v;
}
if (opts.context && typeof opts.context === 'object') {
ref1 = opts.context;
for (k in ref1) {
v = ref1[k];
this.context[k] = v;
function log(msg){
if (config('debug') && this.console) {
console.log( msg );
}
}
}
Notice.prototype.payload = function() {
return this._sanitize({
notifier: {
name: 'honeybadger.js',
url: 'https://github.com/honeybadger-io/honeybadger-js',
version: Honeybadger.version,
language: 'javascript'
},
error: {
"class": this["class"],
message: this.message,
backtrace: this.stack,
generator: this.generator,
source: this.source,
fingerprint: this.fingerprint
},
request: {
url: this.url,
component: this.component,
action: this.action,
context: this.context,
cgi_data: this.cgi_data
},
server: {
project_root: this.project_root,
environment_name: this.environment
function config(key, fallback) {
var value;
if (self[key] !== undefined) {
value = self[key];
}
});
};
if (value === 'false') { value = false; }
if (value !== undefined) { return value; }
return fallback;
}
Notice.prototype._cgiData = function() {
var data, k, v;
data = {};
if (typeof navigator !== "undefined" && navigator !== null) {
for (k in navigator) {
v = navigator[k];
if ((k != null) && (v != null) && !(typeof v === 'object')) {
data[k.replace(/(?=[A-Z][a-z]*)/g, '_').toUpperCase()] = v;
}
}
data['HTTP_USER_AGENT'] = data['USER_AGENT'];
delete data['USER_AGENT'];
function baseURL() {
return 'http' + ((config('ssl', true) && 's') || '') + '://' + config('host', 'api.honeybadger.io');
}
if (document.referrer.match(/\S/)) {
data['HTTP_REFERER'] = document.referrer;
}
return data;
};
Notice.prototype._sanitize = function(obj, seen) {
var e, k, new_obj, v;
if (seen == null) {
seen = [];
}
if (obj instanceof Function) {
return "[FUNC]";
} else if (obj instanceof Object) {
if (indexOf.call(seen, obj) >= 0) {
this.log("Dropping circular data structure.", k, v, obj);
return "[CIRCULAR DATA STRUCTURE]";
function serialize(obj, prefix, depth) {
var k, pk, ret, v;
ret = [];
if (!depth) { depth = 0; }
if (depth >= config('max_depth', 8)) {
return encodeURIComponent(prefix) + '=[MAX DEPTH REACHED]';
}
seen.push(obj);
if (obj instanceof Array) {
return obj.map((function(_this) {
return function(v) {
return _this._sanitize(v, seen);
};
})(this));
} else {
new_obj = {};
try {
for (k in obj) {
v = obj[k];
new_obj[k] = this._sanitize(v, seen);
}
} catch (_error) {
e = _error;
return {
error: "Honeybadger was unable to read this object: " + String(e)
};
for (k in obj) {
v = obj[k];
if (v instanceof Function) { v = '[FUNC]' }
if (obj.hasOwnProperty(k) && (k != null) && (v != null)) {
pk = (prefix ? prefix + '[' + k + ']' : k);
ret.push(typeof v === 'object' ? serialize(v, pk, depth+1) : encodeURIComponent(pk) + '=' + encodeURIComponent(v));
}
return new_obj;
}
return ret.join('&');
}
return obj;
};
return Notice;
function send(payload) {
currentErr = currentPayload = null;
})();
// Generated by CoffeeScript 1.9.3
var Client, Honeybadger, UncaughtError, currentError, currentNotice, ref,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
hasProp = {}.hasOwnProperty,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
if (!config('api_key')) {
log('Unable to send error report: no API key has been configured.');
return false;
}
ref = [null, null], currentError = ref[0], currentNotice = ref[1];
url = baseURL() + '/v1/notices/js.gif?' + serialize({notice: payload}) +
'&api_key=' + config('api_key') + '&t=' + new Date().getTime();
Client = (function() {
Client.prototype.version = '0.3.1';
request(url);
function Client(options) {
this._windowOnErrorHandler = bind(this._windowOnErrorHandler, this);
this._domReady = bind(this._domReady, this);
this.log = bind(this.log, this);
this.log('Initializing honeybadger.js ' + this.version);
if (options) {
this.configure(options);
return true;
}
}
Client.prototype.log = function() {
this.log.history = this.log.history || [];
this.log.history.push(arguments);
if (this.configuration.debug && window.console) {
return console.log(Array.prototype.slice.call(arguments));
}
};
function notify(err, generated) {
if (config('disabled', false)) { return false; }
if (!(err instanceof Object)) { return false; }
Client.prototype.configure = function(options) {
var args, i, k, len, ref1, v;
if (options == null) {
options = {};
}
for (k in options) {
v = options[k];
this.configuration[k] = v;
}
if (!this._configured && this.configuration.debug && window.console) {
ref1 = this.log.history;
for (i = 0, len = ref1.length; i < len; i++) {
args = ref1[i];
console.log(Array.prototype.slice.call(args));
if (err instanceof Error) {
var e = err;
err = {name: e.name, message: e.message, stack: stackTrace(e)};
}
}
this._configured = true;
return this;
};
Client.prototype.configuration = new Configuration();
if (currentErrIs(err)) {
// Skip the duplicate error.
return false;
} else if (currentPayload && loaded) {
// This is a different error, send the old one now.
send(currentPayload);
}
Client.prototype.context = {};
// Halt if err is empty.
if (((function() {
var k, results;
results = [];
for (k in err) {
if (!{}.hasOwnProperty.call(err, k)) continue;
results.push(k);
}
return results;
})()).length === 0) {
return false;
}
Client.prototype.resetContext = function(options) {
if (options == null) {
options = {};
}
this.context = options instanceof Object ? options : {};
return this;
};
if (generated) {
err = merge(err, generated);
}
Client.prototype.setContext = function(options) {
var k, v;
if (options == null) {
options = {};
}
if (options instanceof Object) {
for (k in options) {
v = options[k];
this.context[k] = v;
if (checkHandlers(self.beforeNotifyHandlers, err)) {
return false;
}
}
return this;
};
Client.prototype.beforeNotifyHandlers = [];
var payload = {
notifier: NOTIFIER,
error: {
'class': err.name || 'Error',
message: err.message,
backtrace: err.stack,
generator: err.generator,
fingerprint: err.fingerprint
},
request: {
url: err.url || document.URL,
component: err.component || config('component'),
action: err.action || config('action'),
context: merge(self.context, err.context),
cgi_data: cgiData()
},
server: {
project_root: err.project_root || config('project_root', window.location.protocol + '//' + window.location.host),
environment_name: err.environment || config('environment')
}
};
Client.prototype.beforeNotify = function(handler) {
return this.beforeNotifyHandlers.push(handler);
};
currentPayload = payload;
currentErr = err;
Client.prototype.notify = function(error, name, opts) {
var generator, k, notice, ref1, ref2, ref3, ref4, stack, v;
if (opts == null) {
opts = {};
}
if (!this._validConfig() || this.configuration.disabled === true) {
return false;
}
ref1 = [void 0, void 0], stack = ref1[0], generator = ref1[1];
if (name instanceof Object) {
opts = name;
name = void 0;
} else if (name != null) {
opts['name'] = name;
}
if (error instanceof Object && (error.error != null)) {
error = error.error;
error['error'] = void 0;
}
if (error instanceof Error) {
stack = this._stackTrace(error);
opts['name'] || (opts['name'] = error.name);
opts['message'] || (opts['message'] = error.message);
} else if (typeof error === 'string') {
opts['message'] = error;
} else if (error instanceof Object) {
for (k in error) {
v = error[k];
opts[k] = v;
if (loaded) {
log('Defering notice.', err, payload);
window.setTimeout(function(){
if (currentErrIs(err)) {
send(payload);
}
});
} else {
log('Queuing notice.', err, payload);
queue.push(payload);
}
return err;
}
if (currentNotice) {
if (error === currentError) {
return;
} else if (this._loaded) {
this._send(currentNotice);
}
var preferCatch = true;
// IE < 10
if (!window.atob) { preferCatch = false; }
// See https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent
if (window.ErrorEvent) {
try {
if ((new window.ErrorEvent('')).colno === 0) {
preferCatch = false;
}
} catch(_e) {}
}
if (((function() {
var results;
results = [];
for (k in opts) {
if (!hasProp.call(opts, k)) continue;
results.push(k);
// wrap always returns the same function so that callbacks can be removed via
// removeEventListener.
function wrap(fn, force) {
try {
if (typeof fn !== 'function') {
return fn;
}
if (!fn.___hb) {
fn.___hb = function() {
var onerror = config('onerror', true);
// Don't catch if the browser is old or supports the new error
// object and there is a window.onerror handler available instead.
if ((preferCatch && (onerror || force)) || (force && !onerror)) {
try {
return fn.apply(this, arguments);
} catch (e) {
notify(e);
throw(e);
}
} else {
return fn.apply(this, arguments);
}
};
}
return fn.___hb;
} catch(_e) {
return fn;
}
return results;
})()).length === 0) {
return false;
}
if (!stack) {
ref2 = this._generateStackTrace(), stack = ref2[0], generator = ref2[1];
}
ref3 = [stack, generator], opts['stack'] = ref3[0], opts['generator'] = ref3[1];
notice = this._buildNotice(opts);
if (this._checkHandlers(this.beforeNotifyHandlers, notice)) {
return false;
}
ref4 = [error, notice], currentError = ref4[0], currentNotice = ref4[1];
if (!this._loaded) {
this.log('Queuing notice', notice);
this._queue.push(notice);
} else {
this.log('Defering notice', notice);
window.setTimeout((function(_this) {
return function() {
if (error === currentError) {
return _this._send(notice);
}
};
})(this));
}
return notice;
};
Client.prototype.wrap = function(func) {
var honeybadgerWrapper;
return honeybadgerWrapper = function() {
var e;
try {
return func.apply(this, arguments);
} catch (_error) {
e = _error;
Honeybadger.notify(e);
throw e;
// Public API.
self.notify = function(err, name, extra) {
if (!err) { err = {}; }
if (err instanceof Error) {
var e = err;
err = {name: e.name, message: e.message, stack: stackTrace(e)};
}
};
};
Client.prototype.reset = function() {
this.resetContext();
this.configuration.reset();
this._configured = false;
return this;
};
if (!(err instanceof Object)) {
var m = String(err);
err = {message: m};
}
Client.prototype.install = function() {
if (this.installed === true) {
return;
}
if (window.onerror !== this._windowOnErrorHandler) {
this.log('Installing window.onerror handler');
this._oldOnErrorHandler = window.onerror;
window.onerror = this._windowOnErrorHandler;
}
if (this._loaded) {
this.log('honeybadger.js ' + this.version + ' ready');
} else {
this.log('Installing ready handler');
if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', this._domReady, true);
window.addEventListener('load', this._domReady, true);
} else {
window.attachEvent('onload', this._domReady);
if (name && !(name instanceof Object)) {
var n = String(name);
name = {name: n};
}
}
this._installed = true;
return this;
};
Client.prototype._queue = [];
if (name) {
err = merge(err, name);
}
if (extra instanceof Object) {
err = merge(err, extra);
}
Client.prototype._loaded = document.readyState === 'complete';
return notify(err, generateStackTrace(err));
};
Client.prototype._configured = false;
self.wrap = function(func) {
return wrap(func, true);
};
Client.prototype._domReady = function() {
var notice, results;
if (this._loaded) {
return;
}
this._loaded = true;
this.log('honeybadger.js ' + this.version + ' ready');
results = [];
while (notice = this._queue.pop()) {
results.push(this._send(notice));
}
return results;
};
self.setContext = function(context) {
if (context instanceof Object) {
self.context = merge(self.context, context);
}
return self;
};
Client.prototype._generateStackTrace = function() {
var e, stack;
try {
throw new Error('');
} catch (_error) {
e = _error;
if (stack = this._stackTrace(e)) {
return [stack, 'throw'];
self.resetContext = function(context) {
if (context instanceof Object) {
self.context = merge({}, context);
} else {
self.context = {};
}
}
return [];
};
return self;
};
Client.prototype._stackTrace = function(error) {
return (error != null ? error.stacktrace : void 0) || (error != null ? error.stack : void 0) || null;
};
Client.prototype._checkHandlers = function(handlers, notice) {
var handler, i, len;
for (i = 0, len = handlers.length; i < len; i++) {
handler = handlers[i];
if (handler(notice) === false) {
return true;
self.configure = function(opts) {
for (k in opts) {
self[k] = opts[k];
}
}
return false;
};
return self;
};
Client.prototype._buildNotice = function(opts) {
return new Notice({
log: this.log,
stack: opts['stack'],
generator: opts['generator'],
message: opts['message'],
name: opts['name'],
fingerprint: opts['fingerprint'],
context: opts['context'],
component: opts['component'],
action: opts['action']
}, this.configuration);
};
self.beforeNotify = function(handler) {
self.beforeNotifyHandlers.push(handler);
return self;
};
Client.prototype._send = function(notice) {
var ref1;
this.log('Sending notice', notice);
ref1 = [null, null], currentError = ref1[0], currentNotice = ref1[1];
return this._sendRequest(notice.payload());
};
var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
self.reset = function() {
self.context = {};
self.beforeNotifyHandlers = [];
for (k in self) {
if (indexOf.call(defaultProps, k) == -1) {
self[k] = undefined;
}
}
return self;
};
Client.prototype._validConfig = function() {
var ref1;
if (!this._configured) {
return false;
self.getVersion = function() {
return VERSION;
}
if ((ref1 = this.configuration.api_key) != null ? ref1.match(/\S/) : void 0) {
return true;
} else {
return false;
// Install instrumentation.
// This should happen once for the first factory call.
function instrument(object, name, replacement) {
if (installed) { return; }
if (!object || !name || !replacement) { return; }
var original = object[name];
object[name] = replacement(original);
}
};
Client.prototype._baseURL = function() {
return 'http' + ((this.configuration.ssl && 's') || '') + '://' + this.configuration.host;
};
var instrumentTimer = function(original) {
// See https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout
return function(func, delay) {
if (func instanceof Function) {
var args = Array.prototype.slice.call(arguments, 2);
func = wrap(func);
return original(function() {
func.apply(null, args);
}, delay);
} else {
return original(func, delay);
}
}
};
instrument(window, 'setTimeout', instrumentTimer);
instrument(window, 'setInterval', instrumentTimer);
Client.prototype._sendRequest = function(data) {
return this._xhrRequest(data) || this._imageRequest(data);
};
// Event targets borrowed from bugsnag-js:
// See https://github.com/bugsnag/bugsnag-js/blob/d55af916a4d3c7757f979d887f9533fe1a04cc93/src/bugsnag.js#L542
'EventTarget Window Node ApplicationCache AudioTrackList ChannelMergerNode CryptoOperation EventSource FileReader HTMLUnknownElement IDBDatabase IDBRequest IDBTransaction KeyOperation MediaController MessagePort ModalWindow Notification SVGElementInstance Screen TextTrack TextTrackCue TextTrackList WebSocket WebSocketWorker Worker XMLHttpRequest XMLHttpRequestEventTarget XMLHttpRequestUpload'.replace(/\w+/g, function (prop) {
if (prototype = window[prop] && window[prop].prototype) {
instrument(prototype, 'addEventListener', function(original) {
// See https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
return function(type, listener, useCapture, wantsUntrusted) {
try {
if (listener && listener.handleEvent != null) {
listener.handleEvent = wrap(listener.handleEvent);
}
} catch(e) {
// Ignore 'Permission denied to access property "handleEvent"' errors.
log(e);
}
return original.call(this, type, wrap(listener), useCapture, wantsUntrusted);
};
});
instrument(prototype, 'removeEventListener', function(original) {
return function(type, listener, useCapture, wantsUntrusted) {
original.call(this, type, listener, useCapture, wantsUntrusted);
return original.call(this, type, wrap(listener), useCapture, wantsUntrusted);
};
});
}
});
Client.prototype._imageRequest = function(data) {
var endpoint, img, ref1, timeout, url;
endpoint = this._baseURL() + '/v1/notices/js.gif';
url = endpoint + '?' + this._serialize({
api_key: this.configuration.api_key,
notice: data,
t: new Date().getTime()
});
ref1 = [new Image(), null], img = ref1[0], timeout = ref1[1];
img.onabort = img.onerror = (function(_this) {
return function() {
if (timeout) {
window.clearTimeout(timeout);
instrument(window, 'onerror', function(original) {
function onerror(msg, url, line, col, err) {
if (currentErr) { return; }
if (!config('onerror', true)) { return; }
if (line === 0 && /Script error\.?/.test(msg)) {
// See https://developer.mozilla.org/en/docs/Web/API/GlobalEventHandlers/onerror#Notes
log('Ignoring cross-domain script error. Use CORS to enable tracking of these types of errors.');
return;
}
return _this.log('Request failed.', url, data);
};
})(this);
img.onload = (function(_this) {
return function() {
if (timeout) {
return window.clearTimeout(timeout);
log('Error caught by window.onerror');
if (err) {
notify(err);
return;
}
// simulate v8 stack
stack = [msg, '\n at ? (', url || 'unknown', ':', line || 0, ':', col || 0, ')'].join('');
notify({
name: 'window.onerror',
message: msg,
stack: stack
});
}
// See https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror
return function(msg, url, line, col, err) {
onerror(msg, url, line, col, err);
if (original instanceof Function) {
return original.apply(this, arguments);
}
return false;
};
})(this);
img.src = url;
if (this.configuration.timeout) {
timeout = window.setTimeout(((function(_this) {
return function() {
img.src = '';
return _this.log('Request timed out.', url, data);
};
})(this)), this.configuration.timeout);
}
return true;
};
});
Client.prototype._xhrRequest = function(data) {
var method, url, xhr;
if (typeof XMLHttpRequest === 'undefined') {
return false;
}
if (typeof JSON === 'undefined') {
return false;
}
method = 'POST';
url = this._baseURL() + '/v1/notices/js?api_key=' + this.configuration.api_key;
xhr = new XMLHttpRequest();
if ('withCredentials' in xhr) {
xhr.open(method, url, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('X-Api-Key', this.configuration.api_key);
} else if (typeof XDomainRequest !== 'undefined') {
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
xhr = null;
}
if (xhr) {
if (this.configuration.timeout) {
xhr.timeout = this.configuration.timeout;
}
xhr.onerror = (function(_this) {
return function() {
return _this.log('Request failed.', data, xhr);
};
})(this);
xhr.ontimeout = (function(_this) {
return function() {
return _this.log('Request timed out.', data, xhr);
};
})(this);
xhr.onreadystatechange = (function(_this) {
return function() {
if (xhr.readyState === 4) {
if (xhr.status === 201) {
return _this.log('Request succeeded.', xhr.status, data, xhr);
} else {
return _this.log('Request rejected by server.', xhr.status, data, xhr);
}
}
};
})(this);
xhr.send(JSON.stringify(data));
return true;
}
return false;
};
// End of instrumentation.
installed = true;
Client.prototype._serialize = function(obj, prefix) {
var k, pk, ret, v;
ret = [];
for (k in obj) {
v = obj[k];
if (obj.hasOwnProperty(k) && (k != null) && (v != null)) {
pk = (prefix ? prefix + '[' + k + ']' : k);
ret.push(typeof v === 'object' ? this._serialize(v, pk) : encodeURIComponent(pk) + '=' + encodeURIComponent(v));
}
// Save original state for reset()
for (k in self) {
defaultProps.push(k);
}
return ret.join('&');
};
Client.prototype._windowOnErrorHandler = function(msg, url, line, col, error) {
if (!currentNotice && this.configuration.onerror) {
this.log('Error caught by window.onerror', msg, url, line, col, error);
if (!error) {
error = new UncaughtError(msg, url, line, col);
// Initialization.
log('Initializing honeybadger.js ' + VERSION);
if (document.readyState === 'complete') {
loaded = true;
log('honeybadger.js ' + VERSION + ' ready');
} else {
log('Installing ready handler');
var domReady = function() {
loaded = true;
log('honeybadger.js ' + VERSION + ' ready');
while (notice = queue.pop()) {
send(notice);
}
};
if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', domReady, true);
} else {
window.attachEvent('onload', domReady);
}
this.notify(error);
}
if (this._oldOnErrorHandler) {
return this._oldOnErrorHandler.apply(this, arguments);
}
return false;
};
return Client;
return self;
});
})();
UncaughtError = (function(superClass) {
extend(UncaughtError, superClass);
function UncaughtError(message, url, line, column) {
this.name = 'window.onerror';
this.message = message || 'An unknown error was caught by window.onerror.';
this.stack = [this.message, '\n at ? (', url || 'unknown', ':', line || 0, ':', column || 0, ')'].join('');
}
return UncaughtError;
})(Error);
Honeybadger = new Client;
Honeybadger.Client = Client;
window.Honeybadger = Honeybadger;
Honeybadger.install();
})(window);
return factory;
}));
{
"name": "honeybadger-js",
"description": "A JavaScript library for integrating apps with the Honeybadger Rails Error Notifier.",
"version": "0.3.1",
"version": "0.4.0",
"homepage": "https://github.com/honeybadger-io/honeybadger-js",

@@ -10,2 +10,3 @@ "author": {

},
"main": "honeybadger.js",
"repository": {

@@ -22,4 +23,9 @@ "type": "git",

"devDependencies": {
"coffee-script": "~1.3.3",
"connect": "~2.7.11",
"grunt": "~0.4.5",
"grunt-contrib-connect": "~0.1.2",
"grunt-contrib-jasmine": "~0.5.2",
"grunt-contrib-watch": "~0.2.0",
"grunt-saucelabs": "~8.3.2",
"grunt-shell": "~1.1.2",
"phantomjs": "~1.9.0"

@@ -26,0 +32,0 @@ },

@@ -1,2 +0,2 @@

# Honeybadger Client Side Javascript Library
# Honeybadger Client-Side Javascript Library

@@ -6,19 +6,19 @@ [![Build

A JavaScript library for integrating apps with the :zap: [Honeybadger Rails Error Notifier](http://honeybadger.io).
A client-side JavaScript library for integrating apps with the :zap: [Honeybadger Error Notifier](http://honeybadger.io). For server-side javascript, check out our [NodeJS library](https://github.com/honeybadger-io/honeybadger-node).
## Upgrading
## Getting Started
It is recommended that you use our CDN, as outlined under
[installation](#installation). The API is 100% backwards compatible, so no other
code changes are required.
### 1. Include the JS library
*Note: 0.1 and 0.2 make significant improvements to error grouping. As a result,
new errors may be grouped differently than old.*
#### Globally
## Installation
Place the following code between the `<head></head>` tags of your page:
```html
<script src="//js.honeybadger.io/v0.3/honeybadger.min.js" type="text/javascript"></script>
<script src="//js.honeybadger.io/v0.4/honeybadger.min.js" type="text/javascript" data-api_key="project api key" data-environment="production"></script>
```
Honeybadger may also be configured via JavaScript:
```javascript
<script type="text/javascript">

@@ -32,33 +32,188 @@ Honeybadger.configure({

## Basic Usage
#### Installing via Node.js
To catch an error and notify Honeybadger:
```
npm install honeybadger-js --save-dev
```
#### Installing via Bower
```sh
bower install honeybadger --save-dev
```
#### Browserify/Webpack (CommonJS)
```sh
var Honeybadger = require("path/to/honeybadger");
Honeybadger.configure({
api_key: 'project api key',
environment: 'production'
});
```
- See an [example browserify + honeybadger.js project](examples/browserify).
- See an [example webpack + honeybadger.js project](examples/webpack).
#### RequireJS (AMD)
```sh
requirejs(["path/to/honeybadger"], function(Honeybadger) {
Honeybadger.configure({
api_key: 'project api key',
environment: 'production'
});
});
```
- See an [example requirejs + honeybadger.js project](examples/requirejs).
### 2. Start reporting exceptions
By default Honeybadger will report all uncaught exceptions automatically using our `window.onerror` handler.
You can also manually notify Honeybadger of errors and other events in your application code:
```javascript
try {
// ...error producing code...
} catch(e) {
Honeybadger.notify(e);
} catch(error) {
Honeybadger.notify(error);
}
```
JavaScript often uses generic class names -- such as `Error` -- which are uninformative and also cause unrelated errors to be grouped together. To get around this issue it's a good practice to send a custom error class when notifying Honeybadger:
See the [full documentation](#honeybadgernotify-send-an-exception-to-honeybadger) for the `notify` method for more examples.
## Advanced Configuration
You can set configuration options by using the `Honeybadger.configure` function. All of the available options are shown below:
```javascript
Honeybadger.configure({
// Honeybadger API key (required)
api_key: '',
// Collector Host
host: 'api.honeybadger.io',
// Use SSL?
ssl: true,
// Project root
project_root: 'http://my-app.com',
// Environment
environment: 'production',
// Component (optional)
component: '',
// Action (optional)
action: '',
// Should unhandled (window.onerror) notifications be sent?
onerror: true,
// Disable notifications?
disabled: false
});
```
You can call `Honeybadger.configure` as many times as you like. The existing configuration data will be merged with any new data you provide. This is especially useful for changing the `action` and `component` values inside of single-page apps.
### Configuring via data attributes
The global Honeybadger instance may also be configured via data attributes on the script tag which loads honeybadger.js:
```html
<script src="honeybadger.js" type="text/javascript" data-component="pages" data-action="index" ...></script>
```
## Public Interface
### `Honeybadger.notify()`: Send an exception to Honeybadger
If you've caught an exception and want to send it to Honeybadger, this is the method for you.
#### Examples:
```javascript
try {
// ...error producing code...
} catch(e) {
Honeybadger.notify(e, 'DescriptiveClass');
} catch(error) {
Honeybadger.notify(error);
}
```
## Sending Custom Data
JavaScript often uses generic class names -- such as `Error` -- which are uninformative and also cause unrelated errors to be grouped together. To get around this issue it's a good practice to send a custom error class when notifying Honeybadger:
Honeybadger allows you to send custom data using
`Honeybadger.setContext` And `Honeybadger.resetContext`:
```javascript
Honeybadger.notify(error, 'DescriptiveClass');
```
You can also set or override other optional data which is reported with the error:
```javascript
Honeybadger.notify(error, {
message: 'My custom message',
name: 'DescriptiveClass',
component: 'badgers',
action: 'show',
context: { badgerId: 1 },
fingerprint: 'This unique string will group similar errors together',
environment: 'production',
project_root: 'https://www.example.com/'
});
```
Finally, you can notify Honeybadger of anything, even if you don't have an error object:
```javascript
Honeybadger.notify('Badgers!');
Honeybadger.notify('Badgers!', { ... });
Honeybadger.notify('Badgers!', 'CustomClass');
Honeybadger.notify('Badgers!', 'CustomClass', { ... });
Honeybadger.notify({
message: 'Badgers!',
name: 'CustomClass',
...
});
```
A stacktrace will be generated for you (when possible) if you do not provide an error object.
---
### `Honeybadger.wrap()`: Wrap the given function in try/catch and report any exceptions
It can be a pain to include try/catch blocks everywhere in your app. A slightly nicer option is to use `Honeybadger.wrap`. You pass it a function. It returns a new function which wraps your existing function in a try/catch block.
#### Examples:
```javascript
Honeybadger.wrap(function(){
throw "oops";
})();
```
Note that `wrap` returns a function. This makes it easy to use with event handlers, as in the example below:
```javascript
$(document).on("click", "#myElement", Honeybadger.wrap(function(){ throw "oops"; }));
```
---
### `Honeybadger.setContext()`: Set metadata to be sent if an exception occurs
Javascript exceptions are pretty bare-bones. You probably have some additional data that could make them a lot easier to understand - perhaps the name of the current Angular view, or the id of the current user. This function lets you set context data that will be sent if an error should occur.
You can call `setContext` as many times as you like. New context data will be merged with the existing data.
#### Examples:
```javascript
// On load
Honeybadger.setContext({
user_id: '<%= current_user.id %>'
user_id: 123
});

@@ -71,31 +226,26 @@

// Honeybadger.context => { user_id: 1, backbone_view: 'tracks' }
// The context now contains { user_id: 123, backbone_view: 'tracks' }
```
---
Honeybadger.resetContext({
some_other_data: 'foo'
});
### `Honeybadger.resetContext()`: Clear context metadata
// Honeybadger.context == { some_other_data: 'foo' }
```
If you've used `Honeybadger.setContext` to store context data, you can clear it with `Honeybadger.resetContext`.
You can also add context to a specific exception by passing an
associative array to the `notify` method. Global context will be
merged locally:
#### Example:
```javascript
Honeybadger.setContext({
user_id: '<%= current_user.id %>'
// Set the context to {}
Honeybadger.resetContext();
// Clear the context, then set it to `{ user_id: 123 }`
Honeybadger.resetContext({
user_id: 123
});
```
try {
// ...error producing code...
} catch(e) {
Honeybadger.notify(e, { context: { some_other_data: 'foo' } });
}
---
// Honeybadger.context == { user_id: 1 }
```
### `Honeybadger.beforeNotify()`: Add a callback to be run before an exception is reported
## Notification handlers
Passing a function to `Honeybadger.beforeNotify` will add the function

@@ -106,2 +256,5 @@ to a list of before notify handlers. If the function includes a

#### Examples
```javascript

@@ -111,7 +264,5 @@ Honeybadger.beforeNotify(function(notice) {

});
```
To halt notification, return false from any `beforeNotify` handler:
```javascript
// To halt notification, return false from any `beforeNotify` handler:
Honeybadger.beforeNotify(function(notice) {

@@ -122,8 +273,6 @@ if (notice.class == 'MyCustomError') return false;

### Notice Attributes
The following notice attributes may be modified by your notification handlers:
* stack - The stack trace
* class - The exception class name
* name - The exception class name
* message - The error message

@@ -138,2 +287,31 @@ * url - The current url

---
### `Honeybadger.configure()`: Set configuration values
The `configure` method takes an object containing config values. Its return value is unspecified.
#### Examples:
```javascript
Honeybadger.configure({api_key: "adlkjfljk"});
```
---
### `Honeybadger.factory()`: create a new client instance.
The `factory` method returns a new instance of Honeybadger which can be configured differently than the global/singleton instance.
#### Examples:
```javascript
var other_hb = Honeybadger.factory({api_key: "zxcvbnm"});
other_hb.notify("This will go to an alternate project.");
```
## Sourcemaps

@@ -143,13 +321,23 @@

In order to make this work you must include a special comment at the bottom of your minified file which points to the corresponding sourcemap file. To see what this comment should look like, [see the comment at the bottom of honeybadger.js](https://js.honeybadger.io/v0.3/honeybadger.min.js). If you upload the original source files along with the sourcemap, Honeybadger will link to those files when displaying the stack trace.
To do this, you'll add a special comment at the bottom of your minified JS. It tells us where to find your sourcemap. For example:
All files must be publically accessible online so that Honeybadger's servers can download and parse them. For the full specification, see the [Source Map Revision 3 Proposal](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit).
```js
// ...minified code...
//# sourceMappingURL=application.min.js.map
```
The sourcemap URL needs to be a valid URL accessible to the public.
For more information on sourcemaps, check out the [Source Map Revision 3 Proposal](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit).
### Linking stack traces to source files
Honeybadger supports an optional `sourceRoot` comment, which should point to the root path of the original source files online. For example:
If you'd like to be able to jump from the Honeybadger backtrace to your unminified source file, just tell us where to find your unminified files using the `sourceRoot` option.
`sourceRoot` is the root URL for your unminified source files. To set it, you can use another magic comment:
```js
// ...minified code...
//# sourceMappingURL=application.min.js.map
//# sourceRoot=https://sources.my-domain.com/src

@@ -167,7 +355,7 @@ ```

To customize this setting *just* for Honeybadger, use `honeybadgerSourceRoot` instead.
If providing the `sourceRoot` option fouls up other tools in your toolchain, you can alternatively use `honeybadgerSourceRoot`.
#### Linking stack traces to GitHub
#### Using GitHub
If you're using the GitHub integration, you can also link to source files on GitHub by substituting a special `[PROJECT_ROOT]` token for the root of your GitHub repository:
If you're using Honeybadger's GitHub integration, you can link to source files on GitHub by substituting a special `[PROJECT_ROOT]` token for the root of your GitHub repository:

@@ -182,10 +370,7 @@ ```js

## Unhandled errors via (window.onerror)
## window.onerror
By default, honeybadger.js does not track unhandled errors. This is
because `window.onerror` is a very limited method of error handling, and
does not usually yield useful information. It is our official
recommendation to always use try/catch explicitly to notify Honeybadger.
If you still want to automatically catch errors via `window.onerror`,
you can set the `onerror` configuration option to true:
Honeybadger.js automatically reports uncaught exceptions from window.onerror. To
disable notifications for uncaught exceptions, set the `onerror` option to
`false`.

@@ -195,48 +380,7 @@ ```javascript

api_key: 'project api key',
onerror: true
onerror: false
});
```
## Configuration
`Honeybadger.configure` may be called multiple times to set/update
configuration options. Existing configuration will be merged. In most
cases configuration will be set once, however the `action` and
`component` options may change semi-frequently for client-side
frameworks like Angular or Ember.
```javascript
Honeybadger.configure({
// Honeybadger API key (required)
api_key: '',
// Collector Host
host: 'api.honeybadger.io',
// Use SSL?
ssl: true,
// Project root
project_root: 'http://my-app.com',
// Environment
environment: 'production',
// Component (optional)
component: '',
// Action (optional)
action: '',
// Should unhandled (window.onerror) notifications be sent?
onerror: false,
// Disable notifications?
disabled: false,
// Timeout (in milliseconds) when making requests.
timeout: false
});
```
## Contributing

@@ -252,7 +396,7 @@

To run the test suite, enter `make test` into the console.
To run the test suite, enter `make test` into the console.
### License
The Honeybadger gem is MIT licensed. See the [MIT-LICENSE](https://raw.github.com/honeybadger-io/honeybadger-js/master/MIT-LICENSE) file in this repository for details.
The Honeybadger gem is MIT licensed. See the [MIT-LICENSE](https://raw.github.com/honeybadger-io/honeybadger-js/master/MIT-LICENSE) file in this repository for details.

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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc