@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.
Files changed (173) hide show
  1. package/dist/activity.d.ts +59 -0
  2. package/dist/activity.d.ts.map +1 -0
  3. package/dist/cjs/activity.d.ts +59 -0
  4. package/dist/cjs/activity.d.ts.map +1 -0
  5. package/dist/cjs/activity.js +131 -0
  6. package/dist/cjs/activity.js.map +1 -0
  7. package/dist/cjs/consent.d.ts +68 -0
  8. package/dist/cjs/consent.d.ts.map +1 -0
  9. package/dist/cjs/consent.js +191 -0
  10. package/dist/cjs/consent.js.map +1 -0
  11. package/dist/cjs/cookies.d.ts +28 -0
  12. package/dist/cjs/cookies.d.ts.map +1 -0
  13. package/dist/cjs/cookies.js +95 -0
  14. package/dist/cjs/cookies.js.map +1 -0
  15. package/dist/cjs/heartbeat.d.ts +42 -0
  16. package/dist/cjs/heartbeat.d.ts.map +1 -0
  17. package/dist/cjs/heartbeat.js +92 -0
  18. package/dist/cjs/heartbeat.js.map +1 -0
  19. package/dist/cjs/index.d.ts +100 -3
  20. package/dist/cjs/index.d.ts.map +1 -1
  21. package/dist/cjs/index.js.map +1 -1
  22. package/dist/cjs/page-tracking.d.ts +60 -0
  23. package/dist/cjs/page-tracking.d.ts.map +1 -0
  24. package/dist/cjs/page-tracking.js +180 -0
  25. package/dist/cjs/page-tracking.js.map +1 -0
  26. package/dist/cjs/react/components/ConsentBanner.d.ts +16 -0
  27. package/dist/cjs/react/components/ConsentBanner.d.ts.map +1 -0
  28. package/dist/cjs/react/components/ConsentBanner.js +112 -0
  29. package/dist/cjs/react/components/ConsentBanner.js.map +1 -0
  30. package/dist/cjs/react/components/CookieNotice.d.ts +12 -0
  31. package/dist/cjs/react/components/CookieNotice.d.ts.map +1 -0
  32. package/dist/cjs/react/components/CookieNotice.js +62 -0
  33. package/dist/cjs/react/components/CookieNotice.js.map +1 -0
  34. package/dist/cjs/react/components/PrivacyPreferenceCenter.d.ts +12 -0
  35. package/dist/cjs/react/components/PrivacyPreferenceCenter.d.ts.map +1 -0
  36. package/dist/cjs/react/components/PrivacyPreferenceCenter.js +120 -0
  37. package/dist/cjs/react/components/PrivacyPreferenceCenter.js.map +1 -0
  38. package/dist/cjs/react/hooks/useConsent.d.ts +13 -0
  39. package/dist/cjs/react/hooks/useConsent.d.ts.map +1 -0
  40. package/dist/cjs/react/hooks/useConsent.js +84 -0
  41. package/dist/cjs/react/hooks/useConsent.js.map +1 -0
  42. package/dist/cjs/react/hooks/useDataDeletion.d.ts +17 -0
  43. package/dist/cjs/react/hooks/useDataDeletion.d.ts.map +1 -0
  44. package/dist/cjs/react/hooks/useDataDeletion.js +117 -0
  45. package/dist/cjs/react/hooks/useDataDeletion.js.map +1 -0
  46. package/dist/cjs/react/hooks/usePrivacyPreferences.d.ts +15 -0
  47. package/dist/cjs/react/hooks/usePrivacyPreferences.d.ts.map +1 -0
  48. package/dist/cjs/react/hooks/usePrivacyPreferences.js +82 -0
  49. package/dist/cjs/react/hooks/usePrivacyPreferences.js.map +1 -0
  50. package/dist/cjs/react/index.d.ts +11 -0
  51. package/dist/cjs/react/index.d.ts.map +1 -1
  52. package/dist/cjs/react/index.js +15 -1
  53. package/dist/cjs/react/index.js.map +1 -1
  54. package/dist/consent.d.ts +68 -0
  55. package/dist/consent.d.ts.map +1 -0
  56. package/dist/cookies.d.ts +28 -0
  57. package/dist/cookies.d.ts.map +1 -0
  58. package/dist/esm/activity.d.ts +59 -0
  59. package/dist/esm/activity.d.ts.map +1 -0
  60. package/dist/esm/activity.js +127 -0
  61. package/dist/esm/activity.js.map +1 -0
  62. package/dist/esm/consent.d.ts +68 -0
  63. package/dist/esm/consent.d.ts.map +1 -0
  64. package/dist/esm/consent.js +187 -0
  65. package/dist/esm/consent.js.map +1 -0
  66. package/dist/esm/cookies.d.ts +28 -0
  67. package/dist/esm/cookies.d.ts.map +1 -0
  68. package/dist/esm/cookies.js +89 -0
  69. package/dist/esm/cookies.js.map +1 -0
  70. package/dist/esm/heartbeat.d.ts +42 -0
  71. package/dist/esm/heartbeat.d.ts.map +1 -0
  72. package/dist/esm/heartbeat.js +88 -0
  73. package/dist/esm/heartbeat.js.map +1 -0
  74. package/dist/esm/index.d.ts +100 -3
  75. package/dist/esm/index.d.ts.map +1 -1
  76. package/dist/esm/index.js.map +1 -1
  77. package/dist/esm/page-tracking.d.ts +60 -0
  78. package/dist/esm/page-tracking.d.ts.map +1 -0
  79. package/dist/esm/page-tracking.js +176 -0
  80. package/dist/esm/page-tracking.js.map +1 -0
  81. package/dist/esm/react/components/ConsentBanner.d.ts +16 -0
  82. package/dist/esm/react/components/ConsentBanner.d.ts.map +1 -0
  83. package/dist/esm/react/components/ConsentBanner.js +76 -0
  84. package/dist/esm/react/components/ConsentBanner.js.map +1 -0
  85. package/dist/esm/react/components/CookieNotice.d.ts +12 -0
  86. package/dist/esm/react/components/CookieNotice.d.ts.map +1 -0
  87. package/dist/esm/react/components/CookieNotice.js +26 -0
  88. package/dist/esm/react/components/CookieNotice.js.map +1 -0
  89. package/dist/esm/react/components/PrivacyPreferenceCenter.d.ts +12 -0
  90. package/dist/esm/react/components/PrivacyPreferenceCenter.d.ts.map +1 -0
  91. package/dist/esm/react/components/PrivacyPreferenceCenter.js +84 -0
  92. package/dist/esm/react/components/PrivacyPreferenceCenter.js.map +1 -0
  93. package/dist/esm/react/hooks/useConsent.d.ts +13 -0
  94. package/dist/esm/react/hooks/useConsent.d.ts.map +1 -0
  95. package/dist/esm/react/hooks/useConsent.js +48 -0
  96. package/dist/esm/react/hooks/useConsent.js.map +1 -0
  97. package/dist/esm/react/hooks/useDataDeletion.d.ts +17 -0
  98. package/dist/esm/react/hooks/useDataDeletion.d.ts.map +1 -0
  99. package/dist/esm/react/hooks/useDataDeletion.js +81 -0
  100. package/dist/esm/react/hooks/useDataDeletion.js.map +1 -0
  101. package/dist/esm/react/hooks/usePrivacyPreferences.d.ts +15 -0
  102. package/dist/esm/react/hooks/usePrivacyPreferences.d.ts.map +1 -0
  103. package/dist/esm/react/hooks/usePrivacyPreferences.js +46 -0
  104. package/dist/esm/react/hooks/usePrivacyPreferences.js.map +1 -0
  105. package/dist/esm/react/index.d.ts +11 -0
  106. package/dist/esm/react/index.d.ts.map +1 -1
  107. package/dist/esm/react/index.js +8 -0
  108. package/dist/esm/react/index.js.map +1 -1
  109. package/dist/heartbeat.d.ts +42 -0
  110. package/dist/heartbeat.d.ts.map +1 -0
  111. package/dist/index.d.ts +100 -3
  112. package/dist/index.d.ts.map +1 -1
  113. package/dist/index.global.dev.js +903 -12
  114. package/dist/index.global.dev.js.map +3 -3
  115. package/dist/index.global.js +2 -2
  116. package/dist/index.global.js.map +4 -4
  117. package/dist/index.js +321 -11
  118. package/dist/index.mjs +321 -11
  119. package/dist/page-tracking.d.ts +60 -0
  120. package/dist/page-tracking.d.ts.map +1 -0
  121. package/dist/react/activity.d.ts +59 -0
  122. package/dist/react/activity.d.ts.map +1 -0
  123. package/dist/react/activity.js +130 -0
  124. package/dist/react/activity.mjs +126 -0
  125. package/dist/react/consent.d.ts +68 -0
  126. package/dist/react/consent.d.ts.map +1 -0
  127. package/dist/react/consent.js +190 -0
  128. package/dist/react/consent.mjs +186 -0
  129. package/dist/react/cookies.d.ts +28 -0
  130. package/dist/react/cookies.d.ts.map +1 -0
  131. package/dist/react/cookies.js +94 -0
  132. package/dist/react/cookies.mjs +88 -0
  133. package/dist/react/heartbeat.d.ts +42 -0
  134. package/dist/react/heartbeat.d.ts.map +1 -0
  135. package/dist/react/heartbeat.js +91 -0
  136. package/dist/react/heartbeat.mjs +87 -0
  137. package/dist/react/index.d.ts +100 -3
  138. package/dist/react/index.d.ts.map +1 -1
  139. package/dist/react/index.js +321 -11
  140. package/dist/react/index.mjs +321 -11
  141. package/dist/react/page-tracking.d.ts +60 -0
  142. package/dist/react/page-tracking.d.ts.map +1 -0
  143. package/dist/react/page-tracking.js +179 -0
  144. package/dist/react/page-tracking.mjs +175 -0
  145. package/dist/react/react/components/ConsentBanner.d.ts +16 -0
  146. package/dist/react/react/components/ConsentBanner.d.ts.map +1 -0
  147. package/dist/react/react/components/ConsentBanner.js +78 -0
  148. package/dist/react/react/components/ConsentBanner.mjs +75 -0
  149. package/dist/react/react/components/CookieNotice.d.ts +12 -0
  150. package/dist/react/react/components/CookieNotice.d.ts.map +1 -0
  151. package/dist/react/react/components/CookieNotice.js +28 -0
  152. package/dist/react/react/components/CookieNotice.mjs +25 -0
  153. package/dist/react/react/components/PrivacyPreferenceCenter.d.ts +12 -0
  154. package/dist/react/react/components/PrivacyPreferenceCenter.d.ts.map +1 -0
  155. package/dist/react/react/components/PrivacyPreferenceCenter.js +86 -0
  156. package/dist/react/react/components/PrivacyPreferenceCenter.mjs +83 -0
  157. package/dist/react/react/hooks/useConsent.d.ts +13 -0
  158. package/dist/react/react/hooks/useConsent.d.ts.map +1 -0
  159. package/dist/react/react/hooks/useConsent.js +50 -0
  160. package/dist/react/react/hooks/useConsent.mjs +47 -0
  161. package/dist/react/react/hooks/useDataDeletion.d.ts +17 -0
  162. package/dist/react/react/hooks/useDataDeletion.d.ts.map +1 -0
  163. package/dist/react/react/hooks/useDataDeletion.js +83 -0
  164. package/dist/react/react/hooks/useDataDeletion.mjs +80 -0
  165. package/dist/react/react/hooks/usePrivacyPreferences.d.ts +15 -0
  166. package/dist/react/react/hooks/usePrivacyPreferences.d.ts.map +1 -0
  167. package/dist/react/react/hooks/usePrivacyPreferences.js +48 -0
  168. package/dist/react/react/hooks/usePrivacyPreferences.mjs +45 -0
  169. package/dist/react/react/index.d.ts +11 -0
  170. package/dist/react/react/index.d.ts.map +1 -1
  171. package/dist/react/react/index.js +15 -1
  172. package/dist/react/react/index.mjs +8 -0
  173. 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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"}