@secondlayer/cli 0.1.0 → 0.2.0
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 +432 -179
- package/dist/cli.js.map +17 -1
- package/dist/index.d.ts +351 -8
- package/dist/index.js +2386 -73
- package/dist/index.js.map +22 -1
- package/dist/plugin-manager.d.ts +257 -0
- package/dist/{core/plugin-manager.js → plugin-manager.js} +35 -67
- package/dist/plugin-manager.js.map +10 -0
- package/package.json +10 -17
- package/LICENSE +0 -21
- package/README.md +0 -233
- package/dist/cli.cjs +0 -897
- package/dist/cli.cjs.map +0 -1
- package/dist/core/plugin-manager.cjs +0 -368
- package/dist/core/plugin-manager.cjs.map +0 -1
- package/dist/core/plugin-manager.d.cts +0 -1
- package/dist/core/plugin-manager.d.ts +0 -1
- package/dist/core/plugin-manager.js.map +0 -1
- package/dist/index.cjs +0 -380
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -8
- package/dist/plugin-manager-DBXFfyFZ.d.cts +0 -285
- package/dist/plugin-manager-DBXFfyFZ.d.ts +0 -285
- package/dist/plugins/index.cjs +0 -2622
- package/dist/plugins/index.cjs.map +0 -1
- package/dist/plugins/index.d.cts +0 -136
- package/dist/plugins/index.d.ts +0 -136
- package/dist/plugins/index.js +0 -2578
- package/dist/plugins/index.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,45 +1,38 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
// @bun
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __esm = (fn, res) => function __init() {
|
|
5
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
-
};
|
|
7
4
|
var __export = (target, all) => {
|
|
8
5
|
for (var name in all)
|
|
9
|
-
__defProp(target, name, {
|
|
6
|
+
__defProp(target, name, {
|
|
7
|
+
get: all[name],
|
|
8
|
+
enumerable: true,
|
|
9
|
+
configurable: true,
|
|
10
|
+
set: (newValue) => all[name] = () => newValue
|
|
11
|
+
});
|
|
10
12
|
};
|
|
11
|
-
|
|
12
|
-
// node_modules/tsup/assets/esm_shims.js
|
|
13
|
-
import path from "path";
|
|
14
|
-
import { fileURLToPath } from "url";
|
|
15
|
-
var init_esm_shims = __esm({
|
|
16
|
-
"node_modules/tsup/assets/esm_shims.js"() {
|
|
17
|
-
"use strict";
|
|
18
|
-
}
|
|
19
|
-
});
|
|
13
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
20
14
|
|
|
21
15
|
// src/commands/init.ts
|
|
22
|
-
var
|
|
23
|
-
__export(
|
|
16
|
+
var exports_init = {};
|
|
17
|
+
__export(exports_init, {
|
|
24
18
|
init: () => init
|
|
25
19
|
});
|
|
26
|
-
import { promises as
|
|
20
|
+
import { promises as fs4 } from "fs";
|
|
27
21
|
import path4 from "path";
|
|
28
22
|
import ora2 from "ora";
|
|
29
23
|
async function init() {
|
|
30
24
|
const spinner = ora2("Initializing").start();
|
|
31
25
|
const configPath = path4.join(process.cwd(), "stacks.config.ts");
|
|
32
26
|
try {
|
|
33
|
-
await
|
|
27
|
+
await fs4.access(configPath);
|
|
34
28
|
spinner.warn("stacks.config.ts already exists");
|
|
35
29
|
return;
|
|
36
|
-
} catch {
|
|
37
|
-
}
|
|
30
|
+
} catch {}
|
|
38
31
|
const hasClarinetProject = await fileExists("./Clarinet.toml");
|
|
39
32
|
let config;
|
|
40
33
|
if (hasClarinetProject) {
|
|
41
|
-
config = `import { defineConfig } from '@
|
|
42
|
-
import { clarinet } from '@
|
|
34
|
+
config = `import { defineConfig } from '@secondlayer/cli';
|
|
35
|
+
import { clarinet } from '@secondlayer/cli/plugins';
|
|
43
36
|
|
|
44
37
|
export default defineConfig({
|
|
45
38
|
out: './src/generated/contracts.ts',
|
|
@@ -48,93 +41,77 @@ export default defineConfig({
|
|
|
48
41
|
]
|
|
49
42
|
});`;
|
|
50
43
|
} else {
|
|
51
|
-
config = `import { defineConfig } from '@
|
|
44
|
+
config = `import { defineConfig } from '@secondlayer/cli';
|
|
52
45
|
|
|
53
46
|
export default defineConfig({
|
|
54
47
|
out: './src/generated/contracts.ts',
|
|
55
48
|
plugins: [],
|
|
56
49
|
});`;
|
|
57
50
|
}
|
|
58
|
-
await
|
|
51
|
+
await fs4.writeFile(configPath, config);
|
|
59
52
|
spinner.succeed("Created `stacks.config.ts`");
|
|
60
|
-
console.log(
|
|
61
|
-
"\nRun `codegen generate` to generate type-safe interfaces, functions, and hooks!"
|
|
62
|
-
);
|
|
53
|
+
console.log("\nRun `secondlayer generate` to generate type-safe interfaces, functions, and hooks!");
|
|
63
54
|
}
|
|
64
55
|
async function fileExists(filePath) {
|
|
65
56
|
try {
|
|
66
|
-
await
|
|
57
|
+
await fs4.access(filePath);
|
|
67
58
|
return true;
|
|
68
59
|
} catch {
|
|
69
60
|
return false;
|
|
70
61
|
}
|
|
71
62
|
}
|
|
72
|
-
var init_init =
|
|
73
|
-
"src/commands/init.ts"() {
|
|
74
|
-
"use strict";
|
|
75
|
-
init_esm_shims();
|
|
76
|
-
}
|
|
77
|
-
});
|
|
63
|
+
var init_init = () => {};
|
|
78
64
|
|
|
79
65
|
// src/cli.ts
|
|
80
|
-
init_esm_shims();
|
|
81
66
|
import { program } from "commander";
|
|
82
67
|
|
|
83
68
|
// src/commands/generate.ts
|
|
84
|
-
|
|
69
|
+
import path3 from "path";
|
|
85
70
|
import chalk from "chalk";
|
|
86
71
|
import ora from "ora";
|
|
72
|
+
var {Glob } = globalThis.Bun;
|
|
87
73
|
|
|
88
74
|
// src/utils/config.ts
|
|
89
|
-
init_esm_shims();
|
|
90
75
|
import { promises as fs2 } from "fs";
|
|
91
|
-
import
|
|
76
|
+
import path2 from "path";
|
|
92
77
|
import { pathToFileURL } from "url";
|
|
93
78
|
import { createRequire } from "module";
|
|
94
79
|
import { transformSync } from "esbuild";
|
|
95
80
|
|
|
96
81
|
// src/core/plugin-manager.ts
|
|
97
|
-
init_esm_shims();
|
|
98
82
|
import { format } from "prettier";
|
|
99
83
|
import { promises as fs } from "fs";
|
|
100
|
-
import
|
|
84
|
+
import path from "path";
|
|
101
85
|
import { validateStacksAddress } from "@stacks/transactions";
|
|
102
|
-
|
|
86
|
+
|
|
87
|
+
class PluginManager {
|
|
88
|
+
plugins = [];
|
|
89
|
+
logger;
|
|
90
|
+
utils;
|
|
91
|
+
executionContext;
|
|
103
92
|
constructor() {
|
|
104
|
-
this.plugins = [];
|
|
105
93
|
this.logger = this.createLogger();
|
|
106
94
|
this.utils = this.createUtils();
|
|
107
95
|
this.executionContext = {
|
|
108
96
|
phase: "config",
|
|
109
97
|
startTime: Date.now(),
|
|
110
|
-
results:
|
|
98
|
+
results: new Map
|
|
111
99
|
};
|
|
112
100
|
}
|
|
113
|
-
/**
|
|
114
|
-
* Register a plugin
|
|
115
|
-
*/
|
|
116
101
|
register(plugin) {
|
|
117
102
|
if (!plugin.name || !plugin.version) {
|
|
118
103
|
throw new Error("Plugin must have a name and version");
|
|
119
104
|
}
|
|
120
105
|
const existing = this.plugins.find((p) => p.name === plugin.name);
|
|
121
106
|
if (existing) {
|
|
122
|
-
throw new Error(
|
|
123
|
-
`Plugin "${plugin.name}" is already registered (version ${existing.version})`
|
|
124
|
-
);
|
|
107
|
+
throw new Error(`Plugin "${plugin.name}" is already registered (version ${existing.version})`);
|
|
125
108
|
}
|
|
126
109
|
this.plugins.push(plugin);
|
|
127
110
|
this.logger.debug(`Registered plugin: ${plugin.name}@${plugin.version}`);
|
|
128
111
|
}
|
|
129
|
-
/**
|
|
130
|
-
* Get all registered plugins
|
|
131
|
-
*/
|
|
132
112
|
getPlugins() {
|
|
133
113
|
return [...this.plugins];
|
|
134
114
|
}
|
|
135
|
-
/**
|
|
136
|
-
* Transform user config through all plugins
|
|
137
|
-
*/
|
|
138
115
|
async transformConfig(config) {
|
|
139
116
|
this.executionContext.phase = "config";
|
|
140
117
|
let transformedConfig = { ...config };
|
|
@@ -153,9 +130,7 @@ var PluginManager = class {
|
|
|
153
130
|
success: false,
|
|
154
131
|
error: err
|
|
155
132
|
});
|
|
156
|
-
throw new Error(
|
|
157
|
-
`Plugin "${plugin.name}" failed during config transformation: ${err.message}`
|
|
158
|
-
);
|
|
133
|
+
throw new Error(`Plugin "${plugin.name}" failed during config transformation: ${err.message}`);
|
|
159
134
|
}
|
|
160
135
|
}
|
|
161
136
|
}
|
|
@@ -165,9 +140,6 @@ var PluginManager = class {
|
|
|
165
140
|
};
|
|
166
141
|
return resolvedConfig;
|
|
167
142
|
}
|
|
168
|
-
/**
|
|
169
|
-
* Transform contracts through all plugins
|
|
170
|
-
*/
|
|
171
143
|
async transformContracts(contracts, _config) {
|
|
172
144
|
const processedContracts = [];
|
|
173
145
|
for (let contract of contracts) {
|
|
@@ -185,6 +157,20 @@ var PluginManager = class {
|
|
|
185
157
|
processedContracts.push(processed);
|
|
186
158
|
continue;
|
|
187
159
|
}
|
|
160
|
+
if (contract._directFile && contract.abi) {
|
|
161
|
+
const address = typeof contract.address === "string" ? contract.address : "";
|
|
162
|
+
const [contractAddress, contractName] = address.split(".");
|
|
163
|
+
const processed = {
|
|
164
|
+
name: contract.name || contractName,
|
|
165
|
+
address: contractAddress,
|
|
166
|
+
contractName,
|
|
167
|
+
abi: contract.abi,
|
|
168
|
+
source: "local",
|
|
169
|
+
metadata: { source: "direct" }
|
|
170
|
+
};
|
|
171
|
+
processedContracts.push(processed);
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
188
174
|
for (const plugin of this.plugins) {
|
|
189
175
|
if (plugin.transformContract) {
|
|
190
176
|
this.executionContext.currentPlugin = plugin;
|
|
@@ -199,9 +185,7 @@ var PluginManager = class {
|
|
|
199
185
|
success: false,
|
|
200
186
|
error: err
|
|
201
187
|
});
|
|
202
|
-
this.logger.warn(
|
|
203
|
-
`Plugin "${plugin.name}" failed to transform contract: ${err.message}`
|
|
204
|
-
);
|
|
188
|
+
this.logger.warn(`Plugin "${plugin.name}" failed to transform contract: ${err.message}`);
|
|
205
189
|
}
|
|
206
190
|
}
|
|
207
191
|
}
|
|
@@ -212,7 +196,6 @@ var PluginManager = class {
|
|
|
212
196
|
contractName: contract.name || "unknown",
|
|
213
197
|
abi: contract.abi,
|
|
214
198
|
source: "api",
|
|
215
|
-
// Use "api" as default for plugin-processed contracts
|
|
216
199
|
metadata: contract.metadata
|
|
217
200
|
};
|
|
218
201
|
processedContracts.push(processed);
|
|
@@ -220,9 +203,6 @@ var PluginManager = class {
|
|
|
220
203
|
}
|
|
221
204
|
return processedContracts;
|
|
222
205
|
}
|
|
223
|
-
/**
|
|
224
|
-
* Execute lifecycle hooks
|
|
225
|
-
*/
|
|
226
206
|
async executeHook(hookName, context) {
|
|
227
207
|
for (const plugin of this.plugins) {
|
|
228
208
|
const hook = plugin[hookName];
|
|
@@ -239,19 +219,14 @@ var PluginManager = class {
|
|
|
239
219
|
success: false,
|
|
240
220
|
error: err
|
|
241
221
|
});
|
|
242
|
-
this.logger.error(
|
|
243
|
-
`Plugin "${plugin.name}" failed during ${hookName}: ${err.message}`
|
|
244
|
-
);
|
|
222
|
+
this.logger.error(`Plugin "${plugin.name}" failed during ${hookName}: ${err.message}`);
|
|
245
223
|
}
|
|
246
224
|
}
|
|
247
225
|
}
|
|
248
226
|
}
|
|
249
|
-
/**
|
|
250
|
-
* Execute generation phase with full context
|
|
251
|
-
*/
|
|
252
227
|
async executeGeneration(contracts, config) {
|
|
253
228
|
this.executionContext.phase = "generate";
|
|
254
|
-
const outputs =
|
|
229
|
+
const outputs = new Map;
|
|
255
230
|
const context = {
|
|
256
231
|
config,
|
|
257
232
|
logger: this.logger,
|
|
@@ -270,22 +245,16 @@ var PluginManager = class {
|
|
|
270
245
|
await this.executeHook("afterGenerate", context);
|
|
271
246
|
return outputs;
|
|
272
247
|
}
|
|
273
|
-
/**
|
|
274
|
-
* Transform outputs through plugins
|
|
275
|
-
*/
|
|
276
248
|
async transformOutputs(outputs) {
|
|
277
249
|
this.executionContext.phase = "output";
|
|
278
|
-
const transformedOutputs =
|
|
250
|
+
const transformedOutputs = new Map;
|
|
279
251
|
for (const [key, output] of outputs) {
|
|
280
252
|
let transformedContent = output.content;
|
|
281
253
|
for (const plugin of this.plugins) {
|
|
282
254
|
if (plugin.transformOutput) {
|
|
283
255
|
this.executionContext.currentPlugin = plugin;
|
|
284
256
|
try {
|
|
285
|
-
transformedContent = await plugin.transformOutput(
|
|
286
|
-
transformedContent,
|
|
287
|
-
output.type || "other"
|
|
288
|
-
);
|
|
257
|
+
transformedContent = await plugin.transformOutput(transformedContent, output.type || "other");
|
|
289
258
|
this.recordHookResult(plugin.name, "transformOutput", {
|
|
290
259
|
success: true
|
|
291
260
|
});
|
|
@@ -295,9 +264,7 @@ var PluginManager = class {
|
|
|
295
264
|
success: false,
|
|
296
265
|
error: err
|
|
297
266
|
});
|
|
298
|
-
this.logger.warn(
|
|
299
|
-
`Plugin "${plugin.name}" failed to transform output: ${err.message}`
|
|
300
|
-
);
|
|
267
|
+
this.logger.warn(`Plugin "${plugin.name}" failed to transform output: ${err.message}`);
|
|
301
268
|
}
|
|
302
269
|
}
|
|
303
270
|
}
|
|
@@ -308,14 +275,11 @@ var PluginManager = class {
|
|
|
308
275
|
}
|
|
309
276
|
return transformedOutputs;
|
|
310
277
|
}
|
|
311
|
-
/**
|
|
312
|
-
* Write outputs to disk
|
|
313
|
-
*/
|
|
314
278
|
async writeOutputs(outputs) {
|
|
315
279
|
for (const [, output] of outputs) {
|
|
316
280
|
try {
|
|
317
|
-
const resolvedPath =
|
|
318
|
-
await this.utils.ensureDir(
|
|
281
|
+
const resolvedPath = path.resolve(process.cwd(), output.path);
|
|
282
|
+
await this.utils.ensureDir(path.dirname(resolvedPath));
|
|
319
283
|
await this.utils.writeFile(resolvedPath, output.content);
|
|
320
284
|
} catch (error) {
|
|
321
285
|
const err = error;
|
|
@@ -324,15 +288,9 @@ var PluginManager = class {
|
|
|
324
288
|
}
|
|
325
289
|
}
|
|
326
290
|
}
|
|
327
|
-
/**
|
|
328
|
-
* Get execution results for debugging
|
|
329
|
-
*/
|
|
330
291
|
getExecutionResults() {
|
|
331
292
|
return new Map(this.executionContext.results);
|
|
332
293
|
}
|
|
333
|
-
/**
|
|
334
|
-
* Augment existing output with additional content
|
|
335
|
-
*/
|
|
336
294
|
augmentOutput(outputs, outputKey, contractName, content) {
|
|
337
295
|
const existing = outputs.get(outputKey);
|
|
338
296
|
if (!existing) {
|
|
@@ -348,18 +306,12 @@ ${JSON.stringify(content, null, 2)}`;
|
|
|
348
306
|
content: augmentedContent
|
|
349
307
|
});
|
|
350
308
|
}
|
|
351
|
-
/**
|
|
352
|
-
* Record hook execution result
|
|
353
|
-
*/
|
|
354
309
|
recordHookResult(pluginName, hookName, result) {
|
|
355
310
|
const key = `${pluginName}:${hookName}`;
|
|
356
311
|
const existing = this.executionContext.results.get(key) || [];
|
|
357
312
|
existing.push({ ...result, plugin: pluginName });
|
|
358
313
|
this.executionContext.results.set(key, existing);
|
|
359
314
|
}
|
|
360
|
-
/**
|
|
361
|
-
* Create logger instance
|
|
362
|
-
*/
|
|
363
315
|
createLogger() {
|
|
364
316
|
return {
|
|
365
317
|
info: (message) => console.log(`\u2139\uFE0F ${message}`),
|
|
@@ -367,15 +319,12 @@ ${JSON.stringify(content, null, 2)}`;
|
|
|
367
319
|
error: (message) => console.error(`\u274C ${message}`),
|
|
368
320
|
debug: (message) => {
|
|
369
321
|
if (process.env.DEBUG) {
|
|
370
|
-
console.log(`\
|
|
322
|
+
console.log(`\uD83D\uDC1B ${message}`);
|
|
371
323
|
}
|
|
372
324
|
},
|
|
373
325
|
success: (message) => console.log(`\u2705 ${message}`)
|
|
374
326
|
};
|
|
375
327
|
}
|
|
376
|
-
/**
|
|
377
|
-
* Create utils instance
|
|
378
|
-
*/
|
|
379
328
|
createUtils() {
|
|
380
329
|
return {
|
|
381
330
|
toCamelCase: (str) => {
|
|
@@ -401,7 +350,7 @@ ${JSON.stringify(content, null, 2)}`;
|
|
|
401
350
|
});
|
|
402
351
|
},
|
|
403
352
|
resolvePath: (relativePath) => {
|
|
404
|
-
return
|
|
353
|
+
return path.resolve(process.cwd(), relativePath);
|
|
405
354
|
},
|
|
406
355
|
fileExists: async (filePath) => {
|
|
407
356
|
try {
|
|
@@ -422,32 +371,29 @@ ${JSON.stringify(content, null, 2)}`;
|
|
|
422
371
|
}
|
|
423
372
|
};
|
|
424
373
|
}
|
|
425
|
-
}
|
|
374
|
+
}
|
|
426
375
|
|
|
427
376
|
// src/utils/config.ts
|
|
428
377
|
var CONFIG_FILE_NAMES = [
|
|
429
378
|
"stacks.config.ts",
|
|
430
|
-
"stacks.config
|
|
379
|
+
"stacks.config",
|
|
431
380
|
"stacks.config.mjs"
|
|
432
381
|
];
|
|
433
382
|
async function findConfigFile(cwd) {
|
|
434
383
|
for (const fileName of CONFIG_FILE_NAMES) {
|
|
435
|
-
const filePath =
|
|
384
|
+
const filePath = path2.join(cwd, fileName);
|
|
436
385
|
try {
|
|
437
386
|
await fs2.access(filePath);
|
|
438
387
|
return filePath;
|
|
439
|
-
} catch {
|
|
440
|
-
}
|
|
388
|
+
} catch {}
|
|
441
389
|
}
|
|
442
390
|
return null;
|
|
443
391
|
}
|
|
444
392
|
async function loadConfig(configPath) {
|
|
445
393
|
const cwd = process.cwd();
|
|
446
|
-
const resolvedPath = configPath ?
|
|
394
|
+
const resolvedPath = configPath ? path2.resolve(cwd, configPath) : await findConfigFile(cwd);
|
|
447
395
|
if (!resolvedPath) {
|
|
448
|
-
throw new Error(
|
|
449
|
-
"No config file found. Create a stacks.config.ts file or specify a path with --config"
|
|
450
|
-
);
|
|
396
|
+
throw new Error("No config file found. Create a stacks.config.ts file or specify a path with --config");
|
|
451
397
|
}
|
|
452
398
|
let config;
|
|
453
399
|
if (resolvedPath.endsWith(".ts")) {
|
|
@@ -455,17 +401,14 @@ async function loadConfig(configPath) {
|
|
|
455
401
|
let replacementPath;
|
|
456
402
|
try {
|
|
457
403
|
const require2 = createRequire(import.meta.url);
|
|
458
|
-
const packagePath = require2.resolve("@
|
|
404
|
+
const packagePath = require2.resolve("@secondlayer/cli");
|
|
459
405
|
replacementPath = pathToFileURL(packagePath).href;
|
|
460
406
|
} catch {
|
|
461
|
-
const currentModuleDir =
|
|
462
|
-
const indexPath =
|
|
407
|
+
const currentModuleDir = path2.dirname(new URL(import.meta.url).pathname);
|
|
408
|
+
const indexPath = path2.resolve(currentModuleDir, "../index");
|
|
463
409
|
replacementPath = pathToFileURL(indexPath).href;
|
|
464
410
|
}
|
|
465
|
-
const transformedCode = code.replace(
|
|
466
|
-
/from\s+["']@stacks\/cli["']/g,
|
|
467
|
-
`from '${replacementPath}'`
|
|
468
|
-
);
|
|
411
|
+
const transformedCode = code.replace(/from\s+["']@stacks\/cli["']/g, `from '${replacementPath}'`);
|
|
469
412
|
const result = transformSync(transformedCode, {
|
|
470
413
|
format: "esm",
|
|
471
414
|
target: "node18",
|
|
@@ -478,8 +421,7 @@ async function loadConfig(configPath) {
|
|
|
478
421
|
const module = await import(fileUrl);
|
|
479
422
|
config = module.default;
|
|
480
423
|
} finally {
|
|
481
|
-
await fs2.unlink(tempPath).catch(() => {
|
|
482
|
-
});
|
|
424
|
+
await fs2.unlink(tempPath).catch(() => {});
|
|
483
425
|
}
|
|
484
426
|
} else {
|
|
485
427
|
const fileUrl = pathToFileURL(resolvedPath).href;
|
|
@@ -493,7 +435,7 @@ async function loadConfig(configPath) {
|
|
|
493
435
|
config = config({});
|
|
494
436
|
}
|
|
495
437
|
validateConfig(config);
|
|
496
|
-
const pluginManager = new PluginManager
|
|
438
|
+
const pluginManager = new PluginManager;
|
|
497
439
|
if (config.plugins && Array.isArray(config.plugins)) {
|
|
498
440
|
for (const plugin of config.plugins) {
|
|
499
441
|
pluginManager.register(plugin);
|
|
@@ -526,22 +468,241 @@ function validateConfig(config) {
|
|
|
526
468
|
}
|
|
527
469
|
|
|
528
470
|
// src/utils/api.ts
|
|
529
|
-
init_esm_shims();
|
|
530
471
|
import got from "got";
|
|
472
|
+
var API_URLS = {
|
|
473
|
+
mainnet: "https://api.hiro.so",
|
|
474
|
+
testnet: "https://api.testnet.hiro.so",
|
|
475
|
+
devnet: "http://localhost:3999"
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
class StacksApiClient {
|
|
479
|
+
baseUrl;
|
|
480
|
+
headers;
|
|
481
|
+
constructor(network = "mainnet", apiKey, apiUrl) {
|
|
482
|
+
this.baseUrl = apiUrl || API_URLS[network];
|
|
483
|
+
this.headers = apiKey ? { "x-api-key": apiKey } : {};
|
|
484
|
+
}
|
|
485
|
+
async getContractInfo(contractId) {
|
|
486
|
+
const [address, contractName] = contractId.split(".");
|
|
487
|
+
if (!address || !contractName) {
|
|
488
|
+
throw new Error(`Invalid contract ID format: ${contractId}. Expected format: ADDRESS.CONTRACT_NAME`);
|
|
489
|
+
}
|
|
490
|
+
const url = `${this.baseUrl}/v2/contracts/interface/${address}/${contractName}`;
|
|
491
|
+
try {
|
|
492
|
+
const response = await got(url, {
|
|
493
|
+
headers: this.headers,
|
|
494
|
+
responseType: "json"
|
|
495
|
+
});
|
|
496
|
+
return response.body;
|
|
497
|
+
} catch (error) {
|
|
498
|
+
if (error.response?.statusCode === 404) {
|
|
499
|
+
throw new Error(`Contract not found: ${contractId}`);
|
|
500
|
+
}
|
|
501
|
+
if (error.response?.statusCode === 429) {
|
|
502
|
+
throw new Error("Rate limited. Please provide an API key in your config.");
|
|
503
|
+
}
|
|
504
|
+
throw new Error(`Failed to fetch contract: ${error.message}`);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
async getContractSource(contractId) {
|
|
508
|
+
const [address, contractName] = contractId.split(".");
|
|
509
|
+
if (!address || !contractName) {
|
|
510
|
+
throw new Error(`Invalid contract ID format: ${contractId}. Expected format: ADDRESS.CONTRACT_NAME`);
|
|
511
|
+
}
|
|
512
|
+
const url = `${this.baseUrl}/v2/contracts/source/${address}/${contractName}`;
|
|
513
|
+
try {
|
|
514
|
+
const response = await got(url, {
|
|
515
|
+
headers: this.headers,
|
|
516
|
+
responseType: "json"
|
|
517
|
+
});
|
|
518
|
+
const data = response.body;
|
|
519
|
+
return data.source;
|
|
520
|
+
} catch (error) {
|
|
521
|
+
return "";
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
531
525
|
|
|
532
526
|
// src/parsers/clarity.ts
|
|
533
|
-
|
|
527
|
+
import { promises as fs3 } from "fs";
|
|
528
|
+
async function parseClarityFile(filePath) {
|
|
529
|
+
const content = await fs3.readFile(filePath, "utf-8");
|
|
530
|
+
return parseClarityContent(content);
|
|
531
|
+
}
|
|
532
|
+
function parseClarityContent(content) {
|
|
533
|
+
const functions = [];
|
|
534
|
+
const functionRegex = /\(define-(public|read-only|private)\s+\(([^)]+)\)([\s\S]*?)\)\s*$/gm;
|
|
535
|
+
let match;
|
|
536
|
+
while ((match = functionRegex.exec(content)) !== null) {
|
|
537
|
+
const [, access, signature, body] = match;
|
|
538
|
+
const func = parseFunctionSignature(signature, access, body);
|
|
539
|
+
if (func) {
|
|
540
|
+
functions.push(func);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return { functions };
|
|
544
|
+
}
|
|
545
|
+
function parseFunctionSignature(signature, access, body) {
|
|
546
|
+
const parts = signature.trim().split(/\s+/);
|
|
547
|
+
const name = parts[0];
|
|
548
|
+
const args = [];
|
|
549
|
+
for (let i = 1;i < parts.length; i += 2) {
|
|
550
|
+
if (parts[i] && parts[i + 1]) {
|
|
551
|
+
const argName = parts[i].replace(/[()]/g, "");
|
|
552
|
+
const argType = parseType(parts[i + 1]);
|
|
553
|
+
if (argType) {
|
|
554
|
+
args.push({ name: argName, type: argType });
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
const outputs = inferReturnType(body);
|
|
559
|
+
return {
|
|
560
|
+
name,
|
|
561
|
+
access,
|
|
562
|
+
args,
|
|
563
|
+
outputs
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
function parseType(typeStr) {
|
|
567
|
+
typeStr = typeStr.replace(/[()]/g, "").trim();
|
|
568
|
+
switch (typeStr) {
|
|
569
|
+
case "uint":
|
|
570
|
+
case "uint128":
|
|
571
|
+
return "uint128";
|
|
572
|
+
case "int":
|
|
573
|
+
case "int128":
|
|
574
|
+
return "int128";
|
|
575
|
+
case "bool":
|
|
576
|
+
return "bool";
|
|
577
|
+
case "principal":
|
|
578
|
+
return "principal";
|
|
579
|
+
case "trait_reference":
|
|
580
|
+
return "principal";
|
|
581
|
+
default:
|
|
582
|
+
if (typeStr.startsWith("string-ascii")) {
|
|
583
|
+
return { "string-ascii": { length: 256 } };
|
|
584
|
+
}
|
|
585
|
+
if (typeStr.startsWith("string-utf8")) {
|
|
586
|
+
return { "string-utf8": { length: 256 } };
|
|
587
|
+
}
|
|
588
|
+
if (typeStr.startsWith("buff")) {
|
|
589
|
+
return { buff: { length: 32 } };
|
|
590
|
+
}
|
|
591
|
+
return "uint128";
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
function inferReturnType(body) {
|
|
595
|
+
if (body.includes("(ok")) {
|
|
596
|
+
if (body.includes("(err")) {
|
|
597
|
+
return {
|
|
598
|
+
response: {
|
|
599
|
+
ok: "bool",
|
|
600
|
+
error: "uint128"
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
if (body.includes("true") || body.includes("false")) {
|
|
606
|
+
return "bool";
|
|
607
|
+
}
|
|
608
|
+
return "bool";
|
|
609
|
+
}
|
|
610
|
+
function parseApiResponse(apiResponse) {
|
|
611
|
+
try {
|
|
612
|
+
const functions = [];
|
|
613
|
+
if (apiResponse.functions) {
|
|
614
|
+
for (const func of apiResponse.functions) {
|
|
615
|
+
const access = func.access === "read_only" ? "read-only" : func.access;
|
|
616
|
+
functions.push({
|
|
617
|
+
name: func.name,
|
|
618
|
+
access,
|
|
619
|
+
args: func.args.map((arg) => ({
|
|
620
|
+
name: arg.name,
|
|
621
|
+
type: convertApiType(arg.type)
|
|
622
|
+
})),
|
|
623
|
+
outputs: convertApiType(func.outputs.type)
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
return { functions };
|
|
628
|
+
} catch (error) {
|
|
629
|
+
throw new Error(`Failed to parse API response: ${error}`);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
function convertApiType(apiType) {
|
|
633
|
+
if (typeof apiType === "string") {
|
|
634
|
+
if (apiType === "trait_reference") {
|
|
635
|
+
return "trait_reference";
|
|
636
|
+
}
|
|
637
|
+
return parseType(apiType) || "uint128";
|
|
638
|
+
}
|
|
639
|
+
if (apiType.response) {
|
|
640
|
+
return {
|
|
641
|
+
response: {
|
|
642
|
+
ok: convertApiType(apiType.response.ok),
|
|
643
|
+
error: convertApiType(apiType.response.error)
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
if (apiType.optional) {
|
|
648
|
+
return {
|
|
649
|
+
optional: convertApiType(apiType.optional)
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
if (apiType.list) {
|
|
653
|
+
return {
|
|
654
|
+
list: {
|
|
655
|
+
type: convertApiType(apiType.list.type),
|
|
656
|
+
length: apiType.list.length || 100
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
if (apiType.tuple) {
|
|
661
|
+
return {
|
|
662
|
+
tuple: apiType.tuple.map((field) => ({
|
|
663
|
+
name: field.name,
|
|
664
|
+
type: convertApiType(field.type)
|
|
665
|
+
}))
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
if (apiType.buffer) {
|
|
669
|
+
return {
|
|
670
|
+
buff: {
|
|
671
|
+
length: apiType.buffer.length || 32
|
|
672
|
+
}
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
if (apiType["string-ascii"]) {
|
|
676
|
+
return {
|
|
677
|
+
"string-ascii": {
|
|
678
|
+
length: apiType["string-ascii"].length || 256
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
if (apiType["string-utf8"]) {
|
|
683
|
+
return {
|
|
684
|
+
"string-utf8": {
|
|
685
|
+
length: apiType["string-utf8"].length || 256
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
if (apiType === "none") {
|
|
690
|
+
return "uint128";
|
|
691
|
+
}
|
|
692
|
+
return "uint128";
|
|
693
|
+
}
|
|
534
694
|
|
|
535
695
|
// src/generators/contract.ts
|
|
536
|
-
init_esm_shims();
|
|
537
696
|
import { format as format2 } from "prettier";
|
|
538
697
|
async function generateContractInterface(contracts) {
|
|
539
698
|
const imports = `import { Cl, validateStacksAddress } from '@stacks/transactions'`;
|
|
540
699
|
const header = `/**
|
|
541
|
-
* Generated by @
|
|
700
|
+
* Generated by @secondlayer/cli
|
|
542
701
|
* DO NOT EDIT MANUALLY
|
|
543
702
|
*/`;
|
|
544
|
-
const contractsCode = contracts.map((contract) => generateContract(contract)).join(
|
|
703
|
+
const contractsCode = contracts.map((contract) => generateContract(contract)).join(`
|
|
704
|
+
|
|
705
|
+
`);
|
|
545
706
|
const code = `${imports}
|
|
546
707
|
|
|
547
708
|
${header}
|
|
@@ -559,7 +720,9 @@ ${contractsCode}`;
|
|
|
559
720
|
function generateContract(contract) {
|
|
560
721
|
const { name, address, contractName, abi } = contract;
|
|
561
722
|
const abiCode = generateAbiConstant(name, abi);
|
|
562
|
-
const methods = abi.functions.filter((func) => func.access !== "private").map((func) => generateMethod(func, address, contractName)).join(
|
|
723
|
+
const methods = abi.functions.filter((func) => func.access !== "private").map((func) => generateMethod(func, address, contractName)).join(`,
|
|
724
|
+
|
|
725
|
+
`);
|
|
563
726
|
const contractCode = `export const ${name} = {
|
|
564
727
|
address: '${address}',
|
|
565
728
|
contractAddress: '${address}',
|
|
@@ -663,9 +826,7 @@ function getTypeForArg(arg) {
|
|
|
663
826
|
return `${innerType}[]`;
|
|
664
827
|
}
|
|
665
828
|
if (type.tuple) {
|
|
666
|
-
const fields = type.tuple.map(
|
|
667
|
-
(field) => `${toCamelCase(field.name)}: ${getTypeForArg({ type: field.type })}`
|
|
668
|
-
).join("; ");
|
|
829
|
+
const fields = type.tuple.map((field) => `${toCamelCase(field.name)}: ${getTypeForArg({ type: field.type })}`).join("; ");
|
|
669
830
|
return `{ ${fields} }`;
|
|
670
831
|
}
|
|
671
832
|
if (type.response) {
|
|
@@ -762,10 +923,7 @@ function generateClarityConversion(argName, argType) {
|
|
|
762
923
|
if (type.tuple) {
|
|
763
924
|
const fields = type.tuple.map((field) => {
|
|
764
925
|
const camelFieldName = toCamelCase(field.name);
|
|
765
|
-
const fieldConversion = generateClarityConversion(
|
|
766
|
-
`${argName}.${camelFieldName}`,
|
|
767
|
-
{ type: field.type }
|
|
768
|
-
);
|
|
926
|
+
const fieldConversion = generateClarityConversion(`${argName}.${camelFieldName}`, { type: field.type });
|
|
769
927
|
return `"${field.name}": ${fieldConversion}`;
|
|
770
928
|
}).join(", ");
|
|
771
929
|
return `Cl.tuple({ ${fields} })`;
|
|
@@ -783,43 +941,144 @@ function generateClarityConversion(argName, argType) {
|
|
|
783
941
|
}
|
|
784
942
|
|
|
785
943
|
// src/commands/generate.ts
|
|
786
|
-
|
|
944
|
+
function isContractAddress(input) {
|
|
945
|
+
const contractIdPattern = /^(SP|ST|SM|SN)[A-Z0-9]{38,}\.[a-zA-Z][a-zA-Z0-9-]*$/;
|
|
946
|
+
return contractIdPattern.test(input);
|
|
947
|
+
}
|
|
948
|
+
function inferNetwork(address) {
|
|
949
|
+
if (address.startsWith("SP") || address.startsWith("SM")) {
|
|
950
|
+
return "mainnet";
|
|
951
|
+
}
|
|
952
|
+
return "testnet";
|
|
953
|
+
}
|
|
954
|
+
async function parseInputs(inputs) {
|
|
955
|
+
const files = [];
|
|
956
|
+
const contractIds = [];
|
|
957
|
+
for (const input of inputs) {
|
|
958
|
+
if (isContractAddress(input)) {
|
|
959
|
+
contractIds.push(input);
|
|
960
|
+
continue;
|
|
961
|
+
}
|
|
962
|
+
if (input.includes("*") || input.includes("?")) {
|
|
963
|
+
const glob = new Glob(input);
|
|
964
|
+
for await (const file of glob.scan({ cwd: process.cwd(), absolute: true })) {
|
|
965
|
+
if (file.endsWith(".clar")) {
|
|
966
|
+
files.push(file);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
continue;
|
|
970
|
+
}
|
|
971
|
+
if (input.endsWith(".clar")) {
|
|
972
|
+
const absolutePath = path3.resolve(process.cwd(), input);
|
|
973
|
+
files.push(absolutePath);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
return {
|
|
977
|
+
files: [...new Set(files)],
|
|
978
|
+
contractIds: [...new Set(contractIds)]
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
function deriveContractName(filePath) {
|
|
982
|
+
const basename = path3.basename(filePath, ".clar");
|
|
983
|
+
return basename.replace(/[-_](.)/g, (_, char) => char.toUpperCase()).replace(/^(.)/, (_, char) => char.toLowerCase()).replace(/^\d/, "_$&");
|
|
984
|
+
}
|
|
985
|
+
function toCamelCase2(str) {
|
|
986
|
+
return str.replace(/[-_](.)/g, (_, char) => char.toUpperCase()).replace(/^(.)/, (_, char) => char.toLowerCase()).replace(/^\d/, "_$&");
|
|
987
|
+
}
|
|
988
|
+
async function buildConfigFromInputs(parsedInputs, outPath, apiKey, spinner) {
|
|
989
|
+
const contracts = [];
|
|
990
|
+
for (const file of parsedInputs.files) {
|
|
991
|
+
const abi = await parseClarityFile(file);
|
|
992
|
+
const name = deriveContractName(file);
|
|
993
|
+
contracts.push({
|
|
994
|
+
name,
|
|
995
|
+
address: `ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.${name}`,
|
|
996
|
+
abi,
|
|
997
|
+
_directFile: true
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
for (const contractId of parsedInputs.contractIds) {
|
|
1001
|
+
const [address, contractName] = contractId.split(".");
|
|
1002
|
+
const network = inferNetwork(address);
|
|
1003
|
+
spinner.text = `Fetching ${contractName} from ${network}...`;
|
|
1004
|
+
try {
|
|
1005
|
+
const apiClient = new StacksApiClient(network, apiKey);
|
|
1006
|
+
const contractInfo = await apiClient.getContractInfo(contractId);
|
|
1007
|
+
const abi = parseApiResponse(contractInfo);
|
|
1008
|
+
const name = toCamelCase2(contractName);
|
|
1009
|
+
contracts.push({
|
|
1010
|
+
name,
|
|
1011
|
+
address: contractId,
|
|
1012
|
+
abi,
|
|
1013
|
+
_directFile: true
|
|
1014
|
+
});
|
|
1015
|
+
} catch (error) {
|
|
1016
|
+
throw new Error(`Failed to fetch contract ${contractId}: ${error.message}`);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
return {
|
|
1020
|
+
out: outPath,
|
|
1021
|
+
contracts,
|
|
1022
|
+
plugins: []
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
async function generate(files, options) {
|
|
787
1026
|
const spinner = ora("Processing contracts").start();
|
|
788
1027
|
try {
|
|
789
|
-
|
|
790
|
-
|
|
1028
|
+
let config;
|
|
1029
|
+
if (files && files.length > 0) {
|
|
1030
|
+
if (!options.out) {
|
|
1031
|
+
spinner.fail("Output path required");
|
|
1032
|
+
console.error(chalk.red(`
|
|
1033
|
+
When using direct inputs, you must specify an output path with -o/--out`));
|
|
1034
|
+
console.log(chalk.gray(`
|
|
1035
|
+
Examples:`));
|
|
1036
|
+
console.log(chalk.gray(" secondlayer generate ./contracts/*.clar -o ./src/generated.ts"));
|
|
1037
|
+
console.log(chalk.gray(" secondlayer generate SP2C2YFP12AJZB1M6DY7SF9A3PRHWKGYGVWQKW3.my-token -o ./src/generated.ts"));
|
|
1038
|
+
process.exit(1);
|
|
1039
|
+
}
|
|
1040
|
+
const parsedInputs = await parseInputs(files);
|
|
1041
|
+
const totalInputs = parsedInputs.files.length + parsedInputs.contractIds.length;
|
|
1042
|
+
if (totalInputs === 0) {
|
|
1043
|
+
spinner.fail("No valid inputs found");
|
|
1044
|
+
console.error(chalk.red(`
|
|
1045
|
+
No .clar files or contract addresses matched the provided inputs`));
|
|
1046
|
+
process.exit(1);
|
|
1047
|
+
}
|
|
1048
|
+
spinner.text = `Processing ${totalInputs} contract(s)...`;
|
|
1049
|
+
const apiKey = options.apiKey || process.env.HIRO_API_KEY;
|
|
1050
|
+
config = await buildConfigFromInputs(parsedInputs, options.out, apiKey, spinner);
|
|
1051
|
+
} else {
|
|
1052
|
+
config = await loadConfig(options.config);
|
|
1053
|
+
}
|
|
1054
|
+
const pluginManager = new PluginManager;
|
|
791
1055
|
if (config.plugins) {
|
|
792
1056
|
for (const plugin of config.plugins) {
|
|
793
1057
|
pluginManager.register(plugin);
|
|
794
1058
|
}
|
|
795
1059
|
}
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
(
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
);
|
|
808
|
-
const processedContracts = await pluginManager.transformContracts(
|
|
809
|
-
contractConfigs,
|
|
810
|
-
config
|
|
811
|
-
);
|
|
1060
|
+
const resolvedConfig = {
|
|
1061
|
+
...config,
|
|
1062
|
+
plugins: pluginManager.getPlugins()
|
|
1063
|
+
};
|
|
1064
|
+
await pluginManager.executeHook("configResolved", resolvedConfig);
|
|
1065
|
+
const contractConfigs = (config.contracts || []).map((contract) => ({
|
|
1066
|
+
name: contract.name,
|
|
1067
|
+
address: contract.address,
|
|
1068
|
+
source: contract.source,
|
|
1069
|
+
abi: contract.abi,
|
|
1070
|
+
_clarinetSource: contract._clarinetSource
|
|
1071
|
+
}));
|
|
1072
|
+
const processedContracts = await pluginManager.transformContracts(contractConfigs, resolvedConfig);
|
|
812
1073
|
if (processedContracts.length === 0) {
|
|
813
1074
|
spinner.warn("No contracts found to generate");
|
|
814
|
-
console.log(
|
|
1075
|
+
console.log(`
|
|
1076
|
+
To get started:`);
|
|
815
1077
|
console.log(" \u2022 Add contracts to your config file, or");
|
|
816
1078
|
console.log(" \u2022 Use plugins like clarinet() for local contracts");
|
|
817
1079
|
return;
|
|
818
1080
|
}
|
|
819
|
-
const outputs = await pluginManager.executeGeneration(
|
|
820
|
-
processedContracts,
|
|
821
|
-
config
|
|
822
|
-
);
|
|
1081
|
+
const outputs = await pluginManager.executeGeneration(processedContracts, resolvedConfig);
|
|
823
1082
|
if (!outputs.has("contracts") && processedContracts.length > 0) {
|
|
824
1083
|
const contractsCode = await generateContractInterface(processedContracts);
|
|
825
1084
|
outputs.set("contracts", {
|
|
@@ -834,22 +1093,14 @@ async function generate(options) {
|
|
|
834
1093
|
const contractWord = contractCount === 1 ? "contract" : "contracts";
|
|
835
1094
|
spinner.succeed(`Generation complete for ${contractCount} ${contractWord}`);
|
|
836
1095
|
console.log(`
|
|
837
|
-
\
|
|
1096
|
+
\uD83D\uDCC4 ${config.out}`);
|
|
838
1097
|
console.log(`
|
|
839
|
-
\
|
|
1098
|
+
\uD83D\uDCA1 Import your contracts:`);
|
|
840
1099
|
if (processedContracts.length > 0) {
|
|
841
1100
|
const exampleContract = processedContracts[0];
|
|
842
|
-
console.log(
|
|
843
|
-
chalk.gray(
|
|
844
|
-
` import { ${exampleContract.name} } from '${config.out.replace(/\.ts$/, "")}'`
|
|
845
|
-
)
|
|
846
|
-
);
|
|
1101
|
+
console.log(chalk.gray(` import { ${exampleContract.name} } from '${config.out.replace(/\.ts$/, "")}'`));
|
|
847
1102
|
if (processedContracts.length > 1) {
|
|
848
|
-
console.log(
|
|
849
|
-
chalk.gray(
|
|
850
|
-
` // Also available: ${processedContracts.slice(1).map((c) => c.name).join(", ")}`
|
|
851
|
-
)
|
|
852
|
-
);
|
|
1103
|
+
console.log(chalk.gray(` // Also available: ${processedContracts.slice(1).map((c) => c.name).join(", ")}`));
|
|
853
1104
|
}
|
|
854
1105
|
}
|
|
855
1106
|
} catch (error) {
|
|
@@ -865,10 +1116,12 @@ ${error.message}`));
|
|
|
865
1116
|
|
|
866
1117
|
// src/cli.ts
|
|
867
1118
|
program.name("stacks").description("CLI tool for generating type-safe Stacks contract interfaces").version("0.1.0");
|
|
868
|
-
program.command("generate").alias("gen").description("Generate TypeScript interfaces from Clarity contracts").option("-c, --config <path>", "Path to config file").option("-w, --watch", "Watch for changes").action(generate);
|
|
1119
|
+
program.command("generate [files...]").alias("gen").description("Generate TypeScript interfaces from Clarity contracts").option("-c, --config <path>", "Path to config file").option("-o, --out <path>", "Output file path (required when using direct files)").option("-k, --api-key <key>", "Hiro API key (or set HIRO_API_KEY env var)").option("-w, --watch", "Watch for changes").action(generate);
|
|
869
1120
|
program.command("init").description("Initialize a new stacks.config.ts file").action(async () => {
|
|
870
|
-
const { init: init2 } = await Promise.resolve().then(() => (init_init(),
|
|
1121
|
+
const { init: init2 } = await Promise.resolve().then(() => (init_init(), exports_init));
|
|
871
1122
|
await init2();
|
|
872
1123
|
});
|
|
873
1124
|
program.parse();
|
|
874
|
-
|
|
1125
|
+
|
|
1126
|
+
//# debugId=5EBBA30969CCC98F64756E2164756E21
|
|
1127
|
+
//# sourceMappingURL=cli.js.map
|