spb/datacenterindex
Public
HTML 53.9%
TypeScript 44.5%
JavaScript 0.6%
SQL 0.5%
1import { countryFromText } from "@dci/core";23/**4 * Curated list of data-center cities / metros (≈ 300) with their region and ISO-2 country, plus US states and5 * Canadian provinces. Used to locate an announcement from free text. Ambiguous names (Birmingham, Lancaster…)6 * are deliberately absent: they are only resolved through an explicit "City, State" pattern.7 */8export interface CityEntry { city: string; region?: string; country: string; aliases?: string[] }910const c = (city: string, country: string, region?: string, aliases?: string[]): CityEntry => ({ city, country, region, aliases });1112export const CITIES: CityEntry[] = [13 // United States — Virginia / Mid-Atlantic14 c("Ashburn", "US", "Virginia"), c("Leesburg", "US", "Virginia"), c("Sterling", "US", "Virginia"), c("Manassas", "US", "Virginia"), c("Gainesville", "US", "Virginia"), c("Haymarket", "US", "Virginia"),15 c("Culpeper", "US", "Virginia"), c("Richmond", "US", "Virginia"), c("Henrico County", "US", "Virginia", ["Henrico"]), c("Chesterfield County", "US", "Virginia", ["Chesterfield"]), c("Spotsylvania County", "US", "Virginia", ["Spotsylvania"]),16 c("Stafford County", "US", "Virginia", ["Stafford"]), c("Louisa County", "US", "Virginia", ["Louisa"]), c("Loudoun County", "US", "Virginia", ["Loudoun"]), c("Prince William County", "US", "Virginia", ["Prince William"]), c("Fauquier County", "US", "Virginia", ["Fauquier"]),17 c("Fairfax County", "US", "Virginia", ["Fairfax"]), c("Mecklenburg County", "US", "Virginia"), c("Pittsylvania County", "US", "Virginia"), c("Frederick", "US", "Maryland"), c("Baltimore", "US", "Maryland"), c("Quantico", "US", "Virginia"),18 // Georgia / Southeast19 c("Atlanta", "US", "Georgia"), c("Douglasville", "US", "Georgia"), c("Lithia Springs", "US", "Georgia"), c("Fayetteville", "US", "Georgia"), c("Coweta County", "US", "Georgia"), c("Newton County", "US", "Georgia"), c("Bartow County", "US", "Georgia"),20 c("Charlotte", "US", "North Carolina"), c("Forest City", "US", "North Carolina"), c("Maiden", "US", "North Carolina"), c("Nashville", "US", "Tennessee"), c("Memphis", "US", "Tennessee"), c("Clarksville", "US", "Tennessee"),21 c("Huntsville", "US", "Alabama"), c("Bessemer", "US", "Alabama"), c("Jackson", "US", "Mississippi"), c("Meridian", "US", "Mississippi"), c("New Orleans", "US", "Louisiana"), c("Richland Parish", "US", "Louisiana"), c("Little Rock", "US", "Arkansas"),22 c("Miami", "US", "Florida"), c("Tampa", "US", "Florida"), c("Jacksonville", "US", "Florida"), c("Orlando", "US", "Florida"), c("Greenville", "US", "South Carolina"), c("Spartanburg", "US", "South Carolina"),23 // Texas / South Central24 c("Dallas", "US", "Texas"), c("Fort Worth", "US", "Texas"), c("Garland", "US", "Texas"), c("Richardson", "US", "Texas"), c("Plano", "US", "Texas"), c("Irving", "US", "Texas"), c("Red Oak", "US", "Texas"), c("Midlothian", "US", "Texas"),25 c("Denton", "US", "Texas"), c("Houston", "US", "Texas"), c("San Antonio", "US", "Texas"), c("Austin", "US", "Texas"), c("Hutto", "US", "Texas"), c("Taylor", "US", "Texas"), c("Temple", "US", "Texas"), c("Abilene", "US", "Texas"),26 c("Amarillo", "US", "Texas"), c("Lubbock", "US", "Texas"), c("El Paso", "US", "Texas"), c("Corsicana", "US", "Texas"), c("Odessa", "US", "Texas"), c("Milam County", "US", "Texas"), c("Ellis County", "US", "Texas"),27 c("Oklahoma City", "US", "Oklahoma"), c("Pryor", "US", "Oklahoma"), c("Tulsa", "US", "Oklahoma"), c("Stillwater", "US", "Oklahoma"),28 // Midwest29 c("Chicago", "US", "Illinois"), c("Elk Grove Village", "US", "Illinois"), c("Northlake", "US", "Illinois"), c("Hoffman Estates", "US", "Illinois"), c("DeKalb", "US", "Illinois"), c("Yorkville", "US", "Illinois"),30 c("Columbus", "US", "Ohio"), c("New Albany", "US", "Ohio"), c("Hilliard", "US", "Ohio"), c("Cincinnati", "US", "Ohio"), c("Cleveland", "US", "Ohio"), c("Mount Pleasant", "US", "Wisconsin"), c("Kenosha", "US", "Wisconsin"), c("Port Washington", "US", "Wisconsin"),31 c("Minneapolis", "US", "Minnesota"), c("Rosemount", "US", "Minnesota"), c("Farmington", "US", "Minnesota"), c("Indianapolis", "US", "Indiana"), c("Lebanon", "US", "Indiana"), c("New Carlisle", "US", "Indiana"), c("Fort Wayne", "US", "Indiana"),32 c("Omaha", "US", "Nebraska"), c("Papillion", "US", "Nebraska"), c("Council Bluffs", "US", "Iowa"), c("Altoona", "US", "Iowa"), c("Des Moines", "US", "Iowa"), c("West Des Moines", "US", "Iowa"), c("Waukee", "US", "Iowa"), c("Cedar Rapids", "US", "Iowa"),33 c("Kansas City", "US", "Missouri"), c("De Soto", "US", "Kansas"), c("St. Louis", "US", "Missouri", ["Saint Louis"]), c("Detroit", "US", "Michigan"), c("Saline Township", "US", "Michigan"), c("Grand Rapids", "US", "Michigan"), c("Louisville", "US", "Kentucky"),34 // Mountain / West35 c("Phoenix", "US", "Arizona"), c("Mesa", "US", "Arizona"), c("Chandler", "US", "Arizona"), c("Goodyear", "US", "Arizona"), c("Buckeye", "US", "Arizona"), c("Tucson", "US", "Arizona"), c("Glendale", "US", "Arizona"), c("Surprise", "US", "Arizona"),36 c("Reno", "US", "Nevada"), c("Las Vegas", "US", "Nevada"), c("Henderson", "US", "Nevada"), c("Salt Lake City", "US", "Utah"), c("West Jordan", "US", "Utah"), c("Eagle Mountain", "US", "Utah"), c("Denver", "US", "Colorado"),37 c("Cheyenne", "US", "Wyoming"), c("Boise", "US", "Idaho"), c("Kuna", "US", "Idaho"), c("Albuquerque", "US", "New Mexico"), c("Los Lunas", "US", "New Mexico"), c("Santa Teresa", "US", "New Mexico"),38 // Pacific39 c("Santa Clara", "US", "California"), c("San Jose", "US", "California"), c("Silicon Valley", "US", "California"), c("Los Angeles", "US", "California"), c("El Segundo", "US", "California"), c("Sacramento", "US", "California"), c("Rancho Cordova", "US", "California"), c("San Francisco", "US", "California"),40 c("Hillsboro", "US", "Oregon"), c("Prineville", "US", "Oregon"), c("The Dalles", "US", "Oregon"), c("Umatilla", "US", "Oregon"), c("Boardman", "US", "Oregon"), c("Portland", "US", "Oregon"),41 c("Quincy", "US", "Washington"), c("Moses Lake", "US", "Washington"), c("Wenatchee", "US", "Washington"), c("Seattle", "US", "Washington"), c("Grant County", "US", "Washington"),42 // Northeast43 c("Boston", "US", "Massachusetts"), c("New York", "US", "New York", ["New York City", "NYC"]), c("Secaucus", "US", "New Jersey"), c("Piscataway", "US", "New Jersey"), c("Newark", "US", "New Jersey"), c("Carteret", "US", "New Jersey"),44 c("Philadelphia", "US", "Pennsylvania"), c("Pittsburgh", "US", "Pennsylvania"), c("Homer City", "US", "Pennsylvania"), c("Berwick", "US", "Pennsylvania"), c("Washington, D.C.", "US", "District of Columbia", ["Washington, DC", "Washington D.C."]),45 // Canada46 c("Montreal", "CA", "Quebec", ["Montréal"]), c("Quebec City", "CA", "Quebec", ["Québec City"]), c("Beauharnois", "CA", "Quebec"), c("Lévis", "CA", "Quebec", ["Levis"]), c("Toronto", "CA", "Ontario"), c("Markham", "CA", "Ontario"), c("Vaughan", "CA", "Ontario"), c("Mississauga", "CA", "Ontario"),47 c("Ottawa", "CA", "Ontario"), c("Calgary", "CA", "Alberta"), c("Edmonton", "CA", "Alberta"), c("Red Deer", "CA", "Alberta"), c("Grande Prairie", "CA", "Alberta"), c("Vancouver", "CA", "British Columbia"), c("Kamloops", "CA", "British Columbia"), c("Winnipeg", "CA", "Manitoba"), c("Halifax", "CA", "Nova Scotia"),48 // United Kingdom & Ireland49 c("London", "GB", "England"), c("Slough", "GB", "England"), c("Hayes", "GB", "England"), c("Manchester", "GB", "England"), c("Newport", "GB", "Wales"), c("Cardiff", "GB", "Wales"), c("Didcot", "GB", "England"), c("Hemel Hempstead", "GB", "England"),50 c("Cambridge", "GB", "England"), c("Hertfordshire", "GB", "England"), c("Buckinghamshire", "GB", "England"), c("Teesside", "GB", "England"), c("Blyth", "GB", "England"), c("Ravenscraig", "GB", "Scotland"), c("Edinburgh", "GB", "Scotland"), c("Glasgow", "GB", "Scotland"),51 c("Dublin", "IE", "Leinster"), c("Clondalkin", "IE", "Leinster"), c("Grange Castle", "IE", "Leinster"), c("Ennis", "IE", "Munster"), c("Cork", "IE", "Munster"),52 // Continental Europe53 c("Frankfurt", "DE", "Hesse", ["Frankfurt am Main"]), c("Hanau", "DE", "Hesse"), c("Offenbach", "DE", "Hesse"), c("Berlin", "DE", "Berlin"), c("Munich", "DE", "Bavaria", ["München"]), c("Hamburg", "DE", "Hamburg"), c("Düsseldorf", "DE", "North Rhine-Westphalia", ["Dusseldorf"]), c("Magdeburg", "DE", "Saxony-Anhalt"),54 c("Amsterdam", "NL", "North Holland"), c("Schiphol", "NL", "North Holland"), c("Haarlemmermeer", "NL", "North Holland"), c("Middenmeer", "NL", "North Holland"), c("Eemshaven", "NL", "Groningen"), c("Groningen", "NL", "Groningen"), c("Almere", "NL", "Flevoland"), c("Zeewolde", "NL", "Flevoland"),55 c("Paris", "FR", "Île-de-France"), c("Marseille", "FR", "Provence-Alpes-Côte d'Azur"), c("Lyon", "FR", "Auvergne-Rhône-Alpes"), c("Lisses", "FR", "Île-de-France"), c("Bordeaux", "FR", "Nouvelle-Aquitaine"), c("Fos-sur-Mer", "FR", "Provence-Alpes-Côte d'Azur"),56 c("Madrid", "ES", "Community of Madrid"), c("Barcelona", "ES", "Catalonia"), c("Aragon", "ES", "Aragon", ["Aragón"]), c("Zaragoza", "ES", "Aragon"), c("Milan", "IT", "Lombardy", ["Milano"]), c("Rome", "IT", "Lazio"), c("Turin", "IT", "Piedmont"),57 c("Lisbon", "PT", "Lisbon"), c("Sines", "PT", "Setúbal"), c("Porto", "PT", "Porto"), c("Stockholm", "SE", "Stockholm"), c("Gävle", "SE", "Gävleborg"), c("Luleå", "SE", "Norrbotten", ["Lulea"]), c("Boden", "SE", "Norrbotten"),58 c("Oslo", "NO", "Oslo"), c("Skien", "NO", "Telemark"), c("Hamar", "NO", "Innlandet"), c("Rjukan", "NO", "Telemark"), c("Copenhagen", "DK", "Capital Region"), c("Odense", "DK", "Southern Denmark"), c("Fredericia", "DK", "Southern Denmark"),59 c("Helsinki", "FI", "Uusimaa"), c("Hamina", "FI", "Kymenlaakso"), c("Espoo", "FI", "Uusimaa"), c("Kajaani", "FI", "Kainuu"), c("Reykjavik", "IS", undefined, ["Reykjavík"]), c("Keflavik", "IS", undefined, ["Keflavík"]),60 c("Brussels", "BE", "Brussels"), c("Zurich", "CH", "Zurich", ["Zürich"]), c("Geneva", "CH", "Geneva"), c("Vienna", "AT", "Vienna"), c("Warsaw", "PL", "Masovia"), c("Prague", "CZ", "Prague"), c("Athens", "GR", "Attica"), c("Bucharest", "RO", "Bucharest"), c("Istanbul", "TR", "Istanbul"), c("Luxembourg City", "LU", undefined, ["Luxembourg"]),61 // Middle East & Africa62 c("Dubai", "AE", "Dubai"), c("Abu Dhabi", "AE", "Abu Dhabi"), c("Riyadh", "SA", "Riyadh"), c("Dammam", "SA", "Eastern Province"), c("Jeddah", "SA", "Makkah"), c("NEOM", "SA", "Tabuk"), c("Doha", "QA"), c("Manama", "BH"), c("Muscat", "OM"), c("Tel Aviv", "IL"),63 c("Cairo", "EG"), c("Casablanca", "MA"), c("Lagos", "NG"), c("Nairobi", "KE"), c("Johannesburg", "ZA", "Gauteng"), c("Cape Town", "ZA", "Western Cape"), c("Accra", "GH"),64 // Asia-Pacific65 c("Singapore", "SG"), c("Johor Bahru", "MY", "Johor"), c("Johor", "MY", "Johor"), c("Sedenak", "MY", "Johor"), c("Kulai", "MY", "Johor"), c("Cyberjaya", "MY", "Selangor"), c("Kuala Lumpur", "MY"), c("Batam", "ID", "Riau Islands"), c("Jakarta", "ID"), c("Bekasi", "ID", "West Java"),66 c("Bangkok", "TH"), c("Chonburi", "TH"), c("Manila", "PH"), c("Ho Chi Minh City", "VN"), c("Hanoi", "VN"), c("Tokyo", "JP"), c("Inzai", "JP", "Chiba"), c("Osaka", "JP"), c("Hokkaido", "JP", "Hokkaido"), c("Seoul", "KR"), c("Incheon", "KR"), c("Busan", "KR"),67 c("Hong Kong", "HK"), c("Taipei", "TW"), c("Taoyuan", "TW"), c("Sydney", "AU", "New South Wales"), c("Melbourne", "AU", "Victoria"), c("Canberra", "AU", "Australian Capital Territory"), c("Perth", "AU", "Western Australia"), c("Brisbane", "AU", "Queensland"), c("Auckland", "NZ"),68 c("Mumbai", "IN", "Maharashtra"), c("Navi Mumbai", "IN", "Maharashtra"), c("Chennai", "IN", "Tamil Nadu"), c("Hyderabad", "IN", "Telangana"), c("Bengaluru", "IN", "Karnataka", ["Bangalore"]), c("Pune", "IN", "Maharashtra"), c("Noida", "IN", "Uttar Pradesh"), c("Delhi", "IN", "Delhi", ["New Delhi"]), c("Kolkata", "IN", "West Bengal"), c("Visakhapatnam", "IN", "Andhra Pradesh"),69 c("Shanghai", "CN"), c("Beijing", "CN"), c("Shenzhen", "CN", "Guangdong"), c("Ulanqab", "CN", "Inner Mongolia"), c("Zhangjiakou", "CN", "Hebei"), c("Guizhou", "CN", "Guizhou"),70 // Latin America71 c("São Paulo", "BR", "São Paulo", ["Sao Paulo"]), c("Campinas", "BR", "São Paulo"), c("Rio de Janeiro", "BR", "Rio de Janeiro"), c("Fortaleza", "BR", "Ceará"), c("Santiago", "CL"), c("Bogotá", "CO", undefined, ["Bogota"]), c("Querétaro", "MX", "Querétaro", ["Queretaro"]), c("Mexico City", "MX"), c("Monterrey", "MX", "Nuevo León"),72 c("Buenos Aires", "AR"), c("Lima", "PE"), c("Montevideo", "UY"),73];7475export const US_STATES: Record<string, string> = {76 AL: "Alabama", AK: "Alaska", AZ: "Arizona", AR: "Arkansas", CA: "California", CO: "Colorado", CT: "Connecticut", DE: "Delaware", FL: "Florida", GA: "Georgia", HI: "Hawaii", ID: "Idaho", IL: "Illinois", IN: "Indiana", IA: "Iowa", KS: "Kansas", KY: "Kentucky", LA: "Louisiana", ME: "Maine", MD: "Maryland", MA: "Massachusetts", MI: "Michigan", MN: "Minnesota", MS: "Mississippi", MO: "Missouri", MT: "Montana", NE: "Nebraska", NV: "Nevada", NH: "New Hampshire", NJ: "New Jersey", NM: "New Mexico", NY: "New York", NC: "North Carolina", ND: "North Dakota", OH: "Ohio", OK: "Oklahoma", OR: "Oregon", PA: "Pennsylvania", RI: "Rhode Island", SC: "South Carolina", SD: "South Dakota", TN: "Tennessee", TX: "Texas", UT: "Utah", VT: "Vermont", VA: "Virginia", WA: "Washington", WV: "West Virginia", WI: "Wisconsin", WY: "Wyoming", DC: "District of Columbia",77};78export const CA_PROVINCES: Record<string, string> = { AB: "Alberta", BC: "British Columbia", MB: "Manitoba", NB: "New Brunswick", NL: "Newfoundland and Labrador", NS: "Nova Scotia", ON: "Ontario", PE: "Prince Edward Island", QC: "Quebec", SK: "Saskatchewan" };7980const STATE_NAMES = new Map<string, { region: string; country: string }>();81for (const [ab, n] of Object.entries(US_STATES)) { STATE_NAMES.set(n.toLowerCase(), { region: n, country: "US" }); STATE_NAMES.set(ab, { region: n, country: "US" }); }82for (const [ab, n] of Object.entries(CA_PROVINCES)) { STATE_NAMES.set(n.toLowerCase(), { region: n, country: "CA" }); STATE_NAMES.set(ab, { region: n, country: "CA" }); }83STATE_NAMES.set("québec", { region: "Quebec", country: "CA" });8485function escapeRe(s: string): string { return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }86const cityPatterns: Array<{ entry: CityEntry; re: RegExp }> = CITIES.flatMap((entry) => [entry.city, ...(entry.aliases ?? [])].map((a) => ({ entry, re: new RegExp(`(?<![A-Za-z])${escapeRe(a)}(?![a-z])`, "u") }))).sort((a, b) => b.re.source.length - a.re.source.length);8788const stateAlternation = [...Object.values(US_STATES), ...Object.values(CA_PROVINCES), "Québec", ...Object.keys(US_STATES), ...Object.keys(CA_PROVINCES)].sort((a, b) => b.length - a.length).map(escapeRe).join("|");89/** "Frederick, Maryland" / "Frederick, MD" / "Frederick, Md." — the city token must look like a proper noun. */90const CITY_STATE_RE = new RegExp(`\\b([A-Z][a-zA-Z.'-]+(?:[ -][A-Z][a-zA-Z.'-]+){0,3}),\\s*(${stateAlternation})(?![a-z])\\.?(?![A-Za-z])`, "g");91/** Standalone state / province name ("in Virginia", "Texas-based"). */92const STATE_RE = new RegExp(`(?<![A-Za-z])(${[...Object.values(US_STATES), ...Object.values(CA_PROVINCES), "Québec"].sort((a, b) => b.length - a.length).map(escapeRe).join("|")})(?![a-z])`, "g");9394export interface LocationHit { city: string | null; region: string | null; country: string | null; method: string; index: number }9596const NOT_A_CITY = /^(The|A|An|In|At|Of|By|From|Data|Center|Centre|County|Inc|LLC|Corp|Group|North|South|East|West|Northern|Southern|Central|New|Greater|Downtown|Rural|Metro|AI|MW|GW|US|U\.S\.|Today|Yesterday|Tomorrow|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday|January|February|March|April|May|June|July|August|September|October|November|December|Gov|Governor|Sen|Senator|Rep|Mayor|President|CEO|Inc\.|Company|Energy|Power|Electric|Utility|University|College|State|Commonwealth|Republic|Kingdom|Government|Department|Board|Council|Commission)$/;9798/** Best location in a text: explicit "City, State" first, then curated cities, then standalone states, then countries. */99export function detectLocation(text: string): LocationHit | null {100 if (!text) return null;101 CITY_STATE_RE.lastIndex = 0;102 for (const m of text.matchAll(CITY_STATE_RE)) {103 // "54MW Data Center In Dallas, Texas": drop leading words that cannot start a city name until a proper noun is left104 const words = m[1]!.replace(/\.$/, "").split(/[ ]/);105 while (words.length > 1 && (NOT_A_CITY.test(words[0]!) || /^(Near|Outside|Around|Into|Onto|Across|Through|Plans?|Planned|Proposed|Approves?|Approved|Files?|Builds?|Opens?|Campus|Facility|Site|Project|Hub|Park)$/i.test(words[0]!))) words.shift();106 const city = words.join(" ");107 const st = m[2]!;108 const stateInfo = STATE_NAMES.get(st.length <= 2 ? st : st.toLowerCase());109 if (!stateInfo || NOT_A_CITY.test(city)) continue;110 // "County, Virginia" is fine, but "Virginia, USA"-style matches are not a city111 if (STATE_NAMES.has(city.toLowerCase())) continue;112 return { city: city.replace(/\b(city|town) of\b\s*/i, ""), region: stateInfo.region, country: stateInfo.country, method: "regex:city_state", index: m.index ?? 0 };113 }114 // earliest hit wins, but a proper city beats a county / parish named earlier in the same text115 let best: { entry: CityEntry; index: number; county: boolean } | null = null;116 for (const { entry, re } of cityPatterns) {117 const m = re.exec(text);118 if (!m) continue;119 const county = /\b(County|Parish|Township)$/.test(entry.city);120 if (!best || (county === best.county ? m.index < best.index : !county)) best = { entry, index: m.index, county };121 }122 if (best) return { city: best.entry.city, region: best.entry.region ?? null, country: best.entry.country, method: "lexicon:city", index: best.index };123 STATE_RE.lastIndex = 0;124 const sm = STATE_RE.exec(text);125 if (sm) {126 const info = STATE_NAMES.get(sm[1]!.toLowerCase());127 // "Georgia" alone is the US state unless the text is clearly about the country128 if (info && !(info.region === "Georgia" && /\b(Tbilisi|Republic of Georgia|Georgian government|Caucasus)\b/.test(text))) return { city: null, region: info.region, country: info.country, method: "lexicon:state", index: sm.index };129 }130 const country = countryFromTextSafe(text);131 if (country) return { city: null, region: null, country, method: "lexicon:country", index: 0 };132 return null;133}134135/** countryFromText with the traps removed: "Latin/North America" is not the US, "Georgia" is a US state, "Jordan" is a person. */136export function countryFromTextSafe(text: string | null | undefined): string | null {137 if (!text) return null;138 const cleaned = text139 .replace(/\b(Latin|North|South|Central) America(n)?\b/g, " ")140 .replace(/\bAmericas\b/g, " ")141 .replace(/\bGeorgia\b/g, (m) => (/\b(Tbilisi|Republic of Georgia|Caucasus)\b/.test(text) ? m : " "))142 .replace(/\b[A-Z][a-z]+ Jordan\b/g, " ");143 return countryFromText(cleaned);144}145