Comparing version 0.2.1 to 0.2.2
@@ -27,3 +27,3 @@ // Copyright 2006-2008 the V8 project authors. All rights reserved. | ||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
module.exports = function(format, filter) { | ||
module.exports = function(filter) { | ||
filter = Array.isArray(filter) ? filter : [] | ||
@@ -57,10 +57,3 @@ | ||
line = " at " + line; | ||
if ('function' === typeof format) { | ||
line = format(line) | ||
} | ||
if (line) { | ||
lines.push(line); | ||
} | ||
lines.push(line); | ||
} | ||
@@ -67,0 +60,0 @@ } |
@@ -0,283 +1,365 @@ | ||
// Don't rely on domains to catch errors | ||
// Use domains to pass caught errors to active | ||
// Keep (add?) fix for event emitter handler | ||
// emit uncaughtException if (isCore(err) && err instanceof EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError)) | ||
// when we run, set domain to active | ||
// do usual long-stack-traces/trycatch run | ||
// | ||
// in guard, if it's eventemitter, reset active domain to closure domain | ||
var util = require('util') | ||
, domain = require('domain') | ||
, FormatStackTrace = require('./formatStackTrace') | ||
, hook = require('./hook') | ||
, hooked = false | ||
, fileNameFilter = [__filename, require.resolve('./hook'), 'domain.js'] | ||
, d = require('path').join('/') | ||
, node_modules = d + 'node_modules' + d | ||
, options = { | ||
'long-stack-traces': null | ||
, 'colors': { | ||
'node': 'white', | ||
'node_modules': 'cyan', | ||
'default': 'red' | ||
} | ||
, 'format': defaultFormat | ||
, 'filter': fileNameFilter | ||
} | ||
, domain = require('domain') | ||
, path = require('path') | ||
, FormatStackTrace = require('./formatStackTrace') | ||
, hookit = require('hookit') | ||
, fileNameFilter = [__filename, require.resolve('hookit'), 'domain.js'] | ||
, formatter = FormatStackTrace(fileNameFilter) | ||
, node_modules = path.sep + 'node_modules' + path.sep | ||
, hasGuardedWithoutLST | ||
, options | ||
options = { | ||
'long-stack-traces': null | ||
, 'colors': { | ||
'node': false, //'white', | ||
'node_modules': false, //'cyan', | ||
'default': 'red' | ||
} | ||
, 'format': defaultFormat | ||
, 'filter': fileNameFilter | ||
} | ||
module.exports = trycatch | ||
Error.stackTraceLimit = 30 | ||
// use colors module, if available | ||
if (process.stdout.isTTY) { | ||
try { trycatch.colors = require('colors') } catch(err) {} | ||
try { trycatch.colors = require('colors') } catch(err) {} | ||
} | ||
module.exports = trycatch | ||
function trycatch(tryFn, catchFn) { | ||
if ('function' !== typeof tryFn || 'function' !== typeof catchFn) { | ||
throw new Error('tryFn and catchFn must be functions') | ||
} | ||
trycatch.begin(tryFn, catchFn) | ||
if ('function' !== typeof tryFn || 'function' !== typeof catchFn) { | ||
throw new Error('tryFn and catchFn must be functions') | ||
} | ||
return trycatch.run(tryFn, catchFn) | ||
} | ||
trycatch.begin = domainTrycatch | ||
// Won't be used until long-stack-traces turned off=>on=>off | ||
trycatch.guard = nopGuard | ||
trycatch.run = run | ||
trycatch.wrap = wrap | ||
trycatch.configure = configure | ||
trycatch.format = FormatStackTrace(defaultFormat, fileNameFilter) | ||
trycatch.format = defaultFormat | ||
function configure(opts) { | ||
var resetFormat = false | ||
hookit(function generateShim(callback, fnName) { | ||
return trycatch.wrap(callback, fnName) | ||
}) | ||
if ('undefined' !== typeof opts['long-stack-traces']) { | ||
options['long-stack-traces'] = Boolean(opts['long-stack-traces']) | ||
if (true === options['long-stack-traces']) { | ||
if (!hooked) { | ||
hooked = true | ||
// Replace built-in async functions, shim callbacks with generator | ||
hookit() | ||
} | ||
function run(fn, cb) { | ||
var parentDomain = domain.active | ||
, d = domain.create() | ||
// findToken fails when _TOKEN_ deeper than Error.stackTraceLimit | ||
Error.stackTraceLimit = Infinity | ||
trycatch.begin = longStackTraceBegin | ||
trycatch.guard = longStackTraceGuard | ||
} else if (false === options['long-stack-traces'] && hooked) { | ||
console.warn('Turning long-stack-traces off can result in indeterminate behavior.') | ||
Error.stackTraceLimit = 10 | ||
trycatch.begin = domainTrycatch | ||
trycatch.guard = nopGuard | ||
} | ||
} | ||
d.on('error', function(e) { | ||
handleException(e, parentDomain, cb) | ||
}) | ||
if (null != opts.colors && 'object' === typeof opts.colors) { | ||
if ('undefined' !== typeof opts.colors.node) { | ||
options.colors.node = opts.colors.node | ||
} | ||
if ('undefined' !== typeof opts.colors.node_modules) { | ||
options.colors.node_modules = opts.colors.node_modules | ||
} | ||
if ('undefined' !== typeof opts.colors.default) { | ||
options.colors.default = opts.colors.default | ||
} | ||
} | ||
if (options['long-stack-traces']) { | ||
fn = lstRunWrap(fn) | ||
} | ||
if ('undefined' !== typeof opts.format) { | ||
if ('function' === typeof opts.format) { | ||
options.format = opts.format | ||
resetFormat = true | ||
} else if (!Boolean(opts.format)) { | ||
options.format = defaultFormat | ||
resetFormat = true | ||
} | ||
} | ||
runInDomain(d, fn) | ||
return d | ||
} | ||
if (Array.isArray(opts.filter)) { | ||
options.filter = opts.filter | ||
resetFormat = true | ||
} | ||
function handleException(err, parentDomain, cb) { | ||
var caught | ||
if (resetFormat) { | ||
trycatch.format = FormatStackTrace(options.format, options.filter) | ||
} | ||
} | ||
err = buildError(err, options['long-stack-traces']) | ||
function hookit() { | ||
hook(function generateShim(callback, fnName) { | ||
return trycatch.guard(callback, fnName) | ||
}) | ||
formatError(err) | ||
if (!err.catchable) { | ||
// Unexpected error, core in undefined state, requires restart | ||
caught = process.emit('uncaughtException', err) | ||
// Otherwise crash the process | ||
if (!caught) { | ||
throw err | ||
} | ||
return | ||
} | ||
if (!parentDomain) { | ||
if ('function' === typeof cb) { | ||
cb(err) | ||
return | ||
} | ||
caught = process.emit('uncaughtApplicationException', err) | ||
if (!caught) { | ||
caught = process.emit('uncaughtException', err) | ||
} | ||
if (!caught) { | ||
throw err | ||
} | ||
return | ||
} | ||
runInDomain(parentDomain, function() { | ||
cb(err) | ||
}) | ||
} | ||
// In case long-stack-traces are switched on -> off | ||
function nopGuard(cb) { | ||
return cb | ||
function runInDomain(d, fn) { | ||
if (!d || d._disposed) { | ||
try { | ||
fn() | ||
} catch(e1) { | ||
handleException(e1) | ||
} | ||
return | ||
} | ||
try { | ||
d.run(fn) | ||
} catch(e2) { | ||
d.emit('error', e2) | ||
} | ||
} | ||
function domainTrycatch (fn, cb, isLongStackTrace) { | ||
var parentDomain = domain.active | ||
, d = domain.create() | ||
function isCatchableError(err) { | ||
if (!isCoreError(err)) return true | ||
d.on('error', function onError(err) { | ||
err = buildError(err, Boolean(options['long-stack-traces'] && isLongStackTrace)) | ||
// Unexpected runtime errors aren't catchable | ||
if (err instanceof EvalError || | ||
err instanceof RangeError || | ||
err instanceof ReferenceError || | ||
err instanceof SyntaxError || | ||
err instanceof URIError) return false | ||
runInDomain(parentDomain, function() { | ||
cb(err) | ||
}) | ||
}) | ||
runInDomain(d, fn) | ||
// Can't catch invalid input errors more than 1 layer deep | ||
return !isCoreSlice(err.stack.split('\n')[2]) | ||
} | ||
function runInDomain(d, fn) { | ||
if (d && !d._disposed) { | ||
try { | ||
d.run(fn) | ||
} catch(e) { | ||
d.emit('error', e) | ||
} | ||
} else { | ||
fn() | ||
} | ||
function isCoreSlice(slice) { | ||
return slice && slice.indexOf(path.sep) === -1 | ||
} | ||
function buildError(err, isLongStackTrace) { | ||
var parent | ||
, token | ||
, skip = false | ||
, isError = err instanceof Error | ||
function isCoreError(err) { | ||
return true !== err.coerced && isCoreSlice(err.stack.split('\n')[1]) | ||
} | ||
// Coerce to error | ||
if (!isError) { | ||
err = new Error(err) | ||
} | ||
function lstRunWrap(tryFn) { | ||
// Create origin _TOKEN_ for stack termination | ||
function _TOKEN_() { | ||
tryFn() | ||
} | ||
_TOKEN_.error = new Error | ||
_TOKEN_.parent = 'trycatch' | ||
if (!isLongStackTrace) { | ||
return generateStack(err) | ||
} else if (!isError) { | ||
console.warn('Unable to generate long-stack-trace for thrown non-Error') | ||
return generateStack(err) | ||
} | ||
return _TOKEN_ | ||
} | ||
if (err.token) { | ||
skip = true | ||
token = err.token | ||
} else { | ||
parent = generateStack(err, stackSearch).stack | ||
err.stack = parent.stack | ||
if (!parent.token) { | ||
// options['long-stack-traces'] was probably toggled after async was invoked | ||
return err | ||
} | ||
token = parent.token | ||
parent = null | ||
} | ||
// Generate a new callback wrapped in _TOKEN_ (optionally with Error for LSTs) | ||
function wrap(callback, name) { | ||
var parentDomain = domain.active | ||
while(token.error) { | ||
if (!skip && token.catchFn) break | ||
if ('function' !== typeof callback) return callback | ||
skip = false | ||
function _TOKEN_(that, args) { | ||
callback.apply(that, args) | ||
} | ||
// HACK: Use Error.prepareStackTrace = stackSearch to find parent | ||
parent = generateStack(token.error, stackSearch).stack | ||
if (!parent) { | ||
throw err | ||
} | ||
if (options['long-stack-traces']) { | ||
_TOKEN_.parent = name | ||
_TOKEN_.error = new Error | ||
} | ||
if (!token.catchFn && parent.stack) { | ||
err.stack += '\n ----------------------------------------\n' + | ||
' at '+token.parent+'\n' + | ||
parent.stack.substring(parent.stack.indexOf("\n") + 1) | ||
} | ||
token = parent.token | ||
} | ||
// Running in parentDomain should only be necessary for EventEmitter handlers | ||
// Core should handle the rest | ||
// Wrapping is still necessary to fix core error handling | ||
return function() { | ||
var that = this | ||
, args = arguments | ||
if ('function' === typeof token.catchFn) { | ||
err.token = token | ||
return err | ||
} | ||
runInDomain(parentDomain, function() { | ||
_TOKEN_(that, args) | ||
}) | ||
} | ||
} | ||
function longStackTraceBegin(tryFn, catchFn) { | ||
// Create origin _TOKEN_ for stack termination | ||
function _TOKEN_() { | ||
tryFn() | ||
} | ||
_TOKEN_.catchFn = catchFn | ||
_TOKEN_.error = new Error | ||
_TOKEN_.parent = 'trycatch' | ||
function buildError(err, isLST) { | ||
var parent | ||
, token | ||
, skip = false | ||
, isError = err instanceof Error | ||
domainTrycatch(_TOKEN_, catchFn, true) | ||
} | ||
// Coerce to error | ||
if (!isError) { | ||
err = new Error(err) | ||
err.coerced = true | ||
} | ||
function generateStack(err, fn) { | ||
if ('function' !== typeof fn) { | ||
fn = trycatch.format | ||
} | ||
if (!err) err = new Error | ||
var old = Error.prepareStackTrace | ||
Error.prepareStackTrace = fn | ||
// Generate stack trace by accessing stack property | ||
err.stack = err.stack | ||
Error.prepareStackTrace = old | ||
return err | ||
} | ||
if (!isLST) { | ||
return setProperties(generateStack(err)) | ||
} else if (!isError) { | ||
console.warn('Unable to generate long-stack-trace for thrown non-Error') | ||
return setProperties(generateStack(err)) | ||
} | ||
// Generate a new callback wrapped in _TOKEN_ with Error to trace back | ||
function longStackTraceGuard(callback, name) { | ||
var parentDomain | ||
// If previously caught | ||
if (err.token) { | ||
skip = true | ||
token = err.token | ||
} else { | ||
parent = generateStack(err, stackSearch).stack | ||
err.stack = parent.stack | ||
err = setProperties(err) | ||
if (!parent.token) { | ||
// options['long-stack-traces'] was probably toggled after async was invoked | ||
return err | ||
} | ||
token = parent.token | ||
parent = null | ||
} | ||
if ('function' !== typeof callback) return callback | ||
while(token.error) { | ||
if (!skip && 'trycatch' === token.parent) break | ||
parentDomain = domain.active | ||
skip = false | ||
// _TOKEN_ is the new callback and calls the real callback, callback() | ||
function _TOKEN_() { | ||
callback.apply(this, arguments) | ||
} | ||
// HACK: Use Error.prepareStackTrace = stackSearch to find parent | ||
parent = generateStack(token.error, stackSearch).stack | ||
if (!parent) { | ||
throw err | ||
} | ||
_TOKEN_.parent = name | ||
_TOKEN_.error = new Error | ||
return function() { | ||
var args = arguments | ||
runInDomain(parentDomain, function() { | ||
_TOKEN_.apply(this, args) | ||
}) | ||
} | ||
if ('trycatch' !== token.parent && parent.stack) { | ||
err.stack += '\n ----------------------------------------\n' + | ||
' at '+token.parent+'\n' + | ||
parent.stack.substring(parent.stack.indexOf("\n") + 1) | ||
} | ||
token = parent.token | ||
} | ||
if ('trycatch' === token.parent) { | ||
err.token = token | ||
return err | ||
} | ||
} | ||
function setProperties(err) { | ||
err.coreThrown = isCoreError(err) | ||
err.catchable = isCatchableError(err) | ||
return err | ||
} | ||
function generateStack(err, fn) { | ||
var old = Error.prepareStackTrace | ||
if ('function' !== typeof fn) { | ||
fn = formatter | ||
} | ||
if (!err) err = new Error | ||
Error.prepareStackTrace = fn | ||
// Generate stack trace by accessing stack property | ||
err.stack = err.stack | ||
Error.prepareStackTrace = old | ||
return err | ||
} | ||
function stackSearch(error, structuredStackTrace) { | ||
var stack, fn, i, l | ||
var stack, fn, i, l | ||
if (!structuredStackTrace) return | ||
stack = trycatch.format(error, structuredStackTrace) | ||
if (!structuredStackTrace) return | ||
for (i=0, l=structuredStackTrace.length; i<l; i++) { | ||
fn = structuredStackTrace[i].fun | ||
if ('_TOKEN_' === fn.name) { | ||
return { | ||
token: fn, | ||
stack: stack | ||
} | ||
} | ||
} | ||
return {stack: stack} | ||
stack = formatter(error, structuredStackTrace) | ||
for (i=0, l=structuredStackTrace.length; i<l; i++) { | ||
fn = structuredStackTrace[i].fun | ||
if ('_TOKEN_' === fn.name) { | ||
return { | ||
token: fn, | ||
stack: stack | ||
} | ||
} | ||
} | ||
return {stack: stack} | ||
} | ||
function formatError(err) { | ||
var stack = err.stack.split('\n') | ||
, i = stack.length | ||
while (i--) { | ||
stack[i] = trycatch.format(stack[i]) | ||
} | ||
return stack.join('') | ||
} | ||
function defaultFormat(line) { | ||
var type, color | ||
var type, color | ||
if (trycatch.colors) { | ||
if (line.indexOf(node_modules) >= 0) { | ||
type = "node_modules"; | ||
} else if (line.indexOf(d) >= 0) { | ||
type = "default"; | ||
} else { | ||
type = "node" | ||
} | ||
if (trycatch.colors) { | ||
if (line.indexOf(node_modules) >= 0) { | ||
type = "node_modules"; | ||
} else if (line.indexOf(path.sep) >= 0) { | ||
type = "default"; | ||
} else { | ||
type = "node" | ||
} | ||
color = options.colors[type] | ||
if ('none' === color || !Boolean(color)) { | ||
return | ||
} | ||
color = options.colors[type] | ||
if ('none' === color || !Boolean(color)) { | ||
return | ||
} | ||
if (trycatch.colors[color]) { | ||
return trycatch.colors[color](line); | ||
} | ||
} | ||
if (trycatch.colors[color]) { | ||
return trycatch.colors[color](line); | ||
} | ||
} | ||
return line | ||
} | ||
function configure(opts) { | ||
if (null != opts['long-stack-traces']) { | ||
opts['long-stack-traces'] = Boolean(opts['long-stack-traces']) | ||
if (opts['long-stack-traces']) { | ||
if (hasGuardedWithoutLST) { | ||
console.warn('Turning long-stack-traces off can result in indeterminate behavior.') | ||
} | ||
// findToken fails when _TOKEN_ deeper than Error.stackTraceLimit | ||
Error.stackTraceLimit = Infinity | ||
} else { | ||
Error.stackTraceLimit = 30 | ||
} | ||
options['long-stack-traces'] = opts['long-stack-traces'] | ||
} | ||
if (null != opts.colors && 'object' === typeof opts.colors) { | ||
if ('undefined' !== typeof opts.colors.node) { | ||
options.colors.node = opts.colors.node | ||
} | ||
if ('undefined' !== typeof opts.colors.node_modules) { | ||
options.colors.node_modules = opts.colors.node_modules | ||
} | ||
if ('undefined' !== typeof opts.colors.default) { | ||
options.colors.default = opts.colors.default | ||
} | ||
} | ||
if ('undefined' !== typeof opts.format) { | ||
if ('function' === typeof opts.format) { | ||
trycatch.format = options.format = opts.format | ||
} else if (!Boolean(opts.format)) { | ||
trycatch.format = options.format = defaultFormat | ||
} | ||
} | ||
if (Array.isArray(opts.filter)) { | ||
options.filter = opts.filter | ||
} | ||
} | ||
{ | ||
"name": "trycatch", | ||
"version": "0.2.1", | ||
"version": "0.2.2", | ||
"description": "An asynchronous domain-based exception handler with long stack traces for node.js", | ||
@@ -19,3 +19,3 @@ "homepage": "http://github.com/CrabDude/trycatch", | ||
"engines": { | ||
"node": ">=0.9.5" | ||
"node": ">=0.8.14" | ||
}, | ||
@@ -33,3 +33,4 @@ "engineStrict": true, | ||
"dependencies": { | ||
"colors": ">=0.5.0 <0.6" | ||
"colors": ">=0.5.0 <0.6", | ||
"hookit": "0.0.2" | ||
}, | ||
@@ -36,0 +37,0 @@ "keywords": [ |
var trycatch = require('../lib/trycatch') | ||
, assert = require('assert') | ||
/* | ||
@@ -11,3 +11,3 @@ This tests the basic functionality of catching errors synchronously and asynchronously | ||
var str = longStackTraces ? ' (long-stack-traces)' : '' | ||
describe('Basic Error catching' + str, function() { | ||
@@ -14,0 +14,0 @@ before(function() { |
@@ -12,3 +12,3 @@ var assert = require('assert'), | ||
var str = longStackTraces ? ' (long-stack-traces)' : '' | ||
describe('Nested trycatchs' + str, function() { | ||
@@ -74,3 +74,3 @@ before(function() { | ||
assert.equal(count, 3) | ||
if (longStackTraces) { | ||
@@ -97,3 +97,3 @@ assert.equal(err.stack.split(delimitter).length, 3) | ||
++count | ||
if (longStackTraces) { | ||
@@ -112,3 +112,3 @@ assert.equal(err.stack.split(delimitter).length, 2) | ||
assert.equal(count, 3) | ||
if (longStackTraces) { | ||
@@ -115,0 +115,0 @@ assert.equal(err.stack.split(delimitter).length, 3) |
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
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
53234
906
2
+ Addedhookit@0.0.2
+ Addedhookit@0.0.2(transitive)