@rainy-updates/cli 0.5.1 → 0.5.2-rc.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 +93 -1
- package/README.md +88 -25
- package/dist/bin/cli.js +50 -1
- package/dist/commands/audit/fetcher.d.ts +2 -6
- package/dist/commands/audit/fetcher.js +2 -79
- package/dist/commands/audit/mapper.d.ts +8 -1
- package/dist/commands/audit/mapper.js +106 -10
- package/dist/commands/audit/parser.js +36 -2
- package/dist/commands/audit/runner.js +179 -15
- package/dist/commands/audit/sources/github.d.ts +2 -0
- package/dist/commands/audit/sources/github.js +125 -0
- package/dist/commands/audit/sources/index.d.ts +6 -0
- package/dist/commands/audit/sources/index.js +92 -0
- package/dist/commands/audit/sources/osv.d.ts +2 -0
- package/dist/commands/audit/sources/osv.js +131 -0
- package/dist/commands/audit/sources/types.d.ts +21 -0
- package/dist/commands/audit/sources/types.js +1 -0
- package/dist/commands/audit/targets.d.ts +20 -0
- package/dist/commands/audit/targets.js +314 -0
- package/dist/commands/changelog/fetcher.d.ts +9 -0
- package/dist/commands/changelog/fetcher.js +130 -0
- package/dist/commands/licenses/parser.d.ts +2 -0
- package/dist/commands/licenses/parser.js +116 -0
- package/dist/commands/licenses/runner.d.ts +9 -0
- package/dist/commands/licenses/runner.js +163 -0
- package/dist/commands/licenses/sbom.d.ts +10 -0
- package/dist/commands/licenses/sbom.js +70 -0
- package/dist/commands/resolve/graph/builder.d.ts +20 -0
- package/dist/commands/resolve/graph/builder.js +183 -0
- package/dist/commands/resolve/graph/conflict.d.ts +20 -0
- package/dist/commands/resolve/graph/conflict.js +52 -0
- package/dist/commands/resolve/graph/resolver.d.ts +17 -0
- package/dist/commands/resolve/graph/resolver.js +71 -0
- package/dist/commands/resolve/parser.d.ts +2 -0
- package/dist/commands/resolve/parser.js +89 -0
- package/dist/commands/resolve/runner.d.ts +13 -0
- package/dist/commands/resolve/runner.js +136 -0
- package/dist/commands/snapshot/parser.d.ts +2 -0
- package/dist/commands/snapshot/parser.js +80 -0
- package/dist/commands/snapshot/runner.d.ts +11 -0
- package/dist/commands/snapshot/runner.js +115 -0
- package/dist/commands/snapshot/store.d.ts +35 -0
- package/dist/commands/snapshot/store.js +158 -0
- package/dist/commands/unused/matcher.d.ts +22 -0
- package/dist/commands/unused/matcher.js +95 -0
- package/dist/commands/unused/parser.d.ts +2 -0
- package/dist/commands/unused/parser.js +95 -0
- package/dist/commands/unused/runner.d.ts +11 -0
- package/dist/commands/unused/runner.js +113 -0
- package/dist/commands/unused/scanner.d.ts +18 -0
- package/dist/commands/unused/scanner.js +129 -0
- package/dist/core/impact.d.ts +36 -0
- package/dist/core/impact.js +82 -0
- package/dist/core/options.d.ts +13 -1
- package/dist/core/options.js +35 -13
- package/dist/types/index.d.ts +187 -1
- package/dist/ui/tui.d.ts +6 -0
- package/dist/ui/tui.js +50 -0
- package/dist/utils/semver.d.ts +18 -0
- package/dist/utils/semver.js +88 -3
- package/package.json +8 -1
package/dist/types/index.d.ts
CHANGED
|
@@ -64,6 +64,15 @@ export interface PackageDependency {
|
|
|
64
64
|
range: string;
|
|
65
65
|
kind: DependencyKind;
|
|
66
66
|
}
|
|
67
|
+
export interface ImpactScore {
|
|
68
|
+
rank: "critical" | "high" | "medium" | "low";
|
|
69
|
+
score: number;
|
|
70
|
+
factors: {
|
|
71
|
+
diffTypeWeight: number;
|
|
72
|
+
hasAdvisory: boolean;
|
|
73
|
+
affectedWorkspaceCount: number;
|
|
74
|
+
};
|
|
75
|
+
}
|
|
67
76
|
export interface PackageUpdate {
|
|
68
77
|
packagePath: string;
|
|
69
78
|
name: string;
|
|
@@ -75,6 +84,8 @@ export interface PackageUpdate {
|
|
|
75
84
|
filtered: boolean;
|
|
76
85
|
autofix: boolean;
|
|
77
86
|
reason?: string;
|
|
87
|
+
impactScore?: ImpactScore;
|
|
88
|
+
homepage?: string;
|
|
78
89
|
}
|
|
79
90
|
export interface Summary {
|
|
80
91
|
contractVersion: "2";
|
|
@@ -151,14 +162,20 @@ export interface VersionResolver {
|
|
|
151
162
|
resolveLatestVersion(packageName: string): Promise<string | null>;
|
|
152
163
|
}
|
|
153
164
|
export type AuditSeverity = "critical" | "high" | "medium" | "low";
|
|
154
|
-
export type AuditReportFormat = "table" | "json";
|
|
165
|
+
export type AuditReportFormat = "table" | "json" | "summary";
|
|
166
|
+
export type AuditSourceMode = "auto" | "osv" | "github" | "all";
|
|
167
|
+
export type AuditSourceName = "osv" | "github";
|
|
168
|
+
export type AuditSourceStatusLevel = "ok" | "partial" | "failed";
|
|
155
169
|
export interface AuditOptions {
|
|
156
170
|
cwd: string;
|
|
157
171
|
workspace: boolean;
|
|
158
172
|
severity?: AuditSeverity;
|
|
159
173
|
fix: boolean;
|
|
160
174
|
dryRun: boolean;
|
|
175
|
+
commit: boolean;
|
|
176
|
+
packageManager: "auto" | "npm" | "pnpm" | "bun" | "yarn";
|
|
161
177
|
reportFormat: AuditReportFormat;
|
|
178
|
+
sourceMode: AuditSourceMode;
|
|
162
179
|
jsonFile?: string;
|
|
163
180
|
concurrency: number;
|
|
164
181
|
registryTimeoutMs: number;
|
|
@@ -166,17 +183,44 @@ export interface AuditOptions {
|
|
|
166
183
|
export interface CveAdvisory {
|
|
167
184
|
cveId: string;
|
|
168
185
|
packageName: string;
|
|
186
|
+
currentVersion: string | null;
|
|
169
187
|
severity: AuditSeverity;
|
|
170
188
|
vulnerableRange: string;
|
|
171
189
|
patchedVersion: string | null;
|
|
172
190
|
title: string;
|
|
173
191
|
url: string;
|
|
192
|
+
sources: readonly AuditSourceName[];
|
|
193
|
+
}
|
|
194
|
+
export interface AuditPackageSummary {
|
|
195
|
+
packageName: string;
|
|
196
|
+
currentVersion: string | null;
|
|
197
|
+
severity: AuditSeverity;
|
|
198
|
+
advisoryCount: number;
|
|
199
|
+
patchedVersion: string | null;
|
|
200
|
+
sources: readonly AuditSourceName[];
|
|
201
|
+
}
|
|
202
|
+
export interface AuditSourceStatus {
|
|
203
|
+
source: AuditSourceName;
|
|
204
|
+
status: AuditSourceStatusLevel;
|
|
205
|
+
attemptedTargets: number;
|
|
206
|
+
successfulTargets: number;
|
|
207
|
+
failedTargets: number;
|
|
208
|
+
advisoriesFound: number;
|
|
209
|
+
message?: string;
|
|
174
210
|
}
|
|
175
211
|
export interface AuditResult {
|
|
176
212
|
advisories: CveAdvisory[];
|
|
213
|
+
packages: AuditPackageSummary[];
|
|
177
214
|
autoFixable: number;
|
|
178
215
|
errors: string[];
|
|
179
216
|
warnings: string[];
|
|
217
|
+
sourcesUsed: AuditSourceName[];
|
|
218
|
+
sourceHealth: AuditSourceStatus[];
|
|
219
|
+
resolution: {
|
|
220
|
+
lockfile: number;
|
|
221
|
+
manifest: number;
|
|
222
|
+
unresolved: number;
|
|
223
|
+
};
|
|
180
224
|
}
|
|
181
225
|
export interface BisectOptions {
|
|
182
226
|
cwd: string;
|
|
@@ -224,3 +268,145 @@ export interface HealthResult {
|
|
|
224
268
|
errors: string[];
|
|
225
269
|
warnings: string[];
|
|
226
270
|
}
|
|
271
|
+
export interface PeerNode {
|
|
272
|
+
name: string;
|
|
273
|
+
resolvedVersion: string;
|
|
274
|
+
peerRequirements: Map<string, string>;
|
|
275
|
+
}
|
|
276
|
+
export interface PeerGraph {
|
|
277
|
+
nodes: Map<string, PeerNode>;
|
|
278
|
+
roots: string[];
|
|
279
|
+
}
|
|
280
|
+
export type PeerConflictSeverity = "error" | "warning";
|
|
281
|
+
export interface PeerConflict {
|
|
282
|
+
requester: string;
|
|
283
|
+
peer: string;
|
|
284
|
+
requiredRange: string;
|
|
285
|
+
resolvedVersion: string;
|
|
286
|
+
severity: PeerConflictSeverity;
|
|
287
|
+
suggestion: string;
|
|
288
|
+
}
|
|
289
|
+
export interface ResolveOptions {
|
|
290
|
+
cwd: string;
|
|
291
|
+
workspace: boolean;
|
|
292
|
+
afterUpdate: boolean;
|
|
293
|
+
safe: boolean;
|
|
294
|
+
jsonFile?: string;
|
|
295
|
+
concurrency: number;
|
|
296
|
+
registryTimeoutMs: number;
|
|
297
|
+
cacheTtlSeconds: number;
|
|
298
|
+
}
|
|
299
|
+
export interface ResolveResult {
|
|
300
|
+
conflicts: PeerConflict[];
|
|
301
|
+
errorConflicts: number;
|
|
302
|
+
warningConflicts: number;
|
|
303
|
+
errors: string[];
|
|
304
|
+
warnings: string[];
|
|
305
|
+
}
|
|
306
|
+
export type UnusedKind = "declared-not-imported" | "imported-not-declared";
|
|
307
|
+
export interface UnusedDependency {
|
|
308
|
+
name: string;
|
|
309
|
+
kind: UnusedKind;
|
|
310
|
+
declaredIn?: string;
|
|
311
|
+
importedFrom?: string;
|
|
312
|
+
}
|
|
313
|
+
export interface UnusedOptions {
|
|
314
|
+
cwd: string;
|
|
315
|
+
workspace: boolean;
|
|
316
|
+
srcDirs: string[];
|
|
317
|
+
includeDevDependencies: boolean;
|
|
318
|
+
fix: boolean;
|
|
319
|
+
dryRun: boolean;
|
|
320
|
+
jsonFile?: string;
|
|
321
|
+
concurrency: number;
|
|
322
|
+
}
|
|
323
|
+
export interface UnusedResult {
|
|
324
|
+
unused: UnusedDependency[];
|
|
325
|
+
missing: UnusedDependency[];
|
|
326
|
+
totalUnused: number;
|
|
327
|
+
totalMissing: number;
|
|
328
|
+
errors: string[];
|
|
329
|
+
warnings: string[];
|
|
330
|
+
}
|
|
331
|
+
export interface PackageLicense {
|
|
332
|
+
name: string;
|
|
333
|
+
version: string;
|
|
334
|
+
license: string;
|
|
335
|
+
spdxExpression: string | null;
|
|
336
|
+
homepage?: string;
|
|
337
|
+
repository?: string;
|
|
338
|
+
}
|
|
339
|
+
export interface SbomDocument {
|
|
340
|
+
spdxVersion: "SPDX-2.3";
|
|
341
|
+
dataLicense: "CC0-1.0";
|
|
342
|
+
name: string;
|
|
343
|
+
documentNamespace: string;
|
|
344
|
+
packages: SbomPackage[];
|
|
345
|
+
relationships: SbomRelationship[];
|
|
346
|
+
}
|
|
347
|
+
export interface SbomPackage {
|
|
348
|
+
SPDXID: string;
|
|
349
|
+
name: string;
|
|
350
|
+
versionInfo: string;
|
|
351
|
+
downloadLocation: string;
|
|
352
|
+
licenseConcluded: string;
|
|
353
|
+
licenseDeclared: string;
|
|
354
|
+
copyrightText: string;
|
|
355
|
+
}
|
|
356
|
+
export interface SbomRelationship {
|
|
357
|
+
spdxElementId: string;
|
|
358
|
+
relationshipType: "DESCRIBES" | "DEPENDS_ON";
|
|
359
|
+
relatedSpdxElement: string;
|
|
360
|
+
}
|
|
361
|
+
export interface LicenseOptions {
|
|
362
|
+
cwd: string;
|
|
363
|
+
workspace: boolean;
|
|
364
|
+
allow?: string[];
|
|
365
|
+
deny?: string[];
|
|
366
|
+
sbomFile?: string;
|
|
367
|
+
jsonFile?: string;
|
|
368
|
+
diffMode: boolean;
|
|
369
|
+
concurrency: number;
|
|
370
|
+
registryTimeoutMs: number;
|
|
371
|
+
cacheTtlSeconds: number;
|
|
372
|
+
}
|
|
373
|
+
export interface LicenseResult {
|
|
374
|
+
packages: PackageLicense[];
|
|
375
|
+
violations: PackageLicense[];
|
|
376
|
+
totalViolations: number;
|
|
377
|
+
errors: string[];
|
|
378
|
+
warnings: string[];
|
|
379
|
+
}
|
|
380
|
+
export interface SnapshotEntry {
|
|
381
|
+
id: string;
|
|
382
|
+
label: string;
|
|
383
|
+
createdAt: number;
|
|
384
|
+
manifests: Record<string, string>;
|
|
385
|
+
lockfileHashes: Record<string, string>;
|
|
386
|
+
}
|
|
387
|
+
export type SnapshotAction = "save" | "list" | "restore" | "diff";
|
|
388
|
+
export interface SnapshotOptions {
|
|
389
|
+
cwd: string;
|
|
390
|
+
workspace: boolean;
|
|
391
|
+
action: SnapshotAction;
|
|
392
|
+
label?: string;
|
|
393
|
+
snapshotId?: string;
|
|
394
|
+
storeFile?: string;
|
|
395
|
+
}
|
|
396
|
+
export interface SnapshotResult {
|
|
397
|
+
action: SnapshotAction;
|
|
398
|
+
snapshotId?: string;
|
|
399
|
+
label?: string;
|
|
400
|
+
entries?: Array<{
|
|
401
|
+
id: string;
|
|
402
|
+
label: string;
|
|
403
|
+
createdAt: string;
|
|
404
|
+
}>;
|
|
405
|
+
diff?: Array<{
|
|
406
|
+
name: string;
|
|
407
|
+
from: string;
|
|
408
|
+
to: string;
|
|
409
|
+
}>;
|
|
410
|
+
errors: string[];
|
|
411
|
+
warnings: string[];
|
|
412
|
+
}
|
package/dist/ui/tui.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
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
|
+
export declare function runTui(updates: PackageUpdate[]): Promise<PackageUpdate[]>;
|
package/dist/ui/tui.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { render, Text, Box, useInput } from "ink";
|
|
4
|
+
// Basic version diff string parser to split major.minor.patch
|
|
5
|
+
export function VersionDiff({ from, to }) {
|
|
6
|
+
if (from === to)
|
|
7
|
+
return _jsx(Text, { color: "gray", children: to });
|
|
8
|
+
// Very simplistic semver coloring: highlight the changed part
|
|
9
|
+
// E.g., from 1.2.3 to 1.3.0 -> 1 is dim, 3.0 is bright green
|
|
10
|
+
return (_jsxs(Box, { children: [_jsxs(Text, { color: "gray", children: [from, " \u2192 "] }), _jsx(Text, { color: "green", children: to })] }));
|
|
11
|
+
}
|
|
12
|
+
function TuiApp({ updates, onComplete }) {
|
|
13
|
+
const [cursorIndex, setCursorIndex] = useState(0);
|
|
14
|
+
const [selectedIndices, setSelectedIndices] = useState(new Set(updates.map((_, i) => i)));
|
|
15
|
+
useInput((input, key) => {
|
|
16
|
+
if (key.upArrow) {
|
|
17
|
+
setCursorIndex((prev) => Math.max(0, prev - 1));
|
|
18
|
+
}
|
|
19
|
+
if (key.downArrow) {
|
|
20
|
+
setCursorIndex((prev) => Math.min(updates.length - 1, prev + 1));
|
|
21
|
+
}
|
|
22
|
+
if (input === " ") {
|
|
23
|
+
setSelectedIndices((prev) => {
|
|
24
|
+
const next = new Set(prev);
|
|
25
|
+
if (next.has(cursorIndex))
|
|
26
|
+
next.delete(cursorIndex);
|
|
27
|
+
else
|
|
28
|
+
next.add(cursorIndex);
|
|
29
|
+
return next;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
if (key.return) {
|
|
33
|
+
const selected = updates.filter((_, i) => selectedIndices.has(i));
|
|
34
|
+
onComplete(selected);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Choose updates to install (Space to toggle, Enter to confirm, Up/Down to navigate)" }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: updates.map((update, index) => {
|
|
38
|
+
const isSelected = selectedIndices.has(index);
|
|
39
|
+
const isFocused = cursorIndex === index;
|
|
40
|
+
return (_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: isFocused ? "cyan" : "gray", children: [isFocused ? "❯ " : " ", isSelected ? "◉ " : "◯ "] }), _jsx(Box, { width: 30, children: _jsx(Text, { bold: isFocused, children: update.name }) }), _jsx(Box, { width: 15, children: _jsx(Text, { color: "gray", children: update.diffType }) }), _jsx(VersionDiff, { from: update.fromRange, to: update.toVersionResolved })] }, update.name));
|
|
41
|
+
}) }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "gray", children: [selectedIndices.size, " of ", updates.length, " selected"] }) })] }));
|
|
42
|
+
}
|
|
43
|
+
export async function runTui(updates) {
|
|
44
|
+
return new Promise((resolve) => {
|
|
45
|
+
const { unmount } = render(_jsx(TuiApp, { updates: updates, onComplete: (selected) => {
|
|
46
|
+
unmount();
|
|
47
|
+
resolve(selected);
|
|
48
|
+
} }));
|
|
49
|
+
});
|
|
50
|
+
}
|
package/dist/utils/semver.d.ts
CHANGED
|
@@ -12,3 +12,21 @@ export declare function pickTargetVersion(currentRange: string, latestVersion: s
|
|
|
12
12
|
export declare function pickTargetVersionFromAvailable(currentRange: string, availableVersions: string[], latestVersion: string, target: TargetLevel): string | null;
|
|
13
13
|
export declare function applyRangeStyle(previousRange: string, version: string): string;
|
|
14
14
|
export declare function clampTarget(requested: TargetLevel, maxAllowed?: TargetLevel): TargetLevel;
|
|
15
|
+
/**
|
|
16
|
+
* Checks whether a concrete version satisfies a semver range expression.
|
|
17
|
+
*
|
|
18
|
+
* Handles the common npm range operators used in peerDependencies:
|
|
19
|
+
* exact: "1.2.3" → version must equal
|
|
20
|
+
* ^: "^1.2.3" → major must match, version must be >=
|
|
21
|
+
* ~: "~1.2.3" → major+minor must match, version must be >=
|
|
22
|
+
* >=: ">=1.2.3" → version must be >=
|
|
23
|
+
* <=: "<=1.2.3" → version must be <=
|
|
24
|
+
* >: ">1.2.3" → version must be >
|
|
25
|
+
* <: "<1.2.3" → version must be <
|
|
26
|
+
* *: "*" | "" → always true
|
|
27
|
+
* ranges: ">=1 <3" → all space-separated clauses AND-ed together
|
|
28
|
+
*
|
|
29
|
+
* Does NOT handle || unions or hyphen ranges — those are rare in peerDependencies
|
|
30
|
+
* and degrade gracefully (returns true to avoid false-positive conflicts).
|
|
31
|
+
*/
|
|
32
|
+
export declare function satisfies(version: string, range: string): boolean;
|
package/dist/utils/semver.js
CHANGED
|
@@ -45,13 +45,16 @@ export function pickTargetVersion(currentRange, latestVersion, target) {
|
|
|
45
45
|
if (!current || target === "latest")
|
|
46
46
|
return latestVersion;
|
|
47
47
|
if (target === "patch") {
|
|
48
|
-
if (current.major === latest.major &&
|
|
48
|
+
if (current.major === latest.major &&
|
|
49
|
+
current.minor === latest.minor &&
|
|
50
|
+
latest.patch > current.patch) {
|
|
49
51
|
return latestVersion;
|
|
50
52
|
}
|
|
51
53
|
return null;
|
|
52
54
|
}
|
|
53
55
|
if (target === "minor") {
|
|
54
|
-
if (current.major === latest.major &&
|
|
56
|
+
if (current.major === latest.major &&
|
|
57
|
+
compareVersions(latest, current) > 0) {
|
|
55
58
|
return latestVersion;
|
|
56
59
|
}
|
|
57
60
|
return null;
|
|
@@ -83,7 +86,8 @@ export function pickTargetVersionFromAvailable(currentRange, availableVersions,
|
|
|
83
86
|
return sameMajor.length > 0 ? sameMajor[sameMajor.length - 1].raw : null;
|
|
84
87
|
}
|
|
85
88
|
if (target === "patch") {
|
|
86
|
-
const sameLine = parsed.filter((item) => item.parsed.major === current.major &&
|
|
89
|
+
const sameLine = parsed.filter((item) => item.parsed.major === current.major &&
|
|
90
|
+
item.parsed.minor === current.minor);
|
|
87
91
|
return sameLine.length > 0 ? sameLine[sameLine.length - 1].raw : null;
|
|
88
92
|
}
|
|
89
93
|
return latestVersion;
|
|
@@ -102,3 +106,84 @@ export function clampTarget(requested, maxAllowed) {
|
|
|
102
106
|
return requested;
|
|
103
107
|
return TARGET_ORDER[Math.min(requestedIndex, allowedIndex)];
|
|
104
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Checks whether a concrete version satisfies a semver range expression.
|
|
111
|
+
*
|
|
112
|
+
* Handles the common npm range operators used in peerDependencies:
|
|
113
|
+
* exact: "1.2.3" → version must equal
|
|
114
|
+
* ^: "^1.2.3" → major must match, version must be >=
|
|
115
|
+
* ~: "~1.2.3" → major+minor must match, version must be >=
|
|
116
|
+
* >=: ">=1.2.3" → version must be >=
|
|
117
|
+
* <=: "<=1.2.3" → version must be <=
|
|
118
|
+
* >: ">1.2.3" → version must be >
|
|
119
|
+
* <: "<1.2.3" → version must be <
|
|
120
|
+
* *: "*" | "" → always true
|
|
121
|
+
* ranges: ">=1 <3" → all space-separated clauses AND-ed together
|
|
122
|
+
*
|
|
123
|
+
* Does NOT handle || unions or hyphen ranges — those are rare in peerDependencies
|
|
124
|
+
* and degrade gracefully (returns true to avoid false-positive conflicts).
|
|
125
|
+
*/
|
|
126
|
+
export function satisfies(version, range) {
|
|
127
|
+
const trimmedRange = range.trim();
|
|
128
|
+
if (!trimmedRange || trimmedRange === "*")
|
|
129
|
+
return true;
|
|
130
|
+
const parsed = parseVersion(version);
|
|
131
|
+
if (!parsed)
|
|
132
|
+
return true; // non-semver versions (e.g. "latest", "workspace:*") pass through
|
|
133
|
+
// Handle OR unions — split on " || " and return true if any clause matches
|
|
134
|
+
if (trimmedRange.includes("||")) {
|
|
135
|
+
return trimmedRange
|
|
136
|
+
.split("||")
|
|
137
|
+
.some((clause) => satisfies(version, clause.trim()));
|
|
138
|
+
}
|
|
139
|
+
// Handle AND ranges — split on whitespace and require all clauses to match
|
|
140
|
+
const clauses = trimmedRange.split(/\s+/).filter(Boolean);
|
|
141
|
+
if (clauses.length > 1) {
|
|
142
|
+
return clauses.every((clause) => satisfies(version, clause));
|
|
143
|
+
}
|
|
144
|
+
const clause = clauses[0] ?? "";
|
|
145
|
+
const op = parseRangeOperator(clause);
|
|
146
|
+
if (!op)
|
|
147
|
+
return true;
|
|
148
|
+
const cmp = compareVersions(parsed, op.version);
|
|
149
|
+
switch (op.operator) {
|
|
150
|
+
case "^": {
|
|
151
|
+
// same major, version >= bound
|
|
152
|
+
return parsed.major === op.version.major && cmp >= 0;
|
|
153
|
+
}
|
|
154
|
+
case "~": {
|
|
155
|
+
// same major+minor, version >= bound
|
|
156
|
+
return (parsed.major === op.version.major &&
|
|
157
|
+
parsed.minor === op.version.minor &&
|
|
158
|
+
cmp >= 0);
|
|
159
|
+
}
|
|
160
|
+
case ">=":
|
|
161
|
+
return cmp >= 0;
|
|
162
|
+
case "<=":
|
|
163
|
+
return cmp <= 0;
|
|
164
|
+
case ">":
|
|
165
|
+
return cmp > 0;
|
|
166
|
+
case "<":
|
|
167
|
+
return cmp < 0;
|
|
168
|
+
case "=":
|
|
169
|
+
return cmp === 0;
|
|
170
|
+
default:
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function parseRangeOperator(clause) {
|
|
175
|
+
const ops = [">=", "<=", "^", "~", ">", "<", "="];
|
|
176
|
+
for (const op of ops) {
|
|
177
|
+
if (clause.startsWith(op)) {
|
|
178
|
+
const versionStr = clause.slice(op.length);
|
|
179
|
+
const parsed = parseVersion(versionStr);
|
|
180
|
+
if (parsed)
|
|
181
|
+
return { operator: op, version: parsed };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Bare version string — treat as exact
|
|
185
|
+
const parsed = parseVersion(clause);
|
|
186
|
+
if (parsed)
|
|
187
|
+
return { operator: "=", version: parsed };
|
|
188
|
+
return null;
|
|
189
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rainy-updates/cli",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2-rc.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,
|
|
@@ -69,9 +69,16 @@
|
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/bun": "latest",
|
|
72
|
+
"@types/react": "^19.2.14",
|
|
73
|
+
"ink-testing-library": "^4.0.0",
|
|
72
74
|
"typescript": "^5.9.3"
|
|
73
75
|
},
|
|
74
76
|
"optionalDependencies": {
|
|
75
77
|
"undici": "^7.22.0"
|
|
78
|
+
},
|
|
79
|
+
"dependencies": {
|
|
80
|
+
"ink": "^6.8.0",
|
|
81
|
+
"react": "^19.2.4",
|
|
82
|
+
"zod": "^4.3.6"
|
|
76
83
|
}
|
|
77
84
|
}
|