@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.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // src/errors/CaatingaErrorCode.ts
2
2
  var CaatingaErrorCode = {
3
3
  CONFIG_NOT_FOUND: "CAATINGA_CONFIG_NOT_FOUND",
4
+ DEPENDENCIES_NOT_INSTALLED: "CAATINGA_DEPENDENCIES_NOT_INSTALLED",
4
5
  INVALID_CONFIG: "CAATINGA_INVALID_CONFIG",
5
6
  COMMAND_FAILED: "CAATINGA_COMMAND_FAILED",
6
7
  UNEXPECTED_ERROR: "CAATINGA_UNEXPECTED_ERROR",
@@ -105,7 +106,7 @@ function formatCause(cause) {
105
106
  }
106
107
 
107
108
  // src/version.ts
108
- var CAATINGA_CORE_VERSION = "2.3.0";
109
+ var CAATINGA_CORE_VERSION = "2.4.0";
109
110
 
110
111
  // src/config/config.schema.ts
111
112
  import { z } from "zod";
@@ -143,7 +144,7 @@ var CaatingaConfigSchema = z.object({
143
144
  frontend: z.object({
144
145
  framework: z.enum(["vite-react", "next", "astro"]).default("vite-react"),
145
146
  bindingsOutput: z.string().min(1)
146
- }),
147
+ }).optional(),
147
148
  zk: ZkConfigSchema
148
149
  });
149
150
 
@@ -157,6 +158,28 @@ import { access } from "fs/promises";
157
158
  import path from "path";
158
159
  import { createJiti } from "jiti";
159
160
  import { z as z2 } from "zod";
161
+
162
+ // src/config/is-dependencies-not-installed-error.ts
163
+ function isDependenciesNotInstalledError(error) {
164
+ if (!error || typeof error !== "object") {
165
+ return false;
166
+ }
167
+ const candidate = error;
168
+ const code = candidate.code;
169
+ const message = String(candidate.message ?? error);
170
+ if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") {
171
+ return /@caatinga\/core/.test(message);
172
+ }
173
+ if (/Cannot find (module|package).+@caatinga\/core/i.test(message)) {
174
+ return true;
175
+ }
176
+ if (candidate.cause !== void 0) {
177
+ return isDependenciesNotInstalledError(candidate.cause);
178
+ }
179
+ return false;
180
+ }
181
+
182
+ // src/config/load-config.ts
160
183
  async function loadConfig(options = {}) {
161
184
  const cwd = options.cwd ?? process.cwd();
162
185
  const configPath = path.resolve(cwd, options.configPath ?? "caatinga.config.ts");
@@ -181,6 +204,14 @@ async function loadConfig(options = {}) {
181
204
  error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")
182
205
  );
183
206
  }
207
+ if (isDependenciesNotInstalledError(error)) {
208
+ throw new CaatingaError(
209
+ "Project dependencies are not installed.",
210
+ CaatingaErrorCode.DEPENDENCIES_NOT_INSTALLED,
211
+ "Run npm install (or pnpm install) in the project root, then retry.",
212
+ error
213
+ );
214
+ }
184
215
  throw error;
185
216
  }
186
217
  }
@@ -341,6 +372,15 @@ async function listGeneratedEntries(outputDir) {
341
372
  }
342
373
  async function evaluateBindingFreshness(options) {
343
374
  const cwd = options.cwd ?? process.cwd();
375
+ if (!options.config.frontend) {
376
+ return {
377
+ contractName: options.contractName,
378
+ status: "unknown",
379
+ outputDir: "",
380
+ marker: null,
381
+ reason: "frontend bindings are not configured"
382
+ };
383
+ }
344
384
  const outputDir = path5.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
345
385
  const contractArtifact = options.artifacts.networks[options.networkName]?.contracts[options.contractName];
346
386
  if (!contractArtifact) {
@@ -578,6 +618,64 @@ function defaultEmitWarning(warning) {
578
618
  `);
579
619
  }
580
620
 
621
+ // src/shell/resolve-subprocess-env.ts
622
+ import { existsSync } from "fs";
623
+ import os from "os";
624
+ import path6 from "path";
625
+ function uniquePaths(entries) {
626
+ const seen = /* @__PURE__ */ new Set();
627
+ const ordered = [];
628
+ for (const entry of entries) {
629
+ if (!entry || seen.has(entry)) {
630
+ continue;
631
+ }
632
+ seen.add(entry);
633
+ ordered.push(entry);
634
+ }
635
+ return ordered;
636
+ }
637
+ function hasExecutable(binDir, name) {
638
+ return existsSync(path6.join(binDir, name));
639
+ }
640
+ function toolchainBinDirs(home, env) {
641
+ const candidates = [
642
+ path6.join(home, ".cargo", "bin"),
643
+ env.CARGO_HOME ? path6.join(env.CARGO_HOME, "bin") : void 0
644
+ ];
645
+ return candidates.filter((entry) => Boolean(entry && existsSync(entry)));
646
+ }
647
+ function buildToolchainPrepend(existingPath, toolchainBins, executableExists = hasExecutable) {
648
+ const prepend = [];
649
+ for (const binDir of toolchainBins) {
650
+ const externalStellarDir = existingPath.find(
651
+ (entry) => entry !== binDir && executableExists(entry, "stellar")
652
+ );
653
+ if (externalStellarDir && executableExists(binDir, "stellar")) {
654
+ prepend.push(externalStellarDir);
655
+ }
656
+ prepend.push(binDir);
657
+ }
658
+ return prepend;
659
+ }
660
+ function resolveSubprocessEnv(overrides = {}) {
661
+ const env = { ...process.env, ...overrides };
662
+ const home = env.HOME ?? os.homedir();
663
+ const existingPath = (env.PATH ?? "").split(path6.delimiter).filter(Boolean);
664
+ const toolchainBins = toolchainBinDirs(home, env);
665
+ const prepend = buildToolchainPrepend(existingPath, toolchainBins);
666
+ env.PATH = uniquePaths([...prepend, ...existingPath]).join(path6.delimiter);
667
+ return env;
668
+ }
669
+ function isCargoBinMissingFromPath(baseEnv = process.env) {
670
+ const home = baseEnv.HOME ?? os.homedir();
671
+ const cargoBin = path6.join(home, ".cargo", "bin", "cargo");
672
+ if (!existsSync(cargoBin)) {
673
+ return false;
674
+ }
675
+ const pathEntries = (baseEnv.PATH ?? "").split(path6.delimiter);
676
+ return !pathEntries.some((entry) => entry === path6.join(home, ".cargo", "bin"));
677
+ }
678
+
581
679
  // src/shell/run-command.ts
582
680
  async function runCommand(command, args, options = {}) {
583
681
  try {
@@ -586,7 +684,7 @@ async function runCommand(command, args, options = {}) {
586
684
  }
587
685
  const result = await execa(command, args, {
588
686
  cwd: options.cwd,
589
- env: options.env,
687
+ env: resolveSubprocessEnv(options.env ?? {}),
590
688
  input: options.input,
591
689
  all: true,
592
690
  reject: true
@@ -784,7 +882,7 @@ function validateSourceShape(source) {
784
882
  }
785
883
 
786
884
  // src/contracts/resolve-contract.ts
787
- import path6 from "path";
885
+ import path7 from "path";
788
886
  function resolveContract(config, contractName, cwd = process.cwd()) {
789
887
  const contract = config.contracts[contractName];
790
888
  if (!contract) {
@@ -797,8 +895,8 @@ function resolveContract(config, contractName, cwd = process.cwd()) {
797
895
  return {
798
896
  name: contractName,
799
897
  config: contract,
800
- sourcePath: path6.resolve(cwd, contract.path),
801
- wasmPath: path6.resolve(cwd, contract.wasm)
898
+ sourcePath: path7.resolve(cwd, contract.path),
899
+ wasmPath: path7.resolve(cwd, contract.wasm)
802
900
  };
803
901
  }
804
902
 
@@ -818,7 +916,7 @@ function resolveDefaultContractName(config) {
818
916
  // src/contracts/wasm.ts
819
917
  import { createHash } from "crypto";
820
918
  import { access as access2, readdir as readdir2, readFile as readFile3, stat } from "fs/promises";
821
- import path7 from "path";
919
+ import path8 from "path";
822
920
  var LEGACY_RUST_WASM_TARGET = "wasm32-unknown-unknown";
823
921
  var CURRENT_RUST_WASM_TARGET = "wasm32v1-none";
824
922
  function toCurrentWasmTargetPath(wasmPath) {
@@ -841,8 +939,8 @@ function wasmNotFoundError(configuredWasmPath, options) {
841
939
  );
842
940
  }
843
941
  function toConfigRelativeWasmPath(absoluteWasmPath) {
844
- const relative = path7.relative(process.cwd(), absoluteWasmPath);
845
- return relative.startsWith("..") ? absoluteWasmPath : `./${relative.split(path7.sep).join("/")}`;
942
+ const relative = path8.relative(process.cwd(), absoluteWasmPath);
943
+ return relative.startsWith("..") ? absoluteWasmPath : `./${relative.split(path8.sep).join("/")}`;
846
944
  }
847
945
  async function resolveWasmArtifactPath(configuredWasmPath) {
848
946
  try {
@@ -875,7 +973,7 @@ async function getNewestMtimeInDirectory(directory) {
875
973
  async function walk(dir) {
876
974
  const entries = await readdir2(dir, { withFileTypes: true });
877
975
  for (const entry of entries) {
878
- const entryPath = path7.join(dir, entry.name);
976
+ const entryPath = path8.join(dir, entry.name);
879
977
  if (entry.isDirectory()) {
880
978
  await walk(entryPath);
881
979
  continue;
@@ -891,7 +989,7 @@ async function getNewestMtimeInDirectory(directory) {
891
989
  return newest > 0 ? newest : void 0;
892
990
  }
893
991
  async function isWasmOlderThanSources(input) {
894
- const srcDir = path7.join(input.contractPath, "src");
992
+ const srcDir = path8.join(input.contractPath, "src");
895
993
  const newestSourceMtime = await getNewestMtimeInDirectory(srcDir);
896
994
  if (newestSourceMtime === void 0) {
897
995
  return false;
@@ -964,7 +1062,7 @@ async function buildContract(options) {
964
1062
  }
965
1063
 
966
1064
  // src/contracts/deploy-contract.ts
967
- import path8 from "path";
1065
+ import path9 from "path";
968
1066
 
969
1067
  // src/contracts/dependency-graph.ts
970
1068
  function buildDependencyGraph(contracts) {
@@ -1021,6 +1119,9 @@ function assertSafeSourceAccount(source) {
1021
1119
  }
1022
1120
  return source;
1023
1121
  }
1122
+ function resolveCliSource(explicit) {
1123
+ return assertSafeSourceAccount(explicit ?? process.env.CAATINGA_SOURCE ?? "alice");
1124
+ }
1024
1125
 
1025
1126
  // src/contracts/deploy-contract.ts
1026
1127
  function toSnakeCaseFlag(key) {
@@ -1065,7 +1166,7 @@ async function deployContract(options) {
1065
1166
  contract: contractWithWasm,
1066
1167
  network,
1067
1168
  contractId: existing.contractId,
1068
- artifactsPath: path8.resolve(cwd, "caatinga.artifacts.json"),
1169
+ artifactsPath: path9.resolve(cwd, "caatinga.artifacts.json"),
1069
1170
  output: "",
1070
1171
  skipped: true,
1071
1172
  staleWasmWarning
@@ -1342,13 +1443,13 @@ async function deployContractGraph(options) {
1342
1443
 
1343
1444
  // src/contracts/generate-bindings.ts
1344
1445
  import { access as access3, mkdir as mkdir2, unlink } from "fs/promises";
1345
- import path9 from "path";
1446
+ import path10 from "path";
1346
1447
  function toBindingImportPath(bindingsOutput, contractName) {
1347
- const normalized = bindingsOutput.replace(/^\.\//, "").split(path9.sep).join("/");
1348
- return `./${path9.posix.join(normalized, contractName, "src", "index.js")}`;
1448
+ const normalized = bindingsOutput.replace(/^\.\//, "").split(path10.sep).join("/");
1449
+ return `./${path10.posix.join(normalized, contractName, "src", "index.js")}`;
1349
1450
  }
1350
1451
  async function removeLegacyBindingStub(cwd, bindingsOutput, contractName) {
1351
- const legacyPath = path9.resolve(cwd, bindingsOutput, `${contractName}.ts`);
1452
+ const legacyPath = path10.resolve(cwd, bindingsOutput, `${contractName}.ts`);
1352
1453
  try {
1353
1454
  await access3(legacyPath);
1354
1455
  await unlink(legacyPath);
@@ -1359,6 +1460,13 @@ async function removeLegacyBindingStub(cwd, bindingsOutput, contractName) {
1359
1460
  }
1360
1461
  async function generateBindings(options) {
1361
1462
  const cwd = options.cwd ?? process.cwd();
1463
+ if (!options.config.frontend) {
1464
+ throw new CaatingaError(
1465
+ "Frontend bindings are not configured.",
1466
+ CaatingaErrorCode.INVALID_CONFIG,
1467
+ "Add a frontend.bindingsOutput entry to caatinga.config.ts before running caatinga generate."
1468
+ );
1469
+ }
1362
1470
  const network = resolveNetwork(options.config, options.networkName);
1363
1471
  const artifacts = await readArtifacts(cwd);
1364
1472
  const contractArtifact = artifacts.networks[network.name]?.contracts[options.contractName];
@@ -1370,7 +1478,7 @@ async function generateBindings(options) {
1370
1478
  );
1371
1479
  }
1372
1480
  await checkBinary("stellar", "Install Stellar CLI before running caatinga generate.");
1373
- const outputDir = path9.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
1481
+ const outputDir = path10.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
1374
1482
  await mkdir2(outputDir, { recursive: true });
1375
1483
  const result = await runCommand("stellar", [
1376
1484
  "contract",
@@ -1444,8 +1552,8 @@ async function generateBindingsGraph(options) {
1444
1552
  return { network, results };
1445
1553
  }
1446
1554
 
1447
- // src/contracts/invoke-contract.ts
1448
- var INVOKE_SIGNING_FAILURE_REGEX = /xdr processing error: xdr value invalid/i;
1555
+ // src/contracts/invoke-target.ts
1556
+ var READ_CALL_FAILURE_REGEX = /this is a read call|read-only/i;
1449
1557
  function parseInvokeTarget(target) {
1450
1558
  const [contractName, method, extra] = target.split(".");
1451
1559
  if (!contractName || !method || extra) {
@@ -1457,6 +1565,29 @@ function parseInvokeTarget(target) {
1457
1565
  }
1458
1566
  return { contractName, method };
1459
1567
  }
1568
+ function buildReadCallHint(target, networkName) {
1569
+ return [
1570
+ `"${target.contractName}.${target.method}" is a read-only contract method.`,
1571
+ "Simulate without signing instead:",
1572
+ ` npx caatinga read ${target.contractName}.${target.method} --network ${networkName}`,
1573
+ " (--source is optional; Caatinga resolves CAATINGA_SOURCE or defaults to alice)",
1574
+ "In browser code, use:",
1575
+ ` client.contract("${target.contractName}").read("${target.method}")`,
1576
+ ` client.contract("${target.contractName}").simulate("${target.method}")`,
1577
+ "Pass method args as the second argument to read() when the contract method takes parameters.",
1578
+ "Only pass Stellar CLI --force when you intentionally need a signed read simulation."
1579
+ ].join("\n");
1580
+ }
1581
+ function isReadCallFailure(error) {
1582
+ if (!(error instanceof CaatingaError) || error.code !== CaatingaErrorCode.INVOKE_FAILED) {
1583
+ return false;
1584
+ }
1585
+ return READ_CALL_FAILURE_REGEX.test(`${error.message}
1586
+ ${error.hint ?? ""}`);
1587
+ }
1588
+
1589
+ // src/contracts/invoke-contract.ts
1590
+ var INVOKE_SIGNING_FAILURE_REGEX = /xdr processing error: xdr value invalid/i;
1460
1591
  async function invokeContract(options) {
1461
1592
  const cwd = options.cwd ?? process.cwd();
1462
1593
  const network = resolveNetwork(options.config, options.networkName);
@@ -1490,6 +1621,14 @@ async function invokeContract(options) {
1490
1621
  failureCode: CaatingaErrorCode.INVOKE_FAILED
1491
1622
  });
1492
1623
  } catch (error) {
1624
+ if (error instanceof CaatingaError && error.code === CaatingaErrorCode.INVOKE_FAILED && isReadCallFailure(error)) {
1625
+ throw new CaatingaError(
1626
+ error.message,
1627
+ error.code,
1628
+ buildReadCallHint(target, network.name),
1629
+ error
1630
+ );
1631
+ }
1493
1632
  if (error instanceof CaatingaError && error.code === CaatingaErrorCode.INVOKE_FAILED && INVOKE_SIGNING_FAILURE_REGEX.test(`${error.message}
1494
1633
  ${error.hint ?? ""}`)) {
1495
1634
  throw new CaatingaError(
@@ -1515,9 +1654,48 @@ ${error.hint ?? ""}`)) {
1515
1654
  };
1516
1655
  }
1517
1656
 
1657
+ // src/contracts/read-contract.ts
1658
+ async function readContract(options) {
1659
+ const cwd = options.cwd ?? process.cwd();
1660
+ const network = resolveNetwork(options.config, options.networkName);
1661
+ const target = parseInvokeTarget(options.target);
1662
+ const artifacts = await readArtifacts(cwd);
1663
+ const contractArtifact = artifacts.networks[network.name]?.contracts[target.contractName];
1664
+ if (!contractArtifact) {
1665
+ throw new CaatingaError(
1666
+ `No deployed artifact found for "${target.contractName}" on "${network.name}".`,
1667
+ CaatingaErrorCode.ARTIFACT_NOT_FOUND,
1668
+ "Run caatinga deploy for this contract and network before reading it."
1669
+ );
1670
+ }
1671
+ await checkBinary("stellar", "Install Stellar CLI before running caatinga read.");
1672
+ const stellarArgs = [
1673
+ "contract",
1674
+ "invoke",
1675
+ "--id",
1676
+ contractArtifact.contractId,
1677
+ "--source-account",
1678
+ resolveCliSource(options.source),
1679
+ "--send=no",
1680
+ ...buildStellarNetworkArgs(network),
1681
+ "--",
1682
+ target.method,
1683
+ ...options.args ?? []
1684
+ ];
1685
+ const result = await runCommand("stellar", stellarArgs, {
1686
+ cwd,
1687
+ failureCode: CaatingaErrorCode.INVOKE_FAILED
1688
+ });
1689
+ return {
1690
+ target,
1691
+ network,
1692
+ result: result.stdout || result.all
1693
+ };
1694
+ }
1695
+
1518
1696
  // src/templates/create-project-from-template.ts
1519
1697
  import { cp, mkdir as mkdir3, readFile as readFile4, readdir as readdir3, stat as stat2, writeFile as writeFile3 } from "fs/promises";
1520
- import path10 from "path";
1698
+ import path11 from "path";
1521
1699
  import { z as z7 } from "zod";
1522
1700
 
1523
1701
  // src/templates/template-manifest.schema.ts
@@ -1593,8 +1771,8 @@ var TEMPLATE_COPY_EXCLUDED_DIRS = /* @__PURE__ */ new Set([
1593
1771
  ".git"
1594
1772
  ]);
1595
1773
  async function createProjectFromTemplate(options) {
1596
- const targetDir = path10.resolve(options.targetDir);
1597
- const templateDir = path10.resolve(options.templateDir);
1774
+ const targetDir = path11.resolve(options.targetDir);
1775
+ const templateDir = path11.resolve(options.templateDir);
1598
1776
  try {
1599
1777
  await stat2(templateDir);
1600
1778
  } catch {
@@ -1632,7 +1810,7 @@ async function ensureArtifacts(targetDir, projectName) {
1632
1810
  }
1633
1811
  }
1634
1812
  async function readTemplateManifest(templateDir) {
1635
- const manifestPath = path10.join(templateDir, "caatinga.template.json");
1813
+ const manifestPath = path11.join(templateDir, "caatinga.template.json");
1636
1814
  try {
1637
1815
  const rawManifest = await readFile4(manifestPath, "utf8");
1638
1816
  const manifest = TemplateManifestSchema.parse(JSON.parse(rawManifest));
@@ -1669,7 +1847,7 @@ async function readTemplateManifest(templateDir) {
1669
1847
  async function replaceTemplateVariables(dir, projectName) {
1670
1848
  const entries = await readdir3(dir);
1671
1849
  await Promise.all(entries.map(async (entry) => {
1672
- const entryPath = path10.join(dir, entry);
1850
+ const entryPath = path11.join(dir, entry);
1673
1851
  const entryStat = await stat2(entryPath);
1674
1852
  if (entryStat.isDirectory()) {
1675
1853
  await replaceTemplateVariables(entryPath, projectName);
@@ -1683,15 +1861,15 @@ async function replaceTemplateVariables(dir, projectName) {
1683
1861
  }));
1684
1862
  }
1685
1863
  function shouldCopyTemplateEntry(templateDir, source, userFilter) {
1686
- const relativePath = path10.relative(templateDir, source);
1864
+ const relativePath = path11.relative(templateDir, source);
1687
1865
  if (!relativePath || relativePath === ".") {
1688
1866
  return true;
1689
1867
  }
1690
- const normalizedPath = relativePath.split(path10.sep).join("/");
1868
+ const normalizedPath = relativePath.split(path11.sep).join("/");
1691
1869
  if (userFilter && !userFilter(normalizedPath)) {
1692
1870
  return false;
1693
1871
  }
1694
- return !relativePath.split(path10.sep).some((segment) => TEMPLATE_COPY_EXCLUDED_DIRS.has(segment));
1872
+ return !relativePath.split(path11.sep).some((segment) => TEMPLATE_COPY_EXCLUDED_DIRS.has(segment));
1695
1873
  }
1696
1874
  function isTextTemplateFile(filePath) {
1697
1875
  return [
@@ -1703,7 +1881,222 @@ function isTextTemplateFile(filePath) {
1703
1881
  ".tsx",
1704
1882
  ".css",
1705
1883
  ".html"
1706
- ].includes(path10.extname(filePath));
1884
+ ].includes(path11.extname(filePath));
1885
+ }
1886
+
1887
+ // src/scaffold/create-zk-project.ts
1888
+ import { cp as cp2, mkdir as mkdir4, writeFile as writeFile4 } from "fs/promises";
1889
+ import { existsSync as existsSync2 } from "fs";
1890
+ import path12 from "path";
1891
+ import { fileURLToPath } from "url";
1892
+ var moduleDir = typeof __dirname === "string" ? __dirname : path12.dirname(fileURLToPath(import.meta.url));
1893
+ function scaffoldRoot() {
1894
+ const candidates = [
1895
+ path12.resolve(moduleDir, "../../scaffolds"),
1896
+ path12.resolve(moduleDir, "../scaffolds")
1897
+ ];
1898
+ const found = candidates.find((candidate) => existsSync2(candidate));
1899
+ return found ?? candidates[0];
1900
+ }
1901
+ function configSource(projectName) {
1902
+ return `import { defineConfig } from "@caatinga/core";
1903
+
1904
+ export default defineConfig({
1905
+ project: "${projectName}",
1906
+ defaultNetwork: "testnet",
1907
+ contracts: {
1908
+ verifier: {
1909
+ path: "./contracts/verifier",
1910
+ wasm: "./contracts/verifier/target/wasm32v1-none/release/verifier.wasm"
1911
+ }
1912
+ },
1913
+ networks: {
1914
+ testnet: {
1915
+ rpcUrl: "https://soroban-testnet.stellar.org",
1916
+ networkPassphrase: "Test SDF Network ; September 2015"
1917
+ }
1918
+ },
1919
+ zk: {
1920
+ circuits: {
1921
+ main: {
1922
+ path: "./circuits",
1923
+ protocol: "groth16",
1924
+ curve: "bls12381",
1925
+ verifierContract: "verifier"
1926
+ }
1927
+ }
1928
+ }
1929
+ });
1930
+ `;
1931
+ }
1932
+ function packageJsonSource(projectName) {
1933
+ return `${JSON.stringify({
1934
+ name: projectName,
1935
+ version: "0.1.0",
1936
+ private: true,
1937
+ type: "module",
1938
+ scripts: {
1939
+ "zk:build": "caatinga zk build main",
1940
+ "zk:prove": "caatinga zk prove main",
1941
+ build: "caatinga build verifier",
1942
+ deploy: "caatinga deploy verifier"
1943
+ },
1944
+ devDependencies: {
1945
+ "@caatinga/cli": `^${CAATINGA_CORE_VERSION}`,
1946
+ "@caatinga/core": `^${CAATINGA_CORE_VERSION}`
1947
+ }
1948
+ }, null, 2)}
1949
+ `;
1950
+ }
1951
+ function readmeSource(projectName) {
1952
+ return `# ${projectName}
1953
+
1954
+ Minimal Caatinga ZK project.
1955
+
1956
+ ## Workflow
1957
+
1958
+ \`\`\`bash
1959
+ npm install
1960
+ npx caatinga zk build main
1961
+ npx caatinga build verifier
1962
+ npx caatinga deploy verifier --network testnet
1963
+ npx caatinga zk prove main
1964
+ \`\`\`
1965
+
1966
+ Replace \`circuits/main.circom\` with your circuit. Keep the entry point named \`main\`.
1967
+ `;
1968
+ }
1969
+ async function createZkProject(options) {
1970
+ const targetDir = path12.resolve(options.targetDir);
1971
+ const force = options.force ?? false;
1972
+ const projectFiles = options.projectFiles ?? true;
1973
+ await mkdir4(targetDir, { recursive: true });
1974
+ if (projectFiles) {
1975
+ await Promise.all([
1976
+ writeFile4(path12.join(targetDir, "caatinga.config.ts"), configSource(options.projectName), { encoding: "utf8", flag: force ? "w" : "wx" }),
1977
+ writeFile4(path12.join(targetDir, "package.json"), packageJsonSource(options.projectName), { encoding: "utf8", flag: force ? "w" : "wx" }),
1978
+ writeFile4(path12.join(targetDir, ".gitignore"), "node_modules\n.artifacts\ntarget\n", { encoding: "utf8", flag: force ? "w" : "wx" }),
1979
+ writeFile4(path12.join(targetDir, "README.md"), readmeSource(options.projectName), { encoding: "utf8", flag: force ? "w" : "wx" })
1980
+ ]);
1981
+ }
1982
+ await mkdir4(path12.join(targetDir, "contracts"), { recursive: true });
1983
+ await cp2(path12.join(scaffoldRoot(), "zk-circuit-stub"), path12.join(targetDir, "circuits"), {
1984
+ recursive: true,
1985
+ force,
1986
+ errorOnExist: !force
1987
+ });
1988
+ await cp2(path12.join(scaffoldRoot(), "zk-verifier"), path12.join(targetDir, "contracts", "verifier"), {
1989
+ recursive: true,
1990
+ force,
1991
+ errorOnExist: !force
1992
+ });
1993
+ if (projectFiles) {
1994
+ await writeArtifacts(createInitialArtifacts(options.projectName, { networks: ["testnet"] }), targetDir);
1995
+ }
1996
+ return { targetDir };
1997
+ }
1998
+
1999
+ // src/scaffold/create-minimal-project.ts
2000
+ import { cp as cp3, mkdir as mkdir5, writeFile as writeFile5 } from "fs/promises";
2001
+ import { existsSync as existsSync3 } from "fs";
2002
+ import path13 from "path";
2003
+ import { fileURLToPath as fileURLToPath2 } from "url";
2004
+ var moduleDir2 = typeof __dirname === "string" ? __dirname : path13.dirname(fileURLToPath2(import.meta.url));
2005
+ function scaffoldRoot2() {
2006
+ const candidates = [
2007
+ path13.resolve(moduleDir2, "../../scaffolds"),
2008
+ path13.resolve(moduleDir2, "../scaffolds")
2009
+ ];
2010
+ const found = candidates.find((candidate) => existsSync3(candidate));
2011
+ return found ?? candidates[0];
2012
+ }
2013
+ function configSource2(projectName) {
2014
+ return `import { defineConfig } from "@caatinga/core";
2015
+
2016
+ export default defineConfig({
2017
+ project: "${projectName}",
2018
+ defaultNetwork: "testnet",
2019
+ contracts: {
2020
+ app: {
2021
+ path: "./contracts/app",
2022
+ wasm: "./contracts/app/target/wasm32v1-none/release/app.wasm"
2023
+ }
2024
+ },
2025
+ networks: {
2026
+ testnet: {
2027
+ rpcUrl: "https://soroban-testnet.stellar.org",
2028
+ networkPassphrase: "Test SDF Network ; September 2015"
2029
+ }
2030
+ }
2031
+ });
2032
+ `;
2033
+ }
2034
+ function packageJsonSource2(projectName) {
2035
+ return `${JSON.stringify({
2036
+ name: projectName,
2037
+ version: "0.1.0",
2038
+ private: true,
2039
+ type: "module",
2040
+ scripts: {
2041
+ build: "caatinga build app",
2042
+ deploy: "caatinga deploy app --network testnet --source ${CAATINGA_SOURCE:-alice}",
2043
+ doctor: "caatinga doctor",
2044
+ "read:hello": "caatinga read app.hello --network testnet --source ${CAATINGA_SOURCE:-alice}",
2045
+ "read:version": "caatinga read app.version --network testnet --source ${CAATINGA_SOURCE:-alice}"
2046
+ },
2047
+ devDependencies: {
2048
+ "@caatinga/cli": `^${CAATINGA_CORE_VERSION}`,
2049
+ "@caatinga/core": `^${CAATINGA_CORE_VERSION}`
2050
+ }
2051
+ }, null, 2)}
2052
+ `;
2053
+ }
2054
+ function readmeSource2(projectName) {
2055
+ return `# ${projectName}
2056
+
2057
+ Minimal Caatinga project with a Soroban contract stub (no frontend template).
2058
+
2059
+ ## Workflow
2060
+
2061
+ \`\`\`bash
2062
+ npm install
2063
+ npx caatinga doctor
2064
+ npx caatinga build app
2065
+ npx caatinga deploy app --network testnet --source <identity>
2066
+ npx caatinga read app.version --network testnet
2067
+ npx caatinga read app.hello --network testnet
2068
+ \`\`\`
2069
+
2070
+ ## Contract
2071
+
2072
+ - \`hello()\` \u2014 read-only; returns Soroban Symbol \`hello\`
2073
+ - \`version()\` \u2014 read-only; returns \`1\`
2074
+
2075
+ Use \`caatinga read\` for read-only methods. Use \`caatinga invoke\` only after you add state-changing methods to the contract.
2076
+
2077
+ 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).
2078
+
2079
+ Edit \`contracts/app/src/lib.rs\` to customize the contract. Add a frontend later with \`@caatinga/client\` and your chosen UI stack.
2080
+ `;
2081
+ }
2082
+ async function createMinimalProject(options) {
2083
+ const targetDir = path13.resolve(options.targetDir);
2084
+ const force = options.force ?? false;
2085
+ await mkdir5(targetDir, { recursive: true });
2086
+ await Promise.all([
2087
+ writeFile5(path13.join(targetDir, "caatinga.config.ts"), configSource2(options.projectName), { encoding: "utf8", flag: force ? "w" : "wx" }),
2088
+ writeFile5(path13.join(targetDir, "package.json"), packageJsonSource2(options.projectName), { encoding: "utf8", flag: force ? "w" : "wx" }),
2089
+ writeFile5(path13.join(targetDir, ".gitignore"), "node_modules\n.artifacts\ntarget\n", { encoding: "utf8", flag: force ? "w" : "wx" }),
2090
+ writeFile5(path13.join(targetDir, "README.md"), readmeSource2(options.projectName), { encoding: "utf8", flag: force ? "w" : "wx" })
2091
+ ]);
2092
+ await mkdir5(path13.join(targetDir, "contracts"), { recursive: true });
2093
+ await cp3(path13.join(scaffoldRoot2(), "soroban-contract-stub"), path13.join(targetDir, "contracts", "app"), {
2094
+ recursive: true,
2095
+ force,
2096
+ errorOnExist: !force
2097
+ });
2098
+ await writeArtifacts(createInitialArtifacts(options.projectName, { networks: ["testnet"] }), targetDir);
2099
+ return { targetDir };
1707
2100
  }
1708
2101
 
1709
2102
  // src/ci/is-transient-testnet-smoke-failure.ts
@@ -1734,17 +2127,21 @@ export {
1734
2127
  CaatingaConfigSchema,
1735
2128
  CaatingaError,
1736
2129
  CaatingaErrorCode,
2130
+ READ_CALL_FAILURE_REGEX,
1737
2131
  STELLAR_CLI_LAST_TESTED_VERSION,
1738
2132
  STELLAR_CLI_MIN_VERSION,
1739
2133
  TemplateManifestSchema,
1740
2134
  WELL_KNOWN_NETWORKS,
1741
2135
  buildContract,
1742
2136
  buildDependencyGraph,
2137
+ buildReadCallHint,
1743
2138
  checkBinary,
1744
2139
  checkStellarCliVersion,
1745
2140
  collectProjectStatus,
1746
2141
  createInitialArtifacts,
2142
+ createMinimalProject,
1747
2143
  createProjectFromTemplate,
2144
+ createZkProject,
1748
2145
  defineConfig,
1749
2146
  deployContract,
1750
2147
  deployContractGraph,
@@ -1755,6 +2152,8 @@ export {
1755
2152
  generateBindings,
1756
2153
  generateBindingsGraph,
1757
2154
  invokeContract,
2155
+ isCargoBinMissingFromPath,
2156
+ isReadCallFailure,
1758
2157
  isTransientTestnetSmokeFailure,
1759
2158
  loadConfig,
1760
2159
  parseContractId,
@@ -1762,11 +2161,13 @@ export {
1762
2161
  parseStellarCliVersion,
1763
2162
  readArtifacts,
1764
2163
  readBindingMarker,
2164
+ readContract,
1765
2165
  resolveContract,
1766
2166
  resolveDefaultContractName,
1767
2167
  resolveDeployArgs,
1768
2168
  resolveDeployOrder,
1769
2169
  resolveNetwork,
2170
+ resolveSubprocessEnv,
1770
2171
  runCommand,
1771
2172
  toCaatingaError,
1772
2173
  updateArtifact,