npq 3.19.6 → 3.21.0

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/lib/cli.js CHANGED
@@ -20,6 +20,12 @@ const INSTALL_SUBCOMMANDS = new Set([
20
20
  ])
21
21
 
22
22
  class CliParser {
23
+ static registryConfigArgs(values = {}) {
24
+ return ['registry', 'userconfig', 'globalconfig'].flatMap((key) =>
25
+ values[key] ? [`--${key}=${values[key]}`] : []
26
+ )
27
+ }
28
+
23
29
  static isInstallSubcommand(token) {
24
30
  return INSTALL_SUBCOMMANDS.has(token)
25
31
  }
@@ -67,6 +73,9 @@ class CliParser {
67
73
  packageManager: { type: 'string' },
68
74
  pkgMgr: { type: 'string' },
69
75
  'disable-auto-continue': { type: 'boolean' },
76
+ registry: { type: 'string' },
77
+ userconfig: { type: 'string' },
78
+ globalconfig: { type: 'string' },
70
79
  help: { type: 'boolean', short: 'h' },
71
80
  version: { type: 'boolean', short: 'v' }
72
81
  }
@@ -95,6 +104,9 @@ Options:
95
104
  --packageManager Package Manager to use (default: npm)
96
105
  --pkgMgr Alias for packageManager
97
106
  --disable-auto-continue Disable auto-continue countdown, always prompt
107
+ --registry npm registry used for audit and install
108
+ --userconfig Path to the user npm configuration file
109
+ --globalconfig Path to the global npm configuration file
98
110
  -h, --help Show help
99
111
  -v, --version Show version
100
112
 
@@ -127,6 +139,7 @@ curated by Liran Tal at https://github.com/lirantal/npq`)
127
139
  plain: values.plain || false,
128
140
  disableAutoContinue:
129
141
  values['disable-auto-continue'] || process.env.NPQ_DISABLE_AUTO_CONTINUE === 'true',
142
+ registryConfigArgs: this.registryConfigArgs(values),
130
143
  installSubcommandExplicit
131
144
  }
132
145
  }
@@ -140,17 +153,21 @@ curated by Liran Tal at https://github.com/lirantal/npq`)
140
153
  type: 'string',
141
154
  short: 'i',
142
155
  default: 'install'
143
- }
156
+ },
157
+ registry: { type: 'string' },
158
+ userconfig: { type: 'string' },
159
+ globalconfig: { type: 'string' }
144
160
  }
145
161
  }
146
162
 
147
- const { positionals } = parseArgs(config)
163
+ const { values, positionals } = parseArgs(config)
148
164
 
149
165
  const earlyExitNoInstall = true
150
166
  const normalizedPackages = this._extractPackagesFromPositionals(positionals, earlyExitNoInstall)
151
167
 
152
168
  return {
153
- packages: normalizedPackages
169
+ packages: normalizedPackages,
170
+ registryConfigArgs: this.registryConfigArgs(values)
154
171
  }
155
172
  }
156
173
  }
@@ -0,0 +1,11 @@
1
+ 'use strict'
2
+
3
+ class NotEvaluated extends Error {
4
+ constructor(message, { capability = null } = {}) {
5
+ super(message)
6
+ this.name = 'NotEvaluated'
7
+ this.capability = capability
8
+ }
9
+ }
10
+
11
+ module.exports = NotEvaluated
@@ -13,57 +13,6 @@ const MISSING_TIME_CUTOFF = '2015-01-01T00:00:00.000Z'
13
13
  * This replaces pacote functionality for npq specific needs
14
14
  */
15
15
  class NpmRegistry {
16
- constructor(opts = {}) {
17
- this.opts = opts
18
- this.registry = opts.registry || 'https://registry.npmjs.org'
19
- }
20
-
21
- /**
22
- * Fetch package manifest from npm registry
23
- * @param {string} packageSpec - Package name and version (e.g., 'express@4.18.2')
24
- * @param {Object} options - Additional options
25
- * @returns {Promise<Object>} Package manifest
26
- */
27
- async getManifest(packageSpec, options = {}) {
28
- const spec = npa(packageSpec)
29
- const escapedName = spec.escapedName
30
- const packumentUrl = `${this.registry}/${escapedName}`
31
-
32
- // Fetch packument (package metadata)
33
- const response = await fetch(packumentUrl, {
34
- headers: {
35
- accept: 'application/json',
36
- 'user-agent': 'npq-npm-registry-client',
37
- ...(options.headers || {})
38
- }
39
- })
40
-
41
- if (!response.ok) {
42
- throw new Error(`Failed to fetch package manifest: ${response.status} ${response.statusText}`)
43
- }
44
-
45
- const packument = await response.json()
46
-
47
- // Pick the specific version
48
- let version = spec.fetchSpec
49
- if (version === 'latest' || !version || version === '*') {
50
- version = packument['dist-tags']?.latest
51
- }
52
-
53
- if (!packument.versions || !packument.versions[version]) {
54
- throw new Error(`Version ${version} not found for package ${spec.name}`)
55
- }
56
-
57
- const manifest = packument.versions[version]
58
-
59
- // Add timing information if available
60
- if (packument.time && packument.time[version]) {
61
- manifest._time = packument.time[version]
62
- }
63
-
64
- return manifest
65
- }
66
-
67
16
  /**
68
17
  * Verify package signatures using npm registry public keys
69
18
  * @param {Object} manifest - Package manifest
@@ -140,27 +89,15 @@ class NpmRegistry {
140
89
  * @param {Array} registryKeys - Public keys from npm registry
141
90
  * @returns {Promise<Object>} Verified manifest with _attestations
142
91
  */
143
- async verifyAttestations(manifest, registryKeys) {
92
+ async verifyAttestations(manifest, registryKeys, attestations) {
144
93
  if (!manifest.dist || !manifest.dist.attestations) {
145
94
  throw new Error('Package has no attestations to verify')
146
95
  }
147
96
 
148
- const attestationsPath = new URL(manifest.dist.attestations.url).pathname
149
- const attestationsUrl = this.registry + attestationsPath
150
-
151
- const response = await fetch(attestationsUrl, {
152
- headers: {
153
- accept: 'application/json',
154
- 'user-agent': 'npq-npm-registry-client'
155
- }
156
- })
157
-
158
- if (!response.ok) {
159
- throw new Error(`Failed to fetch attestations: ${response.status} ${response.statusText}`)
97
+ if (!Array.isArray(attestations)) {
98
+ throw new Error('Package attestations response is invalid')
160
99
  }
161
100
 
162
- const { attestations } = await response.json()
163
-
164
101
  const bundles = attestations.map(({ predicateType, bundle }) => {
165
102
  const statement = JSON.parse(
166
103
  Buffer.from(bundle.dsseEnvelope.payload, 'base64').toString('utf8')
@@ -1,31 +1,23 @@
1
1
  'use strict'
2
2
 
3
3
  const semver = require('semver')
4
- const NPM_REGISTRY = 'http://registry.npmjs.org'
5
- const NPM_REGISTRY_API = 'https://api.npmjs.org'
4
+ const RegistryClient = require('./registryClient')
6
5
 
7
6
  class PackageRepoUtils {
8
- constructor(options = {}) {
9
- this.registryUrl = options.registryUrl ? options.registryUrl : NPM_REGISTRY
10
- this.registryApiUrl = options.registryApiUrl ? options.registryApiUrl : NPM_REGISTRY_API
7
+ constructor({ registryClient = RegistryClient.public() } = {}) {
8
+ this.registryClient = registryClient
11
9
  this.pkgInfoCache = {}
12
10
  }
13
11
 
14
- formatPackageForUrl(pkg) {
15
- return pkg.replace(/\//g, '%2F')
16
- }
17
-
18
12
  getPackageInfo(pkg) {
19
- if (this.pkgInfoCache[pkg]) {
20
- return Promise.resolve(this.pkgInfoCache[pkg])
21
- } else {
22
- return fetch(`${this.registryUrl}/${this.formatPackageForUrl(pkg)}`)
23
- .then((response) => response.json())
24
- .then((data) => {
25
- this.pkgInfoCache[pkg] = data
26
- return data
27
- })
13
+ const cacheKey = `${this.registryClient.registryFor(pkg)}|${pkg}`
14
+ if (this.pkgInfoCache[cacheKey]) {
15
+ return Promise.resolve(this.pkgInfoCache[cacheKey])
28
16
  }
17
+ return this.registryClient.getPackageInfo(pkg).then((data) => {
18
+ this.pkgInfoCache[cacheKey] = data
19
+ return data
20
+ })
29
21
  }
30
22
 
31
23
  getLatestVersion(pkg) {
@@ -35,9 +27,7 @@ class PackageRepoUtils {
35
27
  }
36
28
 
37
29
  getDownloadInfo(pkg) {
38
- return fetch(`${this.registryApiUrl}/downloads/point/last-month/${pkg}`)
39
- .then((response) => response.json())
40
- .then(({ downloads }) => downloads)
30
+ return this.registryClient.getDownloadInfo(pkg)
41
31
  }
42
32
 
43
33
  getReadmeInfo(pkg) {
@@ -0,0 +1,206 @@
1
+ 'use strict'
2
+
3
+ const npa = require('npm-package-arg')
4
+ const npmFetch = require('npm-registry-fetch')
5
+ const NotEvaluated = require('./notEvaluated')
6
+ const RegistryConfig = require('./registryConfig')
7
+ const { RegistryError } = require('./registryErrors')
8
+
9
+ const PUBLIC_REGISTRY = 'https://registry.npmjs.org/'
10
+ const OPTIONAL_STATUS = new Set([404, 405, 501])
11
+ const CAPABILITY = Object.freeze({
12
+ SIGNING_KEYS: 'signing-keys',
13
+ ATTESTATIONS: 'attestations',
14
+ DOWNLOAD_COUNTS: 'download-counts'
15
+ })
16
+ const CAPABILITY_MESSAGE = Object.freeze({
17
+ [CAPABILITY.SIGNING_KEYS]: 'configured registry does not expose signing keys',
18
+ [CAPABILITY.ATTESTATIONS]: 'configured registry does not expose attestations',
19
+ [CAPABILITY.DOWNLOAD_COUNTS]:
20
+ 'download counts are available only for the public npm registry'
21
+ })
22
+
23
+ class RegistryClient {
24
+ constructor(registryConfig, { fetcher = npmFetch } = {}) {
25
+ this.registryConfig = registryConfig
26
+ this.fetcher = fetcher
27
+ this.keyCache = new Map()
28
+ this.capabilityCache = new Map()
29
+ }
30
+
31
+ static public() {
32
+ return new RegistryClient(RegistryConfig.defaults())
33
+ }
34
+
35
+ registryFor(packageSpec) {
36
+ return this.registryConfig.registryFor(packageSpec)
37
+ }
38
+
39
+ requestOptions(packageSpec) {
40
+ return {
41
+ ...this.registryConfig.requestOptions,
42
+ spec: packageSpec,
43
+ headers: {
44
+ accept: 'application/json',
45
+ 'user-agent': 'npq-npm-registry-client'
46
+ }
47
+ }
48
+ }
49
+
50
+ async requestJson(
51
+ requestPath,
52
+ packageSpec,
53
+ { capability = null, notFoundAsData = false } = {}
54
+ ) {
55
+ const registry = this.registryFor(packageSpec)
56
+ try {
57
+ return await this.fetcher.json(requestPath, this.requestOptions(packageSpec))
58
+ } catch (error) {
59
+ const statusCode = error.statusCode || error.status || null
60
+ if (notFoundAsData && statusCode === 404) {
61
+ return { error: 'Not found' }
62
+ }
63
+ if (capability && OPTIONAL_STATUS.has(statusCode)) {
64
+ this.capabilityCache.set(`${registry}|${capability}`, false)
65
+ throw new NotEvaluated(CAPABILITY_MESSAGE[capability], { capability })
66
+ }
67
+ const code =
68
+ statusCode === 401 || statusCode === 403
69
+ ? 'EREGISTRYAUTH'
70
+ : statusCode
71
+ ? 'EREGISTRYHTTP'
72
+ : 'EREGISTRYNETWORK'
73
+ throw new RegistryError(
74
+ statusCode === 401 || statusCode === 403
75
+ ? 'Registry authentication or authorization failed'
76
+ : statusCode
77
+ ? `Registry request failed with HTTP ${statusCode}`
78
+ : 'Registry network request failed',
79
+ { registry, code, statusCode, cause: error }
80
+ )
81
+ }
82
+ }
83
+
84
+ async getPackageInfo(packageName) {
85
+ const spec = npa(packageName)
86
+ const data = await this.requestJson(spec.escapedName, packageName, {
87
+ notFoundAsData: true
88
+ })
89
+ if (data && data.error === 'Not found') {
90
+ return data
91
+ }
92
+ if (!data || typeof data !== 'object' || Array.isArray(data)) {
93
+ throw new RegistryError('Registry package metadata is malformed', {
94
+ registry: this.registryFor(packageName),
95
+ code: 'EREGISTRYPROTOCOL'
96
+ })
97
+ }
98
+ return data
99
+ }
100
+
101
+ async getManifest(packageSpec, packument = null) {
102
+ const spec = npa(packageSpec)
103
+ const data = packument || (await this.getPackageInfo(spec.name))
104
+ let version = spec.fetchSpec
105
+ if (!version || version === '*' || version === 'latest') {
106
+ version = data['dist-tags'] && data['dist-tags'].latest
107
+ }
108
+ if (!data.versions || !data.versions[version]) {
109
+ throw new Error(`Version ${version} not found for package ${spec.name}`)
110
+ }
111
+ return {
112
+ ...data.versions[version],
113
+ ...(data.time && data.time[version] ? { _time: data.time[version] } : {})
114
+ }
115
+ }
116
+
117
+ unavailable(capability) {
118
+ throw new NotEvaluated(CAPABILITY_MESSAGE[capability], { capability })
119
+ }
120
+
121
+ async getRegistryKeys(packageSpec) {
122
+ const registry = this.registryFor(packageSpec)
123
+ const cacheKey = `${registry}|${CAPABILITY.SIGNING_KEYS}`
124
+ if (this.keyCache.has(cacheKey)) {
125
+ return this.keyCache.get(cacheKey)
126
+ }
127
+ if (this.capabilityCache.get(cacheKey) === false) {
128
+ return this.unavailable(CAPABILITY.SIGNING_KEYS)
129
+ }
130
+ const response = await this.requestJson('-/npm/v1/keys', packageSpec, {
131
+ capability: CAPABILITY.SIGNING_KEYS
132
+ })
133
+ if (!response || !Array.isArray(response.keys)) {
134
+ throw new RegistryError('Registry signing-key response is malformed', {
135
+ registry,
136
+ code: 'EREGISTRYPROTOCOL'
137
+ })
138
+ }
139
+ if (response.keys.length === 0) {
140
+ this.capabilityCache.set(cacheKey, false)
141
+ return this.unavailable(CAPABILITY.SIGNING_KEYS)
142
+ }
143
+ const keys = response.keys.map((key) => ({
144
+ ...key,
145
+ pemkey: `-----BEGIN PUBLIC KEY-----\n${key.key}\n-----END PUBLIC KEY-----`
146
+ }))
147
+ this.keyCache.set(cacheKey, keys)
148
+ return keys
149
+ }
150
+
151
+ async getAttestations(packageSpec, manifest) {
152
+ const registry = this.registryFor(packageSpec)
153
+ const cacheKey = `${registry}|${CAPABILITY.ATTESTATIONS}`
154
+ if (this.capabilityCache.get(cacheKey) === false) {
155
+ return this.unavailable(CAPABILITY.ATTESTATIONS)
156
+ }
157
+ let requestPath
158
+ try {
159
+ const advertisedPath = new URL(manifest.dist.attestations.url).pathname
160
+ const registryPath = new URL(registry).pathname
161
+ requestPath = advertisedPath.startsWith(registryPath)
162
+ ? advertisedPath.slice(registryPath.length)
163
+ : advertisedPath.replace(/^\/+/, '')
164
+ } catch (error) {
165
+ throw new RegistryError('Package attestation URL is malformed', {
166
+ registry,
167
+ code: 'EREGISTRYPROTOCOL',
168
+ cause: error
169
+ })
170
+ }
171
+ const response = await this.requestJson(requestPath, packageSpec, {
172
+ capability: CAPABILITY.ATTESTATIONS
173
+ })
174
+ if (!response || !Array.isArray(response.attestations)) {
175
+ throw new RegistryError('Registry attestation response is malformed', {
176
+ registry,
177
+ code: 'EREGISTRYPROTOCOL'
178
+ })
179
+ }
180
+ if (response.attestations.length === 0) {
181
+ this.capabilityCache.set(cacheKey, false)
182
+ return this.unavailable(CAPABILITY.ATTESTATIONS)
183
+ }
184
+ return response.attestations
185
+ }
186
+
187
+ async getDownloadInfo(packageName) {
188
+ if (this.registryFor(packageName) !== PUBLIC_REGISTRY) {
189
+ return this.unavailable(CAPABILITY.DOWNLOAD_COUNTS)
190
+ }
191
+ const escapedName = npa(packageName).escapedName
192
+ const response = await this.requestJson(
193
+ `https://api.npmjs.org/downloads/point/last-month/${escapedName}`,
194
+ packageName
195
+ )
196
+ if (!response || typeof response.downloads !== 'number') {
197
+ throw new RegistryError('Registry download response is malformed', {
198
+ registry: PUBLIC_REGISTRY,
199
+ code: 'EREGISTRYPROTOCOL'
200
+ })
201
+ }
202
+ return response.downloads
203
+ }
204
+ }
205
+
206
+ module.exports = RegistryClient
@@ -0,0 +1,79 @@
1
+ 'use strict'
2
+
3
+ const path = require('node:path')
4
+ const Config = require('@npmcli/config')
5
+ const npmFetch = require('npm-registry-fetch')
6
+ const {
7
+ definitions,
8
+ flatten,
9
+ nerfDarts,
10
+ shorthands
11
+ } = require('@npmcli/config/lib/definitions')
12
+ const { RegistryError, sanitizeRegistryUrl } = require('./registryErrors')
13
+
14
+ const DEFAULT_REGISTRY = 'https://registry.npmjs.org/'
15
+
16
+ function normalizeRegistry(value) {
17
+ const url = new URL(value || DEFAULT_REGISTRY)
18
+ if (!url.pathname.endsWith('/')) {
19
+ url.pathname += '/'
20
+ }
21
+ return url.toString()
22
+ }
23
+
24
+ class RegistryConfig {
25
+ constructor(requestOptions) {
26
+ this.requestOptions = Object.freeze({
27
+ ...requestOptions,
28
+ registry: normalizeRegistry(requestOptions.registry)
29
+ })
30
+ }
31
+
32
+ static defaults() {
33
+ return new RegistryConfig({ registry: DEFAULT_REGISTRY })
34
+ }
35
+
36
+ static async load({ argv = [], env = process.env, cwd = process.cwd() } = {}) {
37
+ try {
38
+ const registryArg = argv.find((arg) => arg.startsWith('--registry='))
39
+ if (registryArg) {
40
+ normalizeRegistry(registryArg.slice('--registry='.length))
41
+ }
42
+
43
+ const config = new Config({
44
+ npmPath: path.resolve(__dirname, '..'),
45
+ definitions,
46
+ shorthands,
47
+ flatten,
48
+ nerfDarts,
49
+ argv: ['node', 'npq', ...argv],
50
+ env,
51
+ cwd,
52
+ warn: false
53
+ })
54
+ await config.load()
55
+ if (!config.validate()) {
56
+ throw new Error('Invalid npm configuration')
57
+ }
58
+ return new RegistryConfig(config.flat)
59
+ } catch (error) {
60
+ const code =
61
+ error.code === 'ERR_INVALID_AUTH' ? 'EREGISTRYCONFIGAUTH' : 'EREGISTRYCONFIG'
62
+ const message =
63
+ error.code === 'ERR_INVALID_AUTH'
64
+ ? 'Invalid npm registry authentication configuration'
65
+ : `Unable to load npm registry configuration: ${error.code || error.name}`
66
+ throw new RegistryError(message, { code, cause: error })
67
+ }
68
+ }
69
+
70
+ registryFor(packageSpec) {
71
+ return normalizeRegistry(npmFetch.pickRegistry(packageSpec, this.requestOptions))
72
+ }
73
+
74
+ describeRegistry(packageSpec) {
75
+ return sanitizeRegistryUrl(this.registryFor(packageSpec))
76
+ }
77
+ }
78
+
79
+ module.exports = RegistryConfig
@@ -0,0 +1,32 @@
1
+ 'use strict'
2
+
3
+ function sanitizeRegistryUrl(value) {
4
+ try {
5
+ const url = new URL(value)
6
+ url.username = ''
7
+ url.password = ''
8
+ url.search = ''
9
+ url.hash = ''
10
+ return url.toString()
11
+ } catch {
12
+ return 'configured registry'
13
+ }
14
+ }
15
+
16
+ class RegistryError extends Error {
17
+ constructor(
18
+ message,
19
+ { registry = null, code = 'EREGISTRY', statusCode = null, cause = null } = {}
20
+ ) {
21
+ const sanitizedRegistry = registry ? sanitizeRegistryUrl(registry) : null
22
+ super(sanitizedRegistry ? `${message} (${sanitizedRegistry})` : message, {
23
+ cause: cause || undefined
24
+ })
25
+ this.name = 'RegistryError'
26
+ this.code = code
27
+ this.statusCode = statusCode
28
+ this.registry = sanitizedRegistry
29
+ }
30
+ }
31
+
32
+ module.exports = { RegistryError, sanitizeRegistryUrl }
@@ -108,6 +108,7 @@ function reportResults(marshallResults, { plain = false } = {}) {
108
108
 
109
109
  let countErrors = 0
110
110
  let countWarnings = 0
111
+ let countNotEvaluated = 0
111
112
  let countPackages = results.length
112
113
 
113
114
  results.forEach((result, index) => {
@@ -219,11 +220,33 @@ function reportResults(marshallResults, { plain = false } = {}) {
219
220
  })
220
221
  }
221
222
 
223
+ if (result.notEvaluated && result.notEvaluated.length > 0) {
224
+ result.notEvaluated.forEach((entryArray) => {
225
+ countNotEvaluated += entryArray.length
226
+ entryArray.forEach((entry) => {
227
+ const topicRich = styleText(
228
+ ['cyan'],
229
+ entry.categoryFriendlyName.padEnd(maxTextLength, ' ')
230
+ )
231
+ const iconRich = styleText(['cyan'], '○')
232
+ const wrappedMessage = wrapTextWithIndent(
233
+ entry.message,
234
+ maxMessageWidth,
235
+ indentString
236
+ )
237
+ resultsForPrettyPrint +=
238
+ `\n ${prefixRich} ${iconRich} ${topicRich} ${separatorRich} ${wrappedMessage} `
239
+ resultsForPlainTextPrint +=
240
+ `\n NOT EVALUATED: ${entry.categoryFriendlyName} - ${entry.message}`
241
+ })
242
+ })
243
+ }
244
+
222
245
  // Close rich formatting box
223
246
  resultsForPrettyPrint += `\n ${styleText(['dim'], '└─')}`
224
247
  })
225
248
 
226
- const maxTextLength = 'Total packages:'.length
249
+ const maxTextLength = 'Total not evaluated:'.length
227
250
 
228
251
  // Rich formatting summary
229
252
  summaryForPrettyPrint += `\n\n${styleText(['bold'], 'Summary:')}\n`
@@ -239,6 +262,10 @@ function reportResults(marshallResults, { plain = false } = {}) {
239
262
  ['yellow', 'bold'],
240
263
  String(countWarnings)
241
264
  )}`
265
+ summaryForPrettyPrint += `\n - ${'Total not evaluated:'.padEnd(maxTextLength, ' ')} ${styleText(
266
+ ['cyan'],
267
+ String(countNotEvaluated)
268
+ )}`
242
269
  summaryForPrettyPrint += `\n`
243
270
 
244
271
  // Plain text summary
@@ -246,11 +273,13 @@ function reportResults(marshallResults, { plain = false } = {}) {
246
273
  summaryForPlainTextPrint += `\n - Total packages: ${countPackages}`
247
274
  summaryForPlainTextPrint += `\n - Total errors: ${countErrors}`
248
275
  summaryForPlainTextPrint += `\n - Total warnings: ${countWarnings}`
276
+ summaryForPlainTextPrint += `\n - Total not evaluated: ${countNotEvaluated}`
249
277
  summaryForPlainTextPrint += `\n`
250
278
 
251
279
  return {
252
280
  countErrors,
253
281
  countWarnings,
282
+ countNotEvaluated,
254
283
  resultsForPrettyPrint,
255
284
  resultsForJSON,
256
285
  resultsForPlainTextPrint,
@@ -267,7 +296,8 @@ function marshallResultsToIssuesPerPackage(results) {
267
296
  const issues = {
268
297
  pkg,
269
298
  errors: [],
270
- warnings: []
299
+ warnings: [],
300
+ notEvaluated: []
271
301
  }
272
302
  for (const marshall of allMarshallResultsList) {
273
303
  for (const value of Object.values(marshall)) {
@@ -294,9 +324,22 @@ function marshallResultsToIssuesPerPackage(results) {
294
324
 
295
325
  issues.warnings.push(marshallWarnings)
296
326
  }
327
+ if (value.notEvaluated && value.notEvaluated.length > 0) {
328
+ const skippedChecks = value.notEvaluated.map((entry) => ({
329
+ marshall: value.marshall,
330
+ categoryId: value.categoryId,
331
+ categoryFriendlyName: marshallCategories[value.categoryId].title,
332
+ ...entry
333
+ }))
334
+ issues.notEvaluated.push(skippedChecks)
335
+ }
297
336
  }
298
337
  }
299
- if (issues.errors.length > 0 || issues.warnings.length > 0) {
338
+ if (
339
+ issues.errors.length > 0 ||
340
+ issues.warnings.length > 0 ||
341
+ issues.notEvaluated.length > 0
342
+ ) {
300
343
  issuesPerPackage.push(issues)
301
344
  }
302
345
  }
package/lib/marshall.js CHANGED
@@ -3,12 +3,20 @@
3
3
  // const util = require('node:util')
4
4
  const Marshalls = require('./marshalls')
5
5
  const PackageRepoUtils = require('./helpers/packageRepoUtils')
6
+ const RegistryClient = require('./helpers/registryClient')
6
7
  // const { marshallCategories } = require('./marshalls/constants')
7
8
 
8
9
  class Marshall {
9
10
  constructor(options = {}) {
10
- this.pkgs = options ? options.pkgs : null
11
- this.packageRepoUtils = new PackageRepoUtils()
11
+ options = options || {}
12
+ this.pkgs = options.pkgs || null
13
+ this.registryClient =
14
+ options.registryClient ||
15
+ options.packageRepoUtils?.registryClient ||
16
+ RegistryClient.public()
17
+ this.packageRepoUtils =
18
+ options.packageRepoUtils ||
19
+ new PackageRepoUtils({ registryClient: this.registryClient })
12
20
  this.progressManager = options.progressManager || null
13
21
  this.promiseThrottleHelper = options.promiseThrottleHelper || null
14
22
  }
@@ -56,7 +64,8 @@ class Marshall {
56
64
  const allPackages = Array.isArray(pkg) ? pkg : [pkg]
57
65
  const config = {
58
66
  pkgs: this.createPackageVersionMaps(allPackages),
59
- packageRepoUtils: packageRepoUtils
67
+ packageRepoUtils: packageRepoUtils,
68
+ registryClient: this.registryClient
60
69
  }
61
70
  return Marshalls.tasks(config, this.progressManager)
62
71
  }