@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.
package/dist/index.d.mts CHANGED
@@ -108,6 +108,12 @@ declare function normalizeRoute(route: string): string;
108
108
  * Must stay aligned with SESSION_GAP_MS in apps/orchestrator/lib/aws-profiles.ts.
109
109
  */
110
110
  declare const SESSION_IDLE_GAP_MS: number;
111
+ /**
112
+ * Time on Page: one event per page visit, sent when the visit ends
113
+ * (SPA navigation, tab close, or 30-minute idle resume). Switching tabs
114
+ * pauses the clock; abandoned-but-visible tabs freeze after this idle gap.
115
+ */
116
+ declare const DWELL_ACTIVITY_IDLE_MS: number;
111
117
  interface EventTrackingConfig {
112
118
  eventName: string;
113
119
  eventType: string;
@@ -194,13 +200,15 @@ declare class AmplifyUpSDK {
194
200
  /** Current page path for engagement attribution. */
195
201
  private currentPath;
196
202
  /**
197
- * Automatic page-level Scroll Depth + Time on Page (dwell heartbeat).
198
- * State lives on the instance so SPA route changes can reset milestones.
203
+ * Automatic page-level Scroll Depth + Time on Page.
204
+ * Time on Page fires once per visit when the visit ends (route change,
205
+ * tab close, or 30-minute idle resume). Hidden tabs pause the clock;
206
+ * visible-but-idle tabs freeze after DWELL_ACTIVITY_IDLE_MS.
199
207
  */
200
208
  private setupPageEngagement;
201
209
  /** Flush dwell then reset — used on SPA route changes. */
202
210
  private resetEngagementForNewPage;
203
- /** Reset dwell/scroll state without emitting (idle resume already flushed on hide). */
211
+ /** Reset dwell/scroll state without emitting (idle resume already flushed). */
204
212
  private resetEngagementQuiet;
205
213
  /**
206
214
  * SPA / soft navigation: fire a page view and reset engagement.
@@ -505,4 +513,4 @@ declare const verifyAWSConnection: () => {
505
513
 
506
514
  declare const _default: AmplifyUpSDK;
507
515
 
508
- export { AmplifyContentMeta, AmplifyUpConfig, AmplifyUpSDK, AmplifyUpTarget, CDPAdapter, ContentSource, EdgeResolveError, EventProperties, EventTrackingConfig$1 as EventTrackingConfig, type FetchContentOptions, type FetchContentResult, IdentifyInput, IdentifyTraits, PageConfig, ProfileFieldsInput, SESSION_IDLE_GAP_MS, buildLayoutProviderUrl, buildPageRenderUrl, cleanupEventTracking, _default as default, fetchContent, fetchEdgeResolve, fetchPageConfig, getDiagnostics, getInstanceId, getOrchestratorUrl, getSDK, getStatus, getTarget, getTrackingId, getUserId, identify, init, mapResolvedToPageConfig, normalizeRoute, page, refreshEventTracking, reset, routeChange, setProfileFields, setupComponentViewTracking, setupEventTracking, testTracking, track, trackClick, trackContentEngagement, trackContentView, trackFormFieldInteraction, trackFormSubmit, verifyAWSConnection };
516
+ export { AmplifyContentMeta, AmplifyUpConfig, AmplifyUpSDK, AmplifyUpTarget, CDPAdapter, ContentSource, DWELL_ACTIVITY_IDLE_MS, EdgeResolveError, EventProperties, EventTrackingConfig$1 as EventTrackingConfig, type FetchContentOptions, type FetchContentResult, IdentifyInput, IdentifyTraits, PageConfig, ProfileFieldsInput, SESSION_IDLE_GAP_MS, buildLayoutProviderUrl, buildPageRenderUrl, cleanupEventTracking, _default as default, fetchContent, fetchEdgeResolve, fetchPageConfig, getDiagnostics, getInstanceId, getOrchestratorUrl, getSDK, getStatus, getTarget, getTrackingId, getUserId, identify, init, mapResolvedToPageConfig, normalizeRoute, page, refreshEventTracking, reset, routeChange, setProfileFields, setupComponentViewTracking, setupEventTracking, testTracking, track, trackClick, trackContentEngagement, trackContentView, trackFormFieldInteraction, trackFormSubmit, verifyAWSConnection };
package/dist/index.d.ts CHANGED
@@ -108,6 +108,12 @@ declare function normalizeRoute(route: string): string;
108
108
  * Must stay aligned with SESSION_GAP_MS in apps/orchestrator/lib/aws-profiles.ts.
109
109
  */
110
110
  declare const SESSION_IDLE_GAP_MS: number;
111
+ /**
112
+ * Time on Page: one event per page visit, sent when the visit ends
113
+ * (SPA navigation, tab close, or 30-minute idle resume). Switching tabs
114
+ * pauses the clock; abandoned-but-visible tabs freeze after this idle gap.
115
+ */
116
+ declare const DWELL_ACTIVITY_IDLE_MS: number;
111
117
  interface EventTrackingConfig {
112
118
  eventName: string;
113
119
  eventType: string;
@@ -194,13 +200,15 @@ declare class AmplifyUpSDK {
194
200
  /** Current page path for engagement attribution. */
195
201
  private currentPath;
196
202
  /**
197
- * Automatic page-level Scroll Depth + Time on Page (dwell heartbeat).
198
- * State lives on the instance so SPA route changes can reset milestones.
203
+ * Automatic page-level Scroll Depth + Time on Page.
204
+ * Time on Page fires once per visit when the visit ends (route change,
205
+ * tab close, or 30-minute idle resume). Hidden tabs pause the clock;
206
+ * visible-but-idle tabs freeze after DWELL_ACTIVITY_IDLE_MS.
199
207
  */
200
208
  private setupPageEngagement;
201
209
  /** Flush dwell then reset — used on SPA route changes. */
202
210
  private resetEngagementForNewPage;
203
- /** Reset dwell/scroll state without emitting (idle resume already flushed on hide). */
211
+ /** Reset dwell/scroll state without emitting (idle resume already flushed). */
204
212
  private resetEngagementQuiet;
205
213
  /**
206
214
  * SPA / soft navigation: fire a page view and reset engagement.
@@ -505,4 +513,4 @@ declare const verifyAWSConnection: () => {
505
513
 
506
514
  declare const _default: AmplifyUpSDK;
507
515
 
508
- export { AmplifyContentMeta, AmplifyUpConfig, AmplifyUpSDK, AmplifyUpTarget, CDPAdapter, ContentSource, EdgeResolveError, EventProperties, EventTrackingConfig$1 as EventTrackingConfig, type FetchContentOptions, type FetchContentResult, IdentifyInput, IdentifyTraits, PageConfig, ProfileFieldsInput, SESSION_IDLE_GAP_MS, buildLayoutProviderUrl, buildPageRenderUrl, cleanupEventTracking, _default as default, fetchContent, fetchEdgeResolve, fetchPageConfig, getDiagnostics, getInstanceId, getOrchestratorUrl, getSDK, getStatus, getTarget, getTrackingId, getUserId, identify, init, mapResolvedToPageConfig, normalizeRoute, page, refreshEventTracking, reset, routeChange, setProfileFields, setupComponentViewTracking, setupEventTracking, testTracking, track, trackClick, trackContentEngagement, trackContentView, trackFormFieldInteraction, trackFormSubmit, verifyAWSConnection };
516
+ export { AmplifyContentMeta, AmplifyUpConfig, AmplifyUpSDK, AmplifyUpTarget, CDPAdapter, ContentSource, DWELL_ACTIVITY_IDLE_MS, EdgeResolveError, EventProperties, EventTrackingConfig$1 as EventTrackingConfig, type FetchContentOptions, type FetchContentResult, IdentifyInput, IdentifyTraits, PageConfig, ProfileFieldsInput, SESSION_IDLE_GAP_MS, buildLayoutProviderUrl, buildPageRenderUrl, cleanupEventTracking, _default as default, fetchContent, fetchEdgeResolve, fetchPageConfig, getDiagnostics, getInstanceId, getOrchestratorUrl, getSDK, getStatus, getTarget, getTrackingId, getUserId, identify, init, mapResolvedToPageConfig, normalizeRoute, page, refreshEventTracking, reset, routeChange, setProfileFields, setupComponentViewTracking, setupEventTracking, testTracking, track, trackClick, trackContentEngagement, trackContentView, trackFormFieldInteraction, trackFormSubmit, verifyAWSConnection };
package/dist/index.js CHANGED
@@ -51,7 +51,10 @@ var AWSAdapter = class {
51
51
  headers: {
52
52
  "Content-Type": "application/json"
53
53
  },
54
- body: JSON.stringify(eventData)
54
+ body: JSON.stringify(eventData),
55
+ // Keep the request alive through tab close so the single Time on Page
56
+ // flush on pagehide is not cancelled with the document.
57
+ keepalive: true
55
58
  });
56
59
  if (!response.ok) {
57
60
  if (this.config.debug) {
@@ -510,6 +513,8 @@ async function fetchContent(options) {
510
513
 
511
514
  // src/index.ts
512
515
  var SESSION_IDLE_GAP_MS = 30 * 60 * 1e3;
516
+ var DWELL_ACTIVITY_IDLE_MS = 2 * 60 * 1e3;
517
+ var DWELL_POLL_MS = 5e3;
513
518
  function readPreviewQueryOverrides() {
514
519
  if (typeof window === "undefined") return {};
515
520
  const params = new URLSearchParams(window.location.search);
@@ -578,9 +583,13 @@ var AmplifyUpSDK = class {
578
583
  reached: /* @__PURE__ */ new Set(),
579
584
  visibleMs: 0,
580
585
  visibleSince: null,
581
- lastDwellSecondsSent: 0,
586
+ /** True after Time on Page has been sent for the current visit. */
587
+ dwellEmitted: false,
582
588
  /** When the tab last became hidden — used for idle-resume re-page. */
583
- hiddenAt: null
589
+ hiddenAt: null,
590
+ /** Last user interaction — freezes dwell when stale while visible. */
591
+ lastActivityAt: 0,
592
+ activityIdle: false
584
593
  };
585
594
  this.adapter = adapter || new AWSAdapter();
586
595
  if (typeof window !== "undefined") {
@@ -803,8 +812,10 @@ var AmplifyUpSDK = class {
803
812
  return window.location.pathname || "/";
804
813
  }
805
814
  /**
806
- * Automatic page-level Scroll Depth + Time on Page (dwell heartbeat).
807
- * State lives on the instance so SPA route changes can reset milestones.
815
+ * Automatic page-level Scroll Depth + Time on Page.
816
+ * Time on Page fires once per visit when the visit ends (route change,
817
+ * tab close, or 30-minute idle resume). Hidden tabs pause the clock;
818
+ * visible-but-idle tabs freeze after DWELL_ACTIVITY_IDLE_MS.
808
819
  */
809
820
  setupPageEngagement() {
810
821
  if (typeof window === "undefined" || typeof document === "undefined") return;
@@ -815,8 +826,10 @@ var AmplifyUpSDK = class {
815
826
  state.reached = /* @__PURE__ */ new Set();
816
827
  state.visibleMs = 0;
817
828
  state.visibleSince = document.visibilityState === "visible" ? Date.now() : null;
818
- state.lastDwellSecondsSent = 0;
829
+ state.dwellEmitted = false;
819
830
  state.hiddenAt = document.visibilityState === "hidden" ? Date.now() : null;
831
+ state.lastActivityAt = Date.now();
832
+ state.activityIdle = false;
820
833
  let scrollRaf = null;
821
834
  const path = () => this.currentPath();
822
835
  const measureScrollPercent = () => {
@@ -848,6 +861,7 @@ var AmplifyUpSDK = class {
848
861
  }
849
862
  };
850
863
  const onScroll = () => {
864
+ noteActivity();
851
865
  if (scrollRaf != null) return;
852
866
  scrollRaf = window.requestAnimationFrame(() => {
853
867
  scrollRaf = null;
@@ -859,24 +873,49 @@ var AmplifyUpSDK = class {
859
873
  state.visibleMs += Date.now() - state.visibleSince;
860
874
  state.visibleSince = Date.now();
861
875
  };
862
- const emitDwell = (_force = false) => {
863
- if (this.isPreviewMode()) return;
876
+ const freezeActivityIdle = () => {
877
+ if (state.activityIdle) return;
864
878
  accumulateVisible();
879
+ state.visibleSince = null;
880
+ state.activityIdle = true;
881
+ };
882
+ const noteActivity = () => {
883
+ state.lastActivityAt = Date.now();
884
+ if (state.activityIdle) {
885
+ state.activityIdle = false;
886
+ if (document.visibilityState === "visible" && state.visibleSince == null) {
887
+ state.visibleSince = Date.now();
888
+ }
889
+ }
890
+ };
891
+ const emitDwell = (reason) => {
892
+ if (this.isPreviewMode()) return;
893
+ if (state.dwellEmitted) return;
894
+ if (!state.activityIdle) {
895
+ accumulateVisible();
896
+ }
865
897
  const seconds = Math.floor(state.visibleMs / 1e3);
866
898
  if (seconds <= 0) return;
867
- if (seconds <= state.lastDwellSecondsSent) return;
868
- state.lastDwellSecondsSent = seconds;
899
+ state.dwellEmitted = true;
869
900
  this.track("Time on Page", {
870
901
  seconds,
871
- path: this.lastTrackedPath || path()
902
+ path: this.lastTrackedPath || path(),
903
+ reason
872
904
  });
873
905
  };
874
- this._emitPageDwell = emitDwell;
906
+ const evaluateIdle = () => {
907
+ if (document.visibilityState !== "visible") return;
908
+ if (Date.now() - state.lastActivityAt >= DWELL_ACTIVITY_IDLE_MS) {
909
+ freezeActivityIdle();
910
+ }
911
+ };
912
+ this._emitPageDwell = () => emitDwell("route");
875
913
  const onVisibility = () => {
876
914
  if (document.visibilityState === "visible") {
877
915
  const idleMs = state.hiddenAt != null ? Date.now() - state.hiddenAt : 0;
878
916
  state.hiddenAt = null;
879
917
  if (idleMs >= SESSION_IDLE_GAP_MS) {
918
+ emitDwell("idle-resume");
880
919
  this.resetEngagementQuiet();
881
920
  const resumePath = path();
882
921
  this.page(resumePath, { resumed: true });
@@ -889,35 +928,51 @@ var AmplifyUpSDK = class {
889
928
  );
890
929
  }
891
930
  }
931
+ noteActivity();
892
932
  state.visibleSince = Date.now();
933
+ state.activityIdle = false;
893
934
  } else {
894
- emitDwell(true);
935
+ accumulateVisible();
895
936
  state.hiddenAt = Date.now();
896
937
  state.visibleSince = null;
938
+ state.activityIdle = false;
897
939
  }
898
940
  };
899
941
  const onPageHide = () => {
900
- emitDwell(true);
942
+ emitDwell("unload");
901
943
  if (state.hiddenAt == null) {
902
944
  state.hiddenAt = Date.now();
903
945
  }
904
946
  state.visibleSince = null;
905
947
  };
948
+ const activityEvents = [
949
+ "pointerdown",
950
+ "keydown",
951
+ "scroll",
952
+ "touchstart"
953
+ ];
954
+ const onActivity = () => noteActivity();
906
955
  window.addEventListener("scroll", onScroll, { passive: true });
907
956
  document.addEventListener("visibilitychange", onVisibility);
908
957
  window.addEventListener("pagehide", onPageHide);
958
+ for (const evt of activityEvents) {
959
+ if (evt === "scroll") continue;
960
+ window.addEventListener(evt, onActivity, { passive: true });
961
+ }
909
962
  checkScroll();
910
- const heartbeat = setInterval(() => {
911
- if (document.visibilityState === "visible") {
912
- emitDwell(false);
913
- }
914
- }, 15e3);
915
- this.trackingIntervals.push(heartbeat);
963
+ const idlePoll = setInterval(() => {
964
+ evaluateIdle();
965
+ }, DWELL_POLL_MS);
966
+ this.trackingIntervals.push(idlePoll);
916
967
  const previousCleanup = this.trackingCleanup;
917
968
  this.trackingCleanup = () => {
918
969
  window.removeEventListener("scroll", onScroll);
919
970
  document.removeEventListener("visibilitychange", onVisibility);
920
971
  window.removeEventListener("pagehide", onPageHide);
972
+ for (const evt of activityEvents) {
973
+ if (evt === "scroll") continue;
974
+ window.removeEventListener(evt, onActivity);
975
+ }
921
976
  if (scrollRaf != null) {
922
977
  window.cancelAnimationFrame(scrollRaf);
923
978
  scrollRaf = null;
@@ -936,13 +991,15 @@ var AmplifyUpSDK = class {
936
991
  emit?.(true);
937
992
  this.resetEngagementQuiet();
938
993
  }
939
- /** Reset dwell/scroll state without emitting (idle resume already flushed on hide). */
994
+ /** Reset dwell/scroll state without emitting (idle resume already flushed). */
940
995
  resetEngagementQuiet() {
941
996
  const state = this.pageEngagementState;
942
997
  state.reached = /* @__PURE__ */ new Set();
943
998
  state.visibleMs = 0;
944
- state.lastDwellSecondsSent = 0;
999
+ state.dwellEmitted = false;
945
1000
  state.hiddenAt = null;
1001
+ state.lastActivityAt = Date.now();
1002
+ state.activityIdle = false;
946
1003
  state.visibleSince = typeof document !== "undefined" && document.visibilityState === "visible" ? Date.now() : null;
947
1004
  this.componentViewSeen.clear();
948
1005
  }
@@ -2693,6 +2750,7 @@ var verifyAWSConnection = () => {
2693
2750
  var index_default = getSDK();
2694
2751
 
2695
2752
  exports.AmplifyUpSDK = AmplifyUpSDK;
2753
+ exports.DWELL_ACTIVITY_IDLE_MS = DWELL_ACTIVITY_IDLE_MS;
2696
2754
  exports.EdgeResolveError = EdgeResolveError;
2697
2755
  exports.SESSION_IDLE_GAP_MS = SESSION_IDLE_GAP_MS;
2698
2756
  exports.buildLayoutProviderUrl = buildLayoutProviderUrl;