inup 1.4.10 → 1.5.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.
- package/README.md +1 -7
- package/dist/cli.js +2 -1
- package/dist/config/constants.js +1 -2
- package/dist/config/project-config.js +6 -0
- package/dist/core/package-detector.js +163 -89
- package/dist/core/upgrade-runner.js +68 -16
- package/dist/features/changelog/clients/github-client.js +134 -0
- package/dist/features/changelog/clients/npm-registry-client.js +53 -0
- package/dist/features/changelog/index.js +19 -0
- package/dist/features/changelog/parsers/changelog-parser.js +68 -0
- package/dist/features/changelog/parsers/github-release-html-parser.js +61 -0
- package/dist/features/changelog/parsers/package-metadata.js +34 -0
- package/dist/features/changelog/parsers/repository-ref.js +26 -0
- package/dist/features/changelog/services/changelog-service.js +30 -0
- package/dist/features/changelog/services/package-metadata-service.js +108 -0
- package/dist/features/changelog/services/release-notes-service.js +180 -0
- package/dist/features/changelog/types/changelog.types.js +3 -0
- package/dist/interactive-ui.js +343 -161
- package/dist/services/background-audit.js +60 -0
- package/dist/services/index.js +3 -3
- package/dist/services/jsdelivr-registry.js +92 -176
- package/dist/services/npm-registry.js +97 -27
- package/dist/services/vulnerability-checker.js +133 -0
- package/dist/ui/controllers/index.js +8 -0
- package/dist/ui/controllers/package-info-modal-controller.js +237 -0
- package/dist/ui/controllers/vulnerability-audit-controller.js +82 -0
- package/dist/ui/index.js +3 -0
- package/dist/ui/input-handler.js +41 -10
- package/dist/ui/modal/index.js +22 -0
- package/dist/ui/modal/layout.js +84 -0
- package/dist/ui/modal/package-info-sections.js +327 -0
- package/dist/ui/modal/package-info.js +147 -0
- package/dist/ui/modal/theme-selector.js +46 -0
- package/dist/ui/modal/types.js +3 -0
- package/dist/ui/presenters/index.js +11 -0
- package/dist/ui/presenters/vulnerability.js +76 -0
- package/dist/ui/renderer/index.js +9 -11
- package/dist/ui/renderer/package-list.js +166 -66
- package/dist/ui/state/filter-manager.js +17 -2
- package/dist/ui/state/modal-manager.js +48 -6
- package/dist/ui/state/state-manager.js +49 -12
- package/dist/ui/utils/cursor.js +18 -0
- package/dist/ui/utils/index.js +8 -1
- package/dist/ui/utils/terminal-input.js +82 -0
- package/dist/ui/utils/text.js +75 -0
- package/dist/ui/utils/version.js +3 -2
- package/package.json +7 -11
- package/dist/services/changelog-fetcher.js +0 -190
- package/dist/ui/renderer/modal.js +0 -190
- package/dist/ui/renderer/theme-selector.js +0 -83
|
@@ -11,6 +11,11 @@ exports.renderPackagesTable = renderPackagesTable;
|
|
|
11
11
|
const chalk_1 = __importDefault(require("chalk"));
|
|
12
12
|
const utils_1 = require("../utils");
|
|
13
13
|
const themes_colors_1 = require("../themes-colors");
|
|
14
|
+
const vulnerability_1 = require("../presenters/vulnerability");
|
|
15
|
+
function padLineToWidth(line, terminalWidth) {
|
|
16
|
+
const padding = Math.max(0, terminalWidth - utils_1.VersionUtils.getVisualLength(line));
|
|
17
|
+
return line + ' '.repeat(padding);
|
|
18
|
+
}
|
|
14
19
|
/**
|
|
15
20
|
* Get type badge for dependency type (theme-aware)
|
|
16
21
|
*/
|
|
@@ -34,7 +39,7 @@ function getTypeBadge(type) {
|
|
|
34
39
|
* @param isCurrentRow Whether this is the current/highlighted row
|
|
35
40
|
* @param terminalWidth Terminal width for dynamic truncation (default 80)
|
|
36
41
|
*/
|
|
37
|
-
function renderPackageLine(state, index, isCurrentRow, terminalWidth = 80) {
|
|
42
|
+
function renderPackageLine(state, index, isCurrentRow, terminalWidth = 80, options = {}) {
|
|
38
43
|
const prefix = isCurrentRow ? (0, themes_colors_1.getThemeColor)('success')('❯ ') : ' ';
|
|
39
44
|
// Package name with special formatting for scoped packages (@author/package)
|
|
40
45
|
let packageName;
|
|
@@ -44,14 +49,18 @@ function renderPackageLine(state, index, isCurrentRow, terminalWidth = 80) {
|
|
|
44
49
|
const author = parts[0]; // @author
|
|
45
50
|
const packagePart = parts.slice(1).join('/'); // package name
|
|
46
51
|
if (isCurrentRow) {
|
|
47
|
-
packageName =
|
|
52
|
+
packageName =
|
|
53
|
+
chalk_1.default.bold((0, themes_colors_1.getThemeColor)('packageAuthor')(author)) +
|
|
54
|
+
(0, themes_colors_1.getThemeColor)('packageName')('/' + packagePart);
|
|
48
55
|
}
|
|
49
56
|
else {
|
|
50
57
|
packageName = chalk_1.default.bold.white(author) + chalk_1.default.white('/' + packagePart);
|
|
51
58
|
}
|
|
52
59
|
}
|
|
53
60
|
else {
|
|
54
|
-
packageName = isCurrentRow
|
|
61
|
+
packageName = isCurrentRow
|
|
62
|
+
? (0, themes_colors_1.getThemeColor)('packageName')(state.name)
|
|
63
|
+
: chalk_1.default.white(state.name);
|
|
55
64
|
}
|
|
56
65
|
}
|
|
57
66
|
else {
|
|
@@ -61,13 +70,23 @@ function renderPackageLine(state, index, isCurrentRow, terminalWidth = 80) {
|
|
|
61
70
|
const isCurrentSelected = state.selectedOption === 'none';
|
|
62
71
|
const isRangeSelected = state.selectedOption === 'range';
|
|
63
72
|
const isLatestSelected = state.selectedOption === 'latest';
|
|
73
|
+
const isPending = state.loadState === 'pending';
|
|
74
|
+
const isFailed = state.loadState === 'failed';
|
|
64
75
|
// Current version dot and version (show original specifier with prefix)
|
|
65
76
|
const currentDot = isCurrentSelected ? (0, themes_colors_1.getThemeColor)('dot')('●') : (0, themes_colors_1.getThemeColor)('dotEmpty')('○');
|
|
66
77
|
const currentVersion = chalk_1.default.white(state.currentVersionSpecifier);
|
|
67
78
|
// Range version dot and version
|
|
68
79
|
let rangeDot = '';
|
|
69
80
|
let rangeVersionText = '';
|
|
70
|
-
if (
|
|
81
|
+
if (isPending) {
|
|
82
|
+
rangeDot = (0, themes_colors_1.getThemeColor)('dotEmpty')('◌');
|
|
83
|
+
rangeVersionText = chalk_1.default.gray('loading');
|
|
84
|
+
}
|
|
85
|
+
else if (isFailed) {
|
|
86
|
+
rangeDot = (0, themes_colors_1.getThemeColor)('dotEmpty')('◌');
|
|
87
|
+
rangeVersionText = chalk_1.default.gray('unavailable');
|
|
88
|
+
}
|
|
89
|
+
else if (state.hasRangeUpdate) {
|
|
71
90
|
rangeDot = isRangeSelected ? (0, themes_colors_1.getThemeColor)('dot')('●') : (0, themes_colors_1.getThemeColor)('dotEmpty')('○');
|
|
72
91
|
const rangeVersionWithPrefix = utils_1.VersionUtils.applyVersionPrefix(state.currentVersionSpecifier, state.rangeVersion);
|
|
73
92
|
rangeVersionText = (0, themes_colors_1.getThemeColor)('versionRange')(rangeVersionWithPrefix);
|
|
@@ -79,7 +98,15 @@ function renderPackageLine(state, index, isCurrentRow, terminalWidth = 80) {
|
|
|
79
98
|
// Latest version dot and version
|
|
80
99
|
let latestDot = '';
|
|
81
100
|
let latestVersionText = '';
|
|
82
|
-
if (
|
|
101
|
+
if (isPending) {
|
|
102
|
+
latestDot = (0, themes_colors_1.getThemeColor)('dotEmpty')('◌');
|
|
103
|
+
latestVersionText = chalk_1.default.gray('loading');
|
|
104
|
+
}
|
|
105
|
+
else if (isFailed) {
|
|
106
|
+
latestDot = (0, themes_colors_1.getThemeColor)('dotEmpty')('◌');
|
|
107
|
+
latestVersionText = chalk_1.default.gray('unavailable');
|
|
108
|
+
}
|
|
109
|
+
else if (state.hasMajorUpdate) {
|
|
83
110
|
latestDot = isLatestSelected ? (0, themes_colors_1.getThemeColor)('dot')('●') : (0, themes_colors_1.getThemeColor)('dotEmpty')('○');
|
|
84
111
|
const latestVersionWithPrefix = utils_1.VersionUtils.applyVersionPrefix(state.currentVersionSpecifier, state.latestVersion);
|
|
85
112
|
latestVersionText = (0, themes_colors_1.getThemeColor)('versionLatest')(latestVersionWithPrefix);
|
|
@@ -112,26 +139,36 @@ function renderPackageLine(state, index, isCurrentRow, terminalWidth = 80) {
|
|
|
112
139
|
const displayName = truncatedName !== state.name ? truncatedName : packageName;
|
|
113
140
|
// Package name with dashes and badge at the end
|
|
114
141
|
const typeBadge = getTypeBadge(state.type);
|
|
142
|
+
const shouldShowVulnerability = (0, vulnerability_1.shouldDisplayVulnerabilityForDependency)(state.type, options);
|
|
143
|
+
const vulnBadge = shouldShowVulnerability ? (0, vulnerability_1.getVulnerabilityBadge)(state.vulnerability) : '';
|
|
144
|
+
const vulnBadgeWidth = vulnBadge ? utils_1.VersionUtils.getVisualLength(vulnBadge) + 1 : 0; // +1 for space
|
|
115
145
|
const nameLength = utils_1.VersionUtils.getVisualLength(truncatedName);
|
|
116
|
-
const namePadding = Math.max(0, packageNameWidth - nameLength - 1 - badgeWidth); // -1 for space after package name
|
|
117
|
-
const nameDashes = shouldShowDashes(namePadding)
|
|
118
|
-
|
|
146
|
+
const namePadding = Math.max(0, packageNameWidth - nameLength - 1 - badgeWidth - vulnBadgeWidth); // -1 for space after package name
|
|
147
|
+
const nameDashes = shouldShowDashes(namePadding)
|
|
148
|
+
? dashColor('-').repeat(namePadding)
|
|
149
|
+
: ' '.repeat(namePadding);
|
|
150
|
+
// Place badges at the end of dashes: name ------⚠[D]
|
|
151
|
+
const vulnSuffix = vulnBadge ? ` ${vulnBadge}` : '';
|
|
119
152
|
const packageNameSection = typeBadge
|
|
120
|
-
? `${displayName} ${nameDashes}${typeBadge}`
|
|
121
|
-
: `${displayName} ${nameDashes}`;
|
|
153
|
+
? `${displayName} ${nameDashes}${vulnSuffix}${typeBadge}`
|
|
154
|
+
: `${displayName} ${nameDashes}${vulnSuffix}`;
|
|
122
155
|
// Current version section with dashes only if needed
|
|
123
156
|
const currentSection = `${currentDot} ${currentVersion}`;
|
|
124
157
|
const currentSectionLength = utils_1.VersionUtils.getVisualLength(currentSection) + 1; // +1 for space before padding
|
|
125
158
|
const currentPadding = Math.max(0, currentColumnWidth - currentSectionLength);
|
|
126
|
-
const currentPaddingText = shouldShowDashes(currentPadding)
|
|
159
|
+
const currentPaddingText = shouldShowDashes(currentPadding)
|
|
160
|
+
? dashColor('-').repeat(currentPadding)
|
|
161
|
+
: ' '.repeat(currentPadding);
|
|
127
162
|
const currentWithPadding = currentSection + ' ' + currentPaddingText;
|
|
128
163
|
// Range version section with dashes only if needed
|
|
129
164
|
let rangeSection = '';
|
|
130
|
-
if (state.hasRangeUpdate) {
|
|
165
|
+
if (isPending || isFailed || state.hasRangeUpdate) {
|
|
131
166
|
rangeSection = `${rangeDot} ${rangeVersionText}`;
|
|
132
167
|
const rangeSectionLength = utils_1.VersionUtils.getVisualLength(rangeSection) + 1; // +1 for space before padding
|
|
133
168
|
const rangePadding = Math.max(0, rangeColumnWidth - rangeSectionLength);
|
|
134
|
-
const rangePaddingText = shouldShowDashes(rangePadding)
|
|
169
|
+
const rangePaddingText = shouldShowDashes(rangePadding)
|
|
170
|
+
? dashColor('-').repeat(rangePadding)
|
|
171
|
+
: ' '.repeat(rangePadding);
|
|
135
172
|
rangeSection += ' ' + rangePaddingText;
|
|
136
173
|
}
|
|
137
174
|
else {
|
|
@@ -140,11 +177,13 @@ function renderPackageLine(state, index, isCurrentRow, terminalWidth = 80) {
|
|
|
140
177
|
}
|
|
141
178
|
// Latest version section with dashes only if needed
|
|
142
179
|
let latestSection = '';
|
|
143
|
-
if (state.hasMajorUpdate) {
|
|
180
|
+
if (isPending || isFailed || state.hasMajorUpdate) {
|
|
144
181
|
latestSection = `${latestDot} ${latestVersionText}`;
|
|
145
182
|
const latestSectionLength = utils_1.VersionUtils.getVisualLength(latestSection) + 1; // +1 for space before padding
|
|
146
183
|
const latestPadding = Math.max(0, latestColumnWidth - latestSectionLength);
|
|
147
|
-
const latestPaddingText = shouldShowDashes(latestPadding)
|
|
184
|
+
const latestPaddingText = shouldShowDashes(latestPadding)
|
|
185
|
+
? dashColor('-').repeat(latestPadding)
|
|
186
|
+
: ' '.repeat(latestPadding);
|
|
148
187
|
latestSection += ' ' + latestPaddingText;
|
|
149
188
|
}
|
|
150
189
|
else {
|
|
@@ -171,7 +210,7 @@ function renderSpacer() {
|
|
|
171
210
|
/**
|
|
172
211
|
* Render the main interface
|
|
173
212
|
*/
|
|
174
|
-
function renderInterface(states, currentRow, scrollOffset, maxVisibleItems, forceFullRender, renderableItems, activeFilterLabel, packageManager, filterMode, filterQuery, totalPackagesBeforeFilter, terminalWidth = 80) {
|
|
213
|
+
function renderInterface(states, currentRow, scrollOffset, maxVisibleItems, forceFullRender, renderableItems, activeFilterLabel, packageManager, filterMode, filterQuery, totalPackagesBeforeFilter, terminalWidth = 80, loadingProgress, auditProgress, options = {}) {
|
|
175
214
|
const output = [];
|
|
176
215
|
// Header section (same for initial and incremental render)
|
|
177
216
|
if (packageManager) {
|
|
@@ -186,20 +225,33 @@ function renderInterface(states, currentRow, scrollOffset, maxVisibleItems, forc
|
|
|
186
225
|
// Each character in "inup" gets a different color
|
|
187
226
|
const inupColors = [chalk_1.default.red, chalk_1.default.yellow, chalk_1.default.blue, chalk_1.default.magenta];
|
|
188
227
|
const coloredInup = inupColors.map((color, i) => color.bold('inup'[i])).join('');
|
|
189
|
-
const headerLine = ' ' +
|
|
228
|
+
const headerLine = ' ' +
|
|
229
|
+
chalk_1.default.bold(pmColor('🚀')) +
|
|
230
|
+
' ' +
|
|
231
|
+
coloredInup +
|
|
232
|
+
(0, themes_colors_1.getThemeColor)('textSecondary')(` (${packageManager.displayName})`);
|
|
190
233
|
// Show filter state (always show, including "All")
|
|
191
234
|
const fullHeaderLine = activeFilterLabel
|
|
192
|
-
? headerLine +
|
|
235
|
+
? headerLine +
|
|
236
|
+
(0, themes_colors_1.getThemeColor)('textSecondary')(' - ') +
|
|
237
|
+
(0, themes_colors_1.getThemeColor)('primary')(activeFilterLabel)
|
|
193
238
|
: headerLine;
|
|
194
239
|
// Pad to terminal width to clear any leftover characters
|
|
195
240
|
const headerPadding = Math.max(0, terminalWidth - utils_1.VersionUtils.getVisualLength(fullHeaderLine));
|
|
196
241
|
output.push(fullHeaderLine + ' '.repeat(headerPadding));
|
|
197
242
|
}
|
|
198
243
|
else {
|
|
199
|
-
const headerLine = ' ' +
|
|
244
|
+
const headerLine = ' ' +
|
|
245
|
+
chalk_1.default.bold.blue('🚀 ') +
|
|
246
|
+
chalk_1.default.bold.red('i') +
|
|
247
|
+
chalk_1.default.bold.yellow('n') +
|
|
248
|
+
chalk_1.default.bold.blue('u') +
|
|
249
|
+
chalk_1.default.bold.magenta('p');
|
|
200
250
|
// Show filter state (always show, including "All")
|
|
201
251
|
const fullHeaderLine = activeFilterLabel
|
|
202
|
-
? headerLine +
|
|
252
|
+
? headerLine +
|
|
253
|
+
(0, themes_colors_1.getThemeColor)('textSecondary')(' - ') +
|
|
254
|
+
(0, themes_colors_1.getThemeColor)('primary')(activeFilterLabel)
|
|
203
255
|
: headerLine;
|
|
204
256
|
// Pad to terminal width to clear any leftover characters
|
|
205
257
|
const headerPadding = Math.max(0, terminalWidth - utils_1.VersionUtils.getVisualLength(fullHeaderLine));
|
|
@@ -208,14 +260,20 @@ function renderInterface(states, currentRow, scrollOffset, maxVisibleItems, forc
|
|
|
208
260
|
output.push('');
|
|
209
261
|
if (filterMode) {
|
|
210
262
|
// Show filter input with cursor when actively filtering
|
|
211
|
-
const filterDisplay = ' ' +
|
|
263
|
+
const filterDisplay = ' ' +
|
|
264
|
+
chalk_1.default.bold.white('Search: ') +
|
|
265
|
+
(0, themes_colors_1.getThemeColor)('primary')(filterQuery || '') +
|
|
266
|
+
(0, themes_colors_1.getThemeColor)('border')('█');
|
|
212
267
|
// Pad to terminal width to clear any leftover characters from backspace
|
|
213
268
|
const padding = Math.max(0, terminalWidth - utils_1.VersionUtils.getVisualLength(filterDisplay));
|
|
214
269
|
output.push(filterDisplay + ' '.repeat(padding));
|
|
215
270
|
}
|
|
216
271
|
else if (filterQuery) {
|
|
217
272
|
// Show applied filter when not in filter mode but filter is active
|
|
218
|
-
const filterDisplay = ' ' +
|
|
273
|
+
const filterDisplay = ' ' +
|
|
274
|
+
chalk_1.default.bold.white('Search: ') +
|
|
275
|
+
(0, themes_colors_1.getThemeColor)('primary')(filterQuery) +
|
|
276
|
+
(0, themes_colors_1.getThemeColor)('textSecondary')(' (press / to edit)');
|
|
219
277
|
const padding = Math.max(0, terminalWidth - utils_1.VersionUtils.getVisualLength(filterDisplay));
|
|
220
278
|
output.push(filterDisplay + ' '.repeat(padding));
|
|
221
279
|
}
|
|
@@ -237,6 +295,9 @@ function renderInterface(states, currentRow, scrollOffset, maxVisibleItems, forc
|
|
|
237
295
|
chalk_1.default.bold.white('I ') +
|
|
238
296
|
(0, themes_colors_1.getThemeColor)('textSecondary')('Info') +
|
|
239
297
|
' ' +
|
|
298
|
+
chalk_1.default.bold.white('S ') +
|
|
299
|
+
(0, themes_colors_1.getThemeColor)('textSecondary')('Vulnerable') +
|
|
300
|
+
' ' +
|
|
240
301
|
chalk_1.default.bold.white('M ') +
|
|
241
302
|
(0, themes_colors_1.getThemeColor)('textSecondary')('Minor') +
|
|
242
303
|
' ' +
|
|
@@ -257,67 +318,97 @@ function renderInterface(states, currentRow, scrollOffset, maxVisibleItems, forc
|
|
|
257
318
|
if (filterMode) {
|
|
258
319
|
// In filter mode, show Enter to apply and ESC to clear
|
|
259
320
|
if (totalPackages === 0) {
|
|
260
|
-
statusLine =
|
|
261
|
-
'
|
|
262
|
-
|
|
321
|
+
statusLine =
|
|
322
|
+
(0, themes_colors_1.getThemeColor)('warning')(`No matches found`) +
|
|
323
|
+
' ' +
|
|
324
|
+
chalk_1.default.bold.white('Esc ') +
|
|
325
|
+
chalk_1.default.gray('Clear');
|
|
263
326
|
}
|
|
264
327
|
else if (totalVisualItems > maxVisibleItems) {
|
|
265
|
-
statusLine =
|
|
266
|
-
'
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
328
|
+
statusLine =
|
|
329
|
+
(0, themes_colors_1.getThemeColor)('textSecondary')(`Showing ${chalk_1.default.white(startItem)}-${chalk_1.default.white(endItem)} of ${chalk_1.default.white(totalPackages)} matches`) +
|
|
330
|
+
' ' +
|
|
331
|
+
chalk_1.default.bold.white('Enter ') +
|
|
332
|
+
chalk_1.default.gray('Apply') +
|
|
333
|
+
' ' +
|
|
334
|
+
chalk_1.default.bold.white('Esc ') +
|
|
335
|
+
chalk_1.default.gray('Clear');
|
|
270
336
|
}
|
|
271
337
|
else {
|
|
272
|
-
statusLine =
|
|
273
|
-
'
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
338
|
+
statusLine =
|
|
339
|
+
(0, themes_colors_1.getThemeColor)('textSecondary')(`Showing all ${chalk_1.default.white(totalPackages)} matches`) +
|
|
340
|
+
' ' +
|
|
341
|
+
chalk_1.default.bold.white('Enter ') +
|
|
342
|
+
chalk_1.default.gray('Apply') +
|
|
343
|
+
' ' +
|
|
344
|
+
chalk_1.default.bold.white('Esc ') +
|
|
345
|
+
chalk_1.default.gray('Clear');
|
|
277
346
|
}
|
|
278
347
|
}
|
|
279
348
|
else if (totalPackages < totalBeforeFilter) {
|
|
280
349
|
// Filter is applied but not in filter mode
|
|
281
350
|
if (totalVisualItems > maxVisibleItems) {
|
|
282
|
-
statusLine =
|
|
283
|
-
'
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
351
|
+
statusLine =
|
|
352
|
+
(0, themes_colors_1.getThemeColor)('textSecondary')(`Showing ${chalk_1.default.white(startItem)}-${chalk_1.default.white(endItem)} of ${chalk_1.default.white(totalPackages)} matches`) +
|
|
353
|
+
' ' +
|
|
354
|
+
chalk_1.default.bold.white('D/P/O ') +
|
|
355
|
+
chalk_1.default.gray('Filter') +
|
|
356
|
+
' ' +
|
|
357
|
+
chalk_1.default.bold.white('M ') +
|
|
358
|
+
chalk_1.default.gray('Minor') +
|
|
359
|
+
' ' +
|
|
360
|
+
chalk_1.default.bold.white('L ') +
|
|
361
|
+
chalk_1.default.gray('All') +
|
|
362
|
+
' ' +
|
|
363
|
+
chalk_1.default.bold.white('U ') +
|
|
364
|
+
chalk_1.default.gray('None') +
|
|
365
|
+
' ' +
|
|
366
|
+
chalk_1.default.bold.white('Esc ') +
|
|
367
|
+
chalk_1.default.gray('Clear');
|
|
293
368
|
}
|
|
294
369
|
else {
|
|
295
|
-
statusLine =
|
|
296
|
-
'
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
370
|
+
statusLine =
|
|
371
|
+
(0, themes_colors_1.getThemeColor)('textSecondary')(`Showing all ${chalk_1.default.white(totalPackages)} matches`) +
|
|
372
|
+
' ' +
|
|
373
|
+
chalk_1.default.bold.white('D/P/O ') +
|
|
374
|
+
chalk_1.default.gray('Filter') +
|
|
375
|
+
' ' +
|
|
376
|
+
chalk_1.default.bold.white('M ') +
|
|
377
|
+
chalk_1.default.gray('Minor') +
|
|
378
|
+
' ' +
|
|
379
|
+
chalk_1.default.bold.white('L ') +
|
|
380
|
+
chalk_1.default.gray('All') +
|
|
381
|
+
' ' +
|
|
382
|
+
chalk_1.default.bold.white('U ') +
|
|
383
|
+
chalk_1.default.gray('None') +
|
|
384
|
+
' ' +
|
|
385
|
+
chalk_1.default.bold.white('Esc ') +
|
|
386
|
+
chalk_1.default.gray('Clear');
|
|
306
387
|
}
|
|
307
388
|
}
|
|
308
389
|
else {
|
|
309
390
|
// No filter applied
|
|
310
391
|
if (totalVisualItems > maxVisibleItems) {
|
|
311
|
-
statusLine =
|
|
312
|
-
|
|
313
|
-
|
|
392
|
+
statusLine =
|
|
393
|
+
chalk_1.default.gray(`Showing ${chalk_1.default.white(startItem)}-${chalk_1.default.white(endItem)} of ${chalk_1.default.white(totalPackages)} packages`) +
|
|
394
|
+
' ' +
|
|
395
|
+
chalk_1.default.bold.white('Enter ') +
|
|
396
|
+
chalk_1.default.gray('Confirm');
|
|
314
397
|
}
|
|
315
398
|
else {
|
|
316
|
-
statusLine =
|
|
317
|
-
|
|
318
|
-
|
|
399
|
+
statusLine =
|
|
400
|
+
chalk_1.default.gray(`Showing all ${chalk_1.default.white(totalPackages)} packages`) +
|
|
401
|
+
' ' +
|
|
402
|
+
chalk_1.default.bold.white('Enter ') +
|
|
403
|
+
chalk_1.default.gray('Confirm');
|
|
319
404
|
}
|
|
320
405
|
}
|
|
406
|
+
if (auditProgress && auditProgress.total > 0) {
|
|
407
|
+
const auditLabel = auditProgress.isRunning
|
|
408
|
+
? `Audit ${auditProgress.completed}/${auditProgress.total}`
|
|
409
|
+
: `Audit ${auditProgress.total}/${auditProgress.total}`;
|
|
410
|
+
statusLine += ' ' + (0, themes_colors_1.getThemeColor)('textSecondary')(auditLabel);
|
|
411
|
+
}
|
|
321
412
|
// Pad status line to terminal width to clear any leftover characters
|
|
322
413
|
const statusLineFull = ' ' + statusLine;
|
|
323
414
|
const statusPadding = Math.max(0, terminalWidth - utils_1.VersionUtils.getVisualLength(statusLineFull));
|
|
@@ -335,7 +426,7 @@ function renderInterface(states, currentRow, scrollOffset, maxVisibleItems, forc
|
|
|
335
426
|
output.push(renderSpacer());
|
|
336
427
|
}
|
|
337
428
|
else if (item.type === 'package') {
|
|
338
|
-
const line = renderPackageLine(item.state, item.originalIndex, item.originalIndex === currentRow, terminalWidth);
|
|
429
|
+
const line = renderPackageLine(item.state, item.originalIndex, item.originalIndex === currentRow, terminalWidth, options);
|
|
339
430
|
output.push(line);
|
|
340
431
|
}
|
|
341
432
|
}
|
|
@@ -343,11 +434,20 @@ function renderInterface(states, currentRow, scrollOffset, maxVisibleItems, forc
|
|
|
343
434
|
else {
|
|
344
435
|
// Fallback to flat rendering (legacy mode)
|
|
345
436
|
for (let i = scrollOffset; i < Math.min(scrollOffset + maxVisibleItems, states.length); i++) {
|
|
346
|
-
const line = renderPackageLine(states[i], i, i === currentRow, terminalWidth);
|
|
437
|
+
const line = renderPackageLine(states[i], i, i === currentRow, terminalWidth, options);
|
|
347
438
|
output.push(line);
|
|
348
439
|
}
|
|
349
440
|
}
|
|
350
|
-
|
|
441
|
+
if (loadingProgress?.isLoading) {
|
|
442
|
+
const loadingLabel = `Loading packages... (${loadingProgress.resolved}/${loadingProgress.total} checked)`;
|
|
443
|
+
const failedLabel = loadingProgress.failed > 0 ? ` ${loadingProgress.failed} unavailable` : '';
|
|
444
|
+
const loadingLine = ' ' +
|
|
445
|
+
(0, themes_colors_1.getThemeColor)('textSecondary')(loadingLabel) +
|
|
446
|
+
(failedLabel ? chalk_1.default.yellow(failedLabel) : '');
|
|
447
|
+
const loadingPadding = Math.max(0, terminalWidth - utils_1.VersionUtils.getVisualLength(loadingLine));
|
|
448
|
+
output.push(loadingLine + ' '.repeat(loadingPadding));
|
|
449
|
+
}
|
|
450
|
+
return output.map((line) => padLineToWidth(line, terminalWidth));
|
|
351
451
|
}
|
|
352
452
|
/**
|
|
353
453
|
* Render packages table
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.FilterManager = void 0;
|
|
4
|
+
const vulnerability_1 = require("../presenters/vulnerability");
|
|
4
5
|
class FilterManager {
|
|
5
6
|
constructor() {
|
|
6
7
|
this.state = {
|
|
@@ -10,6 +11,7 @@ class FilterManager {
|
|
|
10
11
|
showDevDependencies: true,
|
|
11
12
|
showPeerDependencies: true,
|
|
12
13
|
showOptionalDependencies: true,
|
|
14
|
+
showOnlyVulnerable: false,
|
|
13
15
|
};
|
|
14
16
|
}
|
|
15
17
|
getState() {
|
|
@@ -60,6 +62,12 @@ class FilterManager {
|
|
|
60
62
|
break;
|
|
61
63
|
}
|
|
62
64
|
}
|
|
65
|
+
toggleVulnerableFilter() {
|
|
66
|
+
this.state.showOnlyVulnerable = !this.state.showOnlyVulnerable;
|
|
67
|
+
}
|
|
68
|
+
isVulnerableFilterActive() {
|
|
69
|
+
return this.state.showOnlyVulnerable;
|
|
70
|
+
}
|
|
63
71
|
getActiveFilterLabel() {
|
|
64
72
|
const activeTypes = [];
|
|
65
73
|
if (this.state.showDependencies)
|
|
@@ -72,9 +80,10 @@ class FilterManager {
|
|
|
72
80
|
activeTypes.push('Optional');
|
|
73
81
|
if (activeTypes.length === 0)
|
|
74
82
|
return 'None';
|
|
75
|
-
|
|
83
|
+
const label = activeTypes.join(', ');
|
|
84
|
+
return this.state.showOnlyVulnerable ? label + ' (vulnerable only)' : label;
|
|
76
85
|
}
|
|
77
|
-
getFilteredStates(allStates) {
|
|
86
|
+
getFilteredStates(allStates, options = {}) {
|
|
78
87
|
let filtered = allStates;
|
|
79
88
|
// Apply text filter
|
|
80
89
|
if (this.state.filterQuery) {
|
|
@@ -96,6 +105,12 @@ class FilterManager {
|
|
|
96
105
|
return true;
|
|
97
106
|
}
|
|
98
107
|
});
|
|
108
|
+
// Apply vulnerability filter
|
|
109
|
+
if (this.state.showOnlyVulnerable) {
|
|
110
|
+
filtered = filtered.filter((state) => (0, vulnerability_1.shouldDisplayVulnerabilityForDependency)(state.type, options) &&
|
|
111
|
+
!!state.vulnerability &&
|
|
112
|
+
state.vulnerability.count > 0);
|
|
113
|
+
}
|
|
99
114
|
return filtered;
|
|
100
115
|
}
|
|
101
116
|
}
|
|
@@ -7,6 +7,8 @@ class ModalManager {
|
|
|
7
7
|
showInfoModal: false,
|
|
8
8
|
infoModalRow: -1,
|
|
9
9
|
isLoadingModalInfo: false,
|
|
10
|
+
infoModalScrollOffset: 0,
|
|
11
|
+
infoModalSessionId: 0,
|
|
10
12
|
};
|
|
11
13
|
}
|
|
12
14
|
getState() {
|
|
@@ -21,24 +23,64 @@ class ModalManager {
|
|
|
21
23
|
isLoading() {
|
|
22
24
|
return this.state.isLoadingModalInfo;
|
|
23
25
|
}
|
|
26
|
+
getScrollOffset() {
|
|
27
|
+
return this.state.infoModalScrollOffset;
|
|
28
|
+
}
|
|
29
|
+
getSessionId() {
|
|
30
|
+
return this.state.infoModalSessionId;
|
|
31
|
+
}
|
|
32
|
+
clampScrollOffset(maxOffset) {
|
|
33
|
+
const nextOffset = Math.max(0, Math.min(this.state.infoModalScrollOffset, maxOffset));
|
|
34
|
+
if (nextOffset === this.state.infoModalScrollOffset) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
this.state.infoModalScrollOffset = nextOffset;
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
resetScroll() {
|
|
41
|
+
this.state.infoModalScrollOffset = 0;
|
|
42
|
+
}
|
|
43
|
+
scrollModalUp() {
|
|
44
|
+
if (this.state.infoModalScrollOffset > 0) {
|
|
45
|
+
this.state.infoModalScrollOffset--;
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
scrollModalDown(maxOffset) {
|
|
51
|
+
if (this.state.infoModalScrollOffset < maxOffset) {
|
|
52
|
+
this.state.infoModalScrollOffset++;
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
24
57
|
toggleInfoModal(currentRow) {
|
|
25
58
|
if (this.state.showInfoModal) {
|
|
26
59
|
// Close the modal
|
|
27
60
|
this.closeInfoModal();
|
|
61
|
+
return this.state.infoModalSessionId;
|
|
28
62
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
63
|
+
// Open the modal for the current package
|
|
64
|
+
this.state.showInfoModal = true;
|
|
65
|
+
this.state.infoModalRow = currentRow;
|
|
66
|
+
this.state.infoModalScrollOffset = 0;
|
|
67
|
+
this.state.isLoadingModalInfo = false;
|
|
68
|
+
this.state.infoModalSessionId += 1;
|
|
69
|
+
return this.state.infoModalSessionId;
|
|
34
70
|
}
|
|
35
71
|
closeInfoModal() {
|
|
36
72
|
this.state.showInfoModal = false;
|
|
37
73
|
this.state.infoModalRow = -1;
|
|
38
74
|
this.state.isLoadingModalInfo = false;
|
|
75
|
+
this.state.infoModalScrollOffset = 0;
|
|
76
|
+
this.state.infoModalSessionId += 1;
|
|
39
77
|
}
|
|
40
|
-
setModalLoading(isLoading) {
|
|
78
|
+
setModalLoading(isLoading, sessionId) {
|
|
79
|
+
if (sessionId !== undefined && sessionId !== this.state.infoModalSessionId) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
41
82
|
this.state.isLoadingModalInfo = isLoading;
|
|
83
|
+
return true;
|
|
42
84
|
}
|
|
43
85
|
}
|
|
44
86
|
exports.ModalManager = ModalManager;
|
|
@@ -42,6 +42,7 @@ class StateManager {
|
|
|
42
42
|
showInfoModal: modalState.showInfoModal,
|
|
43
43
|
infoModalRow: modalState.infoModalRow,
|
|
44
44
|
isLoadingModalInfo: modalState.isLoadingModalInfo,
|
|
45
|
+
infoModalScrollOffset: modalState.infoModalScrollOffset,
|
|
45
46
|
filterMode: filterState.filterMode,
|
|
46
47
|
filterQuery: filterState.filterQuery,
|
|
47
48
|
showThemeModal: themeState.showThemeModal,
|
|
@@ -68,7 +69,7 @@ class StateManager {
|
|
|
68
69
|
return;
|
|
69
70
|
const currentRow = this.navigationManager.getCurrentRow();
|
|
70
71
|
const currentState = states[currentRow];
|
|
71
|
-
if (!currentState)
|
|
72
|
+
if (!currentState || currentState.loadState !== 'ready')
|
|
72
73
|
return;
|
|
73
74
|
if (direction === 'left') {
|
|
74
75
|
// Move selection left with wraparound: latest -> range -> none -> latest
|
|
@@ -122,7 +123,7 @@ class StateManager {
|
|
|
122
123
|
if (states.length === 0)
|
|
123
124
|
return;
|
|
124
125
|
states.forEach((state) => {
|
|
125
|
-
if (state.hasRangeUpdate) {
|
|
126
|
+
if (state.loadState === 'ready' && state.hasRangeUpdate) {
|
|
126
127
|
state.selectedOption = 'range';
|
|
127
128
|
}
|
|
128
129
|
});
|
|
@@ -131,10 +132,10 @@ class StateManager {
|
|
|
131
132
|
if (states.length === 0)
|
|
132
133
|
return;
|
|
133
134
|
states.forEach((state) => {
|
|
134
|
-
if (state.hasMajorUpdate) {
|
|
135
|
+
if (state.loadState === 'ready' && state.hasMajorUpdate) {
|
|
135
136
|
state.selectedOption = 'latest';
|
|
136
137
|
}
|
|
137
|
-
else if (state.hasRangeUpdate) {
|
|
138
|
+
else if (state.loadState === 'ready' && state.hasRangeUpdate) {
|
|
138
139
|
state.selectedOption = 'range';
|
|
139
140
|
}
|
|
140
141
|
});
|
|
@@ -143,22 +144,48 @@ class StateManager {
|
|
|
143
144
|
if (states.length === 0)
|
|
144
145
|
return;
|
|
145
146
|
states.forEach((state) => {
|
|
146
|
-
state.
|
|
147
|
+
if (state.loadState === 'ready') {
|
|
148
|
+
state.selectedOption = 'none';
|
|
149
|
+
}
|
|
147
150
|
});
|
|
148
151
|
}
|
|
149
152
|
// Modal delegation
|
|
150
153
|
toggleInfoModal() {
|
|
151
154
|
const currentRow = this.navigationManager.getCurrentRow();
|
|
152
|
-
this.modalManager.toggleInfoModal(currentRow);
|
|
155
|
+
const sessionId = this.modalManager.toggleInfoModal(currentRow);
|
|
153
156
|
this.renderState.forceFullRender = true;
|
|
157
|
+
return sessionId;
|
|
154
158
|
}
|
|
155
159
|
closeInfoModal() {
|
|
156
160
|
this.modalManager.closeInfoModal();
|
|
157
161
|
this.renderState.forceFullRender = true;
|
|
158
162
|
}
|
|
159
|
-
setModalLoading(isLoading) {
|
|
160
|
-
this.modalManager.setModalLoading(isLoading);
|
|
161
|
-
|
|
163
|
+
setModalLoading(isLoading, sessionId) {
|
|
164
|
+
const updated = this.modalManager.setModalLoading(isLoading, sessionId);
|
|
165
|
+
if (updated) {
|
|
166
|
+
this.renderState.forceFullRender = true;
|
|
167
|
+
}
|
|
168
|
+
return updated;
|
|
169
|
+
}
|
|
170
|
+
getInfoModalSessionId() {
|
|
171
|
+
return this.modalManager.getSessionId();
|
|
172
|
+
}
|
|
173
|
+
resetInfoModalScroll() {
|
|
174
|
+
this.modalManager.resetScroll();
|
|
175
|
+
}
|
|
176
|
+
scrollInfoModalUp() {
|
|
177
|
+
return this.modalManager.scrollModalUp();
|
|
178
|
+
// Don't force full render — modal viewport handles its own overwrite
|
|
179
|
+
}
|
|
180
|
+
scrollInfoModalDown(maxOffset) {
|
|
181
|
+
return this.modalManager.scrollModalDown(maxOffset);
|
|
182
|
+
// Don't force full render — modal viewport handles its own overwrite
|
|
183
|
+
}
|
|
184
|
+
getInfoModalScrollOffset() {
|
|
185
|
+
return this.modalManager.getScrollOffset();
|
|
186
|
+
}
|
|
187
|
+
clampInfoModalScrollOffset(maxOffset) {
|
|
188
|
+
return this.modalManager.clampScrollOffset(maxOffset);
|
|
162
189
|
}
|
|
163
190
|
// Filter delegation
|
|
164
191
|
enterFilterMode(preserveQuery = false) {
|
|
@@ -188,8 +215,8 @@ class StateManager {
|
|
|
188
215
|
this.navigationManager.setCurrentRow(0);
|
|
189
216
|
this.navigationManager.setScrollOffset(0);
|
|
190
217
|
}
|
|
191
|
-
getFilteredStates(allStates) {
|
|
192
|
-
return this.filterManager.getFilteredStates(allStates);
|
|
218
|
+
getFilteredStates(allStates, options) {
|
|
219
|
+
return this.filterManager.getFilteredStates(allStates, options);
|
|
193
220
|
}
|
|
194
221
|
toggleDependencyTypeFilter(type) {
|
|
195
222
|
this.filterManager.toggleDependencyType(type);
|
|
@@ -198,6 +225,14 @@ class StateManager {
|
|
|
198
225
|
this.navigationManager.setScrollOffset(0);
|
|
199
226
|
// Use incremental render (no blink)
|
|
200
227
|
}
|
|
228
|
+
toggleVulnerableFilter() {
|
|
229
|
+
this.filterManager.toggleVulnerableFilter();
|
|
230
|
+
this.navigationManager.setCurrentRow(0);
|
|
231
|
+
this.navigationManager.setScrollOffset(0);
|
|
232
|
+
}
|
|
233
|
+
isVulnerableFilterActive() {
|
|
234
|
+
return this.filterManager.isVulnerableFilterActive();
|
|
235
|
+
}
|
|
201
236
|
getActiveFilterLabel() {
|
|
202
237
|
return this.filterManager.getActiveFilterLabel();
|
|
203
238
|
}
|
|
@@ -221,7 +256,9 @@ class StateManager {
|
|
|
221
256
|
this.renderState.forceFullRender = isInitial;
|
|
222
257
|
}
|
|
223
258
|
resetForResize(totalFilteredItems) {
|
|
224
|
-
const totalItems = totalFilteredItems ||
|
|
259
|
+
const totalItems = totalFilteredItems ||
|
|
260
|
+
this.renderState.renderableItems.length ||
|
|
261
|
+
this.displayState.maxVisibleItems;
|
|
225
262
|
this.navigationManager.resetForResize(totalItems);
|
|
226
263
|
this.renderState.forceFullRender = true;
|
|
227
264
|
}
|