import type { Parser, ExtractedRecord, RawDocument } from "@dci/connectors"; import { resolvePlace, regionRecord, type Place } from "./shared.js"; /** * Microsoft Azure — https://datacenters.microsoft.com/globe/data/geo/regions.json * Public JSON behind the "Azure global infrastructure" globe. One object per region: * id, displayName, location (city or state), geographyId, latitude/longitude (region marker, NOT a * facility), typeId (regionaz | aregionaz | region | regionaaz), isOpen, yearOpen, monthOpen, * availabilityZonesId (available | soon | nearest | nearestSoon), availableTo, announcementLink. */ interface AzureRegion { id: string; displayName: string; location?: string; geographyId?: string; continent?: string; latitude?: number; longitude?: number; typeId?: string; isOpen?: boolean; yearOpen?: number | null; monthOpen?: string | null; availabilityZonesId?: string; availabilityZonesNearestRegionIds?: string[]; availableTo?: string; announcementLink?: string; dataResidency?: string; hasGroundStation?: boolean; } /** Azure geographyId → ISO-2 (fallback when `location` is a state/area that the city table cannot resolve). */ const GEOGRAPHY_ISO: Record = { africa: "ZA", australia: "AU", austria: "AT", belgium: "BE", brazil: "BR", canada: "CA", chile: "CL", china: "CN", denmark: "DK", finland: "FI", france: "FR", germany: "DE", greece: "GR", india: "IN", indonesia: "ID", israel: "IL", italy: "IT", japan: "JP", korea: "KR", malaysia: "MY", mexico: "MX", newzealand: "NZ", norway: "NO", poland: "PL", qatar: "QA", saudiarabia: "SA", spain: "ES", sweden: "SE", switzerland: "CH", taiwan: "TW", unitedarabemirates: "AE", unitedkingdom: "GB", unitedstates: "US", }; /** Regions whose `location` is ambiguous without a country (e.g. "Hong Kong" under geography asiapacific). */ const LOCATION_ISO_OVERRIDES: Record = { eastasia: "HK", southeastasia: "SG", northeurope: "IE", westeurope: "NL", chinanorth3: "CN" }; /** Sub-national areas Microsoft names that are neither a city nor a US state. */ const AREA_LOCATIONS: Record = { Hebei: { regionName: "Hebei", iso: "CN" } }; const MONTHS: Record = { january: "01", february: "02", march: "03", april: "04", may: "05", june: "06", july: "07", august: "08", september: "09", october: "10", november: "11", december: "12" }; export const azureRegionsParser: Parser = { name: "cloud_azure_regions_v1", version: "1.0.0", pageTypes: ["cloud_region"], parse(doc: RawDocument, ctx): ExtractedRecord[] { let list: AzureRegion[]; try { const parsed = JSON.parse(doc.text) as unknown; list = Array.isArray(parsed) ? (parsed as AzureRegion[]) : ((parsed as { regions?: AzureRegion[] }).regions ?? []); } catch (e) { ctx.log("warn", `azure regions.json not JSON: ${(e as Error).message}`); return []; } const out: ExtractedRecord[] = []; for (const r of list) { if (!r?.id || !r.displayName) continue; const isoHint = LOCATION_ISO_OVERRIDES[r.id] ?? (r.geographyId ? GEOGRAPHY_ISO[r.geographyId] : undefined) ?? null; const area = r.location ? AREA_LOCATIONS[r.location] : undefined; let place: Place = area ? { city: null, regionName: area.regionName, countryIso2: area.iso, lat: null, lng: null, geoPrecision: null } : resolvePlace(r.location ?? null, isoHint); if (!place.countryIso2 && isoHint) place.countryIso2 = isoHint; // Curated city coordinates first. When Microsoft only names a sub-national area (a US state, Hebei, …) fall // back to their own region marker flagged "approximate". When they only name a country ("Netherlands", // "Ireland", "Israel") no marker is emitted: their country markers can sit across a border or offshore. let geoSource: string | undefined; if (place.lat == null && place.regionName && !place.city && typeof r.latitude === "number" && typeof r.longitude === "number") { place = { ...place, lat: r.latitude, lng: r.longitude, geoPrecision: "approximate" }; geoSource = "provider-marker:azure"; } const open = r.isOpen === true; const restricted = /business presence in China|government|by invitation/i.test(r.availableTo ?? ""); const status = !open ? "announced" : restricted ? "restricted" : "operational"; const launchedOn = open && r.yearOpen ? (r.monthOpen && MONTHS[r.monthOpen.toLowerCase()] ? `${r.yearOpen}-${MONTHS[r.monthOpen.toLowerCase()]}` : String(r.yearOpen)) : null; const azState = r.availabilityZonesId ?? ""; const externalIds: Record = { azure_region_id: r.id, azure_type: r.typeId ?? "", azure_geography: r.geographyId ?? "", availability_zones_state: azState }; if (r.availableTo) externalIds.available_to = r.availableTo; if (r.announcementLink) externalIds.announcement_url = r.announcementLink; if (r.dataResidency) externalIds.data_residency = r.dataResidency; if (r.location) externalIds.published_location = r.location; out.push( regionRecord({ connectorId: ctx.connectorId, keyPrefix: "azure:region", providerName: "Microsoft Azure", code: r.id, name: r.displayName, place, // Microsoft publishes whether AZs are available, not how many; leave the count null when unknown. availabilityZones: null, launchedOn, status, isSovereign: false, externalIds, geoSource, url: doc.finalUrl, methods: { code: "json:id", name: "json:displayName", city: "json:location+curated-city-table", status: "json:isOpen", launchedOn: "json:yearOpen", ...(geoSource ? { lat: "json:latitude(region marker)" } : {}) }, certainty: 0.98, }), ); } ctx.log("info", `azure: ${out.length} regions (${out.filter((r) => r.data.status === "announced").length} announced)`); return out; }, };