From 6759adf53f05bf45230cb210fb127da60bc81358 Mon Sep 17 00:00:00 2001 From: LeSingh1 Date: Thu, 11 Jun 2026 23:33:19 -0700 Subject: [PATCH] fix(utils): prefix watch callback strips prefix and filters external events When calling watch() on a prefixStorage, the callback received full storage keys (including the prefix) and was triggered for events on keys outside the namespace. Override watch on the prefixed storage so the callback only fires for matching keys and receives the key relative to the prefix, consistent with how getKeys and getItem work. --- src/utils.ts | 7 +++++++ test/storage.test.ts | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/utils.ts b/src/utils.ts index 5a4c011a1..ffe1a021f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -76,6 +76,13 @@ export function prefixStorage( return storage.setItems(prefixedItems, commonOptions); }; + nsStorage.watch = (callback) => + storage.watch((event, key) => { + if (key.startsWith(base)) { + callback(event, key.slice(base.length)); + } + }); + return nsStorage; } diff --git a/test/storage.test.ts b/test/storage.test.ts index 2cef5f54d..0f9ffc628 100644 --- a/test/storage.test.ts +++ b/test/storage.test.ts @@ -274,4 +274,26 @@ describe("Regression", () => { { key: "key2", value: "value2" }, ]); }); + + it("prefixStorage watch strips prefix and filters events", async () => { + const storage = createStorage(); + const pStorage = prefixStorage(storage, "ns"); + + const events: { event: string; key: string }[] = []; + const unwatch = await pStorage.watch((event, key) => { + events.push({ event, key }); + }); + + await pStorage.setItem("foo", "bar"); + await storage.setItem("other:x", "y"); + + // give the synchronous memory-driver onChange time to flush + await new Promise((resolve) => setTimeout(resolve, 10)); + await unwatch(); + + // key delivered to the callback should be relative (no "ns:" prefix) + expect(events).toContainEqual({ event: "update", key: "foo" }); + // events for keys outside the prefix must be filtered out + expect(events.every((e) => !e.key.startsWith("other"))).toBe(true); + }); });