New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

trycatch

Package Overview
Dependencies
Maintainers
1
Versions
75
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

trycatch - npm Package Compare versions

Comparing version

to
0.2.0

225

lib/trycatch.js
var util = require('util')
, domain = require('domain')
, FormatStackTrace = require('./formatStackTrace')
, Block = require('./control-block')
, filename1 = __filename
, filename2 = require.resolve('./hook')
, hook = require('./hook')
, hooked = false
, fileNameFilter = [__filename, require.resolve('./hook'), 'domain.js']
, options = {
'long-stack-traces': false
'long-stack-traces': null
}

@@ -16,27 +17,135 @@

function trycatch(tryFn, catchFn) {
if ('function' !== typeof tryFn || 'function' !== typeof catchFn) {
throw new Error('tryFn and catchFn must be functions')
}
trycatch.begin(tryFn, catchFn)
}
trycatch.begin = Block.begin
trycatch.guard = Block.guard
trycatch.begin = domainTrycatch
// Won't be used until long-stack-traces turned off=>on=>off
trycatch.guard = nopGuard
trycatch.configure = configure
function configure(opts) {
util._extend(options, opts)
if ('undefined' !== typeof opts['long-stack-traces']) {
options['long-stack-traces'] = Boolean(opts['long-stack-traces'])
}
if (!!opts['long-stack-traces']) {
if (true === options['long-stack-traces']) {
if (!hooked) {
hooked = true
// Replace built-in async functions, shim callbacks with generator
hookit()
}
// findToken fails when _TOKEN_ deeper than Error.stackTraceLimit
Error.stackTraceLimit = Infinity
trycatch.guard = longStackTracesGuard
trycatch.begin = begin
} else if (typeof opts['long-stack-traces'] !== 'undefined') {
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 = Block.begin
trycatch.guard = Block.guard
trycatch.begin = domainTrycatch
trycatch.guard = nopGuard
}
}
function begin(tryFn, catchFn) {
function hookit() {
hook(function generateShim(callback, fnName) {
return trycatch.guard(callback, fnName)
})
}
// In case long-stack-traces are switched on -> off
function nopGuard(cb) {
return cb
}
function domainTrycatch (fn, cb, token) {
var parentDomain = domain.active
, d = domain.create()
d.on('error', function onError(err) {
var isError = err instanceof Error
if (true === options['long-stack-traces'] && '_TOKEN_' === fn.name) {
if (isError) {
err = handleError(err)
} else {
console.warn('Unable to generate long-stack-trace for thrown non-Error')
}
}
if (!isError) {
err = new Error(err)
}
runInDomain(parentDomain, function() {
cb(err)
})
})
runInDomain(d, fn)
}
function runInDomain(d, fn) {
if (d && !d._disposed) {
try {
d.run(fn)
} catch(e) {
d.emit('error', e)
}
} else {
fn()
}
}
function handleError(err) {
var parent
, token
, skip = false
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
}
while(token.error) {
if (!skip && token.catchFn) break
skip = false
// HACK: Use Error.prepareStackTrace = stackSearch to find parent
parent = generateStack(token.error, stackSearch).stack
if (!parent) {
throw err
}
if (!token.catchFn && parent.stack) {
err.stack += '\n ----------------------------------------\n' +
' at '+token.parent+'\n' +
parent.stack.substring(parent.stack.indexOf("\n") + 1)
}
token = parent.token
}
if ('function' === typeof token.catchFn) {
err.token = token
return err
}
}
function longStackTraceBegin(tryFn, catchFn) {
// Create origin _TOKEN_ for stack termination
function _TOKEN_() {
tryFn()
tryFn()
}

@@ -47,15 +156,11 @@ _TOKEN_.catchFn = catchFn

try {
_TOKEN_()
} catch (err) {
catchFn(generateStack(err))
}
domainTrycatch(_TOKEN_, catchFn)
}
function generateStack(err, fn) {
if (typeof fn !== 'function') {
if ('function' !== typeof fn) {
fn = function(error, structuredStackTrace) {
return FormatStackTrace(error
, structuredStackTrace
, [filename1, filename2]
, fileNameFilter
, trycatch.colors)

@@ -67,2 +172,3 @@ }

Error.prepareStackTrace = fn
// Generate stack trace by accessing stack property
err.stack = err.stack

@@ -74,12 +180,12 @@ Error.prepareStackTrace = old

// Generate a new callback wrapped in _TOKEN_ with Error to trace back
function longStackTracesGuard(next, name, location) {
if (typeof next !== 'function') return next
function longStackTraceGuard(callback, name) {
var parentDomain
// _TOKEN_ is the new callback and calls the real callback, next()
if ('function' !== typeof callback) return callback
parentDomain = domain.active
// _TOKEN_ is the new callback and calls the real callback, callback()
function _TOKEN_() {
try {
return next.apply(this, arguments)
} catch (e) {
handleError(e, _TOKEN_, false)
}
callback.apply(this, arguments)
}

@@ -90,60 +196,27 @@

return _TOKEN_
}
function handleError(err, token, recursive) {
var parent
if (!recursive) {
if (!err.token) {
// Newly created Error
err = err instanceof Error ? err : new Error(''+err)
err = generateStack(err)
err.parentalStack = err.stack
} else {
token = err.token
}
return function() {
var args = arguments
runInDomain(parentDomain, function() {
_TOKEN_.apply(this, args)
})
}
while(token.error) {
// HACK: Use Error.prepareStackTrace = stackSearch to find parent
parent = generateStack(token.error, stackSearch).stack
if (!parent) throw err
if (!token.catchFn && parent.stack) {
err.stack += '\n ----------------------------------------\n' +
' at '+token.parent+'\n' +
parent.stack.substring(parent.stack.indexOf("\n") + 1)
}
token = parent.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)
}
}
}
function stackSearch(error, structuredStackTrace) {
var stack
if (!structuredStackTrace) return
stack = FormatStackTrace(error, structuredStackTrace, fileNameFilter, trycatch.colors)
for (var fn, i=0, l=structuredStackTrace.length; i<l; i++) {
fn = structuredStackTrace[i].fun
if (fn.name === '_TOKEN_') {
if ('_TOKEN_' === fn.name) {
return {
token: fn,
stack: FormatStackTrace(error, structuredStackTrace, [filename1, filename2], trycatch.colors)
stack: stack
}
}
}
}
// Replace built-in async functions, shim callbacks with generator
require('./hook')(function generateShim(next, name, location) {
return trycatch.guard(next, name, location)
})
return {stack: stack}
}
{
"name": "trycatch",
"version": "0.1.5",
"description": "An asynchronous exception handler with long stack traces for node.js",
"version": "0.2.0",
"description": "An asynchronous domain-based exception handler with long stack traces for node.js",
"homepage": "http://github.com/CrabDude/trycatch",

@@ -19,4 +19,5 @@ "repository": {

"engines": {
"node": ">=0.2.0"
"node": ">=0.9.5"
},
"engineStrict": true,
"scripts": {

@@ -23,0 +24,0 @@ "test": "mocha --reporter spec ./test/*.js"

@@ -15,16 +15,29 @@ var trycatch = require('../lib/trycatch')

trycatch.configure({
'long-stack-traces': !!longStackTraces
'long-stack-traces': Boolean(longStackTraces)
})
})
it('should require tryFn to be a function', function() {
assert.throws(function() {
trycatch(null, function() {})
}, Error)
})
it('should require catchFn to be a function', function() {
assert.throws(function() {
trycatch(function() {}, null)
}, Error)
})
it('should catch Error object thrown synchronously', function(done) {
trycatch(function() {
(function foo() {
throw new Error('Sync')
})()
}, function(err) {
assert.equal(err.message, 'Sync')
assert.notEqual(err.stack, undefined)
done()
})
(function foo() {
throw new Error('Sync')
})()
}
, function(err) {
assert.equal(err.message, 'Sync')
assert.notEqual(err.stack, undefined)
done()
})
})

@@ -34,10 +47,11 @@

trycatch(function() {
process.nextTick(function() {
throw new Error('Async')
process.nextTick(function() {
throw new Error('Async')
})
}
, function(err) {
assert.equal(err.message, 'Async')
assert.notEqual(err.stack, undefined)
done()
})
}, function(err) {
assert.equal(err.message, 'Async')
assert.notEqual(err.stack, undefined)
done()
})
})

@@ -48,2 +62,2 @@ })

run(false)
run(true)
run(true)

@@ -19,3 +19,3 @@ var trycatch = require('../lib/trycatch')

trycatch.configure({
'long-stack-traces': !!longStackTraces
'long-stack-traces': Boolean(longStackTraces)
})

@@ -101,2 +101,2 @@ })

run(false)
run(true)
run(true)

@@ -16,3 +16,3 @@ var assert = require('assert'),

trycatch.configure({
'long-stack-traces': !!longStackTraces
'long-stack-traces': Boolean(longStackTraces)
})

@@ -28,19 +28,21 @@ })

trycatch(function () {
++count
throw new Error('test 1')
}
, function(err) {
++count
assert.equal(err.stack.split(delimitter).length, 1)
throw err
})
}
, function(err) {
++count
throw new Error('test 1')
}, function(err) {
++count
// assert.equal(err.stack.split(delimitter).length, 1)
throw err;
assert.notEqual(err.name, 'AssertionError')
assert.equal(err.message, 'test 1')
assert.equal(count, 3)
if (longStackTraces) {
assert.equal(err.stack.split(delimitter).length, 1)
}
done()
})
}, function(err) {
++count
assert.notEqual(err.name, 'AssertionError')
assert.equal(err.message, 'test 1')
assert.equal(count, 3)
if (longStackTraces) {
assert.equal(err.stack.split(delimitter).length, 1)
}
done();
})
})

@@ -52,27 +54,29 @@

trycatch(function () {
setTimeout(function() {
trycatch(function () {
setTimeout(function() {
++count
throw new Error('test 2')
}, 0)
}, function(err) {
++count
if (longStackTraces) {
assert.equal(err.stack.split(delimitter).length, 2)
}
throw err;
})
}, 0)
}, function(err) {
++count
assert.notEqual(err.name, 'AssertionError')
assert.equal(err.message, 'test 2')
assert.equal(count, 3)
if (longStackTraces) {
assert.equal(err.stack.split(delimitter).length, 3)
setTimeout(function() {
trycatch(function () {
setTimeout(function() {
++count
throw new Error('test 2')
}, 0)
}
, function(err) {
++count
if (longStackTraces) {
assert.equal(err.stack.split(delimitter).length, 2)
}
throw err
})
}, 0)
}
done();
})
, function(err) {
++count
assert.notEqual(err.name, 'AssertionError')
assert.equal(err.message, 'test 2')
assert.equal(count, 3)
if (longStackTraces) {
assert.equal(err.stack.split(delimitter).length, 3)
}
done()
})
})

@@ -84,30 +88,32 @@

trycatch(function () {
setTimeout(function() {
trycatch(function () {
setTimeout(function() {
++count
throw new Error('test 3')
}, 0)
}, function(err) {
setTimeout(function() {
++count
if (longStackTraces) {
assert.equal(err.stack.split(delimitter).length, 2)
setTimeout(function() {
trycatch(function () {
setTimeout(function() {
++count
throw new Error('test 3')
}, 0)
}
throw err;
}, 0)
})
}, 0)
}, function(err) {
++count
assert.notEqual(err.name, 'AssertionError')
assert.equal(err.message, 'test 3')
assert.equal(count, 3)
if (longStackTraces) {
assert.equal(err.stack.split(delimitter).length, 3)
, function(err) {
setTimeout(function() {
++count
if (longStackTraces) {
assert.equal(err.stack.split(delimitter).length, 2)
}
throw err
}, 0)
})
}, 0)
}
done();
})
, function(err) {
++count
assert.notEqual(err.name, 'AssertionError')
assert.equal(err.message, 'test 3')
assert.equal(count, 3)
if (longStackTraces) {
assert.equal(err.stack.split(delimitter).length, 3)
}
done()
})
})

@@ -118,2 +124,2 @@ })

run(false)
run(true)
run(true)

@@ -20,3 +20,3 @@ var assert = require('assert'),

trycatch.configure({
'long-stack-traces': !!longStackTraces
'long-stack-traces': Boolean(longStackTraces)
})

@@ -27,10 +27,11 @@ })

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()
})
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()
})
})

@@ -40,10 +41,11 @@

trycatch(function () {
setTimeout(function () {
throw 123
}, 0)
}, function onError(err) {
assert.equal(err.message, (123).toString())
assert.notEqual(err.stack, undefined)
done()
})
setTimeout(function () {
throw 123
}, 0)
}
, function onError(err) {
assert.equal(err.message, String(123))
assert.notEqual(err.stack, undefined)
done()
})
})

@@ -55,10 +57,11 @@

trycatch(function () {
setTimeout(function () {
throw true
}, 0)
}, function onError(err) {
assert.equal(err.message, (true).toString())
assert.notEqual(err.stack, undefined)
done()
})
setTimeout(function () {
throw true
}, 0)
}
, function onError(err) {
assert.equal(err.message, String(true))
assert.notEqual(err.stack, undefined)
done()
})
})

@@ -69,2 +72,3 @@ })

run(false)
run(true)
// Unable to generate long-stack-traces: Throwing non-Errors is incompatible with domains
run(true)