@caatinga/core 3.2.0 → 3.3.1

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",
@@ -50,6 +52,8 @@ var CaatingaErrorCode = {
50
52
  ZK_VERIFICATION_FAILED: "CAATINGA_ZK_VERIFICATION_FAILED",
51
53
  ZK_DEV_CEREMONY_BLOCKED: "CAATINGA_ZK_DEV_CEREMONY_BLOCKED",
52
54
  DOCTOR_PARTIAL_DEPLOY: "CAATINGA_DOCTOR_PARTIAL_DEPLOY",
55
+ ROLLBACK_TARGET_NOT_FOUND: "CAATINGA_ROLLBACK_TARGET_NOT_FOUND",
56
+ ESTIMATE_FAILED: "CAATINGA_ESTIMATE_FAILED",
53
57
  MULTI_AUTH_REQUIRED: "CAATINGA_MULTI_AUTH_REQUIRED"
54
58
  };
55
59
 
@@ -236,6 +240,14 @@ async function loadConfig(options = {}) {
236
240
 
237
241
  // src/artifacts/artifact.schema.ts
238
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
+ });
239
251
  var ContractArtifactSchema = z3.object({
240
252
  contractId: z3.string().min(1),
241
253
  wasmHash: z3.string().min(1),
@@ -243,17 +255,28 @@ var ContractArtifactSchema = z3.object({
243
255
  sourcePath: z3.string().min(1),
244
256
  wasmPath: z3.string().min(1),
245
257
  dependencies: z3.array(z3.string().min(1)).default([]),
246
- 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()
247
260
  });
248
261
  var NetworkArtifactsSchema = z3.object({
249
262
  contracts: z3.record(z3.string().min(1), ContractArtifactSchema).default({}),
250
263
  dependencyGraph: z3.record(z3.string().min(1), z3.array(z3.string().min(1))).default({})
251
264
  });
252
- var CaatingaArtifactsSchema = z3.object({
265
+ var CaatingaArtifactsBaseSchema = z3.object({
253
266
  project: z3.string().min(1),
254
- version: z3.literal(1),
255
267
  networks: z3.record(z3.string().min(1), NetworkArtifactsSchema).default({})
256
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;
257
280
 
258
281
  // src/artifacts/read-artifacts.ts
259
282
  import { readFile } from "fs/promises";
@@ -305,29 +328,139 @@ function createInitialArtifacts(project, options = {}) {
305
328
  );
306
329
  return {
307
330
  project,
308
- version: 1,
331
+ version: 2,
309
332
  networks
310
333
  };
311
334
  }
312
335
 
313
336
  // src/artifacts/update-artifact.ts
314
- 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 = {}) {
315
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;
316
356
  return {
317
357
  ...artifacts,
358
+ version: nextVersion,
318
359
  networks: {
319
360
  ...artifacts.networks,
320
361
  [networkName]: {
321
362
  ...existingNetwork,
322
- dependencyGraph: networkExtras?.dependencyGraph ?? existingNetwork.dependencyGraph ?? {},
363
+ dependencyGraph: options.dependencyGraph ?? existingNetwork.dependencyGraph ?? {},
323
364
  contracts: {
324
365
  ...existingNetwork.contracts,
325
- [contractName]: contractArtifact
366
+ [contractName]: {
367
+ ...contractArtifact,
368
+ history: history ?? contractArtifact.history
369
+ }
326
370
  }
327
371
  }
328
372
  }
329
373
  };
330
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
+ }
331
464
 
332
465
  // src/networks/resolve-network.ts
333
466
  function resolveNetwork(config, networkName) {
@@ -594,6 +727,35 @@ function evaluateStellarCliCompatibility(input) {
594
727
  };
595
728
  }
596
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
+
597
759
  // src/stellar-cli/check-stellar-cli-version.ts
598
760
  async function checkStellarCliVersion(input = {}) {
599
761
  let rawOutput;
@@ -613,9 +775,12 @@ async function checkStellarCliVersion(input = {}) {
613
775
  }
614
776
  throw error;
615
777
  }
778
+ const version = parseStellarCliVersion(rawOutput);
779
+ const probedMissing = input.probeFeatures === false ? [] : await probeMissingStellarCliFeatures(version);
780
+ const missingFeatures = [...input.features ?? [], ...probedMissing];
616
781
  const report = evaluateStellarCliCompatibility({
617
- version: parseStellarCliVersion(rawOutput),
618
- features: input.features,
782
+ version,
783
+ features: missingFeatures.length > 0 ? missingFeatures : void 0,
619
784
  lastTestedVersion: input.lastTestedVersion
620
785
  });
621
786
  for (const warning of report.warnings) {
@@ -761,6 +926,122 @@ function parseContractId(output) {
761
926
  return match[0];
762
927
  }
763
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
+
764
1045
  // src/stellar-cli/build-stellar-network-args.ts
765
1046
  function matchesWellKnownNetwork(name, config) {
766
1047
  const known = WELL_KNOWN_NETWORKS[name];
@@ -898,8 +1179,38 @@ function validateSourceShape(source) {
898
1179
  return void 0;
899
1180
  }
900
1181
 
1182
+ // src/contracts/source-account.ts
1183
+ function assertSafeSourceAccount(source) {
1184
+ if (!source) {
1185
+ throw new CaatingaError(
1186
+ "A source account or Stellar CLI identity is required.",
1187
+ CaatingaErrorCode.SOURCE_ACCOUNT_REQUIRED,
1188
+ "Pass a Stellar CLI identity alias, for example: --source alice"
1189
+ );
1190
+ }
1191
+ const unsafeSource = validateSourceShape(source);
1192
+ if (unsafeSource) {
1193
+ throw unsafeSource;
1194
+ }
1195
+ return source;
1196
+ }
1197
+ var DEFAULT_CLI_SOURCE = "alice";
1198
+ function describeCliSource(explicit) {
1199
+ if (explicit) {
1200
+ return { source: assertSafeSourceAccount(explicit), origin: "explicit" };
1201
+ }
1202
+ const fromEnv = process.env.CAATINGA_SOURCE;
1203
+ if (fromEnv) {
1204
+ return { source: assertSafeSourceAccount(fromEnv), origin: "env" };
1205
+ }
1206
+ return { source: assertSafeSourceAccount(DEFAULT_CLI_SOURCE), origin: "default" };
1207
+ }
1208
+ function resolveCliSource(explicit) {
1209
+ return describeCliSource(explicit).source;
1210
+ }
1211
+
901
1212
  // src/contracts/resolve-contract.ts
902
- import path7 from "path";
1213
+ import path8 from "path";
903
1214
  function resolveContract(config, contractName, cwd = process.cwd()) {
904
1215
  const contract = config.contracts[contractName];
905
1216
  if (!contract) {
@@ -912,8 +1223,8 @@ function resolveContract(config, contractName, cwd = process.cwd()) {
912
1223
  return {
913
1224
  name: contractName,
914
1225
  config: contract,
915
- sourcePath: path7.resolve(cwd, contract.path),
916
- wasmPath: path7.resolve(cwd, contract.wasm)
1226
+ sourcePath: path8.resolve(cwd, contract.path),
1227
+ wasmPath: path8.resolve(cwd, contract.wasm)
917
1228
  };
918
1229
  }
919
1230
 
@@ -932,8 +1243,8 @@ function resolveDefaultContractName(config) {
932
1243
 
933
1244
  // src/contracts/wasm.ts
934
1245
  import { createHash } from "crypto";
935
- import { access as access2, readdir as readdir2, readFile as readFile3, stat } from "fs/promises";
936
- import path8 from "path";
1246
+ import { access as access2, readdir as readdir2, readFile as readFile4, stat } from "fs/promises";
1247
+ import path9 from "path";
937
1248
  var LEGACY_RUST_WASM_TARGET = "wasm32-unknown-unknown";
938
1249
  var CURRENT_RUST_WASM_TARGET = "wasm32v1-none";
939
1250
  function toCurrentWasmTargetPath(wasmPath) {
@@ -964,18 +1275,18 @@ function wasmNotFoundError(configuredWasmPath, options) {
964
1275
  );
965
1276
  }
966
1277
  function toConfigRelativeWasmPath(absoluteWasmPath) {
967
- const relative = path8.relative(process.cwd(), absoluteWasmPath);
968
- return relative.startsWith("..") ? absoluteWasmPath : `./${relative.split(path8.sep).join("/")}`;
1278
+ const relative = path9.relative(process.cwd(), absoluteWasmPath);
1279
+ return relative.startsWith("..") ? absoluteWasmPath : `./${relative.split(path9.sep).join("/")}`;
969
1280
  }
970
1281
  function wasmFileName(configuredWasmPath) {
971
- return path8.basename(configuredWasmPath);
1282
+ return path9.basename(configuredWasmPath);
972
1283
  }
973
1284
  function buildAlternateWasmCandidates(configuredWasmPath, options) {
974
1285
  const fileName = wasmFileName(configuredWasmPath);
975
1286
  const candidates = [];
976
1287
  const seen = /* @__PURE__ */ new Set();
977
1288
  function addCandidate(candidate) {
978
- const resolved = path8.resolve(candidate);
1289
+ const resolved = path9.resolve(candidate);
979
1290
  if (seen.has(resolved)) {
980
1291
  return;
981
1292
  }
@@ -984,15 +1295,15 @@ function buildAlternateWasmCandidates(configuredWasmPath, options) {
984
1295
  }
985
1296
  const cargoTargetDir = process.env.CARGO_TARGET_DIR;
986
1297
  if (cargoTargetDir) {
987
- addCandidate(path8.join(cargoTargetDir, CURRENT_RUST_WASM_TARGET, "release", fileName));
988
- addCandidate(path8.join(cargoTargetDir, LEGACY_RUST_WASM_TARGET, "release", fileName));
1298
+ addCandidate(path9.join(cargoTargetDir, CURRENT_RUST_WASM_TARGET, "release", fileName));
1299
+ addCandidate(path9.join(cargoTargetDir, LEGACY_RUST_WASM_TARGET, "release", fileName));
989
1300
  }
990
1301
  if (options?.sourcePath) {
991
1302
  addCandidate(
992
- path8.join(options.sourcePath, "target", CURRENT_RUST_WASM_TARGET, "release", fileName)
1303
+ path9.join(options.sourcePath, "target", CURRENT_RUST_WASM_TARGET, "release", fileName)
993
1304
  );
994
1305
  addCandidate(
995
- path8.join(options.sourcePath, "target", LEGACY_RUST_WASM_TARGET, "release", fileName)
1306
+ path9.join(options.sourcePath, "target", LEGACY_RUST_WASM_TARGET, "release", fileName)
996
1307
  );
997
1308
  }
998
1309
  return candidates;
@@ -1009,7 +1320,7 @@ async function firstExistingPath(paths) {
1009
1320
  return void 0;
1010
1321
  }
1011
1322
  async function resolveWasmArtifactPath(configuredWasmPath, options) {
1012
- const resolvedConfiguredPath = path8.resolve(configuredWasmPath);
1323
+ const resolvedConfiguredPath = path9.resolve(configuredWasmPath);
1013
1324
  try {
1014
1325
  await access2(resolvedConfiguredPath);
1015
1326
  return resolvedConfiguredPath;
@@ -1033,7 +1344,7 @@ async function resolveWasmArtifactPath(configuredWasmPath, options) {
1033
1344
  }
1034
1345
  }
1035
1346
  async function hashWasm(wasmPath) {
1036
- const bytes = await readFile3(wasmPath);
1347
+ const bytes = await readFile4(wasmPath);
1037
1348
  return createHash("sha256").update(bytes).digest("hex");
1038
1349
  }
1039
1350
  async function getNewestMtimeInDirectory(directory) {
@@ -1046,7 +1357,7 @@ async function getNewestMtimeInDirectory(directory) {
1046
1357
  async function walk(dir) {
1047
1358
  const entries = await readdir2(dir, { withFileTypes: true });
1048
1359
  for (const entry of entries) {
1049
- const entryPath = path8.join(dir, entry.name);
1360
+ const entryPath = path9.join(dir, entry.name);
1050
1361
  if (entry.isDirectory()) {
1051
1362
  await walk(entryPath);
1052
1363
  continue;
@@ -1062,7 +1373,7 @@ async function getNewestMtimeInDirectory(directory) {
1062
1373
  return newest > 0 ? newest : void 0;
1063
1374
  }
1064
1375
  async function isWasmOlderThanSources(input) {
1065
- const srcDir = path8.join(input.contractPath, "src");
1376
+ const srcDir = path9.join(input.contractPath, "src");
1066
1377
  const newestSourceMtime = await getNewestMtimeInDirectory(srcDir);
1067
1378
  if (newestSourceMtime === void 0) {
1068
1379
  return false;
@@ -1137,7 +1448,38 @@ async function buildContract(options) {
1137
1448
  }
1138
1449
 
1139
1450
  // src/contracts/deploy-contract.ts
1140
- import path9 from "path";
1451
+ import path10 from "path";
1452
+
1453
+ // src/shell/is-transient-command-failure.ts
1454
+ var NO_RETRY_CAATINGA_SUBSTRINGS = [
1455
+ "CAATINGA_UNSUPPORTED_CLI_VERSION",
1456
+ "CAATINGA_STELLAR_CLI_VERSION_PARSE_FAILED",
1457
+ "CAATINGA_STELLAR_CLI_NOT_FOUND",
1458
+ "CAATINGA_INVALID_CONFIG",
1459
+ "CAATINGA_CONFIG_NOT_FOUND"
1460
+ ];
1461
+ 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;
1462
+ function isTransientCommandFailure(logText) {
1463
+ if (!logText.trim()) {
1464
+ return false;
1465
+ }
1466
+ for (const marker of NO_RETRY_CAATINGA_SUBSTRINGS) {
1467
+ if (logText.includes(marker)) {
1468
+ return false;
1469
+ }
1470
+ }
1471
+ return TRANSIENT_COMMAND_FAILURE_PATTERN.test(logText);
1472
+ }
1473
+
1474
+ // src/contracts/is-transient-deploy-failure.ts
1475
+ function isTransientDeployFailure(error) {
1476
+ if (!(error instanceof CaatingaError) || error.code !== CaatingaErrorCode.DEPLOY_FAILED) {
1477
+ return false;
1478
+ }
1479
+ const logText = `${error.message}
1480
+ ${error.hint ?? ""}`;
1481
+ return isTransientCommandFailure(logText);
1482
+ }
1141
1483
 
1142
1484
  // src/contracts/dependency-graph.ts
1143
1485
  function buildDependencyGraph(contracts) {
@@ -1179,26 +1521,13 @@ function resolveDeployArgs(input) {
1179
1521
  return resolved;
1180
1522
  }
1181
1523
 
1182
- // src/contracts/source-account.ts
1183
- function assertSafeSourceAccount(source) {
1184
- if (!source) {
1185
- throw new CaatingaError(
1186
- "A source account or Stellar CLI identity is required.",
1187
- CaatingaErrorCode.SOURCE_ACCOUNT_REQUIRED,
1188
- "Pass a Stellar CLI identity alias, for example: --source alice"
1189
- );
1190
- }
1191
- const unsafeSource = validateSourceShape(source);
1192
- if (unsafeSource) {
1193
- throw unsafeSource;
1194
- }
1195
- return source;
1196
- }
1197
- function resolveCliSource(explicit) {
1198
- return assertSafeSourceAccount(explicit ?? process.env.CAATINGA_SOURCE ?? "alice");
1199
- }
1200
-
1201
1524
  // src/contracts/deploy-contract.ts
1525
+ var DEFAULT_DEPLOY_RETRY_DELAYS_MS = [2e3, 5e3];
1526
+ function sleep(ms) {
1527
+ return new Promise((resolve) => {
1528
+ setTimeout(resolve, ms);
1529
+ });
1530
+ }
1202
1531
  function toSnakeCaseFlag(key) {
1203
1532
  return key.replace(/([A-Z])/g, "_$1").replace(/^_/, "").toLowerCase();
1204
1533
  }
@@ -1243,7 +1572,7 @@ async function deployContract(options) {
1243
1572
  contract: contractWithWasm,
1244
1573
  network,
1245
1574
  contractId: existing.contractId,
1246
- artifactsPath: path9.resolve(cwd, "caatinga.artifacts.json"),
1575
+ artifactsPath: path10.resolve(cwd, "caatinga.artifacts.json"),
1247
1576
  output: "",
1248
1577
  skipped: true,
1249
1578
  staleWasmWarning
@@ -1283,40 +1612,69 @@ async function deployContract(options) {
1283
1612
  ...buildStellarNetworkArgs(network),
1284
1613
  ...constructorArgs
1285
1614
  ];
1286
- let output = "";
1287
- let contractId;
1288
- try {
1289
- const result = await runCommand("stellar", stellarArgs, {
1290
- cwd,
1291
- failureCode: CaatingaErrorCode.DEPLOY_FAILED
1292
- });
1293
- output = result.all || `${result.stdout}
1615
+ let deployOutcome;
1616
+ const retryDelaysMs = options.deployRetryDelaysMs ?? DEFAULT_DEPLOY_RETRY_DELAYS_MS;
1617
+ const maxDeployAttempts = retryDelaysMs.length + 1;
1618
+ for (let attempt = 0; attempt < maxDeployAttempts; attempt++) {
1619
+ try {
1620
+ const result = await runCommand("stellar", stellarArgs, {
1621
+ cwd,
1622
+ failureCode: CaatingaErrorCode.DEPLOY_FAILED
1623
+ });
1624
+ const output2 = result.all || `${result.stdout}
1294
1625
  ${result.stderr}`;
1295
- contractId = parseContractId(output);
1296
- } catch (error) {
1297
- if (!(error instanceof CaatingaError) || error.code !== CaatingaErrorCode.DEPLOY_FAILED) {
1298
- throw error;
1299
- }
1300
- const recoveredContractId = await tryRecoverContractIdFromDeployFailure({
1301
- output: `${error.message}
1626
+ deployOutcome = {
1627
+ output: output2,
1628
+ contractId: parseContractId(output2)
1629
+ };
1630
+ break;
1631
+ } catch (error) {
1632
+ if (!(error instanceof CaatingaError) || error.code !== CaatingaErrorCode.DEPLOY_FAILED) {
1633
+ throw error;
1634
+ }
1635
+ const recoveredContractId = await tryRecoverContractIdFromDeployFailure({
1636
+ output: `${error.message}
1302
1637
  ${error.hint ?? ""}`,
1303
- source,
1304
- network: network.config,
1305
- cwd
1306
- });
1307
- if (!recoveredContractId) {
1308
- throw error;
1638
+ source,
1639
+ network: network.config,
1640
+ cwd
1641
+ });
1642
+ if (recoveredContractId) {
1643
+ deployOutcome = {
1644
+ output: [
1645
+ error.hint ?? "",
1646
+ "Caatinga recovered the contract ID from the on-chain deploy transaction.",
1647
+ `Contract ID: ${recoveredContractId}`
1648
+ ].filter(Boolean).join("\n"),
1649
+ contractId: recoveredContractId
1650
+ };
1651
+ break;
1652
+ }
1653
+ const isLastAttempt = attempt === maxDeployAttempts - 1;
1654
+ if (!isTransientDeployFailure(error) || isLastAttempt) {
1655
+ throw error;
1656
+ }
1657
+ const delayMs = retryDelaysMs[attempt] ?? retryDelaysMs[retryDelaysMs.length - 1] ?? 0;
1658
+ options.onTransientDeployRetry?.({
1659
+ attempt: attempt + 1,
1660
+ maxAttempts: maxDeployAttempts,
1661
+ delayMs
1662
+ });
1663
+ await sleep(delayMs);
1309
1664
  }
1310
- contractId = recoveredContractId;
1311
- output = [
1312
- error.hint ?? "",
1313
- "Caatinga recovered the contract ID from the on-chain deploy transaction.",
1314
- `Contract ID: ${contractId}`
1315
- ].filter(Boolean).join("\n");
1316
1665
  }
1666
+ if (!deployOutcome) {
1667
+ throw new CaatingaError(
1668
+ "Deploy failed without a contract ID.",
1669
+ CaatingaErrorCode.DEPLOY_FAILED,
1670
+ "Re-run the deploy command with the underlying Stellar CLI for full diagnostics."
1671
+ );
1672
+ }
1673
+ const { output, contractId } = deployOutcome;
1317
1674
  const wasmHash = await hashWasm(wasmPath);
1318
1675
  const dependencyGraph = buildDependencyGraph(options.config.contracts);
1319
1676
  const dependencies = options.dependencies ?? contract.config.dependsOn;
1677
+ const supersedeReason = existing?.contractId && options.force ? options.upgrade ? "upgrade" : "force-redeploy" : void 0;
1320
1678
  const nextArtifacts = updateArtifact(
1321
1679
  artifactsBefore,
1322
1680
  network.name,
@@ -1330,7 +1688,7 @@ ${error.hint ?? ""}`,
1330
1688
  dependencies,
1331
1689
  resolvedDeployArgs
1332
1690
  },
1333
- { dependencyGraph }
1691
+ { dependencyGraph, supersedeReason }
1334
1692
  );
1335
1693
  const artifactsPath = await writeArtifacts(nextArtifacts, cwd);
1336
1694
  return {
@@ -1494,9 +1852,11 @@ async function deployContractGraph(options) {
1494
1852
  source: options.source,
1495
1853
  cwd,
1496
1854
  force: options.force,
1855
+ upgrade: options.upgrade,
1497
1856
  checkStaleWasm: options.checkStaleWasm,
1498
1857
  resolvedDeployArgs,
1499
- dependencies: contractConfig.dependsOn
1858
+ dependencies: contractConfig.dependsOn,
1859
+ onTransientDeployRetry: options.onTransientDeployRetry
1500
1860
  });
1501
1861
  if (result.staleWasmWarning) {
1502
1862
  staleWasmWarnings.push({
@@ -1520,11 +1880,11 @@ async function deployContractGraph(options) {
1520
1880
 
1521
1881
  // src/contracts/generate-bindings.ts
1522
1882
  import { access as access4, mkdir as mkdir2, unlink } from "fs/promises";
1523
- import path11 from "path";
1883
+ import path12 from "path";
1524
1884
 
1525
1885
  // src/bindings/patch-generated-binding-package.ts
1526
- import { access as access3, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
1527
- import path10 from "path";
1886
+ import { access as access3, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
1887
+ import path11 from "path";
1528
1888
  var BUNDLER_ENTRY = "./src/index.ts";
1529
1889
  var ROOT_BINDING_INDEX_CONTENT = 'export * from "./src/index.js";\n';
1530
1890
  function resolveExportEntry(exportsField) {
@@ -1559,9 +1919,9 @@ function shouldPatchPackageJson(packageJson) {
1559
1919
  return pointsToDist(packageJson.main) || pointsToDist(packageJson.types) || !pointsToBundlerSource(exportEntry);
1560
1920
  }
1561
1921
  async function ensureRootBindingIndex(outputDir) {
1562
- const rootIndexPath = path10.join(outputDir, "index.ts");
1922
+ const rootIndexPath = path11.join(outputDir, "index.ts");
1563
1923
  try {
1564
- const existing = await readFile4(rootIndexPath, "utf8");
1924
+ const existing = await readFile5(rootIndexPath, "utf8");
1565
1925
  if (existing === ROOT_BINDING_INDEX_CONTENT) {
1566
1926
  return;
1567
1927
  }
@@ -1571,8 +1931,8 @@ async function ensureRootBindingIndex(outputDir) {
1571
1931
  }
1572
1932
  }
1573
1933
  async function patchGeneratedBindingPackage(outputDir) {
1574
- const packageJsonPath = path10.join(outputDir, "package.json");
1575
- const entryPath = path10.join(outputDir, "src", "index.ts");
1934
+ const packageJsonPath = path11.join(outputDir, "package.json");
1935
+ const entryPath = path11.join(outputDir, "src", "index.ts");
1576
1936
  try {
1577
1937
  await access3(entryPath);
1578
1938
  } catch {
@@ -1584,7 +1944,7 @@ async function patchGeneratedBindingPackage(outputDir) {
1584
1944
  }
1585
1945
  let raw;
1586
1946
  try {
1587
- raw = await readFile4(packageJsonPath, "utf8");
1947
+ raw = await readFile5(packageJsonPath, "utf8");
1588
1948
  } catch {
1589
1949
  throw new CaatingaError(
1590
1950
  "Generated binding package is missing package.json.",
@@ -1625,11 +1985,11 @@ function buildGenerateNetworkArgs(network) {
1625
1985
 
1626
1986
  // src/contracts/generate-bindings.ts
1627
1987
  function toBindingImportPath(bindingsOutput, contractName) {
1628
- const normalized = bindingsOutput.replace(/^\.\//, "").split(path11.sep).join("/");
1629
- return `./${path11.posix.join(normalized, contractName)}`;
1988
+ const normalized = bindingsOutput.replace(/^\.\//, "").split(path12.sep).join("/");
1989
+ return `./${path12.posix.join(normalized, contractName)}`;
1630
1990
  }
1631
1991
  async function removeLegacyBindingStub(cwd, bindingsOutput, contractName) {
1632
- const legacyPath = path11.resolve(cwd, bindingsOutput, `${contractName}.ts`);
1992
+ const legacyPath = path12.resolve(cwd, bindingsOutput, `${contractName}.ts`);
1633
1993
  try {
1634
1994
  await access4(legacyPath);
1635
1995
  await unlink(legacyPath);
@@ -1657,8 +2017,9 @@ async function generateBindings(options) {
1657
2017
  "Run caatinga deploy for this contract and network before generating bindings."
1658
2018
  );
1659
2019
  }
1660
- const outputDir = path11.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
2020
+ const outputDir = path12.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
1661
2021
  await mkdir2(outputDir, { recursive: true });
2022
+ await checkStellarSdkVersion({ cwd });
1662
2023
  const result = await runCommand(
1663
2024
  "npx",
1664
2025
  [
@@ -1883,14 +2244,172 @@ async function readContract(options) {
1883
2244
  };
1884
2245
  }
1885
2246
 
2247
+ // src/contracts/estimate-deploy-cost.ts
2248
+ import path13 from "path";
2249
+ function toSnakeCaseFlag2(key) {
2250
+ return key.replace(/([A-Z])/g, "_$1").replace(/^_/, "").toLowerCase();
2251
+ }
2252
+ function formatConstructorCliArgs2(resolved) {
2253
+ const entries = Object.entries(resolved);
2254
+ if (entries.length === 0) {
2255
+ return [];
2256
+ }
2257
+ const tail = ["--"];
2258
+ for (const [key, value] of entries) {
2259
+ tail.push(`--${toSnakeCaseFlag2(key)}`, String(value));
2260
+ }
2261
+ return tail;
2262
+ }
2263
+ function parseFeeStroops(output) {
2264
+ const inclusionMatch = output.match(/inclusion[_\s-]*fee[:\s]+(\d+)/i);
2265
+ const resourceMatch = output.match(/resource[_\s-]*fee[:\s]+(\d+)/i);
2266
+ const totalMatch = output.match(/total[_\s-]*fee[:\s]+(\d+)/i);
2267
+ return {
2268
+ inclusion: inclusionMatch ? Number(inclusionMatch[1]) : void 0,
2269
+ resource: resourceMatch ? Number(resourceMatch[1]) : void 0,
2270
+ ...totalMatch && !resourceMatch ? { resource: Number(totalMatch[1]) } : {}
2271
+ };
2272
+ }
2273
+ async function estimateDeployCost(options) {
2274
+ const cwd = options.cwd ?? process.cwd();
2275
+ const contract = resolveContract(options.config, options.contractName, cwd);
2276
+ const network = resolveNetwork(options.config, options.networkName);
2277
+ const source = assertSafeSourceAccount(options.source);
2278
+ await checkBinary("stellar", "Install Stellar CLI before running caatinga estimate.");
2279
+ const wasmPath = await resolveWasmArtifactPath(contract.wasmPath, {
2280
+ sourcePath: contract.sourcePath
2281
+ });
2282
+ const artifacts = await readArtifacts(cwd);
2283
+ const rawDeployArgs = contract.config.deployArgs;
2284
+ const resolvedDeployArgs = Object.keys(rawDeployArgs).length > 0 ? resolveDeployArgs({
2285
+ deployArgs: rawDeployArgs,
2286
+ artifacts,
2287
+ network: network.name
2288
+ }) : {};
2289
+ const constructorArgs = formatConstructorCliArgs2(resolvedDeployArgs);
2290
+ const deployArgs = [
2291
+ "contract",
2292
+ "deploy",
2293
+ "--wasm",
2294
+ wasmPath,
2295
+ "--source-account",
2296
+ source,
2297
+ "--build-only",
2298
+ ...buildStellarNetworkArgs(network),
2299
+ ...constructorArgs
2300
+ ];
2301
+ let buildOutput;
2302
+ try {
2303
+ const buildResult = await runCommand("stellar", deployArgs, {
2304
+ cwd,
2305
+ failureCode: CaatingaErrorCode.ESTIMATE_FAILED
2306
+ });
2307
+ buildOutput = (buildResult.stdout || buildResult.all).trim();
2308
+ } catch (error) {
2309
+ if (error instanceof CaatingaError) {
2310
+ throw new CaatingaError(
2311
+ `Deploy cost estimate failed for "${contract.name}".`,
2312
+ CaatingaErrorCode.ESTIMATE_FAILED,
2313
+ error.hint ?? "Ensure WASM is built and deploy args resolve correctly.",
2314
+ error.cause
2315
+ );
2316
+ }
2317
+ throw error;
2318
+ }
2319
+ const simulateArgs = ["tx", "simulate", "--source-account", source, buildOutput];
2320
+ let simulateOutput = "";
2321
+ try {
2322
+ const simulateResult = await runCommand("stellar", simulateArgs, {
2323
+ cwd,
2324
+ failureCode: CaatingaErrorCode.ESTIMATE_FAILED
2325
+ });
2326
+ simulateOutput = simulateResult.all || `${simulateResult.stdout}
2327
+ ${simulateResult.stderr}`;
2328
+ } catch {
2329
+ simulateOutput = "";
2330
+ }
2331
+ const parsed = parseFeeStroops(simulateOutput);
2332
+ const inclusionFeeStroops = parsed.inclusion ?? 100;
2333
+ const resourceFeeStroops = parsed.resource;
2334
+ const totalFeeStroops = inclusionFeeStroops + (resourceFeeStroops ?? 0);
2335
+ return {
2336
+ contractName: contract.name,
2337
+ network: network.name,
2338
+ wasmPath: path13.relative(cwd, wasmPath) || wasmPath,
2339
+ inclusionFeeStroops,
2340
+ resourceFeeStroops,
2341
+ totalFeeStroops,
2342
+ advisory: "Advisory estimate only \u2014 actual fees may differ under network congestion or contract complexity.",
2343
+ rawOutput: simulateOutput || buildOutput
2344
+ };
2345
+ }
2346
+
2347
+ // src/contracts/inspect-contract.ts
2348
+ async function inspectContract(options) {
2349
+ const cwd = options.cwd ?? process.cwd();
2350
+ const contract = resolveContract(options.config, options.contractName, cwd);
2351
+ const network = resolveNetwork(options.config, options.networkName);
2352
+ const artifacts = await readArtifacts(cwd);
2353
+ const artifact = artifacts.networks[network.name]?.contracts[contract.name];
2354
+ if (!artifact) {
2355
+ throw new CaatingaError(
2356
+ `No deployed artifact found for "${contract.name}" on "${network.name}".`,
2357
+ CaatingaErrorCode.ARTIFACT_NOT_FOUND,
2358
+ "Run caatinga deploy before inspect."
2359
+ );
2360
+ }
2361
+ await checkBinary("stellar", "Install Stellar CLI before running caatinga inspect.");
2362
+ let reachable = false;
2363
+ let detail;
2364
+ try {
2365
+ await verifyDependencyContract({
2366
+ dependencyName: contract.name,
2367
+ contractId: artifact.contractId,
2368
+ network,
2369
+ cwd
2370
+ });
2371
+ reachable = true;
2372
+ detail = "Contract interface reachable on network.";
2373
+ } catch (error) {
2374
+ reachable = false;
2375
+ detail = error instanceof CaatingaError ? error.message : "Contract not reachable on network.";
2376
+ }
2377
+ let localHash;
2378
+ try {
2379
+ const wasmPath = await resolveWasmArtifactPath(contract.wasmPath, {
2380
+ sourcePath: contract.sourcePath
2381
+ });
2382
+ localHash = await hashWasm(wasmPath);
2383
+ } catch {
2384
+ localHash = void 0;
2385
+ }
2386
+ return {
2387
+ contractName: contract.name,
2388
+ network: network.name,
2389
+ artifact: {
2390
+ contractId: artifact.contractId,
2391
+ wasmHash: artifact.wasmHash,
2392
+ deployedAt: artifact.deployedAt,
2393
+ historyCount: artifact.history?.length ?? 0
2394
+ },
2395
+ onChain: { reachable, detail },
2396
+ localWasm: {
2397
+ path: contract.config.wasm,
2398
+ hash: localHash,
2399
+ matchesArtifact: Boolean(localHash && localHash === artifact.wasmHash)
2400
+ },
2401
+ dependencies: artifact.dependencies ?? contract.config.dependsOn ?? []
2402
+ };
2403
+ }
2404
+
1886
2405
  // src/templates/create-project-from-template.ts
1887
- import { cp, mkdir as mkdir3, readFile as readFile5, readdir as readdir3, stat as stat2, writeFile as writeFile4 } from "fs/promises";
1888
- import path12 from "path";
2406
+ import { cp, lstat, mkdir as mkdir3, readFile as readFile6, readdir as readdir3, stat as stat2, writeFile as writeFile4 } from "fs/promises";
2407
+ import path14 from "path";
1889
2408
  import { z as z7 } from "zod";
1890
2409
 
1891
2410
  // src/templates/template-manifest.schema.ts
1892
2411
  import { z as z6 } from "zod";
1893
- import semver3 from "semver";
2412
+ import semver5 from "semver";
1894
2413
  var CURRENT_TEMPLATE_VERSION = 1;
1895
2414
  var TemplateManifestSchema = z6.object({
1896
2415
  name: z6.string().min(1),
@@ -1914,14 +2433,14 @@ var TemplateManifestSchema = z6.object({
1914
2433
  })
1915
2434
  });
1916
2435
  function defaultCompatibleCoreRange(coreVersion = CAATINGA_CORE_VERSION) {
1917
- const version = semver3.valid(semver3.coerce(coreVersion));
2436
+ const version = semver5.valid(semver5.coerce(coreVersion));
1918
2437
  if (!version) {
1919
2438
  throw new Error(`Invalid core version: ${coreVersion}`);
1920
2439
  }
1921
2440
  return `^${version}`;
1922
2441
  }
1923
2442
  function isCoreVersionCompatible(range, coreVersion = CAATINGA_CORE_VERSION) {
1924
- return semver3.satisfies(coreVersion, range);
2443
+ return semver5.satisfies(coreVersion, range);
1925
2444
  }
1926
2445
  function getTemplateCompatibilityIssue(manifest, coreVersion = CAATINGA_CORE_VERSION) {
1927
2446
  if (manifest.caatinga.templateVersion !== CURRENT_TEMPLATE_VERSION) {
@@ -1956,8 +2475,8 @@ function formatTemplateCompatibilityHint(issue) {
1956
2475
  // src/templates/create-project-from-template.ts
1957
2476
  var TEMPLATE_COPY_EXCLUDED_DIRS = /* @__PURE__ */ new Set(["target", "test_snapshots", "node_modules", ".git"]);
1958
2477
  async function createProjectFromTemplate(options) {
1959
- const targetDir = path12.resolve(options.targetDir);
1960
- const templateDir = path12.resolve(options.templateDir);
2478
+ const targetDir = path14.resolve(options.targetDir);
2479
+ const templateDir = path14.resolve(options.templateDir);
1961
2480
  try {
1962
2481
  await stat2(templateDir);
1963
2482
  } catch {
@@ -1998,9 +2517,9 @@ async function ensureArtifacts(targetDir, projectName) {
1998
2517
  }
1999
2518
  }
2000
2519
  async function readTemplateManifest(templateDir) {
2001
- const manifestPath = path12.join(templateDir, "caatinga.template.json");
2520
+ const manifestPath = path14.join(templateDir, "caatinga.template.json");
2002
2521
  try {
2003
- const rawManifest = await readFile5(manifestPath, "utf8");
2522
+ const rawManifest = await readFile6(manifestPath, "utf8");
2004
2523
  const manifest = TemplateManifestSchema.parse(JSON.parse(rawManifest));
2005
2524
  const compatibilityIssue = getTemplateCompatibilityIssue(manifest);
2006
2525
  if (compatibilityIssue) {
@@ -2036,47 +2555,53 @@ async function replaceTemplateVariables(dir, projectName) {
2036
2555
  const entries = await readdir3(dir);
2037
2556
  await Promise.all(
2038
2557
  entries.map(async (entry) => {
2039
- const entryPath = path12.join(dir, entry);
2040
- const entryStat = await stat2(entryPath);
2558
+ if (TEMPLATE_COPY_EXCLUDED_DIRS.has(entry)) {
2559
+ return;
2560
+ }
2561
+ const entryPath = path14.join(dir, entry);
2562
+ const entryStat = await lstat(entryPath);
2563
+ if (entryStat.isSymbolicLink()) {
2564
+ return;
2565
+ }
2041
2566
  if (entryStat.isDirectory()) {
2042
2567
  await replaceTemplateVariables(entryPath, projectName);
2043
2568
  return;
2044
2569
  }
2045
- if (!isTextTemplateFile(entryPath)) {
2570
+ if (!entryStat.isFile() || !isTextTemplateFile(entryPath)) {
2046
2571
  return;
2047
2572
  }
2048
- const content = await readFile5(entryPath, "utf8");
2573
+ const content = await readFile6(entryPath, "utf8");
2049
2574
  await writeFile4(entryPath, content.replaceAll("__PROJECT_NAME__", projectName), "utf8");
2050
2575
  })
2051
2576
  );
2052
2577
  }
2053
2578
  function shouldCopyTemplateEntry(templateDir, source, userFilter) {
2054
- const relativePath = path12.relative(templateDir, source);
2579
+ const relativePath = path14.relative(templateDir, source);
2055
2580
  if (!relativePath || relativePath === ".") {
2056
2581
  return true;
2057
2582
  }
2058
- const normalizedPath = relativePath.split(path12.sep).join("/");
2583
+ const normalizedPath = relativePath.split(path14.sep).join("/");
2059
2584
  if (userFilter && !userFilter(normalizedPath)) {
2060
2585
  return false;
2061
2586
  }
2062
- return !relativePath.split(path12.sep).some((segment) => TEMPLATE_COPY_EXCLUDED_DIRS.has(segment));
2587
+ return !relativePath.split(path14.sep).some((segment) => TEMPLATE_COPY_EXCLUDED_DIRS.has(segment));
2063
2588
  }
2064
2589
  function isTextTemplateFile(filePath) {
2065
2590
  return [".json", ".md", ".rs", ".toml", ".ts", ".tsx", ".css", ".html"].includes(
2066
- path12.extname(filePath)
2591
+ path14.extname(filePath)
2067
2592
  );
2068
2593
  }
2069
2594
 
2070
2595
  // src/scaffold/create-zk-project.ts
2071
2596
  import { cp as cp2, mkdir as mkdir4, writeFile as writeFile5 } from "fs/promises";
2072
2597
  import { existsSync as existsSync2 } from "fs";
2073
- import path13 from "path";
2598
+ import path15 from "path";
2074
2599
  import { fileURLToPath } from "url";
2075
- var moduleDir = typeof __dirname === "string" ? __dirname : path13.dirname(fileURLToPath(import.meta.url));
2600
+ var moduleDir = typeof __dirname === "string" ? __dirname : path15.dirname(fileURLToPath(import.meta.url));
2076
2601
  function scaffoldRoot() {
2077
2602
  const candidates = [
2078
- path13.resolve(moduleDir, "../../scaffolds"),
2079
- path13.resolve(moduleDir, "../scaffolds")
2603
+ path15.resolve(moduleDir, "../../scaffolds"),
2604
+ path15.resolve(moduleDir, "../scaffolds")
2080
2605
  ];
2081
2606
  const found = candidates.find((candidate) => existsSync2(candidate));
2082
2607
  return found ?? candidates[0];
@@ -2167,39 +2692,39 @@ Replace \`circuits/main.circom\` with your circuit. Keep the entry point named \
2167
2692
  `;
2168
2693
  }
2169
2694
  async function createZkProject(options) {
2170
- const targetDir = path13.resolve(options.targetDir);
2695
+ const targetDir = path15.resolve(options.targetDir);
2171
2696
  const force = options.force ?? false;
2172
2697
  const projectFiles = options.projectFiles ?? true;
2173
2698
  await mkdir4(targetDir, { recursive: true });
2174
2699
  if (projectFiles) {
2175
2700
  await Promise.all([
2176
- writeFile5(path13.join(targetDir, "caatinga.config.ts"), configSource(options.projectName), {
2701
+ writeFile5(path15.join(targetDir, "caatinga.config.ts"), configSource(options.projectName), {
2177
2702
  encoding: "utf8",
2178
2703
  flag: force ? "w" : "wx"
2179
2704
  }),
2180
- writeFile5(path13.join(targetDir, "package.json"), packageJsonSource(options.projectName), {
2705
+ writeFile5(path15.join(targetDir, "package.json"), packageJsonSource(options.projectName), {
2181
2706
  encoding: "utf8",
2182
2707
  flag: force ? "w" : "wx"
2183
2708
  }),
2184
- writeFile5(path13.join(targetDir, ".gitignore"), "node_modules\n.artifacts\ntarget\n", {
2709
+ writeFile5(path15.join(targetDir, ".gitignore"), "node_modules\n.artifacts\ntarget\n", {
2185
2710
  encoding: "utf8",
2186
2711
  flag: force ? "w" : "wx"
2187
2712
  }),
2188
- writeFile5(path13.join(targetDir, "README.md"), readmeSource(options.projectName), {
2713
+ writeFile5(path15.join(targetDir, "README.md"), readmeSource(options.projectName), {
2189
2714
  encoding: "utf8",
2190
2715
  flag: force ? "w" : "wx"
2191
2716
  })
2192
2717
  ]);
2193
2718
  }
2194
- await mkdir4(path13.join(targetDir, "contracts"), { recursive: true });
2195
- await cp2(path13.join(scaffoldRoot(), "zk-circuit-stub"), path13.join(targetDir, "circuits"), {
2719
+ await mkdir4(path15.join(targetDir, "contracts"), { recursive: true });
2720
+ await cp2(path15.join(scaffoldRoot(), "zk-circuit-stub"), path15.join(targetDir, "circuits"), {
2196
2721
  recursive: true,
2197
2722
  force,
2198
2723
  errorOnExist: !force
2199
2724
  });
2200
2725
  await cp2(
2201
- path13.join(scaffoldRoot(), "zk-verifier"),
2202
- path13.join(targetDir, "contracts", "verifier"),
2726
+ path15.join(scaffoldRoot(), "zk-verifier"),
2727
+ path15.join(targetDir, "contracts", "verifier"),
2203
2728
  {
2204
2729
  recursive: true,
2205
2730
  force,
@@ -2218,13 +2743,13 @@ async function createZkProject(options) {
2218
2743
  // src/scaffold/create-minimal-project.ts
2219
2744
  import { cp as cp3, mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
2220
2745
  import { existsSync as existsSync3 } from "fs";
2221
- import path14 from "path";
2746
+ import path16 from "path";
2222
2747
  import { fileURLToPath as fileURLToPath2 } from "url";
2223
- var moduleDir2 = typeof __dirname === "string" ? __dirname : path14.dirname(fileURLToPath2(import.meta.url));
2748
+ var moduleDir2 = typeof __dirname === "string" ? __dirname : path16.dirname(fileURLToPath2(import.meta.url));
2224
2749
  function scaffoldRoot2() {
2225
2750
  const candidates = [
2226
- path14.resolve(moduleDir2, "../../scaffolds"),
2227
- path14.resolve(moduleDir2, "../scaffolds")
2751
+ path16.resolve(moduleDir2, "../../scaffolds"),
2752
+ path16.resolve(moduleDir2, "../scaffolds")
2228
2753
  ];
2229
2754
  const found = candidates.find((candidate) => existsSync3(candidate));
2230
2755
  return found ?? candidates[0];
@@ -2315,31 +2840,31 @@ Edit \`contracts/app/src/lib.rs\` to customize the contract. Add a frontend late
2315
2840
  `;
2316
2841
  }
2317
2842
  async function createMinimalProject(options) {
2318
- const targetDir = path14.resolve(options.targetDir);
2843
+ const targetDir = path16.resolve(options.targetDir);
2319
2844
  const force = options.force ?? false;
2320
2845
  await mkdir5(targetDir, { recursive: true });
2321
2846
  await Promise.all([
2322
- writeFile6(path14.join(targetDir, "caatinga.config.ts"), configSource2(options.projectName), {
2847
+ writeFile6(path16.join(targetDir, "caatinga.config.ts"), configSource2(options.projectName), {
2323
2848
  encoding: "utf8",
2324
2849
  flag: force ? "w" : "wx"
2325
2850
  }),
2326
- writeFile6(path14.join(targetDir, "package.json"), packageJsonSource2(options.projectName), {
2851
+ writeFile6(path16.join(targetDir, "package.json"), packageJsonSource2(options.projectName), {
2327
2852
  encoding: "utf8",
2328
2853
  flag: force ? "w" : "wx"
2329
2854
  }),
2330
- writeFile6(path14.join(targetDir, ".gitignore"), "node_modules\n.artifacts\ntarget\n", {
2855
+ writeFile6(path16.join(targetDir, ".gitignore"), "node_modules\n.artifacts\ntarget\n", {
2331
2856
  encoding: "utf8",
2332
2857
  flag: force ? "w" : "wx"
2333
2858
  }),
2334
- writeFile6(path14.join(targetDir, "README.md"), readmeSource2(options.projectName), {
2859
+ writeFile6(path16.join(targetDir, "README.md"), readmeSource2(options.projectName), {
2335
2860
  encoding: "utf8",
2336
2861
  flag: force ? "w" : "wx"
2337
2862
  })
2338
2863
  ]);
2339
- await mkdir5(path14.join(targetDir, "contracts"), { recursive: true });
2864
+ await mkdir5(path16.join(targetDir, "contracts"), { recursive: true });
2340
2865
  await cp3(
2341
- path14.join(scaffoldRoot2(), "soroban-contract-stub"),
2342
- path14.join(targetDir, "contracts", "app"),
2866
+ path16.join(scaffoldRoot2(), "soroban-contract-stub"),
2867
+ path16.join(targetDir, "contracts", "app"),
2343
2868
  {
2344
2869
  recursive: true,
2345
2870
  force,
@@ -2354,36 +2879,24 @@ async function createMinimalProject(options) {
2354
2879
  }
2355
2880
 
2356
2881
  // src/ci/is-transient-testnet-smoke-failure.ts
2357
- var NO_RETRY_CAATINGA_SUBSTRINGS = [
2358
- "CAATINGA_UNSUPPORTED_CLI_VERSION",
2359
- "CAATINGA_STELLAR_CLI_VERSION_PARSE_FAILED",
2360
- "CAATINGA_STELLAR_CLI_NOT_FOUND",
2361
- "CAATINGA_INVALID_CONFIG",
2362
- "CAATINGA_CONFIG_NOT_FOUND"
2363
- ];
2364
- var TRANSIENT_PATTERN = /timeout|i\/o timeout|econnreset|connection reset|503|502|429|rate limit|temporar|bad gateway|fetch failed|network error|unavailable/i;
2365
2882
  function isTransientTestnetSmokeFailure(logText) {
2366
- if (!logText.trim()) {
2367
- return false;
2368
- }
2369
- for (const marker of NO_RETRY_CAATINGA_SUBSTRINGS) {
2370
- if (logText.includes(marker)) {
2371
- return false;
2372
- }
2373
- }
2374
- return TRANSIENT_PATTERN.test(logText);
2883
+ return isTransientCommandFailure(logText);
2375
2884
  }
2376
2885
  export {
2377
2886
  BINDING_MARKER_FILENAME,
2378
2887
  BindingMarkerSchema,
2379
2888
  CAATINGA_CORE_VERSION,
2889
+ CURRENT_ARTIFACTS_SCHEMA_VERSION,
2380
2890
  CaatingaArtifactsSchema,
2381
2891
  CaatingaConfigSchema,
2382
2892
  CaatingaError,
2383
2893
  CaatingaErrorCode,
2894
+ DEFAULT_CLI_SOURCE,
2384
2895
  READ_CALL_FAILURE_REGEX,
2385
2896
  STELLAR_CLI_LAST_TESTED_VERSION,
2386
2897
  STELLAR_CLI_MIN_VERSION,
2898
+ STELLAR_SDK_LAST_TESTED_VERSION,
2899
+ STELLAR_SDK_MIN_VERSION,
2387
2900
  TemplateManifestSchema,
2388
2901
  WELL_KNOWN_NETWORKS,
2389
2902
  buildContract,
@@ -2391,6 +2904,7 @@ export {
2391
2904
  buildReadCallHint,
2392
2905
  checkBinary,
2393
2906
  checkStellarCliVersion,
2907
+ checkStellarSdkVersion,
2394
2908
  collectProjectStatus,
2395
2909
  createInitialArtifacts,
2396
2910
  createMinimalProject,
@@ -2399,20 +2913,27 @@ export {
2399
2913
  defineConfig,
2400
2914
  deployContract,
2401
2915
  deployContractGraph,
2916
+ describeCliSource,
2917
+ estimateDeployCost,
2402
2918
  evaluateBindingFreshness,
2403
2919
  evaluateBindingsFreshness,
2404
2920
  evaluateStellarCliCompatibility,
2921
+ evaluateStellarSdkCompatibility,
2405
2922
  formatCaatingaError,
2406
2923
  generateBindings,
2407
2924
  generateBindingsGraph,
2925
+ inspectContract,
2408
2926
  invokeContract,
2409
2927
  isCargoBinMissingFromPath,
2410
2928
  isReadCallFailure,
2411
2929
  isTransientTestnetSmokeFailure,
2412
2930
  loadConfig,
2931
+ migrateArtifactsFile,
2932
+ migrateArtifactsToV2,
2413
2933
  parseContractId,
2414
2934
  parseInvokeTarget,
2415
2935
  parseStellarCliVersion,
2936
+ parseStellarSdkVersion,
2416
2937
  readArtifacts,
2417
2938
  readBindingMarker,
2418
2939
  readContract,
@@ -2422,6 +2943,8 @@ export {
2422
2943
  resolveDeployOrder,
2423
2944
  resolveNetwork,
2424
2945
  resolveSubprocessEnv,
2946
+ restoreArtifactFromHistory,
2947
+ rollbackContractArtifact,
2425
2948
  runCommand,
2426
2949
  toCaatingaError,
2427
2950
  updateArtifact,