@better-sol/cli 0.1.0-alpha.11 → 0.1.0-alpha.13

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/index.js CHANGED
@@ -3,11 +3,11 @@ import { createRequire } from "node:module";
3
3
  import { cancel, confirm, intro, isCancel, log, outro, select, spinner, text } from "@clack/prompts";
4
4
  import { Command } from "commander";
5
5
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
6
- import path, { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
6
+ import path, { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
7
7
  import { homedir, tmpdir } from "node:os";
8
8
  import { execFile, execSync } from "node:child_process";
9
9
  import { generateKeyPairSigner, getAddressDecoder } from "@solana/kit";
10
- import { access, mkdir, mkdtemp, opendir, readFile, rm, writeFile } from "node:fs/promises";
10
+ import { access, mkdir, mkdtemp, opendir, readFile, readdir, rm, writeFile } from "node:fs/promises";
11
11
  import { parseSync } from "oxc-parser";
12
12
  import { promisify } from "node:util";
13
13
  import { fileURLToPath, pathToFileURL } from "node:url";
@@ -388,13 +388,11 @@ function collectAccounts(source, program, rawStructZCs) {
388
388
  const firstArg = call.arguments[0];
389
389
  if (firstArg === void 0 || !isObjectExpression(firstArg)) continue;
390
390
  const fields = parseFields(source, firstArg);
391
- const chainText = nodeTextOf(source, decl.init);
392
- if (chainText.includes(".pda(")) throw new Error(".pda() was renamed to .derive(). Use .derive((seed) => ['literal', seed.fieldName]).");
393
- if (chainText.includes(".seeds(")) throw new Error(".seeds() was removed. Use .derive((seed) => ['literal', seed.fieldName]).");
394
- const zeroCopy = chainText.includes(".zeroCopy");
391
+ const zeroCopy = hasChainMethod(source, decl.init, "zeroCopy");
395
392
  if (zeroCopy) validateZeroCopyFields(name, fields, rawStructZCs);
396
- const seeds = parseSeeds(chainText);
397
- const hasOneFields = parseHasOneFields(chainText);
393
+ const deriveCall = findChainCall(source, decl.init, "derive");
394
+ const seeds = deriveCall !== void 0 ? parseSeedsFromAst(source, deriveCall) : [];
395
+ const hasOneFields = parseHasOneFieldsFromAst(source, decl.init);
398
396
  accounts.push({
399
397
  name,
400
398
  fields,
@@ -578,6 +576,17 @@ function resolveConstraint(source, prop, accountName, rawAccounts) {
578
576
  };
579
577
  }
580
578
  case "remaining": {
579
+ const argNode = init.arguments[0];
580
+ if (argNode !== void 0 && isIdentifier(argNode)) {
581
+ if (argNode.name === "tokenAccount" || argNode.name.includes("TokenAccount") || argNode.name.includes("tokenAccount")) return {
582
+ kind: "remaining",
583
+ itemType: "tokenAccount"
584
+ };
585
+ if (argNode.name === "signer" || argNode.name.includes("Signer")) return {
586
+ kind: "remaining",
587
+ itemType: "signer"
588
+ };
589
+ }
581
590
  const argText = getCallArgText(source, init, 0) ?? "";
582
591
  if (argText.includes("tokenAccount")) return {
583
592
  kind: "remaining",
@@ -704,50 +713,73 @@ function tryResolvePrimitive(name) {
704
713
  "bytes"
705
714
  ].includes(name) ? name : void 0;
706
715
  }
707
- function parseSeeds(chainText) {
708
- const args = extractPdaArgs(chainText);
709
- if (args === void 0) return [];
716
+ function hasChainMethod(source, node, methodName) {
717
+ return findChainCall(source, node, methodName) !== void 0;
718
+ }
719
+ function findChainCall(source, node, methodName) {
720
+ let current = node;
721
+ while (current !== void 0) {
722
+ if (!isCallExpression(current)) break;
723
+ if (isMemberExpression(current.callee)) {
724
+ if (getMemberPropertyName(source, current.callee) === methodName) return current;
725
+ current = current.callee.object;
726
+ } else break;
727
+ }
728
+ }
729
+ function parseSeedsFromAst(source, deriveCall) {
730
+ const firstArg = deriveCall.arguments[0];
731
+ if (firstArg === void 0 || !isArrowFunctionExpression(firstArg)) return [];
732
+ const body = unwrapParenthesized(firstArg.body);
733
+ if (!isArrayExpression(body)) return [];
710
734
  const seeds = [];
711
- const regex = /\b[A-Za-z_$][\w$]*\.([A-Za-z_$][\w$]*)|'([^']*)'|"([^"]*)"/g;
712
- let match;
713
- while ((match = regex.exec(args)) !== null) {
714
- const field = match[1];
715
- const singleQuotedLiteral = match[2];
716
- const doubleQuotedLiteral = match[3];
717
- if (field !== void 0) seeds.push({
718
- kind: "field",
719
- fieldName: field
720
- });
721
- else if (singleQuotedLiteral !== void 0 && singleQuotedLiteral !== "") seeds.push(parseLiteralSeed(singleQuotedLiteral));
722
- else if (doubleQuotedLiteral !== void 0 && doubleQuotedLiteral !== "") seeds.push(parseLiteralSeed(doubleQuotedLiteral));
735
+ for (const element of body.elements) {
736
+ if (element === void 0 || element === null || isSpreadElement(element)) continue;
737
+ const seed = parseSingleSeed(source, element);
738
+ if (seed !== void 0) seeds.push(seed);
723
739
  }
724
740
  return seeds;
725
741
  }
742
+ function parseSingleSeed(source, node) {
743
+ if (isStringLiteral(node)) return parseLiteralSeed(node.value);
744
+ if (isTemplateLiteral(node) && node.quasis.length === 1) {
745
+ const quasi = node.quasis[0];
746
+ if (quasi !== void 0) return parseLiteralSeed(quasi.value.cooked ?? quasi.value.raw);
747
+ }
748
+ if (isMemberExpression(node)) {
749
+ const property = getMemberPropertyName(source, node);
750
+ if (property !== void 0) return {
751
+ kind: "field",
752
+ fieldName: property
753
+ };
754
+ }
755
+ if (isCallExpression(node) && isMemberExpression(node.callee)) {
756
+ const property = getMemberPropertyName(source, node.callee);
757
+ if (property !== void 0) return {
758
+ kind: "field",
759
+ fieldName: property
760
+ };
761
+ }
762
+ }
726
763
  function parseLiteralSeed(value) {
727
- if (/^\{[A-Za-z_$][\w$]*\}$/.test(value)) throw new Error(`Dynamic PDA seed template '${value}' is not supported. Store the value as an account field and reference it with seed.${value.slice(1, -1)}.`);
728
764
  return {
729
765
  kind: "literal",
730
766
  value
731
767
  };
732
768
  }
733
- function parseHasOneFields(chainText) {
769
+ function parseHasOneFieldsFromAst(source, node) {
734
770
  const fields = [];
735
- const regex = /\.hasOne\(["']([^"']+)["']\)/g;
736
- let match;
737
- while ((match = regex.exec(chainText)) !== null) fields.push(match[1]);
738
- return fields;
739
- }
740
- function extractPdaArgs(chainText) {
741
- const start = chainText.indexOf(".derive(");
742
- if (start === -1) return void 0;
743
- const argsStart = start + 8;
744
- let depth = 1;
745
- for (let index = argsStart; index < chainText.length; index += 1) {
746
- const char = chainText[index];
747
- if (char === "(") depth += 1;
748
- else if (char === ")") depth -= 1;
749
- if (depth === 0) return chainText.slice(argsStart, index);
771
+ let current = node;
772
+ while (current !== void 0) {
773
+ if (!isCallExpression(current)) break;
774
+ if (isMemberExpression(current.callee)) {
775
+ if (getMemberPropertyName(source, current.callee) === "hasOne") {
776
+ const arg = current.arguments[0];
777
+ if (isStringLiteral(arg)) fields.push(arg.value);
778
+ }
779
+ current = current.callee.object;
780
+ } else break;
750
781
  }
782
+ return fields;
751
783
  }
752
784
  function validateZeroCopyFields(accountName, fields, structs) {
753
785
  for (const field of fields) try {
@@ -3500,12 +3532,21 @@ function flattenAccountItems(items) {
3500
3532
  return result;
3501
3533
  }
3502
3534
  function idlTypeToCode(type) {
3503
- if (typeof type === "string") return `bs.${type}()`;
3504
- if ("option" in type && type.option !== void 0) return `bs.optional(${idlTypeToCode(type.option)})`;
3505
- if ("coption" in type && type.coption !== void 0) return `bs.optional(${idlTypeToCode(type.coption)})`;
3506
- if ("vec" in type && type.vec !== void 0) return `bs.vector(${idlTypeToCode(type.vec)})`;
3507
- if ("array" in type && type.array !== void 0) return `bs.array(${idlTypeToCode(type.array[0])}, ${type.array[1]})`;
3508
- if ("defined" in type) return "bs.pubkey()";
3535
+ if (typeof type === "string") {
3536
+ if (type === "publicKey") return "bs.pubkey()";
3537
+ return `bs.${type}()`;
3538
+ }
3539
+ if (typeof type !== "object" || type === null) return "bs.pubkey()";
3540
+ const record = type;
3541
+ const option = record.option;
3542
+ if (option !== void 0) return `bs.optional(${idlTypeToCode(option)})`;
3543
+ const coption = record.coption;
3544
+ if (coption !== void 0) return `bs.optional(${idlTypeToCode(coption)})`;
3545
+ const vec = record.vec;
3546
+ if (vec !== void 0) return `bs.vector(${idlTypeToCode(vec)})`;
3547
+ const array = record.array;
3548
+ if (Array.isArray(array) && array.length === 2) return `bs.array(${idlTypeToCode(array[0])}, ${array[1]})`;
3549
+ if (record.defined !== void 0) return "bs.pubkey()";
3509
3550
  return "bs.pubkey()";
3510
3551
  }
3511
3552
  function escapeString(str) {
@@ -3521,18 +3562,35 @@ function getStructFields(typeDef) {
3521
3562
  if (!Array.isArray(fields)) return [];
3522
3563
  return fields;
3523
3564
  }
3565
+ function getStructFieldsFromAccount(account) {
3566
+ if (typeof account !== "object" || account === null) return [];
3567
+ const accountType = account.type;
3568
+ if (typeof accountType !== "object" || accountType === null) return [];
3569
+ const typeRecord = accountType;
3570
+ if (typeRecord.kind !== "struct") return [];
3571
+ const fields = typeRecord.fields;
3572
+ if (!Array.isArray(fields)) return [];
3573
+ return fields;
3574
+ }
3575
+ function getIdlName$1(idl) {
3576
+ if (typeof idl.metadata?.name === "string") return idl.metadata.name;
3577
+ const record = idl;
3578
+ return typeof record.name === "string" ? record.name : void 0;
3579
+ }
3524
3580
  function generateIdlProgram(idl, sourceLabel) {
3525
3581
  const w = new CodeWriter();
3526
- const programName = idl.metadata?.name ?? "unknown";
3582
+ const programName = toCamel(getIdlName$1(idl) ?? "unknown");
3527
3583
  const programAddress = idl.address ?? "";
3528
3584
  const typesByName = /* @__PURE__ */ new Map();
3529
3585
  for (const t of idl.types ?? []) typesByName.set(t.name, t);
3530
3586
  const accountDefs = idl.accounts ?? [];
3531
3587
  const resolvedAccounts = /* @__PURE__ */ new Map();
3532
3588
  for (const acc of accountDefs) {
3533
- const fields = getStructFields(typesByName.get(acc.name));
3589
+ const fromTypes = getStructFields(typesByName.get(acc.name));
3590
+ const fromAccount = getStructFieldsFromAccount(acc);
3591
+ const fields = fromTypes.length > 0 ? fromTypes : fromAccount;
3534
3592
  resolvedAccounts.set(acc.name, {
3535
- name: acc.name,
3593
+ exportName: toPascal(acc.name),
3536
3594
  fields
3537
3595
  });
3538
3596
  }
@@ -3541,11 +3599,11 @@ function generateIdlProgram(idl, sourceLabel) {
3541
3599
  w.line(`// Generated by better-sol from ${sourceLabel}. Do not edit.`);
3542
3600
  w.line(`import { bs } from "better-sol/program"`);
3543
3601
  w.blank();
3544
- for (const [name, { fields }] of resolvedAccounts) {
3602
+ for (const [, { exportName, fields }] of resolvedAccounts) {
3545
3603
  if (fields.length === 0) continue;
3546
- w.line(`const ${name} = bs.account({`);
3604
+ w.line(`const ${exportName} = bs.account({`);
3547
3605
  w.indent(1);
3548
- for (const field of fields) w.line(`${field.name}: ${idlTypeToCode(field.type)},`);
3606
+ for (const field of fields) w.line(`${toCamel(field.name)}: ${idlTypeToCode(field.type)},`);
3549
3607
  w.indent(0);
3550
3608
  w.line("})");
3551
3609
  w.blank();
@@ -3558,10 +3616,9 @@ function generateIdlProgram(idl, sourceLabel) {
3558
3616
  w.line(`address: "${escapeString(programAddress)}",`);
3559
3617
  if (resolvedAccounts.size > 0) {
3560
3618
  w.line(`accounts: {`);
3561
- for (const name of resolvedAccounts.keys()) {
3562
- const resolved = resolvedAccounts.get(name);
3563
- if (resolved === void 0 || resolved.fields.length === 0) continue;
3564
- w.line(` ${name},`);
3619
+ for (const [, { exportName, fields }] of resolvedAccounts) {
3620
+ if (fields.length === 0) continue;
3621
+ w.line(` ${exportName},`);
3565
3622
  }
3566
3623
  w.line(`},`);
3567
3624
  }
@@ -3569,7 +3626,7 @@ function generateIdlProgram(idl, sourceLabel) {
3569
3626
  w.line(`errors: {`);
3570
3627
  for (const err of errorEntries) {
3571
3628
  const msg = err.msg ?? err.name;
3572
- w.line(` ${err.name}: "${escapeString(msg)}",`);
3629
+ w.line(` ${toPascal(err.name)}: "${escapeString(msg)}",`);
3573
3630
  }
3574
3631
  w.line(`},`);
3575
3632
  }
@@ -3577,8 +3634,8 @@ function generateIdlProgram(idl, sourceLabel) {
3577
3634
  w.line(`events: {`);
3578
3635
  for (const ev of eventEntries) {
3579
3636
  const fields = getStructFields(typesByName.get(ev.name));
3580
- w.line(` ${ev.name}: {`);
3581
- for (const f of fields) w.line(` ${f.name}: ${idlTypeToCode(f.type)},`);
3637
+ w.line(` ${toPascal(ev.name)}: {`);
3638
+ for (const f of fields) w.line(` ${toCamel(f.name)}: ${idlTypeToCode(f.type)},`);
3582
3639
  w.line(` },`);
3583
3640
  }
3584
3641
  w.line(`},`);
@@ -3587,21 +3644,22 @@ function generateIdlProgram(idl, sourceLabel) {
3587
3644
  w.line(`}, ix => ({`);
3588
3645
  for (const instr of idl.instructions) {
3589
3646
  const flatAccounts = flattenAccountItems(instr.accounts);
3647
+ const ixName = toCamel(instr.name);
3590
3648
  w.blank();
3591
- w.line(`${instr.name}: ix({`);
3649
+ w.line(`${ixName}: ix({`);
3592
3650
  if (flatAccounts.length > 0) {
3593
3651
  w.line(` accounts: {`);
3594
3652
  for (const acc of flatAccounts) {
3595
3653
  if (acc.optional === true) continue;
3596
3654
  const constraint = accountToConstraint(acc, resolvedAccounts);
3597
- w.line(` ${acc.name}: ${constraint},`);
3655
+ w.line(` ${toCamel(acc.name)}: ${constraint},`);
3598
3656
  }
3599
3657
  w.line(` },`);
3600
3658
  }
3601
3659
  const args = instr.args ?? [];
3602
3660
  if (args.length > 0) {
3603
3661
  w.line(` args: {`);
3604
- for (const arg of args) w.line(` ${arg.name}: ${idlTypeToCode(arg.type)},`);
3662
+ for (const arg of args) w.line(` ${toCamel(arg.name)}: ${idlTypeToCode(arg.type)},`);
3605
3663
  w.line(` },`);
3606
3664
  }
3607
3665
  if (instr.returns !== void 0) w.line(` returns: ${idlTypeToCode(instr.returns)},`);
@@ -3617,15 +3675,22 @@ function generateIdlProgram(idl, sourceLabel) {
3617
3675
  }
3618
3676
  function accountToConstraint(acc, resolvedAccounts) {
3619
3677
  if (acc.signer === true) return "bs.signer()";
3620
- const accountRef = resolvedAccounts.has(acc.name) ? acc.name : "bs.account({})";
3678
+ const resolved = resolveAccountDefinition(acc.name, resolvedAccounts);
3679
+ const accountRef = resolved !== void 0 && resolved.fields.length > 0 ? resolved.exportName : "bs.account({})";
3621
3680
  if (acc.writable === true) return `bs.mut(${accountRef})`;
3622
3681
  return accountRef;
3623
3682
  }
3683
+ function resolveAccountDefinition(accountName, resolvedAccounts) {
3684
+ const exact = resolvedAccounts.get(accountName);
3685
+ if (exact !== void 0) return exact;
3686
+ const pascalName = toPascal(accountName);
3687
+ for (const [idlName, account] of resolvedAccounts) if (idlName === pascalName || account.exportName === pascalName) return account;
3688
+ }
3624
3689
  function runParams(accounts, args) {
3625
3690
  const parts = [];
3626
- const accountNames = accounts.filter((a) => a.optional !== true).map((a) => a.name);
3691
+ const accountNames = accounts.filter((a) => a.optional !== true).map((a) => toCamel(a.name));
3627
3692
  if (accountNames.length > 0) parts.push(`{ ${accountNames.join(", ")} }`);
3628
- const argNames = args.map((a) => a.name);
3693
+ const argNames = args.map((a) => toCamel(a.name));
3629
3694
  if (argNames.length > 0) parts.push(`{ ${argNames.join(", ")} }`);
3630
3695
  parts.push("ctx");
3631
3696
  return parts.join(", ");
@@ -69213,7 +69278,7 @@ async function generateIdl(input, options) {
69213
69278
  sourceLabel = basename(idlPath);
69214
69279
  s.stop("IDL loaded");
69215
69280
  }
69216
- const programName = options.name ?? idl.metadata?.name ?? "program";
69281
+ const programName = toCamel(options.name ?? getIdlName(idl) ?? "program");
69217
69282
  const outPath = cwdPath(options.out ?? `generated/${programName}.ts`);
69218
69283
  s.message("Generating program definition");
69219
69284
  const code = generateIdlProgram(idl, sourceLabel);
@@ -69221,12 +69286,101 @@ async function generateIdl(input, options) {
69221
69286
  await writeFile(outPath, code, "utf-8");
69222
69287
  s.stop("Generated");
69223
69288
  log.info(`Wrote ${resolve(outPath)}`);
69224
- const importPath = removeExt(relative(dirname(outPath), outPath));
69225
- log.step(`Import it in your app:\n\n import { ${programName} } from "./${importPath}"\n\nThen register it with the client:\n\n const sol = await betterSol({ programs: { ${programName} } })`);
69289
+ log.step(await buildImportGuide(outPath, programName));
69226
69290
  outro("Done");
69227
69291
  }
69292
+ const IGNORED_DIRECTORIES = new Set([
69293
+ ".better-sol",
69294
+ ".git",
69295
+ ".next",
69296
+ "dist",
69297
+ "build",
69298
+ "node_modules"
69299
+ ]);
69300
+ async function buildImportGuide(outPath, programName) {
69301
+ const usage = await findBetterSolUsage(process.cwd());
69302
+ if (usage !== void 0) return `Import it in ${relativePath(process.cwd(), usage.filePath)}:\n\n import { ${programName} } from "${moduleSpecifier(dirname(usage.filePath), outPath)}"\n\n${usage.hasProgramsObject ? `Add it to the existing programs object:\n\n programs: { ..., ${programName} }` : `Register it with the client:\n\n const sol = await betterSol({ programs: { ${programName} } })`}`;
69303
+ return `Import it in your app:\n\n import { ${programName} } from "${moduleSpecifier(process.cwd(), outPath)}"\n\nThen register it with the client:\n\n const sol = await betterSol({ programs: { ${programName} } })`;
69304
+ }
69305
+ async function findBetterSolUsage(root) {
69306
+ const files = await collectTypeScriptFiles(root);
69307
+ let fallback;
69308
+ for (const filePath of files) {
69309
+ const usage = parseBetterSolUsage(await readFile(filePath, "utf-8"), filePath);
69310
+ if (usage === void 0) continue;
69311
+ if (usage.hasProgramsObject) return usage;
69312
+ fallback ??= usage;
69313
+ }
69314
+ return fallback;
69315
+ }
69316
+ async function collectTypeScriptFiles(root) {
69317
+ const results = [];
69318
+ await collectTypeScriptFilesInto(root, results);
69319
+ return results.sort();
69320
+ }
69321
+ async function collectTypeScriptFilesInto(directory, results) {
69322
+ const entries = await readdir(directory, { withFileTypes: true });
69323
+ for (const entry of entries) {
69324
+ const fullPath = join(directory, entry.name);
69325
+ if (entry.isDirectory()) {
69326
+ if (!IGNORED_DIRECTORIES.has(entry.name)) await collectTypeScriptFilesInto(fullPath, results);
69327
+ continue;
69328
+ }
69329
+ if (!entry.isFile()) continue;
69330
+ const extension = extname(entry.name);
69331
+ if (extension === ".ts" || extension === ".tsx") results.push(fullPath);
69332
+ }
69333
+ }
69334
+ function parseBetterSolUsage(source, filePath) {
69335
+ let program;
69336
+ try {
69337
+ program = parseModule(filePath, source);
69338
+ } catch {
69339
+ return;
69340
+ }
69341
+ let hasCall = false;
69342
+ let hasProgramsObject = false;
69343
+ visitAst(program, (node) => {
69344
+ if (!isBetterSolCall(node)) return;
69345
+ hasCall = true;
69346
+ const firstArg = node.arguments[0];
69347
+ if (!isObjectExpression(firstArg)) return;
69348
+ if (isObjectExpression(getObjectProperty(firstArg, "programs"))) hasProgramsObject = true;
69349
+ });
69350
+ return hasCall ? {
69351
+ filePath,
69352
+ hasProgramsObject
69353
+ } : void 0;
69354
+ }
69355
+ function isBetterSolCall(node) {
69356
+ return isCallExpression(node) && isIdentifier(node.callee) && node.callee.name === "betterSol";
69357
+ }
69358
+ function visitAst(node, visit) {
69359
+ if (!isAstNode(node)) return;
69360
+ visit(node);
69361
+ for (const value of Object.values(node)) if (Array.isArray(value)) for (const item of value) visitAst(item, visit);
69362
+ else visitAst(value, visit);
69363
+ }
69364
+ function isAstNode(value) {
69365
+ if (typeof value !== "object" || value === null) return false;
69366
+ return typeof value.type === "string";
69367
+ }
69368
+ function moduleSpecifier(fromDirectory, toFile) {
69369
+ const normalized = removeExt(relative(fromDirectory, toFile)).split(sep).join("/");
69370
+ if (normalized.startsWith(".")) return normalized;
69371
+ return `./${normalized}`;
69372
+ }
69373
+ function relativePath(fromDirectory, toFile) {
69374
+ const value = relative(fromDirectory, toFile).split(sep).join("/");
69375
+ return value.length > 0 ? value : ".";
69376
+ }
69228
69377
  function removeExt(path) {
69229
- return path.replace(/\.ts$/, "");
69378
+ return path.replace(/\.tsx?$/, "");
69379
+ }
69380
+ function getIdlName(idl) {
69381
+ if (typeof idl.metadata?.name === "string") return idl.metadata.name;
69382
+ const record = idl;
69383
+ return typeof record.name === "string" ? record.name : void 0;
69230
69384
  }
69231
69385
  //#endregion
69232
69386
  //#region src/commands/login.ts
@@ -69328,7 +69482,7 @@ async function gitCommit() {
69328
69482
  //#endregion
69329
69483
  //#region src/index.ts
69330
69484
  const cli = new Command();
69331
- cli.name("better-sol").description("Write Solana programs in TypeScript. Run with npx @better-sol/cli@alpha").version("0.1.0-alpha.11");
69485
+ cli.name("better-sol").description("Write Solana programs in TypeScript. Run with npx @better-sol/cli@alpha").version("0.1.0-alpha.13");
69332
69486
  cli.command("init").description("Initialize a better-sol project").option("--force", "overwrite existing files", false).option("--skip-install", "skip installing dependencies", false).action((options) => run(() => init(options)));
69333
69487
  cli.command("create").description("Create a new better-sol program").argument("[name]", "program name").option("--dir <dir>", "program directory", "programs").option("--force", "overwrite existing files", false).action((name, options) => run(() => create(name, options)));
69334
69488
  cli.command("login").description("Save your compiler API key").argument("[apiKey]", "compiler API key").action((apiKey) => run(() => login(apiKey)));