fuels
Version:
Fuel TS SDK
1 lines • 73.9 kB
Source Map (JSON)
{"version":3,"sources":["../src/cli.ts","../src/cli/utils/logger.ts","../src/cli/commands/build/generateTypes.ts","../src/cli/config/forcUtils.ts","../src/cli/templates/index.ts","../src/cli/templates/index.hbs","../src/cli/commands/deploy/deployContracts.ts","../src/cli/commands/deploy/createWallet.ts","../src/cli/commands/deploy/getDeployConfig.ts","../src/cli/commands/deploy/deployPredicates.ts","../src/cli/commands/deploy/deployScripts.ts","../src/cli/commands/deploy/saveContractIds.ts","../src/cli/commands/deploy/savePredicateFiles.ts","../src/cli/commands/deploy/saveScriptFiles.ts","../src/cli/commands/deploy/index.ts","../src/cli/commands/dev/autoStartFuelCore.ts","../src/test-utils.ts","../src/cli/commands/build/buildSwayProgram.ts","../src/cli/commands/build/forcHandlers.ts","../src/cli/commands/build/buildSwayPrograms.ts","../src/cli/commands/build/index.ts","../src/cli/commands/dev/index.ts","../src/cli/config/loadConfig.ts","../src/cli-utils.ts","../src/cli/config/validateConfig.ts","../src/cli/commands/withConfig.ts","../src/cli/commands/init/index.ts","../src/cli/templates/fuels.config.ts","../src/cli/templates/fuels.config.hbs","../src/cli/commands/node/index.ts","../src/cli/commands/withBinaryPaths.ts","../src/cli/commands/withProgram.ts"],"sourcesContent":["import { configureCliOptions as configureTypegenCliOptions } from '@fuel-ts/abi-typegen/cli';\nimport { versions } from '@fuel-ts/versions';\nimport { runVersions } from '@fuel-ts/versions/cli';\nimport { Command, Option } from 'commander';\n\nimport { build } from './cli/commands/build';\nimport { deploy } from './cli/commands/deploy';\nimport { dev } from './cli/commands/dev';\nimport { init } from './cli/commands/init';\nimport { node } from './cli/commands/node';\nimport { withBinaryPaths } from './cli/commands/withBinaryPaths';\nimport { withConfig } from './cli/commands/withConfig';\nimport { withProgram } from './cli/commands/withProgram';\nimport { Commands } from './cli/types';\nimport { configureLogging } from './cli/utils/logger';\n\nexport const onPreAction = (command: Command) => {\n const opts = command.opts();\n configureLogging({\n isDebugEnabled: opts.debug,\n isLoggingEnabled: !opts.silent,\n });\n};\n\nexport const configureCli = () => {\n const program = new Command();\n\n program.name('fuels');\n\n program.option('-D, --debug', 'Enables verbose logging', false);\n program.option('-S, --silent', 'Omit output messages', false);\n\n program.version(versions.FUELS, '-v, --version', 'Output the version number');\n program.helpOption('-h, --help', 'Display help');\n program.helpCommand('help [command]', 'Display help for command');\n\n program.enablePositionalOptions(true);\n\n program.hook('preAction', onPreAction);\n\n /**\n * Defining local commands\n */\n\n const pathOption = new Option('--path <path>', 'Path to project root').default(process.cwd());\n\n let command: Command;\n\n (command = program.command(Commands.init))\n .description('Create a sample `fuel.config.ts` file')\n .addOption(pathOption)\n .option('-w, --workspace <path>', 'Relative dir path to Forc workspace')\n .addOption(\n new Option(`-c, --contracts [paths...]`, `Relative paths to Contracts`).conflicts('workspace')\n )\n .addOption(\n new Option(`-s, --scripts [paths...]`, `Relative paths to Scripts`).conflicts('workspace')\n )\n .addOption(\n new Option(`-p, --predicates [paths...]`, `Relative paths to Predicates`).conflicts(\n 'workspace'\n )\n )\n .requiredOption('-o, --output <path>', 'Relative dir path for Typescript generation output')\n .option('--forc-path <path>', 'Path to the `forc` binary')\n .option('--fuel-core-path <path>', 'Path to the `fuel-core` binary')\n .option('--auto-start-fuel-core', 'Auto-starts a `fuel-core` node during `dev` command')\n .option(\n '--fuel-core-port <port>',\n 'Port to use when starting a local `fuel-core` node for dev mode'\n )\n .action(withProgram(command, Commands.init, init));\n\n (command = program.command(Commands.dev))\n .description('Start a Fuel node with hot-reload capabilities')\n .addOption(pathOption)\n .action(withConfig(command, Commands.dev, dev));\n\n (command = program.command(Commands.node))\n .description('Start a Fuel node using project configs')\n .addOption(pathOption)\n .action(withConfig(command, Commands.node, node));\n\n (command = program.command(Commands.build))\n .description('Build Sway programs and generate Typescript for them')\n .addOption(pathOption)\n .option(\n '-d, --deploy',\n 'Deploy contracts after build (auto-starts a `fuel-core` node if needed)'\n )\n .action(withConfig(command, Commands.build, build));\n\n (command = program.command(Commands.deploy))\n .description('Deploy contracts to the Fuel network')\n .addOption(pathOption)\n .action(withConfig(command, Commands.deploy, deploy));\n\n /**\n * Routing external commands from sub-packages' CLIs\n */\n\n // Typegen\n configureTypegenCliOptions(\n program.command('typegen').description(`Generate Typescript from Sway ABI JSON files`)\n );\n\n // Versions\n (command = program.command('versions'))\n .description('Check for version incompatibilities')\n .addOption(pathOption)\n .action(withBinaryPaths(command, Commands.versions, runVersions));\n\n return program;\n};\n","import chalk from 'chalk';\n\nexport const loggingConfig = {\n isDebugEnabled: false,\n isLoggingEnabled: true,\n};\n\nexport function configureLogging(params: { isDebugEnabled: boolean; isLoggingEnabled: boolean }) {\n loggingConfig.isLoggingEnabled = params.isLoggingEnabled;\n loggingConfig.isDebugEnabled = params.isDebugEnabled && loggingConfig.isLoggingEnabled;\n}\n\nexport function log(...data: unknown[]) {\n if (loggingConfig.isLoggingEnabled) {\n // eslint-disable-next-line no-console\n console.log(data.join(' '));\n }\n}\n\nexport function debug(...data: unknown[]) {\n if (loggingConfig.isDebugEnabled) {\n log(data);\n }\n}\n\nexport function error(...data: unknown[]) {\n // eslint-disable-next-line no-console\n console.log(chalk.red(data.join(' ')));\n}\n\nexport function warn(...data: unknown[]) {\n log(chalk.yellow(data.join(' ')));\n}\n","import { ProgramTypeEnum } from '@fuel-ts/abi-typegen';\nimport { runTypegen } from '@fuel-ts/abi-typegen/runTypegen';\nimport { getBinaryVersions } from '@fuel-ts/versions/cli';\nimport { writeFileSync, mkdirSync } from 'fs';\nimport { globSync } from 'glob';\nimport { join } from 'path';\n\nimport { getABIPaths } from '../../config/forcUtils';\nimport { renderIndexTemplate } from '../../templates';\nimport type { FuelsConfig } from '../../types';\nimport { debug, log, loggingConfig } from '../../utils/logger';\n\nasync function generateTypesForProgramType(\n config: FuelsConfig,\n paths: string[],\n programType: ProgramTypeEnum\n) {\n debug('Generating types..');\n\n let filepaths = await getABIPaths(paths, config);\n const pluralizedDirName = `${String(programType).toLocaleLowerCase()}s`;\n const versions = getBinaryVersions(config);\n\n const isScript = programType === ProgramTypeEnum.SCRIPT;\n const isPredicate = programType === ProgramTypeEnum.PREDICATE;\n\n if (isScript || isPredicate) {\n const loaderFiles = paths.flatMap((dirpath) => {\n const glob = `*-abi.json`;\n const cwd = `${dirpath}/out`;\n return globSync(glob, { cwd }).map((filename) => `${dirpath}/out/${filename}`);\n });\n filepaths = filepaths.concat(loaderFiles);\n }\n\n runTypegen({\n programType,\n cwd: config.basePath,\n filepaths,\n output: join(config.output, pluralizedDirName),\n silent: !loggingConfig.isDebugEnabled,\n versions,\n });\n\n return pluralizedDirName;\n}\n\nexport async function generateTypes(config: FuelsConfig) {\n log('Generating types..');\n\n const { contracts, scripts, predicates, output } = config;\n\n mkdirSync(output, { recursive: true });\n\n const members = [\n { type: ProgramTypeEnum.CONTRACT, programs: contracts },\n { type: ProgramTypeEnum.SCRIPT, programs: scripts },\n { type: ProgramTypeEnum.PREDICATE, programs: predicates },\n ];\n\n const pluralizedDirNames = await Promise.all(\n members\n .filter(({ programs }) => !!programs.length)\n .map(({ programs, type }) => generateTypesForProgramType(config, programs, type))\n );\n\n const indexFile = await renderIndexTemplate(pluralizedDirNames);\n\n writeFileSync(join(config.output, 'index.ts'), indexFile);\n}\n","import { FuelError } from '@fuel-ts/errors';\nimport { readFileSync, existsSync, writeFileSync } from 'fs';\nimport { globSync } from 'glob';\nimport camelCase from 'lodash.camelcase';\nimport { dirname, join } from 'path';\nimport toml from 'toml';\n\nimport type { FuelsConfig } from '../types';\n\nexport type ForcToml = {\n project: {\n authors?: string[];\n entry: string;\n license: string;\n name: string;\n };\n workspace: {\n members: string[];\n };\n dependencies: {\n [key: string]: string;\n };\n proxy?: {\n enabled: boolean;\n address?: string;\n };\n};\n\nexport enum SwayType {\n contract = 'contract',\n script = 'script',\n predicate = 'predicate',\n library = 'library',\n}\n\nexport const swayFiles = new Map<string, SwayType>();\n\nexport const getClosestForcTomlDir = (dir: string): string => {\n let forcPath = join(dir, 'Forc.toml');\n\n if (existsSync(forcPath)) {\n return forcPath;\n }\n\n const parent = join(dir, '..');\n forcPath = getClosestForcTomlDir(parent);\n\n if (parent === '/' && !existsSync(forcPath)) {\n const msg = `TOML file not found:\\n ${dir}`;\n throw new FuelError(FuelError.CODES.CONFIG_FILE_NOT_FOUND, msg);\n }\n\n return forcPath;\n};\n\nexport function readForcToml(contractPath: string) {\n if (!existsSync(contractPath)) {\n throw new FuelError(\n FuelError.CODES.CONFIG_FILE_NOT_FOUND,\n `TOML file not found:\\n ${contractPath}`\n );\n }\n\n const forcPath = getClosestForcTomlDir(contractPath);\n\n if (!existsSync(forcPath)) {\n throw new FuelError(\n FuelError.CODES.CONFIG_FILE_NOT_FOUND,\n `TOML file not found:\\n ${forcPath}`\n );\n }\n\n const forcFile = readFileSync(forcPath, 'utf8');\n return toml.parse(forcFile) as ForcToml;\n}\n\nexport function setForcTomlProxyAddress(contractPath: string, address: string) {\n const forcPath = getClosestForcTomlDir(contractPath);\n const tomlPristine = readFileSync(forcPath).toString();\n const tomlJson = readForcToml(forcPath);\n\n const isProxyEnabled = tomlJson.proxy?.enabled;\n const hasProxyAddress = tomlJson.proxy?.address;\n\n // never override address\n if (isProxyEnabled && hasProxyAddress) {\n return address;\n }\n\n // injects address into toml string\n const replaceReg = /(\\[proxy\\][\\s\\S]+^enabled.+$)/gm;\n const replaceStr = `$1\\naddress = \"${address}\"`;\n const modifiedToml = tomlPristine.replace(replaceReg, replaceStr);\n\n writeFileSync(forcPath, modifiedToml);\n\n return address;\n}\n\nexport function readSwayType(path: string) {\n const forcToml = readForcToml(path);\n const entryFile = forcToml.project.entry || 'main.sw';\n const swayEntryPath = join(path, 'src', entryFile);\n\n if (!swayFiles.has(swayEntryPath)) {\n const swayFile = readFileSync(swayEntryPath, 'utf8');\n const swayTypeLines = Object.values(SwayType).map((type) => `${type};`);\n const swayType = swayFile\n .split('\\n')\n .find((line) => swayTypeLines.some((swayTypeLine) => line === swayTypeLine))\n ?.split(';')[0];\n swayFiles.set(swayEntryPath, swayType as SwayType);\n }\n\n return swayFiles.get(swayEntryPath) as SwayType;\n}\n\nexport function getContractName(contractPath: string) {\n const { project } = readForcToml(contractPath);\n return project.name;\n}\n\nexport function getScriptName(scriptPath: string) {\n const { project } = readForcToml(scriptPath);\n return project.name;\n}\n\nexport function getPredicateName(predicatePath: string) {\n const { project } = readForcToml(predicatePath);\n return project.name;\n}\n\nexport function getContractCamelCase(contractPath: string) {\n const projectName = getContractName(contractPath);\n return camelCase(projectName);\n}\n\nexport function getBinaryPath(contractPath: string, { buildMode }: FuelsConfig) {\n const projectName = getContractName(contractPath);\n return join(contractPath, `/out/${buildMode}/${projectName}.bin`);\n}\n\nexport function getABIPath(contractPath: string, { buildMode }: FuelsConfig) {\n const projectName = getContractName(contractPath);\n return join(contractPath, `/out/${buildMode}/${projectName}-abi.json`);\n}\n\nexport function getABIPaths(paths: string[], config: FuelsConfig) {\n return Promise.all(paths.map((path) => getABIPath(path, config)));\n}\n\nexport const getStorageSlotsPath = (contractPath: string, { buildMode }: FuelsConfig) => {\n const projectName = getContractName(contractPath);\n return join(contractPath, `/out/${buildMode}/${projectName}-storage_slots.json`);\n};\n\nexport const findPrograms = (pathOrGlob: string, opts?: { cwd?: string }) => {\n const pathWithoutGlob = pathOrGlob.replace(/[/][*]*$/, '').replace(opts?.cwd ?? '', '');\n const absolutePath = join(opts?.cwd ?? '', pathWithoutGlob);\n const allTomlPaths = globSync(`${absolutePath}/**/*.toml`);\n\n return (\n allTomlPaths\n // Filter out the workspace\n .map((path) => ({ path, isWorkspace: readForcToml(path).workspace !== undefined }))\n .filter(({ isWorkspace }) => !isWorkspace)\n // Parse the sway type and filter out the library\n .map(({ path }) => ({ path: dirname(path), swayType: readSwayType(dirname(path)) }))\n .filter(({ swayType }) => swayType !== SwayType.library)\n );\n};\n","/* eslint-disable @typescript-eslint/triple-slash-reference */\n/// <reference path=\"../../hbs.d.ts\" />\n\n// TODO: once abi-typegen implements a way to generate all types of sway\n// programs in a bundle file we don't need to create a index.ts file\nimport Handlebars from 'handlebars';\n\nimport indexTemplate from './index.hbs';\n\nexport function renderIndexTemplate(paths: string[]) {\n const renderTemplate = Handlebars.compile(indexTemplate, {\n strict: true,\n noEscape: true,\n });\n return renderTemplate({\n paths,\n });\n}\n","{{#each paths}}\nexport * from './{{this}}';\n{{/each}}\n","import type { WalletUnlocked } from '@fuel-ts/account';\nimport { ContractFactory } from '@fuel-ts/contract';\nimport type { DeployContractOptions } from '@fuel-ts/contract';\nimport { Contract } from '@fuel-ts/program';\nimport { Src14OwnedProxy, Src14OwnedProxyFactory } from '@fuel-ts/recipes';\nimport { existsSync, readFileSync } from 'fs';\n\nimport {\n getABIPath,\n getBinaryPath,\n getClosestForcTomlDir,\n getContractCamelCase,\n getContractName,\n getStorageSlotsPath,\n readForcToml,\n setForcTomlProxyAddress,\n type ForcToml,\n} from '../../config/forcUtils';\nimport type { FuelsConfig, DeployedContract } from '../../types';\nimport { debug, log } from '../../utils/logger';\n\nimport { createWallet } from './createWallet';\nimport { getDeployConfig } from './getDeployConfig';\n\n/**\n * Deploys one contract.\n */\nexport async function deployContract(\n wallet: WalletUnlocked,\n binaryPath: string,\n abiPath: string,\n storageSlotsPath: string,\n deployConfig: DeployContractOptions,\n contractPath: string,\n tomlContents: ForcToml\n) {\n debug(`Deploying contract for ABI: ${abiPath}`);\n\n if (existsSync(storageSlotsPath)) {\n const storageSlots = JSON.parse(readFileSync(storageSlotsPath, 'utf-8'));\n // eslint-disable-next-line no-param-reassign\n deployConfig.storageSlots = storageSlots;\n }\n\n const targetBytecode = readFileSync(binaryPath);\n const targetAbi = JSON.parse(readFileSync(abiPath, 'utf-8'));\n const targetStorageSlots = deployConfig.storageSlots ?? [];\n\n const proxyBytecode = Src14OwnedProxyFactory.bytecode;\n const proxyAbi = Src14OwnedProxy.abi;\n const proxyStorageSlots = Src14OwnedProxy.storageSlots ?? [];\n\n const isProxyEnabled = tomlContents?.proxy?.enabled;\n const proxyAddress = tomlContents?.proxy?.address;\n\n /**\n * 0. If contract does NOT require a proxy.\n * Deploy as normal contract\n */\n if (!isProxyEnabled) {\n // a. Deploy the target contract\n const contractFactory = new ContractFactory(targetBytecode, targetAbi, wallet);\n const { waitForResult } = await contractFactory.deploy(deployConfig);\n const { contract } = await waitForResult();\n return contract.id.toB256();\n }\n\n /**\n * 1. If contract DOES require a proxy and HAS the `address` property set in their `Forc.Toml` file.\n * We need to re-deploy the target contract and update its ID in the proxy contract.\n */\n if (proxyAddress) {\n // a. Deploy the target contract\n const targetContractFactory = new ContractFactory(targetBytecode, targetAbi, wallet);\n const { waitForResult: waitForTarget } = await targetContractFactory.deploy(deployConfig);\n const { contract: targetContract } = await waitForTarget();\n\n // b. Update proxy contract with the new target contract ID\n const proxyContract = new Contract(proxyAddress, proxyAbi, wallet);\n const { waitForResult: waitForProxyUpdate } = await proxyContract.functions\n .set_proxy_target({ bits: targetContract.id.toB256() })\n .call();\n\n await waitForProxyUpdate();\n\n return proxyAddress;\n }\n\n /**\n * 2. If contract DOES require proxy and the address is NOT set.\n * We need to deploy the proxy and the target contracts, and update the\n * [proxy].address property in the users' Forc.Toml file.\n */\n\n // a. Deploy the target contract\n const targetContractFactory = new ContractFactory(targetBytecode, targetAbi, wallet);\n const { waitForResult: waitForTarget } = await targetContractFactory.deploy(deployConfig);\n const { contract: targetContract } = await waitForTarget();\n\n // b. Deploy the SR-C14 Compliant / Proxy Contract\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { storageSlots, stateRoot, ...commonDeployConfig } = deployConfig;\n const mergedStorageSlots = targetStorageSlots.concat(proxyStorageSlots);\n\n const proxyDeployConfig: DeployContractOptions = {\n ...commonDeployConfig,\n storageSlots: mergedStorageSlots,\n configurableConstants: {\n INITIAL_TARGET: { bits: targetContract.id.toB256() },\n INITIAL_OWNER: { Initialized: { Address: { bits: wallet.address.toB256() } } },\n },\n };\n\n const proxyFactory = new ContractFactory(proxyBytecode, proxyAbi, wallet);\n const { waitForResult: waitForProxy } = await proxyFactory.deploy(proxyDeployConfig);\n const { contract: proxyContract } = await waitForProxy();\n\n // c. Initialize the proxy contract\n const { waitForResult: waitForProxyInit } = await proxyContract.functions\n .initialize_proxy()\n .call();\n\n await waitForProxyInit();\n\n const proxyContractId = proxyContract.id.toB256();\n\n // d. Write the address of the proxy contract to user's Forc.Toml file\n setForcTomlProxyAddress(contractPath, proxyContractId);\n\n return proxyContractId;\n}\n\n/**\n * Deploys all contracts.\n */\nexport async function deployContracts(config: FuelsConfig) {\n const contracts: DeployedContract[] = [];\n\n const wallet = await createWallet(config.providerUrl, config.privateKey);\n\n log(`Deploying contracts to: ${wallet.provider.url}`);\n\n const contractsLen = config.contracts.length;\n\n for (let i = 0; i < contractsLen; i++) {\n const contractPath = config.contracts[i];\n const forcTomlPath = getClosestForcTomlDir(contractPath);\n const binaryPath = getBinaryPath(contractPath, config);\n const abiPath = getABIPath(contractPath, config);\n const storageSlotsPath = getStorageSlotsPath(contractPath, config);\n const projectName = getContractName(contractPath);\n const contractName = getContractCamelCase(contractPath);\n const tomlContents = readForcToml(forcTomlPath);\n const deployConfig = await getDeployConfig(config.deployConfig, {\n contracts: Array.from(contracts),\n contractName,\n contractPath,\n });\n\n const contractId = await deployContract(\n wallet,\n binaryPath,\n abiPath,\n storageSlotsPath,\n deployConfig,\n contractPath,\n tomlContents\n );\n\n debug(`Contract deployed: ${projectName} - ${contractId}`);\n\n contracts.push({\n name: contractName,\n contractId,\n });\n }\n\n return contracts;\n}\n","import { Wallet, Provider } from '@fuel-ts/account';\nimport { FuelError } from '@fuel-ts/errors';\n\nexport async function createWallet(providerUrl: string, privateKey?: string) {\n let pvtKey: string;\n\n if (privateKey) {\n pvtKey = privateKey;\n } else if (process.env.PRIVATE_KEY) {\n pvtKey = process.env.PRIVATE_KEY;\n } else {\n throw new FuelError(\n FuelError.CODES.MISSING_REQUIRED_PARAMETER,\n 'You must provide a privateKey via config.privateKey or env PRIVATE_KEY'\n );\n }\n\n try {\n const provider = new Provider(providerUrl);\n await provider.init(); // can probably be removed\n\n return Wallet.fromPrivateKey(pvtKey, provider);\n } catch (e) {\n const error = e as Error & { cause?: { code: string } };\n if (/EADDRNOTAVAIL|ECONNREFUSED/.test(error.cause?.code ?? '')) {\n throw new FuelError(\n FuelError.CODES.CONNECTION_REFUSED,\n `Couldn't connect to the node at \"${providerUrl}\". Check that you've got a node running at the config's providerUrl or set autoStartFuelCore to true.`\n );\n } else {\n throw error;\n }\n }\n}\n","import type { DeployContractOptions } from '@fuel-ts/contract';\n\nimport type { ContractDeployOptions, OptionsFunction } from '../../types';\n\nexport async function getDeployConfig(\n deployConfig: DeployContractOptions | OptionsFunction,\n options: ContractDeployOptions\n) {\n let config: DeployContractOptions;\n\n if (typeof deployConfig === 'function') {\n config = await deployConfig(options);\n } else {\n config = deployConfig;\n }\n\n return config;\n}\n","import { getPredicateRoot, Predicate } from '@fuel-ts/account';\nimport { debug, log } from 'console';\nimport { readFileSync } from 'fs';\n\nimport { getABIPath, getBinaryPath, getPredicateName } from '../../config/forcUtils';\nimport type { DeployedPredicate, FuelsConfig } from '../../types';\n\nimport { createWallet } from './createWallet';\n\n/**\n * Deploys all predicates.\n */\nexport async function deployPredicates(config: FuelsConfig) {\n const predicates: DeployedPredicate[] = [];\n\n const wallet = await createWallet(config.providerUrl, config.privateKey);\n\n log(`Deploying predicates to: ${wallet.provider.url}`);\n\n const predicatesLen = config.predicates.length;\n\n for (let i = 0; i < predicatesLen; i++) {\n const predicatePath = config.predicates[i];\n const binaryPath = getBinaryPath(predicatePath, config);\n const abiPath = getABIPath(predicatePath, config);\n const projectName = getPredicateName(predicatePath);\n const bytecode = readFileSync(binaryPath);\n const abi = JSON.parse(readFileSync(abiPath, 'utf-8'));\n\n const predicate = new Predicate({ abi, bytecode, provider: wallet.provider });\n const {\n bytes: loaderBytecode,\n interface: { jsonAbi },\n } = await (await predicate.deploy(wallet)).waitForResult();\n\n const predicateRoot = getPredicateRoot(loaderBytecode);\n\n debug(`Predicate deployed: ${projectName} - ${predicateRoot}`);\n\n predicates.push({\n path: predicatePath,\n predicateRoot,\n loaderBytecode,\n abi: jsonAbi,\n });\n }\n\n return predicates;\n}\n","import { Script } from '@fuel-ts/script';\nimport { debug, log } from 'console';\nimport { readFileSync } from 'fs';\n\nimport { getBinaryPath, getABIPath, getScriptName } from '../../config/forcUtils';\nimport type { FuelsConfig, DeployedScript } from '../../types';\n\nimport { createWallet } from './createWallet';\n\n/**\n * Deploys all scripts.\n */\nexport async function deployScripts(config: FuelsConfig) {\n const scripts: DeployedScript[] = [];\n\n const wallet = await createWallet(config.providerUrl, config.privateKey);\n\n log(`Deploying scripts to: ${wallet.provider.url}`);\n\n const scriptsLen = config.scripts.length;\n\n for (let i = 0; i < scriptsLen; i++) {\n const scriptPath = config.scripts[i];\n const binaryPath = getBinaryPath(scriptPath, config);\n const abiPath = getABIPath(scriptPath, config);\n const projectName = getScriptName(scriptPath);\n\n const bytecode = readFileSync(binaryPath);\n const abi = JSON.parse(readFileSync(abiPath, 'utf-8'));\n\n const script = new Script(bytecode, abi, wallet);\n const {\n bytes: loaderBytecode,\n interface: { jsonAbi },\n } = await (await script.deploy(wallet)).waitForResult();\n\n debug(`Script deployed: ${projectName}`);\n\n scripts.push({\n path: scriptPath,\n loaderBytecode,\n abi: jsonAbi,\n });\n }\n\n return scripts;\n}\n","import { writeFile, mkdir } from 'fs/promises';\nimport { resolve } from 'path';\n\nimport type { DeployedContract } from '../../types';\nimport { log } from '../../utils/logger';\n\nexport async function saveContractIds(contracts: DeployedContract[], output: string) {\n const contractsMap = contracts.reduce(\n (cConfig, { name, contractId }) => ({\n ...cConfig,\n [name]: contractId,\n }),\n {}\n );\n\n const filePath = resolve(output, 'contract-ids.json');\n\n await mkdir(output, { recursive: true });\n await writeFile(filePath, JSON.stringify(contractsMap, null, 2));\n\n log(`Contract IDs saved at: ${filePath}`);\n}\n","import { writeFileSync } from 'fs';\n\nimport { getPredicateName } from '../../config/forcUtils';\nimport type { DeployedPredicate, FuelsConfig } from '../../types';\n\nexport function savePredicateFiles(predicates: DeployedPredicate[], _config: FuelsConfig) {\n for (const { path, predicateRoot, loaderBytecode, abi } of predicates) {\n const predicateName = getPredicateName(path);\n\n const predicateRootPath = `${path}/out/${predicateName}-loader-bin-root`;\n writeFileSync(predicateRootPath, predicateRoot);\n\n const loaderBytecodePath = `${path}/out/${predicateName}-loader.bin`;\n writeFileSync(loaderBytecodePath, loaderBytecode);\n\n const abiPath = `${path}/out/${predicateName}-loader-abi.json`;\n writeFileSync(abiPath, JSON.stringify(abi, null, 2));\n }\n}\n","import { writeFileSync } from 'fs';\n\nimport { getScriptName } from '../../config/forcUtils';\nimport type { DeployedScript, FuelsConfig } from '../../types';\n\nexport function saveScriptFiles(scripts: DeployedScript[], _config: FuelsConfig) {\n for (const { path, loaderBytecode, abi } of scripts) {\n const scriptName = getScriptName(path);\n\n const loaderBytecodePath = `${path}/out/${scriptName}-loader.bin`;\n writeFileSync(loaderBytecodePath, loaderBytecode);\n\n const abiPath = `${path}/out/${scriptName}-loader-abi.json`;\n writeFileSync(abiPath, JSON.stringify(abi, null, 2));\n }\n}\n","import type { FuelsConfig } from '../../types';\nimport { generateTypes } from '../build/generateTypes';\n\nimport { deployContracts } from './deployContracts';\nimport { deployPredicates } from './deployPredicates';\nimport { deployScripts } from './deployScripts';\nimport { saveContractIds } from './saveContractIds';\nimport { savePredicateFiles } from './savePredicateFiles';\nimport { saveScriptFiles } from './saveScriptFiles';\n\nexport async function deploy(config: FuelsConfig) {\n /**\n * Deploy contract and save their IDs to JSON file.\n */\n const contracts = await deployContracts(config);\n await saveContractIds(contracts, config.output);\n\n /**\n * Deploy scripts and save deployed files to disk.\n */\n const scripts = await deployScripts(config);\n saveScriptFiles(scripts, config);\n\n /**\n * Deploy predicates and save deployed files to disk.\n */\n const predicates = await deployPredicates(config);\n savePredicateFiles(predicates, config);\n\n await config.onDeploy?.(config, {\n contracts,\n scripts,\n predicates,\n });\n\n /**\n * After deploying scripts/predicates, we need to\n * re-generate factory classe with the loader coee\n */\n await generateTypes(config);\n\n return {\n contracts,\n scripts,\n predicates,\n };\n}\n","import { defaultConsensusKey } from '@fuel-ts/utils';\nimport { getPortPromise } from 'portfinder';\n\nimport { launchNode } from '../../../test-utils';\nimport type { FuelsConfig } from '../../types';\nimport { log, loggingConfig } from '../../utils/logger';\n\nexport type FuelCoreNode = {\n bindIp: string;\n accessIp: string;\n port: number;\n providerUrl: string;\n snapshotDir: string;\n killChildProcess: () => void;\n};\n\nexport const autoStartFuelCore = async (config: FuelsConfig) => {\n let fuelCore: FuelCoreNode | undefined;\n\n if (config.autoStartFuelCore) {\n log(`Starting node using: '${config.fuelCorePath}'`);\n\n const bindIp = '0.0.0.0';\n const accessIp = '127.0.0.1';\n\n const port = config.fuelCorePort ?? (await getPortPromise({ port: 4000 }));\n\n const { cleanup, url, snapshotDir } = await launchNode({\n args: [\n ['--snapshot', config.snapshotDir],\n ['--db-type', 'in-memory'],\n ].flat() as string[],\n ip: bindIp,\n port: port.toString(),\n loggingEnabled: loggingConfig.isLoggingEnabled,\n basePath: config.basePath,\n fuelCorePath: config.fuelCorePath,\n includeInitialState: true,\n killProcessOnExit: true,\n });\n\n fuelCore = {\n bindIp,\n accessIp,\n port,\n providerUrl: url,\n snapshotDir,\n killChildProcess: cleanup,\n };\n\n // eslint-disable-next-line no-param-reassign\n config.providerUrl = fuelCore.providerUrl;\n // eslint-disable-next-line no-param-reassign\n config.privateKey = defaultConsensusKey;\n }\n\n return fuelCore;\n};\n","export * from '@fuel-ts/contract/test-utils';\nexport * from '@fuel-ts/account/test-utils';\nexport * from '@fuel-ts/errors/test-utils';\nexport * from '@fuel-ts/utils/test-utils';\n","import { spawn } from 'child_process';\n\nimport type { FuelsConfig } from '../../types';\nimport { debug, log, loggingConfig } from '../../utils/logger';\n\nimport { onForcExit, onForcError } from './forcHandlers';\n\nexport const buildSwayProgram = async (config: FuelsConfig, path: string) => {\n debug('Building Sway program', path);\n\n return new Promise<void>((resolve, reject) => {\n const args = ['build', '-p', path].concat(config.forcBuildFlags);\n const forc = spawn(config.forcPath, args, { stdio: 'pipe' });\n if (loggingConfig.isLoggingEnabled) {\n forc.stderr?.on('data', (chunk) => log(chunk.toString()));\n }\n\n if (loggingConfig.isDebugEnabled) {\n forc.stdout?.on('data', (chunk) => {\n debug(chunk.toString());\n });\n }\n\n const onExit = onForcExit(resolve, reject);\n const onError = onForcError(reject);\n\n forc.on('exit', onExit);\n forc.on('error', onError);\n });\n};\n","import { error } from '../../utils/logger';\n\ntype OnResultFn = () => void;\ntype OnErrorFn = (reason?: number | Error) => void;\n\nexport const onForcExit =\n (onResultFn: OnResultFn, onErrorFn: OnErrorFn) => (code: number | null) => {\n if (code) {\n onErrorFn(new Error(`forc exited with error code ${code}`));\n } else {\n onResultFn();\n }\n };\n\nexport const onForcError = (onError: OnErrorFn) => (err: Error) => {\n error(err);\n onError(err);\n};\n","import type { FuelsConfig } from '../../types';\nimport { log } from '../../utils/logger';\n\nimport { buildSwayProgram } from './buildSwayProgram';\n\nexport async function buildSwayPrograms(config: FuelsConfig) {\n log(`Building Sway programs using: '${config.forcPath}'`);\n\n const paths = config.workspace\n ? [config.workspace]\n : [config.contracts, config.predicates, config.scripts].flat();\n\n await Promise.all(paths.map((path) => buildSwayProgram(config, path)));\n}\n","import { type Command } from 'commander';\n\nimport type { FuelsConfig } from '../../types';\nimport { log } from '../../utils/logger';\nimport { deploy } from '../deploy';\nimport { autoStartFuelCore } from '../dev/autoStartFuelCore';\n\nimport { buildSwayPrograms } from './buildSwayPrograms';\nimport { generateTypes } from './generateTypes';\n\nexport async function build(config: FuelsConfig, program?: Command) {\n log('Building..');\n\n await buildSwayPrograms(config);\n await generateTypes(config);\n await config.onBuild?.(config);\n\n const options = program?.opts();\n if (options?.deploy) {\n const fuelCore = await autoStartFuelCore(config);\n await deploy(config);\n fuelCore?.killChildProcess();\n }\n}\n","import type { FSWatcher } from 'chokidar';\nimport { watch } from 'chokidar';\nimport { globSync } from 'glob';\n\nimport { loadConfig } from '../../config/loadConfig';\nimport { type FuelsConfig } from '../../types';\nimport { error, log } from '../../utils/logger';\nimport { build } from '../build';\nimport { deploy } from '../deploy';\nimport { withConfigErrorHandler } from '../withConfig';\n\nimport type { FuelCoreNode } from './autoStartFuelCore';\nimport { autoStartFuelCore } from './autoStartFuelCore';\n\nexport const closeAllFileHandlers = (handlers: FSWatcher[]) => {\n handlers.forEach((h) => h.close());\n};\n\nexport const buildAndDeploy = async (config: FuelsConfig) => {\n await build(config);\n const deployedContracts = await deploy(config);\n await config.onDev?.(config);\n\n return deployedContracts;\n};\n\nexport const getConfigFilepathsToWatch = (config: FuelsConfig) => {\n const configFilePathsToWatch: string[] = [config.configPath];\n if (config.snapshotDir) {\n configFilePathsToWatch.push(config.snapshotDir);\n }\n return configFilePathsToWatch;\n};\n\nexport type DevState = {\n config: FuelsConfig;\n watchHandlers: FSWatcher[];\n fuelCore?: FuelCoreNode;\n};\n\nexport const workspaceFileChanged = (state: DevState) => async (_event: string, path: string) => {\n log(`\\nFile changed: ${path}`);\n await buildAndDeploy(state.config);\n};\n\nexport const configFileChanged = (state: DevState) => async (_event: string, path: string) => {\n log(`\\nFile changed: ${path}`);\n\n closeAllFileHandlers(state.watchHandlers);\n state.fuelCore?.killChildProcess();\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-use-before-define\n await dev(await loadConfig(state.config.basePath));\n } catch (err: unknown) {\n await withConfigErrorHandler(<Error>err, state.config);\n }\n};\n\nexport const dev = async (config: FuelsConfig) => {\n const fuelCore = await autoStartFuelCore(config);\n\n const configFilePaths = getConfigFilepathsToWatch(config);\n\n const { contracts, scripts, predicates, basePath: cwd } = config;\n\n const workspaceFilePaths = [contracts, predicates, scripts]\n .flat()\n .flatMap((dir) => [\n dir,\n globSync(`${dir}/**/*.toml`, { cwd }),\n globSync(`${dir}/**/*.sw`, { cwd }),\n ])\n .flat();\n\n try {\n // Run once\n await buildAndDeploy(config);\n\n const watchHandlers: FSWatcher[] = [];\n const options = { persistent: true, ignoreInitial: true, ignored: '**/out/**' };\n const state = { config, watchHandlers, fuelCore };\n\n // watch: fuels.config.ts and snapshotDir\n watchHandlers.push(watch(configFilePaths, options).on('all', configFileChanged(state)));\n\n // watch: Forc's workspace members\n watchHandlers.push(watch(workspaceFilePaths, options).on('all', workspaceFileChanged(state)));\n } catch (err: unknown) {\n error(err);\n throw err;\n }\n};\n","import { FuelError } from '@fuel-ts/errors';\nimport { defaultConsensusKey } from '@fuel-ts/utils';\nimport { bundleRequire } from 'bundle-require';\nimport type { BuildOptions } from 'esbuild';\nimport JoyCon from 'joycon';\nimport { resolve, parse } from 'path';\n\nimport { tryFindBinaries } from '../../cli-utils';\nimport type { FuelsConfig, UserFuelsConfig } from '../types';\n\nimport { SwayType, readForcToml, readSwayType } from './forcUtils';\nimport { validateConfig } from './validateConfig';\n\nexport async function loadUserConfig(\n cwd: string\n): Promise<{ userConfig: UserFuelsConfig; configPath: string }> {\n const configJoycon = new JoyCon();\n\n const configPath = await configJoycon.resolve({\n files: ['ts', 'js', 'cjs', 'mjs'].map((e) => `fuels.config.${e}`),\n cwd,\n stopDir: parse(cwd).root,\n });\n\n if (!configPath) {\n throw new FuelError(FuelError.CODES.CONFIG_FILE_NOT_FOUND, 'Config file not found!');\n }\n\n const esbuildOptions: BuildOptions = {\n target: 'ES2021',\n platform: 'node',\n format: 'esm',\n };\n\n const result = await bundleRequire({\n filepath: configPath,\n esbuildOptions,\n cwd,\n });\n\n const userConfig: UserFuelsConfig = result.mod.default;\n return { configPath, userConfig };\n}\n\nexport async function loadConfig(cwd: string): Promise<FuelsConfig> {\n const { configPath, userConfig } = await loadUserConfig(cwd);\n await validateConfig(userConfig);\n\n const { forcBuildFlags = [] } = userConfig;\n const releaseFlag = forcBuildFlags.find((f) => f === '--release');\n const buildMode = releaseFlag ? 'release' : 'debug';\n\n const { forcPath, fuelCorePath } = tryFindBinaries({\n forcPath: userConfig.forcPath,\n fuelCorePath: userConfig.fuelCorePath,\n });\n\n // Start clone-object while initializing optional props\n const config: FuelsConfig = {\n contracts: [],\n scripts: [],\n predicates: [],\n deployConfig: {},\n autoStartFuelCore: true,\n fuelCorePort: 4000,\n providerUrl: process.env.FUEL_NETWORK_URL ?? 'http://127.0.0.1:4000/v1/graphql',\n privateKey: defaultConsensusKey,\n ...userConfig,\n basePath: cwd,\n forcPath,\n fuelCorePath,\n configPath,\n forcBuildFlags,\n buildMode,\n };\n\n // Resolve the output path on loaded config\n config.output = resolve(cwd, config.output);\n\n // Initialize optional variables\n config.autoStartFuelCore = userConfig.autoStartFuelCore ?? true;\n\n if (!userConfig.workspace) {\n // Resolve members individually\n const { contracts, predicates, scripts } = userConfig;\n config.contracts = (contracts || []).map((c: string) => resolve(cwd, c));\n config.scripts = (scripts || []).map((s: string) => resolve(cwd, s));\n config.predicates = (predicates || []).map((p: string) => resolve(cwd, p));\n } else {\n // Resolve members via workspace\n const workspace = resolve(cwd, userConfig.workspace);\n const forcToml = readForcToml(workspace);\n\n if (!forcToml.workspace) {\n const workspaceMsg = `Forc workspace not detected in:\\n ${workspace}/Forc.toml`;\n\n const swayProgramType = readSwayType(workspace);\n const exampleMsg = `Try using '${swayProgramType}s' instead of 'workspace' in:\\n ${configPath}`;\n\n throw new FuelError(\n FuelError.CODES.WORKSPACE_NOT_DETECTED,\n [workspaceMsg, exampleMsg].join('\\n\\n')\n );\n }\n\n const swayMembers = forcToml.workspace.members.map((member) => resolve(workspace, member));\n\n swayMembers\n .map((path) => ({ path, type: readSwayType(path) }))\n .filter(({ type }) => type !== SwayType.library)\n .forEach(({ path, type }) => config[`${type as Exclude<SwayType, 'library'>}s`].push(path));\n\n config.workspace = workspace;\n }\n\n return config;\n}\n","export * from '@fuel-ts/utils/cli-utils';\n","import * as yup from 'yup';\n\nimport type { UserFuelsConfig } from '../types';\n\nconst schema = yup\n .object({\n workspace: yup.string(),\n contracts: yup.array(yup.string()),\n scripts: yup.array(yup.string()),\n predicates: yup.array(yup.string()),\n output: yup.string().required('config.output should be a valid string'),\n })\n .required();\n\nexport async function validateConfig(config: UserFuelsConfig) {\n return schema.validate(config);\n}\n","import { capitalizeString } from '@fuel-ts/utils';\nimport type { Command } from 'commander';\n\nimport { loadConfig } from '../config/loadConfig';\nimport type { Commands, FuelsConfig, CommandEvent } from '../types';\nimport { error, log } from '../utils/logger';\n\nexport const withConfigErrorHandler = async (err: Error, config?: FuelsConfig): Promise<void> => {\n error(err.message);\n await config?.onFailure?.(config, <Error>err);\n throw err;\n};\n\nexport function withConfig<CType extends Commands>(\n program: Command,\n command: CType,\n fn: (\n config: FuelsConfig,\n options?: Command\n ) => Promise<Extract<CommandEvent, { type: CType }>['data']>\n) {\n return async () => {\n const options = program.opts();\n\n let config: FuelsConfig;\n\n try {\n config = await loadConfig(options.path);\n } catch (err) {\n await withConfigErrorHandler(<Error>err);\n return;\n }\n\n try {\n await fn(config, program);\n log(`🎉 ${capitalizeString(command)} completed successfully!`);\n } catch (err: unknown) {\n await withConfigErrorHandler(<Error>err, config);\n }\n };\n}\n","import { FuelError } from '@fuel-ts/errors';\nimport { type Command } from 'commander';\nimport { existsSync, statSync, writeFileSync } from 'fs';\nimport { dirname, join, relative, resolve } from 'path';\n\nimport { findPrograms } from '../../config/forcUtils';\nimport { renderFuelsConfigTemplate } from '../../templates/fuels.config';\nimport { log } from '../../utils/logger';\n\nexport function init(program: Command) {\n const options = program.opts();\n\n const { path, autoStartFuelCore, forcPath, fuelCorePath, fuelCorePort } = options;\n\n let workspace: string | undefined;\n let absoluteWorkspace: string | undefined;\n\n if (options.workspace) {\n absoluteWorkspace = resolve(path, options.workspace);\n workspace = `./${relative(path, absoluteWorkspace)}`;\n }\n\n const absoluteOutput = resolve(path, options.output);\n const output = `./${relative(path, absoluteOutput)}`;\n\n const convertFilePathToDir = (filePath: string) =>\n statSync(resolve(path, filePath)).isDirectory() ? filePath : dirname(filePath);\n\n const [contracts, scripts, predicates] = ['contracts', 'scripts', 'predicates'].map(\n (optionName) => {\n // Globs `/*` get expanded by the OS auto-magically\n const paths: string[] = options[optionName];\n if (!paths) {\n return undefined;\n }\n\n const selectedSwayType = optionName.slice(0, -1);\n\n const programs = paths\n .map(convertFilePathToDir)\n .flatMap((pathOrGlob) => findPrograms(pathOrGlob, { cwd: path }));\n const programDirs = programs\n .filter(({ swayType }) => swayType === selectedSwayType)\n .map(({ path: programPath }) => relative(path, programPath));\n\n return [...new Set(programDirs)];\n }\n );\n\n // Check that at least one of the options is informed\n const noneIsInformed = ![workspace, contracts, scripts, predicates].find((v) => v !== undefined);\n if (noneIsInformed) {\n // mimicking commander property validation\n // We want to use `console.log` here to avoid the ability to turn off this command prompt.\n // eslint-disable-next-line no-console\n console.log(`error: required option '-w, --workspace <path>' not specified\\r`);\n process.exit(1);\n }\n\n // Ensure that every program that is defined, has at least one program\n const programLengths = [contracts, scripts, predicates]\n .filter(Boolean)\n .map((programs) => programs?.length);\n if (programLengths.some((length) => length === 0)) {\n const [contractLength, scriptLength, predicateLength] = programLengths;\n\n const message = ['error: unable to detect program/s'];\n if (contractLength === 0) {\n message.push(`- contract/s detected ${contractLength}`);\n }\n if (scriptLength === 0) {\n message.push(`- script/s detected ${scriptLength}`);\n }\n if (predicateLength === 0) {\n message.push(`- predicate/s detected ${predicateLength}`);\n }\n\n log(message.join('\\r\\n'));\n process.exit(1);\n }\n\n const fuelsConfigPath = join(path, 'fuels.config.ts');\n\n if (existsSync(fuelsConfigPath)) {\n throw new FuelError(\n FuelError.CODES.CONFIG_FILE_ALREADY_EXISTS,\n `Config file exists, aborting.\\n ${fuelsConfigPath}`\n );\n }\n\n const renderedConfig = renderFuelsConfigTemplate({\n workspace,\n contracts,\n scripts,\n predicates,\n output,\n forcPath,\n fuelCorePath,\n autoStartFuelCore,\n fuelCorePort,\n });\n\n writeFileSync(fuelsConfigPath, renderedConfig);\n\n log(`Config file created at:\\n\\n ${fuelsConfigPath}\\n`);\n}\n","/* eslint-disable @typescript-eslint/triple-slash-reference */\n/// <reference path=\"../../hbs.d.ts\" />\n\nimport Handlebars from 'handlebars';\n\nimport fuelsConfigTemplate from './fuels.config.hbs';\n\nHandlebars.registerHelper('isDefined', (v) => v !== undefined);\n\nexport function renderFuelsConfigTemplate(props: {\n workspace?: string;\n contracts?: string[];\n scripts?: string[];\n predicates?: string[];\n output: string;\n forcPath?: string;\n fuelCorePath?: string;\n autoStartFuelCore?: boolean;\n fuelCorePort?: string;\n}) {\n const renderTemplate = Handlebars.compile(fuelsConfigTemplate, {\n strict: true,\n noEscape: true,\n });\n\n return renderTemplate(props);\n}\n","import { createConfig } from 'fuels';\n\nexport default createConfig({\n {{#if (isDefined workspace)}}\n workspace: '{{workspace}}',\n {{else}}\n {{#if (isDefined contracts)}}\n contracts: [\n {{#each contracts}}\n '{{this}}',\n {{/each}}\n ],\n {{/if}}\n {{#if (isDefined predicates)}}\n predicates: [\n {{#each predicates}}\n '{{this}}',\n {{/each}}\n ],\n {{/if}}\n {{#if (isDefined scripts)}}\n scripts: [\n {{#each scripts}}\n '{{this}}',\n {{/each}}\n ],\n {{/if}}\n {{/if}}\n output: '{{output}}',\n {{#if (isDefined forcPath)}}\n forcPath: '{{forcPath}}',\n {{/if}}\n {{#if (isDefined fuelCorePath)}}\n fuelCorePath: '{{fuelCorePath}}',\n {{/if}}\n {{#if (isDefined autoStartFuelCore)}}\n autoStartFuelCore: {{autoStartFuelCore}},\n {{/if}}\n {{#if (isDefined fuelCorePort)}}\n fuelCorePort: {{fuelCorePort}},\n {{/if}}\n});\n\n/**\n * Check the docs:\n * https://docs.fuel.network/docs/fuels-ts/fuels-cli/config-file/\n */\n","import { watch, type FSWatcher } from 'chokidar';\n\nimport { loadConfig } from '../../config/loadConfig';\nimport type { FuelsConfig } from '../../types';\nimport { error, log } from '../../utils/logger';\nimport type { FuelCoreNode } from '../dev/autoStartFuelCore';\nimport { autoStartFuelCore } from '../dev/autoStartFuelCore';\nimport { withConfigErrorHandler } from '../withConfig';\n\nexport type NodeState = {\n config: FuelsConfig;\n watchHandlers: FSWatcher[];\n fuelCore?: FuelCoreNode;\n};\n\nexport const getConfigFilepathsToWatch = (config: FuelsConfig) => {\n const configFilePathsToWatch: string[] = [config.configPath];\n if (config.snapshotDir) {\n configFilePathsToWatch.push(config.snapshotDir);\n }\n return configFilePathsToWatch;\n};\n\nexport const closeAllFileHandlers = (handlers: FSWatcher[]) => {\n handlers.forEach((h) => h.close());\n};\n\nexport const configFileChanged = (state: NodeState) => async (_event: string, path: string) => {\n log(`\\nFile changed: ${path}`);\n\n closeAllFileHandlers(state.watchHandlers);\n state.fuelCore?.killChildProcess();\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-use-before-define\n await node(await loadConfig(state.config.basePath));\n await state.config.onNode?.(state.config);\n } catch (err: unknown) {\n await withConfigErrorHandler(<Error>err, state.config);\n }\n};\n\nexport const node = async (config: FuelsConfig) => {\n const fuelCore = await autoStartFuelCore(config);\n\n const configFilePaths = getConfigFilepathsToWatch(config);\n\n try {\n const watchHandlers: FSWatcher[] = [];\n const options = { persistent: true, ignoreInitial: true, ignored: '**/out/**' };\n const state = { config, watchHandlers, fuelCore };\n\n // watch: fuels.config.ts and snapshotDir\n watchHandlers.push(watch(configFilePaths, options).on('all', configFileChanged(state)));\n } catch (err: unknown) {\n error(err);\n throw err;\n }\n};\n","import type { Command } from 'commander';\n\nimport { loadUserConfig } from '../config/loadConfig';\nimport type { Commands, UserFuelsConfig } from '../types';\nimport { debug, error } from '../utils/logger';\n\ntype BinaryPaths = Pick<UserFuelsConfig, 'forcPath' | 'fuelCorePath'>;\n\nexport function withBinaryPaths<CType extends Commands>(\n program: Command,\n _command: CType,\n fn: (paths: BinaryPaths) => void\n) {\n return async () => {\n const options = program.opts();\n\n const paths: BinaryPaths = {};\n\n try {\n const { userConfig } = await loadUserConfig(options.path);\n paths.forcPath = userConfig.forcPath;\n paths.fuelCorePath = userConfig.fuelCorePath;\n } catch (err) {\n debug((<Error>err).message);\n }\n\n try {\n await fn(paths);\n } catch (err) {\n error(err);\n }\n };\n}\n","import type { Command } from 'commander';\n\nimport type { Commands } from '../types';\nimport { error } from '../utils/logger';\n\nexport function withProgram<CType ex