@semaphore-protocol/cli 4.0.0-beta.9 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -46,23 +46,25 @@
46
46
  </h4>
47
47
  </div>
48
48
 
49
- | Setting up a project, although not particularly complex, can be a lengthy process for some people. The Semaphore CLI reduces the set-up time from a few minutes to a few seconds. In addition, it can also be used to obtain on-chain group data. |
50
- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
49
+ | Semaphore CLI simplifies the process of setting up Semaphore projects and retrieving on-chain group data, reducing setup time from minutes to seconds. |
50
+ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
51
51
 
52
52
  ## 🛠 Install
53
53
 
54
- Install the `@semaphore-protocol/cli` package globally:
54
+ To install Semaphore CLI globally:
55
55
 
56
56
  ```bash
57
57
  npm i -g @semaphore-protocol/cli
58
58
  ```
59
59
 
60
- or run specific commands with `npx`:
60
+ Alternatively, you can use `npx` to run commands without installing the package globally:
61
61
 
62
62
  ```bash
63
63
  npx @semaphore-protocol/cli create my-app
64
64
  ```
65
65
 
66
+ This command sets up a new project in the `my-app` directory using the `monorepo-ethers` template.
67
+
66
68
  ## 📜 Usage
67
69
 
68
70
  ```
@@ -82,3 +84,15 @@ Commands:
82
84
  get-proofs [options] [group-id] Get the proofs of a group from a supported network (e.g. sepolia or arbitrum).
83
85
  help [command] Display help for a specific command.
84
86
  ```
87
+
88
+ ## 🌐 Supported Networks
89
+
90
+ Semaphore CLI supports multiple Ethereum networks. Use the `get-groups` command to interact with groups on networks like Sepolia or Arbitrum.
91
+
92
+ ## 📦 Supported Templates
93
+
94
+ When creating a new project, you can choose from several templates designed to integrate seamlessly with Semaphore's privacy protocols:
95
+
96
+ - **monorepo-ethers**: Hardhat + Next.js + SemaphoreEthers
97
+ - **monorepo-subgraph**: Hardhat + Next.js + SemaphoreSubgraph
98
+ - **contracts-hardhat**: Hardhat only, focused on smart contract development.
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  /**
4
4
  * @module @semaphore-protocol/cli
5
- * @version 4.0.0-beta.9
5
+ * @version 4.0.0
6
6
  * @file A command line tool to set up your Semaphore project and get group data.
7
7
  * @copyright Ethereum Foundation 2024
8
8
  * @license MIT
@@ -15,7 +15,7 @@ import chalk from 'chalk';
15
15
  import { program } from 'commander';
16
16
  import decompress from 'decompress';
17
17
  import figlet from 'figlet';
18
- import { readFileSync, existsSync, unlinkSync, copyFileSync } from 'fs';
18
+ import { readFileSync, existsSync, unlinkSync, copyFileSync, writeFileSync } from 'fs';
19
19
  import logSymbols from 'log-symbols';
20
20
  import pacote from 'pacote';
21
21
  import { dirname } from 'path';
@@ -29,10 +29,10 @@ import inquirer from 'inquirer';
29
29
 
30
30
  const cliRegistryURL = "https://registry.npmjs.org/-/package/@semaphore-protocol/cli/dist-tags";
31
31
  /**
32
- * Checks the registry directly via the API, if that fails, tries the slower `npm view [package] version` command.
33
- * This is important for users in environments where direct access to npm is blocked by a firewall, and packages are
34
- * provided exclusively via a private registry.
35
- * @param currentVersion The current version of the CLI.
32
+ * Checks for the latest version of the CLI tool against the registry. It first attempts to fetch the version directly
33
+ * via an API call. If this fails, possibly due to network restrictions, it falls back to using the `npm view` command.
34
+ * This method ensures that users behind a firewall or using a private registry can still check for updates.
35
+ * @param currentVersion The current version of the CLI being used.
36
36
  */
37
37
  async function checkLatestVersion(currentVersion) {
38
38
  let latestVersion;
@@ -55,6 +55,11 @@ async function checkLatestVersion(currentVersion) {
55
55
  }
56
56
  }
57
57
 
58
+ /**
59
+ * A utility class for managing a CLI spinner. This class encapsulates the functionality of the `ora` spinner,
60
+ * providing methods to start and stop the spinner. It is used to give visual feedback to the user during operations
61
+ * that have a noticeable delay, such as network requests.
62
+ */
58
63
  class Spinner {
59
64
  constructor(text) {
60
65
  this.ora = ora({
@@ -72,8 +77,11 @@ class Spinner {
72
77
  }
73
78
 
74
79
  /**
75
- * Gets all group ids on the specified network
76
- * @param network The specified network
80
+ * Retrieves all group IDs from the Semaphore protocol on a specified network. This function first attempts to
81
+ * fetch the group IDs using the SemaphoreSubgraph interface. If that fails, it tries the SemaphoreEthers interface
82
+ * as a fallback. This dual-method approach ensures higher reliability in fetching data across different network conditions.
83
+ * @param network The blockchain network from which to fetch the group IDs.
84
+ * @returns A promise that resolves to an array of group IDs or null if an error occurs.
77
85
  */
78
86
  async function getGroupIds(network) {
79
87
  let groupIds;
@@ -99,6 +107,10 @@ async function getGroupIds(network) {
99
107
  return groupIds;
100
108
  }
101
109
 
110
+ /**
111
+ * Prompts the user to input the name of their project. Provides a default name of "my-app".
112
+ * @returns A promise that resolves to the user's input for the project name.
113
+ */
102
114
  async function getProjectName() {
103
115
  const { projectName } = await inquirer.prompt({
104
116
  name: "projectName",
@@ -108,6 +120,12 @@ async function getProjectName() {
108
120
  });
109
121
  return projectName;
110
122
  }
123
+ /**
124
+ * Prompts the user to select a template from a list of supported templates. Each template is presented
125
+ * with its value and name for better clarity.
126
+ * @param supportedTemplates An array of objects, each containing a 'value' and 'name' property for the template.
127
+ * @returns A promise that resolves to the selected template's value.
128
+ */
111
129
  async function getSupportedTemplate(supportedTemplates) {
112
130
  const { selectedTemplate } = await inquirer.prompt({
113
131
  name: "selectedTemplate",
@@ -121,6 +139,11 @@ async function getSupportedTemplate(supportedTemplates) {
121
139
  });
122
140
  return selectedTemplate;
123
141
  }
142
+ /**
143
+ * Prompts the user to select a network from a list of supported networks.
144
+ * @param supportedNetworks An array of strings representing the supported networks.
145
+ * @returns A promise that resolves to the selected network.
146
+ */
124
147
  async function getSupportedNetwork(supportedNetworks) {
125
148
  const { selectedNetwork } = await inquirer.prompt({
126
149
  name: "selectedNetwork",
@@ -131,6 +154,11 @@ async function getSupportedNetwork(supportedNetworks) {
131
154
  });
132
155
  return selectedNetwork;
133
156
  }
157
+ /**
158
+ * Prompts the user to select a group ID from a list of existing group IDs.
159
+ * @param groupIds An array of strings representing the group IDs.
160
+ * @returns A promise that resolves to the selected group ID.
161
+ */
134
162
  async function getGroupId(groupIds) {
135
163
  const { selectedGroupId } = await inquirer.prompt({
136
164
  name: "selectedGroupId",
@@ -142,8 +170,10 @@ async function getGroupId(groupIds) {
142
170
  return selectedGroupId;
143
171
  }
144
172
 
173
+ // Define the path to the package.json file to extract metadata for the CLI.
145
174
  const packagePath = `${dirname(fileURLToPath(import.meta.url))}/..`;
146
175
  const { description, version } = JSON.parse(readFileSync(`${packagePath}/package.json`, "utf8"));
176
+ // List of supported templates for project creation.
147
177
  const supportedTemplates = [
148
178
  {
149
179
  value: "monorepo-ethers",
@@ -158,6 +188,7 @@ const supportedTemplates = [
158
188
  name: "Hardhat"
159
189
  }
160
190
  ];
191
+ // Setup the CLI program with basic information and help text.
161
192
  program
162
193
  .name("semaphore")
163
194
  .description(description)
@@ -171,6 +202,7 @@ program
171
202
  console.info(`\n ${logSymbols.error}`, message);
172
203
  }
173
204
  });
205
+ // Define the 'create' command to scaffold new Semaphore projects.
174
206
  program
175
207
  .command("create")
176
208
  .description("Create a Semaphore project with a supported template.")
@@ -196,15 +228,22 @@ program
196
228
  }
197
229
  spinner.start();
198
230
  await checkLatestVersion(version);
231
+ // Extract the template package into the project directory.
199
232
  await pacote.extract(`@semaphore-protocol/cli-template-${template}@${version}`, `${currentDirectory}/${projectDirectory}`);
233
+ // Decompress the template files after extraction.
200
234
  await decompress(`${currentDirectory}/${projectDirectory}/files.tgz`, `${currentDirectory}/${projectDirectory}`);
235
+ // Clean up the compressed file after extraction.
201
236
  unlinkSync(`${currentDirectory}/${projectDirectory}/files.tgz`);
237
+ // Copy the example environment file to the actual environment file.
202
238
  copyFileSync(`${currentDirectory}/${projectDirectory}/.env.example`, `${currentDirectory}/${projectDirectory}/.env`);
239
+ // Create an empty yarn.lock file to install dependencies successfully
240
+ writeFileSync(`${currentDirectory}/${projectDirectory}/yarn.lock`, "");
203
241
  spinner.stop();
204
242
  console.info(`\n ${logSymbols.success}`, `Your project is ready!\n`);
205
243
  console.info(` Please, install your dependencies by running:\n`);
206
244
  console.info(` ${chalk.cyan("cd")} ${projectDirectory}`);
207
245
  console.info(` ${chalk.cyan("yarn install")}\n`);
246
+ // Read the package.json to list available npm scripts.
208
247
  const { scripts } = JSON.parse(readFileSync(`${currentDirectory}/${projectDirectory}/package.json`, "utf8"));
209
248
  if (scripts) {
210
249
  console.info(` Available scripts:\n`);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@semaphore-protocol/cli",
3
3
  "type": "module",
4
- "version": "4.0.0-beta.9",
4
+ "version": "4.0.0",
5
5
  "description": "A command line tool to set up your Semaphore project and get group data.",
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -41,8 +41,8 @@
41
41
  "rollup-plugin-cleanup": "^3.2.1"
42
42
  },
43
43
  "dependencies": {
44
- "@semaphore-protocol/data": "4.0.0-beta.9",
45
- "@semaphore-protocol/utils": "4.0.0-beta.9",
44
+ "@semaphore-protocol/data": "4.0.0",
45
+ "@semaphore-protocol/utils": "4.0.0",
46
46
  "axios": "^1.6.7",
47
47
  "boxen": "^7.1.1",
48
48
  "chalk": "^5.3.0",
@@ -7,10 +7,10 @@ import { lt as semverLt } from "semver"
7
7
  const cliRegistryURL = "https://registry.npmjs.org/-/package/@semaphore-protocol/cli/dist-tags"
8
8
 
9
9
  /**
10
- * Checks the registry directly via the API, if that fails, tries the slower `npm view [package] version` command.
11
- * This is important for users in environments where direct access to npm is blocked by a firewall, and packages are
12
- * provided exclusively via a private registry.
13
- * @param currentVersion The current version of the CLI.
10
+ * Checks for the latest version of the CLI tool against the registry. It first attempts to fetch the version directly
11
+ * via an API call. If this fails, possibly due to network restrictions, it falls back to using the `npm view` command.
12
+ * This method ensures that users behind a firewall or using a private registry can still check for updates.
13
+ * @param currentVersion The current version of the CLI being used.
14
14
  */
15
15
  export default async function checkLatestVersion(currentVersion: string) {
16
16
  let latestVersion: string
@@ -1,12 +1,16 @@
1
1
  import { SemaphoreSubgraph, SemaphoreEthers } from "@semaphore-protocol/data"
2
+ import { SupportedNetwork } from "@semaphore-protocol/utils"
2
3
  import logSymbols from "log-symbols"
3
4
  import Spinner from "./spinner.js"
4
5
 
5
6
  /**
6
- * Gets all group ids on the specified network
7
- * @param network The specified network
7
+ * Retrieves all group IDs from the Semaphore protocol on a specified network. This function first attempts to
8
+ * fetch the group IDs using the SemaphoreSubgraph interface. If that fails, it tries the SemaphoreEthers interface
9
+ * as a fallback. This dual-method approach ensures higher reliability in fetching data across different network conditions.
10
+ * @param network The blockchain network from which to fetch the group IDs.
11
+ * @returns A promise that resolves to an array of group IDs or null if an error occurs.
8
12
  */
9
- export default async function getGroupIds(network): Promise<string[]> {
13
+ export default async function getGroupIds(network: SupportedNetwork): Promise<string[]> {
10
14
  let groupIds: string[]
11
15
 
12
16
  const spinner = new Spinner("Fetching groups")
package/src/index.ts CHANGED
@@ -4,7 +4,7 @@ import chalk from "chalk"
4
4
  import { program } from "commander"
5
5
  import decompress from "decompress"
6
6
  import figlet from "figlet"
7
- import { copyFileSync, existsSync, readFileSync, unlinkSync } from "fs"
7
+ import { copyFileSync, existsSync, readFileSync, unlinkSync, writeFileSync } from "fs"
8
8
  import logSymbols from "log-symbols"
9
9
  import pacote from "pacote"
10
10
  import { dirname } from "path"
@@ -14,9 +14,11 @@ import getGroupIds from "./getGroupIds.js"
14
14
  import { getGroupId, getProjectName, getSupportedNetwork, getSupportedTemplate } from "./inquirerPrompts.js"
15
15
  import Spinner from "./spinner.js"
16
16
 
17
+ // Define the path to the package.json file to extract metadata for the CLI.
17
18
  const packagePath = `${dirname(fileURLToPath(import.meta.url))}/..`
18
19
  const { description, version } = JSON.parse(readFileSync(`${packagePath}/package.json`, "utf8"))
19
20
 
21
+ // List of supported templates for project creation.
20
22
  const supportedTemplates = [
21
23
  {
22
24
  value: "monorepo-ethers",
@@ -32,6 +34,7 @@ const supportedTemplates = [
32
34
  }
33
35
  ]
34
36
 
37
+ // Setup the CLI program with basic information and help text.
35
38
  program
36
39
  .name("semaphore")
37
40
  .description(description)
@@ -46,6 +49,7 @@ program
46
49
  }
47
50
  })
48
51
 
52
+ // Define the 'create' command to scaffold new Semaphore projects.
49
53
  program
50
54
  .command("create")
51
55
  .description("Create a Semaphore project with a supported template.")
@@ -78,20 +82,27 @@ program
78
82
 
79
83
  await checkLatestVersion(version)
80
84
 
85
+ // Extract the template package into the project directory.
81
86
  await pacote.extract(
82
87
  `@semaphore-protocol/cli-template-${template}@${version}`,
83
88
  `${currentDirectory}/${projectDirectory}`
84
89
  )
85
90
 
91
+ // Decompress the template files after extraction.
86
92
  await decompress(`${currentDirectory}/${projectDirectory}/files.tgz`, `${currentDirectory}/${projectDirectory}`)
87
93
 
94
+ // Clean up the compressed file after extraction.
88
95
  unlinkSync(`${currentDirectory}/${projectDirectory}/files.tgz`)
89
96
 
97
+ // Copy the example environment file to the actual environment file.
90
98
  copyFileSync(
91
99
  `${currentDirectory}/${projectDirectory}/.env.example`,
92
100
  `${currentDirectory}/${projectDirectory}/.env`
93
101
  )
94
102
 
103
+ // Create an empty yarn.lock file to install dependencies successfully
104
+ writeFileSync(`${currentDirectory}/${projectDirectory}/yarn.lock`, "")
105
+
95
106
  spinner.stop()
96
107
 
97
108
  console.info(`\n ${logSymbols.success}`, `Your project is ready!\n`)
@@ -99,6 +110,7 @@ program
99
110
  console.info(` ${chalk.cyan("cd")} ${projectDirectory}`)
100
111
  console.info(` ${chalk.cyan("yarn install")}\n`)
101
112
 
113
+ // Read the package.json to list available npm scripts.
102
114
  const { scripts } = JSON.parse(readFileSync(`${currentDirectory}/${projectDirectory}/package.json`, "utf8"))
103
115
 
104
116
  if (scripts) {
@@ -1,5 +1,9 @@
1
1
  import inquirer from "inquirer"
2
2
 
3
+ /**
4
+ * Prompts the user to input the name of their project. Provides a default name of "my-app".
5
+ * @returns A promise that resolves to the user's input for the project name.
6
+ */
3
7
  export async function getProjectName() {
4
8
  const { projectName } = await inquirer.prompt({
5
9
  name: "projectName",
@@ -10,6 +14,12 @@ export async function getProjectName() {
10
14
  return projectName
11
15
  }
12
16
 
17
+ /**
18
+ * Prompts the user to select a template from a list of supported templates. Each template is presented
19
+ * with its value and name for better clarity.
20
+ * @param supportedTemplates An array of objects, each containing a 'value' and 'name' property for the template.
21
+ * @returns A promise that resolves to the selected template's value.
22
+ */
13
23
  export async function getSupportedTemplate(supportedTemplates: { value: string; name: string }[]) {
14
24
  const { selectedTemplate } = await inquirer.prompt({
15
25
  name: "selectedTemplate",
@@ -24,6 +34,11 @@ export async function getSupportedTemplate(supportedTemplates: { value: string;
24
34
  return selectedTemplate
25
35
  }
26
36
 
37
+ /**
38
+ * Prompts the user to select a network from a list of supported networks.
39
+ * @param supportedNetworks An array of strings representing the supported networks.
40
+ * @returns A promise that resolves to the selected network.
41
+ */
27
42
  export async function getSupportedNetwork(supportedNetworks: string[]) {
28
43
  const { selectedNetwork } = await inquirer.prompt({
29
44
  name: "selectedNetwork",
@@ -35,6 +50,11 @@ export async function getSupportedNetwork(supportedNetworks: string[]) {
35
50
  return selectedNetwork
36
51
  }
37
52
 
53
+ /**
54
+ * Prompts the user to select a group ID from a list of existing group IDs.
55
+ * @param groupIds An array of strings representing the group IDs.
56
+ * @returns A promise that resolves to the selected group ID.
57
+ */
38
58
  export async function getGroupId(groupIds: string[]) {
39
59
  const { selectedGroupId } = await inquirer.prompt({
40
60
  name: "selectedGroupId",
package/src/spinner.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  import ora, { Ora } from "ora"
2
2
 
3
+ /**
4
+ * A utility class for managing a CLI spinner. This class encapsulates the functionality of the `ora` spinner,
5
+ * providing methods to start and stop the spinner. It is used to give visual feedback to the user during operations
6
+ * that have a noticeable delay, such as network requests.
7
+ */
3
8
  export default class Spinner {
4
9
  private ora: Ora
5
10