export type RateLimitVerdict = { success: boolean; resetAt: number };
/** Shard count for the limiter namespace; one fixed window per (limiter, key) lives in exactly one shard. */
export const RATE_LIMIT_SHARDS = 256;
/** sha256(limiter + key), hex; the storage key, so raw IPs and user ids never land in DO storage. */
export async function rateLimitDigest(limiter: string, key: string): Promise<string> {
const bytes = new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(`${limiter}\0${key}`)));
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
}
export function rateLimitShard(digest: string): string {
return `rl:${parseInt(digest.slice(0, 4), 16) % RATE_LIMIT_SHARDS}`;
}