/** * computeSubResourceCacheTTL * * Computes the Redis TTL (in milliseconds) for opportunity sub-resources * (notes, contacts, activities, etc.) that share the parent opportunity's * activity window. * * Rules are identical to the main opportunity TTL but use different * exported constant names so callers can distinguish them. */ export const SUB_TTL_HIGH_ACTIVITY = 60_000; // 60 seconds export const SUB_TTL_MODERATE_ACTIVITY = 120_000; // 2 minutes export const SUB_TTL_LOW_ACTIVITY = 300_000; // 5 minutes export interface ComputeSubResourceCacheTTLInput { closedFlag: boolean; closedDate: Date | null; expectedCloseDate: Date | null; lastUpdated: Date | null; now?: Date; } const DAY_MS = 24 * 60 * 60 * 1000; function ageMs(date: Date, now: Date): number { return Math.abs(now.getTime() - date.getTime()); } export function computeSubResourceCacheTTL( input: ComputeSubResourceCacheTTLInput ): number | null { const now = input.now ?? new Date(); // Rule 1a — closed > 30 days → no cache if (input.closedFlag) { if (!input.closedDate) return null; const daysClosed = ageMs(input.closedDate, now) / DAY_MS; if (daysClosed > 30) return null; return SUB_TTL_LOW_ACTIVITY; } const dates = [input.lastUpdated, input.expectedCloseDate].filter( (d): d is Date => d !== null ); if (dates.length === 0) { return SUB_TTL_LOW_ACTIVITY; } const minDeltaDays = Math.min(...dates.map((d) => ageMs(d, now))) / DAY_MS; if (minDeltaDays <= 5) return SUB_TTL_HIGH_ACTIVITY; if (minDeltaDays <= 14) return SUB_TTL_MODERATE_ACTIVITY; return SUB_TTL_LOW_ACTIVITY; }