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

async-hook-domain

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

async-hook-domain - npm Package Compare versions

Comparing version 1.1.0 to 1.1.1

158

index.js
const { executionAsyncId, createHook } = require('async_hooks')
/* istanbul ignore next */
const debug = process.env.ASYNC_HOOK_DOMAIN_DEBUG !== '1' ? () => {}

@@ -29,4 +28,4 @@ : (() => {

domainHook.enable()
process.on('uncaughtException', uncaughtException)
process.on('unhandledRejection', unhandledRejection)
process.emit = domainProcessEmit
process._fatalException = domainProcessFatalException
}

@@ -39,4 +38,4 @@ }

domainHook = null
process.removeListener('uncaughtException', uncaughtException)
process.removeListener('unhandledRejection', unhandledRejection)
process.emit = realProcessEmit
process._fatalException = realProcessFatalException
}

@@ -50,8 +49,7 @@ }

if (current) {
debug('INIT', id, type, resource, current)
debug('INIT', id, type, current)
current.ids.add(id)
domains.set(id, current)
debug('POST INIT', id, type, current)
}
if (type === 'PROMISE')
activePromise = resource.promise
},

@@ -72,3 +70,3 @@

const domain = domains.get(id)
debug('DESTROY', id, domain)
debug('DESTROY', id, domain && domain.ids)
if (!domain)

@@ -83,63 +81,111 @@ return

// Promise rejection handler
const unhandledRejection = er => {
debug('UNHANDLED REJECTION', er)
const domain = domains.get(executionAsyncId())
|| domains.get(promiseExecutionId)
if (domain) {
try {
domain.onerror(er, 'unhandledRejection')
} catch (e) {
domain.destroy()
uncaughtException(e)
// Dangerous monkey-patching ahead.
// Errors can bubble up to the top level in one of two ways:
// 1. thrown
// 2. promise rejection
//
// Thrown errors are easy. They emit `uncaughtException`, and
// are considered nonfatal if there are listeners that don't throw.
// Managing an event listener is relatively straightforward, but we
// need to recognize when the error ISN'T handled by a domain, and
// make the error fatal, which is tricky but doable.
//
// Promise rejections are harder. They do one of four possible things,
// depending on the --unhandled-rejections argument passed to node.
// - throw:
// - call process._fatalException(er) and THEN emits unhandledRejection
// - emit unhandledRejection
// - if no handlers, warn
// - ignore: emit only
// - always warn: emit event, then warn
// - default:
// - emit event
// - if not handled, print warning and deprecation
//
// When we're ready to make a hard break with the domains builtin, and
// drop support for everything below 12.11.0, it'd be good to do this with
// a process.setUncaughtExceptionCaptureCallback(). However, the downside
// there is that any module that does this has to be a singleton, which
// feels overly pushy for a library like this.
//
// Also, there's been changes in how this all works between v8 and now.
//
// To cover all cases, we monkey-patch process._fatalException and .emit
const _handled = Symbol('handled by async-hook-domain')
const domainProcessEmit = (ev, ...args) => {
if (ev === 'unhandledRejection' || ev === 'unaughtException') {
debug('DOMAIN PROCESS EMIT', ev, ...args)
const er = args[0]
const p = args[1]
// check to see if we have a domain
const fromPromise = ev === 'unhandledRejection'
const domain = currentDomain(fromPromise)
if (domain) {
debug('HAS DOMAIN', domain)
if (promiseFatal) {
// don't need to handle a second time when the event emits
return realProcessEmit.call(process, ev, ...args) || true
}
try {
domain.onerror(er, ev)
} catch (e) {
domain.destroy()
// this is pretty bad. treat it as a fatal exception, which
// may or may not be caught in the next domain up.
// We drop 'from promise', because now it's a throw.
return domainProcessFatalException(e)
}
return realProcessEmit.call(process, ev, ...args) || true
}
} else if (process.listeners('unhandledRejection').length <= 1) {
process.removeListener('unhandledRejection', unhandledRejection)
/* istanbul ignore else */
if (activePromise) {
// we reject it a second time, which is somewhat of a weird
// side effect, but at this point, we know that the promise
// isn't having its rejections handled, or we wouldn't be here.
Promise.reject(er)
setImmediate(() =>
process.on('unhandledRejection', unhandledRejection))
} else {
// treat as fatal, because how could this even happen?
fatalException(er)
}
}
return realProcessEmit.call(process, ev, ...args)
}
// thrown error handler
const uncaughtException = er => {
debug('UNCAUGHT EXCEPTION', er)
const domain = domains.get(executionAsyncId())
const currentDomain = fromPromise =>
domains.get(executionAsyncId()) ||
(fromPromise ? domains.get(promiseExecutionId) : null)
const realProcessEmit = process.emit
let promiseFatal = false
const domainProcessFatalException = (er, fromPromise) => {
debug('_FATAL EXCEPTION', er, fromPromise)
const domain = currentDomain(fromPromise)
if (domain) {
const ev = fromPromise ? 'unhandledRejection' : 'uncaughtException'
// if it's from a promise, then that means --unhandled-rejection=strict
// we don't need to handle it a second time.
promiseFatal = promiseFatal || fromPromise
try {
domain.onerror(er, 'uncaughtException')
threw = false
domain.onerror(er, ev)
} catch (e) {
domain.destroy()
uncaughtException(e)
return domainProcessFatalException(e)
}
} else if (process.listeners('uncaughtException').length <= 1)
fatalException(er)
// we add a handler just to ensure that node knows the event will
// be handled. otherwise we get async hook stack corruption.
if (promiseFatal) {
// don't blow up our process on a promise if we handled it.
return true
}
process.once(ev, () => {})
// this ||true is just a safety guard. it should always be true.
return realProcessFatalException.call(process, er, fromPromise) ||
/* istanbul ignore next */ true
}
return realProcessFatalException.call(process, er, fromPromise)
}
const fatalException = er => {
debug('FATAL')
// prevent infinite recursion
process.removeListener('uncaughtException', uncaughtException)
// set an immediate so that the tick queue isn't empty
// otherwise the stderr printing won't make it out in time.
/* istanbul ignore next */
setImmediate(() => {})
// ~ ~ ~ FINISH HIM ~ ~ ~
process._fatalException(er)
}
const realProcessFatalException = process._fatalException
class Domain {
constructor (onerror) {
if (typeof onerror !== 'function')
throw new TypeError('onerror must be a function')
if (typeof onerror !== 'function') {
// point at where the wrong thing was actually done
const er = new TypeError('onerror must be a function')
Error.captureStackTrace(er, this.constructor)
throw er
}
const eid = executionAsyncId()

@@ -146,0 +192,0 @@ this.ids = new Set([eid])

{
"name": "async-hook-domain",
"version": "1.1.0",
"version": "1.1.1",
"description": "An implementation of Domain-like error handling, built on async_hooks",

@@ -13,10 +13,10 @@ "main": "index.js",

"devDependencies": {
"tap": "^13.0.0-rc.11"
"tap": "^14.6.6"
},
"scripts": {
"test": "tap test/run.js",
"snap": "tap test/run.js",
"test": "tap --node-arg=test/run.js test/fixtures",
"snap": "tap --node-arg=test/run.js test/fixtures",
"preversion": "npm test",
"postversion": "npm publish",
"postpublish": "git push origin --all; git push origin --tags"
"postpublish": "git push origin --follow-tags"
},

@@ -23,0 +23,0 @@ "tap": {

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