UNPKG

@fuel-infrastructure/fuel-hyperlane-registry

Version:

A collection of configs, artifacts, and schemas for Hyperlane

173 lines (172 loc) 7.32 kB
import fs from 'fs'; import path from 'path'; import { parse as yamlParse } from 'yaml'; import { CHAIN_FILE_REGEX, SCHEMA_REF, WARP_ROUTE_CONFIG_FILE_REGEX, WARP_ROUTE_DEPLOY_FILE_REGEX, } from '../consts.js'; import { ChainAddressesSchema } from '../types.js'; import { toYamlString } from '../utils.js'; import { RegistryType, } from './IRegistry.js'; import { SynchronousRegistry } from './SynchronousRegistry.js'; import { warpRouteConfigPathToId, warpRouteDeployConfigPathToId } from './warp-utils.js'; /** * A registry that uses a local file system path as its data source. * Requires file system access so it cannot be used in the browser. */ export class FileSystemRegistry extends SynchronousRegistry { type = RegistryType.FileSystem; constructor(options) { super(options); } getUri(itemPath) { if (!itemPath) return super.getUri(); return path.join(this.uri, itemPath); } /** * Retrieves filepaths for chains, warp core, and warp deploy configs */ listRegistryContent() { if (this.listContentCache) return this.listContentCache; const chainFileList = this.listFiles(path.join(this.uri, this.getChainsPath())); const chains = {}; for (const filePath of chainFileList) { const matches = filePath.match(CHAIN_FILE_REGEX); if (!matches) continue; const [_, chainName, fileName] = matches; chains[chainName] ??= {}; // @ts-ignore allow dynamic key assignment chains[chainName][fileName] = filePath; } const warpRoutes = {}; const warpRouteFiles = this.listFiles(path.join(this.uri, this.getWarpRoutesPath())); for (const filePath of warpRouteFiles) { if (!WARP_ROUTE_CONFIG_FILE_REGEX.test(filePath)) continue; const routeId = warpRouteConfigPathToId(filePath); warpRoutes[routeId] = filePath; } const warpDeployConfig = {}; const warpDeployFiles = this.listFiles(path.join(this.uri, this.getWarpRoutesPath())); for (const filePath of warpDeployFiles) { if (!WARP_ROUTE_DEPLOY_FILE_REGEX.test(filePath)) continue; const routeId = warpRouteDeployConfigPathToId(filePath); warpDeployConfig[routeId] = filePath; } return (this.listContentCache = { chains, deployments: { warpRoutes, warpDeployConfig } }); } getMetadata() { if (this.metadataCache) return this.metadataCache; const chainMetadata = {}; const repoContents = this.listRegistryContent(); for (const [chainName, chainFiles] of Object.entries(repoContents.chains)) { if (!chainFiles.metadata) continue; const data = fs.readFileSync(chainFiles.metadata, 'utf8'); chainMetadata[chainName] = yamlParse(data); } return (this.metadataCache = chainMetadata); } getAddresses() { if (this.addressCache) return this.addressCache; const chainAddresses = {}; const repoContents = this.listRegistryContent(); for (const [chainName, chainFiles] of Object.entries(repoContents.chains)) { if (!chainFiles.addresses) continue; const data = fs.readFileSync(chainFiles.addresses, 'utf8'); chainAddresses[chainName] = ChainAddressesSchema.parse(yamlParse(data)); } return (this.addressCache = chainAddresses); } removeChain(chainName) { const chainFiles = this.listRegistryContent().chains[chainName]; super.removeChain(chainName); this.removeFiles(Object.values(chainFiles)); } addWarpRoute(config, options) { let { configPath } = this.getWarpRoutesArtifactPaths(config, options); configPath = path.join(this.uri, configPath); this.createFile({ filePath: configPath, data: toYamlString(config, SCHEMA_REF) }); } addWarpRouteConfig(warpConfig, fileName) { const filePath = path.join(this.uri, this.getWarpRoutesPath(), fileName); this.createFile({ filePath, data: toYamlString(warpConfig) }); } listFiles(dirPath) { if (!fs.existsSync(dirPath)) return []; const entries = fs.readdirSync(dirPath, { withFileTypes: true }); const filePaths = entries.map((entry) => { const fullPath = path.join(dirPath, entry.name); return entry.isDirectory() ? this.listFiles(fullPath) : fullPath; }); return filePaths.flat(); } createOrUpdateChain(chain) { if (!chain.metadata && !chain.addresses) throw new Error(`Chain ${chain.chainName} must have metadata or addresses, preferably both`); const currentChains = this.listRegistryContent(); if (!currentChains.chains[chain.chainName]) { this.logger.debug(`Chain ${chain.chainName} not found in registry, adding it now`); } if (chain.metadata) { this.createChainFile(chain.chainName, 'metadata', chain.metadata, this.getMetadata(), SCHEMA_REF); } if (chain.addresses) { this.createChainFile(chain.chainName, 'addresses', chain.addresses, this.getAddresses()); } } createChainFile(chainName, fileName, data, cache, prefix) { const filePath = path.join(this.uri, this.getChainsPath(), chainName, `${fileName}.yaml`); const currentChains = this.listRegistryContent().chains; currentChains[chainName] ||= {}; currentChains[chainName][fileName] = filePath; cache[chainName] = data; this.createFile({ filePath, data: toYamlString(data, prefix) }); } createFile(file) { const dirPath = path.dirname(file.filePath); if (!fs.existsSync(dirPath)) fs.mkdirSync(dirPath, { recursive: true, }); fs.writeFileSync(file.filePath, file.data); } removeFiles(filePaths) { for (const filePath of filePaths) { fs.unlinkSync(filePath); } const parentDir = path.dirname(filePaths[0]); if (fs.readdirSync(parentDir).length === 0) { fs.rmdirSync(parentDir); } } getWarpRoutesForIds(ids) { const warpRoutes = this.listRegistryContent().deployments.warpRoutes; return this.readConfigsForIds(ids, warpRoutes); } getWarpDeployConfigForIds(ids) { const warpDeployConfig = this.listRegistryContent().deployments.warpDeployConfig; return this.readConfigsForIds(ids, warpDeployConfig); } /** * Reads config files for the given WarpRouteIds. * @param ids - The WarpRouteIds to read configs for. * @param configURIs - A mapping of WarpRouteIds to file paths where the configs are stored. * @returns An array of config objects. */ readConfigsForIds(ids, configURIs) { const configs = []; for (const [id, filePath] of Object.entries(configURIs)) { if (!ids.includes(id)) continue; const data = fs.readFileSync(filePath, 'utf8'); configs.push(yamlParse(data)); } return configs; } }