@secondlayer/cli 0.2.5 → 0.3.1

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
@@ -15702,6 +15702,8 @@ function inferReturnType(body) {
15702
15702
  function parseApiResponse(apiResponse) {
15703
15703
  try {
15704
15704
  const functions = [];
15705
+ const maps = [];
15706
+ const variables = [];
15705
15707
  if (apiResponse.functions) {
15706
15708
  for (const func of apiResponse.functions) {
15707
15709
  const access = func.access === "read_only" ? "read-only" : func.access;
@@ -15716,7 +15718,29 @@ function parseApiResponse(apiResponse) {
15716
15718
  });
15717
15719
  }
15718
15720
  }
15719
- return { functions };
15721
+ if (apiResponse.maps) {
15722
+ for (const map of apiResponse.maps) {
15723
+ maps.push({
15724
+ name: map.name,
15725
+ key: convertApiType(map.key),
15726
+ value: convertApiType(map.value)
15727
+ });
15728
+ }
15729
+ }
15730
+ if (apiResponse.variables) {
15731
+ for (const variable of apiResponse.variables) {
15732
+ variables.push({
15733
+ name: variable.name,
15734
+ type: convertApiType(variable.type),
15735
+ access: variable.access
15736
+ });
15737
+ }
15738
+ }
15739
+ return {
15740
+ functions,
15741
+ maps: maps.length > 0 ? maps : undefined,
15742
+ variables: variables.length > 0 ? variables : undefined
15743
+ };
15720
15744
  } catch (error) {
15721
15745
  throw new Error(`Failed to parse API response: ${error}`);
15722
15746
  }
@@ -15787,12 +15811,44 @@ var init_clarity = () => {};
15787
15811
 
15788
15812
  // src/generators/contract.ts
15789
15813
  import { format } from "prettier";
15814
+ function generateNetworkUtils() {
15815
+ return `/**
15816
+ * API URLs for different networks
15817
+ */
15818
+ const API_URLS: Record<'mainnet' | 'testnet' | 'devnet', string> = {
15819
+ mainnet: 'https://api.hiro.so',
15820
+ testnet: 'https://api.testnet.hiro.so',
15821
+ devnet: 'http://localhost:3999'
15822
+ };
15823
+
15824
+ /**
15825
+ * Infer network from Stacks address prefix
15826
+ * SP/SM = mainnet, ST/SN = testnet
15827
+ */
15828
+ function inferNetworkFromAddress(address: string): 'mainnet' | 'testnet' | undefined {
15829
+ if (address.startsWith('SP') || address.startsWith('SM')) return 'mainnet';
15830
+ if (address.startsWith('ST') || address.startsWith('SN')) return 'testnet';
15831
+ return undefined;
15832
+ }
15833
+
15834
+ /**
15835
+ * Get API URL, inferring network from contract address if not specified
15836
+ */
15837
+ function getApiUrl(
15838
+ contractAddress: string,
15839
+ explicitNetwork?: 'mainnet' | 'testnet' | 'devnet'
15840
+ ): string {
15841
+ const network = explicitNetwork ?? inferNetworkFromAddress(contractAddress) ?? 'mainnet';
15842
+ return API_URLS[network];
15843
+ }`;
15844
+ }
15790
15845
  async function generateContractInterface(contracts) {
15791
15846
  const imports = `import { Cl, validateStacksAddress } from '@stacks/transactions'`;
15792
15847
  const header = `/**
15793
15848
  * Generated by @secondlayer/cli
15794
15849
  * DO NOT EDIT MANUALLY
15795
15850
  */`;
15851
+ const networkUtils = generateNetworkUtils();
15796
15852
  const contractsCode = contracts.map((contract) => generateContract(contract)).join(`
15797
15853
 
15798
15854
  `);
@@ -15800,6 +15856,8 @@ async function generateContractInterface(contracts) {
15800
15856
 
15801
15857
  ${header}
15802
15858
 
15859
+ ${networkUtils}
15860
+
15803
15861
  ${contractsCode}`;
15804
15862
  const formatted = await format(code, {
15805
15863
  parser: "typescript",
@@ -15816,12 +15874,18 @@ function generateContract(contract) {
15816
15874
  const methods = abi.functions.filter((func) => func.access !== "private").map((func) => generateMethod(func, address, contractName)).join(`,
15817
15875
 
15818
15876
  `);
15877
+ const mapsObject = generateMapsObject(abi.maps || [], address, contractName);
15878
+ const varsObject = generateVarsObject(abi.variables || [], address, contractName);
15879
+ const constantsObject = generateConstantsObject(abi.variables || [], address, contractName);
15880
+ const allMembers = [methods, mapsObject, varsObject, constantsObject].filter(Boolean);
15819
15881
  const contractCode = `export const ${name} = {
15820
15882
  address: '${address}',
15821
15883
  contractAddress: '${address}',
15822
15884
  contractName: '${contractName}',
15823
-
15824
- ${methods}
15885
+
15886
+ ${allMembers.join(`,
15887
+
15888
+ `)}
15825
15889
  } as const`;
15826
15890
  return `${abiCode}
15827
15891
 
@@ -16032,6 +16096,143 @@ function generateClarityConversion(argName, argType) {
16032
16096
  }
16033
16097
  return `${argName}`;
16034
16098
  }
16099
+ function generateMapsObject(maps, address, contractName) {
16100
+ if (!maps || maps.length === 0) {
16101
+ return "";
16102
+ }
16103
+ const mapMethods = maps.map((map) => {
16104
+ const methodName = toCamelCase(map.name);
16105
+ const keyType = getTypeForArg({ type: map.key });
16106
+ const valueType = getTypeForArg({ type: map.value });
16107
+ const keyConversion = generateMapKeyConversion(map.key);
16108
+ return `${methodName}: {
16109
+ async get(key: ${keyType}, options?: { network?: 'mainnet' | 'testnet' | 'devnet' }): Promise<${valueType} | null> {
16110
+ const { cvToJSON, serializeCV } = await import('@stacks/transactions');
16111
+ const baseUrl = getApiUrl('${address}', options?.network);
16112
+ const mapKey = ${keyConversion};
16113
+ const keyHex = serializeCV(mapKey).toString('hex');
16114
+
16115
+ const response = await fetch(
16116
+ \`\${baseUrl}/v2/map_entry/${address}/${contractName}/${map.name}\`,
16117
+ {
16118
+ method: 'POST',
16119
+ headers: { 'Content-Type': 'application/json' },
16120
+ body: JSON.stringify(keyHex)
16121
+ }
16122
+ );
16123
+
16124
+ if (!response.ok) {
16125
+ throw new Error(\`Failed to fetch map entry: \${response.statusText}\`);
16126
+ }
16127
+
16128
+ const result = await response.json();
16129
+ if (!result.data || result.data === '0x09') {
16130
+ return null; // none value
16131
+ }
16132
+
16133
+ const { deserializeCV } = await import('@stacks/transactions');
16134
+ const cv = deserializeCV(result.data);
16135
+ const parsed = cvToJSON(cv);
16136
+ // Unwrap the (some ...) wrapper
16137
+ return parsed.value?.value ?? parsed.value ?? null;
16138
+ },
16139
+ keyType: ${JSON.stringify(map.key)} as const,
16140
+ valueType: ${JSON.stringify(map.value)} as const
16141
+ }`;
16142
+ });
16143
+ return `maps: {
16144
+ ${mapMethods.join(`,
16145
+
16146
+ `)}
16147
+ }`;
16148
+ }
16149
+ function generateVarsObject(variables, address, contractName) {
16150
+ if (!variables || variables.length === 0) {
16151
+ return "";
16152
+ }
16153
+ const dataVars = variables.filter((v) => v.access === "variable");
16154
+ if (dataVars.length === 0) {
16155
+ return "";
16156
+ }
16157
+ const varMethods = dataVars.map((variable) => {
16158
+ const methodName = toCamelCase(variable.name);
16159
+ const valueType = getTypeForArg({ type: variable.type });
16160
+ return `${methodName}: {
16161
+ async get(options?: { network?: 'mainnet' | 'testnet' | 'devnet' }): Promise<${valueType}> {
16162
+ const { cvToJSON, deserializeCV } = await import('@stacks/transactions');
16163
+ const baseUrl = getApiUrl('${address}', options?.network);
16164
+
16165
+ const response = await fetch(
16166
+ \`\${baseUrl}/v2/data_var/${address}/${contractName}/${variable.name}?proof=0\`
16167
+ );
16168
+
16169
+ if (!response.ok) {
16170
+ throw new Error(\`Failed to fetch data var: \${response.statusText}\`);
16171
+ }
16172
+
16173
+ const result = await response.json();
16174
+ const cv = deserializeCV(result.data);
16175
+ const parsed = cvToJSON(cv);
16176
+ return parsed.value ?? parsed;
16177
+ },
16178
+ type: ${JSON.stringify(variable.type)} as const
16179
+ }`;
16180
+ });
16181
+ return `vars: {
16182
+ ${varMethods.join(`,
16183
+
16184
+ `)}
16185
+ }`;
16186
+ }
16187
+ function generateConstantsObject(variables, address, contractName) {
16188
+ if (!variables || variables.length === 0) {
16189
+ return "";
16190
+ }
16191
+ const constants = variables.filter((v) => v.access === "constant");
16192
+ if (constants.length === 0) {
16193
+ return "";
16194
+ }
16195
+ const constMethods = constants.map((constant) => {
16196
+ const methodName = toCamelCase(constant.name);
16197
+ const valueType = getTypeForArg({ type: constant.type });
16198
+ return `${methodName}: {
16199
+ async get(options?: { network?: 'mainnet' | 'testnet' | 'devnet' }): Promise<${valueType}> {
16200
+ const { cvToJSON, deserializeCV } = await import('@stacks/transactions');
16201
+ const baseUrl = getApiUrl('${address}', options?.network);
16202
+
16203
+ const response = await fetch(
16204
+ \`\${baseUrl}/v2/constant_val/${address}/${contractName}/${constant.name}?proof=0\`
16205
+ );
16206
+
16207
+ if (!response.ok) {
16208
+ throw new Error(\`Failed to fetch constant: \${response.statusText}\`);
16209
+ }
16210
+
16211
+ const result = await response.json();
16212
+ const cv = deserializeCV(result.data);
16213
+ const parsed = cvToJSON(cv);
16214
+ return parsed.value ?? parsed;
16215
+ },
16216
+ type: ${JSON.stringify(constant.type)} as const
16217
+ }`;
16218
+ });
16219
+ return `constants: {
16220
+ ${constMethods.join(`,
16221
+
16222
+ `)}
16223
+ }`;
16224
+ }
16225
+ function generateMapKeyConversion(keyType) {
16226
+ if (keyType.tuple) {
16227
+ const fields = keyType.tuple.map((field) => {
16228
+ const camelFieldName = toCamelCase(field.name);
16229
+ const fieldConversion = generateClarityConversion(`key.${camelFieldName}`, { type: field.type });
16230
+ return `"${field.name}": ${fieldConversion}`;
16231
+ }).join(", ");
16232
+ return `Cl.tuple({ ${fields} })`;
16233
+ }
16234
+ return generateClarityConversion("key", { type: keyType });
16235
+ }
16035
16236
  var init_contract = () => {};
16036
16237
 
16037
16238
  // src/commands/generate.ts
@@ -16361,7 +16562,7 @@ var {
16361
16562
  // package.json
16362
16563
  var package_default = {
16363
16564
  name: "@secondlayer/cli",
16364
- version: "0.2.5",
16565
+ version: "0.3.1",
16365
16566
  description: "CLI for generating type-safe contract interfaces for the Stacks blockchain",
16366
16567
  type: "module",
16367
16568
  bin: {
@@ -16400,7 +16601,7 @@ var package_default = {
16400
16601
  author: "",
16401
16602
  license: "MIT",
16402
16603
  dependencies: {
16403
- "@secondlayer/clarity-types": "^0.2.2",
16604
+ "@secondlayer/clarity-types": "^0.3.0",
16404
16605
  "@stacks/transactions": "7.0.6",
16405
16606
  esbuild: "^0.19.0",
16406
16607
  prettier: "^3.1.0"
@@ -16437,5 +16638,5 @@ program.command("init").description("Initialize a new stacks.config.ts file").ac
16437
16638
  });
16438
16639
  program.parse();
16439
16640
 
16440
- //# debugId=C236EDEA54ABBFA664756E2164756E21
16641
+ //# debugId=00D09707C564934264756E2164756E21
16441
16642
  //# sourceMappingURL=cli.js.map