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