create-moncircle 1.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.
Files changed (3) hide show
  1. package/README.md +23 -0
  2. package/index.js +194 -0
  3. package/package.json +21 -0
package/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # create-moncircle
2
+
3
+ Interactive setup wizard for [@moncircle/sdk](https://npmjs.com/package/@moncircle/sdk).
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ npx create-moncircle
9
+ ```
10
+
11
+ This will:
12
+ 1. Ask for your MongoDB URI, Monad wallet, and other config
13
+ 2. Write a `.env` file in your current directory
14
+ 3. Tell you how to start the server
15
+
16
+ ## Reconfigure
17
+
18
+ Run the same command anytime to edit your `.env`:
19
+
20
+ ```bash
21
+ npx create-moncircle
22
+ # Detects existing .env and enters edit mode
23
+ ```
package/index.js ADDED
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * create-moncircle
4
+ *
5
+ * Usage: npx create-moncircle
6
+ *
7
+ * Walks devs through setting up @moncircle/sdk in their project.
8
+ * Runs as a foreground process (visible output, interactive TTY).
9
+ */
10
+
11
+ const readline = require("readline")
12
+ const fs = require("fs")
13
+ const path = require("path")
14
+
15
+ const ENV_PATH = path.join(process.cwd(), ".env")
16
+
17
+ const CYAN = "\x1b[36m"
18
+ const GREEN = "\x1b[32m"
19
+ const YELLOW = "\x1b[33m"
20
+ const RED = "\x1b[31m"
21
+ const DIM = "\x1b[2m"
22
+ const BOLD = "\x1b[1m"
23
+ const RESET = "\x1b[0m"
24
+
25
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
26
+
27
+ const ask = (question, defaultVal = "") =>
28
+ new Promise((resolve) => {
29
+ const hint = defaultVal ? ` ${DIM}(${defaultVal})${RESET}` : ""
30
+ rl.question(` ${question}${hint} › `, (answer) => {
31
+ resolve(answer.trim() || defaultVal)
32
+ })
33
+ })
34
+
35
+ const fields = [
36
+ {
37
+ key: "MONGODB_URI",
38
+ label: "MongoDB URI",
39
+ hint: "mongodb+srv://user:pass@cluster.mongodb.net/mon-loyalty",
40
+ required: true,
41
+ default: ""
42
+ },
43
+ {
44
+ key: "PORT",
45
+ label: "Server Port",
46
+ hint: "Port to run the API on",
47
+ required: false,
48
+ default: "4000"
49
+ },
50
+ {
51
+ key: "JWT_SECRET",
52
+ label: "JWT Secret",
53
+ hint: "Long random string for signing tokens",
54
+ required: false,
55
+ default: "change_me_" + Math.random().toString(36).slice(2)
56
+ },
57
+ {
58
+ key: "MONAD_RPC_URL",
59
+ label: "Monad RPC URL",
60
+ hint: "Your Monad node RPC endpoint",
61
+ required: false,
62
+ default: "https://testnet-rpc.monad.xyz"
63
+ },
64
+ {
65
+ key: "MONAD_PRIVATE_KEY",
66
+ label: "Monad Private Key",
67
+ hint: "Master wallet private key — KEEP SECRET",
68
+ required: false,
69
+ default: ""
70
+ },
71
+ {
72
+ key: "MONAD_CONTRACT_ADDRESS",
73
+ label: "MonLoyalty Contract Address",
74
+ hint: "Deployed contract address (leave blank for now)",
75
+ required: false,
76
+ default: ""
77
+ }
78
+ ]
79
+
80
+ function loadExistingEnv() {
81
+ if (!fs.existsSync(ENV_PATH)) return {}
82
+ const content = fs.readFileSync(ENV_PATH, "utf-8")
83
+ const map = {}
84
+ for (const line of content.split("\n")) {
85
+ const match = line.match(/^\s*([^#=\s][^=]*)=(.*)$/)
86
+ if (match) map[match[1].trim()] = match[2].trim().replace(/^"|"$/g, "")
87
+ }
88
+ return map
89
+ }
90
+
91
+ function mergeEnv(existing, updated) {
92
+ const envContent = fs.existsSync(ENV_PATH) ? fs.readFileSync(ENV_PATH, "utf-8") : ""
93
+ const lines = envContent.split("\n")
94
+ const handled = new Set()
95
+ const result = lines.map((line) => {
96
+ const match = line.match(/^\s*([^#=\s][^=]*)=(.*)$/)
97
+ if (match) {
98
+ const key = match[1].trim()
99
+ if (updated[key] !== undefined) {
100
+ handled.add(key)
101
+ return `${key}="${updated[key]}"`
102
+ }
103
+ }
104
+ return line
105
+ })
106
+ for (const key of Object.keys(updated)) {
107
+ if (!handled.has(key)) result.push(`${key}="${updated[key]}"`)
108
+ }
109
+ return result.join("\n")
110
+ }
111
+
112
+ function writeFreshEnv(values) {
113
+ const lines = [
114
+ "# Generated by create-moncircle",
115
+ "# Run: npx create-moncircle to reconfigure",
116
+ "",
117
+ "# ── Required ─────────────────────────────────",
118
+ `MONGODB_URI="${values.MONGODB_URI}"`,
119
+ "",
120
+ "# ── Server ───────────────────────────────────",
121
+ `PORT=${values.PORT}`,
122
+ `NODE_ENV=development`,
123
+ "",
124
+ "# ── Auth ─────────────────────────────────────",
125
+ `JWT_SECRET="${values.JWT_SECRET}"`,
126
+ "",
127
+ "# ── Monad On-Chain (withdrawal/settlement) ────",
128
+ `MONAD_RPC_URL="${values.MONAD_RPC_URL}"`,
129
+ `MONAD_PRIVATE_KEY="${values.MONAD_PRIVATE_KEY}"`,
130
+ `MONAD_CONTRACT_ADDRESS="${values.MONAD_CONTRACT_ADDRESS}"`,
131
+ ""
132
+ ]
133
+ fs.writeFileSync(ENV_PATH, lines.join("\n"), "utf-8")
134
+ }
135
+
136
+ async function main() {
137
+ const existing = loadExistingEnv()
138
+ const isEditMode = Object.keys(existing).length > 0
139
+
140
+ console.log(`\n${BOLD}${CYAN}╔══════════════════════════════════════════════════════╗`)
141
+ console.log(`║ @moncircle/sdk — ${isEditMode ? "Edit Configuration " : "Setup Wizard "} ║`)
142
+ console.log(`╚══════════════════════════════════════════════════════╝${RESET}\n`)
143
+
144
+ if (isEditMode) {
145
+ console.log(` ${GREEN}✓ Existing .env found.${RESET} ${DIM}Press Enter to keep current values.${RESET}\n`)
146
+ } else {
147
+ console.log(` ${DIM}No .env found — let's create one.${RESET}`)
148
+ console.log(` ${DIM}Writing to: ${ENV_PATH}${RESET}\n`)
149
+ }
150
+
151
+ const updated = { ...existing }
152
+
153
+ for (const field of fields) {
154
+ const currentVal = existing[field.key] ?? field.default
155
+ const requiredTag = field.required ? ` ${RED}*required${RESET}` : ""
156
+
157
+ console.log(` ${BOLD}${field.label}${RESET}${requiredTag}`)
158
+ if (!isEditMode || !existing[field.key]) {
159
+ console.log(` ${DIM}${field.hint}${RESET}`)
160
+ }
161
+
162
+ let val = ""
163
+ while (true) {
164
+ val = await ask(` →`, currentVal)
165
+ if (field.required && !val) {
166
+ console.log(` ${RED}✖ This field is required.${RESET}`)
167
+ } else {
168
+ break
169
+ }
170
+ }
171
+ updated[field.key] = val
172
+ console.log()
173
+ }
174
+
175
+ if (isEditMode) {
176
+ fs.writeFileSync(ENV_PATH, mergeEnv(existing, updated), "utf-8")
177
+ console.log(`${GREEN}${BOLD} ✅ .env updated successfully!${RESET}\n`)
178
+ } else {
179
+ writeFreshEnv(updated)
180
+ console.log(`${GREEN}${BOLD} ✅ .env created successfully!${RESET}\n`)
181
+ }
182
+
183
+ console.log(` Next steps:\n`)
184
+ console.log(` ${CYAN}npx mon-loyalty-api start${RESET} — start the server`)
185
+ console.log(` ${CYAN}npx create-moncircle${RESET} — reconfigure anytime\n`)
186
+
187
+ rl.close()
188
+ }
189
+
190
+ main().catch((err) => {
191
+ console.error(`\n${RED}Setup failed:${RESET}`, err.message)
192
+ rl.close()
193
+ process.exit(1)
194
+ })
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "create-moncircle",
3
+ "version": "1.0.0",
4
+ "description": "Interactive setup wizard for @moncircle/sdk — run via npx create-moncircle",
5
+ "bin": {
6
+ "create-moncircle": "index.js"
7
+ },
8
+ "files": [
9
+ "index.js",
10
+ "README.md"
11
+ ],
12
+ "keywords": [
13
+ "moncircle",
14
+ "loyalty",
15
+ "monad",
16
+ "setup",
17
+ "wizard"
18
+ ],
19
+ "author": "stondral",
20
+ "license": "MIT"
21
+ }