@secondlayer/cli 0.3.8 → 0.3.10

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/cli.js CHANGED
@@ -7539,6 +7539,35 @@ var require_out4 = __commonJS((exports, module) => {
7539
7539
  module.exports = FastGlob;
7540
7540
  });
7541
7541
 
7542
+ // src/types/plugin.ts
7543
+ function isClarinetContract(c) {
7544
+ return "_clarinetSource" in c && c._clarinetSource === true;
7545
+ }
7546
+ function isDirectFileContract(c) {
7547
+ return "_directFile" in c && c._directFile === true;
7548
+ }
7549
+
7550
+ // src/utils/format.ts
7551
+ var exports_format = {};
7552
+ __export(exports_format, {
7553
+ formatCode: () => formatCode,
7554
+ PRETTIER_OPTIONS: () => PRETTIER_OPTIONS
7555
+ });
7556
+ import { format } from "prettier";
7557
+ async function formatCode(code) {
7558
+ return format(code, PRETTIER_OPTIONS);
7559
+ }
7560
+ var PRETTIER_OPTIONS;
7561
+ var init_format = __esm(() => {
7562
+ PRETTIER_OPTIONS = {
7563
+ parser: "typescript",
7564
+ singleQuote: true,
7565
+ semi: true,
7566
+ printWidth: 100,
7567
+ trailingComma: "es5"
7568
+ };
7569
+ });
7570
+
7542
7571
  // src/core/plugin-manager.ts
7543
7572
  import { promises as fs } from "fs";
7544
7573
  import path from "path";
@@ -7603,7 +7632,7 @@ class PluginManager {
7603
7632
  async transformContracts(contracts, _config) {
7604
7633
  const processedContracts = [];
7605
7634
  for (let contract of contracts) {
7606
- if (contract._clarinetSource && contract.abi) {
7635
+ if (isClarinetContract(contract) && contract.abi) {
7607
7636
  const address = typeof contract.address === "string" ? contract.address : "";
7608
7637
  const [contractAddress, contractName] = address.split(".");
7609
7638
  const processed = {
@@ -7617,7 +7646,7 @@ class PluginManager {
7617
7646
  processedContracts.push(processed);
7618
7647
  continue;
7619
7648
  }
7620
- if (contract._directFile && contract.abi) {
7649
+ if (isDirectFileContract(contract) && contract.abi) {
7621
7650
  const address = typeof contract.address === "string" ? contract.address : "";
7622
7651
  const [contractAddress, contractName] = address.split(".");
7623
7652
  const processed = {
@@ -7803,14 +7832,8 @@ ${JSON.stringify(content, null, 2)}`;
7803
7832
  return { address, contractName };
7804
7833
  },
7805
7834
  formatCode: async (code) => {
7806
- const { format } = await import("prettier");
7807
- return format(code, {
7808
- parser: "typescript",
7809
- singleQuote: true,
7810
- semi: true,
7811
- printWidth: 100,
7812
- trailingComma: "es5"
7813
- });
7835
+ const { formatCode: formatCode2 } = await Promise.resolve().then(() => (init_format(), exports_format));
7836
+ return formatCode2(code);
7814
7837
  },
7815
7838
  resolvePath: (relativePath) => {
7816
7839
  return path.resolve(process.cwd(), relativePath);
@@ -7871,7 +7894,7 @@ async function loadConfig(configPath) {
7871
7894
  const indexPath = path2.resolve(currentModuleDir, "../index");
7872
7895
  replacementPath = pathToFileURL(indexPath).href;
7873
7896
  }
7874
- const transformedCode = code.replace(/from\s+["']@stacks\/cli["']/g, `from '${replacementPath}'`);
7897
+ const transformedCode = code.replace(/from\s+["']@secondlayer\/cli["']/g, `from '${replacementPath}'`);
7875
7898
  const { transformSync } = await import("esbuild");
7876
7899
  const result = transformSync(transformedCode, {
7877
7900
  format: "esm",
@@ -15561,11 +15584,18 @@ var init_source4 = __esm(() => {
15561
15584
 
15562
15585
  // src/utils/api.ts
15563
15586
  class StacksApiClient {
15587
+ static hasWarnedAboutApiKey = false;
15564
15588
  baseUrl;
15565
15589
  headers;
15566
15590
  constructor(network = "mainnet", apiKey, apiUrl) {
15567
15591
  this.baseUrl = apiUrl || API_URLS[network];
15568
15592
  this.headers = apiKey ? { "x-api-key": apiKey } : {};
15593
+ if (!apiKey && !StacksApiClient.hasWarnedAboutApiKey) {
15594
+ console.warn(`⚠️ No API key provided. You may be rate-limited.
15595
+ ` + ` Set HIRO_API_KEY env var or use --api-key flag.
15596
+ ` + " Get a free key at: https://platform.hiro.so/");
15597
+ StacksApiClient.hasWarnedAboutApiKey = true;
15598
+ }
15569
15599
  }
15570
15600
  async getContractInfo(contractId) {
15571
15601
  const [address, contractName] = contractId.split(".");
@@ -15574,7 +15604,7 @@ class StacksApiClient {
15574
15604
  }
15575
15605
  const url = `${this.baseUrl}/v2/contracts/interface/${address}/${contractName}`;
15576
15606
  try {
15577
- const response2 = await source_default3(url, {
15607
+ const response2 = await gotWithRetry(url, {
15578
15608
  headers: this.headers,
15579
15609
  responseType: "json"
15580
15610
  });
@@ -15596,20 +15626,35 @@ class StacksApiClient {
15596
15626
  }
15597
15627
  const url = `${this.baseUrl}/v2/contracts/source/${address}/${contractName}`;
15598
15628
  try {
15599
- const response2 = await source_default3(url, {
15629
+ const response2 = await gotWithRetry(url, {
15600
15630
  headers: this.headers,
15601
15631
  responseType: "json"
15602
15632
  });
15603
15633
  const data = response2.body;
15604
15634
  return data.source;
15605
15635
  } catch (error) {
15606
- return "";
15636
+ if (error.response?.statusCode === 404) {
15637
+ throw new Error(`Contract source not found: ${contractId}`);
15638
+ }
15639
+ if (error.response?.statusCode === 429) {
15640
+ throw new Error("Rate limited. Please provide an API key in your config.");
15641
+ }
15642
+ throw new Error(`Failed to fetch contract source: ${error.message}`);
15607
15643
  }
15608
15644
  }
15609
15645
  }
15610
- var API_URLS;
15646
+ var gotWithRetry, API_URLS;
15611
15647
  var init_api = __esm(() => {
15612
15648
  init_source4();
15649
+ gotWithRetry = source_default3.extend({
15650
+ timeout: { request: 30000 },
15651
+ retry: {
15652
+ limit: 3,
15653
+ methods: ["GET", "POST"],
15654
+ statusCodes: [408, 429, 500, 502, 503, 504],
15655
+ calculateDelay: ({ attemptCount }) => attemptCount * 1000
15656
+ }
15657
+ });
15613
15658
  API_URLS = {
15614
15659
  mainnet: "https://api.hiro.so",
15615
15660
  testnet: "https://api.testnet.hiro.so",
@@ -15637,7 +15682,7 @@ function normalizeType(type) {
15637
15682
  }
15638
15683
  }
15639
15684
  if (typeof type !== "object" || type === null) {
15640
- return "uint128";
15685
+ throw new Error(`Invalid ABI type: expected object, got ${typeof type}`);
15641
15686
  }
15642
15687
  const typeObj = type;
15643
15688
  if ("buffer" in typeObj) {
@@ -15704,7 +15749,7 @@ function normalizeType(type) {
15704
15749
  }))
15705
15750
  };
15706
15751
  }
15707
- return "uint128";
15752
+ throw new Error(`Unknown ABI type structure: ${JSON.stringify(type)}`);
15708
15753
  }
15709
15754
  function normalizeFunction(func) {
15710
15755
  const access = normalizeAccess(func.access);
@@ -15773,8 +15818,17 @@ function normalizeAbi(abi) {
15773
15818
  // src/parsers/clarity.ts
15774
15819
  import { promises as fs3 } from "fs";
15775
15820
  async function parseClarityFile(filePath) {
15776
- const content = await fs3.readFile(filePath, "utf-8");
15777
- return parseClarityContent(content);
15821
+ try {
15822
+ const content = await fs3.readFile(filePath, "utf-8");
15823
+ const result = parseClarityContent(content);
15824
+ if (result.functions.length === 0) {
15825
+ console.warn(`⚠️ No functions found in ${filePath}. ` + `For complex contracts, deploy first and use the contract address instead.`);
15826
+ }
15827
+ return result;
15828
+ } catch (error) {
15829
+ throw new Error(`Unable to parse ${filePath}. ` + `For complex contracts, deploy first and use the contract address instead.
15830
+ ` + `Original error: ${error}`);
15831
+ }
15778
15832
  }
15779
15833
  function parseClarityContent(content) {
15780
15834
  const functions = [];
@@ -15940,7 +15994,6 @@ function getTypeForArg(arg) {
15940
15994
  var init_type_mapping = () => {};
15941
15995
 
15942
15996
  // src/generators/contract.ts
15943
- import { format } from "prettier";
15944
15997
  import {
15945
15998
  toCamelCase as toCamelCase2
15946
15999
  } from "@secondlayer/clarity-types";
@@ -16004,14 +16057,7 @@ ${validationUtils}
16004
16057
  ${networkUtils}
16005
16058
 
16006
16059
  ${contractsCode}`;
16007
- const formatted = await format(code, {
16008
- parser: "typescript",
16009
- singleQuote: true,
16010
- semi: true,
16011
- printWidth: 100,
16012
- trailingComma: "es5"
16013
- });
16014
- return formatted;
16060
+ return formatCode(code);
16015
16061
  }
16016
16062
  function generateContract(contract) {
16017
16063
  const { name, address, contractName, abi } = contract;
@@ -16241,34 +16287,41 @@ function generateMapsObject(maps, address, contractName) {
16241
16287
  const keyConversion = generateMapKeyConversion(map.key);
16242
16288
  return `${methodName}: {
16243
16289
  async get(key: ${keyType}, options?: { network?: 'mainnet' | 'testnet' | 'devnet' }): Promise<${valueType} | null> {
16244
- const { cvToJSON, serializeCV } = await import('@stacks/transactions');
16245
- const baseUrl = getApiUrl('${address}', options?.network);
16246
- const mapKey = ${keyConversion};
16247
- const keyHex = serializeCV(mapKey).toString('hex');
16248
-
16249
- const response = await fetch(
16250
- \`\${baseUrl}/v2/map_entry/${address}/${contractName}/${map.name}\`,
16251
- {
16252
- method: 'POST',
16253
- headers: { 'Content-Type': 'application/json' },
16254
- body: JSON.stringify(keyHex)
16290
+ try {
16291
+ const { cvToJSON, serializeCV } = await import('@stacks/transactions');
16292
+ const baseUrl = getApiUrl('${address}', options?.network);
16293
+ const mapKey = ${keyConversion};
16294
+ const keyHex = serializeCV(mapKey).toString('hex');
16295
+
16296
+ const response = await fetch(
16297
+ \`\${baseUrl}/v2/map_entry/${address}/${contractName}/${map.name}\`,
16298
+ {
16299
+ method: 'POST',
16300
+ headers: { 'Content-Type': 'application/json' },
16301
+ body: JSON.stringify(keyHex)
16302
+ }
16303
+ );
16304
+
16305
+ if (!response.ok) {
16306
+ throw new Error(\`HTTP \${response.status}: \${response.statusText}\`);
16255
16307
  }
16256
- );
16257
16308
 
16258
- if (!response.ok) {
16259
- throw new Error(\`Failed to fetch map entry: \${response.statusText}\`);
16260
- }
16309
+ const result = await response.json();
16310
+ if (!result.data || result.data === '0x09') {
16311
+ return null; // none value
16312
+ }
16261
16313
 
16262
- const result = await response.json();
16263
- if (!result.data || result.data === '0x09') {
16264
- return null; // none value
16314
+ const { deserializeCV } = await import('@stacks/transactions');
16315
+ const cv = deserializeCV(result.data);
16316
+ const parsed = cvToJSON(cv);
16317
+ // Unwrap the (some ...) wrapper
16318
+ return parsed.value?.value ?? parsed.value ?? null;
16319
+ } catch (error) {
16320
+ if (error instanceof Error) {
16321
+ throw new Error(\`Map access failed for '${map.name}': \${error.message}\`);
16322
+ }
16323
+ throw error;
16265
16324
  }
16266
-
16267
- const { deserializeCV } = await import('@stacks/transactions');
16268
- const cv = deserializeCV(result.data);
16269
- const parsed = cvToJSON(cv);
16270
- // Unwrap the (some ...) wrapper
16271
- return parsed.value?.value ?? parsed.value ?? null;
16272
16325
  },
16273
16326
  keyType: ${JSON.stringify(map.key)} as const,
16274
16327
  valueType: ${JSON.stringify(map.value)} as const
@@ -16293,21 +16346,28 @@ function generateVarsObject(variables, address, contractName) {
16293
16346
  const valueType = getTypeForArg({ type: variable.type });
16294
16347
  return `${methodName}: {
16295
16348
  async get(options?: { network?: 'mainnet' | 'testnet' | 'devnet' }): Promise<${valueType}> {
16296
- const { cvToJSON, deserializeCV } = await import('@stacks/transactions');
16297
- const baseUrl = getApiUrl('${address}', options?.network);
16349
+ try {
16350
+ const { cvToJSON, deserializeCV } = await import('@stacks/transactions');
16351
+ const baseUrl = getApiUrl('${address}', options?.network);
16298
16352
 
16299
- const response = await fetch(
16300
- \`\${baseUrl}/v2/data_var/${address}/${contractName}/${variable.name}?proof=0\`
16301
- );
16353
+ const response = await fetch(
16354
+ \`\${baseUrl}/v2/data_var/${address}/${contractName}/${variable.name}?proof=0\`
16355
+ );
16302
16356
 
16303
- if (!response.ok) {
16304
- throw new Error(\`Failed to fetch data var: \${response.statusText}\`);
16305
- }
16357
+ if (!response.ok) {
16358
+ throw new Error(\`HTTP \${response.status}: \${response.statusText}\`);
16359
+ }
16306
16360
 
16307
- const result = await response.json();
16308
- const cv = deserializeCV(result.data);
16309
- const parsed = cvToJSON(cv);
16310
- return parsed.value ?? parsed;
16361
+ const result = await response.json();
16362
+ const cv = deserializeCV(result.data);
16363
+ const parsed = cvToJSON(cv);
16364
+ return parsed.value ?? parsed;
16365
+ } catch (error) {
16366
+ if (error instanceof Error) {
16367
+ throw new Error(\`Variable access failed for '${variable.name}': \${error.message}\`);
16368
+ }
16369
+ throw error;
16370
+ }
16311
16371
  },
16312
16372
  type: ${JSON.stringify(variable.type)} as const
16313
16373
  }`;
@@ -16331,21 +16391,28 @@ function generateConstantsObject(variables, address, contractName) {
16331
16391
  const valueType = getTypeForArg({ type: constant.type });
16332
16392
  return `${methodName}: {
16333
16393
  async get(options?: { network?: 'mainnet' | 'testnet' | 'devnet' }): Promise<${valueType}> {
16334
- const { cvToJSON, deserializeCV } = await import('@stacks/transactions');
16335
- const baseUrl = getApiUrl('${address}', options?.network);
16394
+ try {
16395
+ const { cvToJSON, deserializeCV } = await import('@stacks/transactions');
16396
+ const baseUrl = getApiUrl('${address}', options?.network);
16336
16397
 
16337
- const response = await fetch(
16338
- \`\${baseUrl}/v2/constant_val/${address}/${contractName}/${constant.name}?proof=0\`
16339
- );
16398
+ const response = await fetch(
16399
+ \`\${baseUrl}/v2/constant_val/${address}/${contractName}/${constant.name}?proof=0\`
16400
+ );
16340
16401
 
16341
- if (!response.ok) {
16342
- throw new Error(\`Failed to fetch constant: \${response.statusText}\`);
16343
- }
16402
+ if (!response.ok) {
16403
+ throw new Error(\`HTTP \${response.status}: \${response.statusText}\`);
16404
+ }
16344
16405
 
16345
- const result = await response.json();
16346
- const cv = deserializeCV(result.data);
16347
- const parsed = cvToJSON(cv);
16348
- return parsed.value ?? parsed;
16406
+ const result = await response.json();
16407
+ const cv = deserializeCV(result.data);
16408
+ const parsed = cvToJSON(cv);
16409
+ return parsed.value ?? parsed;
16410
+ } catch (error) {
16411
+ if (error instanceof Error) {
16412
+ throw new Error(\`Constant access failed for '${constant.name}': \${error.message}\`);
16413
+ }
16414
+ throw error;
16415
+ }
16349
16416
  },
16350
16417
  type: ${JSON.stringify(constant.type)} as const
16351
16418
  }`;
@@ -16368,6 +16435,7 @@ function generateMapKeyConversion(keyType) {
16368
16435
  return generateClarityConversion("key", { type: keyType });
16369
16436
  }
16370
16437
  var init_contract = __esm(() => {
16438
+ init_format();
16371
16439
  init_type_mapping();
16372
16440
  });
16373
16441
 
@@ -27929,6 +27997,7 @@ __export(exports_generate, {
27929
27997
  generate: () => generate
27930
27998
  });
27931
27999
  import path11 from "path";
28000
+ import { toCamelCase as toCamelCase3 } from "@secondlayer/clarity-types";
27932
28001
  function isContractAddress(input) {
27933
28002
  const contractIdPattern = /^(SP|ST|SM|SN)[A-Z0-9]{38,}\.[a-zA-Z][a-zA-Z0-9-]*$/;
27934
28003
  return contractIdPattern.test(input);
@@ -27970,17 +28039,20 @@ function deriveContractName(filePath) {
27970
28039
  const basename = path11.basename(filePath, ".clar");
27971
28040
  return basename.replace(/[-_](.)/g, (_2, char) => char.toUpperCase()).replace(/^(.)/, (_2, char) => char.toLowerCase()).replace(/^\d/, "_$&");
27972
28041
  }
27973
- function toCamelCase3(str) {
27974
- return str.replace(/[-_](.)/g, (_2, char) => char.toUpperCase()).replace(/^(.)/, (_2, char) => char.toLowerCase()).replace(/^\d/, "_$&");
27975
- }
27976
- async function buildConfigFromInputs(parsedInputs, outPath, apiKey) {
28042
+ async function buildConfigFromInputs(parsedInputs, outPath, apiKey, defaultAddress) {
27977
28043
  const contracts = [];
28044
+ const deployer = defaultAddress || DEFAULT_DEVNET_ADDRESS;
28045
+ if (parsedInputs.files.length > 0 && !defaultAddress) {
28046
+ console.warn(source_default.yellow(`⚠️ Using placeholder address (${deployer}) for local contracts.
28047
+ ` + ` Generated contract addresses won't match deployed addresses.
28048
+ ` + ` Set defaultAddress in config or use deployed contract addresses.`));
28049
+ }
27978
28050
  for (const file of parsedInputs.files) {
27979
28051
  const abi = await parseClarityFile(file);
27980
28052
  const name = deriveContractName(file);
27981
28053
  contracts.push({
27982
28054
  name,
27983
- address: `ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.${name}`,
28055
+ address: `${deployer}.${name}`,
27984
28056
  abi,
27985
28057
  _directFile: true
27986
28058
  });
@@ -28094,7 +28166,7 @@ async function resolveContract(source, network, apiKey, apiUrl) {
28094
28166
  const filePath = path11.resolve(process.cwd(), source.source);
28095
28167
  const abi = await parseClarityFile(filePath);
28096
28168
  const name = source.name || path11.basename(source.source, ".clar").replace(/-/g, "_").replace(/^\d/, "_$&");
28097
- const address = typeof source.address === "string" ? source.address : source.address?.[network] || "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM";
28169
+ const address = typeof source.address === "string" ? source.address : source.address?.[network] || DEFAULT_DEVNET_ADDRESS;
28098
28170
  const [contractAddress, contractName] = address.includes(".") ? address.split(".") : [address, name];
28099
28171
  return {
28100
28172
  name,
@@ -28157,7 +28229,7 @@ async function resolveContracts(source, defaultNetwork, apiKey, apiUrl) {
28157
28229
  }
28158
28230
  throw new Error("Contract must have either address or source");
28159
28231
  }
28160
- var import_fast_glob;
28232
+ var import_fast_glob, DEFAULT_DEVNET_ADDRESS = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM";
28161
28233
  var init_generate = __esm(() => {
28162
28234
  init_source();
28163
28235
  init_config();
@@ -28237,7 +28309,7 @@ var {
28237
28309
  // package.json
28238
28310
  var package_default = {
28239
28311
  name: "@secondlayer/cli",
28240
- version: "0.3.8",
28312
+ version: "0.3.10",
28241
28313
  description: "CLI for generating type-safe contract interfaces for the Stacks blockchain",
28242
28314
  type: "module",
28243
28315
  bin: {
@@ -28276,7 +28348,7 @@ var package_default = {
28276
28348
  author: "",
28277
28349
  license: "MIT",
28278
28350
  dependencies: {
28279
- "@secondlayer/clarity-types": "^0.4.1",
28351
+ "@secondlayer/clarity-types": "^0.5.0",
28280
28352
  "@stacks/transactions": "7.0.6",
28281
28353
  esbuild: "^0.19.0",
28282
28354
  prettier: "^3.1.0"
@@ -28313,5 +28385,5 @@ program.command("init").description("Initialize a new stacks.config.ts file").ac
28313
28385
  });
28314
28386
  program.parse();
28315
28387
 
28316
- //# debugId=7DA8ED9A30BA795D64756E2164756E21
28388
+ //# debugId=B1F6E649C8DA5F5D64756E2164756E21
28317
28389
  //# sourceMappingURL=cli.js.map