Skip to main content

QuotaWindow

@forge/monorepo


@forge/monorepo / backend/src / QuotaWindow

Type Alias: QuotaWindow

QuotaWindow = { kind: "calendar"; period: RollupPeriod; } | { kind: "rolling"; minutes: number; }

Defined in: backend/src/persistence/index.ts:474

The span a limit is measured over — #181.

A union, not an optional field beside period, because the two kinds are read differently and confusing them produces a wrong number rather than an error. A calendar window is a bucket the rollups already hold; a rolling window has no bucket and has to be summed from the ledger. An optional minutes next to a required period would let a caller set both, and something would have to decide which one meant it.

A rolling window slides; it does not reset. "No more than X in any five hours" — the window is always the last minutes up to now. That is a deliberate choice over the other reading, an anchored session that starts on first use and hard-resets after five hours, and the reason is that an anchored window's boundary is state: to know which session a spend belongs to you must know when the current one began, which cannot be derived from the records without walking history forward from the first one ever. Storing the anchor would make the boundary a row that can be wrong, stale or missing, and the refusal message quotes it to people.

The sliding reading needs no state, cannot drift, and is strictly harder to game — you cannot wait out a boundary and spend twice the allowance across it. What it gives up is a single clean "resets at": headroom returns gradually as records age out, so the honest statement is when the oldest one leaves, which is what earliestAt is for.

Union Members

Type Literal

{ kind: "calendar"; period: RollupPeriod; }


Type Literal

{ kind: "rolling"; minutes: number; }

minutes so an admin can express 5 hours, 90 minutes or 2 days without a new type.