inup 1.4.12 → 1.5.1
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/LICENSE +21 -0
- package/README.md +39 -30
- package/dist/cli.js +29 -14
- package/dist/config/project-config.js +6 -0
- package/dist/core/package-detector.js +3 -2
- package/dist/core/upgrade-runner.js +6 -3
- package/dist/features/changelog/clients/github-client.js +134 -0
- package/dist/features/changelog/clients/npm-registry-client.js +53 -0
- package/dist/features/changelog/index.js +19 -0
- package/dist/features/changelog/parsers/changelog-parser.js +68 -0
- package/dist/features/changelog/parsers/github-release-html-parser.js +61 -0
- package/dist/features/changelog/parsers/package-metadata.js +34 -0
- package/dist/features/changelog/parsers/repository-ref.js +26 -0
- package/dist/features/changelog/services/changelog-service.js +30 -0
- package/dist/features/changelog/services/package-metadata-service.js +108 -0
- package/dist/features/changelog/services/release-notes-service.js +180 -0
- package/dist/features/changelog/types/changelog.types.js +3 -0
- package/dist/interactive-ui.js +242 -114
- package/dist/services/background-audit.js +60 -0
- package/dist/services/index.js +3 -1
- package/dist/services/vulnerability-checker.js +133 -0
- package/dist/ui/controllers/index.js +8 -0
- package/dist/ui/controllers/package-info-modal-controller.js +237 -0
- package/dist/ui/controllers/vulnerability-audit-controller.js +82 -0
- package/dist/ui/index.js +3 -0
- package/dist/ui/input-handler.js +40 -9
- package/dist/ui/modal/index.js +22 -0
- package/dist/ui/modal/layout.js +84 -0
- package/dist/ui/modal/package-info-sections.js +327 -0
- package/dist/ui/modal/package-info.js +147 -0
- package/dist/ui/modal/theme-selector.js +46 -0
- package/dist/ui/modal/types.js +3 -0
- package/dist/ui/presenters/index.js +11 -0
- package/dist/ui/presenters/vulnerability.js +76 -0
- package/dist/ui/renderer/index.js +9 -11
- package/dist/ui/renderer/package-list.js +135 -62
- package/dist/ui/state/filter-manager.js +17 -2
- package/dist/ui/state/modal-manager.js +48 -6
- package/dist/ui/state/state-manager.js +42 -7
- package/dist/ui/utils/cursor.js +18 -0
- package/dist/ui/utils/index.js +8 -1
- package/dist/ui/utils/terminal-input.js +125 -0
- package/dist/ui/utils/text.js +75 -0
- package/dist/ui/utils/version.js +3 -2
- package/dist/utils/git.js +33 -0
- package/dist/utils/index.js +1 -0
- package/package.json +22 -19
- package/dist/services/changelog-fetcher.js +0 -215
- package/dist/ui/renderer/modal.js +0 -190
- package/dist/ui/renderer/theme-selector.js +0 -83
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.TerminalInput = void 0;
|
|
37
|
+
const readline = __importStar(require("node:readline"));
|
|
38
|
+
const ESCAPE_CODE_TIMEOUT_MS = 25;
|
|
39
|
+
exports.TerminalInput = {
|
|
40
|
+
startKeypressSession(onKeypress) {
|
|
41
|
+
const rl = readline.createInterface({
|
|
42
|
+
input: process.stdin,
|
|
43
|
+
escapeCodeTimeout: ESCAPE_CODE_TIMEOUT_MS,
|
|
44
|
+
terminal: true,
|
|
45
|
+
});
|
|
46
|
+
readline.emitKeypressEvents(process.stdin, rl);
|
|
47
|
+
if (process.stdin.setRawMode) {
|
|
48
|
+
process.stdin.setRawMode(true);
|
|
49
|
+
}
|
|
50
|
+
process.stdin.resume();
|
|
51
|
+
process.stdin.on('keypress', onKeypress);
|
|
52
|
+
return {
|
|
53
|
+
close: () => {
|
|
54
|
+
process.stdin.off('keypress', onKeypress);
|
|
55
|
+
rl.close();
|
|
56
|
+
if (process.stdin.setRawMode) {
|
|
57
|
+
process.stdin.setRawMode(false);
|
|
58
|
+
}
|
|
59
|
+
process.stdin.pause();
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
promptForConfirmation(prompt, defaultValue = true) {
|
|
64
|
+
return new Promise((resolve) => {
|
|
65
|
+
const rl = readline.createInterface({
|
|
66
|
+
input: process.stdin,
|
|
67
|
+
output: process.stdout,
|
|
68
|
+
terminal: true,
|
|
69
|
+
});
|
|
70
|
+
const finish = (value) => {
|
|
71
|
+
rl.close();
|
|
72
|
+
resolve(value);
|
|
73
|
+
};
|
|
74
|
+
rl.question(prompt, (answer) => {
|
|
75
|
+
const normalizedAnswer = answer.trim().toLowerCase();
|
|
76
|
+
if (normalizedAnswer === '') {
|
|
77
|
+
finish(defaultValue);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
finish(normalizedAnswer === 'y' || normalizedAnswer === 'yes');
|
|
81
|
+
});
|
|
82
|
+
rl.on('SIGINT', () => finish(false));
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
promptForImmediateConfirmation(prompt, defaultValue = true) {
|
|
86
|
+
return new Promise((resolve) => {
|
|
87
|
+
process.stdout.write(prompt);
|
|
88
|
+
let cleanup = () => { };
|
|
89
|
+
const finish = (value) => {
|
|
90
|
+
cleanup();
|
|
91
|
+
process.stdout.write('\n');
|
|
92
|
+
resolve(value);
|
|
93
|
+
};
|
|
94
|
+
try {
|
|
95
|
+
const session = exports.TerminalInput.startKeypressSession((str, key) => {
|
|
96
|
+
const normalized = str.trim().toLowerCase();
|
|
97
|
+
if (key.name === 'return' || key.name === 'enter') {
|
|
98
|
+
finish(defaultValue);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (normalized === 'y') {
|
|
102
|
+
finish(true);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (normalized === 'n') {
|
|
106
|
+
finish(false);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (key.ctrl && key.name === 'c') {
|
|
110
|
+
finish(false);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
cleanup = () => {
|
|
114
|
+
session.close();
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
exports.TerminalInput.promptForConfirmation(prompt, defaultValue)
|
|
119
|
+
.then(resolve)
|
|
120
|
+
.catch(() => resolve(false));
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
//# sourceMappingURL=terminal-input.js.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.stripAnsi = stripAnsi;
|
|
4
|
+
exports.getVisualLength = getVisualLength;
|
|
5
|
+
exports.truncatePlainText = truncatePlainText;
|
|
6
|
+
exports.wrapPlainText = wrapPlainText;
|
|
7
|
+
const version_1 = require("./version");
|
|
8
|
+
function stripAnsi(text) {
|
|
9
|
+
return version_1.VersionUtils.stripAnsi(text);
|
|
10
|
+
}
|
|
11
|
+
function getVisualLength(text) {
|
|
12
|
+
const cleaned = stripAnsi(text);
|
|
13
|
+
const SegmenterCtor = Intl.Segmenter;
|
|
14
|
+
let length = 0;
|
|
15
|
+
const segments = SegmenterCtor
|
|
16
|
+
? SegmenterCtor.prototype.segment.call(new SegmenterCtor(undefined, { granularity: 'grapheme' }), cleaned)
|
|
17
|
+
: cleaned;
|
|
18
|
+
for (const item of segments) {
|
|
19
|
+
const segment = typeof item === 'string' ? item : item.segment;
|
|
20
|
+
if (/\p{Extended_Pictographic}/u.test(segment) || segment.includes('\uFE0F')) {
|
|
21
|
+
length += 2;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
for (const char of segment) {
|
|
25
|
+
const codePoint = char.codePointAt(0) ?? 0;
|
|
26
|
+
if ((codePoint >= 0xfe00 && codePoint <= 0xfe0f) ||
|
|
27
|
+
(codePoint >= 0x0300 && codePoint <= 0x036f)) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
length += 1;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return length;
|
|
35
|
+
}
|
|
36
|
+
function truncatePlainText(text, maxWidth) {
|
|
37
|
+
if (maxWidth <= 0) {
|
|
38
|
+
return '';
|
|
39
|
+
}
|
|
40
|
+
if (getVisualLength(text) <= maxWidth) {
|
|
41
|
+
return text;
|
|
42
|
+
}
|
|
43
|
+
if (maxWidth <= 3) {
|
|
44
|
+
return '.'.repeat(maxWidth);
|
|
45
|
+
}
|
|
46
|
+
return text.substring(0, maxWidth - 3) + '...';
|
|
47
|
+
}
|
|
48
|
+
function wrapPlainText(text, maxWidth) {
|
|
49
|
+
if (!text) {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
if (maxWidth <= 0 || getVisualLength(text) <= maxWidth) {
|
|
53
|
+
return [text];
|
|
54
|
+
}
|
|
55
|
+
const lines = [];
|
|
56
|
+
let current = '';
|
|
57
|
+
const words = text.split(' ');
|
|
58
|
+
for (const word of words) {
|
|
59
|
+
const candidate = (current + ' ' + word).trim();
|
|
60
|
+
if (getVisualLength(candidate) > maxWidth) {
|
|
61
|
+
if (current) {
|
|
62
|
+
lines.push(current);
|
|
63
|
+
}
|
|
64
|
+
current = word;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
current = current ? `${current} ${word}` : word;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (current) {
|
|
71
|
+
lines.push(current);
|
|
72
|
+
}
|
|
73
|
+
return lines;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=text.js.map
|
package/dist/ui/utils/version.js
CHANGED
|
@@ -8,7 +8,8 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
8
8
|
/**
|
|
9
9
|
* ANSI escape code pattern for stripping terminal colors
|
|
10
10
|
*/
|
|
11
|
-
const ANSI_PATTERN = /\u001b\[[0-9
|
|
11
|
+
const ANSI_PATTERN = /\u001b\[[0-9;?]*[ -/]*[@-~]/g;
|
|
12
|
+
const OSC8_PATTERN = /\u001b]8;;.*?(?:\u0007|\u001b\\)|\u001b]8;;(?:\u0007|\u001b\\)/g;
|
|
12
13
|
class VersionUtils {
|
|
13
14
|
static applyVersionPrefix(originalSpecifier, targetVersion) {
|
|
14
15
|
// Extract prefix from original specifier (^ or ~)
|
|
@@ -21,7 +22,7 @@ class VersionUtils {
|
|
|
21
22
|
* Strip ANSI escape codes from a string
|
|
22
23
|
*/
|
|
23
24
|
static stripAnsi(str) {
|
|
24
|
-
return str.replace(ANSI_PATTERN, '');
|
|
25
|
+
return str.replace(OSC8_PATTERN, '').replace(ANSI_PATTERN, '');
|
|
25
26
|
}
|
|
26
27
|
/**
|
|
27
28
|
* Get the visual length of a string (excluding ANSI codes)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getGitWorkingTreeState = getGitWorkingTreeState;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
/**
|
|
6
|
+
* Detect whether cwd is a git work tree and whether it has local changes.
|
|
7
|
+
* Fail soft if git is unavailable or cwd is not a repository.
|
|
8
|
+
*/
|
|
9
|
+
function getGitWorkingTreeState(cwd) {
|
|
10
|
+
try {
|
|
11
|
+
const isInsideWorkTree = (0, child_process_1.execSync)('git rev-parse --is-inside-work-tree', {
|
|
12
|
+
cwd,
|
|
13
|
+
encoding: 'utf-8',
|
|
14
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
15
|
+
}).trim();
|
|
16
|
+
if (isInsideWorkTree !== 'true') {
|
|
17
|
+
return { isRepo: false, isDirty: false };
|
|
18
|
+
}
|
|
19
|
+
const status = (0, child_process_1.execSync)('git status --porcelain', {
|
|
20
|
+
cwd,
|
|
21
|
+
encoding: 'utf-8',
|
|
22
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
23
|
+
});
|
|
24
|
+
return {
|
|
25
|
+
isRepo: true,
|
|
26
|
+
isDirty: status.trim().length > 0,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return { isRepo: false, isDirty: false };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=git.js.map
|
package/dist/utils/index.js
CHANGED
|
@@ -20,6 +20,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
20
20
|
exports.collectAllDependenciesAsync = exports.readPackageJsonAsync = void 0;
|
|
21
21
|
__exportStar(require("./filesystem"), exports);
|
|
22
22
|
__exportStar(require("./exec"), exports);
|
|
23
|
+
__exportStar(require("./git"), exports);
|
|
23
24
|
__exportStar(require("./version"), exports);
|
|
24
25
|
__exportStar(require("./debug-logger"), exports);
|
|
25
26
|
// Re-export async functions for convenience
|
package/package.json
CHANGED
|
@@ -1,26 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "inup",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Interactive
|
|
3
|
+
"version": "1.5.1",
|
|
4
|
+
"description": "Interactive dependency upgrader for npm, yarn, pnpm & bun. Zero-config, monorepo-ready. Upgrade-interactive for every package manager.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"inup": "./dist/cli.js"
|
|
8
8
|
},
|
|
9
9
|
"author": "Donfear",
|
|
10
10
|
"keywords": [
|
|
11
|
+
"upgrade-interactive",
|
|
12
|
+
"interactive",
|
|
13
|
+
"dependency-management",
|
|
14
|
+
"outdated",
|
|
15
|
+
"upgrade",
|
|
16
|
+
"update",
|
|
11
17
|
"npm",
|
|
12
18
|
"yarn",
|
|
13
19
|
"pnpm",
|
|
14
20
|
"bun",
|
|
15
|
-
"upgrade",
|
|
16
|
-
"interactive",
|
|
17
|
-
"cli",
|
|
18
|
-
"package-manager",
|
|
19
|
-
"dependency-management",
|
|
20
|
-
"yarn-upgrade-interactive",
|
|
21
21
|
"monorepo",
|
|
22
22
|
"workspace",
|
|
23
|
-
"
|
|
23
|
+
"cli",
|
|
24
|
+
"vulnerability",
|
|
25
|
+
"audit",
|
|
26
|
+
"changelog",
|
|
27
|
+
"package-manager",
|
|
28
|
+
"dependencies",
|
|
29
|
+
"semver",
|
|
30
|
+
"ncu"
|
|
24
31
|
],
|
|
25
32
|
"license": "MIT",
|
|
26
33
|
"homepage": "https://github.com/donfear/inup#readme",
|
|
@@ -36,24 +43,20 @@
|
|
|
36
43
|
"README.md"
|
|
37
44
|
],
|
|
38
45
|
"devDependencies": {
|
|
39
|
-
"@types/
|
|
40
|
-
"@types/keypress.js": "^2.1.3",
|
|
41
|
-
"@types/node": "^24.12.0",
|
|
46
|
+
"@types/node": "^24.12.2",
|
|
42
47
|
"@types/semver": "^7.7.1",
|
|
43
|
-
"@vitest/coverage-v8": "^4.1.
|
|
44
|
-
"prettier": "^3.8.
|
|
45
|
-
"typescript": "^
|
|
46
|
-
"vitest": "^4.1.
|
|
48
|
+
"@vitest/coverage-v8": "^4.1.4",
|
|
49
|
+
"prettier": "^3.8.2",
|
|
50
|
+
"typescript": "^6.0.2",
|
|
51
|
+
"vitest": "^4.1.4"
|
|
47
52
|
},
|
|
48
53
|
"dependencies": {
|
|
49
54
|
"chalk": "^5.6.2",
|
|
50
55
|
"commander": "^14.0.3",
|
|
51
56
|
"env-paths": "^4.0.0",
|
|
52
|
-
"inquirer": "^13.3.2",
|
|
53
|
-
"keypress": "^0.2.1",
|
|
54
57
|
"nanospinner": "^1.2.2",
|
|
55
58
|
"semver": "^7.7.4",
|
|
56
|
-
"undici": "^
|
|
59
|
+
"undici": "^8.1.0"
|
|
57
60
|
},
|
|
58
61
|
"engines": {
|
|
59
62
|
"node": ">=20.0.0"
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.changelogFetcher = exports.ChangelogFetcher = void 0;
|
|
4
|
-
const constants_1 = require("../config/constants");
|
|
5
|
-
const jsdelivr_registry_1 = require("./jsdelivr-registry");
|
|
6
|
-
/**
|
|
7
|
-
* Fetches package metadata from npm registry
|
|
8
|
-
* Includes description, repository info, and basic metadata
|
|
9
|
-
*/
|
|
10
|
-
class ChangelogFetcher {
|
|
11
|
-
constructor() {
|
|
12
|
-
this.cache = new Map();
|
|
13
|
-
this.failureCache = new Set(); // Track packages that failed to fetch
|
|
14
|
-
this.inFlight = new Map();
|
|
15
|
-
}
|
|
16
|
-
getCacheKey(packageName, version) {
|
|
17
|
-
return `${packageName}@${version?.trim() || 'latest'}`;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Fetch package metadata from npm registry
|
|
21
|
-
* Uses a cached approach to avoid repeated requests
|
|
22
|
-
*/
|
|
23
|
-
async fetchPackageMetadata(packageName, version) {
|
|
24
|
-
const cacheKey = this.getCacheKey(packageName, version);
|
|
25
|
-
// Check if we already have this in cache
|
|
26
|
-
if (this.cache.has(cacheKey)) {
|
|
27
|
-
return this.cache.get(cacheKey);
|
|
28
|
-
}
|
|
29
|
-
// Check if we already failed to fetch this
|
|
30
|
-
if (this.failureCache.has(cacheKey)) {
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
const inFlight = this.inFlight.get(cacheKey);
|
|
34
|
-
if (inFlight) {
|
|
35
|
-
return await inFlight;
|
|
36
|
-
}
|
|
37
|
-
const lookupPromise = this.fetchAndCachePackageMetadata(packageName, version).finally(() => {
|
|
38
|
-
this.inFlight.delete(cacheKey);
|
|
39
|
-
});
|
|
40
|
-
this.inFlight.set(cacheKey, lookupPromise);
|
|
41
|
-
return await lookupPromise;
|
|
42
|
-
}
|
|
43
|
-
async fetchAndCachePackageMetadata(packageName, version) {
|
|
44
|
-
const cacheKey = this.getCacheKey(packageName, version);
|
|
45
|
-
try {
|
|
46
|
-
const response = await this.fetchPackageManifest(packageName, version);
|
|
47
|
-
if (!response) {
|
|
48
|
-
this.failureCache.add(cacheKey);
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
const repository = response.repository;
|
|
52
|
-
const bugs = response.bugs;
|
|
53
|
-
const keywords = Array.isArray(response.keywords) ? response.keywords : [];
|
|
54
|
-
const author = typeof response.author === 'object' && response.author !== null
|
|
55
|
-
? (response.author.name ?? response.author)
|
|
56
|
-
: response.author;
|
|
57
|
-
const repositoryUrl = this.extractRepositoryUrl(repository?.url || '');
|
|
58
|
-
const npmUrl = `https://www.npmjs.com/package/${encodeURIComponent(packageName)}`;
|
|
59
|
-
const issuesUrl = repositoryUrl ? `${repositoryUrl}/issues` : undefined;
|
|
60
|
-
const metadata = {
|
|
61
|
-
description: typeof response.description === 'string' && response.description
|
|
62
|
-
? response.description
|
|
63
|
-
: 'No description available',
|
|
64
|
-
homepage: typeof response.homepage === 'string' ? response.homepage : undefined,
|
|
65
|
-
repository,
|
|
66
|
-
bugs,
|
|
67
|
-
keywords,
|
|
68
|
-
author: typeof author === 'string' ? author : undefined,
|
|
69
|
-
license: typeof response.license === 'string' ? response.license : undefined,
|
|
70
|
-
repositoryUrl,
|
|
71
|
-
npmUrl,
|
|
72
|
-
issuesUrl,
|
|
73
|
-
};
|
|
74
|
-
// Try to extract release notes/changelog info
|
|
75
|
-
if (repositoryUrl) {
|
|
76
|
-
metadata.releaseNotes = `${repositoryUrl}/releases`;
|
|
77
|
-
}
|
|
78
|
-
// Try to get weekly download count
|
|
79
|
-
try {
|
|
80
|
-
const downloadsData = await this.fetchDownloadStats(packageName);
|
|
81
|
-
if (downloadsData) {
|
|
82
|
-
metadata.weeklyDownloads = downloadsData.downloads;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
catch {
|
|
86
|
-
// Ignore download stats errors - optional data
|
|
87
|
-
}
|
|
88
|
-
this.cache.set(cacheKey, metadata);
|
|
89
|
-
return metadata;
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
// Cache the failure to avoid retrying
|
|
93
|
-
this.failureCache.add(cacheKey);
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Fetch metadata from a lightweight manifest endpoint.
|
|
99
|
-
*/
|
|
100
|
-
async fetchPackageManifest(packageName, version) {
|
|
101
|
-
try {
|
|
102
|
-
const normalizedVersion = version?.trim();
|
|
103
|
-
if (normalizedVersion) {
|
|
104
|
-
const jsdelivrManifest = await (0, jsdelivr_registry_1.fetchExactPackageManifest)(packageName, normalizedVersion);
|
|
105
|
-
if (jsdelivrManifest) {
|
|
106
|
-
return jsdelivrManifest;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
const npmPath = normalizedVersion ? normalizedVersion : 'latest';
|
|
110
|
-
const response = await fetch(`${constants_1.NPM_REGISTRY_URL}/${encodeURIComponent(packageName)}/${encodeURIComponent(npmPath)}`, {
|
|
111
|
-
method: 'GET',
|
|
112
|
-
headers: {
|
|
113
|
-
accept: 'application/json',
|
|
114
|
-
},
|
|
115
|
-
});
|
|
116
|
-
if (!response.ok) {
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
return (await response.json());
|
|
120
|
-
}
|
|
121
|
-
catch {
|
|
122
|
-
return null;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Extract GitHub URL from repository URL for easier access to releases
|
|
127
|
-
*/
|
|
128
|
-
extractRepositoryUrl(repoUrl) {
|
|
129
|
-
if (!repoUrl)
|
|
130
|
-
return '';
|
|
131
|
-
// Handle various repository URL formats
|
|
132
|
-
// git+https://github.com/user/repo.git -> https://github.com/user/repo/releases
|
|
133
|
-
// https://github.com/user/repo.git -> https://github.com/user/repo/releases
|
|
134
|
-
// github:user/repo -> https://github.com/user/repo/releases
|
|
135
|
-
let cleanUrl = repoUrl
|
|
136
|
-
.replace(/^git\+/, '') // Remove git+ prefix
|
|
137
|
-
.replace(/\.git$/, '') // Remove .git suffix
|
|
138
|
-
.replace(/^github:/, 'https://github.com/'); // Convert github: format
|
|
139
|
-
// Ensure it's a proper URL
|
|
140
|
-
if (!cleanUrl.startsWith('http')) {
|
|
141
|
-
cleanUrl = 'https://github.com/' + cleanUrl;
|
|
142
|
-
}
|
|
143
|
-
return cleanUrl;
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Fetch weekly download statistics from npm
|
|
147
|
-
*/
|
|
148
|
-
async fetchDownloadStats(packageName) {
|
|
149
|
-
try {
|
|
150
|
-
const response = await fetch(`https://api.npmjs.org/downloads/point/last-week/${encodeURIComponent(packageName)}`, {
|
|
151
|
-
method: 'GET',
|
|
152
|
-
headers: {
|
|
153
|
-
accept: 'application/json',
|
|
154
|
-
},
|
|
155
|
-
});
|
|
156
|
-
if (!response.ok) {
|
|
157
|
-
return null;
|
|
158
|
-
}
|
|
159
|
-
const data = (await response.json());
|
|
160
|
-
return {
|
|
161
|
-
downloads: data.downloads || 0,
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
catch {
|
|
165
|
-
return null;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Get repository release URL for a package
|
|
170
|
-
*/
|
|
171
|
-
getRepositoryReleaseUrl(packageName, version) {
|
|
172
|
-
const metadata = this.cache.get(this.getCacheKey(packageName, version)) ??
|
|
173
|
-
this.cache.get(this.getCacheKey(packageName)) ??
|
|
174
|
-
this.cache.get(packageName);
|
|
175
|
-
if (!metadata || !metadata.releaseNotes) {
|
|
176
|
-
return null;
|
|
177
|
-
}
|
|
178
|
-
return `${metadata.releaseNotes}/releases/tag/v${version}`;
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* Cache package metadata directly (used by utils to avoid duplicate fetches)
|
|
182
|
-
*/
|
|
183
|
-
cacheMetadata(packageName, rawData) {
|
|
184
|
-
const repositoryUrl = this.extractRepositoryUrl(rawData.repository?.url || '');
|
|
185
|
-
const npmUrl = `https://www.npmjs.com/package/${encodeURIComponent(packageName)}`;
|
|
186
|
-
const issuesUrl = repositoryUrl ? `${repositoryUrl}/issues` : undefined;
|
|
187
|
-
const metadata = {
|
|
188
|
-
description: rawData.description || 'No description available',
|
|
189
|
-
homepage: rawData.homepage,
|
|
190
|
-
repository: rawData.repository,
|
|
191
|
-
bugs: rawData.bugs,
|
|
192
|
-
keywords: rawData.keywords || [],
|
|
193
|
-
author: rawData.author?.name || rawData.author,
|
|
194
|
-
license: rawData.license,
|
|
195
|
-
repositoryUrl,
|
|
196
|
-
npmUrl,
|
|
197
|
-
issuesUrl,
|
|
198
|
-
};
|
|
199
|
-
if (repositoryUrl) {
|
|
200
|
-
metadata.releaseNotes = `${repositoryUrl}/releases`;
|
|
201
|
-
}
|
|
202
|
-
this.cache.set(packageName, metadata);
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Clear the cache (useful for testing)
|
|
206
|
-
*/
|
|
207
|
-
clearCache() {
|
|
208
|
-
this.cache.clear();
|
|
209
|
-
this.failureCache.clear();
|
|
210
|
-
this.inFlight.clear();
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
exports.ChangelogFetcher = ChangelogFetcher;
|
|
214
|
-
exports.changelogFetcher = new ChangelogFetcher();
|
|
215
|
-
//# sourceMappingURL=changelog-fetcher.js.map
|