newrelic 12.5.2 → 12.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NEWS.md +83 -42
- package/lib/collector/http-agents.js +4 -1
- package/lib/config/default.js +152 -0
- package/lib/db/parsed-statement.js +0 -54
- package/lib/metrics/recorders/database-operation.js +62 -0
- package/lib/metrics/recorders/database.js +68 -0
- package/lib/metrics/recorders/middleware.js +29 -0
- package/lib/serverless/aws-lambda.js +3 -1
- package/lib/shim/datastore-shim.js +4 -68
- package/lib/shim/webframework-shim/middleware.js +2 -22
- package/lib/utilization/docker-info.js +21 -3
- package/package.json +4 -6
package/NEWS.md
CHANGED
|
@@ -1,45 +1,86 @@
|
|
|
1
|
-
### v12.
|
|
2
|
-
|
|
3
|
-
#### Features
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
-
#### Bug fixes
|
|
8
|
-
|
|
9
|
-
* Fixed
|
|
10
|
-
|
|
11
|
-
#### Documentation
|
|
12
|
-
|
|
13
|
-
*
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
*
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
1
|
+
### v12.6.1 (2024-11-07)
|
|
2
|
+
|
|
3
|
+
#### Features
|
|
4
|
+
|
|
5
|
+
* added `cloud.aws.account_id` to default config ([#2691](https://github.com/newrelic/node-newrelic/pull/2691)) ([0ccee8e](https://github.com/newrelic/node-newrelic/commit/0ccee8e471b5568a36a5ef755f83f0da513548c8))
|
|
6
|
+
|
|
7
|
+
#### Bug fixes
|
|
8
|
+
|
|
9
|
+
* Fixed issue parsing docker container id ([#2705](https://github.com/newrelic/node-newrelic/pull/2705)) ([0c897ab](https://github.com/newrelic/node-newrelic/commit/0c897ab7bea32daf0afbf75e2349f2fad008cc92))
|
|
10
|
+
|
|
11
|
+
#### Documentation
|
|
12
|
+
|
|
13
|
+
* Updated compatibility report ([#2679](https://github.com/newrelic/node-newrelic/pull/2679)) ([3c19cdf](https://github.com/newrelic/node-newrelic/commit/3c19cdfed751dfa20ebba471a6cdd320f0610d95))
|
|
14
|
+
|
|
15
|
+
#### Miscellaneous chores
|
|
16
|
+
|
|
17
|
+
* Moved recorders to `lib/metrics/recorders` ([#2666](https://github.com/newrelic/node-newrelic/pull/2666)) ([d8dfe84](https://github.com/newrelic/node-newrelic/commit/d8dfe843aebf275bda6d5c857cfe263039bc1a83))
|
|
18
|
+
|
|
19
|
+
#### Tests
|
|
20
|
+
|
|
21
|
+
* Fixed file extensions for aws-sdk v2 and v3 versioned tests to reflect they have been migrated to `node:test` ([#2687](https://github.com/newrelic/node-newrelic/pull/2687)) ([4ec09ba](https://github.com/newrelic/node-newrelic/commit/4ec09ba34a825fa9decdc2d854eec6d24ee37ac4))
|
|
22
|
+
* Migrated elasticsearch and esm-package versioned tests to `node:test` ([#2680](https://github.com/newrelic/node-newrelic/pull/2680)) ([0e0c2b2](https://github.com/newrelic/node-newrelic/commit/0e0c2b2f15e2179def4e67741fc988b7d16248d7))
|
|
23
|
+
* Updated cls and connect tests to node:test ([#2676](https://github.com/newrelic/node-newrelic/pull/2676)) ([1e74434](https://github.com/newrelic/node-newrelic/commit/1e74434efd21c13199ad12af837129d251136c76))
|
|
24
|
+
* Reorganized custom assertions and improved test reporter ([#2700](https://github.com/newrelic/node-newrelic/pull/2700)) ([9e98b18](https://github.com/newrelic/node-newrelic/commit/9e98b18b0f2768df9f75348975bebe904418a4a2))
|
|
25
|
+
* Updated cassandra-driver tests to node:test ([#2678](https://github.com/newrelic/node-newrelic/pull/2678)) ([bd4f7ff](https://github.com/newrelic/node-newrelic/commit/bd4f7ff9df2cd1057e21a6ba3e28aac9fe02ecc6))
|
|
26
|
+
|
|
27
|
+
### v12.6.0 (2024-10-30)
|
|
28
|
+
|
|
29
|
+
#### Features
|
|
30
|
+
|
|
31
|
+
* Added ARN and provider to Lambda segments ([#2674](https://github.com/newrelic/node-newrelic/pull/2674)) ([a23294c](https://github.com/newrelic/node-newrelic/commit/a23294c2d2cc665de5df1a0f3c9099dedbfbb896))
|
|
32
|
+
* Added IAST configurations for scan scheduling and restrictions ([#2645](https://github.com/newrelic/node-newrelic/pull/2645)) ([13a627a](https://github.com/newrelic/node-newrelic/commit/13a627a1a1529dd8f8c93d0b9f582457c019a63e))
|
|
33
|
+
|
|
34
|
+
#### Documentation
|
|
35
|
+
|
|
36
|
+
* Updated compatibility report ([#2673](https://github.com/newrelic/node-newrelic/pull/2673)) ([e4d0503](https://github.com/newrelic/node-newrelic/commit/e4d0503934f7de45d3cdb6dbb4640a66cf8d8421))
|
|
37
|
+
|
|
38
|
+
#### Miscellaneous chores
|
|
39
|
+
|
|
40
|
+
* Replaced static openssl cert usage with in-process cert ([#2671](https://github.com/newrelic/node-newrelic/pull/2671)) ([72872f4](https://github.com/newrelic/node-newrelic/commit/72872f4313fd9bc6d2b358a735dc76b9cda1a489))
|
|
41
|
+
|
|
42
|
+
### v12.5.2 (2024-10-23)
|
|
43
|
+
|
|
44
|
+
#### Features
|
|
45
|
+
|
|
46
|
+
* Updated shim/when instrumentation to use tracer to run in context ([#2642](https://github.com/newrelic/node-newrelic/pull/2642)) ([1a80ad5](https://github.com/newrelic/node-newrelic/commit/1a80ad56a6d502182a0db368f40443467f7943df))
|
|
47
|
+
|
|
48
|
+
#### Bug fixes
|
|
49
|
+
|
|
50
|
+
* Fixed amqplib instrumentation via ESM ([#2668](https://github.com/newrelic/node-newrelic/pull/2668)) ([a36deba](https://github.com/newrelic/node-newrelic/commit/a36deba7ba7b46c4947fcb83db0a4f97bd4c0bf1))
|
|
51
|
+
|
|
52
|
+
#### Documentation
|
|
53
|
+
|
|
54
|
+
* Remove SECURITY.md ([#2633](https://github.com/newrelic/node-newrelic/pull/2633)) ([41002cd](https://github.com/newrelic/node-newrelic/commit/41002cd1c423c378bfbe024ebe7dae03d02d2949))
|
|
55
|
+
* Updated compatibility report ([#2670](https://github.com/newrelic/node-newrelic/pull/2670)) ([281c0fa](https://github.com/newrelic/node-newrelic/commit/281c0fa3be096a0ef3eef25b0f51df7ae8bf50bf))
|
|
56
|
+
* Updated match custom-assertion jsdoc ([#2636](https://github.com/newrelic/node-newrelic/pull/2636)) ([c37abe5](https://github.com/newrelic/node-newrelic/commit/c37abe5eb4528493bb3950e376bf780d6cd29023))
|
|
57
|
+
|
|
58
|
+
#### Miscellaneous chores
|
|
59
|
+
|
|
60
|
+
* Upgraded `import-in-the-middle` to work around a bug introduced in 1.11.1 ([#2618](https://github.com/newrelic/node-newrelic/pull/2618)) ([9ad476a](https://github.com/newrelic/node-newrelic/commit/9ad476a765eee472f510239b4265d10f0a50c666))
|
|
61
|
+
|
|
62
|
+
#### Tests
|
|
63
|
+
|
|
64
|
+
* Migrated `aws-sdk-v2` and `aws-sdk-v3` tests to `node:test` ([#2620](https://github.com/newrelic/node-newrelic/pull/2620)) ([e0dc015](https://github.com/newrelic/node-newrelic/commit/e0dc01571087c3d53434d2d21d77206592137b66))
|
|
65
|
+
* Migrated `test/unit/shim` to `node:test` ([#2599](https://github.com/newrelic/node-newrelic/pull/2599)) ([8d1acff](https://github.com/newrelic/node-newrelic/commit/8d1acffabf29ba6e0b5e20a675b87a81f893fc0a))
|
|
66
|
+
* Migrated `test/versioned/amqplib` to `node:test` ([#2612](https://github.com/newrelic/node-newrelic/pull/2612)) ([7bdada6](https://github.com/newrelic/node-newrelic/commit/7bdada678c18997c268cf19c56262b870bae5673))
|
|
67
|
+
* Migrated `test/versioned/express` to `node:test` ([#2609](https://github.com/newrelic/node-newrelic/pull/2609)) ([bd2f1a5](https://github.com/newrelic/node-newrelic/commit/bd2f1a53f8e03810e3c0aa5d042b6b256ec7346b))
|
|
68
|
+
* Migrated bluebird versioned tests to `node:test` ([#2635](https://github.com/newrelic/node-newrelic/pull/2635)) ([6e28fad](https://github.com/newrelic/node-newrelic/commit/6e28fad70390ffaf4df7ccbc96c88f79bb5d2fe2))
|
|
69
|
+
* Migrated last group of unit tests to `node:test` ([#2624](https://github.com/newrelic/node-newrelic/pull/2624)) ([386f546](https://github.com/newrelic/node-newrelic/commit/386f54682128d0dda8ad073a57cd45109c927fe1))
|
|
70
|
+
* Migrated unit tests to `node:test` ([#2623](https://github.com/newrelic/node-newrelic/pull/2623)) ([86231b7](https://github.com/newrelic/node-newrelic/commit/86231b7dec5bc5807ae26a88a7b8f2ff1535d9c4))
|
|
71
|
+
* Updated tests that relied on `tspl` by awating the `plan.completed` instead of calling `end` to avoid flaky tests ([#2610](https://github.com/newrelic/node-newrelic/pull/2610)) ([935ac14](https://github.com/newrelic/node-newrelic/commit/935ac14dbff7d11e797d290fb24a0d791ac9a61a))
|
|
72
|
+
* Updated tests that used the context manager directly and instead use the tracer to access the segment context ([#2643](https://github.com/newrelic/node-newrelic/pull/2643)) ([b917b3e](https://github.com/newrelic/node-newrelic/commit/b917b3ea9416eaf64bf365f6f46a0d4eafdfc437))
|
|
73
|
+
* Updated the mininum version of pg-native in pg-esm tests to align with the pg tests ([#2616](https://github.com/newrelic/node-newrelic/pull/2616)) ([16be714](https://github.com/newrelic/node-newrelic/commit/16be71404dcea903f3f7b7d4d238cc0a416d7b79))
|
|
74
|
+
* Migrated `bunyan`, `pino`, and `winston` tests to `node:test` ([#2634](https://github.com/newrelic/node-newrelic/pull/2634)) ([69c1ab8](https://github.com/newrelic/node-newrelic/commit/69c1ab8951f8cd405986e879399dff716f839a78))
|
|
75
|
+
* Migrated `fastify` tests to `node:test` ([#2632](https://github.com/newrelic/node-newrelic/pull/2632)) ([b522477](https://github.com/newrelic/node-newrelic/commit/b522477168c2049b12bcfd39ae485f9e5374f724))
|
|
76
|
+
* Migrated block of unit tests to `node:test` ([#2607](https://github.com/newrelic/node-newrelic/pull/2607)) ([e33807b](https://github.com/newrelic/node-newrelic/commit/e33807b817852bb7cdc93c9b171250df17a3b867))
|
|
77
|
+
* Migrated block of unit tests to `node:test` ([#2604](https://github.com/newrelic/node-newrelic/pull/2604)) ([cd90ce1](https://github.com/newrelic/node-newrelic/commit/cd90ce11908edc4376a704153f44d4f3ddfb6866))
|
|
78
|
+
* Migrated block of unit tests to `node:test` ([#2593](https://github.com/newrelic/node-newrelic/pull/2593)) ([6d4d49e](https://github.com/newrelic/node-newrelic/commit/6d4d49e075d8c4c687d4730b65aa39177e384ce5))
|
|
79
|
+
|
|
80
|
+
#### Continuous integration
|
|
81
|
+
|
|
82
|
+
* Added delay to site extension publishing to wait for NPM ([#2665](https://github.com/newrelic/node-newrelic/pull/2665)) ([e412020](https://github.com/newrelic/node-newrelic/commit/e412020865bab187d8c7d274cdc6973946286a1f))
|
|
83
|
+
|
|
43
84
|
### v12.5.1 (2024-09-23)
|
|
44
85
|
|
|
45
86
|
#### Bug fixes
|
|
@@ -53,11 +53,14 @@ exports.proxyAgent = function proxyAgent(config) {
|
|
|
53
53
|
}
|
|
54
54
|
const proxyUrl = proxyOptions(config)
|
|
55
55
|
|
|
56
|
+
// Tests may supply 127.0.0.1 as the host, but SNI requires a hostname.
|
|
57
|
+
const servername = config.host
|
|
56
58
|
const proxyOpts = {
|
|
57
59
|
secureEndpoint: config.ssl,
|
|
58
60
|
auth: proxyUrl.auth,
|
|
59
61
|
ca: config?.certificates?.length ? config.certificates : [],
|
|
60
|
-
keepAlive: true
|
|
62
|
+
keepAlive: true,
|
|
63
|
+
servername
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
logger.info(`using proxy: ${proxyUrl}`)
|
package/lib/config/default.js
CHANGED
|
@@ -413,6 +413,17 @@ defaultConfig.definition = () => ({
|
|
|
413
413
|
default: false
|
|
414
414
|
}
|
|
415
415
|
},
|
|
416
|
+
cloud: {
|
|
417
|
+
aws: {
|
|
418
|
+
/**
|
|
419
|
+
* The AWS account ID for the AWS account associated with this app.
|
|
420
|
+
*/
|
|
421
|
+
account_id: {
|
|
422
|
+
formatter: int,
|
|
423
|
+
default: null
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
},
|
|
416
427
|
/**
|
|
417
428
|
* Options regarding collecting system information. Used for system
|
|
418
429
|
* utilization based pricing scheme.
|
|
@@ -1318,6 +1329,147 @@ defaultConfig.definition = () => ({
|
|
|
1318
1329
|
default: true
|
|
1319
1330
|
}
|
|
1320
1331
|
}
|
|
1332
|
+
},
|
|
1333
|
+
|
|
1334
|
+
/**
|
|
1335
|
+
* Unique test identifier when runnning IAST with CI/CD
|
|
1336
|
+
*/
|
|
1337
|
+
iast_test_identifier: '',
|
|
1338
|
+
|
|
1339
|
+
/**
|
|
1340
|
+
* IAST scan controllers to get more control over IAST analysis
|
|
1341
|
+
*/
|
|
1342
|
+
scan_controllers: {
|
|
1343
|
+
/**
|
|
1344
|
+
* The maximum number of analysis probes or requests
|
|
1345
|
+
* that can be sent to the application in one minute.
|
|
1346
|
+
*/
|
|
1347
|
+
iast_scan_request_rate_limit: {
|
|
1348
|
+
formatter: int,
|
|
1349
|
+
default: 3600
|
|
1350
|
+
},
|
|
1351
|
+
/**
|
|
1352
|
+
* The number of application instances for a specific entity where IAST analysis is performed.
|
|
1353
|
+
* Values are 0 or 1, 0 signifies run on all application instances
|
|
1354
|
+
*/
|
|
1355
|
+
scan_instance_count: {
|
|
1356
|
+
formatter: int,
|
|
1357
|
+
default: 0
|
|
1358
|
+
}
|
|
1359
|
+
},
|
|
1360
|
+
/**
|
|
1361
|
+
* Schedule start and stop of IAST scan
|
|
1362
|
+
*/
|
|
1363
|
+
scan_schedule: {
|
|
1364
|
+
/**
|
|
1365
|
+
* The delay field specifies the time in minutes
|
|
1366
|
+
* before an IAST scan begins after the application starts
|
|
1367
|
+
*/
|
|
1368
|
+
delay: {
|
|
1369
|
+
formatter: int,
|
|
1370
|
+
default: 0
|
|
1371
|
+
},
|
|
1372
|
+
/**
|
|
1373
|
+
* The duration field specifies the amount of
|
|
1374
|
+
* time in minutes that the IAST scan will run
|
|
1375
|
+
*/
|
|
1376
|
+
duration: {
|
|
1377
|
+
formatter: int,
|
|
1378
|
+
default: 0
|
|
1379
|
+
},
|
|
1380
|
+
/**
|
|
1381
|
+
* The schedule field specifies a unix cron expression that defines when the IAST scan should run.
|
|
1382
|
+
* By default, schedule is disabled
|
|
1383
|
+
*
|
|
1384
|
+
*/
|
|
1385
|
+
schedule: '',
|
|
1386
|
+
/**
|
|
1387
|
+
* Allows IAST to actively collect trace data in the background
|
|
1388
|
+
* and the security agent will use this collected data to perform
|
|
1389
|
+
* an IAST scan at the scheduled time
|
|
1390
|
+
*/
|
|
1391
|
+
always_sample_traces: {
|
|
1392
|
+
formatter: boolean,
|
|
1393
|
+
default: false
|
|
1394
|
+
}
|
|
1395
|
+
},
|
|
1396
|
+
|
|
1397
|
+
/**
|
|
1398
|
+
* The exclude from IAST scan setting allows to exclude specific APIs,
|
|
1399
|
+
* vulnerability categories, and parameters from IAST analysis.
|
|
1400
|
+
*/
|
|
1401
|
+
exclude_from_iast_scan: {
|
|
1402
|
+
/**
|
|
1403
|
+
* Ignore specific APIs from IAST analysis.
|
|
1404
|
+
* The regex pattern should provide a full match for the URL without the endpoint.
|
|
1405
|
+
*/
|
|
1406
|
+
api: {
|
|
1407
|
+
formatter: array,
|
|
1408
|
+
default: []
|
|
1409
|
+
},
|
|
1410
|
+
/**
|
|
1411
|
+
* Ignore specific HTTP request parameters from IAST analysis.
|
|
1412
|
+
*/
|
|
1413
|
+
http_request_parameters: {
|
|
1414
|
+
header: {
|
|
1415
|
+
formatter: array,
|
|
1416
|
+
default: []
|
|
1417
|
+
},
|
|
1418
|
+
query: {
|
|
1419
|
+
formatter: array,
|
|
1420
|
+
default: []
|
|
1421
|
+
},
|
|
1422
|
+
body: {
|
|
1423
|
+
formatter: array,
|
|
1424
|
+
default: []
|
|
1425
|
+
}
|
|
1426
|
+
},
|
|
1427
|
+
/**
|
|
1428
|
+
* Allows users to specify categories of vulnerabilities
|
|
1429
|
+
* for which IAST analysis will be applied or ignored.
|
|
1430
|
+
*/
|
|
1431
|
+
iast_detection_category: {
|
|
1432
|
+
insecure_settings: {
|
|
1433
|
+
formatter: boolean,
|
|
1434
|
+
default: false
|
|
1435
|
+
},
|
|
1436
|
+
invalid_file_access: {
|
|
1437
|
+
formatter: boolean,
|
|
1438
|
+
default: false
|
|
1439
|
+
},
|
|
1440
|
+
sql_injection: {
|
|
1441
|
+
formatter: boolean,
|
|
1442
|
+
default: false
|
|
1443
|
+
},
|
|
1444
|
+
nosql_injection: {
|
|
1445
|
+
formatter: boolean,
|
|
1446
|
+
default: false
|
|
1447
|
+
},
|
|
1448
|
+
ldap_injection: {
|
|
1449
|
+
formatter: boolean,
|
|
1450
|
+
default: false
|
|
1451
|
+
},
|
|
1452
|
+
javascript_injection: {
|
|
1453
|
+
formatter: boolean,
|
|
1454
|
+
default: false
|
|
1455
|
+
},
|
|
1456
|
+
command_injection: {
|
|
1457
|
+
formatter: boolean,
|
|
1458
|
+
default: false
|
|
1459
|
+
},
|
|
1460
|
+
xpath_injection: {
|
|
1461
|
+
formatter: boolean,
|
|
1462
|
+
default: false
|
|
1463
|
+
},
|
|
1464
|
+
ssrf: {
|
|
1465
|
+
formatter: boolean,
|
|
1466
|
+
default: false
|
|
1467
|
+
},
|
|
1468
|
+
rxss: {
|
|
1469
|
+
formatter: boolean,
|
|
1470
|
+
default: false
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1321
1473
|
}
|
|
1322
1474
|
},
|
|
1323
1475
|
|
|
@@ -5,9 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict'
|
|
7
7
|
|
|
8
|
-
const { DB, ALL } = require('../metrics/names')
|
|
9
|
-
const { DESTINATIONS } = require('../config/attribute-filter')
|
|
10
|
-
|
|
11
8
|
function ParsedStatement(type, operation, collection, raw) {
|
|
12
9
|
this.type = type
|
|
13
10
|
this.operation = operation
|
|
@@ -21,55 +18,4 @@ function ParsedStatement(type, operation, collection, raw) {
|
|
|
21
18
|
}
|
|
22
19
|
}
|
|
23
20
|
|
|
24
|
-
ParsedStatement.prototype.recordMetrics = function recordMetrics(segment, scope) {
|
|
25
|
-
const duration = segment.getDurationInMillis()
|
|
26
|
-
const exclusive = segment.getExclusiveDurationInMillis()
|
|
27
|
-
const transaction = segment.transaction
|
|
28
|
-
const type = transaction.isWeb() ? DB.WEB : DB.OTHER
|
|
29
|
-
const thisTypeSlash = this.type + '/'
|
|
30
|
-
const operation = DB.OPERATION + '/' + thisTypeSlash + this.operation
|
|
31
|
-
|
|
32
|
-
// Note, an operation metric should _always_ be created even if the action was
|
|
33
|
-
// a statement. This is part of the spec.
|
|
34
|
-
|
|
35
|
-
// Rollups
|
|
36
|
-
transaction.measure(operation, null, duration, exclusive)
|
|
37
|
-
transaction.measure(DB.PREFIX + type, null, duration, exclusive)
|
|
38
|
-
transaction.measure(DB.PREFIX + thisTypeSlash + type, null, duration, exclusive)
|
|
39
|
-
transaction.measure(DB.PREFIX + thisTypeSlash + ALL, null, duration, exclusive)
|
|
40
|
-
transaction.measure(DB.ALL, null, duration, exclusive)
|
|
41
|
-
|
|
42
|
-
// If we can parse the SQL statement, create a 'statement' metric, and use it
|
|
43
|
-
// as the scoped metric for transaction breakdowns. Otherwise, skip the
|
|
44
|
-
// 'statement' metric and use the 'operation' metric as the scoped metric for
|
|
45
|
-
// transaction breakdowns.
|
|
46
|
-
let collection
|
|
47
|
-
if (this.collection) {
|
|
48
|
-
collection = DB.STATEMENT + '/' + thisTypeSlash + this.collection + '/' + this.operation
|
|
49
|
-
transaction.measure(collection, null, duration, exclusive)
|
|
50
|
-
if (scope) {
|
|
51
|
-
transaction.measure(collection, scope, duration, exclusive)
|
|
52
|
-
}
|
|
53
|
-
} else if (scope) {
|
|
54
|
-
transaction.measure(operation, scope, duration, exclusive)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// This recorder is side-effectful Because we are depending on the recorder
|
|
58
|
-
// setting the transaction name, recorders must always be run before generating
|
|
59
|
-
// the final transaction trace
|
|
60
|
-
segment.name = collection || operation
|
|
61
|
-
|
|
62
|
-
// Datastore instance metrics.
|
|
63
|
-
const attributes = segment.attributes.get(DESTINATIONS.TRANS_SEGMENT)
|
|
64
|
-
if (attributes.host && attributes.port_path_or_id) {
|
|
65
|
-
const instanceName =
|
|
66
|
-
DB.INSTANCE + '/' + thisTypeSlash + attributes.host + '/' + attributes.port_path_or_id
|
|
67
|
-
transaction.measure(instanceName, null, duration, exclusive)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (this.raw) {
|
|
71
|
-
transaction.agent.queries.add(segment, this.type.toLowerCase(), this.raw, this.trace)
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
21
|
module.exports = ParsedStatement
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict'
|
|
7
|
+
|
|
8
|
+
const metrics = require('../names')
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Records all the metrics required for database operations.
|
|
12
|
+
*
|
|
13
|
+
* - `recordOperationMetrics(segment [, scope])`
|
|
14
|
+
*
|
|
15
|
+
* @private
|
|
16
|
+
* @this DatastoreShim
|
|
17
|
+
* @implements {MetricFunction}
|
|
18
|
+
* @param {TraceSegment} segment - The segment being recorded.
|
|
19
|
+
* @param {string} [scope] - The scope of the segment.
|
|
20
|
+
* @see DatastoreShim#recordOperation
|
|
21
|
+
* @see MetricFunction
|
|
22
|
+
*/
|
|
23
|
+
function recordOperationMetrics(segment, scope) {
|
|
24
|
+
if (!segment) {
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const duration = segment.getDurationInMillis()
|
|
29
|
+
const exclusive = segment.getExclusiveDurationInMillis()
|
|
30
|
+
const transaction = segment.transaction
|
|
31
|
+
const type = transaction.isWeb() ? 'allWeb' : 'allOther'
|
|
32
|
+
const operation = segment.name
|
|
33
|
+
|
|
34
|
+
if (scope) {
|
|
35
|
+
transaction.measure(operation, scope, duration, exclusive)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
transaction.measure(operation, null, duration, exclusive)
|
|
39
|
+
transaction.measure(metrics.DB.PREFIX + type, null, duration, exclusive)
|
|
40
|
+
transaction.measure(metrics.DB.ALL, null, duration, exclusive)
|
|
41
|
+
transaction.measure(this._metrics.ALL, null, duration, exclusive)
|
|
42
|
+
transaction.measure(
|
|
43
|
+
metrics.DB.PREFIX + this._metrics.PREFIX + '/' + type,
|
|
44
|
+
null,
|
|
45
|
+
duration,
|
|
46
|
+
exclusive
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
const attributes = segment.getAttributes()
|
|
50
|
+
if (attributes.host && attributes.port_path_or_id) {
|
|
51
|
+
const instanceName = [
|
|
52
|
+
metrics.DB.INSTANCE,
|
|
53
|
+
this._metrics.PREFIX,
|
|
54
|
+
attributes.host,
|
|
55
|
+
attributes.port_path_or_id
|
|
56
|
+
].join('/')
|
|
57
|
+
|
|
58
|
+
transaction.measure(instanceName, null, duration, exclusive)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = recordOperationMetrics
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict'
|
|
7
|
+
|
|
8
|
+
const { DB, ALL } = require('../names')
|
|
9
|
+
const { DESTINATIONS } = require('../../config/attribute-filter')
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @this ParsedStatement
|
|
13
|
+
* @param {TraceSegment} segment - The segment being recorded.
|
|
14
|
+
* @param {string} [scope] - The scope of the segment.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
function recordQueryMetrics(segment, scope) {
|
|
18
|
+
const duration = segment.getDurationInMillis()
|
|
19
|
+
const exclusive = segment.getExclusiveDurationInMillis()
|
|
20
|
+
const transaction = segment.transaction
|
|
21
|
+
const type = transaction.isWeb() ? DB.WEB : DB.OTHER
|
|
22
|
+
const thisTypeSlash = this.type + '/'
|
|
23
|
+
const operation = DB.OPERATION + '/' + thisTypeSlash + this.operation
|
|
24
|
+
|
|
25
|
+
// Note, an operation metric should _always_ be created even if the action was
|
|
26
|
+
// a statement. This is part of the spec.
|
|
27
|
+
|
|
28
|
+
// Rollups
|
|
29
|
+
transaction.measure(operation, null, duration, exclusive)
|
|
30
|
+
transaction.measure(DB.PREFIX + type, null, duration, exclusive)
|
|
31
|
+
transaction.measure(DB.PREFIX + thisTypeSlash + type, null, duration, exclusive)
|
|
32
|
+
transaction.measure(DB.PREFIX + thisTypeSlash + ALL, null, duration, exclusive)
|
|
33
|
+
transaction.measure(DB.ALL, null, duration, exclusive)
|
|
34
|
+
|
|
35
|
+
// If we can parse the SQL statement, create a 'statement' metric, and use it
|
|
36
|
+
// as the scoped metric for transaction breakdowns. Otherwise, skip the
|
|
37
|
+
// 'statement' metric and use the 'operation' metric as the scoped metric for
|
|
38
|
+
// transaction breakdowns.
|
|
39
|
+
let collection
|
|
40
|
+
if (this.collection) {
|
|
41
|
+
collection = DB.STATEMENT + '/' + thisTypeSlash + this.collection + '/' + this.operation
|
|
42
|
+
transaction.measure(collection, null, duration, exclusive)
|
|
43
|
+
if (scope) {
|
|
44
|
+
transaction.measure(collection, scope, duration, exclusive)
|
|
45
|
+
}
|
|
46
|
+
} else if (scope) {
|
|
47
|
+
transaction.measure(operation, scope, duration, exclusive)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// This recorder is side-effectful Because we are depending on the recorder
|
|
51
|
+
// setting the transaction name, recorders must always be run before generating
|
|
52
|
+
// the final transaction trace
|
|
53
|
+
segment.name = collection || operation
|
|
54
|
+
|
|
55
|
+
// Datastore instance metrics.
|
|
56
|
+
const attributes = segment.attributes.get(DESTINATIONS.TRANS_SEGMENT)
|
|
57
|
+
if (attributes.host && attributes.port_path_or_id) {
|
|
58
|
+
const instanceName =
|
|
59
|
+
DB.INSTANCE + '/' + thisTypeSlash + attributes.host + '/' + attributes.port_path_or_id
|
|
60
|
+
transaction.measure(instanceName, null, duration, exclusive)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (this.raw) {
|
|
64
|
+
segment.transaction.agent.queries.add(segment, this.type.toLowerCase(), this.raw, this.trace)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
module.exports = recordQueryMetrics
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates a recorder for middleware metrics.
|
|
10
|
+
*
|
|
11
|
+
* @private
|
|
12
|
+
* @param {object} _shim instance of shim
|
|
13
|
+
* @param {string} metricName name of metric
|
|
14
|
+
* @returns {Function} recorder for middleware
|
|
15
|
+
*/
|
|
16
|
+
function makeMiddlewareRecorder(_shim, metricName) {
|
|
17
|
+
return function middlewareMetricRecorder(segment, scope) {
|
|
18
|
+
const duration = segment.getDurationInMillis()
|
|
19
|
+
const exclusive = segment.getExclusiveDurationInMillis()
|
|
20
|
+
const transaction = segment.transaction
|
|
21
|
+
|
|
22
|
+
if (scope) {
|
|
23
|
+
transaction.measure(metricName, scope, duration, exclusive)
|
|
24
|
+
}
|
|
25
|
+
transaction.measure(metricName, null, duration, exclusive)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = makeMiddlewareRecorder
|
|
@@ -249,7 +249,9 @@ class AwsLambda {
|
|
|
249
249
|
_getAwsAgentAttributes(event, context) {
|
|
250
250
|
const attributes = {
|
|
251
251
|
'aws.lambda.arn': context.invokedFunctionArn,
|
|
252
|
-
'aws.requestId': context.awsRequestId
|
|
252
|
+
'aws.requestId': context.awsRequestId,
|
|
253
|
+
'cloud.resource_id': context.invokedFunctionArn,
|
|
254
|
+
'cloud.platform': 'aws_lambda'
|
|
253
255
|
}
|
|
254
256
|
|
|
255
257
|
const eventSourceInfo = this._detectEventType(event)
|
|
@@ -15,7 +15,9 @@ const Shim = require('./shim')
|
|
|
15
15
|
const urltils = require('../util/urltils')
|
|
16
16
|
const util = require('util')
|
|
17
17
|
const specs = require('./specs')
|
|
18
|
+
const recordOperationMetrics = require('../../lib/metrics/recorders/database-operation')
|
|
18
19
|
const { DatastoreParameters } = specs.params
|
|
20
|
+
const recordQueryMetrics = require('../../lib/metrics/recorders/database')
|
|
19
21
|
|
|
20
22
|
/**
|
|
21
23
|
* An enumeration of well-known datastores so that new instrumentations can use
|
|
@@ -284,7 +286,7 @@ function recordOperation(nodule, properties, opSpec) {
|
|
|
284
286
|
if (!segDesc?.name?.startsWith(shim._metrics.OPERATION)) {
|
|
285
287
|
segDesc.name = shim._metrics.OPERATION + segDesc.name
|
|
286
288
|
}
|
|
287
|
-
segDesc.recorder =
|
|
289
|
+
segDesc.recorder = recordOperationMetrics.bind(shim)
|
|
288
290
|
}
|
|
289
291
|
|
|
290
292
|
return segDesc
|
|
@@ -567,7 +569,7 @@ function _recordQuery(suffix, nodule, properties, querySpec) {
|
|
|
567
569
|
const name = (parsed.collection || 'other') + '/' + parsed.operation + suffix
|
|
568
570
|
shim.logger.trace('Found and parsed query %s -> %s', parsed.type, name)
|
|
569
571
|
segDesc.name = shim._metrics.STATEMENT + name
|
|
570
|
-
segDesc.recorder =
|
|
572
|
+
segDesc.recorder = recordQueryMetrics.bind(parsed)
|
|
571
573
|
}
|
|
572
574
|
|
|
573
575
|
return segDesc
|
|
@@ -598,72 +600,6 @@ function _getSpec({ spec, shim, fn, fnName, args }) {
|
|
|
598
600
|
return dsSpec
|
|
599
601
|
}
|
|
600
602
|
|
|
601
|
-
/**
|
|
602
|
-
* Records all query metrics when a segment is active
|
|
603
|
-
*
|
|
604
|
-
* @private
|
|
605
|
-
* @param {ParsedStatement} parsed instance of ParsedStatement
|
|
606
|
-
* @param {TraceSegment} segment active segment
|
|
607
|
-
* @param {string} scope scope of metrics if it exists
|
|
608
|
-
*/
|
|
609
|
-
function _recordQueryMetrics(parsed, segment, scope) {
|
|
610
|
-
if (segment) {
|
|
611
|
-
parsed.recordMetrics(segment, scope)
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
/**
|
|
616
|
-
* Records all the metrics required for database operations.
|
|
617
|
-
*
|
|
618
|
-
* - `_recordOperationMetrics(segment [, scope])`
|
|
619
|
-
*
|
|
620
|
-
* @private
|
|
621
|
-
* @this DatastoreShim
|
|
622
|
-
* @implements {MetricFunction}
|
|
623
|
-
* @param {TraceSegment} segment - The segment being recorded.
|
|
624
|
-
* @param {string} [scope] - The scope of the segment.
|
|
625
|
-
* @see DatastoreShim#recordOperation
|
|
626
|
-
* @see MetricFunction
|
|
627
|
-
*/
|
|
628
|
-
function _recordOperationMetrics(segment, scope) {
|
|
629
|
-
if (!segment) {
|
|
630
|
-
return
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
const duration = segment.getDurationInMillis()
|
|
634
|
-
const exclusive = segment.getExclusiveDurationInMillis()
|
|
635
|
-
const transaction = segment.transaction
|
|
636
|
-
const type = transaction.isWeb() ? 'allWeb' : 'allOther'
|
|
637
|
-
const operation = segment.name
|
|
638
|
-
|
|
639
|
-
if (scope) {
|
|
640
|
-
transaction.measure(operation, scope, duration, exclusive)
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
transaction.measure(operation, null, duration, exclusive)
|
|
644
|
-
transaction.measure(metrics.DB.PREFIX + type, null, duration, exclusive)
|
|
645
|
-
transaction.measure(metrics.DB.ALL, null, duration, exclusive)
|
|
646
|
-
transaction.measure(this._metrics.ALL, null, duration, exclusive)
|
|
647
|
-
transaction.measure(
|
|
648
|
-
metrics.DB.PREFIX + this._metrics.PREFIX + '/' + type,
|
|
649
|
-
null,
|
|
650
|
-
duration,
|
|
651
|
-
exclusive
|
|
652
|
-
)
|
|
653
|
-
|
|
654
|
-
const attributes = segment.getAttributes()
|
|
655
|
-
if (attributes.host && attributes.port_path_or_id) {
|
|
656
|
-
const instanceName = [
|
|
657
|
-
metrics.DB.INSTANCE,
|
|
658
|
-
this._metrics.PREFIX,
|
|
659
|
-
attributes.host,
|
|
660
|
-
attributes.port_path_or_id
|
|
661
|
-
].join('/')
|
|
662
|
-
|
|
663
|
-
transaction.measure(instanceName, null, duration, exclusive)
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
|
|
667
603
|
/**
|
|
668
604
|
* Extracts the query string from the arguments according to the given spec.
|
|
669
605
|
*
|
|
@@ -13,6 +13,7 @@ const {
|
|
|
13
13
|
} = require('./common')
|
|
14
14
|
const { assignCLMSymbol } = require('../../util/code-level-metrics')
|
|
15
15
|
const { RecorderSpec } = require('../specs')
|
|
16
|
+
const makeMiddlewareRecorder = require('../../metrics/recorders/middleware')
|
|
16
17
|
|
|
17
18
|
const MIDDLEWARE_TYPE_DETAILS = {
|
|
18
19
|
APPLICATION: { name: 'Mounted App: ', path: true, record: false },
|
|
@@ -88,7 +89,7 @@ function constructRecorder({ txInfo, typeDetails, shim, metricName }) {
|
|
|
88
89
|
let recorder = null
|
|
89
90
|
if (typeDetails.record) {
|
|
90
91
|
const stackPath = txInfo.transaction.nameState.getPath() || ''
|
|
91
|
-
recorder =
|
|
92
|
+
recorder = makeMiddlewareRecorder(shim, metricName + '/' + stackPath)
|
|
92
93
|
}
|
|
93
94
|
return recorder
|
|
94
95
|
}
|
|
@@ -325,27 +326,6 @@ module.exports._recordMiddleware = function _recordMiddleware(shim, middleware,
|
|
|
325
326
|
)
|
|
326
327
|
}
|
|
327
328
|
|
|
328
|
-
/**
|
|
329
|
-
* Creates a recorder for middleware metrics.
|
|
330
|
-
*
|
|
331
|
-
* @private
|
|
332
|
-
* @param {object} _shim instance of shim
|
|
333
|
-
* @param {string} metricName name of metric
|
|
334
|
-
* @returns {Function} recorder for middleware
|
|
335
|
-
*/
|
|
336
|
-
function _makeMiddlewareRecorder(_shim, metricName) {
|
|
337
|
-
return function middlewareMetricRecorder(segment, scope) {
|
|
338
|
-
const duration = segment.getDurationInMillis()
|
|
339
|
-
const exclusive = segment.getExclusiveDurationInMillis()
|
|
340
|
-
const transaction = segment.transaction
|
|
341
|
-
|
|
342
|
-
if (scope) {
|
|
343
|
-
transaction.measure(metricName, scope, duration, exclusive)
|
|
344
|
-
}
|
|
345
|
-
transaction.measure(metricName, null, duration, exclusive)
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
329
|
/**
|
|
350
330
|
* Wrap the `next` middleware function and push on our name state if we find it. We only want to
|
|
351
331
|
* push the name state if there is a next so that we can safely remove it
|
|
@@ -116,11 +116,27 @@ function fetchDockerVendorInfo(agent, callback, logger = log) {
|
|
|
116
116
|
logger.debug(
|
|
117
117
|
`${CGROUPS_V2_PATH} not found, trying to parse container id from ${CGROUPS_V1_PATH}`
|
|
118
118
|
)
|
|
119
|
-
findCGroupsV1(callback)
|
|
119
|
+
findCGroupsV1(callback, logger)
|
|
120
120
|
return
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
parseCGroupsV2(
|
|
123
|
+
parseCGroupsV2(
|
|
124
|
+
data,
|
|
125
|
+
(_, v2Data) => {
|
|
126
|
+
if (v2Data !== null) {
|
|
127
|
+
// We found a valid Docker identifier in the v2 file, so we are going
|
|
128
|
+
// to prioritize it.
|
|
129
|
+
return callback(null, v2Data)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// For some reason, we have a /proc/self/mountinfo but it does not have
|
|
133
|
+
// any Docker information in it (that we have detected). So we will
|
|
134
|
+
// fall back to trying the cgroups v1 file.
|
|
135
|
+
logger.debug(`Attempting to fall back to cgroups v1 parsing.`)
|
|
136
|
+
findCGroupsV1(callback, logger)
|
|
137
|
+
},
|
|
138
|
+
logger
|
|
139
|
+
)
|
|
124
140
|
})
|
|
125
141
|
}
|
|
126
142
|
|
|
@@ -136,6 +152,7 @@ function parseCGroupsV2(data, callback, logger = log) {
|
|
|
136
152
|
const containerLine = new RegExp('/docker/containers/([0-9a-f]{64})/')
|
|
137
153
|
const line = containerLine.exec(data)
|
|
138
154
|
if (line) {
|
|
155
|
+
logger.debug(`Found docker id from cgroups v2: ${line[1]}`)
|
|
139
156
|
callback(null, { id: line[1] })
|
|
140
157
|
} else {
|
|
141
158
|
logger.debug(`Found ${CGROUPS_V2_PATH} but failed to parse Docker container id.`)
|
|
@@ -169,7 +186,8 @@ function findCGroupsV1(callback, logger = log) {
|
|
|
169
186
|
})
|
|
170
187
|
|
|
171
188
|
if (id) {
|
|
172
|
-
vendorInfo = { id
|
|
189
|
+
vendorInfo = { id }
|
|
190
|
+
logger.debug(`Found docker id from cgroups v1: ${id}`)
|
|
173
191
|
callback(null, vendorInfo)
|
|
174
192
|
} else {
|
|
175
193
|
logger.debug('No matching cpu group found.')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "newrelic",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.6.1",
|
|
4
4
|
"author": "New Relic Node.js agent team <nodejs@newrelic.com>",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"contributors": [
|
|
@@ -163,15 +163,14 @@
|
|
|
163
163
|
"docs": "rm -rf ./out && jsdoc -c ./jsdoc-conf.jsonc --private -r .",
|
|
164
164
|
"integration": "npm run prepare-test && npm run sub-install && BORP_CONF_FILE=.borp.int.yaml time c8 -o ./coverage/integration borp --timeout 600000 --reporter ./test/lib/test-reporter.mjs",
|
|
165
165
|
"integration:esm": "NODE_OPTIONS='--loader=./esm-loader.mjs' BORP_CONF_FILE=.borp.int-esm.yaml time c8 -o ./coverage/integration-esm borp --reporter ./test/lib/test-reporter.mjs",
|
|
166
|
-
"prepare-test": "npm run
|
|
166
|
+
"prepare-test": "npm run docker-env",
|
|
167
167
|
"lint": "eslint ./*.{js,mjs} lib test bin",
|
|
168
168
|
"lint:fix": "eslint --fix, ./*.{js,mjs} lib test bin",
|
|
169
169
|
"public-docs": "jsdoc -c ./jsdoc-conf.jsonc",
|
|
170
170
|
"publish-docs": "./bin/publish-docs.sh",
|
|
171
171
|
"services": "DOCKER_PLATFORM=linux/$(uname -m) docker compose up -d --wait",
|
|
172
172
|
"services:stop": "docker compose down",
|
|
173
|
-
"smoke": "
|
|
174
|
-
"ssl": "./bin/ssl.sh",
|
|
173
|
+
"smoke": "time borp --timeout 180000 --reporter ./test/lib/test-reporter.mjs 'test/smoke/**/*.{test,tap}.js'",
|
|
175
174
|
"sub-install": "node test/bin/install_sub_deps",
|
|
176
175
|
"test": "npm run integration && npm run unit",
|
|
177
176
|
"third-party-updates": "oss third-party manifest --includeOptDeps && oss third-party notices --includeOptDeps && git add THIRD_PARTY_NOTICES.md third_party_manifest.json",
|
|
@@ -195,7 +194,7 @@
|
|
|
195
194
|
"newrelic-naming-rules": "./bin/test-naming-rules.js"
|
|
196
195
|
},
|
|
197
196
|
"dependencies": {
|
|
198
|
-
"@grpc/grpc-js": "^1.
|
|
197
|
+
"@grpc/grpc-js": "^1.12.2",
|
|
199
198
|
"@grpc/proto-loader": "^0.7.5",
|
|
200
199
|
"@newrelic/security-agent": "^2.0.0",
|
|
201
200
|
"@tyriar/fibonacci-heap": "^2.0.7",
|
|
@@ -253,7 +252,6 @@
|
|
|
253
252
|
"lint-staged": "^11.0.0",
|
|
254
253
|
"lockfile-lint": "^4.9.6",
|
|
255
254
|
"nock": "11.8.0",
|
|
256
|
-
"proxy": "^2.1.1",
|
|
257
255
|
"proxyquire": "^1.8.0",
|
|
258
256
|
"rimraf": "^2.6.3",
|
|
259
257
|
"self-cert": "^2.0.0",
|