{"version":3,"sources":["../../src/cli/localNode.ts"],"sourcesContent":["import { ChildProcessWithoutNullStreams, spawn } from \"node:child_process\";\nimport kill from \"tree-kill\";\nimport { platform } from \"node:os\";\n\nimport { sleep } from \"../utils/helpers\";\n\n/**\n * Represents a local node for running a localnet environment.\n * This class provides methods to start, stop, and check the status of the localnet process.\n * It manages the lifecycle of the node process and ensures that it is operational before executing tests.\n * @group Implementation\n * @category CLI\n */\nexport class LocalNode {\n  readonly MAXIMUM_WAIT_TIME_SEC = 75;\n\n  readonly READINESS_ENDPOINT = \"http://127.0.0.1:8070/\";\n\n  showStdout: boolean = true;\n\n  process: ChildProcessWithoutNullStreams | null = null;\n\n  extraArgs: string[] = [];\n\n  constructor(args?: { showStdout?: boolean; extraArgs?: string[] }) {\n    this.showStdout = args?.showStdout ?? true;\n    this.extraArgs = args?.extraArgs ?? [];\n  }\n\n  /**\n   * Kills the current process and all its descendant processes.\n   *\n   * @returns {Promise<void>} A promise that resolves to true if the process was successfully killed.\n   * @throws {Error} If there is an error while attempting to kill the process.\n   * @group Implementation\n   * @category CLI\n   */\n  async stop(): Promise<void> {\n    await new Promise((resolve, reject) => {\n      if (!this.process?.pid) return;\n\n      /**\n       * Terminates the process associated with the given process ID.\n       *\n       * @param pid - The process ID of the process to be terminated.\n       * @param callback - A function that is called after the termination attempt is complete.\n       * @param callback.err - An error object if the termination failed; otherwise, null.\n       * @param callback.resolve - A boolean indicating whether the termination was successful.\n       * @group Implementation\n       * @category CLI\n       */\n      kill(this.process.pid, (err) => {\n        if (err) {\n          reject(err);\n        } else {\n          resolve(true);\n        }\n      });\n    });\n  }\n\n  /**\n   * Runs a localnet and waits for the process to be up.\n   * If the local node process is already running, it returns without starting the process.\n   *\n   * @returns {Promise<void>} A promise that resolves when the process is up.\n   * @group Implementation\n   * @category CLI\n   */\n  async run(): Promise<void> {\n    const nodeIsUp = await this.checkIfProcessIsUp();\n    if (nodeIsUp) {\n      return;\n    }\n    this.start();\n    await this.waitUntilProcessIsUp();\n  }\n\n  /**\n   * Starts the localnet by running the Aptos node with the specified command-line arguments.\n   *\n   * @returns {void}\n   *\n   * @throws {Error} If there is an issue starting the localnet.\n   * @group Implementation\n   * @category CLI\n   */\n  start(): void {\n    const cliCommand = \"npx\";\n    const cliArgs = [\n      \"aptos\",\n      \"node\",\n      \"run-localnet\",\n      \"--force-restart\",\n      \"--assume-yes\",\n      \"--with-indexer-api\",\n      ...this.extraArgs,\n    ];\n\n    const currentPlatform = platform();\n    const spawnConfig = {\n      env: { ...process.env, ENABLE_KEYLESS_DEFAULT: \"1\" },\n      ...(currentPlatform === \"win32\" && { shell: true }),\n    };\n\n    this.process = spawn(cliCommand, cliArgs, spawnConfig);\n\n    this.process.stdout?.on(\"data\", (data: any) => {\n      const str = data.toString();\n      // Print local node output log\n      if (this.showStdout) {\n        console.log(str);\n      }\n    });\n  }\n\n  /**\n   * Waits for the localnet process to be operational within a specified maximum wait time.\n   * This function continuously checks if the process is up and will throw an error if it fails to start.\n   *\n   * @returns Promise<boolean> - Resolves to true if the process is up, otherwise throws an error.\n   * @group Implementation\n   * @category CLI\n   */\n  async waitUntilProcessIsUp(): Promise<boolean> {\n    let operational = await this.checkIfProcessIsUp();\n    const start = Date.now() / 1000;\n    let last = start;\n\n    while (!operational && start + this.MAXIMUM_WAIT_TIME_SEC > last) {\n      await sleep(1000);\n\n      operational = await this.checkIfProcessIsUp();\n      last = Date.now() / 1000;\n    }\n\n    // If we are here it means something blocks the process to start.\n    // Might worth checking if another process is running on port 8080\n    if (!operational) {\n      throw new Error(\"Process failed to start\");\n    }\n\n    return true;\n  }\n\n  /**\n   * Checks if the localnet is up by querying the readiness endpoint.\n   *\n   * @returns Promise<boolean> - A promise that resolves to true if the localnet is up, otherwise false.\n   * @group Implementation\n   * @category CLI\n   */\n  async checkIfProcessIsUp(): Promise<boolean> {\n    try {\n      // Query readiness endpoint\n      const data = await fetch(this.READINESS_ENDPOINT);\n      if (data.status === 200) {\n        return true;\n      }\n      return false;\n    } catch {\n      return false;\n    }\n  }\n}\n"],"mappings":"yCAAA,OAAyC,SAAAA,MAAa,gBACtD,OAAOC,MAAU,YACjB,OAAS,YAAAC,MAAgB,KAWlB,IAAMC,EAAN,KAAgB,CAWrB,YAAYC,EAAuD,CAVnE,KAAS,sBAAwB,GAEjC,KAAS,mBAAqB,yBAE9B,gBAAsB,GAEtB,aAAiD,KAEjD,eAAsB,CAAC,EAGrB,KAAK,WAAaA,GAAM,YAAc,GACtC,KAAK,UAAYA,GAAM,WAAa,CAAC,CACvC,CAUA,MAAM,MAAsB,CAC1B,MAAM,IAAI,QAAQ,CAACC,EAASC,IAAW,CAChC,KAAK,SAAS,KAYnBC,EAAK,KAAK,QAAQ,IAAMC,GAAQ,CAC1BA,EACFF,EAAOE,CAAG,EAEVH,EAAQ,EAAI,CAEhB,CAAC,CACH,CAAC,CACH,CAUA,MAAM,KAAqB,CACR,MAAM,KAAK,mBAAmB,IAI/C,KAAK,MAAM,EACX,MAAM,KAAK,qBAAqB,EAClC,CAWA,OAAc,CAEZ,IAAMI,EAAU,CACd,QACA,OACA,eACA,kBACA,eACA,qBACA,GAAG,KAAK,SACV,EAEMC,EAAkBC,EAAS,EAC3BC,EAAc,CAClB,IAAK,CAAE,GAAG,QAAQ,IAAK,uBAAwB,GAAI,EACnD,GAAIF,IAAoB,SAAW,CAAE,MAAO,EAAK,CACnD,EAEA,KAAK,QAAUG,EAAM,MAAYJ,EAASG,CAAW,EAErD,KAAK,QAAQ,QAAQ,GAAG,OAASE,GAAc,CAC7C,IAAMC,EAAMD,EAAK,SAAS,EAEtB,KAAK,YACP,QAAQ,IAAIC,CAAG,CAEnB,CAAC,CACH,CAUA,MAAM,sBAAyC,CAC7C,IAAIC,EAAc,MAAM,KAAK,mBAAmB,EAC1CC,EAAQ,KAAK,IAAI,EAAI,IACvBC,EAAOD,EAEX,KAAO,CAACD,GAAeC,EAAQ,KAAK,sBAAwBC,GAC1D,MAAMC,EAAM,GAAI,EAEhBH,EAAc,MAAM,KAAK,mBAAmB,EAC5CE,EAAO,KAAK,IAAI,EAAI,IAKtB,GAAI,CAACF,EACH,MAAM,IAAI,MAAM,yBAAyB,EAG3C,MAAO,EACT,CASA,MAAM,oBAAuC,CAC3C,GAAI,CAGF,OADa,MAAM,MAAM,KAAK,kBAAkB,GACvC,SAAW,GAItB,MAAQ,CACN,MAAO,EACT,CACF,CACF","names":["spawn","kill","platform","LocalNode","args","resolve","reject","kill","err","cliArgs","currentPlatform","platform","spawnConfig","spawn","data","str","operational","start","last","sleep"]}