/** Operators, countries, metros, cloud regions, IXPs. */ import type { FastifyInstance } from "fastify"; import { z } from "zod"; import { publicGet } from "../../lib/route.js"; import { TTL, notFound } from "../../lib/http.js"; import { boolParam, intParam, orderParam, pageParam, strParam } from "../../lib/params.js"; import { METHODOLOGY_MW, METHODOLOGY_CONTAINMENT } from "../../lib/sql.js"; import { getOperatorDetail, listOperators } from "../../repositories/operators.js"; import { getCountryDetail, listCountries } from "../../repositories/countries.js"; import { getMetroDetail, listMetros } from "../../repositories/metros.js"; import { getCloudRegion, listCloudRegions } from "../../repositories/cloud-regions.js"; import { getIxp, listIxps } from "../../repositories/ixps.js"; const empty = (v: unknown) => (v === "" || v === null ? undefined : v); const fPage = z.object({ fPage: pageParam }); const METHODOLOGY = `${METHODOLOGY_MW} ${METHODOLOGY_CONTAINMENT} mwCoverage (0..1) is the share of counted facilities with a published MW figure — compare MW totals only when coverage is comparable.`; const HHI_NOTE = "concentration.hhi = Σ (share × 100)² over operators (0–10 000), computed on facility counts and separately on known MW with its coverage; momentum components are shown separately and never collapsed into a score."; export async function graphRoutes(app: FastifyInstance): Promise { // ---- operators const operatorsQuery = z.object({ q: strParam, kind: strParam, country: strParam, sort: z.preprocess(empty, z.enum(["facilities", "name", "mw"]).optional()), order: orderParam, page: pageParam, per_page: intParam }); publicGet(app, { url: "/operators", ttl: TTL.list, query: operatorsQuery, summary: "List operators (OperatorSummary[]) with containment-aware facility aggregates", tags: ["operators"], response: { type: "OperatorSummary[]", example: [{ id: "op_x", slug: "equinix", name: "Equinix", kind: "colocation", facilityCount: 262, countryCount: 33, metroCount: 71, knownMw: 1180.5, plannedMw: null, constructionMw: 60, projectCount: 4, aiCount: 0, cloudRegionCount: 0, mwCoverage: 0.61, isCloudProvider: false, isCarrier: false }] } }, async (q) => { const res = await listOperators(q); return { data: res.items, meta: { total: res.total, page: res.page, perPage: res.perPage, methodology: METHODOLOGY } }; }); publicGet(app, { url: "/operators/:slug", ttl: TTL.detail, query: fPage, summary: "Operator detail (OperatorDetail): pipeline, expansion velocity, top countries / metros, AI facilities, cloud regions, corporate events, claims, data quality; facilities paginated with ?fPage (50 per page)", tags: ["operators"], params: { slug: "operator slug or op_… id" }, response: { type: "OperatorDetail", description: "OperatorSummary + pipeline (PipelineBreakdown), velocity (12m / 3y / 5y windows, countriesOverTime with basis), topCountries / topMetros with share, aiFacilities, cloudRegions, corporateEvents, claims, dataQuality and the legacy lists." } }, async (q, params) => { const res = await getOperatorDetail(params.slug!, q.fPage); if (!res) throw notFound("operator"); return { data: res.detail, sources: res.sources, meta: { facilitiesPage: q.fPage, facilitiesPerPage: 50, facilitiesTotal: res.detail.facilityCount, methodology: `${METHODOLOGY} Velocity windows use opened_on (else first indexed) for facilities and announced_on (else first indexed) for projects — the basis is labelled per point. Corporate events (acquisitions, financing, partnerships, executive changes) are never mixed with physical projects.` } }; }); // ---- countries publicGet(app, { url: "/countries", ttl: TTL.list, query: z.object({ all: boolParam, region: strParam }), summary: "Countries with ≥1 facility or cloud region (CountrySummary[]); ?all=1 for every country", tags: ["countries"], response: { type: "CountrySummary[]", example: [{ iso2: "US", iso3: "USA", slug: "united-states", name: "United States", facilityCount: 3200, operationalCount: 2900, constructionCount: 60, plannedCount: 110, knownMw: 9800, projectCount: 120, projectPlannedMw: 14000, ixpCount: 140, mwCoverage: 0.22 }] } }, async (q) => { const items = await listCountries({ all: q.all === true, region: q.region }); return { data: items, meta: { total: items.length, methodology: METHODOLOGY } }; }); publicGet(app, { url: "/countries/:slugOrIso2", ttl: TTL.detail, query: fPage, summary: "Country detail (CountryDetail): IXPs, grid constraints, energy context, AI facilities / projects, pipeline, coverage, claims; facilities paginated with ?fPage", tags: ["countries"], params: { slugOrIso2: "country slug, ISO-3166 alpha-2 or alpha-3" }, response: { type: "CountryDetail", description: "CountrySummary + ixps, gridConstraints, energy (national grid averages with a note — they do not describe a facility's contracted electricity), aiFacilities, aiProjects, pipeline, coverage (CoverageRow), claims and the legacy lists." } }, async (q, params) => { const d = await getCountryDetail(params.slugOrIso2!, q.fPage); if (!d) throw notFound("country"); return { data: d, meta: { facilitiesPage: q.fPage, facilitiesPerPage: 50, facilitiesTotal: d.facilityCount, methodology: `${METHODOLOGY} announcedInvestmentUsd sums site-scoped project investments only (company capex, deal values and national programmes are kept as claims).` } }; }); // ---- metros publicGet(app, { url: "/metros", ttl: TTL.list, query: z.object({ country: strParam, q: strParam }), summary: "Metros / markets with containment-aware counts (MetroSummary[])", tags: ["metros"], response: { type: "MetroSummary[]", example: [{ id: "met_x", slug: "northern-virginia", name: "Northern Virginia", countryIso2: "US", lat: 39.04, lng: -77.49, facilityCount: 310, operationalCount: 280, constructionCount: 20, plannedCount: 10, knownMw: 4200, operatorCount: 40, cloudRegionCount: 6, ixpCount: 4, projectCount: 25, projectPlannedMw: 5600, aiCount: 12, mwCoverage: 0.48 }] } }, async (q) => { const items = await listMetros({ country: q.country, q: q.q }); return { data: items, meta: { total: items.length, methodology: METHODOLOGY } }; }); publicGet(app, { url: "/metros/:slug", ttl: TTL.detail, query: fPage, summary: "Metro detail (MetroDetail): concentration (HHI), 12-month momentum components, pipeline, grid constraints, AI facilities, opening timeline, coverage, claims; facilities paginated with ?fPage", tags: ["metros"], params: { slug: "metro slug or met_… id" }, response: { type: "MetroDetail", description: "MetroSummary + concentration (MarketConcentration), momentum (MarketMomentum, window 12m), pipeline, gridConstraints (grid_constraints rows + grid / utility / power events), aiFacilities, openingTimeline, coverage, claims and the legacy lists." } }, async (q, params) => { const d = await getMetroDetail(params.slug!, q.fPage); if (!d) throw notFound("metro"); return { data: d, meta: { facilitiesPage: q.fPage, facilitiesPerPage: 50, facilitiesTotal: d.facilityCount, methodology: `${METHODOLOGY} ${HHI_NOTE}` } }; }); // ---- cloud regions publicGet(app, { url: "/cloud-regions", ttl: TTL.list, query: z.object({ provider: strParam, country: strParam, status: strParam }), summary: "Cloud regions (CloudRegionSummary[])", tags: ["cloud-regions"], response: { type: "CloudRegionSummary[]" } }, async (q) => { const items = await listCloudRegions({ provider: q.provider, country: q.country, status: q.status }); return { data: items, meta: { total: items.length } }; }); publicGet(app, { url: "/cloud-regions/:slug", ttl: TTL.detail, summary: "Cloud region detail (CloudRegionDetail): host facilities (publicly verified only) or market facilities, siblings, events, provenance", tags: ["cloud-regions"], params: { slug: "cloud region slug or cr_… id" }, response: { type: "CloudRegionDetail" } }, async (_q, params) => { const d = await getCloudRegion(params.slug!); if (!d) throw notFound("cloud region"); return { data: d, meta: { methodology: "hostFacilities lists only facilities with an explicit public tenancy link to the provider; otherwise the region is associated with its market (marketFacilities) and never pinned to a building." } }; }); // ---- ixps publicGet(app, { url: "/ixps", ttl: TTL.list, query: z.object({ country: strParam, q: strParam }), summary: "Internet exchange points (IxpSummary[])", tags: ["ixps"], response: { type: "IxpSummary[]" } }, async (q) => { const items = await listIxps({ country: q.country, q: q.q }); return { data: items, meta: { total: items.length } }; }); publicGet(app, { url: "/ixps/:slug", ttl: TTL.detail, summary: "IXP detail (IxpDetail): facilities, operators, nearby facilities (metro coordinates), source history", tags: ["ixps"], params: { slug: "ixp slug or ix_… id" }, response: { type: "IxpDetail" } }, async (_q, params) => { const d = await getIxp(params.slug!); if (!d) throw notFound("ixp"); return { data: d, meta: { methodology: "IXPs carry no published coordinates; nearby facilities are computed from the metro reference point when the IXP is assigned to a metro." } }; }); }