npq 3.7.0 → 3.8.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.
@@ -53,3 +53,54 @@ test('running marshall tasks fails', async () => {
53
53
 
54
54
  await expect(marshalls.tasks(config)).resolves.toMatchObject(context)
55
55
  })
56
+
57
+ test('running marshall tasks throws error when single package is not found', async () => {
58
+ const PackageRepoUtilsNotFoundMock = class Fake {
59
+ getPackageInfo() {
60
+ return Promise.resolve({ error: 'Not found' })
61
+ }
62
+ }
63
+
64
+ marshalls.collectMarshalls = jest.fn(() => {
65
+ return Promise.resolve([path.join(process.cwd(), '__tests__/__fixtures__/test.marshall.js')])
66
+ })
67
+
68
+ const config = {
69
+ pkgs: [{ packageName: 'nonexistent-package' }],
70
+ packageRepoUtils: new PackageRepoUtilsNotFoundMock()
71
+ }
72
+
73
+ await expect(marshalls.tasks(config)).resolves.toEqual({
74
+ error: true,
75
+ message: 'Package not found: nonexistent-package'
76
+ })
77
+ })
78
+
79
+ test('running marshall tasks filters out not found packages when multiple packages provided', async () => {
80
+ const PackageRepoUtilsMixedMock = class Fake {
81
+ getPackageInfo(packageName) {
82
+ if (packageName === 'nonexistent-package') {
83
+ return Promise.resolve({ error: 'Not found' })
84
+ }
85
+ return Promise.resolve(true)
86
+ }
87
+ }
88
+
89
+ marshalls.collectMarshalls = jest.fn(() => {
90
+ return Promise.resolve([path.join(process.cwd(), '__tests__/__fixtures__/test.marshall.js')])
91
+ })
92
+
93
+ const config = {
94
+ pkgs: [
95
+ { packageName: 'express' },
96
+ { packageName: 'nonexistent-package' },
97
+ { packageName: 'semver' }
98
+ ],
99
+ packageRepoUtils: new PackageRepoUtilsMixedMock()
100
+ }
101
+
102
+ const result = await marshalls.tasks(config)
103
+ // Should filter out the nonexistent package and keep only express and semver
104
+ expect(result.pkgs).toHaveLength(2)
105
+ expect(result.pkgs).toEqual([{ packageName: 'express' }, { packageName: 'semver' }])
106
+ })
@@ -160,3 +160,92 @@ test('repo utils returns valid semver for different cases of version asked', asy
160
160
  'could not find dist-tag next for package testPackage'
161
161
  )
162
162
  })
163
+
164
+ test('repo utils resolves semver ranges by finding the highest satisfying version', async () => {
165
+ const PackageRepoUtils = require('../lib/helpers/packageRepoUtils')
166
+ global.fetch = jest.fn().mockImplementation(() =>
167
+ Promise.resolve({
168
+ json: () => require('./mocks/registryPackageOk.mock.json')
169
+ })
170
+ )
171
+
172
+ const packageRepoUtils = new PackageRepoUtils()
173
+ const packageName = 'testPackage'
174
+
175
+ // Test major version range - should find highest 3.x version (3.1.0)
176
+ let result = await packageRepoUtils.getSemVer(packageName, '^3.0.0')
177
+ expect(result).toEqual('3.1.0')
178
+
179
+ // Test simple major version - should find highest 3.x version (3.1.0)
180
+ result = await packageRepoUtils.getSemVer(packageName, '3')
181
+ expect(result).toEqual('3.1.0')
182
+
183
+ // Test tilde range - should find 3.1.x version (3.1.0)
184
+ result = await packageRepoUtils.getSemVer(packageName, '~3.1.0')
185
+ expect(result).toEqual('3.1.0')
186
+
187
+ // Test invalid semver range that doesn't match any version
188
+ await expect(packageRepoUtils.getSemVer(packageName, '^10.0.0')).rejects.toThrow(
189
+ 'could not find dist-tag ^10.0.0 for package testPackage'
190
+ )
191
+
192
+ // Test invalid semver range with version 2 (no 2.x versions in mock)
193
+ await expect(packageRepoUtils.getSemVer(packageName, '2')).rejects.toThrow(
194
+ 'could not find dist-tag 2 for package testPackage'
195
+ )
196
+ })
197
+
198
+ test('repo utils resolves semver ranges with multiple versions', async () => {
199
+ const PackageRepoUtils = require('../lib/helpers/packageRepoUtils')
200
+
201
+ // Create a more comprehensive mock that includes multiple versions
202
+ const comprehensiveMock = {
203
+ name: '@astrojs/vue',
204
+ 'dist-tags': {
205
+ latest: '4.5.0'
206
+ },
207
+ versions: {
208
+ '1.2.0': { name: '@astrojs/vue', version: '1.2.0' },
209
+ '2.0.0': { name: '@astrojs/vue', version: '2.0.0' },
210
+ '2.1.0': { name: '@astrojs/vue', version: '2.1.0' },
211
+ '2.2.1': { name: '@astrojs/vue', version: '2.2.1' },
212
+ '3.0.0': { name: '@astrojs/vue', version: '3.0.0' },
213
+ '3.1.0': { name: '@astrojs/vue', version: '3.1.0' },
214
+ '3.2.2': { name: '@astrojs/vue', version: '3.2.2' },
215
+ '4.0.0': { name: '@astrojs/vue', version: '4.0.0' },
216
+ '4.5.0': { name: '@astrojs/vue', version: '4.5.0' }
217
+ }
218
+ }
219
+
220
+ global.fetch = jest.fn().mockImplementation(() =>
221
+ Promise.resolve({
222
+ json: () => comprehensiveMock
223
+ })
224
+ )
225
+
226
+ const packageRepoUtils = new PackageRepoUtils()
227
+ const packageName = '@astrojs/vue'
228
+
229
+ // Test the problematic case mentioned in the issue: "@astrojs/vue@3"
230
+ let result = await packageRepoUtils.getSemVer(packageName, '3')
231
+ expect(result).toEqual('3.2.2') // Should find highest 3.x version
232
+
233
+ // Test other major versions
234
+ result = await packageRepoUtils.getSemVer(packageName, '2')
235
+ expect(result).toEqual('2.2.1') // Should find highest 2.x version
236
+
237
+ result = await packageRepoUtils.getSemVer(packageName, '4')
238
+ expect(result).toEqual('4.5.0') // Should find highest 4.x version
239
+
240
+ // Test caret ranges
241
+ result = await packageRepoUtils.getSemVer(packageName, '^3.0.0')
242
+ expect(result).toEqual('3.2.2')
243
+
244
+ result = await packageRepoUtils.getSemVer(packageName, '^2.1.0')
245
+ expect(result).toEqual('2.2.1')
246
+
247
+ // Test that non-existent major versions still fail appropriately
248
+ await expect(packageRepoUtils.getSemVer(packageName, '10')).rejects.toThrow(
249
+ 'could not find dist-tag 10 for package @astrojs/vue'
250
+ )
251
+ })
@@ -64,13 +64,28 @@ class PackageRepoUtils {
64
64
  throw new Error(`could not find dist-tags for package ${packageName}`)
65
65
  }
66
66
 
67
- if (packageInfo['dist-tags'][packageVersion] === undefined) {
68
- throw new Error(`could not find dist-tag ${packageVersion} for package ${packageName}`)
67
+ if (packageInfo['dist-tags'][packageVersion] !== undefined) {
68
+ const semverVersion = packageInfo['dist-tags'][packageVersion]
69
+ return semverVersion
69
70
  }
70
71
 
71
- const semverVersion = packageInfo['dist-tags'][packageVersion]
72
+ // If not found in dist-tags, try to find the highest version that satisfies
73
+ // the semver range from the versions object
74
+ if (packageInfo.versions) {
75
+ const availableVersions = Object.keys(packageInfo.versions).filter((v) => semver.valid(v))
72
76
 
73
- return semverVersion
77
+ try {
78
+ const satisfyingVersion = semver.maxSatisfying(availableVersions, packageVersion)
79
+
80
+ if (satisfyingVersion) {
81
+ return satisfyingVersion
82
+ }
83
+ } catch (error) {
84
+ // semver.maxSatisfying throws if the range is invalid, continue to error below
85
+ }
86
+ }
87
+
88
+ throw new Error(`could not find dist-tag ${packageVersion} for package ${packageName}`)
74
89
  }
75
90
  }
76
91
  }
@@ -65,7 +65,26 @@ class Marshalls {
65
65
  }
66
66
 
67
67
  static tasks(options) {
68
+ // console.log(options);
69
+ // process.exit(1);
68
70
  return Marshalls.warmUpPackagesCache(options)
71
+ .then((packagesDataList) => {
72
+ // handle error in case we get just one package and it is not found
73
+ if (packagesDataList && packagesDataList.length === 1) {
74
+ if (packagesDataList[0].error && packagesDataList[0].error === 'Not found') {
75
+ throw new Error(`Package not found: ${options.pkgs[0].packageName}`)
76
+ }
77
+ }
78
+
79
+ // handle error in case we get more than one package and at least one is not found
80
+ // in which we case we simply remove the package from the `options` array
81
+ // which lists objects (each have a property of packageName)
82
+ if (packagesDataList && packagesDataList.length > 1) {
83
+ options.pkgs = options.pkgs.filter((pkg, index) => {
84
+ return !packagesDataList[index].error || packagesDataList[index].error !== 'Not found'
85
+ })
86
+ }
87
+ })
69
88
  .then(() => Marshalls.collectMarshalls())
70
89
  .then((marshalls) => {
71
90
  return Marshalls.buildMarshallTasks(marshalls, {
@@ -75,6 +94,12 @@ class Marshalls {
75
94
  .then((tasks) => {
76
95
  return Marshalls.runTasks(tasks, options)
77
96
  })
97
+ .catch((err) => {
98
+ // console.error('Error:', err.message)
99
+ // avoid implementing a custom error message for a not found package
100
+ // because the package manager will yield its own error message
101
+ return { error: true, message: err.message }
102
+ })
78
103
  }
79
104
 
80
105
  static warmUpPackagesCache(options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "npq",
3
- "version": "3.7.0",
3
+ "version": "3.8.0",
4
4
  "description": "marshall your npm/npm package installs with high quality and class 🎖",
5
5
  "bin": {
6
6
  "npq": "./bin/npq.js",
@@ -135,7 +135,7 @@
135
135
  "security/detect-no-csrf-before-method-override": "error",
136
136
  "security/detect-non-literal-regexp": "error",
137
137
  "security/detect-non-literal-require": "warn",
138
- "security/detect-object-injection": "warn",
138
+ "security/detect-object-injection": "off",
139
139
  "security/detect-possible-timing-attacks": "error",
140
140
  "security/detect-pseudoRandomBytes": "error",
141
141
  "node/no-unsupported-features/node-builtins": [