Skip to content

Commit 2217947

Browse files
authored
fix(init): add size guard and deduplicate JSON minification in preReadCommonFiles (#713)
## Summary Two fixes for `preReadCommonFiles`: 1. **Per-file size guard** -- Stat files before reading and skip anything over `MAX_FILE_BYTES` (256KB). Prevents memory spikes from large files like `Gemfile.lock` that could eat the entire 512KB budget in one read. 2. **Deduplicate JSON minification** -- Extract `minifyJson()` helper used by both `preReadCommonFiles` and `readSingleFile`. Prevents the two identical `JSON.stringify(JSON.parse())` blocks from drifting out of sync. ## Test plan - [x] All 68 local-ops tests pass - [x] Typecheck clean - [x] Lint clean Made with [Cursor](https://cursor.com)
1 parent f997526 commit 2217947

File tree

1 file changed

+15
-11
lines changed

1 file changed

+15
-11
lines changed

src/lib/init/local-ops.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,15 @@ function prettyPrintJson(content: string, indent: JsonIndent): string {
8080
}
8181
}
8282

83+
/** Strip whitespace/formatting from JSON. Returns input unchanged if not valid JSON. */
84+
function minifyJson(content: string): string {
85+
try {
86+
return JSON.stringify(JSON.parse(content));
87+
} catch {
88+
return content;
89+
}
90+
}
91+
8392
/**
8493
* Patterns that indicate shell injection. Commands run via `spawn` (no shell),
8594
* so these have no runtime effect — they are defense-in-depth against command
@@ -387,13 +396,13 @@ export async function preReadCommonFiles(
387396
}
388397
try {
389398
const absPath = path.join(directory, filePath);
399+
const stat = await fs.promises.stat(absPath);
400+
if (stat.size > MAX_FILE_BYTES) {
401+
continue;
402+
}
390403
let content = await fs.promises.readFile(absPath, "utf-8");
391404
if (filePath.endsWith(".json")) {
392-
try {
393-
content = JSON.stringify(JSON.parse(content));
394-
} catch {
395-
// Not valid JSON — send as-is
396-
}
405+
content = minifyJson(content);
397406
}
398407
if (totalBytes + content.length <= MAX_PREREAD_TOTAL_BYTES) {
399408
cache[filePath] = content;
@@ -540,13 +549,8 @@ async function readSingleFile(
540549
content = await fs.promises.readFile(absPath, "utf-8");
541550
}
542551

543-
// Minify JSON files by stripping whitespace/formatting
544552
if (filePath.endsWith(".json")) {
545-
try {
546-
content = JSON.stringify(JSON.parse(content));
547-
} catch {
548-
// Not valid JSON (truncated, JSONC, etc.) — send as-is
549-
}
553+
content = minifyJson(content);
550554
}
551555

552556
return content;

0 commit comments

Comments
 (0)