inup 1.5.4 → 1.5.6
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 +24 -10
- package/dist/cli.js +20 -22
- package/dist/config/constants.js +4 -2
- package/dist/config/package-meta.js +10 -0
- package/dist/config/project-config.js +6 -1
- package/dist/core/package-detector.js +8 -4
- package/dist/core/upgrade-runner.js +7 -10
- package/dist/core/upgrader.js +6 -8
- package/dist/features/changelog/clients/github-client.js +5 -6
- package/dist/features/changelog/services/changelog-service.js +2 -4
- package/dist/features/changelog/services/package-metadata-service.js +6 -11
- package/dist/features/changelog/services/release-notes-service.js +4 -2
- package/dist/features/debug/services/performance-tracker.js +6 -8
- package/dist/index.js +1 -2
- package/dist/interactive-ui.js +30 -587
- package/dist/services/background-audit.js +3 -5
- package/dist/services/cache-manager.js +5 -7
- package/dist/services/http/inflight.js +22 -0
- package/dist/services/http/retry.js +30 -0
- package/dist/services/jsdelivr/client.js +191 -0
- package/dist/services/jsdelivr/manifest.js +136 -0
- package/dist/services/jsdelivr-registry.js +6 -392
- package/dist/services/npm-registry.js +12 -81
- package/dist/services/persistent-cache.js +8 -13
- package/dist/types/domain.js +3 -0
- package/dist/types/streaming.js +3 -0
- package/dist/types/ui.js +3 -0
- package/dist/types.js +17 -0
- package/dist/ui/controllers/package-info-modal-controller.js +22 -31
- package/dist/ui/controllers/vulnerability-audit-controller.js +17 -8
- package/dist/ui/index.js +3 -0
- package/dist/ui/input-handler.js +62 -77
- package/dist/ui/keymap.js +208 -0
- package/dist/ui/modal/package-info-sections/release-notes.js +161 -0
- package/dist/ui/modal/package-info-sections/sections.js +150 -0
- package/dist/ui/modal/package-info-sections/text.js +75 -0
- package/dist/ui/modal/package-info-sections.js +15 -368
- package/dist/ui/modal/package-info.js +1 -1
- package/dist/ui/renderer/help-modal.js +75 -0
- package/dist/ui/renderer/index.js +2 -5
- package/dist/ui/renderer/package-list/interface.js +184 -0
- package/dist/ui/renderer/package-list/rows.js +172 -0
- package/dist/ui/renderer/package-list.js +15 -462
- package/dist/ui/session/action-dispatcher.js +233 -0
- package/dist/ui/session/index.js +13 -0
- package/dist/ui/session/interactive-session.js +280 -0
- package/dist/ui/session/selection-state-builder.js +144 -0
- package/dist/ui/state/filter-manager.js +17 -6
- package/dist/ui/state/modal-manager.js +35 -0
- package/dist/ui/state/navigation-manager.js +33 -4
- package/dist/ui/state/state-manager.js +68 -3
- package/dist/ui/state/theme-manager.js +1 -0
- package/dist/ui/themes-colors.js +8 -0
- package/dist/ui/themes.js +11 -19
- package/dist/ui/utils/cursor.js +3 -4
- package/dist/ui/utils/index.js +6 -1
- package/dist/ui/utils/text.js +3 -2
- package/dist/ui/utils/version.js +60 -86
- package/dist/utils/config.js +13 -1
- package/dist/utils/filesystem/io.js +87 -0
- package/dist/utils/filesystem/paths.js +19 -0
- package/dist/utils/filesystem/scan.js +205 -0
- package/dist/utils/filesystem.js +17 -335
- package/dist/utils/version.js +31 -1
- package/package.json +10 -9
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dispatchAction = dispatchAction;
|
|
4
|
+
const themes_1 = require("../themes");
|
|
5
|
+
const INTERACTIVE_ACTIONS = new Set([
|
|
6
|
+
'navigate_up',
|
|
7
|
+
'navigate_down',
|
|
8
|
+
'navigate_top',
|
|
9
|
+
'navigate_bottom',
|
|
10
|
+
'select_left',
|
|
11
|
+
'select_right',
|
|
12
|
+
'toggle_selection',
|
|
13
|
+
'bulk_select_minor',
|
|
14
|
+
'bulk_select_latest',
|
|
15
|
+
'bulk_unselect_all',
|
|
16
|
+
'toggle_dep_type_filter',
|
|
17
|
+
'toggle_vulnerable_filter',
|
|
18
|
+
]);
|
|
19
|
+
function dispatchAction(action, ctx) {
|
|
20
|
+
const { stateManager, states, vulnerabilityDisplayOptions, packageInfoModalController, vulnerabilityAuditController, isResolved, renderInterface, handleCancel, getInfoModalMaxScrollOffset, getDebugModalMaxScrollOffset, getHelpModalMaxScrollOffset, } = ctx;
|
|
21
|
+
const uiState = stateManager.getUIState();
|
|
22
|
+
const filteredStates = stateManager.getFilteredStates(states, vulnerabilityDisplayOptions);
|
|
23
|
+
// Any deliberate action clears a one-shot status notice (except the action that sets it).
|
|
24
|
+
if (action.type !== 'notify_empty_selection') {
|
|
25
|
+
stateManager.clearNotice();
|
|
26
|
+
}
|
|
27
|
+
if (uiState.showThemeModal && INTERACTIVE_ACTIONS.has(action.type))
|
|
28
|
+
return;
|
|
29
|
+
// Shared by `s` (audit) and `v` (vulnerable filter): run the scan if we have no
|
|
30
|
+
// data yet, otherwise toggle the vulnerable-only filter.
|
|
31
|
+
const auditOrToggleVulnerable = () => {
|
|
32
|
+
const auditProgress = vulnerabilityAuditController.getProgress();
|
|
33
|
+
if (auditProgress.hasData) {
|
|
34
|
+
stateManager.toggleVulnerableFilter();
|
|
35
|
+
}
|
|
36
|
+
else if (!auditProgress.isRunning) {
|
|
37
|
+
vulnerabilityAuditController.enqueueStates(states, () => {
|
|
38
|
+
if (!isResolved())
|
|
39
|
+
renderInterface();
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
switch (action.type) {
|
|
44
|
+
case 'navigate_up':
|
|
45
|
+
stateManager.navigateUp(filteredStates.length);
|
|
46
|
+
break;
|
|
47
|
+
case 'navigate_down':
|
|
48
|
+
stateManager.navigateDown(filteredStates.length);
|
|
49
|
+
break;
|
|
50
|
+
case 'navigate_top':
|
|
51
|
+
stateManager.navigateTop(filteredStates.length);
|
|
52
|
+
break;
|
|
53
|
+
case 'navigate_bottom':
|
|
54
|
+
stateManager.navigateBottom(filteredStates.length);
|
|
55
|
+
break;
|
|
56
|
+
case 'select_left':
|
|
57
|
+
stateManager.updateSelection(filteredStates, 'left');
|
|
58
|
+
break;
|
|
59
|
+
case 'select_right':
|
|
60
|
+
stateManager.updateSelection(filteredStates, 'right');
|
|
61
|
+
break;
|
|
62
|
+
case 'toggle_selection':
|
|
63
|
+
stateManager.toggleSelection(filteredStates);
|
|
64
|
+
break;
|
|
65
|
+
case 'notify_empty_selection':
|
|
66
|
+
stateManager.setNotice('Nothing selected — use ←/→ or Space to choose updates, then Enter');
|
|
67
|
+
break;
|
|
68
|
+
case 'bulk_select_minor':
|
|
69
|
+
stateManager.bulkSelectMinor(filteredStates);
|
|
70
|
+
break;
|
|
71
|
+
case 'bulk_select_latest':
|
|
72
|
+
stateManager.bulkSelectLatest(filteredStates);
|
|
73
|
+
break;
|
|
74
|
+
case 'bulk_unselect_all':
|
|
75
|
+
stateManager.bulkUnselectAll(filteredStates);
|
|
76
|
+
break;
|
|
77
|
+
case 'toggle_dep_type_filter':
|
|
78
|
+
stateManager.toggleDependencyTypeFilter(action.depType);
|
|
79
|
+
break;
|
|
80
|
+
case 'toggle_info_modal':
|
|
81
|
+
if (!uiState.showInfoModal) {
|
|
82
|
+
const modalSessionId = stateManager.toggleInfoModal();
|
|
83
|
+
const currentState = filteredStates[uiState.currentRow];
|
|
84
|
+
const canFetchMetadata = currentState?.loadState === 'ready';
|
|
85
|
+
stateManager.setModalLoading(canFetchMetadata, modalSessionId);
|
|
86
|
+
renderInterface();
|
|
87
|
+
if (currentState && canFetchMetadata) {
|
|
88
|
+
packageInfoModalController
|
|
89
|
+
.hydrate(currentState)
|
|
90
|
+
.then((update) => {
|
|
91
|
+
if (isResolved() || stateManager.getInfoModalSessionId() !== modalSessionId)
|
|
92
|
+
return;
|
|
93
|
+
if (update)
|
|
94
|
+
Object.assign(currentState, update.patch);
|
|
95
|
+
stateManager.setModalLoading(false, modalSessionId);
|
|
96
|
+
renderInterface();
|
|
97
|
+
if (stateManager.getInfoModalSessionId() === modalSessionId &&
|
|
98
|
+
packageInfoModalController.getVersionCount(currentState) > 0) {
|
|
99
|
+
packageInfoModalController.loadVersionAtIndex(currentState, 0, () => {
|
|
100
|
+
if (!isResolved())
|
|
101
|
+
renderInterface();
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
.catch(() => {
|
|
106
|
+
if (isResolved() || stateManager.getInfoModalSessionId() !== modalSessionId)
|
|
107
|
+
return;
|
|
108
|
+
stateManager.setModalLoading(false, modalSessionId);
|
|
109
|
+
renderInterface();
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
packageInfoModalController.cancel();
|
|
115
|
+
stateManager.toggleInfoModal();
|
|
116
|
+
renderInterface();
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
case 'scroll_info_modal_up':
|
|
120
|
+
if (!stateManager.scrollInfoModalUp())
|
|
121
|
+
return;
|
|
122
|
+
break;
|
|
123
|
+
case 'scroll_info_modal_down':
|
|
124
|
+
if (!stateManager.scrollInfoModalDown(getInfoModalMaxScrollOffset()))
|
|
125
|
+
return;
|
|
126
|
+
break;
|
|
127
|
+
case 'toggle_debug_modal':
|
|
128
|
+
stateManager.toggleDebugModal();
|
|
129
|
+
break;
|
|
130
|
+
case 'toggle_help_modal':
|
|
131
|
+
stateManager.toggleHelpModal();
|
|
132
|
+
break;
|
|
133
|
+
case 'scroll_help_modal_up':
|
|
134
|
+
if (!stateManager.scrollHelpModalUp())
|
|
135
|
+
return;
|
|
136
|
+
break;
|
|
137
|
+
case 'scroll_help_modal_down':
|
|
138
|
+
if (!stateManager.scrollHelpModalDown(getHelpModalMaxScrollOffset()))
|
|
139
|
+
return;
|
|
140
|
+
break;
|
|
141
|
+
case 'scroll_debug_modal_up':
|
|
142
|
+
if (!stateManager.scrollDebugModalUp())
|
|
143
|
+
return;
|
|
144
|
+
break;
|
|
145
|
+
case 'scroll_debug_modal_down':
|
|
146
|
+
if (!stateManager.scrollDebugModalDown(getDebugModalMaxScrollOffset()))
|
|
147
|
+
return;
|
|
148
|
+
break;
|
|
149
|
+
case 'switch_info_modal_tab': {
|
|
150
|
+
const nextTab = stateManager.getInfoModalTab() === 'info' ? 'usedBy' : 'info';
|
|
151
|
+
stateManager.setInfoModalTab(nextTab);
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
case 'navigate_info_modal_version': {
|
|
155
|
+
if (uiState.infoModalRow >= 0 && uiState.infoModalRow < filteredStates.length) {
|
|
156
|
+
const currentState = filteredStates[uiState.infoModalRow];
|
|
157
|
+
const newIndex = packageInfoModalController.navigateVersion(currentState, action.direction);
|
|
158
|
+
if (newIndex >= 0) {
|
|
159
|
+
stateManager.resetInfoModalScroll();
|
|
160
|
+
if (!packageInfoModalController.isVersionLoaded(currentState, newIndex)) {
|
|
161
|
+
packageInfoModalController.loadVersionAtIndex(currentState, newIndex, () => {
|
|
162
|
+
if (!isResolved())
|
|
163
|
+
renderInterface();
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
case 'enter_filter_mode':
|
|
177
|
+
stateManager.enterFilterMode(action.preserveQuery);
|
|
178
|
+
break;
|
|
179
|
+
case 'exit_filter_mode':
|
|
180
|
+
stateManager.exitFilterMode(action.clearQuery);
|
|
181
|
+
break;
|
|
182
|
+
case 'filter_input':
|
|
183
|
+
stateManager.appendToFilterQuery(action.char);
|
|
184
|
+
break;
|
|
185
|
+
case 'filter_backspace':
|
|
186
|
+
stateManager.deleteFromFilterQuery();
|
|
187
|
+
break;
|
|
188
|
+
case 'resize': {
|
|
189
|
+
const heightChanged = stateManager.updateTerminalHeight(action.height);
|
|
190
|
+
if (heightChanged) {
|
|
191
|
+
stateManager.resetForResize(filteredStates.length);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
stateManager.setInitialRender(true);
|
|
195
|
+
}
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
case 'toggle_theme_modal':
|
|
199
|
+
stateManager.toggleThemeModal();
|
|
200
|
+
break;
|
|
201
|
+
case 'theme_navigate_up': {
|
|
202
|
+
const themeManager = stateManager.getThemeManager();
|
|
203
|
+
const currentIndex = themes_1.themeNames.indexOf(themeManager.getPreviewTheme());
|
|
204
|
+
const themeArray = Object.keys(themes_1.themes);
|
|
205
|
+
const nextIndex = currentIndex > 0 ? currentIndex - 1 : themeArray.length - 1;
|
|
206
|
+
stateManager.previewTheme(themeArray[nextIndex]);
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
case 'theme_navigate_down': {
|
|
210
|
+
const themeManager = stateManager.getThemeManager();
|
|
211
|
+
const currentIndex = themes_1.themeNames.indexOf(themeManager.getPreviewTheme());
|
|
212
|
+
const themeArray = Object.keys(themes_1.themes);
|
|
213
|
+
const nextIndex = currentIndex < themeArray.length - 1 ? currentIndex + 1 : 0;
|
|
214
|
+
stateManager.previewTheme(themeArray[nextIndex]);
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
case 'theme_confirm':
|
|
218
|
+
stateManager.confirmTheme();
|
|
219
|
+
break;
|
|
220
|
+
case 'trigger_audit_scan':
|
|
221
|
+
auditOrToggleVulnerable();
|
|
222
|
+
break;
|
|
223
|
+
case 'toggle_vulnerable_filter':
|
|
224
|
+
auditOrToggleVulnerable();
|
|
225
|
+
break;
|
|
226
|
+
case 'cancel':
|
|
227
|
+
packageInfoModalController.cancel();
|
|
228
|
+
handleCancel();
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
renderInterface();
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=action-dispatcher.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dispatchAction = exports.deduplicatePackages = exports.createUpgradeChoices = exports.createPendingSelectionStates = exports.createSelectionStates = exports.runInteractiveSession = void 0;
|
|
4
|
+
var interactive_session_1 = require("./interactive-session");
|
|
5
|
+
Object.defineProperty(exports, "runInteractiveSession", { enumerable: true, get: function () { return interactive_session_1.runInteractiveSession; } });
|
|
6
|
+
var selection_state_builder_1 = require("./selection-state-builder");
|
|
7
|
+
Object.defineProperty(exports, "createSelectionStates", { enumerable: true, get: function () { return selection_state_builder_1.createSelectionStates; } });
|
|
8
|
+
Object.defineProperty(exports, "createPendingSelectionStates", { enumerable: true, get: function () { return selection_state_builder_1.createPendingSelectionStates; } });
|
|
9
|
+
Object.defineProperty(exports, "createUpgradeChoices", { enumerable: true, get: function () { return selection_state_builder_1.createUpgradeChoices; } });
|
|
10
|
+
Object.defineProperty(exports, "deduplicatePackages", { enumerable: true, get: function () { return selection_state_builder_1.deduplicatePackages; } });
|
|
11
|
+
var action_dispatcher_1 = require("./action-dispatcher");
|
|
12
|
+
Object.defineProperty(exports, "dispatchAction", { enumerable: true, get: function () { return action_dispatcher_1.dispatchAction; } });
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,280 @@
|
|
|
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.runInteractiveSession = runInteractiveSession;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const index_1 = require("../index");
|
|
9
|
+
const cursor_1 = require("../utils/cursor");
|
|
10
|
+
const themes_colors_1 = require("../themes-colors");
|
|
11
|
+
const debug_1 = require("../../features/debug");
|
|
12
|
+
const help_modal_1 = require("../renderer/help-modal");
|
|
13
|
+
const config_1 = require("../../utils/config");
|
|
14
|
+
const action_dispatcher_1 = require("./action-dispatcher");
|
|
15
|
+
function getTerminalHeight() {
|
|
16
|
+
if (process.stdout.isTTY && typeof process.stdout.rows === 'number' && process.stdout.rows > 0) {
|
|
17
|
+
return process.stdout.rows;
|
|
18
|
+
}
|
|
19
|
+
return 24;
|
|
20
|
+
}
|
|
21
|
+
async function runInteractiveSession(selectionStates, packageManager, renderer, packageInfoModalController, vulnerabilityAuditController, options, onRefreshViewReady, loadingProgress, attachRefresh) {
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
const states = selectionStates;
|
|
24
|
+
const stateManager = new index_1.StateManager(0, getTerminalHeight(), config_1.configManager.getFilters() ?? undefined);
|
|
25
|
+
let isResolved = false;
|
|
26
|
+
let ownsAlternateScreen = false;
|
|
27
|
+
const vulnerabilityDisplayOptions = options;
|
|
28
|
+
let infoModalMaxScrollOffset = 0;
|
|
29
|
+
let debugModalMaxScrollOffset = 0;
|
|
30
|
+
let helpModalMaxScrollOffset = 0;
|
|
31
|
+
let previousViewportMode = null;
|
|
32
|
+
let previousModalViewportLineCount = null;
|
|
33
|
+
stateManager.setRenderableItems([]);
|
|
34
|
+
const claimInteractiveScreen = () => {
|
|
35
|
+
if (ownsAlternateScreen)
|
|
36
|
+
return;
|
|
37
|
+
index_1.ConsoleUtils.clearProgress();
|
|
38
|
+
index_1.CursorUtils.enterAlternateScreen();
|
|
39
|
+
index_1.CursorUtils.clearScreen();
|
|
40
|
+
ownsAlternateScreen = true;
|
|
41
|
+
};
|
|
42
|
+
const releaseInteractiveScreen = () => {
|
|
43
|
+
if (!ownsAlternateScreen)
|
|
44
|
+
return;
|
|
45
|
+
index_1.CursorUtils.exitAlternateScreen();
|
|
46
|
+
ownsAlternateScreen = false;
|
|
47
|
+
};
|
|
48
|
+
const resetAnsiPattern = /\x1b\[(?:0|49)m/g;
|
|
49
|
+
const packageListRenderOptions = {
|
|
50
|
+
showPeerDependencyVulnerabilities: options.showPeerDependencyVulnerabilities,
|
|
51
|
+
showOptionalDependencyVulnerabilities: options.showOptionalDependencyVulnerabilities,
|
|
52
|
+
};
|
|
53
|
+
const key = (text) => chalk_1.default.bold.white(text);
|
|
54
|
+
const hint = (text) => chalk_1.default.gray(text);
|
|
55
|
+
const sep = hint(' · ');
|
|
56
|
+
const SHORTCUTS = {
|
|
57
|
+
scroll: key('↑/↓ ') + hint('Scroll'),
|
|
58
|
+
version: key('←/→ ') + hint('Version'),
|
|
59
|
+
switchTab: key('Tab ') + hint('Switch tab'),
|
|
60
|
+
closeInfo: key('I / Esc ') + hint('Close'),
|
|
61
|
+
closeTheme: key('T / Esc ') + hint('Close'),
|
|
62
|
+
closeHelp: key('? / Esc ') + hint('Close'),
|
|
63
|
+
};
|
|
64
|
+
const buildModalHeaderLines = (shortcutLabel) => [
|
|
65
|
+
' ' + chalk_1.default.bold('🚀 ') + (0, themes_colors_1.coloredInupLogo)(),
|
|
66
|
+
'',
|
|
67
|
+
' ' + shortcutLabel,
|
|
68
|
+
'',
|
|
69
|
+
];
|
|
70
|
+
const buildRemainingViewport = (terminalWidth, terminalHeight, usedLines) => {
|
|
71
|
+
const remainingLines = Math.max(0, terminalHeight - usedLines);
|
|
72
|
+
const blankLine = ' '.repeat(terminalWidth);
|
|
73
|
+
return Array.from({ length: remainingLines }, () => blankLine);
|
|
74
|
+
};
|
|
75
|
+
const applyBackgroundToLine = (line, bgCode) => `${bgCode}${line.replace(resetAnsiPattern, (match) => `${match}${bgCode}`)}${(0, themes_colors_1.getTerminalResetCode)()}`;
|
|
76
|
+
const writeFrame = (lines, bgCode) => {
|
|
77
|
+
if (lines.length === 0)
|
|
78
|
+
return;
|
|
79
|
+
process.stdout.write(lines.map((line) => applyBackgroundToLine(line, bgCode)).join('\n'));
|
|
80
|
+
};
|
|
81
|
+
const renderViewport = (lines, terminalWidth, terminalHeight, bgCode) => {
|
|
82
|
+
const viewportLines = [
|
|
83
|
+
...lines,
|
|
84
|
+
...buildRemainingViewport(terminalWidth, terminalHeight, lines.length),
|
|
85
|
+
];
|
|
86
|
+
writeFrame(viewportLines, bgCode);
|
|
87
|
+
};
|
|
88
|
+
const renderModalViewport = (mode, shortcutLabel, modalLines, terminalWidth, terminalHeight, bgCode) => {
|
|
89
|
+
const viewportLineCount = buildModalHeaderLines(shortcutLabel).length + modalLines.length;
|
|
90
|
+
const shouldClearBeforeRender = previousViewportMode !== mode || previousModalViewportLineCount !== viewportLineCount;
|
|
91
|
+
if (shouldClearBeforeRender) {
|
|
92
|
+
index_1.CursorUtils.clearScreen();
|
|
93
|
+
index_1.CursorUtils.hide();
|
|
94
|
+
}
|
|
95
|
+
renderViewport([...buildModalHeaderLines(shortcutLabel), ...modalLines], terminalWidth, terminalHeight, bgCode);
|
|
96
|
+
previousViewportMode = mode;
|
|
97
|
+
previousModalViewportLineCount = viewportLineCount;
|
|
98
|
+
stateManager.markRendered([]);
|
|
99
|
+
};
|
|
100
|
+
const renderInterface = () => {
|
|
101
|
+
const uiState = stateManager.getUIState();
|
|
102
|
+
const filteredStates = stateManager.getFilteredStates(states, vulnerabilityDisplayOptions);
|
|
103
|
+
const auditProgress = vulnerabilityAuditController.getProgress();
|
|
104
|
+
const bgCode = (0, themes_colors_1.getTerminalBgColorCode)();
|
|
105
|
+
process.stdout.write(bgCode);
|
|
106
|
+
if (uiState.forceFullRender) {
|
|
107
|
+
index_1.CursorUtils.clearScreen();
|
|
108
|
+
index_1.CursorUtils.hide();
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
index_1.CursorUtils.moveToHome();
|
|
112
|
+
}
|
|
113
|
+
if (uiState.showThemeModal) {
|
|
114
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
115
|
+
const terminalHeight = getTerminalHeight();
|
|
116
|
+
const themeManager = stateManager.getThemeManager();
|
|
117
|
+
const modalLines = renderer.renderThemeSelectorModal(themeManager.getCurrentTheme(), themeManager.getPreviewTheme(), terminalWidth, Math.max(8, terminalHeight - 8));
|
|
118
|
+
renderModalViewport('theme-modal', SHORTCUTS.closeTheme, modalLines, terminalWidth, terminalHeight, bgCode);
|
|
119
|
+
}
|
|
120
|
+
else if (uiState.showHelpModal) {
|
|
121
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
122
|
+
const terminalHeight = getTerminalHeight();
|
|
123
|
+
const result = (0, help_modal_1.renderHelpModal)(terminalWidth, Math.max(8, terminalHeight - 4), uiState.helpModalScrollOffset);
|
|
124
|
+
helpModalMaxScrollOffset = result.maxScrollOffset;
|
|
125
|
+
stateManager.clampHelpModalScrollOffset(helpModalMaxScrollOffset);
|
|
126
|
+
const helpHints = [
|
|
127
|
+
result.usesInternalScroll && result.maxScrollOffset > 0 ? SHORTCUTS.scroll : '',
|
|
128
|
+
SHORTCUTS.closeHelp,
|
|
129
|
+
]
|
|
130
|
+
.filter(Boolean)
|
|
131
|
+
.join(sep);
|
|
132
|
+
renderModalViewport('info-modal', helpHints, result.lines, terminalWidth, terminalHeight, bgCode);
|
|
133
|
+
}
|
|
134
|
+
else if (uiState.showDebugModal) {
|
|
135
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
136
|
+
const terminalHeight = getTerminalHeight();
|
|
137
|
+
const snapshot = (0, debug_1.getPerformanceTracker)().snapshot();
|
|
138
|
+
const result = (0, debug_1.renderPerformanceModal)(snapshot, terminalWidth, Math.max(8, terminalHeight - 4), uiState.debugModalScrollOffset);
|
|
139
|
+
debugModalMaxScrollOffset = result.maxScrollOffset;
|
|
140
|
+
stateManager.clampDebugModalScrollOffset(debugModalMaxScrollOffset);
|
|
141
|
+
const debugHints = [
|
|
142
|
+
result.usesInternalScroll && result.maxScrollOffset > 0 ? SHORTCUTS.scroll : '',
|
|
143
|
+
SHORTCUTS.closeInfo,
|
|
144
|
+
]
|
|
145
|
+
.filter(Boolean)
|
|
146
|
+
.join(sep);
|
|
147
|
+
renderModalViewport('info-modal', debugHints, result.lines, terminalWidth, terminalHeight, bgCode);
|
|
148
|
+
}
|
|
149
|
+
else if (uiState.showInfoModal &&
|
|
150
|
+
uiState.infoModalRow >= 0 &&
|
|
151
|
+
uiState.infoModalRow < filteredStates.length) {
|
|
152
|
+
const selectedState = filteredStates[uiState.infoModalRow];
|
|
153
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
154
|
+
const terminalHeight = getTerminalHeight();
|
|
155
|
+
if (uiState.isLoadingModalInfo) {
|
|
156
|
+
const result = renderer.renderPackageInfoLoading(selectedState, terminalWidth, Math.max(8, terminalHeight - 8));
|
|
157
|
+
infoModalMaxScrollOffset = result.maxScrollOffset;
|
|
158
|
+
renderModalViewport('info-modal', SHORTCUTS.closeInfo, result.lines, terminalWidth, terminalHeight, bgCode);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
const activeTab = uiState.infoModalTab;
|
|
162
|
+
const result = renderer.renderPackageInfoModal(selectedState, terminalWidth, Math.max(8, terminalHeight - 4), uiState.infoModalScrollOffset, activeTab);
|
|
163
|
+
infoModalMaxScrollOffset = result.maxScrollOffset;
|
|
164
|
+
stateManager.clampInfoModalScrollOffset(infoModalMaxScrollOffset);
|
|
165
|
+
const hints = [
|
|
166
|
+
result.usesInternalScroll && result.maxScrollOffset > 0 ? SHORTCUTS.scroll : '',
|
|
167
|
+
activeTab === 'info' ? SHORTCUTS.version : '',
|
|
168
|
+
SHORTCUTS.switchTab,
|
|
169
|
+
SHORTCUTS.closeInfo,
|
|
170
|
+
]
|
|
171
|
+
.filter(Boolean)
|
|
172
|
+
.join(sep);
|
|
173
|
+
renderModalViewport('info-modal', hints, result.lines, terminalWidth, terminalHeight, bgCode);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
178
|
+
const terminalHeight = getTerminalHeight();
|
|
179
|
+
const activeFilterLabel = stateManager.getActiveFilterLabel();
|
|
180
|
+
const lines = renderer.renderInterface(filteredStates, uiState.currentRow, uiState.scrollOffset, uiState.maxVisibleItems, uiState.forceFullRender, [], activeFilterLabel, packageManager, uiState.filterMode, uiState.filterQuery, states.length, terminalWidth, loadingProgress, auditProgress, packageListRenderOptions, uiState.notice);
|
|
181
|
+
renderViewport(lines, terminalWidth, terminalHeight, bgCode);
|
|
182
|
+
previousViewportMode = 'list';
|
|
183
|
+
previousModalViewportLineCount = null;
|
|
184
|
+
stateManager.markRendered(lines);
|
|
185
|
+
}
|
|
186
|
+
stateManager.setInitialRender(false);
|
|
187
|
+
};
|
|
188
|
+
// Safety net: restore terminal if the process exits without going through finalizeSelection.
|
|
189
|
+
// Only synchronous writes work in an 'exit' handler, but that's all we need here.
|
|
190
|
+
const emergencyCleanup = () => {
|
|
191
|
+
if (ownsAlternateScreen) {
|
|
192
|
+
process.stdout.write(cursor_1.RAW_EXIT_ALT_SCREEN);
|
|
193
|
+
}
|
|
194
|
+
process.stdout.write(cursor_1.RAW_SHOW_CURSOR);
|
|
195
|
+
if (process.stdin.setRawMode) {
|
|
196
|
+
process.stdin.setRawMode(false);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
process.on('exit', emergencyCleanup);
|
|
200
|
+
let cleanupInteractiveSession = () => {
|
|
201
|
+
process.stdout.write((0, themes_colors_1.getTerminalResetCode)());
|
|
202
|
+
index_1.CursorUtils.show();
|
|
203
|
+
process.stdin.off('keypress', keypressHandler);
|
|
204
|
+
process.stdin.pause();
|
|
205
|
+
process.off('SIGWINCH', handleResize);
|
|
206
|
+
};
|
|
207
|
+
const finalizeSelection = (selectedStates) => {
|
|
208
|
+
isResolved = true;
|
|
209
|
+
onRefreshViewReady?.(undefined);
|
|
210
|
+
packageInfoModalController.cancel();
|
|
211
|
+
// Remember the view filters for next launch (best-effort, never throws).
|
|
212
|
+
config_1.configManager.setFilters(stateManager.getFilterSnapshot());
|
|
213
|
+
releaseInteractiveScreen();
|
|
214
|
+
cleanupInteractiveSession();
|
|
215
|
+
process.off('exit', emergencyCleanup);
|
|
216
|
+
resolve(selectedStates);
|
|
217
|
+
};
|
|
218
|
+
const handleConfirm = (confirmed) => {
|
|
219
|
+
finalizeSelection(confirmed);
|
|
220
|
+
};
|
|
221
|
+
const handleCancel = () => {
|
|
222
|
+
finalizeSelection(states.map((s) => ({ ...s, selectedOption: 'none' })));
|
|
223
|
+
};
|
|
224
|
+
const inputHandler = new index_1.InputHandler(stateManager, (action) => (0, action_dispatcher_1.dispatchAction)(action, {
|
|
225
|
+
stateManager,
|
|
226
|
+
states,
|
|
227
|
+
vulnerabilityDisplayOptions,
|
|
228
|
+
packageInfoModalController,
|
|
229
|
+
vulnerabilityAuditController,
|
|
230
|
+
isResolved: () => isResolved,
|
|
231
|
+
renderInterface,
|
|
232
|
+
handleCancel,
|
|
233
|
+
getInfoModalMaxScrollOffset: () => infoModalMaxScrollOffset,
|
|
234
|
+
getDebugModalMaxScrollOffset: () => debugModalMaxScrollOffset,
|
|
235
|
+
getHelpModalMaxScrollOffset: () => helpModalMaxScrollOffset,
|
|
236
|
+
}), handleConfirm, handleCancel);
|
|
237
|
+
const keypressHandler = (str, key) => inputHandler.handleKeypress(str, key, states);
|
|
238
|
+
const handleResize = () => {
|
|
239
|
+
inputHandler.handleResize(getTerminalHeight());
|
|
240
|
+
};
|
|
241
|
+
try {
|
|
242
|
+
claimInteractiveScreen();
|
|
243
|
+
onRefreshViewReady?.(() => {
|
|
244
|
+
if (!isResolved)
|
|
245
|
+
renderInterface();
|
|
246
|
+
});
|
|
247
|
+
attachRefresh?.(() => {
|
|
248
|
+
if (!isResolved) {
|
|
249
|
+
renderInterface();
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
const keypressSession = index_1.TerminalInput.startKeypressSession(keypressHandler);
|
|
253
|
+
const previousCleanup = cleanupInteractiveSession;
|
|
254
|
+
cleanupInteractiveSession = () => {
|
|
255
|
+
keypressSession.close();
|
|
256
|
+
previousCleanup();
|
|
257
|
+
};
|
|
258
|
+
process.on('SIGWINCH', handleResize);
|
|
259
|
+
const currentHeight = getTerminalHeight();
|
|
260
|
+
if (stateManager.updateTerminalHeight(currentHeight)) {
|
|
261
|
+
const initialFiltered = stateManager.getFilteredStates(states, vulnerabilityDisplayOptions);
|
|
262
|
+
stateManager.resetForResize(initialFiltered.length);
|
|
263
|
+
}
|
|
264
|
+
renderInterface();
|
|
265
|
+
vulnerabilityAuditController.enqueueStates(states, () => {
|
|
266
|
+
if (!isResolved)
|
|
267
|
+
renderInterface();
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
onRefreshViewReady?.(undefined);
|
|
272
|
+
process.off('exit', emergencyCleanup);
|
|
273
|
+
releaseInteractiveScreen();
|
|
274
|
+
process.stdout.write((0, themes_colors_1.getTerminalResetCode)());
|
|
275
|
+
console.log(chalk_1.default.yellow('Raw mode not available, using fallback interface...'));
|
|
276
|
+
resolve(states);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
//# sourceMappingURL=interactive-session.js.map
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.deduplicatePackages = deduplicatePackages;
|
|
37
|
+
exports.createSelectionStates = createSelectionStates;
|
|
38
|
+
exports.createPendingSelectionStates = createPendingSelectionStates;
|
|
39
|
+
exports.createUpgradeChoices = createUpgradeChoices;
|
|
40
|
+
const semver = __importStar(require("semver"));
|
|
41
|
+
const utils_1 = require("../utils");
|
|
42
|
+
function deduplicatePackages(packages) {
|
|
43
|
+
const uniquePackages = new Map();
|
|
44
|
+
for (const pkg of packages) {
|
|
45
|
+
const key = `${pkg.name}@${pkg.currentVersion}@${pkg.type}`;
|
|
46
|
+
if (!uniquePackages.has(key)) {
|
|
47
|
+
uniquePackages.set(key, {
|
|
48
|
+
pkg,
|
|
49
|
+
packageJsonPaths: new Set([pkg.packageJsonPath]),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
uniquePackages.get(key).packageJsonPaths.add(pkg.packageJsonPath);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return new Map(Array.from(uniquePackages.entries()).sort(([, a], [, b]) => {
|
|
57
|
+
const aIsScoped = a.pkg.name.startsWith('@');
|
|
58
|
+
const bIsScoped = b.pkg.name.startsWith('@');
|
|
59
|
+
if (aIsScoped && !bIsScoped)
|
|
60
|
+
return -1;
|
|
61
|
+
if (!aIsScoped && bIsScoped)
|
|
62
|
+
return 1;
|
|
63
|
+
return a.pkg.name.localeCompare(b.pkg.name);
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
function createSelectionStates(packages, getCachedSummary, previousSelections, includeUpToDate = true) {
|
|
67
|
+
const relevantPackages = includeUpToDate ? packages : packages.filter((p) => p.isOutdated);
|
|
68
|
+
const uniquePackages = deduplicatePackages(relevantPackages);
|
|
69
|
+
return Array.from(uniquePackages.values()).map(({ pkg, packageJsonPaths }) => {
|
|
70
|
+
const currentClean = semver.coerce(pkg.currentVersion)?.version || pkg.currentVersion;
|
|
71
|
+
const rangeClean = semver.coerce(pkg.rangeVersion)?.version || pkg.rangeVersion;
|
|
72
|
+
const latestClean = semver.coerce(pkg.latestVersion)?.version || pkg.latestVersion;
|
|
73
|
+
const key = `${pkg.name}@${pkg.currentVersion}@${pkg.type}`;
|
|
74
|
+
const previousSelection = previousSelections?.get(key) || 'none';
|
|
75
|
+
return {
|
|
76
|
+
name: pkg.name,
|
|
77
|
+
packageJsonPath: pkg.packageJsonPath,
|
|
78
|
+
packageJsonPaths: Array.from(packageJsonPaths),
|
|
79
|
+
currentVersionSpecifier: pkg.currentVersion,
|
|
80
|
+
currentVersion: currentClean,
|
|
81
|
+
rangeVersion: rangeClean,
|
|
82
|
+
latestVersion: latestClean,
|
|
83
|
+
selectedOption: previousSelection,
|
|
84
|
+
loadState: 'ready',
|
|
85
|
+
hasRangeUpdate: pkg.hasRangeUpdate,
|
|
86
|
+
hasMajorUpdate: pkg.hasMajorUpdate,
|
|
87
|
+
type: pkg.type,
|
|
88
|
+
vulnerability: getCachedSummary(pkg.name, pkg.currentVersion, pkg.type),
|
|
89
|
+
allVersions: pkg.allVersions,
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function createPendingSelectionStates(packages, getCachedSummary, previousSelections) {
|
|
94
|
+
const uniquePackages = deduplicatePackages(packages.map((pkg) => ({
|
|
95
|
+
...pkg,
|
|
96
|
+
rangeVersion: pkg.currentVersion,
|
|
97
|
+
latestVersion: pkg.currentVersion,
|
|
98
|
+
isOutdated: false,
|
|
99
|
+
hasRangeUpdate: false,
|
|
100
|
+
hasMajorUpdate: false,
|
|
101
|
+
})));
|
|
102
|
+
return Array.from(uniquePackages.values()).map(({ pkg, packageJsonPaths }) => {
|
|
103
|
+
const currentClean = semver.coerce(pkg.currentVersion)?.version || pkg.currentVersion;
|
|
104
|
+
const key = `${pkg.name}@${pkg.currentVersion}@${pkg.type}`;
|
|
105
|
+
const previousSelection = previousSelections?.get(key) || 'none';
|
|
106
|
+
return {
|
|
107
|
+
name: pkg.name,
|
|
108
|
+
packageJsonPath: pkg.packageJsonPath,
|
|
109
|
+
packageJsonPaths: Array.from(packageJsonPaths),
|
|
110
|
+
currentVersionSpecifier: pkg.currentVersion,
|
|
111
|
+
currentVersion: currentClean,
|
|
112
|
+
rangeVersion: 'loading',
|
|
113
|
+
latestVersion: 'loading',
|
|
114
|
+
selectedOption: previousSelection,
|
|
115
|
+
loadState: 'pending',
|
|
116
|
+
hasRangeUpdate: false,
|
|
117
|
+
hasMajorUpdate: false,
|
|
118
|
+
type: pkg.type,
|
|
119
|
+
vulnerability: getCachedSummary(pkg.name, pkg.currentVersion, pkg.type),
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
function createUpgradeChoices(selectedStates) {
|
|
124
|
+
const choices = [];
|
|
125
|
+
selectedStates
|
|
126
|
+
.filter((state) => state.loadState === 'ready' && state.selectedOption !== 'none')
|
|
127
|
+
.forEach((state) => {
|
|
128
|
+
const targetVersion = state.selectedOption === 'range' ? state.rangeVersion : state.latestVersion;
|
|
129
|
+
const targetVersionWithPrefix = (0, utils_1.applyVersionPrefix)(state.currentVersionSpecifier, targetVersion);
|
|
130
|
+
const pathsToUpdate = state.packageJsonPaths || [state.packageJsonPath];
|
|
131
|
+
pathsToUpdate.forEach((packageJsonPath) => {
|
|
132
|
+
choices.push({
|
|
133
|
+
name: state.name,
|
|
134
|
+
packageJsonPath,
|
|
135
|
+
dependencyType: state.type,
|
|
136
|
+
upgradeType: state.selectedOption,
|
|
137
|
+
targetVersion: targetVersionWithPrefix,
|
|
138
|
+
currentVersionSpecifier: state.currentVersionSpecifier,
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
return choices;
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=selection-state-builder.js.map
|