155 lines · 5.4 KB
import type { MeterId } from "./meters";

/**
 * Plans are config-driven: `METERING_PLANS` (JSON, same shape as DEFAULT_PLANS) overrides these defaults.
 * `included` is free per period; usage past it is billed (paid plans) or blocked (hard caps).
 * `hardCaps` are enforced atomically by reserveUsage; every other meter is soft and only alerts.
 */
export type PlanId = "scratch" | "free" | "pro";

export type Plan = {
  id: PlanId;
  label: string;
  included: Partial<Record<MeterId, number>>;
  hardCaps: Partial<Record<MeterId, number>>;
  /** Whether overage is billed; when false every included amount acts as a soft cap with alerts. */
  billsOverage: boolean;
  /**
   * The monthly overage limit for accounts that never set one (spending_limit_cents NULL). Without it a
   * paid plan's overage would be unbounded by default; owners can raise or lower it on the billing page.
   */
  defaultSpendingLimitCents?: number;
  /** Without Stripe billing (self-hosted), whether accounts on this plan may add custom domains. */
  customDomains?: boolean;
};

const GB = 1024 ** 3;

/** $50 of overage a month (a little over 3x the Pro price) unless the owner chooses another limit. */
export const DEFAULT_PRO_SPENDING_LIMIT_CENTS = 5000;

/** The highest monthly spending limit an owner can set: $1,000,000. */
export const MAX_SPENDING_LIMIT_CENTS = 100_000_000;
const HOUR = 3600;

const FREE_INCLUDED: Plan["included"] = {
  "storage.artifacts": 1 * GB,
  "storage.lfs": 1 * GB,
  "storage.ci_cache": 0.5 * GB,
  "storage.sites": 0.25 * GB,
  "storage.r2": 0.25 * GB,