interface AppviewCursorCheckpoint {
matchingSeen: number;
scannedSeen: number;
cursor?: string;
exhausted: boolean;
}
const APPVIEW_CURSOR_CACHE_LIMIT = 64;
const appviewPageCursorCache = new Map<string, Map<number, AppviewCursorCheckpoint>>();
export const clearAppviewStatefulCursorCache = () => {
appviewPageCursorCache.clear();
};
export const rememberAppviewCursorCheckpoint = (cacheKey: string, checkpoint: AppviewCursorCheckpoint) => {
let checkpoints = appviewPageCursorCache.get(cacheKey);
if (!checkpoints) {
checkpoints = new Map();
appviewPageCursorCache.set(cacheKey, checkpoints);
}
const existing = checkpoints.get(checkpoint.matchingSeen);
if (!existing || checkpoint.scannedSeen >= existing.scannedSeen) {
checkpoints.set(checkpoint.matchingSeen, checkpoint);
}
while (checkpoints.size > APPVIEW_CURSOR_CACHE_LIMIT) {
const first = checkpoints.keys().next().value;
if (first === undefined) break;
checkpoints.delete(first);
}
};
export const getNearestAppviewCursorCheckpoint = (
cacheKey: string,
offset: number,
): AppviewCursorCheckpoint => {
const checkpoints = appviewPageCursorCache.get(cacheKey);
const start: AppviewCursorCheckpoint = {
matchingSeen: 0,
scannedSeen: 0,
exhausted: false,
};
if (!checkpoints) {
return start;
}
let nearest = start;
for (const checkpoint of checkpoints.values()) {
if (checkpoint.matchingSeen > offset) {
continue;
}
if (
checkpoint.matchingSeen > nearest.matchingSeen ||
(
checkpoint.matchingSeen === nearest.matchingSeen &&
checkpoint.scannedSeen > nearest.scannedSeen
)
) {
nearest = checkpoint;
}
}
return nearest;
};