smoonb 1.0.11 → 1.0.13

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.
@@ -307,10 +307,8 @@ async function askComponentsFlags() {
307
307
  const includeFunctions = await confirm(getT('backup.components.edgeFunctions.include'), true);
308
308
 
309
309
  // Pergunta de limpeza de functions imediatamente após
310
- let cleanFunctions = false;
311
- if (includeFunctions) {
312
- cleanFunctions = await confirm(getT('backup.components.edgeFunctions.cleanup'), false);
313
- }
310
+ const cleanFunctions = false;
311
+
314
312
 
315
313
  // Explicação sobre .temp
316
314
  console.log(chalk.cyan(`\n📁 ${getT('backup.components.temp.title')}`));
@@ -320,10 +318,8 @@ async function askComponentsFlags() {
320
318
  const includeTemp = await confirm(getT('backup.components.temp.include'), true);
321
319
 
322
320
  // Pergunta de limpeza de .temp imediatamente após
323
- let cleanTemp = false;
324
- if (includeTemp) {
325
- cleanTemp = await confirm(getT('backup.components.temp.cleanup'), false);
326
- }
321
+ const cleanTemp = false;
322
+
327
323
 
328
324
  // Explicação sobre Migrations
329
325
  console.log(chalk.cyan(`\n📋 ${getT('backup.components.migrations.title')}`));
@@ -334,10 +330,8 @@ async function askComponentsFlags() {
334
330
  const includeMigrations = await confirm(getT('backup.components.migrations.include'), true);
335
331
 
336
332
  // Pergunta de limpeza de migrations imediatamente após
337
- let cleanMigrations = false;
338
- if (includeMigrations) {
339
- cleanMigrations = await confirm(getT('backup.components.migrations.cleanup'), false);
340
- }
333
+ const cleanMigrations = false;
334
+
341
335
 
342
336
  // Continuar com outras perguntas
343
337
  const includeStorage = await confirm(getT('backup.components.storage.include'), true);
@@ -2,6 +2,7 @@ const { exec } = require('child_process');
2
2
  const { promisify } = require('util');
3
3
 
4
4
  const execAsync = promisify(exec);
5
+ const { runSupabase } = require('./supabaseCli');
5
6
 
6
7
  /**
7
8
  * Detecta se Docker Desktop está instalado no sistema
@@ -37,7 +38,7 @@ async function detectDockerRunning() {
37
38
  */
38
39
  async function detectSupabaseCLI() {
39
40
  try {
40
- await execAsync('supabase --version');
41
+ runSupabase('supabase', ['--version'], { encoding: 'utf8', stdio: 'pipe' });
41
42
  return true;
42
43
  } catch {
43
44
  return false;
@@ -50,7 +51,7 @@ async function detectSupabaseCLI() {
50
51
  */
51
52
  async function getSupabaseCLIVersion() {
52
53
  try {
53
- const { stdout } = await execAsync('supabase --version');
54
+ const { stdout } = runSupabase('supabase', ['--version'], { encoding: 'utf8', stdio: 'pipe' });
54
55
  const match = stdout.match(/(\d+)\.(\d+)\.(\d+)/);
55
56
  return match ? `${match[1]}.${match[2]}.${match[3]}` : null;
56
57
  } catch {
@@ -178,7 +179,7 @@ async function detectDockerDependencies() {
178
179
  * @param {boolean} [opts.skipSupabaseVersionCheck=false] - Pular checagem de versão do Supabase CLI
179
180
  * @returns {Promise<{canBackupComplete: boolean, reason?: string, dockerStatus: any}>}
180
181
  */
181
- async function canPerformCompleteBackup({ skipSupabaseVersionCheck = false } = {}) {
182
+ async function canPerformCompleteBackup() {
182
183
  const dockerStatus = await detectDockerDesktop();
183
184
 
184
185
  if (!dockerStatus.installed) {
@@ -206,54 +207,6 @@ async function canPerformCompleteBackup({ skipSupabaseVersionCheck = false } = {
206
207
  };
207
208
  }
208
209
 
209
- if (skipSupabaseVersionCheck) {
210
- return {
211
- canBackupComplete: true,
212
- supabaseVersionCheckSkipped: true,
213
- dockerStatus
214
- };
215
- }
216
-
217
- const supabaseCliVersion = await getSupabaseCLIVersion();
218
- const latestResult = await getSupabaseCLILatestVersion();
219
- if (latestResult.error) {
220
- return {
221
- canBackupComplete: false,
222
- reason: 'supabase_cli_latest_unknown',
223
- latestError: latestResult.error,
224
- supabaseCliVersion: supabaseCliVersion || null,
225
- dockerStatus
226
- };
227
- }
228
-
229
- const minVersion = getMinAcceptableVersion(latestResult.version);
230
- if (supabaseCliVersion && minVersion && compareSemver(supabaseCliVersion, minVersion) < 0) {
231
- return {
232
- canBackupComplete: false,
233
- reason: 'supabase_cli_outdated',
234
- supabaseCliVersion,
235
- supabaseCliLatest: latestResult.version,
236
- supabaseCliMinVersion: minVersion,
237
- dockerStatus
238
- };
239
- }
240
-
241
- return {
242
- canBackupComplete: true,
243
- dockerStatus
244
- };
210
+ return { canBackupComplete: true, dockerStatus };
245
211
  }
246
-
247
- module.exports = {
248
- detectDockerInstallation,
249
- detectDockerRunning,
250
- detectSupabaseCLI,
251
- detectDockerDependencies,
252
- detectDockerDesktop,
253
- getDockerVersion,
254
- getSupabaseCLIVersion,
255
- getSupabaseCLILatestVersion,
256
- compareSemver,
257
- getMinAcceptableVersion,
258
- canPerformCompleteBackup
259
- };
212
+ module.exports = { detectDockerInstallation, detectDockerRunning, detectSupabaseCLI, detectDockerDependencies, detectDockerDesktop, getDockerVersion, getSupabaseCLIVersion, compareSemver, getMinAcceptableVersion, getSupabaseCLILatestVersion, canPerformCompleteBackup };
@@ -1,64 +1,68 @@
1
- const { StringDecoder } = require('string_decoder');
2
- const fs = require('fs').promises;
3
-
4
- // Only the exact, known pg_dump circular-FK block is consolidated. All other
5
- // stderr (including errors and unfamiliar/localized warnings) remains visible.
6
- function createPgDumpDiagnostics(writeStderr) {
7
- const decoder = new StringDecoder('utf8');
8
- let pending = '';
9
- let inCircularWarning = false;
10
- const warnings = [];
11
- const tables = new Set();
12
- const hints = new Set([
13
- 'pg_dump: hint: You might not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints.',
14
- 'pg_dump: hint: Consider using a full dump instead of a --data-only dump to avoid this problem.'
15
- ]);
16
- function line(raw) {
17
- const text = raw.replace(/\r?\n$/, '');
18
- if (/^pg_dump: warning: there are circular foreign-key constraints (?:among these tables|on this table):$/.test(text)) {
19
- inCircularWarning = true;
20
- warnings.push(text);
21
- } else if (inCircularWarning && text.startsWith('pg_dump: detail: ')) {
22
- tables.add(text.slice('pg_dump: detail: '.length));
23
- warnings.push(text);
24
- } else if (inCircularWarning && hints.has(text)) {
25
- warnings.push(text);
26
- } else {
27
- inCircularWarning = false;
28
- writeStderr(raw);
29
- }
30
- }
31
- function consume(text) {
32
- pending += text;
33
- let end;
34
- while ((end = pending.indexOf('\n')) !== -1) {
35
- line(pending.slice(0, end + 1));
36
- pending = pending.slice(end + 1);
37
- }
38
- }
39
- return {
40
- write(chunk) { consume(decoder.write(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))); },
41
- end() {
42
- consume(decoder.end());
43
- if (pending) line(pending);
44
- pending = '';
45
- return { circularForeignKeys: warnings.length > 0, tables: [...tables], messages: warnings };
46
- }
47
- };
48
- }
49
-
50
- // Supabase prepends this command before pg_dump output. Check the first line,
51
- // not arbitrary table contents: a data row containing SQL is not protection.
52
- async function hasReplicaRolePrologue(file) {
53
- const handle = await fs.open(file, 'r');
54
- try {
55
- const buffer = Buffer.alloc(256);
56
- const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
57
- const firstLine = buffer.toString('utf8', 0, bytesRead).split('\n')[0].replace(/^\uFEFF/, '').trim();
58
- return /^SET\s+session_replication_role\s*=\s*(?:replica|'replica');$/i.test(firstLine);
59
- } finally {
60
- await handle.close();
61
- }
62
- }
63
-
64
- module.exports = { createPgDumpDiagnostics, hasReplicaRolePrologue };
1
+ const { StringDecoder } = require('string_decoder');
2
+ const fs = require('fs').promises;
3
+
4
+ // Only the exact, known pg_dump circular-FK block is consolidated. All other
5
+ // stderr (including errors and unfamiliar/localized warnings) remains visible.
6
+ function createPgDumpDiagnostics(writeStderr) {
7
+ const decoder = new StringDecoder('utf8');
8
+ let pending = '';
9
+ let inCircularWarning = false;
10
+ const warnings = [];
11
+ const tables = new Set();
12
+ const hints = new Set([
13
+ 'pg_dump: hint: You might not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints.',
14
+ 'pg_dump: hint: Consider using a full dump instead of a --data-only dump to avoid this problem.'
15
+ ]);
16
+ function line(raw) {
17
+ const text = raw.replace(/\r?\n$/, '');
18
+ if (/^pg_dump: warning: there are circular foreign-key constraints (?:among these tables|on this table):$/.test(text)) {
19
+ inCircularWarning = true;
20
+ warnings.push(text);
21
+ } else if (inCircularWarning && text.startsWith('pg_dump: detail: ')) {
22
+ tables.add(text.slice('pg_dump: detail: '.length));
23
+ warnings.push(text);
24
+ } else if (inCircularWarning && hints.has(text)) {
25
+ warnings.push(text);
26
+ } else {
27
+ inCircularWarning = false;
28
+ writeStderr(raw);
29
+ }
30
+ }
31
+ function consume(text) {
32
+ pending += text;
33
+ let end;
34
+ while ((end = pending.indexOf('\n')) !== -1) {
35
+ line(pending.slice(0, end + 1));
36
+ pending = pending.slice(end + 1);
37
+ }
38
+ }
39
+ return {
40
+ write(chunk) { consume(decoder.write(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))); },
41
+ end() {
42
+ consume(decoder.end());
43
+ if (pending) line(pending);
44
+ pending = '';
45
+ return { circularForeignKeys: warnings.length > 0, tables: [...tables], messages: warnings };
46
+ }
47
+ };
48
+ }
49
+
50
+ // Supabase prepends this command before pg_dump output. Check the first line,
51
+ // not arbitrary table contents: a data row containing SQL is not protection.
52
+ async function hasReplicaRolePrologue(file) {
53
+ const handle = await fs.open(file, 'r');
54
+ try {
55
+ const buffer = Buffer.alloc(256);
56
+ const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
57
+ const firstLine = buffer.toString('utf8', 0, bytesRead).split('\n')[0].replace(/^\uFEFF/, '').trim();
58
+ return /^SET\s+session_replication_role\s*=\s*(?:replica|'replica');$/i.test(firstLine);
59
+ } finally {
60
+ await handle.close();
61
+ }
62
+ }
63
+
64
+ // The bundled CLI is deliberately pinned. Suppress only its generic upgrade advert.
65
+ function withoutCliUpgradeNotice(text) {
66
+ return String(text).split(/(?<=\n)/).filter(line => !/^A new version of Supabase CLI is available: v[0-9.]+ \(currently installed v[0-9.]+\)\r?\n?$/.test(line) && !/^We recommend updating regularly for new features and bug fixes: https:\/\/supabase\.com\/docs\/guides\/cli\/getting-started#updating-the-supabase-cli\r?\n?$/.test(line)).join('');
67
+ }
68
+ module.exports = { createPgDumpDiagnostics, hasReplicaRolePrologue, withoutCliUpgradeNotice };
@@ -1,32 +1,26 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const { execFileSync } = require('child_process');
4
-
5
- // npm installs a .cmd shim on Windows; execFile cannot execute that shim safely.
6
- // Resolve its package entry point instead of interpolating arguments into cmd.exe.
7
- function runSupabase(_command, args, options = {}) {
8
- if (process.platform !== 'win32')
9
- return execFileSync('supabase', args, options);
10
- for (const directory of (process.env.PATH || '').split(path.delimiter)) {
11
- const executable = path.join(directory, 'supabase.exe');
12
- if (fs.existsSync(executable))
13
- return execFileSync(executable, args, options);
14
- for (const root of [
15
- path.join(directory, 'node_modules', 'supabase'),
16
- path.resolve(directory, '..', 'supabase'),
17
- ]) {
18
- const file = path.join(root, 'package.json');
19
- if (!fs.existsSync(file)) continue;
20
- const pkg = JSON.parse(fs.readFileSync(file, 'utf8'));
21
- const bin = typeof pkg.bin === 'string' ? pkg.bin : pkg.bin?.supabase;
22
- if (!bin) continue;
23
- const entry = path.resolve(root, bin);
24
- if (!fs.existsSync(entry)) continue;
25
- if (entry.endsWith('.js'))
26
- return execFileSync(process.execPath, [entry, ...args], options);
27
- return execFileSync(entry, args, options);
28
- }
29
- }
30
- throw new Error('Supabase CLI executable was not found in PATH');
31
- }
32
- module.exports = { runSupabase };
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+ const { spawnSync } = require('child_process');
5
+ function runSupabase(_command, args, options = {}) {
6
+ const root = path.dirname(require.resolve('supabase/package.json'));
7
+ const pkg = require(path.join(root, 'package.json'));
8
+ if (pkg.version !== require('../../package.json').dependencies.supabase) throw new Error('Supabase CLI version mismatch; reinstall smoonb');
9
+ const entry = path.resolve(root, typeof pkg.bin === 'string' ? pkg.bin : pkg.bin.supabase);
10
+ try {
11
+ const result = spawnSync(entry.endsWith('.js') ? process.execPath : entry, entry.endsWith('.js') ? [entry, ...args] : args, { ...options, shell: false, windowsHide: true, timeout: options.timeout || 180000, maxBuffer: 16 * 1024 * 1024 });
12
+ if (result.error || result.status !== 0) throw { status: result.status, code: result.error?.code };
13
+ return options.capture ? result : result.stdout;
14
+ } catch (error) {
15
+ throw new Error(`Supabase CLI operation failed (${error.code === 'ETIMEDOUT' ? 'timeout; check Docker image downloads and connectivity' : 'exit ' + (Number.isInteger(error.status) ? error.status : 'unavailable')}). Check connectivity and credentials.`);
16
+ }
17
+ }
18
+ async function isolatedSupabase(args, env = {}, options = {}) {
19
+ const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'smoonb-cli-'));
20
+ try {
21
+ await fs.promises.mkdir(path.join(root, 'supabase'));
22
+ await fs.promises.writeFile(path.join(root, 'supabase', 'config.toml'), 'project_id = "smoonb-backup"\n');
23
+ return runSupabase('supabase', [...args, '--workdir', root], { ...options, cwd: root, env: { ...process.env, ...env }, encoding: 'utf8', stdio: 'pipe' });
24
+ } finally { await fs.promises.rm(root, { recursive: true, force: true }); }
25
+ }
26
+ module.exports = { runSupabase, isolatedSupabase };