soundbip 2.1.2 → 2.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/index.js +92 -27
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const readline = require('readline');
|
|
3
3
|
const https = require('https');
|
|
4
|
-
|
|
4
|
+
// child_process used in openBrowser()
|
|
5
5
|
|
|
6
6
|
const API_URL = 'api.dltpays.com';
|
|
7
|
-
const VERSION = '2.
|
|
7
|
+
const VERSION = '2.2.0';
|
|
8
8
|
|
|
9
9
|
// Colors
|
|
10
10
|
const c = {
|
|
@@ -112,19 +112,25 @@ function apiCall(method, path, data = null) {
|
|
|
112
112
|
port: 443,
|
|
113
113
|
path: `/api/v1${path}`,
|
|
114
114
|
method,
|
|
115
|
-
headers: { 'Content-Type': 'application/json' }
|
|
115
|
+
headers: { 'Content-Type': 'application/json' },
|
|
116
|
+
timeout: 30000
|
|
116
117
|
};
|
|
117
118
|
const req = https.request(options, res => {
|
|
118
119
|
let body = '';
|
|
119
120
|
res.on('data', chunk => body += chunk);
|
|
120
121
|
res.on('end', () => {
|
|
121
122
|
try {
|
|
122
|
-
|
|
123
|
+
const parsed = JSON.parse(body);
|
|
124
|
+
resolve(parsed);
|
|
123
125
|
} catch {
|
|
124
|
-
|
|
126
|
+
reject(new Error(`Server returned invalid response (HTTP ${res.statusCode})`));
|
|
125
127
|
}
|
|
126
128
|
});
|
|
127
129
|
});
|
|
130
|
+
req.on('timeout', () => {
|
|
131
|
+
req.destroy();
|
|
132
|
+
reject(new Error('Request timed out — check your internet connection'));
|
|
133
|
+
});
|
|
128
134
|
req.on('error', reject);
|
|
129
135
|
if (data) req.write(JSON.stringify(data));
|
|
130
136
|
req.end();
|
|
@@ -132,18 +138,22 @@ function apiCall(method, path, data = null) {
|
|
|
132
138
|
}
|
|
133
139
|
|
|
134
140
|
function openBrowser(url) {
|
|
141
|
+
const { execFile } = require('child_process');
|
|
135
142
|
const platform = process.platform;
|
|
136
|
-
let command;
|
|
137
143
|
|
|
144
|
+
let cmd, args;
|
|
138
145
|
if (platform === 'darwin') {
|
|
139
|
-
|
|
146
|
+
cmd = 'open';
|
|
147
|
+
args = [url];
|
|
140
148
|
} else if (platform === 'win32') {
|
|
141
|
-
|
|
149
|
+
cmd = 'cmd';
|
|
150
|
+
args = ['/c', 'start', '', url];
|
|
142
151
|
} else {
|
|
143
|
-
|
|
152
|
+
cmd = 'xdg-open';
|
|
153
|
+
args = [url];
|
|
144
154
|
}
|
|
145
155
|
|
|
146
|
-
|
|
156
|
+
execFile(cmd, args, (err) => {
|
|
147
157
|
if (err) {
|
|
148
158
|
console.log(`${c.dim} Could not open browser automatically.${c.reset}`);
|
|
149
159
|
}
|
|
@@ -198,6 +208,10 @@ function info(label, value, indent = ' ') {
|
|
|
198
208
|
}
|
|
199
209
|
|
|
200
210
|
function spinner(text) {
|
|
211
|
+
if (!process.stdout.isTTY) {
|
|
212
|
+
console.log(` ${text}`);
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
201
215
|
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
202
216
|
let i = 0;
|
|
203
217
|
process.stdout.write(`\n${c.yellow} ${frames[0]} ${text}${c.reset}`);
|
|
@@ -210,9 +224,12 @@ function spinner(text) {
|
|
|
210
224
|
}
|
|
211
225
|
|
|
212
226
|
function stopSpinner(interval) {
|
|
227
|
+
if (!interval) return;
|
|
213
228
|
clearInterval(interval);
|
|
214
|
-
process.stdout.
|
|
215
|
-
|
|
229
|
+
if (process.stdout.isTTY) {
|
|
230
|
+
process.stdout.clearLine(0);
|
|
231
|
+
process.stdout.cursorTo(0);
|
|
232
|
+
}
|
|
216
233
|
}
|
|
217
234
|
|
|
218
235
|
const PLATFORMS = {
|
|
@@ -471,8 +488,16 @@ async function main() {
|
|
|
471
488
|
console.log(`${c.dim} ${c.reset}${c.cyan}[${num.padStart(2)}]${c.reset} ${name}`);
|
|
472
489
|
});
|
|
473
490
|
console.log('');
|
|
474
|
-
|
|
475
|
-
|
|
491
|
+
let addressCity;
|
|
492
|
+
while (true) {
|
|
493
|
+
const parishChoice = await ask(`${c.cyan} Select parish [1-10]: ${c.reset}`);
|
|
494
|
+
const picked = PARISHES[parishChoice.trim()];
|
|
495
|
+
if (picked) {
|
|
496
|
+
addressCity = picked;
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
console.log(`${c.red} ✗ Please enter a number between 1 and 10${c.reset}`);
|
|
500
|
+
}
|
|
476
501
|
|
|
477
502
|
// Optional fields
|
|
478
503
|
section('📝', 'Additional Details');
|
|
@@ -480,8 +505,8 @@ async function main() {
|
|
|
480
505
|
|
|
481
506
|
const registrationNumber = await askWithValidation(
|
|
482
507
|
`${c.cyan} Guernsey registration number: ${c.reset}`,
|
|
483
|
-
(n) => n.
|
|
484
|
-
'
|
|
508
|
+
(n) => /^\d{4,10}$/.test(n.trim()),
|
|
509
|
+
'Must be a valid numeric registration number (4-10 digits)'
|
|
485
510
|
);
|
|
486
511
|
|
|
487
512
|
const description = await ask(`${c.cyan} Describe your business (optional, max 250 chars): ${c.reset}`);
|
|
@@ -491,19 +516,51 @@ async function main() {
|
|
|
491
516
|
|
|
492
517
|
let referred_by_store = null;
|
|
493
518
|
if (referralCode.trim()) {
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
519
|
+
try {
|
|
520
|
+
const lookupResult = await apiCall('GET', `/store/lookup-referral/${encodeURIComponent(referralCode.trim())}`);
|
|
521
|
+
if (lookupResult.success) {
|
|
522
|
+
referred_by_store = lookupResult.store_id;
|
|
523
|
+
console.log(`${c.green} ✓ Referred by: ${lookupResult.store_name}${c.reset}`);
|
|
524
|
+
} else {
|
|
525
|
+
console.log(`${c.yellow} ⚠ Referral code not found - continuing without referral${c.reset}`);
|
|
526
|
+
}
|
|
527
|
+
} catch {
|
|
528
|
+
console.log(`${c.yellow} ⚠ Could not verify referral code - continuing without referral${c.reset}`);
|
|
500
529
|
}
|
|
501
530
|
}
|
|
502
531
|
|
|
503
532
|
// Platform selection
|
|
504
533
|
showPlatformMenu();
|
|
505
|
-
|
|
506
|
-
|
|
534
|
+
let platform;
|
|
535
|
+
while (true) {
|
|
536
|
+
const platformChoice = await ask(`${c.cyan} Select platform [1-7]: ${c.reset}`);
|
|
537
|
+
platform = PLATFORMS[platformChoice.trim()];
|
|
538
|
+
if (platform) break;
|
|
539
|
+
console.log(`${c.red} ✗ Please enter a number between 1 and 7${c.reset}`);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// Review before submitting
|
|
543
|
+
section('✅', 'Review Your Details');
|
|
544
|
+
console.log('');
|
|
545
|
+
info('Business', storeName);
|
|
546
|
+
info('Website', storeUrl);
|
|
547
|
+
info('Email', email);
|
|
548
|
+
info('Owner', ownerName);
|
|
549
|
+
info('Address', cleanStreet);
|
|
550
|
+
info('Parish', addressCity);
|
|
551
|
+
info('Postcode', addressPostcode);
|
|
552
|
+
info('Reg Number', registrationNumber.trim());
|
|
553
|
+
if (description.trim()) info('Description', description.trim());
|
|
554
|
+
info('Platform', platform.name);
|
|
555
|
+
if (referred_by_store) info('Referral', '✓ Applied');
|
|
556
|
+
console.log('');
|
|
557
|
+
|
|
558
|
+
const confirmReg = await ask(`${c.cyan} Everything correct? [y/n]: ${c.reset}`);
|
|
559
|
+
if (confirmReg.trim().toLowerCase() !== 'y' && confirmReg.trim().toLowerCase() !== 'yes') {
|
|
560
|
+
console.log(`\n${c.yellow} Registration cancelled. Run npx soundbip again to start over.${c.reset}\n`);
|
|
561
|
+
rl.close();
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
507
564
|
|
|
508
565
|
const spin = spinner('Registering your store...');
|
|
509
566
|
|
|
@@ -528,12 +585,20 @@ async function main() {
|
|
|
528
585
|
registerBody.description = description.trim();
|
|
529
586
|
}
|
|
530
587
|
|
|
531
|
-
|
|
588
|
+
let result;
|
|
589
|
+
try {
|
|
590
|
+
result = await apiCall('POST', '/store/register', registerBody);
|
|
591
|
+
} catch (err) {
|
|
592
|
+
stopSpinner(spin);
|
|
593
|
+
console.log(`\n${c.red} ✗ ${err.message}${c.reset}\n`);
|
|
594
|
+
rl.close();
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
532
597
|
|
|
533
598
|
stopSpinner(spin);
|
|
534
599
|
|
|
535
|
-
if (result.
|
|
536
|
-
console.log(`\n${c.red} ✗ Error: ${result
|
|
600
|
+
if (!result || !result.store_id) {
|
|
601
|
+
console.log(`\n${c.red} ✗ Error: ${result?.error || 'Registration failed — unexpected server response'}${c.reset}\n`);
|
|
537
602
|
rl.close();
|
|
538
603
|
return;
|
|
539
604
|
}
|