import { DurableObject } from "cloudflare:workers";
import { unreadNotificationCount } from "./notification-store";
import type { InvolvementEntry, InvolvementQuery, InvolvementRole } from "./involvement";
/** Bounds drift from writes that bypass `invalidateUnread`, e.g. repo deletes cascading notifications. */
const CACHE_TTL_MS = 5 * 60 * 1000;
const MAX_INVOLVEMENT_ROWS = 50_000;
const MAX_LIST = 200;
type Cached = { count: number; at: number };
type InvolvementRow = {
issue_id: string;
repo_id: string;
number: number;
is_pull: number;
state: string;
title: string;
updated_at: number;
roles: string;
};
/**
* One per user. Holds the unread-notification count so the header badge on every page view is an
* in-memory read on a per-user object instead of a COUNT on D1. Writers call `invalidate`; a version
* number keeps a count computed before an invalidation from being cached after it.
*
* Also holds the user's involvement index: every issue and pull request they authored, are assigned to or
* were asked to review, pushed here by each repository's RepoState. Cross-repo lists ("assigned to me",
* "review requested") read this instead of joining across every repository.
*/
export class UserInbox extends DurableObject<Env> {
private cached: Cached | null = null;
private version = 0;
private involvementReady = false;
private get sql(): SqlStorage {
const sql = this.ctx.storage.sql;
if (!this.involvementReady) {
sql.exec(`CREATE TABLE IF NOT EXISTS involvement (