@hyperlane-xyz/registry
Version:
A collection of configs, artifacts, and schemas for Hyperlane
150 lines (149 loc) • 4.53 kB
JavaScript
import { RegistryType, } from './IRegistry.js';
export class HttpError extends Error {
status;
body;
constructor(message, status, body = null) {
super(message);
this.name = 'HttpError';
this.status = status;
this.body = body;
}
}
export class HttpClientRegistry {
baseUrl;
type = RegistryType.Http;
uri;
unimplementedMethods = new Set([
'getUri',
'getChainLogoUri',
'addChain',
'removeChain',
'addWarpRoute',
'getWarpDeployConfig',
'getWarpDeployConfigs',
'addWarpRouteConfig',
'merge',
]);
constructor(baseUrl = 'http://localhost:3001') {
this.baseUrl = baseUrl;
this.uri = baseUrl;
}
getMetadata() {
return this.fetchJson('/metadata');
}
getAddresses() {
return this.fetchJson('/addresses');
}
getUri(_itemPath) {
throw new Error('Method not implemented.');
}
listRegistryContent() {
return this.fetchJson('/list-registry-content');
}
getChains() {
return this.fetchJson('/chains');
}
async getChainMetadata(chainName) {
try {
return await this.fetchJson(`/chain/${chainName}/metadata`);
}
catch (e) {
if (e instanceof HttpError && e.status === 404) {
return null;
}
throw e;
}
}
async getChainAddresses(chainName) {
try {
return await this.fetchJson(`/chain/${chainName}/addresses`);
}
catch (e) {
if (e instanceof HttpError && e.status === 404) {
return null;
}
throw e;
}
}
async updateChain(update) {
await this.fetchJson(`/chain/${update.chainName}`, {
method: 'POST',
body: JSON.stringify({
metadata: update.metadata,
addresses: update.addresses,
}),
headers: {
'Content-Type': 'application/json',
},
});
}
getChainLogoUri(_chainName) {
throw new Error('Method not implemented.');
}
addChain(_chain) {
throw new Error('Method not implemented.');
}
removeChain(_chain) {
throw new Error('Method not implemented.');
}
getWarpRoute(routeId) {
return this.fetchJson(`/warp-route/core/${routeId}`);
}
getWarpRoutes(filter) {
const queryParams = new URLSearchParams();
if (filter?.symbol) {
queryParams.set('symbol', filter.symbol);
}
if (filter?.label) {
queryParams.set('label', filter.label);
}
return this.fetchJson(`/warp-route/core?${queryParams.toString()}`);
}
addWarpRoute(_config) {
throw new Error('Method not implemented.');
}
getWarpDeployConfig(routeId) {
return this.fetchJson(`/warp-route/deploy/${routeId}`);
}
getWarpDeployConfigs() {
throw new Error('Method not implemented.');
}
addWarpRouteConfig(_config, _options) {
throw new Error('Method not implemented.');
}
merge(_otherRegistry) {
throw new Error('Method not implemented.');
}
async fetchJson(endpoint, options = {}) {
const requestOptions = { ...options };
// Only add JSON content-type header if there's a body
if (options.body) {
requestOptions.headers = {
'Content-Type': 'application/json',
...options.headers,
};
}
const response = await fetch(`${this.baseUrl}${endpoint}`, requestOptions);
// Handle successful requests that have no content to parse.
if (response.status === 204) {
return null;
}
if (!response.ok) {
let errorBody = null;
let errorMessage = `HTTP Error: ${response.status} ${response.statusText}`;
try {
errorBody = await response.json();
// Use the server's detailed error message if available
if (errorBody?.message) {
errorMessage = errorBody.message;
}
}
catch (e) {
// Ignore if error body isn't valid JSON, use statusText instead.
}
// Throw the structured error
throw new HttpError(errorMessage, response.status, errorBody);
}
return response.json();
}
}