diff --git a/.jules/bolt.md b/.jules/bolt.md index 69da333..427be48 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -64,3 +64,6 @@ ## 2024-05-15 - Electron Main Process File Operations **Learning:** Using synchronous `fs` methods (`readFileSync`, `existsSync`, `statSync`) in the Electron main process (e.g., `preview-server.ts`) severely blocks the Node.js event loop, preventing concurrent processing of HTTP requests and potentially causing UI thread starvation or micro-stuttering. Furthermore, buffering entire large files (e.g., `readFileSync`) consumes unnecessary heap memory. **Action:** Always default to async file operations (`fs.promises`) and stream piping (`createReadStream(..).pipe(res)`) when dealing with HTTP response bodies in the Electron main process to maintain responsiveness and minimize memory footprint. +## 2025-05-17 - Replace O(N²) array search with O(N) Set lookup in session listing +**Learning:** Using `array.some(...)` with string operations (like `replace()`) inside a loop over files creates an unnecessary O(N²) performance bottleneck, particularly when reading directories with many files (like the sessions history directory). +**Action:** When filtering files against another list of files, pre-compute a `Set` of base names outside the loop for O(1) lookups, changing the overall time complexity from O(N²) to O(N). diff --git a/packages/core/src/session-manager.ts b/packages/core/src/session-manager.ts index af6412b..226eb1e 100644 --- a/packages/core/src/session-manager.ts +++ b/packages/core/src/session-manager.ts @@ -556,6 +556,8 @@ export class SessionManager { // Collect legacy JSON sessions const jsonFiles = files.filter((f) => f.endsWith('.json')); + // ⚡ Bolt: Pre-compute Set for O(1) lookups instead of O(N^2) array.some() string replacement in the inner loop + const jsonlBaseNames = new Set(jsonlFiles.map((f) => f.slice(0, -6))); const CONCURRENCY_LIMIT = 20; for (let i = 0; i < jsonFiles.length; i += CONCURRENCY_LIMIT) { const chunk = jsonFiles.slice(i, i + CONCURRENCY_LIMIT); @@ -566,7 +568,7 @@ export class SessionManager { const raw = await fs.readFile(fullPath, 'utf-8'); const data = JSON.parse(raw) as ChatSession; // Skip if already have a JSONL version - if (jsonlFiles.some((jf) => jf.replace('.jsonl', '') === file.replace('.json', ''))) return; + if (jsonlBaseNames.has(file.slice(0, -5))) return; const { messages: _messages, stats: _stats, ...meta } = data; metas.push(meta); } catch {