From 59105628f4692549341ee262a1342dac78561bf9 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Wed, 10 Jun 2026 23:53:00 +0530 Subject: [PATCH] fix: Treat a stored JSON null as a missing persisted value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit readJSON returned any successfully parsed value, so a localStorage entry holding the literal "null" came back as null instead of the fallback — and a null inputState crashes module init on every load until storage is cleared. The deleted MacFJA persist layer guarded this with `null !== initialValue`; restore that behavior. Co-Authored-By: Claude Fable 5 --- src/lib/util/persist.svelte.test.ts | 12 ++++++++++++ src/lib/util/persist.svelte.ts | 7 ++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/lib/util/persist.svelte.test.ts b/src/lib/util/persist.svelte.test.ts index 3776bb54..fa85c0a5 100644 --- a/src/lib/util/persist.svelte.test.ts +++ b/src/lib/util/persist.svelte.test.ts @@ -22,6 +22,12 @@ describe('readJSON', () => { window.localStorage.setItem('legacy', 'undefined'); expect(readJSON('legacy', 'fallback')).toBe('fallback'); }); + + it('returns the fallback when the stored value parses to null', () => { + // The pre-runes persistence layer treated a stored null as absent. + window.localStorage.setItem('legacy-null', 'null'); + expect(readJSON('legacy-null', 'fallback')).toBe('fallback'); + }); }); describe('writeJSON', () => { @@ -51,4 +57,10 @@ describe('persisted', () => { expect(counter.value).toBe(42); expect(window.localStorage.getItem('counter')).toBe('42'); }); + + it('uses the initial value when storage holds a literal null', () => { + window.localStorage.setItem('settings', 'null'); + const settings = persisted('settings', { theme: 'default' }); + expect(settings.value).toEqual({ theme: 'default' }); + }); }); diff --git a/src/lib/util/persist.svelte.ts b/src/lib/util/persist.svelte.ts index 5204af25..a8c8e0fb 100644 --- a/src/lib/util/persist.svelte.ts +++ b/src/lib/util/persist.svelte.ts @@ -15,7 +15,12 @@ export const readJSON = (key: string, fallback: T): T => { } try { const raw = window.localStorage.getItem(key); - return raw === null ? fallback : (JSON.parse(raw) as T); + if (raw === null) { + return fallback; + } + // A stored literal "null" means the value is absent: the pre-runes + // persistence layer never wrote null and treated it as missing. + return (JSON.parse(raw) as T) ?? fallback; } catch { return fallback; }