maven-proxy 1.0.1 → 1.0.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.
- package/README.md +466 -420
- package/bin/maven-proxy.js +585 -573
- package/package.json +54 -54
- package/scripts/truststore.js +96 -96
- package/src/cache/cache-path.js +50 -50
- package/src/cache/downloader.js +350 -350
- package/src/cert/cert-manager.js +194 -194
- package/src/cert/truststore-utils.js +383 -289
- package/src/common/console-log-file.js +61 -61
- package/src/common/daily-log-file.js +78 -78
- package/src/common/domain-match.js +39 -39
- package/src/common/download-log-writer.js +26 -26
- package/src/common/ecosystem.js +63 -63
- package/src/common/java-home.js +327 -327
- package/src/config/config.js +224 -213
- package/src/index.js +93 -93
- package/src/proxy/proxy-connect-handler.js +173 -173
- package/src/proxy/proxy-http-handler.js +187 -187
- package/src/proxy/proxy-server.js +35 -35
- package/src/proxy/upstream-proxy.js +236 -236
- package/src/repo/repo-server.js +120 -120
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
import util from "node:util";
|
|
2
|
-
import { DailyLogFile } from "./daily-log-file.js";
|
|
3
|
-
|
|
4
|
-
const MIRROR_INSTALLED = Symbol.for("maven-proxy.console-log-file.installed");
|
|
5
|
-
const GLOBAL_ERROR_HOOK_INSTALLED = Symbol.for("maven-proxy.global-error-hook.installed");
|
|
6
|
-
|
|
7
|
-
function mirrorConsoleMethod({ level, originalMethod, logFile }) {
|
|
8
|
-
return (...args) => {
|
|
9
|
-
originalMethod(...args);
|
|
10
|
-
|
|
11
|
-
const line = `[${new Date().toISOString()}] [${level}] ${util.format(...args)}`;
|
|
12
|
-
logFile.appendLine(line).catch((error) => {
|
|
13
|
-
process.stderr.write(`[maven-proxy] write console log failed: ${error.message}\n`);
|
|
14
|
-
});
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function installConsoleLogFileMirror({ logDir, retentionDays = 7 }) {
|
|
19
|
-
if (globalThis[MIRROR_INSTALLED]) {
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
globalThis[MIRROR_INSTALLED] = true;
|
|
23
|
-
|
|
24
|
-
const logFile = new DailyLogFile({
|
|
25
|
-
logDir,
|
|
26
|
-
filePrefix: "console",
|
|
27
|
-
retentionDays,
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
console.log = mirrorConsoleMethod({
|
|
31
|
-
level: "INFO",
|
|
32
|
-
originalMethod: console.log.bind(console),
|
|
33
|
-
logFile,
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
console.warn = mirrorConsoleMethod({
|
|
37
|
-
level: "WARN",
|
|
38
|
-
originalMethod: console.warn.bind(console),
|
|
39
|
-
logFile,
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
console.error = mirrorConsoleMethod({
|
|
43
|
-
level: "ERROR",
|
|
44
|
-
originalMethod: console.error.bind(console),
|
|
45
|
-
logFile,
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function installGlobalErrorLogging() {
|
|
50
|
-
if (globalThis[GLOBAL_ERROR_HOOK_INSTALLED]) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
globalThis[GLOBAL_ERROR_HOOK_INSTALLED] = true;
|
|
54
|
-
|
|
55
|
-
process.on("uncaughtExceptionMonitor", (error, origin) => {
|
|
56
|
-
console.error(`[global-error] uncaughtException origin=${origin}`, error);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
process.on("unhandledRejection", (reason) => {
|
|
60
|
-
console.error("[global-error] unhandledRejection", reason);
|
|
61
|
-
});
|
|
1
|
+
import util from "node:util";
|
|
2
|
+
import { DailyLogFile } from "./daily-log-file.js";
|
|
3
|
+
|
|
4
|
+
const MIRROR_INSTALLED = Symbol.for("maven-proxy.console-log-file.installed");
|
|
5
|
+
const GLOBAL_ERROR_HOOK_INSTALLED = Symbol.for("maven-proxy.global-error-hook.installed");
|
|
6
|
+
|
|
7
|
+
function mirrorConsoleMethod({ level, originalMethod, logFile }) {
|
|
8
|
+
return (...args) => {
|
|
9
|
+
originalMethod(...args);
|
|
10
|
+
|
|
11
|
+
const line = `[${new Date().toISOString()}] [${level}] ${util.format(...args)}`;
|
|
12
|
+
logFile.appendLine(line).catch((error) => {
|
|
13
|
+
process.stderr.write(`[maven-proxy] write console log failed: ${error.message}\n`);
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function installConsoleLogFileMirror({ logDir, retentionDays = 7 }) {
|
|
19
|
+
if (globalThis[MIRROR_INSTALLED]) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
globalThis[MIRROR_INSTALLED] = true;
|
|
23
|
+
|
|
24
|
+
const logFile = new DailyLogFile({
|
|
25
|
+
logDir,
|
|
26
|
+
filePrefix: "console",
|
|
27
|
+
retentionDays,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
console.log = mirrorConsoleMethod({
|
|
31
|
+
level: "INFO",
|
|
32
|
+
originalMethod: console.log.bind(console),
|
|
33
|
+
logFile,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
console.warn = mirrorConsoleMethod({
|
|
37
|
+
level: "WARN",
|
|
38
|
+
originalMethod: console.warn.bind(console),
|
|
39
|
+
logFile,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
console.error = mirrorConsoleMethod({
|
|
43
|
+
level: "ERROR",
|
|
44
|
+
originalMethod: console.error.bind(console),
|
|
45
|
+
logFile,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function installGlobalErrorLogging() {
|
|
50
|
+
if (globalThis[GLOBAL_ERROR_HOOK_INSTALLED]) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
globalThis[GLOBAL_ERROR_HOOK_INSTALLED] = true;
|
|
54
|
+
|
|
55
|
+
process.on("uncaughtExceptionMonitor", (error, origin) => {
|
|
56
|
+
console.error(`[global-error] uncaughtException origin=${origin}`, error);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
process.on("unhandledRejection", (reason) => {
|
|
60
|
+
console.error("[global-error] unhandledRejection", reason);
|
|
61
|
+
});
|
|
62
62
|
}
|
|
@@ -1,79 +1,79 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
|
|
4
|
-
function toDateStampLocal(date = new Date()) {
|
|
5
|
-
const year = String(date.getFullYear());
|
|
6
|
-
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
7
|
-
const day = String(date.getDate()).padStart(2, "0");
|
|
8
|
-
return `${year}-${month}-${day}`;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function escapeRegExp(text) {
|
|
12
|
-
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export class DailyLogFile {
|
|
16
|
-
constructor({ logDir, filePrefix, retentionDays = 7 }) {
|
|
17
|
-
this.logDir = logDir;
|
|
18
|
-
this.filePrefix = filePrefix;
|
|
19
|
-
this.retentionDays = Math.max(1, Number.parseInt(retentionDays, 10) || 7);
|
|
20
|
-
this.ensureDirPromise = null;
|
|
21
|
-
this.lastCleanupStamp = "";
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async ensureDir() {
|
|
25
|
-
if (!this.ensureDirPromise) {
|
|
26
|
-
this.ensureDirPromise = fs.promises.mkdir(this.logDir, { recursive: true });
|
|
27
|
-
}
|
|
28
|
-
await this.ensureDirPromise;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
getDailyLogPath(date = new Date()) {
|
|
32
|
-
return path.join(this.logDir, `${this.filePrefix}-${toDateStampLocal(date)}.log`);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async cleanupOldLogsIfNeeded(date = new Date()) {
|
|
36
|
-
const todayStamp = toDateStampLocal(date);
|
|
37
|
-
if (this.lastCleanupStamp === todayStamp) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
this.lastCleanupStamp = todayStamp;
|
|
42
|
-
|
|
43
|
-
const cutoff = new Date(date);
|
|
44
|
-
cutoff.setHours(0, 0, 0, 0);
|
|
45
|
-
cutoff.setDate(cutoff.getDate() - (this.retentionDays - 1));
|
|
46
|
-
const cutoffStamp = toDateStampLocal(cutoff);
|
|
47
|
-
|
|
48
|
-
const pattern = new RegExp(`^${escapeRegExp(this.filePrefix)}-(\\d{4}-\\d{2}-\\d{2})\\.log$`);
|
|
49
|
-
const entries = await fs.promises.readdir(this.logDir, { withFileTypes: true });
|
|
50
|
-
|
|
51
|
-
const deleteTasks = [];
|
|
52
|
-
for (const entry of entries) {
|
|
53
|
-
if (!entry.isFile()) {
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const match = entry.name.match(pattern);
|
|
58
|
-
if (!match) {
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const dateStamp = match[1];
|
|
63
|
-
if (dateStamp < cutoffStamp) {
|
|
64
|
-
deleteTasks.push(fs.promises.unlink(path.join(this.logDir, entry.name)));
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (deleteTasks.length > 0) {
|
|
69
|
-
await Promise.all(deleteTasks);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async appendLine(line, date = new Date()) {
|
|
74
|
-
await this.ensureDir();
|
|
75
|
-
await this.cleanupOldLogsIfNeeded(date);
|
|
76
|
-
const content = line.endsWith("\n") ? line : `${line}\n`;
|
|
77
|
-
await fs.promises.appendFile(this.getDailyLogPath(date), content, "utf8");
|
|
78
|
-
}
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
function toDateStampLocal(date = new Date()) {
|
|
5
|
+
const year = String(date.getFullYear());
|
|
6
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
7
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
8
|
+
return `${year}-${month}-${day}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function escapeRegExp(text) {
|
|
12
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class DailyLogFile {
|
|
16
|
+
constructor({ logDir, filePrefix, retentionDays = 7 }) {
|
|
17
|
+
this.logDir = logDir;
|
|
18
|
+
this.filePrefix = filePrefix;
|
|
19
|
+
this.retentionDays = Math.max(1, Number.parseInt(retentionDays, 10) || 7);
|
|
20
|
+
this.ensureDirPromise = null;
|
|
21
|
+
this.lastCleanupStamp = "";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async ensureDir() {
|
|
25
|
+
if (!this.ensureDirPromise) {
|
|
26
|
+
this.ensureDirPromise = fs.promises.mkdir(this.logDir, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
await this.ensureDirPromise;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
getDailyLogPath(date = new Date()) {
|
|
32
|
+
return path.join(this.logDir, `${this.filePrefix}-${toDateStampLocal(date)}.log`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async cleanupOldLogsIfNeeded(date = new Date()) {
|
|
36
|
+
const todayStamp = toDateStampLocal(date);
|
|
37
|
+
if (this.lastCleanupStamp === todayStamp) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.lastCleanupStamp = todayStamp;
|
|
42
|
+
|
|
43
|
+
const cutoff = new Date(date);
|
|
44
|
+
cutoff.setHours(0, 0, 0, 0);
|
|
45
|
+
cutoff.setDate(cutoff.getDate() - (this.retentionDays - 1));
|
|
46
|
+
const cutoffStamp = toDateStampLocal(cutoff);
|
|
47
|
+
|
|
48
|
+
const pattern = new RegExp(`^${escapeRegExp(this.filePrefix)}-(\\d{4}-\\d{2}-\\d{2})\\.log$`);
|
|
49
|
+
const entries = await fs.promises.readdir(this.logDir, { withFileTypes: true });
|
|
50
|
+
|
|
51
|
+
const deleteTasks = [];
|
|
52
|
+
for (const entry of entries) {
|
|
53
|
+
if (!entry.isFile()) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const match = entry.name.match(pattern);
|
|
58
|
+
if (!match) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const dateStamp = match[1];
|
|
63
|
+
if (dateStamp < cutoffStamp) {
|
|
64
|
+
deleteTasks.push(fs.promises.unlink(path.join(this.logDir, entry.name)));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (deleteTasks.length > 0) {
|
|
69
|
+
await Promise.all(deleteTasks);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async appendLine(line, date = new Date()) {
|
|
74
|
+
await this.ensureDir();
|
|
75
|
+
await this.cleanupOldLogsIfNeeded(date);
|
|
76
|
+
const content = line.endsWith("\n") ? line : `${line}\n`;
|
|
77
|
+
await fs.promises.appendFile(this.getDailyLogPath(date), content, "utf8");
|
|
78
|
+
}
|
|
79
79
|
}
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
function escapeRegExp(value) {
|
|
2
|
-
return value.replace(/[.*+?^${}()|[\\]\\]/g, "\\$&");
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
function wildcardToRegExp(pattern) {
|
|
6
|
-
const escaped = pattern
|
|
7
|
-
.split("*")
|
|
8
|
-
.map((segment) => escapeRegExp(segment))
|
|
9
|
-
.join(".*");
|
|
10
|
-
return new RegExp(`^${escaped}$`, "i");
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function matchesDomain(hostname, patterns) {
|
|
14
|
-
if (!hostname || !patterns || patterns.length === 0) {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const host = hostname.toLowerCase();
|
|
19
|
-
|
|
20
|
-
for (const rawPattern of patterns) {
|
|
21
|
-
const pattern = rawPattern.trim().toLowerCase();
|
|
22
|
-
if (!pattern) {
|
|
23
|
-
continue;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (pattern.includes("*")) {
|
|
27
|
-
if (wildcardToRegExp(pattern).test(host)) {
|
|
28
|
-
return true;
|
|
29
|
-
}
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (host === pattern || host.endsWith(`.${pattern}`)) {
|
|
34
|
-
return true;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
1
|
+
function escapeRegExp(value) {
|
|
2
|
+
return value.replace(/[.*+?^${}()|[\\]\\]/g, "\\$&");
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function wildcardToRegExp(pattern) {
|
|
6
|
+
const escaped = pattern
|
|
7
|
+
.split("*")
|
|
8
|
+
.map((segment) => escapeRegExp(segment))
|
|
9
|
+
.join(".*");
|
|
10
|
+
return new RegExp(`^${escaped}$`, "i");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function matchesDomain(hostname, patterns) {
|
|
14
|
+
if (!hostname || !patterns || patterns.length === 0) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const host = hostname.toLowerCase();
|
|
19
|
+
|
|
20
|
+
for (const rawPattern of patterns) {
|
|
21
|
+
const pattern = rawPattern.trim().toLowerCase();
|
|
22
|
+
if (!pattern) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (pattern.includes("*")) {
|
|
27
|
+
if (wildcardToRegExp(pattern).test(host)) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (host === pattern || host.endsWith(`.${pattern}`)) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { DailyLogFile } from "./daily-log-file.js";
|
|
2
|
-
|
|
3
|
-
export class DownloadLogWriter {
|
|
4
|
-
constructor(logDir, retentionDays = 7) {
|
|
5
|
-
this.logFile = new DailyLogFile({
|
|
6
|
-
logDir,
|
|
7
|
-
filePrefix: "download",
|
|
8
|
-
retentionDays,
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
async append(event, url, details = {}) {
|
|
13
|
-
const record = {
|
|
14
|
-
time: new Date().toISOString(),
|
|
15
|
-
event,
|
|
16
|
-
url,
|
|
17
|
-
...details,
|
|
18
|
-
};
|
|
19
|
-
await this.logFile.appendLine(JSON.stringify(record));
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
write(event, url, details = {}) {
|
|
23
|
-
this.append(event, url, details).catch((error) => {
|
|
24
|
-
console.warn(`[downloader] write download log failed: ${error.message}`);
|
|
25
|
-
});
|
|
26
|
-
}
|
|
1
|
+
import { DailyLogFile } from "./daily-log-file.js";
|
|
2
|
+
|
|
3
|
+
export class DownloadLogWriter {
|
|
4
|
+
constructor(logDir, retentionDays = 7) {
|
|
5
|
+
this.logFile = new DailyLogFile({
|
|
6
|
+
logDir,
|
|
7
|
+
filePrefix: "download",
|
|
8
|
+
retentionDays,
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async append(event, url, details = {}) {
|
|
13
|
+
const record = {
|
|
14
|
+
time: new Date().toISOString(),
|
|
15
|
+
event,
|
|
16
|
+
url,
|
|
17
|
+
...details,
|
|
18
|
+
};
|
|
19
|
+
await this.logFile.appendLine(JSON.stringify(record));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
write(event, url, details = {}) {
|
|
23
|
+
this.append(event, url, details).catch((error) => {
|
|
24
|
+
console.warn(`[downloader] write download log failed: ${error.message}`);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
27
|
}
|
package/src/common/ecosystem.js
CHANGED
|
@@ -1,64 +1,64 @@
|
|
|
1
|
-
const MAVEN_ARTIFACT_EXTENSIONS = new Set([
|
|
2
|
-
".pom",
|
|
3
|
-
".jar",
|
|
4
|
-
".aar",
|
|
5
|
-
".war",
|
|
6
|
-
".module",
|
|
7
|
-
".xml",
|
|
8
|
-
".sha1",
|
|
9
|
-
".md5",
|
|
10
|
-
]);
|
|
11
|
-
|
|
12
|
-
function safeDecode(pathname) {
|
|
13
|
-
try {
|
|
14
|
-
return decodeURIComponent(pathname || "/");
|
|
15
|
-
} catch {
|
|
16
|
-
return pathname || "/";
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function hasExtension(pathname, extensions) {
|
|
21
|
-
const lower = String(pathname || "").toLowerCase();
|
|
22
|
-
for (const ext of extensions) {
|
|
23
|
-
if (lower.endsWith(ext)) {
|
|
24
|
-
return true;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function detectPackageEcosystem(urlObj, config, matchesDomain) {
|
|
31
|
-
const hostname = String(urlObj.hostname || "").toLowerCase();
|
|
32
|
-
const pathname = safeDecode(urlObj.pathname || "/");
|
|
33
|
-
const lowerPath = pathname.toLowerCase();
|
|
34
|
-
|
|
35
|
-
if (matchesDomain(hostname, config.npmRegistryDomains || [])) {
|
|
36
|
-
return "npm";
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (matchesDomain(hostname, config.mavenRepoDomains || [])) {
|
|
40
|
-
return "maven";
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (lowerPath.startsWith("/maven2/") || hasExtension(lowerPath, MAVEN_ARTIFACT_EXTENSIONS)) {
|
|
44
|
-
return "maven";
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (
|
|
48
|
-
lowerPath.startsWith("/-/v1/") ||
|
|
49
|
-
/\/\/-\/.+\.tgz$/i.test(lowerPath) ||
|
|
50
|
-
lowerPath.startsWith("/@")
|
|
51
|
-
) {
|
|
52
|
-
return "npm";
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (hostname.includes("npm")) {
|
|
56
|
-
return "npm";
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (hostname.includes("maven") || hostname.includes("jitpack") || hostname.includes("gradle")) {
|
|
60
|
-
return "maven";
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return "generic";
|
|
1
|
+
const MAVEN_ARTIFACT_EXTENSIONS = new Set([
|
|
2
|
+
".pom",
|
|
3
|
+
".jar",
|
|
4
|
+
".aar",
|
|
5
|
+
".war",
|
|
6
|
+
".module",
|
|
7
|
+
".xml",
|
|
8
|
+
".sha1",
|
|
9
|
+
".md5",
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
function safeDecode(pathname) {
|
|
13
|
+
try {
|
|
14
|
+
return decodeURIComponent(pathname || "/");
|
|
15
|
+
} catch {
|
|
16
|
+
return pathname || "/";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function hasExtension(pathname, extensions) {
|
|
21
|
+
const lower = String(pathname || "").toLowerCase();
|
|
22
|
+
for (const ext of extensions) {
|
|
23
|
+
if (lower.endsWith(ext)) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function detectPackageEcosystem(urlObj, config, matchesDomain) {
|
|
31
|
+
const hostname = String(urlObj.hostname || "").toLowerCase();
|
|
32
|
+
const pathname = safeDecode(urlObj.pathname || "/");
|
|
33
|
+
const lowerPath = pathname.toLowerCase();
|
|
34
|
+
|
|
35
|
+
if (matchesDomain(hostname, config.npmRegistryDomains || [])) {
|
|
36
|
+
return "npm";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (matchesDomain(hostname, config.mavenRepoDomains || [])) {
|
|
40
|
+
return "maven";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (lowerPath.startsWith("/maven2/") || hasExtension(lowerPath, MAVEN_ARTIFACT_EXTENSIONS)) {
|
|
44
|
+
return "maven";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (
|
|
48
|
+
lowerPath.startsWith("/-/v1/") ||
|
|
49
|
+
/\/\/-\/.+\.tgz$/i.test(lowerPath) ||
|
|
50
|
+
lowerPath.startsWith("/@")
|
|
51
|
+
) {
|
|
52
|
+
return "npm";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (hostname.includes("npm")) {
|
|
56
|
+
return "npm";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (hostname.includes("maven") || hostname.includes("jitpack") || hostname.includes("gradle")) {
|
|
60
|
+
return "maven";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return "generic";
|
|
64
64
|
}
|