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.
- package/README.md +23 -0
- package/index.js +194 -0
- 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
|
+
}
|