@grainql/analytics-web 3.2.2 → 3.4.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/dist/index.js CHANGED
@@ -84,7 +84,7 @@ class GrainAnalytics {
84
84
  this.debugAgent = null;
85
85
  this.isDebugMode = false;
86
86
  this.config = {
87
- apiUrl: 'https://api.grainql.com',
87
+ apiUrl: 'https://clientapis.grainql.com',
88
88
  authStrategy: 'NONE',
89
89
  batchSize: 50,
90
90
  flushInterval: 5000, // 5 seconds
@@ -435,38 +435,34 @@ class GrainAnalytics {
435
435
  formatEvent(event) {
436
436
  const properties = event.properties || {};
437
437
  // Auto-enrich events with session-level attribution properties
438
- // This ensures UTM parameters and attribution data are available on ALL events, not just page_view
438
+ // This ensures UTM parameters and attribution data are available on ALL events
439
439
  if (!this.config.disableAutoProperties && typeof window !== 'undefined') {
440
440
  const hasConsent = this.consentManager.hasConsent('analytics');
441
- // Only enrich if not a system event (they handle their own properties)
441
+ const sessionUTMs = (0, attribution_1.getSessionUTMParameters)();
442
+ if (sessionUTMs) {
443
+ if (sessionUTMs.utm_source)
444
+ properties.utm_source = sessionUTMs.utm_source;
445
+ if (sessionUTMs.utm_medium)
446
+ properties.utm_medium = sessionUTMs.utm_medium;
447
+ if (sessionUTMs.utm_campaign)
448
+ properties.utm_campaign = sessionUTMs.utm_campaign;
449
+ if (sessionUTMs.utm_term)
450
+ properties.utm_term = sessionUTMs.utm_term;
451
+ if (sessionUTMs.utm_content)
452
+ properties.utm_content = sessionUTMs.utm_content;
453
+ }
454
+ // Get first-touch attribution - ALWAYS include for analytics
455
+ const firstTouch = (0, attribution_1.getFirstTouchAttribution)(this.config.tenantId);
456
+ if (firstTouch) {
457
+ properties.first_touch_source = firstTouch.source;
458
+ properties.first_touch_medium = firstTouch.medium;
459
+ properties.first_touch_campaign = firstTouch.campaign;
460
+ properties.first_touch_referrer_category = firstTouch.referrer_category;
461
+ }
462
+ // Add session ID if not already present (only with consent for non-system events)
442
463
  const isSystemEvent = event.eventName.startsWith('_grain_');
443
- if (!isSystemEvent && hasConsent) {
444
- // Get session UTM parameters
445
- const sessionUTMs = (0, attribution_1.getSessionUTMParameters)();
446
- if (sessionUTMs) {
447
- if (sessionUTMs.utm_source)
448
- properties.utm_source = sessionUTMs.utm_source;
449
- if (sessionUTMs.utm_medium)
450
- properties.utm_medium = sessionUTMs.utm_medium;
451
- if (sessionUTMs.utm_campaign)
452
- properties.utm_campaign = sessionUTMs.utm_campaign;
453
- if (sessionUTMs.utm_term)
454
- properties.utm_term = sessionUTMs.utm_term;
455
- if (sessionUTMs.utm_content)
456
- properties.utm_content = sessionUTMs.utm_content;
457
- }
458
- // Get first-touch attribution
459
- const firstTouch = (0, attribution_1.getFirstTouchAttribution)(this.config.tenantId);
460
- if (firstTouch) {
461
- properties.first_touch_source = firstTouch.source;
462
- properties.first_touch_medium = firstTouch.medium;
463
- properties.first_touch_campaign = firstTouch.campaign;
464
- properties.first_touch_referrer_category = firstTouch.referrer_category;
465
- }
466
- // Add session ID if not already present
467
- if (!properties.session_id) {
468
- properties.session_id = this.getSessionId();
469
- }
464
+ if (!isSystemEvent && hasConsent && !properties.session_id) {
465
+ properties.session_id = this.getSessionId();
470
466
  }
471
467
  }
472
468
  return {
@@ -974,19 +970,21 @@ class GrainAnalytics {
974
970
  if (this.isDestroyed)
975
971
  return;
976
972
  const hasConsent = this.consentManager.hasConsent('analytics');
977
- // Create event with appropriate user ID
978
- // v2.0: Always use IdManager which returns daily rotating ID or permanent ID based on consent
973
+ // Use formatEvent to ensure attribution properties are added
974
+ // This is critical for Mission Control analytics
979
975
  const event = {
980
976
  eventName,
981
- userId: this.getEffectiveUserId(), // IdManager handles daily vs permanent based on consent
982
- properties: {
983
- ...properties,
984
- _minimal: !hasConsent, // Flag to indicate minimal tracking (daily rotating ID)
985
- _consent_status: hasConsent ? 'granted' : 'pending',
986
- },
977
+ properties,
978
+ };
979
+ const formattedEvent = this.formatEvent(event);
980
+ // Add consent status flags
981
+ formattedEvent.properties = {
982
+ ...formattedEvent.properties,
983
+ _minimal: !hasConsent, // Flag to indicate minimal tracking (daily rotating ID)
984
+ _consent_status: hasConsent ? 'granted' : 'pending',
987
985
  };
988
986
  // Bypass consent check for necessary system events
989
- this.eventQueue.push(event);
987
+ this.eventQueue.push(formattedEvent);
990
988
  this.eventCountSinceLastHeartbeat++;
991
989
  this.log(`Queued system event: ${eventName}`);
992
990
  // Consider flushing
package/dist/index.mjs CHANGED
@@ -43,7 +43,7 @@ export class GrainAnalytics {
43
43
  this.debugAgent = null;
44
44
  this.isDebugMode = false;
45
45
  this.config = {
46
- apiUrl: 'https://api.grainql.com',
46
+ apiUrl: 'https://clientapis.grainql.com',
47
47
  authStrategy: 'NONE',
48
48
  batchSize: 50,
49
49
  flushInterval: 5000, // 5 seconds
@@ -394,38 +394,34 @@ export class GrainAnalytics {
394
394
  formatEvent(event) {
395
395
  const properties = event.properties || {};
396
396
  // Auto-enrich events with session-level attribution properties
397
- // This ensures UTM parameters and attribution data are available on ALL events, not just page_view
397
+ // This ensures UTM parameters and attribution data are available on ALL events
398
398
  if (!this.config.disableAutoProperties && typeof window !== 'undefined') {
399
399
  const hasConsent = this.consentManager.hasConsent('analytics');
400
- // Only enrich if not a system event (they handle their own properties)
400
+ const sessionUTMs = getSessionUTMParameters();
401
+ if (sessionUTMs) {
402
+ if (sessionUTMs.utm_source)
403
+ properties.utm_source = sessionUTMs.utm_source;
404
+ if (sessionUTMs.utm_medium)
405
+ properties.utm_medium = sessionUTMs.utm_medium;
406
+ if (sessionUTMs.utm_campaign)
407
+ properties.utm_campaign = sessionUTMs.utm_campaign;
408
+ if (sessionUTMs.utm_term)
409
+ properties.utm_term = sessionUTMs.utm_term;
410
+ if (sessionUTMs.utm_content)
411
+ properties.utm_content = sessionUTMs.utm_content;
412
+ }
413
+ // Get first-touch attribution - ALWAYS include for analytics
414
+ const firstTouch = getFirstTouchAttribution(this.config.tenantId);
415
+ if (firstTouch) {
416
+ properties.first_touch_source = firstTouch.source;
417
+ properties.first_touch_medium = firstTouch.medium;
418
+ properties.first_touch_campaign = firstTouch.campaign;
419
+ properties.first_touch_referrer_category = firstTouch.referrer_category;
420
+ }
421
+ // Add session ID if not already present (only with consent for non-system events)
401
422
  const isSystemEvent = event.eventName.startsWith('_grain_');
402
- if (!isSystemEvent && hasConsent) {
403
- // Get session UTM parameters
404
- const sessionUTMs = getSessionUTMParameters();
405
- if (sessionUTMs) {
406
- if (sessionUTMs.utm_source)
407
- properties.utm_source = sessionUTMs.utm_source;
408
- if (sessionUTMs.utm_medium)
409
- properties.utm_medium = sessionUTMs.utm_medium;
410
- if (sessionUTMs.utm_campaign)
411
- properties.utm_campaign = sessionUTMs.utm_campaign;
412
- if (sessionUTMs.utm_term)
413
- properties.utm_term = sessionUTMs.utm_term;
414
- if (sessionUTMs.utm_content)
415
- properties.utm_content = sessionUTMs.utm_content;
416
- }
417
- // Get first-touch attribution
418
- const firstTouch = getFirstTouchAttribution(this.config.tenantId);
419
- if (firstTouch) {
420
- properties.first_touch_source = firstTouch.source;
421
- properties.first_touch_medium = firstTouch.medium;
422
- properties.first_touch_campaign = firstTouch.campaign;
423
- properties.first_touch_referrer_category = firstTouch.referrer_category;
424
- }
425
- // Add session ID if not already present
426
- if (!properties.session_id) {
427
- properties.session_id = this.getSessionId();
428
- }
423
+ if (!isSystemEvent && hasConsent && !properties.session_id) {
424
+ properties.session_id = this.getSessionId();
429
425
  }
430
426
  }
431
427
  return {
@@ -933,19 +929,21 @@ export class GrainAnalytics {
933
929
  if (this.isDestroyed)
934
930
  return;
935
931
  const hasConsent = this.consentManager.hasConsent('analytics');
936
- // Create event with appropriate user ID
937
- // v2.0: Always use IdManager which returns daily rotating ID or permanent ID based on consent
932
+ // Use formatEvent to ensure attribution properties are added
933
+ // This is critical for Mission Control analytics
938
934
  const event = {
939
935
  eventName,
940
- userId: this.getEffectiveUserId(), // IdManager handles daily vs permanent based on consent
941
- properties: {
942
- ...properties,
943
- _minimal: !hasConsent, // Flag to indicate minimal tracking (daily rotating ID)
944
- _consent_status: hasConsent ? 'granted' : 'pending',
945
- },
936
+ properties,
937
+ };
938
+ const formattedEvent = this.formatEvent(event);
939
+ // Add consent status flags
940
+ formattedEvent.properties = {
941
+ ...formattedEvent.properties,
942
+ _minimal: !hasConsent, // Flag to indicate minimal tracking (daily rotating ID)
943
+ _consent_status: hasConsent ? 'granted' : 'pending',
946
944
  };
947
945
  // Bypass consent check for necessary system events
948
- this.eventQueue.push(event);
946
+ this.eventQueue.push(formattedEvent);
949
947
  this.eventCountSinceLastHeartbeat++;
950
948
  this.log(`Queued system event: ${eventName}`);
951
949
  // Consider flushing
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grainql/analytics-web",
3
- "version": "3.2.2",
3
+ "version": "3.4.0",
4
4
  "description": "Lightweight TypeScript SDK for sending analytics events and managing remote configurations via Grain's REST API",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",