agentkeepalive
Advanced tools
Comparing version 3.5.2 to 4.0.0
4.0.0 / 2018-10-23 | ||
================== | ||
**features** | ||
* [[`5c9f3bb`](http://github.com/node-modules/agentkeepalive/commit/5c9f3bbd60555744edcf777105b148982a1a42b6)] - feat: impl the new Agent extend http.Agent (fengmk2 <<fengmk2@gmail.com>>) | ||
**others** | ||
* [[`498c8f1`](http://github.com/node-modules/agentkeepalive/commit/498c8f13cf76600d3dd6e1c91cdf2d8292355dff)] - chore: move LICENSE from readme to file (fengmk2 <<fengmk2@gmail.com>>) | ||
* [[`4f39894`](http://github.com/node-modules/agentkeepalive/commit/4f398942ba2f90cf4501239e56ac4e6344931a01)] - bugfix: support agent.options.timeout on https agent (fengmk2 <<fengmk2@gmail.com>>) | ||
3.5.2 / 2018-10-19 | ||
@@ -3,0 +13,0 @@ ================== |
@@ -18,2 +18,4 @@ declare module "agentkeepalive" { | ||
interface HttpOptions extends http.AgentOptions { | ||
keepAlive?: boolean; | ||
freeSocketTimeout?: number; | ||
freeSocketKeepAliveTimeout?: number; | ||
@@ -25,2 +27,4 @@ timeout?: number; | ||
interface HttpsOptions extends https.AgentOptions { | ||
keepAlive?: boolean; | ||
freeSocketTimeout?: number; | ||
freeSocketKeepAliveTimeout?: number; | ||
@@ -27,0 +31,0 @@ timeout?: number; |
@@ -5,1 +5,2 @@ 'use strict'; | ||
module.exports.HttpsAgent = require('./lib/https_agent'); | ||
module.exports.constants = require('./lib/constants'); |
311
lib/agent.js
@@ -1,14 +0,21 @@ | ||
/** | ||
* refer: | ||
* * @atimb "Real keep-alive HTTP agent": https://gist.github.com/2963672 | ||
* * https://github.com/joyent/node/blob/master/lib/http.js | ||
* * https://github.com/joyent/node/blob/master/lib/https.js | ||
* * https://github.com/joyent/node/blob/master/lib/_http_agent.js | ||
*/ | ||
'use strict'; | ||
const OriginalAgent = require('./_http_agent').Agent; | ||
const OriginalAgent = require('http').Agent; | ||
const ms = require('humanize-ms'); | ||
const debug = require('debug')('agentkeepalive'); | ||
const deprecate = require('depd')('agentkeepalive'); | ||
const { | ||
INIT_SOCKET, | ||
CURRENT_ID, | ||
CREATE_ID, | ||
SOCKET_CREATED_TIME, | ||
SOCKET_NAME, | ||
SOCKET_REQUEST_COUNT, | ||
SOCKET_REQUEST_FINISHED_COUNT, | ||
} = require('./constants'); | ||
// OriginalAgent come from | ||
// - https://github.com/nodejs/node/blob/v8.12.0/lib/_http_agent.js | ||
// - https://github.com/nodejs/node/blob/v10.12.0/lib/_http_agent.js | ||
class Agent extends OriginalAgent { | ||
@@ -19,24 +26,35 @@ constructor(options) { | ||
// default is keep-alive and 15s free socket timeout | ||
if (options.freeSocketKeepAliveTimeout === undefined) { | ||
options.freeSocketKeepAliveTimeout = 15000; | ||
if (options.freeSocketTimeout === undefined) { | ||
options.freeSocketTimeout = 15000; | ||
} | ||
// Legacy API: keepAliveTimeout should be rename to `freeSocketKeepAliveTimeout` | ||
// Legacy API: keepAliveTimeout should be rename to `freeSocketTimeout` | ||
if (options.keepAliveTimeout) { | ||
options.freeSocketKeepAliveTimeout = options.keepAliveTimeout; | ||
deprecate('options.keepAliveTimeout is deprecated, please use options.freeSocketTimeout instead'); | ||
options.freeSocketTimeout = options.keepAliveTimeout; | ||
delete options.keepAliveTimeout; | ||
} | ||
options.freeSocketKeepAliveTimeout = ms(options.freeSocketKeepAliveTimeout); | ||
// Legacy API: freeSocketKeepAliveTimeout should be rename to `freeSocketTimeout` | ||
if (options.freeSocketKeepAliveTimeout) { | ||
deprecate('options.freeSocketKeepAliveTimeout is deprecated, please use options.freeSocketTimeout instead'); | ||
options.freeSocketTimeout = options.freeSocketKeepAliveTimeout; | ||
delete options.freeSocketKeepAliveTimeout; | ||
} | ||
// Sets the socket to timeout after timeout milliseconds of inactivity on the socket. | ||
// By default is double free socket keepalive timeout. | ||
// By default is double free socket timeout. | ||
if (options.timeout === undefined) { | ||
options.timeout = options.freeSocketKeepAliveTimeout * 2; | ||
// make sure socket default inactivity timeout >= 30s | ||
if (options.timeout < 30000) { | ||
options.timeout = 30000; | ||
} | ||
options.timeout = Math.max(options.freeSocketTimeout * 2, 30000); | ||
} | ||
// support humanize format | ||
options.timeout = ms(options.timeout); | ||
options.freeSocketTimeout = ms(options.freeSocketTimeout); | ||
options.socketActiveTTL = options.socketActiveTTL ? ms(options.socketActiveTTL) : 0; | ||
super(options); | ||
this[CURRENT_ID] = 0; | ||
// create socket success counter | ||
this.createSocketCount = 0; | ||
@@ -55,38 +73,134 @@ this.createSocketCountLastCheck = 0; | ||
// request finished counter | ||
this.requestCount = 0; | ||
this.requestCountLastCheck = 0; | ||
// including free socket timeout counter | ||
this.timeoutSocketCount = 0; | ||
this.timeoutSocketCountLastCheck = 0; | ||
} | ||
this.on('free', s => { | ||
this.requestCount++; | ||
// last enter free queue timestamp | ||
s.lastFreeTime = Date.now(); | ||
}); | ||
this.on('timeout', () => { | ||
this.timeoutSocketCount++; | ||
}); | ||
this.on('close', () => { | ||
this.closeSocketCount++; | ||
}); | ||
this.on('error', () => { | ||
this.errorSocketCount++; | ||
}); | ||
get freeSocketKeepAliveTimeout() { | ||
deprecate('agent.freeSocketKeepAliveTimeout is deprecated, please use agent.options.freeSocketTimeout instead'); | ||
return this.options.freeSocketTimeout; | ||
} | ||
createSocket(req, options, cb) { | ||
super.createSocket(req, options, (err, socket) => { | ||
get timeout() { | ||
deprecate('agent.timeout is deprecated, please use agent.options.timeout instead'); | ||
return this.options.timeout; | ||
} | ||
get socketActiveTTL() { | ||
deprecate('agent.socketActiveTTL is deprecated, please use agent.options.socketActiveTTL instead'); | ||
return this.options.socketActiveTTL; | ||
} | ||
keepSocketAlive(socket) { | ||
const result = super.keepSocketAlive(socket); | ||
// should not keepAlive, do nothing | ||
if (!result) return result; | ||
let freeSocketTimeout = this.options.freeSocketTimeout; | ||
const socketActiveTTL = this.options.socketActiveTTL; | ||
if (socketActiveTTL) { | ||
// check socketActiveTTL | ||
const aliveTime = Date.now() - socket[SOCKET_CREATED_TIME]; | ||
const diff = socketActiveTTL - aliveTime; | ||
// destroy it | ||
if (diff <= 0) { | ||
debug('%s(requests: %s, finished: %s) free but need to destroy by TTL, alive %sms(max %sms)', | ||
socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], socket[SOCKET_REQUEST_FINISHED_COUNT], | ||
aliveTime, socketActiveTTL); | ||
return false; | ||
} | ||
if (freeSocketTimeout && diff < freeSocketTimeout) { | ||
debug('%s(requests: %s, finished: %s) free and wait for %sms TTL timeout', | ||
socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], socket[SOCKET_REQUEST_FINISHED_COUNT], | ||
diff); | ||
freeSocketTimeout = diff; | ||
} | ||
} | ||
// set freeSocketTimeout | ||
if (freeSocketTimeout) { | ||
// set free keepalive timer | ||
// try to use socket custom freeSocketTimeout first, support headers['keep-alive'] | ||
// https://github.com/node-modules/urllib/blob/b76053020923f4d99a1c93cf2e16e0c5ba10bacf/lib/urllib.js#L498 | ||
const customFreeSocketTimeout = socket.freeSocketTimeout || socket.freeSocketKeepAliveTimeout; | ||
if (customFreeSocketTimeout && customFreeSocketTimeout < freeSocketTimeout) { | ||
freeSocketTimeout = customFreeSocketTimeout; | ||
} | ||
// FIXME: need to make setRequestSocket as a method on Agent class | ||
// then we can reset the agent.options.timeout when free socket is reused. | ||
socket.setTimeout(freeSocketTimeout); | ||
} | ||
return true; | ||
} | ||
// only call on addRequest | ||
reuseSocket(...args) { | ||
// reuseSocket(socket, req) | ||
super.reuseSocket(...args); | ||
const socket = args[0]; | ||
const agentTimeout = this.options.timeout; | ||
if (getSocketTimeout(socket) !== agentTimeout) { | ||
// reset timeout before use | ||
socket.setTimeout(agentTimeout); | ||
debug('%s reset timeout to %sms', socket[SOCKET_NAME], agentTimeout); | ||
} | ||
socket[SOCKET_REQUEST_COUNT]++; | ||
debug('%s(requests: %s, finished: %s) reuse on addRequest, timeout %sms', | ||
socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], socket[SOCKET_REQUEST_FINISHED_COUNT], | ||
getSocketTimeout(socket)); | ||
} | ||
[CREATE_ID]() { | ||
const id = this[CURRENT_ID]++; | ||
if (this[CURRENT_ID] === Number.MAX_SAFE_INTEGER) this[CURRENT_ID] = 0; | ||
return id; | ||
} | ||
[INIT_SOCKET](socket, options) { | ||
// bugfix here. | ||
// https on node 8, 10 won't set agent.options.timeout by default | ||
// TODO: need to fix on node itself | ||
if (options.timeout) { | ||
const timeout = getSocketTimeout(socket); | ||
if (!timeout) { | ||
socket.setTimeout(options.timeout); | ||
} | ||
} | ||
if (this.keepAlive) { | ||
// Disable Nagle's algorithm: http://blog.caustik.com/2012/04/08/scaling-node-js-to-100k-concurrent-connections/ | ||
// https://fengmk2.com/benchmark/nagle-algorithm-delayed-ack-mock.html | ||
socket.setNoDelay(true); | ||
} | ||
this.createSocketCount++; | ||
if (this.options.socketActiveTTL) { | ||
socket[SOCKET_CREATED_TIME] = Date.now(); | ||
} | ||
// don't show the hole '-----BEGIN CERTIFICATE----' key string | ||
socket[SOCKET_NAME] = `sock[${this[CREATE_ID]()}#${options._agentKey}]`.split('-----BEGIN', 1)[0]; | ||
socket[SOCKET_REQUEST_COUNT] = 1; | ||
socket[SOCKET_REQUEST_FINISHED_COUNT] = 0; | ||
installListeners(this, socket, options); | ||
} | ||
createConnection(options, oncreate) { | ||
let called = false; | ||
const onNewCreate = (err, socket) => { | ||
if (called) return; | ||
called = true; | ||
if (err) { | ||
this.createSocketErrorCount++; | ||
return cb(err); | ||
return oncreate(err); | ||
} | ||
if (this.keepAlive) { | ||
// Disable Nagle's algorithm: http://blog.caustik.com/2012/04/08/scaling-node-js-to-100k-concurrent-connections/ | ||
// https://fengmk2.com/benchmark/nagle-algorithm-delayed-ack-mock.html | ||
socket.setNoDelay(true); | ||
} | ||
this.createSocketCount++; | ||
cb(null, socket); | ||
}); | ||
this[INIT_SOCKET](socket, options); | ||
oncreate(err, socket); | ||
}; | ||
const newSocket = super.createConnection(options, onNewCreate); | ||
if (newSocket) onNewCreate(null, newSocket); | ||
} | ||
@@ -127,2 +241,111 @@ | ||
// node 8 don't has timeout attribute on socket | ||
// https://github.com/nodejs/node/pull/21204/files#diff-e6ef024c3775d787c38487a6309e491dR408 | ||
function getSocketTimeout(socket) { | ||
return socket.timeout || socket._idleTimeout; | ||
} | ||
function installListeners(agent, socket, options) { | ||
debug('%s create, timeout %sms', socket[SOCKET_NAME], getSocketTimeout(socket)); | ||
// listener socket events: close, timeout, error, free | ||
function onFree() { | ||
// create and socket.emit('free') logic | ||
// https://github.com/nodejs/node/blob/master/lib/_http_agent.js#L311 | ||
// no req on the socket, it should be the new socket | ||
if (!socket._httpMessage && socket[SOCKET_REQUEST_COUNT] === 1) return; | ||
socket[SOCKET_REQUEST_FINISHED_COUNT]++; | ||
agent.requestCount++; | ||
debug('%s(requests: %s, finished: %s) free', | ||
socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], socket[SOCKET_REQUEST_FINISHED_COUNT]); | ||
// should reuse on pedding requests? | ||
const name = agent.getName(options); | ||
if (socket.writable && agent.requests[name] && agent.requests[name].length) { | ||
// will be reuse on agent free listener | ||
socket[SOCKET_REQUEST_COUNT]++; | ||
debug('%s(requests: %s, finished: %s) will be reuse on agent free event', | ||
socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], socket[SOCKET_REQUEST_FINISHED_COUNT]); | ||
} | ||
} | ||
socket.on('free', onFree); | ||
function onClose(isError) { | ||
debug('%s(requests: %s, finished: %s) close, isError: %s', | ||
socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], socket[SOCKET_REQUEST_FINISHED_COUNT], isError); | ||
agent.closeSocketCount++; | ||
} | ||
socket.on('close', onClose); | ||
// start socket timeout handler | ||
function onTimeout() { | ||
const listenerCount = socket.listeners('timeout').length; | ||
debug('%s(requests: %s, finished: %s) timeout after %sms, listeners %s', | ||
socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], socket[SOCKET_REQUEST_FINISHED_COUNT], | ||
getSocketTimeout(socket), listenerCount); | ||
agent.timeoutSocketCount++; | ||
const name = agent.getName(options); | ||
if (agent.freeSockets[name] && agent.freeSockets[name].indexOf(socket) !== -1) { | ||
// free socket timeout, destroy quietly | ||
socket.destroy(); | ||
// Remove it from freeSockets list immediately to prevent new requests | ||
// from being sent through this socket. | ||
agent.removeSocket(socket, options); | ||
debug('%s is free, destroy quietly', socket[SOCKET_NAME]); | ||
} else { | ||
// if there is no any request socket timeout handler, | ||
// agent need to handle socket timeout itself. | ||
// | ||
// custom request socket timeout handle logic must follow these rules: | ||
// 1. Destroy socket first | ||
// 2. Must emit socket 'agentRemove' event tell agent remove socket | ||
// from freeSockets list immediately. | ||
// Otherise you may be get 'socket hang up' error when reuse | ||
// free socket and timeout happen in the same time. | ||
if (listenerCount === 1) { | ||
const error = new Error('Socket timeout'); | ||
error.code = 'ERR_SOCKET_TIMEOUT'; | ||
error.timeout = getSocketTimeout(socket); | ||
// must manually call socket.end() or socket.destroy() to end the connection. | ||
// https://nodejs.org/dist/latest-v10.x/docs/api/net.html#net_socket_settimeout_timeout_callback | ||
socket.destroy(error); | ||
agent.removeSocket(socket, options); | ||
debug('%s destroy with timeout error', socket[SOCKET_NAME]); | ||
} | ||
} | ||
} | ||
socket.on('timeout', onTimeout); | ||
function onError(err) { | ||
const listenerCount = socket.listeners('error').length; | ||
debug('%s(requests: %s, finished: %s) error: %s, listenerCount: %s', | ||
socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], socket[SOCKET_REQUEST_FINISHED_COUNT], | ||
err, listenerCount); | ||
agent.errorSocketCount++; | ||
if (listenerCount === 1) { | ||
// if socket don't contain error event handler, don't catch it, emit it again | ||
debug('%s emit uncaught error event', socket[SOCKET_NAME]); | ||
socket.removeListener('error', onError); | ||
socket.emit('error', err); | ||
} | ||
} | ||
socket.on('error', onError); | ||
function onRemove() { | ||
debug('%s(requests: %s, finished: %s) agentRemove', | ||
socket[SOCKET_NAME], | ||
socket[SOCKET_REQUEST_COUNT], socket[SOCKET_REQUEST_FINISHED_COUNT]); | ||
// We need this function for cases like HTTP 'upgrade' | ||
// (defined by WebSockets) where we need to remove a socket from the | ||
// pool because it'll be locked up indefinitely | ||
socket.removeListener('close', onClose); | ||
socket.removeListener('error', onError); | ||
socket.removeListener('free', onFree); | ||
socket.removeListener('timeout', onTimeout); | ||
socket.removeListener('agentRemove', onRemove); | ||
} | ||
socket.on('agentRemove', onRemove); | ||
} | ||
module.exports = Agent; | ||
@@ -129,0 +352,0 @@ |
@@ -1,10 +0,9 @@ | ||
/** | ||
* Https Agent base on custom http agent | ||
*/ | ||
'use strict'; | ||
const https = require('https'); | ||
const OriginalHttpsAgent = require('https').Agent; | ||
const HttpAgent = require('./agent'); | ||
const OriginalHttpsAgent = https.Agent; | ||
const { | ||
INIT_SOCKET, | ||
CREATE_HTTPS_CONNECTION, | ||
} = require('./constants'); | ||
@@ -18,2 +17,3 @@ class HttpsAgent extends HttpAgent { | ||
this.maxCachedSessions = this.options.maxCachedSessions; | ||
/* istanbul ignore next */ | ||
if (this.maxCachedSessions === undefined) { | ||
@@ -28,6 +28,14 @@ this.maxCachedSessions = 100; | ||
} | ||
createConnection(options) { | ||
const socket = this[CREATE_HTTPS_CONNECTION](options); | ||
this[INIT_SOCKET](socket, options); | ||
return socket; | ||
} | ||
} | ||
// https://github.com/nodejs/node/blob/master/lib/https.js#L89 | ||
HttpsAgent.prototype[CREATE_HTTPS_CONNECTION] = OriginalHttpsAgent.prototype.createConnection; | ||
[ | ||
'createConnection', | ||
'getName', | ||
@@ -39,2 +47,3 @@ '_getSession', | ||
].forEach(function(method) { | ||
/* istanbul ignore next */ | ||
if (typeof OriginalHttpsAgent.prototype[method] === 'function') { | ||
@@ -41,0 +50,0 @@ HttpsAgent.prototype[method] = OriginalHttpsAgent.prototype[method]; |
{ | ||
"name": "agentkeepalive", | ||
"version": "3.5.2", | ||
"version": "4.0.0", | ||
"description": "Missing keepalive http.Agent", | ||
@@ -14,3 +14,4 @@ "main": "index.js", | ||
"scripts": { | ||
"test": "egg-bin test", | ||
"test": "npm run lint && egg-bin test", | ||
"test-local": "egg-bin test", | ||
"cov": "egg-bin cov", | ||
@@ -33,5 +34,9 @@ "ci": "npm run lint && npm run cov", | ||
"keepalive", | ||
"agentkeepalive" | ||
"agentkeepalive", | ||
"HttpAgent", | ||
"HttpsAgent" | ||
], | ||
"dependencies": { | ||
"debug": "^4.1.0", | ||
"depd": "^1.1.2", | ||
"humanize-ms": "^1.2.1" | ||
@@ -41,13 +46,14 @@ }, | ||
"autod": "^3.0.1", | ||
"egg-bin": "^1.11.1", | ||
"egg-ci": "^1.8.0", | ||
"eslint": "^4.19.1", | ||
"eslint-config-egg": "^6.0.0", | ||
"egg-bin": "^4.9.0", | ||
"egg-ci": "^1.10.0", | ||
"eslint": "^5.7.0", | ||
"eslint-config-egg": "^7.1.0", | ||
"mm": "^2.4.1", | ||
"pedding": "^1.1.0" | ||
}, | ||
"engines": { | ||
"node": ">= 4.0.0" | ||
"node": ">= 8.0.0" | ||
}, | ||
"ci": { | ||
"version": "4, 6, 8, 10" | ||
"version": "8, 10" | ||
}, | ||
@@ -54,0 +60,0 @@ "author": "fengmk2 <fengmk2@gmail.com> (https://fengmk2.com)", |
@@ -26,3 +26,3 @@ # agentkeepalive | ||
The Node.js's missing `keep alive` `http.Agent`. Support `http` and `https`. | ||
The enhancement features `keep alive` `http.Agent`. Support `http` and `https`. | ||
@@ -35,3 +35,8 @@ ## What's different from original `http.Agent`? | ||
- Add active socket timeout: avoid long time inactivity socket leak in the active-sockets queue. | ||
- TTL for active socket. | ||
## Node.js version required | ||
Support Node.js >= `8.0.0` | ||
## Install | ||
@@ -52,4 +57,4 @@ | ||
Default = `1000`. Only relevant if `keepAlive` is set to `true`. | ||
* `freeSocketKeepAliveTimeout`: {Number} Sets the free socket to timeout | ||
after `freeSocketKeepAliveTimeout` milliseconds of inactivity on the free socket. | ||
* `freeSocketTimeout`: {Number} Sets the free socket to timeout | ||
after `freeSocketTimeout` milliseconds of inactivity on the free socket. | ||
Default is `15000`. | ||
@@ -59,3 +64,3 @@ Only relevant if `keepAlive` is set to `true`. | ||
after `timeout` milliseconds of inactivity on the working socket. | ||
Default is `freeSocketKeepAliveTimeout * 2`. | ||
Default is `freeSocketTimeout * 2`. | ||
* `maxSockets` {Number} Maximum number of sockets to allow per | ||
@@ -79,4 +84,4 @@ host. Default = `Infinity`. | ||
maxFreeSockets: 10, | ||
timeout: 60000, | ||
freeSocketKeepAliveTimeout: 30000, // free socket keepalive for 30 seconds | ||
timeout: 60000, // active socket keepalive for 60 seconds | ||
freeSocketTimeout: 30000, // free socket keepalive for 30 seconds | ||
}); | ||
@@ -219,3 +224,3 @@ | ||
``` | ||
```bash | ||
[proxy.js:120000] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 0 sockets, 0 unusedSockets, 50 timeout | ||
@@ -230,26 +235,2 @@ {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} | ||
``` | ||
(The MIT License) | ||
Copyright(c) node-modules and other contributors. | ||
Copyright(c) 2012 - 2015 fengmk2 <fengmk2@gmail.com> | ||
Permission is hereby granted, free of charge, to any person obtaining | ||
a copy of this software and associated documentation files (the | ||
'Software'), to deal in the Software without restriction, including | ||
without limitation the rights to use, copy, modify, merge, publish, | ||
distribute, sublicense, and/or sell copies of the Software, and to | ||
permit persons to whom the Software is furnished to do so, subject to | ||
the following conditions: | ||
The above copyright notice and this permission notice shall be | ||
included in all copies or substantial portions of the Software. | ||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, | ||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
``` | ||
[MIT](LICENSE) |
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
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
Network access
Supply chain riskThis module accesses the network.
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
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
10
32071
3
7
422
230
3
+ Addeddebug@^4.1.0
+ Addeddepd@^1.1.2
+ Addeddebug@4.3.7(transitive)
+ Addeddepd@1.1.2(transitive)