Files
OmniRoute/open-sse/services/quotaWindowLabel.ts
Ravi Tharuma d6a61074dc fix(quota): align AUTH window labels with usage API and clarify 503 (#12884)
Um `ALL_TARGETS_SKIPPED` 503 que não diz qual janela esgotou é opaco justamente no momento em que o operador mais precisa saber. Alinhar os rótulos de janela AUTH com os da API de uso fecha a outra metade: dois nomes para a mesma coisa fazem o dashboard e o erro parecerem discordar.

Revalidei sobre o tip: **6/6**, typecheck:core limpo, check-file-size OK.

**Dois consertos meus na sua branch.**

1. `typecheck:core` falhava com `TS2345` em `comboAttemptLoop.ts` (linhas 130 e 416): o `QuotaSkipTarget` declarava `connectionId?: string`, mas o `ResolvedComboTarget` carrega `string | null` para alvo não-pinado. Alarguei para `string | null` no tipo de diagnóstico em vez de estreitar o call site — o módulo só **lê** o campo e a linha 29 já narrowa com `typeof === "string"`, então null não custa nada ali. Isso apareceu porque o `comboAttemptLoop` mudou de forma no #12746/#12811, mergeados nesta mesma campanha depois que você cortou a branch.

2. O `roundRobinCombo.ts` foi de 1198 para 1205 e cruzou o teto de 1200 para arquivo novo. Congelei com justificativa: o arquivo já nasceu em 1198 quando o #12811 o levantou de dentro do `combo.ts`, e os diagnósticos em si vivem no `quotaSkipDiagnostics.ts`, sob o cap. Registrei que a próxima extração natural é o corpo do attempt loop, mas que ele acabou de ser movido e deve assentar antes de ser cortado de novo.
2026-09-10 10:47:11 -03:00

74 lines
2.6 KiB
TypeScript

/**
* Shared duration-aware quota window labels for AUTH preflight and /api/usage.
* Routing keys (`session`, `weekly`) stay position-based; only the display
* string follows the real window length so a 30-day primary window is not
* reported as "session (5h)".
*/
const SESSION_MAX_SECONDS = 6 * 3600;
const WEEKLY_MAX_SECONDS = 8 * 24 * 3600;
const MONTHLY_MAX_SECONDS = 40 * 24 * 3600;
const GENERIC_FAMILY = new Set(["session", "weekly", "monthly"]);
export type QuotaWindowLabelInput = {
key: string;
displayName?: string | null;
windowSeconds?: number | null;
};
function toPositiveSeconds(value: unknown): number | null {
if (typeof value === "number" && Number.isFinite(value) && value > 0) return value;
if (typeof value === "string" && value.trim().length > 0) {
const parsed = Number(value);
if (Number.isFinite(parsed) && parsed > 0) return parsed;
}
return null;
}
export function formatWindowDuration(windowSeconds: number): string {
if (windowSeconds % 86400 === 0) return `${windowSeconds / 86400}d`;
if (windowSeconds % 3600 === 0) return `${windowSeconds / 3600}h`;
return `${Math.round(windowSeconds)}s`;
}
export function inferWindowFamilyLabel(windowSeconds: number): string | undefined {
if (windowSeconds <= SESSION_MAX_SECONDS) return "Session";
if (windowSeconds <= WEEKLY_MAX_SECONDS) return "Weekly";
if (windowSeconds <= MONTHLY_MAX_SECONDS) return "Monthly";
return undefined;
}
function normalizeFamily(value: string): string {
return value.trim().toLowerCase();
}
/**
* Prefer a duration-aware label. When usage already stamped a generic family
* (`Weekly`) on a 30-day window, replace it with Monthly so AUTH and the
* usage API agree on the reset window.
*/
export function formatQuotaWindowLabel(input: QuotaWindowLabelInput): string {
const seconds = toPositiveSeconds(input.windowSeconds);
const duration = seconds != null ? formatWindowDuration(seconds) : null;
const inferred = seconds != null ? inferWindowFamilyLabel(seconds) : undefined;
const named = typeof input.displayName === "string" ? input.displayName.trim() : "";
const key = typeof input.key === "string" && input.key.trim() ? input.key.trim() : "quota";
let base = named || inferred || key;
if (inferred && named && GENERIC_FAMILY.has(normalizeFamily(named)) && inferred !== named) {
base = inferred;
}
if (duration && !base.toLowerCase().includes(duration.toLowerCase())) {
return `${base} (${duration})`;
}
return base;
}
export function formatQuotaUsageReason(
input: QuotaWindowLabelInput,
usedPercentage: number
): string {
return `${formatQuotaWindowLabel(input)} usage ${Math.round(usedPercentage)}%`;
}