Add TypeScript cache adapter with cross-language conformance tests
Port of Python's origin-side cache to TypeScript: - cache/keys.ts: deriveCacheKey with stableStringify for JSON-canonical HMAC - cache/backend.ts: MemoryCache (same API as Python) - cache/index.ts: cacheGet, cachePut, cachePurge with AND semantics Integrated into dispatch.ts: - handleContextFetch: cache lookup before execution, store after - handleMutationCall: purge on invalidation Cross-language pin test proves Python and TypeScript produce identical HMAC-SHA256 output for the same inputs: Public: 605a1ca5ad5994e9b765c8d1b330474c2a0d51a7b8fbbdc402f992da7ba902f6 User-scoped: 30fc08eb46ee4ff2cf7d317e97dca90fd616511e0587304416f71dc863338dc2 34 TypeScript tests (9 new), 165 Python tests (1 new pin test). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
70
packages/mizan-ts/src/cache/backend.ts
vendored
Normal file
70
packages/mizan-ts/src/cache/backend.ts
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Cache backends — MemoryCache for testing.
|
||||
*
|
||||
* Same API as Python's MemoryCache. RedisCache is implemented
|
||||
* per-adapter for production (not included here).
|
||||
*/
|
||||
|
||||
export interface CacheBackend {
|
||||
get(key: string): string | null
|
||||
put(key: string, value: string, indexes: string[]): void
|
||||
deleteMany(keys: string[]): number
|
||||
getIndex(indexKey: string): Set<string>
|
||||
removeFromIndex(indexKey: string, members: Set<string>): void
|
||||
deleteIndex(indexKey: string): void
|
||||
deleteIndexesByPrefix(prefix: string): void
|
||||
clear(): void
|
||||
}
|
||||
|
||||
export class MemoryCache implements CacheBackend {
|
||||
private _store = new Map<string, string>()
|
||||
private _indexes = new Map<string, Set<string>>()
|
||||
|
||||
get(key: string): string | null {
|
||||
return this._store.get(key) ?? null
|
||||
}
|
||||
|
||||
put(key: string, value: string, indexes: string[]): void {
|
||||
this._store.set(key, value)
|
||||
for (const idx of indexes) {
|
||||
if (!this._indexes.has(idx)) {
|
||||
this._indexes.set(idx, new Set())
|
||||
}
|
||||
this._indexes.get(idx)!.add(key)
|
||||
}
|
||||
}
|
||||
|
||||
deleteMany(keys: string[]): number {
|
||||
let count = 0
|
||||
for (const key of keys) {
|
||||
if (this._store.delete(key)) count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
getIndex(indexKey: string): Set<string> {
|
||||
return new Set(this._indexes.get(indexKey) ?? [])
|
||||
}
|
||||
|
||||
removeFromIndex(indexKey: string, members: Set<string>): void {
|
||||
const idx = this._indexes.get(indexKey)
|
||||
if (!idx) return
|
||||
for (const m of members) idx.delete(m)
|
||||
if (idx.size === 0) this._indexes.delete(indexKey)
|
||||
}
|
||||
|
||||
deleteIndex(indexKey: string): void {
|
||||
this._indexes.delete(indexKey)
|
||||
}
|
||||
|
||||
deleteIndexesByPrefix(prefix: string): void {
|
||||
for (const key of [...this._indexes.keys()]) {
|
||||
if (key.startsWith(prefix)) this._indexes.delete(key)
|
||||
}
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this._store.clear()
|
||||
this._indexes.clear()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user