rosinterface 1.0.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 (36) hide show
  1. package/README.md +517 -37
  2. package/dist/cli/SchemaInferrer.d.ts +4 -0
  3. package/dist/cli/SchemaInferrer.js +42 -0
  4. package/dist/cli/SchemaInferrer.js.map +1 -0
  5. package/dist/cli/generate.d.ts +1 -0
  6. package/dist/cli/generate.js +139 -0
  7. package/dist/cli/generate.js.map +1 -0
  8. package/dist/client/CommandBuilder.js +19 -3
  9. package/dist/client/CommandBuilder.js.map +1 -1
  10. package/dist/client/MikrotikClient.d.ts +11 -1
  11. package/dist/client/MikrotikClient.js +47 -5
  12. package/dist/client/MikrotikClient.js.map +1 -1
  13. package/dist/client/MikrotikTransaction.d.ts +12 -0
  14. package/dist/client/MikrotikTransaction.js +52 -0
  15. package/dist/client/MikrotikTransaction.js.map +1 -0
  16. package/dist/client/SnapshotSubscription.d.ts +35 -0
  17. package/dist/client/SnapshotSubscription.js +143 -0
  18. package/dist/client/SnapshotSubscription.js.map +1 -0
  19. package/dist/core/SocketClient.d.ts +2 -1
  20. package/dist/core/SocketClient.js +12 -8
  21. package/dist/core/SocketClient.js.map +1 -1
  22. package/dist/features/LiveCollection.d.ts +18 -0
  23. package/dist/features/LiveCollection.js +98 -0
  24. package/dist/features/LiveCollection.js.map +1 -0
  25. package/dist/features/PrometheusExporter.js +1 -1
  26. package/dist/features/PrometheusExporter.js.map +1 -1
  27. package/dist/generated/ISecret.d.ts +14 -0
  28. package/dist/generated/ISecret.js +3 -0
  29. package/dist/generated/ISecret.js.map +1 -0
  30. package/dist/test_library.d.ts +1 -0
  31. package/dist/test_library.js +34 -0
  32. package/dist/test_library.js.map +1 -0
  33. package/dist/utils/MikrotikCollection.d.ts +7 -2
  34. package/dist/utils/MikrotikCollection.js +37 -6
  35. package/dist/utils/MikrotikCollection.js.map +1 -1
  36. package/package.json +11 -5
package/README.md CHANGED
@@ -1,13 +1,19 @@
1
- # RosInterface
1
+ # RosInterface v1.2
2
2
 
3
- > **Enterprise-Grade MikroTik RouterOS API Client for Node.js**
4
- > High-performance, **Offline-First** and **Type-Safe** framework for modern network automation.
3
+ > **High-Performance RouterOS Automation A modern TypeScript and JavasScript Library for MikroTik interactions.**
4
+ > Built with Reactive Streaming (onSnapshot), Circuit Breakers for fault tolerance, and an Offline Queue system.
5
+ > Transform raw RouterOS commands into synchronized, type-safe application state.
6
+
7
+ > **Automatización de Alto Rendimiento para RouterOS Una librería moderna en TypeScript y JavaScript para interactuar con MikroTik.**
8
+ > Diseñada con Streaming Reactivo (onSnapshot), Circuit Breakers para tolerancia a fallos y un sistema de Cola Offline.
9
+ > Transforma comandos crudos de RouterOS en un estado de aplicación sincronizado y con tipado seguro.
5
10
 
6
11
  [![npm version](https://img.shields.io/npm/v/rosinterface.svg?style=flat-square)](https://www.npmjs.com/package/rosinterface)
7
12
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
8
13
 
9
14
  ---
10
15
 
16
+
11
17
  ## English Documentation
12
18
 
13
19
  **RosInterface** is designed for **ISPs and mission-critical environments**.
@@ -17,21 +23,45 @@ It focuses on **connection stability**, **router hardware protection**, and **ef
17
23
 
18
24
  ### ✨ Key Features
19
25
 
20
- - **🛡️ Hardware Protection**
26
+ - **Hardware Protection**
21
27
  Built-in **Rate Limiter** with intelligent backoff to prevent RouterOS CPU saturation.
22
28
 
23
- - **🔌 Offline-First Architecture**
29
+ - **Offline-First Architecture**
24
30
  Commands marked with `.persistent()` are automatically queued if the router is offline and synchronized once connectivity is restored.
25
31
 
26
- - **🧠 Smart Caching**
32
+ - **Smart Caching**
27
33
  Read operations are cached for **5 seconds (TTL)** to reduce redundant API calls and router load.
28
34
 
29
- - **🔍 Source-Side Filtering**
35
+ - **Source-Side Filtering**
30
36
  Use `.findBy()` and `.findOne()` to filter data **directly on the router**, minimizing bandwidth and latency.
31
37
 
32
- - **🌊 Fluent API**
38
+ - **On-time changes**
39
+ Use `.collection()` and `.onSnapshot()` to get changes on real time **directly the router**, for to get new data without call to many times to your router.
40
+
41
+ - **Fault Tolerance (Circuit Breaker)**
42
+
43
+ Implements the `Circuit Breaker` pattern. If the router stops responding or encounters critical errors, the library temporarily "cuts the circuit" to prevent the accumulation of failed requests and protect the stability of your Node.js server.
44
+
45
+ - **Native Observability**
46
+
47
+ Built-in `.getMetrics()` method. Exports traffic statistics and router status directly in a format compatible with Prometheus and Grafana without the need for external plugins.
48
+
49
+ - **Transport Security**
50
+
51
+ Full support for **TLS/SSL (SSL API)**, allowing secure encrypted connections over port 8729 (recommended to change this), ideal for managing routers over the public internet.
52
+
53
+ - **Fluent API**
33
54
  Chainable, expressive syntax for clean and maintainable automation code.
34
55
 
56
+ - **CLI Code Generator**
57
+ Auto-generate TypeScript interfaces from your live router. No more guessing field names.
58
+
59
+ - **Differential Updates (onDiff)**
60
+ Receive atomic added, modified, and removed events instead of full arrays. Optimized for high-performance UIs.
61
+
62
+ - **Intelligent Throttling**
63
+ Control the flow of real-time data with "Leading + Trailing Edge" strategies.
64
+
35
65
  ---
36
66
 
37
67
 
@@ -39,7 +69,7 @@ It focuses on **connection stability**, **router hardware protection**, and **ef
39
69
 
40
70
  ### ✨ Important Considerations
41
71
 
42
- #### 📄 Environment Variables (.env)
72
+ #### Environment Variables (.env)
43
73
 
44
74
  Create a `.env` file in the root directory of your project:
45
75
 
@@ -56,32 +86,41 @@ MIKROTIK_PORT=8729
56
86
  >
57
87
  > Use **only in controlled, local or test environments**.
58
88
 
89
+
90
+ >⚠️ **Performance Warning**
91
+ >
92
+ > The `onSnapshot` function allows you to receive **real-time** router changes in RosInterface.
93
+ > To avoid negative performance impacts on low-spec devices, it is recommended to use it with **Resilience** enabled.
94
+ >
95
+ > Use it **in moderation; excessive use of onSnapshot can negatively affect the performance of your devices**.
96
+
97
+
59
98
  ---
60
99
 
61
100
 
62
- ### 🛠 Usage Examples
101
+ ### Usage Examples
63
102
 
64
- #### 1️⃣ Basic Connection
103
+ #### 1. Basic Connection
65
104
 
66
105
  ```ts
67
106
  import { MikrotikClient } from 'rosinterface';
68
107
 
69
108
  const client = new MikrotikClient({
70
- host: 'IP_DEL_ROUTER',
109
+ host: 'ROUTER_IP (LOCAL OR REMOTE)',
71
110
  user: 'admin',
72
111
  password: 'password',
73
- useTLS: false, // ⚠️ Test only
112
+ port: 8729, // We recommended change the default port
113
+ useTLS: false, // Test only
74
114
  rateLimit: 50,
75
- allowInsecureConfig: true // ⚠️ Never in production or release
115
+ allowInsecureConfig: true // Never in production or release
76
116
  });
77
117
 
78
118
  await client.connect();
79
119
  ```
80
120
  ---
81
121
 
82
- ---
83
122
 
84
- #### 2️⃣ Recommended connection (Production/Release)
123
+ #### 2. Recommended connection (Production/Release)
85
124
 
86
125
  ```ts
87
126
  import { MikrotikClient } from 'rosinterface';
@@ -92,8 +131,8 @@ const client = new MikrotikClient({
92
131
  user: process.env.MIKROTIK_USER,
93
132
  password: process.env.MIKROTIK_PASSWORD,
94
133
  port: Number(process.env.MIKROTIK_PORT) || 8729,
95
- useTLS: true,
96
- rateLimit: 50
134
+ useTLS: true, //Always use TLS between TLS v1.2 or TLS v1.3 deppend that your devices
135
+ rateLimit: 50 //Here you adjust the number of requests per second you want it to support (Anti DDoS)
97
136
  });
98
137
 
99
138
  await client.connect();
@@ -103,7 +142,7 @@ await client.connect();
103
142
 
104
143
 
105
144
 
106
- #### 2️⃣ Optimized Read (select + findOne)
145
+ #### 3. Optimized Read (select + findOne)
107
146
 
108
147
  ```ts
109
148
  const user = await client.command('/ppp/secret')
@@ -115,7 +154,7 @@ console.log(`User: ${user?.name}, Profile: ${user?.profile}`);
115
154
 
116
155
  ---
117
156
 
118
- #### 3️⃣ Resilience with Persistence (Offline-Safe)
157
+ #### 4. Resilience with Persistence (Offline-Safe)
119
158
 
120
159
  ```ts
121
160
  const result = await client.command('/ppp/secret')
@@ -131,9 +170,204 @@ if (result === 'QUEUED_OFFLINE') {
131
170
  }
132
171
  ```
133
172
 
173
+
174
+ #### 5. Time-Limited Listener
175
+
176
+ ```ts
177
+ // Start the subscription and store the cleanup function (unsubscribe)
178
+ const unsubscribe = client.collection('/ppp/secret')
179
+ .onSnapshot((secrets) => {
180
+ console.log(`Update received. Total users: ${secrets.length}`);
181
+ });
182
+
183
+ // Set a timer to stop listening
184
+ setTimeout(() => {
185
+ console.log('Time up. Stopping subscription...');
186
+
187
+ // Stops the Router stream and clears local memory
188
+ unsubscribe();
189
+
190
+ // Optional: Close the connection if no further tasks are needed
191
+ client.close();
192
+ }, 60000); // 60 seconds
193
+ ```
194
+
195
+
196
+ #### 6. Persistent Listener (Always On)
197
+
198
+ ```ts
199
+ // The client automatically handles Keep-Alive pings (every 10s)
200
+ // to prevent idle disconnects. No need to call 'unsubscribe'.
201
+
202
+ client.collection('/interface')
203
+ .where('disabled', false) // Optional: Only active interfaces
204
+ .onSnapshot((interfaces) => {
205
+ // This callback will execute infinitely whenever a physical change
206
+ // occurs on the router (cable unplugged, traffic state, rename, etc.)
207
+ const downInterfaces = interfaces.filter(i => i.running === 'false');
208
+
209
+ if (downInterfaces.length > 0) {
210
+ console.alert(`ALERT: ${downInterfaces.length} interfaces are down.`);
211
+ }
212
+ });
213
+
214
+ // Important: Handle network errors to reconnect if power or internet is lost
215
+ client.on('close', () => {
216
+ console.log('Connection lost. Restarting service in 5s...');
217
+ setTimeout(() => connectAndListen(), 5000);
218
+ });
219
+ ```
220
+
221
+
222
+
223
+ #### 7. Prometheus Metric
224
+
225
+ ```ts
226
+ // 1. Define the map: Mikrotik Field -> Prometheus Metric
227
+ const metricsDef = [
228
+ {
229
+ name: 'mikrotik_iface_rx_total', // Final name in Prometheus
230
+ help: 'Bytes received', // Description
231
+ type: 'counter', // Type: 'counter' or 'gauge'
232
+ path: 'rx-byte', // Original field in RouterOS
233
+ labels: ['name', 'type'] // Labels for filtering (e.g., wan, bridge)
234
+ },
235
+ {
236
+ name: 'mikrotik_iface_tx_total',
237
+ help: 'Bytes transmitted',
238
+ type: 'counter',
239
+ path: 'tx-byte',
240
+ labels: ['name', 'type']
241
+ }
242
+ ];
243
+
244
+ // 2. Get the formatted plain text ready for Prometheus
245
+ // (Internally executes /interface/print and transforms the data)
246
+ const result = await client.getMetrics('/interface', metricsDef);
247
+
248
+ console.log(result);
249
+ ```
250
+
251
+
252
+
253
+ #### 8. Type-Safe Realtime Stream (onDiff)
254
+
255
+ ```ts
256
+ client.collection('/ppp/secret')
257
+ .onSnapshot((data) => {
258
+ // Check if we are in Diff Mode
259
+ if ('added' in data) {
260
+ // TypeScript automatically infers that 'data' is a SnapshotDiff!
261
+
262
+ // Added items
263
+ data.added.forEach(secret => console.log('New:', secret.name));
264
+
265
+ // Modified items
266
+ data.modified.forEach(secret => console.log('Modified:', secret.name));
267
+
268
+ // Removed items
269
+ data.removed.forEach(secret => console.log('Removed:', secret.name));
270
+ }
271
+ else {
272
+ // If execution reaches here, .onDiff() was NOT used, so 'data' is a standard Array
273
+ console.log('Full list size:', data.length);
274
+ }
275
+ }).onDiff();
276
+ ```
277
+
278
+
279
+ #### 9. Data Transformers With Print
280
+
281
+ ```ts
282
+ // EXAMPLE 1: Using .toMap() for O(1) Lookup
283
+ // Scenario: Quickly finding a specific user by name without looping through an array.
284
+ client.command('/ppp/secret').print().then(result => {
285
+ // Transform the array into a Dictionary (Map) indexed by 'name'
286
+ const usersMap = result.toMap('name');
287
+
288
+ // Instant access! No .find() required.
289
+ const john = usersMap['john_doe'];
290
+ if (john) {
291
+ console.log(`User Found: ${john.profile}`);
292
+ }
293
+ });
294
+
295
+ // EXAMPLE 2: Using .toGrouped() for Reporting
296
+ // Scenario: Grouping active connections by their service profile (e.g. 10mb, 50mb plans).
297
+ client.command('/ppp/active').print().then(result => {
298
+ // Group users by the 'profile' property
299
+ const byProfile = result.toGrouped('profile');
300
+
301
+ console.log('--- Connected Users Summary ---');
302
+ Object.keys(byProfile).forEach(profile => {
303
+ console.log(`Plan ${profile}: ${byProfile[profile].length} users`);
304
+ });
305
+ });
306
+
307
+ // EXAMPLE 3: Using .toPages() for Pagination
308
+ // Scenario: Displaying a large list of logs in a frontend table, page by page.
309
+ const PAGE_SIZE = 50;
310
+ const CURRENT_PAGE = 1;
311
+
312
+ client.command('/log').print().then(result => {
313
+ // Get only the items for the first page
314
+ const pageData = result.toPages(CURRENT_PAGE, PAGE_SIZE);
315
+
316
+ console.log(`Showing ${pageData.length} logs (Page ${CURRENT_PAGE})`);
317
+ pageData.forEach(log => console.log(`[${log.time}] ${log.message}`));
318
+ });
319
+
320
+ ```
321
+
322
+
323
+ #### 10. CLI Codegen - Automatic Type Generation
324
+
325
+ > **OPTION A: Using .env file (Recommended for Security)**
326
+ > Create a .env file with MIKROTIK_HOST, MIKROTIK_USER, MIKROTIK_PASS
327
+ > Run the command:
328
+ > > npm run codegen -- -p /ppp/secret -n ISecret
329
+ >
330
+ > OPTION B: Manual Configuration (Quick Testing / CI)
331
+ > Pass connection details directly via flags. Use double quotes for passwords with special chars.
332
+ > >npm run codegen -- -p /ppp/secret -n ISecret --host ROUTER_IP --user admin --pass "secret123" --port 8728
333
+
334
+
335
+ ```ts
336
+ // Usage A: Type-Safe Reading (Auto-complete for properties)
337
+ // Pass the interface <ISecret> to .collection() or .command()
338
+ client.collection<ISecret>('/ppp/secret').onSnapshot((diff) => {
339
+ if ('added' in diff) {
340
+ diff.added.forEach(secret => {
341
+ // TypeScript knows 'profile' and 'service' exist!
342
+ // If you type "secret.", your IDE will show all available fields.
343
+ console.log(`New User: ${secret.name} (Plan: ${secret.profile})`);
344
+ });
345
+ }
346
+ });
347
+
348
+ // Usage B: Type-Safe Writing (Validation for .add)
349
+ async function createSecureUser() {
350
+ const api = client.command<ISecret>('/ppp/secret');
351
+
352
+ // TypeScript ensures you only pass valid fields defined in ISecret.
353
+ // It prevents typos like 'passwrod' or using invalid profile names if unions were generated.
354
+ await api.add({
355
+ name: 'vip_user',
356
+ password: 'secure_password',
357
+ profile: 'default',
358
+ service: 'pppoe',
359
+ disabled: false
360
+ });
361
+ }
362
+
363
+ ```
364
+
365
+
366
+
367
+
134
368
  ---
135
369
 
136
- ## 📕 Documentación en Español
370
+ ## Documentación en Español
137
371
 
138
372
  **RosInterface** está diseñado para **ISPs y entornos críticos**.
139
373
  Su objetivo es garantizar **estabilidad de conexión**, **protección del hardware MikroTik** y **consultas eficientes**, incluso con enlaces inestables.
@@ -142,26 +376,50 @@ Su objetivo es garantizar **estabilidad de conexión**, **protección del hardwa
142
376
 
143
377
  ### ✨ Características Principales
144
378
 
145
- - **🛡️ Protección del Hardware**
379
+ - **Protección del Hardware**
146
380
  Limitador de tasa integrado con retroceso inteligente para evitar saturación de CPU.
147
381
 
148
- - **🔌 Arquitectura Offline-First**
382
+ - **Arquitectura Offline-First**
149
383
  Los comandos con `.persistent()` se guardan en cola si el router no está disponible y se ejecutan automáticamente al reconectar.
150
384
 
151
- - **🧠 Caché Inteligente**
385
+ - **Caché Inteligente**
152
386
  Consultas con TTL de **5 segundos**, reduciendo llamadas innecesarias.
153
387
 
154
- - **🔍 Filtrado en Origen**
388
+ - **Filtrado en Origen**
155
389
  `.findBy()` y `.findOne()` filtran directamente en RouterOS sin descargar tablas completas.
156
390
 
157
- - **🌊 API Fluida**
391
+ - **Cambios puntuales**
392
+ Usa `.collection()` y `.onSnapshot()` para obtener cambios en tiempo real **directamente desde el router**, para obtener datos nuevos, sin llamar a su router repetidamente.
393
+
394
+ - **Tolerancia a Fallos (Circuit Breaker)**
395
+ Implementa el patrón `Circuit Breaker` . Si el router deja de responder o da errores críticos, la librería "corta el circuito" temporalmente para evitar acumular peticiones fallidas y proteger la estabilidad de tu servidor Node.js.
396
+
397
+ - **Observabilidad Nativa**
398
+ Método `.getMetrics()`integrado. Exporta estadísticas de tráfico y estado del router directamente en formato compatible con Prometheus y Grafana sin necesidad de plugins externos.
399
+
400
+ - **Seguridad de Transporte**
401
+ Soporte completo para **TLS/SSL (API-SSL)**, permitiendo conexiones encriptadas seguras a través del puerto 8729 (Recomendado cambiar), ideal para administrar routers a través de internet pública.
402
+
403
+ - **API Fluida**
158
404
  Sintaxis encadenable y clara.
159
405
 
406
+ - **Generador de código CLI**
407
+
408
+ Genere automáticamente interfaces TypeScript desde su enrutador en vivo. Olvídese de adivinar los nombres de los campos.
409
+
410
+ - **Actualizaciones diferenciales (onDiff)**
411
+
412
+ Reciba eventos atómicos de adición, modificación y eliminación en lugar de matrices completas. Optimizado para interfaces de usuario de alto rendimiento.
413
+
414
+ - **Limitación inteligente**
415
+
416
+ Controle el flujo de datos en tiempo real con estrategias de vanguardia y vanguardia.
417
+
160
418
  ---
161
419
 
162
420
  ### ✨ Consideraciones Importantes
163
421
 
164
- #### 📄 Variables de Entorno (.env)
422
+ #### Variables de Entorno (.env)
165
423
 
166
424
  Crea un archivo `.env` en el directorio raíz de tu proyecto:
167
425
 
@@ -178,22 +436,32 @@ MIKROTIK_PORT=8729
178
436
  >
179
437
  > Úsala **solo en entornos controlados, locales o de prueba**.
180
438
 
439
+
440
+ > ⚠️ **Aviso de Rendimiento**
441
+ > La función `onSnapshot` permite recibir cambios en **tiempo real** del router en RosInterface.
442
+ > Para evitar el impacto negativo de rendimiento en equipos con pocas prestaciones, se recomienda usarlo con **Resiliencia**.
443
+ >
444
+ > Úsala **con moderación, el abuso de onSnapshot puede afectar el rendimiento de tus equipos**.
445
+
446
+
447
+
181
448
  ---
182
449
 
183
- ### 🛠 Ejemplos de Uso
450
+ ### Ejemplos de Uso
184
451
 
185
- #### 1️⃣ Conexión rápida (solo pruebas)
452
+ #### 1. Conexión rápida (solo pruebas)
186
453
 
187
454
  ```ts
188
455
  import { MikrotikClient } from 'rosinterface';
189
456
 
190
457
  const client = new MikrotikClient({
191
- host: 'IP_DEL_ROUTER',
458
+ host: 'IP_DEL_ROUTER (LOCAL O REMOTO)',
192
459
  user: 'admin',
193
460
  password: 'password',
194
- useTLS: false, // ⚠️ Solo pruebas
461
+ port: 8729, // Se recomienda cambiar el puerto por defecto
462
+ useTLS: false, // Solo pruebas
195
463
  rateLimit: 50,
196
- allowInsecureConfig: true // ⚠️ Nunca en producción
464
+ allowInsecureConfig: true // Nunca en producción
197
465
  });
198
466
 
199
467
  await client.connect();
@@ -201,7 +469,7 @@ await client.connect();
201
469
 
202
470
  ---
203
471
 
204
- #### 2️⃣ Conexión Recomendada (Producción)
472
+ #### 2. Conexión Recomendada (Producción)
205
473
 
206
474
  ```ts
207
475
  import { MikrotikClient } from 'rosinterface';
@@ -212,8 +480,8 @@ const client = new MikrotikClient({
212
480
  user: process.env.MIKROTIK_USER,
213
481
  password: process.env.MIKROTIK_PASSWORD,
214
482
  port: Number(process.env.MIKROTIK_PORT) || 8729,
215
- useTLS: true,
216
- rateLimit: 50
483
+ useTLS: true, //Utilice siempre TLS entre TLS v1.2 o TLS v1.3 dependiendo de sus dispositivos
484
+ rateLimit: 50 //Aqui ajustas la cantidad de peticiones por segundo que deseas que soporte (Anti DDoS)
217
485
  });
218
486
 
219
487
  await client.connect();
@@ -221,7 +489,7 @@ await client.connect();
221
489
 
222
490
  ---
223
491
 
224
- #### 3️⃣ Lectura Optimizada
492
+ #### 3. Lectura Optimizada
225
493
 
226
494
  ```ts
227
495
  const user = await client.command('/ppp/secret')
@@ -233,7 +501,7 @@ console.log(`User: ${user?.name}, Profile: ${user?.profile}`);
233
501
 
234
502
  ---
235
503
 
236
- #### 4️⃣ Persistencia Offline
504
+ #### 4. Persistencia Offline
237
505
 
238
506
  ```ts
239
507
  const result = await client.command('/ppp/secret')
@@ -249,4 +517,216 @@ if (result === 'QUEUED_OFFLINE') {
249
517
  }
250
518
  ```
251
519
 
520
+
521
+ #### 5. Escuchar por un Tiempo Determinado (Time-Limited)
522
+
523
+ ```ts
524
+ // Iniciar la suscripción y guardar la función de limpieza (unsubscribe)
525
+ const unsubscribe = client.collection('/ppp/secret')
526
+ .onSnapshot((secrets) => {
527
+ console.log(`Actualización recibida. Total usuarios: ${secrets.length}`);
528
+ });
529
+
530
+ // Establecer un temporizador para detener la escucha
531
+ setTimeout(() => {
532
+ console.log('Tiempo finalizado. Cerrando suscripción...');
533
+
534
+ // Detiene el stream del Router y limpia la memoria local
535
+ unsubscribe();
536
+
537
+ // Opcional: Cerrar la conexión si ya no harás más tareas
538
+ client.close();
539
+ }, 60000); // 60 segundos
540
+ ```
541
+
542
+
543
+
544
+ #### 6. Escucha Persistente (Always On)
545
+
546
+ ```ts
547
+ // Configuración para mantener la conexión viva (Keep-Alive)
548
+ // El cliente enviará pings internos cada 10s para evitar desconexiones por inactividad.
549
+ // No es necesario llamar a 'unsubscribe' ni poner timeouts.
550
+
551
+ client.collection('/interface')
552
+ .where('disabled', false) // Opcional: Solo interfaces activas
553
+ .onSnapshot((interfaces) => {
554
+ // Este callback se ejecutará infinitamente cada vez que haya un cambio
555
+ // físico en el router (cable desconectado, tráfico, cambio de nombre, etc.)
556
+ const downInterfaces = interfaces.filter(i => i.running === 'false');
557
+
558
+ if (downInterfaces.length > 0) {
559
+ console.alert(`ALERTA: ${downInterfaces.length} interfaces caídas.`);
560
+ }
561
+ });
562
+
563
+ // Importante: Manejar errores de red para reconectar si se va la luz o internet
564
+ client.on('close', () => {
565
+ console.log('Conexión perdida. Reiniciando servicio en 5s...');
566
+ setTimeout(() => connectAndListen(), 5000);
567
+ });
568
+ ```
569
+
570
+
571
+ #### 7. Métricas Prometheus
572
+
573
+ ```ts
574
+ // 1. Define el mapeo: Campo Mikrotik -> Métrica Prometheus
575
+ const metricasDef = [
576
+ {
577
+ name: 'mikrotik_iface_rx_total', // Nombre final en Prometheus
578
+ help: 'Bytes recibidos', // Descripción
579
+ type: 'counter', // Tipo: 'counter' o 'gauge'
580
+ path: 'rx-byte', // Campo original en RouterOS
581
+ labels: ['name', 'type'] // Etiquetas para filtrar (ej: wan, bridge)
582
+ },
583
+ {
584
+ name: 'mikrotik_iface_tx_total',
585
+ help: 'Bytes transmitidos',
586
+ type: 'counter',
587
+ path: 'tx-byte',
588
+ labels: ['name', 'type']
589
+ }
590
+ ];
591
+
592
+ // 2. Obtén el texto formateado listo para Prometheus
593
+ // (Internamente ejecuta /interface/print y transforma los datos)
594
+ const resultado = await client.getMetrics('/interface', metricasDef);
595
+
596
+ console.log(resultado);
597
+ ```
598
+
599
+ #### 8. Transmisión en tiempo real con seguridad de tipos (onDiff)
600
+
601
+ ```ts
602
+ client.collection('/ppp/secret') // Sin <T>, se asume Record<string, any>
603
+ .onSnapshot((data) => {
604
+ // Verificar si estamos en modo Diff
605
+ if ('added' in data) {
606
+ // TypeScript infiere automáticamente que 'data' es un SnapshotDiff
607
+
608
+ // Elementos Agregados
609
+ data.added.forEach(secret => console.log('Nuevo:', secret.name));
610
+
611
+ // Elementos Modificados
612
+ data.modified.forEach(secret => console.log('Editado:', secret.name));
613
+
614
+ // Elementos Eliminados
615
+ data.removed.forEach(secret => console.log('Borrado:', secret.name));
616
+ }
617
+ else {
618
+ // Si entra aquí, es que NO se usó .onDiff() y es un Array normal
619
+ console.log('Tamaño lista completa:', data.length);
620
+ }
621
+ })
622
+ .onDiff(); // <--- IMPORTANTE: Activa el modo diferencial
623
+ ```
624
+
625
+
626
+ #### 9. Tranformadores de tipo de datos con Print
627
+
628
+ ```ts
629
+ // EJEMPLO 1: Usando .toMap() para Búsqueda Instantánea O(1)
630
+ // Escenario: Encontrar rápidamente un usuario específico por nombre sin recorrer un array.
631
+ client.command('/ppp/secret').print().then(result => {
632
+ // Transformar el array en un Diccionario (Map) indexado por 'name'
633
+ const usersMap = result.toMap('name');
634
+
635
+ // ¡Acceso instantáneo! No se requiere .find().
636
+ const john = usersMap['john_doe'];
637
+ if (john) {
638
+ console.log(`Usuario Encontrado: ${john.profile}`);
639
+ }
640
+ });
641
+
642
+
643
+ // EJEMPLO 2: Usando .toGrouped() para Reportes
644
+ // Escenario: Agrupar conexiones activas por su perfil de servicio (ej. planes de 10mb, 50mb).
645
+ client.command('/ppp/active').print().then(result => {
646
+ // Agrupar usuarios por la propiedad 'profile'
647
+ const byProfile = result.toGrouped('profile');
648
+
649
+ console.log('--- Resumen de Usuarios Conectados ---');
650
+ Object.keys(byProfile).forEach(profile => {
651
+ console.log(`Plan ${profile}: ${byProfile[profile].length} usuarios`);
652
+ });
653
+ });
654
+
655
+
656
+ // EJEMPLO 3: Usando .toPages() para Paginación
657
+ // Escenario: Mostrar una lista grande de logs en una tabla frontend, página por página.
658
+ const PAGE_SIZE = 50;
659
+ const CURRENT_PAGE = 1;
660
+
661
+ client.command('/log').print().then(result => {
662
+ // Obtener solo los elementos para la primera página
663
+ const pageData = result.toPages(CURRENT_PAGE, PAGE_SIZE);
664
+
665
+ console.log(`Mostrando ${pageData.length} logs (Página ${CURRENT_PAGE})`);
666
+ pageData.forEach(log => console.log(`[${log.time}] ${log.message}`));
667
+ });
668
+
669
+ ```
670
+
671
+
672
+ #### 10. CLI Codegen - Generación Automática de Tipos
673
+
674
+ > **OPCIÓN A: Usando archivo .env (Recomendado por Seguridad)**
675
+ > Crea un archivo .env con MIKROTIK_HOST, MIKROTIK_USER, MIKROTIK_PASS
676
+ > Ejecuta el comando:
677
+ > > npm run codegen -- -p /ppp/secret -n ISecret
678
+ >
679
+ > OPCIÓN B: Configuración Manual (Pruebas Rápidas / CI)
680
+ > PPasa los detalles de conexión directamente mediante banderas. Usa comillas dobles para contraseñas con caracteres especiales.
681
+ > >npm run codegen -- -p /ppp/secret -n ISecret --host ROUTER_IP --user admin --pass "secret123" --port 8728
682
+
683
+
684
+ ```ts
685
+ // Uso A: Lectura con Tipado Seguro (Auto-completado para propiedades)
686
+ // Pasar la interfaz <ISecret> a .collection() o .command()
687
+ client.collection<ISecret>('/ppp/secret').onSnapshot((diff) => {
688
+ if ('added' in diff) {
689
+ diff.added.forEach(secret => {
690
+ // ¡TypeScript sabe que 'profile' y 'service' existen!
691
+ // Si escribes "secret.", tu IDE mostrará todos los campos disponibles.
692
+ console.log(`Nuevo Usuario: ${secret.name} (Plan: ${secret.profile})`);
693
+ });
694
+ }
695
+ });
696
+
697
+ // Uso B: Escritura con Tipado Seguro (Validación para .add)
698
+ async function createSecureUser() {
699
+ const api = client.command<ISecret>('/ppp/secret');
700
+
701
+ // TypeScript asegura que solo pases campos válidos definidos en ISecret.
702
+ // Previene errores tipográficos como 'passwrod' o usar nombres de perfil inválidos si se generaron uniones.
703
+ await api.add({
704
+ name: 'vip_user',
705
+ password: 'secure_password',
706
+ profile: 'default',
707
+ service: 'pppoe',
708
+ disabled: false
709
+ });
710
+ }
711
+
712
+ ```
713
+
714
+
715
+ ---
716
+
717
+ ## Author's Note / Nota del Autor
718
+
719
+ **English**
720
+ >Thank you for choosing RosInterface for your automation needs. I built this library to solve the real-world challenges of managing MikroTik networks at scale. I hope it helps you build your systems.
721
+
722
+ Happy Coding!
723
+ — **McBlockier**
724
+
725
+ **Español**
726
+ > Gracias por elegir **RosInterface** para tus necesidades de automatización. Construí esta librería para resolver los desafíos reales de administrar redes MikroTik a escala. Espero te ayude mucho a construir tus sistemas .
727
+
728
+ ¡Feliz Código!
729
+ — **McBlockier**
730
+
731
+
252
732
  ---