Files
2026-05-27 23:29:53 +01:00

178 lines
6.6 KiB
Markdown

# OpenSpec Change: enterprise-browser-cache-browser
- **Change ID**: enterprise-browser-cache-browser
- **Package**: `@suaveplan/cache-browser`
- **Category**: browser
- **Target path**: `browser/storage/cache-browser`
- **Status**: IN PROGRESS
- **Date**: 2026-04-24
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/cache-browser.md`
---
## Summary
Deliver `@suaveplan/cache-browser` on the genesis branch at enterprise quality. The package provides browser-side cache implementations (`LocalStorageCache`, `SessionStorageCache`, `IndexedDBCache`) plugging into `@suaveplan/cache`, plus quota utilities, pluggable serializers, cross-tab invalidation via `@suaveplan/broadcast-channel`, pluggable eviction strategies, and OTel metrics. Auto-registration contract and `sideEffects` declaration are aligned.
---
## Current-state audit (genesis)
Fresh build — does not exist on genesis yet.
---
## Available workspace deps on genesis
| Package | Role |
|---|---|
| `@suaveplan/cache` (`workspace:^`) | `Cache` interface, `CacheFactory` |
| `@suaveplan/events` (`workspace:^`) | Storage-event multiplexer |
| `@suaveplan/error` (`workspace:^`) | Typed cache errors |
| `@suaveplan/types` (`workspace:^`) | `LoggerLike` |
| `@suaveplan/broadcast-channel` (`workspace:^`) | Cross-tab invalidation |
| `@suaveplan/crypto` (`workspace:^`) | Optional AES-GCM encryption transform |
| `@suaveplan/schema-registry` (`workspace:^`) | Register cache config schemas |
| `zod` (`catalog:forms`) | Config schemas |
| `@opentelemetry/api` (`catalog:observability`) | Cache hit/miss/eviction metrics |
| `tslib` (`catalog:build`) | Runtime helpers |
Dev: `vitest`, `@vitest/coverage-istanbul`, `fake-indexeddb` (`catalog:testing`), `@playwright/test`, `@suaveplan/testing`.
---
## Preserved public API surface
```ts
// Three cache implementations
class LocalStorageCache implements Cache { constructor(config: LocalStorageCacheConfig) }
class SessionStorageCache implements Cache { constructor(config: SessionStorageCacheConfig) }
class IndexedDBCache implements Cache { constructor(config: IndexedDBCacheConfig) }
// Subpath exports
// @suaveplan/cache-browser/localstorage
// @suaveplan/cache-browser/sessionstorage
// @suaveplan/cache-browser/indexeddb
// @suaveplan/cache-browser/register (explicit auto-registration)
// Quota utilities
function getStorageQuota(): Promise<StorageQuota>
function isStorageNearQuota(threshold?: number): Promise<boolean>
function estimateStorageSize(): Promise<number>
function handleQuotaExceeded(cache: Cache, strategy: EvictionStrategy): Promise<void>
function isStorageAvailable(type: 'localStorage' | 'sessionStorage'): boolean
// Serialization
function defaultSerializer<T>(value: T): string
function defaultDeserializer<T>(raw: string): T
function isSerializable(value: unknown): boolean
function safeDeserialize<T>(raw: string): T | undefined
// Storage events
class StorageEventHandler { ... }
const globalStorageEventHandler: StorageEventHandler
// Config types
interface LocalStorageCacheConfig { namespace: string; ttl?: number; serializer?: Serializer<unknown> }
interface SessionStorageCacheConfig { namespace: string; ttl?: number; serializer?: Serializer<unknown> }
interface IndexedDBCacheConfig { dbName: string; storeName: string; ttl?: number; schemaVersion?: number; serializer?: Serializer<unknown> }
```
---
## New additions for genesis
### 1. `sideEffects` contract
Auto-registration moves to explicit `@suaveplan/cache-browser/register` subpath; `"sideEffects": false` is then accurate for all other entry points. The `register` subpath is a `sideEffects: ["./dist/register.js"]` entry.
### 2. Pluggable `Serializer<T>` interface
```ts
interface Serializer<T> { serialize(value: T): string; deserialize(raw: string): T }
```
Default: JSON. Alternatives: `CborSerializer` (via `@msgpack/msgpack`), `StructuredCloneSerializer`.
### 3. `EvictionStrategy` interface
```ts
interface EvictionStrategy { selectForEviction(entries: CacheEntry[]): CacheEntry[] }
class LRUEviction implements EvictionStrategy { ... }
class LFUEviction implements EvictionStrategy { ... }
class TTLFirstEviction implements EvictionStrategy { ... }
```
### 4. Cross-tab invalidation
`LocalStorageCache` and `IndexedDBCache` accept an optional `broadcastChannel?: TypedBroadcastChannel` — on write/delete/clear, publishes invalidation events so other tabs update their in-memory views.
### 5. Split oversized files
Each implementation splits into: `store.ts` (core CRUD), `ttl.ts` (expiry logic), `quota.ts` (quota guard), `events.ts` (storage-event wiring). All ≤300 lines.
### 6. Public introspection surface
`cache.getMetrics(): CacheMetrics` replaces private-property reach-through in tests.
### 7. Error codes
`CACHE_QUOTA_EXCEEDED`, `CACHE_SERIALIZATION_FAILED`, `INDEXEDDB_NOT_AVAILABLE`.
### 8. OTel metrics
Counter `cache.hits_total{backend}`, counter `cache.misses_total{backend}`, counter `cache.evictions_total{strategy}`, histograms `cache.read_ms`, `cache.write_ms`.
---
## File layout
```text
browser/storage/cache-browser/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
register.ts (explicit CacheFactory.register calls; sideEffects entry)
error-codes.ts
indexeddb/
store/ (store.ts, store.test.ts, store.md)
ttl/ (ttl.ts, ttl.test.ts, ttl.md)
quota/ (quota.ts, quota.test.ts, quota.md)
events/ (events.ts, events.test.ts, events.md)
indexeddb/ (indexeddb.ts — orchestrator, indexeddb.test.ts, indexeddb.md, indexeddb.types.ts)
localstorage/
store/
ttl/
quota/
events/
localstorage/ (localstorage.ts, localstorage.test.ts, localstorage.md, localstorage.types.ts)
sessionstorage/
store/
ttl/
sessionstorage/ (sessionstorage.ts, sessionstorage.test.ts, sessionstorage.md, sessionstorage.types.ts)
utils/
quota/ (getStorageQuota, isStorageNearQuota, estimateStorageSize, handleQuotaExceeded, isStorageAvailable)
serialization/ (defaultSerializer/Deserializer, isSerializable, safeDeserialize, Serializer interface)
storage-events/(StorageEventHandler, globalStorageEventHandler)
eviction/
lru-eviction/
lfu-eviction/
ttl-first-eviction/
types/
types/ (all shared types)
tests/
integration.test.ts
e2e/
cache-browser.spec.ts (Playwright: Chromium/WebKit/Firefox real storage)
```
---
## Impact
**Downstream**: `@suaveplan/analytics-browser`, `@suaveplan/pwa`, `@suaveplan/network-discovery-*`, any package using browser caching.
**Prerequisites**: `@suaveplan/cache`, `@suaveplan/events`, `@suaveplan/error`, `@suaveplan/broadcast-channel` on Verdaccio.