From 68ff3a8044d97618612420aa647207bc120a0278 Mon Sep 17 00:00:00 2001 From: satoshai-dev <262845409+satoshai-dev@users.noreply.github.com> Date: Tue, 24 Feb 2026 18:26:56 +0000 Subject: [PATCH] fix: add CLI validation and improve error handling - error on --output with multiple contracts (prevents silent overwrite) - error on --stdout with multiple contracts (prevents invalid output) - wrap fetch() to provide context on network errors - wrap response.json() to handle malformed API responses Closes #1, closes #2, closes #3, closes #4 Co-Authored-By: Claude Opus 4.6 --- .changeset/fix-cli-validation.md | 5 +++++ src/commands/fetch.ts | 12 ++++++++++++ src/fetcher.ts | 19 +++++++++++++++++-- 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 .changeset/fix-cli-validation.md diff --git a/.changeset/fix-cli-validation.md b/.changeset/fix-cli-validation.md new file mode 100644 index 0000000..4920343 --- /dev/null +++ b/.changeset/fix-cli-validation.md @@ -0,0 +1,5 @@ +--- +"@satoshai/abi-cli": patch +--- + +Add CLI validation for --output/--stdout with multiple contracts and improve fetch error messages diff --git a/src/commands/fetch.ts b/src/commands/fetch.ts index d03a7a0..528081d 100644 --- a/src/commands/fetch.ts +++ b/src/commands/fetch.ts @@ -50,6 +50,18 @@ export const fetchCommand = defineCommand({ const contractIds = args.contract.split(',').map((s) => s.trim()); + if (args.output && contractIds.length > 1) { + throw new Error( + '--output cannot be used with multiple contracts. Omit --output to write separate files, or use --stdout.', + ); + } + + if (args.stdout && contractIds.length > 1) { + throw new Error( + '--stdout cannot be used with multiple contracts. Fetch one contract at a time with --stdout.', + ); + } + for (const contractId of contractIds) { const { address, name } = parseContractId(contractId); diff --git a/src/fetcher.ts b/src/fetcher.ts index 18a1523..18de543 100644 --- a/src/fetcher.ts +++ b/src/fetcher.ts @@ -31,7 +31,15 @@ export async function fetchContractAbi( const baseUrl = resolveNetwork(network); const url = `${baseUrl}/v2/contracts/interface/${address}/${name}`; - const response = await fetch(url); + let response: Response; + try { + response = await fetch(url); + } catch (cause) { + throw new Error( + `Network error fetching ABI for ${address}.${name} on ${network}`, + { cause }, + ); + } if (!response.ok) { if (response.status === 404) { @@ -44,5 +52,12 @@ export async function fetchContractAbi( ); } - return (await response.json()) as ClarityAbi; + try { + return (await response.json()) as ClarityAbi; + } catch (cause) { + throw new Error( + `Invalid JSON response for ${address}.${name} on ${network}`, + { cause }, + ); + } }