Files
CurrenciCombo/src/services/http.ts
Devin AI 007c79d7a9 feat(portal): wire DashboardPage to live Chain-138 RPC + SolaceScan Explorer
- Add services/{http,chain138,explorer,proxmox,dbisCore} + hooks/{useLiveChain,useOnChainBalances}
- Add BackendStatusBar + LiveNetworkPanel components on DashboardPage
- Overlay on-chain META balance on account rows carrying a walletAddress
- Normalize EIP-55 checksum in chain138.getNativeBalance so hand-typed
  sample custody addresses (e.g. 0x742d35Cc...bD38) don't silently drop
  out of the balance map
- Default RPC: https://rpc.d-bis.org (user-preferred gateway)
- proxmox.ts stays mocked (CF-Access, needs BFF); dbisCore.ts stays
  mocked (no public deployment yet)

Co-Authored-By: Nakamoto, S <defi@defi-oracle.io>
2026-04-19 00:33:46 +00:00

58 lines
1.8 KiB
TypeScript

/**
* Thin fetch wrapper with timeout + JSON handling + typed errors.
* Keep this dependency-free so every service can share it.
*/
export class HttpError extends Error {
readonly status: number;
readonly statusText: string;
readonly url: string;
readonly body?: unknown;
constructor(status: number, statusText: string, url: string, body?: unknown) {
super(`HTTP ${status} ${statusText} (${url})`);
this.name = 'HttpError';
this.status = status;
this.statusText = statusText;
this.url = url;
this.body = body;
}
}
export interface HttpOptions extends Omit<RequestInit, 'body'> {
/** Request body — automatically JSON-stringified when an object. */
body?: unknown;
/** Abort the request after N ms. Default 10000. */
timeoutMs?: number;
}
export async function httpJson<T>(url: string, opts: HttpOptions = {}): Promise<T> {
const { body, timeoutMs = 10_000, headers, ...rest } = opts;
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);
try {
const res = await fetch(url, {
...rest,
signal: controller.signal,
headers: {
Accept: 'application/json',
...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),
...headers,
},
body: body === undefined ? undefined : typeof body === 'string' ? body : JSON.stringify(body),
});
if (!res.ok) {
let parsed: unknown;
try { parsed = await res.json(); } catch { parsed = await res.text().catch(() => undefined); }
throw new HttpError(res.status, res.statusText, url, parsed);
}
const ct = res.headers.get('content-type') ?? '';
if (ct.includes('application/json')) return (await res.json()) as T;
return (await res.text()) as unknown as T;
} finally {
clearTimeout(timer);
}
}