@caatinga/core 2.3.1 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -37,16 +37,19 @@ __export(index_exports, {
37
37
  CaatingaConfigSchema: () => CaatingaConfigSchema,
38
38
  CaatingaError: () => CaatingaError,
39
39
  CaatingaErrorCode: () => CaatingaErrorCode,
40
+ READ_CALL_FAILURE_REGEX: () => READ_CALL_FAILURE_REGEX,
40
41
  STELLAR_CLI_LAST_TESTED_VERSION: () => STELLAR_CLI_LAST_TESTED_VERSION,
41
42
  STELLAR_CLI_MIN_VERSION: () => STELLAR_CLI_MIN_VERSION,
42
43
  TemplateManifestSchema: () => TemplateManifestSchema,
43
44
  WELL_KNOWN_NETWORKS: () => WELL_KNOWN_NETWORKS,
44
45
  buildContract: () => buildContract,
45
46
  buildDependencyGraph: () => buildDependencyGraph,
47
+ buildReadCallHint: () => buildReadCallHint,
46
48
  checkBinary: () => checkBinary,
47
49
  checkStellarCliVersion: () => checkStellarCliVersion,
48
50
  collectProjectStatus: () => collectProjectStatus,
49
51
  createInitialArtifacts: () => createInitialArtifacts,
52
+ createMinimalProject: () => createMinimalProject,
50
53
  createProjectFromTemplate: () => createProjectFromTemplate,
51
54
  createZkProject: () => createZkProject,
52
55
  defineConfig: () => defineConfig,
@@ -59,6 +62,8 @@ __export(index_exports, {
59
62
  generateBindings: () => generateBindings,
60
63
  generateBindingsGraph: () => generateBindingsGraph,
61
64
  invokeContract: () => invokeContract,
65
+ isCargoBinMissingFromPath: () => isCargoBinMissingFromPath,
66
+ isReadCallFailure: () => isReadCallFailure,
62
67
  isTransientTestnetSmokeFailure: () => isTransientTestnetSmokeFailure,
63
68
  loadConfig: () => loadConfig,
64
69
  parseContractId: () => parseContractId,
@@ -66,11 +71,13 @@ __export(index_exports, {
66
71
  parseStellarCliVersion: () => parseStellarCliVersion,
67
72
  readArtifacts: () => readArtifacts,
68
73
  readBindingMarker: () => readBindingMarker,
74
+ readContract: () => readContract,
69
75
  resolveContract: () => resolveContract,
70
76
  resolveDefaultContractName: () => resolveDefaultContractName,
71
77
  resolveDeployArgs: () => resolveDeployArgs,
72
78
  resolveDeployOrder: () => resolveDeployOrder,
73
79
  resolveNetwork: () => resolveNetwork,
80
+ resolveSubprocessEnv: () => resolveSubprocessEnv,
74
81
  runCommand: () => runCommand,
75
82
  toCaatingaError: () => toCaatingaError,
76
83
  updateArtifact: () => updateArtifact,
@@ -83,6 +90,7 @@ module.exports = __toCommonJS(index_exports);
83
90
  // src/errors/CaatingaErrorCode.ts
84
91
  var CaatingaErrorCode = {
85
92
  CONFIG_NOT_FOUND: "CAATINGA_CONFIG_NOT_FOUND",
93
+ DEPENDENCIES_NOT_INSTALLED: "CAATINGA_DEPENDENCIES_NOT_INSTALLED",
86
94
  INVALID_CONFIG: "CAATINGA_INVALID_CONFIG",
87
95
  COMMAND_FAILED: "CAATINGA_COMMAND_FAILED",
88
96
  UNEXPECTED_ERROR: "CAATINGA_UNEXPECTED_ERROR",
@@ -187,7 +195,7 @@ function formatCause(cause) {
187
195
  }
188
196
 
189
197
  // src/version.ts
190
- var CAATINGA_CORE_VERSION = "2.3.1";
198
+ var CAATINGA_CORE_VERSION = "2.4.0";
191
199
 
192
200
  // src/config/config.schema.ts
193
201
  var import_zod = require("zod");
@@ -239,6 +247,28 @@ var import_promises = require("fs/promises");
239
247
  var import_node_path = __toESM(require("path"), 1);
240
248
  var import_jiti = require("jiti");
241
249
  var import_zod2 = require("zod");
250
+
251
+ // src/config/is-dependencies-not-installed-error.ts
252
+ function isDependenciesNotInstalledError(error) {
253
+ if (!error || typeof error !== "object") {
254
+ return false;
255
+ }
256
+ const candidate = error;
257
+ const code = candidate.code;
258
+ const message = String(candidate.message ?? error);
259
+ if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") {
260
+ return /@caatinga\/core/.test(message);
261
+ }
262
+ if (/Cannot find (module|package).+@caatinga\/core/i.test(message)) {
263
+ return true;
264
+ }
265
+ if (candidate.cause !== void 0) {
266
+ return isDependenciesNotInstalledError(candidate.cause);
267
+ }
268
+ return false;
269
+ }
270
+
271
+ // src/config/load-config.ts
242
272
  var import_meta = {};
243
273
  async function loadConfig(options = {}) {
244
274
  const cwd = options.cwd ?? process.cwd();
@@ -264,6 +294,14 @@ async function loadConfig(options = {}) {
264
294
  error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")
265
295
  );
266
296
  }
297
+ if (isDependenciesNotInstalledError(error)) {
298
+ throw new CaatingaError(
299
+ "Project dependencies are not installed.",
300
+ CaatingaErrorCode.DEPENDENCIES_NOT_INSTALLED,
301
+ "Run npm install (or pnpm install) in the project root, then retry.",
302
+ error
303
+ );
304
+ }
267
305
  throw error;
268
306
  }
269
307
  }
@@ -670,6 +708,64 @@ function defaultEmitWarning(warning) {
670
708
  `);
671
709
  }
672
710
 
711
+ // src/shell/resolve-subprocess-env.ts
712
+ var import_node_fs = require("fs");
713
+ var import_node_os = __toESM(require("os"), 1);
714
+ var import_node_path6 = __toESM(require("path"), 1);
715
+ function uniquePaths(entries) {
716
+ const seen = /* @__PURE__ */ new Set();
717
+ const ordered = [];
718
+ for (const entry of entries) {
719
+ if (!entry || seen.has(entry)) {
720
+ continue;
721
+ }
722
+ seen.add(entry);
723
+ ordered.push(entry);
724
+ }
725
+ return ordered;
726
+ }
727
+ function hasExecutable(binDir, name) {
728
+ return (0, import_node_fs.existsSync)(import_node_path6.default.join(binDir, name));
729
+ }
730
+ function toolchainBinDirs(home, env) {
731
+ const candidates = [
732
+ import_node_path6.default.join(home, ".cargo", "bin"),
733
+ env.CARGO_HOME ? import_node_path6.default.join(env.CARGO_HOME, "bin") : void 0
734
+ ];
735
+ return candidates.filter((entry) => Boolean(entry && (0, import_node_fs.existsSync)(entry)));
736
+ }
737
+ function buildToolchainPrepend(existingPath, toolchainBins, executableExists = hasExecutable) {
738
+ const prepend = [];
739
+ for (const binDir of toolchainBins) {
740
+ const externalStellarDir = existingPath.find(
741
+ (entry) => entry !== binDir && executableExists(entry, "stellar")
742
+ );
743
+ if (externalStellarDir && executableExists(binDir, "stellar")) {
744
+ prepend.push(externalStellarDir);
745
+ }
746
+ prepend.push(binDir);
747
+ }
748
+ return prepend;
749
+ }
750
+ function resolveSubprocessEnv(overrides = {}) {
751
+ const env = { ...process.env, ...overrides };
752
+ const home = env.HOME ?? import_node_os.default.homedir();
753
+ const existingPath = (env.PATH ?? "").split(import_node_path6.default.delimiter).filter(Boolean);
754
+ const toolchainBins = toolchainBinDirs(home, env);
755
+ const prepend = buildToolchainPrepend(existingPath, toolchainBins);
756
+ env.PATH = uniquePaths([...prepend, ...existingPath]).join(import_node_path6.default.delimiter);
757
+ return env;
758
+ }
759
+ function isCargoBinMissingFromPath(baseEnv = process.env) {
760
+ const home = baseEnv.HOME ?? import_node_os.default.homedir();
761
+ const cargoBin = import_node_path6.default.join(home, ".cargo", "bin", "cargo");
762
+ if (!(0, import_node_fs.existsSync)(cargoBin)) {
763
+ return false;
764
+ }
765
+ const pathEntries = (baseEnv.PATH ?? "").split(import_node_path6.default.delimiter);
766
+ return !pathEntries.some((entry) => entry === import_node_path6.default.join(home, ".cargo", "bin"));
767
+ }
768
+
673
769
  // src/shell/run-command.ts
674
770
  async function runCommand(command, args, options = {}) {
675
771
  try {
@@ -678,7 +774,7 @@ async function runCommand(command, args, options = {}) {
678
774
  }
679
775
  const result = await (0, import_execa.execa)(command, args, {
680
776
  cwd: options.cwd,
681
- env: options.env,
777
+ env: resolveSubprocessEnv(options.env ?? {}),
682
778
  input: options.input,
683
779
  all: true,
684
780
  reject: true
@@ -876,7 +972,7 @@ function validateSourceShape(source) {
876
972
  }
877
973
 
878
974
  // src/contracts/resolve-contract.ts
879
- var import_node_path6 = __toESM(require("path"), 1);
975
+ var import_node_path7 = __toESM(require("path"), 1);
880
976
  function resolveContract(config, contractName, cwd = process.cwd()) {
881
977
  const contract = config.contracts[contractName];
882
978
  if (!contract) {
@@ -889,8 +985,8 @@ function resolveContract(config, contractName, cwd = process.cwd()) {
889
985
  return {
890
986
  name: contractName,
891
987
  config: contract,
892
- sourcePath: import_node_path6.default.resolve(cwd, contract.path),
893
- wasmPath: import_node_path6.default.resolve(cwd, contract.wasm)
988
+ sourcePath: import_node_path7.default.resolve(cwd, contract.path),
989
+ wasmPath: import_node_path7.default.resolve(cwd, contract.wasm)
894
990
  };
895
991
  }
896
992
 
@@ -910,7 +1006,7 @@ function resolveDefaultContractName(config) {
910
1006
  // src/contracts/wasm.ts
911
1007
  var import_node_crypto = require("crypto");
912
1008
  var import_promises6 = require("fs/promises");
913
- var import_node_path7 = __toESM(require("path"), 1);
1009
+ var import_node_path8 = __toESM(require("path"), 1);
914
1010
  var LEGACY_RUST_WASM_TARGET = "wasm32-unknown-unknown";
915
1011
  var CURRENT_RUST_WASM_TARGET = "wasm32v1-none";
916
1012
  function toCurrentWasmTargetPath(wasmPath) {
@@ -933,8 +1029,8 @@ function wasmNotFoundError(configuredWasmPath, options) {
933
1029
  );
934
1030
  }
935
1031
  function toConfigRelativeWasmPath(absoluteWasmPath) {
936
- const relative = import_node_path7.default.relative(process.cwd(), absoluteWasmPath);
937
- return relative.startsWith("..") ? absoluteWasmPath : `./${relative.split(import_node_path7.default.sep).join("/")}`;
1032
+ const relative = import_node_path8.default.relative(process.cwd(), absoluteWasmPath);
1033
+ return relative.startsWith("..") ? absoluteWasmPath : `./${relative.split(import_node_path8.default.sep).join("/")}`;
938
1034
  }
939
1035
  async function resolveWasmArtifactPath(configuredWasmPath) {
940
1036
  try {
@@ -967,7 +1063,7 @@ async function getNewestMtimeInDirectory(directory) {
967
1063
  async function walk(dir) {
968
1064
  const entries = await (0, import_promises6.readdir)(dir, { withFileTypes: true });
969
1065
  for (const entry of entries) {
970
- const entryPath = import_node_path7.default.join(dir, entry.name);
1066
+ const entryPath = import_node_path8.default.join(dir, entry.name);
971
1067
  if (entry.isDirectory()) {
972
1068
  await walk(entryPath);
973
1069
  continue;
@@ -983,7 +1079,7 @@ async function getNewestMtimeInDirectory(directory) {
983
1079
  return newest > 0 ? newest : void 0;
984
1080
  }
985
1081
  async function isWasmOlderThanSources(input) {
986
- const srcDir = import_node_path7.default.join(input.contractPath, "src");
1082
+ const srcDir = import_node_path8.default.join(input.contractPath, "src");
987
1083
  const newestSourceMtime = await getNewestMtimeInDirectory(srcDir);
988
1084
  if (newestSourceMtime === void 0) {
989
1085
  return false;
@@ -1056,7 +1152,7 @@ async function buildContract(options) {
1056
1152
  }
1057
1153
 
1058
1154
  // src/contracts/deploy-contract.ts
1059
- var import_node_path8 = __toESM(require("path"), 1);
1155
+ var import_node_path9 = __toESM(require("path"), 1);
1060
1156
 
1061
1157
  // src/contracts/dependency-graph.ts
1062
1158
  function buildDependencyGraph(contracts) {
@@ -1113,6 +1209,9 @@ function assertSafeSourceAccount(source) {
1113
1209
  }
1114
1210
  return source;
1115
1211
  }
1212
+ function resolveCliSource(explicit) {
1213
+ return assertSafeSourceAccount(explicit ?? process.env.CAATINGA_SOURCE ?? "alice");
1214
+ }
1116
1215
 
1117
1216
  // src/contracts/deploy-contract.ts
1118
1217
  function toSnakeCaseFlag(key) {
@@ -1157,7 +1256,7 @@ async function deployContract(options) {
1157
1256
  contract: contractWithWasm,
1158
1257
  network,
1159
1258
  contractId: existing.contractId,
1160
- artifactsPath: import_node_path8.default.resolve(cwd, "caatinga.artifacts.json"),
1259
+ artifactsPath: import_node_path9.default.resolve(cwd, "caatinga.artifacts.json"),
1161
1260
  output: "",
1162
1261
  skipped: true,
1163
1262
  staleWasmWarning
@@ -1434,13 +1533,13 @@ async function deployContractGraph(options) {
1434
1533
 
1435
1534
  // src/contracts/generate-bindings.ts
1436
1535
  var import_promises7 = require("fs/promises");
1437
- var import_node_path9 = __toESM(require("path"), 1);
1536
+ var import_node_path10 = __toESM(require("path"), 1);
1438
1537
  function toBindingImportPath(bindingsOutput, contractName) {
1439
- const normalized = bindingsOutput.replace(/^\.\//, "").split(import_node_path9.default.sep).join("/");
1440
- return `./${import_node_path9.default.posix.join(normalized, contractName, "src", "index.js")}`;
1538
+ const normalized = bindingsOutput.replace(/^\.\//, "").split(import_node_path10.default.sep).join("/");
1539
+ return `./${import_node_path10.default.posix.join(normalized, contractName, "src", "index.js")}`;
1441
1540
  }
1442
1541
  async function removeLegacyBindingStub(cwd, bindingsOutput, contractName) {
1443
- const legacyPath = import_node_path9.default.resolve(cwd, bindingsOutput, `${contractName}.ts`);
1542
+ const legacyPath = import_node_path10.default.resolve(cwd, bindingsOutput, `${contractName}.ts`);
1444
1543
  try {
1445
1544
  await (0, import_promises7.access)(legacyPath);
1446
1545
  await (0, import_promises7.unlink)(legacyPath);
@@ -1469,7 +1568,7 @@ async function generateBindings(options) {
1469
1568
  );
1470
1569
  }
1471
1570
  await checkBinary("stellar", "Install Stellar CLI before running caatinga generate.");
1472
- const outputDir = import_node_path9.default.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
1571
+ const outputDir = import_node_path10.default.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
1473
1572
  await (0, import_promises7.mkdir)(outputDir, { recursive: true });
1474
1573
  const result = await runCommand("stellar", [
1475
1574
  "contract",
@@ -1543,8 +1642,8 @@ async function generateBindingsGraph(options) {
1543
1642
  return { network, results };
1544
1643
  }
1545
1644
 
1546
- // src/contracts/invoke-contract.ts
1547
- var INVOKE_SIGNING_FAILURE_REGEX = /xdr processing error: xdr value invalid/i;
1645
+ // src/contracts/invoke-target.ts
1646
+ var READ_CALL_FAILURE_REGEX = /this is a read call|read-only/i;
1548
1647
  function parseInvokeTarget(target) {
1549
1648
  const [contractName, method, extra] = target.split(".");
1550
1649
  if (!contractName || !method || extra) {
@@ -1556,6 +1655,29 @@ function parseInvokeTarget(target) {
1556
1655
  }
1557
1656
  return { contractName, method };
1558
1657
  }
1658
+ function buildReadCallHint(target, networkName) {
1659
+ return [
1660
+ `"${target.contractName}.${target.method}" is a read-only contract method.`,
1661
+ "Simulate without signing instead:",
1662
+ ` npx caatinga read ${target.contractName}.${target.method} --network ${networkName}`,
1663
+ " (--source is optional; Caatinga resolves CAATINGA_SOURCE or defaults to alice)",
1664
+ "In browser code, use:",
1665
+ ` client.contract("${target.contractName}").read("${target.method}")`,
1666
+ ` client.contract("${target.contractName}").simulate("${target.method}")`,
1667
+ "Pass method args as the second argument to read() when the contract method takes parameters.",
1668
+ "Only pass Stellar CLI --force when you intentionally need a signed read simulation."
1669
+ ].join("\n");
1670
+ }
1671
+ function isReadCallFailure(error) {
1672
+ if (!(error instanceof CaatingaError) || error.code !== CaatingaErrorCode.INVOKE_FAILED) {
1673
+ return false;
1674
+ }
1675
+ return READ_CALL_FAILURE_REGEX.test(`${error.message}
1676
+ ${error.hint ?? ""}`);
1677
+ }
1678
+
1679
+ // src/contracts/invoke-contract.ts
1680
+ var INVOKE_SIGNING_FAILURE_REGEX = /xdr processing error: xdr value invalid/i;
1559
1681
  async function invokeContract(options) {
1560
1682
  const cwd = options.cwd ?? process.cwd();
1561
1683
  const network = resolveNetwork(options.config, options.networkName);
@@ -1589,6 +1711,14 @@ async function invokeContract(options) {
1589
1711
  failureCode: CaatingaErrorCode.INVOKE_FAILED
1590
1712
  });
1591
1713
  } catch (error) {
1714
+ if (error instanceof CaatingaError && error.code === CaatingaErrorCode.INVOKE_FAILED && isReadCallFailure(error)) {
1715
+ throw new CaatingaError(
1716
+ error.message,
1717
+ error.code,
1718
+ buildReadCallHint(target, network.name),
1719
+ error
1720
+ );
1721
+ }
1592
1722
  if (error instanceof CaatingaError && error.code === CaatingaErrorCode.INVOKE_FAILED && INVOKE_SIGNING_FAILURE_REGEX.test(`${error.message}
1593
1723
  ${error.hint ?? ""}`)) {
1594
1724
  throw new CaatingaError(
@@ -1614,9 +1744,48 @@ ${error.hint ?? ""}`)) {
1614
1744
  };
1615
1745
  }
1616
1746
 
1747
+ // src/contracts/read-contract.ts
1748
+ async function readContract(options) {
1749
+ const cwd = options.cwd ?? process.cwd();
1750
+ const network = resolveNetwork(options.config, options.networkName);
1751
+ const target = parseInvokeTarget(options.target);
1752
+ const artifacts = await readArtifacts(cwd);
1753
+ const contractArtifact = artifacts.networks[network.name]?.contracts[target.contractName];
1754
+ if (!contractArtifact) {
1755
+ throw new CaatingaError(
1756
+ `No deployed artifact found for "${target.contractName}" on "${network.name}".`,
1757
+ CaatingaErrorCode.ARTIFACT_NOT_FOUND,
1758
+ "Run caatinga deploy for this contract and network before reading it."
1759
+ );
1760
+ }
1761
+ await checkBinary("stellar", "Install Stellar CLI before running caatinga read.");
1762
+ const stellarArgs = [
1763
+ "contract",
1764
+ "invoke",
1765
+ "--id",
1766
+ contractArtifact.contractId,
1767
+ "--source-account",
1768
+ resolveCliSource(options.source),
1769
+ "--send=no",
1770
+ ...buildStellarNetworkArgs(network),
1771
+ "--",
1772
+ target.method,
1773
+ ...options.args ?? []
1774
+ ];
1775
+ const result = await runCommand("stellar", stellarArgs, {
1776
+ cwd,
1777
+ failureCode: CaatingaErrorCode.INVOKE_FAILED
1778
+ });
1779
+ return {
1780
+ target,
1781
+ network,
1782
+ result: result.stdout || result.all
1783
+ };
1784
+ }
1785
+
1617
1786
  // src/templates/create-project-from-template.ts
1618
1787
  var import_promises8 = require("fs/promises");
1619
- var import_node_path10 = __toESM(require("path"), 1);
1788
+ var import_node_path11 = __toESM(require("path"), 1);
1620
1789
  var import_zod7 = require("zod");
1621
1790
 
1622
1791
  // src/templates/template-manifest.schema.ts
@@ -1692,8 +1861,8 @@ var TEMPLATE_COPY_EXCLUDED_DIRS = /* @__PURE__ */ new Set([
1692
1861
  ".git"
1693
1862
  ]);
1694
1863
  async function createProjectFromTemplate(options) {
1695
- const targetDir = import_node_path10.default.resolve(options.targetDir);
1696
- const templateDir = import_node_path10.default.resolve(options.templateDir);
1864
+ const targetDir = import_node_path11.default.resolve(options.targetDir);
1865
+ const templateDir = import_node_path11.default.resolve(options.templateDir);
1697
1866
  try {
1698
1867
  await (0, import_promises8.stat)(templateDir);
1699
1868
  } catch {
@@ -1731,7 +1900,7 @@ async function ensureArtifacts(targetDir, projectName) {
1731
1900
  }
1732
1901
  }
1733
1902
  async function readTemplateManifest(templateDir) {
1734
- const manifestPath = import_node_path10.default.join(templateDir, "caatinga.template.json");
1903
+ const manifestPath = import_node_path11.default.join(templateDir, "caatinga.template.json");
1735
1904
  try {
1736
1905
  const rawManifest = await (0, import_promises8.readFile)(manifestPath, "utf8");
1737
1906
  const manifest = TemplateManifestSchema.parse(JSON.parse(rawManifest));
@@ -1768,7 +1937,7 @@ async function readTemplateManifest(templateDir) {
1768
1937
  async function replaceTemplateVariables(dir, projectName) {
1769
1938
  const entries = await (0, import_promises8.readdir)(dir);
1770
1939
  await Promise.all(entries.map(async (entry) => {
1771
- const entryPath = import_node_path10.default.join(dir, entry);
1940
+ const entryPath = import_node_path11.default.join(dir, entry);
1772
1941
  const entryStat = await (0, import_promises8.stat)(entryPath);
1773
1942
  if (entryStat.isDirectory()) {
1774
1943
  await replaceTemplateVariables(entryPath, projectName);
@@ -1782,15 +1951,15 @@ async function replaceTemplateVariables(dir, projectName) {
1782
1951
  }));
1783
1952
  }
1784
1953
  function shouldCopyTemplateEntry(templateDir, source, userFilter) {
1785
- const relativePath = import_node_path10.default.relative(templateDir, source);
1954
+ const relativePath = import_node_path11.default.relative(templateDir, source);
1786
1955
  if (!relativePath || relativePath === ".") {
1787
1956
  return true;
1788
1957
  }
1789
- const normalizedPath = relativePath.split(import_node_path10.default.sep).join("/");
1958
+ const normalizedPath = relativePath.split(import_node_path11.default.sep).join("/");
1790
1959
  if (userFilter && !userFilter(normalizedPath)) {
1791
1960
  return false;
1792
1961
  }
1793
- return !relativePath.split(import_node_path10.default.sep).some((segment) => TEMPLATE_COPY_EXCLUDED_DIRS.has(segment));
1962
+ return !relativePath.split(import_node_path11.default.sep).some((segment) => TEMPLATE_COPY_EXCLUDED_DIRS.has(segment));
1794
1963
  }
1795
1964
  function isTextTemplateFile(filePath) {
1796
1965
  return [
@@ -1802,22 +1971,22 @@ function isTextTemplateFile(filePath) {
1802
1971
  ".tsx",
1803
1972
  ".css",
1804
1973
  ".html"
1805
- ].includes(import_node_path10.default.extname(filePath));
1974
+ ].includes(import_node_path11.default.extname(filePath));
1806
1975
  }
1807
1976
 
1808
1977
  // src/scaffold/create-zk-project.ts
1809
1978
  var import_promises9 = require("fs/promises");
1810
- var import_node_fs = require("fs");
1811
- var import_node_path11 = __toESM(require("path"), 1);
1979
+ var import_node_fs2 = require("fs");
1980
+ var import_node_path12 = __toESM(require("path"), 1);
1812
1981
  var import_node_url = require("url");
1813
1982
  var import_meta2 = {};
1814
- var moduleDir = typeof __dirname === "string" ? __dirname : import_node_path11.default.dirname((0, import_node_url.fileURLToPath)(import_meta2.url));
1983
+ var moduleDir = typeof __dirname === "string" ? __dirname : import_node_path12.default.dirname((0, import_node_url.fileURLToPath)(import_meta2.url));
1815
1984
  function scaffoldRoot() {
1816
1985
  const candidates = [
1817
- import_node_path11.default.resolve(moduleDir, "../../scaffolds"),
1818
- import_node_path11.default.resolve(moduleDir, "../scaffolds")
1986
+ import_node_path12.default.resolve(moduleDir, "../../scaffolds"),
1987
+ import_node_path12.default.resolve(moduleDir, "../scaffolds")
1819
1988
  ];
1820
- const found = candidates.find((candidate) => (0, import_node_fs.existsSync)(candidate));
1989
+ const found = candidates.find((candidate) => (0, import_node_fs2.existsSync)(candidate));
1821
1990
  return found ?? candidates[0];
1822
1991
  }
1823
1992
  function configSource(projectName) {
@@ -1889,25 +2058,25 @@ Replace \`circuits/main.circom\` with your circuit. Keep the entry point named \
1889
2058
  `;
1890
2059
  }
1891
2060
  async function createZkProject(options) {
1892
- const targetDir = import_node_path11.default.resolve(options.targetDir);
2061
+ const targetDir = import_node_path12.default.resolve(options.targetDir);
1893
2062
  const force = options.force ?? false;
1894
2063
  const projectFiles = options.projectFiles ?? true;
1895
2064
  await (0, import_promises9.mkdir)(targetDir, { recursive: true });
1896
2065
  if (projectFiles) {
1897
2066
  await Promise.all([
1898
- (0, import_promises9.writeFile)(import_node_path11.default.join(targetDir, "caatinga.config.ts"), configSource(options.projectName), { encoding: "utf8", flag: force ? "w" : "wx" }),
1899
- (0, import_promises9.writeFile)(import_node_path11.default.join(targetDir, "package.json"), packageJsonSource(options.projectName), { encoding: "utf8", flag: force ? "w" : "wx" }),
1900
- (0, import_promises9.writeFile)(import_node_path11.default.join(targetDir, ".gitignore"), "node_modules\n.artifacts\ntarget\n", { encoding: "utf8", flag: force ? "w" : "wx" }),
1901
- (0, import_promises9.writeFile)(import_node_path11.default.join(targetDir, "README.md"), readmeSource(options.projectName), { encoding: "utf8", flag: force ? "w" : "wx" })
2067
+ (0, import_promises9.writeFile)(import_node_path12.default.join(targetDir, "caatinga.config.ts"), configSource(options.projectName), { encoding: "utf8", flag: force ? "w" : "wx" }),
2068
+ (0, import_promises9.writeFile)(import_node_path12.default.join(targetDir, "package.json"), packageJsonSource(options.projectName), { encoding: "utf8", flag: force ? "w" : "wx" }),
2069
+ (0, import_promises9.writeFile)(import_node_path12.default.join(targetDir, ".gitignore"), "node_modules\n.artifacts\ntarget\n", { encoding: "utf8", flag: force ? "w" : "wx" }),
2070
+ (0, import_promises9.writeFile)(import_node_path12.default.join(targetDir, "README.md"), readmeSource(options.projectName), { encoding: "utf8", flag: force ? "w" : "wx" })
1902
2071
  ]);
1903
2072
  }
1904
- await (0, import_promises9.mkdir)(import_node_path11.default.join(targetDir, "contracts"), { recursive: true });
1905
- await (0, import_promises9.cp)(import_node_path11.default.join(scaffoldRoot(), "zk-circuit-stub"), import_node_path11.default.join(targetDir, "circuits"), {
2073
+ await (0, import_promises9.mkdir)(import_node_path12.default.join(targetDir, "contracts"), { recursive: true });
2074
+ await (0, import_promises9.cp)(import_node_path12.default.join(scaffoldRoot(), "zk-circuit-stub"), import_node_path12.default.join(targetDir, "circuits"), {
1906
2075
  recursive: true,
1907
2076
  force,
1908
2077
  errorOnExist: !force
1909
2078
  });
1910
- await (0, import_promises9.cp)(import_node_path11.default.join(scaffoldRoot(), "zk-verifier"), import_node_path11.default.join(targetDir, "contracts", "verifier"), {
2079
+ await (0, import_promises9.cp)(import_node_path12.default.join(scaffoldRoot(), "zk-verifier"), import_node_path12.default.join(targetDir, "contracts", "verifier"), {
1911
2080
  recursive: true,
1912
2081
  force,
1913
2082
  errorOnExist: !force
@@ -1918,6 +2087,110 @@ async function createZkProject(options) {
1918
2087
  return { targetDir };
1919
2088
  }
1920
2089
 
2090
+ // src/scaffold/create-minimal-project.ts
2091
+ var import_promises10 = require("fs/promises");
2092
+ var import_node_fs3 = require("fs");
2093
+ var import_node_path13 = __toESM(require("path"), 1);
2094
+ var import_node_url2 = require("url");
2095
+ var import_meta3 = {};
2096
+ var moduleDir2 = typeof __dirname === "string" ? __dirname : import_node_path13.default.dirname((0, import_node_url2.fileURLToPath)(import_meta3.url));
2097
+ function scaffoldRoot2() {
2098
+ const candidates = [
2099
+ import_node_path13.default.resolve(moduleDir2, "../../scaffolds"),
2100
+ import_node_path13.default.resolve(moduleDir2, "../scaffolds")
2101
+ ];
2102
+ const found = candidates.find((candidate) => (0, import_node_fs3.existsSync)(candidate));
2103
+ return found ?? candidates[0];
2104
+ }
2105
+ function configSource2(projectName) {
2106
+ return `import { defineConfig } from "@caatinga/core";
2107
+
2108
+ export default defineConfig({
2109
+ project: "${projectName}",
2110
+ defaultNetwork: "testnet",
2111
+ contracts: {
2112
+ app: {
2113
+ path: "./contracts/app",
2114
+ wasm: "./contracts/app/target/wasm32v1-none/release/app.wasm"
2115
+ }
2116
+ },
2117
+ networks: {
2118
+ testnet: {
2119
+ rpcUrl: "https://soroban-testnet.stellar.org",
2120
+ networkPassphrase: "Test SDF Network ; September 2015"
2121
+ }
2122
+ }
2123
+ });
2124
+ `;
2125
+ }
2126
+ function packageJsonSource2(projectName) {
2127
+ return `${JSON.stringify({
2128
+ name: projectName,
2129
+ version: "0.1.0",
2130
+ private: true,
2131
+ type: "module",
2132
+ scripts: {
2133
+ build: "caatinga build app",
2134
+ deploy: "caatinga deploy app --network testnet --source ${CAATINGA_SOURCE:-alice}",
2135
+ doctor: "caatinga doctor",
2136
+ "read:hello": "caatinga read app.hello --network testnet --source ${CAATINGA_SOURCE:-alice}",
2137
+ "read:version": "caatinga read app.version --network testnet --source ${CAATINGA_SOURCE:-alice}"
2138
+ },
2139
+ devDependencies: {
2140
+ "@caatinga/cli": `^${CAATINGA_CORE_VERSION}`,
2141
+ "@caatinga/core": `^${CAATINGA_CORE_VERSION}`
2142
+ }
2143
+ }, null, 2)}
2144
+ `;
2145
+ }
2146
+ function readmeSource2(projectName) {
2147
+ return `# ${projectName}
2148
+
2149
+ Minimal Caatinga project with a Soroban contract stub (no frontend template).
2150
+
2151
+ ## Workflow
2152
+
2153
+ \`\`\`bash
2154
+ npm install
2155
+ npx caatinga doctor
2156
+ npx caatinga build app
2157
+ npx caatinga deploy app --network testnet --source <identity>
2158
+ npx caatinga read app.version --network testnet
2159
+ npx caatinga read app.hello --network testnet
2160
+ \`\`\`
2161
+
2162
+ ## Contract
2163
+
2164
+ - \`hello()\` \u2014 read-only; returns Soroban Symbol \`hello\`
2165
+ - \`version()\` \u2014 read-only; returns \`1\`
2166
+
2167
+ Use \`caatinga read\` for read-only methods. Use \`caatinga invoke\` only after you add state-changing methods to the contract.
2168
+
2169
+ Soroban \`Symbol\` parameters are generated as TypeScript \`string\` values with host-specific restrictions \u2014 see the Caatinga docs on [Soroban types](https://github.com/caatinga/caatinga/blob/main/docs/soroban-types.md).
2170
+
2171
+ Edit \`contracts/app/src/lib.rs\` to customize the contract. Add a frontend later with \`@caatinga/client\` and your chosen UI stack.
2172
+ `;
2173
+ }
2174
+ async function createMinimalProject(options) {
2175
+ const targetDir = import_node_path13.default.resolve(options.targetDir);
2176
+ const force = options.force ?? false;
2177
+ await (0, import_promises10.mkdir)(targetDir, { recursive: true });
2178
+ await Promise.all([
2179
+ (0, import_promises10.writeFile)(import_node_path13.default.join(targetDir, "caatinga.config.ts"), configSource2(options.projectName), { encoding: "utf8", flag: force ? "w" : "wx" }),
2180
+ (0, import_promises10.writeFile)(import_node_path13.default.join(targetDir, "package.json"), packageJsonSource2(options.projectName), { encoding: "utf8", flag: force ? "w" : "wx" }),
2181
+ (0, import_promises10.writeFile)(import_node_path13.default.join(targetDir, ".gitignore"), "node_modules\n.artifacts\ntarget\n", { encoding: "utf8", flag: force ? "w" : "wx" }),
2182
+ (0, import_promises10.writeFile)(import_node_path13.default.join(targetDir, "README.md"), readmeSource2(options.projectName), { encoding: "utf8", flag: force ? "w" : "wx" })
2183
+ ]);
2184
+ await (0, import_promises10.mkdir)(import_node_path13.default.join(targetDir, "contracts"), { recursive: true });
2185
+ await (0, import_promises10.cp)(import_node_path13.default.join(scaffoldRoot2(), "soroban-contract-stub"), import_node_path13.default.join(targetDir, "contracts", "app"), {
2186
+ recursive: true,
2187
+ force,
2188
+ errorOnExist: !force
2189
+ });
2190
+ await writeArtifacts(createInitialArtifacts(options.projectName, { networks: ["testnet"] }), targetDir);
2191
+ return { targetDir };
2192
+ }
2193
+
1921
2194
  // src/ci/is-transient-testnet-smoke-failure.ts
1922
2195
  var NO_RETRY_CAATINGA_SUBSTRINGS = [
1923
2196
  "CAATINGA_UNSUPPORTED_CLI_VERSION",
@@ -1947,16 +2220,19 @@ function isTransientTestnetSmokeFailure(logText) {
1947
2220
  CaatingaConfigSchema,
1948
2221
  CaatingaError,
1949
2222
  CaatingaErrorCode,
2223
+ READ_CALL_FAILURE_REGEX,
1950
2224
  STELLAR_CLI_LAST_TESTED_VERSION,
1951
2225
  STELLAR_CLI_MIN_VERSION,
1952
2226
  TemplateManifestSchema,
1953
2227
  WELL_KNOWN_NETWORKS,
1954
2228
  buildContract,
1955
2229
  buildDependencyGraph,
2230
+ buildReadCallHint,
1956
2231
  checkBinary,
1957
2232
  checkStellarCliVersion,
1958
2233
  collectProjectStatus,
1959
2234
  createInitialArtifacts,
2235
+ createMinimalProject,
1960
2236
  createProjectFromTemplate,
1961
2237
  createZkProject,
1962
2238
  defineConfig,
@@ -1969,6 +2245,8 @@ function isTransientTestnetSmokeFailure(logText) {
1969
2245
  generateBindings,
1970
2246
  generateBindingsGraph,
1971
2247
  invokeContract,
2248
+ isCargoBinMissingFromPath,
2249
+ isReadCallFailure,
1972
2250
  isTransientTestnetSmokeFailure,
1973
2251
  loadConfig,
1974
2252
  parseContractId,
@@ -1976,11 +2254,13 @@ function isTransientTestnetSmokeFailure(logText) {
1976
2254
  parseStellarCliVersion,
1977
2255
  readArtifacts,
1978
2256
  readBindingMarker,
2257
+ readContract,
1979
2258
  resolveContract,
1980
2259
  resolveDefaultContractName,
1981
2260
  resolveDeployArgs,
1982
2261
  resolveDeployOrder,
1983
2262
  resolveNetwork,
2263
+ resolveSubprocessEnv,
1984
2264
  runCommand,
1985
2265
  toCaatingaError,
1986
2266
  updateArtifact,