@grainql/analytics-web 2.2.1 → 2.3.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/dist/attribution.d.ts +47 -0
- package/dist/attribution.d.ts.map +1 -0
- package/dist/attribution.js +228 -0
- package/dist/cjs/attribution.d.ts +47 -0
- package/dist/cjs/attribution.d.ts.map +1 -0
- package/dist/cjs/attribution.js +228 -0
- package/dist/cjs/attribution.js.map +1 -0
- package/dist/cjs/heartbeat.d.ts +1 -0
- package/dist/cjs/heartbeat.d.ts.map +1 -1
- package/dist/cjs/heartbeat.js +1 -1
- package/dist/cjs/heartbeat.js.map +1 -1
- package/dist/cjs/index.d.ts +25 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/page-tracking.d.ts +25 -0
- package/dist/cjs/page-tracking.d.ts.map +1 -1
- package/dist/cjs/page-tracking.js +158 -9
- package/dist/cjs/page-tracking.js.map +1 -1
- package/dist/esm/attribution.d.ts +47 -0
- package/dist/esm/attribution.d.ts.map +1 -0
- package/dist/esm/attribution.js +218 -0
- package/dist/esm/attribution.js.map +1 -0
- package/dist/esm/heartbeat.d.ts +1 -0
- package/dist/esm/heartbeat.d.ts.map +1 -1
- package/dist/esm/heartbeat.js +1 -1
- package/dist/esm/heartbeat.js.map +1 -1
- package/dist/esm/index.d.ts +25 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/page-tracking.d.ts +25 -0
- package/dist/esm/page-tracking.d.ts.map +1 -1
- package/dist/esm/page-tracking.js +158 -9
- package/dist/esm/page-tracking.js.map +1 -1
- package/dist/heartbeat.d.ts +1 -0
- package/dist/heartbeat.d.ts.map +1 -1
- package/dist/heartbeat.js +1 -1
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.global.dev.js +464 -11
- package/dist/index.global.dev.js.map +3 -3
- package/dist/index.global.js +2 -2
- package/dist/index.global.js.map +4 -4
- package/dist/index.js +157 -1
- package/dist/index.mjs +155 -0
- package/dist/page-tracking.d.ts +25 -0
- package/dist/page-tracking.d.ts.map +1 -1
- package/dist/page-tracking.js +158 -9
- package/package.json +1 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attribution and Referral Tracking for Grain Analytics
|
|
3
|
+
* Handles referral categorization and first-touch attribution
|
|
4
|
+
*/
|
|
5
|
+
export type ReferrerCategory = 'organic' | 'paid' | 'social' | 'direct' | 'email' | 'referral';
|
|
6
|
+
export interface UTMParameters {
|
|
7
|
+
utm_source?: string;
|
|
8
|
+
utm_medium?: string;
|
|
9
|
+
utm_campaign?: string;
|
|
10
|
+
utm_term?: string;
|
|
11
|
+
utm_content?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface FirstTouchAttribution {
|
|
14
|
+
source: string;
|
|
15
|
+
medium: string;
|
|
16
|
+
campaign: string;
|
|
17
|
+
referrer: string;
|
|
18
|
+
referrer_category: ReferrerCategory;
|
|
19
|
+
timestamp: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Categorize referrer based on domain and parameters
|
|
23
|
+
*/
|
|
24
|
+
export declare function categorizeReferrer(referrer: string, currentUrl?: string): ReferrerCategory;
|
|
25
|
+
/**
|
|
26
|
+
* Parse UTM parameters from URL
|
|
27
|
+
*/
|
|
28
|
+
export declare function parseUTMParameters(url: string): UTMParameters;
|
|
29
|
+
/**
|
|
30
|
+
* Get first-touch attribution from localStorage
|
|
31
|
+
*/
|
|
32
|
+
export declare function getFirstTouchAttribution(tenantId: string): FirstTouchAttribution | null;
|
|
33
|
+
/**
|
|
34
|
+
* Set first-touch attribution in localStorage
|
|
35
|
+
*/
|
|
36
|
+
export declare function setFirstTouchAttribution(tenantId: string, attribution: FirstTouchAttribution): void;
|
|
37
|
+
/**
|
|
38
|
+
* Get or create first-touch attribution
|
|
39
|
+
*/
|
|
40
|
+
export declare function getOrCreateFirstTouchAttribution(tenantId: string, referrer: string, currentUrl: string, utmParams: UTMParameters): FirstTouchAttribution;
|
|
41
|
+
export declare function getSessionUTMParameters(): UTMParameters | null;
|
|
42
|
+
export declare function setSessionUTMParameters(params: UTMParameters): void;
|
|
43
|
+
/**
|
|
44
|
+
* Clear session UTM parameters
|
|
45
|
+
*/
|
|
46
|
+
export declare function clearSessionUTMParameters(): void;
|
|
47
|
+
//# sourceMappingURL=attribution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attribution.d.ts","sourceRoot":"","sources":["../src/attribution.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;AAE/F,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,gBAAgB,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;CACnB;AAkFD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,GAAE,MAAW,GAAG,gBAAgB,CAsC9F;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAqB7D;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,qBAAqB,GAAG,IAAI,CAgBvF;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,qBAAqB,GACjC,IAAI,CAWN;AAED;;GAEG;AACH,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,aAAa,GACvB,qBAAqB,CAwBvB;AAOD,wBAAgB,uBAAuB,IAAI,aAAa,GAAG,IAAI,CAE9D;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAEnE;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Attribution and Referral Tracking for Grain Analytics
|
|
4
|
+
* Handles referral categorization and first-touch attribution
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.categorizeReferrer = categorizeReferrer;
|
|
8
|
+
exports.parseUTMParameters = parseUTMParameters;
|
|
9
|
+
exports.getFirstTouchAttribution = getFirstTouchAttribution;
|
|
10
|
+
exports.setFirstTouchAttribution = setFirstTouchAttribution;
|
|
11
|
+
exports.getOrCreateFirstTouchAttribution = getOrCreateFirstTouchAttribution;
|
|
12
|
+
exports.getSessionUTMParameters = getSessionUTMParameters;
|
|
13
|
+
exports.setSessionUTMParameters = setSessionUTMParameters;
|
|
14
|
+
exports.clearSessionUTMParameters = clearSessionUTMParameters;
|
|
15
|
+
/**
|
|
16
|
+
* Known paid search parameters
|
|
17
|
+
*/
|
|
18
|
+
const PAID_SEARCH_PARAMS = [
|
|
19
|
+
'gclid', // Google Ads
|
|
20
|
+
'msclkid', // Microsoft Ads
|
|
21
|
+
'fbclid', // Facebook Ads
|
|
22
|
+
'ttclid', // TikTok Ads
|
|
23
|
+
'li_fat_id', // LinkedIn Ads
|
|
24
|
+
'twclid', // Twitter Ads
|
|
25
|
+
'ScCid', // Snapchat Ads
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* Known social media domains
|
|
29
|
+
*/
|
|
30
|
+
const SOCIAL_DOMAINS = [
|
|
31
|
+
'facebook.com',
|
|
32
|
+
'twitter.com',
|
|
33
|
+
'x.com',
|
|
34
|
+
'linkedin.com',
|
|
35
|
+
'instagram.com',
|
|
36
|
+
'pinterest.com',
|
|
37
|
+
'reddit.com',
|
|
38
|
+
'tiktok.com',
|
|
39
|
+
'youtube.com',
|
|
40
|
+
'snapchat.com',
|
|
41
|
+
't.co', // Twitter short links
|
|
42
|
+
'fb.me', // Facebook short links
|
|
43
|
+
'lnkd.in', // LinkedIn short links
|
|
44
|
+
];
|
|
45
|
+
/**
|
|
46
|
+
* Known organic search engines
|
|
47
|
+
*/
|
|
48
|
+
const SEARCH_ENGINES = [
|
|
49
|
+
'google.',
|
|
50
|
+
'bing.com',
|
|
51
|
+
'yahoo.com',
|
|
52
|
+
'duckduckgo.com',
|
|
53
|
+
'baidu.com',
|
|
54
|
+
'yandex.com',
|
|
55
|
+
'ecosia.org',
|
|
56
|
+
'ask.com',
|
|
57
|
+
];
|
|
58
|
+
/**
|
|
59
|
+
* Email client domains
|
|
60
|
+
*/
|
|
61
|
+
const EMAIL_DOMAINS = [
|
|
62
|
+
'mail.google.com',
|
|
63
|
+
'outlook.live.com',
|
|
64
|
+
'mail.yahoo.com',
|
|
65
|
+
'mail.aol.com',
|
|
66
|
+
];
|
|
67
|
+
/**
|
|
68
|
+
* Extract domain from URL
|
|
69
|
+
*/
|
|
70
|
+
function extractDomain(url) {
|
|
71
|
+
try {
|
|
72
|
+
const urlObj = new URL(url);
|
|
73
|
+
return urlObj.hostname.toLowerCase();
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return '';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Check if URL contains paid search parameters
|
|
81
|
+
*/
|
|
82
|
+
function hasPaidSearchParams(url) {
|
|
83
|
+
try {
|
|
84
|
+
const urlObj = new URL(url);
|
|
85
|
+
return PAID_SEARCH_PARAMS.some(param => urlObj.searchParams.has(param));
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Categorize referrer based on domain and parameters
|
|
93
|
+
*/
|
|
94
|
+
function categorizeReferrer(referrer, currentUrl = '') {
|
|
95
|
+
// Direct traffic (no referrer)
|
|
96
|
+
if (!referrer || referrer.trim() === '') {
|
|
97
|
+
return 'direct';
|
|
98
|
+
}
|
|
99
|
+
const domain = extractDomain(referrer);
|
|
100
|
+
// Same domain = direct
|
|
101
|
+
if (currentUrl) {
|
|
102
|
+
const currentDomain = extractDomain(currentUrl);
|
|
103
|
+
if (domain === currentDomain) {
|
|
104
|
+
return 'direct';
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Check for paid search parameters
|
|
108
|
+
if (hasPaidSearchParams(referrer) || hasPaidSearchParams(currentUrl)) {
|
|
109
|
+
return 'paid';
|
|
110
|
+
}
|
|
111
|
+
// Email clients
|
|
112
|
+
if (EMAIL_DOMAINS.some(emailDomain => domain.includes(emailDomain))) {
|
|
113
|
+
return 'email';
|
|
114
|
+
}
|
|
115
|
+
// Social media
|
|
116
|
+
if (SOCIAL_DOMAINS.some(socialDomain => domain.includes(socialDomain))) {
|
|
117
|
+
return 'social';
|
|
118
|
+
}
|
|
119
|
+
// Organic search engines
|
|
120
|
+
if (SEARCH_ENGINES.some(searchEngine => domain.includes(searchEngine))) {
|
|
121
|
+
return 'organic';
|
|
122
|
+
}
|
|
123
|
+
// Everything else is referral
|
|
124
|
+
return 'referral';
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Parse UTM parameters from URL
|
|
128
|
+
*/
|
|
129
|
+
function parseUTMParameters(url) {
|
|
130
|
+
try {
|
|
131
|
+
const urlObj = new URL(url);
|
|
132
|
+
const params = {};
|
|
133
|
+
const utmSource = urlObj.searchParams.get('utm_source');
|
|
134
|
+
const utmMedium = urlObj.searchParams.get('utm_medium');
|
|
135
|
+
const utmCampaign = urlObj.searchParams.get('utm_campaign');
|
|
136
|
+
const utmTerm = urlObj.searchParams.get('utm_term');
|
|
137
|
+
const utmContent = urlObj.searchParams.get('utm_content');
|
|
138
|
+
if (utmSource)
|
|
139
|
+
params.utm_source = utmSource;
|
|
140
|
+
if (utmMedium)
|
|
141
|
+
params.utm_medium = utmMedium;
|
|
142
|
+
if (utmCampaign)
|
|
143
|
+
params.utm_campaign = utmCampaign;
|
|
144
|
+
if (utmTerm)
|
|
145
|
+
params.utm_term = utmTerm;
|
|
146
|
+
if (utmContent)
|
|
147
|
+
params.utm_content = utmContent;
|
|
148
|
+
return params;
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return {};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Get first-touch attribution from localStorage
|
|
156
|
+
*/
|
|
157
|
+
function getFirstTouchAttribution(tenantId) {
|
|
158
|
+
if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
const key = `_grain_first_touch_${tenantId}`;
|
|
163
|
+
const stored = localStorage.getItem(key);
|
|
164
|
+
if (stored) {
|
|
165
|
+
return JSON.parse(stored);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
console.warn('[Grain Attribution] Failed to retrieve first-touch attribution:', error);
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Set first-touch attribution in localStorage
|
|
175
|
+
*/
|
|
176
|
+
function setFirstTouchAttribution(tenantId, attribution) {
|
|
177
|
+
if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
const key = `_grain_first_touch_${tenantId}`;
|
|
182
|
+
localStorage.setItem(key, JSON.stringify(attribution));
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
console.warn('[Grain Attribution] Failed to store first-touch attribution:', error);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Get or create first-touch attribution
|
|
190
|
+
*/
|
|
191
|
+
function getOrCreateFirstTouchAttribution(tenantId, referrer, currentUrl, utmParams) {
|
|
192
|
+
// Try to get existing first-touch
|
|
193
|
+
const existing = getFirstTouchAttribution(tenantId);
|
|
194
|
+
if (existing) {
|
|
195
|
+
return existing;
|
|
196
|
+
}
|
|
197
|
+
// Create new first-touch attribution
|
|
198
|
+
const referrerCategory = categorizeReferrer(referrer, currentUrl);
|
|
199
|
+
const referrerDomain = extractDomain(referrer);
|
|
200
|
+
const firstTouch = {
|
|
201
|
+
source: utmParams.utm_source || referrerDomain || 'direct',
|
|
202
|
+
medium: utmParams.utm_medium || referrerCategory,
|
|
203
|
+
campaign: utmParams.utm_campaign || 'none',
|
|
204
|
+
referrer: referrer || 'direct',
|
|
205
|
+
referrer_category: referrerCategory,
|
|
206
|
+
timestamp: Date.now(),
|
|
207
|
+
};
|
|
208
|
+
// Store it
|
|
209
|
+
setFirstTouchAttribution(tenantId, firstTouch);
|
|
210
|
+
return firstTouch;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Get session UTM parameters (memory-only, not persisted across page loads)
|
|
214
|
+
*/
|
|
215
|
+
let sessionUTMParams = null;
|
|
216
|
+
function getSessionUTMParameters() {
|
|
217
|
+
return sessionUTMParams;
|
|
218
|
+
}
|
|
219
|
+
function setSessionUTMParameters(params) {
|
|
220
|
+
sessionUTMParams = params;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Clear session UTM parameters
|
|
224
|
+
*/
|
|
225
|
+
function clearSessionUTMParameters() {
|
|
226
|
+
sessionUTMParams = null;
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=attribution.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attribution and Referral Tracking for Grain Analytics
|
|
3
|
+
* Handles referral categorization and first-touch attribution
|
|
4
|
+
*/
|
|
5
|
+
export type ReferrerCategory = 'organic' | 'paid' | 'social' | 'direct' | 'email' | 'referral';
|
|
6
|
+
export interface UTMParameters {
|
|
7
|
+
utm_source?: string;
|
|
8
|
+
utm_medium?: string;
|
|
9
|
+
utm_campaign?: string;
|
|
10
|
+
utm_term?: string;
|
|
11
|
+
utm_content?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface FirstTouchAttribution {
|
|
14
|
+
source: string;
|
|
15
|
+
medium: string;
|
|
16
|
+
campaign: string;
|
|
17
|
+
referrer: string;
|
|
18
|
+
referrer_category: ReferrerCategory;
|
|
19
|
+
timestamp: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Categorize referrer based on domain and parameters
|
|
23
|
+
*/
|
|
24
|
+
export declare function categorizeReferrer(referrer: string, currentUrl?: string): ReferrerCategory;
|
|
25
|
+
/**
|
|
26
|
+
* Parse UTM parameters from URL
|
|
27
|
+
*/
|
|
28
|
+
export declare function parseUTMParameters(url: string): UTMParameters;
|
|
29
|
+
/**
|
|
30
|
+
* Get first-touch attribution from localStorage
|
|
31
|
+
*/
|
|
32
|
+
export declare function getFirstTouchAttribution(tenantId: string): FirstTouchAttribution | null;
|
|
33
|
+
/**
|
|
34
|
+
* Set first-touch attribution in localStorage
|
|
35
|
+
*/
|
|
36
|
+
export declare function setFirstTouchAttribution(tenantId: string, attribution: FirstTouchAttribution): void;
|
|
37
|
+
/**
|
|
38
|
+
* Get or create first-touch attribution
|
|
39
|
+
*/
|
|
40
|
+
export declare function getOrCreateFirstTouchAttribution(tenantId: string, referrer: string, currentUrl: string, utmParams: UTMParameters): FirstTouchAttribution;
|
|
41
|
+
export declare function getSessionUTMParameters(): UTMParameters | null;
|
|
42
|
+
export declare function setSessionUTMParameters(params: UTMParameters): void;
|
|
43
|
+
/**
|
|
44
|
+
* Clear session UTM parameters
|
|
45
|
+
*/
|
|
46
|
+
export declare function clearSessionUTMParameters(): void;
|
|
47
|
+
//# sourceMappingURL=attribution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attribution.d.ts","sourceRoot":"","sources":["../../src/attribution.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;AAE/F,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,gBAAgB,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;CACnB;AAkFD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,GAAE,MAAW,GAAG,gBAAgB,CAsC9F;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAqB7D;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,qBAAqB,GAAG,IAAI,CAgBvF;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,qBAAqB,GACjC,IAAI,CAWN;AAED;;GAEG;AACH,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,aAAa,GACvB,qBAAqB,CAwBvB;AAOD,wBAAgB,uBAAuB,IAAI,aAAa,GAAG,IAAI,CAE9D;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAEnE;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Attribution and Referral Tracking for Grain Analytics
|
|
4
|
+
* Handles referral categorization and first-touch attribution
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.categorizeReferrer = categorizeReferrer;
|
|
8
|
+
exports.parseUTMParameters = parseUTMParameters;
|
|
9
|
+
exports.getFirstTouchAttribution = getFirstTouchAttribution;
|
|
10
|
+
exports.setFirstTouchAttribution = setFirstTouchAttribution;
|
|
11
|
+
exports.getOrCreateFirstTouchAttribution = getOrCreateFirstTouchAttribution;
|
|
12
|
+
exports.getSessionUTMParameters = getSessionUTMParameters;
|
|
13
|
+
exports.setSessionUTMParameters = setSessionUTMParameters;
|
|
14
|
+
exports.clearSessionUTMParameters = clearSessionUTMParameters;
|
|
15
|
+
/**
|
|
16
|
+
* Known paid search parameters
|
|
17
|
+
*/
|
|
18
|
+
const PAID_SEARCH_PARAMS = [
|
|
19
|
+
'gclid', // Google Ads
|
|
20
|
+
'msclkid', // Microsoft Ads
|
|
21
|
+
'fbclid', // Facebook Ads
|
|
22
|
+
'ttclid', // TikTok Ads
|
|
23
|
+
'li_fat_id', // LinkedIn Ads
|
|
24
|
+
'twclid', // Twitter Ads
|
|
25
|
+
'ScCid', // Snapchat Ads
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* Known social media domains
|
|
29
|
+
*/
|
|
30
|
+
const SOCIAL_DOMAINS = [
|
|
31
|
+
'facebook.com',
|
|
32
|
+
'twitter.com',
|
|
33
|
+
'x.com',
|
|
34
|
+
'linkedin.com',
|
|
35
|
+
'instagram.com',
|
|
36
|
+
'pinterest.com',
|
|
37
|
+
'reddit.com',
|
|
38
|
+
'tiktok.com',
|
|
39
|
+
'youtube.com',
|
|
40
|
+
'snapchat.com',
|
|
41
|
+
't.co', // Twitter short links
|
|
42
|
+
'fb.me', // Facebook short links
|
|
43
|
+
'lnkd.in', // LinkedIn short links
|
|
44
|
+
];
|
|
45
|
+
/**
|
|
46
|
+
* Known organic search engines
|
|
47
|
+
*/
|
|
48
|
+
const SEARCH_ENGINES = [
|
|
49
|
+
'google.',
|
|
50
|
+
'bing.com',
|
|
51
|
+
'yahoo.com',
|
|
52
|
+
'duckduckgo.com',
|
|
53
|
+
'baidu.com',
|
|
54
|
+
'yandex.com',
|
|
55
|
+
'ecosia.org',
|
|
56
|
+
'ask.com',
|
|
57
|
+
];
|
|
58
|
+
/**
|
|
59
|
+
* Email client domains
|
|
60
|
+
*/
|
|
61
|
+
const EMAIL_DOMAINS = [
|
|
62
|
+
'mail.google.com',
|
|
63
|
+
'outlook.live.com',
|
|
64
|
+
'mail.yahoo.com',
|
|
65
|
+
'mail.aol.com',
|
|
66
|
+
];
|
|
67
|
+
/**
|
|
68
|
+
* Extract domain from URL
|
|
69
|
+
*/
|
|
70
|
+
function extractDomain(url) {
|
|
71
|
+
try {
|
|
72
|
+
const urlObj = new URL(url);
|
|
73
|
+
return urlObj.hostname.toLowerCase();
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return '';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Check if URL contains paid search parameters
|
|
81
|
+
*/
|
|
82
|
+
function hasPaidSearchParams(url) {
|
|
83
|
+
try {
|
|
84
|
+
const urlObj = new URL(url);
|
|
85
|
+
return PAID_SEARCH_PARAMS.some(param => urlObj.searchParams.has(param));
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Categorize referrer based on domain and parameters
|
|
93
|
+
*/
|
|
94
|
+
function categorizeReferrer(referrer, currentUrl = '') {
|
|
95
|
+
// Direct traffic (no referrer)
|
|
96
|
+
if (!referrer || referrer.trim() === '') {
|
|
97
|
+
return 'direct';
|
|
98
|
+
}
|
|
99
|
+
const domain = extractDomain(referrer);
|
|
100
|
+
// Same domain = direct
|
|
101
|
+
if (currentUrl) {
|
|
102
|
+
const currentDomain = extractDomain(currentUrl);
|
|
103
|
+
if (domain === currentDomain) {
|
|
104
|
+
return 'direct';
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Check for paid search parameters
|
|
108
|
+
if (hasPaidSearchParams(referrer) || hasPaidSearchParams(currentUrl)) {
|
|
109
|
+
return 'paid';
|
|
110
|
+
}
|
|
111
|
+
// Email clients
|
|
112
|
+
if (EMAIL_DOMAINS.some(emailDomain => domain.includes(emailDomain))) {
|
|
113
|
+
return 'email';
|
|
114
|
+
}
|
|
115
|
+
// Social media
|
|
116
|
+
if (SOCIAL_DOMAINS.some(socialDomain => domain.includes(socialDomain))) {
|
|
117
|
+
return 'social';
|
|
118
|
+
}
|
|
119
|
+
// Organic search engines
|
|
120
|
+
if (SEARCH_ENGINES.some(searchEngine => domain.includes(searchEngine))) {
|
|
121
|
+
return 'organic';
|
|
122
|
+
}
|
|
123
|
+
// Everything else is referral
|
|
124
|
+
return 'referral';
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Parse UTM parameters from URL
|
|
128
|
+
*/
|
|
129
|
+
function parseUTMParameters(url) {
|
|
130
|
+
try {
|
|
131
|
+
const urlObj = new URL(url);
|
|
132
|
+
const params = {};
|
|
133
|
+
const utmSource = urlObj.searchParams.get('utm_source');
|
|
134
|
+
const utmMedium = urlObj.searchParams.get('utm_medium');
|
|
135
|
+
const utmCampaign = urlObj.searchParams.get('utm_campaign');
|
|
136
|
+
const utmTerm = urlObj.searchParams.get('utm_term');
|
|
137
|
+
const utmContent = urlObj.searchParams.get('utm_content');
|
|
138
|
+
if (utmSource)
|
|
139
|
+
params.utm_source = utmSource;
|
|
140
|
+
if (utmMedium)
|
|
141
|
+
params.utm_medium = utmMedium;
|
|
142
|
+
if (utmCampaign)
|
|
143
|
+
params.utm_campaign = utmCampaign;
|
|
144
|
+
if (utmTerm)
|
|
145
|
+
params.utm_term = utmTerm;
|
|
146
|
+
if (utmContent)
|
|
147
|
+
params.utm_content = utmContent;
|
|
148
|
+
return params;
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return {};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Get first-touch attribution from localStorage
|
|
156
|
+
*/
|
|
157
|
+
function getFirstTouchAttribution(tenantId) {
|
|
158
|
+
if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
const key = `_grain_first_touch_${tenantId}`;
|
|
163
|
+
const stored = localStorage.getItem(key);
|
|
164
|
+
if (stored) {
|
|
165
|
+
return JSON.parse(stored);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
console.warn('[Grain Attribution] Failed to retrieve first-touch attribution:', error);
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Set first-touch attribution in localStorage
|
|
175
|
+
*/
|
|
176
|
+
function setFirstTouchAttribution(tenantId, attribution) {
|
|
177
|
+
if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
const key = `_grain_first_touch_${tenantId}`;
|
|
182
|
+
localStorage.setItem(key, JSON.stringify(attribution));
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
console.warn('[Grain Attribution] Failed to store first-touch attribution:', error);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Get or create first-touch attribution
|
|
190
|
+
*/
|
|
191
|
+
function getOrCreateFirstTouchAttribution(tenantId, referrer, currentUrl, utmParams) {
|
|
192
|
+
// Try to get existing first-touch
|
|
193
|
+
const existing = getFirstTouchAttribution(tenantId);
|
|
194
|
+
if (existing) {
|
|
195
|
+
return existing;
|
|
196
|
+
}
|
|
197
|
+
// Create new first-touch attribution
|
|
198
|
+
const referrerCategory = categorizeReferrer(referrer, currentUrl);
|
|
199
|
+
const referrerDomain = extractDomain(referrer);
|
|
200
|
+
const firstTouch = {
|
|
201
|
+
source: utmParams.utm_source || referrerDomain || 'direct',
|
|
202
|
+
medium: utmParams.utm_medium || referrerCategory,
|
|
203
|
+
campaign: utmParams.utm_campaign || 'none',
|
|
204
|
+
referrer: referrer || 'direct',
|
|
205
|
+
referrer_category: referrerCategory,
|
|
206
|
+
timestamp: Date.now(),
|
|
207
|
+
};
|
|
208
|
+
// Store it
|
|
209
|
+
setFirstTouchAttribution(tenantId, firstTouch);
|
|
210
|
+
return firstTouch;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Get session UTM parameters (memory-only, not persisted across page loads)
|
|
214
|
+
*/
|
|
215
|
+
let sessionUTMParams = null;
|
|
216
|
+
function getSessionUTMParameters() {
|
|
217
|
+
return sessionUTMParams;
|
|
218
|
+
}
|
|
219
|
+
function setSessionUTMParameters(params) {
|
|
220
|
+
sessionUTMParams = params;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Clear session UTM parameters
|
|
224
|
+
*/
|
|
225
|
+
function clearSessionUTMParameters() {
|
|
226
|
+
sessionUTMParams = null;
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=attribution.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attribution.js","sourceRoot":"","sources":["../../src/attribution.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/heartbeat.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export interface HeartbeatTracker {
|
|
|
13
13
|
hasConsent(category?: string): boolean;
|
|
14
14
|
getEffectiveUserId(): string;
|
|
15
15
|
getEphemeralSessionId(): string;
|
|
16
|
+
getSessionId(): string;
|
|
16
17
|
getCurrentPage(): string | null;
|
|
17
18
|
getEventCountSinceLastHeartbeat(): number;
|
|
18
19
|
resetEventCountSinceLastHeartbeat(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"heartbeat.d.ts","sourceRoot":"","sources":["../../src/heartbeat.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC/E,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACvC,kBAAkB,IAAI,MAAM,CAAC;IAC7B,qBAAqB,IAAI,MAAM,CAAC;IAChC,cAAc,IAAI,MAAM,GAAG,IAAI,CAAC;IAChC,+BAA+B,IAAI,MAAM,CAAC;IAC1C,iCAAiC,IAAI,IAAI,CAAC;CAC3C;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,wBAAwB,CAAS;gBAGvC,OAAO,EAAE,gBAAgB,EACzB,gBAAgB,EAAE,gBAAgB,EAClC,MAAM,EAAE,eAAe;IAezB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAkB7B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAyB7B;;OAEG;IACH,OAAO,CAAC,aAAa;
|
|
1
|
+
{"version":3,"file":"heartbeat.d.ts","sourceRoot":"","sources":["../../src/heartbeat.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC/E,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACvC,kBAAkB,IAAI,MAAM,CAAC;IAC7B,qBAAqB,IAAI,MAAM,CAAC;IAChC,YAAY,IAAI,MAAM,CAAC;IACvB,cAAc,IAAI,MAAM,GAAG,IAAI,CAAC;IAChC,+BAA+B,IAAI,MAAM,CAAC;IAC1C,iCAAiC,IAAI,IAAI,CAAC;CAC3C;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,wBAAwB,CAAS;gBAGvC,OAAO,EAAE,gBAAgB,EACzB,gBAAgB,EAAE,gBAAgB,EAClC,MAAM,EAAE,eAAe;IAezB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAkB7B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAyB7B;;OAEG;IACH,OAAO,CAAC,aAAa;IA2CrB;;OAEG;IACH,OAAO,IAAI,IAAI;CAchB"}
|
package/dist/cjs/heartbeat.js
CHANGED
|
@@ -74,7 +74,6 @@ class HeartbeatManager {
|
|
|
74
74
|
const hasConsent = this.tracker.hasConsent('analytics');
|
|
75
75
|
// Base properties (always included)
|
|
76
76
|
const properties = {
|
|
77
|
-
type: 'heartbeat',
|
|
78
77
|
heartbeat_type: heartbeatType,
|
|
79
78
|
status: isActive ? 'active' : 'inactive',
|
|
80
79
|
timestamp: now,
|
|
@@ -85,6 +84,7 @@ class HeartbeatManager {
|
|
|
85
84
|
if (page) {
|
|
86
85
|
properties.page = page;
|
|
87
86
|
}
|
|
87
|
+
properties.session_id = this.tracker.getSessionId();
|
|
88
88
|
// Only include duration and event count for periodic heartbeats
|
|
89
89
|
if (heartbeatType === 'periodic') {
|
|
90
90
|
properties.duration = now - this.lastHeartbeatTime;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../../src/heartbeat.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;
|
|
1
|
+
{"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../../src/heartbeat.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -6,6 +6,9 @@ import { ConsentState, ConsentMode } from './consent';
|
|
|
6
6
|
import { CookieConfig } from './cookies';
|
|
7
7
|
import { type HeartbeatTracker } from './heartbeat';
|
|
8
8
|
import { type PageTracker } from './page-tracking';
|
|
9
|
+
import { categorizeReferrer, parseUTMParameters, type ReferrerCategory, type UTMParameters, type FirstTouchAttribution } from './attribution';
|
|
10
|
+
export type { ReferrerCategory, UTMParameters, FirstTouchAttribution };
|
|
11
|
+
export { categorizeReferrer, parseUTMParameters };
|
|
9
12
|
export interface GrainEvent {
|
|
10
13
|
eventName: string;
|
|
11
14
|
userId?: string;
|
|
@@ -213,6 +216,8 @@ export declare class GrainAnalytics implements HeartbeatTracker, PageTracker {
|
|
|
213
216
|
private pageTrackingManager;
|
|
214
217
|
private ephemeralSessionId;
|
|
215
218
|
private eventCountSinceLastHeartbeat;
|
|
219
|
+
private sessionStartTime;
|
|
220
|
+
private sessionEventCount;
|
|
216
221
|
constructor(config: GrainConfig);
|
|
217
222
|
private validateConfig;
|
|
218
223
|
/**
|
|
@@ -285,6 +290,26 @@ export declare class GrainAnalytics implements HeartbeatTracker, PageTracker {
|
|
|
285
290
|
* Initialize automatic tracking (heartbeat and page views)
|
|
286
291
|
*/
|
|
287
292
|
private initializeAutomaticTracking;
|
|
293
|
+
/**
|
|
294
|
+
* Track session start event
|
|
295
|
+
*/
|
|
296
|
+
private trackSessionStart;
|
|
297
|
+
/**
|
|
298
|
+
* Track session end event
|
|
299
|
+
*/
|
|
300
|
+
private trackSessionEnd;
|
|
301
|
+
/**
|
|
302
|
+
* Detect browser name
|
|
303
|
+
*/
|
|
304
|
+
private getBrowser;
|
|
305
|
+
/**
|
|
306
|
+
* Detect operating system
|
|
307
|
+
*/
|
|
308
|
+
private getOS;
|
|
309
|
+
/**
|
|
310
|
+
* Detect device type (Mobile, Tablet, Desktop)
|
|
311
|
+
*/
|
|
312
|
+
private getDeviceType;
|
|
288
313
|
/**
|
|
289
314
|
* Handle consent granted - upgrade ephemeral session to persistent user
|
|
290
315
|
*/
|