@moncircle/sdk 1.0.2 → 1.1.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/package.json +5 -2
- package/scripts/postinstall.js +25 -31
- package/scripts/setup.js +86 -38
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moncircle/sdk",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Self-hosted MON loyalty reward infrastructure for e-commerce platforms. Supports SINGLE, MULTI, and HYBRID reward modes with Monad on-chain settlement.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -34,15 +34,18 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"cors": "^2.8.6",
|
|
36
36
|
"dotenv": "^17.3.1",
|
|
37
|
-
"
|
|
37
|
+
"ethers": "^6.16.0",
|
|
38
|
+
"express": ">=4.0.0",
|
|
38
39
|
"mongoose": "^8.0.0",
|
|
39
40
|
"uuid": "^13.0.0"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
43
|
+
"@nomicfoundation/hardhat-toolbox": "^6.1.0",
|
|
42
44
|
"@types/cors": "^2.8.19",
|
|
43
45
|
"@types/express": "^5.0.6",
|
|
44
46
|
"@types/node": "^25.3.0",
|
|
45
47
|
"@types/uuid": "^10.0.0",
|
|
48
|
+
"hardhat": "^3.1.9",
|
|
46
49
|
"ts-node-dev": "^2.0.0",
|
|
47
50
|
"typescript": "^5.9.3"
|
|
48
51
|
},
|
package/scripts/postinstall.js
CHANGED
|
@@ -1,43 +1,37 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* @moncircle/sdk postinstall hook
|
|
4
|
+
*
|
|
5
|
+
* Spawns create-moncircle with inherited stdio — this makes it visible
|
|
6
|
+
* to the dev's terminal even when run from inside npm install.
|
|
6
7
|
*/
|
|
7
8
|
|
|
9
|
+
const { spawnSync } = require("child_process")
|
|
8
10
|
const fs = require("fs")
|
|
9
11
|
const path = require("path")
|
|
10
12
|
|
|
11
|
-
const CYAN = "\x1b[36m"
|
|
12
|
-
const GREEN = "\x1b[32m"
|
|
13
|
-
const YELLOW = "\x1b[33m"
|
|
14
|
-
const BOLD = "\x1b[1m"
|
|
15
|
-
const RESET = "\x1b[0m"
|
|
16
|
-
|
|
17
13
|
const projectRoot = process.env.INIT_CWD || process.cwd()
|
|
18
14
|
const envExists = fs.existsSync(path.join(projectRoot, ".env"))
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (envExists) {
|
|
27
|
-
console.log(` ${GREEN}✓ .env found — you're already configured.${RESET}`)
|
|
28
|
-
console.log(` Run ${CYAN}npx mon-loyalty-setup${RESET} to reconfigure anytime.\n`)
|
|
29
|
-
} else {
|
|
30
|
-
console.log(` ${YELLOW}${BOLD}⚠ No .env file found.${RESET}`)
|
|
31
|
-
console.log(`
|
|
32
|
-
Run the interactive setup wizard now:
|
|
33
|
-
|
|
34
|
-
${BOLD}${CYAN} npx mon-loyalty-setup${RESET}
|
|
35
|
-
|
|
36
|
-
It will ask for your MongoDB URI, Monad wallet, and other
|
|
37
|
-
config, then write your .env file automatically.
|
|
38
|
-
|
|
39
|
-
Then start the server:
|
|
16
|
+
// Skip in CI
|
|
17
|
+
if (process.env.CI === "true" || process.env.CI === "1") {
|
|
18
|
+
console.log("\n[@moncircle/sdk] CI detected — skipping setup. Run: npx create-moncircle\n")
|
|
19
|
+
process.exit(0)
|
|
20
|
+
}
|
|
40
21
|
|
|
41
|
-
|
|
42
|
-
|
|
22
|
+
// Spawn create-moncircle with --yes to skip "Ok to proceed?" confirmation
|
|
23
|
+
// stdio: "inherit" makes all output/input flow directly to the user's terminal
|
|
24
|
+
const result = spawnSync("npx", ["--yes", "create-moncircle"], {
|
|
25
|
+
stdio: "inherit",
|
|
26
|
+
cwd: projectRoot,
|
|
27
|
+
shell: true,
|
|
28
|
+
env: {
|
|
29
|
+
...process.env,
|
|
30
|
+
MON_LOYALTY_ENV_PATH: path.join(projectRoot, ".env"),
|
|
31
|
+
MON_ENV_EXISTS: envExists ? "1" : "0"
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
if (result.error) {
|
|
36
|
+
console.warn("\n[@moncircle/sdk] Could not launch setup wizard. Run: npx create-moncircle\n")
|
|
43
37
|
}
|
package/scripts/setup.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* mon-loyalty-api setup
|
|
4
|
-
* Run: npm run setup OR npx mon-loyalty-
|
|
3
|
+
* mon-loyalty-api interactive setup wizard
|
|
4
|
+
* Run: npm run setup OR npx mon-loyalty-setup
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* - NEW MODE: prompts for all values, writes .env from scratch
|
|
7
|
+
* - EDIT MODE: shows existing values, only updates what you change
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
const readline = require("readline")
|
|
@@ -16,6 +17,7 @@ const CYAN = "\x1b[36m"
|
|
|
16
17
|
const GREEN = "\x1b[32m"
|
|
17
18
|
const YELLOW = "\x1b[33m"
|
|
18
19
|
const RED = "\x1b[31m"
|
|
20
|
+
const DIM = "\x1b[2m"
|
|
19
21
|
const BOLD = "\x1b[1m"
|
|
20
22
|
const RESET = "\x1b[0m"
|
|
21
23
|
|
|
@@ -23,8 +25,8 @@ const rl = readline.createInterface({ input: process.stdin, output: process.stdo
|
|
|
23
25
|
|
|
24
26
|
const ask = (question, defaultVal = "") =>
|
|
25
27
|
new Promise((resolve) => {
|
|
26
|
-
const hint = defaultVal ? ` ${
|
|
27
|
-
rl.question(` ${question}${hint}
|
|
28
|
+
const hint = defaultVal ? ` ${DIM}(${defaultVal})${RESET}` : ""
|
|
29
|
+
rl.question(` ${question}${hint} › `, (answer) => {
|
|
28
30
|
resolve(answer.trim() || defaultVal)
|
|
29
31
|
})
|
|
30
32
|
})
|
|
@@ -33,7 +35,7 @@ const fields = [
|
|
|
33
35
|
{
|
|
34
36
|
key: "MONGODB_URI",
|
|
35
37
|
label: "MongoDB URI",
|
|
36
|
-
hint: "
|
|
38
|
+
hint: "mongodb+srv://user:pass@cluster.mongodb.net/mon-loyalty",
|
|
37
39
|
required: true,
|
|
38
40
|
default: ""
|
|
39
41
|
},
|
|
@@ -47,48 +49,79 @@ const fields = [
|
|
|
47
49
|
{
|
|
48
50
|
key: "JWT_SECRET",
|
|
49
51
|
label: "JWT Secret",
|
|
50
|
-
hint: "
|
|
52
|
+
hint: "Long random string for signing tokens",
|
|
51
53
|
required: false,
|
|
52
54
|
default: "change_me_in_production_" + Math.random().toString(36).slice(2)
|
|
53
55
|
},
|
|
54
56
|
{
|
|
55
57
|
key: "MONAD_RPC_URL",
|
|
56
58
|
label: "Monad RPC URL",
|
|
57
|
-
hint: "Your Monad node RPC endpoint
|
|
59
|
+
hint: "Your Monad node RPC endpoint",
|
|
58
60
|
required: false,
|
|
59
61
|
default: "https://testnet-rpc.monad.xyz"
|
|
60
62
|
},
|
|
61
63
|
{
|
|
62
64
|
key: "MONAD_PRIVATE_KEY",
|
|
63
65
|
label: "Monad Private Key",
|
|
64
|
-
hint: "
|
|
66
|
+
hint: "Master wallet private key — KEEP SECRET",
|
|
65
67
|
required: false,
|
|
66
68
|
default: ""
|
|
67
69
|
},
|
|
68
70
|
{
|
|
69
71
|
key: "MONAD_CONTRACT_ADDRESS",
|
|
70
72
|
label: "MonLoyalty Contract Address",
|
|
71
|
-
hint: "Deployed
|
|
73
|
+
hint: "Deployed contract address (leave blank for now)",
|
|
72
74
|
required: false,
|
|
73
75
|
default: ""
|
|
74
76
|
}
|
|
75
77
|
]
|
|
76
78
|
|
|
77
|
-
|
|
79
|
+
// Parse existing .env into a key/value map
|
|
80
|
+
function loadExistingEnv() {
|
|
78
81
|
if (!fs.existsSync(ENV_PATH)) return {}
|
|
79
82
|
const content = fs.readFileSync(ENV_PATH, "utf-8")
|
|
80
|
-
const
|
|
83
|
+
const map = {}
|
|
81
84
|
for (const line of content.split("\n")) {
|
|
82
|
-
const match = line.match(
|
|
83
|
-
if (match)
|
|
85
|
+
const match = line.match(/^\s*([^#=\s][^=]*)=(.*)$/)
|
|
86
|
+
if (match) map[match[1].trim()] = match[2].trim().replace(/^"|"$/g, "")
|
|
84
87
|
}
|
|
85
|
-
return
|
|
88
|
+
return map
|
|
86
89
|
}
|
|
87
90
|
|
|
88
|
-
|
|
91
|
+
// Merge updated values back into the existing .env content (preserving comments/order)
|
|
92
|
+
// For new keys, append at the bottom
|
|
93
|
+
function mergeEnv(existing, updated) {
|
|
94
|
+
const envContent = fs.existsSync(ENV_PATH) ? fs.readFileSync(ENV_PATH, "utf-8") : ""
|
|
95
|
+
const lines = envContent.split("\n")
|
|
96
|
+
const handled = new Set()
|
|
97
|
+
|
|
98
|
+
const result = lines.map((line) => {
|
|
99
|
+
const match = line.match(/^\s*([^#=\s][^=]*)=(.*)$/)
|
|
100
|
+
if (match) {
|
|
101
|
+
const key = match[1].trim()
|
|
102
|
+
if (updated[key] !== undefined) {
|
|
103
|
+
handled.add(key)
|
|
104
|
+
return `${key}="${updated[key]}"`
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return line
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
// Append any new keys that weren't in the original file
|
|
111
|
+
for (const key of Object.keys(updated)) {
|
|
112
|
+
if (!handled.has(key)) {
|
|
113
|
+
result.push(`${key}="${updated[key]}"`)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return result.join("\n")
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Write a fresh .env
|
|
121
|
+
function writeFreshEnv(values) {
|
|
89
122
|
const lines = [
|
|
90
|
-
"# Generated by
|
|
91
|
-
"# Run: npx mon-loyalty-
|
|
123
|
+
"# Generated by @moncircle/sdk setup",
|
|
124
|
+
"# Run: npx mon-loyalty-setup to reconfigure",
|
|
92
125
|
"",
|
|
93
126
|
"# ── Required ─────────────────────────────────",
|
|
94
127
|
`MONGODB_URI="${values.MONGODB_URI}"`,
|
|
@@ -100,7 +133,7 @@ function writeEnv(values) {
|
|
|
100
133
|
"# ── Auth ─────────────────────────────────────",
|
|
101
134
|
`JWT_SECRET="${values.JWT_SECRET}"`,
|
|
102
135
|
"",
|
|
103
|
-
"# ── Monad On-Chain (
|
|
136
|
+
"# ── Monad On-Chain (withdrawal/settlement) ────",
|
|
104
137
|
`MONAD_RPC_URL="${values.MONAD_RPC_URL}"`,
|
|
105
138
|
`MONAD_PRIVATE_KEY="${values.MONAD_PRIVATE_KEY}"`,
|
|
106
139
|
`MONAD_CONTRACT_ADDRESS="${values.MONAD_CONTRACT_ADDRESS}"`,
|
|
@@ -110,29 +143,38 @@ function writeEnv(values) {
|
|
|
110
143
|
}
|
|
111
144
|
|
|
112
145
|
async function main() {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
console.log(
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
146
|
+
const existing = loadExistingEnv()
|
|
147
|
+
const isEditMode = Object.keys(existing).length > 0
|
|
148
|
+
|
|
149
|
+
console.log(`\n${BOLD}${CYAN}╔══════════════════════════════════════════════════════╗`)
|
|
150
|
+
if (isEditMode) {
|
|
151
|
+
console.log(`║ @moncircle/sdk — Edit Configuration ║`)
|
|
152
|
+
} else {
|
|
153
|
+
console.log(`║ @moncircle/sdk — Setup Wizard ║`)
|
|
154
|
+
}
|
|
155
|
+
console.log(`╚══════════════════════════════════════════════════════╝${RESET}\n`)
|
|
120
156
|
|
|
121
|
-
if (
|
|
122
|
-
console.log(` ${
|
|
157
|
+
if (isEditMode) {
|
|
158
|
+
console.log(` ${GREEN}✓ Existing .env found.${RESET} ${DIM}Press Enter to keep a value unchanged.${RESET}`)
|
|
159
|
+
console.log(` ${DIM}Leave blank to clear a value.${RESET}\n`)
|
|
160
|
+
} else {
|
|
161
|
+
console.log(` ${DIM}Writing to: ${ENV_PATH}${RESET}\n`)
|
|
123
162
|
}
|
|
124
163
|
|
|
125
|
-
const
|
|
164
|
+
const updated = { ...existing }
|
|
126
165
|
|
|
127
166
|
for (const field of fields) {
|
|
128
|
-
const currentVal = existing[field.key]
|
|
167
|
+
const currentVal = existing[field.key] ?? field.default
|
|
129
168
|
const requiredTag = field.required ? ` ${RED}*required${RESET}` : ""
|
|
169
|
+
|
|
130
170
|
console.log(` ${BOLD}${field.label}${RESET}${requiredTag}`)
|
|
131
|
-
|
|
171
|
+
if (!isEditMode || !existing[field.key]) {
|
|
172
|
+
console.log(` ${DIM}${field.hint}${RESET}`)
|
|
173
|
+
}
|
|
132
174
|
|
|
133
175
|
let val = ""
|
|
134
176
|
while (true) {
|
|
135
|
-
val = await ask(`
|
|
177
|
+
val = await ask(` →`, currentVal)
|
|
136
178
|
if (field.required && !val) {
|
|
137
179
|
console.log(` ${RED}✖ This field is required.${RESET}`)
|
|
138
180
|
} else {
|
|
@@ -140,17 +182,23 @@ async function main() {
|
|
|
140
182
|
}
|
|
141
183
|
}
|
|
142
184
|
|
|
143
|
-
|
|
185
|
+
updated[field.key] = val
|
|
144
186
|
console.log()
|
|
145
187
|
}
|
|
146
188
|
|
|
147
|
-
|
|
189
|
+
if (isEditMode) {
|
|
190
|
+
// Merge changes into existing file (preserves comments, order, and extra vars)
|
|
191
|
+
const merged = mergeEnv(existing, updated)
|
|
192
|
+
fs.writeFileSync(ENV_PATH, merged, "utf-8")
|
|
193
|
+
console.log(`${GREEN}${BOLD} ✅ .env updated successfully!${RESET}\n`)
|
|
194
|
+
} else {
|
|
195
|
+
writeFreshEnv(updated)
|
|
196
|
+
console.log(`${GREEN}${BOLD} ✅ .env created successfully!${RESET}\n`)
|
|
197
|
+
}
|
|
148
198
|
|
|
149
|
-
console.log(`${GREEN}${BOLD} ✅ .env file written successfully!${RESET}\n`)
|
|
150
199
|
console.log(` Next steps:`)
|
|
151
|
-
console.log(` ${CYAN}npm run dev${RESET}
|
|
152
|
-
console.log(` ${CYAN}npm
|
|
153
|
-
console.log(` ${CYAN}npm start${RESET} — run production build\n`)
|
|
200
|
+
console.log(` ${CYAN}npm run dev${RESET} — start the development server`)
|
|
201
|
+
console.log(` ${CYAN}npm start${RESET} — run production build\n`)
|
|
154
202
|
|
|
155
203
|
rl.close()
|
|
156
204
|
}
|