import type { Parser, ExtractedRecord, RawDocument } from "@dci/connectors"; import { countryFromText, slugify } from "@dci/core"; import { loadHtml, txt, parenthetical, resolvePlace, regionRecord, type Place } from "./shared.js"; import { normKey } from "./city-coords.js"; /** * Oracle Cloud Infrastructure — https://www.oracle.com/cloud/public-cloud-regions/ * Server-rendered HTML (needs a browser identity: the bot UA gets 403). Sections (h2): Commercial, * Sovereign (EU Sovereign Cloud), Government, Multicloud (Oracle Database@AWS/Azure/Google — NOT Oracle * regions, skipped). Each table: US East (Ashburn) … Live|Coming soon|Planned. * Quirks seen live: a few rows lack geo-data, one code is duplicated ("Spain Central 2"), and two rows have * swapped codes (ap-batam-1 / ap-hyderabad-1). The parser trusts the display name and falls back to a slug * code when the geo-data code contradicts the city in the name. */ const PREFIX_ISO: Array<[RegExp, string]> = [ [/^(US|United States)\b/i, "US"], [/^Canada\b/i, "CA"], [/^Mexico\b/i, "MX"], [/^Brazil\b/i, "BR"], [/^Chile\b/i, "CL"], [/^Colombia\b/i, "CO"], [/^France\b/i, "FR"], [/^Germany\b/i, "DE"], [/^Italy\b/i, "IT"], [/^Netherlands\b/i, "NL"], [/^Serbia\b/i, "RS"], [/^Spain\b/i, "ES"], [/^Sweden\b/i, "SE"], [/^Switzerland\b/i, "CH"], [/^UK\b/i, "GB"], [/^Israel\b/i, "IL"], [/^Kenya\b/i, "KE"], [/^Morocco\b/i, "MA"], [/^Saudi Arabia\b/i, "SA"], [/^South Africa\b/i, "ZA"], [/^UAE\b/i, "AE"], [/^Australia/i, "AU"], [/^India\b/i, "IN"], [/^Indonesia\b/i, "ID"], [/^Japan\b/i, "JP"], [/^Malaysia\b/i, "MY"], [/^Singapore\b/i, "SG"], [/^South Korea\b/i, "KR"], ]; /** OCI codes legitimately named after a different town than the one displayed (uk-cardiff-1 is "UK West (Newport)"). */ const CODE_CITY_ALIASES: Record = { "uk-cardiff-1": ["newp"], "sa-vinhedo-1": ["vinh"], "ap-chuncheon-1": ["chun"] }; function statusOf(s: string): "operational" | "announced" { return /^live$/i.test(s.trim()) ? "operational" : "announced"; } export const oracleRegionsParser: Parser = { name: "cloud_oracle_regions_v1", version: "1.0.0", pageTypes: ["cloud_region"], parse(doc: RawDocument, ctx): ExtractedRecord[] { const $ = loadHtml(doc); const out: ExtractedRecord[] = []; const seenCodes = new Set(); // Walk section headers (h2 inside the accordion list) → tables that follow within the same
  • $("li").each((_, li) => { const section = txt($(li).find("h2").first()); if (!/^(Commercial|Sovereign|Government)$/i.test(section)) return; const realm = section.toLowerCase(); $(li).find("table").each((__, table) => { const area = txt($(table).find("thead th").first()); $(table).find("tbody tr").each((___, tr) => { const tds = $(tr).find("td"); if (tds.length < 2) return; const first = tds.first(); const name = txt(first); if (!name || /^Oracle (AI )?Database@/i.test(name)) return; const statusCell = tds.toArray().map((c) => txt($(c))).find((t) => /^(Live|Coming soon|Planned)$/i.test(t)) ?? ""; const geoCode = (first.attr("geo-data") ?? "").trim(); const label = parenthetical(name); const iso = PREFIX_ISO.find(([re]) => re.test(name))?.[1] ?? (realm === "sovereign" ? null : countryFromText(name)); let place: Place = resolvePlace(label, iso); if (!place.countryIso2) place.countryIso2 = iso ?? (label ? countryFromText(label) : null) ?? countryFromText(name); // Code: trust geo-data unless missing, duplicated, or contradicting the named city (site typo) let code = geoCode; const cityToken = label ? normKey(label).replace(/\s+/g, "").slice(0, 4) : null; const codeBody = code.replace(/-/g, ""); const contradicts = Boolean(code && cityToken && !codeBody.includes(cityToken) && !(CODE_CITY_ALIASES[code] ?? []).includes(cityToken)); if (!code || seenCodes.has(code) || contradicts) code = slugify(name); seenCodes.add(code); const externalIds: Record = { oci_realm: realm, oci_area: area }; if (geoCode) externalIds.oci_geo_data = geoCode; if (label) externalIds.published_location = label; const status = statusOf(statusCell); out.push( regionRecord({ connectorId: ctx.connectorId, keyPrefix: "oracle:region", providerName: "Oracle Cloud", code, name, place, availabilityZones: null, status: realm === "government" && status === "operational" ? "restricted" : status, isSovereign: realm === "sovereign", externalIds, url: doc.finalUrl, methods: { code: geoCode && code === geoCode ? "html:td@geo-data" : "slug:name", name: "html:td", status: "html:td.Status", city: "curated-city-table" }, certainty: code === geoCode ? 0.95 : 0.85, }), ); }); }); }); ctx.log("info", `oracle: ${out.length} regions (${out.filter((r) => r.data.status === "announced").length} announced, ${out.filter((r) => r.data.isSovereign).length} sovereign)`); return out; }, };