inup 1.4.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.
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PackageUpgrader = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const nanospinner_1 = require("nanospinner");
9
+ const fs_1 = require("fs");
10
+ const path_1 = require("path");
11
+ const utils_1 = require("../utils");
12
+ class PackageUpgrader {
13
+ constructor(packageManager) {
14
+ this.packageManager = packageManager;
15
+ }
16
+ async upgradePackages(choices, packageInfos) {
17
+ if (choices.length === 0) {
18
+ console.log(chalk_1.default.yellow('No packages to upgrade.'));
19
+ return;
20
+ }
21
+ // Group choices by package.json path and dependency type
22
+ const choicesByFileAndType = this.groupChoicesByFileAndType(choices, packageInfos);
23
+ for (const [fileAndType, choiceList] of Object.entries(choicesByFileAndType)) {
24
+ if (choiceList.length === 0)
25
+ continue;
26
+ const [packageJsonPath, type] = fileAndType.split('|');
27
+ console.log(`Processing ${type} in ${packageJsonPath}`);
28
+ await this.upgradeChoiceGroup(choiceList, packageJsonPath, type);
29
+ }
30
+ // Count unique packages upgraded
31
+ const uniquePackages = new Set(choices.map((c) => c.name));
32
+ console.log(chalk_1.default.green(`\n✅ Successfully upgraded ${uniquePackages.size} package(s)!`));
33
+ // Execute package manager install after all upgrades are complete
34
+ await this.runInstall(choices, packageInfos);
35
+ }
36
+ async runInstall(choices, packageInfos) {
37
+ if (choices.length === 0) {
38
+ return;
39
+ }
40
+ // Determine the directory to run install in
41
+ // Use workspace root if it exists, otherwise use the directory of the first package.json
42
+ const firstPackageJsonPath = choices[0].packageJsonPath;
43
+ const firstPackageDir = (0, path_1.dirname)(firstPackageJsonPath);
44
+ const workspaceRoot = (0, utils_1.findWorkspaceRoot)(firstPackageDir, this.packageManager.name);
45
+ const installDir = workspaceRoot || firstPackageDir;
46
+ // Check if package manager is installed
47
+ try {
48
+ (0, utils_1.executeCommand)(`${this.packageManager.name} --version`, installDir);
49
+ }
50
+ catch (error) {
51
+ console.log(chalk_1.default.yellow(`\n⚠️ ${this.packageManager.displayName} is detected but not installed on your system.\n` +
52
+ `Please run the install command manually:\n` +
53
+ ` cd ${installDir}\n` +
54
+ ` ${this.packageManager.installCommand}\n`));
55
+ return; // Skip install, let user do it manually
56
+ }
57
+ const spinner = (0, nanospinner_1.createSpinner)(`Running ${this.packageManager.displayName} install...`).start();
58
+ try {
59
+ (0, utils_1.executeCommand)(this.packageManager.installCommand, installDir);
60
+ spinner.success();
61
+ }
62
+ catch (error) {
63
+ spinner.error();
64
+ console.error(chalk_1.default.red(`Error: ${error}`));
65
+ throw error;
66
+ }
67
+ }
68
+ groupChoicesByFileAndType(choices, packageInfos) {
69
+ const groups = {};
70
+ choices.forEach((choice) => {
71
+ const info = packageInfos.find((p) => p.name === choice.name && p.packageJsonPath === choice.packageJsonPath);
72
+ if (info) {
73
+ const key = `${choice.packageJsonPath}|${info.type}`;
74
+ if (!groups[key]) {
75
+ groups[key] = [];
76
+ }
77
+ groups[key].push(choice);
78
+ }
79
+ });
80
+ return groups;
81
+ }
82
+ async upgradeChoiceGroup(choices, packageJsonPath, type) {
83
+ // Validate that package.json exists
84
+ if (!(0, fs_1.existsSync)(packageJsonPath)) {
85
+ console.warn(chalk_1.default.yellow(`⚠️ Skipping ${type} in ${packageJsonPath} - package.json file not found`));
86
+ return;
87
+ }
88
+ const packageDir = packageJsonPath.replace('/package.json', '');
89
+ const spinner = (0, nanospinner_1.createSpinner)(`Upgrading ${type} in ${packageDir}...`).start();
90
+ try {
91
+ // Read the current package.json
92
+ const packageJson = (0, utils_1.readPackageJson)(packageJsonPath);
93
+ // Find workspace root
94
+ const workspaceRoot = (0, utils_1.findWorkspaceRoot)(packageDir, this.packageManager.name);
95
+ const isWorkspaceRoot = packageDir === workspaceRoot;
96
+ // Group by upgrade type (range vs latest)
97
+ const rangeChoices = choices.filter((c) => c.upgradeType === 'range');
98
+ const latestChoices = choices.filter((c) => c.upgradeType === 'latest');
99
+ let modified = false;
100
+ // Upgrade range versions by directly modifying package.json
101
+ if (rangeChoices.length > 0) {
102
+ if (!packageJson[type]) {
103
+ packageJson[type] = {};
104
+ }
105
+ rangeChoices.forEach((choice) => {
106
+ packageJson[type][choice.name] = choice.targetVersion;
107
+ modified = true;
108
+ });
109
+ }
110
+ // Upgrade to latest versions by directly modifying package.json
111
+ if (latestChoices.length > 0) {
112
+ if (!packageJson[type]) {
113
+ packageJson[type] = {};
114
+ }
115
+ latestChoices.forEach((choice) => {
116
+ packageJson[type][choice.name] = choice.targetVersion;
117
+ modified = true;
118
+ });
119
+ }
120
+ // Write back the modified package.json
121
+ if (modified) {
122
+ (0, fs_1.writeFileSync)(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
123
+ }
124
+ spinner.success({ text: `Upgraded ${choices.length} ${type} in ${packageDir}` });
125
+ // Show which packages were upgraded
126
+ choices.forEach((choice) => {
127
+ const upgradeTypeColor = choice.upgradeType === 'range' ? chalk_1.default.yellow : chalk_1.default.red;
128
+ console.log(` ${chalk_1.default.green('✓')} ${chalk_1.default.cyan(choice.name)} → ${upgradeTypeColor(choice.targetVersion)}`);
129
+ });
130
+ }
131
+ catch (error) {
132
+ spinner.error({ text: `Failed to upgrade ${type} in ${packageDir}` });
133
+ console.error(chalk_1.default.red(`Error: ${error}`));
134
+ throw error;
135
+ }
136
+ }
137
+ }
138
+ exports.PackageUpgrader = PackageUpgrader;
139
+ //# sourceMappingURL=upgrader.js.map
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ /**
3
+ * Public API for inup
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PnpmUpgradeInteractive = exports.UpgradeRunner = void 0;
7
+ var core_1 = require("./core");
8
+ Object.defineProperty(exports, "UpgradeRunner", { enumerable: true, get: function () { return core_1.UpgradeRunner; } });
9
+ Object.defineProperty(exports, "PnpmUpgradeInteractive", { enumerable: true, get: function () { return core_1.PnpmUpgradeInteractive; } });
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,411 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.InteractiveUI = void 0;
40
+ const inquirer_1 = __importDefault(require("inquirer"));
41
+ const chalk_1 = __importDefault(require("chalk"));
42
+ const semver = __importStar(require("semver"));
43
+ const keypress = require('keypress');
44
+ const ui_1 = require("./ui");
45
+ const services_1 = require("./services");
46
+ class InteractiveUI {
47
+ constructor(packageManager) {
48
+ this.renderer = new ui_1.UIRenderer();
49
+ this.packageManager = packageManager;
50
+ }
51
+ async displayPackagesTable(packages) {
52
+ console.log(this.renderer.renderPackagesTable(packages));
53
+ }
54
+ async selectPackagesToUpgrade(packages, previousSelections, options) {
55
+ const outdatedPackages = packages.filter((p) => p.isOutdated);
56
+ if (outdatedPackages.length === 0) {
57
+ return [];
58
+ }
59
+ // Filter packages based on CLI options.
60
+ // IMPORTANT: This uses an opt-in approach where:
61
+ // - Default (no flags): shows dependencies + devDependencies
62
+ // - With -p flag: shows ONLY peerDependencies (excludes dependencies)
63
+ // - With -o flag: shows ONLY optionalDependencies (excludes dependencies)
64
+ // - With -p -o flags: shows peerDependencies + optionalDependencies (excludes dependencies)
65
+ //
66
+ // This design allows users to focus on one dependency type at a time,
67
+ // which is useful since peer/optional deps have different upgrade semantics.
68
+ let filteredPackages = outdatedPackages;
69
+ let dependencyTypeLabel = '';
70
+ if (options?.includePeerDeps || options?.includeOptionalDeps) {
71
+ // If any special-case flag is provided, filter to show ONLY those types
72
+ // (excluding regular dependencies/devDependencies)
73
+ filteredPackages = outdatedPackages.filter((pkg) => {
74
+ if (options.includePeerDeps && pkg.type === 'peerDependencies')
75
+ return true;
76
+ if (options.includeOptionalDeps && pkg.type === 'optionalDependencies')
77
+ return true;
78
+ return false;
79
+ });
80
+ // Build label describing which types are shown
81
+ const types = [];
82
+ if (options.includePeerDeps)
83
+ types.push('Peer Dependencies');
84
+ if (options.includeOptionalDeps)
85
+ types.push('Optional Dependencies');
86
+ dependencyTypeLabel = types.join(' & ');
87
+ }
88
+ else {
89
+ // Default: show only regular dependencies and devDependencies
90
+ filteredPackages = outdatedPackages.filter((pkg) => pkg.type === 'dependencies' || pkg.type === 'devDependencies');
91
+ dependencyTypeLabel = 'Dependencies & Dev Dependencies';
92
+ }
93
+ if (filteredPackages.length === 0) {
94
+ return [];
95
+ }
96
+ // Deduplicate packages by name and version specifier, but track all package.json paths
97
+ const uniquePackages = new Map();
98
+ for (const pkg of filteredPackages) {
99
+ const key = `${pkg.name}@${pkg.currentVersion}`;
100
+ if (!uniquePackages.has(key)) {
101
+ uniquePackages.set(key, {
102
+ pkg,
103
+ packageJsonPaths: new Set([pkg.packageJsonPath]),
104
+ type: pkg.type,
105
+ });
106
+ }
107
+ else {
108
+ uniquePackages.get(key).packageJsonPaths.add(pkg.packageJsonPath);
109
+ }
110
+ }
111
+ // Convert to array and sort alphabetically by name (@scoped packages first, then unscoped)
112
+ const deduplicatedPackages = Array.from(uniquePackages.values()).map(({ pkg, packageJsonPaths, type }) => ({
113
+ ...pkg,
114
+ packageJsonPaths: Array.from(packageJsonPaths),
115
+ type,
116
+ }));
117
+ deduplicatedPackages.sort((a, b) => {
118
+ const aIsScoped = a.name.startsWith('@');
119
+ const bIsScoped = b.name.startsWith('@');
120
+ // If one is scoped and the other isn't, scoped comes first
121
+ if (aIsScoped && !bIsScoped)
122
+ return -1;
123
+ if (!aIsScoped && bIsScoped)
124
+ return 1;
125
+ // Both scoped or both unscoped - sort alphabetically
126
+ return a.name.localeCompare(b.name);
127
+ });
128
+ // Create selection states for each unique package
129
+ const selectionStates = deduplicatedPackages.map((pkg) => {
130
+ const currentClean = semver.coerce(pkg.currentVersion)?.version || pkg.currentVersion;
131
+ const rangeClean = semver.coerce(pkg.rangeVersion)?.version || pkg.rangeVersion;
132
+ const latestClean = semver.coerce(pkg.latestVersion)?.version || pkg.latestVersion;
133
+ // Use previous selection if available, otherwise default to 'none'
134
+ const key = `${pkg.name}@${pkg.currentVersion}`;
135
+ const previousSelection = previousSelections?.get(key) || 'none';
136
+ return {
137
+ name: pkg.name,
138
+ packageJsonPath: pkg.packageJsonPaths[0], // Use first path for display
139
+ packageJsonPaths: pkg.packageJsonPaths, // Store all paths for upgrading
140
+ currentVersionSpecifier: pkg.currentVersion, // Keep original with prefix
141
+ currentVersion: currentClean,
142
+ rangeVersion: rangeClean,
143
+ latestVersion: latestClean,
144
+ selectedOption: previousSelection,
145
+ hasRangeUpdate: pkg.hasRangeUpdate,
146
+ hasMajorUpdate: pkg.hasMajorUpdate,
147
+ type: pkg.type,
148
+ };
149
+ });
150
+ // Use custom interactive table selector (simplified - no grouping)
151
+ const selectedStates = await this.interactiveTableSelector(selectionStates, dependencyTypeLabel);
152
+ // Convert to PackageUpgradeChoice[] - create one choice per package.json path
153
+ const choices = [];
154
+ selectedStates
155
+ .filter((state) => state.selectedOption !== 'none')
156
+ .forEach((state) => {
157
+ const targetVersion = state.selectedOption === 'range' ? state.rangeVersion : state.latestVersion;
158
+ const targetVersionWithPrefix = ui_1.VersionUtils.applyVersionPrefix(state.currentVersionSpecifier, targetVersion);
159
+ // Create a choice for each package.json path where this package appears
160
+ const pathsToUpdate = state.packageJsonPaths || [state.packageJsonPath];
161
+ pathsToUpdate.forEach((packageJsonPath) => {
162
+ choices.push({
163
+ name: state.name,
164
+ packageJsonPath,
165
+ upgradeType: state.selectedOption,
166
+ targetVersion: targetVersionWithPrefix,
167
+ currentVersionSpecifier: state.currentVersionSpecifier,
168
+ });
169
+ });
170
+ });
171
+ return choices;
172
+ }
173
+ getTerminalHeight() {
174
+ // Check if stdout is a TTY and has rows property
175
+ if (process.stdout.isTTY && typeof process.stdout.rows === 'number' && process.stdout.rows > 0) {
176
+ return process.stdout.rows;
177
+ }
178
+ return 24; // Fallback default
179
+ }
180
+ async interactiveTableSelector(selectionStates, dependencyTypeLabel) {
181
+ return new Promise((resolve) => {
182
+ const states = [...selectionStates];
183
+ const stateManager = new ui_1.StateManager(0, this.getTerminalHeight());
184
+ // No grouping needed - packages are already filtered by type
185
+ // This simplifies scrolling and avoids rendering issues
186
+ stateManager.setRenderableItems([]);
187
+ const handleAction = (action) => {
188
+ const uiState = stateManager.getUIState();
189
+ switch (action.type) {
190
+ case 'navigate_up':
191
+ if (!uiState.showInfoModal) {
192
+ stateManager.navigateUp(states.length);
193
+ }
194
+ break;
195
+ case 'navigate_down':
196
+ if (!uiState.showInfoModal) {
197
+ stateManager.navigateDown(states.length);
198
+ }
199
+ break;
200
+ case 'select_left':
201
+ if (!uiState.showInfoModal) {
202
+ stateManager.updateSelection(states, 'left');
203
+ }
204
+ break;
205
+ case 'select_right':
206
+ if (!uiState.showInfoModal) {
207
+ stateManager.updateSelection(states, 'right');
208
+ }
209
+ break;
210
+ case 'bulk_select_minor':
211
+ if (!uiState.showInfoModal) {
212
+ stateManager.bulkSelectMinor(states);
213
+ }
214
+ break;
215
+ case 'bulk_select_latest':
216
+ if (!uiState.showInfoModal) {
217
+ stateManager.bulkSelectLatest(states);
218
+ }
219
+ break;
220
+ case 'bulk_unselect_all':
221
+ if (!uiState.showInfoModal) {
222
+ stateManager.bulkUnselectAll(states);
223
+ }
224
+ break;
225
+ case 'toggle_info_modal':
226
+ if (!uiState.showInfoModal) {
227
+ // Opening modal - load package info asynchronously
228
+ stateManager.toggleInfoModal();
229
+ const currentState = states[uiState.currentRow];
230
+ stateManager.setModalLoading(true);
231
+ renderInterface();
232
+ // Fetch metadata asynchronously
233
+ services_1.changelogFetcher.fetchPackageMetadata(currentState.name).then((metadata) => {
234
+ if (metadata) {
235
+ currentState.description = metadata.description;
236
+ currentState.homepage = metadata.homepage;
237
+ currentState.repository = metadata.releaseNotes;
238
+ currentState.weeklyDownloads = metadata.weeklyDownloads;
239
+ currentState.author = metadata.author;
240
+ currentState.license = metadata.license;
241
+ }
242
+ stateManager.setModalLoading(false);
243
+ renderInterface();
244
+ });
245
+ }
246
+ else {
247
+ // Closing modal
248
+ stateManager.toggleInfoModal();
249
+ renderInterface();
250
+ }
251
+ break;
252
+ case 'resize':
253
+ const heightChanged = stateManager.updateTerminalHeight(action.height);
254
+ if (heightChanged) {
255
+ stateManager.resetForResize();
256
+ }
257
+ else {
258
+ // Even if height didn't change, width might have changed
259
+ // Force a full re-render to clear any wrapping issues
260
+ stateManager.setInitialRender(true);
261
+ }
262
+ break;
263
+ case 'cancel':
264
+ handleCancel();
265
+ return;
266
+ }
267
+ if (action.type !== 'toggle_info_modal') {
268
+ renderInterface();
269
+ }
270
+ };
271
+ const handleConfirm = (selectedStates) => {
272
+ // Clean up listeners
273
+ if (process.stdin.setRawMode) {
274
+ process.stdin.setRawMode(false);
275
+ }
276
+ process.stdin.removeAllListeners('keypress');
277
+ process.stdin.pause();
278
+ process.removeAllListeners('SIGWINCH');
279
+ resolve(selectedStates);
280
+ };
281
+ const handleCancel = () => {
282
+ // Clean up listeners
283
+ if (process.stdin.setRawMode) {
284
+ process.stdin.setRawMode(false);
285
+ }
286
+ process.stdin.removeAllListeners('keypress');
287
+ process.stdin.pause();
288
+ process.removeAllListeners('SIGWINCH');
289
+ resolve(states.map((s) => ({ ...s, selectedOption: 'none' })));
290
+ };
291
+ const inputHandler = new ui_1.InputHandler(stateManager, handleAction, handleConfirm, handleCancel);
292
+ const renderInterface = () => {
293
+ const uiState = stateManager.getUIState();
294
+ if (uiState.isInitialRender) {
295
+ console.clear();
296
+ }
297
+ else {
298
+ // Move cursor to top and rewrite everything to minimize flicker
299
+ process.stdout.write('\x1b[H');
300
+ }
301
+ // 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];
304
+ const terminalWidth = process.stdout.columns || 80;
305
+ const terminalHeight = this.getTerminalHeight();
306
+ // Render header
307
+ const headerLines = [];
308
+ headerLines.push(' ' + chalk_1.default.bold.magenta('🚀 inup'));
309
+ headerLines.push('');
310
+ headerLines.push(' ' +
311
+ chalk_1.default.bold.white('I / Esc ') +
312
+ chalk_1.default.gray('Exit this view'));
313
+ headerLines.push('');
314
+ headerLines.forEach((line) => console.log(line));
315
+ if (uiState.isLoadingModalInfo) {
316
+ // Show loading state
317
+ const modalLines = this.renderer.renderPackageInfoLoading(selectedState, terminalWidth, terminalHeight);
318
+ modalLines.forEach((line) => console.log(line));
319
+ }
320
+ else {
321
+ // Show full info
322
+ const modalLines = this.renderer.renderPackageInfoModal(selectedState, terminalWidth, terminalHeight);
323
+ modalLines.forEach((line) => console.log(line));
324
+ }
325
+ // Clear any remaining lines from previous render
326
+ process.stdout.write('\x1b[J');
327
+ stateManager.markRendered([]);
328
+ }
329
+ else {
330
+ // 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
332
+ dependencyTypeLabel, // Show which dependency type we're upgrading
333
+ this.packageManager // Pass package manager info for header
334
+ );
335
+ // Print all lines
336
+ lines.forEach((line) => console.log(line));
337
+ // Clear any remaining lines from previous render
338
+ if (!uiState.isInitialRender) {
339
+ process.stdout.write('\x1b[J');
340
+ }
341
+ stateManager.markRendered(lines);
342
+ }
343
+ stateManager.setInitialRender(false);
344
+ };
345
+ const handleResize = () => {
346
+ // On resize (width or height change), always trigger a re-render
347
+ // This prevents layout breaking when terminal width changes
348
+ // The action handler will update height and force a full re-render
349
+ inputHandler.handleResize(this.getTerminalHeight());
350
+ };
351
+ // Setup keypress handling
352
+ try {
353
+ keypress(process.stdin);
354
+ if (process.stdin.setRawMode) {
355
+ process.stdin.setRawMode(true);
356
+ }
357
+ process.stdin.resume();
358
+ process.stdin.on('keypress', (str, key) => inputHandler.handleKeypress(str, key, states));
359
+ // Setup resize handler
360
+ process.on('SIGWINCH', handleResize);
361
+ // Update terminal height directly before initial render to ensure correct dimensions
362
+ // This handles cases where process.stdout.rows might not be accurate at startup
363
+ const currentHeight = this.getTerminalHeight();
364
+ if (stateManager.updateTerminalHeight(currentHeight)) {
365
+ stateManager.resetForResize();
366
+ }
367
+ // Initial render
368
+ renderInterface();
369
+ }
370
+ catch (error) {
371
+ // Fallback to simple interface if raw mode fails
372
+ console.log(chalk_1.default.yellow('Raw mode not available, using fallback interface...'));
373
+ resolve(states);
374
+ }
375
+ });
376
+ }
377
+ async confirmUpgrade(choices) {
378
+ console.log(this.renderer.renderConfirmation(choices));
379
+ return new Promise((resolve) => {
380
+ const handleConfirm = (confirmed) => {
381
+ resolve(confirmed);
382
+ };
383
+ const inputHandler = new ui_1.ConfirmationInputHandler(handleConfirm);
384
+ // Setup keypress handling
385
+ try {
386
+ keypress(process.stdin);
387
+ if (process.stdin.setRawMode) {
388
+ process.stdin.setRawMode(true);
389
+ }
390
+ process.stdin.resume();
391
+ process.stdin.on('keypress', (str, key) => inputHandler.handleKeypress(str, key));
392
+ }
393
+ catch (error) {
394
+ // Fallback to inquirer
395
+ inquirer_1.default
396
+ .prompt([
397
+ {
398
+ type: 'confirm',
399
+ name: 'proceed',
400
+ message: 'Proceed with upgrade?',
401
+ default: true,
402
+ },
403
+ ])
404
+ .then((answer) => resolve(answer.proceed))
405
+ .catch(() => resolve(false));
406
+ }
407
+ });
408
+ }
409
+ }
410
+ exports.InteractiveUI = InteractiveUI;
411
+ //# sourceMappingURL=interactive-ui.js.map