SPB Git forge
38commits 1branches 0releases
338.7 MBsize
maindefault branch
6 h agolast push
HTML 53.9% TypeScript 44.5% JavaScript 0.6% SQL 0.5%
3.4 KB · 61 lines typescript
Raw Blame History
1import type { Parser, ExtractedRecord, RawDocument } from "@dci/connectors";2import { countryFromText, cleanText } from "@dci/core";3import { loadHtml, tableRows, parenthetical, resolvePlace, regionRecord, type Place } from "./shared.js";45/**6 * Tencent Cloud — https://www.tencentcloud.com/document/product/213/6091 (docs table, server-rendered for the7 * SDK's direct fetcher; curl with a bot UA gets an anti-bot JS page, so `looksBlockedOrEmpty` escalation covers it):8 *   Region | Region ID | Number of AZs | AZ  — the first row of each region carries name/id/count, following rows only AZs.9 * Names look like "North China (Beijing)", "Asia Pacific and the Middle East (Singapore) Coverage: Southeast Asia",10 * "Beijing Finance Available only to finance institutions…", "Shanghai Self-driving Cloud".11 */12export const tencentRegionsParser: Parser = {13  name: "cloud_tencent_regions_v1",14  version: "1.0.0",15  pageTypes: ["cloud_region"],16  parse(doc: RawDocument, ctx): ExtractedRecord[] {17    const $ = loadHtml(doc);18    const out: ExtractedRecord[] = [];19    const seen = new Set<string>();20    for (const row of tableRows($, $("table"))) {21      if (row.length < 3) continue;22      const code = row[1]!.trim();23      if (!/^(ap|na|eu|sa|me|af)-[a-z]+(-[a-z]+)*$/.test(code) || seen.has(code)) continue;24      seen.add(code);25      const raw = row[0]!;26      const restricted = /available only|upon application/i.test(raw);27      const name = cleanText(raw.replace(/\s*Coverage:.*$/i, "").replace(/\s*Available only.*$/i, "")) ?? raw;28      const azs = Number.parseInt(row[2]!.replace(/[^\d]/g, ""), 10);29      // "Hong Kong (China), Macao (China), and Taiwan (China)" → drop the "(China)" qualifiers before reading the city30      const bare = name.replace(/\s*\(China\)/g, "");31      const label = parenthetical(bare)?.trim() ?? bare.replace(/\s+(Finance|Self-driving Cloud).*$/i, "").split(/,|\band\b/)[0]!.trim();32      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);33      const isoHint = /hong kong/i.test(label) ? "HK" : chinaRegion ? "CN" : null;34      let place: Place = resolvePlace(label, isoHint);35      if (!place.countryIso2) place.countryIso2 = isoHint ?? countryFromText(label) ?? countryFromText(name);36      const externalIds: Record<string, string | number> = { tencent_region_id: code, published_location: label };37      const coverage = raw.match(/Coverage:\s*(.+)$/i)?.[1];38      if (coverage) externalIds.coverage = cleanText(coverage) ?? coverage;39      if (/Finance/i.test(name)) externalIds.realm = "finance";40      if (/Self-driving/i.test(name)) externalIds.realm = "self_driving_cloud";41      out.push(42        regionRecord({43          connectorId: ctx.connectorId,44          keyPrefix: "tencent:region",45          providerName: "Tencent Cloud",46          code,47          name,48          place,49          availabilityZones: Number.isFinite(azs) && azs > 0 ? azs : null,50          status: restricted || /Self-driving/i.test(name) ? "restricted" : "operational",51          externalIds,52          url: doc.finalUrl,53          methods: { code: "html:table.RegionID", name: "html:table.Region", availabilityZones: "html:table.NumberOfAZs", city: "curated-city-table" },54        }),55      );56    }57    ctx.log("info", `tencent: ${out.length} regions`);58    return out;59  },60};61