SPB Git forge
38commits 1branches 0releases
338.7 MBsize
maindefault branch
5 h agolast push
HTML 53.9% TypeScript 44.5% JavaScript 0.6% SQL 0.5%
9.0 KB · 76 lines typescript
Raw Blame History
1/** Operators, countries, metros, cloud regions, IXPs. */2import type { FastifyInstance } from "fastify";3import { z } from "zod";4import { publicGet } from "../../lib/route.js";5import { TTL, notFound } from "../../lib/http.js";6import { boolParam, intParam, orderParam, pageParam, strParam } from "../../lib/params.js";7import { METHODOLOGY_MW, METHODOLOGY_CONTAINMENT } from "../../lib/sql.js";8import { getOperatorDetail, listOperators } from "../../repositories/operators.js";9import { getCountryDetail, listCountries } from "../../repositories/countries.js";10import { getMetroDetail, listMetros } from "../../repositories/metros.js";11import { getCloudRegion, listCloudRegions } from "../../repositories/cloud-regions.js";12import { getIxp, listIxps } from "../../repositories/ixps.js";1314const empty = (v: unknown) => (v === "" || v === null ? undefined : v);15const fPage = z.object({ fPage: pageParam });16const 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.`;17const 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.";1819export async function graphRoutes(app: FastifyInstance): Promise<void> {20  // ---- operators21  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 });22  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) => {23    const res = await listOperators(q);24    return { data: res.items, meta: { total: res.total, page: res.page, perPage: res.perPage, methodology: METHODOLOGY } };25  });26  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) => {27    const res = await getOperatorDetail(params.slug!, q.fPage);28    if (!res) throw notFound("operator");29    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.` } };30  });3132  // ---- countries33  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) => {34    const items = await listCountries({ all: q.all === true, region: q.region });35    return { data: items, meta: { total: items.length, methodology: METHODOLOGY } };36  });37  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) => {38    const d = await getCountryDetail(params.slugOrIso2!, q.fPage);39    if (!d) throw notFound("country");40    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).` } };41  });4243  // ---- metros44  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) => {45    const items = await listMetros({ country: q.country, q: q.q });46    return { data: items, meta: { total: items.length, methodology: METHODOLOGY } };47  });48  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) => {49    const d = await getMetroDetail(params.slug!, q.fPage);50    if (!d) throw notFound("metro");51    return { data: d, meta: { facilitiesPage: q.fPage, facilitiesPerPage: 50, facilitiesTotal: d.facilityCount, methodology: `${METHODOLOGY} ${HHI_NOTE}` } };52  });5354  // ---- cloud regions55  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) => {56    const items = await listCloudRegions({ provider: q.provider, country: q.country, status: q.status });57    return { data: items, meta: { total: items.length } };58  });59  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) => {60    const d = await getCloudRegion(params.slug!);61    if (!d) throw notFound("cloud region");62    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." } };63  });6465  // ---- ixps66  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) => {67    const items = await listIxps({ country: q.country, q: q.q });68    return { data: items, meta: { total: items.length } };69  });70  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) => {71    const d = await getIxp(params.slug!);72    if (!d) throw notFound("ixp");73    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." } };74  });75}76