9.6 KiB
9.6 KiB
Tasks: enterprise-cache
Change ID: enterprise-cache
Package: @suaveplan/cache
Target path: universal/database/cache
Prerequisite packages on Verdaccio: @suaveplan/error, @suaveplan/types, @suaveplan/events
1. Scaffold
- Create directory
universal/database/cache/withsrc/,tests/,src/__benchmarks__/, and all feature subdirectories per the layout inproposal.md. - Write
package.jsonat version0.1.0with:"type": "module","sideEffects": falseexportsmap: top-level"."barrel plus subpaths./abstract/abstract-cache,./abstract/namespaced-cache,./memory/memory-cache,./factory/cache-factory,./types/common,./types/schemasdependencies:@suaveplan/error workspace:^,@suaveplan/types workspace:^,@suaveplan/events workspace:^,zod catalog:forms,@opentelemetry/api catalog:observability,tslib catalog:builddevDependencies:vitest catalog:testing,@vitest/coverage-istanbul catalog:testing,fast-check catalog:testing,mitata catalog:testing,@suaveplan/testing workspace:^
- Write
tsconfig.jsonextending@suaveplan/typescript-config/library.json. - Write
vitest.config.tsusingcreateTestConfig()from@suaveplan/testing/config(pool:forks). - Add
lint,typecheck,test,test:coverage,build,docs:check,benchscripts topackage.json.
2. Types + errors
2a. Common types (src/types/common.ts + src/types/common.md)
- Define all public TS interfaces:
CacheEntry,CacheStats,CacheEvictionPolicy,CacheOptions,CacheSetOptions,CacheWrapOptions,CacheTransformHook,CacheMultiGetResult,CacheMultiSetEntry,CacheProviderType,CacheProviderConfig,CacheProviderConfigFor,CacheProviderImplementation,CacheFactoryConfig,CacheProviderRegistry,CacheEvents. - Add genesis-only optional fields to
CacheOptions:clock?: Clock(from@suaveplan/types),events?: EventBusLike(from@suaveplan/types). - Add genesis-only optional field to
CacheWrapOptions:signal?: AbortSignal. - Write
src/types/common.md(≥200 words, both simple + advanced examples, API reference for every type).
2b. Error codes (src/error-codes.ts)
- Import
CodeMetadata,ErrorCodes,SuaveplanErrorfrom@suaveplan/error. - Define
CacheErrorCodeunion — six codes:CACHE_KEY_INVALID,CACHE_CAPACITY_EXCEEDED,CACHE_PROVIDER_NOT_FOUND,CACHE_SET_FAILED,CACHE_GET_FAILED,CACHE_SERIALIZATION_FAILED. - Define
CACHE_ERROR_METADATAwithhttpStatus,severity,retryable,userMessageKey,category,descriptionfor each code. - Export
registerCacheErrorCodes()— callsErrorCodes.registerMany(CACHE_ERROR_METADATA). Idempotent. - Call
registerCacheErrorCodes()as an eager side-effect at module scope. - Export
CacheErrorextendingSuaveplanErrorcarryingCacheErrorCodediscriminant.
2c. Zod schemas (src/types/schemas.ts + src/types/schemas.md)
- Write
CacheOptionsSchema,CacheSetOptionsSchema,CacheWrapOptionsSchema,CacheFactoryConfigSchema,CacheTransformHookSchema. - Export inferred TypeScript types as companions where they differ from hand-written types.
- Write
src/types/schemas.md(≥200 words).
3. Constants (src/constants/index.ts)
- Export
DEFAULT_MAX_SIZE = 1000,DEFAULT_TTL_SECONDS = 3600,DEFAULT_EVICTION_POLICY: CacheEvictionPolicy = 'lru',DEFAULT_EXPIRATION_INTERVAL_MS = 60_000.
4. Abstract base (src/abstract/abstract-cache/)
- Implement
AbstractCache<T>abstract class:- Abstract methods:
get,set,delete,clear,has,getStats,getMultiple,setMultiple. - Concrete
wrap(key, fn, options?):- Check cache; if hit and not stale, return cached value.
- If stale-while-revalidate: return stale value, trigger background refresh (respects
options.signal). - If miss or expired: call
fn(), store result ifshouldCache(value)returns true. - OTel: span
cache.wrap, histogramcache.wrap_duration_ms.
- All methods protected by try/catch; on internal error throw
CacheErrorwith appropriate code.
- Abstract methods:
- Write
abstract-cache.test.ts— testwraplogic via a concrete in-memory subclass stub (no vi.mock). - Write
abstract-cache.md(≥200 words).
4b. NamespacedCache (src/abstract/namespaced-cache/)
- Implement
NamespacedCache<T>wrapping anAbstractCache<T>:- Prefixes all keys with
"${namespace}:". - Delegates all methods to the wrapped cache.
getStats()returns stats scoped to the namespace where possible.
- Prefixes all keys with
- Write
namespaced-cache.test.ts: key isolation between namespaces, clear only clears own namespace. - Write
namespaced-cache.md(≥200 words).
5. Memory cache (src/memory/memory-cache/)
- Implement
MemoryCache<T>extendingAbstractCache<T>:- Internal store:
Map<string, CacheEntry<T>>. - Injectable
clock?: Clock— default{ now: () => Date.now() }. - Injectable
events?: EventBusLike— emitCacheEventson every operation. - LRU eviction: doubly-linked list + map for O(1) evict-on-capacity.
- LFU eviction: frequency map + bucket list for O(1) evict-on-capacity.
- FIFO eviction: insertion-order queue.
- TTL eviction: periodic sweep via
setIntervalusing injectable clock. setIntervalis cleared onclear()and optionally on explicitdispose().getStats()returns live counters (noDate.now()— useclock.now()).- Stale-while-revalidate: tracked in
CacheEntrymetadata. - OTel: counter
cache.get_total(hit/miss),cache.set_total,cache.delete_total,cache.clear_total, gaugecache.size, gaugecache.hit_rate, countercache.evict_total(withreasonattribute).
- Internal store:
- Write
memory-cache.test.ts(≤500 lines): get/set/delete/clear/has, TTL expiry (advance injectable clock), LRU eviction, LFU eviction, stats accuracy, multi-get/set, namespace isolation, event emission. - Write
memory-cache.md(≥200 words, both basic + advanced example with injectable clock).
6. Factory (src/factory/cache-factory/)
- Implement
CacheFactoryclass with static methods:create,register,unregister,has,providers. - Implement free functions:
createCache,createCacheWithConfig,registerCacheProvider,unregisterCacheProvider,hasCacheProvider,getCacheProviders,clearCacheProviders. CacheFactory.createvalidates the type against the registry; throwsCacheError(CACHE_PROVIDER_NOT_FOUND)for unknown types.- Write
cache-factory.test.ts: register/unregister, create known + unknown type,clearCacheProvidersresets state. - Write
cache-factory.md(≥200 words).
Registry (src/factory/registry/)
registry.ts— auto-registersMemoryCacheas thememoryprovider on import (side-effect).- Write
registry.mdexplaining the auto-registration side-effect and tree-shaking implications.
7. Public barrel (src/index.ts)
- Re-export:
AbstractCache,NamespacedCache,MemoryCache,CacheFactory, all free factory functions, all types, error-codes, constants. - Import
./factory/registryto trigger built-in provider registration on package load.
8. Property-based tests (tests/property.test.ts)
Use fast-check. All timeouts: { timeout: 30000 }.
- LRU eviction invariant: fill cache of capacity N (N in 5–20), set N+1 items with sequential access order; assert LRU item is evicted.
- TTL expiry: set items with TTL T (T in 1–100 ms), advance injectable clock by T+1; assert
has()returns false. getMultiplecompleteness: for M items set and K keys queried, assertfound.size + missing.length === K.- Stats accuracy: for N random get/set sequences, assert
stats.hits + stats.misses === total get calls.
9. Benchmarks (src/__benchmarks__/cache.bench.ts)
Use mitata.
get_hot_1k: 1000get()on LRU cache of 500 pre-populated items (all hits).set_1k: 1000set()with TTL = 60s.wrap_cold_100: 100wrap()all misses, factory returnsPromise.resolve(1).lru_eviction_pressure: fill 2x capacity, measure throughput of evictions.- Add
benchscript topackage.json. - Add placeholder benchmark table to
README.md.
10. Docs
src/types/common.mdsrc/types/schemas.mdsrc/abstract/abstract-cache/abstract-cache.mdsrc/abstract/namespaced-cache/namespaced-cache.mdsrc/memory/memory-cache/memory-cache.mdsrc/factory/cache-factory/cache-factory.mdsrc/factory/registry/registry.md- Root
universal/database/cache/README.md— purpose, installation, quick-start, dep table, full API symbol list, benchmarks table.
11. Verify and publish
- Run
bunx turbo lint --filter=@suaveplan/cache— zero errors. - Run
bunx turbo typecheck --filter=@suaveplan/cache— zero errors. - Run
bunx turbo test --filter=@suaveplan/cache— all tests pass. - Run
bunx turbo test:coverage --filter=@suaveplan/cache— 100% on all touched modules. - Run
bun run docs:check --filter=@suaveplan/cache— zero errors. - Confirm no
TODO,FIXME,@deprecated, untypedany,ts-ignore, or.skipinsrc/ortests/. - Confirm
"sideEffects": falseinpackage.json. - Run
bun scripts/publish.ts --package @suaveplan/cache. - Update
CLAUDE.mdpackage listing to include@suaveplan/cache. - Archive: move
openspec/changes/enterprise-cache/toopenspec/archive/2026-04-24-enterprise-cache/and fold spec delta intoopenspec/specs/database/spec.md.