entexto-cli 1.4.1 → 1.4.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.
@@ -231,18 +231,45 @@ module.exports = async function sync(options) {
231
231
  let sseStream = null;
232
232
  let sseReconnectTimer = null;
233
233
 
234
+ // Reintenta una operación FS hasta maxRetries veces con delay ms entre intentos.
235
+ // Necesario en Windows donde los archivos abiertos en VS Code quedan bloqueados (EPERM/EBUSY).
236
+ function retryFsOp(opName, ruta, fn, maxRetries, delay) {
237
+ let attempt = 0;
238
+ function tryOnce() {
239
+ try {
240
+ fn();
241
+ } catch (err) {
242
+ const locked = err.code === 'EBUSY' || err.code === 'EPERM' || err.code === 'EACCES';
243
+ if (locked && attempt < maxRetries) {
244
+ attempt++;
245
+ if (attempt === 1) {
246
+ console.log(' ' + chalk.yellow('⏳') + ' ' + chalk.white(ruta) + chalk.gray(' bloqueado por otro proceso, reintentando...'));
247
+ }
248
+ setTimeout(tryOnce, delay);
249
+ } else if (locked) {
250
+ console.log(' ' + chalk.red('✖') + ' ' + chalk.white(ruta) + chalk.red(' bloqueado — cierra el archivo en VS Code e intenta de nuevo'));
251
+ } else {
252
+ console.log(' ' + chalk.red('✖') + ' ' + opName + ' fallido (' + (err.code || err.message) + '): ' + ruta);
253
+ }
254
+ }
255
+ }
256
+ tryOnce();
257
+ }
258
+
234
259
  function manejarEventoRemoto(eventType, data) {
235
260
  if (eventType === 'file-delete') {
236
261
  const { ruta } = data || {};
237
262
  if (!ruta) return;
238
263
  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); }
264
+ // ⚠️ Guard ANTES de la operación FS: chokidar detecta el 'unlink'
265
+ // síncronamente y dispararía eliminarArchivoRemoto antes de que el
266
+ // guard estuviera listo (race condition).
267
+ localDeletedAt.set(ruta, Date.now());
268
+ if (!fs.existsSync(destFile)) return;
269
+ retryFsOp('borrar', ruta, () => {
270
+ fs.unlinkSync(destFile);
271
+ console.log(' ' + chalk.red('✖') + ' ' + chalk.white(ruta) + chalk.gray(' ← borrado remoto'));
272
+ }, 5, 500);
246
273
  return;
247
274
  }
248
275
  if (eventType === 'file-rename') {
@@ -251,15 +278,15 @@ module.exports = async function sync(options) {
251
278
  const oldFile = path.join(absDir, ruta_antigua);
252
279
  const newFile = path.join(absDir, ruta_nueva);
253
280
  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); }
281
+ // ⚠️ Guards ANTES de la operación FS por el mismo motivo.
282
+ localDeletedAt.set(ruta_antigua, Date.now());
283
+ localUploadedAt.set(ruta_nueva, Date.now());
284
+ if (!fs.existsSync(oldFile)) return;
285
+ retryFsOp('renombrar', ruta_antigua, () => {
286
+ if (!fs.existsSync(newDir)) fs.mkdirSync(newDir, { recursive: true });
287
+ fs.renameSync(oldFile, newFile);
288
+ console.log(' ' + chalk.yellow('↔') + ' ' + chalk.white(ruta_antigua) + chalk.gray(' → ') + chalk.white(ruta_nueva));
289
+ }, 5, 500);
263
290
  return;
264
291
  }
265
292
  if (eventType !== 'file-update') return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "entexto-cli",
3
- "version": "1.4.1",
3
+ "version": "1.4.3",
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": {