react-native-qalink 0.4.1 → 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 +382 -253
- 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 +129 -9
- 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 -2
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +7 -5
package/README.md
CHANGED
|
@@ -1,354 +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
4
|
|
|
5
|
-
La conexión WebSocket apunta siempre a **wss://ws.airbites.org
|
|
5
|
+
La conexión WebSocket apunta siempre a **wss://ws.airbites.org**.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## 📦 Instalación
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
```bash
|
|
12
|
+
npm install react-native-qalink
|
|
13
|
+
# o
|
|
14
|
+
yarn add react-native-qalink
|
|
15
|
+
```
|
|
12
16
|
|
|
13
|
-
###
|
|
14
|
-
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:
|
|
15
18
|
|
|
19
|
+
```bash
|
|
20
|
+
npm install react-native-view-shot
|
|
21
|
+
cd ios && pod install
|
|
16
22
|
```
|
|
17
|
-
Tu código → fetch('/api/users')
|
|
18
|
-
↓
|
|
19
|
-
QALink intercepta → registra método, URL, tiempo de inicio
|
|
20
|
-
↓
|
|
21
|
-
Ejecuta fetch real → espera respuesta del servidor
|
|
22
|
-
↓
|
|
23
|
-
QALink registra → status code, body, duración, clasificación
|
|
24
|
-
↓
|
|
25
|
-
Devuelve al código → response (sin modificarla)
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
### 2. Interceptores de Axios
|
|
29
|
-
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.
|
|
30
23
|
|
|
31
24
|
---
|
|
32
25
|
|
|
33
|
-
##
|
|
26
|
+
## 🚀 Inicio rápido
|
|
34
27
|
|
|
35
|
-
|
|
28
|
+
```typescript
|
|
29
|
+
import QALink from 'react-native-qalink';
|
|
36
30
|
|
|
37
|
-
|
|
38
|
-
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
|
+
});
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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');
|
|
45
68
|
```
|
|
46
69
|
|
|
47
|
-
|
|
48
|
-
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.
|
|
70
|
+
---
|
|
49
71
|
|
|
72
|
+
## 🔧 Configuración HTTP avanzada
|
|
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
|
+
});
|
|
50
109
|
```
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
110
|
+
|
|
111
|
+
### Actualizar configuración HTTP en runtime:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
QALink.configureHTTP({
|
|
115
|
+
captureOnlyErrors: true,
|
|
116
|
+
slowRequestThreshold: 1000
|
|
117
|
+
});
|
|
56
118
|
```
|
|
57
119
|
|
|
58
|
-
|
|
59
|
-
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.
|
|
120
|
+
---
|
|
60
121
|
|
|
61
|
-
|
|
122
|
+
## 📝 API de Logging
|
|
62
123
|
|
|
63
|
-
|
|
64
|
-
|--------------------------|-----------------|----------------------------------------------|
|
|
65
|
-
| Error fatal | `red_screen` | `Invariant Violation`, `undefined is not...` |
|
|
66
|
-
| Warning de RN | `yellow_box` | `componentWillMount is deprecated` |
|
|
67
|
-
| Módulo nativo | `native_module` | `NativeModule X is null` |
|
|
68
|
-
| Bridge JS↔Native | `bridge` | `RCTBridge error` |
|
|
69
|
-
| Promise sin .catch() | `unknown` | `Unhandled Promise Rejection` |
|
|
70
|
-
| Error de Metro/bundler | `rn_error` | `Unable to resolve module`, `SyntaxError` |
|
|
124
|
+
Todos los métodos de logging hacen **console.log nativo** (visible en Metro) **Y** envían al servidor QALink:
|
|
71
125
|
|
|
72
|
-
|
|
126
|
+
```typescript
|
|
127
|
+
// Acepta múltiples argumentos como console.log
|
|
128
|
+
QALink.debug('User action', { screen: 'Settings' }, 'Extra data');
|
|
73
129
|
|
|
74
|
-
|
|
130
|
+
QALink.info('API call started', {
|
|
131
|
+
endpoint: '/api/users',
|
|
132
|
+
method: 'GET'
|
|
133
|
+
});
|
|
75
134
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
+
});
|
|
144
|
+
|
|
145
|
+
QALink.critical('Unrecoverable error', {
|
|
146
|
+
code: 'FATAL_001',
|
|
147
|
+
message: 'Cannot connect to database'
|
|
148
|
+
});
|
|
80
149
|
```
|
|
81
150
|
|
|
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
|
|
157
|
+
|
|
82
158
|
---
|
|
83
159
|
|
|
84
|
-
##
|
|
160
|
+
## 🎯 Eventos Custom
|
|
85
161
|
|
|
86
|
-
|
|
87
|
-
import { QALink } from 'react-native-qalink';
|
|
88
|
-
import axios from 'axios';
|
|
162
|
+
Registra eventos de negocio importantes:
|
|
89
163
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
|
96
174
|
});
|
|
97
175
|
|
|
98
|
-
QALink.
|
|
176
|
+
QALink.logEvent('feature_used', {
|
|
177
|
+
featureName: 'dark_mode',
|
|
178
|
+
enabled: true,
|
|
179
|
+
timestamp: Date.now()
|
|
180
|
+
});
|
|
99
181
|
```
|
|
100
182
|
|
|
101
|
-
|
|
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
|
|
102
190
|
|
|
103
191
|
---
|
|
104
192
|
|
|
105
|
-
## API
|
|
193
|
+
## 👤 Context API
|
|
106
194
|
|
|
107
|
-
|
|
195
|
+
### User Context
|
|
196
|
+
Información del usuario que se adjunta a **todos** los eventos:
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
QALink.setUserContext({
|
|
200
|
+
userId: '12345',
|
|
201
|
+
email: 'john@example.com',
|
|
202
|
+
username: 'john_doe',
|
|
203
|
+
plan: 'premium',
|
|
204
|
+
region: 'LATAM'
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Actualizar parcialmente
|
|
208
|
+
QALink.setUserContext({ plan: 'enterprise' });
|
|
209
|
+
|
|
210
|
+
// Limpiar
|
|
211
|
+
QALink.clearContext();
|
|
212
|
+
```
|
|
108
213
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
| **`QALink.init(config)`** | Inicializa el SDK. Conecta a QALink, intercepta `fetch`, consola y runtime. Requiere `apiKey` y `appVersion`. |
|
|
112
|
-
| **`QALink.interceptAxios(axiosInstance)`** | Registra una instancia de Axios para capturar sus requests/respuestas. Llamar después de `init()`. |
|
|
113
|
-
| **`QALink.setScreen(screenName)`** | Indica la pantalla actual. Genera un breadcrumb `NAVIGATE → screenName`. |
|
|
114
|
-
| **`QALink.addBreadcrumb(action, data?)`** | Envía un breadcrumb (acción + datos opcionales) a la línea de tiempo del dashboard. |
|
|
115
|
-
| **`QALink.logRequest(options)`** | Registra manualmente una petición de red (método, URL, status, payloads, duración). Útil si no usas fetch ni Axios. |
|
|
116
|
-
| **`QALink.getDeviceId()`** | Devuelve el `deviceId` que usa el SDK en esta sesión. |
|
|
117
|
-
| **`QALink.getStatus()`** | Devuelve el estado de la conexión WebSocket (p. ej. `'connected'`, `'not initialized'`). |
|
|
118
|
-
| **`QALink.destroy()`** | Desconecta, elimina todos los interceptores y deja el SDK no inicializado. |
|
|
214
|
+
### Custom Context
|
|
215
|
+
Datos custom que se adjuntan a todos los eventos:
|
|
119
216
|
|
|
120
|
-
|
|
217
|
+
```typescript
|
|
218
|
+
QALink.setCustomContext({
|
|
219
|
+
experimentVariant: 'new_ui_v2',
|
|
220
|
+
featureFlags: {
|
|
221
|
+
darkMode: true,
|
|
222
|
+
newCheckout: false
|
|
223
|
+
},
|
|
224
|
+
build: 'staging-142'
|
|
225
|
+
});
|
|
226
|
+
```
|
|
121
227
|
|
|
122
228
|
---
|
|
123
229
|
|
|
124
|
-
##
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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');
|
|
238
|
+
|
|
239
|
+
// Capturar componente específico
|
|
240
|
+
import { useRef } from 'react';
|
|
241
|
+
|
|
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
|
|
260
|
+
QALink.init({
|
|
261
|
+
apiKey: 'qlk_xxx',
|
|
262
|
+
appVersion: '1.0.0',
|
|
263
|
+
enableScreenCapture: true, // ← activar
|
|
264
|
+
});
|
|
265
|
+
```
|
|
153
266
|
|
|
154
267
|
---
|
|
155
268
|
|
|
156
|
-
##
|
|
269
|
+
## 🔌 Interceptores HTTP
|
|
270
|
+
|
|
271
|
+
### Fetch (automático)
|
|
272
|
+
El SDK intercepta **todos** los `fetch()` automáticamente. No necesitas hacer nada.
|
|
157
273
|
|
|
158
|
-
###
|
|
274
|
+
### Axios
|
|
275
|
+
Si usas Axios, puedes interceptar instancias específicas:
|
|
159
276
|
|
|
160
|
-
```
|
|
161
|
-
// App.tsx
|
|
162
|
-
import { useEffect } from 'react';
|
|
163
|
-
import { QALink } from 'react-native-qalink';
|
|
277
|
+
```typescript
|
|
164
278
|
import axios from 'axios';
|
|
279
|
+
import QALink from 'react-native-qalink';
|
|
165
280
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
console: {
|
|
173
|
-
captureLogs: true,
|
|
174
|
-
captureWarnings: true,
|
|
175
|
-
captureErrors: true,
|
|
176
|
-
},
|
|
177
|
-
debug: __DEV__,
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
QALink.interceptAxios(axios);
|
|
181
|
-
}, []);
|
|
182
|
-
|
|
183
|
-
return <RootNavigator />;
|
|
184
|
-
}
|
|
281
|
+
const apiClient = axios.create({
|
|
282
|
+
baseURL: 'https://api.example.com'
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// Interceptar esta instancia de Axios
|
|
286
|
+
QALink.interceptAxios(apiClient);
|
|
185
287
|
```
|
|
186
288
|
|
|
187
|
-
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## 🛠️ Configuración completa
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
import QALink from 'react-native-qalink';
|
|
188
295
|
|
|
189
|
-
```tsx
|
|
190
296
|
QALink.init({
|
|
191
|
-
|
|
297
|
+
// ── Requerido ──────────────────────────────────
|
|
298
|
+
apiKey: 'qlk_your_64_char_hex_key',
|
|
192
299
|
appVersion: '1.2.3',
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
],
|
|
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,
|
|
204
321
|
},
|
|
205
|
-
});
|
|
206
|
-
```
|
|
207
322
|
|
|
208
|
-
|
|
323
|
+
// ── Legacy Network (compatibilidad) ────────────
|
|
324
|
+
logNetworkBodies: false,
|
|
325
|
+
sensitiveHeaders: ['Authorization'],
|
|
326
|
+
sensitiveUrlPatterns: ['/auth/', '/login'],
|
|
327
|
+
sensitiveBodyFields: ['password', 'token'],
|
|
209
328
|
|
|
210
|
-
|
|
211
|
-
QALink.init({
|
|
212
|
-
apiKey: 'qlk_...',
|
|
213
|
-
appVersion: '1.2.3',
|
|
329
|
+
// ── Console ─────────────────────────────────────
|
|
214
330
|
console: {
|
|
215
331
|
captureLogs: true,
|
|
216
332
|
captureWarnings: true,
|
|
217
333
|
captureErrors: true,
|
|
218
|
-
|
|
334
|
+
ignorePatterns: ['Ignore this'],
|
|
335
|
+
includePatterns: [],
|
|
219
336
|
},
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
// En tu código:
|
|
223
|
-
console.log('[QA] Usuario llegó al checkout', { userId, cartTotal });
|
|
224
|
-
console.log('[FLOW] Pago procesado', { orderId });
|
|
225
|
-
```
|
|
226
337
|
|
|
227
|
-
|
|
338
|
+
// ── Errors ──────────────────────────────────────
|
|
339
|
+
captureRuntimeErrors: true, // red screen / yellow box
|
|
340
|
+
captureMetroErrors: true, // bundler errors
|
|
228
341
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
const apiPagos = axios.create({ baseURL: 'https://pagos.miapp.com' });
|
|
342
|
+
// ── Screen Capture ──────────────────────────────
|
|
343
|
+
enableScreenCapture: true, // requiere react-native-view-shot
|
|
232
344
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
345
|
+
// ── Callbacks ───────────────────────────────────
|
|
346
|
+
onEvent: (event) => {
|
|
347
|
+
console.log('Event sent:', event.type);
|
|
348
|
+
}
|
|
349
|
+
});
|
|
236
350
|
```
|
|
237
351
|
|
|
238
|
-
|
|
352
|
+
---
|
|
239
353
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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 |
|
|
249
372
|
|
|
250
|
-
|
|
373
|
+
---
|
|
251
374
|
|
|
252
|
-
|
|
253
|
-
const handleConfirmarCompra = async () => {
|
|
254
|
-
QALink.addBreadcrumb('TAP → Confirmar Compra', {
|
|
255
|
-
cartId: cart.id,
|
|
256
|
-
items: cart.items.length,
|
|
257
|
-
total: cart.total,
|
|
258
|
-
});
|
|
375
|
+
## 🔐 Compatibilidad con fetch-blob
|
|
259
376
|
|
|
260
|
-
|
|
261
|
-
try {
|
|
262
|
-
const result = await apiPagos.post('/checkout', { cartId: cart.id });
|
|
263
|
-
console.log('[QA] Checkout exitoso', { orderId: result.data.orderId });
|
|
264
|
-
} catch (error) {
|
|
265
|
-
console.error('[QA] Checkout falló', error);
|
|
266
|
-
}
|
|
267
|
-
};
|
|
268
|
-
```
|
|
377
|
+
El interceptor de fetch es **100% compatible** con `fetch-blob`:
|
|
269
378
|
|
|
270
|
-
|
|
379
|
+
```typescript
|
|
380
|
+
import { fetch, Blob } from 'react-native-fetch-blob';
|
|
271
381
|
|
|
272
|
-
|
|
382
|
+
// QALink detecta automáticamente Blobs y no intenta serializarlos
|
|
383
|
+
const blob = new Blob([...], { type: 'image/jpeg' });
|
|
273
384
|
|
|
274
|
-
|
|
275
|
-
QALink.logRequest({
|
|
385
|
+
await fetch('https://api.example.com/upload', {
|
|
276
386
|
method: 'POST',
|
|
277
|
-
|
|
278
|
-
statusCode: 200,
|
|
279
|
-
requestPayload: { items: [1, 2, 3] },
|
|
280
|
-
responsePayload: { orderId: 'abc123' },
|
|
281
|
-
durationMs: 450,
|
|
387
|
+
body: blob
|
|
282
388
|
});
|
|
389
|
+
|
|
390
|
+
// En el dashboard verás:
|
|
391
|
+
// body: { _type: 'Blob', size: 12345, type: 'image/jpeg' }
|
|
283
392
|
```
|
|
284
393
|
|
|
285
|
-
|
|
394
|
+
---
|
|
286
395
|
|
|
287
|
-
|
|
288
|
-
if (QALink.getStatus() === 'connected') {
|
|
289
|
-
console.log('QALink grabando. Device:', QALink.getDeviceId());
|
|
290
|
-
}
|
|
291
|
-
```
|
|
396
|
+
## 🎯 Casos de uso
|
|
292
397
|
|
|
293
|
-
###
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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');
|
|
404
|
+
|
|
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
|
|
312
418
|
});
|
|
313
|
-
QALink.
|
|
419
|
+
await QALink.captureScreen('payment_error');
|
|
314
420
|
}
|
|
315
421
|
```
|
|
316
422
|
|
|
317
|
-
|
|
423
|
+
### 2. Debugging de API lenta
|
|
424
|
+
```typescript
|
|
425
|
+
QALink.configureHTTP({
|
|
426
|
+
captureTimings: true,
|
|
427
|
+
slowRequestThreshold: 1500, // 1.5s
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
// Los requests > 1.5s aparecerán marcados en el dashboard
|
|
431
|
+
```
|
|
318
432
|
|
|
319
|
-
|
|
433
|
+
### 3. Tracking de experimentos A/B
|
|
434
|
+
```typescript
|
|
435
|
+
const variant = getExperimentVariant('new_ui');
|
|
320
436
|
|
|
321
|
-
|
|
437
|
+
QALink.setCustomContext({
|
|
438
|
+
experiment: 'new_ui',
|
|
439
|
+
variant: variant, // 'A' or 'B'
|
|
440
|
+
});
|
|
322
441
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
| 🟡 | Warning (RN) | LogBox / console |
|
|
329
|
-
| 🔵 | Log de consola | console.log |
|
|
330
|
-
| 📍 | Breadcrumb | `QALink.addBreadcrumb` |
|
|
331
|
-
| 📱 | Pantalla | `QALink.setScreen` |
|
|
442
|
+
QALink.logEvent('experiment_viewed', {
|
|
443
|
+
experiment: 'new_ui',
|
|
444
|
+
variant
|
|
445
|
+
});
|
|
446
|
+
```
|
|
332
447
|
|
|
333
448
|
---
|
|
334
449
|
|
|
335
|
-
##
|
|
450
|
+
## 🚨 Troubleshooting
|
|
336
451
|
|
|
337
|
-
|
|
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'`
|
|
338
456
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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()`
|
|
464
|
+
|
|
465
|
+
---
|
|
466
|
+
|
|
467
|
+
## 📖 Recursos
|
|
468
|
+
|
|
469
|
+
- **Dashboard:** https://qalink.airbites.org
|
|
470
|
+
- **Documentación completa:** (próximamente)
|
|
471
|
+
- **GitHub:** (próximamente)
|
|
349
472
|
|
|
350
473
|
---
|
|
351
474
|
|
|
352
|
-
## Licencia
|
|
475
|
+
## 📄 Licencia
|
|
353
476
|
|
|
354
477
|
MIT
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
## 🤝 Contribuir
|
|
482
|
+
|
|
483
|
+
Pull requests son bienvenidos. Para cambios mayores, abre primero un issue.
|