@caatinga/core 2.0.2 → 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.js CHANGED
@@ -27,6 +27,7 @@ var CaatingaErrorCode = {
27
27
  DEPLOY_ARG_PLACEHOLDER_UNRESOLVED: "CAATINGA_DEPLOY_ARG_PLACEHOLDER_UNRESOLVED",
28
28
  BINDING_CLIENT_NOT_FOUND: "CAATINGA_BINDING_CLIENT_NOT_FOUND",
29
29
  BINDING_METHOD_NOT_FOUND: "CAATINGA_BINDING_METHOD_NOT_FOUND",
30
+ PLACEHOLDER_BINDING: "CAATINGA_PLACEHOLDER_BINDING",
30
31
  XDR_BUILD_FAILED: "CAATINGA_XDR_BUILD_FAILED",
31
32
  XDR_PREPARE_FAILED: "CAATINGA_XDR_PREPARE_FAILED",
32
33
  XDR_SIGN_FAILED: "CAATINGA_XDR_SIGN_FAILED",
@@ -71,8 +72,40 @@ function toCaatingaError(error) {
71
72
  return new CaatingaError("An unexpected error occurred.", CaatingaErrorCode.UNEXPECTED_ERROR);
72
73
  }
73
74
 
75
+ // src/errors/format-caatinga-error.ts
76
+ function formatCaatingaError(error) {
77
+ if (error instanceof CaatingaError) {
78
+ const lines = [`[${error.code}] ${error.message}`];
79
+ if (error.hint) {
80
+ lines.push("", error.hint);
81
+ }
82
+ const detail = formatCause(error.cause);
83
+ if (detail) {
84
+ lines.push("", `Details: ${detail}`);
85
+ }
86
+ return lines.join("\n");
87
+ }
88
+ return error instanceof Error ? error.message : String(error);
89
+ }
90
+ function formatCause(cause) {
91
+ if (cause === void 0 || cause === null) {
92
+ return "";
93
+ }
94
+ if (cause instanceof Error) {
95
+ return cause.message;
96
+ }
97
+ if (typeof cause === "string") {
98
+ return cause;
99
+ }
100
+ try {
101
+ return JSON.stringify(cause);
102
+ } catch {
103
+ return String(cause);
104
+ }
105
+ }
106
+
74
107
  // src/version.ts
75
- var CAATINGA_CORE_VERSION = "2.0.2";
108
+ var CAATINGA_CORE_VERSION = "2.2.0";
76
109
 
77
110
  // src/config/config.schema.ts
78
111
  import { z } from "zod";
@@ -237,18 +270,6 @@ function updateArtifact(artifacts, networkName, contractName, contractArtifact,
237
270
  };
238
271
  }
239
272
 
240
- // src/networks/networks.ts
241
- var WELL_KNOWN_NETWORKS = {
242
- testnet: {
243
- rpcUrl: "https://soroban-testnet.stellar.org",
244
- networkPassphrase: "Test SDF Network ; September 2015"
245
- },
246
- mainnet: {
247
- rpcUrl: "https://mainnet.sorobanrpc.com",
248
- networkPassphrase: "Public Global Stellar Network ; September 2015"
249
- }
250
- };
251
-
252
273
  // src/networks/resolve-network.ts
253
274
  function resolveNetwork(config, networkName) {
254
275
  const name = networkName ?? config.defaultNetwork;
@@ -263,6 +284,174 @@ function resolveNetwork(config, networkName) {
263
284
  return { name, config: network };
264
285
  }
265
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
+
266
455
  // src/shell/run-command.ts
267
456
  import { execa } from "execa";
268
457
 
@@ -584,7 +773,7 @@ function validateSourceShape(source) {
584
773
  }
585
774
 
586
775
  // src/contracts/resolve-contract.ts
587
- import path4 from "path";
776
+ import path6 from "path";
588
777
  function resolveContract(config, contractName, cwd = process.cwd()) {
589
778
  const contract = config.contracts[contractName];
590
779
  if (!contract) {
@@ -597,15 +786,15 @@ function resolveContract(config, contractName, cwd = process.cwd()) {
597
786
  return {
598
787
  name: contractName,
599
788
  config: contract,
600
- sourcePath: path4.resolve(cwd, contract.path),
601
- wasmPath: path4.resolve(cwd, contract.wasm)
789
+ sourcePath: path6.resolve(cwd, contract.path),
790
+ wasmPath: path6.resolve(cwd, contract.wasm)
602
791
  };
603
792
  }
604
793
 
605
794
  // src/contracts/wasm.ts
606
795
  import { createHash } from "crypto";
607
- import { access as access2, readdir, readFile as readFile2, stat } from "fs/promises";
608
- import path5 from "path";
796
+ import { access as access2, readdir as readdir2, readFile as readFile3, stat } from "fs/promises";
797
+ import path7 from "path";
609
798
  var LEGACY_RUST_WASM_TARGET = "wasm32-unknown-unknown";
610
799
  var CURRENT_RUST_WASM_TARGET = "wasm32v1-none";
611
800
  function toCurrentWasmTargetPath(wasmPath) {
@@ -628,8 +817,8 @@ function wasmNotFoundError(configuredWasmPath, options) {
628
817
  );
629
818
  }
630
819
  function toConfigRelativeWasmPath(absoluteWasmPath) {
631
- const relative = path5.relative(process.cwd(), absoluteWasmPath);
632
- 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("/")}`;
633
822
  }
634
823
  async function resolveWasmArtifactPath(configuredWasmPath) {
635
824
  try {
@@ -649,7 +838,7 @@ async function resolveWasmArtifactPath(configuredWasmPath) {
649
838
  }
650
839
  }
651
840
  async function hashWasm(wasmPath) {
652
- const bytes = await readFile2(wasmPath);
841
+ const bytes = await readFile3(wasmPath);
653
842
  return createHash("sha256").update(bytes).digest("hex");
654
843
  }
655
844
  async function getNewestMtimeInDirectory(directory) {
@@ -660,9 +849,9 @@ async function getNewestMtimeInDirectory(directory) {
660
849
  }
661
850
  let newest = 0;
662
851
  async function walk(dir) {
663
- const entries = await readdir(dir, { withFileTypes: true });
852
+ const entries = await readdir2(dir, { withFileTypes: true });
664
853
  for (const entry of entries) {
665
- const entryPath = path5.join(dir, entry.name);
854
+ const entryPath = path7.join(dir, entry.name);
666
855
  if (entry.isDirectory()) {
667
856
  await walk(entryPath);
668
857
  continue;
@@ -678,7 +867,7 @@ async function getNewestMtimeInDirectory(directory) {
678
867
  return newest > 0 ? newest : void 0;
679
868
  }
680
869
  async function isWasmOlderThanSources(input) {
681
- const srcDir = path5.join(input.contractPath, "src");
870
+ const srcDir = path7.join(input.contractPath, "src");
682
871
  const newestSourceMtime = await getNewestMtimeInDirectory(srcDir);
683
872
  if (newestSourceMtime === void 0) {
684
873
  return false;
@@ -751,7 +940,7 @@ async function buildContract(options) {
751
940
  }
752
941
 
753
942
  // src/contracts/deploy-contract.ts
754
- import path6 from "path";
943
+ import path8 from "path";
755
944
 
756
945
  // src/contracts/dependency-graph.ts
757
946
  function buildDependencyGraph(contracts) {
@@ -852,7 +1041,7 @@ async function deployContract(options) {
852
1041
  contract: contractWithWasm,
853
1042
  network,
854
1043
  contractId: existing.contractId,
855
- artifactsPath: path6.resolve(cwd, "caatinga.artifacts.json"),
1044
+ artifactsPath: path8.resolve(cwd, "caatinga.artifacts.json"),
856
1045
  output: "",
857
1046
  skipped: true,
858
1047
  staleWasmWarning
@@ -1129,13 +1318,13 @@ async function deployContractGraph(options) {
1129
1318
 
1130
1319
  // src/contracts/generate-bindings.ts
1131
1320
  import { access as access3, mkdir as mkdir2, unlink } from "fs/promises";
1132
- import path7 from "path";
1321
+ import path9 from "path";
1133
1322
  function toBindingImportPath(bindingsOutput, contractName) {
1134
- const normalized = bindingsOutput.replace(/^\.\//, "").split(path7.sep).join("/");
1135
- 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")}`;
1136
1325
  }
1137
1326
  async function removeLegacyBindingStub(cwd, bindingsOutput, contractName) {
1138
- const legacyPath = path7.resolve(cwd, bindingsOutput, `${contractName}.ts`);
1327
+ const legacyPath = path9.resolve(cwd, bindingsOutput, `${contractName}.ts`);
1139
1328
  try {
1140
1329
  await access3(legacyPath);
1141
1330
  await unlink(legacyPath);
@@ -1157,7 +1346,7 @@ async function generateBindings(options) {
1157
1346
  );
1158
1347
  }
1159
1348
  await checkBinary("stellar", "Install Stellar CLI before running caatinga generate.");
1160
- const outputDir = path7.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
1349
+ const outputDir = path9.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
1161
1350
  await mkdir2(outputDir, { recursive: true });
1162
1351
  const result = await runCommand("stellar", [
1163
1352
  "contract",
@@ -1178,16 +1367,59 @@ async function generateBindings(options) {
1178
1367
  options.config.frontend.bindingsOutput,
1179
1368
  options.contractName
1180
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);
1181
1378
  return {
1182
1379
  contractName: options.contractName,
1183
1380
  network,
1184
1381
  outputDir,
1185
1382
  importPath: toBindingImportPath(options.config.frontend.bindingsOutput, options.contractName),
1186
1383
  legacyStubRemoved,
1384
+ marker,
1187
1385
  output: result.all || result.stdout
1188
1386
  };
1189
1387
  }
1190
1388
 
1389
+ // src/contracts/generate-bindings-graph.ts
1390
+ async function generateBindingsGraph(options) {
1391
+ const cwd = options.cwd ?? process.cwd();
1392
+ const network = resolveNetwork(options.config, options.networkName);
1393
+ let targets;
1394
+ if (options.contractName) {
1395
+ targets = [options.contractName];
1396
+ } else if (options.contractNames && options.contractNames.length > 0) {
1397
+ targets = options.contractNames;
1398
+ } else {
1399
+ const artifacts = await readArtifacts(cwd);
1400
+ targets = Object.keys(artifacts.networks[network.name]?.contracts ?? {});
1401
+ if (targets.length === 0) {
1402
+ throw new CaatingaError(
1403
+ `No deployed contracts found on "${network.name}".`,
1404
+ CaatingaErrorCode.ARTIFACT_NOT_FOUND,
1405
+ "Run caatinga deploy before generating bindings, or pass a contract name."
1406
+ );
1407
+ }
1408
+ }
1409
+ const results = [];
1410
+ for (const contractName of targets) {
1411
+ results.push(
1412
+ await generateBindings({
1413
+ config: options.config,
1414
+ contractName,
1415
+ networkName: network.name,
1416
+ cwd
1417
+ })
1418
+ );
1419
+ }
1420
+ return { network, results };
1421
+ }
1422
+
1191
1423
  // src/contracts/invoke-contract.ts
1192
1424
  var INVOKE_SIGNING_FAILURE_REGEX = /xdr processing error: xdr value invalid/i;
1193
1425
  function parseInvokeTarget(target) {
@@ -1260,33 +1492,33 @@ ${error.hint ?? ""}`)) {
1260
1492
  }
1261
1493
 
1262
1494
  // src/templates/create-project-from-template.ts
1263
- import { cp, mkdir as mkdir3, readFile as readFile3, readdir as readdir2, stat as stat2, writeFile as writeFile2 } from "fs/promises";
1264
- import path8 from "path";
1265
- 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";
1266
1498
 
1267
1499
  // src/templates/template-manifest.schema.ts
1268
- import { z as z5 } from "zod";
1500
+ import { z as z6 } from "zod";
1269
1501
  import semver3 from "semver";
1270
1502
  var CURRENT_TEMPLATE_VERSION = 1;
1271
- var TemplateManifestSchema = z5.object({
1272
- name: z5.string().min(1),
1273
- version: z5.string().min(1),
1274
- description: z5.string().optional(),
1275
- caatinga: z5.object({
1276
- compatibleCore: z5.string().min(1),
1277
- 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()
1278
1510
  }),
1279
- frontend: z5.object({
1280
- framework: z5.enum(["vite-react", "next", "astro"]),
1281
- 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")
1282
1514
  }),
1283
- contracts: z5.object({
1284
- path: z5.string(),
1285
- default: z5.string().optional()
1515
+ contracts: z6.object({
1516
+ path: z6.string(),
1517
+ default: z6.string().optional()
1286
1518
  }),
1287
- files: z5.object({
1288
- config: z5.string().default("caatinga.config.ts"),
1289
- 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")
1290
1522
  })
1291
1523
  });
1292
1524
  function defaultCompatibleCoreRange(coreVersion = CAATINGA_CORE_VERSION) {
@@ -1337,8 +1569,8 @@ var TEMPLATE_COPY_EXCLUDED_DIRS = /* @__PURE__ */ new Set([
1337
1569
  ".git"
1338
1570
  ]);
1339
1571
  async function createProjectFromTemplate(options) {
1340
- const targetDir = path8.resolve(options.targetDir);
1341
- const templateDir = path8.resolve(options.templateDir);
1572
+ const targetDir = path10.resolve(options.targetDir);
1573
+ const templateDir = path10.resolve(options.templateDir);
1342
1574
  try {
1343
1575
  await stat2(templateDir);
1344
1576
  } catch {
@@ -1373,9 +1605,9 @@ async function ensureArtifacts(targetDir, projectName) {
1373
1605
  }
1374
1606
  }
1375
1607
  async function readTemplateManifest(templateDir) {
1376
- const manifestPath = path8.join(templateDir, "caatinga.template.json");
1608
+ const manifestPath = path10.join(templateDir, "caatinga.template.json");
1377
1609
  try {
1378
- const rawManifest = await readFile3(manifestPath, "utf8");
1610
+ const rawManifest = await readFile4(manifestPath, "utf8");
1379
1611
  const manifest = TemplateManifestSchema.parse(JSON.parse(rawManifest));
1380
1612
  const compatibilityIssue = getTemplateCompatibilityIssue(manifest);
1381
1613
  if (compatibilityIssue) {
@@ -1397,7 +1629,7 @@ async function readTemplateManifest(templateDir) {
1397
1629
  if (error instanceof CaatingaError) {
1398
1630
  throw error;
1399
1631
  }
1400
- if (error instanceof SyntaxError || error instanceof z6.ZodError) {
1632
+ if (error instanceof SyntaxError || error instanceof z7.ZodError) {
1401
1633
  throw new CaatingaError(
1402
1634
  "Template manifest is invalid.",
1403
1635
  CaatingaErrorCode.INVALID_TEMPLATE_MANIFEST,
@@ -1408,9 +1640,9 @@ async function readTemplateManifest(templateDir) {
1408
1640
  }
1409
1641
  }
1410
1642
  async function replaceTemplateVariables(dir, projectName) {
1411
- const entries = await readdir2(dir);
1643
+ const entries = await readdir3(dir);
1412
1644
  await Promise.all(entries.map(async (entry) => {
1413
- const entryPath = path8.join(dir, entry);
1645
+ const entryPath = path10.join(dir, entry);
1414
1646
  const entryStat = await stat2(entryPath);
1415
1647
  if (entryStat.isDirectory()) {
1416
1648
  await replaceTemplateVariables(entryPath, projectName);
@@ -1419,16 +1651,16 @@ async function replaceTemplateVariables(dir, projectName) {
1419
1651
  if (!isTextTemplateFile(entryPath)) {
1420
1652
  return;
1421
1653
  }
1422
- const content = await readFile3(entryPath, "utf8");
1423
- 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");
1424
1656
  }));
1425
1657
  }
1426
1658
  function shouldCopyTemplateEntry(templateDir, source) {
1427
- const relativePath = path8.relative(templateDir, source);
1659
+ const relativePath = path10.relative(templateDir, source);
1428
1660
  if (!relativePath || relativePath === ".") {
1429
1661
  return true;
1430
1662
  }
1431
- 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));
1432
1664
  }
1433
1665
  function isTextTemplateFile(filePath) {
1434
1666
  return [
@@ -1440,7 +1672,7 @@ function isTextTemplateFile(filePath) {
1440
1672
  ".tsx",
1441
1673
  ".css",
1442
1674
  ".html"
1443
- ].includes(path8.extname(filePath));
1675
+ ].includes(path10.extname(filePath));
1444
1676
  }
1445
1677
 
1446
1678
  // src/ci/is-transient-testnet-smoke-failure.ts
@@ -1464,6 +1696,8 @@ function isTransientTestnetSmokeFailure(logText) {
1464
1696
  return TRANSIENT_PATTERN.test(logText);
1465
1697
  }
1466
1698
  export {
1699
+ BINDING_MARKER_FILENAME,
1700
+ BindingMarkerSchema,
1467
1701
  CAATINGA_CORE_VERSION,
1468
1702
  CaatingaArtifactsSchema,
1469
1703
  CaatingaConfigSchema,
@@ -1477,13 +1711,18 @@ export {
1477
1711
  buildDependencyGraph,
1478
1712
  checkBinary,
1479
1713
  checkStellarCliVersion,
1714
+ collectProjectStatus,
1480
1715
  createInitialArtifacts,
1481
1716
  createProjectFromTemplate,
1482
1717
  defineConfig,
1483
1718
  deployContract,
1484
1719
  deployContractGraph,
1720
+ evaluateBindingFreshness,
1721
+ evaluateBindingsFreshness,
1485
1722
  evaluateStellarCliCompatibility,
1723
+ formatCaatingaError,
1486
1724
  generateBindings,
1725
+ generateBindingsGraph,
1487
1726
  invokeContract,
1488
1727
  isTransientTestnetSmokeFailure,
1489
1728
  loadConfig,
@@ -1491,6 +1730,7 @@ export {
1491
1730
  parseInvokeTarget,
1492
1731
  parseStellarCliVersion,
1493
1732
  readArtifacts,
1733
+ readBindingMarker,
1494
1734
  resolveContract,
1495
1735
  resolveDeployArgs,
1496
1736
  resolveDeployOrder,
@@ -1499,5 +1739,6 @@ export {
1499
1739
  toCaatingaError,
1500
1740
  updateArtifact,
1501
1741
  validateSourceShape,
1502
- writeArtifacts
1742
+ writeArtifacts,
1743
+ writeBindingMarker
1503
1744
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caatinga/core",
3
- "version": "2.0.2",
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",