react-native-qalink 0.6.0 → 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 +258 -0
- 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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NavigationLoopTracker.js","sourceRoot":"","sources":["../../src/trackers/NavigationLoopTracker.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;;AAGH,6CAA2D;AAE3D,MAAM,sBAAsB,GAAG,CAAC,CAAC;AACjC,MAAM,WAAW,GAAG,IAAK,CAAC;AAI1B,MAAa,qBAAqB;IAQhC,YACE,MAAoB,EACpB,QAAgB,EAChB,aAAa,GAAG,sBAAsB;QAVhC,YAAO,GAAa,EAAE,CAAC;QAKvB,gBAAW,GAAG,CAAC,CAAC;QAOtB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,UAAU,GAAG,aAAa,GAAG,CAAC,GAAG,CAAC,CAAC;IAC1C,CAAC;IAED,oEAAoE;IACpE,MAAM,CAAC,UAAkB;QACvB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC9B,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YAC1C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACtD,CAAC;QACD,IAAI,CAAC,MAAM,EAAE,CAAC;IAChB,CAAC;IAEO,MAAM;QACZ,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,aAAa,GAAG,CAAC;YAAE,OAAO;QAEzD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,GAAG,GAAG,IAAI,CAAC,WAAW,GAAG,WAAW;YAAE,OAAO;QAEjD,MAAM,aAAa,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;QAEjD,+DAA+D;QAC/D,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAEvC,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7C,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC;gBAAE,YAAY,EAAE,CAAC;QAC9D,CAAC;QAED,IAAI,YAAY,IAAI,IAAI,CAAC,aAAa,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/C,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC;YACvB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IAEO,IAAI,CAAC,OAAiB,EAAE,YAAoB;QAClD,MAAM,KAAK,GAAwB;YACjC,EAAE,EAAE,IAAA,oBAAU,GAAE;YAChB,IAAI,EAAE,iBAAiB;YACvB,OAAO;YACP,YAAY;YACZ,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;YACjC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,SAAS,EAAE,IAAA,sBAAY,GAAE;YACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACxB,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;CACF;AA/DD,sDA+DC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RageClickTracker — detects when a user taps the same target rapidly multiple times.
|
|
3
|
+
*
|
|
4
|
+
* A "rage click" is ≥ `threshold` taps on the same target within `windowMs`.
|
|
5
|
+
* Fires a `rage_click` event that appears in the dashboard as a QA signal.
|
|
6
|
+
*
|
|
7
|
+
* Usage — call `recordTap(target)` from any onPress / onTouchEnd handler:
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* <Button onPress={() => { QALink.trackUIInteraction('submit-button'); handlePress(); }} />
|
|
11
|
+
*/
|
|
12
|
+
import type { RageClickEvent } from '../types';
|
|
13
|
+
export interface RageClickOptions {
|
|
14
|
+
/** Minimum number of taps to qualify as rage click. @default 3 */
|
|
15
|
+
threshold?: number;
|
|
16
|
+
/** Time window in ms within which taps must occur. @default 600 */
|
|
17
|
+
windowMs?: number;
|
|
18
|
+
}
|
|
19
|
+
type RageClickCallback = (event: RageClickEvent) => void;
|
|
20
|
+
export declare class RageClickTracker {
|
|
21
|
+
private taps;
|
|
22
|
+
private readonly threshold;
|
|
23
|
+
private readonly windowMs;
|
|
24
|
+
private readonly onRageClick;
|
|
25
|
+
private readonly getScreen;
|
|
26
|
+
private readonly deviceId;
|
|
27
|
+
constructor(onRageClick: RageClickCallback, getScreen: () => string, deviceId: string, options?: RageClickOptions);
|
|
28
|
+
/** Register a tap on `target`. Call from onPress / onTouchEnd. */
|
|
29
|
+
recordTap(target: string): void;
|
|
30
|
+
private fireRageClick;
|
|
31
|
+
}
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=RageClickTracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RageClickTracker.d.ts","sourceRoot":"","sources":["../../src/trackers/RageClickTracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAM/C,MAAM,WAAW,gBAAgB;IAC/B,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAOD,KAAK,iBAAiB,GAAG,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;AAEzD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,IAAI,CAAkB;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;IAChD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAGhC,WAAW,EAAE,iBAAiB,EAC9B,SAAS,EAAE,MAAM,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,gBAAqB;IAShC,kEAAkE;IAClE,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAgB/B,OAAO,CAAC,aAAa;CActB"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* RageClickTracker — detects when a user taps the same target rapidly multiple times.
|
|
4
|
+
*
|
|
5
|
+
* A "rage click" is ≥ `threshold` taps on the same target within `windowMs`.
|
|
6
|
+
* Fires a `rage_click` event that appears in the dashboard as a QA signal.
|
|
7
|
+
*
|
|
8
|
+
* Usage — call `recordTap(target)` from any onPress / onTouchEnd handler:
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* <Button onPress={() => { QALink.trackUIInteraction('submit-button'); handlePress(); }} />
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.RageClickTracker = void 0;
|
|
15
|
+
const session_1 = require("../core/session");
|
|
16
|
+
const DEFAULT_THRESHOLD = 3;
|
|
17
|
+
const DEFAULT_WINDOW_MS = 600;
|
|
18
|
+
class RageClickTracker {
|
|
19
|
+
constructor(onRageClick, getScreen, deviceId, options = {}) {
|
|
20
|
+
var _a, _b;
|
|
21
|
+
this.taps = [];
|
|
22
|
+
this.onRageClick = onRageClick;
|
|
23
|
+
this.getScreen = getScreen;
|
|
24
|
+
this.deviceId = deviceId;
|
|
25
|
+
this.threshold = (_a = options.threshold) !== null && _a !== void 0 ? _a : DEFAULT_THRESHOLD;
|
|
26
|
+
this.windowMs = (_b = options.windowMs) !== null && _b !== void 0 ? _b : DEFAULT_WINDOW_MS;
|
|
27
|
+
}
|
|
28
|
+
/** Register a tap on `target`. Call from onPress / onTouchEnd. */
|
|
29
|
+
recordTap(target) {
|
|
30
|
+
const now = Date.now();
|
|
31
|
+
// Keep only recent taps for this exact target within the time window
|
|
32
|
+
this.taps = this.taps.filter((t) => t.target === target && now - t.time <= this.windowMs);
|
|
33
|
+
this.taps.push({ time: now, target });
|
|
34
|
+
if (this.taps.length >= this.threshold) {
|
|
35
|
+
const count = this.taps.length;
|
|
36
|
+
this.taps = [];
|
|
37
|
+
this.fireRageClick(target, count);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
fireRageClick(target, count) {
|
|
41
|
+
const event = {
|
|
42
|
+
id: (0, session_1.generateId)(),
|
|
43
|
+
type: 'rage_click',
|
|
44
|
+
target,
|
|
45
|
+
count,
|
|
46
|
+
windowMs: this.windowMs,
|
|
47
|
+
screen: this.getScreen(),
|
|
48
|
+
timestamp: Date.now(),
|
|
49
|
+
sessionId: (0, session_1.getSessionId)(),
|
|
50
|
+
deviceId: this.deviceId,
|
|
51
|
+
};
|
|
52
|
+
this.onRageClick(event);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.RageClickTracker = RageClickTracker;
|
|
56
|
+
//# sourceMappingURL=RageClickTracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RageClickTracker.js","sourceRoot":"","sources":["../../src/trackers/RageClickTracker.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AAGH,6CAA2D;AAE3D,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAC5B,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAgB9B,MAAa,gBAAgB;IAQ3B,YACE,WAA8B,EAC9B,SAAuB,EACvB,QAAgB,EAChB,UAA4B,EAAE;;QAXxB,SAAI,GAAe,EAAE,CAAC;QAa5B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,SAAS,GAAG,MAAA,OAAO,CAAC,SAAS,mCAAI,iBAAiB,CAAC;QACxD,IAAI,CAAC,QAAQ,GAAG,MAAA,OAAO,CAAC,QAAQ,mCAAI,iBAAiB,CAAC;IACxD,CAAC;IAED,kEAAkE;IAClE,SAAS,CAAC,MAAc;QACtB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEvB,qEAAqE;QACrE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAC1B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,CAC5D,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;QAEtC,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACvC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;YAC/B,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;YACf,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAEO,aAAa,CAAC,MAAc,EAAE,KAAa;QACjD,MAAM,KAAK,GAAmB;YAC5B,EAAE,EAAE,IAAA,oBAAU,GAAE;YAChB,IAAI,EAAE,YAAY;YAClB,MAAM;YACN,KAAK;YACL,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE;YACxB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,SAAS,EAAE,IAAA,sBAAY,GAAE;YACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACxB,CAAC;QACF,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;CACF;AApDD,4CAoDC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SilentFailureTracker — detects API calls that succeed (2xx) but return invalid/empty data.
|
|
3
|
+
*
|
|
4
|
+
* The classic QA nightmare: no error thrown, no red screen — the app just quietly renders
|
|
5
|
+
* an empty state or shows stale data because the server returned something unexpected.
|
|
6
|
+
*
|
|
7
|
+
* Call `check()` after any API response to assert that the data meets expectations.
|
|
8
|
+
* Returns `true` if the check passes, `false` if a silent failure was detected and fired.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const user = await fetchUser(id); // 200 OK
|
|
12
|
+
* QALink.trackDataState({
|
|
13
|
+
* context: 'user_profile',
|
|
14
|
+
* expected: user?.id !== undefined, // what we expect to be true
|
|
15
|
+
* actual: user,
|
|
16
|
+
* statusCode: 200,
|
|
17
|
+
* url: '/api/users/' + id,
|
|
18
|
+
* });
|
|
19
|
+
*/
|
|
20
|
+
import type { SilentFailureEvent } from '../types';
|
|
21
|
+
export interface DataStateCheck {
|
|
22
|
+
/**
|
|
23
|
+
* Label for what you are validating (e.g. 'cart_items', 'user_profile').
|
|
24
|
+
* Shown in the dashboard as the failure context.
|
|
25
|
+
*/
|
|
26
|
+
context: string;
|
|
27
|
+
/**
|
|
28
|
+
* The expectation: pass `true` to indicate "this should be truthy".
|
|
29
|
+
* Can also pass the value directly — falsy values trigger a silent failure.
|
|
30
|
+
*/
|
|
31
|
+
expected: unknown;
|
|
32
|
+
/** The actual value received from the API. Used only for the summary log. */
|
|
33
|
+
actual?: unknown;
|
|
34
|
+
/** HTTP status code of the originating request. */
|
|
35
|
+
statusCode?: number;
|
|
36
|
+
/** Request URL for reference. */
|
|
37
|
+
url?: string;
|
|
38
|
+
}
|
|
39
|
+
type SilentFailureCallback = (event: SilentFailureEvent) => void;
|
|
40
|
+
export declare class SilentFailureTracker {
|
|
41
|
+
private readonly onFailure;
|
|
42
|
+
private readonly getScreen;
|
|
43
|
+
private readonly deviceId;
|
|
44
|
+
constructor(onFailure: SilentFailureCallback, getScreen: () => string, deviceId: string);
|
|
45
|
+
/**
|
|
46
|
+
* Evaluate `check.expected`. Returns `true` if it passes.
|
|
47
|
+
* Fires a `silent_failure` event and returns `false` if it fails.
|
|
48
|
+
*/
|
|
49
|
+
check(check: DataStateCheck): boolean;
|
|
50
|
+
private evaluate;
|
|
51
|
+
private fire;
|
|
52
|
+
private summarize;
|
|
53
|
+
}
|
|
54
|
+
export {};
|
|
55
|
+
//# sourceMappingURL=SilentFailureTracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SilentFailureTracker.d.ts","sourceRoot":"","sources":["../../src/trackers/SilentFailureTracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAGnD,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,mDAAmD;IACnD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,KAAK,qBAAqB,GAAG,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAEjE,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwB;IAClD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAGhC,SAAS,EAAE,qBAAqB,EAChC,SAAS,EAAE,MAAM,MAAM,EACvB,QAAQ,EAAE,MAAM;IAOlB;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO;IAMrC,OAAO,CAAC,QAAQ;IAUhB,OAAO,CAAC,IAAI;IAgBZ,OAAO,CAAC,SAAS;CASlB"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SilentFailureTracker — detects API calls that succeed (2xx) but return invalid/empty data.
|
|
4
|
+
*
|
|
5
|
+
* The classic QA nightmare: no error thrown, no red screen — the app just quietly renders
|
|
6
|
+
* an empty state or shows stale data because the server returned something unexpected.
|
|
7
|
+
*
|
|
8
|
+
* Call `check()` after any API response to assert that the data meets expectations.
|
|
9
|
+
* Returns `true` if the check passes, `false` if a silent failure was detected and fired.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const user = await fetchUser(id); // 200 OK
|
|
13
|
+
* QALink.trackDataState({
|
|
14
|
+
* context: 'user_profile',
|
|
15
|
+
* expected: user?.id !== undefined, // what we expect to be true
|
|
16
|
+
* actual: user,
|
|
17
|
+
* statusCode: 200,
|
|
18
|
+
* url: '/api/users/' + id,
|
|
19
|
+
* });
|
|
20
|
+
*/
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.SilentFailureTracker = void 0;
|
|
23
|
+
const session_1 = require("../core/session");
|
|
24
|
+
class SilentFailureTracker {
|
|
25
|
+
constructor(onFailure, getScreen, deviceId) {
|
|
26
|
+
this.onFailure = onFailure;
|
|
27
|
+
this.getScreen = getScreen;
|
|
28
|
+
this.deviceId = deviceId;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Evaluate `check.expected`. Returns `true` if it passes.
|
|
32
|
+
* Fires a `silent_failure` event and returns `false` if it fails.
|
|
33
|
+
*/
|
|
34
|
+
check(check) {
|
|
35
|
+
const passes = this.evaluate(check.expected);
|
|
36
|
+
if (!passes)
|
|
37
|
+
this.fire(check);
|
|
38
|
+
return passes;
|
|
39
|
+
}
|
|
40
|
+
evaluate(value) {
|
|
41
|
+
if (typeof value === 'boolean')
|
|
42
|
+
return value;
|
|
43
|
+
if (value === null || value === undefined)
|
|
44
|
+
return false;
|
|
45
|
+
if (Array.isArray(value))
|
|
46
|
+
return value.length > 0;
|
|
47
|
+
if (typeof value === 'string')
|
|
48
|
+
return value.trim().length > 0;
|
|
49
|
+
if (typeof value === 'number')
|
|
50
|
+
return !isNaN(value) && isFinite(value);
|
|
51
|
+
if (typeof value === 'object')
|
|
52
|
+
return Object.keys(value).length > 0;
|
|
53
|
+
return Boolean(value);
|
|
54
|
+
}
|
|
55
|
+
fire(check) {
|
|
56
|
+
const event = {
|
|
57
|
+
id: (0, session_1.generateId)(),
|
|
58
|
+
type: 'silent_failure',
|
|
59
|
+
context: check.context,
|
|
60
|
+
url: check.url,
|
|
61
|
+
statusCode: check.statusCode,
|
|
62
|
+
actualSummary: this.summarize(check.actual),
|
|
63
|
+
screen: this.getScreen(),
|
|
64
|
+
timestamp: Date.now(),
|
|
65
|
+
sessionId: (0, session_1.getSessionId)(),
|
|
66
|
+
deviceId: this.deviceId,
|
|
67
|
+
};
|
|
68
|
+
this.onFailure(event);
|
|
69
|
+
}
|
|
70
|
+
summarize(value) {
|
|
71
|
+
if (value === undefined)
|
|
72
|
+
return 'undefined';
|
|
73
|
+
if (value === null)
|
|
74
|
+
return 'null';
|
|
75
|
+
if (Array.isArray(value))
|
|
76
|
+
return `Array(${value.length})`;
|
|
77
|
+
if (typeof value === 'object') {
|
|
78
|
+
return `Object(${Object.keys(value).length} keys)`;
|
|
79
|
+
}
|
|
80
|
+
return String(value);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.SilentFailureTracker = SilentFailureTracker;
|
|
84
|
+
//# sourceMappingURL=SilentFailureTracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SilentFailureTracker.js","sourceRoot":"","sources":["../../src/trackers/SilentFailureTracker.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;GAkBG;;;AAGH,6CAA2D;AAuB3D,MAAa,oBAAoB;IAK/B,YACE,SAAgC,EAChC,SAAuB,EACvB,QAAgB;QAEhB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAqB;QACzB,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM;YAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,QAAQ,CAAC,KAAc;QAC7B,IAAI,OAAO,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAC7C,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QACxD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QAClD,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;QAC9D,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC;QACvE,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,KAAgC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;QAC/F,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC;IAEO,IAAI,CAAC,KAAqB;QAChC,MAAM,KAAK,GAAuB;YAChC,EAAE,EAAE,IAAA,oBAAU,GAAE;YAChB,IAAI,EAAE,gBAAgB;YACtB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC;YAC3C,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE;YACxB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,SAAS,EAAE,IAAA,sBAAY,GAAE;YACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACxB,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC;IAEO,SAAS,CAAC,KAAc;QAC9B,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,WAAW,CAAC;QAC5C,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,MAAM,CAAC;QAClC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,SAAS,KAAK,CAAC,MAAM,GAAG,CAAC;QAC1D,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,OAAO,UAAU,MAAM,CAAC,IAAI,CAAC,KAAgC,CAAC,CAAC,MAAM,QAAQ,CAAC;QAChF,CAAC;QACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;CACF;AA5DD,oDA4DC"}
|
|
@@ -1,30 +1,39 @@
|
|
|
1
|
-
import { QALinkEvent } from '../types';
|
|
1
|
+
import { QALinkEvent, OfflineQueueConfig } from '../types';
|
|
2
|
+
import { MiddlewarePipeline, EventMiddleware } from '../core/MiddlewarePipeline';
|
|
2
3
|
type TransportStatus = 'disconnected' | 'connecting' | 'connected' | 'error';
|
|
3
4
|
export declare class WebSocketTransport {
|
|
4
5
|
private ws;
|
|
5
|
-
private queue;
|
|
6
6
|
private status;
|
|
7
7
|
private reconnectTimer;
|
|
8
8
|
private reconnectAttempts;
|
|
9
9
|
private readonly maxReconnectAttempts;
|
|
10
10
|
private readonly reconnectBaseMs;
|
|
11
|
-
private debug;
|
|
12
|
-
private connectionUrl;
|
|
11
|
+
private readonly debug;
|
|
12
|
+
private readonly connectionUrl;
|
|
13
13
|
private readonly apiKey;
|
|
14
|
-
|
|
14
|
+
private readonly offlineQueue;
|
|
15
|
+
readonly middleware: MiddlewarePipeline;
|
|
16
|
+
constructor(connectionUrl: string, apiKey: string, debug?: boolean, queueConfig?: OfflineQueueConfig);
|
|
15
17
|
connect(): void;
|
|
16
18
|
disconnect(): void;
|
|
17
19
|
send(event: QALinkEvent): void;
|
|
20
|
+
/** Manually flush the offline queue — sends all buffered events immediately. */
|
|
21
|
+
flush(): void;
|
|
22
|
+
getStatus(): TransportStatus;
|
|
23
|
+
getQueueSize(): number;
|
|
24
|
+
use(middleware: EventMiddleware): void;
|
|
18
25
|
/**
|
|
19
|
-
* Build metadata
|
|
26
|
+
* Build envelope metadata. Always in clear — lets the dashboard route and
|
|
27
|
+
* display the event type without decrypting the payload.
|
|
20
28
|
*/
|
|
21
29
|
private envelopeMetadata;
|
|
22
30
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
31
|
+
* Encrypt the event body and send inside an envelope.
|
|
32
|
+
* Metadata (apiKey, sessionId, deviceId, timestamp, eventType, sequenceNumber)
|
|
33
|
+
* is always sent in plain — only the payload is encrypted.
|
|
34
|
+
* Falls back to plain envelope when crypto.subtle is unavailable.
|
|
25
35
|
*/
|
|
26
36
|
private encryptAndSend;
|
|
27
|
-
getStatus(): TransportStatus;
|
|
28
37
|
private flushQueue;
|
|
29
38
|
private scheduleReconnect;
|
|
30
39
|
private log;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../../src/transport/websocket.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../../src/transport/websocket.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EAIX,kBAAkB,EACnB,MAAM,UAAU,CAAC;AAIlB,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAGjF,KAAK,eAAe,GAAG,cAAc,GAAG,YAAY,GAAG,WAAW,GAAG,OAAO,CAAC;AAE7E,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,MAAM,CAAmC;IACjD,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAM;IAC3C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAQ;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAU;IAChC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAGhC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAG5C,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAA4B;gBAGjE,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,EACd,KAAK,UAAQ,EACb,WAAW,GAAE,kBAAuB;IActC,OAAO,IAAI,IAAI;IAyCf,UAAU,IAAI,IAAI;IAalB,IAAI,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAkB9B,gFAAgF;IAChF,KAAK,IAAI,IAAI;IAUb,SAAS,IAAI,eAAe;IAI5B,YAAY,IAAI,MAAM;IAMtB,GAAG,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI;IAMtC;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAmBxB;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IA6CtB,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,gBAAgB;CASzB"}
|
|
@@ -3,24 +3,37 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.WebSocketTransport = void 0;
|
|
4
4
|
const crypto_1 = require("../core/crypto");
|
|
5
5
|
const PackageManager_1 = require("../core/PackageManager");
|
|
6
|
+
const OfflineQueue_1 = require("../core/OfflineQueue");
|
|
7
|
+
const MiddlewarePipeline_1 = require("../core/MiddlewarePipeline");
|
|
8
|
+
const session_1 = require("../core/session");
|
|
6
9
|
class WebSocketTransport {
|
|
7
|
-
constructor(connectionUrl, apiKey, debug = false) {
|
|
10
|
+
constructor(connectionUrl, apiKey, debug = false, queueConfig = {}) {
|
|
8
11
|
this.ws = null;
|
|
9
|
-
this.queue = [];
|
|
10
12
|
this.status = 'disconnected';
|
|
11
13
|
this.reconnectTimer = null;
|
|
12
14
|
this.reconnectAttempts = 0;
|
|
13
15
|
this.maxReconnectAttempts = 10;
|
|
14
16
|
this.reconnectBaseMs = 1000;
|
|
17
|
+
// Middleware pipeline (applied before sending)
|
|
18
|
+
this.middleware = new MiddlewarePipeline_1.MiddlewarePipeline();
|
|
15
19
|
this.connectionUrl = connectionUrl;
|
|
16
20
|
this.apiKey = apiKey;
|
|
17
21
|
this.debug = debug;
|
|
22
|
+
this.offlineQueue = new OfflineQueue_1.OfflineQueue(Object.assign(Object.assign({}, queueConfig), { debug }));
|
|
23
|
+
this.offlineQueue.setFlushCallback((events) => {
|
|
24
|
+
events.forEach((ev) => this.encryptAndSend(ev));
|
|
25
|
+
});
|
|
18
26
|
}
|
|
27
|
+
// ─── Lifecycle ─────────────────────────────────────────────────────────────
|
|
19
28
|
connect() {
|
|
20
29
|
if (this.status === 'connecting' || this.status === 'connected')
|
|
21
30
|
return;
|
|
22
31
|
this.status = 'connecting';
|
|
23
32
|
this.log(`Connecting to ${this.maskedUrlForLogs()}...`);
|
|
33
|
+
// Load any events persisted from a previous session, then start auto-flush
|
|
34
|
+
this.offlineQueue.loadPersisted().then(() => {
|
|
35
|
+
this.offlineQueue.startAutoFlush();
|
|
36
|
+
});
|
|
24
37
|
try {
|
|
25
38
|
this.ws = new WebSocket(this.connectionUrl);
|
|
26
39
|
this.ws.onopen = () => {
|
|
@@ -49,6 +62,7 @@ class WebSocketTransport {
|
|
|
49
62
|
}
|
|
50
63
|
disconnect() {
|
|
51
64
|
var _a;
|
|
65
|
+
this.offlineQueue.stopAutoFlush();
|
|
52
66
|
if (this.reconnectTimer) {
|
|
53
67
|
clearTimeout(this.reconnectTimer);
|
|
54
68
|
this.reconnectTimer = null;
|
|
@@ -57,23 +71,49 @@ class WebSocketTransport {
|
|
|
57
71
|
this.ws = null;
|
|
58
72
|
this.status = 'disconnected';
|
|
59
73
|
}
|
|
74
|
+
// ─── Public: send + flush ──────────────────────────────────────────────────
|
|
60
75
|
send(event) {
|
|
61
|
-
var _a;
|
|
62
76
|
// If a package is active, buffer event instead of sending (except event_package itself).
|
|
63
77
|
if (event.type !== 'event_package' && PackageManager_1.packageManager.isActive()) {
|
|
64
78
|
PackageManager_1.packageManager.addEvent(event);
|
|
65
79
|
return;
|
|
66
80
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
81
|
+
// Run event through the middleware pipeline first
|
|
82
|
+
this.middleware.process(event, (processed) => {
|
|
83
|
+
var _a;
|
|
84
|
+
if (this.status === 'connected' && ((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
|
|
85
|
+
this.encryptAndSend(processed);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
this.offlineQueue.push(processed).catch(() => { });
|
|
89
|
+
this.log(`Queued event (${this.offlineQueue.size} pending): ${processed.type}`);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/** Manually flush the offline queue — sends all buffered events immediately. */
|
|
94
|
+
flush() {
|
|
95
|
+
var _a;
|
|
96
|
+
if (this.status !== 'connected' || ((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) !== WebSocket.OPEN) {
|
|
97
|
+
this.log('flush() called but not connected — events remain queued');
|
|
98
|
+
return;
|
|
73
99
|
}
|
|
100
|
+
this.flushQueue();
|
|
101
|
+
}
|
|
102
|
+
// ─── Status ────────────────────────────────────────────────────────────────
|
|
103
|
+
getStatus() {
|
|
104
|
+
return this.status;
|
|
105
|
+
}
|
|
106
|
+
getQueueSize() {
|
|
107
|
+
return this.offlineQueue.size;
|
|
108
|
+
}
|
|
109
|
+
// ─── Middleware shortcut ───────────────────────────────────────────────────
|
|
110
|
+
use(middleware) {
|
|
111
|
+
this.middleware.use(middleware);
|
|
74
112
|
}
|
|
113
|
+
// ─── Private: encrypt + send ───────────────────────────────────────────────
|
|
75
114
|
/**
|
|
76
|
-
* Build metadata
|
|
115
|
+
* Build envelope metadata. Always in clear — lets the dashboard route and
|
|
116
|
+
* display the event type without decrypting the payload.
|
|
77
117
|
*/
|
|
78
118
|
envelopeMetadata(event) {
|
|
79
119
|
var _a, _b, _c, _d;
|
|
@@ -84,11 +124,14 @@ class WebSocketTransport {
|
|
|
84
124
|
deviceId: (_b = ev.deviceId) !== null && _b !== void 0 ? _b : '',
|
|
85
125
|
timestamp: (_c = ev.timestamp) !== null && _c !== void 0 ? _c : Date.now(),
|
|
86
126
|
eventType: (_d = ev.type) !== null && _d !== void 0 ? _d : 'unknown',
|
|
127
|
+
sequenceNumber: (0, session_1.nextSequenceNumber)(),
|
|
87
128
|
};
|
|
88
129
|
}
|
|
89
130
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
131
|
+
* Encrypt the event body and send inside an envelope.
|
|
132
|
+
* Metadata (apiKey, sessionId, deviceId, timestamp, eventType, sequenceNumber)
|
|
133
|
+
* is always sent in plain — only the payload is encrypted.
|
|
134
|
+
* Falls back to plain envelope when crypto.subtle is unavailable.
|
|
92
135
|
*/
|
|
93
136
|
encryptAndSend(event) {
|
|
94
137
|
const meta = this.envelopeMetadata(event);
|
|
@@ -102,17 +145,16 @@ class WebSocketTransport {
|
|
|
102
145
|
};
|
|
103
146
|
if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
|
|
104
147
|
this.ws.send(JSON.stringify(envelope));
|
|
105
|
-
this.log(`Sent
|
|
148
|
+
this.log(`Sent (plain) [#${meta.sequenceNumber}]: ${event.type}`);
|
|
106
149
|
}
|
|
107
150
|
else {
|
|
108
|
-
this.
|
|
151
|
+
this.offlineQueue.push(event).catch(() => { });
|
|
109
152
|
}
|
|
110
153
|
};
|
|
111
154
|
if (!(0, crypto_1.isEncryptionAvailable)()) {
|
|
112
155
|
sendPlain();
|
|
113
156
|
return;
|
|
114
157
|
}
|
|
115
|
-
// Only encrypt the event body; metadata stays in clear in envelope
|
|
116
158
|
const plaintext = JSON.stringify(event);
|
|
117
159
|
(0, crypto_1.encrypt)(plaintext, this.apiKey)
|
|
118
160
|
.then((payload) => {
|
|
@@ -125,27 +167,23 @@ class WebSocketTransport {
|
|
|
125
167
|
};
|
|
126
168
|
if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
|
|
127
169
|
this.ws.send(JSON.stringify(envelope));
|
|
128
|
-
this.log(`Sent
|
|
170
|
+
this.log(`Sent (encrypted) [#${meta.sequenceNumber}]: ${event.type}`);
|
|
129
171
|
}
|
|
130
172
|
else {
|
|
131
|
-
this.
|
|
173
|
+
this.offlineQueue.push(event).catch(() => { });
|
|
132
174
|
}
|
|
133
175
|
})
|
|
134
176
|
.catch(() => {
|
|
135
|
-
this.log('Encryption failed
|
|
177
|
+
this.log('Encryption failed — falling back to plain:', event.type);
|
|
136
178
|
sendPlain();
|
|
137
179
|
});
|
|
138
180
|
}
|
|
139
|
-
getStatus() {
|
|
140
|
-
return this.status;
|
|
141
|
-
}
|
|
142
181
|
flushQueue() {
|
|
143
|
-
if (this.
|
|
182
|
+
if (this.offlineQueue.size === 0)
|
|
144
183
|
return;
|
|
145
|
-
this.log(`Flushing ${this.
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
toSend.forEach((event) => this.encryptAndSend(event));
|
|
184
|
+
this.log(`Flushing ${this.offlineQueue.size} queued events...`);
|
|
185
|
+
const events = this.offlineQueue.drain();
|
|
186
|
+
events.forEach((ev) => this.encryptAndSend(ev));
|
|
149
187
|
}
|
|
150
188
|
scheduleReconnect() {
|
|
151
189
|
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
@@ -160,16 +198,14 @@ class WebSocketTransport {
|
|
|
160
198
|
}, delay);
|
|
161
199
|
}
|
|
162
200
|
log(...args) {
|
|
163
|
-
if (this.debug)
|
|
201
|
+
if (this.debug)
|
|
164
202
|
console.log('[QALink Transport]', ...args);
|
|
165
|
-
}
|
|
166
203
|
}
|
|
167
204
|
maskedUrlForLogs() {
|
|
168
205
|
try {
|
|
169
206
|
const url = new URL(this.connectionUrl);
|
|
170
|
-
if (url.searchParams.has('apiKey'))
|
|
207
|
+
if (url.searchParams.has('apiKey'))
|
|
171
208
|
url.searchParams.set('apiKey', '***');
|
|
172
|
-
}
|
|
173
209
|
return url.toString();
|
|
174
210
|
}
|
|
175
211
|
catch (_a) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket.js","sourceRoot":"","sources":["../../src/transport/websocket.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"websocket.js","sourceRoot":"","sources":["../../src/transport/websocket.ts"],"names":[],"mappings":"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"}
|