depstein 0.1.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/README.md +438 -0
- package/dist/core/cache.d.ts +15 -0
- package/dist/core/cache.js +75 -0
- package/dist/core/cache.js.map +1 -0
- package/dist/core/fetcher.d.ts +7 -0
- package/dist/core/fetcher.js +59 -0
- package/dist/core/fetcher.js.map +1 -0
- package/dist/core/replacements.d.ts +32 -0
- package/dist/core/replacements.js +419 -0
- package/dist/core/replacements.js.map +1 -0
- package/dist/core/scanner.d.ts +7 -0
- package/dist/core/scanner.js +298 -0
- package/dist/core/scanner.js.map +1 -0
- package/dist/core/scorer.d.ts +5 -0
- package/dist/core/scorer.js +289 -0
- package/dist/core/scorer.js.map +1 -0
- package/dist/core/types.d.ts +99 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/vulnerabilities.d.ts +5 -0
- package/dist/core/vulnerabilities.js +93 -0
- package/dist/core/vulnerabilities.js.map +1 -0
- package/dist/ecosystems/cargo.d.ts +2 -0
- package/dist/ecosystems/cargo.js +74 -0
- package/dist/ecosystems/cargo.js.map +1 -0
- package/dist/ecosystems/golang.d.ts +2 -0
- package/dist/ecosystems/golang.js +90 -0
- package/dist/ecosystems/golang.js.map +1 -0
- package/dist/ecosystems/npm.d.ts +2 -0
- package/dist/ecosystems/npm.js +92 -0
- package/dist/ecosystems/npm.js.map +1 -0
- package/dist/ecosystems/pypi.d.ts +2 -0
- package/dist/ecosystems/pypi.js +80 -0
- package/dist/ecosystems/pypi.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +303 -0
- package/dist/index.js.map +1 -0
- package/dist/tui/colors.d.ts +17 -0
- package/dist/tui/colors.js +86 -0
- package/dist/tui/colors.js.map +1 -0
- package/dist/tui/dashboard.d.ts +7 -0
- package/dist/tui/dashboard.js +236 -0
- package/dist/tui/dashboard.js.map +1 -0
- package/dist/tui/detail.d.ts +6 -0
- package/dist/tui/detail.js +68 -0
- package/dist/tui/detail.js.map +1 -0
- package/dist/tui/picker.d.ts +7 -0
- package/dist/tui/picker.js +62 -0
- package/dist/tui/picker.js.map +1 -0
- package/dist/tui/summary.d.ts +6 -0
- package/dist/tui/summary.js +13 -0
- package/dist/tui/summary.js.map +1 -0
- package/dist/tui/table.d.ts +9 -0
- package/dist/tui/table.js +50 -0
- package/dist/tui/table.js.map +1 -0
- package/dist/utils/format.d.ts +9 -0
- package/dist/utils/format.js +72 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/http.d.ts +2 -0
- package/dist/utils/http.js +53 -0
- package/dist/utils/http.js.map +1 -0
- package/dist/utils/spinner.d.ts +11 -0
- package/dist/utils/spinner.js +40 -0
- package/dist/utils/spinner.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
export type Ecosystem = 'npm' | 'pypi' | 'cargo' | 'golang';
|
|
2
|
+
export type Grade = 'A' | 'B' | 'C' | 'D' | 'F';
|
|
3
|
+
export type FilterMode = 'all' | 'critical' | 'poor' | 'dev' | 'prod';
|
|
4
|
+
export type SortMode = 'score' | 'name' | 'size' | 'updated';
|
|
5
|
+
export type SeverityLevel = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
|
|
6
|
+
export interface RawDependency {
|
|
7
|
+
name: string;
|
|
8
|
+
version: string;
|
|
9
|
+
ecosystem: Ecosystem;
|
|
10
|
+
isDev: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface Vulnerability {
|
|
13
|
+
id: string;
|
|
14
|
+
summary: string;
|
|
15
|
+
severity: SeverityLevel;
|
|
16
|
+
aliases: string[];
|
|
17
|
+
}
|
|
18
|
+
export interface PackageMetadata {
|
|
19
|
+
name: string;
|
|
20
|
+
ecosystem: Ecosystem;
|
|
21
|
+
latestVersion: string;
|
|
22
|
+
requestedVersion: string;
|
|
23
|
+
publishedAt: Date | null;
|
|
24
|
+
description: string;
|
|
25
|
+
license: string;
|
|
26
|
+
repositoryUrl: string | null;
|
|
27
|
+
weeklyDownloads: number | null;
|
|
28
|
+
monthlyDownloads: number | null;
|
|
29
|
+
totalDownloads: number | null;
|
|
30
|
+
unpackedSize: number | null;
|
|
31
|
+
gzipSize: number | null;
|
|
32
|
+
minifiedSize: number | null;
|
|
33
|
+
hasBuiltinTypes: boolean;
|
|
34
|
+
hasTypesPackage: boolean;
|
|
35
|
+
vulnerabilities: Vulnerability[];
|
|
36
|
+
fromCache: boolean;
|
|
37
|
+
fetchError: string | null;
|
|
38
|
+
}
|
|
39
|
+
export interface ScoreDimension {
|
|
40
|
+
name: string;
|
|
41
|
+
score: number;
|
|
42
|
+
maxScore: number;
|
|
43
|
+
reason: string;
|
|
44
|
+
}
|
|
45
|
+
export interface ScoredDependency {
|
|
46
|
+
raw: RawDependency;
|
|
47
|
+
metadata: PackageMetadata;
|
|
48
|
+
dimensions: {
|
|
49
|
+
maintenance: ScoreDimension;
|
|
50
|
+
popularity: ScoreDimension;
|
|
51
|
+
bundleSize: ScoreDimension;
|
|
52
|
+
typescript: ScoreDimension;
|
|
53
|
+
license: ScoreDimension;
|
|
54
|
+
vulnerability: ScoreDimension;
|
|
55
|
+
};
|
|
56
|
+
totalScore: number;
|
|
57
|
+
grade: Grade;
|
|
58
|
+
issues: string[];
|
|
59
|
+
suggestion: string | null;
|
|
60
|
+
}
|
|
61
|
+
export interface ScanResult {
|
|
62
|
+
projectPath: string;
|
|
63
|
+
ecosystem: Ecosystem;
|
|
64
|
+
dependencies: RawDependency[];
|
|
65
|
+
}
|
|
66
|
+
export interface AuditResult {
|
|
67
|
+
projectPath: string;
|
|
68
|
+
ecosystem: Ecosystem;
|
|
69
|
+
scoredDependencies: ScoredDependency[];
|
|
70
|
+
averageScore: number;
|
|
71
|
+
grade: Grade;
|
|
72
|
+
totalCount: number;
|
|
73
|
+
criticalCount: number;
|
|
74
|
+
poorCount: number;
|
|
75
|
+
fairCount: number;
|
|
76
|
+
goodCount: number;
|
|
77
|
+
excellentCount: number;
|
|
78
|
+
timestamp: string;
|
|
79
|
+
}
|
|
80
|
+
export interface CLIOptions {
|
|
81
|
+
path: string;
|
|
82
|
+
ecosystem?: Ecosystem;
|
|
83
|
+
filter: FilterMode;
|
|
84
|
+
sort: SortMode;
|
|
85
|
+
noCache: boolean;
|
|
86
|
+
json: boolean;
|
|
87
|
+
top?: number;
|
|
88
|
+
fix: boolean;
|
|
89
|
+
minScore?: number;
|
|
90
|
+
ci: boolean;
|
|
91
|
+
}
|
|
92
|
+
export type CachedMetadata = Omit<PackageMetadata, 'fromCache'> & {
|
|
93
|
+
publishedAt: string | null;
|
|
94
|
+
};
|
|
95
|
+
export interface CacheEntry {
|
|
96
|
+
data: CachedMetadata;
|
|
97
|
+
timestamp: number;
|
|
98
|
+
}
|
|
99
|
+
export type CacheStore = Record<string, CacheEntry>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Vulnerability, Ecosystem } from './types.js';
|
|
2
|
+
export declare function fetchVulnerabilities(name: string, ecosystem: Ecosystem,
|
|
3
|
+
/** Resolved/installed version — enables version-aware filtering so only
|
|
4
|
+
* vulns that affect THIS version are returned (not all historical CVEs). */
|
|
5
|
+
version?: string | null): Promise<Vulnerability[]>;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { fetchJson } from '../utils/http.js';
|
|
2
|
+
/**
|
|
3
|
+
* Ecosystem name mappings for OSV API
|
|
4
|
+
* https://osv.dev/docs/#section/Background
|
|
5
|
+
*/
|
|
6
|
+
const OSV_ECOSYSTEM = {
|
|
7
|
+
npm: 'npm',
|
|
8
|
+
pypi: 'PyPI',
|
|
9
|
+
cargo: 'crates.io',
|
|
10
|
+
golang: 'Go',
|
|
11
|
+
};
|
|
12
|
+
/** Map a CVSS numeric base score (0–10) to our severity tiers. */
|
|
13
|
+
function cvssScoreToSeverity(score) {
|
|
14
|
+
if (score >= 9.0)
|
|
15
|
+
return 'CRITICAL';
|
|
16
|
+
if (score >= 7.0)
|
|
17
|
+
return 'HIGH';
|
|
18
|
+
if (score >= 4.0)
|
|
19
|
+
return 'MEDIUM';
|
|
20
|
+
return 'LOW';
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Resolve severity using a three-tier strategy:
|
|
24
|
+
* 1. Explicit string from database_specific.severity (GitHub Advisory / NVD)
|
|
25
|
+
* 2. Numeric CVSS score from database_specific.cvss.score
|
|
26
|
+
* 3. ecosystem_specific.severity string
|
|
27
|
+
* 4. Fall back to LOW (conservative unknown)
|
|
28
|
+
*/
|
|
29
|
+
function resolveSeverity(v) {
|
|
30
|
+
// Tier 1: string severity from authoritative database fields
|
|
31
|
+
for (const raw of [v.database_specific?.severity, v.ecosystem_specific?.severity]) {
|
|
32
|
+
if (!raw)
|
|
33
|
+
continue;
|
|
34
|
+
const s = raw.toUpperCase();
|
|
35
|
+
if (s === 'CRITICAL')
|
|
36
|
+
return 'CRITICAL';
|
|
37
|
+
if (s === 'HIGH')
|
|
38
|
+
return 'HIGH';
|
|
39
|
+
if (s === 'MEDIUM' || s === 'MODERATE')
|
|
40
|
+
return 'MEDIUM';
|
|
41
|
+
if (s === 'LOW')
|
|
42
|
+
return 'LOW';
|
|
43
|
+
}
|
|
44
|
+
// Tier 2: numeric CVSS score
|
|
45
|
+
const cvssScore = v.database_specific?.cvss?.score;
|
|
46
|
+
if (cvssScore != null && cvssScore > 0) {
|
|
47
|
+
return cvssScoreToSeverity(cvssScore);
|
|
48
|
+
}
|
|
49
|
+
return 'LOW';
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Returns true if the version string looks like a concrete resolved version
|
|
53
|
+
* (i.e. not a semver range like ^1.0, ~1.0, >=1.0, etc.)
|
|
54
|
+
*/
|
|
55
|
+
function isConcreteVersion(version) {
|
|
56
|
+
return /^\d+\.\d+/.test(version);
|
|
57
|
+
}
|
|
58
|
+
export async function fetchVulnerabilities(name, ecosystem,
|
|
59
|
+
/** Resolved/installed version — enables version-aware filtering so only
|
|
60
|
+
* vulns that affect THIS version are returned (not all historical CVEs). */
|
|
61
|
+
version = null) {
|
|
62
|
+
try {
|
|
63
|
+
const pkgQuery = {
|
|
64
|
+
name,
|
|
65
|
+
ecosystem: OSV_ECOSYSTEM[ecosystem],
|
|
66
|
+
};
|
|
67
|
+
const body = { package: pkgQuery };
|
|
68
|
+
// Pass the concrete installed version so OSV returns only CVEs that
|
|
69
|
+
// affect the version the user actually has installed. Range selectors
|
|
70
|
+
// (^, ~, >) are not valid OSV version strings — skip them.
|
|
71
|
+
if (version && isConcreteVersion(version)) {
|
|
72
|
+
body.version = version;
|
|
73
|
+
}
|
|
74
|
+
const res = (await fetchJson('https://api.osv.dev/v1/query', {
|
|
75
|
+
method: 'POST',
|
|
76
|
+
headers: { 'Content-Type': 'application/json' },
|
|
77
|
+
body: JSON.stringify(body),
|
|
78
|
+
}));
|
|
79
|
+
if (!res.vulns || !Array.isArray(res.vulns))
|
|
80
|
+
return [];
|
|
81
|
+
return res.vulns.slice(0, 20).map(v => ({
|
|
82
|
+
id: v.id,
|
|
83
|
+
summary: v.summary ?? 'No description available',
|
|
84
|
+
severity: resolveSeverity(v),
|
|
85
|
+
aliases: v.aliases ?? [],
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// Never let vulnerability check crash the whole audit
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=vulnerabilities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vulnerabilities.js","sourceRoot":"","sources":["../../src/core/vulnerabilities.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { fetchJson } from '../utils/http.js';
|
|
2
|
+
import { cache } from '../core/cache.js';
|
|
3
|
+
import { fetchVulnerabilities } from '../core/vulnerabilities.js';
|
|
4
|
+
export async function fetchCargo(name, requestedVersion, noCache) {
|
|
5
|
+
if (!noCache) {
|
|
6
|
+
const cached = cache.get('cargo', name);
|
|
7
|
+
if (cached)
|
|
8
|
+
return { ...cached, fromCache: true };
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
const data = (await fetchJson(`https://crates.io/api/v1/crates/${encodeURIComponent(name)}`, {
|
|
12
|
+
headers: {
|
|
13
|
+
// crates.io requires a descriptive User-Agent
|
|
14
|
+
'User-Agent': 'depstein/0.1.0 (https://github.com/kstij/DepStein)',
|
|
15
|
+
},
|
|
16
|
+
}));
|
|
17
|
+
if (!data.crate) {
|
|
18
|
+
throw new Error(`Crate "${name}" not found on crates.io`);
|
|
19
|
+
}
|
|
20
|
+
const krate = data.crate;
|
|
21
|
+
const latestVersion = krate.newest_version;
|
|
22
|
+
// Extract license from the matching version entry
|
|
23
|
+
const versionEntry = data.versions?.find(v => v.num === latestVersion);
|
|
24
|
+
const license = versionEntry?.license ?? 'Unknown';
|
|
25
|
+
const updatedAt = versionEntry?.updated_at ?? krate.updated_at;
|
|
26
|
+
const vulnerabilities = await fetchVulnerabilities(name, 'cargo', latestVersion);
|
|
27
|
+
const metadata = {
|
|
28
|
+
name,
|
|
29
|
+
ecosystem: 'cargo',
|
|
30
|
+
latestVersion,
|
|
31
|
+
requestedVersion,
|
|
32
|
+
publishedAt: updatedAt ? new Date(updatedAt) : null,
|
|
33
|
+
description: krate.description ?? '',
|
|
34
|
+
license,
|
|
35
|
+
repositoryUrl: krate.repository ?? krate.homepage ?? krate.documentation ?? null,
|
|
36
|
+
weeklyDownloads: null,
|
|
37
|
+
monthlyDownloads: null,
|
|
38
|
+
totalDownloads: krate.downloads,
|
|
39
|
+
unpackedSize: null,
|
|
40
|
+
gzipSize: null,
|
|
41
|
+
minifiedSize: null,
|
|
42
|
+
hasBuiltinTypes: false,
|
|
43
|
+
hasTypesPackage: false,
|
|
44
|
+
vulnerabilities,
|
|
45
|
+
fetchError: null,
|
|
46
|
+
};
|
|
47
|
+
cache.set('cargo', name, metadata);
|
|
48
|
+
return { ...metadata, fromCache: false };
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
return {
|
|
52
|
+
name,
|
|
53
|
+
ecosystem: 'cargo',
|
|
54
|
+
latestVersion: requestedVersion,
|
|
55
|
+
requestedVersion,
|
|
56
|
+
publishedAt: null,
|
|
57
|
+
description: '',
|
|
58
|
+
license: 'Unknown',
|
|
59
|
+
repositoryUrl: null,
|
|
60
|
+
weeklyDownloads: null,
|
|
61
|
+
monthlyDownloads: null,
|
|
62
|
+
totalDownloads: null,
|
|
63
|
+
unpackedSize: null,
|
|
64
|
+
gzipSize: null,
|
|
65
|
+
minifiedSize: null,
|
|
66
|
+
hasBuiltinTypes: false,
|
|
67
|
+
hasTypesPackage: false,
|
|
68
|
+
vulnerabilities: [],
|
|
69
|
+
fromCache: false,
|
|
70
|
+
fetchError: err.message,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=cargo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cargo.js","sourceRoot":"","sources":["../../src/ecosystems/cargo.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { fetchJson } from '../utils/http.js';
|
|
2
|
+
import { cache } from '../core/cache.js';
|
|
3
|
+
import { fetchVulnerabilities } from '../core/vulnerabilities.js';
|
|
4
|
+
function extractGithubPath(module) {
|
|
5
|
+
const m = module.match(/^github\.com\/([^/]+\/[^/]+)/);
|
|
6
|
+
return m ? m[1] : null;
|
|
7
|
+
}
|
|
8
|
+
export async function fetchGolang(name, requestedVersion, noCache) {
|
|
9
|
+
if (!noCache) {
|
|
10
|
+
const cached = cache.get('golang', name);
|
|
11
|
+
if (cached)
|
|
12
|
+
return { ...cached, fromCache: true };
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
// URL-encode the module path for the proxy: '@' and '/' need special handling
|
|
16
|
+
const encodedModule = name
|
|
17
|
+
.split('/')
|
|
18
|
+
.map(s => encodeURIComponent(s))
|
|
19
|
+
.join('/');
|
|
20
|
+
// Fetch latest version info from Go module proxy
|
|
21
|
+
const proxyResult = await fetchJson(`https://proxy.golang.org/${encodedModule}/@latest`).catch(() => null);
|
|
22
|
+
const specificResult = requestedVersion && requestedVersion !== 'latest'
|
|
23
|
+
? await fetchJson(`https://proxy.golang.org/${encodedModule}/@v/${encodeURIComponent(requestedVersion)}.info`).catch(() => null)
|
|
24
|
+
: null;
|
|
25
|
+
const latestVersion = proxyResult?.Version ?? specificResult?.version ?? requestedVersion;
|
|
26
|
+
const rawTime = proxyResult?.Time ?? specificResult?.time ?? null;
|
|
27
|
+
const publishedAt = rawTime ? new Date(rawTime) : null;
|
|
28
|
+
// Try GitHub API for richer metadata
|
|
29
|
+
let description = '';
|
|
30
|
+
let license = 'Unknown';
|
|
31
|
+
let repositoryUrl = null;
|
|
32
|
+
const githubPath = extractGithubPath(name);
|
|
33
|
+
if (githubPath) {
|
|
34
|
+
repositoryUrl = `https://github.com/${githubPath}`;
|
|
35
|
+
const ghResult = await fetchJson(`https://api.github.com/repos/${githubPath}`).catch(() => null);
|
|
36
|
+
if (ghResult) {
|
|
37
|
+
description = ghResult.description ?? '';
|
|
38
|
+
license = ghResult.license?.spdx_id ?? ghResult.license?.name ?? 'Unknown';
|
|
39
|
+
repositoryUrl = ghResult.html_url ?? repositoryUrl;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const vulnerabilities = await fetchVulnerabilities(name, 'golang', latestVersion);
|
|
43
|
+
const metadata = {
|
|
44
|
+
name,
|
|
45
|
+
ecosystem: 'golang',
|
|
46
|
+
latestVersion,
|
|
47
|
+
requestedVersion,
|
|
48
|
+
publishedAt,
|
|
49
|
+
description,
|
|
50
|
+
license,
|
|
51
|
+
repositoryUrl,
|
|
52
|
+
weeklyDownloads: null,
|
|
53
|
+
monthlyDownloads: null,
|
|
54
|
+
totalDownloads: null,
|
|
55
|
+
unpackedSize: null,
|
|
56
|
+
gzipSize: null,
|
|
57
|
+
minifiedSize: null,
|
|
58
|
+
hasBuiltinTypes: false,
|
|
59
|
+
hasTypesPackage: false,
|
|
60
|
+
vulnerabilities,
|
|
61
|
+
fetchError: null,
|
|
62
|
+
};
|
|
63
|
+
cache.set('golang', name, metadata);
|
|
64
|
+
return { ...metadata, fromCache: false };
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
return {
|
|
68
|
+
name,
|
|
69
|
+
ecosystem: 'golang',
|
|
70
|
+
latestVersion: requestedVersion,
|
|
71
|
+
requestedVersion,
|
|
72
|
+
publishedAt: null,
|
|
73
|
+
description: '',
|
|
74
|
+
license: 'Unknown',
|
|
75
|
+
repositoryUrl: null,
|
|
76
|
+
weeklyDownloads: null,
|
|
77
|
+
monthlyDownloads: null,
|
|
78
|
+
totalDownloads: null,
|
|
79
|
+
unpackedSize: null,
|
|
80
|
+
gzipSize: null,
|
|
81
|
+
minifiedSize: null,
|
|
82
|
+
hasBuiltinTypes: false,
|
|
83
|
+
hasTypesPackage: false,
|
|
84
|
+
vulnerabilities: [],
|
|
85
|
+
fromCache: false,
|
|
86
|
+
fetchError: err.message,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=golang.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"golang.js","sourceRoot":"","sources":["../../src/ecosystems/golang.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { fetchJson } from '../utils/http.js';
|
|
2
|
+
import { cache } from '../core/cache.js';
|
|
3
|
+
import { fetchVulnerabilities } from '../core/vulnerabilities.js';
|
|
4
|
+
import { fetchHead } from '../utils/http.js';
|
|
5
|
+
export async function fetchNpm(name, requestedVersion, noCache) {
|
|
6
|
+
if (!noCache) {
|
|
7
|
+
const cached = cache.get('npm', name);
|
|
8
|
+
if (cached)
|
|
9
|
+
return { ...cached, fromCache: true };
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
const encodedName = encodeURIComponent(name).replace('%40', '@');
|
|
13
|
+
const [registryResult, downloadsResult] = await Promise.allSettled([
|
|
14
|
+
fetchJson(`https://registry.npmjs.org/${encodedName}`),
|
|
15
|
+
fetchJson(`https://api.npmjs.org/downloads/point/last-week/${encodedName}`),
|
|
16
|
+
]);
|
|
17
|
+
if (registryResult.status === 'rejected') {
|
|
18
|
+
throw new Error(String(registryResult.reason));
|
|
19
|
+
}
|
|
20
|
+
const registry = registryResult.value;
|
|
21
|
+
const downloads = downloadsResult.status === 'fulfilled' ? downloadsResult.value : null;
|
|
22
|
+
const latestVersion = registry['dist-tags']?.latest ?? requestedVersion;
|
|
23
|
+
const versionData = registry.versions?.[latestVersion] ?? {};
|
|
24
|
+
const publishedAt = registry.time?.[latestVersion] ? new Date(registry.time[latestVersion]) : null;
|
|
25
|
+
const hasBuiltinTypes = !!(versionData.types || versionData.typings);
|
|
26
|
+
// Check for @types/{name} on npm (strip scope prefix for scoped packages)
|
|
27
|
+
const unscoped = name.startsWith('@') ? name.split('/')[1] : name;
|
|
28
|
+
const hasTypesPackage = hasBuiltinTypes
|
|
29
|
+
? false
|
|
30
|
+
: await fetchHead(`https://registry.npmjs.org/@types%2F${encodeURIComponent(unscoped ?? name)}`);
|
|
31
|
+
const rawRepo = versionData.repository?.url ?? registry.repository?.url ?? null;
|
|
32
|
+
const repositoryUrl = rawRepo
|
|
33
|
+
? rawRepo.replace(/^git\+/, '').replace(/\.git$/, '')
|
|
34
|
+
: null;
|
|
35
|
+
// Fetch real bundle sizes from bundlephobia (minified + gzip).
|
|
36
|
+
// Bundlephobia gives the actual browser-visible weight, which is far
|
|
37
|
+
// smaller than npm's unpackedSize (total extracted file bytes).
|
|
38
|
+
const bundleResult = await fetchJson(`https://bundlephobia.com/api/size?package=${encodedName}@${latestVersion}`).catch(() => null);
|
|
39
|
+
const gzipSize = bundleResult?.gzip ?? null;
|
|
40
|
+
const minifiedSize = bundleResult?.size ?? null;
|
|
41
|
+
const vulnerabilities = await fetchVulnerabilities(name, 'npm', latestVersion);
|
|
42
|
+
const metadata = {
|
|
43
|
+
name,
|
|
44
|
+
ecosystem: 'npm',
|
|
45
|
+
latestVersion,
|
|
46
|
+
requestedVersion,
|
|
47
|
+
publishedAt,
|
|
48
|
+
description: registry.description ?? '',
|
|
49
|
+
license: versionData.license ?? registry.license ?? 'Unknown',
|
|
50
|
+
repositoryUrl,
|
|
51
|
+
weeklyDownloads: downloads?.downloads ?? null,
|
|
52
|
+
monthlyDownloads: null,
|
|
53
|
+
totalDownloads: null,
|
|
54
|
+
unpackedSize: versionData.dist?.unpackedSize ?? null,
|
|
55
|
+
gzipSize,
|
|
56
|
+
minifiedSize,
|
|
57
|
+
hasBuiltinTypes,
|
|
58
|
+
hasTypesPackage,
|
|
59
|
+
vulnerabilities,
|
|
60
|
+
fetchError: null,
|
|
61
|
+
};
|
|
62
|
+
cache.set('npm', name, metadata);
|
|
63
|
+
return { ...metadata, fromCache: false };
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
return failedMetadata(name, 'npm', requestedVersion, err.message);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function failedMetadata(name, ecosystem, requestedVersion, error) {
|
|
70
|
+
return {
|
|
71
|
+
name,
|
|
72
|
+
ecosystem,
|
|
73
|
+
latestVersion: requestedVersion,
|
|
74
|
+
requestedVersion,
|
|
75
|
+
publishedAt: null,
|
|
76
|
+
description: '',
|
|
77
|
+
license: 'Unknown',
|
|
78
|
+
repositoryUrl: null,
|
|
79
|
+
weeklyDownloads: null,
|
|
80
|
+
monthlyDownloads: null,
|
|
81
|
+
totalDownloads: null,
|
|
82
|
+
unpackedSize: null,
|
|
83
|
+
gzipSize: null,
|
|
84
|
+
minifiedSize: null,
|
|
85
|
+
hasBuiltinTypes: false,
|
|
86
|
+
hasTypesPackage: false,
|
|
87
|
+
vulnerabilities: [],
|
|
88
|
+
fromCache: false,
|
|
89
|
+
fetchError: error,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=npm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"npm.js","sourceRoot":"","sources":["../../src/ecosystems/npm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { fetchJson } from '../utils/http.js';
|
|
2
|
+
import { cache } from '../core/cache.js';
|
|
3
|
+
import { fetchVulnerabilities } from '../core/vulnerabilities.js';
|
|
4
|
+
export async function fetchPypi(name, requestedVersion, noCache) {
|
|
5
|
+
if (!noCache) {
|
|
6
|
+
const cached = cache.get('pypi', name);
|
|
7
|
+
if (cached)
|
|
8
|
+
return { ...cached, fromCache: true };
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
const [infoResult, statsResult] = await Promise.allSettled([
|
|
12
|
+
fetchJson(`https://pypi.org/pypi/${encodeURIComponent(name)}/json`),
|
|
13
|
+
fetchJson(`https://pypistats.org/api/packages/${encodeURIComponent(name.toLowerCase())}/recent`),
|
|
14
|
+
]);
|
|
15
|
+
if (infoResult.status === 'rejected') {
|
|
16
|
+
throw new Error(String(infoResult.reason));
|
|
17
|
+
}
|
|
18
|
+
const pkg = infoResult.value;
|
|
19
|
+
const stats = statsResult.status === 'fulfilled' ? statsResult.value : null;
|
|
20
|
+
const latestVersion = pkg.info.version;
|
|
21
|
+
const latestReleases = pkg.releases?.[latestVersion] ?? pkg.urls ?? [];
|
|
22
|
+
const lastRelease = latestReleases[latestReleases.length - 1];
|
|
23
|
+
const publishedAt = lastRelease?.upload_time ? new Date(lastRelease.upload_time + 'Z') : null;
|
|
24
|
+
const urls = pkg.info.project_urls ?? {};
|
|
25
|
+
const repositoryUrl = urls['Source Code'] ??
|
|
26
|
+
urls['Source'] ??
|
|
27
|
+
urls['Repository'] ??
|
|
28
|
+
urls['GitHub'] ??
|
|
29
|
+
urls['Homepage'] ??
|
|
30
|
+
pkg.info.home_page ??
|
|
31
|
+
null;
|
|
32
|
+
const vulnerabilities = await fetchVulnerabilities(name, 'pypi', latestVersion);
|
|
33
|
+
const metadata = {
|
|
34
|
+
name,
|
|
35
|
+
ecosystem: 'pypi',
|
|
36
|
+
latestVersion,
|
|
37
|
+
requestedVersion,
|
|
38
|
+
publishedAt,
|
|
39
|
+
description: pkg.info.summary ?? '',
|
|
40
|
+
license: pkg.info.license ?? 'Unknown',
|
|
41
|
+
repositoryUrl,
|
|
42
|
+
weeklyDownloads: null,
|
|
43
|
+
monthlyDownloads: stats?.data?.last_month ?? null,
|
|
44
|
+
totalDownloads: null,
|
|
45
|
+
unpackedSize: null,
|
|
46
|
+
gzipSize: null,
|
|
47
|
+
minifiedSize: null,
|
|
48
|
+
hasBuiltinTypes: false,
|
|
49
|
+
hasTypesPackage: false,
|
|
50
|
+
vulnerabilities,
|
|
51
|
+
fetchError: null,
|
|
52
|
+
};
|
|
53
|
+
cache.set('pypi', name, metadata);
|
|
54
|
+
return { ...metadata, fromCache: false };
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
return {
|
|
58
|
+
name,
|
|
59
|
+
ecosystem: 'pypi',
|
|
60
|
+
latestVersion: requestedVersion,
|
|
61
|
+
requestedVersion,
|
|
62
|
+
publishedAt: null,
|
|
63
|
+
description: '',
|
|
64
|
+
license: 'Unknown',
|
|
65
|
+
repositoryUrl: null,
|
|
66
|
+
weeklyDownloads: null,
|
|
67
|
+
monthlyDownloads: null,
|
|
68
|
+
totalDownloads: null,
|
|
69
|
+
unpackedSize: null,
|
|
70
|
+
gzipSize: null,
|
|
71
|
+
minifiedSize: null,
|
|
72
|
+
hasBuiltinTypes: false,
|
|
73
|
+
hasTypesPackage: false,
|
|
74
|
+
vulnerabilities: [],
|
|
75
|
+
fromCache: false,
|
|
76
|
+
fetchError: err.message,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=pypi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pypi.js","sourceRoot":"","sources":["../../src/ecosystems/pypi.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED