inup 1.5.5 → 1.6.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 (75) hide show
  1. package/README.md +53 -10
  2. package/dist/cli.js +53 -31
  3. package/dist/config/constants.js +4 -6
  4. package/dist/config/package-meta.js +10 -0
  5. package/dist/config/project-config.js +11 -1
  6. package/dist/core/package-detector.js +37 -5
  7. package/dist/core/upgrade-runner.js +8 -10
  8. package/dist/core/upgrader.js +15 -11
  9. package/dist/features/changelog/clients/github-client.js +5 -6
  10. package/dist/features/changelog/services/changelog-service.js +2 -4
  11. package/dist/features/changelog/services/package-metadata-service.js +7 -23
  12. package/dist/features/changelog/services/release-notes-service.js +4 -29
  13. package/dist/features/debug/services/performance-tracker.js +6 -8
  14. package/dist/features/headless/headless-runner.js +53 -0
  15. package/dist/features/headless/index.js +27 -0
  16. package/dist/features/headless/report.js +64 -0
  17. package/dist/features/headless/types.js +6 -0
  18. package/dist/features/headless/vulnerability-audit.js +106 -0
  19. package/dist/index.js +1 -2
  20. package/dist/interactive-ui.js +15 -606
  21. package/dist/services/background-audit.js +3 -5
  22. package/dist/services/cache-manager.js +5 -7
  23. package/dist/services/http/inflight.js +22 -0
  24. package/dist/services/http/retry.js +30 -0
  25. package/dist/services/index.js +0 -1
  26. package/dist/services/npm-registry.js +20 -97
  27. package/dist/services/package-manager-detector.js +6 -3
  28. package/dist/services/persistent-cache.js +8 -13
  29. package/dist/types/domain.js +3 -0
  30. package/dist/types/streaming.js +3 -0
  31. package/dist/types/ui.js +3 -0
  32. package/dist/types.js +17 -0
  33. package/dist/ui/controllers/package-info-modal-controller.js +22 -31
  34. package/dist/ui/controllers/vulnerability-audit-controller.js +17 -8
  35. package/dist/ui/index.js +3 -0
  36. package/dist/ui/input-handler.js +58 -75
  37. package/dist/ui/keymap.js +208 -0
  38. package/dist/ui/modal/package-info-sections/release-notes.js +161 -0
  39. package/dist/ui/modal/package-info-sections/sections.js +174 -0
  40. package/dist/ui/modal/package-info-sections/text.js +75 -0
  41. package/dist/ui/modal/package-info-sections.js +15 -369
  42. package/dist/ui/modal/package-info.js +1 -1
  43. package/dist/ui/presenters/health.js +24 -0
  44. package/dist/ui/renderer/help-modal.js +75 -0
  45. package/dist/ui/renderer/index.js +2 -5
  46. package/dist/ui/renderer/package-list/interface.js +184 -0
  47. package/dist/ui/renderer/package-list/rows.js +177 -0
  48. package/dist/ui/renderer/package-list.js +15 -455
  49. package/dist/ui/session/action-dispatcher.js +233 -0
  50. package/dist/ui/session/index.js +13 -0
  51. package/dist/ui/session/interactive-session.js +280 -0
  52. package/dist/ui/session/selection-state-builder.js +149 -0
  53. package/dist/ui/state/filter-manager.js +17 -6
  54. package/dist/ui/state/modal-manager.js +35 -0
  55. package/dist/ui/state/navigation-manager.js +33 -4
  56. package/dist/ui/state/state-manager.js +68 -3
  57. package/dist/ui/state/theme-manager.js +1 -0
  58. package/dist/ui/themes-colors.js +16 -4
  59. package/dist/ui/themes.js +11 -19
  60. package/dist/ui/utils/cursor.js +12 -7
  61. package/dist/ui/utils/index.js +6 -1
  62. package/dist/ui/utils/text.js +3 -2
  63. package/dist/ui/utils/version.js +60 -86
  64. package/dist/utils/color.js +38 -0
  65. package/dist/utils/config.js +13 -1
  66. package/dist/utils/engines.js +63 -0
  67. package/dist/utils/filesystem/io.js +103 -0
  68. package/dist/utils/filesystem/paths.js +19 -0
  69. package/dist/utils/filesystem/scan.js +280 -0
  70. package/dist/utils/filesystem.js +17 -335
  71. package/dist/utils/index.js +3 -0
  72. package/dist/utils/manifest.js +35 -0
  73. package/dist/utils/version.js +38 -1
  74. package/package.json +8 -7
  75. package/dist/services/jsdelivr-registry.js +0 -395
@@ -1,338 +1,20 @@
1
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
+ };
2
16
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.findPackageJson = findPackageJson;
4
- exports.findWorkspaceRoot = findWorkspaceRoot;
5
- exports.readPackageJson = readPackageJson;
6
- exports.readPackageJsonAsync = readPackageJsonAsync;
7
- exports.collectAllDependencies = collectAllDependencies;
8
- exports.collectAllDependenciesAsync = collectAllDependenciesAsync;
9
- exports.findAllPackageJsonFiles = findAllPackageJsonFiles;
10
- exports.findAllPackageJsonFilesAsync = findAllPackageJsonFilesAsync;
11
- const fs_1 = require("fs");
12
- const fs_2 = require("fs");
13
- const path_1 = require("path");
14
- const package_manager_detector_1 = require("../services/package-manager-detector");
15
- /**
16
- * Find package.json in the current working directory
17
- */
18
- function findPackageJson(cwd = process.cwd()) {
19
- const packageJsonPath = (0, path_1.join)(cwd, 'package.json');
20
- return (0, fs_1.existsSync)(packageJsonPath) ? packageJsonPath : null;
21
- }
22
- /**
23
- * Find the workspace root by detecting package manager and checking for workspace configuration
24
- * @param cwd - Current working directory
25
- * @param packageManager - Optional package manager to use (will auto-detect if not provided)
26
- */
27
- function findWorkspaceRoot(cwd = process.cwd(), packageManager) {
28
- // Auto-detect if not provided
29
- if (!packageManager) {
30
- const detected = package_manager_detector_1.PackageManagerDetector.detect(cwd);
31
- packageManager = detected.name;
32
- }
33
- return package_manager_detector_1.PackageManagerDetector.findWorkspaceRoot(cwd, packageManager);
34
- }
35
- /**
36
- * Read and parse a package.json file
37
- */
38
- function readPackageJson(path) {
39
- try {
40
- const content = (0, fs_1.readFileSync)(path, 'utf-8');
41
- return JSON.parse(content);
42
- }
43
- catch (error) {
44
- throw new Error(`Failed to read package.json: ${error}`);
45
- }
46
- }
47
- /**
48
- * Read and parse a package.json file asynchronously
49
- */
50
- async function readPackageJsonAsync(path) {
51
- try {
52
- const content = await fs_2.promises.readFile(path, 'utf-8');
53
- return JSON.parse(content);
54
- }
55
- catch (error) {
56
- throw new Error(`Failed to read package.json: ${error}`);
57
- }
58
- }
59
- const SKIP_DIRS = new Set([
60
- 'node_modules',
61
- 'dist',
62
- 'build',
63
- 'coverage',
64
- 'out',
65
- 'lib',
66
- 'es',
67
- 'esm',
68
- 'cjs',
69
- ]);
70
- function shouldSkipDirectory(name) {
71
- return name.startsWith('.') || SKIP_DIRS.has(name);
72
- }
73
- /**
74
- * Collects all dependencies from multiple package.json files.
75
- * Always includes regular dependencies and devDependencies.
76
- * Optionally includes peer and optional dependencies based on flags.
77
- */
78
- function collectAllDependencies(packageJsonFiles, options = {}) {
79
- const allDeps = [];
80
- for (const packageJsonPath of packageJsonFiles) {
81
- try {
82
- const packageJson = readPackageJson(packageJsonPath);
83
- const depTypes = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];
84
- for (const depType of depTypes) {
85
- const deps = packageJson[depType];
86
- if (deps && typeof deps === 'object') {
87
- for (const [name, version] of Object.entries(deps)) {
88
- allDeps.push({
89
- name,
90
- version: version,
91
- type: depType,
92
- packageJsonPath,
93
- });
94
- }
95
- }
96
- }
97
- }
98
- catch (error) {
99
- // Skip malformed package.json files
100
- }
101
- }
102
- return allDeps;
103
- }
104
- /**
105
- * Collects all dependencies from multiple package.json files asynchronously.
106
- * Reads all package.json files in parallel for better performance.
107
- * Always includes regular dependencies and devDependencies.
108
- * Optionally includes peer and optional dependencies based on flags.
109
- */
110
- async function collectAllDependenciesAsync(packageJsonFiles, options = {}) {
111
- // Read all package.json files in parallel
112
- const packageJsonPromises = packageJsonFiles.map(async (packageJsonPath) => {
113
- try {
114
- const packageJson = await readPackageJsonAsync(packageJsonPath);
115
- return { packageJson, packageJsonPath };
116
- }
117
- catch (error) {
118
- // Skip malformed package.json files
119
- return null;
120
- }
121
- });
122
- const results = await Promise.all(packageJsonPromises);
123
- // Collect dependencies from all successfully read package.json files
124
- const allDeps = [];
125
- for (const result of results) {
126
- if (!result)
127
- continue;
128
- const { packageJson, packageJsonPath } = result;
129
- const depTypes = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];
130
- for (const depType of depTypes) {
131
- const deps = packageJson[depType];
132
- if (deps && typeof deps === 'object') {
133
- for (const [name, version] of Object.entries(deps)) {
134
- allDeps.push({
135
- name,
136
- version: version,
137
- type: depType,
138
- packageJsonPath,
139
- });
140
- }
141
- }
142
- }
143
- }
144
- return allDeps;
145
- }
146
- /**
147
- * Find all package.json files recursively
148
- */
149
- function findAllPackageJsonFiles(rootDir = process.cwd(), excludePatterns = [], maxDepth = 10, onProgress) {
150
- const packageJsonFiles = [];
151
- const visitedPaths = new Set();
152
- let directoriesScanned = 0;
153
- let lastProgressAt = 0;
154
- const progressIntervalMs = 250;
155
- // Compile regex patterns for exclude filtering
156
- const excludeRegexes = excludePatterns.map((pattern) => new RegExp(pattern, 'i'));
157
- function shouldExcludePath(relativePath) {
158
- return excludeRegexes.some((regex) => regex.test(relativePath));
159
- }
160
- function reportProgress(currentDir, force = false) {
161
- if (!onProgress)
162
- return;
163
- const now = Date.now();
164
- if (!force && now - lastProgressAt < progressIntervalMs) {
165
- return;
166
- }
167
- lastProgressAt = now;
168
- const relativePath = (0, path_1.relative)(rootDir, currentDir) || '.';
169
- onProgress(relativePath, packageJsonFiles.length);
170
- }
171
- function traverseDirectory(dir, depth = 0) {
172
- // Prevent infinite recursion with depth limit
173
- if (depth > maxDepth) {
174
- return;
175
- }
176
- try {
177
- // Prevent symlink cycles by tracking visited real paths
178
- const realPath = (0, fs_1.realpathSync)(dir);
179
- if (visitedPaths.has(realPath)) {
180
- return;
181
- }
182
- visitedPaths.add(realPath);
183
- directoriesScanned++;
184
- // Report progress every 10 directories or on first scan
185
- if (onProgress && (directoriesScanned % 10 === 0 || directoriesScanned === 1)) {
186
- reportProgress(dir, true);
187
- }
188
- const files = (0, fs_1.readdirSync)(dir);
189
- for (const file of files) {
190
- reportProgress(dir);
191
- const fullPath = (0, path_1.join)(dir, file);
192
- const relativePath = (0, path_1.relative)(rootDir, fullPath);
193
- // Skip if path matches exclude patterns
194
- if (shouldExcludePath(relativePath)) {
195
- continue;
196
- }
197
- let stat;
198
- try {
199
- stat = (0, fs_1.statSync)(fullPath);
200
- }
201
- catch {
202
- // Skip files/dirs we can't stat (broken symlinks, permission issues)
203
- continue;
204
- }
205
- if (stat.isDirectory() && !shouldSkipDirectory(file)) {
206
- traverseDirectory(fullPath, depth + 1);
207
- }
208
- else if (file === 'package.json' && stat.isFile()) {
209
- packageJsonFiles.push(fullPath);
210
- }
211
- }
212
- }
213
- catch (error) {
214
- // Skip directories that can't be read (permission issues, etc.)
215
- }
216
- }
217
- traverseDirectory(rootDir);
218
- return packageJsonFiles;
219
- }
220
- /**
221
- * Find all package.json files recursively with bounded parallel directory traversal.
222
- */
223
- async function findAllPackageJsonFilesAsync(rootDir = process.cwd(), excludePatterns = [], maxDepth = 10, onProgress, options = {}) {
224
- const packageJsonFiles = [];
225
- const visitedPaths = new Set();
226
- let directoriesScanned = 0;
227
- let lastProgressAt = 0;
228
- const progressIntervalMs = 250;
229
- const concurrency = Math.max(1, Math.min(options.concurrency ?? 16, 64));
230
- const excludeRegexes = excludePatterns.map((pattern) => new RegExp(pattern, 'i'));
231
- function shouldExcludePath(relativePath) {
232
- return excludeRegexes.some((regex) => regex.test(relativePath));
233
- }
234
- function reportProgress(currentDir, force = false) {
235
- if (!onProgress)
236
- return;
237
- const now = Date.now();
238
- if (!force && now - lastProgressAt < progressIntervalMs) {
239
- return;
240
- }
241
- lastProgressAt = now;
242
- const relativePath = (0, path_1.relative)(rootDir, currentDir) || '.';
243
- onProgress(relativePath, packageJsonFiles.length);
244
- }
245
- const pending = [];
246
- let activeTasks = 0;
247
- let failedError = null;
248
- let resolveDone = null;
249
- let rejectDone = null;
250
- const done = new Promise((resolve, reject) => {
251
- resolveDone = resolve;
252
- rejectDone = reject;
253
- });
254
- function finishIfIdle() {
255
- if (pending.length === 0 && activeTasks === 0) {
256
- resolveDone?.();
257
- }
258
- }
259
- function schedule(dir, depth) {
260
- pending.push({ dir, depth });
261
- pump();
262
- }
263
- async function processDirectory(dir, depth) {
264
- if (depth > maxDepth) {
265
- return;
266
- }
267
- let realPath;
268
- try {
269
- realPath = await fs_2.promises.realpath(dir);
270
- }
271
- catch {
272
- return;
273
- }
274
- if (visitedPaths.has(realPath)) {
275
- return;
276
- }
277
- visitedPaths.add(realPath);
278
- directoriesScanned++;
279
- if (directoriesScanned % 10 === 0 || directoriesScanned === 1) {
280
- reportProgress(dir, true);
281
- }
282
- let files;
283
- try {
284
- files = await fs_2.promises.readdir(dir);
285
- }
286
- catch {
287
- return;
288
- }
289
- for (const file of files) {
290
- reportProgress(dir);
291
- const fullPath = (0, path_1.join)(dir, file);
292
- const relativePath = (0, path_1.relative)(rootDir, fullPath);
293
- if (shouldExcludePath(relativePath)) {
294
- continue;
295
- }
296
- let stat;
297
- try {
298
- stat = await fs_2.promises.stat(fullPath);
299
- }
300
- catch {
301
- continue;
302
- }
303
- if (stat.isDirectory() && !shouldSkipDirectory(file)) {
304
- schedule(fullPath, depth + 1);
305
- }
306
- else if (file === 'package.json' && stat.isFile()) {
307
- packageJsonFiles.push(fullPath);
308
- }
309
- }
310
- }
311
- function pump() {
312
- while (activeTasks < concurrency && pending.length > 0 && !failedError) {
313
- const next = pending.shift();
314
- if (!next)
315
- break;
316
- activeTasks++;
317
- void processDirectory(next.dir, next.depth)
318
- .catch((error) => {
319
- if (!failedError) {
320
- failedError = error;
321
- rejectDone?.(error);
322
- }
323
- })
324
- .finally(() => {
325
- activeTasks--;
326
- if (failedError) {
327
- return;
328
- }
329
- pump();
330
- finishIfIdle();
331
- });
332
- }
333
- }
334
- schedule(rootDir, 0);
335
- await done;
336
- return packageJsonFiles;
337
- }
17
+ __exportStar(require("./filesystem/paths"), exports);
18
+ __exportStar(require("./filesystem/io"), exports);
19
+ __exportStar(require("./filesystem/scan"), exports);
338
20
  //# sourceMappingURL=filesystem.js.map
@@ -23,6 +23,9 @@ __exportStar(require("./exec"), exports);
23
23
  __exportStar(require("./git"), exports);
24
24
  __exportStar(require("./version"), exports);
25
25
  __exportStar(require("./debug-logger"), exports);
26
+ __exportStar(require("./color"), exports);
27
+ __exportStar(require("./engines"), exports);
28
+ __exportStar(require("./manifest"), exports);
26
29
  // Re-export async functions for convenience
27
30
  var filesystem_1 = require("./filesystem");
28
31
  Object.defineProperty(exports, "readPackageJsonAsync", { enumerable: true, get: function () { return filesystem_1.readPackageJsonAsync; } });
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ /**
3
+ * Helpers for reading optional health signals out of an npm version manifest
4
+ * (the per-version object inside an abbreviated packument, or a full version
5
+ * document). Both fields are advisory and frequently absent.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.normalizeDeprecatedMessage = normalizeDeprecatedMessage;
9
+ exports.extractEnginesNode = extractEnginesNode;
10
+ /**
11
+ * npm represents deprecation as either a string message or the boolean `true`.
12
+ * Normalize both into a displayable message, or `undefined` when not deprecated.
13
+ */
14
+ function normalizeDeprecatedMessage(value) {
15
+ if (typeof value === 'string' && value.trim()) {
16
+ return value;
17
+ }
18
+ if (value === true) {
19
+ return 'This version is deprecated.';
20
+ }
21
+ return undefined;
22
+ }
23
+ /**
24
+ * Extract the `engines.node` range from a manifest's `engines` object, if any.
25
+ */
26
+ function extractEnginesNode(engines) {
27
+ if (typeof engines === 'object' && engines !== null) {
28
+ const node = engines.node;
29
+ if (typeof node === 'string' && node.trim()) {
30
+ return node;
31
+ }
32
+ }
33
+ return undefined;
34
+ }
35
+ //# sourceMappingURL=manifest.js.map
@@ -33,10 +33,47 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.extractMajorVersion = extractMajorVersion;
37
+ exports.toComparableVersion = toComparableVersion;
38
+ exports.versionIdentity = versionIdentity;
39
+ exports.parseVersions = parseVersions;
36
40
  exports.isVersionOutdated = isVersionOutdated;
37
41
  exports.getOptimizedRangeVersion = getOptimizedRangeVersion;
38
42
  exports.findClosestMinorVersion = findClosestMinorVersion;
39
43
  const semver = __importStar(require("semver"));
44
+ const manifest_1 = require("./manifest");
45
+ function extractMajorVersion(version) {
46
+ if (!version)
47
+ return null;
48
+ const coerced = semver.coerce(version);
49
+ if (!coerced)
50
+ return null;
51
+ return semver.major(coerced).toString();
52
+ }
53
+ function toComparableVersion(version) {
54
+ const validVersion = semver.valid(version);
55
+ if (validVersion)
56
+ return validVersion;
57
+ const coerced = semver.coerce(version);
58
+ return coerced ? coerced.version : null;
59
+ }
60
+ function versionIdentity(version) {
61
+ const comparable = toComparableVersion(version);
62
+ return comparable ?? `raw:${version}`;
63
+ }
64
+ function parseVersions(raw) {
65
+ const data = JSON.parse(raw);
66
+ const versions = data.versions || {};
67
+ const allVersions = Object.keys(versions).filter((v) => /^[0-9]+\.[0-9]+\.[0-9]+$/.test(v));
68
+ const sortedVersions = allVersions.sort(semver.rcompare);
69
+ const latestVersion = sortedVersions.length > 0 ? sortedVersions[0] : 'unknown';
70
+ // Surface health signals for the latest version straight from the abbreviated
71
+ // packument we already fetched — no extra request. Both fields are optional.
72
+ const latestManifest = versions[latestVersion];
73
+ const deprecated = (0, manifest_1.normalizeDeprecatedMessage)(latestManifest?.deprecated);
74
+ const enginesNode = (0, manifest_1.extractEnginesNode)(latestManifest?.engines);
75
+ return { latestVersion, allVersions, deprecated, enginesNode };
76
+ }
40
77
  /**
41
78
  * Checks if a version is outdated compared to the latest version.
42
79
  * Handles version prefixes (^, ~, >=, etc.) by coercing them to valid semver.
@@ -55,7 +92,7 @@ function isVersionOutdated(current, latest) {
55
92
  /**
56
93
  * Get the optimized range version for a package
57
94
  */
58
- function getOptimizedRangeVersion(packageName, currentRange, allVersions, latestVersion) {
95
+ function getOptimizedRangeVersion(_packageName, currentRange, allVersions, latestVersion) {
59
96
  try {
60
97
  // Find the highest version that satisfies the current range
61
98
  const satisfyingVersions = allVersions.filter((version) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inup",
3
- "version": "1.5.5",
3
+ "version": "1.6.0",
4
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": {
@@ -43,19 +43,19 @@
43
43
  "README.md"
44
44
  ],
45
45
  "devDependencies": {
46
- "@types/node": "^24.12.4",
46
+ "@types/node": "^25.9.1",
47
47
  "@types/semver": "^7.7.1",
48
- "@vitest/coverage-v8": "^4.1.6",
48
+ "@vitest/coverage-v8": "^4.1.7",
49
49
  "prettier": "^3.8.3",
50
50
  "typescript": "^6.0.3",
51
- "vitest": "^4.1.6"
51
+ "vitest": "^4.1.7"
52
52
  },
53
53
  "dependencies": {
54
54
  "chalk": "^5.6.2",
55
- "commander": "^14.0.3",
55
+ "commander": "^15.0.0",
56
56
  "env-paths": "^4.0.0",
57
57
  "nanospinner": "^1.2.2",
58
- "semver": "^7.8.0",
58
+ "semver": "^7.8.1",
59
59
  "undici": "^8.3.0"
60
60
  },
61
61
  "engines": {
@@ -75,6 +75,7 @@
75
75
  "demo:setup": "cd docs/demo-project && pnpm install",
76
76
  "test": "vitest run",
77
77
  "test:watch": "vitest",
78
- "test:coverage": "vitest run --coverage"
78
+ "test:coverage": "vitest run --coverage",
79
+ "docs:keys": "UPDATE_README=1 vitest run keymap-readme"
79
80
  }
80
81
  }