@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/{browser-Cq4ZofIq.d.cts → artifact.schema-BH5K-IiZ.d.cts} +2 -1
- package/dist/{browser-Cq4ZofIq.d.ts → artifact.schema-BH5K-IiZ.d.ts} +2 -1
- package/dist/browser.cjs +20 -0
- package/dist/browser.d.cts +5 -1
- package/dist/browser.d.ts +5 -1
- package/dist/browser.js +19 -0
- package/dist/index.cjs +322 -42
- package/dist/index.d.cts +35 -5
- package/dist/index.d.ts +35 -5
- package/dist/index.js +314 -42
- package/package.json +1 -1
- package/scaffolds/soroban-contract-stub/Cargo.toml +24 -0
- package/scaffolds/soroban-contract-stub/src/lib.rs +42 -0
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.
|
|
109
|
+
var CAATINGA_CORE_VERSION = "2.4.0";
|
|
109
110
|
|
|
110
111
|
// src/config/config.schema.ts
|
|
111
112
|
import { z } from "zod";
|
|
@@ -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
|
}
|
|
@@ -587,6 +618,64 @@ function defaultEmitWarning(warning) {
|
|
|
587
618
|
`);
|
|
588
619
|
}
|
|
589
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
|
+
|
|
590
679
|
// src/shell/run-command.ts
|
|
591
680
|
async function runCommand(command, args, options = {}) {
|
|
592
681
|
try {
|
|
@@ -595,7 +684,7 @@ async function runCommand(command, args, options = {}) {
|
|
|
595
684
|
}
|
|
596
685
|
const result = await execa(command, args, {
|
|
597
686
|
cwd: options.cwd,
|
|
598
|
-
env: options.env,
|
|
687
|
+
env: resolveSubprocessEnv(options.env ?? {}),
|
|
599
688
|
input: options.input,
|
|
600
689
|
all: true,
|
|
601
690
|
reject: true
|
|
@@ -793,7 +882,7 @@ function validateSourceShape(source) {
|
|
|
793
882
|
}
|
|
794
883
|
|
|
795
884
|
// src/contracts/resolve-contract.ts
|
|
796
|
-
import
|
|
885
|
+
import path7 from "path";
|
|
797
886
|
function resolveContract(config, contractName, cwd = process.cwd()) {
|
|
798
887
|
const contract = config.contracts[contractName];
|
|
799
888
|
if (!contract) {
|
|
@@ -806,8 +895,8 @@ function resolveContract(config, contractName, cwd = process.cwd()) {
|
|
|
806
895
|
return {
|
|
807
896
|
name: contractName,
|
|
808
897
|
config: contract,
|
|
809
|
-
sourcePath:
|
|
810
|
-
wasmPath:
|
|
898
|
+
sourcePath: path7.resolve(cwd, contract.path),
|
|
899
|
+
wasmPath: path7.resolve(cwd, contract.wasm)
|
|
811
900
|
};
|
|
812
901
|
}
|
|
813
902
|
|
|
@@ -827,7 +916,7 @@ function resolveDefaultContractName(config) {
|
|
|
827
916
|
// src/contracts/wasm.ts
|
|
828
917
|
import { createHash } from "crypto";
|
|
829
918
|
import { access as access2, readdir as readdir2, readFile as readFile3, stat } from "fs/promises";
|
|
830
|
-
import
|
|
919
|
+
import path8 from "path";
|
|
831
920
|
var LEGACY_RUST_WASM_TARGET = "wasm32-unknown-unknown";
|
|
832
921
|
var CURRENT_RUST_WASM_TARGET = "wasm32v1-none";
|
|
833
922
|
function toCurrentWasmTargetPath(wasmPath) {
|
|
@@ -850,8 +939,8 @@ function wasmNotFoundError(configuredWasmPath, options) {
|
|
|
850
939
|
);
|
|
851
940
|
}
|
|
852
941
|
function toConfigRelativeWasmPath(absoluteWasmPath) {
|
|
853
|
-
const relative =
|
|
854
|
-
return relative.startsWith("..") ? absoluteWasmPath : `./${relative.split(
|
|
942
|
+
const relative = path8.relative(process.cwd(), absoluteWasmPath);
|
|
943
|
+
return relative.startsWith("..") ? absoluteWasmPath : `./${relative.split(path8.sep).join("/")}`;
|
|
855
944
|
}
|
|
856
945
|
async function resolveWasmArtifactPath(configuredWasmPath) {
|
|
857
946
|
try {
|
|
@@ -884,7 +973,7 @@ async function getNewestMtimeInDirectory(directory) {
|
|
|
884
973
|
async function walk(dir) {
|
|
885
974
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
886
975
|
for (const entry of entries) {
|
|
887
|
-
const entryPath =
|
|
976
|
+
const entryPath = path8.join(dir, entry.name);
|
|
888
977
|
if (entry.isDirectory()) {
|
|
889
978
|
await walk(entryPath);
|
|
890
979
|
continue;
|
|
@@ -900,7 +989,7 @@ async function getNewestMtimeInDirectory(directory) {
|
|
|
900
989
|
return newest > 0 ? newest : void 0;
|
|
901
990
|
}
|
|
902
991
|
async function isWasmOlderThanSources(input) {
|
|
903
|
-
const srcDir =
|
|
992
|
+
const srcDir = path8.join(input.contractPath, "src");
|
|
904
993
|
const newestSourceMtime = await getNewestMtimeInDirectory(srcDir);
|
|
905
994
|
if (newestSourceMtime === void 0) {
|
|
906
995
|
return false;
|
|
@@ -973,7 +1062,7 @@ async function buildContract(options) {
|
|
|
973
1062
|
}
|
|
974
1063
|
|
|
975
1064
|
// src/contracts/deploy-contract.ts
|
|
976
|
-
import
|
|
1065
|
+
import path9 from "path";
|
|
977
1066
|
|
|
978
1067
|
// src/contracts/dependency-graph.ts
|
|
979
1068
|
function buildDependencyGraph(contracts) {
|
|
@@ -1030,6 +1119,9 @@ function assertSafeSourceAccount(source) {
|
|
|
1030
1119
|
}
|
|
1031
1120
|
return source;
|
|
1032
1121
|
}
|
|
1122
|
+
function resolveCliSource(explicit) {
|
|
1123
|
+
return assertSafeSourceAccount(explicit ?? process.env.CAATINGA_SOURCE ?? "alice");
|
|
1124
|
+
}
|
|
1033
1125
|
|
|
1034
1126
|
// src/contracts/deploy-contract.ts
|
|
1035
1127
|
function toSnakeCaseFlag(key) {
|
|
@@ -1074,7 +1166,7 @@ async function deployContract(options) {
|
|
|
1074
1166
|
contract: contractWithWasm,
|
|
1075
1167
|
network,
|
|
1076
1168
|
contractId: existing.contractId,
|
|
1077
|
-
artifactsPath:
|
|
1169
|
+
artifactsPath: path9.resolve(cwd, "caatinga.artifacts.json"),
|
|
1078
1170
|
output: "",
|
|
1079
1171
|
skipped: true,
|
|
1080
1172
|
staleWasmWarning
|
|
@@ -1351,13 +1443,13 @@ async function deployContractGraph(options) {
|
|
|
1351
1443
|
|
|
1352
1444
|
// src/contracts/generate-bindings.ts
|
|
1353
1445
|
import { access as access3, mkdir as mkdir2, unlink } from "fs/promises";
|
|
1354
|
-
import
|
|
1446
|
+
import path10 from "path";
|
|
1355
1447
|
function toBindingImportPath(bindingsOutput, contractName) {
|
|
1356
|
-
const normalized = bindingsOutput.replace(/^\.\//, "").split(
|
|
1357
|
-
return `./${
|
|
1448
|
+
const normalized = bindingsOutput.replace(/^\.\//, "").split(path10.sep).join("/");
|
|
1449
|
+
return `./${path10.posix.join(normalized, contractName, "src", "index.js")}`;
|
|
1358
1450
|
}
|
|
1359
1451
|
async function removeLegacyBindingStub(cwd, bindingsOutput, contractName) {
|
|
1360
|
-
const legacyPath =
|
|
1452
|
+
const legacyPath = path10.resolve(cwd, bindingsOutput, `${contractName}.ts`);
|
|
1361
1453
|
try {
|
|
1362
1454
|
await access3(legacyPath);
|
|
1363
1455
|
await unlink(legacyPath);
|
|
@@ -1386,7 +1478,7 @@ async function generateBindings(options) {
|
|
|
1386
1478
|
);
|
|
1387
1479
|
}
|
|
1388
1480
|
await checkBinary("stellar", "Install Stellar CLI before running caatinga generate.");
|
|
1389
|
-
const outputDir =
|
|
1481
|
+
const outputDir = path10.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
|
|
1390
1482
|
await mkdir2(outputDir, { recursive: true });
|
|
1391
1483
|
const result = await runCommand("stellar", [
|
|
1392
1484
|
"contract",
|
|
@@ -1460,8 +1552,8 @@ async function generateBindingsGraph(options) {
|
|
|
1460
1552
|
return { network, results };
|
|
1461
1553
|
}
|
|
1462
1554
|
|
|
1463
|
-
// src/contracts/invoke-
|
|
1464
|
-
var
|
|
1555
|
+
// src/contracts/invoke-target.ts
|
|
1556
|
+
var READ_CALL_FAILURE_REGEX = /this is a read call|read-only/i;
|
|
1465
1557
|
function parseInvokeTarget(target) {
|
|
1466
1558
|
const [contractName, method, extra] = target.split(".");
|
|
1467
1559
|
if (!contractName || !method || extra) {
|
|
@@ -1473,6 +1565,29 @@ function parseInvokeTarget(target) {
|
|
|
1473
1565
|
}
|
|
1474
1566
|
return { contractName, method };
|
|
1475
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;
|
|
1476
1591
|
async function invokeContract(options) {
|
|
1477
1592
|
const cwd = options.cwd ?? process.cwd();
|
|
1478
1593
|
const network = resolveNetwork(options.config, options.networkName);
|
|
@@ -1506,6 +1621,14 @@ async function invokeContract(options) {
|
|
|
1506
1621
|
failureCode: CaatingaErrorCode.INVOKE_FAILED
|
|
1507
1622
|
});
|
|
1508
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
|
+
}
|
|
1509
1632
|
if (error instanceof CaatingaError && error.code === CaatingaErrorCode.INVOKE_FAILED && INVOKE_SIGNING_FAILURE_REGEX.test(`${error.message}
|
|
1510
1633
|
${error.hint ?? ""}`)) {
|
|
1511
1634
|
throw new CaatingaError(
|
|
@@ -1531,9 +1654,48 @@ ${error.hint ?? ""}`)) {
|
|
|
1531
1654
|
};
|
|
1532
1655
|
}
|
|
1533
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
|
+
|
|
1534
1696
|
// src/templates/create-project-from-template.ts
|
|
1535
1697
|
import { cp, mkdir as mkdir3, readFile as readFile4, readdir as readdir3, stat as stat2, writeFile as writeFile3 } from "fs/promises";
|
|
1536
|
-
import
|
|
1698
|
+
import path11 from "path";
|
|
1537
1699
|
import { z as z7 } from "zod";
|
|
1538
1700
|
|
|
1539
1701
|
// src/templates/template-manifest.schema.ts
|
|
@@ -1609,8 +1771,8 @@ var TEMPLATE_COPY_EXCLUDED_DIRS = /* @__PURE__ */ new Set([
|
|
|
1609
1771
|
".git"
|
|
1610
1772
|
]);
|
|
1611
1773
|
async function createProjectFromTemplate(options) {
|
|
1612
|
-
const targetDir =
|
|
1613
|
-
const templateDir =
|
|
1774
|
+
const targetDir = path11.resolve(options.targetDir);
|
|
1775
|
+
const templateDir = path11.resolve(options.templateDir);
|
|
1614
1776
|
try {
|
|
1615
1777
|
await stat2(templateDir);
|
|
1616
1778
|
} catch {
|
|
@@ -1648,7 +1810,7 @@ async function ensureArtifacts(targetDir, projectName) {
|
|
|
1648
1810
|
}
|
|
1649
1811
|
}
|
|
1650
1812
|
async function readTemplateManifest(templateDir) {
|
|
1651
|
-
const manifestPath =
|
|
1813
|
+
const manifestPath = path11.join(templateDir, "caatinga.template.json");
|
|
1652
1814
|
try {
|
|
1653
1815
|
const rawManifest = await readFile4(manifestPath, "utf8");
|
|
1654
1816
|
const manifest = TemplateManifestSchema.parse(JSON.parse(rawManifest));
|
|
@@ -1685,7 +1847,7 @@ async function readTemplateManifest(templateDir) {
|
|
|
1685
1847
|
async function replaceTemplateVariables(dir, projectName) {
|
|
1686
1848
|
const entries = await readdir3(dir);
|
|
1687
1849
|
await Promise.all(entries.map(async (entry) => {
|
|
1688
|
-
const entryPath =
|
|
1850
|
+
const entryPath = path11.join(dir, entry);
|
|
1689
1851
|
const entryStat = await stat2(entryPath);
|
|
1690
1852
|
if (entryStat.isDirectory()) {
|
|
1691
1853
|
await replaceTemplateVariables(entryPath, projectName);
|
|
@@ -1699,15 +1861,15 @@ async function replaceTemplateVariables(dir, projectName) {
|
|
|
1699
1861
|
}));
|
|
1700
1862
|
}
|
|
1701
1863
|
function shouldCopyTemplateEntry(templateDir, source, userFilter) {
|
|
1702
|
-
const relativePath =
|
|
1864
|
+
const relativePath = path11.relative(templateDir, source);
|
|
1703
1865
|
if (!relativePath || relativePath === ".") {
|
|
1704
1866
|
return true;
|
|
1705
1867
|
}
|
|
1706
|
-
const normalizedPath = relativePath.split(
|
|
1868
|
+
const normalizedPath = relativePath.split(path11.sep).join("/");
|
|
1707
1869
|
if (userFilter && !userFilter(normalizedPath)) {
|
|
1708
1870
|
return false;
|
|
1709
1871
|
}
|
|
1710
|
-
return !relativePath.split(
|
|
1872
|
+
return !relativePath.split(path11.sep).some((segment) => TEMPLATE_COPY_EXCLUDED_DIRS.has(segment));
|
|
1711
1873
|
}
|
|
1712
1874
|
function isTextTemplateFile(filePath) {
|
|
1713
1875
|
return [
|
|
@@ -1719,21 +1881,21 @@ function isTextTemplateFile(filePath) {
|
|
|
1719
1881
|
".tsx",
|
|
1720
1882
|
".css",
|
|
1721
1883
|
".html"
|
|
1722
|
-
].includes(
|
|
1884
|
+
].includes(path11.extname(filePath));
|
|
1723
1885
|
}
|
|
1724
1886
|
|
|
1725
1887
|
// src/scaffold/create-zk-project.ts
|
|
1726
1888
|
import { cp as cp2, mkdir as mkdir4, writeFile as writeFile4 } from "fs/promises";
|
|
1727
|
-
import { existsSync } from "fs";
|
|
1728
|
-
import
|
|
1889
|
+
import { existsSync as existsSync2 } from "fs";
|
|
1890
|
+
import path12 from "path";
|
|
1729
1891
|
import { fileURLToPath } from "url";
|
|
1730
|
-
var moduleDir = typeof __dirname === "string" ? __dirname :
|
|
1892
|
+
var moduleDir = typeof __dirname === "string" ? __dirname : path12.dirname(fileURLToPath(import.meta.url));
|
|
1731
1893
|
function scaffoldRoot() {
|
|
1732
1894
|
const candidates = [
|
|
1733
|
-
|
|
1734
|
-
|
|
1895
|
+
path12.resolve(moduleDir, "../../scaffolds"),
|
|
1896
|
+
path12.resolve(moduleDir, "../scaffolds")
|
|
1735
1897
|
];
|
|
1736
|
-
const found = candidates.find((candidate) =>
|
|
1898
|
+
const found = candidates.find((candidate) => existsSync2(candidate));
|
|
1737
1899
|
return found ?? candidates[0];
|
|
1738
1900
|
}
|
|
1739
1901
|
function configSource(projectName) {
|
|
@@ -1805,25 +1967,25 @@ Replace \`circuits/main.circom\` with your circuit. Keep the entry point named \
|
|
|
1805
1967
|
`;
|
|
1806
1968
|
}
|
|
1807
1969
|
async function createZkProject(options) {
|
|
1808
|
-
const targetDir =
|
|
1970
|
+
const targetDir = path12.resolve(options.targetDir);
|
|
1809
1971
|
const force = options.force ?? false;
|
|
1810
1972
|
const projectFiles = options.projectFiles ?? true;
|
|
1811
1973
|
await mkdir4(targetDir, { recursive: true });
|
|
1812
1974
|
if (projectFiles) {
|
|
1813
1975
|
await Promise.all([
|
|
1814
|
-
writeFile4(
|
|
1815
|
-
writeFile4(
|
|
1816
|
-
writeFile4(
|
|
1817
|
-
writeFile4(
|
|
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" })
|
|
1818
1980
|
]);
|
|
1819
1981
|
}
|
|
1820
|
-
await mkdir4(
|
|
1821
|
-
await cp2(
|
|
1982
|
+
await mkdir4(path12.join(targetDir, "contracts"), { recursive: true });
|
|
1983
|
+
await cp2(path12.join(scaffoldRoot(), "zk-circuit-stub"), path12.join(targetDir, "circuits"), {
|
|
1822
1984
|
recursive: true,
|
|
1823
1985
|
force,
|
|
1824
1986
|
errorOnExist: !force
|
|
1825
1987
|
});
|
|
1826
|
-
await cp2(
|
|
1988
|
+
await cp2(path12.join(scaffoldRoot(), "zk-verifier"), path12.join(targetDir, "contracts", "verifier"), {
|
|
1827
1989
|
recursive: true,
|
|
1828
1990
|
force,
|
|
1829
1991
|
errorOnExist: !force
|
|
@@ -1834,6 +1996,109 @@ async function createZkProject(options) {
|
|
|
1834
1996
|
return { targetDir };
|
|
1835
1997
|
}
|
|
1836
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 };
|
|
2100
|
+
}
|
|
2101
|
+
|
|
1837
2102
|
// src/ci/is-transient-testnet-smoke-failure.ts
|
|
1838
2103
|
var NO_RETRY_CAATINGA_SUBSTRINGS = [
|
|
1839
2104
|
"CAATINGA_UNSUPPORTED_CLI_VERSION",
|
|
@@ -1862,16 +2127,19 @@ export {
|
|
|
1862
2127
|
CaatingaConfigSchema,
|
|
1863
2128
|
CaatingaError,
|
|
1864
2129
|
CaatingaErrorCode,
|
|
2130
|
+
READ_CALL_FAILURE_REGEX,
|
|
1865
2131
|
STELLAR_CLI_LAST_TESTED_VERSION,
|
|
1866
2132
|
STELLAR_CLI_MIN_VERSION,
|
|
1867
2133
|
TemplateManifestSchema,
|
|
1868
2134
|
WELL_KNOWN_NETWORKS,
|
|
1869
2135
|
buildContract,
|
|
1870
2136
|
buildDependencyGraph,
|
|
2137
|
+
buildReadCallHint,
|
|
1871
2138
|
checkBinary,
|
|
1872
2139
|
checkStellarCliVersion,
|
|
1873
2140
|
collectProjectStatus,
|
|
1874
2141
|
createInitialArtifacts,
|
|
2142
|
+
createMinimalProject,
|
|
1875
2143
|
createProjectFromTemplate,
|
|
1876
2144
|
createZkProject,
|
|
1877
2145
|
defineConfig,
|
|
@@ -1884,6 +2152,8 @@ export {
|
|
|
1884
2152
|
generateBindings,
|
|
1885
2153
|
generateBindingsGraph,
|
|
1886
2154
|
invokeContract,
|
|
2155
|
+
isCargoBinMissingFromPath,
|
|
2156
|
+
isReadCallFailure,
|
|
1887
2157
|
isTransientTestnetSmokeFailure,
|
|
1888
2158
|
loadConfig,
|
|
1889
2159
|
parseContractId,
|
|
@@ -1891,11 +2161,13 @@ export {
|
|
|
1891
2161
|
parseStellarCliVersion,
|
|
1892
2162
|
readArtifacts,
|
|
1893
2163
|
readBindingMarker,
|
|
2164
|
+
readContract,
|
|
1894
2165
|
resolveContract,
|
|
1895
2166
|
resolveDefaultContractName,
|
|
1896
2167
|
resolveDeployArgs,
|
|
1897
2168
|
resolveDeployOrder,
|
|
1898
2169
|
resolveNetwork,
|
|
2170
|
+
resolveSubprocessEnv,
|
|
1899
2171
|
runCommand,
|
|
1900
2172
|
toCaatingaError,
|
|
1901
2173
|
updateArtifact,
|
package/package.json
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "app"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
rust-version = "1.84.0"
|
|
5
|
+
edition = "2021"
|
|
6
|
+
|
|
7
|
+
[lib]
|
|
8
|
+
crate-type = ["cdylib"]
|
|
9
|
+
|
|
10
|
+
[dependencies]
|
|
11
|
+
soroban-sdk = "22.0.1"
|
|
12
|
+
|
|
13
|
+
[dev-dependencies]
|
|
14
|
+
soroban-sdk = { version = "22.0.1", features = ["testutils"] }
|
|
15
|
+
|
|
16
|
+
[profile.release]
|
|
17
|
+
opt-level = "z"
|
|
18
|
+
overflow-checks = true
|
|
19
|
+
debug = 0
|
|
20
|
+
strip = "symbols"
|
|
21
|
+
debug-assertions = false
|
|
22
|
+
panic = "abort"
|
|
23
|
+
codegen-units = 1
|
|
24
|
+
lto = true
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#![no_std]
|
|
2
|
+
|
|
3
|
+
use soroban_sdk::{contract, contractimpl, Env, Symbol};
|
|
4
|
+
|
|
5
|
+
#[contract]
|
|
6
|
+
pub struct AppContract;
|
|
7
|
+
|
|
8
|
+
#[contractimpl]
|
|
9
|
+
impl AppContract {
|
|
10
|
+
pub fn hello(env: Env) -> Symbol {
|
|
11
|
+
Symbol::new(&env, "hello")
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
pub fn version(env: Env) -> u32 {
|
|
15
|
+
let _ = env;
|
|
16
|
+
1
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
#[cfg(test)]
|
|
21
|
+
mod test {
|
|
22
|
+
use super::*;
|
|
23
|
+
use soroban_sdk::Env;
|
|
24
|
+
|
|
25
|
+
#[test]
|
|
26
|
+
fn returns_hello_symbol() {
|
|
27
|
+
let env = Env::default();
|
|
28
|
+
let contract_id = env.register(AppContract, ());
|
|
29
|
+
let client = AppContractClient::new(&env, &contract_id);
|
|
30
|
+
|
|
31
|
+
assert_eq!(client.hello(), Symbol::new(&env, "hello"));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#[test]
|
|
35
|
+
fn returns_version() {
|
|
36
|
+
let env = Env::default();
|
|
37
|
+
let contract_id = env.register(AppContract, ());
|
|
38
|
+
let client = AppContractClient::new(&env, &contract_id);
|
|
39
|
+
|
|
40
|
+
assert_eq!(client.version(), 1);
|
|
41
|
+
}
|
|
42
|
+
}
|