npq 3.16.0 → 3.16.2

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.
@@ -0,0 +1,78 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * Calculates the Levenshtein distance between two strings.
5
+ * Uses the Wagner-Fischer algorithm with single-row space optimization
6
+ * and optional early termination when distance exceeds maxDistance.
7
+ *
8
+ * @param {string} a - First string
9
+ * @param {string} b - Second string
10
+ * @param {number} [maxDistance] - Optional maximum distance threshold for early termination
11
+ * @returns {number} The Levenshtein edit distance between the two strings
12
+ */
13
+ function levenshteinDistance(a, b, maxDistance) {
14
+ // Handle edge cases
15
+ if (a === b) return 0
16
+ if (a.length === 0) return b.length
17
+ if (b.length === 0) return a.length
18
+
19
+ // Ensure a is the shorter string for space optimization
20
+ if (a.length > b.length) {
21
+ const temp = a
22
+ a = b
23
+ b = temp
24
+ }
25
+
26
+ const aLen = a.length
27
+ const bLen = b.length
28
+
29
+ // Early termination: if length difference exceeds maxDistance, no need to compute
30
+ if (maxDistance !== undefined && bLen - aLen >= maxDistance) {
31
+ return bLen - aLen
32
+ }
33
+
34
+ // Initialize the previous row (represents distances for empty string a prefix)
35
+ let prevRow = new Array(aLen + 1)
36
+ for (let i = 0; i <= aLen; i++) {
37
+ prevRow[i] = i
38
+ }
39
+
40
+ // Current row for computation
41
+ let currRow = new Array(aLen + 1)
42
+
43
+ // Fill in the matrix row by row
44
+ for (let j = 1; j <= bLen; j++) {
45
+ currRow[0] = j
46
+
47
+ let rowMin = currRow[0]
48
+
49
+ for (let i = 1; i <= aLen; i++) {
50
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1
51
+
52
+ currRow[i] = Math.min(
53
+ prevRow[i] + 1, // deletion
54
+ currRow[i - 1] + 1, // insertion
55
+ prevRow[i - 1] + cost // substitution
56
+ )
57
+
58
+ if (currRow[i] < rowMin) {
59
+ rowMin = currRow[i]
60
+ }
61
+ }
62
+
63
+ // Early termination: if minimum value in current row exceeds maxDistance,
64
+ // the final distance will also exceed maxDistance
65
+ if (maxDistance !== undefined && rowMin >= maxDistance) {
66
+ return rowMin
67
+ }
68
+
69
+ // Swap rows
70
+ const temp = prevRow
71
+ prevRow = currRow
72
+ currRow = temp
73
+ }
74
+
75
+ return prevRow[aLen]
76
+ }
77
+
78
+ module.exports = { levenshteinDistance }
@@ -12,9 +12,20 @@ async function getProjectPackages() {
12
12
  const packageJSON = await fs.readFile(packageJSONPath, 'utf-8')
13
13
  const packageData = JSON.parse(packageJSON)
14
14
  if (packageData && packageData.dependencies) {
15
- packages = Object.keys(packageData.dependencies).map((dep) => {
16
- return `${dep}@${packageData.dependencies[dep]}`
17
- })
15
+ packages = [
16
+ ...packages,
17
+ ...Object.keys(packageData.dependencies).map((dep) => {
18
+ return `${dep}@${packageData.dependencies[dep]}`
19
+ })
20
+ ]
21
+ }
22
+ if (packageData && packageData.devDependencies) {
23
+ packages = [
24
+ ...packages,
25
+ ...Object.keys(packageData.devDependencies).map((dep) => {
26
+ return `${dep}@${packageData.devDependencies[dep]}`
27
+ })
28
+ ]
18
29
  }
19
30
 
20
31
  return packages
@@ -4,7 +4,7 @@ const BaseMarshall = require('./baseMarshall')
4
4
  const { marshallCategories } = require('./constants')
5
5
 
6
6
  const path = require('path')
7
- const { distance } = require('fastest-levenshtein')
7
+ const { levenshteinDistance } = require('../helpers/levenshteinDistance')
8
8
  const topPackagesRawJSON = require(path.join(__dirname, '../../data/top-packages.json'))
9
9
 
10
10
  const MARSHALL_NAME = 'typosquatting'
@@ -21,7 +21,7 @@ class Marshall extends BaseMarshall {
21
21
  }
22
22
 
23
23
  validate(pkg) {
24
- let levenshteinDistance = null
24
+ let editDistance = null
25
25
  let similarPackages = []
26
26
  return new Promise((resolve, reject) => {
27
27
  // If package is within an allow-list
@@ -36,9 +36,9 @@ class Marshall extends BaseMarshall {
36
36
  return resolve([])
37
37
  }
38
38
 
39
- levenshteinDistance = distance(pkg.packageName, popularPackageNameInRepository)
39
+ editDistance = levenshteinDistance(pkg.packageName, popularPackageNameInRepository)
40
40
 
41
- if (levenshteinDistance > 0 && levenshteinDistance < 3) {
41
+ if (editDistance > 0 && editDistance < 3) {
42
42
  similarPackages.push(popularPackageNameInRepository)
43
43
  }
44
44
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "npq",
3
- "version": "3.16.0",
3
+ "version": "3.16.2",
4
4
  "description": "marshall your npm/npm package installs with high quality and class 🎖",
5
5
  "bin": {
6
6
  "npq": "./bin/npq.js",
@@ -43,10 +43,9 @@
43
43
  "access": "public"
44
44
  },
45
45
  "dependencies": {
46
- "fastest-levenshtein": "^1.0.16",
47
46
  "npm-package-arg": "^13.0.2",
48
47
  "semver": "^7.7.3",
49
- "sigstore": "^3.1.0",
48
+ "sigstore": "^4.0.0",
50
49
  "ssri": "^12.0.0"
51
50
  },
52
51
  "devDependencies": {