@semaphore-protocol/cli 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  <h1 align="center">
3
3
  Semaphore CLI
4
4
  </h1>
5
- <p align="center">A command line tool to set up a Semaphore project.</p>
5
+ <p align="center">A command line tool to set up your Semaphore project and query groups.</p>
6
6
  </p>
7
7
 
8
8
  <p align="center">
package/dist/index.js CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  /**
4
4
  * @module @semaphore-protocol/cli
5
- * @version 0.0.1
6
- * @file A command line tool to set up a Semaphore project and query groups.
5
+ * @version 0.2.0
6
+ * @file A command line tool to set up your Semaphore project and query groups.
7
7
  * @copyright Ethereum Foundation 2022
8
8
  * @license MIT
9
9
  * @see [Github]{@link https://github.com/semaphore-protocol/semaphore/tree/main/packages/cli}
@@ -12,55 +12,128 @@
12
12
  import { Subgraph } from '@semaphore-protocol/subgraph';
13
13
  import chalk from 'chalk';
14
14
  import { program } from 'commander';
15
+ import download from 'download';
15
16
  import figlet from 'figlet';
16
- import { readFileSync } from 'fs';
17
- import ora from 'ora';
17
+ import { readFileSync, existsSync, renameSync } from 'fs';
18
+ import logSymbols from 'log-symbols';
18
19
  import { dirname } from 'path';
19
20
  import { fileURLToPath } from 'url';
21
+ import ora from 'ora';
22
+
23
+ class Spinner {
24
+ constructor(text) {
25
+ this.text = text;
26
+ }
27
+ start() {
28
+ console.info("");
29
+ this.ora = ora({
30
+ text: this.text,
31
+ indent: 1
32
+ }).start();
33
+ }
34
+ stop() {
35
+ this.ora.stop();
36
+ process.stdout.cursorTo(0);
37
+ }
38
+ }
20
39
 
21
- /* eslint-disable no-console */
22
40
  const packagePath = `${dirname(fileURLToPath(import.meta.url))}/..`;
23
41
  const { version } = JSON.parse(readFileSync(`${packagePath}/package.json`, "utf8"));
24
42
  program
25
43
  .name("semaphore")
26
- .description("A command line tool to set up a Semaphore project and query groups.")
44
+ .description("A command line tool to set up your Semaphore project and query groups.")
27
45
  .version(version, "-v, --version", "Show Semaphore CLI version.")
28
- .addHelpText("before", `${figlet.textSync("Semaphore CLI")}\n`)
46
+ .addHelpText("before", `${figlet.textSync("Semaphore")}\n`)
47
+ .addHelpText("after", "\r")
29
48
  .helpOption(undefined, "Display this help.")
30
- .addHelpCommand("help [command]", "Display help for a specific command.");
49
+ .addHelpCommand("help [command]", "Display help for a specific command.")
50
+ .configureOutput({
51
+ outputError: (message) => {
52
+ console.info(`\n ${logSymbols.error}`, message);
53
+ }
54
+ });
55
+ program
56
+ .command("init")
57
+ .description("Initialize a Semaphore project with a supported template.")
58
+ .argument("<project-directory>", "Directory of the project.")
59
+ // .option("-t, --template <template-name>", "Supported Semaphore template.", "hardhat")
60
+ .action(async (projectDirectory) => {
61
+ // const originalDirectory = process.cwd()
62
+ const spinner = new Spinner(`Creating '${projectDirectory}' project`);
63
+ const templateURL = `https://registry.npmjs.org/@semaphore-protocol/cli-template-hardhat/-/cli-template-hardhat-${version}.tgz`;
64
+ if (existsSync(projectDirectory)) {
65
+ console.info(`\n ${logSymbols.error}`, `error: the '${projectDirectory}' folder already exists\n`);
66
+ return;
67
+ }
68
+ spinner.start();
69
+ await download(templateURL, "./", { extract: true });
70
+ renameSync("package", projectDirectory);
71
+ spinner.stop();
72
+ console.info(` ${logSymbols.success}`, `Your '${projectDirectory}' project is ready!\n`);
73
+ });
74
+ program
75
+ .command("get-groups")
76
+ .description("Get the list of groups from a supported network (Goerli or Arbitrum).")
77
+ .option("-n, --network <network-name>", "Supported Ethereum network.", "goerli")
78
+ .action(async ({ network }) => {
79
+ const subgraph = new Subgraph(network);
80
+ const spinner = new Spinner("Fetching groups");
81
+ spinner.start();
82
+ try {
83
+ const groups = await subgraph.getGroups();
84
+ spinner.stop();
85
+ if (groups.length === 0) {
86
+ console.info(` ${logSymbols.error}`, "error: there are no groups in this network\n");
87
+ return;
88
+ }
89
+ const content = `${groups.map(({ id }) => ` - ${id}`).join("\n")}`;
90
+ console.info(`${content}\n`);
91
+ }
92
+ catch (error) {
93
+ spinner.stop();
94
+ console.info(` ${logSymbols.error}`, "error: unexpected error with the Semaphore subgraph");
95
+ }
96
+ });
31
97
  program
32
98
  .command("get-group")
33
- .description("Get group data from a supported network.")
99
+ .description("Get the data of a group from a supported network (Goerli or Arbitrum).")
34
100
  .argument("<group-id>", "Identifier of the group.")
35
101
  .option("-n, --network <network-name>", "Supported Ethereum network.", "goerli")
36
102
  .option("--members", "Show group members.")
37
103
  .option("--signals", "Show group signals.")
38
- .usage("<group-id> [options]")
39
104
  .action(async (groupId, { network, members, signals }) => {
40
105
  const subgraph = new Subgraph(network);
41
- const spinner = ora({
42
- text: `Fetching group ${groupId}`,
43
- spinner: "boxBounce"
44
- }).start();
45
- const group = await subgraph.getGroup(groupId, { members, verifiedProofs: signals });
46
- let content = `\n ${chalk.bold("Id")}: ${group.id}\n`;
47
- content += ` ${chalk.bold("Admin")}: ${group.admin}\n`;
48
- content += ` ${chalk.bold("Merkle tree")}:\n`;
49
- content += ` Root: ${group.merkleTree.root}\n`;
50
- content += ` Depth: ${group.merkleTree.depth}\n`;
51
- content += ` Zero value: ${group.merkleTree.zeroValue}\n`;
52
- content += ` Number of leaves: ${group.merkleTree.numberOfLeaves}`;
53
- if (members) {
54
- content += `\n\n ${chalk.bold("Members")}: \n${group.members
55
- .map((member, i) => ` [${i}] ${member}`)
56
- .join("\n")}`;
106
+ const spinner = new Spinner(`Fetching group ${groupId}`);
107
+ spinner.start();
108
+ try {
109
+ const group = await subgraph.getGroup(groupId, { members, verifiedProofs: signals });
110
+ spinner.stop();
111
+ if (!group) {
112
+ console.info(` ${logSymbols.error}`, "error: the group does not exist\n");
113
+ return;
114
+ }
115
+ let content = ` ${chalk.bold("Id")}: ${group.id}\n`;
116
+ content += ` ${chalk.bold("Admin")}: ${group.admin}\n`;
117
+ content += ` ${chalk.bold("Merkle tree")}:\n`;
118
+ content += ` Root: ${group.merkleTree.root}\n`;
119
+ content += ` Depth: ${group.merkleTree.depth}\n`;
120
+ content += ` Zero value: ${group.merkleTree.zeroValue}\n`;
121
+ content += ` Number of leaves: ${group.merkleTree.numberOfLeaves}`;
122
+ if (members) {
123
+ content += `\n\n ${chalk.bold("Members")}: \n${group.members
124
+ .map((member, i) => ` ${i}. ${member}`)
125
+ .join("\n")}`;
126
+ }
127
+ if (signals) {
128
+ content += `\n\n ${chalk.bold("Signals")}: \n${group.verifiedProofs
129
+ .map(({ signal }) => ` - ${signal}`)
130
+ .join("\n")}`;
131
+ }
132
+ console.info(`${content}\n`);
57
133
  }
58
- if (signals) {
59
- content += `\n\n ${chalk.bold("Signals")}: \n${group.verifiedProofs
60
- .map(({ signal }, i) => ` [${i}] ${signal}`)
61
- .join("\n")}`;
134
+ catch (error) {
135
+ spinner.stop();
136
+ console.info(` ${logSymbols.error}`, "error: unexpected error with the Semaphore subgraph");
62
137
  }
63
- spinner.stop();
64
- console.log(`${content}\n`);
65
138
  });
66
139
  program.parse();
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@semaphore-protocol/cli",
3
3
  "type": "module",
4
- "version": "0.0.1",
5
- "description": "A command line tool to set up a Semaphore project and query groups.",
4
+ "version": "0.2.0",
5
+ "description": "A command line tool to set up your Semaphore project and query groups.",
6
6
  "license": "MIT",
7
7
  "bin": {
8
8
  "semaphore": "dist/index.js"
@@ -40,9 +40,12 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "@semaphore-protocol/subgraph": "2.6.1",
43
+ "@types/clear": "^0.1.2",
43
44
  "chalk": "^5.1.2",
44
45
  "commander": "^9.4.1",
46
+ "download": "^8.0.0",
45
47
  "figlet": "^1.5.2",
48
+ "log-symbols": "^5.1.0",
46
49
  "ora": "^6.1.2"
47
50
  }
48
51
  }
package/src/index.ts CHANGED
@@ -1,65 +1,137 @@
1
- /* eslint-disable no-console */
2
1
  import { Subgraph } from "@semaphore-protocol/subgraph"
3
2
  import chalk from "chalk"
4
3
  import { program } from "commander"
4
+ import download from "download"
5
5
  import figlet from "figlet"
6
- import { readFileSync } from "fs"
7
- import ora from "ora"
6
+ import { existsSync, readFileSync, renameSync } from "fs"
7
+ import logSymbols from "log-symbols"
8
8
  import { dirname } from "path"
9
9
  import { fileURLToPath } from "url"
10
+ import Spinner from "./spinner.js"
10
11
 
11
12
  const packagePath = `${dirname(fileURLToPath(import.meta.url))}/..`
12
13
  const { version } = JSON.parse(readFileSync(`${packagePath}/package.json`, "utf8"))
13
14
 
14
15
  program
15
16
  .name("semaphore")
16
- .description("A command line tool to set up a Semaphore project and query groups.")
17
+ .description("A command line tool to set up your Semaphore project and query groups.")
17
18
  .version(version, "-v, --version", "Show Semaphore CLI version.")
18
- .addHelpText("before", `${figlet.textSync("Semaphore CLI")}\n`)
19
+ .addHelpText("before", `${figlet.textSync("Semaphore")}\n`)
20
+ .addHelpText("after", "\r")
19
21
  .helpOption(undefined, "Display this help.")
20
22
  .addHelpCommand("help [command]", "Display help for a specific command.")
23
+ .configureOutput({
24
+ outputError: (message) => {
25
+ console.info(`\n ${logSymbols.error}`, message)
26
+ }
27
+ })
28
+
29
+ program
30
+ .command("init")
31
+ .description("Initialize a Semaphore project with a supported template.")
32
+ .argument("<project-directory>", "Directory of the project.")
33
+ // .option("-t, --template <template-name>", "Supported Semaphore template.", "hardhat")
34
+ .action(async (projectDirectory) => {
35
+ // const originalDirectory = process.cwd()
36
+ const spinner = new Spinner(`Creating '${projectDirectory}' project`)
37
+ const templateURL = `https://registry.npmjs.org/@semaphore-protocol/cli-template-hardhat/-/cli-template-hardhat-${version}.tgz`
38
+
39
+ if (existsSync(projectDirectory)) {
40
+ console.info(`\n ${logSymbols.error}`, `error: the '${projectDirectory}' folder already exists\n`)
41
+ return
42
+ }
43
+
44
+ spinner.start()
45
+
46
+ await download(templateURL, "./", { extract: true })
47
+
48
+ renameSync("package", projectDirectory)
49
+
50
+ spinner.stop()
51
+
52
+ console.info(` ${logSymbols.success}`, `Your '${projectDirectory}' project is ready!\n`)
53
+ })
54
+
55
+ program
56
+ .command("get-groups")
57
+ .description("Get the list of groups from a supported network (Goerli or Arbitrum).")
58
+ .option("-n, --network <network-name>", "Supported Ethereum network.", "goerli")
59
+ .action(async ({ network }) => {
60
+ const subgraph = new Subgraph(network)
61
+ const spinner = new Spinner("Fetching groups")
62
+
63
+ spinner.start()
64
+
65
+ try {
66
+ const groups = await subgraph.getGroups()
67
+
68
+ spinner.stop()
69
+
70
+ if (groups.length === 0) {
71
+ console.info(` ${logSymbols.error}`, "error: there are no groups in this network\n")
72
+ return
73
+ }
74
+
75
+ const content = `${groups.map(({ id }: any) => ` - ${id}`).join("\n")}`
76
+
77
+ console.info(`${content}\n`)
78
+ } catch (error) {
79
+ spinner.stop()
80
+
81
+ console.info(` ${logSymbols.error}`, "error: unexpected error with the Semaphore subgraph")
82
+ }
83
+ })
21
84
 
22
85
  program
23
86
  .command("get-group")
24
- .description("Get group data from a supported network.")
87
+ .description("Get the data of a group from a supported network (Goerli or Arbitrum).")
25
88
  .argument("<group-id>", "Identifier of the group.")
26
89
  .option("-n, --network <network-name>", "Supported Ethereum network.", "goerli")
27
90
  .option("--members", "Show group members.")
28
91
  .option("--signals", "Show group signals.")
29
- .usage("<group-id> [options]")
30
92
  .action(async (groupId, { network, members, signals }) => {
31
93
  const subgraph = new Subgraph(network)
94
+ const spinner = new Spinner(`Fetching group ${groupId}`)
32
95
 
33
- const spinner = ora({
34
- text: `Fetching group ${groupId}`,
35
- spinner: "boxBounce"
36
- }).start()
37
-
38
- const group = await subgraph.getGroup(groupId, { members, verifiedProofs: signals })
39
-
40
- let content = `\n ${chalk.bold("Id")}: ${group.id}\n`
41
- content += ` ${chalk.bold("Admin")}: ${group.admin}\n`
42
- content += ` ${chalk.bold("Merkle tree")}:\n`
43
- content += ` Root: ${group.merkleTree.root}\n`
44
- content += ` Depth: ${group.merkleTree.depth}\n`
45
- content += ` Zero value: ${group.merkleTree.zeroValue}\n`
46
- content += ` Number of leaves: ${group.merkleTree.numberOfLeaves}`
47
-
48
- if (members) {
49
- content += `\n\n ${chalk.bold("Members")}: \n${group.members
50
- .map((member: string, i: number) => ` [${i}] ${member}`)
51
- .join("\n")}`
52
- }
96
+ spinner.start()
53
97
 
54
- if (signals) {
55
- content += `\n\n ${chalk.bold("Signals")}: \n${group.verifiedProofs
56
- .map(({ signal }: any, i: number) => ` [${i}] ${signal}`)
57
- .join("\n")}`
58
- }
98
+ try {
99
+ const group = await subgraph.getGroup(groupId, { members, verifiedProofs: signals })
59
100
 
60
- spinner.stop()
101
+ spinner.stop()
102
+
103
+ if (!group) {
104
+ console.info(` ${logSymbols.error}`, "error: the group does not exist\n")
105
+
106
+ return
107
+ }
108
+
109
+ let content = ` ${chalk.bold("Id")}: ${group.id}\n`
110
+ content += ` ${chalk.bold("Admin")}: ${group.admin}\n`
111
+ content += ` ${chalk.bold("Merkle tree")}:\n`
112
+ content += ` Root: ${group.merkleTree.root}\n`
113
+ content += ` Depth: ${group.merkleTree.depth}\n`
114
+ content += ` Zero value: ${group.merkleTree.zeroValue}\n`
115
+ content += ` Number of leaves: ${group.merkleTree.numberOfLeaves}`
61
116
 
62
- console.log(`${content}\n`)
117
+ if (members) {
118
+ content += `\n\n ${chalk.bold("Members")}: \n${group.members
119
+ .map((member: string, i: number) => ` ${i}. ${member}`)
120
+ .join("\n")}`
121
+ }
122
+
123
+ if (signals) {
124
+ content += `\n\n ${chalk.bold("Signals")}: \n${group.verifiedProofs
125
+ .map(({ signal }: any) => ` - ${signal}`)
126
+ .join("\n")}`
127
+ }
128
+
129
+ console.info(`${content}\n`)
130
+ } catch (error) {
131
+ spinner.stop()
132
+
133
+ console.info(` ${logSymbols.error}`, "error: unexpected error with the Semaphore subgraph")
134
+ }
63
135
  })
64
136
 
65
137
  program.parse()
package/src/spinner.ts ADDED
@@ -0,0 +1,25 @@
1
+ import ora, { Ora } from "ora"
2
+
3
+ export default class Spinner {
4
+ private text: string
5
+ private ora: Ora
6
+
7
+ constructor(text: string) {
8
+ this.text = text
9
+ }
10
+
11
+ start() {
12
+ console.info("")
13
+
14
+ this.ora = ora({
15
+ text: this.text,
16
+ indent: 1
17
+ }).start()
18
+ }
19
+
20
+ stop() {
21
+ this.ora.stop()
22
+
23
+ process.stdout.cursorTo(0)
24
+ }
25
+ }