@caatinga/core 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -30,6 +30,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ BINDING_MARKER_FILENAME: () => BINDING_MARKER_FILENAME,
34
+ BindingMarkerSchema: () => BindingMarkerSchema,
33
35
  CAATINGA_CORE_VERSION: () => CAATINGA_CORE_VERSION,
34
36
  CaatingaArtifactsSchema: () => CaatingaArtifactsSchema,
35
37
  CaatingaConfigSchema: () => CaatingaConfigSchema,
@@ -43,11 +45,14 @@ __export(index_exports, {
43
45
  buildDependencyGraph: () => buildDependencyGraph,
44
46
  checkBinary: () => checkBinary,
45
47
  checkStellarCliVersion: () => checkStellarCliVersion,
48
+ collectProjectStatus: () => collectProjectStatus,
46
49
  createInitialArtifacts: () => createInitialArtifacts,
47
50
  createProjectFromTemplate: () => createProjectFromTemplate,
48
51
  defineConfig: () => defineConfig,
49
52
  deployContract: () => deployContract,
50
53
  deployContractGraph: () => deployContractGraph,
54
+ evaluateBindingFreshness: () => evaluateBindingFreshness,
55
+ evaluateBindingsFreshness: () => evaluateBindingsFreshness,
51
56
  evaluateStellarCliCompatibility: () => evaluateStellarCliCompatibility,
52
57
  formatCaatingaError: () => formatCaatingaError,
53
58
  generateBindings: () => generateBindings,
@@ -59,6 +64,7 @@ __export(index_exports, {
59
64
  parseInvokeTarget: () => parseInvokeTarget,
60
65
  parseStellarCliVersion: () => parseStellarCliVersion,
61
66
  readArtifacts: () => readArtifacts,
67
+ readBindingMarker: () => readBindingMarker,
62
68
  resolveContract: () => resolveContract,
63
69
  resolveDeployArgs: () => resolveDeployArgs,
64
70
  resolveDeployOrder: () => resolveDeployOrder,
@@ -67,7 +73,8 @@ __export(index_exports, {
67
73
  toCaatingaError: () => toCaatingaError,
68
74
  updateArtifact: () => updateArtifact,
69
75
  validateSourceShape: () => validateSourceShape,
70
- writeArtifacts: () => writeArtifacts
76
+ writeArtifacts: () => writeArtifacts,
77
+ writeBindingMarker: () => writeBindingMarker
71
78
  });
72
79
  module.exports = __toCommonJS(index_exports);
73
80
 
@@ -178,7 +185,7 @@ function formatCause(cause) {
178
185
  }
179
186
 
180
187
  // src/version.ts
181
- var CAATINGA_CORE_VERSION = "2.1.0";
188
+ var CAATINGA_CORE_VERSION = "2.2.0";
182
189
 
183
190
  // src/config/config.schema.ts
184
191
  var import_zod = require("zod");
@@ -344,18 +351,6 @@ function updateArtifact(artifacts, networkName, contractName, contractArtifact,
344
351
  };
345
352
  }
346
353
 
347
- // src/networks/networks.ts
348
- var WELL_KNOWN_NETWORKS = {
349
- testnet: {
350
- rpcUrl: "https://soroban-testnet.stellar.org",
351
- networkPassphrase: "Test SDF Network ; September 2015"
352
- },
353
- mainnet: {
354
- rpcUrl: "https://mainnet.sorobanrpc.com",
355
- networkPassphrase: "Public Global Stellar Network ; September 2015"
356
- }
357
- };
358
-
359
354
  // src/networks/resolve-network.ts
360
355
  function resolveNetwork(config, networkName) {
361
356
  const name = networkName ?? config.defaultNetwork;
@@ -370,6 +365,174 @@ function resolveNetwork(config, networkName) {
370
365
  return { name, config: network };
371
366
  }
372
367
 
368
+ // src/bindings/binding-freshness.ts
369
+ var import_promises5 = require("fs/promises");
370
+ var import_node_path5 = __toESM(require("path"), 1);
371
+
372
+ // src/bindings/binding-marker.ts
373
+ var import_promises4 = require("fs/promises");
374
+ var import_node_path4 = __toESM(require("path"), 1);
375
+ var import_zod5 = require("zod");
376
+ var BINDING_MARKER_FILENAME = ".caatinga-bindings.json";
377
+ var BindingMarkerSchema = import_zod5.z.object({
378
+ version: import_zod5.z.literal(1),
379
+ contractId: import_zod5.z.string().min(1),
380
+ wasmHash: import_zod5.z.string().min(1),
381
+ network: import_zod5.z.string().min(1),
382
+ generatedAt: import_zod5.z.string().datetime()
383
+ });
384
+ async function writeBindingMarker(outputDir, marker) {
385
+ const markerPath = import_node_path4.default.join(outputDir, BINDING_MARKER_FILENAME);
386
+ await (0, import_promises4.writeFile)(markerPath, `${JSON.stringify(marker, null, 2)}
387
+ `, "utf8");
388
+ }
389
+ async function readBindingMarker(outputDir) {
390
+ const markerPath = import_node_path4.default.join(outputDir, BINDING_MARKER_FILENAME);
391
+ let raw;
392
+ try {
393
+ raw = await (0, import_promises4.readFile)(markerPath, "utf8");
394
+ } catch {
395
+ return null;
396
+ }
397
+ try {
398
+ return BindingMarkerSchema.parse(JSON.parse(raw));
399
+ } catch {
400
+ return null;
401
+ }
402
+ }
403
+
404
+ // src/bindings/binding-freshness.ts
405
+ async function listGeneratedEntries(outputDir) {
406
+ try {
407
+ const entries = await (0, import_promises5.readdir)(outputDir);
408
+ return entries.filter((entry) => entry !== BINDING_MARKER_FILENAME);
409
+ } catch {
410
+ return null;
411
+ }
412
+ }
413
+ async function evaluateBindingFreshness(options) {
414
+ const cwd = options.cwd ?? process.cwd();
415
+ const outputDir = import_node_path5.default.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
416
+ const contractArtifact = options.artifacts.networks[options.networkName]?.contracts[options.contractName];
417
+ if (!contractArtifact) {
418
+ return {
419
+ contractName: options.contractName,
420
+ status: "missing",
421
+ outputDir,
422
+ marker: null,
423
+ reason: `not deployed on "${options.networkName}"`
424
+ };
425
+ }
426
+ const generatedEntries = await listGeneratedEntries(outputDir);
427
+ if (generatedEntries === null || generatedEntries.length === 0) {
428
+ return {
429
+ contractName: options.contractName,
430
+ status: "missing",
431
+ outputDir,
432
+ marker: null,
433
+ reason: "no generated bindings found"
434
+ };
435
+ }
436
+ const marker = await readBindingMarker(outputDir);
437
+ if (!marker) {
438
+ return {
439
+ contractName: options.contractName,
440
+ status: "unknown",
441
+ outputDir,
442
+ marker: null,
443
+ reason: "bindings exist but have no provenance marker \u2014 rerun caatinga generate to record it"
444
+ };
445
+ }
446
+ if (marker.contractId !== contractArtifact.contractId) {
447
+ return {
448
+ contractName: options.contractName,
449
+ status: "stale",
450
+ outputDir,
451
+ marker,
452
+ reason: "contractId changed since last generate"
453
+ };
454
+ }
455
+ if (marker.wasmHash !== contractArtifact.wasmHash) {
456
+ return {
457
+ contractName: options.contractName,
458
+ status: "stale",
459
+ outputDir,
460
+ marker,
461
+ reason: "wasmHash changed since last generate"
462
+ };
463
+ }
464
+ return {
465
+ contractName: options.contractName,
466
+ status: "fresh",
467
+ outputDir,
468
+ marker
469
+ };
470
+ }
471
+ async function evaluateBindingsFreshness(options) {
472
+ const contractNames = Object.keys(
473
+ options.artifacts.networks[options.networkName]?.contracts ?? {}
474
+ );
475
+ const results = [];
476
+ for (const contractName of contractNames) {
477
+ results.push(await evaluateBindingFreshness({ ...options, contractName }));
478
+ }
479
+ return results;
480
+ }
481
+
482
+ // src/artifacts/project-status.ts
483
+ async function collectProjectStatus(options) {
484
+ const cwd = options.cwd ?? process.cwd();
485
+ const artifacts = await readArtifacts(cwd);
486
+ let networkNames;
487
+ if (options.networkName) {
488
+ networkNames = [resolveNetwork(options.config, options.networkName).name];
489
+ } else {
490
+ const fromArtifacts = Object.keys(artifacts.networks);
491
+ const fallback = options.config.defaultNetwork ?? "testnet";
492
+ networkNames = fromArtifacts.length > 0 ? fromArtifacts : [fallback];
493
+ if (!networkNames.includes(fallback) && options.config.networks[fallback]) {
494
+ networkNames.push(fallback);
495
+ }
496
+ }
497
+ const networks = [];
498
+ for (const networkName of networkNames) {
499
+ const contracts = [];
500
+ for (const name of Object.keys(options.config.contracts)) {
501
+ const artifact = artifacts.networks[networkName]?.contracts[name];
502
+ const bindings = await evaluateBindingFreshness({
503
+ config: options.config,
504
+ artifacts,
505
+ networkName,
506
+ contractName: name,
507
+ cwd
508
+ });
509
+ contracts.push({
510
+ name,
511
+ deployed: Boolean(artifact),
512
+ contractId: artifact?.contractId,
513
+ wasmHash: artifact?.wasmHash,
514
+ deployedAt: artifact?.deployedAt,
515
+ dependencies: artifact?.dependencies ?? options.config.contracts[name].dependsOn ?? [],
516
+ bindings
517
+ });
518
+ }
519
+ networks.push({ network: networkName, contracts });
520
+ }
521
+ return { project: options.config.project, networks };
522
+ }
523
+
524
+ // src/networks/networks.ts
525
+ var WELL_KNOWN_NETWORKS = {
526
+ testnet: {
527
+ rpcUrl: "https://soroban-testnet.stellar.org",
528
+ networkPassphrase: "Test SDF Network ; September 2015"
529
+ },
530
+ mainnet: {
531
+ rpcUrl: "https://mainnet.sorobanrpc.com",
532
+ networkPassphrase: "Public Global Stellar Network ; September 2015"
533
+ }
534
+ };
535
+
373
536
  // src/shell/run-command.ts
374
537
  var import_execa = require("execa");
375
538
 
@@ -691,7 +854,7 @@ function validateSourceShape(source) {
691
854
  }
692
855
 
693
856
  // src/contracts/resolve-contract.ts
694
- var import_node_path4 = __toESM(require("path"), 1);
857
+ var import_node_path6 = __toESM(require("path"), 1);
695
858
  function resolveContract(config, contractName, cwd = process.cwd()) {
696
859
  const contract = config.contracts[contractName];
697
860
  if (!contract) {
@@ -704,15 +867,15 @@ function resolveContract(config, contractName, cwd = process.cwd()) {
704
867
  return {
705
868
  name: contractName,
706
869
  config: contract,
707
- sourcePath: import_node_path4.default.resolve(cwd, contract.path),
708
- wasmPath: import_node_path4.default.resolve(cwd, contract.wasm)
870
+ sourcePath: import_node_path6.default.resolve(cwd, contract.path),
871
+ wasmPath: import_node_path6.default.resolve(cwd, contract.wasm)
709
872
  };
710
873
  }
711
874
 
712
875
  // src/contracts/wasm.ts
713
876
  var import_node_crypto = require("crypto");
714
- var import_promises4 = require("fs/promises");
715
- var import_node_path5 = __toESM(require("path"), 1);
877
+ var import_promises6 = require("fs/promises");
878
+ var import_node_path7 = __toESM(require("path"), 1);
716
879
  var LEGACY_RUST_WASM_TARGET = "wasm32-unknown-unknown";
717
880
  var CURRENT_RUST_WASM_TARGET = "wasm32v1-none";
718
881
  function toCurrentWasmTargetPath(wasmPath) {
@@ -735,12 +898,12 @@ function wasmNotFoundError(configuredWasmPath, options) {
735
898
  );
736
899
  }
737
900
  function toConfigRelativeWasmPath(absoluteWasmPath) {
738
- const relative = import_node_path5.default.relative(process.cwd(), absoluteWasmPath);
739
- return relative.startsWith("..") ? absoluteWasmPath : `./${relative.split(import_node_path5.default.sep).join("/")}`;
901
+ const relative = import_node_path7.default.relative(process.cwd(), absoluteWasmPath);
902
+ return relative.startsWith("..") ? absoluteWasmPath : `./${relative.split(import_node_path7.default.sep).join("/")}`;
740
903
  }
741
904
  async function resolveWasmArtifactPath(configuredWasmPath) {
742
905
  try {
743
- await (0, import_promises4.access)(configuredWasmPath);
906
+ await (0, import_promises6.access)(configuredWasmPath);
744
907
  return configuredWasmPath;
745
908
  } catch {
746
909
  const currentTargetPath = toCurrentWasmTargetPath(configuredWasmPath);
@@ -748,7 +911,7 @@ async function resolveWasmArtifactPath(configuredWasmPath) {
748
911
  throw wasmNotFoundError(configuredWasmPath);
749
912
  }
750
913
  try {
751
- await (0, import_promises4.access)(currentTargetPath);
914
+ await (0, import_promises6.access)(currentTargetPath);
752
915
  return currentTargetPath;
753
916
  } catch {
754
917
  throw wasmNotFoundError(configuredWasmPath, { migratedPath: currentTargetPath });
@@ -756,20 +919,20 @@ async function resolveWasmArtifactPath(configuredWasmPath) {
756
919
  }
757
920
  }
758
921
  async function hashWasm(wasmPath) {
759
- const bytes = await (0, import_promises4.readFile)(wasmPath);
922
+ const bytes = await (0, import_promises6.readFile)(wasmPath);
760
923
  return (0, import_node_crypto.createHash)("sha256").update(bytes).digest("hex");
761
924
  }
762
925
  async function getNewestMtimeInDirectory(directory) {
763
926
  try {
764
- await (0, import_promises4.access)(directory);
927
+ await (0, import_promises6.access)(directory);
765
928
  } catch {
766
929
  return void 0;
767
930
  }
768
931
  let newest = 0;
769
932
  async function walk(dir) {
770
- const entries = await (0, import_promises4.readdir)(dir, { withFileTypes: true });
933
+ const entries = await (0, import_promises6.readdir)(dir, { withFileTypes: true });
771
934
  for (const entry of entries) {
772
- const entryPath = import_node_path5.default.join(dir, entry.name);
935
+ const entryPath = import_node_path7.default.join(dir, entry.name);
773
936
  if (entry.isDirectory()) {
774
937
  await walk(entryPath);
775
938
  continue;
@@ -777,7 +940,7 @@ async function getNewestMtimeInDirectory(directory) {
777
940
  if (!entry.isFile()) {
778
941
  continue;
779
942
  }
780
- const fileStat = await (0, import_promises4.stat)(entryPath);
943
+ const fileStat = await (0, import_promises6.stat)(entryPath);
781
944
  newest = Math.max(newest, fileStat.mtimeMs);
782
945
  }
783
946
  }
@@ -785,14 +948,14 @@ async function getNewestMtimeInDirectory(directory) {
785
948
  return newest > 0 ? newest : void 0;
786
949
  }
787
950
  async function isWasmOlderThanSources(input) {
788
- const srcDir = import_node_path5.default.join(input.contractPath, "src");
951
+ const srcDir = import_node_path7.default.join(input.contractPath, "src");
789
952
  const newestSourceMtime = await getNewestMtimeInDirectory(srcDir);
790
953
  if (newestSourceMtime === void 0) {
791
954
  return false;
792
955
  }
793
956
  let wasmStat;
794
957
  try {
795
- wasmStat = await (0, import_promises4.stat)(input.wasmPath);
958
+ wasmStat = await (0, import_promises6.stat)(input.wasmPath);
796
959
  } catch {
797
960
  return false;
798
961
  }
@@ -858,7 +1021,7 @@ async function buildContract(options) {
858
1021
  }
859
1022
 
860
1023
  // src/contracts/deploy-contract.ts
861
- var import_node_path6 = __toESM(require("path"), 1);
1024
+ var import_node_path8 = __toESM(require("path"), 1);
862
1025
 
863
1026
  // src/contracts/dependency-graph.ts
864
1027
  function buildDependencyGraph(contracts) {
@@ -959,7 +1122,7 @@ async function deployContract(options) {
959
1122
  contract: contractWithWasm,
960
1123
  network,
961
1124
  contractId: existing.contractId,
962
- artifactsPath: import_node_path6.default.resolve(cwd, "caatinga.artifacts.json"),
1125
+ artifactsPath: import_node_path8.default.resolve(cwd, "caatinga.artifacts.json"),
963
1126
  output: "",
964
1127
  skipped: true,
965
1128
  staleWasmWarning
@@ -1235,17 +1398,17 @@ async function deployContractGraph(options) {
1235
1398
  }
1236
1399
 
1237
1400
  // src/contracts/generate-bindings.ts
1238
- var import_promises5 = require("fs/promises");
1239
- var import_node_path7 = __toESM(require("path"), 1);
1401
+ var import_promises7 = require("fs/promises");
1402
+ var import_node_path9 = __toESM(require("path"), 1);
1240
1403
  function toBindingImportPath(bindingsOutput, contractName) {
1241
- const normalized = bindingsOutput.replace(/^\.\//, "").split(import_node_path7.default.sep).join("/");
1242
- return `./${import_node_path7.default.posix.join(normalized, contractName, "src", "index.js")}`;
1404
+ const normalized = bindingsOutput.replace(/^\.\//, "").split(import_node_path9.default.sep).join("/");
1405
+ return `./${import_node_path9.default.posix.join(normalized, contractName, "src", "index.js")}`;
1243
1406
  }
1244
1407
  async function removeLegacyBindingStub(cwd, bindingsOutput, contractName) {
1245
- const legacyPath = import_node_path7.default.resolve(cwd, bindingsOutput, `${contractName}.ts`);
1408
+ const legacyPath = import_node_path9.default.resolve(cwd, bindingsOutput, `${contractName}.ts`);
1246
1409
  try {
1247
- await (0, import_promises5.access)(legacyPath);
1248
- await (0, import_promises5.unlink)(legacyPath);
1410
+ await (0, import_promises7.access)(legacyPath);
1411
+ await (0, import_promises7.unlink)(legacyPath);
1249
1412
  return true;
1250
1413
  } catch {
1251
1414
  return false;
@@ -1264,8 +1427,8 @@ async function generateBindings(options) {
1264
1427
  );
1265
1428
  }
1266
1429
  await checkBinary("stellar", "Install Stellar CLI before running caatinga generate.");
1267
- const outputDir = import_node_path7.default.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
1268
- await (0, import_promises5.mkdir)(outputDir, { recursive: true });
1430
+ const outputDir = import_node_path9.default.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
1431
+ await (0, import_promises7.mkdir)(outputDir, { recursive: true });
1269
1432
  const result = await runCommand("stellar", [
1270
1433
  "contract",
1271
1434
  "bindings",
@@ -1285,12 +1448,21 @@ async function generateBindings(options) {
1285
1448
  options.config.frontend.bindingsOutput,
1286
1449
  options.contractName
1287
1450
  );
1451
+ const marker = {
1452
+ version: 1,
1453
+ contractId: contractArtifact.contractId,
1454
+ wasmHash: contractArtifact.wasmHash,
1455
+ network: network.name,
1456
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
1457
+ };
1458
+ await writeBindingMarker(outputDir, marker);
1288
1459
  return {
1289
1460
  contractName: options.contractName,
1290
1461
  network,
1291
1462
  outputDir,
1292
1463
  importPath: toBindingImportPath(options.config.frontend.bindingsOutput, options.contractName),
1293
1464
  legacyStubRemoved,
1465
+ marker,
1294
1466
  output: result.all || result.stdout
1295
1467
  };
1296
1468
  }
@@ -1302,6 +1474,8 @@ async function generateBindingsGraph(options) {
1302
1474
  let targets;
1303
1475
  if (options.contractName) {
1304
1476
  targets = [options.contractName];
1477
+ } else if (options.contractNames && options.contractNames.length > 0) {
1478
+ targets = options.contractNames;
1305
1479
  } else {
1306
1480
  const artifacts = await readArtifacts(cwd);
1307
1481
  targets = Object.keys(artifacts.networks[network.name]?.contracts ?? {});
@@ -1399,33 +1573,33 @@ ${error.hint ?? ""}`)) {
1399
1573
  }
1400
1574
 
1401
1575
  // src/templates/create-project-from-template.ts
1402
- var import_promises6 = require("fs/promises");
1403
- var import_node_path8 = __toESM(require("path"), 1);
1404
- var import_zod6 = require("zod");
1576
+ var import_promises8 = require("fs/promises");
1577
+ var import_node_path10 = __toESM(require("path"), 1);
1578
+ var import_zod7 = require("zod");
1405
1579
 
1406
1580
  // src/templates/template-manifest.schema.ts
1407
- var import_zod5 = require("zod");
1581
+ var import_zod6 = require("zod");
1408
1582
  var import_semver3 = __toESM(require("semver"), 1);
1409
1583
  var CURRENT_TEMPLATE_VERSION = 1;
1410
- var TemplateManifestSchema = import_zod5.z.object({
1411
- name: import_zod5.z.string().min(1),
1412
- version: import_zod5.z.string().min(1),
1413
- description: import_zod5.z.string().optional(),
1414
- caatinga: import_zod5.z.object({
1415
- compatibleCore: import_zod5.z.string().min(1),
1416
- templateVersion: import_zod5.z.number().int().positive()
1584
+ var TemplateManifestSchema = import_zod6.z.object({
1585
+ name: import_zod6.z.string().min(1),
1586
+ version: import_zod6.z.string().min(1),
1587
+ description: import_zod6.z.string().optional(),
1588
+ caatinga: import_zod6.z.object({
1589
+ compatibleCore: import_zod6.z.string().min(1),
1590
+ templateVersion: import_zod6.z.number().int().positive()
1417
1591
  }),
1418
- frontend: import_zod5.z.object({
1419
- framework: import_zod5.z.enum(["vite-react", "next", "astro"]),
1420
- packageManager: import_zod5.z.enum(["npm", "pnpm", "yarn", "bun"]).default("npm")
1592
+ frontend: import_zod6.z.object({
1593
+ framework: import_zod6.z.enum(["vite-react", "next", "astro"]),
1594
+ packageManager: import_zod6.z.enum(["npm", "pnpm", "yarn", "bun"]).default("npm")
1421
1595
  }),
1422
- contracts: import_zod5.z.object({
1423
- path: import_zod5.z.string(),
1424
- default: import_zod5.z.string().optional()
1596
+ contracts: import_zod6.z.object({
1597
+ path: import_zod6.z.string(),
1598
+ default: import_zod6.z.string().optional()
1425
1599
  }),
1426
- files: import_zod5.z.object({
1427
- config: import_zod5.z.string().default("caatinga.config.ts"),
1428
- artifacts: import_zod5.z.string().default("caatinga.artifacts.json")
1600
+ files: import_zod6.z.object({
1601
+ config: import_zod6.z.string().default("caatinga.config.ts"),
1602
+ artifacts: import_zod6.z.string().default("caatinga.artifacts.json")
1429
1603
  })
1430
1604
  });
1431
1605
  function defaultCompatibleCoreRange(coreVersion = CAATINGA_CORE_VERSION) {
@@ -1476,10 +1650,10 @@ var TEMPLATE_COPY_EXCLUDED_DIRS = /* @__PURE__ */ new Set([
1476
1650
  ".git"
1477
1651
  ]);
1478
1652
  async function createProjectFromTemplate(options) {
1479
- const targetDir = import_node_path8.default.resolve(options.targetDir);
1480
- const templateDir = import_node_path8.default.resolve(options.templateDir);
1653
+ const targetDir = import_node_path10.default.resolve(options.targetDir);
1654
+ const templateDir = import_node_path10.default.resolve(options.templateDir);
1481
1655
  try {
1482
- await (0, import_promises6.stat)(templateDir);
1656
+ await (0, import_promises8.stat)(templateDir);
1483
1657
  } catch {
1484
1658
  throw new CaatingaError(
1485
1659
  `Template directory was not found: ${templateDir}`,
@@ -1488,8 +1662,8 @@ async function createProjectFromTemplate(options) {
1488
1662
  );
1489
1663
  }
1490
1664
  const manifest = await readTemplateManifest(templateDir);
1491
- await (0, import_promises6.mkdir)(targetDir, { recursive: true });
1492
- await (0, import_promises6.cp)(templateDir, targetDir, {
1665
+ await (0, import_promises8.mkdir)(targetDir, { recursive: true });
1666
+ await (0, import_promises8.cp)(templateDir, targetDir, {
1493
1667
  recursive: true,
1494
1668
  force: false,
1495
1669
  errorOnExist: true,
@@ -1512,9 +1686,9 @@ async function ensureArtifacts(targetDir, projectName) {
1512
1686
  }
1513
1687
  }
1514
1688
  async function readTemplateManifest(templateDir) {
1515
- const manifestPath = import_node_path8.default.join(templateDir, "caatinga.template.json");
1689
+ const manifestPath = import_node_path10.default.join(templateDir, "caatinga.template.json");
1516
1690
  try {
1517
- const rawManifest = await (0, import_promises6.readFile)(manifestPath, "utf8");
1691
+ const rawManifest = await (0, import_promises8.readFile)(manifestPath, "utf8");
1518
1692
  const manifest = TemplateManifestSchema.parse(JSON.parse(rawManifest));
1519
1693
  const compatibilityIssue = getTemplateCompatibilityIssue(manifest);
1520
1694
  if (compatibilityIssue) {
@@ -1536,7 +1710,7 @@ async function readTemplateManifest(templateDir) {
1536
1710
  if (error instanceof CaatingaError) {
1537
1711
  throw error;
1538
1712
  }
1539
- if (error instanceof SyntaxError || error instanceof import_zod6.z.ZodError) {
1713
+ if (error instanceof SyntaxError || error instanceof import_zod7.z.ZodError) {
1540
1714
  throw new CaatingaError(
1541
1715
  "Template manifest is invalid.",
1542
1716
  CaatingaErrorCode.INVALID_TEMPLATE_MANIFEST,
@@ -1547,10 +1721,10 @@ async function readTemplateManifest(templateDir) {
1547
1721
  }
1548
1722
  }
1549
1723
  async function replaceTemplateVariables(dir, projectName) {
1550
- const entries = await (0, import_promises6.readdir)(dir);
1724
+ const entries = await (0, import_promises8.readdir)(dir);
1551
1725
  await Promise.all(entries.map(async (entry) => {
1552
- const entryPath = import_node_path8.default.join(dir, entry);
1553
- const entryStat = await (0, import_promises6.stat)(entryPath);
1726
+ const entryPath = import_node_path10.default.join(dir, entry);
1727
+ const entryStat = await (0, import_promises8.stat)(entryPath);
1554
1728
  if (entryStat.isDirectory()) {
1555
1729
  await replaceTemplateVariables(entryPath, projectName);
1556
1730
  return;
@@ -1558,16 +1732,16 @@ async function replaceTemplateVariables(dir, projectName) {
1558
1732
  if (!isTextTemplateFile(entryPath)) {
1559
1733
  return;
1560
1734
  }
1561
- const content = await (0, import_promises6.readFile)(entryPath, "utf8");
1562
- await (0, import_promises6.writeFile)(entryPath, content.replaceAll("__PROJECT_NAME__", projectName), "utf8");
1735
+ const content = await (0, import_promises8.readFile)(entryPath, "utf8");
1736
+ await (0, import_promises8.writeFile)(entryPath, content.replaceAll("__PROJECT_NAME__", projectName), "utf8");
1563
1737
  }));
1564
1738
  }
1565
1739
  function shouldCopyTemplateEntry(templateDir, source) {
1566
- const relativePath = import_node_path8.default.relative(templateDir, source);
1740
+ const relativePath = import_node_path10.default.relative(templateDir, source);
1567
1741
  if (!relativePath || relativePath === ".") {
1568
1742
  return true;
1569
1743
  }
1570
- return !relativePath.split(import_node_path8.default.sep).some((segment) => TEMPLATE_COPY_EXCLUDED_DIRS.has(segment));
1744
+ return !relativePath.split(import_node_path10.default.sep).some((segment) => TEMPLATE_COPY_EXCLUDED_DIRS.has(segment));
1571
1745
  }
1572
1746
  function isTextTemplateFile(filePath) {
1573
1747
  return [
@@ -1579,7 +1753,7 @@ function isTextTemplateFile(filePath) {
1579
1753
  ".tsx",
1580
1754
  ".css",
1581
1755
  ".html"
1582
- ].includes(import_node_path8.default.extname(filePath));
1756
+ ].includes(import_node_path10.default.extname(filePath));
1583
1757
  }
1584
1758
 
1585
1759
  // src/ci/is-transient-testnet-smoke-failure.ts
@@ -1604,6 +1778,8 @@ function isTransientTestnetSmokeFailure(logText) {
1604
1778
  }
1605
1779
  // Annotate the CommonJS export names for ESM import in node:
1606
1780
  0 && (module.exports = {
1781
+ BINDING_MARKER_FILENAME,
1782
+ BindingMarkerSchema,
1607
1783
  CAATINGA_CORE_VERSION,
1608
1784
  CaatingaArtifactsSchema,
1609
1785
  CaatingaConfigSchema,
@@ -1617,11 +1793,14 @@ function isTransientTestnetSmokeFailure(logText) {
1617
1793
  buildDependencyGraph,
1618
1794
  checkBinary,
1619
1795
  checkStellarCliVersion,
1796
+ collectProjectStatus,
1620
1797
  createInitialArtifacts,
1621
1798
  createProjectFromTemplate,
1622
1799
  defineConfig,
1623
1800
  deployContract,
1624
1801
  deployContractGraph,
1802
+ evaluateBindingFreshness,
1803
+ evaluateBindingsFreshness,
1625
1804
  evaluateStellarCliCompatibility,
1626
1805
  formatCaatingaError,
1627
1806
  generateBindings,
@@ -1633,6 +1812,7 @@ function isTransientTestnetSmokeFailure(logText) {
1633
1812
  parseInvokeTarget,
1634
1813
  parseStellarCliVersion,
1635
1814
  readArtifacts,
1815
+ readBindingMarker,
1636
1816
  resolveContract,
1637
1817
  resolveDeployArgs,
1638
1818
  resolveDeployOrder,
@@ -1641,5 +1821,6 @@ function isTransientTestnetSmokeFailure(logText) {
1641
1821
  toCaatingaError,
1642
1822
  updateArtifact,
1643
1823
  validateSourceShape,
1644
- writeArtifacts
1824
+ writeArtifacts,
1825
+ writeBindingMarker
1645
1826
  });
package/dist/index.d.cts CHANGED
@@ -2,7 +2,7 @@ import { C as CaatingaArtifacts, a as ContractArtifact, b as CaatingaErrorCodeVa
2
2
  export { d as CaatingaArtifactsSchema, e as CaatingaErrorCode, f as formatCaatingaError, t as toCaatingaError } from './browser-Cq4ZofIq.cjs';
3
3
  import { z } from 'zod';
4
4
 
5
- declare const CAATINGA_CORE_VERSION = "2.1.0";
5
+ declare const CAATINGA_CORE_VERSION = "2.2.0";
6
6
 
7
7
  declare const ContractConfigSchema: z.ZodObject<{
8
8
  path: z.ZodString;
@@ -144,6 +144,78 @@ declare function updateArtifact(artifacts: CaatingaArtifacts, networkName: strin
144
144
  dependencyGraph?: Record<string, string[]>;
145
145
  }): CaatingaArtifacts;
146
146
 
147
+ declare const BINDING_MARKER_FILENAME = ".caatinga-bindings.json";
148
+ declare const BindingMarkerSchema: z.ZodObject<{
149
+ version: z.ZodLiteral<1>;
150
+ contractId: z.ZodString;
151
+ wasmHash: z.ZodString;
152
+ network: z.ZodString;
153
+ generatedAt: z.ZodString;
154
+ }, "strip", z.ZodTypeAny, {
155
+ contractId: string;
156
+ wasmHash: string;
157
+ version: 1;
158
+ network: string;
159
+ generatedAt: string;
160
+ }, {
161
+ contractId: string;
162
+ wasmHash: string;
163
+ version: 1;
164
+ network: string;
165
+ generatedAt: string;
166
+ }>;
167
+ type BindingMarker = z.infer<typeof BindingMarkerSchema>;
168
+ declare function writeBindingMarker(outputDir: string, marker: BindingMarker): Promise<void>;
169
+ /** Returns null when the marker is absent or unreadable — freshness degrades, never throws. */
170
+ declare function readBindingMarker(outputDir: string): Promise<BindingMarker | null>;
171
+
172
+ type BindingFreshnessStatus = "fresh" | "stale" | "missing" | "unknown";
173
+ type BindingFreshness = {
174
+ contractName: string;
175
+ status: BindingFreshnessStatus;
176
+ outputDir: string;
177
+ marker: BindingMarker | null;
178
+ reason?: string;
179
+ };
180
+ type EvaluateBindingFreshnessOptions = {
181
+ config: CaatingaConfig;
182
+ artifacts: CaatingaArtifacts;
183
+ networkName: string;
184
+ contractName: string;
185
+ cwd?: string;
186
+ };
187
+ declare function evaluateBindingFreshness(options: EvaluateBindingFreshnessOptions): Promise<BindingFreshness>;
188
+ declare function evaluateBindingsFreshness(options: {
189
+ config: CaatingaConfig;
190
+ artifacts: CaatingaArtifacts;
191
+ networkName: string;
192
+ cwd?: string;
193
+ }): Promise<BindingFreshness[]>;
194
+
195
+ type ContractStatusEntry = {
196
+ name: string;
197
+ deployed: boolean;
198
+ contractId?: string;
199
+ wasmHash?: string;
200
+ deployedAt?: string;
201
+ dependencies: string[];
202
+ bindings: BindingFreshness;
203
+ };
204
+ type NetworkStatus = {
205
+ network: string;
206
+ contracts: ContractStatusEntry[];
207
+ };
208
+ type ProjectStatus = {
209
+ project: string;
210
+ networks: NetworkStatus[];
211
+ };
212
+ type CollectProjectStatusOptions = {
213
+ config: CaatingaConfig;
214
+ networkName?: string;
215
+ cwd?: string;
216
+ };
217
+ declare function collectProjectStatus(options: CollectProjectStatusOptions): Promise<ProjectStatus>;
218
+
147
219
  declare const WELL_KNOWN_NETWORKS: Record<string, NetworkConfig>;
148
220
 
149
221
  type ResolvedNetwork = {
@@ -327,6 +399,13 @@ declare function generateBindings(options: GenerateBindingsOptions): Promise<{
327
399
  outputDir: string;
328
400
  importPath: string;
329
401
  legacyStubRemoved: boolean;
402
+ marker: {
403
+ contractId: string;
404
+ wasmHash: string;
405
+ version: 1;
406
+ network: string;
407
+ generatedAt: string;
408
+ };
330
409
  output: string;
331
410
  }>;
332
411
 
@@ -337,6 +416,7 @@ type GenerateBindingsGraphResult = {
337
416
  declare function generateBindingsGraph(options: {
338
417
  config: CaatingaConfig;
339
418
  contractName?: string;
419
+ contractNames?: string[];
340
420
  networkName?: string;
341
421
  cwd?: string;
342
422
  }): Promise<GenerateBindingsGraphResult>;
@@ -479,4 +559,4 @@ type TemplateManifest = z.infer<typeof TemplateManifestSchema>;
479
559
 
480
560
  declare function isTransientTestnetSmokeFailure(logText: string): boolean;
481
561
 
482
- export { type BuildContractOptions, CAATINGA_CORE_VERSION, CaatingaArtifacts, type CaatingaConfig, CaatingaConfigSchema, CaatingaError, type CheckStellarCliVersionOptions, type CompatibilityReport, type CompatibilityStatus, type CompatibilityWarning, type CompatibilityWarningCode, ContractArtifact, type ContractConfig, type CreateInitialArtifactsOptions, type CreateProjectFromTemplateOptions, type DeployArgValue, type DeployContractGraphResult, type DeployContractOptions, type EvaluateStellarCliCompatibilityInput, type GenerateBindingsGraphResult, type GenerateBindingsOptions, type InvokeContractOptions, type InvokeTarget, type LoadConfigOptions, type NetworkConfig, type ResolvedContract, type ResolvedNetwork, type RunCommandResult, STELLAR_CLI_LAST_TESTED_VERSION, STELLAR_CLI_MIN_VERSION, type SkippedContract, type TemplateManifest, TemplateManifestSchema, WELL_KNOWN_NETWORKS, buildContract, buildDependencyGraph, checkBinary, checkStellarCliVersion, createInitialArtifacts, createProjectFromTemplate, defineConfig, deployContract, deployContractGraph, evaluateStellarCliCompatibility, generateBindings, generateBindingsGraph, invokeContract, isTransientTestnetSmokeFailure, loadConfig, parseContractId, parseInvokeTarget, parseStellarCliVersion, readArtifacts, resolveContract, resolveDeployArgs, resolveDeployOrder, resolveNetwork, runCommand, updateArtifact, validateSourceShape, writeArtifacts };
562
+ export { BINDING_MARKER_FILENAME, type BindingFreshness, type BindingFreshnessStatus, type BindingMarker, BindingMarkerSchema, type BuildContractOptions, CAATINGA_CORE_VERSION, CaatingaArtifacts, type CaatingaConfig, CaatingaConfigSchema, CaatingaError, type CheckStellarCliVersionOptions, type CollectProjectStatusOptions, type CompatibilityReport, type CompatibilityStatus, type CompatibilityWarning, type CompatibilityWarningCode, ContractArtifact, type ContractConfig, type ContractStatusEntry, type CreateInitialArtifactsOptions, type CreateProjectFromTemplateOptions, type DeployArgValue, type DeployContractGraphResult, type DeployContractOptions, type EvaluateBindingFreshnessOptions, type EvaluateStellarCliCompatibilityInput, type GenerateBindingsGraphResult, type GenerateBindingsOptions, type InvokeContractOptions, type InvokeTarget, type LoadConfigOptions, type NetworkConfig, type NetworkStatus, type ProjectStatus, type ResolvedContract, type ResolvedNetwork, type RunCommandResult, STELLAR_CLI_LAST_TESTED_VERSION, STELLAR_CLI_MIN_VERSION, type SkippedContract, type TemplateManifest, TemplateManifestSchema, WELL_KNOWN_NETWORKS, buildContract, buildDependencyGraph, checkBinary, checkStellarCliVersion, collectProjectStatus, createInitialArtifacts, createProjectFromTemplate, defineConfig, deployContract, deployContractGraph, evaluateBindingFreshness, evaluateBindingsFreshness, evaluateStellarCliCompatibility, generateBindings, generateBindingsGraph, invokeContract, isTransientTestnetSmokeFailure, loadConfig, parseContractId, parseInvokeTarget, parseStellarCliVersion, readArtifacts, readBindingMarker, resolveContract, resolveDeployArgs, resolveDeployOrder, resolveNetwork, runCommand, updateArtifact, validateSourceShape, writeArtifacts, writeBindingMarker };
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import { C as CaatingaArtifacts, a as ContractArtifact, b as CaatingaErrorCodeVa
2
2
  export { d as CaatingaArtifactsSchema, e as CaatingaErrorCode, f as formatCaatingaError, t as toCaatingaError } from './browser-Cq4ZofIq.js';
3
3
  import { z } from 'zod';
4
4
 
5
- declare const CAATINGA_CORE_VERSION = "2.1.0";
5
+ declare const CAATINGA_CORE_VERSION = "2.2.0";
6
6
 
7
7
  declare const ContractConfigSchema: z.ZodObject<{
8
8
  path: z.ZodString;
@@ -144,6 +144,78 @@ declare function updateArtifact(artifacts: CaatingaArtifacts, networkName: strin
144
144
  dependencyGraph?: Record<string, string[]>;
145
145
  }): CaatingaArtifacts;
146
146
 
147
+ declare const BINDING_MARKER_FILENAME = ".caatinga-bindings.json";
148
+ declare const BindingMarkerSchema: z.ZodObject<{
149
+ version: z.ZodLiteral<1>;
150
+ contractId: z.ZodString;
151
+ wasmHash: z.ZodString;
152
+ network: z.ZodString;
153
+ generatedAt: z.ZodString;
154
+ }, "strip", z.ZodTypeAny, {
155
+ contractId: string;
156
+ wasmHash: string;
157
+ version: 1;
158
+ network: string;
159
+ generatedAt: string;
160
+ }, {
161
+ contractId: string;
162
+ wasmHash: string;
163
+ version: 1;
164
+ network: string;
165
+ generatedAt: string;
166
+ }>;
167
+ type BindingMarker = z.infer<typeof BindingMarkerSchema>;
168
+ declare function writeBindingMarker(outputDir: string, marker: BindingMarker): Promise<void>;
169
+ /** Returns null when the marker is absent or unreadable — freshness degrades, never throws. */
170
+ declare function readBindingMarker(outputDir: string): Promise<BindingMarker | null>;
171
+
172
+ type BindingFreshnessStatus = "fresh" | "stale" | "missing" | "unknown";
173
+ type BindingFreshness = {
174
+ contractName: string;
175
+ status: BindingFreshnessStatus;
176
+ outputDir: string;
177
+ marker: BindingMarker | null;
178
+ reason?: string;
179
+ };
180
+ type EvaluateBindingFreshnessOptions = {
181
+ config: CaatingaConfig;
182
+ artifacts: CaatingaArtifacts;
183
+ networkName: string;
184
+ contractName: string;
185
+ cwd?: string;
186
+ };
187
+ declare function evaluateBindingFreshness(options: EvaluateBindingFreshnessOptions): Promise<BindingFreshness>;
188
+ declare function evaluateBindingsFreshness(options: {
189
+ config: CaatingaConfig;
190
+ artifacts: CaatingaArtifacts;
191
+ networkName: string;
192
+ cwd?: string;
193
+ }): Promise<BindingFreshness[]>;
194
+
195
+ type ContractStatusEntry = {
196
+ name: string;
197
+ deployed: boolean;
198
+ contractId?: string;
199
+ wasmHash?: string;
200
+ deployedAt?: string;
201
+ dependencies: string[];
202
+ bindings: BindingFreshness;
203
+ };
204
+ type NetworkStatus = {
205
+ network: string;
206
+ contracts: ContractStatusEntry[];
207
+ };
208
+ type ProjectStatus = {
209
+ project: string;
210
+ networks: NetworkStatus[];
211
+ };
212
+ type CollectProjectStatusOptions = {
213
+ config: CaatingaConfig;
214
+ networkName?: string;
215
+ cwd?: string;
216
+ };
217
+ declare function collectProjectStatus(options: CollectProjectStatusOptions): Promise<ProjectStatus>;
218
+
147
219
  declare const WELL_KNOWN_NETWORKS: Record<string, NetworkConfig>;
148
220
 
149
221
  type ResolvedNetwork = {
@@ -327,6 +399,13 @@ declare function generateBindings(options: GenerateBindingsOptions): Promise<{
327
399
  outputDir: string;
328
400
  importPath: string;
329
401
  legacyStubRemoved: boolean;
402
+ marker: {
403
+ contractId: string;
404
+ wasmHash: string;
405
+ version: 1;
406
+ network: string;
407
+ generatedAt: string;
408
+ };
330
409
  output: string;
331
410
  }>;
332
411
 
@@ -337,6 +416,7 @@ type GenerateBindingsGraphResult = {
337
416
  declare function generateBindingsGraph(options: {
338
417
  config: CaatingaConfig;
339
418
  contractName?: string;
419
+ contractNames?: string[];
340
420
  networkName?: string;
341
421
  cwd?: string;
342
422
  }): Promise<GenerateBindingsGraphResult>;
@@ -479,4 +559,4 @@ type TemplateManifest = z.infer<typeof TemplateManifestSchema>;
479
559
 
480
560
  declare function isTransientTestnetSmokeFailure(logText: string): boolean;
481
561
 
482
- export { type BuildContractOptions, CAATINGA_CORE_VERSION, CaatingaArtifacts, type CaatingaConfig, CaatingaConfigSchema, CaatingaError, type CheckStellarCliVersionOptions, type CompatibilityReport, type CompatibilityStatus, type CompatibilityWarning, type CompatibilityWarningCode, ContractArtifact, type ContractConfig, type CreateInitialArtifactsOptions, type CreateProjectFromTemplateOptions, type DeployArgValue, type DeployContractGraphResult, type DeployContractOptions, type EvaluateStellarCliCompatibilityInput, type GenerateBindingsGraphResult, type GenerateBindingsOptions, type InvokeContractOptions, type InvokeTarget, type LoadConfigOptions, type NetworkConfig, type ResolvedContract, type ResolvedNetwork, type RunCommandResult, STELLAR_CLI_LAST_TESTED_VERSION, STELLAR_CLI_MIN_VERSION, type SkippedContract, type TemplateManifest, TemplateManifestSchema, WELL_KNOWN_NETWORKS, buildContract, buildDependencyGraph, checkBinary, checkStellarCliVersion, createInitialArtifacts, createProjectFromTemplate, defineConfig, deployContract, deployContractGraph, evaluateStellarCliCompatibility, generateBindings, generateBindingsGraph, invokeContract, isTransientTestnetSmokeFailure, loadConfig, parseContractId, parseInvokeTarget, parseStellarCliVersion, readArtifacts, resolveContract, resolveDeployArgs, resolveDeployOrder, resolveNetwork, runCommand, updateArtifact, validateSourceShape, writeArtifacts };
562
+ export { BINDING_MARKER_FILENAME, type BindingFreshness, type BindingFreshnessStatus, type BindingMarker, BindingMarkerSchema, type BuildContractOptions, CAATINGA_CORE_VERSION, CaatingaArtifacts, type CaatingaConfig, CaatingaConfigSchema, CaatingaError, type CheckStellarCliVersionOptions, type CollectProjectStatusOptions, type CompatibilityReport, type CompatibilityStatus, type CompatibilityWarning, type CompatibilityWarningCode, ContractArtifact, type ContractConfig, type ContractStatusEntry, type CreateInitialArtifactsOptions, type CreateProjectFromTemplateOptions, type DeployArgValue, type DeployContractGraphResult, type DeployContractOptions, type EvaluateBindingFreshnessOptions, type EvaluateStellarCliCompatibilityInput, type GenerateBindingsGraphResult, type GenerateBindingsOptions, type InvokeContractOptions, type InvokeTarget, type LoadConfigOptions, type NetworkConfig, type NetworkStatus, type ProjectStatus, type ResolvedContract, type ResolvedNetwork, type RunCommandResult, STELLAR_CLI_LAST_TESTED_VERSION, STELLAR_CLI_MIN_VERSION, type SkippedContract, type TemplateManifest, TemplateManifestSchema, WELL_KNOWN_NETWORKS, buildContract, buildDependencyGraph, checkBinary, checkStellarCliVersion, collectProjectStatus, createInitialArtifacts, createProjectFromTemplate, defineConfig, deployContract, deployContractGraph, evaluateBindingFreshness, evaluateBindingsFreshness, evaluateStellarCliCompatibility, generateBindings, generateBindingsGraph, invokeContract, isTransientTestnetSmokeFailure, loadConfig, parseContractId, parseInvokeTarget, parseStellarCliVersion, readArtifacts, readBindingMarker, resolveContract, resolveDeployArgs, resolveDeployOrder, resolveNetwork, runCommand, updateArtifact, validateSourceShape, writeArtifacts, writeBindingMarker };
package/dist/index.js CHANGED
@@ -105,7 +105,7 @@ function formatCause(cause) {
105
105
  }
106
106
 
107
107
  // src/version.ts
108
- var CAATINGA_CORE_VERSION = "2.1.0";
108
+ var CAATINGA_CORE_VERSION = "2.2.0";
109
109
 
110
110
  // src/config/config.schema.ts
111
111
  import { z } from "zod";
@@ -270,18 +270,6 @@ function updateArtifact(artifacts, networkName, contractName, contractArtifact,
270
270
  };
271
271
  }
272
272
 
273
- // src/networks/networks.ts
274
- var WELL_KNOWN_NETWORKS = {
275
- testnet: {
276
- rpcUrl: "https://soroban-testnet.stellar.org",
277
- networkPassphrase: "Test SDF Network ; September 2015"
278
- },
279
- mainnet: {
280
- rpcUrl: "https://mainnet.sorobanrpc.com",
281
- networkPassphrase: "Public Global Stellar Network ; September 2015"
282
- }
283
- };
284
-
285
273
  // src/networks/resolve-network.ts
286
274
  function resolveNetwork(config, networkName) {
287
275
  const name = networkName ?? config.defaultNetwork;
@@ -296,6 +284,174 @@ function resolveNetwork(config, networkName) {
296
284
  return { name, config: network };
297
285
  }
298
286
 
287
+ // src/bindings/binding-freshness.ts
288
+ import { readdir } from "fs/promises";
289
+ import path5 from "path";
290
+
291
+ // src/bindings/binding-marker.ts
292
+ import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
293
+ import path4 from "path";
294
+ import { z as z5 } from "zod";
295
+ var BINDING_MARKER_FILENAME = ".caatinga-bindings.json";
296
+ var BindingMarkerSchema = z5.object({
297
+ version: z5.literal(1),
298
+ contractId: z5.string().min(1),
299
+ wasmHash: z5.string().min(1),
300
+ network: z5.string().min(1),
301
+ generatedAt: z5.string().datetime()
302
+ });
303
+ async function writeBindingMarker(outputDir, marker) {
304
+ const markerPath = path4.join(outputDir, BINDING_MARKER_FILENAME);
305
+ await writeFile2(markerPath, `${JSON.stringify(marker, null, 2)}
306
+ `, "utf8");
307
+ }
308
+ async function readBindingMarker(outputDir) {
309
+ const markerPath = path4.join(outputDir, BINDING_MARKER_FILENAME);
310
+ let raw;
311
+ try {
312
+ raw = await readFile2(markerPath, "utf8");
313
+ } catch {
314
+ return null;
315
+ }
316
+ try {
317
+ return BindingMarkerSchema.parse(JSON.parse(raw));
318
+ } catch {
319
+ return null;
320
+ }
321
+ }
322
+
323
+ // src/bindings/binding-freshness.ts
324
+ async function listGeneratedEntries(outputDir) {
325
+ try {
326
+ const entries = await readdir(outputDir);
327
+ return entries.filter((entry) => entry !== BINDING_MARKER_FILENAME);
328
+ } catch {
329
+ return null;
330
+ }
331
+ }
332
+ async function evaluateBindingFreshness(options) {
333
+ const cwd = options.cwd ?? process.cwd();
334
+ const outputDir = path5.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
335
+ const contractArtifact = options.artifacts.networks[options.networkName]?.contracts[options.contractName];
336
+ if (!contractArtifact) {
337
+ return {
338
+ contractName: options.contractName,
339
+ status: "missing",
340
+ outputDir,
341
+ marker: null,
342
+ reason: `not deployed on "${options.networkName}"`
343
+ };
344
+ }
345
+ const generatedEntries = await listGeneratedEntries(outputDir);
346
+ if (generatedEntries === null || generatedEntries.length === 0) {
347
+ return {
348
+ contractName: options.contractName,
349
+ status: "missing",
350
+ outputDir,
351
+ marker: null,
352
+ reason: "no generated bindings found"
353
+ };
354
+ }
355
+ const marker = await readBindingMarker(outputDir);
356
+ if (!marker) {
357
+ return {
358
+ contractName: options.contractName,
359
+ status: "unknown",
360
+ outputDir,
361
+ marker: null,
362
+ reason: "bindings exist but have no provenance marker \u2014 rerun caatinga generate to record it"
363
+ };
364
+ }
365
+ if (marker.contractId !== contractArtifact.contractId) {
366
+ return {
367
+ contractName: options.contractName,
368
+ status: "stale",
369
+ outputDir,
370
+ marker,
371
+ reason: "contractId changed since last generate"
372
+ };
373
+ }
374
+ if (marker.wasmHash !== contractArtifact.wasmHash) {
375
+ return {
376
+ contractName: options.contractName,
377
+ status: "stale",
378
+ outputDir,
379
+ marker,
380
+ reason: "wasmHash changed since last generate"
381
+ };
382
+ }
383
+ return {
384
+ contractName: options.contractName,
385
+ status: "fresh",
386
+ outputDir,
387
+ marker
388
+ };
389
+ }
390
+ async function evaluateBindingsFreshness(options) {
391
+ const contractNames = Object.keys(
392
+ options.artifacts.networks[options.networkName]?.contracts ?? {}
393
+ );
394
+ const results = [];
395
+ for (const contractName of contractNames) {
396
+ results.push(await evaluateBindingFreshness({ ...options, contractName }));
397
+ }
398
+ return results;
399
+ }
400
+
401
+ // src/artifacts/project-status.ts
402
+ async function collectProjectStatus(options) {
403
+ const cwd = options.cwd ?? process.cwd();
404
+ const artifacts = await readArtifacts(cwd);
405
+ let networkNames;
406
+ if (options.networkName) {
407
+ networkNames = [resolveNetwork(options.config, options.networkName).name];
408
+ } else {
409
+ const fromArtifacts = Object.keys(artifacts.networks);
410
+ const fallback = options.config.defaultNetwork ?? "testnet";
411
+ networkNames = fromArtifacts.length > 0 ? fromArtifacts : [fallback];
412
+ if (!networkNames.includes(fallback) && options.config.networks[fallback]) {
413
+ networkNames.push(fallback);
414
+ }
415
+ }
416
+ const networks = [];
417
+ for (const networkName of networkNames) {
418
+ const contracts = [];
419
+ for (const name of Object.keys(options.config.contracts)) {
420
+ const artifact = artifacts.networks[networkName]?.contracts[name];
421
+ const bindings = await evaluateBindingFreshness({
422
+ config: options.config,
423
+ artifacts,
424
+ networkName,
425
+ contractName: name,
426
+ cwd
427
+ });
428
+ contracts.push({
429
+ name,
430
+ deployed: Boolean(artifact),
431
+ contractId: artifact?.contractId,
432
+ wasmHash: artifact?.wasmHash,
433
+ deployedAt: artifact?.deployedAt,
434
+ dependencies: artifact?.dependencies ?? options.config.contracts[name].dependsOn ?? [],
435
+ bindings
436
+ });
437
+ }
438
+ networks.push({ network: networkName, contracts });
439
+ }
440
+ return { project: options.config.project, networks };
441
+ }
442
+
443
+ // src/networks/networks.ts
444
+ var WELL_KNOWN_NETWORKS = {
445
+ testnet: {
446
+ rpcUrl: "https://soroban-testnet.stellar.org",
447
+ networkPassphrase: "Test SDF Network ; September 2015"
448
+ },
449
+ mainnet: {
450
+ rpcUrl: "https://mainnet.sorobanrpc.com",
451
+ networkPassphrase: "Public Global Stellar Network ; September 2015"
452
+ }
453
+ };
454
+
299
455
  // src/shell/run-command.ts
300
456
  import { execa } from "execa";
301
457
 
@@ -617,7 +773,7 @@ function validateSourceShape(source) {
617
773
  }
618
774
 
619
775
  // src/contracts/resolve-contract.ts
620
- import path4 from "path";
776
+ import path6 from "path";
621
777
  function resolveContract(config, contractName, cwd = process.cwd()) {
622
778
  const contract = config.contracts[contractName];
623
779
  if (!contract) {
@@ -630,15 +786,15 @@ function resolveContract(config, contractName, cwd = process.cwd()) {
630
786
  return {
631
787
  name: contractName,
632
788
  config: contract,
633
- sourcePath: path4.resolve(cwd, contract.path),
634
- wasmPath: path4.resolve(cwd, contract.wasm)
789
+ sourcePath: path6.resolve(cwd, contract.path),
790
+ wasmPath: path6.resolve(cwd, contract.wasm)
635
791
  };
636
792
  }
637
793
 
638
794
  // src/contracts/wasm.ts
639
795
  import { createHash } from "crypto";
640
- import { access as access2, readdir, readFile as readFile2, stat } from "fs/promises";
641
- import path5 from "path";
796
+ import { access as access2, readdir as readdir2, readFile as readFile3, stat } from "fs/promises";
797
+ import path7 from "path";
642
798
  var LEGACY_RUST_WASM_TARGET = "wasm32-unknown-unknown";
643
799
  var CURRENT_RUST_WASM_TARGET = "wasm32v1-none";
644
800
  function toCurrentWasmTargetPath(wasmPath) {
@@ -661,8 +817,8 @@ function wasmNotFoundError(configuredWasmPath, options) {
661
817
  );
662
818
  }
663
819
  function toConfigRelativeWasmPath(absoluteWasmPath) {
664
- const relative = path5.relative(process.cwd(), absoluteWasmPath);
665
- return relative.startsWith("..") ? absoluteWasmPath : `./${relative.split(path5.sep).join("/")}`;
820
+ const relative = path7.relative(process.cwd(), absoluteWasmPath);
821
+ return relative.startsWith("..") ? absoluteWasmPath : `./${relative.split(path7.sep).join("/")}`;
666
822
  }
667
823
  async function resolveWasmArtifactPath(configuredWasmPath) {
668
824
  try {
@@ -682,7 +838,7 @@ async function resolveWasmArtifactPath(configuredWasmPath) {
682
838
  }
683
839
  }
684
840
  async function hashWasm(wasmPath) {
685
- const bytes = await readFile2(wasmPath);
841
+ const bytes = await readFile3(wasmPath);
686
842
  return createHash("sha256").update(bytes).digest("hex");
687
843
  }
688
844
  async function getNewestMtimeInDirectory(directory) {
@@ -693,9 +849,9 @@ async function getNewestMtimeInDirectory(directory) {
693
849
  }
694
850
  let newest = 0;
695
851
  async function walk(dir) {
696
- const entries = await readdir(dir, { withFileTypes: true });
852
+ const entries = await readdir2(dir, { withFileTypes: true });
697
853
  for (const entry of entries) {
698
- const entryPath = path5.join(dir, entry.name);
854
+ const entryPath = path7.join(dir, entry.name);
699
855
  if (entry.isDirectory()) {
700
856
  await walk(entryPath);
701
857
  continue;
@@ -711,7 +867,7 @@ async function getNewestMtimeInDirectory(directory) {
711
867
  return newest > 0 ? newest : void 0;
712
868
  }
713
869
  async function isWasmOlderThanSources(input) {
714
- const srcDir = path5.join(input.contractPath, "src");
870
+ const srcDir = path7.join(input.contractPath, "src");
715
871
  const newestSourceMtime = await getNewestMtimeInDirectory(srcDir);
716
872
  if (newestSourceMtime === void 0) {
717
873
  return false;
@@ -784,7 +940,7 @@ async function buildContract(options) {
784
940
  }
785
941
 
786
942
  // src/contracts/deploy-contract.ts
787
- import path6 from "path";
943
+ import path8 from "path";
788
944
 
789
945
  // src/contracts/dependency-graph.ts
790
946
  function buildDependencyGraph(contracts) {
@@ -885,7 +1041,7 @@ async function deployContract(options) {
885
1041
  contract: contractWithWasm,
886
1042
  network,
887
1043
  contractId: existing.contractId,
888
- artifactsPath: path6.resolve(cwd, "caatinga.artifacts.json"),
1044
+ artifactsPath: path8.resolve(cwd, "caatinga.artifacts.json"),
889
1045
  output: "",
890
1046
  skipped: true,
891
1047
  staleWasmWarning
@@ -1162,13 +1318,13 @@ async function deployContractGraph(options) {
1162
1318
 
1163
1319
  // src/contracts/generate-bindings.ts
1164
1320
  import { access as access3, mkdir as mkdir2, unlink } from "fs/promises";
1165
- import path7 from "path";
1321
+ import path9 from "path";
1166
1322
  function toBindingImportPath(bindingsOutput, contractName) {
1167
- const normalized = bindingsOutput.replace(/^\.\//, "").split(path7.sep).join("/");
1168
- return `./${path7.posix.join(normalized, contractName, "src", "index.js")}`;
1323
+ const normalized = bindingsOutput.replace(/^\.\//, "").split(path9.sep).join("/");
1324
+ return `./${path9.posix.join(normalized, contractName, "src", "index.js")}`;
1169
1325
  }
1170
1326
  async function removeLegacyBindingStub(cwd, bindingsOutput, contractName) {
1171
- const legacyPath = path7.resolve(cwd, bindingsOutput, `${contractName}.ts`);
1327
+ const legacyPath = path9.resolve(cwd, bindingsOutput, `${contractName}.ts`);
1172
1328
  try {
1173
1329
  await access3(legacyPath);
1174
1330
  await unlink(legacyPath);
@@ -1190,7 +1346,7 @@ async function generateBindings(options) {
1190
1346
  );
1191
1347
  }
1192
1348
  await checkBinary("stellar", "Install Stellar CLI before running caatinga generate.");
1193
- const outputDir = path7.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
1349
+ const outputDir = path9.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
1194
1350
  await mkdir2(outputDir, { recursive: true });
1195
1351
  const result = await runCommand("stellar", [
1196
1352
  "contract",
@@ -1211,12 +1367,21 @@ async function generateBindings(options) {
1211
1367
  options.config.frontend.bindingsOutput,
1212
1368
  options.contractName
1213
1369
  );
1370
+ const marker = {
1371
+ version: 1,
1372
+ contractId: contractArtifact.contractId,
1373
+ wasmHash: contractArtifact.wasmHash,
1374
+ network: network.name,
1375
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
1376
+ };
1377
+ await writeBindingMarker(outputDir, marker);
1214
1378
  return {
1215
1379
  contractName: options.contractName,
1216
1380
  network,
1217
1381
  outputDir,
1218
1382
  importPath: toBindingImportPath(options.config.frontend.bindingsOutput, options.contractName),
1219
1383
  legacyStubRemoved,
1384
+ marker,
1220
1385
  output: result.all || result.stdout
1221
1386
  };
1222
1387
  }
@@ -1228,6 +1393,8 @@ async function generateBindingsGraph(options) {
1228
1393
  let targets;
1229
1394
  if (options.contractName) {
1230
1395
  targets = [options.contractName];
1396
+ } else if (options.contractNames && options.contractNames.length > 0) {
1397
+ targets = options.contractNames;
1231
1398
  } else {
1232
1399
  const artifacts = await readArtifacts(cwd);
1233
1400
  targets = Object.keys(artifacts.networks[network.name]?.contracts ?? {});
@@ -1325,33 +1492,33 @@ ${error.hint ?? ""}`)) {
1325
1492
  }
1326
1493
 
1327
1494
  // src/templates/create-project-from-template.ts
1328
- import { cp, mkdir as mkdir3, readFile as readFile3, readdir as readdir2, stat as stat2, writeFile as writeFile2 } from "fs/promises";
1329
- import path8 from "path";
1330
- import { z as z6 } from "zod";
1495
+ import { cp, mkdir as mkdir3, readFile as readFile4, readdir as readdir3, stat as stat2, writeFile as writeFile3 } from "fs/promises";
1496
+ import path10 from "path";
1497
+ import { z as z7 } from "zod";
1331
1498
 
1332
1499
  // src/templates/template-manifest.schema.ts
1333
- import { z as z5 } from "zod";
1500
+ import { z as z6 } from "zod";
1334
1501
  import semver3 from "semver";
1335
1502
  var CURRENT_TEMPLATE_VERSION = 1;
1336
- var TemplateManifestSchema = z5.object({
1337
- name: z5.string().min(1),
1338
- version: z5.string().min(1),
1339
- description: z5.string().optional(),
1340
- caatinga: z5.object({
1341
- compatibleCore: z5.string().min(1),
1342
- templateVersion: z5.number().int().positive()
1503
+ var TemplateManifestSchema = z6.object({
1504
+ name: z6.string().min(1),
1505
+ version: z6.string().min(1),
1506
+ description: z6.string().optional(),
1507
+ caatinga: z6.object({
1508
+ compatibleCore: z6.string().min(1),
1509
+ templateVersion: z6.number().int().positive()
1343
1510
  }),
1344
- frontend: z5.object({
1345
- framework: z5.enum(["vite-react", "next", "astro"]),
1346
- packageManager: z5.enum(["npm", "pnpm", "yarn", "bun"]).default("npm")
1511
+ frontend: z6.object({
1512
+ framework: z6.enum(["vite-react", "next", "astro"]),
1513
+ packageManager: z6.enum(["npm", "pnpm", "yarn", "bun"]).default("npm")
1347
1514
  }),
1348
- contracts: z5.object({
1349
- path: z5.string(),
1350
- default: z5.string().optional()
1515
+ contracts: z6.object({
1516
+ path: z6.string(),
1517
+ default: z6.string().optional()
1351
1518
  }),
1352
- files: z5.object({
1353
- config: z5.string().default("caatinga.config.ts"),
1354
- artifacts: z5.string().default("caatinga.artifacts.json")
1519
+ files: z6.object({
1520
+ config: z6.string().default("caatinga.config.ts"),
1521
+ artifacts: z6.string().default("caatinga.artifacts.json")
1355
1522
  })
1356
1523
  });
1357
1524
  function defaultCompatibleCoreRange(coreVersion = CAATINGA_CORE_VERSION) {
@@ -1402,8 +1569,8 @@ var TEMPLATE_COPY_EXCLUDED_DIRS = /* @__PURE__ */ new Set([
1402
1569
  ".git"
1403
1570
  ]);
1404
1571
  async function createProjectFromTemplate(options) {
1405
- const targetDir = path8.resolve(options.targetDir);
1406
- const templateDir = path8.resolve(options.templateDir);
1572
+ const targetDir = path10.resolve(options.targetDir);
1573
+ const templateDir = path10.resolve(options.templateDir);
1407
1574
  try {
1408
1575
  await stat2(templateDir);
1409
1576
  } catch {
@@ -1438,9 +1605,9 @@ async function ensureArtifacts(targetDir, projectName) {
1438
1605
  }
1439
1606
  }
1440
1607
  async function readTemplateManifest(templateDir) {
1441
- const manifestPath = path8.join(templateDir, "caatinga.template.json");
1608
+ const manifestPath = path10.join(templateDir, "caatinga.template.json");
1442
1609
  try {
1443
- const rawManifest = await readFile3(manifestPath, "utf8");
1610
+ const rawManifest = await readFile4(manifestPath, "utf8");
1444
1611
  const manifest = TemplateManifestSchema.parse(JSON.parse(rawManifest));
1445
1612
  const compatibilityIssue = getTemplateCompatibilityIssue(manifest);
1446
1613
  if (compatibilityIssue) {
@@ -1462,7 +1629,7 @@ async function readTemplateManifest(templateDir) {
1462
1629
  if (error instanceof CaatingaError) {
1463
1630
  throw error;
1464
1631
  }
1465
- if (error instanceof SyntaxError || error instanceof z6.ZodError) {
1632
+ if (error instanceof SyntaxError || error instanceof z7.ZodError) {
1466
1633
  throw new CaatingaError(
1467
1634
  "Template manifest is invalid.",
1468
1635
  CaatingaErrorCode.INVALID_TEMPLATE_MANIFEST,
@@ -1473,9 +1640,9 @@ async function readTemplateManifest(templateDir) {
1473
1640
  }
1474
1641
  }
1475
1642
  async function replaceTemplateVariables(dir, projectName) {
1476
- const entries = await readdir2(dir);
1643
+ const entries = await readdir3(dir);
1477
1644
  await Promise.all(entries.map(async (entry) => {
1478
- const entryPath = path8.join(dir, entry);
1645
+ const entryPath = path10.join(dir, entry);
1479
1646
  const entryStat = await stat2(entryPath);
1480
1647
  if (entryStat.isDirectory()) {
1481
1648
  await replaceTemplateVariables(entryPath, projectName);
@@ -1484,16 +1651,16 @@ async function replaceTemplateVariables(dir, projectName) {
1484
1651
  if (!isTextTemplateFile(entryPath)) {
1485
1652
  return;
1486
1653
  }
1487
- const content = await readFile3(entryPath, "utf8");
1488
- await writeFile2(entryPath, content.replaceAll("__PROJECT_NAME__", projectName), "utf8");
1654
+ const content = await readFile4(entryPath, "utf8");
1655
+ await writeFile3(entryPath, content.replaceAll("__PROJECT_NAME__", projectName), "utf8");
1489
1656
  }));
1490
1657
  }
1491
1658
  function shouldCopyTemplateEntry(templateDir, source) {
1492
- const relativePath = path8.relative(templateDir, source);
1659
+ const relativePath = path10.relative(templateDir, source);
1493
1660
  if (!relativePath || relativePath === ".") {
1494
1661
  return true;
1495
1662
  }
1496
- return !relativePath.split(path8.sep).some((segment) => TEMPLATE_COPY_EXCLUDED_DIRS.has(segment));
1663
+ return !relativePath.split(path10.sep).some((segment) => TEMPLATE_COPY_EXCLUDED_DIRS.has(segment));
1497
1664
  }
1498
1665
  function isTextTemplateFile(filePath) {
1499
1666
  return [
@@ -1505,7 +1672,7 @@ function isTextTemplateFile(filePath) {
1505
1672
  ".tsx",
1506
1673
  ".css",
1507
1674
  ".html"
1508
- ].includes(path8.extname(filePath));
1675
+ ].includes(path10.extname(filePath));
1509
1676
  }
1510
1677
 
1511
1678
  // src/ci/is-transient-testnet-smoke-failure.ts
@@ -1529,6 +1696,8 @@ function isTransientTestnetSmokeFailure(logText) {
1529
1696
  return TRANSIENT_PATTERN.test(logText);
1530
1697
  }
1531
1698
  export {
1699
+ BINDING_MARKER_FILENAME,
1700
+ BindingMarkerSchema,
1532
1701
  CAATINGA_CORE_VERSION,
1533
1702
  CaatingaArtifactsSchema,
1534
1703
  CaatingaConfigSchema,
@@ -1542,11 +1711,14 @@ export {
1542
1711
  buildDependencyGraph,
1543
1712
  checkBinary,
1544
1713
  checkStellarCliVersion,
1714
+ collectProjectStatus,
1545
1715
  createInitialArtifacts,
1546
1716
  createProjectFromTemplate,
1547
1717
  defineConfig,
1548
1718
  deployContract,
1549
1719
  deployContractGraph,
1720
+ evaluateBindingFreshness,
1721
+ evaluateBindingsFreshness,
1550
1722
  evaluateStellarCliCompatibility,
1551
1723
  formatCaatingaError,
1552
1724
  generateBindings,
@@ -1558,6 +1730,7 @@ export {
1558
1730
  parseInvokeTarget,
1559
1731
  parseStellarCliVersion,
1560
1732
  readArtifacts,
1733
+ readBindingMarker,
1561
1734
  resolveContract,
1562
1735
  resolveDeployArgs,
1563
1736
  resolveDeployOrder,
@@ -1566,5 +1739,6 @@ export {
1566
1739
  toCaatingaError,
1567
1740
  updateArtifact,
1568
1741
  validateSourceShape,
1569
- writeArtifacts
1742
+ writeArtifacts,
1743
+ writeBindingMarker
1570
1744
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caatinga/core",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "Core config, artifacts, command orchestration, and error primitives for Caatinga/Soroban toolkit",
5
5
  "keywords": [
6
6
  "stellar",