@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.
Files changed (48) hide show
  1. package/dist/attribution.d.ts +47 -0
  2. package/dist/attribution.d.ts.map +1 -0
  3. package/dist/attribution.js +228 -0
  4. package/dist/cjs/attribution.d.ts +47 -0
  5. package/dist/cjs/attribution.d.ts.map +1 -0
  6. package/dist/cjs/attribution.js +228 -0
  7. package/dist/cjs/attribution.js.map +1 -0
  8. package/dist/cjs/heartbeat.d.ts +1 -0
  9. package/dist/cjs/heartbeat.d.ts.map +1 -1
  10. package/dist/cjs/heartbeat.js +1 -1
  11. package/dist/cjs/heartbeat.js.map +1 -1
  12. package/dist/cjs/index.d.ts +25 -0
  13. package/dist/cjs/index.d.ts.map +1 -1
  14. package/dist/cjs/index.js.map +1 -1
  15. package/dist/cjs/page-tracking.d.ts +25 -0
  16. package/dist/cjs/page-tracking.d.ts.map +1 -1
  17. package/dist/cjs/page-tracking.js +158 -9
  18. package/dist/cjs/page-tracking.js.map +1 -1
  19. package/dist/esm/attribution.d.ts +47 -0
  20. package/dist/esm/attribution.d.ts.map +1 -0
  21. package/dist/esm/attribution.js +218 -0
  22. package/dist/esm/attribution.js.map +1 -0
  23. package/dist/esm/heartbeat.d.ts +1 -0
  24. package/dist/esm/heartbeat.d.ts.map +1 -1
  25. package/dist/esm/heartbeat.js +1 -1
  26. package/dist/esm/heartbeat.js.map +1 -1
  27. package/dist/esm/index.d.ts +25 -0
  28. package/dist/esm/index.d.ts.map +1 -1
  29. package/dist/esm/index.js.map +1 -1
  30. package/dist/esm/page-tracking.d.ts +25 -0
  31. package/dist/esm/page-tracking.d.ts.map +1 -1
  32. package/dist/esm/page-tracking.js +158 -9
  33. package/dist/esm/page-tracking.js.map +1 -1
  34. package/dist/heartbeat.d.ts +1 -0
  35. package/dist/heartbeat.d.ts.map +1 -1
  36. package/dist/heartbeat.js +1 -1
  37. package/dist/index.d.ts +25 -0
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.global.dev.js +464 -11
  40. package/dist/index.global.dev.js.map +3 -3
  41. package/dist/index.global.js +2 -2
  42. package/dist/index.global.js.map +4 -4
  43. package/dist/index.js +157 -1
  44. package/dist/index.mjs +155 -0
  45. package/dist/page-tracking.d.ts +25 -0
  46. package/dist/page-tracking.d.ts.map +1 -1
  47. package/dist/page-tracking.js +158 -9
  48. 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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"}
@@ -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;IA0CrB;;OAEG;IACH,OAAO,IAAI,IAAI;CAchB"}
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"}
@@ -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":"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"}
1
+ {"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../../src/heartbeat.ts"],"names":[],"mappings":"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"}
@@ -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
  */