spb/datacenterindex
Public
HTML 53.9%
TypeScript 44.5%
JavaScript 0.6%
SQL 0.5%
1/** Operators2 — Americas facility parsers (US/CA/LATAM colocation + wholesale operators). */2import type { ExtractedRecord } from "@dci/connectors";3import { extractGeo } from "@dci/connectors";4import { countryFromText } from "@dci/core";5import { usStateCode } from "../operators1/shared.js";6import { Rec, all, applyLdAddress, applyLdGeo, breadcrumbLeaf, capacityMw, certifications, countryFromCity, countryOf, describe, facilityParser, first, ha, itMw, key, latinNumbers, ldWithAddress, mw, pue, safeCountryText, seg, splitStreetCity, sqm, statusOf, tier, titleCase, usAddress } from "./shared.js";78/** DataBank — /data-centers/<metro>/[<campus>/]<facility>/ ; "<Name> (<CODE>)" heading followed by the street address; stats in .c-figure-stats / .c-info-table__stats. Campus pages (code before name) yield nothing. */9facilityParser("databank_facility_v1", (p) => {10 const head = p.text.match(/([A-Z][^\n(]{3,80}?)\s*\n\s*\(([A-Z]{2,5}\s?\d{1,2})\)\s*\n\s*(\d[^\n]*?)\s+-\s+([^,\n]+),\s+([A-Z]{2})\s+(\d{5})/);11 const uk = head ? null : p.text.match(/([A-Z][^\n(]{3,80}?)\s*\n\s*\(([A-Z]{2,5}\s?\d{1,2})\)\s*\n\s*([^\n]{6,120}?)\s+-\s+([A-Za-z ]+),?\s+([A-Z]{1,2}\d[\dA-Z]? ?\d[A-Z]{2})\b/);12 const m = head ?? uk;13 if (!m) return []; // metro / campus index page14 const code = m[2]!.replace(/\s+/g, "");15 const r = new Rec();16 r.set("name", `${m[1]!.trim().replace(/^Explore\s+/i, "")} (${code})`, "regex:heading_code").set("code", code, "regex:heading_code");17 if (head) r.set("address", head[3], "regex:address_line").set("city", head[4], "regex:address_line").set("regionName", head[5], "regex:address_line").set("postalCode", head[6], "regex:address_line").set("countryIso2", "US", "regex:address_line");18 else if (uk) r.set("address", uk[3], "regex:address_line_uk").set("city", uk[4], "regex:address_line_uk").set("postalCode", uk[5], "regex:address_line_uk").set("countryIso2", "GB", "regex:address_line_uk");19 p.$(".c-figure-stats li, .c-info-table__stats li").each((_, li) => {20 const strong = p.$(li).find("strong").first().text().trim();21 const label = p.$(li).text().replace(strong, "").trim();22 if (/IT Square Feet/i.test(label)) r.set("buildingSqm", sqm(`${strong} sq ft`), "selector:.c-figure-stats+sqft");23 else if (/Critical IT Load/i.test(label)) r.set("itCapacityMw", mw(strong), "selector:.c-info-table__stats+mw");24 else if (/Energy Source/i.test(label) && /renewable/i.test(strong)) r.set("renewableClaim", `${strong} energy source`, "selector:.c-info-table__stats");25 else if (/Cooling Design/i.test(label)) r.set("coolingType", `${strong} cooling design`, "selector:.c-info-table__stats");26 });27 if (!r.has("itCapacityMw")) r.set("itCapacityMw", itMw(p.text), "regex:it_mw_v1");28 r.set("certifications", certifications(p.text), "regex:certifications_v1").set("description", describe(p), "meta:description");29 return [r.done(key("databank", code), p.url)];30});3132/**33 * EdgeConneX — metro pages; one JSON-LD LocalBusiness per facility (address only). Without JSON-LD, one metro-level record34 * (city / state from the BreadcrumbList leaf "Richmond, VA", else from the h1 minus its "Data Center" suffix; facility codes as aliases).35 */36facilityParser("edgeconnex_facility_v1", (p) => {37 const out: ExtractedRecord[] = [];38 const metro = seg(p.url).pop() ?? "";39 for (const o of ldWithAddress(p.html, "LocalBusiness")) {40 const a = o.address;41 if (!a.streetAddress) continue;42 const r = new Rec();43 r.set("name", `EdgeConneX ${String(a.addressLocality ?? titleCase(metro))} – ${String(a.streetAddress)}`, "json-ld:LocalBusiness.name+street");44 applyLdAddress(r, a); applyLdGeo(r, o);45 r.set("description", describe(p), "meta:description").set("certifications", certifications(p.text), "regex:certifications_v1");46 out.push(r.done(key("edgeconnex", metro, String(a.streetAddress)), p.url, 0.8));47 }48 if (out.length) return out;49 const h1 = p.h1 ?? "";50 if (!h1 || !/edgeconnex/i.test(p.title ?? "")) return [];51 // h1 "Richmond Data Center" is the facility name; the breadcrumb leaf "Richmond, VA" is the location line52 const crumb = breadcrumbLeaf(p.html);53 const locLine = crumb && crumb.length <= 60 ? crumb : h1.replace(/\s+Data\s+Cent(?:er|re)s?$/i, "");54 const locMethod = crumb && crumb.length <= 60 ? "json-ld:BreadcrumbList" : "selector:h1";55 const loc = locLine.match(/^([A-Z][^,]+?)(?:,\s*([A-Z][^,]+))?$/);56 if (!loc) return [];57 const r = new Rec();58 r.set("name", `EdgeConneX ${h1}`, "selector:h1").set("city", loc[1], locMethod);59 const region = loc[2]?.trim() ?? null;60 const c = region && usStateCode(region) ? { iso: "US", method: "lookup:us-state" } : countryOf(region, h1, p.desc, p.text.slice(0, 1500));61 if (c) r.set("countryIso2", c.iso, c.method);62 if (region && c?.iso === "US") r.set("regionName", region, locMethod);63 const codes = [...new Set([...p.text.matchAll(/\b([A-Z]{3}\d{2})\b/g)].map((m) => m[1]!))];64 r.set("aliases", codes, "regex:facility_codes");65 const single = codes.length === 1 ? p.text.match(new RegExp(`${codes[0]}:\\s*(\\d[\\d.]*\\+?\\s*MW\\+?)`)) : null;66 const st = statusOf(p.text, 2500); if (st) r.set("status", st, "regex:status_v1");67 if (single) r.set(st && st !== "operational" ? "plannedPowerMw" : "itCapacityMw", mw(single[1]), "regex:code_mw_row");68 r.set("tier", tier(p.text), "regex:tier_v1").set("certifications", certifications(p.text), "regex:certifications_v1").set("description", describe(p), "meta:description");69 return [r.done(key("edgeconnex", metro), p.url, 0.65)];70});7172/** TierPoint — /data-centers/<state>/<slug>/ ; JSON-LD LocalBusiness (#location) with address + geo. */73facilityParser("tierpoint_facility_v1", (p) => {74 const o = ldWithAddress(p.html, "LocalBusiness").find((x) => String(x["@id"] ?? "").includes("#location")) ?? ldWithAddress(p.html, "LocalBusiness")[0];75 if (!o) return [];76 const r = new Rec();77 r.set("name", (o.name as string) ?? p.h1, "json-ld:LocalBusiness.name");78 applyLdAddress(r, o.address); applyLdGeo(r, o);79 r.set("description", (o.description as string) ?? describe(p), "json-ld:LocalBusiness.description");80 r.set("itCapacityMw", itMw(p.text), "regex:it_mw_v1").set("buildingSqm", sqm(first(p.text, /([\d,]+\+?\s*(?:total\s+)?square feet)/i)), "regex:sqft_v1");81 r.set("pue", pue(p.text), "regex:pue_v1").set("tier", tier(p.text), "regex:tier_v1").set("certifications", certifications(p.text), "regex:certifications_v1");82 const st = statusOf(`${r.data.description ?? ""}\n${p.text}`); if (st) r.set("status", st, "regex:status_v1");83 const slug = seg(p.url).slice(-2).join("-");84 return [r.done(key("tierpoint", slug), p.url, 0.9)];85});8687/** Evocative — /data-centers/<city>-<st>-data-center-<code>/ ; address in Google Maps embed, MW in meta description. */88facilityParser("evocative_facility_v1", (p) => {89 const slug = seg(p.url).pop() ?? "";90 const m = slug.match(/^(.+?)-data-center-([a-z]{3}\d+)$/);91 if (!m) return [];92 const code = m[2]!.toUpperCase();93 const loc = m[1]!.split("-");94 const region = loc.length > 1 && loc[loc.length - 1]!.length === 2 ? loc.pop()!.toUpperCase() : null;95 const r = new Rec();96 r.set("code", code, "url:slug").set("name", `Evocative ${titleCase(loc.join("-"))} ${code}`, "url:slug").set("city", titleCase(loc.join("-")), "url:slug").set("regionName", region, "url:slug").set("countryIso2", "US", "default:operator");97 const q = p.html.match(/maps\/embed\/v1\/place\?[^"']*?\bq=([^&"']+)/);98 if (q) { const addr = usAddress(decodeURIComponent(q[1]!.replace(/\+/g, " "))); if (addr) r.set("address", addr.street, "link:google-maps-embed").set("city", addr.city, "link:google-maps-embed").set("regionName", addr.region, "link:google-maps-embed").set("postalCode", addr.postal, "link:google-maps-embed"); }99 const desc = p.desc ?? "";100 r.set("itCapacityMw", itMw(desc) ?? itMw(p.text), "regex:it_mw_v1").set("buildingSqm", sqm(first(desc, /([\d,]+\s*(?:sq\.?\s*ft|sqft|square feet))/i)), "meta:description+sqft");101 r.set("tier", tier(p.text), "regex:tier_v1").set("certifications", certifications(p.text), "regex:certifications_v1").set("description", describe(p), "meta:description");102 return [r.done(key("evocative", code), p.url)];103});104105/** Serverfarm — /<code>-<city>-data-center/ ; data-lat/lng map marker, spec list "IT 100MW", carriers. */106facilityParser("serverfarm_facility_v1", (p) => {107 const h1 = p.h1 ?? p.title ?? "";108 if (!/data center/i.test(h1)) return [];109 const r = new Rec();110 const parts = h1.split(/\s[–-]\s/);111 const code = parts.length > 1 && /^[A-Z]{2,4}\d/.test(parts[0]!.trim()) ? parts[0]!.trim() : null;112 const cityPart = (parts[parts.length - 1] ?? h1).replace(/\s*data center.*$/i, "").trim();113 r.set("name", `Serverfarm ${h1.replace(/\s*data center\s*$/i, "")}`, "selector:h1").set("code", code, "selector:h1").set("city", cityPart, "selector:h1");114 const c = countryOf(cityPart) ?? countryOf(p.text.slice(0, 3000)); if (c) r.set("countryIso2", c.iso, c.method);115 const g = extractGeo(p.html); if (g && g.method === "attr:data-lat") r.set("lat", g.lat, g.method).set("lng", g.lng, g.method).set("geoPrecision", "exact", g.method);116 r.set("itCapacityMw", mw(first(p.text, /\bIT\s+(\d[\d.,]*\+?\s*MW\+?)/)) ?? itMw(p.text), "regex:it_mw_serverfarm").set("pue", pue(p.text), "regex:pue_v1");117 const carriers = first(p.text, /Carriers\s*\n?\s*([A-Z][^\n]{5,300}?)(?:\n|$)/); if (carriers && /,/.test(carriers)) r.set("carriers", carriers.split(/,\s*/).map((s) => s.trim()).filter((s) => s.length > 1 && s.length < 40), "regex:carriers_list");118 r.set("certifications", certifications(p.text), "regex:certifications_v1").set("description", describe(p), "meta:description");119 return [r.done(key("serverfarm", code ?? cityPart), p.url)];120});121122/** Sabey — /locations/<slug> ; campus stats (MW capacity, PUE, sqft), city from prose. */123facilityParser("sabey_facility_v1", (p) => {124 const name = (p.title ?? "").split(/\s*[|–-]\s*/)[0]?.trim();125 if (!name || !/^SDC\b/i.test(name)) return [];126 const r = new Rec();127 r.set("name", `Sabey ${name}`, "html:title").set("countryIso2", "US", "default:operator");128 const loc = p.text.match(/(?:campus|data center) in ([A-Z][a-zA-Z.]+(?: [A-Z][a-zA-Z.]+)?), ([A-Z][a-z]+(?: [A-Z][a-z]+)?|[A-Z]{2})\b/);129 if (loc) r.set("city", loc[1], "regex:in_city_state").set("regionName", loc[2], "regex:in_city_state");130 r.set("totalPowerMw", mw(first(p.text, /(\d[\d.,]*)\s*\n?\s*MW Capacity/i)?.concat(" MW")) ?? capacityMw(p.text), "regex:mw_capacity_stat");131 r.set("pue", pue(p.text), "regex:pue_v1").set("buildingSqm", sqm(first(p.text, /([\d,]+\s*square feet)/i)), "regex:sqft_v1").set("certifications", certifications(p.text), "regex:certifications_v1").set("description", describe(p), "meta:description");132 const st = statusOf(p.text); if (st) r.set("status", st, "regex:status_v1");133 return [r.done(key("sabey", seg(p.url).pop()), p.url)];134});135136/** Stream Data Centers — /locations/<slug>/ ; "<address or City, State> Product Type: … Status: …" + hero stats (MW, SF, acres). */137facilityParser("stream_facility_v1", (p) => {138 const tag = p.text.match(/\n\s*(?:Product Type|Status):/);139 if (!tag || tag.index == null) return []; // metro page140 const before = p.text.slice(0, tag.index).split("\n").map((l) => l.trim()).filter(Boolean);141 const locLine = before[before.length - 1] ?? "";142 const tail = p.text.slice(tag.index, tag.index + 400);143 const ptype = first(tail, /Product Type:\s*([^\n]+)/) ?? "";144 const status = first(tail, /Status:\s*([^\n]+)/) ?? "";145 const r = new Rec();146 const code = p.h1 ?? p.title ?? "";147 r.set("name", `Stream ${code}`, "selector:h1").set("code", /^[A-Z]{3,5}\d?$/.test(code) ? code : null, "selector:h1").set("countryIso2", "US", "default:operator");148 const addr = locLine.match(/^(\d[^\n,]*?)\s*,?\s*([A-Z][a-zA-Z.'\- ]+?),\s*([A-Z][a-z]+(?: [A-Z][a-z]+)?|[A-Z]{2})\s*(\d{5})?$/);149 const csz = locLine.match(/^([A-Z][a-zA-Z.'\- ]+?),\s*([A-Z][a-z]+(?: [A-Z][a-z]+)?|[A-Z]{2})\s*(\d{5})?$/);150 if (addr && /\d{5}$/.test(locLine)) {151 const sc = splitStreetCity(`${addr[1]} ${addr[2]}`.trim());152 if (sc) r.set("address", sc.street, "regex:address_line").set("city", sc.city, "regex:address_line"); else r.set("city", addr[2], "regex:address_line");153 r.set("regionName", addr[3], "regex:address_line").set("postalCode", addr[4] ?? null, "regex:address_line");154 } else if (csz) r.set("city", csz[1], "regex:city_state").set("regionName", csz[2], "regex:city_state").set("postalCode", csz[3] ?? null, "regex:city_state");155 else { const cs = locLine.match(/([A-Z][A-Za-z.'\- ]+?),\s*([A-Z][a-z]+(?: [A-Z][a-z]+)?)$/); if (cs) r.set("city", cs[1], "regex:city_state").set("regionName", cs[2], "regex:city_state"); }156 if (status) r.set("status", /leased|operational|in service|complete|sold|stabili[sz]ed/i.test(status) ? "operational" : /construction/i.test(status) ? "under_construction" : /planned|future|available|development|land/i.test(status) ? "announced" : null, "regex:status_label");157 if (/hyperscale/i.test(ptype)) r.set("facilityType", "hyperscale", "regex:product_type"); else if (/wholesale|build-to-suit/i.test(ptype)) r.set("facilityType", "wholesale", "regex:product_type"); else if (/private|enterprise/i.test(ptype)) r.set("facilityType", "enterprise", "regex:product_type");158 r.set("itCapacityMw", itMw(p.text), "regex:it_mw_v1");159 if (!r.has("itCapacityMw")) r.set("totalPowerMw", mw(first(p.text, /^\s*(\d[\d.]*\+?\s*MW\+?)\s*\n\s*(?:Campus|Power|Capacity)/m)), "regex:hero_mw_stat");160 r.set("buildingSqm", sqm(first(p.text, /([\d,]+\s*(?:Square Feet|SF))\s*\n?\s*(?:Data Center|Building|$)/im) ?? first(p.text, /([\d,]+\s*Square Feet)/i)), "regex:sqft_v1").set("siteAreaHa", ha(first(p.text, /([\d.]+\s*Acres)/i)), "regex:acres_v1");161 r.set("certifications", certifications(p.text), "regex:certifications_v1").set("description", describe(p), "meta:description");162 return [r.done(key("stream", seg(p.url).pop()), p.url)];163});164165/** Skybox — /locations/<slug> ; stat header "CITY 30 MW 141,000 SF", JSON-LD Organization description. */166facilityParser("skybox_facility_v1", (p) => {167 const seg1 = (p.title ?? "").split("|").map((s) => s.trim()).find((s) => s && !/^Skybox Datacenters$/i.test(s));168 if (!seg1) return [];169 const name = /^Skybox/i.test(seg1) ? seg1 : `Skybox ${seg1}`;170 const r = new Rec();171 r.set("name", name.replace(/™/g, ""), "html:title").set("countryIso2", "US", "default:operator");172 const h1 = (p.h1 ?? "").replace(/™/g, "");173 const dev = /PowerCampus|Build to Suit|Development/i.test(h1);174 const hero = p.text.match(/^\s*([A-Za-z™ ]{2,40})\n\s*(\d[\d.,]*\+?\s*MW\+?)\n(?:\s*([\d,]+\s*SF)\n)?(?:\s*([\d,.]+\s*Acres)\n)?/m);175 const cityRaw = h1.replace(/^PowerCampus\s*/i, "").trim() || hero?.[1]?.trim() || "";176 r.set("city", cityRaw ? titleCase(cityRaw.toLowerCase()) : null, "selector:h1");177 if (hero) { r.set(dev ? "plannedPowerMw" : "totalPowerMw", mw(hero[2]), "regex:hero_stats").set("buildingSqm", sqm(hero[3] ?? null), "regex:hero_stats").set("siteAreaHa", ha(hero[4] ?? null), "regex:hero_stats"); }178 const state = first(`${p.desc ?? ""} ${p.text.slice(0, 1500)}`, /\b(Texas|Georgia|Illinois|California|Virginia|Arizona|Ohio|Oregon|Nevada|Washington|Colorado|Florida|Utah|Iowa)\b/); if (state) r.set("regionName", state, "regex:state_name");179 if (!r.has("siteAreaHa")) r.set("siteAreaHa", ha(first(p.text, /((?:\d+|[a-z-]+)\s*-?\s*acre)/i)?.replace(/^(twenty-five|twenty|ten|fifteen|thirty|forty|fifty)\b/i, (w) => ({ ten: "10", fifteen: "15", twenty: "20", "twenty-five": "25", thirty: "30", forty: "40", fifty: "50" })[w.toLowerCase()] ?? w)), "regex:acres_v1");180 r.set("certifications", certifications(p.text), "regex:certifications_v1").set("description", describe(p), "meta:description");181 const st = statusOf(p.text, 1500); if (st) r.set("status", st, "regex:status_v1"); else if (dev) r.set("status", "announced", "regex:powercampus_development");182 if (dev) r.set("facilityType", "hyperscale", "regex:powercampus");183 return [r.done(key("skybox", seg(p.url).pop()), p.url)];184});185186/** Hivelocity — /products/colocation/<slug>/ ; address block, code, sqft, carriers, certifications. */187facilityParser("hivelocity_facility_v1", (p) => {188 const h1 = p.h1 ?? "";189 if (!/colocation|data center/i.test(h1)) return [];190 const r = new Rec();191 const code = first(p.text, /\b([A-Z]{3}\d)\s+Colo/);192 r.set("name", `Hivelocity ${h1.replace(/\s*colocation\s*data\s*center.*$/i, "").trim()}${code ? ` (${code})` : ""}`, "selector:h1").set("code", code, "regex:code_colo");193 const addr = usAddress(p.text.slice(0, 4000));194 if (addr) r.set("address", addr.street, "regex:us_address_v1").set("city", addr.city, "regex:us_address_v1").set("regionName", addr.region, "regex:us_address_v1").set("postalCode", addr.postal, "regex:us_address_v1").set("countryIso2", /^\d{5}/.test(addr.postal) ? "US" : "CA", "regex:us_address_v1");195 else { const c = countryOf(p.text.slice(0, 3000)); if (c) r.set("countryIso2", c.iso, c.method); }196 r.set("buildingSqm", sqm(first(p.text, /([\d,]+\s*square feet)/i)), "regex:sqft_v1");197 const carriers = first(p.text, /Carrier Neutral\s*[—-]\s*([^\n]{5,300}?)(?:\s+Peering|\n)/i); if (carriers) r.set("carriers", carriers.split(/,\s*/).map((s) => s.replace(/\(.*?\)/g, "").trim()).filter((s) => s.length > 1 && s.length < 40), "regex:carriers_list");198 r.set("certifications", certifications(p.text), "regex:certifications_v1").set("description", describe(p), "meta:description");199 return [r.done(key("hivelocity", code ?? seg(p.url).pop()), p.url)];200});201202/** Element Critical — /data-centers/<slug>/ ; "Location <street>, <city>, <state>, <zip>" spec table. */203facilityParser("elementcritical_facility_v1", (p) => {204 const loc = p.text.match(/Location\s*\n?\s*(\d[^\n]*?),\s*([A-Z][a-zA-Z .]+?),\s*([A-Z][a-zA-Z ]+?),?\s*(\d{5})\b/);205 if (!loc) return []; // metro page206 const r = new Rec();207 const name = (p.h1 ?? p.title ?? "").replace(/\s*data center services\s*$/i, "").replace(/\s*[-|].*$/, "").trim();208 r.set("name", `Element Critical ${name}`, "selector:h1").set("address", loc[1], "regex:location_row").set("city", loc[2], "regex:location_row").set("regionName", loc[3], "regex:location_row").set("postalCode", loc[4], "regex:location_row").set("countryIso2", "US", "regex:location_row");209 r.set("buildingSqm", sqm(first(p.text, /Property Size\s*\n?\s*([\d,]+\s*Sq\.?\s*Ft\.?)/i)), "regex:property_size").set("totalPowerMw", mw(first(p.text, /Power\s*\n?\s*(\d[\d.]*\+?\s*MW\+?)/)) ?? capacityMw(p.text), "regex:power_row");210 r.set("tier", tier(p.text), "regex:tier_v1").set("certifications", certifications(p.text), "regex:certifications_v1").set("description", describe(p), "meta:description");211 return [r.done(key("elementcritical", loc[1]), p.url)];212});213214/** 365 Data Centers — /<city>-data-center/ ; address at the top of the page, MW of power, sqft. */215facilityParser("threesixtyfive_facility_v1", (p) => {216 const addr = usAddress(p.text.slice(0, 3000)) ?? usAddress(p.desc ?? "");217 if (!addr) return [];218 const r = new Rec();219 const name = (p.title ?? "").split(/\s*[-|]\s*/)[0]?.trim() || titleCase(seg(p.url).pop() ?? "");220 r.set("name", `365 Data Centers ${name.replace(/\s*data center$/i, "")}`, "html:title").set("address", addr.street, "regex:us_address_v1").set("city", addr.city, "regex:us_address_v1").set("regionName", addr.region, "regex:us_address_v1").set("postalCode", addr.postal, "regex:us_address_v1").set("countryIso2", "US", "regex:us_address_v1");221 r.set("totalPowerMw", mw(first(p.text, /(\d[\d.]*\+?\s*MW\+?) of (?:critical\s+)?power/i)) ?? capacityMw(p.text), "regex:mw_of_power").set("buildingSqm", sqm(first(p.text, /([\d,]+\s*sq\.?\s*ft)\s+(?:data center|facility)/i)), "regex:sqft_v1");222 r.set("certifications", certifications(p.text), "regex:certifications_v1").set("description", describe(p), "meta:description");223 return [r.done(key("365dc", seg(p.url).pop()), p.url)];224});225226/** Netrality — /data-center/<slug>/ ; h1 "City, State Data Center <street>", carrier-hotel positioning. */227facilityParser("netrality_facility_v1", (p) => {228 const h1 = p.h1 ?? "";229 const m = h1.match(/^(.+?),\s*([A-Za-z ]+?)\s+Data Center\s+(.+)$/);230 if (!m) return [];231 const r = new Rec();232 r.set("name", `Netrality ${m[3]!.trim()} – ${m[1]!.trim()}`, "selector:h1").set("address", m[3], "selector:h1").set("city", m[1], "selector:h1").set("regionName", m[2], "selector:h1").set("countryIso2", "US", "default:operator");233 const sf = p.text.match(/([\d,]+)(k)?\s*Sq\.?\s*Ft\.?/i);234 if (sf) { let n = Number(sf[1]!.replace(/,/g, "")); if (sf[2] && n < 1000) n *= 1000; r.set("buildingSqm", sqm(`${n} sq ft`), "regex:sqft_k_v1"); }235 if (/carrier hotel/i.test(p.text.slice(0, 3000))) r.set("facilityType", "carrier_hotel", "regex:carrier_hotel");236 r.set("certifications", certifications(p.text), "regex:certifications_v1").set("description", describe(p), "meta:description");237 return [r.done(key("netrality", seg(p.url).pop()), p.url, 0.75)];238});239240/** CloudHQ — /campus/<slug>/ ; published "lat, lng" line, total critical IT load, SF, RFS timing. */241facilityParser("cloudhq_campus_v1", (p) => {242 const h1 = p.h1 ?? "";243 if (!/campus/i.test(h1)) return [];244 const r = new Rec();245 r.set("name", `CloudHQ ${h1}`, "selector:h1").set("code", first(h1, /^([A-Z]{3})\b/), "selector:h1");246 const ll = p.text.match(/^\s*(-?\d{1,2}\.\d{3,}),\s*(-?\d{1,3}\.\d{3,})\s*$/m);247 if (ll) r.set("lat", Number(ll[1]), "text:coordinates_line").set("lng", Number(ll[2]), "text:coordinates_line").set("geoPrecision", "parcel", "text:coordinates_line");248 r.set("itCapacityMw", mw(first(p.text, /(\d[\d.,]*\s*MWs?)\s+Total Critical IT Load/i)) ?? itMw(p.text), "regex:total_critical_it").set("buildingSqm", sqm(first(p.text, /([\d,]+\s*SF)\b/)), "regex:sf_v1");249 const st = statusOf(p.text, 1500); if (st) r.set("status", st, "regex:status_v1");250 const cityGuess = first(p.text, /(?:of|in|near) ([A-Z][a-zA-Z]+(?: [A-Z][a-zA-Z]+)?)(?:’s|'s)? (?:Schiphol|airport|metro|area|CBD)/);251 const c = countryOf(p.text.slice(0, 4000), cityGuess); if (c) r.set("countryIso2", c.iso, c.method);252 r.set("city", cityGuess, "regex:near_city");253 r.set("facilityType", "hyperscale", "default:operator").set("description", describe(p), "meta:description");254 return [r.done(key("cloudhq", seg(p.url).pop()), p.url, 0.8)];255});256257/** ODATA — /en/blog/data-center/dc-<code>/ ; IT power, floor area, Google Maps link with coordinates, Tier III. */258facilityParser("odata_facility_v1", (p) => {259 const code = first(`${p.title ?? ""} ${p.h1 ?? ""} ${p.text.slice(0, 200)}`, /\bDC[\s-]?([A-Z]{2}\d{2})\b/);260 if (!code) return [];261 const r = new Rec();262 r.set("name", `ODATA ${code}`, "html:title").set("code", code, "html:title");263 const t = latinNumbers(p.text);264 r.set("itCapacityMw", mw(first(t, /IT power\s*\n?\s*(\d[\d.,]*\+?\s*MW\+?)/i)) ?? itMw(t), "regex:it_power_row").set("buildingSqm", sqm(first(t, /Floor area\s*\n?\s*([\d.,]+\s*m²)/i)), "regex:floor_area_row");265 const g = extractGeo(p.html); if (g && g.method === "link:google-maps") r.set("lat", g.lat, g.method).set("lng", g.lng, g.method).set("geoPrecision", "exact", g.method);266 const loc = p.text.match(/Located in (?:the )?(?:[A-Z][\w ]+? area in )?([A-Z][^,.\n(]{2,40}?)\s*[,.(]/);267 if (loc) r.set("city", loc[1], "regex:located_in");268 const c = countryOf(safeCountryText(p.text.slice(0, 5000)), loc?.[1]);269 const prefix: Record<string, string> = { SP: "BR", RJ: "BR", QR: "MX", ST: "CL", BG: "CO" };270 if (c) r.set("countryIso2", c.iso, c.method); else r.set("countryIso2", prefix[code.slice(0, 2)] ?? null, "lookup:odata-code-prefix");271 r.set("tier", tier(p.text), "regex:tier_v1").set("certifications", certifications(p.text), "regex:certifications_v1").set("facilityType", "wholesale", "default:operator").set("description", describe(p), "meta:description");272 return [r.done(key("odata", code), p.url)];273});274275/** Ark Data Centers (arkdna.com, formerly Involta) — /data-centers/<state>/<city>/ metro pages listing 1–2 facilities. */276facilityParser("arkdna_facility_v1", (p) => {277 const parts = seg(p.url);278 if (parts.length < 3 || parts[0] !== "data-centers") return [];279 const r = new Rec();280 const h1 = p.h1 ?? titleCase(parts[2]!);281 r.set("name", `Ark ${h1.replace(/\s*data centers?$/i, "")}`, "selector:h1").set("city", titleCase(parts[2]!), "url:segment").set("regionName", titleCase(parts[1]!), "url:segment").set("countryIso2", "US", "default:operator");282 r.set("buildingSqm", sqm(first(`${p.desc ?? ""}\n${p.text}`, /([\d,]+\+?\s*square feet)/i)), "regex:sqft_v1").set("tier", tier(p.text), "regex:tier_v1").set("certifications", certifications(p.text), "regex:certifications_v1").set("description", describe(p), "meta:description");283 return [r.done(key("arkdna", parts[1], parts[2]), p.url, 0.7)];284});285286export const AMERICAS_PARSERS = ["databank_facility_v1", "edgeconnex_facility_v1", "tierpoint_facility_v1", "evocative_facility_v1", "serverfarm_facility_v1", "sabey_facility_v1", "stream_facility_v1", "skybox_facility_v1", "hivelocity_facility_v1", "elementcritical_facility_v1", "threesixtyfive_facility_v1", "netrality_facility_v1", "cloudhq_campus_v1", "odata_facility_v1", "arkdna_facility_v1"];287void all; void countryFromCity; void countryFromText; void usAddress;288