@rainy-updates/cli 0.5.1-rc.3 → 0.5.2-rc.1
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 +145 -2
- package/README.md +92 -26
- package/dist/bin/cli.js +87 -7
- package/dist/commands/audit/fetcher.d.ts +6 -0
- package/dist/commands/audit/fetcher.js +79 -0
- package/dist/commands/audit/mapper.d.ts +16 -0
- package/dist/commands/audit/mapper.js +61 -0
- package/dist/commands/audit/parser.d.ts +3 -0
- package/dist/commands/audit/parser.js +87 -0
- package/dist/commands/audit/runner.d.ts +7 -0
- package/dist/commands/audit/runner.js +64 -0
- package/dist/commands/bisect/engine.d.ts +12 -0
- package/dist/commands/bisect/engine.js +89 -0
- package/dist/commands/bisect/oracle.d.ts +7 -0
- package/dist/commands/bisect/oracle.js +36 -0
- package/dist/commands/bisect/parser.d.ts +2 -0
- package/dist/commands/bisect/parser.js +73 -0
- package/dist/commands/bisect/runner.d.ts +6 -0
- package/dist/commands/bisect/runner.js +27 -0
- package/dist/commands/health/parser.d.ts +2 -0
- package/dist/commands/health/parser.js +90 -0
- package/dist/commands/health/runner.d.ts +7 -0
- package/dist/commands/health/runner.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/config/loader.d.ts +5 -1
- package/dist/config/policy.d.ts +4 -0
- package/dist/config/policy.js +2 -0
- package/dist/core/check.js +56 -3
- package/dist/core/fix-pr-batch.js +3 -2
- package/dist/core/fix-pr.js +19 -4
- package/dist/core/impact.d.ts +36 -0
- package/dist/core/impact.js +82 -0
- package/dist/core/init-ci.js +3 -3
- package/dist/core/options.d.ts +22 -1
- package/dist/core/options.js +151 -13
- package/dist/core/summary.d.ts +1 -0
- package/dist/core/summary.js +11 -1
- package/dist/core/upgrade.js +10 -0
- package/dist/core/warm-cache.js +19 -1
- package/dist/output/format.js +4 -0
- package/dist/output/github.js +3 -0
- package/dist/registry/npm.d.ts +9 -2
- package/dist/registry/npm.js +87 -17
- package/dist/types/index.d.ts +236 -0
- package/dist/utils/lockfile.d.ts +5 -0
- package/dist/utils/lockfile.js +44 -0
- package/dist/utils/semver.d.ts +18 -0
- package/dist/utils/semver.js +88 -3
- package/package.json +13 -4
package/dist/types/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export type DependencyKind = "dependencies" | "devDependencies" | "optionalDepen
|
|
|
2
2
|
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
|
+
export type LockfileMode = "preserve" | "update" | "error";
|
|
5
6
|
export type OutputFormat = "table" | "json" | "minimal" | "github" | "metrics";
|
|
6
7
|
export type FailOnLevel = "none" | "patch" | "minor" | "major" | "any";
|
|
7
8
|
export type LogLevel = "error" | "warn" | "info" | "debug";
|
|
@@ -20,7 +21,10 @@ export interface RunOptions {
|
|
|
20
21
|
githubOutputFile?: string;
|
|
21
22
|
sarifFile?: string;
|
|
22
23
|
concurrency: number;
|
|
24
|
+
registryTimeoutMs: number;
|
|
25
|
+
registryRetries: number;
|
|
23
26
|
offline: boolean;
|
|
27
|
+
stream: boolean;
|
|
24
28
|
policyFile?: string;
|
|
25
29
|
prReportFile?: string;
|
|
26
30
|
failOn?: FailOnLevel;
|
|
@@ -39,6 +43,7 @@ export interface RunOptions {
|
|
|
39
43
|
prLimit?: number;
|
|
40
44
|
onlyChanged: boolean;
|
|
41
45
|
ciProfile: CiProfile;
|
|
46
|
+
lockfileMode: LockfileMode;
|
|
42
47
|
}
|
|
43
48
|
export interface CheckOptions extends RunOptions {
|
|
44
49
|
}
|
|
@@ -59,6 +64,15 @@ export interface PackageDependency {
|
|
|
59
64
|
range: string;
|
|
60
65
|
kind: DependencyKind;
|
|
61
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
|
+
}
|
|
62
76
|
export interface PackageUpdate {
|
|
63
77
|
packagePath: string;
|
|
64
78
|
name: string;
|
|
@@ -68,7 +82,10 @@ export interface PackageUpdate {
|
|
|
68
82
|
toVersionResolved: string;
|
|
69
83
|
diffType: TargetLevel;
|
|
70
84
|
filtered: boolean;
|
|
85
|
+
autofix: boolean;
|
|
71
86
|
reason?: string;
|
|
87
|
+
impactScore?: ImpactScore;
|
|
88
|
+
homepage?: string;
|
|
72
89
|
}
|
|
73
90
|
export interface Summary {
|
|
74
91
|
contractVersion: "2";
|
|
@@ -84,6 +101,7 @@ export interface Summary {
|
|
|
84
101
|
total: number;
|
|
85
102
|
offlineCacheMiss: number;
|
|
86
103
|
registryFailure: number;
|
|
104
|
+
registryAuthFailure: number;
|
|
87
105
|
other: number;
|
|
88
106
|
};
|
|
89
107
|
warningCounts: {
|
|
@@ -106,6 +124,8 @@ export interface Summary {
|
|
|
106
124
|
cooldownSkipped: number;
|
|
107
125
|
ciProfile: CiProfile;
|
|
108
126
|
prLimitHit: boolean;
|
|
127
|
+
streamedEvents: number;
|
|
128
|
+
policyOverridesApplied: number;
|
|
109
129
|
}
|
|
110
130
|
export interface CheckResult {
|
|
111
131
|
projectPath: string;
|
|
@@ -141,3 +161,219 @@ export interface CachedVersion {
|
|
|
141
161
|
export interface VersionResolver {
|
|
142
162
|
resolveLatestVersion(packageName: string): Promise<string | null>;
|
|
143
163
|
}
|
|
164
|
+
export type AuditSeverity = "critical" | "high" | "medium" | "low";
|
|
165
|
+
export type AuditReportFormat = "table" | "json";
|
|
166
|
+
export interface AuditOptions {
|
|
167
|
+
cwd: string;
|
|
168
|
+
workspace: boolean;
|
|
169
|
+
severity?: AuditSeverity;
|
|
170
|
+
fix: boolean;
|
|
171
|
+
dryRun: boolean;
|
|
172
|
+
reportFormat: AuditReportFormat;
|
|
173
|
+
jsonFile?: string;
|
|
174
|
+
concurrency: number;
|
|
175
|
+
registryTimeoutMs: number;
|
|
176
|
+
}
|
|
177
|
+
export interface CveAdvisory {
|
|
178
|
+
cveId: string;
|
|
179
|
+
packageName: string;
|
|
180
|
+
severity: AuditSeverity;
|
|
181
|
+
vulnerableRange: string;
|
|
182
|
+
patchedVersion: string | null;
|
|
183
|
+
title: string;
|
|
184
|
+
url: string;
|
|
185
|
+
}
|
|
186
|
+
export interface AuditResult {
|
|
187
|
+
advisories: CveAdvisory[];
|
|
188
|
+
autoFixable: number;
|
|
189
|
+
errors: string[];
|
|
190
|
+
warnings: string[];
|
|
191
|
+
}
|
|
192
|
+
export interface BisectOptions {
|
|
193
|
+
cwd: string;
|
|
194
|
+
packageName: string;
|
|
195
|
+
versionRange?: string;
|
|
196
|
+
testCommand: string;
|
|
197
|
+
concurrency: number;
|
|
198
|
+
registryTimeoutMs: number;
|
|
199
|
+
cacheTtlSeconds: number;
|
|
200
|
+
dryRun: boolean;
|
|
201
|
+
}
|
|
202
|
+
export type BisectOutcome = "good" | "bad" | "skip";
|
|
203
|
+
export interface BisectResult {
|
|
204
|
+
packageName: string;
|
|
205
|
+
breakingVersion: string | null;
|
|
206
|
+
lastGoodVersion: string | null;
|
|
207
|
+
totalVersionsTested: number;
|
|
208
|
+
iterations: number;
|
|
209
|
+
}
|
|
210
|
+
export type HealthFlag = "stale" | "deprecated" | "archived" | "unmaintained";
|
|
211
|
+
export interface HealthOptions {
|
|
212
|
+
cwd: string;
|
|
213
|
+
workspace: boolean;
|
|
214
|
+
staleDays: number;
|
|
215
|
+
includeDeprecated: boolean;
|
|
216
|
+
includeAlternatives: boolean;
|
|
217
|
+
reportFormat: "table" | "json";
|
|
218
|
+
jsonFile?: string;
|
|
219
|
+
concurrency: number;
|
|
220
|
+
registryTimeoutMs: number;
|
|
221
|
+
}
|
|
222
|
+
export interface PackageHealthMetric {
|
|
223
|
+
name: string;
|
|
224
|
+
currentVersion: string;
|
|
225
|
+
lastPublished: string | null;
|
|
226
|
+
isDeprecated: boolean;
|
|
227
|
+
deprecatedMessage?: string;
|
|
228
|
+
isArchived: boolean;
|
|
229
|
+
daysSinceLastRelease: number | null;
|
|
230
|
+
flags: HealthFlag[];
|
|
231
|
+
}
|
|
232
|
+
export interface HealthResult {
|
|
233
|
+
metrics: PackageHealthMetric[];
|
|
234
|
+
totalFlagged: number;
|
|
235
|
+
errors: string[];
|
|
236
|
+
warnings: string[];
|
|
237
|
+
}
|
|
238
|
+
export interface PeerNode {
|
|
239
|
+
name: string;
|
|
240
|
+
resolvedVersion: string;
|
|
241
|
+
peerRequirements: Map<string, string>;
|
|
242
|
+
}
|
|
243
|
+
export interface PeerGraph {
|
|
244
|
+
nodes: Map<string, PeerNode>;
|
|
245
|
+
roots: string[];
|
|
246
|
+
}
|
|
247
|
+
export type PeerConflictSeverity = "error" | "warning";
|
|
248
|
+
export interface PeerConflict {
|
|
249
|
+
requester: string;
|
|
250
|
+
peer: string;
|
|
251
|
+
requiredRange: string;
|
|
252
|
+
resolvedVersion: string;
|
|
253
|
+
severity: PeerConflictSeverity;
|
|
254
|
+
suggestion: string;
|
|
255
|
+
}
|
|
256
|
+
export interface ResolveOptions {
|
|
257
|
+
cwd: string;
|
|
258
|
+
workspace: boolean;
|
|
259
|
+
afterUpdate: boolean;
|
|
260
|
+
safe: boolean;
|
|
261
|
+
jsonFile?: string;
|
|
262
|
+
concurrency: number;
|
|
263
|
+
registryTimeoutMs: number;
|
|
264
|
+
cacheTtlSeconds: number;
|
|
265
|
+
}
|
|
266
|
+
export interface ResolveResult {
|
|
267
|
+
conflicts: PeerConflict[];
|
|
268
|
+
errorConflicts: number;
|
|
269
|
+
warningConflicts: number;
|
|
270
|
+
errors: string[];
|
|
271
|
+
warnings: string[];
|
|
272
|
+
}
|
|
273
|
+
export type UnusedKind = "declared-not-imported" | "imported-not-declared";
|
|
274
|
+
export interface UnusedDependency {
|
|
275
|
+
name: string;
|
|
276
|
+
kind: UnusedKind;
|
|
277
|
+
declaredIn?: string;
|
|
278
|
+
importedFrom?: string;
|
|
279
|
+
}
|
|
280
|
+
export interface UnusedOptions {
|
|
281
|
+
cwd: string;
|
|
282
|
+
workspace: boolean;
|
|
283
|
+
srcDirs: string[];
|
|
284
|
+
includeDevDependencies: boolean;
|
|
285
|
+
fix: boolean;
|
|
286
|
+
dryRun: boolean;
|
|
287
|
+
jsonFile?: string;
|
|
288
|
+
concurrency: number;
|
|
289
|
+
}
|
|
290
|
+
export interface UnusedResult {
|
|
291
|
+
unused: UnusedDependency[];
|
|
292
|
+
missing: UnusedDependency[];
|
|
293
|
+
totalUnused: number;
|
|
294
|
+
totalMissing: number;
|
|
295
|
+
errors: string[];
|
|
296
|
+
warnings: string[];
|
|
297
|
+
}
|
|
298
|
+
export interface PackageLicense {
|
|
299
|
+
name: string;
|
|
300
|
+
version: string;
|
|
301
|
+
license: string;
|
|
302
|
+
spdxExpression: string | null;
|
|
303
|
+
homepage?: string;
|
|
304
|
+
repository?: string;
|
|
305
|
+
}
|
|
306
|
+
export interface SbomDocument {
|
|
307
|
+
spdxVersion: "SPDX-2.3";
|
|
308
|
+
dataLicense: "CC0-1.0";
|
|
309
|
+
name: string;
|
|
310
|
+
documentNamespace: string;
|
|
311
|
+
packages: SbomPackage[];
|
|
312
|
+
relationships: SbomRelationship[];
|
|
313
|
+
}
|
|
314
|
+
export interface SbomPackage {
|
|
315
|
+
SPDXID: string;
|
|
316
|
+
name: string;
|
|
317
|
+
versionInfo: string;
|
|
318
|
+
downloadLocation: string;
|
|
319
|
+
licenseConcluded: string;
|
|
320
|
+
licenseDeclared: string;
|
|
321
|
+
copyrightText: string;
|
|
322
|
+
}
|
|
323
|
+
export interface SbomRelationship {
|
|
324
|
+
spdxElementId: string;
|
|
325
|
+
relationshipType: "DESCRIBES" | "DEPENDS_ON";
|
|
326
|
+
relatedSpdxElement: string;
|
|
327
|
+
}
|
|
328
|
+
export interface LicenseOptions {
|
|
329
|
+
cwd: string;
|
|
330
|
+
workspace: boolean;
|
|
331
|
+
allow?: string[];
|
|
332
|
+
deny?: string[];
|
|
333
|
+
sbomFile?: string;
|
|
334
|
+
jsonFile?: string;
|
|
335
|
+
diffMode: boolean;
|
|
336
|
+
concurrency: number;
|
|
337
|
+
registryTimeoutMs: number;
|
|
338
|
+
cacheTtlSeconds: number;
|
|
339
|
+
}
|
|
340
|
+
export interface LicenseResult {
|
|
341
|
+
packages: PackageLicense[];
|
|
342
|
+
violations: PackageLicense[];
|
|
343
|
+
totalViolations: number;
|
|
344
|
+
errors: string[];
|
|
345
|
+
warnings: string[];
|
|
346
|
+
}
|
|
347
|
+
export interface SnapshotEntry {
|
|
348
|
+
id: string;
|
|
349
|
+
label: string;
|
|
350
|
+
createdAt: number;
|
|
351
|
+
manifests: Record<string, string>;
|
|
352
|
+
lockfileHashes: Record<string, string>;
|
|
353
|
+
}
|
|
354
|
+
export type SnapshotAction = "save" | "list" | "restore" | "diff";
|
|
355
|
+
export interface SnapshotOptions {
|
|
356
|
+
cwd: string;
|
|
357
|
+
workspace: boolean;
|
|
358
|
+
action: SnapshotAction;
|
|
359
|
+
label?: string;
|
|
360
|
+
snapshotId?: string;
|
|
361
|
+
storeFile?: string;
|
|
362
|
+
}
|
|
363
|
+
export interface SnapshotResult {
|
|
364
|
+
action: SnapshotAction;
|
|
365
|
+
snapshotId?: string;
|
|
366
|
+
label?: string;
|
|
367
|
+
entries?: Array<{
|
|
368
|
+
id: string;
|
|
369
|
+
label: string;
|
|
370
|
+
createdAt: string;
|
|
371
|
+
}>;
|
|
372
|
+
diff?: Array<{
|
|
373
|
+
name: string;
|
|
374
|
+
from: string;
|
|
375
|
+
to: string;
|
|
376
|
+
}>;
|
|
377
|
+
errors: string[];
|
|
378
|
+
warnings: string[];
|
|
379
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { LockfileMode } from "../types/index.js";
|
|
2
|
+
export type LockfileSnapshot = Map<string, string | null>;
|
|
3
|
+
export declare function captureLockfileSnapshot(cwd: string): Promise<LockfileSnapshot>;
|
|
4
|
+
export declare function changedLockfiles(cwd: string, before: LockfileSnapshot): Promise<string[]>;
|
|
5
|
+
export declare function validateLockfileMode(mode: LockfileMode, install: boolean): void;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
const LOCKFILE_NAMES = ["package-lock.json", "npm-shrinkwrap.json", "pnpm-lock.yaml", "yarn.lock", "bun.lock"];
|
|
5
|
+
export async function captureLockfileSnapshot(cwd) {
|
|
6
|
+
const snapshot = new Map();
|
|
7
|
+
for (const name of LOCKFILE_NAMES) {
|
|
8
|
+
const filePath = path.join(cwd, name);
|
|
9
|
+
try {
|
|
10
|
+
const content = await fs.readFile(filePath);
|
|
11
|
+
snapshot.set(filePath, hashBuffer(content));
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
snapshot.set(filePath, null);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return snapshot;
|
|
18
|
+
}
|
|
19
|
+
export async function changedLockfiles(cwd, before) {
|
|
20
|
+
const changed = [];
|
|
21
|
+
for (const name of LOCKFILE_NAMES) {
|
|
22
|
+
const filePath = path.join(cwd, name);
|
|
23
|
+
let current = null;
|
|
24
|
+
try {
|
|
25
|
+
const content = await fs.readFile(filePath);
|
|
26
|
+
current = hashBuffer(content);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
current = null;
|
|
30
|
+
}
|
|
31
|
+
if ((before.get(filePath) ?? null) !== current) {
|
|
32
|
+
changed.push(filePath);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return changed.sort((a, b) => a.localeCompare(b));
|
|
36
|
+
}
|
|
37
|
+
export function validateLockfileMode(mode, install) {
|
|
38
|
+
if (mode === "update" && !install) {
|
|
39
|
+
throw new Error("--lockfile-mode update requires --install to update lockfiles deterministically.");
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function hashBuffer(value) {
|
|
43
|
+
return createHash("sha256").update(value).digest("hex");
|
|
44
|
+
}
|
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,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rainy-updates/cli",
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.5.2-rc.1",
|
|
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,
|
|
7
7
|
"license": "MIT",
|
|
@@ -18,12 +18,21 @@
|
|
|
18
18
|
"updates",
|
|
19
19
|
"cli",
|
|
20
20
|
"ci",
|
|
21
|
+
"devops",
|
|
21
22
|
"npm",
|
|
22
23
|
"pnpm",
|
|
23
|
-
"monorepo"
|
|
24
|
+
"monorepo",
|
|
25
|
+
"audit",
|
|
26
|
+
"security",
|
|
27
|
+
"bisect",
|
|
28
|
+
"ncu",
|
|
29
|
+
"taze",
|
|
30
|
+
"renovate"
|
|
24
31
|
],
|
|
25
32
|
"bin": {
|
|
26
|
-
"rainy-updates": "./dist/bin/cli.js"
|
|
33
|
+
"rainy-updates": "./dist/bin/cli.js",
|
|
34
|
+
"rainy-up": "./dist/bin/cli.js",
|
|
35
|
+
"rup": "./dist/bin/cli.js"
|
|
27
36
|
},
|
|
28
37
|
"types": "./dist/index.d.ts",
|
|
29
38
|
"exports": {
|