@caatinga/core 3.1.2 → 3.3.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
@@ -8,6 +8,8 @@ var CaatingaErrorCode = {
8
8
  STELLAR_CLI_NOT_FOUND: "CAATINGA_STELLAR_CLI_NOT_FOUND",
9
9
  STELLAR_CLI_VERSION_PARSE_FAILED: "CAATINGA_STELLAR_CLI_VERSION_PARSE_FAILED",
10
10
  UNSUPPORTED_CLI_VERSION: "CAATINGA_UNSUPPORTED_CLI_VERSION",
11
+ STELLAR_SDK_VERSION_PARSE_FAILED: "CAATINGA_STELLAR_SDK_VERSION_PARSE_FAILED",
12
+ UNSUPPORTED_SDK_VERSION: "CAATINGA_UNSUPPORTED_SDK_VERSION",
11
13
  RUST_NOT_FOUND: "CAATINGA_RUST_NOT_FOUND",
12
14
  RUST_TARGET_NOT_FOUND: "CAATINGA_RUST_TARGET_NOT_FOUND",
13
15
  DEPLOY_FAILED: "CAATINGA_DEPLOY_FAILED",
@@ -48,7 +50,10 @@ var CaatingaErrorCode = {
48
50
  TEMPLATE_MANIFEST_NOT_FOUND: "CAATINGA_TEMPLATE_MANIFEST_NOT_FOUND",
49
51
  TEMPLATE_INCOMPATIBLE: "CAATINGA_TEMPLATE_INCOMPATIBLE",
50
52
  ZK_VERIFICATION_FAILED: "CAATINGA_ZK_VERIFICATION_FAILED",
53
+ ZK_DEV_CEREMONY_BLOCKED: "CAATINGA_ZK_DEV_CEREMONY_BLOCKED",
51
54
  DOCTOR_PARTIAL_DEPLOY: "CAATINGA_DOCTOR_PARTIAL_DEPLOY",
55
+ ROLLBACK_TARGET_NOT_FOUND: "CAATINGA_ROLLBACK_TARGET_NOT_FOUND",
56
+ ESTIMATE_FAILED: "CAATINGA_ESTIMATE_FAILED",
52
57
  MULTI_AUTH_REQUIRED: "CAATINGA_MULTI_AUTH_REQUIRED"
53
58
  };
54
59
 
@@ -66,7 +71,8 @@ var CaatingaError = class extends Error {
66
71
  cause;
67
72
  };
68
73
  var ZK_ERROR_CODE_MAP = {
69
- ZK_VERIFY_FAILED: CaatingaErrorCode.ZK_VERIFICATION_FAILED
74
+ ZK_VERIFY_FAILED: CaatingaErrorCode.ZK_VERIFICATION_FAILED,
75
+ ZK_DEV_CEREMONY_BLOCKED: CaatingaErrorCode.ZK_DEV_CEREMONY_BLOCKED
70
76
  };
71
77
  function toCaatingaError(error) {
72
78
  if (error instanceof CaatingaError) {
@@ -158,7 +164,7 @@ var CaatingaConfigSchema = z.object({
158
164
  "At least one network must be configured."
159
165
  ),
160
166
  frontend: z.object({
161
- framework: z.enum(["vite-react", "next", "astro"]).default("vite-react"),
167
+ framework: z.literal("vite-react").default("vite-react"),
162
168
  bindingsOutput: z.string().min(1)
163
169
  }).optional(),
164
170
  zk: ZkConfigSchema
@@ -234,6 +240,14 @@ async function loadConfig(options = {}) {
234
240
 
235
241
  // src/artifacts/artifact.schema.ts
236
242
  import { z as z3 } from "zod";
243
+ var ArtifactSupersedeReasonSchema = z3.enum(["upgrade", "rollback", "force-redeploy"]);
244
+ var ContractArtifactHistoryEntrySchema = z3.object({
245
+ contractId: z3.string().min(1),
246
+ wasmHash: z3.string().min(1),
247
+ deployedAt: z3.string().datetime(),
248
+ supersededAt: z3.string().datetime(),
249
+ reason: ArtifactSupersedeReasonSchema.optional()
250
+ });
237
251
  var ContractArtifactSchema = z3.object({
238
252
  contractId: z3.string().min(1),
239
253
  wasmHash: z3.string().min(1),
@@ -241,17 +255,28 @@ var ContractArtifactSchema = z3.object({
241
255
  sourcePath: z3.string().min(1),
242
256
  wasmPath: z3.string().min(1),
243
257
  dependencies: z3.array(z3.string().min(1)).default([]),
244
- resolvedDeployArgs: z3.record(z3.string().min(1), z3.union([z3.string(), z3.number(), z3.boolean()])).default({})
258
+ resolvedDeployArgs: z3.record(z3.string().min(1), z3.union([z3.string(), z3.number(), z3.boolean()])).default({}),
259
+ history: z3.array(ContractArtifactHistoryEntrySchema).optional()
245
260
  });
246
261
  var NetworkArtifactsSchema = z3.object({
247
262
  contracts: z3.record(z3.string().min(1), ContractArtifactSchema).default({}),
248
263
  dependencyGraph: z3.record(z3.string().min(1), z3.array(z3.string().min(1))).default({})
249
264
  });
250
- var CaatingaArtifactsSchema = z3.object({
265
+ var CaatingaArtifactsBaseSchema = z3.object({
251
266
  project: z3.string().min(1),
252
- version: z3.literal(1),
253
267
  networks: z3.record(z3.string().min(1), NetworkArtifactsSchema).default({})
254
268
  });
269
+ var CaatingaArtifactsV1Schema = CaatingaArtifactsBaseSchema.extend({
270
+ version: z3.literal(1)
271
+ });
272
+ var CaatingaArtifactsV2Schema = CaatingaArtifactsBaseSchema.extend({
273
+ version: z3.literal(2)
274
+ });
275
+ var CaatingaArtifactsSchema = z3.union([
276
+ CaatingaArtifactsV1Schema,
277
+ CaatingaArtifactsV2Schema
278
+ ]);
279
+ var CURRENT_ARTIFACTS_SCHEMA_VERSION = 2;
255
280
 
256
281
  // src/artifacts/read-artifacts.ts
257
282
  import { readFile } from "fs/promises";
@@ -303,29 +328,139 @@ function createInitialArtifacts(project, options = {}) {
303
328
  );
304
329
  return {
305
330
  project,
306
- version: 1,
331
+ version: 2,
307
332
  networks
308
333
  };
309
334
  }
310
335
 
311
336
  // src/artifacts/update-artifact.ts
312
- function updateArtifact(artifacts, networkName, contractName, contractArtifact, networkExtras) {
337
+ function appendHistory(existing, reason) {
338
+ if (!existing || !reason) {
339
+ return existing?.history;
340
+ }
341
+ const supersededAt = (/* @__PURE__ */ new Date()).toISOString();
342
+ const entry = {
343
+ contractId: existing.contractId,
344
+ wasmHash: existing.wasmHash,
345
+ deployedAt: existing.deployedAt,
346
+ supersededAt,
347
+ reason
348
+ };
349
+ return [...existing.history ?? [], entry];
350
+ }
351
+ function updateArtifact(artifacts, networkName, contractName, contractArtifact, options = {}) {
313
352
  const existingNetwork = artifacts.networks[networkName] ?? { contracts: {}, dependencyGraph: {} };
353
+ const existingContract = existingNetwork.contracts[contractName];
354
+ const history = appendHistory(existingContract, options.supersedeReason);
355
+ const nextVersion = artifacts.version === 1 && options.supersedeReason ? 2 : artifacts.version;
314
356
  return {
315
357
  ...artifacts,
358
+ version: nextVersion,
316
359
  networks: {
317
360
  ...artifacts.networks,
318
361
  [networkName]: {
319
362
  ...existingNetwork,
320
- dependencyGraph: networkExtras?.dependencyGraph ?? existingNetwork.dependencyGraph ?? {},
363
+ dependencyGraph: options.dependencyGraph ?? existingNetwork.dependencyGraph ?? {},
321
364
  contracts: {
322
365
  ...existingNetwork.contracts,
323
- [contractName]: contractArtifact
366
+ [contractName]: {
367
+ ...contractArtifact,
368
+ history: history ?? contractArtifact.history
369
+ }
324
370
  }
325
371
  }
326
372
  }
327
373
  };
328
374
  }
375
+ function restoreArtifactFromHistory(input) {
376
+ const network = input.artifacts.networks[input.networkName];
377
+ const current = network?.contracts[input.contractName];
378
+ if (!current) {
379
+ throw new CaatingaError(
380
+ `No artifact for "${input.contractName}" on "${input.networkName}".`,
381
+ CaatingaErrorCode.ARTIFACT_NOT_FOUND,
382
+ "Deploy the contract before attempting rollback."
383
+ );
384
+ }
385
+ if (current.contractId === input.contractId) {
386
+ return input.artifacts;
387
+ }
388
+ const fromHistory = (current.history ?? []).find(
389
+ (entry) => entry.contractId === input.contractId
390
+ );
391
+ if (!fromHistory) {
392
+ throw new CaatingaError(
393
+ `Rollback target "${input.contractId}" was not found in artifact history for "${input.contractName}".`,
394
+ CaatingaErrorCode.ROLLBACK_TARGET_NOT_FOUND,
395
+ "Use caatinga inspect to list prior contract IDs, or redeploy manually."
396
+ );
397
+ }
398
+ const supersededAt = (/* @__PURE__ */ new Date()).toISOString();
399
+ const restoredArtifact = {
400
+ contractId: fromHistory.contractId,
401
+ wasmHash: fromHistory.wasmHash,
402
+ deployedAt: fromHistory.deployedAt,
403
+ sourcePath: current.sourcePath,
404
+ wasmPath: current.wasmPath,
405
+ dependencies: current.dependencies,
406
+ resolvedDeployArgs: current.resolvedDeployArgs,
407
+ history: [
408
+ ...current.history ?? [],
409
+ {
410
+ contractId: current.contractId,
411
+ wasmHash: current.wasmHash,
412
+ deployedAt: current.deployedAt,
413
+ supersededAt,
414
+ reason: "rollback"
415
+ }
416
+ ]
417
+ };
418
+ return {
419
+ ...updateArtifact(input.artifacts, input.networkName, input.contractName, restoredArtifact),
420
+ version: 2
421
+ };
422
+ }
423
+
424
+ // src/artifacts/migrate-artifacts.ts
425
+ function migrateArtifactsToV2(artifacts) {
426
+ if (artifacts.version === CURRENT_ARTIFACTS_SCHEMA_VERSION) {
427
+ return {
428
+ artifacts,
429
+ migrated: false
430
+ };
431
+ }
432
+ const v1 = artifacts;
433
+ return {
434
+ artifacts: {
435
+ project: v1.project,
436
+ version: CURRENT_ARTIFACTS_SCHEMA_VERSION,
437
+ networks: v1.networks
438
+ },
439
+ migrated: true
440
+ };
441
+ }
442
+
443
+ // src/artifacts/migrate-artifacts-file.ts
444
+ async function migrateArtifactsFile(cwd = process.cwd()) {
445
+ const artifacts = await readArtifacts(cwd);
446
+ const { artifacts: migrated, migrated: changed } = migrateArtifactsToV2(artifacts);
447
+ const path17 = await writeArtifacts(migrated, cwd);
448
+ return { path: path17, migrated: changed, artifacts: migrated };
449
+ }
450
+
451
+ // src/artifacts/rollback-artifact.ts
452
+ async function rollbackContractArtifact(input) {
453
+ const cwd = input.cwd ?? process.cwd();
454
+ const artifacts = await readArtifacts(cwd);
455
+ const next = restoreArtifactFromHistory({
456
+ artifacts,
457
+ networkName: input.networkName,
458
+ contractName: input.contractName,
459
+ contractId: input.contractId
460
+ });
461
+ const path17 = await writeArtifacts(next, cwd);
462
+ return { path: path17, artifacts: next };
463
+ }
329
464
 
330
465
  // src/networks/resolve-network.ts
331
466
  function resolveNetwork(config, networkName) {
@@ -592,6 +727,35 @@ function evaluateStellarCliCompatibility(input) {
592
727
  };
593
728
  }
594
729
 
730
+ // src/stellar-cli/probe-stellar-cli-features.ts
731
+ import semver3 from "semver";
732
+ var STELLAR_CLI_REQUIRED_FEATURES = [
733
+ "contract-build",
734
+ "contract-deploy",
735
+ "contract-invoke-sign"
736
+ ];
737
+ var FEATURE_COMMANDS = {
738
+ "contract-build": ["contract", "build", "--help"],
739
+ "contract-deploy": ["contract", "deploy", "--help"],
740
+ "contract-invoke-sign": ["contract", "invoke", "--help"]
741
+ };
742
+ async function probeMissingStellarCliFeatures(version) {
743
+ const missing = [];
744
+ if (semver3.valid(version) && semver3.lt(version, STELLAR_CLI_MIN_VERSION)) {
745
+ return ["contract-invoke-sign"];
746
+ }
747
+ for (const feature of STELLAR_CLI_REQUIRED_FEATURES) {
748
+ try {
749
+ await runCommand("stellar", FEATURE_COMMANDS[feature], {
750
+ skipStellarVersionCheck: true
751
+ });
752
+ } catch {
753
+ missing.push(feature);
754
+ }
755
+ }
756
+ return missing;
757
+ }
758
+
595
759
  // src/stellar-cli/check-stellar-cli-version.ts
596
760
  async function checkStellarCliVersion(input = {}) {
597
761
  let rawOutput;
@@ -611,9 +775,12 @@ async function checkStellarCliVersion(input = {}) {
611
775
  }
612
776
  throw error;
613
777
  }
778
+ const version = parseStellarCliVersion(rawOutput);
779
+ const probedMissing = input.probeFeatures === false ? [] : await probeMissingStellarCliFeatures(version);
780
+ const missingFeatures = [...input.features ?? [], ...probedMissing];
614
781
  const report = evaluateStellarCliCompatibility({
615
- version: parseStellarCliVersion(rawOutput),
616
- features: input.features,
782
+ version,
783
+ features: missingFeatures.length > 0 ? missingFeatures : void 0,
617
784
  lastTestedVersion: input.lastTestedVersion
618
785
  });
619
786
  for (const warning of report.warnings) {
@@ -759,6 +926,122 @@ function parseContractId(output) {
759
926
  return match[0];
760
927
  }
761
928
 
929
+ // src/stellar-sdk/check-stellar-sdk-version.ts
930
+ import { readFile as readFile3 } from "fs/promises";
931
+ import path7 from "path";
932
+
933
+ // src/stellar-sdk/compat.ts
934
+ import semver4 from "semver";
935
+
936
+ // src/stellar-sdk/version.ts
937
+ var STELLAR_SDK_MIN_VERSION = "16.0.1";
938
+ var STELLAR_SDK_LAST_TESTED_VERSION = "16.0.1";
939
+
940
+ // src/stellar-sdk/compat.ts
941
+ function evaluateStellarSdkCompatibility(input) {
942
+ const parsed = semver4.parse(input.version);
943
+ if (!parsed || !semver4.valid(input.version)) {
944
+ throw new CaatingaError(
945
+ "Could not parse @stellar/stellar-sdk version.",
946
+ CaatingaErrorCode.STELLAR_SDK_VERSION_PARSE_FAILED,
947
+ "Use a semantic version such as 16.0.1."
948
+ );
949
+ }
950
+ const lastTestedVersion = semver4.valid(input.lastTestedVersion ?? STELLAR_SDK_LAST_TESTED_VERSION) ?? STELLAR_SDK_LAST_TESTED_VERSION;
951
+ const warnings = [];
952
+ let status = "supported";
953
+ if (semver4.lt(parsed, STELLAR_SDK_MIN_VERSION)) {
954
+ throw new CaatingaError(
955
+ `@stellar/stellar-sdk ${input.version} is below the supported minimum ${STELLAR_SDK_MIN_VERSION}.`,
956
+ CaatingaErrorCode.UNSUPPORTED_SDK_VERSION,
957
+ `Install @stellar/stellar-sdk ${STELLAR_SDK_MIN_VERSION} or newer.`
958
+ );
959
+ }
960
+ if (semver4.gt(parsed, lastTestedVersion)) {
961
+ status = "untested";
962
+ warnings.push({
963
+ code: "STELLAR_SDK_UNTESTED_VERSION",
964
+ message: `@stellar/stellar-sdk ${input.version} is newer than the last-tested ${lastTestedVersion}; binding output may differ.`,
965
+ remediation: "Pin @stellar/stellar-sdk to the last-tested version in package.json, or update Caatinga fixtures after validating generate output."
966
+ });
967
+ }
968
+ return {
969
+ version: input.version,
970
+ status,
971
+ minVersion: STELLAR_SDK_MIN_VERSION,
972
+ lastTestedVersion,
973
+ warnings
974
+ };
975
+ }
976
+ function parseStellarSdkVersion(raw) {
977
+ const trimmed = raw.trim();
978
+ const match = trimmed.match(/(\d+\.\d+\.\d+(?:[-+][\w.-]+)?)/);
979
+ if (!match) {
980
+ throw new CaatingaError(
981
+ "Could not parse @stellar/stellar-sdk version.",
982
+ CaatingaErrorCode.STELLAR_SDK_VERSION_PARSE_FAILED,
983
+ "Expected output like 16.0.1."
984
+ );
985
+ }
986
+ return match[1];
987
+ }
988
+
989
+ // src/stellar-sdk/check-stellar-sdk-version.ts
990
+ async function readInstalledSdkVersion(cwd) {
991
+ try {
992
+ const pkgPath = path7.join(cwd, "node_modules", "@stellar", "stellar-sdk", "package.json");
993
+ const raw = await readFile3(pkgPath, "utf8");
994
+ const pkg = JSON.parse(raw);
995
+ return typeof pkg.version === "string" ? pkg.version : void 0;
996
+ } catch {
997
+ return void 0;
998
+ }
999
+ }
1000
+ async function resolveRegistrySdkVersion() {
1001
+ const result = await runCommand("npm", ["view", "@stellar/stellar-sdk", "version"], {
1002
+ skipStellarVersionCheck: true
1003
+ });
1004
+ return parseStellarSdkVersion(result.stdout || result.all);
1005
+ }
1006
+ async function checkStellarSdkVersion(input = {}) {
1007
+ const cwd = input.cwd ?? process.cwd();
1008
+ let version;
1009
+ try {
1010
+ const installed = await readInstalledSdkVersion(cwd);
1011
+ version = installed ?? await resolveRegistrySdkVersion();
1012
+ } catch (error) {
1013
+ if (error instanceof CaatingaError) {
1014
+ throw error;
1015
+ }
1016
+ throw new CaatingaError(
1017
+ "Could not resolve @stellar/stellar-sdk version.",
1018
+ CaatingaErrorCode.STELLAR_SDK_VERSION_PARSE_FAILED,
1019
+ "Install @stellar/stellar-sdk in your project or ensure npm registry access.",
1020
+ error
1021
+ );
1022
+ }
1023
+ const report = evaluateStellarSdkCompatibility({
1024
+ version,
1025
+ lastTestedVersion: input.lastTestedVersion
1026
+ });
1027
+ for (const warning of report.warnings) {
1028
+ if (input.onWarning) {
1029
+ input.onWarning(warning);
1030
+ } else {
1031
+ defaultEmitWarning2(warning);
1032
+ }
1033
+ }
1034
+ return report;
1035
+ }
1036
+ function defaultEmitWarning2(warning) {
1037
+ const lines = [
1038
+ `Warning: ${warning.message}`,
1039
+ warning.remediation ? ` ${warning.remediation}` : void 0
1040
+ ].filter((line) => Boolean(line));
1041
+ process.stderr.write(`${lines.join("\n")}
1042
+ `);
1043
+ }
1044
+
762
1045
  // src/stellar-cli/build-stellar-network-args.ts
763
1046
  function matchesWellKnownNetwork(name, config) {
764
1047
  const known = WELL_KNOWN_NETWORKS[name];
@@ -897,7 +1180,7 @@ function validateSourceShape(source) {
897
1180
  }
898
1181
 
899
1182
  // src/contracts/resolve-contract.ts
900
- import path7 from "path";
1183
+ import path8 from "path";
901
1184
  function resolveContract(config, contractName, cwd = process.cwd()) {
902
1185
  const contract = config.contracts[contractName];
903
1186
  if (!contract) {
@@ -910,8 +1193,8 @@ function resolveContract(config, contractName, cwd = process.cwd()) {
910
1193
  return {
911
1194
  name: contractName,
912
1195
  config: contract,
913
- sourcePath: path7.resolve(cwd, contract.path),
914
- wasmPath: path7.resolve(cwd, contract.wasm)
1196
+ sourcePath: path8.resolve(cwd, contract.path),
1197
+ wasmPath: path8.resolve(cwd, contract.wasm)
915
1198
  };
916
1199
  }
917
1200
 
@@ -930,8 +1213,8 @@ function resolveDefaultContractName(config) {
930
1213
 
931
1214
  // src/contracts/wasm.ts
932
1215
  import { createHash } from "crypto";
933
- import { access as access2, readdir as readdir2, readFile as readFile3, stat } from "fs/promises";
934
- import path8 from "path";
1216
+ import { access as access2, readdir as readdir2, readFile as readFile4, stat } from "fs/promises";
1217
+ import path9 from "path";
935
1218
  var LEGACY_RUST_WASM_TARGET = "wasm32-unknown-unknown";
936
1219
  var CURRENT_RUST_WASM_TARGET = "wasm32v1-none";
937
1220
  function toCurrentWasmTargetPath(wasmPath) {
@@ -962,18 +1245,18 @@ function wasmNotFoundError(configuredWasmPath, options) {
962
1245
  );
963
1246
  }
964
1247
  function toConfigRelativeWasmPath(absoluteWasmPath) {
965
- const relative = path8.relative(process.cwd(), absoluteWasmPath);
966
- return relative.startsWith("..") ? absoluteWasmPath : `./${relative.split(path8.sep).join("/")}`;
1248
+ const relative = path9.relative(process.cwd(), absoluteWasmPath);
1249
+ return relative.startsWith("..") ? absoluteWasmPath : `./${relative.split(path9.sep).join("/")}`;
967
1250
  }
968
1251
  function wasmFileName(configuredWasmPath) {
969
- return path8.basename(configuredWasmPath);
1252
+ return path9.basename(configuredWasmPath);
970
1253
  }
971
1254
  function buildAlternateWasmCandidates(configuredWasmPath, options) {
972
1255
  const fileName = wasmFileName(configuredWasmPath);
973
1256
  const candidates = [];
974
1257
  const seen = /* @__PURE__ */ new Set();
975
1258
  function addCandidate(candidate) {
976
- const resolved = path8.resolve(candidate);
1259
+ const resolved = path9.resolve(candidate);
977
1260
  if (seen.has(resolved)) {
978
1261
  return;
979
1262
  }
@@ -982,15 +1265,15 @@ function buildAlternateWasmCandidates(configuredWasmPath, options) {
982
1265
  }
983
1266
  const cargoTargetDir = process.env.CARGO_TARGET_DIR;
984
1267
  if (cargoTargetDir) {
985
- addCandidate(path8.join(cargoTargetDir, CURRENT_RUST_WASM_TARGET, "release", fileName));
986
- addCandidate(path8.join(cargoTargetDir, LEGACY_RUST_WASM_TARGET, "release", fileName));
1268
+ addCandidate(path9.join(cargoTargetDir, CURRENT_RUST_WASM_TARGET, "release", fileName));
1269
+ addCandidate(path9.join(cargoTargetDir, LEGACY_RUST_WASM_TARGET, "release", fileName));
987
1270
  }
988
1271
  if (options?.sourcePath) {
989
1272
  addCandidate(
990
- path8.join(options.sourcePath, "target", CURRENT_RUST_WASM_TARGET, "release", fileName)
1273
+ path9.join(options.sourcePath, "target", CURRENT_RUST_WASM_TARGET, "release", fileName)
991
1274
  );
992
1275
  addCandidate(
993
- path8.join(options.sourcePath, "target", LEGACY_RUST_WASM_TARGET, "release", fileName)
1276
+ path9.join(options.sourcePath, "target", LEGACY_RUST_WASM_TARGET, "release", fileName)
994
1277
  );
995
1278
  }
996
1279
  return candidates;
@@ -1007,7 +1290,7 @@ async function firstExistingPath(paths) {
1007
1290
  return void 0;
1008
1291
  }
1009
1292
  async function resolveWasmArtifactPath(configuredWasmPath, options) {
1010
- const resolvedConfiguredPath = path8.resolve(configuredWasmPath);
1293
+ const resolvedConfiguredPath = path9.resolve(configuredWasmPath);
1011
1294
  try {
1012
1295
  await access2(resolvedConfiguredPath);
1013
1296
  return resolvedConfiguredPath;
@@ -1031,7 +1314,7 @@ async function resolveWasmArtifactPath(configuredWasmPath, options) {
1031
1314
  }
1032
1315
  }
1033
1316
  async function hashWasm(wasmPath) {
1034
- const bytes = await readFile3(wasmPath);
1317
+ const bytes = await readFile4(wasmPath);
1035
1318
  return createHash("sha256").update(bytes).digest("hex");
1036
1319
  }
1037
1320
  async function getNewestMtimeInDirectory(directory) {
@@ -1044,7 +1327,7 @@ async function getNewestMtimeInDirectory(directory) {
1044
1327
  async function walk(dir) {
1045
1328
  const entries = await readdir2(dir, { withFileTypes: true });
1046
1329
  for (const entry of entries) {
1047
- const entryPath = path8.join(dir, entry.name);
1330
+ const entryPath = path9.join(dir, entry.name);
1048
1331
  if (entry.isDirectory()) {
1049
1332
  await walk(entryPath);
1050
1333
  continue;
@@ -1060,7 +1343,7 @@ async function getNewestMtimeInDirectory(directory) {
1060
1343
  return newest > 0 ? newest : void 0;
1061
1344
  }
1062
1345
  async function isWasmOlderThanSources(input) {
1063
- const srcDir = path8.join(input.contractPath, "src");
1346
+ const srcDir = path9.join(input.contractPath, "src");
1064
1347
  const newestSourceMtime = await getNewestMtimeInDirectory(srcDir);
1065
1348
  if (newestSourceMtime === void 0) {
1066
1349
  return false;
@@ -1135,7 +1418,38 @@ async function buildContract(options) {
1135
1418
  }
1136
1419
 
1137
1420
  // src/contracts/deploy-contract.ts
1138
- import path9 from "path";
1421
+ import path10 from "path";
1422
+
1423
+ // src/shell/is-transient-command-failure.ts
1424
+ var NO_RETRY_CAATINGA_SUBSTRINGS = [
1425
+ "CAATINGA_UNSUPPORTED_CLI_VERSION",
1426
+ "CAATINGA_STELLAR_CLI_VERSION_PARSE_FAILED",
1427
+ "CAATINGA_STELLAR_CLI_NOT_FOUND",
1428
+ "CAATINGA_INVALID_CONFIG",
1429
+ "CAATINGA_CONFIG_NOT_FOUND"
1430
+ ];
1431
+ var TRANSIENT_COMMAND_FAILURE_PATTERN = /timeout|i\/o timeout|econnreset|connection reset|503|502|429|rate limit|temporar|bad gateway|fetch failed|network error|unavailable/i;
1432
+ function isTransientCommandFailure(logText) {
1433
+ if (!logText.trim()) {
1434
+ return false;
1435
+ }
1436
+ for (const marker of NO_RETRY_CAATINGA_SUBSTRINGS) {
1437
+ if (logText.includes(marker)) {
1438
+ return false;
1439
+ }
1440
+ }
1441
+ return TRANSIENT_COMMAND_FAILURE_PATTERN.test(logText);
1442
+ }
1443
+
1444
+ // src/contracts/is-transient-deploy-failure.ts
1445
+ function isTransientDeployFailure(error) {
1446
+ if (!(error instanceof CaatingaError) || error.code !== CaatingaErrorCode.DEPLOY_FAILED) {
1447
+ return false;
1448
+ }
1449
+ const logText = `${error.message}
1450
+ ${error.hint ?? ""}`;
1451
+ return isTransientCommandFailure(logText);
1452
+ }
1139
1453
 
1140
1454
  // src/contracts/dependency-graph.ts
1141
1455
  function buildDependencyGraph(contracts) {
@@ -1197,6 +1511,12 @@ function resolveCliSource(explicit) {
1197
1511
  }
1198
1512
 
1199
1513
  // src/contracts/deploy-contract.ts
1514
+ var DEFAULT_DEPLOY_RETRY_DELAYS_MS = [2e3, 5e3];
1515
+ function sleep(ms) {
1516
+ return new Promise((resolve) => {
1517
+ setTimeout(resolve, ms);
1518
+ });
1519
+ }
1200
1520
  function toSnakeCaseFlag(key) {
1201
1521
  return key.replace(/([A-Z])/g, "_$1").replace(/^_/, "").toLowerCase();
1202
1522
  }
@@ -1241,7 +1561,7 @@ async function deployContract(options) {
1241
1561
  contract: contractWithWasm,
1242
1562
  network,
1243
1563
  contractId: existing.contractId,
1244
- artifactsPath: path9.resolve(cwd, "caatinga.artifacts.json"),
1564
+ artifactsPath: path10.resolve(cwd, "caatinga.artifacts.json"),
1245
1565
  output: "",
1246
1566
  skipped: true,
1247
1567
  staleWasmWarning
@@ -1281,40 +1601,69 @@ async function deployContract(options) {
1281
1601
  ...buildStellarNetworkArgs(network),
1282
1602
  ...constructorArgs
1283
1603
  ];
1284
- let output = "";
1285
- let contractId;
1286
- try {
1287
- const result = await runCommand("stellar", stellarArgs, {
1288
- cwd,
1289
- failureCode: CaatingaErrorCode.DEPLOY_FAILED
1290
- });
1291
- output = result.all || `${result.stdout}
1604
+ let deployOutcome;
1605
+ const retryDelaysMs = options.deployRetryDelaysMs ?? DEFAULT_DEPLOY_RETRY_DELAYS_MS;
1606
+ const maxDeployAttempts = retryDelaysMs.length + 1;
1607
+ for (let attempt = 0; attempt < maxDeployAttempts; attempt++) {
1608
+ try {
1609
+ const result = await runCommand("stellar", stellarArgs, {
1610
+ cwd,
1611
+ failureCode: CaatingaErrorCode.DEPLOY_FAILED
1612
+ });
1613
+ const output2 = result.all || `${result.stdout}
1292
1614
  ${result.stderr}`;
1293
- contractId = parseContractId(output);
1294
- } catch (error) {
1295
- if (!(error instanceof CaatingaError) || error.code !== CaatingaErrorCode.DEPLOY_FAILED) {
1296
- throw error;
1297
- }
1298
- const recoveredContractId = await tryRecoverContractIdFromDeployFailure({
1299
- output: `${error.message}
1615
+ deployOutcome = {
1616
+ output: output2,
1617
+ contractId: parseContractId(output2)
1618
+ };
1619
+ break;
1620
+ } catch (error) {
1621
+ if (!(error instanceof CaatingaError) || error.code !== CaatingaErrorCode.DEPLOY_FAILED) {
1622
+ throw error;
1623
+ }
1624
+ const recoveredContractId = await tryRecoverContractIdFromDeployFailure({
1625
+ output: `${error.message}
1300
1626
  ${error.hint ?? ""}`,
1301
- source,
1302
- network: network.config,
1303
- cwd
1304
- });
1305
- if (!recoveredContractId) {
1306
- throw error;
1627
+ source,
1628
+ network: network.config,
1629
+ cwd
1630
+ });
1631
+ if (recoveredContractId) {
1632
+ deployOutcome = {
1633
+ output: [
1634
+ error.hint ?? "",
1635
+ "Caatinga recovered the contract ID from the on-chain deploy transaction.",
1636
+ `Contract ID: ${recoveredContractId}`
1637
+ ].filter(Boolean).join("\n"),
1638
+ contractId: recoveredContractId
1639
+ };
1640
+ break;
1641
+ }
1642
+ const isLastAttempt = attempt === maxDeployAttempts - 1;
1643
+ if (!isTransientDeployFailure(error) || isLastAttempt) {
1644
+ throw error;
1645
+ }
1646
+ const delayMs = retryDelaysMs[attempt] ?? retryDelaysMs[retryDelaysMs.length - 1] ?? 0;
1647
+ options.onTransientDeployRetry?.({
1648
+ attempt: attempt + 1,
1649
+ maxAttempts: maxDeployAttempts,
1650
+ delayMs
1651
+ });
1652
+ await sleep(delayMs);
1307
1653
  }
1308
- contractId = recoveredContractId;
1309
- output = [
1310
- error.hint ?? "",
1311
- "Caatinga recovered the contract ID from the on-chain deploy transaction.",
1312
- `Contract ID: ${contractId}`
1313
- ].filter(Boolean).join("\n");
1314
1654
  }
1655
+ if (!deployOutcome) {
1656
+ throw new CaatingaError(
1657
+ "Deploy failed without a contract ID.",
1658
+ CaatingaErrorCode.DEPLOY_FAILED,
1659
+ "Re-run the deploy command with the underlying Stellar CLI for full diagnostics."
1660
+ );
1661
+ }
1662
+ const { output, contractId } = deployOutcome;
1315
1663
  const wasmHash = await hashWasm(wasmPath);
1316
1664
  const dependencyGraph = buildDependencyGraph(options.config.contracts);
1317
1665
  const dependencies = options.dependencies ?? contract.config.dependsOn;
1666
+ const supersedeReason = existing?.contractId && options.force ? options.upgrade ? "upgrade" : "force-redeploy" : void 0;
1318
1667
  const nextArtifacts = updateArtifact(
1319
1668
  artifactsBefore,
1320
1669
  network.name,
@@ -1328,7 +1677,7 @@ ${error.hint ?? ""}`,
1328
1677
  dependencies,
1329
1678
  resolvedDeployArgs
1330
1679
  },
1331
- { dependencyGraph }
1680
+ { dependencyGraph, supersedeReason }
1332
1681
  );
1333
1682
  const artifactsPath = await writeArtifacts(nextArtifacts, cwd);
1334
1683
  return {
@@ -1492,9 +1841,11 @@ async function deployContractGraph(options) {
1492
1841
  source: options.source,
1493
1842
  cwd,
1494
1843
  force: options.force,
1844
+ upgrade: options.upgrade,
1495
1845
  checkStaleWasm: options.checkStaleWasm,
1496
1846
  resolvedDeployArgs,
1497
- dependencies: contractConfig.dependsOn
1847
+ dependencies: contractConfig.dependsOn,
1848
+ onTransientDeployRetry: options.onTransientDeployRetry
1498
1849
  });
1499
1850
  if (result.staleWasmWarning) {
1500
1851
  staleWasmWarnings.push({
@@ -1518,11 +1869,11 @@ async function deployContractGraph(options) {
1518
1869
 
1519
1870
  // src/contracts/generate-bindings.ts
1520
1871
  import { access as access4, mkdir as mkdir2, unlink } from "fs/promises";
1521
- import path11 from "path";
1872
+ import path12 from "path";
1522
1873
 
1523
1874
  // src/bindings/patch-generated-binding-package.ts
1524
- import { access as access3, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
1525
- import path10 from "path";
1875
+ import { access as access3, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
1876
+ import path11 from "path";
1526
1877
  var BUNDLER_ENTRY = "./src/index.ts";
1527
1878
  var ROOT_BINDING_INDEX_CONTENT = 'export * from "./src/index.js";\n';
1528
1879
  function resolveExportEntry(exportsField) {
@@ -1557,9 +1908,9 @@ function shouldPatchPackageJson(packageJson) {
1557
1908
  return pointsToDist(packageJson.main) || pointsToDist(packageJson.types) || !pointsToBundlerSource(exportEntry);
1558
1909
  }
1559
1910
  async function ensureRootBindingIndex(outputDir) {
1560
- const rootIndexPath = path10.join(outputDir, "index.ts");
1911
+ const rootIndexPath = path11.join(outputDir, "index.ts");
1561
1912
  try {
1562
- const existing = await readFile4(rootIndexPath, "utf8");
1913
+ const existing = await readFile5(rootIndexPath, "utf8");
1563
1914
  if (existing === ROOT_BINDING_INDEX_CONTENT) {
1564
1915
  return;
1565
1916
  }
@@ -1569,8 +1920,8 @@ async function ensureRootBindingIndex(outputDir) {
1569
1920
  }
1570
1921
  }
1571
1922
  async function patchGeneratedBindingPackage(outputDir) {
1572
- const packageJsonPath = path10.join(outputDir, "package.json");
1573
- const entryPath = path10.join(outputDir, "src", "index.ts");
1923
+ const packageJsonPath = path11.join(outputDir, "package.json");
1924
+ const entryPath = path11.join(outputDir, "src", "index.ts");
1574
1925
  try {
1575
1926
  await access3(entryPath);
1576
1927
  } catch {
@@ -1582,7 +1933,7 @@ async function patchGeneratedBindingPackage(outputDir) {
1582
1933
  }
1583
1934
  let raw;
1584
1935
  try {
1585
- raw = await readFile4(packageJsonPath, "utf8");
1936
+ raw = await readFile5(packageJsonPath, "utf8");
1586
1937
  } catch {
1587
1938
  throw new CaatingaError(
1588
1939
  "Generated binding package is missing package.json.",
@@ -1623,11 +1974,11 @@ function buildGenerateNetworkArgs(network) {
1623
1974
 
1624
1975
  // src/contracts/generate-bindings.ts
1625
1976
  function toBindingImportPath(bindingsOutput, contractName) {
1626
- const normalized = bindingsOutput.replace(/^\.\//, "").split(path11.sep).join("/");
1627
- return `./${path11.posix.join(normalized, contractName)}`;
1977
+ const normalized = bindingsOutput.replace(/^\.\//, "").split(path12.sep).join("/");
1978
+ return `./${path12.posix.join(normalized, contractName)}`;
1628
1979
  }
1629
1980
  async function removeLegacyBindingStub(cwd, bindingsOutput, contractName) {
1630
- const legacyPath = path11.resolve(cwd, bindingsOutput, `${contractName}.ts`);
1981
+ const legacyPath = path12.resolve(cwd, bindingsOutput, `${contractName}.ts`);
1631
1982
  try {
1632
1983
  await access4(legacyPath);
1633
1984
  await unlink(legacyPath);
@@ -1655,8 +2006,9 @@ async function generateBindings(options) {
1655
2006
  "Run caatinga deploy for this contract and network before generating bindings."
1656
2007
  );
1657
2008
  }
1658
- const outputDir = path11.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
2009
+ const outputDir = path12.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
1659
2010
  await mkdir2(outputDir, { recursive: true });
2011
+ await checkStellarSdkVersion({ cwd });
1660
2012
  const result = await runCommand(
1661
2013
  "npx",
1662
2014
  [
@@ -1881,14 +2233,172 @@ async function readContract(options) {
1881
2233
  };
1882
2234
  }
1883
2235
 
2236
+ // src/contracts/estimate-deploy-cost.ts
2237
+ import path13 from "path";
2238
+ function toSnakeCaseFlag2(key) {
2239
+ return key.replace(/([A-Z])/g, "_$1").replace(/^_/, "").toLowerCase();
2240
+ }
2241
+ function formatConstructorCliArgs2(resolved) {
2242
+ const entries = Object.entries(resolved);
2243
+ if (entries.length === 0) {
2244
+ return [];
2245
+ }
2246
+ const tail = ["--"];
2247
+ for (const [key, value] of entries) {
2248
+ tail.push(`--${toSnakeCaseFlag2(key)}`, String(value));
2249
+ }
2250
+ return tail;
2251
+ }
2252
+ function parseFeeStroops(output) {
2253
+ const inclusionMatch = output.match(/inclusion[_\s-]*fee[:\s]+(\d+)/i);
2254
+ const resourceMatch = output.match(/resource[_\s-]*fee[:\s]+(\d+)/i);
2255
+ const totalMatch = output.match(/total[_\s-]*fee[:\s]+(\d+)/i);
2256
+ return {
2257
+ inclusion: inclusionMatch ? Number(inclusionMatch[1]) : void 0,
2258
+ resource: resourceMatch ? Number(resourceMatch[1]) : void 0,
2259
+ ...totalMatch && !resourceMatch ? { resource: Number(totalMatch[1]) } : {}
2260
+ };
2261
+ }
2262
+ async function estimateDeployCost(options) {
2263
+ const cwd = options.cwd ?? process.cwd();
2264
+ const contract = resolveContract(options.config, options.contractName, cwd);
2265
+ const network = resolveNetwork(options.config, options.networkName);
2266
+ const source = assertSafeSourceAccount(options.source);
2267
+ await checkBinary("stellar", "Install Stellar CLI before running caatinga estimate.");
2268
+ const wasmPath = await resolveWasmArtifactPath(contract.wasmPath, {
2269
+ sourcePath: contract.sourcePath
2270
+ });
2271
+ const artifacts = await readArtifacts(cwd);
2272
+ const rawDeployArgs = contract.config.deployArgs;
2273
+ const resolvedDeployArgs = Object.keys(rawDeployArgs).length > 0 ? resolveDeployArgs({
2274
+ deployArgs: rawDeployArgs,
2275
+ artifacts,
2276
+ network: network.name
2277
+ }) : {};
2278
+ const constructorArgs = formatConstructorCliArgs2(resolvedDeployArgs);
2279
+ const deployArgs = [
2280
+ "contract",
2281
+ "deploy",
2282
+ "--wasm",
2283
+ wasmPath,
2284
+ "--source-account",
2285
+ source,
2286
+ "--build-only",
2287
+ ...buildStellarNetworkArgs(network),
2288
+ ...constructorArgs
2289
+ ];
2290
+ let buildOutput;
2291
+ try {
2292
+ const buildResult = await runCommand("stellar", deployArgs, {
2293
+ cwd,
2294
+ failureCode: CaatingaErrorCode.ESTIMATE_FAILED
2295
+ });
2296
+ buildOutput = (buildResult.stdout || buildResult.all).trim();
2297
+ } catch (error) {
2298
+ if (error instanceof CaatingaError) {
2299
+ throw new CaatingaError(
2300
+ `Deploy cost estimate failed for "${contract.name}".`,
2301
+ CaatingaErrorCode.ESTIMATE_FAILED,
2302
+ error.hint ?? "Ensure WASM is built and deploy args resolve correctly.",
2303
+ error.cause
2304
+ );
2305
+ }
2306
+ throw error;
2307
+ }
2308
+ const simulateArgs = ["tx", "simulate", "--source-account", source, buildOutput];
2309
+ let simulateOutput = "";
2310
+ try {
2311
+ const simulateResult = await runCommand("stellar", simulateArgs, {
2312
+ cwd,
2313
+ failureCode: CaatingaErrorCode.ESTIMATE_FAILED
2314
+ });
2315
+ simulateOutput = simulateResult.all || `${simulateResult.stdout}
2316
+ ${simulateResult.stderr}`;
2317
+ } catch {
2318
+ simulateOutput = "";
2319
+ }
2320
+ const parsed = parseFeeStroops(simulateOutput);
2321
+ const inclusionFeeStroops = parsed.inclusion ?? 100;
2322
+ const resourceFeeStroops = parsed.resource;
2323
+ const totalFeeStroops = inclusionFeeStroops + (resourceFeeStroops ?? 0);
2324
+ return {
2325
+ contractName: contract.name,
2326
+ network: network.name,
2327
+ wasmPath: path13.relative(cwd, wasmPath) || wasmPath,
2328
+ inclusionFeeStroops,
2329
+ resourceFeeStroops,
2330
+ totalFeeStroops,
2331
+ advisory: "Advisory estimate only \u2014 actual fees may differ under network congestion or contract complexity.",
2332
+ rawOutput: simulateOutput || buildOutput
2333
+ };
2334
+ }
2335
+
2336
+ // src/contracts/inspect-contract.ts
2337
+ async function inspectContract(options) {
2338
+ const cwd = options.cwd ?? process.cwd();
2339
+ const contract = resolveContract(options.config, options.contractName, cwd);
2340
+ const network = resolveNetwork(options.config, options.networkName);
2341
+ const artifacts = await readArtifacts(cwd);
2342
+ const artifact = artifacts.networks[network.name]?.contracts[contract.name];
2343
+ if (!artifact) {
2344
+ throw new CaatingaError(
2345
+ `No deployed artifact found for "${contract.name}" on "${network.name}".`,
2346
+ CaatingaErrorCode.ARTIFACT_NOT_FOUND,
2347
+ "Run caatinga deploy before inspect."
2348
+ );
2349
+ }
2350
+ await checkBinary("stellar", "Install Stellar CLI before running caatinga inspect.");
2351
+ let reachable = false;
2352
+ let detail;
2353
+ try {
2354
+ await verifyDependencyContract({
2355
+ dependencyName: contract.name,
2356
+ contractId: artifact.contractId,
2357
+ network,
2358
+ cwd
2359
+ });
2360
+ reachable = true;
2361
+ detail = "Contract interface reachable on network.";
2362
+ } catch (error) {
2363
+ reachable = false;
2364
+ detail = error instanceof CaatingaError ? error.message : "Contract not reachable on network.";
2365
+ }
2366
+ let localHash;
2367
+ try {
2368
+ const wasmPath = await resolveWasmArtifactPath(contract.wasmPath, {
2369
+ sourcePath: contract.sourcePath
2370
+ });
2371
+ localHash = await hashWasm(wasmPath);
2372
+ } catch {
2373
+ localHash = void 0;
2374
+ }
2375
+ return {
2376
+ contractName: contract.name,
2377
+ network: network.name,
2378
+ artifact: {
2379
+ contractId: artifact.contractId,
2380
+ wasmHash: artifact.wasmHash,
2381
+ deployedAt: artifact.deployedAt,
2382
+ historyCount: artifact.history?.length ?? 0
2383
+ },
2384
+ onChain: { reachable, detail },
2385
+ localWasm: {
2386
+ path: contract.config.wasm,
2387
+ hash: localHash,
2388
+ matchesArtifact: Boolean(localHash && localHash === artifact.wasmHash)
2389
+ },
2390
+ dependencies: artifact.dependencies ?? contract.config.dependsOn ?? []
2391
+ };
2392
+ }
2393
+
1884
2394
  // src/templates/create-project-from-template.ts
1885
- import { cp, mkdir as mkdir3, readFile as readFile5, readdir as readdir3, stat as stat2, writeFile as writeFile4 } from "fs/promises";
1886
- import path12 from "path";
2395
+ import { cp, mkdir as mkdir3, readFile as readFile6, readdir as readdir3, stat as stat2, writeFile as writeFile4 } from "fs/promises";
2396
+ import path14 from "path";
1887
2397
  import { z as z7 } from "zod";
1888
2398
 
1889
2399
  // src/templates/template-manifest.schema.ts
1890
2400
  import { z as z6 } from "zod";
1891
- import semver3 from "semver";
2401
+ import semver5 from "semver";
1892
2402
  var CURRENT_TEMPLATE_VERSION = 1;
1893
2403
  var TemplateManifestSchema = z6.object({
1894
2404
  name: z6.string().min(1),
@@ -1899,7 +2409,7 @@ var TemplateManifestSchema = z6.object({
1899
2409
  templateVersion: z6.number().int().positive()
1900
2410
  }),
1901
2411
  frontend: z6.object({
1902
- framework: z6.enum(["vite-react", "next", "astro"]),
2412
+ framework: z6.literal("vite-react"),
1903
2413
  packageManager: z6.enum(["npm", "pnpm", "yarn", "bun"]).default("npm")
1904
2414
  }),
1905
2415
  contracts: z6.object({
@@ -1912,14 +2422,14 @@ var TemplateManifestSchema = z6.object({
1912
2422
  })
1913
2423
  });
1914
2424
  function defaultCompatibleCoreRange(coreVersion = CAATINGA_CORE_VERSION) {
1915
- const version = semver3.valid(semver3.coerce(coreVersion));
2425
+ const version = semver5.valid(semver5.coerce(coreVersion));
1916
2426
  if (!version) {
1917
2427
  throw new Error(`Invalid core version: ${coreVersion}`);
1918
2428
  }
1919
2429
  return `^${version}`;
1920
2430
  }
1921
2431
  function isCoreVersionCompatible(range, coreVersion = CAATINGA_CORE_VERSION) {
1922
- return semver3.satisfies(coreVersion, range);
2432
+ return semver5.satisfies(coreVersion, range);
1923
2433
  }
1924
2434
  function getTemplateCompatibilityIssue(manifest, coreVersion = CAATINGA_CORE_VERSION) {
1925
2435
  if (manifest.caatinga.templateVersion !== CURRENT_TEMPLATE_VERSION) {
@@ -1954,8 +2464,8 @@ function formatTemplateCompatibilityHint(issue) {
1954
2464
  // src/templates/create-project-from-template.ts
1955
2465
  var TEMPLATE_COPY_EXCLUDED_DIRS = /* @__PURE__ */ new Set(["target", "test_snapshots", "node_modules", ".git"]);
1956
2466
  async function createProjectFromTemplate(options) {
1957
- const targetDir = path12.resolve(options.targetDir);
1958
- const templateDir = path12.resolve(options.templateDir);
2467
+ const targetDir = path14.resolve(options.targetDir);
2468
+ const templateDir = path14.resolve(options.templateDir);
1959
2469
  try {
1960
2470
  await stat2(templateDir);
1961
2471
  } catch {
@@ -1996,9 +2506,9 @@ async function ensureArtifacts(targetDir, projectName) {
1996
2506
  }
1997
2507
  }
1998
2508
  async function readTemplateManifest(templateDir) {
1999
- const manifestPath = path12.join(templateDir, "caatinga.template.json");
2509
+ const manifestPath = path14.join(templateDir, "caatinga.template.json");
2000
2510
  try {
2001
- const rawManifest = await readFile5(manifestPath, "utf8");
2511
+ const rawManifest = await readFile6(manifestPath, "utf8");
2002
2512
  const manifest = TemplateManifestSchema.parse(JSON.parse(rawManifest));
2003
2513
  const compatibilityIssue = getTemplateCompatibilityIssue(manifest);
2004
2514
  if (compatibilityIssue) {
@@ -2034,7 +2544,7 @@ async function replaceTemplateVariables(dir, projectName) {
2034
2544
  const entries = await readdir3(dir);
2035
2545
  await Promise.all(
2036
2546
  entries.map(async (entry) => {
2037
- const entryPath = path12.join(dir, entry);
2547
+ const entryPath = path14.join(dir, entry);
2038
2548
  const entryStat = await stat2(entryPath);
2039
2549
  if (entryStat.isDirectory()) {
2040
2550
  await replaceTemplateVariables(entryPath, projectName);
@@ -2043,38 +2553,38 @@ async function replaceTemplateVariables(dir, projectName) {
2043
2553
  if (!isTextTemplateFile(entryPath)) {
2044
2554
  return;
2045
2555
  }
2046
- const content = await readFile5(entryPath, "utf8");
2556
+ const content = await readFile6(entryPath, "utf8");
2047
2557
  await writeFile4(entryPath, content.replaceAll("__PROJECT_NAME__", projectName), "utf8");
2048
2558
  })
2049
2559
  );
2050
2560
  }
2051
2561
  function shouldCopyTemplateEntry(templateDir, source, userFilter) {
2052
- const relativePath = path12.relative(templateDir, source);
2562
+ const relativePath = path14.relative(templateDir, source);
2053
2563
  if (!relativePath || relativePath === ".") {
2054
2564
  return true;
2055
2565
  }
2056
- const normalizedPath = relativePath.split(path12.sep).join("/");
2566
+ const normalizedPath = relativePath.split(path14.sep).join("/");
2057
2567
  if (userFilter && !userFilter(normalizedPath)) {
2058
2568
  return false;
2059
2569
  }
2060
- return !relativePath.split(path12.sep).some((segment) => TEMPLATE_COPY_EXCLUDED_DIRS.has(segment));
2570
+ return !relativePath.split(path14.sep).some((segment) => TEMPLATE_COPY_EXCLUDED_DIRS.has(segment));
2061
2571
  }
2062
2572
  function isTextTemplateFile(filePath) {
2063
2573
  return [".json", ".md", ".rs", ".toml", ".ts", ".tsx", ".css", ".html"].includes(
2064
- path12.extname(filePath)
2574
+ path14.extname(filePath)
2065
2575
  );
2066
2576
  }
2067
2577
 
2068
2578
  // src/scaffold/create-zk-project.ts
2069
2579
  import { cp as cp2, mkdir as mkdir4, writeFile as writeFile5 } from "fs/promises";
2070
2580
  import { existsSync as existsSync2 } from "fs";
2071
- import path13 from "path";
2581
+ import path15 from "path";
2072
2582
  import { fileURLToPath } from "url";
2073
- var moduleDir = typeof __dirname === "string" ? __dirname : path13.dirname(fileURLToPath(import.meta.url));
2583
+ var moduleDir = typeof __dirname === "string" ? __dirname : path15.dirname(fileURLToPath(import.meta.url));
2074
2584
  function scaffoldRoot() {
2075
2585
  const candidates = [
2076
- path13.resolve(moduleDir, "../../scaffolds"),
2077
- path13.resolve(moduleDir, "../scaffolds")
2586
+ path15.resolve(moduleDir, "../../scaffolds"),
2587
+ path15.resolve(moduleDir, "../scaffolds")
2078
2588
  ];
2079
2589
  const found = candidates.find((candidate) => existsSync2(candidate));
2080
2590
  return found ?? candidates[0];
@@ -2165,39 +2675,39 @@ Replace \`circuits/main.circom\` with your circuit. Keep the entry point named \
2165
2675
  `;
2166
2676
  }
2167
2677
  async function createZkProject(options) {
2168
- const targetDir = path13.resolve(options.targetDir);
2678
+ const targetDir = path15.resolve(options.targetDir);
2169
2679
  const force = options.force ?? false;
2170
2680
  const projectFiles = options.projectFiles ?? true;
2171
2681
  await mkdir4(targetDir, { recursive: true });
2172
2682
  if (projectFiles) {
2173
2683
  await Promise.all([
2174
- writeFile5(path13.join(targetDir, "caatinga.config.ts"), configSource(options.projectName), {
2684
+ writeFile5(path15.join(targetDir, "caatinga.config.ts"), configSource(options.projectName), {
2175
2685
  encoding: "utf8",
2176
2686
  flag: force ? "w" : "wx"
2177
2687
  }),
2178
- writeFile5(path13.join(targetDir, "package.json"), packageJsonSource(options.projectName), {
2688
+ writeFile5(path15.join(targetDir, "package.json"), packageJsonSource(options.projectName), {
2179
2689
  encoding: "utf8",
2180
2690
  flag: force ? "w" : "wx"
2181
2691
  }),
2182
- writeFile5(path13.join(targetDir, ".gitignore"), "node_modules\n.artifacts\ntarget\n", {
2692
+ writeFile5(path15.join(targetDir, ".gitignore"), "node_modules\n.artifacts\ntarget\n", {
2183
2693
  encoding: "utf8",
2184
2694
  flag: force ? "w" : "wx"
2185
2695
  }),
2186
- writeFile5(path13.join(targetDir, "README.md"), readmeSource(options.projectName), {
2696
+ writeFile5(path15.join(targetDir, "README.md"), readmeSource(options.projectName), {
2187
2697
  encoding: "utf8",
2188
2698
  flag: force ? "w" : "wx"
2189
2699
  })
2190
2700
  ]);
2191
2701
  }
2192
- await mkdir4(path13.join(targetDir, "contracts"), { recursive: true });
2193
- await cp2(path13.join(scaffoldRoot(), "zk-circuit-stub"), path13.join(targetDir, "circuits"), {
2702
+ await mkdir4(path15.join(targetDir, "contracts"), { recursive: true });
2703
+ await cp2(path15.join(scaffoldRoot(), "zk-circuit-stub"), path15.join(targetDir, "circuits"), {
2194
2704
  recursive: true,
2195
2705
  force,
2196
2706
  errorOnExist: !force
2197
2707
  });
2198
2708
  await cp2(
2199
- path13.join(scaffoldRoot(), "zk-verifier"),
2200
- path13.join(targetDir, "contracts", "verifier"),
2709
+ path15.join(scaffoldRoot(), "zk-verifier"),
2710
+ path15.join(targetDir, "contracts", "verifier"),
2201
2711
  {
2202
2712
  recursive: true,
2203
2713
  force,
@@ -2216,13 +2726,13 @@ async function createZkProject(options) {
2216
2726
  // src/scaffold/create-minimal-project.ts
2217
2727
  import { cp as cp3, mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
2218
2728
  import { existsSync as existsSync3 } from "fs";
2219
- import path14 from "path";
2729
+ import path16 from "path";
2220
2730
  import { fileURLToPath as fileURLToPath2 } from "url";
2221
- var moduleDir2 = typeof __dirname === "string" ? __dirname : path14.dirname(fileURLToPath2(import.meta.url));
2731
+ var moduleDir2 = typeof __dirname === "string" ? __dirname : path16.dirname(fileURLToPath2(import.meta.url));
2222
2732
  function scaffoldRoot2() {
2223
2733
  const candidates = [
2224
- path14.resolve(moduleDir2, "../../scaffolds"),
2225
- path14.resolve(moduleDir2, "../scaffolds")
2734
+ path16.resolve(moduleDir2, "../../scaffolds"),
2735
+ path16.resolve(moduleDir2, "../scaffolds")
2226
2736
  ];
2227
2737
  const found = candidates.find((candidate) => existsSync3(candidate));
2228
2738
  return found ?? candidates[0];
@@ -2313,31 +2823,31 @@ Edit \`contracts/app/src/lib.rs\` to customize the contract. Add a frontend late
2313
2823
  `;
2314
2824
  }
2315
2825
  async function createMinimalProject(options) {
2316
- const targetDir = path14.resolve(options.targetDir);
2826
+ const targetDir = path16.resolve(options.targetDir);
2317
2827
  const force = options.force ?? false;
2318
2828
  await mkdir5(targetDir, { recursive: true });
2319
2829
  await Promise.all([
2320
- writeFile6(path14.join(targetDir, "caatinga.config.ts"), configSource2(options.projectName), {
2830
+ writeFile6(path16.join(targetDir, "caatinga.config.ts"), configSource2(options.projectName), {
2321
2831
  encoding: "utf8",
2322
2832
  flag: force ? "w" : "wx"
2323
2833
  }),
2324
- writeFile6(path14.join(targetDir, "package.json"), packageJsonSource2(options.projectName), {
2834
+ writeFile6(path16.join(targetDir, "package.json"), packageJsonSource2(options.projectName), {
2325
2835
  encoding: "utf8",
2326
2836
  flag: force ? "w" : "wx"
2327
2837
  }),
2328
- writeFile6(path14.join(targetDir, ".gitignore"), "node_modules\n.artifacts\ntarget\n", {
2838
+ writeFile6(path16.join(targetDir, ".gitignore"), "node_modules\n.artifacts\ntarget\n", {
2329
2839
  encoding: "utf8",
2330
2840
  flag: force ? "w" : "wx"
2331
2841
  }),
2332
- writeFile6(path14.join(targetDir, "README.md"), readmeSource2(options.projectName), {
2842
+ writeFile6(path16.join(targetDir, "README.md"), readmeSource2(options.projectName), {
2333
2843
  encoding: "utf8",
2334
2844
  flag: force ? "w" : "wx"
2335
2845
  })
2336
2846
  ]);
2337
- await mkdir5(path14.join(targetDir, "contracts"), { recursive: true });
2847
+ await mkdir5(path16.join(targetDir, "contracts"), { recursive: true });
2338
2848
  await cp3(
2339
- path14.join(scaffoldRoot2(), "soroban-contract-stub"),
2340
- path14.join(targetDir, "contracts", "app"),
2849
+ path16.join(scaffoldRoot2(), "soroban-contract-stub"),
2850
+ path16.join(targetDir, "contracts", "app"),
2341
2851
  {
2342
2852
  recursive: true,
2343
2853
  force,
@@ -2352,29 +2862,14 @@ async function createMinimalProject(options) {
2352
2862
  }
2353
2863
 
2354
2864
  // src/ci/is-transient-testnet-smoke-failure.ts
2355
- var NO_RETRY_CAATINGA_SUBSTRINGS = [
2356
- "CAATINGA_UNSUPPORTED_CLI_VERSION",
2357
- "CAATINGA_STELLAR_CLI_VERSION_PARSE_FAILED",
2358
- "CAATINGA_STELLAR_CLI_NOT_FOUND",
2359
- "CAATINGA_INVALID_CONFIG",
2360
- "CAATINGA_CONFIG_NOT_FOUND"
2361
- ];
2362
- var TRANSIENT_PATTERN = /timeout|i\/o timeout|econnreset|connection reset|503|502|429|rate limit|temporar|bad gateway|fetch failed|network error|unavailable/i;
2363
2865
  function isTransientTestnetSmokeFailure(logText) {
2364
- if (!logText.trim()) {
2365
- return false;
2366
- }
2367
- for (const marker of NO_RETRY_CAATINGA_SUBSTRINGS) {
2368
- if (logText.includes(marker)) {
2369
- return false;
2370
- }
2371
- }
2372
- return TRANSIENT_PATTERN.test(logText);
2866
+ return isTransientCommandFailure(logText);
2373
2867
  }
2374
2868
  export {
2375
2869
  BINDING_MARKER_FILENAME,
2376
2870
  BindingMarkerSchema,
2377
2871
  CAATINGA_CORE_VERSION,
2872
+ CURRENT_ARTIFACTS_SCHEMA_VERSION,
2378
2873
  CaatingaArtifactsSchema,
2379
2874
  CaatingaConfigSchema,
2380
2875
  CaatingaError,
@@ -2382,6 +2877,8 @@ export {
2382
2877
  READ_CALL_FAILURE_REGEX,
2383
2878
  STELLAR_CLI_LAST_TESTED_VERSION,
2384
2879
  STELLAR_CLI_MIN_VERSION,
2880
+ STELLAR_SDK_LAST_TESTED_VERSION,
2881
+ STELLAR_SDK_MIN_VERSION,
2385
2882
  TemplateManifestSchema,
2386
2883
  WELL_KNOWN_NETWORKS,
2387
2884
  buildContract,
@@ -2389,6 +2886,7 @@ export {
2389
2886
  buildReadCallHint,
2390
2887
  checkBinary,
2391
2888
  checkStellarCliVersion,
2889
+ checkStellarSdkVersion,
2392
2890
  collectProjectStatus,
2393
2891
  createInitialArtifacts,
2394
2892
  createMinimalProject,
@@ -2397,20 +2895,26 @@ export {
2397
2895
  defineConfig,
2398
2896
  deployContract,
2399
2897
  deployContractGraph,
2898
+ estimateDeployCost,
2400
2899
  evaluateBindingFreshness,
2401
2900
  evaluateBindingsFreshness,
2402
2901
  evaluateStellarCliCompatibility,
2902
+ evaluateStellarSdkCompatibility,
2403
2903
  formatCaatingaError,
2404
2904
  generateBindings,
2405
2905
  generateBindingsGraph,
2906
+ inspectContract,
2406
2907
  invokeContract,
2407
2908
  isCargoBinMissingFromPath,
2408
2909
  isReadCallFailure,
2409
2910
  isTransientTestnetSmokeFailure,
2410
2911
  loadConfig,
2912
+ migrateArtifactsFile,
2913
+ migrateArtifactsToV2,
2411
2914
  parseContractId,
2412
2915
  parseInvokeTarget,
2413
2916
  parseStellarCliVersion,
2917
+ parseStellarSdkVersion,
2414
2918
  readArtifacts,
2415
2919
  readBindingMarker,
2416
2920
  readContract,
@@ -2420,6 +2924,8 @@ export {
2420
2924
  resolveDeployOrder,
2421
2925
  resolveNetwork,
2422
2926
  resolveSubprocessEnv,
2927
+ restoreArtifactFromHistory,
2928
+ rollbackContractArtifact,
2423
2929
  runCommand,
2424
2930
  toCaatingaError,
2425
2931
  updateArtifact,