@secondlayer/cli 0.3.7 → 0.3.9

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 = [];
@@ -15887,6 +15941,9 @@ function clarityTypeToTS(type) {
15887
15941
  return "string";
15888
15942
  default: {
15889
15943
  const typeStr = type;
15944
+ if (typeStr === "none") {
15945
+ return "null";
15946
+ }
15890
15947
  if (typeStr.includes("string") || typeStr.includes("ascii") || typeStr.includes("utf8")) {
15891
15948
  return "string";
15892
15949
  }
@@ -15937,7 +15994,6 @@ function getTypeForArg(arg) {
15937
15994
  var init_type_mapping = () => {};
15938
15995
 
15939
15996
  // src/generators/contract.ts
15940
- import { format } from "prettier";
15941
15997
  import {
15942
15998
  toCamelCase as toCamelCase2
15943
15999
  } from "@secondlayer/clarity-types";
@@ -16001,14 +16057,7 @@ ${validationUtils}
16001
16057
  ${networkUtils}
16002
16058
 
16003
16059
  ${contractsCode}`;
16004
- const formatted = await format(code, {
16005
- parser: "typescript",
16006
- singleQuote: true,
16007
- semi: true,
16008
- printWidth: 100,
16009
- trailingComma: "es5"
16010
- });
16011
- return formatted;
16060
+ return formatCode(code);
16012
16061
  }
16013
16062
  function generateContract(contract) {
16014
16063
  const { name, address, contractName, abi } = contract;
@@ -16238,34 +16287,41 @@ function generateMapsObject(maps, address, contractName) {
16238
16287
  const keyConversion = generateMapKeyConversion(map.key);
16239
16288
  return `${methodName}: {
16240
16289
  async get(key: ${keyType}, options?: { network?: 'mainnet' | 'testnet' | 'devnet' }): Promise<${valueType} | null> {
16241
- const { cvToJSON, serializeCV } = await import('@stacks/transactions');
16242
- const baseUrl = getApiUrl('${address}', options?.network);
16243
- const mapKey = ${keyConversion};
16244
- const keyHex = serializeCV(mapKey).toString('hex');
16245
-
16246
- const response = await fetch(
16247
- \`\${baseUrl}/v2/map_entry/${address}/${contractName}/${map.name}\`,
16248
- {
16249
- method: 'POST',
16250
- headers: { 'Content-Type': 'application/json' },
16251
- 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}\`);
16252
16307
  }
16253
- );
16254
16308
 
16255
- if (!response.ok) {
16256
- throw new Error(\`Failed to fetch map entry: \${response.statusText}\`);
16257
- }
16309
+ const result = await response.json();
16310
+ if (!result.data || result.data === '0x09') {
16311
+ return null; // none value
16312
+ }
16258
16313
 
16259
- const result = await response.json();
16260
- if (!result.data || result.data === '0x09') {
16261
- 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;
16262
16324
  }
16263
-
16264
- const { deserializeCV } = await import('@stacks/transactions');
16265
- const cv = deserializeCV(result.data);
16266
- const parsed = cvToJSON(cv);
16267
- // Unwrap the (some ...) wrapper
16268
- return parsed.value?.value ?? parsed.value ?? null;
16269
16325
  },
16270
16326
  keyType: ${JSON.stringify(map.key)} as const,
16271
16327
  valueType: ${JSON.stringify(map.value)} as const
@@ -16290,21 +16346,28 @@ function generateVarsObject(variables, address, contractName) {
16290
16346
  const valueType = getTypeForArg({ type: variable.type });
16291
16347
  return `${methodName}: {
16292
16348
  async get(options?: { network?: 'mainnet' | 'testnet' | 'devnet' }): Promise<${valueType}> {
16293
- const { cvToJSON, deserializeCV } = await import('@stacks/transactions');
16294
- const baseUrl = getApiUrl('${address}', options?.network);
16349
+ try {
16350
+ const { cvToJSON, deserializeCV } = await import('@stacks/transactions');
16351
+ const baseUrl = getApiUrl('${address}', options?.network);
16295
16352
 
16296
- const response = await fetch(
16297
- \`\${baseUrl}/v2/data_var/${address}/${contractName}/${variable.name}?proof=0\`
16298
- );
16353
+ const response = await fetch(
16354
+ \`\${baseUrl}/v2/data_var/${address}/${contractName}/${variable.name}?proof=0\`
16355
+ );
16299
16356
 
16300
- if (!response.ok) {
16301
- throw new Error(\`Failed to fetch data var: \${response.statusText}\`);
16302
- }
16357
+ if (!response.ok) {
16358
+ throw new Error(\`HTTP \${response.status}: \${response.statusText}\`);
16359
+ }
16303
16360
 
16304
- const result = await response.json();
16305
- const cv = deserializeCV(result.data);
16306
- const parsed = cvToJSON(cv);
16307
- 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
+ }
16308
16371
  },
16309
16372
  type: ${JSON.stringify(variable.type)} as const
16310
16373
  }`;
@@ -16328,21 +16391,28 @@ function generateConstantsObject(variables, address, contractName) {
16328
16391
  const valueType = getTypeForArg({ type: constant.type });
16329
16392
  return `${methodName}: {
16330
16393
  async get(options?: { network?: 'mainnet' | 'testnet' | 'devnet' }): Promise<${valueType}> {
16331
- const { cvToJSON, deserializeCV } = await import('@stacks/transactions');
16332
- const baseUrl = getApiUrl('${address}', options?.network);
16394
+ try {
16395
+ const { cvToJSON, deserializeCV } = await import('@stacks/transactions');
16396
+ const baseUrl = getApiUrl('${address}', options?.network);
16333
16397
 
16334
- const response = await fetch(
16335
- \`\${baseUrl}/v2/constant_val/${address}/${contractName}/${constant.name}?proof=0\`
16336
- );
16398
+ const response = await fetch(
16399
+ \`\${baseUrl}/v2/constant_val/${address}/${contractName}/${constant.name}?proof=0\`
16400
+ );
16337
16401
 
16338
- if (!response.ok) {
16339
- throw new Error(\`Failed to fetch constant: \${response.statusText}\`);
16340
- }
16402
+ if (!response.ok) {
16403
+ throw new Error(\`HTTP \${response.status}: \${response.statusText}\`);
16404
+ }
16341
16405
 
16342
- const result = await response.json();
16343
- const cv = deserializeCV(result.data);
16344
- const parsed = cvToJSON(cv);
16345
- 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
+ }
16346
16416
  },
16347
16417
  type: ${JSON.stringify(constant.type)} as const
16348
16418
  }`;
@@ -16365,6 +16435,7 @@ function generateMapKeyConversion(keyType) {
16365
16435
  return generateClarityConversion("key", { type: keyType });
16366
16436
  }
16367
16437
  var init_contract = __esm(() => {
16438
+ init_format();
16368
16439
  init_type_mapping();
16369
16440
  });
16370
16441
 
@@ -27926,6 +27997,7 @@ __export(exports_generate, {
27926
27997
  generate: () => generate
27927
27998
  });
27928
27999
  import path11 from "path";
28000
+ import { toCamelCase as toCamelCase3 } from "@secondlayer/clarity-types";
27929
28001
  function isContractAddress(input) {
27930
28002
  const contractIdPattern = /^(SP|ST|SM|SN)[A-Z0-9]{38,}\.[a-zA-Z][a-zA-Z0-9-]*$/;
27931
28003
  return contractIdPattern.test(input);
@@ -27967,17 +28039,20 @@ function deriveContractName(filePath) {
27967
28039
  const basename = path11.basename(filePath, ".clar");
27968
28040
  return basename.replace(/[-_](.)/g, (_2, char) => char.toUpperCase()).replace(/^(.)/, (_2, char) => char.toLowerCase()).replace(/^\d/, "_$&");
27969
28041
  }
27970
- function toCamelCase3(str) {
27971
- return str.replace(/[-_](.)/g, (_2, char) => char.toUpperCase()).replace(/^(.)/, (_2, char) => char.toLowerCase()).replace(/^\d/, "_$&");
27972
- }
27973
- async function buildConfigFromInputs(parsedInputs, outPath, apiKey) {
28042
+ async function buildConfigFromInputs(parsedInputs, outPath, apiKey, defaultAddress) {
27974
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
+ }
27975
28050
  for (const file of parsedInputs.files) {
27976
28051
  const abi = await parseClarityFile(file);
27977
28052
  const name = deriveContractName(file);
27978
28053
  contracts.push({
27979
28054
  name,
27980
- address: `ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.${name}`,
28055
+ address: `${deployer}.${name}`,
27981
28056
  abi,
27982
28057
  _directFile: true
27983
28058
  });
@@ -28091,7 +28166,7 @@ async function resolveContract(source, network, apiKey, apiUrl) {
28091
28166
  const filePath = path11.resolve(process.cwd(), source.source);
28092
28167
  const abi = await parseClarityFile(filePath);
28093
28168
  const name = source.name || path11.basename(source.source, ".clar").replace(/-/g, "_").replace(/^\d/, "_$&");
28094
- 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;
28095
28170
  const [contractAddress, contractName] = address.includes(".") ? address.split(".") : [address, name];
28096
28171
  return {
28097
28172
  name,
@@ -28154,7 +28229,7 @@ async function resolveContracts(source, defaultNetwork, apiKey, apiUrl) {
28154
28229
  }
28155
28230
  throw new Error("Contract must have either address or source");
28156
28231
  }
28157
- var import_fast_glob;
28232
+ var import_fast_glob, DEFAULT_DEVNET_ADDRESS = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM";
28158
28233
  var init_generate = __esm(() => {
28159
28234
  init_source();
28160
28235
  init_config();
@@ -28234,7 +28309,7 @@ var {
28234
28309
  // package.json
28235
28310
  var package_default = {
28236
28311
  name: "@secondlayer/cli",
28237
- version: "0.3.7",
28312
+ version: "0.3.9",
28238
28313
  description: "CLI for generating type-safe contract interfaces for the Stacks blockchain",
28239
28314
  type: "module",
28240
28315
  bin: {
@@ -28273,7 +28348,7 @@ var package_default = {
28273
28348
  author: "",
28274
28349
  license: "MIT",
28275
28350
  dependencies: {
28276
- "@secondlayer/clarity-types": "^0.4.1",
28351
+ "@secondlayer/clarity-types": "^0.4.2",
28277
28352
  "@stacks/transactions": "7.0.6",
28278
28353
  esbuild: "^0.19.0",
28279
28354
  prettier: "^3.1.0"
@@ -28310,5 +28385,5 @@ program.command("init").description("Initialize a new stacks.config.ts file").ac
28310
28385
  });
28311
28386
  program.parse();
28312
28387
 
28313
- //# debugId=58D4865FBB414E7B64756E2164756E21
28388
+ //# debugId=C3EAA77AEC0BD7A564756E2164756E21
28314
28389
  //# sourceMappingURL=cli.js.map