@amplifyup/sdk 0.1.17 → 0.1.19

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.mjs CHANGED
@@ -356,6 +356,9 @@ var AmplifyUpSDK = class {
356
356
  this.attachedEventListeners = [];
357
357
  this.trackingIntervals = [];
358
358
  this.trackingCleanup = null;
359
+ this.componentViewObservers = [];
360
+ this.componentViewSeen = /* @__PURE__ */ new Set();
361
+ this.pageEngagementActive = false;
359
362
  this.adapter = adapter || new AWSAdapter();
360
363
  if (typeof window !== "undefined") {
361
364
  this.isVisualEditorMode = this.detectVisualEditorMode();
@@ -528,6 +531,7 @@ var AmplifyUpSDK = class {
528
531
  this.initialPageSent = true;
529
532
  const url = window.location.pathname;
530
533
  this.page(url);
534
+ this.setupPageEngagement();
531
535
  }
532
536
  }
533
537
  if (this.config?.debug && this.initialized) {
@@ -560,6 +564,168 @@ var AmplifyUpSDK = class {
560
564
  }
561
565
  this.trackingIntervals.forEach((id) => clearInterval(id));
562
566
  this.trackingIntervals = [];
567
+ this.cleanupComponentViewTracking();
568
+ this.pageEngagementActive = false;
569
+ }
570
+ /** Current page path for engagement attribution. */
571
+ currentPath() {
572
+ if (typeof window === "undefined") return "/";
573
+ return window.location.pathname || "/";
574
+ }
575
+ /**
576
+ * Automatic page-level Scroll Depth + Time on Page (dwell heartbeat).
577
+ */
578
+ setupPageEngagement() {
579
+ if (typeof window === "undefined" || typeof document === "undefined") return;
580
+ if (this.isPreviewMode() || this.pageEngagementActive) return;
581
+ this.pageEngagementActive = true;
582
+ const milestones = [25, 50, 75, 100];
583
+ const reached = /* @__PURE__ */ new Set();
584
+ let visibleMs = 0;
585
+ let visibleSince = document.visibilityState === "visible" ? Date.now() : null;
586
+ let lastDwellSecondsSent = 0;
587
+ let scrollRaf = null;
588
+ const path = () => this.currentPath();
589
+ const measureScrollPercent = () => {
590
+ const doc = document.documentElement;
591
+ const body = document.body;
592
+ const scrollTop = window.scrollY || doc.scrollTop || body?.scrollTop || 0;
593
+ const scrollHeight = Math.max(
594
+ doc.scrollHeight,
595
+ body?.scrollHeight || 0,
596
+ doc.offsetHeight,
597
+ body?.offsetHeight || 0
598
+ );
599
+ const clientHeight = window.innerHeight || doc.clientHeight || 0;
600
+ const traversable = Math.max(scrollHeight - clientHeight, 0);
601
+ if (traversable <= 0) return 100;
602
+ return Math.min(100, Math.round(scrollTop / traversable * 100));
603
+ };
604
+ const checkScroll = () => {
605
+ if (this.isPreviewMode()) return;
606
+ const pct = measureScrollPercent();
607
+ for (const milestone of milestones) {
608
+ if (pct >= milestone && !reached.has(milestone)) {
609
+ reached.add(milestone);
610
+ this.track("Scroll Depth", {
611
+ percentage: milestone,
612
+ path: path()
613
+ });
614
+ }
615
+ }
616
+ };
617
+ const onScroll = () => {
618
+ if (scrollRaf != null) return;
619
+ scrollRaf = window.requestAnimationFrame(() => {
620
+ scrollRaf = null;
621
+ checkScroll();
622
+ });
623
+ };
624
+ const accumulateVisible = () => {
625
+ if (visibleSince == null) return;
626
+ visibleMs += Date.now() - visibleSince;
627
+ visibleSince = Date.now();
628
+ };
629
+ const emitDwell = (force = false) => {
630
+ if (this.isPreviewMode()) return;
631
+ accumulateVisible();
632
+ const seconds = Math.floor(visibleMs / 1e3);
633
+ if (seconds <= 0) return;
634
+ if (!force && seconds <= lastDwellSecondsSent) return;
635
+ lastDwellSecondsSent = seconds;
636
+ this.track("Time on Page", {
637
+ seconds,
638
+ path: path()
639
+ });
640
+ };
641
+ const onVisibility = () => {
642
+ if (document.visibilityState === "visible") {
643
+ visibleSince = Date.now();
644
+ } else {
645
+ emitDwell(true);
646
+ visibleSince = null;
647
+ }
648
+ };
649
+ const onPageHide = () => {
650
+ emitDwell(true);
651
+ };
652
+ window.addEventListener("scroll", onScroll, { passive: true });
653
+ document.addEventListener("visibilitychange", onVisibility);
654
+ window.addEventListener("pagehide", onPageHide);
655
+ checkScroll();
656
+ const heartbeat = setInterval(() => {
657
+ if (document.visibilityState === "visible") {
658
+ emitDwell(false);
659
+ }
660
+ }, 15e3);
661
+ this.trackingIntervals.push(heartbeat);
662
+ const previousCleanup = this.trackingCleanup;
663
+ this.trackingCleanup = () => {
664
+ window.removeEventListener("scroll", onScroll);
665
+ document.removeEventListener("visibilitychange", onVisibility);
666
+ window.removeEventListener("pagehide", onPageHide);
667
+ if (scrollRaf != null) {
668
+ window.cancelAnimationFrame(scrollRaf);
669
+ scrollRaf = null;
670
+ }
671
+ previousCleanup?.();
672
+ this.pageEngagementActive = false;
673
+ };
674
+ if (this.config?.debug) {
675
+ console.log("[AmplifyUp SDK] Page engagement tracking enabled");
676
+ }
677
+ }
678
+ /**
679
+ * Automatic Component View when component hosts enter the viewport.
680
+ */
681
+ setupComponentViewTracking() {
682
+ if (typeof document === "undefined" || typeof IntersectionObserver === "undefined") {
683
+ return;
684
+ }
685
+ if (this.isPreviewMode()) return;
686
+ this.cleanupComponentViewTracking();
687
+ const hosts = document.querySelectorAll("[data-amplifyup-component]");
688
+ if (hosts.length === 0) {
689
+ if (this.config?.debug) {
690
+ console.log("[AmplifyUp SDK] No component hosts found for Component View");
691
+ }
692
+ return;
693
+ }
694
+ const observer = new IntersectionObserver(
695
+ (entries) => {
696
+ entries.forEach((entry) => {
697
+ if (!entry.isIntersecting) return;
698
+ const el = entry.target;
699
+ const componentId = el.getAttribute("data-amplifyup-component") || el.getAttribute("data-component-id");
700
+ if (!componentId || this.componentViewSeen.has(componentId)) {
701
+ observer.unobserve(el);
702
+ return;
703
+ }
704
+ this.componentViewSeen.add(componentId);
705
+ const componentName = el.getAttribute("data-component-name") || void 0;
706
+ this.track("Component View", {
707
+ componentId,
708
+ componentName,
709
+ path: this.currentPath()
710
+ });
711
+ observer.unobserve(el);
712
+ });
713
+ },
714
+ { threshold: 0.5 }
715
+ );
716
+ hosts.forEach((host) => observer.observe(host));
717
+ this.componentViewObservers.push(observer);
718
+ if (this.config?.debug) {
719
+ console.log(
720
+ "[AmplifyUp SDK] Component View tracking enabled for",
721
+ hosts.length,
722
+ "hosts"
723
+ );
724
+ }
725
+ }
726
+ cleanupComponentViewTracking() {
727
+ this.componentViewObservers.forEach((obs) => obs.disconnect());
728
+ this.componentViewObservers = [];
563
729
  }
564
730
  /**
565
731
  * Track a page view
@@ -599,9 +765,13 @@ var AmplifyUpSDK = class {
599
765
  instanceId,
600
766
  trackingId: this.config?.trackingId,
601
767
  url: typeof window !== "undefined" ? window.location.href : void 0,
768
+ path: properties?.path || (typeof window !== "undefined" ? window.location.pathname : void 0),
602
769
  // Timestamp is included in event payload
603
770
  ...properties
604
771
  };
772
+ if (!trackProperties.path && typeof window !== "undefined") {
773
+ trackProperties.path = window.location.pathname;
774
+ }
605
775
  this.adapter.track(eventName, trackProperties);
606
776
  }
607
777
  /**
@@ -890,6 +1060,13 @@ Available Console Commands:
890
1060
  if (pageConfig?.eventTracking) {
891
1061
  this.setupEventTracking(pageConfig.eventTracking);
892
1062
  }
1063
+ if (typeof window !== "undefined") {
1064
+ window.setTimeout(() => this.setupComponentViewTracking(), 150);
1065
+ window.setTimeout(() => {
1066
+ this.setupComponentViewTracking();
1067
+ this.refreshEventTracking();
1068
+ }, 800);
1069
+ }
893
1070
  return pageConfig;
894
1071
  }
895
1072
  /**
@@ -897,10 +1074,10 @@ Available Console Commands:
897
1074
  */
898
1075
  setupEventTracking(eventConfig) {
899
1076
  if (typeof document === "undefined") return;
900
- this.cleanupEventTracking();
901
- this.eventTrackingConfig = eventConfig;
1077
+ this.detachEventListeners();
1078
+ this.eventTrackingConfig = Array.isArray(eventConfig) ? [...eventConfig] : [];
902
1079
  if (this.config?.debug) {
903
- console.log("[AmplifyUp SDK] Setting up event tracking:", eventConfig.length, "events");
1080
+ console.log("[AmplifyUp SDK] Setting up event tracking:", this.eventTrackingConfig.length, "events");
904
1081
  }
905
1082
  if (document.readyState === "loading") {
906
1083
  document.addEventListener("DOMContentLoaded", () => this.attachEventListeners());
@@ -915,19 +1092,35 @@ Available Console Commands:
915
1092
  if (typeof document === "undefined") return;
916
1093
  this.eventTrackingConfig.forEach((eventConfig) => {
917
1094
  if (eventConfig.enabled === false) return;
1095
+ if (!eventConfig.selector) {
1096
+ if (this.config?.debug) {
1097
+ console.warn(`[AmplifyUp SDK] Event ${eventConfig.eventName} has no selector, skipping`);
1098
+ }
1099
+ return;
1100
+ }
918
1101
  let targetElements = [];
919
- if (eventConfig.selector) {
920
- try {
921
- targetElements = document.querySelectorAll(eventConfig.selector);
922
- } catch (e) {
1102
+ try {
1103
+ const escapeAttr = (value) => typeof CSS !== "undefined" && typeof CSS.escape === "function" ? CSS.escape(value) : value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
1104
+ const scopeRoot = eventConfig.componentId ? document.querySelector(
1105
+ `[data-amplifyup-component="${escapeAttr(eventConfig.componentId)}"], [data-component-id="${escapeAttr(eventConfig.componentId)}"]`
1106
+ ) : document;
1107
+ if (!scopeRoot) {
923
1108
  if (this.config?.debug) {
924
- console.warn(`[AmplifyUp SDK] Invalid selector: ${eventConfig.selector}`, e);
1109
+ console.log(
1110
+ `[AmplifyUp SDK] Component host not found for ${eventConfig.componentId}`
1111
+ );
925
1112
  }
926
1113
  return;
927
1114
  }
928
- } else {
1115
+ targetElements = Array.from(
1116
+ scopeRoot.querySelectorAll(eventConfig.selector)
1117
+ );
1118
+ if (scopeRoot instanceof Element && scopeRoot !== document.documentElement && scopeRoot.matches?.(eventConfig.selector)) {
1119
+ targetElements.unshift(scopeRoot);
1120
+ }
1121
+ } catch (e) {
929
1122
  if (this.config?.debug) {
930
- console.warn(`[AmplifyUp SDK] Event ${eventConfig.eventName} has no selector, skipping`);
1123
+ console.warn(`[AmplifyUp SDK] Invalid selector: ${eventConfig.selector}`, e);
931
1124
  }
932
1125
  return;
933
1126
  }
@@ -983,8 +1176,15 @@ Available Console Commands:
983
1176
  handleTrackedEvent(event, config, element) {
984
1177
  const properties = {
985
1178
  eventType: config.eventType,
986
- selector: config.selector
1179
+ selector: config.selector,
1180
+ path: this.currentPath()
987
1181
  };
1182
+ if (config.componentId) {
1183
+ properties.componentId = config.componentId;
1184
+ }
1185
+ if (config.componentName) {
1186
+ properties.componentName = config.componentName;
1187
+ }
988
1188
  if (config.properties && config.properties.length > 0) {
989
1189
  config.properties.forEach((prop) => {
990
1190
  const dataAttrValue = element.getAttribute(`data-${prop}`);
@@ -1055,9 +1255,9 @@ Available Console Commands:
1055
1255
  element._amplifyupViewObserver = observer;
1056
1256
  }
1057
1257
  /**
1058
- * Clean up all attached event listeners
1258
+ * Detach listeners/observers without clearing the saved event config.
1059
1259
  */
1060
- cleanupEventTracking() {
1260
+ detachEventListeners() {
1061
1261
  this.attachedEventListeners.forEach(({ element, eventType, handler }) => {
1062
1262
  element.removeEventListener(eventType, handler);
1063
1263
  });
@@ -1071,6 +1271,12 @@ Available Console Commands:
1071
1271
  }
1072
1272
  });
1073
1273
  }
1274
+ }
1275
+ /**
1276
+ * Clean up all attached event listeners and clear config
1277
+ */
1278
+ cleanupEventTracking() {
1279
+ this.detachEventListeners();
1074
1280
  this.eventTrackingConfig = [];
1075
1281
  if (this.config?.debug) {
1076
1282
  console.log("[AmplifyUp SDK] Cleaned up event tracking");
@@ -1080,11 +1286,13 @@ Available Console Commands:
1080
1286
  * Manually trigger event tracking setup (useful after dynamic content loads)
1081
1287
  */
1082
1288
  refreshEventTracking() {
1083
- if (this.eventTrackingConfig.length > 0) {
1084
- this.cleanupEventTracking();
1085
- const config = [...this.eventTrackingConfig];
1086
- this.eventTrackingConfig = config;
1087
- this.attachEventListeners();
1289
+ if (this.eventTrackingConfig.length === 0) return;
1290
+ const config = [...this.eventTrackingConfig];
1291
+ this.detachEventListeners();
1292
+ this.eventTrackingConfig = config;
1293
+ this.attachEventListeners();
1294
+ if (this.config?.debug) {
1295
+ console.log("[AmplifyUp SDK] Refreshed event tracking:", config.length, "events");
1088
1296
  }
1089
1297
  }
1090
1298
  /** Origins allowed to send Composer postMessage commands to the SDK. */
@@ -2100,6 +2308,7 @@ var fetchPageConfig = (route, preview) => getSDK().fetchPageConfig(route, previe
2100
2308
  var setupEventTracking = (config) => getSDK().setupEventTracking(config);
2101
2309
  var cleanupEventTracking = () => getSDK().cleanupEventTracking();
2102
2310
  var refreshEventTracking = () => getSDK().refreshEventTracking();
2311
+ var setupComponentViewTracking = () => getSDK().setupComponentViewTracking();
2103
2312
  var getDiagnostics = () => getSDK().getDiagnostics();
2104
2313
  var getStatus = () => {
2105
2314
  const status = getSDK().getStatus();
@@ -2115,6 +2324,6 @@ var verifyAWSConnection = () => {
2115
2324
  };
2116
2325
  var index_default = getSDK();
2117
2326
 
2118
- export { AmplifyUpSDK, buildLayoutProviderUrl, buildPageRenderUrl, cleanupEventTracking, index_default as default, fetchPageConfig, getDiagnostics, getInstanceId2 as getInstanceId, getOrchestratorUrl, getSDK, getStatus, getTarget, getTrackingId, getUserId2 as getUserId, identify, init, isAmplifyUpTarget, isComposerPreview, isPreviewRequest, normalizeAmplifyUpTarget, page, refreshEventTracking, reset, resolveAmplifyUpTarget, resolveAmplifyUpUrls, setupEventTracking, testTracking, track, trackClick, trackContentEngagement, trackContentView, trackFormFieldInteraction, trackFormSubmit, verifyAWSConnection };
2327
+ export { AmplifyUpSDK, buildLayoutProviderUrl, buildPageRenderUrl, cleanupEventTracking, index_default as default, fetchPageConfig, getDiagnostics, getInstanceId2 as getInstanceId, getOrchestratorUrl, getSDK, getStatus, getTarget, getTrackingId, getUserId2 as getUserId, identify, init, isAmplifyUpTarget, isComposerPreview, isPreviewRequest, normalizeAmplifyUpTarget, page, refreshEventTracking, reset, resolveAmplifyUpTarget, resolveAmplifyUpUrls, setupComponentViewTracking, setupEventTracking, testTracking, track, trackClick, trackContentEngagement, trackContentView, trackFormFieldInteraction, trackFormSubmit, verifyAWSConnection };
2119
2328
  //# sourceMappingURL=index.mjs.map
2120
2329
  //# sourceMappingURL=index.mjs.map