smoonb 0.0.98 → 0.0.99

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.

Potentially problematic release.


This version of smoonb might be problematic. Click here for more details.

package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smoonb",
3
- "version": "0.0.98",
3
+ "version": "0.0.99",
4
4
  "description": "Complete Supabase backup and migration tool - EXPERIMENTAL VERSION - USE AT YOUR OWN RISK",
5
5
  "preferGlobal": false,
6
6
  "preventGlobalInstall": true,
@@ -24,6 +24,7 @@ const step09EdgeFunctions = require('./steps/09-edge-functions');
24
24
  const step10SupabaseTemp = require('./steps/10-supabase-temp');
25
25
  const step11Migrations = require('./steps/11-migrations');
26
26
  const { sendTelemetry } = require('../../telemetry');
27
+ const ui = require('../../utils/cliUi');
27
28
 
28
29
  // Exportar FUNÇÃO em vez de objeto Command
29
30
  module.exports = async (options) => {
@@ -142,7 +143,7 @@ module.exports = async (options) => {
142
143
  console.log(chalk.yellow(` 2. ${getT('backup.error.databaseUrlStep2')}`));
143
144
  console.log('');
144
145
  console.log(chalk.blue(`💡 ${getT('backup.error.databaseUrlExample')}:`));
145
- console.log(chalk.gray(` ${getT('backup.error.databaseUrlExampleValue')}`));
146
+ ui.hint(` ${getT('backup.error.databaseUrlExampleValue')}`);
146
147
  console.log('');
147
148
  console.log(chalk.red(`🚫 ${getT('backup.error.databaseUrlCancelled')}`));
148
149
  process.exit(1);
@@ -157,9 +158,9 @@ module.exports = async (options) => {
157
158
  console.log(chalk.yellow(` 3. ${getT('backup.error.accessTokenStep3')}`));
158
159
  console.log('');
159
160
  console.log(chalk.blue(`🔗 ${getT('backup.error.accessTokenHowTo')}:`));
160
- console.log(chalk.gray(` ${getT('backup.error.accessTokenStep1Detail')}`));
161
- console.log(chalk.gray(` ${getT('backup.error.accessTokenStep2Detail')}`));
162
- console.log(chalk.gray(` ${getT('backup.error.accessTokenStep3Detail')}`));
161
+ ui.hint(` ${getT('backup.error.accessTokenStep1Detail')}`);
162
+ ui.hint(` ${getT('backup.error.accessTokenStep2Detail')}`);
163
+ ui.hint(` ${getT('backup.error.accessTokenStep3Detail')}`);
163
164
  console.log('');
164
165
  console.log(chalk.red(`🚫 ${getT('backup.error.accessTokenCancelled')}`));
165
166
  process.exit(1);
@@ -169,7 +170,7 @@ module.exports = async (options) => {
169
170
  if (!postgresMajorRaw) {
170
171
  console.log(chalk.red(`❌ ${getT('backup.error.postgresMajorNotSet')}`));
171
172
  console.log(chalk.white(` ${getT('backup.error.postgresMajorInstructions')}`));
172
- printSupabasePostgresMajorHintOnce(getT, chalk);
173
+ printSupabasePostgresMajorHintOnce(getT);
173
174
  console.log('');
174
175
  process.exit(1);
175
176
  }
@@ -177,7 +178,7 @@ module.exports = async (options) => {
177
178
  if (Number.isNaN(parsedMajor) || !isPostgresMajorSupported(parsedMajor, true)) {
178
179
  console.log(chalk.red(`❌ ${getT('backup.error.postgresMajorInvalid')}`));
179
180
  console.log(chalk.white(` ${getT('backup.error.postgresMajorInstructions')}`));
180
- printSupabasePostgresMajorHintOnce(getT, chalk);
181
+ printSupabasePostgresMajorHintOnce(getT);
181
182
  console.log('');
182
183
  process.exit(1);
183
184
  }
@@ -1,10 +1,10 @@
1
- const chalk = require('chalk');
2
1
  const https = require('https');
3
2
  const inquirer = require('inquirer');
4
3
  const path = require('path');
5
4
  const { readEnvFile, writeEnvFile } = require('../../../utils/env');
6
5
  const { APP_CONFIG } = require('../../../config/appConfig');
7
6
  const { t } = require('../../../i18n');
7
+ const ui = require('../../../utils/cliUi');
8
8
  const {
9
9
  createCorrelationId,
10
10
  maskSecret,
@@ -78,12 +78,12 @@ function httpsPost(url, body, headers, timeoutMs) {
78
78
 
79
79
  /**
80
80
  * Exibe erro estruturado + instruções de suporte + bundle (pronto para colar).
81
+ * Usa ui (sem gray/dim) para legibilidade em Windows e copy/paste.
81
82
  * @param {Function} getT
82
- * @param {object} chalk
83
83
  * @param {string} appUrl - APP_CONFIG.appUrl
84
84
  * @param {object} opts
85
85
  */
86
- function printLicenseValidationError(getT, chalk, appUrl, opts) {
86
+ function printLicenseValidationError(getT, appUrl, opts) {
87
87
  const {
88
88
  correlationId,
89
89
  kind,
@@ -137,29 +137,29 @@ function printLicenseValidationError(getT, chalk, appUrl, opts) {
137
137
  : kind === 'parse' ? 'license.error.typeParse'
138
138
  : 'license.error.typeNetwork';
139
139
 
140
- console.log(chalk.red(`\n❌ ${getT('license.error.title')}\n`));
141
- console.log(chalk.white(` ${getT('license.error.whatHappened')}`));
142
- console.log(chalk.white(` ${getT(typeKey)}`));
140
+ ui.error(`\n❌ ${getT('license.error.title')}\n`);
141
+ ui.info(` ${getT('license.error.whatHappened')}`);
142
+ ui.info(` ${getT(typeKey)}`);
143
143
  if (httpStatus != null) {
144
- console.log(chalk.white(` ${getT('license.error.status', { status: `${httpStatus} ${(httpStatusText || '').trim()}` })}`));
144
+ ui.info(` ${getT('license.error.status', { status: `${httpStatus} ${(httpStatusText || '').trim()}` })}`);
145
145
  }
146
146
  if (endpoint || fullUrl) {
147
- console.log(chalk.white(` ${getT('license.error.endpoint', { endpoint: fullUrl || endpoint })}`));
147
+ ui.info(` ${getT('license.error.endpoint', { endpoint: fullUrl || endpoint })}`);
148
148
  }
149
- console.log(chalk.white(` ${getT('license.error.correlationId', { id: correlationId })}\n`));
149
+ ui.info(` ${getT('license.error.correlationId', { id: correlationId })}\n`);
150
150
 
151
- console.log(chalk.cyan(` ${getT('license.error.howToHelp')}`));
152
- console.log(chalk.white(` ${getT('license.error.supportStep1', { url: appUrl })}`));
153
- console.log(chalk.white(` ${getT('license.error.supportStep2')}`));
154
- console.log(chalk.white(` ${getT('license.error.supportStep3')}`));
155
- console.log(chalk.white(` ${getT('license.error.supportStep4')}`));
156
- console.log(chalk.white(` ${getT('license.error.supportStep5')}\n`));
151
+ ui.link(` ${getT('license.error.howToHelp')}`);
152
+ ui.info(` ${getT('license.error.supportStep1', { url: appUrl })}`);
153
+ ui.info(` ${getT('license.error.supportStep2')}`);
154
+ ui.info(` ${getT('license.error.supportStep3')}`);
155
+ ui.info(` ${getT('license.error.supportStep4')}`);
156
+ ui.info(` ${getT('license.error.supportStep5')}\n`);
157
157
 
158
- console.log(chalk.gray(formatBundleForTerminal(bundleText)));
159
- console.log('');
160
- console.log(chalk.cyan(` ${getT('license.error.visit', { url: appUrl })}\n`));
161
- console.log(chalk.gray(` ${getT('license.error.subjectSuggestion', { id: correlationId })}`));
162
- console.log(chalk.gray(` ${getT('license.error.messageSuggestion')}\n`));
158
+ ui.block(formatBundleForTerminal(bundleText));
159
+ ui.info('');
160
+ ui.link(` ${getT('license.error.visit', { url: appUrl })}\n`);
161
+ ui.hint(` ${getT('license.error.subjectSuggestion', { id: correlationId })}`);
162
+ ui.hint(` ${getT('license.error.messageSuggestion')}\n`);
163
163
  }
164
164
 
165
165
  /**
@@ -191,8 +191,8 @@ module.exports = async (options) => {
191
191
  }]);
192
192
  licenseKey = (prompted || '').toString().trim();
193
193
  if (!licenseKey) {
194
- console.log(chalk.red(`❌ ${getT('license.required')}`));
195
- console.log(chalk.cyan(` ${appUrl}`));
194
+ ui.error(`❌ ${getT('license.required')}`);
195
+ ui.link(` ${appUrl}`);
196
196
  process.exit(1);
197
197
  }
198
198
  const currentEnv = await readEnvFile(envPath);
@@ -226,9 +226,9 @@ module.exports = async (options) => {
226
226
  const correlationId = createCorrelationId();
227
227
  const classification = classifyRequestError(err);
228
228
  if (classification.hintUser) {
229
- console.log(chalk.yellow(` ${classification.hintUser}`));
229
+ ui.warn(` ${classification.hintUser}`);
230
230
  }
231
- printLicenseValidationError(getT, chalk, appUrl, {
231
+ printLicenseValidationError(getT, appUrl, {
232
232
  correlationId,
233
233
  kind: classification.kind,
234
234
  endpoint: ENDPOINT_PATH,
@@ -250,7 +250,7 @@ module.exports = async (options) => {
250
250
  const correlationId = createCorrelationId();
251
251
  const responseHeadersStr = safeStringifyHeaders(headers);
252
252
  const responseBodyTruncated = truncateBody(rawBody);
253
- printLicenseValidationError(getT, chalk, appUrl, {
253
+ printLicenseValidationError(getT, appUrl, {
254
254
  correlationId,
255
255
  kind: 'parse',
256
256
  endpoint: ENDPOINT_PATH,
@@ -269,7 +269,7 @@ module.exports = async (options) => {
269
269
  const correlationId = createCorrelationId();
270
270
  const responseHeadersStr = safeStringifyHeaders(headers);
271
271
  const responseBodyTruncated = rawBody ? truncateBody(rawBody) : '';
272
- printLicenseValidationError(getT, chalk, appUrl, {
272
+ printLicenseValidationError(getT, appUrl, {
273
273
  correlationId,
274
274
  kind: 'http',
275
275
  httpStatus: statusCode,
@@ -290,14 +290,14 @@ module.exports = async (options) => {
290
290
  const status = resBody.status || '';
291
291
 
292
292
  if (!valid || ['expired', 'canceled'].includes(status)) {
293
- console.log(chalk.red(`❌ ${getT('license.invalidOrInactive')}`));
294
- console.log(chalk.white(` ${getT('license.openDesktopApp')}`));
295
- console.log(chalk.cyan(` ${appUrl}`));
293
+ ui.error(`❌ ${getT('license.invalidOrInactive')}`);
294
+ ui.info(` ${getT('license.openDesktopApp')}`);
295
+ ui.link(` ${appUrl}`);
296
296
  process.exit(1);
297
297
  }
298
298
 
299
299
  if (status === 'trial') {
300
- console.log(chalk.yellow(` ⚠️ ${getT('license.trialNotice')}`));
300
+ ui.warn(` ⚠️ ${getT('license.trialNotice')}`);
301
301
  }
302
302
 
303
303
  const license = {
@@ -4,6 +4,7 @@ const { execSync } = require('child_process');
4
4
  const { extractPasswordFromDbUrl, ensureCleanLink } = require('../../../utils/supabaseLink');
5
5
  const { cleanDir, countFiles, copyDirSafe } = require('../../../utils/fsExtra');
6
6
  const { t } = require('../../../i18n');
7
+ const ui = require('../../../utils/cliUi');
7
8
 
8
9
  /**
9
10
  * Etapa 10: Migrations Backup (NOVA ETAPA INDEPENDENTE)
@@ -62,7 +63,7 @@ module.exports = async (context) => {
62
63
 
63
64
  if (shouldClean) {
64
65
  await cleanDir(migrationsDir);
65
- console.log(chalk.gray(` - ${getT('backup.steps.migrations.cleaned')}`));
66
+ ui.hint(` - ${getT('backup.steps.migrations.cleaned')}`);
66
67
  }
67
68
 
68
69
  return {
@@ -1,5 +1,6 @@
1
1
  const chalk = require('chalk');
2
2
  const { t } = require('../../i18n');
3
+ const ui = require('../../utils/cliUi');
3
4
 
4
5
  const SUPPORTED_MAJORS_DEFAULT = [15, 17];
5
6
  const SUPPORTED_MAJORS_ADVANCED = [16, 18];
@@ -53,7 +54,7 @@ function showDockerMessagesAndExit(reason, data = {}) {
53
54
  console.log('');
54
55
  console.log(chalk.blue(`🔗 ${getT('docker.download')}`));
55
56
  console.log('');
56
- console.log(chalk.gray(`💡 ${getT('docker.requiredComponents')}`));
57
+ ui.hint(`💡 ${getT('docker.requiredComponents')}`);
57
58
  break;
58
59
 
59
60
  case 'docker_not_running':
@@ -66,7 +67,7 @@ function showDockerMessagesAndExit(reason, data = {}) {
66
67
  console.log('');
67
68
  console.log(chalk.blue(`💡 ${getT('docker.tip')}`));
68
69
  console.log('');
69
- console.log(chalk.gray(`💡 ${getT('docker.requiredComponents')}`));
70
+ ui.hint(`💡 ${getT('docker.requiredComponents')}`);
70
71
  break;
71
72
 
72
73
  case 'supabase_cli_not_found':
@@ -78,7 +79,7 @@ function showDockerMessagesAndExit(reason, data = {}) {
78
79
  console.log('');
79
80
  console.log(chalk.blue(`🔗 ${getT('supabase.installLink')}`));
80
81
  console.log('');
81
- console.log(chalk.gray(`💡 ${getT('supabase.requiredComponents')}`));
82
+ ui.hint(`💡 ${getT('supabase.requiredComponents')}`);
82
83
  break;
83
84
 
84
85
  case 'supabase_cli_outdated':
@@ -89,22 +90,22 @@ function showDockerMessagesAndExit(reason, data = {}) {
89
90
  console.log(chalk.cyan(` ${getT('supabase.cliUpdateCommandGlobal')}`));
90
91
  console.log(chalk.cyan(` ${getT('supabase.cliUpdateCommandLocal')}`));
91
92
  console.log('');
92
- console.log(chalk.gray(`💡 ${getT('supabase.cliUpdateLink')}`));
93
+ ui.hint(`💡 ${getT('supabase.cliUpdateLink')}`);
93
94
  break;
94
95
 
95
96
  case 'supabase_cli_latest_unknown':
96
97
  console.log(chalk.red(`❌ ${getT('supabase.cliLatestUnknown')}`));
97
98
  console.log('');
98
99
  console.log(chalk.yellow(`📋 ${getT('supabase.cliLatestErrorLabel')}`));
99
- console.log(chalk.gray(` ${data.latestError || getT('supabase.cliLatestErrorUnknown')}`));
100
+ ui.hint(` ${data.latestError || getT('supabase.cliLatestErrorUnknown')}`);
100
101
  console.log('');
101
- console.log(chalk.gray(`💡 ${getT('supabase.cliUpdateLink')}`));
102
+ ui.hint(`💡 ${getT('supabase.cliUpdateLink')}`);
102
103
  break;
103
104
  }
104
105
 
105
106
  console.log('');
106
107
  console.log(chalk.red(`🚫 ${getT('docker.cancelled')}`));
107
- console.log(chalk.gray(` ${getT('docker.installComponents')}`));
108
+ ui.hint(` ${getT('docker.installComponents')}`);
108
109
  console.log('');
109
110
 
110
111
  process.exit(1);
@@ -3,6 +3,7 @@ const fs = require('fs').promises;
3
3
  const path = require('path');
4
4
  const { showBetaBanner } = require('../utils/banner');
5
5
  const { t } = require('../i18n');
6
+ const ui = require('../utils/cliUi');
6
7
 
7
8
  // Exportar FUNÇÃO em vez de objeto Command
8
9
  module.exports = async (options) => {
@@ -20,8 +21,8 @@ module.exports = async (options) => {
20
21
  await showConfig(configPath);
21
22
  } else {
22
23
  console.log(chalk.yellow(`💡 ${getT('config.options')}`));
23
- console.log(chalk.gray(` ${getT('config.initOption')}`));
24
- console.log(chalk.gray(` ${getT('config.showOption')}`));
24
+ ui.hint(` ${getT('config.initOption')}`);
25
+ ui.hint(` ${getT('config.showOption')}`);
25
26
  }
26
27
 
27
28
  } catch (error) {
@@ -63,9 +64,9 @@ async function initializeConfig(configPath) {
63
64
  await fs.writeFile(configPath, JSON.stringify(defaultConfig, null, 2));
64
65
  console.log(chalk.green(`✅ ${getT('config.fileCreated', { path: '.smoonbrc' })}`));
65
66
  console.log(chalk.yellow('\n📝 Próximos passos:'));
66
- console.log(chalk.gray(' 1. Edite .smoonbrc com suas credenciais Supabase'));
67
- console.log(chalk.gray(' 2. Substitua os valores placeholder pelos reais'));
68
- console.log(chalk.gray(' 3. Execute: npx smoonb backup'));
67
+ ui.hint(' 1. Edite .smoonbrc com suas credenciais Supabase');
68
+ ui.hint(' 2. Substitua os valores placeholder pelos reais');
69
+ ui.hint(' 3. Execute: npx smoonb backup');
69
70
  } catch (error) {
70
71
  const getT = global.smoonbI18n?.t || t;
71
72
  throw new Error(`${getT('config.error')}: ${error.message}`);
@@ -82,60 +83,60 @@ async function showConfig(configPath) {
82
83
  const config = JSON.parse(configContent);
83
84
 
84
85
  console.log(chalk.green('✅ Arquivo de configuração encontrado'));
85
- console.log(chalk.gray(` - Localização: ${configPath}`));
86
+ ui.hint(` - Localização: ${configPath}`);
86
87
 
87
88
  if (config.supabase?.projectId && config.supabase.projectId !== 'your-project-id-here') {
88
- console.log(chalk.gray(` - Project ID: ${config.supabase.projectId}`));
89
+ ui.hint(` - Project ID: ${config.supabase.projectId}`);
89
90
  } else {
90
91
  console.log(chalk.yellow(' - Project ID: Não configurado'));
91
92
  }
92
93
 
93
94
  if (config.supabase?.url && config.supabase.url !== 'https://your-project-id.supabase.co') {
94
- console.log(chalk.gray(` - Supabase URL: ${config.supabase.url}`));
95
+ ui.hint(` - Supabase URL: ${config.supabase.url}`);
95
96
  } else {
96
97
  console.log(chalk.yellow(' - Supabase URL: Não configurado'));
97
98
  }
98
99
 
99
100
  if (config.supabase?.serviceKey && config.supabase.serviceKey !== 'your-service-key-here') {
100
- console.log(chalk.gray(' - Service Key: Configurada'));
101
+ ui.hint(' - Service Key: Configurada');
101
102
  } else {
102
103
  console.log(chalk.yellow(' - Service Key: Não configurada'));
103
104
  }
104
105
 
105
106
  if (config.supabase?.anonKey && config.supabase.anonKey !== 'your-anon-key-here') {
106
- console.log(chalk.gray(' - Anon Key: Configurada'));
107
+ ui.hint(' - Anon Key: Configurada');
107
108
  } else {
108
109
  console.log(chalk.yellow(' - Anon Key: Não configurada'));
109
110
  }
110
111
 
111
112
  if (config.supabase?.databaseUrl && !config.supabase.databaseUrl.includes('[password]')) {
112
- console.log(chalk.gray(' - Database URL: Configurada'));
113
+ ui.hint(' - Database URL: Configurada');
113
114
  } else {
114
115
  console.log(chalk.yellow(' - Database URL: Não configurada'));
115
116
  }
116
117
 
117
118
  if (config.supabase?.accessToken && config.supabase.accessToken !== 'your-personal-access-token-here') {
118
- console.log(chalk.gray(' - Access Token: Configurado'));
119
+ ui.hint(' - Access Token: Configurado');
119
120
  } else {
120
121
  console.log(chalk.yellow(' - Access Token: Não configurado (obrigatório para Management API)'));
121
122
  }
122
123
 
123
124
  console.log(chalk.blue('\n📊 Configurações de backup:'));
124
- console.log(chalk.gray(` - Output Dir: ${config.backup?.outputDir || './backups'}`));
125
- console.log(chalk.gray(` - Include Functions: ${config.backup?.includeFunctions || true}`));
126
- console.log(chalk.gray(` - Include Storage: ${config.backup?.includeStorage || true}`));
127
- console.log(chalk.gray(` - Include Auth: ${config.backup?.includeAuth || true}`));
128
- console.log(chalk.gray(` - Include Realtime: ${config.backup?.includeRealtime || true}`));
125
+ ui.hint(` - Output Dir: ${config.backup?.outputDir || './backups'}`);
126
+ ui.hint(` - Include Functions: ${config.backup?.includeFunctions || true}`);
127
+ ui.hint(` - Include Storage: ${config.backup?.includeStorage || true}`);
128
+ ui.hint(` - Include Auth: ${config.backup?.includeAuth || true}`);
129
+ ui.hint(` - Include Realtime: ${config.backup?.includeRealtime || true}`);
129
130
 
130
131
  console.log(chalk.blue('\n🔄 Configurações de restore:'));
131
- console.log(chalk.gray(` - Clean Restore: ${config.restore?.cleanRestore || true}`));
132
- console.log(chalk.gray(` - Verify After Restore: ${config.restore?.verifyAfterRestore || true}`));
132
+ ui.hint(` - Clean Restore: ${config.restore?.cleanRestore || true}`);
133
+ ui.hint(` - Verify After Restore: ${config.restore?.verifyAfterRestore || true}`);
133
134
 
134
135
  } catch (error) {
135
136
  const getT = global.smoonbI18n?.t || t;
136
137
  if (error.code === 'ENOENT') {
137
138
  console.log(chalk.yellow(`⚠️ ${getT('config.fileNotFound', { path: configPath })}`));
138
- console.log(chalk.gray(' - Use: npx smoonb config --init'));
139
+ ui.hint(' - Use: npx smoonb config --init');
139
140
  } else {
140
141
  throw new Error(`${getT('config.error')}: ${error.message}`);
141
142
  }
@@ -3,6 +3,7 @@ const { ensureBin, runCommand } = require('../utils/cli');
3
3
  const { readConfig, validateFor } = require('../utils/config');
4
4
  const { showBetaBanner } = require('../utils/banner');
5
5
  const { t } = require('../i18n');
6
+ const ui = require('../utils/cliUi');
6
7
 
7
8
  // Exportar FUNÇÃO em vez de objeto Command
8
9
  module.exports = async (_options) => {
@@ -23,13 +24,13 @@ module.exports = async (_options) => {
23
24
 
24
25
  console.log(chalk.blue(`⚡ ${getT('functions.availableCommands')}`));
25
26
  console.log(chalk.yellow(`\n📋 ${getT('functions.list')}`));
26
- console.log(chalk.gray(' npx smoonb functions list'));
27
+ ui.hint(' npx smoonb functions list');
27
28
  console.log(chalk.yellow(`\n🚀 ${getT('functions.deploy')}`));
28
- console.log(chalk.gray(' npx smoonb functions push'));
29
+ ui.hint(' npx smoonb functions push');
29
30
  console.log(chalk.yellow(`\n📥 ${getT('functions.pull')}`));
30
- console.log(chalk.gray(' npx smoonb functions pull'));
31
+ ui.hint(' npx smoonb functions pull');
31
32
  console.log(chalk.yellow(`\n💡 ${getT('functions.moreOptions')}`));
32
- console.log(chalk.gray(' supabase functions --help'));
33
+ ui.hint(' supabase functions --help');
33
34
 
34
35
  } catch (error) {
35
36
  const getT = global.smoonbI18n?.t || t;
@@ -120,9 +121,9 @@ async function pullFunctions(projectRef) {
120
121
 
121
122
  console.log(chalk.yellow('⚠️ Pull de Edge Functions não é oficialmente suportado pelo Supabase CLI'));
122
123
  console.log(chalk.yellow('💡 Para baixar código das functions remotas:'));
123
- console.log(chalk.gray(' 1. Use o Dashboard do Supabase'));
124
- console.log(chalk.gray(' 2. Ou clone o código do seu repositório Git'));
125
- console.log(chalk.gray(' 3. Ou use a API do Supabase diretamente'));
124
+ ui.hint(' 1. Use o Dashboard do Supabase');
125
+ ui.hint(' 2. Ou clone o código do seu repositório Git');
126
+ ui.hint(' 3. Ou use a API do Supabase diretamente');
126
127
  console.log(chalk.blue('\n📚 Documentação: https://supabase.com/docs/guides/functions'));
127
128
 
128
129
  } catch (error) {
@@ -2,6 +2,7 @@ const chalk = require('chalk');
2
2
  const path = require('path');
3
3
  const { execSync } = require('child_process');
4
4
  const { t } = require('../../../i18n');
5
+ const ui = require('../../../utils/cliUi');
5
6
 
6
7
  /**
7
8
  * Etapa 3: Restaurar Database via psql
@@ -62,7 +63,7 @@ module.exports = async ({ backupFilePath, targetDatabaseUrl }) => {
62
63
  execSync(restoreCmd, { stdio: 'inherit', encoding: 'utf8' });
63
64
 
64
65
  console.log(chalk.green(` ✅ ${getT('restore.steps.database.success')}`));
65
- console.log(chalk.gray(` ℹ️ ${getT('restore.steps.database.normalErrors')}`));
66
+ ui.hint(` ℹ️ ${getT('restore.steps.database.normalErrors')}`);
66
67
 
67
68
  } catch (error) {
68
69
  // Erros esperados conforme documentação oficial Supabase
@@ -73,7 +74,7 @@ module.exports = async ({ backupFilePath, targetDatabaseUrl }) => {
73
74
  error.stdout?.includes('already exists')) {
74
75
  console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.database.expectedErrors')}`));
75
76
  console.log(chalk.green(` ✅ ${getT('restore.steps.database.success')}`));
76
- console.log(chalk.gray(` ℹ️ ${getT('restore.steps.database.errorsIgnored')}`));
77
+ ui.hint(` ℹ️ ${getT('restore.steps.database.errorsIgnored')}`);
77
78
  } else {
78
79
  console.error(chalk.red(` ❌ ${getT('restore.steps.database.error', { message: error.message })}`));
79
80
  throw error;
package/src/index.js CHANGED
@@ -9,6 +9,7 @@ const chalk = require('chalk');
9
9
  const path = require('path');
10
10
  const { showBetaBanner } = require('./utils/banner');
11
11
  const { t } = require('./i18n');
12
+ const ui = require('./utils/cliUi');
12
13
 
13
14
  // Exportar comandos
14
15
  const backupCommand = require('./commands/backup');
@@ -139,7 +140,7 @@ function showPrerequisitesStatus() {
139
140
  Object.entries(prerequisites).forEach(([name, info]) => {
140
141
  const icon = info.installed ? '✅' : '❌';
141
142
  const status = info.installed ? chalk.green('Instalado') : chalk.red('Não instalado');
142
- const version = info.version ? chalk.gray(`(${info.version})`) : '';
143
+ const version = info.version ? `(${info.version})` : '';
143
144
 
144
145
  console.log(` ${icon} ${chalk.cyan(name)}: ${status} ${version}`);
145
146
  });
@@ -153,11 +154,11 @@ function showPrerequisitesStatus() {
153
154
  console.log(chalk.yellow.bold('\n💡 Instruções de instalação:'));
154
155
 
155
156
  if (missing.includes('supabase_cli')) {
156
- console.log(chalk.gray(' - Supabase CLI: npm install -g supabase'));
157
+ ui.hint(' - Supabase CLI: npm install -g supabase');
157
158
  }
158
159
 
159
160
  if (missing.includes('pg_dump')) {
160
- console.log(chalk.gray(' - PostgreSQL: https://www.postgresql.org/download/'));
161
+ ui.hint(' - PostgreSQL: https://www.postgresql.org/download/');
161
162
  }
162
163
  }
163
164
  }
@@ -173,29 +174,29 @@ function checkCurrentConfig() {
173
174
 
174
175
  if (config) {
175
176
  console.log(chalk.green('✅ Arquivo de configuração encontrado'));
176
- console.log(chalk.gray(` - Localização: ${path.join(process.cwd(), '.env.local')}`));
177
+ ui.hint(` - Localização: ${path.join(process.cwd(), '.env.local')}`);
177
178
 
178
179
  if (config.supabase?.url) {
179
- console.log(chalk.gray(` - Supabase URL: ${config.supabase.url}`));
180
+ ui.hint(` - Supabase URL: ${config.supabase.url}`);
180
181
  }
181
182
 
182
183
  if (config.supabase?.serviceKey) {
183
- console.log(chalk.gray(' - Service Key: Configurada'));
184
+ ui.hint(' - Service Key: Configurada');
184
185
  }
185
186
 
186
187
  if (config.supabase?.anonKey) {
187
- console.log(chalk.gray(' - Anon Key: Configurada'));
188
+ ui.hint(' - Anon Key: Configurada');
188
189
  }
189
190
  } else {
190
191
  console.log(chalk.yellow('⚠️ Arquivo de configuração não encontrado'));
191
- console.log(chalk.gray(' - Configure o arquivo .env.local na raiz do projeto'));
192
+ ui.hint(' - Configure o arquivo .env.local na raiz do projeto');
192
193
  }
193
194
 
194
195
  if (hasCredentials) {
195
196
  console.log(chalk.green('✅ Credenciais configuradas'));
196
197
  } else {
197
198
  console.log(chalk.yellow('⚠️ Credenciais não configuradas'));
198
- console.log(chalk.gray(' - Configure SUPABASE_URL e SUPABASE_ANON_KEY'));
199
+ ui.hint(' - Configure SUPABASE_URL e SUPABASE_ANON_KEY');
199
200
  }
200
201
  }
201
202
 
@@ -2,15 +2,16 @@ const inquirer = require('inquirer');
2
2
  const chalk = require('chalk');
3
3
  const { confirm } = require('../utils/prompt');
4
4
  const { t } = require('../i18n');
5
+ const ui = require('../utils/cliUi');
5
6
 
6
7
  let _printedSupabasePostgresMajorHint = false;
7
8
 
8
- function printSupabasePostgresMajorHintOnce(getT, chalk) {
9
+ function printSupabasePostgresMajorHintOnce(getT) {
9
10
  if (_printedSupabasePostgresMajorHint) return;
10
11
  _printedSupabasePostgresMajorHint = true;
11
- console.log(chalk.cyan(` ℹ️ ${getT('env.supabasePostgresMajor.hintTitle')}`));
12
- console.log(chalk.white(` ${getT('env.supabasePostgresMajor.hintPath')}`));
13
- console.log(chalk.gray(` ${getT('env.supabasePostgresMajor.hintBody')}`));
12
+ ui.link(` ℹ️ ${getT('env.supabasePostgresMajor.hintTitle')}`);
13
+ ui.info(` ${getT('env.supabasePostgresMajor.hintPath')}`);
14
+ ui.hint(` ${getT('env.supabasePostgresMajor.hintBody')}`);
14
15
  }
15
16
 
16
17
  async function mapEnvVariablesInteractively(env, expectedKeys) {
@@ -123,13 +124,13 @@ async function mapEnvVariablesInteractively(env, expectedKeys) {
123
124
  for (const expected of expectedKeys) {
124
125
  console.log(chalk.blue(`\n🔧 ${getT('env.mapping.title', { variable: expected })}`));
125
126
  if (expected === 'SUPABASE_POSTGRES_MAJOR') {
126
- printSupabasePostgresMajorHintOnce(getT, chalk);
127
+ printSupabasePostgresMajorHintOnce(getT);
127
128
  }
128
129
  if (expected === 'SMOONB_LICENSE_KEY' && instructions.help === '') {
129
- console.log(chalk.gray(` ${getT('env.licenseKey.help')}`));
130
+ ui.hint(` ${getT('env.licenseKey.help')}`);
130
131
  }
131
132
  if (expected === 'SMOONB_TELEMETRY_ENABLED' && instructions.help === '') {
132
- console.log(chalk.gray(` ${getT('env.telemetry.help')}`));
133
+ ui.hint(` ${getT('env.telemetry.help')}`);
133
134
  }
134
135
 
135
136
  let clientKey = undefined;
@@ -179,7 +180,7 @@ async function mapEnvVariablesInteractively(env, expectedKeys) {
179
180
  if (!currentValue) {
180
181
  console.log(chalk.yellow(getT('env.mapping.notFound', { variable: expected })));
181
182
  if (expected === 'SUPABASE_POSTGRES_MAJOR') {
182
- printSupabasePostgresMajorHintOnce(getT, chalk);
183
+ printSupabasePostgresMajorHintOnce(getT);
183
184
  } else if (instructions.help) {
184
185
  // Se o help contém link (https://), mostrar como link
185
186
  if (instructions.help.includes('https://')) {
@@ -0,0 +1,104 @@
1
+ /**
2
+ * UI/console padronizado do smoonb CLI.
3
+ * Toda saída para o usuário usa este módulo para legibilidade (incl. Windows PowerShell).
4
+ * Regras: sem chalk.dim/chalk.gray/chalk.blackBright em blocos longos; cores só para rótulos/ênfase.
5
+ * Saída principal em stdout.
6
+ */
7
+ const chalk = require('chalk');
8
+
9
+ const stdout = process.stdout;
10
+
11
+ function writeOut(msg) {
12
+ stdout.write(msg + (msg.endsWith('\n') ? '' : '\n'));
13
+ }
14
+
15
+ /**
16
+ * Título / cabeçalho (cyan, boa leitura).
17
+ */
18
+ function title(text) {
19
+ writeOut(chalk.cyan(text));
20
+ }
21
+
22
+ /**
23
+ * Texto informativo (branco/default, nunca dim/gray).
24
+ */
25
+ function info(text) {
26
+ writeOut(chalk.white(text));
27
+ }
28
+
29
+ /**
30
+ * Aviso (amarelo).
31
+ */
32
+ function warn(text) {
33
+ writeOut(chalk.yellow(text));
34
+ }
35
+
36
+ /**
37
+ * Erro (vermelho). Opcionalmente enviar só a primeira linha para stderr (resto stdout).
38
+ */
39
+ function error(text) {
40
+ writeOut(chalk.red(text));
41
+ }
42
+
43
+ /**
44
+ * Passo / label de etapa (azul).
45
+ */
46
+ function step(text) {
47
+ writeOut(chalk.blue(text));
48
+ }
49
+
50
+ /**
51
+ * Sucesso (verde).
52
+ */
53
+ function success(text) {
54
+ writeOut(chalk.green(text));
55
+ }
56
+
57
+ /**
58
+ * Link ou destaque secundário (cyan, não apagado).
59
+ */
60
+ function link(text) {
61
+ writeOut(chalk.cyan(text));
62
+ }
63
+
64
+ /**
65
+ * Bloco de texto longo (ex.: bundle de diagnóstico) — SEM cor para copiar/colar e legibilidade.
66
+ */
67
+ function block(text) {
68
+ if (Array.isArray(text)) {
69
+ text.forEach((line) => writeOut(line));
70
+ } else {
71
+ writeOut(String(text));
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Múltiplas linhas em cor padrão (branco), sem dim/gray.
77
+ */
78
+ function multiline(text) {
79
+ writeOut(chalk.white(String(text)));
80
+ }
81
+
82
+ /**
83
+ * Sugestão / hint curto — branco para manter legível (não gray).
84
+ */
85
+ function hint(text) {
86
+ writeOut(chalk.white(text));
87
+ }
88
+
89
+ const ui = {
90
+ title,
91
+ info,
92
+ warn,
93
+ error,
94
+ step,
95
+ success,
96
+ link,
97
+ block,
98
+ multiline,
99
+ hint,
100
+ // Para código que precisa do chalk com restrições (ex.: formatação inline)
101
+ chalk
102
+ };
103
+
104
+ module.exports = ui;