@arela/uploader 1.0.24 → 1.1.0

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 (79) 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 +8 -0
  29. package/src/config/config.js +2 -2
  30. package/src/file-detection.js +42 -1
  31. package/src/scoring/scoring-engine.js +35 -7
  32. package/.vscode/settings.json +0 -1
  33. package/coverage/IdentifyCommand.js.html +0 -1462
  34. package/coverage/PropagateCommand.js.html +0 -1507
  35. package/coverage/PushCommand.js.html +0 -1504
  36. package/coverage/ScanCommand.js.html +0 -1654
  37. package/coverage/UploadCommand.js.html +0 -1846
  38. package/coverage/WatchCommand.js.html +0 -4111
  39. package/coverage/base.css +0 -224
  40. package/coverage/block-navigation.js +0 -87
  41. package/coverage/favicon.png +0 -0
  42. package/coverage/index.html +0 -191
  43. package/coverage/lcov-report/IdentifyCommand.js.html +0 -1462
  44. package/coverage/lcov-report/PropagateCommand.js.html +0 -1507
  45. package/coverage/lcov-report/PushCommand.js.html +0 -1504
  46. package/coverage/lcov-report/ScanCommand.js.html +0 -1654
  47. package/coverage/lcov-report/UploadCommand.js.html +0 -1846
  48. package/coverage/lcov-report/WatchCommand.js.html +0 -4111
  49. package/coverage/lcov-report/base.css +0 -224
  50. package/coverage/lcov-report/block-navigation.js +0 -87
  51. package/coverage/lcov-report/favicon.png +0 -0
  52. package/coverage/lcov-report/index.html +0 -191
  53. package/coverage/lcov-report/prettify.css +0 -1
  54. package/coverage/lcov-report/prettify.js +0 -2
  55. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  56. package/coverage/lcov-report/sorter.js +0 -210
  57. package/coverage/lcov.info +0 -1937
  58. package/coverage/prettify.css +0 -1
  59. package/coverage/prettify.js +0 -2
  60. package/coverage/sort-arrow-sprite.png +0 -0
  61. package/coverage/sorter.js +0 -210
  62. package/docs/API_ENDPOINTS_FOR_DETECTION.md +0 -647
  63. package/docs/API_RETRY_MECHANISM.md +0 -338
  64. package/docs/ARELA_IDENTIFY_IMPLEMENTATION.md +0 -489
  65. package/docs/ARELA_IDENTIFY_QUICKREF.md +0 -186
  66. package/docs/ARELA_PROPAGATE_IMPLEMENTATION.md +0 -581
  67. package/docs/ARELA_PROPAGATE_QUICKREF.md +0 -272
  68. package/docs/ARELA_PUSH_IMPLEMENTATION.md +0 -577
  69. package/docs/ARELA_PUSH_QUICKREF.md +0 -322
  70. package/docs/ARELA_SCAN_IMPLEMENTATION.md +0 -373
  71. package/docs/ARELA_SCAN_QUICKREF.md +0 -139
  72. package/docs/CROSS_PLATFORM_PATH_HANDLING.md +0 -597
  73. package/docs/DETECTION_ATTEMPT_TRACKING.md +0 -414
  74. package/docs/MIGRATION_UPLOADER_TO_FILE_STATS.md +0 -1020
  75. package/docs/MULTI_LEVEL_DIRECTORY_SCANNING.md +0 -494
  76. package/docs/QUICK_REFERENCE_API_DETECTION.md +0 -264
  77. package/docs/REFACTORING_SUMMARY_DETECT_PEDIMENTOS.md +0 -200
  78. package/docs/STATS_COMMAND_SEQUENCE_DIAGRAM.md +0 -287
  79. 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
+