nwinread 1.1.0 → 1.2.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 (41) hide show
  1. package/README.md +779 -96
  2. package/binding.gyp +30 -0
  3. package/build/binding.sln +6 -0
  4. package/build/eventlogasync.vcxproj +148 -0
  5. package/build/eventlogasync.vcxproj.filters +43 -0
  6. package/doc/ASYNC_STATUS.md +104 -0
  7. package/doc/COHERENCIA_APIS.md +154 -0
  8. package/doc/CONTRIBUTING.md +64 -0
  9. package/doc/CORRECCION_NAPI.md +74 -0
  10. package/doc/CPU_EFFICIENCY_GUIDE.md +199 -0
  11. package/doc/NAPI-SETUP.md +294 -0
  12. package/doc/PREBUILDS.md +180 -0
  13. package/doc/README_eventlogasync.md +134 -0
  14. package/doc/RESUMABLE_READER.md +250 -0
  15. package/doc/USAGE.md +527 -0
  16. package/index.js +202 -25
  17. package/native/eventlogasync.cc +687 -0
  18. package/package.json +37 -9
  19. package/prebuilds/metadata.json +24 -0
  20. package/prebuilds/win32-x64/eventlog.node +0 -0
  21. package/prebuilds/win32-x64/eventlogasync.node +0 -0
  22. package/prebuilds/win32-x64/meta.json +20 -0
  23. package/prebuilds/win32-x64/nwinread.node +0 -0
  24. package/scripts/generate-prebuilds-advanced.js +186 -0
  25. package/scripts/generate-prebuilds.js +86 -0
  26. package/scripts/prebuilds/win32-x64/meta.json +20 -0
  27. package/scripts/verify-setup.js +2 -1
  28. package/test/README.md +105 -0
  29. package/test/example_async.js +107 -0
  30. package/test/example_sync.js +76 -0
  31. package/test/test_beginning_mode.js +40 -0
  32. package/test/test_build_version.js +46 -0
  33. package/test/test_callback_simple.js +46 -0
  34. package/test/test_modes_comparison.js +74 -0
  35. package/test/test_watermark_realistic.js +75 -0
  36. package/test/test_watermark_specific.js +88 -0
  37. package/test/test_wrapper_vs_native.js +58 -0
  38. package/test/verify_sync_events.js +19 -0
  39. package/CHANGES.md +0 -120
  40. package/NAPI-SETUP.md +0 -142
  41. package/test.js +0 -34
package/doc/USAGE.md ADDED
@@ -0,0 +1,527 @@
1
+ # nwinread - Native Windows Event Log Reader
2
+
3
+ Lector nativo de Event Log de Windows para Node.js con soporte para lectura síncrona (polling) y asíncrona (suscripciones).
4
+
5
+ ## ✨ Novedades v1.1.1
6
+
7
+ - **API Coherente**: Modos de lectura consistentes entre sync y async
8
+ - **Nuevos Métodos**: `subscribeFromBeginning()`, `subscribeFromEnd()`, `subscribeFromWatermark()`
9
+ - **Improved EventEmitter**: Soporte para modos en `createEventEmitter()`
10
+ - **Compatibilidad 100%**: Código existente sigue funcionando sin cambios
11
+
12
+ Ver [COHERENCIA_APIS.md](COHERENCIA_APIS.md) para detalles completos.
13
+
14
+ ## Instalación y Compilación
15
+
16
+ ```bash
17
+ # Instalar dependencias
18
+ npm install
19
+
20
+ # Compilar módulos nativos
21
+ npm run rebuild
22
+ ```
23
+
24
+ ## Uso Básico
25
+
26
+ ### API Síncrona (Polling)
27
+
28
+ ```javascript
29
+ const eventLog = require('nwinread');
30
+
31
+ // Leer eventos más recientes
32
+ const result = eventLog.readEvents(
33
+ "System", // Canal
34
+ eventLog.START_MODE.END, // Desde el final
35
+ 0, // Watermark
36
+ 100, // Máximo eventos
37
+ [1074, 1076] // Solo shutdown/restart
38
+ );
39
+
40
+ console.log(`Eventos: ${result.records.length}`);
41
+ console.log(`Último ID: ${result.lastRecordId}`);
42
+
43
+ result.records.forEach(event => {
44
+ console.log(`ID: ${event.recordId}, XML: ${event.xml}`);
45
+ });
46
+ ```
47
+
48
+ ### API Asíncrona (Suscripciones)
49
+
50
+ ```javascript
51
+ const eventLog = require('nwinread');
52
+
53
+ // Crear suscripción con callbacks
54
+ const subscription = eventLog.subscribe(
55
+ "System", // Canal
56
+ 0, // Watermark (0 = solo futuros)
57
+ (event) => { // Callback de eventos
58
+ console.log(`Evento: ${event.recordId}`);
59
+ console.log(`XML: ${event.xml}`);
60
+ },
61
+ (error) => { // Callback de errores
62
+ console.error(`Error: ${error.message}`);
63
+ },
64
+ [6005, 6006] // Solo eventos de inicio/fin de sesión
65
+ );
66
+
67
+ // Gestionar la suscripción
68
+ console.log(`ID: ${subscription.id}`);
69
+ console.log(`Último Record ID: ${subscription.getLastRecordId()}`);
70
+
71
+ // Cancelar cuando termine
72
+ subscription.unsubscribe();
73
+ ```
74
+
75
+ ### API EventEmitter (Conveniente)
76
+
77
+ ```javascript
78
+ const eventLog = require('nwinread');
79
+
80
+ // Crear EventEmitter
81
+ const emitter = eventLog.createEventEmitter('Application', {
82
+ watermark: 0,
83
+ eventIds: [1000, 1001, 1002]
84
+ });
85
+
86
+ // Escuchar eventos
87
+ emitter.on('event', (event) => {
88
+ console.log(`App Event: ${event.recordId}`);
89
+ });
90
+
91
+ emitter.on('error', (error) => {
92
+ console.error(`Error: ${error.message}`);
93
+ });
94
+
95
+ // Limpiar
96
+ emitter.unsubscribe();
97
+ ```
98
+
99
+ ## API Coherente (Modos Síncronos/Asíncronos)
100
+
101
+ New! APIs que mantienen coherencia entre operaciones síncronas y asíncronas:
102
+
103
+ ### Métodos Helper Específicos
104
+
105
+ ```javascript
106
+ const eventLog = require('nwinread');
107
+
108
+ // 1. Procesar TODOS los eventos (equivale a mode=BEGINNING)
109
+ const allEvents = eventLog.subscribeFromBeginning("System",
110
+ (event) => console.log(`Historical: ${event.recordId}`),
111
+ (error) => console.error(error.message)
112
+ );
113
+
114
+ // 2. Solo eventos FUTUROS (equivale a mode=END)
115
+ const futureEvents = eventLog.subscribeFromEnd("Security",
116
+ (event) => console.log(`New: ${event.recordId}`),
117
+ (error) => console.error(error.message)
118
+ );
119
+
120
+ // 3. Desde un PUNTO ESPECÍFICO (equivale a mode=WATERMARK)
121
+ const fromPoint = eventLog.subscribeFromWatermark("Application", 50000,
122
+ (event) => console.log(`From 50k: ${event.recordId}`),
123
+ (error) => console.error(error.message)
124
+ );
125
+ ```
126
+
127
+ ### subscribe() con Opciones de Modo
128
+
129
+ ```javascript
130
+ // Nuevo formato con modo explícito
131
+ const subscription = eventLog.subscribe("System", {
132
+ mode: eventLog.START_MODE.BEGINNING, // BEGINNING, END, o WATERMARK
133
+ watermark: 12345, // Para WATERMARK mode
134
+ eventIds: [1074, 6005] // Filtros opcionales
135
+ }, onEvent, onError);
136
+
137
+ // Formato anterior sigue funcionando (compatibilidad total)
138
+ const legacy = eventLog.subscribe("System", 0, onEvent, onError, [1074]);
139
+ ```
140
+
141
+ ### EventEmitter con Modos
142
+
143
+ ```javascript
144
+ // EventEmitter que procesa todos los eventos históricos
145
+ const historicalEmitter = eventLog.createEventEmitter('Application', {
146
+ mode: eventLog.START_MODE.BEGINNING,
147
+ eventIds: [1000, 1001]
148
+ });
149
+
150
+ // EventEmitter solo para eventos futuros con filtros
151
+ const futureEmitter = eventLog.createEventEmitter('Security', {
152
+ mode: eventLog.START_MODE.END,
153
+ eventIds: [4624, 4625, 4634] // Login/logout events
154
+ });
155
+ ```
156
+
157
+ ## API Referencia
158
+
159
+ ### `readEvents(channel, mode, watermark, maxEvents, eventIds)`
160
+
161
+ Lectura síncrona de eventos del Event Log.
162
+
163
+ - **channel** `string` - Canal del Event Log ("System", "Application", "Security", etc.)
164
+ - **mode** `number` - Modo de lectura (START_MODE.BEGINNING, END, WATERMARK)
165
+ - **watermark** `number` - Record ID de inicio para modo WATERMARK
166
+ - **maxEvents** `number` - Número máximo de eventos a retornar (1-10000)
167
+ - **eventIds** `Array<number>` - Array opcional de Event IDs para filtrar
168
+
169
+ **Retorna:** `Object`
170
+ ```javascript
171
+ {
172
+ records: [
173
+ { recordId: number, xml: string },
174
+ ...
175
+ ],
176
+ lastRecordId: number
177
+ }
178
+ ```
179
+
180
+ ### `subscribe(channel, watermark, onEvent, onError, eventIds)`
181
+
182
+ Crear suscripción asíncrona a eventos en tiempo real.
183
+
184
+ - **channel** `string` - Canal del Event Log
185
+ - **watermark** `number` - Record ID de inicio (ver [Configuración de Offset](#configuración-de-offset))
186
+ - **onEvent** `Function` - Callback `(event) => {}` llamado por cada evento
187
+ - **onError** `Function` - Callback `(error) => {}` llamado en errores
188
+ - **eventIds** `Array<number>` - Array opcional de Event IDs para filtrar
189
+
190
+ **Retorna:** `EventLogSubscription`
191
+
192
+ ### `createEventEmitter(channel, options)`
193
+
194
+ Crear EventEmitter para suscripción conveniente.
195
+
196
+ - **channel** `string` - Canal del Event Log
197
+ - **options** `Object` - Opciones de configuración
198
+ - **watermark** `number` - Record ID de inicio (default: 0)
199
+ - **eventIds** `Array<number>` - Event IDs para filtrar (default: null)
200
+
201
+ **Retorna:** `EventEmitter` que emite eventos 'event' y 'error'
202
+
203
+ ### Clase `EventLogSubscription`
204
+
205
+ #### Métodos
206
+
207
+ - **`getLastRecordId()`** - Obtiene el último Record ID procesado
208
+ - **`unsubscribe()`** - Cancela la suscripción
209
+ - **`isActive()`** - Verifica si la suscripción está activa
210
+
211
+ #### Propiedades
212
+
213
+ - **`id`** - ID único de la suscripción
214
+ - **`channel`** - Canal al que está suscrito
215
+ - **`watermark`** - Watermark inicial
216
+
217
+ ## Constantes
218
+
219
+ ### `START_MODE`
220
+
221
+ ```javascript
222
+ {
223
+ BEGINNING: 0, // Leer desde el inicio del log
224
+ END: 1, // Leer desde el final del log
225
+ WATERMARK: 2 // Leer desde un Record ID específico
226
+ }
227
+ ```
228
+
229
+ ## Canales Comunes
230
+
231
+ - **"System"** - Eventos del sistema (inicio, apagado, errores hardware)
232
+ - **"Application"** - Eventos de aplicaciones
233
+ - **"Security"** - Eventos de seguridad y auditoría
234
+ - **"Setup"** - Eventos de instalación
235
+ - **"Microsoft-Windows-*"** - Canales específicos de Windows
236
+
237
+ ## Event IDs Comunes
238
+
239
+ ### Sistema
240
+ - **1074** - Shutdown del sistema
241
+ - **1076** - Motivo de último shutdown
242
+ - **6005** - Inicio del Event Log Service
243
+ - **6006** - Parada del Event Log Service
244
+ - **6008** - Shutdown inesperado anterior
245
+
246
+ ### Seguridad
247
+ - **4624** - Logon exitoso
248
+ - **4625** - Logon fallido
249
+ - **4634** - Logoff de cuenta
250
+
251
+ ## Ejemplos Avanzados
252
+
253
+ ### Monitoreo de Seguridad
254
+
255
+ ```javascript
256
+ const eventLog = require('nwinread');
257
+
258
+ const securityMonitor = eventLog.createEventEmitter('Security', {
259
+ eventIds: [4624, 4625, 4634] // Logon/Logoff events
260
+ });
261
+
262
+ securityMonitor.on('event', (event) => {
263
+ // Parsear XML del evento para obtener detalles
264
+ const xml = event.xml;
265
+
266
+ if (xml.includes('4625')) {
267
+ console.log('⚠️ Intento de login fallido detectado');
268
+ } else if (xml.includes('4624')) {
269
+ console.log('✅ Login exitoso');
270
+ }
271
+ });
272
+ ```
273
+
274
+ ### Sistema de Alarmas
275
+
276
+ ```javascript
277
+ const eventLog = require('nwinread');
278
+
279
+ class SystemAlerts {
280
+ constructor() {
281
+ this.subscription = eventLog.subscribe(
282
+ "System",
283
+ 0,
284
+ this.handleEvent.bind(this),
285
+ this.handleError.bind(this),
286
+ [1074, 1076, 6008] // Shutdown y errores
287
+ );
288
+ }
289
+
290
+ handleEvent(event) {
291
+ const xml = event.xml;
292
+
293
+ if (xml.includes('1074')) {
294
+ this.sendAlert('Sistema apagándose', event);
295
+ } else if (xml.includes('6008')) {
296
+ this.sendAlert('Shutdown inesperado detectado', event);
297
+ }
298
+ }
299
+
300
+ handleError(error) {
301
+ console.error(`Sistema de alertas error: ${error.message}`);
302
+ }
303
+
304
+ sendAlert(message, event) {
305
+ console.log(`🚨 ALERTA: ${message}`);
306
+ console.log(` Record ID: ${event.recordId}`);
307
+ console.log(` Timestamp: ${new Date().toISOString()}`);
308
+ }
309
+
310
+ stop() {
311
+ return this.subscription.unsubscribe();
312
+ }
313
+ }
314
+
315
+ const alerts = new SystemAlerts();
316
+ ```
317
+
318
+ ## Manejo de Errores
319
+
320
+ ### Errores Comunes
321
+
322
+ - **Access Denied** - Permisos insuficientes para leer el canal
323
+ - **Channel Not Found** - El canal especificado no existe
324
+ - **Invalid Arguments** - Parámetros incorrectos
325
+
326
+ ### Códigos de Error Windows
327
+
328
+ Los errores retornan objetos con:
329
+ ```javascript
330
+ {
331
+ message: "Descripción del error",
332
+ code: 123 // Código de error de Windows
333
+ }
334
+ ```
335
+
336
+ ## Rendimiento y Best Practices
337
+
338
+ ### Para API Síncrona
339
+ - Use para consultas puntuales o análisis histórico
340
+ - Limite maxEvents para evitar uso excesivo de memoria
341
+ - Use filtros eventIds para mejorar rendimiento
342
+
343
+ ### Para API Asíncrona
344
+ - Ideal para monitoreo en tiempo real
345
+ - Menor uso de CPU que polling
346
+ - Maneje adecuadamente cleanup en cierre de aplicación
347
+ - Use filtros para reducir ruido
348
+
349
+ ### Cleanup Apropiado
350
+
351
+ ```javascript
352
+ // Manejar señales de terminación
353
+ process.on('SIGINT', cleanup);
354
+ process.on('SIGTERM', cleanup);
355
+
356
+ function cleanup() {
357
+ if (subscription && subscription.isActive()) {
358
+ subscription.unsubscribe();
359
+ }
360
+ process.exit(0);
361
+ }
362
+ ```
363
+
364
+ ## Configuración de Offset
365
+
366
+ El parámetro `watermark` (offset de inicio) permite controlar desde qué punto iniciar la lectura de eventos. Es especialmente útil para recuperación tras interrupciones o análisis histórico selectivo.
367
+
368
+ ### Valores de Watermark
369
+
370
+ | Valor | Comportamiento | Caso de Uso |
371
+ |-------|---------------|-------------|
372
+ | `0` | Solo eventos futuros | Monitoreo en tiempo real |
373
+ | `N > 0` | Desde Record ID específico | Recuperación desde punto conocido |
374
+ | `-1` | Todos los eventos disponibles | Análisis histórico completo |
375
+
376
+ ### Ejemplos de Uso
377
+
378
+ #### 1. Monitoreo en Tiempo Real
379
+ ```javascript
380
+ // Solo eventos que ocurran después de la suscripción
381
+ const subscription = eventLog.subscribe(
382
+ "System",
383
+ 0, // Solo eventos futuros
384
+ onEvent,
385
+ onError
386
+ );
387
+ ```
388
+
389
+ #### 2. Recuperación desde Punto Específico
390
+ ```javascript
391
+ // Continuar desde un Record ID conocido
392
+ const subscription = eventLog.subscribe(
393
+ "Application",
394
+ 5000, // Desde Record ID 5000 en adelante
395
+ onEvent,
396
+ onError
397
+ );
398
+ ```
399
+
400
+ #### 3. Análisis Histórico Completo
401
+ ```javascript
402
+ // Procesar todos los eventos disponibles
403
+ const subscription = eventLog.subscribe(
404
+ "Security",
405
+ -1, // Todos los eventos del log
406
+ onEvent,
407
+ onError
408
+ );
409
+ ```
410
+
411
+ #### 4. Offset Dinámico por Fecha
412
+ ```javascript
413
+ function getOffsetByDate(channel, targetDate) {
414
+ const events = eventLog.read(channel, "recent", 0, 100);
415
+ let closestRecordId = 0;
416
+ let closestDiff = Infinity;
417
+
418
+ for (const event of events) {
419
+ const eventTime = new Date(event.timestamp);
420
+ const timeDiff = Math.abs(eventTime.getTime() - targetDate.getTime());
421
+
422
+ if (timeDiff < closestDiff) {
423
+ closestDiff = timeDiff;
424
+ closestRecordId = event.recordId;
425
+ }
426
+ }
427
+
428
+ return closestRecordId;
429
+ }
430
+
431
+ // Suscribirse desde hace 1 hora
432
+ const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000);
433
+ const offset = getOffsetByDate("System", oneHourAgo);
434
+
435
+ const subscription = eventLog.subscribe("System", offset, onEvent, onError);
436
+ ```
437
+
438
+ #### 5. Estado de Suscripción con Offset
439
+ ```javascript
440
+ const subscription = eventLog.subscribe("Application", 1000, onEvent, onError);
441
+
442
+ console.log(`Iniciado desde Record ID: ${subscription.getStartWatermark()}`);
443
+ console.log(`Canal: ${subscription.channel}`);
444
+ console.log(`Activa: ${subscription.isActive()}`);
445
+ ```
446
+
447
+ ### EventEmitter con Offset
448
+
449
+ ```javascript
450
+ const emitter = eventLog.createEventEmitter('System', {
451
+ watermark: 2500, // Desde Record ID 2500
452
+ eventIds: [1074, 6008] // Con filtro de eventos
453
+ });
454
+
455
+ emitter.on('event', (event) => {
456
+ console.log(`Record: ${event.recordId}, desde offset 2500`);
457
+ });
458
+ ```
459
+
460
+ ### Mejores Prácticas para Offsets
461
+
462
+ 1. **Persistir último Record ID**: Guarde el último Record ID procesado para recuperación
463
+ 2. **Validar disponibilidad**: Los logs rotan, verifique que el offset sigue disponible
464
+ 3. **Fallback a 0**: Si el offset específico falla, use 0 como fallback
465
+ 4. **Monitorear gaps**: Detecte saltos en Record IDs que indican rotación de logs
466
+
467
+ ```javascript
468
+ class PersistentEventReader {
469
+ constructor(channel, stateFile = './last-record-id.txt') {
470
+ this.channel = channel;
471
+ this.stateFile = stateFile;
472
+ this.lastRecordId = this.loadState();
473
+ }
474
+
475
+ start() {
476
+ this.subscription = eventLog.subscribe(
477
+ this.channel,
478
+ this.lastRecordId,
479
+ (event) => {
480
+ this.handleEvent(event);
481
+ this.saveState(event.recordId);
482
+ },
483
+ this.handleError.bind(this)
484
+ );
485
+ }
486
+
487
+ handleEvent(event) {
488
+ console.log(`Procesando Record ID: ${event.recordId}`);
489
+ // Procesar evento...
490
+ }
491
+
492
+ loadState() {
493
+ try {
494
+ const fs = require('fs');
495
+ const content = fs.readFileSync(this.stateFile, 'utf8');
496
+ return parseInt(content.trim()) + 1; // Siguiente al último procesado
497
+ } catch {
498
+ return 0; // Primera ejecución, solo eventos futuros
499
+ }
500
+ }
501
+
502
+ saveState(recordId) {
503
+ const fs = require('fs');
504
+ fs.writeFileSync(this.stateFile, recordId.toString());
505
+ }
506
+ }
507
+ ```
508
+
509
+ ## Requisitos del Sistema
510
+
511
+ - Windows Vista o superior
512
+ - Permisos de lectura al Event Log
513
+ - Node.js 14+ con N-API support
514
+ - Visual Studio Build Tools para compilación
515
+
516
+ ## Troubleshooting
517
+
518
+ ### Error: "Module not found"
519
+ ```bash
520
+ npm run rebuild
521
+ ```
522
+
523
+ ### Error: "Access denied"
524
+ Ejecutar con permisos elevados o verificar permisos de usuario.
525
+
526
+ ### Error de compilación
527
+ Verificar que Visual Studio Build Tools esté instalado.