Skip to content

Commit a8a05db

Browse files
committed
fix(action): cap cached action handlers to bound memory
1 parent cbc9db1 commit a8a05db

File tree

2 files changed

+26
-0
lines changed

2 files changed

+26
-0
lines changed

src/action.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const LEGACY_ACTION_QRL_KEY = '__fictReactAction'
1111
const ACTION_PROP_PATTERN = /^on[A-Z]/
1212
const RETRY_BASE_DELAY_MS = 100
1313
const RETRY_MAX_DELAY_MS = 5_000
14+
const ACTION_HANDLER_CACHE_MAX_ENTRIES = 500
1415

1516
const moduleCache = new Map<string, Promise<Record<string, unknown>>>()
1617
const moduleRetryState = new Map<string, { failures: number; nextRetryAt: number }>()
@@ -118,6 +119,12 @@ function toActionHandler(qrl: string): (...args: unknown[]) => void {
118119
})
119120
}
120121

122+
if (actionHandlerCache.size >= ACTION_HANDLER_CACHE_MAX_ENTRIES) {
123+
const oldestKey = actionHandlerCache.keys().next().value as string | undefined
124+
if (oldestKey) {
125+
actionHandlerCache.delete(oldestKey)
126+
}
127+
}
121128
actionHandlerCache.set(qrl, handler)
122129
return handler
123130
}

test/action.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,25 @@ describe('materializeReactProps', () => {
9090
expect(typeof result.onAction).toBe('function')
9191
})
9292

93+
it('evicts oldest cached action handlers after cache limit is exceeded', () => {
94+
const firstQrl = '/mock/first-module.js#run'
95+
const firstMaterialized = materializeReactProps({
96+
onAction: reactActionFromQrl(firstQrl),
97+
})
98+
const firstHandler = firstMaterialized.onAction
99+
100+
for (let index = 0; index < 600; index += 1) {
101+
materializeReactProps({
102+
onAction: reactActionFromQrl(`/mock/module-${index}.js#run`),
103+
})
104+
}
105+
106+
const rematerialized = materializeReactProps({
107+
onAction: reactActionFromQrl(firstQrl),
108+
})
109+
expect(rematerialized.onAction).not.toBe(firstHandler)
110+
})
111+
93112
it('ignores legacy-looking objects when additional keys are present', () => {
94113
const legacyLike = {
95114
__fictReactAction: '/mock/module.js#run',

0 commit comments

Comments
 (0)