fix(rules-injector): bound matcher cache

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-05-18 12:36:33 +09:00
parent 1ab1b54ce6
commit 46b965b9da
2 changed files with 25 additions and 1 deletions
+13
View File
@@ -30,6 +30,19 @@ describe("shouldApplyRule", () => {
expect(getMatcherCacheStats()).toEqual({ entries: 2 })
})
it("#given many unique globs #when matching repeatedly #then matcher cache stays bounded", () => {
// given
const projectRoot = "/workspace/project"
// when
for (let index = 0; index < 300; index += 1) {
shouldApplyRule({ globs: `src/file-${index}.ts` }, `${projectRoot}/src/file-${index}.ts`, projectRoot)
}
// then
expect(getMatcherCacheStats().entries <= 256).toBe(true)
})
it("#given matching glob #when path is under project root #then returns matching reason", () => {
// given / when
const result = shouldApplyRule({ globs: "src/**/*.ts" }, "/workspace/project/src/index.ts", "/workspace/project")
+12 -1
View File
@@ -15,13 +15,24 @@ export interface MatcherCacheStats {
}
const PICOMATCH_OPTIONS = { dot: true, bash: true } as const
const MAX_MATCHER_CACHE_ENTRIES = 256
const matcherCache = new Map<string, PathMatcher>()
function matcherFor(pattern: string): PathMatcher {
const cached = matcherCache.get(pattern)
if (cached) return cached
if (cached) {
matcherCache.delete(pattern)
matcherCache.set(pattern, cached)
return cached
}
const matcher = picomatch(pattern, PICOMATCH_OPTIONS)
if (matcherCache.size >= MAX_MATCHER_CACHE_ENTRIES) {
const oldestPattern = matcherCache.keys().next().value
if (oldestPattern !== undefined) {
matcherCache.delete(oldestPattern)
}
}
matcherCache.set(pattern, matcher)
return matcher
}