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%
5.1 KB · 44 lines typescript
Raw Blame History
1import type { FastifyInstance } from "fastify";2import { z } from "zod";3import { publicGet } from "../../lib/route.js";4import { TTL, csv, notFound } from "../../lib/http.js";5import { facilityFiltersSchema, csvParam, numParam, strParam } from "../../lib/params.js";6import { METHODOLOGY_MW, METHODOLOGY_CONTAINMENT } from "../../lib/sql.js";7import { sourcesForEntities } from "../../lib/source-history.js";8import { getFacilityClaims, getFacilityDetail, getFacilityHistory, getFacilityProvenance, listFacilities, toFacilityQuery } from "../../repositories/facilities.js";910const LIST_NOTE = `${METHODOLOGY_MW} ${METHODOLOGY_CONTAINMENT} List rows may show both a campus (recordScope=campus) and its buildings (parentFacility set) — aggregates never count both.`;11const radiusQuery = z.object({ radius_km: numParam });12const claimsQuery = z.object({ status: csvParam, predicate: strParam });1314export async function facilityRoutes(app: FastifyInstance): Promise<void> {15  publicGet(app, { url: "/datacenters", ttl: TTL.list, query: facilityFiltersSchema, summary: "List facilities (FacilitySummary[]) with filters, sorting and pagination", tags: ["facilities"], response: { type: "FacilitySummary[]", description: "Facilities matching the filters; `sources` = distinct sources behind the returned rows (≤ 30).", example: [{ id: "fac_…", slug: "example-dc1", name: "Example DC1", status: "operational", itCapacityMw: 12.5, recordScope: "facility", aiEvidence: "unknown" }] } }, async (q) => {16    const res = await listFacilities(toFacilityQuery(q));17    return { data: res.items, meta: { total: res.total, page: res.page, perPage: res.perPage, methodology: LIST_NOTE }, sources: await sourcesForEntities("facility", res.items.map((f) => f.id)) };18  });1920  publicGet(app, { url: "/datacenters/:idOrSlug", ttl: TTL.detail, query: radiusQuery, summary: "Facility detail by slug or id (FacilityDetail): buildings, tenants, claims, capacity history, nearby infrastructure (?radius_km ≤ 200, default 25), power context, data quality", tags: ["facilities"], params: { idOrSlug: "facility slug or fac_… id" }, response: { type: "FacilityDetail" } }, async (q, params) => {21    const res = await getFacilityDetail(params.idOrSlug!, { radiusKm: q.radius_km != null ? Math.min(200, Math.max(0.1, q.radius_km)) : undefined });22    if (!res) throw notFound("facility");23    return { data: res.detail, sources: res.sources, meta: { methodology: `${METHODOLOGY_MW} Claims list every figure published about this site with its scope; only site-scoped, non-rejected claims may back the displayed values (isWinner).` } };24  });2526  publicGet(app, { url: "/datacenters/:idOrSlug/history", ttl: TTL.detail, summary: "Field history (EntityHistory): dated observations, claims and value changes per field", tags: ["facilities"], params: { idOrSlug: "facility slug or fac_… id" }, response: { type: "EntityHistory", example: { entityType: "facility", entityId: "fac_…", fields: { itCapacityMw: [{ date: "2025-03-01T00:00:00.000Z", field: "itCapacityMw", value: 12.5, kind: "observed", sourceId: "src_…", sourceName: "Operator site", sourceKind: "operator", url: "https://…" }] }, changes: [] } } }, async (_q, params) => {27    const res = await getFacilityHistory(params.idOrSlug!);28    if (!res) throw notFound("facility");29    return { data: res.history, sources: res.sources, meta: { methodology: "observed = a source page stated the value (first observation date); claim = a figure asserted by a document (published date when known); changed = a detected value change with old → new. Dates are ISO or partial (YYYY, YYYY-MM)." } };30  });3132  publicGet(app, { url: "/datacenters/:idOrSlug/claims", ttl: TTL.detail, query: claimsQuery, summary: "Claims about a facility (ClaimDTO[]) — every published figure with scope, evidence sentence, authority tier and status (?status=current,unscoped&predicate=)", tags: ["facilities"], params: { idOrSlug: "facility slug or fac_… id" }, response: { type: "ClaimDTO[]" } }, async (q, params) => {33    const res = await getFacilityClaims(params.idOrSlug!, { status: csv(q.status), predicate: q.predicate });34    if (!res) throw notFound("facility");35    return { data: res.claims, sources: res.sources, meta: { total: res.claims.length, facilityId: res.id, methodology: "A claim is one figure asserted by one document about this site. Scope building/facility/campus may back the displayed value (isWinner); portfolio/company/country/metro/unknown scopes are kept as claims only (status unscoped)." } };36  });3738  publicGet(app, { url: "/datacenters/:idOrSlug/provenance", ttl: TTL.detail, summary: "All provenance observations for a facility (ProvenanceDTO[]), current and superseded, with winner flag", tags: ["facilities"], params: { idOrSlug: "facility slug or fac_… id" }, response: { type: "ProvenanceDTO[]" } }, async (_q, params) => {39    const res = await getFacilityProvenance(params.idOrSlug!);40    if (!res) throw notFound("facility");41    return { data: res.provenance, sources: res.sources, meta: { total: res.provenance.length, current: res.current, superseded: res.provenance.length - res.current, facilityId: res.id } };42  });43}44