@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.
- package/dist/index.global.dev.js +1 -1
- package/dist/index.global.js +1 -1
- package/dist/react/index.d.ts +44 -519
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +54 -1517
- package/dist/react/index.mjs +42 -1514
- package/package.json +1 -1
- package/dist/react/activity.d.ts +0 -59
- package/dist/react/activity.d.ts.map +0 -1
- package/dist/react/activity.js +0 -130
- package/dist/react/activity.mjs +0 -126
- package/dist/react/consent.d.ts +0 -72
- package/dist/react/consent.d.ts.map +0 -1
- package/dist/react/consent.js +0 -195
- package/dist/react/consent.mjs +0 -191
- package/dist/react/cookies.d.ts +0 -28
- package/dist/react/cookies.d.ts.map +0 -1
- package/dist/react/cookies.js +0 -94
- package/dist/react/cookies.mjs +0 -88
- package/dist/react/heartbeat.d.ts +0 -47
- package/dist/react/heartbeat.d.ts.map +0 -1
- package/dist/react/heartbeat.js +0 -119
- package/dist/react/heartbeat.mjs +0 -115
- package/dist/react/page-tracking.d.ts +0 -60
- package/dist/react/page-tracking.d.ts.map +0 -1
- package/dist/react/page-tracking.js +0 -179
- package/dist/react/page-tracking.mjs +0 -175
- package/dist/react/react/index.d.ts +0 -47
- package/dist/react/react/index.d.ts.map +0 -1
- package/dist/react/react/index.js +0 -58
- package/dist/react/react/index.mjs +0 -44
- /package/dist/react/{react/GrainProvider.d.ts → GrainProvider.d.ts} +0 -0
- /package/dist/react/{react/GrainProvider.d.ts.map → GrainProvider.d.ts.map} +0 -0
- /package/dist/react/{react/GrainProvider.js → GrainProvider.js} +0 -0
- /package/dist/react/{react/GrainProvider.mjs → GrainProvider.mjs} +0 -0
- /package/dist/react/{react/components → components}/ConsentBanner.d.ts +0 -0
- /package/dist/react/{react/components → components}/ConsentBanner.d.ts.map +0 -0
- /package/dist/react/{react/components → components}/ConsentBanner.js +0 -0
- /package/dist/react/{react/components → components}/ConsentBanner.mjs +0 -0
- /package/dist/react/{react/components → components}/CookieNotice.d.ts +0 -0
- /package/dist/react/{react/components → components}/CookieNotice.d.ts.map +0 -0
- /package/dist/react/{react/components → components}/CookieNotice.js +0 -0
- /package/dist/react/{react/components → components}/CookieNotice.mjs +0 -0
- /package/dist/react/{react/components → components}/PrivacyPreferenceCenter.d.ts +0 -0
- /package/dist/react/{react/components → components}/PrivacyPreferenceCenter.d.ts.map +0 -0
- /package/dist/react/{react/components → components}/PrivacyPreferenceCenter.js +0 -0
- /package/dist/react/{react/components → components}/PrivacyPreferenceCenter.mjs +0 -0
- /package/dist/react/{react/context.d.ts → context.d.ts} +0 -0
- /package/dist/react/{react/context.d.ts.map → context.d.ts.map} +0 -0
- /package/dist/react/{react/context.js → context.js} +0 -0
- /package/dist/react/{react/context.mjs → context.mjs} +0 -0
- /package/dist/react/{react/hooks → hooks}/useAllConfigs.d.ts +0 -0
- /package/dist/react/{react/hooks → hooks}/useAllConfigs.d.ts.map +0 -0
- /package/dist/react/{react/hooks → hooks}/useAllConfigs.js +0 -0
- /package/dist/react/{react/hooks → hooks}/useAllConfigs.mjs +0 -0
- /package/dist/react/{react/hooks → hooks}/useConfig.d.ts +0 -0
- /package/dist/react/{react/hooks → hooks}/useConfig.d.ts.map +0 -0
- /package/dist/react/{react/hooks → hooks}/useConfig.js +0 -0
- /package/dist/react/{react/hooks → hooks}/useConfig.mjs +0 -0
- /package/dist/react/{react/hooks → hooks}/useConsent.d.ts +0 -0
- /package/dist/react/{react/hooks → hooks}/useConsent.d.ts.map +0 -0
- /package/dist/react/{react/hooks → hooks}/useConsent.js +0 -0
- /package/dist/react/{react/hooks → hooks}/useConsent.mjs +0 -0
- /package/dist/react/{react/hooks → hooks}/useDataDeletion.d.ts +0 -0
- /package/dist/react/{react/hooks → hooks}/useDataDeletion.d.ts.map +0 -0
- /package/dist/react/{react/hooks → hooks}/useDataDeletion.js +0 -0
- /package/dist/react/{react/hooks → hooks}/useDataDeletion.mjs +0 -0
- /package/dist/react/{react/hooks → hooks}/useGrainAnalytics.d.ts +0 -0
- /package/dist/react/{react/hooks → hooks}/useGrainAnalytics.d.ts.map +0 -0
- /package/dist/react/{react/hooks → hooks}/useGrainAnalytics.js +0 -0
- /package/dist/react/{react/hooks → hooks}/useGrainAnalytics.mjs +0 -0
- /package/dist/react/{react/hooks → hooks}/usePrivacyPreferences.d.ts +0 -0
- /package/dist/react/{react/hooks → hooks}/usePrivacyPreferences.d.ts.map +0 -0
- /package/dist/react/{react/hooks → hooks}/usePrivacyPreferences.js +0 -0
- /package/dist/react/{react/hooks → hooks}/usePrivacyPreferences.mjs +0 -0
- /package/dist/react/{react/hooks → hooks}/useTrack.d.ts +0 -0
- /package/dist/react/{react/hooks → hooks}/useTrack.d.ts.map +0 -0
- /package/dist/react/{react/hooks → hooks}/useTrack.js +0 -0
- /package/dist/react/{react/hooks → hooks}/useTrack.mjs +0 -0
- /package/dist/react/{react/types.d.ts → types.d.ts} +0 -0
- /package/dist/react/{react/types.d.ts.map → types.d.ts.map} +0 -0
- /package/dist/react/{react/types.js → types.js} +0 -0
- /package/dist/react/{react/types.mjs → types.mjs} +0 -0
package/dist/react/consent.mjs
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
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
|
-
* GDPR Compliance: In opt-in mode, we can use localStorage for consent preferences
|
|
22
|
-
* since storing consent choices is a legitimate interest and necessary for compliance.
|
|
23
|
-
* The consent preference itself is not tracking data.
|
|
24
|
-
*/
|
|
25
|
-
loadConsentState() {
|
|
26
|
-
if (typeof window === 'undefined')
|
|
27
|
-
return;
|
|
28
|
-
try {
|
|
29
|
-
const stored = localStorage.getItem(this.storageKey);
|
|
30
|
-
if (stored) {
|
|
31
|
-
const parsed = JSON.parse(stored);
|
|
32
|
-
this.consentState = {
|
|
33
|
-
...parsed,
|
|
34
|
-
timestamp: new Date(parsed.timestamp),
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
else if (this.consentMode === 'opt-out' || this.consentMode === 'disabled') {
|
|
38
|
-
// Auto-grant consent for opt-out and disabled modes
|
|
39
|
-
this.consentState = {
|
|
40
|
-
granted: true,
|
|
41
|
-
categories: DEFAULT_CONSENT_CATEGORIES,
|
|
42
|
-
timestamp: new Date(),
|
|
43
|
-
version: CONSENT_VERSION,
|
|
44
|
-
};
|
|
45
|
-
this.saveConsentState();
|
|
46
|
-
}
|
|
47
|
-
// Note: In opt-in mode without stored consent, consentState remains null (no consent)
|
|
48
|
-
}
|
|
49
|
-
catch (error) {
|
|
50
|
-
console.error('[Grain Consent] Failed to load consent state:', error);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Save consent state to localStorage
|
|
55
|
-
*/
|
|
56
|
-
saveConsentState() {
|
|
57
|
-
if (typeof window === 'undefined' || !this.consentState)
|
|
58
|
-
return;
|
|
59
|
-
try {
|
|
60
|
-
localStorage.setItem(this.storageKey, JSON.stringify(this.consentState));
|
|
61
|
-
}
|
|
62
|
-
catch (error) {
|
|
63
|
-
console.error('[Grain Consent] Failed to save consent state:', error);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Grant consent with optional categories
|
|
68
|
-
*/
|
|
69
|
-
grantConsent(categories) {
|
|
70
|
-
const grantedCategories = categories || DEFAULT_CONSENT_CATEGORIES;
|
|
71
|
-
this.consentState = {
|
|
72
|
-
granted: true,
|
|
73
|
-
categories: grantedCategories,
|
|
74
|
-
timestamp: new Date(),
|
|
75
|
-
version: CONSENT_VERSION,
|
|
76
|
-
};
|
|
77
|
-
this.saveConsentState();
|
|
78
|
-
this.notifyListeners();
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Revoke consent (opt-out)
|
|
82
|
-
*/
|
|
83
|
-
revokeConsent(categories) {
|
|
84
|
-
if (!this.consentState) {
|
|
85
|
-
this.consentState = {
|
|
86
|
-
granted: false,
|
|
87
|
-
categories: [],
|
|
88
|
-
timestamp: new Date(),
|
|
89
|
-
version: CONSENT_VERSION,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
else if (categories) {
|
|
93
|
-
// Remove specific categories
|
|
94
|
-
this.consentState = {
|
|
95
|
-
...this.consentState,
|
|
96
|
-
categories: this.consentState.categories.filter(cat => !categories.includes(cat)),
|
|
97
|
-
granted: this.consentState.categories.length > 0,
|
|
98
|
-
timestamp: new Date(),
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
// Revoke all consent
|
|
103
|
-
this.consentState = {
|
|
104
|
-
granted: false,
|
|
105
|
-
categories: [],
|
|
106
|
-
timestamp: new Date(),
|
|
107
|
-
version: CONSENT_VERSION,
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
this.saveConsentState();
|
|
111
|
-
this.notifyListeners();
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Get current consent state
|
|
115
|
-
*/
|
|
116
|
-
getConsentState() {
|
|
117
|
-
return this.consentState ? { ...this.consentState } : null;
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Check if user has granted consent
|
|
121
|
-
*/
|
|
122
|
-
hasConsent(category) {
|
|
123
|
-
// Disabled mode always returns true (no consent required)
|
|
124
|
-
if (this.consentMode === 'disabled') {
|
|
125
|
-
return true;
|
|
126
|
-
}
|
|
127
|
-
// No consent state in opt-in mode means no consent
|
|
128
|
-
if (this.consentMode === 'opt-in' && !this.consentState) {
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
// Check consent state
|
|
132
|
-
if (!this.consentState?.granted) {
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
// Check specific category if provided
|
|
136
|
-
if (category) {
|
|
137
|
-
return this.consentState.categories.includes(category);
|
|
138
|
-
}
|
|
139
|
-
return true;
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Check if we should wait for consent before tracking
|
|
143
|
-
*/
|
|
144
|
-
shouldWaitForConsent() {
|
|
145
|
-
return this.consentMode === 'opt-in' && !this.consentState?.granted;
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Add consent change listener
|
|
149
|
-
*/
|
|
150
|
-
addListener(listener) {
|
|
151
|
-
this.listeners.push(listener);
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Remove consent change listener
|
|
155
|
-
*/
|
|
156
|
-
removeListener(listener) {
|
|
157
|
-
const index = this.listeners.indexOf(listener);
|
|
158
|
-
if (index > -1) {
|
|
159
|
-
this.listeners.splice(index, 1);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Notify all listeners of consent state change
|
|
164
|
-
*/
|
|
165
|
-
notifyListeners() {
|
|
166
|
-
if (!this.consentState)
|
|
167
|
-
return;
|
|
168
|
-
this.listeners.forEach(listener => {
|
|
169
|
-
try {
|
|
170
|
-
listener(this.consentState);
|
|
171
|
-
}
|
|
172
|
-
catch (error) {
|
|
173
|
-
console.error('[Grain Consent] Listener error:', error);
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* Clear all consent data
|
|
179
|
-
*/
|
|
180
|
-
clearConsent() {
|
|
181
|
-
if (typeof window === 'undefined')
|
|
182
|
-
return;
|
|
183
|
-
try {
|
|
184
|
-
localStorage.removeItem(this.storageKey);
|
|
185
|
-
this.consentState = null;
|
|
186
|
-
}
|
|
187
|
-
catch (error) {
|
|
188
|
-
console.error('[Grain Consent] Failed to clear consent:', error);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
package/dist/react/cookies.d.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
package/dist/react/cookies.js
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Cookie utilities for Grain Analytics
|
|
4
|
-
* Provides GDPR-compliant cookie management with configurable options
|
|
5
|
-
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.setCookie = setCookie;
|
|
8
|
-
exports.getCookie = getCookie;
|
|
9
|
-
exports.deleteCookie = deleteCookie;
|
|
10
|
-
exports.areCookiesEnabled = areCookiesEnabled;
|
|
11
|
-
/**
|
|
12
|
-
* Set a cookie with configurable options
|
|
13
|
-
*/
|
|
14
|
-
function setCookie(name, value, config) {
|
|
15
|
-
if (typeof document === 'undefined')
|
|
16
|
-
return;
|
|
17
|
-
const parts = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`];
|
|
18
|
-
if (config?.maxAge !== undefined) {
|
|
19
|
-
parts.push(`max-age=${config.maxAge}`);
|
|
20
|
-
}
|
|
21
|
-
if (config?.domain) {
|
|
22
|
-
parts.push(`domain=${config.domain}`);
|
|
23
|
-
}
|
|
24
|
-
if (config?.path) {
|
|
25
|
-
parts.push(`path=${config.path}`);
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
parts.push('path=/');
|
|
29
|
-
}
|
|
30
|
-
if (config?.sameSite) {
|
|
31
|
-
parts.push(`samesite=${config.sameSite}`);
|
|
32
|
-
}
|
|
33
|
-
if (config?.secure) {
|
|
34
|
-
parts.push('secure');
|
|
35
|
-
}
|
|
36
|
-
document.cookie = parts.join('; ');
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Get a cookie value by name
|
|
40
|
-
*/
|
|
41
|
-
function getCookie(name) {
|
|
42
|
-
if (typeof document === 'undefined')
|
|
43
|
-
return null;
|
|
44
|
-
const nameEQ = encodeURIComponent(name) + '=';
|
|
45
|
-
const cookies = document.cookie.split(';');
|
|
46
|
-
for (let i = 0; i < cookies.length; i++) {
|
|
47
|
-
let cookie = cookies[i];
|
|
48
|
-
while (cookie.charAt(0) === ' ') {
|
|
49
|
-
cookie = cookie.substring(1);
|
|
50
|
-
}
|
|
51
|
-
if (cookie.indexOf(nameEQ) === 0) {
|
|
52
|
-
return decodeURIComponent(cookie.substring(nameEQ.length));
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Delete a cookie by name
|
|
59
|
-
*/
|
|
60
|
-
function deleteCookie(name, config) {
|
|
61
|
-
if (typeof document === 'undefined')
|
|
62
|
-
return;
|
|
63
|
-
const parts = [
|
|
64
|
-
`${encodeURIComponent(name)}=`,
|
|
65
|
-
'max-age=0',
|
|
66
|
-
];
|
|
67
|
-
if (config?.domain) {
|
|
68
|
-
parts.push(`domain=${config.domain}`);
|
|
69
|
-
}
|
|
70
|
-
if (config?.path) {
|
|
71
|
-
parts.push(`path=${config.path}`);
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
parts.push('path=/');
|
|
75
|
-
}
|
|
76
|
-
document.cookie = parts.join('; ');
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Check if cookies are available and working
|
|
80
|
-
*/
|
|
81
|
-
function areCookiesEnabled() {
|
|
82
|
-
if (typeof document === 'undefined')
|
|
83
|
-
return false;
|
|
84
|
-
try {
|
|
85
|
-
const testCookie = '_grain_cookie_test';
|
|
86
|
-
setCookie(testCookie, 'test', { maxAge: 1 });
|
|
87
|
-
const result = getCookie(testCookie) === 'test';
|
|
88
|
-
deleteCookie(testCookie);
|
|
89
|
-
return result;
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
}
|
package/dist/react/cookies.mjs
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
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
|
-
private hasSentPageLoadHeartbeat;
|
|
29
|
-
constructor(tracker: HeartbeatTracker, activityDetector: ActivityDetector, config: HeartbeatConfig);
|
|
30
|
-
/**
|
|
31
|
-
* Send initial heartbeat when page loads
|
|
32
|
-
*/
|
|
33
|
-
private sendPageLoadHeartbeat;
|
|
34
|
-
/**
|
|
35
|
-
* Schedule the next heartbeat based on current activity
|
|
36
|
-
*/
|
|
37
|
-
private scheduleNextHeartbeat;
|
|
38
|
-
/**
|
|
39
|
-
* Send heartbeat event
|
|
40
|
-
*/
|
|
41
|
-
private sendHeartbeat;
|
|
42
|
-
/**
|
|
43
|
-
* Destroy the heartbeat manager
|
|
44
|
-
*/
|
|
45
|
-
destroy(): void;
|
|
46
|
-
}
|
|
47
|
-
//# sourceMappingURL=heartbeat.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
package/dist/react/heartbeat.js
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Heartbeat Manager for Grain Analytics
|
|
4
|
-
* Tracks session activity with consent-aware behavior
|
|
5
|
-
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.HeartbeatManager = void 0;
|
|
8
|
-
class HeartbeatManager {
|
|
9
|
-
constructor(tracker, activityDetector, config) {
|
|
10
|
-
this.heartbeatTimer = null;
|
|
11
|
-
this.isDestroyed = false;
|
|
12
|
-
this.hasSentPageLoadHeartbeat = false;
|
|
13
|
-
this.tracker = tracker;
|
|
14
|
-
this.activityDetector = activityDetector;
|
|
15
|
-
this.config = config;
|
|
16
|
-
this.lastHeartbeatTime = Date.now();
|
|
17
|
-
this.currentInterval = config.activeInterval;
|
|
18
|
-
// Send initial heartbeat when page loads (if allowed by consent)
|
|
19
|
-
this.sendPageLoadHeartbeat();
|
|
20
|
-
// Start periodic heartbeat tracking
|
|
21
|
-
this.scheduleNextHeartbeat();
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Send initial heartbeat when page loads
|
|
25
|
-
*/
|
|
26
|
-
sendPageLoadHeartbeat() {
|
|
27
|
-
if (this.isDestroyed || this.hasSentPageLoadHeartbeat)
|
|
28
|
-
return;
|
|
29
|
-
// Wait for page to be fully loaded
|
|
30
|
-
if (typeof window !== 'undefined' && document.readyState !== 'complete') {
|
|
31
|
-
const handleLoad = () => {
|
|
32
|
-
this.sendHeartbeat('page_load');
|
|
33
|
-
this.hasSentPageLoadHeartbeat = true;
|
|
34
|
-
window.removeEventListener('load', handleLoad);
|
|
35
|
-
};
|
|
36
|
-
window.addEventListener('load', handleLoad);
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
// Page is already loaded or we're in a non-browser environment
|
|
40
|
-
this.sendHeartbeat('page_load');
|
|
41
|
-
this.hasSentPageLoadHeartbeat = true;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Schedule the next heartbeat based on current activity
|
|
46
|
-
*/
|
|
47
|
-
scheduleNextHeartbeat() {
|
|
48
|
-
if (this.isDestroyed)
|
|
49
|
-
return;
|
|
50
|
-
// Clear existing timer
|
|
51
|
-
if (this.heartbeatTimer !== null) {
|
|
52
|
-
clearTimeout(this.heartbeatTimer);
|
|
53
|
-
}
|
|
54
|
-
// Determine interval based on activity
|
|
55
|
-
const isActive = this.activityDetector.isActive(60000); // 1 minute threshold
|
|
56
|
-
this.currentInterval = isActive ? this.config.activeInterval : this.config.inactiveInterval;
|
|
57
|
-
// Schedule next heartbeat
|
|
58
|
-
this.heartbeatTimer = window.setTimeout(() => {
|
|
59
|
-
this.sendHeartbeat('periodic');
|
|
60
|
-
this.scheduleNextHeartbeat();
|
|
61
|
-
}, this.currentInterval);
|
|
62
|
-
if (this.config.debug) {
|
|
63
|
-
console.log(`[Heartbeat] Scheduled next heartbeat in ${this.currentInterval / 1000}s (${isActive ? 'active' : 'inactive'})`);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Send heartbeat event
|
|
68
|
-
*/
|
|
69
|
-
sendHeartbeat(heartbeatType = 'periodic') {
|
|
70
|
-
if (this.isDestroyed)
|
|
71
|
-
return;
|
|
72
|
-
const now = Date.now();
|
|
73
|
-
const isActive = this.activityDetector.isActive(60000); // 1 minute threshold
|
|
74
|
-
const hasConsent = this.tracker.hasConsent('analytics');
|
|
75
|
-
// Base properties (always included)
|
|
76
|
-
const properties = {
|
|
77
|
-
type: 'heartbeat',
|
|
78
|
-
heartbeat_type: heartbeatType,
|
|
79
|
-
status: isActive ? 'active' : 'inactive',
|
|
80
|
-
timestamp: now,
|
|
81
|
-
};
|
|
82
|
-
// Enhanced properties when consent is granted
|
|
83
|
-
if (hasConsent) {
|
|
84
|
-
const page = this.tracker.getCurrentPage();
|
|
85
|
-
if (page) {
|
|
86
|
-
properties.page = page;
|
|
87
|
-
}
|
|
88
|
-
// Only include duration and event count for periodic heartbeats
|
|
89
|
-
if (heartbeatType === 'periodic') {
|
|
90
|
-
properties.duration = now - this.lastHeartbeatTime;
|
|
91
|
-
properties.event_count = this.tracker.getEventCountSinceLastHeartbeat();
|
|
92
|
-
// Reset event count
|
|
93
|
-
this.tracker.resetEventCountSinceLastHeartbeat();
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
// Track the heartbeat event
|
|
97
|
-
this.tracker.trackSystemEvent('_grain_heartbeat', properties);
|
|
98
|
-
this.lastHeartbeatTime = now;
|
|
99
|
-
if (this.config.debug) {
|
|
100
|
-
console.log('[Heartbeat] Sent heartbeat:', properties);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Destroy the heartbeat manager
|
|
105
|
-
*/
|
|
106
|
-
destroy() {
|
|
107
|
-
if (this.isDestroyed)
|
|
108
|
-
return;
|
|
109
|
-
if (this.heartbeatTimer !== null) {
|
|
110
|
-
clearTimeout(this.heartbeatTimer);
|
|
111
|
-
this.heartbeatTimer = null;
|
|
112
|
-
}
|
|
113
|
-
this.isDestroyed = true;
|
|
114
|
-
if (this.config.debug) {
|
|
115
|
-
console.log('[Heartbeat] Destroyed');
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
exports.HeartbeatManager = HeartbeatManager;
|
package/dist/react/heartbeat.mjs
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
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.hasSentPageLoadHeartbeat = false;
|
|
10
|
-
this.tracker = tracker;
|
|
11
|
-
this.activityDetector = activityDetector;
|
|
12
|
-
this.config = config;
|
|
13
|
-
this.lastHeartbeatTime = Date.now();
|
|
14
|
-
this.currentInterval = config.activeInterval;
|
|
15
|
-
// Send initial heartbeat when page loads (if allowed by consent)
|
|
16
|
-
this.sendPageLoadHeartbeat();
|
|
17
|
-
// Start periodic heartbeat tracking
|
|
18
|
-
this.scheduleNextHeartbeat();
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Send initial heartbeat when page loads
|
|
22
|
-
*/
|
|
23
|
-
sendPageLoadHeartbeat() {
|
|
24
|
-
if (this.isDestroyed || this.hasSentPageLoadHeartbeat)
|
|
25
|
-
return;
|
|
26
|
-
// Wait for page to be fully loaded
|
|
27
|
-
if (typeof window !== 'undefined' && document.readyState !== 'complete') {
|
|
28
|
-
const handleLoad = () => {
|
|
29
|
-
this.sendHeartbeat('page_load');
|
|
30
|
-
this.hasSentPageLoadHeartbeat = true;
|
|
31
|
-
window.removeEventListener('load', handleLoad);
|
|
32
|
-
};
|
|
33
|
-
window.addEventListener('load', handleLoad);
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
// Page is already loaded or we're in a non-browser environment
|
|
37
|
-
this.sendHeartbeat('page_load');
|
|
38
|
-
this.hasSentPageLoadHeartbeat = true;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Schedule the next heartbeat based on current activity
|
|
43
|
-
*/
|
|
44
|
-
scheduleNextHeartbeat() {
|
|
45
|
-
if (this.isDestroyed)
|
|
46
|
-
return;
|
|
47
|
-
// Clear existing timer
|
|
48
|
-
if (this.heartbeatTimer !== null) {
|
|
49
|
-
clearTimeout(this.heartbeatTimer);
|
|
50
|
-
}
|
|
51
|
-
// Determine interval based on activity
|
|
52
|
-
const isActive = this.activityDetector.isActive(60000); // 1 minute threshold
|
|
53
|
-
this.currentInterval = isActive ? this.config.activeInterval : this.config.inactiveInterval;
|
|
54
|
-
// Schedule next heartbeat
|
|
55
|
-
this.heartbeatTimer = window.setTimeout(() => {
|
|
56
|
-
this.sendHeartbeat('periodic');
|
|
57
|
-
this.scheduleNextHeartbeat();
|
|
58
|
-
}, this.currentInterval);
|
|
59
|
-
if (this.config.debug) {
|
|
60
|
-
console.log(`[Heartbeat] Scheduled next heartbeat in ${this.currentInterval / 1000}s (${isActive ? 'active' : 'inactive'})`);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Send heartbeat event
|
|
65
|
-
*/
|
|
66
|
-
sendHeartbeat(heartbeatType = 'periodic') {
|
|
67
|
-
if (this.isDestroyed)
|
|
68
|
-
return;
|
|
69
|
-
const now = Date.now();
|
|
70
|
-
const isActive = this.activityDetector.isActive(60000); // 1 minute threshold
|
|
71
|
-
const hasConsent = this.tracker.hasConsent('analytics');
|
|
72
|
-
// Base properties (always included)
|
|
73
|
-
const properties = {
|
|
74
|
-
type: 'heartbeat',
|
|
75
|
-
heartbeat_type: heartbeatType,
|
|
76
|
-
status: isActive ? 'active' : 'inactive',
|
|
77
|
-
timestamp: now,
|
|
78
|
-
};
|
|
79
|
-
// Enhanced properties when consent is granted
|
|
80
|
-
if (hasConsent) {
|
|
81
|
-
const page = this.tracker.getCurrentPage();
|
|
82
|
-
if (page) {
|
|
83
|
-
properties.page = page;
|
|
84
|
-
}
|
|
85
|
-
// Only include duration and event count for periodic heartbeats
|
|
86
|
-
if (heartbeatType === 'periodic') {
|
|
87
|
-
properties.duration = now - this.lastHeartbeatTime;
|
|
88
|
-
properties.event_count = this.tracker.getEventCountSinceLastHeartbeat();
|
|
89
|
-
// Reset event count
|
|
90
|
-
this.tracker.resetEventCountSinceLastHeartbeat();
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
// Track the heartbeat event
|
|
94
|
-
this.tracker.trackSystemEvent('_grain_heartbeat', properties);
|
|
95
|
-
this.lastHeartbeatTime = now;
|
|
96
|
-
if (this.config.debug) {
|
|
97
|
-
console.log('[Heartbeat] Sent heartbeat:', properties);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Destroy the heartbeat manager
|
|
102
|
-
*/
|
|
103
|
-
destroy() {
|
|
104
|
-
if (this.isDestroyed)
|
|
105
|
-
return;
|
|
106
|
-
if (this.heartbeatTimer !== null) {
|
|
107
|
-
clearTimeout(this.heartbeatTimer);
|
|
108
|
-
this.heartbeatTimer = null;
|
|
109
|
-
}
|
|
110
|
-
this.isDestroyed = true;
|
|
111
|
-
if (this.config.debug) {
|
|
112
|
-
console.log('[Heartbeat] Destroyed');
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|