@secondlayer/cli 0.1.1 → 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.cjs DELETED
@@ -1,897 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- var __create = Object.create;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __esm = (fn, res) => function __init() {
10
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
- };
12
- var __export = (target, all) => {
13
- for (var name in all)
14
- __defProp(target, name, { get: all[name], enumerable: true });
15
- };
16
- var __copyProps = (to, from, except, desc) => {
17
- if (from && typeof from === "object" || typeof from === "function") {
18
- for (let key of __getOwnPropNames(from))
19
- if (!__hasOwnProp.call(to, key) && key !== except)
20
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
21
- }
22
- return to;
23
- };
24
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
25
- // If the importer is in node compatibility mode or this is not an ESM
26
- // file that has been converted to a CommonJS file using a Babel-
27
- // compatible transform (i.e. "__esModule" has not been set), then set
28
- // "default" to the CommonJS "module.exports" for node compatibility.
29
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
30
- mod
31
- ));
32
-
33
- // node_modules/tsup/assets/cjs_shims.js
34
- var getImportMetaUrl, importMetaUrl;
35
- var init_cjs_shims = __esm({
36
- "node_modules/tsup/assets/cjs_shims.js"() {
37
- "use strict";
38
- getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href;
39
- importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
40
- }
41
- });
42
-
43
- // src/commands/init.ts
44
- var init_exports = {};
45
- __export(init_exports, {
46
- init: () => init
47
- });
48
- async function init() {
49
- const spinner = (0, import_ora2.default)("Initializing").start();
50
- const configPath = import_path3.default.join(process.cwd(), "stacks.config.ts");
51
- try {
52
- await import_fs3.promises.access(configPath);
53
- spinner.warn("stacks.config.ts already exists");
54
- return;
55
- } catch {
56
- }
57
- const hasClarinetProject = await fileExists("./Clarinet.toml");
58
- let config;
59
- if (hasClarinetProject) {
60
- config = `import { defineConfig } from '@secondlayer/cli';
61
- import { clarinet } from '@secondlayer/cli/plugins';
62
-
63
- export default defineConfig({
64
- out: './src/generated/contracts.ts',
65
- plugins: [
66
- clarinet() // Found Clarinet.toml in current directory
67
- ]
68
- });`;
69
- } else {
70
- config = `import { defineConfig } from '@secondlayer/cli';
71
-
72
- export default defineConfig({
73
- out: './src/generated/contracts.ts',
74
- plugins: [],
75
- });`;
76
- }
77
- await import_fs3.promises.writeFile(configPath, config);
78
- spinner.succeed("Created `stacks.config.ts`");
79
- console.log(
80
- "\nRun `secondlayer generate` to generate type-safe interfaces, functions, and hooks!"
81
- );
82
- }
83
- async function fileExists(filePath) {
84
- try {
85
- await import_fs3.promises.access(filePath);
86
- return true;
87
- } catch {
88
- return false;
89
- }
90
- }
91
- var import_fs3, import_path3, import_ora2;
92
- var init_init = __esm({
93
- "src/commands/init.ts"() {
94
- "use strict";
95
- init_cjs_shims();
96
- import_fs3 = require("fs");
97
- import_path3 = __toESM(require("path"), 1);
98
- import_ora2 = __toESM(require("ora"), 1);
99
- }
100
- });
101
-
102
- // src/cli.ts
103
- init_cjs_shims();
104
- var import_commander = require("commander");
105
-
106
- // src/commands/generate.ts
107
- init_cjs_shims();
108
- var import_chalk = __toESM(require("chalk"), 1);
109
- var import_ora = __toESM(require("ora"), 1);
110
-
111
- // src/utils/config.ts
112
- init_cjs_shims();
113
- var import_fs2 = require("fs");
114
- var import_path2 = __toESM(require("path"), 1);
115
- var import_url = require("url");
116
- var import_module = require("module");
117
- var import_esbuild = require("esbuild");
118
-
119
- // src/core/plugin-manager.ts
120
- init_cjs_shims();
121
- var import_prettier = require("prettier");
122
- var import_fs = require("fs");
123
- var import_path = __toESM(require("path"), 1);
124
- var import_transactions = require("@stacks/transactions");
125
- var PluginManager = class {
126
- constructor() {
127
- this.plugins = [];
128
- this.logger = this.createLogger();
129
- this.utils = this.createUtils();
130
- this.executionContext = {
131
- phase: "config",
132
- startTime: Date.now(),
133
- results: /* @__PURE__ */ new Map()
134
- };
135
- }
136
- /**
137
- * Register a plugin
138
- */
139
- register(plugin) {
140
- if (!plugin.name || !plugin.version) {
141
- throw new Error("Plugin must have a name and version");
142
- }
143
- const existing = this.plugins.find((p) => p.name === plugin.name);
144
- if (existing) {
145
- throw new Error(
146
- `Plugin "${plugin.name}" is already registered (version ${existing.version})`
147
- );
148
- }
149
- this.plugins.push(plugin);
150
- this.logger.debug(`Registered plugin: ${plugin.name}@${plugin.version}`);
151
- }
152
- /**
153
- * Get all registered plugins
154
- */
155
- getPlugins() {
156
- return [...this.plugins];
157
- }
158
- /**
159
- * Transform user config through all plugins
160
- */
161
- async transformConfig(config) {
162
- this.executionContext.phase = "config";
163
- let transformedConfig = { ...config };
164
- for (const plugin of this.plugins) {
165
- if (plugin.transformConfig) {
166
- this.executionContext.currentPlugin = plugin;
167
- try {
168
- const result = await plugin.transformConfig(transformedConfig);
169
- transformedConfig = result;
170
- this.recordHookResult(plugin.name, "transformConfig", {
171
- success: true
172
- });
173
- } catch (error) {
174
- const err = error;
175
- this.recordHookResult(plugin.name, "transformConfig", {
176
- success: false,
177
- error: err
178
- });
179
- throw new Error(
180
- `Plugin "${plugin.name}" failed during config transformation: ${err.message}`
181
- );
182
- }
183
- }
184
- }
185
- const resolvedConfig = {
186
- ...transformedConfig,
187
- plugins: this.plugins
188
- };
189
- return resolvedConfig;
190
- }
191
- /**
192
- * Transform contracts through all plugins
193
- */
194
- async transformContracts(contracts, _config) {
195
- const processedContracts = [];
196
- for (let contract of contracts) {
197
- if (contract._clarinetSource && contract.abi) {
198
- const address = typeof contract.address === "string" ? contract.address : "";
199
- const [contractAddress, contractName] = address.split(".");
200
- const processed = {
201
- name: contract.name || contractName,
202
- address: contractAddress,
203
- contractName,
204
- abi: contract.abi,
205
- source: "local",
206
- metadata: { source: "clarinet" }
207
- };
208
- processedContracts.push(processed);
209
- continue;
210
- }
211
- for (const plugin of this.plugins) {
212
- if (plugin.transformContract) {
213
- this.executionContext.currentPlugin = plugin;
214
- try {
215
- contract = await plugin.transformContract(contract);
216
- this.recordHookResult(plugin.name, "transformContract", {
217
- success: true
218
- });
219
- } catch (error) {
220
- const err = error;
221
- this.recordHookResult(plugin.name, "transformContract", {
222
- success: false,
223
- error: err
224
- });
225
- this.logger.warn(
226
- `Plugin "${plugin.name}" failed to transform contract: ${err.message}`
227
- );
228
- }
229
- }
230
- }
231
- if (contract.abi) {
232
- const processed = {
233
- name: contract.name || "unknown",
234
- address: typeof contract.address === "string" ? contract.address.split(".")[0] : "unknown",
235
- contractName: contract.name || "unknown",
236
- abi: contract.abi,
237
- source: "api",
238
- // Use "api" as default for plugin-processed contracts
239
- metadata: contract.metadata
240
- };
241
- processedContracts.push(processed);
242
- }
243
- }
244
- return processedContracts;
245
- }
246
- /**
247
- * Execute lifecycle hooks
248
- */
249
- async executeHook(hookName, context) {
250
- for (const plugin of this.plugins) {
251
- const hook = plugin[hookName];
252
- if (typeof hook === "function") {
253
- this.executionContext.currentPlugin = plugin;
254
- try {
255
- await hook.call(plugin, context);
256
- this.recordHookResult(plugin.name, hookName, {
257
- success: true
258
- });
259
- } catch (error) {
260
- const err = error;
261
- this.recordHookResult(plugin.name, hookName, {
262
- success: false,
263
- error: err
264
- });
265
- this.logger.error(
266
- `Plugin "${plugin.name}" failed during ${hookName}: ${err.message}`
267
- );
268
- }
269
- }
270
- }
271
- }
272
- /**
273
- * Execute generation phase with full context
274
- */
275
- async executeGeneration(contracts, config) {
276
- this.executionContext.phase = "generate";
277
- const outputs = /* @__PURE__ */ new Map();
278
- const context = {
279
- config,
280
- logger: this.logger,
281
- utils: this.utils,
282
- contracts,
283
- outputs,
284
- augment: (outputKey, contractName, content) => {
285
- this.augmentOutput(outputs, outputKey, contractName, content);
286
- },
287
- addOutput: (key, output) => {
288
- outputs.set(key, output);
289
- }
290
- };
291
- await this.executeHook("beforeGenerate", context);
292
- await this.executeHook("generate", context);
293
- await this.executeHook("afterGenerate", context);
294
- return outputs;
295
- }
296
- /**
297
- * Transform outputs through plugins
298
- */
299
- async transformOutputs(outputs) {
300
- this.executionContext.phase = "output";
301
- const transformedOutputs = /* @__PURE__ */ new Map();
302
- for (const [key, output] of outputs) {
303
- let transformedContent = output.content;
304
- for (const plugin of this.plugins) {
305
- if (plugin.transformOutput) {
306
- this.executionContext.currentPlugin = plugin;
307
- try {
308
- transformedContent = await plugin.transformOutput(
309
- transformedContent,
310
- output.type || "other"
311
- );
312
- this.recordHookResult(plugin.name, "transformOutput", {
313
- success: true
314
- });
315
- } catch (error) {
316
- const err = error;
317
- this.recordHookResult(plugin.name, "transformOutput", {
318
- success: false,
319
- error: err
320
- });
321
- this.logger.warn(
322
- `Plugin "${plugin.name}" failed to transform output: ${err.message}`
323
- );
324
- }
325
- }
326
- }
327
- transformedOutputs.set(key, {
328
- ...output,
329
- content: transformedContent
330
- });
331
- }
332
- return transformedOutputs;
333
- }
334
- /**
335
- * Write outputs to disk
336
- */
337
- async writeOutputs(outputs) {
338
- for (const [, output] of outputs) {
339
- try {
340
- const resolvedPath = import_path.default.resolve(process.cwd(), output.path);
341
- await this.utils.ensureDir(import_path.default.dirname(resolvedPath));
342
- await this.utils.writeFile(resolvedPath, output.content);
343
- } catch (error) {
344
- const err = error;
345
- this.logger.error(`Failed to write ${output.path}: ${err.message}`);
346
- throw err;
347
- }
348
- }
349
- }
350
- /**
351
- * Get execution results for debugging
352
- */
353
- getExecutionResults() {
354
- return new Map(this.executionContext.results);
355
- }
356
- /**
357
- * Augment existing output with additional content
358
- */
359
- augmentOutput(outputs, outputKey, contractName, content) {
360
- const existing = outputs.get(outputKey);
361
- if (!existing) {
362
- this.logger.warn(`Cannot augment non-existent output: ${outputKey}`);
363
- return;
364
- }
365
- const augmentedContent = `${existing.content}
366
-
367
- // Augmented by plugin for ${contractName}
368
- ${JSON.stringify(content, null, 2)}`;
369
- outputs.set(outputKey, {
370
- ...existing,
371
- content: augmentedContent
372
- });
373
- }
374
- /**
375
- * Record hook execution result
376
- */
377
- recordHookResult(pluginName, hookName, result) {
378
- const key = `${pluginName}:${hookName}`;
379
- const existing = this.executionContext.results.get(key) || [];
380
- existing.push({ ...result, plugin: pluginName });
381
- this.executionContext.results.set(key, existing);
382
- }
383
- /**
384
- * Create logger instance
385
- */
386
- createLogger() {
387
- return {
388
- info: (message) => console.log(`\u2139\uFE0F ${message}`),
389
- warn: (message) => console.warn(`\u26A0\uFE0F ${message}`),
390
- error: (message) => console.error(`\u274C ${message}`),
391
- debug: (message) => {
392
- if (process.env.DEBUG) {
393
- console.log(`\u{1F41B} ${message}`);
394
- }
395
- },
396
- success: (message) => console.log(`\u2705 ${message}`)
397
- };
398
- }
399
- /**
400
- * Create utils instance
401
- */
402
- createUtils() {
403
- return {
404
- toCamelCase: (str) => {
405
- return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
406
- },
407
- toKebabCase: (str) => {
408
- return str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
409
- },
410
- validateAddress: (address) => {
411
- return (0, import_transactions.validateStacksAddress)(address.split(".")[0]);
412
- },
413
- parseContractId: (contractId) => {
414
- const [address, contractName] = contractId.split(".");
415
- return { address, contractName };
416
- },
417
- formatCode: async (code) => {
418
- return (0, import_prettier.format)(code, {
419
- parser: "typescript",
420
- singleQuote: true,
421
- semi: true,
422
- printWidth: 100,
423
- trailingComma: "es5"
424
- });
425
- },
426
- resolvePath: (relativePath) => {
427
- return import_path.default.resolve(process.cwd(), relativePath);
428
- },
429
- fileExists: async (filePath) => {
430
- try {
431
- await import_fs.promises.access(filePath);
432
- return true;
433
- } catch {
434
- return false;
435
- }
436
- },
437
- readFile: async (filePath) => {
438
- return import_fs.promises.readFile(filePath, "utf-8");
439
- },
440
- writeFile: async (filePath, content) => {
441
- await import_fs.promises.writeFile(filePath, content, "utf-8");
442
- },
443
- ensureDir: async (dirPath) => {
444
- await import_fs.promises.mkdir(dirPath, { recursive: true });
445
- }
446
- };
447
- }
448
- };
449
-
450
- // src/utils/config.ts
451
- var CONFIG_FILE_NAMES = [
452
- "stacks.config.ts",
453
- "stacks.config.js",
454
- "stacks.config.mjs"
455
- ];
456
- async function findConfigFile(cwd) {
457
- for (const fileName of CONFIG_FILE_NAMES) {
458
- const filePath = import_path2.default.join(cwd, fileName);
459
- try {
460
- await import_fs2.promises.access(filePath);
461
- return filePath;
462
- } catch {
463
- }
464
- }
465
- return null;
466
- }
467
- async function loadConfig(configPath) {
468
- const cwd = process.cwd();
469
- const resolvedPath = configPath ? import_path2.default.resolve(cwd, configPath) : await findConfigFile(cwd);
470
- if (!resolvedPath) {
471
- throw new Error(
472
- "No config file found. Create a stacks.config.ts file or specify a path with --config"
473
- );
474
- }
475
- let config;
476
- if (resolvedPath.endsWith(".ts")) {
477
- const code = await import_fs2.promises.readFile(resolvedPath, "utf-8");
478
- let replacementPath;
479
- try {
480
- const require2 = (0, import_module.createRequire)(importMetaUrl);
481
- const packagePath = require2.resolve("@secondlayer/cli");
482
- replacementPath = (0, import_url.pathToFileURL)(packagePath).href;
483
- } catch {
484
- const currentModuleDir = import_path2.default.dirname(new URL(importMetaUrl).pathname);
485
- const indexPath = import_path2.default.resolve(currentModuleDir, "../index.js");
486
- replacementPath = (0, import_url.pathToFileURL)(indexPath).href;
487
- }
488
- const transformedCode = code.replace(
489
- /from\s+["']@stacks\/cli["']/g,
490
- `from '${replacementPath}'`
491
- );
492
- const result = (0, import_esbuild.transformSync)(transformedCode, {
493
- format: "esm",
494
- target: "node18",
495
- loader: "ts"
496
- });
497
- const tempPath = resolvedPath.replace(/\.ts$/, ".mjs");
498
- await import_fs2.promises.writeFile(tempPath, result.code);
499
- try {
500
- const fileUrl = (0, import_url.pathToFileURL)(tempPath).href;
501
- const module2 = await import(fileUrl);
502
- config = module2.default;
503
- } finally {
504
- await import_fs2.promises.unlink(tempPath).catch(() => {
505
- });
506
- }
507
- } else {
508
- const fileUrl = (0, import_url.pathToFileURL)(resolvedPath).href;
509
- const module2 = await import(fileUrl);
510
- config = module2.default;
511
- }
512
- if (!config) {
513
- throw new Error("Config file must export a default configuration");
514
- }
515
- if (typeof config === "function") {
516
- config = config({});
517
- }
518
- validateConfig(config);
519
- const pluginManager = new PluginManager();
520
- if (config.plugins && Array.isArray(config.plugins)) {
521
- for (const plugin of config.plugins) {
522
- pluginManager.register(plugin);
523
- }
524
- }
525
- const resolvedConfig = await pluginManager.transformConfig(config);
526
- return resolvedConfig;
527
- }
528
- function validateConfig(config) {
529
- if (!config || typeof config !== "object") {
530
- throw new Error("Config must be an object");
531
- }
532
- const c = config;
533
- if (c.contracts && !Array.isArray(c.contracts)) {
534
- throw new Error("Config contracts must be an array");
535
- }
536
- if (!c.out || typeof c.out !== "string") {
537
- throw new Error("Config out must be a string path");
538
- }
539
- if (c.contracts) {
540
- for (const contract of c.contracts) {
541
- if (!contract.address && !contract.source) {
542
- throw new Error("Each contract must have either an address or source");
543
- }
544
- }
545
- }
546
- if (c.plugins && !Array.isArray(c.plugins)) {
547
- throw new Error("Config plugins must be an array");
548
- }
549
- }
550
-
551
- // src/utils/api.ts
552
- init_cjs_shims();
553
- var import_got = __toESM(require("got"), 1);
554
-
555
- // src/parsers/clarity.ts
556
- init_cjs_shims();
557
-
558
- // src/generators/contract.ts
559
- init_cjs_shims();
560
- var import_prettier2 = require("prettier");
561
- async function generateContractInterface(contracts) {
562
- const imports = `import { Cl, validateStacksAddress } from '@stacks/transactions'`;
563
- const header = `/**
564
- * Generated by @secondlayer/cli
565
- * DO NOT EDIT MANUALLY
566
- */`;
567
- const contractsCode = contracts.map((contract) => generateContract(contract)).join("\n\n");
568
- const code = `${imports}
569
-
570
- ${header}
571
-
572
- ${contractsCode}`;
573
- const formatted = await (0, import_prettier2.format)(code, {
574
- parser: "typescript",
575
- singleQuote: true,
576
- semi: true,
577
- printWidth: 100,
578
- trailingComma: "es5"
579
- });
580
- return formatted;
581
- }
582
- function generateContract(contract) {
583
- const { name, address, contractName, abi } = contract;
584
- const abiCode = generateAbiConstant(name, abi);
585
- const methods = abi.functions.filter((func) => func.access !== "private").map((func) => generateMethod(func, address, contractName)).join(",\n\n ");
586
- const contractCode = `export const ${name} = {
587
- address: '${address}',
588
- contractAddress: '${address}',
589
- contractName: '${contractName}',
590
-
591
- ${methods}
592
- } as const`;
593
- return `${abiCode}
594
-
595
- ${contractCode}`;
596
- }
597
- function generateAbiConstant(name, abi) {
598
- const abiJson = JSON.stringify(abi, null, 2).replace(/"([a-zA-Z_$][a-zA-Z0-9_$]*)":/g, "$1:").replace(/"/g, "'");
599
- return `export const ${name}Abi = ${abiJson} as const`;
600
- }
601
- function generateMethod(func, address, contractName) {
602
- const methodName = toCamelCase(func.name);
603
- if (func.args.length === 0) {
604
- return `${methodName}() {
605
- return {
606
- contractAddress: '${address}',
607
- contractName: '${contractName}',
608
- functionName: '${func.name}',
609
- functionArgs: []
610
- }
611
- }`;
612
- }
613
- if (func.args.length === 1) {
614
- const originalArgName = func.args[0].name;
615
- const argName = toCamelCase(originalArgName);
616
- const argType = getTypeForArg(func.args[0]);
617
- const clarityConversion = generateClarityConversion(argName, func.args[0]);
618
- return `${methodName}(...args: [{ ${argName}: ${argType} }] | [${argType}]) {
619
- const ${argName} = args.length === 1 && typeof args[0] === 'object' && args[0] !== null && '${argName}' in args[0]
620
- ? args[0].${argName}
621
- : args[0] as ${argType}
622
-
623
- return {
624
- contractAddress: '${address}',
625
- contractName: '${contractName}',
626
- functionName: '${func.name}',
627
- functionArgs: [${clarityConversion}]
628
- }
629
- }`;
630
- }
631
- const argsList = func.args.map((arg) => toCamelCase(arg.name)).join(", ");
632
- const argsTypes = func.args.map((arg) => {
633
- const camelName = toCamelCase(arg.name);
634
- return `${camelName}: ${getTypeForArg(arg)}`;
635
- }).join("; ");
636
- const argsArray = func.args.map((arg) => {
637
- const argName = toCamelCase(arg.name);
638
- return generateClarityConversion(argName, arg);
639
- }).join(", ");
640
- const objectAccess = func.args.map((arg) => {
641
- const camelName = toCamelCase(arg.name);
642
- return `args[0].${camelName}`;
643
- }).join(", ");
644
- const positionTypes = func.args.map((arg) => getTypeForArg(arg)).join(", ");
645
- return `${methodName}(...args: [{ ${argsTypes} }] | [${positionTypes}]) {
646
- const [${argsList}] = args.length === 1 && typeof args[0] === 'object' && args[0] !== null
647
- ? [${objectAccess}]
648
- : args as [${positionTypes}]
649
-
650
- return {
651
- contractAddress: '${address}',
652
- contractName: '${contractName}',
653
- functionName: '${func.name}',
654
- functionArgs: [${argsArray}]
655
- }
656
- }`;
657
- }
658
- function getTypeForArg(arg) {
659
- const type = arg.type;
660
- if (typeof type === "string") {
661
- switch (type) {
662
- case "uint128":
663
- case "int128":
664
- return "bigint";
665
- case "bool":
666
- return "boolean";
667
- case "principal":
668
- case "trait_reference":
669
- return "string";
670
- default:
671
- return "any";
672
- }
673
- }
674
- if (type["string-ascii"] || type["string-utf8"]) {
675
- return "string";
676
- }
677
- if (type.buff) {
678
- return "Uint8Array | string | { type: 'ascii' | 'utf8' | 'hex'; value: string }";
679
- }
680
- if (type.optional) {
681
- const innerType = getTypeForArg({ type: type.optional });
682
- return `${innerType} | null`;
683
- }
684
- if (type.list) {
685
- const innerType = getTypeForArg({ type: type.list.type });
686
- return `${innerType}[]`;
687
- }
688
- if (type.tuple) {
689
- const fields = type.tuple.map(
690
- (field) => `${toCamelCase(field.name)}: ${getTypeForArg({ type: field.type })}`
691
- ).join("; ");
692
- return `{ ${fields} }`;
693
- }
694
- if (type.response) {
695
- const okType = getTypeForArg({ type: type.response.ok });
696
- const errType = getTypeForArg({ type: type.response.error });
697
- return `{ ok: ${okType} } | { err: ${errType} }`;
698
- }
699
- return "any";
700
- }
701
- function toCamelCase(str) {
702
- return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()).replace(/-([A-Z])/g, (_, letter) => letter).replace(/-(\d)/g, (_, digit) => digit).replace(/-/g, "");
703
- }
704
- function generateClarityConversion(argName, argType) {
705
- const type = argType.type;
706
- if (typeof type === "string") {
707
- switch (type) {
708
- case "uint128":
709
- return `Cl.uint(${argName})`;
710
- case "int128":
711
- return `Cl.int(${argName})`;
712
- case "bool":
713
- return `Cl.bool(${argName})`;
714
- case "principal":
715
- case "trait_reference":
716
- return `(() => {
717
- const [address, contractName] = ${argName}.split(".") as [string, string];
718
- if (!validateStacksAddress(address)) {
719
- throw new Error("Invalid Stacks address format");
720
- }
721
- if (${argName}.includes(".")) {
722
- return Cl.contractPrincipal(address, contractName);
723
- } else {
724
- return Cl.standardPrincipal(${argName});
725
- }
726
- })()`;
727
- default:
728
- return `${argName}`;
729
- }
730
- }
731
- if (type["string-ascii"]) {
732
- return `Cl.stringAscii(${argName})`;
733
- }
734
- if (type["string-utf8"]) {
735
- return `Cl.stringUtf8(${argName})`;
736
- }
737
- if (type.buff) {
738
- return `(() => {
739
- const value = ${argName};
740
- // Direct Uint8Array
741
- if (value instanceof Uint8Array) {
742
- return Cl.buffer(value);
743
- }
744
- // Object notation with explicit type
745
- if (typeof value === 'object' && value !== null && value.type && value.value) {
746
- switch (value.type) {
747
- case 'ascii':
748
- return Cl.bufferFromAscii(value.value);
749
- case 'utf8':
750
- return Cl.bufferFromUtf8(value.value);
751
- case 'hex':
752
- return Cl.bufferFromHex(value.value);
753
- default:
754
- throw new Error(\`Unsupported buffer type: \${value.type}\`);
755
- }
756
- }
757
- // Auto-detect string type
758
- if (typeof value === 'string') {
759
- // Check for hex (0x prefix or pure hex pattern)
760
- if (value.startsWith('0x') || /^[0-9a-fA-F]+$/.test(value)) {
761
- return Cl.bufferFromHex(value);
762
- }
763
- // Check for non-ASCII characters (UTF-8)
764
- if (!/^[\\x00-\\x7F]*$/.test(value)) {
765
- return Cl.bufferFromUtf8(value);
766
- }
767
- // Default to ASCII for simple ASCII strings
768
- return Cl.bufferFromAscii(value);
769
- }
770
- throw new Error(\`Invalid buffer value: \${value}\`);
771
- })()`;
772
- }
773
- if (type.optional) {
774
- const innerConversion = generateClarityConversion(argName, {
775
- type: type.optional
776
- });
777
- return `${argName} !== null ? Cl.some(${innerConversion.replace(argName, `${argName}`)}) : Cl.none()`;
778
- }
779
- if (type.list) {
780
- const innerConversion = generateClarityConversion("item", {
781
- type: type.list.type
782
- });
783
- return `Cl.list(${argName}.map(item => ${innerConversion}))`;
784
- }
785
- if (type.tuple) {
786
- const fields = type.tuple.map((field) => {
787
- const camelFieldName = toCamelCase(field.name);
788
- const fieldConversion = generateClarityConversion(
789
- `${argName}.${camelFieldName}`,
790
- { type: field.type }
791
- );
792
- return `"${field.name}": ${fieldConversion}`;
793
- }).join(", ");
794
- return `Cl.tuple({ ${fields} })`;
795
- }
796
- if (type.response) {
797
- const okConversion = generateClarityConversion(`${argName}.ok`, {
798
- type: type.response.ok
799
- });
800
- const errConversion = generateClarityConversion(`${argName}.err`, {
801
- type: type.response.error
802
- });
803
- return `'ok' in ${argName} ? Cl.ok(${okConversion.replace(`${argName}.ok`, `${argName}.ok`)}) : Cl.error(${errConversion.replace(`${argName}.err`, `${argName}.err`)})`;
804
- }
805
- return `${argName}`;
806
- }
807
-
808
- // src/commands/generate.ts
809
- async function generate(options) {
810
- const spinner = (0, import_ora.default)("Processing contracts").start();
811
- try {
812
- const config = await loadConfig(options.config);
813
- const pluginManager = new PluginManager();
814
- if (config.plugins) {
815
- for (const plugin of config.plugins) {
816
- pluginManager.register(plugin);
817
- }
818
- }
819
- await pluginManager.executeHook("configResolved", config);
820
- const contractConfigs = (config.contracts || []).map(
821
- (contract) => ({
822
- name: contract.name,
823
- address: contract.address,
824
- source: contract.source,
825
- abi: contract.abi,
826
- // Include ABI if it exists (from plugins)
827
- _clarinetSource: contract._clarinetSource
828
- // Include plugin flags
829
- })
830
- );
831
- const processedContracts = await pluginManager.transformContracts(
832
- contractConfigs,
833
- config
834
- );
835
- if (processedContracts.length === 0) {
836
- spinner.warn("No contracts found to generate");
837
- console.log("\nTo get started:");
838
- console.log(" \u2022 Add contracts to your config file, or");
839
- console.log(" \u2022 Use plugins like clarinet() for local contracts");
840
- return;
841
- }
842
- const outputs = await pluginManager.executeGeneration(
843
- processedContracts,
844
- config
845
- );
846
- if (!outputs.has("contracts") && processedContracts.length > 0) {
847
- const contractsCode = await generateContractInterface(processedContracts);
848
- outputs.set("contracts", {
849
- path: config.out,
850
- content: contractsCode,
851
- type: "contracts"
852
- });
853
- }
854
- const transformedOutputs = await pluginManager.transformOutputs(outputs);
855
- await pluginManager.writeOutputs(transformedOutputs);
856
- const contractCount = processedContracts.length;
857
- const contractWord = contractCount === 1 ? "contract" : "contracts";
858
- spinner.succeed(`Generation complete for ${contractCount} ${contractWord}`);
859
- console.log(`
860
- \u{1F4C4} ${config.out}`);
861
- console.log(`
862
- \u{1F4A1} Import your contracts:`);
863
- if (processedContracts.length > 0) {
864
- const exampleContract = processedContracts[0];
865
- console.log(
866
- import_chalk.default.gray(
867
- ` import { ${exampleContract.name} } from '${config.out.replace(/\.ts$/, "")}'`
868
- )
869
- );
870
- if (processedContracts.length > 1) {
871
- console.log(
872
- import_chalk.default.gray(
873
- ` // Also available: ${processedContracts.slice(1).map((c) => c.name).join(", ")}`
874
- )
875
- );
876
- }
877
- }
878
- } catch (error) {
879
- spinner.fail("Generation failed");
880
- console.error(import_chalk.default.red(`
881
- ${error.message}`));
882
- if (process.env.DEBUG) {
883
- console.error(error.stack);
884
- }
885
- process.exit(1);
886
- }
887
- }
888
-
889
- // src/cli.ts
890
- import_commander.program.name("stacks").description("CLI tool for generating type-safe Stacks contract interfaces").version("0.1.0");
891
- import_commander.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);
892
- import_commander.program.command("init").description("Initialize a new stacks.config.ts file").action(async () => {
893
- const { init: init2 } = await Promise.resolve().then(() => (init_init(), init_exports));
894
- await init2();
895
- });
896
- import_commander.program.parse();
897
- //# sourceMappingURL=cli.cjs.map