spb/datacenterindex
Public
HTML 53.9%
TypeScript 44.5%
JavaScript 0.6%
SQL 0.5%
1import type { OperatorKind } from "@dci/core";23/**4 * Curated operator lexicon used by the news / planning parsers to recognise who is building, buying or5 * operating a data center. Canonical `name` is what we emit; `aliases` are matched with word boundaries6 * (case-sensitive for proper nouns and acronyms so that "Switch" or "Meta" cannot match ordinary words).7 * An alias may be a RegExp when a plain word is too ambiguous. Order = priority when several match at the8 * same position (hyperscalers first).9 */10export interface OperatorEntry {11 name: string;12 aliases: Array<string | RegExp>;13 kind: OperatorKind;14 hq?: string; // ISO-2 of headquarters, informative only15}1617/**18 * Words that may follow an ambiguous company name in a headline or sentence ("Meta scraps…", "Aligned delays…",19 * "Switch to build…"). Used as a lookahead so plain English words are never mistaken for operators.20 */21const VERBS = "is|has|had|will|would|could|may|might|plans?|planned|planning|announced?|announces|announcing|to|data|campus|said|says|and|the|for|in|–|-|—|AI|Platforms|scraps|scrapped|cancels|cancelled|canceled|pulls|drops|dropped|buys|bought|acquires|acquired|breaks|broke|opens|opened|unveils|unveiled|confirms|confirmed|signs|signed|secures|secured|wins|won|expands|expanded|invests|invested|files|filed|seeks|sought|eyes|eyed|eyeing|mulls|weighs|reportedly|also|now|just|wants|looks|set|slated|expected|delays|delayed|postpones|pauses|paused|completes|completed|begins|began|starts|started|launches|launched|picks|picked|selects|selected|commits|committed|pledges|pledged|reveals|revealed|considers|considering|targets|targeting|inks|inked|lands|landed|leases|leased|proposes|proposed|submits|submitted|receives|received|gets|got|halts|halted|abandons|abandoned|revives|revived|doubles|adds|added|brings|brought|powers|powered|taps|tapped|partners|partnered|teams|teamed|moves|moved|readies|prepares|preparing|weighing|exploring|explores|explored|CEO|CFO|CTO|president|spokesperson|executive|shares|stock";22const after = (name: string) => new RegExp(`${name}(?:'s)?(?= (?:${VERBS})\\b)`);2324export const OPERATORS: OperatorEntry[] = [25 // ─── Hyperscalers / cloud ───────────────────────────────────────────────────────────────────────26 { name: "Amazon Web Services", aliases: ["Amazon Web Services", "AWS", after("Amazon(?:\\.com)?"), "Amazon Web Services'", "Amazon Data Services"], kind: "hyperscaler", hq: "US" },27 { name: "Microsoft", aliases: ["Microsoft", "Microsoft Azure", "Azure"], kind: "hyperscaler", hq: "US" },28 { name: "Google", aliases: ["Google", "Google Cloud", "Alphabet Inc", "Alphabet's"], kind: "hyperscaler", hq: "US" },29 { name: "Meta", aliases: ["Meta Platforms", "Facebook", after("Meta")], kind: "hyperscaler", hq: "US" },30 { name: "Apple", aliases: ["Apple Inc", after("Apple")], kind: "hyperscaler", hq: "US" },31 { name: "Oracle", aliases: ["Oracle", "Oracle Cloud", "Oracle Cloud Infrastructure", "OCI"], kind: "hyperscaler", hq: "US" },32 { name: "IBM", aliases: ["IBM", "IBM Cloud"], kind: "cloud", hq: "US" },33 { name: "Alibaba Cloud", aliases: ["Alibaba Cloud", "Alibaba", "Aliyun"], kind: "hyperscaler", hq: "CN" },34 { name: "Tencent Cloud", aliases: ["Tencent Cloud", "Tencent"], kind: "hyperscaler", hq: "CN" },35 { name: "Huawei Cloud", aliases: ["Huawei Cloud", "Huawei"], kind: "cloud", hq: "CN" },36 { name: "Baidu", aliases: ["Baidu", "Baidu AI Cloud"], kind: "cloud", hq: "CN" },37 { name: "ByteDance", aliases: ["ByteDance", "TikTok"], kind: "enterprise", hq: "CN" },38 { name: "OpenAI", aliases: ["OpenAI"], kind: "enterprise", hq: "US" },39 { name: "xAI", aliases: ["xAI", "X.AI"], kind: "enterprise", hq: "US" },40 { name: "Anthropic", aliases: ["Anthropic"], kind: "enterprise", hq: "US" },41 { name: "CoreWeave", aliases: ["CoreWeave"], kind: "cloud", hq: "US" },42 { name: "Lambda", aliases: ["Lambda Labs", after("Lambda")], kind: "cloud", hq: "US" },43 { name: "Crusoe", aliases: ["Crusoe", "Crusoe Energy"], kind: "developer", hq: "US" },44 { name: "Nebius", aliases: ["Nebius"], kind: "cloud", hq: "NL" },45 { name: "Nscale", aliases: ["Nscale"], kind: "cloud", hq: "GB" },46 { name: "Fluidstack", aliases: ["Fluidstack"], kind: "cloud", hq: "GB" },47 { name: "Voltage Park", aliases: ["Voltage Park"], kind: "cloud", hq: "US" },48 { name: "Vultr", aliases: ["Vultr"], kind: "cloud", hq: "US" },49 { name: "DigitalOcean", aliases: ["DigitalOcean"], kind: "cloud", hq: "US" },50 { name: "OVHcloud", aliases: ["OVHcloud", "OVH"], kind: "cloud", hq: "FR" },51 { name: "Scaleway", aliases: ["Scaleway"], kind: "cloud", hq: "FR" },52 { name: "Hetzner", aliases: ["Hetzner"], kind: "cloud", hq: "DE" },53 { name: "IONOS", aliases: ["IONOS"], kind: "cloud", hq: "DE" },54 { name: "Akamai", aliases: ["Akamai", "Linode"], kind: "cloud", hq: "US" },55 { name: "Cloudflare", aliases: ["Cloudflare"], kind: "cloud", hq: "US" },56 { name: "Salesforce", aliases: ["Salesforce"], kind: "enterprise", hq: "US" },57 { name: "SAP", aliases: ["SAP SE", after("SAP")], kind: "enterprise", hq: "DE" },58 { name: "Nvidia", aliases: ["Nvidia", "NVIDIA"], kind: "enterprise", hq: "US" },59 { name: "Deutsche Telekom", aliases: ["Deutsche Telekom", "T-Systems"], kind: "carrier", hq: "DE" },60 { name: "Tata Communications", aliases: ["Tata Communications"], kind: "carrier", hq: "IN" },61 { name: "Singtel", aliases: ["Singtel", "Nxera"], kind: "carrier", hq: "SG" },62 { name: "KDDI", aliases: ["KDDI", "Telehouse"], kind: "carrier", hq: "JP" },63 { name: "NTT", aliases: ["NTT Global Data Centers", "NTT Data", "NTT DATA", "NTT Ltd", "NTT Communications", "NTT"], kind: "colocation", hq: "JP" },6465 // ─── Colocation / wholesale — Americas ──────────────────────────────────────────────────────────66 { name: "Equinix", aliases: ["Equinix"], kind: "colocation", hq: "US" },67 { name: "Digital Realty", aliases: ["Digital Realty", "Digital Realty Trust", "Interxion", "Teraco", "Ascenty"], kind: "colocation", hq: "US" },68 { name: "CyrusOne", aliases: ["CyrusOne"], kind: "colocation", hq: "US" },69 { name: "QTS", aliases: ["QTS Data Centers", "QTS Realty", "QTS"], kind: "colocation", hq: "US" },70 { name: "Vantage Data Centers", aliases: ["Vantage Data Centers", "Vantage Data Centres", "Vantage's", after("Vantage")], kind: "colocation", hq: "US" },71 { name: "Aligned Data Centers", aliases: ["Aligned Data Centers", "Aligned's", after("Aligned")], kind: "colocation", hq: "US" },72 { name: "STACK Infrastructure", aliases: ["STACK Infrastructure", "Stack Infrastructure", "STACK's", after("STACK")], kind: "colocation", hq: "US" },73 { name: "Compass Datacenters", aliases: ["Compass Datacenters", "Compass Data Centers", "Compass's"], kind: "developer", hq: "US" },74 { name: "EdgeCore", aliases: ["EdgeCore", "EdgeCore Digital Infrastructure"], kind: "developer", hq: "US" },75 { name: "EdgeConneX", aliases: ["EdgeConneX"], kind: "colocation", hq: "US" },76 { name: "Prime Data Centers", aliases: ["Prime Data Centers"], kind: "developer", hq: "US" },77 { name: "Sabey Data Centers", aliases: ["Sabey Data Centers", "Sabey"], kind: "colocation", hq: "US" },78 { name: "Iron Mountain", aliases: ["Iron Mountain", "Iron Mountain Data Centers"], kind: "colocation", hq: "US" },79 { name: "Flexential", aliases: ["Flexential"], kind: "colocation", hq: "US" },80 { name: "DataBank", aliases: ["DataBank"], kind: "colocation", hq: "US" },81 { name: "Cologix", aliases: ["Cologix"], kind: "colocation", hq: "US" },82 { name: "Evoque", aliases: ["Evoque", "Cyxtera"], kind: "colocation", hq: "US" },83 { name: "TierPoint", aliases: ["TierPoint"], kind: "colocation", hq: "US" },84 { name: "Switch", aliases: ["Switch Inc", "Switch, Inc", "Switch's", after("Switch")], kind: "colocation", hq: "US" },85 { name: "Novva Data Centers", aliases: ["Novva Data Centers", "Novva"], kind: "colocation", hq: "US" },86 { name: "Element Critical", aliases: ["Element Critical"], kind: "colocation", hq: "US" },87 { name: "365 Data Centers", aliases: ["365 Data Centers"], kind: "colocation", hq: "US" },88 { name: "H5 Data Centers", aliases: ["H5 Data Centers"], kind: "colocation", hq: "US" },89 { name: "Stream Data Centers", aliases: ["Stream Data Centers"], kind: "developer", hq: "US" },90 { name: "Skybox Datacenters", aliases: ["Skybox Datacenters", "Skybox Data Centers"], kind: "developer", hq: "US" },91 { name: "T5 Data Centers", aliases: ["T5 Data Centers"], kind: "developer", hq: "US" },92 { name: "Corscale", aliases: ["Corscale", "Corscale Data Centers"], kind: "developer", hq: "US" },93 { name: "CloudHQ", aliases: ["CloudHQ", "Cloud HQ"], kind: "developer", hq: "US" },94 { name: "Yondr", aliases: ["Yondr", "Yondr Group"], kind: "developer", hq: "GB" },95 { name: "Colovore", aliases: ["Colovore"], kind: "colocation", hq: "US" },96 { name: "Ada Infrastructure", aliases: ["Ada Infrastructure"], kind: "developer", hq: "GB" },97 { name: "PowerHouse Data Centers", aliases: ["PowerHouse Data Centers", "PowerHouse"], kind: "developer", hq: "US" },98 { name: "Tract", aliases: ["Tract Capital", after("Tract")], kind: "developer", hq: "US" },99 { name: "Rowan Digital Infrastructure", aliases: ["Rowan Digital Infrastructure", "Rowan Digital"], kind: "developer", hq: "US" },100 { name: "Lancium", aliases: ["Lancium"], kind: "developer", hq: "US" },101 { name: "Soluna", aliases: ["Soluna", "Soluna Holdings"], kind: "developer", hq: "US" },102 { name: "Chirisa", aliases: ["Chirisa", "Chirisa Technology Parks"], kind: "developer", hq: "US" },103 { name: "Applied Digital", aliases: ["Applied Digital"], kind: "developer", hq: "US" },104 { name: "Core Scientific", aliases: ["Core Scientific"], kind: "developer", hq: "US" },105 { name: "IREN", aliases: ["IREN", "Iris Energy"], kind: "developer", hq: "AU" },106 { name: "TeraWulf", aliases: ["TeraWulf"], kind: "developer", hq: "US" },107 { name: "Cipher Mining", aliases: ["Cipher Mining"], kind: "developer", hq: "US" },108 { name: "Hut 8", aliases: ["Hut 8"], kind: "developer", hq: "US" },109 { name: "Riot Platforms", aliases: ["Riot Platforms", "Riot Blockchain"], kind: "developer", hq: "US" },110 { name: "MARA", aliases: ["MARA Holdings", "Marathon Digital", "MARA"], kind: "developer", hq: "US" },111 { name: "Bitdeer", aliases: ["Bitdeer"], kind: "developer", hq: "SG" },112 { name: "Bitfarms", aliases: ["Bitfarms"], kind: "developer", hq: "CA" },113 { name: "Galaxy Digital", aliases: ["Galaxy Digital"], kind: "developer", hq: "US" },114 { name: "Northern Data", aliases: ["Northern Data"], kind: "developer", hq: "DE" },115 { name: "Cielo Digital Infrastructure", aliases: ["Cielo Digital Infrastructure"], kind: "developer", hq: "US" },116 { name: "Quantum Loophole", aliases: ["Quantum Loophole"], kind: "developer", hq: "US" },117 { name: "Scala Data Centers", aliases: ["Scala Data Centers", "Scala"], kind: "colocation", hq: "BR" },118 { name: "ODATA", aliases: ["ODATA", "Odata"], kind: "colocation", hq: "BR" },119 { name: "Elea Data Centers", aliases: ["Elea Data Centers", "Elea Digital"], kind: "colocation", hq: "BR" },120 { name: "Cirion", aliases: ["Cirion", "Cirion Technologies"], kind: "colocation", hq: "US" },121 { name: "KIO Networks", aliases: ["KIO Networks", "KIO"], kind: "colocation", hq: "MX" },122 { name: "Urbacon", aliases: ["Urbacon Data Centre Solutions", "Urbacon"], kind: "developer", hq: "CA" },123 { name: "eStruxture", aliases: ["eStruxture"], kind: "colocation", hq: "CA" },124125 // ─── Europe ─────────────────────────────────────────────────────────────────────────────────────126 { name: "Global Switch", aliases: ["Global Switch"], kind: "colocation", hq: "GB" },127 { name: "Colt DCS", aliases: ["Colt DCS", "Colt Data Centre Services", "Colt Technology Services"], kind: "colocation", hq: "GB" },128 { name: "VIRTUS Data Centres", aliases: ["VIRTUS Data Centres", "Virtus Data Centres", "VIRTUS"], kind: "colocation", hq: "GB" },129 { name: "Ark Data Centres", aliases: ["Ark Data Centres"], kind: "colocation", hq: "GB" },130 { name: "Kao Data", aliases: ["Kao Data"], kind: "colocation", hq: "GB" },131 { name: "Pure DC", aliases: ["Pure DC", "Pure Data Centres"], kind: "developer", hq: "GB" },132 { name: "Echelon Data Centres", aliases: ["Echelon Data Centres", "Echelon DC"], kind: "developer", hq: "IE" },133 { name: "Data4", aliases: ["Data4", "DATA4"], kind: "colocation", hq: "FR" },134 { name: "atNorth", aliases: ["atNorth"], kind: "colocation", hq: "IS" },135 { name: "Green Mountain", aliases: ["Green Mountain"], kind: "colocation", hq: "NO" },136 { name: "Bulk Infrastructure", aliases: ["Bulk Infrastructure", "Bulk Data Centers"], kind: "colocation", hq: "NO" },137 { name: "Verne", aliases: ["Verne Global", "Verne"], kind: "colocation", hq: "GB" },138 { name: "EcoDataCenter", aliases: ["EcoDataCenter"], kind: "colocation", hq: "SE" },139 { name: "Penta Infra", aliases: ["Penta Infra"], kind: "colocation", hq: "NL" },140 { name: "maincubes", aliases: ["maincubes", "Maincubes"], kind: "colocation", hq: "DE" },141 { name: "NorthC", aliases: ["NorthC"], kind: "colocation", hq: "NL" },142 { name: "Serverfarm", aliases: ["Serverfarm"], kind: "colocation", hq: "US" },143 { name: "Portus Data Centers", aliases: ["Portus Data Centers", "Portus"], kind: "colocation", hq: "DE" },144 { name: "AQ Compute", aliases: ["AQ Compute"], kind: "developer", hq: "NO" },145 { name: "Ardent Data Centers", aliases: ["Ardent Data Centers"], kind: "developer", hq: "GB" },146 { name: "LCL Data Centers", aliases: ["LCL Data Centers", "LCL"], kind: "colocation", hq: "BE" },147 { name: "Kevlinx", aliases: ["Kevlinx"], kind: "developer", hq: "BE" },148 { name: "Etix Everywhere", aliases: ["Etix Everywhere"], kind: "colocation", hq: "LU" },149 { name: "Green Datacenter", aliases: ["Green Datacenter", "Green.ch"], kind: "colocation", hq: "CH" },150 { name: "Nabiax", aliases: ["Nabiax"], kind: "colocation", hq: "ES" },151 { name: "Start Campus", aliases: ["Start Campus"], kind: "developer", hq: "PT" },152 { name: "Merlin Properties", aliases: ["Merlin Properties", "MERLIN Properties"], kind: "developer", hq: "ES" },153 { name: "Telefónica Tech", aliases: ["Telefónica Tech", "Telefonica Tech"], kind: "carrier", hq: "ES" },154 { name: "Aruba", aliases: ["Aruba S.p.A", "Aruba Enterprise", after("Aruba")], kind: "colocation", hq: "IT" },155 { name: "Stack EMEA", aliases: ["Digiplex", "DigiPlex"], kind: "colocation", hq: "NO" },156 { name: "Ficolo", aliases: ["Ficolo"], kind: "colocation", hq: "FI" },157 { name: "Telia", aliases: ["Telia", "Telia Helsinki Data Center"], kind: "carrier", hq: "SE" },158 { name: "Elisa", aliases: ["Elisa"], kind: "carrier", hq: "FI" },159160 // ─── Asia-Pacific ───────────────────────────────────────────────────────────────────────────────161 { name: "AirTrunk", aliases: ["AirTrunk"], kind: "colocation", hq: "AU" },162 { name: "NEXTDC", aliases: ["NEXTDC", "NextDC"], kind: "colocation", hq: "AU" },163 { name: "CDC Data Centres", aliases: ["CDC Data Centres", "Canberra Data Centres"], kind: "colocation", hq: "AU" },164 { name: "Macquarie Data Centres", aliases: ["Macquarie Data Centres", "Macquarie Technology Group"], kind: "colocation", hq: "AU" },165 { name: "Firmus", aliases: ["Firmus", "Firmus Technologies"], kind: "developer", hq: "AU" },166 { name: "STT GDC", aliases: ["ST Telemedia Global Data Centres", "STT GDC"], kind: "colocation", hq: "SG" },167 { name: "Keppel", aliases: ["Keppel Data Centres", "Keppel DC REIT", "Keppel"], kind: "colocation", hq: "SG" },168 { name: "Princeton Digital Group", aliases: ["Princeton Digital Group", "PDG"], kind: "colocation", hq: "SG" },169 { name: "Bridge Data Centres", aliases: ["Bridge Data Centres", "Bridge DC"], kind: "colocation", hq: "SG" },170 { name: "DayOne", aliases: ["DayOne Data Centers", "DayOne"], kind: "colocation", hq: "SG" },171 { name: "GDS", aliases: ["GDS Holdings", "GDS"], kind: "colocation", hq: "CN" },172 { name: "Chindata", aliases: ["Chindata", "Bain-backed Chindata"], kind: "colocation", hq: "CN" },173 { name: "VNET", aliases: ["VNET", "21Vianet"], kind: "colocation", hq: "CN" },174 { name: "SpaceDC", aliases: ["SpaceDC"], kind: "colocation", hq: "SG" },175 { name: "Empyrion Digital", aliases: ["Empyrion Digital", "Empyrion DC"], kind: "colocation", hq: "SG" },176 { name: "BDx", aliases: ["BDx", "Big Data Exchange"], kind: "colocation", hq: "SG" },177 { name: "Evolution Data Centres", aliases: ["Evolution Data Centres"], kind: "developer", hq: "SG" },178 { name: "YTL Data Center", aliases: ["YTL Data Center", "YTL Power"], kind: "developer", hq: "MY" },179 { name: "Yotta", aliases: ["Yotta Data Services", "Yotta Infrastructure", "Yotta"], kind: "colocation", hq: "IN" },180 { name: "AdaniConneX", aliases: ["AdaniConneX", "Adani Enterprises", "Adani Group"], kind: "developer", hq: "IN" },181 { name: "Sify", aliases: ["Sify Technologies", "Sify Infinit Spaces", "Sify"], kind: "colocation", hq: "IN" },182 { name: "Nxtra", aliases: ["Nxtra", "Nxtra by Airtel", "Airtel Nxtra"], kind: "colocation", hq: "IN" },183 { name: "CtrlS", aliases: ["CtrlS", "CtrlS Datacenters"], kind: "colocation", hq: "IN" },184 { name: "Reliance Jio", aliases: ["Reliance Jio", "Reliance Industries", "Jio Platforms"], kind: "enterprise", hq: "IN" },185 { name: "SoftBank", aliases: ["SoftBank", "SB Energy"], kind: "investor", hq: "JP" },186 { name: "SK Group", aliases: ["SK Group", "SK Telecom", "SK Broadband"], kind: "carrier", hq: "KR" },187 { name: "Naver", aliases: ["Naver", "NAVER Cloud"], kind: "cloud", hq: "KR" },188 { name: "Kakao", aliases: ["Kakao", "Kakao Enterprise"], kind: "cloud", hq: "KR" },189190 // ─── Middle East & Africa ───────────────────────────────────────────────────────────────────────191 { name: "Khazna Data Centers", aliases: ["Khazna Data Centers", "Khazna"], kind: "colocation", hq: "AE" },192 { name: "Gulf Data Hub", aliases: ["Gulf Data Hub"], kind: "colocation", hq: "AE" },193 { name: "center3", aliases: ["center3", "Center3"], kind: "colocation", hq: "SA" },194 { name: "DataVolt", aliases: ["DataVolt"], kind: "developer", hq: "SA" },195 { name: "Africa Data Centres", aliases: ["Africa Data Centres", "Cassava Technologies"], kind: "colocation", hq: "ZA" },196 { name: "Rack Centre", aliases: ["Rack Centre"], kind: "colocation", hq: "NG" },197 { name: "MainOne", aliases: ["MainOne", "MDXi"], kind: "colocation", hq: "NG" },198 { name: "Open Access Data Centres", aliases: ["Open Access Data Centres", "OADC"], kind: "colocation", hq: "ZA" },199 { name: "Raxio", aliases: ["Raxio", "Raxio Group"], kind: "colocation", hq: "UG" },200 { name: "G42", aliases: ["G42", "Core42"], kind: "enterprise", hq: "AE" },201202 // ─── Investors / owners frequently named as the developer ──────────────────────────────────────203 { name: "Blackstone", aliases: ["Blackstone"], kind: "investor", hq: "US" },204 { name: "Brookfield", aliases: ["Brookfield", "Brookfield Infrastructure", "Brookfield Asset Management"], kind: "investor", hq: "CA" },205 { name: "KKR", aliases: ["KKR"], kind: "investor", hq: "US" },206 { name: "Blue Owl", aliases: ["Blue Owl", "Blue Owl Capital"], kind: "investor", hq: "US" },207 { name: "DigitalBridge", aliases: ["DigitalBridge"], kind: "investor", hq: "US" },208 { name: "Stonepeak", aliases: ["Stonepeak"], kind: "investor", hq: "US" },209 { name: "GIC", aliases: ["GIC"], kind: "investor", hq: "SG" },210 { name: "Macquarie Asset Management", aliases: ["Macquarie Asset Management"], kind: "investor", hq: "AU" },211 { name: "Related Digital", aliases: ["Related Digital", "Related Companies"], kind: "developer", hq: "US" },212 { name: "Hines", aliases: [after("Hines")], kind: "developer", hq: "US" },213];214215function escapeRe(s: string): string { return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }216217const compiled: Array<{ entry: OperatorEntry; re: RegExp }> = OPERATORS.map((entry) => {218 const parts = entry.aliases.map((a) => (a instanceof RegExp ? `(?:${a.source})` : escapeRe(a)));219 // word boundaries that also work for names starting/ending with digits or symbols220 return { entry, re: new RegExp(`(?<![A-Za-z0-9])(?:${parts.join("|")})(?![A-Za-z0-9])`, "g") };221});222223export interface OperatorHit { name: string; kind: OperatorKind; index: number; matched: string }224225/** All operators mentioned in `text`, ordered by first appearance (then lexicon priority). */226export function detectOperators(text: string): OperatorHit[] {227 if (!text) return [];228 const hits = new Map<string, OperatorHit>();229 for (const { entry, re } of compiled) {230 re.lastIndex = 0;231 const m = re.exec(text);232 if (!m) continue;233 const prev = hits.get(entry.name);234 if (!prev || m.index < prev.index) hits.set(entry.name, { name: entry.name, kind: entry.kind, index: m.index, matched: m[0] });235 }236 return [...hits.values()].sort((a, b) => a.index - b.index);237}238239/** Primary operator: first one named in the title, else first one in the body. */240export function primaryOperator(title: string | null, text: string): OperatorHit | null {241 const inTitle = detectOperators(title ?? "");242 if (inTitle.length) return inTitle[0]!;243 const inText = detectOperators(text.slice(0, 3000));244 return inText[0] ?? null;245}246247export function operatorNames(): string[] { return OPERATORS.map((o) => o.name); }248