fuels
Version:
Fuel TS SDK
1,135 lines (1,086 loc) • 45.7 kB
JavaScript
;
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