| |
| |
| |
| |
| |
| |
|
|
| import type { BillingVerificationStatus } from '../../server/_shared/entitlement-check'; |
|
|
| export type BillingVerificationCode = |
| | BillingVerificationStatus |
| |
| |
| |
| | 'entitlement_verification_unavailable'; |
|
|
| const BILLING_VERIFICATION_CODES: ReadonlySet<string> = new Set([ |
| 'subscription_lapsed', |
| 'renewal_verification_pending', |
| 'renewal_verification_failed', |
| 'entitlement_verification_unavailable', |
| ] satisfies BillingVerificationCode[]); |
|
|
| export class BillingDenialError extends Error { |
| readonly operation: string; |
| readonly status: number; |
| readonly billingCode: BillingVerificationCode; |
| readonly retryAfterSeconds: number | undefined; |
|
|
| constructor( |
| label: string, |
| status: number, |
| billingCode: BillingVerificationCode, |
| retryAfterSeconds: number | undefined, |
| ) { |
| |
| |
| super(`${label} HTTP ${status} (${billingCode})`); |
| this.name = 'BillingDenialError'; |
| this.operation = label; |
| this.status = status; |
| this.billingCode = billingCode; |
| this.retryAfterSeconds = retryAfterSeconds; |
| } |
| } |
|
|
| |
| |
| |
| type ToolFetchResponse = { |
| ok: boolean; |
| status: number; |
| headers?: { get(name: string): string | null }; |
| }; |
|
|
| |
| |
| |
| |
| |
| export function throwIfBillingDenial(response: ToolFetchResponse, label: string): void { |
| if (response.ok) return; |
| const marker = response.headers?.get('X-Billing-Verification'); |
| if (!marker || !BILLING_VERIFICATION_CODES.has(marker)) return; |
| |
| |
| const retryHeader = response.headers?.get('Retry-After'); |
| const rawRetryAfter = retryHeader == null ? Number.NaN : Number(retryHeader); |
| throw new BillingDenialError( |
| label, |
| response.status, |
| marker as BillingVerificationCode, |
| Number.isFinite(rawRetryAfter) ? rawRetryAfter : undefined, |
| ); |
| } |
|
|
| |
| |
| |
| |
| |
| export function assertToolFetchOk(response: ToolFetchResponse, label: string): void { |
| if (response.ok) return; |
| throwIfBillingDenial(response, label); |
| throw new Error(`${label} HTTP ${response.status}`); |
| } |
|
|