core-outline 1.1.21 → 1.1.23
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 +81 -50
- package/dist/index.es.js +404 -2403
- package/dist/index.js +404 -2403
- package/docs/changelog/270626-143000_tracking_pipeline_changelog.md +44 -0
- package/package.json +2 -2
- package/src/components/CoreOutline/CoreOutline.js +138 -355
- package/src/components/CoreOutline/helpers.js +66 -0
- package/src/components/CoreOutline/ingest.js +116 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
+
|
|
3
|
+
const ANON_ID_KEY = 'co_anon_id';
|
|
4
|
+
const SESSION_ID_KEY = 'co_session_id';
|
|
5
|
+
|
|
6
|
+
export function getAnonymousId() {
|
|
7
|
+
let id = localStorage.getItem(ANON_ID_KEY);
|
|
8
|
+
if (!id) {
|
|
9
|
+
id = uuidv4();
|
|
10
|
+
localStorage.setItem(ANON_ID_KEY, id);
|
|
11
|
+
}
|
|
12
|
+
return id;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getSessionId() {
|
|
16
|
+
let id = sessionStorage.getItem(SESSION_ID_KEY);
|
|
17
|
+
if (!id) {
|
|
18
|
+
id = uuidv4();
|
|
19
|
+
sessionStorage.setItem(SESSION_ID_KEY, id);
|
|
20
|
+
}
|
|
21
|
+
return id;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function getUtmParams() {
|
|
25
|
+
const params = new URLSearchParams(window.location.search);
|
|
26
|
+
return {
|
|
27
|
+
utm_source: params.get('utm_source') || undefined,
|
|
28
|
+
utm_medium: params.get('utm_medium') || undefined,
|
|
29
|
+
utm_campaign: params.get('utm_campaign') || undefined,
|
|
30
|
+
utm_term: params.get('utm_term') || undefined,
|
|
31
|
+
utm_content: params.get('utm_content') || undefined,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function detectDeviceType() {
|
|
36
|
+
const ua = navigator.userAgent;
|
|
37
|
+
if (/Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(ua)) {
|
|
38
|
+
if (/iPad|Tablet/i.test(ua)) return 'tablet';
|
|
39
|
+
return 'mobile';
|
|
40
|
+
}
|
|
41
|
+
return 'desktop';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function detectOS() {
|
|
45
|
+
const ua = navigator.userAgent;
|
|
46
|
+
if (/iPhone|iPad|iPod/i.test(ua)) return 'iOS';
|
|
47
|
+
if (/Android/i.test(ua)) return 'Android';
|
|
48
|
+
if (/Win/i.test(ua)) return 'Windows';
|
|
49
|
+
if (/Mac/i.test(ua)) return 'macOS';
|
|
50
|
+
if (/Linux/i.test(ua)) return 'Linux';
|
|
51
|
+
return 'Other';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function getBrowserName() {
|
|
55
|
+
const ua = navigator.userAgent;
|
|
56
|
+
if (ua.indexOf('Firefox') > -1) return 'Firefox';
|
|
57
|
+
if (ua.indexOf('Opera') > -1 || ua.indexOf('OPR') > -1) return 'Opera';
|
|
58
|
+
if (ua.indexOf('Chrome') > -1) return 'Chrome';
|
|
59
|
+
if (ua.indexOf('Safari') > -1) return 'Safari';
|
|
60
|
+
if (ua.indexOf('MSIE') > -1 || ua.indexOf('Trident/') > -1) return 'IE';
|
|
61
|
+
return 'Unknown';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function getPagePath() {
|
|
65
|
+
return window.location.pathname;
|
|
66
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
+
|
|
3
|
+
const INGEST_BASE = 'https://atlas-orchestrator.atlas.coreoutline.com';
|
|
4
|
+
const FLUSH_INTERVAL_MS = 5000;
|
|
5
|
+
|
|
6
|
+
let _token = null;
|
|
7
|
+
let _warehouseId = null;
|
|
8
|
+
let _organizationId = null;
|
|
9
|
+
let _dataSourceId = null;
|
|
10
|
+
let _buffer = [];
|
|
11
|
+
let _flushInterval = null;
|
|
12
|
+
|
|
13
|
+
export async function initIngest(warehouseId, dataSourceId, dataSourceSecret) {
|
|
14
|
+
_warehouseId = warehouseId;
|
|
15
|
+
_dataSourceId = dataSourceId;
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const res = await fetch(`${INGEST_BASE}/api/ingest/authorize`, {
|
|
19
|
+
method: 'POST',
|
|
20
|
+
headers: { 'Content-Type': 'application/json' },
|
|
21
|
+
body: JSON.stringify({
|
|
22
|
+
warehouse_id: warehouseId,
|
|
23
|
+
data_source_id: dataSourceId,
|
|
24
|
+
data_source_secret: dataSourceSecret,
|
|
25
|
+
}),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (!res.ok) {
|
|
29
|
+
console.error('[CoreOutline] Authorization failed:', res.status);
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const data = await res.json();
|
|
34
|
+
_token = data.access_token;
|
|
35
|
+
_organizationId = data.organization_id;
|
|
36
|
+
return true;
|
|
37
|
+
} catch (err) {
|
|
38
|
+
console.error('[CoreOutline] Authorization error:', err);
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function trackEvent(eventType, payload) {
|
|
44
|
+
if (!_token) return;
|
|
45
|
+
|
|
46
|
+
const event = {
|
|
47
|
+
event_id: uuidv4(),
|
|
48
|
+
event_type: eventType,
|
|
49
|
+
event_ts: new Date().toISOString(),
|
|
50
|
+
...payload,
|
|
51
|
+
};
|
|
52
|
+
_buffer.push(event);
|
|
53
|
+
|
|
54
|
+
if (!_flushInterval) {
|
|
55
|
+
_flushInterval = setInterval(flush, FLUSH_INTERVAL_MS);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function flush() {
|
|
60
|
+
if (!_token || _buffer.length === 0) return;
|
|
61
|
+
|
|
62
|
+
const events = _buffer.splice(0, _buffer.length);
|
|
63
|
+
const body = JSON.stringify({
|
|
64
|
+
warehouse_id: _warehouseId,
|
|
65
|
+
organization_id: _organizationId,
|
|
66
|
+
events,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const headers = {
|
|
70
|
+
'Content-Type': 'application/json',
|
|
71
|
+
Authorization: `Bearer ${_token}`,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
if (navigator.sendBeacon) {
|
|
75
|
+
const blob = new Blob([body], { type: 'application/json' });
|
|
76
|
+
navigator.sendBeacon(`${INGEST_BASE}/api/ingest/events`, blob);
|
|
77
|
+
} else {
|
|
78
|
+
fetch(`${INGEST_BASE}/api/ingest/events`, {
|
|
79
|
+
method: 'POST',
|
|
80
|
+
headers,
|
|
81
|
+
body,
|
|
82
|
+
keepalive: true,
|
|
83
|
+
}).catch((err) => console.error('[CoreOutline] Flush error:', err));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function sendRrwebBatch(chunks, sessionId) {
|
|
88
|
+
if (!_token || !chunks || chunks.length === 0) return;
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
await fetch(`${INGEST_BASE}/api/ingest/rrweb`, {
|
|
92
|
+
method: 'POST',
|
|
93
|
+
headers: {
|
|
94
|
+
'Content-Type': 'application/json',
|
|
95
|
+
Authorization: `Bearer ${_token}`,
|
|
96
|
+
},
|
|
97
|
+
body: JSON.stringify({
|
|
98
|
+
warehouse_id: _warehouseId,
|
|
99
|
+
organization_id: _organizationId,
|
|
100
|
+
session_id: sessionId,
|
|
101
|
+
data_source_id: _dataSourceId,
|
|
102
|
+
chunks,
|
|
103
|
+
}),
|
|
104
|
+
keepalive: true,
|
|
105
|
+
});
|
|
106
|
+
} catch (err) {
|
|
107
|
+
console.error('[CoreOutline] rrweb upload error:', err);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function stopFlushInterval() {
|
|
112
|
+
if (_flushInterval) {
|
|
113
|
+
clearInterval(_flushInterval);
|
|
114
|
+
_flushInterval = null;
|
|
115
|
+
}
|
|
116
|
+
}
|