1// The per-user daily Jev budget: one JevBudget Durable Object per signed-in 2// user (`BUDGET.idFromName(userId)`), counting that user's relay calls per 3// UTC day in its own SQLite storage. The counting is budget-counter.ts; the 4// limit is wrangler.json's JEV_DAILY_PER_USER, read here from the object's 5// own env so there is one source for it. 6// 7// A Durable Object, not D1: the count is live coordination (every tab and 8// device of one user racing to spend the same allowance), and an object 9// runs one call at a time, so the check and the charge cannot interleave. 10// It is not a record: yesterday's count is deleted as today's begins. 11import { DurableObject } from 'cloudflare:workers'; 12import { balance, createSchema, dailyLimit, spend, type Balance, type Spend } from './budget-counter'; 13 14type BudgetEnv = { JEV_DAILY_PER_USER?: unknown }; 15 16export class JevBudget extends DurableObject<BudgetEnv> { 17 constructor(ctx: DurableObjectState, env: BudgetEnv) { 18 super(ctx, env); 19 createSchema(ctx.storage.sql); 20 } 21 22 // Charges `calls` relay calls (one, unless said) to today, unless they 23 // do not all fit in what is left of today's budget. 24 spend(calls = 1): Spend { 25 return spend(this.ctx.storage.sql, dailyLimit(this.env.JEV_DAILY_PER_USER), Date.now(), calls); 26 } 27 28 // Today's count, without charging. 29 balance(): Balance { 30 return balance(this.ctx.storage.sql, dailyLimit(this.env.JEV_DAILY_PER_USER), Date.now()); 31 } 32}