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
28 changes: 28 additions & 0 deletions src/lib/chain-aliases.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Chain-slug alias helpers, isolated from the chain registry so that
* `src/lib/spec.ts` can import them without pulling in the registry's
* data-layer dependency (`@/data/benchmarks` → `@/lib/spec` is a cycle
* that fails with a TDZ "Cannot access 'X' before initialization"
* crash at build time when both modules touch each other during ESM
* evaluation).
*
* Used during chain rebrand transitions (e.g. TON → Gram, June 2026)
* where stored snapshots + harness Prom labels still carry the legacy
* slug while the YAMLs + chain registry have moved to the canonical
* one. The site reads through these helpers to absorb the lag.
*
* Add an alias when a chain rebrands; remove an alias once the caches
* and harnesses have rotated past it.
*/

export const CHAIN_SLUG_ALIASES: Record<string, string> = {
ton: "gram",
};

/** Map any slug to its canonical chain slug. Identity for known slugs,
* resolves a legacy slug to its current canonical via CHAIN_SLUG_ALIASES,
* returns the input lowercased for anything unknown. */
export function canonicalChainSlug(slug: string): string {
const lc = slug.toLowerCase();
return CHAIN_SLUG_ALIASES[lc] ?? lc;
}
45 changes: 11 additions & 34 deletions src/lib/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
import { cache } from "react";
import { getBenchmarksSafe } from "@/data/benchmarks";
import type { Benchmark } from "@/types/benchmark";
import {
CHAIN_SLUG_ALIASES,
canonicalChainSlug,
} from "@/lib/chain-aliases";

export { CHAIN_SLUG_ALIASES, canonicalChainSlug };

export type ChainCategory = "L1" | "L2";

Expand Down Expand Up @@ -207,43 +213,14 @@ export const CHAINS: ChainEntry[] = [

export const CHAIN_BY_SLUG = new Map(CHAINS.map((c) => [c.slug, c]));

/**
* Legacy chain slugs that should resolve to a current canonical chain.
*
* Necessary because a slug rename (e.g. TON token rebrand to Gram in
* June 2026) leaves stale identifiers in three places that don't all
* roll over at the same time:
* - YAML provider entries that haven't been edited yet
* - Cached Benchmark snapshots in Upstash KV (populated by the
* materialize worker before the rename, results[].slug = old)
* - Harness Prom labels still emitting the old chain label
*
* `getBenchmarksForChain` honours these aliases so the new canonical
* /chains/<gram> URL still surfaces benches whose results[].slug is the
* old "ton" until everything downstream catches up. ChainHeadingsSummary
* and any other component reading r.slug uses `chainLabelForSlug` to
* resolve the alias to the registry's display label, so a stale "TON"
* row reads as "Gram" anywhere the chain registry can override it.
*
* Add a new alias when a chain rebrands; remove an alias once the
* caches and harnesses have rotated past it.
*/
export const CHAIN_SLUG_ALIASES: Record<string, string> = {
ton: "gram",
};

/** Map any slug to its canonical chain slug. Identity for known slugs,
* resolves a legacy slug to its current canonical via CHAIN_SLUG_ALIASES,
* returns the input lowercased for anything unknown. */
export function canonicalChainSlug(slug: string): string {
const lc = slug.toLowerCase();
return CHAIN_SLUG_ALIASES[lc] ?? lc;
}

/** Display label for a slug, resolving aliases against the chain
* registry. Returns null when neither the canonical nor the raw slug
* is registered, so callers can fall back to whatever local data they
* have (e.g. the bench result's own name field). */
* have (e.g. the bench result's own name field). Lives here (not in
* chain-aliases.ts) because it depends on the CHAIN_BY_SLUG registry
* which IS in this module. Callers inside the spec/data layer that
* would create a circular import should use `canonicalChainSlug`
* from `@/lib/chain-aliases` directly. */
export function chainLabelForSlug(slug: string): string | null {
const canon = canonicalChainSlug(slug);
return CHAIN_BY_SLUG.get(canon)?.label ?? null;
Expand Down
6 changes: 5 additions & 1 deletion src/lib/spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ import { cache } from "react";
import { unstable_cache } from "next/cache";
import type { Benchmark } from "@/types/benchmark";
import type { Spec } from "@/lib/spec-schema";
import { canonicalChainSlug } from "@/lib/chains";
// Imported from the isolated alias module (NOT @/lib/chains) to avoid
// a circular import: chains.ts → @/data/benchmarks → @/lib/spec. The
// cycle fails at module-eval time with "Cannot access 'X' before
// initialization" because both ends touch each other during ESM load.
import { canonicalChainSlug } from "@/lib/chain-aliases";
import { renderBenchmarkText } from "@/lib/bench-template";
import {
buildEditorial,
Expand Down
Loading