fabrica-e-commerce 0.2.1 โ 0.2.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.
- 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/sql.js +1 -1
- 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/sql.js
CHANGED
|
@@ -191,7 +191,7 @@ INSERT INTO public.products (id,name,price,main_image_url,gallery_image_urls,cat
|
|
|
191
191
|
('burgundy-blazer-cream-trousers','Burgundy Blazer with Cream Trousers',985.00,'/thudarum-burgundy-blazer-combo.jpg',ARRAY['/thudarum-burgundy-combo-detail.jpg','/thudarum-burgundy-combo-side.jpg'],'Separates','A striking burgundy blazer combination with cream trousers for a polished statement look.',ARRAY['Premium wool construction','Double-breasted design','Gold-tone buttons','Tailored separates'],ARRAY['38','40','42','44','46','48'],'best_sellers',ARRAY['executive','evening'],10,true),
|
|
192
192
|
('camel-trench-coat','Camel Trench Coat',645.00,'/camel-trench-coat-elegant.jpg',ARRAY['/minimalist-fashion-studio-elegant-clothing.jpg'],'Outerwear','A refined camel trench coat with a versatile silhouette for smart everyday dressing.',ARRAY['Water-resistant cotton blend','Belted waist','Classic storm flap'],ARRAY['S','M','L','XL'],'general',ARRAY['heritage','contemporary'],20,true),
|
|
193
193
|
('classic-taupe-double-breasted-suit','Classic Taupe Double-Breasted Suit',1289.00,'/thudarum-taupe-suit-hero.jpg',ARRAY['/thudarum-taupe-suit-detail.jpg','/thudarum-taupe-suit-side.jpg'],'Suits','An impeccably tailored double-breasted suit in refined taupe wool with peak lapels and matching trousers.',ARRAY['100% Italian wool','Double-breasted closure','Peak lapels','Complete with trousers'],ARRAY['38','40','42','44','46','48'],'new_arrivals',ARRAY['executive','contemporary'],10,true),
|
|
194
|
-
('elegant-black-wool-trousers','Elegant Black Wool Trousers',325.00,'/elegant-black-wool-trousers.jpg',ARRAY[],'Trousers','A foundational black wool trouser with a sharp line and versatile formal appeal.',ARRAY['Wool blend','Pressed front crease','Tailored waistband'],ARRAY['30','32','34','36','38'],'general',ARRAY[],30,true),
|
|
194
|
+
('elegant-black-wool-trousers','Elegant Black Wool Trousers',325.00,'/elegant-black-wool-trousers.jpg',ARRAY[]::text[],'Trousers','A foundational black wool trouser with a sharp line and versatile formal appeal.',ARRAY['Wool blend','Pressed front crease','Tailored waistband'],ARRAY['30','32','34','36','38'],'general',ARRAY[]::text[],30,true),
|
|
195
195
|
('heritage-green-check-blazer','Heritage Green Check Blazer',895.00,'/thudarum-green-check-blazer.jpg',ARRAY['/thudarum-green-check-detail.jpg','/thudarum-green-check-side.jpg'],'Blazers','A distinguished houndstooth blazer with classic green patterning, refined tailoring, and gold-tone buttons.',ARRAY['Premium wool houndstooth','Double-breasted design','Gold-tone buttons','Made in England'],ARRAY['38','40','42','44','46','48'],'new_arrivals',ARRAY['heritage'],20,true),
|
|
196
196
|
('luxe-burgundy-evening-suit','Luxe Burgundy Evening Suit',1545.00,'/thudarum-burgundy-evening-suit.jpg',ARRAY['/thudarum-burgundy-suit-detail.jpg','/thudarum-burgundy-suit-side.jpg'],'Suits','A sophisticated burgundy suit designed for elevated evening occasions with a timeless tailored silhouette.',ARRAY['Fine Italian wool','Double-breasted style','Tone-on-tone buttons','Full suit ensemble'],ARRAY['38','40','42','44','46','48'],'new_arrivals',ARRAY['executive','evening'],30,true),
|
|
197
197
|
('minimalist-white-linen-shirt','Minimalist White Linen Shirt',245.00,'/minimalist-white-linen-shirt-fashion.jpg',ARRAY['/ivory-silk-blouse-minimal.jpg'],'Shirts','A clean white linen shirt designed for effortless layering and warm-weather polish.',ARRAY['Premium linen','Relaxed tailored fit','Breathable finish'],ARRAY['S','M','L','XL'],'general',ARRAY['contemporary'],10,true),
|
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
|
}
|