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.
Files changed (46) hide show
  1. package/dist/cli.js +2 -1
  2. package/dist/config/project-config.js +6 -0
  3. package/dist/core/package-detector.js +3 -2
  4. package/dist/core/upgrade-runner.js +6 -3
  5. package/dist/features/changelog/clients/github-client.js +134 -0
  6. package/dist/features/changelog/clients/npm-registry-client.js +53 -0
  7. package/dist/features/changelog/index.js +19 -0
  8. package/dist/features/changelog/parsers/changelog-parser.js +68 -0
  9. package/dist/features/changelog/parsers/github-release-html-parser.js +61 -0
  10. package/dist/features/changelog/parsers/package-metadata.js +34 -0
  11. package/dist/features/changelog/parsers/repository-ref.js +26 -0
  12. package/dist/features/changelog/services/changelog-service.js +30 -0
  13. package/dist/features/changelog/services/package-metadata-service.js +108 -0
  14. package/dist/features/changelog/services/release-notes-service.js +180 -0
  15. package/dist/features/changelog/types/changelog.types.js +3 -0
  16. package/dist/interactive-ui.js +242 -114
  17. package/dist/services/background-audit.js +60 -0
  18. package/dist/services/index.js +3 -1
  19. package/dist/services/vulnerability-checker.js +133 -0
  20. package/dist/ui/controllers/index.js +8 -0
  21. package/dist/ui/controllers/package-info-modal-controller.js +237 -0
  22. package/dist/ui/controllers/vulnerability-audit-controller.js +82 -0
  23. package/dist/ui/index.js +3 -0
  24. package/dist/ui/input-handler.js +40 -9
  25. package/dist/ui/modal/index.js +22 -0
  26. package/dist/ui/modal/layout.js +84 -0
  27. package/dist/ui/modal/package-info-sections.js +327 -0
  28. package/dist/ui/modal/package-info.js +147 -0
  29. package/dist/ui/modal/theme-selector.js +46 -0
  30. package/dist/ui/modal/types.js +3 -0
  31. package/dist/ui/presenters/index.js +11 -0
  32. package/dist/ui/presenters/vulnerability.js +76 -0
  33. package/dist/ui/renderer/index.js +9 -11
  34. package/dist/ui/renderer/package-list.js +135 -62
  35. package/dist/ui/state/filter-manager.js +17 -2
  36. package/dist/ui/state/modal-manager.js +48 -6
  37. package/dist/ui/state/state-manager.js +42 -7
  38. package/dist/ui/utils/cursor.js +18 -0
  39. package/dist/ui/utils/index.js +8 -1
  40. package/dist/ui/utils/terminal-input.js +82 -0
  41. package/dist/ui/utils/text.js +75 -0
  42. package/dist/ui/utils/version.js +3 -2
  43. package/package.json +7 -11
  44. package/dist/services/changelog-fetcher.js +0 -215
  45. package/dist/ui/renderer/modal.js +0 -190
  46. 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.batchSizes = [10, 15, 20, 25];
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
- batchSizes: this.batchSizes,
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
- console.clear();
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(/&amp;/g, '&')
48
+ .replace(/&lt;/g, '<')
49
+ .replace(/&gt;/g, '>')
50
+ .replace(/&quot;/g, '"')
51
+ .replace(/&#39;/g, "'")
52
+ .replace(/&nbsp;/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