react-native-qalink 0.6.1 → 0.7.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 +127 -1
- package/dist/core/MiddlewarePipeline.d.ts +37 -0
- package/dist/core/MiddlewarePipeline.d.ts.map +1 -0
- package/dist/core/MiddlewarePipeline.js +58 -0
- package/dist/core/MiddlewarePipeline.js.map +1 -0
- package/dist/core/OfflineQueue.d.ts +48 -0
- package/dist/core/OfflineQueue.d.ts.map +1 -0
- package/dist/core/OfflineQueue.js +145 -0
- package/dist/core/OfflineQueue.js.map +1 -0
- package/dist/core/session.d.ts +2 -0
- package/dist/core/session.d.ts.map +1 -1
- package/dist/core/session.js +9 -0
- package/dist/core/session.js.map +1 -1
- package/dist/index.d.ts +79 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +160 -28
- package/dist/index.js.map +1 -1
- package/dist/trackers/NavigationLoopTracker.d.ts +31 -0
- package/dist/trackers/NavigationLoopTracker.d.ts.map +1 -0
- package/dist/trackers/NavigationLoopTracker.js +73 -0
- package/dist/trackers/NavigationLoopTracker.js.map +1 -0
- package/dist/trackers/RageClickTracker.d.ts +33 -0
- package/dist/trackers/RageClickTracker.d.ts.map +1 -0
- package/dist/trackers/RageClickTracker.js +56 -0
- package/dist/trackers/RageClickTracker.js.map +1 -0
- package/dist/trackers/SilentFailureTracker.d.ts +55 -0
- package/dist/trackers/SilentFailureTracker.d.ts.map +1 -0
- package/dist/trackers/SilentFailureTracker.js +84 -0
- package/dist/trackers/SilentFailureTracker.js.map +1 -0
- package/dist/transport/websocket.d.ts +18 -9
- package/dist/transport/websocket.d.ts.map +1 -1
- package/dist/transport/websocket.js +66 -30
- package/dist/transport/websocket.js.map +1 -1
- package/dist/types/index.d.ts +90 -10
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -351,6 +351,122 @@ QALink.init({
|
|
|
351
351
|
|
|
352
352
|
---
|
|
353
353
|
|
|
354
|
+
## 🧠Advanced QA Features (v0.7.0)
|
|
355
|
+
|
|
356
|
+
### Rage Click Detection
|
|
357
|
+
|
|
358
|
+
Detects when a user taps the same element multiple times rapidly — a strong frustration signal.
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
// 1. Enable (once, after init)
|
|
362
|
+
QALink.enableRageClickDetection({ threshold: 3, windowMs: 600 });
|
|
363
|
+
|
|
364
|
+
// 2. Call from any onPress handler
|
|
365
|
+
<Button
|
|
366
|
+
onPress={() => {
|
|
367
|
+
QALink.trackUIInteraction('checkout-submit-btn');
|
|
368
|
+
handleSubmit();
|
|
369
|
+
}}
|
|
370
|
+
/>
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
A `rage_click` event fires in the dashboard when the threshold is hit.
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
### Navigation Loop Detection
|
|
378
|
+
|
|
379
|
+
Detects when the user bounces back and forth between the same two screens (A→B→A→B…). Signals broken back navigation or an unresolved error state.
|
|
380
|
+
|
|
381
|
+
```typescript
|
|
382
|
+
// Enable (once, after init)
|
|
383
|
+
QALink.enableNavigationLoopDetection(3); // fires after 3 alternations
|
|
384
|
+
|
|
385
|
+
// Automatically triggered by setScreen() and startPackage()
|
|
386
|
+
QALink.setScreen('CheckoutScreen');
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
A `navigation_loop` event fires in the dashboard with the full navigation pattern.
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
### Silent Failure Detection
|
|
394
|
+
|
|
395
|
+
The hardest QA bug: API returns 200 OK but the data is null/empty. No error, no crash — the app just silently breaks.
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
const orders = await fetchOrders(userId); // 200 OK
|
|
399
|
+
|
|
400
|
+
QALink.trackDataState({
|
|
401
|
+
context: 'orders_list',
|
|
402
|
+
expected: Array.isArray(orders) && orders.length > 0,
|
|
403
|
+
actual: orders,
|
|
404
|
+
statusCode: 200,
|
|
405
|
+
url: '/api/orders',
|
|
406
|
+
});
|
|
407
|
+
// If orders is [] → fires a silent_failure event in the dashboard
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Returns `true` if the check passes, `false` if a failure was detected.
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
### Middleware (Event Processing Pipeline)
|
|
415
|
+
|
|
416
|
+
Intercept, enrich, filter, or transform any event before it's sent.
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
// Drop noisy console_log events in production
|
|
420
|
+
QALink.use((event, next) => {
|
|
421
|
+
if (event.type === 'console_log') return; // drop
|
|
422
|
+
next(event);
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
// Add a custom tag to every event
|
|
426
|
+
QALink.use((event, next) => {
|
|
427
|
+
next({ ...event, _release: '2.1.0' } as typeof event);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
// Log every event locally for debugging
|
|
431
|
+
QALink.use((event, next) => {
|
|
432
|
+
console.log('[QALink middleware]', event.type);
|
|
433
|
+
next(event);
|
|
434
|
+
});
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
Middlewares run in insertion order. Calling `next` is optional — skipping it drops the event.
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
### Offline-First Queue
|
|
442
|
+
|
|
443
|
+
Events are buffered automatically when the connection drops. Auto-flush triggers:
|
|
444
|
+
|
|
445
|
+
```typescript
|
|
446
|
+
QALink.init({
|
|
447
|
+
apiKey: 'qlk_...',
|
|
448
|
+
appVersion: '1.0.0',
|
|
449
|
+
offlineQueue: {
|
|
450
|
+
maxSize: 500, // max buffered events (drops oldest when full)
|
|
451
|
+
flushOnCount: 20, // auto-flush when 20 events are buffered
|
|
452
|
+
flushIntervalMs: 30_000, // auto-flush every 30s
|
|
453
|
+
},
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
// Manual flush (e.g. before app goes to background)
|
|
457
|
+
QALink.flush();
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
If `@react-native-async-storage/async-storage` is installed, events also persist across app restarts.
|
|
461
|
+
|
|
462
|
+
---
|
|
463
|
+
|
|
464
|
+
### Sequence Numbers
|
|
465
|
+
|
|
466
|
+
Every envelope sent to the dashboard now includes a `sequenceNumber` — a monotonically increasing counter per session. The dashboard can use it to detect out-of-order or missing events.
|
|
467
|
+
|
|
468
|
+
---
|
|
469
|
+
|
|
354
470
|
## 📦 Event Packages (v0.6.0)
|
|
355
471
|
|
|
356
472
|
Los **Event Packages** agrupan **todos los eventos** de una pantalla en un solo payload que se envÃa cuando el usuario sale de ella. Incluyen métricas pre-calculadas y el historial completo de eventos.
|
|
@@ -421,6 +537,9 @@ El SDK captura automáticamente:
|
|
|
421
537
|
|------|-------------|
|
|
422
538
|
| `session_start` | Inicio de sesión |
|
|
423
539
|
| `event_package` | Batch de eventos de una pantalla (v0.6.0) |
|
|
540
|
+
| `rage_click` | Usuario tapeó el mismo elemento N veces (v0.7.0) |
|
|
541
|
+
| `navigation_loop` | Usuario rebotando entre mismas pantallas (v0.7.0) |
|
|
542
|
+
| `silent_failure` | API 200 OK con datos inválidos/vacÃos (v0.7.0) |
|
|
424
543
|
| `http_request` | Request HTTP (fetch/axios) |
|
|
425
544
|
| `user_log` | Logs con debug/info/warn/error/critical |
|
|
426
545
|
| `custom_event` | Eventos custom con logEvent() |
|
|
@@ -550,6 +669,7 @@ import { QALink } from 'react-native-qalink';
|
|
|
550
669
|
|--------|-------------|
|
|
551
670
|
| **`init(config)`** | Inicializa el SDK. Requerido antes de todo. |
|
|
552
671
|
| **`interceptAxios(instance)`** | Registra una instancia de Axios. |
|
|
672
|
+
| **`use(middleware)`** | Agrega un middleware al pipeline de eventos. |
|
|
553
673
|
| **`setUserContext(ctx)`** | Adjunta datos del usuario a todos los eventos. |
|
|
554
674
|
| **`setCustomContext(ctx)`** | Adjunta datos custom a todos los eventos. |
|
|
555
675
|
| **`clearContext()`** | Limpia usuario y contexto custom. |
|
|
@@ -561,14 +681,20 @@ import { QALink } from 'react-native-qalink';
|
|
|
561
681
|
| **`logEvent(name, data?)`** | Evento custom de negocio. |
|
|
562
682
|
| **`addBreadcrumb(action, data?)`** | Breadcrumb manual. |
|
|
563
683
|
| **`logRequest(options)`** | Registra request de red manualmente. |
|
|
564
|
-
| **`setScreen(name)`** | Cambia la pantalla actual
|
|
684
|
+
| **`setScreen(name)`** | Cambia la pantalla actual + breadcrumb + loop detection. |
|
|
565
685
|
| **`startPackage(screenName)`** | Inicia un Event Package para esa pantalla. |
|
|
566
686
|
| **`endPackage()`** | Cierra el package activo y lo envÃa. |
|
|
687
|
+
| **`flush()`** | EnvÃa inmediatamente todos los eventos en cola offline. |
|
|
688
|
+
| **`enableRageClickDetection(options?)`** | Activa detección de rage clicks. |
|
|
689
|
+
| **`trackUIInteraction(target)`** | Registra tap para rage click. |
|
|
690
|
+
| **`enableNavigationLoopDetection(threshold?)`** | Activa detección de loops de navegación. |
|
|
691
|
+
| **`trackDataState(check)`** | Detecta silent failures (API 200 con datos inválidos). |
|
|
567
692
|
| **`captureScreen(label?)`** | Captura la pantalla completa. |
|
|
568
693
|
| **`captureRef(ref, label?)`** | Captura un componente especÃfico. |
|
|
569
694
|
| **`configureHTTP(config)`** | Actualiza config HTTP en runtime. |
|
|
570
695
|
| **`getDeviceId()`** | Devuelve el deviceId de la sesión. |
|
|
571
696
|
| **`getStatus()`** | Estado de la conexión WebSocket. |
|
|
697
|
+
| **`getQueueSize()`** | Número de eventos en cola offline. |
|
|
572
698
|
| **`destroy()`** | Limpia interceptores y desconecta. |
|
|
573
699
|
|
|
574
700
|
**Hook:**
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MiddlewarePipeline — chain of event processors applied before sending.
|
|
3
|
+
*
|
|
4
|
+
* Each middleware receives an event and a `next` callback:
|
|
5
|
+
* - Call `next(event)` to pass the (optionally modified) event forward.
|
|
6
|
+
* - Don't call `next` to drop the event entirely.
|
|
7
|
+
* - Mutate or replace the event to enrich/transform it.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* pipeline.use((event, next) => {
|
|
11
|
+
* // Drop noisy console logs in production
|
|
12
|
+
* if (event.type === 'console_log') return;
|
|
13
|
+
* next(event);
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* pipeline.use((event, next) => {
|
|
18
|
+
* // Enrich every event with a custom tag
|
|
19
|
+
* next({ ...event, _tag: 'v2' } as typeof event);
|
|
20
|
+
* });
|
|
21
|
+
*/
|
|
22
|
+
import type { QALinkEvent } from '../types';
|
|
23
|
+
export type EventMiddleware = (event: QALinkEvent, next: (event: QALinkEvent) => void) => void;
|
|
24
|
+
export declare class MiddlewarePipeline {
|
|
25
|
+
private readonly middlewares;
|
|
26
|
+
/** Register a middleware. Middlewares run in insertion order. */
|
|
27
|
+
use(middleware: EventMiddleware): this;
|
|
28
|
+
/** Clear all registered middlewares. */
|
|
29
|
+
clear(): void;
|
|
30
|
+
get count(): number;
|
|
31
|
+
/**
|
|
32
|
+
* Run `event` through the full middleware chain.
|
|
33
|
+
* `finalHandler` is called only if every middleware calls `next`.
|
|
34
|
+
*/
|
|
35
|
+
process(event: QALinkEvent, finalHandler: (event: QALinkEvent) => void): void;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=MiddlewarePipeline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MiddlewarePipeline.d.ts","sourceRoot":"","sources":["../../src/core/MiddlewarePipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,MAAM,MAAM,eAAe,GAAG,CAC5B,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,KAC/B,IAAI,CAAC;AAEV,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAyB;IAErD,iEAAiE;IACjE,GAAG,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI;IAKtC,wCAAwC;IACxC,KAAK,IAAI,IAAI;IAIb,IAAI,KAAK,IAAI,MAAM,CAElB;IAED;;;OAGG;IACH,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI;CAa9E"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MiddlewarePipeline — chain of event processors applied before sending.
|
|
4
|
+
*
|
|
5
|
+
* Each middleware receives an event and a `next` callback:
|
|
6
|
+
* - Call `next(event)` to pass the (optionally modified) event forward.
|
|
7
|
+
* - Don't call `next` to drop the event entirely.
|
|
8
|
+
* - Mutate or replace the event to enrich/transform it.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* pipeline.use((event, next) => {
|
|
12
|
+
* // Drop noisy console logs in production
|
|
13
|
+
* if (event.type === 'console_log') return;
|
|
14
|
+
* next(event);
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* pipeline.use((event, next) => {
|
|
19
|
+
* // Enrich every event with a custom tag
|
|
20
|
+
* next({ ...event, _tag: 'v2' } as typeof event);
|
|
21
|
+
* });
|
|
22
|
+
*/
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.MiddlewarePipeline = void 0;
|
|
25
|
+
class MiddlewarePipeline {
|
|
26
|
+
constructor() {
|
|
27
|
+
this.middlewares = [];
|
|
28
|
+
}
|
|
29
|
+
/** Register a middleware. Middlewares run in insertion order. */
|
|
30
|
+
use(middleware) {
|
|
31
|
+
this.middlewares.push(middleware);
|
|
32
|
+
return this;
|
|
33
|
+
}
|
|
34
|
+
/** Clear all registered middlewares. */
|
|
35
|
+
clear() {
|
|
36
|
+
this.middlewares.length = 0;
|
|
37
|
+
}
|
|
38
|
+
get count() {
|
|
39
|
+
return this.middlewares.length;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Run `event` through the full middleware chain.
|
|
43
|
+
* `finalHandler` is called only if every middleware calls `next`.
|
|
44
|
+
*/
|
|
45
|
+
process(event, finalHandler) {
|
|
46
|
+
const chain = this.middlewares;
|
|
47
|
+
const run = (index, current) => {
|
|
48
|
+
if (index >= chain.length) {
|
|
49
|
+
finalHandler(current);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
chain[index](current, (next) => run(index + 1, next));
|
|
53
|
+
};
|
|
54
|
+
run(0, event);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.MiddlewarePipeline = MiddlewarePipeline;
|
|
58
|
+
//# sourceMappingURL=MiddlewarePipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MiddlewarePipeline.js","sourceRoot":"","sources":["../../src/core/MiddlewarePipeline.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;;AASH,MAAa,kBAAkB;IAA/B;QACmB,gBAAW,GAAsB,EAAE,CAAC;IAkCvD,CAAC;IAhCC,iEAAiE;IACjE,GAAG,CAAC,UAA2B;QAC7B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,wCAAwC;IACxC,KAAK;QACH,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC;IAC9B,CAAC;IAED,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;IACjC,CAAC;IAED;;;OAGG;IACH,OAAO,CAAC,KAAkB,EAAE,YAA0C;QACpE,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC;QAE/B,MAAM,GAAG,GAAG,CAAC,KAAa,EAAE,OAAoB,EAAQ,EAAE;YACxD,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBAC1B,YAAY,CAAC,OAAO,CAAC,CAAC;gBACtB,OAAO;YACT,CAAC;YACD,KAAK,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;QACxD,CAAC,CAAC;QAEF,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAChB,CAAC;CACF;AAnCD,gDAmCC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OfflineQueue — FIFO event queue with optional AsyncStorage persistence.
|
|
3
|
+
*
|
|
4
|
+
* - Falls back to pure in-memory when AsyncStorage is not installed.
|
|
5
|
+
* - Respects a max-size limit (drops oldest event when full).
|
|
6
|
+
* - Supports two auto-flush triggers:
|
|
7
|
+
* · count-based: flush when queue reaches `flushOnCount` events.
|
|
8
|
+
* · time-based: flush every `flushIntervalMs` milliseconds.
|
|
9
|
+
* - Persists across app restarts when AsyncStorage is available.
|
|
10
|
+
*/
|
|
11
|
+
import type { QALinkEvent } from '../types';
|
|
12
|
+
export interface OfflineQueueConfig {
|
|
13
|
+
maxSize?: number;
|
|
14
|
+
/** Trigger flush when this many events are buffered. @default 20 */
|
|
15
|
+
flushOnCount?: number;
|
|
16
|
+
/** Trigger flush every N milliseconds. @default 30000 */
|
|
17
|
+
flushIntervalMs?: number;
|
|
18
|
+
debug?: boolean;
|
|
19
|
+
}
|
|
20
|
+
type FlushCallback = (events: QALinkEvent[]) => void;
|
|
21
|
+
export declare class OfflineQueue {
|
|
22
|
+
private memory;
|
|
23
|
+
private readonly maxSize;
|
|
24
|
+
private readonly flushOnCount;
|
|
25
|
+
private readonly flushIntervalMs;
|
|
26
|
+
private readonly debug;
|
|
27
|
+
private flushTimer;
|
|
28
|
+
private onFlush;
|
|
29
|
+
private storage;
|
|
30
|
+
constructor(config?: OfflineQueueConfig);
|
|
31
|
+
private tryBindAsyncStorage;
|
|
32
|
+
setFlushCallback(cb: FlushCallback): void;
|
|
33
|
+
startAutoFlush(): void;
|
|
34
|
+
stopAutoFlush(): void;
|
|
35
|
+
push(event: QALinkEvent): Promise<void>;
|
|
36
|
+
/** Remove and return all buffered events. Clears persistence. */
|
|
37
|
+
drain(): QALinkEvent[];
|
|
38
|
+
peek(): readonly QALinkEvent[];
|
|
39
|
+
get size(): number;
|
|
40
|
+
/** Load events persisted from a previous session and merge into current queue. */
|
|
41
|
+
loadPersisted(): Promise<void>;
|
|
42
|
+
private persist;
|
|
43
|
+
private clearPersisted;
|
|
44
|
+
private triggerFlush;
|
|
45
|
+
private log;
|
|
46
|
+
}
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=OfflineQueue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OfflineQueue.d.ts","sourceRoot":"","sources":["../../src/core/OfflineQueue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAO5C,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yDAAyD;IACzD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,KAAK,aAAa,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,IAAI,CAAC;AAQrD,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAU;IAChC,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,OAAO,CAAoC;gBAEvC,MAAM,GAAE,kBAAuB;IAU3C,OAAO,CAAC,mBAAmB;IAa3B,gBAAgB,CAAC,EAAE,EAAE,aAAa,GAAG,IAAI;IAMzC,cAAc,IAAI,IAAI;IAUtB,aAAa,IAAI,IAAI;IASf,IAAI,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAc7C,iEAAiE;IACjE,KAAK,IAAI,WAAW,EAAE;IAOtB,IAAI,IAAI,SAAS,WAAW,EAAE;IAI9B,IAAI,IAAI,IAAI,MAAM,CAEjB;IAID,kFAAkF;IAC5E,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;YAwBtB,OAAO;IASrB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,GAAG;CAGZ"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OfflineQueue — FIFO event queue with optional AsyncStorage persistence.
|
|
4
|
+
*
|
|
5
|
+
* - Falls back to pure in-memory when AsyncStorage is not installed.
|
|
6
|
+
* - Respects a max-size limit (drops oldest event when full).
|
|
7
|
+
* - Supports two auto-flush triggers:
|
|
8
|
+
* · count-based: flush when queue reaches `flushOnCount` events.
|
|
9
|
+
* · time-based: flush every `flushIntervalMs` milliseconds.
|
|
10
|
+
* - Persists across app restarts when AsyncStorage is available.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.OfflineQueue = void 0;
|
|
14
|
+
const STORAGE_KEY = '__qalink_offline_queue__';
|
|
15
|
+
const DEFAULT_MAX_SIZE = 500;
|
|
16
|
+
const DEFAULT_FLUSH_COUNT = 20;
|
|
17
|
+
const DEFAULT_FLUSH_INTERVAL_MS = 30000;
|
|
18
|
+
class OfflineQueue {
|
|
19
|
+
constructor(config = {}) {
|
|
20
|
+
var _a, _b, _c, _d;
|
|
21
|
+
this.memory = [];
|
|
22
|
+
this.flushTimer = null;
|
|
23
|
+
this.onFlush = null;
|
|
24
|
+
this.storage = null;
|
|
25
|
+
this.maxSize = (_a = config.maxSize) !== null && _a !== void 0 ? _a : DEFAULT_MAX_SIZE;
|
|
26
|
+
this.flushOnCount = (_b = config.flushOnCount) !== null && _b !== void 0 ? _b : DEFAULT_FLUSH_COUNT;
|
|
27
|
+
this.flushIntervalMs = (_c = config.flushIntervalMs) !== null && _c !== void 0 ? _c : DEFAULT_FLUSH_INTERVAL_MS;
|
|
28
|
+
this.debug = (_d = config.debug) !== null && _d !== void 0 ? _d : false;
|
|
29
|
+
this.tryBindAsyncStorage();
|
|
30
|
+
}
|
|
31
|
+
// ─── Setup ─────────────────────────────────────────────────────────────────
|
|
32
|
+
tryBindAsyncStorage() {
|
|
33
|
+
try {
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
35
|
+
const AS = require('@react-native-async-storage/async-storage').default;
|
|
36
|
+
if (AS && typeof AS.getItem === 'function') {
|
|
37
|
+
this.storage = AS;
|
|
38
|
+
this.log('AsyncStorage persistence enabled');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch (_a) {
|
|
42
|
+
this.log('AsyncStorage unavailable — in-memory queue only');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
setFlushCallback(cb) {
|
|
46
|
+
this.onFlush = cb;
|
|
47
|
+
}
|
|
48
|
+
// ─── Auto-flush lifecycle ──────────────────────────────────────────────────
|
|
49
|
+
startAutoFlush() {
|
|
50
|
+
if (this.flushTimer)
|
|
51
|
+
return;
|
|
52
|
+
this.flushTimer = setInterval(() => {
|
|
53
|
+
if (this.memory.length > 0) {
|
|
54
|
+
this.log(`Auto-flush (time) — ${this.memory.length} events`);
|
|
55
|
+
this.triggerFlush();
|
|
56
|
+
}
|
|
57
|
+
}, this.flushIntervalMs);
|
|
58
|
+
}
|
|
59
|
+
stopAutoFlush() {
|
|
60
|
+
if (this.flushTimer) {
|
|
61
|
+
clearInterval(this.flushTimer);
|
|
62
|
+
this.flushTimer = null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// ─── Queue operations ──────────────────────────────────────────────────────
|
|
66
|
+
async push(event) {
|
|
67
|
+
if (this.memory.length >= this.maxSize) {
|
|
68
|
+
this.memory.shift();
|
|
69
|
+
this.log('Queue full — dropped oldest event');
|
|
70
|
+
}
|
|
71
|
+
this.memory.push(event);
|
|
72
|
+
await this.persist();
|
|
73
|
+
if (this.memory.length >= this.flushOnCount) {
|
|
74
|
+
this.log(`Auto-flush (count) — ${this.memory.length} events`);
|
|
75
|
+
this.triggerFlush();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Remove and return all buffered events. Clears persistence. */
|
|
79
|
+
drain() {
|
|
80
|
+
const events = [...this.memory];
|
|
81
|
+
this.memory = [];
|
|
82
|
+
this.clearPersisted();
|
|
83
|
+
return events;
|
|
84
|
+
}
|
|
85
|
+
peek() {
|
|
86
|
+
return this.memory;
|
|
87
|
+
}
|
|
88
|
+
get size() {
|
|
89
|
+
return this.memory.length;
|
|
90
|
+
}
|
|
91
|
+
// ─── Persistence ───────────────────────────────────────────────────────────
|
|
92
|
+
/** Load events persisted from a previous session and merge into current queue. */
|
|
93
|
+
async loadPersisted() {
|
|
94
|
+
if (!this.storage)
|
|
95
|
+
return;
|
|
96
|
+
try {
|
|
97
|
+
const raw = await this.storage.getItem(STORAGE_KEY);
|
|
98
|
+
if (!raw)
|
|
99
|
+
return;
|
|
100
|
+
const parsed = JSON.parse(raw);
|
|
101
|
+
if (!Array.isArray(parsed))
|
|
102
|
+
return;
|
|
103
|
+
const existingIds = new Set(this.memory.map((e) => e.id).filter(Boolean));
|
|
104
|
+
for (const ev of parsed) {
|
|
105
|
+
const id = ev.id;
|
|
106
|
+
if (!id || !existingIds.has(id)) {
|
|
107
|
+
this.memory.push(ev);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
this.log(`Loaded ${parsed.length} persisted events from previous session`);
|
|
111
|
+
}
|
|
112
|
+
catch (_a) {
|
|
113
|
+
this.log('Failed to load persisted queue — starting fresh');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async persist() {
|
|
117
|
+
if (!this.storage)
|
|
118
|
+
return;
|
|
119
|
+
try {
|
|
120
|
+
await this.storage.setItem(STORAGE_KEY, JSON.stringify(this.memory));
|
|
121
|
+
}
|
|
122
|
+
catch (_a) {
|
|
123
|
+
// Ignore silently — persistence is best-effort
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
clearPersisted() {
|
|
127
|
+
var _a;
|
|
128
|
+
(_a = this.storage) === null || _a === void 0 ? void 0 : _a.removeItem(STORAGE_KEY).catch(() => { });
|
|
129
|
+
}
|
|
130
|
+
// ─── Flush trigger ─────────────────────────────────────────────────────────
|
|
131
|
+
triggerFlush() {
|
|
132
|
+
if (!this.onFlush)
|
|
133
|
+
return;
|
|
134
|
+
const events = this.drain();
|
|
135
|
+
if (events.length > 0)
|
|
136
|
+
this.onFlush(events);
|
|
137
|
+
}
|
|
138
|
+
// ─── Helpers ───────────────────────────────────────────────────────────────
|
|
139
|
+
log(...args) {
|
|
140
|
+
if (this.debug)
|
|
141
|
+
console.log('[QALink OfflineQueue]', ...args);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
exports.OfflineQueue = OfflineQueue;
|
|
145
|
+
//# sourceMappingURL=OfflineQueue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OfflineQueue.js","sourceRoot":"","sources":["../../src/core/OfflineQueue.ts"],"names":[],"mappings":"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"}
|
package/dist/core/session.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { DeviceInfo } from '../types';
|
|
|
2
2
|
export declare function generateId(): string;
|
|
3
3
|
export declare function getSessionId(): string;
|
|
4
4
|
export declare function resetSession(): string;
|
|
5
|
+
export declare function nextSequenceNumber(): number;
|
|
6
|
+
export declare function resetSequenceCounter(): void;
|
|
5
7
|
export declare function getDeviceInfo(appVersion: string): Promise<DeviceInfo>;
|
|
6
8
|
export declare function sanitizeBody(body: unknown, logBodies: boolean): unknown;
|
|
7
9
|
//# sourceMappingURL=session.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/core/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/core/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAKtC,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,YAAY,IAAI,MAAM,CAKrC;AAED,wBAAgB,YAAY,IAAI,MAAM,CAGrC;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAED,wBAAsB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAmB3E;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAGvE"}
|
package/dist/core/session.js
CHANGED
|
@@ -3,9 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.generateId = generateId;
|
|
4
4
|
exports.getSessionId = getSessionId;
|
|
5
5
|
exports.resetSession = resetSession;
|
|
6
|
+
exports.nextSequenceNumber = nextSequenceNumber;
|
|
7
|
+
exports.resetSequenceCounter = resetSequenceCounter;
|
|
6
8
|
exports.getDeviceInfo = getDeviceInfo;
|
|
7
9
|
exports.sanitizeBody = sanitizeBody;
|
|
8
10
|
let currentSessionId = null;
|
|
11
|
+
let sequenceCounter = 0;
|
|
9
12
|
function generateId() {
|
|
10
13
|
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
11
14
|
}
|
|
@@ -19,6 +22,12 @@ function resetSession() {
|
|
|
19
22
|
currentSessionId = generateId();
|
|
20
23
|
return currentSessionId;
|
|
21
24
|
}
|
|
25
|
+
function nextSequenceNumber() {
|
|
26
|
+
return ++sequenceCounter;
|
|
27
|
+
}
|
|
28
|
+
function resetSequenceCounter() {
|
|
29
|
+
sequenceCounter = 0;
|
|
30
|
+
}
|
|
22
31
|
async function getDeviceInfo(appVersion) {
|
|
23
32
|
var _a, _b;
|
|
24
33
|
try {
|
package/dist/core/session.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/core/session.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/core/session.ts"],"names":[],"mappings":";;AAKA,gCAEC;AAED,oCAKC;AAED,oCAGC;AAED,gDAEC;AAED,oDAEC;AAED,sCAmBC;AAED,oCAGC;AAnDD,IAAI,gBAAgB,GAAkB,IAAI,CAAC;AAC3C,IAAI,eAAe,GAAG,CAAC,CAAC;AAExB,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;AAED,SAAgB,kBAAkB;IAChC,OAAO,EAAE,eAAe,CAAC;AAC3B,CAAC;AAED,SAAgB,oBAAoB;IAClC,eAAe,GAAG,CAAC,CAAC;AACtB,CAAC;AAEM,KAAK,UAAU,aAAa,CAAC,UAAkB;;IACpD,IAAI,CAAC;QACH,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,OAAO,KAAK,WAAW,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;SAC3E,CAAC;IACJ,CAAC;IAAC,WAAM,CAAC;QACP,OAAO;YACL,QAAQ,EAAE,SAAS;YACnB,SAAS,EAAE,SAAS;YACpB,UAAU;YACV,WAAW,EAAE,SAAS;YACtB,SAAS,EAAE,SAAS;SACrB,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAgB,YAAY,CAAC,IAAa,EAAE,SAAkB;IAC5D,IAAI,CAAC,SAAS;QAAE,OAAO,0CAA0C,CAAC;IAClE,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { QALinkConfig, LogRequestOptions, UserContext, CustomContext, HTTPInterceptorConfig } from './types';
|
|
1
|
+
import { QALinkConfig, LogRequestOptions, UserContext, CustomContext, HTTPInterceptorConfig, DataStateCheck } from './types';
|
|
2
|
+
import { EventMiddleware } from './core/MiddlewarePipeline';
|
|
3
|
+
import { RageClickOptions } from './trackers/RageClickTracker';
|
|
2
4
|
declare class QALinkSDK {
|
|
3
5
|
private transport;
|
|
4
6
|
private config;
|
|
@@ -10,36 +12,98 @@ declare class QALinkSDK {
|
|
|
10
12
|
private logger;
|
|
11
13
|
private eventManager;
|
|
12
14
|
private screenCapture;
|
|
15
|
+
private rageClickTracker;
|
|
16
|
+
private navigationLoopTracker;
|
|
17
|
+
private silentFailureTracker;
|
|
13
18
|
init(config: QALinkConfig): Promise<void>;
|
|
14
|
-
interceptAxios(axiosInstance:
|
|
19
|
+
interceptAxios(axiosInstance: unknown): void;
|
|
20
|
+
/**
|
|
21
|
+
* Add an event middleware. Each middleware runs before an event is sent.
|
|
22
|
+
*
|
|
23
|
+
* @example — Drop console_log events in production:
|
|
24
|
+
* QALink.use((event, next) => {
|
|
25
|
+
* if (event.type === 'console_log') return; // drop
|
|
26
|
+
* next(event);
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* @example — Tag every event with a release ID:
|
|
30
|
+
* QALink.use((event, next) => next({ ...event, _release: '1.2.3' } as typeof event));
|
|
31
|
+
*/
|
|
32
|
+
use(middleware: EventMiddleware): void;
|
|
15
33
|
setUserContext(context: UserContext): void;
|
|
16
34
|
setCustomContext(context: CustomContext): void;
|
|
17
35
|
clearContext(): void;
|
|
18
|
-
debug(...args:
|
|
19
|
-
info(...args:
|
|
20
|
-
warn(...args:
|
|
21
|
-
error(...args:
|
|
22
|
-
critical(...args:
|
|
23
|
-
logEvent(eventName: string, data?:
|
|
36
|
+
debug(...args: unknown[]): void;
|
|
37
|
+
info(...args: unknown[]): void;
|
|
38
|
+
warn(...args: unknown[]): void;
|
|
39
|
+
error(...args: unknown[]): void;
|
|
40
|
+
critical(...args: unknown[]): void;
|
|
41
|
+
logEvent(eventName: string, data?: unknown): void;
|
|
24
42
|
captureScreen(label?: string): Promise<void>;
|
|
25
|
-
captureRef(viewRef:
|
|
43
|
+
captureRef(viewRef: unknown, label?: string): Promise<void>;
|
|
26
44
|
configureHTTP(httpConfig: Partial<HTTPInterceptorConfig>): void;
|
|
45
|
+
/**
|
|
46
|
+
* Manually flush all buffered offline events.
|
|
47
|
+
* Useful before the user puts the app in background.
|
|
48
|
+
*/
|
|
49
|
+
flush(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Enable rage-click detection.
|
|
52
|
+
* After enabling, call `QALink.trackUIInteraction(target)` from any onPress handler.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* QALink.enableRageClickDetection({ threshold: 3, windowMs: 600 });
|
|
56
|
+
* // then in a component:
|
|
57
|
+
* <Button onPress={() => { QALink.trackUIInteraction('submit-btn'); ... }} />
|
|
58
|
+
*/
|
|
59
|
+
enableRageClickDetection(options?: RageClickOptions): void;
|
|
60
|
+
/**
|
|
61
|
+
* Record a UI interaction (tap/press) for rage-click detection.
|
|
62
|
+
* Has no effect if `enableRageClickDetection()` was not called.
|
|
63
|
+
*
|
|
64
|
+
* @param target - Component label, testID, or any string that identifies the element.
|
|
65
|
+
*/
|
|
66
|
+
trackUIInteraction(target: string): void;
|
|
67
|
+
/**
|
|
68
|
+
* Enable navigation-loop detection.
|
|
69
|
+
* Automatically active when you call `setScreen()` or `startPackage()`.
|
|
70
|
+
*
|
|
71
|
+
* @param loopThreshold - How many back-and-forth alternations trigger the alert. @default 3
|
|
72
|
+
*/
|
|
73
|
+
enableNavigationLoopDetection(loopThreshold?: number): void;
|
|
74
|
+
/**
|
|
75
|
+
* Assert that API data meets expectations. Fires a `silent_failure` event
|
|
76
|
+
* when an API call succeeds (2xx) but returns invalid/empty/unexpected data.
|
|
77
|
+
*
|
|
78
|
+
* Returns `true` if the check passes, `false` if a silent failure was detected.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* const users = await fetchUsers(); // 200 OK
|
|
82
|
+
* QALink.trackDataState({
|
|
83
|
+
* context: 'users_list',
|
|
84
|
+
* expected: Array.isArray(users) && users.length > 0,
|
|
85
|
+
* actual: users,
|
|
86
|
+
* statusCode: 200,
|
|
87
|
+
* url: '/api/users',
|
|
88
|
+
* });
|
|
89
|
+
*/
|
|
90
|
+
trackDataState(check: DataStateCheck): boolean;
|
|
27
91
|
addBreadcrumb(action: string, data?: Record<string, unknown>): void;
|
|
28
92
|
logRequest(options: LogRequestOptions): void;
|
|
29
93
|
setScreen(screenName: string): void;
|
|
30
94
|
/**
|
|
31
95
|
* Start grouping all subsequent events for `screenName` into an in-memory package.
|
|
32
|
-
* Call
|
|
33
|
-
*
|
|
96
|
+
* Call when a screen mounts. Pair with endPackage() on unmount.
|
|
97
|
+
* `useTrackedScreen(screenName)` does both automatically.
|
|
34
98
|
*/
|
|
35
99
|
startPackage(screenName: string): void;
|
|
36
100
|
/**
|
|
37
|
-
* Close the active package, compute metrics and send
|
|
38
|
-
* All buffered events are included; nothing is lost.
|
|
101
|
+
* Close the active package, compute metrics, and send as a single EventPackage event.
|
|
39
102
|
*/
|
|
40
103
|
endPackage(): void;
|
|
41
104
|
getDeviceId(): string;
|
|
42
105
|
getStatus(): string;
|
|
106
|
+
getQueueSize(): number;
|
|
43
107
|
destroy(): void;
|
|
44
108
|
private log;
|
|
45
109
|
private validateRequiredConfig;
|
|
@@ -47,6 +111,8 @@ declare class QALinkSDK {
|
|
|
47
111
|
}
|
|
48
112
|
export declare const QALink: QALinkSDK;
|
|
49
113
|
export * from './types';
|
|
114
|
+
export type { EventMiddleware } from './core/MiddlewarePipeline';
|
|
115
|
+
export type { RageClickOptions } from './trackers/RageClickTracker';
|
|
50
116
|
export { getSourceLabel } from './core/classifier';
|
|
51
117
|
export { useTrackedScreen } from './hooks/useTrackedScreen';
|
|
52
118
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EAGZ,iBAAiB,EAGjB,WAAW,EACX,aAAa,EACb,qBAAqB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|