inup 1.4.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -59,6 +59,15 @@ inup
59
59
 
60
60
  The tool will scan your entire workspace (including monorepos), find outdated packages, and let you choose which ones to upgrade interactively.
61
61
 
62
+ ### Interactive Features
63
+
64
+ - **Search**: Press `/` to search for a specific package by name
65
+ - **Navigate**: Use arrow keys to move between packages
66
+ - **Select Version**: Use `Left` and `Right` arrow keys to cycle through available versions (default, minor, patch, major)
67
+ - **Select All Minor**: Press `m` to select all minor updates
68
+ - **Package Info**: Press `i` to view detailed information about the selected package
69
+ - **Exit Search**: Press `Esc` to exit search mode
70
+
62
71
  ### Command line options
63
72
 
64
73
  - `-d, --dir <directory>`: Run in a specific directory (default: current directory)
@@ -3,12 +3,17 @@
3
3
  * Constants for npm registry queries and configuration
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.WORKSPACE_FILES = exports.LOCK_FILES = exports.REQUEST_TIMEOUT = exports.CACHE_TTL = exports.MAX_CONCURRENT_REQUESTS = exports.JSDELIVR_CDN_URL = exports.NPM_REGISTRY_URL = void 0;
6
+ exports.WORKSPACE_FILES = exports.LOCK_FILES = exports.DEFAULT_REGISTRY = exports.REQUEST_TIMEOUT = exports.CACHE_TTL = exports.MAX_CONCURRENT_REQUESTS = exports.JSDELIVR_CDN_URL = exports.NPM_REGISTRY_URL = void 0;
7
7
  exports.NPM_REGISTRY_URL = 'https://registry.npmjs.org';
8
8
  exports.JSDELIVR_CDN_URL = 'https://cdn.jsdelivr.net/npm';
9
- exports.MAX_CONCURRENT_REQUESTS = 80;
9
+ exports.MAX_CONCURRENT_REQUESTS = 150;
10
10
  exports.CACHE_TTL = 5 * 60 * 1000; // 5 minutes in milliseconds
11
- exports.REQUEST_TIMEOUT = 30000; // 30 seconds in milliseconds
11
+ exports.REQUEST_TIMEOUT = 60000; // 60 seconds in milliseconds
12
+ /**
13
+ * Registry selection: 'jsdelivr' for fast CDN lookups, 'npm' for direct npm registry
14
+ * Set to 'npm' to use npm registry by default instead of jsdelivr
15
+ */
16
+ exports.DEFAULT_REGISTRY = 'jsdelivr';
12
17
  /**
13
18
  * Package manager lock files
14
19
  */
@@ -0,0 +1,18 @@
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("./constants"), exports);
18
+ //# sourceMappingURL=index.js.map
@@ -37,6 +37,7 @@ exports.PackageDetector = void 0;
37
37
  const semver = __importStar(require("semver"));
38
38
  const utils_1 = require("../utils");
39
39
  const services_1 = require("../services");
40
+ const config_1 = require("../config");
40
41
  class PackageDetector {
41
42
  constructor(options) {
42
43
  this.packageJsonPath = null;
@@ -80,7 +81,7 @@ class PackageDetector {
80
81
  }
81
82
  }
82
83
  const packageNames = Array.from(uniquePackageNames);
83
- // Step 4: Fetch all package data in one call per package using jsdelivr CDN
84
+ // Step 4: Fetch all package data in one call per package
84
85
  // Create a map of package names to their current versions for major version optimization
85
86
  const currentVersions = new Map();
86
87
  for (const dep of allDeps) {
@@ -89,11 +90,21 @@ class PackageDetector {
89
90
  currentVersions.set(dep.name, dep.version);
90
91
  }
91
92
  }
92
- const allPackageData = await (0, services_1.getAllPackageDataFromJsdelivr)(packageNames, currentVersions, (currentPackage, completed, total) => {
93
- const percentage = Math.round((completed / total) * 100);
94
- const truncatedPackage = currentPackage.length > 40 ? currentPackage.substring(0, 37) + '...' : currentPackage;
95
- this.showProgress(`🌐 Fetching ${percentage}% (${truncatedPackage})`);
96
- });
93
+ const allPackageData = config_1.DEFAULT_REGISTRY === 'jsdelivr'
94
+ ? await (0, services_1.getAllPackageDataFromJsdelivr)(packageNames, currentVersions, (currentPackage, completed, total) => {
95
+ const percentage = Math.round((completed / total) * 100);
96
+ const truncatedPackage = currentPackage.length > 40
97
+ ? currentPackage.substring(0, 37) + '...'
98
+ : currentPackage;
99
+ this.showProgress(`🌐 Fetching ${percentage}% (${truncatedPackage})`);
100
+ })
101
+ : await (0, services_1.getAllPackageData)(packageNames, (currentPackage, completed, total) => {
102
+ const percentage = Math.round((completed / total) * 100);
103
+ const truncatedPackage = currentPackage.length > 40
104
+ ? currentPackage.substring(0, 37) + '...'
105
+ : currentPackage;
106
+ this.showProgress(`🌐 Fetching ${percentage}% (${truncatedPackage})`);
107
+ });
97
108
  try {
98
109
  for (const dep of allDeps) {
99
110
  try {
@@ -80,15 +80,15 @@ class InteractiveUI {
80
80
  // Build label describing which types are shown
81
81
  const types = [];
82
82
  if (options.includePeerDeps)
83
- types.push('Peer Dependencies');
83
+ types.push('Peer Deps');
84
84
  if (options.includeOptionalDeps)
85
- types.push('Optional Dependencies');
85
+ types.push('Optional Deps');
86
86
  dependencyTypeLabel = types.join(' & ');
87
87
  }
88
88
  else {
89
89
  // Default: show only regular dependencies and devDependencies
90
90
  filteredPackages = outdatedPackages.filter((pkg) => pkg.type === 'dependencies' || pkg.type === 'devDependencies');
91
- dependencyTypeLabel = 'Dependencies & Dev Dependencies';
91
+ dependencyTypeLabel = 'Deps & Dev Deps';
92
92
  }
93
93
  if (filteredPackages.length === 0) {
94
94
  return [];
@@ -186,47 +186,48 @@ class InteractiveUI {
186
186
  stateManager.setRenderableItems([]);
187
187
  const handleAction = (action) => {
188
188
  const uiState = stateManager.getUIState();
189
+ const filteredStates = stateManager.getFilteredStates(states);
189
190
  switch (action.type) {
190
191
  case 'navigate_up':
191
192
  if (!uiState.showInfoModal) {
192
- stateManager.navigateUp(states.length);
193
+ stateManager.navigateUp(filteredStates.length);
193
194
  }
194
195
  break;
195
196
  case 'navigate_down':
196
197
  if (!uiState.showInfoModal) {
197
- stateManager.navigateDown(states.length);
198
+ stateManager.navigateDown(filteredStates.length);
198
199
  }
199
200
  break;
200
201
  case 'select_left':
201
202
  if (!uiState.showInfoModal) {
202
- stateManager.updateSelection(states, 'left');
203
+ stateManager.updateSelection(filteredStates, 'left');
203
204
  }
204
205
  break;
205
206
  case 'select_right':
206
207
  if (!uiState.showInfoModal) {
207
- stateManager.updateSelection(states, 'right');
208
+ stateManager.updateSelection(filteredStates, 'right');
208
209
  }
209
210
  break;
210
211
  case 'bulk_select_minor':
211
212
  if (!uiState.showInfoModal) {
212
- stateManager.bulkSelectMinor(states);
213
+ stateManager.bulkSelectMinor(filteredStates);
213
214
  }
214
215
  break;
215
216
  case 'bulk_select_latest':
216
217
  if (!uiState.showInfoModal) {
217
- stateManager.bulkSelectLatest(states);
218
+ stateManager.bulkSelectLatest(filteredStates);
218
219
  }
219
220
  break;
220
221
  case 'bulk_unselect_all':
221
222
  if (!uiState.showInfoModal) {
222
- stateManager.bulkUnselectAll(states);
223
+ stateManager.bulkUnselectAll(filteredStates);
223
224
  }
224
225
  break;
225
226
  case 'toggle_info_modal':
226
227
  if (!uiState.showInfoModal) {
227
228
  // Opening modal - load package info asynchronously
228
229
  stateManager.toggleInfoModal();
229
- const currentState = states[uiState.currentRow];
230
+ const currentState = filteredStates[uiState.currentRow];
230
231
  stateManager.setModalLoading(true);
231
232
  renderInterface();
232
233
  // Fetch metadata asynchronously
@@ -249,6 +250,20 @@ class InteractiveUI {
249
250
  renderInterface();
250
251
  }
251
252
  break;
253
+ case 'enter_filter_mode':
254
+ stateManager.enterFilterMode();
255
+ break;
256
+ case 'exit_filter_mode':
257
+ stateManager.exitFilterMode();
258
+ break;
259
+ case 'filter_input':
260
+ stateManager.appendToFilterQuery(action.char);
261
+ // Re-calculate filtered states after input
262
+ break;
263
+ case 'filter_backspace':
264
+ stateManager.deleteFromFilterQuery();
265
+ // Re-calculate filtered states after backspace
266
+ break;
252
267
  case 'resize':
253
268
  const heightChanged = stateManager.updateTerminalHeight(action.height);
254
269
  if (heightChanged) {
@@ -269,6 +284,7 @@ class InteractiveUI {
269
284
  }
270
285
  };
271
286
  const handleConfirm = (selectedStates) => {
287
+ ui_1.CursorUtils.show();
272
288
  // Clean up listeners
273
289
  if (process.stdin.setRawMode) {
274
290
  process.stdin.setRawMode(false);
@@ -279,6 +295,7 @@ class InteractiveUI {
279
295
  resolve(selectedStates);
280
296
  };
281
297
  const handleCancel = () => {
298
+ ui_1.CursorUtils.show();
282
299
  // Clean up listeners
283
300
  if (process.stdin.setRawMode) {
284
301
  process.stdin.setRawMode(false);
@@ -291,16 +308,17 @@ class InteractiveUI {
291
308
  const inputHandler = new ui_1.InputHandler(stateManager, handleAction, handleConfirm, handleCancel);
292
309
  const renderInterface = () => {
293
310
  const uiState = stateManager.getUIState();
294
- if (uiState.isInitialRender) {
311
+ const filteredStates = stateManager.getFilteredStates(states);
312
+ if (uiState.forceFullRender) {
295
313
  console.clear();
314
+ ui_1.CursorUtils.hide();
296
315
  }
297
316
  else {
298
- // Move cursor to top and rewrite everything to minimize flicker
299
- process.stdout.write('\x1b[H');
317
+ ui_1.CursorUtils.moveToHome();
300
318
  }
301
319
  // If modal is open, render only the modal with header/footer
302
- if (uiState.showInfoModal && uiState.infoModalRow >= 0 && uiState.infoModalRow < states.length) {
303
- const selectedState = states[uiState.infoModalRow];
320
+ if (uiState.showInfoModal && uiState.infoModalRow >= 0 && uiState.infoModalRow < filteredStates.length) {
321
+ const selectedState = filteredStates[uiState.infoModalRow];
304
322
  const terminalWidth = process.stdout.columns || 80;
305
323
  const terminalHeight = this.getTerminalHeight();
306
324
  // Render header
@@ -323,20 +341,20 @@ class InteractiveUI {
323
341
  modalLines.forEach((line) => console.log(line));
324
342
  }
325
343
  // Clear any remaining lines from previous render
326
- process.stdout.write('\x1b[J');
344
+ ui_1.CursorUtils.clearToEndOfScreen();
327
345
  stateManager.markRendered([]);
328
346
  }
329
347
  else {
330
348
  // Normal list view (flat rendering - no grouping)
331
- const lines = this.renderer.renderInterface(states, uiState.currentRow, uiState.scrollOffset, uiState.maxVisibleItems, uiState.isInitialRender, [], // No renderable items - use flat rendering
349
+ const lines = this.renderer.renderInterface(filteredStates, uiState.currentRow, uiState.scrollOffset, uiState.maxVisibleItems, uiState.forceFullRender, [], // No renderable items - use flat rendering
332
350
  dependencyTypeLabel, // Show which dependency type we're upgrading
333
- this.packageManager // Pass package manager info for header
334
- );
351
+ this.packageManager, // Pass package manager info for header
352
+ uiState.filterMode, uiState.filterQuery, states.length);
335
353
  // Print all lines
336
354
  lines.forEach((line) => console.log(line));
337
355
  // Clear any remaining lines from previous render
338
- if (!uiState.isInitialRender) {
339
- process.stdout.write('\x1b[J');
356
+ if (!uiState.forceFullRender) {
357
+ ui_1.CursorUtils.clearToEndOfScreen();
340
358
  }
341
359
  stateManager.markRendered(lines);
342
360
  }
@@ -387,6 +405,7 @@ class InteractiveUI {
387
405
  if (process.stdin.setRawMode) {
388
406
  process.stdin.setRawMode(true);
389
407
  }
408
+ ui_1.CursorUtils.hide();
390
409
  process.stdin.resume();
391
410
  process.stdin.on('keypress', (str, key) => inputHandler.handleKeypress(str, key));
392
411
  }
@@ -38,15 +38,16 @@ exports.clearJsdelivrPackageCache = clearJsdelivrPackageCache;
38
38
  exports.closeJsdelivrPool = closeJsdelivrPool;
39
39
  const undici_1 = require("undici");
40
40
  const semver = __importStar(require("semver"));
41
- const constants_1 = require("../constants");
41
+ const config_1 = require("../config");
42
42
  const npm_registry_1 = require("./npm-registry");
43
43
  // Create a persistent connection pool for jsDelivr CDN with optimal settings
44
44
  // This enables connection reuse and HTTP/1.1 keep-alive for blazing fast requests
45
45
  const jsdelivrPool = new undici_1.Pool('https://cdn.jsdelivr.net', {
46
- connections: constants_1.MAX_CONCURRENT_REQUESTS, // Maximum concurrent connections
46
+ connections: config_1.MAX_CONCURRENT_REQUESTS, // Maximum concurrent connections
47
47
  pipelining: 10, // Enable request pipelining for even better performance
48
- keepAliveTimeout: 60000, // Keep connections alive for 60 seconds
49
- keepAliveMaxTimeout: 600000, // Maximum keep-alive timeout
48
+ keepAliveTimeout: config_1.REQUEST_TIMEOUT, // Keep connections alive for 60 seconds
49
+ keepAliveMaxTimeout: config_1.REQUEST_TIMEOUT, // Maximum keep-alive timeout
50
+ connectTimeout: config_1.REQUEST_TIMEOUT, // 60 seconds connect timeout
50
51
  });
51
52
  const packageCache = new Map();
52
53
  /**
@@ -58,15 +59,15 @@ const packageCache = new Map();
58
59
  */
59
60
  async function fetchPackageJsonFromJsdelivr(packageName, versionTag) {
60
61
  try {
61
- const url = `${constants_1.JSDELIVR_CDN_URL}/${encodeURIComponent(packageName)}@${versionTag}/package.json`;
62
+ const url = `${config_1.JSDELIVR_CDN_URL}/${encodeURIComponent(packageName)}@${versionTag}/package.json`;
62
63
  const { statusCode, body } = await (0, undici_1.request)(url, {
63
64
  dispatcher: jsdelivrPool,
64
65
  method: 'GET',
65
66
  headers: {
66
67
  accept: 'application/json',
67
68
  },
68
- headersTimeout: constants_1.REQUEST_TIMEOUT,
69
- bodyTimeout: constants_1.REQUEST_TIMEOUT,
69
+ headersTimeout: config_1.REQUEST_TIMEOUT,
70
+ bodyTimeout: config_1.REQUEST_TIMEOUT,
70
71
  });
71
72
  if (statusCode !== 200) {
72
73
  // Consume body to prevent memory leaks
@@ -84,7 +85,7 @@ async function fetchPackageJsonFromJsdelivr(packageName, versionTag) {
84
85
  }
85
86
  /**
86
87
  * Fetches package data from jsdelivr CDN with fallback to npm registry.
87
- * Makes simultaneous requests for @latest and @major version.
88
+ * Makes simultaneous requests for @latest, @major version, and current version.
88
89
  * @param packageName - The npm package name
89
90
  * @param currentVersion - The current version to extract major from (optional)
90
91
  * @returns Package data with latest version and all versions
@@ -92,26 +93,31 @@ async function fetchPackageJsonFromJsdelivr(packageName, versionTag) {
92
93
  async function fetchPackageFromJsdelivr(packageName, currentVersion) {
93
94
  // Check cache first
94
95
  const cached = packageCache.get(packageName);
95
- if (cached && Date.now() - cached.timestamp < constants_1.CACHE_TTL) {
96
+ if (cached && Date.now() - cached.timestamp < config_1.CACHE_TTL) {
96
97
  return cached.data;
97
98
  }
98
99
  try {
100
+ // Coerce the current version in case it's a range like ^1.1.5
101
+ const coercedVersion = currentVersion ? semver.coerce(currentVersion)?.version : null;
99
102
  // Determine major version from current version if provided
100
- // Need to coerce the version first in case it's a range like ^1.1.5
101
103
  const majorVersion = currentVersion
102
104
  ? semver.major(semver.coerce(currentVersion) || '0.0.0').toString()
103
105
  : null;
104
- // Prepare requests: always fetch @latest, and @major if we have a current version
106
+ // Prepare requests: always fetch @latest, @major if we have a current version, and @currentVersion
105
107
  const requests = [
106
108
  fetchPackageJsonFromJsdelivr(packageName, 'latest'),
107
109
  ];
108
110
  if (majorVersion) {
109
111
  requests.push(fetchPackageJsonFromJsdelivr(packageName, majorVersion));
110
112
  }
113
+ if (coercedVersion) {
114
+ requests.push(fetchPackageJsonFromJsdelivr(packageName, coercedVersion));
115
+ }
111
116
  // Execute all requests simultaneously
112
117
  const results = await Promise.all(requests);
113
118
  const latestResult = results[0];
114
119
  const majorResult = results[1];
120
+ const currentResult = results[2];
115
121
  if (!latestResult) {
116
122
  // jsdelivr doesn't have this package, fallback to npm registry
117
123
  const npmData = await (0, npm_registry_1.getAllPackageData)([packageName]);
@@ -124,40 +130,14 @@ async function fetchPackageFromJsdelivr(packageName, currentVersion) {
124
130
  return data;
125
131
  }
126
132
  const latestVersion = latestResult.version;
127
- // If we have a major version result, we can build a minimal version list
128
- // This is much faster than fetching all versions from npm
129
- if (majorResult) {
130
- const allVersions = [latestVersion];
131
- // Add the major version result if different from latest
132
- if (majorResult.version !== latestVersion) {
133
- allVersions.push(majorResult.version);
134
- }
135
- // Add the current version if it's valid and not already in the list
136
- if (currentVersion && semver.valid(currentVersion)) {
137
- const coerced = semver.coerce(currentVersion);
138
- if (coerced && !allVersions.includes(coerced.version)) {
139
- allVersions.push(coerced.version);
140
- }
141
- }
142
- const result = {
143
- latestVersion,
144
- allVersions: allVersions.sort(semver.rcompare),
145
- };
146
- // Cache the result
147
- packageCache.set(packageName, {
148
- data: result,
149
- timestamp: Date.now(),
150
- });
151
- return result;
152
- }
153
- // No major version provided, just return latest with minimal version list
154
133
  const allVersions = [latestVersion];
155
- // Add the current version if it's valid and not already in the list
156
- if (currentVersion && semver.valid(currentVersion)) {
157
- const coerced = semver.coerce(currentVersion);
158
- if (coerced && !allVersions.includes(coerced.version)) {
159
- allVersions.push(coerced.version);
160
- }
134
+ // Add the major version result if different from latest
135
+ if (majorResult && majorResult.version !== latestVersion) {
136
+ allVersions.push(majorResult.version);
137
+ }
138
+ // Add the current version result if different from latest and major
139
+ if (currentResult && !allVersions.includes(currentResult.version)) {
140
+ allVersions.push(currentResult.version);
161
141
  }
162
142
  const result = {
163
143
  latestVersion,
@@ -35,65 +35,59 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.getAllPackageData = getAllPackageData;
37
37
  exports.clearPackageCache = clearPackageCache;
38
- exports.closeNpmPool = closeNpmPool;
39
- const undici_1 = require("undici");
40
38
  const semver = __importStar(require("semver"));
41
- const constants_1 = require("../constants");
42
- // Create a persistent connection pool for npm registry with optimal settings
43
- // This enables connection reuse and HTTP/1.1 keep-alive for blazing fast requests
44
- const npmPool = new undici_1.Pool('https://registry.npmjs.org', {
45
- connections: constants_1.MAX_CONCURRENT_REQUESTS, // Maximum concurrent connections
46
- pipelining: 10, // Enable request pipelining for even better performance
47
- keepAliveTimeout: 60000, // Keep connections alive for 60 seconds
48
- keepAliveMaxTimeout: 600000, // Maximum keep-alive timeout
49
- });
39
+ const config_1 = require("../config");
50
40
  const packageCache = new Map();
51
41
  /**
52
- * Fetches package data from npm registry with caching using undici pool.
53
- * Uses connection pooling and keep-alive for maximum performance.
42
+ * Fetches package data from npm registry with caching using native fetch.
43
+ * Includes timeout support for slow connections.
54
44
  */
55
45
  async function fetchPackageFromRegistry(packageName) {
56
46
  // Check cache first
57
47
  const cached = packageCache.get(packageName);
58
- if (cached && Date.now() - cached.timestamp < constants_1.CACHE_TTL) {
48
+ if (cached && Date.now() - cached.timestamp < config_1.CACHE_TTL) {
59
49
  return cached.data;
60
50
  }
61
51
  try {
62
- const url = `${constants_1.NPM_REGISTRY_URL}/${encodeURIComponent(packageName)}`;
63
- const { statusCode, body } = await (0, undici_1.request)(url, {
64
- dispatcher: npmPool,
65
- method: 'GET',
66
- headers: {
67
- accept: 'application/vnd.npm.install-v1+json',
68
- },
69
- headersTimeout: constants_1.REQUEST_TIMEOUT,
70
- bodyTimeout: constants_1.REQUEST_TIMEOUT,
71
- });
72
- if (statusCode !== 200) {
73
- // Consume body to prevent memory leaks
74
- await body.text();
75
- throw new Error(`HTTP ${statusCode}`);
52
+ const url = `${config_1.NPM_REGISTRY_URL}/${encodeURIComponent(packageName)}`;
53
+ const controller = new AbortController();
54
+ const timeoutId = setTimeout(() => controller.abort(), config_1.REQUEST_TIMEOUT);
55
+ try {
56
+ const response = await fetch(url, {
57
+ method: 'GET',
58
+ headers: {
59
+ accept: 'application/vnd.npm.install-v1+json',
60
+ },
61
+ signal: controller.signal,
62
+ });
63
+ clearTimeout(timeoutId);
64
+ if (!response.ok) {
65
+ throw new Error(`HTTP ${response.status}`);
66
+ }
67
+ const text = await response.text();
68
+ const data = JSON.parse(text);
69
+ // Extract versions and filter to valid semver (X.Y.Z format, no pre-releases)
70
+ const allVersions = Object.keys(data.versions || {}).filter((version) => {
71
+ // Match only X.Y.Z format (no pre-release, no build metadata)
72
+ return /^[0-9]+\.[0-9]+\.[0-9]+$/.test(version);
73
+ });
74
+ // Sort versions to find the latest
75
+ const sortedVersions = allVersions.sort(semver.rcompare);
76
+ const latestVersion = sortedVersions.length > 0 ? sortedVersions[0] : 'unknown';
77
+ const result = {
78
+ latestVersion,
79
+ allVersions,
80
+ };
81
+ // Cache the result
82
+ packageCache.set(packageName, {
83
+ data: result,
84
+ timestamp: Date.now(),
85
+ });
86
+ return result;
87
+ }
88
+ finally {
89
+ clearTimeout(timeoutId);
76
90
  }
77
- const text = await body.text();
78
- const data = JSON.parse(text);
79
- // Extract versions and filter to valid semver (X.Y.Z format, no pre-releases)
80
- const allVersions = Object.keys(data.versions || {}).filter((version) => {
81
- // Match only X.Y.Z format (no pre-release, no build metadata)
82
- return /^[0-9]+\.[0-9]+\.[0-9]+$/.test(version);
83
- });
84
- // Sort versions to find the latest
85
- const sortedVersions = allVersions.sort(semver.rcompare);
86
- const latestVersion = sortedVersions.length > 0 ? sortedVersions[0] : 'unknown';
87
- const result = {
88
- latestVersion,
89
- allVersions,
90
- };
91
- // Cache the result
92
- packageCache.set(packageName, {
93
- data: result,
94
- timestamp: Date.now(),
95
- });
96
- return result;
97
91
  }
98
92
  catch (error) {
99
93
  // Return fallback data for failed packages
@@ -102,7 +96,7 @@ async function fetchPackageFromRegistry(packageName) {
102
96
  }
103
97
  /**
104
98
  * Fetches package version data from npm registry for multiple packages.
105
- * Uses undici connection pool for blazing fast performance with connection reuse.
99
+ * Uses native fetch with timeout support for reliable performance.
106
100
  * Only returns valid semantic versions (X.Y.Z format, excluding pre-releases).
107
101
  */
108
102
  async function getAllPackageData(packageNames, onProgress) {
@@ -112,8 +106,8 @@ async function getAllPackageData(packageNames, onProgress) {
112
106
  }
113
107
  const total = packageNames.length;
114
108
  let completedCount = 0;
115
- // Fire all requests simultaneously - undici pool handles concurrency internally
116
- // No need for p-limit - the pool's connection limit controls concurrency
109
+ // Fire all requests simultaneously
110
+ // Concurrency is handled naturally by the event loop with fetch
117
111
  const allPromises = packageNames.map(async (packageName) => {
118
112
  const data = await fetchPackageFromRegistry(packageName);
119
113
  packageData.set(packageName, data);
@@ -136,10 +130,4 @@ async function getAllPackageData(packageNames, onProgress) {
136
130
  function clearPackageCache() {
137
131
  packageCache.clear();
138
132
  }
139
- /**
140
- * Close the npm registry connection pool (useful for graceful shutdown)
141
- */
142
- async function closeNpmPool() {
143
- await npmPool.close();
144
- }
145
133
  //# sourceMappingURL=npm-registry.js.map
@@ -37,7 +37,7 @@ exports.checkForUpdate = checkForUpdate;
37
37
  exports.checkForUpdateAsync = checkForUpdateAsync;
38
38
  const child_process_1 = require("child_process");
39
39
  const semver = __importStar(require("semver"));
40
- const constants_1 = require("../constants");
40
+ const config_1 = require("../config");
41
41
  /**
42
42
  * Check if the current package version is outdated compared to npm registry
43
43
  */
@@ -46,7 +46,7 @@ async function checkForUpdate(packageName, currentVersion) {
46
46
  // Use npm view to get the latest version from registry
47
47
  const result = (0, child_process_1.execSync)(`npm view ${packageName} version`, {
48
48
  encoding: 'utf-8',
49
- timeout: constants_1.REQUEST_TIMEOUT,
49
+ timeout: config_1.REQUEST_TIMEOUT,
50
50
  stdio: ['pipe', 'pipe', 'pipe'],
51
51
  });
52
52
  const latestVersion = result.trim();
package/dist/ui/index.js CHANGED
@@ -1,10 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ConfirmationInputHandler = exports.InputHandler = exports.UIRenderer = exports.StateManager = exports.VersionUtils = void 0;
3
+ exports.ConfirmationInputHandler = exports.InputHandler = exports.UIRenderer = exports.FilterManager = exports.ModalManager = exports.NavigationManager = exports.StateManager = exports.CursorUtils = exports.VersionUtils = void 0;
4
4
  var utils_1 = require("./utils");
5
5
  Object.defineProperty(exports, "VersionUtils", { enumerable: true, get: function () { return utils_1.VersionUtils; } });
6
+ Object.defineProperty(exports, "CursorUtils", { enumerable: true, get: function () { return utils_1.CursorUtils; } });
6
7
  var state_1 = require("./state");
7
8
  Object.defineProperty(exports, "StateManager", { enumerable: true, get: function () { return state_1.StateManager; } });
9
+ Object.defineProperty(exports, "NavigationManager", { enumerable: true, get: function () { return state_1.NavigationManager; } });
10
+ Object.defineProperty(exports, "ModalManager", { enumerable: true, get: function () { return state_1.ModalManager; } });
11
+ Object.defineProperty(exports, "FilterManager", { enumerable: true, get: function () { return state_1.FilterManager; } });
8
12
  var index_1 = require("./renderer/index");
9
13
  Object.defineProperty(exports, "UIRenderer", { enumerable: true, get: function () { return index_1.UIRenderer; } });
10
14
  var input_handler_1 = require("./input-handler");