Skip to content

Commit 9a14cb0

Browse files
authoredApr 26, 2024··
fix: Fixed issue with CJS being imported as ESM (#2168)
1 parent cb21d2c commit 9a14cb0

14 files changed

+394
-848
lines changed
 

‎.github/workflows/ci-workflow.yml

+10
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ jobs:
9494
if: needs.skip_if_release.outputs.should_skip != 'true'
9595
runs-on: ubuntu-latest
9696

97+
env:
98+
NODE_NO_WARNINGS: 1
99+
97100
strategy:
98101
fail-fast: false
99102
matrix:
@@ -111,11 +114,18 @@ jobs:
111114
run: npm run services
112115
- name: Run Integration Tests
113116
run: npm run integration
117+
- name: Run ESM Integration Tests
118+
run: npm run integration:esm
114119
- name: Archive Integration Test Coverage
115120
uses: actions/upload-artifact@v3
116121
with:
117122
name: integration-tests-${{ matrix.node-version }}
118123
path: ./coverage/integration/lcov.info
124+
- name: Archive Integration (ESM) Test Coverage
125+
uses: actions/upload-artifact@v3
126+
with:
127+
name: integration-tests-${{ matrix.node-version }}
128+
path: ./coverage/integration-esm/lcov.info
119129

120130
versioned-internal:
121131
needs: skip_if_release

‎THIRD_PARTY_NOTICES.md

+152-770
Large diffs are not rendered by default.

‎lib/instrumentation-tracker.js

+17
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,23 @@ class InstrumentationTracker {
217217
}
218218
}
219219
}
220+
221+
/**
222+
* Given a full absolute path to a module, look up the instrumentation
223+
* associated with it and return the name for that instrumentation.
224+
*
225+
* @param {string} modulePath The path to the module being instrumented.
226+
*
227+
* @returns {string|undefined} The name of the module.
228+
*/
229+
simpleNameFromPath(modulePath) {
230+
for (const [key, items] of this.#tracked.entries()) {
231+
const instrumentation = items.find((i) => i.instrumentation.absolutePath === modulePath)
232+
if (instrumentation) {
233+
return key
234+
}
235+
}
236+
}
220237
}
221238

222239
module.exports = InstrumentationTracker

‎lib/instrumentation/fastify.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ module.exports = function initialize(agent, fastify, moduleName, shim) {
9595
*/
9696
const wrappedExport = shim.wrapExport(fastify, function wrapFastifyModule(shim, fn) {
9797
return function wrappedFastifyModule() {
98-
// call original function get get fastify object (which is singleton-ish)
98+
// call original function to get the fastify object (which is singleton-ish)
9999
const fastifyForWrapping = fn.apply(this, arguments)
100100

101101
setupRouteHandler(shim, fastifyForWrapping)

‎lib/shim/shim.js

+7
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,13 @@ function wrapClass(nodule, properties, spec, args) {
563563
* @returns {*} The return value from `spec`.
564564
*/
565565
function wrapExport(nodule, spec) {
566+
if (nodule[symbols.nrEsmProxy] === true) {
567+
// A CJS module has been imported as ESM through import-in-the-middle. This
568+
// means that `nodule` is set to an instance of our proxy. What we actually
569+
// want is the thing to be instrumented. We assume it is the "default"
570+
// export.
571+
nodule = nodule.default
572+
}
566573
return (this._toExport = this.wrap(nodule, null, spec))
567574
}
568575

‎lib/shimmer.js

+17
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const shims = require('./shim')
1414
const { Hook } = require('@newrelic/ritm')
1515
const IitmHook = require('import-in-the-middle')
1616
const { nrEsmProxy } = require('./symbols')
17+
const isAbsolutePath = require('./util/is-absolute-path')
1718
const InstrumentationDescriptor = require('./instrumentation-descriptor')
1819
const InstrumentationTracker = require('./instrumentation-tracker')
1920
let pkgsToHook = []
@@ -463,6 +464,12 @@ const shimmer = (module.exports = {
463464
moduleName = 'pg'
464465
}
465466

467+
if (isAbsolutePath(moduleName) === true) {
468+
// moduleName is an absolute path to a module. So we need to look up
469+
// the simple name from the registered instrumentations.
470+
return this.registeredInstrumentations.simpleNameFromPath(moduleName)
471+
}
472+
466473
return moduleName
467474
},
468475

@@ -733,6 +740,16 @@ function _postLoad(agent, nodule, name, resolvedName, esmResolver) {
733740

734741
// Check if this is a known instrumentation and then run it.
735742
if (hasPostLoadInstrumentation) {
743+
if (resolvedName === undefined && isAbsolutePath(name) === true) {
744+
// `resolvedName` comes from the `basedir` returned by the `Hook`
745+
// function from import-in-the-middle or require-in-the-middle. At least
746+
// with IITM, if the path string does not include a `node_modules` then
747+
// `basedir` will be `undefined`. But we need it for our instrumentation
748+
// to work. We'll only reach this situation if the module being
749+
// instrumented has an `absolutePath` defined. So we detect that and
750+
// assign appropriately.
751+
resolvedName = name
752+
}
736753
shimmer.registeredInstrumentations.setResolvedName(simpleName, resolvedName)
737754
logger.trace('Instrumenting %s with onRequire (module loaded) hook.', name)
738755
return instrumentPostLoad(agent, nodule, simpleName, resolvedName, esmResolver)

‎lib/util/is-absolute-path.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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+
* Determines if a given string represents an absolute path to a module.
10+
*
11+
* @param {string} target Path to a module.
12+
*
13+
* @returns {boolean} True if it is an absolute path.
14+
*/
15+
module.exports = function isAbsolutePath(target) {
16+
const leadChar = target.slice(0, 1)
17+
if (leadChar !== '.' && leadChar !== '/') {
18+
return false
19+
}
20+
21+
const suffix = target.slice(-4)
22+
/* eslint-disable-next-line */
23+
if (suffix.slice(-3) !== '.js' && suffix !== '.cjs' && suffix !== '.mjs') {
24+
return false
25+
}
26+
27+
return true
28+
}

‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@
157157
"docker-env": "./bin/docker-env-vars.sh",
158158
"docs": "npm ci && jsdoc -c ./jsdoc-conf.json --private -r .",
159159
"integration": "npm run prepare-test && npm run sub-install && time c8 -o ./coverage/integration tap --test-regex='(\\/|^test\\/integration\\/.*\\.tap\\.js)$' --timeout=600 --no-coverage --reporter classic",
160+
"integration:esm": "time c8 -o ./coverage/integration-esm tap --node-arg='--loader=./esm-loader.mjs' --test-regex='(test\\/integration\\/.*\\.tap\\.mjs)$' --timeout=600 --no-coverage --reporter classic",
160161
"prepare-test": "npm run ssl && npm run docker-env",
161162
"lint": "eslint ./*.{js,mjs} lib test bin examples",
162163
"lint:fix": "eslint --fix, ./*.{js,mjs} lib test bin examples",

‎test/integration/issue-2155/foo.cjs

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2020 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
'use strict'
7+
8+
// A fake module that will be imported as ESM. Basically, the issue is that
9+
// CJS imported as ESM needs its exports proxied and our `shim.wrapExport`
10+
// needs to recognize the "original" export in order to pass it in to the
11+
// instrumentation.
12+
13+
function foo() {
14+
return Object.create({
15+
name() {
16+
return 'foo'
17+
}
18+
})
19+
}
20+
21+
// This triplet export replicates they way Fastify solves the CJS utilized in
22+
// ESM issue. It makes it possible to `import foo from './foo.cjs'` or to
23+
// `import { foo } from './foo.cjs'`. It also allows us to replicate the
24+
// issue at hand.
25+
module.exports = foo
26+
module.exports.default = foo
27+
module.exports.foo = foo
+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2024 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import tap from 'tap'
7+
import crypto from 'crypto'
8+
import path from 'path'
9+
import url from 'url'
10+
import helper from '../../lib/agent_helper.js'
11+
import shimmer from '../../../lib/shimmer.js'
12+
import InstrumentationDescriptor from '../../../lib/instrumentation-descriptor.js'
13+
14+
let modPath
15+
if (import.meta.dirname) {
16+
modPath = path.join(import.meta.dirname, 'foo.cjs')
17+
} else {
18+
modPath = path.join(path.dirname(url.fileURLToPath(import.meta.url)), 'foo.cjs')
19+
}
20+
21+
function instrumentation(shim, resolvedModule) {
22+
shim.wrapExport(resolvedModule, function wrapModule(shim, fn) {
23+
return function wrappedModule() {
24+
// `fn` _should_ be the `foo()` function exported by the module.
25+
// If it is anything else, i.e. the proxy object, then we have an error
26+
// in our handling of CJS modules as ESM.
27+
const foo = fn.apply(this, arguments)
28+
const _name = foo.name
29+
foo.name = () => {
30+
const value = _name.call(foo)
31+
return `wrapped: ${value}`
32+
}
33+
return foo
34+
}
35+
})
36+
}
37+
38+
tap.beforeEach(async (t) => {
39+
shimmer.registerInstrumentation({
40+
type: InstrumentationDescriptor.TYPE_GENERIC,
41+
moduleName: 'foo',
42+
absolutePath: modPath,
43+
onRequire: instrumentation
44+
})
45+
46+
const agent = helper.instrumentMockedAgent()
47+
t.context.agent = agent
48+
49+
const { default: foo } = await import('./foo.cjs?v=' + crypto.randomBytes(16).toString('hex'))
50+
t.context.mod = foo
51+
})
52+
53+
tap.afterEach((t) => {
54+
helper.unloadAgent(t.context.agent)
55+
})
56+
57+
tap.test('CJS imported as ESM gets wrapped correctly', async (t) => {
58+
const { mod } = t.context
59+
const instance = mod()
60+
t.equal(instance.name(), 'wrapped: foo')
61+
})

‎test/unit/is-absolute-path.test.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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 tap = require('tap')
9+
const isAbsolutePath = require('../../lib/util/is-absolute-path')
10+
11+
tap.test('verifies paths correctly', async (t) => {
12+
const tests = [
13+
['./foo/bar.js', true],
14+
['/foo/bar.cjs', true],
15+
['/foo.mjs', true],
16+
['/foo.smj', false],
17+
['foo', false],
18+
['foo.js', false]
19+
]
20+
21+
for (const [input, expected] of tests) {
22+
t.equal(isAbsolutePath(input), expected)
23+
}
24+
})

‎test/versioned/esm-package/parse-json-instrumentation.mjs

-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
'use-strict'
7-
86
export default function initialize(shim, parseJson) {
97
shim.wrap(parseJson, 'default', function wrappedParseJsonLib(_shim, orig) {
108
return function wrappedParseJsonFunc() {

‎test/versioned/winston-esm/winston.tap.mjs

+14-1
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,26 @@
66
import tap from 'tap'
77
import { randomUUID } from 'node:crypto'
88
import fs from 'node:fs/promises'
9+
import path from 'node:path'
10+
import url from 'node:url'
911
import semver from 'semver'
1012
import helper from '../../lib/agent_helper.js'
1113
import names from '../../../lib/metrics/names.js'
1214
import { Sink } from './common.mjs'
1315

1416
const { LOGGING } = names
15-
const winstonPkg = JSON.parse(await fs.readFile('./node_modules/winston/package.json'))
17+
let pkgPath
18+
if (import.meta.dirname) {
19+
pkgPath = path.join(import.meta.dirname, 'node_modules', 'winston', 'package.json')
20+
} else {
21+
pkgPath = path.join(
22+
path.dirname(url.fileURLToPath(import.meta.url)),
23+
'node_modules',
24+
'winston',
25+
'package.json'
26+
)
27+
}
28+
const winstonPkg = JSON.parse(await fs.readFile(pkgPath))
1629

1730
tap.skip = true
1831

‎third_party_manifest.json

+35-74
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"lastUpdated": "Wed Apr 24 2024 12:06:09 GMT-0400 (Eastern Daylight Time)",
2+
"lastUpdated": "Fri Apr 26 2024 14:57:01 GMT-0400 (Eastern Daylight Time)",
33
"projectName": "New Relic Node Agent",
44
"projectUrl": "https://github.com/newrelic/node-newrelic",
55
"includeOptDeps": true,
@@ -16,15 +16,15 @@
1616
"licenseTextSource": "file",
1717
"publisher": "Contrast Security"
1818
},
19-
"@newrelic/native-metrics@10.0.1": {
19+
"@newrelic/native-metrics@10.1.1": {
2020
"name": "@newrelic/native-metrics",
21-
"version": "10.0.1",
21+
"version": "10.1.1",
2222
"range": "^10.0.0",
2323
"licenses": "Apache-2.0",
2424
"repoUrl": "https://github.com/newrelic/node-native-metrics",
25-
"versionedRepoUrl": "https://github.com/newrelic/node-native-metrics/tree/v10.0.1",
25+
"versionedRepoUrl": "https://github.com/newrelic/node-native-metrics/tree/v10.1.1",
2626
"licenseFile": "node_modules/@newrelic/native-metrics/LICENSE",
27-
"licenseUrl": "https://github.com/newrelic/node-native-metrics/blob/v10.0.1/LICENSE",
27+
"licenseUrl": "https://github.com/newrelic/node-native-metrics/blob/v10.1.1/LICENSE",
2828
"licenseTextSource": "file",
2929
"publisher": "New Relic Node.js agent team",
3030
"email": "nodejs@newrelic.com"
@@ -44,27 +44,27 @@
4444
},
4545
"includeDev": true,
4646
"dependencies": {
47-
"@grpc/grpc-js@1.10.0": {
47+
"@grpc/grpc-js@1.10.6": {
4848
"name": "@grpc/grpc-js",
49-
"version": "1.10.0",
49+
"version": "1.10.6",
5050
"range": "^1.9.4",
5151
"licenses": "Apache-2.0",
5252
"repoUrl": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js",
53-
"versionedRepoUrl": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js/tree/v1.10.0",
53+
"versionedRepoUrl": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js/tree/v1.10.6",
5454
"licenseFile": "node_modules/@grpc/grpc-js/LICENSE",
55-
"licenseUrl": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js/blob/v1.10.0/LICENSE",
55+
"licenseUrl": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js/blob/v1.10.6/LICENSE",
5656
"licenseTextSource": "file",
5757
"publisher": "Google Inc."
5858
},
59-
"@grpc/proto-loader@0.7.10": {
59+
"@grpc/proto-loader@0.7.12": {
6060
"name": "@grpc/proto-loader",
61-
"version": "0.7.10",
61+
"version": "0.7.12",
6262
"range": "^0.7.5",
6363
"licenses": "Apache-2.0",
6464
"repoUrl": "https://github.com/grpc/grpc-node",
65-
"versionedRepoUrl": "https://github.com/grpc/grpc-node/tree/v0.7.10",
65+
"versionedRepoUrl": "https://github.com/grpc/grpc-node/tree/v0.7.12",
6666
"licenseFile": "node_modules/@grpc/proto-loader/LICENSE",
67-
"licenseUrl": "https://github.com/grpc/grpc-node/blob/v0.7.10/LICENSE",
67+
"licenseUrl": "https://github.com/grpc/grpc-node/blob/v0.7.12/LICENSE",
6868
"licenseTextSource": "file",
6969
"publisher": "Google Inc."
7070
},
@@ -82,15 +82,15 @@
8282
"email": "w@tson.dk",
8383
"url": "https://twitter.com/wa7son"
8484
},
85-
"@newrelic/security-agent@1.1.1": {
85+
"@newrelic/security-agent@1.2.0": {
8686
"name": "@newrelic/security-agent",
87-
"version": "1.1.1",
87+
"version": "1.2.0",
8888
"range": "^1.1.1",
8989
"licenses": "UNKNOWN",
9090
"repoUrl": "https://github.com/newrelic/csec-node-agent",
91-
"versionedRepoUrl": "https://github.com/newrelic/csec-node-agent/tree/v1.1.1",
91+
"versionedRepoUrl": "https://github.com/newrelic/csec-node-agent/tree/v1.2.0",
9292
"licenseFile": "node_modules/@newrelic/security-agent/LICENSE",
93-
"licenseUrl": "https://github.com/newrelic/csec-node-agent/blob/v1.1.1/LICENSE",
93+
"licenseUrl": "https://github.com/newrelic/csec-node-agent/blob/v1.2.0/LICENSE",
9494
"licenseTextSource": "file",
9595
"publisher": "newrelic"
9696
},
@@ -120,16 +120,16 @@
120120
"publisher": "Max Ogden",
121121
"email": "max@maxogden.com"
122122
},
123-
"https-proxy-agent@7.0.2": {
123+
"https-proxy-agent@7.0.4": {
124124
"name": "https-proxy-agent",
125-
"version": "7.0.2",
125+
"version": "7.0.4",
126126
"range": "^7.0.1",
127127
"licenses": "MIT",
128128
"repoUrl": "https://github.com/TooTallNate/proxy-agents",
129-
"versionedRepoUrl": "https://github.com/TooTallNate/proxy-agents/tree/v7.0.2",
130-
"licenseFile": "node_modules/https-proxy-agent/README.md",
131-
"licenseUrl": "https://github.com/TooTallNate/proxy-agents/blob/v7.0.2/README.md",
132-
"licenseTextSource": "spdx",
129+
"versionedRepoUrl": "https://github.com/TooTallNate/proxy-agents/tree/v7.0.4",
130+
"licenseFile": "node_modules/https-proxy-agent/LICENSE",
131+
"licenseUrl": "https://github.com/TooTallNate/proxy-agents/blob/v7.0.4/LICENSE",
132+
"licenseTextSource": "file",
133133
"publisher": "Nathan Rajlich",
134134
"email": "nathan@tootallnate.net",
135135
"url": "http://n8.io/"
@@ -226,32 +226,6 @@
226226
}
227227
},
228228
"devDependencies": {
229-
"@aws-sdk/client-s3@3.556.0": {
230-
"name": "@aws-sdk/client-s3",
231-
"version": "3.556.0",
232-
"range": "^3.556.0",
233-
"licenses": "Apache-2.0",
234-
"repoUrl": "https://github.com/aws/aws-sdk-js-v3",
235-
"versionedRepoUrl": "https://github.com/aws/aws-sdk-js-v3/tree/v3.556.0",
236-
"licenseFile": "node_modules/@aws-sdk/client-s3/LICENSE",
237-
"licenseUrl": "https://github.com/aws/aws-sdk-js-v3/blob/v3.556.0/LICENSE",
238-
"licenseTextSource": "file",
239-
"publisher": "AWS SDK for JavaScript Team",
240-
"url": "https://aws.amazon.com/javascript/"
241-
},
242-
"@aws-sdk/s3-request-presigner@3.556.0": {
243-
"name": "@aws-sdk/s3-request-presigner",
244-
"version": "3.556.0",
245-
"range": "^3.556.0",
246-
"licenses": "Apache-2.0",
247-
"repoUrl": "https://github.com/aws/aws-sdk-js-v3",
248-
"versionedRepoUrl": "https://github.com/aws/aws-sdk-js-v3/tree/v3.556.0",
249-
"licenseFile": "node_modules/@aws-sdk/s3-request-presigner/LICENSE",
250-
"licenseUrl": "https://github.com/aws/aws-sdk-js-v3/blob/v3.556.0/LICENSE",
251-
"licenseTextSource": "file",
252-
"publisher": "AWS SDK for JavaScript Team",
253-
"url": "https://aws.amazon.com/javascript/"
254-
},
255229
"@koa/router@12.0.1": {
256230
"name": "@koa/router",
257231
"version": "12.0.1",
@@ -376,19 +350,6 @@
376350
"licenseTextSource": "file",
377351
"publisher": "Caolan McMahon"
378352
},
379-
"aws-sdk@2.1604.0": {
380-
"name": "aws-sdk",
381-
"version": "2.1604.0",
382-
"range": "^2.1604.0",
383-
"licenses": "Apache-2.0",
384-
"repoUrl": "https://github.com/aws/aws-sdk-js",
385-
"versionedRepoUrl": "https://github.com/aws/aws-sdk-js/tree/v2.1604.0",
386-
"licenseFile": "node_modules/aws-sdk/LICENSE.txt",
387-
"licenseUrl": "https://github.com/aws/aws-sdk-js/blob/v2.1604.0/LICENSE.txt",
388-
"licenseTextSource": "file",
389-
"publisher": "Amazon Web Services",
390-
"url": "https://aws.amazon.com/"
391-
},
392353
"c8@8.0.1": {
393354
"name": "c8",
394355
"version": "8.0.1",
@@ -480,15 +441,15 @@
480441
"publisher": "Michael Radionov",
481442
"url": "https://github.com/mradionov"
482443
},
483-
"eslint-plugin-jsdoc@48.0.6": {
444+
"eslint-plugin-jsdoc@48.2.3": {
484445
"name": "eslint-plugin-jsdoc",
485-
"version": "48.0.6",
446+
"version": "48.2.3",
486447
"range": "^48.0.5",
487448
"licenses": "BSD-3-Clause",
488449
"repoUrl": "https://github.com/gajus/eslint-plugin-jsdoc",
489-
"versionedRepoUrl": "https://github.com/gajus/eslint-plugin-jsdoc/tree/v48.0.6",
450+
"versionedRepoUrl": "https://github.com/gajus/eslint-plugin-jsdoc/tree/v48.2.3",
490451
"licenseFile": "node_modules/eslint-plugin-jsdoc/LICENSE",
491-
"licenseUrl": "https://github.com/gajus/eslint-plugin-jsdoc/blob/v48.0.6/LICENSE",
452+
"licenseUrl": "https://github.com/gajus/eslint-plugin-jsdoc/blob/v48.2.3/LICENSE",
492453
"licenseTextSource": "file",
493454
"publisher": "Gajus Kuizinas",
494455
"email": "gajus@gajus.com",
@@ -505,15 +466,15 @@
505466
"licenseUrl": "https://github.com/SonarSource/eslint-plugin-sonarjs/blob/v0.18.0/LICENSE",
506467
"licenseTextSource": "file"
507468
},
508-
"eslint@8.56.0": {
469+
"eslint@8.57.0": {
509470
"name": "eslint",
510-
"version": "8.56.0",
471+
"version": "8.57.0",
511472
"range": "^8.24.0",
512473
"licenses": "MIT",
513474
"repoUrl": "https://github.com/eslint/eslint",
514-
"versionedRepoUrl": "https://github.com/eslint/eslint/tree/v8.56.0",
475+
"versionedRepoUrl": "https://github.com/eslint/eslint/tree/v8.57.0",
515476
"licenseFile": "node_modules/eslint/LICENSE",
516-
"licenseUrl": "https://github.com/eslint/eslint/blob/v8.56.0/LICENSE",
477+
"licenseUrl": "https://github.com/eslint/eslint/blob/v8.57.0/LICENSE",
517478
"licenseTextSource": "file",
518479
"publisher": "Nicholas C. Zakas",
519480
"email": "nicholas+npm@nczconsulting.com"
@@ -644,15 +605,15 @@
644605
"publisher": "Andrey Okonetchnikov",
645606
"email": "andrey@okonet.ru"
646607
},
647-
"lockfile-lint@4.12.1": {
608+
"lockfile-lint@4.13.2": {
648609
"name": "lockfile-lint",
649-
"version": "4.12.1",
610+
"version": "4.13.2",
650611
"range": "^4.9.6",
651612
"licenses": "Apache-2.0",
652613
"repoUrl": "https://github.com/lirantal/lockfile-lint",
653-
"versionedRepoUrl": "https://github.com/lirantal/lockfile-lint/tree/v4.12.1",
614+
"versionedRepoUrl": "https://github.com/lirantal/lockfile-lint/tree/v4.13.2",
654615
"licenseFile": "node_modules/lockfile-lint/LICENSE",
655-
"licenseUrl": "https://github.com/lirantal/lockfile-lint/blob/v4.12.1/LICENSE",
616+
"licenseUrl": "https://github.com/lirantal/lockfile-lint/blob/v4.13.2/LICENSE",
656617
"licenseTextSource": "file",
657618
"publisher": "Liran Tal",
658619
"email": "liran.tal@gmail.com",

0 commit comments

Comments
 (0)
Please sign in to comment.