@amplifyup/sdk 0.1.24 → 0.1.26

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.
@@ -58,7 +58,10 @@ var AWSAdapter = class {
58
58
  headers: {
59
59
  "Content-Type": "application/json"
60
60
  },
61
- body: JSON.stringify(eventData)
61
+ body: JSON.stringify(eventData),
62
+ // Keep the request alive through tab close so the single Time on Page
63
+ // flush on pagehide is not cancelled with the document.
64
+ keepalive: true
62
65
  });
63
66
  if (!response.ok) {
64
67
  if (this.config.debug) {
@@ -346,6 +349,8 @@ function isComposerPreview() {
346
349
 
347
350
  // src/index.ts
348
351
  var SESSION_IDLE_GAP_MS = 30 * 60 * 1e3;
352
+ var DWELL_ACTIVITY_IDLE_MS = 2 * 60 * 1e3;
353
+ var DWELL_POLL_MS = 5e3;
349
354
  function readPreviewQueryOverrides() {
350
355
  if (typeof window === "undefined") return {};
351
356
  const params = new URLSearchParams(window.location.search);
@@ -414,9 +419,13 @@ var AmplifyUpSDK = class {
414
419
  reached: /* @__PURE__ */ new Set(),
415
420
  visibleMs: 0,
416
421
  visibleSince: null,
417
- lastDwellSecondsSent: 0,
422
+ /** True after Time on Page has been sent for the current visit. */
423
+ dwellEmitted: false,
418
424
  /** When the tab last became hidden — used for idle-resume re-page. */
419
- hiddenAt: null
425
+ hiddenAt: null,
426
+ /** Last user interaction — freezes dwell when stale while visible. */
427
+ lastActivityAt: 0,
428
+ activityIdle: false
420
429
  };
421
430
  this.adapter = adapter || new AWSAdapter();
422
431
  if (typeof window !== "undefined") {
@@ -639,8 +648,10 @@ var AmplifyUpSDK = class {
639
648
  return window.location.pathname || "/";
640
649
  }
641
650
  /**
642
- * Automatic page-level Scroll Depth + Time on Page (dwell heartbeat).
643
- * State lives on the instance so SPA route changes can reset milestones.
651
+ * Automatic page-level Scroll Depth + Time on Page.
652
+ * Time on Page fires once per visit when the visit ends (route change,
653
+ * tab close, or 30-minute idle resume). Hidden tabs pause the clock;
654
+ * visible-but-idle tabs freeze after DWELL_ACTIVITY_IDLE_MS.
644
655
  */
645
656
  setupPageEngagement() {
646
657
  if (typeof window === "undefined" || typeof document === "undefined") return;
@@ -651,8 +662,10 @@ var AmplifyUpSDK = class {
651
662
  state.reached = /* @__PURE__ */ new Set();
652
663
  state.visibleMs = 0;
653
664
  state.visibleSince = document.visibilityState === "visible" ? Date.now() : null;
654
- state.lastDwellSecondsSent = 0;
665
+ state.dwellEmitted = false;
655
666
  state.hiddenAt = document.visibilityState === "hidden" ? Date.now() : null;
667
+ state.lastActivityAt = Date.now();
668
+ state.activityIdle = false;
656
669
  let scrollRaf = null;
657
670
  const path = () => this.currentPath();
658
671
  const measureScrollPercent = () => {
@@ -684,6 +697,7 @@ var AmplifyUpSDK = class {
684
697
  }
685
698
  };
686
699
  const onScroll = () => {
700
+ noteActivity();
687
701
  if (scrollRaf != null) return;
688
702
  scrollRaf = window.requestAnimationFrame(() => {
689
703
  scrollRaf = null;
@@ -695,24 +709,49 @@ var AmplifyUpSDK = class {
695
709
  state.visibleMs += Date.now() - state.visibleSince;
696
710
  state.visibleSince = Date.now();
697
711
  };
698
- const emitDwell = (_force = false) => {
699
- if (this.isPreviewMode()) return;
712
+ const freezeActivityIdle = () => {
713
+ if (state.activityIdle) return;
700
714
  accumulateVisible();
715
+ state.visibleSince = null;
716
+ state.activityIdle = true;
717
+ };
718
+ const noteActivity = () => {
719
+ state.lastActivityAt = Date.now();
720
+ if (state.activityIdle) {
721
+ state.activityIdle = false;
722
+ if (document.visibilityState === "visible" && state.visibleSince == null) {
723
+ state.visibleSince = Date.now();
724
+ }
725
+ }
726
+ };
727
+ const emitDwell = (reason) => {
728
+ if (this.isPreviewMode()) return;
729
+ if (state.dwellEmitted) return;
730
+ if (!state.activityIdle) {
731
+ accumulateVisible();
732
+ }
701
733
  const seconds = Math.floor(state.visibleMs / 1e3);
702
734
  if (seconds <= 0) return;
703
- if (seconds <= state.lastDwellSecondsSent) return;
704
- state.lastDwellSecondsSent = seconds;
735
+ state.dwellEmitted = true;
705
736
  this.track("Time on Page", {
706
737
  seconds,
707
- path: this.lastTrackedPath || path()
738
+ path: this.lastTrackedPath || path(),
739
+ reason
708
740
  });
709
741
  };
710
- this._emitPageDwell = emitDwell;
742
+ const evaluateIdle = () => {
743
+ if (document.visibilityState !== "visible") return;
744
+ if (Date.now() - state.lastActivityAt >= DWELL_ACTIVITY_IDLE_MS) {
745
+ freezeActivityIdle();
746
+ }
747
+ };
748
+ this._emitPageDwell = () => emitDwell("route");
711
749
  const onVisibility = () => {
712
750
  if (document.visibilityState === "visible") {
713
751
  const idleMs = state.hiddenAt != null ? Date.now() - state.hiddenAt : 0;
714
752
  state.hiddenAt = null;
715
753
  if (idleMs >= SESSION_IDLE_GAP_MS) {
754
+ emitDwell("idle-resume");
716
755
  this.resetEngagementQuiet();
717
756
  const resumePath = path();
718
757
  this.page(resumePath, { resumed: true });
@@ -725,35 +764,51 @@ var AmplifyUpSDK = class {
725
764
  );
726
765
  }
727
766
  }
767
+ noteActivity();
728
768
  state.visibleSince = Date.now();
769
+ state.activityIdle = false;
729
770
  } else {
730
- emitDwell(true);
771
+ accumulateVisible();
731
772
  state.hiddenAt = Date.now();
732
773
  state.visibleSince = null;
774
+ state.activityIdle = false;
733
775
  }
734
776
  };
735
777
  const onPageHide = () => {
736
- emitDwell(true);
778
+ emitDwell("unload");
737
779
  if (state.hiddenAt == null) {
738
780
  state.hiddenAt = Date.now();
739
781
  }
740
782
  state.visibleSince = null;
741
783
  };
784
+ const activityEvents = [
785
+ "pointerdown",
786
+ "keydown",
787
+ "scroll",
788
+ "touchstart"
789
+ ];
790
+ const onActivity = () => noteActivity();
742
791
  window.addEventListener("scroll", onScroll, { passive: true });
743
792
  document.addEventListener("visibilitychange", onVisibility);
744
793
  window.addEventListener("pagehide", onPageHide);
794
+ for (const evt of activityEvents) {
795
+ if (evt === "scroll") continue;
796
+ window.addEventListener(evt, onActivity, { passive: true });
797
+ }
745
798
  checkScroll();
746
- const heartbeat = setInterval(() => {
747
- if (document.visibilityState === "visible") {
748
- emitDwell(false);
749
- }
750
- }, 15e3);
751
- this.trackingIntervals.push(heartbeat);
799
+ const idlePoll = setInterval(() => {
800
+ evaluateIdle();
801
+ }, DWELL_POLL_MS);
802
+ this.trackingIntervals.push(idlePoll);
752
803
  const previousCleanup = this.trackingCleanup;
753
804
  this.trackingCleanup = () => {
754
805
  window.removeEventListener("scroll", onScroll);
755
806
  document.removeEventListener("visibilitychange", onVisibility);
756
807
  window.removeEventListener("pagehide", onPageHide);
808
+ for (const evt of activityEvents) {
809
+ if (evt === "scroll") continue;
810
+ window.removeEventListener(evt, onActivity);
811
+ }
757
812
  if (scrollRaf != null) {
758
813
  window.cancelAnimationFrame(scrollRaf);
759
814
  scrollRaf = null;
@@ -772,13 +827,15 @@ var AmplifyUpSDK = class {
772
827
  emit?.(true);
773
828
  this.resetEngagementQuiet();
774
829
  }
775
- /** Reset dwell/scroll state without emitting (idle resume already flushed on hide). */
830
+ /** Reset dwell/scroll state without emitting (idle resume already flushed). */
776
831
  resetEngagementQuiet() {
777
832
  const state = this.pageEngagementState;
778
833
  state.reached = /* @__PURE__ */ new Set();
779
834
  state.visibleMs = 0;
780
- state.lastDwellSecondsSent = 0;
835
+ state.dwellEmitted = false;
781
836
  state.hiddenAt = null;
837
+ state.lastActivityAt = Date.now();
838
+ state.activityIdle = false;
782
839
  state.visibleSince = typeof document !== "undefined" && document.visibilityState === "visible" ? Date.now() : null;
783
840
  this.componentViewSeen.clear();
784
841
  }