@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.
Files changed (61) hide show
  1. package/README.md +36 -3
  2. package/dist/cjs/consent.d.ts +38 -7
  3. package/dist/cjs/consent.d.ts.map +1 -1
  4. package/dist/cjs/consent.js +82 -23
  5. package/dist/cjs/consent.js.map +1 -1
  6. package/dist/cjs/id-manager.d.ts +66 -0
  7. package/dist/cjs/id-manager.d.ts.map +1 -0
  8. package/dist/cjs/id-manager.js +212 -0
  9. package/dist/cjs/id-manager.js.map +1 -0
  10. package/dist/cjs/index.d.ts +12 -8
  11. package/dist/cjs/index.d.ts.map +1 -1
  12. package/dist/cjs/index.js.map +1 -1
  13. package/dist/cjs/page-tracking.d.ts +6 -0
  14. package/dist/cjs/page-tracking.d.ts.map +1 -1
  15. package/dist/cjs/page-tracking.js +23 -2
  16. package/dist/cjs/page-tracking.js.map +1 -1
  17. package/dist/cjs/react/hooks/useConsent.d.ts +18 -2
  18. package/dist/cjs/react/hooks/useConsent.d.ts.map +1 -1
  19. package/dist/cjs/react/hooks/useConsent.js +52 -1
  20. package/dist/cjs/react/hooks/useConsent.js.map +1 -1
  21. package/dist/consent.d.ts +38 -7
  22. package/dist/consent.d.ts.map +1 -1
  23. package/dist/consent.js +82 -23
  24. package/dist/esm/consent.d.ts +38 -7
  25. package/dist/esm/consent.d.ts.map +1 -1
  26. package/dist/esm/consent.js +82 -23
  27. package/dist/esm/consent.js.map +1 -1
  28. package/dist/esm/id-manager.d.ts +66 -0
  29. package/dist/esm/id-manager.d.ts.map +1 -0
  30. package/dist/esm/id-manager.js +208 -0
  31. package/dist/esm/id-manager.js.map +1 -0
  32. package/dist/esm/index.d.ts +12 -8
  33. package/dist/esm/index.d.ts.map +1 -1
  34. package/dist/esm/index.js.map +1 -1
  35. package/dist/esm/page-tracking.d.ts +6 -0
  36. package/dist/esm/page-tracking.d.ts.map +1 -1
  37. package/dist/esm/page-tracking.js +23 -2
  38. package/dist/esm/page-tracking.js.map +1 -1
  39. package/dist/esm/react/hooks/useConsent.d.ts +18 -2
  40. package/dist/esm/react/hooks/useConsent.d.ts.map +1 -1
  41. package/dist/esm/react/hooks/useConsent.js +49 -1
  42. package/dist/esm/react/hooks/useConsent.js.map +1 -1
  43. package/dist/id-manager.d.ts +66 -0
  44. package/dist/id-manager.d.ts.map +1 -0
  45. package/dist/id-manager.js +212 -0
  46. package/dist/index.d.ts +12 -8
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.global.dev.js +310 -81
  49. package/dist/index.global.dev.js.map +4 -4
  50. package/dist/index.global.js +8 -8
  51. package/dist/index.global.js.map +4 -4
  52. package/dist/index.js +72 -44
  53. package/dist/index.mjs +73 -45
  54. package/dist/page-tracking.d.ts +6 -0
  55. package/dist/page-tracking.d.ts.map +1 -1
  56. package/dist/page-tracking.js +23 -2
  57. package/dist/react/hooks/useConsent.d.ts +18 -2
  58. package/dist/react/hooks/useConsent.d.ts.map +1 -1
  59. package/dist/react/hooks/useConsent.js +52 -1
  60. package/dist/react/hooks/useConsent.mjs +49 -1
  61. 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
  [![npm version](https://badge.fury.io/js/@grainql%2Fanalytics-web.svg)](https://www.npmjs.com/package/@grainql/analytics-web)
6
6
  [![Bundle Size](https://img.shields.io/bundlephobia/minzip/@grainql/analytics-web)](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
- - 🚀 **Zero dependencies** - ~6KB gzipped
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
@@ -1,8 +1,13 @@
1
1
  /**
2
- * Consent management for Grain Analytics
3
- * Handles GDPR-compliant consent tracking and state management
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 = 'opt-in' | 'opt-out' | 'disabled';
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: 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.
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;;;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
+ {"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"}
@@ -1,7 +1,12 @@
1
1
  "use strict";
2
2
  /**
3
- * Consent management for Grain Analytics
4
- * Handles GDPR-compliant consent tracking and state management
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 = 'opt-out') {
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: 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.
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' || this.consentMode === 'disabled') {
41
- // Auto-grant consent for opt-out and disabled modes
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: In opt-in mode without stored consent, consentState remains null (no consent)
55
+ // Note: cookieless and gdpr-strict modes without stored consent no permanent tracking
51
56
  }
52
57
  catch (error) {
53
- // Silent failure - consent will be requested when needed
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
- // Disabled mode always returns true (no consent required)
127
- if (this.consentMode === 'disabled') {
128
- return true;
131
+ // Cookie-less mode: no consent needed (no permanent tracking)
132
+ if (this.consentMode === 'cookieless') {
133
+ return false; // No permanent IDs
129
134
  }
130
- // No consent state in opt-in mode means no consent
131
- if (this.consentMode === 'opt-in' && !this.consentState) {
132
- return false;
135
+ // GDPR Strict: requires explicit consent
136
+ if (this.consentMode === 'gdpr-strict') {
137
+ if (!this.consentState?.granted) {
138
+ return false;
139
+ }
133
140
  }
134
- // Check consent state
135
- if (!this.consentState?.granted) {
136
- return false;
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 true;
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 === 'opt-in' && !this.consentState?.granted;
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
@@ -1 +1 @@
1
- {"version":3,"file":"consent.js","sourceRoot":"","sources":["../../src/consent.ts"],"names":[],"mappings":"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"}
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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"}