Browse Source

Initial commit

Loris Leiva 1 year ago
commit
13b58340e2
87 changed files with 7849 additions and 0 deletions
  1. 8 0
      .gitignore
  2. 125 0
      index.ts
  3. 68 0
      locales/en-US.json
  4. 71 0
      locales/fr-FR.json
  5. 13 0
      my-program/.gitignore
  6. 3 0
      my-program/Cargo.toml
  7. 21 0
      my-program/LICENSE
  8. 12 0
      my-program/README.md
  9. 19 0
      my-program/package.json
  10. 30 0
      my-program/scripts/generate-idls.mjs
  11. 14 0
      my-program/scripts/program/build.mjs
  12. 10 0
      my-program/scripts/program/clean.mjs
  13. 69 0
      my-program/scripts/program/dump.mjs
  14. 11 0
      my-program/scripts/program/format.mjs
  15. 11 0
      my-program/scripts/program/lint.mjs
  16. 20 0
      my-program/scripts/program/test.mjs
  17. 66 0
      my-program/scripts/utils.mjs
  18. 48 0
      package.json
  19. 811 0
      pnpm-lock.yaml
  20. 31 0
      scripts/build.mjs
  21. 29 0
      scripts/prepublish.mjs
  22. 30 0
      scripts/snapshot.mjs
  23. 37 0
      scripts/test.mjs
  24. 3 0
      template/base/Cargo.toml
  25. 21 0
      template/base/LICENSE
  26. 13 0
      template/base/_gitignore
  27. 19 0
      template/base/package.json
  28. 30 0
      template/base/scripts/generate-idls.mjs
  29. 14 0
      template/base/scripts/program/build.mjs
  30. 10 0
      template/base/scripts/program/clean.mjs
  31. 69 0
      template/base/scripts/program/dump.mjs
  32. 11 0
      template/base/scripts/program/format.mjs
  33. 11 0
      template/base/scripts/program/lint.mjs
  34. 20 0
      template/base/scripts/program/test.mjs
  35. 66 0
      template/base/scripts/utils.mjs
  36. 11 0
      template/clients/base/package.json
  37. 86 0
      template/clients/base/scripts/generate-clients.mjs
  38. 100 0
      template/clients/base/scripts/start-validator.mjs
  39. 13 0
      template/clients/base/scripts/stop-validator.mjs
  40. 24 0
      template/clients/js/clients/js/.eslintrc.js
  41. 2 0
      template/clients/js/clients/js/.gitignore
  42. 10 0
      template/clients/js/clients/js/.prettierrc.json
  43. 3 0
      template/clients/js/clients/js/README.md
  44. 3 0
      template/clients/js/clients/js/env-shim.ts
  45. 82 0
      template/clients/js/clients/js/package.json
  46. 3711 0
      template/clients/js/clients/js/pnpm-lock.yaml
  47. 1 0
      template/clients/js/clients/js/src/index.ts
  48. 103 0
      template/clients/js/clients/js/test/_setup.ts
  49. 38 0
      template/clients/js/clients/js/test/create.test.ts
  50. 117 0
      template/clients/js/clients/js/test/increment.test.ts
  51. 9 0
      template/clients/js/clients/js/tsconfig.declarations.json
  52. 24 0
      template/clients/js/clients/js/tsconfig.json
  53. 27 0
      template/clients/js/clients/js/tsup.config.ts
  54. 6 0
      template/clients/js/clients/js/typedoc.json
  55. 5 0
      template/clients/js/package.json
  56. 12 0
      template/clients/js/scripts/client/test-js.mjs
  57. 3 0
      template/clients/rust/Cargo.toml
  58. 24 0
      template/clients/rust/clients/rust/Cargo.toml
  59. 3 0
      template/clients/rust/clients/rust/README.md
  60. 4 0
      template/clients/rust/clients/rust/src/lib.rs
  61. 47 0
      template/clients/rust/clients/rust/tests/create.rs
  62. 5 0
      template/clients/rust/package.json
  63. 15 0
      template/clients/rust/scripts/client/test-rust.mjs
  64. 25 0
      template/programs/counter-shank/program/Cargo.toml
  65. 3 0
      template/programs/counter-shank/program/README.md
  66. 184 0
      template/programs/counter-shank/program/idl.json
  67. 8 0
      template/programs/counter-shank/program/rustfmt.toml
  68. 137 0
      template/programs/counter-shank/program/src/assertions.rs
  69. 20 0
      template/programs/counter-shank/program/src/entrypoint.rs
  70. 62 0
      template/programs/counter-shank/program/src/error.rs
  71. 18 0
      template/programs/counter-shank/program/src/instruction.rs
  72. 11 0
      template/programs/counter-shank/program/src/lib.rs
  73. 97 0
      template/programs/counter-shank/program/src/processor.rs
  74. 50 0
      template/programs/counter-shank/program/src/state.rs
  75. 123 0
      template/programs/counter-shank/program/src/utils.rs
  76. 10 0
      tsconfig.json
  77. 23 0
      utils/deepMerge.ts
  78. 43 0
      utils/directoryTraverse.ts
  79. 17 0
      utils/generateReadme.ts
  80. 20 0
      utils/getBanner.ts
  81. 330 0
      utils/getInputs.ts
  82. 106 0
      utils/getLanguage.ts
  83. 31 0
      utils/getPackageManager.ts
  84. 49 0
      utils/getRenderContext.ts
  85. 92 0
      utils/renderTemplate.ts
  86. 27 0
      utils/sortDependencies.ts
  87. 31 0
      utils/strings.ts

+ 8 - 0
.gitignore

@@ -0,0 +1,8 @@
+.DS_Store
+**/.DS_Store
+**/target
+**/*.rs.bk
+node_modules
+dist
+outfile.cjs
+playground

+ 125 - 0
index.ts

@@ -0,0 +1,125 @@
+#!/usr/bin/env node
+
+import chalk from "chalk";
+import ejs from "ejs";
+import * as fs from "node:fs";
+import * as path from "node:path";
+
+import {
+  postOrderDirectoryTraverse,
+  preOrderDirectoryTraverse,
+} from "./utils/directoryTraverse";
+import { generateReadme } from "./utils/generateReadme";
+import { consoleLogBanner } from "./utils/getBanner";
+import { getInputs } from "./utils/getInputs";
+import { Language, getLanguage } from "./utils/getLanguage";
+import {
+  getPackageManager,
+  getPackageManagerCommand,
+} from "./utils/getPackageManager";
+import renderTemplate from "./utils/renderTemplate";
+import { RenderContext, getRenderContext } from "./utils/getRenderContext";
+
+init().catch((e) => {
+  console.error(e);
+});
+
+async function init() {
+  // Welcome message.
+  consoleLogBanner();
+
+  // Gather user inputs.
+  const ctx = await getRenderContext();
+  console.log(ctx);
+
+  // Prepare the target directory.
+  createOrEmptyTargetDirectory(ctx);
+
+  // Log start of scaffolding.
+  console.log(
+    `\n${ctx.language.infos.scaffolding} ${ctx.targetDirectoryName}...`
+  );
+
+  // Prepare rendering function and accumulate callbacks.
+  const callbacks = [];
+  const render = function render(templateName: string) {
+    const directory = path.resolve(ctx.templateDirectory, templateName);
+    renderTemplate(directory, ctx.targetDirectory, callbacks);
+  };
+
+  // Render base template.
+  render("base");
+
+  // Add configs.
+  // if (needsJsx) {
+  //   render("config/jsx");
+  // }
+
+  // An external data store for callbacks to share data
+  const dataStore = {};
+  // Process callbacks
+  for (const cb of callbacks) {
+    await cb(dataStore);
+  }
+
+  // EJS template rendering
+  preOrderDirectoryTraverse(
+    ctx.targetDirectory,
+    () => {},
+    (filepath) => {
+      if (filepath.endsWith(".ejs")) {
+        const template = fs.readFileSync(filepath, "utf-8");
+        const dest = filepath.replace(/\.ejs$/, "");
+        const content = ejs.render(template, dataStore[dest]);
+
+        fs.writeFileSync(dest, content);
+        fs.unlinkSync(filepath);
+      }
+    }
+  );
+
+  // README generation
+  fs.writeFileSync(
+    path.resolve(ctx.targetDirectory, "README.md"),
+    generateReadme(ctx)
+  );
+
+  // Log end of scaffolding.
+  console.log(`\n${ctx.language.infos.done}\n`);
+
+  // Log next steps: Cd into the target directory.
+  if (ctx.targetDirectory !== ctx.currentDirectory) {
+    const cdCommand = `cd ${ctx.targetDirectoryName.includes(" ") ? `"${ctx.targetDirectoryName}"` : ctx.targetDirectoryName}`;
+    console.log(`  ${chalk.bold(chalk.green(cdCommand))}`);
+  }
+
+  // Log next steps: Install dependencies.
+  const installCommand = ctx.getCommand("install");
+  console.log(`  ${chalk.bold(chalk.green(installCommand))}`);
+
+  // Log next steps: Generate Idls and clients.
+  const generateCommand = ctx.getCommand("run", "generate");
+  console.log(`  ${chalk.bold(chalk.green(generateCommand))}`);
+
+  // Final line break.
+  console.log();
+}
+
+function createOrEmptyTargetDirectory(ctx: RenderContext) {
+  if (!fs.existsSync(ctx.targetDirectory)) {
+    fs.mkdirSync(ctx.targetDirectory);
+  } else if (ctx.shouldOverride) {
+    postOrderDirectoryTraverse(
+      ctx.targetDirectory,
+      (dir) => fs.rmdirSync(dir),
+      (file) => fs.unlinkSync(file)
+    );
+  } else {
+    const message = ctx.language.errors.cannotOverrideDirectory.replace(
+      "$targetDirectory",
+      ctx.targetDirectoryName
+    );
+    console.log(chalk.red("✖") + ` ${message}`);
+    process.exit(1);
+  }
+}

+ 68 - 0
locales/en-US.json

@@ -0,0 +1,68 @@
+{
+  "programName": {
+    "message": "Program name:"
+  },
+  "shouldOverride": {
+    "dirForPrompts": {
+      "current": "Current directory",
+      "target": "Target directory"
+    },
+    "message": "is not empty. Remove existing files and continue?"
+  },
+  "organizationName": {
+    "message": "Organization name:"
+  },
+  "programCrateName": {
+    "message": "Program crate name:"
+  },
+  "programAddress": {
+    "message": "Program address:"
+  },
+  "programFramework": {
+    "message": "Program framework:",
+    "selectOptions": {
+      "shank": {
+        "title": "Shank",
+        "desc": "Vanilla Solana program with Shank macros to generate IDLs"
+      },
+      "anchor": {
+        "title": "Anchor"
+      }
+    }
+  },
+  "clients": {
+    "message": "Generated clients:",
+    "selectOptions": {
+      "js": {
+        "title": "JavaScript Client",
+        "desc": "A TypeScript library compatible with the new web3.js"
+      },
+      "rust": {
+        "title": "Rust Client",
+        "desc": "A Rust crate allowing consumers to interact with the program"
+      }
+    }
+  },
+  "jsClientPackageName": {
+    "message": "JavaScript client package name:"
+  },
+  "rustClientCrateName": {
+    "message": "Rust client crate name:"
+  },
+  "errors": {
+    "operationCancelled": "Operation cancelled",
+    "cannotOverrideDirectory": "Cannot override target directory \"$targetDirectory\". Run with option --force to override."
+  },
+  "defaultToggleOptions": {
+    "active": "Yes",
+    "inactive": "No"
+  },
+  "instructions": {
+    "select": "[↑/↓]: Select / [enter]: Submit answer",
+    "multiselect": "[↑/↓]: Select / [space]: Toggle selection / [a]: Toggle all / [enter]: Submit answer"
+  },
+  "infos": {
+    "scaffolding": "Scaffolding project in",
+    "done": "Done. Now run:"
+  }
+}

+ 71 - 0
locales/fr-FR.json

@@ -0,0 +1,71 @@
+{
+  "programName": {
+    "message": "Nom du program\u00a0:"
+  },
+  "shouldOverride": {
+    "dirForPrompts": {
+      "current": "Répertoire courant",
+      "target": "Répertoire cible"
+    },
+    "message": "n'est pas vide. Supprimer les fichiers existants et continuer\u00a0?"
+  },
+  "organizationName": {
+    "message": "Nom de l'organisation\u00a0:"
+  },
+  "programCrateName": {
+    "message": "Nom du crate pour le program\u00a0:"
+  },
+  "programAddress": {
+    "message": "Address du program\u00a0:"
+  },
+  "programFramework": {
+    "message": "Program framework\u00a0:",
+    "selectOptions": {
+      "shank": {
+        "title": "Shank",
+        "desc": "Solana program pure avec des macros Shank pour générer les IDLs"
+      },
+      "anchor": {
+        "title": "Anchor"
+      }
+    }
+  },
+  "clients": {
+    "message": "Clients générés\u00a0:",
+    "selectOptions": {
+      "js": {
+        "title": "Client JavaScript",
+        "desc": "Une librairie TypeScript compatible avec le nouveau web3.js"
+      },
+      "rust": {
+        "title": "Client Rust",
+        "desc": "Une librairie Rust pour interagir avec le program"
+      }
+    }
+  },
+  "jsClientPackageName": {
+    "message": "Nom du package pour le client JavaScript\u00a0:"
+  },
+  "rustClientCrateName": {
+    "message": "Nom du crate pour le client Rust\u00a0:"
+  },
+  "needsTypeScript": {
+    "message": "Ajouter TypeScript\u00a0?"
+  },
+  "errors": {
+    "operationCancelled": "Operation annulée",
+    "cannotOverrideDirectory": "Impossible de remplacer le répertoire cible \"$targetDirectory\". Exécutez avec l'option --force pour remplacer."
+  },
+  "defaultToggleOptions": {
+    "active": "Oui",
+    "inactive": "Non"
+  },
+  "instructions": {
+    "select": "[↑/↓]: Sélectionner / [entrée]: Valider",
+    "multiselect": "[↑/↓]: Sélectionner / [espace]: Basculer la sélection / [a]: Basculer tout / [entrée]: Valider"
+  },
+  "infos": {
+    "scaffolding": "Génération du projet dans",
+    "done": "Terminé. Exécutez maintenant\u00a0:"
+  }
+}

+ 13 - 0
my-program/.gitignore

@@ -0,0 +1,13 @@
+.anchor
+.DS_Store
+**/.DS_Store
+**/target
+**/*.rs.bk
+node_modules
+test-ledger
+dist
+.amman
+.cargo
+.bin
+.programs
+.env.*

+ 3 - 0
my-program/Cargo.toml

@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = ["program"]

+ 21 - 0
my-program/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 Solana Foundation
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 12 - 0
my-program/README.md

@@ -0,0 +1,12 @@
+# My Program
+
+TODO
+
+```sh
+npm run run -- programs:build
+npm run run -- programs:test
+npm run run -- programs:format
+npm run run -- programs:lint
+```
+
+TODO

+ 19 - 0
my-program/package.json

@@ -0,0 +1,19 @@
+{
+  "private": true,
+  "scripts": {
+    "programs:build": "./scripts/program/build.mjs",
+    "programs:test": "./scripts/program/test.mjs",
+    "programs:debug": "./scripts/program/test.mjs",
+    "programs:clean": "./scripts/program/clean.mjs",
+    "programs:format": "./scripts/program/format.mjs",
+    "programs:lint": "./scripts/program/lint.mjs",
+    "generate:idls": "./scripts/generate-idls.mjs"
+  },
+  "devDependencies": {
+    "@iarna/toml": "^2.2.5",
+    "@metaplex-foundation/shank-js": "^0.1.7",
+    "typescript": "^5.3.3",
+    "zx": "^7.2.3"
+  },
+  "packageManager": "pnpm@8.9.0"
+}

+ 30 - 0
my-program/scripts/generate-idls.mjs

@@ -0,0 +1,30 @@
+#!/usr/bin/env zx
+import "zx/globals";
+import { generateIdl } from "@metaplex-foundation/shank-js";
+import { getCargo, getProgramFolders } from "./utils.mjs";
+
+// REMOVE ME: START
+echo(chalk.red("This program doesn't use Shank."));
+echo("This is a temporary guard to prevent accidental execution.");
+echo("When the program uses Shank to generate its IDL,");
+echo("Remove this guard in `scripts/generate-idls.mjs`.");
+process.exit();
+// REMOVE ME: END
+
+const binaryInstallDir = path.join(__dirname, "..", ".cargo");
+
+getProgramFolders().forEach((folder) => {
+  const cargo = getCargo(folder);
+  const isShank = Object.keys(cargo.dependencies).includes("shank");
+  const programDir = path.join(__dirname, "..", folder);
+
+  generateIdl({
+    generator: isShank ? "shank" : "anchor",
+    programName: cargo.package.name.replace(/-/g, "_"),
+    programId: cargo.package.metadata.solana["program-id"],
+    idlDir: programDir,
+    idlName: "idl",
+    programDir,
+    binaryInstallDir,
+  });
+});

+ 14 - 0
my-program/scripts/program/build.mjs

@@ -0,0 +1,14 @@
+#!/usr/bin/env zx
+import "zx/globals";
+import { workingDirectory, getProgramFolders } from "../utils.mjs";
+
+// Save external programs binaries to the output directory.
+import "./dump.mjs";
+
+// Build the programs.
+await Promise.all(
+  getProgramFolders().map(async (folder) => {
+    await $`cd ${path.join(workingDirectory, folder)}`.quiet();
+    await $`cargo build-sbf ${argv._}`;
+  })
+);

+ 10 - 0
my-program/scripts/program/clean.mjs

@@ -0,0 +1,10 @@
+#!/usr/bin/env zx
+import "zx/globals";
+import { getExternalProgramOutputDir } from "../utils.mjs";
+
+// Remove the programs output directories.
+const externalProgramOutput = getExternalProgramOutputDir();
+await $`rm -rf ${externalProgramOutput}`;
+
+// Remove the target directory.
+await $`rm -rf target`;

+ 69 - 0
my-program/scripts/program/dump.mjs

@@ -0,0 +1,69 @@
+#!/usr/bin/env zx
+import "zx/globals";
+import {
+  getExternalProgramAddresses,
+  getExternalProgramOutputDir,
+} from "../utils.mjs";
+
+// Get input from environment variables.
+const rpc = process.env.RPC ?? "https://api.mainnet-beta.solana.com";
+const outputDir = getExternalProgramOutputDir();
+await dump();
+
+/** Dump external programs binaries if needed. */
+async function dump() {
+  // Ensure we have some external programs to dump.
+  const addresses = getExternalProgramAddresses();
+  if (addresses.length === 0) return;
+  echo(`Dumping external accounts to '${outputDir}':`);
+
+  // Create the output directory if needed.
+  $`mkdir -p ${outputDir}`.quiet();
+
+  // Copy the binaries from the chain or warn if they are different.
+  await Promise.all(
+    addresses.map(async (address) => {
+      const binary = `${address}.so`;
+      const hasBinary = await fs.exists(`${outputDir}/${binary}`);
+
+      if (!hasBinary) {
+        await copyFromChain(address, binary);
+        echo(`Wrote account data to ${outputDir}/${binary}`);
+        return;
+      }
+
+      await copyFromChain(address, `onchain-${binary}`);
+      const [onChainHash, localHash] = await Promise.all([
+        $`sha256sum -b ${outputDir}/onchain-${binary} | cut -d ' ' -f 1`.quiet(),
+        $`sha256sum -b ${outputDir}/${binary} | cut -d ' ' -f 1`.quiet(),
+      ]);
+
+      if (onChainHash.toString() !== localHash.toString()) {
+        echo(
+          chalk.yellow("[ WARNING ]"),
+          `on-chain and local binaries are different for '${binary}'`
+        );
+      } else {
+        echo(
+          chalk.green("[ SKIPPED ]"),
+          `on-chain and local binaries are the same for '${binary}'`
+        );
+      }
+
+      await $`rm ${outputDir}/onchain-${binary}`.quiet();
+    })
+  );
+}
+
+/** Helper function to copy external programs or accounts binaries from the chain. */
+async function copyFromChain(address, binary) {
+  switch (binary.split(".").pop()) {
+    case "bin":
+      return $`solana account -u ${rpc} ${address} -o ${outputDir}/${binary} >/dev/null`.quiet();
+    case "so":
+      return $`solana program dump -u ${rpc} ${address} ${outputDir}/${binary} >/dev/null`.quiet();
+    default:
+      echo(chalk.red(`[  ERROR  ] unknown account type for '${binary}'`));
+      await $`exit 1`;
+  }
+}

+ 11 - 0
my-program/scripts/program/format.mjs

@@ -0,0 +1,11 @@
+#!/usr/bin/env zx
+import "zx/globals";
+import { workingDirectory, getProgramFolders } from "../utils.mjs";
+
+// Format the programs.
+await Promise.all(
+  getProgramFolders().map(async (folder) => {
+    await $`cd ${path.join(workingDirectory, folder)}`.quiet();
+    await $`cargo fmt ${argv._}`;
+  })
+);

+ 11 - 0
my-program/scripts/program/lint.mjs

@@ -0,0 +1,11 @@
+#!/usr/bin/env zx
+import "zx/globals";
+import { workingDirectory, getProgramFolders } from "../utils.mjs";
+
+// Lint the programs using clippy.
+await Promise.all(
+  getProgramFolders().map(async (folder) => {
+    await $`cd ${path.join(workingDirectory, folder)}`.quiet();
+    await $`cargo clippy ${argv._}`;
+  })
+);

+ 20 - 0
my-program/scripts/program/test.mjs

@@ -0,0 +1,20 @@
+#!/usr/bin/env zx
+import "zx/globals";
+import { workingDirectory, getProgramFolders } from "../utils.mjs";
+
+// Save external programs binaries to the output directory.
+import "./dump.mjs";
+
+// Test the programs.
+await Promise.all(
+  getProgramFolders().map(async (folder) => {
+    await $`cd ${path.join(workingDirectory, folder)}`.quiet();
+    const hasSolfmt = await which("solfmt", { nothrow: true });
+
+    if (hasSolfmt) {
+      await $`RUST_LOG=error cargo test-sbf ${argv._} 2>&1 | solfmt`;
+    } else {
+      await $`RUST_LOG=error cargo test-sbf ${argv._}`;
+    }
+  })
+);

+ 66 - 0
my-program/scripts/utils.mjs

@@ -0,0 +1,66 @@
+import "zx/globals";
+import { parse as parseToml } from "@iarna/toml";
+
+process.env.FORCE_COLOR = 3;
+process.env.CARGO_TERM_COLOR = "always";
+
+export const workingDirectory = (await $`pwd`.quiet()).toString().trim();
+
+export function getAllProgramIdls() {
+  return getAllProgramFolders().map((folder) =>
+    path.join(workingDirectory, folder, "idl.json")
+  );
+}
+
+export function getExternalProgramOutputDir() {
+  const config =
+    getCargo().workspace?.metadata?.solana?.["external-programs-output"];
+  return path.join(workingDirectory, config ?? "target/deploy");
+}
+
+export function getExternalProgramAddresses() {
+  const addresses = getProgramFolders().flatMap(
+    (folder) =>
+      getCargo(folder).package?.metadata?.solana?.["program-dependencies"] ?? []
+  );
+  return Array.from(new Set(addresses));
+}
+
+let didWarnAboutMissingPrograms = false;
+export function getProgramFolders() {
+  const programs = process.env.PROGRAMS
+    ? process.env.PROGRAMS.split(/\s+/)
+    : getAllProgramFolders();
+  const filteredPrograms = programs.filter((program) =>
+    fs.existsSync(path.join(workingDirectory, program))
+  );
+
+  if (
+    filteredPrograms.length !== programs.length &&
+    !didWarnAboutMissingPrograms
+  ) {
+    didWarnAboutMissingPrograms = true;
+    programs
+      .filter((program) => !filteredPrograms.includes(program))
+      .forEach((program) => {
+        echo(chalk.yellow(`Program not found: ${workingDirectory}/${program}`));
+      });
+  }
+
+  return filteredPrograms;
+}
+
+export function getAllProgramFolders() {
+  return getCargo().workspace.members.filter((member) =>
+    (getCargo(member).lib?.["crate-type"] ?? []).includes("cdylib")
+  );
+}
+
+export function getCargo(folder) {
+  return parseToml(
+    fs.readFileSync(
+      path.join(workingDirectory, folder ? folder : ".", "Cargo.toml"),
+      "utf8"
+    )
+  );
+}

+ 48 - 0
package.json

@@ -0,0 +1,48 @@
+{
+  "name": "create-solana-program",
+  "version": "0.1.0",
+  "description": "An easy way to start a Solana program",
+  "type": "module",
+  "bin": {
+    "create-solana-program": "dist/index.cjs"
+  },
+  "files": [
+    "dist",
+    "locales",
+    "template"
+  ],
+  "engines": {
+    "node": ">=v18.0.0"
+  },
+  "scripts": {
+    "format": "prettier --write .",
+    "build": "zx ./scripts/build.mjs",
+    "snapshot": "zx ./scripts/snapshot.mjs",
+    "pretest": "pnpm build snapshot",
+    "test": "zx ./scripts/test.mjs",
+    "prepublishOnly": "zx ./scripts/prepublish.mjs"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/solana-program/create-solana-program.git"
+  },
+  "keywords": [],
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/solana-program/create-solana-program/issues"
+  },
+  "homepage": "https://github.com/solana-program/create-solana-program#readme",
+  "devDependencies": {
+    "@tsconfig/node20": "^20.1.2",
+    "@types/eslint": "^8.56.5",
+    "@types/node": "^20.11.24",
+    "@types/prompts": "^2.4.9",
+    "chalk": "^5.3.0",
+    "ejs": "^3.1.9",
+    "esbuild": "^0.18.20",
+    "gradient-string": "^2.0.2",
+    "prettier": "^3.2.5",
+    "prompts": "^2.4.2",
+    "zx": "^7.2.3"
+  }
+}

+ 811 - 0
pnpm-lock.yaml

@@ -0,0 +1,811 @@
+lockfileVersion: '6.0'
+
+settings:
+  autoInstallPeers: true
+  excludeLinksFromLockfile: false
+
+devDependencies:
+  '@tsconfig/node20':
+    specifier: ^20.1.2
+    version: 20.1.2
+  '@types/eslint':
+    specifier: ^8.56.5
+    version: 8.56.5
+  '@types/node':
+    specifier: ^20.11.24
+    version: 20.11.24
+  '@types/prompts':
+    specifier: ^2.4.9
+    version: 2.4.9
+  chalk:
+    specifier: ^5.3.0
+    version: 5.3.0
+  ejs:
+    specifier: ^3.1.9
+    version: 3.1.9
+  esbuild:
+    specifier: ^0.18.20
+    version: 0.18.20
+  gradient-string:
+    specifier: ^2.0.2
+    version: 2.0.2
+  prettier:
+    specifier: ^3.2.5
+    version: 3.2.5
+  prompts:
+    specifier: ^2.4.2
+    version: 2.4.2
+  zx:
+    specifier: ^7.2.3
+    version: 7.2.3
+
+packages:
+
+  /@esbuild/android-arm64@0.18.20:
+    resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/android-arm@0.18.20:
+    resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/android-x64@0.18.20:
+    resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/darwin-arm64@0.18.20:
+    resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/darwin-x64@0.18.20:
+    resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/freebsd-arm64@0.18.20:
+    resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [freebsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/freebsd-x64@0.18.20:
+    resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [freebsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-arm64@0.18.20:
+    resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-arm@0.18.20:
+    resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-ia32@0.18.20:
+    resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-loong64@0.18.20:
+    resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
+    engines: {node: '>=12'}
+    cpu: [loong64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-mips64el@0.18.20:
+    resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
+    engines: {node: '>=12'}
+    cpu: [mips64el]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-ppc64@0.18.20:
+    resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-riscv64@0.18.20:
+    resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
+    engines: {node: '>=12'}
+    cpu: [riscv64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-s390x@0.18.20:
+    resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
+    engines: {node: '>=12'}
+    cpu: [s390x]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-x64@0.18.20:
+    resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/netbsd-x64@0.18.20:
+    resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [netbsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/openbsd-x64@0.18.20:
+    resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [openbsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/sunos-x64@0.18.20:
+    resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [sunos]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/win32-arm64@0.18.20:
+    resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/win32-ia32@0.18.20:
+    resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/win32-x64@0.18.20:
+    resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@nodelib/fs.scandir@2.1.5:
+    resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+    engines: {node: '>= 8'}
+    dependencies:
+      '@nodelib/fs.stat': 2.0.5
+      run-parallel: 1.2.0
+    dev: true
+
+  /@nodelib/fs.stat@2.0.5:
+    resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+    engines: {node: '>= 8'}
+    dev: true
+
+  /@nodelib/fs.walk@1.2.8:
+    resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+    engines: {node: '>= 8'}
+    dependencies:
+      '@nodelib/fs.scandir': 2.1.5
+      fastq: 1.17.1
+    dev: true
+
+  /@tsconfig/node20@20.1.2:
+    resolution: {integrity: sha512-madaWq2k+LYMEhmcp0fs+OGaLFk0OenpHa4gmI4VEmCKX4PJntQ6fnnGADVFrVkBj0wIdAlQnK/MrlYTHsa1gQ==}
+    dev: true
+
+  /@types/eslint@8.56.5:
+    resolution: {integrity: sha512-u5/YPJHo1tvkSF2CE0USEkxon82Z5DBy2xR+qfyYNszpX9qcs4sT6uq2kBbj4BXY1+DBGDPnrhMZV3pKWGNukw==}
+    dependencies:
+      '@types/estree': 1.0.5
+      '@types/json-schema': 7.0.15
+    dev: true
+
+  /@types/estree@1.0.5:
+    resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
+    dev: true
+
+  /@types/fs-extra@11.0.4:
+    resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==}
+    dependencies:
+      '@types/jsonfile': 6.1.4
+      '@types/node': 20.11.24
+    dev: true
+
+  /@types/json-schema@7.0.15:
+    resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+    dev: true
+
+  /@types/jsonfile@6.1.4:
+    resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==}
+    dependencies:
+      '@types/node': 20.11.24
+    dev: true
+
+  /@types/minimist@1.2.5:
+    resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==}
+    dev: true
+
+  /@types/node@18.19.21:
+    resolution: {integrity: sha512-2Q2NeB6BmiTFQi4DHBzncSoq/cJMLDdhPaAoJFnFCyD9a8VPZRf7a1GAwp1Edb7ROaZc5Jz/tnZyL6EsWMRaqw==}
+    dependencies:
+      undici-types: 5.26.5
+    dev: true
+
+  /@types/node@20.11.24:
+    resolution: {integrity: sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==}
+    dependencies:
+      undici-types: 5.26.5
+    dev: true
+
+  /@types/prompts@2.4.9:
+    resolution: {integrity: sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==}
+    dependencies:
+      '@types/node': 20.11.24
+      kleur: 3.0.3
+    dev: true
+
+  /@types/ps-tree@1.1.6:
+    resolution: {integrity: sha512-PtrlVaOaI44/3pl3cvnlK+GxOM3re2526TJvPvh7W+keHIXdV4TE0ylpPBAcvFQCbGitaTXwL9u+RF7qtVeazQ==}
+    dev: true
+
+  /@types/tinycolor2@1.4.6:
+    resolution: {integrity: sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==}
+    dev: true
+
+  /@types/which@3.0.3:
+    resolution: {integrity: sha512-2C1+XoY0huExTbs8MQv1DuS5FS86+SEjdM9F/+GS61gg5Hqbtj8ZiDSx8MfWcyei907fIPbfPGCOrNUTnVHY1g==}
+    dev: true
+
+  /ansi-styles@4.3.0:
+    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+    engines: {node: '>=8'}
+    dependencies:
+      color-convert: 2.0.1
+    dev: true
+
+  /async@3.2.5:
+    resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==}
+    dev: true
+
+  /balanced-match@1.0.2:
+    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+    dev: true
+
+  /brace-expansion@1.1.11:
+    resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+    dependencies:
+      balanced-match: 1.0.2
+      concat-map: 0.0.1
+    dev: true
+
+  /brace-expansion@2.0.1:
+    resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
+    dependencies:
+      balanced-match: 1.0.2
+    dev: true
+
+  /braces@3.0.2:
+    resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
+    engines: {node: '>=8'}
+    dependencies:
+      fill-range: 7.0.1
+    dev: true
+
+  /chalk@4.1.2:
+    resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+    engines: {node: '>=10'}
+    dependencies:
+      ansi-styles: 4.3.0
+      supports-color: 7.2.0
+    dev: true
+
+  /chalk@5.3.0:
+    resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==}
+    engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
+    dev: true
+
+  /color-convert@2.0.1:
+    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+    engines: {node: '>=7.0.0'}
+    dependencies:
+      color-name: 1.1.4
+    dev: true
+
+  /color-name@1.1.4:
+    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+    dev: true
+
+  /concat-map@0.0.1:
+    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+    dev: true
+
+  /data-uri-to-buffer@4.0.1:
+    resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
+    engines: {node: '>= 12'}
+    dev: true
+
+  /dir-glob@3.0.1:
+    resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
+    engines: {node: '>=8'}
+    dependencies:
+      path-type: 4.0.0
+    dev: true
+
+  /duplexer@0.1.2:
+    resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
+    dev: true
+
+  /ejs@3.1.9:
+    resolution: {integrity: sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==}
+    engines: {node: '>=0.10.0'}
+    hasBin: true
+    dependencies:
+      jake: 10.8.7
+    dev: true
+
+  /esbuild@0.18.20:
+    resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
+    engines: {node: '>=12'}
+    hasBin: true
+    requiresBuild: true
+    optionalDependencies:
+      '@esbuild/android-arm': 0.18.20
+      '@esbuild/android-arm64': 0.18.20
+      '@esbuild/android-x64': 0.18.20
+      '@esbuild/darwin-arm64': 0.18.20
+      '@esbuild/darwin-x64': 0.18.20
+      '@esbuild/freebsd-arm64': 0.18.20
+      '@esbuild/freebsd-x64': 0.18.20
+      '@esbuild/linux-arm': 0.18.20
+      '@esbuild/linux-arm64': 0.18.20
+      '@esbuild/linux-ia32': 0.18.20
+      '@esbuild/linux-loong64': 0.18.20
+      '@esbuild/linux-mips64el': 0.18.20
+      '@esbuild/linux-ppc64': 0.18.20
+      '@esbuild/linux-riscv64': 0.18.20
+      '@esbuild/linux-s390x': 0.18.20
+      '@esbuild/linux-x64': 0.18.20
+      '@esbuild/netbsd-x64': 0.18.20
+      '@esbuild/openbsd-x64': 0.18.20
+      '@esbuild/sunos-x64': 0.18.20
+      '@esbuild/win32-arm64': 0.18.20
+      '@esbuild/win32-ia32': 0.18.20
+      '@esbuild/win32-x64': 0.18.20
+    dev: true
+
+  /event-stream@3.3.4:
+    resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==}
+    dependencies:
+      duplexer: 0.1.2
+      from: 0.1.7
+      map-stream: 0.1.0
+      pause-stream: 0.0.11
+      split: 0.3.3
+      stream-combiner: 0.0.4
+      through: 2.3.8
+    dev: true
+
+  /fast-glob@3.3.2:
+    resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
+    engines: {node: '>=8.6.0'}
+    dependencies:
+      '@nodelib/fs.stat': 2.0.5
+      '@nodelib/fs.walk': 1.2.8
+      glob-parent: 5.1.2
+      merge2: 1.4.1
+      micromatch: 4.0.5
+    dev: true
+
+  /fastq@1.17.1:
+    resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
+    dependencies:
+      reusify: 1.0.4
+    dev: true
+
+  /fetch-blob@3.2.0:
+    resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
+    engines: {node: ^12.20 || >= 14.13}
+    dependencies:
+      node-domexception: 1.0.0
+      web-streams-polyfill: 3.3.3
+    dev: true
+
+  /filelist@1.0.4:
+    resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
+    dependencies:
+      minimatch: 5.1.6
+    dev: true
+
+  /fill-range@7.0.1:
+    resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
+    engines: {node: '>=8'}
+    dependencies:
+      to-regex-range: 5.0.1
+    dev: true
+
+  /formdata-polyfill@4.0.10:
+    resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
+    engines: {node: '>=12.20.0'}
+    dependencies:
+      fetch-blob: 3.2.0
+    dev: true
+
+  /from@0.1.7:
+    resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==}
+    dev: true
+
+  /fs-extra@11.2.0:
+    resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==}
+    engines: {node: '>=14.14'}
+    dependencies:
+      graceful-fs: 4.2.11
+      jsonfile: 6.1.0
+      universalify: 2.0.1
+    dev: true
+
+  /fx@31.0.0:
+    resolution: {integrity: sha512-OoeYSPKqNKmfnH4s+rGYI0c8OZmqqOOXsUtqy0YyHqQQoQSDiDs3m3M9uXKx5OQR+jDx7/FhYqpO3kl/As/xgg==}
+    hasBin: true
+    dev: true
+
+  /glob-parent@5.1.2:
+    resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+    engines: {node: '>= 6'}
+    dependencies:
+      is-glob: 4.0.3
+    dev: true
+
+  /globby@13.2.2:
+    resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    dependencies:
+      dir-glob: 3.0.1
+      fast-glob: 3.3.2
+      ignore: 5.3.1
+      merge2: 1.4.1
+      slash: 4.0.0
+    dev: true
+
+  /graceful-fs@4.2.11:
+    resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+    dev: true
+
+  /gradient-string@2.0.2:
+    resolution: {integrity: sha512-rEDCuqUQ4tbD78TpzsMtt5OIf0cBCSDWSJtUDaF6JsAh+k0v9r++NzxNEG87oDZx9ZwGhD8DaezR2L/yrw0Jdw==}
+    engines: {node: '>=10'}
+    dependencies:
+      chalk: 4.1.2
+      tinygradient: 1.1.5
+    dev: true
+
+  /has-flag@4.0.0:
+    resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /ignore@5.3.1:
+    resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
+    engines: {node: '>= 4'}
+    dev: true
+
+  /is-extglob@2.1.1:
+    resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /is-glob@4.0.3:
+    resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      is-extglob: 2.1.1
+    dev: true
+
+  /is-number@7.0.0:
+    resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+    engines: {node: '>=0.12.0'}
+    dev: true
+
+  /isexe@2.0.0:
+    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+    dev: true
+
+  /jake@10.8.7:
+    resolution: {integrity: sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==}
+    engines: {node: '>=10'}
+    hasBin: true
+    dependencies:
+      async: 3.2.5
+      chalk: 4.1.2
+      filelist: 1.0.4
+      minimatch: 3.1.2
+    dev: true
+
+  /jsonfile@6.1.0:
+    resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
+    dependencies:
+      universalify: 2.0.1
+    optionalDependencies:
+      graceful-fs: 4.2.11
+    dev: true
+
+  /kleur@3.0.3:
+    resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /map-stream@0.1.0:
+    resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==}
+    dev: true
+
+  /merge2@1.4.1:
+    resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+    engines: {node: '>= 8'}
+    dev: true
+
+  /micromatch@4.0.5:
+    resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
+    engines: {node: '>=8.6'}
+    dependencies:
+      braces: 3.0.2
+      picomatch: 2.3.1
+    dev: true
+
+  /minimatch@3.1.2:
+    resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+    dependencies:
+      brace-expansion: 1.1.11
+    dev: true
+
+  /minimatch@5.1.6:
+    resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
+    engines: {node: '>=10'}
+    dependencies:
+      brace-expansion: 2.0.1
+    dev: true
+
+  /minimist@1.2.8:
+    resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+    dev: true
+
+  /node-domexception@1.0.0:
+    resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
+    engines: {node: '>=10.5.0'}
+    dev: true
+
+  /node-fetch@3.3.1:
+    resolution: {integrity: sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    dependencies:
+      data-uri-to-buffer: 4.0.1
+      fetch-blob: 3.2.0
+      formdata-polyfill: 4.0.10
+    dev: true
+
+  /path-type@4.0.0:
+    resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /pause-stream@0.0.11:
+    resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==}
+    dependencies:
+      through: 2.3.8
+    dev: true
+
+  /picomatch@2.3.1:
+    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+    engines: {node: '>=8.6'}
+    dev: true
+
+  /prettier@3.2.5:
+    resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==}
+    engines: {node: '>=14'}
+    hasBin: true
+    dev: true
+
+  /prompts@2.4.2:
+    resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
+    engines: {node: '>= 6'}
+    dependencies:
+      kleur: 3.0.3
+      sisteransi: 1.0.5
+    dev: true
+
+  /ps-tree@1.2.0:
+    resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==}
+    engines: {node: '>= 0.10'}
+    hasBin: true
+    dependencies:
+      event-stream: 3.3.4
+    dev: true
+
+  /queue-microtask@1.2.3:
+    resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+    dev: true
+
+  /reusify@1.0.4:
+    resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
+    engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+    dev: true
+
+  /run-parallel@1.2.0:
+    resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+    dependencies:
+      queue-microtask: 1.2.3
+    dev: true
+
+  /sisteransi@1.0.5:
+    resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
+    dev: true
+
+  /slash@4.0.0:
+    resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /split@0.3.3:
+    resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==}
+    dependencies:
+      through: 2.3.8
+    dev: true
+
+  /stream-combiner@0.0.4:
+    resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==}
+    dependencies:
+      duplexer: 0.1.2
+    dev: true
+
+  /supports-color@7.2.0:
+    resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+    engines: {node: '>=8'}
+    dependencies:
+      has-flag: 4.0.0
+    dev: true
+
+  /through@2.3.8:
+    resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
+    dev: true
+
+  /tinycolor2@1.6.0:
+    resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==}
+    dev: true
+
+  /tinygradient@1.1.5:
+    resolution: {integrity: sha512-8nIfc2vgQ4TeLnk2lFj4tRLvvJwEfQuabdsmvDdQPT0xlk9TaNtpGd6nNRxXoK6vQhN6RSzj+Cnp5tTQmpxmbw==}
+    dependencies:
+      '@types/tinycolor2': 1.4.6
+      tinycolor2: 1.6.0
+    dev: true
+
+  /to-regex-range@5.0.1:
+    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+    engines: {node: '>=8.0'}
+    dependencies:
+      is-number: 7.0.0
+    dev: true
+
+  /undici-types@5.26.5:
+    resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
+    dev: true
+
+  /universalify@2.0.1:
+    resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+    engines: {node: '>= 10.0.0'}
+    dev: true
+
+  /web-streams-polyfill@3.3.3:
+    resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
+    engines: {node: '>= 8'}
+    dev: true
+
+  /webpod@0.0.2:
+    resolution: {integrity: sha512-cSwwQIeg8v4i3p4ajHhwgR7N6VyxAf+KYSSsY6Pd3aETE+xEU4vbitz7qQkB0I321xnhDdgtxuiSfk5r/FVtjg==}
+    hasBin: true
+    dev: true
+
+  /which@3.0.1:
+    resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+    hasBin: true
+    dependencies:
+      isexe: 2.0.0
+    dev: true
+
+  /yaml@2.4.0:
+    resolution: {integrity: sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==}
+    engines: {node: '>= 14'}
+    hasBin: true
+    dev: true
+
+  /zx@7.2.3:
+    resolution: {integrity: sha512-QODu38nLlYXg/B/Gw7ZKiZrvPkEsjPN3LQ5JFXM7h0JvwhEdPNNl+4Ao1y4+o3CLNiDUNcwzQYZ4/Ko7kKzCMA==}
+    engines: {node: '>= 16.0.0'}
+    hasBin: true
+    dependencies:
+      '@types/fs-extra': 11.0.4
+      '@types/minimist': 1.2.5
+      '@types/node': 18.19.21
+      '@types/ps-tree': 1.1.6
+      '@types/which': 3.0.3
+      chalk: 5.3.0
+      fs-extra: 11.2.0
+      fx: 31.0.0
+      globby: 13.2.2
+      minimist: 1.2.8
+      node-fetch: 3.3.1
+      ps-tree: 1.2.0
+      webpod: 0.0.2
+      which: 3.0.1
+      yaml: 2.4.0
+    dev: true

+ 31 - 0
scripts/build.mjs

@@ -0,0 +1,31 @@
+import * as esbuild from "esbuild";
+
+await esbuild.build({
+  bundle: true,
+  entryPoints: ["index.ts"],
+  external: ["locales/*"],
+  outfile: "outfile.cjs",
+  format: "cjs",
+  platform: "node",
+  target: "node14",
+
+  plugins: [
+    {
+      name: "alias",
+      setup({ onResolve, resolve }) {
+        onResolve(
+          { filter: /^prompts$/, namespace: "file" },
+          async ({ importer, resolveDir }) => {
+            // we can always use non-transpiled code since we support 14.16.0+
+            const result = await resolve("prompts/lib/index.js", {
+              importer,
+              resolveDir,
+              kind: "import-statement",
+            });
+            return result;
+          }
+        );
+      },
+    },
+  ],
+});

+ 29 - 0
scripts/prepublish.mjs

@@ -0,0 +1,29 @@
+#!/usr/bin/env zx
+import 'zx/globals'
+
+await $`pnpm build`
+await $`pnpm snapshot`
+
+let { version } = JSON.parse(await fs.readFile('./package.json'))
+
+const playgroundDir = path.resolve(__dirname, '../playground/')
+cd(playgroundDir)
+
+await $`pnpm install`
+await $`git add -A .`
+try {
+  await $`git commit -m "version ${version} snapshot"`
+} catch (e) {
+  if (!e.stdout.includes('nothing to commit')) {
+    throw e
+  }
+}
+
+await $`git tag -m "v${version}" v${version}`
+await $`git push --follow-tags`
+
+const projectRoot = path.resolve(__dirname, '../')
+cd(projectRoot)
+await $`git add playground`
+await $`git commit -m 'chore: update snapshot' --allow-empty`
+await $`git push --follow-tags`

+ 30 - 0
scripts/snapshot.mjs

@@ -0,0 +1,30 @@
+#!/usr/bin/env zx
+import fs from "node:fs";
+import "zx/globals";
+
+$.verbose = false;
+
+if (!/pnpm/.test(process.env.npm_config_user_agent ?? ""))
+  throw new Error(
+    "Please use pnpm ('pnpm run snapshot') to generate snapshots!"
+  );
+
+const playgroundDir = path.resolve(__dirname, "../playground/");
+cd(playgroundDir);
+
+// remove all previous combinations
+for (const flags of flagCombinations) {
+  const projectName = flags.join("-");
+
+  console.log(`Removing previously generated project ${projectName}`);
+  fs.rmSync(projectName, { recursive: true, force: true });
+}
+
+const bin = path.posix.relative("../playground/", "../outfile.cjs");
+
+for (const flags of flagCombinations) {
+  const projectName = flags.join("-");
+
+  console.log(`Creating project ${projectName}`);
+  await $`node ${[bin, projectName, ...flags.map((flag) => `--${flag}`), "--force"]}`;
+}

+ 37 - 0
scripts/test.mjs

@@ -0,0 +1,37 @@
+#!/usr/bin/env zx
+import "zx/globals";
+
+const playgroundDir = path.resolve(__dirname, "../playground/");
+let projects = fs
+  .readdirSync(playgroundDir, { withFileTypes: true })
+  .filter((dirent) => dirent.isDirectory())
+  .map((dirent) => dirent.name)
+  .filter((name) => !name.startsWith(".") && name !== "node_modules");
+
+if (process.argv[3])
+  projects = projects.filter((project) => project.includes(process.argv[3]));
+
+cd(playgroundDir);
+console.log("Installing playground dependencies");
+await $`pnpm install`;
+
+for (const projectName of projects) {
+  cd(path.resolve(playgroundDir, projectName));
+  const packageJSON = require(
+    path.resolve(playgroundDir, projectName, "package.json")
+  );
+
+  console.log(`
+  
+#####
+Building ${projectName}
+#####
+  
+  `);
+  await $`pnpm build`;
+
+  if ("type-check" in packageJSON.scripts) {
+    console.log(`Running type-check in ${projectName}`);
+    await $`pnpm type-check`;
+  }
+}

+ 3 - 0
template/base/Cargo.toml

@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = ["program"]

+ 21 - 0
template/base/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 Solana Foundation
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 13 - 0
template/base/_gitignore

@@ -0,0 +1,13 @@
+.anchor
+.DS_Store
+**/.DS_Store
+**/target
+**/*.rs.bk
+node_modules
+test-ledger
+dist
+.amman
+.cargo
+.bin
+.programs
+.env.*

+ 19 - 0
template/base/package.json

@@ -0,0 +1,19 @@
+{
+  "private": true,
+  "scripts": {
+    "programs:build": "./scripts/program/build.mjs",
+    "programs:test": "./scripts/program/test.mjs",
+    "programs:debug": "./scripts/program/test.mjs",
+    "programs:clean": "./scripts/program/clean.mjs",
+    "programs:format": "./scripts/program/format.mjs",
+    "programs:lint": "./scripts/program/lint.mjs",
+    "generate:idls": "./scripts/generate-idls.mjs"
+  },
+  "devDependencies": {
+    "@iarna/toml": "^2.2.5",
+    "@metaplex-foundation/shank-js": "^0.1.7",
+    "typescript": "^5.3.3",
+    "zx": "^7.2.3"
+  },
+  "packageManager": "pnpm@8.9.0"
+}

+ 30 - 0
template/base/scripts/generate-idls.mjs

@@ -0,0 +1,30 @@
+#!/usr/bin/env zx
+import "zx/globals";
+import { generateIdl } from "@metaplex-foundation/shank-js";
+import { getCargo, getProgramFolders } from "./utils.mjs";
+
+// REMOVE ME: START
+echo(chalk.red("This program doesn't use Shank."));
+echo("This is a temporary guard to prevent accidental execution.");
+echo("When the program uses Shank to generate its IDL,");
+echo("Remove this guard in `scripts/generate-idls.mjs`.");
+process.exit();
+// REMOVE ME: END
+
+const binaryInstallDir = path.join(__dirname, "..", ".cargo");
+
+getProgramFolders().forEach((folder) => {
+  const cargo = getCargo(folder);
+  const isShank = Object.keys(cargo.dependencies).includes("shank");
+  const programDir = path.join(__dirname, "..", folder);
+
+  generateIdl({
+    generator: isShank ? "shank" : "anchor",
+    programName: cargo.package.name.replace(/-/g, "_"),
+    programId: cargo.package.metadata.solana["program-id"],
+    idlDir: programDir,
+    idlName: "idl",
+    programDir,
+    binaryInstallDir,
+  });
+});

+ 14 - 0
template/base/scripts/program/build.mjs

@@ -0,0 +1,14 @@
+#!/usr/bin/env zx
+import "zx/globals";
+import { workingDirectory, getProgramFolders } from "../utils.mjs";
+
+// Save external programs binaries to the output directory.
+import "./dump.mjs";
+
+// Build the programs.
+await Promise.all(
+  getProgramFolders().map(async (folder) => {
+    await $`cd ${path.join(workingDirectory, folder)}`.quiet();
+    await $`cargo build-sbf ${argv._}`;
+  })
+);

+ 10 - 0
template/base/scripts/program/clean.mjs

@@ -0,0 +1,10 @@
+#!/usr/bin/env zx
+import "zx/globals";
+import { getExternalProgramOutputDir } from "../utils.mjs";
+
+// Remove the programs output directories.
+const externalProgramOutput = getExternalProgramOutputDir();
+await $`rm -rf ${externalProgramOutput}`;
+
+// Remove the target directory.
+await $`rm -rf target`;

+ 69 - 0
template/base/scripts/program/dump.mjs

@@ -0,0 +1,69 @@
+#!/usr/bin/env zx
+import "zx/globals";
+import {
+  getExternalProgramAddresses,
+  getExternalProgramOutputDir,
+} from "../utils.mjs";
+
+// Get input from environment variables.
+const rpc = process.env.RPC ?? "https://api.mainnet-beta.solana.com";
+const outputDir = getExternalProgramOutputDir();
+await dump();
+
+/** Dump external programs binaries if needed. */
+async function dump() {
+  // Ensure we have some external programs to dump.
+  const addresses = getExternalProgramAddresses();
+  if (addresses.length === 0) return;
+  echo(`Dumping external accounts to '${outputDir}':`);
+
+  // Create the output directory if needed.
+  $`mkdir -p ${outputDir}`.quiet();
+
+  // Copy the binaries from the chain or warn if they are different.
+  await Promise.all(
+    addresses.map(async (address) => {
+      const binary = `${address}.so`;
+      const hasBinary = await fs.exists(`${outputDir}/${binary}`);
+
+      if (!hasBinary) {
+        await copyFromChain(address, binary);
+        echo(`Wrote account data to ${outputDir}/${binary}`);
+        return;
+      }
+
+      await copyFromChain(address, `onchain-${binary}`);
+      const [onChainHash, localHash] = await Promise.all([
+        $`sha256sum -b ${outputDir}/onchain-${binary} | cut -d ' ' -f 1`.quiet(),
+        $`sha256sum -b ${outputDir}/${binary} | cut -d ' ' -f 1`.quiet(),
+      ]);
+
+      if (onChainHash.toString() !== localHash.toString()) {
+        echo(
+          chalk.yellow("[ WARNING ]"),
+          `on-chain and local binaries are different for '${binary}'`
+        );
+      } else {
+        echo(
+          chalk.green("[ SKIPPED ]"),
+          `on-chain and local binaries are the same for '${binary}'`
+        );
+      }
+
+      await $`rm ${outputDir}/onchain-${binary}`.quiet();
+    })
+  );
+}
+
+/** Helper function to copy external programs or accounts binaries from the chain. */
+async function copyFromChain(address, binary) {
+  switch (binary.split(".").pop()) {
+    case "bin":
+      return $`solana account -u ${rpc} ${address} -o ${outputDir}/${binary} >/dev/null`.quiet();
+    case "so":
+      return $`solana program dump -u ${rpc} ${address} ${outputDir}/${binary} >/dev/null`.quiet();
+    default:
+      echo(chalk.red(`[  ERROR  ] unknown account type for '${binary}'`));
+      await $`exit 1`;
+  }
+}

+ 11 - 0
template/base/scripts/program/format.mjs

@@ -0,0 +1,11 @@
+#!/usr/bin/env zx
+import "zx/globals";
+import { workingDirectory, getProgramFolders } from "../utils.mjs";
+
+// Format the programs.
+await Promise.all(
+  getProgramFolders().map(async (folder) => {
+    await $`cd ${path.join(workingDirectory, folder)}`.quiet();
+    await $`cargo fmt ${argv._}`;
+  })
+);

+ 11 - 0
template/base/scripts/program/lint.mjs

@@ -0,0 +1,11 @@
+#!/usr/bin/env zx
+import "zx/globals";
+import { workingDirectory, getProgramFolders } from "../utils.mjs";
+
+// Lint the programs using clippy.
+await Promise.all(
+  getProgramFolders().map(async (folder) => {
+    await $`cd ${path.join(workingDirectory, folder)}`.quiet();
+    await $`cargo clippy ${argv._}`;
+  })
+);

+ 20 - 0
template/base/scripts/program/test.mjs

@@ -0,0 +1,20 @@
+#!/usr/bin/env zx
+import "zx/globals";
+import { workingDirectory, getProgramFolders } from "../utils.mjs";
+
+// Save external programs binaries to the output directory.
+import "./dump.mjs";
+
+// Test the programs.
+await Promise.all(
+  getProgramFolders().map(async (folder) => {
+    await $`cd ${path.join(workingDirectory, folder)}`.quiet();
+    const hasSolfmt = await which("solfmt", { nothrow: true });
+
+    if (hasSolfmt) {
+      await $`RUST_LOG=error cargo test-sbf ${argv._} 2>&1 | solfmt`;
+    } else {
+      await $`RUST_LOG=error cargo test-sbf ${argv._}`;
+    }
+  })
+);

+ 66 - 0
template/base/scripts/utils.mjs

@@ -0,0 +1,66 @@
+import "zx/globals";
+import { parse as parseToml } from "@iarna/toml";
+
+process.env.FORCE_COLOR = 3;
+process.env.CARGO_TERM_COLOR = "always";
+
+export const workingDirectory = (await $`pwd`.quiet()).toString().trim();
+
+export function getAllProgramIdls() {
+  return getAllProgramFolders().map((folder) =>
+    path.join(workingDirectory, folder, "idl.json")
+  );
+}
+
+export function getExternalProgramOutputDir() {
+  const config =
+    getCargo().workspace?.metadata?.solana?.["external-programs-output"];
+  return path.join(workingDirectory, config ?? "target/deploy");
+}
+
+export function getExternalProgramAddresses() {
+  const addresses = getProgramFolders().flatMap(
+    (folder) =>
+      getCargo(folder).package?.metadata?.solana?.["program-dependencies"] ?? []
+  );
+  return Array.from(new Set(addresses));
+}
+
+let didWarnAboutMissingPrograms = false;
+export function getProgramFolders() {
+  const programs = process.env.PROGRAMS
+    ? process.env.PROGRAMS.split(/\s+/)
+    : getAllProgramFolders();
+  const filteredPrograms = programs.filter((program) =>
+    fs.existsSync(path.join(workingDirectory, program))
+  );
+
+  if (
+    filteredPrograms.length !== programs.length &&
+    !didWarnAboutMissingPrograms
+  ) {
+    didWarnAboutMissingPrograms = true;
+    programs
+      .filter((program) => !filteredPrograms.includes(program))
+      .forEach((program) => {
+        echo(chalk.yellow(`Program not found: ${workingDirectory}/${program}`));
+      });
+  }
+
+  return filteredPrograms;
+}
+
+export function getAllProgramFolders() {
+  return getCargo().workspace.members.filter((member) =>
+    (getCargo(member).lib?.["crate-type"] ?? []).includes("cdylib")
+  );
+}
+
+export function getCargo(folder) {
+  return parseToml(
+    fs.readFileSync(
+      path.join(workingDirectory, folder ? folder : ".", "Cargo.toml"),
+      "utf8"
+    )
+  );
+}

+ 11 - 0
template/clients/base/package.json

@@ -0,0 +1,11 @@
+{
+  "scripts": {
+    "generate": "pnpm generate:idls && pnpm generate:clients",
+    "generate:clients": "./scripts/generate-clients.mjs",
+    "validator": "./scripts/start-validator.mjs",
+    "validator:stop": "./scripts/stop-validator.mjs"
+  },
+  "devDependencies": {
+    "@metaplex-foundation/kinobi": "^0.17.4"
+  }
+}

+ 86 - 0
template/clients/base/scripts/generate-clients.mjs

@@ -0,0 +1,86 @@
+#!/usr/bin/env zx
+import "zx/globals";
+import * as k from "@metaplex-foundation/kinobi";
+import { getAllProgramIdls } from "./utils.mjs";
+
+// Instanciate Kinobi.
+const kinobi = k.createFromIdls(getAllProgramIdls());
+
+// Update accounts.
+kinobi.update(
+  k.updateAccountsVisitor({
+    addressLookupTable: {
+      seeds: [
+        k.variablePdaSeedNode(
+          "authority",
+          k.publicKeyTypeNode(),
+          "The address of the LUT's authority"
+        ),
+        k.variablePdaSeedNode(
+          "recentSlot",
+          k.numberTypeNode("u64"),
+          "The recent slot associated with the LUT"
+        ),
+      ],
+    },
+  })
+);
+
+// Update instructions.
+kinobi.update(
+  k.updateInstructionsVisitor({
+    createLookupTable: {
+      byteDeltas: [k.instructionByteDeltaNode(k.numberValueNode(56))],
+      accounts: {
+        address: { defaultValue: k.pdaValueNode("addressLookupTable") },
+        payer: { defaultValue: k.accountValueNode("authority") },
+      },
+      arguments: {
+        bump: { defaultValue: k.accountBumpValueNode("address") },
+      },
+    },
+    extendLookupTable: {
+      byteDeltas: [
+        k.instructionByteDeltaNode(
+          k.resolverValueNode("resolveExtendLookupTableBytes", {
+            dependsOn: [k.argumentValueNode("addresses")],
+          })
+        ),
+      ],
+    },
+  })
+);
+
+// Set account discriminators.
+kinobi.update(
+  k.setAccountDiscriminatorFromFieldVisitor({
+    addressLookupTable: { field: "discriminator", value: k.numberValueNode(1) },
+  })
+);
+
+// Set default values for structs.
+kinobi.update(
+  k.setStructDefaultValuesVisitor({
+    addressLookupTable: {
+      padding: { value: k.numberValueNode(0), strategy: "omitted" },
+    },
+  })
+);
+
+// Render JavaScript.
+const jsClient = path.join(__dirname, "..", "clients", "js");
+kinobi.accept(
+  k.renderJavaScriptExperimentalVisitor(
+    path.join(jsClient, "src", "generated"),
+    { prettier: require(path.join(jsClient, ".prettierrc.json")) }
+  )
+);
+
+// Render Rust.
+const rustClient = path.join(__dirname, "..", "clients", "rust");
+kinobi.accept(
+  k.renderRustVisitor(path.join(rustClient, "src", "generated"), {
+    formatCode: true,
+    crateFolder: rustClient,
+  })
+);

+ 100 - 0
template/clients/base/scripts/start-validator.mjs

@@ -0,0 +1,100 @@
+#!/usr/bin/env zx
+import { spawn } from "node:child_process";
+import fs from "node:fs";
+import "zx/globals";
+import {
+  getCargo,
+  getExternalProgramAddresses,
+  getExternalProgramOutputDir,
+  getProgramFolders,
+} from "./utils.mjs";
+
+// Options and arguments.
+const force = argv["force"];
+
+// Keep the validator running when not using the force flag.
+const isValidatorRunning = (await $`lsof -t -i:8899`.quiet().exitCode) === 0;
+if (!force && isValidatorRunning) {
+  echo(chalk.yellow("Local validator is already running."));
+  process.exit();
+}
+
+// Initial message.
+const verb = isValidatorRunning ? "Restarting" : "Starting";
+const programs = [...getPrograms(), ...getExternalPrograms()];
+const programPluralized = programs.length === 1 ? "program" : "programs";
+echo(
+  `${verb} local validator with ${programs.length} custom ${programPluralized}...`
+);
+
+// Kill the validator if it's already running.
+if (isValidatorRunning) {
+  await $`pkill -f solana-test-validator`.quiet();
+  await sleep(1000);
+}
+
+// Global validator arguments.
+const args = [/* Reset ledger */ "-r"];
+
+// Load programs.
+programs.forEach(({ programId, deployPath }) => {
+  args.push(/* Load BPF program */ "--bpf-program", programId, deployPath);
+});
+
+// Start the validator in detached mode.
+const cliLogs = path.join(os.tmpdir(), "validator-cli.log");
+fs.writeFileSync(cliLogs, "", () => {});
+const out = fs.openSync(cliLogs, "a");
+const err = fs.openSync(cliLogs, "a");
+const validator = spawn("solana-test-validator", args, {
+  detached: true,
+  stdio: ["ignore", out, err],
+});
+validator.unref();
+
+// Wait for the validator to stabilize.
+const waitForValidator = spinner(
+  "Waiting for local validator to stabilize...",
+  () =>
+    new Promise((resolve, reject) => {
+      setInterval(() => {
+        const logs = fs.readFileSync(cliLogs, "utf8");
+        if (validator.exitCode !== null) {
+          reject(logs);
+        } else if (logs.includes("Confirmed Slot: 1")) {
+          resolve();
+        }
+      }, 1000);
+    })
+);
+
+try {
+  await waitForValidator;
+  echo(chalk.green("Local validator is up and running!"));
+} catch (error) {
+  echo(error);
+  echo(chalk.red("Could not start local validator."));
+} finally {
+  fs.rmSync(cliLogs);
+  process.exit();
+}
+
+function getPrograms() {
+  const binaryDir = path.join(__dirname, "..", "target", "deploy");
+  return getProgramFolders().map((folder) => {
+    const cargo = getCargo(folder);
+    const name = cargo.package.name.replace(/-/g, "_");
+    return {
+      programId: cargo.package.metadata.solana["program-id"],
+      deployPath: path.join(binaryDir, `${name}.so`),
+    };
+  });
+}
+
+function getExternalPrograms() {
+  const binaryDir = getExternalProgramOutputDir();
+  return getExternalProgramAddresses().map((address) => ({
+    programId: address,
+    deployPath: path.join(binaryDir, `${address}.so`),
+  }));
+}

+ 13 - 0
template/clients/base/scripts/stop-validator.mjs

@@ -0,0 +1,13 @@
+#!/usr/bin/env zx
+import "zx/globals";
+
+const isValidatorRunning = (await $`lsof -t -i:8899`.quiet().exitCode) === 0;
+
+if (isValidatorRunning) {
+  // Kill the validator if it's already running.
+  await $`pkill -f solana-test-validator`.quiet();
+  await sleep(1000);
+  echo(chalk.green("Local validator terminated!"));
+} else {
+  echo(chalk.yellow("Local validator is not running."));
+}

+ 24 - 0
template/clients/js/clients/js/.eslintrc.js

@@ -0,0 +1,24 @@
+module.exports = {
+  extends: ['airbnb-base', 'airbnb-typescript/base', 'prettier'],
+  plugins: ['prettier'],
+  overrides: [],
+  parserOptions: {
+    ecmaVersion: 'latest',
+    sourceType: 'module',
+    project: 'tsconfig.json',
+    tsconfigRootDir: __dirname,
+  },
+  rules: {
+    '@typescript-eslint/no-use-before-define': 'off',
+    '@typescript-eslint/no-unused-vars': 'off',
+    'class-methods-use-this': 'off',
+    'import/prefer-default-export': 'off',
+    'import/no-cycle': 'off',
+    'no-underscore-dangle': 'off',
+    'max-classes-per-file': 'off',
+    'no-param-reassign': 'off',
+    'func-names': 'off',
+    'prefer-destructuring': 'off',
+  },
+  ignorePatterns: ['dist/**', '.eslintrc.js', 'tsup.config.ts', 'env-shim.ts'],
+};

+ 2 - 0
template/clients/js/clients/js/.gitignore

@@ -0,0 +1,2 @@
+.vercel
+docs

+ 10 - 0
template/clients/js/clients/js/.prettierrc.json

@@ -0,0 +1,10 @@
+{
+  "semi": true,
+  "singleQuote": true,
+  "trailingComma": "es5",
+  "useTabs": false,
+  "tabWidth": 2,
+  "arrowParens": "always",
+  "printWidth": 80,
+  "parser": "typescript"
+}

+ 3 - 0
template/clients/js/clients/js/README.md

@@ -0,0 +1,3 @@
+# JavaScript client
+
+A generated JavaScript library for the Counter program.

+ 3 - 0
template/clients/js/clients/js/env-shim.ts

@@ -0,0 +1,3 @@
+// Clever obfuscation to prevent the build system from inlining the value of `NODE_ENV`
+export const __DEV__ = /* @__PURE__ */ (() =>
+  (process as any)['en' + 'v'].NODE_ENV === 'development')();

+ 82 - 0
template/clients/js/clients/js/package.json

@@ -0,0 +1,82 @@
+{
+  "name": "@acme/counter",
+  "version": "0.1.0",
+  "description": "Counters on-chain",
+  "sideEffects": false,
+  "module": "dist/src/index.mjs",
+  "main": "dist/src/index.js",
+  "types": "dist/types/index.d.ts",
+  "exports": {
+    ".": {
+      "types": "./dist/src/index.d.ts",
+      "import": "./dist/src/index.mjs",
+      "require": "./dist/src/index.js"
+    }
+  },
+  "files": [
+    "./dist/src",
+    "./dist/types"
+  ],
+  "scripts": {
+    "build": "rimraf dist && tsup && tsc -p ./tsconfig.declarations.json",
+    "build:docs": "typedoc",
+    "test": "ava",
+    "lint": "eslint --ext js,ts,tsx src",
+    "lint:fix": "eslint --fix --ext js,ts,tsx src",
+    "format": "prettier --check src test",
+    "format:fix": "prettier --write src test"
+  },
+  "publishConfig": {
+    "access": "public",
+    "registry": "https://registry.npmjs.org"
+  },
+  "repository": "https://github.com/lorisleiva/kinobi-template.git",
+  "author": "Loris Leiva",
+  "license": "MIT",
+  "dependencies": {
+    "@solana/accounts": "2.0.0-experimental.a7a613a",
+    "@solana/addresses": "2.0.0-experimental.a7a613a",
+    "@solana/codecs-core": "2.0.0-experimental.a7a613a",
+    "@solana/codecs-data-structures": "2.0.0-experimental.a7a613a",
+    "@solana/codecs-numbers": "2.0.0-experimental.a7a613a",
+    "@solana/codecs-strings": "2.0.0-experimental.a7a613a",
+    "@solana/instructions": "2.0.0-experimental.a7a613a",
+    "@solana/keys": "2.0.0-experimental.a7a613a",
+    "@solana/options": "2.0.0-experimental.a7a613a",
+    "@solana/programs": "2.0.0-experimental.a7a613a",
+    "@solana/signers": "2.0.0-experimental.a7a613a",
+    "@solana/transactions": "2.0.0-experimental.a7a613a"
+  },
+  "devDependencies": {
+    "@ava/typescript": "^4.1.0",
+    "@solana/web3.js": "2.0.0-experimental.a7a613a",
+    "@solana/webcrypto-ed25519-polyfill": "2.0.0-experimental.a7a613a",
+    "@typescript-eslint/eslint-plugin": "^7.0.2",
+    "@typescript-eslint/parser": "^7.0.2",
+    "ava": "^6.1.1",
+    "eslint": "^8.0.1",
+    "eslint-config-airbnb-typescript": "^17.0.0",
+    "eslint-config-prettier": "^8.5.0",
+    "eslint-plugin-import": "^2.26.0",
+    "eslint-plugin-prettier": "^4.2.1",
+    "prettier": "^2.5.1",
+    "rimraf": "^5.0.5",
+    "tsup": "^8.0.2",
+    "typedoc": "^0.25.8",
+    "typedoc-plugin-expand-object-like-types": "^0.1.2",
+    "typedoc-plugin-missing-exports": "^2.2.0",
+    "typescript": "^5.3.3"
+  },
+  "ava": {
+    "require": [
+      "@solana/webcrypto-ed25519-polyfill"
+    ],
+    "typescript": {
+      "compile": false,
+      "rewritePaths": {
+        "test/": "dist/test/"
+      }
+    }
+  },
+  "packageManager": "pnpm@8.2.0"
+}

+ 3711 - 0
template/clients/js/clients/js/pnpm-lock.yaml

@@ -0,0 +1,3711 @@
+lockfileVersion: '6.0'
+
+settings:
+  autoInstallPeers: true
+  excludeLinksFromLockfile: false
+
+dependencies:
+  '@solana/accounts':
+    specifier: 2.0.0-experimental.a7a613a
+    version: 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+  '@solana/addresses':
+    specifier: 2.0.0-experimental.a7a613a
+    version: 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+  '@solana/codecs-core':
+    specifier: 2.0.0-experimental.a7a613a
+    version: 2.0.0-experimental.a7a613a
+  '@solana/codecs-data-structures':
+    specifier: 2.0.0-experimental.a7a613a
+    version: 2.0.0-experimental.a7a613a
+  '@solana/codecs-numbers':
+    specifier: 2.0.0-experimental.a7a613a
+    version: 2.0.0-experimental.a7a613a
+  '@solana/codecs-strings':
+    specifier: 2.0.0-experimental.a7a613a
+    version: 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+  '@solana/instructions':
+    specifier: 2.0.0-experimental.a7a613a
+    version: 2.0.0-experimental.a7a613a
+  '@solana/keys':
+    specifier: 2.0.0-experimental.a7a613a
+    version: 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+  '@solana/options':
+    specifier: 2.0.0-experimental.a7a613a
+    version: 2.0.0-experimental.a7a613a
+  '@solana/programs':
+    specifier: 2.0.0-experimental.a7a613a
+    version: 2.0.0-experimental.a7a613a
+  '@solana/signers':
+    specifier: 2.0.0-experimental.a7a613a
+    version: 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+  '@solana/transactions':
+    specifier: 2.0.0-experimental.a7a613a
+    version: 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+
+devDependencies:
+  '@ava/typescript':
+    specifier: ^4.1.0
+    version: 4.1.0
+  '@solana/web3.js':
+    specifier: 2.0.0-experimental.a7a613a
+    version: 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)(ws@8.14.2)
+  '@solana/webcrypto-ed25519-polyfill':
+    specifier: 2.0.0-experimental.a7a613a
+    version: 2.0.0-experimental.a7a613a
+  '@typescript-eslint/eslint-plugin':
+    specifier: ^7.0.2
+    version: 7.0.2(@typescript-eslint/parser@7.0.2)(eslint@8.30.0)(typescript@5.3.3)
+  '@typescript-eslint/parser':
+    specifier: ^7.0.2
+    version: 7.0.2(eslint@8.30.0)(typescript@5.3.3)
+  ava:
+    specifier: ^6.1.1
+    version: 6.1.1(@ava/typescript@4.1.0)
+  eslint:
+    specifier: ^8.0.1
+    version: 8.30.0
+  eslint-config-airbnb-typescript:
+    specifier: ^17.0.0
+    version: 17.0.0(@typescript-eslint/eslint-plugin@7.0.2)(@typescript-eslint/parser@7.0.2)(eslint-plugin-import@2.26.0)(eslint@8.30.0)
+  eslint-config-prettier:
+    specifier: ^8.5.0
+    version: 8.5.0(eslint@8.30.0)
+  eslint-plugin-import:
+    specifier: ^2.26.0
+    version: 2.26.0(@typescript-eslint/parser@7.0.2)(eslint@8.30.0)
+  eslint-plugin-prettier:
+    specifier: ^4.2.1
+    version: 4.2.1(eslint-config-prettier@8.5.0)(eslint@8.30.0)(prettier@2.8.1)
+  prettier:
+    specifier: ^2.5.1
+    version: 2.8.1
+  rimraf:
+    specifier: ^5.0.5
+    version: 5.0.5
+  tsup:
+    specifier: ^8.0.2
+    version: 8.0.2(typescript@5.3.3)
+  typedoc:
+    specifier: ^0.25.8
+    version: 0.25.8(typescript@5.3.3)
+  typedoc-plugin-expand-object-like-types:
+    specifier: ^0.1.2
+    version: 0.1.2(typedoc@0.25.8)
+  typedoc-plugin-missing-exports:
+    specifier: ^2.2.0
+    version: 2.2.0(typedoc@0.25.8)
+  typescript:
+    specifier: ^5.3.3
+    version: 5.3.3
+
+packages:
+
+  /@ava/typescript@4.1.0:
+    resolution: {integrity: sha512-1iWZQ/nr9iflhLK9VN8H+1oDZqe93qxNnyYUz+jTzkYPAHc5fdZXBrqmNIgIfFhWYXK5OaQ5YtC7OmLeTNhVEg==}
+    engines: {node: ^14.19 || ^16.15 || ^18 || ^20}
+    dependencies:
+      escape-string-regexp: 5.0.0
+      execa: 7.2.0
+    dev: true
+
+  /@esbuild/aix-ppc64@0.19.12:
+    resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [aix]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/android-arm64@0.19.12:
+    resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/android-arm@0.19.12:
+    resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/android-x64@0.19.12:
+    resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/darwin-arm64@0.19.12:
+    resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/darwin-x64@0.19.12:
+    resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/freebsd-arm64@0.19.12:
+    resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [freebsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/freebsd-x64@0.19.12:
+    resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [freebsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-arm64@0.19.12:
+    resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-arm@0.19.12:
+    resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-ia32@0.19.12:
+    resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-loong64@0.19.12:
+    resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==}
+    engines: {node: '>=12'}
+    cpu: [loong64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-mips64el@0.19.12:
+    resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==}
+    engines: {node: '>=12'}
+    cpu: [mips64el]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-ppc64@0.19.12:
+    resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-riscv64@0.19.12:
+    resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==}
+    engines: {node: '>=12'}
+    cpu: [riscv64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-s390x@0.19.12:
+    resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==}
+    engines: {node: '>=12'}
+    cpu: [s390x]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-x64@0.19.12:
+    resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/netbsd-x64@0.19.12:
+    resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [netbsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/openbsd-x64@0.19.12:
+    resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [openbsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/sunos-x64@0.19.12:
+    resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [sunos]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/win32-arm64@0.19.12:
+    resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/win32-ia32@0.19.12:
+    resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/win32-x64@0.19.12:
+    resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@eslint-community/eslint-utils@4.4.0(eslint@8.30.0):
+    resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+    dependencies:
+      eslint: 8.30.0
+      eslint-visitor-keys: 3.3.0
+    dev: true
+
+  /@eslint-community/regexpp@4.10.0:
+    resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==}
+    engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+    dev: true
+
+  /@eslint/eslintrc@1.4.0:
+    resolution: {integrity: sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dependencies:
+      ajv: 6.12.6
+      debug: 4.3.4
+      espree: 9.4.1
+      globals: 13.19.0
+      ignore: 5.2.4
+      import-fresh: 3.3.0
+      js-yaml: 4.1.0
+      minimatch: 3.1.2
+      strip-json-comments: 3.1.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@humanwhocodes/config-array@0.11.8:
+    resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==}
+    engines: {node: '>=10.10.0'}
+    dependencies:
+      '@humanwhocodes/object-schema': 1.2.1
+      debug: 4.3.4
+      minimatch: 3.1.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@humanwhocodes/module-importer@1.0.1:
+    resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+    engines: {node: '>=12.22'}
+    dev: true
+
+  /@humanwhocodes/object-schema@1.2.1:
+    resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
+    dev: true
+
+  /@isaacs/cliui@8.0.2:
+    resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
+    engines: {node: '>=12'}
+    dependencies:
+      string-width: 5.1.2
+      string-width-cjs: /string-width@4.2.3
+      strip-ansi: 7.0.1
+      strip-ansi-cjs: /strip-ansi@6.0.1
+      wrap-ansi: 8.1.0
+      wrap-ansi-cjs: /wrap-ansi@7.0.0
+    dev: true
+
+  /@jridgewell/gen-mapping@0.3.2:
+    resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      '@jridgewell/set-array': 1.1.2
+      '@jridgewell/sourcemap-codec': 1.4.14
+      '@jridgewell/trace-mapping': 0.3.17
+    dev: true
+
+  /@jridgewell/resolve-uri@3.1.0:
+    resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
+    engines: {node: '>=6.0.0'}
+    dev: true
+
+  /@jridgewell/set-array@1.1.2:
+    resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
+    engines: {node: '>=6.0.0'}
+    dev: true
+
+  /@jridgewell/sourcemap-codec@1.4.14:
+    resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
+    dev: true
+
+  /@jridgewell/trace-mapping@0.3.17:
+    resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
+    dependencies:
+      '@jridgewell/resolve-uri': 3.1.0
+      '@jridgewell/sourcemap-codec': 1.4.14
+    dev: true
+
+  /@mapbox/node-pre-gyp@1.0.10:
+    resolution: {integrity: sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==}
+    hasBin: true
+    dependencies:
+      detect-libc: 2.0.1
+      https-proxy-agent: 5.0.1
+      make-dir: 3.1.0
+      node-fetch: 2.6.7
+      nopt: 5.0.0
+      npmlog: 5.0.1
+      rimraf: 3.0.2
+      semver: 7.3.8
+      tar: 6.1.13
+    transitivePeerDependencies:
+      - encoding
+      - supports-color
+    dev: true
+
+  /@noble/ed25519@2.0.0:
+    resolution: {integrity: sha512-/extjhkwFupyopDrt80OMWKdLgP429qLZj+z6sYJz90rF2Iz0gjZh2ArMKPImUl13Kx+0EXI2hN9T/KJV0/Zng==}
+    dev: true
+
+  /@nodelib/fs.scandir@2.1.5:
+    resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+    engines: {node: '>= 8'}
+    dependencies:
+      '@nodelib/fs.stat': 2.0.5
+      run-parallel: 1.2.0
+    dev: true
+
+  /@nodelib/fs.stat@2.0.5:
+    resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+    engines: {node: '>= 8'}
+    dev: true
+
+  /@nodelib/fs.walk@1.2.8:
+    resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+    engines: {node: '>= 8'}
+    dependencies:
+      '@nodelib/fs.scandir': 2.1.5
+      fastq: 1.14.0
+    dev: true
+
+  /@pkgjs/parseargs@0.11.0:
+    resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
+    engines: {node: '>=14'}
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/pluginutils@4.2.1:
+    resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
+    engines: {node: '>= 8.0.0'}
+    dependencies:
+      estree-walker: 2.0.2
+      picomatch: 2.3.1
+    dev: true
+
+  /@rollup/rollup-android-arm-eabi@4.12.0:
+    resolution: {integrity: sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==}
+    cpu: [arm]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-android-arm64@4.12.0:
+    resolution: {integrity: sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==}
+    cpu: [arm64]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-darwin-arm64@4.12.0:
+    resolution: {integrity: sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==}
+    cpu: [arm64]
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-darwin-x64@4.12.0:
+    resolution: {integrity: sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==}
+    cpu: [x64]
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-arm-gnueabihf@4.12.0:
+    resolution: {integrity: sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==}
+    cpu: [arm]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-arm64-gnu@4.12.0:
+    resolution: {integrity: sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==}
+    cpu: [arm64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-arm64-musl@4.12.0:
+    resolution: {integrity: sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==}
+    cpu: [arm64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-riscv64-gnu@4.12.0:
+    resolution: {integrity: sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==}
+    cpu: [riscv64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-x64-gnu@4.12.0:
+    resolution: {integrity: sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==}
+    cpu: [x64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-x64-musl@4.12.0:
+    resolution: {integrity: sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==}
+    cpu: [x64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-win32-arm64-msvc@4.12.0:
+    resolution: {integrity: sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==}
+    cpu: [arm64]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-win32-ia32-msvc@4.12.0:
+    resolution: {integrity: sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==}
+    cpu: [ia32]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-win32-x64-msvc@4.12.0:
+    resolution: {integrity: sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==}
+    cpu: [x64]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@sindresorhus/merge-streams@2.3.0:
+    resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==}
+    engines: {node: '>=18'}
+    dev: true
+
+  /@solana/accounts@2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22):
+    resolution: {integrity: sha512-fifvhOC49UmCqooNI5inAnkkVfLc2diQ/R3WDQSocJ8u9E3chDFg+SPmwXRNNk3Prp1MnbJhsNpRPbO+7A/ypA==}
+    dependencies:
+      '@solana/addresses': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/codecs-core': 2.0.0-experimental.a7a613a
+      '@solana/codecs-strings': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/rpc-spec': 2.0.0-experimental.a7a613a
+      '@solana/rpc-types': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+
+  /@solana/addresses@2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22):
+    resolution: {integrity: sha512-W5lc7y9CQYwooshmZ6qr2T6Ymj6aVVEM0hlaQA6GVZ20PYCJOGSXg0mloNalG0Fp50J2ouE3Xhy5OoFjoYQgvQ==}
+    dependencies:
+      '@solana/assertions': 2.0.0-experimental.a7a613a
+      '@solana/codecs-core': 2.0.0-experimental.a7a613a
+      '@solana/codecs-strings': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+
+  /@solana/assertions@2.0.0-experimental.a7a613a:
+    resolution: {integrity: sha512-V3/Q4hkdRkaSiJE3FG7ELhgFnKC1WfnrkIm+6gUfhrzh5Vihg494HXV+x7xEOJFw5m3EYmTDiYEevIr9d5qvMg==}
+
+  /@solana/codecs-core@2.0.0-experimental.a7a613a:
+    resolution: {integrity: sha512-4C0GCWoIP8zvUd4kIs4QQnNupjh+GEXcoO+RAYRJ8nDlDW4DdEhb/W4weC+hoFaEjr0STEhCeH4yMWaKF3L6WQ==}
+
+  /@solana/codecs-data-structures@2.0.0-experimental.a7a613a:
+    resolution: {integrity: sha512-K22h6+YA8hVZ5Nq+49nFUIUJgOMxYP9zEt69oQxh/OnETZF538uIOrsCFU7MzLCLCwUnoJynkBY4Oycm+4m7OQ==}
+    dependencies:
+      '@solana/codecs-core': 2.0.0-experimental.a7a613a
+      '@solana/codecs-numbers': 2.0.0-experimental.a7a613a
+
+  /@solana/codecs-numbers@2.0.0-experimental.a7a613a:
+    resolution: {integrity: sha512-B1aNhPkoSRqMskHcgB/u0/tnAWfrp6dYHf9o36q+ppAoOZdbS9xz4rwzkt2FMUHaoSWPPa6/tHFQ0Vbge3VMRA==}
+    dependencies:
+      '@solana/codecs-core': 2.0.0-experimental.a7a613a
+
+  /@solana/codecs-strings@2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22):
+    resolution: {integrity: sha512-Tw9fqT8UzScpuP4bsDCiKb7DGXQ9BmsqvLJY7b1sjga9Vr0M3mA8BGHemz4WusHXb9AXJEN1Pc1NVYxd4rsH0w==}
+    peerDependencies:
+      fastestsmallesttextencoderdecoder: ^1.0.22
+    dependencies:
+      '@solana/codecs-core': 2.0.0-experimental.a7a613a
+      '@solana/codecs-numbers': 2.0.0-experimental.a7a613a
+      fastestsmallesttextencoderdecoder: 1.0.22
+
+  /@solana/codecs@2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22):
+    resolution: {integrity: sha512-9egdKvdYisL1pc4EXQJmikuIJISJFvmRkSFq147xSQoTg19J7wKikILqa39/jnthqE1NwyVgZgkv/h2ILoB/8A==}
+    dependencies:
+      '@solana/codecs-core': 2.0.0-experimental.a7a613a
+      '@solana/codecs-data-structures': 2.0.0-experimental.a7a613a
+      '@solana/codecs-numbers': 2.0.0-experimental.a7a613a
+      '@solana/codecs-strings': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/options': 2.0.0-experimental.a7a613a
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+    dev: true
+
+  /@solana/errors@2.0.0-experimental.a7a613a:
+    resolution: {integrity: sha512-B7rkUWtAqXgsQwgOAH2qRyy/SOZ1T/3ThC4YezmWwbjxNHqx7+kekoCRhyshkw5AdhV7U4kQJSACQq/j3Cy1WA==}
+    hasBin: true
+    dependencies:
+      chalk: 5.3.0
+      commander: 11.1.0
+
+  /@solana/functional@2.0.0-experimental.a7a613a:
+    resolution: {integrity: sha512-OkaYVw6O5vNzOjEut0E44mxF1yS6BMXCnUcZuKy9nvuHNeUFZ6BjFJhMQXOGhhs5qNzI9m+JHnrb7z3J0ZPrBw==}
+
+  /@solana/instructions@2.0.0-experimental.a7a613a:
+    resolution: {integrity: sha512-UJSlElg/w+pTYySxmane6RNP5Ghe52SJDy0gsEC40SkusdGtJbwlusNfGVZVFFGf14n+urWKyR+2gcdaj11qJw==}
+
+  /@solana/keys@2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22):
+    resolution: {integrity: sha512-A2Hf4IL7EH85yaadZMwu8dVMDzKLVRpIug8QjgKKiJRhu9FIezzbUDMT8l4ILBOpsRdcwgcK3/YFVRdVoMcWmA==}
+    dependencies:
+      '@solana/assertions': 2.0.0-experimental.a7a613a
+      '@solana/codecs-core': 2.0.0-experimental.a7a613a
+      '@solana/codecs-strings': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/errors': 2.0.0-experimental.a7a613a
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+
+  /@solana/options@2.0.0-experimental.a7a613a:
+    resolution: {integrity: sha512-gUV7uSOfHrOBLt3+Wv3LJDYkVQN1PTmNu511/zkkKdE3AwUMUfG7x+jMg85h3Tiw7xPs+HOgHBEyGdiYHk0W1g==}
+    dependencies:
+      '@solana/codecs-core': 2.0.0-experimental.a7a613a
+      '@solana/codecs-numbers': 2.0.0-experimental.a7a613a
+
+  /@solana/programs@2.0.0-experimental.a7a613a:
+    resolution: {integrity: sha512-1A6iwvlKmuJKZrL4+bTZUV1fh4aIHDqtCuQofRdenK46TJ0QqyTewEG3i6G1UuDQx2F6eKoFL3kC0QDK+7HUyw==}
+
+  /@solana/rpc-api@2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22):
+    resolution: {integrity: sha512-XSJwYDrVEbiuQHU6Xy17ZJFEkU/xz0cahzvHzrcKLGjYNjyV0vNKYHMoGAcVvorlDHxp/I52UdFO6zFjuao1EA==}
+    dependencies:
+      '@solana/addresses': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/codecs-core': 2.0.0-experimental.a7a613a
+      '@solana/codecs-strings': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/keys': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/rpc-parsed-types': 2.0.0-experimental.a7a613a
+      '@solana/rpc-spec': 2.0.0-experimental.a7a613a
+      '@solana/rpc-transformers': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/rpc-types': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/transactions': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+    dev: true
+
+  /@solana/rpc-parsed-types@2.0.0-experimental.a7a613a:
+    resolution: {integrity: sha512-3+M0MMy29fbZ1cI46pmsktM5G7XweaMIGddBqTF+2h6i22IS4CkhoSr74wPqoJ2YaXU3P3egkUNhNCbYmftCUQ==}
+    dev: true
+
+  /@solana/rpc-spec-types@2.0.0-experimental.a7a613a:
+    resolution: {integrity: sha512-lyZmEHac9WEL0Uf02X6sM7/vqGW6NAGV1fW3JRaLI9KOjMtN5OmRMiJiPhDKFHM/mBHGs0ddDwABBR0M1rNbzw==}
+
+  /@solana/rpc-spec@2.0.0-experimental.a7a613a:
+    resolution: {integrity: sha512-YfCbUxtSP/uVD+EPnM709JFF/LDBAUiLcosXLQZF62qSEDGWc15MelfXDHHqcSdUaPbxy6cCCqbVUhWaZt4ifQ==}
+    dependencies:
+      '@solana/rpc-spec-types': 2.0.0-experimental.a7a613a
+
+  /@solana/rpc-subscriptions-api@2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22):
+    resolution: {integrity: sha512-5G0hI+7IuZ/2/uze79EuCWGaJyk9e8NuYgq+WsY2tWKIkR1SYZJ+Gev9c/eDGrbwcFiP68HFHQ+1FYVGY+VBCA==}
+    dependencies:
+      '@solana/addresses': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/keys': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/rpc-subscriptions-spec': 2.0.0-experimental.a7a613a
+      '@solana/rpc-transformers': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/rpc-types': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/transactions': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+    dev: true
+
+  /@solana/rpc-subscriptions-spec@2.0.0-experimental.a7a613a:
+    resolution: {integrity: sha512-2yzQx6XezdC3vjnI/IqJLh0Doa6tITEND/+NGyP3Nw+knddY/KpWz8VQ9nPzMJ9SULBCRAa26qCtY13wwM7lSA==}
+    dependencies:
+      '@solana/rpc-spec-types': 2.0.0-experimental.a7a613a
+    dev: true
+
+  /@solana/rpc-subscriptions-transport-websocket@2.0.0-experimental.a7a613a(ws@8.14.2):
+    resolution: {integrity: sha512-wfT9WnSk4aCebWHgXQJ7j0U87Prlmaf7YvJxHqEL/tgk7YRArlJOjtbtak6RlYWkWJS7AMUYxkQvlJmIleSF8g==}
+    peerDependencies:
+      ws: ^8.14.0
+    dependencies:
+      '@solana/rpc-subscriptions-spec': 2.0.0-experimental.a7a613a
+      ws: 8.14.2
+    dev: true
+
+  /@solana/rpc-subscriptions@2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)(ws@8.14.2):
+    resolution: {integrity: sha512-p6AINhq2onETCTAUAC82R9s3mWLfQ4Cxpq5R8iRbGhsNSqYubnU0e+MzlxDS6fTocwm921VkkTbqsA5ztNm49g==}
+    dependencies:
+      '@solana/errors': 2.0.0-experimental.a7a613a
+      '@solana/functional': 2.0.0-experimental.a7a613a
+      '@solana/rpc-subscriptions-api': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/rpc-subscriptions-spec': 2.0.0-experimental.a7a613a
+      '@solana/rpc-subscriptions-transport-websocket': 2.0.0-experimental.a7a613a(ws@8.14.2)
+      '@solana/rpc-transformers': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/rpc-types': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      fast-stable-stringify: 1.0.0
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+      - ws
+    dev: true
+
+  /@solana/rpc-transformers@2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22):
+    resolution: {integrity: sha512-TBRk1VzP4m0ukKSiquYy47aBl3aUJvXTfDJK0c8mV0nRn0UuPmmSYDdxLagtJarcaP5UAa/+sZMi7gA/Ck68tg==}
+    dependencies:
+      '@solana/rpc-spec': 2.0.0-experimental.a7a613a
+      '@solana/rpc-subscriptions-spec': 2.0.0-experimental.a7a613a
+      '@solana/rpc-types': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+    dev: true
+
+  /@solana/rpc-transport-http@2.0.0-experimental.a7a613a:
+    resolution: {integrity: sha512-WLnpTN83gFm4n93nQwNJ2gWXvixLKx1Q9IIA5W9WpihKgokwo/Infmue1nQc1hbRNdaak9jEXWKM5L4Wxdn82w==}
+    dependencies:
+      '@solana/rpc-spec': 2.0.0-experimental.a7a613a
+    dev: true
+
+  /@solana/rpc-types@2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22):
+    resolution: {integrity: sha512-oICRh5Q4/5N/QRxea+xnUxLGFEp7jgRmJHqsR1eDhCUoSntwBL9ME3bdvsQh65gZ9DjKMUIU6FAPUzgmz/upWg==}
+    dependencies:
+      '@solana/addresses': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/codecs-strings': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+
+  /@solana/rpc@2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22):
+    resolution: {integrity: sha512-LM+BP3iQfdt1XxsAMPIJC7dZIItkqXDQnb5OM+kSfodNc3SfRUQFgk2DSkpd17GD2DsUVXqvScQ35cLo/WJXZw==}
+    dependencies:
+      '@solana/errors': 2.0.0-experimental.a7a613a
+      '@solana/functional': 2.0.0-experimental.a7a613a
+      '@solana/rpc-api': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/rpc-spec': 2.0.0-experimental.a7a613a
+      '@solana/rpc-transformers': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/rpc-transport-http': 2.0.0-experimental.a7a613a
+      '@solana/rpc-types': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      fast-stable-stringify: 1.0.0
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+    dev: true
+
+  /@solana/signers@2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22):
+    resolution: {integrity: sha512-2gnsMbDDnzcj63YiqkuNfcRIcWJEbuW2gwYfuypbby86g4etMhnYlsYwbRptb2BBd79ho0vBQfJco22D2UiCKw==}
+    dependencies:
+      '@solana/addresses': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/errors': 2.0.0-experimental.a7a613a
+      '@solana/instructions': 2.0.0-experimental.a7a613a
+      '@solana/keys': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/transactions': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+
+  /@solana/transaction-confirmation@2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)(ws@8.14.2):
+    resolution: {integrity: sha512-SwL7qEORrdsFnhpQJZW+6b2hwzZWCARnJXtnk/CGEmHI/7UHOCMPTxNaqH/usLwZC6uAO28X3N/Q2nnTomobGw==}
+    dependencies:
+      '@solana/addresses': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/codecs-strings': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/errors': 2.0.0-experimental.a7a613a
+      '@solana/keys': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/rpc': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/rpc-subscriptions': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)(ws@8.14.2)
+      '@solana/rpc-types': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/transactions': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+      - ws
+    dev: true
+
+  /@solana/transactions@2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22):
+    resolution: {integrity: sha512-DxxMzGEPGw0s3+AQGaqJqqFkcGWFJFuMB/z3U3cYMA5AmDZ3y51lGRsUonvdYw/ksQOGXawS6hrTeQ5VPu1yig==}
+    dependencies:
+      '@solana/addresses': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/codecs-core': 2.0.0-experimental.a7a613a
+      '@solana/codecs-data-structures': 2.0.0-experimental.a7a613a
+      '@solana/codecs-numbers': 2.0.0-experimental.a7a613a
+      '@solana/codecs-strings': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/errors': 2.0.0-experimental.a7a613a
+      '@solana/functional': 2.0.0-experimental.a7a613a
+      '@solana/keys': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+
+  /@solana/web3.js@2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)(ws@8.14.2):
+    resolution: {integrity: sha512-OxXxKoOJhSoKWgNoXEl7YmD1c1e5FG/lzgBGBtVZ7JxAuAWGCx7I1QvfJAXmu4IMcQ1HTvBMZonx+XwwqcoC2w==}
+    dependencies:
+      '@solana/accounts': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/addresses': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/codecs': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/errors': 2.0.0-experimental.a7a613a
+      '@solana/functional': 2.0.0-experimental.a7a613a
+      '@solana/instructions': 2.0.0-experimental.a7a613a
+      '@solana/keys': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/programs': 2.0.0-experimental.a7a613a
+      '@solana/rpc': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/rpc-parsed-types': 2.0.0-experimental.a7a613a
+      '@solana/rpc-subscriptions': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)(ws@8.14.2)
+      '@solana/rpc-types': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/signers': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+      '@solana/transaction-confirmation': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)(ws@8.14.2)
+      '@solana/transactions': 2.0.0-experimental.a7a613a(fastestsmallesttextencoderdecoder@1.0.22)
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+      - ws
+    dev: true
+
+  /@solana/webcrypto-ed25519-polyfill@2.0.0-experimental.a7a613a:
+    resolution: {integrity: sha512-ahmmIDa3BXKc1aehQ1CKuH47EI59cA/iamLk+sQdFScWdw627eM87RN4fn0F0LAsDwbzYj7+tRsgDMg9cglBRg==}
+    dependencies:
+      '@noble/ed25519': 2.0.0
+    dev: true
+
+  /@types/estree@1.0.5:
+    resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
+    dev: true
+
+  /@types/json-schema@7.0.15:
+    resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+    dev: true
+
+  /@types/json5@0.0.29:
+    resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
+    dev: true
+
+  /@types/semver@7.5.7:
+    resolution: {integrity: sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==}
+    dev: true
+
+  /@typescript-eslint/eslint-plugin@7.0.2(@typescript-eslint/parser@7.0.2)(eslint@8.30.0)(typescript@5.3.3):
+    resolution: {integrity: sha512-/XtVZJtbaphtdrWjr+CJclaCVGPtOdBpFEnvtNf/jRV0IiEemRrL0qABex/nEt8isYcnFacm3nPHYQwL+Wb7qg==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    peerDependencies:
+      '@typescript-eslint/parser': ^7.0.0
+      eslint: ^8.56.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@eslint-community/regexpp': 4.10.0
+      '@typescript-eslint/parser': 7.0.2(eslint@8.30.0)(typescript@5.3.3)
+      '@typescript-eslint/scope-manager': 7.0.2
+      '@typescript-eslint/type-utils': 7.0.2(eslint@8.30.0)(typescript@5.3.3)
+      '@typescript-eslint/utils': 7.0.2(eslint@8.30.0)(typescript@5.3.3)
+      '@typescript-eslint/visitor-keys': 7.0.2
+      debug: 4.3.4
+      eslint: 8.30.0
+      graphemer: 1.4.0
+      ignore: 5.2.4
+      natural-compare: 1.4.0
+      semver: 7.6.0
+      ts-api-utils: 1.2.1(typescript@5.3.3)
+      typescript: 5.3.3
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@typescript-eslint/parser@7.0.2(eslint@8.30.0)(typescript@5.3.3):
+    resolution: {integrity: sha512-GdwfDglCxSmU+QTS9vhz2Sop46ebNCXpPPvsByK7hu0rFGRHL+AusKQJ7SoN+LbLh6APFpQwHKmDSwN35Z700Q==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    peerDependencies:
+      eslint: ^8.56.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@typescript-eslint/scope-manager': 7.0.2
+      '@typescript-eslint/types': 7.0.2
+      '@typescript-eslint/typescript-estree': 7.0.2(typescript@5.3.3)
+      '@typescript-eslint/visitor-keys': 7.0.2
+      debug: 4.3.4
+      eslint: 8.30.0
+      typescript: 5.3.3
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@typescript-eslint/scope-manager@7.0.2:
+    resolution: {integrity: sha512-l6sa2jF3h+qgN2qUMjVR3uCNGjWw4ahGfzIYsCtFrQJCjhbrDPdiihYT8FnnqFwsWX+20hK592yX9I2rxKTP4g==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    dependencies:
+      '@typescript-eslint/types': 7.0.2
+      '@typescript-eslint/visitor-keys': 7.0.2
+    dev: true
+
+  /@typescript-eslint/type-utils@7.0.2(eslint@8.30.0)(typescript@5.3.3):
+    resolution: {integrity: sha512-IKKDcFsKAYlk8Rs4wiFfEwJTQlHcdn8CLwLaxwd6zb8HNiMcQIFX9sWax2k4Cjj7l7mGS5N1zl7RCHOVwHq2VQ==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    peerDependencies:
+      eslint: ^8.56.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@typescript-eslint/typescript-estree': 7.0.2(typescript@5.3.3)
+      '@typescript-eslint/utils': 7.0.2(eslint@8.30.0)(typescript@5.3.3)
+      debug: 4.3.4
+      eslint: 8.30.0
+      ts-api-utils: 1.2.1(typescript@5.3.3)
+      typescript: 5.3.3
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@typescript-eslint/types@7.0.2:
+    resolution: {integrity: sha512-ZzcCQHj4JaXFjdOql6adYV4B/oFOFjPOC9XYwCaZFRvqN8Llfvv4gSxrkQkd2u4Ci62i2c6W6gkDwQJDaRc4nA==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    dev: true
+
+  /@typescript-eslint/typescript-estree@7.0.2(typescript@5.3.3):
+    resolution: {integrity: sha512-3AMc8khTcELFWcKcPc0xiLviEvvfzATpdPj/DXuOGIdQIIFybf4DMT1vKRbuAEOFMwhWt7NFLXRkbjsvKZQyvw==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@typescript-eslint/types': 7.0.2
+      '@typescript-eslint/visitor-keys': 7.0.2
+      debug: 4.3.4
+      globby: 11.1.0
+      is-glob: 4.0.3
+      minimatch: 9.0.3
+      semver: 7.6.0
+      ts-api-utils: 1.2.1(typescript@5.3.3)
+      typescript: 5.3.3
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@typescript-eslint/utils@7.0.2(eslint@8.30.0)(typescript@5.3.3):
+    resolution: {integrity: sha512-PZPIONBIB/X684bhT1XlrkjNZJIEevwkKDsdwfiu1WeqBxYEEdIgVDgm8/bbKHVu+6YOpeRqcfImTdImx/4Bsw==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    peerDependencies:
+      eslint: ^8.56.0
+    dependencies:
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.30.0)
+      '@types/json-schema': 7.0.15
+      '@types/semver': 7.5.7
+      '@typescript-eslint/scope-manager': 7.0.2
+      '@typescript-eslint/types': 7.0.2
+      '@typescript-eslint/typescript-estree': 7.0.2(typescript@5.3.3)
+      eslint: 8.30.0
+      semver: 7.6.0
+    transitivePeerDependencies:
+      - supports-color
+      - typescript
+    dev: true
+
+  /@typescript-eslint/visitor-keys@7.0.2:
+    resolution: {integrity: sha512-8Y+YiBmqPighbm5xA2k4wKTxRzx9EkBu7Rlw+WHqMvRJ3RPz/BMBO9b2ru0LUNmXg120PHUXD5+SWFy2R8DqlQ==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    dependencies:
+      '@typescript-eslint/types': 7.0.2
+      eslint-visitor-keys: 3.4.3
+    dev: true
+
+  /@vercel/nft@0.26.4:
+    resolution: {integrity: sha512-j4jCOOXke2t8cHZCIxu1dzKLHLcFmYzC3yqAK6MfZznOL1QIJKd0xcFsXK3zcqzU7ScsE2zWkiMMNHGMHgp+FA==}
+    engines: {node: '>=16'}
+    hasBin: true
+    dependencies:
+      '@mapbox/node-pre-gyp': 1.0.10
+      '@rollup/pluginutils': 4.2.1
+      acorn: 8.11.3
+      acorn-import-attributes: 1.9.2(acorn@8.11.3)
+      async-sema: 3.1.1
+      bindings: 1.5.0
+      estree-walker: 2.0.2
+      glob: 7.2.3
+      graceful-fs: 4.2.10
+      micromatch: 4.0.5
+      node-gyp-build: 4.6.0
+      resolve-from: 5.0.0
+    transitivePeerDependencies:
+      - encoding
+      - supports-color
+    dev: true
+
+  /abbrev@1.1.1:
+    resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
+    dev: true
+
+  /acorn-import-attributes@1.9.2(acorn@8.11.3):
+    resolution: {integrity: sha512-O+nfJwNolEA771IYJaiLWK1UAwjNsQmZbTRqqwBYxCgVQTmpFEMvBw6LOIQV0Me339L5UMVYFyRohGnGlQDdIQ==}
+    peerDependencies:
+      acorn: ^8
+    dependencies:
+      acorn: 8.11.3
+    dev: true
+
+  /acorn-jsx@5.3.2(acorn@8.8.1):
+    resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+    peerDependencies:
+      acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+    dependencies:
+      acorn: 8.8.1
+    dev: true
+
+  /acorn-walk@8.3.2:
+    resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==}
+    engines: {node: '>=0.4.0'}
+    dev: true
+
+  /acorn@8.11.3:
+    resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+    dev: true
+
+  /acorn@8.8.1:
+    resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+    dev: true
+
+  /agent-base@6.0.2:
+    resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
+    engines: {node: '>= 6.0.0'}
+    dependencies:
+      debug: 4.3.4
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /ajv@6.12.6:
+    resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+    dependencies:
+      fast-deep-equal: 3.1.3
+      fast-json-stable-stringify: 2.1.0
+      json-schema-traverse: 0.4.1
+      uri-js: 4.4.1
+    dev: true
+
+  /ansi-regex@5.0.1:
+    resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /ansi-regex@6.0.1:
+    resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /ansi-sequence-parser@1.1.1:
+    resolution: {integrity: sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==}
+    dev: true
+
+  /ansi-styles@4.3.0:
+    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+    engines: {node: '>=8'}
+    dependencies:
+      color-convert: 2.0.1
+    dev: true
+
+  /ansi-styles@6.2.1:
+    resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /any-promise@1.3.0:
+    resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
+    dev: true
+
+  /anymatch@3.1.3:
+    resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+    engines: {node: '>= 8'}
+    dependencies:
+      normalize-path: 3.0.0
+      picomatch: 2.3.1
+    dev: true
+
+  /aproba@2.0.0:
+    resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
+    dev: true
+
+  /are-we-there-yet@2.0.0:
+    resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==}
+    engines: {node: '>=10'}
+    dependencies:
+      delegates: 1.0.0
+      readable-stream: 3.6.0
+    dev: true
+
+  /argparse@1.0.10:
+    resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
+    dependencies:
+      sprintf-js: 1.0.3
+    dev: true
+
+  /argparse@2.0.1:
+    resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+    dev: true
+
+  /array-find-index@1.0.2:
+    resolution: {integrity: sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /array-includes@3.1.6:
+    resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      call-bind: 1.0.2
+      define-properties: 1.1.4
+      es-abstract: 1.20.5
+      get-intrinsic: 1.1.3
+      is-string: 1.0.7
+    dev: true
+
+  /array-union@2.1.0:
+    resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /array.prototype.flat@1.3.1:
+    resolution: {integrity: sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      call-bind: 1.0.2
+      define-properties: 1.1.4
+      es-abstract: 1.20.5
+      es-shim-unscopables: 1.0.0
+    dev: true
+
+  /arrgv@1.0.2:
+    resolution: {integrity: sha512-a4eg4yhp7mmruZDQFqVMlxNRFGi/i1r87pt8SDHy0/I8PqSXoUTlWZRdAZo0VXgvEARcujbtTk8kiZRi1uDGRw==}
+    engines: {node: '>=8.0.0'}
+    dev: true
+
+  /arrify@3.0.0:
+    resolution: {integrity: sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /async-sema@3.1.1:
+    resolution: {integrity: sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==}
+    dev: true
+
+  /ava@6.1.1(@ava/typescript@4.1.0):
+    resolution: {integrity: sha512-A+DG0Ag0e5zvt262Ze0pG5QH7EBmhn+DB9uK7WkUtJVAtGjZFeKTpUOKx339DMGn53+FB24pCJC5klX2WU4VOw==}
+    engines: {node: ^18.18 || ^20.8 || ^21}
+    hasBin: true
+    peerDependencies:
+      '@ava/typescript': '*'
+    peerDependenciesMeta:
+      '@ava/typescript':
+        optional: true
+    dependencies:
+      '@ava/typescript': 4.1.0
+      '@vercel/nft': 0.26.4
+      acorn: 8.11.3
+      acorn-walk: 8.3.2
+      ansi-styles: 6.2.1
+      arrgv: 1.0.2
+      arrify: 3.0.0
+      callsites: 4.1.0
+      cbor: 9.0.2
+      chalk: 5.3.0
+      chunkd: 2.0.1
+      ci-info: 4.0.0
+      ci-parallel-vars: 1.0.1
+      cli-truncate: 4.0.0
+      code-excerpt: 4.0.0
+      common-path-prefix: 3.0.0
+      concordance: 5.0.4
+      currently-unhandled: 0.4.1
+      debug: 4.3.4
+      emittery: 1.0.1
+      figures: 6.0.1
+      globby: 14.0.1
+      ignore-by-default: 2.1.0
+      indent-string: 5.0.0
+      is-plain-object: 5.0.0
+      is-promise: 4.0.0
+      matcher: 5.0.0
+      memoize: 10.0.0
+      ms: 2.1.3
+      p-map: 7.0.1
+      package-config: 5.0.0
+      picomatch: 3.0.1
+      plur: 5.1.0
+      pretty-ms: 9.0.0
+      resolve-cwd: 3.0.0
+      stack-utils: 2.0.6
+      strip-ansi: 7.1.0
+      supertap: 3.0.1
+      temp-dir: 3.0.0
+      write-file-atomic: 5.0.1
+      yargs: 17.7.2
+    transitivePeerDependencies:
+      - encoding
+      - supports-color
+    dev: true
+
+  /balanced-match@1.0.2:
+    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+    dev: true
+
+  /binary-extensions@2.2.0:
+    resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /bindings@1.5.0:
+    resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
+    dependencies:
+      file-uri-to-path: 1.0.0
+    dev: true
+
+  /blueimp-md5@2.19.0:
+    resolution: {integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==}
+    dev: true
+
+  /brace-expansion@1.1.11:
+    resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+    dependencies:
+      balanced-match: 1.0.2
+      concat-map: 0.0.1
+    dev: true
+
+  /brace-expansion@2.0.1:
+    resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
+    dependencies:
+      balanced-match: 1.0.2
+    dev: true
+
+  /braces@3.0.2:
+    resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
+    engines: {node: '>=8'}
+    dependencies:
+      fill-range: 7.0.1
+    dev: true
+
+  /bundle-require@4.0.2(esbuild@0.19.12):
+    resolution: {integrity: sha512-jwzPOChofl67PSTW2SGubV9HBQAhhR2i6nskiOThauo9dzwDUgOWQScFVaJkjEfYX+UXiD+LEx8EblQMc2wIag==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    peerDependencies:
+      esbuild: '>=0.17'
+    dependencies:
+      esbuild: 0.19.12
+      load-tsconfig: 0.2.5
+    dev: true
+
+  /cac@6.7.14:
+    resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /call-bind@1.0.2:
+    resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
+    dependencies:
+      function-bind: 1.1.1
+      get-intrinsic: 1.1.3
+    dev: true
+
+  /callsites@3.1.0:
+    resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /callsites@4.1.0:
+    resolution: {integrity: sha512-aBMbD1Xxay75ViYezwT40aQONfr+pSXTHwNKvIXhXD6+LY3F1dLIcceoC5OZKBVHbXcysz1hL9D2w0JJIMXpUw==}
+    engines: {node: '>=12.20'}
+    dev: true
+
+  /cbor@9.0.2:
+    resolution: {integrity: sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==}
+    engines: {node: '>=16'}
+    dependencies:
+      nofilter: 3.1.0
+    dev: true
+
+  /chalk@4.1.2:
+    resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+    engines: {node: '>=10'}
+    dependencies:
+      ansi-styles: 4.3.0
+      supports-color: 7.2.0
+    dev: true
+
+  /chalk@5.3.0:
+    resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==}
+    engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
+
+  /chokidar@3.5.3:
+    resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
+    engines: {node: '>= 8.10.0'}
+    dependencies:
+      anymatch: 3.1.3
+      braces: 3.0.2
+      glob-parent: 5.1.2
+      is-binary-path: 2.1.0
+      is-glob: 4.0.3
+      normalize-path: 3.0.0
+      readdirp: 3.6.0
+    optionalDependencies:
+      fsevents: 2.3.2
+    dev: true
+
+  /chownr@2.0.0:
+    resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /chunkd@2.0.1:
+    resolution: {integrity: sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ==}
+    dev: true
+
+  /ci-info@4.0.0:
+    resolution: {integrity: sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /ci-parallel-vars@1.0.1:
+    resolution: {integrity: sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg==}
+    dev: true
+
+  /cli-truncate@4.0.0:
+    resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==}
+    engines: {node: '>=18'}
+    dependencies:
+      slice-ansi: 5.0.0
+      string-width: 7.1.0
+    dev: true
+
+  /cliui@8.0.1:
+    resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
+    engines: {node: '>=12'}
+    dependencies:
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+      wrap-ansi: 7.0.0
+    dev: true
+
+  /code-excerpt@4.0.0:
+    resolution: {integrity: sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    dependencies:
+      convert-to-spaces: 2.0.1
+    dev: true
+
+  /color-convert@2.0.1:
+    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+    engines: {node: '>=7.0.0'}
+    dependencies:
+      color-name: 1.1.4
+    dev: true
+
+  /color-name@1.1.4:
+    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+    dev: true
+
+  /color-support@1.1.3:
+    resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
+    hasBin: true
+    dev: true
+
+  /commander@11.1.0:
+    resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==}
+    engines: {node: '>=16'}
+
+  /commander@4.1.1:
+    resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
+    engines: {node: '>= 6'}
+    dev: true
+
+  /common-path-prefix@3.0.0:
+    resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==}
+    dev: true
+
+  /concat-map@0.0.1:
+    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+    dev: true
+
+  /concordance@5.0.4:
+    resolution: {integrity: sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==}
+    engines: {node: '>=10.18.0 <11 || >=12.14.0 <13 || >=14'}
+    dependencies:
+      date-time: 3.1.0
+      esutils: 2.0.3
+      fast-diff: 1.2.0
+      js-string-escape: 1.0.1
+      lodash: 4.17.21
+      md5-hex: 3.0.1
+      semver: 7.3.8
+      well-known-symbols: 2.0.0
+    dev: true
+
+  /confusing-browser-globals@1.0.11:
+    resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==}
+    dev: true
+
+  /console-control-strings@1.1.0:
+    resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
+    dev: true
+
+  /convert-to-spaces@2.0.1:
+    resolution: {integrity: sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    dev: true
+
+  /cross-spawn@7.0.3:
+    resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
+    engines: {node: '>= 8'}
+    dependencies:
+      path-key: 3.1.1
+      shebang-command: 2.0.0
+      which: 2.0.2
+    dev: true
+
+  /currently-unhandled@0.4.1:
+    resolution: {integrity: sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      array-find-index: 1.0.2
+    dev: true
+
+  /date-time@3.1.0:
+    resolution: {integrity: sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==}
+    engines: {node: '>=6'}
+    dependencies:
+      time-zone: 1.0.0
+    dev: true
+
+  /debug@2.6.9:
+    resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+    dependencies:
+      ms: 2.0.0
+    dev: true
+
+  /debug@3.2.7:
+    resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+    dependencies:
+      ms: 2.1.2
+    dev: true
+
+  /debug@4.3.4:
+    resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
+    engines: {node: '>=6.0'}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+    dependencies:
+      ms: 2.1.2
+    dev: true
+
+  /deep-is@0.1.4:
+    resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+    dev: true
+
+  /define-properties@1.1.4:
+    resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      has-property-descriptors: 1.0.0
+      object-keys: 1.1.1
+    dev: true
+
+  /delegates@1.0.0:
+    resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
+    dev: true
+
+  /detect-libc@2.0.1:
+    resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /dir-glob@3.0.1:
+    resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
+    engines: {node: '>=8'}
+    dependencies:
+      path-type: 4.0.0
+    dev: true
+
+  /doctrine@2.1.0:
+    resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      esutils: 2.0.3
+    dev: true
+
+  /doctrine@3.0.0:
+    resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      esutils: 2.0.3
+    dev: true
+
+  /eastasianwidth@0.2.0:
+    resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+    dev: true
+
+  /emittery@1.0.1:
+    resolution: {integrity: sha512-2ID6FdrMD9KDLldGesP6317G78K7km/kMcwItRtVFva7I/cSEOIaLpewaUb+YLXVwdAp3Ctfxh/V5zIl1sj7dQ==}
+    engines: {node: '>=14.16'}
+    dev: true
+
+  /emoji-regex@10.3.0:
+    resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==}
+    dev: true
+
+  /emoji-regex@8.0.0:
+    resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+    dev: true
+
+  /emoji-regex@9.2.2:
+    resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+    dev: true
+
+  /es-abstract@1.20.5:
+    resolution: {integrity: sha512-7h8MM2EQhsCA7pU/Nv78qOXFpD8Rhqd12gYiSJVkrH9+e8VuA8JlPJK/hQjjlLv6pJvx/z1iRFKzYb0XT/RuAQ==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      call-bind: 1.0.2
+      es-to-primitive: 1.2.1
+      function-bind: 1.1.1
+      function.prototype.name: 1.1.5
+      get-intrinsic: 1.1.3
+      get-symbol-description: 1.0.0
+      gopd: 1.0.1
+      has: 1.0.3
+      has-property-descriptors: 1.0.0
+      has-symbols: 1.0.3
+      internal-slot: 1.0.4
+      is-callable: 1.2.7
+      is-negative-zero: 2.0.2
+      is-regex: 1.1.4
+      is-shared-array-buffer: 1.0.2
+      is-string: 1.0.7
+      is-weakref: 1.0.2
+      object-inspect: 1.12.2
+      object-keys: 1.1.1
+      object.assign: 4.1.4
+      regexp.prototype.flags: 1.4.3
+      safe-regex-test: 1.0.0
+      string.prototype.trimend: 1.0.6
+      string.prototype.trimstart: 1.0.6
+      unbox-primitive: 1.0.2
+    dev: true
+
+  /es-shim-unscopables@1.0.0:
+    resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==}
+    dependencies:
+      has: 1.0.3
+    dev: true
+
+  /es-to-primitive@1.2.1:
+    resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      is-callable: 1.2.7
+      is-date-object: 1.0.5
+      is-symbol: 1.0.4
+    dev: true
+
+  /esbuild@0.19.12:
+    resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==}
+    engines: {node: '>=12'}
+    hasBin: true
+    requiresBuild: true
+    optionalDependencies:
+      '@esbuild/aix-ppc64': 0.19.12
+      '@esbuild/android-arm': 0.19.12
+      '@esbuild/android-arm64': 0.19.12
+      '@esbuild/android-x64': 0.19.12
+      '@esbuild/darwin-arm64': 0.19.12
+      '@esbuild/darwin-x64': 0.19.12
+      '@esbuild/freebsd-arm64': 0.19.12
+      '@esbuild/freebsd-x64': 0.19.12
+      '@esbuild/linux-arm': 0.19.12
+      '@esbuild/linux-arm64': 0.19.12
+      '@esbuild/linux-ia32': 0.19.12
+      '@esbuild/linux-loong64': 0.19.12
+      '@esbuild/linux-mips64el': 0.19.12
+      '@esbuild/linux-ppc64': 0.19.12
+      '@esbuild/linux-riscv64': 0.19.12
+      '@esbuild/linux-s390x': 0.19.12
+      '@esbuild/linux-x64': 0.19.12
+      '@esbuild/netbsd-x64': 0.19.12
+      '@esbuild/openbsd-x64': 0.19.12
+      '@esbuild/sunos-x64': 0.19.12
+      '@esbuild/win32-arm64': 0.19.12
+      '@esbuild/win32-ia32': 0.19.12
+      '@esbuild/win32-x64': 0.19.12
+    dev: true
+
+  /escalade@3.1.1:
+    resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /escape-string-regexp@2.0.0:
+    resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /escape-string-regexp@4.0.0:
+    resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /escape-string-regexp@5.0.0:
+    resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.26.0)(eslint@8.30.0):
+    resolution: {integrity: sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==}
+    engines: {node: ^10.12.0 || >=12.0.0}
+    peerDependencies:
+      eslint: ^7.32.0 || ^8.2.0
+      eslint-plugin-import: ^2.25.2
+    dependencies:
+      confusing-browser-globals: 1.0.11
+      eslint: 8.30.0
+      eslint-plugin-import: 2.26.0(@typescript-eslint/parser@7.0.2)(eslint@8.30.0)
+      object.assign: 4.1.4
+      object.entries: 1.1.6
+      semver: 6.3.0
+    dev: true
+
+  /eslint-config-airbnb-typescript@17.0.0(@typescript-eslint/eslint-plugin@7.0.2)(@typescript-eslint/parser@7.0.2)(eslint-plugin-import@2.26.0)(eslint@8.30.0):
+    resolution: {integrity: sha512-elNiuzD0kPAPTXjFWg+lE24nMdHMtuxgYoD30OyMD6yrW1AhFZPAg27VX7d3tzOErw+dgJTNWfRSDqEcXb4V0g==}
+    peerDependencies:
+      '@typescript-eslint/eslint-plugin': ^5.13.0
+      '@typescript-eslint/parser': ^5.0.0
+      eslint: ^7.32.0 || ^8.2.0
+      eslint-plugin-import: ^2.25.3
+    dependencies:
+      '@typescript-eslint/eslint-plugin': 7.0.2(@typescript-eslint/parser@7.0.2)(eslint@8.30.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 7.0.2(eslint@8.30.0)(typescript@5.3.3)
+      eslint: 8.30.0
+      eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.26.0)(eslint@8.30.0)
+      eslint-plugin-import: 2.26.0(@typescript-eslint/parser@7.0.2)(eslint@8.30.0)
+    dev: true
+
+  /eslint-config-prettier@8.5.0(eslint@8.30.0):
+    resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==}
+    hasBin: true
+    peerDependencies:
+      eslint: '>=7.0.0'
+    dependencies:
+      eslint: 8.30.0
+    dev: true
+
+  /eslint-import-resolver-node@0.3.6:
+    resolution: {integrity: sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==}
+    dependencies:
+      debug: 3.2.7
+      resolve: 1.22.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /eslint-module-utils@2.7.4(@typescript-eslint/parser@7.0.2)(eslint-import-resolver-node@0.3.6)(eslint@8.30.0):
+    resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==}
+    engines: {node: '>=4'}
+    peerDependencies:
+      '@typescript-eslint/parser': '*'
+      eslint: '*'
+      eslint-import-resolver-node: '*'
+      eslint-import-resolver-typescript: '*'
+      eslint-import-resolver-webpack: '*'
+    peerDependenciesMeta:
+      '@typescript-eslint/parser':
+        optional: true
+      eslint:
+        optional: true
+      eslint-import-resolver-node:
+        optional: true
+      eslint-import-resolver-typescript:
+        optional: true
+      eslint-import-resolver-webpack:
+        optional: true
+    dependencies:
+      '@typescript-eslint/parser': 7.0.2(eslint@8.30.0)(typescript@5.3.3)
+      debug: 3.2.7
+      eslint: 8.30.0
+      eslint-import-resolver-node: 0.3.6
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /eslint-plugin-import@2.26.0(@typescript-eslint/parser@7.0.2)(eslint@8.30.0):
+    resolution: {integrity: sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==}
+    engines: {node: '>=4'}
+    peerDependencies:
+      '@typescript-eslint/parser': '*'
+      eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8
+    peerDependenciesMeta:
+      '@typescript-eslint/parser':
+        optional: true
+    dependencies:
+      '@typescript-eslint/parser': 7.0.2(eslint@8.30.0)(typescript@5.3.3)
+      array-includes: 3.1.6
+      array.prototype.flat: 1.3.1
+      debug: 2.6.9
+      doctrine: 2.1.0
+      eslint: 8.30.0
+      eslint-import-resolver-node: 0.3.6
+      eslint-module-utils: 2.7.4(@typescript-eslint/parser@7.0.2)(eslint-import-resolver-node@0.3.6)(eslint@8.30.0)
+      has: 1.0.3
+      is-core-module: 2.11.0
+      is-glob: 4.0.3
+      minimatch: 3.1.2
+      object.values: 1.1.6
+      resolve: 1.22.1
+      tsconfig-paths: 3.14.1
+    transitivePeerDependencies:
+      - eslint-import-resolver-typescript
+      - eslint-import-resolver-webpack
+      - supports-color
+    dev: true
+
+  /eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.5.0)(eslint@8.30.0)(prettier@2.8.1):
+    resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==}
+    engines: {node: '>=12.0.0'}
+    peerDependencies:
+      eslint: '>=7.28.0'
+      eslint-config-prettier: '*'
+      prettier: '>=2.0.0'
+    peerDependenciesMeta:
+      eslint-config-prettier:
+        optional: true
+    dependencies:
+      eslint: 8.30.0
+      eslint-config-prettier: 8.5.0(eslint@8.30.0)
+      prettier: 2.8.1
+      prettier-linter-helpers: 1.0.0
+    dev: true
+
+  /eslint-scope@7.1.1:
+    resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dependencies:
+      esrecurse: 4.3.0
+      estraverse: 5.3.0
+    dev: true
+
+  /eslint-utils@3.0.0(eslint@8.30.0):
+    resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==}
+    engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0}
+    peerDependencies:
+      eslint: '>=5'
+    dependencies:
+      eslint: 8.30.0
+      eslint-visitor-keys: 2.1.0
+    dev: true
+
+  /eslint-visitor-keys@2.1.0:
+    resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /eslint-visitor-keys@3.3.0:
+    resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dev: true
+
+  /eslint-visitor-keys@3.4.3:
+    resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dev: true
+
+  /eslint@8.30.0:
+    resolution: {integrity: sha512-MGADB39QqYuzEGov+F/qb18r4i7DohCDOfatHaxI2iGlPuC65bwG2gxgO+7DkyL38dRFaRH7RaRAgU6JKL9rMQ==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    hasBin: true
+    dependencies:
+      '@eslint/eslintrc': 1.4.0
+      '@humanwhocodes/config-array': 0.11.8
+      '@humanwhocodes/module-importer': 1.0.1
+      '@nodelib/fs.walk': 1.2.8
+      ajv: 6.12.6
+      chalk: 4.1.2
+      cross-spawn: 7.0.3
+      debug: 4.3.4
+      doctrine: 3.0.0
+      escape-string-regexp: 4.0.0
+      eslint-scope: 7.1.1
+      eslint-utils: 3.0.0(eslint@8.30.0)
+      eslint-visitor-keys: 3.3.0
+      espree: 9.4.1
+      esquery: 1.4.0
+      esutils: 2.0.3
+      fast-deep-equal: 3.1.3
+      file-entry-cache: 6.0.1
+      find-up: 5.0.0
+      glob-parent: 6.0.2
+      globals: 13.19.0
+      grapheme-splitter: 1.0.4
+      ignore: 5.2.4
+      import-fresh: 3.3.0
+      imurmurhash: 0.1.4
+      is-glob: 4.0.3
+      is-path-inside: 3.0.3
+      js-sdsl: 4.2.0
+      js-yaml: 4.1.0
+      json-stable-stringify-without-jsonify: 1.0.1
+      levn: 0.4.1
+      lodash.merge: 4.6.2
+      minimatch: 3.1.2
+      natural-compare: 1.4.0
+      optionator: 0.9.1
+      regexpp: 3.2.0
+      strip-ansi: 6.0.1
+      strip-json-comments: 3.1.1
+      text-table: 0.2.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /espree@9.4.1:
+    resolution: {integrity: sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dependencies:
+      acorn: 8.8.1
+      acorn-jsx: 5.3.2(acorn@8.8.1)
+      eslint-visitor-keys: 3.3.0
+    dev: true
+
+  /esprima@4.0.1:
+    resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
+    engines: {node: '>=4'}
+    hasBin: true
+    dev: true
+
+  /esquery@1.4.0:
+    resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==}
+    engines: {node: '>=0.10'}
+    dependencies:
+      estraverse: 5.3.0
+    dev: true
+
+  /esrecurse@4.3.0:
+    resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+    engines: {node: '>=4.0'}
+    dependencies:
+      estraverse: 5.3.0
+    dev: true
+
+  /estraverse@5.3.0:
+    resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+    engines: {node: '>=4.0'}
+    dev: true
+
+  /estree-walker@2.0.2:
+    resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+    dev: true
+
+  /esutils@2.0.3:
+    resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /execa@5.1.1:
+    resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
+    engines: {node: '>=10'}
+    dependencies:
+      cross-spawn: 7.0.3
+      get-stream: 6.0.1
+      human-signals: 2.1.0
+      is-stream: 2.0.1
+      merge-stream: 2.0.0
+      npm-run-path: 4.0.1
+      onetime: 5.1.2
+      signal-exit: 3.0.7
+      strip-final-newline: 2.0.0
+    dev: true
+
+  /execa@7.2.0:
+    resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==}
+    engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0}
+    dependencies:
+      cross-spawn: 7.0.3
+      get-stream: 6.0.1
+      human-signals: 4.3.1
+      is-stream: 3.0.0
+      merge-stream: 2.0.0
+      npm-run-path: 5.2.0
+      onetime: 6.0.0
+      signal-exit: 3.0.7
+      strip-final-newline: 3.0.0
+    dev: true
+
+  /fast-deep-equal@3.1.3:
+    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+    dev: true
+
+  /fast-diff@1.2.0:
+    resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==}
+    dev: true
+
+  /fast-glob@3.2.12:
+    resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==}
+    engines: {node: '>=8.6.0'}
+    dependencies:
+      '@nodelib/fs.stat': 2.0.5
+      '@nodelib/fs.walk': 1.2.8
+      glob-parent: 5.1.2
+      merge2: 1.4.1
+      micromatch: 4.0.5
+    dev: true
+
+  /fast-glob@3.3.2:
+    resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
+    engines: {node: '>=8.6.0'}
+    dependencies:
+      '@nodelib/fs.stat': 2.0.5
+      '@nodelib/fs.walk': 1.2.8
+      glob-parent: 5.1.2
+      merge2: 1.4.1
+      micromatch: 4.0.5
+    dev: true
+
+  /fast-json-stable-stringify@2.1.0:
+    resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+    dev: true
+
+  /fast-levenshtein@2.0.6:
+    resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+    dev: true
+
+  /fast-stable-stringify@1.0.0:
+    resolution: {integrity: sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==}
+    dev: true
+
+  /fastestsmallesttextencoderdecoder@1.0.22:
+    resolution: {integrity: sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==}
+
+  /fastq@1.14.0:
+    resolution: {integrity: sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==}
+    dependencies:
+      reusify: 1.0.4
+    dev: true
+
+  /figures@6.0.1:
+    resolution: {integrity: sha512-0oY/olScYD4IhQ8u//gCPA4F3mlTn2dacYmiDm/mbDQvpmLjV4uH+zhsQ5IyXRyvqkvtUkXkNdGvg5OFJTCsuQ==}
+    engines: {node: '>=18'}
+    dependencies:
+      is-unicode-supported: 2.0.0
+    dev: true
+
+  /file-entry-cache@6.0.1:
+    resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
+    engines: {node: ^10.12.0 || >=12.0.0}
+    dependencies:
+      flat-cache: 3.0.4
+    dev: true
+
+  /file-uri-to-path@1.0.0:
+    resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
+    dev: true
+
+  /fill-range@7.0.1:
+    resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
+    engines: {node: '>=8'}
+    dependencies:
+      to-regex-range: 5.0.1
+    dev: true
+
+  /find-up-simple@1.0.0:
+    resolution: {integrity: sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==}
+    engines: {node: '>=18'}
+    dev: true
+
+  /find-up@5.0.0:
+    resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+    engines: {node: '>=10'}
+    dependencies:
+      locate-path: 6.0.0
+      path-exists: 4.0.0
+    dev: true
+
+  /flat-cache@3.0.4:
+    resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==}
+    engines: {node: ^10.12.0 || >=12.0.0}
+    dependencies:
+      flatted: 3.2.7
+      rimraf: 3.0.2
+    dev: true
+
+  /flatted@3.2.7:
+    resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==}
+    dev: true
+
+  /foreground-child@3.1.1:
+    resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
+    engines: {node: '>=14'}
+    dependencies:
+      cross-spawn: 7.0.3
+      signal-exit: 4.1.0
+    dev: true
+
+  /fs-minipass@2.1.0:
+    resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
+    engines: {node: '>= 8'}
+    dependencies:
+      minipass: 3.3.6
+    dev: true
+
+  /fs.realpath@1.0.0:
+    resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+    dev: true
+
+  /fsevents@2.3.2:
+    resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /function-bind@1.1.1:
+    resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
+    dev: true
+
+  /function.prototype.name@1.1.5:
+    resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      call-bind: 1.0.2
+      define-properties: 1.1.4
+      es-abstract: 1.20.5
+      functions-have-names: 1.2.3
+    dev: true
+
+  /functions-have-names@1.2.3:
+    resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
+    dev: true
+
+  /gauge@3.0.2:
+    resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
+    engines: {node: '>=10'}
+    dependencies:
+      aproba: 2.0.0
+      color-support: 1.1.3
+      console-control-strings: 1.1.0
+      has-unicode: 2.0.1
+      object-assign: 4.1.1
+      signal-exit: 3.0.7
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+      wide-align: 1.1.5
+    dev: true
+
+  /get-caller-file@2.0.5:
+    resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+    engines: {node: 6.* || 8.* || >= 10.*}
+    dev: true
+
+  /get-east-asian-width@1.2.0:
+    resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==}
+    engines: {node: '>=18'}
+    dev: true
+
+  /get-intrinsic@1.1.3:
+    resolution: {integrity: sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==}
+    dependencies:
+      function-bind: 1.1.1
+      has: 1.0.3
+      has-symbols: 1.0.3
+    dev: true
+
+  /get-stream@6.0.1:
+    resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /get-symbol-description@1.0.0:
+    resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      call-bind: 1.0.2
+      get-intrinsic: 1.1.3
+    dev: true
+
+  /glob-parent@5.1.2:
+    resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+    engines: {node: '>= 6'}
+    dependencies:
+      is-glob: 4.0.3
+    dev: true
+
+  /glob-parent@6.0.2:
+    resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+    engines: {node: '>=10.13.0'}
+    dependencies:
+      is-glob: 4.0.3
+    dev: true
+
+  /glob@10.3.10:
+    resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==}
+    engines: {node: '>=16 || 14 >=14.17'}
+    hasBin: true
+    dependencies:
+      foreground-child: 3.1.1
+      jackspeak: 2.3.6
+      minimatch: 9.0.3
+      minipass: 7.0.4
+      path-scurry: 1.10.1
+    dev: true
+
+  /glob@7.2.3:
+    resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+    dependencies:
+      fs.realpath: 1.0.0
+      inflight: 1.0.6
+      inherits: 2.0.4
+      minimatch: 3.1.2
+      once: 1.4.0
+      path-is-absolute: 1.0.1
+    dev: true
+
+  /globals@13.19.0:
+    resolution: {integrity: sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==}
+    engines: {node: '>=8'}
+    dependencies:
+      type-fest: 0.20.2
+    dev: true
+
+  /globby@11.1.0:
+    resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
+    engines: {node: '>=10'}
+    dependencies:
+      array-union: 2.1.0
+      dir-glob: 3.0.1
+      fast-glob: 3.2.12
+      ignore: 5.2.4
+      merge2: 1.4.1
+      slash: 3.0.0
+    dev: true
+
+  /globby@14.0.1:
+    resolution: {integrity: sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==}
+    engines: {node: '>=18'}
+    dependencies:
+      '@sindresorhus/merge-streams': 2.3.0
+      fast-glob: 3.3.2
+      ignore: 5.2.4
+      path-type: 5.0.0
+      slash: 5.1.0
+      unicorn-magic: 0.1.0
+    dev: true
+
+  /gopd@1.0.1:
+    resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
+    dependencies:
+      get-intrinsic: 1.1.3
+    dev: true
+
+  /graceful-fs@4.2.10:
+    resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
+    dev: true
+
+  /grapheme-splitter@1.0.4:
+    resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
+    dev: true
+
+  /graphemer@1.4.0:
+    resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
+    dev: true
+
+  /has-bigints@1.0.2:
+    resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
+    dev: true
+
+  /has-flag@4.0.0:
+    resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /has-property-descriptors@1.0.0:
+    resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==}
+    dependencies:
+      get-intrinsic: 1.1.3
+    dev: true
+
+  /has-symbols@1.0.3:
+    resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
+    engines: {node: '>= 0.4'}
+    dev: true
+
+  /has-tostringtag@1.0.0:
+    resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      has-symbols: 1.0.3
+    dev: true
+
+  /has-unicode@2.0.1:
+    resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
+    dev: true
+
+  /has@1.0.3:
+    resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
+    engines: {node: '>= 0.4.0'}
+    dependencies:
+      function-bind: 1.1.1
+    dev: true
+
+  /https-proxy-agent@5.0.1:
+    resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
+    engines: {node: '>= 6'}
+    dependencies:
+      agent-base: 6.0.2
+      debug: 4.3.4
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /human-signals@2.1.0:
+    resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
+    engines: {node: '>=10.17.0'}
+    dev: true
+
+  /human-signals@4.3.1:
+    resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==}
+    engines: {node: '>=14.18.0'}
+    dev: true
+
+  /ignore-by-default@2.1.0:
+    resolution: {integrity: sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw==}
+    engines: {node: '>=10 <11 || >=12 <13 || >=14'}
+    dev: true
+
+  /ignore@5.2.4:
+    resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
+    engines: {node: '>= 4'}
+    dev: true
+
+  /import-fresh@3.3.0:
+    resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
+    engines: {node: '>=6'}
+    dependencies:
+      parent-module: 1.0.1
+      resolve-from: 4.0.0
+    dev: true
+
+  /imurmurhash@0.1.4:
+    resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+    engines: {node: '>=0.8.19'}
+    dev: true
+
+  /indent-string@5.0.0:
+    resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /inflight@1.0.6:
+    resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+    dependencies:
+      once: 1.4.0
+      wrappy: 1.0.2
+    dev: true
+
+  /inherits@2.0.4:
+    resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+    dev: true
+
+  /internal-slot@1.0.4:
+    resolution: {integrity: sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      get-intrinsic: 1.1.3
+      has: 1.0.3
+      side-channel: 1.0.4
+    dev: true
+
+  /irregular-plurals@3.3.0:
+    resolution: {integrity: sha512-MVBLKUTangM3EfRPFROhmWQQKRDsrgI83J8GS3jXy+OwYqiR2/aoWndYQ5416jLE3uaGgLH7ncme3X9y09gZ3g==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /is-bigint@1.0.4:
+    resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
+    dependencies:
+      has-bigints: 1.0.2
+    dev: true
+
+  /is-binary-path@2.1.0:
+    resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+    engines: {node: '>=8'}
+    dependencies:
+      binary-extensions: 2.2.0
+    dev: true
+
+  /is-boolean-object@1.1.2:
+    resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      call-bind: 1.0.2
+      has-tostringtag: 1.0.0
+    dev: true
+
+  /is-callable@1.2.7:
+    resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
+    engines: {node: '>= 0.4'}
+    dev: true
+
+  /is-core-module@2.11.0:
+    resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==}
+    dependencies:
+      has: 1.0.3
+    dev: true
+
+  /is-date-object@1.0.5:
+    resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      has-tostringtag: 1.0.0
+    dev: true
+
+  /is-extglob@2.1.1:
+    resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /is-fullwidth-code-point@3.0.0:
+    resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /is-fullwidth-code-point@4.0.0:
+    resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /is-glob@4.0.3:
+    resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      is-extglob: 2.1.1
+    dev: true
+
+  /is-negative-zero@2.0.2:
+    resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==}
+    engines: {node: '>= 0.4'}
+    dev: true
+
+  /is-number-object@1.0.7:
+    resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      has-tostringtag: 1.0.0
+    dev: true
+
+  /is-number@7.0.0:
+    resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+    engines: {node: '>=0.12.0'}
+    dev: true
+
+  /is-path-inside@3.0.3:
+    resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /is-plain-object@5.0.0:
+    resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /is-promise@4.0.0:
+    resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==}
+    dev: true
+
+  /is-regex@1.1.4:
+    resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      call-bind: 1.0.2
+      has-tostringtag: 1.0.0
+    dev: true
+
+  /is-shared-array-buffer@1.0.2:
+    resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==}
+    dependencies:
+      call-bind: 1.0.2
+    dev: true
+
+  /is-stream@2.0.1:
+    resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /is-stream@3.0.0:
+    resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    dev: true
+
+  /is-string@1.0.7:
+    resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      has-tostringtag: 1.0.0
+    dev: true
+
+  /is-symbol@1.0.4:
+    resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      has-symbols: 1.0.3
+    dev: true
+
+  /is-unicode-supported@2.0.0:
+    resolution: {integrity: sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==}
+    engines: {node: '>=18'}
+    dev: true
+
+  /is-weakref@1.0.2:
+    resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
+    dependencies:
+      call-bind: 1.0.2
+    dev: true
+
+  /isexe@2.0.0:
+    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+    dev: true
+
+  /jackspeak@2.3.6:
+    resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
+    engines: {node: '>=14'}
+    dependencies:
+      '@isaacs/cliui': 8.0.2
+    optionalDependencies:
+      '@pkgjs/parseargs': 0.11.0
+    dev: true
+
+  /joycon@3.1.1:
+    resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /js-sdsl@4.2.0:
+    resolution: {integrity: sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==}
+    dev: true
+
+  /js-string-escape@1.0.1:
+    resolution: {integrity: sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==}
+    engines: {node: '>= 0.8'}
+    dev: true
+
+  /js-yaml@3.14.1:
+    resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
+    hasBin: true
+    dependencies:
+      argparse: 1.0.10
+      esprima: 4.0.1
+    dev: true
+
+  /js-yaml@4.1.0:
+    resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+    hasBin: true
+    dependencies:
+      argparse: 2.0.1
+    dev: true
+
+  /json-schema-traverse@0.4.1:
+    resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+    dev: true
+
+  /json-stable-stringify-without-jsonify@1.0.1:
+    resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+    dev: true
+
+  /json5@1.0.1:
+    resolution: {integrity: sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==}
+    hasBin: true
+    dependencies:
+      minimist: 1.2.7
+    dev: true
+
+  /jsonc-parser@3.2.0:
+    resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
+    dev: true
+
+  /levn@0.4.1:
+    resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      prelude-ls: 1.2.1
+      type-check: 0.4.0
+    dev: true
+
+  /lilconfig@3.1.1:
+    resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==}
+    engines: {node: '>=14'}
+    dev: true
+
+  /lines-and-columns@1.2.4:
+    resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+    dev: true
+
+  /load-json-file@7.0.1:
+    resolution: {integrity: sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    dev: true
+
+  /load-tsconfig@0.2.5:
+    resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    dev: true
+
+  /locate-path@6.0.0:
+    resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+    engines: {node: '>=10'}
+    dependencies:
+      p-locate: 5.0.0
+    dev: true
+
+  /lodash.merge@4.6.2:
+    resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+    dev: true
+
+  /lodash.sortby@4.7.0:
+    resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==}
+    dev: true
+
+  /lodash@4.17.21:
+    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+    dev: true
+
+  /lru-cache@10.2.0:
+    resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==}
+    engines: {node: 14 || >=16.14}
+    dev: true
+
+  /lru-cache@6.0.0:
+    resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
+    engines: {node: '>=10'}
+    dependencies:
+      yallist: 4.0.0
+    dev: true
+
+  /lunr@2.3.9:
+    resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==}
+    dev: true
+
+  /make-dir@3.1.0:
+    resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
+    engines: {node: '>=8'}
+    dependencies:
+      semver: 6.3.0
+    dev: true
+
+  /marked@4.3.0:
+    resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==}
+    engines: {node: '>= 12'}
+    hasBin: true
+    dev: true
+
+  /matcher@5.0.0:
+    resolution: {integrity: sha512-s2EMBOWtXFc8dgqvoAzKJXxNHibcdJMV0gwqKUaw9E2JBJuGUK7DrNKrA6g/i+v72TT16+6sVm5mS3thaMLQUw==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    dependencies:
+      escape-string-regexp: 5.0.0
+    dev: true
+
+  /md5-hex@3.0.1:
+    resolution: {integrity: sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==}
+    engines: {node: '>=8'}
+    dependencies:
+      blueimp-md5: 2.19.0
+    dev: true
+
+  /memoize@10.0.0:
+    resolution: {integrity: sha512-H6cBLgsi6vMWOcCpvVCdFFnl3kerEXbrYh9q+lY6VXvQSmM6CkmV08VOwT+WE2tzIEqRPFfAq3fm4v/UIW6mSA==}
+    engines: {node: '>=18'}
+    dependencies:
+      mimic-function: 5.0.0
+    dev: true
+
+  /merge-stream@2.0.0:
+    resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
+    dev: true
+
+  /merge2@1.4.1:
+    resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+    engines: {node: '>= 8'}
+    dev: true
+
+  /micromatch@4.0.5:
+    resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
+    engines: {node: '>=8.6'}
+    dependencies:
+      braces: 3.0.2
+      picomatch: 2.3.1
+    dev: true
+
+  /mimic-fn@2.1.0:
+    resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /mimic-fn@4.0.0:
+    resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /mimic-function@5.0.0:
+    resolution: {integrity: sha512-RBfQ+9X9DpXdEoK7Bu+KeEU6vFhumEIiXKWECPzRBmDserEq4uR2b/VCm0LwpMSosoq2k+Zuxj/GzOr0Fn6h/g==}
+    engines: {node: '>=18'}
+    dev: true
+
+  /minimatch@3.1.2:
+    resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+    dependencies:
+      brace-expansion: 1.1.11
+    dev: true
+
+  /minimatch@9.0.3:
+    resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
+    engines: {node: '>=16 || 14 >=14.17'}
+    dependencies:
+      brace-expansion: 2.0.1
+    dev: true
+
+  /minimist@1.2.7:
+    resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==}
+    dev: true
+
+  /minipass@3.3.6:
+    resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
+    engines: {node: '>=8'}
+    dependencies:
+      yallist: 4.0.0
+    dev: true
+
+  /minipass@4.0.3:
+    resolution: {integrity: sha512-OW2r4sQ0sI+z5ckEt5c1Tri4xTgZwYDxpE54eqWlQloQRoWtXjqt9udJ5Z4dSv7wK+nfFI7FRXyCpBSft+gpFw==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /minipass@7.0.4:
+    resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==}
+    engines: {node: '>=16 || 14 >=14.17'}
+    dev: true
+
+  /minizlib@2.1.2:
+    resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
+    engines: {node: '>= 8'}
+    dependencies:
+      minipass: 3.3.6
+      yallist: 4.0.0
+    dev: true
+
+  /mkdirp@1.0.4:
+    resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
+    engines: {node: '>=10'}
+    hasBin: true
+    dev: true
+
+  /ms@2.0.0:
+    resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
+    dev: true
+
+  /ms@2.1.2:
+    resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
+    dev: true
+
+  /ms@2.1.3:
+    resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+    dev: true
+
+  /mz@2.7.0:
+    resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
+    dependencies:
+      any-promise: 1.3.0
+      object-assign: 4.1.1
+      thenify-all: 1.6.0
+    dev: true
+
+  /natural-compare@1.4.0:
+    resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+    dev: true
+
+  /node-fetch@2.6.7:
+    resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
+    engines: {node: 4.x || >=6.0.0}
+    peerDependencies:
+      encoding: ^0.1.0
+    peerDependenciesMeta:
+      encoding:
+        optional: true
+    dependencies:
+      whatwg-url: 5.0.0
+    dev: true
+
+  /node-gyp-build@4.6.0:
+    resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==}
+    hasBin: true
+    dev: true
+
+  /nofilter@3.1.0:
+    resolution: {integrity: sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==}
+    engines: {node: '>=12.19'}
+    dev: true
+
+  /nopt@5.0.0:
+    resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==}
+    engines: {node: '>=6'}
+    hasBin: true
+    dependencies:
+      abbrev: 1.1.1
+    dev: true
+
+  /normalize-path@3.0.0:
+    resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /npm-run-path@4.0.1:
+    resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
+    engines: {node: '>=8'}
+    dependencies:
+      path-key: 3.1.1
+    dev: true
+
+  /npm-run-path@5.2.0:
+    resolution: {integrity: sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    dependencies:
+      path-key: 4.0.0
+    dev: true
+
+  /npmlog@5.0.1:
+    resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
+    dependencies:
+      are-we-there-yet: 2.0.0
+      console-control-strings: 1.1.0
+      gauge: 3.0.2
+      set-blocking: 2.0.0
+    dev: true
+
+  /object-assign@4.1.1:
+    resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /object-inspect@1.12.2:
+    resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==}
+    dev: true
+
+  /object-keys@1.1.1:
+    resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
+    engines: {node: '>= 0.4'}
+    dev: true
+
+  /object.assign@4.1.4:
+    resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      call-bind: 1.0.2
+      define-properties: 1.1.4
+      has-symbols: 1.0.3
+      object-keys: 1.1.1
+    dev: true
+
+  /object.entries@1.1.6:
+    resolution: {integrity: sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      call-bind: 1.0.2
+      define-properties: 1.1.4
+      es-abstract: 1.20.5
+    dev: true
+
+  /object.values@1.1.6:
+    resolution: {integrity: sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      call-bind: 1.0.2
+      define-properties: 1.1.4
+      es-abstract: 1.20.5
+    dev: true
+
+  /once@1.4.0:
+    resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+    dependencies:
+      wrappy: 1.0.2
+    dev: true
+
+  /onetime@5.1.2:
+    resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
+    engines: {node: '>=6'}
+    dependencies:
+      mimic-fn: 2.1.0
+    dev: true
+
+  /onetime@6.0.0:
+    resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
+    engines: {node: '>=12'}
+    dependencies:
+      mimic-fn: 4.0.0
+    dev: true
+
+  /optionator@0.9.1:
+    resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      deep-is: 0.1.4
+      fast-levenshtein: 2.0.6
+      levn: 0.4.1
+      prelude-ls: 1.2.1
+      type-check: 0.4.0
+      word-wrap: 1.2.3
+    dev: true
+
+  /p-limit@3.1.0:
+    resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+    engines: {node: '>=10'}
+    dependencies:
+      yocto-queue: 0.1.0
+    dev: true
+
+  /p-locate@5.0.0:
+    resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+    engines: {node: '>=10'}
+    dependencies:
+      p-limit: 3.1.0
+    dev: true
+
+  /p-map@7.0.1:
+    resolution: {integrity: sha512-2wnaR0XL/FDOj+TgpDuRb2KTjLnu3Fma6b1ZUwGY7LcqenMcvP/YFpjpbPKY6WVGsbuJZRuoUz8iPrt8ORnAFw==}
+    engines: {node: '>=18'}
+    dev: true
+
+  /package-config@5.0.0:
+    resolution: {integrity: sha512-GYTTew2slBcYdvRHqjhwaaydVMvn/qrGC323+nKclYioNSLTDUM/lGgtGTgyHVtYcozb+XkE8CNhwcraOmZ9Mg==}
+    engines: {node: '>=18'}
+    dependencies:
+      find-up-simple: 1.0.0
+      load-json-file: 7.0.1
+    dev: true
+
+  /parent-module@1.0.1:
+    resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+    engines: {node: '>=6'}
+    dependencies:
+      callsites: 3.1.0
+    dev: true
+
+  /parse-ms@4.0.0:
+    resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==}
+    engines: {node: '>=18'}
+    dev: true
+
+  /path-exists@4.0.0:
+    resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /path-is-absolute@1.0.1:
+    resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /path-key@3.1.1:
+    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /path-key@4.0.0:
+    resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /path-parse@1.0.7:
+    resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+    dev: true
+
+  /path-scurry@1.10.1:
+    resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==}
+    engines: {node: '>=16 || 14 >=14.17'}
+    dependencies:
+      lru-cache: 10.2.0
+      minipass: 7.0.4
+    dev: true
+
+  /path-type@4.0.0:
+    resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /path-type@5.0.0:
+    resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /picomatch@2.3.1:
+    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+    engines: {node: '>=8.6'}
+    dev: true
+
+  /picomatch@3.0.1:
+    resolution: {integrity: sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /pirates@4.0.6:
+    resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
+    engines: {node: '>= 6'}
+    dev: true
+
+  /plur@5.1.0:
+    resolution: {integrity: sha512-VP/72JeXqak2KiOzjgKtQen5y3IZHn+9GOuLDafPv0eXa47xq0At93XahYBs26MsifCQ4enGKwbjBTKgb9QJXg==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    dependencies:
+      irregular-plurals: 3.3.0
+    dev: true
+
+  /postcss-load-config@4.0.2:
+    resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
+    engines: {node: '>= 14'}
+    peerDependencies:
+      postcss: '>=8.0.9'
+      ts-node: '>=9.0.0'
+    peerDependenciesMeta:
+      postcss:
+        optional: true
+      ts-node:
+        optional: true
+    dependencies:
+      lilconfig: 3.1.1
+      yaml: 2.3.4
+    dev: true
+
+  /prelude-ls@1.2.1:
+    resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+    engines: {node: '>= 0.8.0'}
+    dev: true
+
+  /prettier-linter-helpers@1.0.0:
+    resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      fast-diff: 1.2.0
+    dev: true
+
+  /prettier@2.8.1:
+    resolution: {integrity: sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==}
+    engines: {node: '>=10.13.0'}
+    hasBin: true
+    dev: true
+
+  /pretty-ms@9.0.0:
+    resolution: {integrity: sha512-E9e9HJ9R9NasGOgPaPE8VMeiPKAyWR5jcFpNnwIejslIhWqdqOrb2wShBsncMPUb+BcCd2OPYfh7p2W6oemTng==}
+    engines: {node: '>=18'}
+    dependencies:
+      parse-ms: 4.0.0
+    dev: true
+
+  /punycode@2.1.1:
+    resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /queue-microtask@1.2.3:
+    resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+    dev: true
+
+  /readable-stream@3.6.0:
+    resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==}
+    engines: {node: '>= 6'}
+    dependencies:
+      inherits: 2.0.4
+      string_decoder: 1.3.0
+      util-deprecate: 1.0.2
+    dev: true
+
+  /readdirp@3.6.0:
+    resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+    engines: {node: '>=8.10.0'}
+    dependencies:
+      picomatch: 2.3.1
+    dev: true
+
+  /regexp.prototype.flags@1.4.3:
+    resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      call-bind: 1.0.2
+      define-properties: 1.1.4
+      functions-have-names: 1.2.3
+    dev: true
+
+  /regexpp@3.2.0:
+    resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /require-directory@2.1.1:
+    resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /resolve-cwd@3.0.0:
+    resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==}
+    engines: {node: '>=8'}
+    dependencies:
+      resolve-from: 5.0.0
+    dev: true
+
+  /resolve-from@4.0.0:
+    resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /resolve-from@5.0.0:
+    resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /resolve@1.22.1:
+    resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==}
+    hasBin: true
+    dependencies:
+      is-core-module: 2.11.0
+      path-parse: 1.0.7
+      supports-preserve-symlinks-flag: 1.0.0
+    dev: true
+
+  /reusify@1.0.4:
+    resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
+    engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+    dev: true
+
+  /rimraf@3.0.2:
+    resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
+    hasBin: true
+    dependencies:
+      glob: 7.2.3
+    dev: true
+
+  /rimraf@5.0.5:
+    resolution: {integrity: sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==}
+    engines: {node: '>=14'}
+    hasBin: true
+    dependencies:
+      glob: 10.3.10
+    dev: true
+
+  /rollup@4.12.0:
+    resolution: {integrity: sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==}
+    engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+    hasBin: true
+    dependencies:
+      '@types/estree': 1.0.5
+    optionalDependencies:
+      '@rollup/rollup-android-arm-eabi': 4.12.0
+      '@rollup/rollup-android-arm64': 4.12.0
+      '@rollup/rollup-darwin-arm64': 4.12.0
+      '@rollup/rollup-darwin-x64': 4.12.0
+      '@rollup/rollup-linux-arm-gnueabihf': 4.12.0
+      '@rollup/rollup-linux-arm64-gnu': 4.12.0
+      '@rollup/rollup-linux-arm64-musl': 4.12.0
+      '@rollup/rollup-linux-riscv64-gnu': 4.12.0
+      '@rollup/rollup-linux-x64-gnu': 4.12.0
+      '@rollup/rollup-linux-x64-musl': 4.12.0
+      '@rollup/rollup-win32-arm64-msvc': 4.12.0
+      '@rollup/rollup-win32-ia32-msvc': 4.12.0
+      '@rollup/rollup-win32-x64-msvc': 4.12.0
+      fsevents: 2.3.2
+    dev: true
+
+  /run-parallel@1.2.0:
+    resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+    dependencies:
+      queue-microtask: 1.2.3
+    dev: true
+
+  /safe-buffer@5.2.1:
+    resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+    dev: true
+
+  /safe-regex-test@1.0.0:
+    resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==}
+    dependencies:
+      call-bind: 1.0.2
+      get-intrinsic: 1.1.3
+      is-regex: 1.1.4
+    dev: true
+
+  /semver@6.3.0:
+    resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
+    hasBin: true
+    dev: true
+
+  /semver@7.3.8:
+    resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==}
+    engines: {node: '>=10'}
+    hasBin: true
+    dependencies:
+      lru-cache: 6.0.0
+    dev: true
+
+  /semver@7.6.0:
+    resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==}
+    engines: {node: '>=10'}
+    hasBin: true
+    dependencies:
+      lru-cache: 6.0.0
+    dev: true
+
+  /serialize-error@7.0.1:
+    resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==}
+    engines: {node: '>=10'}
+    dependencies:
+      type-fest: 0.13.1
+    dev: true
+
+  /set-blocking@2.0.0:
+    resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
+    dev: true
+
+  /shebang-command@2.0.0:
+    resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+    engines: {node: '>=8'}
+    dependencies:
+      shebang-regex: 3.0.0
+    dev: true
+
+  /shebang-regex@3.0.0:
+    resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /shiki@0.14.7:
+    resolution: {integrity: sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==}
+    dependencies:
+      ansi-sequence-parser: 1.1.1
+      jsonc-parser: 3.2.0
+      vscode-oniguruma: 1.7.0
+      vscode-textmate: 8.0.0
+    dev: true
+
+  /side-channel@1.0.4:
+    resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
+    dependencies:
+      call-bind: 1.0.2
+      get-intrinsic: 1.1.3
+      object-inspect: 1.12.2
+    dev: true
+
+  /signal-exit@3.0.7:
+    resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
+    dev: true
+
+  /signal-exit@4.1.0:
+    resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+    engines: {node: '>=14'}
+    dev: true
+
+  /slash@3.0.0:
+    resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /slash@5.1.0:
+    resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==}
+    engines: {node: '>=14.16'}
+    dev: true
+
+  /slice-ansi@5.0.0:
+    resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==}
+    engines: {node: '>=12'}
+    dependencies:
+      ansi-styles: 6.2.1
+      is-fullwidth-code-point: 4.0.0
+    dev: true
+
+  /source-map@0.8.0-beta.0:
+    resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
+    engines: {node: '>= 8'}
+    dependencies:
+      whatwg-url: 7.1.0
+    dev: true
+
+  /sprintf-js@1.0.3:
+    resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
+    dev: true
+
+  /stack-utils@2.0.6:
+    resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
+    engines: {node: '>=10'}
+    dependencies:
+      escape-string-regexp: 2.0.0
+    dev: true
+
+  /string-width@4.2.3:
+    resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+    engines: {node: '>=8'}
+    dependencies:
+      emoji-regex: 8.0.0
+      is-fullwidth-code-point: 3.0.0
+      strip-ansi: 6.0.1
+    dev: true
+
+  /string-width@5.1.2:
+    resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
+    engines: {node: '>=12'}
+    dependencies:
+      eastasianwidth: 0.2.0
+      emoji-regex: 9.2.2
+      strip-ansi: 7.0.1
+    dev: true
+
+  /string-width@7.1.0:
+    resolution: {integrity: sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==}
+    engines: {node: '>=18'}
+    dependencies:
+      emoji-regex: 10.3.0
+      get-east-asian-width: 1.2.0
+      strip-ansi: 7.1.0
+    dev: true
+
+  /string.prototype.trimend@1.0.6:
+    resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==}
+    dependencies:
+      call-bind: 1.0.2
+      define-properties: 1.1.4
+      es-abstract: 1.20.5
+    dev: true
+
+  /string.prototype.trimstart@1.0.6:
+    resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==}
+    dependencies:
+      call-bind: 1.0.2
+      define-properties: 1.1.4
+      es-abstract: 1.20.5
+    dev: true
+
+  /string_decoder@1.3.0:
+    resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
+    dependencies:
+      safe-buffer: 5.2.1
+    dev: true
+
+  /strip-ansi@6.0.1:
+    resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+    engines: {node: '>=8'}
+    dependencies:
+      ansi-regex: 5.0.1
+    dev: true
+
+  /strip-ansi@7.0.1:
+    resolution: {integrity: sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==}
+    engines: {node: '>=12'}
+    dependencies:
+      ansi-regex: 6.0.1
+    dev: true
+
+  /strip-ansi@7.1.0:
+    resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
+    engines: {node: '>=12'}
+    dependencies:
+      ansi-regex: 6.0.1
+    dev: true
+
+  /strip-bom@3.0.0:
+    resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /strip-final-newline@2.0.0:
+    resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /strip-final-newline@3.0.0:
+    resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /strip-json-comments@3.1.1:
+    resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /sucrase@3.35.0:
+    resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
+    engines: {node: '>=16 || 14 >=14.17'}
+    hasBin: true
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.2
+      commander: 4.1.1
+      glob: 10.3.10
+      lines-and-columns: 1.2.4
+      mz: 2.7.0
+      pirates: 4.0.6
+      ts-interface-checker: 0.1.13
+    dev: true
+
+  /supertap@3.0.1:
+    resolution: {integrity: sha512-u1ZpIBCawJnO+0QePsEiOknOfCRq0yERxiAchT0i4li0WHNUJbf0evXXSXOcCAR4M8iMDoajXYmstm/qO81Isw==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    dependencies:
+      indent-string: 5.0.0
+      js-yaml: 3.14.1
+      serialize-error: 7.0.1
+      strip-ansi: 7.1.0
+    dev: true
+
+  /supports-color@7.2.0:
+    resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+    engines: {node: '>=8'}
+    dependencies:
+      has-flag: 4.0.0
+    dev: true
+
+  /supports-preserve-symlinks-flag@1.0.0:
+    resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+    engines: {node: '>= 0.4'}
+    dev: true
+
+  /tar@6.1.13:
+    resolution: {integrity: sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==}
+    engines: {node: '>=10'}
+    dependencies:
+      chownr: 2.0.0
+      fs-minipass: 2.1.0
+      minipass: 4.0.3
+      minizlib: 2.1.2
+      mkdirp: 1.0.4
+      yallist: 4.0.0
+    dev: true
+
+  /temp-dir@3.0.0:
+    resolution: {integrity: sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==}
+    engines: {node: '>=14.16'}
+    dev: true
+
+  /text-table@0.2.0:
+    resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
+    dev: true
+
+  /thenify-all@1.6.0:
+    resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
+    engines: {node: '>=0.8'}
+    dependencies:
+      thenify: 3.3.1
+    dev: true
+
+  /thenify@3.3.1:
+    resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
+    dependencies:
+      any-promise: 1.3.0
+    dev: true
+
+  /time-zone@1.0.0:
+    resolution: {integrity: sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /to-regex-range@5.0.1:
+    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+    engines: {node: '>=8.0'}
+    dependencies:
+      is-number: 7.0.0
+    dev: true
+
+  /tr46@0.0.3:
+    resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
+    dev: true
+
+  /tr46@1.0.1:
+    resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==}
+    dependencies:
+      punycode: 2.1.1
+    dev: true
+
+  /tree-kill@1.2.2:
+    resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
+    hasBin: true
+    dev: true
+
+  /ts-api-utils@1.2.1(typescript@5.3.3):
+    resolution: {integrity: sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==}
+    engines: {node: '>=16'}
+    peerDependencies:
+      typescript: '>=4.2.0'
+    dependencies:
+      typescript: 5.3.3
+    dev: true
+
+  /ts-interface-checker@0.1.13:
+    resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
+    dev: true
+
+  /tsconfig-paths@3.14.1:
+    resolution: {integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==}
+    dependencies:
+      '@types/json5': 0.0.29
+      json5: 1.0.1
+      minimist: 1.2.7
+      strip-bom: 3.0.0
+    dev: true
+
+  /tsup@8.0.2(typescript@5.3.3):
+    resolution: {integrity: sha512-NY8xtQXdH7hDUAZwcQdY/Vzlw9johQsaqf7iwZ6g1DOUlFYQ5/AtVAjTvihhEyeRlGo4dLRVHtrRaL35M1daqQ==}
+    engines: {node: '>=18'}
+    hasBin: true
+    peerDependencies:
+      '@microsoft/api-extractor': ^7.36.0
+      '@swc/core': ^1
+      postcss: ^8.4.12
+      typescript: '>=4.5.0'
+    peerDependenciesMeta:
+      '@microsoft/api-extractor':
+        optional: true
+      '@swc/core':
+        optional: true
+      postcss:
+        optional: true
+      typescript:
+        optional: true
+    dependencies:
+      bundle-require: 4.0.2(esbuild@0.19.12)
+      cac: 6.7.14
+      chokidar: 3.5.3
+      debug: 4.3.4
+      esbuild: 0.19.12
+      execa: 5.1.1
+      globby: 11.1.0
+      joycon: 3.1.1
+      postcss-load-config: 4.0.2
+      resolve-from: 5.0.0
+      rollup: 4.12.0
+      source-map: 0.8.0-beta.0
+      sucrase: 3.35.0
+      tree-kill: 1.2.2
+      typescript: 5.3.3
+    transitivePeerDependencies:
+      - supports-color
+      - ts-node
+    dev: true
+
+  /type-check@0.4.0:
+    resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      prelude-ls: 1.2.1
+    dev: true
+
+  /type-fest@0.13.1:
+    resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /type-fest@0.20.2:
+    resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /typedoc-plugin-expand-object-like-types@0.1.2(typedoc@0.25.8):
+    resolution: {integrity: sha512-RRMOCWMElQHBOVraWMWrh/0tDqCdS5oxYwaWMZBB3KlUUUUCxKllpvJPsRH/uFLO1nOuy28CbJxGVU1umv7LOQ==}
+    peerDependencies:
+      typedoc: 0.22.x || 0.23.x
+    dependencies:
+      typedoc: 0.25.8(typescript@5.3.3)
+    dev: true
+
+  /typedoc-plugin-missing-exports@2.2.0(typedoc@0.25.8):
+    resolution: {integrity: sha512-2+XR1IcyQ5UwXZVJe9NE6HrLmNufT9i5OwoIuuj79VxuA3eYq+Y6itS9rnNV1D7UeQnUSH8kISYD73gHE5zw+w==}
+    peerDependencies:
+      typedoc: 0.24.x || 0.25.x
+    dependencies:
+      typedoc: 0.25.8(typescript@5.3.3)
+    dev: true
+
+  /typedoc@0.25.8(typescript@5.3.3):
+    resolution: {integrity: sha512-mh8oLW66nwmeB9uTa0Bdcjfis+48bAjSH3uqdzSuSawfduROQLlXw//WSNZLYDdhmMVB7YcYZicq6e8T0d271A==}
+    engines: {node: '>= 16'}
+    hasBin: true
+    peerDependencies:
+      typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x
+    dependencies:
+      lunr: 2.3.9
+      marked: 4.3.0
+      minimatch: 9.0.3
+      shiki: 0.14.7
+      typescript: 5.3.3
+    dev: true
+
+  /typescript@5.3.3:
+    resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==}
+    engines: {node: '>=14.17'}
+    hasBin: true
+    dev: true
+
+  /unbox-primitive@1.0.2:
+    resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
+    dependencies:
+      call-bind: 1.0.2
+      has-bigints: 1.0.2
+      has-symbols: 1.0.3
+      which-boxed-primitive: 1.0.2
+    dev: true
+
+  /unicorn-magic@0.1.0:
+    resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==}
+    engines: {node: '>=18'}
+    dev: true
+
+  /uri-js@4.4.1:
+    resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+    dependencies:
+      punycode: 2.1.1
+    dev: true
+
+  /util-deprecate@1.0.2:
+    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+    dev: true
+
+  /vscode-oniguruma@1.7.0:
+    resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==}
+    dev: true
+
+  /vscode-textmate@8.0.0:
+    resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==}
+    dev: true
+
+  /webidl-conversions@3.0.1:
+    resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
+    dev: true
+
+  /webidl-conversions@4.0.2:
+    resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
+    dev: true
+
+  /well-known-symbols@2.0.0:
+    resolution: {integrity: sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /whatwg-url@5.0.0:
+    resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+    dependencies:
+      tr46: 0.0.3
+      webidl-conversions: 3.0.1
+    dev: true
+
+  /whatwg-url@7.1.0:
+    resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==}
+    dependencies:
+      lodash.sortby: 4.7.0
+      tr46: 1.0.1
+      webidl-conversions: 4.0.2
+    dev: true
+
+  /which-boxed-primitive@1.0.2:
+    resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
+    dependencies:
+      is-bigint: 1.0.4
+      is-boolean-object: 1.1.2
+      is-number-object: 1.0.7
+      is-string: 1.0.7
+      is-symbol: 1.0.4
+    dev: true
+
+  /which@2.0.2:
+    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+    engines: {node: '>= 8'}
+    hasBin: true
+    dependencies:
+      isexe: 2.0.0
+    dev: true
+
+  /wide-align@1.1.5:
+    resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
+    dependencies:
+      string-width: 4.2.3
+    dev: true
+
+  /word-wrap@1.2.3:
+    resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /wrap-ansi@7.0.0:
+    resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+    engines: {node: '>=10'}
+    dependencies:
+      ansi-styles: 4.3.0
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+    dev: true
+
+  /wrap-ansi@8.1.0:
+    resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
+    engines: {node: '>=12'}
+    dependencies:
+      ansi-styles: 6.2.1
+      string-width: 5.1.2
+      strip-ansi: 7.0.1
+    dev: true
+
+  /wrappy@1.0.2:
+    resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+    dev: true
+
+  /write-file-atomic@5.0.1:
+    resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+    dependencies:
+      imurmurhash: 0.1.4
+      signal-exit: 4.1.0
+    dev: true
+
+  /ws@8.14.2:
+    resolution: {integrity: sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==}
+    engines: {node: '>=10.0.0'}
+    peerDependencies:
+      bufferutil: ^4.0.1
+      utf-8-validate: '>=5.0.2'
+    peerDependenciesMeta:
+      bufferutil:
+        optional: true
+      utf-8-validate:
+        optional: true
+    dev: true
+
+  /y18n@5.0.8:
+    resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /yallist@4.0.0:
+    resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+    dev: true
+
+  /yaml@2.3.4:
+    resolution: {integrity: sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==}
+    engines: {node: '>= 14'}
+    dev: true
+
+  /yargs-parser@21.1.1:
+    resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /yargs@17.7.2:
+    resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
+    engines: {node: '>=12'}
+    dependencies:
+      cliui: 8.0.1
+      escalade: 3.1.1
+      get-caller-file: 2.0.5
+      require-directory: 2.1.1
+      string-width: 4.2.3
+      y18n: 5.0.8
+      yargs-parser: 21.1.1
+    dev: true
+
+  /yocto-queue@0.1.0:
+    resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+    engines: {node: '>=10'}
+    dev: true

+ 1 - 0
template/clients/js/clients/js/src/index.ts

@@ -0,0 +1 @@
+export * from './generated';

+ 103 - 0
template/clients/js/clients/js/test/_setup.ts

@@ -0,0 +1,103 @@
+import {
+  Address,
+  Commitment,
+  CompilableTransaction,
+  ITransactionWithBlockhashLifetime,
+  ProgramDerivedAddress,
+  TransactionSigner,
+  airdropFactory,
+  appendTransactionInstruction,
+  createDefaultRpcSubscriptionsTransport,
+  createDefaultRpcTransport,
+  createSolanaRpc,
+  createSolanaRpcSubscriptions,
+  createTransaction,
+  generateKeyPairSigner,
+  getSignatureFromTransaction,
+  lamports,
+  pipe,
+  sendAndConfirmTransactionFactory,
+  setTransactionFeePayerSigner,
+  setTransactionLifetimeUsingBlockhash,
+  signTransactionWithSigners,
+} from '@solana/web3.js';
+import { findCounterPda, getCreateInstructionAsync } from '../src';
+
+type Client = {
+  rpc: ReturnType<typeof createSolanaRpc>;
+  rpcSubscriptions: ReturnType<typeof createSolanaRpcSubscriptions>;
+};
+
+export const createDefaultSolanaClient = (): Client => {
+  const rpc = createSolanaRpc({
+    transport: createDefaultRpcTransport({ url: 'http://127.0.0.1:8899' }),
+  });
+  const rpcSubscriptions = createSolanaRpcSubscriptions({
+    transport: createDefaultRpcSubscriptionsTransport({
+      url: 'ws://127.0.0.1:8900',
+    }),
+  });
+
+  return { rpc, rpcSubscriptions };
+};
+
+export const generateKeyPairSignerWithSol = async (
+  client: Client,
+  putativeLamports: bigint = 1_000_000_000n
+) => {
+  const signer = await generateKeyPairSigner();
+  await airdropFactory(client)({
+    recipientAddress: signer.address,
+    lamports: lamports(putativeLamports),
+    commitment: 'confirmed',
+  });
+  return signer;
+};
+
+export const createDefaultTransaction = async (
+  client: Client,
+  feePayer: TransactionSigner
+) => {
+  const { value: latestBlockhash } = await client.rpc
+    .getLatestBlockhash()
+    .send();
+  return pipe(
+    createTransaction({ version: 0 }),
+    (tx) => setTransactionFeePayerSigner(feePayer, tx),
+    (tx) => setTransactionLifetimeUsingBlockhash(latestBlockhash, tx)
+  );
+};
+
+export const signAndSendTransaction = async (
+  client: Client,
+  transaction: CompilableTransaction & ITransactionWithBlockhashLifetime,
+  commitment: Commitment = 'confirmed'
+) => {
+  const signedTransaction = await signTransactionWithSigners(transaction);
+  const signature = getSignatureFromTransaction(signedTransaction);
+  await sendAndConfirmTransactionFactory(client)(signedTransaction, {
+    commitment,
+  });
+  return signature;
+};
+
+export const getBalance = async (client: Client, address: Address) =>
+  (await client.rpc.getBalance(address, { commitment: 'confirmed' }).send())
+    .value;
+
+export const createCounterForAuthority = async (
+  client: Client,
+  authority: TransactionSigner
+): Promise<ProgramDerivedAddress> => {
+  const [transaction, counterPda, createIx] = await Promise.all([
+    createDefaultTransaction(client, authority),
+    findCounterPda({ authority: authority.address }),
+    getCreateInstructionAsync({ authority }),
+  ]);
+  await pipe(
+    transaction,
+    (tx) => appendTransactionInstruction(createIx, tx),
+    (tx) => signAndSendTransaction(client, tx)
+  );
+  return counterPda;
+};

+ 38 - 0
template/clients/js/clients/js/test/create.test.ts

@@ -0,0 +1,38 @@
+import { appendTransactionInstruction, pipe } from '@solana/web3.js';
+import test from 'ava';
+import {
+  Counter,
+  fetchCounterFromSeeds,
+  getCreateInstructionAsync,
+} from '../src';
+import {
+  createDefaultSolanaClient,
+  createDefaultTransaction,
+  generateKeyPairSignerWithSol,
+  signAndSendTransaction,
+} from './_setup';
+
+test('it creates a new counter account', async (t) => {
+  // Given an authority key pair with some SOL.
+  const client = createDefaultSolanaClient();
+  const authority = await generateKeyPairSignerWithSol(client);
+
+  // When we create a new counter account.
+  const createIx = await getCreateInstructionAsync({ authority });
+  await pipe(
+    await createDefaultTransaction(client, authority),
+    (tx) => appendTransactionInstruction(createIx, tx),
+    (tx) => signAndSendTransaction(client, tx)
+  );
+
+  // Then we expect the counter account to exist and have a value of 0.
+  const counter = await fetchCounterFromSeeds(client.rpc, {
+    authority: authority.address,
+  });
+  t.like(counter, <Counter>{
+    data: {
+      authority: authority.address,
+      value: 0,
+    },
+  });
+});

+ 117 - 0
template/clients/js/clients/js/test/increment.test.ts

@@ -0,0 +1,117 @@
+import { appendTransactionInstruction, lamports, pipe } from '@solana/web3.js';
+import test from 'ava';
+import {
+  fetchCounter,
+  findCounterPda,
+  getIncrementInstruction,
+  getIncrementInstructionAsync,
+} from '../src';
+import {
+  createCounterForAuthority,
+  createDefaultSolanaClient,
+  createDefaultTransaction,
+  generateKeyPairSignerWithSol,
+  getBalance,
+  signAndSendTransaction,
+} from './_setup';
+
+test('it increments an existing counter by 1 by default', async (t) => {
+  // Given an authority key pair with an associated counter account of value 0.
+  const client = createDefaultSolanaClient();
+  const authority = await generateKeyPairSignerWithSol(client);
+  const [counterPda] = await createCounterForAuthority(client, authority);
+  t.is((await fetchCounter(client.rpc, counterPda)).data.value, 0);
+
+  // When we increment the counter account.
+  const incrementIx = await getIncrementInstructionAsync({ authority });
+  await pipe(
+    await createDefaultTransaction(client, authority),
+    (tx) => appendTransactionInstruction(incrementIx, tx),
+    (tx) => signAndSendTransaction(client, tx)
+  );
+
+  // Then we expect the counter account to have a value of 1.
+  const counter = await fetchCounter(client.rpc, counterPda);
+  t.is(counter.data.value, 1);
+});
+
+test('it can increment an existing counter by a specified amount', async (t) => {
+  // Given an authority key pair with an associated counter account of value 0.
+  const client = createDefaultSolanaClient();
+  const authority = await generateKeyPairSignerWithSol(client);
+  const [counterPda] = await createCounterForAuthority(client, authority);
+  t.is((await fetchCounter(client.rpc, counterPda)).data.value, 0);
+
+  // When we increment the counter account by 5.
+  const incrementIx = await getIncrementInstructionAsync({
+    authority,
+    amount: 5,
+  });
+  await pipe(
+    await createDefaultTransaction(client, authority),
+    (tx) => appendTransactionInstruction(incrementIx, tx),
+    (tx) => signAndSendTransaction(client, tx)
+  );
+
+  // Then we expect the counter account to have a value of 5.
+  const counter = await fetchCounter(client.rpc, counterPda);
+  t.is(counter.data.value, 5);
+});
+
+test('it cannot increment a counter that does not exist', async (t) => {
+  // Given an authority key pair with no associated counter account.
+  const client = createDefaultSolanaClient();
+  const authority = await generateKeyPairSignerWithSol(client);
+  const [counterPda] = await findCounterPda({ authority: authority.address });
+  t.is(await getBalance(client, counterPda), lamports(0n));
+
+  // When we try to increment the inexistent counter account.
+  const incrementIx = await getIncrementInstructionAsync({ authority });
+  const promise = pipe(
+    await createDefaultTransaction(client, authority),
+    (tx) => appendTransactionInstruction(incrementIx, tx),
+    (tx) => signAndSendTransaction(client, tx)
+  );
+
+  // Then we expect the program to throw an error.
+  const error = await t.throwsAsync<Error & { data: { logs: string[] } }>(
+    promise,
+    { message: /Error processing Instruction 0: custom program error: 0x2/ }
+  );
+  t.regex(
+    error.data.logs.join('\n'),
+    /Account "counter" \[.+\] expected program owner \[MyProgram1111111111111111111111111111111111\], got \[11111111111111111111111111111111\]/
+  );
+});
+
+test('it cannot increment a counter that belongs to another authority', async (t) => {
+  // Given two authority key pairs such that
+  // only one of them (authority A) is associated with a counter account.
+  const client = createDefaultSolanaClient();
+  const [authorityA, authorityB] = await Promise.all([
+    generateKeyPairSignerWithSol(client),
+    generateKeyPairSignerWithSol(client),
+  ]);
+  const [counterPda] = await createCounterForAuthority(client, authorityA);
+
+  // When authority B tries to increment the counter account of authority A.
+  const incrementIx = getIncrementInstruction({
+    authority: authorityB,
+    counter: counterPda,
+  });
+  const promise = pipe(
+    await createDefaultTransaction(client, authorityB),
+    (tx) => appendTransactionInstruction(incrementIx, tx),
+    (tx) => signAndSendTransaction(client, tx)
+  );
+
+  // Then we expect the program to throw an error.
+  const error = await t.throwsAsync<Error & { data: { logs: string[] } }>(
+    promise,
+    { message: /Error processing Instruction 0: custom program error: 0x3/ }
+  );
+  t.regex(
+    error.data.logs.join('\n'),
+    /Account "counter" \[.+\] is an invalid PDA. Expected the following valid PDA \[.+\]/
+  );
+});

+ 9 - 0
template/clients/js/clients/js/tsconfig.declarations.json

@@ -0,0 +1,9 @@
+{
+  "compilerOptions": {
+      "declaration": true,
+      "declarationMap": true,
+      "emitDeclarationOnly": true,
+      "outDir": "./dist/types",
+  },
+  "extends": "./tsconfig.json"
+}

+ 24 - 0
template/clients/js/clients/js/tsconfig.json

@@ -0,0 +1,24 @@
+{
+  "$schema": "https://json.schemastore.org/tsconfig",
+  "compilerOptions": {
+      "composite": false,
+      "declaration": true,
+      "declarationMap": true,
+      "esModuleInterop": true,
+      "forceConsistentCasingInFileNames": true,
+      "inlineSources": false,
+      "isolatedModules": true,
+      "module": "commonjs",
+      "moduleResolution": "node",
+      "noFallthroughCasesInSwitch": true,
+      "noUnusedLocals": true,
+      "noUnusedParameters": true,
+      "outDir": "./dist",
+      "preserveWatchOutput": true,
+      "skipLibCheck": true,
+      "strict": true,
+      "target": "ESNext"
+  },
+  "exclude": ["node_modules"],
+  "include": ["src", "test"]
+}

+ 27 - 0
template/clients/js/clients/js/tsup.config.ts

@@ -0,0 +1,27 @@
+import { env } from 'node:process';
+import path from 'path';
+import { defineConfig, Options } from 'tsup';
+
+const SHARED_OPTIONS: Options = {
+  define: { __VERSION__: `"${env.npm_package_version}"` },
+  entry: ['./src/index.ts'],
+  inject: [path.resolve(__dirname, 'env-shim.ts')],
+  outDir: './dist/src',
+  sourcemap: true,
+  treeshake: true,
+};
+
+export default defineConfig(() => [
+  // Source.
+  { ...SHARED_OPTIONS, format: 'cjs' },
+  { ...SHARED_OPTIONS, format: 'esm' },
+
+  // Tests.
+  {
+    ...SHARED_OPTIONS,
+    bundle: false,
+    entry: ['./test/*.ts'],
+    format: 'cjs',
+    outDir: './dist/test',
+  },
+]);

+ 6 - 0
template/clients/js/clients/js/typedoc.json

@@ -0,0 +1,6 @@
+{
+  "entryPoints": ["src/index.ts"],
+  "includeVersion": true,
+  "readme": "none",
+  "out": "docs"
+}

+ 5 - 0
template/clients/js/package.json

@@ -0,0 +1,5 @@
+{
+  "scripts": {
+    "clients:js:test": "./scripts/client/test-js.mjs"
+  }
+}

+ 12 - 0
template/clients/js/scripts/client/test-js.mjs

@@ -0,0 +1,12 @@
+#!/usr/bin/env zx
+import "zx/globals";
+import { workingDirectory } from "../utils.mjs";
+
+// Start the local validator if it's not already running.
+await $`pnpm validator`;
+
+// Build the client and run the tests.
+cd(path.join(workingDirectory, "clients", "js"));
+await $`pnpm install`;
+await $`pnpm build`;
+await $`pnpm test ${argv._}`;

+ 3 - 0
template/clients/rust/Cargo.toml

@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = ["clients/rust", "program"]

+ 24 - 0
template/clients/rust/clients/rust/Cargo.toml

@@ -0,0 +1,24 @@
+[package]
+name = "acme-counter-client"
+version = "0.1.0"
+edition = "2021"
+readme = "README.md"
+license-file = "../../LICENSE"
+
+[features]
+test-sbf = []
+serde = ["dep:serde", "dep:serde_with"]
+
+[dependencies]
+borsh = "^0.10"
+num-derive = "^0.3"
+num-traits = "^0.2"
+serde = { version = "^1.0", features = ["derive"], optional = true }
+serde_with = { version = "^3.0", optional = true }
+solana-program = "~1.16"
+thiserror = "^1.0"
+
+[dev-dependencies]
+assert_matches = "1.5.0"
+solana-program-test = "~1.16"
+solana-sdk = "~1.16"

+ 3 - 0
template/clients/rust/clients/rust/README.md

@@ -0,0 +1,3 @@
+# Rust client
+
+A generated Rust library for the Counter program.

+ 4 - 0
template/clients/rust/clients/rust/src/lib.rs

@@ -0,0 +1,4 @@
+mod generated;
+
+pub use generated::programs::ACME_COUNTER_ID as ID;
+pub use generated::*;

+ 47 - 0
template/clients/rust/clients/rust/tests/create.rs

@@ -0,0 +1,47 @@
+#![cfg(feature = "test-sbf")]
+
+use acme_counter_client::{accounts::Counter, instructions::CreateBuilder};
+use borsh::BorshDeserialize;
+use solana_program_test::{tokio, ProgramTest};
+use solana_sdk::{signature::Signer, transaction::Transaction};
+
+#[tokio::test]
+async fn create() {
+    let mut context = ProgramTest::new("acme_counter", acme_counter_client::ID, None)
+        .start_with_context()
+        .await;
+
+    // Given a PDA derived from the payer's public key.
+
+    let address = Counter::find_pda(&context.payer.pubkey()).0;
+
+    let ix = CreateBuilder::new()
+        .counter(address)
+        .authority(context.payer.pubkey())
+        .payer(context.payer.pubkey())
+        .instruction();
+
+    // When we create a new counter.
+
+    let tx = Transaction::new_signed_with_payer(
+        &[ix],
+        Some(&context.payer.pubkey()),
+        &[&context.payer],
+        context.last_blockhash,
+    );
+    context.banks_client.process_transaction(tx).await.unwrap();
+
+    // Then an account was created with the correct data.
+
+    let account = context.banks_client.get_account(address).await.unwrap();
+
+    assert!(account.is_some());
+
+    let account = account.unwrap();
+    assert_eq!(account.data.len(), Counter::LEN);
+
+    let mut account_data = account.data.as_ref();
+    let counter = Counter::deserialize(&mut account_data).unwrap();
+    assert_eq!(counter.authority, context.payer.pubkey());
+    assert_eq!(counter.value, 0);
+}

+ 5 - 0
template/clients/rust/package.json

@@ -0,0 +1,5 @@
+{
+  "scripts": {
+    "clients:rust:test": "./scripts/client/test-rust.mjs"
+  }
+}

+ 15 - 0
template/clients/rust/scripts/client/test-rust.mjs

@@ -0,0 +1,15 @@
+#!/usr/bin/env zx
+import "zx/globals";
+import { workingDirectory } from "../utils.mjs";
+
+// Start the local validator if it's not already running.
+await $`pnpm validator`;
+
+// Run the tests.
+cd(path.join(workingDirectory, "clients", "rust"));
+const hasSolfmt = await which("solfmt", { nothrow: true });
+if (hasSolfmt) {
+  await $`cargo test-sbf ${argv._} 2>&1 | solfmt`;
+} else {
+  await $`cargo test-sbf ${argv._}`;
+}

+ 25 - 0
template/programs/counter-shank/program/Cargo.toml

@@ -0,0 +1,25 @@
+[package]
+name = "acme-counter"
+version = "0.1.0"
+edition = "2021"
+readme = "./README.md"
+license-file = "../LICENSE"
+publish = false
+
+[package.metadata.solana]
+program-id = "MyProgram1111111111111111111111111111111111"
+program-dependencies = [
+  "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s",
+  "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV",
+]
+
+[lib]
+crate-type = ["cdylib", "lib"]
+
+[dependencies]
+borsh = "^0.10"
+shank = "^0.4.2"
+num-derive = "^0.3"
+num-traits = "^0.2"
+solana-program = "~1.16"
+thiserror = "^1.0"

+ 3 - 0
template/programs/counter-shank/program/README.md

@@ -0,0 +1,3 @@
+# Counter program
+
+Simple counter program as an example.

+ 184 - 0
template/programs/counter-shank/program/idl.json

@@ -0,0 +1,184 @@
+{
+  "version": "0.1.0",
+  "name": "acme_counter",
+  "instructions": [
+    {
+      "name": "Create",
+      "accounts": [
+        {
+          "name": "counter",
+          "isMut": true,
+          "isSigner": false,
+          "docs": [
+            "The program derived address of the counter account to create (seeds: ['counter', authority])"
+          ]
+        },
+        {
+          "name": "authority",
+          "isMut": false,
+          "isSigner": true,
+          "docs": [
+            "The authority of the counter"
+          ]
+        },
+        {
+          "name": "payer",
+          "isMut": true,
+          "isSigner": true,
+          "docs": [
+            "The account paying for the storage fees"
+          ]
+        },
+        {
+          "name": "systemProgram",
+          "isMut": false,
+          "isSigner": false,
+          "docs": [
+            "The system program"
+          ]
+        }
+      ],
+      "args": [],
+      "discriminant": {
+        "type": "u8",
+        "value": 0
+      }
+    },
+    {
+      "name": "Increment",
+      "accounts": [
+        {
+          "name": "counter",
+          "isMut": true,
+          "isSigner": false,
+          "docs": [
+            "The program derived address of the counter account to increment (seeds: ['counter', authority])"
+          ]
+        },
+        {
+          "name": "authority",
+          "isMut": false,
+          "isSigner": true,
+          "docs": [
+            "The authority of the counter"
+          ]
+        }
+      ],
+      "args": [
+        {
+          "name": "amount",
+          "type": {
+            "option": "u32"
+          }
+        }
+      ],
+      "discriminant": {
+        "type": "u8",
+        "value": 1
+      }
+    }
+  ],
+  "accounts": [
+    {
+      "name": "Counter",
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "key",
+            "type": {
+              "defined": "Key"
+            }
+          },
+          {
+            "name": "authority",
+            "type": "publicKey"
+          },
+          {
+            "name": "value",
+            "type": "u32"
+          }
+        ]
+      }
+    }
+  ],
+  "types": [
+    {
+      "name": "Key",
+      "type": {
+        "kind": "enum",
+        "variants": [
+          {
+            "name": "Uninitialized"
+          },
+          {
+            "name": "Counter"
+          }
+        ]
+      }
+    }
+  ],
+  "errors": [
+    {
+      "code": 0,
+      "name": "DeserializationError",
+      "msg": "Error deserializing an account"
+    },
+    {
+      "code": 1,
+      "name": "SerializationError",
+      "msg": "Error serializing an account"
+    },
+    {
+      "code": 2,
+      "name": "InvalidProgramOwner",
+      "msg": "Invalid program owner. This likely mean the provided account does not exist"
+    },
+    {
+      "code": 3,
+      "name": "InvalidPda",
+      "msg": "Invalid PDA derivation"
+    },
+    {
+      "code": 4,
+      "name": "ExpectedEmptyAccount",
+      "msg": "Expected empty account"
+    },
+    {
+      "code": 5,
+      "name": "ExpectedNonEmptyAccount",
+      "msg": "Expected non empty account"
+    },
+    {
+      "code": 6,
+      "name": "ExpectedSignerAccount",
+      "msg": "Expected signer account"
+    },
+    {
+      "code": 7,
+      "name": "ExpectedWritableAccount",
+      "msg": "Expected writable account"
+    },
+    {
+      "code": 8,
+      "name": "AccountMismatch",
+      "msg": "Account mismatch"
+    },
+    {
+      "code": 9,
+      "name": "InvalidAccountKey",
+      "msg": "Invalid account key"
+    },
+    {
+      "code": 10,
+      "name": "NumericalOverflow",
+      "msg": "Numerical overflow"
+    }
+  ],
+  "metadata": {
+    "origin": "shank",
+    "address": "MyProgram1111111111111111111111111111111111",
+    "binaryVersion": "0.4.2",
+    "libVersion": "^0.4.2"
+  }
+}

+ 8 - 0
template/programs/counter-shank/program/rustfmt.toml

@@ -0,0 +1,8 @@
+max_width = 100
+imports_indent = "Block"
+imports_layout = "Mixed"
+imports_granularity = "Crate"
+group_imports = "Preserve"
+reorder_imports = true
+reorder_modules = true
+reorder_impl_items = false

+ 137 - 0
template/programs/counter-shank/program/src/assertions.rs

@@ -0,0 +1,137 @@
+use crate::{error::CounterError, state::Key};
+use solana_program::{
+    account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError,
+    pubkey::Pubkey,
+};
+
+/// Assert that the given account is owned by the given program.
+pub fn assert_program_owner(
+    account_name: &str,
+    account: &AccountInfo,
+    owner: &Pubkey,
+) -> ProgramResult {
+    if account.owner != owner {
+        msg!(
+            "Account \"{}\" [{}] expected program owner [{}], got [{}]",
+            account_name,
+            account.key,
+            owner,
+            account.owner
+        );
+        Err(CounterError::InvalidProgramOwner.into())
+    } else {
+        Ok(())
+    }
+}
+
+/// Assert the derivation of the seeds against the given account and return the bump seed.
+pub fn assert_pda(
+    account_name: &str,
+    account: &AccountInfo,
+    program_id: &Pubkey,
+    seeds: &[&[u8]],
+) -> Result<u8, ProgramError> {
+    let (key, bump) = Pubkey::find_program_address(seeds, program_id);
+    if *account.key != key {
+        msg!(
+            "Account \"{}\" [{}] is an invalid PDA. Expected the following valid PDA [{}]",
+            account_name,
+            account.key,
+            key,
+        );
+        return Err(CounterError::InvalidPda.into());
+    }
+    Ok(bump)
+}
+
+/// Assert that the given account is empty.
+pub fn assert_empty(account_name: &str, account: &AccountInfo) -> ProgramResult {
+    if !account.data_is_empty() {
+        msg!(
+            "Account \"{}\" [{}] must be empty",
+            account_name,
+            account.key,
+        );
+        Err(CounterError::ExpectedEmptyAccount.into())
+    } else {
+        Ok(())
+    }
+}
+
+/// Assert that the given account is non empty.
+pub fn assert_non_empty(account_name: &str, account: &AccountInfo) -> ProgramResult {
+    if account.data_is_empty() {
+        msg!(
+            "Account \"{}\" [{}] must not be empty",
+            account_name,
+            account.key,
+        );
+        Err(CounterError::ExpectedNonEmptyAccount.into())
+    } else {
+        Ok(())
+    }
+}
+
+/// Assert that the given account is a signer.
+pub fn assert_signer(account_name: &str, account: &AccountInfo) -> ProgramResult {
+    if !account.is_signer {
+        msg!(
+            "Account \"{}\" [{}] must be a signer",
+            account_name,
+            account.key,
+        );
+        Err(CounterError::ExpectedSignerAccount.into())
+    } else {
+        Ok(())
+    }
+}
+
+/// Assert that the given account is writable.
+pub fn assert_writable(account_name: &str, account: &AccountInfo) -> ProgramResult {
+    if !account.is_writable {
+        msg!(
+            "Account \"{}\" [{}] must be writable",
+            account_name,
+            account.key,
+        );
+        Err(CounterError::ExpectedWritableAccount.into())
+    } else {
+        Ok(())
+    }
+}
+
+/// Assert that the given account matches the given public key.
+pub fn assert_same_pubkeys(
+    account_name: &str,
+    account: &AccountInfo,
+    expected: &Pubkey,
+) -> ProgramResult {
+    if account.key != expected {
+        msg!(
+            "Account \"{}\" [{}] must match the following public key [{}]",
+            account_name,
+            account.key,
+            expected
+        );
+        Err(CounterError::AccountMismatch.into())
+    } else {
+        Ok(())
+    }
+}
+
+/// Assert that the given account has the expected account key.
+pub fn assert_account_key(account_name: &str, account: &AccountInfo, key: Key) -> ProgramResult {
+    let key_number = key as u8;
+    if account.data_len() <= 1 || account.try_borrow_data()?[0] != key_number {
+        msg!(
+            "Account \"{}\" [{}] expected account key [{}], got [{}]",
+            account_name,
+            account.key,
+            key_number,
+            account.try_borrow_data()?[0]
+        );
+        Err(CounterError::InvalidAccountKey.into())
+    } else {
+        Ok(())
+    }
+}

+ 20 - 0
template/programs/counter-shank/program/src/entrypoint.rs

@@ -0,0 +1,20 @@
+use solana_program::{
+    account_info::AccountInfo, entrypoint, entrypoint::ProgramResult,
+    program_error::PrintProgramError, pubkey::Pubkey,
+};
+
+use crate::{error::CounterError, processor};
+
+entrypoint!(process_instruction);
+fn process_instruction<'a>(
+    program_id: &'a Pubkey,
+    accounts: &'a [AccountInfo<'a>],
+    instruction_data: &[u8],
+) -> ProgramResult {
+    if let Err(error) = processor::process_instruction(program_id, accounts, instruction_data) {
+        // catch the error so we can print it
+        error.print::<CounterError>();
+        return Err(error);
+    }
+    Ok(())
+}

+ 62 - 0
template/programs/counter-shank/program/src/error.rs

@@ -0,0 +1,62 @@
+use num_derive::FromPrimitive;
+use solana_program::{
+    decode_error::DecodeError,
+    msg,
+    program_error::{PrintProgramError, ProgramError},
+};
+use thiserror::Error;
+
+#[derive(Error, Clone, Debug, Eq, PartialEq, FromPrimitive)]
+pub enum CounterError {
+    /// 0 - Error deserializing an account
+    #[error("Error deserializing an account")]
+    DeserializationError,
+    /// 1 - Error serializing an account
+    #[error("Error serializing an account")]
+    SerializationError,
+    /// 2 - Invalid program owner
+    #[error("Invalid program owner. This likely mean the provided account does not exist")]
+    InvalidProgramOwner,
+    /// 3 - Invalid PDA derivation
+    #[error("Invalid PDA derivation")]
+    InvalidPda,
+    /// 4 - Expected empty account
+    #[error("Expected empty account")]
+    ExpectedEmptyAccount,
+    /// 5 - Expected non empty account
+    #[error("Expected non empty account")]
+    ExpectedNonEmptyAccount,
+    /// 6 - Expected signer account
+    #[error("Expected signer account")]
+    ExpectedSignerAccount,
+    /// 7 - Expected writable account
+    #[error("Expected writable account")]
+    ExpectedWritableAccount,
+    /// 8 - Account mismatch
+    #[error("Account mismatch")]
+    AccountMismatch,
+    /// 9 - Invalid account key
+    #[error("Invalid account key")]
+    InvalidAccountKey,
+    /// 10 - Numerical overflow
+    #[error("Numerical overflow")]
+    NumericalOverflow,
+}
+
+impl PrintProgramError for CounterError {
+    fn print<E>(&self) {
+        msg!(&self.to_string());
+    }
+}
+
+impl From<CounterError> for ProgramError {
+    fn from(e: CounterError) -> Self {
+        ProgramError::Custom(e as u32)
+    }
+}
+
+impl<T> DecodeError<T> for CounterError {
+    fn type_of() -> &'static str {
+        "Mpl Project Name Error"
+    }
+}

+ 18 - 0
template/programs/counter-shank/program/src/instruction.rs

@@ -0,0 +1,18 @@
+use borsh::{BorshDeserialize, BorshSerialize};
+use shank::{ShankContext, ShankInstruction};
+
+#[derive(BorshDeserialize, BorshSerialize, Clone, Debug, ShankContext, ShankInstruction)]
+#[rustfmt::skip]
+pub enum CounterInstruction {
+    /// Creates the counter account derived from the provided authority.
+    #[account(0, writable, name="counter", desc = "The program derived address of the counter account to create (seeds: ['counter', authority])")]
+    #[account(1, signer, name="authority", desc = "The authority of the counter")]
+    #[account(2, writable, signer, name="payer", desc = "The account paying for the storage fees")]
+    #[account(3, name="system_program", desc = "The system program")]
+    Create,
+
+    /// Increments the counter by the provided amount or 1 if no amount is provided.
+    #[account(0, writable, name="counter", desc = "The program derived address of the counter account to increment (seeds: ['counter', authority])")]
+    #[account(1, signer, name="authority", desc = "The authority of the counter")]
+    Increment { amount: Option<u32> },
+}

+ 11 - 0
template/programs/counter-shank/program/src/lib.rs

@@ -0,0 +1,11 @@
+pub mod assertions;
+pub mod entrypoint;
+pub mod error;
+pub mod instruction;
+pub mod processor;
+pub mod state;
+pub mod utils;
+
+pub use solana_program;
+
+solana_program::declare_id!("MyProgram1111111111111111111111111111111111");

+ 97 - 0
template/programs/counter-shank/program/src/processor.rs

@@ -0,0 +1,97 @@
+use borsh::BorshDeserialize;
+use solana_program::{
+    account_info::AccountInfo, entrypoint::ProgramResult, msg, pubkey::Pubkey, system_program,
+};
+
+use crate::assertions::{
+    assert_pda, assert_program_owner, assert_same_pubkeys, assert_signer, assert_writable,
+};
+use crate::instruction::accounts::{CreateAccounts, IncrementAccounts};
+use crate::instruction::CounterInstruction;
+use crate::state::{Counter, Key};
+use crate::utils::create_account;
+
+pub fn process_instruction<'a>(
+    _program_id: &Pubkey,
+    accounts: &'a [AccountInfo<'a>],
+    instruction_data: &[u8],
+) -> ProgramResult {
+    let instruction: CounterInstruction = CounterInstruction::try_from_slice(instruction_data)?;
+    match instruction {
+        CounterInstruction::Create => {
+            msg!("Instruction: Create");
+            create(accounts)
+        }
+        CounterInstruction::Increment { amount } => {
+            msg!("Instruction: Increment");
+            increment(accounts, amount)
+        }
+    }
+}
+
+fn create<'a>(accounts: &'a [AccountInfo<'a>]) -> ProgramResult {
+    // Accounts.
+    let ctx = CreateAccounts::context(accounts)?;
+
+    // Guards.
+    let counter_bump = assert_pda(
+        "counter",
+        ctx.accounts.counter,
+        &crate::ID,
+        &Counter::seeds(ctx.accounts.authority.key),
+    )?;
+    assert_signer("authority", ctx.accounts.authority)?;
+    assert_signer("payer", ctx.accounts.payer)?;
+    assert_writable("payer", ctx.accounts.payer)?;
+    assert_same_pubkeys(
+        "system_program",
+        ctx.accounts.system_program,
+        &system_program::id(),
+    )?;
+
+    // Do nothing if the domain already exists.
+    if !ctx.accounts.counter.data_is_empty() {
+        return Ok(());
+    }
+
+    // Create Counter PDA.
+    let counter = Counter {
+        key: Key::Counter,
+        authority: *ctx.accounts.authority.key,
+        value: 0,
+    };
+    let mut seeds = Counter::seeds(ctx.accounts.authority.key);
+    let bump = [counter_bump];
+    seeds.push(&bump);
+    create_account(
+        ctx.accounts.counter,
+        ctx.accounts.payer,
+        ctx.accounts.system_program,
+        Counter::LEN,
+        &crate::ID,
+        Some(&[&seeds]),
+    )?;
+
+    counter.save(ctx.accounts.counter)
+}
+
+fn increment<'a>(accounts: &'a [AccountInfo<'a>], amount: Option<u32>) -> ProgramResult {
+    // Accounts.
+    let ctx = IncrementAccounts::context(accounts)?;
+
+    // Guards.
+    assert_signer("authority", ctx.accounts.authority)?;
+    assert_pda(
+        "counter",
+        ctx.accounts.counter,
+        &crate::ID,
+        &Counter::seeds(ctx.accounts.authority.key),
+    )?;
+    assert_program_owner("counter", ctx.accounts.counter, &crate::ID)?;
+    let mut counter = Counter::load(ctx.accounts.counter)?;
+    assert_same_pubkeys("authority", ctx.accounts.authority, &counter.authority)?;
+
+    // Increment Counter PDA.
+    counter.value += amount.unwrap_or(1);
+    counter.save(ctx.accounts.counter)
+}

+ 50 - 0
template/programs/counter-shank/program/src/state.rs

@@ -0,0 +1,50 @@
+use borsh::{BorshDeserialize, BorshSerialize};
+use shank::ShankAccount;
+use solana_program::account_info::AccountInfo;
+use solana_program::entrypoint::ProgramResult;
+use solana_program::msg;
+use solana_program::program_error::ProgramError;
+use solana_program::pubkey::Pubkey;
+
+use crate::error::CounterError;
+
+#[derive(Clone, BorshSerialize, BorshDeserialize, Debug)]
+pub enum Key {
+    Uninitialized,
+    Counter,
+}
+
+#[repr(C)]
+#[derive(Clone, BorshSerialize, BorshDeserialize, Debug, ShankAccount)]
+pub struct Counter {
+    pub key: Key,
+    pub authority: Pubkey,
+    pub value: u32,
+}
+
+impl Counter {
+    pub const LEN: usize = 1 + 32 + 4;
+
+    pub fn seeds(authority: &Pubkey) -> Vec<&[u8]> {
+        vec!["counter".as_bytes(), authority.as_ref()]
+    }
+
+    pub fn find_pda(authority: &Pubkey) -> (Pubkey, u8) {
+        Pubkey::find_program_address(&Self::seeds(authority), &crate::ID)
+    }
+
+    pub fn load(account: &AccountInfo) -> Result<Self, ProgramError> {
+        let mut bytes: &[u8] = &(*account.data).borrow();
+        Counter::deserialize(&mut bytes).map_err(|error| {
+            msg!("Error: {}", error);
+            CounterError::DeserializationError.into()
+        })
+    }
+
+    pub fn save(&self, account: &AccountInfo) -> ProgramResult {
+        borsh::to_writer(&mut account.data.borrow_mut()[..], self).map_err(|error| {
+            msg!("Error: {}", error);
+            CounterError::SerializationError.into()
+        })
+    }
+}

+ 123 - 0
template/programs/counter-shank/program/src/utils.rs

@@ -0,0 +1,123 @@
+use solana_program::{
+    account_info::AccountInfo,
+    entrypoint::ProgramResult,
+    program::{invoke, invoke_signed},
+    program_error::ProgramError,
+    pubkey::Pubkey,
+    rent::Rent,
+    system_instruction,
+    sysvar::Sysvar,
+};
+
+use crate::error::CounterError;
+
+/// Create a new account from the given size.
+#[inline(always)]
+pub fn create_account<'a>(
+    target_account: &AccountInfo<'a>,
+    funding_account: &AccountInfo<'a>,
+    system_program: &AccountInfo<'a>,
+    size: usize,
+    owner: &Pubkey,
+    signer_seeds: Option<&[&[&[u8]]]>,
+) -> ProgramResult {
+    let rent = Rent::get()?;
+    let lamports: u64 = rent.minimum_balance(size);
+
+    invoke_signed(
+        &system_instruction::create_account(
+            funding_account.key,
+            target_account.key,
+            lamports,
+            size as u64,
+            owner,
+        ),
+        &[
+            funding_account.clone(),
+            target_account.clone(),
+            system_program.clone(),
+        ],
+        signer_seeds.unwrap_or(&[]),
+    )
+}
+
+/// Resize an account using realloc, lifted from Solana Cookbook.
+#[inline(always)]
+pub fn realloc_account<'a>(
+    target_account: &AccountInfo<'a>,
+    funding_account: &AccountInfo<'a>,
+    system_program: &AccountInfo<'a>,
+    new_size: usize,
+    refund: bool,
+) -> ProgramResult {
+    let rent = Rent::get()?;
+    let old_minimum_balance = rent.minimum_balance(target_account.data_len());
+    let new_minimum_balance = rent.minimum_balance(new_size);
+    let lamports_diff = new_minimum_balance.abs_diff(old_minimum_balance);
+
+    if new_minimum_balance > old_minimum_balance {
+        invoke(
+            &system_instruction::transfer(funding_account.key, target_account.key, lamports_diff),
+            &[
+                funding_account.clone(),
+                target_account.clone(),
+                system_program.clone(),
+            ],
+        )?;
+    } else if refund {
+        transfer_lamports_from_pdas(target_account, funding_account, lamports_diff)?;
+    }
+
+    target_account.realloc(new_size, false)
+}
+
+/// Close an account.
+#[inline(always)]
+pub fn close_account<'a>(
+    target_account: &AccountInfo<'a>,
+    receiving_account: &AccountInfo<'a>,
+) -> ProgramResult {
+    let dest_starting_lamports = receiving_account.lamports();
+    **receiving_account.lamports.borrow_mut() = dest_starting_lamports
+        .checked_add(target_account.lamports())
+        .unwrap();
+    **target_account.lamports.borrow_mut() = 0;
+
+    let mut src_data = target_account.data.borrow_mut();
+    src_data.fill(0);
+
+    Ok(())
+}
+
+/// Transfer lamports.
+#[inline(always)]
+pub fn transfer_lamports<'a>(
+    from: &AccountInfo<'a>,
+    to: &AccountInfo<'a>,
+    lamports: u64,
+    signer_seeds: Option<&[&[&[u8]]]>,
+) -> ProgramResult {
+    invoke_signed(
+        &system_instruction::transfer(from.key, to.key, lamports),
+        &[from.clone(), to.clone()],
+        signer_seeds.unwrap_or(&[]),
+    )
+}
+
+pub fn transfer_lamports_from_pdas<'a>(
+    from: &AccountInfo<'a>,
+    to: &AccountInfo<'a>,
+    lamports: u64,
+) -> ProgramResult {
+    **from.lamports.borrow_mut() = from
+        .lamports()
+        .checked_sub(lamports)
+        .ok_or::<ProgramError>(CounterError::NumericalOverflow.into())?;
+
+    **to.lamports.borrow_mut() = to
+        .lamports()
+        .checked_add(lamports)
+        .ok_or::<ProgramError>(CounterError::NumericalOverflow.into())?;
+
+    Ok(())
+}

+ 10 - 0
tsconfig.json

@@ -0,0 +1,10 @@
+{
+  "extends": "@tsconfig/node20/tsconfig.json",
+  "include": ["index.ts", "utils/**/*"],
+  "compilerOptions": {
+    "strict": false,
+    "resolveJsonModule": true,
+    "moduleResolution": "Bundler",
+    "module": "ESNext"
+  }
+}

+ 23 - 0
utils/deepMerge.ts

@@ -0,0 +1,23 @@
+const isObject = (val: unknown): val is object =>
+  val && typeof val === "object";
+
+const mergeArrayWithDedupe = (a: any[], b: any[]) =>
+  Array.from(new Set([...a, ...b]));
+
+/** Recursively merge the content of the new object to the existing one. */
+export function deepMerge(target: object, obj: object): object {
+  for (const key of Object.keys(obj)) {
+    const oldVal = target[key];
+    const newVal = obj[key];
+
+    if (Array.isArray(oldVal) && Array.isArray(newVal)) {
+      target[key] = mergeArrayWithDedupe(oldVal, newVal);
+    } else if (isObject(oldVal) && isObject(newVal)) {
+      target[key] = deepMerge(oldVal, newVal);
+    } else {
+      target[key] = newVal;
+    }
+  }
+
+  return target;
+}

+ 43 - 0
utils/directoryTraverse.ts

@@ -0,0 +1,43 @@
+import * as fs from "node:fs";
+import * as path from "node:path";
+
+export function preOrderDirectoryTraverse(
+  dir: string,
+  dirCallback: (dir: string) => void,
+  fileCallback: (file: string) => void
+) {
+  for (const filename of fs.readdirSync(dir)) {
+    if (filename === ".git") {
+      continue;
+    }
+    const fullpath = path.resolve(dir, filename);
+    if (fs.lstatSync(fullpath).isDirectory()) {
+      dirCallback(fullpath);
+      // in case the dirCallback removes the directory entirely
+      if (fs.existsSync(fullpath)) {
+        preOrderDirectoryTraverse(fullpath, dirCallback, fileCallback);
+      }
+      continue;
+    }
+    fileCallback(fullpath);
+  }
+}
+
+export function postOrderDirectoryTraverse(
+  dir: string,
+  dirCallback: (dir: string) => void,
+  fileCallback: (file: string) => void
+) {
+  for (const filename of fs.readdirSync(dir)) {
+    if (filename === ".git") {
+      continue;
+    }
+    const fullpath = path.resolve(dir, filename);
+    if (fs.lstatSync(fullpath).isDirectory()) {
+      postOrderDirectoryTraverse(fullpath, dirCallback, fileCallback);
+      dirCallback(fullpath);
+      continue;
+    }
+    fileCallback(fullpath);
+  }
+}

+ 17 - 0
utils/generateReadme.ts

@@ -0,0 +1,17 @@
+import { RenderContext } from "./getRenderContext";
+import { titleCase } from "./strings";
+
+export function generateReadme(ctx: RenderContext): string {
+  return `# ${titleCase(ctx.programName)}
+
+TODO
+
+\`\`\`sh
+${ctx.getCommand("run", "programs:build")}
+${ctx.getCommand("run", "programs:test")}
+${ctx.getCommand("run", "programs:format")}
+${ctx.getCommand("run", "programs:lint")}
+\`\`\`
+
+TODO`;
+}

+ 20 - 0
utils/getBanner.ts

@@ -0,0 +1,20 @@
+import chalk from "chalk";
+import gradient from "gradient-string";
+
+export function getBanner() {
+  const textBanner = "Create Solana Program";
+  const gradientBanner = chalk.bold(
+    gradient(["#89d7c8", "#dc7a8b"])(textBanner, {
+      interpolation: "hsv",
+      hsvSpin: "long",
+    })
+  );
+
+  return process.stdout.isTTY && process.stdout.getColorDepth() > 8
+    ? gradientBanner
+    : textBanner;
+}
+
+export function consoleLogBanner() {
+  console.log(`\n${getBanner()}\n`);
+}

+ 330 - 0
utils/getInputs.ts

@@ -0,0 +1,330 @@
+import chalk from "chalk";
+import * as fs from "node:fs";
+import { parseArgs } from "node:util";
+import prompts from "prompts";
+
+import { Language } from "./getLanguage";
+import { kebabCase } from "./strings";
+
+export const allClients = ["js", "rust"] as const;
+export type Client = (typeof allClients)[number];
+
+export type Inputs = {
+  jsClient: boolean;
+  jsClientPackageName: string;
+  organizationName: string;
+  programAddress: string;
+  programCrateName: string;
+  programFramework: "shank" | "anchor";
+  programName: string;
+  rustClient: boolean;
+  rustClientCrateName: string;
+  shouldOverride: boolean;
+  targetDirectoryName: string;
+  useDefaults: boolean;
+};
+
+export async function getInputs(language: Language): Promise<Inputs> {
+  const argInputs = getInputsFromArgs();
+  const defaultInputs = getDefaultInputs(argInputs);
+
+  if (argInputs.useDefaults) {
+    return defaultInputs;
+  }
+
+  return getInputsFromPrompts(language, argInputs);
+}
+
+async function getInputsFromPrompts(
+  language: Language,
+  argInputs: Partial<Inputs>
+): Promise<Inputs> {
+  type PromptInputs = {
+    programName?: string;
+    shouldOverride?: boolean;
+    organizationName?: string;
+    programCrateName?: string;
+    programAddress?: string;
+    programFramework?: "shank" | "anchor";
+    clients?: Array<"js" | "rust">;
+    jsClientPackageName?: string;
+    rustClientCrateName?: string;
+  };
+
+  let defaultInputs = getDefaultInputs(argInputs);
+
+  function parsePromptInputs(promptInputs: PromptInputs): Inputs {
+    const inputs = {} as Partial<Inputs>;
+
+    if (promptInputs.programName) inputs.programName = promptInputs.programName;
+    if (promptInputs.shouldOverride !== undefined)
+      inputs.shouldOverride = promptInputs.shouldOverride;
+    if (promptInputs.organizationName)
+      inputs.organizationName = promptInputs.organizationName;
+    if (promptInputs.programCrateName)
+      inputs.programCrateName = promptInputs.programCrateName;
+    if (promptInputs.programAddress)
+      inputs.programAddress = promptInputs.programAddress;
+    if (promptInputs.programFramework)
+      inputs.programFramework = promptInputs.programFramework;
+    if (promptInputs.clients !== undefined) {
+      inputs.jsClient = promptInputs.clients.includes("js");
+      inputs.rustClient = promptInputs.clients.includes("rust");
+    }
+    if (promptInputs.jsClientPackageName)
+      inputs.jsClientPackageName = promptInputs.jsClientPackageName;
+    if (promptInputs.rustClientCrateName)
+      inputs.rustClientCrateName = promptInputs.rustClientCrateName;
+
+    return getDefaultInputs({ ...argInputs, ...inputs });
+  }
+
+  try {
+    const promptInputs: PromptInputs = await prompts(
+      [
+        {
+          name: "programName",
+          type: argInputs.targetDirectoryName ? null : "text",
+          message: language.programName.message,
+          initial: () => defaultInputs.programName,
+        },
+        {
+          name: "shouldOverride",
+          type: (_, values) => {
+            if (argInputs.shouldOverride) return null;
+            defaultInputs = parsePromptInputs(values);
+            return canSkipEmptying(defaultInputs.targetDirectoryName)
+              ? null
+              : "toggle";
+          },
+          message: () => {
+            const dirForPrompt =
+              defaultInputs.targetDirectoryName === "."
+                ? language.shouldOverride.dirForPrompts.current
+                : `${language.shouldOverride.dirForPrompts.target} "${defaultInputs.targetDirectoryName}"`;
+
+            return `${dirForPrompt} ${language.shouldOverride.message}`;
+          },
+          initial: false,
+          active: language.defaultToggleOptions.active,
+          inactive: language.defaultToggleOptions.inactive,
+        },
+        {
+          name: "overwriteChecker",
+          type: (_, values) => {
+            if (values.shouldOverride === false) {
+              throw new Error(
+                chalk.red("✖") + ` ${language.errors.operationCancelled}`
+              );
+            }
+            return null;
+          },
+        },
+        {
+          name: "organizationName",
+          type: argInputs.organizationName ? null : "text",
+          message: language.organizationName.message,
+          initial: () => defaultInputs.organizationName,
+        },
+        {
+          name: "programCrateName",
+          type: argInputs.programCrateName ? null : "text",
+          message: language.programCrateName.message,
+          initial: (_, values) => {
+            defaultInputs = parsePromptInputs(values);
+            return defaultInputs.programCrateName;
+          },
+        },
+        {
+          name: "programAddress",
+          type: argInputs.programAddress ? null : "text",
+          message: language.programAddress.message,
+          initial: () => defaultInputs.programAddress,
+        },
+        {
+          name: "programFramework",
+          type: argInputs.programFramework ? null : "select",
+          message: language.programFramework.message,
+          hint: language.instructions.select,
+          initial: 0,
+          choices: [
+            {
+              title: language.programFramework.selectOptions.shank.title,
+              description: language.programFramework.selectOptions.shank.desc,
+              value: "shank",
+            },
+            {
+              title: language.programFramework.selectOptions.anchor.title,
+              description: language.programFramework.selectOptions.anchor.desc,
+              value: "anchor",
+              disabled: true,
+            },
+          ],
+        },
+        {
+          name: "clients",
+          type: () => {
+            const hasSelectedClients = [
+              argInputs.jsClient,
+              argInputs.rustClient,
+            ].every((client) => typeof client === "boolean");
+            return hasSelectedClients ? null : "multiselect";
+          },
+          message: language.clients.message,
+          hint: language.clients.hint,
+          instructions: language.instructions.multiselect,
+          choices: allClients.map((client) => ({
+            title: language.clients.selectOptions[client].title,
+            description: language.clients.selectOptions[client].desc,
+            value: client,
+            selected: true,
+          })),
+        },
+        {
+          name: "jsClientPackageName",
+          type: (_, values) => {
+            if (argInputs.jsClientPackageName) return null;
+            defaultInputs = parsePromptInputs(values);
+            return defaultInputs.jsClient ? "text" : null;
+          },
+          message: language.jsClientPackageName.message,
+          initial: () => defaultInputs.jsClientPackageName,
+        },
+        {
+          name: "rustClientCrateName",
+          type: (_, values) => {
+            if (argInputs.rustClientCrateName) return null;
+            defaultInputs = parsePromptInputs(values);
+            return defaultInputs.rustClient ? "text" : null;
+          },
+          message: language.rustClientCrateName.message,
+          initial: () => defaultInputs.rustClientCrateName,
+        },
+      ],
+      {
+        onCancel: () => {
+          throw new Error(
+            chalk.red("✖") + ` ${language.errors.operationCancelled}`
+          );
+        },
+      }
+    );
+    return parsePromptInputs(promptInputs);
+  } catch (cancelled) {
+    console.log(cancelled.message);
+    process.exit(1);
+  }
+}
+
+function getInputsFromArgs(): Partial<Inputs> {
+  type ArgInputs = {
+    address: string;
+    anchorProgram: boolean;
+    clients: Array<"js" | "rust">;
+    force: boolean;
+    noClients: boolean;
+    organizationName: string;
+    shankProgram: boolean;
+    useDefaults: boolean;
+    targetDirectoryName?: string;
+  };
+
+  function parseArgInputs(argInputs: ArgInputs): Partial<Inputs> {
+    const inputs = {} as Partial<Inputs>;
+
+    if (argInputs.targetDirectoryName)
+      inputs.targetDirectoryName = argInputs.targetDirectoryName;
+    if (argInputs.address) inputs.programAddress = argInputs.address;
+    if (argInputs.organizationName)
+      inputs.organizationName = kebabCase(argInputs.organizationName);
+    if (argInputs.force) inputs.shouldOverride = true;
+    if (argInputs.useDefaults) inputs.useDefaults = true;
+
+    if (argInputs.anchorProgram) {
+      inputs.programFramework = "anchor";
+    } else if (argInputs.shankProgram) {
+      inputs.programFramework = "shank";
+    }
+
+    if (argInputs.noClients) {
+      inputs.jsClient = false;
+      inputs.rustClient = false;
+    } else if (argInputs.clients) {
+      inputs.jsClient = argInputs.clients.includes("js");
+      inputs.rustClient = argInputs.clients.includes("rust");
+    }
+
+    return inputs;
+  }
+
+  const args = process.argv.slice(2);
+  const { values: options, positionals } = parseArgs({
+    args,
+    options: {
+      address: { type: "string" },
+      anchor: { type: "boolean" },
+      client: { type: "string", multiple: true },
+      default: { type: "boolean", short: "d" },
+      force: { type: "boolean" },
+      "no-clients": { type: "boolean" },
+      org: { type: "string" },
+      shank: { type: "boolean" },
+    },
+    strict: false,
+  });
+
+  return parseArgInputs({
+    address: options.address,
+    anchorProgram: options.anchor ?? false,
+    clients: options.client,
+    force: options.force ?? false,
+    noClients: options["no-clients"] ?? false,
+    organizationName: options.org,
+    shankProgram: options.shank ?? false,
+    useDefaults: options.default ?? false,
+    targetDirectoryName: positionals[0],
+  } as ArgInputs);
+}
+
+function getDefaultInputs(partialInputs: Partial<Inputs>): Inputs {
+  const organizationName = kebabCase(
+    partialInputs.organizationName ?? "solana-program"
+  );
+  const programName = kebabCase(
+    partialInputs.programName ??
+      partialInputs.targetDirectoryName ??
+      "my-program"
+  );
+
+  return {
+    jsClient: true,
+    jsClientPackageName: `@${organizationName}/${programName}`,
+    organizationName,
+    programAddress: "MyProgram1111111111111111111111111111111111",
+    programCrateName: `${organizationName}-${programName}`,
+    programFramework: "shank",
+    programName,
+    rustClient: true,
+    rustClientCrateName: `${organizationName}-${programName}-client`,
+    shouldOverride: false,
+    targetDirectoryName: programName,
+    useDefaults: false,
+    ...partialInputs,
+  };
+}
+
+function canSkipEmptying(dir: fs.PathLike) {
+  if (!fs.existsSync(dir)) {
+    return true;
+  }
+
+  const files = fs.readdirSync(dir);
+  if (files.length === 0) {
+    return true;
+  }
+  if (files.length === 1 && files[0] === ".git") {
+    return true;
+  }
+
+  return false;
+}

+ 106 - 0
utils/getLanguage.ts

@@ -0,0 +1,106 @@
+import * as fs from "node:fs";
+import * as path from "node:path";
+
+interface LanguageItem {
+  hint?: string;
+  message: string;
+  invalidMessage?: string;
+  dirForPrompts?: {
+    current: string;
+    target: string;
+  };
+  toggleOptions?: {
+    active: string;
+    inactive: string;
+  };
+  selectOptions?: {
+    [key: string]: { title: string; desc?: string };
+  };
+}
+
+export interface Language {
+  programName: LanguageItem;
+  shouldOverride: LanguageItem;
+  organizationName: LanguageItem;
+  programCrateName: LanguageItem;
+  programAddress: LanguageItem;
+  programFramework: LanguageItem;
+  clients: LanguageItem;
+  jsClientPackageName: LanguageItem;
+  rustClientCrateName: LanguageItem;
+  errors: {
+    operationCancelled: string;
+    cannotOverrideDirectory: string;
+  };
+  defaultToggleOptions: {
+    active: string;
+    inactive: string;
+  };
+  instructions: {
+    select: string;
+    multiselect: string;
+  };
+  infos: {
+    scaffolding: string;
+    done: string;
+  };
+}
+
+/**
+ *
+ * This function is used to link obtained locale with correct locale file in order to make locales reusable
+ *
+ * @param locale the obtained locale
+ * @returns locale that linked with correct name
+ */
+function linkLocale(locale: string) {
+  let linkedLocale: string;
+  try {
+    // @ts-ignore
+    linkedLocale = Intl.getCanonicalLocales(locale)[0];
+  } catch (error) {
+    console.log(`${error.toString()}\n`);
+  }
+  switch (linkedLocale) {
+    case "zh-TW":
+    case "zh-HK":
+    case "zh-MO":
+      linkedLocale = "zh-Hant";
+      break;
+    case "zh-CN":
+    case "zh-SG":
+      linkedLocale = "zh-Hans";
+      break;
+    default:
+      linkedLocale = locale;
+  }
+
+  return linkedLocale;
+}
+
+function getLocale() {
+  const shellLocale =
+    process.env.LC_ALL || // POSIX locale environment variables
+    process.env.LC_MESSAGES ||
+    process.env.LANG ||
+    Intl.DateTimeFormat().resolvedOptions().locale || // Built-in ECMA-402 support
+    "en-US"; // Default fallback
+
+  return linkLocale(shellLocale.split(".")[0].replace("_", "-"));
+}
+
+export function getLanguage() {
+  const locale = getLocale();
+
+  // Note here __dirname would not be transpiled,
+  // so it refers to the __dirname of the file `<repositoryRoot>/outfile.cjs`
+  const localesRoot = path.resolve(__dirname, "locales");
+  const languageFilePath = path.resolve(localesRoot, `${locale}.json`);
+  const doesLanguageExist = fs.existsSync(languageFilePath);
+
+  const lang: Language = doesLanguageExist
+    ? require(languageFilePath)
+    : require(path.resolve(localesRoot, "en-US.json"));
+
+  return lang;
+}

+ 31 - 0
utils/getPackageManager.ts

@@ -0,0 +1,31 @@
+export type PackageManager = "npm" | "yarn" | "pnpm";
+
+export function getPackageManager(): PackageManager {
+  // Supported package managers: pnpm > yarn > npm
+  const userAgent = process.env.npm_config_user_agent ?? "";
+  return /pnpm/.test(userAgent)
+    ? "pnpm"
+    : /yarn/.test(userAgent)
+      ? "yarn"
+      : "npm";
+}
+
+export function getPackageManagerCommand(
+  packageManager: PackageManager,
+  scriptName: string,
+  args?: string
+) {
+  if (scriptName === "install") {
+    return packageManager === "yarn" ? "yarn" : `${packageManager} install`;
+  }
+
+  if (args) {
+    return packageManager === "npm"
+      ? `npm run ${scriptName} -- ${args}`
+      : `${packageManager} ${scriptName} ${args}`;
+  } else {
+    return packageManager === "npm"
+      ? `npm run ${scriptName}`
+      : `${packageManager} ${scriptName}`;
+  }
+}

+ 49 - 0
utils/getRenderContext.ts

@@ -0,0 +1,49 @@
+import * as path from "node:path";
+
+import { Client, Inputs, allClients, getInputs } from "./getInputs";
+import { Language, getLanguage } from "./getLanguage";
+import {
+  PackageManager,
+  getPackageManager,
+  getPackageManagerCommand,
+} from "./getPackageManager";
+
+export type RenderContext = Inputs & {
+  clients: Client[];
+  currentDirectory: string;
+  getCommand: (scriptName: string, args?: string) => string;
+  language: Language;
+  packageManager: PackageManager;
+  targetDirectory: string;
+  templateDirectory: string;
+};
+
+export async function getRenderContext(): Promise<RenderContext> {
+  const language = getLanguage();
+  const packageManager = getPackageManager();
+  const inputs = await getInputs(language);
+  const clients = allClients.flatMap((client) =>
+    inputs[`${client}Client`] ? [client] : []
+  );
+  const getCommand: RenderContext["getCommand"] = (...args) =>
+    getPackageManagerCommand(packageManager, ...args);
+
+  // Directories.
+  const templateDirectory = path.resolve(__dirname, "template");
+  const currentDirectory = process.cwd();
+  const targetDirectory = path.join(
+    currentDirectory,
+    inputs.targetDirectoryName
+  );
+
+  return {
+    ...inputs,
+    clients,
+    currentDirectory,
+    getCommand,
+    language,
+    packageManager,
+    targetDirectory,
+    templateDirectory,
+  };
+}

+ 92 - 0
utils/renderTemplate.ts

@@ -0,0 +1,92 @@
+import * as fs from "node:fs";
+import * as path from "node:path";
+import { pathToFileURL } from "node:url";
+
+import { deepMerge } from "./deepMerge";
+import { sortDependencies } from "./sortDependencies";
+
+/**
+ * Renders a template folder/file to the file system,
+ * by recursively copying all files under the `src` directory,
+ * with the following exception:
+ *   - `_filename` should be renamed to `.filename`
+ *   - Fields in `package.json` should be recursively merged
+ * @param {string} src source filename to copy
+ * @param {string} dest destination filename of the copy operation
+ */
+function renderTemplate(src: string, dest: string, callbacks: Array<Function>) {
+  const stats = fs.statSync(src);
+
+  if (stats.isDirectory()) {
+    // skip node_module
+    if (path.basename(src) === "node_modules") {
+      return;
+    }
+
+    // if it's a directory, render its subdirectories and files recursively
+    fs.mkdirSync(dest, { recursive: true });
+    for (const file of fs.readdirSync(src)) {
+      renderTemplate(
+        path.resolve(src, file),
+        path.resolve(dest, file),
+        callbacks
+      );
+    }
+    return;
+  }
+
+  const filename = path.basename(src);
+
+  if (filename === "package.json" && fs.existsSync(dest)) {
+    // merge instead of overwriting
+    const existing = JSON.parse(fs.readFileSync(dest, "utf8"));
+    const newPackage = JSON.parse(fs.readFileSync(src, "utf8"));
+    const pkg = sortDependencies(deepMerge(existing, newPackage));
+    fs.writeFileSync(dest, JSON.stringify(pkg, null, 2) + "\n");
+    return;
+  }
+
+  if (filename === "extensions.json" && fs.existsSync(dest)) {
+    // merge instead of overwriting
+    const existing = JSON.parse(fs.readFileSync(dest, "utf8"));
+    const newExtensions = JSON.parse(fs.readFileSync(src, "utf8"));
+    const extensions = deepMerge(existing, newExtensions);
+    fs.writeFileSync(dest, JSON.stringify(extensions, null, 2) + "\n");
+    return;
+  }
+
+  if (filename.startsWith("_")) {
+    // rename `_file` to `.file`
+    dest = path.resolve(path.dirname(dest), filename.replace(/^_/, "."));
+  }
+
+  if (filename === "_gitignore" && fs.existsSync(dest)) {
+    // append to existing .gitignore
+    const existing = fs.readFileSync(dest, "utf8");
+    const newGitignore = fs.readFileSync(src, "utf8");
+    fs.writeFileSync(dest, existing + "\n" + newGitignore);
+    return;
+  }
+
+  // data file for EJS templates
+  if (filename.endsWith(".data.mjs")) {
+    // use dest path as key for the data store
+    dest = dest.replace(/\.data\.mjs$/, "");
+
+    // Add a callback to the array for late usage when template files are being processed
+    callbacks.push(async (dataStore) => {
+      const getData = (await import(pathToFileURL(src).toString())).default;
+
+      // Though current `getData` are all sync, we still retain the possibility of async
+      dataStore[dest] = await getData({
+        oldData: dataStore[dest] || {},
+      });
+    });
+
+    return; // skip copying the data file
+  }
+
+  fs.copyFileSync(src, dest);
+}
+
+export default renderTemplate;

+ 27 - 0
utils/sortDependencies.ts

@@ -0,0 +1,27 @@
+export function sortDependencies<T extends object = object>(packageJson: T): T {
+  const sorted = {};
+
+  const depTypes = [
+    "dependencies",
+    "devDependencies",
+    "peerDependencies",
+    "optionalDependencies",
+  ];
+
+  for (const depType of depTypes) {
+    if (packageJson[depType]) {
+      sorted[depType] = {};
+
+      Object.keys(packageJson[depType])
+        .sort()
+        .forEach((name) => {
+          sorted[depType][name] = packageJson[depType][name];
+        });
+    }
+  }
+
+  return {
+    ...packageJson,
+    ...sorted,
+  };
+}

+ 31 - 0
utils/strings.ts

@@ -0,0 +1,31 @@
+export function capitalize(str: string): string {
+  if (str.length === 0) return str;
+  return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
+}
+
+export function titleCase(str: string): string {
+  return str
+    .replace(/([A-Z])/g, " $1")
+    .split(/[-_\s+.]/)
+    .filter((word) => word.length > 0)
+    .map(capitalize)
+    .join(" ");
+}
+
+export function pascalCase(str: string): string {
+  return titleCase(str).split(" ").join("");
+}
+
+export function camelCase(str: string): string {
+  if (str.length === 0) return str;
+  const pascalStr = pascalCase(str);
+  return pascalStr.charAt(0).toLowerCase() + pascalStr.slice(1);
+}
+
+export function kebabCase(str: string): string {
+  return titleCase(str).split(" ").join("-").toLowerCase();
+}
+
+export function snakeCase(str: string): string {
+  return titleCase(str).split(" ").join("_").toLowerCase();
+}