entexto-cli 1.3.0 → 1.4.1

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.
@@ -8,7 +8,7 @@ const ora = require('ora');
8
8
  const FormData = require('form-data');
9
9
  const chokidar = require('chokidar');
10
10
  const { getToken } = require('../utils/config');
11
- const { getProjects, deployFiles, getManifest, pullProject, streamEvents } = require('../utils/api');
11
+ const { getProjects, deployFiles, getManifest, pullProject, streamEvents, deleteFileRemote } = require('../utils/api');
12
12
  const { hashFile, hashContent } = require('../utils/delta');
13
13
 
14
14
  const IGNORE_NAMES = new Set([
@@ -43,6 +43,7 @@ function recolectarArchivos(dir) {
43
43
 
44
44
  // Rastrea cuándo subimos cada archivo (para evitar eco del SSE)
45
45
  const localUploadedAt = new Map();
46
+ const localDeletedAt = new Map();
46
47
 
47
48
  async function subirArchivoSolo(uuid, absDir, filePath) {
48
49
  if (!fs.existsSync(filePath)) return;
@@ -60,6 +61,19 @@ async function subirArchivoSolo(uuid, absDir, filePath) {
60
61
  }
61
62
  }
62
63
 
64
+ async function eliminarArchivoRemoto(uuid, absDir, filePath) {
65
+ const ruta = path.relative(absDir, filePath).replace(/\\/g, '/');
66
+ if (debeIgnorar(path.basename(filePath))) return;
67
+ try {
68
+ await deleteFileRemote(uuid, ruta);
69
+ localDeletedAt.set(ruta, Date.now());
70
+ console.log(' ' + chalk.red('✖') + ' ' + chalk.white(ruta) + chalk.gray(' borrado → remoto'));
71
+ } catch (err) {
72
+ const msg = err.response?.data?.error || err.message;
73
+ console.log(' ' + chalk.red('✖') + ' ' + chalk.white(ruta) + chalk.red(' error borrando: ' + msg));
74
+ }
75
+ }
76
+
63
77
  module.exports = async function sync(options) {
64
78
  if (!getToken()) {
65
79
  console.error(chalk.red('✖ No has iniciado sesión. Ejecuta: entexto login'));
@@ -218,6 +232,36 @@ module.exports = async function sync(options) {
218
232
  let sseReconnectTimer = null;
219
233
 
220
234
  function manejarEventoRemoto(eventType, data) {
235
+ if (eventType === 'file-delete') {
236
+ const { ruta } = data || {};
237
+ if (!ruta) return;
238
+ const destFile = path.join(absDir, ruta);
239
+ try {
240
+ if (fs.existsSync(destFile)) {
241
+ fs.unlinkSync(destFile);
242
+ localDeletedAt.set(ruta, Date.now());
243
+ console.log(' ' + chalk.red('✖') + ' ' + chalk.white(ruta) + chalk.gray(' ← borrado remoto'));
244
+ }
245
+ } catch { console.log(' ' + chalk.red('✖') + ' No se pudo borrar: ' + ruta); }
246
+ return;
247
+ }
248
+ if (eventType === 'file-rename') {
249
+ const { ruta_antigua, ruta_nueva } = data || {};
250
+ if (!ruta_antigua || !ruta_nueva) return;
251
+ const oldFile = path.join(absDir, ruta_antigua);
252
+ const newFile = path.join(absDir, ruta_nueva);
253
+ const newDir = path.dirname(newFile);
254
+ try {
255
+ if (fs.existsSync(oldFile)) {
256
+ if (!fs.existsSync(newDir)) fs.mkdirSync(newDir, { recursive: true });
257
+ fs.renameSync(oldFile, newFile);
258
+ localDeletedAt.set(ruta_antigua, Date.now());
259
+ localUploadedAt.set(ruta_nueva, Date.now());
260
+ console.log(' ' + chalk.yellow('↔') + ' ' + chalk.white(ruta_antigua) + chalk.gray(' → ') + chalk.white(ruta_nueva));
261
+ }
262
+ } catch { console.log(' ' + chalk.red('✖') + ' No se pudo renombrar: ' + ruta_antigua); }
263
+ return;
264
+ }
221
265
  if (eventType !== 'file-update') return;
222
266
  const { ruta, contenido } = data || {};
223
267
  if (!ruta || contenido === undefined) return;
@@ -304,8 +348,26 @@ module.exports = async function sync(options) {
304
348
  watcher.on('change', fp => { changed.add(fp); disparar(); });
305
349
  watcher.on('error', err => console.error(chalk.red(' [watch] Error: ' + err.message)));
306
350
 
351
+ const deletedFiles = new Set();
352
+ let deleteDebounce = null;
353
+
354
+ watcher.on('unlink', fp => {
355
+ const ruta = path.relative(absDir, fp).replace(/\\/g, '/');
356
+ if (debeIgnorar(path.basename(fp))) return;
357
+ const deletedAt = localDeletedAt.get(ruta);
358
+ if (deletedAt && Date.now() - deletedAt < 3000) return;
359
+ deletedFiles.add(fp);
360
+ clearTimeout(deleteDebounce);
361
+ deleteDebounce = setTimeout(async () => {
362
+ const files = [...deletedFiles];
363
+ deletedFiles.clear();
364
+ for (const f of files) await eliminarArchivoRemoto(uuid, absDir, f);
365
+ }, 300);
366
+ });
367
+
307
368
  process.on('SIGINT', () => {
308
369
  clearTimeout(sseReconnectTimer);
370
+ clearTimeout(deleteDebounce);
309
371
  if (sseStream) try { sseStream.destroy(); } catch {}
310
372
  watcher.close();
311
373
  console.log(chalk.gray('\n Sync detenido.\n'));
package/lib/utils/api.js CHANGED
@@ -79,11 +79,11 @@ async function streamEvents(uuid, onEvent) {
79
79
  });
80
80
  const stream = response.data;
81
81
  let buffer = '';
82
+ let eventType = 'message', dataStr = '';
82
83
  stream.on('data', (chunk) => {
83
84
  buffer += chunk.toString();
84
85
  const parts = buffer.split('\n');
85
86
  buffer = parts.pop();
86
- let eventType = 'message', dataStr = '';
87
87
  for (const line of parts) {
88
88
  if (line.startsWith('event: ')) eventType = line.slice(7).trim();
89
89
  else if (line.startsWith('data: ')) dataStr = line.slice(6).trim();
@@ -96,4 +96,9 @@ async function streamEvents(uuid, onEvent) {
96
96
  return stream;
97
97
  }
98
98
 
99
- module.exports = { login, getProjects, deployFiles, deployWithPublish, publishProject, pullProject, getManifest, streamEvents };
99
+ async function deleteFileRemote(uuid, ruta) {
100
+ const res = await client().post(`/api/cli/delete/${uuid}`, { ruta });
101
+ return res.data;
102
+ }
103
+
104
+ module.exports = { login, getProjects, deployFiles, deployWithPublish, publishProject, pullProject, getManifest, streamEvents, deleteFileRemote };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "entexto-cli",
3
- "version": "1.3.0",
3
+ "version": "1.4.1",
4
4
  "description": "CLI oficial de Entexto — Deploy y gestión de proyectos desde tu terminal",
5
5
  "main": "lib/index.js",
6
6
  "bin": {