import type { Parser, ExtractedRecord, RawDocument } from "@dci/connectors"; import { countryFromText, cleanText } from "@dci/core"; import { loadHtml, tableRows, parenthetical, resolvePlace, regionRecord, type Place } from "./shared.js"; /** * Tencent Cloud — https://www.tencentcloud.com/document/product/213/6091 (docs table, server-rendered for the * SDK's direct fetcher; curl with a bot UA gets an anti-bot JS page, so `looksBlockedOrEmpty` escalation covers it): * Region | Region ID | Number of AZs | AZ — the first row of each region carries name/id/count, following rows only AZs. * Names look like "North China (Beijing)", "Asia Pacific and the Middle East (Singapore) Coverage: Southeast Asia", * "Beijing Finance Available only to finance institutions…", "Shanghai Self-driving Cloud". */ export const tencentRegionsParser: Parser = { name: "cloud_tencent_regions_v1", version: "1.0.0", pageTypes: ["cloud_region"], parse(doc: RawDocument, ctx): ExtractedRecord[] { const $ = loadHtml(doc); const out: ExtractedRecord[] = []; const seen = new Set(); for (const row of tableRows($, $("table"))) { if (row.length < 3) continue; const code = row[1]!.trim(); if (!/^(ap|na|eu|sa|me|af)-[a-z]+(-[a-z]+)*$/.test(code) || seen.has(code)) continue; seen.add(code); const raw = row[0]!; const restricted = /available only|upon application/i.test(raw); const name = cleanText(raw.replace(/\s*Coverage:.*$/i, "").replace(/\s*Available only.*$/i, "")) ?? raw; const azs = Number.parseInt(row[2]!.replace(/[^\d]/g, ""), 10); // "Hong Kong (China), Macao (China), and Taiwan (China)" → drop the "(China)" qualifiers before reading the city const bare = name.replace(/\s*\(China\)/g, ""); const label = parenthetical(bare)?.trim() ?? bare.replace(/\s+(Finance|Self-driving Cloud).*$/i, "").split(/,|\band\b/)[0]!.trim(); const chinaRegion = /^(North|East|South|Southwest|Northwest|Central) China\b/i.test(name) || /Finance|Self-driving/i.test(name) || /^cn|^ap-(beijing|shanghai|nanjing|guangzhou|chengdu|chongqing|zhongwei|shenzhen|hongkong)/.test(code); const isoHint = /hong kong/i.test(label) ? "HK" : chinaRegion ? "CN" : null; let place: Place = resolvePlace(label, isoHint); if (!place.countryIso2) place.countryIso2 = isoHint ?? countryFromText(label) ?? countryFromText(name); const externalIds: Record = { tencent_region_id: code, published_location: label }; const coverage = raw.match(/Coverage:\s*(.+)$/i)?.[1]; if (coverage) externalIds.coverage = cleanText(coverage) ?? coverage; if (/Finance/i.test(name)) externalIds.realm = "finance"; if (/Self-driving/i.test(name)) externalIds.realm = "self_driving_cloud"; out.push( regionRecord({ connectorId: ctx.connectorId, keyPrefix: "tencent:region", providerName: "Tencent Cloud", code, name, place, availabilityZones: Number.isFinite(azs) && azs > 0 ? azs : null, status: restricted || /Self-driving/i.test(name) ? "restricted" : "operational", externalIds, url: doc.finalUrl, methods: { code: "html:table.RegionID", name: "html:table.Region", availabilityZones: "html:table.NumberOfAZs", city: "curated-city-table" }, }), ); } ctx.log("info", `tencent: ${out.length} regions`); return out; }, };