Files
podcastdistributiona/lib/utils.ts
T

45 lines
1.4 KiB
TypeScript

import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
/** Merge conditional class names and de-duplicate Tailwind utilities. */
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
/** Format a number of cents (e.g. 900) as a currency string ("$9"). */
export function formatPrice(cents: number, currency = "USD") {
const dollars = cents / 100;
return new Intl.NumberFormat("en-US", {
style: "currency",
currency,
maximumFractionDigits: dollars % 1 === 0 ? 0 : 2,
}).format(dollars);
}
/** The monthly usage bucket key, e.g. "2026-06". Pass a date for testability. */
export function periodKey(date: Date): string {
const y = date.getUTCFullYear();
const m = String(date.getUTCMonth() + 1).padStart(2, "0");
return `${y}-${m}`;
}
/**
* Returns `path` only if it is a safe same-origin relative path; otherwise
* falls back to "/dashboard". Guards against open-redirect attacks by rejecting
* protocol-relative ("//", "/\"), absolute ("https://…"), and backslash URLs.
*/
export function safeRedirect(path: string | null | undefined): string {
if (!path) return "/dashboard";
// Must be a single-slash-rooted relative path with no scheme or backslash escapes.
if (
!path.startsWith("/") ||
path.startsWith("//") ||
path.startsWith("/\\") ||
path.startsWith("\\") ||
path.includes("://")
) {
return "/dashboard";
}
return path;
}