@moncircle/sdk 1.0.1 → 1.0.3

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 (2) hide show
  1. package/package.json +3 -4
  2. 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.1",
3
+ "version": "1.0.3",
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",
@@ -19,8 +19,7 @@
19
19
  "setup": "node scripts/setup.js",
20
20
  "dev": "ts-node-dev --respawn --transpile-only src/bin.ts",
21
21
  "build": "tsc",
22
- "start": "node dist/bin.js",
23
- "prepublishOnly": "npm run build"
22
+ "start": "node dist/bin.js"
24
23
  },
25
24
  "keywords": [
26
25
  "loyalty",
@@ -50,4 +49,4 @@
50
49
  "peerDependencies": {
51
50
  "express": ">=4.0.0"
52
51
  }
53
- }
52
+ }
package/scripts/setup.js CHANGED
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * mon-loyalty-api setup script
4
- * Run: npm run setup OR npx mon-loyalty-api setup
3
+ * mon-loyalty-api interactive setup wizard
4
+ * Run: npm run setup OR npx mon-loyalty-setup
5
5
  *
6
- * Walks the dev through all required ENV vars and writes them to .env
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 ? ` ${YELLOW}[${defaultVal}]${RESET}` : ""
27
- rl.question(` ${question}${hint}: `, (answer) => {
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: "e.g. mongodb+srv://user:pass@cluster.mongodb.net/mon-loyalty",
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: "Any long random string for signing tokens",
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 (required for on-chain withdrawals)",
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: "Your master wallet private key (used for on-chain settlement — KEEP SECRET)",
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 MonLoyalty smart contract address (leave blank for now)",
73
+ hint: "Deployed contract address (leave blank for now)",
72
74
  required: false,
73
75
  default: ""
74
76
  }
75
77
  ]
76
78
 
77
- async function loadExistingEnv() {
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 existing = {}
83
+ const map = {}
81
84
  for (const line of content.split("\n")) {
82
- const match = line.match(/^([^#=]+)=(.*)$/)
83
- if (match) existing[match[1].trim()] = match[2].trim().replace(/^"|"$/g, "")
85
+ const match = line.match(/^\s*([^#=\s][^=]*)=(.*)$/)
86
+ if (match) map[match[1].trim()] = match[2].trim().replace(/^"|"$/g, "")
84
87
  }
85
- return existing
88
+ return map
86
89
  }
87
90
 
88
- function writeEnv(values) {
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 mon-loyalty-api setup",
91
- "# Run: npx mon-loyalty-api setup to reconfigure",
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 (for withdrawal/settlement) ",
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
- console.log(`\n${BOLD}${CYAN}╔══════════════════════════════════════════════╗`)
114
- console.log(`║ MON Loyalty API Setup Wizard ║`)
115
- console.log(`╚══════════════════════════════════════════════╝${RESET}\n`)
116
- console.log(` This will create a ${BOLD}.env${RESET} file in: ${YELLOW}${process.cwd()}${RESET}\n`)
117
-
118
- const existing = await loadExistingEnv()
119
- const hasExisting = Object.keys(existing).length > 0
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 (hasExisting) {
122
- console.log(` ${YELLOW}⚠ An existing .env was found. Press Enter to keep current values.${RESET}\n`)
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 values = {}
164
+ const updated = { ...existing }
126
165
 
127
166
  for (const field of fields) {
128
- const currentVal = existing[field.key] || field.default
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
- console.log(` ${CYAN}${field.hint}${RESET}`)
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(` Enter value`, currentVal)
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
- values[field.key] = val
185
+ updated[field.key] = val
144
186
  console.log()
145
187
  }
146
188
 
147
- writeEnv(values)
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} — start the development server`)
152
- console.log(` ${CYAN}npm run build${RESET} build for production`)
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
  }