SPB Git forge
38commits 1branches 0releases
338.7 MBsize
maindefault branch
4 h agolast push
HTML 53.9% TypeScript 44.5% JavaScript 0.6% SQL 0.5%
12.3 KB · 384 lines typescript
Raw Blame History
1import type { AiEvidence, AuthorityTier, ClaimScope, ClaimStatus, ConfidenceLevel, DensityView, EventType, FacilityStatus, FacilityType, GeoPrecision, MapLayer, OperatorKind, ProjectStage, Significance, SourceKind } from "@dci/core";23/** Semantic status groups → one colour token each (shared by badges, map, charts). */4export type StatusTone = "operational" | "construction" | "planned" | "permitting" | "delayed" | "closed" | "rumored" | "unknown";56export const STATUS_TONE: Record<FacilityStatus, StatusTone> = {7  operational: "operational",8  partially_operational: "operational",9  expansion: "operational",10  under_construction: "construction",11  announced: "planned",12  proposed: "planned",13  permitting: "permitting",14  approved: "permitting",15  delayed: "delayed",16  cancelled: "closed",17  closed: "closed",18  rumored: "rumored",19  unknown: "unknown",20};2122export const STATUS_LABEL: Record<FacilityStatus, string> = {23  rumored: "Rumored",24  proposed: "Proposed",25  announced: "Announced",26  permitting: "Permitting",27  approved: "Approved",28  under_construction: "Under construction",29  partially_operational: "Partially operational",30  operational: "Operational",31  expansion: "Expansion",32  delayed: "Delayed",33  cancelled: "Cancelled",34  closed: "Closed",35  unknown: "Unknown",36};3738export const STATUS_SHORT: Record<FacilityStatus, string> = {39  ...STATUS_LABEL,40  under_construction: "Construction",41  partially_operational: "Partial",42};4344/** Display order used by legends, stacked bars and filter chips. */45export const STATUS_ORDER: FacilityStatus[] = [46  "operational",47  "partially_operational",48  "expansion",49  "under_construction",50  "approved",51  "permitting",52  "announced",53  "proposed",54  "rumored",55  "delayed",56  "cancelled",57  "closed",58  "unknown",59];6061/** CSS variable name for a tone — resolved at paint time so the map / SVG follow the theme. */62export function statusVar(status: FacilityStatus | StatusTone): string {63  const tone = (status in STATUS_TONE ? STATUS_TONE[status as FacilityStatus] : status) as StatusTone;64  return `var(--st-${tone})`;65}6667export const TYPE_LABEL: Record<FacilityType, string> = {68  colocation: "Colocation",69  hyperscale: "Hyperscale",70  cloud_region: "Cloud region",71  edge: "Edge",72  carrier_hotel: "Carrier hotel",73  internet_exchange: "Internet exchange",74  ai: "AI",75  hpc: "HPC",76  sovereign_cloud: "Sovereign cloud",77  government: "Government",78  research: "Research",79  enterprise: "Enterprise",80  telecom: "Telecom",81  wholesale: "Wholesale",82  unknown: "Unknown",83};8485export const CONFIDENCE_LABEL: Record<ConfidenceLevel, string> = {86  verified: "Verified",87  high: "High confidence",88  moderate: "Moderate confidence",89  estimated: "Estimated",90  unverified: "Unverified",91};92export const CONFIDENCE_SHORT: Record<ConfidenceLevel, string> = {93  verified: "Verified",94  high: "High",95  moderate: "Moderate",96  estimated: "Estimate",97  unverified: "Unverified",98};99/** 0..4 dots filled for the confidence meter. */100export const CONFIDENCE_RANK: Record<ConfidenceLevel, number> = { verified: 4, high: 3, moderate: 2, estimated: 1, unverified: 0 };101102export const PRECISION_LABEL: Record<GeoPrecision, string> = {103  exact: "Exact location",104  parcel: "Parcel",105  street: "Street level",106  city: "City level",107  metro: "Metro level",108  approximate: "Approximate",109  unknown: "Location unknown",110};111export const PRECISION_SHORT: Record<GeoPrecision, string> = {112  exact: "Exact",113  parcel: "Parcel",114  street: "Street",115  city: "City",116  metro: "Metro",117  approximate: "Approx.",118  unknown: "Unknown",119};120/** True when the point should be drawn as a hollow ring (not a precise site). */121export function isImprecise(p: GeoPrecision): boolean {122  return p === "city" || p === "metro" || p === "approximate" || p === "unknown";123}124125export const OPERATOR_KIND_LABEL: Record<OperatorKind, string> = {126  colocation: "Colocation",127  hyperscaler: "Hyperscaler",128  cloud: "Cloud",129  carrier: "Carrier",130  enterprise: "Enterprise",131  government: "Government",132  research: "Research",133  developer: "Developer",134  investor: "Investor",135  ixp_operator: "IXP operator",136  other: "Other",137};138139export const SOURCE_KIND_LABEL: Record<SourceKind, string> = {140  operator: "Operator",141  government: "Government",142  filing: "Filing",143  utility: "Utility",144  dataset: "Dataset",145  community: "Community",146  secondary: "Secondary",147  news: "News",148  cloud_provider: "Cloud provider",149  registry: "Registry",150};151152export const EVENT_LABEL: Record<EventType, string> = {153  facility_discovered: "New facility",154  facility_updated: "Facility updated",155  status_changed: "Status change",156  capacity_changed: "Capacity change",157  planned_capacity_changed: "Planned capacity",158  opening_date_changed: "Opening date",159  operator_changed: "Operator change",160  owner_changed: "Owner change",161  project_announced: "Project announced",162  project_status_changed: "Project status",163  phase_announced: "Phase announced",164  planning_filed: "Planning filed",165  planning_approved: "Planning approved",166  construction_started: "Construction started",167  facility_opened: "Facility opened",168  expansion_announced: "Expansion",169  acquisition: "Acquisition",170  closure: "Closure",171  cloud_region_announced: "Cloud region announced",172  cloud_region_launched: "Cloud region launched",173  cloud_region_updated: "Cloud region updated",174  power_agreement: "Power agreement",175  investment_announced: "Investment",176  incident: "Incident",177  page_changed: "Page changed",178  news: "News",179  land_acquired: "Land acquired",180  grid_connection: "Grid connection",181  grid_constraint: "Grid constraint",182  utility_event: "Utility event",183  operator_expansion: "Operator expansion",184  customer_agreement: "Customer agreement",185  partnership: "Partnership",186  executive_change: "Executive change",187  project_delayed: "Project delayed",188  project_cancelled: "Project cancelled",189};190191/** Event families → tone, so the feed reads by colour without a rainbow. */192export function eventTone(t: EventType): StatusTone | "accent" | "neutral" {193  switch (t) {194    case "facility_opened":195    case "cloud_region_launched":196    case "facility_discovered":197      return "operational";198    case "construction_started":199    case "phase_announced":200      return "construction";201    case "project_announced":202    case "expansion_announced":203    case "cloud_region_announced":204    case "investment_announced":205    case "power_agreement":206      return "accent";207    case "planning_filed":208    case "planning_approved":209      return "permitting";210    case "closure":211    case "incident":212      return "delayed";213    case "status_changed":214    case "project_status_changed":215    case "capacity_changed":216    case "planned_capacity_changed":217    case "opening_date_changed":218    case "operator_changed":219    case "owner_changed":220    case "acquisition":221      return "planned";222    default:223      return "neutral";224  }225}226227export const ENTITY_TYPE_LABEL: Record<string, string> = {228  facility: "Data center",229  campus: "Campus",230  operator: "Operator",231  company: "Company",232  cloud_region: "Cloud region",233  metro: "Market",234  city: "City",235  country: "Country",236  project: "Project",237  ixp: "IXP",238  power_project: "Power project",239  carrier: "Carrier",240  news_event: "News",241};242243/* ------------------------------------------------------------------ claim-first vocabulary (2026-09-12) */244245/** Claim / figure scope — what a number describes. Site scopes may populate a record's own figures; the rest never do. */246export const SCOPE_LABEL: Record<ClaimScope, string> = {247  building: "Building",248  facility: "Facility",249  campus: "Campus",250  metro: "Metro",251  country: "Country",252  portfolio: "Portfolio",253  company: "Company-wide",254  unknown: "Scope unknown",255};256export const SCOPE_SHORT: Record<ClaimScope, string> = { ...SCOPE_LABEL, company: "Company", unknown: "Unknown" };257export function isSiteScopeLabel(s: ClaimScope | string | null | undefined): boolean {258  return s === "building" || s === "facility" || s === "campus";259}260261/** Capacity semantics — the predicate behind a MW figure; also accepts the free-text `capacitySemantics` the API returns. */262export const SEMANTICS_LABEL: Record<string, string> = {263  it_capacity_mw: "IT capacity",264  it_capacity: "IT capacity",265  it_load: "IT capacity",266  critical_power_mw: "Critical power",267  critical_power: "Critical power",268  current_power_mw: "Total power",269  total_power: "Total power",270  total_power_mw: "Total power",271  planned_power_mw: "Planned",272  planned: "Planned",273  grid_connection_mw: "Grid connection",274  grid_connection: "Grid connection",275  utility_capacity_mw: "Utility",276  utility: "Utility",277  utility_capacity: "Utility",278  ultimate_campus_mw: "Ultimate build-out",279  ultimate: "Ultimate build-out",280  phase_mw: "Phase capacity",281  project_investment_usd: "Project investment",282  campus_investment_usd: "Campus investment",283  company_investment_usd: "Company investment",284  country_program_usd: "Country programme",285  multi_year_capex_usd: "Multi-year capex",286  deal_value_usd: "Deal value",287  status: "Status",288};289export function semanticsLabel(s: string | null | undefined): string | null {290  if (!s) return null;291  return SEMANTICS_LABEL[s] ?? s.replace(/_mw$|_usd$/, "").replace(/_/g, " ").replace(/^./, (c) => c.toUpperCase());292}293294export const AUTHORITY_TIER_LABEL: Record<AuthorityTier, string> = {295  A: "Tier A — primary authority for this field",296  B: "Tier B — first-party or provider source",297  C: "Tier C — registry / secondary source",298  D: "Tier D — press coverage",299  E: "Tier E — dataset / community source",300};301302export const CLAIM_STATUS_LABEL: Record<ClaimStatus, string> = {303  current: "Current",304  superseded: "Superseded",305  rejected: "Rejected",306  review: "In review",307  unscoped: "Not site-scoped",308};309310/** Extraction method families for the evidence drawer (from provenance.method / claims.parserName). */311export type ExtractionKind = "structured" | "parser" | "regex" | "reviewed" | "llm" | "unknown";312export function extractionKind(method: string | null | undefined): ExtractionKind {313  if (!method) return "unknown";314  const m = method.toLowerCase();315  if (/review|manual|human|curated/.test(m)) return "reviewed";316  if (/llm|gpt|claude|model/.test(m)) return "llm";317  if (/regex|prose|sentence|text/.test(m)) return "regex";318  if (/json-?ld|dataset|api|structured|embedded|spec|table|field/.test(m)) return "structured";319  return "parser";320}321export const EXTRACTION_LABEL: Record<ExtractionKind, string> = {322  structured: "Structured data",323  parser: "Parser",324  regex: "Sentence extraction",325  reviewed: "Human-reviewed",326  llm: "Model-assisted",327  unknown: "Method not recorded",328};329330export const SIGNIFICANCE_LABEL: Record<Significance, string> = { major: "Major", medium: "Medium", minor: "Minor" };331export function significanceBand(value: number, band?: Significance | null): Significance {332  if (band) return band;333  return value >= 70 ? "major" : value >= 40 ? "medium" : "minor";334}335336export const AI_EVIDENCE_LABEL: Record<AiEvidence, string> = { confirmed: "AI · confirmed", likely: "AI · likely", associated: "AI · associated", unknown: "AI evidence unknown" };337338/** Project lifecycle stages in funnel order (side branches last). */339export const PROJECT_STAGE_ORDER: ProjectStage[] = ["rumored", "proposed", "announced", "permitting", "approved", "under_construction", "partially_operational", "operational", "delayed", "cancelled"];340export const STAGE_LABEL: Record<ProjectStage, string> = {341  rumored: "Rumored",342  proposed: "Proposed",343  announced: "Announced",344  permitting: "Permitting",345  approved: "Approved",346  under_construction: "Construction",347  partially_operational: "Partially open",348  operational: "Opened",349  delayed: "Delayed",350  cancelled: "Cancelled",351};352353export const MAP_LAYER_LABEL: Record<MapLayer, string> = {354  facilities: "Facilities",355  capacity: "Capacity",356  projects: "Projects",357  ai: "AI / HPC",358  cloud: "Cloud",359  ixps: "IXPs",360  connectivity: "Connectivity",361  power: "Power",362  pipeline: "Pipeline",363};364export const DENSITY_LABEL: Record<DensityView, string> = {365  facilities: "Facility density",366  known_mw: "Known MW",367  pipeline_mw: "Pipeline MW",368  ai: "AI facilities",369  operators: "Operator diversity",370  cloud: "Cloud regions",371  ixps: "Internet exchanges",372};373374export const SEARCH_GROUP_LABEL: Record<string, string> = {375  facility: "Data centers",376  operator: "Operators",377  country: "Countries",378  metro: "Markets",379  city: "Cities",380  cloud_region: "Cloud regions",381  project: "Projects",382  ixp: "Internet exchanges",383};384