← back to Trademarks Copyright
src/lib/scoring.ts
66 lines
import type { Item } from "./types";
export interface ScoringInputs {
distinctiveness_score: number | null;
recognition_score: number | null;
domain_available: boolean | null;
competing_active_marks: number | null;
monetization_breadth: number | null;
expired_date: string | Date;
}
/**
* Composite 0-100. Weights:
* distinctiveness 25, years-since-expired 20, recognition 15,
* domain 15, competing-marks 15, monetization 10.
* Sweet spot for "years since expired" is 3-15. Too recent = revival risk.
* Too old = brand memory decayed.
*/
export function scoreItem(inp: ScoringInputs): number {
const distinct = clamp(inp.distinctiveness_score ?? 50, 0, 100);
const recog = clamp(inp.recognition_score ?? 30, 0, 100);
const monet = clamp(inp.monetization_breadth ?? 40, 0, 100);
const yearsExpired = yearsSince(inp.expired_date);
const ageScore = ageFit(yearsExpired);
const domainScore = inp.domain_available === true ? 100 : inp.domain_available === false ? 15 : 50;
// Competing active marks penalty: 0 = perfect, 10+ = heavy saturation
const competing = inp.competing_active_marks ?? 0;
const competingScore = clamp(100 - competing * 10, 0, 100);
const composite =
distinct * 0.25 +
ageScore * 0.20 +
recog * 0.15 +
domainScore * 0.15 +
competingScore * 0.15 +
monet * 0.10;
return round2(composite);
}
function yearsSince(d: string | Date): number {
const past = new Date(d).getTime();
const now = Date.now();
return (now - past) / (365.25 * 24 * 3600 * 1000);
}
function ageFit(years: number): number {
if (years < 0) return 0;
if (years < 1) return 20; // too recent — revival likely
if (years < 3) return 55;
if (years <= 15) return 100; // sweet spot
if (years <= 30) return 70;
if (years <= 50) return 45;
return 30;
}
const clamp = (n: number, lo: number, hi: number) => Math.max(lo, Math.min(hi, n));
const round2 = (n: number) => Math.round(n * 100) / 100;
export function yearsExpired(item: Pick<Item, "expired_date">): number {
return round2(yearsSince(item.expired_date));
}