Comparing version 1.3.2 to 1.4.0
128
api.js
'use strict'; | ||
var path = require('path') | ||
, util = require('util') | ||
, logger = require(path.join(__dirname, 'lib', 'logger')).child({component : 'api'}) | ||
@@ -8,2 +9,28 @@ , NAMES = require(path.join(__dirname, 'lib', 'metrics', 'names')) | ||
/* | ||
* | ||
* CONSTANTS | ||
* | ||
*/ | ||
var RUM_STUB = "<script type='text/javascript'>window.NREUM||(NREUM={});" + | ||
"NREUM.info = %s; %s</script>"; | ||
// these messages are used in the _gracefail() method below in getBrowserTimingHeader | ||
var RUM_ISSUES = [ | ||
'NREUM: no browser monitoring headers generated; disabled', | ||
'NREUM: transaction missing while generating browser monitoring headers', | ||
'NREUM: conf.browser_monitoring missing, something is probably wrong', | ||
'NREUM: browser_monitoring headers need a transaction name', | ||
'NREUM: browser_monitoring requires valid application_id', | ||
'NREUM: browser_monitoring requires valid browser_key' | ||
]; | ||
function _rumObfuscate(string, license_key) { | ||
var bytes = new Buffer(string); | ||
for (var i = 0; i < bytes.length; i++) { | ||
bytes[i] = bytes[i] ^ license_key[i % 13].charCodeAt(0); | ||
} | ||
return bytes.toString('base64'); | ||
} | ||
/** | ||
@@ -175,2 +202,103 @@ * The exported New Relic API. This contains all of the functions meant to be | ||
/** | ||
* Get the <script>...</script> header necessary for Browser Monitoring | ||
* This script must be manually injected into your templates, as high as possible | ||
* in the header, but _after_ any X-UA-COMPATIBLE HTTP-EQUIV meta tags. | ||
* Otherwise you may hurt IE! | ||
* | ||
* This method must be called _during_ a transaction, and must be called every | ||
* time you want to generate the headers. | ||
* | ||
* Do *not* reuse the headers between users, or even between requests. | ||
* | ||
* @returns {string} the <script> header to be injected | ||
*/ | ||
API.prototype.getBrowserTimingHeader = function () { | ||
var conf = this.agent.config; | ||
/* Gracefully fail. | ||
* | ||
* Output an HTML comment and log a warning the comment is meant to be | ||
* innocuous to the end user. | ||
*/ | ||
function _gracefail(num){ | ||
logger.warn(RUM_ISSUES[num]); | ||
return '<!-- NREUM: (' + num + ') -->'; | ||
} | ||
var browser_monitoring = conf.browser_monitoring; | ||
// conf.browser_monitoring should always exist, but we don't want the agent to bail | ||
// here if something goes wrong | ||
if (!browser_monitoring) return _gracefail(2); | ||
/* Can control header generation with configuration this setting is only | ||
* available in the newrelic.js config file, it is not ever set by the | ||
* server. | ||
*/ | ||
if (!browser_monitoring.enable) return _gracefail(0); | ||
var trans = this.agent.getTransaction(); | ||
// bail gracefully outside a transaction | ||
if (!trans) return _gracefail(1); | ||
var name = trans.partialName; | ||
/* If we're in an unnamed transaction, add a friendly warning this is to | ||
* avoid people going crazy, trying to figure out why browser monitoring is | ||
* not working when they're missing a transaction name. | ||
*/ | ||
if (!name) return _gracefail(3); | ||
var time = trans.timer.getDurationInMillis(); | ||
var key = conf.license_key; | ||
var appid = conf.application_id; | ||
/* This is only going to work if the agent has successfully handshaked with | ||
* the collector. If the networks is bad, or there is no license key set in | ||
* newrelis.js, there will be no application_id set. We bail instead of | ||
* outputting null/undefined configuration values. | ||
*/ | ||
if (!appid) return _gracefail(4); | ||
/* If there is no browser_key, the server has likely decided to disable | ||
* browser monitoring. | ||
*/ | ||
var licenseKey = browser_monitoring.browser_key; | ||
if (!licenseKey) return _gracefail(5); | ||
// This hash gets written directly into the browser. | ||
var rum_hash = { | ||
agent : browser_monitoring.js_agent_file, | ||
beacon : browser_monitoring.beacon, | ||
errorBeacon : browser_monitoring.error_beacon, | ||
licenseKey : licenseKey, | ||
applicationID : appid, | ||
applicationTime : time, | ||
transactionName : _rumObfuscate(name, key), | ||
queueTime : trans.queueTime, | ||
// we don't use these parameters yet | ||
agentToken : null, | ||
ttGuid : "" | ||
}; | ||
// if debugging, do pretty format of JSON | ||
var tabs = conf.browser_monitoring.debug ? 2 : 0 | ||
, json = JSON.stringify(rum_hash, null, tabs) | ||
; | ||
// the complete header to be written to the browser | ||
var out = util.format( | ||
RUM_STUB, | ||
json, | ||
browser_monitoring.js_agent_loader | ||
); | ||
logger.trace('generating RUM header', out); | ||
return out; | ||
}; | ||
module.exports = API; |
@@ -6,2 +6,3 @@ 'use strict'; | ||
module.exports = function facts(agent) { | ||
var bm = agent.config.browser_monitoring; | ||
return { | ||
@@ -13,4 +14,15 @@ pid : process.pid, | ||
agent_version : agent.version, | ||
environment : agent.environment | ||
environment : agent.environment, | ||
settings : { | ||
// this means RUM on/off | ||
"browser_monitoring.auto_instrument" : bm.enable, | ||
// these are fixed parameters for now | ||
"browser_monitoring.loader" : bm.loader, | ||
"browser_monitoring.loader_version" : bm.loader_version, | ||
// un-minify RUM loader | ||
"browser_monitoring.debug" : bm.debug | ||
} | ||
}; | ||
}; |
@@ -7,8 +7,10 @@ 'use strict'; | ||
, http = require('http') | ||
, https = require('https') | ||
, deflate = require('zlib').deflate | ||
, lookup = require('dns').lookup | ||
, logger = require(path.join(__dirname, '..', 'logger.js')) | ||
.child({component : 'new_relic_call'}) | ||
.child({component : 'remote_method_invoke'}) | ||
, parse = require(path.join(__dirname, 'parse-response.js')) | ||
, Sink = require(path.join(__dirname, '..', 'util', 'stream-sink')) | ||
, Sink = require(path.join(__dirname, '..', 'util', 'stream-sink.js')) | ||
, agents = require(path.join(__dirname, 'http-agents.js')) | ||
; | ||
@@ -148,5 +150,6 @@ | ||
var protocol = sender._config.ssl ? 'https' : 'http'; | ||
logger.trace( | ||
{body : options.body}, | ||
"Posting to http://%s:%s%s", | ||
"Posting to " + protocol + "://%s:%s%s", | ||
options.host, | ||
@@ -176,3 +179,3 @@ options.port, | ||
RemoteMethod.prototype._request = function _request(options) { | ||
var request = http.request({ | ||
var requestOptions = { | ||
method : 'POST', | ||
@@ -185,4 +188,14 @@ setHost : false, // see below | ||
__NR__connection : true // who measures the metrics measurer? | ||
}); | ||
}; | ||
var request; | ||
if (this._config.ssl) { | ||
requestOptions.agent = agents.https; | ||
request = https.request(requestOptions); | ||
} | ||
else { | ||
requestOptions.agent = agents.http; | ||
request = http.request(requestOptions); | ||
} | ||
request.on('error', options.onError); | ||
@@ -206,6 +219,4 @@ request.on('response', options.onResponse); | ||
/** | ||
* FIXME Use the newer "RESTful" URLs. | ||
* Generate a URL the collector understands. | ||
* | ||
* @param string methodName The method to invoke on the collector. | ||
* | ||
* @returns string The URL path to be POSTed to. | ||
@@ -212,0 +223,0 @@ */ |
@@ -34,7 +34,13 @@ /** | ||
*/ | ||
port : 80, | ||
port : 443, | ||
/** | ||
* Whether or not to use SSL to connect to New Relic's servers. | ||
* | ||
* @env NEW_RELIC_USE_SSL | ||
*/ | ||
ssl : true, | ||
/** | ||
* Proxy host to use to connect to the internet. | ||
* | ||
* FIXME: proxy support is completely untested. | ||
* FIXME: proxy support does not currently work | ||
* | ||
@@ -47,3 +53,3 @@ * @env NEW_RELIC_PROXY_HOST | ||
* | ||
* FIXME: proxy support is completely untested. | ||
* FIXME: proxy support does not currently work | ||
* | ||
@@ -249,3 +255,50 @@ * @env NEW_RELIC_PROXY_PORT | ||
*/ | ||
enforce_backstop : true | ||
enforce_backstop : true, | ||
/** | ||
* Browser Monitoring | ||
* | ||
* Browser monitoring lets you correlate transactions between the server and browser | ||
* giving you accurate data on how long a page request takes, from request, | ||
* through the server response, up until the actual page render completes. | ||
*/ | ||
browser_monitoring : { | ||
/** | ||
* Enable browser monitoring header generation. | ||
* | ||
* This does not auto-instrument, rather it enables the agent to generate headers. | ||
* The newrelic module can generate the appropriate <script> header, but you must | ||
* inject the header yourself, or use a module that does so. | ||
* | ||
* Usage: | ||
* | ||
* var newrelic = require('newrelic'); | ||
* | ||
* router.get('/', function (req, res) { | ||
* var header = newrelic.getBrowserTimingHeader(); | ||
* res.write(header) | ||
* // write the rest of the page | ||
* }); | ||
* | ||
* This generates the <script>...</script> header necessary for Browser Monitoring | ||
* This script must be manually injected into your templates, as high as possible | ||
* in the header, but _after_ any X-UA-COMPATIBLE HTTP-EQUIV meta tags. | ||
* Otherwise you may hurt IE! | ||
* | ||
* This method must be called _during_ a transaction, and must be called every | ||
* time you want to generate the headers. | ||
* | ||
* Do *not* reuse the headers between users, or even between requests. | ||
* | ||
* @env NEW_RELIC_BROWSER_MONITOR_ENABLE | ||
*/ | ||
enable : true, | ||
/** | ||
* Request un-minified sources from the server. | ||
* | ||
* @env NEW_RELIC_BROWSER_MONITOR_DEBUG | ||
*/ | ||
debug : false | ||
} | ||
}; |
@@ -39,2 +39,3 @@ 'use strict'; | ||
license_key : "NEW_RELIC_LICENSE_KEY", | ||
ssl : "NEW_RELIC_USE_SSL", | ||
host : "NEW_RELIC_HOST", | ||
@@ -70,3 +71,7 @@ port : "NEW_RELIC_PORT", | ||
}, | ||
enforce_backstop : "NEW_RELIC_ENFORCE_BACKSTOP" | ||
enforce_backstop : "NEW_RELIC_ENFORCE_BACKSTOP", | ||
browser_monitoring: { | ||
enable : "NEW_RELIC_BROWSER_MONITOR_ENABLE", | ||
debug : "NEW_RELIC_BROWSER_MONITOR_DEBUG" | ||
} | ||
}; | ||
@@ -99,3 +104,6 @@ | ||
"NEW_RELIC_DEBUG_TRACER", | ||
"NEW_RELIC_ENFORCE_BACKSTOP" | ||
"NEW_RELIC_ENFORCE_BACKSTOP", | ||
"NEW_RELIC_USE_SSL", | ||
"NEW_RELIC_BROWSER_MONITOR_ENABLE", | ||
"NEW_RELIC_BROWSER_MONITOR_DEBUG" | ||
]; | ||
@@ -176,3 +184,3 @@ | ||
}, this); | ||
// 2. initialize undocumented, internal-only default values | ||
@@ -196,2 +204,5 @@ | ||
this.browser_monitoring.loader = 'rum'; | ||
this.browser_monitoring.loader_version = ''; | ||
// 3. override defaults with values from the loaded / passed configuration | ||
@@ -274,2 +285,3 @@ this._fromPassed(config); | ||
// setting these can be disabled by ignore_server_configuration | ||
case 'ssl': | ||
case 'apdex_t': | ||
@@ -307,8 +319,20 @@ case 'web_transactions_apdex': | ||
// these are used by browser_monitoring | ||
// and the api.getRUMHeader() method | ||
case 'js_agent_file': | ||
case 'js_agent_loader_file': | ||
case 'beacon': | ||
case 'error_beacon': | ||
case 'browser_key': | ||
case 'js_agent_loader': | ||
this._updateNestedIfChangedRaw( | ||
params, | ||
this.browser_monitoring, | ||
key, | ||
key | ||
); | ||
break; | ||
// these settings aren't supported by the agent (yet) | ||
case 'sampling_rate': | ||
case 'beacon': | ||
case 'error_beacon': | ||
case 'js_agent_file': | ||
case 'js_agent_loader_file': | ||
case 'episodes_file': | ||
@@ -319,6 +343,4 @@ case 'episodes_url': | ||
case 'encoding_key': | ||
case 'browser_key': | ||
case 'trusted_account_ids': | ||
case 'high_security': | ||
case 'ssl': | ||
case 'transaction_tracer.record_sql': | ||
@@ -384,3 +406,7 @@ case 'slow_sql.enabled': | ||
if (this.ignore_server_configuration) return this.logDisabled(remote, remoteKey); | ||
else return this._updateNestedIfChangedRaw(remote, local, remoteKey, localKey); | ||
}; | ||
Config.prototype._updateNestedIfChangedRaw = function ( | ||
remote, local, remoteKey, localKey) { | ||
var value = remote[remoteKey]; | ||
@@ -387,0 +413,0 @@ if (value !== null && value !== undefined && local[localKey] !== value) { |
@@ -19,2 +19,3 @@ 'use strict'; | ||
var DEFAULT_PORT = 80; | ||
var QUEUE_HEADER = 'x-request-start'; | ||
@@ -43,2 +44,12 @@ function wrapListener(agent, listener) { | ||
/** | ||
* Calculate Queue Time | ||
* | ||
* Queue time is provided by certain providers by stamping the request | ||
* header with the time the request arrived at the router. | ||
*/ | ||
if (request.headers[QUEUE_HEADER]) { | ||
transaction.queueTime = Date.now() - request.headers[QUEUE_HEADER]; | ||
} | ||
function instrumentedFinish() { | ||
@@ -45,0 +56,0 @@ /* Express breaks URLs up by application, but the unmodified URL can be |
@@ -72,2 +72,7 @@ "use strict"; | ||
/* each time the cursor is called, update the prospective end time | ||
* for the MongoDB cursor | ||
*/ | ||
segment.touch(); | ||
return returned; | ||
@@ -74,0 +79,0 @@ }); |
@@ -16,3 +16,2 @@ 'use strict'; | ||
, STOPPED = 3 | ||
, DEAD = 4 | ||
; | ||
@@ -59,2 +58,12 @@ | ||
/** | ||
* Update the duration of the timer without ending it.. | ||
*/ | ||
Timer.prototype.touch = function () { | ||
if (this.state > RUNNING) return; | ||
if (process.hrtime) this.hrDuration = process.hrtime(this.hrstart); | ||
this.duration = Date.now() - this.start; | ||
}; | ||
/** | ||
* @return {bool} Is this timer currently running? | ||
@@ -93,31 +102,24 @@ */ | ||
* Returns how long the timer has been running (if it's still running) or | ||
* how long it ran (if it's been ended). | ||
* how long it ran (if it's been ended or touched). | ||
*/ | ||
Timer.prototype.getDurationInMillis = function () { | ||
switch (this.state) { | ||
case PENDING: | ||
return 0; | ||
if (this.state === PENDING) return 0; | ||
case RUNNING: | ||
if (process.hrtime) { | ||
return hrToMillis(process.hrtime(this.hrstart)); | ||
} | ||
else { | ||
return Date.now() - this.start; | ||
} | ||
break; | ||
// only set by setDurationInMilis | ||
if (this.durationInMillis >= 0) return this.durationInMillis; | ||
case STOPPED: | ||
case DEAD: | ||
if (!this.durationInMillis) { | ||
if (this.hrDuration) { | ||
this.durationInMillis = hrToMillis(this.hrDuration); | ||
} | ||
else { | ||
return this.duration; | ||
} | ||
} | ||
return this.durationInMillis; | ||
// prioritize .end() and .touch() | ||
if (this.hrDuration) { | ||
return hrToMillis(this.hrDuration); | ||
} | ||
else if (this.duration) { | ||
return this.duration; | ||
} | ||
// fall back to time elapsed since start | ||
else if (process.hrtime) { | ||
return hrToMillis(process.hrtime(this.hrstart)); | ||
} | ||
else { | ||
return Date.now() - this.start; | ||
} | ||
}; | ||
@@ -124,0 +126,0 @@ |
@@ -48,2 +48,3 @@ 'use strict'; | ||
this.ignore = false; | ||
this.queueTime = 0; | ||
} | ||
@@ -50,0 +51,0 @@ |
@@ -74,2 +74,14 @@ 'use strict'; | ||
/** | ||
* A segment attached to something evented (such as a database | ||
* cursor) just finished an action, so set the timer to mark | ||
* the timer as having a stop time. | ||
*/ | ||
TraceSegment.prototype.touch = function () { | ||
this.timer.touch(); | ||
}; | ||
/** | ||
* Stop timing the related action. | ||
*/ | ||
TraceSegment.prototype.end = function () { | ||
@@ -76,0 +88,0 @@ if (!this.timer.isActive()) return; |
17
NEWS.md
@@ -0,1 +1,18 @@ | ||
### v1.4.0 (2014-03-14): | ||
* Browser monitoring! Real User Monitoring! Which is also known as RUM! | ||
Whatever it's called, it allows you to see how long your pages take to load, | ||
not just on the server side, but in the browser! Wow! It's super cool! We | ||
know a lot of you have been waiting for this, and it's here! It's manually | ||
set up with an API call! Check the README for details! | ||
* By default, all communication between New Relic for Node and New Relic's | ||
servers is now protected with crisp, clean TLS encryption. To minimize the | ||
CPU overhead of running connections over SSL (and it can be configured, see | ||
the README and the online documentation for details on how to return to plain | ||
HTTP), New Relic for Node is now using a keep-alive connection that will | ||
properly pipeline connections, for both HTTP and HTTPS. | ||
* Improved the timings for a large class of MongoDB / Mongoose use cases. If | ||
you've encountered the issue where MongoDB trace segments last for an | ||
absurdly long duration, this should help. | ||
### v1.3.2 (2014-02-12): | ||
@@ -2,0 +19,0 @@ |
{ | ||
"name": "newrelic", | ||
"version": "1.3.2", | ||
"version": "1.4.0", | ||
"author": "New Relic Node.js agent team <nodejs@newrelic.com>", | ||
@@ -49,7 +49,9 @@ "licenses": [ | ||
"bunyan": "0.14.6", | ||
"continuation-local-storage": "2.6.2" | ||
"continuation-local-storage": "2.6.2", | ||
"yakaa": "0.0.0" | ||
}, | ||
"bundledDependencies": [ | ||
"bunyan", | ||
"continuation-local-storage" | ||
"continuation-local-storage", | ||
"yakaa" | ||
], | ||
@@ -67,3 +69,3 @@ "devDependencies": { | ||
"tap": "*", | ||
"memcached": "*", | ||
"memcached": ">=0.2.8", | ||
"sequelize": "*", | ||
@@ -77,3 +79,4 @@ "redis": "*", | ||
"split": "*", | ||
"JSV": "~4.0.2" | ||
"JSV": "~4.0.2", | ||
"glob": "~3.2.9" | ||
}, | ||
@@ -80,0 +83,0 @@ "repository": { |
@@ -18,2 +18,3 @@ [![npm status badge](https://nodei.co/npm/newrelic.png?stars=true&downloads=true)](https://nodei.co/npm/newrelic/) | ||
* [Getting started](#getting-started) | ||
* [RUM / browser timings](#browser-timings-rum--real-user-monitoring) | ||
* [Transactions and request naming](#transactions-and-request-naming) | ||
@@ -54,2 +55,82 @@ * [Configuration](#configuring-the-module) | ||
## Browser timings (RUM / Real User Monitoring) | ||
New Relic's instrumentation can extend beyond your application into the | ||
client's browser. The `newrelic` module can generate `<script>` headers which, | ||
when inserted into your HTML templates, will capture client-side page load | ||
times. | ||
Headers must be manually injected, but no extra configuration is necessary to | ||
enable browser timings. | ||
### Basics | ||
- Insert the result of `newrelic.getBrowserTimingHeader()` | ||
into your html page. | ||
- The browser timing headers should be placed in the beginning of your `<head>` tag. | ||
- As an exception to the above, for maximum IE compatability, the results of `getBrowserTimingHeader()` | ||
should be placed *after* any `X-UA-COMPATIBLE HTTP-EQUIV` meta tags. | ||
- Do *not* cache the header, call it once for every request. | ||
### Example | ||
Below is an example using `express` and `jade`; Express is a popular web | ||
application framework, and `jade` is a popular template module. Although the | ||
specifics are different for other frameworks, the general approach described | ||
below should work in most cases. | ||
The simplest way to insert browser timing headers is to pass the `newrelic` | ||
module into your template, and call `newrelic.getBrowserTimingHeader()` from | ||
within the template. | ||
*app.js:* | ||
```javascript | ||
var newrelic = require('newrelic'); | ||
var app = require('express')(); | ||
// In Express, this lets you call newrelic from within a template. | ||
app.locals.newrelic = newrelic; | ||
app.get('/user/:id', function (req, res) { | ||
res.render('user'); | ||
}); | ||
app.listen(process.env.PORT); | ||
``` | ||
*layout.jade:* | ||
```jade | ||
doctype html | ||
html | ||
head | ||
!= newrelic.getBrowserTimingHeader() | ||
title= title | ||
link(rel='stylesheet', href='/stylesheets/style.css') | ||
body | ||
block content | ||
``` | ||
By defaults calls to `newrelic.getBrowserTimingHeader()` should return valid | ||
headers. You can disable header generation *without* removing your template | ||
code. In your `newrelic.js` file, add the following to disable header | ||
generation. | ||
```javascript | ||
exports.config = { | ||
// ... other config | ||
browser_monitoring : { | ||
enable : false | ||
} | ||
}; | ||
``` | ||
You can also set the environment variable `NEW_RELIC_BROWSER_MONITOR_ENABLE=false`. | ||
It is safe to leave the header generation code in place even when you're not | ||
using it. If browser timings are disabled, or there is an error such that | ||
working headers cannot be generated, the `newrelic` module will generate an | ||
innocuous HTML comment. If the `newrelic` module is disabled entirely no | ||
content will be generated. | ||
## Transactions and request naming | ||
@@ -336,2 +417,4 @@ | ||
* `NEW_RELIC_HOME`: path to the directory in which you've placed newrelic.js. | ||
* `NEW_RELIC_USE_SSL`: Use SSL for communication with New Relic's servers. | ||
Enabled by default. | ||
* `NEW_RELIC_LOG`: Complete path to the New Relic agent log, including the | ||
@@ -395,2 +478,3 @@ filename. The agent will shut down the process if it can't create this file, | ||
Node.js engineer and it has a significant performance cost, so use with care. | ||
* `NEW_RELIC_BROWSER_MONITOR_ENABLE`: Whether to generate browser timing (RUM) headers or not. | ||
@@ -445,5 +529,2 @@ ## Security | ||
* SSL-protected connections between the module and New Relic | ||
* high-security mode | ||
* Real User Monitoring (RUM) | ||
* cross-application tracing (depends on RUM) | ||
@@ -450,0 +531,0 @@ * custom parameters, metrics and instrumentation |
@@ -29,2 +29,9 @@ 'use strict'; | ||
// this code gets injected into HTML templates | ||
// and we don't want it to return undefined/null | ||
Stub.prototype.getBrowserTimingHeader = function (){ | ||
logger.debug('Not calling getBrowserTimingHeader because New Relic is disabled.'); | ||
return ''; | ||
}; | ||
module.exports = Stub; |
@@ -21,3 +21,5 @@ 'use strict'; | ||
*/ | ||
var RUN_ID = 1337; | ||
var RUN_ID = 1337 | ||
, URL = 'https://collector.newrelic.com' | ||
; | ||
@@ -144,11 +146,11 @@ describe("the New Relic agent", function () { | ||
var redirect = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('get_redirect_host')) | ||
.reply(200, {return_value : 'collector.newrelic.com'}); | ||
var connect = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('connect')) | ||
.reply(200, {return_value : {agent_run_id : RUN_ID, apdex_t : 0.5}}); | ||
var shutdown = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('shutdown', RUN_ID)) | ||
@@ -391,3 +393,3 @@ .reply(200, {return_value : null}); | ||
var metrics = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('metric_data', RUN_ID)) | ||
@@ -417,11 +419,11 @@ .reply(200, {return_value : []}); | ||
var redirect = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('get_redirect_host')) | ||
.reply(200, {return_value : 'collector.newrelic.com'}); | ||
var connect = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('connect')) | ||
.reply(200, {return_value : {agent_run_id : RUN_ID}}); | ||
var metrics = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('metric_data', RUN_ID)) | ||
@@ -447,11 +449,11 @@ .reply(200, {return_value : []}); | ||
var redirect = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('get_redirect_host')) | ||
.reply(200, {return_value : 'collector.newrelic.com'}); | ||
var connect = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('connect')) | ||
.reply(200, {return_value : {agent_run_id : RUN_ID}}); | ||
var metrics = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('metric_data', RUN_ID)) | ||
@@ -531,3 +533,3 @@ .times(2) | ||
var shutdown = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('shutdown', RUN_ID)) | ||
@@ -547,3 +549,3 @@ .reply(200, {return_value : null}); | ||
var shutdown = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('shutdown', RUN_ID)) | ||
@@ -586,6 +588,6 @@ .reply(503); | ||
var redirect = nock('http://collector.newrelic.com') | ||
var redirect = nock(URL) | ||
.post(helper.generateCollectorPath('get_redirect_host')) | ||
.reply(200, {return_value : 'collector.newrelic.com'}); | ||
var handshake = nock('http://collector.newrelic.com') | ||
var handshake = nock(URL) | ||
.post(helper.generateCollectorPath('connect')) | ||
@@ -652,3 +654,3 @@ .reply(200, {return_value : config}); | ||
var metrics = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('metric_data', RUN_ID)) | ||
@@ -673,3 +675,3 @@ .reply(200, {return_value : null}); | ||
var metrics = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('metric_data', RUN_ID)) | ||
@@ -693,3 +695,3 @@ .reply(200, {return_value : rules}); | ||
var metrics = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('metric_data', RUN_ID)) | ||
@@ -839,3 +841,3 @@ .reply(200, {return_value : rules}); | ||
var metricData = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('metric_data', RUN_ID)) | ||
@@ -884,3 +886,3 @@ .reply(200, {return_value : []}); | ||
var metricData = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('metric_data', RUN_ID)) | ||
@@ -902,7 +904,7 @@ .reply(503); | ||
var metricData = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('metric_data', RUN_ID)) | ||
.reply(200, {return_value : null}); | ||
var errorData = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('error_data', RUN_ID)) | ||
@@ -925,3 +927,3 @@ .reply(503); | ||
var metricData = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('metric_data', RUN_ID)) | ||
@@ -947,3 +949,3 @@ .reply(200, {return_value : []}); | ||
var metricData = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('metric_data', RUN_ID)) | ||
@@ -976,7 +978,7 @@ .reply(200, {return_value : []}); | ||
var metricData = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('metric_data', RUN_ID)) | ||
.reply(200, {return_value : []}); | ||
var errorData = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('error_data', RUN_ID)) | ||
@@ -1006,7 +1008,7 @@ .reply(200, {return_value : null}); | ||
var metricData = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('metric_data', RUN_ID)) | ||
.reply(200, {return_value : []}); | ||
var errorData = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('error_data', RUN_ID)) | ||
@@ -1037,11 +1039,11 @@ .reply(200, {return_value : null}); | ||
var metricData = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('metric_data', RUN_ID)) | ||
.reply(200, {return_value : []}); | ||
var errorData = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('error_data', RUN_ID)) | ||
.reply(200, {return_value : null}); | ||
var transactionSampleData = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('transaction_sample_data', RUN_ID)) | ||
@@ -1068,11 +1070,11 @@ .reply(200, {return_value : null}); | ||
var metricData = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('metric_data', RUN_ID)) | ||
.reply(200, {return_value : []}); | ||
var errorData = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('error_data', RUN_ID)) | ||
.reply(200, {return_value : null}); | ||
var transactionSampleData = | ||
nock('http://collector.newrelic.com') | ||
nock(URL) | ||
.post(helper.generateCollectorPath('transaction_sample_data', RUN_ID)) | ||
@@ -1079,0 +1081,0 @@ .reply(503); |
@@ -17,4 +17,4 @@ 'use strict'; | ||
it("should export 6 API calls", function () { | ||
expect(Object.keys(api.constructor.prototype).length).equal(6); | ||
it("should export 7 API calls", function () { | ||
expect(Object.keys(api.constructor.prototype).length).equal(7); | ||
}); | ||
@@ -75,2 +75,6 @@ | ||
}); | ||
it("should return an empty string when requesting Browser Monitoring", function () { | ||
api.getBrowserTimingHeader().should.equal(''); | ||
}); | ||
}); |
@@ -46,3 +46,4 @@ 'use strict'; | ||
return ['TEST']; | ||
} | ||
}, | ||
browser_monitoring: {} | ||
} | ||
@@ -1160,3 +1161,4 @@ }; | ||
return ['TEST']; | ||
} | ||
}, | ||
browser_monitoring: {} | ||
}; | ||
@@ -1163,0 +1165,0 @@ var properties = { |
@@ -272,6 +272,10 @@ 'use strict'; | ||
it("should connect to the collector on port 80", function () { | ||
expect(configuration.port).equal(80); | ||
it("should connect to the collector on port 443", function () { | ||
expect(configuration.port).equal(443); | ||
}); | ||
it("should have SSL enabled", function () { | ||
expect(configuration.ssl).equal(true); | ||
}); | ||
it("should have no proxy host", function () { | ||
@@ -718,2 +722,21 @@ expect(configuration.proxy_host).equal(''); | ||
it("should still set rum properties", function () { | ||
config.onConnect({ | ||
js_agent_loader : "LOADER", | ||
js_agent_file : "FILE", | ||
js_agent_loader_file : "LOADER_FILE", | ||
beacon : "BEACON", | ||
error_beacon : "ERR_BEACON", | ||
browser_key : "KEY" | ||
}); | ||
var bm = config.browser_monitoring; | ||
expect(bm.js_agent_loader) .equal ("LOADER"); | ||
expect(bm.js_agent_file) .equal ("FILE"); | ||
expect(bm.js_agent_loader_file) .equal ("LOADER_FILE"); | ||
expect(bm.beacon) .equal ("BEACON"); | ||
expect(bm.error_beacon) .equal ("ERR_BEACON"); | ||
expect(bm.browser_key) .equal ("KEY"); | ||
}); | ||
it("should still set agent_run_id", function () { | ||
@@ -827,2 +850,10 @@ config.onConnect({'agent_run_id' : 1234}); | ||
it("should ignore ssl", function () { | ||
expect(config.ssl).eql(true); | ||
expect(function () { | ||
config.onConnect({'ssl' : false}); | ||
}).not.throws(); | ||
expect(config.ssl).eql(true); | ||
}); | ||
it("should ignore cross_process_id", function () { | ||
@@ -858,8 +889,2 @@ expect(function () { | ||
it("should ignore ssl", function () { | ||
expect(function () { | ||
config.onConnect({'ssl' : true}); | ||
}).not.throws(); | ||
}); | ||
it("should ignore transaction_tracer.record_sql", function () { | ||
@@ -866,0 +891,0 @@ expect(function () { |
@@ -12,3 +12,4 @@ 'use strict'; | ||
var EXPECTED = ['pid', 'host', 'language', 'app_name', 'agent_version', 'environment']; | ||
var EXPECTED = ['pid', 'host', 'language', 'app_name', | ||
'agent_version', 'environment', 'settings']; | ||
@@ -15,0 +16,0 @@ describe("fun facts about apps that New Relic is interested in include", function () { |
@@ -15,2 +15,3 @@ 'use strict'; | ||
'port' : 80, | ||
'ssl' : false, | ||
'logging' : { | ||
@@ -17,0 +18,0 @@ 'level' : 'trace' |
@@ -15,2 +15,3 @@ 'use strict'; | ||
'port' : 80, | ||
'ssl' : false, | ||
'logging' : { | ||
@@ -17,0 +18,0 @@ 'level' : 'trace' |
@@ -15,2 +15,3 @@ 'use strict'; | ||
'port' : 80, | ||
'ssl' : false, | ||
'logging' : { | ||
@@ -17,0 +18,0 @@ 'level' : 'trace' |
@@ -15,2 +15,3 @@ 'use strict'; | ||
'port' : 80, | ||
'ssl' : false, | ||
'logging' : { | ||
@@ -17,0 +18,0 @@ 'level' : 'trace' |
@@ -15,2 +15,3 @@ 'use strict'; | ||
'port' : 80, | ||
'ssl' : false, | ||
'logging' : { | ||
@@ -17,0 +18,0 @@ 'level' : 'trace' |
@@ -16,2 +16,3 @@ 'use strict'; | ||
'port' : 80, | ||
'ssl' : false, | ||
'logging' : { | ||
@@ -18,0 +19,0 @@ 'level' : 'trace' |
@@ -16,2 +16,3 @@ 'use strict'; | ||
'port' : 80, | ||
'ssl' : false, | ||
'logging' : { | ||
@@ -18,0 +19,0 @@ 'level' : 'trace' |
@@ -16,2 +16,3 @@ 'use strict'; | ||
'port' : 80, | ||
'ssl' : false, | ||
'logging' : { | ||
@@ -18,0 +19,0 @@ 'level' : 'trace' |
@@ -16,2 +16,3 @@ 'use strict'; | ||
'port' : 80, | ||
'ssl' : false, | ||
'logging' : { | ||
@@ -18,0 +19,0 @@ 'level' : 'trace' |
@@ -15,3 +15,3 @@ 'use strict'; | ||
var RUN_ID = 1337 | ||
, url = 'http://collector.newrelic.com' | ||
, url = 'https://collector.newrelic.com' | ||
, agent = new Agent(configurator.initialize()) | ||
@@ -71,3 +71,3 @@ , transaction = new Transaction(agent) | ||
var RUN_ID = 1338 | ||
, url = 'http://collector.newrelic.com' | ||
, url = 'https://collector.newrelic.com' | ||
, agent = new Agent(configurator.initialize()) | ||
@@ -74,0 +74,0 @@ , transaction = new Transaction(agent) |
@@ -15,3 +15,3 @@ 'use strict'; | ||
var RUN_ID = 1337 | ||
, url = 'http://collector.newrelic.com' | ||
, url = 'https://collector.newrelic.com' | ||
, agent = new Agent(configurator.initialize()) | ||
@@ -72,3 +72,3 @@ , transaction = new Transaction(agent) | ||
var RUN_ID = 1338 | ||
, url = 'http://collector.newrelic.com' | ||
, url = 'https://collector.newrelic.com' | ||
, agent = new Agent(configurator.initialize()) | ||
@@ -75,0 +75,0 @@ , transaction = new Transaction(agent) |
@@ -15,3 +15,3 @@ 'use strict'; | ||
var RUN_ID = 1337 | ||
, url = 'http://collector.newrelic.com' | ||
, url = 'https://collector.newrelic.com' | ||
, agent = new Agent(configurator.initialize()) | ||
@@ -77,3 +77,3 @@ , transaction = new Transaction(agent) | ||
var RUN_ID = 1338 | ||
, url = 'http://collector.newrelic.com' | ||
, url = 'https://collector.newrelic.com' | ||
, agent = new Agent(configurator.initialize()) | ||
@@ -80,0 +80,0 @@ , transaction = new Transaction(agent) |
@@ -114,3 +114,3 @@ 'use strict'; | ||
"should register the get"); | ||
t.equals(getSegment.parameters.key, "[\"testkey\",\"otherkey\"]", | ||
t.equals(getSegment.parameters.key, "[[\"testkey\",\"otherkey\"]]", | ||
"should have the multiple keys fetched as a parameter"); | ||
@@ -117,0 +117,0 @@ t.equals(getSegment.children.length, 0, |
@@ -91,2 +91,25 @@ 'use strict'; | ||
}); | ||
it("should support updating the duration with touch", function (done) { | ||
var timer = new Timer(); | ||
timer.begin(); | ||
setTimeout(function () { | ||
timer.touch(); | ||
var first = timer.getDurationInMillis(); | ||
expect(first).above(0); | ||
expect(timer.isActive()).equal(true); | ||
setTimeout(function () { | ||
timer.end(); | ||
var second = timer.getDurationInMillis(); | ||
expect(second).above(first); | ||
expect(timer.isActive()).equal(false); | ||
done(); | ||
}, 20); | ||
}, 20); | ||
}); | ||
}); |
@@ -69,2 +69,9 @@ 'use strict'; | ||
it("allows the timer to be updated without ending it", function () { | ||
var segment = new TraceSegment(new Trace('Test/TraceExample04'), 'UnitTest'); | ||
segment.touch(); | ||
expect(segment.timer.isRunning()).equal(true); | ||
expect(segment.getDurationInMillis()).above(0); | ||
}); | ||
it("accepts a callback that records metrics associated with this segment", | ||
@@ -71,0 +78,0 @@ function (done) { |
@@ -11,2 +11,3 @@ 'use strict'; | ||
, helper = require(path.join(__dirname, '..', '..', 'lib', 'agent_helper')) | ||
, API = require(path.join('..', '..', '..', 'api.js')) | ||
; | ||
@@ -31,3 +32,3 @@ | ||
test("agent instrumentation of Express 2", function (t) { | ||
t.plan(5); | ||
t.plan(6); | ||
@@ -126,2 +127,45 @@ t.test("for a normal request", {timeout : 1000}, function (t) { | ||
t.test("should generate rum headers", | ||
{timeout : 1000}, | ||
function (t) { | ||
var agent = helper.instrumentMockedAgent() | ||
, app = require('express').createServer() | ||
, api = new API(agent) | ||
; | ||
agent.config.application_id = '12345'; | ||
agent.config.browser_monitoring.browser_key = '12345'; | ||
this.tearDown(function () { | ||
app.close(); | ||
helper.unloadAgent(agent); | ||
}); | ||
app.set('views', __dirname + '/views'); | ||
app.set('view engine', 'ejs'); | ||
app.set('view options', {layout : false}); | ||
app.get(TEST_PATH, function (req, res) { | ||
var rum = api.getBrowserTimingHeader(); | ||
t.equal(rum.substr(0,7), '<script'); | ||
res.render('index', { title: 'yo dawg', rum: rum }); | ||
}); | ||
app.listen(TEST_PORT, TEST_HOST); | ||
agent.once('transactionFinished', function () { | ||
var stats = agent.metrics.getMetric('View/index/Rendering'); | ||
t.equal(stats.callCount, 1, "should note the view rendering"); | ||
}); | ||
request(TEST_URL, function (error, response, body) { | ||
if (error) t.fail(error); | ||
t.equal(response.statusCode, 200, "response code should be 200"); | ||
t.equal(body, BODY, "template should still render fine"); | ||
t.end(); | ||
}); | ||
}); | ||
t.test("should trap errors correctly", | ||
@@ -128,0 +172,0 @@ {timeout : 1000}, |
@@ -11,2 +11,3 @@ 'use strict'; | ||
, helper = require(path.join(__dirname, '..', '..', 'lib', 'agent_helper')) | ||
, API = require(path.join('..', '..', '..', 'api.js')) | ||
; | ||
@@ -31,3 +32,3 @@ | ||
test("agent instrumentation of Express 3", function (t) { | ||
t.plan(5); | ||
t.plan(6); | ||
@@ -126,2 +127,44 @@ t.test("for a normal request", {timeout : 1000}, function (t) { | ||
t.test("should generate rum headers", | ||
{timeout : 1000}, | ||
function (t) { | ||
var agent = helper.instrumentMockedAgent() | ||
, app = require('express')() | ||
, server = require('http').createServer(app) | ||
, api = new API(agent) | ||
; | ||
agent.config.application_id = '12345'; | ||
agent.config.browser_monitoring.browser_key = '12345'; | ||
this.tearDown(function () { | ||
server.close(); | ||
helper.unloadAgent(agent); | ||
}); | ||
app.set('views', __dirname + '/views'); | ||
app.set('view engine', 'ejs'); | ||
app.get(TEST_PATH, function (req, res) { | ||
var rum = api.getBrowserTimingHeader(); | ||
t.equal(rum.substr(0,7), '<script'); | ||
res.render('index', { title: 'yo dawg', rum: rum }); | ||
}); | ||
server.listen(TEST_PORT, TEST_HOST); | ||
agent.once('transactionFinished', function () { | ||
var stats = agent.metrics.getMetric('View/index/Rendering'); | ||
t.equal(stats.callCount, 1, "should note the view rendering"); | ||
}); | ||
request(TEST_URL, function (error, response, body) { | ||
if (error) t.fail(error); | ||
t.equal(response.statusCode, 200, "response code should be 200"); | ||
t.equal(body, BODY, "template should still render fine"); | ||
t.end(); | ||
}); | ||
}); | ||
t.test("should trap errors correctly", function (t) { | ||
@@ -128,0 +171,0 @@ var agent = helper.instrumentMockedAgent(); |
@@ -14,2 +14,3 @@ 'use strict'; | ||
, helper = require(path.join(__dirname, '..', '..', 'lib', 'agent_helper')) | ||
, API = require(path.join('..', '..', '..', 'api.js')) | ||
; | ||
@@ -33,3 +34,3 @@ | ||
test("agent instrumentation of Hapi", function (t) { | ||
t.plan(3); | ||
t.plan(4); | ||
@@ -139,2 +140,54 @@ t.test("for a normal request", {timeout : 1000}, function (t) { | ||
t.test("should generate rum headers", | ||
{timeout : 1000}, | ||
function (t) { | ||
var agent = helper.instrumentMockedAgent() | ||
, hapi = require('hapi') | ||
, api = new API(agent) | ||
; | ||
agent.config.application_id = '12345'; | ||
agent.config.browser_monitoring.browser_key = '12345'; | ||
var options = { | ||
views : { | ||
path : path.join(__dirname, 'views'), | ||
engines : { | ||
ejs : 'ejs' | ||
} | ||
} | ||
}; | ||
var server = hapi.createServer(TEST_HOST, TEST_PORT, options); | ||
server.route({ | ||
method : 'GET', | ||
path : TEST_PATH, | ||
handler : function () { | ||
var rum = api.getBrowserTimingHeader(); | ||
t.equal(rum.substr(0,7), '<script'); | ||
this.reply.view('index', {title : 'yo dawg'}); | ||
} | ||
}); | ||
agent.once('transactionFinished', function () { | ||
var stats = agent.metrics.getMetric('View/index/Rendering'); | ||
t.equal(stats.callCount, 1, "should note the view rendering"); | ||
}); | ||
server.start(function () { | ||
request(TEST_URL, function (error, response, body) { | ||
if (error) t.fail(error); | ||
t.equal(response.statusCode, 200, "response code should be 200"); | ||
t.equal(body, BODY, "template should still render fine"); | ||
server.stop(function () { | ||
helper.unloadAgent(agent); | ||
t.end(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
t.test("should trap errors correctly", function (t) { | ||
@@ -141,0 +194,0 @@ var agent = helper.instrumentMockedAgent() |
@@ -14,2 +14,3 @@ 'use strict'; | ||
, helper = require(path.join(__dirname, '..', '..', 'lib', 'agent_helper')) | ||
, API = require(path.join('..', '..', '..', 'api.js')) | ||
; | ||
@@ -33,3 +34,3 @@ | ||
test("agent instrumentation of Hapi", function (t) { | ||
t.plan(3); | ||
t.plan(4); | ||
@@ -139,2 +140,54 @@ t.test("for a normal request", {timeout : 1000}, function (t) { | ||
t.test("should generate rum headers", | ||
{timeout : 1000}, | ||
function (t) { | ||
var agent = helper.instrumentMockedAgent() | ||
, hapi = require('hapi') | ||
, api = new API(agent) | ||
; | ||
agent.config.application_id = '12345'; | ||
agent.config.browser_monitoring.browser_key = '12345'; | ||
var options = { | ||
views : { | ||
path : path.join(__dirname, 'views'), | ||
engines : { | ||
ejs : 'ejs' | ||
} | ||
} | ||
}; | ||
var server = hapi.createServer(TEST_HOST, TEST_PORT, options); | ||
server.route({ | ||
method : 'GET', | ||
path : TEST_PATH, | ||
handler : function (request, reply) { | ||
var rum = api.getBrowserTimingHeader(); | ||
t.equal(rum.substr(0,7), '<script'); | ||
reply.view('index', {title : 'yo dawg', rum: rum}); | ||
} | ||
}); | ||
agent.once('transactionFinished', function () { | ||
var stats = agent.metrics.getMetric('View/index/Rendering'); | ||
t.equal(stats.callCount, 1, "should note the view rendering"); | ||
}); | ||
server.start(function () { | ||
request(TEST_URL, function (error, response, body) { | ||
if (error) t.fail(error); | ||
t.equal(response.statusCode, 200, "response code should be 200"); | ||
t.equal(body, BODY, "template should still render fine"); | ||
server.stop(function () { | ||
helper.unloadAgent(agent); | ||
t.end(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
t.test("should trap errors correctly", function (t) { | ||
@@ -141,0 +194,0 @@ var agent = helper.instrumentMockedAgent() |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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 2 instances in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
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
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
1746493
412
37389
538
3
21
431
59
+ Addedyakaa@0.0.0