fabrica-e-commerce 0.2.2 โ 0.2.4
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 +113 -78
- package/package.json +1 -1
- package/src/cli.js +98 -46
- package/src/deploy.js +111 -27
- package/src/deps.js +397 -61
- package/src/prompt.js +59 -22
- package/src/ui.js +37 -29
package/src/prompt.js
CHANGED
|
@@ -1,23 +1,65 @@
|
|
|
1
1
|
import readline from 'node:readline/promises';
|
|
2
2
|
import { stdin as input, stdout as output } from 'node:process';
|
|
3
|
-
import { orange, dimOrange, bold } from './ui.js';
|
|
3
|
+
import { orange, dimOrange, bold, dim, green, cyan } from './ui.js';
|
|
4
4
|
|
|
5
|
-
// Raw readline ask (no frills)
|
|
6
5
|
export async function ask(message, defaultValue = '') {
|
|
7
6
|
const rl = readline.createInterface({ input, output });
|
|
8
|
-
const suffix = defaultValue ? ` (${defaultValue})` : '';
|
|
9
|
-
const answer = await rl.question(` ${orange('?')} ${bold(message)}${
|
|
7
|
+
const suffix = defaultValue ? dim(` (${defaultValue})`) : '';
|
|
8
|
+
const answer = await rl.question(` ${orange('?')} ${bold(message)}${suffix}: `);
|
|
10
9
|
rl.close();
|
|
11
10
|
return answer.trim() || defaultValue;
|
|
12
11
|
}
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
export async function askPassword(message) {
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
process.stdout.write(` ${orange('๐')} ${bold(message)}: `);
|
|
16
|
+
|
|
17
|
+
if (!process.stdin.isTTY) {
|
|
18
|
+
const rl = readline.createInterface({ input, output });
|
|
19
|
+
rl.question('', (answer) => {
|
|
20
|
+
rl.close();
|
|
21
|
+
resolve(answer.trim());
|
|
22
|
+
});
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let password = '';
|
|
27
|
+
process.stdin.setRawMode(true);
|
|
28
|
+
process.stdin.resume();
|
|
29
|
+
process.stdin.setEncoding('utf8');
|
|
30
|
+
|
|
31
|
+
function onKey(key) {
|
|
32
|
+
if (key === '\r' || key === '\n') {
|
|
33
|
+
process.stdin.setRawMode(false);
|
|
34
|
+
process.stdin.pause();
|
|
35
|
+
process.stdin.removeListener('data', onKey);
|
|
36
|
+
process.stdout.write('\n');
|
|
37
|
+
resolve(password);
|
|
38
|
+
} else if (key === '\u007f' || key === '\b') {
|
|
39
|
+
if (password.length > 0) {
|
|
40
|
+
password = password.slice(0, -1);
|
|
41
|
+
process.stdout.write('\b \b');
|
|
42
|
+
}
|
|
43
|
+
} else if (key === '\x03') {
|
|
44
|
+
process.stdin.setRawMode(false);
|
|
45
|
+
process.exit(0);
|
|
46
|
+
} else {
|
|
47
|
+
password += key;
|
|
48
|
+
process.stdout.write(dim('โข'));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
process.stdin.on('data', onKey);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
15
56
|
export async function choose(message, choices) {
|
|
16
57
|
return new Promise((resolve) => {
|
|
17
58
|
if (!process.stdin.isTTY) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
59
|
+
console.log(`\n ${orange('โ')} ${bold(message)}`);
|
|
60
|
+
choices.forEach((choice, i) =>
|
|
61
|
+
console.log(` ${dimOrange((i + 1) + '.')} ${choice.name}`)
|
|
62
|
+
);
|
|
21
63
|
const rl = readline.createInterface({ input, output });
|
|
22
64
|
rl.question(` ${orange('?')} Select number (1): `, (answer) => {
|
|
23
65
|
rl.close();
|
|
@@ -31,44 +73,39 @@ export async function choose(message, choices) {
|
|
|
31
73
|
let selected = 0;
|
|
32
74
|
|
|
33
75
|
function render(first) {
|
|
34
|
-
if (!first) {
|
|
35
|
-
|
|
36
|
-
process.stdout.write(`\x1b[${choices.length + 1}A`);
|
|
37
|
-
}
|
|
38
|
-
console.log(` ${orange('โ')} ${bold(message)}`);
|
|
76
|
+
if (!first) process.stdout.write(`\x1b[${choices.length + 1}A`);
|
|
77
|
+
console.log(`\n ${orange('โ')} ${bold(message)}`);
|
|
39
78
|
choices.forEach((choice, i) => {
|
|
40
79
|
const cursor = i === selected ? orange('โถ ') : ' ';
|
|
41
|
-
const label
|
|
80
|
+
const label = i === selected ? bold(orange(choice.name)) : dimOrange(choice.name);
|
|
42
81
|
console.log(` ${cursor}${label}`);
|
|
43
82
|
});
|
|
44
83
|
}
|
|
45
84
|
|
|
46
85
|
render(true);
|
|
47
|
-
|
|
48
86
|
process.stdin.setRawMode(true);
|
|
49
87
|
process.stdin.resume();
|
|
50
88
|
process.stdin.setEncoding('utf8');
|
|
51
89
|
|
|
52
90
|
function onKey(key) {
|
|
53
|
-
if (key === '\x1b[A' || key === '\x1b[D') {
|
|
91
|
+
if (key === '\x1b[A' || key === '\x1b[D') {
|
|
54
92
|
selected = (selected - 1 + choices.length) % choices.length;
|
|
55
93
|
render(false);
|
|
56
|
-
} else if (key === '\x1b[B' || key === '\x1b[C') {
|
|
94
|
+
} else if (key === '\x1b[B' || key === '\x1b[C') {
|
|
57
95
|
selected = (selected + 1) % choices.length;
|
|
58
96
|
render(false);
|
|
59
97
|
} else if (key === '\r' || key === '\n') {
|
|
60
98
|
process.stdin.setRawMode(false);
|
|
61
99
|
process.stdin.pause();
|
|
62
100
|
process.stdin.removeListener('data', onKey);
|
|
63
|
-
// print selected
|
|
64
101
|
process.stdout.write(`\x1b[${choices.length + 1}A`);
|
|
65
|
-
console.log(
|
|
102
|
+
console.log(`\n ${orange('โ')} ${bold(message)}`);
|
|
66
103
|
choices.forEach((choice, i) => {
|
|
67
|
-
if (i === selected) console.log(` ${
|
|
68
|
-
else
|
|
104
|
+
if (i === selected) console.log(` ${green('โถ ')}${bold(green(choice.name))}`);
|
|
105
|
+
else console.log(` ${dimOrange(' ' + choice.name)}`);
|
|
69
106
|
});
|
|
70
107
|
resolve(choices[selected].value);
|
|
71
|
-
} else if (key === '\x03') {
|
|
108
|
+
} else if (key === '\x03') {
|
|
72
109
|
process.stdin.setRawMode(false);
|
|
73
110
|
process.exit(0);
|
|
74
111
|
}
|
package/src/ui.js
CHANGED
|
@@ -16,7 +16,6 @@ export const yellow = (t) => chalk.hex('#ffc846')(t);
|
|
|
16
16
|
export const gray = (t) => chalk.hex('#888888')(t);
|
|
17
17
|
export const white = (t) => chalk.white(t);
|
|
18
18
|
|
|
19
|
-
// strip ANSI for length calculations
|
|
20
19
|
function stripAnsi(s) { return s.replace(/\x1b\[[0-9;]*m/g, ''); }
|
|
21
20
|
|
|
22
21
|
// โโ section buffer โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -57,18 +56,24 @@ export function endSections() { flushSection(); }
|
|
|
57
56
|
export function kv(key, value) {
|
|
58
57
|
const k = bold(orange(key));
|
|
59
58
|
const arrow = dimOrange(figures.arrowRight);
|
|
60
|
-
const v = typeof value === 'string' && value.startsWith('http')
|
|
59
|
+
const v = typeof value === 'string' && value.startsWith('http')
|
|
60
|
+
? cyan(value)
|
|
61
|
+
: white(String(value));
|
|
61
62
|
addLine(` ${dimOrange(figures.pointer)} ${k} ${arrow} ${v}`);
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
export function kvSuccess(key, value) {
|
|
65
|
-
addLine(` ${green(figures.tick)} ${bold(key)} ${dimOrange(figures.arrowRight)} ${green(String(value))}`);
|
|
66
|
+
addLine(` ${green(figures.tick)} ${bold(white(key))} ${dimOrange(figures.arrowRight)} ${green(String(value))}`);
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
export function kvFail(key, value) {
|
|
69
70
|
addLine(` ${red(figures.cross)} ${bold(key)} ${dimOrange(figures.arrowRight)} ${red(String(value))}`);
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
export function kvPending(key, value) {
|
|
74
|
+
addLine(` ${dimOrange(figures.bullet)} ${bold(dimOrange(key))} ${dimOrange(figures.arrowRight)} ${dim(String(value))}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
72
77
|
// spinner โ inline stdout, then adds result line to buffer
|
|
73
78
|
const CLEAR_LINE = '\x1b[K';
|
|
74
79
|
export function spinner(text) {
|
|
@@ -76,7 +81,7 @@ export function spinner(text) {
|
|
|
76
81
|
return {
|
|
77
82
|
succeed(msg) {
|
|
78
83
|
process.stdout.write(`\r${CLEAR_LINE}`);
|
|
79
|
-
addLine(` ${green(figures.tick)} ${msg}`);
|
|
84
|
+
addLine(` ${green(figures.tick)} ${white(msg)}`);
|
|
80
85
|
},
|
|
81
86
|
fail(msg) {
|
|
82
87
|
process.stdout.write(`\r${CLEAR_LINE}`);
|
|
@@ -85,11 +90,12 @@ export function spinner(text) {
|
|
|
85
90
|
};
|
|
86
91
|
}
|
|
87
92
|
|
|
88
|
-
export function log(text)
|
|
89
|
-
export function logInfo(text) { addLine(` ${cyan(figures.info)} ${text}`); }
|
|
93
|
+
export function log(text) { addLine(` ${dimOrange(figures.line)} ${text}`); }
|
|
94
|
+
export function logInfo(text) { addLine(` ${cyan(figures.info)} ${cyan(text)}`); }
|
|
90
95
|
export function logWarn(text) { addLine(` ${yellow(figures.warning)} ${yellow(text)}`); }
|
|
96
|
+
export function logSuccess(text) { addLine(` ${green(figures.tick)} ${green(text)}`); }
|
|
91
97
|
|
|
92
|
-
export function divider() { addLine(` ${dimOrange('โ'.repeat(
|
|
98
|
+
export function divider() { addLine(` ${dimOrange('โ'.repeat(50))}`); }
|
|
93
99
|
|
|
94
100
|
// sub-step block
|
|
95
101
|
export function subBox(lines, { isError = false } = {}) {
|
|
@@ -125,7 +131,7 @@ export function banner() {
|
|
|
125
131
|
|
|
126
132
|
const subtitle = [
|
|
127
133
|
cyan(' Supabase + Vercel ยท Deploy in minutes'),
|
|
128
|
-
dim('
|
|
134
|
+
dim(' by SPARROW AI SOLUTION'),
|
|
129
135
|
].join('\n');
|
|
130
136
|
|
|
131
137
|
console.log(boxen(`${gradientArt}\n\n${subtitle}`, {
|
|
@@ -142,47 +148,48 @@ export function help() {
|
|
|
142
148
|
|
|
143
149
|
const commands = [
|
|
144
150
|
{ cmd: 'build', desc: 'Connect Supabase ยท collect secrets ยท deploy or run locally' },
|
|
145
|
-
{ cmd: 'list', desc: 'Show all Fabrica projects (local & cloud)'
|
|
146
|
-
{ cmd: 'env', desc: 'View and update environment variables for any project'
|
|
147
|
-
{ cmd: 'rerun', desc: 'Re-run or re-open an existing project'
|
|
148
|
-
{ cmd: 'vins', desc: 'Verify & auto-install CLI
|
|
149
|
-
{ cmd: 'clean', desc: '
|
|
150
|
-
{ cmd: 'info', desc: 'Package, bridge, repo and storage info'
|
|
151
|
-
{ cmd: 'help', desc: 'Show this help screen'
|
|
151
|
+
{ cmd: 'list', desc: 'Show all Fabrica projects (local & cloud)' },
|
|
152
|
+
{ cmd: 'env', desc: 'View and update environment variables for any project' },
|
|
153
|
+
{ cmd: 'rerun', desc: 'Re-run or re-open an existing project' },
|
|
154
|
+
{ cmd: 'vins', desc: 'Verify & auto-install CLI deps (git, gh, vercel)' },
|
|
155
|
+
{ cmd: 'clean', desc: 'Clean data, env files, or logout from Vercel / GitHub' },
|
|
156
|
+
{ cmd: 'info', desc: 'Package, bridge, repo and storage info' },
|
|
157
|
+
{ cmd: 'help', desc: 'Show this help screen' },
|
|
152
158
|
];
|
|
153
159
|
|
|
154
160
|
const cmdRows = commands.map(({ cmd, desc }) =>
|
|
155
|
-
` ${bold(orange(cmd.padEnd(10)))} ${dim(desc)}`
|
|
161
|
+
` ${dimOrange(figures.pointer)} ${bold(orange(cmd.padEnd(10)))} ${dim(desc)}`
|
|
156
162
|
).join('\n');
|
|
157
163
|
|
|
158
164
|
const examples = [
|
|
159
|
-
` ${dimOrange('$')} npx fabrica-e-commerce build`,
|
|
160
|
-
` ${dimOrange('$')} npx fabrica-e-commerce vins`,
|
|
161
|
-
` ${dimOrange('$')} npx fabrica-e-commerce list`,
|
|
162
|
-
` ${dimOrange('$')} npx fabrica-e-commerce
|
|
163
|
-
` ${dimOrange('$')} npx fabrica-e-commerce env`,
|
|
164
|
-
` ${dimOrange('$')} npx fabrica-e-commerce
|
|
165
|
+
` ${dimOrange('$')} ${cyan('npx fabrica-e-commerce build')} ${dim('# start a new store')}`,
|
|
166
|
+
` ${dimOrange('$')} ${cyan('npx fabrica-e-commerce vins')} ${dim('# check dependencies')}`,
|
|
167
|
+
` ${dimOrange('$')} ${cyan('npx fabrica-e-commerce list')} ${dim('# see all projects')}`,
|
|
168
|
+
` ${dimOrange('$')} ${cyan('npx fabrica-e-commerce rerun')} ${dim('# open existing project')}`,
|
|
169
|
+
` ${dimOrange('$')} ${cyan('npx fabrica-e-commerce env')} ${dim('# update env variables')}`,
|
|
170
|
+
` ${dimOrange('$')} ${cyan('npx fabrica-e-commerce clean')} ${dim('# clean up data / logout')}`,
|
|
165
171
|
].join('\n');
|
|
166
172
|
|
|
167
173
|
const content = [
|
|
168
174
|
bold(orange(' Commands')),
|
|
169
|
-
dimOrange(' ' + 'โ'.repeat(
|
|
175
|
+
dimOrange(' ' + 'โ'.repeat(60)),
|
|
170
176
|
'',
|
|
171
177
|
cmdRows,
|
|
172
178
|
'',
|
|
173
179
|
bold(orange(' Examples')),
|
|
174
|
-
dimOrange(' ' + 'โ'.repeat(
|
|
180
|
+
dimOrange(' ' + 'โ'.repeat(60)),
|
|
175
181
|
'',
|
|
176
182
|
examples,
|
|
177
183
|
'',
|
|
178
|
-
|
|
184
|
+
dimOrange(' ' + 'โ'.repeat(60)),
|
|
185
|
+
dim(` Creator: SPARROW AI SOLUTION ยท MIT License ยท v${process.env.npm_package_version || ''}`),
|
|
179
186
|
].join('\n');
|
|
180
187
|
|
|
181
188
|
console.log(dimOrange(' โ'));
|
|
182
189
|
console.log(boxen(content, {
|
|
183
190
|
title: ` ${bold(orange('Help & Commands'))} `,
|
|
184
191
|
titleAlignment: 'left',
|
|
185
|
-
padding: { top: 1, bottom: 1, left: 1, right:
|
|
192
|
+
padding: { top: 1, bottom: 1, left: 1, right: 2 },
|
|
186
193
|
borderStyle: 'round',
|
|
187
194
|
borderColor: '#ff8a00',
|
|
188
195
|
}));
|
|
@@ -191,7 +198,8 @@ export function help() {
|
|
|
191
198
|
// โโ step progress โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
192
199
|
export function stepHeader(step, total, title) {
|
|
193
200
|
endSections();
|
|
194
|
-
const
|
|
195
|
-
const line = `${
|
|
196
|
-
|
|
201
|
+
const pill = chalk.bgHex('#ff8a00').black(` ${step}/${total} `);
|
|
202
|
+
const line = `${pill} ${bold(white(title))}`;
|
|
203
|
+
const bar = dimOrange('โ'.repeat(Math.max(0, 52 - stripAnsi(title).length - 6)));
|
|
204
|
+
console.log(`\n ${dimOrange(figures.arrowRight)} ${line} ${bar}`);
|
|
197
205
|
}
|