react-native-qalink 0.4.0 → 0.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 +384 -252
- package/dist/core/ContextManager.d.ts +13 -0
- package/dist/core/ContextManager.d.ts.map +1 -0
- package/dist/core/ContextManager.js +33 -0
- package/dist/core/ContextManager.js.map +1 -0
- package/dist/core/EventManager.d.ts +12 -0
- package/dist/core/EventManager.d.ts.map +1 -0
- package/dist/core/EventManager.js +30 -0
- package/dist/core/EventManager.js.map +1 -0
- package/dist/core/Logger.d.ts +18 -0
- package/dist/core/Logger.d.ts.map +1 -0
- package/dist/core/Logger.js +78 -0
- package/dist/core/Logger.js.map +1 -0
- package/dist/core/ScreenCapture.d.ts +23 -0
- package/dist/core/ScreenCapture.d.ts.map +1 -0
- package/dist/core/ScreenCapture.js +94 -0
- package/dist/core/ScreenCapture.js.map +1 -0
- package/dist/index.d.ts +17 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +134 -16
- package/dist/index.js.map +1 -1
- package/dist/interceptors/axios.d.ts +23 -3
- package/dist/interceptors/axios.d.ts.map +1 -1
- package/dist/interceptors/axios.js +158 -63
- package/dist/interceptors/axios.js.map +1 -1
- package/dist/interceptors/fetch.d.ts +21 -3
- package/dist/interceptors/fetch.d.ts.map +1 -1
- package/dist/interceptors/fetch.js +205 -91
- package/dist/interceptors/fetch.js.map +1 -1
- package/dist/types/index.d.ts +97 -3
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +7 -5
package/README.md
CHANGED
|
@@ -1,351 +1,483 @@
|
|
|
1
1
|
# react-native-qalink 🔍
|
|
2
2
|
|
|
3
|
-
SDK de captura de errores en tiempo real para React Native.
|
|
3
|
+
SDK de captura de errores en tiempo real para React Native. Captura errores, logs, eventos custom, requests HTTP y screenshots — permitiendo al equipo de QA ver en tiempo real qué está pasando en la app, sin necesidad de Android Studio ni logs técnicos.
|
|
4
|
+
|
|
5
|
+
La conexión WebSocket apunta siempre a **wss://ws.airbites.org**.
|
|
4
6
|
|
|
5
7
|
---
|
|
6
8
|
|
|
7
|
-
##
|
|
9
|
+
## 📦 Instalación
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
```bash
|
|
12
|
+
npm install react-native-qalink
|
|
13
|
+
# o
|
|
14
|
+
yarn add react-native-qalink
|
|
15
|
+
```
|
|
10
16
|
|
|
11
|
-
###
|
|
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.
|
|
17
|
+
### Dependencia opcional para capturas de pantalla:
|
|
13
18
|
|
|
19
|
+
```bash
|
|
20
|
+
npm install react-native-view-shot
|
|
21
|
+
cd ios && pod install
|
|
14
22
|
```
|
|
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
23
|
|
|
29
24
|
---
|
|
30
25
|
|
|
31
|
-
##
|
|
26
|
+
## 🚀 Inicio rápido
|
|
32
27
|
|
|
33
|
-
|
|
28
|
+
```typescript
|
|
29
|
+
import QALink from 'react-native-qalink';
|
|
34
30
|
|
|
35
|
-
|
|
36
|
-
QALink
|
|
31
|
+
// 1. Inicializar el SDK
|
|
32
|
+
QALink.init({
|
|
33
|
+
apiKey: 'qlk_your_api_key_here',
|
|
34
|
+
appVersion: '1.0.0',
|
|
35
|
+
environment: 'dev',
|
|
36
|
+
debug: true,
|
|
37
|
+
});
|
|
37
38
|
|
|
39
|
+
// 2. Configurar contexto de usuario
|
|
40
|
+
QALink.setUserContext({
|
|
41
|
+
userId: '12345',
|
|
42
|
+
email: 'user@example.com',
|
|
43
|
+
plan: 'premium'
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// 3. Configurar contexto custom
|
|
47
|
+
QALink.setCustomContext({
|
|
48
|
+
experimentVariant: 'new_checkout_flow',
|
|
49
|
+
featureFlags: { darkMode: true }
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// 4. Usar los nuevos métodos de logging
|
|
53
|
+
QALink.debug('User opened settings');
|
|
54
|
+
QALink.info('Payment initiated', { amount: 99.99 });
|
|
55
|
+
QALink.warn('Slow API response', { duration: 3500 });
|
|
56
|
+
QALink.error('Payment failed', { code: 'CARD_DECLINED' });
|
|
57
|
+
QALink.critical('App about to crash');
|
|
58
|
+
|
|
59
|
+
// 5. Eventos custom
|
|
60
|
+
QALink.logEvent('checkout_completed', {
|
|
61
|
+
items: 3,
|
|
62
|
+
totalAmount: 299.99,
|
|
63
|
+
paymentMethod: 'credit_card'
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// 6. Captura de pantalla (requiere react-native-view-shot)
|
|
67
|
+
await QALink.captureScreen('before_payment');
|
|
38
68
|
```
|
|
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
69
|
|
|
45
|
-
|
|
46
|
-
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 🔧 Configuración HTTP avanzada
|
|
47
73
|
|
|
74
|
+
El SDK captura automáticamente todas las peticiones HTTP (fetch y axios). Puedes configurar qué capturar:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
QALink.init({
|
|
78
|
+
apiKey: 'qlk_xxx',
|
|
79
|
+
appVersion: '1.0.0',
|
|
80
|
+
httpConfig: {
|
|
81
|
+
// Captura requests y responses
|
|
82
|
+
captureRequests: true,
|
|
83
|
+
captureResponses: true,
|
|
84
|
+
|
|
85
|
+
// Filtrar por URL
|
|
86
|
+
includeUrls: ['/api/payment', '/api/checkout'],
|
|
87
|
+
excludeUrls: ['/api/analytics', '/health'],
|
|
88
|
+
|
|
89
|
+
// Filtrar por método HTTP
|
|
90
|
+
methods: ['POST', 'PUT', 'DELETE'], // ignorar GETs
|
|
91
|
+
|
|
92
|
+
// Sanitización de datos sensibles
|
|
93
|
+
sanitizeHeaders: ['Authorization', 'X-API-Key'],
|
|
94
|
+
sanitizeRequestBody: ['password', 'creditCard', 'cvv'],
|
|
95
|
+
sanitizeResponseBody: ['token', 'secret'],
|
|
96
|
+
|
|
97
|
+
// Capturar solo errores HTTP (4xx, 5xx)
|
|
98
|
+
captureOnlyErrors: true,
|
|
99
|
+
minStatusCode: 400,
|
|
100
|
+
|
|
101
|
+
// Límite de tamaño de body
|
|
102
|
+
maxBodySize: 5000, // bytes
|
|
103
|
+
|
|
104
|
+
// Performance monitoring
|
|
105
|
+
captureTimings: true,
|
|
106
|
+
slowRequestThreshold: 2000, // marcar requests > 2s como lentos
|
|
107
|
+
}
|
|
108
|
+
});
|
|
48
109
|
```
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
110
|
+
|
|
111
|
+
### Actualizar configuración HTTP en runtime:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
QALink.configureHTTP({
|
|
115
|
+
captureOnlyErrors: true,
|
|
116
|
+
slowRequestThreshold: 1000
|
|
117
|
+
});
|
|
54
118
|
```
|
|
55
119
|
|
|
56
|
-
|
|
57
|
-
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## 📝 API de Logging
|
|
123
|
+
|
|
124
|
+
Todos los métodos de logging hacen **console.log nativo** (visible en Metro) **Y** envían al servidor QALink:
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
// Acepta múltiples argumentos como console.log
|
|
128
|
+
QALink.debug('User action', { screen: 'Settings' }, 'Extra data');
|
|
129
|
+
|
|
130
|
+
QALink.info('API call started', {
|
|
131
|
+
endpoint: '/api/users',
|
|
132
|
+
method: 'GET'
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
QALink.warn('Memory warning', {
|
|
136
|
+
available: '50MB',
|
|
137
|
+
threshold: '100MB'
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
QALink.error('Validation failed', {
|
|
141
|
+
field: 'email',
|
|
142
|
+
reason: 'Invalid format'
|
|
143
|
+
});
|
|
58
144
|
|
|
59
|
-
|
|
145
|
+
QALink.critical('Unrecoverable error', {
|
|
146
|
+
code: 'FATAL_001',
|
|
147
|
+
message: 'Cannot connect to database'
|
|
148
|
+
});
|
|
149
|
+
```
|
|
60
150
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
| Promise sin .catch() | `unknown` | `Unhandled Promise Rejection` |
|
|
68
|
-
| Error de Metro/bundler | `rn_error` | `Unable to resolve module`, `SyntaxError` |
|
|
151
|
+
**Niveles de log:**
|
|
152
|
+
- `debug` → desarrollo y debugging
|
|
153
|
+
- `info` → información general
|
|
154
|
+
- `warn` → advertencias y problemas no críticos
|
|
155
|
+
- `error` → errores recuperables
|
|
156
|
+
- `critical` → errores fatales que pueden causar crash
|
|
69
157
|
|
|
70
158
|
---
|
|
71
159
|
|
|
72
|
-
##
|
|
160
|
+
## 🎯 Eventos Custom
|
|
73
161
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
162
|
+
Registra eventos de negocio importantes:
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
// Evento simple
|
|
166
|
+
QALink.logEvent('user_login');
|
|
167
|
+
|
|
168
|
+
// Evento con datos
|
|
169
|
+
QALink.logEvent('purchase_completed', {
|
|
170
|
+
productId: 'SKU-123',
|
|
171
|
+
price: 29.99,
|
|
172
|
+
currency: 'USD',
|
|
173
|
+
quantity: 2
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
QALink.logEvent('feature_used', {
|
|
177
|
+
featureName: 'dark_mode',
|
|
178
|
+
enabled: true,
|
|
179
|
+
timestamp: Date.now()
|
|
180
|
+
});
|
|
78
181
|
```
|
|
79
182
|
|
|
183
|
+
**Los eventos incluyen automáticamente:**
|
|
184
|
+
- User context (userId, email, etc.)
|
|
185
|
+
- Custom context (feature flags, experiments)
|
|
186
|
+
- Screen actual
|
|
187
|
+
- Session ID
|
|
188
|
+
- Device ID
|
|
189
|
+
- Timestamp
|
|
190
|
+
|
|
80
191
|
---
|
|
81
192
|
|
|
82
|
-
##
|
|
193
|
+
## 👤 Context API
|
|
83
194
|
|
|
84
|
-
###
|
|
195
|
+
### User Context
|
|
196
|
+
Información del usuario que se adjunta a **todos** los eventos:
|
|
85
197
|
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
198
|
+
```typescript
|
|
199
|
+
QALink.setUserContext({
|
|
200
|
+
userId: '12345',
|
|
201
|
+
email: 'john@example.com',
|
|
202
|
+
username: 'john_doe',
|
|
203
|
+
plan: 'premium',
|
|
204
|
+
region: 'LATAM'
|
|
205
|
+
});
|
|
91
206
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
},
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
207
|
+
// Actualizar parcialmente
|
|
208
|
+
QALink.setUserContext({ plan: 'enterprise' });
|
|
209
|
+
|
|
210
|
+
// Limpiar
|
|
211
|
+
QALink.clearContext();
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Custom Context
|
|
215
|
+
Datos custom que se adjuntan a todos los eventos:
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
QALink.setCustomContext({
|
|
219
|
+
experimentVariant: 'new_ui_v2',
|
|
220
|
+
featureFlags: {
|
|
221
|
+
darkMode: true,
|
|
222
|
+
newCheckout: false
|
|
223
|
+
},
|
|
224
|
+
build: 'staging-142'
|
|
225
|
+
});
|
|
112
226
|
```
|
|
113
227
|
|
|
114
228
|
---
|
|
115
229
|
|
|
116
|
-
|
|
230
|
+
## 📸 Captura de Pantalla
|
|
231
|
+
|
|
232
|
+
**Requisito:** `react-native-view-shot`
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
// Capturar pantalla completa
|
|
236
|
+
await QALink.captureScreen('before_payment');
|
|
237
|
+
await QALink.captureScreen('error_state');
|
|
117
238
|
|
|
118
|
-
|
|
119
|
-
|
|
239
|
+
// Capturar componente específico
|
|
240
|
+
import { useRef } from 'react';
|
|
120
241
|
|
|
121
|
-
|
|
242
|
+
const MyComponent = () => {
|
|
243
|
+
const viewRef = useRef(null);
|
|
244
|
+
|
|
245
|
+
const handleCapture = async () => {
|
|
246
|
+
await QALink.captureRef(viewRef, 'form_error');
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
return (
|
|
250
|
+
<View ref={viewRef}>
|
|
251
|
+
{/* contenido */}
|
|
252
|
+
</View>
|
|
253
|
+
);
|
|
254
|
+
};
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**Configuración:**
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
122
260
|
QALink.init({
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
261
|
+
apiKey: 'qlk_xxx',
|
|
262
|
+
appVersion: '1.0.0',
|
|
263
|
+
enableScreenCapture: true, // ← activar
|
|
264
|
+
});
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## 🔌 Interceptores HTTP
|
|
270
|
+
|
|
271
|
+
### Fetch (automático)
|
|
272
|
+
El SDK intercepta **todos** los `fetch()` automáticamente. No necesitas hacer nada.
|
|
273
|
+
|
|
274
|
+
### Axios
|
|
275
|
+
Si usas Axios, puedes interceptar instancias específicas:
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
import axios from 'axios';
|
|
279
|
+
import QALink from 'react-native-qalink';
|
|
280
|
+
|
|
281
|
+
const apiClient = axios.create({
|
|
282
|
+
baseURL: 'https://api.example.com'
|
|
139
283
|
});
|
|
284
|
+
|
|
285
|
+
// Interceptar esta instancia de Axios
|
|
286
|
+
QALink.interceptAxios(apiClient);
|
|
140
287
|
```
|
|
141
288
|
|
|
142
289
|
---
|
|
143
290
|
|
|
144
|
-
|
|
291
|
+
## 🛠️ Configuración completa
|
|
145
292
|
|
|
146
|
-
|
|
293
|
+
```typescript
|
|
294
|
+
import QALink from 'react-native-qalink';
|
|
147
295
|
|
|
148
|
-
```tsx
|
|
149
296
|
QALink.init({
|
|
150
|
-
|
|
151
|
-
apiKey: '
|
|
297
|
+
// ── Requerido ──────────────────────────────────
|
|
298
|
+
apiKey: 'qlk_your_64_char_hex_key',
|
|
152
299
|
appVersion: '1.2.3',
|
|
300
|
+
|
|
301
|
+
// ── Básico ─────────────────────────────────────
|
|
302
|
+
enabled: true, // habilitar/deshabilitar SDK
|
|
303
|
+
environment: 'dev', // 'dev' | 'prod'
|
|
304
|
+
debug: true, // logs de debug en consola
|
|
305
|
+
|
|
306
|
+
// ── HTTP Interceptors ──────────────────────────
|
|
307
|
+
httpConfig: {
|
|
308
|
+
captureRequests: true,
|
|
309
|
+
captureResponses: true,
|
|
310
|
+
includeUrls: ['/api/'],
|
|
311
|
+
excludeUrls: ['/health', '/metrics'],
|
|
312
|
+
methods: ['POST', 'PUT', 'DELETE'],
|
|
313
|
+
sanitizeHeaders: ['Authorization'],
|
|
314
|
+
sanitizeRequestBody: ['password', 'token', 'creditCard'],
|
|
315
|
+
sanitizeResponseBody: ['secret', 'apiKey'],
|
|
316
|
+
captureOnlyErrors: false,
|
|
317
|
+
minStatusCode: 200,
|
|
318
|
+
maxBodySize: 5000,
|
|
319
|
+
captureTimings: true,
|
|
320
|
+
slowRequestThreshold: 2000,
|
|
321
|
+
},
|
|
322
|
+
|
|
323
|
+
// ── Legacy Network (compatibilidad) ────────────
|
|
324
|
+
logNetworkBodies: false,
|
|
325
|
+
sensitiveHeaders: ['Authorization'],
|
|
326
|
+
sensitiveUrlPatterns: ['/auth/', '/login'],
|
|
327
|
+
sensitiveBodyFields: ['password', 'token'],
|
|
328
|
+
|
|
329
|
+
// ── Console ─────────────────────────────────────
|
|
153
330
|
console: {
|
|
154
331
|
captureLogs: true,
|
|
155
332
|
captureWarnings: true,
|
|
156
333
|
captureErrors: true,
|
|
157
|
-
|
|
158
|
-
includePatterns: [
|
|
334
|
+
ignorePatterns: ['Ignore this'],
|
|
335
|
+
includePatterns: [],
|
|
159
336
|
},
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
// En tu código, prefija los logs importantes:
|
|
163
|
-
console.log('[QA] Usuario llegó al checkout', { userId, cartTotal });
|
|
164
|
-
console.log('[FLOW] Pago procesado', { orderId });
|
|
165
|
-
console.error('[ERROR] Falló validación', { campo, valor });
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
---
|
|
169
337
|
|
|
170
|
-
|
|
338
|
+
// ── Errors ──────────────────────────────────────
|
|
339
|
+
captureRuntimeErrors: true, // red screen / yellow box
|
|
340
|
+
captureMetroErrors: true, // bundler errors
|
|
171
341
|
|
|
172
|
-
|
|
173
|
-
//
|
|
174
|
-
export const apiPrincipal = axios.create({ baseURL: 'https://api.miapp.com/v1' });
|
|
175
|
-
export const apiPagos = axios.create({ baseURL: 'https://pagos.miapp.com' });
|
|
342
|
+
// ── Screen Capture ──────────────────────────────
|
|
343
|
+
enableScreenCapture: true, // requiere react-native-view-shot
|
|
176
344
|
|
|
177
|
-
//
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
appVersion: '1.2.3',
|
|
345
|
+
// ── Callbacks ───────────────────────────────────
|
|
346
|
+
onEvent: (event) => {
|
|
347
|
+
console.log('Event sent:', event.type);
|
|
348
|
+
}
|
|
182
349
|
});
|
|
183
|
-
|
|
184
|
-
QALink.interceptAxios(apiPrincipal);
|
|
185
|
-
QALink.interceptAxios(apiPagos);
|
|
186
|
-
// fetch nativo ya se intercepta automáticamente con init()
|
|
187
350
|
```
|
|
188
351
|
|
|
189
352
|
---
|
|
190
353
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
354
|
+
## 📊 Tipos de eventos capturados
|
|
355
|
+
|
|
356
|
+
El SDK captura automáticamente:
|
|
357
|
+
|
|
358
|
+
| Tipo | Descripción |
|
|
359
|
+
|------|-------------|
|
|
360
|
+
| `session_start` | Inicio de sesión |
|
|
361
|
+
| `http_request` | Request HTTP (fetch/axios) |
|
|
362
|
+
| `user_log` | Logs con debug/info/warn/error/critical |
|
|
363
|
+
| `custom_event` | Eventos custom con logEvent() |
|
|
364
|
+
| `console_log` | console.log |
|
|
365
|
+
| `console_warn` | console.warn |
|
|
366
|
+
| `console_error` | console.error |
|
|
367
|
+
| `runtime_error` | Red screen / Yellow box |
|
|
368
|
+
| `metro_error` | Errores de Metro Bundler |
|
|
369
|
+
| `js_error` | Errores de JavaScript |
|
|
370
|
+
| `breadcrumb` | Navegación y acciones |
|
|
371
|
+
| `screen_capture` | Screenshots |
|
|
203
372
|
|
|
204
373
|
---
|
|
205
374
|
|
|
206
|
-
|
|
375
|
+
## 🔐 Compatibilidad con fetch-blob
|
|
207
376
|
|
|
208
|
-
|
|
377
|
+
El interceptor de fetch es **100% compatible** con `fetch-blob`:
|
|
209
378
|
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
QALink.addBreadcrumb('TAP → Confirmar Compra', {
|
|
213
|
-
cartId: cart.id,
|
|
214
|
-
items: cart.items.length,
|
|
215
|
-
total: cart.total,
|
|
216
|
-
});
|
|
379
|
+
```typescript
|
|
380
|
+
import { fetch, Blob } from 'react-native-fetch-blob';
|
|
217
381
|
|
|
218
|
-
|
|
382
|
+
// QALink detecta automáticamente Blobs y no intenta serializarlos
|
|
383
|
+
const blob = new Blob([...], { type: 'image/jpeg' });
|
|
219
384
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
// QALink ya capturó el error de red automáticamente
|
|
225
|
-
// console.error aquí también se envía al dashboard
|
|
226
|
-
console.error('[QA] Checkout falló', error);
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
```
|
|
385
|
+
await fetch('https://api.example.com/upload', {
|
|
386
|
+
method: 'POST',
|
|
387
|
+
body: blob
|
|
388
|
+
});
|
|
230
389
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
14:33:40 NAVIGATE → CheckoutScreen
|
|
234
|
-
14:33:55 TAP → Confirmar Compra { cartId: "abc", items: 3, total: 150 }
|
|
235
|
-
14:33:55 🔵 [QA] Iniciando checkout { cartId: "abc" }
|
|
236
|
-
14:33:55 ❌ POST /checkout → 500 🔴 ERROR DE BACKEND
|
|
237
|
-
14:33:55 🔴 [QA] Checkout falló Error: Internal Server Error
|
|
390
|
+
// En el dashboard verás:
|
|
391
|
+
// body: { _type: 'Blob', size: 12345, type: 'image/jpeg' }
|
|
238
392
|
```
|
|
239
393
|
|
|
240
394
|
---
|
|
241
395
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
await QALink.init({
|
|
252
|
-
serverUrl: Config.QALINK_SERVER,
|
|
253
|
-
apiKey: Config.QALINK_API_KEY,
|
|
254
|
-
appVersion: Config.VERSION_NAME ?? '0.0.0',
|
|
255
|
-
logNetworkBodies: true,
|
|
256
|
-
captureRuntimeErrors: true,
|
|
257
|
-
console: {
|
|
258
|
-
captureLogs: true,
|
|
259
|
-
captureWarnings: true,
|
|
260
|
-
captureErrors: true,
|
|
261
|
-
ignorePatterns: ['[react-query]'],
|
|
262
|
-
},
|
|
263
|
-
sensitiveHeaders: ['Authorization'],
|
|
264
|
-
sensitiveUrlPatterns: ['/auth/refresh'],
|
|
265
|
-
});
|
|
396
|
+
## 🎯 Casos de uso
|
|
397
|
+
|
|
398
|
+
### 1. Tracking de flujo de checkout
|
|
399
|
+
```typescript
|
|
400
|
+
QALink.setUserContext({ userId: user.id, email: user.email });
|
|
401
|
+
|
|
402
|
+
QALink.logEvent('checkout_started', { cartTotal: 299.99 });
|
|
403
|
+
QALink.info('Loading payment methods');
|
|
266
404
|
|
|
267
|
-
|
|
405
|
+
// ... usuario selecciona método de pago ...
|
|
406
|
+
QALink.logEvent('payment_method_selected', { method: 'credit_card' });
|
|
407
|
+
|
|
408
|
+
try {
|
|
409
|
+
const response = await processPayment();
|
|
410
|
+
QALink.logEvent('payment_success', {
|
|
411
|
+
orderId: response.orderId,
|
|
412
|
+
amount: 299.99
|
|
413
|
+
});
|
|
414
|
+
} catch (error) {
|
|
415
|
+
QALink.error('Payment failed', {
|
|
416
|
+
reason: error.message,
|
|
417
|
+
code: error.code
|
|
418
|
+
});
|
|
419
|
+
await QALink.captureScreen('payment_error');
|
|
268
420
|
}
|
|
269
421
|
```
|
|
270
422
|
|
|
271
|
-
|
|
423
|
+
### 2. Debugging de API lenta
|
|
424
|
+
```typescript
|
|
425
|
+
QALink.configureHTTP({
|
|
426
|
+
captureTimings: true,
|
|
427
|
+
slowRequestThreshold: 1500, // 1.5s
|
|
428
|
+
});
|
|
272
429
|
|
|
273
|
-
|
|
430
|
+
// Los requests > 1.5s aparecerán marcados en el dashboard
|
|
431
|
+
```
|
|
274
432
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
433
|
+
### 3. Tracking de experimentos A/B
|
|
434
|
+
```typescript
|
|
435
|
+
const variant = getExperimentVariant('new_ui');
|
|
278
436
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
437
|
+
QALink.setCustomContext({
|
|
438
|
+
experiment: 'new_ui',
|
|
439
|
+
variant: variant, // 'A' or 'B'
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
QALink.logEvent('experiment_viewed', {
|
|
443
|
+
experiment: 'new_ui',
|
|
444
|
+
variant
|
|
445
|
+
});
|
|
285
446
|
```
|
|
286
447
|
|
|
287
448
|
---
|
|
288
449
|
|
|
289
|
-
##
|
|
450
|
+
## 🚨 Troubleshooting
|
|
290
451
|
|
|
291
|
-
|
|
452
|
+
### Los logs no aparecen en el dashboard
|
|
453
|
+
- Verifica que `apiKey` sea correcto (formato: `qlk_<64 hex>`)
|
|
454
|
+
- Verifica que `enabled: true`
|
|
455
|
+
- Revisa la consola: `QALink.getStatus()` debe ser `'connected'`
|
|
292
456
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
| 📍 | Breadcrumb | QALink.addBreadcrumb|
|
|
301
|
-
| 📱 | Pantalla | QALink.setScreen |
|
|
457
|
+
### Screen capture no funciona
|
|
458
|
+
- Instala: `npm install react-native-view-shot`
|
|
459
|
+
- Habilita: `enableScreenCapture: true` en config
|
|
460
|
+
- iOS: `cd ios && pod install`
|
|
461
|
+
|
|
462
|
+
### Axios no se intercepta
|
|
463
|
+
- Llama `QALink.interceptAxios(axiosInstance)` **después** de `QALink.init()`
|
|
302
464
|
|
|
303
465
|
---
|
|
304
466
|
|
|
305
|
-
##
|
|
467
|
+
## 📖 Recursos
|
|
306
468
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
serverUrl: 'ws://192.168.1.100:3000',
|
|
311
|
-
apiKey: 'qlk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
|
|
312
|
-
appVersion: '1.0.0',
|
|
469
|
+
- **Dashboard:** https://qalink.airbites.org
|
|
470
|
+
- **Documentación completa:** (próximamente)
|
|
471
|
+
- **GitHub:** (próximamente)
|
|
313
472
|
|
|
314
|
-
|
|
315
|
-
logNetworkBodies: false,
|
|
316
|
-
sensitiveHeaders: ['Authorization'],
|
|
317
|
-
sensitiveUrlPatterns: ['/auth/refresh'],
|
|
473
|
+
---
|
|
318
474
|
|
|
319
|
-
|
|
320
|
-
captureRuntimeErrors: true, // pantalla roja, yellow box, promises
|
|
475
|
+
## 📄 Licencia
|
|
321
476
|
|
|
322
|
-
|
|
323
|
-
console: {
|
|
324
|
-
captureLogs: true,
|
|
325
|
-
captureWarnings: true,
|
|
326
|
-
captureErrors: true,
|
|
327
|
-
ignorePatterns: ['[redux]'], // patrones a ignorar
|
|
328
|
-
includePatterns: [], // si se define, solo captura estos
|
|
329
|
-
},
|
|
330
|
-
|
|
331
|
-
// — General —
|
|
332
|
-
enabled: true,
|
|
333
|
-
debug: false,
|
|
334
|
-
onEvent: (event) => {
|
|
335
|
-
console.log(event.type, event);
|
|
336
|
-
},
|
|
337
|
-
});
|
|
338
|
-
```
|
|
477
|
+
MIT
|
|
339
478
|
|
|
340
479
|
---
|
|
341
480
|
|
|
342
|
-
##
|
|
481
|
+
## 🤝 Contribuir
|
|
343
482
|
|
|
344
|
-
|
|
345
|
-
|---------------------------------|-----------------------------------------------------------|
|
|
346
|
-
| `QALink.init(config)` | Inicializa el SDK, intercepta fetch, consola y runtime |
|
|
347
|
-
| `QALink.interceptAxios(instance)`| Registra una instancia de axios |
|
|
348
|
-
| `QALink.setScreen(name)` | Registra la pantalla actual (genera breadcrumb) |
|
|
349
|
-
| `QALink.addBreadcrumb(action, data?)` | Registra una acción del usuario |
|
|
350
|
-
| `QALink.getStatus()` | Estado de la conexión WS |
|
|
351
|
-
| `QALink.destroy()` | Limpia interceptores y desconecta |
|
|
483
|
+
Pull requests son bienvenidos. Para cambios mayores, abre primero un issue.
|