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%
14.1 KB · 292 lines tsx
Raw Blame History
1import type { CloudRegionSummary } from "@dci/core";2import type { Metadata } from "next";3import Link from "next/link";4import { Breadcrumbs } from "@/components/entity/breadcrumbs";5import { SectionNav } from "@/components/entity/section-nav";6import { SourcesFooter } from "@/components/entity/sources-footer";7import { Bars } from "@/components/intelligence/client-charts";8import { ApiNote, CoverageBar, HonestEmpty, PillLink } from "@/components/intelligence/shared";9import { Page, PageHeader, Section } from "@/components/ui/card";10import { CountryFlag } from "@/components/ui/country-flag";11import { Metric, MetricCell, MetricRow } from "@/components/ui/metric";12import { ErrorState, OfflineNotice } from "@/components/ui/states";13import { RowLink, TBody, THead, Table, Td, Th, Tr } from "@/components/ui/table";14import { api } from "@/lib/api";15import { fmtInt, placeLine } from "@/lib/format";16import { routes } from "@/lib/routes";17import { pageMetadata } from "@/lib/seo";1819export const revalidate = 120;2021export const metadata: Metadata = pageMetadata({22  title: "Connectivity",23  description: "Internet exchanges, cloud regions by provider, carrier hotels and connectivity density by market — from published IXP↔facility links and provider region lists. Submarine cable landing stations are listed only once a licensed source exists.",24  path: routes.connectivity(),25});2627const IXP_LIMIT = 50;2829export default async function ConnectivityPage() {30  const r = await api.connectivity();31  if (!r.ok) {32    return (33      <Page className="py-6">34        <Breadcrumbs items={[{ name: "Connectivity", href: routes.connectivity() }]} className="mb-4" />35        <OfflineNotice detail={r.error} className="mb-4" />36        <ErrorState />37      </Page>38    );39  }40  const d = r.data;41  const ixps = [...d.ixps].sort((a, b) => b.facilityCount - a.facilityCount).slice(0, IXP_LIMIT);4243  // cloud regions grouped by provider44  const byProvider = new Map<string, { provider: CloudRegionSummary["provider"]; regions: CloudRegionSummary[] }>();45  for (const cr of d.cloudRegions) {46    const g = byProvider.get(cr.provider.id) ?? { provider: cr.provider, regions: [] };47    g.regions.push(cr);48    byProvider.set(cr.provider.id, g);49  }50  const providers = Array.from(byProvider.values()).sort((a, b) => b.regions.length - a.regions.length);51  const statusSplit = (regions: CloudRegionSummary[]) => {52    const c: Record<string, number> = {};53    for (const x of regions) c[x.status] = (c[x.status] ?? 0) + 1;54    return Object.entries(c)55      .sort((a, b) => b[1] - a[1])56      .map(([k, v]) => `${v} ${k}`)57      .join(" · ");58  };59  const metrosCovered = d.byMetro.filter((m) => m.ixps > 0 || m.cloudRegions > 0 || m.facilitiesWithCarriers > 0).length;6061  const sections = [62    { id: "ixps", label: "Internet exchanges", count: ixps.length },63    { id: "cloud", label: "Cloud regions", count: providers.length },64    { id: "carrier-hotels", label: "Carrier hotels", count: d.carrierHotels.length },65    { id: "metros", label: "By market", count: d.byMetro.length },66    { id: "landing", label: "Landing stations" },67  ];6869  return (70    <Page>71      <Breadcrumbs items={[{ name: "Connectivity", href: routes.connectivity() }]} className="pt-3" />72      <PageHeader eyebrow="Intelligence" title="Connectivity" description="Where networks meet: internet exchanges, public cloud regions, carrier-dense facilities and the markets that concentrate them." aside={<PillLink href={routes.map("layer=connectivity")}>Connectivity layer on the map →</PillLink>} />7374      <MetricRow cols={5}>75        <MetricCell>76          <Metric value={fmtInt(d.ixps.length)} label="Internet exchanges" note="with facility links" />77        </MetricCell>78        <MetricCell>79          <Metric value={fmtInt(d.cloudRegions.length)} label="Cloud regions" note={`${fmtInt(providers.length)} providers`} />80        </MetricCell>81        <MetricCell>82          <Metric value={fmtInt(d.carrierHotels.length)} label="Carrier hotels" note="type carrier hotel or ≥ 20 carriers" />83        </MetricCell>84        <MetricCell>85          <Metric value={fmtInt(metrosCovered)} label="Markets covered" note={`of ${fmtInt(d.byMetro.length)} listed`} />86        </MetricCell>87        <MetricCell>88          <Metric value={fmtInt(d.landingStations.length)} label="Landing stations" note={d.landingStations.length === 0 ? "no licensed connector yet" : "licensed source"} />89        </MetricCell>90      </MetricRow>91      <ApiNote note={d.note} label="From the index" className="mt-3" />9293      <SectionNav items={sections} className="mt-4" />9495      <div className="flex flex-col gap-8 pt-6">96        <Section id="ixps" title="Internet exchanges" description={`Top ${fmtInt(ixps.length)} by facilities where the exchange is present. Network counts are shown only when published.`} className="scroll-mt-28" aside={<Link href={routes.ixps()} className="text-[12.5px] text-ink-3 hover:text-ink">All exchanges →</Link>}>97          {ixps.length === 0 ? (98            <HonestEmpty title="No internet exchange linked to a facility yet" />99          ) : (100            <Table dense mobileCollapse={{ total: ixps.length, label: "exchanges" }}>101              <THead>102                <tr>103                  <Th>Exchange</Th>104                  <Th>Country</Th>105                  <Th className="hidden md:table-cell">Market</Th>106                  <Th numeric className="hidden sm:table-cell">107                    Networks108                  </Th>109                  <Th numeric>Facilities</Th>110                  <Th numeric className="hidden sm:table-cell">111                    Operators112                  </Th>113                </tr>114              </THead>115              <TBody>116                {ixps.map((x) => (117                  <Tr key={x.id}>118                    <Td primary>119                      <RowLink href={routes.ixp(x.slug)}>{x.name}</RowLink>120                      {x.nameLong && x.nameLong !== x.name && <div className="hidden text-[11.5px] text-ink-3 sm:block">{x.nameLong}</div>}121                    </Td>122                    <Td label="Country">123                      <CountryFlag iso2={x.countryIso2} withName className="max-w-[140px] text-ink-2" />124                    </Td>125                    <Td label="Market" className="hidden md:table-cell" muted>126                      {x.metro ? (127                        <Link href={routes.market(x.metro.slug)} className="hover:text-ink hover:underline">128                          {x.metro.name}129                        </Link>130                      ) : (131                        (x.city ?? "—")132                      )}133                    </Td>134                    <Td label="Networks" numeric className="hidden sm:table-cell">135                      {x.networkCount === null ? <span className="text-ink-4">—</span> : fmtInt(x.networkCount)}136                    </Td>137                    <Td label="Facilities" numeric lead>138                      {fmtInt(x.facilityCount)}139                    </Td>140                    <Td label="Operators" numeric className="hidden sm:table-cell">141                      {fmtInt(x.operatorCount)}142                    </Td>143                  </Tr>144                ))}145              </TBody>146            </Table>147          )}148        </Section>149150        <Section id="cloud" title="Cloud regions by provider" description="Public cloud regions as published by each provider; regions are associated with a market, not a facility, unless a source says so." className="scroll-mt-28" aside={<Link href={routes.cloudRegions()} className="text-[12.5px] text-ink-3 hover:text-ink">All cloud regions →</Link>}>151          {providers.length === 0 ? (152            <HonestEmpty title="No cloud region indexed" />153          ) : (154            <Bars155              showRank={false}156              items={providers.map((p) => ({157                label: p.provider.name,158                value: p.regions.length,159                href: `${routes.cloudRegions()}?provider=${encodeURIComponent(p.provider.slug)}`,160                secondary: statusSplit(p.regions),161                display: `${fmtInt(p.regions.length)} ${p.regions.length === 1 ? "region" : "regions"}`,162              }))}163            />164          )}165        </Section>166167        <Section id="carrier-hotels" title="Carrier hotels" description="Facilities typed carrier hotel or with ≥ 20 published carriers, by carrier count." className="scroll-mt-28">168          {d.carrierHotels.length === 0 ? (169            <HonestEmpty title="No carrier hotel identified" detail="carrier counts come from published facility pages and registries" />170          ) : (171            <Table dense mobileCollapse={{ total: d.carrierHotels.length, label: "facilities" }}>172              <THead>173                <tr>174                  <Th>Facility</Th>175                  <Th className="hidden sm:table-cell">Operator</Th>176                  <Th>Location</Th>177                  <Th numeric>Carriers</Th>178                  <Th numeric className="hidden sm:table-cell">179                    IXPs180                  </Th>181                </tr>182              </THead>183              <TBody>184                {d.carrierHotels.map((f) => (185                  <Tr key={f.id}>186                    <Td primary>187                      <RowLink href={routes.facility(f.slug)}>{f.name}</RowLink>188                      <div className="text-[11.5px] text-ink-3 sm:hidden">{f.operator?.name ?? "Operator unknown"}</div>189                    </Td>190                    <Td label="Operator" className="hidden sm:table-cell">191                      {f.operator ? (192                        <Link href={routes.operator(f.operator.slug)} className="text-ink-2 hover:text-ink hover:underline">193                          {f.operator.name}194                        </Link>195                      ) : (196                        <span className="text-ink-4">—</span>197                      )}198                    </Td>199                    <Td label="Location" muted>200                      <span className="inline-flex items-center gap-1.5">201                        <CountryFlag iso2={f.countryIso2} size={12} />202                        <span className="truncate">{placeLine(f)}</span>203                      </span>204                    </Td>205                    <Td label="Carriers" numeric lead>206                      {f.carriersCount === null || f.carriersCount === undefined ? <span className="text-ink-4">—</span> : fmtInt(f.carriersCount)}207                    </Td>208                    <Td label="IXPs" numeric className="hidden sm:table-cell">209                      {f.ixpCount === null || f.ixpCount === undefined ? <span className="text-ink-4">—</span> : fmtInt(f.ixpCount)}210                    </Td>211                  </Tr>212                ))}213              </TBody>214            </Table>215          )}216        </Section>217218        <Section id="metros" title="By market" description="Exchanges, cloud regions and the share of facilities with published carriers, per market." className="scroll-mt-28">219          {d.byMetro.length === 0 ? (220            <HonestEmpty title="No market with connectivity data" />221          ) : (222            <Table dense mobileCollapse={{ total: d.byMetro.length, label: "markets" }}>223              <THead>224                <tr>225                  <Th>Market</Th>226                  <Th numeric>IXPs</Th>227                  <Th numeric>Cloud regions</Th>228                  <Th className="hidden sm:table-cell">Facilities with carriers</Th>229                  <Th numeric className="hidden md:table-cell">230                    Facilities231                  </Th>232                </tr>233              </THead>234              <TBody>235                {d.byMetro.map((m) => (236                  <Tr key={m.id}>237                    <Td primary>238                      <RowLink href={routes.market(m.slug)}>239                        <span className="inline-flex items-center gap-1.5">240                          <CountryFlag iso2={m.countryIso2} size={12} />241                          {m.name}242                        </span>243                      </RowLink>244                    </Td>245                    <Td label="IXPs" numeric lead>246                      {fmtInt(m.ixps)}247                    </Td>248                    <Td label="Cloud regions" numeric>249                      {fmtInt(m.cloudRegions)}250                    </Td>251                    <Td label="Facilities with carriers" className="hidden sm:table-cell">252                      <span className="inline-flex items-center gap-2">253                        <CoverageBar value={m.facilities ? m.facilitiesWithCarriers / m.facilities : null} width="w-[110px]" label="Facilities with published carriers" />254                        <span className="figure text-[11.5px] text-ink-3">255                          {fmtInt(m.facilitiesWithCarriers)}/{fmtInt(m.facilities)}256                        </span>257                      </span>258                    </Td>259                    <Td label="Facilities" numeric className="hidden md:table-cell">260                      {fmtInt(m.facilities)}261                    </Td>262                  </Tr>263                ))}264              </TBody>265            </Table>266          )}267        </Section>268269        <Section id="landing" title="Submarine cable landing stations" description="Listed only from a licensed source." className="scroll-mt-28">270          {d.landingStations.length === 0 ? (271            <HonestEmpty title="No landing station indexed" detail="submarine cable landing stations are empty until a licensed connector exists — never inferred from proximity or news" />272          ) : (273            <ul className="divide-y divide-hair">274              {d.landingStations.map((l) => (275                <li key={l.id} className="flex items-center justify-between gap-3 py-2 text-[13px]">276                  <span className="inline-flex items-center gap-1.5 text-ink">277                    <CountryFlag iso2={l.countryIso2} size={12} />278                    {l.name}279                  </span>280                  <span className="text-[11.5px] text-ink-3">{l.sourceName}</span>281                </li>282              ))}283            </ul>284          )}285        </Section>286      </div>287288      <SourcesFooter sources={r.sources} note="IXP presence comes from published exchange↔facility links; carrier counts and cloud regions are taken as published. Nothing here is inferred from geography." />289    </Page>290  );291}292