@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
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { BaseTracker } from './base-tracker';
|
|
2
|
+
import { throttle } from '../utils/helpers';
|
|
3
|
+
import { getElementSelector, getElementText } from '../utils/helpers';
|
|
4
|
+
import { getCurrentPath, getPageTitle, getCurrentUrl } from '../utils/device';
|
|
5
|
+
export class InteractionTracker extends BaseTracker {
|
|
6
|
+
constructor(config, trackCallback) {
|
|
7
|
+
super(config, trackCallback, 2);
|
|
8
|
+
this.unsubscribers = [];
|
|
9
|
+
this.lastPath = '';
|
|
10
|
+
}
|
|
11
|
+
setup() {
|
|
12
|
+
if (typeof window === 'undefined') {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
this.trackScreenView();
|
|
16
|
+
this.setupHistoryTracking();
|
|
17
|
+
this.setupClickTracking();
|
|
18
|
+
this.setupFormTracking();
|
|
19
|
+
this.setupInputTracking();
|
|
20
|
+
this.setupErrorTracking();
|
|
21
|
+
}
|
|
22
|
+
teardown() {
|
|
23
|
+
this.unsubscribers.forEach((unsubscribe) => unsubscribe());
|
|
24
|
+
this.unsubscribers = [];
|
|
25
|
+
}
|
|
26
|
+
trackScreenView() {
|
|
27
|
+
const path = getCurrentPath();
|
|
28
|
+
if (path === this.lastPath) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
this.lastPath = path;
|
|
32
|
+
this.track('screen_view', {
|
|
33
|
+
path,
|
|
34
|
+
url: getCurrentUrl(),
|
|
35
|
+
title: getPageTitle(),
|
|
36
|
+
referrer: document.referrer || 'direct',
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
setupHistoryTracking() {
|
|
40
|
+
const originalPushState = history.pushState;
|
|
41
|
+
const originalReplaceState = history.replaceState;
|
|
42
|
+
const trackOnHistoryChange = () => {
|
|
43
|
+
this.trackScreenView();
|
|
44
|
+
};
|
|
45
|
+
history.pushState = function (...args) {
|
|
46
|
+
originalPushState.apply(history, args);
|
|
47
|
+
trackOnHistoryChange();
|
|
48
|
+
};
|
|
49
|
+
history.replaceState = function (...args) {
|
|
50
|
+
originalReplaceState.apply(history, args);
|
|
51
|
+
trackOnHistoryChange();
|
|
52
|
+
};
|
|
53
|
+
window.addEventListener('popstate', trackOnHistoryChange);
|
|
54
|
+
this.unsubscribers.push(() => {
|
|
55
|
+
history.pushState = originalPushState;
|
|
56
|
+
history.replaceState = originalReplaceState;
|
|
57
|
+
window.removeEventListener('popstate', trackOnHistoryChange);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
setupClickTracking() {
|
|
61
|
+
const handleClick = (event) => {
|
|
62
|
+
const target = event.target;
|
|
63
|
+
if (!target)
|
|
64
|
+
return;
|
|
65
|
+
const button = target.closest('button, [role="button"], a');
|
|
66
|
+
if (button) {
|
|
67
|
+
const tagName = button.tagName.toLowerCase();
|
|
68
|
+
const isLink = tagName === 'a';
|
|
69
|
+
this.track(isLink ? 'link_click' : 'button_click', {
|
|
70
|
+
element: getElementSelector(button),
|
|
71
|
+
text: getElementText(button),
|
|
72
|
+
href: isLink ? button.href : undefined,
|
|
73
|
+
target: isLink ? button.target : undefined,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
document.addEventListener('click', handleClick, { capture: true });
|
|
78
|
+
this.unsubscribers.push(() => {
|
|
79
|
+
document.removeEventListener('click', handleClick, { capture: true });
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
setupFormTracking() {
|
|
83
|
+
const handleSubmit = (event) => {
|
|
84
|
+
const form = event.target;
|
|
85
|
+
if (!form)
|
|
86
|
+
return;
|
|
87
|
+
this.track('form_submit', {
|
|
88
|
+
form_id: form.id || undefined,
|
|
89
|
+
form_name: form.name || undefined,
|
|
90
|
+
form_action: form.action || undefined,
|
|
91
|
+
form_method: form.method || undefined,
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
document.addEventListener('submit', handleSubmit, { capture: true });
|
|
95
|
+
this.unsubscribers.push(() => {
|
|
96
|
+
document.removeEventListener('submit', handleSubmit, { capture: true });
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
setupInputTracking() {
|
|
100
|
+
const handleFocus = throttle((event) => {
|
|
101
|
+
const target = event.target;
|
|
102
|
+
if (!target)
|
|
103
|
+
return;
|
|
104
|
+
const tagName = target.tagName.toLowerCase();
|
|
105
|
+
if (['input', 'textarea', 'select'].includes(tagName)) {
|
|
106
|
+
const input = target;
|
|
107
|
+
this.track('input_focus', {
|
|
108
|
+
element: getElementSelector(input),
|
|
109
|
+
input_type: input.type || tagName,
|
|
110
|
+
input_name: input.name || undefined,
|
|
111
|
+
input_id: input.id || undefined,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}, 1000);
|
|
115
|
+
document.addEventListener('focus', handleFocus, { capture: true });
|
|
116
|
+
this.unsubscribers.push(() => {
|
|
117
|
+
document.removeEventListener('focus', handleFocus, { capture: true });
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
setupErrorTracking() {
|
|
121
|
+
const handleError = (event) => {
|
|
122
|
+
this.track('js_error', {
|
|
123
|
+
message: event.message,
|
|
124
|
+
filename: event.filename,
|
|
125
|
+
lineno: event.lineno,
|
|
126
|
+
colno: event.colno,
|
|
127
|
+
stack: event.error?.stack,
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
window.addEventListener('error', handleError);
|
|
131
|
+
this.unsubscribers.push(() => {
|
|
132
|
+
window.removeEventListener('error', handleError);
|
|
133
|
+
});
|
|
134
|
+
const handleRejection = (event) => {
|
|
135
|
+
this.track('unhandled_rejection', {
|
|
136
|
+
reason: String(event.reason),
|
|
137
|
+
promise: String(event.promise),
|
|
138
|
+
});
|
|
139
|
+
};
|
|
140
|
+
window.addEventListener('unhandledrejection', handleRejection);
|
|
141
|
+
this.unsubscribers.push(() => {
|
|
142
|
+
window.removeEventListener('unhandledrejection', handleRejection);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DashgramEvent } from '../types';
|
|
2
|
+
import type { Config } from '../core/config';
|
|
3
|
+
import { Transport } from './transport';
|
|
4
|
+
export declare class BatchProcessor {
|
|
5
|
+
private config;
|
|
6
|
+
private queue;
|
|
7
|
+
private transport;
|
|
8
|
+
private flushTimer;
|
|
9
|
+
private isStarted;
|
|
10
|
+
constructor(config: Config, transport: Transport);
|
|
11
|
+
start(): void;
|
|
12
|
+
stop(): void;
|
|
13
|
+
addEvent(event: DashgramEvent): void;
|
|
14
|
+
private scheduleFlush;
|
|
15
|
+
flush(): void;
|
|
16
|
+
flushAsync(): Promise<void>;
|
|
17
|
+
private setupPageUnloadHandler;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=batch-processor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch-processor.d.ts","sourceRoot":"","sources":["../../src/transport/batch-processor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAE7C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAKxC,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,SAAS,CAAkB;gBAEvB,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS;IAShD,KAAK,IAAI,IAAI;IAab,IAAI,IAAI,IAAI;IAYZ,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAapC,OAAO,CAAC,aAAa;IAkBrB,KAAK,IAAI,IAAI;IAWP,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAajC,OAAO,CAAC,sBAAsB;CAwB/B"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { EventQueue } from '../core/event-queue';
|
|
2
|
+
export class BatchProcessor {
|
|
3
|
+
constructor(config, transport) {
|
|
4
|
+
this.flushTimer = null;
|
|
5
|
+
this.isStarted = false;
|
|
6
|
+
this.config = config;
|
|
7
|
+
this.transport = transport;
|
|
8
|
+
this.queue = new EventQueue(200);
|
|
9
|
+
}
|
|
10
|
+
start() {
|
|
11
|
+
if (this.isStarted) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
this.isStarted = true;
|
|
15
|
+
this.scheduleFlush();
|
|
16
|
+
this.setupPageUnloadHandler();
|
|
17
|
+
}
|
|
18
|
+
stop() {
|
|
19
|
+
this.isStarted = false;
|
|
20
|
+
if (this.flushTimer) {
|
|
21
|
+
clearTimeout(this.flushTimer);
|
|
22
|
+
this.flushTimer = null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
addEvent(event) {
|
|
26
|
+
this.queue.enqueue(event);
|
|
27
|
+
const batchSize = this.config.get('batchSize');
|
|
28
|
+
if (this.queue.size() >= batchSize) {
|
|
29
|
+
this.flush();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
scheduleFlush() {
|
|
33
|
+
if (this.flushTimer) {
|
|
34
|
+
clearTimeout(this.flushTimer);
|
|
35
|
+
}
|
|
36
|
+
const interval = this.config.get('flushInterval');
|
|
37
|
+
this.flushTimer = setTimeout(() => {
|
|
38
|
+
this.flush();
|
|
39
|
+
if (this.isStarted) {
|
|
40
|
+
this.scheduleFlush();
|
|
41
|
+
}
|
|
42
|
+
}, interval);
|
|
43
|
+
}
|
|
44
|
+
flush() {
|
|
45
|
+
const events = this.queue.flush();
|
|
46
|
+
if (events.length > 0) {
|
|
47
|
+
this.transport.send(events);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async flushAsync() {
|
|
51
|
+
const events = this.queue.flush();
|
|
52
|
+
if (events.length > 0) {
|
|
53
|
+
await this.transport.send(events);
|
|
54
|
+
}
|
|
55
|
+
await this.transport.flush();
|
|
56
|
+
}
|
|
57
|
+
setupPageUnloadHandler() {
|
|
58
|
+
if (typeof window === 'undefined') {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const handleUnload = () => {
|
|
62
|
+
const events = this.queue.flush();
|
|
63
|
+
if (events.length > 0) {
|
|
64
|
+
this.transport.sendBeacon(events);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
window.addEventListener('visibilitychange', () => {
|
|
68
|
+
if (document.visibilityState === 'hidden') {
|
|
69
|
+
handleUnload();
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
window.addEventListener('pagehide', handleUnload);
|
|
73
|
+
window.addEventListener('beforeunload', handleUnload);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DashgramEvent } from "../types";
|
|
2
|
+
import type { Config } from "../core/config";
|
|
3
|
+
export declare class Transport {
|
|
4
|
+
private config;
|
|
5
|
+
private isOnline;
|
|
6
|
+
private pendingRequests;
|
|
7
|
+
constructor(config: Config);
|
|
8
|
+
private setupOnlineListener;
|
|
9
|
+
send(events: DashgramEvent[]): Promise<void>;
|
|
10
|
+
private sendRequest;
|
|
11
|
+
sendBeacon(events: DashgramEvent[]): boolean;
|
|
12
|
+
flush(): Promise<void>;
|
|
13
|
+
private log;
|
|
14
|
+
private logError;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/transport/transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAC7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAO5C,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,eAAe,CAAgC;gBAE3C,MAAM,EAAE,MAAM;IAQ1B,OAAO,CAAC,mBAAmB;IAuBrB,IAAI,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YAuDpC,WAAW;IAqDzB,UAAU,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO;IAmCtC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B,OAAO,CAAC,GAAG;IASX,OAAO,CAAC,QAAQ;CAKjB"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { safeStringify } from "../utils/helpers";
|
|
2
|
+
import { InvalidCredentialsError, DashgramAPIError, NetworkError } from "../errors";
|
|
3
|
+
export class Transport {
|
|
4
|
+
constructor(config) {
|
|
5
|
+
this.isOnline = true;
|
|
6
|
+
this.pendingRequests = new Set();
|
|
7
|
+
this.config = config;
|
|
8
|
+
this.setupOnlineListener();
|
|
9
|
+
}
|
|
10
|
+
setupOnlineListener() {
|
|
11
|
+
if (typeof window === "undefined") {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
window.addEventListener("online", () => {
|
|
15
|
+
this.isOnline = true;
|
|
16
|
+
this.log("Connection restored");
|
|
17
|
+
});
|
|
18
|
+
window.addEventListener("offline", () => {
|
|
19
|
+
this.isOnline = false;
|
|
20
|
+
this.log("Connection lost");
|
|
21
|
+
});
|
|
22
|
+
this.isOnline = navigator.onLine;
|
|
23
|
+
}
|
|
24
|
+
async send(events) {
|
|
25
|
+
if (events.length === 0) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (this.config.isDisabled()) {
|
|
29
|
+
this.log("Tracking disabled, skipping send");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (!this.isOnline) {
|
|
33
|
+
this.log("Offline, skipping send");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const request = this.sendRequest(events);
|
|
37
|
+
this.pendingRequests.add(request);
|
|
38
|
+
try {
|
|
39
|
+
await request;
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
this.logError("Failed to send events:", error);
|
|
43
|
+
const onError = this.config.getOnError();
|
|
44
|
+
if (onError) {
|
|
45
|
+
try {
|
|
46
|
+
if (error instanceof InvalidCredentialsError ||
|
|
47
|
+
error instanceof DashgramAPIError ||
|
|
48
|
+
error instanceof NetworkError) {
|
|
49
|
+
onError(error);
|
|
50
|
+
}
|
|
51
|
+
else if (error instanceof Error) {
|
|
52
|
+
onError(new NetworkError(error));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (handlerError) {
|
|
56
|
+
this.logError("Error in onError callback:", handlerError);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
this.pendingRequests.delete(request);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async sendRequest(events) {
|
|
65
|
+
const url = this.config.get("apiUrl");
|
|
66
|
+
const apiKey = this.config.get("apiKey");
|
|
67
|
+
const projectId = this.config.get("projectId");
|
|
68
|
+
try {
|
|
69
|
+
const response = await fetch(url, {
|
|
70
|
+
method: "POST",
|
|
71
|
+
headers: {
|
|
72
|
+
"Content-Type": "application/json",
|
|
73
|
+
"X-API-Key": apiKey,
|
|
74
|
+
"X-Project-ID": projectId
|
|
75
|
+
},
|
|
76
|
+
body: safeStringify({ events }),
|
|
77
|
+
keepalive: true
|
|
78
|
+
});
|
|
79
|
+
if (response.status === 403) {
|
|
80
|
+
throw new InvalidCredentialsError();
|
|
81
|
+
}
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
let details = response.statusText;
|
|
84
|
+
try {
|
|
85
|
+
const errorData = await response.json();
|
|
86
|
+
details = errorData.details || errorData.message || details;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
}
|
|
90
|
+
throw new DashgramAPIError(response.status, details);
|
|
91
|
+
}
|
|
92
|
+
this.log(`Sent ${events.length} events successfully`);
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
if (error instanceof InvalidCredentialsError || error instanceof DashgramAPIError) {
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
if (error instanceof Error) {
|
|
99
|
+
throw new NetworkError(error);
|
|
100
|
+
}
|
|
101
|
+
throw new NetworkError(new Error(String(error)));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
sendBeacon(events) {
|
|
105
|
+
if (events.length === 0) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
if (this.config.isDisabled()) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
if (typeof navigator === "undefined" || !navigator.sendBeacon) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
const url = this.config.get("apiUrl");
|
|
115
|
+
const apiKey = this.config.get("apiKey");
|
|
116
|
+
const projectId = this.config.get("projectId");
|
|
117
|
+
const payload = safeStringify({
|
|
118
|
+
events,
|
|
119
|
+
apiKey,
|
|
120
|
+
projectId
|
|
121
|
+
});
|
|
122
|
+
const blob = new Blob([payload], { type: "application/json" });
|
|
123
|
+
const sent = navigator.sendBeacon(url, blob);
|
|
124
|
+
this.log(`sendBeacon ${sent ? "succeeded" : "failed"} for ${events.length} events`);
|
|
125
|
+
return sent;
|
|
126
|
+
}
|
|
127
|
+
async flush() {
|
|
128
|
+
await Promise.all(Array.from(this.pendingRequests));
|
|
129
|
+
}
|
|
130
|
+
log(...args) {
|
|
131
|
+
if (this.config.isDebug()) {
|
|
132
|
+
console.log("[Dashgram Transport]", ...args);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
logError(...args) {
|
|
136
|
+
if (this.config.isDebug()) {
|
|
137
|
+
console.error("[Dashgram Transport]", ...args);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { DashgramError } from "../errors";
|
|
2
|
+
export type TrackLevel = 1 | 2 | 3;
|
|
3
|
+
export type EventSource = "auto" | "manual";
|
|
4
|
+
export interface DashgramConfig {
|
|
5
|
+
projectId: string;
|
|
6
|
+
apiKey: string;
|
|
7
|
+
trackLevel?: TrackLevel;
|
|
8
|
+
apiUrl?: string;
|
|
9
|
+
batchSize?: number;
|
|
10
|
+
flushInterval?: number;
|
|
11
|
+
debug?: boolean;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
onError?: (error: DashgramError) => void;
|
|
14
|
+
}
|
|
15
|
+
export interface EventContext {
|
|
16
|
+
platform: string;
|
|
17
|
+
app_version: string;
|
|
18
|
+
language: string;
|
|
19
|
+
screen_width: number;
|
|
20
|
+
screen_height: number;
|
|
21
|
+
viewport_width: number;
|
|
22
|
+
viewport_height: number;
|
|
23
|
+
user_agent: string;
|
|
24
|
+
timezone: string;
|
|
25
|
+
telegram_version?: string;
|
|
26
|
+
theme?: string;
|
|
27
|
+
}
|
|
28
|
+
export type EventProperties = Record<string, any>;
|
|
29
|
+
export interface DashgramEvent {
|
|
30
|
+
event: string;
|
|
31
|
+
properties: EventProperties;
|
|
32
|
+
timestamp: string;
|
|
33
|
+
source: EventSource;
|
|
34
|
+
level: TrackLevel;
|
|
35
|
+
session_id: string;
|
|
36
|
+
user_id: string | null;
|
|
37
|
+
context: EventContext;
|
|
38
|
+
}
|
|
39
|
+
export type UserTraits = Record<string, any>;
|
|
40
|
+
export interface TelegramWebApp {
|
|
41
|
+
initDataUnsafe?: {
|
|
42
|
+
user?: {
|
|
43
|
+
id: number;
|
|
44
|
+
first_name?: string;
|
|
45
|
+
last_name?: string;
|
|
46
|
+
username?: string;
|
|
47
|
+
language_code?: string;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
platform?: string;
|
|
51
|
+
version?: string;
|
|
52
|
+
themeParams?: Record<string, string>;
|
|
53
|
+
onEvent?: (event: string, callback: () => void) => void;
|
|
54
|
+
offEvent?: (event: string, callback: () => void) => void;
|
|
55
|
+
}
|
|
56
|
+
export interface TelegramWindow extends Window {
|
|
57
|
+
Telegram?: {
|
|
58
|
+
WebApp?: TelegramWebApp;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAK9C,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;AAKlC,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ,CAAA;AAK3C,MAAM,WAAW,cAAc;IAE7B,SAAS,EAAE,MAAM,CAAA;IAGjB,MAAM,EAAE,MAAM,CAAA;IAGd,UAAU,CAAC,EAAE,UAAU,CAAA;IAGvB,MAAM,CAAC,EAAE,MAAM,CAAA;IAGf,SAAS,CAAC,EAAE,MAAM,CAAA;IAGlB,aAAa,CAAC,EAAE,MAAM,CAAA;IAGtB,KAAK,CAAC,EAAE,OAAO,CAAA;IAGf,QAAQ,CAAC,EAAE,OAAO,CAAA;IAGlB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;CACzC;AAKD,MAAM,WAAW,YAAY;IAE3B,QAAQ,EAAE,MAAM,CAAA;IAGhB,WAAW,EAAE,MAAM,CAAA;IAGnB,QAAQ,EAAE,MAAM,CAAA;IAGhB,YAAY,EAAE,MAAM,CAAA;IAGpB,aAAa,EAAE,MAAM,CAAA;IAGrB,cAAc,EAAE,MAAM,CAAA;IAGtB,eAAe,EAAE,MAAM,CAAA;IAGvB,UAAU,EAAE,MAAM,CAAA;IAGlB,QAAQ,EAAE,MAAM,CAAA;IAGhB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IAGzB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAKD,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;AAKjD,MAAM,WAAW,aAAa;IAE5B,KAAK,EAAE,MAAM,CAAA;IAGb,UAAU,EAAE,eAAe,CAAA;IAG3B,SAAS,EAAE,MAAM,CAAA;IAGjB,MAAM,EAAE,WAAW,CAAA;IAGnB,KAAK,EAAE,UAAU,CAAA;IAGjB,UAAU,EAAE,MAAM,CAAA;IAGlB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAGtB,OAAO,EAAE,YAAY,CAAA;CACtB;AAKD,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;AAK5C,MAAM,WAAW,cAAc;IAC7B,cAAc,CAAC,EAAE;QACf,IAAI,CAAC,EAAE;YACL,EAAE,EAAE,MAAM,CAAA;YACV,UAAU,CAAC,EAAE,MAAM,CAAA;YACnB,SAAS,CAAC,EAAE,MAAM,CAAA;YAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;YACjB,aAAa,CAAC,EAAE,MAAM,CAAA;SACvB,CAAA;KACF,CAAA;IACD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAA;IACvD,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAA;CACzD;AAKD,MAAM,WAAW,cAAe,SAAQ,MAAM;IAC5C,QAAQ,CAAC,EAAE;QACT,MAAM,CAAC,EAAE,cAAc,CAAA;KACxB,CAAA;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { EventContext } from '../types';
|
|
2
|
+
export declare function getDeviceContext(): EventContext;
|
|
3
|
+
export declare function getViewportSize(): {
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
};
|
|
7
|
+
export declare function getScrollDepth(): number;
|
|
8
|
+
export declare function getCurrentUrl(): string;
|
|
9
|
+
export declare function getCurrentPath(): string;
|
|
10
|
+
export declare function getPageTitle(): string;
|
|
11
|
+
//# sourceMappingURL=device.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../src/utils/device.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAW7C,wBAAgB,gBAAgB,IAAI,YAAY,CA+B/C;AAKD,wBAAgB,eAAe,IAAI;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CASnE;AAKD,wBAAgB,cAAc,IAAI,MAAM,CAiBvC;AAKD,wBAAgB,aAAa,IAAI,MAAM,CAMtC;AAKD,wBAAgB,cAAc,IAAI,MAAM,CAMvC;AAKD,wBAAgB,YAAY,IAAI,MAAM,CAMrC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { getTelegramPlatform, getTelegramVersion, getTelegramLanguage, getTelegramTheme, } from './telegram';
|
|
2
|
+
export function getDeviceContext() {
|
|
3
|
+
if (typeof window === 'undefined') {
|
|
4
|
+
return {
|
|
5
|
+
platform: 'unknown',
|
|
6
|
+
app_version: 'unknown',
|
|
7
|
+
language: 'unknown',
|
|
8
|
+
screen_width: 0,
|
|
9
|
+
screen_height: 0,
|
|
10
|
+
viewport_width: 0,
|
|
11
|
+
viewport_height: 0,
|
|
12
|
+
user_agent: '',
|
|
13
|
+
timezone: 'UTC',
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const telegramLang = getTelegramLanguage();
|
|
17
|
+
const browserLang = navigator.language || 'en';
|
|
18
|
+
return {
|
|
19
|
+
platform: getTelegramPlatform(),
|
|
20
|
+
app_version: getTelegramVersion(),
|
|
21
|
+
language: telegramLang || browserLang,
|
|
22
|
+
screen_width: window.screen.width,
|
|
23
|
+
screen_height: window.screen.height,
|
|
24
|
+
viewport_width: window.innerWidth,
|
|
25
|
+
viewport_height: window.innerHeight,
|
|
26
|
+
user_agent: navigator.userAgent,
|
|
27
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC',
|
|
28
|
+
telegram_version: getTelegramVersion(),
|
|
29
|
+
theme: getTelegramTheme(),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function getViewportSize() {
|
|
33
|
+
if (typeof window === 'undefined') {
|
|
34
|
+
return { width: 0, height: 0 };
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
width: window.innerWidth,
|
|
38
|
+
height: window.innerHeight,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function getScrollDepth() {
|
|
42
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
const windowHeight = window.innerHeight;
|
|
46
|
+
const documentHeight = document.documentElement.scrollHeight;
|
|
47
|
+
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
|
48
|
+
if (documentHeight <= windowHeight) {
|
|
49
|
+
return 100;
|
|
50
|
+
}
|
|
51
|
+
const maxScroll = documentHeight - windowHeight;
|
|
52
|
+
const scrollPercentage = (scrollTop / maxScroll) * 100;
|
|
53
|
+
return Math.min(Math.round(scrollPercentage), 100);
|
|
54
|
+
}
|
|
55
|
+
export function getCurrentUrl() {
|
|
56
|
+
if (typeof window === 'undefined') {
|
|
57
|
+
return '';
|
|
58
|
+
}
|
|
59
|
+
return window.location.href;
|
|
60
|
+
}
|
|
61
|
+
export function getCurrentPath() {
|
|
62
|
+
if (typeof window === 'undefined') {
|
|
63
|
+
return '';
|
|
64
|
+
}
|
|
65
|
+
return window.location.pathname;
|
|
66
|
+
}
|
|
67
|
+
export function getPageTitle() {
|
|
68
|
+
if (typeof document === 'undefined') {
|
|
69
|
+
return '';
|
|
70
|
+
}
|
|
71
|
+
return document.title || '';
|
|
72
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function generateUUID(): string;
|
|
2
|
+
export declare function getTimestamp(): string;
|
|
3
|
+
export declare function throttle<T extends (...args: any[]) => any>(func: T, wait: number): (...args: Parameters<T>) => void;
|
|
4
|
+
export declare function debounce<T extends (...args: any[]) => any>(func: T, wait: number): (...args: Parameters<T>) => void;
|
|
5
|
+
export declare function safeStringify(obj: any): string;
|
|
6
|
+
export declare function isBrowser(): boolean;
|
|
7
|
+
export declare function getElementSelector(element: Element): string;
|
|
8
|
+
export declare function getElementText(element: Element, maxLength?: number): string;
|
|
9
|
+
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/utils/helpers.ts"],"names":[],"mappings":"AAGA,wBAAgB,YAAY,IAAI,MAAM,CAWrC;AAKD,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAKD,wBAAgB,QAAQ,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACxD,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,MAAM,GACX,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CAuBlC;AAKD,wBAAgB,QAAQ,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACxD,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,MAAM,GACX,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CAWlC;AAKD,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAM9C;AAKD,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAKD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAa3D;AAKD,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,SAAK,GAAG,MAAM,CAGvE"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export function generateUUID() {
|
|
2
|
+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
3
|
+
return crypto.randomUUID();
|
|
4
|
+
}
|
|
5
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
6
|
+
const r = (Math.random() * 16) | 0;
|
|
7
|
+
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
8
|
+
return v.toString(16);
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
export function getTimestamp() {
|
|
12
|
+
return new Date().toISOString();
|
|
13
|
+
}
|
|
14
|
+
export function throttle(func, wait) {
|
|
15
|
+
let timeout = null;
|
|
16
|
+
let previous = 0;
|
|
17
|
+
return function (...args) {
|
|
18
|
+
const now = Date.now();
|
|
19
|
+
const remaining = wait - (now - previous);
|
|
20
|
+
if (remaining <= 0 || remaining > wait) {
|
|
21
|
+
if (timeout) {
|
|
22
|
+
clearTimeout(timeout);
|
|
23
|
+
timeout = null;
|
|
24
|
+
}
|
|
25
|
+
previous = now;
|
|
26
|
+
func.apply(this, args);
|
|
27
|
+
}
|
|
28
|
+
else if (!timeout) {
|
|
29
|
+
timeout = setTimeout(() => {
|
|
30
|
+
previous = Date.now();
|
|
31
|
+
timeout = null;
|
|
32
|
+
func.apply(this, args);
|
|
33
|
+
}, remaining);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export function debounce(func, wait) {
|
|
38
|
+
let timeout = null;
|
|
39
|
+
return function (...args) {
|
|
40
|
+
if (timeout) {
|
|
41
|
+
clearTimeout(timeout);
|
|
42
|
+
}
|
|
43
|
+
timeout = setTimeout(() => {
|
|
44
|
+
func.apply(this, args);
|
|
45
|
+
}, wait);
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export function safeStringify(obj) {
|
|
49
|
+
try {
|
|
50
|
+
return JSON.stringify(obj);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
return '{}';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export function isBrowser() {
|
|
57
|
+
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
58
|
+
}
|
|
59
|
+
export function getElementSelector(element) {
|
|
60
|
+
if (element.id) {
|
|
61
|
+
return `#${element.id}`;
|
|
62
|
+
}
|
|
63
|
+
if (element.className && typeof element.className === 'string') {
|
|
64
|
+
const classes = element.className.trim().split(/\s+/).slice(0, 2).join('.');
|
|
65
|
+
if (classes) {
|
|
66
|
+
return `${element.tagName.toLowerCase()}.${classes}`;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return element.tagName.toLowerCase();
|
|
70
|
+
}
|
|
71
|
+
export function getElementText(element, maxLength = 50) {
|
|
72
|
+
const text = element.textContent?.trim() || '';
|
|
73
|
+
return text.length > maxLength ? text.substring(0, maxLength) + '...' : text;
|
|
74
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TelegramWebApp } from '../types';
|
|
2
|
+
export declare function getTelegramWebApp(): TelegramWebApp | null;
|
|
3
|
+
export declare function isTelegramMiniApp(): boolean;
|
|
4
|
+
export declare function getTelegramUserId(): string | null;
|
|
5
|
+
export declare function getTelegramPlatform(): string;
|
|
6
|
+
export declare function getTelegramVersion(): string;
|
|
7
|
+
export declare function getTelegramLanguage(): string | null;
|
|
8
|
+
export declare function getTelegramTheme(): string;
|
|
9
|
+
export declare function subscribeToTelegramEvent(event: string, callback: () => void): () => void;
|
|
10
|
+
//# sourceMappingURL=telegram.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telegram.d.ts","sourceRoot":"","sources":["../../src/utils/telegram.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,UAAU,CAAC;AAK/D,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,IAAI,CAOzD;AAKD,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAKD,wBAAgB,iBAAiB,IAAI,MAAM,GAAG,IAAI,CAIjD;AAKD,wBAAgB,mBAAmB,IAAI,MAAM,CAG5C;AAKD,wBAAgB,kBAAkB,IAAI,MAAM,CAG3C;AAKD,wBAAgB,mBAAmB,IAAI,MAAM,GAAG,IAAI,CAGnD;AAKD,wBAAgB,gBAAgB,IAAI,MAAM,CAmBzC;AAKD,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,IAAI,GACnB,MAAM,IAAI,CAgBZ"}
|