import { type DbExec } from "../db/client.js"; import type { ShareRole } from "./schema.js"; /** * Requests from signed-in people who hold a link to a resource they can't * open. One row per resource and requester: asking again reopens the same row * with a new generation, so owners are never notified twice for one ask. */ export type AccessRequestState = "pending" | "approved" | "declined"; export interface AccessRequestRow { id: string; resourceType: string; resourceId: string; requesterEmail: string; requesterName: string | null; /** The resource owner when the request was made, for per-owner limits. */ ownerEmail: string; note: string | null; state: AccessRequestState; /** Increments each time a closed request is asked again. */ generation: number; /** When this generation was asked, in epoch ms. */ requestedAt: number; /** When the requester first asked for this resource, in epoch ms. */ createdAt: number; decidedBy: string | null; decidedAt: number | null; grantedRole: ShareRole | null; /** Whether anyone was told about this generation. */ delivered: boolean; } export declare const resourceAccessRequestsStore: import("../db/store-registry.js").Store; export declare function ensureTable(): Promise; export declare function getAccessRequest(id: string): Promise; export declare function findAccessRequest(resourceType: string, resourceId: string, requesterEmail: string): Promise; export declare function listPendingAccessRequests(resourceType: string, resourceId: string, limit: number): Promise; /** * How many requests were asked since `since` by one requester or against one * owner, and when the oldest of them was asked. */ export declare function countRecentAccessRequests(by: { requesterEmail: string; } | { ownerEmail: string; }, since: number): Promise<{ count: number; oldestAt: number | null; }>; export interface OpenAccessRequestInput { resourceType: string; resourceId: string; requesterEmail: string; requesterName: string | null; ownerEmail: string; note: string | null; now: number; /** A request declined after this time stays closed instead of reopening. */ declinedCooldownStart: number; /** * A pending request nobody was told about, asked before this time, is * claimed to be sent again; one asked later may still be sending. */ sendWindowStart: number; } /** * Opens a request, or reopens a closed one as a new generation. A pending * request whose send was cut off before anyone was told is claimed again in * its own generation, so recipients the cut-off send reached get nothing new. * Returns `opened: false` with the existing row when a request is already * pending, or was declined too recently to ask again, so the caller sends * nothing. */ export declare function openAccessRequest(input: OpenAccessRequestInput): Promise<{ request: AccessRequestRow; opened: boolean; }>; /** * Withdraws a request that was just opened but reached nobody, so asking * again isn't blocked and doesn't count toward the limits. Only the ask that * opened or claimed it at `requestedAt` can: one another ask has claimed * since, or already delivered, stays. */ export declare function deleteAccessRequest(id: string, generation: number, requestedAt: number): Promise; export declare function recordAccessRequestDelivery(id: string, generation: number, delivery: Record): Promise; /** * Closes a pending request. Only a request still pending in the generation * the decider saw changes, so a decision on a stale page can't close a newer * ask and two people can't both decide one. Pass the transaction a grant runs * in so both commit together. */ export declare function decideAccessRequest(input: { id: string; generation: number; state: Exclude; decidedBy: string; grantedRole: ShareRole | null; now: number; }, exec?: DbExec): Promise;