/** * Explorer data source: live indexer when up, else mock. */ import { MOCK_BLOCKS, MOCK_TRANSACTIONS, getBlock as mockGetBlock, getTransaction as mockGetTx, } from "@/lib/explorer-mock"; import type { MockBlock, MockTransaction } from "@/lib/explorer-types"; const INDEXER = process.env.NEXT_PUBLIC_INDEXER_URL?.trim() || "http://127.0.0.1:4091"; export type ExplorerSource = "live" | "mock"; async function tryFetch(path: string): Promise { try { const res = await fetch(`${INDEXER}${path}`, { next: { revalidate: 0 }, signal: AbortSignal.timeout(1500), }); if (!res.ok) return null; return (await res.json()) as T; } catch { return null; } } export async function listTransactions(): Promise<{ source: ExplorerSource; items: MockTransaction[]; }> { const data = await tryFetch<{ items: MockTransaction[] }>("/v1/transactions"); if (data?.items?.length) return { source: "live", items: data.items }; return { source: "mock", items: [...MOCK_TRANSACTIONS] }; } export async function listBlocks(): Promise<{ source: ExplorerSource; items: MockBlock[]; }> { const data = await tryFetch<{ items: MockBlock[] }>("/v1/blocks"); if (data?.items?.length) return { source: "live", items: data.items }; return { source: "mock", items: [...MOCK_BLOCKS] }; } export async function fetchTransaction( hash: string, ): Promise<{ source: ExplorerSource; tx: MockTransaction | null }> { const live = await tryFetch( `/v1/transactions/${encodeURIComponent(hash)}`, ); if (live && "hash" in live) return { source: "live", tx: live }; return { source: "mock", tx: mockGetTx(hash) ?? null }; } export async function fetchBlock( key: string, ): Promise<{ source: ExplorerSource; block: MockBlock | null }> { const live = await tryFetch( `/v1/blocks/${encodeURIComponent(key)}`, ); if (live && "height" in live) return { source: "live", block: live }; return { source: "mock", block: mockGetBlock(key) ?? null }; } export async function indexerHealth(): Promise { const h = await tryFetch<{ ok?: boolean }>("/health"); return Boolean(h?.ok); } export type HybridHealth = { ok?: boolean; mode?: string; privateLayer?: { ok?: boolean; chainId?: number; blockNumber?: number }; publicLayer?: { ok?: boolean; chainId?: number; blockNumber?: number }; sim?: { pendingAnchors?: number; postedAnchors?: number }; }; export async function fetchIndexerHybrid(): Promise { return tryFetch("/v1/hybrid"); }