Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion src/app/benchmarks/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,46 @@ export default async function BenchmarkPage({
const variants: Record<string, Benchmark> = {
[variantKey(null, null, null)]: aggregate,
};
const benchmark = aggregate;
// Single-origin surface guard. When this build pins one region
// (aggregate_filters.region) and declares no region dimension, the page
// must not ship the other regions' data in its payload either: the blob
// carries every region the worker computed, and whatever is passed to a
// client component lands in the RSC payload, readable in view-source even
// though nothing renders it. Keep the pinned region's cells, drop the
// rest. Benches that declare region tabs are untouched.
const pinnedRegion = aggregate.aggregateFilters?.region;
const singleOrigin = regionOptions.length === 0 && typeof pinnedRegion === "string";
const samePinned = (r: string) => canonRegion(r) === canonRegion(pinnedRegion ?? "");
const benchmark = !singleOrigin
? aggregate
: {
...aggregate,
extras: {
...aggregate.extras,
regions: Object.fromEntries(
Object.entries(aggregate.extras.regions ?? {}).map(([slug, pts]) => [
slug,
(pts ?? []).filter((pt) => samePinned(pt.region)),
]),
),
seriesByRegion24h: aggregate.extras.seriesByRegion24h
? Object.fromEntries(
Object.entries(aggregate.extras.seriesByRegion24h).map(([slug, byRegion]) => [
slug,
Object.fromEntries(Object.entries(byRegion).filter(([r]) => samePinned(r))),
]),
)
: undefined,
},
cellRanks: aggregate.cellRanks
? Object.fromEntries(
Object.entries(aggregate.cellRanks).filter(([key]) => {
const r = key.split("|").pop() ?? "";
return r === "" || r === "all" || samePinned(r);
}),
)
: undefined,
};

const isDraft = benchmark.status === "draft";
const isAwaiting = isDraft && benchmark.editorialStatus === "live";
Expand Down
15 changes: 15 additions & 0 deletions src/lib/sitemap-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { COMPARE_PAIRS } from "@/data/compare-pairs";
import { REMOVED_BENCH_SLUGS } from "@/middleware";
import { REMOVED_PRODUCT_SLUGS } from "@/lib/removed-benches";
import { isHlBuilderSlug } from "@/lib/hl-builder-stats";
import { getSpecs } from "@/lib/spec";
import { PROVIDER_REGISTRY } from "@/data/provider-registry";
import { PERP_PRODUCT_PILL_SLUGS } from "@/lib/perp-venue-context";
import { loadAllAlternatives } from "@/lib/alternatives";
import { loadAllAnswers } from "@/lib/answers";
Expand Down Expand Up @@ -275,9 +277,22 @@ async function buildFullSitemap(): Promise<MetadataRoute.Sitemap> {
// Prom) so it's safe async — no OOM risk (unlike the old getProvider fan-out).
// It also catches dormant HL frontends missing from the Prom cohort that
// the worker couldn't filter without the spec provider list.
// Only list product pages this build can actually serve. The blob is
// produced by the worker from its own checkout, so it can name providers
// that a spec on THIS branch does not declare yet; /products/<slug> then
// 404s and the deploy's sitemap smoke blocks the release (2026-09-08:
// /products/serialized, declared on dev, listed in prod's sitemap).
const declaredProviderSlugs = new Set<string>();
for (const spec of await getSpecs()) {
for (const p of spec.providers ?? []) declaredProviderSlugs.add(p.slug);
}
for (const entry of Object.values(PROVIDER_REGISTRY)) {
if (entry.parent) declaredProviderSlugs.add(entry.parent);
}
const validatedSlugs = (
await Promise.all(
providerSlugs.map(async (slug) => {
if (!declaredProviderSlugs.has(slug)) return null;
if (CHAIN_BY_SLUG.has(slug)) return null;
if (hlBuilderSlugSet.has(slug)) return null;
if (await isHlBuilderSlug(slug)) return null;
Expand Down
Loading