@grainql/analytics-web 3.2.2 → 3.3.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
@@ -435,38 +435,36 @@ 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
+ // CRITICAL: Always add attribution for Mission Control analytics
442
+ // Attribution is essential for goal tracking regardless of consent or event type
443
+ const sessionUTMs = (0, attribution_1.getSessionUTMParameters)();
444
+ if (sessionUTMs) {
445
+ if (sessionUTMs.utm_source)
446
+ properties.utm_source = sessionUTMs.utm_source;
447
+ if (sessionUTMs.utm_medium)
448
+ properties.utm_medium = sessionUTMs.utm_medium;
449
+ if (sessionUTMs.utm_campaign)
450
+ properties.utm_campaign = sessionUTMs.utm_campaign;
451
+ if (sessionUTMs.utm_term)
452
+ properties.utm_term = sessionUTMs.utm_term;
453
+ if (sessionUTMs.utm_content)
454
+ properties.utm_content = sessionUTMs.utm_content;
455
+ }
456
+ // Get first-touch attribution - ALWAYS include for analytics
457
+ const firstTouch = (0, attribution_1.getFirstTouchAttribution)(this.config.tenantId);
458
+ if (firstTouch) {
459
+ properties.first_touch_source = firstTouch.source;
460
+ properties.first_touch_medium = firstTouch.medium;
461
+ properties.first_touch_campaign = firstTouch.campaign;
462
+ properties.first_touch_referrer_category = firstTouch.referrer_category;
463
+ }
464
+ // Add session ID if not already present (only with consent for non-system events)
442
465
  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
- }
466
+ if (!isSystemEvent && hasConsent && !properties.session_id) {
467
+ properties.session_id = this.getSessionId();
470
468
  }
471
469
  }
472
470
  return {
@@ -974,19 +972,21 @@ class GrainAnalytics {
974
972
  if (this.isDestroyed)
975
973
  return;
976
974
  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
975
+ // Use formatEvent to ensure attribution properties are added
976
+ // This is critical for Mission Control analytics
979
977
  const event = {
980
978
  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
- },
979
+ properties,
980
+ };
981
+ const formattedEvent = this.formatEvent(event);
982
+ // Add consent status flags
983
+ formattedEvent.properties = {
984
+ ...formattedEvent.properties,
985
+ _minimal: !hasConsent, // Flag to indicate minimal tracking (daily rotating ID)
986
+ _consent_status: hasConsent ? 'granted' : 'pending',
987
987
  };
988
988
  // Bypass consent check for necessary system events
989
- this.eventQueue.push(event);
989
+ this.eventQueue.push(formattedEvent);
990
990
  this.eventCountSinceLastHeartbeat++;
991
991
  this.log(`Queued system event: ${eventName}`);
992
992
  // Consider flushing
package/dist/index.mjs CHANGED
@@ -394,38 +394,36 @@ 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
+ // CRITICAL: Always add attribution for Mission Control analytics
401
+ // Attribution is essential for goal tracking regardless of consent or event type
402
+ const sessionUTMs = getSessionUTMParameters();
403
+ if (sessionUTMs) {
404
+ if (sessionUTMs.utm_source)
405
+ properties.utm_source = sessionUTMs.utm_source;
406
+ if (sessionUTMs.utm_medium)
407
+ properties.utm_medium = sessionUTMs.utm_medium;
408
+ if (sessionUTMs.utm_campaign)
409
+ properties.utm_campaign = sessionUTMs.utm_campaign;
410
+ if (sessionUTMs.utm_term)
411
+ properties.utm_term = sessionUTMs.utm_term;
412
+ if (sessionUTMs.utm_content)
413
+ properties.utm_content = sessionUTMs.utm_content;
414
+ }
415
+ // Get first-touch attribution - ALWAYS include for analytics
416
+ const firstTouch = getFirstTouchAttribution(this.config.tenantId);
417
+ if (firstTouch) {
418
+ properties.first_touch_source = firstTouch.source;
419
+ properties.first_touch_medium = firstTouch.medium;
420
+ properties.first_touch_campaign = firstTouch.campaign;
421
+ properties.first_touch_referrer_category = firstTouch.referrer_category;
422
+ }
423
+ // Add session ID if not already present (only with consent for non-system events)
401
424
  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
- }
425
+ if (!isSystemEvent && hasConsent && !properties.session_id) {
426
+ properties.session_id = this.getSessionId();
429
427
  }
430
428
  }
431
429
  return {
@@ -933,19 +931,21 @@ export class GrainAnalytics {
933
931
  if (this.isDestroyed)
934
932
  return;
935
933
  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
934
+ // Use formatEvent to ensure attribution properties are added
935
+ // This is critical for Mission Control analytics
938
936
  const event = {
939
937
  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
- },
938
+ properties,
939
+ };
940
+ const formattedEvent = this.formatEvent(event);
941
+ // Add consent status flags
942
+ formattedEvent.properties = {
943
+ ...formattedEvent.properties,
944
+ _minimal: !hasConsent, // Flag to indicate minimal tracking (daily rotating ID)
945
+ _consent_status: hasConsent ? 'granted' : 'pending',
946
946
  };
947
947
  // Bypass consent check for necessary system events
948
- this.eventQueue.push(event);
948
+ this.eventQueue.push(formattedEvent);
949
949
  this.eventCountSinceLastHeartbeat++;
950
950
  this.log(`Queued system event: ${eventName}`);
951
951
  // 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.3.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",