From dbb8ea4a5b8b21dd6e69be9bf48b9bd7ea7ad877 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Apr 2026 06:12:55 +0000 Subject: [PATCH] refactor: make cache entry updates immutable touchCacheEntry() now returns a new object via spread instead of mutating the entry in-place. CacheManager.replaceCards() likewise creates a new entry object instead of mutating existing fields. Update tests to assert the new immutable semantics (returned value differs from input by reference). https://claude.ai/code/session_016QvEfqw6YZ3RjwBHrJ4w8S --- src/cache-manager.ts | 19 +++++++++++-------- src/cache.ts | 3 +-- tests/main.test.js | 6 ++++-- tests/modules.test.js | 5 +++-- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/cache-manager.ts b/src/cache-manager.ts index 90b5c63..5a3ea6e 100644 --- a/src/cache-manager.ts +++ b/src/cache-manager.ts @@ -131,14 +131,17 @@ export class CacheManager { const entry = this.cache[filePath]; if (!entry) return false; const now = new Date().toISOString(); - entry.cards = (cards || []).map((card: ResolvedCard) => ({ - title: card.title, - anchor: card.anchor, - gist: card.gist, - bullets: card.bullets || [], - })); - entry.updatedAt = now; - entry.lastAccessedAt = now; + this.cache[filePath] = { + ...entry, + cards: (cards || []).map((card: ResolvedCard) => ({ + title: card.title, + anchor: card.anchor, + gist: card.gist, + bullets: card.bullets || [], + })), + updatedAt: now, + lastAccessedAt: now, + }; await this.save(); return true; } diff --git a/src/cache.ts b/src/cache.ts index 3718f9a..6b8b846 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -4,8 +4,7 @@ import type { CacheEntry } from './types'; export function touchCacheEntry(entry: CacheEntry | null, now?: string): CacheEntry | null { if (!entry) return null; - entry.lastAccessedAt = now || new Date().toISOString(); - return entry; + return { ...entry, lastAccessedAt: now || new Date().toISOString() }; } export function serializeCacheFile(entries: Record): string { diff --git a/tests/main.test.js b/tests/main.test.js index 84fd7cd..387b5bd 100644 --- a/tests/main.test.js +++ b/tests/main.test.js @@ -281,8 +281,10 @@ assert.deepStrictEqual(t.folderPathsForTarget('Reading'), ['Reading']); assert.deepStrictEqual(t.folderPathsForTarget(''), []); const untouchedCacheEntry = { generatedAt: '2024-01-01T00:00:00.000Z' }; assert.strictEqual(t.touchCacheEntry(null), null); -assert.strictEqual(t.touchCacheEntry(untouchedCacheEntry, '2024-01-05T00:00:00.000Z'), untouchedCacheEntry); -assert.strictEqual(untouchedCacheEntry.lastAccessedAt, '2024-01-05T00:00:00.000Z'); +const touchedEntry = t.touchCacheEntry(untouchedCacheEntry, '2024-01-05T00:00:00.000Z'); +assert.notStrictEqual(touchedEntry, untouchedCacheEntry, 'touchCacheEntry returns new object'); +assert.strictEqual(touchedEntry.lastAccessedAt, '2024-01-05T00:00:00.000Z', 'touched entry has updated timestamp'); +assert.strictEqual(untouchedCacheEntry.lastAccessedAt, undefined, 'original entry not mutated'); assert.strictEqual(t.shouldConfirmRegenerate({ updatedAt: '2024-01-05T00:00:00.000Z' }, true), true); assert.strictEqual(t.shouldConfirmRegenerate({ updatedAt: '2024-01-05T00:00:00.000Z' }, false), false); assert.strictEqual(t.shouldConfirmRegenerate({ generatedAt: '2024-01-01T00:00:00.000Z' }, true), false); diff --git a/tests/modules.test.js b/tests/modules.test.js index 77e251e..42a2c72 100644 --- a/tests/modules.test.js +++ b/tests/modules.test.js @@ -90,8 +90,9 @@ assert.deepStrictEqual( assert.strictEqual(t.touchCacheEntry(null), null, 'touchCacheEntry on null returns null'); const entry = { generatedAt: '2024-01-01T00:00:00.000Z' }; -t.touchCacheEntry(entry, '2024-06-01T00:00:00.000Z'); -assert.strictEqual(entry.lastAccessedAt, '2024-06-01T00:00:00.000Z', 'touchCacheEntry sets lastAccessedAt'); +const touched = t.touchCacheEntry(entry, '2024-06-01T00:00:00.000Z'); +assert.strictEqual(touched.lastAccessedAt, '2024-06-01T00:00:00.000Z', 'touchCacheEntry sets lastAccessedAt on returned entry'); +assert.strictEqual(entry.lastAccessedAt, undefined, 'touchCacheEntry does not mutate original entry'); const serialized = t.serializeCacheFile({ 'a.md': { cards: [] } }); const parsed = JSON.parse(serialized);