@grainql/analytics-web 2.2.0 → 2.2.1

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 (83) hide show
  1. package/dist/index.global.dev.js +1 -1
  2. package/dist/index.global.js +1 -1
  3. package/dist/react/index.d.ts +44 -519
  4. package/dist/react/index.d.ts.map +1 -1
  5. package/dist/react/index.js +54 -1517
  6. package/dist/react/index.mjs +42 -1514
  7. package/package.json +1 -1
  8. package/dist/react/activity.d.ts +0 -59
  9. package/dist/react/activity.d.ts.map +0 -1
  10. package/dist/react/activity.js +0 -130
  11. package/dist/react/activity.mjs +0 -126
  12. package/dist/react/consent.d.ts +0 -72
  13. package/dist/react/consent.d.ts.map +0 -1
  14. package/dist/react/consent.js +0 -195
  15. package/dist/react/consent.mjs +0 -191
  16. package/dist/react/cookies.d.ts +0 -28
  17. package/dist/react/cookies.d.ts.map +0 -1
  18. package/dist/react/cookies.js +0 -94
  19. package/dist/react/cookies.mjs +0 -88
  20. package/dist/react/heartbeat.d.ts +0 -47
  21. package/dist/react/heartbeat.d.ts.map +0 -1
  22. package/dist/react/heartbeat.js +0 -119
  23. package/dist/react/heartbeat.mjs +0 -115
  24. package/dist/react/page-tracking.d.ts +0 -60
  25. package/dist/react/page-tracking.d.ts.map +0 -1
  26. package/dist/react/page-tracking.js +0 -179
  27. package/dist/react/page-tracking.mjs +0 -175
  28. package/dist/react/react/index.d.ts +0 -47
  29. package/dist/react/react/index.d.ts.map +0 -1
  30. package/dist/react/react/index.js +0 -58
  31. package/dist/react/react/index.mjs +0 -44
  32. /package/dist/react/{react/GrainProvider.d.ts → GrainProvider.d.ts} +0 -0
  33. /package/dist/react/{react/GrainProvider.d.ts.map → GrainProvider.d.ts.map} +0 -0
  34. /package/dist/react/{react/GrainProvider.js → GrainProvider.js} +0 -0
  35. /package/dist/react/{react/GrainProvider.mjs → GrainProvider.mjs} +0 -0
  36. /package/dist/react/{react/components → components}/ConsentBanner.d.ts +0 -0
  37. /package/dist/react/{react/components → components}/ConsentBanner.d.ts.map +0 -0
  38. /package/dist/react/{react/components → components}/ConsentBanner.js +0 -0
  39. /package/dist/react/{react/components → components}/ConsentBanner.mjs +0 -0
  40. /package/dist/react/{react/components → components}/CookieNotice.d.ts +0 -0
  41. /package/dist/react/{react/components → components}/CookieNotice.d.ts.map +0 -0
  42. /package/dist/react/{react/components → components}/CookieNotice.js +0 -0
  43. /package/dist/react/{react/components → components}/CookieNotice.mjs +0 -0
  44. /package/dist/react/{react/components → components}/PrivacyPreferenceCenter.d.ts +0 -0
  45. /package/dist/react/{react/components → components}/PrivacyPreferenceCenter.d.ts.map +0 -0
  46. /package/dist/react/{react/components → components}/PrivacyPreferenceCenter.js +0 -0
  47. /package/dist/react/{react/components → components}/PrivacyPreferenceCenter.mjs +0 -0
  48. /package/dist/react/{react/context.d.ts → context.d.ts} +0 -0
  49. /package/dist/react/{react/context.d.ts.map → context.d.ts.map} +0 -0
  50. /package/dist/react/{react/context.js → context.js} +0 -0
  51. /package/dist/react/{react/context.mjs → context.mjs} +0 -0
  52. /package/dist/react/{react/hooks → hooks}/useAllConfigs.d.ts +0 -0
  53. /package/dist/react/{react/hooks → hooks}/useAllConfigs.d.ts.map +0 -0
  54. /package/dist/react/{react/hooks → hooks}/useAllConfigs.js +0 -0
  55. /package/dist/react/{react/hooks → hooks}/useAllConfigs.mjs +0 -0
  56. /package/dist/react/{react/hooks → hooks}/useConfig.d.ts +0 -0
  57. /package/dist/react/{react/hooks → hooks}/useConfig.d.ts.map +0 -0
  58. /package/dist/react/{react/hooks → hooks}/useConfig.js +0 -0
  59. /package/dist/react/{react/hooks → hooks}/useConfig.mjs +0 -0
  60. /package/dist/react/{react/hooks → hooks}/useConsent.d.ts +0 -0
  61. /package/dist/react/{react/hooks → hooks}/useConsent.d.ts.map +0 -0
  62. /package/dist/react/{react/hooks → hooks}/useConsent.js +0 -0
  63. /package/dist/react/{react/hooks → hooks}/useConsent.mjs +0 -0
  64. /package/dist/react/{react/hooks → hooks}/useDataDeletion.d.ts +0 -0
  65. /package/dist/react/{react/hooks → hooks}/useDataDeletion.d.ts.map +0 -0
  66. /package/dist/react/{react/hooks → hooks}/useDataDeletion.js +0 -0
  67. /package/dist/react/{react/hooks → hooks}/useDataDeletion.mjs +0 -0
  68. /package/dist/react/{react/hooks → hooks}/useGrainAnalytics.d.ts +0 -0
  69. /package/dist/react/{react/hooks → hooks}/useGrainAnalytics.d.ts.map +0 -0
  70. /package/dist/react/{react/hooks → hooks}/useGrainAnalytics.js +0 -0
  71. /package/dist/react/{react/hooks → hooks}/useGrainAnalytics.mjs +0 -0
  72. /package/dist/react/{react/hooks → hooks}/usePrivacyPreferences.d.ts +0 -0
  73. /package/dist/react/{react/hooks → hooks}/usePrivacyPreferences.d.ts.map +0 -0
  74. /package/dist/react/{react/hooks → hooks}/usePrivacyPreferences.js +0 -0
  75. /package/dist/react/{react/hooks → hooks}/usePrivacyPreferences.mjs +0 -0
  76. /package/dist/react/{react/hooks → hooks}/useTrack.d.ts +0 -0
  77. /package/dist/react/{react/hooks → hooks}/useTrack.d.ts.map +0 -0
  78. /package/dist/react/{react/hooks → hooks}/useTrack.js +0 -0
  79. /package/dist/react/{react/hooks → hooks}/useTrack.mjs +0 -0
  80. /package/dist/react/{react/types.d.ts → types.d.ts} +0 -0
  81. /package/dist/react/{react/types.d.ts.map → types.d.ts.map} +0 -0
  82. /package/dist/react/{react/types.js → types.js} +0 -0
  83. /package/dist/react/{react/types.mjs → types.mjs} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grainql/analytics-web",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "Lightweight TypeScript SDK for sending analytics events and managing remote configurations via Grain's REST API",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -1,59 +0,0 @@
1
- /**
2
- * Activity Detection for Grain Analytics
3
- * Tracks user activity (mouse, keyboard, touch, scroll) to determine if user is active
4
- */
5
- export declare class ActivityDetector {
6
- private lastActivityTime;
7
- private activityThreshold;
8
- private listeners;
9
- private boundActivityHandler;
10
- private isDestroyed;
11
- private readonly activityEvents;
12
- constructor();
13
- /**
14
- * Setup event listeners for activity detection
15
- */
16
- private setupListeners;
17
- /**
18
- * Handle activity event
19
- */
20
- private handleActivity;
21
- /**
22
- * Debounce function to limit how often activity handler is called
23
- */
24
- private debounce;
25
- /**
26
- * Check if user is currently active
27
- * @param threshold Time in ms to consider user inactive (default: 30s)
28
- */
29
- isActive(threshold?: number): boolean;
30
- /**
31
- * Get time since last activity in milliseconds
32
- */
33
- getTimeSinceLastActivity(): number;
34
- /**
35
- * Get last activity timestamp
36
- */
37
- getLastActivityTime(): number;
38
- /**
39
- * Set activity threshold
40
- */
41
- setActivityThreshold(threshold: number): void;
42
- /**
43
- * Add listener for activity changes
44
- */
45
- addListener(listener: () => void): void;
46
- /**
47
- * Remove listener
48
- */
49
- removeListener(listener: () => void): void;
50
- /**
51
- * Notify all listeners
52
- */
53
- private notifyListeners;
54
- /**
55
- * Cleanup and remove listeners
56
- */
57
- destroy(): void;
58
- }
59
- //# sourceMappingURL=activity.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"activity.d.ts","sourceRoot":"","sources":["../../src/activity.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,iBAAiB,CAAiB;IAC1C,OAAO,CAAC,SAAS,CAAyB;IAC1C,OAAO,CAAC,oBAAoB,CAAa;IACzC,OAAO,CAAC,WAAW,CAAS;IAG5B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAOpB;;IAQX;;OAEG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;IACH,OAAO,CAAC,cAAc;IAMtB;;OAEG;IACH,OAAO,CAAC,QAAQ;IAehB;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO;IAMrC;;OAEG;IACH,wBAAwB,IAAI,MAAM;IAIlC;;OAEG;IACH,mBAAmB,IAAI,MAAM;IAI7B;;OAEG;IACH,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAI7C;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAIvC;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAO1C;;OAEG;IACH,OAAO,CAAC,eAAe;IAUvB;;OAEG;IACH,OAAO,IAAI,IAAI;CAYhB"}
@@ -1,130 +0,0 @@
1
- "use strict";
2
- /**
3
- * Activity Detection for Grain Analytics
4
- * Tracks user activity (mouse, keyboard, touch, scroll) to determine if user is active
5
- */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.ActivityDetector = void 0;
8
- class ActivityDetector {
9
- constructor() {
10
- this.activityThreshold = 30000; // 30 seconds
11
- this.listeners = [];
12
- this.isDestroyed = false;
13
- // Events that indicate user activity
14
- this.activityEvents = [
15
- 'mousemove',
16
- 'mousedown',
17
- 'keydown',
18
- 'scroll',
19
- 'touchstart',
20
- 'click',
21
- ];
22
- this.lastActivityTime = Date.now();
23
- this.boundActivityHandler = this.debounce(this.handleActivity.bind(this), 500);
24
- this.setupListeners();
25
- }
26
- /**
27
- * Setup event listeners for activity detection
28
- */
29
- setupListeners() {
30
- if (typeof window === 'undefined')
31
- return;
32
- for (const event of this.activityEvents) {
33
- window.addEventListener(event, this.boundActivityHandler, { passive: true });
34
- }
35
- }
36
- /**
37
- * Handle activity event
38
- */
39
- handleActivity() {
40
- if (this.isDestroyed)
41
- return;
42
- this.lastActivityTime = Date.now();
43
- this.notifyListeners();
44
- }
45
- /**
46
- * Debounce function to limit how often activity handler is called
47
- */
48
- debounce(func, wait) {
49
- let timeout = null;
50
- return () => {
51
- if (timeout !== null) {
52
- clearTimeout(timeout);
53
- }
54
- timeout = window.setTimeout(() => {
55
- func();
56
- timeout = null;
57
- }, wait);
58
- };
59
- }
60
- /**
61
- * Check if user is currently active
62
- * @param threshold Time in ms to consider user inactive (default: 30s)
63
- */
64
- isActive(threshold) {
65
- const thresholdToUse = threshold ?? this.activityThreshold;
66
- const now = Date.now();
67
- return (now - this.lastActivityTime) < thresholdToUse;
68
- }
69
- /**
70
- * Get time since last activity in milliseconds
71
- */
72
- getTimeSinceLastActivity() {
73
- return Date.now() - this.lastActivityTime;
74
- }
75
- /**
76
- * Get last activity timestamp
77
- */
78
- getLastActivityTime() {
79
- return this.lastActivityTime;
80
- }
81
- /**
82
- * Set activity threshold
83
- */
84
- setActivityThreshold(threshold) {
85
- this.activityThreshold = threshold;
86
- }
87
- /**
88
- * Add listener for activity changes
89
- */
90
- addListener(listener) {
91
- this.listeners.push(listener);
92
- }
93
- /**
94
- * Remove listener
95
- */
96
- removeListener(listener) {
97
- const index = this.listeners.indexOf(listener);
98
- if (index > -1) {
99
- this.listeners.splice(index, 1);
100
- }
101
- }
102
- /**
103
- * Notify all listeners
104
- */
105
- notifyListeners() {
106
- for (const listener of this.listeners) {
107
- try {
108
- listener();
109
- }
110
- catch (error) {
111
- console.error('[Activity Detector] Listener error:', error);
112
- }
113
- }
114
- }
115
- /**
116
- * Cleanup and remove listeners
117
- */
118
- destroy() {
119
- if (this.isDestroyed)
120
- return;
121
- if (typeof window !== 'undefined') {
122
- for (const event of this.activityEvents) {
123
- window.removeEventListener(event, this.boundActivityHandler);
124
- }
125
- }
126
- this.listeners = [];
127
- this.isDestroyed = true;
128
- }
129
- }
130
- exports.ActivityDetector = ActivityDetector;
@@ -1,126 +0,0 @@
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
- }
@@ -1,72 +0,0 @@
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
- * GDPR Compliance: In opt-in mode, we can use localStorage for consent preferences
27
- * since storing consent choices is a legitimate interest and necessary for compliance.
28
- * The consent preference itself is not tracking data.
29
- */
30
- private loadConsentState;
31
- /**
32
- * Save consent state to localStorage
33
- */
34
- private saveConsentState;
35
- /**
36
- * Grant consent with optional categories
37
- */
38
- grantConsent(categories?: string[]): void;
39
- /**
40
- * Revoke consent (opt-out)
41
- */
42
- revokeConsent(categories?: string[]): void;
43
- /**
44
- * Get current consent state
45
- */
46
- getConsentState(): ConsentState | null;
47
- /**
48
- * Check if user has granted consent
49
- */
50
- hasConsent(category?: string): boolean;
51
- /**
52
- * Check if we should wait for consent before tracking
53
- */
54
- shouldWaitForConsent(): boolean;
55
- /**
56
- * Add consent change listener
57
- */
58
- addListener(listener: (state: ConsentState) => void): void;
59
- /**
60
- * Remove consent change listener
61
- */
62
- removeListener(listener: (state: ConsentState) => void): void;
63
- /**
64
- * Notify all listeners of consent state change
65
- */
66
- private notifyListeners;
67
- /**
68
- * Clear all consent data
69
- */
70
- clearConsent(): void;
71
- }
72
- //# sourceMappingURL=consent.d.ts.map
@@ -1 +0,0 @@
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;;;;;;OAMG;IACH,OAAO,CAAC,gBAAgB;IA2BxB;;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"}
@@ -1,195 +0,0 @@
1
- "use strict";
2
- /**
3
- * Consent management for Grain Analytics
4
- * Handles GDPR-compliant consent tracking and state management
5
- */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.ConsentManager = exports.CONSENT_VERSION = exports.DEFAULT_CONSENT_CATEGORIES = void 0;
8
- exports.DEFAULT_CONSENT_CATEGORIES = ['necessary', 'analytics', 'functional'];
9
- exports.CONSENT_VERSION = '1.0.0';
10
- /**
11
- * Consent manager for handling user consent state
12
- */
13
- class ConsentManager {
14
- constructor(tenantId, consentMode = 'opt-out') {
15
- this.consentState = null;
16
- this.listeners = [];
17
- this.consentMode = consentMode;
18
- this.storageKey = `grain_consent_${tenantId}`;
19
- this.loadConsentState();
20
- }
21
- /**
22
- * Load consent state from localStorage
23
- *
24
- * GDPR Compliance: In opt-in mode, we can use localStorage for consent preferences
25
- * since storing consent choices is a legitimate interest and necessary for compliance.
26
- * The consent preference itself is not tracking data.
27
- */
28
- loadConsentState() {
29
- if (typeof window === 'undefined')
30
- return;
31
- try {
32
- const stored = localStorage.getItem(this.storageKey);
33
- if (stored) {
34
- const parsed = JSON.parse(stored);
35
- this.consentState = {
36
- ...parsed,
37
- timestamp: new Date(parsed.timestamp),
38
- };
39
- }
40
- else if (this.consentMode === 'opt-out' || this.consentMode === 'disabled') {
41
- // Auto-grant consent for opt-out and disabled modes
42
- this.consentState = {
43
- granted: true,
44
- categories: exports.DEFAULT_CONSENT_CATEGORIES,
45
- timestamp: new Date(),
46
- version: exports.CONSENT_VERSION,
47
- };
48
- this.saveConsentState();
49
- }
50
- // Note: In opt-in mode without stored consent, consentState remains null (no consent)
51
- }
52
- catch (error) {
53
- console.error('[Grain Consent] Failed to load consent state:', error);
54
- }
55
- }
56
- /**
57
- * Save consent state to localStorage
58
- */
59
- saveConsentState() {
60
- if (typeof window === 'undefined' || !this.consentState)
61
- return;
62
- try {
63
- localStorage.setItem(this.storageKey, JSON.stringify(this.consentState));
64
- }
65
- catch (error) {
66
- console.error('[Grain Consent] Failed to save consent state:', error);
67
- }
68
- }
69
- /**
70
- * Grant consent with optional categories
71
- */
72
- grantConsent(categories) {
73
- const grantedCategories = categories || exports.DEFAULT_CONSENT_CATEGORIES;
74
- this.consentState = {
75
- granted: true,
76
- categories: grantedCategories,
77
- timestamp: new Date(),
78
- version: exports.CONSENT_VERSION,
79
- };
80
- this.saveConsentState();
81
- this.notifyListeners();
82
- }
83
- /**
84
- * Revoke consent (opt-out)
85
- */
86
- revokeConsent(categories) {
87
- if (!this.consentState) {
88
- this.consentState = {
89
- granted: false,
90
- categories: [],
91
- timestamp: new Date(),
92
- version: exports.CONSENT_VERSION,
93
- };
94
- }
95
- else if (categories) {
96
- // Remove specific categories
97
- this.consentState = {
98
- ...this.consentState,
99
- categories: this.consentState.categories.filter(cat => !categories.includes(cat)),
100
- granted: this.consentState.categories.length > 0,
101
- timestamp: new Date(),
102
- };
103
- }
104
- else {
105
- // Revoke all consent
106
- this.consentState = {
107
- granted: false,
108
- categories: [],
109
- timestamp: new Date(),
110
- version: exports.CONSENT_VERSION,
111
- };
112
- }
113
- this.saveConsentState();
114
- this.notifyListeners();
115
- }
116
- /**
117
- * Get current consent state
118
- */
119
- getConsentState() {
120
- return this.consentState ? { ...this.consentState } : null;
121
- }
122
- /**
123
- * Check if user has granted consent
124
- */
125
- hasConsent(category) {
126
- // Disabled mode always returns true (no consent required)
127
- if (this.consentMode === 'disabled') {
128
- return true;
129
- }
130
- // No consent state in opt-in mode means no consent
131
- if (this.consentMode === 'opt-in' && !this.consentState) {
132
- return false;
133
- }
134
- // Check consent state
135
- if (!this.consentState?.granted) {
136
- return false;
137
- }
138
- // Check specific category if provided
139
- if (category) {
140
- return this.consentState.categories.includes(category);
141
- }
142
- return true;
143
- }
144
- /**
145
- * Check if we should wait for consent before tracking
146
- */
147
- shouldWaitForConsent() {
148
- return this.consentMode === 'opt-in' && !this.consentState?.granted;
149
- }
150
- /**
151
- * Add consent change listener
152
- */
153
- addListener(listener) {
154
- this.listeners.push(listener);
155
- }
156
- /**
157
- * Remove consent change listener
158
- */
159
- removeListener(listener) {
160
- const index = this.listeners.indexOf(listener);
161
- if (index > -1) {
162
- this.listeners.splice(index, 1);
163
- }
164
- }
165
- /**
166
- * Notify all listeners of consent state change
167
- */
168
- notifyListeners() {
169
- if (!this.consentState)
170
- return;
171
- this.listeners.forEach(listener => {
172
- try {
173
- listener(this.consentState);
174
- }
175
- catch (error) {
176
- console.error('[Grain Consent] Listener error:', error);
177
- }
178
- });
179
- }
180
- /**
181
- * Clear all consent data
182
- */
183
- clearConsent() {
184
- if (typeof window === 'undefined')
185
- return;
186
- try {
187
- localStorage.removeItem(this.storageKey);
188
- this.consentState = null;
189
- }
190
- catch (error) {
191
- console.error('[Grain Consent] Failed to clear consent:', error);
192
- }
193
- }
194
- }
195
- exports.ConsentManager = ConsentManager;