pruneguard 0.2.1 → 0.3.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 +247 -115
- package/bin/pruneguard +0 -0
- package/dist/bin.mjs +5 -1
- package/dist/index.d.mts +177 -4
- package/dist/index.mjs +64 -5
- package/dist/{runtime-BxlGT_W-.mjs → runtime-BSHQsTbN.mjs} +71 -9
- package/package.json +11 -9
- package/report_schema.json +96 -0
- package/review_report_schema.json +278 -0
- package/safe_delete_report_schema.json +131 -0
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
//#region src-js/runtime.d.ts
|
|
2
|
+
type ResolutionSource = "env" | "platform-package" | "dev" | "path";
|
|
3
|
+
type ResolutionInfo = {
|
|
4
|
+
binaryPath: string;
|
|
5
|
+
source: ResolutionSource;
|
|
6
|
+
platformPackage?: string;
|
|
7
|
+
schemaPath?: string;
|
|
8
|
+
version?: string;
|
|
9
|
+
platform?: string;
|
|
10
|
+
};
|
|
2
11
|
type CommandResult = {
|
|
3
12
|
args: string[];
|
|
4
13
|
cwd?: string;
|
|
@@ -14,12 +23,14 @@ declare class PruneguardExecutionError extends Error {
|
|
|
14
23
|
stderr?: string;
|
|
15
24
|
binaryPath?: string;
|
|
16
25
|
args?: string[];
|
|
26
|
+
resolutionSource?: ResolutionSource;
|
|
17
27
|
constructor(code: PruneguardExecutionError["code"], message: string, details?: {
|
|
18
28
|
exitCode?: number;
|
|
19
29
|
stdout?: string;
|
|
20
30
|
stderr?: string;
|
|
21
31
|
binaryPath?: string;
|
|
22
32
|
args?: string[];
|
|
33
|
+
resolutionSource?: ResolutionSource;
|
|
23
34
|
});
|
|
24
35
|
}
|
|
25
36
|
//#endregion
|
|
@@ -113,6 +124,8 @@ type AnalysisReport = {
|
|
|
113
124
|
}>;
|
|
114
125
|
suggestion?: string;
|
|
115
126
|
ruleName?: string;
|
|
127
|
+
primaryActionKind?: string;
|
|
128
|
+
actionKinds?: string[];
|
|
116
129
|
}>;
|
|
117
130
|
entrypoints: Array<{
|
|
118
131
|
path: string;
|
|
@@ -164,6 +177,12 @@ type AnalysisReport = {
|
|
|
164
177
|
cacheEntriesRead: number;
|
|
165
178
|
cacheEntriesWritten: number;
|
|
166
179
|
affectedScopeIncomplete: boolean;
|
|
180
|
+
executionMode?: "oneshot" | "daemon";
|
|
181
|
+
indexWarm?: boolean;
|
|
182
|
+
indexAgeMs?: number;
|
|
183
|
+
reusedGraphNodes?: number;
|
|
184
|
+
reusedGraphEdges?: number;
|
|
185
|
+
watcherLagMs?: number;
|
|
167
186
|
};
|
|
168
187
|
};
|
|
169
188
|
type MigrationOutput = {
|
|
@@ -197,8 +216,156 @@ type ExplainReport = {
|
|
|
197
216
|
focusFiltered: boolean;
|
|
198
217
|
};
|
|
199
218
|
type PruneguardConfig = Record<string, unknown>;
|
|
219
|
+
type ReviewOptions = {
|
|
220
|
+
cwd?: string;
|
|
221
|
+
config?: string;
|
|
222
|
+
profile?: Profile;
|
|
223
|
+
baseRef?: string;
|
|
224
|
+
noCache?: boolean;
|
|
225
|
+
noBaseline?: boolean;
|
|
226
|
+
};
|
|
227
|
+
type ReviewReport = {
|
|
228
|
+
baseRef?: string;
|
|
229
|
+
changedFiles: string[];
|
|
230
|
+
newFindings: AnalysisReport["findings"];
|
|
231
|
+
blockingFindings: AnalysisReport["findings"];
|
|
232
|
+
advisoryFindings: AnalysisReport["findings"];
|
|
233
|
+
trust: {
|
|
234
|
+
fullScope: boolean;
|
|
235
|
+
baselineApplied: boolean;
|
|
236
|
+
unresolvedPressure: number;
|
|
237
|
+
confidenceCounts: {
|
|
238
|
+
high: number;
|
|
239
|
+
medium: number;
|
|
240
|
+
low: number;
|
|
241
|
+
};
|
|
242
|
+
};
|
|
243
|
+
recommendations: string[];
|
|
244
|
+
proposedActions?: Array<{
|
|
245
|
+
id: string;
|
|
246
|
+
kind: string;
|
|
247
|
+
targets: string[];
|
|
248
|
+
why: string;
|
|
249
|
+
preconditions: string[];
|
|
250
|
+
steps: Array<{
|
|
251
|
+
description: string;
|
|
252
|
+
file?: string;
|
|
253
|
+
action?: string;
|
|
254
|
+
}>;
|
|
255
|
+
verification: string[];
|
|
256
|
+
risk: "low" | "medium" | "high";
|
|
257
|
+
confidence: "high" | "medium" | "low";
|
|
258
|
+
}>;
|
|
259
|
+
executionMode?: "oneshot" | "daemon";
|
|
260
|
+
latencyMs?: number;
|
|
261
|
+
};
|
|
262
|
+
type SafeDeleteOptions = {
|
|
263
|
+
cwd?: string;
|
|
264
|
+
config?: string;
|
|
265
|
+
profile?: Profile;
|
|
266
|
+
targets: string[];
|
|
267
|
+
noCache?: boolean;
|
|
268
|
+
};
|
|
269
|
+
type SafeDeleteReport = {
|
|
270
|
+
targets: string[];
|
|
271
|
+
safe: Array<{
|
|
272
|
+
target: string;
|
|
273
|
+
confidence?: "high" | "medium" | "low";
|
|
274
|
+
reasons: string[];
|
|
275
|
+
}>;
|
|
276
|
+
needsReview: Array<{
|
|
277
|
+
target: string;
|
|
278
|
+
confidence?: "high" | "medium" | "low";
|
|
279
|
+
reasons: string[];
|
|
280
|
+
}>;
|
|
281
|
+
blocked: Array<{
|
|
282
|
+
target: string;
|
|
283
|
+
confidence?: "high" | "medium" | "low";
|
|
284
|
+
reasons: string[];
|
|
285
|
+
}>;
|
|
286
|
+
deletionOrder: string[];
|
|
287
|
+
evidence: Array<{
|
|
288
|
+
kind: string;
|
|
289
|
+
file?: string;
|
|
290
|
+
line?: number;
|
|
291
|
+
description: string;
|
|
292
|
+
}>;
|
|
293
|
+
};
|
|
294
|
+
type FixPlanOptions = {
|
|
295
|
+
cwd?: string;
|
|
296
|
+
config?: string;
|
|
297
|
+
profile?: Profile;
|
|
298
|
+
targets: string[];
|
|
299
|
+
noCache?: boolean;
|
|
300
|
+
};
|
|
301
|
+
type SuggestRulesOptions = {
|
|
302
|
+
cwd?: string;
|
|
303
|
+
config?: string;
|
|
304
|
+
profile?: Profile;
|
|
305
|
+
noCache?: boolean;
|
|
306
|
+
};
|
|
307
|
+
type SuggestRulesReport = {
|
|
308
|
+
suggestedRules: Array<{
|
|
309
|
+
kind: string;
|
|
310
|
+
name: string;
|
|
311
|
+
description: string;
|
|
312
|
+
configFragment: Record<string, unknown>;
|
|
313
|
+
confidence: "high" | "medium" | "low";
|
|
314
|
+
evidence?: string[];
|
|
315
|
+
}>;
|
|
316
|
+
tags?: Array<{
|
|
317
|
+
name: string;
|
|
318
|
+
glob: string;
|
|
319
|
+
rationale: string;
|
|
320
|
+
}>;
|
|
321
|
+
ownershipHints?: Array<{
|
|
322
|
+
pathGlob: string;
|
|
323
|
+
suggestedOwner: string;
|
|
324
|
+
crossTeamEdges: number;
|
|
325
|
+
rationale: string;
|
|
326
|
+
}>;
|
|
327
|
+
hotspots?: Array<{
|
|
328
|
+
file: string;
|
|
329
|
+
crossPackageImports: number;
|
|
330
|
+
crossOwnerImports: number;
|
|
331
|
+
incomingEdges: number;
|
|
332
|
+
outgoingEdges: number;
|
|
333
|
+
suggestion: string;
|
|
334
|
+
}>;
|
|
335
|
+
rationale?: string[];
|
|
336
|
+
};
|
|
337
|
+
type FixPlanReport = {
|
|
338
|
+
query: string[];
|
|
339
|
+
matchedFindings: AnalysisReport["findings"];
|
|
340
|
+
actions: Array<{
|
|
341
|
+
id: string;
|
|
342
|
+
kind: string;
|
|
343
|
+
targets: string[];
|
|
344
|
+
why: string;
|
|
345
|
+
preconditions: string[];
|
|
346
|
+
steps: Array<{
|
|
347
|
+
description: string;
|
|
348
|
+
file?: string;
|
|
349
|
+
action?: string;
|
|
350
|
+
}>;
|
|
351
|
+
verification: string[];
|
|
352
|
+
risk: "low" | "medium" | "high";
|
|
353
|
+
confidence: "high" | "medium" | "low";
|
|
354
|
+
}>;
|
|
355
|
+
blockedBy: string[];
|
|
356
|
+
verificationSteps: string[];
|
|
357
|
+
riskLevel: "low" | "medium" | "high";
|
|
358
|
+
confidence: "high" | "medium" | "low";
|
|
359
|
+
};
|
|
360
|
+
type DaemonStatusReport = {
|
|
361
|
+
running: boolean;
|
|
362
|
+
pid?: number;
|
|
363
|
+
port?: number;
|
|
364
|
+
version?: string;
|
|
365
|
+
startedAt?: string;
|
|
366
|
+
projectRoot?: string;
|
|
367
|
+
};
|
|
200
368
|
declare function scan(options?: ScanOptions): Promise<AnalysisReport>;
|
|
201
|
-
/** @experimental */
|
|
202
369
|
declare function scanDot(options?: ScanOptions): Promise<string>;
|
|
203
370
|
declare function impact(options: ImpactOptions): Promise<ImpactReport>;
|
|
204
371
|
declare function explain(options: ExplainOptions): Promise<ExplainReport>;
|
|
@@ -208,21 +375,27 @@ declare function loadConfig(options?: {
|
|
|
208
375
|
}): Promise<PruneguardConfig>;
|
|
209
376
|
declare function schemaPath(): string;
|
|
210
377
|
declare function binaryPath(): string;
|
|
378
|
+
declare function resolutionInfo(): ResolutionInfo;
|
|
211
379
|
declare function run(args: string[], options?: {
|
|
212
380
|
cwd?: string;
|
|
213
381
|
}): Promise<CommandResult>;
|
|
214
382
|
declare function debugResolve(options: DebugResolveOptions): Promise<string>;
|
|
215
383
|
declare function debugEntrypoints(options?: DebugEntrypointsOptions): Promise<string[]>;
|
|
216
|
-
|
|
384
|
+
declare function review(options?: ReviewOptions): Promise<ReviewReport>;
|
|
385
|
+
declare function safeDelete(options: SafeDeleteOptions): Promise<SafeDeleteReport>;
|
|
386
|
+
declare function fixPlan(options: FixPlanOptions): Promise<FixPlanReport>;
|
|
387
|
+
declare function suggestRules(options?: SuggestRulesOptions): Promise<SuggestRulesReport>;
|
|
217
388
|
declare function migrateKnip(options?: {
|
|
218
389
|
cwd?: string;
|
|
219
390
|
file?: string;
|
|
220
391
|
}): Promise<MigrationOutput>;
|
|
221
|
-
/** @experimental */
|
|
222
392
|
declare function migrateDepcruise(options?: {
|
|
223
393
|
cwd?: string;
|
|
224
394
|
file?: string;
|
|
225
395
|
node?: boolean;
|
|
226
396
|
}): Promise<MigrationOutput>;
|
|
397
|
+
declare function daemonStatus(options?: {
|
|
398
|
+
cwd?: string;
|
|
399
|
+
}): Promise<DaemonStatusReport>;
|
|
227
400
|
//#endregion
|
|
228
|
-
export { AnalysisReport, type CommandResult, DebugEntrypointsOptions, DebugResolveOptions, ExplainOptions, ExplainReport, ImpactOptions, ImpactReport, MigrationOutput, Profile, PruneguardConfig, PruneguardExecutionError, ScanOptions, binaryPath, debugEntrypoints, debugResolve, explain, impact, loadConfig, migrateDepcruise, migrateKnip, run, scan, scanDot, schemaPath };
|
|
401
|
+
export { AnalysisReport, type CommandResult, DaemonStatusReport, DebugEntrypointsOptions, DebugResolveOptions, ExplainOptions, ExplainReport, FixPlanOptions, FixPlanReport, ImpactOptions, ImpactReport, MigrationOutput, Profile, PruneguardConfig, PruneguardExecutionError, type ResolutionInfo, type ResolutionSource, ReviewOptions, ReviewReport, SafeDeleteOptions, SafeDeleteReport, ScanOptions, SuggestRulesOptions, SuggestRulesReport, binaryPath, daemonStatus, debugEntrypoints, debugResolve, explain, fixPlan, impact, loadConfig, migrateDepcruise, migrateKnip, resolutionInfo, review, run, safeDelete, scan, scanDot, schemaPath, suggestRules };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as binaryPath$1, r as
|
|
1
|
+
import { i as run$1, n as binaryPath$1, r as resolutionInfo$1, t as PruneguardExecutionError } from "./runtime-BSHQsTbN.mjs";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
//#region src-js/index.ts
|
|
4
4
|
function parseJson(result) {
|
|
@@ -44,7 +44,6 @@ async function scan(options = {}) {
|
|
|
44
44
|
if (result.exitCode !== 0 && result.exitCode !== 1) requireSuccess(result);
|
|
45
45
|
return parseJson(result);
|
|
46
46
|
}
|
|
47
|
-
/** @experimental */
|
|
48
47
|
async function scanDot(options = {}) {
|
|
49
48
|
const args = [
|
|
50
49
|
"--format",
|
|
@@ -93,6 +92,9 @@ function schemaPath() {
|
|
|
93
92
|
function binaryPath() {
|
|
94
93
|
return binaryPath$1();
|
|
95
94
|
}
|
|
95
|
+
function resolutionInfo() {
|
|
96
|
+
return resolutionInfo$1();
|
|
97
|
+
}
|
|
96
98
|
function run(args, options) {
|
|
97
99
|
return run$1(args, options);
|
|
98
100
|
}
|
|
@@ -113,7 +115,49 @@ async function debugEntrypoints(options = {}) {
|
|
|
113
115
|
requireSuccess(result);
|
|
114
116
|
return result.stdout.trimEnd().split("\n").filter(Boolean);
|
|
115
117
|
}
|
|
116
|
-
|
|
118
|
+
async function review(options = {}) {
|
|
119
|
+
const args = [
|
|
120
|
+
"--format",
|
|
121
|
+
"json",
|
|
122
|
+
"--severity",
|
|
123
|
+
"info"
|
|
124
|
+
];
|
|
125
|
+
pushGlobalFlags(args, options);
|
|
126
|
+
if (options.baseRef) args.push("--changed-since", options.baseRef);
|
|
127
|
+
if (options.noCache) args.push("--no-cache");
|
|
128
|
+
if (options.noBaseline) args.push("--no-baseline");
|
|
129
|
+
args.push("review");
|
|
130
|
+
const result = await run$1(args, { cwd: options.cwd });
|
|
131
|
+
if (result.exitCode !== 0 && result.exitCode !== 1) requireSuccess(result);
|
|
132
|
+
return parseJson(result);
|
|
133
|
+
}
|
|
134
|
+
async function safeDelete(options) {
|
|
135
|
+
const args = ["--format", "json"];
|
|
136
|
+
pushGlobalFlags(args, options);
|
|
137
|
+
if (options.noCache) args.push("--no-cache");
|
|
138
|
+
args.push("safe-delete", ...options.targets);
|
|
139
|
+
const result = await run$1(args, { cwd: options.cwd });
|
|
140
|
+
if (result.exitCode !== 0 && result.exitCode !== 1) requireSuccess(result);
|
|
141
|
+
return parseJson(result);
|
|
142
|
+
}
|
|
143
|
+
async function fixPlan(options) {
|
|
144
|
+
const args = ["--format", "json"];
|
|
145
|
+
pushGlobalFlags(args, options);
|
|
146
|
+
if (options.noCache) args.push("--no-cache");
|
|
147
|
+
args.push("fix-plan", ...options.targets);
|
|
148
|
+
const result = await run$1(args, { cwd: options.cwd });
|
|
149
|
+
requireSuccess(result);
|
|
150
|
+
return parseJson(result);
|
|
151
|
+
}
|
|
152
|
+
async function suggestRules(options = {}) {
|
|
153
|
+
const args = ["--format", "json"];
|
|
154
|
+
pushGlobalFlags(args, options);
|
|
155
|
+
if (options.noCache) args.push("--no-cache");
|
|
156
|
+
args.push("suggest-rules");
|
|
157
|
+
const result = await run$1(args, { cwd: options.cwd });
|
|
158
|
+
requireSuccess(result);
|
|
159
|
+
return parseJson(result);
|
|
160
|
+
}
|
|
117
161
|
async function migrateKnip(options = {}) {
|
|
118
162
|
const args = [
|
|
119
163
|
"--format",
|
|
@@ -126,7 +170,6 @@ async function migrateKnip(options = {}) {
|
|
|
126
170
|
requireSuccess(result);
|
|
127
171
|
return parseJson(result);
|
|
128
172
|
}
|
|
129
|
-
/** @experimental */
|
|
130
173
|
async function migrateDepcruise(options = {}) {
|
|
131
174
|
const args = [
|
|
132
175
|
"--format",
|
|
@@ -140,5 +183,21 @@ async function migrateDepcruise(options = {}) {
|
|
|
140
183
|
requireSuccess(result);
|
|
141
184
|
return parseJson(result);
|
|
142
185
|
}
|
|
186
|
+
async function daemonStatus(options) {
|
|
187
|
+
const result = await run$1(["daemon", "status"], { cwd: options?.cwd });
|
|
188
|
+
if (result.exitCode === 1 && result.stdout.includes("no running daemon")) return { running: false };
|
|
189
|
+
if (result.exitCode !== 0) return { running: false };
|
|
190
|
+
const lines = result.stdout.trim().split("\n");
|
|
191
|
+
const report = { running: true };
|
|
192
|
+
for (const line of lines) {
|
|
193
|
+
const [key, ...rest] = line.split(": ");
|
|
194
|
+
const value = rest.join(": ").trim();
|
|
195
|
+
if (key === "pid") report.pid = parseInt(value, 10);
|
|
196
|
+
else if (key === "port") report.port = parseInt(value, 10);
|
|
197
|
+
else if (key === "version") report.version = value;
|
|
198
|
+
else if (key === "started_at") report.startedAt = value;
|
|
199
|
+
}
|
|
200
|
+
return report;
|
|
201
|
+
}
|
|
143
202
|
//#endregion
|
|
144
|
-
export { PruneguardExecutionError, binaryPath, debugEntrypoints, debugResolve, explain, impact, loadConfig, migrateDepcruise, migrateKnip, run, scan, scanDot, schemaPath };
|
|
203
|
+
export { PruneguardExecutionError, binaryPath, daemonStatus, debugEntrypoints, debugResolve, explain, fixPlan, impact, loadConfig, migrateDepcruise, migrateKnip, resolutionInfo, review, run, safeDelete, scan, scanDot, schemaPath, suggestRules };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
import { execFileSync, spawn } from "node:child_process";
|
|
4
|
-
import { existsSync } from "node:fs";
|
|
4
|
+
import { accessSync, constants, existsSync } from "node:fs";
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
6
|
//#region src-js/runtime.ts
|
|
7
7
|
const require = createRequire(import.meta.url);
|
|
@@ -13,6 +13,7 @@ var PruneguardExecutionError = class extends Error {
|
|
|
13
13
|
stderr;
|
|
14
14
|
binaryPath;
|
|
15
15
|
args;
|
|
16
|
+
resolutionSource;
|
|
16
17
|
constructor(code, message, details) {
|
|
17
18
|
super(message);
|
|
18
19
|
this.name = "PruneguardExecutionError";
|
|
@@ -22,6 +23,7 @@ var PruneguardExecutionError = class extends Error {
|
|
|
22
23
|
this.stderr = details?.stderr;
|
|
23
24
|
this.binaryPath = details?.binaryPath;
|
|
24
25
|
this.args = details?.args;
|
|
26
|
+
this.resolutionSource = details?.resolutionSource;
|
|
25
27
|
}
|
|
26
28
|
};
|
|
27
29
|
const PLATFORM_PACKAGES = {
|
|
@@ -40,7 +42,10 @@ function findPlatformBinary() {
|
|
|
40
42
|
if (!candidates) return void 0;
|
|
41
43
|
for (const pkg of candidates) try {
|
|
42
44
|
const binPath = join(dirname(require.resolve(`${pkg}/package.json`)), "bin", exeName());
|
|
43
|
-
if (existsSync(binPath)) return
|
|
45
|
+
if (existsSync(binPath)) return {
|
|
46
|
+
path: binPath,
|
|
47
|
+
packageName: pkg
|
|
48
|
+
};
|
|
44
49
|
} catch {
|
|
45
50
|
continue;
|
|
46
51
|
}
|
|
@@ -63,38 +68,94 @@ function findPathBinary() {
|
|
|
63
68
|
} catch {}
|
|
64
69
|
}
|
|
65
70
|
let cachedBinaryPath;
|
|
71
|
+
let cachedResolutionSource;
|
|
72
|
+
let cachedPlatformPackage;
|
|
73
|
+
function validateExecutable(binPath, source) {
|
|
74
|
+
if (!existsSync(binPath)) throw new PruneguardExecutionError("PRUNEGUARD_BINARY_NOT_FOUND", `[${source}] Binary does not exist: ${binPath}. Run "pruneguard debug runtime" for diagnostics.`, {
|
|
75
|
+
binaryPath: binPath,
|
|
76
|
+
resolutionSource: source
|
|
77
|
+
});
|
|
78
|
+
if (process.platform !== "win32") try {
|
|
79
|
+
accessSync(binPath, constants.X_OK);
|
|
80
|
+
} catch {
|
|
81
|
+
throw new PruneguardExecutionError("PRUNEGUARD_BINARY_NOT_FOUND", `[${source}] Binary exists but is not executable: ${binPath}. Try: chmod +x "${binPath}"`, {
|
|
82
|
+
binaryPath: binPath,
|
|
83
|
+
resolutionSource: source
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
66
87
|
function binaryPath(options) {
|
|
67
88
|
if (cachedBinaryPath) return cachedBinaryPath;
|
|
68
89
|
const envPath = process.env.PRUNEGUARD_BINARY;
|
|
69
90
|
if (envPath) {
|
|
70
|
-
|
|
91
|
+
validateExecutable(envPath, "env");
|
|
71
92
|
cachedBinaryPath = envPath;
|
|
93
|
+
cachedResolutionSource = "env";
|
|
72
94
|
return envPath;
|
|
73
95
|
}
|
|
74
96
|
const platformBin = findPlatformBinary();
|
|
75
97
|
if (platformBin) {
|
|
76
|
-
|
|
77
|
-
|
|
98
|
+
validateExecutable(platformBin.path, "platform-package");
|
|
99
|
+
cachedBinaryPath = platformBin.path;
|
|
100
|
+
cachedResolutionSource = "platform-package";
|
|
101
|
+
cachedPlatformPackage = platformBin.packageName;
|
|
102
|
+
return platformBin.path;
|
|
78
103
|
}
|
|
79
104
|
const devBin = findDevBinary();
|
|
80
105
|
if (devBin) {
|
|
106
|
+
validateExecutable(devBin, "dev");
|
|
81
107
|
cachedBinaryPath = devBin;
|
|
108
|
+
cachedResolutionSource = "dev";
|
|
82
109
|
return devBin;
|
|
83
110
|
}
|
|
84
111
|
if (options?.allowPathFallback) {
|
|
85
112
|
const pathBin = findPathBinary();
|
|
86
113
|
if (pathBin) {
|
|
114
|
+
validateExecutable(pathBin, "path");
|
|
87
115
|
cachedBinaryPath = pathBin;
|
|
116
|
+
cachedResolutionSource = "path";
|
|
88
117
|
return pathBin;
|
|
89
118
|
}
|
|
90
119
|
}
|
|
91
|
-
|
|
120
|
+
const expectedPkgs = PLATFORM_PACKAGES[`${process.platform}-${process.arch === "arm64" ? "arm64" : "x64"}`];
|
|
121
|
+
const tried = [
|
|
122
|
+
"env(PRUNEGUARD_BINARY)",
|
|
123
|
+
"platform-package",
|
|
124
|
+
"dev(cargo build)"
|
|
125
|
+
];
|
|
126
|
+
if (options?.allowPathFallback) tried.push("PATH");
|
|
127
|
+
const pkgHint = expectedPkgs?.length ? `\n Expected platform package: ${expectedPkgs.join(" or ")}` : "";
|
|
128
|
+
throw new PruneguardExecutionError("PRUNEGUARD_BINARY_NOT_FOUND", `Could not find the pruneguard binary for ${`${process.platform}-${process.arch}`}.\n Tried: ${tried.join(", ")}${pkgHint}\n Fix: npm install pruneguard (or set PRUNEGUARD_BINARY)\n Debug: npx pruneguard debug runtime`);
|
|
129
|
+
}
|
|
130
|
+
function resolutionInfo() {
|
|
131
|
+
return {
|
|
132
|
+
binaryPath: binaryPath(),
|
|
133
|
+
source: cachedResolutionSource,
|
|
134
|
+
...cachedPlatformPackage ? { platformPackage: cachedPlatformPackage } : {},
|
|
135
|
+
schemaPath: join(dirname(fileURLToPath(import.meta.url)), "..", "configuration_schema.json"),
|
|
136
|
+
platform: `${process.platform}-${process.arch}`
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Returns `true` if the current process is running in a CI environment.
|
|
141
|
+
*
|
|
142
|
+
* Checks the common `CI` env var used by GitHub Actions, GitLab CI,
|
|
143
|
+
* CircleCI, Travis CI, Jenkins, and most other CI providers.
|
|
144
|
+
*/
|
|
145
|
+
function isCI() {
|
|
146
|
+
const ci = process.env.CI;
|
|
147
|
+
return ci !== void 0 && ci !== "" && ci !== "0" && ci.toLowerCase() !== "false";
|
|
92
148
|
}
|
|
93
149
|
function run(args, options) {
|
|
94
150
|
const binary = binaryPath();
|
|
95
151
|
const start = performance.now();
|
|
152
|
+
const finalArgs = args.some((arg) => arg === "--daemon" || arg.startsWith("--daemon=")) ? args : [
|
|
153
|
+
"--daemon",
|
|
154
|
+
options?.daemon ?? (isCI() ? "off" : "auto"),
|
|
155
|
+
...args
|
|
156
|
+
];
|
|
96
157
|
return new Promise((resolve, reject) => {
|
|
97
|
-
const child = spawn(binary,
|
|
158
|
+
const child = spawn(binary, finalArgs, {
|
|
98
159
|
cwd: options?.cwd,
|
|
99
160
|
stdio: [
|
|
100
161
|
"ignore",
|
|
@@ -113,7 +174,8 @@ function run(args, options) {
|
|
|
113
174
|
child.on("error", (err) => {
|
|
114
175
|
reject(new PruneguardExecutionError("PRUNEGUARD_EXECUTION_FAILED", `Failed to spawn pruneguard: ${err.message}`, {
|
|
115
176
|
binaryPath: binary,
|
|
116
|
-
args
|
|
177
|
+
args,
|
|
178
|
+
resolutionSource: cachedResolutionSource
|
|
117
179
|
}));
|
|
118
180
|
});
|
|
119
181
|
child.on("close", (exitCode) => {
|
|
@@ -129,4 +191,4 @@ function run(args, options) {
|
|
|
129
191
|
});
|
|
130
192
|
}
|
|
131
193
|
//#endregion
|
|
132
|
-
export { binaryPath as n,
|
|
194
|
+
export { run as i, binaryPath as n, resolutionInfo as r, PruneguardExecutionError as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pruneguard",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Repo truth engine for JS/TS monorepos",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"javascript",
|
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
"files": [
|
|
28
28
|
"configuration_schema.json",
|
|
29
29
|
"report_schema.json",
|
|
30
|
+
"review_report_schema.json",
|
|
31
|
+
"safe_delete_report_schema.json",
|
|
30
32
|
"dist",
|
|
31
33
|
"README.md",
|
|
32
34
|
"bin/pruneguard"
|
|
@@ -41,14 +43,14 @@
|
|
|
41
43
|
}
|
|
42
44
|
},
|
|
43
45
|
"optionalDependencies": {
|
|
44
|
-
"@pruneguard/cli-darwin-arm64": "0.
|
|
45
|
-
"@pruneguard/cli-darwin-x64": "0.
|
|
46
|
-
"@pruneguard/cli-linux-arm64-gnu": "0.
|
|
47
|
-
"@pruneguard/cli-linux-arm64-musl": "0.
|
|
48
|
-
"@pruneguard/cli-linux-x64-gnu": "0.
|
|
49
|
-
"@pruneguard/cli-linux-x64-musl": "0.
|
|
50
|
-
"@pruneguard/cli-win32-arm64-msvc": "0.
|
|
51
|
-
"@pruneguard/cli-win32-x64-msvc": "0.
|
|
46
|
+
"@pruneguard/cli-darwin-arm64": "0.3.0",
|
|
47
|
+
"@pruneguard/cli-darwin-x64": "0.3.0",
|
|
48
|
+
"@pruneguard/cli-linux-arm64-gnu": "0.3.0",
|
|
49
|
+
"@pruneguard/cli-linux-arm64-musl": "0.3.0",
|
|
50
|
+
"@pruneguard/cli-linux-x64-gnu": "0.3.0",
|
|
51
|
+
"@pruneguard/cli-linux-x64-musl": "0.3.0",
|
|
52
|
+
"@pruneguard/cli-win32-arm64-msvc": "0.3.0",
|
|
53
|
+
"@pruneguard/cli-win32-x64-msvc": "0.3.0"
|
|
52
54
|
},
|
|
53
55
|
"engines": {
|
|
54
56
|
"node": ">=18.0.0"
|
package/report_schema.json
CHANGED
|
@@ -162,6 +162,14 @@
|
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
164
|
},
|
|
165
|
+
"ExecutionMode": {
|
|
166
|
+
"description": "Execution mode for daemon/oneshot distinction.",
|
|
167
|
+
"type": "string",
|
|
168
|
+
"enum": [
|
|
169
|
+
"oneshot",
|
|
170
|
+
"daemon"
|
|
171
|
+
]
|
|
172
|
+
},
|
|
165
173
|
"FileInfo": {
|
|
166
174
|
"description": "Info about a discovered file.",
|
|
167
175
|
"type": "object",
|
|
@@ -233,6 +241,13 @@
|
|
|
233
241
|
"subject"
|
|
234
242
|
],
|
|
235
243
|
"properties": {
|
|
244
|
+
"actionKinds": {
|
|
245
|
+
"description": "All applicable remediation action kinds.",
|
|
246
|
+
"type": "array",
|
|
247
|
+
"items": {
|
|
248
|
+
"$ref": "#/definitions/RemediationActionKind"
|
|
249
|
+
}
|
|
250
|
+
},
|
|
236
251
|
"category": {
|
|
237
252
|
"description": "Category of the finding.",
|
|
238
253
|
"allOf": [
|
|
@@ -275,6 +290,17 @@
|
|
|
275
290
|
"null"
|
|
276
291
|
]
|
|
277
292
|
},
|
|
293
|
+
"primaryActionKind": {
|
|
294
|
+
"description": "Primary remediation action kind for this finding.",
|
|
295
|
+
"anyOf": [
|
|
296
|
+
{
|
|
297
|
+
"$ref": "#/definitions/RemediationActionKind"
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
"type": "null"
|
|
301
|
+
}
|
|
302
|
+
]
|
|
303
|
+
},
|
|
278
304
|
"ruleName": {
|
|
279
305
|
"description": "Name of the rule that produced this finding, if any.",
|
|
280
306
|
"type": [
|
|
@@ -394,6 +420,22 @@
|
|
|
394
420
|
}
|
|
395
421
|
}
|
|
396
422
|
},
|
|
423
|
+
"RemediationActionKind": {
|
|
424
|
+
"description": "The kind of remediation action to take.",
|
|
425
|
+
"type": "string",
|
|
426
|
+
"enum": [
|
|
427
|
+
"delete-file",
|
|
428
|
+
"delete-export",
|
|
429
|
+
"remove-dependency",
|
|
430
|
+
"break-cycle",
|
|
431
|
+
"move-import",
|
|
432
|
+
"tighten-entrypoint",
|
|
433
|
+
"update-boundary-rule",
|
|
434
|
+
"assign-owner",
|
|
435
|
+
"split-package",
|
|
436
|
+
"acknowledge-baseline"
|
|
437
|
+
]
|
|
438
|
+
},
|
|
397
439
|
"Stats": {
|
|
398
440
|
"description": "Performance statistics.",
|
|
399
441
|
"type": "object",
|
|
@@ -492,6 +534,17 @@
|
|
|
492
534
|
"format": "uint",
|
|
493
535
|
"minimum": 0.0
|
|
494
536
|
},
|
|
537
|
+
"executionMode": {
|
|
538
|
+
"description": "Execution mode used for this analysis.",
|
|
539
|
+
"anyOf": [
|
|
540
|
+
{
|
|
541
|
+
"$ref": "#/definitions/ExecutionMode"
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
"type": "null"
|
|
545
|
+
}
|
|
546
|
+
]
|
|
547
|
+
},
|
|
495
548
|
"filesCached": {
|
|
496
549
|
"type": "integer",
|
|
497
550
|
"format": "uint",
|
|
@@ -538,6 +591,22 @@
|
|
|
538
591
|
"format": "uint",
|
|
539
592
|
"minimum": 0.0
|
|
540
593
|
},
|
|
594
|
+
"indexAgeMs": {
|
|
595
|
+
"description": "Age of the reused graph index in milliseconds.",
|
|
596
|
+
"type": [
|
|
597
|
+
"integer",
|
|
598
|
+
"null"
|
|
599
|
+
],
|
|
600
|
+
"format": "uint64",
|
|
601
|
+
"minimum": 0.0
|
|
602
|
+
},
|
|
603
|
+
"indexWarm": {
|
|
604
|
+
"description": "Whether the graph index was warm (reused from a previous run).",
|
|
605
|
+
"type": [
|
|
606
|
+
"boolean",
|
|
607
|
+
"null"
|
|
608
|
+
]
|
|
609
|
+
},
|
|
541
610
|
"newFindings": {
|
|
542
611
|
"type": "integer",
|
|
543
612
|
"format": "uint",
|
|
@@ -563,6 +632,24 @@
|
|
|
563
632
|
"format": "uint",
|
|
564
633
|
"minimum": 0.0
|
|
565
634
|
},
|
|
635
|
+
"reusedGraphEdges": {
|
|
636
|
+
"description": "Number of graph edges reused from a warm index.",
|
|
637
|
+
"type": [
|
|
638
|
+
"integer",
|
|
639
|
+
"null"
|
|
640
|
+
],
|
|
641
|
+
"format": "uint",
|
|
642
|
+
"minimum": 0.0
|
|
643
|
+
},
|
|
644
|
+
"reusedGraphNodes": {
|
|
645
|
+
"description": "Number of graph nodes reused from a warm index.",
|
|
646
|
+
"type": [
|
|
647
|
+
"integer",
|
|
648
|
+
"null"
|
|
649
|
+
],
|
|
650
|
+
"format": "uint",
|
|
651
|
+
"minimum": 0.0
|
|
652
|
+
},
|
|
566
653
|
"suppressedFindings": {
|
|
567
654
|
"type": "integer",
|
|
568
655
|
"format": "uint",
|
|
@@ -575,6 +662,15 @@
|
|
|
575
662
|
"type": "integer",
|
|
576
663
|
"format": "uint",
|
|
577
664
|
"minimum": 0.0
|
|
665
|
+
},
|
|
666
|
+
"watcherLagMs": {
|
|
667
|
+
"description": "Lag of the file-system watcher in milliseconds.",
|
|
668
|
+
"type": [
|
|
669
|
+
"integer",
|
|
670
|
+
"null"
|
|
671
|
+
],
|
|
672
|
+
"format": "uint64",
|
|
673
|
+
"minimum": 0.0
|
|
578
674
|
}
|
|
579
675
|
}
|
|
580
676
|
},
|