Files
deepseek-harness/packages/experimental/client-ui-forge/src/patch-cache.ts
T
Yichen Jiang 929e283c5e feat(forge-panel): classify changed files into review layers and read chosen commits
Each diff file now carries a review layer and a change digest. The layer
follows the new testPaths and interfacePaths globs and the linguist-generated
lines of the root .gitattributes, which the panel reads at the compared side
and caches beside change request patches. localCommits and
changeRequestCommits list commits, and a branch scope or change request diff
can name consecutive commits to show instead of the whole comparison.
2026-11-06 17:51:06 +08:00

74 lines
2.6 KiB
TypeScript

/**
* Change request patches kept in memory with the repository attributes read beside them, so the changes view reads
* neither from the code host again while a change request keeps its head commit and base branch.
*/
import { Buffer } from 'node:buffer'
import type { ChangeRequestKey } from '@deepseek-ai/dsh-experimental-forge'
/** One change request's patch and the root `.gitattributes` text at its head commit, empty without one. */
export interface CachedPatch {
readonly patch: string
readonly attributes: string
}
interface Entry {
/** Head commit and base branch the patch was read at. */
readonly version: string
readonly value: CachedPatch
/** UTF-8 size of the patch and the attributes. */
readonly bytes: number
}
/**
* One patch per change request, bounded by the total UTF-8 size of the kept
* patches and their attributes; the least recently read patch leaves first.
*/
export class PatchCache {
private readonly entries = new Map<ChangeRequestKey, Entry>()
private bytes = 0
/** @param maxBytes - most UTF-8 bytes kept together; a larger patch is not kept. */
constructor(private readonly maxBytes: number) {}
/**
* Read the patch kept for a change request at a version, marking it the most recently read.
* @param key - the change request.
* @param version - head commit and base branch the reader needs.
* @returns the patch and its attributes, or undefined when none is kept at that version.
*/
get(key: ChangeRequestKey, version: string): CachedPatch | undefined {
const entry = this.entries.get(key)
if (entry?.version !== version) return undefined
this.entries.delete(key)
this.entries.set(key, entry)
return entry.value
}
/**
* Keep a patch in place of the change request's earlier one, then drop the
* least recently read patches until the rest fit within `maxBytes`.
* @param key - the change request.
* @param version - head commit and base branch the patch was read at.
* @param value - the patch and its attributes.
*/
set(key: ChangeRequestKey, version: string, value: CachedPatch): void {
this.remove(key)
const bytes = Buffer.byteLength(value.patch) + Buffer.byteLength(value.attributes)
if (bytes > this.maxBytes) return
this.entries.set(key, { version, value, bytes })
this.bytes += bytes
for (const oldest of this.entries.keys()) {
if (this.bytes <= this.maxBytes) break
this.remove(oldest)
}
}
private remove(key: ChangeRequestKey): void {
const entry = this.entries.get(key)
if (entry === undefined) return
this.entries.delete(key)
this.bytes -= entry.bytes
}
}