@rainy-updates/cli 0.5.2-rc.2 → 0.5.2
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/CHANGELOG.md +52 -0
- package/README.md +29 -0
- package/dist/bin/cli.js +108 -2
- package/dist/cache/cache.d.ts +1 -0
- package/dist/cache/cache.js +9 -2
- package/dist/commands/audit/runner.js +8 -1
- package/dist/commands/audit/sources/index.js +8 -1
- package/dist/commands/doctor/parser.d.ts +2 -0
- package/dist/commands/doctor/parser.js +92 -0
- package/dist/commands/doctor/runner.d.ts +2 -0
- package/dist/commands/doctor/runner.js +13 -0
- package/dist/commands/resolve/runner.js +3 -0
- package/dist/commands/review/parser.d.ts +2 -0
- package/dist/commands/review/parser.js +174 -0
- package/dist/commands/review/runner.d.ts +2 -0
- package/dist/commands/review/runner.js +30 -0
- package/dist/config/loader.d.ts +3 -0
- package/dist/core/check.js +39 -5
- package/dist/core/errors.d.ts +11 -0
- package/dist/core/errors.js +6 -0
- package/dist/core/options.d.ts +8 -1
- package/dist/core/options.js +43 -0
- package/dist/core/review-model.d.ts +5 -0
- package/dist/core/review-model.js +382 -0
- package/dist/core/summary.js +11 -2
- package/dist/core/upgrade.d.ts +1 -0
- package/dist/core/upgrade.js +27 -21
- package/dist/core/warm-cache.js +28 -4
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/output/format.d.ts +4 -1
- package/dist/output/format.js +29 -3
- package/dist/output/github.js +5 -0
- package/dist/output/sarif.js +11 -0
- package/dist/registry/npm.d.ts +20 -0
- package/dist/registry/npm.js +27 -4
- package/dist/types/index.d.ts +57 -0
- package/dist/ui/tui.d.ts +0 -4
- package/dist/ui/tui.js +78 -21
- package/package.json +5 -2
package/dist/output/sarif.js
CHANGED
|
@@ -30,6 +30,12 @@ export function createSarifReport(result) {
|
|
|
30
30
|
kind: update.kind,
|
|
31
31
|
diffType: update.diffType,
|
|
32
32
|
resolvedVersion: update.toVersionResolved,
|
|
33
|
+
impactRank: update.impactScore?.rank,
|
|
34
|
+
impactScore: update.impactScore?.score,
|
|
35
|
+
riskLevel: update.riskLevel,
|
|
36
|
+
advisoryCount: update.advisoryCount ?? 0,
|
|
37
|
+
peerConflictSeverity: update.peerConflictSeverity ?? "none",
|
|
38
|
+
licenseStatus: update.licenseStatus ?? "allowed",
|
|
33
39
|
},
|
|
34
40
|
}));
|
|
35
41
|
const errorResults = [...result.errors].sort((a, b) => a.localeCompare(b)).map((error) => ({
|
|
@@ -75,6 +81,11 @@ export function createSarifReport(result) {
|
|
|
75
81
|
prLimitHit: result.summary.prLimitHit,
|
|
76
82
|
fixPrBranchesCreated: result.summary.fixPrBranchesCreated,
|
|
77
83
|
durationMs: result.summary.durationMs,
|
|
84
|
+
verdict: result.summary.verdict,
|
|
85
|
+
riskPackages: result.summary.riskPackages ?? 0,
|
|
86
|
+
securityPackages: result.summary.securityPackages ?? 0,
|
|
87
|
+
peerConflictPackages: result.summary.peerConflictPackages ?? 0,
|
|
88
|
+
licenseViolationPackages: result.summary.licenseViolationPackages ?? 0,
|
|
78
89
|
},
|
|
79
90
|
},
|
|
80
91
|
],
|
package/dist/registry/npm.d.ts
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
interface RegistryConfig {
|
|
2
|
+
defaultRegistry: string;
|
|
3
|
+
scopedRegistries: Map<string, string>;
|
|
4
|
+
authByRegistry: Map<string, RegistryAuth>;
|
|
5
|
+
}
|
|
6
|
+
interface RegistryAuth {
|
|
7
|
+
token?: string;
|
|
8
|
+
basicAuth?: string;
|
|
9
|
+
alwaysAuth: boolean;
|
|
10
|
+
}
|
|
1
11
|
export interface ResolveManyOptions {
|
|
2
12
|
concurrency: number;
|
|
3
13
|
timeoutMs?: number;
|
|
@@ -12,6 +22,9 @@ export interface ResolveManyResult {
|
|
|
12
22
|
latestVersion: string | null;
|
|
13
23
|
versions: string[];
|
|
14
24
|
publishedAtByVersion: Record<string, number>;
|
|
25
|
+
homepage?: string;
|
|
26
|
+
repository?: string;
|
|
27
|
+
hasInstallScript: boolean;
|
|
15
28
|
}>;
|
|
16
29
|
errors: Map<string, string>;
|
|
17
30
|
}
|
|
@@ -24,6 +37,9 @@ export declare class NpmRegistryClient {
|
|
|
24
37
|
latestVersion: string | null;
|
|
25
38
|
versions: string[];
|
|
26
39
|
publishedAtByVersion: Record<string, number>;
|
|
40
|
+
homepage?: string;
|
|
41
|
+
repository?: string;
|
|
42
|
+
hasInstallScript: boolean;
|
|
27
43
|
}>;
|
|
28
44
|
resolveLatestVersion(packageName: string, timeoutMs?: number): Promise<string | null>;
|
|
29
45
|
resolveManyPackageMetadata(packageNames: string[], options: ResolveManyOptions): Promise<ResolveManyResult>;
|
|
@@ -32,3 +48,7 @@ export declare class NpmRegistryClient {
|
|
|
32
48
|
errors: Map<string, string>;
|
|
33
49
|
}>;
|
|
34
50
|
}
|
|
51
|
+
export declare function loadRegistryConfig(cwd: string): Promise<RegistryConfig>;
|
|
52
|
+
export declare function resolveRegistryForPackage(packageName: string, config: RegistryConfig): string;
|
|
53
|
+
export declare function resolveAuthHeader(registry: string, config: RegistryConfig): string | undefined;
|
|
54
|
+
export {};
|
package/dist/registry/npm.js
CHANGED
|
@@ -22,7 +22,7 @@ export class NpmRegistryClient {
|
|
|
22
22
|
try {
|
|
23
23
|
const response = await requester(packageName, timeoutMs);
|
|
24
24
|
if (response.status === 404) {
|
|
25
|
-
return { latestVersion: null, versions: [], publishedAtByVersion: {} };
|
|
25
|
+
return { latestVersion: null, versions: [], publishedAtByVersion: {}, hasInstallScript: false };
|
|
26
26
|
}
|
|
27
27
|
if (response.status === 429 || response.status >= 500) {
|
|
28
28
|
throw new RetryableRegistryError(`Registry temporary error: ${response.status}`, response.retryAfterMs ?? computeBackoffMs(attempt));
|
|
@@ -35,6 +35,9 @@ export class NpmRegistryClient {
|
|
|
35
35
|
latestVersion: response.data?.["dist-tags"]?.latest ?? null,
|
|
36
36
|
versions,
|
|
37
37
|
publishedAtByVersion: extractPublishTimes(response.data?.time),
|
|
38
|
+
homepage: response.data?.homepage,
|
|
39
|
+
repository: normalizeRepository(response.data?.repository),
|
|
40
|
+
hasInstallScript: detectInstallScript(response.data?.versions),
|
|
38
41
|
};
|
|
39
42
|
}
|
|
40
43
|
catch (error) {
|
|
@@ -94,6 +97,26 @@ export class NpmRegistryClient {
|
|
|
94
97
|
function sleep(ms) {
|
|
95
98
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
96
99
|
}
|
|
100
|
+
function normalizeRepository(value) {
|
|
101
|
+
if (!value)
|
|
102
|
+
return undefined;
|
|
103
|
+
if (typeof value === "string")
|
|
104
|
+
return value;
|
|
105
|
+
return value.url;
|
|
106
|
+
}
|
|
107
|
+
function detectInstallScript(versions) {
|
|
108
|
+
if (!versions)
|
|
109
|
+
return false;
|
|
110
|
+
for (const metadata of Object.values(versions)) {
|
|
111
|
+
const scripts = metadata?.scripts;
|
|
112
|
+
if (!scripts)
|
|
113
|
+
continue;
|
|
114
|
+
if (scripts.preinstall || scripts.install || scripts.postinstall) {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
97
120
|
function computeBackoffMs(attempt) {
|
|
98
121
|
const baseMs = Math.max(120, attempt * 180);
|
|
99
122
|
const jitterMs = Math.floor(Math.random() * 120);
|
|
@@ -195,7 +218,7 @@ async function tryCreateUndiciRequester(registryConfig) {
|
|
|
195
218
|
return null;
|
|
196
219
|
}
|
|
197
220
|
}
|
|
198
|
-
async function loadRegistryConfig(cwd) {
|
|
221
|
+
export async function loadRegistryConfig(cwd) {
|
|
199
222
|
const homeNpmrc = path.join(os.homedir(), ".npmrc");
|
|
200
223
|
const projectNpmrc = path.join(cwd, ".npmrc");
|
|
201
224
|
const merged = new Map();
|
|
@@ -274,7 +297,7 @@ function normalizeRegistryUrl(value) {
|
|
|
274
297
|
const normalized = value.endsWith("/") ? value : `${value}/`;
|
|
275
298
|
return normalized;
|
|
276
299
|
}
|
|
277
|
-
function resolveRegistryForPackage(packageName, config) {
|
|
300
|
+
export function resolveRegistryForPackage(packageName, config) {
|
|
278
301
|
const scope = extractScope(packageName);
|
|
279
302
|
if (scope) {
|
|
280
303
|
const scoped = config.scopedRegistries.get(scope);
|
|
@@ -295,7 +318,7 @@ function buildRegistryUrl(registry, packageName) {
|
|
|
295
318
|
const base = normalizeRegistryUrl(registry);
|
|
296
319
|
return new URL(encodeURIComponent(packageName), base).toString();
|
|
297
320
|
}
|
|
298
|
-
function resolveAuthHeader(registry, config) {
|
|
321
|
+
export function resolveAuthHeader(registry, config) {
|
|
299
322
|
const registryUrl = normalizeRegistryUrl(registry);
|
|
300
323
|
const auth = findRegistryAuth(registryUrl, config.authByRegistry);
|
|
301
324
|
if (!auth)
|
package/dist/types/index.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export type TargetLevel = "patch" | "minor" | "major" | "latest";
|
|
|
3
3
|
export type GroupBy = "none" | "name" | "scope" | "kind" | "risk";
|
|
4
4
|
export type CiProfile = "minimal" | "strict" | "enterprise";
|
|
5
5
|
export type LockfileMode = "preserve" | "update" | "error";
|
|
6
|
+
export type Verdict = "safe" | "review" | "blocked" | "actionable";
|
|
7
|
+
export type RiskLevel = "critical" | "high" | "medium" | "low";
|
|
6
8
|
export type OutputFormat = "table" | "json" | "minimal" | "github" | "metrics";
|
|
7
9
|
export type FailOnLevel = "none" | "patch" | "minor" | "major" | "any";
|
|
8
10
|
export type LogLevel = "error" | "warn" | "info" | "debug";
|
|
@@ -44,6 +46,9 @@ export interface RunOptions {
|
|
|
44
46
|
onlyChanged: boolean;
|
|
45
47
|
ciProfile: CiProfile;
|
|
46
48
|
lockfileMode: LockfileMode;
|
|
49
|
+
interactive: boolean;
|
|
50
|
+
showImpact: boolean;
|
|
51
|
+
showHomepage: boolean;
|
|
47
52
|
}
|
|
48
53
|
export interface CheckOptions extends RunOptions {
|
|
49
54
|
}
|
|
@@ -86,6 +91,12 @@ export interface PackageUpdate {
|
|
|
86
91
|
reason?: string;
|
|
87
92
|
impactScore?: ImpactScore;
|
|
88
93
|
homepage?: string;
|
|
94
|
+
riskLevel?: RiskLevel;
|
|
95
|
+
riskReasons?: string[];
|
|
96
|
+
advisoryCount?: number;
|
|
97
|
+
peerConflictSeverity?: "none" | PeerConflictSeverity;
|
|
98
|
+
licenseStatus?: "allowed" | "review" | "denied";
|
|
99
|
+
healthStatus?: "healthy" | HealthFlag;
|
|
89
100
|
}
|
|
90
101
|
export interface Summary {
|
|
91
102
|
contractVersion: "2";
|
|
@@ -126,6 +137,13 @@ export interface Summary {
|
|
|
126
137
|
prLimitHit: boolean;
|
|
127
138
|
streamedEvents: number;
|
|
128
139
|
policyOverridesApplied: number;
|
|
140
|
+
verdict?: Verdict;
|
|
141
|
+
interactiveSession?: boolean;
|
|
142
|
+
riskPackages?: number;
|
|
143
|
+
securityPackages?: number;
|
|
144
|
+
peerConflictPackages?: number;
|
|
145
|
+
licenseViolationPackages?: number;
|
|
146
|
+
privateRegistryPackages?: number;
|
|
129
147
|
}
|
|
130
148
|
export interface CheckResult {
|
|
131
149
|
projectPath: string;
|
|
@@ -303,6 +321,45 @@ export interface ResolveResult {
|
|
|
303
321
|
errors: string[];
|
|
304
322
|
warnings: string[];
|
|
305
323
|
}
|
|
324
|
+
export interface RiskSignal {
|
|
325
|
+
packageName: string;
|
|
326
|
+
level: RiskLevel;
|
|
327
|
+
reasons: string[];
|
|
328
|
+
}
|
|
329
|
+
export interface ReviewItem {
|
|
330
|
+
update: PackageUpdate;
|
|
331
|
+
advisories: CveAdvisory[];
|
|
332
|
+
health?: PackageHealthMetric;
|
|
333
|
+
peerConflicts: PeerConflict[];
|
|
334
|
+
license?: PackageLicense;
|
|
335
|
+
unusedIssues: UnusedDependency[];
|
|
336
|
+
selected: boolean;
|
|
337
|
+
}
|
|
338
|
+
export interface ReviewResult {
|
|
339
|
+
projectPath: string;
|
|
340
|
+
target: TargetLevel;
|
|
341
|
+
summary: Summary;
|
|
342
|
+
items: ReviewItem[];
|
|
343
|
+
updates: PackageUpdate[];
|
|
344
|
+
errors: string[];
|
|
345
|
+
warnings: string[];
|
|
346
|
+
}
|
|
347
|
+
export interface ReviewOptions extends CheckOptions {
|
|
348
|
+
securityOnly: boolean;
|
|
349
|
+
risk?: RiskLevel;
|
|
350
|
+
diff?: TargetLevel;
|
|
351
|
+
applySelected: boolean;
|
|
352
|
+
}
|
|
353
|
+
export interface DoctorOptions extends CheckOptions {
|
|
354
|
+
verdictOnly: boolean;
|
|
355
|
+
}
|
|
356
|
+
export interface DoctorResult {
|
|
357
|
+
verdict: Verdict;
|
|
358
|
+
summary: Summary;
|
|
359
|
+
review: ReviewResult;
|
|
360
|
+
primaryFindings: string[];
|
|
361
|
+
recommendedCommand: string;
|
|
362
|
+
}
|
|
306
363
|
export type UnusedKind = "declared-not-imported" | "imported-not-declared";
|
|
307
364
|
export interface UnusedDependency {
|
|
308
365
|
name: string;
|
package/dist/ui/tui.d.ts
CHANGED
|
@@ -1,6 +1,2 @@
|
|
|
1
1
|
import type { PackageUpdate } from "../types/index.js";
|
|
2
|
-
export declare function VersionDiff({ from, to }: {
|
|
3
|
-
from: string;
|
|
4
|
-
to: string;
|
|
5
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
6
2
|
export declare function runTui(updates: PackageUpdate[]): Promise<PackageUpdate[]>;
|
package/dist/ui/tui.js
CHANGED
|
@@ -1,44 +1,101 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from "react";
|
|
3
|
-
import { render, Text,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
import { Box, render, Text, useInput } from "ink";
|
|
4
|
+
const FILTER_ORDER = [
|
|
5
|
+
"all",
|
|
6
|
+
"security",
|
|
7
|
+
"risky",
|
|
8
|
+
"major",
|
|
9
|
+
];
|
|
10
|
+
function VersionDiff({ from, to }) {
|
|
11
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: "gray", children: from }), _jsxs(Text, { color: "gray", children: [" ", " -> ", " "] }), _jsx(Text, { color: "green", children: to })] }));
|
|
12
|
+
}
|
|
13
|
+
function riskColor(level) {
|
|
14
|
+
switch (level) {
|
|
15
|
+
case "critical":
|
|
16
|
+
return "red";
|
|
17
|
+
case "high":
|
|
18
|
+
return "yellow";
|
|
19
|
+
case "medium":
|
|
20
|
+
return "cyan";
|
|
21
|
+
default:
|
|
22
|
+
return "green";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function diffColor(level) {
|
|
26
|
+
switch (level) {
|
|
27
|
+
case "major":
|
|
28
|
+
return "red";
|
|
29
|
+
case "minor":
|
|
30
|
+
return "yellow";
|
|
31
|
+
case "patch":
|
|
32
|
+
return "green";
|
|
33
|
+
default:
|
|
34
|
+
return "cyan";
|
|
35
|
+
}
|
|
11
36
|
}
|
|
12
37
|
function TuiApp({ updates, onComplete }) {
|
|
13
38
|
const [cursorIndex, setCursorIndex] = useState(0);
|
|
14
|
-
const [
|
|
39
|
+
const [filterIndex, setFilterIndex] = useState(0);
|
|
40
|
+
const [selectedIndices, setSelectedIndices] = useState(new Set(updates.map((_, index) => index)));
|
|
41
|
+
const activeFilter = FILTER_ORDER[filterIndex] ?? "all";
|
|
42
|
+
const filteredIndices = updates
|
|
43
|
+
.map((update, index) => ({ update, index }))
|
|
44
|
+
.filter(({ update }) => {
|
|
45
|
+
if (activeFilter === "security")
|
|
46
|
+
return (update.advisoryCount ?? 0) > 0;
|
|
47
|
+
if (activeFilter === "risky") {
|
|
48
|
+
return update.riskLevel === "critical" || update.riskLevel === "high";
|
|
49
|
+
}
|
|
50
|
+
if (activeFilter === "major")
|
|
51
|
+
return update.diffType === "major";
|
|
52
|
+
return true;
|
|
53
|
+
})
|
|
54
|
+
.map(({ index }) => index);
|
|
55
|
+
const boundedCursor = Math.min(cursorIndex, Math.max(0, filteredIndices.length - 1));
|
|
56
|
+
const focusedIndex = filteredIndices[boundedCursor] ?? 0;
|
|
57
|
+
const focusedUpdate = updates[focusedIndex];
|
|
15
58
|
useInput((input, key) => {
|
|
59
|
+
if (key.leftArrow) {
|
|
60
|
+
setFilterIndex((prev) => Math.max(0, prev - 1));
|
|
61
|
+
setCursorIndex(0);
|
|
62
|
+
}
|
|
63
|
+
if (key.rightArrow) {
|
|
64
|
+
setFilterIndex((prev) => Math.min(FILTER_ORDER.length - 1, prev + 1));
|
|
65
|
+
setCursorIndex(0);
|
|
66
|
+
}
|
|
16
67
|
if (key.upArrow) {
|
|
17
68
|
setCursorIndex((prev) => Math.max(0, prev - 1));
|
|
18
69
|
}
|
|
19
70
|
if (key.downArrow) {
|
|
20
|
-
setCursorIndex((prev) => Math.min(
|
|
71
|
+
setCursorIndex((prev) => Math.min(filteredIndices.length - 1, Math.max(0, prev + 1)));
|
|
72
|
+
}
|
|
73
|
+
if (input === "a") {
|
|
74
|
+
setSelectedIndices(new Set(filteredIndices));
|
|
75
|
+
}
|
|
76
|
+
if (input === "n") {
|
|
77
|
+
setSelectedIndices(new Set());
|
|
21
78
|
}
|
|
22
79
|
if (input === " ") {
|
|
23
80
|
setSelectedIndices((prev) => {
|
|
24
81
|
const next = new Set(prev);
|
|
25
|
-
if (next.has(
|
|
26
|
-
next.delete(
|
|
82
|
+
if (next.has(focusedIndex))
|
|
83
|
+
next.delete(focusedIndex);
|
|
27
84
|
else
|
|
28
|
-
next.add(
|
|
85
|
+
next.add(focusedIndex);
|
|
29
86
|
return next;
|
|
30
87
|
});
|
|
31
88
|
}
|
|
32
89
|
if (key.return) {
|
|
33
|
-
|
|
34
|
-
onComplete(selected);
|
|
90
|
+
onComplete(updates.filter((_, index) => selectedIndices.has(index)));
|
|
35
91
|
}
|
|
36
92
|
});
|
|
37
|
-
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
93
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Rainy Review TUI" }), _jsx(Text, { color: "gray", children: "Left/Right filter Up/Down move Space toggle A select all view N clear Enter confirm" }), _jsx(Box, { marginTop: 1, children: FILTER_ORDER.map((filter, index) => (_jsx(Box, { marginRight: 2, children: _jsxs(Text, { color: index === filterIndex ? "cyan" : "gray", children: ["[", filter, "]"] }) }, filter))) }), _jsxs(Box, { marginTop: 1, flexDirection: "row", children: [_jsxs(Box, { width: 72, flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: [_jsx(Text, { bold: true, children: "Updates" }), filteredIndices.length === 0 ? (_jsx(Text, { color: "gray", children: "No updates match this filter." })) : (filteredIndices.map((index, visibleIndex) => {
|
|
94
|
+
const update = updates[index];
|
|
95
|
+
const isFocused = visibleIndex === boundedCursor;
|
|
96
|
+
const isSelected = selectedIndices.has(index);
|
|
97
|
+
return (_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: isFocused ? "cyan" : "gray", children: [isFocused ? ">" : " ", " ", isSelected ? "[x]" : "[ ]", " "] }), _jsx(Box, { width: 22, children: _jsx(Text, { bold: isFocused, children: update.name }) }), _jsx(Box, { width: 10, children: _jsx(Text, { color: diffColor(update.diffType), children: update.diffType }) }), _jsx(Box, { width: 18, children: _jsx(Text, { color: riskColor(update.riskLevel), children: update.riskLevel ?? update.impactScore?.rank ?? "low" }) }), _jsx(VersionDiff, { from: update.fromRange, to: update.toVersionResolved })] }, `${update.packagePath}:${update.name}`));
|
|
98
|
+
}))] }), _jsxs(Box, { marginLeft: 1, width: 46, flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: [_jsx(Text, { bold: true, children: "Details" }), focusedUpdate ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: focusedUpdate.name }), _jsxs(Text, { color: "gray", children: ["package: ", focusedUpdate.packagePath] }), _jsxs(Text, { children: ["diff: ", _jsx(Text, { color: diffColor(focusedUpdate.diffType), children: focusedUpdate.diffType })] }), _jsxs(Text, { children: ["risk: ", _jsx(Text, { color: riskColor(focusedUpdate.riskLevel), children: focusedUpdate.riskLevel ?? focusedUpdate.impactScore?.rank ?? "low" })] }), _jsxs(Text, { children: ["impact: ", focusedUpdate.impactScore?.score ?? 0] }), _jsxs(Text, { children: ["advisories: ", focusedUpdate.advisoryCount ?? 0] }), _jsxs(Text, { children: ["peer: ", focusedUpdate.peerConflictSeverity ?? "none"] }), _jsxs(Text, { children: ["license: ", focusedUpdate.licenseStatus ?? "allowed"] }), _jsxs(Text, { children: ["health: ", focusedUpdate.healthStatus ?? "healthy"] }), focusedUpdate.homepage ? (_jsxs(Text, { color: "blue", children: ["homepage: ", focusedUpdate.homepage] })) : (_jsx(Text, { color: "gray", children: "homepage: unavailable" })), focusedUpdate.riskReasons && focusedUpdate.riskReasons.length > 0 ? (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, children: "Reasons" }), focusedUpdate.riskReasons.slice(0, 4).map((reason) => (_jsxs(Text, { color: "gray", children: ["- ", reason] }, reason)))] })) : (_jsx(Text, { color: "gray", children: "No elevated risk reasons." }))] })) : (_jsx(Text, { color: "gray", children: "No update selected." }))] })] }), _jsx(Box, { marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, children: _jsxs(Text, { color: "gray", children: [selectedIndices.size, " selected of ", updates.length, ". Filter: ", activeFilter, "."] }) })] }));
|
|
42
99
|
}
|
|
43
100
|
export async function runTui(updates) {
|
|
44
101
|
return new Promise((resolve) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rainy-updates/cli",
|
|
3
|
-
"version": "0.5.2
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "The fastest DevOps-first dependency CLI. Checks, audits, upgrades, bisects, and automates npm/pnpm dependencies in CI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -56,7 +56,10 @@
|
|
|
56
56
|
"test": "bun test",
|
|
57
57
|
"lint": "bunx biome check src tests",
|
|
58
58
|
"check": "bun run typecheck && bun test",
|
|
59
|
-
"perf:smoke": "node scripts/perf-smoke.mjs",
|
|
59
|
+
"perf:smoke": "node scripts/perf-smoke.mjs && RAINY_UPDATES_PERF_SCENARIO=resolve node scripts/perf-smoke.mjs && RAINY_UPDATES_PERF_SCENARIO=ci node scripts/perf-smoke.mjs",
|
|
60
|
+
"perf:check": "node scripts/perf-smoke.mjs",
|
|
61
|
+
"perf:resolve": "RAINY_UPDATES_PERF_SCENARIO=resolve node scripts/perf-smoke.mjs",
|
|
62
|
+
"perf:ci": "RAINY_UPDATES_PERF_SCENARIO=ci node scripts/perf-smoke.mjs",
|
|
60
63
|
"test:prod": "node dist/bin/cli.js --help && node dist/bin/cli.js --version",
|
|
61
64
|
"prepublishOnly": "bun run check && bun run build && bun run test:prod"
|
|
62
65
|
},
|