Comparing version 0.0.11 to 0.1.0
@@ -28,3 +28,8 @@ // Copyright 2006-2008 the V8 project authors. All rights reserved. | ||
module.exports = FormatStackTrace; | ||
function FormatStackTrace(error, frames) { | ||
var path = require('path'), | ||
d = path.join('/'), | ||
node_modules = d + 'node_modules' + d; | ||
function FormatStackTrace(error, frames, filter, colors) { | ||
var lines = []; | ||
@@ -44,3 +49,3 @@ try { | ||
try { | ||
line = FormatSourcePosition(frame); | ||
line = FormatSourcePosition(frame, filter); | ||
} catch (e) { | ||
@@ -54,3 +59,15 @@ try { | ||
} | ||
lines.push(" at " + line); | ||
if (line !== undefined) { | ||
line = " at " + line; | ||
if (colors) { | ||
if (line.indexOf(node_modules) >= 0) { | ||
line = colors.cyan(line); | ||
} else if (line.indexOf(d) >= 0) { | ||
line = colors.red(line); | ||
} | ||
} | ||
lines.push(line); | ||
} | ||
} | ||
@@ -60,3 +77,3 @@ return lines.join("\n"); | ||
function FormatSourcePosition(frame) { | ||
function FormatSourcePosition(frame, filter) { | ||
var fileLocation = ""; | ||
@@ -70,2 +87,5 @@ if (frame.isNative()) { | ||
if (fileName) { | ||
for(var i=0, l=filter.length; i<l; i++) { | ||
if (fileName.indexOf(filter[i]) !== -1) return; | ||
} | ||
fileLocation += fileName; | ||
@@ -72,0 +92,0 @@ var lineNumber = frame.getLineNumber(); |
@@ -6,57 +6,57 @@ /** | ||
module.exports = function hook(wrap) { | ||
if (alreadyRequired) throw new Error("This should only be required and used once"); | ||
alreadyRequired = true; | ||
if (alreadyRequired) throw new Error("This should only be required and used once") | ||
alreadyRequired = true | ||
// Wrap setTimeout and setInterval | ||
["setTimeout", "setInterval"].forEach(function (name) { | ||
var original = this[name]; | ||
;["setTimeout", "setInterval"].forEach(function (name) { | ||
var original = this[name] | ||
this[name] = function (callback) { | ||
arguments[0] = wrap(callback, name); | ||
return original.apply(this, arguments); | ||
}; | ||
}); | ||
arguments[0] = wrap(callback, name) | ||
return original.apply(this, arguments) | ||
} | ||
}) | ||
// Wrap process.nextTick | ||
var nextTick = process.nextTick; | ||
var nextTick = process.nextTick | ||
process.nextTick = function wrappedNextTick(callback) { | ||
arguments[0] = wrap(callback, 'process.nextTick'); | ||
return nextTick.apply(this, arguments); | ||
}; | ||
arguments[0] = wrap(callback, 'process.nextTick') | ||
return nextTick.apply(this, arguments) | ||
} | ||
// Wrap FS module async functions | ||
var FS = require('fs'); | ||
var FS = require('fs') | ||
Object.keys(FS).forEach(function (name) { | ||
// If it has a *Sync counterpart, it's probably async | ||
if (!FS.hasOwnProperty(name + "Sync")) return; | ||
var original = FS[name]; | ||
if (!FS.hasOwnProperty(name + "Sync")) return | ||
var original = FS[name] | ||
FS[name] = function () { | ||
var i = arguments.length - 1; | ||
var i = arguments.length - 1 | ||
if (typeof arguments[i] === 'function') { | ||
arguments[i] = wrap(arguments[i], 'fs.'+name); | ||
arguments[i] = wrap(arguments[i], 'fs.'+name) | ||
} | ||
return original.apply(this, arguments); | ||
}; | ||
}); | ||
return original.apply(this, arguments) | ||
} | ||
}) | ||
// Wrap EventEmitters | ||
var EventEmitter = require('events').EventEmitter; | ||
var onEvent = EventEmitter.prototype.on; | ||
var EventEmitter = require('events').EventEmitter | ||
var onEvent = EventEmitter.prototype.on | ||
EventEmitter.prototype.on = EventEmitter.prototype.addListener = function (type, callback) { | ||
var newCallback = wrap(callback, 'EventEmitter.on'); | ||
var newCallback = wrap(callback, 'EventEmitter.on') | ||
if (newCallback !== callback) { | ||
callback.wrappedCallback = newCallback; | ||
arguments[1] = newCallback; | ||
callback.wrappedCallback = newCallback | ||
arguments[1] = newCallback | ||
} | ||
return onEvent.apply(this, arguments); | ||
}; | ||
var removeEvent = EventEmitter.prototype.removeListener; | ||
return onEvent.apply(this, arguments) | ||
} | ||
var removeEvent = EventEmitter.prototype.removeListener | ||
EventEmitter.prototype.removeListener = function (type, callback) { | ||
if (callback && callback.hasOwnProperty("wrappedCallback")) { | ||
arguments[1] = callback.wrappedCallback; | ||
arguments[1] = callback.wrappedCallback | ||
} | ||
return removeEvent.apply(this, arguments); | ||
}; | ||
return removeEvent.apply(this, arguments) | ||
} | ||
} | ||
var alreadyRequired; | ||
var alreadyRequired | ||
@@ -1,94 +0,111 @@ | ||
module.exports = trycatch; | ||
module.exports = trycatch | ||
// use colors module, if available | ||
try { trycatch.colors = require('colors'); } catch(err) {} | ||
var FormatStackTrace = require('./formatStackTrace'); | ||
var path = require('path'); | ||
var d = path.join('/'); | ||
try { trycatch.colors = require('colors') } catch(err) {} | ||
var FormatStackTrace = require('./formatStackTrace'), | ||
filename1 = __filename, | ||
filename2 = require.resolve('./hook') | ||
// findToken fails when _TOKEN_ deeper than Error.stackTraceLimit | ||
Error.stackTraceLimit = Infinity | ||
// findToken fails for stack traces deeper Error.stackTraceLimit => Error.stackTraceLimit = Infinity | ||
// Make configurable? | ||
Error.stackTraceLimit = Infinity; | ||
// The event-source hooks allows tokens & new stacks to be linked | ||
// called as shim | ||
require('./hook')(generateShim); | ||
// Replace built-in async functions, shim callbacks | ||
require('./hook')(generateShim) | ||
// generate a new callback shim for shim'd async function (e.g., fs.stats) | ||
// Generate a new callback wrapped in _TOKEN_ with Error to trace back | ||
function generateShim(next, name, location) { | ||
var self; | ||
var res = findToken(); | ||
if (!res) return next; | ||
var token = res.token; | ||
var stack = res.stack; | ||
// _TOKEN_ is the new callback and calls the real callback, next() | ||
if (typeof next !== 'function') return next | ||
// _TOKEN_ is the new callback and calls the real callback, next() | ||
function _TOKEN_() { | ||
try { | ||
return next.apply(self, arguments); | ||
} catch (err) { | ||
if (!(err instanceof Error)) { | ||
err = new Error(''+err); | ||
} | ||
var catchFn; | ||
token = _TOKEN_; | ||
err.stack = filterInternalFrames(err.stack); | ||
while(token.token) { | ||
if (token.stack) { | ||
err.stack += '\n ----------------------------------------\n' + | ||
' at '+token.orig+'\n' + | ||
token.stack.substring(token.stack.indexOf("\n") + 1) | ||
} | ||
catchFn = token = token.token; | ||
} | ||
catchFn(err); | ||
return next.apply(this, arguments) | ||
} catch (e) { | ||
handleError(e, _TOKEN_, false) | ||
} | ||
} | ||
_TOKEN_.orig = name; | ||
_TOKEN_.stack = stack; | ||
_TOKEN_.token = token; | ||
_TOKEN_.orig = name | ||
_TOKEN_.error = new Error | ||
return function() { | ||
self = this; | ||
_TOKEN_.apply(token, arguments); | ||
}; | ||
return _TOKEN_ | ||
} | ||
// Tags a stack and all decendent stacks with a token | ||
function handleError(err, token, recursive) { | ||
var origin | ||
if (!recursive) { | ||
if (!err.token) { | ||
// Newly created Error | ||
err = err instanceof Error ? err : new Error(''+err) | ||
err = getFilteredError(err) | ||
err.originalStack = err.stack | ||
} else { | ||
token = err.token | ||
} | ||
} | ||
while(token.error) { | ||
// stackSearch returns an object {token, stack} in place of error.stack String | ||
origin = getFilteredError(token.error, stackSearch).stack | ||
if (!origin) throw err | ||
if (!token.catchFn && origin.stack) { | ||
err.stack += '\n ----------------------------------------\n' + | ||
' at '+token.orig+'\n' + | ||
origin.stack.substring(origin.stack.indexOf("\n") + 1) | ||
} | ||
token = origin.token | ||
if (token.catchFn) break | ||
} | ||
if (typeof token.catchFn === 'function') { | ||
err.token = token | ||
try { | ||
token.catchFn.call(null, err, token) | ||
} catch(e2) { | ||
handleError(e2, token, true) | ||
} | ||
} | ||
} | ||
// Create origin _TOKEN_ for stack termination | ||
function trycatch(tryFn, catchFn) { | ||
function _TOKEN_() { | ||
tryFn(); | ||
tryFn() | ||
} | ||
_TOKEN_.token = catchFn; | ||
_TOKEN_.catchFn = catchFn | ||
_TOKEN_.error = new Error | ||
_TOKEN_.orig = 'trycatch' | ||
try { | ||
_TOKEN_(); | ||
_TOKEN_() | ||
} catch (err) { | ||
err.stack = filterInternalFrames(err.stack); | ||
catchFn(err); | ||
catchFn(getFilteredError(err)) | ||
} | ||
} | ||
// Looks for a token in the current stack using the V8 stack trace API | ||
function findToken(err) { | ||
if (!err) err = new Error(); | ||
var original = Error.prepareStackTrace; | ||
// stackSearch returns a function object instead of the string expected from the built-in | ||
Error.prepareStackTrace = stackSearch; | ||
var res = err.stack; | ||
Error.prepareStackTrace = original; | ||
err.stack = res && res.stack; | ||
return res; | ||
function getFilteredError(err, fn) { | ||
if (typeof fn !== 'function') { | ||
fn = function(error, structuredStackTrace) { | ||
return FormatStackTrace(error, structuredStackTrace, [filename1, filename2], trycatch.colors) | ||
} | ||
} | ||
if (!err) err = new Error; | ||
var old = Error.prepareStackTrace | ||
Error.prepareStackTrace = fn | ||
err.stack = err.stack | ||
Error.prepareStackTrace = old | ||
return err | ||
} | ||
function stackSearch(error, structuredStackTrace) { | ||
if (!structuredStackTrace) return; | ||
if (!structuredStackTrace) return | ||
for (var fn, i = 0, l = structuredStackTrace.length; i < l; i++) { | ||
fn = structuredStackTrace[i].fun; | ||
for (var fn, i=0, l=structuredStackTrace.length; i<l; i++) { | ||
fn = structuredStackTrace[i].fun | ||
if (fn.name === '_TOKEN_') { | ||
return { | ||
token: fn, | ||
stack: filterInternalFrames(FormatStackTrace(error, structuredStackTrace)) | ||
stack: FormatStackTrace(error, structuredStackTrace, [filename1, filename2], trycatch.colors) | ||
} | ||
@@ -98,21 +115,1 @@ } | ||
} | ||
var filename1 = __filename; | ||
var filename2 = require.resolve('./hook'); | ||
function filterInternalFrames(frames) { | ||
var ret = []; | ||
ret = frames.split("\n").filter(function(frame) { | ||
return frame.indexOf(filename1) < 0 && frame.indexOf(filename2) < 0; | ||
}); | ||
if (trycatch.colors) { | ||
ret = ret.map(function(frame, k) { | ||
if (frame.indexOf(d + 'node_modules' + d) >= 0) { | ||
frame = trycatch.colors.cyan(frame); | ||
} else if (frame.indexOf(d) >= 0) { | ||
frame = trycatch.colors.red(frame); | ||
} | ||
return frame; | ||
}); | ||
} | ||
return ret.join("\n"); | ||
} |
{ | ||
"name" : "trycatch", | ||
"version" : "0.0.11", | ||
"description" : "An asynchronous exception handler with long stack traces for node.js", | ||
"homepage": "http://github.com/CrabDude/trycatch", | ||
"repository": { | ||
"type" : "git", | ||
"url" : "git://github.com/CrabDude/trycatch.git" | ||
}, | ||
"bugs" : { "web" : "http://github.com/CrabDude/trycatch/issues" }, | ||
"author" : "Adam Crabtree <dude@noderiety.com> (http://noderiety.com)", | ||
"main" : "lib/trycatch", | ||
"directories" : { "lib" : "./lib/" }, | ||
"engines" : { "node" : ">=0.2.0" }, | ||
"licenses" : [{ | ||
"type" : "MIT", | ||
"url" : "http://www.opensource.org/licenses/mit-license.html" | ||
}], | ||
"dependencies" : { | ||
"colors": ">=0.5.0 <0.6" | ||
}, | ||
"keywords": [ | ||
"error", | ||
"exception", | ||
"try", | ||
"catch", | ||
"stack", | ||
"trace" | ||
] | ||
"name": "trycatch", | ||
"version": "0.1.0", | ||
"description": "An asynchronous exception handler with long stack traces for node.js", | ||
"homepage": "http://github.com/CrabDude/trycatch", | ||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com/CrabDude/trycatch.git" | ||
}, | ||
"bugs": { | ||
"url": "http://github.com/CrabDude/trycatch/issues" | ||
}, | ||
"author": "Adam Crabtree <dude@noderiety.com> (http://noderiety.com)", | ||
"main": "lib/trycatch", | ||
"directories": { | ||
"lib": "./lib/" | ||
}, | ||
"engines": { | ||
"node": ">=0.2.0" | ||
}, | ||
"scripts": { | ||
"test": "mocha --reporter spec ./test/*.js" | ||
}, | ||
"licenses": [ | ||
{ | ||
"type": "MIT", | ||
"url": "http://www.opensource.org/licenses/mit-license.html" | ||
} | ||
], | ||
"dependencies": { | ||
"colors": ">=0.5.0 <0.6" | ||
}, | ||
"keywords": [ | ||
"error", | ||
"exception", | ||
"try", | ||
"catch", | ||
"stack", | ||
"trace" | ||
], | ||
"devDependencies": { | ||
"mocha": "~1.4.0", | ||
"underscore": "~1.3.3" | ||
} | ||
} |
trycatch | ||
======= | ||
An asynchronous try catch / exception handler with long stack traces for node.js | ||
An asynchronous try catch exception handler with long stack traces for node.js | ||
Background | ||
---------- | ||
**Now PRODUCTION Ready!** | ||
See the "Background" from the [long-stack-traces](https://github.com/tlrobinson/long-stack-traces) module. | ||
With the update to 0.1.0, stack traces are now lazy, and all try/catch blocks conform to [V8 best practices](https://github.com/joyent/node/wiki/Best-practices-and-gotchas-with-v8). | ||
Install | ||
@@ -16,46 +17,43 @@ ------- | ||
Use | ||
--- | ||
Because trycatch shims all native I/O calls, it must be required before any other modules. | ||
var trycatch = require('trycatch') | ||
trycatch(fnTry, fnCatch) | ||
Basic Example | ||
------------- | ||
trycatch(function() { | ||
function f() { | ||
throw new Error('foo'); | ||
} | ||
setTimeout(f, Math.random()*1000); | ||
setTimeout(f, Math.random()*1000); | ||
}, function(err) { | ||
console.log("This is an asynchronous scoped error handler!\n", err.stack); | ||
}); | ||
```javascript | ||
var trycatch = require("trycatch"), | ||
_ = require('underscore')._ | ||
trycatch(function() { | ||
_.map(['Error 1', 'Error 2'], function foo(v) { | ||
setTimeout(function() { | ||
throw new Error(v) | ||
}, 10) | ||
}) | ||
}, function(err) { | ||
console.log("Async error caught!\n", err.stack); | ||
}); | ||
``` | ||
#### Output | ||
$ node examples/setTimeout.js | ||
This is an asynchronous scoped error handler! | ||
Error: foo | ||
at Object.f (/path/to/trycatch/examples/setTimeout.js:5:9) | ||
at Timer.callback (timers.js:83:39) | ||
---------------------------------------- | ||
at setTimeout | ||
at /path/to/trycatch/examples/setTimeout.js:8:2 | ||
at Object.<anonymous> (/path/to/trycatch/examples/setTimeout.js:3:1) | ||
at Module._compile (module.js:404:26) | ||
at Object..js (module.js:410:10) | ||
at Module.load (module.js:336:31) | ||
This is an asynchronous scoped error handler! | ||
Error: foo | ||
at Object.f (/path/to/trycatch/examples/setTimeout.js:5:9) | ||
at Timer.callback (timers.js:83:39) | ||
---------------------------------------- | ||
at setTimeout | ||
at /path/to/trycatch/examples/setTimeout.js:9:2 | ||
at Object.<anonymous> (/path/to/trycatch/examples/setTimeout.js:3:1) | ||
at Module._compile (module.js:404:26) | ||
at Object..js (module.js:410:10) | ||
at Module.load (module.js:336:31) | ||
![](https://raw.github.com/CrabDude/trycatch/master/screenshot.png) | ||
Advanced Examples | ||
------------- | ||
See the `/test` and `examples` directories for more use cases. | ||
Returning 500s on Server Request | ||
-------------------------------- | ||
```javascript | ||
http.createServer(function(req, res) { | ||
@@ -71,3 +69,11 @@ trycatch(function() { | ||
}).listen(8000); | ||
``` | ||
Visit http://localhost:8000 and get your 500. | ||
Visit http://localhost:8000 and get your 500. | ||
Thanks | ||
---------- | ||
Special thanks to [Tom Robinson](https://github.com/tlrobinson) for his [long-stack-traces](https://github.com/tlrobinson/long-stack-traces) module and [Tim Caswell](https://github.com/creationix) who built out the initial hook.js code. |
@@ -1,6 +0,4 @@ | ||
'use strict'; | ||
var assert = require('assert'), | ||
trycatch = require('../lib/trycatch') | ||
var assert = require('assert'); | ||
var trycatch = require('../lib/trycatch'); | ||
/* | ||
@@ -14,77 +12,42 @@ This test is to test whether trycatch can handle non-Errors being thrown | ||
TypeError: Cannot call method 'split' of undefined | ||
*/ | ||
describe('non-Errors', function() { | ||
it('should catch Strings', function (done) { | ||
trycatch(function () { | ||
setTimeout(function () { | ||
throw 'my-string being thrown' | ||
}, 0) | ||
}, function onError(err) { | ||
assert.equal(err.message, 'my-string being thrown') | ||
assert.notEqual(err.stack, undefined) | ||
done() | ||
}) | ||
}) | ||
To run this test: node ./throw-string.test.js | ||
*/ | ||
it('should catch Numbers', function (done) { | ||
trycatch(function () { | ||
setTimeout(function () { | ||
throw 123 | ||
}, 0) | ||
}, function onError(err) { | ||
assert.equal(err.message, (123).toString()) | ||
assert.notEqual(err.stack, undefined) | ||
done() | ||
}) | ||
}) | ||
//be compatible with running test from command line or from something like Expresso | ||
var EXIT = (require.main === module) ? 'exit' : 'beforeExit'; | ||
it('should catch Booleans', function (done) { | ||
var onErrorCalled = false | ||
exports['throwing non-Error object like string should be caught'] = function () { | ||
var onErrorCalled = false; | ||
trycatch(function () { | ||
setTimeout(function () { | ||
throw 'my-string being thrown'; //throwing a string non-Error object | ||
}, 100); | ||
}, function onError(err) { | ||
onErrorCalled = true; | ||
assert.equal(err.message, 'my-string being thrown'); | ||
assert.notEqual(err.stack, undefined); | ||
}); | ||
process.on(EXIT, function () { | ||
//check callbacks were called here | ||
assert.equal(onErrorCalled, true, 'throw string onError function should have been called'); | ||
console.error('success - caught thrown String'); | ||
}); | ||
}; | ||
exports['throwing non-Error object like number should be caught'] = function () { | ||
var onErrorCalled = false; | ||
trycatch(function () { | ||
setTimeout(function () { | ||
throw 123; //throwing a number non-Error object | ||
}, 100); | ||
}, function onError(err) { | ||
onErrorCalled = true; | ||
assert.equal(err.message, (123).toString()); | ||
assert.notEqual(err.stack, undefined); | ||
}); | ||
process.on(EXIT, function () { | ||
//check callbacks were called here | ||
assert.equal(onErrorCalled, true, 'throw number onError function should have been called'); | ||
console.error('success - caught thrown number'); | ||
}); | ||
}; | ||
exports['throwing non-Error object like boolean should be caught'] = function () { | ||
var onErrorCalled = false; | ||
trycatch(function () { | ||
setTimeout(function () { | ||
throw true; //throwing a boolean non-Error object | ||
}, 100); | ||
}, function onError(err) { | ||
onErrorCalled = true; | ||
assert.equal(err.message, (true).toString()); | ||
assert.notEqual(err.stack, undefined); | ||
}); | ||
process.on(EXIT, function () { | ||
//check callbacks were called here | ||
assert.equal(onErrorCalled, true, 'throw boolean onError function should have been called'); | ||
console.error('success - caught thrown boolean'); | ||
}); | ||
}; | ||
// if run directly from node execute all the exports | ||
if (require.main === module) Object.keys(exports).forEach(function (f) { exports[f](); }); | ||
trycatch(function () { | ||
setTimeout(function () { | ||
throw true | ||
}, 0) | ||
}, function onError(err) { | ||
assert.equal(err.message, (true).toString()) | ||
assert.notEqual(err.stack, undefined) | ||
done() | ||
}) | ||
}) | ||
}) |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Non-existent author
Supply chain riskThe package was published by an npm account that no longer exists.
Found 1 instance in 1 package
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
41976
15
543
1
77
2
1
2