@dashgram/javascript 1.0.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/LICENSE +26 -0
- package/README.md +257 -0
- package/dist/core/config.d.ts +13 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +50 -0
- package/dist/core/context.d.ts +12 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +23 -0
- package/dist/core/event-queue.d.ts +13 -0
- package/dist/core/event-queue.d.ts.map +1 -0
- package/dist/core/event-queue.js +29 -0
- package/dist/core/session.d.ts +13 -0
- package/dist/core/session.d.ts.map +1 -0
- package/dist/core/session.js +63 -0
- package/dist/errors.d.ts +19 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +53 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +148 -0
- package/dist/trackers/base-tracker.d.ts +17 -0
- package/dist/trackers/base-tracker.d.ts.map +1 -0
- package/dist/trackers/base-tracker.js +41 -0
- package/dist/trackers/core-tracker.d.ts +14 -0
- package/dist/trackers/core-tracker.d.ts.map +1 -0
- package/dist/trackers/core-tracker.js +64 -0
- package/dist/trackers/deep-tracker.d.ts +19 -0
- package/dist/trackers/deep-tracker.d.ts.map +1 -0
- package/dist/trackers/deep-tracker.js +241 -0
- package/dist/trackers/interaction-tracker.d.ts +17 -0
- package/dist/trackers/interaction-tracker.d.ts.map +1 -0
- package/dist/trackers/interaction-tracker.js +145 -0
- package/dist/transport/batch-processor.d.ts +19 -0
- package/dist/transport/batch-processor.d.ts.map +1 -0
- package/dist/transport/batch-processor.js +75 -0
- package/dist/transport/transport.d.ts +16 -0
- package/dist/transport/transport.d.ts.map +1 -0
- package/dist/transport/transport.js +140 -0
- package/dist/types/index.d.ts +61 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/device.d.ts +11 -0
- package/dist/utils/device.d.ts.map +1 -0
- package/dist/utils/device.js +72 -0
- package/dist/utils/helpers.d.ts +9 -0
- package/dist/utils/helpers.d.ts.map +1 -0
- package/dist/utils/helpers.js +74 -0
- package/dist/utils/telegram.d.ts +10 -0
- package/dist/utils/telegram.d.ts.map +1 -0
- package/dist/utils/telegram.js +55 -0
- package/package.json +50 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { Config } from "./core/config";
|
|
2
|
+
import { Context } from "./core/context";
|
|
3
|
+
import { Session } from "./core/session";
|
|
4
|
+
import { Transport } from "./transport/transport";
|
|
5
|
+
import { BatchProcessor } from "./transport/batch-processor";
|
|
6
|
+
import { CoreTracker } from "./trackers/core-tracker";
|
|
7
|
+
import { InteractionTracker } from "./trackers/interaction-tracker";
|
|
8
|
+
import { DeepTracker } from "./trackers/deep-tracker";
|
|
9
|
+
import { getTimestamp, isBrowser } from "./utils/helpers";
|
|
10
|
+
import { getTelegramUserId } from "./utils/telegram";
|
|
11
|
+
class DashgramSDK {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.config = null;
|
|
14
|
+
this.context = null;
|
|
15
|
+
this.session = null;
|
|
16
|
+
this.transport = null;
|
|
17
|
+
this.batchProcessor = null;
|
|
18
|
+
this.trackers = [];
|
|
19
|
+
this.isInitialized = false;
|
|
20
|
+
}
|
|
21
|
+
init(userConfig) {
|
|
22
|
+
if (this.isInitialized) {
|
|
23
|
+
console.warn("Dashgram: Already initialized");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (!isBrowser()) {
|
|
27
|
+
console.warn("Dashgram: Not running in browser environment");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
this.config = new Config(userConfig);
|
|
32
|
+
this.context = new Context();
|
|
33
|
+
this.session = new Session();
|
|
34
|
+
this.transport = new Transport(this.config);
|
|
35
|
+
this.batchProcessor = new BatchProcessor(this.config, this.transport);
|
|
36
|
+
this.setupTrackers();
|
|
37
|
+
this.batchProcessor.start();
|
|
38
|
+
this.isInitialized = true;
|
|
39
|
+
this.log("Initialized successfully", {
|
|
40
|
+
projectId: this.config.get("projectId"),
|
|
41
|
+
trackLevel: this.config.getTrackLevel()
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.error("Dashgram: Initialization failed", error);
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
setupTrackers() {
|
|
50
|
+
if (!this.config)
|
|
51
|
+
return;
|
|
52
|
+
const trackCallback = (event, properties) => {
|
|
53
|
+
this.trackAuto(event, properties);
|
|
54
|
+
};
|
|
55
|
+
const coreTracker = new CoreTracker(this.config, trackCallback);
|
|
56
|
+
this.trackers.push(coreTracker);
|
|
57
|
+
coreTracker.start();
|
|
58
|
+
const interactionTracker = new InteractionTracker(this.config, trackCallback);
|
|
59
|
+
this.trackers.push(interactionTracker);
|
|
60
|
+
interactionTracker.start();
|
|
61
|
+
const deepTracker = new DeepTracker(this.config, trackCallback);
|
|
62
|
+
this.trackers.push(deepTracker);
|
|
63
|
+
deepTracker.start();
|
|
64
|
+
}
|
|
65
|
+
track(event, properties = {}) {
|
|
66
|
+
this.ensureInitialized();
|
|
67
|
+
const fullEvent = this.buildEvent(event, properties, "manual");
|
|
68
|
+
this.batchProcessor.addEvent(fullEvent);
|
|
69
|
+
this.log("Tracked event", { event, properties });
|
|
70
|
+
}
|
|
71
|
+
trackAuto(event, properties = {}) {
|
|
72
|
+
if (!this.isInitialized)
|
|
73
|
+
return;
|
|
74
|
+
const fullEvent = this.buildEvent(event, properties, "auto");
|
|
75
|
+
this.batchProcessor.addEvent(fullEvent);
|
|
76
|
+
this.log("Auto-tracked event", { event, properties });
|
|
77
|
+
}
|
|
78
|
+
buildEvent(event, properties, source) {
|
|
79
|
+
this.ensureInitialized();
|
|
80
|
+
this.session.renewIfExpired();
|
|
81
|
+
this.session.updateLastActivity();
|
|
82
|
+
return {
|
|
83
|
+
event,
|
|
84
|
+
properties,
|
|
85
|
+
timestamp: getTimestamp(),
|
|
86
|
+
source,
|
|
87
|
+
level: this.config.getTrackLevel(),
|
|
88
|
+
session_id: this.session.getSessionId(),
|
|
89
|
+
user_id: this.context.getUserId() || getTelegramUserId(),
|
|
90
|
+
context: this.context.getContext()
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
identify(userId, traits = {}) {
|
|
94
|
+
this.ensureInitialized();
|
|
95
|
+
this.context.setUserId(userId);
|
|
96
|
+
this.track("identify", {
|
|
97
|
+
user_id: userId,
|
|
98
|
+
...traits
|
|
99
|
+
});
|
|
100
|
+
this.log("Identified user", { userId, traits });
|
|
101
|
+
}
|
|
102
|
+
setTrackLevel(level) {
|
|
103
|
+
this.ensureInitialized();
|
|
104
|
+
const oldLevel = this.config.getTrackLevel();
|
|
105
|
+
this.config.setTrackLevel(level);
|
|
106
|
+
this.trackers.forEach(tracker => {
|
|
107
|
+
tracker.stop();
|
|
108
|
+
tracker.start();
|
|
109
|
+
});
|
|
110
|
+
this.log("Track level changed", { from: oldLevel, to: level });
|
|
111
|
+
}
|
|
112
|
+
async flush() {
|
|
113
|
+
this.ensureInitialized();
|
|
114
|
+
await this.batchProcessor.flushAsync();
|
|
115
|
+
this.log("Flushed all events");
|
|
116
|
+
}
|
|
117
|
+
reset() {
|
|
118
|
+
this.ensureInitialized();
|
|
119
|
+
this.session.reset();
|
|
120
|
+
this.context.clearUserId();
|
|
121
|
+
this.log("Reset session and user");
|
|
122
|
+
}
|
|
123
|
+
shutdown() {
|
|
124
|
+
if (!this.isInitialized) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
this.trackers.forEach(tracker => tracker.stop());
|
|
128
|
+
this.trackers = [];
|
|
129
|
+
this.batchProcessor.flush();
|
|
130
|
+
this.batchProcessor.stop();
|
|
131
|
+
this.isInitialized = false;
|
|
132
|
+
this.log("Shutdown complete");
|
|
133
|
+
}
|
|
134
|
+
ensureInitialized() {
|
|
135
|
+
if (!this.isInitialized) {
|
|
136
|
+
throw new Error("Dashgram: SDK not initialized. Call init() first.");
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
log(...args) {
|
|
140
|
+
if (this.config?.isDebug()) {
|
|
141
|
+
console.log("[Dashgram SDK]", ...args);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const DashgramMini = new DashgramSDK();
|
|
146
|
+
export { DashgramError, InvalidCredentialsError, DashgramAPIError, NetworkError, DashgramConfigurationError } from "./errors";
|
|
147
|
+
export { DashgramMini };
|
|
148
|
+
export default DashgramMini;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { TrackLevel, EventProperties } from '../types';
|
|
2
|
+
import type { Config } from '../core/config';
|
|
3
|
+
export type TrackCallback = (event: string, properties: EventProperties) => void;
|
|
4
|
+
export declare abstract class BaseTracker {
|
|
5
|
+
protected config: Config;
|
|
6
|
+
protected trackCallback: TrackCallback;
|
|
7
|
+
protected isActive: boolean;
|
|
8
|
+
protected level: TrackLevel;
|
|
9
|
+
constructor(config: Config, trackCallback: TrackCallback, level: TrackLevel);
|
|
10
|
+
start(): void;
|
|
11
|
+
stop(): void;
|
|
12
|
+
protected track(event: string, properties?: EventProperties): void;
|
|
13
|
+
protected abstract setup(): void;
|
|
14
|
+
protected abstract teardown(): void;
|
|
15
|
+
protected log(...args: any[]): void;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=base-tracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-tracker.d.ts","sourceRoot":"","sources":["../../src/trackers/base-tracker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAK7C,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,eAAe,KAAK,IAAI,CAAC;AAKjF,8BAAsB,WAAW;IAC/B,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;IACvC,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAS;IACpC,SAAS,CAAC,KAAK,EAAE,UAAU,CAAC;gBAEhB,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU;IAS3E,KAAK,IAAI,IAAI;IAkBb,IAAI,IAAI,IAAI;IAaZ,SAAS,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,GAAE,eAAoB,GAAG,IAAI;IActE,SAAS,CAAC,QAAQ,CAAC,KAAK,IAAI,IAAI;IAKhC,SAAS,CAAC,QAAQ,CAAC,QAAQ,IAAI,IAAI;IAKnC,SAAS,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI;CAKpC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export class BaseTracker {
|
|
2
|
+
constructor(config, trackCallback, level) {
|
|
3
|
+
this.isActive = false;
|
|
4
|
+
this.config = config;
|
|
5
|
+
this.trackCallback = trackCallback;
|
|
6
|
+
this.level = level;
|
|
7
|
+
}
|
|
8
|
+
start() {
|
|
9
|
+
if (this.isActive) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const currentLevel = this.config.getTrackLevel();
|
|
13
|
+
if (currentLevel >= this.level) {
|
|
14
|
+
this.isActive = true;
|
|
15
|
+
this.setup();
|
|
16
|
+
this.log(`Started (level ${this.level})`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
stop() {
|
|
20
|
+
if (!this.isActive) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
this.isActive = false;
|
|
24
|
+
this.teardown();
|
|
25
|
+
this.log(`Stopped`);
|
|
26
|
+
}
|
|
27
|
+
track(event, properties = {}) {
|
|
28
|
+
if (!this.isActive) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
this.trackCallback(event, {
|
|
32
|
+
...properties,
|
|
33
|
+
_tracker: this.constructor.name,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
log(...args) {
|
|
37
|
+
if (this.config.isDebug()) {
|
|
38
|
+
console.log(`[Dashgram ${this.constructor.name}]`, ...args);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { BaseTracker } from './base-tracker';
|
|
2
|
+
import type { Config } from '../core/config';
|
|
3
|
+
import type { TrackCallback } from './base-tracker';
|
|
4
|
+
export declare class CoreTracker extends BaseTracker {
|
|
5
|
+
private unsubscribers;
|
|
6
|
+
private hasTrackedAppOpen;
|
|
7
|
+
constructor(config: Config, trackCallback: TrackCallback);
|
|
8
|
+
protected setup(): void;
|
|
9
|
+
protected teardown(): void;
|
|
10
|
+
private trackAppOpen;
|
|
11
|
+
private setupVisibilityTracking;
|
|
12
|
+
private setupUnloadTracking;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=core-tracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core-tracker.d.ts","sourceRoot":"","sources":["../../src/trackers/core-tracker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAMpD,qBAAa,WAAY,SAAQ,WAAW;IAC1C,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,iBAAiB,CAAS;gBAEtB,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa;IAIxD,SAAS,CAAC,KAAK,IAAI,IAAI;IAgBvB,SAAS,CAAC,QAAQ,IAAI,IAAI;IAS1B,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,uBAAuB;IAuB/B,OAAO,CAAC,mBAAmB;CAuB5B"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { BaseTracker } from './base-tracker';
|
|
2
|
+
export class CoreTracker extends BaseTracker {
|
|
3
|
+
constructor(config, trackCallback) {
|
|
4
|
+
super(config, trackCallback, 1);
|
|
5
|
+
this.unsubscribers = [];
|
|
6
|
+
this.hasTrackedAppOpen = false;
|
|
7
|
+
}
|
|
8
|
+
setup() {
|
|
9
|
+
if (typeof window === 'undefined') {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
this.trackAppOpen();
|
|
13
|
+
this.track('session_start', {});
|
|
14
|
+
this.setupVisibilityTracking();
|
|
15
|
+
this.setupUnloadTracking();
|
|
16
|
+
}
|
|
17
|
+
teardown() {
|
|
18
|
+
this.unsubscribers.forEach((unsubscribe) => unsubscribe());
|
|
19
|
+
this.unsubscribers = [];
|
|
20
|
+
}
|
|
21
|
+
trackAppOpen() {
|
|
22
|
+
if (this.hasTrackedAppOpen) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
this.track('app_open', {
|
|
26
|
+
referrer: document.referrer || 'direct',
|
|
27
|
+
});
|
|
28
|
+
this.hasTrackedAppOpen = true;
|
|
29
|
+
}
|
|
30
|
+
setupVisibilityTracking() {
|
|
31
|
+
const handleVisibilityChange = () => {
|
|
32
|
+
if (document.visibilityState === 'hidden') {
|
|
33
|
+
this.track('app_close', {
|
|
34
|
+
visibility_state: 'hidden',
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
else if (document.visibilityState === 'visible') {
|
|
38
|
+
this.track('app_open', {
|
|
39
|
+
visibility_state: 'visible',
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
44
|
+
this.unsubscribers.push(() => {
|
|
45
|
+
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
setupUnloadTracking() {
|
|
49
|
+
const handleUnload = () => {
|
|
50
|
+
this.track('app_close', {
|
|
51
|
+
reason: 'unload',
|
|
52
|
+
});
|
|
53
|
+
this.track('session_end', {});
|
|
54
|
+
};
|
|
55
|
+
window.addEventListener('pagehide', handleUnload);
|
|
56
|
+
this.unsubscribers.push(() => {
|
|
57
|
+
window.removeEventListener('pagehide', handleUnload);
|
|
58
|
+
});
|
|
59
|
+
window.addEventListener('beforeunload', handleUnload);
|
|
60
|
+
this.unsubscribers.push(() => {
|
|
61
|
+
window.removeEventListener('beforeunload', handleUnload);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { BaseTracker } from "./base-tracker";
|
|
2
|
+
import type { Config } from "../core/config";
|
|
3
|
+
import type { TrackCallback } from "./base-tracker";
|
|
4
|
+
export declare class DeepTracker extends BaseTracker {
|
|
5
|
+
private unsubscribers;
|
|
6
|
+
private observers;
|
|
7
|
+
private clickTracker;
|
|
8
|
+
private maxScrollDepth;
|
|
9
|
+
constructor(config: Config, trackCallback: TrackCallback);
|
|
10
|
+
protected setup(): void;
|
|
11
|
+
protected teardown(): void;
|
|
12
|
+
private setupScrollTracking;
|
|
13
|
+
private setupVisibilityTracking;
|
|
14
|
+
private setupRageClickTracking;
|
|
15
|
+
private setupLongTaskTracking;
|
|
16
|
+
private setupWebVitals;
|
|
17
|
+
private setupTelegramTracking;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=deep-tracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deep-tracker.d.ts","sourceRoot":"","sources":["../../src/trackers/deep-tracker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AASnD,qBAAa,WAAY,SAAQ,WAAW;IAC1C,OAAO,CAAC,aAAa,CAAqB;IAC1C,OAAO,CAAC,SAAS,CAAwD;IACzE,OAAO,CAAC,YAAY,CAAoD;IACxE,OAAO,CAAC,cAAc,CAAI;gBAEd,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa;IAIxD,SAAS,CAAC,KAAK,IAAI,IAAI;IAavB,SAAS,CAAC,QAAQ,IAAI,IAAI;IAa1B,OAAO,CAAC,mBAAmB;IA8B3B,OAAO,CAAC,uBAAuB;IA+D/B,OAAO,CAAC,sBAAsB;IA6C9B,OAAO,CAAC,qBAAqB;IA6B7B,OAAO,CAAC,cAAc;IAmFtB,OAAO,CAAC,qBAAqB;CA+B9B"}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { BaseTracker } from "./base-tracker";
|
|
2
|
+
import { throttle } from "../utils/helpers";
|
|
3
|
+
import { getScrollDepth } from "../utils/device";
|
|
4
|
+
import { subscribeToTelegramEvent } from "../utils/telegram";
|
|
5
|
+
export class DeepTracker extends BaseTracker {
|
|
6
|
+
constructor(config, trackCallback) {
|
|
7
|
+
super(config, trackCallback, 3);
|
|
8
|
+
this.unsubscribers = [];
|
|
9
|
+
this.observers = [];
|
|
10
|
+
this.clickTracker = new Map();
|
|
11
|
+
this.maxScrollDepth = 0;
|
|
12
|
+
}
|
|
13
|
+
setup() {
|
|
14
|
+
if (typeof window === "undefined") {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
this.setupScrollTracking();
|
|
18
|
+
this.setupVisibilityTracking();
|
|
19
|
+
this.setupRageClickTracking();
|
|
20
|
+
this.setupLongTaskTracking();
|
|
21
|
+
this.setupWebVitals();
|
|
22
|
+
this.setupTelegramTracking();
|
|
23
|
+
}
|
|
24
|
+
teardown() {
|
|
25
|
+
this.unsubscribers.forEach(unsubscribe => unsubscribe());
|
|
26
|
+
this.unsubscribers = [];
|
|
27
|
+
this.observers.forEach(observer => observer.disconnect());
|
|
28
|
+
this.observers = [];
|
|
29
|
+
this.clickTracker.clear();
|
|
30
|
+
}
|
|
31
|
+
setupScrollTracking() {
|
|
32
|
+
const handleScroll = throttle(() => {
|
|
33
|
+
const depth = getScrollDepth();
|
|
34
|
+
if (depth > this.maxScrollDepth) {
|
|
35
|
+
this.maxScrollDepth = depth;
|
|
36
|
+
const milestones = [25, 50, 75, 100];
|
|
37
|
+
const milestone = milestones.find(m => depth >= m && this.maxScrollDepth - depth < m);
|
|
38
|
+
if (milestone) {
|
|
39
|
+
this.track("scroll_depth", {
|
|
40
|
+
depth: milestone,
|
|
41
|
+
max_depth: this.maxScrollDepth
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}, 500);
|
|
46
|
+
window.addEventListener("scroll", handleScroll, { passive: true });
|
|
47
|
+
this.unsubscribers.push(() => {
|
|
48
|
+
window.removeEventListener("scroll", handleScroll);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
setupVisibilityTracking() {
|
|
52
|
+
if (!("IntersectionObserver" in window)) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const observer = new IntersectionObserver(entries => {
|
|
56
|
+
entries.forEach(entry => {
|
|
57
|
+
if (entry.isIntersecting) {
|
|
58
|
+
const element = entry.target;
|
|
59
|
+
const selector = element.getAttribute("data-track-visible");
|
|
60
|
+
if (selector) {
|
|
61
|
+
this.track("element_visible", {
|
|
62
|
+
element: selector,
|
|
63
|
+
intersection_ratio: entry.intersectionRatio
|
|
64
|
+
});
|
|
65
|
+
observer.unobserve(element);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}, { threshold: 0.5 });
|
|
70
|
+
document.querySelectorAll("[data-track-visible]").forEach(element => {
|
|
71
|
+
observer.observe(element);
|
|
72
|
+
});
|
|
73
|
+
this.observers.push(observer);
|
|
74
|
+
const mutationObserver = new MutationObserver(mutations => {
|
|
75
|
+
mutations.forEach(mutation => {
|
|
76
|
+
mutation.addedNodes.forEach(node => {
|
|
77
|
+
if (node instanceof Element) {
|
|
78
|
+
if (node.hasAttribute("data-track-visible")) {
|
|
79
|
+
observer.observe(node);
|
|
80
|
+
}
|
|
81
|
+
node.querySelectorAll("[data-track-visible]").forEach(el => {
|
|
82
|
+
observer.observe(el);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
mutationObserver.observe(document.body, {
|
|
89
|
+
childList: true,
|
|
90
|
+
subtree: true
|
|
91
|
+
});
|
|
92
|
+
this.unsubscribers.push(() => {
|
|
93
|
+
mutationObserver.disconnect();
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
setupRageClickTracking() {
|
|
97
|
+
const RAGE_THRESHOLD = 5;
|
|
98
|
+
const RAGE_TIMEOUT = 2000;
|
|
99
|
+
const handleClick = (event) => {
|
|
100
|
+
const target = event.target;
|
|
101
|
+
if (!target)
|
|
102
|
+
return;
|
|
103
|
+
const tracker = this.clickTracker.get(target);
|
|
104
|
+
if (tracker) {
|
|
105
|
+
tracker.count++;
|
|
106
|
+
clearTimeout(tracker.timer);
|
|
107
|
+
if (tracker.count >= RAGE_THRESHOLD) {
|
|
108
|
+
this.track("rage_click", {
|
|
109
|
+
element: target.tagName.toLowerCase(),
|
|
110
|
+
click_count: tracker.count
|
|
111
|
+
});
|
|
112
|
+
this.clickTracker.delete(target);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
tracker.timer = setTimeout(() => {
|
|
116
|
+
this.clickTracker.delete(target);
|
|
117
|
+
}, RAGE_TIMEOUT);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
const timer = setTimeout(() => {
|
|
122
|
+
this.clickTracker.delete(target);
|
|
123
|
+
}, RAGE_TIMEOUT);
|
|
124
|
+
this.clickTracker.set(target, { count: 1, timer });
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
document.addEventListener("click", handleClick);
|
|
128
|
+
this.unsubscribers.push(() => {
|
|
129
|
+
document.removeEventListener("click", handleClick);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
setupLongTaskTracking() {
|
|
133
|
+
if (!("PerformanceObserver" in window)) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
const observer = new PerformanceObserver(list => {
|
|
138
|
+
for (const entry of list.getEntries()) {
|
|
139
|
+
if (entry.duration > 50) {
|
|
140
|
+
this.track("long_task", {
|
|
141
|
+
duration: Math.round(entry.duration),
|
|
142
|
+
start_time: Math.round(entry.startTime)
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
observer.observe({ entryTypes: ["longtask"] });
|
|
148
|
+
this.observers.push(observer);
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
this.log("Long task tracking not supported");
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
setupWebVitals() {
|
|
155
|
+
if (!("PerformanceObserver" in window)) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
const lcpObserver = new PerformanceObserver(list => {
|
|
160
|
+
const entries = list.getEntries();
|
|
161
|
+
const lastEntry = entries[entries.length - 1];
|
|
162
|
+
this.track("web_vital_lcp", {
|
|
163
|
+
value: Math.round(lastEntry.renderTime || lastEntry.loadTime),
|
|
164
|
+
element: lastEntry.element?.tagName.toLowerCase()
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
lcpObserver.observe({ entryTypes: ["largest-contentful-paint"] });
|
|
168
|
+
this.observers.push(lcpObserver);
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
this.log("LCP tracking not supported");
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
const fidObserver = new PerformanceObserver(list => {
|
|
175
|
+
const entries = list.getEntries();
|
|
176
|
+
entries.forEach((entry) => {
|
|
177
|
+
this.track("web_vital_fid", {
|
|
178
|
+
value: Math.round(entry.processingStart - entry.startTime),
|
|
179
|
+
event_type: entry.name
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
fidObserver.observe({ entryTypes: ["first-input"] });
|
|
184
|
+
this.observers.push(fidObserver);
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
this.log("FID tracking not supported");
|
|
188
|
+
}
|
|
189
|
+
let clsValue = 0;
|
|
190
|
+
try {
|
|
191
|
+
const clsObserver = new PerformanceObserver(list => {
|
|
192
|
+
const entries = list.getEntries();
|
|
193
|
+
entries.forEach((entry) => {
|
|
194
|
+
if (!entry.hadRecentInput) {
|
|
195
|
+
clsValue += entry.value;
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
clsObserver.observe({ entryTypes: ["layout-shift"] });
|
|
200
|
+
this.observers.push(clsObserver);
|
|
201
|
+
const reportCLS = () => {
|
|
202
|
+
if (clsValue > 0) {
|
|
203
|
+
this.track("web_vital_cls", {
|
|
204
|
+
value: Math.round(clsValue * 1000) / 1000
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
window.addEventListener("visibilitychange", () => {
|
|
209
|
+
if (document.visibilityState === "hidden") {
|
|
210
|
+
reportCLS();
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
this.unsubscribers.push(() => {
|
|
214
|
+
reportCLS();
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
this.log("CLS tracking not supported");
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
setupTelegramTracking() {
|
|
222
|
+
const unsubTheme = subscribeToTelegramEvent("themeChanged", () => {
|
|
223
|
+
this.track("telegram_theme_changed", {});
|
|
224
|
+
});
|
|
225
|
+
this.unsubscribers.push(unsubTheme);
|
|
226
|
+
const unsubViewport = subscribeToTelegramEvent("viewportChanged", () => {
|
|
227
|
+
this.track("telegram_viewport_changed", {
|
|
228
|
+
is_expanded: window.Telegram?.WebApp?.isExpanded
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
this.unsubscribers.push(unsubViewport);
|
|
232
|
+
const unsubBack = subscribeToTelegramEvent("backButtonClicked", () => {
|
|
233
|
+
this.track("telegram_back_button_clicked", {});
|
|
234
|
+
});
|
|
235
|
+
this.unsubscribers.push(unsubBack);
|
|
236
|
+
const unsubMain = subscribeToTelegramEvent("mainButtonClicked", () => {
|
|
237
|
+
this.track("telegram_main_button_clicked", {});
|
|
238
|
+
});
|
|
239
|
+
this.unsubscribers.push(unsubMain);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { BaseTracker } from './base-tracker';
|
|
2
|
+
import type { Config } from '../core/config';
|
|
3
|
+
import type { TrackCallback } from './base-tracker';
|
|
4
|
+
export declare class InteractionTracker extends BaseTracker {
|
|
5
|
+
private unsubscribers;
|
|
6
|
+
private lastPath;
|
|
7
|
+
constructor(config: Config, trackCallback: TrackCallback);
|
|
8
|
+
protected setup(): void;
|
|
9
|
+
protected teardown(): void;
|
|
10
|
+
private trackScreenView;
|
|
11
|
+
private setupHistoryTracking;
|
|
12
|
+
private setupClickTracking;
|
|
13
|
+
private setupFormTracking;
|
|
14
|
+
private setupInputTracking;
|
|
15
|
+
private setupErrorTracking;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=interaction-tracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interaction-tracker.d.ts","sourceRoot":"","sources":["../../src/trackers/interaction-tracker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AASpD,qBAAa,kBAAmB,SAAQ,WAAW;IACjD,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,QAAQ,CAAc;gBAElB,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa;IAIxD,SAAS,CAAC,KAAK,IAAI,IAAI;IAgBvB,SAAS,CAAC,QAAQ,IAAI,IAAI;IAQ1B,OAAO,CAAC,eAAe;IAoBvB,OAAO,CAAC,oBAAoB;IAgC5B,OAAO,CAAC,kBAAkB;IA+B1B,OAAO,CAAC,iBAAiB;IAuBzB,OAAO,CAAC,kBAAkB;IA4B1B,OAAO,CAAC,kBAAkB;CAgC3B"}
|