Skip to content

feat(cache): add pluggable TTL caching layer to GuildPassClient#62

Open
AlAfiz wants to merge 1 commit into
Adamantine-guild:mainfrom
AlAfiz:feat/ttl-cache-layer
Open

feat(cache): add pluggable TTL caching layer to GuildPassClient#62
AlAfiz wants to merge 1 commit into
Adamantine-guild:mainfrom
AlAfiz:feat/ttl-cache-layer

Conversation

@AlAfiz

@AlAfiz AlAfiz commented Jun 20, 2026

Copy link
Copy Markdown

Ran command: git checkout -b feat/ttl-cache-layer
Ran command: git commit -m "feat(cache): add pluggable TTL caching layer to GuildPassClient"
Ran command: git push -u origin feat/ttl-cache-layer

## feat(cache): Pluggable TTL Caching Layer for GuildPassClient

### Overview
Implements an optional, transparent caching layer for `GuildPassClient` that memoizes all safe read operations without changing any public API signatures.

---

### What Changed

**`src/cache/cache.types.ts`** *(new)*
- `CacheAdapter` — generic interface (`get<T>`, `set<T>`, `delete`, `clear`) designed to be implemented by any backend (in-memory, Redis, etc.)
- `InMemoryCacheAdapter` — zero-dependency default implementation backed by a `Map` with `Date.now()`-based TTL eviction

**`src/config/sdkConfig.ts`**
- Added `cache?: CacheAdapter` and `cacheTtl?: number` to `GuildPassClientConfig`

**`src/client/GuildPassClient.ts`**
- When a `cache` adapter is provided, each read service (`access`, `membership`, `roles`, `guilds`) is replaced at construction with a prototype-delegating proxy (`Object.create`), keeping every method signature identical to callers
- Deterministic composite cache keys (e.g. `access:checkAccess:{guildId}:{resourceId}:{walletAddress}`)
- New public invalidation helpers: `invalidateGuildCache(guildId)`, `invalidateWalletCache(walletAddress)`, `clearCache()` — all no-ops when no adapter is configured

**`src/index.ts`**
- Re-exports `CacheAdapter` and `InMemoryCacheAdapter` from the package root

**`tests/cache.test.ts`** *(new)*
- 16 tests across two suites covering: cold misses, typed cache hits, TTL expiry via `vi.useFakeTimers()`, no-TTL persistence, delete/clear, guild-scoped invalidation, no-adapter safety, and custom adapter injection (Redis stub pattern)

**`README.md`**
- New **⚡ Caching** section documenting `InMemoryCacheAdapter`, a complete Redis adapter example, and all three invalidation helpers
- Removed the now-shipped roadmap item

---

### Key Design Decisions
- **Zero breaking changes** — caching is entirely opt-in; omitting `cache` from config preserves existing behaviour exactly
- **Adapter pattern** — decouples the SDK from any specific cache implementation; Redis, Memcached, or any async store can be plugged in with ~15 lines
- **No new runtime dependencies**`InMemoryCacheAdapter` uses only native `Map` and `Date.now()`

---

### Testing

Tests 16 passed (16)

All TTL assertions use fake timers — no real delays in the test suite.

closes #15

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a pluggable caching layer

1 participant