@utilia-os/sdk-js 1.2.0 → 1.5.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.
package/README.md CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  SDK JavaScript/TypeScript para integrar aplicaciones externas con UTILIA OS.
4
4
 
5
- ## Instalacion
5
+ ## Instalación
6
6
 
7
7
  ```bash
8
8
  npm install @utilia-os/sdk-js
9
9
  ```
10
10
 
11
- ## Configuracion
11
+ ## Configuración
12
12
 
13
13
  ```typescript
14
14
  import { UtiliaSDK } from '@utilia-os/sdk-js';
@@ -29,7 +29,7 @@ Antes de crear tickets, identifica al usuario en tu sistema:
29
29
 
30
30
  ```typescript
31
31
  const user = await sdk.users.identify({
32
- externalId: 'user-123', // ID unico en tu sistema (requerido)
32
+ externalId: 'user-123', // ID único en tu sistema (requerido)
33
33
  email: 'user@example.com', // opcional
34
34
  name: 'Juan Perez', // opcional
35
35
  avatarUrl: 'https://...', // opcional
@@ -49,10 +49,10 @@ const ticket = await sdk.tickets.create({
49
49
  email: 'user@example.com',
50
50
  name: 'Juan Perez',
51
51
  },
52
- title: 'Problema con la facturacion',
52
+ title: 'Problema con la facturación',
53
53
  description: 'No puedo ver mis facturas del mes pasado...',
54
54
  category: 'PROBLEMA', // CONSULTA | PROBLEMA | SUGERENCIA | OTRO
55
- priority: 'MEDIA', // BAJA | MEDIA | ALTA | CRITICA
55
+ priority: 'MEDIA', // BAJA | MEDIA | ALTA | CRÍTICA
56
56
  context: { // opcional
57
57
  url: window.location.href,
58
58
  appVersion: '1.2.3',
@@ -104,7 +104,7 @@ await sdk.tickets.close('ticket-uuid', 'user-123');
104
104
  await sdk.tickets.reopen('ticket-uuid', 'user-123');
105
105
  ```
106
106
 
107
- ### Obtener Mensajes No Leidos
107
+ ### Obtener Mensajes No Leídos
108
108
 
109
109
  ```typescript
110
110
  const { count } = await sdk.tickets.getUnreadCount('user-123');
@@ -113,6 +113,72 @@ if (count > 0) {
113
113
  }
114
114
  ```
115
115
 
116
+ ### Actualizaciones en Tiempo Real (SSE)
117
+
118
+ Recibe notificaciones cuando un agente responde, cambia el estado, resuelve o cierra un ticket:
119
+
120
+ ```typescript
121
+ const stream = sdk.tickets.streamUpdates('user-123', {
122
+ onTicketUpdated: (event) => {
123
+ console.log(`Ticket ${event.ticketKey} actualizado: ${event.type}`);
124
+ // event.type: 'comment-added' | 'status-changed'
125
+ // Refrescar la UI del ticket
126
+ },
127
+ onError: (error) => {
128
+ console.error('Error en conexion SSE:', error);
129
+ },
130
+ });
131
+
132
+ // Para cerrar la conexion cuando ya no se necesite:
133
+ stream.close();
134
+ ```
135
+
136
+ > **Nota:** En Node.js se requiere un polyfill como `eventsource` (`npm install eventsource`).
137
+
138
+ ### Reportar Error
139
+
140
+ ```typescript
141
+ const result = await sdk.errors.report({
142
+ message: 'Error al procesar pago',
143
+ module: 'payment-processor',
144
+ severity: 'critical',
145
+ stack: error.stack,
146
+ endpoint: '/api/payments',
147
+ method: 'POST',
148
+ context: {
149
+ gatewayId: 'stripe',
150
+ orderId: 'order-456',
151
+ },
152
+ });
153
+
154
+ console.log(result.hash); // Hash único del error
155
+ console.log(result.deduplicated); // true si el error ya existía
156
+ ```
157
+
158
+ ### Listar Errores
159
+
160
+ ```typescript
161
+ const result = await sdk.errors.list({
162
+ severity: ['critical', 'high'],
163
+ resolved: false,
164
+ limit: 20,
165
+ });
166
+
167
+ console.log(result.errors);
168
+ console.log(result.pagination.total);
169
+ ```
170
+
171
+ ### Estadísticas de Errores
172
+
173
+ ```typescript
174
+ const stats = await sdk.errors.stats();
175
+
176
+ console.log(stats.total); // Total de errores
177
+ console.log(stats.unresolved); // Errores sin resolver
178
+ console.log(stats.bySeverity); // { critical: 5, high: 10, ... }
179
+ console.log(stats.byModule); // { auth: 3, payments: 7, ... }
180
+ ```
181
+
116
182
  ## Manejo de Errores
117
183
 
118
184
  ```typescript
@@ -124,13 +190,13 @@ try {
124
190
  if (error instanceof UtiliaSDKError) {
125
191
  switch (error.code) {
126
192
  case ErrorCode.UNAUTHORIZED:
127
- console.error('API Key invalida');
193
+ console.error('API Key inválida');
128
194
  break;
129
195
  case ErrorCode.RATE_LIMITED:
130
196
  console.error('Demasiadas peticiones, espera antes de reintentar');
131
197
  break;
132
198
  case ErrorCode.VALIDATION_ERROR:
133
- console.error('Datos invalidos:', error.message);
199
+ console.error('Datos inválidos:', error.message);
134
200
  break;
135
201
  case ErrorCode.NOT_FOUND:
136
202
  console.error('Recurso no encontrado');
@@ -141,7 +207,7 @@ try {
141
207
 
142
208
  // Verificar si se puede reintentar
143
209
  if (error.isRetryable()) {
144
- // Implementar logica de reintento
210
+ // Implementar lógica de reintento
145
211
  }
146
212
  }
147
213
  }
@@ -153,7 +219,7 @@ El SDK exporta todos los tipos TypeScript necesarios:
153
219
 
154
220
  ```typescript
155
221
  import type {
156
- // Configuracion
222
+ // Configuración
157
223
  UtiliaSDKConfig,
158
224
 
159
225
  // Tickets
@@ -169,6 +235,13 @@ import type {
169
235
  IdentifyUserInput,
170
236
  ExternalUser,
171
237
 
238
+ // Errores del sistema
239
+ ReportErrorInput,
240
+ ReportedError,
241
+ SystemError,
242
+ ErrorFilters,
243
+ ErrorStats,
244
+
172
245
  // Comunes
173
246
  TicketStatus,
174
247
  TicketCategory,
package/dist/index.d.mts CHANGED
@@ -85,6 +85,23 @@ declare class UtiliaClient {
85
85
  postForm<T>(url: string, formData: FormData, onProgress?: (progress: number) => void): Promise<T>;
86
86
  }
87
87
 
88
+ /**
89
+ * Tipos comunes compartidos en el SDK
90
+ */
91
+ type TicketStatus = 'OPEN' | 'IN_REVIEW' | 'RESOLVED' | 'CLOSED';
92
+ type TicketCategory = 'CONSULTA' | 'PROBLEMA' | 'SUGERENCIA' | 'OTRO';
93
+ type TicketPriority = 'BAJA' | 'MEDIA' | 'ALTA' | 'CRITICA';
94
+ interface PaginationMeta {
95
+ page: number;
96
+ limit: number;
97
+ total: number;
98
+ totalPages: number;
99
+ }
100
+ interface PaginatedResponse<T> {
101
+ data: T[];
102
+ pagination: PaginationMeta;
103
+ }
104
+
88
105
  /**
89
106
  * Tipos relacionados con archivos adjuntos
90
107
  */
@@ -130,69 +147,6 @@ interface FileQuota {
130
147
  usagePercent: number;
131
148
  }
132
149
 
133
- /**
134
- * Servicio de archivos adjuntos
135
- * Permite subir archivos para adjuntar a tickets
136
- */
137
-
138
- declare class FilesService {
139
- private readonly client;
140
- private readonly basePath;
141
- constructor(client: UtiliaClient);
142
- /**
143
- * Subir un archivo para adjuntar a tickets
144
- *
145
- * @param file - Archivo a subir (File o Blob)
146
- * @param options - Opciones de subida
147
- * @returns Archivo subido con su ID
148
- *
149
- * @example Browser
150
- * ```typescript
151
- * // Desde input file
152
- * const input = document.querySelector('input[type="file"]');
153
- * const file = input.files[0];
154
- * const uploaded = await sdk.files.upload(file);
155
- *
156
- * // Usar en ticket
157
- * await sdk.tickets.create({
158
- * ...ticketData,
159
- * attachmentIds: [uploaded.id],
160
- * });
161
- * ```
162
- */
163
- upload(file: File | Blob, options?: UploadFileOptions): Promise<UploadedFile>;
164
- /**
165
- * Obtener URL de descarga para un archivo
166
- *
167
- * @param fileId - ID del archivo
168
- * @returns URL temporal de descarga
169
- */
170
- getUrl(fileId: string): Promise<string>;
171
- /**
172
- * Obtener informacion de quota de almacenamiento
173
- *
174
- * @returns Informacion de uso y limites
175
- */
176
- getQuota(): Promise<FileQuota>;
177
- }
178
-
179
- /**
180
- * Tipos comunes compartidos en el SDK
181
- */
182
- type TicketStatus = 'OPEN' | 'IN_REVIEW' | 'RESOLVED' | 'CLOSED';
183
- type TicketCategory = 'CONSULTA' | 'PROBLEMA' | 'SUGERENCIA' | 'OTRO';
184
- type TicketPriority = 'BAJA' | 'MEDIA' | 'ALTA' | 'CRITICA';
185
- interface PaginationMeta {
186
- page: number;
187
- limit: number;
188
- total: number;
189
- totalPages: number;
190
- }
191
- interface PaginatedResponse<T> {
192
- data: T[];
193
- pagination: PaginationMeta;
194
- }
195
-
196
150
  /**
197
151
  * Tipos relacionados con tickets de soporte
198
152
  */
@@ -410,6 +364,237 @@ interface ExternalUser {
410
364
  };
411
365
  }
412
366
 
367
+ /**
368
+ * Tipos relacionados con errores del sistema
369
+ */
370
+ /**
371
+ * Nivel de severidad de un error
372
+ */
373
+ type ErrorSeverity = 'critical' | 'high' | 'medium' | 'low';
374
+ /**
375
+ * Metodo HTTP asociado a un error
376
+ */
377
+ type ErrorHttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
378
+ /**
379
+ * Datos de entrada para reportar un error
380
+ */
381
+ interface ReportErrorInput {
382
+ /** Mensaje descriptivo del error (1-2000 caracteres) */
383
+ message: string;
384
+ /** Nivel de severidad del error */
385
+ severity?: ErrorSeverity;
386
+ /** Modulo o area donde ocurrio el error (1-100 caracteres) */
387
+ module: string;
388
+ /** Stack trace del error */
389
+ stack?: string;
390
+ /** Archivo donde ocurrio el error */
391
+ file?: string;
392
+ /** Linea del archivo donde ocurrio el error */
393
+ line?: number;
394
+ /** Funcion donde ocurrio el error */
395
+ function?: string;
396
+ /** Endpoint relacionado con el error */
397
+ endpoint?: string;
398
+ /** Metodo HTTP de la peticion que causo el error */
399
+ method?: ErrorHttpMethod;
400
+ /** ID del usuario afectado */
401
+ userId?: string;
402
+ /** ID de la peticion donde ocurrio el error */
403
+ requestId?: string;
404
+ /** Contexto adicional del error */
405
+ context?: Record<string, unknown>;
406
+ }
407
+ /**
408
+ * Respuesta al reportar un error
409
+ */
410
+ interface ReportedError {
411
+ /** Indica si el error fue registrado correctamente */
412
+ success: boolean;
413
+ /** Hash unico del error (para deduplicacion) */
414
+ hash: string;
415
+ /** Indica si el error ya existia y se incremento su contador */
416
+ deduplicated: boolean;
417
+ }
418
+ /**
419
+ * Error del sistema en listados
420
+ */
421
+ interface SystemError {
422
+ /** ID unico del error */
423
+ id: string;
424
+ /** Hash de deduplicacion */
425
+ hash: string;
426
+ /** Mensaje del error */
427
+ message: string;
428
+ /** Modulo donde ocurrio */
429
+ module: string;
430
+ /** Nivel de severidad */
431
+ severity: ErrorSeverity;
432
+ /** Archivo donde ocurrio */
433
+ file?: string;
434
+ /** Linea del archivo */
435
+ line?: number;
436
+ /** Endpoint relacionado */
437
+ endpoint?: string;
438
+ /** Numero de ocurrencias */
439
+ count: number;
440
+ /** Fecha de la primera ocurrencia */
441
+ firstOccurrence: string;
442
+ /** Fecha de la ultima ocurrencia */
443
+ lastOccurrence: string;
444
+ /** Indica si el error esta resuelto */
445
+ resolved: boolean;
446
+ /** Fecha en que se resolvio */
447
+ resolvedAt?: string;
448
+ }
449
+ /**
450
+ * Filtros para listar errores
451
+ */
452
+ interface ErrorFilters {
453
+ /** Filtrar por niveles de severidad */
454
+ severity?: ErrorSeverity[];
455
+ /** Filtrar por modulo */
456
+ module?: string;
457
+ /** Filtrar por estado de resolucion */
458
+ resolved?: boolean;
459
+ /** Numero maximo de resultados (default: 50, max: 200) */
460
+ limit?: number;
461
+ /** Desplazamiento para paginacion */
462
+ offset?: number;
463
+ /** Fecha de inicio (ISO 8601) */
464
+ from?: string;
465
+ /** Fecha de fin (ISO 8601) */
466
+ to?: string;
467
+ }
468
+ /**
469
+ * Estadisticas de errores
470
+ */
471
+ interface ErrorStats {
472
+ /** Total de errores registrados */
473
+ total: number;
474
+ /** Errores sin resolver */
475
+ unresolved: number;
476
+ /** Distribucion por severidad */
477
+ bySeverity: Record<string, number>;
478
+ /** Distribucion por modulo */
479
+ byModule: Record<string, number>;
480
+ /** Errores mas frecuentes */
481
+ topErrors: Array<{
482
+ hash: string;
483
+ message: string;
484
+ count: number;
485
+ severity: string;
486
+ }>;
487
+ }
488
+
489
+ /**
490
+ * Servicio de errores del sistema
491
+ * Maneja todas las operaciones relacionadas con el reporte y consulta de errores
492
+ */
493
+
494
+ declare class ErrorsService {
495
+ private readonly client;
496
+ private readonly basePath;
497
+ constructor(client: UtiliaClient);
498
+ /**
499
+ * Reportar un error del sistema
500
+ *
501
+ * @param data - Datos del error a reportar
502
+ * @returns Resultado del reporte con hash de deduplicacion
503
+ *
504
+ * @example
505
+ * ```typescript
506
+ * const result = await sdk.errors.report({
507
+ * message: 'Error al procesar pago',
508
+ * module: 'pagos',
509
+ * severity: 'critical',
510
+ * stack: error.stack,
511
+ * endpoint: '/api/payments',
512
+ * method: 'POST',
513
+ * });
514
+ * console.log(result.hash); // Hash unico del error
515
+ * console.log(result.deduplicated); // true si ya existia
516
+ * ```
517
+ */
518
+ report(data: ReportErrorInput): Promise<ReportedError>;
519
+ /**
520
+ * Listar errores reportados por esta aplicacion
521
+ *
522
+ * @param filters - Filtros opcionales (severidad, modulo, paginacion)
523
+ * @returns Lista paginada de errores
524
+ *
525
+ * @example
526
+ * ```typescript
527
+ * const result = await sdk.errors.list({
528
+ * severity: ['critical', 'high'],
529
+ * resolved: false,
530
+ * limit: 20,
531
+ * });
532
+ * console.log(result.data); // Array de errores
533
+ * ```
534
+ */
535
+ list(filters?: ErrorFilters): Promise<PaginatedResponse<SystemError>>;
536
+ /**
537
+ * Obtener estadisticas de errores de esta aplicacion
538
+ *
539
+ * @returns Estadisticas agregadas de errores
540
+ *
541
+ * @example
542
+ * ```typescript
543
+ * const stats = await sdk.errors.stats();
544
+ * console.log(stats.total); // Total de errores
545
+ * console.log(stats.unresolved); // Errores sin resolver
546
+ * console.log(stats.bySeverity); // { critical: 5, high: 10, ... }
547
+ * ```
548
+ */
549
+ stats(): Promise<ErrorStats>;
550
+ }
551
+
552
+ /**
553
+ * Servicio de archivos adjuntos
554
+ * Permite subir archivos para adjuntar a tickets
555
+ */
556
+
557
+ declare class FilesService {
558
+ private readonly client;
559
+ private readonly basePath;
560
+ constructor(client: UtiliaClient);
561
+ /**
562
+ * Subir un archivo para adjuntar a tickets
563
+ *
564
+ * @param file - Archivo a subir (File o Blob)
565
+ * @param options - Opciones de subida
566
+ * @returns Archivo subido con su ID
567
+ *
568
+ * @example Browser
569
+ * ```typescript
570
+ * // Desde input file
571
+ * const input = document.querySelector('input[type="file"]');
572
+ * const file = input.files[0];
573
+ * const uploaded = await sdk.files.upload(file);
574
+ *
575
+ * // Usar en ticket
576
+ * await sdk.tickets.create({
577
+ * ...ticketData,
578
+ * attachmentIds: [uploaded.id],
579
+ * });
580
+ * ```
581
+ */
582
+ upload(file: File | Blob, options?: UploadFileOptions): Promise<UploadedFile>;
583
+ /**
584
+ * Obtener URL de descarga para un archivo
585
+ *
586
+ * @param fileId - ID del archivo
587
+ * @returns URL temporal de descarga
588
+ */
589
+ getUrl(fileId: string): Promise<string>;
590
+ /**
591
+ * Obtener informacion de quota de almacenamiento
592
+ *
593
+ * @returns Informacion de uso y limites
594
+ */
595
+ getQuota(): Promise<FileQuota>;
596
+ }
597
+
413
598
  /**
414
599
  * Servicio de tickets de soporte
415
600
  * Maneja todas las operaciones relacionadas con tickets
@@ -503,6 +688,40 @@ declare class TicketsService {
503
688
  * ```
504
689
  */
505
690
  addMessage(ticketId: string, userId: string, data: AddMessageInput): Promise<CreatedMessage>;
691
+ /**
692
+ * Suscribirse a actualizaciones de tickets en tiempo real via SSE.
693
+ * Recibe eventos cuando un agente responde, cambia el estado, resuelve o cierra un ticket.
694
+ *
695
+ * Requiere un entorno con EventSource nativo (navegador) o un polyfill como 'eventsource'.
696
+ *
697
+ * @param userId - ID externo del usuario (opcional, filtra eventos por usuario)
698
+ * @param options - Callbacks para eventos y errores
699
+ * @returns Objeto con metodo close() para cerrar la conexion
700
+ *
701
+ * @example
702
+ * ```typescript
703
+ * const stream = sdk.tickets.streamUpdates('user-123', {
704
+ * onTicketUpdated: (event) => {
705
+ * console.log(`Ticket ${event.ticketKey} actualizado: ${event.type}`);
706
+ * // Refrescar la UI del ticket
707
+ * },
708
+ * });
709
+ *
710
+ * // Para cerrar la conexion:
711
+ * stream.close();
712
+ * ```
713
+ */
714
+ streamUpdates(userId?: string, options?: {
715
+ onTicketUpdated?: (event: {
716
+ ticketId: string;
717
+ ticketKey: string;
718
+ type: string;
719
+ externalUserId?: string;
720
+ }) => void;
721
+ onError?: (error: Error) => void;
722
+ }): {
723
+ close: () => void;
724
+ };
506
725
  /**
507
726
  * Cerrar un ticket
508
727
  * Solo el usuario que creo el ticket puede cerrarlo
@@ -1019,9 +1238,12 @@ declare class UtiliaSDKError extends Error {
1019
1238
  readonly requestId?: string;
1020
1239
  /** Codigo de estado HTTP de la respuesta */
1021
1240
  readonly statusCode?: number;
1241
+ /** Codigo de error de negocio especifico del backend (ej: TICKET_NOT_FOUND, FILE_INVALID_MIME) */
1242
+ readonly errorCode?: string;
1022
1243
  constructor(code: ErrorCode, message: string, options?: {
1023
1244
  requestId?: string;
1024
1245
  statusCode?: number;
1246
+ errorCode?: string;
1025
1247
  });
1026
1248
  /**
1027
1249
  * Serializa el error a un objeto JSON
@@ -1029,6 +1251,7 @@ declare class UtiliaSDKError extends Error {
1029
1251
  toJSON(): {
1030
1252
  name: string;
1031
1253
  code: ErrorCode;
1254
+ errorCode: string | undefined;
1032
1255
  message: string;
1033
1256
  timestamp: string;
1034
1257
  requestId: string | undefined;
@@ -1098,12 +1321,15 @@ declare const SDK_LIMITS: {
1098
1321
  * Clase principal del SDK de UTILIA OS
1099
1322
  *
1100
1323
  * Proporciona acceso a todos los servicios disponibles:
1324
+ * - `errors`: Reporte y consulta de errores del sistema
1101
1325
  * - `files`: Operaciones con archivos adjuntos
1102
1326
  * - `tickets`: Operaciones con tickets de soporte
1103
1327
  * - `users`: Operaciones con usuarios externos
1104
1328
  * - `ai`: Funcionalidades de IA (transcripcion, sugerencias)
1105
1329
  */
1106
1330
  declare class UtiliaSDK {
1331
+ /** Servicio de errores del sistema */
1332
+ readonly errors: ErrorsService;
1107
1333
  /** Servicio de archivos adjuntos */
1108
1334
  readonly files: FilesService;
1109
1335
  /** Servicio de tickets de soporte */
@@ -1139,4 +1365,4 @@ declare class UtiliaSDK {
1139
1365
  constructor(config: UtiliaSDKConfig);
1140
1366
  }
1141
1367
 
1142
- export { type AddMessageInput, type AiJobStatus, type AiSuggestInput, type AiSuggestions, type AiUserAction, type CreateTicketInput, type CreateTicketUser, type CreatedMessage, type CreatedTicket, ErrorCode, type ExternalUser, type FileQuota, type GetSuggestionsOptions, type IdentifyUserInput, type MessageAuthor, type PaginatedResponse, type PaginationMeta, type RequestConfig, SDK_LIMITS, type SseEvent, type StreamSuggestionsHandle, type StreamSuggestionsOptions, type StreamTranscriptionOptions, type TicketAttachment, type TicketCategory, type TicketContext, type TicketDetail, type TicketFilters, type TicketListItem, type TicketMessage, type TicketPriority, type TicketReporter, type TicketStatus, type TrackAiActionInput, type TranscriptionJobStatus, type UnreadCount, type UploadFileOptions, type UploadedFile, UtiliaSDK, type UtiliaSDKConfig, UtiliaSDKError };
1368
+ export { type AddMessageInput, type AiJobStatus, type AiSuggestInput, type AiSuggestions, type AiUserAction, type CreateTicketInput, type CreateTicketUser, type CreatedMessage, type CreatedTicket, ErrorCode, type ErrorFilters, type ErrorHttpMethod, type ErrorSeverity, type ErrorStats, type ExternalUser, type FileQuota, type GetSuggestionsOptions, type IdentifyUserInput, type MessageAuthor, type PaginatedResponse, type PaginationMeta, type ReportErrorInput, type ReportedError, type RequestConfig, SDK_LIMITS, type SseEvent, type StreamSuggestionsHandle, type StreamSuggestionsOptions, type StreamTranscriptionOptions, type SystemError, type TicketAttachment, type TicketCategory, type TicketContext, type TicketDetail, type TicketFilters, type TicketListItem, type TicketMessage, type TicketPriority, type TicketReporter, type TicketStatus, type TrackAiActionInput, type TranscriptionJobStatus, type UnreadCount, type UploadFileOptions, type UploadedFile, UtiliaSDK, type UtiliaSDKConfig, UtiliaSDKError };
package/dist/index.d.ts CHANGED
@@ -85,6 +85,23 @@ declare class UtiliaClient {
85
85
  postForm<T>(url: string, formData: FormData, onProgress?: (progress: number) => void): Promise<T>;
86
86
  }
87
87
 
88
+ /**
89
+ * Tipos comunes compartidos en el SDK
90
+ */
91
+ type TicketStatus = 'OPEN' | 'IN_REVIEW' | 'RESOLVED' | 'CLOSED';
92
+ type TicketCategory = 'CONSULTA' | 'PROBLEMA' | 'SUGERENCIA' | 'OTRO';
93
+ type TicketPriority = 'BAJA' | 'MEDIA' | 'ALTA' | 'CRITICA';
94
+ interface PaginationMeta {
95
+ page: number;
96
+ limit: number;
97
+ total: number;
98
+ totalPages: number;
99
+ }
100
+ interface PaginatedResponse<T> {
101
+ data: T[];
102
+ pagination: PaginationMeta;
103
+ }
104
+
88
105
  /**
89
106
  * Tipos relacionados con archivos adjuntos
90
107
  */
@@ -130,69 +147,6 @@ interface FileQuota {
130
147
  usagePercent: number;
131
148
  }
132
149
 
133
- /**
134
- * Servicio de archivos adjuntos
135
- * Permite subir archivos para adjuntar a tickets
136
- */
137
-
138
- declare class FilesService {
139
- private readonly client;
140
- private readonly basePath;
141
- constructor(client: UtiliaClient);
142
- /**
143
- * Subir un archivo para adjuntar a tickets
144
- *
145
- * @param file - Archivo a subir (File o Blob)
146
- * @param options - Opciones de subida
147
- * @returns Archivo subido con su ID
148
- *
149
- * @example Browser
150
- * ```typescript
151
- * // Desde input file
152
- * const input = document.querySelector('input[type="file"]');
153
- * const file = input.files[0];
154
- * const uploaded = await sdk.files.upload(file);
155
- *
156
- * // Usar en ticket
157
- * await sdk.tickets.create({
158
- * ...ticketData,
159
- * attachmentIds: [uploaded.id],
160
- * });
161
- * ```
162
- */
163
- upload(file: File | Blob, options?: UploadFileOptions): Promise<UploadedFile>;
164
- /**
165
- * Obtener URL de descarga para un archivo
166
- *
167
- * @param fileId - ID del archivo
168
- * @returns URL temporal de descarga
169
- */
170
- getUrl(fileId: string): Promise<string>;
171
- /**
172
- * Obtener informacion de quota de almacenamiento
173
- *
174
- * @returns Informacion de uso y limites
175
- */
176
- getQuota(): Promise<FileQuota>;
177
- }
178
-
179
- /**
180
- * Tipos comunes compartidos en el SDK
181
- */
182
- type TicketStatus = 'OPEN' | 'IN_REVIEW' | 'RESOLVED' | 'CLOSED';
183
- type TicketCategory = 'CONSULTA' | 'PROBLEMA' | 'SUGERENCIA' | 'OTRO';
184
- type TicketPriority = 'BAJA' | 'MEDIA' | 'ALTA' | 'CRITICA';
185
- interface PaginationMeta {
186
- page: number;
187
- limit: number;
188
- total: number;
189
- totalPages: number;
190
- }
191
- interface PaginatedResponse<T> {
192
- data: T[];
193
- pagination: PaginationMeta;
194
- }
195
-
196
150
  /**
197
151
  * Tipos relacionados con tickets de soporte
198
152
  */
@@ -410,6 +364,237 @@ interface ExternalUser {
410
364
  };
411
365
  }
412
366
 
367
+ /**
368
+ * Tipos relacionados con errores del sistema
369
+ */
370
+ /**
371
+ * Nivel de severidad de un error
372
+ */
373
+ type ErrorSeverity = 'critical' | 'high' | 'medium' | 'low';
374
+ /**
375
+ * Metodo HTTP asociado a un error
376
+ */
377
+ type ErrorHttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
378
+ /**
379
+ * Datos de entrada para reportar un error
380
+ */
381
+ interface ReportErrorInput {
382
+ /** Mensaje descriptivo del error (1-2000 caracteres) */
383
+ message: string;
384
+ /** Nivel de severidad del error */
385
+ severity?: ErrorSeverity;
386
+ /** Modulo o area donde ocurrio el error (1-100 caracteres) */
387
+ module: string;
388
+ /** Stack trace del error */
389
+ stack?: string;
390
+ /** Archivo donde ocurrio el error */
391
+ file?: string;
392
+ /** Linea del archivo donde ocurrio el error */
393
+ line?: number;
394
+ /** Funcion donde ocurrio el error */
395
+ function?: string;
396
+ /** Endpoint relacionado con el error */
397
+ endpoint?: string;
398
+ /** Metodo HTTP de la peticion que causo el error */
399
+ method?: ErrorHttpMethod;
400
+ /** ID del usuario afectado */
401
+ userId?: string;
402
+ /** ID de la peticion donde ocurrio el error */
403
+ requestId?: string;
404
+ /** Contexto adicional del error */
405
+ context?: Record<string, unknown>;
406
+ }
407
+ /**
408
+ * Respuesta al reportar un error
409
+ */
410
+ interface ReportedError {
411
+ /** Indica si el error fue registrado correctamente */
412
+ success: boolean;
413
+ /** Hash unico del error (para deduplicacion) */
414
+ hash: string;
415
+ /** Indica si el error ya existia y se incremento su contador */
416
+ deduplicated: boolean;
417
+ }
418
+ /**
419
+ * Error del sistema en listados
420
+ */
421
+ interface SystemError {
422
+ /** ID unico del error */
423
+ id: string;
424
+ /** Hash de deduplicacion */
425
+ hash: string;
426
+ /** Mensaje del error */
427
+ message: string;
428
+ /** Modulo donde ocurrio */
429
+ module: string;
430
+ /** Nivel de severidad */
431
+ severity: ErrorSeverity;
432
+ /** Archivo donde ocurrio */
433
+ file?: string;
434
+ /** Linea del archivo */
435
+ line?: number;
436
+ /** Endpoint relacionado */
437
+ endpoint?: string;
438
+ /** Numero de ocurrencias */
439
+ count: number;
440
+ /** Fecha de la primera ocurrencia */
441
+ firstOccurrence: string;
442
+ /** Fecha de la ultima ocurrencia */
443
+ lastOccurrence: string;
444
+ /** Indica si el error esta resuelto */
445
+ resolved: boolean;
446
+ /** Fecha en que se resolvio */
447
+ resolvedAt?: string;
448
+ }
449
+ /**
450
+ * Filtros para listar errores
451
+ */
452
+ interface ErrorFilters {
453
+ /** Filtrar por niveles de severidad */
454
+ severity?: ErrorSeverity[];
455
+ /** Filtrar por modulo */
456
+ module?: string;
457
+ /** Filtrar por estado de resolucion */
458
+ resolved?: boolean;
459
+ /** Numero maximo de resultados (default: 50, max: 200) */
460
+ limit?: number;
461
+ /** Desplazamiento para paginacion */
462
+ offset?: number;
463
+ /** Fecha de inicio (ISO 8601) */
464
+ from?: string;
465
+ /** Fecha de fin (ISO 8601) */
466
+ to?: string;
467
+ }
468
+ /**
469
+ * Estadisticas de errores
470
+ */
471
+ interface ErrorStats {
472
+ /** Total de errores registrados */
473
+ total: number;
474
+ /** Errores sin resolver */
475
+ unresolved: number;
476
+ /** Distribucion por severidad */
477
+ bySeverity: Record<string, number>;
478
+ /** Distribucion por modulo */
479
+ byModule: Record<string, number>;
480
+ /** Errores mas frecuentes */
481
+ topErrors: Array<{
482
+ hash: string;
483
+ message: string;
484
+ count: number;
485
+ severity: string;
486
+ }>;
487
+ }
488
+
489
+ /**
490
+ * Servicio de errores del sistema
491
+ * Maneja todas las operaciones relacionadas con el reporte y consulta de errores
492
+ */
493
+
494
+ declare class ErrorsService {
495
+ private readonly client;
496
+ private readonly basePath;
497
+ constructor(client: UtiliaClient);
498
+ /**
499
+ * Reportar un error del sistema
500
+ *
501
+ * @param data - Datos del error a reportar
502
+ * @returns Resultado del reporte con hash de deduplicacion
503
+ *
504
+ * @example
505
+ * ```typescript
506
+ * const result = await sdk.errors.report({
507
+ * message: 'Error al procesar pago',
508
+ * module: 'pagos',
509
+ * severity: 'critical',
510
+ * stack: error.stack,
511
+ * endpoint: '/api/payments',
512
+ * method: 'POST',
513
+ * });
514
+ * console.log(result.hash); // Hash unico del error
515
+ * console.log(result.deduplicated); // true si ya existia
516
+ * ```
517
+ */
518
+ report(data: ReportErrorInput): Promise<ReportedError>;
519
+ /**
520
+ * Listar errores reportados por esta aplicacion
521
+ *
522
+ * @param filters - Filtros opcionales (severidad, modulo, paginacion)
523
+ * @returns Lista paginada de errores
524
+ *
525
+ * @example
526
+ * ```typescript
527
+ * const result = await sdk.errors.list({
528
+ * severity: ['critical', 'high'],
529
+ * resolved: false,
530
+ * limit: 20,
531
+ * });
532
+ * console.log(result.data); // Array de errores
533
+ * ```
534
+ */
535
+ list(filters?: ErrorFilters): Promise<PaginatedResponse<SystemError>>;
536
+ /**
537
+ * Obtener estadisticas de errores de esta aplicacion
538
+ *
539
+ * @returns Estadisticas agregadas de errores
540
+ *
541
+ * @example
542
+ * ```typescript
543
+ * const stats = await sdk.errors.stats();
544
+ * console.log(stats.total); // Total de errores
545
+ * console.log(stats.unresolved); // Errores sin resolver
546
+ * console.log(stats.bySeverity); // { critical: 5, high: 10, ... }
547
+ * ```
548
+ */
549
+ stats(): Promise<ErrorStats>;
550
+ }
551
+
552
+ /**
553
+ * Servicio de archivos adjuntos
554
+ * Permite subir archivos para adjuntar a tickets
555
+ */
556
+
557
+ declare class FilesService {
558
+ private readonly client;
559
+ private readonly basePath;
560
+ constructor(client: UtiliaClient);
561
+ /**
562
+ * Subir un archivo para adjuntar a tickets
563
+ *
564
+ * @param file - Archivo a subir (File o Blob)
565
+ * @param options - Opciones de subida
566
+ * @returns Archivo subido con su ID
567
+ *
568
+ * @example Browser
569
+ * ```typescript
570
+ * // Desde input file
571
+ * const input = document.querySelector('input[type="file"]');
572
+ * const file = input.files[0];
573
+ * const uploaded = await sdk.files.upload(file);
574
+ *
575
+ * // Usar en ticket
576
+ * await sdk.tickets.create({
577
+ * ...ticketData,
578
+ * attachmentIds: [uploaded.id],
579
+ * });
580
+ * ```
581
+ */
582
+ upload(file: File | Blob, options?: UploadFileOptions): Promise<UploadedFile>;
583
+ /**
584
+ * Obtener URL de descarga para un archivo
585
+ *
586
+ * @param fileId - ID del archivo
587
+ * @returns URL temporal de descarga
588
+ */
589
+ getUrl(fileId: string): Promise<string>;
590
+ /**
591
+ * Obtener informacion de quota de almacenamiento
592
+ *
593
+ * @returns Informacion de uso y limites
594
+ */
595
+ getQuota(): Promise<FileQuota>;
596
+ }
597
+
413
598
  /**
414
599
  * Servicio de tickets de soporte
415
600
  * Maneja todas las operaciones relacionadas con tickets
@@ -503,6 +688,40 @@ declare class TicketsService {
503
688
  * ```
504
689
  */
505
690
  addMessage(ticketId: string, userId: string, data: AddMessageInput): Promise<CreatedMessage>;
691
+ /**
692
+ * Suscribirse a actualizaciones de tickets en tiempo real via SSE.
693
+ * Recibe eventos cuando un agente responde, cambia el estado, resuelve o cierra un ticket.
694
+ *
695
+ * Requiere un entorno con EventSource nativo (navegador) o un polyfill como 'eventsource'.
696
+ *
697
+ * @param userId - ID externo del usuario (opcional, filtra eventos por usuario)
698
+ * @param options - Callbacks para eventos y errores
699
+ * @returns Objeto con metodo close() para cerrar la conexion
700
+ *
701
+ * @example
702
+ * ```typescript
703
+ * const stream = sdk.tickets.streamUpdates('user-123', {
704
+ * onTicketUpdated: (event) => {
705
+ * console.log(`Ticket ${event.ticketKey} actualizado: ${event.type}`);
706
+ * // Refrescar la UI del ticket
707
+ * },
708
+ * });
709
+ *
710
+ * // Para cerrar la conexion:
711
+ * stream.close();
712
+ * ```
713
+ */
714
+ streamUpdates(userId?: string, options?: {
715
+ onTicketUpdated?: (event: {
716
+ ticketId: string;
717
+ ticketKey: string;
718
+ type: string;
719
+ externalUserId?: string;
720
+ }) => void;
721
+ onError?: (error: Error) => void;
722
+ }): {
723
+ close: () => void;
724
+ };
506
725
  /**
507
726
  * Cerrar un ticket
508
727
  * Solo el usuario que creo el ticket puede cerrarlo
@@ -1019,9 +1238,12 @@ declare class UtiliaSDKError extends Error {
1019
1238
  readonly requestId?: string;
1020
1239
  /** Codigo de estado HTTP de la respuesta */
1021
1240
  readonly statusCode?: number;
1241
+ /** Codigo de error de negocio especifico del backend (ej: TICKET_NOT_FOUND, FILE_INVALID_MIME) */
1242
+ readonly errorCode?: string;
1022
1243
  constructor(code: ErrorCode, message: string, options?: {
1023
1244
  requestId?: string;
1024
1245
  statusCode?: number;
1246
+ errorCode?: string;
1025
1247
  });
1026
1248
  /**
1027
1249
  * Serializa el error a un objeto JSON
@@ -1029,6 +1251,7 @@ declare class UtiliaSDKError extends Error {
1029
1251
  toJSON(): {
1030
1252
  name: string;
1031
1253
  code: ErrorCode;
1254
+ errorCode: string | undefined;
1032
1255
  message: string;
1033
1256
  timestamp: string;
1034
1257
  requestId: string | undefined;
@@ -1098,12 +1321,15 @@ declare const SDK_LIMITS: {
1098
1321
  * Clase principal del SDK de UTILIA OS
1099
1322
  *
1100
1323
  * Proporciona acceso a todos los servicios disponibles:
1324
+ * - `errors`: Reporte y consulta de errores del sistema
1101
1325
  * - `files`: Operaciones con archivos adjuntos
1102
1326
  * - `tickets`: Operaciones con tickets de soporte
1103
1327
  * - `users`: Operaciones con usuarios externos
1104
1328
  * - `ai`: Funcionalidades de IA (transcripcion, sugerencias)
1105
1329
  */
1106
1330
  declare class UtiliaSDK {
1331
+ /** Servicio de errores del sistema */
1332
+ readonly errors: ErrorsService;
1107
1333
  /** Servicio de archivos adjuntos */
1108
1334
  readonly files: FilesService;
1109
1335
  /** Servicio de tickets de soporte */
@@ -1139,4 +1365,4 @@ declare class UtiliaSDK {
1139
1365
  constructor(config: UtiliaSDKConfig);
1140
1366
  }
1141
1367
 
1142
- export { type AddMessageInput, type AiJobStatus, type AiSuggestInput, type AiSuggestions, type AiUserAction, type CreateTicketInput, type CreateTicketUser, type CreatedMessage, type CreatedTicket, ErrorCode, type ExternalUser, type FileQuota, type GetSuggestionsOptions, type IdentifyUserInput, type MessageAuthor, type PaginatedResponse, type PaginationMeta, type RequestConfig, SDK_LIMITS, type SseEvent, type StreamSuggestionsHandle, type StreamSuggestionsOptions, type StreamTranscriptionOptions, type TicketAttachment, type TicketCategory, type TicketContext, type TicketDetail, type TicketFilters, type TicketListItem, type TicketMessage, type TicketPriority, type TicketReporter, type TicketStatus, type TrackAiActionInput, type TranscriptionJobStatus, type UnreadCount, type UploadFileOptions, type UploadedFile, UtiliaSDK, type UtiliaSDKConfig, UtiliaSDKError };
1368
+ export { type AddMessageInput, type AiJobStatus, type AiSuggestInput, type AiSuggestions, type AiUserAction, type CreateTicketInput, type CreateTicketUser, type CreatedMessage, type CreatedTicket, ErrorCode, type ErrorFilters, type ErrorHttpMethod, type ErrorSeverity, type ErrorStats, type ExternalUser, type FileQuota, type GetSuggestionsOptions, type IdentifyUserInput, type MessageAuthor, type PaginatedResponse, type PaginationMeta, type ReportErrorInput, type ReportedError, type RequestConfig, SDK_LIMITS, type SseEvent, type StreamSuggestionsHandle, type StreamSuggestionsOptions, type StreamTranscriptionOptions, type SystemError, type TicketAttachment, type TicketCategory, type TicketContext, type TicketDetail, type TicketFilters, type TicketListItem, type TicketMessage, type TicketPriority, type TicketReporter, type TicketStatus, type TrackAiActionInput, type TranscriptionJobStatus, type UnreadCount, type UploadFileOptions, type UploadedFile, UtiliaSDK, type UtiliaSDKConfig, UtiliaSDKError };
package/dist/index.js CHANGED
@@ -59,6 +59,7 @@ var UtiliaSDKError = class _UtiliaSDKError extends Error {
59
59
  this.timestamp = /* @__PURE__ */ new Date();
60
60
  this.requestId = options?.requestId;
61
61
  this.statusCode = options?.statusCode;
62
+ this.errorCode = options?.errorCode;
62
63
  if (Error.captureStackTrace) {
63
64
  Error.captureStackTrace(this, _UtiliaSDKError);
64
65
  }
@@ -70,6 +71,7 @@ var UtiliaSDKError = class _UtiliaSDKError extends Error {
70
71
  return {
71
72
  name: this.name,
72
73
  code: this.code,
74
+ errorCode: this.errorCode,
73
75
  message: this.message,
74
76
  timestamp: this.timestamp.toISOString(),
75
77
  requestId: this.requestId,
@@ -146,7 +148,9 @@ var UtiliaClient = class {
146
148
  toSDKError(error) {
147
149
  const requestId = error.response?.headers?.["x-request-id"] || error.config?._requestId;
148
150
  const statusCode = error.response?.status;
149
- const errorOptions = { requestId, statusCode };
151
+ const data = error.response?.data;
152
+ const errorCode = data?.errorCode;
153
+ const errorOptions = { requestId, statusCode, errorCode };
150
154
  if (!error.response) {
151
155
  if (error.code === "ECONNABORTED") {
152
156
  return new UtiliaSDKError("NETWORK_ERROR" /* NETWORK_ERROR */, "Timeout: la solicitud excedio el tiempo limite", errorOptions);
@@ -154,7 +158,6 @@ var UtiliaClient = class {
154
158
  return new UtiliaSDKError("NETWORK_ERROR" /* NETWORK_ERROR */, "Error de conexion: no se pudo conectar con el servidor", errorOptions);
155
159
  }
156
160
  const status = error.response.status;
157
- const data = error.response.data;
158
161
  const message = data?.message || data?.error || error.message;
159
162
  switch (status) {
160
163
  case 401:
@@ -317,18 +320,6 @@ var UtiliaClient = class {
317
320
  }
318
321
  };
319
322
 
320
- // src/constants.ts
321
- var SDK_LIMITS = {
322
- TICKET_TITLE_MIN: 5,
323
- TICKET_TITLE_MAX: 500,
324
- TICKET_DESCRIPTION_MIN: 10,
325
- MESSAGE_CONTENT_MIN: 1,
326
- MESSAGE_CONTENT_MAX: 5e3,
327
- FILE_NAME_MAX: 255,
328
- EXTERNAL_ID_MAX: 255,
329
- AVATAR_URL_MAX: 2048
330
- };
331
-
332
323
  // src/utils/validators.ts
333
324
  function validateEmail(email) {
334
325
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
@@ -371,6 +362,88 @@ function validateRequired(value, fieldName) {
371
362
  }
372
363
  }
373
364
 
365
+ // src/services/errors.service.ts
366
+ var ErrorsService = class {
367
+ constructor(client) {
368
+ this.basePath = "/external/v1/errors";
369
+ this.client = client;
370
+ }
371
+ /**
372
+ * Reportar un error del sistema
373
+ *
374
+ * @param data - Datos del error a reportar
375
+ * @returns Resultado del reporte con hash de deduplicacion
376
+ *
377
+ * @example
378
+ * ```typescript
379
+ * const result = await sdk.errors.report({
380
+ * message: 'Error al procesar pago',
381
+ * module: 'pagos',
382
+ * severity: 'critical',
383
+ * stack: error.stack,
384
+ * endpoint: '/api/payments',
385
+ * method: 'POST',
386
+ * });
387
+ * console.log(result.hash); // Hash unico del error
388
+ * console.log(result.deduplicated); // true si ya existia
389
+ * ```
390
+ */
391
+ async report(data) {
392
+ validateRequired(data.message, "message");
393
+ validateRequired(data.module, "module");
394
+ return this.client.post(`${this.basePath}/report`, data);
395
+ }
396
+ /**
397
+ * Listar errores reportados por esta aplicacion
398
+ *
399
+ * @param filters - Filtros opcionales (severidad, modulo, paginacion)
400
+ * @returns Lista paginada de errores
401
+ *
402
+ * @example
403
+ * ```typescript
404
+ * const result = await sdk.errors.list({
405
+ * severity: ['critical', 'high'],
406
+ * resolved: false,
407
+ * limit: 20,
408
+ * });
409
+ * console.log(result.data); // Array de errores
410
+ * ```
411
+ */
412
+ async list(filters) {
413
+ return this.client.get(this.basePath, {
414
+ params: filters ? { ...filters } : void 0
415
+ });
416
+ }
417
+ /**
418
+ * Obtener estadisticas de errores de esta aplicacion
419
+ *
420
+ * @returns Estadisticas agregadas de errores
421
+ *
422
+ * @example
423
+ * ```typescript
424
+ * const stats = await sdk.errors.stats();
425
+ * console.log(stats.total); // Total de errores
426
+ * console.log(stats.unresolved); // Errores sin resolver
427
+ * console.log(stats.bySeverity); // { critical: 5, high: 10, ... }
428
+ * ```
429
+ */
430
+ async stats() {
431
+ return this.client.get(`${this.basePath}/stats`);
432
+ }
433
+ };
434
+
435
+ // src/constants.ts
436
+ var SDK_LIMITS = {
437
+ TICKET_TITLE_MIN: 5,
438
+ TICKET_TITLE_MAX: 500,
439
+ TICKET_DESCRIPTION_MIN: 10,
440
+ MESSAGE_CONTENT_MIN: 1,
441
+ MESSAGE_CONTENT_MAX: 5e3,
442
+ FILE_NAME_MAX: 255,
443
+ EXTERNAL_ID_MAX: 255,
444
+ AVATAR_URL_MAX: 2048
445
+ };
446
+
374
447
  // src/services/files.service.ts
375
448
  var FilesService = class {
376
449
  constructor(client) {
@@ -562,6 +635,54 @@ var TicketsService = class {
562
635
  { params: { userId } }
563
636
  );
564
637
  }
638
+ /**
639
+ * Suscribirse a actualizaciones de tickets en tiempo real via SSE.
640
+ * Recibe eventos cuando un agente responde, cambia el estado, resuelve o cierra un ticket.
641
+ *
642
+ * Requiere un entorno con EventSource nativo (navegador) o un polyfill como 'eventsource'.
643
+ *
644
+ * @param userId - ID externo del usuario (opcional, filtra eventos por usuario)
645
+ * @param options - Callbacks para eventos y errores
646
+ * @returns Objeto con metodo close() para cerrar la conexion
647
+ *
648
+ * @example
649
+ * ```typescript
650
+ * const stream = sdk.tickets.streamUpdates('user-123', {
651
+ * onTicketUpdated: (event) => {
652
+ * console.log(`Ticket ${event.ticketKey} actualizado: ${event.type}`);
653
+ * // Refrescar la UI del ticket
654
+ * },
655
+ * });
656
+ *
657
+ * // Para cerrar la conexion:
658
+ * stream.close();
659
+ * ```
660
+ */
661
+ streamUpdates(userId, options) {
662
+ if (typeof EventSource === "undefined") {
663
+ throw new Error(
664
+ 'EventSource no esta disponible en este entorno. Para Node.js, instala un polyfill como "eventsource" y asignalo a globalThis.EventSource.'
665
+ );
666
+ }
667
+ const queryParams = userId ? `?userId=${encodeURIComponent(userId)}` : "";
668
+ const sseUrl = this.client.buildSseUrl(`${this.basePath}/events${queryParams}`);
669
+ const eventSource = new EventSource(sseUrl);
670
+ eventSource.onmessage = (event) => {
671
+ try {
672
+ const data = JSON.parse(event.data);
673
+ if (data.event === "ticket-updated" && options?.onTicketUpdated) {
674
+ options.onTicketUpdated(data);
675
+ }
676
+ } catch {
677
+ }
678
+ };
679
+ eventSource.onerror = () => {
680
+ options?.onError?.(new Error("Error en la conexion SSE"));
681
+ };
682
+ return {
683
+ close: () => eventSource.close()
684
+ };
685
+ }
565
686
  /**
566
687
  * Cerrar un ticket
567
688
  * Solo el usuario que creo el ticket puede cerrarlo
@@ -1090,6 +1211,7 @@ var UtiliaSDK = class {
1090
1211
  */
1091
1212
  constructor(config) {
1092
1213
  const client = new UtiliaClient(config);
1214
+ this.errors = new ErrorsService(client);
1093
1215
  this.files = new FilesService(client);
1094
1216
  this.tickets = new TicketsService(client);
1095
1217
  this.users = new UsersService(client);
package/dist/index.mjs CHANGED
@@ -20,6 +20,7 @@ var UtiliaSDKError = class _UtiliaSDKError extends Error {
20
20
  this.timestamp = /* @__PURE__ */ new Date();
21
21
  this.requestId = options?.requestId;
22
22
  this.statusCode = options?.statusCode;
23
+ this.errorCode = options?.errorCode;
23
24
  if (Error.captureStackTrace) {
24
25
  Error.captureStackTrace(this, _UtiliaSDKError);
25
26
  }
@@ -31,6 +32,7 @@ var UtiliaSDKError = class _UtiliaSDKError extends Error {
31
32
  return {
32
33
  name: this.name,
33
34
  code: this.code,
35
+ errorCode: this.errorCode,
34
36
  message: this.message,
35
37
  timestamp: this.timestamp.toISOString(),
36
38
  requestId: this.requestId,
@@ -107,7 +109,9 @@ var UtiliaClient = class {
107
109
  toSDKError(error) {
108
110
  const requestId = error.response?.headers?.["x-request-id"] || error.config?._requestId;
109
111
  const statusCode = error.response?.status;
110
- const errorOptions = { requestId, statusCode };
112
+ const data = error.response?.data;
113
+ const errorCode = data?.errorCode;
114
+ const errorOptions = { requestId, statusCode, errorCode };
111
115
  if (!error.response) {
112
116
  if (error.code === "ECONNABORTED") {
113
117
  return new UtiliaSDKError("NETWORK_ERROR" /* NETWORK_ERROR */, "Timeout: la solicitud excedio el tiempo limite", errorOptions);
@@ -115,7 +119,6 @@ var UtiliaClient = class {
115
119
  return new UtiliaSDKError("NETWORK_ERROR" /* NETWORK_ERROR */, "Error de conexion: no se pudo conectar con el servidor", errorOptions);
116
120
  }
117
121
  const status = error.response.status;
118
- const data = error.response.data;
119
122
  const message = data?.message || data?.error || error.message;
120
123
  switch (status) {
121
124
  case 401:
@@ -278,18 +281,6 @@ var UtiliaClient = class {
278
281
  }
279
282
  };
280
283
 
281
- // src/constants.ts
282
- var SDK_LIMITS = {
283
- TICKET_TITLE_MIN: 5,
284
- TICKET_TITLE_MAX: 500,
285
- TICKET_DESCRIPTION_MIN: 10,
286
- MESSAGE_CONTENT_MIN: 1,
287
- MESSAGE_CONTENT_MAX: 5e3,
288
- FILE_NAME_MAX: 255,
289
- EXTERNAL_ID_MAX: 255,
290
- AVATAR_URL_MAX: 2048
291
- };
292
-
293
284
  // src/utils/validators.ts
294
285
  function validateEmail(email) {
295
286
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
@@ -332,6 +323,88 @@ function validateRequired(value, fieldName) {
332
323
  }
333
324
  }
334
325
 
326
+ // src/services/errors.service.ts
327
+ var ErrorsService = class {
328
+ constructor(client) {
329
+ this.basePath = "/external/v1/errors";
330
+ this.client = client;
331
+ }
332
+ /**
333
+ * Reportar un error del sistema
334
+ *
335
+ * @param data - Datos del error a reportar
336
+ * @returns Resultado del reporte con hash de deduplicacion
337
+ *
338
+ * @example
339
+ * ```typescript
340
+ * const result = await sdk.errors.report({
341
+ * message: 'Error al procesar pago',
342
+ * module: 'pagos',
343
+ * severity: 'critical',
344
+ * stack: error.stack,
345
+ * endpoint: '/api/payments',
346
+ * method: 'POST',
347
+ * });
348
+ * console.log(result.hash); // Hash unico del error
349
+ * console.log(result.deduplicated); // true si ya existia
350
+ * ```
351
+ */
352
+ async report(data) {
353
+ validateRequired(data.message, "message");
354
+ validateRequired(data.module, "module");
355
+ return this.client.post(`${this.basePath}/report`, data);
356
+ }
357
+ /**
358
+ * Listar errores reportados por esta aplicacion
359
+ *
360
+ * @param filters - Filtros opcionales (severidad, modulo, paginacion)
361
+ * @returns Lista paginada de errores
362
+ *
363
+ * @example
364
+ * ```typescript
365
+ * const result = await sdk.errors.list({
366
+ * severity: ['critical', 'high'],
367
+ * resolved: false,
368
+ * limit: 20,
369
+ * });
370
+ * console.log(result.data); // Array de errores
371
+ * ```
372
+ */
373
+ async list(filters) {
374
+ return this.client.get(this.basePath, {
375
+ params: filters ? { ...filters } : void 0
376
+ });
377
+ }
378
+ /**
379
+ * Obtener estadisticas de errores de esta aplicacion
380
+ *
381
+ * @returns Estadisticas agregadas de errores
382
+ *
383
+ * @example
384
+ * ```typescript
385
+ * const stats = await sdk.errors.stats();
386
+ * console.log(stats.total); // Total de errores
387
+ * console.log(stats.unresolved); // Errores sin resolver
388
+ * console.log(stats.bySeverity); // { critical: 5, high: 10, ... }
389
+ * ```
390
+ */
391
+ async stats() {
392
+ return this.client.get(`${this.basePath}/stats`);
393
+ }
394
+ };
395
+
396
+ // src/constants.ts
397
+ var SDK_LIMITS = {
398
+ TICKET_TITLE_MIN: 5,
399
+ TICKET_TITLE_MAX: 500,
400
+ TICKET_DESCRIPTION_MIN: 10,
401
+ MESSAGE_CONTENT_MIN: 1,
402
+ MESSAGE_CONTENT_MAX: 5e3,
403
+ FILE_NAME_MAX: 255,
404
+ EXTERNAL_ID_MAX: 255,
405
+ AVATAR_URL_MAX: 2048
406
+ };
407
+
335
408
  // src/services/files.service.ts
336
409
  var FilesService = class {
337
410
  constructor(client) {
@@ -523,6 +596,54 @@ var TicketsService = class {
523
596
  { params: { userId } }
524
597
  );
525
598
  }
599
+ /**
600
+ * Suscribirse a actualizaciones de tickets en tiempo real via SSE.
601
+ * Recibe eventos cuando un agente responde, cambia el estado, resuelve o cierra un ticket.
602
+ *
603
+ * Requiere un entorno con EventSource nativo (navegador) o un polyfill como 'eventsource'.
604
+ *
605
+ * @param userId - ID externo del usuario (opcional, filtra eventos por usuario)
606
+ * @param options - Callbacks para eventos y errores
607
+ * @returns Objeto con metodo close() para cerrar la conexion
608
+ *
609
+ * @example
610
+ * ```typescript
611
+ * const stream = sdk.tickets.streamUpdates('user-123', {
612
+ * onTicketUpdated: (event) => {
613
+ * console.log(`Ticket ${event.ticketKey} actualizado: ${event.type}`);
614
+ * // Refrescar la UI del ticket
615
+ * },
616
+ * });
617
+ *
618
+ * // Para cerrar la conexion:
619
+ * stream.close();
620
+ * ```
621
+ */
622
+ streamUpdates(userId, options) {
623
+ if (typeof EventSource === "undefined") {
624
+ throw new Error(
625
+ 'EventSource no esta disponible en este entorno. Para Node.js, instala un polyfill como "eventsource" y asignalo a globalThis.EventSource.'
626
+ );
627
+ }
628
+ const queryParams = userId ? `?userId=${encodeURIComponent(userId)}` : "";
629
+ const sseUrl = this.client.buildSseUrl(`${this.basePath}/events${queryParams}`);
630
+ const eventSource = new EventSource(sseUrl);
631
+ eventSource.onmessage = (event) => {
632
+ try {
633
+ const data = JSON.parse(event.data);
634
+ if (data.event === "ticket-updated" && options?.onTicketUpdated) {
635
+ options.onTicketUpdated(data);
636
+ }
637
+ } catch {
638
+ }
639
+ };
640
+ eventSource.onerror = () => {
641
+ options?.onError?.(new Error("Error en la conexion SSE"));
642
+ };
643
+ return {
644
+ close: () => eventSource.close()
645
+ };
646
+ }
526
647
  /**
527
648
  * Cerrar un ticket
528
649
  * Solo el usuario que creo el ticket puede cerrarlo
@@ -1051,6 +1172,7 @@ var UtiliaSDK = class {
1051
1172
  */
1052
1173
  constructor(config) {
1053
1174
  const client = new UtiliaClient(config);
1175
+ this.errors = new ErrorsService(client);
1054
1176
  this.files = new FilesService(client);
1055
1177
  this.tickets = new TicketsService(client);
1056
1178
  this.users = new UsersService(client);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utilia-os/sdk-js",
3
- "version": "1.2.0",
3
+ "version": "1.5.0",
4
4
  "description": "SDK JavaScript/TypeScript para UTILIA OS External Integrations",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",