import { env, waitUntil } from "cloudflare:workers";
import { accountDb, stmt } from "../scoped-db";
import type { ScopedStatement } from "../scoped-db";
import { locate } from "../scoped-db/locators";
import { creditDrawStatements } from "./credits";
import { claimLocator, noteScratchAccount } from "./directory";
import { randomId } from "../crypto";
import { meterDef } from "~/lib/meters";
import type { MeterId } from "~/lib/meters";
/** Billing periods are UTC calendar months, `YYYY-MM`. */
export function periodOf(now = Date.now()): string {
return new Date(now).toISOString().slice(0, 7);
}
export type UsageInput = {
/** The account billed: a user or org id, or a `scratch:` id for anonymous usage. */
accountId: string;
meter: MeterId;
/** Exact quantity in the meter's unit; compute is per second (fractions allowed), never rounded up. */
quantity: number;
/** Optional sanity check against the catalog unit. */
unit?: string;
repoId?: string | null;
attributes?: Record<string, string | number | boolean | null>;
/** Stable for the logical event: a retry with the same key is ignored. Prefix with your subsystem. */
idempotencyKey: string;
/** Event time, defaults to now; decides the billing period. */
at?: number;
};
const MAX_KEY_LENGTH = 200;
export class MeteringError extends Error {}
export function validate(input: UsageInput): void {
const def = meterDef(input.meter);
if (!def) throw new MeteringError(`Unknown meter ${input.meter}`);
if (def.kind !== "counter") throw new MeteringError(`${input.meter} is a gauge; use setGauge`);
if (input.unit && input.unit !== def.unit) throw new MeteringError(`${input.meter} is measured in ${def.unit}`);