UNPKG

hardhat

Version:

Hardhat is an extensible developer tool that helps smart contract developers increase productivity by reliably bringing together the tools they want.

146 lines 6.11 kB
import path from "node:path"; import { readJsonFile, writeJsonFile, FileNotFoundError, } from "@nomicfoundation/hardhat-utils/fs"; import { getCacheDir } from "@nomicfoundation/hardhat-utils/global-dir"; import { isObject } from "@nomicfoundation/hardhat-utils/lang"; import { getRequest } from "@nomicfoundation/hardhat-utils/request"; import debug from "debug"; const log = debug("hardhat:util:banner-manager"); export const BANNER_CONFIG_URL = "https://raw.githubusercontent.com/NomicFoundation/hardhat/refs/heads/main/banner-config-v3.json"; export const BANNER_CACHE_FILE_NAME = "banner-config-v3.json"; export class BannerManager { static #instance; #bannerConfig; #lastDisplayTime; #lastRequestTime; #dispatcher; #print; constructor(bannerConfig, lastDisplayTime, lastRequestTime, dispatcher, print) { this.#bannerConfig = bannerConfig; this.#lastDisplayTime = lastDisplayTime; this.#lastRequestTime = lastRequestTime; this.#dispatcher = dispatcher; this.#print = print; } /** * Returns a global instance of BannerManager. * * @param options Options used for testing purposes. Only used in the first * invocation of this function, or after calling `resetInstance`. * @returns The current global instance of BannerManager. */ static async getInstance(options) { if (this.#instance === undefined) { log("Initializing BannerManager"); const { bannerConfig, lastDisplayTime, lastRequestTime } = await readCache(); this.#instance = new BannerManager(bannerConfig, lastDisplayTime, lastRequestTime, options?.testDispatcher, options?.print ?? console.log); } return this.#instance; } static resetInstance() { this.#instance = undefined; } /** * Displays a banner message, if any. * * @param timeout The timeout in milliseconds to wait for the banner display. */ async showBanner(timeout) { await this.#requestBannerConfig(timeout); if (this.#bannerConfig === undefined || !this.#bannerConfig.enabled || this.#bannerConfig.formattedMessages.length === 0) { log("Banner is disabled or no messages available."); return; } const { formattedMessages, minSecondsBetweenDisplays } = this.#bannerConfig; const timeSinceLastDisplay = Date.now() - this.#lastDisplayTime; if (timeSinceLastDisplay < minSecondsBetweenDisplays * 1000) { log(`Skipping banner display. Time since last display: ${timeSinceLastDisplay}ms`); return; } // select a random message from the formattedMessages array const randomIndex = Math.floor(Math.random() * formattedMessages.length); const message = formattedMessages[randomIndex]; this.#print(message); this.#lastDisplayTime = Date.now(); await writeCache({ bannerConfig: this.#bannerConfig, lastDisplayTime: this.#lastDisplayTime, lastRequestTime: this.#lastRequestTime, }); } async #requestBannerConfig(timeout) { if (this.#bannerConfig !== undefined) { const timeSinceLastRequest = Date.now() - this.#lastRequestTime; if (timeSinceLastRequest < this.#bannerConfig.minSecondsBetweenRequests * 1000) { log(`Skipping banner config request. Time since last request: ${timeSinceLastRequest}ms`); return; } } try { const response = await getRequest(BANNER_CONFIG_URL, undefined, this.#dispatcher ?? { timeout }); const bannerConfig = await response.body.json(); if (!this.#isBannerConfig(bannerConfig)) { log(`Invalid banner config received:`, bannerConfig); return; } this.#bannerConfig = bannerConfig; this.#lastRequestTime = Date.now(); await writeCache({ bannerConfig: this.#bannerConfig, lastDisplayTime: this.#lastDisplayTime, lastRequestTime: this.#lastRequestTime, }); } catch (error) { log(`Error requesting banner config: ${error instanceof Error ? error.message : JSON.stringify(error)}`); } } #isBannerConfig(value) { if (!isObject(value)) { return false; } return (Object.getOwnPropertyNames(value).length === 4 && "enabled" in value && typeof value.enabled === "boolean" && "formattedMessages" in value && Array.isArray(value.formattedMessages) && value.formattedMessages.every((message) => typeof message === "string") && "minSecondsBetweenDisplays" in value && typeof value.minSecondsBetweenDisplays === "number" && "minSecondsBetweenRequests" in value && typeof value.minSecondsBetweenRequests === "number"); } } async function readCache() { const cacheDir = await getCacheDir(); const bannerCacheFilePath = path.join(cacheDir, BANNER_CACHE_FILE_NAME); try { return await readJsonFile(bannerCacheFilePath); } catch (error) { if (error instanceof FileNotFoundError) { log("No banner cache file found, using defaults"); } else { log(`Error reading cache file: ${error instanceof Error ? error.message : JSON.stringify(error)}`); } return { bannerConfig: undefined, lastDisplayTime: 0, lastRequestTime: 0, }; } } async function writeCache(cache) { const cacheDir = await getCacheDir(); const bannerCacheFilePath = path.join(cacheDir, BANNER_CACHE_FILE_NAME); try { await writeJsonFile(bannerCacheFilePath, cache); } catch (error) { log(`Error writing cache file: ${error instanceof Error ? error.message : JSON.stringify(error)}`); } } //# sourceMappingURL=banner-manager.js.map