Comparing version 0.2.7 to 0.2.8
@@ -1,44 +0,24 @@ | ||
// 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') | ||
, path = require('path') | ||
, formatter = require('./formatStackTrace') | ||
, hookit = require('hookit') | ||
, fileNameFilter = [__filename, require.resolve('hookit'), 'domain.js'] | ||
, node_modules = path.sep + 'node_modules' + path.sep | ||
, originalFormatError = require('./formatError').formatError | ||
, normalizeError = require('./formatError').normalizeError | ||
, fileNameFilter = [__filename, require.resolve('hookit'), 'domain.js', 'Domain.emit'] | ||
, hasGuardedWithoutLST | ||
, options | ||
var delimitter = '----------------------------------------' | ||
options = { | ||
'long-stack-traces': null | ||
, 'colors': { | ||
'node': 'white', | ||
'node_modules': '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) {} | ||
'long-stack-traces': null | ||
, 'colors': { | ||
'node': 'white', | ||
'node_modules': 'cyan', | ||
'default': 'red' | ||
} | ||
, 'format': null | ||
, 'filter': fileNameFilter | ||
} | ||
/* Run Logic */ | ||
function trycatch(tryFn, catchFn) { | ||
@@ -51,132 +31,49 @@ if ('function' !== typeof tryFn || 'function' !== typeof catchFn) { | ||
trycatch.run = run | ||
trycatch.wrap = wrap | ||
trycatch.configure = configure | ||
trycatch.format = defaultFormat | ||
hookit(function generateShim(callback, fnName) { | ||
var newCallback = trycatch.wrap(callback, fnName) | ||
newCallback.__proto__ = callback | ||
return newCallback | ||
}) | ||
function run(fn, cb) { | ||
function run(tryFn, catchFn) { | ||
var parentDomain = domain.active | ||
, d = domain.create() | ||
, stack | ||
d.on('error', function(e) { | ||
handleException(e, parentDomain, cb) | ||
}) | ||
d.parentStack = parentDomain && parentDomain.currentStack | ||
if (options['long-stack-traces']) { | ||
fn = lstRunWrap(fn) | ||
} | ||
runInDomain(d, fn) | ||
d.on('error', onError) | ||
runInDomain(d, tryFn) | ||
return d | ||
} | ||
function handleException(err, parentDomain, cb) { | ||
var caught | ||
err = 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 | ||
function onError(e) { | ||
if (!e.caught) { | ||
d.exit() | ||
throw e | ||
} | ||
if (!parentDomain) { | ||
if ('function' === typeof cb) { | ||
cb(err) | ||
return | ||
if (e.rethrown) { | ||
if (e.parentStack) { | ||
e.stack += '\n ----------------------------------------\n' + e.parentStack | ||
} | ||
caught = process.emit('uncaughtApplicationException', err) | ||
if (!caught) { | ||
caught = process.emit('uncaughtException', err) | ||
} | ||
if (!caught) { | ||
throw err | ||
} | ||
return | ||
} else if (d.currentStack) { | ||
e.stack += '\n ----------------------------------------\n' + d.currentStack | ||
} | ||
e = formatError(e) | ||
runInDomain(parentDomain, function() { | ||
cb(err) | ||
}) | ||
} | ||
function runInDomain(d, fn) { | ||
if (!d || d._disposed) { | ||
try { | ||
fn() | ||
} catch(e1) { | ||
handleException(e1) | ||
if(d.parentStack) { | ||
e.parentStack = d.parentStack | ||
} | ||
return | ||
} | ||
try { | ||
d.run(fn) | ||
} catch(e2) { | ||
d.emit('error', e2) | ||
d.exit() | ||
handleException(e, parentDomain, catchFn) | ||
} | ||
} | ||
function isCatchableError(err) { | ||
if (!isCoreError(err)) return true | ||
/* Wrapping Logic */ | ||
// Unexpected runtime errors aren't catchable | ||
if (err instanceof EvalError || | ||
err instanceof RangeError || | ||
err instanceof ReferenceError || | ||
err instanceof SyntaxError || | ||
err instanceof URIError) return false | ||
// Can't catch invalid input errors more than 1 layer deep | ||
return !isCoreSlice(err.stack.split('\n')[2]) | ||
} | ||
function isCoreSlice(slice) { | ||
return slice && slice.indexOf(path.sep) === -1 | ||
} | ||
function isCoreError(err) { | ||
return true !== err.coerced && isCoreSlice(err.stack.split('\n')[1]) | ||
} | ||
function lstRunWrap(tryFn) { | ||
// Create origin _TOKEN_ for stack termination | ||
function _TOKEN_() { | ||
tryFn() | ||
} | ||
_TOKEN_.error = new Error | ||
_TOKEN_.origin = 'trycatch' | ||
return _TOKEN_ | ||
} | ||
// Generate a new callback wrapped in _TOKEN_ (optionally with Error for LSTs) | ||
// Generate a new callback | ||
// Ensure it runs in the same domain | ||
function wrap(callback, name) { | ||
var parentDomain = process.domain | ||
var d = process.domain | ||
, stack | ||
if ('function' !== typeof callback) return callback | ||
function _TOKEN_(that, args) { | ||
callback.apply(that, args) | ||
} | ||
stack = getStack(d) | ||
if (options['long-stack-traces']) { | ||
_TOKEN_.origin = name | ||
_TOKEN_.error = new Error | ||
} | ||
// 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() { | ||
@@ -186,4 +83,5 @@ var that = this | ||
runInDomain(parentDomain, function() { | ||
_TOKEN_(that, args) | ||
if (d) d.currentStack = stack | ||
runInDomain(d, function() { | ||
callback.apply(that, args) | ||
}) | ||
@@ -193,195 +91,76 @@ } | ||
function buildError(err, structuredStackTrace) { | ||
var parent | ||
, token | ||
, isLST = options['long-stack-traces'] | ||
, skip = false | ||
, isError = err instanceof Error | ||
, stackToParent | ||
function getStack(parentDomain) { | ||
var stack = '' | ||
// Coerce to error | ||
if (!isError) { | ||
err = new Error(err) | ||
err.coerced = true | ||
} | ||
if (!isLST || !isError) { | ||
if (!isError) console.warn('Unable to generate long-stack-trace for thrown non-Error') | ||
if (structuredStackTrace) { | ||
err.stack = formatter(err, structuredStackTrace) | ||
} else { | ||
err = generateStack(err) | ||
if (options['long-stack-traces']) { | ||
stack = new Error().stack.substr(6) | ||
if (parentDomain && parentDomain.currentStack) { | ||
stack += '\n ----------------------------------------\n' + parentDomain.currentStack | ||
} | ||
return setProperties(err) | ||
} | ||
// If previously caught | ||
if (err.token) { | ||
skip = true | ||
token = err.token | ||
} else { | ||
if (structuredStackTrace) { | ||
parent = stackSearch(err, structuredStackTrace) | ||
} else { | ||
parent = generateStack(err, stackSearch).stack | ||
} | ||
err.stack = formatStructuredStackTrace(err, parent.structuredStackTrace) | ||
err = setProperties(err) | ||
if (!parent.token) { | ||
// options['long-stack-traces'] was probably toggled after async was invoked | ||
return err | ||
} | ||
token = parent.token | ||
parent = null | ||
} | ||
while(token.error) { | ||
if (!skip && 'trycatch' === token.origin) break | ||
skip = false | ||
// HACK: Use Error.prepareStackTrace = stackSearch to find parent | ||
parent = generateStack(token.error, stackSearch).stack | ||
if (!parent || !parent.token) { | ||
// An error occurred outside of trycatch | ||
// Handle it by the usual means | ||
return err | ||
} | ||
stackToParent = formatStructuredStackTrace(token.error, parent.structuredStackTrace, parent.token.error ? parent.index : undefined) | ||
if ('trycatch' !== token.origin && stackToParent) { | ||
err.stack += '\n ----------------------------------------\n' + | ||
' at '+token.origin+'\n' + | ||
stackToParent.substring(stackToParent.indexOf("\n") + 1) | ||
} | ||
token = parent.token | ||
} | ||
if ('trycatch' === token.origin) { | ||
err.token = token | ||
} | ||
return err | ||
return stack | ||
} | ||
function setProperties(err) { | ||
err.coreThrown = isCoreError(err) | ||
err.catchable = isCatchableError(err) | ||
return err | ||
} | ||
Error.prepareStackTrace = prepareStackTrace | ||
function prepareStackTrace(error, structuredStackTrace) { | ||
var err = buildError(error, structuredStackTrace) | ||
return err.stack = formatStack(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 fn, i, l | ||
if (!structuredStackTrace) return | ||
for (i=0, l=structuredStackTrace.length; i<l; i++) { | ||
fn = structuredStackTrace[i].fun | ||
if ('_TOKEN_' === fn.name) { | ||
return { | ||
token: fn | ||
, index: i | ||
, structuredStackTrace: structuredStackTrace | ||
} | ||
function runInDomain(d, fn) { | ||
if (!d || d._disposed) { | ||
try { | ||
fn() | ||
} catch(e1) { | ||
handleException(formatError(normalizeError(e1))) | ||
} | ||
return | ||
} | ||
return { | ||
structuredStackTrace: structuredStackTrace | ||
} | ||
} | ||
function formatStructuredStackTrace(error, structuredStackTrace, topIndex) { | ||
var newStructuredStackTrace = [] | ||
, filter = false | ||
, i, j, l, l2 | ||
topIndex = topIndex == null ? Infinity : topIndex | ||
for (i=0, l=structuredStackTrace.length; i<l; i++) { | ||
for(j=0, l2=options.filter.length; j<l2; j++) { | ||
if (structuredStackTrace[i].getFileName().indexOf(options.filter[j]) !== -1) { | ||
filter = true | ||
continue | ||
} | ||
} | ||
if (filter) { | ||
filter = false | ||
continue | ||
} | ||
newStructuredStackTrace.push(structuredStackTrace[i]) | ||
if (i>topIndex) break | ||
try { | ||
d.run(fn) | ||
} catch(e2) { | ||
d.emit('error', normalizeError(e2)) | ||
} | ||
return formatter(error, newStructuredStackTrace) | ||
} | ||
/* | ||
*/ | ||
function handleException(err, parentDomain, catchFn) { | ||
var caught | ||
function formatStack(err) { | ||
var stack = err.stack.split('\n') | ||
, newStack = [] | ||
, line | ||
, i, l | ||
for (i=0, l=stack.length; i<l; i++) { | ||
line = trycatch.format(stack[i]) | ||
if (line) { | ||
newStack.push(line) | ||
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 | ||
} | ||
return newStack.join('\n') | ||
} | ||
function defaultFormat(line) { | ||
var type, color | ||
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)) { | ||
if (!parentDomain) { | ||
if ('function' === typeof catchFn) { | ||
catchFn(err) | ||
return | ||
} | ||
if (trycatch.colors[color]) { | ||
return trycatch.colors[color](line); | ||
caught = process.emit('uncaughtApplicationException', err) | ||
if (!caught) { | ||
caught = process.emit('uncaughtException', err) | ||
} | ||
if (!caught) { | ||
throw err | ||
} | ||
return | ||
} | ||
return line | ||
runInDomain(parentDomain, function() { | ||
catchFn(err) | ||
}) | ||
} | ||
function formatError(err) { | ||
err = buildError(err) | ||
err.stack = formatStack(err) | ||
return err | ||
return originalFormatError(err | ||
, { | ||
'long-stack-traces': options['long-stack-traces'] | ||
, lineFormatter: trycatch.format | ||
, colors: options.colors | ||
, filter: options.filter | ||
}) | ||
} | ||
/* Config Logic */ | ||
function configure(opts) { | ||
@@ -395,6 +174,6 @@ if (null != opts['long-stack-traces']) { | ||
} | ||
// findToken fails when _TOKEN_ deeper than Error.stackTraceLimit | ||
// No longer necessary, but nice to have | ||
Error.stackTraceLimit = Infinity | ||
} else { | ||
Error.stackTraceLimit = 30 | ||
Error.stackTraceLimit = 10 | ||
} | ||
@@ -420,3 +199,3 @@ options['long-stack-traces'] = opts['long-stack-traces'] | ||
} else if (!Boolean(opts.format)) { | ||
trycatch.format = options.format = defaultFormat | ||
trycatch.format = null | ||
} | ||
@@ -430,1 +209,19 @@ } | ||
/* Setup Logic */ | ||
// Pass callback wrapping function to hookit | ||
hookit(function generateShim(callback, fnName) { | ||
if ('function' !== typeof callback) return callback | ||
var newCallback = trycatch.wrap(callback, fnName) | ||
// Inherit from callback for when properties are added | ||
if (newCallback !== callback) newCallback.__proto__ = callback | ||
return newCallback | ||
}) | ||
trycatch.run = run | ||
trycatch.wrap = wrap | ||
trycatch.configure = configure | ||
trycatch.format = null | ||
module.exports = trycatch |
{ | ||
"name": "trycatch", | ||
"version": "0.2.7", | ||
"version": "0.2.8", | ||
"description": "An asynchronous domain-based exception handler with long stack traces for node.js", | ||
@@ -5,0 +5,0 @@ "homepage": "http://github.com/CrabDude/trycatch", |
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
18
50065
815