inup 1.4.12 → 1.5.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/dist/cli.js +2 -1
- 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 +82 -0
- package/dist/ui/utils/text.js +75 -0
- package/dist/ui/utils/version.js +3 -2
- package/package.json +7 -11
- 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
package/dist/cli.js
CHANGED
|
@@ -25,7 +25,6 @@ program
|
|
|
25
25
|
.option('--package-manager <name>', 'manually specify package manager (npm, yarn, pnpm, bun)')
|
|
26
26
|
.option('--debug', 'write verbose debug log to /tmp/inup-debug-YYYY-MM-DD.log')
|
|
27
27
|
.action(async (options) => {
|
|
28
|
-
console.log(chalk_1.default.bold.blue(`🚀 `) + chalk_1.default.bold.red(`i`) + chalk_1.default.bold.yellow(`n`) + chalk_1.default.bold.blue(`u`) + chalk_1.default.bold.magenta(`p`) + `\n`);
|
|
29
28
|
const cwd = (0, path_1.resolve)(options.dir);
|
|
30
29
|
if (options.debug || process.env.INUP_DEBUG === '1') {
|
|
31
30
|
(0, utils_1.enableDebugLogging)();
|
|
@@ -73,6 +72,8 @@ program
|
|
|
73
72
|
maxDepth,
|
|
74
73
|
ignorePackages,
|
|
75
74
|
packageManager,
|
|
75
|
+
showPeerDependencyVulnerabilities: projectConfig.showPeerDependencyVulnerabilities ?? false,
|
|
76
|
+
showOptionalDependencyVulnerabilities: projectConfig.showOptionalDependencyVulnerabilities ?? false,
|
|
76
77
|
debug: options.debug || process.env.INUP_DEBUG === '1',
|
|
77
78
|
});
|
|
78
79
|
await upgrader.run();
|
|
@@ -49,6 +49,12 @@ function normalizeConfig(config) {
|
|
|
49
49
|
normalized.exclude = config.exclude.filter((item) => typeof item === 'string');
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
+
if (typeof config.showPeerDependencyVulnerabilities === 'boolean') {
|
|
53
|
+
normalized.showPeerDependencyVulnerabilities = config.showPeerDependencyVulnerabilities;
|
|
54
|
+
}
|
|
55
|
+
if (typeof config.showOptionalDependencyVulnerabilities === 'boolean') {
|
|
56
|
+
normalized.showOptionalDependencyVulnerabilities = config.showOptionalDependencyVulnerabilities;
|
|
57
|
+
}
|
|
52
58
|
return normalized;
|
|
53
59
|
}
|
|
54
60
|
/**
|
|
@@ -44,7 +44,7 @@ class PackageDetector {
|
|
|
44
44
|
constructor(options) {
|
|
45
45
|
this.packageJsonPath = null;
|
|
46
46
|
this.packageJson = null;
|
|
47
|
-
this.
|
|
47
|
+
this.batchSize = 25;
|
|
48
48
|
this.batchConcurrency = 5;
|
|
49
49
|
this.cwd = options?.cwd || process.cwd();
|
|
50
50
|
this.excludePatterns = options?.excludePatterns || [];
|
|
@@ -115,7 +115,7 @@ class PackageDetector {
|
|
|
115
115
|
},
|
|
116
116
|
});
|
|
117
117
|
}, prepared.currentVersions, {
|
|
118
|
-
|
|
118
|
+
batchSize: this.batchSize,
|
|
119
119
|
concurrency: this.batchConcurrency,
|
|
120
120
|
});
|
|
121
121
|
utils_3.debugLog.perf('PackageDetector', `registry fetch (${resolved}/${prepared.uniquePackages.length} resolved)`, tFetch);
|
|
@@ -245,6 +245,7 @@ class PackageDetector {
|
|
|
245
245
|
isOutdated,
|
|
246
246
|
hasRangeUpdate,
|
|
247
247
|
hasMajorUpdate,
|
|
248
|
+
allVersions,
|
|
248
249
|
};
|
|
249
250
|
}
|
|
250
251
|
catch (error) {
|
|
@@ -9,6 +9,7 @@ const package_detector_1 = require("./package-detector");
|
|
|
9
9
|
const interactive_ui_1 = require("../interactive-ui");
|
|
10
10
|
const upgrader_1 = require("./upgrader");
|
|
11
11
|
const package_manager_detector_1 = require("../services/package-manager-detector");
|
|
12
|
+
const utils_1 = require("../ui/utils");
|
|
12
13
|
/**
|
|
13
14
|
* Main orchestrator for the inup upgrade process
|
|
14
15
|
*/
|
|
@@ -24,7 +25,10 @@ class UpgradeRunner {
|
|
|
24
25
|
this.packageManager = package_manager_detector_1.PackageManagerDetector.detect(cwd);
|
|
25
26
|
}
|
|
26
27
|
this.detector = new package_detector_1.PackageDetector(options);
|
|
27
|
-
this.ui = new interactive_ui_1.InteractiveUI(this.packageManager
|
|
28
|
+
this.ui = new interactive_ui_1.InteractiveUI(this.packageManager, {
|
|
29
|
+
showPeerDependencyVulnerabilities: options?.showPeerDependencyVulnerabilities ?? false,
|
|
30
|
+
showOptionalDependencyVulnerabilities: options?.showOptionalDependencyVulnerabilities ?? false,
|
|
31
|
+
});
|
|
28
32
|
this.upgrader = new upgrader_1.PackageUpgrader(this.packageManager);
|
|
29
33
|
}
|
|
30
34
|
async run() {
|
|
@@ -114,8 +118,7 @@ class UpgradeRunner {
|
|
|
114
118
|
shouldProceed = await this.ui.confirmUpgrade(selectedChoices);
|
|
115
119
|
if (shouldProceed === null) {
|
|
116
120
|
// User pressed N or ESC - go back to selection with current selections preserved
|
|
117
|
-
|
|
118
|
-
console.log(chalk_1.default.bold.blue('🚀 inup\n'));
|
|
121
|
+
utils_1.ConsoleUtils.clearProgress();
|
|
119
122
|
selectedChoices = progress.isLoading
|
|
120
123
|
? await this.ui.selectPackagesToUpgradeProgressive(selectionStates, progress, (refresh) => {
|
|
121
124
|
refreshUI = refresh;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GitHubClient = void 0;
|
|
4
|
+
const repository_ref_1 = require("../parsers/repository-ref");
|
|
5
|
+
const GITHUB_RELEASES_PAGE_LIMIT = 3;
|
|
6
|
+
class GitHubClient {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.releasesCache = new Map();
|
|
9
|
+
this.rawChangelogCache = new Map();
|
|
10
|
+
}
|
|
11
|
+
clearCache() {
|
|
12
|
+
this.releasesCache.clear();
|
|
13
|
+
this.rawChangelogCache.clear();
|
|
14
|
+
}
|
|
15
|
+
async fetchReleaseByTag(repoUrl, tag, signal) {
|
|
16
|
+
const repo = (0, repository_ref_1.parseGitHubRepo)(repoUrl);
|
|
17
|
+
if (!repo)
|
|
18
|
+
return null;
|
|
19
|
+
try {
|
|
20
|
+
const response = await fetch(`https://api.github.com/repos/${repo.owner}/${repo.repo}/releases/tags/${tag}`, {
|
|
21
|
+
method: 'GET',
|
|
22
|
+
headers: {
|
|
23
|
+
accept: 'application/vnd.github.v3+json',
|
|
24
|
+
'user-agent': 'inup-cli',
|
|
25
|
+
},
|
|
26
|
+
signal,
|
|
27
|
+
});
|
|
28
|
+
if (!response.ok)
|
|
29
|
+
return null;
|
|
30
|
+
const data = (await response.json());
|
|
31
|
+
return data.body?.trim() ? data.body.trim() : null;
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async fetchReleasePageHtml(repoUrl, tag, signal) {
|
|
41
|
+
const repo = (0, repository_ref_1.parseGitHubRepo)(repoUrl);
|
|
42
|
+
if (!repo)
|
|
43
|
+
return null;
|
|
44
|
+
try {
|
|
45
|
+
const response = await fetch(`https://github.com/${repo.owner}/${repo.repo}/releases/tag/${tag}`, {
|
|
46
|
+
method: 'GET',
|
|
47
|
+
signal,
|
|
48
|
+
});
|
|
49
|
+
if (!response.ok)
|
|
50
|
+
return null;
|
|
51
|
+
return await response.text();
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async fetchReleases(repoUrl, signal) {
|
|
61
|
+
const repo = (0, repository_ref_1.parseGitHubRepo)(repoUrl);
|
|
62
|
+
if (!repo)
|
|
63
|
+
return null;
|
|
64
|
+
const cacheKey = `${repo.owner}/${repo.repo}`;
|
|
65
|
+
if (this.releasesCache.has(cacheKey)) {
|
|
66
|
+
return this.releasesCache.get(cacheKey);
|
|
67
|
+
}
|
|
68
|
+
const releases = [];
|
|
69
|
+
for (let page = 1; page <= GITHUB_RELEASES_PAGE_LIMIT; page += 1) {
|
|
70
|
+
try {
|
|
71
|
+
const response = await fetch(`https://api.github.com/repos/${repo.owner}/${repo.repo}/releases?per_page=100&page=${page}`, {
|
|
72
|
+
method: 'GET',
|
|
73
|
+
headers: {
|
|
74
|
+
accept: 'application/vnd.github.v3+json',
|
|
75
|
+
'user-agent': 'inup-cli',
|
|
76
|
+
},
|
|
77
|
+
signal,
|
|
78
|
+
});
|
|
79
|
+
if (!response.ok)
|
|
80
|
+
break;
|
|
81
|
+
const pageReleases = (await response.json());
|
|
82
|
+
if (!Array.isArray(pageReleases) || pageReleases.length === 0)
|
|
83
|
+
break;
|
|
84
|
+
releases.push(...pageReleases);
|
|
85
|
+
if (pageReleases.length < 100)
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const result = releases.length > 0 ? releases : null;
|
|
96
|
+
this.releasesCache.set(cacheKey, result);
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
async fetchRawChangelog(repoUrl, signal) {
|
|
100
|
+
const repo = (0, repository_ref_1.parseGitHubRepo)(repoUrl);
|
|
101
|
+
if (!repo)
|
|
102
|
+
return null;
|
|
103
|
+
const cacheKey = `${repo.owner}/${repo.repo}`;
|
|
104
|
+
if (this.rawChangelogCache.has(cacheKey)) {
|
|
105
|
+
return this.rawChangelogCache.get(cacheKey);
|
|
106
|
+
}
|
|
107
|
+
const branches = ['main', 'master'];
|
|
108
|
+
const filenames = ['CHANGELOG.md', 'CHANGES.md', 'HISTORY.md'];
|
|
109
|
+
for (const branch of branches) {
|
|
110
|
+
for (const filename of filenames) {
|
|
111
|
+
try {
|
|
112
|
+
const response = await fetch(`https://raw.githubusercontent.com/${repo.owner}/${repo.repo}/${branch}/${filename}`, {
|
|
113
|
+
method: 'GET',
|
|
114
|
+
signal,
|
|
115
|
+
});
|
|
116
|
+
if (response.ok) {
|
|
117
|
+
const text = await response.text();
|
|
118
|
+
this.rawChangelogCache.set(cacheKey, text);
|
|
119
|
+
return text;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
this.rawChangelogCache.set(cacheKey, null);
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
exports.GitHubClient = GitHubClient;
|
|
134
|
+
//# sourceMappingURL=github-client.js.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NpmRegistryClient = void 0;
|
|
4
|
+
const constants_1 = require("../../../config/constants");
|
|
5
|
+
class NpmRegistryClient {
|
|
6
|
+
async fetchPackageManifest(packageName, version, signal) {
|
|
7
|
+
try {
|
|
8
|
+
const response = await fetch(`${constants_1.NPM_REGISTRY_URL}/${encodeURIComponent(packageName)}/${encodeURIComponent(version)}`, {
|
|
9
|
+
method: 'GET',
|
|
10
|
+
headers: {
|
|
11
|
+
accept: 'application/json',
|
|
12
|
+
},
|
|
13
|
+
signal,
|
|
14
|
+
});
|
|
15
|
+
if (!response.ok) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
return (await response.json());
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
22
|
+
throw error;
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async fetchDownloadStats(packageName, signal) {
|
|
28
|
+
try {
|
|
29
|
+
const response = await fetch(`https://api.npmjs.org/downloads/point/last-week/${encodeURIComponent(packageName)}`, {
|
|
30
|
+
method: 'GET',
|
|
31
|
+
headers: {
|
|
32
|
+
accept: 'application/json',
|
|
33
|
+
},
|
|
34
|
+
signal,
|
|
35
|
+
});
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const data = (await response.json());
|
|
40
|
+
return {
|
|
41
|
+
downloads: data.downloads || 0,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.NpmRegistryClient = NpmRegistryClient;
|
|
53
|
+
//# sourceMappingURL=npm-registry-client.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./types/changelog.types"), exports);
|
|
18
|
+
__exportStar(require("./services/changelog-service"), exports);
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,68 @@
|
|
|
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.normalizeReleaseTag = normalizeReleaseTag;
|
|
37
|
+
exports.extractVersionSection = extractVersionSection;
|
|
38
|
+
const semver = __importStar(require("semver"));
|
|
39
|
+
function normalizeReleaseTag(tagName) {
|
|
40
|
+
if (!tagName)
|
|
41
|
+
return null;
|
|
42
|
+
const cleanedTag = semver.clean(tagName);
|
|
43
|
+
if (cleanedTag)
|
|
44
|
+
return cleanedTag;
|
|
45
|
+
const semverMatch = tagName.match(/\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?(?:\+[0-9A-Za-z-.]+)?/);
|
|
46
|
+
if (!semverMatch)
|
|
47
|
+
return null;
|
|
48
|
+
return semver.clean(semverMatch[0]);
|
|
49
|
+
}
|
|
50
|
+
function extractVersionSection(changelog, version) {
|
|
51
|
+
const escapedVersion = version.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
52
|
+
const sectionRegex = new RegExp(`^##\\s+\\[?v?${escapedVersion}\\]?[^\\n]*\\n`, 'm');
|
|
53
|
+
const match = sectionRegex.exec(changelog);
|
|
54
|
+
if (!match)
|
|
55
|
+
return null;
|
|
56
|
+
const startIndex = match.index + match[0].length;
|
|
57
|
+
const nextSectionMatch = /^## /m.exec(changelog.slice(startIndex));
|
|
58
|
+
const endIndex = nextSectionMatch ? startIndex + nextSectionMatch.index : changelog.length;
|
|
59
|
+
const section = changelog.slice(startIndex, endIndex).trim();
|
|
60
|
+
if (section.length === 0)
|
|
61
|
+
return null;
|
|
62
|
+
const lines = section.split('\n');
|
|
63
|
+
if (lines.length > 100) {
|
|
64
|
+
return `${lines.slice(0, 100).join('\n')}\n...`;
|
|
65
|
+
}
|
|
66
|
+
return section;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=changelog-parser.js.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractReleaseNotesFromHtml = extractReleaseNotesFromHtml;
|
|
4
|
+
function extractReleaseNotesFromHtml(html) {
|
|
5
|
+
const bodyContentIndex = html.indexOf('data-test-selector="body-content"');
|
|
6
|
+
if (bodyContentIndex === -1)
|
|
7
|
+
return null;
|
|
8
|
+
const markdownBodyIndex = html.indexOf('class="markdown-body', bodyContentIndex);
|
|
9
|
+
if (markdownBodyIndex === -1)
|
|
10
|
+
return null;
|
|
11
|
+
const contentStart = html.indexOf('>', markdownBodyIndex);
|
|
12
|
+
if (contentStart === -1)
|
|
13
|
+
return null;
|
|
14
|
+
let depth = 1;
|
|
15
|
+
let cursor = contentStart + 1;
|
|
16
|
+
while (depth > 0 && cursor < html.length) {
|
|
17
|
+
const nextOpen = html.indexOf('<div', cursor);
|
|
18
|
+
const nextClose = html.indexOf('</div>', cursor);
|
|
19
|
+
if (nextClose === -1)
|
|
20
|
+
return null;
|
|
21
|
+
if (nextOpen !== -1 && nextOpen < nextClose) {
|
|
22
|
+
depth += 1;
|
|
23
|
+
cursor = nextOpen + 4;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
depth -= 1;
|
|
27
|
+
cursor = nextClose + 6;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (depth !== 0)
|
|
31
|
+
return null;
|
|
32
|
+
const normalized = html
|
|
33
|
+
.slice(contentStart + 1, cursor - 6)
|
|
34
|
+
.replace(/<svg[\s\S]*?<\/svg>/g, '')
|
|
35
|
+
.replace(/<h([1-6])[^>]*>/g, (_full, level) => `${'#'.repeat(Number(level))} `)
|
|
36
|
+
.replace(/<\/h[1-6]>/g, '\n\n')
|
|
37
|
+
.replace(/<li[^>]*>/g, '- ')
|
|
38
|
+
.replace(/<\/li>/g, '\n')
|
|
39
|
+
.replace(/<p[^>]*>/g, '')
|
|
40
|
+
.replace(/<\/p>/g, '\n\n')
|
|
41
|
+
.replace(/<br\s*\/?>/g, '\n')
|
|
42
|
+
.replace(/<a\b[^>]*>([\s\S]*?)<\/a>/g, '$1')
|
|
43
|
+
.replace(/<strong[^>]*>([\s\S]*?)<\/strong>/g, '**$1**')
|
|
44
|
+
.replace(/<code[^>]*>([\s\S]*?)<\/code>/g, '`$1`')
|
|
45
|
+
.replace(/<[^>]+>/g, '');
|
|
46
|
+
const decoded = normalized
|
|
47
|
+
.replace(/&/g, '&')
|
|
48
|
+
.replace(/</g, '<')
|
|
49
|
+
.replace(/>/g, '>')
|
|
50
|
+
.replace(/"/g, '"')
|
|
51
|
+
.replace(/'/g, "'")
|
|
52
|
+
.replace(/ /g, ' ');
|
|
53
|
+
const cleaned = decoded
|
|
54
|
+
.split('\n')
|
|
55
|
+
.map((line) => line.trimEnd())
|
|
56
|
+
.join('\n')
|
|
57
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
58
|
+
.trim();
|
|
59
|
+
return cleaned.length > 0 ? cleaned : null;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=github-release-html-parser.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mapPackageManifestToMetadata = mapPackageManifestToMetadata;
|
|
4
|
+
const repository_ref_1 = require("./repository-ref");
|
|
5
|
+
function mapPackageManifestToMetadata(packageName, rawData) {
|
|
6
|
+
const repository = rawData.repository;
|
|
7
|
+
const bugs = rawData.bugs;
|
|
8
|
+
const keywords = Array.isArray(rawData.keywords) ? rawData.keywords : [];
|
|
9
|
+
const author = typeof rawData.author === 'object' && rawData.author !== null
|
|
10
|
+
? (rawData.author.name ?? rawData.author)
|
|
11
|
+
: rawData.author;
|
|
12
|
+
const repositoryUrl = (0, repository_ref_1.extractRepositoryUrl)(repository?.url || '');
|
|
13
|
+
const npmUrl = `https://www.npmjs.com/package/${encodeURIComponent(packageName)}`;
|
|
14
|
+
const issuesUrl = repositoryUrl ? `${repositoryUrl}/issues` : undefined;
|
|
15
|
+
const metadata = {
|
|
16
|
+
description: typeof rawData.description === 'string' && rawData.description
|
|
17
|
+
? rawData.description
|
|
18
|
+
: 'No description available',
|
|
19
|
+
homepage: typeof rawData.homepage === 'string' ? rawData.homepage : undefined,
|
|
20
|
+
repository,
|
|
21
|
+
bugs,
|
|
22
|
+
keywords,
|
|
23
|
+
author: typeof author === 'string' ? author : undefined,
|
|
24
|
+
license: typeof rawData.license === 'string' ? rawData.license : undefined,
|
|
25
|
+
repositoryUrl,
|
|
26
|
+
npmUrl,
|
|
27
|
+
issuesUrl,
|
|
28
|
+
};
|
|
29
|
+
if (repositoryUrl) {
|
|
30
|
+
metadata.releaseNotes = `${repositoryUrl}/releases`;
|
|
31
|
+
}
|
|
32
|
+
return metadata;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=package-metadata.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractRepositoryUrl = extractRepositoryUrl;
|
|
4
|
+
exports.parseGitHubRepo = parseGitHubRepo;
|
|
5
|
+
function extractRepositoryUrl(repoUrl) {
|
|
6
|
+
if (!repoUrl)
|
|
7
|
+
return '';
|
|
8
|
+
let cleanUrl = repoUrl
|
|
9
|
+
.replace(/^git\+/, '')
|
|
10
|
+
.replace(/\.git$/, '')
|
|
11
|
+
.replace(/^github:/, 'https://github.com/');
|
|
12
|
+
if (!cleanUrl.startsWith('http')) {
|
|
13
|
+
cleanUrl = `https://github.com/${cleanUrl}`;
|
|
14
|
+
}
|
|
15
|
+
return cleanUrl;
|
|
16
|
+
}
|
|
17
|
+
function parseGitHubRepo(repoUrl) {
|
|
18
|
+
const match = repoUrl.match(/github\.com\/([^/]+)\/([^/]+)/);
|
|
19
|
+
if (!match)
|
|
20
|
+
return null;
|
|
21
|
+
return {
|
|
22
|
+
owner: match[1],
|
|
23
|
+
repo: match[2],
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=repository-ref.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.changelogFetcher = exports.ChangelogFetcher = void 0;
|
|
4
|
+
const package_metadata_service_1 = require("./package-metadata-service");
|
|
5
|
+
const release_notes_service_1 = require("./release-notes-service");
|
|
6
|
+
class ChangelogFetcher {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.metadataService = new package_metadata_service_1.PackageMetadataService();
|
|
9
|
+
this.releaseNotesService = new release_notes_service_1.ReleaseNotesService(this.metadataService);
|
|
10
|
+
}
|
|
11
|
+
async fetchPackageMetadata(packageName, version, signal) {
|
|
12
|
+
return await this.metadataService.fetchPackageMetadata(packageName, version, signal);
|
|
13
|
+
}
|
|
14
|
+
getRepositoryReleaseUrl(packageName, version) {
|
|
15
|
+
return this.metadataService.getRepositoryReleaseUrl(packageName, version);
|
|
16
|
+
}
|
|
17
|
+
cacheMetadata(packageName, rawData) {
|
|
18
|
+
this.metadataService.cacheMetadata(packageName, rawData);
|
|
19
|
+
}
|
|
20
|
+
async fetchReleaseNotesForVersion(packageName, version, signal) {
|
|
21
|
+
return await this.releaseNotesService.fetchReleaseNotesForVersion(packageName, version, signal);
|
|
22
|
+
}
|
|
23
|
+
clearCache() {
|
|
24
|
+
this.metadataService.clearCache();
|
|
25
|
+
this.releaseNotesService.clearCache();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.ChangelogFetcher = ChangelogFetcher;
|
|
29
|
+
exports.changelogFetcher = new ChangelogFetcher();
|
|
30
|
+
//# sourceMappingURL=changelog-service.js.map
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PackageMetadataService = void 0;
|
|
4
|
+
const npm_registry_client_1 = require("../clients/npm-registry-client");
|
|
5
|
+
const package_metadata_1 = require("../parsers/package-metadata");
|
|
6
|
+
const jsdelivr_registry_1 = require("../../../services/jsdelivr-registry");
|
|
7
|
+
class PackageMetadataService {
|
|
8
|
+
constructor(npmRegistryClient = new npm_registry_client_1.NpmRegistryClient(), exactManifestFetcher = jsdelivr_registry_1.fetchExactPackageManifest) {
|
|
9
|
+
this.npmRegistryClient = npmRegistryClient;
|
|
10
|
+
this.exactManifestFetcher = exactManifestFetcher;
|
|
11
|
+
this.cache = new Map();
|
|
12
|
+
this.inFlight = new Map();
|
|
13
|
+
}
|
|
14
|
+
clearCache() {
|
|
15
|
+
this.cache.clear();
|
|
16
|
+
this.inFlight.clear();
|
|
17
|
+
}
|
|
18
|
+
getCached(packageName, version) {
|
|
19
|
+
for (const key of [this.getCacheKey(packageName, version), this.getCacheKey(packageName), packageName]) {
|
|
20
|
+
if (this.cache.has(key)) {
|
|
21
|
+
return this.cache.get(key) ?? null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
async fetchPackageMetadata(packageName, version, signal) {
|
|
27
|
+
const cacheKey = this.getCacheKey(packageName, version);
|
|
28
|
+
if (this.cache.has(cacheKey)) {
|
|
29
|
+
return this.cache.get(cacheKey) ?? null;
|
|
30
|
+
}
|
|
31
|
+
const inFlight = this.inFlight.get(cacheKey);
|
|
32
|
+
if (inFlight) {
|
|
33
|
+
return await inFlight;
|
|
34
|
+
}
|
|
35
|
+
const lookupPromise = this.fetchAndCachePackageMetadata(packageName, version, signal).finally(() => {
|
|
36
|
+
this.inFlight.delete(cacheKey);
|
|
37
|
+
});
|
|
38
|
+
this.inFlight.set(cacheKey, lookupPromise);
|
|
39
|
+
return await lookupPromise;
|
|
40
|
+
}
|
|
41
|
+
cacheMetadata(packageName, rawData) {
|
|
42
|
+
const metadata = this.buildMetadata(packageName, rawData);
|
|
43
|
+
this.cache.set(packageName, metadata);
|
|
44
|
+
}
|
|
45
|
+
getRepositoryReleaseUrl(packageName, version) {
|
|
46
|
+
const metadata = this.getCached(packageName, version);
|
|
47
|
+
if (!metadata?.releaseNotes) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return `${metadata.releaseNotes}/tag/v${version}`;
|
|
51
|
+
}
|
|
52
|
+
getCacheKey(packageName, version) {
|
|
53
|
+
return `${packageName}@${version?.trim() || 'latest'}`;
|
|
54
|
+
}
|
|
55
|
+
cachePackageMetadata(packageName, cacheKey, metadata) {
|
|
56
|
+
this.cache.set(cacheKey, metadata);
|
|
57
|
+
this.cache.set(this.getCacheKey(packageName), metadata);
|
|
58
|
+
this.cache.set(packageName, metadata);
|
|
59
|
+
}
|
|
60
|
+
async fetchAndCachePackageMetadata(packageName, version, signal) {
|
|
61
|
+
const cacheKey = this.getCacheKey(packageName, version);
|
|
62
|
+
try {
|
|
63
|
+
signal?.throwIfAborted();
|
|
64
|
+
const manifest = await this.fetchPackageManifest(packageName, version, signal);
|
|
65
|
+
if (!manifest) {
|
|
66
|
+
this.cache.set(cacheKey, null);
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const metadata = (0, package_metadata_1.mapPackageManifestToMetadata)(packageName, manifest);
|
|
70
|
+
try {
|
|
71
|
+
signal?.throwIfAborted();
|
|
72
|
+
const downloadsData = await this.npmRegistryClient.fetchDownloadStats(packageName, signal);
|
|
73
|
+
if (downloadsData) {
|
|
74
|
+
metadata.weeklyDownloads = downloadsData.downloads;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Optional data should not fail metadata hydration.
|
|
79
|
+
}
|
|
80
|
+
this.cachePackageMetadata(packageName, cacheKey, metadata);
|
|
81
|
+
return metadata;
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
this.cache.set(cacheKey, null);
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async fetchPackageManifest(packageName, version, signal) {
|
|
92
|
+
signal?.throwIfAborted();
|
|
93
|
+
const normalizedVersion = version?.trim();
|
|
94
|
+
if (normalizedVersion) {
|
|
95
|
+
const jsdelivrManifest = await this.exactManifestFetcher(packageName, normalizedVersion);
|
|
96
|
+
if (jsdelivrManifest) {
|
|
97
|
+
return jsdelivrManifest;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
signal?.throwIfAborted();
|
|
101
|
+
return await this.npmRegistryClient.fetchPackageManifest(packageName, normalizedVersion || 'latest', signal);
|
|
102
|
+
}
|
|
103
|
+
buildMetadata(packageName, rawData) {
|
|
104
|
+
return (0, package_metadata_1.mapPackageManifestToMetadata)(packageName, rawData);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.PackageMetadataService = PackageMetadataService;
|
|
108
|
+
//# sourceMappingURL=package-metadata-service.js.map
|