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
7 changes: 7 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"permissions": {
"allow": [
"mcp__openchainbench__list_benchmarks"
]
}
}
8 changes: 4 additions & 4 deletions src/app/products/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { Breadcrumb } from "@/components/breadcrumb";
import { buildBreadcrumbJsonLd, safeJsonLd } from "@/lib/jsonld";
import {
fetchHlBuilderStats,
isHlBuilderSlug,
isHlBuilderWithHistory,
} from "@/lib/hl-builder-stats";
import { HlBuilderDashboard } from "@/components/hl-builder-dashboard";
import { RelatedProvidersSection } from "@/components/related-providers-section";
Expand Down Expand Up @@ -64,7 +64,7 @@ export async function generateMetadata({
// <head> injection — return the canonical + redirect-safe metadata so
// crawlers that peek at the response before the 308 fires still see the
// right canonical target.
if (await isHlBuilderSlug(slug)) {
if (await isHlBuilderWithHistory(slug)) {
const canonicalUrl = `${SITE.url}/hyperliquid/${slug}`;
return {
alternates: { canonical: canonicalUrl },
Expand Down Expand Up @@ -145,7 +145,7 @@ export default async function ProviderPage({
// frontends (12-month focus chart + peer group + KPI strip). Redirect
// /products/<hl-slug> straight there so backlinks + old SERP entries
// land on the richer hub without splitting rank signal across two URLs.
if (await isHlBuilderSlug(slug)) {
if (await isHlBuilderWithHistory(slug)) {
redirect(`/hyperliquid/${slug}`);
}
const p = await getProvider(slug);
Expand All @@ -158,7 +158,7 @@ export default async function ProviderPage({
// on-node harness has data for. Cheap (6 Prom scalars in parallel) and
// gracefully degrades to a hidden section when Prom is unreachable or
// the slug isn't an HL builder.
const isHlBuilder = await isHlBuilderSlug(p.slug);
const isHlBuilder = await isHlBuilderWithHistory(p.slug);
const hlStats = isHlBuilder ? await fetchHlBuilderStats(p.slug) : null;

// Prediction-market deep-dive: if the slug is a tracked PM venue or
Expand Down
47 changes: 33 additions & 14 deletions src/components/hl-cohort-leaderboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ export function HlCohortLeaderboard({
<tbody>
{filtered.map((r, i) => {
const hist = historyBySlug?.get(r.slug);
// Only builders that have a `hist` entry actually have a
// detail page at /hyperliquid/<slug> (the page component
// 404s when the history blob has no matching frontend, see
// hyperliquid/[slug]/page.tsx). Skip the link so Ahrefs +
// Bing don't queue up crawl paths that dead-end at 404.
const hasDetailPage = historyBySlug ? !!hist : true;
return (
<tr
key={r.slug}
Expand All @@ -123,15 +129,24 @@ export function HlCohortLeaderboard({
{i + 1}
</Td>
<Td>
<Link
href={`/hyperliquid/${r.slug}`}
className="flex items-center gap-2 min-w-0 hover:underline"
>
<ProviderLogo slug={r.slug} name={r.name} size={18} />
<span className="font-medium text-ink truncate">
{r.name}
{hasDetailPage ? (
<Link
href={`/hyperliquid/${r.slug}`}
className="flex items-center gap-2 min-w-0 hover:underline"
>
<ProviderLogo slug={r.slug} name={r.name} size={18} />
<span className="font-medium text-ink truncate">
{r.name}
</span>
</Link>
) : (
<span className="flex items-center gap-2 min-w-0">
<ProviderLogo slug={r.slug} name={r.name} size={18} />
<span className="font-medium text-ink truncate">
{r.name}
</span>
</span>
</Link>
)}
</Td>
<Td mono>{fmtUSD(r.revenue30d)}</Td>
<Td mono>{fmtUSD(r.volume30d)}</Td>
Expand All @@ -152,12 +167,16 @@ export function HlCohortLeaderboard({
</Td>
)}
<Td>
<Link
href={`/hyperliquid/${r.slug}`}
className="text-[11px] text-ink-faint hover:text-ink"
>
Open →
</Link>
{hasDetailPage ? (
<Link
href={`/hyperliquid/${r.slug}`}
className="text-[11px] text-ink-faint hover:text-ink"
>
Open →
</Link>
) : (
<span className="text-[11px] text-ink-faint">—</span>
)}
</Td>
</tr>
);
Expand Down
15 changes: 15 additions & 0 deletions src/lib/hl-builder-stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,21 @@ export async function isHlBuilderSlug(slug: string): Promise<boolean> {
return cachedSlugs.has(slug);
}

/**
* Same as `isHlBuilderSlug` but ALSO requires the slug to be present in
* the current history blob. Used before redirecting /products/<slug> to
* /hyperliquid/<slug> so we never send crawlers or users into a 404: a
* builder that's in the spec but not in the last-12-months history blob
* (dormant, new, or paused) would otherwise 404 at the redirect target.
* The 12-month history is the source of truth for what /hyperliquid/<slug>
* can actually render (see hyperliquid/[slug]/page.tsx:83-85).
*/
export async function isHlBuilderWithHistory(slug: string): Promise<boolean> {
if (!(await isHlBuilderSlug(slug))) return false;
const history = await fetchHlHistory();
return !!history?.frontends.some((f) => f.slug === slug);
}

function promUrl(): string | null {
return process.env.PROMETHEUS_URL?.trim() || null;
}
Expand Down
Loading