Skip to content

Commit 71d5f11

Browse files
pi0claude
andcommitted
feat(storage): nullish set deletes entry instead of storing dead weight
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ff516cb commit 71d5f11

4 files changed

Lines changed: 32 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ src/
3737
### Storage (`storage.ts`)
3838

3939
- `StorageInterface` — minimal `get`/`set` with optional TTL
40+
- Setting a nullish value (`null`/`undefined`) via `set` deletes the entry instead of storing dead weight
4041
- `createMemoryStorage()` — in-memory Map-based implementation with TTL expiry
4142
- `useStorage()` / `setStorage()` — global singleton, lazy-inits to memory storage
4243

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ const redisStorage: StorageInterface = {
118118
return JSON.parse(await redis.get(key));
119119
},
120120
set: async (key, value, opts) => {
121+
// Setting null/undefined deletes the entry (used for cache invalidation)
122+
if (value === null || value === undefined) {
123+
await redis.del(key);
124+
return;
125+
}
121126
await redis.set(key, JSON.stringify(value), opts?.ttl ? { EX: opts.ttl } : undefined);
122127
},
123128
};

src/storage.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ export function createMemoryStorage(): StorageInterface {
1919
return entry.value as any;
2020
},
2121
set(key, value, opts) {
22+
if (value === null || value === undefined) {
23+
map.delete(key);
24+
return;
25+
}
2226
map.set(key, {
2327
value,
2428
expires: opts?.ttl ? Date.now() + opts.ttl * 1000 : undefined,

test/index.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,28 @@ describe("storage", () => {
518518
setStorage(custom);
519519
expect(useStorage()).toBe(custom);
520520
});
521+
522+
it("set with null deletes the entry", () => {
523+
const storage = createMemoryStorage();
524+
storage.set("key", "hello");
525+
expect(storage.get("key")).toBe("hello");
526+
storage.set("key", null);
527+
expect(storage.get("key")).toBeNull();
528+
});
529+
530+
it("set with undefined deletes the entry", () => {
531+
const storage = createMemoryStorage();
532+
storage.set("key", "hello");
533+
expect(storage.get("key")).toBe("hello");
534+
storage.set("key", undefined);
535+
expect(storage.get("key")).toBeNull();
536+
});
537+
538+
it("set null on nonexistent key is a no-op", () => {
539+
const storage = createMemoryStorage();
540+
storage.set("nonexistent", null);
541+
expect(storage.get("nonexistent")).toBeNull();
542+
});
521543
});
522544

523545
describe("defineCachedHandler", () => {

0 commit comments

Comments
 (0)