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/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)}${dimOrange(suffix)}: `);
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
- // Arrow-key + Enter dropdown selector
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
- // Fallback for non-TTY (piped): numbered list
19
- console.log(` ${orange('โ—†')} ${bold(message)}`);
20
- choices.forEach((choice, i) => console.log(` ${dimOrange((i + 1) + '.')} ${choice.name}`));
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
- // Move up to redraw
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 = i === selected ? bold(orange(choice.name)) : dimOrange(choice.name);
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') { // up / left
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') { // down / right
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(` ${orange('โ—†')} ${bold(message)}`);
102
+ console.log(`\n ${orange('โ—†')} ${bold(message)}`);
66
103
  choices.forEach((choice, i) => {
67
- if (i === selected) console.log(` ${orange('โ–ถ ')}${bold(orange(choice.name))}`);
68
- else console.log(` ${dimOrange(' ' + choice.name)}`);
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') { // Ctrl+C
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') ? cyan(value) : white(String(value));
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) { addLine(` ${dim(figures.line)} ${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(48))}`); }
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(' by SPARROW AI SOLUTION'),
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 dependencies (git, gh, vercel)' },
149
- { cmd: 'clean', desc: 'Remove local data, env files, or logout from Vercel / GitHub' },
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 clean`,
163
- ` ${dimOrange('$')} npx fabrica-e-commerce env`,
164
- ` ${dimOrange('$')} npx fabrica-e-commerce rerun`,
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(52)),
175
+ dimOrange(' ' + 'โ”€'.repeat(60)),
170
176
  '',
171
177
  cmdRows,
172
178
  '',
173
179
  bold(orange(' Examples')),
174
- dimOrange(' ' + 'โ”€'.repeat(52)),
180
+ dimOrange(' ' + 'โ”€'.repeat(60)),
175
181
  '',
176
182
  examples,
177
183
  '',
178
- dim(` Creator: SPARROW AI SOLUTION ยท MIT License`),
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: 1 },
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 badge = orange(`[${step}/${total}]`);
195
- const line = `${badge} ${bold(white(title))}`;
196
- console.log(`\n ${dimOrange(figures.arrowRight)} ${line}`);
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
  }