@secondlayer/cli 14.0.0 → 14.0.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/README.md CHANGED
@@ -196,6 +196,12 @@ secondlayer codegen contracts "./contracts/*.clar" -o ./src/generated.ts
196
196
  secondlayer codegen contracts --watch # regenerate on .clar / config / Clarinet.toml changes
197
197
  ```
198
198
 
199
+ Where the types come from matters. A bare `.clar` path is read as source, and
200
+ source only declares argument, map, and data-var types — a function's return
201
+ type comes out of Clarity's type checker, so it generates as `any`. A deployed
202
+ contract id or the `clarinet()` plugin (below) carries a real ABI, so returns
203
+ are exact too. Prefer those where you can.
204
+
199
205
  Config-driven:
200
206
 
201
207
  ```bash
package/dist/cli.js CHANGED
@@ -8831,57 +8831,111 @@ var init_contract = __esm(() => {
8831
8831
  // src/parsers/clarity.ts
8832
8832
  import { promises as fs2 } from "node:fs";
8833
8833
  import { normalizeAbi } from "@secondlayer/stacks/clarity";
8834
- async function parseClarityFile(filePath) {
8835
- try {
8836
- const content = await fs2.readFile(filePath, "utf-8");
8837
- const result = parseClarityContent(content);
8838
- if (result.functions.length === 0) {
8839
- console.warn(`⚠️ No functions found in ${filePath}. For complex contracts, deploy first and use the contract address instead.`);
8840
- }
8841
- return result;
8842
- } catch (error2) {
8843
- throw new Error(`Unable to parse ${filePath}. For complex contracts, deploy first and use the contract address instead.
8844
- Original error: ${error2}`);
8845
- }
8834
+ function isWhitespace(char) {
8835
+ return char === " " || char === "\t" || char === "\r" || char === `
8836
+ `;
8846
8837
  }
8847
- function parseClarityContent(content) {
8848
- const functions = [];
8849
- const functionRegex = /\(define-(public|read-only|private)\s+\(([^)]+)\)([\s\S]*?)\)\s*$/gm;
8850
- let match = functionRegex.exec(content);
8851
- while (match !== null) {
8852
- const [, access, signature, body] = match;
8853
- const func = parseFunctionSignature(signature, access, body);
8854
- if (func) {
8855
- functions.push(func);
8838
+ function isCommentStart(source, index) {
8839
+ return source[index] === ";" && source[index + 1] === ";";
8840
+ }
8841
+ function tokenize(source) {
8842
+ const tokens = [];
8843
+ let i = 0;
8844
+ while (i < source.length) {
8845
+ const char = source[i];
8846
+ if (isCommentStart(source, i)) {
8847
+ while (i < source.length && source[i] !== `
8848
+ `)
8849
+ i++;
8850
+ continue;
8851
+ }
8852
+ if (isWhitespace(char)) {
8853
+ i++;
8854
+ continue;
8855
+ }
8856
+ if (char === '"') {
8857
+ let end2 = i + 1;
8858
+ while (end2 < source.length && source[end2] !== '"') {
8859
+ if (source[end2] === "\\")
8860
+ end2++;
8861
+ end2++;
8862
+ }
8863
+ tokens.push(source.slice(i, Math.min(end2 + 1, source.length)));
8864
+ i = end2 + 1;
8865
+ continue;
8866
+ }
8867
+ if (DELIMITERS.has(char)) {
8868
+ tokens.push(char);
8869
+ i++;
8870
+ continue;
8856
8871
  }
8857
- match = functionRegex.exec(content);
8872
+ let end = i;
8873
+ while (end < source.length && !isWhitespace(source[end]) && !DELIMITERS.has(source[end]) && source[end] !== '"' && !isCommentStart(source, end)) {
8874
+ end++;
8875
+ }
8876
+ tokens.push(source.slice(i, end));
8877
+ i = end;
8858
8878
  }
8859
- return { functions };
8879
+ return tokens;
8860
8880
  }
8861
- function parseFunctionSignature(signature, access, body) {
8862
- const parts = signature.trim().split(/\s+/);
8863
- const name = parts[0];
8864
- const args = [];
8865
- for (let i = 1;i < parts.length; i += 2) {
8866
- if (parts[i] && parts[i + 1]) {
8867
- const argName = parts[i].replace(/[()]/g, "");
8868
- const argType = parseType(parts[i + 1]);
8869
- if (argType) {
8870
- args.push({ name: argName, type: argType });
8871
- }
8881
+ function readForms(source) {
8882
+ const tokens = tokenize(source);
8883
+ let pos = 0;
8884
+ function readNode() {
8885
+ const token = tokens[pos];
8886
+ if (token === undefined)
8887
+ return null;
8888
+ if (token === "(") {
8889
+ pos++;
8890
+ return { kind: "list", items: readList() };
8891
+ }
8892
+ if (token === "{") {
8893
+ pos++;
8894
+ return { kind: "tuple", fields: readTupleFields() };
8895
+ }
8896
+ if (DELIMITERS.has(token)) {
8897
+ pos++;
8898
+ return null;
8872
8899
  }
8900
+ pos++;
8901
+ return { kind: "atom", value: token };
8902
+ }
8903
+ function readList() {
8904
+ const items = [];
8905
+ while (pos < tokens.length && tokens[pos] !== ")") {
8906
+ const node = readNode();
8907
+ if (node)
8908
+ items.push(node);
8909
+ }
8910
+ pos++;
8911
+ return items;
8912
+ }
8913
+ function readTupleFields() {
8914
+ const fields = [];
8915
+ while (pos < tokens.length && tokens[pos] !== "}") {
8916
+ const name = tokens[pos];
8917
+ pos++;
8918
+ if (name === "," || name === ":")
8919
+ continue;
8920
+ if (tokens[pos] === ":")
8921
+ pos++;
8922
+ const value = readNode();
8923
+ if (value)
8924
+ fields.push({ name, value });
8925
+ }
8926
+ pos++;
8927
+ return fields;
8873
8928
  }
8874
- const outputs = inferReturnType(body);
8875
- return {
8876
- name,
8877
- access,
8878
- args,
8879
- outputs
8880
- };
8929
+ const forms = [];
8930
+ while (pos < tokens.length) {
8931
+ const node = readNode();
8932
+ if (node)
8933
+ forms.push(node);
8934
+ }
8935
+ return forms;
8881
8936
  }
8882
- function parseType(typeStr) {
8883
- const cleaned = typeStr.replace(/[()]/g, "").trim();
8884
- switch (cleaned) {
8937
+ function atomToAbi(value) {
8938
+ switch (value) {
8885
8939
  case "uint":
8886
8940
  case "uint128":
8887
8941
  return "uint128";
@@ -8893,35 +8947,201 @@ function parseType(typeStr) {
8893
8947
  case "principal":
8894
8948
  return "principal";
8895
8949
  case "trait_reference":
8896
- return "principal";
8950
+ return "trait_reference";
8951
+ default:
8952
+ return value.startsWith("<") ? "trait_reference" : UNKNOWN;
8953
+ }
8954
+ }
8955
+ function readLength(node) {
8956
+ if (node?.kind !== "atom")
8957
+ return null;
8958
+ const length = Number.parseInt(node.value, 10);
8959
+ return Number.isSafeInteger(length) && length >= 0 ? length : null;
8960
+ }
8961
+ function readFieldPairs(nodes) {
8962
+ return nodes.flatMap((node) => {
8963
+ if (node.kind !== "list")
8964
+ return [];
8965
+ const [name, type] = node.items;
8966
+ if (name?.kind !== "atom" || !type)
8967
+ return [];
8968
+ return [{ name: name.value, type: typeToAbi(type) }];
8969
+ });
8970
+ }
8971
+ function typeToAbi(node) {
8972
+ if (node.kind === "atom")
8973
+ return atomToAbi(node.value);
8974
+ if (node.kind === "tuple") {
8975
+ return {
8976
+ tuple: node.fields.map((field) => ({
8977
+ name: field.name,
8978
+ type: typeToAbi(field.value)
8979
+ }))
8980
+ };
8981
+ }
8982
+ const [head, ...args] = node.items;
8983
+ if (!head)
8984
+ return UNKNOWN;
8985
+ if (head.kind !== "atom") {
8986
+ const fields = readFieldPairs(node.items);
8987
+ return fields.length > 0 ? { tuple: fields } : UNKNOWN;
8988
+ }
8989
+ switch (head.value) {
8990
+ case "string-ascii": {
8991
+ const length = readLength(args[0]);
8992
+ return length === null ? UNKNOWN : { "string-ascii": { length } };
8993
+ }
8994
+ case "string-utf8": {
8995
+ const length = readLength(args[0]);
8996
+ return length === null ? UNKNOWN : { "string-utf8": { length } };
8997
+ }
8998
+ case "buff": {
8999
+ const length = readLength(args[0]);
9000
+ return length === null ? UNKNOWN : { buff: { length } };
9001
+ }
9002
+ case "optional":
9003
+ return args[0] ? { optional: typeToAbi(args[0]) } : UNKNOWN;
9004
+ case "list": {
9005
+ const length = readLength(args[0]);
9006
+ if (length === null || !args[1])
9007
+ return UNKNOWN;
9008
+ return { list: { length, type: typeToAbi(args[1]) } };
9009
+ }
9010
+ case "response":
9011
+ return args[0] && args[1] ? { response: { ok: typeToAbi(args[0]), error: typeToAbi(args[1]) } } : UNKNOWN;
9012
+ case "tuple": {
9013
+ const fields = readFieldPairs(args);
9014
+ return fields.length > 0 ? { tuple: fields } : UNKNOWN;
9015
+ }
8897
9016
  default:
8898
- if (cleaned.startsWith("string-ascii")) {
8899
- return { "string-ascii": { length: 256 } };
9017
+ return UNKNOWN;
9018
+ }
9019
+ }
9020
+ function toFunction(form, access) {
9021
+ const signature = form[1];
9022
+ if (signature?.kind !== "list")
9023
+ return null;
9024
+ const [nameNode, ...argNodes] = signature.items;
9025
+ if (nameNode?.kind !== "atom")
9026
+ return null;
9027
+ const args = argNodes.flatMap((node) => {
9028
+ if (node.kind !== "list")
9029
+ return [];
9030
+ const [name, type] = node.items;
9031
+ if (name?.kind !== "atom" || !type)
9032
+ return [];
9033
+ return [{ name: name.value, type: typeToAbi(type) }];
9034
+ });
9035
+ return {
9036
+ name: nameNode.value,
9037
+ access,
9038
+ args,
9039
+ outputs: access === "public" ? { response: { ok: UNKNOWN, error: UNKNOWN } } : UNKNOWN
9040
+ };
9041
+ }
9042
+ function toMap(form) {
9043
+ const [, nameNode, key, value] = form;
9044
+ if (nameNode?.kind !== "atom" || !key || !value)
9045
+ return null;
9046
+ return { name: nameNode.value, key: typeToAbi(key), value: typeToAbi(value) };
9047
+ }
9048
+ function toVariable(form) {
9049
+ const [, nameNode, type] = form;
9050
+ if (nameNode?.kind !== "atom" || !type)
9051
+ return null;
9052
+ return { name: nameNode.value, type: typeToAbi(type), access: "variable" };
9053
+ }
9054
+ function toFungibleToken(form) {
9055
+ const nameNode = form[1];
9056
+ if (nameNode?.kind !== "atom")
9057
+ return null;
9058
+ return { name: nameNode.value };
9059
+ }
9060
+ function toNonFungibleToken(form) {
9061
+ const [, nameNode, type] = form;
9062
+ if (nameNode?.kind !== "atom")
9063
+ return null;
9064
+ return { name: nameNode.value, type: type ? typeToAbi(type) : UNKNOWN };
9065
+ }
9066
+ function parseClarityContent(content) {
9067
+ const functions = [];
9068
+ const maps = [];
9069
+ const variables = [];
9070
+ const fungibleTokens = [];
9071
+ const nonFungibleTokens = [];
9072
+ const implementedTraits = [];
9073
+ for (const form of readForms(content)) {
9074
+ if (form.kind !== "list")
9075
+ continue;
9076
+ const head = form.items[0];
9077
+ if (head?.kind !== "atom")
9078
+ continue;
9079
+ const access = FUNCTION_ACCESS[head.value];
9080
+ if (access) {
9081
+ const func = toFunction(form.items, access);
9082
+ if (func)
9083
+ functions.push(func);
9084
+ continue;
9085
+ }
9086
+ switch (head.value) {
9087
+ case "define-map": {
9088
+ const map = toMap(form.items);
9089
+ if (map)
9090
+ maps.push(map);
9091
+ break;
8900
9092
  }
8901
- if (cleaned.startsWith("string-utf8")) {
8902
- return { "string-utf8": { length: 256 } };
9093
+ case "define-data-var": {
9094
+ const variable = toVariable(form.items);
9095
+ if (variable)
9096
+ variables.push(variable);
9097
+ break;
8903
9098
  }
8904
- if (cleaned.startsWith("buff")) {
8905
- return { buff: { length: 32 } };
9099
+ case "define-fungible-token": {
9100
+ const token = toFungibleToken(form.items);
9101
+ if (token)
9102
+ fungibleTokens.push(token);
9103
+ break;
8906
9104
  }
8907
- return "uint128";
8908
- }
8909
- }
8910
- function inferReturnType(body) {
8911
- if (body.includes("(ok")) {
8912
- if (body.includes("(err")) {
8913
- return {
8914
- response: {
8915
- ok: "bool",
8916
- error: "uint128"
9105
+ case "define-non-fungible-token": {
9106
+ const token = toNonFungibleToken(form.items);
9107
+ if (token)
9108
+ nonFungibleTokens.push(token);
9109
+ break;
9110
+ }
9111
+ case "impl-trait": {
9112
+ const trait = form.items[1];
9113
+ if (trait?.kind === "atom") {
9114
+ implementedTraits.push(trait.value.replace(/^'/, ""));
8917
9115
  }
8918
- };
9116
+ break;
9117
+ }
8919
9118
  }
8920
9119
  }
8921
- if (body.includes("true") || body.includes("false")) {
8922
- return "bool";
9120
+ return {
9121
+ functions,
9122
+ ...maps.length > 0 && { maps },
9123
+ ...variables.length > 0 && { variables },
9124
+ ...fungibleTokens.length > 0 && { fungible_tokens: fungibleTokens },
9125
+ ...nonFungibleTokens.length > 0 && {
9126
+ non_fungible_tokens: nonFungibleTokens
9127
+ },
9128
+ ...implementedTraits.length > 0 && {
9129
+ implemented_traits: implementedTraits
9130
+ }
9131
+ };
9132
+ }
9133
+ async function parseClarityFile(filePath) {
9134
+ try {
9135
+ const content = await fs2.readFile(filePath, "utf-8");
9136
+ const result = parseClarityContent(content);
9137
+ if (result.functions.length === 0) {
9138
+ console.warn(`⚠️ No functions found in ${filePath}. For complex contracts, deploy first and use the contract address instead.`);
9139
+ }
9140
+ return result;
9141
+ } catch (error2) {
9142
+ throw new Error(`Unable to parse ${filePath}. For complex contracts, deploy first and use the contract address instead.
9143
+ Original error: ${error2}`);
8923
9144
  }
8924
- return "bool";
8925
9145
  }
8926
9146
  function parseApiResponse(apiResponse) {
8927
9147
  try {
@@ -8930,7 +9150,15 @@ function parseApiResponse(apiResponse) {
8930
9150
  throw new Error(`Failed to parse API response: ${error2}`);
8931
9151
  }
8932
9152
  }
8933
- var init_clarity = () => {};
9153
+ var DELIMITERS, UNKNOWN = "unknown", FUNCTION_ACCESS;
9154
+ var init_clarity = __esm(() => {
9155
+ DELIMITERS = new Set(["(", ")", "{", "}", ",", ":"]);
9156
+ FUNCTION_ACCESS = {
9157
+ "define-public": "public",
9158
+ "define-read-only": "read-only",
9159
+ "define-private": "private"
9160
+ };
9161
+ });
8934
9162
 
8935
9163
  // ../../node_modules/@sindresorhus/is/dist/index.js
8936
9164
  function isTypedArrayName(name) {
@@ -20969,10 +21197,10 @@ var isTemplateString = (templates) => Array.isArray(templates) && Array.isArray(
20969
21197
  }
20970
21198
  const nextTokens = [];
20971
21199
  let templateStart = 0;
20972
- const leadingWhitespaces = DELIMITERS.has(rawTemplate[0]);
21200
+ const leadingWhitespaces = DELIMITERS2.has(rawTemplate[0]);
20973
21201
  for (let templateIndex = 0, rawIndex = 0;templateIndex < template.length; templateIndex += 1, rawIndex += 1) {
20974
21202
  const rawCharacter = rawTemplate[rawIndex];
20975
- if (DELIMITERS.has(rawCharacter)) {
21203
+ if (DELIMITERS2.has(rawCharacter)) {
20976
21204
  if (templateStart !== templateIndex) {
20977
21205
  nextTokens.push(template.slice(templateStart, templateIndex));
20978
21206
  }
@@ -20995,7 +21223,7 @@ var isTemplateString = (templates) => Array.isArray(templates) && Array.isArray(
20995
21223
  nextTokens.push(template.slice(templateStart));
20996
21224
  }
20997
21225
  return { nextTokens, leadingWhitespaces, trailingWhitespaces };
20998
- }, DELIMITERS, ESCAPE_LENGTH, concatTokens = (tokens, nextTokens, isSeparated) => isSeparated || tokens.length === 0 || nextTokens.length === 0 ? [...tokens, ...nextTokens] : [
21226
+ }, DELIMITERS2, ESCAPE_LENGTH, concatTokens = (tokens, nextTokens, isSeparated) => isSeparated || tokens.length === 0 || nextTokens.length === 0 ? [...tokens, ...nextTokens] : [
20999
21227
  ...tokens.slice(0, -1),
21000
21228
  `${tokens.at(-1)}${nextTokens[0]}`,
21001
21229
  ...nextTokens.slice(1)
@@ -21028,7 +21256,7 @@ var isTemplateString = (templates) => Array.isArray(templates) && Array.isArray(
21028
21256
  };
21029
21257
  var init_template = __esm(() => {
21030
21258
  init_uint_array();
21031
- DELIMITERS = new Set([" ", "\t", "\r", `
21259
+ DELIMITERS2 = new Set([" ", "\t", "\r", `
21032
21260
  `]);
21033
21261
  ESCAPE_LENGTH = { x: 3, u: 5 };
21034
21262
  });
@@ -27907,6 +28135,9 @@ async function buildConfigFromInputs(parsedInputs, outPath, apiKey, defaultAddre
27907
28135
  Generated contract addresses won't match deployed addresses.
27908
28136
  Set defaultAddress in config or use deployed contract addresses.`));
27909
28137
  }
28138
+ if (parsedInputs.files.length > 0) {
28139
+ console.warn(source_default.yellow("⚠️ Return types can't be read from Clarity source and will be `any`.\n Use the clarinet() plugin or a deployed contract id for exact types."));
28140
+ }
27910
28141
  for (const file of parsedInputs.files) {
27911
28142
  const abi = await parseClarityFile(file);
27912
28143
  const name = deriveContractName(file);
@@ -36546,7 +36777,7 @@ var {
36546
36777
  // package.json
36547
36778
  var package_default = {
36548
36779
  name: "@secondlayer/cli",
36549
- version: "14.0.0",
36780
+ version: "14.0.1",
36550
36781
  description: "Run a Stacks index on your own hardware — set it up, restore verified history, and keep it current.",
36551
36782
  repository: {
36552
36783
  type: "git",
@@ -36593,7 +36824,7 @@ var package_default = {
36593
36824
  "@secondlayer/scaffold": "^1.5.5",
36594
36825
  "@secondlayer/sdk": "^10.0.0",
36595
36826
  "@secondlayer/shared": "^11.2.0",
36596
- "@secondlayer/stacks": "^5.0.0",
36827
+ "@secondlayer/stacks": "^5.0.1",
36597
36828
  "@secondlayer/subgraphs": "^4.1.1",
36598
36829
  esbuild: "^0.27.4",
36599
36830
  react: "^19.1.0",
@@ -42515,5 +42746,5 @@ program.commandsGroup("Archive:");
42515
42746
  registerCreditsCommand(program);
42516
42747
  program.parse();
42517
42748
 
42518
- //# debugId=80EA4C7A072B2FCB64756E2164756E21
42749
+ //# debugId=70015BE103AF7D4B64756E2164756E21
42519
42750
  //# sourceMappingURL=cli.js.map