@grainql/analytics-web 2.0.0 → 2.1.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/activity.d.ts +59 -0
- package/dist/activity.d.ts.map +1 -0
- package/dist/cjs/activity.d.ts +59 -0
- package/dist/cjs/activity.d.ts.map +1 -0
- package/dist/cjs/activity.js +131 -0
- package/dist/cjs/activity.js.map +1 -0
- package/dist/cjs/consent.d.ts +68 -0
- package/dist/cjs/consent.d.ts.map +1 -0
- package/dist/cjs/consent.js +191 -0
- package/dist/cjs/consent.js.map +1 -0
- package/dist/cjs/cookies.d.ts +28 -0
- package/dist/cjs/cookies.d.ts.map +1 -0
- package/dist/cjs/cookies.js +95 -0
- package/dist/cjs/cookies.js.map +1 -0
- package/dist/cjs/heartbeat.d.ts +42 -0
- package/dist/cjs/heartbeat.d.ts.map +1 -0
- package/dist/cjs/heartbeat.js +92 -0
- package/dist/cjs/heartbeat.js.map +1 -0
- package/dist/cjs/index.d.ts +100 -3
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/page-tracking.d.ts +60 -0
- package/dist/cjs/page-tracking.d.ts.map +1 -0
- package/dist/cjs/page-tracking.js +180 -0
- package/dist/cjs/page-tracking.js.map +1 -0
- package/dist/cjs/react/components/ConsentBanner.d.ts +16 -0
- package/dist/cjs/react/components/ConsentBanner.d.ts.map +1 -0
- package/dist/cjs/react/components/ConsentBanner.js +112 -0
- package/dist/cjs/react/components/ConsentBanner.js.map +1 -0
- package/dist/cjs/react/components/CookieNotice.d.ts +12 -0
- package/dist/cjs/react/components/CookieNotice.d.ts.map +1 -0
- package/dist/cjs/react/components/CookieNotice.js +62 -0
- package/dist/cjs/react/components/CookieNotice.js.map +1 -0
- package/dist/cjs/react/components/PrivacyPreferenceCenter.d.ts +12 -0
- package/dist/cjs/react/components/PrivacyPreferenceCenter.d.ts.map +1 -0
- package/dist/cjs/react/components/PrivacyPreferenceCenter.js +120 -0
- package/dist/cjs/react/components/PrivacyPreferenceCenter.js.map +1 -0
- package/dist/cjs/react/hooks/useConsent.d.ts +13 -0
- package/dist/cjs/react/hooks/useConsent.d.ts.map +1 -0
- package/dist/cjs/react/hooks/useConsent.js +84 -0
- package/dist/cjs/react/hooks/useConsent.js.map +1 -0
- package/dist/cjs/react/hooks/useDataDeletion.d.ts +17 -0
- package/dist/cjs/react/hooks/useDataDeletion.d.ts.map +1 -0
- package/dist/cjs/react/hooks/useDataDeletion.js +117 -0
- package/dist/cjs/react/hooks/useDataDeletion.js.map +1 -0
- package/dist/cjs/react/hooks/usePrivacyPreferences.d.ts +15 -0
- package/dist/cjs/react/hooks/usePrivacyPreferences.d.ts.map +1 -0
- package/dist/cjs/react/hooks/usePrivacyPreferences.js +82 -0
- package/dist/cjs/react/hooks/usePrivacyPreferences.js.map +1 -0
- package/dist/cjs/react/index.d.ts +11 -0
- package/dist/cjs/react/index.d.ts.map +1 -1
- package/dist/cjs/react/index.js +15 -1
- package/dist/cjs/react/index.js.map +1 -1
- package/dist/consent.d.ts +68 -0
- package/dist/consent.d.ts.map +1 -0
- package/dist/cookies.d.ts +28 -0
- package/dist/cookies.d.ts.map +1 -0
- package/dist/esm/activity.d.ts +59 -0
- package/dist/esm/activity.d.ts.map +1 -0
- package/dist/esm/activity.js +127 -0
- package/dist/esm/activity.js.map +1 -0
- package/dist/esm/consent.d.ts +68 -0
- package/dist/esm/consent.d.ts.map +1 -0
- package/dist/esm/consent.js +187 -0
- package/dist/esm/consent.js.map +1 -0
- package/dist/esm/cookies.d.ts +28 -0
- package/dist/esm/cookies.d.ts.map +1 -0
- package/dist/esm/cookies.js +89 -0
- package/dist/esm/cookies.js.map +1 -0
- package/dist/esm/heartbeat.d.ts +42 -0
- package/dist/esm/heartbeat.d.ts.map +1 -0
- package/dist/esm/heartbeat.js +88 -0
- package/dist/esm/heartbeat.js.map +1 -0
- package/dist/esm/index.d.ts +100 -3
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/page-tracking.d.ts +60 -0
- package/dist/esm/page-tracking.d.ts.map +1 -0
- package/dist/esm/page-tracking.js +176 -0
- package/dist/esm/page-tracking.js.map +1 -0
- package/dist/esm/react/components/ConsentBanner.d.ts +16 -0
- package/dist/esm/react/components/ConsentBanner.d.ts.map +1 -0
- package/dist/esm/react/components/ConsentBanner.js +76 -0
- package/dist/esm/react/components/ConsentBanner.js.map +1 -0
- package/dist/esm/react/components/CookieNotice.d.ts +12 -0
- package/dist/esm/react/components/CookieNotice.d.ts.map +1 -0
- package/dist/esm/react/components/CookieNotice.js +26 -0
- package/dist/esm/react/components/CookieNotice.js.map +1 -0
- package/dist/esm/react/components/PrivacyPreferenceCenter.d.ts +12 -0
- package/dist/esm/react/components/PrivacyPreferenceCenter.d.ts.map +1 -0
- package/dist/esm/react/components/PrivacyPreferenceCenter.js +84 -0
- package/dist/esm/react/components/PrivacyPreferenceCenter.js.map +1 -0
- package/dist/esm/react/hooks/useConsent.d.ts +13 -0
- package/dist/esm/react/hooks/useConsent.d.ts.map +1 -0
- package/dist/esm/react/hooks/useConsent.js +48 -0
- package/dist/esm/react/hooks/useConsent.js.map +1 -0
- package/dist/esm/react/hooks/useDataDeletion.d.ts +17 -0
- package/dist/esm/react/hooks/useDataDeletion.d.ts.map +1 -0
- package/dist/esm/react/hooks/useDataDeletion.js +81 -0
- package/dist/esm/react/hooks/useDataDeletion.js.map +1 -0
- package/dist/esm/react/hooks/usePrivacyPreferences.d.ts +15 -0
- package/dist/esm/react/hooks/usePrivacyPreferences.d.ts.map +1 -0
- package/dist/esm/react/hooks/usePrivacyPreferences.js +46 -0
- package/dist/esm/react/hooks/usePrivacyPreferences.js.map +1 -0
- package/dist/esm/react/index.d.ts +11 -0
- package/dist/esm/react/index.d.ts.map +1 -1
- package/dist/esm/react/index.js +8 -0
- package/dist/esm/react/index.js.map +1 -1
- package/dist/heartbeat.d.ts +42 -0
- package/dist/heartbeat.d.ts.map +1 -0
- package/dist/index.d.ts +100 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.global.dev.js +903 -12
- 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 +321 -11
- package/dist/index.mjs +321 -11
- package/dist/page-tracking.d.ts +60 -0
- package/dist/page-tracking.d.ts.map +1 -0
- package/dist/react/activity.d.ts +59 -0
- package/dist/react/activity.d.ts.map +1 -0
- package/dist/react/activity.js +130 -0
- package/dist/react/activity.mjs +126 -0
- package/dist/react/consent.d.ts +68 -0
- package/dist/react/consent.d.ts.map +1 -0
- package/dist/react/consent.js +190 -0
- package/dist/react/consent.mjs +186 -0
- package/dist/react/cookies.d.ts +28 -0
- package/dist/react/cookies.d.ts.map +1 -0
- package/dist/react/cookies.js +94 -0
- package/dist/react/cookies.mjs +88 -0
- package/dist/react/heartbeat.d.ts +42 -0
- package/dist/react/heartbeat.d.ts.map +1 -0
- package/dist/react/heartbeat.js +91 -0
- package/dist/react/heartbeat.mjs +87 -0
- package/dist/react/index.d.ts +100 -3
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +321 -11
- package/dist/react/index.mjs +321 -11
- package/dist/react/page-tracking.d.ts +60 -0
- package/dist/react/page-tracking.d.ts.map +1 -0
- package/dist/react/page-tracking.js +179 -0
- package/dist/react/page-tracking.mjs +175 -0
- package/dist/react/react/components/ConsentBanner.d.ts +16 -0
- package/dist/react/react/components/ConsentBanner.d.ts.map +1 -0
- package/dist/react/react/components/ConsentBanner.js +78 -0
- package/dist/react/react/components/ConsentBanner.mjs +75 -0
- package/dist/react/react/components/CookieNotice.d.ts +12 -0
- package/dist/react/react/components/CookieNotice.d.ts.map +1 -0
- package/dist/react/react/components/CookieNotice.js +28 -0
- package/dist/react/react/components/CookieNotice.mjs +25 -0
- package/dist/react/react/components/PrivacyPreferenceCenter.d.ts +12 -0
- package/dist/react/react/components/PrivacyPreferenceCenter.d.ts.map +1 -0
- package/dist/react/react/components/PrivacyPreferenceCenter.js +86 -0
- package/dist/react/react/components/PrivacyPreferenceCenter.mjs +83 -0
- package/dist/react/react/hooks/useConsent.d.ts +13 -0
- package/dist/react/react/hooks/useConsent.d.ts.map +1 -0
- package/dist/react/react/hooks/useConsent.js +50 -0
- package/dist/react/react/hooks/useConsent.mjs +47 -0
- package/dist/react/react/hooks/useDataDeletion.d.ts +17 -0
- package/dist/react/react/hooks/useDataDeletion.d.ts.map +1 -0
- package/dist/react/react/hooks/useDataDeletion.js +83 -0
- package/dist/react/react/hooks/useDataDeletion.mjs +80 -0
- package/dist/react/react/hooks/usePrivacyPreferences.d.ts +15 -0
- package/dist/react/react/hooks/usePrivacyPreferences.d.ts.map +1 -0
- package/dist/react/react/hooks/usePrivacyPreferences.js +48 -0
- package/dist/react/react/hooks/usePrivacyPreferences.mjs +45 -0
- package/dist/react/react/index.d.ts +11 -0
- package/dist/react/react/index.d.ts.map +1 -1
- package/dist/react/react/index.js +15 -1
- package/dist/react/react/index.mjs +8 -0
- package/package.json +1 -1
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activity Detection for Grain Analytics
|
|
3
|
+
* Tracks user activity (mouse, keyboard, touch, scroll) to determine if user is active
|
|
4
|
+
*/
|
|
5
|
+
export class ActivityDetector {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.activityThreshold = 30000; // 30 seconds
|
|
8
|
+
this.listeners = [];
|
|
9
|
+
this.isDestroyed = false;
|
|
10
|
+
// Events that indicate user activity
|
|
11
|
+
this.activityEvents = [
|
|
12
|
+
'mousemove',
|
|
13
|
+
'mousedown',
|
|
14
|
+
'keydown',
|
|
15
|
+
'scroll',
|
|
16
|
+
'touchstart',
|
|
17
|
+
'click',
|
|
18
|
+
];
|
|
19
|
+
this.lastActivityTime = Date.now();
|
|
20
|
+
this.boundActivityHandler = this.debounce(this.handleActivity.bind(this), 500);
|
|
21
|
+
this.setupListeners();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Setup event listeners for activity detection
|
|
25
|
+
*/
|
|
26
|
+
setupListeners() {
|
|
27
|
+
if (typeof window === 'undefined')
|
|
28
|
+
return;
|
|
29
|
+
for (const event of this.activityEvents) {
|
|
30
|
+
window.addEventListener(event, this.boundActivityHandler, { passive: true });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Handle activity event
|
|
35
|
+
*/
|
|
36
|
+
handleActivity() {
|
|
37
|
+
if (this.isDestroyed)
|
|
38
|
+
return;
|
|
39
|
+
this.lastActivityTime = Date.now();
|
|
40
|
+
this.notifyListeners();
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Debounce function to limit how often activity handler is called
|
|
44
|
+
*/
|
|
45
|
+
debounce(func, wait) {
|
|
46
|
+
let timeout = null;
|
|
47
|
+
return () => {
|
|
48
|
+
if (timeout !== null) {
|
|
49
|
+
clearTimeout(timeout);
|
|
50
|
+
}
|
|
51
|
+
timeout = window.setTimeout(() => {
|
|
52
|
+
func();
|
|
53
|
+
timeout = null;
|
|
54
|
+
}, wait);
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Check if user is currently active
|
|
59
|
+
* @param threshold Time in ms to consider user inactive (default: 30s)
|
|
60
|
+
*/
|
|
61
|
+
isActive(threshold) {
|
|
62
|
+
const thresholdToUse = threshold ?? this.activityThreshold;
|
|
63
|
+
const now = Date.now();
|
|
64
|
+
return (now - this.lastActivityTime) < thresholdToUse;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get time since last activity in milliseconds
|
|
68
|
+
*/
|
|
69
|
+
getTimeSinceLastActivity() {
|
|
70
|
+
return Date.now() - this.lastActivityTime;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get last activity timestamp
|
|
74
|
+
*/
|
|
75
|
+
getLastActivityTime() {
|
|
76
|
+
return this.lastActivityTime;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Set activity threshold
|
|
80
|
+
*/
|
|
81
|
+
setActivityThreshold(threshold) {
|
|
82
|
+
this.activityThreshold = threshold;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Add listener for activity changes
|
|
86
|
+
*/
|
|
87
|
+
addListener(listener) {
|
|
88
|
+
this.listeners.push(listener);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Remove listener
|
|
92
|
+
*/
|
|
93
|
+
removeListener(listener) {
|
|
94
|
+
const index = this.listeners.indexOf(listener);
|
|
95
|
+
if (index > -1) {
|
|
96
|
+
this.listeners.splice(index, 1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Notify all listeners
|
|
101
|
+
*/
|
|
102
|
+
notifyListeners() {
|
|
103
|
+
for (const listener of this.listeners) {
|
|
104
|
+
try {
|
|
105
|
+
listener();
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
console.error('[Activity Detector] Listener error:', error);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Cleanup and remove listeners
|
|
114
|
+
*/
|
|
115
|
+
destroy() {
|
|
116
|
+
if (this.isDestroyed)
|
|
117
|
+
return;
|
|
118
|
+
if (typeof window !== 'undefined') {
|
|
119
|
+
for (const event of this.activityEvents) {
|
|
120
|
+
window.removeEventListener(event, this.boundActivityHandler);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
this.listeners = [];
|
|
124
|
+
this.isDestroyed = true;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=activity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity.js","sourceRoot":"","sources":["../../src/activity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consent management for Grain Analytics
|
|
3
|
+
* Handles GDPR-compliant consent tracking and state management
|
|
4
|
+
*/
|
|
5
|
+
export type ConsentMode = 'opt-in' | 'opt-out' | 'disabled';
|
|
6
|
+
export interface ConsentState {
|
|
7
|
+
granted: boolean;
|
|
8
|
+
categories: string[];
|
|
9
|
+
timestamp: Date;
|
|
10
|
+
version: string;
|
|
11
|
+
}
|
|
12
|
+
export declare const DEFAULT_CONSENT_CATEGORIES: string[];
|
|
13
|
+
export declare const CONSENT_VERSION = "1.0.0";
|
|
14
|
+
/**
|
|
15
|
+
* Consent manager for handling user consent state
|
|
16
|
+
*/
|
|
17
|
+
export declare class ConsentManager {
|
|
18
|
+
private consentState;
|
|
19
|
+
private consentMode;
|
|
20
|
+
private storageKey;
|
|
21
|
+
private listeners;
|
|
22
|
+
constructor(tenantId: string, consentMode?: ConsentMode);
|
|
23
|
+
/**
|
|
24
|
+
* Load consent state from localStorage
|
|
25
|
+
*/
|
|
26
|
+
private loadConsentState;
|
|
27
|
+
/**
|
|
28
|
+
* Save consent state to localStorage
|
|
29
|
+
*/
|
|
30
|
+
private saveConsentState;
|
|
31
|
+
/**
|
|
32
|
+
* Grant consent with optional categories
|
|
33
|
+
*/
|
|
34
|
+
grantConsent(categories?: string[]): void;
|
|
35
|
+
/**
|
|
36
|
+
* Revoke consent (opt-out)
|
|
37
|
+
*/
|
|
38
|
+
revokeConsent(categories?: string[]): void;
|
|
39
|
+
/**
|
|
40
|
+
* Get current consent state
|
|
41
|
+
*/
|
|
42
|
+
getConsentState(): ConsentState | null;
|
|
43
|
+
/**
|
|
44
|
+
* Check if user has granted consent
|
|
45
|
+
*/
|
|
46
|
+
hasConsent(category?: string): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Check if we should wait for consent before tracking
|
|
49
|
+
*/
|
|
50
|
+
shouldWaitForConsent(): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Add consent change listener
|
|
53
|
+
*/
|
|
54
|
+
addListener(listener: (state: ConsentState) => void): void;
|
|
55
|
+
/**
|
|
56
|
+
* Remove consent change listener
|
|
57
|
+
*/
|
|
58
|
+
removeListener(listener: (state: ConsentState) => void): void;
|
|
59
|
+
/**
|
|
60
|
+
* Notify all listeners of consent state change
|
|
61
|
+
*/
|
|
62
|
+
private notifyListeners;
|
|
63
|
+
/**
|
|
64
|
+
* Clear all consent data
|
|
65
|
+
*/
|
|
66
|
+
clearConsent(): void;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=consent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consent.d.ts","sourceRoot":"","sources":["../../src/consent.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;AAE5D,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,0BAA0B,UAA2C,CAAC;AACnF,eAAO,MAAM,eAAe,UAAU,CAAC;AAEvC;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,SAAS,CAA4C;gBAEjD,QAAQ,EAAE,MAAM,EAAE,WAAW,GAAE,WAAuB;IAMlE;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA0BxB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAUxB;;OAEG;IACH,YAAY,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI;IAczC;;OAEG;IACH,aAAa,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI;IA8B1C;;OAEG;IACH,eAAe,IAAI,YAAY,GAAG,IAAI;IAItC;;OAEG;IACH,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO;IAwBtC;;OAEG;IACH,oBAAoB,IAAI,OAAO;IAI/B;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,IAAI;IAI1D;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,IAAI;IAO7D;;OAEG;IACH,OAAO,CAAC,eAAe;IAYvB;;OAEG;IACH,YAAY,IAAI,IAAI;CAUrB"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consent management for Grain Analytics
|
|
3
|
+
* Handles GDPR-compliant consent tracking and state management
|
|
4
|
+
*/
|
|
5
|
+
export const DEFAULT_CONSENT_CATEGORIES = ['necessary', 'analytics', 'functional'];
|
|
6
|
+
export const CONSENT_VERSION = '1.0.0';
|
|
7
|
+
/**
|
|
8
|
+
* Consent manager for handling user consent state
|
|
9
|
+
*/
|
|
10
|
+
export class ConsentManager {
|
|
11
|
+
constructor(tenantId, consentMode = 'opt-out') {
|
|
12
|
+
this.consentState = null;
|
|
13
|
+
this.listeners = [];
|
|
14
|
+
this.consentMode = consentMode;
|
|
15
|
+
this.storageKey = `grain_consent_${tenantId}`;
|
|
16
|
+
this.loadConsentState();
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Load consent state from localStorage
|
|
20
|
+
*/
|
|
21
|
+
loadConsentState() {
|
|
22
|
+
if (typeof window === 'undefined')
|
|
23
|
+
return;
|
|
24
|
+
try {
|
|
25
|
+
const stored = localStorage.getItem(this.storageKey);
|
|
26
|
+
if (stored) {
|
|
27
|
+
const parsed = JSON.parse(stored);
|
|
28
|
+
this.consentState = {
|
|
29
|
+
...parsed,
|
|
30
|
+
timestamp: new Date(parsed.timestamp),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
else if (this.consentMode === 'opt-out' || this.consentMode === 'disabled') {
|
|
34
|
+
// Auto-grant consent for opt-out and disabled modes
|
|
35
|
+
this.consentState = {
|
|
36
|
+
granted: true,
|
|
37
|
+
categories: DEFAULT_CONSENT_CATEGORIES,
|
|
38
|
+
timestamp: new Date(),
|
|
39
|
+
version: CONSENT_VERSION,
|
|
40
|
+
};
|
|
41
|
+
this.saveConsentState();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.error('[Grain Consent] Failed to load consent state:', error);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Save consent state to localStorage
|
|
50
|
+
*/
|
|
51
|
+
saveConsentState() {
|
|
52
|
+
if (typeof window === 'undefined' || !this.consentState)
|
|
53
|
+
return;
|
|
54
|
+
try {
|
|
55
|
+
localStorage.setItem(this.storageKey, JSON.stringify(this.consentState));
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.error('[Grain Consent] Failed to save consent state:', error);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Grant consent with optional categories
|
|
63
|
+
*/
|
|
64
|
+
grantConsent(categories) {
|
|
65
|
+
const grantedCategories = categories || DEFAULT_CONSENT_CATEGORIES;
|
|
66
|
+
this.consentState = {
|
|
67
|
+
granted: true,
|
|
68
|
+
categories: grantedCategories,
|
|
69
|
+
timestamp: new Date(),
|
|
70
|
+
version: CONSENT_VERSION,
|
|
71
|
+
};
|
|
72
|
+
this.saveConsentState();
|
|
73
|
+
this.notifyListeners();
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Revoke consent (opt-out)
|
|
77
|
+
*/
|
|
78
|
+
revokeConsent(categories) {
|
|
79
|
+
if (!this.consentState) {
|
|
80
|
+
this.consentState = {
|
|
81
|
+
granted: false,
|
|
82
|
+
categories: [],
|
|
83
|
+
timestamp: new Date(),
|
|
84
|
+
version: CONSENT_VERSION,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
else if (categories) {
|
|
88
|
+
// Remove specific categories
|
|
89
|
+
this.consentState = {
|
|
90
|
+
...this.consentState,
|
|
91
|
+
categories: this.consentState.categories.filter(cat => !categories.includes(cat)),
|
|
92
|
+
granted: this.consentState.categories.length > 0,
|
|
93
|
+
timestamp: new Date(),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
// Revoke all consent
|
|
98
|
+
this.consentState = {
|
|
99
|
+
granted: false,
|
|
100
|
+
categories: [],
|
|
101
|
+
timestamp: new Date(),
|
|
102
|
+
version: CONSENT_VERSION,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
this.saveConsentState();
|
|
106
|
+
this.notifyListeners();
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get current consent state
|
|
110
|
+
*/
|
|
111
|
+
getConsentState() {
|
|
112
|
+
return this.consentState ? { ...this.consentState } : null;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Check if user has granted consent
|
|
116
|
+
*/
|
|
117
|
+
hasConsent(category) {
|
|
118
|
+
// Disabled mode always returns true (no consent required)
|
|
119
|
+
if (this.consentMode === 'disabled') {
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
// No consent state in opt-in mode means no consent
|
|
123
|
+
if (this.consentMode === 'opt-in' && !this.consentState) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
// Check consent state
|
|
127
|
+
if (!this.consentState?.granted) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
// Check specific category if provided
|
|
131
|
+
if (category) {
|
|
132
|
+
return this.consentState.categories.includes(category);
|
|
133
|
+
}
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Check if we should wait for consent before tracking
|
|
138
|
+
*/
|
|
139
|
+
shouldWaitForConsent() {
|
|
140
|
+
return this.consentMode === 'opt-in' && !this.consentState?.granted;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Add consent change listener
|
|
144
|
+
*/
|
|
145
|
+
addListener(listener) {
|
|
146
|
+
this.listeners.push(listener);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Remove consent change listener
|
|
150
|
+
*/
|
|
151
|
+
removeListener(listener) {
|
|
152
|
+
const index = this.listeners.indexOf(listener);
|
|
153
|
+
if (index > -1) {
|
|
154
|
+
this.listeners.splice(index, 1);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Notify all listeners of consent state change
|
|
159
|
+
*/
|
|
160
|
+
notifyListeners() {
|
|
161
|
+
if (!this.consentState)
|
|
162
|
+
return;
|
|
163
|
+
this.listeners.forEach(listener => {
|
|
164
|
+
try {
|
|
165
|
+
listener(this.consentState);
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
console.error('[Grain Consent] Listener error:', error);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Clear all consent data
|
|
174
|
+
*/
|
|
175
|
+
clearConsent() {
|
|
176
|
+
if (typeof window === 'undefined')
|
|
177
|
+
return;
|
|
178
|
+
try {
|
|
179
|
+
localStorage.removeItem(this.storageKey);
|
|
180
|
+
this.consentState = null;
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
console.error('[Grain Consent] Failed to clear consent:', error);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=consent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consent.js","sourceRoot":"","sources":["../../src/consent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cookie utilities for Grain Analytics
|
|
3
|
+
* Provides GDPR-compliant cookie management with configurable options
|
|
4
|
+
*/
|
|
5
|
+
export interface CookieConfig {
|
|
6
|
+
domain?: string;
|
|
7
|
+
path?: string;
|
|
8
|
+
sameSite?: 'strict' | 'lax' | 'none';
|
|
9
|
+
secure?: boolean;
|
|
10
|
+
maxAge?: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Set a cookie with configurable options
|
|
14
|
+
*/
|
|
15
|
+
export declare function setCookie(name: string, value: string, config?: CookieConfig): void;
|
|
16
|
+
/**
|
|
17
|
+
* Get a cookie value by name
|
|
18
|
+
*/
|
|
19
|
+
export declare function getCookie(name: string): string | null;
|
|
20
|
+
/**
|
|
21
|
+
* Delete a cookie by name
|
|
22
|
+
*/
|
|
23
|
+
export declare function deleteCookie(name: string, config?: Pick<CookieConfig, 'domain' | 'path'>): void;
|
|
24
|
+
/**
|
|
25
|
+
* Check if cookies are available and working
|
|
26
|
+
*/
|
|
27
|
+
export declare function areCookiesEnabled(): boolean;
|
|
28
|
+
//# sourceMappingURL=cookies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../src/cookies.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;IACrC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,YAAY,GAAG,IAAI,CA4BlF;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAiBrD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,GAAG,MAAM,CAAC,GAAG,IAAI,CAmB/F;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAY3C"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cookie utilities for Grain Analytics
|
|
3
|
+
* Provides GDPR-compliant cookie management with configurable options
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Set a cookie with configurable options
|
|
7
|
+
*/
|
|
8
|
+
export function setCookie(name, value, config) {
|
|
9
|
+
if (typeof document === 'undefined')
|
|
10
|
+
return;
|
|
11
|
+
const parts = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`];
|
|
12
|
+
if (config?.maxAge !== undefined) {
|
|
13
|
+
parts.push(`max-age=${config.maxAge}`);
|
|
14
|
+
}
|
|
15
|
+
if (config?.domain) {
|
|
16
|
+
parts.push(`domain=${config.domain}`);
|
|
17
|
+
}
|
|
18
|
+
if (config?.path) {
|
|
19
|
+
parts.push(`path=${config.path}`);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
parts.push('path=/');
|
|
23
|
+
}
|
|
24
|
+
if (config?.sameSite) {
|
|
25
|
+
parts.push(`samesite=${config.sameSite}`);
|
|
26
|
+
}
|
|
27
|
+
if (config?.secure) {
|
|
28
|
+
parts.push('secure');
|
|
29
|
+
}
|
|
30
|
+
document.cookie = parts.join('; ');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Get a cookie value by name
|
|
34
|
+
*/
|
|
35
|
+
export function getCookie(name) {
|
|
36
|
+
if (typeof document === 'undefined')
|
|
37
|
+
return null;
|
|
38
|
+
const nameEQ = encodeURIComponent(name) + '=';
|
|
39
|
+
const cookies = document.cookie.split(';');
|
|
40
|
+
for (let i = 0; i < cookies.length; i++) {
|
|
41
|
+
let cookie = cookies[i];
|
|
42
|
+
while (cookie.charAt(0) === ' ') {
|
|
43
|
+
cookie = cookie.substring(1);
|
|
44
|
+
}
|
|
45
|
+
if (cookie.indexOf(nameEQ) === 0) {
|
|
46
|
+
return decodeURIComponent(cookie.substring(nameEQ.length));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Delete a cookie by name
|
|
53
|
+
*/
|
|
54
|
+
export function deleteCookie(name, config) {
|
|
55
|
+
if (typeof document === 'undefined')
|
|
56
|
+
return;
|
|
57
|
+
const parts = [
|
|
58
|
+
`${encodeURIComponent(name)}=`,
|
|
59
|
+
'max-age=0',
|
|
60
|
+
];
|
|
61
|
+
if (config?.domain) {
|
|
62
|
+
parts.push(`domain=${config.domain}`);
|
|
63
|
+
}
|
|
64
|
+
if (config?.path) {
|
|
65
|
+
parts.push(`path=${config.path}`);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
parts.push('path=/');
|
|
69
|
+
}
|
|
70
|
+
document.cookie = parts.join('; ');
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Check if cookies are available and working
|
|
74
|
+
*/
|
|
75
|
+
export function areCookiesEnabled() {
|
|
76
|
+
if (typeof document === 'undefined')
|
|
77
|
+
return false;
|
|
78
|
+
try {
|
|
79
|
+
const testCookie = '_grain_cookie_test';
|
|
80
|
+
setCookie(testCookie, 'test', { maxAge: 1 });
|
|
81
|
+
const result = getCookie(testCookie) === 'test';
|
|
82
|
+
deleteCookie(testCookie);
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=cookies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookies.js","sourceRoot":"","sources":["../../src/cookies.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heartbeat Manager for Grain Analytics
|
|
3
|
+
* Tracks session activity with consent-aware behavior
|
|
4
|
+
*/
|
|
5
|
+
import type { ActivityDetector } from './activity';
|
|
6
|
+
export interface HeartbeatConfig {
|
|
7
|
+
activeInterval: number;
|
|
8
|
+
inactiveInterval: number;
|
|
9
|
+
debug?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface HeartbeatTracker {
|
|
12
|
+
trackSystemEvent(eventName: string, properties: Record<string, unknown>): void;
|
|
13
|
+
hasConsent(category?: string): boolean;
|
|
14
|
+
getEffectiveUserId(): string;
|
|
15
|
+
getEphemeralSessionId(): string;
|
|
16
|
+
getCurrentPage(): string | null;
|
|
17
|
+
getEventCountSinceLastHeartbeat(): number;
|
|
18
|
+
resetEventCountSinceLastHeartbeat(): void;
|
|
19
|
+
}
|
|
20
|
+
export declare class HeartbeatManager {
|
|
21
|
+
private config;
|
|
22
|
+
private tracker;
|
|
23
|
+
private activityDetector;
|
|
24
|
+
private heartbeatTimer;
|
|
25
|
+
private isDestroyed;
|
|
26
|
+
private lastHeartbeatTime;
|
|
27
|
+
private currentInterval;
|
|
28
|
+
constructor(tracker: HeartbeatTracker, activityDetector: ActivityDetector, config: HeartbeatConfig);
|
|
29
|
+
/**
|
|
30
|
+
* Schedule the next heartbeat based on current activity
|
|
31
|
+
*/
|
|
32
|
+
private scheduleNextHeartbeat;
|
|
33
|
+
/**
|
|
34
|
+
* Send heartbeat event
|
|
35
|
+
*/
|
|
36
|
+
private sendHeartbeat;
|
|
37
|
+
/**
|
|
38
|
+
* Destroy the heartbeat manager
|
|
39
|
+
*/
|
|
40
|
+
destroy(): void;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=heartbeat.d.ts.map
|
|
@@ -0,0 +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;gBAG9B,OAAO,EAAE,gBAAgB,EACzB,gBAAgB,EAAE,gBAAgB,EAClC,MAAM,EAAE,eAAe;IAYzB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAyB7B;;OAEG;IACH,OAAO,CAAC,aAAa;IAqCrB;;OAEG;IACH,OAAO,IAAI,IAAI;CAchB"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heartbeat Manager for Grain Analytics
|
|
3
|
+
* Tracks session activity with consent-aware behavior
|
|
4
|
+
*/
|
|
5
|
+
export class HeartbeatManager {
|
|
6
|
+
constructor(tracker, activityDetector, config) {
|
|
7
|
+
this.heartbeatTimer = null;
|
|
8
|
+
this.isDestroyed = false;
|
|
9
|
+
this.tracker = tracker;
|
|
10
|
+
this.activityDetector = activityDetector;
|
|
11
|
+
this.config = config;
|
|
12
|
+
this.lastHeartbeatTime = Date.now();
|
|
13
|
+
this.currentInterval = config.activeInterval;
|
|
14
|
+
// Start heartbeat tracking
|
|
15
|
+
this.scheduleNextHeartbeat();
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Schedule the next heartbeat based on current activity
|
|
19
|
+
*/
|
|
20
|
+
scheduleNextHeartbeat() {
|
|
21
|
+
if (this.isDestroyed)
|
|
22
|
+
return;
|
|
23
|
+
// Clear existing timer
|
|
24
|
+
if (this.heartbeatTimer !== null) {
|
|
25
|
+
clearTimeout(this.heartbeatTimer);
|
|
26
|
+
}
|
|
27
|
+
// Determine interval based on activity
|
|
28
|
+
const isActive = this.activityDetector.isActive(60000); // 1 minute threshold
|
|
29
|
+
this.currentInterval = isActive ? this.config.activeInterval : this.config.inactiveInterval;
|
|
30
|
+
// Schedule next heartbeat
|
|
31
|
+
this.heartbeatTimer = window.setTimeout(() => {
|
|
32
|
+
this.sendHeartbeat();
|
|
33
|
+
this.scheduleNextHeartbeat();
|
|
34
|
+
}, this.currentInterval);
|
|
35
|
+
if (this.config.debug) {
|
|
36
|
+
console.log(`[Heartbeat] Scheduled next heartbeat in ${this.currentInterval / 1000}s (${isActive ? 'active' : 'inactive'})`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Send heartbeat event
|
|
41
|
+
*/
|
|
42
|
+
sendHeartbeat() {
|
|
43
|
+
if (this.isDestroyed)
|
|
44
|
+
return;
|
|
45
|
+
const now = Date.now();
|
|
46
|
+
const isActive = this.activityDetector.isActive(60000); // 1 minute threshold
|
|
47
|
+
const hasConsent = this.tracker.hasConsent('analytics');
|
|
48
|
+
// Base properties (always included)
|
|
49
|
+
const properties = {
|
|
50
|
+
type: 'heartbeat',
|
|
51
|
+
status: isActive ? 'active' : 'inactive',
|
|
52
|
+
timestamp: now,
|
|
53
|
+
};
|
|
54
|
+
// Enhanced properties when consent is granted
|
|
55
|
+
if (hasConsent) {
|
|
56
|
+
const page = this.tracker.getCurrentPage();
|
|
57
|
+
if (page) {
|
|
58
|
+
properties.page = page;
|
|
59
|
+
}
|
|
60
|
+
properties.duration = now - this.lastHeartbeatTime;
|
|
61
|
+
properties.event_count = this.tracker.getEventCountSinceLastHeartbeat();
|
|
62
|
+
// Reset event count
|
|
63
|
+
this.tracker.resetEventCountSinceLastHeartbeat();
|
|
64
|
+
}
|
|
65
|
+
// Track the heartbeat event
|
|
66
|
+
this.tracker.trackSystemEvent('_grain_heartbeat', properties);
|
|
67
|
+
this.lastHeartbeatTime = now;
|
|
68
|
+
if (this.config.debug) {
|
|
69
|
+
console.log('[Heartbeat] Sent heartbeat:', properties);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Destroy the heartbeat manager
|
|
74
|
+
*/
|
|
75
|
+
destroy() {
|
|
76
|
+
if (this.isDestroyed)
|
|
77
|
+
return;
|
|
78
|
+
if (this.heartbeatTimer !== null) {
|
|
79
|
+
clearTimeout(this.heartbeatTimer);
|
|
80
|
+
this.heartbeatTimer = null;
|
|
81
|
+
}
|
|
82
|
+
this.isDestroyed = true;
|
|
83
|
+
if (this.config.debug) {
|
|
84
|
+
console.log('[Heartbeat] Destroyed');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=heartbeat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../../src/heartbeat.ts"],"names":[],"mappings":"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"}
|