diff --git a/clients/js/README.md b/clients/js/README.md index b32192b8e..09683a600 100644 --- a/clients/js/README.md +++ b/clients/js/README.md @@ -86,7 +86,7 @@ yarn starship start # test yarn starship:test -# watch +# watch yarn starship:watch ``` @@ -205,7 +205,7 @@ client.undeploy(); client.teardown(); ``` -## StarshipJS Usage +## StarshipJS Usage [`StarshipJS`](https://github.com/cosmology-tech/starship/tree/main/clients/js/packages/starshipjs) is a utility library that provides helpers to leverage [Starship](https://github.com/cosmology-tech/starship)'s internal chain registry, emulating the style of code used in projects like [cosmos-kit](https://github.com/cosmology-tech/cosmos-kit). @@ -220,16 +220,41 @@ import { join } from 'path'; // Path to your YAML configuration file const configFile = join(__dirname, 'your-config.yaml'); -// Set the configuration file in StarshipJS -ConfigContext.setConfigFile(configFile); +// using init for init the config and a default connected registry fetcher. +await ConfigContext.init(configFile); + ``` ### Registry +Using init for init the config and pass an optional customized registry fetcher. + ```js import { useRegistry, ConfigContext } from 'starshipjs'; +import { join } from 'path'; + +// Path to your YAML configuration file +const configFile = join(__dirname, 'your-config.yaml'); + +const fetcher = new ChainRegistryFetcher({ + // your own options +}); + +await ConfigContext.init(configFile, fetcher); +``` + +Or use `useRegistry` to get a registry fetcher. + +```js +import { useRegistry, ConfigContext } from 'starshipjs'; +import { join } from 'path'; + +// Path to your YAML configuration file +const configFile = join(__dirname, 'your-config.yaml'); + +const fetcher = await useRegistry(configFile); -ConfigContext.setRegistry(await useRegistry(Config.configFile)); +await ConfigContext.init(configFile, fetcher); ``` ## Chain Info diff --git a/clients/js/packages/starshipjs/README.md b/clients/js/packages/starshipjs/README.md index 050c30a24..1659905f9 100644 --- a/clients/js/packages/starshipjs/README.md +++ b/clients/js/packages/starshipjs/README.md @@ -92,7 +92,7 @@ yarn starship start-ports yarn starship stop ``` -## Using the Client +## Using the Client StarshipJS is a utility library that provides helpers to leverage [Starship](https://github.com/cosmology-tech/starship)'s internal chain registry, emulating the style of code used in projects like [cosmos-kit](https://github.com/cosmology-tech/cosmos-kit). @@ -107,16 +107,41 @@ import { join } from 'path'; // Path to your YAML configuration file const configFile = join(__dirname, 'your-config.yaml'); -// Set the configuration file in StarshipJS -ConfigContext.setConfigFile(configFile); +// using init for init the config and a default connected registry fetcher. +await ConfigContext.init(configFile); + ``` ### Registry +Using init for init the config and pass an optional customized registry fetcher. + +```js +import { useRegistry, ConfigContext } from 'starshipjs'; +import { join } from 'path'; + +// Path to your YAML configuration file +const configFile = join(__dirname, 'your-config.yaml'); + +const fetcher = new ChainRegistryFetcher({ + // your own options +}); + +await ConfigContext.init(configFile, fetcher); +``` + +Or use `useRegistry` to get a registry fetcher. + ```js import { useRegistry, ConfigContext } from 'starshipjs'; +import { join } from 'path'; + +// Path to your YAML configuration file +const configFile = join(__dirname, 'your-config.yaml'); + +const fetcher = await useRegistry(configFile); -ConfigContext.setRegistry(await useRegistry(Config.configFile)); +await ConfigContext.init(configFile, fetcher); ``` ## Chain Info diff --git a/clients/js/packages/starshipjs/__tests__/config.test.ts b/clients/js/packages/starshipjs/__tests__/config.test.ts new file mode 100644 index 000000000..5508c2291 --- /dev/null +++ b/clients/js/packages/starshipjs/__tests__/config.test.ts @@ -0,0 +1,25 @@ +import path from "path"; + +import { Config, ConfigContext } from "../src/config"; +import { ChainRegistryFetcher } from "@chain-registry/client"; + +// it's more recommended to use ConfigContext.init to set the config file and registry. +it("1. throws without init;\n 2. init the setup and gets config;\n 3. throws when double init;\n", async () => { + expect(() => ConfigContext.registry).toThrow(); + expect(() => ConfigContext.configFile).toThrow(); + + const file = path.join(__dirname, "../../../__fixtures__", "config.yaml"); + + // for unit test, only setup a chain registry fetcher without fetching. + await Config.init(file, new ChainRegistryFetcher()); + + const registry = ConfigContext.registry; + const configFile = ConfigContext.configFile; + + expect(registry).toBeInstanceOf(ChainRegistryFetcher); + expect(configFile).toBe(file); + + expect( + async () => await ConfigContext.init(file, new ChainRegistryFetcher()) + ).rejects.toThrow(); +}); diff --git a/clients/js/packages/starshipjs/__tests__/legacy.test.ts b/clients/js/packages/starshipjs/__tests__/legacy.test.ts new file mode 100644 index 000000000..e2eafee49 --- /dev/null +++ b/clients/js/packages/starshipjs/__tests__/legacy.test.ts @@ -0,0 +1,29 @@ +import path from "path"; + +import { Config, ConfigContext } from "../src/config"; +import { ChainRegistryFetcher } from "@chain-registry/client"; + +// people can still use legacy ConfigContext to set the config file and registry. +it("1. throws without init;\n 2. throws only init partially;\n 3. init the setup and gets config;\n 4. throws when double init;\n", async () => { + expect(() => ConfigContext.registry).toThrow(); + expect(() => ConfigContext.configFile).toThrow(); + + const file = path.join(__dirname, "../../../__fixtures__", "config.yaml"); + + ConfigContext.setConfigFile(file); + + expect(() => ConfigContext.registry).toThrow(); + expect(() => ConfigContext.configFile).toThrow(); + + ConfigContext.setRegistry(new ChainRegistryFetcher()); + + const registry = ConfigContext.registry; + const configFile = ConfigContext.configFile; + + expect(registry).toBeInstanceOf(ChainRegistryFetcher); + expect(configFile).toBe(file); + + expect( + async () => await ConfigContext.init(file, new ChainRegistryFetcher()) + ).rejects.toThrow(); +}); diff --git a/clients/js/packages/starshipjs/src/config.ts b/clients/js/packages/starshipjs/src/config.ts index db0358e3c..d9e34b00c 100644 --- a/clients/js/packages/starshipjs/src/config.ts +++ b/clients/js/packages/starshipjs/src/config.ts @@ -1,25 +1,91 @@ -import { ChainRegistryFetcher } from '@chain-registry/client'; +import { ChainRegistryFetcher } from "@chain-registry/client"; + +import { useRegistry } from "./hooks"; + export class Config { - private static instance: Config; + // keep instantiation private to enforce singletone + private constructor() {} public registry?: ChainRegistryFetcher; public configFile?: string; + private isConfigInitialized = false; + private isRegistryInitialized = false; - // keep instantiation private to enforce singletone - private constructor() {} + private static instance: Config; + + setConfigFile(configFile: string) { + this.configFile = configFile; + this.isConfigInitialized = true; + } + + setRegistry(registry: ChainRegistryFetcher) { + this.registry = registry; + this.isRegistryInitialized = true; + } + + private get isInitialized() { + return this.isConfigInitialized && this.isRegistryInitialized; + } + + // init config with a config file and an optional registry fetcher + // if no registry fetcher is provided, it will use the default registry fetcher + // by enforcing the use of the init method, we can ensure that the config is initialized + public static async init( + configFile: string, + registryFetcher?: ChainRegistryFetcher + ) { + if (Config.instance && Config.instance.isInitialized) { + throw new Error("Config is already initialized."); + } + + const fetcher = registryFetcher ?? (await useRegistry(configFile)); + + Config.instance = new Config(); + Config.instance.setConfigFile(configFile); + Config.instance.setRegistry(fetcher); + } public static getInstance(): Config { + if (!Config.instance || !Config.instance.isInitialized) { + throw new Error("Config's not initialized."); + } + + return Config.instance; + } + + /** + * set the config file path + * @param configFile + * @depracated it's not recommended to set the configFile directly. Use init instead. + */ + public static setConfigFile(configFile: string) { if (!Config.instance) { Config.instance = new Config(); } - return Config.instance; + + Config.instance.setConfigFile(configFile); } - setConfigFile(configFile: string) { - this.configFile = configFile; + /** + * set the chain registry fetcher + * @param registry + * @depracated it's not recommended to set the registry directly. Use init instead. + */ + public static setRegistry(registry: ChainRegistryFetcher) { + if (!Config.instance) { + Config.instance = new Config(); + } + + Config.instance.setRegistry(registry); } - setRegistry(registry: ChainRegistryFetcher) { - this.registry = registry; + public static get configFile() { + // use getInstance to ensure that the config is initialized. + return Config.getInstance().configFile; + } + + public static get registry() { + // use getInstance to ensure that the config is initialized. + return Config.getInstance().registry; } } @@ -27,7 +93,7 @@ export interface ChainConfig { registry: { ports: { rest: number; - } + }; }; chains: Array<{ id: string; @@ -43,4 +109,4 @@ export interface ChainConfig { }>; } -export const ConfigContext = Config.getInstance(); \ No newline at end of file +export const ConfigContext = Config; \ No newline at end of file diff --git a/clients/js/packages/starshipjs/src/hooks.ts b/clients/js/packages/starshipjs/src/hooks.ts index 88e93e821..32db443c4 100644 --- a/clients/js/packages/starshipjs/src/hooks.ts +++ b/clients/js/packages/starshipjs/src/hooks.ts @@ -3,7 +3,7 @@ import fs from 'fs'; import yaml from 'js-yaml'; import fetch from 'node-fetch'; -import { ChainConfig, ConfigContext } from './config'; +import { type ChainConfig, ConfigContext } from './config'; export const useRegistry = async (configFile: string): Promise => { const config = yaml.load(fs.readFileSync(configFile, 'utf8')) as ChainConfig;