@caatinga/core 2.3.0 → 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,17 +37,21 @@ __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,
54
+ createZkProject: () => createZkProject,
51
55
  defineConfig: () => defineConfig,
52
56
  deployContract: () => deployContract,
53
57
  deployContractGraph: () => deployContractGraph,
@@ -58,6 +62,8 @@ __export(index_exports, {
58
62
  generateBindings: () => generateBindings,
59
63
  generateBindingsGraph: () => generateBindingsGraph,
60
64
  invokeContract: () => invokeContract,
65
+ isCargoBinMissingFromPath: () => isCargoBinMissingFromPath,
66
+ isReadCallFailure: () => isReadCallFailure,
61
67
  isTransientTestnetSmokeFailure: () => isTransientTestnetSmokeFailure,
62
68
  loadConfig: () => loadConfig,
63
69
  parseContractId: () => parseContractId,
@@ -65,11 +71,13 @@ __export(index_exports, {
65
71
  parseStellarCliVersion: () => parseStellarCliVersion,
66
72
  readArtifacts: () => readArtifacts,
67
73
  readBindingMarker: () => readBindingMarker,
74
+ readContract: () => readContract,
68
75
  resolveContract: () => resolveContract,
69
76
  resolveDefaultContractName: () => resolveDefaultContractName,
70
77
  resolveDeployArgs: () => resolveDeployArgs,
71
78
  resolveDeployOrder: () => resolveDeployOrder,
72
79
  resolveNetwork: () => resolveNetwork,
80
+ resolveSubprocessEnv: () => resolveSubprocessEnv,
73
81
  runCommand: () => runCommand,
74
82
  toCaatingaError: () => toCaatingaError,
75
83
  updateArtifact: () => updateArtifact,
@@ -82,6 +90,7 @@ module.exports = __toCommonJS(index_exports);
82
90
  // src/errors/CaatingaErrorCode.ts
83
91
  var CaatingaErrorCode = {
84
92
  CONFIG_NOT_FOUND: "CAATINGA_CONFIG_NOT_FOUND",
93
+ DEPENDENCIES_NOT_INSTALLED: "CAATINGA_DEPENDENCIES_NOT_INSTALLED",
85
94
  INVALID_CONFIG: "CAATINGA_INVALID_CONFIG",
86
95
  COMMAND_FAILED: "CAATINGA_COMMAND_FAILED",
87
96
  UNEXPECTED_ERROR: "CAATINGA_UNEXPECTED_ERROR",
@@ -186,7 +195,7 @@ function formatCause(cause) {
186
195
  }
187
196
 
188
197
  // src/version.ts
189
- var CAATINGA_CORE_VERSION = "2.3.0";
198
+ var CAATINGA_CORE_VERSION = "2.4.0";
190
199
 
191
200
  // src/config/config.schema.ts
192
201
  var import_zod = require("zod");
@@ -224,7 +233,7 @@ var CaatingaConfigSchema = import_zod.z.object({
224
233
  frontend: import_zod.z.object({
225
234
  framework: import_zod.z.enum(["vite-react", "next", "astro"]).default("vite-react"),
226
235
  bindingsOutput: import_zod.z.string().min(1)
227
- }),
236
+ }).optional(),
228
237
  zk: ZkConfigSchema
229
238
  });
230
239
 
@@ -238,6 +247,28 @@ var import_promises = require("fs/promises");
238
247
  var import_node_path = __toESM(require("path"), 1);
239
248
  var import_jiti = require("jiti");
240
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
241
272
  var import_meta = {};
242
273
  async function loadConfig(options = {}) {
243
274
  const cwd = options.cwd ?? process.cwd();
@@ -263,6 +294,14 @@ async function loadConfig(options = {}) {
263
294
  error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")
264
295
  );
265
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
+ }
266
305
  throw error;
267
306
  }
268
307
  }
@@ -423,6 +462,15 @@ async function listGeneratedEntries(outputDir) {
423
462
  }
424
463
  async function evaluateBindingFreshness(options) {
425
464
  const cwd = options.cwd ?? process.cwd();
465
+ if (!options.config.frontend) {
466
+ return {
467
+ contractName: options.contractName,
468
+ status: "unknown",
469
+ outputDir: "",
470
+ marker: null,
471
+ reason: "frontend bindings are not configured"
472
+ };
473
+ }
426
474
  const outputDir = import_node_path5.default.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
427
475
  const contractArtifact = options.artifacts.networks[options.networkName]?.contracts[options.contractName];
428
476
  if (!contractArtifact) {
@@ -660,6 +708,64 @@ function defaultEmitWarning(warning) {
660
708
  `);
661
709
  }
662
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
+
663
769
  // src/shell/run-command.ts
664
770
  async function runCommand(command, args, options = {}) {
665
771
  try {
@@ -668,7 +774,7 @@ async function runCommand(command, args, options = {}) {
668
774
  }
669
775
  const result = await (0, import_execa.execa)(command, args, {
670
776
  cwd: options.cwd,
671
- env: options.env,
777
+ env: resolveSubprocessEnv(options.env ?? {}),
672
778
  input: options.input,
673
779
  all: true,
674
780
  reject: true
@@ -866,7 +972,7 @@ function validateSourceShape(source) {
866
972
  }
867
973
 
868
974
  // src/contracts/resolve-contract.ts
869
- var import_node_path6 = __toESM(require("path"), 1);
975
+ var import_node_path7 = __toESM(require("path"), 1);
870
976
  function resolveContract(config, contractName, cwd = process.cwd()) {
871
977
  const contract = config.contracts[contractName];
872
978
  if (!contract) {
@@ -879,8 +985,8 @@ function resolveContract(config, contractName, cwd = process.cwd()) {
879
985
  return {
880
986
  name: contractName,
881
987
  config: contract,
882
- sourcePath: import_node_path6.default.resolve(cwd, contract.path),
883
- 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)
884
990
  };
885
991
  }
886
992
 
@@ -900,7 +1006,7 @@ function resolveDefaultContractName(config) {
900
1006
  // src/contracts/wasm.ts
901
1007
  var import_node_crypto = require("crypto");
902
1008
  var import_promises6 = require("fs/promises");
903
- var import_node_path7 = __toESM(require("path"), 1);
1009
+ var import_node_path8 = __toESM(require("path"), 1);
904
1010
  var LEGACY_RUST_WASM_TARGET = "wasm32-unknown-unknown";
905
1011
  var CURRENT_RUST_WASM_TARGET = "wasm32v1-none";
906
1012
  function toCurrentWasmTargetPath(wasmPath) {
@@ -923,8 +1029,8 @@ function wasmNotFoundError(configuredWasmPath, options) {
923
1029
  );
924
1030
  }
925
1031
  function toConfigRelativeWasmPath(absoluteWasmPath) {
926
- const relative = import_node_path7.default.relative(process.cwd(), absoluteWasmPath);
927
- 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("/")}`;
928
1034
  }
929
1035
  async function resolveWasmArtifactPath(configuredWasmPath) {
930
1036
  try {
@@ -957,7 +1063,7 @@ async function getNewestMtimeInDirectory(directory) {
957
1063
  async function walk(dir) {
958
1064
  const entries = await (0, import_promises6.readdir)(dir, { withFileTypes: true });
959
1065
  for (const entry of entries) {
960
- const entryPath = import_node_path7.default.join(dir, entry.name);
1066
+ const entryPath = import_node_path8.default.join(dir, entry.name);
961
1067
  if (entry.isDirectory()) {
962
1068
  await walk(entryPath);
963
1069
  continue;
@@ -973,7 +1079,7 @@ async function getNewestMtimeInDirectory(directory) {
973
1079
  return newest > 0 ? newest : void 0;
974
1080
  }
975
1081
  async function isWasmOlderThanSources(input) {
976
- const srcDir = import_node_path7.default.join(input.contractPath, "src");
1082
+ const srcDir = import_node_path8.default.join(input.contractPath, "src");
977
1083
  const newestSourceMtime = await getNewestMtimeInDirectory(srcDir);
978
1084
  if (newestSourceMtime === void 0) {
979
1085
  return false;
@@ -1046,7 +1152,7 @@ async function buildContract(options) {
1046
1152
  }
1047
1153
 
1048
1154
  // src/contracts/deploy-contract.ts
1049
- var import_node_path8 = __toESM(require("path"), 1);
1155
+ var import_node_path9 = __toESM(require("path"), 1);
1050
1156
 
1051
1157
  // src/contracts/dependency-graph.ts
1052
1158
  function buildDependencyGraph(contracts) {
@@ -1103,6 +1209,9 @@ function assertSafeSourceAccount(source) {
1103
1209
  }
1104
1210
  return source;
1105
1211
  }
1212
+ function resolveCliSource(explicit) {
1213
+ return assertSafeSourceAccount(explicit ?? process.env.CAATINGA_SOURCE ?? "alice");
1214
+ }
1106
1215
 
1107
1216
  // src/contracts/deploy-contract.ts
1108
1217
  function toSnakeCaseFlag(key) {
@@ -1147,7 +1256,7 @@ async function deployContract(options) {
1147
1256
  contract: contractWithWasm,
1148
1257
  network,
1149
1258
  contractId: existing.contractId,
1150
- artifactsPath: import_node_path8.default.resolve(cwd, "caatinga.artifacts.json"),
1259
+ artifactsPath: import_node_path9.default.resolve(cwd, "caatinga.artifacts.json"),
1151
1260
  output: "",
1152
1261
  skipped: true,
1153
1262
  staleWasmWarning
@@ -1424,13 +1533,13 @@ async function deployContractGraph(options) {
1424
1533
 
1425
1534
  // src/contracts/generate-bindings.ts
1426
1535
  var import_promises7 = require("fs/promises");
1427
- var import_node_path9 = __toESM(require("path"), 1);
1536
+ var import_node_path10 = __toESM(require("path"), 1);
1428
1537
  function toBindingImportPath(bindingsOutput, contractName) {
1429
- const normalized = bindingsOutput.replace(/^\.\//, "").split(import_node_path9.default.sep).join("/");
1430
- 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")}`;
1431
1540
  }
1432
1541
  async function removeLegacyBindingStub(cwd, bindingsOutput, contractName) {
1433
- const legacyPath = import_node_path9.default.resolve(cwd, bindingsOutput, `${contractName}.ts`);
1542
+ const legacyPath = import_node_path10.default.resolve(cwd, bindingsOutput, `${contractName}.ts`);
1434
1543
  try {
1435
1544
  await (0, import_promises7.access)(legacyPath);
1436
1545
  await (0, import_promises7.unlink)(legacyPath);
@@ -1441,6 +1550,13 @@ async function removeLegacyBindingStub(cwd, bindingsOutput, contractName) {
1441
1550
  }
1442
1551
  async function generateBindings(options) {
1443
1552
  const cwd = options.cwd ?? process.cwd();
1553
+ if (!options.config.frontend) {
1554
+ throw new CaatingaError(
1555
+ "Frontend bindings are not configured.",
1556
+ CaatingaErrorCode.INVALID_CONFIG,
1557
+ "Add a frontend.bindingsOutput entry to caatinga.config.ts before running caatinga generate."
1558
+ );
1559
+ }
1444
1560
  const network = resolveNetwork(options.config, options.networkName);
1445
1561
  const artifacts = await readArtifacts(cwd);
1446
1562
  const contractArtifact = artifacts.networks[network.name]?.contracts[options.contractName];
@@ -1452,7 +1568,7 @@ async function generateBindings(options) {
1452
1568
  );
1453
1569
  }
1454
1570
  await checkBinary("stellar", "Install Stellar CLI before running caatinga generate.");
1455
- 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);
1456
1572
  await (0, import_promises7.mkdir)(outputDir, { recursive: true });
1457
1573
  const result = await runCommand("stellar", [
1458
1574
  "contract",
@@ -1526,8 +1642,8 @@ async function generateBindingsGraph(options) {
1526
1642
  return { network, results };
1527
1643
  }
1528
1644
 
1529
- // src/contracts/invoke-contract.ts
1530
- 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;
1531
1647
  function parseInvokeTarget(target) {
1532
1648
  const [contractName, method, extra] = target.split(".");
1533
1649
  if (!contractName || !method || extra) {
@@ -1539,6 +1655,29 @@ function parseInvokeTarget(target) {
1539
1655
  }
1540
1656
  return { contractName, method };
1541
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;
1542
1681
  async function invokeContract(options) {
1543
1682
  const cwd = options.cwd ?? process.cwd();
1544
1683
  const network = resolveNetwork(options.config, options.networkName);
@@ -1572,6 +1711,14 @@ async function invokeContract(options) {
1572
1711
  failureCode: CaatingaErrorCode.INVOKE_FAILED
1573
1712
  });
1574
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
+ }
1575
1722
  if (error instanceof CaatingaError && error.code === CaatingaErrorCode.INVOKE_FAILED && INVOKE_SIGNING_FAILURE_REGEX.test(`${error.message}
1576
1723
  ${error.hint ?? ""}`)) {
1577
1724
  throw new CaatingaError(
@@ -1597,9 +1744,48 @@ ${error.hint ?? ""}`)) {
1597
1744
  };
1598
1745
  }
1599
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
+
1600
1786
  // src/templates/create-project-from-template.ts
1601
1787
  var import_promises8 = require("fs/promises");
1602
- var import_node_path10 = __toESM(require("path"), 1);
1788
+ var import_node_path11 = __toESM(require("path"), 1);
1603
1789
  var import_zod7 = require("zod");
1604
1790
 
1605
1791
  // src/templates/template-manifest.schema.ts
@@ -1675,8 +1861,8 @@ var TEMPLATE_COPY_EXCLUDED_DIRS = /* @__PURE__ */ new Set([
1675
1861
  ".git"
1676
1862
  ]);
1677
1863
  async function createProjectFromTemplate(options) {
1678
- const targetDir = import_node_path10.default.resolve(options.targetDir);
1679
- 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);
1680
1866
  try {
1681
1867
  await (0, import_promises8.stat)(templateDir);
1682
1868
  } catch {
@@ -1714,7 +1900,7 @@ async function ensureArtifacts(targetDir, projectName) {
1714
1900
  }
1715
1901
  }
1716
1902
  async function readTemplateManifest(templateDir) {
1717
- const manifestPath = import_node_path10.default.join(templateDir, "caatinga.template.json");
1903
+ const manifestPath = import_node_path11.default.join(templateDir, "caatinga.template.json");
1718
1904
  try {
1719
1905
  const rawManifest = await (0, import_promises8.readFile)(manifestPath, "utf8");
1720
1906
  const manifest = TemplateManifestSchema.parse(JSON.parse(rawManifest));
@@ -1751,7 +1937,7 @@ async function readTemplateManifest(templateDir) {
1751
1937
  async function replaceTemplateVariables(dir, projectName) {
1752
1938
  const entries = await (0, import_promises8.readdir)(dir);
1753
1939
  await Promise.all(entries.map(async (entry) => {
1754
- const entryPath = import_node_path10.default.join(dir, entry);
1940
+ const entryPath = import_node_path11.default.join(dir, entry);
1755
1941
  const entryStat = await (0, import_promises8.stat)(entryPath);
1756
1942
  if (entryStat.isDirectory()) {
1757
1943
  await replaceTemplateVariables(entryPath, projectName);
@@ -1765,15 +1951,15 @@ async function replaceTemplateVariables(dir, projectName) {
1765
1951
  }));
1766
1952
  }
1767
1953
  function shouldCopyTemplateEntry(templateDir, source, userFilter) {
1768
- const relativePath = import_node_path10.default.relative(templateDir, source);
1954
+ const relativePath = import_node_path11.default.relative(templateDir, source);
1769
1955
  if (!relativePath || relativePath === ".") {
1770
1956
  return true;
1771
1957
  }
1772
- const normalizedPath = relativePath.split(import_node_path10.default.sep).join("/");
1958
+ const normalizedPath = relativePath.split(import_node_path11.default.sep).join("/");
1773
1959
  if (userFilter && !userFilter(normalizedPath)) {
1774
1960
  return false;
1775
1961
  }
1776
- 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));
1777
1963
  }
1778
1964
  function isTextTemplateFile(filePath) {
1779
1965
  return [
@@ -1785,7 +1971,224 @@ function isTextTemplateFile(filePath) {
1785
1971
  ".tsx",
1786
1972
  ".css",
1787
1973
  ".html"
1788
- ].includes(import_node_path10.default.extname(filePath));
1974
+ ].includes(import_node_path11.default.extname(filePath));
1975
+ }
1976
+
1977
+ // src/scaffold/create-zk-project.ts
1978
+ var import_promises9 = require("fs/promises");
1979
+ var import_node_fs2 = require("fs");
1980
+ var import_node_path12 = __toESM(require("path"), 1);
1981
+ var import_node_url = require("url");
1982
+ var import_meta2 = {};
1983
+ var moduleDir = typeof __dirname === "string" ? __dirname : import_node_path12.default.dirname((0, import_node_url.fileURLToPath)(import_meta2.url));
1984
+ function scaffoldRoot() {
1985
+ const candidates = [
1986
+ import_node_path12.default.resolve(moduleDir, "../../scaffolds"),
1987
+ import_node_path12.default.resolve(moduleDir, "../scaffolds")
1988
+ ];
1989
+ const found = candidates.find((candidate) => (0, import_node_fs2.existsSync)(candidate));
1990
+ return found ?? candidates[0];
1991
+ }
1992
+ function configSource(projectName) {
1993
+ return `import { defineConfig } from "@caatinga/core";
1994
+
1995
+ export default defineConfig({
1996
+ project: "${projectName}",
1997
+ defaultNetwork: "testnet",
1998
+ contracts: {
1999
+ verifier: {
2000
+ path: "./contracts/verifier",
2001
+ wasm: "./contracts/verifier/target/wasm32v1-none/release/verifier.wasm"
2002
+ }
2003
+ },
2004
+ networks: {
2005
+ testnet: {
2006
+ rpcUrl: "https://soroban-testnet.stellar.org",
2007
+ networkPassphrase: "Test SDF Network ; September 2015"
2008
+ }
2009
+ },
2010
+ zk: {
2011
+ circuits: {
2012
+ main: {
2013
+ path: "./circuits",
2014
+ protocol: "groth16",
2015
+ curve: "bls12381",
2016
+ verifierContract: "verifier"
2017
+ }
2018
+ }
2019
+ }
2020
+ });
2021
+ `;
2022
+ }
2023
+ function packageJsonSource(projectName) {
2024
+ return `${JSON.stringify({
2025
+ name: projectName,
2026
+ version: "0.1.0",
2027
+ private: true,
2028
+ type: "module",
2029
+ scripts: {
2030
+ "zk:build": "caatinga zk build main",
2031
+ "zk:prove": "caatinga zk prove main",
2032
+ build: "caatinga build verifier",
2033
+ deploy: "caatinga deploy verifier"
2034
+ },
2035
+ devDependencies: {
2036
+ "@caatinga/cli": `^${CAATINGA_CORE_VERSION}`,
2037
+ "@caatinga/core": `^${CAATINGA_CORE_VERSION}`
2038
+ }
2039
+ }, null, 2)}
2040
+ `;
2041
+ }
2042
+ function readmeSource(projectName) {
2043
+ return `# ${projectName}
2044
+
2045
+ Minimal Caatinga ZK project.
2046
+
2047
+ ## Workflow
2048
+
2049
+ \`\`\`bash
2050
+ npm install
2051
+ npx caatinga zk build main
2052
+ npx caatinga build verifier
2053
+ npx caatinga deploy verifier --network testnet
2054
+ npx caatinga zk prove main
2055
+ \`\`\`
2056
+
2057
+ Replace \`circuits/main.circom\` with your circuit. Keep the entry point named \`main\`.
2058
+ `;
2059
+ }
2060
+ async function createZkProject(options) {
2061
+ const targetDir = import_node_path12.default.resolve(options.targetDir);
2062
+ const force = options.force ?? false;
2063
+ const projectFiles = options.projectFiles ?? true;
2064
+ await (0, import_promises9.mkdir)(targetDir, { recursive: true });
2065
+ if (projectFiles) {
2066
+ await Promise.all([
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" })
2071
+ ]);
2072
+ }
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"), {
2075
+ recursive: true,
2076
+ force,
2077
+ errorOnExist: !force
2078
+ });
2079
+ await (0, import_promises9.cp)(import_node_path12.default.join(scaffoldRoot(), "zk-verifier"), import_node_path12.default.join(targetDir, "contracts", "verifier"), {
2080
+ recursive: true,
2081
+ force,
2082
+ errorOnExist: !force
2083
+ });
2084
+ if (projectFiles) {
2085
+ await writeArtifacts(createInitialArtifacts(options.projectName, { networks: ["testnet"] }), targetDir);
2086
+ }
2087
+ return { targetDir };
2088
+ }
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 };
1789
2192
  }
1790
2193
 
1791
2194
  // src/ci/is-transient-testnet-smoke-failure.ts
@@ -1817,17 +2220,21 @@ function isTransientTestnetSmokeFailure(logText) {
1817
2220
  CaatingaConfigSchema,
1818
2221
  CaatingaError,
1819
2222
  CaatingaErrorCode,
2223
+ READ_CALL_FAILURE_REGEX,
1820
2224
  STELLAR_CLI_LAST_TESTED_VERSION,
1821
2225
  STELLAR_CLI_MIN_VERSION,
1822
2226
  TemplateManifestSchema,
1823
2227
  WELL_KNOWN_NETWORKS,
1824
2228
  buildContract,
1825
2229
  buildDependencyGraph,
2230
+ buildReadCallHint,
1826
2231
  checkBinary,
1827
2232
  checkStellarCliVersion,
1828
2233
  collectProjectStatus,
1829
2234
  createInitialArtifacts,
2235
+ createMinimalProject,
1830
2236
  createProjectFromTemplate,
2237
+ createZkProject,
1831
2238
  defineConfig,
1832
2239
  deployContract,
1833
2240
  deployContractGraph,
@@ -1838,6 +2245,8 @@ function isTransientTestnetSmokeFailure(logText) {
1838
2245
  generateBindings,
1839
2246
  generateBindingsGraph,
1840
2247
  invokeContract,
2248
+ isCargoBinMissingFromPath,
2249
+ isReadCallFailure,
1841
2250
  isTransientTestnetSmokeFailure,
1842
2251
  loadConfig,
1843
2252
  parseContractId,
@@ -1845,11 +2254,13 @@ function isTransientTestnetSmokeFailure(logText) {
1845
2254
  parseStellarCliVersion,
1846
2255
  readArtifacts,
1847
2256
  readBindingMarker,
2257
+ readContract,
1848
2258
  resolveContract,
1849
2259
  resolveDefaultContractName,
1850
2260
  resolveDeployArgs,
1851
2261
  resolveDeployOrder,
1852
2262
  resolveNetwork,
2263
+ resolveSubprocessEnv,
1853
2264
  runCommand,
1854
2265
  toCaatingaError,
1855
2266
  updateArtifact,