codebase-snapshot 0.9.12 → 0.9.14
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 +379 -0
- package/README.md +1 -1
- package/dist/bin.js +1313 -633
- package/dist/{chunk-3H45YMBG.js → chunk-DUZK5VL2.js} +1413 -767
- package/dist/{chunk-5OR3WVPW.js → chunk-J4W74N4Y.js} +144 -44
- package/dist/cli.cjs +1305 -628
- package/dist/cli.d.cts +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +43 -54
- package/dist/{generate-worker-D5Xgh27c.d.cts → generate-worker-D_mUjgXw.d.cts} +21 -5
- package/dist/{generate-worker-D5Xgh27c.d.ts → generate-worker-D_mUjgXw.d.ts} +21 -5
- package/dist/index.cjs +1389 -760
- package/dist/index.d.cts +58 -35
- package/dist/index.d.ts +58 -35
- package/dist/index.js +1 -1
- package/dist/mcp.cjs +973 -405
- package/dist/mcp.d.cts +67 -67
- package/dist/mcp.d.ts +67 -67
- package/dist/mcp.js +2 -2
- package/dist/{types-CTewen3J.d.cts → types-DIX6zWSt.d.cts} +15 -0
- package/dist/{types-CTewen3J.d.ts → types-DIX6zWSt.d.ts} +15 -0
- package/docs/cli.md +8 -6
- package/docs/configuration.md +44 -10
- package/docs/mcp.md +16 -3
- package/docs/output.md +13 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -9,6 +9,7 @@ var ts = require('typescript');
|
|
|
9
9
|
var module$1 = require('module');
|
|
10
10
|
var url = require('url');
|
|
11
11
|
var childProcess = require('child_process');
|
|
12
|
+
var http = require('http');
|
|
12
13
|
var https = require('https');
|
|
13
14
|
var readline = require('readline');
|
|
14
15
|
require('worker_threads');
|
|
@@ -39,10 +40,56 @@ var os__namespace = /*#__PURE__*/_interopNamespace(os);
|
|
|
39
40
|
var path16__namespace = /*#__PURE__*/_interopNamespace(path16);
|
|
40
41
|
var ts__default = /*#__PURE__*/_interopDefault(ts);
|
|
41
42
|
var childProcess__namespace = /*#__PURE__*/_interopNamespace(childProcess);
|
|
43
|
+
var http__namespace = /*#__PURE__*/_interopNamespace(http);
|
|
42
44
|
var https__namespace = /*#__PURE__*/_interopNamespace(https);
|
|
43
45
|
var readline__namespace = /*#__PURE__*/_interopNamespace(readline);
|
|
44
46
|
|
|
45
47
|
// src/index.ts
|
|
48
|
+
var hookStdoutScope = new async_hooks.AsyncLocalStorage();
|
|
49
|
+
var captureScope = new async_hooks.AsyncLocalStorage();
|
|
50
|
+
var installs = 0;
|
|
51
|
+
var underlying = null;
|
|
52
|
+
var router = null;
|
|
53
|
+
var asText = (chunk) => typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
|
|
54
|
+
var callbackIn = (rest) => {
|
|
55
|
+
for (const argument of rest) {
|
|
56
|
+
if (typeof argument === "function") return argument;
|
|
57
|
+
}
|
|
58
|
+
return void 0;
|
|
59
|
+
};
|
|
60
|
+
function installStdoutRouter() {
|
|
61
|
+
if (installs === 0) {
|
|
62
|
+
underlying = process.stdout.write;
|
|
63
|
+
const realStdout = underlying.bind(process.stdout);
|
|
64
|
+
const realStderr = process.stderr.write.bind(process.stderr);
|
|
65
|
+
router = ((...args) => {
|
|
66
|
+
if (hookStdoutScope.getStore() === true) return realStderr(...args);
|
|
67
|
+
const capture = captureScope.getStore();
|
|
68
|
+
if (capture !== void 0) {
|
|
69
|
+
capture.write(asText(args[0]));
|
|
70
|
+
const callback = callbackIn(args.slice(1));
|
|
71
|
+
if (callback) callback(null);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
return realStdout(...args);
|
|
75
|
+
});
|
|
76
|
+
process.stdout.write = router;
|
|
77
|
+
}
|
|
78
|
+
installs += 1;
|
|
79
|
+
let released = false;
|
|
80
|
+
return () => {
|
|
81
|
+
if (released) return;
|
|
82
|
+
released = true;
|
|
83
|
+
installs -= 1;
|
|
84
|
+
if (installs === 0) {
|
|
85
|
+
if (process.stdout.write === router && underlying !== null) {
|
|
86
|
+
process.stdout.write = underlying;
|
|
87
|
+
}
|
|
88
|
+
underlying = null;
|
|
89
|
+
router = null;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
46
93
|
|
|
47
94
|
// src/ast/profiles/shared.ts
|
|
48
95
|
var MAX_SUMMARY_CHARS = 1e6;
|
|
@@ -98,7 +145,7 @@ function isCapitalizedName(name) {
|
|
|
98
145
|
}
|
|
99
146
|
|
|
100
147
|
// src/ast/profiles/cfamily.ts
|
|
101
|
-
var INCLUDE = /^#\s*include\s*[<"]([^>"]+)[>"]/;
|
|
148
|
+
var INCLUDE = /^#\s*include\s*([<"])([^>"]+)[>"]/;
|
|
102
149
|
var DEFINE = /^#\s*define\s+([A-Za-z_]\w*)/;
|
|
103
150
|
var USING = /^using\s+(?:namespace\s+)?([\w:]+)\s*;/;
|
|
104
151
|
var TYPE_DECL = /^(?:typedef\s+)?(?:struct|union|class|enum)\s+([A-Za-z_]\w*)/;
|
|
@@ -117,7 +164,7 @@ var cfamilyProfile = {
|
|
|
117
164
|
}
|
|
118
165
|
const included = INCLUDE.exec(trimmed);
|
|
119
166
|
if (included) {
|
|
120
|
-
summary.imports.push(included[1]);
|
|
167
|
+
summary.imports.push(included[1] === '"' ? `./${included[2]}` : included[2]);
|
|
121
168
|
return;
|
|
122
169
|
}
|
|
123
170
|
const used = USING.exec(trimmed);
|
|
@@ -521,7 +568,15 @@ var pythonProfile = {
|
|
|
521
568
|
}
|
|
522
569
|
const from = FROM_IMPORT.exec(trimmed);
|
|
523
570
|
if (from) {
|
|
524
|
-
|
|
571
|
+
const module = from[1];
|
|
572
|
+
if (/^\.+$/.test(module)) {
|
|
573
|
+
for (const name of from[2].split(",")) {
|
|
574
|
+
const imported2 = name.replace(/[()]/g, "").trim().split(/\s+as\s+/)[0].trim();
|
|
575
|
+
if (/^[A-Za-z_]\w*$/.test(imported2)) summary.imports.push(`${module}${imported2}`);
|
|
576
|
+
}
|
|
577
|
+
} else {
|
|
578
|
+
summary.imports.push(module);
|
|
579
|
+
}
|
|
525
580
|
routePending = false;
|
|
526
581
|
return;
|
|
527
582
|
}
|
|
@@ -570,7 +625,8 @@ var pythonProfile = {
|
|
|
570
625
|
};
|
|
571
626
|
|
|
572
627
|
// src/ast/profiles/ruby.ts
|
|
573
|
-
var
|
|
628
|
+
var REQUIRE_RELATIVE = /^require_relative\s+["']([^"']+)["']/;
|
|
629
|
+
var REQUIRE = /^require\s+["']([^"']+)["']/;
|
|
574
630
|
var CLASS_OR_MODULE = /^(?:class|module)\s+([A-Z][\w:]*)/;
|
|
575
631
|
var METHOD2 = /^def\s+(?:self\.)?([A-Za-z_]\w*[?!=]?)/;
|
|
576
632
|
var rubyProfile = {
|
|
@@ -581,6 +637,11 @@ var rubyProfile = {
|
|
|
581
637
|
eachLine(content, (line) => {
|
|
582
638
|
const trimmed = line.trim();
|
|
583
639
|
if (!trimmed || trimmed.startsWith("#")) return;
|
|
640
|
+
const relative8 = REQUIRE_RELATIVE.exec(trimmed);
|
|
641
|
+
if (relative8) {
|
|
642
|
+
summary.imports.push(`./${relative8[1]}`);
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
584
645
|
const required = REQUIRE.exec(trimmed);
|
|
585
646
|
if (required) {
|
|
586
647
|
summary.imports.push(required[1]);
|
|
@@ -777,13 +838,23 @@ function summarizeTypeScriptSource(relativePath, content) {
|
|
|
777
838
|
if (ts__default.default.isImportDeclaration(node) && ts__default.default.isStringLiteral(node.moduleSpecifier)) {
|
|
778
839
|
summary.imports.push(node.moduleSpecifier.text);
|
|
779
840
|
}
|
|
841
|
+
if (ts__default.default.isImportEqualsDeclaration(node) && ts__default.default.isExternalModuleReference(node.moduleReference) && node.moduleReference.expression !== void 0 && ts__default.default.isStringLiteral(node.moduleReference.expression)) {
|
|
842
|
+
summary.imports.push(node.moduleReference.expression.text);
|
|
843
|
+
}
|
|
844
|
+
if (ts__default.default.isCallExpression(node) && node.arguments.length >= 1) {
|
|
845
|
+
const firstArgument = node.arguments[0];
|
|
846
|
+
if (firstArgument !== void 0 && ts__default.default.isStringLiteral(firstArgument)) {
|
|
847
|
+
const isDynamicImport = node.expression.kind === ts__default.default.SyntaxKind.ImportKeyword;
|
|
848
|
+
const isRequire = ts__default.default.isIdentifier(node.expression) && node.expression.text === "require";
|
|
849
|
+
if (isDynamicImport || isRequire) summary.imports.push(firstArgument.text);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
780
852
|
if (ts__default.default.isExportDeclaration(node)) {
|
|
781
853
|
if (node.moduleSpecifier && ts__default.default.isStringLiteral(node.moduleSpecifier)) {
|
|
782
854
|
summary.imports.push(node.moduleSpecifier.text);
|
|
855
|
+
summary.reExports.push(node.moduleSpecifier.text);
|
|
783
856
|
if (node.exportClause && ts__default.default.isNamespaceExport(node.exportClause)) {
|
|
784
857
|
summary.exports.push(node.exportClause.name.text);
|
|
785
|
-
} else if (!node.exportClause) {
|
|
786
|
-
summary.reExports.push(node.moduleSpecifier.text);
|
|
787
858
|
}
|
|
788
859
|
}
|
|
789
860
|
if (node.exportClause && ts__default.default.isNamedExports(node.exportClause)) {
|
|
@@ -875,7 +946,8 @@ function summarizeTypeScriptSource(relativePath, content) {
|
|
|
875
946
|
}
|
|
876
947
|
|
|
877
948
|
// src/ast/profiles/sfc.ts
|
|
878
|
-
var SCRIPT_BLOCK = /<script[^>]
|
|
949
|
+
var SCRIPT_BLOCK = /<script([^>]*)>([\s\S]*?)<\/script>/gi;
|
|
950
|
+
var JSX_LANG = /\blang\s*=\s*["']?[tj]sx\b/i;
|
|
879
951
|
var ASTRO_FRONTMATTER = /^\uFEFF?---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?=\r?\n|$)/;
|
|
880
952
|
function componentName(path17) {
|
|
881
953
|
const base = path17.split("/").pop() ?? path17;
|
|
@@ -886,13 +958,15 @@ var sfcProfile = {
|
|
|
886
958
|
extensions: [".vue", ".svelte", ".astro"],
|
|
887
959
|
summarize(path17, content) {
|
|
888
960
|
let script = "";
|
|
961
|
+
let usesJsx = path17.toLowerCase().endsWith(".astro");
|
|
889
962
|
if (path17.toLowerCase().endsWith(".astro")) {
|
|
890
963
|
const frontmatter = ASTRO_FRONTMATTER.exec(content);
|
|
891
964
|
if (frontmatter) script += `${frontmatter[1]}
|
|
892
965
|
`;
|
|
893
966
|
}
|
|
894
967
|
for (const match of content.matchAll(SCRIPT_BLOCK)) {
|
|
895
|
-
|
|
968
|
+
if (JSX_LANG.test(match[1] ?? "")) usesJsx = true;
|
|
969
|
+
script += `${match[2]}
|
|
896
970
|
`;
|
|
897
971
|
}
|
|
898
972
|
const name = componentName(path17);
|
|
@@ -904,7 +978,7 @@ var sfcProfile = {
|
|
|
904
978
|
}
|
|
905
979
|
return { summary: finalizeSummary(summary2), quality: "heuristic" };
|
|
906
980
|
}
|
|
907
|
-
const summary = summarizeTypeScriptSource(`${name}
|
|
981
|
+
const summary = summarizeTypeScriptSource(`${name}.${usesJsx ? "tsx" : "ts"}`, script);
|
|
908
982
|
summary.reactComponents.push(name);
|
|
909
983
|
summary.exports.push(name);
|
|
910
984
|
return { summary: finalizeSummary(summary), quality: "ast" };
|
|
@@ -1096,124 +1170,421 @@ function summarizeWithProfile(path17, content) {
|
|
|
1096
1170
|
const profile = profileForPath(path17);
|
|
1097
1171
|
return profile ? profile.summarize(path17, content) : null;
|
|
1098
1172
|
}
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1173
|
+
var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
1174
|
+
".png",
|
|
1175
|
+
".jpg",
|
|
1176
|
+
".jpeg",
|
|
1177
|
+
".gif",
|
|
1178
|
+
".webp",
|
|
1179
|
+
".avif",
|
|
1180
|
+
".ico",
|
|
1181
|
+
".bmp",
|
|
1182
|
+
".tiff",
|
|
1183
|
+
".pdf",
|
|
1184
|
+
".zip",
|
|
1185
|
+
".tar",
|
|
1186
|
+
".gz",
|
|
1187
|
+
".tgz",
|
|
1188
|
+
".br",
|
|
1189
|
+
".bz2",
|
|
1190
|
+
".xz",
|
|
1191
|
+
".zst",
|
|
1192
|
+
".lz4",
|
|
1193
|
+
".lzma",
|
|
1194
|
+
".7z",
|
|
1195
|
+
".rar",
|
|
1196
|
+
".mp3",
|
|
1197
|
+
".mp4",
|
|
1198
|
+
".wav",
|
|
1199
|
+
".ogg",
|
|
1200
|
+
".webm",
|
|
1201
|
+
".mov",
|
|
1202
|
+
".avi",
|
|
1203
|
+
".woff",
|
|
1204
|
+
".woff2",
|
|
1205
|
+
".ttf",
|
|
1206
|
+
".otf",
|
|
1207
|
+
".eot",
|
|
1208
|
+
".wasm",
|
|
1209
|
+
".node",
|
|
1210
|
+
".exe",
|
|
1211
|
+
".dll",
|
|
1212
|
+
".dylib",
|
|
1213
|
+
".so"
|
|
1214
|
+
]);
|
|
1215
|
+
function isBinaryExtension(filePath) {
|
|
1216
|
+
const ext = path16__namespace.extname(filePath).toLowerCase();
|
|
1217
|
+
return BINARY_EXTENSIONS.has(ext);
|
|
1218
|
+
}
|
|
1219
|
+
function isBinaryBuffer(buffer) {
|
|
1220
|
+
return buffer.includes(0);
|
|
1221
|
+
}
|
|
1222
|
+
function countControlBytes(buffer) {
|
|
1223
|
+
let count = 0;
|
|
1224
|
+
for (let index = 0; index < buffer.length; index++) {
|
|
1225
|
+
const byte = buffer[index];
|
|
1226
|
+
if (byte < 32) {
|
|
1227
|
+
if (byte !== 9 && byte !== 10 && byte !== 13) count += 1;
|
|
1115
1228
|
continue;
|
|
1116
1229
|
}
|
|
1117
|
-
if (
|
|
1118
|
-
|
|
1119
|
-
inBlockComment = false;
|
|
1120
|
-
i += 1;
|
|
1121
|
-
}
|
|
1230
|
+
if (byte === 127) {
|
|
1231
|
+
count += 1;
|
|
1122
1232
|
continue;
|
|
1123
1233
|
}
|
|
1124
|
-
if (
|
|
1125
|
-
|
|
1126
|
-
if (
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
i += 1;
|
|
1130
|
-
}
|
|
1131
|
-
} else if (char === '"') {
|
|
1132
|
-
inString = false;
|
|
1234
|
+
if (byte === 194 && index + 1 < buffer.length) {
|
|
1235
|
+
const next = buffer[index + 1];
|
|
1236
|
+
if (next >= 128 && next <= 159) {
|
|
1237
|
+
count += 1;
|
|
1238
|
+
index += 1;
|
|
1133
1239
|
}
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
return count;
|
|
1243
|
+
}
|
|
1244
|
+
var CONTROL_BYTES_FLOOR = 4;
|
|
1245
|
+
function countInvalidUtf8Bytes(buffer) {
|
|
1246
|
+
let invalid = 0;
|
|
1247
|
+
let index = 0;
|
|
1248
|
+
const length = buffer.length;
|
|
1249
|
+
while (index < length) {
|
|
1250
|
+
const lead = buffer[index];
|
|
1251
|
+
if (lead < 128) {
|
|
1252
|
+
index += 1;
|
|
1134
1253
|
continue;
|
|
1135
1254
|
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1255
|
+
let continuation = 0;
|
|
1256
|
+
let codePoint = 0;
|
|
1257
|
+
let minimum = 0;
|
|
1258
|
+
if ((lead & 224) === 192) {
|
|
1259
|
+
continuation = 1;
|
|
1260
|
+
codePoint = lead & 31;
|
|
1261
|
+
minimum = 128;
|
|
1262
|
+
} else if ((lead & 240) === 224) {
|
|
1263
|
+
continuation = 2;
|
|
1264
|
+
codePoint = lead & 15;
|
|
1265
|
+
minimum = 2048;
|
|
1266
|
+
} else if ((lead & 248) === 240) {
|
|
1267
|
+
continuation = 3;
|
|
1268
|
+
codePoint = lead & 7;
|
|
1269
|
+
minimum = 65536;
|
|
1270
|
+
} else {
|
|
1271
|
+
invalid += 1;
|
|
1272
|
+
index += 1;
|
|
1139
1273
|
continue;
|
|
1140
1274
|
}
|
|
1141
|
-
if (
|
|
1142
|
-
|
|
1143
|
-
|
|
1275
|
+
if (index + continuation >= length) {
|
|
1276
|
+
invalid += 1;
|
|
1277
|
+
index += 1;
|
|
1144
1278
|
continue;
|
|
1145
1279
|
}
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1280
|
+
let wellFormed = true;
|
|
1281
|
+
for (let offset = 1; offset <= continuation; offset++) {
|
|
1282
|
+
const next = buffer[index + offset];
|
|
1283
|
+
if ((next & 192) !== 128) {
|
|
1284
|
+
wellFormed = false;
|
|
1285
|
+
break;
|
|
1286
|
+
}
|
|
1287
|
+
codePoint = codePoint << 6 | next & 63;
|
|
1288
|
+
}
|
|
1289
|
+
const isSurrogate = codePoint >= 55296 && codePoint <= 57343;
|
|
1290
|
+
if (!wellFormed || codePoint < minimum || codePoint > 1114111 || isSurrogate) {
|
|
1291
|
+
invalid += 1;
|
|
1292
|
+
index += 1;
|
|
1149
1293
|
continue;
|
|
1150
1294
|
}
|
|
1151
|
-
|
|
1295
|
+
index += continuation + 1;
|
|
1152
1296
|
}
|
|
1153
|
-
return
|
|
1297
|
+
return invalid;
|
|
1154
1298
|
}
|
|
1155
|
-
function
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
if (
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1299
|
+
function readBomEncoding(buffer) {
|
|
1300
|
+
if (buffer.length >= 2) {
|
|
1301
|
+
if (buffer[0] === 255 && buffer[1] === 254) return "utf-16le";
|
|
1302
|
+
if (buffer[0] === 254 && buffer[1] === 255) return "utf-16be";
|
|
1303
|
+
}
|
|
1304
|
+
return null;
|
|
1305
|
+
}
|
|
1306
|
+
function looksLikeUtf16(buffer) {
|
|
1307
|
+
if (buffer.length < 8 || buffer.length % 2 !== 0) return null;
|
|
1308
|
+
const sample = Math.min(buffer.length, 4096);
|
|
1309
|
+
let nulInLowByte = 0;
|
|
1310
|
+
let nulInHighByte = 0;
|
|
1311
|
+
const units = sample / 2;
|
|
1312
|
+
for (let index = 0; index < sample; index += 2) {
|
|
1313
|
+
if (buffer[index] === 0) nulInLowByte += 1;
|
|
1314
|
+
if (buffer[index + 1] === 0) nulInHighByte += 1;
|
|
1315
|
+
}
|
|
1316
|
+
if (nulInHighByte > units * 0.3 && nulInLowByte < units * 0.05) return "utf-16le";
|
|
1317
|
+
if (nulInLowByte > units * 0.3 && nulInHighByte < units * 0.05) return "utf-16be";
|
|
1318
|
+
return null;
|
|
1319
|
+
}
|
|
1320
|
+
function decodeUtf16(buffer, encoding) {
|
|
1321
|
+
const littleEndian = encoding === "utf-16le" ? buffer : Buffer.from(buffer.subarray(0, buffer.length - buffer.length % 2)).swap16();
|
|
1322
|
+
return new TextDecoder("utf-16le").decode(littleEndian);
|
|
1323
|
+
}
|
|
1324
|
+
function decodeText(buffer) {
|
|
1325
|
+
const bomEncoding = readBomEncoding(buffer);
|
|
1326
|
+
if (bomEncoding) {
|
|
1327
|
+
const text2 = decodeUtf16(buffer.subarray(2), bomEncoding);
|
|
1328
|
+
return { text: text2, encoding: bomEncoding, invalidBytes: 0, binary: false };
|
|
1329
|
+
}
|
|
1330
|
+
if (buffer.includes(0)) {
|
|
1331
|
+
const shape = looksLikeUtf16(buffer);
|
|
1332
|
+
if (shape) {
|
|
1333
|
+
return { text: decodeUtf16(buffer, shape), encoding: shape, invalidBytes: 0, binary: false };
|
|
1167
1334
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1335
|
+
const offset = buffer.indexOf(0);
|
|
1336
|
+
return {
|
|
1337
|
+
text: "",
|
|
1338
|
+
encoding: "utf-8",
|
|
1339
|
+
invalidBytes: 0,
|
|
1340
|
+
binary: true,
|
|
1341
|
+
binaryReason: `NUL byte at offset ${offset}`
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
1344
|
+
const invalidBytes = countInvalidUtf8Bytes(buffer);
|
|
1345
|
+
if (invalidBytes === 0) {
|
|
1346
|
+
const controlBytes = countControlBytes(buffer);
|
|
1347
|
+
if (controlBytes > CONTROL_BYTES_FLOOR && controlBytes * 100 > buffer.length) {
|
|
1170
1348
|
return {
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1349
|
+
text: "",
|
|
1350
|
+
encoding: "utf-8",
|
|
1351
|
+
invalidBytes: 0,
|
|
1352
|
+
binary: true,
|
|
1353
|
+
binaryReason: `${controlBytes} control byte(s) in ${buffer.length} B; valid UTF-8 but not text`
|
|
1175
1354
|
};
|
|
1176
1355
|
}
|
|
1177
1356
|
return {
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1357
|
+
text: new TextDecoder("utf-8").decode(buffer),
|
|
1358
|
+
encoding: "utf-8",
|
|
1359
|
+
invalidBytes: 0,
|
|
1360
|
+
binary: false
|
|
1182
1361
|
};
|
|
1183
|
-
}
|
|
1362
|
+
}
|
|
1363
|
+
if (invalidBytes >= 32 && invalidBytes * 3 >= buffer.length) {
|
|
1184
1364
|
return {
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1365
|
+
text: "",
|
|
1366
|
+
encoding: "utf-8",
|
|
1367
|
+
invalidBytes,
|
|
1368
|
+
binary: true,
|
|
1369
|
+
binaryReason: `${invalidBytes} of ${buffer.length} bytes are not UTF-8; treated as binary`
|
|
1189
1370
|
};
|
|
1190
1371
|
}
|
|
1372
|
+
return {
|
|
1373
|
+
text: buffer.toString("utf8"),
|
|
1374
|
+
encoding: "utf-8-lossy",
|
|
1375
|
+
invalidBytes,
|
|
1376
|
+
binary: false
|
|
1377
|
+
};
|
|
1191
1378
|
}
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
let
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1379
|
+
var BINARY_SCAN_BYTES = 8 * 1024 * 1024;
|
|
1380
|
+
var CHUNK = 64 * 1024;
|
|
1381
|
+
var SHAPE_SAMPLE_BYTES = 4096;
|
|
1382
|
+
function isBinaryFile(absolutePath) {
|
|
1383
|
+
if (isBinaryExtension(absolutePath)) return true;
|
|
1384
|
+
let fd;
|
|
1385
|
+
try {
|
|
1386
|
+
fd = fs14__namespace.openSync(absolutePath, "r");
|
|
1387
|
+
const buffer = Buffer.alloc(CHUNK);
|
|
1388
|
+
let sample;
|
|
1389
|
+
let position = 0;
|
|
1390
|
+
while (position < BINARY_SCAN_BYTES) {
|
|
1391
|
+
const wanted = Math.min(CHUNK, BINARY_SCAN_BYTES - position);
|
|
1392
|
+
const bytesRead = fs14__namespace.readSync(fd, buffer, 0, wanted, position);
|
|
1393
|
+
if (bytesRead === 0) return false;
|
|
1394
|
+
const chunk = buffer.subarray(0, bytesRead);
|
|
1395
|
+
if (sample === void 0) {
|
|
1396
|
+
sample = Buffer.from(chunk.subarray(0, SHAPE_SAMPLE_BYTES));
|
|
1397
|
+
}
|
|
1398
|
+
if (chunk.includes(0)) {
|
|
1399
|
+
const shape = readBomEncoding(sample) ?? looksLikeUtf16(sample);
|
|
1400
|
+
return shape === null;
|
|
1401
|
+
}
|
|
1402
|
+
position += bytesRead;
|
|
1403
|
+
}
|
|
1404
|
+
return false;
|
|
1405
|
+
} catch {
|
|
1406
|
+
return false;
|
|
1407
|
+
} finally {
|
|
1408
|
+
if (fd !== void 0) {
|
|
1409
|
+
try {
|
|
1410
|
+
fs14__namespace.closeSync(fd);
|
|
1411
|
+
} catch {
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
// src/ast/imports.ts
|
|
1418
|
+
var SUMMARY_MAX_SOURCE_BYTES = 2 * 1024 * 1024;
|
|
1419
|
+
var TS_SOURCE_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
1420
|
+
var isTsSource = (relativePath) => {
|
|
1421
|
+
const lower = relativePath.toLowerCase();
|
|
1422
|
+
return TS_SOURCE_EXTENSIONS.some((extension) => lower.endsWith(extension));
|
|
1423
|
+
};
|
|
1424
|
+
function importsOfText(relativePath, text2) {
|
|
1425
|
+
if (Buffer.byteLength(text2, "utf8") > SUMMARY_MAX_SOURCE_BYTES) return [];
|
|
1426
|
+
if (isTsSource(relativePath)) return summarizeTypeScriptSource(relativePath, text2).imports;
|
|
1427
|
+
return summarizeWithProfile(relativePath, text2)?.summary.imports ?? [];
|
|
1428
|
+
}
|
|
1429
|
+
function importsOfFile(relativePath, absolutePath) {
|
|
1430
|
+
let buffer;
|
|
1431
|
+
try {
|
|
1432
|
+
const stats = fs14__namespace.statSync(absolutePath);
|
|
1433
|
+
if (!stats.isFile() || stats.size > SUMMARY_MAX_SOURCE_BYTES) return [];
|
|
1434
|
+
buffer = fs14__namespace.readFileSync(absolutePath);
|
|
1435
|
+
} catch {
|
|
1436
|
+
return [];
|
|
1437
|
+
}
|
|
1438
|
+
const decoded = decodeText(buffer);
|
|
1439
|
+
if (decoded.binary) return [];
|
|
1440
|
+
return importsOfText(relativePath, decoded.text);
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
// src/ast/structured.ts
|
|
1444
|
+
function stripJsonComments(content) {
|
|
1445
|
+
const text2 = content.replace(/^\uFEFF/, "");
|
|
1446
|
+
const withoutComments = stripComments(text2);
|
|
1447
|
+
return stripTrailingCommas(withoutComments);
|
|
1448
|
+
}
|
|
1449
|
+
function stripComments(text2) {
|
|
1450
|
+
let out = "";
|
|
1451
|
+
let inString = false;
|
|
1452
|
+
let escaped = false;
|
|
1453
|
+
for (let index = 0; index < text2.length; index += 1) {
|
|
1454
|
+
const char = text2[index];
|
|
1455
|
+
if (inString) {
|
|
1456
|
+
out += char;
|
|
1457
|
+
if (escaped) escaped = false;
|
|
1458
|
+
else if (char === "\\") escaped = true;
|
|
1459
|
+
else if (char === '"') inString = false;
|
|
1460
|
+
continue;
|
|
1461
|
+
}
|
|
1462
|
+
if (char === '"') {
|
|
1463
|
+
inString = true;
|
|
1464
|
+
out += char;
|
|
1465
|
+
continue;
|
|
1466
|
+
}
|
|
1467
|
+
if (char === "/" && text2[index + 1] === "/") {
|
|
1468
|
+
while (index < text2.length && text2[index] !== "\n" && text2[index] !== "\r") index += 1;
|
|
1469
|
+
index -= 1;
|
|
1470
|
+
continue;
|
|
1471
|
+
}
|
|
1472
|
+
if (char === "/" && text2[index + 1] === "*") {
|
|
1473
|
+
index += 2;
|
|
1474
|
+
while (index < text2.length && !(text2[index] === "*" && text2[index + 1] === "/")) {
|
|
1475
|
+
if (text2[index] === "\n" || text2[index] === "\r") out += text2[index];
|
|
1476
|
+
index += 1;
|
|
1477
|
+
}
|
|
1478
|
+
index += 1;
|
|
1479
|
+
continue;
|
|
1480
|
+
}
|
|
1481
|
+
out += char;
|
|
1482
|
+
}
|
|
1483
|
+
return out;
|
|
1484
|
+
}
|
|
1485
|
+
function stripTrailingCommas(text2) {
|
|
1486
|
+
let out = "";
|
|
1487
|
+
let inString = false;
|
|
1488
|
+
let escaped = false;
|
|
1489
|
+
for (let index = 0; index < text2.length; index += 1) {
|
|
1490
|
+
const char = text2[index];
|
|
1491
|
+
if (inString) {
|
|
1492
|
+
out += char;
|
|
1493
|
+
if (escaped) escaped = false;
|
|
1494
|
+
else if (char === "\\") escaped = true;
|
|
1495
|
+
else if (char === '"') inString = false;
|
|
1496
|
+
continue;
|
|
1497
|
+
}
|
|
1498
|
+
if (char === '"') {
|
|
1499
|
+
inString = true;
|
|
1500
|
+
out += char;
|
|
1501
|
+
continue;
|
|
1502
|
+
}
|
|
1503
|
+
if (char === ",") {
|
|
1504
|
+
let next = index + 1;
|
|
1505
|
+
while (next < text2.length && /\s/.test(text2[next])) next += 1;
|
|
1506
|
+
if (text2[next] === "}" || text2[next] === "]") continue;
|
|
1507
|
+
}
|
|
1508
|
+
out += char;
|
|
1509
|
+
}
|
|
1510
|
+
return out;
|
|
1511
|
+
}
|
|
1512
|
+
function summarizeJson(content) {
|
|
1513
|
+
try {
|
|
1514
|
+
const parsed = JSON.parse(stripJsonComments(content));
|
|
1515
|
+
if (Array.isArray(parsed)) {
|
|
1516
|
+
const sample = parsed[0];
|
|
1517
|
+
const sampleKeys = sample && typeof sample === "object" && !Array.isArray(sample) ? Object.keys(sample).slice(0, 10).sort() : [];
|
|
1518
|
+
return {
|
|
1519
|
+
shape: "array",
|
|
1520
|
+
itemCount: parsed.length,
|
|
1521
|
+
keys: [],
|
|
1522
|
+
sampleKeys
|
|
1523
|
+
};
|
|
1524
|
+
}
|
|
1525
|
+
if (parsed && typeof parsed === "object") {
|
|
1526
|
+
const keys = Object.keys(parsed).sort();
|
|
1527
|
+
return {
|
|
1528
|
+
shape: "object",
|
|
1529
|
+
itemCount: keys.length,
|
|
1530
|
+
keys: keys.slice(0, 30),
|
|
1531
|
+
sampleKeys: []
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
1534
|
+
return {
|
|
1535
|
+
shape: typeof parsed,
|
|
1536
|
+
itemCount: null,
|
|
1537
|
+
keys: [],
|
|
1538
|
+
sampleKeys: []
|
|
1539
|
+
};
|
|
1540
|
+
} catch {
|
|
1541
|
+
return {
|
|
1542
|
+
shape: "invalid-json",
|
|
1543
|
+
itemCount: null,
|
|
1544
|
+
keys: [],
|
|
1545
|
+
sampleKeys: []
|
|
1546
|
+
};
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
function frontMatterEnd(lines) {
|
|
1550
|
+
if (lines.length < 3 || lines[0].trim() !== "---") return 0;
|
|
1551
|
+
for (let index = 1; index < lines.length; index += 1) {
|
|
1552
|
+
const line = lines[index];
|
|
1553
|
+
const candidate = line.trim();
|
|
1554
|
+
if (candidate === "---" || candidate === "...") {
|
|
1555
|
+
return index > 1 ? index : 0;
|
|
1556
|
+
}
|
|
1557
|
+
if (candidate === "") continue;
|
|
1558
|
+
if (/^#(\s|$)/.test(candidate)) continue;
|
|
1559
|
+
if (/^(`{3,}|~{3,})/.test(candidate)) return 0;
|
|
1560
|
+
if (/^#{1,6}\s+\S/.test(candidate)) return 0;
|
|
1561
|
+
const isYamlMapping = /^[A-Za-z0-9_.-]+\s*:(\s|$)/.test(candidate);
|
|
1562
|
+
const isListItem = /^-(?:(\s|$))/.test(candidate);
|
|
1563
|
+
const isIndented = /^[ \t]/.test(line);
|
|
1564
|
+
if (!isYamlMapping && !isListItem && !isIndented) return 0;
|
|
1565
|
+
}
|
|
1566
|
+
return 0;
|
|
1567
|
+
}
|
|
1568
|
+
function summarizeMarkdown(content) {
|
|
1569
|
+
const headings = [];
|
|
1570
|
+
const frontmatterKeys = [];
|
|
1571
|
+
const lines = content.replace(/^\uFEFF/, "").split(/\r\n|\n|\r/);
|
|
1572
|
+
let inFrontmatter = false;
|
|
1573
|
+
let fence = null;
|
|
1574
|
+
const frontMatterEndLine = frontMatterEnd(lines);
|
|
1575
|
+
for (let index = 0; index < lines.length; index++) {
|
|
1576
|
+
const line = lines[index];
|
|
1577
|
+
if (index === 0 && frontMatterEndLine > 0) {
|
|
1578
|
+
inFrontmatter = true;
|
|
1579
|
+
continue;
|
|
1580
|
+
}
|
|
1211
1581
|
if (inFrontmatter) {
|
|
1212
|
-
|
|
1582
|
+
const trimmedFrontmatter = line.trim();
|
|
1583
|
+
if (trimmedFrontmatter === "---" || trimmedFrontmatter === "...") {
|
|
1213
1584
|
inFrontmatter = false;
|
|
1214
1585
|
continue;
|
|
1215
1586
|
}
|
|
1216
|
-
const match = /^([
|
|
1587
|
+
const match = /^([A-Za-z0-9_.-]+)\s*:/.exec(line);
|
|
1217
1588
|
if (match) frontmatterKeys.push(match[1]);
|
|
1218
1589
|
continue;
|
|
1219
1590
|
}
|
|
@@ -1246,24 +1617,39 @@ function hasCollapsibleBody(node) {
|
|
|
1246
1617
|
return body !== void 0 && ts__default.default.isBlock(body);
|
|
1247
1618
|
}
|
|
1248
1619
|
function isTsxFileName(fileName) {
|
|
1249
|
-
|
|
1620
|
+
const lower = fileName.toLowerCase();
|
|
1621
|
+
return lower.endsWith(".tsx") || lower.endsWith(".jsx");
|
|
1622
|
+
}
|
|
1623
|
+
function scriptKindFor(fileName) {
|
|
1624
|
+
const lower = fileName.toLowerCase();
|
|
1625
|
+
if (lower.endsWith(".tsx")) return ts__default.default.ScriptKind.TSX;
|
|
1626
|
+
if (lower.endsWith(".jsx")) return ts__default.default.ScriptKind.JSX;
|
|
1627
|
+
if (lower.endsWith(".js") || lower.endsWith(".mjs") || lower.endsWith(".cjs")) {
|
|
1628
|
+
return ts__default.default.ScriptKind.JS;
|
|
1629
|
+
}
|
|
1630
|
+
return ts__default.default.ScriptKind.TS;
|
|
1250
1631
|
}
|
|
1251
1632
|
function skeletonizeTypeScript(source, fileName = "file.ts") {
|
|
1252
|
-
const scriptKind = isTsxFileName(fileName) ? ts__default.default.ScriptKind.TSX : ts__default.default.ScriptKind.TS;
|
|
1253
1633
|
const sourceFile = ts__default.default.createSourceFile(
|
|
1254
1634
|
fileName,
|
|
1255
1635
|
source,
|
|
1256
1636
|
ts__default.default.ScriptTarget.Latest,
|
|
1257
|
-
|
|
1258
|
-
|
|
1637
|
+
// `setParentNodes: false`. Nothing here reads `.parent` — the one `getStart` call is given the
|
|
1638
|
+
// source file explicitly, which is the reason that argument exists. Asking for parent pointers
|
|
1639
|
+
// makes the parser run a second full pass over every node in the file to set them, on top of
|
|
1640
|
+
// the summary parse this same file already pays for.
|
|
1641
|
+
false,
|
|
1642
|
+
scriptKindFor(fileName)
|
|
1259
1643
|
);
|
|
1260
1644
|
const spans = [];
|
|
1261
1645
|
const visit = (node) => {
|
|
1262
1646
|
if ((ts__default.default.isFunctionDeclaration(node) || ts__default.default.isMethodDeclaration(node) || ts__default.default.isConstructorDeclaration(node) || ts__default.default.isGetAccessorDeclaration(node) || ts__default.default.isSetAccessorDeclaration(node) || ts__default.default.isFunctionExpression(node) || ts__default.default.isArrowFunction(node)) && hasCollapsibleBody(node)) {
|
|
1263
1647
|
const body = node.body;
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1648
|
+
if (body.statements.length > 0) {
|
|
1649
|
+
const start = body.getStart(sourceFile);
|
|
1650
|
+
const end = body.getEnd();
|
|
1651
|
+
if (end > start) spans.push({ start, end });
|
|
1652
|
+
}
|
|
1267
1653
|
}
|
|
1268
1654
|
ts__default.default.forEachChild(node, visit);
|
|
1269
1655
|
};
|
|
@@ -1275,16 +1661,21 @@ function skeletonizeTypeScript(source, fileName = "file.ts") {
|
|
|
1275
1661
|
if (last && span.start < last.end) continue;
|
|
1276
1662
|
outermost.push(span);
|
|
1277
1663
|
}
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1664
|
+
const chunks = [];
|
|
1665
|
+
let cursor = 0;
|
|
1666
|
+
for (const span of outermost) {
|
|
1667
|
+
chunks.push(source.slice(cursor, span.start), "{ /* ... */ }");
|
|
1668
|
+
cursor = span.end;
|
|
1282
1669
|
}
|
|
1283
|
-
|
|
1670
|
+
chunks.push(source.slice(cursor));
|
|
1671
|
+
return { text: chunks.join(""), collapsed: outermost.length };
|
|
1284
1672
|
}
|
|
1285
1673
|
function isSkeletonizable(filePath) {
|
|
1286
1674
|
const lower = filePath.toLowerCase();
|
|
1287
|
-
|
|
1675
|
+
if (lower.endsWith(".d.ts") || lower.endsWith(".d.mts") || lower.endsWith(".d.cts")) {
|
|
1676
|
+
return false;
|
|
1677
|
+
}
|
|
1678
|
+
return lower.endsWith(".ts") || lower.endsWith(".tsx") || lower.endsWith(".mts") || lower.endsWith(".cts") || lower.endsWith(".js") || lower.endsWith(".jsx") || lower.endsWith(".mjs") || lower.endsWith(".cjs");
|
|
1288
1679
|
}
|
|
1289
1680
|
var MANIFEST_FILES = [
|
|
1290
1681
|
"package.json",
|
|
@@ -2522,9 +2913,18 @@ var DEFAULT_SECRET_PATTERNS = [
|
|
|
2522
2913
|
// The unquoted twin for YAML, docker-compose and `.env`: those formats never
|
|
2523
2914
|
// quote their values, so the rules above cannot see them. `valueGroup` keeps
|
|
2524
2915
|
// `POSTGRES_PASSWORD:` in the output.
|
|
2916
|
+
//
|
|
2917
|
+
// This is the one unquoted rule without `NOT_AFTER_QUOTE`, and the lookbehind is a
|
|
2918
|
+
// string-literal guard rather than a precision rule: a credential assignment *inside* a
|
|
2919
|
+
// literal — `const dsn = "password=hunter2;host=db"`, `curl -d "password=hunter2"` — was
|
|
2920
|
+
// rejected before it could be considered, and a DSN is a credential in a string. What made the
|
|
2921
|
+
// lookbehind redundant is the shape of `UNQUOTED_LITERAL`: a value has to start with a word
|
|
2922
|
+
// character, so `["awsSecret:", "apiKey:"]` — the list of key names the lookbehind was added
|
|
2923
|
+
// for — cannot match here anyway. `redact.ts` still refuses a match whose separator is inside
|
|
2924
|
+
// the literal, which is the case that list actually is.
|
|
2525
2925
|
rule: "credential-assignment-unquoted",
|
|
2526
2926
|
pattern: new RegExp(
|
|
2527
|
-
`${
|
|
2927
|
+
`${UNQUOTED_STRONG_NAME}\\s{0,8}${ASSIGN}\\s{0,8}${NOT_A_LITERAL_VALUE}${UNQUOTED_LITERAL}`,
|
|
2528
2928
|
"i"
|
|
2529
2929
|
),
|
|
2530
2930
|
valueGroup: 1
|
|
@@ -2559,6 +2959,27 @@ var DEFAULT_SECRET_PATTERNS = [
|
|
|
2559
2959
|
pattern: /(?<!["'])(?:api[_-]?key|secret[_-]?key|auth[_-]?token|client[_-]?secret|access[_-]?token|refresh[_-]?token|private[_-]?key|password|passwd|pwd)["']?\s*(?<![=!<>])[:=](?!=)\s*["']([^"'\s]{8,256})["']/i,
|
|
2560
2960
|
valueGroup: 1
|
|
2561
2961
|
},
|
|
2962
|
+
{
|
|
2963
|
+
// `${VAR:-fallback}` — a default value in a shell script, a `.env`, docker-compose or a
|
|
2964
|
+
// GitHub Actions workflow.
|
|
2965
|
+
//
|
|
2966
|
+
// This shape defeated every rule above it, and each refusal was individually reasonable:
|
|
2967
|
+
// the quoted rules need a quote after the separator, and the unquoted ones need the value to
|
|
2968
|
+
// begin with a word character — a fallback begins with `$`. So
|
|
2969
|
+
// `POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-MyProdPassword123!}` was emitted whole, `.env` files
|
|
2970
|
+
// and compose files included, which is where that idiom is the norm.
|
|
2971
|
+
//
|
|
2972
|
+
// The **fallback** is what gets masked, not the whole expansion: the variable name is the
|
|
2973
|
+
// readable context, exactly as `POSTGRES_PASSWORD:` is kept by the rule above. A single
|
|
2974
|
+
// interpolation with no operator (`${DB_PASSWORD}`) still does not match — there is nothing
|
|
2975
|
+
// between the braces to mask, and `isTemplateInterpolation` keeps such a value readable.
|
|
2976
|
+
rule: "interpolation-fallback-secret",
|
|
2977
|
+
pattern: new RegExp(
|
|
2978
|
+
`(?:["']${STRONG_CREDENTIAL_NAME}["']|${NOT_AFTER_QUOTE}${STRONG_CREDENTIAL_NAME})\\s{0,8}${ASSIGN}\\s{0,8}["'\`]?\\$\\{[A-Za-z_][A-Za-z0-9_]*:?[-=?+]([^}"'\`\\r\\n]{2,512})\\}["'\`]?`,
|
|
2979
|
+
"i"
|
|
2980
|
+
),
|
|
2981
|
+
valueGroup: 1
|
|
2982
|
+
},
|
|
2562
2983
|
{
|
|
2563
2984
|
// Covers `.env` / shell assignment syntax, which never quotes its values and
|
|
2564
2985
|
// was therefore invisible to the rule above. Anchored to the line start and
|
|
@@ -2586,8 +3007,8 @@ var DEFAULT_SECRET_PATTERNS = [
|
|
|
2586
3007
|
valueGroup: 1
|
|
2587
3008
|
}
|
|
2588
3009
|
];
|
|
2589
|
-
var PEM_BLOCK_PATTERN = /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----[\s\S]*?-----END (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----/g;
|
|
2590
|
-
var PEM_UNTERMINATED_PATTERN = /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----(?:\r
|
|
3010
|
+
var PEM_BLOCK_PATTERN = /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----(?:(?!-----BEGIN)[\s\S])*?-----END (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----/g;
|
|
3011
|
+
var PEM_UNTERMINATED_PATTERN = /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----(?:\r\n|\n|\r)(?:(?:[ \t]*(?:Proc-Type|DEK-Info|Comment|Version):[^\r\n]*|[ \t]*)(?:\r\n|\n|\r))*(?:[ \t]*[A-Za-z0-9+/=]{8,}[ \t]*(?:\r\n|\n|\r|$))+/g;
|
|
2591
3012
|
|
|
2592
3013
|
// src/security/redact.ts
|
|
2593
3014
|
var REDACTION_MARKER = "[REDACTED: potential secret-like value omitted from snapshot]";
|
|
@@ -2681,6 +3102,20 @@ function toGlobal(rule) {
|
|
|
2681
3102
|
return new RegExp(rule.pattern.source, [...flags].join(""));
|
|
2682
3103
|
}
|
|
2683
3104
|
var LINE_BREAK = /(\r\n|\n|\r)/;
|
|
3105
|
+
var LINE_TERMINATOR = /\r\n|\n|\r/g;
|
|
3106
|
+
var COMPILED_RULES = /* @__PURE__ */ new WeakMap();
|
|
3107
|
+
function compiledRuleFor(rule) {
|
|
3108
|
+
let compiled = COMPILED_RULES.get(rule);
|
|
3109
|
+
if (compiled === void 0) {
|
|
3110
|
+
compiled = {
|
|
3111
|
+
rule: rule.rule,
|
|
3112
|
+
pattern: toGlobal(rule),
|
|
3113
|
+
...rule.valueGroup === void 0 ? {} : { valueGroup: rule.valueGroup }
|
|
3114
|
+
};
|
|
3115
|
+
COMPILED_RULES.set(rule, compiled);
|
|
3116
|
+
}
|
|
3117
|
+
return compiled;
|
|
3118
|
+
}
|
|
2684
3119
|
function scanAndRedactSecrets(content, rules, filePath) {
|
|
2685
3120
|
const findings = [];
|
|
2686
3121
|
let redacted = content;
|
|
@@ -2692,18 +3127,15 @@ function scanAndRedactSecrets(content, rules, filePath) {
|
|
|
2692
3127
|
blockPattern.lastIndex += 1;
|
|
2693
3128
|
continue;
|
|
2694
3129
|
}
|
|
2695
|
-
const
|
|
2696
|
-
const
|
|
3130
|
+
const before = redacted.slice(0, match.index);
|
|
3131
|
+
const line = (before.match(LINE_TERMINATOR) ?? []).length + 1;
|
|
3132
|
+
const newlines = (match[0].match(LINE_TERMINATOR) ?? []).length;
|
|
2697
3133
|
findings.push({ rules: ["private-key-block"], line, path: filePath });
|
|
2698
|
-
redacted =
|
|
3134
|
+
redacted = before + PEM_REDACTION_MARKER + "\n".repeat(newlines) + redacted.slice(match.index + match[0].length);
|
|
2699
3135
|
blockPattern.lastIndex = match.index + PEM_REDACTION_MARKER.length + newlines;
|
|
2700
3136
|
}
|
|
2701
3137
|
}
|
|
2702
|
-
const compiled = rules.map(
|
|
2703
|
-
rule: rule.rule,
|
|
2704
|
-
pattern: toGlobal(rule),
|
|
2705
|
-
...rule.valueGroup === void 0 ? {} : { valueGroup: rule.valueGroup }
|
|
2706
|
-
}));
|
|
3138
|
+
const compiled = rules.map(compiledRuleFor);
|
|
2707
3139
|
const segments = redacted.split(LINE_BREAK);
|
|
2708
3140
|
let lineNumber = 1;
|
|
2709
3141
|
for (let index = 0; index < segments.length; index += 2) {
|
|
@@ -2721,8 +3153,10 @@ function scanAndRedactSecrets(content, rules, filePath) {
|
|
|
2721
3153
|
}
|
|
2722
3154
|
const group = valueGroup === void 0 ? void 0 : match.indices?.[valueGroup];
|
|
2723
3155
|
if (group && isSelfNamingConstant(line, group[0], group[1])) continue;
|
|
2724
|
-
if (valueGroup !== void 0 && startsInsideStringLiteral(line, match.index))
|
|
2725
|
-
|
|
3156
|
+
if (valueGroup !== void 0 && startsInsideStringLiteral(line, match.index, group?.[0])) {
|
|
3157
|
+
continue;
|
|
3158
|
+
}
|
|
3159
|
+
if (group && isUnquotedValue(line, group[0]) && isDeclaredBinding(line, match.index, filePath)) {
|
|
2726
3160
|
continue;
|
|
2727
3161
|
}
|
|
2728
3162
|
if (group && isTemplateInterpolation(line, group[0], group[1])) continue;
|
|
@@ -2755,14 +3189,20 @@ function expandOverToken(line, start, end) {
|
|
|
2755
3189
|
while (right < line.length && TOKEN_CHARS_RIGHT.test(line[right])) right += 1;
|
|
2756
3190
|
return { start: left, end: right };
|
|
2757
3191
|
}
|
|
2758
|
-
function startsInsideStringLiteral(line, matchIndex) {
|
|
3192
|
+
function startsInsideStringLiteral(line, matchIndex, valueStart) {
|
|
2759
3193
|
let runStart = matchIndex;
|
|
2760
3194
|
while (runStart > 0 && /[A-Za-z0-9_-]/.test(line[runStart - 1])) runStart -= 1;
|
|
2761
3195
|
const before = runStart > 0 ? line[runStart - 1] : "";
|
|
2762
|
-
|
|
3196
|
+
if (before !== '"' && before !== "'" && before !== "`") return false;
|
|
3197
|
+
if (valueStart === void 0) return true;
|
|
3198
|
+
return /["'`]/.test(line.slice(matchIndex, valueStart));
|
|
2763
3199
|
}
|
|
2764
3200
|
function isTemplateInterpolation(line, start, end) {
|
|
2765
|
-
|
|
3201
|
+
if (line[start] !== "$" || line[start + 1] !== "{" || line[end - 1] !== "}") {
|
|
3202
|
+
return false;
|
|
3203
|
+
}
|
|
3204
|
+
const inner = line.slice(start + 2, end - 1).trim();
|
|
3205
|
+
return /^[A-Za-z_$][A-Za-z0-9_$.()[\]]*$/.test(inner);
|
|
2766
3206
|
}
|
|
2767
3207
|
function isSelfNamingConstant(line, start, end) {
|
|
2768
3208
|
const value = line.slice(start, end);
|
|
@@ -2787,9 +3227,19 @@ function isUnquotedValue(line, start) {
|
|
|
2787
3227
|
const before = start > 0 ? line[start - 1] : "";
|
|
2788
3228
|
return before !== '"' && before !== "'" && before !== "`";
|
|
2789
3229
|
}
|
|
2790
|
-
var DECLARATION_KEYWORDS = /\b(?:const|let|var|readonly|public|private|protected|static|
|
|
3230
|
+
var DECLARATION_KEYWORDS = /\b(?:const|let|var|readonly|public|private|protected|static|using)\s+$/;
|
|
3231
|
+
var ASSIGNMENT_FILE = /\.(?:sh|bash|zsh|ksh|fish|env|ya?ml|toml|ini|conf|cfg|properties|tf|tfvars|service)$/i;
|
|
3232
|
+
var ASSIGNMENT_BASENAME = /^(?:\.env(?:\.[^/]*)?|\.envrc|\.profile|\.bashrc|\.zshrc|\.bash_profile)$/i;
|
|
3233
|
+
function isAssignmentFile(filePath) {
|
|
3234
|
+
if (filePath === void 0) return false;
|
|
3235
|
+
const normalised = filePath.replace(/\\/g, "/");
|
|
3236
|
+
if (ASSIGNMENT_FILE.test(normalised)) return true;
|
|
3237
|
+
const base = normalised.slice(normalised.lastIndexOf("/") + 1);
|
|
3238
|
+
return ASSIGNMENT_BASENAME.test(base);
|
|
3239
|
+
}
|
|
2791
3240
|
var DECLARATION_WINDOW = 40;
|
|
2792
|
-
function isDeclaredBinding(line, matchIndex) {
|
|
3241
|
+
function isDeclaredBinding(line, matchIndex, filePath) {
|
|
3242
|
+
if (isAssignmentFile(filePath)) return false;
|
|
2793
3243
|
let runStart = matchIndex;
|
|
2794
3244
|
while (runStart > 0 && /[A-Za-z0-9_$]/.test(line[runStart - 1])) runStart -= 1;
|
|
2795
3245
|
const from = Math.max(0, runStart - DECLARATION_WINDOW);
|
|
@@ -2831,6 +3281,15 @@ function readLinkTarget(linkPath, fallback) {
|
|
|
2831
3281
|
return fallback;
|
|
2832
3282
|
}
|
|
2833
3283
|
}
|
|
3284
|
+
function brokenLinkRecord(linkPath, relativePath, rootReal) {
|
|
3285
|
+
const rawTarget = readLinkTarget(linkPath, linkPath);
|
|
3286
|
+
const resolvedFallback = path16__namespace.resolve(path16__namespace.dirname(linkPath), rawTarget);
|
|
3287
|
+
return isInside(rootReal, resolvedFallback) ? { path: relativePath, reason: "read-error", detail: "broken symlink" } : {
|
|
3288
|
+
path: relativePath,
|
|
3289
|
+
reason: "outside-root",
|
|
3290
|
+
detail: `link target resolves outside the scanned root (target: ${rawTarget}); not followed`
|
|
3291
|
+
};
|
|
3292
|
+
}
|
|
2834
3293
|
function isInside(root, candidate) {
|
|
2835
3294
|
const relative8 = path16__namespace.relative(root, candidate);
|
|
2836
3295
|
return relative8 === "" || !relative8.startsWith(`..${path16__namespace.sep}`) && relative8 !== ".." && !path16__namespace.isAbsolute(relative8);
|
|
@@ -2920,6 +3379,13 @@ function isSensitivePath(relativePath) {
|
|
|
2920
3379
|
return matchesSensitiveList(posixPath, base);
|
|
2921
3380
|
}
|
|
2922
3381
|
var GLOBAL_PATTERNS = /* @__PURE__ */ new WeakMap();
|
|
3382
|
+
var GLOB_REGEX_CACHE = /* @__PURE__ */ new Map();
|
|
3383
|
+
var GITIGNORE_REGEX_CACHE = /* @__PURE__ */ new Map();
|
|
3384
|
+
var REGEX_CACHE_LIMIT = 4096;
|
|
3385
|
+
function rememberCompiled(cache, key, value) {
|
|
3386
|
+
if (cache.size >= REGEX_CACHE_LIMIT) cache.clear();
|
|
3387
|
+
cache.set(key, value);
|
|
3388
|
+
}
|
|
2923
3389
|
function globalPattern(pattern) {
|
|
2924
3390
|
let compiled = GLOBAL_PATTERNS.get(pattern);
|
|
2925
3391
|
if (compiled === void 0) {
|
|
@@ -2930,8 +3396,8 @@ function globalPattern(pattern) {
|
|
|
2930
3396
|
}
|
|
2931
3397
|
var DEFAULT_PATH_RULES = [...DEFAULT_SECRET_PATTERNS];
|
|
2932
3398
|
function pathScanRules(extraRules) {
|
|
2933
|
-
if (extraRules === void 0
|
|
2934
|
-
return
|
|
3399
|
+
if (extraRules === void 0) return DEFAULT_PATH_RULES;
|
|
3400
|
+
return extraRules;
|
|
2935
3401
|
}
|
|
2936
3402
|
function findSecretInPath(relativePath, extraRules) {
|
|
2937
3403
|
const posixPath = relativePath.replace(/\\/g, "/");
|
|
@@ -2980,11 +3446,22 @@ function maskSkipPaths(records, extraRules) {
|
|
|
2980
3446
|
});
|
|
2981
3447
|
}
|
|
2982
3448
|
function globToRegExp(glob) {
|
|
3449
|
+
const cached = GLOB_REGEX_CACHE.get(glob);
|
|
3450
|
+
if (cached !== void 0) return cached;
|
|
2983
3451
|
const normalized = glob.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+$/, "");
|
|
2984
3452
|
const anchored = normalized.startsWith("/");
|
|
2985
3453
|
const body = anchored ? normalized.slice(1) : normalized;
|
|
2986
3454
|
const prefix = anchored || body.includes("/") ? "^" : "(?:^|.*/)";
|
|
2987
|
-
|
|
3455
|
+
let compiled;
|
|
3456
|
+
try {
|
|
3457
|
+
compiled = new RegExp(`${prefix}${globBodyToRegExp(body, true)}(?:/.*)?$`);
|
|
3458
|
+
} catch (error) {
|
|
3459
|
+
throw new Error(
|
|
3460
|
+
`"${glob}" is not a valid glob: ${error.message}. A character class must be ascending, as in "[a-z]".`
|
|
3461
|
+
);
|
|
3462
|
+
}
|
|
3463
|
+
rememberCompiled(GLOB_REGEX_CACHE, glob, compiled);
|
|
3464
|
+
return compiled;
|
|
2988
3465
|
}
|
|
2989
3466
|
function globBodyToRegExp(body, allowBraces) {
|
|
2990
3467
|
let out = "";
|
|
@@ -3096,6 +3573,14 @@ function escapeRegExpChar(character) {
|
|
|
3096
3573
|
return /[\\^$.*+?()[\]{}|/]/.test(character) ? `\\${character}` : character;
|
|
3097
3574
|
}
|
|
3098
3575
|
function matchGitignorePattern(target, pattern) {
|
|
3576
|
+
let compiled = GITIGNORE_REGEX_CACHE.get(pattern);
|
|
3577
|
+
if (compiled === void 0) {
|
|
3578
|
+
compiled = compileGitignorePattern(pattern);
|
|
3579
|
+
rememberCompiled(GITIGNORE_REGEX_CACHE, pattern, compiled);
|
|
3580
|
+
}
|
|
3581
|
+
return compiled.test(target);
|
|
3582
|
+
}
|
|
3583
|
+
function compileGitignorePattern(pattern) {
|
|
3099
3584
|
const anchored = pattern.startsWith("/");
|
|
3100
3585
|
const body = anchored ? pattern.slice(1) : pattern;
|
|
3101
3586
|
let source = "";
|
|
@@ -3144,7 +3629,11 @@ function matchGitignorePattern(target, pattern) {
|
|
|
3144
3629
|
source += escapeRegExpChar(character);
|
|
3145
3630
|
}
|
|
3146
3631
|
const prefix = anchored || body.includes("/") ? "^" : "(?:^|.*/)";
|
|
3147
|
-
|
|
3632
|
+
try {
|
|
3633
|
+
return new RegExp(`${prefix}${source}(?:/.*)?$`);
|
|
3634
|
+
} catch {
|
|
3635
|
+
return /$^/;
|
|
3636
|
+
}
|
|
3148
3637
|
}
|
|
3149
3638
|
function matchesGitignoreRule(rule, scoped, isDirectory) {
|
|
3150
3639
|
if (!rule.dirOnly) return matchGitignorePattern(scoped, rule.pattern);
|
|
@@ -3174,14 +3663,31 @@ function gitignoreVerdict(layers, relativePath, isDirectory) {
|
|
|
3174
3663
|
}
|
|
3175
3664
|
return verdict;
|
|
3176
3665
|
}
|
|
3177
|
-
|
|
3666
|
+
var DEFAULT_TOOL_DIR_NAMES = ["snapshot"];
|
|
3667
|
+
function toolDirNamesFor(toolOwnedPaths) {
|
|
3668
|
+
const names = new Set(DEFAULT_TOOL_DIR_NAMES);
|
|
3669
|
+
for (const entry of toolOwnedPaths) {
|
|
3670
|
+
const base = entry.replace(/\\/g, "/").replace(/\/+$/, "").split("/").pop();
|
|
3671
|
+
if (base !== void 0 && base.length > 0) names.add(base);
|
|
3672
|
+
}
|
|
3673
|
+
return [...names];
|
|
3674
|
+
}
|
|
3675
|
+
function isToolLeftoverName(name, dirNames) {
|
|
3676
|
+
for (const dirName of dirNames) {
|
|
3677
|
+
if (name.startsWith(`${dirName}.backup-`) || name.startsWith(`${dirName}.partial-`)) {
|
|
3678
|
+
return true;
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3681
|
+
return false;
|
|
3682
|
+
}
|
|
3683
|
+
function defaultIgnoreReason(relativePath, toolDirNames = DEFAULT_TOOL_DIR_NAMES) {
|
|
3178
3684
|
const posixPath = relativePath.replace(/\\/g, "/");
|
|
3179
3685
|
const parts = posixPath.split("/");
|
|
3180
3686
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
3181
3687
|
if (DEFAULT_IGNORED_DIRS.includes(parts[i])) {
|
|
3182
3688
|
return `built-in ignore: directory "${parts[i]}"`;
|
|
3183
3689
|
}
|
|
3184
|
-
if (parts[i]
|
|
3690
|
+
if (isToolLeftoverName(parts[i], toolDirNames)) {
|
|
3185
3691
|
return `built-in ignore: leftover snapshot backup "${parts[i]}"`;
|
|
3186
3692
|
}
|
|
3187
3693
|
}
|
|
@@ -3189,6 +3695,9 @@ function defaultIgnoreReason(relativePath) {
|
|
|
3189
3695
|
if (DEFAULT_IGNORED_FILES.includes(base)) {
|
|
3190
3696
|
return `built-in ignore: filename "${base}"`;
|
|
3191
3697
|
}
|
|
3698
|
+
if (isToolLeftoverName(base, toolDirNames)) {
|
|
3699
|
+
return `built-in ignore: leftover snapshot backup "${base}"`;
|
|
3700
|
+
}
|
|
3192
3701
|
return null;
|
|
3193
3702
|
}
|
|
3194
3703
|
function listIgnoredPaths(cwd) {
|
|
@@ -3257,41 +3766,72 @@ function dropLinkedDirectories(cwd, candidates, skipped) {
|
|
|
3257
3766
|
}
|
|
3258
3767
|
const reported = /* @__PURE__ */ new Set();
|
|
3259
3768
|
const kept = [];
|
|
3769
|
+
const report = (linkedPath, record) => {
|
|
3770
|
+
if (reported.has(linkedPath)) return;
|
|
3771
|
+
reported.add(linkedPath);
|
|
3772
|
+
skipped.push(record);
|
|
3773
|
+
};
|
|
3260
3774
|
for (const candidate of candidates) {
|
|
3261
3775
|
const parts = candidate.split("/");
|
|
3262
|
-
let
|
|
3776
|
+
let linkedPath = null;
|
|
3263
3777
|
for (let depth = 1; depth <= parts.length; depth += 1) {
|
|
3264
3778
|
const prefix = parts.slice(0, depth).join("/");
|
|
3265
3779
|
if (isLink(path16__namespace.join(cwd, prefix))) {
|
|
3266
|
-
|
|
3780
|
+
linkedPath = prefix;
|
|
3267
3781
|
break;
|
|
3268
3782
|
}
|
|
3269
3783
|
}
|
|
3270
|
-
if (
|
|
3784
|
+
if (linkedPath === null) {
|
|
3271
3785
|
kept.push(candidate);
|
|
3272
3786
|
continue;
|
|
3273
3787
|
}
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
let targetReal = absolute;
|
|
3788
|
+
const absolute = path16__namespace.join(cwd, linkedPath);
|
|
3789
|
+
const rawTarget = readLinkTarget(absolute, absolute);
|
|
3790
|
+
let targetReal = null;
|
|
3278
3791
|
try {
|
|
3279
3792
|
targetReal = fs14__namespace.realpathSync(absolute);
|
|
3280
3793
|
} catch {
|
|
3794
|
+
targetReal = null;
|
|
3281
3795
|
}
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
} : {
|
|
3290
|
-
path: linkedDir,
|
|
3796
|
+
if (targetReal === null) {
|
|
3797
|
+
report(linkedPath, brokenLinkRecord(absolute, linkedPath, rootReal));
|
|
3798
|
+
continue;
|
|
3799
|
+
}
|
|
3800
|
+
if (!isInside(rootReal, targetReal)) {
|
|
3801
|
+
report(linkedPath, {
|
|
3802
|
+
path: linkedPath,
|
|
3291
3803
|
reason: "outside-root",
|
|
3292
|
-
detail: `link target resolves outside the scanned root (target: ${
|
|
3293
|
-
}
|
|
3294
|
-
|
|
3804
|
+
detail: `link target resolves outside the scanned root (target: ${rawTarget}); not followed`
|
|
3805
|
+
});
|
|
3806
|
+
continue;
|
|
3807
|
+
}
|
|
3808
|
+
let stats = null;
|
|
3809
|
+
try {
|
|
3810
|
+
stats = fs14__namespace.statSync(absolute);
|
|
3811
|
+
} catch {
|
|
3812
|
+
stats = null;
|
|
3813
|
+
}
|
|
3814
|
+
if (stats === null) {
|
|
3815
|
+
report(linkedPath, { path: linkedPath, reason: "read-error", detail: "unreadable symlink" });
|
|
3816
|
+
continue;
|
|
3817
|
+
}
|
|
3818
|
+
if (linkedPath !== candidate || stats.isDirectory()) {
|
|
3819
|
+
report(linkedPath, {
|
|
3820
|
+
path: linkedPath,
|
|
3821
|
+
reason: "symlink",
|
|
3822
|
+
detail: `directory link not followed (target: ${rawTarget})`
|
|
3823
|
+
});
|
|
3824
|
+
continue;
|
|
3825
|
+
}
|
|
3826
|
+
if (!stats.isFile()) {
|
|
3827
|
+
report(linkedPath, {
|
|
3828
|
+
path: linkedPath,
|
|
3829
|
+
reason: "symlink",
|
|
3830
|
+
detail: `link to neither a file nor a directory (target: ${rawTarget})`
|
|
3831
|
+
});
|
|
3832
|
+
continue;
|
|
3833
|
+
}
|
|
3834
|
+
kept.push(candidate);
|
|
3295
3835
|
}
|
|
3296
3836
|
return kept;
|
|
3297
3837
|
}
|
|
@@ -3338,6 +3878,7 @@ function collectFiles(cwd, options = {}) {
|
|
|
3338
3878
|
(options.toolOwnedPaths ?? []).map((entry) => entry.replace(/\\/g, "/"))
|
|
3339
3879
|
);
|
|
3340
3880
|
const isToolOwned = makeToolOwnedDecider(cwd, [...toolOwnedPaths]);
|
|
3881
|
+
const toolDirNames = toolDirNamesFor(toolOwnedPaths);
|
|
3341
3882
|
let candidates;
|
|
3342
3883
|
const skipped = [];
|
|
3343
3884
|
const discovery = options.discovery ?? "auto";
|
|
@@ -3366,7 +3907,7 @@ function collectFiles(cwd, options = {}) {
|
|
|
3366
3907
|
}
|
|
3367
3908
|
const files = candidates.filter((file) => {
|
|
3368
3909
|
if (isToolOwned(file)) return false;
|
|
3369
|
-
const ignored = defaultIgnoreReason(file);
|
|
3910
|
+
const ignored = defaultIgnoreReason(file, toolDirNames);
|
|
3370
3911
|
if (ignored) {
|
|
3371
3912
|
skipped.push({ path: file, reason: "default-ignore", detail: ignored });
|
|
3372
3913
|
return false;
|
|
@@ -3421,6 +3962,7 @@ function rootRealFor(root) {
|
|
|
3421
3962
|
function walkDirectory(dir, baseDir, rootReal, visited, reservedPaths = /* @__PURE__ */ new Set(), parentGitignoreLayers = []) {
|
|
3422
3963
|
const results = { files: [], skipped: [] };
|
|
3423
3964
|
const stack = [];
|
|
3965
|
+
const toolDirNames = toolDirNamesFor(reservedPaths);
|
|
3424
3966
|
const relativeToBase = (target) => path16__namespace.relative(baseDir, target).replace(/\\/g, "/") || ".";
|
|
3425
3967
|
const enter = (current, layers) => {
|
|
3426
3968
|
if (visited.has(current)) return false;
|
|
@@ -3465,11 +4007,11 @@ function walkDirectory(dir, baseDir, rootReal, visited, reservedPaths = /* @__PU
|
|
|
3465
4007
|
});
|
|
3466
4008
|
continue;
|
|
3467
4009
|
}
|
|
3468
|
-
if (DEFAULT_IGNORED_FILES.includes(entry.name) || entry.name
|
|
4010
|
+
if (DEFAULT_IGNORED_FILES.includes(entry.name) || isToolLeftoverName(entry.name, toolDirNames)) {
|
|
3469
4011
|
results.skipped.push({
|
|
3470
4012
|
path: relPath,
|
|
3471
4013
|
reason: "default-ignore",
|
|
3472
|
-
detail: "built-in ignore: filename"
|
|
4014
|
+
detail: isToolLeftoverName(entry.name, toolDirNames) ? `built-in ignore: leftover snapshot backup "${entry.name}" (not descended)` : "built-in ignore: filename"
|
|
3473
4015
|
});
|
|
3474
4016
|
continue;
|
|
3475
4017
|
}
|
|
@@ -3490,7 +4032,7 @@ function walkDirectory(dir, baseDir, rootReal, visited, reservedPaths = /* @__PU
|
|
|
3490
4032
|
try {
|
|
3491
4033
|
targetReal = fs14__namespace.realpathSync(fullPath);
|
|
3492
4034
|
} catch {
|
|
3493
|
-
results.skipped.push(
|
|
4035
|
+
results.skipped.push(brokenLinkRecord(fullPath, relPath, rootReal));
|
|
3494
4036
|
continue;
|
|
3495
4037
|
}
|
|
3496
4038
|
let stats;
|
|
@@ -3615,15 +4157,19 @@ function normalizeBudgets(budgets, maxPartBytes) {
|
|
|
3615
4157
|
}
|
|
3616
4158
|
function normalizeResolvedConfig(config) {
|
|
3617
4159
|
const discovery = isDiscoveryMode(config.discovery) ? config.discovery : "auto";
|
|
4160
|
+
const monolithWanted = config.includeMonolith === true || config.artifacts?.monolith === true;
|
|
4161
|
+
const artifacts = resolveArtifacts(
|
|
4162
|
+
config.artifacts,
|
|
4163
|
+
void 0,
|
|
4164
|
+
monolithWanted,
|
|
4165
|
+
monolithWanted
|
|
4166
|
+
);
|
|
3618
4167
|
return markResolvedConfig({
|
|
3619
4168
|
...config,
|
|
3620
4169
|
discovery,
|
|
4170
|
+
includeMonolith: artifacts.monolith,
|
|
3621
4171
|
secretRules: compileSecretRules(config.secretRules),
|
|
3622
|
-
artifacts
|
|
3623
|
-
config.artifacts,
|
|
3624
|
-
void 0,
|
|
3625
|
-
config.includeMonolith === true
|
|
3626
|
-
),
|
|
4172
|
+
artifacts,
|
|
3627
4173
|
include: resolveInclude(config.include),
|
|
3628
4174
|
preset: typeof config.preset === "string" ? config.preset : "generic",
|
|
3629
4175
|
presetExclude: [
|
|
@@ -3702,9 +4248,9 @@ function stripJsonComments2(raw) {
|
|
|
3702
4248
|
}
|
|
3703
4249
|
out += char;
|
|
3704
4250
|
}
|
|
3705
|
-
return
|
|
4251
|
+
return stripTrailingCommas2(out);
|
|
3706
4252
|
}
|
|
3707
|
-
function
|
|
4253
|
+
function stripTrailingCommas2(text2) {
|
|
3708
4254
|
let out = "";
|
|
3709
4255
|
let inString = false;
|
|
3710
4256
|
let escaped = false;
|
|
@@ -3832,6 +4378,16 @@ async function importConfigModule(fullPath) {
|
|
|
3832
4378
|
url$1.searchParams.set("codebaseSnapshotLoad", String(moduleConfigLoadToken));
|
|
3833
4379
|
return await import(url$1.href);
|
|
3834
4380
|
}
|
|
4381
|
+
function attachConfigDir(config, filePath) {
|
|
4382
|
+
if (typeof config === "object" && config !== null) {
|
|
4383
|
+
Object.defineProperty(config, CONFIG_DIR_SYMBOL, {
|
|
4384
|
+
value: path16__namespace.dirname(filePath),
|
|
4385
|
+
enumerable: false,
|
|
4386
|
+
configurable: true
|
|
4387
|
+
});
|
|
4388
|
+
}
|
|
4389
|
+
return config;
|
|
4390
|
+
}
|
|
3835
4391
|
async function loadConfigFileDetailed(cwd) {
|
|
3836
4392
|
const warnings = [];
|
|
3837
4393
|
const errors = [];
|
|
@@ -3847,7 +4403,7 @@ async function loadConfigFileDetailed(cwd) {
|
|
|
3847
4403
|
);
|
|
3848
4404
|
}
|
|
3849
4405
|
assertKnownConfigKeys(config, fullPath);
|
|
3850
|
-
return { config, file: fullPath, warnings, errors };
|
|
4406
|
+
return { config: attachConfigDir(config, fullPath), file: fullPath, warnings, errors };
|
|
3851
4407
|
} catch (err) {
|
|
3852
4408
|
const raw = err.message;
|
|
3853
4409
|
const message = raw.includes(fullPath) ? raw : `${fullPath}: ${raw}`;
|
|
@@ -3866,7 +4422,7 @@ async function loadConfigFileDetailed(cwd) {
|
|
|
3866
4422
|
const imported = await importConfigModule(fullPath);
|
|
3867
4423
|
const hasDefault = Object.prototype.hasOwnProperty.call(imported, "default");
|
|
3868
4424
|
const config = readConfigExport(hasDefault ? imported.default : imported, fullPath);
|
|
3869
|
-
return { config, file: fullPath, warnings, errors };
|
|
4425
|
+
return { config: attachConfigDir(config, fullPath), file: fullPath, warnings, errors };
|
|
3870
4426
|
} catch (err) {
|
|
3871
4427
|
const message = `Could not load ${fullPath}: ${err.message}`;
|
|
3872
4428
|
warnings.push(message);
|
|
@@ -3884,7 +4440,12 @@ async function loadConfigFileDetailed(cwd) {
|
|
|
3884
4440
|
snapshotKey,
|
|
3885
4441
|
`${pkgJsonPath} (the "snapshot" key)`
|
|
3886
4442
|
);
|
|
3887
|
-
return {
|
|
4443
|
+
return {
|
|
4444
|
+
config: attachConfigDir(snapshotKey, pkgJsonPath),
|
|
4445
|
+
file: pkgJsonPath,
|
|
4446
|
+
warnings,
|
|
4447
|
+
errors
|
|
4448
|
+
};
|
|
3888
4449
|
}
|
|
3889
4450
|
if (snapshotKey !== void 0) {
|
|
3890
4451
|
const kind = Array.isArray(snapshotKey) ? "array" : typeof snapshotKey;
|
|
@@ -3902,13 +4463,6 @@ async function loadConfigFileDetailed(cwd) {
|
|
|
3902
4463
|
}
|
|
3903
4464
|
async function loadConfigFile(cwd) {
|
|
3904
4465
|
const loaded = await loadConfigFileDetailed(cwd);
|
|
3905
|
-
if (loaded.config && loaded.file && typeof loaded.config === "object") {
|
|
3906
|
-
Object.defineProperty(loaded.config, CONFIG_DIR_SYMBOL, {
|
|
3907
|
-
value: path16__namespace.dirname(loaded.file),
|
|
3908
|
-
enumerable: false,
|
|
3909
|
-
configurable: true
|
|
3910
|
-
});
|
|
3911
|
-
}
|
|
3912
4466
|
if (loaded.errors.length > 0) {
|
|
3913
4467
|
throw new Error(`The config file could not be read: ${loaded.errors.join("; ")}`);
|
|
3914
4468
|
}
|
|
@@ -3937,7 +4491,7 @@ var DEFAULT_ARTIFACTS = {
|
|
|
3937
4491
|
function isArtifactName(value) {
|
|
3938
4492
|
return ARTIFACT_NAMES.includes(value);
|
|
3939
4493
|
}
|
|
3940
|
-
function resolveArtifacts(requested, skipped, includeMonolith, monolithOverride) {
|
|
4494
|
+
function resolveArtifacts(requested, skipped, includeMonolith, monolithOverride, only) {
|
|
3941
4495
|
const artifacts = {
|
|
3942
4496
|
...DEFAULT_ARTIFACTS,
|
|
3943
4497
|
// `includeMonolith` predates this selection and stays the switch for it.
|
|
@@ -3962,6 +4516,21 @@ function resolveArtifacts(requested, skipped, includeMonolith, monolithOverride)
|
|
|
3962
4516
|
if (monolithOverride !== void 0) {
|
|
3963
4517
|
artifacts.monolith = monolithOverride;
|
|
3964
4518
|
}
|
|
4519
|
+
if (only !== void 0 && only.length > 0) {
|
|
4520
|
+
const keep = /* @__PURE__ */ new Set();
|
|
4521
|
+
for (const name of only) {
|
|
4522
|
+
if (!isArtifactName(name)) {
|
|
4523
|
+
throw new Error(
|
|
4524
|
+
`Unknown artifact "${name}". Expected one of: ${ARTIFACT_NAMES.join(", ")}.`
|
|
4525
|
+
);
|
|
4526
|
+
}
|
|
4527
|
+
keep.add(name);
|
|
4528
|
+
}
|
|
4529
|
+
for (const name of ARTIFACT_NAMES) {
|
|
4530
|
+
artifacts[name] = keep.has(name);
|
|
4531
|
+
}
|
|
4532
|
+
return artifacts;
|
|
4533
|
+
}
|
|
3965
4534
|
for (const name of skipped ?? []) {
|
|
3966
4535
|
if (!isArtifactName(name)) {
|
|
3967
4536
|
throw new Error(
|
|
@@ -4262,7 +4831,8 @@ function resolveConfig(userConfig, cliOptions, baseDir) {
|
|
|
4262
4831
|
userConfig?.artifacts,
|
|
4263
4832
|
cliOptions?.skipArtifacts,
|
|
4264
4833
|
includeMonolith,
|
|
4265
|
-
cliMonolith
|
|
4834
|
+
cliMonolith,
|
|
4835
|
+
cliOptions?.onlyArtifacts
|
|
4266
4836
|
);
|
|
4267
4837
|
const explicitMaxPartBytes = cliOptions?.maxPartBytes ?? userConfig?.maxPartBytes;
|
|
4268
4838
|
const maxPartBytes = boundedBytes(
|
|
@@ -4436,7 +5006,18 @@ function configDigest(config) {
|
|
|
4436
5006
|
excludePatterns: config.excludePatterns,
|
|
4437
5007
|
preset: config.preset,
|
|
4438
5008
|
layersMode: config.layersMode,
|
|
4439
|
-
budgets
|
|
5009
|
+
// `budgets.byLayer` is a plain object, and `JSON.stringify` preserves insertion order — so
|
|
5010
|
+
// `{ docs, api }` and `{ api, docs }` hashed differently despite being the same settings, and
|
|
5011
|
+
// `validate` reported configuration drift for a snapshot nobody had changed. The keys are
|
|
5012
|
+
// canonicalised here; nothing else about the object is order-dependent.
|
|
5013
|
+
budgets: {
|
|
5014
|
+
default: config.budgets.default,
|
|
5015
|
+
byLayer: Object.fromEntries(
|
|
5016
|
+
Object.entries(config.budgets.byLayer).sort(
|
|
5017
|
+
([left], [right]) => left < right ? -1 : left > right ? 1 : 0
|
|
5018
|
+
)
|
|
5019
|
+
)
|
|
5020
|
+
},
|
|
4440
5021
|
// The matcher is what makes a layer a layer. Hashing only the names meant that
|
|
4441
5022
|
// moving `{ name: "api", match: "src/api/**" }` to `"src/v2/**"` produced the same
|
|
4442
5023
|
// digest, so `validate` reported "valid and up to date" for a snapshot whose files
|
|
@@ -4720,9 +5301,21 @@ function generateSarifReport(manifest, findings, rules, toolVersion) {
|
|
|
4720
5301
|
}
|
|
4721
5302
|
|
|
4722
5303
|
// src/partitioner/preview.ts
|
|
5304
|
+
var LINE_ENDINGS = /\r\n|\r/g;
|
|
5305
|
+
var normaliseLineEndings = (content) => content.replace(LINE_ENDINGS, "\n");
|
|
4723
5306
|
function countLines(content) {
|
|
4724
5307
|
if (content.length === 0) return 0;
|
|
4725
|
-
|
|
5308
|
+
let breaks = 0;
|
|
5309
|
+
for (let index = 0; index < content.length; index += 1) {
|
|
5310
|
+
const code = content.charCodeAt(index);
|
|
5311
|
+
if (code === 10) {
|
|
5312
|
+
breaks += 1;
|
|
5313
|
+
continue;
|
|
5314
|
+
}
|
|
5315
|
+
if (code === 13 && content.charCodeAt(index + 1) !== 10) breaks += 1;
|
|
5316
|
+
}
|
|
5317
|
+
const last = content.charCodeAt(content.length - 1);
|
|
5318
|
+
return last === 10 || last === 13 ? breaks : breaks + 1;
|
|
4726
5319
|
}
|
|
4727
5320
|
function truncateToBytes(content, maxBytes) {
|
|
4728
5321
|
if (maxBytes <= 0) return { content: "", isTruncated: content.length > 0 };
|
|
@@ -4733,17 +5326,18 @@ function truncateToBytes(content, maxBytes) {
|
|
|
4733
5326
|
return { content: slice, isTruncated: true };
|
|
4734
5327
|
}
|
|
4735
5328
|
function truncatePreview(content, headLines, tailLines) {
|
|
4736
|
-
const
|
|
4737
|
-
const
|
|
5329
|
+
const normalised = normaliseLineEndings(content);
|
|
5330
|
+
const lines = normalised.split("\n");
|
|
5331
|
+
const total = countLines(normalised);
|
|
4738
5332
|
if (total <= headLines + tailLines) {
|
|
4739
5333
|
return {
|
|
4740
|
-
content,
|
|
5334
|
+
content: normalised,
|
|
4741
5335
|
isTruncated: false,
|
|
4742
5336
|
originalLines: total,
|
|
4743
5337
|
includedLines: total
|
|
4744
5338
|
};
|
|
4745
5339
|
}
|
|
4746
|
-
const window =
|
|
5340
|
+
const window = normalised.endsWith("\n") ? lines.slice(0, -1) : lines;
|
|
4747
5341
|
const head = window.slice(0, headLines);
|
|
4748
5342
|
const tail = tailLines > 0 ? window.slice(window.length - tailLines) : [];
|
|
4749
5343
|
const omitted = total - head.length - tail.length;
|
|
@@ -4797,23 +5391,55 @@ function truncateWithBudgetMarker(content, maxBytes, budget, originalLines) {
|
|
|
4797
5391
|
};
|
|
4798
5392
|
}
|
|
4799
5393
|
function partitionFiles(files, maxPartBytes, contentFingerprint) {
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
const
|
|
4804
|
-
|
|
5394
|
+
return partitionFilesForBuckets([{ files, budgetBytes: maxPartBytes }], contentFingerprint);
|
|
5395
|
+
}
|
|
5396
|
+
function partitionFilesForBuckets(buckets, contentFingerprint) {
|
|
5397
|
+
const groups = [];
|
|
5398
|
+
for (const bucket of buckets) {
|
|
5399
|
+
if (bucket.files.length === 0) continue;
|
|
5400
|
+
const budget = Math.max(
|
|
5401
|
+
MIN_SECTION_BUDGET_BYTES + HEADER_RESERVE_BYTES,
|
|
5402
|
+
bucket.budgetBytes
|
|
5403
|
+
);
|
|
5404
|
+
const sectionBudget = budget - HEADER_RESERVE_BYTES;
|
|
5405
|
+
const prepared = bucket.files.map((file) => prepareSection(file, sectionBudget));
|
|
5406
|
+
for (const sections of greedyGroups(prepared, budget)) groups.push({ sections, budget });
|
|
5407
|
+
}
|
|
5408
|
+
if (groups.length === 0) return [];
|
|
5409
|
+
const measurements = /* @__PURE__ */ new Map();
|
|
5410
|
+
const widthOf = (total) => Math.max(3, String(total).length);
|
|
5411
|
+
const measure = (group, index, total) => {
|
|
5412
|
+
const cached = measurements.get(group);
|
|
5413
|
+
const width = widthOf(total);
|
|
5414
|
+
if (cached !== void 0 && cached.index === index && cached.width === width) {
|
|
5415
|
+
return cached.size;
|
|
5416
|
+
}
|
|
5417
|
+
const { content } = assembleGroup(group, index, total, contentFingerprint);
|
|
5418
|
+
const size2 = Buffer.byteLength(content, "utf8");
|
|
5419
|
+
measurements.set(group, { index, width, size: size2 });
|
|
5420
|
+
return size2;
|
|
5421
|
+
};
|
|
4805
5422
|
for (let pass = 0; pass < MAX_SPLIT_PASSES; pass++) {
|
|
4806
|
-
const
|
|
4807
|
-
|
|
4808
|
-
|
|
5423
|
+
const total = groups.length;
|
|
5424
|
+
let oversized = -1;
|
|
5425
|
+
for (let index = 0; index < total; index += 1) {
|
|
5426
|
+
const group2 = groups[index];
|
|
5427
|
+
if (measure(group2, index, total) > group2.budget) {
|
|
5428
|
+
oversized = index;
|
|
5429
|
+
break;
|
|
5430
|
+
}
|
|
5431
|
+
}
|
|
5432
|
+
if (oversized === -1) return renderGroups(groups, contentFingerprint);
|
|
4809
5433
|
const group = groups[oversized];
|
|
4810
|
-
if (group.length > 1) {
|
|
4811
|
-
const moved = group.pop();
|
|
4812
|
-
groups.splice(oversized + 1, 0, [moved]);
|
|
5434
|
+
if (group.sections.length > 1) {
|
|
5435
|
+
const moved = group.sections.pop();
|
|
5436
|
+
groups.splice(oversized + 1, 0, { sections: [moved], budget: group.budget });
|
|
5437
|
+
measurements.delete(group);
|
|
4813
5438
|
continue;
|
|
4814
5439
|
}
|
|
4815
|
-
const [section] = group;
|
|
4816
|
-
|
|
5440
|
+
const [section] = group.sections;
|
|
5441
|
+
if (section === void 0) return renderGroups(groups, contentFingerprint);
|
|
5442
|
+
const overflow = measure(group, oversized, total) - group.budget;
|
|
4817
5443
|
const currentContent = section.file.content ?? "";
|
|
4818
5444
|
const shrinkTo = Math.max(
|
|
4819
5445
|
0,
|
|
@@ -4822,26 +5448,33 @@ function partitionFiles(files, maxPartBytes, contentFingerprint) {
|
|
|
4822
5448
|
const shrunk = truncateWithBudgetMarker(
|
|
4823
5449
|
currentContent,
|
|
4824
5450
|
shrinkTo,
|
|
4825
|
-
budget,
|
|
5451
|
+
group.budget,
|
|
4826
5452
|
section.file.originalLines
|
|
4827
5453
|
);
|
|
4828
5454
|
if (shrunk.isTruncated) {
|
|
4829
5455
|
applyTruncatedContent(section.file, shrunk.content);
|
|
4830
5456
|
section.text = renderFileSection(section.file);
|
|
4831
5457
|
section.byteSize = Buffer.byteLength(section.text, "utf8");
|
|
5458
|
+
measurements.delete(group);
|
|
4832
5459
|
continue;
|
|
4833
5460
|
}
|
|
5461
|
+
const rendered = renderGroups(groups, contentFingerprint);
|
|
4834
5462
|
const marker2 = TRUNCATION_MARKER;
|
|
4835
|
-
assertBudgetHoldsMarker(budget);
|
|
5463
|
+
assertBudgetHoldsMarker(group.budget);
|
|
4836
5464
|
const target = Math.max(
|
|
4837
5465
|
0,
|
|
4838
|
-
budget - Buffer.byteLength(marker2, "utf8") - TOKEN_LINE_SLACK
|
|
5466
|
+
group.budget - Buffer.byteLength(marker2, "utf8") - TOKEN_LINE_SLACK
|
|
4839
5467
|
);
|
|
4840
5468
|
const clipped = truncateToBytes(rendered[oversized].markdown, target);
|
|
4841
|
-
rendered[oversized] = rehashPart(
|
|
5469
|
+
rendered[oversized] = rehashPart(
|
|
5470
|
+
rendered[oversized],
|
|
5471
|
+
clipped.content + marker2
|
|
5472
|
+
);
|
|
4842
5473
|
return rendered;
|
|
4843
5474
|
}
|
|
4844
|
-
return
|
|
5475
|
+
return renderGroups(groups, contentFingerprint).flatMap(
|
|
5476
|
+
(part) => enforceBudget([part], part.metadata.budgetBytes)
|
|
5477
|
+
);
|
|
4845
5478
|
}
|
|
4846
5479
|
var TRUNCATION_MARKER = "\n... [part truncated: cannot fit budget] ...\n";
|
|
4847
5480
|
function assertBudgetHoldsMarker(budget) {
|
|
@@ -4926,7 +5559,7 @@ function renumberParts(parts, contentFingerprint) {
|
|
|
4926
5559
|
(part) => enforceBudget([part], part.metadata.budgetBytes)[0] ?? part
|
|
4927
5560
|
);
|
|
4928
5561
|
}
|
|
4929
|
-
var SECTION_TRUNCATION_NOTICE = "
|
|
5562
|
+
var SECTION_TRUNCATION_NOTICE = "... [section truncated: the fixed metadata and a code fence did not fit the part budget] ...";
|
|
4930
5563
|
var METADATA_VALUE_MAX = 200;
|
|
4931
5564
|
var DISPLAY_PATH_MAX = 300;
|
|
4932
5565
|
function capValue(value, max) {
|
|
@@ -4968,13 +5601,57 @@ function prepareSection(file, sectionBudget) {
|
|
|
4968
5601
|
shrinkBody();
|
|
4969
5602
|
}
|
|
4970
5603
|
if (byteSize > sectionBudget) {
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
5604
|
+
applyTruncatedContent(file, SECTION_TRUNCATION_NOTICE);
|
|
5605
|
+
file.summary = void 0;
|
|
5606
|
+
file.summaryOmitted = true;
|
|
5607
|
+
text2 = renderFileSection(file);
|
|
4974
5608
|
byteSize = Buffer.byteLength(text2, "utf8");
|
|
5609
|
+
if (byteSize > sectionBudget) {
|
|
5610
|
+
text2 = truncateRenderedSection(text2, sectionBudget, fenceForContent(file.content ?? ""));
|
|
5611
|
+
byteSize = Buffer.byteLength(text2, "utf8");
|
|
5612
|
+
}
|
|
4975
5613
|
}
|
|
4976
5614
|
return { file, text: text2, byteSize };
|
|
4977
5615
|
}
|
|
5616
|
+
function truncateRenderedSection(text2, sectionBudget, fence) {
|
|
5617
|
+
const closing = `
|
|
5618
|
+
${SECTION_TRUNCATION_NOTICE}
|
|
5619
|
+
${fence}
|
|
5620
|
+
`;
|
|
5621
|
+
const bodyRoom = sectionBudget - Buffer.byteLength(closing, "utf8");
|
|
5622
|
+
if (bodyRoom > 0) {
|
|
5623
|
+
const cut2 = truncateToBytes(text2, bodyRoom).content;
|
|
5624
|
+
const lastBreak2 = cut2.lastIndexOf("\n");
|
|
5625
|
+
const kept = lastBreak2 >= 0 ? cut2.slice(0, lastBreak2 + 1) : "";
|
|
5626
|
+
if (fenceIsOpen(kept, fence)) return `${kept}${closing}`;
|
|
5627
|
+
}
|
|
5628
|
+
const probe = text2.slice(0, 512);
|
|
5629
|
+
const emptyFence = fenceForText(probe.slice(0, Math.max(0, probe.lastIndexOf("\n") + 1)), 4);
|
|
5630
|
+
const block = `
|
|
5631
|
+
${emptyFence}
|
|
5632
|
+
${SECTION_TRUNCATION_NOTICE}
|
|
5633
|
+
${emptyFence}
|
|
5634
|
+
`;
|
|
5635
|
+
const prefixRoom = sectionBudget - Buffer.byteLength(block, "utf8");
|
|
5636
|
+
const cut = prefixRoom > 0 ? truncateToBytes(text2, prefixRoom).content : "";
|
|
5637
|
+
const lastBreak = cut.lastIndexOf("\n");
|
|
5638
|
+
const prefix = lastBreak >= 0 ? cut.slice(0, lastBreak + 1) : "";
|
|
5639
|
+
return `${dropTrailingOpenFence(prefix, fence)}${block}`;
|
|
5640
|
+
}
|
|
5641
|
+
function dropTrailingOpenFence(text2, fence) {
|
|
5642
|
+
if (!fenceIsOpen(text2, fence)) return text2;
|
|
5643
|
+
const lines = text2.split("\n");
|
|
5644
|
+
const opening = lines.findIndex((line) => line.startsWith(fence));
|
|
5645
|
+
if (opening < 0) return text2;
|
|
5646
|
+
return lines.slice(0, opening).join("\n") + (opening > 0 ? "\n" : "");
|
|
5647
|
+
}
|
|
5648
|
+
function fenceIsOpen(text2, fence) {
|
|
5649
|
+
let open = false;
|
|
5650
|
+
for (const line of text2.split("\n")) {
|
|
5651
|
+
if (line.startsWith(fence)) open = !open;
|
|
5652
|
+
}
|
|
5653
|
+
return open;
|
|
5654
|
+
}
|
|
4978
5655
|
function applyTruncatedContent(file, content) {
|
|
4979
5656
|
file.content = content;
|
|
4980
5657
|
file.isPreviewTruncated = true;
|
|
@@ -5022,52 +5699,64 @@ function greedyGroups(sections, budget) {
|
|
|
5022
5699
|
if (current.length > 0) groups.push(current);
|
|
5023
5700
|
return groups;
|
|
5024
5701
|
}
|
|
5025
|
-
function
|
|
5702
|
+
function assembleGroup(group, index, total, contentFingerprint) {
|
|
5703
|
+
const width = Math.max(3, String(total).length);
|
|
5704
|
+
const zeroPad = (value) => String(value).padStart(width, "0");
|
|
5705
|
+
const filename = `part-${zeroPad(index + 1)}.md`;
|
|
5706
|
+
const prev = index === 0 ? "none" : `part-${zeroPad(index)}.md`;
|
|
5707
|
+
const next = index === total - 1 ? "none" : `part-${zeroPad(index + 2)}.md`;
|
|
5708
|
+
for (const section of group.sections) {
|
|
5709
|
+
section.file.part = filename;
|
|
5710
|
+
}
|
|
5711
|
+
const layerCounts = {};
|
|
5712
|
+
const kindCounts = {};
|
|
5713
|
+
for (const section of group.sections) {
|
|
5714
|
+
layerCounts[section.file.layer] = (layerCounts[section.file.layer] || 0) + 1;
|
|
5715
|
+
kindCounts[section.file.kind] = (kindCounts[section.file.kind] || 0) + 1;
|
|
5716
|
+
}
|
|
5717
|
+
const pathLines = group.sections.map((s) => displayPath(s.file.relativePath));
|
|
5718
|
+
const listFence = fenceForText(pathLines.join("\n"), 3);
|
|
5719
|
+
const header = [
|
|
5720
|
+
`# Codebase Snapshot Part ${zeroPad(index + 1)} of ${zeroPad(total)}`,
|
|
5721
|
+
"",
|
|
5722
|
+
`- Previous: ${prev}`,
|
|
5723
|
+
`- Next: ${next}`,
|
|
5724
|
+
`- Files: ${group.sections.length}`,
|
|
5725
|
+
`- Content Fingerprint: ${contentFingerprint || "none"}`,
|
|
5726
|
+
`- Layers: ${topEntries(layerCounts, 6) || "none"}`,
|
|
5727
|
+
`- Kinds: ${topEntries(kindCounts, 8) || "none"}`,
|
|
5728
|
+
"",
|
|
5729
|
+
UNTRUSTED_CONTENT_NOTICE,
|
|
5730
|
+
"",
|
|
5731
|
+
"## Files",
|
|
5732
|
+
"",
|
|
5733
|
+
`${listFence}text`,
|
|
5734
|
+
...pathLines,
|
|
5735
|
+
listFence,
|
|
5736
|
+
"",
|
|
5737
|
+
"## Contents",
|
|
5738
|
+
""
|
|
5739
|
+
].join("\n");
|
|
5740
|
+
const assembled = (header + group.sections.map((s) => s.text).join("")).replace(
|
|
5741
|
+
/\r\n|\r/g,
|
|
5742
|
+
"\n"
|
|
5743
|
+
);
|
|
5744
|
+
const estimatedTokens = estimateTokens(assembled);
|
|
5745
|
+
return { filename, content: insertTokenLine(assembled, estimatedTokens), estimatedTokens };
|
|
5746
|
+
}
|
|
5747
|
+
function renderGroups(groups, contentFingerprint) {
|
|
5026
5748
|
const total = groups.length;
|
|
5027
|
-
const zeroPad = (index) => String(index).padStart(Math.max(3, String(total).length), "0");
|
|
5028
5749
|
return groups.map((group, index) => {
|
|
5029
|
-
const filename =
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
const layersCovered = Array.from(new Set(group.map((s) => s.file.layer))).sort();
|
|
5036
|
-
const layerCounts = {};
|
|
5037
|
-
const kindCounts = {};
|
|
5038
|
-
for (const section of group) {
|
|
5039
|
-
layerCounts[section.file.layer] = (layerCounts[section.file.layer] || 0) + 1;
|
|
5040
|
-
kindCounts[section.file.kind] = (kindCounts[section.file.kind] || 0) + 1;
|
|
5041
|
-
}
|
|
5042
|
-
const pathLines = group.map((s) => displayPath(s.file.relativePath));
|
|
5043
|
-
const listFence = fenceForText(pathLines.join("\n"), 3);
|
|
5044
|
-
const header = [
|
|
5045
|
-
`# Codebase Snapshot Part ${zeroPad(index + 1)} of ${zeroPad(total)}`,
|
|
5046
|
-
"",
|
|
5047
|
-
`- Previous: ${prev}`,
|
|
5048
|
-
`- Next: ${next}`,
|
|
5049
|
-
`- Files: ${group.length}`,
|
|
5050
|
-
`- Content Fingerprint: ${contentFingerprint || "none"}`,
|
|
5051
|
-
`- Layers: ${topEntries(layerCounts, 6) || "none"}`,
|
|
5052
|
-
`- Kinds: ${topEntries(kindCounts, 8) || "none"}`,
|
|
5053
|
-
"",
|
|
5054
|
-
UNTRUSTED_CONTENT_NOTICE,
|
|
5055
|
-
"",
|
|
5056
|
-
"## Files",
|
|
5057
|
-
"",
|
|
5058
|
-
`${listFence}text`,
|
|
5059
|
-
...pathLines,
|
|
5060
|
-
listFence,
|
|
5061
|
-
"",
|
|
5062
|
-
"## Contents",
|
|
5063
|
-
""
|
|
5064
|
-
].join("\n");
|
|
5065
|
-
const assembled = (header + group.map((s) => s.text).join("")).replace(/\r\n/g, "\n");
|
|
5066
|
-
const estimatedTokens = estimateTokens(assembled);
|
|
5067
|
-
const content = insertTokenLine(assembled, estimatedTokens);
|
|
5750
|
+
const { filename, content, estimatedTokens } = assembleGroup(
|
|
5751
|
+
group,
|
|
5752
|
+
index,
|
|
5753
|
+
total,
|
|
5754
|
+
contentFingerprint
|
|
5755
|
+
);
|
|
5068
5756
|
const byteSize = Buffer.byteLength(content, "utf8");
|
|
5069
5757
|
const sha256 = crypto2__namespace.createHash("sha256").update(content).digest("hex");
|
|
5070
|
-
const
|
|
5758
|
+
const layersCovered = Array.from(new Set(group.sections.map((s) => s.file.layer))).sort();
|
|
5759
|
+
const includedFiles = group.sections.map(({ file }) => ({
|
|
5071
5760
|
relativePath: file.relativePath,
|
|
5072
5761
|
sourceBytes: file.sizeBytes,
|
|
5073
5762
|
includedBytes: file.includedBytes,
|
|
@@ -5080,10 +5769,10 @@ function renderGroups(groups, budget, contentFingerprint) {
|
|
|
5080
5769
|
filename,
|
|
5081
5770
|
byteSize,
|
|
5082
5771
|
estimatedTokens,
|
|
5083
|
-
budgetBytes: budget,
|
|
5772
|
+
budgetBytes: group.budget,
|
|
5084
5773
|
sha256,
|
|
5085
5774
|
lines: countLines(content),
|
|
5086
|
-
fileCount: group.length,
|
|
5775
|
+
fileCount: group.sections.length,
|
|
5087
5776
|
layersCovered,
|
|
5088
5777
|
includedFiles
|
|
5089
5778
|
};
|
|
@@ -5091,7 +5780,7 @@ function renderGroups(groups, budget, contentFingerprint) {
|
|
|
5091
5780
|
});
|
|
5092
5781
|
}
|
|
5093
5782
|
function emittedBody(content) {
|
|
5094
|
-
const normalised = content.replace(/\r\n/g, "\n");
|
|
5783
|
+
const normalised = content.replace(/\r\n|\r/g, "\n");
|
|
5095
5784
|
return normalised.endsWith("\n") ? normalised.slice(0, -1) : normalised;
|
|
5096
5785
|
}
|
|
5097
5786
|
function compactMetadata(file) {
|
|
@@ -5159,7 +5848,7 @@ function generateMonolithJson(manifest, filesWithContent) {
|
|
|
5159
5848
|
}
|
|
5160
5849
|
function generateMonolithMarkdown(manifest, filesWithContent) {
|
|
5161
5850
|
const sections = filesWithContent.map((file) => {
|
|
5162
|
-
const content = (file.content ?? "").replace(/\r\n/g, "\n");
|
|
5851
|
+
const content = (file.content ?? "").replace(/\r\n|\r/g, "\n");
|
|
5163
5852
|
const fence = fenceForContent(content);
|
|
5164
5853
|
return [
|
|
5165
5854
|
`## File: ${escapeControls(file.relativePath)}`,
|
|
@@ -5324,322 +6013,79 @@ function generateReadme(manifest, artifacts) {
|
|
|
5324
6013
|
`${readOrder.length + 1}. Inspect \`manifest.json\` for precise dependency and AST symbol maps.`
|
|
5325
6014
|
);
|
|
5326
6015
|
}
|
|
5327
|
-
readOrder.push(
|
|
5328
|
-
`${readOrder.length + 1}. Check \`manifest.validation.status\` before trusting the snapshot; \`snapshot validate\` re-checks it against disk.`
|
|
5329
|
-
);
|
|
5330
|
-
lines.push("", "## AI Agent Ingestion Guide", "", ...readOrder, "");
|
|
5331
|
-
return lines.join("\n");
|
|
5332
|
-
}
|
|
5333
|
-
|
|
5334
|
-
// src/generator/tree.ts
|
|
5335
|
-
var MAX_TREE_LINES = 2e4;
|
|
5336
|
-
var MAX_TREE_DEPTH = 512;
|
|
5337
|
-
function childKeys(node) {
|
|
5338
|
-
return Array.from(node.children.keys()).sort((a, b) => {
|
|
5339
|
-
const aIsDir = node.children.get(a).children.size > 0;
|
|
5340
|
-
const bIsDir = node.children.get(b).children.size > 0;
|
|
5341
|
-
if (aIsDir !== bIsDir) return aIsDir ? -1 : 1;
|
|
5342
|
-
return a < b ? -1 : a > b ? 1 : 0;
|
|
5343
|
-
});
|
|
5344
|
-
}
|
|
5345
|
-
function generateProjectTree(filePaths) {
|
|
5346
|
-
const root = { children: /* @__PURE__ */ new Map() };
|
|
5347
|
-
for (const filePath of filePaths) {
|
|
5348
|
-
const parts = filePath.split("/").filter((part) => part.length > 0);
|
|
5349
|
-
let current = root;
|
|
5350
|
-
for (const part of parts) {
|
|
5351
|
-
let child = current.children.get(part);
|
|
5352
|
-
if (!child) {
|
|
5353
|
-
child = { children: /* @__PURE__ */ new Map() };
|
|
5354
|
-
current.children.set(part, child);
|
|
5355
|
-
}
|
|
5356
|
-
current = child;
|
|
5357
|
-
}
|
|
5358
|
-
}
|
|
5359
|
-
const lines = ["."];
|
|
5360
|
-
let truncatedAt = -1;
|
|
5361
|
-
let depthTruncated = false;
|
|
5362
|
-
const stack = [{ node: root, prefix: "", depth: 0, keys: childKeys(root), index: 0 }];
|
|
5363
|
-
while (stack.length > 0) {
|
|
5364
|
-
const frame = stack[stack.length - 1];
|
|
5365
|
-
if (frame.index >= frame.keys.length) {
|
|
5366
|
-
stack.pop();
|
|
5367
|
-
continue;
|
|
5368
|
-
}
|
|
5369
|
-
const key = frame.keys[frame.index];
|
|
5370
|
-
frame.index += 1;
|
|
5371
|
-
const isLast = frame.index === frame.keys.length;
|
|
5372
|
-
const child = frame.node.children.get(key);
|
|
5373
|
-
const isDirectory = child.children.size > 0;
|
|
5374
|
-
const label = escapeControls(key);
|
|
5375
|
-
lines.push(`${frame.prefix}${isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 "}${label}${isDirectory ? "/" : ""}`);
|
|
5376
|
-
if (lines.length >= MAX_TREE_LINES) {
|
|
5377
|
-
truncatedAt = lines.length;
|
|
5378
|
-
break;
|
|
5379
|
-
}
|
|
5380
|
-
if (!isDirectory) continue;
|
|
5381
|
-
if (frame.depth + 1 > MAX_TREE_DEPTH) {
|
|
5382
|
-
depthTruncated = true;
|
|
5383
|
-
continue;
|
|
5384
|
-
}
|
|
5385
|
-
stack.push({
|
|
5386
|
-
node: child,
|
|
5387
|
-
prefix: frame.prefix + (isLast ? " " : "\u2502 "),
|
|
5388
|
-
depth: frame.depth + 1,
|
|
5389
|
-
keys: childKeys(child),
|
|
5390
|
-
index: 0
|
|
5391
|
-
});
|
|
5392
|
-
}
|
|
5393
|
-
if (truncatedAt !== -1) {
|
|
5394
|
-
lines.push(`... [tree truncated at ${MAX_TREE_LINES} lines] ...`);
|
|
5395
|
-
} else if (depthTruncated) {
|
|
5396
|
-
lines.push(`... [tree truncated at ${MAX_TREE_DEPTH} levels] ...`);
|
|
5397
|
-
}
|
|
5398
|
-
const fence = fenceForText(lines.join("\n"), 3);
|
|
5399
|
-
return ["# Project Tree", "", `${fence}text`, ...lines, fence, ""].join("\n");
|
|
5400
|
-
}
|
|
5401
|
-
var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
5402
|
-
".png",
|
|
5403
|
-
".jpg",
|
|
5404
|
-
".jpeg",
|
|
5405
|
-
".gif",
|
|
5406
|
-
".webp",
|
|
5407
|
-
".avif",
|
|
5408
|
-
".ico",
|
|
5409
|
-
".bmp",
|
|
5410
|
-
".tiff",
|
|
5411
|
-
".pdf",
|
|
5412
|
-
".zip",
|
|
5413
|
-
".tar",
|
|
5414
|
-
".gz",
|
|
5415
|
-
".tgz",
|
|
5416
|
-
".br",
|
|
5417
|
-
".bz2",
|
|
5418
|
-
".xz",
|
|
5419
|
-
".zst",
|
|
5420
|
-
".lz4",
|
|
5421
|
-
".lzma",
|
|
5422
|
-
".7z",
|
|
5423
|
-
".rar",
|
|
5424
|
-
".mp3",
|
|
5425
|
-
".mp4",
|
|
5426
|
-
".wav",
|
|
5427
|
-
".ogg",
|
|
5428
|
-
".webm",
|
|
5429
|
-
".mov",
|
|
5430
|
-
".avi",
|
|
5431
|
-
".woff",
|
|
5432
|
-
".woff2",
|
|
5433
|
-
".ttf",
|
|
5434
|
-
".otf",
|
|
5435
|
-
".eot",
|
|
5436
|
-
".wasm",
|
|
5437
|
-
".node",
|
|
5438
|
-
".exe",
|
|
5439
|
-
".dll",
|
|
5440
|
-
".dylib",
|
|
5441
|
-
".so"
|
|
5442
|
-
]);
|
|
5443
|
-
function isBinaryExtension(filePath) {
|
|
5444
|
-
const ext = path16__namespace.extname(filePath).toLowerCase();
|
|
5445
|
-
return BINARY_EXTENSIONS.has(ext);
|
|
5446
|
-
}
|
|
5447
|
-
function isBinaryBuffer(buffer) {
|
|
5448
|
-
return buffer.includes(0);
|
|
5449
|
-
}
|
|
5450
|
-
function countControlBytes(buffer) {
|
|
5451
|
-
let count = 0;
|
|
5452
|
-
for (let index = 0; index < buffer.length; index++) {
|
|
5453
|
-
const byte = buffer[index];
|
|
5454
|
-
if (byte < 32) {
|
|
5455
|
-
if (byte !== 9 && byte !== 10 && byte !== 13) count += 1;
|
|
5456
|
-
continue;
|
|
5457
|
-
}
|
|
5458
|
-
if (byte === 127) {
|
|
5459
|
-
count += 1;
|
|
5460
|
-
continue;
|
|
5461
|
-
}
|
|
5462
|
-
if (byte === 194 && index + 1 < buffer.length) {
|
|
5463
|
-
const next = buffer[index + 1];
|
|
5464
|
-
if (next >= 128 && next <= 159) {
|
|
5465
|
-
count += 1;
|
|
5466
|
-
index += 1;
|
|
5467
|
-
}
|
|
5468
|
-
}
|
|
5469
|
-
}
|
|
5470
|
-
return count;
|
|
5471
|
-
}
|
|
5472
|
-
var CONTROL_BYTES_FLOOR = 4;
|
|
5473
|
-
function countInvalidUtf8Bytes(buffer) {
|
|
5474
|
-
let invalid = 0;
|
|
5475
|
-
let index = 0;
|
|
5476
|
-
const length = buffer.length;
|
|
5477
|
-
while (index < length) {
|
|
5478
|
-
const lead = buffer[index];
|
|
5479
|
-
if (lead < 128) {
|
|
5480
|
-
index += 1;
|
|
5481
|
-
continue;
|
|
5482
|
-
}
|
|
5483
|
-
let continuation = 0;
|
|
5484
|
-
let codePoint = 0;
|
|
5485
|
-
let minimum = 0;
|
|
5486
|
-
if ((lead & 224) === 192) {
|
|
5487
|
-
continuation = 1;
|
|
5488
|
-
codePoint = lead & 31;
|
|
5489
|
-
minimum = 128;
|
|
5490
|
-
} else if ((lead & 240) === 224) {
|
|
5491
|
-
continuation = 2;
|
|
5492
|
-
codePoint = lead & 15;
|
|
5493
|
-
minimum = 2048;
|
|
5494
|
-
} else if ((lead & 248) === 240) {
|
|
5495
|
-
continuation = 3;
|
|
5496
|
-
codePoint = lead & 7;
|
|
5497
|
-
minimum = 65536;
|
|
5498
|
-
} else {
|
|
5499
|
-
invalid += 1;
|
|
5500
|
-
index += 1;
|
|
5501
|
-
continue;
|
|
5502
|
-
}
|
|
5503
|
-
if (index + continuation >= length) {
|
|
5504
|
-
invalid += 1;
|
|
5505
|
-
index += 1;
|
|
5506
|
-
continue;
|
|
5507
|
-
}
|
|
5508
|
-
let wellFormed = true;
|
|
5509
|
-
for (let offset = 1; offset <= continuation; offset++) {
|
|
5510
|
-
const next = buffer[index + offset];
|
|
5511
|
-
if ((next & 192) !== 128) {
|
|
5512
|
-
wellFormed = false;
|
|
5513
|
-
break;
|
|
5514
|
-
}
|
|
5515
|
-
codePoint = codePoint << 6 | next & 63;
|
|
5516
|
-
}
|
|
5517
|
-
const isSurrogate = codePoint >= 55296 && codePoint <= 57343;
|
|
5518
|
-
if (!wellFormed || codePoint < minimum || codePoint > 1114111 || isSurrogate) {
|
|
5519
|
-
invalid += 1;
|
|
5520
|
-
index += 1;
|
|
5521
|
-
continue;
|
|
5522
|
-
}
|
|
5523
|
-
index += continuation + 1;
|
|
5524
|
-
}
|
|
5525
|
-
return invalid;
|
|
5526
|
-
}
|
|
5527
|
-
function readBomEncoding(buffer) {
|
|
5528
|
-
if (buffer.length >= 2) {
|
|
5529
|
-
if (buffer[0] === 255 && buffer[1] === 254) return "utf-16le";
|
|
5530
|
-
if (buffer[0] === 254 && buffer[1] === 255) return "utf-16be";
|
|
5531
|
-
}
|
|
5532
|
-
return null;
|
|
5533
|
-
}
|
|
5534
|
-
function looksLikeUtf16(buffer) {
|
|
5535
|
-
if (buffer.length < 8 || buffer.length % 2 !== 0) return null;
|
|
5536
|
-
const sample = Math.min(buffer.length, 4096);
|
|
5537
|
-
let nulInLowByte = 0;
|
|
5538
|
-
let nulInHighByte = 0;
|
|
5539
|
-
const units = sample / 2;
|
|
5540
|
-
for (let index = 0; index < sample; index += 2) {
|
|
5541
|
-
if (buffer[index] === 0) nulInLowByte += 1;
|
|
5542
|
-
if (buffer[index + 1] === 0) nulInHighByte += 1;
|
|
5543
|
-
}
|
|
5544
|
-
if (nulInHighByte > units * 0.3 && nulInLowByte < units * 0.05) return "utf-16le";
|
|
5545
|
-
if (nulInLowByte > units * 0.3 && nulInHighByte < units * 0.05) return "utf-16be";
|
|
5546
|
-
return null;
|
|
5547
|
-
}
|
|
5548
|
-
function decodeUtf16(buffer, encoding) {
|
|
5549
|
-
const littleEndian = encoding === "utf-16le" ? buffer : Buffer.from(buffer.subarray(0, buffer.length - buffer.length % 2)).swap16();
|
|
5550
|
-
return new TextDecoder("utf-16le").decode(littleEndian);
|
|
5551
|
-
}
|
|
5552
|
-
function decodeText(buffer) {
|
|
5553
|
-
const bomEncoding = readBomEncoding(buffer);
|
|
5554
|
-
if (bomEncoding) {
|
|
5555
|
-
const text2 = decodeUtf16(buffer.subarray(2), bomEncoding);
|
|
5556
|
-
return { text: text2, encoding: bomEncoding, invalidBytes: 0, binary: false };
|
|
5557
|
-
}
|
|
5558
|
-
if (buffer.includes(0)) {
|
|
5559
|
-
const shape = looksLikeUtf16(buffer);
|
|
5560
|
-
if (shape) {
|
|
5561
|
-
return { text: decodeUtf16(buffer, shape), encoding: shape, invalidBytes: 0, binary: false };
|
|
5562
|
-
}
|
|
5563
|
-
const offset = buffer.indexOf(0);
|
|
5564
|
-
return {
|
|
5565
|
-
text: "",
|
|
5566
|
-
encoding: "utf-8",
|
|
5567
|
-
invalidBytes: 0,
|
|
5568
|
-
binary: true,
|
|
5569
|
-
binaryReason: `NUL byte at offset ${offset}`
|
|
5570
|
-
};
|
|
5571
|
-
}
|
|
5572
|
-
const invalidBytes = countInvalidUtf8Bytes(buffer);
|
|
5573
|
-
if (invalidBytes === 0) {
|
|
5574
|
-
const controlBytes = countControlBytes(buffer);
|
|
5575
|
-
if (controlBytes > CONTROL_BYTES_FLOOR && controlBytes * 100 > buffer.length) {
|
|
5576
|
-
return {
|
|
5577
|
-
text: "",
|
|
5578
|
-
encoding: "utf-8",
|
|
5579
|
-
invalidBytes: 0,
|
|
5580
|
-
binary: true,
|
|
5581
|
-
binaryReason: `${controlBytes} control byte(s) in ${buffer.length} B; valid UTF-8 but not text`
|
|
5582
|
-
};
|
|
5583
|
-
}
|
|
5584
|
-
return {
|
|
5585
|
-
text: new TextDecoder("utf-8").decode(buffer),
|
|
5586
|
-
encoding: "utf-8",
|
|
5587
|
-
invalidBytes: 0,
|
|
5588
|
-
binary: false
|
|
5589
|
-
};
|
|
5590
|
-
}
|
|
5591
|
-
if (invalidBytes >= 32 && invalidBytes * 3 >= buffer.length) {
|
|
5592
|
-
return {
|
|
5593
|
-
text: "",
|
|
5594
|
-
encoding: "utf-8",
|
|
5595
|
-
invalidBytes,
|
|
5596
|
-
binary: true,
|
|
5597
|
-
binaryReason: `${invalidBytes} of ${buffer.length} bytes are not UTF-8; treated as binary`
|
|
5598
|
-
};
|
|
5599
|
-
}
|
|
5600
|
-
return {
|
|
5601
|
-
text: buffer.toString("utf8"),
|
|
5602
|
-
encoding: "utf-8-lossy",
|
|
5603
|
-
invalidBytes,
|
|
5604
|
-
binary: false
|
|
5605
|
-
};
|
|
6016
|
+
readOrder.push(
|
|
6017
|
+
`${readOrder.length + 1}. Check \`manifest.validation.status\` before trusting the snapshot; \`snapshot validate\` re-checks it against disk.`
|
|
6018
|
+
);
|
|
6019
|
+
lines.push("", "## AI Agent Ingestion Guide", "", ...readOrder, "");
|
|
6020
|
+
return lines.join("\n");
|
|
5606
6021
|
}
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
var
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
if (
|
|
5627
|
-
|
|
5628
|
-
|
|
6022
|
+
|
|
6023
|
+
// src/generator/tree.ts
|
|
6024
|
+
var MAX_TREE_LINES = 2e4;
|
|
6025
|
+
var MAX_TREE_DEPTH = 512;
|
|
6026
|
+
function childKeys(node) {
|
|
6027
|
+
return Array.from(node.children.keys()).sort((a, b) => {
|
|
6028
|
+
const aIsDir = node.children.get(a).children.size > 0;
|
|
6029
|
+
const bIsDir = node.children.get(b).children.size > 0;
|
|
6030
|
+
if (aIsDir !== bIsDir) return aIsDir ? -1 : 1;
|
|
6031
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
6032
|
+
});
|
|
6033
|
+
}
|
|
6034
|
+
function generateProjectTree(filePaths) {
|
|
6035
|
+
const root = { children: /* @__PURE__ */ new Map() };
|
|
6036
|
+
for (const filePath of filePaths) {
|
|
6037
|
+
const parts = filePath.split("/").filter((part) => part.length > 0);
|
|
6038
|
+
let current = root;
|
|
6039
|
+
for (const part of parts) {
|
|
6040
|
+
let child = current.children.get(part);
|
|
6041
|
+
if (!child) {
|
|
6042
|
+
child = { children: /* @__PURE__ */ new Map() };
|
|
6043
|
+
current.children.set(part, child);
|
|
5629
6044
|
}
|
|
5630
|
-
|
|
6045
|
+
current = child;
|
|
5631
6046
|
}
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
6047
|
+
}
|
|
6048
|
+
const lines = ["."];
|
|
6049
|
+
let truncatedAt = -1;
|
|
6050
|
+
let depthTruncated = false;
|
|
6051
|
+
const stack = [{ node: root, prefix: "", depth: 0, keys: childKeys(root), index: 0 }];
|
|
6052
|
+
while (stack.length > 0) {
|
|
6053
|
+
const frame = stack[stack.length - 1];
|
|
6054
|
+
if (frame.index >= frame.keys.length) {
|
|
6055
|
+
stack.pop();
|
|
6056
|
+
continue;
|
|
6057
|
+
}
|
|
6058
|
+
const key = frame.keys[frame.index];
|
|
6059
|
+
frame.index += 1;
|
|
6060
|
+
const isLast = frame.index === frame.keys.length;
|
|
6061
|
+
const child = frame.node.children.get(key);
|
|
6062
|
+
const isDirectory = child.children.size > 0;
|
|
6063
|
+
const label = escapeControls(key);
|
|
6064
|
+
lines.push(`${frame.prefix}${isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 "}${label}${isDirectory ? "/" : ""}`);
|
|
6065
|
+
if (lines.length >= MAX_TREE_LINES) {
|
|
6066
|
+
truncatedAt = lines.length;
|
|
6067
|
+
break;
|
|
6068
|
+
}
|
|
6069
|
+
if (!isDirectory) continue;
|
|
6070
|
+
if (frame.depth + 1 > MAX_TREE_DEPTH) {
|
|
6071
|
+
depthTruncated = true;
|
|
6072
|
+
continue;
|
|
5641
6073
|
}
|
|
6074
|
+
stack.push({
|
|
6075
|
+
node: child,
|
|
6076
|
+
prefix: frame.prefix + (isLast ? " " : "\u2502 "),
|
|
6077
|
+
depth: frame.depth + 1,
|
|
6078
|
+
keys: childKeys(child),
|
|
6079
|
+
index: 0
|
|
6080
|
+
});
|
|
6081
|
+
}
|
|
6082
|
+
if (truncatedAt !== -1) {
|
|
6083
|
+
lines.push(`... [tree truncated at ${MAX_TREE_LINES} lines] ...`);
|
|
6084
|
+
} else if (depthTruncated) {
|
|
6085
|
+
lines.push(`... [tree truncated at ${MAX_TREE_DEPTH} levels] ...`);
|
|
5642
6086
|
}
|
|
6087
|
+
const fence = fenceForText(lines.join("\n"), 3);
|
|
6088
|
+
return ["# Project Tree", "", `${fence}text`, ...lines, fence, ""].join("\n");
|
|
5643
6089
|
}
|
|
5644
6090
|
function gitPaths(cwd, args) {
|
|
5645
6091
|
let output;
|
|
@@ -5726,6 +6172,10 @@ var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
5726
6172
|
".mjs",
|
|
5727
6173
|
".cjs"
|
|
5728
6174
|
]);
|
|
6175
|
+
var PYTHON_EXTENSIONS = /* @__PURE__ */ new Set([".py", ".pyi"]);
|
|
6176
|
+
var RUBY_EXTENSIONS = /* @__PURE__ */ new Set([".rb", ".rake", ".gemspec"]);
|
|
6177
|
+
var C_EXTENSIONS = /* @__PURE__ */ new Set([".c", ".h", ".cc", ".cpp", ".cxx", ".hpp", ".hxx", ".m", ".mm"]);
|
|
6178
|
+
var PYTHON_SUFFIXES = [".py", ".pyi", "/__init__.py", "/__init__.pyi"];
|
|
5729
6179
|
function buildImportResolver(files) {
|
|
5730
6180
|
const byPath = /* @__PURE__ */ new Map();
|
|
5731
6181
|
const byPathFolded = /* @__PURE__ */ new Map();
|
|
@@ -5752,11 +6202,45 @@ function buildImportResolver(files) {
|
|
|
5752
6202
|
return { byPath, byPathFolded, byStem, byStemFolded };
|
|
5753
6203
|
}
|
|
5754
6204
|
function resolveSpecifier(fromFile, specifier, resolver) {
|
|
6205
|
+
const extension = path16__namespace.posix.extname(fromFile).toLowerCase();
|
|
6206
|
+
const exact = (key) => resolver.byPath.get(key) ?? resolver.byPathFolded.get(key.toLowerCase()) ?? null;
|
|
6207
|
+
const firstOf = (candidates) => {
|
|
6208
|
+
for (const candidate of candidates) {
|
|
6209
|
+
const hit = exact(candidate);
|
|
6210
|
+
if (hit) return hit;
|
|
6211
|
+
}
|
|
6212
|
+
return null;
|
|
6213
|
+
};
|
|
6214
|
+
if (PYTHON_EXTENSIONS.has(extension) && specifier.startsWith(".")) {
|
|
6215
|
+
const dots = /^\.+/.exec(specifier)?.[0].length ?? 0;
|
|
6216
|
+
const rest = specifier.slice(dots).replace(/\./g, "/");
|
|
6217
|
+
let base = path16__namespace.posix.dirname(fromFile);
|
|
6218
|
+
for (let up = 1; up < dots; up += 1) base = path16__namespace.posix.dirname(base);
|
|
6219
|
+
const joined2 = rest ? path16__namespace.posix.normalize(path16__namespace.posix.join(base, rest)) : base;
|
|
6220
|
+
return firstOf([joined2, ...PYTHON_SUFFIXES.map((suffix) => `${joined2}${suffix}`)]);
|
|
6221
|
+
}
|
|
6222
|
+
if (RUBY_EXTENSIONS.has(extension) && specifier.startsWith("./")) {
|
|
6223
|
+
const joined2 = path16__namespace.posix.normalize(
|
|
6224
|
+
path16__namespace.posix.join(path16__namespace.posix.dirname(fromFile), specifier)
|
|
6225
|
+
);
|
|
6226
|
+
return firstOf([`${joined2}.rb`, joined2]);
|
|
6227
|
+
}
|
|
6228
|
+
if (C_EXTENSIONS.has(extension) && specifier.startsWith("./")) {
|
|
6229
|
+
const joined2 = path16__namespace.posix.normalize(
|
|
6230
|
+
path16__namespace.posix.join(path16__namespace.posix.dirname(fromFile), specifier)
|
|
6231
|
+
);
|
|
6232
|
+
return exact(joined2);
|
|
6233
|
+
}
|
|
6234
|
+
if (extension === ".dart" && !specifier.includes(":")) {
|
|
6235
|
+
const joined2 = path16__namespace.posix.normalize(
|
|
6236
|
+
path16__namespace.posix.join(path16__namespace.posix.dirname(fromFile), specifier)
|
|
6237
|
+
);
|
|
6238
|
+
return firstOf([joined2, `${joined2}.dart`]);
|
|
6239
|
+
}
|
|
5755
6240
|
if (!RELATIVE_SPECIFIER.test(specifier)) return null;
|
|
5756
6241
|
const joined = path16__namespace.posix.normalize(
|
|
5757
6242
|
path16__namespace.posix.join(path16__namespace.posix.dirname(fromFile), specifier)
|
|
5758
6243
|
);
|
|
5759
|
-
const exact = (key) => resolver.byPath.get(key) ?? resolver.byPathFolded.get(key.toLowerCase()) ?? null;
|
|
5760
6244
|
const direct = exact(joined);
|
|
5761
6245
|
if (direct) return direct;
|
|
5762
6246
|
for (const suffix of IMPORT_CANDIDATE_SUFFIXES) {
|
|
@@ -5837,28 +6321,6 @@ function detectLanguage(filePath) {
|
|
|
5837
6321
|
const ext = path16__namespace.posix.extname(base).toLowerCase();
|
|
5838
6322
|
return LANGUAGE_BY_EXTENSION[ext] ?? "text";
|
|
5839
6323
|
}
|
|
5840
|
-
var TS_SOURCE_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
5841
|
-
var isTsSource = (relativePath) => {
|
|
5842
|
-
const lower = relativePath.toLowerCase();
|
|
5843
|
-
return TS_SOURCE_EXTENSIONS.some((extension) => lower.endsWith(extension));
|
|
5844
|
-
};
|
|
5845
|
-
function importsOfText(relativePath, text2) {
|
|
5846
|
-
if (isTsSource(relativePath)) return summarizeTypeScriptSource(relativePath, text2).imports;
|
|
5847
|
-
return summarizeWithProfile(relativePath, text2)?.summary.imports ?? [];
|
|
5848
|
-
}
|
|
5849
|
-
function importsOfFile(relativePath, absolutePath) {
|
|
5850
|
-
let buffer;
|
|
5851
|
-
try {
|
|
5852
|
-
buffer = fs14__namespace.readFileSync(absolutePath);
|
|
5853
|
-
} catch {
|
|
5854
|
-
return [];
|
|
5855
|
-
}
|
|
5856
|
-
const decoded = decodeText(buffer);
|
|
5857
|
-
if (decoded.binary) return [];
|
|
5858
|
-
return importsOfText(relativePath, decoded.text);
|
|
5859
|
-
}
|
|
5860
|
-
|
|
5861
|
-
// src/validator/validate.ts
|
|
5862
6324
|
function maskedPath(value, secretRules) {
|
|
5863
6325
|
return findSecretInPath(value, secretRules)?.masked ?? value;
|
|
5864
6326
|
}
|
|
@@ -5979,9 +6441,13 @@ function describeManifestShapeProblem(value) {
|
|
|
5979
6441
|
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
5980
6442
|
return 'a "parts" entry is not an object';
|
|
5981
6443
|
}
|
|
5982
|
-
|
|
6444
|
+
const filename = entry.filename;
|
|
6445
|
+
if (typeof filename !== "string" || filename.trim() === "") {
|
|
5983
6446
|
return 'a "parts" entry has no "filename"';
|
|
5984
6447
|
}
|
|
6448
|
+
if (filename.includes("/") || filename.includes("\\") || filename.includes("\0") || filename === "." || filename === ".." || path16__namespace.isAbsolute(filename)) {
|
|
6449
|
+
return 'a "parts" entry "filename" must be a bare filename inside parts/';
|
|
6450
|
+
}
|
|
5985
6451
|
}
|
|
5986
6452
|
if (!candidate.layers || typeof candidate.layers !== "object" || Array.isArray(candidate.layers)) {
|
|
5987
6453
|
return 'missing "layers" object';
|
|
@@ -6092,7 +6558,7 @@ function validateSnapshot(configInput) {
|
|
|
6092
6558
|
});
|
|
6093
6559
|
for (const diskFile of collection.files) {
|
|
6094
6560
|
const snapshotHash = manifestFileMap.get(diskFile);
|
|
6095
|
-
if (
|
|
6561
|
+
if (snapshotHash === void 0) {
|
|
6096
6562
|
const skipReason = skippedReasonOnDisk(diskFile);
|
|
6097
6563
|
if (skipReason === "unchanged") {
|
|
6098
6564
|
if (unchangedScope !== null) {
|
|
@@ -6106,6 +6572,11 @@ function validateSnapshot(configInput) {
|
|
|
6106
6572
|
drift.missingInSnapshot.push(diskFile);
|
|
6107
6573
|
continue;
|
|
6108
6574
|
}
|
|
6575
|
+
if (unchangedScope !== null && !unchangedScope.has(diskFile) && !becameDependent(diskFile)) {
|
|
6576
|
+
drift.configMismatches.push(
|
|
6577
|
+
`scope: ${maskedPath(diskFile, config.secretRules)} is in the snapshot but is no longer in the changed-files scope`
|
|
6578
|
+
);
|
|
6579
|
+
}
|
|
6109
6580
|
let diskHash;
|
|
6110
6581
|
try {
|
|
6111
6582
|
diskHash = sha256Hex(fs14__namespace.readFileSync(path16__namespace.join(config.cwd, diskFile)));
|
|
@@ -6246,6 +6717,41 @@ function validateSnapshot(configInput) {
|
|
|
6246
6717
|
`includeSensitiveFiles: manifest ${recorded.includeSensitiveFiles} vs current ${config.includeSensitiveFiles}`
|
|
6247
6718
|
);
|
|
6248
6719
|
}
|
|
6720
|
+
if ((recorded.maxPartTokens ?? void 0) !== (config.maxPartTokens ?? void 0)) {
|
|
6721
|
+
drift.configMismatches.push(
|
|
6722
|
+
`maxPartTokens: manifest ${String(recorded.maxPartTokens ?? "unset")} vs current ${String(config.maxPartTokens ?? "unset")}`
|
|
6723
|
+
);
|
|
6724
|
+
}
|
|
6725
|
+
if ((recorded.maxMonolithBytes ?? null) !== (config.maxMonolithBytes ?? null)) {
|
|
6726
|
+
drift.configMismatches.push(
|
|
6727
|
+
`maxMonolithBytes: manifest ${String(recorded.maxMonolithBytes ?? null)} vs current ${String(config.maxMonolithBytes ?? null)}`
|
|
6728
|
+
);
|
|
6729
|
+
}
|
|
6730
|
+
if (Boolean(recorded.skeleton) !== Boolean(config.skeleton)) {
|
|
6731
|
+
drift.configMismatches.push(
|
|
6732
|
+
`skeleton: manifest ${Boolean(recorded.skeleton)} vs current ${Boolean(config.skeleton)}`
|
|
6733
|
+
);
|
|
6734
|
+
}
|
|
6735
|
+
if (Boolean(recorded.sarif) !== Boolean(config.sarif)) {
|
|
6736
|
+
drift.configMismatches.push(
|
|
6737
|
+
`sarif: manifest ${Boolean(recorded.sarif)} vs current ${Boolean(config.sarif)}`
|
|
6738
|
+
);
|
|
6739
|
+
}
|
|
6740
|
+
if ((recorded.changedSince ?? void 0) !== (config.changedSince ?? void 0)) {
|
|
6741
|
+
drift.configMismatches.push(
|
|
6742
|
+
`changedSince: manifest ${String(recorded.changedSince ?? "unset")} vs current ${String(config.changedSince ?? "unset")}`
|
|
6743
|
+
);
|
|
6744
|
+
}
|
|
6745
|
+
if (Boolean(recorded.staged) !== Boolean(config.staged)) {
|
|
6746
|
+
drift.configMismatches.push(
|
|
6747
|
+
`staged: manifest ${Boolean(recorded.staged)} vs current ${Boolean(config.staged)}`
|
|
6748
|
+
);
|
|
6749
|
+
}
|
|
6750
|
+
if (Boolean(recorded.withDependents) !== Boolean(config.withDependents)) {
|
|
6751
|
+
drift.configMismatches.push(
|
|
6752
|
+
`withDependents: manifest ${Boolean(recorded.withDependents)} vs current ${Boolean(config.withDependents)}`
|
|
6753
|
+
);
|
|
6754
|
+
}
|
|
6249
6755
|
const effectiveDiscovery = config.discovery ?? "auto";
|
|
6250
6756
|
const recordedDiscovery = recorded.discovery;
|
|
6251
6757
|
if (recordedDiscovery !== void 0 && recordedDiscovery !== effectiveDiscovery) {
|
|
@@ -6466,7 +6972,7 @@ function validateSnapshot(configInput) {
|
|
|
6466
6972
|
|
|
6467
6973
|
// src/version.ts
|
|
6468
6974
|
var GENERATOR_NAME = "codebase-snapshot";
|
|
6469
|
-
var GENERATOR_VERSION = "0.9.
|
|
6975
|
+
var GENERATOR_VERSION = "0.9.14";
|
|
6470
6976
|
var LEGACY_GENERATOR_NAMES = ["@ankurhalder/codebase-snapshot"];
|
|
6471
6977
|
function isOurGeneratorName(name) {
|
|
6472
6978
|
if (typeof name !== "string") return false;
|
|
@@ -6493,22 +6999,28 @@ function updateCommand(kind) {
|
|
|
6493
6999
|
if (kind === "local") return "npm install codebase-snapshot@latest";
|
|
6494
7000
|
return "npm install -g codebase-snapshot";
|
|
6495
7001
|
}
|
|
6496
|
-
function
|
|
6497
|
-
const
|
|
6498
|
-
|
|
7002
|
+
function parseSemver(value) {
|
|
7003
|
+
const cleaned = value.trim().replace(/^v/i, "").split("+")[0] ?? "";
|
|
7004
|
+
const dash = cleaned.indexOf("-");
|
|
7005
|
+
const corePart = dash === -1 ? cleaned : cleaned.slice(0, dash);
|
|
7006
|
+
const prerelease = dash === -1 ? null : cleaned.slice(dash + 1);
|
|
7007
|
+
const core = corePart.split(".").map((part) => {
|
|
6499
7008
|
const parsed = Number(part);
|
|
6500
7009
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
6501
7010
|
});
|
|
7011
|
+
return { core, prerelease };
|
|
6502
7012
|
}
|
|
6503
7013
|
function isNewer(candidate, current) {
|
|
6504
|
-
const left =
|
|
6505
|
-
const right =
|
|
6506
|
-
|
|
7014
|
+
const left = parseSemver(candidate);
|
|
7015
|
+
const right = parseSemver(current);
|
|
7016
|
+
if (left.prerelease !== null && right.prerelease === null) return false;
|
|
7017
|
+
const length = Math.max(left.core.length, right.core.length);
|
|
6507
7018
|
for (let index = 0; index < length; index += 1) {
|
|
6508
|
-
const a = left[index] ?? 0;
|
|
6509
|
-
const b = right[index] ?? 0;
|
|
7019
|
+
const a = left.core[index] ?? 0;
|
|
7020
|
+
const b = right.core[index] ?? 0;
|
|
6510
7021
|
if (a !== b) return a > b;
|
|
6511
7022
|
}
|
|
7023
|
+
if (left.prerelease === null && right.prerelease !== null) return true;
|
|
6512
7024
|
return false;
|
|
6513
7025
|
}
|
|
6514
7026
|
function cacheDirectory(env = process.env, platform = process.platform) {
|
|
@@ -6576,14 +7088,22 @@ function writeCache(file, entry) {
|
|
|
6576
7088
|
}
|
|
6577
7089
|
}
|
|
6578
7090
|
function fetchLatestVersion(url = PACKAGE_LATEST_URL, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
6579
|
-
return new Promise((
|
|
7091
|
+
return new Promise((resolve6) => {
|
|
6580
7092
|
let settled = false;
|
|
7093
|
+
let request = null;
|
|
6581
7094
|
const finish = (value) => {
|
|
6582
7095
|
if (settled) return;
|
|
6583
7096
|
settled = true;
|
|
6584
|
-
|
|
7097
|
+
clearTimeout(deadline);
|
|
7098
|
+
resolve6(value);
|
|
6585
7099
|
};
|
|
6586
|
-
const
|
|
7100
|
+
const deadline = setTimeout(() => {
|
|
7101
|
+
request?.destroy();
|
|
7102
|
+
finish(null);
|
|
7103
|
+
}, timeoutMs);
|
|
7104
|
+
deadline.unref();
|
|
7105
|
+
const transport = url.startsWith("http:") ? http__namespace : https__namespace;
|
|
7106
|
+
request = transport.get(
|
|
6587
7107
|
url,
|
|
6588
7108
|
{
|
|
6589
7109
|
headers: {
|
|
@@ -6602,7 +7122,7 @@ function fetchLatestVersion(url = PACKAGE_LATEST_URL, timeoutMs = REQUEST_TIMEOU
|
|
|
6602
7122
|
response.on("data", (chunk) => {
|
|
6603
7123
|
body += chunk;
|
|
6604
7124
|
if (body.length > 64 * 1024) {
|
|
6605
|
-
request
|
|
7125
|
+
request?.destroy();
|
|
6606
7126
|
finish(null);
|
|
6607
7127
|
}
|
|
6608
7128
|
});
|
|
@@ -6617,10 +7137,6 @@ function fetchLatestVersion(url = PACKAGE_LATEST_URL, timeoutMs = REQUEST_TIMEOU
|
|
|
6617
7137
|
}
|
|
6618
7138
|
);
|
|
6619
7139
|
request.on("error", () => finish(null));
|
|
6620
|
-
request.setTimeout(timeoutMs, () => {
|
|
6621
|
-
request.destroy();
|
|
6622
|
-
finish(null);
|
|
6623
|
-
});
|
|
6624
7140
|
});
|
|
6625
7141
|
}
|
|
6626
7142
|
async function checkForUpdate(options) {
|
|
@@ -6664,10 +7180,13 @@ function describeMatcher(matcher) {
|
|
|
6664
7180
|
return String(matcher);
|
|
6665
7181
|
}
|
|
6666
7182
|
function normalizeTarget(cwd, target) {
|
|
6667
|
-
const
|
|
6668
|
-
const
|
|
6669
|
-
|
|
6670
|
-
|
|
7183
|
+
const resolvedCwd = path16__namespace.resolve(cwd);
|
|
7184
|
+
const absolute = path16__namespace.resolve(resolvedCwd, target);
|
|
7185
|
+
const relative8 = path16__namespace.relative(resolvedCwd, absolute);
|
|
7186
|
+
if (relative8 === "" || relative8 === ".." || relative8.startsWith(`..${path16__namespace.sep}`) || path16__namespace.isAbsolute(relative8)) {
|
|
7187
|
+
return { relative: target.replace(/\\/g, "/"), outsideRoot: true };
|
|
7188
|
+
}
|
|
7189
|
+
return { relative: relative8.split(path16__namespace.sep).join("/"), outsideRoot: false };
|
|
6671
7190
|
}
|
|
6672
7191
|
function findRuleMatch(rules, relativePath) {
|
|
6673
7192
|
return rules.find(
|
|
@@ -6694,9 +7213,19 @@ function readManifest2(config) {
|
|
|
6694
7213
|
}
|
|
6695
7214
|
}
|
|
6696
7215
|
function explainPath(config, target) {
|
|
6697
|
-
const
|
|
6698
|
-
const
|
|
7216
|
+
const normalized = normalizeTarget(config.cwd, target);
|
|
7217
|
+
const relative8 = normalized.relative;
|
|
6699
7218
|
const maskedPath2 = findSecretInPath(relative8, config.secretRules)?.masked ?? relative8;
|
|
7219
|
+
if (normalized.outsideRoot) {
|
|
7220
|
+
return {
|
|
7221
|
+
path: maskedPath2,
|
|
7222
|
+
exists: false,
|
|
7223
|
+
included: false,
|
|
7224
|
+
reason: "outside-root",
|
|
7225
|
+
detail: "path resolves outside the configured repository root and was not inspected"
|
|
7226
|
+
};
|
|
7227
|
+
}
|
|
7228
|
+
const absolute = path16__namespace.join(config.cwd, relative8);
|
|
6700
7229
|
const result = {
|
|
6701
7230
|
path: maskedPath2,
|
|
6702
7231
|
exists: fs14__namespace.existsSync(absolute),
|
|
@@ -7331,6 +7860,24 @@ function displayWidth(line) {
|
|
|
7331
7860
|
return width;
|
|
7332
7861
|
}
|
|
7333
7862
|
var GUTTER_PATTERN = /^[ \t]*(?:[❯◆◻◼▏✖][ \t]+)*/;
|
|
7863
|
+
function sliceByDisplayWidth(text2, maxCells) {
|
|
7864
|
+
let cells = 0;
|
|
7865
|
+
let lastSpace = -1;
|
|
7866
|
+
let index = 0;
|
|
7867
|
+
for (const character of text2) {
|
|
7868
|
+
const width = isWideCodePoint(character.codePointAt(0) ?? 0) ? 2 : 1;
|
|
7869
|
+
if (cells + width > maxCells) break;
|
|
7870
|
+
if (character === " ") lastSpace = index;
|
|
7871
|
+
cells += width;
|
|
7872
|
+
index += character.length;
|
|
7873
|
+
}
|
|
7874
|
+
if (index >= text2.length) return { head: text2, tail: "" };
|
|
7875
|
+
if (lastSpace > 0) {
|
|
7876
|
+
return { head: text2.slice(0, lastSpace), tail: text2.slice(lastSpace + 1).trimStart() };
|
|
7877
|
+
}
|
|
7878
|
+
const safeIndex = index > 0 ? index : Array.from(text2)[0]?.length ?? 1;
|
|
7879
|
+
return { head: text2.slice(0, safeIndex), tail: text2.slice(safeIndex).trimStart() };
|
|
7880
|
+
}
|
|
7334
7881
|
function wrapOneLine(line, limit) {
|
|
7335
7882
|
if (displayWidth(line) <= limit) return [line];
|
|
7336
7883
|
const gutter = GUTTER_PATTERN.exec(line)?.[0] ?? "";
|
|
@@ -7345,12 +7892,11 @@ function wrapOneLine(line, limit) {
|
|
|
7345
7892
|
rows.push(prefix + rest);
|
|
7346
7893
|
break;
|
|
7347
7894
|
}
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
rest = rest.slice(cut).trimStart();
|
|
7895
|
+
const { head, tail } = sliceByDisplayWidth(rest, room);
|
|
7896
|
+
rows.push(
|
|
7897
|
+
`${prefix}${head.length > 0 ? head : rest.slice(0, 1)}`
|
|
7898
|
+
);
|
|
7899
|
+
rest = tail;
|
|
7354
7900
|
prefix = continuation;
|
|
7355
7901
|
}
|
|
7356
7902
|
return rows;
|
|
@@ -7421,7 +7967,7 @@ function keyReader(input) {
|
|
|
7421
7967
|
next() {
|
|
7422
7968
|
const queued = queue.shift();
|
|
7423
7969
|
if (queued) return Promise.resolve(queued);
|
|
7424
|
-
return new Promise((
|
|
7970
|
+
return new Promise((resolve6) => waiters.push(resolve6));
|
|
7425
7971
|
},
|
|
7426
7972
|
close() {
|
|
7427
7973
|
input.off("keypress", onKey);
|
|
@@ -7638,17 +8184,25 @@ var CONFIG_CANDIDATES = [
|
|
|
7638
8184
|
".snapshotrc.json",
|
|
7639
8185
|
".snapshotrc"
|
|
7640
8186
|
];
|
|
7641
|
-
function
|
|
7642
|
-
|
|
7643
|
-
|
|
7644
|
-
|
|
8187
|
+
function parsePackageJson(raw) {
|
|
8188
|
+
const parsed = JSON.parse(stripJsonComments(raw));
|
|
8189
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
8190
|
+
throw new Error("package.json is not a JSON object");
|
|
8191
|
+
}
|
|
8192
|
+
return parsed;
|
|
8193
|
+
}
|
|
8194
|
+
function readPackageJson(cwd) {
|
|
7645
8195
|
try {
|
|
7646
|
-
|
|
7647
|
-
return parsed.type === "module" ? "snapshot.config.ts" : "snapshot.config.mjs";
|
|
8196
|
+
return parsePackageJson(fs14__namespace.readFileSync(path16__namespace.join(cwd, "package.json"), "utf8"));
|
|
7648
8197
|
} catch {
|
|
7649
|
-
return
|
|
8198
|
+
return null;
|
|
7650
8199
|
}
|
|
7651
8200
|
}
|
|
8201
|
+
function chooseConfigFileName(cwd) {
|
|
8202
|
+
if (!supportsTypeScriptImports()) return "snapshot.config.mjs";
|
|
8203
|
+
const parsed = readPackageJson(cwd);
|
|
8204
|
+
return parsed?.type === "module" ? "snapshot.config.ts" : "snapshot.config.mjs";
|
|
8205
|
+
}
|
|
7652
8206
|
var SIZE_CHOICES = [
|
|
7653
8207
|
{ value: "60k", label: "Small", hint: "60 KB \xB7 ~15k tokens \xB7 tight context windows" },
|
|
7654
8208
|
{ value: "120k", label: "Medium", hint: "120 KB \xB7 ~30k tokens \xB7 recommended" },
|
|
@@ -7656,12 +8210,9 @@ var SIZE_CHOICES = [
|
|
|
7656
8210
|
{ value: "custom", label: "Custom\u2026", hint: "type a size like 150k or 1.5mb" }
|
|
7657
8211
|
];
|
|
7658
8212
|
function packageJsonHasConfig(cwd) {
|
|
7659
|
-
|
|
7660
|
-
|
|
7661
|
-
|
|
7662
|
-
} catch {
|
|
7663
|
-
return false;
|
|
7664
|
-
}
|
|
8213
|
+
const parsed = readPackageJson(cwd);
|
|
8214
|
+
if (parsed === null) return false;
|
|
8215
|
+
return typeof parsed.snapshot === "object" && parsed.snapshot !== null && !Array.isArray(parsed.snapshot);
|
|
7665
8216
|
}
|
|
7666
8217
|
function artifactAnswers(options) {
|
|
7667
8218
|
const artifacts = { ...DEFAULT_ANSWERS.artifacts };
|
|
@@ -7702,12 +8253,7 @@ function writeFileOrExplain(target, content) {
|
|
|
7702
8253
|
function canWriteModuleConfig(name, cwd) {
|
|
7703
8254
|
if (name.endsWith(".mjs") || name.endsWith(".ts")) return true;
|
|
7704
8255
|
if (!name.endsWith(".js")) return false;
|
|
7705
|
-
|
|
7706
|
-
const parsed = JSON.parse(fs14__namespace.readFileSync(path16__namespace.join(cwd, "package.json"), "utf8"));
|
|
7707
|
-
return parsed.type === "module";
|
|
7708
|
-
} catch {
|
|
7709
|
-
return false;
|
|
7710
|
-
}
|
|
8256
|
+
return readPackageJson(cwd)?.type === "module";
|
|
7711
8257
|
}
|
|
7712
8258
|
function configFormatLabel(name) {
|
|
7713
8259
|
if (name.endsWith(".js")) return 'CommonJS `.js` (package.json has no "type": "module")';
|
|
@@ -7735,7 +8281,8 @@ function serializePackageJson(raw, value) {
|
|
|
7735
8281
|
const crlf = raw.includes("\r\n");
|
|
7736
8282
|
const body = JSON.stringify(value, null, spacing);
|
|
7737
8283
|
const text2 = crlf ? body.replace(/\n/g, "\r\n") : body;
|
|
7738
|
-
|
|
8284
|
+
const bom = raw.startsWith("\uFEFF") ? "\uFEFF" : "";
|
|
8285
|
+
return `${bom}${text2}${raw.endsWith("\n") ? crlf ? "\r\n" : "\n" : ""}`;
|
|
7739
8286
|
}
|
|
7740
8287
|
function updatePackageJson(cwd, notes) {
|
|
7741
8288
|
const packagePath = path16__namespace.join(cwd, "package.json");
|
|
@@ -7751,7 +8298,7 @@ function updatePackageJson(cwd, notes) {
|
|
|
7751
8298
|
let parsed;
|
|
7752
8299
|
try {
|
|
7753
8300
|
raw = fs14__namespace.readFileSync(packagePath, "utf8");
|
|
7754
|
-
parsed =
|
|
8301
|
+
parsed = parsePackageJson(raw);
|
|
7755
8302
|
} catch {
|
|
7756
8303
|
notes.push("Could not parse package.json; npm scripts and the devDependency were not added.");
|
|
7757
8304
|
return unchanged;
|
|
@@ -8282,7 +8829,100 @@ function workerEntry(directory, isCjs) {
|
|
|
8282
8829
|
|
|
8283
8830
|
// src/index.ts
|
|
8284
8831
|
var SCHEMA_VERSION = "3.0.0";
|
|
8285
|
-
var
|
|
8832
|
+
var SUMMARY_MAX_SOURCE_BYTES2 = SUMMARY_MAX_SOURCE_BYTES;
|
|
8833
|
+
var FILE_DERIVATIONS = /* @__PURE__ */ new Map();
|
|
8834
|
+
var FILE_DERIVATION_LIMIT_BYTES = 64 * 1024 * 1024;
|
|
8835
|
+
var fileDerivationBytes = 0;
|
|
8836
|
+
function takeDerivation(path17, hash, settings) {
|
|
8837
|
+
const cached = FILE_DERIVATIONS.get(path17);
|
|
8838
|
+
if (cached === void 0 || cached.hash !== hash || cached.settings !== settings) return void 0;
|
|
8839
|
+
return cached;
|
|
8840
|
+
}
|
|
8841
|
+
function rememberDerivation(path17, hash, settings, derivation) {
|
|
8842
|
+
const bodyBytes = derivation.finalContent === derivation.redacted ? derivation.redacted.length : derivation.redacted.length + derivation.finalContent.length;
|
|
8843
|
+
const bytes = bodyBytes * 2 + 512;
|
|
8844
|
+
if (bytes > FILE_DERIVATION_LIMIT_BYTES / 4) return;
|
|
8845
|
+
while (fileDerivationBytes + bytes > FILE_DERIVATION_LIMIT_BYTES) {
|
|
8846
|
+
const oldest = FILE_DERIVATIONS.keys().next();
|
|
8847
|
+
if (oldest.done === true) break;
|
|
8848
|
+
const evicted = FILE_DERIVATIONS.get(oldest.value);
|
|
8849
|
+
FILE_DERIVATIONS.delete(oldest.value);
|
|
8850
|
+
if (evicted !== void 0) fileDerivationBytes -= evicted.bytes;
|
|
8851
|
+
}
|
|
8852
|
+
FILE_DERIVATIONS.set(path17, { ...derivation, hash, settings, bytes });
|
|
8853
|
+
fileDerivationBytes += bytes;
|
|
8854
|
+
}
|
|
8855
|
+
function derivationSettings(config) {
|
|
8856
|
+
const rules = config.secretRules.map(
|
|
8857
|
+
(rule) => `${rule.rule}\0${rule.pattern.source}\0${rule.pattern.flags}\0${rule.valueGroup ?? ""}`
|
|
8858
|
+
).join("");
|
|
8859
|
+
return `${GENERATOR_VERSION}${configDigest(config)}${sha256Hex2(rules)}`;
|
|
8860
|
+
}
|
|
8861
|
+
function deriveFile(relativePath, rawContent, sizeBytes, config) {
|
|
8862
|
+
const lineCount = countLines(rawContent);
|
|
8863
|
+
const { redacted, findings } = scanAndRedactSecrets(rawContent, config.secretRules, relativePath);
|
|
8864
|
+
const isLarge = sizeBytes > config.largeFileBytes;
|
|
8865
|
+
let finalContent = redacted;
|
|
8866
|
+
let isPreviewTruncated = false;
|
|
8867
|
+
let includedLines = lineCount;
|
|
8868
|
+
let inclusionMode = "full";
|
|
8869
|
+
if (config.skeleton === true && isSkeletonizable(relativePath)) {
|
|
8870
|
+
const skeleton = skeletonizeTypeScript(redacted, relativePath);
|
|
8871
|
+
if (skeleton.collapsed > 0) {
|
|
8872
|
+
finalContent = skeleton.text;
|
|
8873
|
+
inclusionMode = "signatures";
|
|
8874
|
+
includedLines = countLines(skeleton.text);
|
|
8875
|
+
}
|
|
8876
|
+
}
|
|
8877
|
+
if (isLarge && inclusionMode !== "signatures") {
|
|
8878
|
+
const preview = truncatePreview(redacted, config.previewHeadLines, config.previewTailLines);
|
|
8879
|
+
finalContent = preview.content;
|
|
8880
|
+
isPreviewTruncated = preview.isTruncated;
|
|
8881
|
+
includedLines = preview.includedLines;
|
|
8882
|
+
inclusionMode = isPreviewTruncated ? "preview" : "full";
|
|
8883
|
+
}
|
|
8884
|
+
let summary;
|
|
8885
|
+
let summaryQuality;
|
|
8886
|
+
let summarySkip = null;
|
|
8887
|
+
let summaryWarning = null;
|
|
8888
|
+
let dependentImports = null;
|
|
8889
|
+
if (Buffer.byteLength(rawContent, "utf8") > SUMMARY_MAX_SOURCE_BYTES2) {
|
|
8890
|
+
summarySkip = "oversize";
|
|
8891
|
+
dependentImports = [];
|
|
8892
|
+
} else if (isPreviewTruncated && hasStructuralSummary(relativePath)) {
|
|
8893
|
+
summarySkip = "preview";
|
|
8894
|
+
dependentImports = importsOfText(relativePath, rawContent);
|
|
8895
|
+
} else {
|
|
8896
|
+
try {
|
|
8897
|
+
const outcome = summarizeFile(relativePath, rawContent);
|
|
8898
|
+
summary = outcome?.summary ? maskSummaryStrings(outcome.summary, config.secretRules) : void 0;
|
|
8899
|
+
summaryQuality = outcome?.quality;
|
|
8900
|
+
if (isSourceSummary(summary) && summary.parseErrors) {
|
|
8901
|
+
summaryWarning = `Parse errors in ${maskPathText(relativePath, config.secretRules)} (${summary.parseErrors}); its summary is incomplete.`;
|
|
8902
|
+
}
|
|
8903
|
+
} catch (err) {
|
|
8904
|
+
summaryWarning = `Summary skipped for ${maskPathText(relativePath, config.secretRules)}: ${err.message}`.replace(
|
|
8905
|
+
/\s+/g,
|
|
8906
|
+
" "
|
|
8907
|
+
);
|
|
8908
|
+
}
|
|
8909
|
+
}
|
|
8910
|
+
return {
|
|
8911
|
+
redacted,
|
|
8912
|
+
findings,
|
|
8913
|
+
lineCount,
|
|
8914
|
+
finalContent,
|
|
8915
|
+
inclusionMode,
|
|
8916
|
+
isPreviewTruncated,
|
|
8917
|
+
includedLines,
|
|
8918
|
+
isLarge,
|
|
8919
|
+
summary,
|
|
8920
|
+
summaryQuality,
|
|
8921
|
+
summarySkip,
|
|
8922
|
+
summaryWarning,
|
|
8923
|
+
dependentImports
|
|
8924
|
+
};
|
|
8925
|
+
}
|
|
8286
8926
|
function sha256Hex2(input) {
|
|
8287
8927
|
return crypto2__namespace.createHash("sha256").update(input).digest("hex");
|
|
8288
8928
|
}
|
|
@@ -8826,10 +9466,7 @@ function acquireRunLock(stagingRoot) {
|
|
|
8826
9466
|
};
|
|
8827
9467
|
} catch (err) {
|
|
8828
9468
|
if (err.code !== "EEXIST") throw err;
|
|
8829
|
-
if (attempt === 0 &&
|
|
8830
|
-
fs14__namespace.rmSync(lockPath, { force: true });
|
|
8831
|
-
continue;
|
|
8832
|
-
}
|
|
9469
|
+
if (attempt === 0 && reclaimStaleLock(lockPath)) continue;
|
|
8833
9470
|
throw new Error(
|
|
8834
9471
|
`Another generate run is already using the staging directory ${stagingRoot}. Wait for it to finish, or delete ${lockPath} if no other run is active.`
|
|
8835
9472
|
);
|
|
@@ -8838,14 +9475,25 @@ function acquireRunLock(stagingRoot) {
|
|
|
8838
9475
|
throw new Error(`Could not acquire the generate lock for ${stagingRoot}.`);
|
|
8839
9476
|
}
|
|
8840
9477
|
function lockKeyFor(stagingRoot) {
|
|
8841
|
-
let key = stagingRoot;
|
|
8842
|
-
try {
|
|
8843
|
-
key = fs14__namespace.realpathSync(stagingRoot);
|
|
8844
|
-
} catch {
|
|
8845
|
-
}
|
|
9478
|
+
let key = canonicalLockTarget(stagingRoot);
|
|
8846
9479
|
if (process.platform === "win32" || process.platform === "darwin") key = key.toLowerCase();
|
|
8847
9480
|
return sha256Hex2(key).slice(0, 16);
|
|
8848
9481
|
}
|
|
9482
|
+
function canonicalLockTarget(targetPath) {
|
|
9483
|
+
const resolved = path16__namespace.resolve(targetPath);
|
|
9484
|
+
let existing = resolved;
|
|
9485
|
+
const tail = [];
|
|
9486
|
+
for (; ; ) {
|
|
9487
|
+
try {
|
|
9488
|
+
return path16__namespace.join(fs14__namespace.realpathSync(existing), ...tail);
|
|
9489
|
+
} catch {
|
|
9490
|
+
const parent = path16__namespace.dirname(existing);
|
|
9491
|
+
if (parent === existing) return resolved;
|
|
9492
|
+
tail.unshift(path16__namespace.basename(existing));
|
|
9493
|
+
existing = parent;
|
|
9494
|
+
}
|
|
9495
|
+
}
|
|
9496
|
+
}
|
|
8849
9497
|
function lockPathFor(stagingRoot) {
|
|
8850
9498
|
return path16__namespace.join(os__namespace.tmpdir(), `codebase-snapshot-${lockKeyFor(stagingRoot)}.lock`);
|
|
8851
9499
|
}
|
|
@@ -8870,6 +9518,24 @@ function lockOwnerIsGone(lockPath) {
|
|
|
8870
9518
|
return err.code === "ESRCH";
|
|
8871
9519
|
}
|
|
8872
9520
|
}
|
|
9521
|
+
function reclaimStaleLock(lockPath) {
|
|
9522
|
+
let before;
|
|
9523
|
+
try {
|
|
9524
|
+
before = fs14__namespace.statSync(lockPath);
|
|
9525
|
+
} catch {
|
|
9526
|
+
return true;
|
|
9527
|
+
}
|
|
9528
|
+
if (!lockOwnerIsGone(lockPath)) return false;
|
|
9529
|
+
try {
|
|
9530
|
+
const current = fs14__namespace.statSync(lockPath);
|
|
9531
|
+
const sameFile = current.ino === before.ino && current.birthtimeMs === before.birthtimeMs && current.mtimeMs === before.mtimeMs;
|
|
9532
|
+
if (!sameFile) return false;
|
|
9533
|
+
fs14__namespace.rmSync(lockPath, { force: true });
|
|
9534
|
+
return true;
|
|
9535
|
+
} catch {
|
|
9536
|
+
return false;
|
|
9537
|
+
}
|
|
9538
|
+
}
|
|
8873
9539
|
function lockFileIsAncient(lockPath) {
|
|
8874
9540
|
try {
|
|
8875
9541
|
return Date.now() - fs14__namespace.statSync(lockPath).mtimeMs > STALE_RUN_MS;
|
|
@@ -8927,7 +9593,6 @@ function swapOnce(stagingDir, outDir) {
|
|
|
8927
9593
|
throw copyErr;
|
|
8928
9594
|
}
|
|
8929
9595
|
}
|
|
8930
|
-
if (hadPrevious) fs14__namespace.rmSync(backupDir, { recursive: true, force: true });
|
|
8931
9596
|
} catch (err) {
|
|
8932
9597
|
if (hadPrevious && fs14__namespace.existsSync(backupDir)) {
|
|
8933
9598
|
if (fs14__namespace.existsSync(outDir)) fs14__namespace.rmSync(outDir, { recursive: true, force: true });
|
|
@@ -8935,6 +9600,16 @@ function swapOnce(stagingDir, outDir) {
|
|
|
8935
9600
|
}
|
|
8936
9601
|
throw err;
|
|
8937
9602
|
}
|
|
9603
|
+
if (hadPrevious) {
|
|
9604
|
+
try {
|
|
9605
|
+
fs14__namespace.rmSync(backupDir, { recursive: true, force: true });
|
|
9606
|
+
} catch (err) {
|
|
9607
|
+
process.stderr.write(
|
|
9608
|
+
`\u26A0 The previous snapshot could not be removed from ${backupDir} (${err.message}). The new snapshot is in place; delete that directory by hand when convenient.
|
|
9609
|
+
`
|
|
9610
|
+
);
|
|
9611
|
+
}
|
|
9612
|
+
}
|
|
8938
9613
|
}
|
|
8939
9614
|
function sleepSync(milliseconds) {
|
|
8940
9615
|
const buffer = new Int32Array(new SharedArrayBuffer(4));
|
|
@@ -8957,22 +9632,18 @@ function artifactWarnings(artifacts, sarif) {
|
|
|
8957
9632
|
}
|
|
8958
9633
|
return messages;
|
|
8959
9634
|
}
|
|
9635
|
+
function installHookStdoutRedirect() {
|
|
9636
|
+
return installStdoutRouter();
|
|
9637
|
+
}
|
|
8960
9638
|
function runAfterGenerate(config, manifest) {
|
|
8961
9639
|
const hook = config.hooks.afterGenerate;
|
|
8962
9640
|
if (!hook) return null;
|
|
8963
|
-
const
|
|
8964
|
-
const stderrWrite = process.stderr.write.bind(process.stderr);
|
|
8965
|
-
const hookScope = new async_hooks.AsyncLocalStorage();
|
|
8966
|
-
const patchedWrite = ((...args) => hookScope.getStore() === true ? stderrWrite(...args) : stdoutWrite(...args));
|
|
8967
|
-
const restoreStdout = () => {
|
|
8968
|
-
if (process.stdout.write === patchedWrite) process.stdout.write = stdoutWrite;
|
|
8969
|
-
};
|
|
9641
|
+
const releaseRedirect = installHookStdoutRedirect();
|
|
8970
9642
|
let returned;
|
|
8971
9643
|
try {
|
|
8972
|
-
|
|
8973
|
-
returned = hookScope.run(true, () => hook(manifest));
|
|
9644
|
+
returned = hookStdoutScope.run(true, () => hook(manifest));
|
|
8974
9645
|
} catch (err) {
|
|
8975
|
-
|
|
9646
|
+
releaseRedirect();
|
|
8976
9647
|
const message = maskPathText(err.message, config.secretRules);
|
|
8977
9648
|
manifest.warnings.push(`hooks.afterGenerate failed: ${message}`);
|
|
8978
9649
|
manifest.totals.warningCount = manifest.warnings.length;
|
|
@@ -8980,10 +9651,10 @@ function runAfterGenerate(config, manifest) {
|
|
|
8980
9651
|
}
|
|
8981
9652
|
const thenable = returned;
|
|
8982
9653
|
if (!thenable || typeof thenable.then !== "function") {
|
|
8983
|
-
|
|
9654
|
+
releaseRedirect();
|
|
8984
9655
|
return null;
|
|
8985
9656
|
}
|
|
8986
|
-
return Promise.resolve(returned).finally(
|
|
9657
|
+
return Promise.resolve(returned).finally(releaseRedirect);
|
|
8987
9658
|
}
|
|
8988
9659
|
function partitionByBudget(files, config, fingerprint) {
|
|
8989
9660
|
const budgetFor = (file) => config.budgets.byLayer[file.layer] ?? config.budgets.default;
|
|
@@ -8994,23 +9665,14 @@ function partitionByBudget(files, config, fingerprint) {
|
|
|
8994
9665
|
if (bucket) bucket.push(file);
|
|
8995
9666
|
else buckets.set(budget, [file]);
|
|
8996
9667
|
}
|
|
8997
|
-
if (buckets.size
|
|
8998
|
-
|
|
8999
|
-
return partitionFiles(files,
|
|
9668
|
+
if (buckets.size === 0) return [];
|
|
9669
|
+
if (buckets.size === 1) {
|
|
9670
|
+
return partitionFiles(files, Array.from(buckets.keys())[0], fingerprint);
|
|
9000
9671
|
}
|
|
9001
|
-
|
|
9002
|
-
|
|
9003
|
-
|
|
9004
|
-
|
|
9005
|
-
const merged = renumberParts(parts, fingerprint);
|
|
9006
|
-
const byPath = new Map(files.map((file) => [file.relativePath, file]));
|
|
9007
|
-
for (const part of merged) {
|
|
9008
|
-
for (const included of part.metadata.includedFiles) {
|
|
9009
|
-
const file = byPath.get(included.relativePath);
|
|
9010
|
-
if (file) file.part = part.filename;
|
|
9011
|
-
}
|
|
9012
|
-
}
|
|
9013
|
-
return merged;
|
|
9672
|
+
return partitionFilesForBuckets(
|
|
9673
|
+
Array.from(buckets.entries()).sort(([left], [right]) => left - right).map(([budgetBytes, bucketFiles]) => ({ files: bucketFiles, budgetBytes })),
|
|
9674
|
+
fingerprint
|
|
9675
|
+
);
|
|
9014
9676
|
}
|
|
9015
9677
|
function generateSnapshot(configInput, options) {
|
|
9016
9678
|
assertUsableConfigInput(configInput);
|
|
@@ -9042,6 +9704,7 @@ function generateSnapshot(configInput, options) {
|
|
|
9042
9704
|
return false;
|
|
9043
9705
|
}) : collection.files;
|
|
9044
9706
|
const files = [];
|
|
9707
|
+
const derivationKey = derivationSettings(config);
|
|
9045
9708
|
const monolithBodies = /* @__PURE__ */ new Map();
|
|
9046
9709
|
const skipped = maskSkipRecords([...collection.skipped], config.secretRules);
|
|
9047
9710
|
const secretFindings = [];
|
|
@@ -9054,6 +9717,7 @@ function generateSnapshot(configInput, options) {
|
|
|
9054
9717
|
const fileWarnings = [];
|
|
9055
9718
|
const oversizeSummaryPaths = [];
|
|
9056
9719
|
const previewSummaryPaths = [];
|
|
9720
|
+
const skippedSummaryImports = /* @__PURE__ */ new Map();
|
|
9057
9721
|
const layerCounts = {};
|
|
9058
9722
|
const kindCounts = {};
|
|
9059
9723
|
let totalLines = 0;
|
|
@@ -9118,8 +9782,6 @@ function generateSnapshot(configInput, options) {
|
|
|
9118
9782
|
}
|
|
9119
9783
|
const hashSha256 = sha256Hex2(buffer);
|
|
9120
9784
|
const rawContent = decoded.text;
|
|
9121
|
-
const lineCount = countLines(rawContent);
|
|
9122
|
-
totalLines += lineCount;
|
|
9123
9785
|
totalSizeBytes += stats.size;
|
|
9124
9786
|
if (decoded.encoding !== "utf-8") {
|
|
9125
9787
|
encodingNotes.push({
|
|
@@ -9133,66 +9795,25 @@ function generateSnapshot(configInput, options) {
|
|
|
9133
9795
|
const language = detectLanguage(relativePath);
|
|
9134
9796
|
bump(layerCounts, layer2);
|
|
9135
9797
|
bump(kindCounts, kind);
|
|
9136
|
-
const
|
|
9137
|
-
rawContent,
|
|
9138
|
-
|
|
9139
|
-
|
|
9140
|
-
);
|
|
9141
|
-
|
|
9142
|
-
|
|
9143
|
-
|
|
9144
|
-
|
|
9145
|
-
|
|
9146
|
-
|
|
9147
|
-
|
|
9148
|
-
|
|
9149
|
-
|
|
9150
|
-
|
|
9151
|
-
|
|
9152
|
-
|
|
9153
|
-
|
|
9154
|
-
|
|
9155
|
-
}
|
|
9156
|
-
if (isLarge && inclusionMode !== "signatures") {
|
|
9157
|
-
const preview = truncatePreview(
|
|
9158
|
-
redacted,
|
|
9159
|
-
config.previewHeadLines,
|
|
9160
|
-
config.previewTailLines
|
|
9161
|
-
);
|
|
9162
|
-
finalContent = preview.content;
|
|
9163
|
-
isPreviewTruncated = preview.isTruncated;
|
|
9164
|
-
includedLines = preview.includedLines;
|
|
9165
|
-
inclusionMode = isPreviewTruncated ? "preview" : "full";
|
|
9166
|
-
}
|
|
9167
|
-
if (config.includeMonolith && (isPreviewTruncated || inclusionMode === "signatures")) {
|
|
9168
|
-
monolithBodies.set(relativePath, redacted);
|
|
9169
|
-
}
|
|
9170
|
-
let summary;
|
|
9171
|
-
let summaryQuality;
|
|
9172
|
-
if (Buffer.byteLength(rawContent, "utf8") > SUMMARY_MAX_SOURCE_BYTES) {
|
|
9173
|
-
oversizeSummaryPaths.push(relativePath);
|
|
9174
|
-
} else if (isPreviewTruncated && hasStructuralSummary(relativePath)) {
|
|
9175
|
-
previewSummaryPaths.push(relativePath);
|
|
9176
|
-
} else {
|
|
9177
|
-
try {
|
|
9178
|
-
const outcome = summarizeFile(relativePath, rawContent);
|
|
9179
|
-
summary = outcome?.summary ? maskSummaryStrings(outcome.summary, config.secretRules) : void 0;
|
|
9180
|
-
summaryQuality = outcome?.quality;
|
|
9181
|
-
if (isSourceSummary(summary) && summary.parseErrors) {
|
|
9182
|
-
fileWarnings.push({
|
|
9183
|
-
path: relativePath,
|
|
9184
|
-
message: `Parse errors in ${maskPathText(relativePath, config.secretRules)} (${summary.parseErrors}); its summary is incomplete.`
|
|
9185
|
-
});
|
|
9186
|
-
}
|
|
9187
|
-
} catch (err) {
|
|
9188
|
-
fileWarnings.push({
|
|
9189
|
-
path: relativePath,
|
|
9190
|
-
message: `Summary skipped for ${maskPathText(relativePath, config.secretRules)}: ${err.message}`.replace(
|
|
9191
|
-
/\s+/g,
|
|
9192
|
-
" "
|
|
9193
|
-
)
|
|
9194
|
-
});
|
|
9195
|
-
}
|
|
9798
|
+
const derivation = takeDerivation(absolutePath, hashSha256, derivationKey) ?? (() => {
|
|
9799
|
+
const fresh = deriveFile(relativePath, rawContent, stats.size, config);
|
|
9800
|
+
rememberDerivation(absolutePath, hashSha256, derivationKey, fresh);
|
|
9801
|
+
return fresh;
|
|
9802
|
+
})();
|
|
9803
|
+
const { lineCount } = derivation;
|
|
9804
|
+
totalLines += lineCount;
|
|
9805
|
+
secretFindings.push(...derivation.findings);
|
|
9806
|
+
if (derivation.isLarge) largeFileCount += 1;
|
|
9807
|
+
if (derivation.summarySkip === "oversize") oversizeSummaryPaths.push(relativePath);
|
|
9808
|
+
else if (derivation.summarySkip === "preview") previewSummaryPaths.push(relativePath);
|
|
9809
|
+
if (derivation.dependentImports !== null) {
|
|
9810
|
+
skippedSummaryImports.set(relativePath, derivation.dependentImports);
|
|
9811
|
+
}
|
|
9812
|
+
if (derivation.summaryWarning !== null) {
|
|
9813
|
+
fileWarnings.push({ path: relativePath, message: derivation.summaryWarning });
|
|
9814
|
+
}
|
|
9815
|
+
if (config.includeMonolith && (derivation.isPreviewTruncated || derivation.inclusionMode === "signatures")) {
|
|
9816
|
+
monolithBodies.set(relativePath, derivation.redacted);
|
|
9196
9817
|
}
|
|
9197
9818
|
files.push({
|
|
9198
9819
|
relativePath,
|
|
@@ -9203,24 +9824,25 @@ function generateSnapshot(configInput, options) {
|
|
|
9203
9824
|
layer: layer2,
|
|
9204
9825
|
kind,
|
|
9205
9826
|
language,
|
|
9206
|
-
inclusionMode,
|
|
9207
|
-
isLarge,
|
|
9208
|
-
isPreviewTruncated,
|
|
9827
|
+
inclusionMode: derivation.inclusionMode,
|
|
9828
|
+
isLarge: derivation.isLarge,
|
|
9829
|
+
isPreviewTruncated: derivation.isPreviewTruncated,
|
|
9209
9830
|
originalLines: lineCount,
|
|
9210
|
-
includedLines,
|
|
9211
|
-
includedBytes: Buffer.byteLength(finalContent, "utf8"),
|
|
9212
|
-
summary,
|
|
9213
|
-
summaryQuality,
|
|
9831
|
+
includedLines: derivation.includedLines,
|
|
9832
|
+
includedBytes: Buffer.byteLength(derivation.finalContent, "utf8"),
|
|
9833
|
+
summary: derivation.summary,
|
|
9834
|
+
summaryQuality: derivation.summaryQuality,
|
|
9214
9835
|
...decoded.encoding === "utf-8" ? {} : { encoding: decoded.encoding },
|
|
9215
|
-
content: finalContent
|
|
9836
|
+
content: derivation.finalContent
|
|
9216
9837
|
});
|
|
9217
9838
|
}
|
|
9218
9839
|
if (changedScope !== null && changedPaths !== null && config.withDependents === true) {
|
|
9219
9840
|
const keep = new Set(changedPaths);
|
|
9220
9841
|
const resolver = buildImportResolver(files);
|
|
9221
9842
|
for (const file of files) {
|
|
9222
|
-
|
|
9223
|
-
if (
|
|
9843
|
+
const fileImports = isSourceSummary(file.summary) ? file.summary.imports : skippedSummaryImports.get(file.relativePath) ?? [];
|
|
9844
|
+
if (fileImports.length === 0) continue;
|
|
9845
|
+
if (importsChangedFile(file.relativePath, fileImports, changedPaths, resolver)) {
|
|
9224
9846
|
keep.add(file.relativePath);
|
|
9225
9847
|
}
|
|
9226
9848
|
}
|
|
@@ -9251,6 +9873,9 @@ function generateSnapshot(configInput, options) {
|
|
|
9251
9873
|
for (const path17 of [...monolithBodies.keys()]) {
|
|
9252
9874
|
if (!keep.has(path17)) monolithBodies.delete(path17);
|
|
9253
9875
|
}
|
|
9876
|
+
for (const path17 of [...skippedSummaryImports.keys()]) {
|
|
9877
|
+
if (!keep.has(path17)) skippedSummaryImports.delete(path17);
|
|
9878
|
+
}
|
|
9254
9879
|
}
|
|
9255
9880
|
if (changedScope !== null) {
|
|
9256
9881
|
for (const relativePath of unchangedPaths) {
|
|
@@ -9286,7 +9911,7 @@ function generateSnapshot(configInput, options) {
|
|
|
9286
9911
|
const shown = oversizeSummaryPaths.slice(0, ENCODING_PATHS_SHOWN).map((entry) => maskPathText(entry, config.secretRules));
|
|
9287
9912
|
const hidden = oversizeSummaryPaths.length - ENCODING_PATHS_SHOWN;
|
|
9288
9913
|
warnings.push(
|
|
9289
|
-
`${oversizeSummaryPaths.length} file(s) exceed ${Math.round(
|
|
9914
|
+
`${oversizeSummaryPaths.length} file(s) exceed ${Math.round(SUMMARY_MAX_SOURCE_BYTES2 / (1024 * 1024))} MB and were not parsed for a structural summary: ${shown.join(", ")}${hidden > 0 ? ` and ${hidden} more` : ""}. Their bodies are emitted as head/tail previews.`
|
|
9290
9915
|
);
|
|
9291
9916
|
}
|
|
9292
9917
|
for (const [encoding, bucket] of encodingBuckets) {
|
|
@@ -9310,11 +9935,13 @@ function generateSnapshot(configInput, options) {
|
|
|
9310
9935
|
).join("\n")
|
|
9311
9936
|
);
|
|
9312
9937
|
const fullContents = /* @__PURE__ */ new Map();
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
|
|
9317
|
-
|
|
9938
|
+
if (config.includeMonolith) {
|
|
9939
|
+
for (const file of files) {
|
|
9940
|
+
fullContents.set(
|
|
9941
|
+
file.relativePath,
|
|
9942
|
+
monolithBodies.get(file.relativePath) ?? file.content ?? ""
|
|
9943
|
+
);
|
|
9944
|
+
}
|
|
9318
9945
|
}
|
|
9319
9946
|
const parts = partitionByBudget(files, config, fingerprint);
|
|
9320
9947
|
const truncatedPreviewCount = files.filter((file) => file.isPreviewTruncated).length;
|
|
@@ -9575,7 +10202,7 @@ function generateSnapshot(configInput, options) {
|
|
|
9575
10202
|
};
|
|
9576
10203
|
shipHookState();
|
|
9577
10204
|
if (pendingHook) {
|
|
9578
|
-
pendingHook.then(
|
|
10205
|
+
const tracked = pendingHook.then(
|
|
9579
10206
|
() => {
|
|
9580
10207
|
try {
|
|
9581
10208
|
shipHookState();
|
|
@@ -9594,8 +10221,10 @@ function generateSnapshot(configInput, options) {
|
|
|
9594
10221
|
} catch {
|
|
9595
10222
|
}
|
|
9596
10223
|
}
|
|
9597
|
-
)
|
|
9598
|
-
|
|
10224
|
+
).finally(() => {
|
|
10225
|
+
if (pendingHooks.get(outDir) === tracked) pendingHooks.delete(outDir);
|
|
10226
|
+
});
|
|
10227
|
+
pendingHooks.set(outDir, tracked);
|
|
9599
10228
|
}
|
|
9600
10229
|
return manifest;
|
|
9601
10230
|
} catch (err) {
|
|
@@ -9613,12 +10242,12 @@ function generateSnapshot(configInput, options) {
|
|
|
9613
10242
|
}
|
|
9614
10243
|
var pendingHooks = /* @__PURE__ */ new Map();
|
|
9615
10244
|
async function generateSnapshotAsync(configInput, options) {
|
|
9616
|
-
|
|
10245
|
+
assertUsableConfigInput(configInput);
|
|
9617
10246
|
const config = isResolvedConfig(configInput) ? normalizeResolvedConfig(configInput) : resolveConfig(configInput ?? null);
|
|
10247
|
+
const manifest = generateSnapshot(config, options);
|
|
9618
10248
|
const { outDir } = resolveSafeOutputDirs(config);
|
|
9619
10249
|
const pending = pendingHooks.get(outDir);
|
|
9620
10250
|
if (pending !== void 0) {
|
|
9621
|
-
pendingHooks.delete(outDir);
|
|
9622
10251
|
await pending.catch(() => void 0);
|
|
9623
10252
|
}
|
|
9624
10253
|
return manifest;
|