dep-oracle 1.2.0 → 1.3.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 +33 -8
- package/dist/action/index.js +398 -16
- package/dist/badge-5Z3WAD2B.js +89 -0
- package/dist/badge-5Z3WAD2B.js.map +1 -0
- package/dist/chunk-32B3QIPY.js +1505 -0
- package/dist/chunk-32B3QIPY.js.map +1 -0
- package/dist/chunk-7DST6SNA.js +258 -0
- package/dist/chunk-7DST6SNA.js.map +1 -0
- package/dist/{chunk-TXSNFX3N.js → chunk-DLWG22RC.js} +403 -17
- package/dist/chunk-DLWG22RC.js.map +1 -0
- package/dist/chunk-HX6MGNBD.js +271 -0
- package/dist/chunk-HX6MGNBD.js.map +1 -0
- package/dist/chunk-IVXGOPRU.js +145 -0
- package/dist/chunk-IVXGOPRU.js.map +1 -0
- package/dist/chunk-SP3VYPXX.js +218 -0
- package/dist/chunk-SP3VYPXX.js.map +1 -0
- package/dist/chunk-T5EVLWZM.js +4234 -0
- package/dist/chunk-T5EVLWZM.js.map +1 -0
- package/dist/chunk-UMB5MJHL.js +239 -0
- package/dist/chunk-UMB5MJHL.js.map +1 -0
- package/dist/cli/index.js +163 -6499
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +9 -84
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +33 -12
- package/dist/mcp/server.js.map +1 -1
- package/dist/npm-UB54H37N.js +9 -0
- package/dist/npm-UB54H37N.js.map +1 -0
- package/dist/orchestrator-VOOYKDPT.js +8 -0
- package/dist/orchestrator-VOOYKDPT.js.map +1 -0
- package/dist/python-U4G2GK4J.js +9 -0
- package/dist/python-U4G2GK4J.js.map +1 -0
- package/dist/server-WONIBSG4.js +640 -0
- package/dist/server-WONIBSG4.js.map +1 -0
- package/dist/store-Z5UANEBB.js +8 -0
- package/dist/store-Z5UANEBB.js.map +1 -0
- package/dist/trust-score-YXYDFVPZ.js +8 -0
- package/dist/trust-score-YXYDFVPZ.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
- package/dist/chunk-TXSNFX3N.js.map +0 -1
|
@@ -940,6 +940,87 @@ function createLogger(label) {
|
|
|
940
940
|
};
|
|
941
941
|
}
|
|
942
942
|
|
|
943
|
+
// src/utils/rate-limiter.ts
|
|
944
|
+
var RateLimiter = class {
|
|
945
|
+
tokens;
|
|
946
|
+
maxTokens;
|
|
947
|
+
refillIntervalMs;
|
|
948
|
+
lastRefill;
|
|
949
|
+
waitQueue = [];
|
|
950
|
+
/**
|
|
951
|
+
* @param maxRequests Maximum number of requests allowed in the window
|
|
952
|
+
* @param windowMs Window duration in milliseconds
|
|
953
|
+
*/
|
|
954
|
+
constructor(maxRequests, windowMs) {
|
|
955
|
+
this.maxTokens = maxRequests;
|
|
956
|
+
this.tokens = maxRequests;
|
|
957
|
+
this.refillIntervalMs = windowMs;
|
|
958
|
+
this.lastRefill = Date.now();
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* Acquire a token. Resolves immediately when tokens are available,
|
|
962
|
+
* otherwise waits until the bucket is refilled.
|
|
963
|
+
*/
|
|
964
|
+
async acquire() {
|
|
965
|
+
this.refill();
|
|
966
|
+
if (this.tokens > 0) {
|
|
967
|
+
this.tokens--;
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
return new Promise((resolve) => {
|
|
971
|
+
this.waitQueue.push(resolve);
|
|
972
|
+
this.scheduleRefill();
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Return the number of tokens currently available (without waiting).
|
|
977
|
+
*/
|
|
978
|
+
get remaining() {
|
|
979
|
+
this.refill();
|
|
980
|
+
return this.tokens;
|
|
981
|
+
}
|
|
982
|
+
/**
|
|
983
|
+
* Return the number of milliseconds until the next refill.
|
|
984
|
+
*/
|
|
985
|
+
get msUntilRefill() {
|
|
986
|
+
const elapsed = Date.now() - this.lastRefill;
|
|
987
|
+
return Math.max(0, this.refillIntervalMs - elapsed);
|
|
988
|
+
}
|
|
989
|
+
// -----------------------------------------------------------------------
|
|
990
|
+
// Internal
|
|
991
|
+
// -----------------------------------------------------------------------
|
|
992
|
+
refill() {
|
|
993
|
+
const now = Date.now();
|
|
994
|
+
const elapsed = now - this.lastRefill;
|
|
995
|
+
if (elapsed >= this.refillIntervalMs) {
|
|
996
|
+
const periods = Math.floor(elapsed / this.refillIntervalMs);
|
|
997
|
+
this.tokens = Math.min(this.maxTokens, this.tokens + periods * this.maxTokens);
|
|
998
|
+
this.lastRefill = now - elapsed % this.refillIntervalMs;
|
|
999
|
+
this.drainWaitQueue();
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
scheduleRefill() {
|
|
1003
|
+
const delay = this.msUntilRefill;
|
|
1004
|
+
if (delay <= 0) {
|
|
1005
|
+
this.refill();
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
setTimeout(() => {
|
|
1009
|
+
this.refill();
|
|
1010
|
+
}, delay);
|
|
1011
|
+
}
|
|
1012
|
+
drainWaitQueue() {
|
|
1013
|
+
while (this.waitQueue.length > 0 && this.tokens > 0) {
|
|
1014
|
+
this.tokens--;
|
|
1015
|
+
const resolve = this.waitQueue.shift();
|
|
1016
|
+
resolve?.();
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
};
|
|
1020
|
+
var githubRateLimiter = new RateLimiter(5e3, 36e5);
|
|
1021
|
+
var npmRateLimiter = new RateLimiter(300, 6e4);
|
|
1022
|
+
var pypiRateLimiter = new RateLimiter(100, 6e4);
|
|
1023
|
+
|
|
943
1024
|
// src/collectors/orchestrator.ts
|
|
944
1025
|
import pLimit from "p-limit";
|
|
945
1026
|
|
|
@@ -1055,6 +1136,7 @@ var RegistryCollector = class extends BaseCollector {
|
|
|
1055
1136
|
async fetchPackument(packageName) {
|
|
1056
1137
|
const url = `https://registry.npmjs.org/${encodeURIComponent(packageName)}`;
|
|
1057
1138
|
logger.debug(`Fetching packument: ${url}`);
|
|
1139
|
+
await npmRateLimiter.acquire();
|
|
1058
1140
|
const res = await fetch(url, {
|
|
1059
1141
|
headers: { Accept: "application/json" }
|
|
1060
1142
|
});
|
|
@@ -1067,6 +1149,7 @@ var RegistryCollector = class extends BaseCollector {
|
|
|
1067
1149
|
const url = `https://api.npmjs.org/downloads/point/last-week/${encodeURIComponent(packageName)}`;
|
|
1068
1150
|
logger.debug(`Fetching weekly downloads: ${url}`);
|
|
1069
1151
|
try {
|
|
1152
|
+
await npmRateLimiter.acquire();
|
|
1070
1153
|
const res = await fetch(url, {
|
|
1071
1154
|
headers: { Accept: "application/json" }
|
|
1072
1155
|
});
|
|
@@ -1094,6 +1177,166 @@ var RegistryCollector = class extends BaseCollector {
|
|
|
1094
1177
|
}
|
|
1095
1178
|
};
|
|
1096
1179
|
|
|
1180
|
+
// src/collectors/pypi-registry.ts
|
|
1181
|
+
var PyPIRegistryCollector = class extends BaseCollector {
|
|
1182
|
+
name = "pypi-registry";
|
|
1183
|
+
constructor(cache) {
|
|
1184
|
+
super(cache);
|
|
1185
|
+
}
|
|
1186
|
+
async collect(packageName, version) {
|
|
1187
|
+
const cached = await this.getCached(packageName, version);
|
|
1188
|
+
if (cached) return cached;
|
|
1189
|
+
try {
|
|
1190
|
+
const [metadataResult, downloadsResult] = await Promise.allSettled([
|
|
1191
|
+
this.fetchMetadata(packageName),
|
|
1192
|
+
this.fetchWeeklyDownloads(packageName)
|
|
1193
|
+
]);
|
|
1194
|
+
const metadata = metadataResult.status === "fulfilled" ? metadataResult.value : null;
|
|
1195
|
+
const downloads = downloadsResult.status === "fulfilled" ? downloadsResult.value : 0;
|
|
1196
|
+
if (!metadata) {
|
|
1197
|
+
throw new Error(`PyPI registry returned no data for ${packageName}`);
|
|
1198
|
+
}
|
|
1199
|
+
const versionCount = metadata.releases ? Object.keys(metadata.releases).length : 0;
|
|
1200
|
+
const lastPublishDate = this.findLastPublishDate(metadata.releases);
|
|
1201
|
+
const deprecated = this.checkYanked(metadata.releases, version);
|
|
1202
|
+
const data = {
|
|
1203
|
+
packageName,
|
|
1204
|
+
version: version === "latest" ? metadata.info.version : version,
|
|
1205
|
+
description: metadata.info.summary ?? null,
|
|
1206
|
+
lastPublishDate,
|
|
1207
|
+
versionCount,
|
|
1208
|
+
deprecated,
|
|
1209
|
+
weeklyDownloads: downloads,
|
|
1210
|
+
license: metadata.info.license ?? null,
|
|
1211
|
+
repositoryUrl: this.extractRepoUrl(metadata.info)
|
|
1212
|
+
};
|
|
1213
|
+
await this.setCache(packageName, version, data);
|
|
1214
|
+
return {
|
|
1215
|
+
status: "success",
|
|
1216
|
+
data,
|
|
1217
|
+
collectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1218
|
+
};
|
|
1219
|
+
} catch (err) {
|
|
1220
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1221
|
+
logger.error(
|
|
1222
|
+
`PyPIRegistryCollector failed for ${packageName}@${version}: ${message}`
|
|
1223
|
+
);
|
|
1224
|
+
return {
|
|
1225
|
+
status: "error",
|
|
1226
|
+
data: null,
|
|
1227
|
+
error: message,
|
|
1228
|
+
collectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
// ---------------------------------------------------------------------------
|
|
1233
|
+
// Private helpers
|
|
1234
|
+
// ---------------------------------------------------------------------------
|
|
1235
|
+
async fetchMetadata(packageName) {
|
|
1236
|
+
await pypiRateLimiter.acquire();
|
|
1237
|
+
const url = `https://pypi.org/pypi/${encodeURIComponent(packageName)}/json`;
|
|
1238
|
+
logger.debug(`Fetching PyPI metadata: ${url}`);
|
|
1239
|
+
const res = await fetch(url, {
|
|
1240
|
+
headers: { Accept: "application/json" }
|
|
1241
|
+
});
|
|
1242
|
+
if (!res.ok) {
|
|
1243
|
+
if (res.status === 404) return null;
|
|
1244
|
+
throw new Error(`PyPI registry returned ${res.status} for ${packageName}`);
|
|
1245
|
+
}
|
|
1246
|
+
return await res.json();
|
|
1247
|
+
}
|
|
1248
|
+
async fetchWeeklyDownloads(packageName) {
|
|
1249
|
+
await pypiRateLimiter.acquire();
|
|
1250
|
+
const url = `https://pypistats.org/api/packages/${encodeURIComponent(packageName)}/recent`;
|
|
1251
|
+
logger.debug(`Fetching PyPI weekly downloads: ${url}`);
|
|
1252
|
+
try {
|
|
1253
|
+
const res = await fetch(url, {
|
|
1254
|
+
headers: { Accept: "application/json" }
|
|
1255
|
+
});
|
|
1256
|
+
if (!res.ok) {
|
|
1257
|
+
logger.warn(
|
|
1258
|
+
`PyPI stats API returned ${res.status} for ${packageName}`
|
|
1259
|
+
);
|
|
1260
|
+
return 0;
|
|
1261
|
+
}
|
|
1262
|
+
const body = await res.json();
|
|
1263
|
+
return body.data?.last_week ?? 0;
|
|
1264
|
+
} catch {
|
|
1265
|
+
logger.warn(`Could not fetch PyPI download stats for ${packageName}`);
|
|
1266
|
+
return 0;
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
/**
|
|
1270
|
+
* Find the most recent upload date across all releases.
|
|
1271
|
+
*/
|
|
1272
|
+
findLastPublishDate(releases) {
|
|
1273
|
+
if (!releases) return null;
|
|
1274
|
+
const dates = [];
|
|
1275
|
+
for (const files of Object.values(releases)) {
|
|
1276
|
+
for (const file of files) {
|
|
1277
|
+
const dateStr = file.upload_time_iso_8601 ?? file.upload_time;
|
|
1278
|
+
if (dateStr) {
|
|
1279
|
+
const ts = new Date(dateStr).getTime();
|
|
1280
|
+
if (!isNaN(ts)) dates.push(ts);
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
if (dates.length === 0) return null;
|
|
1285
|
+
dates.sort((a, b) => b - a);
|
|
1286
|
+
return new Date(dates[0]).toISOString();
|
|
1287
|
+
}
|
|
1288
|
+
/**
|
|
1289
|
+
* Check if the requested version is yanked (PyPI's deprecation mechanism).
|
|
1290
|
+
* Returns the yank reason string, or null if not yanked.
|
|
1291
|
+
*/
|
|
1292
|
+
checkYanked(releases, version) {
|
|
1293
|
+
if (!releases || version === "latest") return null;
|
|
1294
|
+
const files = releases[version];
|
|
1295
|
+
if (!files || files.length === 0) return null;
|
|
1296
|
+
const yankedFile = files.find((f) => f.yanked);
|
|
1297
|
+
if (yankedFile) {
|
|
1298
|
+
return yankedFile.yanked_reason || "This version has been yanked";
|
|
1299
|
+
}
|
|
1300
|
+
return null;
|
|
1301
|
+
}
|
|
1302
|
+
/**
|
|
1303
|
+
* Extract a normalised repository URL from PyPI project_urls or home_page.
|
|
1304
|
+
*/
|
|
1305
|
+
extractRepoUrl(info) {
|
|
1306
|
+
const projectUrls = info.project_urls ?? {};
|
|
1307
|
+
const repoKeys = [
|
|
1308
|
+
"Source",
|
|
1309
|
+
"Source Code",
|
|
1310
|
+
"Repository",
|
|
1311
|
+
"GitHub",
|
|
1312
|
+
"Code",
|
|
1313
|
+
"Homepage",
|
|
1314
|
+
"source",
|
|
1315
|
+
"source_code",
|
|
1316
|
+
"repository",
|
|
1317
|
+
"github",
|
|
1318
|
+
"code",
|
|
1319
|
+
"homepage"
|
|
1320
|
+
];
|
|
1321
|
+
for (const key of repoKeys) {
|
|
1322
|
+
const url = projectUrls[key];
|
|
1323
|
+
if (url && (url.includes("github.com") || url.includes("gitlab.com") || url.includes("bitbucket.org"))) {
|
|
1324
|
+
return url.replace(/\.git$/, "");
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
for (const url of Object.values(projectUrls)) {
|
|
1328
|
+
if (url && (url.includes("github.com") || url.includes("gitlab.com") || url.includes("bitbucket.org"))) {
|
|
1329
|
+
return url.replace(/\.git$/, "");
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
const homePage = info.home_page;
|
|
1333
|
+
if (homePage && (homePage.includes("github.com") || homePage.includes("gitlab.com"))) {
|
|
1334
|
+
return homePage.replace(/\.git$/, "");
|
|
1335
|
+
}
|
|
1336
|
+
return null;
|
|
1337
|
+
}
|
|
1338
|
+
};
|
|
1339
|
+
|
|
1097
1340
|
// src/collectors/github.ts
|
|
1098
1341
|
var GitHubCollector = class extends BaseCollector {
|
|
1099
1342
|
name = "github";
|
|
@@ -1165,6 +1408,7 @@ var GitHubCollector = class extends BaseCollector {
|
|
|
1165
1408
|
async resolveRepoSlug(packageName) {
|
|
1166
1409
|
try {
|
|
1167
1410
|
const url = `https://registry.npmjs.org/${encodeURIComponent(packageName)}`;
|
|
1411
|
+
await npmRateLimiter.acquire();
|
|
1168
1412
|
const res = await fetch(url, {
|
|
1169
1413
|
headers: { Accept: "application/json" }
|
|
1170
1414
|
});
|
|
@@ -1207,6 +1451,7 @@ var GitHubCollector = class extends BaseCollector {
|
|
|
1207
1451
|
async fetchRepoInfo(owner, repo) {
|
|
1208
1452
|
const url = `https://api.github.com/repos/${owner}/${repo}`;
|
|
1209
1453
|
logger.debug(`GitHub: fetching repo info ${url}`);
|
|
1454
|
+
await githubRateLimiter.acquire();
|
|
1210
1455
|
const res = await fetch(url, { headers: this.headers() });
|
|
1211
1456
|
if (!res.ok) {
|
|
1212
1457
|
throw new Error(`GitHub API ${res.status} for ${url}`);
|
|
@@ -1221,6 +1466,7 @@ var GitHubCollector = class extends BaseCollector {
|
|
|
1221
1466
|
const url = `https://api.github.com/repos/${owner}/${repo}/contributors?per_page=1&anon=true`;
|
|
1222
1467
|
logger.debug(`GitHub: fetching contributor count ${url}`);
|
|
1223
1468
|
try {
|
|
1469
|
+
await githubRateLimiter.acquire();
|
|
1224
1470
|
const res = await fetch(url, { headers: this.headers() });
|
|
1225
1471
|
if (!res.ok) return 0;
|
|
1226
1472
|
const count = this.extractLastPage(res.headers.get("link"));
|
|
@@ -1241,6 +1487,7 @@ var GitHubCollector = class extends BaseCollector {
|
|
|
1241
1487
|
const url = `https://api.github.com/repos/${owner}/${repo}/commits?since=${since}&per_page=1`;
|
|
1242
1488
|
logger.debug(`GitHub: fetching recent commit count ${url}`);
|
|
1243
1489
|
try {
|
|
1490
|
+
await githubRateLimiter.acquire();
|
|
1244
1491
|
const res = await fetch(url, { headers: this.headers() });
|
|
1245
1492
|
if (!res.ok) return 0;
|
|
1246
1493
|
const count = this.extractLastPage(res.headers.get("link"));
|
|
@@ -1256,6 +1503,7 @@ var GitHubCollector = class extends BaseCollector {
|
|
|
1256
1503
|
const url = `https://api.github.com/repos/${owner}/${repo}/commits?per_page=1`;
|
|
1257
1504
|
logger.debug(`GitHub: fetching latest commit ${url}`);
|
|
1258
1505
|
try {
|
|
1506
|
+
await githubRateLimiter.acquire();
|
|
1259
1507
|
const res = await fetch(url, { headers: this.headers() });
|
|
1260
1508
|
if (!res.ok) return null;
|
|
1261
1509
|
const body = await res.json();
|
|
@@ -1272,6 +1520,7 @@ var GitHubCollector = class extends BaseCollector {
|
|
|
1272
1520
|
const url = `https://api.github.com/repos/${owner}/${repo}/contents/.github/FUNDING.yml`;
|
|
1273
1521
|
logger.debug(`GitHub: checking FUNDING.yml ${url}`);
|
|
1274
1522
|
try {
|
|
1523
|
+
await githubRateLimiter.acquire();
|
|
1275
1524
|
const res = await fetch(url, { headers: this.headers() });
|
|
1276
1525
|
return res.ok;
|
|
1277
1526
|
} catch {
|
|
@@ -1303,11 +1552,11 @@ var SecurityCollector = class extends BaseCollector {
|
|
|
1303
1552
|
constructor(cache) {
|
|
1304
1553
|
super(cache);
|
|
1305
1554
|
}
|
|
1306
|
-
async collect(packageName, version) {
|
|
1555
|
+
async collect(packageName, version, ecosystem) {
|
|
1307
1556
|
const cached = await this.getCached(packageName, version);
|
|
1308
1557
|
if (cached) return cached;
|
|
1309
1558
|
try {
|
|
1310
|
-
const vulns = await this.queryOsv(packageName);
|
|
1559
|
+
const vulns = await this.queryOsv(packageName, ecosystem);
|
|
1311
1560
|
const totalVulnerabilities = vulns.length;
|
|
1312
1561
|
const severityCounts = {
|
|
1313
1562
|
critical: 0,
|
|
@@ -1357,16 +1606,16 @@ var SecurityCollector = class extends BaseCollector {
|
|
|
1357
1606
|
// ---------------------------------------------------------------------------
|
|
1358
1607
|
// OSV API
|
|
1359
1608
|
// ---------------------------------------------------------------------------
|
|
1360
|
-
async queryOsv(packageName) {
|
|
1609
|
+
async queryOsv(packageName, ecosystem = "npm") {
|
|
1361
1610
|
const url = "https://api.osv.dev/v1/query";
|
|
1362
|
-
logger.debug(`OSV: querying vulnerabilities for ${packageName}`);
|
|
1611
|
+
logger.debug(`OSV: querying vulnerabilities for ${packageName} (ecosystem=${ecosystem})`);
|
|
1363
1612
|
const res = await fetch(url, {
|
|
1364
1613
|
method: "POST",
|
|
1365
1614
|
headers: { "Content-Type": "application/json" },
|
|
1366
1615
|
body: JSON.stringify({
|
|
1367
1616
|
package: {
|
|
1368
1617
|
name: packageName,
|
|
1369
|
-
ecosystem
|
|
1618
|
+
ecosystem
|
|
1370
1619
|
}
|
|
1371
1620
|
})
|
|
1372
1621
|
});
|
|
@@ -1530,6 +1779,7 @@ var FundingCollector = class extends BaseCollector {
|
|
|
1530
1779
|
async fetchNpmFunding(packageName) {
|
|
1531
1780
|
try {
|
|
1532
1781
|
const url = `https://registry.npmjs.org/${encodeURIComponent(packageName)}`;
|
|
1782
|
+
await npmRateLimiter.acquire();
|
|
1533
1783
|
const res = await fetch(url, {
|
|
1534
1784
|
headers: { Accept: "application/json" }
|
|
1535
1785
|
});
|
|
@@ -1564,6 +1814,7 @@ var FundingCollector = class extends BaseCollector {
|
|
|
1564
1814
|
if (this.githubToken) {
|
|
1565
1815
|
headers.Authorization = `Bearer ${this.githubToken}`;
|
|
1566
1816
|
}
|
|
1817
|
+
await githubRateLimiter.acquire();
|
|
1567
1818
|
const res = await fetch(url, { headers });
|
|
1568
1819
|
if (!res.ok) return null;
|
|
1569
1820
|
return await res.text();
|
|
@@ -1594,6 +1845,7 @@ var FundingCollector = class extends BaseCollector {
|
|
|
1594
1845
|
async resolveRepoSlug(packageName) {
|
|
1595
1846
|
try {
|
|
1596
1847
|
const url = `https://registry.npmjs.org/${encodeURIComponent(packageName)}`;
|
|
1848
|
+
await npmRateLimiter.acquire();
|
|
1597
1849
|
const res = await fetch(url, {
|
|
1598
1850
|
headers: { Accept: "application/json" }
|
|
1599
1851
|
});
|
|
@@ -1712,6 +1964,7 @@ var PopularityCollector = class extends BaseCollector {
|
|
|
1712
1964
|
const url = `https://api.npmjs.org/downloads/point/${period}/${encodeURIComponent(packageName)}`;
|
|
1713
1965
|
logger.debug(`Popularity: fetching ${period} downloads: ${url}`);
|
|
1714
1966
|
try {
|
|
1967
|
+
await npmRateLimiter.acquire();
|
|
1715
1968
|
const res = await fetch(url, {
|
|
1716
1969
|
headers: { Accept: "application/json" }
|
|
1717
1970
|
});
|
|
@@ -1739,6 +1992,7 @@ var PopularityCollector = class extends BaseCollector {
|
|
|
1739
1992
|
const url = `https://registry.npmjs.org/-/v1/search?text=${encodeURIComponent(packageName)}&size=1`;
|
|
1740
1993
|
logger.debug(`Popularity: fetching dependent count: ${url}`);
|
|
1741
1994
|
try {
|
|
1995
|
+
await npmRateLimiter.acquire();
|
|
1742
1996
|
const res = await fetch(url, {
|
|
1743
1997
|
headers: { Accept: "application/json" }
|
|
1744
1998
|
});
|
|
@@ -1764,6 +2018,7 @@ var PopularityCollector = class extends BaseCollector {
|
|
|
1764
2018
|
async fetchDependentCountFromRegistry(packageName) {
|
|
1765
2019
|
try {
|
|
1766
2020
|
const url = `https://www.npmjs.com/package/${encodeURIComponent(packageName)}`;
|
|
2021
|
+
await npmRateLimiter.acquire();
|
|
1767
2022
|
const res = await fetch(url, {
|
|
1768
2023
|
headers: {
|
|
1769
2024
|
Accept: "text/html",
|
|
@@ -1931,6 +2186,7 @@ var LicenseCollector = class extends BaseCollector {
|
|
|
1931
2186
|
async fetchLicense(packageName, version) {
|
|
1932
2187
|
const url = `https://registry.npmjs.org/${encodeURIComponent(packageName)}`;
|
|
1933
2188
|
logger.debug(`License: fetching packument ${url}`);
|
|
2189
|
+
await npmRateLimiter.acquire();
|
|
1934
2190
|
const res = await fetch(url, {
|
|
1935
2191
|
headers: { Accept: "application/json" }
|
|
1936
2192
|
});
|
|
@@ -1990,6 +2246,7 @@ var CollectorOrchestrator = class {
|
|
|
1990
2246
|
cache;
|
|
1991
2247
|
options;
|
|
1992
2248
|
registryCollector;
|
|
2249
|
+
pypiRegistryCollector;
|
|
1993
2250
|
githubCollector;
|
|
1994
2251
|
securityCollector;
|
|
1995
2252
|
fundingCollector;
|
|
@@ -2003,6 +2260,7 @@ var CollectorOrchestrator = class {
|
|
|
2003
2260
|
concurrency: options.concurrency ?? 10
|
|
2004
2261
|
};
|
|
2005
2262
|
this.registryCollector = new RegistryCollector(this.cache);
|
|
2263
|
+
this.pypiRegistryCollector = new PyPIRegistryCollector(this.cache);
|
|
2006
2264
|
this.githubCollector = new GitHubCollector(this.cache, this.options.githubToken || void 0);
|
|
2007
2265
|
this.securityCollector = new SecurityCollector(this.cache);
|
|
2008
2266
|
this.fundingCollector = new FundingCollector(this.cache, this.options.githubToken || void 0);
|
|
@@ -2016,13 +2274,15 @@ var CollectorOrchestrator = class {
|
|
|
2016
2274
|
* only the cache is consulted; if there is no cached entry the result gets
|
|
2017
2275
|
* `status: 'offline'` with `data: null`.
|
|
2018
2276
|
*/
|
|
2019
|
-
async collectAll(packageName, version) {
|
|
2277
|
+
async collectAll(packageName, version, ecosystem = "npm") {
|
|
2020
2278
|
logger.info(
|
|
2021
|
-
`Collecting data for ${packageName}@${version} (offline=${String(this.options.offline)})`
|
|
2279
|
+
`Collecting data for ${packageName}@${version} (ecosystem=${ecosystem}, offline=${String(this.options.offline)})`
|
|
2022
2280
|
);
|
|
2023
2281
|
const limit = pLimit(this.options.concurrency);
|
|
2282
|
+
const activeRegistryCollector = ecosystem === "pypi" ? this.pypiRegistryCollector : this.registryCollector;
|
|
2283
|
+
const osvEcosystem = ecosystem === "pypi" ? "PyPI" : "npm";
|
|
2024
2284
|
const entries = [
|
|
2025
|
-
{ key: "registry", collector:
|
|
2285
|
+
{ key: "registry", collector: activeRegistryCollector },
|
|
2026
2286
|
{ key: "github", collector: this.githubCollector },
|
|
2027
2287
|
{ key: "security", collector: this.securityCollector },
|
|
2028
2288
|
{ key: "funding", collector: this.fundingCollector },
|
|
@@ -2038,12 +2298,21 @@ var CollectorOrchestrator = class {
|
|
|
2038
2298
|
result = await this.offlineCollect(collector, packageName, version);
|
|
2039
2299
|
} else {
|
|
2040
2300
|
try {
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2301
|
+
if (key === "security") {
|
|
2302
|
+
result = await Promise.race([
|
|
2303
|
+
this.securityCollector.collect(packageName, version, osvEcosystem),
|
|
2304
|
+
new Promise(
|
|
2305
|
+
(_, reject) => setTimeout(() => reject(new Error("Collector timeout")), COLLECTOR_TIMEOUT)
|
|
2306
|
+
)
|
|
2307
|
+
]);
|
|
2308
|
+
} else {
|
|
2309
|
+
result = await Promise.race([
|
|
2310
|
+
this.onlineCollect(collector, packageName, version),
|
|
2311
|
+
new Promise(
|
|
2312
|
+
(_, reject) => setTimeout(() => reject(new Error("Collector timeout")), COLLECTOR_TIMEOUT)
|
|
2313
|
+
)
|
|
2314
|
+
]);
|
|
2315
|
+
}
|
|
2047
2316
|
} catch {
|
|
2048
2317
|
logger.warn(`[${collector.name}] ${packageName}@${version} => timeout (${COLLECTOR_TIMEOUT}ms)`);
|
|
2049
2318
|
result = {
|
|
@@ -2168,7 +2437,7 @@ var TrustScoreEngine = class {
|
|
|
2168
2437
|
return {
|
|
2169
2438
|
trustScore: Math.round(trustScore),
|
|
2170
2439
|
metrics,
|
|
2171
|
-
insufficientData: unavailableMetrics.length >=
|
|
2440
|
+
insufficientData: unavailableMetrics.length >= 2,
|
|
2172
2441
|
unavailableMetrics
|
|
2173
2442
|
};
|
|
2174
2443
|
}
|
|
@@ -2310,6 +2579,11 @@ var TrustScoreEngine = class {
|
|
|
2310
2579
|
const effectiveWeight = m.weight / totalAvailableWeight;
|
|
2311
2580
|
score += m.score * effectiveWeight;
|
|
2312
2581
|
}
|
|
2582
|
+
const missingWeightFraction = 1 - totalAvailableWeight;
|
|
2583
|
+
if (missingWeightFraction > 0) {
|
|
2584
|
+
const penalty = (score - 50) * missingWeightFraction;
|
|
2585
|
+
score = score - penalty;
|
|
2586
|
+
}
|
|
2313
2587
|
return clamp(Math.round(score));
|
|
2314
2588
|
}
|
|
2315
2589
|
};
|
|
@@ -4764,7 +5038,115 @@ var POPULAR_PACKAGES = [
|
|
|
4764
5038
|
"request",
|
|
4765
5039
|
"tslint",
|
|
4766
5040
|
"node-pre-gyp",
|
|
4767
|
-
"npm-lifecycle"
|
|
5041
|
+
"npm-lifecycle",
|
|
5042
|
+
// ---------------------------------------------------------------------------
|
|
5043
|
+
// Popular Python packages (PyPI)
|
|
5044
|
+
// ---------------------------------------------------------------------------
|
|
5045
|
+
"requests",
|
|
5046
|
+
"flask",
|
|
5047
|
+
"django",
|
|
5048
|
+
"fastapi",
|
|
5049
|
+
"numpy",
|
|
5050
|
+
"pandas",
|
|
5051
|
+
"scipy",
|
|
5052
|
+
"matplotlib",
|
|
5053
|
+
"tensorflow",
|
|
5054
|
+
"torch",
|
|
5055
|
+
"scikit-learn",
|
|
5056
|
+
"keras",
|
|
5057
|
+
"pytorch-lightning",
|
|
5058
|
+
"xgboost",
|
|
5059
|
+
"lightgbm",
|
|
5060
|
+
"pytest",
|
|
5061
|
+
"black",
|
|
5062
|
+
"mypy",
|
|
5063
|
+
"ruff",
|
|
5064
|
+
"pylint",
|
|
5065
|
+
"flake8",
|
|
5066
|
+
"isort",
|
|
5067
|
+
"celery",
|
|
5068
|
+
"redis",
|
|
5069
|
+
"sqlalchemy",
|
|
5070
|
+
"alembic",
|
|
5071
|
+
"pydantic",
|
|
5072
|
+
"httpx",
|
|
5073
|
+
"aiohttp",
|
|
5074
|
+
"uvicorn",
|
|
5075
|
+
"gunicorn",
|
|
5076
|
+
"starlette",
|
|
5077
|
+
"boto3",
|
|
5078
|
+
"botocore",
|
|
5079
|
+
"awscli",
|
|
5080
|
+
"google-cloud-storage",
|
|
5081
|
+
"azure-storage-blob",
|
|
5082
|
+
"pillow",
|
|
5083
|
+
"opencv-python",
|
|
5084
|
+
"beautifulsoup4",
|
|
5085
|
+
"lxml",
|
|
5086
|
+
"scrapy",
|
|
5087
|
+
"cryptography",
|
|
5088
|
+
"paramiko",
|
|
5089
|
+
"fabric",
|
|
5090
|
+
"ansible",
|
|
5091
|
+
"click",
|
|
5092
|
+
"typer",
|
|
5093
|
+
"rich",
|
|
5094
|
+
"tqdm",
|
|
5095
|
+
"colorama",
|
|
5096
|
+
"tabulate",
|
|
5097
|
+
"setuptools",
|
|
5098
|
+
"wheel",
|
|
5099
|
+
"pip",
|
|
5100
|
+
"twine",
|
|
5101
|
+
"poetry",
|
|
5102
|
+
"pdm",
|
|
5103
|
+
"hatch",
|
|
5104
|
+
"jinja2",
|
|
5105
|
+
"mako",
|
|
5106
|
+
"markupsafe",
|
|
5107
|
+
"werkzeug",
|
|
5108
|
+
"psycopg2",
|
|
5109
|
+
"pymongo",
|
|
5110
|
+
"motor",
|
|
5111
|
+
"peewee",
|
|
5112
|
+
"tortoise-orm",
|
|
5113
|
+
"marshmallow",
|
|
5114
|
+
"attrs",
|
|
5115
|
+
"dataclasses-json",
|
|
5116
|
+
"sentry-sdk",
|
|
5117
|
+
"prometheus-client",
|
|
5118
|
+
"opentelemetry-api",
|
|
5119
|
+
"transformers",
|
|
5120
|
+
"huggingface-hub",
|
|
5121
|
+
"tokenizers",
|
|
5122
|
+
"datasets",
|
|
5123
|
+
"langchain",
|
|
5124
|
+
"openai",
|
|
5125
|
+
"anthropic",
|
|
5126
|
+
"tiktoken",
|
|
5127
|
+
"pytest-cov",
|
|
5128
|
+
"pytest-asyncio",
|
|
5129
|
+
"pytest-mock",
|
|
5130
|
+
"coverage",
|
|
5131
|
+
"pyyaml",
|
|
5132
|
+
"toml",
|
|
5133
|
+
"python-dotenv",
|
|
5134
|
+
"decouple",
|
|
5135
|
+
"arrow",
|
|
5136
|
+
"pendulum",
|
|
5137
|
+
"python-dateutil",
|
|
5138
|
+
"stripe",
|
|
5139
|
+
"twilio",
|
|
5140
|
+
"sendgrid",
|
|
5141
|
+
"selenium",
|
|
5142
|
+
"playwright",
|
|
5143
|
+
"httptools",
|
|
5144
|
+
"orjson",
|
|
5145
|
+
"ujson",
|
|
5146
|
+
"msgpack",
|
|
5147
|
+
"networkx",
|
|
5148
|
+
"sympy",
|
|
5149
|
+
"statsmodels"
|
|
4768
5150
|
];
|
|
4769
5151
|
var TyposquatDetector = class _TyposquatDetector {
|
|
4770
5152
|
popularPackages;
|
|
@@ -6576,6 +6958,10 @@ export {
|
|
|
6576
6958
|
isDebug,
|
|
6577
6959
|
logger,
|
|
6578
6960
|
createLogger,
|
|
6961
|
+
RateLimiter,
|
|
6962
|
+
githubRateLimiter,
|
|
6963
|
+
npmRateLimiter,
|
|
6964
|
+
pypiRateLimiter,
|
|
6579
6965
|
CollectorOrchestrator,
|
|
6580
6966
|
TrustScoreEngine,
|
|
6581
6967
|
ZombieDetector,
|
|
@@ -6586,4 +6972,4 @@ export {
|
|
|
6586
6972
|
getBlastRadius,
|
|
6587
6973
|
getImportingFiles
|
|
6588
6974
|
};
|
|
6589
|
-
//# sourceMappingURL=chunk-
|
|
6975
|
+
//# sourceMappingURL=chunk-DLWG22RC.js.map
|