import type { Parser, ExtractedRecord, RawDocument } from "@dci/connectors"; import { countryFromText, cleanText } from "@dci/core"; import { loadHtml, tableRows, txt, resolvePlace, regionRecord, type Place } from "./shared.js"; /** * IBM Cloud — https://cloud.ibm.com/docs/overview?topic=overview-locations (server-rendered docs): * "Multizone regions" / "Single-campus MZRs" tables: Region ("Dallas (us-south)") | Zone ("us-south-1 us-south-2 us-south-3"). * Later tables (fault domains, zone mapping, classic data centers) are ignored for regions. */ export const ibmRegionsParser: Parser = { name: "cloud_ibm_regions_v1", version: "1.0.0", pageTypes: ["cloud_region"], parse(doc: RawDocument, ctx): ExtractedRecord[] { const $ = loadHtml(doc); const out: ExtractedRecord[] = []; const seen = new Set(); $("table").each((_, table) => { const rows = tableRows($, $(table)); const header = rows[0] ?? []; if (header.length !== 2 || !/^Region$/i.test(header[0] ?? "") || !/^Zone/i.test(header[1] ?? "")) return; // find the nearest preceding h2/h3 for the MZR type const heading = txt($(table).prevAll("h2,h3").first()) || txt($(table).parent().prevAll("h2,h3").first()); const kind = /single-campus/i.test(heading) ? "single_campus_mzr" : "mzr"; for (const row of rows.slice(1)) { const m = row[0]!.match(/^(.*?)\s*\(\s*([a-z]{2}-[a-z]{2,4})\s*\)/); if (!m) continue; const code = m[2]!; if (seen.has(code)) continue; seen.add(code); const rawCity = cleanText(m[1]!) ?? m[1]!; const partner = rawCity.match(/-\s*(.+)$/)?.[1]?.trim() ?? null; // "Chennai - Airtel" const cityLabel = rawCity.replace(/\s*-\s*.+$/, "").trim(); const zones = (row[1] ?? "").split(/\s+/).filter((z) => /^[a-z]{2}-[a-z]{2,4}-\d$/.test(z)); let place: Place = resolvePlace(cityLabel); if (!place.countryIso2) place.countryIso2 = countryFromText(cityLabel); if (!place.city) place.city = cityLabel; const externalIds: Record = { ibm_region: code, kind, ibm_zones: zones.join(","), published_location: rawCity }; if (partner) externalIds.partner = partner; out.push( regionRecord({ connectorId: ctx.connectorId, keyPrefix: "ibm:region", providerName: "IBM Cloud", code, name: `${cityLabel} (${code})`, place, availabilityZones: zones.length || null, status: "operational", externalIds, url: doc.finalUrl, methods: { code: "html:table.Region", availabilityZones: "count:zones", city: "curated-city-table" }, }), ); } }); ctx.log("info", `ibm: ${out.length} regions`); return out; }, };