nwinread 1.1.1 → 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 (39) hide show
  1. package/README.md +767 -134
  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/CORRECCION_NAPI.md +74 -0
  9. package/doc/CPU_EFFICIENCY_GUIDE.md +199 -0
  10. package/doc/PREBUILDS.md +180 -0
  11. package/doc/README_eventlogasync.md +134 -0
  12. package/doc/RESUMABLE_READER.md +250 -0
  13. package/doc/USAGE.md +527 -0
  14. package/index.js +206 -5
  15. package/native/eventlogasync.cc +687 -0
  16. package/package.json +33 -6
  17. package/prebuilds/metadata.json +24 -0
  18. package/prebuilds/win32-x64/eventlog.node +0 -0
  19. package/prebuilds/win32-x64/eventlogasync.node +0 -0
  20. package/prebuilds/win32-x64/meta.json +20 -0
  21. package/prebuilds/win32-x64/nwinread.node +0 -0
  22. package/scripts/generate-prebuilds-advanced.js +186 -0
  23. package/scripts/generate-prebuilds.js +86 -0
  24. package/scripts/prebuilds/win32-x64/meta.json +20 -0
  25. package/test/README.md +105 -0
  26. package/test/example_async.js +107 -0
  27. package/test/example_sync.js +76 -0
  28. package/test/test_beginning_mode.js +40 -0
  29. package/test/test_build_version.js +46 -0
  30. package/test/test_callback_simple.js +46 -0
  31. package/test/test_modes_comparison.js +74 -0
  32. package/test/test_watermark_realistic.js +75 -0
  33. package/test/test_watermark_specific.js +88 -0
  34. package/test/test_wrapper_vs_native.js +58 -0
  35. package/test/verify_sync_events.js +19 -0
  36. package/CHANGES.md +0 -120
  37. package/test.js +0 -34
  38. /package/{CONTRIBUTING.md → doc/CONTRIBUTING.md} +0 -0
  39. /package/{NAPI-SETUP.md → doc/NAPI-SETUP.md} +0 -0
package/package.json CHANGED
@@ -1,32 +1,59 @@
1
1
  {
2
2
  "name": "nwinread",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "build": "node-gyp configure && node-gyp build",
7
7
  "rebuild": "node-gyp rebuild",
8
8
  "clean": "node-gyp clean",
9
- "test": "node test.js",
9
+ "test": "npm run example:sync && npm run example:async",
10
+ "example:sync": "node test/example_sync.js",
11
+ "example:async": "node test/example_async.js",
12
+ "example:resumable": "node test/example_resumable.js",
13
+ "example:async-only": "node test/example_async_only.js",
14
+ "test:recovery": "node test/test_recovery.js",
15
+ "test:performance": "node test/performance_comparison.js",
16
+ "demo:cpu": "node cpu_demo.js",
17
+ "examples": "npm run example:sync && npm run example:async",
10
18
  "prebuildify": "prebuildify --napi --strip",
11
19
  "prebuildify-cross": "prebuildify-cross",
12
20
  "prebuild": "npm run prebuildify",
21
+ "prebuild-advanced": "node scripts/generate-prebuilds-advanced.js",
22
+ "prebuild-all": "cross-env BUILD_ALL=1 node scripts/generate-prebuilds-advanced.js",
13
23
  "verify": "node scripts/verify-setup.js",
14
- "prepublishOnly": "npm run prebuild",
15
- "install": "node-gyp-build"
24
+ "prepublishOnly": "npm run prebuild-advanced",
25
+ "install": "node-gyp-build-optional-packages || npm run rebuild"
16
26
  },
17
27
  "author": "solzimer",
18
28
  "license": "MIT",
19
- "description": "Native Windows Event Log Reader for Node.js",
29
+ "description": "High-performance native Node.js module for reading Windows event logs with real-time monitoring and watermark support",
20
30
  "repository": {
21
31
  "type": "git",
22
32
  "url": "https://github.com/solzimer/nwinread.git"
23
33
  },
34
+ "bugs": {
35
+ "url": "https://github.com/solzimer/nwinread/issues"
36
+ },
37
+ "homepage": "https://github.com/solzimer/nwinread#readme",
38
+ "engines": {
39
+ "node": ">=16.0.0"
40
+ },
24
41
  "keywords": [
25
42
  "windows",
26
43
  "event-log",
44
+ "eventlog",
27
45
  "native",
28
46
  "wevtapi",
29
- "napi"
47
+ "napi",
48
+ "monitoring",
49
+ "real-time",
50
+ "windows-api",
51
+ "system-monitoring",
52
+ "log-reader",
53
+ "event-monitoring",
54
+ "security-log",
55
+ "application-log",
56
+ "system-log"
30
57
  ],
31
58
  "devDependencies": {
32
59
  "node-gyp": "^10.0.0",
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "nwinread",
3
+ "version": "1.2.0",
4
+ "generated": "2026-04-10T08:10:15.887Z",
5
+ "prebuilds": [
6
+ {
7
+ "platform": "win32-x64",
8
+ "files": [
9
+ {
10
+ "name": "eventlog.node",
11
+ "size": 183296
12
+ },
13
+ {
14
+ "name": "eventlogasync.node",
15
+ "size": 200192
16
+ },
17
+ {
18
+ "name": "nwinread.node",
19
+ "size": 183296
20
+ }
21
+ ]
22
+ }
23
+ ]
24
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "generated": "2026-04-06T19:11:15.296Z",
3
+ "platform": "win32",
4
+ "arch": "x64",
5
+ "nodeVersion": "v22.17.0",
6
+ "targets": [
7
+ {
8
+ "name": "EventLog (Síncrono)",
9
+ "file": "eventlog.node",
10
+ "exists": true,
11
+ "size": 183296
12
+ },
13
+ {
14
+ "name": "EventLogAsync (Asíncrono)",
15
+ "file": "eventlogasync.node",
16
+ "exists": true,
17
+ "size": 198144
18
+ }
19
+ ]
20
+ }
Binary file
@@ -0,0 +1,186 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { execSync } = require('child_process');
4
+
5
+ // Script avanzado para generar prebuilds multiplataforma
6
+ console.log('=== Generador de Prebuilds Avanzado ===\n');
7
+
8
+ const projectRoot = process.cwd();
9
+ const basePrebuildsDir = path.join(projectRoot, 'prebuilds');
10
+
11
+ // Configuraciones de build
12
+ const buildConfigs = [
13
+ { platform: 'win32', arch: 'x64', supported: true },
14
+ { platform: 'win32', arch: 'ia32', supported: false }, // Opcional
15
+ ];
16
+
17
+ // Versiones de Node a soportar (solo las NAPI versions)
18
+ const napiVersions = [3, 4, 5, 6, 7, 8, 9];
19
+
20
+ console.log('Configuraciones de build:');
21
+ buildConfigs.forEach(config => {
22
+ console.log(` - ${config.platform}-${config.arch}: ${config.supported ? '✓' : '⚠️ (opcional)'}`);
23
+ });
24
+
25
+ console.log(`\nVersiones NAPI a soportar: ${napiVersions.join(', ')}\n`);
26
+
27
+ function generatePrebuildsForPlatform(platform, arch) {
28
+ const prebuildsDir = path.join(basePrebuildsDir, `${platform}-${arch}`);
29
+
30
+ if (!fs.existsSync(prebuildsDir)) {
31
+ fs.mkdirSync(prebuildsDir, { recursive: true });
32
+ }
33
+
34
+ console.log(`📦 Generando prebuilds para ${platform}-${arch}...`);
35
+
36
+ // Para Windows, copiamos los archivos ya compilados
37
+ if (platform === 'win32' && process.platform === 'win32') {
38
+ const buildDir = path.join(projectRoot, 'build', 'Release');
39
+ const targets = ['eventlog', 'eventlogasync'];
40
+
41
+ targets.forEach(target => {
42
+ const sourcePath = path.join(buildDir, `${target}.node`);
43
+ const targetPath = path.join(prebuildsDir, `${target}.node`);
44
+
45
+ if (fs.existsSync(sourcePath)) {
46
+ fs.copyFileSync(sourcePath, targetPath);
47
+ const stats = fs.statSync(targetPath);
48
+ console.log(` ✓ ${target}.node (${Math.round(stats.size / 1024)} KB)`);
49
+ } else {
50
+ console.log(` ❌ ${target}.node (archivo fuente no encontrado)`);
51
+ }
52
+ });
53
+ }
54
+
55
+ return prebuildsDir;
56
+ }
57
+
58
+ function createPackageMetadata() {
59
+ const metadata = {
60
+ name: require('../package.json').name,
61
+ version: require('../package.json').version,
62
+ generated: new Date().toISOString(),
63
+ prebuilds: []
64
+ };
65
+
66
+ // Escanear directorio prebuilds
67
+ if (fs.existsSync(basePrebuildsDir)) {
68
+ const platforms = fs.readdirSync(basePrebuildsDir);
69
+
70
+ platforms.forEach(platformDir => {
71
+ const fullPath = path.join(basePrebuildsDir, platformDir);
72
+ if (fs.statSync(fullPath).isDirectory()) {
73
+ const files = fs.readdirSync(fullPath);
74
+ const nodeFiles = files.filter(f => f.endsWith('.node'));
75
+
76
+ if (nodeFiles.length > 0) {
77
+ metadata.prebuilds.push({
78
+ platform: platformDir,
79
+ files: nodeFiles.map(f => ({
80
+ name: f,
81
+ size: fs.statSync(path.join(fullPath, f)).size
82
+ }))
83
+ });
84
+ }
85
+ }
86
+ });
87
+ }
88
+
89
+ const metadataPath = path.join(basePrebuildsDir, 'metadata.json');
90
+ fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
91
+ console.log(`\n✓ Metadata guardada: ${metadataPath}`);
92
+
93
+ return metadata;
94
+ }
95
+
96
+ function validatePrebuilds() {
97
+ console.log('\n=== Validación de Prebuilds ===');
98
+
99
+ const requiredModules = ['eventlog', 'eventlogasync'];
100
+ let totalSize = 0;
101
+ let platformCount = 0;
102
+
103
+ if (!fs.existsSync(basePrebuildsDir)) {
104
+ console.log('❌ Directorio prebuilds no existe');
105
+ return false;
106
+ }
107
+
108
+ const platforms = fs.readdirSync(basePrebuildsDir).filter(d =>
109
+ fs.statSync(path.join(basePrebuildsDir, d)).isDirectory()
110
+ );
111
+
112
+ platforms.forEach(platform => {
113
+ console.log(`\n📁 Plataforma: ${platform}`);
114
+ const platformDir = path.join(basePrebuildsDir, platform);
115
+ const files = fs.readdirSync(platformDir);
116
+
117
+ requiredModules.forEach(module => {
118
+ const moduleFile = `${module}.node`;
119
+ if (files.includes(moduleFile)) {
120
+ const filePath = path.join(platformDir, moduleFile);
121
+ const size = fs.statSync(filePath).size;
122
+ totalSize += size;
123
+ console.log(` ✓ ${moduleFile} (${Math.round(size / 1024)} KB)`);
124
+ } else {
125
+ console.log(` ❌ ${moduleFile} (faltante)`);
126
+ }
127
+ });
128
+
129
+ platformCount++;
130
+ });
131
+
132
+ console.log(`\n📊 Resumen:`);
133
+ console.log(` - Plataformas: ${platformCount}`);
134
+ console.log(` - Tamaño total: ${Math.round(totalSize / 1024)} KB`);
135
+ console.log(` - Archivos por plataforma: ${requiredModules.length}`);
136
+
137
+ return platformCount > 0;
138
+ }
139
+
140
+ // Main execution
141
+ async function main() {
142
+ try {
143
+ // Verificar que existan builds locales
144
+ const buildDir = path.join(projectRoot, 'build', 'Release');
145
+ if (!fs.existsSync(buildDir)) {
146
+ console.log('❌ No hay builds locales. Ejecute: npm run rebuild');
147
+ process.exit(1);
148
+ }
149
+
150
+ // Generar prebuilds para plataformas soportadas
151
+ buildConfigs.forEach(config => {
152
+ if (config.supported || process.env.BUILD_ALL) {
153
+ generatePrebuildsForPlatform(config.platform, config.arch);
154
+ } else {
155
+ console.log(`⏭️ Omitiendo ${config.platform}-${config.arch} (opcional)`);
156
+ }
157
+ });
158
+
159
+ // Crear metadata
160
+ const metadata = createPackageMetadata();
161
+
162
+ // Validar resultados
163
+ const success = validatePrebuilds();
164
+
165
+ if (success) {
166
+ console.log('\n🎉 Prebuilds generados exitosamente!');
167
+ console.log(`📁 Ubicación: ${basePrebuildsDir}`);
168
+
169
+ // Sugerencias para distribución
170
+ console.log('\n💡 Para distribución:');
171
+ console.log(' - Los prebuilds se incluirán automáticamente en npm pack');
172
+ console.log(' - Los usuarios no necesitarán compilar (si hay prebuild compatible)');
173
+ console.log(' - Para builds adicionales: BUILD_ALL=1 node scripts/generate-prebuilds-advanced.js');
174
+
175
+ } else {
176
+ console.log('\n❌ Error en generación de prebuilds');
177
+ process.exit(1);
178
+ }
179
+
180
+ } catch (error) {
181
+ console.error('\n❌ Error:', error.message);
182
+ process.exit(1);
183
+ }
184
+ }
185
+
186
+ main();
@@ -0,0 +1,86 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ // Script para generar prebuilds manuales para ambos targets
5
+ console.log('=== Generador de Prebuilds Personalizado ===\n');
6
+
7
+ const projectRoot = path.dirname(__dirname); // Directorio padre de scripts
8
+ const buildDir = path.join(projectRoot, 'build', 'Release');
9
+ const prebuildsDir = path.join(projectRoot, 'prebuilds', 'win32-x64');
10
+
11
+ console.log(`Directorio del proyecto: ${projectRoot}`);
12
+ console.log(`Directorio de build: ${buildDir}`);
13
+ console.log(`Directorio de prebuilds: ${prebuildsDir}\n`);
14
+
15
+ // Crear directorio de prebuilds si no existe
16
+ if (!fs.existsSync(prebuildsDir)) {
17
+ fs.mkdirSync(prebuildsDir, { recursive: true });
18
+ console.log('✓ Directorio prebuilds creado');
19
+ }
20
+
21
+ // Lista de archivos a copiar
22
+ const targets = [
23
+ {
24
+ source: path.join(buildDir, 'eventlog.node'),
25
+ target: path.join(prebuildsDir, 'eventlog.node'),
26
+ name: 'EventLog (Síncrono)'
27
+ },
28
+ {
29
+ source: path.join(buildDir, 'eventlogasync.node'),
30
+ target: path.join(prebuildsDir, 'eventlogasync.node'),
31
+ name: 'EventLogAsync (Asíncrono)'
32
+ }
33
+ ];
34
+
35
+ console.log('Copiando archivos compilados...');
36
+
37
+ targets.forEach(({ source, target, name }) => {
38
+ try {
39
+ if (fs.existsSync(source)) {
40
+ fs.copyFileSync(source, target);
41
+
42
+ const stats = fs.statSync(target);
43
+ const sizeKB = Math.round(stats.size / 1024);
44
+
45
+ console.log(`✓ ${name}: ${path.basename(target)} (${sizeKB} KB)`);
46
+ } else {
47
+ console.error(`❌ Archivo fuente no encontrado: ${source}`);
48
+ }
49
+ } catch (err) {
50
+ console.error(`❌ Error copiando ${name}:`, err.message);
51
+ }
52
+ });
53
+
54
+ // Verificar archivos generados
55
+ console.log('\n=== Verificación de Prebuilds ===');
56
+
57
+ const prebuildsFiles = fs.readdirSync(prebuildsDir);
58
+ console.log(`Archivos en prebuilds/win32-x64:`);
59
+
60
+ prebuildsFiles.forEach(file => {
61
+ const filePath = path.join(prebuildsDir, file);
62
+ const stats = fs.statSync(filePath);
63
+ const sizeKB = Math.round(stats.size / 1024);
64
+ console.log(` - ${file} (${sizeKB} KB)`);
65
+ });
66
+
67
+ // Crear meta archivo con información
68
+ const metaInfo = {
69
+ generated: new Date().toISOString(),
70
+ platform: process.platform,
71
+ arch: process.arch,
72
+ nodeVersion: process.version,
73
+ targets: targets.map(t => ({
74
+ name: t.name,
75
+ file: path.basename(t.target),
76
+ exists: fs.existsSync(t.target),
77
+ size: fs.existsSync(t.target) ? fs.statSync(t.target).size : 0
78
+ }))
79
+ };
80
+
81
+ const metaPath = path.join(prebuildsDir, 'meta.json');
82
+ fs.writeFileSync(metaPath, JSON.stringify(metaInfo, null, 2));
83
+ console.log(`✓ Meta información guardada: meta.json`);
84
+
85
+ console.log('\n🎉 Prebuilds generados exitosamente!');
86
+ console.log(`📁 Ubicación: ${prebuildsDir}`);
@@ -0,0 +1,20 @@
1
+ {
2
+ "generated": "2026-04-06T19:10:31.551Z",
3
+ "platform": "win32",
4
+ "arch": "x64",
5
+ "nodeVersion": "v22.17.0",
6
+ "targets": [
7
+ {
8
+ "name": "EventLog (Síncrono)",
9
+ "file": "eventlog.node",
10
+ "exists": false,
11
+ "size": 0
12
+ },
13
+ {
14
+ "name": "EventLogAsync (Asíncrono)",
15
+ "file": "eventlogasync.node",
16
+ "exists": false,
17
+ "size": 0
18
+ }
19
+ ]
20
+ }
package/test/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # nwinread - Ejemplos de Uso
2
+
3
+ Esta carpeta contiene ejemplos simples para usar nwinread.
4
+
5
+ ## 📋 Archivos Disponibles
6
+
7
+ - **example_sync.js** - Ejemplo síncrono (consultas puntuales)
8
+ - **example_async.js** - Ejemplo asíncrono (suscripciones en tiempo real)
9
+
10
+ ## 🚀 Uso
11
+
12
+ ### Ejemplo Síncrono (Consultas Puntuales)
13
+
14
+ ```bash
15
+ # Sintaxis
16
+ node example_sync.js <canal> <modo> [watermark] [maxEvents]
17
+
18
+ # Parámetros
19
+ # canal : System, Application, Security, etc.
20
+ # modo : 0=BEGINNING, 1=END, 2=WATERMARK
21
+ # watermark : Record ID inicial (para modo WATERMARK)
22
+ # maxEvents : Máximo número de eventos (default: 5)
23
+
24
+ # Ejemplos
25
+ node example_sync.js System 1 0 5 # Últimos 5 eventos del sistema
26
+ node example_sync.js Application 0 0 3 # Primeros 3 eventos de aplicación
27
+ node example_sync.js System 2 50000 2 # 2 eventos desde Record ID 50000
28
+ ```
29
+
30
+ ### Ejemplo Asíncrono (Tiempo Real)
31
+
32
+ ```bash
33
+ # Sintaxis
34
+ node example_async.js <canal> <modo> [watermark] [eventIds]
35
+
36
+ # Parámetros
37
+ # canal : System, Application, Security, etc.
38
+ # modo : 0=BEGINNING, 1=END, 2=WATERMARK
39
+ # watermark : Record ID inicial (para modo WATERMARK, o 0 para otros)
40
+ # eventIds : IDs de evento separados por coma (opcional)
41
+
42
+ # Ejemplos
43
+ node example_async.js System 1 0 # Eventos futuros del sistema
44
+ node example_async.js Application 0 0 # Todos los eventos de aplicación
45
+ node example_async.js System 2 50000 # Desde Record ID 50000
46
+ node example_async.js System 1 0 1074,6005 # Solo eventos de shutdown/startup
47
+ ```
48
+
49
+ ## 📚 Modos Disponibles
50
+
51
+ | Modo | Nombre | Descripción |
52
+ |------|--------|-------------|
53
+ | `0` | **BEGINNING** | Lee desde los eventos más antiguos disponibles |
54
+ | `1` | **END** | Lee desde los eventos más recientes (o solo futuros en async) |
55
+ | `2` | **WATERMARK** | Lee desde un Record ID específico |
56
+
57
+ ## 🏷️ Canales Comunes
58
+
59
+ | Canal | Descripción | Admin Requerido |
60
+ |-------|-------------|-----------------|
61
+ | `System` | Eventos del sistema, hardware, drivers | No |
62
+ | `Application` | Eventos de aplicaciones, errores | No |
63
+ | `Security` | Logins, logouts, control de acceso | **Sí** |
64
+ | `Setup` | Instalación de Windows, actualizaciones | No |
65
+
66
+ ## 🎯 Event IDs Útiles
67
+
68
+ ### System Events
69
+ - `1074` - Sistema apagado
70
+ - `6005` - Servicio Event Log iniciado
71
+ - `6006` - Servicio Event Log detenido
72
+ - `41` - Apagado inesperado
73
+
74
+ ### Application Events
75
+ - `1000` - Error de aplicación
76
+ - `1001` - Warning de aplicación
77
+ - `1002` - Información de aplicación
78
+
79
+ ### Security Events (requiere admin)
80
+ - `4624` - Login exitoso
81
+ - `4625` - Login fallido
82
+ - `4634` - Logout
83
+
84
+ ## 💡 Ejemplos Prácticos
85
+
86
+ ```bash
87
+ # Monitorear shutdowns del sistema
88
+ node example_async.js System 1 0 1074
89
+
90
+ # Ver errores de aplicación en tiempo real
91
+ node example_async.js Application 1 0 1000,1001
92
+
93
+ # Analizar eventos históricos del sistema
94
+ node example_sync.js System 0 0 50
95
+
96
+ # Monitorear logins (requiere admin)
97
+ node example_async.js Security 1 0 4624,4625,4634
98
+ ```
99
+
100
+ ## ⚠️ Notas Importantes
101
+
102
+ - Algunos canales como `Security` requieren permisos de administrador
103
+ - El modo asíncrono se ejecuta hasta que presiones `Ctrl+C`
104
+ - Los Event IDs son opcionales y permiten filtrar tipos específicos de eventos
105
+ - Use `Ctrl+C` para terminar las suscripciones asíncronas
@@ -0,0 +1,107 @@
1
+ // Ejemplo de uso de nwinread - API Asíncrona (Suscripciones en Tiempo Real)
2
+ // Uso: node example_async.js <canal> <modo> [watermark] [eventIds]
3
+ // Modos: 0=BEGINNING, 1=END, 2=WATERMARK
4
+ // Ejemplo: node example_async.js System 1 0 1074,6005
5
+
6
+ const eventLog = require('../index.js');
7
+
8
+ // Obtener argumentos de línea de comandos
9
+ const args = process.argv.slice(2);
10
+
11
+ if (args.length < 2) {
12
+ console.log('Uso: node example_async.js <canal> <modo> [watermark] [eventIds]');
13
+ console.log('');
14
+ console.log('Parámetros:');
15
+ console.log(' canal : System, Application, Security, etc.');
16
+ console.log(' modo : 0=BEGINNING, 1=END, 2=WATERMARK');
17
+ console.log(' watermark : Record ID inicial (para modo WATERMARK, o 0 para otros)');
18
+ console.log(' eventIds : IDs de evento separados por coma (opcional)');
19
+ console.log('');
20
+ console.log('Ejemplos:');
21
+ console.log(' node example_async.js System 1 0 # Eventos futuros del sistema');
22
+ console.log(' node example_async.js Application 0 0 # Todos los eventos de aplicación');
23
+ console.log(' node example_async.js System 2 50000 # Desde Record ID 50000');
24
+ console.log(' node example_async.js System 1 0 1074,6005 # Solo eventos de shutdown/startup');
25
+ process.exit(1);
26
+ }
27
+
28
+ const channel = args[0];
29
+ const mode = parseInt(args[1]);
30
+ const watermark = args[2] ? parseInt(args[2]) : 0;
31
+ const eventIds = args[3] ? args[3].split(',').map(id => parseInt(id.trim())) : null;
32
+
33
+ // Validar modo
34
+ if (![0, 1, 2].includes(mode)) {
35
+ console.error('❌ Error: Modo debe ser 0 (BEGINNING), 1 (END), o 2 (WATERMARK)');
36
+ process.exit(1);
37
+ }
38
+
39
+ const mode_names = {
40
+ 0: 'BEGINNING (todos los eventos históricos + futuros)',
41
+ 1: 'END (solo eventos futuros)',
42
+ 2: 'WATERMARK (desde Record ID específico + futuros)'
43
+ };
44
+
45
+ console.log('=== nwinread: Ejemplo Asíncrono ===');
46
+ console.log(`Canal: ${channel}`);
47
+ console.log(`Modo: ${mode} - ${mode_names[mode]}`);
48
+ console.log(`Watermark: ${watermark}`);
49
+ console.log(`Event IDs filter: ${eventIds ? eventIds.join(', ') : 'Ninguno (todos)'}`);
50
+ console.log('');
51
+ console.log('⏳ Iniciando suscripción...');
52
+ console.log(' Presiona Ctrl+C para salir');
53
+ console.log('');
54
+
55
+ let eventCount = 0;
56
+ let subscription;
57
+
58
+ const onEvent = (event) => {
59
+ eventCount++;
60
+ console.log(`📧 Evento #${eventCount}:`);
61
+ console.log(` Record ID: ${event.recordId}`);
62
+ console.log(` Event ID: ${event.eventId || 'N/A'}`);
63
+ console.log(` Timestamp: ${new Date().toLocaleTimeString()}`);
64
+ console.log(` XML (preview): ${event.xml.substring(0, 100)}...`);
65
+ console.log('');
66
+ };
67
+
68
+ const onError = (error) => {
69
+ console.error(`❌ Error en suscripción: ${error.message}`);
70
+ if (error.message.includes('access') || error.message.includes('denied')) {
71
+ console.log('💡 Intenta con permisos de administrador para canales como "Security"');
72
+ }
73
+ };
74
+
75
+ try {
76
+ // Crear suscripción según el modo especificado
77
+ if (mode === 0) {
78
+ // BEGINNING: todos los eventos históricos + futuros
79
+ subscription = eventLog.subscribeFromBeginning(channel, onEvent, onError, eventIds);
80
+ } else if (mode === 1) {
81
+ // END: solo eventos futuros
82
+ subscription = eventLog.subscribeFromEnd(channel, onEvent, onError, eventIds);
83
+ } else if (mode === 2) {
84
+ // WATERMARK: desde Record ID específico
85
+ subscription = eventLog.subscribeFromWatermark(channel, watermark, onEvent, onError, eventIds);
86
+ }
87
+
88
+ console.log(`✅ Suscripción activa - ID: ${subscription.id}`);
89
+ console.log(` Canal: ${subscription.channel}`);
90
+ console.log(` Modo: ${subscription.mode}`);
91
+ console.log('');
92
+
93
+ } catch (error) {
94
+ console.error(`❌ Error al crear suscripción: ${error.message}`);
95
+ process.exit(1);
96
+ }
97
+
98
+ // Cleanup cuando se termine el proceso
99
+ process.on('SIGINT', () => {
100
+ console.log('\n🛑 Cerrando suscripción...');
101
+ if (subscription && subscription.isActive()) {
102
+ subscription.unsubscribe();
103
+ }
104
+ console.log(`✅ Eventos procesados: ${eventCount}`);
105
+ console.log('👋 ¡Hasta luego!');
106
+ process.exit(0);
107
+ });