@ankurhalder/codebase-snapshot 4.0.0 → 4.0.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/dist/bin.js +463 -289
- package/dist/{chunk-FM4OOQPF.js → chunk-CY3XU5TS.js} +301 -127
- package/dist/cli.cjs +427 -253
- package/dist/cli.d.cts +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +9 -6
- package/dist/index.cjs +299 -125
- package/dist/index.d.cts +14 -9
- package/dist/index.d.ts +14 -9
- package/dist/index.js +1 -1
- package/dist/{types-BdpT_4b8.d.cts → types-LSfwsKE-.d.cts} +1 -0
- package/dist/{types-BdpT_4b8.d.ts → types-LSfwsKE-.d.ts} +1 -0
- package/docs/cli.md +5 -3
- package/docs/troubleshooting.md +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2218,11 +2218,15 @@ function isSensitivePath(relativePath) {
|
|
|
2218
2218
|
}
|
|
2219
2219
|
return matchesSensitiveList(posixPath, base);
|
|
2220
2220
|
}
|
|
2221
|
-
function
|
|
2221
|
+
function pathScanRules(extraRules) {
|
|
2222
|
+
const fallback = extraRules ?? [];
|
|
2223
|
+
return [...DEFAULT_SECRET_PATTERNS, ...fallback];
|
|
2224
|
+
}
|
|
2225
|
+
function findSecretInPath(relativePath, extraRules) {
|
|
2222
2226
|
const posixPath = relativePath.replace(/\\/g, "/");
|
|
2223
2227
|
const spans = [];
|
|
2224
2228
|
const rules = [];
|
|
2225
|
-
for (const { rule, pattern } of
|
|
2229
|
+
for (const { rule, pattern } of pathScanRules(extraRules)) {
|
|
2226
2230
|
const scan = new RegExp(pattern.source, `${pattern.flags.replace(/[gy]/g, "")}g`);
|
|
2227
2231
|
let match;
|
|
2228
2232
|
while ((match = scan.exec(posixPath)) !== null) {
|
|
@@ -2230,24 +2234,39 @@ function findSecretInPath(relativePath) {
|
|
|
2230
2234
|
scan.lastIndex += 1;
|
|
2231
2235
|
continue;
|
|
2232
2236
|
}
|
|
2233
|
-
spans.push({ start: match.index, end: match.index + match[0].length
|
|
2237
|
+
spans.push({ start: match.index, end: match.index + match[0].length });
|
|
2234
2238
|
if (!rules.includes(rule)) rules.push(rule);
|
|
2235
2239
|
}
|
|
2236
2240
|
}
|
|
2237
2241
|
if (spans.length === 0) return null;
|
|
2238
2242
|
spans.sort((a, b) => a.start - b.start || a.end - b.end);
|
|
2243
|
+
const merged = [];
|
|
2244
|
+
for (const span of spans) {
|
|
2245
|
+
const last = merged[merged.length - 1];
|
|
2246
|
+
if (last && span.start <= last.end) last.end = Math.max(last.end, span.end);
|
|
2247
|
+
else merged.push({ start: span.start, end: span.end });
|
|
2248
|
+
}
|
|
2239
2249
|
let out = "";
|
|
2240
2250
|
let cursor = 0;
|
|
2241
|
-
for (const span of
|
|
2242
|
-
|
|
2243
|
-
const start = Math.max(span.start, cursor);
|
|
2244
|
-
out += posixPath.slice(cursor, start);
|
|
2251
|
+
for (const span of merged) {
|
|
2252
|
+
out += posixPath.slice(cursor, span.start);
|
|
2245
2253
|
out += REDACTION_MARKER;
|
|
2246
|
-
cursor =
|
|
2254
|
+
cursor = span.end;
|
|
2247
2255
|
}
|
|
2248
2256
|
out += posixPath.slice(cursor);
|
|
2249
2257
|
return { rules, masked: out };
|
|
2250
2258
|
}
|
|
2259
|
+
function maskSkipPaths(records, extraRules) {
|
|
2260
|
+
return records.map((record) => {
|
|
2261
|
+
const hit = findSecretInPath(record.path, extraRules);
|
|
2262
|
+
const detail = record.detail ? findSecretInPath(record.detail, extraRules) : null;
|
|
2263
|
+
return {
|
|
2264
|
+
...record,
|
|
2265
|
+
path: hit ? hit.masked : record.path,
|
|
2266
|
+
...detail ? { detail: detail.masked } : {}
|
|
2267
|
+
};
|
|
2268
|
+
});
|
|
2269
|
+
}
|
|
2251
2270
|
function globToRegExp(glob) {
|
|
2252
2271
|
const normalized = glob.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+$/, "");
|
|
2253
2272
|
const anchored = normalized.startsWith("/");
|
|
@@ -2478,6 +2497,7 @@ function collectFiles(cwd, options = {}) {
|
|
|
2478
2497
|
const includePatterns = options.includePatterns ?? [];
|
|
2479
2498
|
const excludePatterns = options.excludePatterns ?? [];
|
|
2480
2499
|
const includeSensitiveFiles = options.includeSensitiveFiles ?? false;
|
|
2500
|
+
const pathRules = options.secretRules;
|
|
2481
2501
|
const isToolOwned = makeToolOwnedDecider(cwd);
|
|
2482
2502
|
let candidates;
|
|
2483
2503
|
const skipped = [];
|
|
@@ -2513,7 +2533,7 @@ function collectFiles(cwd, options = {}) {
|
|
|
2513
2533
|
}
|
|
2514
2534
|
}
|
|
2515
2535
|
} catch {
|
|
2516
|
-
const walked = walkDirectory(cwd, cwd, /* @__PURE__ */ new Set(), true);
|
|
2536
|
+
const walked = walkDirectory(cwd, cwd, rootRealFor(cwd), /* @__PURE__ */ new Set(), true);
|
|
2517
2537
|
candidates = walked.files;
|
|
2518
2538
|
skipped.push(...walked.skipped);
|
|
2519
2539
|
}
|
|
@@ -2551,7 +2571,7 @@ function collectFiles(cwd, options = {}) {
|
|
|
2551
2571
|
});
|
|
2552
2572
|
return false;
|
|
2553
2573
|
}
|
|
2554
|
-
const secret = findSecretInPath(file);
|
|
2574
|
+
const secret = findSecretInPath(file, pathRules);
|
|
2555
2575
|
if (secret) {
|
|
2556
2576
|
skipped.push({
|
|
2557
2577
|
path: secret.masked,
|
|
@@ -2562,30 +2582,29 @@ function collectFiles(cwd, options = {}) {
|
|
|
2562
2582
|
}
|
|
2563
2583
|
return true;
|
|
2564
2584
|
}).sort();
|
|
2565
|
-
return { files, skipped };
|
|
2585
|
+
return { files, skipped: maskSkipPaths(skipped, pathRules) };
|
|
2566
2586
|
}
|
|
2567
|
-
function
|
|
2568
|
-
const results = { files: [], skipped: [] };
|
|
2569
|
-
let realDir;
|
|
2587
|
+
function rootRealFor(root) {
|
|
2570
2588
|
try {
|
|
2571
|
-
|
|
2589
|
+
return fs9__namespace.realpathSync(root);
|
|
2572
2590
|
} catch {
|
|
2573
|
-
|
|
2574
|
-
}
|
|
2575
|
-
if (visited.has(realDir)) return results;
|
|
2576
|
-
visited.add(realDir);
|
|
2577
|
-
let rootReal;
|
|
2578
|
-
try {
|
|
2579
|
-
rootReal = fs9__namespace.realpathSync(baseDir);
|
|
2580
|
-
} catch {
|
|
2581
|
-
rootReal = baseDir;
|
|
2591
|
+
return root;
|
|
2582
2592
|
}
|
|
2593
|
+
}
|
|
2594
|
+
function walkDirectory(dir, baseDir, rootReal, visited, isRoot = false) {
|
|
2595
|
+
const results = { files: [], skipped: [] };
|
|
2596
|
+
if (visited.has(dir)) return results;
|
|
2597
|
+
visited.add(dir);
|
|
2583
2598
|
const ignoredByGitignore = isRoot ? readGitignoreMatcher(baseDir) : () => false;
|
|
2584
2599
|
let entries;
|
|
2585
2600
|
try {
|
|
2586
2601
|
entries = fs9__namespace.readdirSync(dir, { withFileTypes: true });
|
|
2587
2602
|
} catch {
|
|
2588
|
-
results.skipped.push({
|
|
2603
|
+
results.skipped.push({
|
|
2604
|
+
path: path10__namespace.relative(baseDir, dir).replace(/\\/g, "/") || ".",
|
|
2605
|
+
reason: "read-error",
|
|
2606
|
+
detail: "directory unreadable"
|
|
2607
|
+
});
|
|
2589
2608
|
return results;
|
|
2590
2609
|
}
|
|
2591
2610
|
for (const entry of entries) {
|
|
@@ -2664,7 +2683,7 @@ function walkDirectory(dir, baseDir, visited, isRoot = false) {
|
|
|
2664
2683
|
isFile = true;
|
|
2665
2684
|
}
|
|
2666
2685
|
if (isDirectory) {
|
|
2667
|
-
const nested = walkDirectory(fullPath, baseDir, visited);
|
|
2686
|
+
const nested = walkDirectory(fullPath, baseDir, rootReal, visited);
|
|
2668
2687
|
results.files.push(...nested.files);
|
|
2669
2688
|
results.skipped.push(...nested.skipped);
|
|
2670
2689
|
} else if (isFile) {
|
|
@@ -2935,6 +2954,9 @@ async function loadConfigFileDetailed(cwd) {
|
|
|
2935
2954
|
}
|
|
2936
2955
|
async function loadConfigFile(cwd) {
|
|
2937
2956
|
const loaded = await loadConfigFileDetailed(cwd);
|
|
2957
|
+
if (loaded.errors.length > 0) {
|
|
2958
|
+
throw new Error(`The config file could not be read: ${loaded.errors.join("; ")}`);
|
|
2959
|
+
}
|
|
2938
2960
|
return loaded.config;
|
|
2939
2961
|
}
|
|
2940
2962
|
var MIN_MAX_PART_BYTES = 4096;
|
|
@@ -3363,7 +3385,8 @@ function defineConfig(config) {
|
|
|
3363
3385
|
}
|
|
3364
3386
|
|
|
3365
3387
|
// src/generator/audit.ts
|
|
3366
|
-
function generateAuditReport(manifest, secretFindings) {
|
|
3388
|
+
function generateAuditReport(manifest, secretFindings, secretRules) {
|
|
3389
|
+
const mask = (value) => findSecretInPath(value, secretRules)?.masked ?? value;
|
|
3367
3390
|
const lines = [
|
|
3368
3391
|
"# Codebase Health & Security Audit",
|
|
3369
3392
|
"",
|
|
@@ -3398,7 +3421,7 @@ function generateAuditReport(manifest, secretFindings) {
|
|
|
3398
3421
|
"| :--- | :--- | :--- |"
|
|
3399
3422
|
);
|
|
3400
3423
|
for (const finding of secretFindings) {
|
|
3401
|
-
lines.push(`| \`${finding.path ?? "unknown"}\` | ${finding.line} | \`${finding.rule}\` |`);
|
|
3424
|
+
lines.push(`| \`${mask(finding.path ?? "unknown")}\` | ${finding.line} | \`${finding.rule}\` |`);
|
|
3402
3425
|
}
|
|
3403
3426
|
lines.push("", "### Matches by rule", "", "| Rule | Matches |", "| :--- | :--- |");
|
|
3404
3427
|
for (const [rule, count] of Array.from(byRule.entries()).sort((a, b) => b[1] - a[1])) {
|
|
@@ -3420,7 +3443,7 @@ function generateAuditReport(manifest, secretFindings) {
|
|
|
3420
3443
|
for (const file of largeFiles) {
|
|
3421
3444
|
const sizeKb = (file.sizeBytes / 1024).toFixed(1);
|
|
3422
3445
|
const status = file.isPreviewTruncated ? "Truncated Head/Tail" : "Full";
|
|
3423
|
-
lines.push(`| \`${file.relativePath}\` | ${sizeKb} KB | ${file.lines} | ${status} |`);
|
|
3446
|
+
lines.push(`| \`${mask(file.relativePath)}\` | ${sizeKb} KB | ${file.lines} | ${status} |`);
|
|
3424
3447
|
}
|
|
3425
3448
|
lines.push("");
|
|
3426
3449
|
}
|
|
@@ -3462,7 +3485,7 @@ function generateAuditReport(manifest, secretFindings) {
|
|
|
3462
3485
|
"| :--- | :--- | :--- |"
|
|
3463
3486
|
);
|
|
3464
3487
|
for (const skip of manifest.skipped) {
|
|
3465
|
-
lines.push(`| \`${skip.path}\` | \`${skip.reason}\` | ${skip.detail ?? "-"} |`);
|
|
3488
|
+
lines.push(`| \`${mask(skip.path)}\` | \`${skip.reason}\` | ${skip.detail ?? "-"} |`);
|
|
3466
3489
|
}
|
|
3467
3490
|
lines.push("");
|
|
3468
3491
|
}
|
|
@@ -4124,6 +4147,9 @@ function detectLanguage(filePath) {
|
|
|
4124
4147
|
const ext = path10__namespace.posix.extname(base).toLowerCase();
|
|
4125
4148
|
return LANGUAGE_BY_EXTENSION[ext] ?? "text";
|
|
4126
4149
|
}
|
|
4150
|
+
function maskedPath(value, secretRules) {
|
|
4151
|
+
return findSecretInPath(value, secretRules)?.masked ?? value;
|
|
4152
|
+
}
|
|
4127
4153
|
function emptyDrift() {
|
|
4128
4154
|
return {
|
|
4129
4155
|
missingInSnapshot: [],
|
|
@@ -4194,15 +4220,20 @@ function validateSnapshot(configInput) {
|
|
|
4194
4220
|
const skippedSet = new Set(
|
|
4195
4221
|
manifest.skipped.filter((entry) => entry && typeof entry.path === "string").map((entry) => entry.path)
|
|
4196
4222
|
);
|
|
4223
|
+
const isSkippedOnDisk = (diskFile) => {
|
|
4224
|
+
if (skippedSet.has(diskFile)) return true;
|
|
4225
|
+
return skippedSet.has(maskedPath(diskFile, config.secretRules));
|
|
4226
|
+
};
|
|
4197
4227
|
const collection = collectFiles(config.cwd, {
|
|
4198
4228
|
includePatterns: config.include,
|
|
4199
4229
|
excludePatterns: [...config.excludePatterns, ...config.presetExclude],
|
|
4200
|
-
includeSensitiveFiles: config.includeSensitiveFiles
|
|
4230
|
+
includeSensitiveFiles: config.includeSensitiveFiles,
|
|
4231
|
+
secretRules: config.secretRules
|
|
4201
4232
|
});
|
|
4202
4233
|
for (const diskFile of collection.files) {
|
|
4203
4234
|
const snapshotHash = manifestFileMap.get(diskFile);
|
|
4204
4235
|
if (!snapshotHash) {
|
|
4205
|
-
if (
|
|
4236
|
+
if (isSkippedOnDisk(diskFile) && !wouldNowBeIncluded(config.cwd, diskFile)) continue;
|
|
4206
4237
|
drift.missingInSnapshot.push(diskFile);
|
|
4207
4238
|
continue;
|
|
4208
4239
|
}
|
|
@@ -4471,10 +4502,19 @@ function validateSnapshot(configInput) {
|
|
|
4471
4502
|
`${drift.configMismatches.length} configuration mismatch(es) between the manifest and this run.`
|
|
4472
4503
|
);
|
|
4473
4504
|
}
|
|
4505
|
+
const maskedDrift = {
|
|
4506
|
+
...drift,
|
|
4507
|
+
missingInSnapshot: drift.missingInSnapshot.map((value) => maskedPath(value, config.secretRules)),
|
|
4508
|
+
missingOnDisk: drift.missingOnDisk.map((value) => maskedPath(value, config.secretRules)),
|
|
4509
|
+
hashMismatches: drift.hashMismatches.map((mismatch) => ({
|
|
4510
|
+
...mismatch,
|
|
4511
|
+
path: maskedPath(mismatch.path, config.secretRules)
|
|
4512
|
+
}))
|
|
4513
|
+
};
|
|
4474
4514
|
return {
|
|
4475
4515
|
valid: errors.length === 0,
|
|
4476
4516
|
manifestPresent: true,
|
|
4477
|
-
drift,
|
|
4517
|
+
drift: maskedDrift,
|
|
4478
4518
|
errors,
|
|
4479
4519
|
warnings
|
|
4480
4520
|
};
|
|
@@ -4509,18 +4549,22 @@ function readManifest2(config) {
|
|
|
4509
4549
|
function explainPath(config, target) {
|
|
4510
4550
|
const relative4 = normalizeTarget(config.cwd, target);
|
|
4511
4551
|
const absolute = path10__namespace.join(config.cwd, relative4);
|
|
4552
|
+
const maskedPath2 = findSecretInPath(relative4, config.secretRules)?.masked ?? relative4;
|
|
4512
4553
|
const result = {
|
|
4513
|
-
path:
|
|
4554
|
+
path: maskedPath2,
|
|
4514
4555
|
exists: fs9__namespace.existsSync(absolute),
|
|
4515
4556
|
included: false
|
|
4516
4557
|
};
|
|
4517
4558
|
const collection = collectFiles(config.cwd, {
|
|
4518
4559
|
includePatterns: config.include,
|
|
4519
4560
|
excludePatterns: [...config.excludePatterns, ...config.presetExclude],
|
|
4520
|
-
includeSensitiveFiles: config.includeSensitiveFiles
|
|
4561
|
+
includeSensitiveFiles: config.includeSensitiveFiles,
|
|
4562
|
+
secretRules: config.secretRules
|
|
4521
4563
|
});
|
|
4522
4564
|
if (!collection.files.includes(relative4)) {
|
|
4523
|
-
const record = collection.skipped.find(
|
|
4565
|
+
const record = collection.skipped.find(
|
|
4566
|
+
(entry2) => entry2.path === relative4 || entry2.path === maskedPath2
|
|
4567
|
+
);
|
|
4524
4568
|
if (record) {
|
|
4525
4569
|
result.reason = record.reason;
|
|
4526
4570
|
result.detail = record.detail;
|
|
@@ -4557,7 +4601,7 @@ function explainPath(config, target) {
|
|
|
4557
4601
|
result.secrets = findings.slice(0, 20).map((finding) => ({ rule: finding.rule, line: finding.line }));
|
|
4558
4602
|
}
|
|
4559
4603
|
const manifest = readManifest2(config);
|
|
4560
|
-
const entry = manifest?.files.find((file) => file.relativePath === relative4);
|
|
4604
|
+
const entry = Array.isArray(manifest?.files) ? manifest.files.find((file) => file.relativePath === relative4) : void 0;
|
|
4561
4605
|
result.part = entry?.part ?? null;
|
|
4562
4606
|
result.inclusionMode = entry?.inclusionMode;
|
|
4563
4607
|
return result;
|
|
@@ -4662,12 +4706,13 @@ function compareManifests(before, after) {
|
|
|
4662
4706
|
if (!right.has(filePath)) removed.push(filePath);
|
|
4663
4707
|
}
|
|
4664
4708
|
const sort = (values) => values.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
4709
|
+
const mask = (values) => values.map((value) => findSecretInPath(value)?.masked ?? value);
|
|
4665
4710
|
return {
|
|
4666
4711
|
before: side(before),
|
|
4667
4712
|
after: side(after),
|
|
4668
|
-
added: sort(added),
|
|
4669
|
-
removed: sort(removed),
|
|
4670
|
-
changed: sort(changed),
|
|
4713
|
+
added: mask(sort(added)),
|
|
4714
|
+
removed: mask(sort(removed)),
|
|
4715
|
+
changed: mask(sort(changed)),
|
|
4671
4716
|
unchanged
|
|
4672
4717
|
};
|
|
4673
4718
|
}
|
|
@@ -5392,7 +5437,7 @@ async function runInit(options) {
|
|
|
5392
5437
|
|
|
5393
5438
|
// src/index.ts
|
|
5394
5439
|
var GENERATOR_NAME = "@ankurhalder/codebase-snapshot";
|
|
5395
|
-
var GENERATOR_VERSION = "4.0.
|
|
5440
|
+
var GENERATOR_VERSION = "4.0.2";
|
|
5396
5441
|
var SCHEMA_VERSION = "3.0.0";
|
|
5397
5442
|
function sha256Hex2(input) {
|
|
5398
5443
|
return crypto__namespace.createHash("sha256").update(input).digest("hex");
|
|
@@ -5429,7 +5474,6 @@ function bump(counts, key) {
|
|
|
5429
5474
|
}
|
|
5430
5475
|
var RELATIVE_SPECIFIER = /^\.{1,2}\//;
|
|
5431
5476
|
var IMPORT_CANDIDATE_SUFFIXES = [
|
|
5432
|
-
"",
|
|
5433
5477
|
".ts",
|
|
5434
5478
|
".tsx",
|
|
5435
5479
|
".mts",
|
|
@@ -5443,23 +5487,59 @@ var IMPORT_CANDIDATE_SUFFIXES = [
|
|
|
5443
5487
|
"/index.js",
|
|
5444
5488
|
"/index.mjs"
|
|
5445
5489
|
];
|
|
5446
|
-
|
|
5490
|
+
var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
5491
|
+
".ts",
|
|
5492
|
+
".tsx",
|
|
5493
|
+
".mts",
|
|
5494
|
+
".cts",
|
|
5495
|
+
".js",
|
|
5496
|
+
".jsx",
|
|
5497
|
+
".mjs",
|
|
5498
|
+
".cjs"
|
|
5499
|
+
]);
|
|
5500
|
+
function buildImportResolver(files) {
|
|
5501
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
5502
|
+
const byPathFolded = /* @__PURE__ */ new Map();
|
|
5503
|
+
const byStem = /* @__PURE__ */ new Map();
|
|
5504
|
+
const byStemFolded = /* @__PURE__ */ new Map();
|
|
5505
|
+
const remember = (map, key, value) => {
|
|
5506
|
+
if (!map.has(key)) map.set(key, value);
|
|
5507
|
+
};
|
|
5508
|
+
for (const file of files) {
|
|
5509
|
+
const relativePath = file.relativePath;
|
|
5510
|
+
remember(byPath, relativePath, relativePath);
|
|
5511
|
+
remember(byPathFolded, relativePath.toLowerCase(), relativePath);
|
|
5512
|
+
const extension = path10__namespace.posix.extname(relativePath);
|
|
5513
|
+
if (!SOURCE_EXTENSIONS.has(extension)) continue;
|
|
5514
|
+
const stem = relativePath.slice(0, -extension.length);
|
|
5515
|
+
remember(byStem, stem, relativePath);
|
|
5516
|
+
remember(byStemFolded, stem.toLowerCase(), relativePath);
|
|
5517
|
+
if (path10__namespace.posix.basename(stem) === "index") {
|
|
5518
|
+
const directory = path10__namespace.posix.dirname(stem);
|
|
5519
|
+
remember(byStem, directory, relativePath);
|
|
5520
|
+
remember(byStemFolded, directory.toLowerCase(), relativePath);
|
|
5521
|
+
}
|
|
5522
|
+
}
|
|
5523
|
+
return { byPath, byPathFolded, byStem, byStemFolded };
|
|
5524
|
+
}
|
|
5525
|
+
function resolveSpecifier(fromFile, specifier, resolver) {
|
|
5447
5526
|
if (!RELATIVE_SPECIFIER.test(specifier)) return null;
|
|
5448
|
-
const
|
|
5449
|
-
|
|
5450
|
-
|
|
5527
|
+
const joined = path10__namespace.posix.normalize(
|
|
5528
|
+
path10__namespace.posix.join(path10__namespace.posix.dirname(fromFile), specifier)
|
|
5529
|
+
);
|
|
5530
|
+
const exact = (key) => resolver.byPath.get(key) ?? resolver.byPathFolded.get(key.toLowerCase()) ?? null;
|
|
5531
|
+
const direct = exact(joined);
|
|
5532
|
+
if (direct) return direct;
|
|
5451
5533
|
for (const suffix of IMPORT_CANDIDATE_SUFFIXES) {
|
|
5452
|
-
const
|
|
5453
|
-
if (
|
|
5534
|
+
const hit = exact(`${joined}${suffix}`);
|
|
5535
|
+
if (hit) return hit;
|
|
5454
5536
|
}
|
|
5455
|
-
const
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
}
|
|
5459
|
-
return null;
|
|
5537
|
+
const stem = joined.replace(/\.(?:js|mjs|cjs|jsx)$/, "");
|
|
5538
|
+
const byStem = (key) => resolver.byStem.get(key) ?? resolver.byStemFolded.get(key.toLowerCase()) ?? null;
|
|
5539
|
+
return byStem(joined) ?? byStem(stem);
|
|
5460
5540
|
}
|
|
5461
5541
|
function buildIndexes(files) {
|
|
5462
|
-
const
|
|
5542
|
+
const resolver = buildImportResolver(files);
|
|
5463
5543
|
const importerCounts = /* @__PURE__ */ new Map();
|
|
5464
5544
|
const byFile = files.filter((file) => isSourceSummary(file.summary)).map((file) => {
|
|
5465
5545
|
const summary = file.summary;
|
|
@@ -5471,7 +5551,7 @@ function buildIndexes(files) {
|
|
|
5471
5551
|
});
|
|
5472
5552
|
for (const entry of byFile) {
|
|
5473
5553
|
for (const specifier of entry.imports) {
|
|
5474
|
-
const resolved = resolveSpecifier(entry.path, specifier,
|
|
5554
|
+
const resolved = resolveSpecifier(entry.path, specifier, resolver);
|
|
5475
5555
|
if (resolved) importerCounts.set(resolved, (importerCounts.get(resolved) ?? 0) + 1);
|
|
5476
5556
|
}
|
|
5477
5557
|
}
|
|
@@ -5562,15 +5642,22 @@ function resolveSafeOutputDirs(config) {
|
|
|
5562
5642
|
return { outDir, tmpDir };
|
|
5563
5643
|
}
|
|
5564
5644
|
var PLACEHOLDER_ENTRIES = /* @__PURE__ */ new Set([".gitkeep", ".gitignore", ".DS_Store"]);
|
|
5565
|
-
var
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5645
|
+
var RUN_DIR_PREFIX = "run-";
|
|
5646
|
+
var STALE_RUN_MS = 60 * 60 * 1e3;
|
|
5647
|
+
function maskPathText(value, secretRules) {
|
|
5648
|
+
return findSecretInPath(value, secretRules)?.masked ?? value;
|
|
5649
|
+
}
|
|
5650
|
+
function maskSkipRecords(records, secretRules) {
|
|
5651
|
+
return maskSkipPaths(records, secretRules);
|
|
5652
|
+
}
|
|
5653
|
+
function isProcessAlive(pid) {
|
|
5654
|
+
try {
|
|
5655
|
+
process.kill(pid, 0);
|
|
5656
|
+
return true;
|
|
5657
|
+
} catch (err) {
|
|
5658
|
+
return err.code !== "ESRCH";
|
|
5659
|
+
}
|
|
5660
|
+
}
|
|
5574
5661
|
var OWNERSHIP_MARKER = ".codebase-snapshot";
|
|
5575
5662
|
var SUPPORTED_MARKER_SCHEMAS = /* @__PURE__ */ new Set([SCHEMA_VERSION]);
|
|
5576
5663
|
var SNAPSHOT_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -5600,6 +5687,7 @@ function markerPayload(dir, state, id, createdAt) {
|
|
|
5600
5687
|
createdAt,
|
|
5601
5688
|
state,
|
|
5602
5689
|
id,
|
|
5690
|
+
pid: process.pid,
|
|
5603
5691
|
entries: listEntries(dir)
|
|
5604
5692
|
};
|
|
5605
5693
|
const manifestPath = path10__namespace.join(dir, "manifest.json");
|
|
@@ -5663,6 +5751,7 @@ function readOwnershipMarker(dir) {
|
|
|
5663
5751
|
createdAt: typeof candidate.createdAt === "string" ? candidate.createdAt : "unknown",
|
|
5664
5752
|
state: candidate.state,
|
|
5665
5753
|
id: candidate.id,
|
|
5754
|
+
pid: typeof candidate.pid === "number" ? candidate.pid : void 0,
|
|
5666
5755
|
manifestSha256: typeof candidate.manifestSha256 === "string" ? candidate.manifestSha256 : void 0,
|
|
5667
5756
|
entries: Array.isArray(candidate.entries) && candidate.entries.every((entry) => typeof entry === "string") ? candidate.entries : void 0
|
|
5668
5757
|
},
|
|
@@ -5770,7 +5859,7 @@ function describeEntries(entries, limit = 5) {
|
|
|
5770
5859
|
const shown = entries.slice(0, limit).join(", ");
|
|
5771
5860
|
return entries.length > limit ? `${shown}, and ${entries.length - limit} more` : shown;
|
|
5772
5861
|
}
|
|
5773
|
-
function assertReplaceable(target, label) {
|
|
5862
|
+
function assertReplaceable(target, label, secretRules) {
|
|
5774
5863
|
if (!fs9__namespace.existsSync(target)) return;
|
|
5775
5864
|
if (!fs9__namespace.statSync(target).isDirectory()) {
|
|
5776
5865
|
throw new Error(
|
|
@@ -5779,34 +5868,89 @@ function assertReplaceable(target, label) {
|
|
|
5779
5868
|
}
|
|
5780
5869
|
const entries = fs9__namespace.readdirSync(target);
|
|
5781
5870
|
if (entries.length === 0) return;
|
|
5782
|
-
if (
|
|
5783
|
-
const {
|
|
5784
|
-
if (label === "tmpDir") {
|
|
5785
|
-
if (!marker2) {
|
|
5786
|
-
throw new Error(
|
|
5787
|
-
`Refusing to replace tmpDir ${target}: ${problem ?? "it does not carry this generator's marker"}. This generator only clears a staging directory it wrote. Point tmpDir at a new or empty directory, or remove ${target} yourself.`
|
|
5788
|
-
);
|
|
5789
|
-
}
|
|
5790
|
-
const unexpected = entries.filter((name) => !STAGING_ENTRIES.has(name));
|
|
5791
|
-
if (unexpected.length > 0) {
|
|
5792
|
-
throw new Error(
|
|
5793
|
-
`Refusing to replace tmpDir ${target}: it holds ${describeEntries(unexpected)}, which this generator did not write. Point tmpDir at a new or empty directory, or remove those files yourself.`
|
|
5794
|
-
);
|
|
5795
|
-
}
|
|
5796
|
-
return;
|
|
5797
|
-
}
|
|
5871
|
+
if (entries.every((name) => PLACEHOLDER_ENTRIES.has(name))) return;
|
|
5872
|
+
const { problem } = readOwnershipMarker(target);
|
|
5798
5873
|
const verdict = assessSnapshotDirectory(target);
|
|
5799
5874
|
if (verdict.owned) return;
|
|
5875
|
+
const shownForeign = verdict.foreignEntries.map((name) => maskPathText(name, secretRules));
|
|
5876
|
+
const shownEntries = entries.map((name) => maskPathText(name, secretRules));
|
|
5800
5877
|
if (verdict.foreignEntries.length > 0) {
|
|
5801
5878
|
throw new Error(
|
|
5802
|
-
`Refusing to replace ${label} ${target}: it carries this generator's ownership marker, but these entries are not part of the snapshot it recorded: ${describeEntries(
|
|
5879
|
+
`Refusing to replace ${label} ${target}: it carries this generator's ownership marker, but these entries are not part of the snapshot it recorded: ${describeEntries(shownForeign)}. Remove them yourself, or point ${label} at a new or empty directory.`
|
|
5803
5880
|
);
|
|
5804
5881
|
}
|
|
5805
5882
|
const reason = verdict.problem ?? problem ?? "it does not look like a snapshot this generator wrote";
|
|
5806
5883
|
throw new Error(
|
|
5807
|
-
`Refusing to replace ${label} ${target}: ${reason} (found ${describeEntries(
|
|
5884
|
+
`Refusing to replace ${label} ${target}: ${reason} (found ${describeEntries(shownEntries)}). Remove the directory yourself, or point ${label} at a new or empty directory.`
|
|
5808
5885
|
);
|
|
5809
5886
|
}
|
|
5887
|
+
function assertStagingRootUsable(tmpDir, secretRules) {
|
|
5888
|
+
if (!fs9__namespace.existsSync(tmpDir)) return;
|
|
5889
|
+
if (!fs9__namespace.statSync(tmpDir).isDirectory()) {
|
|
5890
|
+
throw new Error(`Refusing to write into tmpDir ${tmpDir}: it exists but is not a directory.`);
|
|
5891
|
+
}
|
|
5892
|
+
const entries = fs9__namespace.readdirSync(tmpDir);
|
|
5893
|
+
if (entries.length === 0) return;
|
|
5894
|
+
const foreign = entries.filter((name) => {
|
|
5895
|
+
const full = path10__namespace.join(tmpDir, name);
|
|
5896
|
+
if (!name.startsWith(RUN_DIR_PREFIX)) return true;
|
|
5897
|
+
let stats;
|
|
5898
|
+
try {
|
|
5899
|
+
stats = fs9__namespace.statSync(full);
|
|
5900
|
+
} catch {
|
|
5901
|
+
return true;
|
|
5902
|
+
}
|
|
5903
|
+
if (!stats.isDirectory()) return true;
|
|
5904
|
+
const { marker: marker2 } = readOwnershipMarker(full);
|
|
5905
|
+
if (marker2) return false;
|
|
5906
|
+
try {
|
|
5907
|
+
return fs9__namespace.readdirSync(full).length > 0;
|
|
5908
|
+
} catch {
|
|
5909
|
+
return true;
|
|
5910
|
+
}
|
|
5911
|
+
});
|
|
5912
|
+
if (foreign.length > 0) {
|
|
5913
|
+
const shown = describeEntries(foreign.map((name) => maskPathText(name, secretRules)));
|
|
5914
|
+
throw new Error(
|
|
5915
|
+
`Refusing to replace tmpDir ${tmpDir}: it holds ${shown}, which this generator did not write. Point tmpDir at a new or empty directory, or remove those files yourself.`
|
|
5916
|
+
);
|
|
5917
|
+
}
|
|
5918
|
+
}
|
|
5919
|
+
function cleanStaleRunDirs(tmpDir) {
|
|
5920
|
+
let entries;
|
|
5921
|
+
try {
|
|
5922
|
+
entries = fs9__namespace.readdirSync(tmpDir);
|
|
5923
|
+
} catch {
|
|
5924
|
+
return;
|
|
5925
|
+
}
|
|
5926
|
+
for (const name of entries) {
|
|
5927
|
+
if (!name.startsWith(RUN_DIR_PREFIX)) continue;
|
|
5928
|
+
const full = path10__namespace.join(tmpDir, name);
|
|
5929
|
+
const { marker: marker2 } = readOwnershipMarker(full);
|
|
5930
|
+
if (marker2) {
|
|
5931
|
+
const created = Date.parse(marker2.createdAt);
|
|
5932
|
+
const aged = !Number.isFinite(created) || Date.now() - created > STALE_RUN_MS;
|
|
5933
|
+
const abandoned = typeof marker2.pid === "number" && !isProcessAlive(marker2.pid);
|
|
5934
|
+
if (aged || abandoned) {
|
|
5935
|
+
try {
|
|
5936
|
+
fs9__namespace.rmSync(full, { recursive: true, force: true });
|
|
5937
|
+
} catch {
|
|
5938
|
+
}
|
|
5939
|
+
}
|
|
5940
|
+
continue;
|
|
5941
|
+
}
|
|
5942
|
+
try {
|
|
5943
|
+
if (fs9__namespace.readdirSync(full).length === 0) fs9__namespace.rmSync(full, { recursive: true, force: true });
|
|
5944
|
+
} catch {
|
|
5945
|
+
}
|
|
5946
|
+
}
|
|
5947
|
+
}
|
|
5948
|
+
function removeIfEmpty(dir) {
|
|
5949
|
+
try {
|
|
5950
|
+
if (fs9__namespace.readdirSync(dir).length === 0) fs9__namespace.rmSync(dir, { recursive: true, force: true });
|
|
5951
|
+
} catch {
|
|
5952
|
+
}
|
|
5953
|
+
}
|
|
5810
5954
|
function ensureParentDirectory(target, label) {
|
|
5811
5955
|
const parent = path10__namespace.dirname(target);
|
|
5812
5956
|
if (fs9__namespace.existsSync(parent) && !fs9__namespace.statSync(parent).isDirectory()) {
|
|
@@ -5822,14 +5966,12 @@ function ensureParentDirectory(target, label) {
|
|
|
5822
5966
|
);
|
|
5823
5967
|
}
|
|
5824
5968
|
}
|
|
5825
|
-
function acquireRunLock(
|
|
5826
|
-
const
|
|
5827
|
-
|
|
5828
|
-
`codebase-snapshot-${sha256Hex2(outDir).slice(0, 16)}.lock`
|
|
5829
|
-
);
|
|
5969
|
+
function acquireRunLock(stagingRoot) {
|
|
5970
|
+
const key = lockKeyFor(stagingRoot);
|
|
5971
|
+
const lockPath = path10__namespace.join(os__namespace.tmpdir(), `codebase-snapshot-${key}.lock`);
|
|
5830
5972
|
const payload = `${JSON.stringify({
|
|
5831
5973
|
pid: process.pid,
|
|
5832
|
-
|
|
5974
|
+
stagingRoot,
|
|
5833
5975
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5834
5976
|
})}
|
|
5835
5977
|
`;
|
|
@@ -5854,11 +5996,20 @@ function acquireRunLock(outDir) {
|
|
|
5854
5996
|
continue;
|
|
5855
5997
|
}
|
|
5856
5998
|
throw new Error(
|
|
5857
|
-
`Another generate run is already
|
|
5999
|
+
`Another generate run is already using the staging directory ${stagingRoot}. Wait for it to finish, or delete ${lockPath} if no other run is active.`
|
|
5858
6000
|
);
|
|
5859
6001
|
}
|
|
5860
6002
|
}
|
|
5861
|
-
throw new Error(`Could not acquire the generate lock for ${
|
|
6003
|
+
throw new Error(`Could not acquire the generate lock for ${stagingRoot}.`);
|
|
6004
|
+
}
|
|
6005
|
+
function lockKeyFor(stagingRoot) {
|
|
6006
|
+
let key = stagingRoot;
|
|
6007
|
+
try {
|
|
6008
|
+
key = fs9__namespace.realpathSync(stagingRoot);
|
|
6009
|
+
} catch {
|
|
6010
|
+
}
|
|
6011
|
+
if (process.platform === "win32" || process.platform === "darwin") key = key.toLowerCase();
|
|
6012
|
+
return sha256Hex2(key).slice(0, 16);
|
|
5862
6013
|
}
|
|
5863
6014
|
function lockOwnerIsGone(lockPath) {
|
|
5864
6015
|
let raw;
|
|
@@ -5894,18 +6045,32 @@ function writeManifestAtomically(manifestPath, manifest) {
|
|
|
5894
6045
|
throw err;
|
|
5895
6046
|
}
|
|
5896
6047
|
}
|
|
5897
|
-
function swapIntoPlace(
|
|
6048
|
+
function swapIntoPlace(stagingDir, outDir) {
|
|
5898
6049
|
ensureParentDirectory(outDir, "outDir");
|
|
6050
|
+
const deadline = Date.now() + 3e3;
|
|
6051
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
6052
|
+
try {
|
|
6053
|
+
swapOnce(stagingDir, outDir);
|
|
6054
|
+
return;
|
|
6055
|
+
} catch (err) {
|
|
6056
|
+
const code = err.code;
|
|
6057
|
+
const racy = code === "ENOENT" || code === "ENOTEMPTY" || code === "EEXIST" || code === "EPERM";
|
|
6058
|
+
if (!racy || attempt >= 10 || Date.now() > deadline) throw err;
|
|
6059
|
+
sleepSync(50);
|
|
6060
|
+
}
|
|
6061
|
+
}
|
|
6062
|
+
}
|
|
6063
|
+
function swapOnce(stagingDir, outDir) {
|
|
5899
6064
|
const backupDir = `${outDir}.backup-${process.pid}-${Date.now()}`;
|
|
5900
6065
|
const hadPrevious = fs9__namespace.existsSync(outDir);
|
|
5901
6066
|
if (hadPrevious) fs9__namespace.renameSync(outDir, backupDir);
|
|
5902
6067
|
try {
|
|
5903
6068
|
try {
|
|
5904
|
-
fs9__namespace.renameSync(
|
|
6069
|
+
fs9__namespace.renameSync(stagingDir, outDir);
|
|
5905
6070
|
} catch (err) {
|
|
5906
6071
|
if (err.code === "EXDEV") {
|
|
5907
|
-
fs9__namespace.cpSync(
|
|
5908
|
-
fs9__namespace.rmSync(
|
|
6072
|
+
fs9__namespace.cpSync(stagingDir, outDir, { recursive: true });
|
|
6073
|
+
fs9__namespace.rmSync(stagingDir, { recursive: true, force: true });
|
|
5909
6074
|
} else {
|
|
5910
6075
|
throw err;
|
|
5911
6076
|
}
|
|
@@ -5919,6 +6084,10 @@ function swapIntoPlace(tmpDir, outDir) {
|
|
|
5919
6084
|
throw err;
|
|
5920
6085
|
}
|
|
5921
6086
|
}
|
|
6087
|
+
function sleepSync(milliseconds) {
|
|
6088
|
+
const buffer = new Int32Array(new SharedArrayBuffer(4));
|
|
6089
|
+
Atomics.wait(buffer, 0, 0, milliseconds);
|
|
6090
|
+
}
|
|
5922
6091
|
function artifactWarnings(artifacts) {
|
|
5923
6092
|
const messages = [];
|
|
5924
6093
|
if (!artifacts.manifest) {
|
|
@@ -5981,11 +6150,12 @@ function generateSnapshot(configInput, options) {
|
|
|
5981
6150
|
const collection = collectFiles(config.cwd, {
|
|
5982
6151
|
includePatterns: config.include,
|
|
5983
6152
|
excludePatterns: [...config.excludePatterns, ...config.presetExclude],
|
|
5984
|
-
includeSensitiveFiles: config.includeSensitiveFiles
|
|
6153
|
+
includeSensitiveFiles: config.includeSensitiveFiles,
|
|
6154
|
+
secretRules: config.secretRules
|
|
5985
6155
|
});
|
|
5986
6156
|
const files = [];
|
|
5987
6157
|
const monolithBodies = /* @__PURE__ */ new Map();
|
|
5988
|
-
const skipped = [...collection.skipped];
|
|
6158
|
+
const skipped = maskSkipRecords([...collection.skipped], config.secretRules);
|
|
5989
6159
|
const secretFindings = [];
|
|
5990
6160
|
const warnings = [];
|
|
5991
6161
|
const layerCounts = {};
|
|
@@ -6059,12 +6229,12 @@ function generateSnapshot(configInput, options) {
|
|
|
6059
6229
|
summaryQuality = outcome?.quality;
|
|
6060
6230
|
if (isSourceSummary(summary) && summary.parseErrors) {
|
|
6061
6231
|
warnings.push(
|
|
6062
|
-
`Parse errors in ${relativePath} (${summary.parseErrors}); its summary is incomplete.`
|
|
6232
|
+
`Parse errors in ${maskPathText(relativePath, config.secretRules)} (${summary.parseErrors}); its summary is incomplete.`
|
|
6063
6233
|
);
|
|
6064
6234
|
}
|
|
6065
6235
|
} catch (err) {
|
|
6066
6236
|
warnings.push(
|
|
6067
|
-
`Summary skipped for ${relativePath}: ${err.message}`.replace(/\s+/g, " ")
|
|
6237
|
+
`Summary skipped for ${maskPathText(relativePath, config.secretRules)}: ${err.message}`.replace(/\s+/g, " ")
|
|
6068
6238
|
);
|
|
6069
6239
|
}
|
|
6070
6240
|
files.push({
|
|
@@ -6108,6 +6278,7 @@ function generateSnapshot(configInput, options) {
|
|
|
6108
6278
|
const truncatedPreviewCount = files.filter((file) => file.isPreviewTruncated).length;
|
|
6109
6279
|
const validationErrors = verifyInMemory(files, parts, maxEffectiveBudget);
|
|
6110
6280
|
const totalContentBytes = files.reduce((total, file) => total + file.includedBytes, 0);
|
|
6281
|
+
const maskName = (value) => maskPathText(value, config.secretRules);
|
|
6111
6282
|
const manifest = {
|
|
6112
6283
|
schemaVersion: SCHEMA_VERSION,
|
|
6113
6284
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -6128,13 +6299,13 @@ function generateSnapshot(configInput, options) {
|
|
|
6128
6299
|
includeMonolith: config.includeMonolith,
|
|
6129
6300
|
includeSensitiveFiles: config.includeSensitiveFiles,
|
|
6130
6301
|
artifacts: { ...config.artifacts },
|
|
6131
|
-
include: [...config.include],
|
|
6302
|
+
include: [...config.include].map(maskName),
|
|
6132
6303
|
preset: config.preset,
|
|
6133
6304
|
layersMode: config.layersMode,
|
|
6134
6305
|
budgets: { default: config.budgets.default, byLayer: { ...config.budgets.byLayer } },
|
|
6135
|
-
excludePatterns: [...config.excludePatterns],
|
|
6136
|
-
layers: config.layers.map((layer2) => layer2.name),
|
|
6137
|
-
kinds: config.kinds.map((kind) => kind.kind)
|
|
6306
|
+
excludePatterns: [...config.excludePatterns].map(maskName),
|
|
6307
|
+
layers: config.layers.map((layer2) => maskName(layer2.name)),
|
|
6308
|
+
kinds: config.kinds.map((kind) => maskName(kind.kind))
|
|
6138
6309
|
},
|
|
6139
6310
|
counts: {
|
|
6140
6311
|
totalFiles: files.length,
|
|
@@ -6173,12 +6344,12 @@ function generateSnapshot(configInput, options) {
|
|
|
6173
6344
|
const { content: _content, ...rest } = file;
|
|
6174
6345
|
return rest;
|
|
6175
6346
|
}),
|
|
6176
|
-
skipped,
|
|
6347
|
+
skipped: maskSkipRecords(skipped, config.secretRules),
|
|
6177
6348
|
warnings
|
|
6178
6349
|
};
|
|
6179
6350
|
manifest.warnings = [
|
|
6180
6351
|
...warnings,
|
|
6181
|
-
...secretFindings.map((finding) => `${finding.rule} at ${finding.path ?? "unknown"}:${finding.line}`)
|
|
6352
|
+
...secretFindings.map((finding) => `${finding.rule} at ${maskPathText(finding.path ?? "unknown", config.secretRules)}:${finding.line}`)
|
|
6182
6353
|
];
|
|
6183
6354
|
manifest.totals.warningCount = manifest.warnings.length;
|
|
6184
6355
|
let monolithJson = null;
|
|
@@ -6224,31 +6395,33 @@ function generateSnapshot(configInput, options) {
|
|
|
6224
6395
|
manifest.totals.warningCount = manifest.warnings.length;
|
|
6225
6396
|
const { outDir, tmpDir } = resolveSafeOutputDirs(config);
|
|
6226
6397
|
const { artifacts } = config;
|
|
6227
|
-
assertReplaceable(
|
|
6228
|
-
|
|
6398
|
+
assertReplaceable(outDir, "outDir", config.secretRules);
|
|
6399
|
+
assertStagingRootUsable(tmpDir, config.secretRules);
|
|
6229
6400
|
if (dryRun) {
|
|
6230
6401
|
runAfterGenerate(config, manifest);
|
|
6231
6402
|
return manifest;
|
|
6232
6403
|
}
|
|
6233
|
-
const releaseLock = acquireRunLock(
|
|
6234
|
-
let
|
|
6404
|
+
const releaseLock = acquireRunLock(tmpDir);
|
|
6405
|
+
let stagingDir = null;
|
|
6235
6406
|
try {
|
|
6236
6407
|
ensureParentDirectory(outDir, "outDir");
|
|
6237
|
-
fs9__namespace.rmSync(tmpDir, { recursive: true, force: true });
|
|
6238
6408
|
fs9__namespace.mkdirSync(tmpDir, { recursive: true });
|
|
6239
|
-
|
|
6409
|
+
cleanStaleRunDirs(tmpDir);
|
|
6240
6410
|
const snapshotId = crypto__namespace.randomUUID();
|
|
6241
6411
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6242
|
-
|
|
6412
|
+
stagingDir = path10__namespace.join(tmpDir, `${RUN_DIR_PREFIX}${process.pid}-${snapshotId.slice(0, 8)}`);
|
|
6413
|
+
fs9__namespace.mkdirSync(stagingDir);
|
|
6414
|
+
writeOwnershipMarker(stagingDir, {
|
|
6243
6415
|
generator: GENERATOR_NAME,
|
|
6244
6416
|
version: GENERATOR_VERSION,
|
|
6245
6417
|
schemaVersion: SCHEMA_VERSION,
|
|
6246
6418
|
createdAt,
|
|
6247
6419
|
state: "staging",
|
|
6248
|
-
id: snapshotId
|
|
6420
|
+
id: snapshotId,
|
|
6421
|
+
pid: process.pid
|
|
6249
6422
|
});
|
|
6250
|
-
const partsDir = path10__namespace.join(
|
|
6251
|
-
const reportsDir = path10__namespace.join(
|
|
6423
|
+
const partsDir = path10__namespace.join(stagingDir, "parts");
|
|
6424
|
+
const reportsDir = path10__namespace.join(stagingDir, "reports");
|
|
6252
6425
|
if (artifacts.parts) fs9__namespace.mkdirSync(partsDir, { recursive: true });
|
|
6253
6426
|
if (artifacts.navigation || artifacts.projectTree || artifacts.audit) {
|
|
6254
6427
|
fs9__namespace.mkdirSync(reportsDir, { recursive: true });
|
|
@@ -6268,25 +6441,25 @@ function generateSnapshot(configInput, options) {
|
|
|
6268
6441
|
if (artifacts.audit) {
|
|
6269
6442
|
fs9__namespace.writeFileSync(
|
|
6270
6443
|
path10__namespace.join(reportsDir, "audit.md"),
|
|
6271
|
-
generateAuditReport(manifest, secretFindings),
|
|
6444
|
+
generateAuditReport(manifest, secretFindings, config.secretRules),
|
|
6272
6445
|
"utf8"
|
|
6273
6446
|
);
|
|
6274
6447
|
}
|
|
6275
6448
|
if (artifacts.readme) {
|
|
6276
|
-
fs9__namespace.writeFileSync(path10__namespace.join(
|
|
6449
|
+
fs9__namespace.writeFileSync(path10__namespace.join(stagingDir, "README.md"), generateReadme(manifest), "utf8");
|
|
6277
6450
|
}
|
|
6278
6451
|
if (artifacts.manifest) {
|
|
6279
|
-
fs9__namespace.writeFileSync(path10__namespace.join(
|
|
6452
|
+
fs9__namespace.writeFileSync(path10__namespace.join(stagingDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf8");
|
|
6280
6453
|
}
|
|
6281
6454
|
if (monolithJson !== null) {
|
|
6282
|
-
fs9__namespace.writeFileSync(path10__namespace.join(
|
|
6455
|
+
fs9__namespace.writeFileSync(path10__namespace.join(stagingDir, "full-codebase.json"), monolithJson, "utf8");
|
|
6283
6456
|
}
|
|
6284
6457
|
if (monolithMarkdown !== null) {
|
|
6285
|
-
fs9__namespace.writeFileSync(path10__namespace.join(
|
|
6458
|
+
fs9__namespace.writeFileSync(path10__namespace.join(stagingDir, "full-codebase.md"), monolithMarkdown, "utf8");
|
|
6286
6459
|
}
|
|
6287
|
-
writeOwnershipMarker(
|
|
6288
|
-
swapIntoPlace(
|
|
6289
|
-
|
|
6460
|
+
writeOwnershipMarker(stagingDir, markerPayload(stagingDir, "complete", snapshotId, createdAt));
|
|
6461
|
+
swapIntoPlace(stagingDir, outDir);
|
|
6462
|
+
stagingDir = null;
|
|
6290
6463
|
const warningsBeforeHook = manifest.warnings.length;
|
|
6291
6464
|
runAfterGenerate(config, manifest);
|
|
6292
6465
|
if (artifacts.manifest && manifest.warnings.length !== warningsBeforeHook) {
|
|
@@ -6295,14 +6468,15 @@ function generateSnapshot(configInput, options) {
|
|
|
6295
6468
|
writeOwnershipMarker(outDir, markerPayload(outDir, "complete", snapshotId, createdAt));
|
|
6296
6469
|
return manifest;
|
|
6297
6470
|
} catch (err) {
|
|
6298
|
-
if (
|
|
6471
|
+
if (stagingDir !== null) {
|
|
6299
6472
|
try {
|
|
6300
|
-
fs9__namespace.rmSync(
|
|
6473
|
+
fs9__namespace.rmSync(stagingDir, { recursive: true, force: true });
|
|
6301
6474
|
} catch {
|
|
6302
6475
|
}
|
|
6303
6476
|
}
|
|
6304
6477
|
throw err;
|
|
6305
6478
|
} finally {
|
|
6479
|
+
removeIfEmpty(tmpDir);
|
|
6306
6480
|
releaseLock();
|
|
6307
6481
|
}
|
|
6308
6482
|
}
|