inup 1.6.7 → 1.6.8

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 (66) hide show
  1. package/README.md +9 -2
  2. package/dist/app/interactive-ui.js +17 -15
  3. package/dist/app/upgrade-runner.js +11 -16
  4. package/dist/cli.js +12 -13
  5. package/dist/features/audit/headless-audit.js +4 -11
  6. package/dist/features/audit/index.js +9 -9
  7. package/dist/features/audit/presenter.js +0 -2
  8. package/dist/features/audit/vulnerability-audit-controller.js +3 -3
  9. package/dist/features/audit/vulnerability-checker.js +1 -5
  10. package/dist/features/changelog/clients/github-client.js +9 -7
  11. package/dist/features/changelog/index.js +1 -1
  12. package/dist/features/changelog/services/package-metadata-service.js +1 -1
  13. package/dist/features/changelog/services/release-notes-service.js +3 -2
  14. package/dist/features/debug/index.js +2 -2
  15. package/dist/features/debug/perf-logger.js +14 -14
  16. package/dist/features/headless/headless-runner.js +22 -9
  17. package/dist/features/headless/index.js +1 -1
  18. package/dist/features/interactive/index.js +10 -10
  19. package/dist/features/interactive/input-handler.js +3 -3
  20. package/dist/features/interactive/modal/index.js +1 -1
  21. package/dist/features/interactive/modal/layout.js +3 -3
  22. package/dist/features/interactive/modal/package-info-sections/index.js +1 -1
  23. package/dist/features/interactive/modal/package-info-sections/release-notes.js +1 -1
  24. package/dist/features/interactive/modal/package-info-sections/sections.js +5 -5
  25. package/dist/features/interactive/modal/package-info-sections/text.js +1 -1
  26. package/dist/features/interactive/modal/package-info.js +15 -18
  27. package/dist/features/interactive/renderer/confirmation.js +6 -3
  28. package/dist/features/interactive/renderer/help-modal.js +3 -3
  29. package/dist/features/interactive/renderer/index.js +3 -3
  30. package/dist/features/interactive/renderer/package-list/index.js +1 -1
  31. package/dist/features/interactive/renderer/package-list/interface.js +9 -9
  32. package/dist/features/interactive/renderer/package-list/rows.js +8 -9
  33. package/dist/features/interactive/renderer/performance-modal.js +2 -2
  34. package/dist/features/interactive/renderer/version-format.js +4 -4
  35. package/dist/features/interactive/session/action-dispatcher.js +2 -2
  36. package/dist/features/interactive/session/index.js +4 -4
  37. package/dist/features/interactive/session/interactive-session.js +9 -8
  38. package/dist/features/interactive/session/selection-state-builder.js +5 -4
  39. package/dist/features/interactive/state/filter-manager.js +1 -1
  40. package/dist/features/interactive/state/index.js +7 -7
  41. package/dist/features/interactive/state/state-manager.js +4 -4
  42. package/dist/features/interactive/state/theme-manager.js +4 -6
  43. package/dist/features/interactive/themes-colors.js +4 -5
  44. package/dist/features/upgrade/package-detector.js +30 -12
  45. package/dist/features/upgrade/upgrader.js +28 -37
  46. package/dist/shared/config/project-config.js +10 -11
  47. package/dist/shared/config/user-config.js +8 -8
  48. package/dist/shared/debug-logger.js +9 -9
  49. package/dist/shared/exec.js +4 -4
  50. package/dist/shared/fs/index.js +1 -1
  51. package/dist/shared/fs/io.js +3 -4
  52. package/dist/shared/fs/paths.js +4 -4
  53. package/dist/shared/fs/scan.js +21 -21
  54. package/dist/shared/git.js +3 -3
  55. package/dist/shared/http/resizable-semaphore.js +3 -1
  56. package/dist/shared/local-env.js +7 -7
  57. package/dist/shared/package-manager.js +25 -24
  58. package/dist/shared/registry/npm-registry.js +12 -10
  59. package/dist/shared/registry/version-checker.js +1 -1
  60. package/dist/shared/terminal/cursor.js +1 -1
  61. package/dist/shared/terminal/index.js +5 -5
  62. package/dist/shared/terminal/terminal-input.js +6 -2
  63. package/dist/shared/terminal/text.js +1 -1
  64. package/dist/shared/types/index.js +1 -1
  65. package/dist/shared/versions.js +37 -33
  66. package/package.json +10 -6
@@ -2,9 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.findAllPackageJsonFiles = findAllPackageJsonFiles;
4
4
  exports.findAllPackageJsonFilesAsync = findAllPackageJsonFilesAsync;
5
- const fs_1 = require("fs");
6
- const fs_2 = require("fs");
7
- const path_1 = require("path");
5
+ const node_fs_1 = require("node:fs");
6
+ const node_path_1 = require("node:path");
8
7
  /**
9
8
  * Normalize a relative path to forward slashes before matching `.inuprc` exclude patterns.
10
9
  * On Windows `path.relative` yields backslash separators (e.g. `packages\skipme`), but users write
@@ -55,12 +54,12 @@ function classifyDirectory(name, skipSet) {
55
54
  * layout). Stays shallow (depth 1) so detecting a skip doesn't re-walk the subtree we just pruned.
56
55
  */
57
56
  function prunedDirHoldsPackage(dir) {
58
- if ((0, fs_1.existsSync)((0, path_1.join)(dir, 'package.json'))) {
57
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)(dir, 'package.json'))) {
59
58
  return true;
60
59
  }
61
60
  let entries;
62
61
  try {
63
- entries = (0, fs_1.readdirSync)(dir);
62
+ entries = (0, node_fs_1.readdirSync)(dir);
64
63
  }
65
64
  catch {
66
65
  return false;
@@ -68,9 +67,9 @@ function prunedDirHoldsPackage(dir) {
68
67
  for (const entry of entries) {
69
68
  if (entry.startsWith('.'))
70
69
  continue;
71
- const child = (0, path_1.join)(dir, entry);
70
+ const child = (0, node_path_1.join)(dir, entry);
72
71
  try {
73
- if ((0, fs_1.statSync)(child).isDirectory() && (0, fs_1.existsSync)((0, path_1.join)(child, 'package.json'))) {
72
+ if ((0, node_fs_1.statSync)(child).isDirectory() && (0, node_fs_1.existsSync)((0, node_path_1.join)(child, 'package.json'))) {
74
73
  return true;
75
74
  }
76
75
  }
@@ -117,7 +116,7 @@ function findAllPackageJsonFiles(rootDir = process.cwd(), excludePatterns = [],
117
116
  return;
118
117
  }
119
118
  lastProgressAt = now;
120
- const relativePath = (0, path_1.relative)(rootDir, currentDir) || '.';
119
+ const relativePath = (0, node_path_1.relative)(rootDir, currentDir) || '.';
121
120
  onProgress(relativePath, packageJsonFiles.length);
122
121
  }
123
122
  function traverseDirectory(dir, depth = 0) {
@@ -126,7 +125,7 @@ function findAllPackageJsonFiles(rootDir = process.cwd(), excludePatterns = [],
126
125
  }
127
126
  try {
128
127
  // Prevent symlink cycles by tracking visited real paths
129
- const realPath = (0, fs_1.realpathSync)(dir);
128
+ const realPath = (0, node_fs_1.realpathSync)(dir);
130
129
  if (visitedPaths.has(realPath)) {
131
130
  return;
132
131
  }
@@ -136,17 +135,17 @@ function findAllPackageJsonFiles(rootDir = process.cwd(), excludePatterns = [],
136
135
  if (onProgress && (directoriesScanned % 10 === 0 || directoriesScanned === 1)) {
137
136
  reportProgress(dir, true);
138
137
  }
139
- const files = (0, fs_1.readdirSync)(dir);
138
+ const files = (0, node_fs_1.readdirSync)(dir);
140
139
  for (const file of files) {
141
140
  reportProgress(dir);
142
- const fullPath = (0, path_1.join)(dir, file);
143
- const relativePath = (0, path_1.relative)(rootDir, fullPath);
141
+ const fullPath = (0, node_path_1.join)(dir, file);
142
+ const relativePath = (0, node_path_1.relative)(rootDir, fullPath);
144
143
  if (shouldExcludePath(relativePath)) {
145
144
  continue;
146
145
  }
147
146
  let stat;
148
147
  try {
149
- stat = (0, fs_1.statSync)(fullPath);
148
+ stat = (0, node_fs_1.statSync)(fullPath);
150
149
  }
151
150
  catch {
152
151
  // Skip files/dirs we can't stat (broken symlinks, permission issues)
@@ -190,7 +189,7 @@ async function findAllPackageJsonFilesAsync(rootDir = process.cwd(), excludePatt
190
189
  return;
191
190
  }
192
191
  lastProgressAt = now;
193
- const relativePath = (0, path_1.relative)(rootDir, currentDir) || '.';
192
+ const relativePath = (0, node_path_1.relative)(rootDir, currentDir) || '.';
194
193
  onProgress(relativePath, packageJsonFiles.length);
195
194
  }
196
195
  const pending = [];
@@ -217,7 +216,7 @@ async function findAllPackageJsonFilesAsync(rootDir = process.cwd(), excludePatt
217
216
  }
218
217
  let realPath;
219
218
  try {
220
- realPath = await fs_2.promises.realpath(dir);
219
+ realPath = await node_fs_1.promises.realpath(dir);
221
220
  }
222
221
  catch {
223
222
  return;
@@ -232,21 +231,21 @@ async function findAllPackageJsonFilesAsync(rootDir = process.cwd(), excludePatt
232
231
  }
233
232
  let files;
234
233
  try {
235
- files = await fs_2.promises.readdir(dir);
234
+ files = await node_fs_1.promises.readdir(dir);
236
235
  }
237
236
  catch {
238
237
  return;
239
238
  }
240
239
  for (const file of files) {
241
240
  reportProgress(dir);
242
- const fullPath = (0, path_1.join)(dir, file);
243
- const relativePath = (0, path_1.relative)(rootDir, fullPath);
241
+ const fullPath = (0, node_path_1.join)(dir, file);
242
+ const relativePath = (0, node_path_1.relative)(rootDir, fullPath);
244
243
  if (shouldExcludePath(relativePath)) {
245
244
  continue;
246
245
  }
247
246
  let stat;
248
247
  try {
249
- stat = await fs_2.promises.stat(fullPath);
248
+ stat = await node_fs_1.promises.stat(fullPath);
250
249
  }
251
250
  catch {
252
251
  continue;
@@ -262,9 +261,10 @@ async function findAllPackageJsonFilesAsync(rootDir = process.cwd(), excludePatt
262
261
  }
263
262
  }
264
263
  function pump() {
265
- while (activeTasks < concurrency && pending.length > 0 && !failedError) {
266
- // Non-null: pending.length > 0 was just checked and nothing runs in between.
264
+ while (activeTasks < concurrency && !failedError) {
267
265
  const next = pending.shift();
266
+ if (!next)
267
+ break;
268
268
  activeTasks++;
269
269
  void processDirectory(next.dir, next.depth)
270
270
  .catch((error) => {
@@ -1,14 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getGitWorkingTreeState = getGitWorkingTreeState;
4
- const child_process_1 = require("child_process");
4
+ const node_child_process_1 = require("node:child_process");
5
5
  /**
6
6
  * Detect whether cwd is a git work tree and whether it has local changes.
7
7
  * Fail soft if git is unavailable or cwd is not a repository.
8
8
  */
9
9
  function getGitWorkingTreeState(cwd) {
10
10
  try {
11
- const isInsideWorkTree = (0, child_process_1.execSync)('git rev-parse --is-inside-work-tree', {
11
+ const isInsideWorkTree = (0, node_child_process_1.execSync)('git rev-parse --is-inside-work-tree', {
12
12
  cwd,
13
13
  encoding: 'utf-8',
14
14
  stdio: ['ignore', 'pipe', 'ignore'],
@@ -16,7 +16,7 @@ function getGitWorkingTreeState(cwd) {
16
16
  if (isInsideWorkTree !== 'true') {
17
17
  return { isRepo: false, isDirty: false };
18
18
  }
19
- const status = (0, child_process_1.execSync)('git status --porcelain', {
19
+ const status = (0, node_child_process_1.execSync)('git status --porcelain', {
20
20
  cwd,
21
21
  encoding: 'utf-8',
22
22
  stdio: ['ignore', 'pipe', 'ignore'],
@@ -60,8 +60,10 @@ class ResizableSemaphore {
60
60
  * each as it is admitted so the count stays authoritative and synchronous.
61
61
  */
62
62
  drainWaiters() {
63
- while (this.inFlight < this.limit && this.waiters.length > 0) {
63
+ while (this.inFlight < this.limit) {
64
64
  const next = this.waiters.shift();
65
+ if (!next)
66
+ break;
65
67
  this.inFlight++;
66
68
  next();
67
69
  }
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.loadInupLocalEnv = loadInupLocalEnv;
4
- const fs_1 = require("fs");
5
- const path_1 = require("path");
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
6
  /**
7
7
  * Loads a gitignored `.env.local` from the inup repo itself (NOT the cwd), so
8
8
  * developer-only toggles can be "set once" and apply to every `inup` run in any
@@ -21,15 +21,15 @@ const ENV_FILE_NAME = '.env.local';
21
21
  /** Walk upward from this file's dir to find the .env.local; null if none. */
22
22
  function findEnvFile() {
23
23
  let dir = __dirname;
24
- const root = (0, path_1.parse)(dir).root;
24
+ const root = (0, node_path_1.parse)(dir).root;
25
25
  // Cap the walk so a missing file can never loop unbounded.
26
26
  for (let i = 0; i < 12; i++) {
27
- const candidate = (0, path_1.join)(dir, ENV_FILE_NAME);
28
- if ((0, fs_1.existsSync)(candidate))
27
+ const candidate = (0, node_path_1.join)(dir, ENV_FILE_NAME);
28
+ if ((0, node_fs_1.existsSync)(candidate))
29
29
  return candidate;
30
30
  if (dir === root)
31
31
  break;
32
- dir = (0, path_1.dirname)(dir);
32
+ dir = (0, node_path_1.dirname)(dir);
33
33
  }
34
34
  return null;
35
35
  }
@@ -65,7 +65,7 @@ function loadInupLocalEnv() {
65
65
  const file = findEnvFile();
66
66
  if (!file)
67
67
  return null;
68
- const parsed = parseEnv((0, fs_1.readFileSync)(file, 'utf8'));
68
+ const parsed = parseEnv((0, node_fs_1.readFileSync)(file, 'utf8'));
69
69
  for (const [key, value] of Object.entries(parsed)) {
70
70
  if (process.env[key] === undefined) {
71
71
  process.env[key] = value;
@@ -4,8 +4,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.PackageManagerDetector = void 0;
7
- const fs_1 = require("fs");
8
- const path_1 = require("path");
7
+ const node_fs_1 = require("node:fs");
8
+ const node_path_1 = require("node:path");
9
9
  const chalk_1 = __importDefault(require("chalk"));
10
10
  const PACKAGE_MANAGERS = {
11
11
  npm: {
@@ -45,18 +45,19 @@ const PACKAGE_MANAGERS = {
45
45
  color: chalk_1.default.magenta,
46
46
  },
47
47
  };
48
+ // biome-ignore lint/complexity/noStaticOnlyClass: intentional namespace-style API used throughout the codebase
48
49
  class PackageManagerDetector {
49
50
  /**
50
51
  * Detect package manager from packageManager field or lock files
51
52
  */
52
53
  static detect(cwd = process.cwd()) {
53
54
  // 1. Check packageManager field in package.json
54
- const fromPackageJson = this.detectFromPackageJson(cwd);
55
+ const fromPackageJson = PackageManagerDetector.detectFromPackageJson(cwd);
55
56
  if (fromPackageJson) {
56
57
  return fromPackageJson;
57
58
  }
58
59
  // 2. Check for lock files
59
- const fromLockFile = this.detectFromLockFiles(cwd);
60
+ const fromLockFile = PackageManagerDetector.detectFromLockFiles(cwd);
60
61
  if (fromLockFile) {
61
62
  return fromLockFile;
62
63
  }
@@ -68,12 +69,12 @@ class PackageManagerDetector {
68
69
  * Detect from package.json packageManager field
69
70
  */
70
71
  static detectFromPackageJson(cwd) {
71
- const packageJsonPath = (0, path_1.join)(cwd, 'package.json');
72
- if (!(0, fs_1.existsSync)(packageJsonPath)) {
72
+ const packageJsonPath = (0, node_path_1.join)(cwd, 'package.json');
73
+ if (!(0, node_fs_1.existsSync)(packageJsonPath)) {
73
74
  return null;
74
75
  }
75
76
  try {
76
- const content = (0, fs_1.readFileSync)(packageJsonPath, 'utf-8');
77
+ const content = (0, node_fs_1.readFileSync)(packageJsonPath, 'utf-8');
77
78
  const packageJson = JSON.parse(content);
78
79
  if (packageJson.packageManager) {
79
80
  // Parse format: "pnpm@10.28.1" or "npm@9.0.0+sha512.abc..."
@@ -84,7 +85,7 @@ class PackageManagerDetector {
84
85
  }
85
86
  }
86
87
  }
87
- catch (error) {
88
+ catch {
88
89
  // Invalid package.json, continue to lock file detection
89
90
  }
90
91
  return null;
@@ -94,19 +95,19 @@ class PackageManagerDetector {
94
95
  */
95
96
  static detectFromLockFiles(cwd) {
96
97
  const lockFileChecks = [
97
- { pm: PACKAGE_MANAGERS.pnpm, path: (0, path_1.join)(cwd, 'pnpm-lock.yaml') },
98
- { pm: PACKAGE_MANAGERS.bun, path: (0, path_1.join)(cwd, 'bun.lockb') },
98
+ { pm: PACKAGE_MANAGERS.pnpm, path: (0, node_path_1.join)(cwd, 'pnpm-lock.yaml') },
99
+ { pm: PACKAGE_MANAGERS.bun, path: (0, node_path_1.join)(cwd, 'bun.lockb') },
99
100
  // Bun >= 1.2 writes a text `bun.lock` instead of the binary `bun.lockb`.
100
- { pm: PACKAGE_MANAGERS.bun, path: (0, path_1.join)(cwd, 'bun.lock') },
101
- { pm: PACKAGE_MANAGERS.yarn, path: (0, path_1.join)(cwd, 'yarn.lock') },
102
- { pm: PACKAGE_MANAGERS.npm, path: (0, path_1.join)(cwd, 'package-lock.json') },
101
+ { pm: PACKAGE_MANAGERS.bun, path: (0, node_path_1.join)(cwd, 'bun.lock') },
102
+ { pm: PACKAGE_MANAGERS.yarn, path: (0, node_path_1.join)(cwd, 'yarn.lock') },
103
+ { pm: PACKAGE_MANAGERS.npm, path: (0, node_path_1.join)(cwd, 'package-lock.json') },
103
104
  ];
104
105
  const existingLocks = lockFileChecks
105
- .filter(({ path }) => (0, fs_1.existsSync)(path))
106
+ .filter(({ path }) => (0, node_fs_1.existsSync)(path))
106
107
  .map(({ pm, path }) => ({
107
108
  pm,
108
109
  path,
109
- mtime: (0, fs_1.statSync)(path).mtime.getTime(),
110
+ mtime: (0, node_fs_1.statSync)(path).mtime.getTime(),
110
111
  }));
111
112
  if (existingLocks.length === 0) {
112
113
  return null;
@@ -131,20 +132,20 @@ class PackageManagerDetector {
131
132
  static findWorkspaceRoot(cwd = process.cwd(), packageManager) {
132
133
  const pmInfo = PACKAGE_MANAGERS[packageManager];
133
134
  let currentDir = cwd;
134
- while (currentDir !== (0, path_1.join)(currentDir, '..')) {
135
+ while (currentDir !== (0, node_path_1.join)(currentDir, '..')) {
135
136
  // Check for package manager-specific workspace file
136
137
  if (pmInfo.workspaceFile) {
137
- const workspaceFilePath = (0, path_1.join)(currentDir, pmInfo.workspaceFile);
138
- if ((0, fs_1.existsSync)(workspaceFilePath)) {
138
+ const workspaceFilePath = (0, node_path_1.join)(currentDir, pmInfo.workspaceFile);
139
+ if ((0, node_fs_1.existsSync)(workspaceFilePath)) {
139
140
  return currentDir;
140
141
  }
141
142
  }
142
143
  else {
143
144
  // Check for package.json with workspaces field
144
- const packageJsonPath = (0, path_1.join)(currentDir, 'package.json');
145
- if ((0, fs_1.existsSync)(packageJsonPath)) {
145
+ const packageJsonPath = (0, node_path_1.join)(currentDir, 'package.json');
146
+ if ((0, node_fs_1.existsSync)(packageJsonPath)) {
146
147
  try {
147
- const content = (0, fs_1.readFileSync)(packageJsonPath, 'utf-8');
148
+ const content = (0, node_fs_1.readFileSync)(packageJsonPath, 'utf-8');
148
149
  const packageJson = JSON.parse(content);
149
150
  // Check if workspaces field exists and is non-empty
150
151
  if (packageJson.workspaces) {
@@ -159,12 +160,12 @@ class PackageManagerDetector {
159
160
  }
160
161
  }
161
162
  }
162
- catch (error) {
163
+ catch {
163
164
  // Invalid package.json, continue searching
164
165
  }
165
166
  }
166
167
  }
167
- currentDir = (0, path_1.join)(currentDir, '..');
168
+ currentDir = (0, node_path_1.join)(currentDir, '..');
168
169
  }
169
170
  return null;
170
171
  }
@@ -172,7 +173,7 @@ class PackageManagerDetector {
172
173
  * Check if directory is in a workspace
173
174
  */
174
175
  static isInWorkspace(cwd, packageManager) {
175
- return this.findWorkspaceRoot(cwd, packageManager) !== null;
176
+ return PackageManagerDetector.findWorkspaceRoot(cwd, packageManager) !== null;
176
177
  }
177
178
  }
178
179
  exports.PackageManagerDetector = PackageManagerDetector;
@@ -2,20 +2,20 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.fetchPackageVersions = fetchPackageVersions;
4
4
  exports.clearPackageCache = clearPackageCache;
5
- const undici_1 = require("undici");
6
- const node_zlib_1 = require("node:zlib");
7
5
  const node_util_1 = require("node:util");
6
+ const node_zlib_1 = require("node:zlib");
7
+ const undici_1 = require("undici");
8
8
  const gunzipAsync = (0, node_util_1.promisify)(node_zlib_1.gunzip);
9
9
  const inflateAsync = (0, node_util_1.promisify)(node_zlib_1.inflate);
10
10
  const brotliDecompressAsync = (0, node_util_1.promisify)(node_zlib_1.brotliDecompress);
11
11
  const config_1 = require("../config");
12
- const registry_config_1 = require("./registry-config");
13
- const versions_1 = require("../versions");
14
- const retry_1 = require("../http/retry");
15
- const inflight_1 = require("../http/inflight");
16
- const resizable_semaphore_1 = require("../http/resizable-semaphore");
17
12
  const adaptive_controller_1 = require("../http/adaptive-controller");
18
13
  const etag_store_1 = require("../http/etag-store");
14
+ const inflight_1 = require("../http/inflight");
15
+ const resizable_semaphore_1 = require("../http/resizable-semaphore");
16
+ const retry_1 = require("../http/retry");
17
+ const versions_1 = require("../versions");
18
+ const registry_config_1 = require("./registry-config");
19
19
  const inFlightLookups = new inflight_1.InflightMap();
20
20
  // One pool per registry origin: scoped packages may resolve to different
21
21
  // registries (`@scope:registry` in .npmrc), and each origin keeps its own
@@ -79,7 +79,7 @@ async function attemptRegistryFetch(target, path) {
79
79
  'accept-encoding': 'gzip, deflate, br',
80
80
  };
81
81
  if (target.authHeader) {
82
- requestHeaders['authorization'] = target.authHeader;
82
+ requestHeaders.authorization = target.authHeader;
83
83
  }
84
84
  if (cached) {
85
85
  requestHeaders['if-none-match'] = cached.etag;
@@ -130,7 +130,7 @@ async function attemptRegistryFetch(target, path) {
130
130
  }
131
131
  const data = (0, versions_1.parseVersions)(decoded.toString('utf8'));
132
132
  // Persist the ETag for next run's conditional request.
133
- const etagHeader = headers['etag'];
133
+ const etagHeader = headers.etag;
134
134
  const etag = Array.isArray(etagHeader) ? etagHeader[0] : etagHeader;
135
135
  if (etag) {
136
136
  (0, etag_store_1.writeEtag)(cacheKey, etag.toString(), data);
@@ -232,8 +232,10 @@ async function fetchPackageVersions(packageNames, options = {}) {
232
232
  const pendingEmissions = new Map();
233
233
  let nextEmitIndex = 0;
234
234
  const flushPending = () => {
235
- while (pendingEmissions.has(nextEmitIndex)) {
235
+ while (true) {
236
236
  const ready = pendingEmissions.get(nextEmitIndex);
237
+ if (!ready)
238
+ break;
237
239
  pendingEmissions.delete(nextEmitIndex);
238
240
  options.onBatchReady?.(ready);
239
241
  nextEmitIndex++;
@@ -74,7 +74,7 @@ async function checkForUpdate(packageName, currentVersion) {
74
74
  updateCommand,
75
75
  };
76
76
  }
77
- catch (error) {
77
+ catch {
78
78
  // Silently fail - don't interrupt the user experience
79
79
  return null;
80
80
  }
@@ -82,7 +82,7 @@ exports.ConsoleUtils = {
82
82
  clearProgress() {
83
83
  if (!process.stderr.isTTY)
84
84
  return;
85
- process.stderr.write('\r' + ' '.repeat(exports.ConsoleUtils.LINE_WIDTH) + '\r');
85
+ process.stderr.write(`\r${' '.repeat(exports.ConsoleUtils.LINE_WIDTH)}\r`);
86
86
  },
87
87
  };
88
88
  //# sourceMappingURL=cursor.js.map
@@ -1,9 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.shouldDisableColor = exports.applyColorSetting = exports.wrapPlainText = exports.truncatePlainText = exports.stripAnsi = exports.getVisualLength = exports.TerminalInput = exports.RAW_SHOW_CURSOR = exports.RAW_EXIT_ALT_SCREEN = exports.ConsoleUtils = exports.CursorUtils = void 0;
3
+ exports.wrapPlainText = exports.truncatePlainText = exports.stripAnsi = exports.getVisualLength = exports.TerminalInput = exports.RAW_SHOW_CURSOR = exports.RAW_EXIT_ALT_SCREEN = exports.CursorUtils = exports.ConsoleUtils = exports.shouldDisableColor = exports.applyColorSetting = void 0;
4
+ var color_1 = require("./color");
5
+ Object.defineProperty(exports, "applyColorSetting", { enumerable: true, get: function () { return color_1.applyColorSetting; } });
6
+ Object.defineProperty(exports, "shouldDisableColor", { enumerable: true, get: function () { return color_1.shouldDisableColor; } });
4
7
  var cursor_1 = require("./cursor");
5
- Object.defineProperty(exports, "CursorUtils", { enumerable: true, get: function () { return cursor_1.CursorUtils; } });
6
8
  Object.defineProperty(exports, "ConsoleUtils", { enumerable: true, get: function () { return cursor_1.ConsoleUtils; } });
9
+ Object.defineProperty(exports, "CursorUtils", { enumerable: true, get: function () { return cursor_1.CursorUtils; } });
7
10
  Object.defineProperty(exports, "RAW_EXIT_ALT_SCREEN", { enumerable: true, get: function () { return cursor_1.RAW_EXIT_ALT_SCREEN; } });
8
11
  Object.defineProperty(exports, "RAW_SHOW_CURSOR", { enumerable: true, get: function () { return cursor_1.RAW_SHOW_CURSOR; } });
9
12
  var terminal_input_1 = require("./terminal-input");
@@ -13,7 +16,4 @@ Object.defineProperty(exports, "getVisualLength", { enumerable: true, get: funct
13
16
  Object.defineProperty(exports, "stripAnsi", { enumerable: true, get: function () { return text_1.stripAnsi; } });
14
17
  Object.defineProperty(exports, "truncatePlainText", { enumerable: true, get: function () { return text_1.truncatePlainText; } });
15
18
  Object.defineProperty(exports, "wrapPlainText", { enumerable: true, get: function () { return text_1.wrapPlainText; } });
16
- var color_1 = require("./color");
17
- Object.defineProperty(exports, "applyColorSetting", { enumerable: true, get: function () { return color_1.applyColorSetting; } });
18
- Object.defineProperty(exports, "shouldDisableColor", { enumerable: true, get: function () { return color_1.shouldDisableColor; } });
19
19
  //# sourceMappingURL=index.js.map
@@ -85,8 +85,12 @@ exports.TerminalInput = {
85
85
  promptForImmediateConfirmation(prompt, defaultValue = true) {
86
86
  return new Promise((resolve) => {
87
87
  process.stdout.write(prompt);
88
- // Replaced synchronously below before any keypress can call finish().
89
- let cleanup = /* v8 ignore next */ () => { };
88
+ // Replaced synchronously below before any keypress can call finish(),
89
+ // so the placeholder body is unreachable. (The `v8 ignore next` form
90
+ // is not honored by coverage-v8 here; the block form is.)
91
+ /* v8 ignore start */
92
+ let cleanup = () => { };
93
+ /* v8 ignore stop */
90
94
  const finish = (value) => {
91
95
  cleanup();
92
96
  process.stdout.write('\n');
@@ -7,10 +7,10 @@ exports.stripAnsi = stripAnsi;
7
7
  exports.getVisualLength = getVisualLength;
8
8
  exports.truncatePlainText = truncatePlainText;
9
9
  exports.wrapPlainText = wrapPlainText;
10
+ const cli_truncate_1 = __importDefault(require("cli-truncate"));
10
11
  const string_width_1 = __importDefault(require("string-width"));
11
12
  const strip_ansi_1 = __importDefault(require("strip-ansi"));
12
13
  const wrap_ansi_1 = __importDefault(require("wrap-ansi"));
13
- const cli_truncate_1 = __importDefault(require("cli-truncate"));
14
14
  // Thin wrappers over the battle-tested terminal-string stack (string-width,
15
15
  // strip-ansi, wrap-ansi, cli-truncate). The previous hand-rolled versions
16
16
  // handled emoji but had no East Asian Width tables, so CJK text was counted at
@@ -15,6 +15,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./domain"), exports);
18
- __exportStar(require("./ui"), exports);
19
18
  __exportStar(require("./streaming"), exports);
19
+ __exportStar(require("./ui"), exports);
20
20
  //# sourceMappingURL=index.js.map
@@ -40,6 +40,7 @@ exports.parseVersions = parseVersions;
40
40
  exports.isVersionOutdated = isVersionOutdated;
41
41
  exports.getOptimizedRangeVersion = getOptimizedRangeVersion;
42
42
  exports.findClosestMinorVersion = findClosestMinorVersion;
43
+ exports.findHighestPatchVersion = findHighestPatchVersion;
43
44
  exports.applyVersionPrefix = applyVersionPrefix;
44
45
  const semver = __importStar(require("semver"));
45
46
  const manifest_1 = require("./manifest");
@@ -95,19 +96,9 @@ function isVersionOutdated(current, latest) {
95
96
  */
96
97
  function getOptimizedRangeVersion(_packageName, currentRange, allVersions, latestVersion) {
97
98
  try {
98
- // Find the highest version that satisfies the current range
99
- const satisfyingVersions = allVersions.filter((version) => {
100
- try {
101
- return semver.satisfies(version, currentRange);
102
- // semver.satisfies swallows invalid input internally (returns false);
103
- // safety net for future semver behavior changes only.
104
- /* v8 ignore start */
105
- }
106
- catch {
107
- return false;
108
- }
109
- /* v8 ignore stop */
110
- });
99
+ // Find the highest version that satisfies the current range. satisfies()
100
+ // returns false (never throws) for invalid input, so no guard is needed.
101
+ const satisfyingVersions = allVersions.filter((version) => semver.satisfies(version, currentRange));
111
102
  if (satisfyingVersions.length === 0) {
112
103
  return latestVersion;
113
104
  }
@@ -130,7 +121,6 @@ function findClosestMinorVersion(installedVersion, allVersions) {
130
121
  }
131
122
  const installedMajor = semver.major(coercedInstalled);
132
123
  const installedMinor = semver.minor(coercedInstalled);
133
- const installedPatch = semver.patch(coercedInstalled);
134
124
  let bestMinorVersion = null;
135
125
  let bestMinorValue = -1;
136
126
  // Single pass to find best minor version in same major
@@ -150,30 +140,44 @@ function findClosestMinorVersion(installedVersion, allVersions) {
150
140
  if (bestMinorVersion) {
151
141
  return bestMinorVersion;
152
142
  }
153
- // Fallback: find highest patch version in same major.minor that's higher than installed
154
- let bestPatchVersion = null;
155
- for (const version of allVersions) {
156
- try {
157
- const major = semver.major(version);
158
- const minor = semver.minor(version);
159
- const patch = semver.patch(version);
160
- // Same major and minor, but higher patch
161
- if (major === installedMajor && minor === installedMinor && patch > installedPatch) {
162
- if (!bestPatchVersion || semver.gt(version, bestPatchVersion)) {
163
- bestPatchVersion = version;
164
- }
165
- }
166
- }
167
- catch {
168
- // Skip invalid versions
169
- }
170
- }
171
- return bestPatchVersion;
143
+ // Fallback: highest patch version in the same major.minor that's higher than installed
144
+ return findHighestPatchVersion(installedVersion, allVersions);
172
145
  }
173
146
  catch {
174
147
  return null;
175
148
  }
176
149
  }
150
+ /**
151
+ * Find the highest patch version in the installed version's own major.minor line.
152
+ * This is the `--target patch` policy: never crosses a minor (or major) boundary.
153
+ */
154
+ function findHighestPatchVersion(installedVersion, allVersions) {
155
+ const coercedInstalled = semver.coerce(installedVersion);
156
+ if (!coercedInstalled) {
157
+ return null;
158
+ }
159
+ const installedMajor = semver.major(coercedInstalled);
160
+ const installedMinor = semver.minor(coercedInstalled);
161
+ const installedPatch = semver.patch(coercedInstalled);
162
+ let bestPatchVersion = null;
163
+ for (const version of allVersions) {
164
+ try {
165
+ const major = semver.major(version);
166
+ const minor = semver.minor(version);
167
+ const patch = semver.patch(version);
168
+ // Same major and minor, but higher patch
169
+ if (major === installedMajor && minor === installedMinor && patch > installedPatch) {
170
+ if (!bestPatchVersion || semver.gt(version, bestPatchVersion)) {
171
+ bestPatchVersion = version;
172
+ }
173
+ }
174
+ }
175
+ catch {
176
+ // Skip invalid versions
177
+ }
178
+ }
179
+ return bestPatchVersion;
180
+ }
177
181
  /** Re-apply the original specifier's range prefix (^, ~, >=, …) to a new version. */
178
182
  function applyVersionPrefix(originalSpecifier, targetVersion) {
179
183
  const prefixMatch = originalSpecifier.match(/^([^\d]+)/);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inup",
3
- "version": "1.6.7",
3
+ "version": "1.6.8",
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": {
@@ -30,7 +30,7 @@
30
30
  "ncu"
31
31
  ],
32
32
  "license": "MIT",
33
- "homepage": "https://github.com/donfear/inup#readme",
33
+ "homepage": "https://donfear.github.io/inup/",
34
34
  "repository": {
35
35
  "type": "git",
36
36
  "url": "git+https://github.com/donfear/inup.git"
@@ -43,11 +43,11 @@
43
43
  "README.md"
44
44
  ],
45
45
  "devDependencies": {
46
+ "@biomejs/biome": "^2.5.2",
46
47
  "@types/node": "^25.9.4",
47
48
  "@types/semver": "^7.7.1",
48
49
  "@vitest/coverage-v8": "^4.1.9",
49
50
  "dependency-cruiser": "^18.0.0",
50
- "prettier": "^3.9.4",
51
51
  "typescript": "^6.0.3",
52
52
  "vitest": "^4.1.9"
53
53
  },
@@ -73,8 +73,9 @@
73
73
  "dev": "tsc --watch",
74
74
  "start": "node dist/cli.js",
75
75
  "link": "pnpm build && pnpm add -g .",
76
- "format": "prettier --write \"src/**/*.ts\"",
77
- "format:check": "prettier --check \"src/**/*.ts\"",
76
+ "check": "biome check --write .",
77
+ "check:ci": "biome ci .",
78
+ "typecheck": "tsc --noEmit",
78
79
  "demo:record": "bash docs/demo/record-demo.sh",
79
80
  "demo:setup": "cd docs/demo-project && pnpm install",
80
81
  "test": "vitest run",
@@ -82,6 +83,9 @@
82
83
  "test:coverage": "vitest run --coverage --reporter=default --reporter=json --outputFile.json=coverage/test-results.json",
83
84
  "lint:deps": "depcruise src --config .dependency-cruiser.cjs",
84
85
  "docs:keys": "UPDATE_README=1 vitest run keymap-readme",
85
- "docs:badges": "pnpm test:coverage && UPDATE_README=1 vitest run badges-readme"
86
+ "docs:badges": "pnpm test:coverage && UPDATE_README=1 vitest run badges-readme",
87
+ "site:dev": "pnpm --filter @inup/website dev",
88
+ "site:build": "pnpm --filter @inup/website build",
89
+ "site:preview": "pnpm --filter @inup/website preview"
86
90
  }
87
91
  }