entexto-cli 1.4.2 → 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,21 +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
- // ⚠️ El guard DEBE ponerse ANTES de unlinkSync porque chokidar
240
- // detecta el 'unlink' síncronamente y dispararía eliminarArchivoRemoto
241
- // antes de que el guard estuviera listo (race condition).
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).
242
267
  localDeletedAt.set(ruta, Date.now());
243
- try {
244
- if (fs.existsSync(destFile)) {
245
- fs.unlinkSync(destFile);
246
- console.log(' ' + chalk.red('✖') + ' ' + chalk.white(ruta) + chalk.gray(' ← borrado remoto'));
247
- }
248
- } catch { console.log(' ' + chalk.red('✖') + ' No se pudo borrar: ' + ruta); }
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);
249
273
  return;
250
274
  }
251
275
  if (eventType === 'file-rename') {
@@ -254,16 +278,15 @@ module.exports = async function sync(options) {
254
278
  const oldFile = path.join(absDir, ruta_antigua);
255
279
  const newFile = path.join(absDir, ruta_nueva);
256
280
  const newDir = path.dirname(newFile);
257
- // ⚠️ Guards ANTES de renameSync por el mismo motivo (race con chokidar).
281
+ // ⚠️ Guards ANTES de la operación FS por el mismo motivo.
258
282
  localDeletedAt.set(ruta_antigua, Date.now());
259
283
  localUploadedAt.set(ruta_nueva, Date.now());
260
- try {
261
- if (fs.existsSync(oldFile)) {
262
- if (!fs.existsSync(newDir)) fs.mkdirSync(newDir, { recursive: true });
263
- fs.renameSync(oldFile, newFile);
264
- console.log(' ' + chalk.yellow('↔') + ' ' + chalk.white(ruta_antigua) + chalk.gray(' → ') + chalk.white(ruta_nueva));
265
- }
266
- } catch { console.log(' ' + chalk.red('✖') + ' No se pudo renombrar: ' + ruta_antigua); }
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);
267
290
  return;
268
291
  }
269
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.2",
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": {