react-native-qalink 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +342 -0
  2. package/dist/core/classifier.d.ts +9 -0
  3. package/dist/core/classifier.d.ts.map +1 -0
  4. package/dist/core/classifier.js +51 -0
  5. package/dist/core/classifier.js.map +1 -0
  6. package/dist/core/session.d.ts +8 -0
  7. package/dist/core/session.d.ts.map +1 -0
  8. package/dist/core/session.js +60 -0
  9. package/dist/core/session.js.map +1 -0
  10. package/dist/index.d.ts +58 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +171 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/interceptors/axios.d.ts +4 -0
  15. package/dist/interceptors/axios.d.ts.map +1 -0
  16. package/dist/interceptors/axios.js +78 -0
  17. package/dist/interceptors/axios.js.map +1 -0
  18. package/dist/interceptors/console.d.ts +4 -0
  19. package/dist/interceptors/console.d.ts.map +1 -0
  20. package/dist/interceptors/console.js +159 -0
  21. package/dist/interceptors/console.js.map +1 -0
  22. package/dist/interceptors/errors.d.ts +4 -0
  23. package/dist/interceptors/errors.d.ts.map +1 -0
  24. package/dist/interceptors/errors.js +49 -0
  25. package/dist/interceptors/errors.js.map +1 -0
  26. package/dist/interceptors/fetch.d.ts +4 -0
  27. package/dist/interceptors/fetch.d.ts.map +1 -0
  28. package/dist/interceptors/fetch.js +83 -0
  29. package/dist/interceptors/fetch.js.map +1 -0
  30. package/dist/interceptors/runtime.d.ts +4 -0
  31. package/dist/interceptors/runtime.d.ts.map +1 -0
  32. package/dist/interceptors/runtime.js +141 -0
  33. package/dist/interceptors/runtime.js.map +1 -0
  34. package/dist/transport/websocket.d.ts +23 -0
  35. package/dist/transport/websocket.d.ts.map +1 -0
  36. package/dist/transport/websocket.js +104 -0
  37. package/dist/transport/websocket.js.map +1 -0
  38. package/dist/types/index.d.ts +139 -0
  39. package/dist/types/index.d.ts.map +1 -0
  40. package/dist/types/index.js +3 -0
  41. package/dist/types/index.js.map +1 -0
  42. package/package.json +36 -0
package/README.md ADDED
@@ -0,0 +1,342 @@
1
+ # react-native-qalink 🔍
2
+
3
+ SDK de captura de errores en tiempo real para React Native. Se instala en la app y transmite al servidor QALink cada request de red, error JS, log de consola y error del runtime — permitiendo al equipo de QA ver en tiempo real si un bug es del **frontend** o del **backend**, sin necesidad de Android Studio ni logs técnicos.
4
+
5
+ ---
6
+
7
+ ## ¿Cómo identifica los requests a la API?
8
+
9
+ La librería usa **dos estrategias de interceptación simultáneas**:
10
+
11
+ ### 1. Monkey-patch de `fetch` global
12
+ Al llamar `QALink.init()`, la librería reemplaza el `fetch` nativo del entorno JavaScript por su propia versión instrumentada. Cualquier llamada a `fetch(...)` en toda la app pasa primero por QALink, que registra el request y la respuesta antes de devolver el control al código original.
13
+
14
+ ```
15
+ Tu código → fetch('/api/users')
16
+
17
+ QALink intercepta → registra método, URL, tiempo de inicio
18
+
19
+ Ejecuta fetch real → espera respuesta del servidor
20
+
21
+ QALink registra → status code, body, duración, clasificación
22
+
23
+ Devuelve al código → response (sin modificarla)
24
+ ```
25
+
26
+ ### 2. Interceptores de Axios
27
+ Axios expone un sistema oficial de interceptores (`.interceptors.request` y `.interceptors.response`). QALink se engancha en ambos para capturar cada request antes de que salga y cada respuesta antes de que llegue al código de la app. Funciona con la instancia global y con instancias personalizadas.
28
+
29
+ ---
30
+
31
+ ## ¿Cómo captura los logs de Metro y el runtime de React Native?
32
+
33
+ Hay tres capas adicionales que se activan automáticamente con `init()`:
34
+
35
+ ### 3. Monkey-patch de `console`
36
+ QALink reemplaza `console.log`, `console.warn` y `console.error` con versiones instrumentadas. **Metro sigue mostrando todo normalmente** (se llama al original primero), pero cada mensaje se envía también al dashboard. Los mensajes se clasifican automáticamente:
37
+
38
+ ```
39
+ console.log('Productos cargados', data) → 🔵 user_log
40
+ console.warn('componentWillMount deprecated') → 🟡 rn_warning
41
+ console.error('Invariant Violation: ...') → 🔴 rn_error
42
+ console.error('Unable to resolve module') → 🔴 rn_error (Metro)
43
+ ```
44
+
45
+ ### 4. `ErrorUtils.setGlobalHandler` — Pantalla roja (Red Screen)
46
+ React Native expone `ErrorUtils` para capturar errores **antes** de que la app crashee y muestre la pantalla roja. QALink se registra ahí para enviar el error al dashboard con todo el stack trace, y luego llama al handler original para que RN siga comportándose normalmente.
47
+
48
+ ```
49
+ Error fatal en JS
50
+
51
+ QALink captura → envía al dashboard con stack trace completo
52
+
53
+ ErrorUtils original → React Native muestra la pantalla roja
54
+ ```
55
+
56
+ ### 5. LogBox — Cajas amarillas (Yellow Box)
57
+ En React Native ≥ 0.64, los warnings se manejan internamente via `LogBox`. QALink se engancha al método interno `__warn` de LogBox para capturar cada warning antes de que aparezca en pantalla.
58
+
59
+ ### Clasificación de errores de runtime
60
+
61
+ | Tipo | Categoría | Ejemplo |
62
+ |--------------------------|----------------|----------------------------------------------|
63
+ | Error fatal | `red_screen` | `Invariant Violation`, `undefined is not...` |
64
+ | Warning de RN | `yellow_box` | `componentWillMount is deprecated` |
65
+ | Módulo nativo | `native_module`| `NativeModule X is null` |
66
+ | Bridge JS↔Native | `bridge` | `RCTBridge error` |
67
+ | Promise sin .catch() | `unknown` | `Unhandled Promise Rejection` |
68
+ | Error de Metro/bundler | `rn_error` | `Unable to resolve module`, `SyntaxError` |
69
+
70
+ ---
71
+
72
+ ## Instalación
73
+
74
+ ```bash
75
+ npm install react-native-qalink
76
+ # o
77
+ yarn add react-native-qalink
78
+ ```
79
+
80
+ ---
81
+
82
+ ## Ejemplos de uso
83
+
84
+ ### Ejemplo 1 — Setup básico con captura de consola
85
+
86
+ ```tsx
87
+ // App.tsx
88
+ import { useEffect } from 'react';
89
+ import { QALink } from 'react-native-qalink';
90
+ import axios from 'axios';
91
+
92
+ export default function App() {
93
+ useEffect(() => {
94
+ QALink.init({
95
+ serverUrl: 'ws://192.168.1.100:3000',
96
+ appVersion: '1.2.3',
97
+ captureRuntimeErrors: true, // pantalla roja, yellow box
98
+ console: {
99
+ captureLogs: true, // console.log
100
+ captureWarnings: true, // console.warn
101
+ captureErrors: true, // console.error + errores de Metro
102
+ },
103
+ debug: __DEV__,
104
+ });
105
+
106
+ QALink.interceptAxios(axios);
107
+ }, []);
108
+
109
+ return <RootNavigator />;
110
+ }
111
+ ```
112
+
113
+ ---
114
+
115
+ ### Ejemplo 2 — Filtrar logs muy verbosos de librerías
116
+
117
+ Si usas librerías que loguean mucho (react-query, redux, etc.), puedes filtrarlas
118
+ para que el dashboard no se llene de ruido.
119
+
120
+ ```tsx
121
+ QALink.init({
122
+ serverUrl: 'ws://192.168.1.100:3000',
123
+ appVersion: '1.2.3',
124
+ console: {
125
+ captureLogs: true,
126
+ captureWarnings: true,
127
+ captureErrors: true,
128
+ // Ignorar logs de estas librerías
129
+ ignorePatterns: [
130
+ '[react-query]',
131
+ '[redux]',
132
+ 'Reanimated',
133
+ 'Gesture Handler',
134
+ '[MMKV]',
135
+ ],
136
+ },
137
+ });
138
+ ```
139
+
140
+ ---
141
+
142
+ ### Ejemplo 3 — Capturar solo logs relevantes para QA
143
+
144
+ Si quieres que el QA vea únicamente los logs que tú marcas explícitamente:
145
+
146
+ ```tsx
147
+ QALink.init({
148
+ serverUrl: 'ws://192.168.1.100:3000',
149
+ appVersion: '1.2.3',
150
+ console: {
151
+ captureLogs: true,
152
+ captureWarnings: true,
153
+ captureErrors: true,
154
+ // Solo capturar logs que contengan estas palabras
155
+ includePatterns: ['[QA]', '[FLOW]', '[ERROR]'],
156
+ },
157
+ });
158
+
159
+ // En tu código, prefija los logs importantes:
160
+ console.log('[QA] Usuario llegó al checkout', { userId, cartTotal });
161
+ console.log('[FLOW] Pago procesado', { orderId });
162
+ console.error('[ERROR] Falló validación', { campo, valor });
163
+ ```
164
+
165
+ ---
166
+
167
+ ### Ejemplo 4 — App con múltiples instancias de Axios
168
+
169
+ ```tsx
170
+ // services/api.ts
171
+ export const apiPrincipal = axios.create({ baseURL: 'https://api.miapp.com/v1' });
172
+ export const apiPagos = axios.create({ baseURL: 'https://pagos.miapp.com' });
173
+
174
+ // App.tsx
175
+ await QALink.init({ serverUrl: 'ws://192.168.1.100:3000', appVersion: '1.2.3' });
176
+
177
+ QALink.interceptAxios(apiPrincipal);
178
+ QALink.interceptAxios(apiPagos);
179
+ // fetch nativo ya se intercepta automáticamente con init()
180
+ ```
181
+
182
+ ---
183
+
184
+ ### Ejemplo 5 — Integración con React Navigation
185
+
186
+ ```tsx
187
+ <NavigationContainer
188
+ ref={navigationRef}
189
+ onStateChange={() => {
190
+ const routeName = navigationRef.current?.getCurrentRoute()?.name ?? '';
191
+ QALink.setScreen(routeName);
192
+ // Genera automáticamente: breadcrumb "NAVIGATE → CheckoutScreen"
193
+ }}
194
+ >
195
+ ```
196
+
197
+ ---
198
+
199
+ ### Ejemplo 6 — Breadcrumbs en acciones críticas
200
+
201
+ Los breadcrumbs + logs de consola juntos dan la línea de tiempo completa al QA:
202
+
203
+ ```tsx
204
+ const handleConfirmarCompra = async () => {
205
+ QALink.addBreadcrumb('TAP → Confirmar Compra', {
206
+ cartId: cart.id,
207
+ items: cart.items.length,
208
+ total: cart.total,
209
+ });
210
+
211
+ console.log('[QA] Iniciando checkout', { cartId: cart.id });
212
+
213
+ try {
214
+ const result = await apiPagos.post('/checkout', { cartId: cart.id });
215
+ console.log('[QA] Checkout exitoso', { orderId: result.data.orderId });
216
+ } catch (error) {
217
+ // QALink ya capturó el error de red automáticamente
218
+ // console.error aquí también se envía al dashboard
219
+ console.error('[QA] Checkout falló', error);
220
+ }
221
+ };
222
+ ```
223
+
224
+ El QA verá en el dashboard esta línea de tiempo completa:
225
+ ```
226
+ 14:33:40 NAVIGATE → CheckoutScreen
227
+ 14:33:55 TAP → Confirmar Compra { cartId: "abc", items: 3, total: 150 }
228
+ 14:33:55 🔵 [QA] Iniciando checkout { cartId: "abc" }
229
+ 14:33:55 ❌ POST /checkout → 500 🔴 ERROR DE BACKEND
230
+ 14:33:55 🔴 [QA] Checkout falló Error: Internal Server Error
231
+ ```
232
+
233
+ ---
234
+
235
+ ### Ejemplo 7 — Configuración para build de QA (sin exponer en producción)
236
+
237
+ ```tsx
238
+ // config/qalink.ts
239
+ import Config from 'react-native-config';
240
+
241
+ export async function initQALink() {
242
+ if (Config.QALINK_ENABLED !== 'true') return;
243
+
244
+ await QALink.init({
245
+ serverUrl: Config.QALINK_SERVER,
246
+ appVersion: Config.VERSION_NAME ?? '0.0.0',
247
+ logNetworkBodies: true,
248
+ captureRuntimeErrors: true,
249
+ console: {
250
+ captureLogs: true,
251
+ captureWarnings: true,
252
+ captureErrors: true,
253
+ ignorePatterns: ['[react-query]'],
254
+ },
255
+ sensitiveHeaders: ['Authorization'],
256
+ sensitiveUrlPatterns: ['/auth/refresh'],
257
+ });
258
+
259
+ QALink.interceptAxios(axios);
260
+ }
261
+ ```
262
+
263
+ ---
264
+
265
+ ### Ejemplo 8 — Verificar estado de conexión
266
+
267
+ ```tsx
268
+ function QAStatusBadge() {
269
+ if (QALink.getStatus() !== 'connected') return null;
270
+
271
+ return (
272
+ <View style={styles.badge}>
273
+ <Text>🔴 QALink grabando</Text>
274
+ </View>
275
+ );
276
+ }
277
+ ```
278
+
279
+ ---
280
+
281
+ ## Qué ve el QA en el dashboard
282
+
283
+ Cada sesión incluye una línea de tiempo unificada con todos los eventos:
284
+
285
+ | Ícono | Tipo | Fuente |
286
+ |-------|-------------------|---------------------|
287
+ | ✅ | Request exitoso | fetch / axios |
288
+ | ❌ | Error de red | fetch / axios |
289
+ | 🔴 | Error fatal (RN) | ErrorUtils / LogBox |
290
+ | 🟡 | Warning (RN) | LogBox / console |
291
+ | 🔵 | Log de consola | console.log |
292
+ | 📍 | Breadcrumb | QALink.addBreadcrumb|
293
+ | 📱 | Pantalla | QALink.setScreen |
294
+
295
+ ---
296
+
297
+ ## Configuración completa
298
+
299
+ ```ts
300
+ QALink.init({
301
+ // — Requeridos —
302
+ serverUrl: 'ws://192.168.1.100:3000',
303
+ appVersion: '1.0.0',
304
+
305
+ // — Red —
306
+ logNetworkBodies: false,
307
+ sensitiveHeaders: ['Authorization'],
308
+ sensitiveUrlPatterns: ['/auth/refresh'],
309
+
310
+ // — Runtime de React Native —
311
+ captureRuntimeErrors: true, // pantalla roja, yellow box, promises
312
+
313
+ // — Consola y Metro —
314
+ console: {
315
+ captureLogs: true,
316
+ captureWarnings: true,
317
+ captureErrors: true,
318
+ ignorePatterns: ['[redux]'], // patrones a ignorar
319
+ includePatterns: [], // si se define, solo captura estos
320
+ },
321
+
322
+ // — General —
323
+ enabled: true,
324
+ debug: false,
325
+ onEvent: (event) => {
326
+ console.log(event.type, event);
327
+ },
328
+ });
329
+ ```
330
+
331
+ ---
332
+
333
+ ## API completa
334
+
335
+ | Método | Descripción |
336
+ |---------------------------------|-----------------------------------------------------------|
337
+ | `QALink.init(config)` | Inicializa el SDK, intercepta fetch, consola y runtime |
338
+ | `QALink.interceptAxios(instance)`| Registra una instancia de axios |
339
+ | `QALink.setScreen(name)` | Registra la pantalla actual (genera breadcrumb) |
340
+ | `QALink.addBreadcrumb(action, data?)` | Registra una acción del usuario |
341
+ | `QALink.getStatus()` | Estado de la conexión WS |
342
+ | `QALink.destroy()` | Limpia interceptores y desconecta |
@@ -0,0 +1,9 @@
1
+ import { ErrorSource } from '../types';
2
+ /**
3
+ * Clasifica el origen del error basado en el status code y contexto.
4
+ * Esta es la lógica core que le dice al QA quién tiene la culpa.
5
+ */
6
+ export declare function classifyErrorSource(statusCode?: number, error?: unknown): ErrorSource;
7
+ export declare function getSourceLabel(source: ErrorSource): string;
8
+ export declare function isError(statusCode?: number): boolean;
9
+ //# sourceMappingURL=classifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classifier.d.ts","sourceRoot":"","sources":["../../src/core/classifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvC;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,WAAW,CAqBrF;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAS1D;AAED,wBAAgB,OAAO,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAEpD"}
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.classifyErrorSource = classifyErrorSource;
4
+ exports.getSourceLabel = getSourceLabel;
5
+ exports.isError = isError;
6
+ /**
7
+ * Clasifica el origen del error basado en el status code y contexto.
8
+ * Esta es la lógica core que le dice al QA quién tiene la culpa.
9
+ */
10
+ function classifyErrorSource(statusCode, error) {
11
+ if (!statusCode) {
12
+ if (error instanceof Error) {
13
+ const msg = error.message.toLowerCase();
14
+ if (msg.includes('network') || msg.includes('timeout') || msg.includes('connection')) {
15
+ return 'NETWORK';
16
+ }
17
+ }
18
+ return 'NETWORK';
19
+ }
20
+ if (statusCode >= 200 && statusCode < 300)
21
+ return 'UNKNOWN';
22
+ if (statusCode >= 300 && statusCode < 400)
23
+ return 'BACKEND';
24
+ if (statusCode === 401 || statusCode === 403)
25
+ return 'AUTH';
26
+ if (statusCode === 404)
27
+ return 'BACKEND';
28
+ if (statusCode === 422)
29
+ return 'FRONTEND';
30
+ if (statusCode === 429)
31
+ return 'BACKEND';
32
+ if (statusCode >= 400 && statusCode < 500)
33
+ return 'FRONTEND';
34
+ if (statusCode >= 500)
35
+ return 'BACKEND';
36
+ return 'UNKNOWN';
37
+ }
38
+ function getSourceLabel(source) {
39
+ const labels = {
40
+ BACKEND: '🔴 Error de Backend',
41
+ FRONTEND: '🟠 Error de Frontend',
42
+ NETWORK: '🟡 Error de Red/Conectividad',
43
+ AUTH: '🔐 Error de Autenticación',
44
+ UNKNOWN: '⚪ Origen desconocido',
45
+ };
46
+ return labels[source];
47
+ }
48
+ function isError(statusCode) {
49
+ return !!statusCode && statusCode >= 400;
50
+ }
51
+ //# sourceMappingURL=classifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classifier.js","sourceRoot":"","sources":["../../src/core/classifier.ts"],"names":[],"mappings":";;AAMA,kDAqBC;AAED,wCASC;AAED,0BAEC;AAxCD;;;GAGG;AACH,SAAgB,mBAAmB,CAAC,UAAmB,EAAE,KAAe;IACtE,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YACxC,IAAI,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;gBACrF,OAAO,SAAS,CAAC;YACnB,CAAC;QACH,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,UAAU,IAAI,GAAG,IAAI,UAAU,GAAG,GAAG;QAAE,OAAO,SAAS,CAAC;IAC5D,IAAI,UAAU,IAAI,GAAG,IAAI,UAAU,GAAG,GAAG;QAAE,OAAO,SAAS,CAAC;IAC5D,IAAI,UAAU,KAAK,GAAG,IAAI,UAAU,KAAK,GAAG;QAAE,OAAO,MAAM,CAAC;IAC5D,IAAI,UAAU,KAAK,GAAG;QAAE,OAAO,SAAS,CAAC;IACzC,IAAI,UAAU,KAAK,GAAG;QAAE,OAAO,UAAU,CAAC;IAC1C,IAAI,UAAU,KAAK,GAAG;QAAE,OAAO,SAAS,CAAC;IACzC,IAAI,UAAU,IAAI,GAAG,IAAI,UAAU,GAAG,GAAG;QAAE,OAAO,UAAU,CAAC;IAC7D,IAAI,UAAU,IAAI,GAAG;QAAE,OAAO,SAAS,CAAC;IAExC,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAgB,cAAc,CAAC,MAAmB;IAChD,MAAM,MAAM,GAAgC;QAC1C,OAAO,EAAE,qBAAqB;QAC9B,QAAQ,EAAE,sBAAsB;QAChC,OAAO,EAAE,8BAA8B;QACvC,IAAI,EAAE,2BAA2B;QACjC,OAAO,EAAE,sBAAsB;KAChC,CAAC;IACF,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC;AACxB,CAAC;AAED,SAAgB,OAAO,CAAC,UAAmB;IACzC,OAAO,CAAC,CAAC,UAAU,IAAI,UAAU,IAAI,GAAG,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { DeviceInfo } from '../types';
2
+ export declare function generateId(): string;
3
+ export declare function getSessionId(): string;
4
+ export declare function resetSession(): string;
5
+ export declare function getDeviceInfo(appVersion: string): Promise<DeviceInfo>;
6
+ export declare function sanitizeHeaders(headers: Record<string, string>, sensitiveHeaders?: string[]): Record<string, string>;
7
+ export declare function sanitizeBody(body: unknown, logBodies: boolean): unknown;
8
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/core/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAItC,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,YAAY,IAAI,MAAM,CAKrC;AAED,wBAAgB,YAAY,IAAI,MAAM,CAGrC;AAED,wBAAsB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAsB3E;AAED,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,gBAAgB,GAAE,MAAM,EAAO,GAC9B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUxB;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAGvE"}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateId = generateId;
4
+ exports.getSessionId = getSessionId;
5
+ exports.resetSession = resetSession;
6
+ exports.getDeviceInfo = getDeviceInfo;
7
+ exports.sanitizeHeaders = sanitizeHeaders;
8
+ exports.sanitizeBody = sanitizeBody;
9
+ let currentSessionId = null;
10
+ function generateId() {
11
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
12
+ }
13
+ function getSessionId() {
14
+ if (!currentSessionId) {
15
+ currentSessionId = generateId();
16
+ }
17
+ return currentSessionId;
18
+ }
19
+ function resetSession() {
20
+ currentSessionId = generateId();
21
+ return currentSessionId;
22
+ }
23
+ async function getDeviceInfo(appVersion) {
24
+ var _a, _b, _c, _d;
25
+ try {
26
+ const { Platform } = require('react-native');
27
+ const DeviceInfo = require('react-native-device-info');
28
+ return {
29
+ platform: Platform.OS,
30
+ osVersion: (_b = (_a = Platform.Version) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : 'unknown',
31
+ appVersion,
32
+ deviceModel: await DeviceInfo.getModel().catch(() => 'unknown'),
33
+ buildType: __DEV__ ? 'debug' : 'release',
34
+ };
35
+ }
36
+ catch (_e) {
37
+ const { Platform } = require('react-native');
38
+ return {
39
+ platform: Platform.OS,
40
+ osVersion: (_d = (_c = Platform.Version) === null || _c === void 0 ? void 0 : _c.toString()) !== null && _d !== void 0 ? _d : 'unknown',
41
+ appVersion,
42
+ deviceModel: 'unknown',
43
+ buildType: __DEV__ ? 'debug' : 'release',
44
+ };
45
+ }
46
+ }
47
+ function sanitizeHeaders(headers, sensitiveHeaders = []) {
48
+ const defaultSensitive = ['authorization', 'x-api-key', 'cookie', 'set-cookie'];
49
+ const allSensitive = [...defaultSensitive, ...sensitiveHeaders.map(h => h.toLowerCase())];
50
+ return Object.fromEntries(Object.entries(headers).map(([key, value]) => [
51
+ key,
52
+ allSensitive.includes(key.toLowerCase()) ? '[REDACTED]' : value,
53
+ ]));
54
+ }
55
+ function sanitizeBody(body, logBodies) {
56
+ if (!logBodies)
57
+ return '[body omitted - enable logNetworkBodies]';
58
+ return body;
59
+ }
60
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/core/session.ts"],"names":[],"mappings":";;AAIA,gCAEC;AAED,oCAKC;AAED,oCAGC;AAED,sCAsBC;AAED,0CAaC;AAED,oCAGC;AA5DD,IAAI,gBAAgB,GAAkB,IAAI,CAAC;AAE3C,SAAgB,UAAU;IACxB,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AACpE,CAAC;AAED,SAAgB,YAAY;IAC1B,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,gBAAgB,GAAG,UAAU,EAAE,CAAC;IAClC,CAAC;IACD,OAAO,gBAAgB,CAAC;AAC1B,CAAC;AAED,SAAgB,YAAY;IAC1B,gBAAgB,GAAG,UAAU,EAAE,CAAC;IAChC,OAAO,gBAAgB,CAAC;AAC1B,CAAC;AAEM,KAAK,UAAU,aAAa,CAAC,UAAkB;;IACpD,IAAI,CAAC;QACH,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;QAC7C,MAAM,UAAU,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAAC;QAEvD,OAAO;YACL,QAAQ,EAAE,QAAQ,CAAC,EAAuB;YAC1C,SAAS,EAAE,MAAA,MAAA,QAAQ,CAAC,OAAO,0CAAE,QAAQ,EAAE,mCAAI,SAAS;YACpD,UAAU;YACV,WAAW,EAAE,MAAM,UAAU,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;YAC/D,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;SACzC,CAAC;IACJ,CAAC;IAAC,WAAM,CAAC;QACP,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;QAC7C,OAAO;YACL,QAAQ,EAAE,QAAQ,CAAC,EAAuB;YAC1C,SAAS,EAAE,MAAA,MAAA,QAAQ,CAAC,OAAO,0CAAE,QAAQ,EAAE,mCAAI,SAAS;YACpD,UAAU;YACV,WAAW,EAAE,SAAS;YACtB,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;SACzC,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAgB,eAAe,CAC7B,OAA+B,EAC/B,mBAA6B,EAAE;IAE/B,MAAM,gBAAgB,GAAG,CAAC,eAAe,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;IAChF,MAAM,YAAY,GAAG,CAAC,GAAG,gBAAgB,EAAE,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IAE1F,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC;QAC5C,GAAG;QACH,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK;KAChE,CAAC,CACH,CAAC;AACJ,CAAC;AAED,SAAgB,YAAY,CAAC,IAAa,EAAE,SAAkB;IAC5D,IAAI,CAAC,SAAS;QAAE,OAAO,0CAA0C,CAAC;IAClE,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,58 @@
1
+ import { QALinkConfig } from './types';
2
+ declare class QALinkSDK {
3
+ private transport;
4
+ private config;
5
+ private cleanups;
6
+ private initialized;
7
+ private currentScreen;
8
+ /**
9
+ * Inicializa el SDK. Llamar al inicio de la app, antes de cualquier request.
10
+ *
11
+ * @example
12
+ * QALink.init({
13
+ * serverUrl: 'ws://192.168.1.100:3000',
14
+ * appVersion: '1.2.3',
15
+ * captureRuntimeErrors: true,
16
+ * console: {
17
+ * captureLogs: true,
18
+ * captureWarnings: true,
19
+ * captureErrors: true,
20
+ * ignorePatterns: ['[react-query]'],
21
+ * },
22
+ * });
23
+ */
24
+ init(config: QALinkConfig): Promise<void>;
25
+ /**
26
+ * Registra interceptor para una instancia específica de Axios.
27
+ *
28
+ * @example
29
+ * import axios from 'axios';
30
+ * QALink.interceptAxios(axios);
31
+ *
32
+ * // También con instancias custom:
33
+ * const api = axios.create({ baseURL: 'https://api.miapp.com' });
34
+ * QALink.interceptAxios(api);
35
+ */
36
+ interceptAxios(axiosInstance: any): void;
37
+ /**
38
+ * Registra una acción del usuario (breadcrumb).
39
+ *
40
+ * @example
41
+ * QALink.addBreadcrumb('TAP → Confirmar Compra', { total: 150 });
42
+ */
43
+ addBreadcrumb(action: string, data?: Record<string, unknown>): void;
44
+ /**
45
+ * Registra la pantalla actual. Llamar en cada cambio de navegación.
46
+ * Genera automáticamente un breadcrumb de navegación.
47
+ */
48
+ setScreen(screenName: string): void;
49
+ /** Estado actual de la conexión WebSocket */
50
+ getStatus(): string;
51
+ /** Limpia todos los interceptores y desconecta. Útil en tests. */
52
+ destroy(): void;
53
+ private log;
54
+ }
55
+ export declare const QALink: QALinkSDK;
56
+ export * from './types';
57
+ export { getSourceLabel } from './core/classifier';
58
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAmD,MAAM,SAAS,CAAC;AASxF,cAAM,SAAS;IACb,OAAO,CAAC,SAAS,CAAmC;IACpD,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,QAAQ,CAAyB;IACzC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,aAAa,CAAa;IAElC;;;;;;;;;;;;;;;OAeG;IACG,IAAI,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IA0E/C;;;;;;;;;;OAUG;IACH,cAAc,CAAC,aAAa,EAAE,GAAG,GAAG,IAAI;IASxC;;;;;OAKG;IACH,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAiBnE;;;OAGG;IACH,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAMnC,6CAA6C;IAC7C,SAAS,IAAI,MAAM;IAInB,kEAAkE;IAClE,OAAO,IAAI,IAAI;IASf,OAAO,CAAC,GAAG;CAKZ;AAED,eAAO,MAAM,MAAM,WAAkB,CAAC;AAEtC,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC"}