UNPKG

fuels

Version:

Fuel TS SDK

1,135 lines (1,086 loc) • 45.7 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; 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")); var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); // src/cli/utils/logger.ts var import_chalk = __toESM(require("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(import_chalk.default.red(data.join(" "))); } __name(error, "error"); function warn(...data) { log(import_chalk.default.yellow(data.join(" "))); } __name(warn, "warn"); // src/cli.ts var import_cli2 = require("@fuel-ts/abi-typegen/cli"); var import_versions = require("@fuel-ts/versions"); var import_cli3 = require("@fuel-ts/versions/cli"); var import_commander = require("commander"); // src/cli/commands/build/generateTypes.ts var import_abi_typegen = require("@fuel-ts/abi-typegen"); var import_runTypegen = require("@fuel-ts/abi-typegen/runTypegen"); var import_cli = require("@fuel-ts/versions/cli"); var import_fs2 = require("fs"); var import_glob2 = require("glob"); var import_path2 = require("path"); // src/cli/config/forcUtils.ts var import_errors = require("@fuel-ts/errors"); var import_fs = require("fs"); var import_glob = require("glob"); var import_lodash = __toESM(require("lodash.camelcase")); var import_path = require("path"); var import_toml = __toESM(require("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 = (0, import_path.join)(dir, "Forc.toml"); if ((0, import_fs.existsSync)(forcPath)) { return forcPath; } const parent = (0, import_path.join)(dir, ".."); forcPath = getClosestForcTomlDir(parent); if (parent === "/" && !(0, import_fs.existsSync)(forcPath)) { const msg = `TOML file not found: ${dir}`; throw new import_errors.FuelError(import_errors.FuelError.CODES.CONFIG_FILE_NOT_FOUND, msg); } return forcPath; }, "getClosestForcTomlDir"); function readForcToml(contractPath) { if (!(0, import_fs.existsSync)(contractPath)) { throw new import_errors.FuelError( import_errors.FuelError.CODES.CONFIG_FILE_NOT_FOUND, `TOML file not found: ${contractPath}` ); } const forcPath = getClosestForcTomlDir(contractPath); if (!(0, import_fs.existsSync)(forcPath)) { throw new import_errors.FuelError( import_errors.FuelError.CODES.CONFIG_FILE_NOT_FOUND, `TOML file not found: ${forcPath}` ); } const forcFile = (0, import_fs.readFileSync)(forcPath, "utf8"); return import_toml.default.parse(forcFile); } __name(readForcToml, "readForcToml"); function setForcTomlProxyAddress(contractPath, address) { const forcPath = getClosestForcTomlDir(contractPath); const tomlPristine = (0, import_fs.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); (0, import_fs.writeFileSync)(forcPath, modifiedToml); return address; } __name(setForcTomlProxyAddress, "setForcTomlProxyAddress"); function readSwayType(path2) { const forcToml = readForcToml(path2); const entryFile = forcToml.project.entry || "main.sw"; const swayEntryPath = (0, import_path.join)(path2, "src", entryFile); if (!swayFiles.has(swayEntryPath)) { const swayFile = (0, import_fs.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 (0, import_lodash.default)(projectName); } __name(getContractCamelCase, "getContractCamelCase"); function getBinaryPath(contractPath, { buildMode }) { const projectName = getContractName(contractPath); return (0, import_path.join)(contractPath, `/out/${buildMode}/${projectName}.bin`); } __name(getBinaryPath, "getBinaryPath"); function getABIPath(contractPath, { buildMode }) { const projectName = getContractName(contractPath); return (0, import_path.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 (0, import_path.join)(contractPath, `/out/${buildMode}/${projectName}-storage_slots.json`); }, "getStorageSlotsPath"); var findPrograms = /* @__PURE__ */ __name((pathOrGlob, opts) => { const pathWithoutGlob = pathOrGlob.replace(/[/][*]*$/, "").replace(opts?.cwd ?? "", ""); const absolutePath = (0, import_path.join)(opts?.cwd ?? "", pathWithoutGlob); const allTomlPaths = (0, import_glob.globSync)(`${absolutePath}/**/*.toml`); return allTomlPaths.map((path2) => ({ path: path2, isWorkspace: readForcToml(path2).workspace !== void 0 })).filter(({ isWorkspace }) => !isWorkspace).map(({ path: path2 }) => ({ path: (0, import_path.dirname)(path2), swayType: readSwayType((0, import_path.dirname)(path2)) })).filter(({ swayType }) => swayType !== "library" /* library */); }, "findPrograms"); // src/cli/templates/index.ts var import_handlebars = __toESM(require("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 = import_handlebars.default.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 = (0, import_cli.getBinaryVersions)(config); const isScript = programType === import_abi_typegen.ProgramTypeEnum.SCRIPT; const isPredicate = programType === import_abi_typegen.ProgramTypeEnum.PREDICATE; if (isScript || isPredicate) { const loaderFiles = paths.flatMap((dirpath) => { const glob = `*-abi.json`; const cwd = `${dirpath}/out`; return (0, import_glob2.globSync)(glob, { cwd }).map((filename) => `${dirpath}/out/${filename}`); }); filepaths = filepaths.concat(loaderFiles); } (0, import_runTypegen.runTypegen)({ programType, cwd: config.basePath, filepaths, output: (0, import_path2.join)(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; (0, import_fs2.mkdirSync)(output, { recursive: true }); const members = [ { type: import_abi_typegen.ProgramTypeEnum.CONTRACT, programs: contracts }, { type: import_abi_typegen.ProgramTypeEnum.SCRIPT, programs: scripts }, { type: import_abi_typegen.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); (0, import_fs2.writeFileSync)((0, import_path2.join)(config.output, "index.ts"), indexFile); } __name(generateTypes, "generateTypes"); // src/cli/commands/deploy/deployContracts.ts var import_contract = require("@fuel-ts/contract"); var import_program = require("@fuel-ts/program"); var import_recipes = require("@fuel-ts/recipes"); var import_fs3 = require("fs"); // src/cli/commands/deploy/createWallet.ts var import_account = require("@fuel-ts/account"); var import_errors2 = require("@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 import_errors2.FuelError( import_errors2.FuelError.CODES.MISSING_REQUIRED_PARAMETER, "You must provide a privateKey via config.privateKey or env PRIVATE_KEY" ); } try { const provider = new import_account.Provider(providerUrl); await provider.init(); return import_account.Wallet.fromPrivateKey(pvtKey, provider); } catch (e) { const error2 = e; if (/EADDRNOTAVAIL|ECONNREFUSED/.test(error2.cause?.code ?? "")) { throw new import_errors2.FuelError( import_errors2.FuelError.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 ((0, import_fs3.existsSync)(storageSlotsPath)) { const storageSlots2 = JSON.parse((0, import_fs3.readFileSync)(storageSlotsPath, "utf-8")); deployConfig.storageSlots = storageSlots2; } const targetBytecode = (0, import_fs3.readFileSync)(binaryPath); const targetAbi = JSON.parse((0, import_fs3.readFileSync)(abiPath, "utf-8")); const targetStorageSlots = deployConfig.storageSlots ?? []; const proxyBytecode = import_recipes.Src14OwnedProxyFactory.bytecode; const proxyAbi = import_recipes.Src14OwnedProxy.abi; const proxyStorageSlots = import_recipes.Src14OwnedProxy.storageSlots ?? []; const isProxyEnabled = tomlContents?.proxy?.enabled; const proxyAddress = tomlContents?.proxy?.address; if (!isProxyEnabled) { const contractFactory = new import_contract.ContractFactory(targetBytecode, targetAbi, wallet); const { waitForResult } = await contractFactory.deploy(deployConfig); const { contract } = await waitForResult(); return contract.id.toB256(); } if (proxyAddress) { const targetContractFactory2 = new import_contract.ContractFactory(targetBytecode, targetAbi, wallet); const { waitForResult: waitForTarget2 } = await targetContractFactory2.deploy(deployConfig); const { contract: targetContract2 } = await waitForTarget2(); const proxyContract2 = new import_program.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 import_contract.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 import_contract.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 var import_account2 = require("@fuel-ts/account"); var import_console = require("console"); var import_fs4 = require("fs"); async function deployPredicates(config) { const predicates = []; const wallet = await createWallet(config.providerUrl, config.privateKey); (0, import_console.log)(`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 = (0, import_fs4.readFileSync)(binaryPath); const abi = JSON.parse((0, import_fs4.readFileSync)(abiPath, "utf-8")); const predicate = new import_account2.Predicate({ abi, bytecode, provider: wallet.provider }); const { bytes: loaderBytecode, interface: { jsonAbi } } = await (await predicate.deploy(wallet)).waitForResult(); const predicateRoot = (0, import_account2.getPredicateRoot)(loaderBytecode); (0, import_console.debug)(`Predicate deployed: ${projectName} - ${predicateRoot}`); predicates.push({ path: predicatePath, predicateRoot, loaderBytecode, abi: jsonAbi }); } return predicates; } __name(deployPredicates, "deployPredicates"); // src/cli/commands/deploy/deployScripts.ts var import_script = require("@fuel-ts/script"); var import_console2 = require("console"); var import_fs5 = require("fs"); async function deployScripts(config) { const scripts = []; const wallet = await createWallet(config.providerUrl, config.privateKey); (0, import_console2.log)(`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 = (0, import_fs5.readFileSync)(binaryPath); const abi = JSON.parse((0, import_fs5.readFileSync)(abiPath, "utf-8")); const script = new import_script.Script(bytecode, abi, wallet); const { bytes: loaderBytecode, interface: { jsonAbi } } = await (await script.deploy(wallet)).waitForResult(); (0, import_console2.debug)(`Script deployed: ${projectName}`); scripts.push({ path: scriptPath, loaderBytecode, abi: jsonAbi }); } return scripts; } __name(deployScripts, "deployScripts"); // src/cli/commands/deploy/saveContractIds.ts var import_promises = require("fs/promises"); var import_path3 = require("path"); async function saveContractIds(contracts, output) { const contractsMap = contracts.reduce( (cConfig, { name, contractId }) => ({ ...cConfig, [name]: contractId }), {} ); const filePath = (0, import_path3.resolve)(output, "contract-ids.json"); await (0, import_promises.mkdir)(output, { recursive: true }); await (0, import_promises.writeFile)(filePath, JSON.stringify(contractsMap, null, 2)); log(`Contract IDs saved at: ${filePath}`); } __name(saveContractIds, "saveContractIds"); // src/cli/commands/deploy/savePredicateFiles.ts var import_fs6 = require("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`; (0, import_fs6.writeFileSync)(predicateRootPath, predicateRoot); const loaderBytecodePath = `${path2}/out/${predicateName}-loader.bin`; (0, import_fs6.writeFileSync)(loaderBytecodePath, loaderBytecode); const abiPath = `${path2}/out/${predicateName}-loader-abi.json`; (0, import_fs6.writeFileSync)(abiPath, JSON.stringify(abi, null, 2)); } } __name(savePredicateFiles, "savePredicateFiles"); // src/cli/commands/deploy/saveScriptFiles.ts var import_fs7 = require("fs"); function saveScriptFiles(scripts, _config) { for (const { path: path2, loaderBytecode, abi } of scripts) { const scriptName = getScriptName(path2); const loaderBytecodePath = `${path2}/out/${scriptName}-loader.bin`; (0, import_fs7.writeFileSync)(loaderBytecodePath, loaderBytecode); const abiPath = `${path2}/out/${scriptName}-loader-abi.json`; (0, import_fs7.writeFileSync)(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 var import_utils = require("@fuel-ts/utils"); var import_portfinder = require("portfinder"); // src/test-utils.ts var test_utils_exports = {}; __reExport(test_utils_exports, require("@fuel-ts/contract/test-utils")); __reExport(test_utils_exports, require("@fuel-ts/account/test-utils")); __reExport(test_utils_exports, require("@fuel-ts/errors/test-utils")); __reExport(test_utils_exports, require("@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 (0, import_portfinder.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 = import_utils.defaultConsensusKey; } return fuelCore; }, "autoStartFuelCore"); // src/cli/commands/build/buildSwayProgram.ts var import_child_process = require("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 = (0, import_child_process.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 var import_chokidar = require("chokidar"); var import_glob3 = require("glob"); // src/cli/config/loadConfig.ts var import_errors3 = require("@fuel-ts/errors"); var import_utils2 = require("@fuel-ts/utils"); var import_bundle_require = require("bundle-require"); var import_joycon = __toESM(require("joycon")); var import_path4 = require("path"); // src/cli-utils.ts var cli_utils_exports = {}; __reExport(cli_utils_exports, require("@fuel-ts/utils/cli-utils")); // src/cli/config/validateConfig.ts var yup = __toESM(require("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 import_joycon.default(); const configPath = await configJoycon.resolve({ files: ["ts", "js", "cjs", "mjs"].map((e) => `fuels.config.${e}`), cwd, stopDir: (0, import_path4.parse)(cwd).root }); if (!configPath) { throw new import_errors3.FuelError(import_errors3.FuelError.CODES.CONFIG_FILE_NOT_FOUND, "Config file not found!"); } const esbuildOptions = { target: "ES2021", platform: "node", format: "esm" }; const result = await (0, import_bundle_require.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: import_utils2.defaultConsensusKey, ...userConfig, basePath: cwd, forcPath, fuelCorePath, configPath, forcBuildFlags, buildMode }; config.output = (0, import_path4.resolve)(cwd, config.output); config.autoStartFuelCore = userConfig.autoStartFuelCore ?? true; if (!userConfig.workspace) { const { contracts, predicates, scripts } = userConfig; config.contracts = (contracts || []).map((c) => (0, import_path4.resolve)(cwd, c)); config.scripts = (scripts || []).map((s) => (0, import_path4.resolve)(cwd, s)); config.predicates = (predicates || []).map((p) => (0, import_path4.resolve)(cwd, p)); } else { const workspace = (0, import_path4.resolve)(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 import_errors3.FuelError( import_errors3.FuelError.CODES.WORKSPACE_NOT_DETECTED, [workspaceMsg, exampleMsg].join("\n\n") ); } const swayMembers = forcToml.workspace.members.map((member) => (0, import_path4.resolve)(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 var import_utils3 = require("@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} ${(0, import_utils3.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, (0, import_glob3.globSync)(`${dir}/**/*.toml`, { cwd }), (0, import_glob3.globSync)(`${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((0, import_chokidar.watch)(configFilePaths, options).on("all", configFileChanged(state))); watchHandlers.push((0, import_chokidar.watch)(workspaceFilePaths, options).on("all", workspaceFileChanged(state))); } catch (err) { error(err); throw err; } }, "dev"); // src/cli/commands/init/index.ts var import_errors4 = require("@fuel-ts/errors"); var import_fs8 = require("fs"); var import_path5 = require("path"); // src/cli/templates/fuels.config.ts var import_handlebars2 = __toESM(require("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 import_handlebars2.default.registerHelper("isDefined", (v) => v !== void 0); function renderFuelsConfigTemplate(props) { const renderTemplate = import_handlebars2.default.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 = (0, import_path5.resolve)(path2, options.workspace); workspace = `./${(0, import_path5.relative)(path2, absoluteWorkspace)}`; } const absoluteOutput = (0, import_path5.resolve)(path2, options.output); const output = `./${(0, import_path5.relative)(path2, absoluteOutput)}`; const convertFilePathToDir = /* @__PURE__ */ __name((filePath) => (0, import_fs8.statSync)((0, import_path5.resolve)(path2, filePath)).isDirectory() ? filePath : (0, import_path5.dirname)(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 }) => (0, import_path5.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 = (0, import_path5.join)(path2, "fuels.config.ts"); if ((0, import_fs8.existsSync)(fuelsConfigPath)) { throw new import_errors4.FuelError( import_errors4.FuelError.CODES.CONFIG_FILE_ALREADY_EXISTS, `Config file exists, aborting. ${fuelsConfigPath}` ); } const renderedConfig = renderFuelsConfigTemplate({ workspace, contracts, scripts, predicates, output, forcPath, fuelCorePath, autoStartFuelCore: autoStartFuelCore2, fuelCorePort }); (0, import_fs8.writeFileSync)(fuelsConfigPath, renderedConfig); log(`Config file created at: ${fuelsConfigPath} `); } __name(init, "init"); // src/cli/commands/node/index.ts var import_chokidar2 = require("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((0, import_chokidar2.watch)(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 import_commander.Command(); program.name("fuels"); program.option("-D, --debug", "Enables verbose logging", false); program.option("-S, --silent", "Omit output messages", false); program.version(import_versions.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 import_commander.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 import_commander.Option(`-c, --contracts [paths...]`, `Relative paths to Contracts`).conflicts("workspace") ).addOption( new import_commander.Option(`-s, --scripts [paths...]`, `Relative paths to Scripts`).conflicts("workspace") ).addOption( new import_commander.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)); (0, import_cli2.configureCliOptions)( 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 */, import_cli3.runVersions)); return program; }, "configureCli"); // src/cli/utils/checkForAndDisplayUpdates.ts var import_versions2 = require("@fuel-ts/versions"); // src/cli/utils/fuelsVersionCache.ts var import_fs9 = __toESM(require("fs")); var import_path6 = __toESM(require("path")); var FUELS_VERSION_CACHE_FILE = import_path6.default.join(__dirname, "FUELS_VERSION"); var saveToCache = /* @__PURE__ */ __name((cache) => { import_fs9.default.writeFileSync(FUELS_VERSION_CACHE_FILE, cache, "utf-8"); }, "saveToCache"); var FUELS_VERSION_CACHE_TTL = 6 * 60 * 60 * 1e3; var checkAndLoadCache = /* @__PURE__ */ __name(() => { const doesVersionCacheExist = import_fs9.default.existsSync(FUELS_VERSION_CACHE_FILE); if (doesVersionCacheExist) { const cachedVersion = import_fs9.default.readFileSync(FUELS_VERSION_CACHE_FILE, "utf-8").trim(); if (!cachedVersion) { return null; } const { mtimeMs: cacheTimestamp } = import_fs9.default.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 } = import_versions2.versions; const latestFuelsVersion = await getLatestFuelsVersion(); if (!latestFuelsVersion) { log(` Unable to fetch latest fuels version. Skipping... `); return; } const isFuelsVersionOutdated = (0, import_versions2.gt)(latestFuelsVersion, userFuelsVersion); const isFuelsVersionUpToDate = (0, import_versions2.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.js.map