@caatinga/core 0.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/LICENSE +21 -0
- package/README.md +33 -0
- package/dist/index.cjs +1104 -0
- package/dist/index.d.cts +575 -0
- package/dist/index.d.ts +575 -0
- package/dist/index.js +1033 -0
- package/package.json +58 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1033 @@
|
|
|
1
|
+
// src/errors/CaatingaErrorCode.ts
|
|
2
|
+
var CaatingaErrorCode = {
|
|
3
|
+
CONFIG_NOT_FOUND: "CAATINGA_CONFIG_NOT_FOUND",
|
|
4
|
+
INVALID_CONFIG: "CAATINGA_INVALID_CONFIG",
|
|
5
|
+
COMMAND_FAILED: "CAATINGA_COMMAND_FAILED",
|
|
6
|
+
UNEXPECTED_ERROR: "CAATINGA_UNEXPECTED_ERROR",
|
|
7
|
+
STELLAR_CLI_NOT_FOUND: "CAATINGA_STELLAR_CLI_NOT_FOUND",
|
|
8
|
+
STELLAR_CLI_VERSION_PARSE_FAILED: "CAATINGA_STELLAR_CLI_VERSION_PARSE_FAILED",
|
|
9
|
+
UNSUPPORTED_CLI_VERSION: "CAATINGA_UNSUPPORTED_CLI_VERSION",
|
|
10
|
+
UNTESTED_CLI_VERSION: "CAATINGA_UNTESTED_CLI_VERSION",
|
|
11
|
+
RUST_NOT_FOUND: "CAATINGA_RUST_NOT_FOUND",
|
|
12
|
+
RUST_TARGET_NOT_FOUND: "CAATINGA_RUST_TARGET_NOT_FOUND",
|
|
13
|
+
DEPLOY_FAILED: "CAATINGA_DEPLOY_FAILED",
|
|
14
|
+
BUILD_FAILED: "CAATINGA_BUILD_FAILED",
|
|
15
|
+
BINDINGS_FAILED: "CAATINGA_BINDINGS_FAILED",
|
|
16
|
+
INVOKE_FAILED: "CAATINGA_INVOKE_FAILED",
|
|
17
|
+
CONTRACT_NOT_FOUND: "CAATINGA_CONTRACT_NOT_FOUND",
|
|
18
|
+
NETWORK_NOT_FOUND: "CAATINGA_NETWORK_NOT_FOUND",
|
|
19
|
+
ARTIFACT_NOT_FOUND: "CAATINGA_ARTIFACT_NOT_FOUND",
|
|
20
|
+
ARTIFACT_INVALID: "CAATINGA_ARTIFACT_INVALID",
|
|
21
|
+
CONTRACT_ID_NOT_FOUND: "CAATINGA_CONTRACT_ID_NOT_FOUND",
|
|
22
|
+
CONTRACT_ARTIFACT_NOT_FOUND: "CAATINGA_CONTRACT_ARTIFACT_NOT_FOUND",
|
|
23
|
+
CONTRACT_DEPENDENCY_NOT_FOUND: "CAATINGA_CONTRACT_DEPENDENCY_NOT_FOUND",
|
|
24
|
+
CONTRACT_DEPENDENCY_CYCLE: "CAATINGA_CONTRACT_DEPENDENCY_CYCLE",
|
|
25
|
+
CONTRACT_DEPENDENCY_ARTIFACT_NOT_FOUND: "CAATINGA_CONTRACT_DEPENDENCY_ARTIFACT_NOT_FOUND",
|
|
26
|
+
DEPLOY_ARG_PLACEHOLDER_INVALID: "CAATINGA_DEPLOY_ARG_PLACEHOLDER_INVALID",
|
|
27
|
+
DEPLOY_ARG_PLACEHOLDER_UNRESOLVED: "CAATINGA_DEPLOY_ARG_PLACEHOLDER_UNRESOLVED",
|
|
28
|
+
BINDING_CLIENT_NOT_FOUND: "CAATINGA_BINDING_CLIENT_NOT_FOUND",
|
|
29
|
+
BINDING_METHOD_NOT_FOUND: "CAATINGA_BINDING_METHOD_NOT_FOUND",
|
|
30
|
+
XDR_BUILD_FAILED: "CAATINGA_XDR_BUILD_FAILED",
|
|
31
|
+
XDR_PREPARE_FAILED: "CAATINGA_XDR_PREPARE_FAILED",
|
|
32
|
+
XDR_SIGN_FAILED: "CAATINGA_XDR_SIGN_FAILED",
|
|
33
|
+
XDR_SUBMIT_FAILED: "CAATINGA_XDR_SUBMIT_FAILED",
|
|
34
|
+
XDR_RESULT_FAILED: "CAATINGA_XDR_RESULT_FAILED",
|
|
35
|
+
WALLET_NOT_CONNECTED: "CAATINGA_WALLET_NOT_CONNECTED",
|
|
36
|
+
SOURCE_ACCOUNT_REQUIRED: "CAATINGA_SOURCE_ACCOUNT_REQUIRED",
|
|
37
|
+
UNSAFE_SOURCE_ACCOUNT: "CAATINGA_UNSAFE_SOURCE_ACCOUNT",
|
|
38
|
+
INVOKE_TARGET_INVALID: "CAATINGA_INVOKE_TARGET_INVALID",
|
|
39
|
+
TEMPLATE_NOT_FOUND: "CAATINGA_TEMPLATE_NOT_FOUND",
|
|
40
|
+
INVALID_TEMPLATE_MANIFEST: "CAATINGA_INVALID_TEMPLATE_MANIFEST",
|
|
41
|
+
TEMPLATE_MANIFEST_NOT_FOUND: "CAATINGA_TEMPLATE_MANIFEST_NOT_FOUND",
|
|
42
|
+
TEMPLATE_INCOMPATIBLE: "CAATINGA_TEMPLATE_INCOMPATIBLE"
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// src/errors/CaatingaError.ts
|
|
46
|
+
var CaatingaError = class extends Error {
|
|
47
|
+
constructor(message, code, hint, cause) {
|
|
48
|
+
super(message);
|
|
49
|
+
this.code = code;
|
|
50
|
+
this.hint = hint;
|
|
51
|
+
this.cause = cause;
|
|
52
|
+
this.name = "CaatingaError";
|
|
53
|
+
}
|
|
54
|
+
code;
|
|
55
|
+
hint;
|
|
56
|
+
cause;
|
|
57
|
+
};
|
|
58
|
+
function toCaatingaError(error) {
|
|
59
|
+
if (error instanceof CaatingaError) {
|
|
60
|
+
return error;
|
|
61
|
+
}
|
|
62
|
+
if (error instanceof Error) {
|
|
63
|
+
return new CaatingaError(error.message, CaatingaErrorCode.UNEXPECTED_ERROR, void 0, error);
|
|
64
|
+
}
|
|
65
|
+
return new CaatingaError("An unexpected error occurred.", CaatingaErrorCode.UNEXPECTED_ERROR);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/version.ts
|
|
69
|
+
var CAATINGA_CORE_VERSION = "0.2.0";
|
|
70
|
+
|
|
71
|
+
// src/config/config.schema.ts
|
|
72
|
+
import { z } from "zod";
|
|
73
|
+
var DeployArgValueSchema = z.union([z.string(), z.number(), z.boolean()]);
|
|
74
|
+
var ContractConfigSchema = z.object({
|
|
75
|
+
path: z.string().min(1),
|
|
76
|
+
wasm: z.string().min(1),
|
|
77
|
+
dependsOn: z.array(z.string().min(1)).default([]),
|
|
78
|
+
deployArgs: z.record(z.string().min(1), DeployArgValueSchema).default({})
|
|
79
|
+
});
|
|
80
|
+
var NetworkConfigSchema = z.object({
|
|
81
|
+
rpcUrl: z.string().url(),
|
|
82
|
+
networkPassphrase: z.string().min(1)
|
|
83
|
+
});
|
|
84
|
+
var CaatingaConfigSchema = z.object({
|
|
85
|
+
project: z.string().min(1),
|
|
86
|
+
defaultNetwork: z.string().min(1).default("testnet"),
|
|
87
|
+
contracts: z.record(z.string().min(1), ContractConfigSchema).refine(
|
|
88
|
+
(contracts) => Object.keys(contracts).length > 0,
|
|
89
|
+
"At least one contract must be configured."
|
|
90
|
+
),
|
|
91
|
+
networks: z.record(z.string().min(1), NetworkConfigSchema).refine(
|
|
92
|
+
(networks) => Object.keys(networks).length > 0,
|
|
93
|
+
"At least one network must be configured."
|
|
94
|
+
),
|
|
95
|
+
frontend: z.object({
|
|
96
|
+
framework: z.enum(["vite-react", "next", "astro"]).default("vite-react"),
|
|
97
|
+
bindingsOutput: z.string().min(1)
|
|
98
|
+
})
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// src/config/define-config.ts
|
|
102
|
+
function defineConfig(config) {
|
|
103
|
+
return config;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/config/load-config.ts
|
|
107
|
+
import { access } from "fs/promises";
|
|
108
|
+
import path from "path";
|
|
109
|
+
import { createJiti } from "jiti";
|
|
110
|
+
import { z as z2 } from "zod";
|
|
111
|
+
async function loadConfig(options = {}) {
|
|
112
|
+
const cwd = options.cwd ?? process.cwd();
|
|
113
|
+
const configPath = path.resolve(cwd, options.configPath ?? "caatinga.config.ts");
|
|
114
|
+
try {
|
|
115
|
+
await access(configPath);
|
|
116
|
+
} catch {
|
|
117
|
+
throw new CaatingaError(
|
|
118
|
+
"caatinga.config.ts was not found.",
|
|
119
|
+
CaatingaErrorCode.CONFIG_NOT_FOUND,
|
|
120
|
+
"Run this command from a Caatinga project root, or create a caatinga.config.ts file."
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
const jiti = createJiti(import.meta.url);
|
|
125
|
+
const loaded = await jiti.import(configPath, { default: true });
|
|
126
|
+
return CaatingaConfigSchema.parse(loaded);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
if (error instanceof z2.ZodError) {
|
|
129
|
+
throw new CaatingaError(
|
|
130
|
+
"caatinga.config.ts is invalid.",
|
|
131
|
+
CaatingaErrorCode.INVALID_CONFIG,
|
|
132
|
+
error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// src/artifacts/artifact.schema.ts
|
|
140
|
+
import { z as z3 } from "zod";
|
|
141
|
+
var ContractArtifactSchema = z3.object({
|
|
142
|
+
contractId: z3.string().min(1),
|
|
143
|
+
wasmHash: z3.string().min(1),
|
|
144
|
+
deployedAt: z3.string().datetime(),
|
|
145
|
+
sourcePath: z3.string().min(1),
|
|
146
|
+
wasmPath: z3.string().min(1),
|
|
147
|
+
dependencies: z3.array(z3.string().min(1)).default([]),
|
|
148
|
+
resolvedDeployArgs: z3.record(z3.string().min(1), z3.union([z3.string(), z3.number(), z3.boolean()])).default({})
|
|
149
|
+
});
|
|
150
|
+
var NetworkArtifactsSchema = z3.object({
|
|
151
|
+
contracts: z3.record(z3.string().min(1), ContractArtifactSchema).default({}),
|
|
152
|
+
dependencyGraph: z3.record(z3.string().min(1), z3.array(z3.string().min(1))).default({})
|
|
153
|
+
});
|
|
154
|
+
var CaatingaArtifactsSchema = z3.object({
|
|
155
|
+
project: z3.string().min(1),
|
|
156
|
+
version: z3.literal(1),
|
|
157
|
+
networks: z3.record(z3.string().min(1), NetworkArtifactsSchema).default({})
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// src/artifacts/read-artifacts.ts
|
|
161
|
+
import { readFile } from "fs/promises";
|
|
162
|
+
import path2 from "path";
|
|
163
|
+
import { z as z4 } from "zod";
|
|
164
|
+
async function readArtifacts(cwd = process.cwd()) {
|
|
165
|
+
const artifactsPath = path2.resolve(cwd, "caatinga.artifacts.json");
|
|
166
|
+
try {
|
|
167
|
+
const json = await readFile(artifactsPath, "utf8");
|
|
168
|
+
return CaatingaArtifactsSchema.parse(JSON.parse(json));
|
|
169
|
+
} catch (error) {
|
|
170
|
+
if (error.code === "ENOENT") {
|
|
171
|
+
throw new CaatingaError(
|
|
172
|
+
"caatinga.artifacts.json was not found.",
|
|
173
|
+
CaatingaErrorCode.ARTIFACT_NOT_FOUND,
|
|
174
|
+
"Run caatinga init, or create the artifacts file before deploying or generating bindings."
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
if (error instanceof SyntaxError || error instanceof z4.ZodError) {
|
|
178
|
+
throw new CaatingaError(
|
|
179
|
+
"caatinga.artifacts.json is invalid.",
|
|
180
|
+
CaatingaErrorCode.ARTIFACT_INVALID,
|
|
181
|
+
"Fix the JSON shape before running Caatinga commands."
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
throw error;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// src/artifacts/write-artifacts.ts
|
|
189
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
190
|
+
import path3 from "path";
|
|
191
|
+
async function writeArtifacts(artifacts, cwd = process.cwd()) {
|
|
192
|
+
const artifactsPath = path3.resolve(cwd, "caatinga.artifacts.json");
|
|
193
|
+
await mkdir(path3.dirname(artifactsPath), { recursive: true });
|
|
194
|
+
await writeFile(artifactsPath, `${JSON.stringify(artifacts, null, 2)}
|
|
195
|
+
`, "utf8");
|
|
196
|
+
return artifactsPath;
|
|
197
|
+
}
|
|
198
|
+
function createInitialArtifacts(project) {
|
|
199
|
+
return {
|
|
200
|
+
project,
|
|
201
|
+
version: 1,
|
|
202
|
+
networks: {}
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// src/artifacts/update-artifact.ts
|
|
207
|
+
function updateArtifact(artifacts, networkName, contractName, contractArtifact, networkExtras) {
|
|
208
|
+
const existingNetwork = artifacts.networks[networkName] ?? { contracts: {}, dependencyGraph: {} };
|
|
209
|
+
return {
|
|
210
|
+
...artifacts,
|
|
211
|
+
networks: {
|
|
212
|
+
...artifacts.networks,
|
|
213
|
+
[networkName]: {
|
|
214
|
+
...existingNetwork,
|
|
215
|
+
dependencyGraph: networkExtras?.dependencyGraph ?? existingNetwork.dependencyGraph ?? {},
|
|
216
|
+
contracts: {
|
|
217
|
+
...existingNetwork.contracts,
|
|
218
|
+
[contractName]: contractArtifact
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// src/networks/networks.ts
|
|
226
|
+
var WELL_KNOWN_NETWORKS = {
|
|
227
|
+
testnet: {
|
|
228
|
+
rpcUrl: "https://soroban-testnet.stellar.org",
|
|
229
|
+
networkPassphrase: "Test SDF Network ; September 2015"
|
|
230
|
+
},
|
|
231
|
+
mainnet: {
|
|
232
|
+
rpcUrl: "https://mainnet.sorobanrpc.com",
|
|
233
|
+
networkPassphrase: "Public Global Stellar Network ; September 2015"
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
// src/networks/resolve-network.ts
|
|
238
|
+
function resolveNetwork(config, networkName) {
|
|
239
|
+
const name = networkName ?? config.defaultNetwork;
|
|
240
|
+
const network = config.networks[name];
|
|
241
|
+
if (!network) {
|
|
242
|
+
throw new CaatingaError(
|
|
243
|
+
`Network "${name}" is not configured.`,
|
|
244
|
+
CaatingaErrorCode.NETWORK_NOT_FOUND,
|
|
245
|
+
`Add "${name}" to caatinga.config.ts networks, or pass a configured --network value.`
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
return { name, config: network };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// src/shell/run-command.ts
|
|
252
|
+
import { execa } from "execa";
|
|
253
|
+
|
|
254
|
+
// src/stellar-cli/version.ts
|
|
255
|
+
import semver from "semver";
|
|
256
|
+
var STELLAR_CLI_MIN_VERSION = "22.0.0";
|
|
257
|
+
var STELLAR_CLI_TESTED_MAX_VERSION = "25.2.0";
|
|
258
|
+
var STELLAR_CLI_SEMVER_REGEX = /\b(\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?(?:\+[0-9A-Za-z-.]+)?)\b/;
|
|
259
|
+
function parseStellarCliVersion(output) {
|
|
260
|
+
const match = output.match(STELLAR_CLI_SEMVER_REGEX);
|
|
261
|
+
const version = match?.[1];
|
|
262
|
+
if (!version || !semver.valid(version)) {
|
|
263
|
+
throw new CaatingaError(
|
|
264
|
+
"Could not parse Stellar CLI version from command output.",
|
|
265
|
+
CaatingaErrorCode.STELLAR_CLI_VERSION_PARSE_FAILED,
|
|
266
|
+
"Run `stellar --version` and verify that the output includes a semantic version."
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
return version;
|
|
270
|
+
}
|
|
271
|
+
function assertSupportedStellarCliVersion(input) {
|
|
272
|
+
const normalizedVersion = semver.valid(input.version);
|
|
273
|
+
if (!normalizedVersion) {
|
|
274
|
+
throw new CaatingaError(
|
|
275
|
+
"Could not parse Stellar CLI version.",
|
|
276
|
+
CaatingaErrorCode.STELLAR_CLI_VERSION_PARSE_FAILED,
|
|
277
|
+
"Use a semantic version such as 22.0.1."
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
if (semver.lt(normalizedVersion, STELLAR_CLI_MIN_VERSION)) {
|
|
281
|
+
throw new CaatingaError(
|
|
282
|
+
`Stellar CLI ${normalizedVersion} is below the supported minimum ${STELLAR_CLI_MIN_VERSION}.`,
|
|
283
|
+
CaatingaErrorCode.UNSUPPORTED_CLI_VERSION,
|
|
284
|
+
`Install Stellar CLI ${STELLAR_CLI_MIN_VERSION} or newer.`
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
if (!input.allowUntested && semver.gt(normalizedVersion, STELLAR_CLI_TESTED_MAX_VERSION)) {
|
|
288
|
+
throw new CaatingaError(
|
|
289
|
+
`Stellar CLI ${normalizedVersion} is newer than the tested maximum ${STELLAR_CLI_TESTED_MAX_VERSION}.`,
|
|
290
|
+
CaatingaErrorCode.UNTESTED_CLI_VERSION,
|
|
291
|
+
"Pass --allow-untested-stellar-cli only after accepting the compatibility risk."
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
return normalizedVersion;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// src/stellar-cli/check-stellar-cli-version.ts
|
|
298
|
+
async function checkStellarCliVersion(input) {
|
|
299
|
+
try {
|
|
300
|
+
const result = await runCommand("stellar", ["--version"], {
|
|
301
|
+
skipStellarVersionCheck: true
|
|
302
|
+
});
|
|
303
|
+
const output = result.all || result.stdout || result.stderr;
|
|
304
|
+
return assertSupportedStellarCliVersion({
|
|
305
|
+
version: parseStellarCliVersion(output),
|
|
306
|
+
allowUntested: input.allowUntested
|
|
307
|
+
});
|
|
308
|
+
} catch (error) {
|
|
309
|
+
if (typeof error === "object" && error && "code" in error && error.code === "ENOENT") {
|
|
310
|
+
throw new CaatingaError(
|
|
311
|
+
"Stellar CLI was not found.",
|
|
312
|
+
CaatingaErrorCode.STELLAR_CLI_NOT_FOUND,
|
|
313
|
+
"Install Stellar CLI before running Caatinga-backed commands.",
|
|
314
|
+
error
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
throw error;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// src/shell/run-command.ts
|
|
322
|
+
async function runCommand(command, args, options = {}) {
|
|
323
|
+
try {
|
|
324
|
+
if (command === "stellar" && !options.skipStellarVersionCheck) {
|
|
325
|
+
await checkStellarCliVersion({
|
|
326
|
+
allowUntested: options.allowUntestedStellarCli === true
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
const result = await execa(command, args, {
|
|
330
|
+
cwd: options.cwd,
|
|
331
|
+
env: options.env,
|
|
332
|
+
all: true,
|
|
333
|
+
reject: true
|
|
334
|
+
});
|
|
335
|
+
return {
|
|
336
|
+
stdout: result.stdout,
|
|
337
|
+
stderr: result.stderr,
|
|
338
|
+
all: result.all ?? ""
|
|
339
|
+
};
|
|
340
|
+
} catch (error) {
|
|
341
|
+
if (error instanceof CaatingaError) {
|
|
342
|
+
throw error;
|
|
343
|
+
}
|
|
344
|
+
if (command === "stellar" && typeof error === "object" && error && "code" in error && error.code === "ENOENT") {
|
|
345
|
+
throw new CaatingaError(
|
|
346
|
+
"Stellar CLI was not found.",
|
|
347
|
+
CaatingaErrorCode.STELLAR_CLI_NOT_FOUND,
|
|
348
|
+
"Install Stellar CLI before running Caatinga-backed commands.",
|
|
349
|
+
error
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
const output = typeof error === "object" && error && "all" in error ? String(error.all) : void 0;
|
|
353
|
+
throw new CaatingaError(
|
|
354
|
+
`Command failed: ${command} ${args.join(" ")}`,
|
|
355
|
+
options.failureCode ?? CaatingaErrorCode.COMMAND_FAILED,
|
|
356
|
+
output || "Re-run the command with the underlying tool directly for full diagnostics.",
|
|
357
|
+
error
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// src/shell/check-binary.ts
|
|
363
|
+
async function checkBinary(binary, hint, options = {}) {
|
|
364
|
+
try {
|
|
365
|
+
await runCommand(binary, ["--version"], options);
|
|
366
|
+
} catch (error) {
|
|
367
|
+
if (error instanceof CaatingaError) {
|
|
368
|
+
throw error;
|
|
369
|
+
}
|
|
370
|
+
const code = binary === "stellar" ? CaatingaErrorCode.STELLAR_CLI_NOT_FOUND : binary === "rustc" ? CaatingaErrorCode.RUST_NOT_FOUND : CaatingaErrorCode.COMMAND_FAILED;
|
|
371
|
+
throw new CaatingaError(`${binary} was not found.`, code, hint, error);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// src/stellar-cli/parse-contract-id.ts
|
|
376
|
+
var CONTRACT_ID_REGEX = /\bC[A-Z0-9]{55}\b/;
|
|
377
|
+
function parseContractId(output) {
|
|
378
|
+
const match = output.match(CONTRACT_ID_REGEX);
|
|
379
|
+
if (!match) {
|
|
380
|
+
throw new CaatingaError(
|
|
381
|
+
"Could not find contract ID in Stellar CLI output.",
|
|
382
|
+
CaatingaErrorCode.CONTRACT_ID_NOT_FOUND,
|
|
383
|
+
"Check whether the Stellar CLI output format changed."
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
return match[0];
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// src/contracts/resolve-contract.ts
|
|
390
|
+
import path4 from "path";
|
|
391
|
+
function resolveContract(config, contractName, cwd = process.cwd()) {
|
|
392
|
+
const contract = config.contracts[contractName];
|
|
393
|
+
if (!contract) {
|
|
394
|
+
throw new CaatingaError(
|
|
395
|
+
`Contract "${contractName}" is not configured.`,
|
|
396
|
+
CaatingaErrorCode.CONTRACT_NOT_FOUND,
|
|
397
|
+
`Add "${contractName}" to caatinga.config.ts contracts, or pass a configured contract name.`
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
return {
|
|
401
|
+
name: contractName,
|
|
402
|
+
config: contract,
|
|
403
|
+
sourcePath: path4.resolve(cwd, contract.path),
|
|
404
|
+
wasmPath: path4.resolve(cwd, contract.wasm)
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// src/contracts/wasm.ts
|
|
409
|
+
import { createHash } from "crypto";
|
|
410
|
+
import { access as access2, readFile as readFile2 } from "fs/promises";
|
|
411
|
+
async function assertWasmExists(wasmPath) {
|
|
412
|
+
try {
|
|
413
|
+
await access2(wasmPath);
|
|
414
|
+
} catch {
|
|
415
|
+
throw new CaatingaError(
|
|
416
|
+
`WASM output was not found at ${wasmPath}.`,
|
|
417
|
+
CaatingaErrorCode.ARTIFACT_NOT_FOUND,
|
|
418
|
+
"Run caatinga build before deploy or generate."
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
async function hashWasm(wasmPath) {
|
|
423
|
+
const bytes = await readFile2(wasmPath);
|
|
424
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// src/contracts/build-contract.ts
|
|
428
|
+
var RUST_WASM_TARGET = "wasm32-unknown-unknown";
|
|
429
|
+
var MISSING_WASM_TARGET_HINT_SUBSTRINGS = [
|
|
430
|
+
"not installed",
|
|
431
|
+
"not found",
|
|
432
|
+
"needs to be installed",
|
|
433
|
+
"add the",
|
|
434
|
+
"rustup target"
|
|
435
|
+
];
|
|
436
|
+
function isMissingRustWasmTargetError(error) {
|
|
437
|
+
if (!(error instanceof CaatingaError)) {
|
|
438
|
+
return false;
|
|
439
|
+
}
|
|
440
|
+
const parts = [
|
|
441
|
+
error.message,
|
|
442
|
+
error.hint ?? "",
|
|
443
|
+
error.cause === void 0 ? "" : String(error.cause)
|
|
444
|
+
];
|
|
445
|
+
const haystack = parts.join("\n").toLowerCase();
|
|
446
|
+
if (!haystack.includes(RUST_WASM_TARGET)) {
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
return MISSING_WASM_TARGET_HINT_SUBSTRINGS.some((needle) => haystack.includes(needle));
|
|
450
|
+
}
|
|
451
|
+
async function buildContract(options) {
|
|
452
|
+
const cwd = options.cwd ?? process.cwd();
|
|
453
|
+
const contract = resolveContract(options.config, options.contractName, cwd);
|
|
454
|
+
await checkBinary("rustc", "Install Rust before running caatinga build.");
|
|
455
|
+
await checkBinary("stellar", "Install Stellar CLI before running caatinga build.", {
|
|
456
|
+
allowUntestedStellarCli: options.allowUntestedStellarCli
|
|
457
|
+
});
|
|
458
|
+
let result;
|
|
459
|
+
try {
|
|
460
|
+
result = await runCommand("stellar", ["contract", "build"], {
|
|
461
|
+
cwd: contract.sourcePath,
|
|
462
|
+
allowUntestedStellarCli: options.allowUntestedStellarCli,
|
|
463
|
+
failureCode: CaatingaErrorCode.BUILD_FAILED
|
|
464
|
+
});
|
|
465
|
+
} catch (error) {
|
|
466
|
+
if (error instanceof CaatingaError && error.code === CaatingaErrorCode.BUILD_FAILED && isMissingRustWasmTargetError(error)) {
|
|
467
|
+
throw new CaatingaError(
|
|
468
|
+
`Required Rust wasm target "${RUST_WASM_TARGET}" is missing.`,
|
|
469
|
+
CaatingaErrorCode.RUST_TARGET_NOT_FOUND,
|
|
470
|
+
`Run \`rustup target add ${RUST_WASM_TARGET}\` and retry the build.`,
|
|
471
|
+
error
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
throw error;
|
|
475
|
+
}
|
|
476
|
+
await assertWasmExists(contract.wasmPath);
|
|
477
|
+
return {
|
|
478
|
+
contract,
|
|
479
|
+
output: result.all || result.stdout
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// src/contracts/deploy-contract.ts
|
|
484
|
+
import path5 from "path";
|
|
485
|
+
|
|
486
|
+
// src/contracts/dependency-graph.ts
|
|
487
|
+
function buildDependencyGraph(contracts) {
|
|
488
|
+
const graph = {};
|
|
489
|
+
for (const name of Object.keys(contracts)) {
|
|
490
|
+
graph[name] = [...contracts[name].dependsOn];
|
|
491
|
+
}
|
|
492
|
+
return graph;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// src/contracts/resolve-deploy-args.ts
|
|
496
|
+
var CONTRACT_ID_PLACEHOLDER = /^\$\{contracts\.([A-Za-z0-9_-]+)\.contractId\}$/;
|
|
497
|
+
function resolveDeployArgs(input) {
|
|
498
|
+
const resolved = {};
|
|
499
|
+
for (const [key, value] of Object.entries(input.deployArgs)) {
|
|
500
|
+
if (typeof value !== "string" || !value.includes("${")) {
|
|
501
|
+
resolved[key] = value;
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
const match = value.match(CONTRACT_ID_PLACEHOLDER);
|
|
505
|
+
if (!match) {
|
|
506
|
+
throw new CaatingaError(
|
|
507
|
+
`Deploy arg "${key}" contains an unsupported placeholder.`,
|
|
508
|
+
CaatingaErrorCode.DEPLOY_ARG_PLACEHOLDER_INVALID,
|
|
509
|
+
"Use only ${contracts.<contractName>.contractId}."
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
const contractName = match[1];
|
|
513
|
+
const contractArtifact = input.artifacts.networks[input.network]?.contracts[contractName];
|
|
514
|
+
if (!contractArtifact?.contractId) {
|
|
515
|
+
throw new CaatingaError(
|
|
516
|
+
`No dependency artifact found for "${contractName}" on "${input.network}".`,
|
|
517
|
+
CaatingaErrorCode.CONTRACT_DEPENDENCY_ARTIFACT_NOT_FOUND,
|
|
518
|
+
"Deploy the dependency first or run deploy without --no-deps."
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
resolved[key] = contractArtifact.contractId;
|
|
522
|
+
}
|
|
523
|
+
return resolved;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// src/contracts/source-account.ts
|
|
527
|
+
function assertSafeSourceAccount(source) {
|
|
528
|
+
if (!source) {
|
|
529
|
+
throw new CaatingaError(
|
|
530
|
+
"A source account or Stellar CLI identity is required.",
|
|
531
|
+
CaatingaErrorCode.SOURCE_ACCOUNT_REQUIRED,
|
|
532
|
+
"Pass --source alice or --source G...; do not pass secret keys or seed phrases."
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
if (source.startsWith("S") || source.trim().includes(" ")) {
|
|
536
|
+
throw new CaatingaError(
|
|
537
|
+
"Refusing to accept a likely secret key or seed phrase as --source.",
|
|
538
|
+
CaatingaErrorCode.UNSAFE_SOURCE_ACCOUNT,
|
|
539
|
+
"Use a Stellar CLI identity alias or public account address instead."
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
return source;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// src/contracts/deploy-contract.ts
|
|
546
|
+
function toSnakeCaseFlag(key) {
|
|
547
|
+
return key.replace(/([A-Z])/g, "_$1").replace(/^_/, "").toLowerCase();
|
|
548
|
+
}
|
|
549
|
+
function formatConstructorCliArgs(resolved) {
|
|
550
|
+
const entries = Object.entries(resolved);
|
|
551
|
+
if (entries.length === 0) {
|
|
552
|
+
return [];
|
|
553
|
+
}
|
|
554
|
+
const tail = ["--"];
|
|
555
|
+
for (const [key, value] of entries) {
|
|
556
|
+
tail.push(`--${toSnakeCaseFlag(key)}`, String(value));
|
|
557
|
+
}
|
|
558
|
+
return tail;
|
|
559
|
+
}
|
|
560
|
+
async function deployContract(options) {
|
|
561
|
+
const cwd = options.cwd ?? process.cwd();
|
|
562
|
+
const contract = resolveContract(options.config, options.contractName, cwd);
|
|
563
|
+
const network = resolveNetwork(options.config, options.networkName);
|
|
564
|
+
const source = assertSafeSourceAccount(options.source);
|
|
565
|
+
await checkBinary("stellar", "Install Stellar CLI before running caatinga deploy.", {
|
|
566
|
+
allowUntestedStellarCli: options.allowUntestedStellarCli
|
|
567
|
+
});
|
|
568
|
+
await assertWasmExists(contract.wasmPath);
|
|
569
|
+
const artifactsBefore = await readArtifacts(cwd);
|
|
570
|
+
const existing = artifactsBefore.networks[network.name]?.contracts[contract.name];
|
|
571
|
+
if (existing?.contractId && !options.force) {
|
|
572
|
+
return {
|
|
573
|
+
contract,
|
|
574
|
+
network,
|
|
575
|
+
contractId: existing.contractId,
|
|
576
|
+
artifactsPath: path5.resolve(cwd, "caatinga.artifacts.json"),
|
|
577
|
+
output: "",
|
|
578
|
+
skipped: true
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
const rawDeployArgs = contract.config.deployArgs;
|
|
582
|
+
const hasConfiguredArgs = Object.keys(rawDeployArgs).length > 0;
|
|
583
|
+
let resolvedDeployArgs;
|
|
584
|
+
if (options.resolvedDeployArgs !== void 0) {
|
|
585
|
+
resolvedDeployArgs = options.resolvedDeployArgs;
|
|
586
|
+
} else if (hasConfiguredArgs) {
|
|
587
|
+
resolvedDeployArgs = resolveDeployArgs({
|
|
588
|
+
deployArgs: rawDeployArgs,
|
|
589
|
+
artifacts: artifactsBefore,
|
|
590
|
+
network: network.name
|
|
591
|
+
});
|
|
592
|
+
} else {
|
|
593
|
+
resolvedDeployArgs = {};
|
|
594
|
+
}
|
|
595
|
+
for (const value of Object.values(resolvedDeployArgs)) {
|
|
596
|
+
if (typeof value === "string" && value.includes("${")) {
|
|
597
|
+
throw new CaatingaError(
|
|
598
|
+
`Deploy args for "${contract.name}" still contain unresolved placeholders.`,
|
|
599
|
+
CaatingaErrorCode.DEPLOY_ARG_PLACEHOLDER_UNRESOLVED,
|
|
600
|
+
"Deploy dependencies first or fix deployArgs templates."
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
const constructorArgs = formatConstructorCliArgs(resolvedDeployArgs);
|
|
605
|
+
const stellarArgs = [
|
|
606
|
+
"contract",
|
|
607
|
+
"deploy",
|
|
608
|
+
"--wasm",
|
|
609
|
+
contract.wasmPath,
|
|
610
|
+
"--source-account",
|
|
611
|
+
source,
|
|
612
|
+
"--rpc-url",
|
|
613
|
+
network.config.rpcUrl,
|
|
614
|
+
"--network-passphrase",
|
|
615
|
+
network.config.networkPassphrase,
|
|
616
|
+
...constructorArgs
|
|
617
|
+
];
|
|
618
|
+
const result = await runCommand("stellar", stellarArgs, {
|
|
619
|
+
cwd,
|
|
620
|
+
allowUntestedStellarCli: options.allowUntestedStellarCli,
|
|
621
|
+
failureCode: CaatingaErrorCode.DEPLOY_FAILED
|
|
622
|
+
});
|
|
623
|
+
const output = result.all || `${result.stdout}
|
|
624
|
+
${result.stderr}`;
|
|
625
|
+
const contractId = parseContractId(output);
|
|
626
|
+
const wasmHash = await hashWasm(contract.wasmPath);
|
|
627
|
+
const dependencyGraph = buildDependencyGraph(options.config.contracts);
|
|
628
|
+
const dependencies = options.dependencies ?? contract.config.dependsOn;
|
|
629
|
+
const nextArtifacts = updateArtifact(
|
|
630
|
+
artifactsBefore,
|
|
631
|
+
network.name,
|
|
632
|
+
contract.name,
|
|
633
|
+
{
|
|
634
|
+
contractId,
|
|
635
|
+
wasmHash,
|
|
636
|
+
deployedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
637
|
+
sourcePath: contract.config.path,
|
|
638
|
+
wasmPath: contract.config.wasm,
|
|
639
|
+
dependencies,
|
|
640
|
+
resolvedDeployArgs
|
|
641
|
+
},
|
|
642
|
+
{ dependencyGraph }
|
|
643
|
+
);
|
|
644
|
+
const artifactsPath = await writeArtifacts(nextArtifacts, cwd);
|
|
645
|
+
return {
|
|
646
|
+
contract,
|
|
647
|
+
network,
|
|
648
|
+
contractId,
|
|
649
|
+
artifactsPath,
|
|
650
|
+
output,
|
|
651
|
+
skipped: false
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// src/contracts/resolve-deploy-order.ts
|
|
656
|
+
function resolveDeployOrder(input) {
|
|
657
|
+
const order = [];
|
|
658
|
+
const state = /* @__PURE__ */ new Map();
|
|
659
|
+
const selected = input.selectedContract ? [input.selectedContract] : Object.keys(input.contracts);
|
|
660
|
+
for (const contractName of selected) {
|
|
661
|
+
visit(contractName, []);
|
|
662
|
+
}
|
|
663
|
+
return order;
|
|
664
|
+
function visit(contractName, stack) {
|
|
665
|
+
const contract = input.contracts[contractName];
|
|
666
|
+
if (!contract) {
|
|
667
|
+
throw new CaatingaError(
|
|
668
|
+
`Contract dependency "${contractName}" was not found.`,
|
|
669
|
+
CaatingaErrorCode.CONTRACT_DEPENDENCY_NOT_FOUND,
|
|
670
|
+
"Add the dependency to caatinga.config.ts or remove it from dependsOn."
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
if (state.get(contractName) === "visited") {
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
if (state.get(contractName) === "visiting") {
|
|
677
|
+
throw new CaatingaError(
|
|
678
|
+
`Contract dependency cycle detected: ${[...stack, contractName].join(" -> ")}.`,
|
|
679
|
+
CaatingaErrorCode.CONTRACT_DEPENDENCY_CYCLE,
|
|
680
|
+
"Remove the cycle from dependsOn."
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
state.set(contractName, "visiting");
|
|
684
|
+
if (input.includeDependencies) {
|
|
685
|
+
for (const dependency of contract.dependsOn) {
|
|
686
|
+
visit(dependency, [...stack, contractName]);
|
|
687
|
+
}
|
|
688
|
+
} else if (contract.dependsOn.length > 0 && input.selectedContract === contractName) {
|
|
689
|
+
for (const dependency of contract.dependsOn) {
|
|
690
|
+
if (!input.contracts[dependency]) {
|
|
691
|
+
throw new CaatingaError(
|
|
692
|
+
`Contract dependency "${dependency}" was not found.`,
|
|
693
|
+
CaatingaErrorCode.CONTRACT_DEPENDENCY_NOT_FOUND,
|
|
694
|
+
"Add the dependency to caatinga.config.ts or remove it from dependsOn."
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
state.set(contractName, "visited");
|
|
700
|
+
if (!order.includes(contractName)) {
|
|
701
|
+
order.push(contractName);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// src/contracts/deploy-contract-graph.ts
|
|
707
|
+
async function deployContractGraph(options) {
|
|
708
|
+
const cwd = options.cwd ?? process.cwd();
|
|
709
|
+
const network = resolveNetwork(options.config, options.networkName);
|
|
710
|
+
const order = resolveDeployOrder({
|
|
711
|
+
contracts: options.config.contracts,
|
|
712
|
+
selectedContract: options.contractName,
|
|
713
|
+
includeDependencies: options.includeDependencies
|
|
714
|
+
});
|
|
715
|
+
const deployedContracts = [];
|
|
716
|
+
for (const contractName of order) {
|
|
717
|
+
const artifacts = await readArtifacts(cwd);
|
|
718
|
+
const existing = artifacts.networks[network.name]?.contracts[contractName];
|
|
719
|
+
const contractConfig = options.config.contracts[contractName];
|
|
720
|
+
const resolvedDeployArgs = resolveDeployArgs({
|
|
721
|
+
deployArgs: contractConfig.deployArgs,
|
|
722
|
+
artifacts,
|
|
723
|
+
network: network.name
|
|
724
|
+
});
|
|
725
|
+
if (existing?.contractId && !options.force) {
|
|
726
|
+
deployedContracts.push({ name: contractName, contractId: existing.contractId });
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
const result = await deployContract({
|
|
730
|
+
config: options.config,
|
|
731
|
+
contractName,
|
|
732
|
+
networkName: network.name,
|
|
733
|
+
source: options.source,
|
|
734
|
+
cwd,
|
|
735
|
+
allowUntestedStellarCli: options.allowUntestedStellarCli,
|
|
736
|
+
force: options.force,
|
|
737
|
+
resolvedDeployArgs,
|
|
738
|
+
dependencies: contractConfig.dependsOn
|
|
739
|
+
});
|
|
740
|
+
deployedContracts.push({ name: contractName, contractId: result.contractId });
|
|
741
|
+
}
|
|
742
|
+
return {
|
|
743
|
+
network,
|
|
744
|
+
deployedContracts
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// src/contracts/generate-bindings.ts
|
|
749
|
+
import { mkdir as mkdir2 } from "fs/promises";
|
|
750
|
+
import path6 from "path";
|
|
751
|
+
async function generateBindings(options) {
|
|
752
|
+
const cwd = options.cwd ?? process.cwd();
|
|
753
|
+
const network = resolveNetwork(options.config, options.networkName);
|
|
754
|
+
const artifacts = await readArtifacts(cwd);
|
|
755
|
+
const contractArtifact = artifacts.networks[network.name]?.contracts[options.contractName];
|
|
756
|
+
if (!contractArtifact) {
|
|
757
|
+
throw new CaatingaError(
|
|
758
|
+
`No deployed artifact found for "${options.contractName}" on "${network.name}".`,
|
|
759
|
+
CaatingaErrorCode.ARTIFACT_NOT_FOUND,
|
|
760
|
+
"Run caatinga deploy for this contract and network before generating bindings."
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
await checkBinary("stellar", "Install Stellar CLI before running caatinga generate.", {
|
|
764
|
+
allowUntestedStellarCli: options.allowUntestedStellarCli
|
|
765
|
+
});
|
|
766
|
+
const outputDir = path6.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
|
|
767
|
+
await mkdir2(outputDir, { recursive: true });
|
|
768
|
+
const result = await runCommand("stellar", [
|
|
769
|
+
"contract",
|
|
770
|
+
"bindings",
|
|
771
|
+
"typescript",
|
|
772
|
+
"--contract-id",
|
|
773
|
+
contractArtifact.contractId,
|
|
774
|
+
"--output-dir",
|
|
775
|
+
outputDir,
|
|
776
|
+
"--overwrite",
|
|
777
|
+
"--rpc-url",
|
|
778
|
+
network.config.rpcUrl,
|
|
779
|
+
"--network-passphrase",
|
|
780
|
+
network.config.networkPassphrase
|
|
781
|
+
], {
|
|
782
|
+
cwd,
|
|
783
|
+
allowUntestedStellarCli: options.allowUntestedStellarCli,
|
|
784
|
+
failureCode: CaatingaErrorCode.BINDINGS_FAILED
|
|
785
|
+
});
|
|
786
|
+
return {
|
|
787
|
+
contractName: options.contractName,
|
|
788
|
+
network,
|
|
789
|
+
outputDir,
|
|
790
|
+
output: result.all || result.stdout
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// src/contracts/invoke-contract.ts
|
|
795
|
+
function parseInvokeTarget(target) {
|
|
796
|
+
const [contractName, method, extra] = target.split(".");
|
|
797
|
+
if (!contractName || !method || extra) {
|
|
798
|
+
throw new CaatingaError(
|
|
799
|
+
`Invalid invoke target "${target}".`,
|
|
800
|
+
CaatingaErrorCode.INVOKE_TARGET_INVALID,
|
|
801
|
+
"Use the format contract.method, for example counter.increment."
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
return { contractName, method };
|
|
805
|
+
}
|
|
806
|
+
async function invokeContract(options) {
|
|
807
|
+
const cwd = options.cwd ?? process.cwd();
|
|
808
|
+
const network = resolveNetwork(options.config, options.networkName);
|
|
809
|
+
const target = parseInvokeTarget(options.target);
|
|
810
|
+
const source = assertSafeSourceAccount(options.source);
|
|
811
|
+
const artifacts = await readArtifacts(cwd);
|
|
812
|
+
const contractArtifact = artifacts.networks[network.name]?.contracts[target.contractName];
|
|
813
|
+
if (!contractArtifact) {
|
|
814
|
+
throw new CaatingaError(
|
|
815
|
+
`No deployed artifact found for "${target.contractName}" on "${network.name}".`,
|
|
816
|
+
CaatingaErrorCode.ARTIFACT_NOT_FOUND,
|
|
817
|
+
"Run caatinga deploy for this contract and network before invoking it."
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
await checkBinary("stellar", "Install Stellar CLI before running caatinga invoke.", {
|
|
821
|
+
allowUntestedStellarCli: options.allowUntestedStellarCli
|
|
822
|
+
});
|
|
823
|
+
const result = await runCommand("stellar", [
|
|
824
|
+
"contract",
|
|
825
|
+
"invoke",
|
|
826
|
+
"--id",
|
|
827
|
+
contractArtifact.contractId,
|
|
828
|
+
"--source-account",
|
|
829
|
+
source,
|
|
830
|
+
"--rpc-url",
|
|
831
|
+
network.config.rpcUrl,
|
|
832
|
+
"--network-passphrase",
|
|
833
|
+
network.config.networkPassphrase,
|
|
834
|
+
"--",
|
|
835
|
+
target.method,
|
|
836
|
+
...options.args ?? []
|
|
837
|
+
], {
|
|
838
|
+
cwd,
|
|
839
|
+
allowUntestedStellarCli: options.allowUntestedStellarCli,
|
|
840
|
+
failureCode: CaatingaErrorCode.INVOKE_FAILED
|
|
841
|
+
});
|
|
842
|
+
return {
|
|
843
|
+
target,
|
|
844
|
+
network,
|
|
845
|
+
result: result.stdout || result.all
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
// src/templates/create-project-from-template.ts
|
|
850
|
+
import { cp, mkdir as mkdir3, readFile as readFile3, readdir, stat, writeFile as writeFile2 } from "fs/promises";
|
|
851
|
+
import path7 from "path";
|
|
852
|
+
import { z as z6 } from "zod";
|
|
853
|
+
|
|
854
|
+
// src/templates/template-manifest.schema.ts
|
|
855
|
+
import { z as z5 } from "zod";
|
|
856
|
+
import semver2 from "semver";
|
|
857
|
+
var CURRENT_TEMPLATE_VERSION = 1;
|
|
858
|
+
var TemplateManifestSchema = z5.object({
|
|
859
|
+
name: z5.string().min(1),
|
|
860
|
+
version: z5.string().min(1),
|
|
861
|
+
description: z5.string().optional(),
|
|
862
|
+
caatinga: z5.object({
|
|
863
|
+
compatibleCore: z5.string().min(1),
|
|
864
|
+
templateVersion: z5.number().int().positive()
|
|
865
|
+
}),
|
|
866
|
+
frontend: z5.object({
|
|
867
|
+
framework: z5.enum(["vite-react", "next", "astro"]),
|
|
868
|
+
packageManager: z5.enum(["npm", "pnpm", "yarn", "bun"]).default("npm")
|
|
869
|
+
}),
|
|
870
|
+
contracts: z5.object({
|
|
871
|
+
path: z5.string(),
|
|
872
|
+
default: z5.string().optional()
|
|
873
|
+
}),
|
|
874
|
+
files: z5.object({
|
|
875
|
+
config: z5.string().default("caatinga.config.ts"),
|
|
876
|
+
artifacts: z5.string().default("caatinga.artifacts.json")
|
|
877
|
+
})
|
|
878
|
+
});
|
|
879
|
+
function isCoreVersionCompatible(range, coreVersion = CAATINGA_CORE_VERSION) {
|
|
880
|
+
return semver2.satisfies(coreVersion, range);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// src/templates/create-project-from-template.ts
|
|
884
|
+
async function createProjectFromTemplate(options) {
|
|
885
|
+
const targetDir = path7.resolve(options.targetDir);
|
|
886
|
+
const templateDir = path7.resolve(options.templateDir);
|
|
887
|
+
try {
|
|
888
|
+
await stat(templateDir);
|
|
889
|
+
} catch {
|
|
890
|
+
throw new CaatingaError(
|
|
891
|
+
`Template directory was not found: ${templateDir}`,
|
|
892
|
+
CaatingaErrorCode.TEMPLATE_NOT_FOUND,
|
|
893
|
+
"Use a bundled Caatinga template or set CAATINGA_TEMPLATES_DIR for local development."
|
|
894
|
+
);
|
|
895
|
+
}
|
|
896
|
+
const manifest = await readTemplateManifest(templateDir);
|
|
897
|
+
await mkdir3(targetDir, { recursive: true });
|
|
898
|
+
await cp(templateDir, targetDir, {
|
|
899
|
+
recursive: true,
|
|
900
|
+
force: false,
|
|
901
|
+
errorOnExist: true
|
|
902
|
+
});
|
|
903
|
+
await replaceTemplateVariables(targetDir, options.projectName);
|
|
904
|
+
await writeArtifacts(createInitialArtifacts(options.projectName), targetDir);
|
|
905
|
+
return { targetDir, template: manifest };
|
|
906
|
+
}
|
|
907
|
+
async function readTemplateManifest(templateDir) {
|
|
908
|
+
const manifestPath = path7.join(templateDir, "caatinga.template.json");
|
|
909
|
+
try {
|
|
910
|
+
const rawManifest = await readFile3(manifestPath, "utf8");
|
|
911
|
+
const manifest = TemplateManifestSchema.parse(JSON.parse(rawManifest));
|
|
912
|
+
if (manifest.caatinga.templateVersion !== CURRENT_TEMPLATE_VERSION) {
|
|
913
|
+
throw new CaatingaError(
|
|
914
|
+
"Template is not compatible with this Caatinga version.",
|
|
915
|
+
CaatingaErrorCode.TEMPLATE_INCOMPATIBLE,
|
|
916
|
+
"Use a compatible template version or upgrade Caatinga."
|
|
917
|
+
);
|
|
918
|
+
}
|
|
919
|
+
if (!isCoreVersionCompatible(manifest.caatinga.compatibleCore)) {
|
|
920
|
+
throw new CaatingaError(
|
|
921
|
+
"Template is not compatible with this Caatinga version.",
|
|
922
|
+
CaatingaErrorCode.TEMPLATE_INCOMPATIBLE,
|
|
923
|
+
"Use a compatible template version or upgrade Caatinga."
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
return manifest;
|
|
927
|
+
} catch (error) {
|
|
928
|
+
if (error.code === "ENOENT") {
|
|
929
|
+
throw new CaatingaError(
|
|
930
|
+
"Template manifest was not found.",
|
|
931
|
+
CaatingaErrorCode.TEMPLATE_MANIFEST_NOT_FOUND,
|
|
932
|
+
"Add a caatinga.template.json file to the template root."
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
if (error instanceof CaatingaError) {
|
|
936
|
+
throw error;
|
|
937
|
+
}
|
|
938
|
+
if (error instanceof SyntaxError || error instanceof z6.ZodError) {
|
|
939
|
+
throw new CaatingaError(
|
|
940
|
+
"Template manifest is invalid.",
|
|
941
|
+
CaatingaErrorCode.INVALID_TEMPLATE_MANIFEST,
|
|
942
|
+
"Fix caatinga.template.json so it is valid JSON and matches the template manifest schema."
|
|
943
|
+
);
|
|
944
|
+
}
|
|
945
|
+
throw error;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
async function replaceTemplateVariables(dir, projectName) {
|
|
949
|
+
const entries = await readdir(dir);
|
|
950
|
+
await Promise.all(entries.map(async (entry) => {
|
|
951
|
+
const entryPath = path7.join(dir, entry);
|
|
952
|
+
const entryStat = await stat(entryPath);
|
|
953
|
+
if (entryStat.isDirectory()) {
|
|
954
|
+
await replaceTemplateVariables(entryPath, projectName);
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
if (!isTextTemplateFile(entryPath)) {
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
960
|
+
const content = await readFile3(entryPath, "utf8");
|
|
961
|
+
await writeFile2(entryPath, content.replaceAll("__PROJECT_NAME__", projectName), "utf8");
|
|
962
|
+
}));
|
|
963
|
+
}
|
|
964
|
+
function isTextTemplateFile(filePath) {
|
|
965
|
+
return [
|
|
966
|
+
".json",
|
|
967
|
+
".md",
|
|
968
|
+
".rs",
|
|
969
|
+
".toml",
|
|
970
|
+
".ts",
|
|
971
|
+
".tsx",
|
|
972
|
+
".css",
|
|
973
|
+
".html"
|
|
974
|
+
].includes(path7.extname(filePath));
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// src/ci/is-transient-testnet-smoke-failure.ts
|
|
978
|
+
var NO_RETRY_CAATINGA_SUBSTRINGS = [
|
|
979
|
+
"CAATINGA_UNSUPPORTED_CLI_VERSION",
|
|
980
|
+
"CAATINGA_UNTESTED_CLI_VERSION",
|
|
981
|
+
"CAATINGA_STELLAR_CLI_VERSION_PARSE_FAILED",
|
|
982
|
+
"CAATINGA_STELLAR_CLI_NOT_FOUND",
|
|
983
|
+
"CAATINGA_INVALID_CONFIG",
|
|
984
|
+
"CAATINGA_CONFIG_NOT_FOUND"
|
|
985
|
+
];
|
|
986
|
+
var TRANSIENT_PATTERN = /timeout|i\/o timeout|econnreset|connection reset|503|502|429|rate limit|temporar|bad gateway|fetch failed|network error|unavailable/i;
|
|
987
|
+
function isTransientTestnetSmokeFailure(logText) {
|
|
988
|
+
if (!logText.trim()) {
|
|
989
|
+
return false;
|
|
990
|
+
}
|
|
991
|
+
for (const marker of NO_RETRY_CAATINGA_SUBSTRINGS) {
|
|
992
|
+
if (logText.includes(marker)) {
|
|
993
|
+
return false;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
return TRANSIENT_PATTERN.test(logText);
|
|
997
|
+
}
|
|
998
|
+
export {
|
|
999
|
+
CAATINGA_CORE_VERSION,
|
|
1000
|
+
CaatingaArtifactsSchema,
|
|
1001
|
+
CaatingaConfigSchema,
|
|
1002
|
+
CaatingaError,
|
|
1003
|
+
CaatingaErrorCode,
|
|
1004
|
+
STELLAR_CLI_MIN_VERSION,
|
|
1005
|
+
STELLAR_CLI_TESTED_MAX_VERSION,
|
|
1006
|
+
TemplateManifestSchema,
|
|
1007
|
+
WELL_KNOWN_NETWORKS,
|
|
1008
|
+
assertSupportedStellarCliVersion,
|
|
1009
|
+
buildContract,
|
|
1010
|
+
buildDependencyGraph,
|
|
1011
|
+
checkBinary,
|
|
1012
|
+
createInitialArtifacts,
|
|
1013
|
+
createProjectFromTemplate,
|
|
1014
|
+
defineConfig,
|
|
1015
|
+
deployContract,
|
|
1016
|
+
deployContractGraph,
|
|
1017
|
+
generateBindings,
|
|
1018
|
+
invokeContract,
|
|
1019
|
+
isTransientTestnetSmokeFailure,
|
|
1020
|
+
loadConfig,
|
|
1021
|
+
parseContractId,
|
|
1022
|
+
parseInvokeTarget,
|
|
1023
|
+
parseStellarCliVersion,
|
|
1024
|
+
readArtifacts,
|
|
1025
|
+
resolveContract,
|
|
1026
|
+
resolveDeployArgs,
|
|
1027
|
+
resolveDeployOrder,
|
|
1028
|
+
resolveNetwork,
|
|
1029
|
+
runCommand,
|
|
1030
|
+
toCaatingaError,
|
|
1031
|
+
updateArtifact,
|
|
1032
|
+
writeArtifacts
|
|
1033
|
+
};
|