Skip to content

SEC-359: Replace compare app with server-rendered RPC performance overview#9

Merged
smypmsa merged 90 commits into
mainfrom
feature/SEC-359-grafana-iframe
Jun 16, 2026
Merged

SEC-359: Replace compare app with server-rendered RPC performance overview#9
smypmsa merged 90 commits into
mainfrom
feature/SEC-359-grafana-iframe

Conversation

@smypmsa

@smypmsa smypmsa commented May 22, 2026

Copy link
Copy Markdown
Member

SEC-359

Summary

Replaces the legacy client-side compare app with a server-rendered RPC provider performance page sourced from the Grafana Cloud Prometheus API. Also migrates the codebase from JavaScript to TypeScript.

What it does

  • Server-rendered page (src/app/page.tsx, force-dynamic) fetches all 8 chains' provider metrics in parallel and passes them to a client component for instant protocol switching.
  • Data layer (src/lib/):
    • grafana.ts — server-only Prometheus client (instant + range queries), bearer GRAFANA_API_TOKEN, 180s fetch revalidate, 10s request timeout.
    • queries.ts — PromQL for per-provider/per-region p50/p95/p99 latency, success rate, and a p95 trend series (successes only; TEST_* providers excluded).
    • chain-data.ts — shapes per-chain provider lists; failed queries degrade to partial/error flags rather than fabricating values.
  • UI (src/components/RpcPerformance/): provider table with availability %, a p50/p95/p99 latency bar, a p95 sparkline, and a per-region p95 heatmap; protocol chips, a 24h/7d switcher, a share link, a "how ranking works" tooltip, and a route-level loading skeleton (loading.tsx). Providers are ranked by a combined speed × reliability score.
  • Freshness: a "Live" indicator refreshes the route every 3 minutes.

Removed

Legacy compare/injection routes (/compare-single, /compare-double, /injection-start, /injection-result-double), the client store, and their components and icons. next.config.js 301-redirects /compare-single and /compare-double to /, and /dashboard to the public Grafana dashboard; removed routes otherwise 404.

Security headers (next.config.js)

CSP (including frame-ancestors 'none'), HSTS, X-Content-Type-Options: nosniff, and Referrer-Policy: strict-origin-when-cross-origin. Vercel preview-toolbar allowances are gated to the preview environment only.

Config / deploy

  • .env.sample: GRAFANA_API_TOKEN (Grafana Cloud service account, Viewer role) and NEXT_PUBLIC_CLIENT_DOMAIN. Set both in Vercel for Production and Preview.
  • reactStrictMode: true.

Testing

npm run build, tsc --noEmit, and next lint all pass.

@vercel

vercel Bot commented May 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
chainstack-compare Ready Ready Preview, Comment Jun 16, 2026 7:32am

Request Review

@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR replaces the client-side RPC comparison UI with a server-rendered Grafana/Prometheus-backed dashboard: adds server Grafana/Prometheus helpers and queries, cached chain data aggregation, server async components (ChainTOC, ChainCard) with Suspense, redesigns the home page as an async server component, introduces a comprehensive metrics/scoring layer for provider ranking, and removes the old comparison pages and related client state/components.

Changes

Server-Side Performance Dashboard Implementation

Layer / File(s) Summary
Configuration, environment, and dependencies
.env.sample, package.json, next.config.js
Environment template adds GRAFANA_API_TOKEN and NEXT_PUBLIC_CLIENT_DOMAIN; package.json adds server-only dependency; Next.js config applies global CSP headers, redirects old comparison routes, and Grafana dashboard redirect.
Grafana Prometheus API client and PromQL builders
src/lib/grafana.js, src/lib/queries.js
Server-only Grafana datasource proxy helpers with auth, timeout, and revalidate caching; PromQL builders for quantiles, success rates, and trends; CHAINS metadata with public tokens.
Cached chain data aggregation and display formatting
src/lib/chain-data.js, src/lib/format.js
fetchChainData() orchestrates parallel Prometheus queries and aggregates per-provider/region metrics; formatting utilities for latency, percentages, region labels with disambiguation, and anchor IDs.
Home page conversion to server-side async architecture
src/app/page.js
Home page converts to async server component with force-dynamic revalidation; sequentially fetches all chain data and wraps RpcPerformancePage in Suspense boundary.
Client-side store deprecation
src/app/store/store.js
Client state store reduced to 'use client' directive with deprecation comment; all exports removed.
Chain performance card, visualization components, and loading states
src/components/Chain/ChainCard.js, src/components/Chain/BulletBar.js, src/components/Chain/Sparkline.js, src/components/Chain/ChainCardSkeleton.js
Async ChainCard renders provider metrics table with latency bars, region heatmap, and trend sparklines; BulletBar stacks p50/p95/p99; Sparkline renders SVG trend; skeleton provides loading placeholders.
Chain index and navigation components
src/components/ChainTOC.js, src/components/ChainTOCSkeleton.js
Async ChainTOC concurrently fetches chain data and renders navigation grid with leader metrics and color-coded latency; skeleton provides animated placeholders.
Performance ranking, scoring, and summary logic
src/components/RpcPerformance/metrics.js
Computes enriched provider data (quantiles in ms, availability %, tail-risk, regions, severity); scoring via grafanaScore() and sorting by reliability; multi-view summary generation for overview/latency/reliability/issues/regions.
RPC performance dashboard page and interactive controls
src/components/RpcPerformance/RpcPerformancePage.js, src/components/RpcPerformance/ProtocolChips.js, src/components/RpcPerformance/ProviderMetricsTable.js, src/components/RpcPerformance/TimeRangeSwitcher.js, src/components/RpcPerformance/TableSkeleton.js, src/components/RpcPerformance/brandColors.js, src/components/RpcPerformance/SortControl.js, src/components/RpcPerformance/UseCaseSwitcher.js, src/components/RpcPerformance/ViewTabs.js, src/components/RpcPerformance/ProtocolTabs.js, src/components/RpcPerformance/RegionFocusControl.js
Main dashboard component manages protocol/range URL state, enriches/scores/sorts providers, renders ranking summary, metrics table with heatmap, time-range switcher, share button, and Grafana link; subcomponents provide protocol/region/view selection, table skeleton loading, and brand color mapping.
Page layout, header, and decorative background components
src/app/layout.tsx, src/app/loading.js, src/components/Header/Header.js, src/components/PageBackground.js, src/components/ChunkyGrainBackground.js, src/components/HeroMesh.js, src/components/InfraBackground.js, src/components/LiveDot.js, src/components/DotGrid.js, src/components/Logo/Logo.js
Layout switches to Space_Mono font; loading page derives state from URL and localStorage; header becomes client component with hover tooltip; new background/mesh/grain components provide layered visual effects.
CSS design tokens and global typography
src/app/globals.css, src/styles/typography.js
Global CSS defines comprehensive color and typography design tokens; new utility classes for typography, backgrounds, and animations; typography module exports token map and style helper.
Grafana public API client and data parsing
src/lib/grafana-public.js
Public panel query client with 429 retry/backoff; parsers for instant and timeseries panel results; orchestrates sequential requests for p95/success/trend and merges into sorted provider rankings.
Mock data generation and Grafana probe endpoint
src/lib/mock-data.js, src/app/api/probe-panels/route.js
Mock data generates synthetic chain metrics with per-chain scaling; probe endpoint fetches Grafana dashboard metadata and concurrent panel queries with no-store caching.

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant HomeServer as Home (Server)
  participant FetchAllChains as fetchAllChains()
  participant FetchChainData as fetchChainData()
  participant GrafanaAPI as Grafana API
  participant RpcPerformance as RpcPerformancePage
  participant Browser

  Browser->>HomeServer: GET / (async server component)
  HomeServer->>FetchAllChains: await fetchAllChains(timeRange)
  loop For each CHAINS entry with delay
    FetchAllChains->>FetchChainData: fetchChainData(chain, timeRange)
    alt GRAFANA_API_TOKEN set
      FetchChainData->>GrafanaAPI: runPromQuery (p50/p95/p99 quantiles)
      GrafanaAPI-->>FetchChainData: result rows
      FetchChainData->>GrafanaAPI: runPromQuery (success rates)
      GrafanaAPI-->>FetchChainData: result rows
      FetchChainData->>GrafanaAPI: runPromRangeQuery (24h trend)
      GrafanaAPI-->>FetchChainData: timeseries
    else Token missing
      FetchChainData->>GrafanaAPI: fetchPublicChainData (public panels)
      GrafanaAPI-->>FetchChainData: parsed panels
    end
    FetchChainData-->>FetchAllChains: { providers, regions, leader, error }
  end
  FetchAllChains-->>HomeServer: allChainsData[]
  HomeServer->>RpcPerformance: <Suspense> RpcPerformancePage { allChainsData, timeRange }
  RpcPerformance->>RpcPerformance: enrichProviders(), computeScores(), sortByReliabilityThenLatency()
  RpcPerformance-->>Browser: Render table, summary, share/time-range controls
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐰 I hopped through code at break of dawn,
Grafana tokens freshly drawn,
Sparklines sing and tables hum,
Old client pages now are gone,
Server-side dashboard, bright as morn. 🌤️

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.91% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: replacing the interactive compare application with a server-rendered RPC performance overview.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/SEC-359-grafana-iframe

- Home page rewritten as server-rendered RPC provider overview via Grafana Cloud Prometheus API; ISR revalidate=60
- Streaming SSR: per-ChainCard + Headline + ChainTOC <Suspense> boundaries so cards stream as their PromQL resolves
- Bullet bars: p50/p95/p99 layered horizontal bars per provider, leader emerald, others slate
- Inline-SVG 24h sparkline per provider row, no chart library
- Regional heatmap per chain: provider × region p95 grid, HSL-tinted from chain-wide min/max
- Headline strip: fastest-right-now, most-p95-wins, tail-latency leader across all chains
- Mini-sparkline TOC: 4×2 chain grid with leader trend + anchor jumps
- lib/grafana.js: runPromQuery + runPromRangeQuery
- lib/chain-data.js: React.cache-wrapped per-chain fetcher, parallel queries, partial-data tolerant; TOC/Headline/Cards share results
- lib/queries.js: providerByRegionQuery(chain, q) for any quantile + providerTrendQuery for range data
- Route deletions: compare-single/, compare-double/, injection-start/, injection-result-double/ — all hit the backend's /scenarios/* SSRF surface; legacy URLs 301 to / in next.config.js
- next.config.js: dropped wide-open /api/* CORS; CSP with frame-ancestors 'none', HSTS, nosniff, Referrer-Policy
- store/store.js emptied; .env.sample drops NEXT_PUBLIC_BACKEND_APP_URL and adds GRAFANA_API_TOKEN
- Dead components removed: Bento, Faq, Performance, ProtocolIcon (+ SVGs), ResultCard, orphaned icons
- Subtitle: drop jargon and middle dot, cap line width
- Remove the dynamic headline stat line
- Per-provider success rate (sum by provider) replaces the chain aggregate;
  shown under each provider, amber below 99.9%
- Region codes shown as short country labels (US, DE, SG …) with full
  name in the column tooltip
- Merge the region heatmap into the provider table as right-hand columns,
  sticky provider column; no duplicated provider names
- Rename Trend column to "p95, 24h"; fix trend query comment
@smypmsa
smypmsa marked this pull request as ready for review May 27, 2026 07:33
@smypmsa smypmsa changed the title SEC-359: Replace compare app with embedded Grafana dashboard SEC-359: Replace compare app with server-rendered RPC performance overview May 27, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.env.sample:
- Line 2: The env sample has spaces around the equals sign for assignments
(e.g., NEXT_PUBLIC_CLIENT_DOMAIN =), which can cause parsing issues; update the
assignment to remove spaces so it reads NEXT_PUBLIC_CLIENT_DOMAIN= and similarly
normalize other keys mentioned (line 8) to KEY= format throughout the file to
ensure valid .env syntax.

In `@next.config.js`:
- Line 26: Update the Referrer-Policy header value to a stricter setting to
reduce cross-origin data leakage: locate the header object with key
'Referrer-Policy' in next.config.js and change its value from
'no-referrer-when-downgrade' to 'strict-origin-when-cross-origin' (or a stricter
policy if desired), ensuring the header entry remains otherwise unchanged.

In `@src/lib/grafana.js`:
- Around line 20-23: The Grafana fetch call currently has no abort path; wrap
the request in an AbortController: create an AbortController before calling
fetch, pass controller.signal in the fetch options alongside headers:
authHeaders() and next: { revalidate }, start a timer (configurable timeout,
e.g. 3–5s) that calls controller.abort() on expiry, and clear the timer once the
response is received; also ensure any fetch errors from abort are
handled/translated appropriately where res is used so SSR doesn't hang.
- Around line 39-44: runPromRangeQuery currently passes start, end, and step
straight into fetchProm which can produce "undefined" query params; update
runPromRangeQuery to validate that start, end, and step are provided and not
undefined/null (and optionally coercible to valid values) before calling
fetchProm, and throw a clear error (e.g., TypeError or rejected Promise) if any
are missing; reference the runPromRangeQuery function and its call to
fetchProm('/api/v1/query_range', { query: promql, start, end, step }, opts) so
you enforce the checks there and only build/pass the params when valid.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 275bc1dd-78bd-4675-ac0f-7a4f46d89dbc

📥 Commits

Reviewing files that changed from the base of the PR and between aaf3404 and d2d9db9.

⛔ Files ignored due to path filters (25)
  • package-lock.json is excluded by !**/package-lock.json
  • src/components/ProtocolIcon/aptos.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/arbitrum.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/aurora.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/avalanche.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/base.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/bitcoin.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/bnb.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/cronos.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/ethereum.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/fantom.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/filecoin.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/fuse.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/gnosis.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/harmony.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/near.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/optimism.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/polygonPOS.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/polygonZkEvm.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/ronin.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/scroll.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/solana.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/starknet.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/tezos.svg is excluded by !**/*.svg
  • src/components/ProtocolIcon/zkSync.svg is excluded by !**/*.svg
📒 Files selected for processing (33)
  • .env.sample
  • next.config.js
  • package.json
  • src/app/compare-double/page.js
  • src/app/compare-single/page.js
  • src/app/injection-result-double/page.js
  • src/app/injection-start/page.js
  • src/app/page.js
  • src/app/store/store.js
  • src/components/Bento/Bento.js
  • src/components/Chain/BulletBar.js
  • src/components/Chain/ChainCard.js
  • src/components/Chain/ChainCardSkeleton.js
  • src/components/Chain/Sparkline.js
  • src/components/ChainTOC.js
  • src/components/ChainTOCSkeleton.js
  • src/components/Faq/FaqAccordion.js
  • src/components/Faq/FaqBasic.js
  • src/components/Faq/post.mdx
  • src/components/Header/Header.js
  • src/components/Icons/BarChartIcon.js
  • src/components/Icons/Customization.js
  • src/components/Icons/ExplainResultsIcon.js
  • src/components/Icons/GrafanaIcon.js
  • src/components/Icons/Profiling.js
  • src/components/Performance/Compare.js
  • src/components/Performance/Preformance.js
  • src/components/ProtocolIcon/ProtocolIcon.js
  • src/components/ResultCard/ResultCard.js
  • src/lib/chain-data.js
  • src/lib/format.js
  • src/lib/grafana.js
  • src/lib/queries.js
💤 Files with no reviewable changes (18)
  • src/components/Faq/post.mdx
  • src/components/Faq/FaqAccordion.js
  • src/components/Icons/Customization.js
  • src/components/Faq/FaqBasic.js
  • src/app/injection-start/page.js
  • src/components/Performance/Compare.js
  • src/components/Icons/GrafanaIcon.js
  • src/components/Icons/BarChartIcon.js
  • src/components/Bento/Bento.js
  • src/components/Performance/Preformance.js
  • src/components/Icons/ExplainResultsIcon.js
  • src/components/Icons/Profiling.js
  • src/components/ProtocolIcon/ProtocolIcon.js
  • src/components/Header/Header.js
  • src/components/ResultCard/ResultCard.js
  • src/app/compare-single/page.js
  • src/app/compare-double/page.js
  • src/app/injection-result-double/page.js

Comment thread .env.sample Outdated
Comment thread next.config.js Outdated
Comment thread src/lib/grafana.js Outdated
Comment thread src/lib/grafana.js Outdated
The CSP added in this PR blocked the Vercel toolbar (vercel.live + Pusher
websockets), so comments/live feedback couldn't load or be submitted on
preview deployments. Allowlist vercel.live and Pusher in script/style/font/
frame/connect-src, gated to VERCEL_ENV=preview so production stays locked down.
The latency table is w-full with auto layout, and the BulletBar column was
the only flexible one, so it absorbed all leftover width. Chains with fewer
region columns (e.g. TON) left more slack, stretching the bar and pushing the
p50/p95/p99 numbers further from the provider name. Add a flexible spacer
column before the region block so slack lands there instead, pinning every
data column to a consistent width across all chains and keeping the region
heatmap flush-right.
Each chain rendered its own auto-layout table, so column widths were computed
per-card from content: chains with long provider names (TON) pushed the bars
and numbers further right, and slack pooled unpredictably. The earlier spacer
column made it worse by parking all slack between p99 and the heatmap.

Switch to table-fixed with a generated colgroup: fixed widths on the metric
columns (provider/bar/sparkline/p50/p95/p99) so they're identical in every
card — bars start at the same x and numbers line up regardless of name length
(long names truncate) — and auto-width region columns that split the remaining
space, keeping the heatmap flush-right with no gap. Sparkline/p99 columns
collapse to 0 width on mobile in step with their hidden cells.
Only consumer was the deleted app/store/store.js.
Accidental npm package, never imported.
- add semantic color tokens (fg/tier/signal/accent/panel) + keyframes/animation
  + mono font to tailwind.config; 'panel' avoids collision with Wedges 'surface'
- convert components from inline styles to Tailwind classes
- replace 16 JS-driven hover handlers with hover: classes
- dynamic values (bar widths, brand row highlight) via CSS variables
- remove inline <style> keyframe blocks; delete lib/theme.js
- convert all 20 .js files to .ts/.tsx; add src/lib/types.ts with domain types
  (Chain, Provider, ChainData, EnrichedProvider, ScoredProvider, AvailTier)
- type the data pipeline (grafana/queries/chain-data), metrics, and all component props
- fix RootLayout props (was : any); type Prometheus responses, CSS-var styles
- tsconfig: target es2017 (Map/Set iteration under strict), drop stale .js includes
- passes strict type-check (noImplicitAny via strict) + lint clean
pnpm auto-created a build-approval stub with no 'packages:' field; pnpm 9 on
Vercel rejects it ('packages field missing or empty'). This is a single-package
repo, not a workspace. Deleted and gitignored.
…allbacks

- score = 1 / mean_region((1/p95_region) * successRate_region^3), computed per
  region then averaged then inverted — matches the compare dashboard exactly
- success rate is now per-region (queries: 'by (provider, source_region)'),
  carried as Provider.regionSuccess; display availability = mean of regions
- grafanaScore: unscoreable provider -> Infinity (ranks last), NO latency-only
  substitution (was a silent ranking change); partial-data banner still surfaces
- generateSummary: headline always names the actual #1 row (sorted[0]); no
  silent provider-swap. availTier is display-only now (docs corrected; no gating)
- fix copy: 'three regions' -> 'all monitored regions'; table header P95 range
  now reflects 24h/7d instead of hardcoded 24h
- drop dead range coercions ('?? 24h' / ': 24h') — type-guaranteed unreachable
The probes (compare-dashboard-functions Vercel cron) measure every 3 minutes
(*/3), but the page claimed 'Updated every minute' with a pulsing dot and never
auto-refreshed. Now:
- FreshnessIndicator: router.refresh() every 180s (pauses on hidden tab,
  refreshes on revisit) so the data actually updates on the probe cadence
- copy -> 'Live · updates every 3 min' (page + loading skeleton)
- align Grafana query cache 60s -> 180s to match the measurement cadence
It sorts by the single combined grafanaScore (reliability folded in via SR^3),
not a two-key reliability-then-latency sort. Name now matches behavior.
Prometheus labels carry tiers (Alchemy-Growth, Helius-Developer,
TonCenter-WithAPIKey). Add providerDisplayName: strip everything before the
first hyphen (generic — new tiers never break it) + a tiny casing-only map
(quicknode->QuickNode, drpc->dRPC). Display-only via EnrichedProvider.displayName;
raw label stays the row identity/key, so no silent tier-merging.
- layout: use metadataBase + relative OG URL instead of string concat
  (avoids "undefined/og-image.png" when NEXT_PUBLIC_CLIENT_DOMAIN unset)
- RpcPerformancePage: move localStorage write out of useMemo into useEffect
- next.config: enable reactStrictMode (verified no double-render issues)
…lpers

- add isNum type guard; drop repeated Number.isFinite + as-number casts
  across metrics.ts and ProviderMetricsTable.tsx
- merge BRAND + LOGO_MAP into one CHAIN_BRAND record (adding a chain now
  touches one display entry instead of two files)
- extract resolveProtocol + rangeQuery into lib/url-params.ts; reuse in
  RpcPerformancePage, TimeRangeSwitcher, and loading.tsx

No behavior change: tsc, lint, and production build all pass.
depcheck + grep confirmed unused (no source imports):
- @iconicicons/react (icons now from @phosphor-icons/react)
- react-google-charts, react-type-animation (old compare app)

MDX stack removed — no .mdx files, no mdx-components, never wired into
pageExtensions, so it was inert:
- @mdx-js/loader, @mdx-js/react, @next/mdx
- drop withMDX wrapper from next.config.js

Build, tsc, and lint pass; bundle size unchanged (deps weren't shipped).
@smypmsa smypmsa added the enhancement New feature or request label Jun 16, 2026
@smypmsa smypmsa self-assigned this Jun 16, 2026
@smypmsa
smypmsa merged commit 2a534c0 into main Jun 16, 2026
3 checks passed
@smypmsa
smypmsa deleted the feature/SEC-359-grafana-iframe branch June 17, 2026 08:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants