@arela/uploader 1.0.24 → 1.1.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.
Files changed (80) hide show
  1. package/docs/AUTO_PROCESSING_PIPELINE.md +258 -0
  2. package/docs/COMPLETE_USAGE_GUIDE.md +1363 -0
  3. package/docs/DATABASESERVICE_IMPROVEMENTS.md +546 -0
  4. package/docs/PASO_2_TEST_RESULTS.md +298 -0
  5. package/docs/PASO_3_PLAN.md +385 -0
  6. package/docs/PHASE_1_FILE_DETECTION.md +366 -0
  7. package/docs/PHASE_2_API_INTEGRATION.md +426 -0
  8. package/docs/PHASE_3_DATABASE_MANAGEMENT.md +480 -0
  9. package/docs/PHASE_4_FILE_OPERATIONS.md +448 -0
  10. package/docs/PHASE_5_WATCH_MODE.md +450 -0
  11. package/docs/PHASE_6_SIGNAL_HANDLING.md +472 -0
  12. package/docs/PHASE_7_ADVANCED_FEATURES.md +560 -0
  13. package/docs/PLAN_WATCH_FEATURE.md +417 -0
  14. package/docs/README.md +480 -0
  15. package/docs/SCHEMA_ALIGNMENT_SUMMARY.md +301 -0
  16. package/docs/SMARTWATCH_DATABASE_REFACTORING.md +181 -0
  17. package/docs/SMART_WATCH_DATABASE_CHANGES.md +502 -0
  18. package/docs/TESTING_WATCH_MODE.md +212 -0
  19. package/docs/WATCHER_API_IMPLEMENTATION.md +520 -0
  20. package/docs/WATCHER_API_INTEGRATION.md +562 -0
  21. package/docs/WATCHER_SETUP_GUIDE.md +614 -0
  22. package/docs/WATCH_ARCHITECTURE.md +395 -0
  23. package/docs/WATCH_AUTO_PIPELINE.md +334 -0
  24. package/docs/WATCH_CONFIGURATION.md +267 -0
  25. package/docs/WATCH_USAGE_GUIDE.md +567 -0
  26. package/docs/commands.md +14 -0
  27. package/package.json +1 -1
  28. package/src/commands/IdentifyCommand.js +11 -0
  29. package/src/config/config.js +2 -2
  30. package/src/file-detection.js +42 -1
  31. package/src/scoring/scoring-engine.js +40 -7
  32. package/src/services/LoggingService.js +5 -3
  33. package/.vscode/settings.json +0 -1
  34. package/coverage/IdentifyCommand.js.html +0 -1462
  35. package/coverage/PropagateCommand.js.html +0 -1507
  36. package/coverage/PushCommand.js.html +0 -1504
  37. package/coverage/ScanCommand.js.html +0 -1654
  38. package/coverage/UploadCommand.js.html +0 -1846
  39. package/coverage/WatchCommand.js.html +0 -4111
  40. package/coverage/base.css +0 -224
  41. package/coverage/block-navigation.js +0 -87
  42. package/coverage/favicon.png +0 -0
  43. package/coverage/index.html +0 -191
  44. package/coverage/lcov-report/IdentifyCommand.js.html +0 -1462
  45. package/coverage/lcov-report/PropagateCommand.js.html +0 -1507
  46. package/coverage/lcov-report/PushCommand.js.html +0 -1504
  47. package/coverage/lcov-report/ScanCommand.js.html +0 -1654
  48. package/coverage/lcov-report/UploadCommand.js.html +0 -1846
  49. package/coverage/lcov-report/WatchCommand.js.html +0 -4111
  50. package/coverage/lcov-report/base.css +0 -224
  51. package/coverage/lcov-report/block-navigation.js +0 -87
  52. package/coverage/lcov-report/favicon.png +0 -0
  53. package/coverage/lcov-report/index.html +0 -191
  54. package/coverage/lcov-report/prettify.css +0 -1
  55. package/coverage/lcov-report/prettify.js +0 -2
  56. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  57. package/coverage/lcov-report/sorter.js +0 -210
  58. package/coverage/lcov.info +0 -1937
  59. package/coverage/prettify.css +0 -1
  60. package/coverage/prettify.js +0 -2
  61. package/coverage/sort-arrow-sprite.png +0 -0
  62. package/coverage/sorter.js +0 -210
  63. package/docs/API_ENDPOINTS_FOR_DETECTION.md +0 -647
  64. package/docs/API_RETRY_MECHANISM.md +0 -338
  65. package/docs/ARELA_IDENTIFY_IMPLEMENTATION.md +0 -489
  66. package/docs/ARELA_IDENTIFY_QUICKREF.md +0 -186
  67. package/docs/ARELA_PROPAGATE_IMPLEMENTATION.md +0 -581
  68. package/docs/ARELA_PROPAGATE_QUICKREF.md +0 -272
  69. package/docs/ARELA_PUSH_IMPLEMENTATION.md +0 -577
  70. package/docs/ARELA_PUSH_QUICKREF.md +0 -322
  71. package/docs/ARELA_SCAN_IMPLEMENTATION.md +0 -373
  72. package/docs/ARELA_SCAN_QUICKREF.md +0 -139
  73. package/docs/CROSS_PLATFORM_PATH_HANDLING.md +0 -597
  74. package/docs/DETECTION_ATTEMPT_TRACKING.md +0 -414
  75. package/docs/MIGRATION_UPLOADER_TO_FILE_STATS.md +0 -1020
  76. package/docs/MULTI_LEVEL_DIRECTORY_SCANNING.md +0 -494
  77. package/docs/QUICK_REFERENCE_API_DETECTION.md +0 -264
  78. package/docs/REFACTORING_SUMMARY_DETECT_PEDIMENTOS.md +0 -200
  79. package/docs/STATS_COMMAND_SEQUENCE_DIAGRAM.md +0 -287
  80. package/docs/STATS_COMMAND_SIMPLE.md +0 -93
@@ -0,0 +1,520 @@
1
+ # Archivo de Ejemplo: watcher.routes.js para arela-api
2
+
3
+ **Ubicación:** `arela-api/src/routes/watcher.routes.js`
4
+
5
+ Este archivo contiene la implementación completa de los endpoints REST para exposer el WatchService.
6
+
7
+ ```javascript
8
+ import express from 'express';
9
+ import logger from '../services/LoggingService.js';
10
+
11
+ const router = express.Router();
12
+
13
+ /**
14
+ * Inicializar rutas del watcher
15
+ * Requiere que watchService esté disponible en el contexto de la app
16
+ * @param {Object} watchService - Instancia de WatchService desde arela-uploader
17
+ */
18
+ export function initWatcherRoutes(app, watchService) {
19
+ /**
20
+ * GET /api/watcher/status
21
+ * Obtener estado actual del watcher
22
+ *
23
+ * @returns {Object} Estado del watcher incluyendo estadísticas
24
+ */
25
+ router.get('/status', (req, res) => {
26
+ try {
27
+ const stats = watchService.getStats();
28
+
29
+ res.json({
30
+ isRunning: stats.isRunning,
31
+ autoProcessingEnabled: stats.autoProcessingEnabled,
32
+ watchedDirectories: stats.watchedDirectories,
33
+ activeWatchers: stats.activeWatchers,
34
+ allWatchersReady: watchService.allWatchersReady,
35
+ lastUpdated: new Date().toISOString(),
36
+ stats: {
37
+ filesAdded: stats.filesAdded,
38
+ filesModified: stats.filesModified,
39
+ filesRemoved: stats.filesRemoved,
40
+ uploadsTriggered: stats.uploadsTriggered,
41
+ pipelinesTriggered: stats.pipelinesTriggered,
42
+ errorsEncountered: stats.errorsEncountered,
43
+ },
44
+ });
45
+ } catch (error) {
46
+ logger.error(`Error getting watcher status: ${error.message}`);
47
+ res.status(500).json({
48
+ error: 'Failed to get watcher status',
49
+ details: error.message,
50
+ });
51
+ }
52
+ });
53
+
54
+ /**
55
+ * GET /api/watcher/queue-stats
56
+ * Obtener estadísticas de la cola de procesamiento
57
+ *
58
+ * @returns {Object} Estadísticas de la cola y progreso
59
+ */
60
+ router.get('/queue-stats', async (req, res) => {
61
+ try {
62
+ const queueStats = await watchService.getQueueStats();
63
+
64
+ if (!queueStats) {
65
+ return res.status(500).json({
66
+ error: 'Could not fetch queue stats',
67
+ });
68
+ }
69
+
70
+ res.json({
71
+ queue: queueStats.queue,
72
+ progress: queueStats.progress,
73
+ timestamp: queueStats.timestamp,
74
+ metadata: {
75
+ totalCapacity: queueStats.queue.total,
76
+ utilization:
77
+ ((queueStats.queue.total - queueStats.queue.pending) /
78
+ queueStats.queue.total) *
79
+ 100,
80
+ },
81
+ });
82
+ } catch (error) {
83
+ logger.error(`Error getting queue stats: ${error.message}`);
84
+ res.status(500).json({
85
+ error: 'Failed to get queue stats',
86
+ details: error.message,
87
+ });
88
+ }
89
+ });
90
+
91
+ /**
92
+ * GET /api/watcher/directories
93
+ * Obtener lista de directorios monitoreados
94
+ *
95
+ * @returns {Object} Lista de directorios con detalles
96
+ */
97
+ router.get('/directories', (req, res) => {
98
+ try {
99
+ const directories = Array.from(watchService.watchedDirs).map((dir) => {
100
+ const config = watchService.directoryConfigs.get(dir);
101
+ const isReady = watchService.watcherReady.get(dir) === true;
102
+
103
+ return {
104
+ path: dir,
105
+ isReady,
106
+ folderStructure: config?.folderStructure || 'default',
107
+ config: {
108
+ ignored: config?.ignored || 'hidden files, node_modules, .git',
109
+ awaitWriteFinish: config?.awaitWriteFinish || {
110
+ stabilityThreshold: 300,
111
+ pollInterval: 100,
112
+ },
113
+ },
114
+ };
115
+ });
116
+
117
+ res.json({
118
+ directories,
119
+ totalDirectories: directories.length,
120
+ readyCount: directories.filter((d) => d.isReady).length,
121
+ timestamp: new Date().toISOString(),
122
+ });
123
+ } catch (error) {
124
+ logger.error(`Error getting watched directories: ${error.message}`);
125
+ res.status(500).json({
126
+ error: 'Failed to get watched directories',
127
+ details: error.message,
128
+ });
129
+ }
130
+ });
131
+
132
+ /**
133
+ * POST /api/watcher/start
134
+ * Iniciar el watcher con directorios específicos
135
+ *
136
+ * @param {string[]} directories - Array de rutas a monitorear
137
+ * @param {boolean} autoProcessing - Habilitar procesamiento automático
138
+ * @param {string} folderStructure - Estructura de carpetas (arela, custom, etc)
139
+ * @returns {Object} Estado después de iniciar
140
+ */
141
+ router.post('/start', async (req, res) => {
142
+ try {
143
+ const {
144
+ directories,
145
+ autoProcessing = true,
146
+ folderStructure = 'arela',
147
+ } = req.body;
148
+
149
+ // Validación
150
+ if (!directories || !Array.isArray(directories) || directories.length === 0) {
151
+ return res.status(400).json({
152
+ error: 'Invalid request',
153
+ details: 'directories must be a non-empty array of strings',
154
+ });
155
+ }
156
+
157
+ // Validar que sean rutas válidas
158
+ const invalidPaths = directories.filter(
159
+ (dir) => typeof dir !== 'string' || dir.trim().length === 0
160
+ );
161
+
162
+ if (invalidPaths.length > 0) {
163
+ return res.status(400).json({
164
+ error: 'Invalid paths provided',
165
+ details: `${invalidPaths.length} invalid path(s)`,
166
+ });
167
+ }
168
+
169
+ logger.info(`[API] Starting watcher for ${directories.length} directories`);
170
+
171
+ // Agregar watchers para cada directorio
172
+ for (const dir of directories) {
173
+ await watchService.addWatcher(
174
+ dir,
175
+ {},
176
+ { folderStructure: folderStructure || 'arela' }
177
+ );
178
+ }
179
+
180
+ // Habilitar procesamiento automático si se solicita
181
+ if (autoProcessing) {
182
+ watchService.enableAutoProcessing({ batchSize: 10 });
183
+ }
184
+
185
+ // Iniciar watchers
186
+ await watchService.start();
187
+
188
+ // Esperar a que todos los watchers estén listos
189
+ await watchService.waitForWatchersReady();
190
+
191
+ res.json({
192
+ success: true,
193
+ message: 'Watcher started successfully',
194
+ stats: watchService.getStats(),
195
+ directoryCount: directories.length,
196
+ timestamp: new Date().toISOString(),
197
+ });
198
+ } catch (error) {
199
+ logger.error(`Error starting watcher: ${error.message}`);
200
+ res.status(500).json({
201
+ error: 'Failed to start watcher',
202
+ details: error.message,
203
+ });
204
+ }
205
+ });
206
+
207
+ /**
208
+ * POST /api/watcher/stop
209
+ * Detener el watcher
210
+ *
211
+ * @returns {Object} Estadísticas finales
212
+ */
213
+ router.post('/stop', async (req, res) => {
214
+ try {
215
+ logger.info('[API] Stopping watcher');
216
+
217
+ const finalStats = watchService.getStats();
218
+ await watchService.stop('API request');
219
+
220
+ res.json({
221
+ success: true,
222
+ message: 'Watcher stopped successfully',
223
+ finalStats,
224
+ timestamp: new Date().toISOString(),
225
+ });
226
+ } catch (error) {
227
+ logger.error(`Error stopping watcher: ${error.message}`);
228
+ res.status(500).json({
229
+ error: 'Failed to stop watcher',
230
+ details: error.message,
231
+ });
232
+ }
233
+ });
234
+
235
+ /**
236
+ * POST /api/watcher/enable-auto-processing
237
+ * Habilitar procesamiento automático
238
+ *
239
+ * @param {number} batchSize - Tamaño de lote para procesamiento
240
+ * @returns {Object} Estado actualizado
241
+ */
242
+ router.post('/enable-auto-processing', (req, res) => {
243
+ try {
244
+ const { batchSize = 10 } = req.body;
245
+
246
+ if (typeof batchSize !== 'number' || batchSize < 1) {
247
+ return res.status(400).json({
248
+ error: 'Invalid batchSize',
249
+ details: 'batchSize must be a positive number',
250
+ });
251
+ }
252
+
253
+ logger.info(`[API] Enabling auto-processing with batchSize=${batchSize}`);
254
+
255
+ watchService.enableAutoProcessing({ batchSize });
256
+
257
+ res.json({
258
+ success: true,
259
+ message: 'Auto-processing enabled',
260
+ autoProcessingEnabled: watchService.isAutoProcessingEnabled(),
261
+ batchSize,
262
+ timestamp: new Date().toISOString(),
263
+ });
264
+ } catch (error) {
265
+ logger.error(`Error enabling auto-processing: ${error.message}`);
266
+ res.status(500).json({
267
+ error: 'Failed to enable auto-processing',
268
+ details: error.message,
269
+ });
270
+ }
271
+ });
272
+
273
+ /**
274
+ * POST /api/watcher/disable-auto-processing
275
+ * Deshabilitar procesamiento automático
276
+ *
277
+ * @returns {Object} Estado actualizado
278
+ */
279
+ router.post('/disable-auto-processing', (req, res) => {
280
+ try {
281
+ logger.info('[API] Disabling auto-processing');
282
+
283
+ watchService.disableAutoProcessing();
284
+
285
+ res.json({
286
+ success: true,
287
+ message: 'Auto-processing disabled',
288
+ autoProcessingEnabled: watchService.isAutoProcessingEnabled(),
289
+ timestamp: new Date().toISOString(),
290
+ });
291
+ } catch (error) {
292
+ logger.error(`Error disabling auto-processing: ${error.message}`);
293
+ res.status(500).json({
294
+ error: 'Failed to disable auto-processing',
295
+ details: error.message,
296
+ });
297
+ }
298
+ });
299
+
300
+ /**
301
+ * GET /api/watcher/pipeline-status
302
+ * Obtener estado de pipelines en ejecución y recientes
303
+ *
304
+ * @returns {Object} Estado de pipelines y recientes
305
+ */
306
+ router.get('/pipeline-status', (req, res) => {
307
+ try {
308
+ const stats = watchService.getStats();
309
+
310
+ res.json({
311
+ isProcessing: stats.pipelinesTriggered > 0,
312
+ activePipelines: 0, // Valor placeholder - actualizar si se mantiene historial
313
+ recentPipelines: {
314
+ total: stats.pipelinesTriggered,
315
+ successful: stats.pipelinesTriggered - stats.errorsEncountered,
316
+ failed: stats.errorsEncountered,
317
+ },
318
+ errorStats: {
319
+ totalErrors: stats.errorsEncountered,
320
+ lastError: null, // Obtener de errorTracker si es necesario
321
+ },
322
+ timestamp: new Date().toISOString(),
323
+ });
324
+ } catch (error) {
325
+ logger.error(`Error getting pipeline status: ${error.message}`);
326
+ res.status(500).json({
327
+ error: 'Failed to get pipeline status',
328
+ details: error.message,
329
+ });
330
+ }
331
+ });
332
+
333
+ /**
334
+ * GET /api/watcher/health
335
+ * Health check del watcher
336
+ *
337
+ * @returns {Object} Estado de salud
338
+ */
339
+ router.get('/health', (req, res) => {
340
+ try {
341
+ const stats = watchService.getStats();
342
+
343
+ const health = {
344
+ status: stats.isRunning ? 'healthy' : 'stopped',
345
+ isRunning: stats.isRunning,
346
+ activeWatchers: stats.activeWatchers,
347
+ errors: stats.errorsEncountered,
348
+ uptime: stats.isRunning ? 'tracking' : 'not running',
349
+ timestamp: new Date().toISOString(),
350
+ };
351
+
352
+ const statusCode = stats.isRunning && stats.errorsEncountered === 0 ? 200 : 503;
353
+ res.status(statusCode).json(health);
354
+ } catch (error) {
355
+ res.status(500).json({
356
+ status: 'error',
357
+ error: error.message,
358
+ });
359
+ }
360
+ });
361
+
362
+ // Montar el router en la app
363
+ app.use('/api/watcher', router);
364
+
365
+ logger.info('✅ Watcher API routes initialized');
366
+ }
367
+
368
+ export default router;
369
+ ```
370
+
371
+ ---
372
+
373
+ ## 📋 Integración en app.js
374
+
375
+ ```javascript
376
+ // app.js (o main.js, index.js)
377
+
378
+ import express from 'express';
379
+ import { initWatcherRoutes } from './routes/watcher.routes.js';
380
+ import watchService from '@arela/uploader/services'; // O importar directamente
381
+
382
+ const app = express();
383
+
384
+ // Middleware
385
+ app.use(express.json());
386
+ app.use(express.urlencoded({ extended: true }));
387
+
388
+ // ... otras rutas ...
389
+
390
+ // Inicializar rutas del watcher
391
+ initWatcherRoutes(app, watchService);
392
+
393
+ // Error handling
394
+ app.use((err, req, res, next) => {
395
+ console.error(err);
396
+ res.status(500).json({
397
+ error: 'Internal server error',
398
+ message: err.message,
399
+ });
400
+ });
401
+
402
+ // Iniciar servidor
403
+ const PORT = process.env.PORT || 3000;
404
+ app.listen(PORT, () => {
405
+ console.log(`🚀 Server running on port ${PORT}`);
406
+ });
407
+
408
+ export default app;
409
+ ```
410
+
411
+ ---
412
+
413
+ ## 🧪 Ejemplo de uso desde el frontend
414
+
415
+ ```javascript
416
+ // hooks/useWatcherMonitor.js
417
+ import { useState, useEffect, useCallback } from 'react';
418
+
419
+ export const useWatcherMonitor = (pollInterval = 5000) => {
420
+ const [status, setStatus] = useState(null);
421
+ const [queue, setQueue] = useState(null);
422
+ const [directories, setDirectories] = useState([]);
423
+ const [loading, setLoading] = useState(false);
424
+ const [error, setError] = useState(null);
425
+
426
+ // Refrescar datos
427
+ const refresh = useCallback(async () => {
428
+ try {
429
+ setLoading(true);
430
+ setError(null);
431
+
432
+ const [statusRes, queueRes, dirRes] = await Promise.all([
433
+ fetch('/api/watcher/status'),
434
+ fetch('/api/watcher/queue-stats'),
435
+ fetch('/api/watcher/directories'),
436
+ ]);
437
+
438
+ if (!statusRes.ok || !queueRes.ok || !dirRes.ok) {
439
+ throw new Error('Failed to fetch watcher data');
440
+ }
441
+
442
+ setStatus(await statusRes.json());
443
+ setQueue(await queueRes.json());
444
+ setDirectories(await dirRes.json());
445
+ } catch (err) {
446
+ setError(err.message);
447
+ console.error('Error fetching watcher data:', err);
448
+ } finally {
449
+ setLoading(false);
450
+ }
451
+ }, []);
452
+
453
+ // Poll automático
454
+ useEffect(() => {
455
+ refresh(); // Fetch inicial
456
+
457
+ const interval = setInterval(refresh, pollInterval);
458
+ return () => clearInterval(interval);
459
+ }, [pollInterval, refresh]);
460
+
461
+ // Acciones
462
+ const startWatcher = useCallback(
463
+ async (dirs) => {
464
+ const res = await fetch('/api/watcher/start', {
465
+ method: 'POST',
466
+ headers: { 'Content-Type': 'application/json' },
467
+ body: JSON.stringify({
468
+ directories: dirs,
469
+ autoProcessing: true,
470
+ }),
471
+ });
472
+ if (!res.ok) throw new Error('Failed to start watcher');
473
+ await refresh();
474
+ return res.json();
475
+ },
476
+ [refresh]
477
+ );
478
+
479
+ const stopWatcher = useCallback(async () => {
480
+ const res = await fetch('/api/watcher/stop', { method: 'POST' });
481
+ if (!res.ok) throw new Error('Failed to stop watcher');
482
+ await refresh();
483
+ return res.json();
484
+ }, [refresh]);
485
+
486
+ const enableAutoProcessing = useCallback(async () => {
487
+ const res = await fetch('/api/watcher/enable-auto-processing', {
488
+ method: 'POST',
489
+ headers: { 'Content-Type': 'application/json' },
490
+ body: JSON.stringify({ batchSize: 10 }),
491
+ });
492
+ if (!res.ok) throw new Error('Failed to enable auto-processing');
493
+ await refresh();
494
+ return res.json();
495
+ }, [refresh]);
496
+
497
+ return {
498
+ status,
499
+ queue,
500
+ directories,
501
+ loading,
502
+ error,
503
+ refresh,
504
+ startWatcher,
505
+ stopWatcher,
506
+ enableAutoProcessing,
507
+ };
508
+ };
509
+ ```
510
+
511
+ ---
512
+
513
+ ## 📌 Resumen de cambios necesarios en arela-api
514
+
515
+ 1. ✅ Crear `routes/watcher.routes.js` (archivo completo arriba)
516
+ 2. ✅ Importar en `app.js` o `index.js`
517
+ 3. ✅ Asegurar que WatchService de arela-uploader esté disponible
518
+ 4. ✅ Agregar autenticación si es necesario
519
+ 5. ✅ Documentar endpoints en Swagger/OpenAPI
520
+