UNPKG

fuels

Version:

Fuel TS SDK

1,129 lines (1,080 loc) • 43.5 kB
var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default")); // src/cli/utils/logger.ts import chalk from "chalk"; var loggingConfig = { isDebugEnabled: false, isLoggingEnabled: true }; function configureLogging(params) { loggingConfig.isLoggingEnabled = params.isLoggingEnabled; loggingConfig.isDebugEnabled = params.isDebugEnabled && loggingConfig.isLoggingEnabled; } __name(configureLogging, "configureLogging"); function log(...data) { if (loggingConfig.isLoggingEnabled) { console.log(data.join(" ")); } } __name(log, "log"); function debug(...data) { if (loggingConfig.isDebugEnabled) { log(data); } } __name(debug, "debug"); function error(...data) { console.log(chalk.red(data.join(" "))); } __name(error, "error"); function warn(...data) { log(chalk.yellow(data.join(" "))); } __name(warn, "warn"); // src/cli.ts import { configureCliOptions as configureTypegenCliOptions } from "@fuel-ts/abi-typegen/cli"; import { versions } from "@fuel-ts/versions"; import { runVersions } from "@fuel-ts/versions/cli"; import { Command, Option } from "commander"; // src/cli/commands/build/generateTypes.ts import { ProgramTypeEnum } from "@fuel-ts/abi-typegen"; import { runTypegen } from "@fuel-ts/abi-typegen/runTypegen"; import { getBinaryVersions } from "@fuel-ts/versions/cli"; import { writeFileSync as writeFileSync2, mkdirSync } from "fs"; import { globSync as globSync2 } from "glob"; import { join as join2 } from "path"; // src/cli/config/forcUtils.ts import { FuelError } from "@fuel-ts/errors"; import { readFileSync, existsSync, writeFileSync } from "fs"; import { globSync } from "glob"; import camelCase from "lodash.camelcase"; import { dirname, join } from "path"; import toml from "toml"; var SwayType = /* @__PURE__ */ ((SwayType2) => { SwayType2["contract"] = "contract"; SwayType2["script"] = "script"; SwayType2["predicate"] = "predicate"; SwayType2["library"] = "library"; return SwayType2; })(SwayType || {}); var swayFiles = /* @__PURE__ */ new Map(); var getClosestForcTomlDir = /* @__PURE__ */ __name((dir) => { let forcPath = join(dir, "Forc.toml"); if (existsSync(forcPath)) { return forcPath; } const parent = join(dir, ".."); forcPath = getClosestForcTomlDir(parent); if (parent === "/" && !existsSync(forcPath)) { const msg = `TOML file not found: ${dir}`; throw new FuelError(FuelError.CODES.CONFIG_FILE_NOT_FOUND, msg); } return forcPath; }, "getClosestForcTomlDir"); function readForcToml(contractPath) { if (!existsSync(contractPath)) { throw new FuelError( FuelError.CODES.CONFIG_FILE_NOT_FOUND, `TOML file not found: ${contractPath}` ); } const forcPath = getClosestForcTomlDir(contractPath); if (!existsSync(forcPath)) { throw new FuelError( FuelError.CODES.CONFIG_FILE_NOT_FOUND, `TOML file not found: ${forcPath}` ); } const forcFile = readFileSync(forcPath, "utf8"); return toml.parse(forcFile); } __name(readForcToml, "readForcToml"); function setForcTomlProxyAddress(contractPath, address) { const forcPath = getClosestForcTomlDir(contractPath); const tomlPristine = readFileSync(forcPath).toString(); const tomlJson = readForcToml(forcPath); const isProxyEnabled = tomlJson.proxy?.enabled; const hasProxyAddress = tomlJson.proxy?.address; if (isProxyEnabled && hasProxyAddress) { return address; } const replaceReg = /(\[proxy\][\s\S]+^enabled.+$)/gm; const replaceStr = `$1 address = "${address}"`; const modifiedToml = tomlPristine.replace(replaceReg, replaceStr); writeFileSync(forcPath, modifiedToml); return address; } __name(setForcTomlProxyAddress, "setForcTomlProxyAddress"); function readSwayType(path2) { const forcToml = readForcToml(path2); const entryFile = forcToml.project.entry || "main.sw"; const swayEntryPath = join(path2, "src", entryFile); if (!swayFiles.has(swayEntryPath)) { const swayFile = readFileSync(swayEntryPath, "utf8"); const swayTypeLines = Object.values(SwayType).map((type) => `${type};`); const swayType = swayFile.split("\n").find((line) => swayTypeLines.some((swayTypeLine) => line === swayTypeLine))?.split(";")[0]; swayFiles.set(swayEntryPath, swayType); } return swayFiles.get(swayEntryPath); } __name(readSwayType, "readSwayType"); function getContractName(contractPath) { const { project } = readForcToml(contractPath); return project.name; } __name(getContractName, "getContractName"); function getScriptName(scriptPath) { const { project } = readForcToml(scriptPath); return project.name; } __name(getScriptName, "getScriptName"); function getPredicateName(predicatePath) { const { project } = readForcToml(predicatePath); return project.name; } __name(getPredicateName, "getPredicateName"); function getContractCamelCase(contractPath) { const projectName = getContractName(contractPath); return camelCase(projectName); } __name(getContractCamelCase, "getContractCamelCase"); function getBinaryPath(contractPath, { buildMode }) { const projectName = getContractName(contractPath); return join(contractPath, `/out/${buildMode}/${projectName}.bin`); } __name(getBinaryPath, "getBinaryPath"); function getABIPath(contractPath, { buildMode }) { const projectName = getContractName(contractPath); return join(contractPath, `/out/${buildMode}/${projectName}-abi.json`); } __name(getABIPath, "getABIPath"); function getABIPaths(paths, config) { return Promise.all(paths.map((path2) => getABIPath(path2, config))); } __name(getABIPaths, "getABIPaths"); var getStorageSlotsPath = /* @__PURE__ */ __name((contractPath, { buildMode }) => { const projectName = getContractName(contractPath); return join(contractPath, `/out/${buildMode}/${projectName}-storage_slots.json`); }, "getStorageSlotsPath"); var findPrograms = /* @__PURE__ */ __name((pathOrGlob, opts) => { const pathWithoutGlob = pathOrGlob.replace(/[/][*]*$/, "").replace(opts?.cwd ?? "", ""); const absolutePath = join(opts?.cwd ?? "", pathWithoutGlob); const allTomlPaths = globSync(`${absolutePath}/**/*.toml`); return allTomlPaths.map((path2) => ({ path: path2, isWorkspace: readForcToml(path2).workspace !== void 0 })).filter(({ isWorkspace }) => !isWorkspace).map(({ path: path2 }) => ({ path: dirname(path2), swayType: readSwayType(dirname(path2)) })).filter(({ swayType }) => swayType !== "library" /* library */); }, "findPrograms"); // src/cli/templates/index.ts import Handlebars from "handlebars"; // src/cli/templates/index.hbs var templates_default = "{{#each paths}}\nexport * from './{{this}}';\n{{/each}}\n"; // src/cli/templates/index.ts function renderIndexTemplate(paths) { const renderTemplate = Handlebars.compile(templates_default, { strict: true, noEscape: true }); return renderTemplate({ paths }); } __name(renderIndexTemplate, "renderIndexTemplate"); // src/cli/commands/build/generateTypes.ts async function generateTypesForProgramType(config, paths, programType) { debug("Generating types.."); let filepaths = await getABIPaths(paths, config); const pluralizedDirName = `${String(programType).toLocaleLowerCase()}s`; const versions3 = getBinaryVersions(config); const isScript = programType === ProgramTypeEnum.SCRIPT; const isPredicate = programType === ProgramTypeEnum.PREDICATE; if (isScript || isPredicate) { const loaderFiles = paths.flatMap((dirpath) => { const glob = `*-abi.json`; const cwd = `${dirpath}/out`; return globSync2(glob, { cwd }).map((filename) => `${dirpath}/out/${filename}`); }); filepaths = filepaths.concat(loaderFiles); } runTypegen({ programType, cwd: config.basePath, filepaths, output: join2(config.output, pluralizedDirName), silent: !loggingConfig.isDebugEnabled, versions: versions3 }); return pluralizedDirName; } __name(generateTypesForProgramType, "generateTypesForProgramType"); async function generateTypes(config) { log("Generating types.."); const { contracts, scripts, predicates, output } = config; mkdirSync(output, { recursive: true }); const members = [ { type: ProgramTypeEnum.CONTRACT, programs: contracts }, { type: ProgramTypeEnum.SCRIPT, programs: scripts }, { type: ProgramTypeEnum.PREDICATE, programs: predicates } ]; const pluralizedDirNames = await Promise.all( members.filter(({ programs }) => !!programs.length).map(({ programs, type }) => generateTypesForProgramType(config, programs, type)) ); const indexFile = await renderIndexTemplate(pluralizedDirNames); writeFileSync2(join2(config.output, "index.ts"), indexFile); } __name(generateTypes, "generateTypes"); // src/cli/commands/deploy/deployContracts.ts import { ContractFactory } from "@fuel-ts/contract"; import { Contract } from "@fuel-ts/program"; import { Src14OwnedProxy, Src14OwnedProxyFactory } from "@fuel-ts/recipes"; import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs"; // src/cli/commands/deploy/createWallet.ts import { Wallet, Provider } from "@fuel-ts/account"; import { FuelError as FuelError2 } from "@fuel-ts/errors"; async function createWallet(providerUrl, privateKey) { let pvtKey; if (privateKey) { pvtKey = privateKey; } else if (process.env.PRIVATE_KEY) { pvtKey = process.env.PRIVATE_KEY; } else { throw new FuelError2( FuelError2.CODES.MISSING_REQUIRED_PARAMETER, "You must provide a privateKey via config.privateKey or env PRIVATE_KEY" ); } try { const provider = new Provider(providerUrl); await provider.init(); return Wallet.fromPrivateKey(pvtKey, provider); } catch (e) { const error2 = e; if (/EADDRNOTAVAIL|ECONNREFUSED/.test(error2.cause?.code ?? "")) { throw new FuelError2( FuelError2.CODES.CONNECTION_REFUSED, `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.` ); } else { throw error2; } } } __name(createWallet, "createWallet"); // src/cli/commands/deploy/getDeployConfig.ts async function getDeployConfig(deployConfig, options) { let config; if (typeof deployConfig === "function") { config = await deployConfig(options); } else { config = deployConfig; } return config; } __name(getDeployConfig, "getDeployConfig"); // src/cli/commands/deploy/deployContracts.ts async function deployContract(wallet, binaryPath, abiPath, storageSlotsPath, deployConfig, contractPath, tomlContents) { debug(`Deploying contract for ABI: ${abiPath}`); if (existsSync2(storageSlotsPath)) { const storageSlots2 = JSON.parse(readFileSync2(storageSlotsPath, "utf-8")); deployConfig.storageSlots = storageSlots2; } const targetBytecode = readFileSync2(binaryPath); const targetAbi = JSON.parse(readFileSync2(abiPath, "utf-8")); const targetStorageSlots = deployConfig.storageSlots ?? []; const proxyBytecode = Src14OwnedProxyFactory.bytecode; const proxyAbi = Src14OwnedProxy.abi; const proxyStorageSlots = Src14OwnedProxy.storageSlots ?? []; const isProxyEnabled = tomlContents?.proxy?.enabled; const proxyAddress = tomlContents?.proxy?.address; if (!isProxyEnabled) { const contractFactory = new ContractFactory(targetBytecode, targetAbi, wallet); const { waitForResult } = await contractFactory.deploy(deployConfig); const { contract } = await waitForResult(); return contract.id.toB256(); } if (proxyAddress) { const targetContractFactory2 = new ContractFactory(targetBytecode, targetAbi, wallet); const { waitForResult: waitForTarget2 } = await targetContractFactory2.deploy(deployConfig); const { contract: targetContract2 } = await waitForTarget2(); const proxyContract2 = new Contract(proxyAddress, proxyAbi, wallet); const { waitForResult: waitForProxyUpdate } = await proxyContract2.functions.set_proxy_target({ bits: targetContract2.id.toB256() }).call(); await waitForProxyUpdate(); return proxyAddress; } const targetContractFactory = new ContractFactory(targetBytecode, targetAbi, wallet); const { waitForResult: waitForTarget } = await targetContractFactory.deploy(deployConfig); const { contract: targetContract } = await waitForTarget(); const { storageSlots, stateRoot, ...commonDeployConfig } = deployConfig; const mergedStorageSlots = targetStorageSlots.concat(proxyStorageSlots); const proxyDeployConfig = { ...commonDeployConfig, storageSlots: mergedStorageSlots, configurableConstants: { INITIAL_TARGET: { bits: targetContract.id.toB256() }, INITIAL_OWNER: { Initialized: { Address: { bits: wallet.address.toB256() } } } } }; const proxyFactory = new ContractFactory(proxyBytecode, proxyAbi, wallet); const { waitForResult: waitForProxy } = await proxyFactory.deploy(proxyDeployConfig); const { contract: proxyContract } = await waitForProxy(); const { waitForResult: waitForProxyInit } = await proxyContract.functions.initialize_proxy().call(); await waitForProxyInit(); const proxyContractId = proxyContract.id.toB256(); setForcTomlProxyAddress(contractPath, proxyContractId); return proxyContractId; } __name(deployContract, "deployContract"); async function deployContracts(config) { const contracts = []; const wallet = await createWallet(config.providerUrl, config.privateKey); log(`Deploying contracts to: ${wallet.provider.url}`); const contractsLen = config.contracts.length; for (let i = 0; i < contractsLen; i++) { const contractPath = config.contracts[i]; const forcTomlPath = getClosestForcTomlDir(contractPath); const binaryPath = getBinaryPath(contractPath, config); const abiPath = getABIPath(contractPath, config); const storageSlotsPath = getStorageSlotsPath(contractPath, config); const projectName = getContractName(contractPath); const contractName = getContractCamelCase(contractPath); const tomlContents = readForcToml(forcTomlPath); const deployConfig = await getDeployConfig(config.deployConfig, { contracts: Array.from(contracts), contractName, contractPath }); const contractId = await deployContract( wallet, binaryPath, abiPath, storageSlotsPath, deployConfig, contractPath, tomlContents ); debug(`Contract deployed: ${projectName} - ${contractId}`); contracts.push({ name: contractName, contractId }); } return contracts; } __name(deployContracts, "deployContracts"); // src/cli/commands/deploy/deployPredicates.ts import { getPredicateRoot, Predicate } from "@fuel-ts/account"; import { debug as debug2, log as log2 } from "console"; import { readFileSync as readFileSync3 } from "fs"; async function deployPredicates(config) { const predicates = []; const wallet = await createWallet(config.providerUrl, config.privateKey); log2(`Deploying predicates to: ${wallet.provider.url}`); const predicatesLen = config.predicates.length; for (let i = 0; i < predicatesLen; i++) { const predicatePath = config.predicates[i]; const binaryPath = getBinaryPath(predicatePath, config); const abiPath = getABIPath(predicatePath, config); const projectName = getPredicateName(predicatePath); const bytecode = readFileSync3(binaryPath); const abi = JSON.parse(readFileSync3(abiPath, "utf-8")); const predicate = new Predicate({ abi, bytecode, provider: wallet.provider }); const { bytes: loaderBytecode, interface: { jsonAbi } } = await (await predicate.deploy(wallet)).waitForResult(); const predicateRoot = getPredicateRoot(loaderBytecode); debug2(`Predicate deployed: ${projectName} - ${predicateRoot}`); predicates.push({ path: predicatePath, predicateRoot, loaderBytecode, abi: jsonAbi }); } return predicates; } __name(deployPredicates, "deployPredicates"); // src/cli/commands/deploy/deployScripts.ts import { Script } from "@fuel-ts/script"; import { debug as debug3, log as log3 } from "console"; import { readFileSync as readFileSync4 } from "fs"; async function deployScripts(config) { const scripts = []; const wallet = await createWallet(config.providerUrl, config.privateKey); log3(`Deploying scripts to: ${wallet.provider.url}`); const scriptsLen = config.scripts.length; for (let i = 0; i < scriptsLen; i++) { const scriptPath = config.scripts[i]; const binaryPath = getBinaryPath(scriptPath, config); const abiPath = getABIPath(scriptPath, config); const projectName = getScriptName(scriptPath); const bytecode = readFileSync4(binaryPath); const abi = JSON.parse(readFileSync4(abiPath, "utf-8")); const script = new Script(bytecode, abi, wallet); const { bytes: loaderBytecode, interface: { jsonAbi } } = await (await script.deploy(wallet)).waitForResult(); debug3(`Script deployed: ${projectName}`); scripts.push({ path: scriptPath, loaderBytecode, abi: jsonAbi }); } return scripts; } __name(deployScripts, "deployScripts"); // src/cli/commands/deploy/saveContractIds.ts import { writeFile, mkdir } from "fs/promises"; import { resolve } from "path"; async function saveContractIds(contracts, output) { const contractsMap = contracts.reduce( (cConfig, { name, contractId }) => ({ ...cConfig, [name]: contractId }), {} ); const filePath = resolve(output, "contract-ids.json"); await mkdir(output, { recursive: true }); await writeFile(filePath, JSON.stringify(contractsMap, null, 2)); log(`Contract IDs saved at: ${filePath}`); } __name(saveContractIds, "saveContractIds"); // src/cli/commands/deploy/savePredicateFiles.ts import { writeFileSync as writeFileSync3 } from "fs"; function savePredicateFiles(predicates, _config) { for (const { path: path2, predicateRoot, loaderBytecode, abi } of predicates) { const predicateName = getPredicateName(path2); const predicateRootPath = `${path2}/out/${predicateName}-loader-bin-root`; writeFileSync3(predicateRootPath, predicateRoot); const loaderBytecodePath = `${path2}/out/${predicateName}-loader.bin`; writeFileSync3(loaderBytecodePath, loaderBytecode); const abiPath = `${path2}/out/${predicateName}-loader-abi.json`; writeFileSync3(abiPath, JSON.stringify(abi, null, 2)); } } __name(savePredicateFiles, "savePredicateFiles"); // src/cli/commands/deploy/saveScriptFiles.ts import { writeFileSync as writeFileSync4 } from "fs"; function saveScriptFiles(scripts, _config) { for (const { path: path2, loaderBytecode, abi } of scripts) { const scriptName = getScriptName(path2); const loaderBytecodePath = `${path2}/out/${scriptName}-loader.bin`; writeFileSync4(loaderBytecodePath, loaderBytecode); const abiPath = `${path2}/out/${scriptName}-loader-abi.json`; writeFileSync4(abiPath, JSON.stringify(abi, null, 2)); } } __name(saveScriptFiles, "saveScriptFiles"); // src/cli/commands/deploy/index.ts async function deploy(config) { const contracts = await deployContracts(config); await saveContractIds(contracts, config.output); const scripts = await deployScripts(config); saveScriptFiles(scripts, config); const predicates = await deployPredicates(config); savePredicateFiles(predicates, config); await config.onDeploy?.(config, { contracts, scripts, predicates }); await generateTypes(config); return { contracts, scripts, predicates }; } __name(deploy, "deploy"); // src/cli/commands/dev/autoStartFuelCore.ts import { defaultConsensusKey } from "@fuel-ts/utils"; import { getPortPromise } from "portfinder"; // src/test-utils.ts var test_utils_exports = {}; __reExport(test_utils_exports, test_utils_star); __reExport(test_utils_exports, test_utils_star2); __reExport(test_utils_exports, test_utils_star3); __reExport(test_utils_exports, test_utils_star4); import * as test_utils_star from "@fuel-ts/contract/test-utils"; import * as test_utils_star2 from "@fuel-ts/account/test-utils"; import * as test_utils_star3 from "@fuel-ts/errors/test-utils"; import * as test_utils_star4 from "@fuel-ts/utils/test-utils"; // src/cli/commands/dev/autoStartFuelCore.ts var autoStartFuelCore = /* @__PURE__ */ __name(async (config) => { let fuelCore; if (config.autoStartFuelCore) { log(`Starting node using: '${config.fuelCorePath}'`); const bindIp = "0.0.0.0"; const accessIp = "127.0.0.1"; const port = config.fuelCorePort ?? await getPortPromise({ port: 4e3 }); const { cleanup, url, snapshotDir } = await (0, test_utils_exports.launchNode)({ args: [ ["--snapshot", config.snapshotDir], ["--db-type", "in-memory"] ].flat(), ip: bindIp, port: port.toString(), loggingEnabled: loggingConfig.isLoggingEnabled, basePath: config.basePath, fuelCorePath: config.fuelCorePath, includeInitialState: true, killProcessOnExit: true }); fuelCore = { bindIp, accessIp, port, providerUrl: url, snapshotDir, killChildProcess: cleanup }; config.providerUrl = fuelCore.providerUrl; config.privateKey = defaultConsensusKey; } return fuelCore; }, "autoStartFuelCore"); // src/cli/commands/build/buildSwayProgram.ts import { spawn } from "child_process"; // src/cli/commands/build/forcHandlers.ts var onForcExit = /* @__PURE__ */ __name((onResultFn, onErrorFn) => (code) => { if (code) { onErrorFn(new Error(`forc exited with error code ${code}`)); } else { onResultFn(); } }, "onForcExit"); var onForcError = /* @__PURE__ */ __name((onError) => (err) => { error(err); onError(err); }, "onForcError"); // src/cli/commands/build/buildSwayProgram.ts var buildSwayProgram = /* @__PURE__ */ __name(async (config, path2) => { debug("Building Sway program", path2); return new Promise((resolve4, reject) => { const args = ["build", "-p", path2].concat(config.forcBuildFlags); const forc = spawn(config.forcPath, args, { stdio: "pipe" }); if (loggingConfig.isLoggingEnabled) { forc.stderr?.on("data", (chunk) => log(chunk.toString())); } if (loggingConfig.isDebugEnabled) { forc.stdout?.on("data", (chunk) => { debug(chunk.toString()); }); } const onExit = onForcExit(resolve4, reject); const onError = onForcError(reject); forc.on("exit", onExit); forc.on("error", onError); }); }, "buildSwayProgram"); // src/cli/commands/build/buildSwayPrograms.ts async function buildSwayPrograms(config) { log(`Building Sway programs using: '${config.forcPath}'`); const paths = config.workspace ? [config.workspace] : [config.contracts, config.predicates, config.scripts].flat(); await Promise.all(paths.map((path2) => buildSwayProgram(config, path2))); } __name(buildSwayPrograms, "buildSwayPrograms"); // src/cli/commands/build/index.ts async function build(config, program) { log("Building.."); await buildSwayPrograms(config); await generateTypes(config); await config.onBuild?.(config); const options = program?.opts(); if (options?.deploy) { const fuelCore = await autoStartFuelCore(config); await deploy(config); fuelCore?.killChildProcess(); } } __name(build, "build"); // src/cli/commands/dev/index.ts import { watch } from "chokidar"; import { globSync as globSync3 } from "glob"; // src/cli/config/loadConfig.ts import { FuelError as FuelError3 } from "@fuel-ts/errors"; import { defaultConsensusKey as defaultConsensusKey2 } from "@fuel-ts/utils"; import { bundleRequire } from "bundle-require"; import JoyCon from "joycon"; import { resolve as resolve2, parse } from "path"; // src/cli-utils.ts var cli_utils_exports = {}; __reExport(cli_utils_exports, cli_utils_star); import * as cli_utils_star from "@fuel-ts/utils/cli-utils"; // src/cli/config/validateConfig.ts import * as yup from "yup"; var schema = yup.object({ workspace: yup.string(), contracts: yup.array(yup.string()), scripts: yup.array(yup.string()), predicates: yup.array(yup.string()), output: yup.string().required("config.output should be a valid string") }).required(); async function validateConfig(config) { return schema.validate(config); } __name(validateConfig, "validateConfig"); // src/cli/config/loadConfig.ts async function loadUserConfig(cwd) { const configJoycon = new JoyCon(); const configPath = await configJoycon.resolve({ files: ["ts", "js", "cjs", "mjs"].map((e) => `fuels.config.${e}`), cwd, stopDir: parse(cwd).root }); if (!configPath) { throw new FuelError3(FuelError3.CODES.CONFIG_FILE_NOT_FOUND, "Config file not found!"); } const esbuildOptions = { target: "ES2021", platform: "node", format: "esm" }; const result = await bundleRequire({ filepath: configPath, esbuildOptions, cwd }); const userConfig = result.mod.default; return { configPath, userConfig }; } __name(loadUserConfig, "loadUserConfig"); async function loadConfig(cwd) { const { configPath, userConfig } = await loadUserConfig(cwd); await validateConfig(userConfig); const { forcBuildFlags = [] } = userConfig; const releaseFlag = forcBuildFlags.find((f) => f === "--release"); const buildMode = releaseFlag ? "release" : "debug"; const { forcPath, fuelCorePath } = (0, cli_utils_exports.tryFindBinaries)({ forcPath: userConfig.forcPath, fuelCorePath: userConfig.fuelCorePath }); const config = { contracts: [], scripts: [], predicates: [], deployConfig: {}, autoStartFuelCore: true, fuelCorePort: 4e3, providerUrl: process.env.FUEL_NETWORK_URL ?? "http://127.0.0.1:4000/v1/graphql", privateKey: defaultConsensusKey2, ...userConfig, basePath: cwd, forcPath, fuelCorePath, configPath, forcBuildFlags, buildMode }; config.output = resolve2(cwd, config.output); config.autoStartFuelCore = userConfig.autoStartFuelCore ?? true; if (!userConfig.workspace) { const { contracts, predicates, scripts } = userConfig; config.contracts = (contracts || []).map((c) => resolve2(cwd, c)); config.scripts = (scripts || []).map((s) => resolve2(cwd, s)); config.predicates = (predicates || []).map((p) => resolve2(cwd, p)); } else { const workspace = resolve2(cwd, userConfig.workspace); const forcToml = readForcToml(workspace); if (!forcToml.workspace) { const workspaceMsg = `Forc workspace not detected in: ${workspace}/Forc.toml`; const swayProgramType = readSwayType(workspace); const exampleMsg = `Try using '${swayProgramType}s' instead of 'workspace' in: ${configPath}`; throw new FuelError3( FuelError3.CODES.WORKSPACE_NOT_DETECTED, [workspaceMsg, exampleMsg].join("\n\n") ); } const swayMembers = forcToml.workspace.members.map((member) => resolve2(workspace, member)); swayMembers.map((path2) => ({ path: path2, type: readSwayType(path2) })).filter(({ type }) => type !== "library" /* library */).forEach(({ path: path2, type }) => config[`${type}s`].push(path2)); config.workspace = workspace; } return config; } __name(loadConfig, "loadConfig"); // src/cli/commands/withConfig.ts import { capitalizeString } from "@fuel-ts/utils"; var withConfigErrorHandler = /* @__PURE__ */ __name(async (err, config) => { error(err.message); await config?.onFailure?.(config, err); throw err; }, "withConfigErrorHandler"); function withConfig(program, command, fn) { return async () => { const options = program.opts(); let config; try { config = await loadConfig(options.path); } catch (err) { await withConfigErrorHandler(err); return; } try { await fn(config, program); log(`\u{1F389} ${capitalizeString(command)} completed successfully!`); } catch (err) { await withConfigErrorHandler(err, config); } }; } __name(withConfig, "withConfig"); // src/cli/commands/dev/index.ts var closeAllFileHandlers = /* @__PURE__ */ __name((handlers) => { handlers.forEach((h) => h.close()); }, "closeAllFileHandlers"); var buildAndDeploy = /* @__PURE__ */ __name(async (config) => { await build(config); const deployedContracts = await deploy(config); await config.onDev?.(config); return deployedContracts; }, "buildAndDeploy"); var getConfigFilepathsToWatch = /* @__PURE__ */ __name((config) => { const configFilePathsToWatch = [config.configPath]; if (config.snapshotDir) { configFilePathsToWatch.push(config.snapshotDir); } return configFilePathsToWatch; }, "getConfigFilepathsToWatch"); var workspaceFileChanged = /* @__PURE__ */ __name((state) => async (_event, path2) => { log(` File changed: ${path2}`); await buildAndDeploy(state.config); }, "workspaceFileChanged"); var configFileChanged = /* @__PURE__ */ __name((state) => async (_event, path2) => { log(` File changed: ${path2}`); closeAllFileHandlers(state.watchHandlers); state.fuelCore?.killChildProcess(); try { await dev(await loadConfig(state.config.basePath)); } catch (err) { await withConfigErrorHandler(err, state.config); } }, "configFileChanged"); var dev = /* @__PURE__ */ __name(async (config) => { const fuelCore = await autoStartFuelCore(config); const configFilePaths = getConfigFilepathsToWatch(config); const { contracts, scripts, predicates, basePath: cwd } = config; const workspaceFilePaths = [contracts, predicates, scripts].flat().flatMap((dir) => [ dir, globSync3(`${dir}/**/*.toml`, { cwd }), globSync3(`${dir}/**/*.sw`, { cwd }) ]).flat(); try { await buildAndDeploy(config); const watchHandlers = []; const options = { persistent: true, ignoreInitial: true, ignored: "**/out/**" }; const state = { config, watchHandlers, fuelCore }; watchHandlers.push(watch(configFilePaths, options).on("all", configFileChanged(state))); watchHandlers.push(watch(workspaceFilePaths, options).on("all", workspaceFileChanged(state))); } catch (err) { error(err); throw err; } }, "dev"); // src/cli/commands/init/index.ts import { FuelError as FuelError4 } from "@fuel-ts/errors"; import { existsSync as existsSync3, statSync, writeFileSync as writeFileSync5 } from "fs"; import { dirname as dirname2, join as join3, relative, resolve as resolve3 } from "path"; // src/cli/templates/fuels.config.ts import Handlebars2 from "handlebars"; // src/cli/templates/fuels.config.hbs var fuels_config_default = "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"; // src/cli/templates/fuels.config.ts Handlebars2.registerHelper("isDefined", (v) => v !== void 0); function renderFuelsConfigTemplate(props) { const renderTemplate = Handlebars2.compile(fuels_config_default, { strict: true, noEscape: true }); return renderTemplate(props); } __name(renderFuelsConfigTemplate, "renderFuelsConfigTemplate"); // src/cli/commands/init/index.ts function init(program) { const options = program.opts(); const { path: path2, autoStartFuelCore: autoStartFuelCore2, forcPath, fuelCorePath, fuelCorePort } = options; let workspace; let absoluteWorkspace; if (options.workspace) { absoluteWorkspace = resolve3(path2, options.workspace); workspace = `./${relative(path2, absoluteWorkspace)}`; } const absoluteOutput = resolve3(path2, options.output); const output = `./${relative(path2, absoluteOutput)}`; const convertFilePathToDir = /* @__PURE__ */ __name((filePath) => statSync(resolve3(path2, filePath)).isDirectory() ? filePath : dirname2(filePath), "convertFilePathToDir"); const [contracts, scripts, predicates] = ["contracts", "scripts", "predicates"].map( (optionName) => { const paths = options[optionName]; if (!paths) { return void 0; } const selectedSwayType = optionName.slice(0, -1); const programs = paths.map(convertFilePathToDir).flatMap((pathOrGlob) => findPrograms(pathOrGlob, { cwd: path2 })); const programDirs = programs.filter(({ swayType }) => swayType === selectedSwayType).map(({ path: programPath }) => relative(path2, programPath)); return [...new Set(programDirs)]; } ); const noneIsInformed = ![workspace, contracts, scripts, predicates].find((v) => v !== void 0); if (noneIsInformed) { console.log(`error: required option '-w, --workspace <path>' not specified\r`); process.exit(1); } const programLengths = [contracts, scripts, predicates].filter(Boolean).map((programs) => programs?.length); if (programLengths.some((length) => length === 0)) { const [contractLength, scriptLength, predicateLength] = programLengths; const message = ["error: unable to detect program/s"]; if (contractLength === 0) { message.push(`- contract/s detected ${contractLength}`); } if (scriptLength === 0) { message.push(`- script/s detected ${scriptLength}`); } if (predicateLength === 0) { message.push(`- predicate/s detected ${predicateLength}`); } log(message.join("\r\n")); process.exit(1); } const fuelsConfigPath = join3(path2, "fuels.config.ts"); if (existsSync3(fuelsConfigPath)) { throw new FuelError4( FuelError4.CODES.CONFIG_FILE_ALREADY_EXISTS, `Config file exists, aborting. ${fuelsConfigPath}` ); } const renderedConfig = renderFuelsConfigTemplate({ workspace, contracts, scripts, predicates, output, forcPath, fuelCorePath, autoStartFuelCore: autoStartFuelCore2, fuelCorePort }); writeFileSync5(fuelsConfigPath, renderedConfig); log(`Config file created at: ${fuelsConfigPath} `); } __name(init, "init"); // src/cli/commands/node/index.ts import { watch as watch2 } from "chokidar"; var getConfigFilepathsToWatch2 = /* @__PURE__ */ __name((config) => { const configFilePathsToWatch = [config.configPath]; if (config.snapshotDir) { configFilePathsToWatch.push(config.snapshotDir); } return configFilePathsToWatch; }, "getConfigFilepathsToWatch"); var closeAllFileHandlers2 = /* @__PURE__ */ __name((handlers) => { handlers.forEach((h) => h.close()); }, "closeAllFileHandlers"); var configFileChanged2 = /* @__PURE__ */ __name((state) => async (_event, path2) => { log(` File changed: ${path2}`); closeAllFileHandlers2(state.watchHandlers); state.fuelCore?.killChildProcess(); try { await node(await loadConfig(state.config.basePath)); await state.config.onNode?.(state.config); } catch (err) { await withConfigErrorHandler(err, state.config); } }, "configFileChanged"); var node = /* @__PURE__ */ __name(async (config) => { const fuelCore = await autoStartFuelCore(config); const configFilePaths = getConfigFilepathsToWatch2(config); try { const watchHandlers = []; const options = { persistent: true, ignoreInitial: true, ignored: "**/out/**" }; const state = { config, watchHandlers, fuelCore }; watchHandlers.push(watch2(configFilePaths, options).on("all", configFileChanged2(state))); } catch (err) { error(err); throw err; } }, "node"); // src/cli/commands/withBinaryPaths.ts function withBinaryPaths(program, _command, fn) { return async () => { const options = program.opts(); const paths = {}; try { const { userConfig } = await loadUserConfig(options.path); paths.forcPath = userConfig.forcPath; paths.fuelCorePath = userConfig.fuelCorePath; } catch (err) { debug(err.message); } try { await fn(paths); } catch (err) { error(err); } }; } __name(withBinaryPaths, "withBinaryPaths"); // src/cli/commands/withProgram.ts function withProgram(program, _command, fn) { return async () => { try { await fn(program); } catch (err) { error(err); } }; } __name(withProgram, "withProgram"); // src/cli.ts var onPreAction = /* @__PURE__ */ __name((command) => { const opts = command.opts(); configureLogging({ isDebugEnabled: opts.debug, isLoggingEnabled: !opts.silent }); }, "onPreAction"); var configureCli = /* @__PURE__ */ __name(() => { const program = new Command(); program.name("fuels"); program.option("-D, --debug", "Enables verbose logging", false); program.option("-S, --silent", "Omit output messages", false); program.version(versions.FUELS, "-v, --version", "Output the version number"); program.helpOption("-h, --help", "Display help"); program.helpCommand("help [command]", "Display help for command"); program.enablePositionalOptions(true); program.hook("preAction", onPreAction); const pathOption = new Option("--path <path>", "Path to project root").default(process.cwd()); let command; (command = program.command("init" /* init */)).description("Create a sample `fuel.config.ts` file").addOption(pathOption).option("-w, --workspace <path>", "Relative dir path to Forc workspace").addOption( new Option(`-c, --contracts [paths...]`, `Relative paths to Contracts`).conflicts("workspace") ).addOption( new Option(`-s, --scripts [paths...]`, `Relative paths to Scripts`).conflicts("workspace") ).addOption( new Option(`-p, --predicates [paths...]`, `Relative paths to Predicates`).conflicts( "workspace" ) ).requiredOption("-o, --output <path>", "Relative dir path for Typescript generation output").option("--forc-path <path>", "Path to the `forc` binary").option("--fuel-core-path <path>", "Path to the `fuel-core` binary").option("--auto-start-fuel-core", "Auto-starts a `fuel-core` node during `dev` command").option( "--fuel-core-port <port>", "Port to use when starting a local `fuel-core` node for dev mode" ).action(withProgram(command, "init" /* init */, init)); (command = program.command("dev" /* dev */)).description("Start a Fuel node with hot-reload capabilities").addOption(pathOption).action(withConfig(command, "dev" /* dev */, dev)); (command = program.command("node" /* node */)).description("Start a Fuel node using project configs").addOption(pathOption).action(withConfig(command, "node" /* node */, node)); (command = program.command("build" /* build */)).description("Build Sway programs and generate Typescript for them").addOption(pathOption).option( "-d, --deploy", "Deploy contracts after build (auto-starts a `fuel-core` node if needed)" ).action(withConfig(command, "build" /* build */, build)); (command = program.command("deploy" /* deploy */)).description("Deploy contracts to the Fuel network").addOption(pathOption).action(withConfig(command, "deploy" /* deploy */, deploy)); configureTypegenCliOptions( program.command("typegen").description(`Generate Typescript from Sway ABI JSON files`) ); (command = program.command("versions")).description("Check for version incompatibilities").addOption(pathOption).action(withBinaryPaths(command, "versions" /* versions */, runVersions)); return program; }, "configureCli"); // src/cli/utils/checkForAndDisplayUpdates.ts import { versions as versions2, gt, eq } from "@fuel-ts/versions"; // src/cli/utils/fuelsVersionCache.ts import fs from "fs"; import path from "path"; var FUELS_VERSION_CACHE_FILE = path.join(__dirname, "FUELS_VERSION"); var saveToCache = /* @__PURE__ */ __name((cache) => { fs.writeFileSync(FUELS_VERSION_CACHE_FILE, cache, "utf-8"); }, "saveToCache"); var FUELS_VERSION_CACHE_TTL = 6 * 60 * 60 * 1e3; var checkAndLoadCache = /* @__PURE__ */ __name(() => { const doesVersionCacheExist = fs.existsSync(FUELS_VERSION_CACHE_FILE); if (doesVersionCacheExist) { const cachedVersion = fs.readFileSync(FUELS_VERSION_CACHE_FILE, "utf-8").trim(); if (!cachedVersion) { return null; } const { mtimeMs: cacheTimestamp } = fs.statSync(FUELS_VERSION_CACHE_FILE); const hasCacheExpired = Date.now() - cacheTimestamp > FUELS_VERSION_CACHE_TTL; return hasCacheExpired ? null : cachedVersion; } return null; }, "checkAndLoadCache"); // src/cli/utils/getLatestFuelsVersion.ts var getLatestFuelsVersion = /* @__PURE__ */ __name(async () => { const cachedVersion = checkAndLoadCache(); if (cachedVersion) { return cachedVersion; } const data = await Promise.race([ new Promise((_, reject) => { setTimeout(() => reject(null), 3e3); }), fetch("https://registry.npmjs.org/fuels/latest").then((response) => response.json()) ]); if (!data) { throw new Error("Failed to fetch latest fuels version."); } const version = data.version; saveToCache(version); return version; }, "getLatestFuelsVersion"); // src/cli/utils/checkForAndDisplayUpdates.ts var checkForAndDisplayUpdates = /* @__PURE__ */ __name(async () => { try { const { FUELS: userFuelsVersion } = versions2; const latestFuelsVersion = await getLatestFuelsVersion(); if (!latestFuelsVersion) { log(` Unable to fetch latest fuels version. Skipping... `); return; } const isFuelsVersionOutdated = gt(latestFuelsVersion, userFuelsVersion); const isFuelsVersionUpToDate = eq(latestFuelsVersion, userFuelsVersion); if (isFuelsVersionOutdated) { warn( ` \u26A0\uFE0F There is a newer version of fuels available: ${latestFuelsVersion}. Your version is: ${userFuelsVersion} ` ); return; } if (isFuelsVersionUpToDate) { log(` \u2705 Your fuels version is up to date: ${userFuelsVersion} `); } } catch { log(` Unable to fetch latest fuels version. Skipping... `); } }, "checkForAndDisplayUpdates"); // src/run.ts var run = /* @__PURE__ */ __name(async (argv) => { await checkForAndDisplayUpdates().catch(error); const program = configureCli(); return program.parseAsync(argv); }, "run"); // src/bin.ts run(process.argv).catch((err) => { error(err?.message || err); process.exit(1); }); //# sourceMappingURL=bin.mjs.map