@grainql/analytics-web 2.8.0 → 2.9.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/README.md +36 -3
- package/dist/cjs/consent.d.ts +38 -7
- package/dist/cjs/consent.d.ts.map +1 -1
- package/dist/cjs/consent.js +82 -23
- package/dist/cjs/consent.js.map +1 -1
- package/dist/cjs/id-manager.d.ts +66 -0
- package/dist/cjs/id-manager.d.ts.map +1 -0
- package/dist/cjs/id-manager.js +212 -0
- package/dist/cjs/id-manager.js.map +1 -0
- package/dist/cjs/index.d.ts +12 -8
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/page-tracking.d.ts +6 -0
- package/dist/cjs/page-tracking.d.ts.map +1 -1
- package/dist/cjs/page-tracking.js +23 -2
- package/dist/cjs/page-tracking.js.map +1 -1
- package/dist/cjs/react/hooks/useConsent.d.ts +18 -2
- package/dist/cjs/react/hooks/useConsent.d.ts.map +1 -1
- package/dist/cjs/react/hooks/useConsent.js +52 -1
- package/dist/cjs/react/hooks/useConsent.js.map +1 -1
- package/dist/consent.d.ts +38 -7
- package/dist/consent.d.ts.map +1 -1
- package/dist/consent.js +82 -23
- package/dist/esm/consent.d.ts +38 -7
- package/dist/esm/consent.d.ts.map +1 -1
- package/dist/esm/consent.js +82 -23
- package/dist/esm/consent.js.map +1 -1
- package/dist/esm/id-manager.d.ts +66 -0
- package/dist/esm/id-manager.d.ts.map +1 -0
- package/dist/esm/id-manager.js +208 -0
- package/dist/esm/id-manager.js.map +1 -0
- package/dist/esm/index.d.ts +12 -8
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/page-tracking.d.ts +6 -0
- package/dist/esm/page-tracking.d.ts.map +1 -1
- package/dist/esm/page-tracking.js +23 -2
- package/dist/esm/page-tracking.js.map +1 -1
- package/dist/esm/react/hooks/useConsent.d.ts +18 -2
- package/dist/esm/react/hooks/useConsent.d.ts.map +1 -1
- package/dist/esm/react/hooks/useConsent.js +49 -1
- package/dist/esm/react/hooks/useConsent.js.map +1 -1
- package/dist/id-manager.d.ts +66 -0
- package/dist/id-manager.d.ts.map +1 -0
- package/dist/id-manager.js +212 -0
- package/dist/index.d.ts +12 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.global.dev.js +310 -81
- package/dist/index.global.dev.js.map +4 -4
- package/dist/index.global.js +8 -8
- package/dist/index.global.js.map +4 -4
- package/dist/index.js +72 -44
- package/dist/index.mjs +73 -45
- package/dist/page-tracking.d.ts +6 -0
- package/dist/page-tracking.d.ts.map +1 -1
- package/dist/page-tracking.js +23 -2
- package/dist/react/hooks/useConsent.d.ts +18 -2
- package/dist/react/hooks/useConsent.d.ts.map +1 -1
- package/dist/react/hooks/useConsent.js +52 -1
- package/dist/react/hooks/useConsent.mjs +49 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
# Grain Analytics Web SDK
|
|
2
2
|
|
|
3
|
-
A lightweight, dependency-free TypeScript SDK for analytics and remote configuration management.
|
|
3
|
+
A lightweight, dependency-free TypeScript SDK for privacy-first analytics and remote configuration management.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@grainql/analytics-web)
|
|
6
6
|
[](https://bundlephobia.com/package/@grainql/analytics-web)
|
|
7
7
|
|
|
8
|
+
## 🆕 What's New in v2.0
|
|
9
|
+
|
|
10
|
+
**🔒 Privacy-First by Default:**
|
|
11
|
+
- ✅ **Cookie-less** - No tracking cookies, daily rotating IDs
|
|
12
|
+
- ✅ **GDPR Compliant** - Cookie-less mode requires no consent
|
|
13
|
+
- ✅ **No IP Storage** - GeoIP data only (country, region, city)
|
|
14
|
+
- ✅ **Query Params Stripped** - Privacy-first URL tracking
|
|
15
|
+
- ✅ **Three Consent Modes** - Choose your privacy level
|
|
16
|
+
|
|
17
|
+
**🚨 Breaking Changes:** This is a major version with breaking changes. See [BREAKING_CHANGES.md](./BREAKING_CHANGES.md) and [MIGRATION_GUIDE_V2.md](./MIGRATION_GUIDE_V2.md).
|
|
18
|
+
|
|
8
19
|
## Features
|
|
9
20
|
|
|
10
|
-
-
|
|
21
|
+
- 🔒 **Privacy-First** - Cookie-less by default, GDPR compliant
|
|
22
|
+
- 🚀 **Zero dependencies** - ~7KB gzipped
|
|
11
23
|
- 📦 **Automatic batching** - Efficient event delivery
|
|
12
24
|
- 🔄 **Retry logic** - Reliable with exponential backoff
|
|
13
25
|
- 🎯 **TypeScript first** - Full type safety
|
|
@@ -46,8 +58,10 @@ npm install @grainql/analytics-web
|
|
|
46
58
|
```typescript
|
|
47
59
|
import { createGrainAnalytics } from '@grainql/analytics-web';
|
|
48
60
|
|
|
61
|
+
// Cookie-less by default (no consent needed)
|
|
49
62
|
const grain = createGrainAnalytics({
|
|
50
|
-
tenantId: 'your-tenant-id'
|
|
63
|
+
tenantId: 'your-tenant-id',
|
|
64
|
+
consentMode: 'cookieless', // Default: daily rotating IDs
|
|
51
65
|
});
|
|
52
66
|
|
|
53
67
|
// Track events
|
|
@@ -57,6 +71,25 @@ grain.track('page_viewed', { page: '/home' });
|
|
|
57
71
|
const heroText = grain.getConfig('hero_text');
|
|
58
72
|
```
|
|
59
73
|
|
|
74
|
+
**For GDPR Strict (with consent management):**
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
const grain = createGrainAnalytics({
|
|
78
|
+
tenantId: 'your-tenant-id',
|
|
79
|
+
consentMode: 'gdpr-strict', // Requires consent for permanent IDs
|
|
80
|
+
waitForConsent: true, // Queue events until consent granted
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Show consent banner
|
|
84
|
+
if (!grain.hasConsent()) {
|
|
85
|
+
// Show your consent UI
|
|
86
|
+
showConsentBanner({
|
|
87
|
+
onAccept: () => grain.grantConsent(['analytics']),
|
|
88
|
+
onReject: () => grain.revokeConsent(),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
60
93
|
### React
|
|
61
94
|
|
|
62
95
|
```typescript
|
package/dist/cjs/consent.d.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Consent management for Grain Analytics
|
|
3
|
-
*
|
|
2
|
+
* Consent management for Grain Analytics v2.0
|
|
3
|
+
* Privacy-first, cookie-less by default
|
|
4
|
+
*
|
|
5
|
+
* Consent Modes:
|
|
6
|
+
* - cookieless: Default mode, daily rotating IDs, no consent needed
|
|
7
|
+
* - gdpr-strict: Requires explicit consent, falls back to cookieless
|
|
8
|
+
* - gdpr-opt-out: Permanent IDs by default, cookieless on opt-out
|
|
4
9
|
*/
|
|
5
|
-
export type ConsentMode = '
|
|
10
|
+
export type ConsentMode = 'cookieless' | 'gdpr-strict' | 'gdpr-opt-out';
|
|
6
11
|
export interface ConsentState {
|
|
7
12
|
granted: boolean;
|
|
8
13
|
categories: string[];
|
|
@@ -13,6 +18,7 @@ export declare const DEFAULT_CONSENT_CATEGORIES: string[];
|
|
|
13
18
|
export declare const CONSENT_VERSION = "1.0.0";
|
|
14
19
|
/**
|
|
15
20
|
* Consent manager for handling user consent state
|
|
21
|
+
* v2.0: Cookie-less by default, privacy-first approach
|
|
16
22
|
*/
|
|
17
23
|
export declare class ConsentManager {
|
|
18
24
|
private consentState;
|
|
@@ -23,9 +29,8 @@ export declare class ConsentManager {
|
|
|
23
29
|
/**
|
|
24
30
|
* Load consent state from localStorage
|
|
25
31
|
*
|
|
26
|
-
* GDPR Compliance:
|
|
27
|
-
*
|
|
28
|
-
* The consent preference itself is not tracking data.
|
|
32
|
+
* GDPR Compliance: localStorage only used for storing consent preferences
|
|
33
|
+
* (not for tracking), which is a legitimate interest for compliance.
|
|
29
34
|
*/
|
|
30
35
|
private loadConsentState;
|
|
31
36
|
/**
|
|
@@ -45,11 +50,28 @@ export declare class ConsentManager {
|
|
|
45
50
|
*/
|
|
46
51
|
getConsentState(): ConsentState | null;
|
|
47
52
|
/**
|
|
48
|
-
* Check if user has granted consent
|
|
53
|
+
* Check if user has granted consent for permanent IDs
|
|
49
54
|
*/
|
|
50
55
|
hasConsent(category?: string): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Check if permanent IDs are allowed
|
|
58
|
+
*/
|
|
59
|
+
shouldUsePermanentId(): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Check if we should strip query parameters from URLs
|
|
62
|
+
* Query params stripped unless:
|
|
63
|
+
* - Mode is gdpr-opt-out, OR
|
|
64
|
+
* - Mode is gdpr-strict AND consent given
|
|
65
|
+
*/
|
|
66
|
+
shouldStripQueryParams(): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Check if we can track events (always true in v2.0)
|
|
69
|
+
* Even cookieless mode allows basic analytics with daily IDs
|
|
70
|
+
*/
|
|
71
|
+
canTrack(): boolean;
|
|
51
72
|
/**
|
|
52
73
|
* Check if we should wait for consent before tracking
|
|
74
|
+
* Only relevant for GDPR Strict mode
|
|
53
75
|
*/
|
|
54
76
|
shouldWaitForConsent(): boolean;
|
|
55
77
|
/**
|
|
@@ -68,5 +90,14 @@ export declare class ConsentManager {
|
|
|
68
90
|
* Clear all consent data
|
|
69
91
|
*/
|
|
70
92
|
clearConsent(): void;
|
|
93
|
+
/**
|
|
94
|
+
* Get current consent mode
|
|
95
|
+
*/
|
|
96
|
+
getConsentMode(): ConsentMode;
|
|
97
|
+
/**
|
|
98
|
+
* Get ID mode based on consent state
|
|
99
|
+
* Returns 'cookieless' or 'permanent'
|
|
100
|
+
*/
|
|
101
|
+
getIdMode(): 'cookieless' | 'permanent';
|
|
71
102
|
}
|
|
72
103
|
//# sourceMappingURL=consent.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consent.d.ts","sourceRoot":"","sources":["../../src/consent.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"consent.d.ts","sourceRoot":"","sources":["../../src/consent.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,aAAa,GAAG,cAAc,CAAC;AAExE,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;;;GAGG;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,WAA0B;IAMrE;;;;;OAKG;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;IAiCtC;;OAEG;IACH,oBAAoB,IAAI,OAAO;IAI/B;;;;;OAKG;IACH,sBAAsB,IAAI,OAAO;IAgBjC;;;OAGG;IACH,QAAQ,IAAI,OAAO;IAInB;;;OAGG;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;IAWpB;;OAEG;IACH,cAAc,IAAI,WAAW;IAI7B;;;OAGG;IACH,SAAS,IAAI,YAAY,GAAG,WAAW;CAGxC"}
|
package/dist/cjs/consent.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Consent management for Grain Analytics
|
|
4
|
-
*
|
|
3
|
+
* Consent management for Grain Analytics v2.0
|
|
4
|
+
* Privacy-first, cookie-less by default
|
|
5
|
+
*
|
|
6
|
+
* Consent Modes:
|
|
7
|
+
* - cookieless: Default mode, daily rotating IDs, no consent needed
|
|
8
|
+
* - gdpr-strict: Requires explicit consent, falls back to cookieless
|
|
9
|
+
* - gdpr-opt-out: Permanent IDs by default, cookieless on opt-out
|
|
5
10
|
*/
|
|
6
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
12
|
exports.ConsentManager = exports.CONSENT_VERSION = exports.DEFAULT_CONSENT_CATEGORIES = void 0;
|
|
@@ -9,9 +14,10 @@ exports.DEFAULT_CONSENT_CATEGORIES = ['necessary', 'analytics', 'functional'];
|
|
|
9
14
|
exports.CONSENT_VERSION = '1.0.0';
|
|
10
15
|
/**
|
|
11
16
|
* Consent manager for handling user consent state
|
|
17
|
+
* v2.0: Cookie-less by default, privacy-first approach
|
|
12
18
|
*/
|
|
13
19
|
class ConsentManager {
|
|
14
|
-
constructor(tenantId, consentMode = '
|
|
20
|
+
constructor(tenantId, consentMode = 'cookieless') {
|
|
15
21
|
this.consentState = null;
|
|
16
22
|
this.listeners = [];
|
|
17
23
|
this.consentMode = consentMode;
|
|
@@ -21,9 +27,8 @@ class ConsentManager {
|
|
|
21
27
|
/**
|
|
22
28
|
* Load consent state from localStorage
|
|
23
29
|
*
|
|
24
|
-
* GDPR Compliance:
|
|
25
|
-
*
|
|
26
|
-
* The consent preference itself is not tracking data.
|
|
30
|
+
* GDPR Compliance: localStorage only used for storing consent preferences
|
|
31
|
+
* (not for tracking), which is a legitimate interest for compliance.
|
|
27
32
|
*/
|
|
28
33
|
loadConsentState() {
|
|
29
34
|
if (typeof window === 'undefined')
|
|
@@ -37,8 +42,8 @@ class ConsentManager {
|
|
|
37
42
|
timestamp: new Date(parsed.timestamp),
|
|
38
43
|
};
|
|
39
44
|
}
|
|
40
|
-
else if (this.consentMode === 'opt-out'
|
|
41
|
-
// Auto-grant consent for opt-out
|
|
45
|
+
else if (this.consentMode === 'gdpr-opt-out') {
|
|
46
|
+
// Auto-grant consent for opt-out mode (user hasn't opted out yet)
|
|
42
47
|
this.consentState = {
|
|
43
48
|
granted: true,
|
|
44
49
|
categories: exports.DEFAULT_CONSENT_CATEGORIES,
|
|
@@ -47,10 +52,10 @@ class ConsentManager {
|
|
|
47
52
|
};
|
|
48
53
|
this.saveConsentState();
|
|
49
54
|
}
|
|
50
|
-
// Note:
|
|
55
|
+
// Note: cookieless and gdpr-strict modes without stored consent → no permanent tracking
|
|
51
56
|
}
|
|
52
57
|
catch (error) {
|
|
53
|
-
// Silent failure -
|
|
58
|
+
// Silent failure - will use cookieless mode by default
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
61
|
/**
|
|
@@ -120,32 +125,73 @@ class ConsentManager {
|
|
|
120
125
|
return this.consentState ? { ...this.consentState } : null;
|
|
121
126
|
}
|
|
122
127
|
/**
|
|
123
|
-
* Check if user has granted consent
|
|
128
|
+
* Check if user has granted consent for permanent IDs
|
|
124
129
|
*/
|
|
125
130
|
hasConsent(category) {
|
|
126
|
-
//
|
|
127
|
-
if (this.consentMode === '
|
|
128
|
-
return
|
|
131
|
+
// Cookie-less mode: no consent needed (no permanent tracking)
|
|
132
|
+
if (this.consentMode === 'cookieless') {
|
|
133
|
+
return false; // No permanent IDs
|
|
129
134
|
}
|
|
130
|
-
//
|
|
131
|
-
if (this.consentMode === '
|
|
132
|
-
|
|
135
|
+
// GDPR Strict: requires explicit consent
|
|
136
|
+
if (this.consentMode === 'gdpr-strict') {
|
|
137
|
+
if (!this.consentState?.granted) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
133
140
|
}
|
|
134
|
-
//
|
|
135
|
-
if (
|
|
136
|
-
|
|
141
|
+
// GDPR Opt-out: consent by default unless explicitly revoked
|
|
142
|
+
if (this.consentMode === 'gdpr-opt-out') {
|
|
143
|
+
// If no state, assume consent (opt-out model)
|
|
144
|
+
if (!this.consentState) {
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
// Check if consent was revoked
|
|
148
|
+
if (!this.consentState.granted) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
137
151
|
}
|
|
138
152
|
// Check specific category if provided
|
|
139
|
-
if (category) {
|
|
153
|
+
if (category && this.consentState) {
|
|
140
154
|
return this.consentState.categories.includes(category);
|
|
141
155
|
}
|
|
142
|
-
return
|
|
156
|
+
return this.consentState?.granted ?? (this.consentMode === 'gdpr-opt-out');
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Check if permanent IDs are allowed
|
|
160
|
+
*/
|
|
161
|
+
shouldUsePermanentId() {
|
|
162
|
+
return this.hasConsent();
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Check if we should strip query parameters from URLs
|
|
166
|
+
* Query params stripped unless:
|
|
167
|
+
* - Mode is gdpr-opt-out, OR
|
|
168
|
+
* - Mode is gdpr-strict AND consent given
|
|
169
|
+
*/
|
|
170
|
+
shouldStripQueryParams() {
|
|
171
|
+
if (this.consentMode === 'cookieless') {
|
|
172
|
+
return true; // Always strip in cookieless mode
|
|
173
|
+
}
|
|
174
|
+
if (this.consentMode === 'gdpr-strict') {
|
|
175
|
+
return !this.hasConsent(); // Strip unless consented
|
|
176
|
+
}
|
|
177
|
+
if (this.consentMode === 'gdpr-opt-out') {
|
|
178
|
+
return false; // Don't strip in opt-out mode
|
|
179
|
+
}
|
|
180
|
+
return true; // Default: strip
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Check if we can track events (always true in v2.0)
|
|
184
|
+
* Even cookieless mode allows basic analytics with daily IDs
|
|
185
|
+
*/
|
|
186
|
+
canTrack() {
|
|
187
|
+
return true; // All modes allow some form of tracking
|
|
143
188
|
}
|
|
144
189
|
/**
|
|
145
190
|
* Check if we should wait for consent before tracking
|
|
191
|
+
* Only relevant for GDPR Strict mode
|
|
146
192
|
*/
|
|
147
193
|
shouldWaitForConsent() {
|
|
148
|
-
return this.consentMode === '
|
|
194
|
+
return this.consentMode === 'gdpr-strict' && !this.consentState?.granted;
|
|
149
195
|
}
|
|
150
196
|
/**
|
|
151
197
|
* Add consent change listener
|
|
@@ -191,6 +237,19 @@ class ConsentManager {
|
|
|
191
237
|
// Silent failure - consent state may not be fully cleared
|
|
192
238
|
}
|
|
193
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* Get current consent mode
|
|
242
|
+
*/
|
|
243
|
+
getConsentMode() {
|
|
244
|
+
return this.consentMode;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Get ID mode based on consent state
|
|
248
|
+
* Returns 'cookieless' or 'permanent'
|
|
249
|
+
*/
|
|
250
|
+
getIdMode() {
|
|
251
|
+
return this.shouldUsePermanentId() ? 'permanent' : 'cookieless';
|
|
252
|
+
}
|
|
194
253
|
}
|
|
195
254
|
exports.ConsentManager = ConsentManager;
|
|
196
255
|
//# sourceMappingURL=consent.js.map
|
package/dist/cjs/consent.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consent.js","sourceRoot":"","sources":["../../src/consent.ts"],"names":[],"mappings":";AAAA;;;
|
|
1
|
+
{"version":3,"file":"consent.js","sourceRoot":"","sources":["../../src/consent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ID Manager for Grain Analytics
|
|
3
|
+
* Generates and manages user IDs based on consent mode
|
|
4
|
+
*
|
|
5
|
+
* Privacy-first implementation:
|
|
6
|
+
* - Cookie-less mode: Daily rotating IDs (no persistence)
|
|
7
|
+
* - GDPR Strict: Permanent IDs only with consent
|
|
8
|
+
* - GDPR Opt-out: Permanent IDs by default
|
|
9
|
+
*/
|
|
10
|
+
export type IdMode = 'cookieless' | 'permanent';
|
|
11
|
+
export interface IdConfig {
|
|
12
|
+
mode: IdMode;
|
|
13
|
+
tenantId: string;
|
|
14
|
+
useLocalStorage?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* ID Manager class
|
|
18
|
+
* Handles both daily rotating IDs and permanent IDs
|
|
19
|
+
*/
|
|
20
|
+
export declare class IdManager {
|
|
21
|
+
private config;
|
|
22
|
+
private cachedDailyId;
|
|
23
|
+
private dailyIdDate;
|
|
24
|
+
private permanentId;
|
|
25
|
+
constructor(config: IdConfig);
|
|
26
|
+
/**
|
|
27
|
+
* Generate a daily rotating ID
|
|
28
|
+
* Rotates at midnight in user's local timezone
|
|
29
|
+
* Provides same-day continuity without persistent tracking
|
|
30
|
+
*/
|
|
31
|
+
generateDailyRotatingId(): string;
|
|
32
|
+
/**
|
|
33
|
+
* Generate or retrieve permanent user ID
|
|
34
|
+
* Only used when consent is given
|
|
35
|
+
*/
|
|
36
|
+
generatePermanentId(): string;
|
|
37
|
+
/**
|
|
38
|
+
* Get the current user ID based on mode
|
|
39
|
+
*/
|
|
40
|
+
getCurrentUserId(): string;
|
|
41
|
+
/**
|
|
42
|
+
* Switch ID mode (e.g., when consent is granted/revoked)
|
|
43
|
+
*/
|
|
44
|
+
setMode(mode: IdMode): void;
|
|
45
|
+
/**
|
|
46
|
+
* Load permanent ID from localStorage
|
|
47
|
+
*/
|
|
48
|
+
private loadPermanentId;
|
|
49
|
+
/**
|
|
50
|
+
* Save permanent ID to localStorage
|
|
51
|
+
*/
|
|
52
|
+
private savePermanentId;
|
|
53
|
+
/**
|
|
54
|
+
* Clear permanent ID from localStorage
|
|
55
|
+
*/
|
|
56
|
+
private clearPermanentId;
|
|
57
|
+
/**
|
|
58
|
+
* Get info about current ID for debugging
|
|
59
|
+
*/
|
|
60
|
+
getIdInfo(): {
|
|
61
|
+
mode: IdMode;
|
|
62
|
+
id: string;
|
|
63
|
+
isDailyRotating: boolean;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=id-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id-manager.d.ts","sourceRoot":"","sources":["../../src/id-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,MAAM,GAAG,YAAY,GAAG,WAAW,CAAC;AAEhD,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AA2DD;;;GAGG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,WAAW,CAAuB;gBAE9B,MAAM,EAAE,QAAQ;IAS5B;;;;OAIG;IACH,uBAAuB,IAAI,MAAM;IAoBjC;;;OAGG;IACH,mBAAmB,IAAI,MAAM;IAyB7B;;OAEG;IACH,gBAAgB,IAAI,MAAM;IAQ1B;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAkB3B;;OAEG;IACH,OAAO,CAAC,eAAe;IAiBvB;;OAEG;IACH,OAAO,CAAC,eAAe;IAWvB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAWxB;;OAEG;IACH,SAAS,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,OAAO,CAAA;KAAE;CAQpE"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ID Manager for Grain Analytics
|
|
4
|
+
* Generates and manages user IDs based on consent mode
|
|
5
|
+
*
|
|
6
|
+
* Privacy-first implementation:
|
|
7
|
+
* - Cookie-less mode: Daily rotating IDs (no persistence)
|
|
8
|
+
* - GDPR Strict: Permanent IDs only with consent
|
|
9
|
+
* - GDPR Opt-out: Permanent IDs by default
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.IdManager = void 0;
|
|
13
|
+
/**
|
|
14
|
+
* Generate a simple hash from a string
|
|
15
|
+
*/
|
|
16
|
+
function simpleHash(str) {
|
|
17
|
+
let hash = 0;
|
|
18
|
+
for (let i = 0; i < str.length; i++) {
|
|
19
|
+
const char = str.charCodeAt(i);
|
|
20
|
+
hash = ((hash << 5) - hash) + char;
|
|
21
|
+
hash = hash & hash; // Convert to 32-bit integer
|
|
22
|
+
}
|
|
23
|
+
return Math.abs(hash).toString(36);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Generate a UUID v4
|
|
27
|
+
*/
|
|
28
|
+
function generateUUID() {
|
|
29
|
+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
30
|
+
return crypto.randomUUID();
|
|
31
|
+
}
|
|
32
|
+
// Fallback for older browsers
|
|
33
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
34
|
+
const r = Math.random() * 16 | 0;
|
|
35
|
+
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
36
|
+
return v.toString(16);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Get minimal browser fingerprint for daily ID generation
|
|
41
|
+
* NOT for tracking, just for same-day continuity
|
|
42
|
+
*/
|
|
43
|
+
function getBrowserFingerprint() {
|
|
44
|
+
if (typeof window === 'undefined')
|
|
45
|
+
return 'server';
|
|
46
|
+
const components = [
|
|
47
|
+
screen.width?.toString() || '',
|
|
48
|
+
screen.height?.toString() || '',
|
|
49
|
+
navigator.language || '',
|
|
50
|
+
Intl.DateTimeFormat().resolvedOptions().timeZone || ''
|
|
51
|
+
];
|
|
52
|
+
return simpleHash(components.join('|'));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get current date string in local timezone (YYYY-MM-DD)
|
|
56
|
+
*/
|
|
57
|
+
function getLocalDateString() {
|
|
58
|
+
const now = new Date();
|
|
59
|
+
const year = now.getFullYear();
|
|
60
|
+
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
61
|
+
const day = String(now.getDate()).padStart(2, '0');
|
|
62
|
+
return `${year}-${month}-${day}`;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* ID Manager class
|
|
66
|
+
* Handles both daily rotating IDs and permanent IDs
|
|
67
|
+
*/
|
|
68
|
+
class IdManager {
|
|
69
|
+
constructor(config) {
|
|
70
|
+
this.cachedDailyId = null;
|
|
71
|
+
this.dailyIdDate = null;
|
|
72
|
+
this.permanentId = null;
|
|
73
|
+
this.config = config;
|
|
74
|
+
// Load permanent ID from localStorage if in permanent mode
|
|
75
|
+
if (config.mode === 'permanent' && config.useLocalStorage) {
|
|
76
|
+
this.loadPermanentId();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Generate a daily rotating ID
|
|
81
|
+
* Rotates at midnight in user's local timezone
|
|
82
|
+
* Provides same-day continuity without persistent tracking
|
|
83
|
+
*/
|
|
84
|
+
generateDailyRotatingId() {
|
|
85
|
+
const currentDate = getLocalDateString();
|
|
86
|
+
// Return cached ID if still the same day
|
|
87
|
+
if (this.cachedDailyId && this.dailyIdDate === currentDate) {
|
|
88
|
+
return this.cachedDailyId;
|
|
89
|
+
}
|
|
90
|
+
// Generate new daily ID
|
|
91
|
+
const fingerprint = getBrowserFingerprint();
|
|
92
|
+
const seed = `${this.config.tenantId}|${currentDate}|${fingerprint}`;
|
|
93
|
+
const dailyId = `daily_${simpleHash(seed)}_${simpleHash(Date.now().toString())}`;
|
|
94
|
+
// Cache for same-day requests
|
|
95
|
+
this.cachedDailyId = dailyId;
|
|
96
|
+
this.dailyIdDate = currentDate;
|
|
97
|
+
return dailyId;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Generate or retrieve permanent user ID
|
|
101
|
+
* Only used when consent is given
|
|
102
|
+
*/
|
|
103
|
+
generatePermanentId() {
|
|
104
|
+
if (this.permanentId) {
|
|
105
|
+
return this.permanentId;
|
|
106
|
+
}
|
|
107
|
+
// Try to load from localStorage
|
|
108
|
+
if (this.config.useLocalStorage) {
|
|
109
|
+
const stored = this.loadPermanentId();
|
|
110
|
+
if (stored) {
|
|
111
|
+
return stored;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Generate new permanent ID
|
|
115
|
+
const newId = generateUUID();
|
|
116
|
+
this.permanentId = newId;
|
|
117
|
+
// Store in localStorage if enabled
|
|
118
|
+
if (this.config.useLocalStorage) {
|
|
119
|
+
this.savePermanentId(newId);
|
|
120
|
+
}
|
|
121
|
+
return newId;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get the current user ID based on mode
|
|
125
|
+
*/
|
|
126
|
+
getCurrentUserId() {
|
|
127
|
+
if (this.config.mode === 'cookieless') {
|
|
128
|
+
return this.generateDailyRotatingId();
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
return this.generatePermanentId();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Switch ID mode (e.g., when consent is granted/revoked)
|
|
136
|
+
*/
|
|
137
|
+
setMode(mode) {
|
|
138
|
+
this.config.mode = mode;
|
|
139
|
+
// Clear cached daily ID when switching to permanent
|
|
140
|
+
if (mode === 'permanent') {
|
|
141
|
+
this.cachedDailyId = null;
|
|
142
|
+
this.dailyIdDate = null;
|
|
143
|
+
}
|
|
144
|
+
// Clear permanent ID when switching to cookieless
|
|
145
|
+
if (mode === 'cookieless') {
|
|
146
|
+
this.permanentId = null;
|
|
147
|
+
if (this.config.useLocalStorage) {
|
|
148
|
+
this.clearPermanentId();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Load permanent ID from localStorage
|
|
154
|
+
*/
|
|
155
|
+
loadPermanentId() {
|
|
156
|
+
if (typeof window === 'undefined')
|
|
157
|
+
return null;
|
|
158
|
+
try {
|
|
159
|
+
const storageKey = `grain_anonymous_user_id_${this.config.tenantId}`;
|
|
160
|
+
const stored = localStorage.getItem(storageKey);
|
|
161
|
+
if (stored) {
|
|
162
|
+
this.permanentId = stored;
|
|
163
|
+
return stored;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
// Silent failure - localStorage might be disabled
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Save permanent ID to localStorage
|
|
173
|
+
*/
|
|
174
|
+
savePermanentId(id) {
|
|
175
|
+
if (typeof window === 'undefined')
|
|
176
|
+
return;
|
|
177
|
+
try {
|
|
178
|
+
const storageKey = `grain_anonymous_user_id_${this.config.tenantId}`;
|
|
179
|
+
localStorage.setItem(storageKey, id);
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
// Silent failure - localStorage might be disabled
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Clear permanent ID from localStorage
|
|
187
|
+
*/
|
|
188
|
+
clearPermanentId() {
|
|
189
|
+
if (typeof window === 'undefined')
|
|
190
|
+
return;
|
|
191
|
+
try {
|
|
192
|
+
const storageKey = `grain_anonymous_user_id_${this.config.tenantId}`;
|
|
193
|
+
localStorage.removeItem(storageKey);
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
// Silent failure
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Get info about current ID for debugging
|
|
201
|
+
*/
|
|
202
|
+
getIdInfo() {
|
|
203
|
+
const id = this.getCurrentUserId();
|
|
204
|
+
return {
|
|
205
|
+
mode: this.config.mode,
|
|
206
|
+
id: id,
|
|
207
|
+
isDailyRotating: id.startsWith('daily_')
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
exports.IdManager = IdManager;
|
|
212
|
+
//# sourceMappingURL=id-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id-manager.js","sourceRoot":"","sources":["../../src/id-manager.ts"],"names":[],"mappings":"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"}
|