@amplifyup/sdk 0.1.23 → 0.1.25

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.
@@ -3,6 +3,7 @@
3
3
 
4
4
  var react = require('react');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
+ require('marked');
6
7
 
7
8
  /* @amplifyup/sdk - https://amplifyup.ai */
8
9
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
@@ -347,6 +348,10 @@ function isComposerPreview() {
347
348
 
348
349
  // src/index.ts
349
350
  var SESSION_IDLE_GAP_MS = 30 * 60 * 1e3;
351
+ var DWELL_MILESTONES_SEC = [15, 30, 60, 120, 300];
352
+ var DWELL_CHECKPOINT_SEC = 15 * 60;
353
+ var DWELL_ACTIVITY_IDLE_MS = 2 * 60 * 1e3;
354
+ var DWELL_POLL_MS = 5e3;
350
355
  function readPreviewQueryOverrides() {
351
356
  if (typeof window === "undefined") return {};
352
357
  const params = new URLSearchParams(window.location.search);
@@ -417,7 +422,10 @@ var AmplifyUpSDK = class {
417
422
  visibleSince: null,
418
423
  lastDwellSecondsSent: 0,
419
424
  /** When the tab last became hidden — used for idle-resume re-page. */
420
- hiddenAt: null
425
+ hiddenAt: null,
426
+ /** Last user interaction — freezes dwell when stale while visible. */
427
+ lastActivityAt: 0,
428
+ activityIdle: false
421
429
  };
422
430
  this.adapter = adapter || new AWSAdapter();
423
431
  if (typeof window !== "undefined") {
@@ -640,8 +648,10 @@ var AmplifyUpSDK = class {
640
648
  return window.location.pathname || "/";
641
649
  }
642
650
  /**
643
- * Automatic page-level Scroll Depth + Time on Page (dwell heartbeat).
644
- * State lives on the instance so SPA route changes can reset milestones.
651
+ * Automatic page-level Scroll Depth + Time on Page.
652
+ * Dwell uses milestones / sparse checkpoints (not a fixed 15s heartbeat),
653
+ * freezes while the tab is visible but the user is idle, and always flushes
654
+ * on hide / unload / SPA route change.
645
655
  */
646
656
  setupPageEngagement() {
647
657
  if (typeof window === "undefined" || typeof document === "undefined") return;
@@ -654,6 +664,8 @@ var AmplifyUpSDK = class {
654
664
  state.visibleSince = document.visibilityState === "visible" ? Date.now() : null;
655
665
  state.lastDwellSecondsSent = 0;
656
666
  state.hiddenAt = document.visibilityState === "hidden" ? Date.now() : null;
667
+ state.lastActivityAt = Date.now();
668
+ state.activityIdle = false;
657
669
  let scrollRaf = null;
658
670
  const path = () => this.currentPath();
659
671
  const measureScrollPercent = () => {
@@ -685,6 +697,7 @@ var AmplifyUpSDK = class {
685
697
  }
686
698
  };
687
699
  const onScroll = () => {
700
+ noteActivity();
688
701
  if (scrollRaf != null) return;
689
702
  scrollRaf = window.requestAnimationFrame(() => {
690
703
  scrollRaf = null;
@@ -696,19 +709,58 @@ var AmplifyUpSDK = class {
696
709
  state.visibleMs += Date.now() - state.visibleSince;
697
710
  state.visibleSince = Date.now();
698
711
  };
699
- const emitDwell = (_force = false) => {
700
- if (this.isPreviewMode()) return;
712
+ const freezeActivityIdle = () => {
713
+ if (state.activityIdle) return;
701
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 nextEmitTarget = (lastSent) => {
728
+ for (const m of DWELL_MILESTONES_SEC) {
729
+ if (m > lastSent) return { seconds: m, reason: "milestone" };
730
+ }
731
+ const nextCheckpoint = Math.floor(lastSent / DWELL_CHECKPOINT_SEC) * DWELL_CHECKPOINT_SEC + DWELL_CHECKPOINT_SEC;
732
+ const target = nextCheckpoint > lastSent ? nextCheckpoint : lastSent + DWELL_CHECKPOINT_SEC;
733
+ return { seconds: target, reason: "checkpoint" };
734
+ };
735
+ const emitDwell = (reason, force = false) => {
736
+ if (this.isPreviewMode()) return;
737
+ if (!state.activityIdle) {
738
+ accumulateVisible();
739
+ }
702
740
  const seconds = Math.floor(state.visibleMs / 1e3);
703
741
  if (seconds <= 0) return;
704
742
  if (seconds <= state.lastDwellSecondsSent) return;
743
+ if (!force) {
744
+ const target = nextEmitTarget(state.lastDwellSecondsSent);
745
+ if (!target || seconds < target.seconds) return;
746
+ reason = target.reason;
747
+ }
705
748
  state.lastDwellSecondsSent = seconds;
706
749
  this.track("Time on Page", {
707
750
  seconds,
708
- path: this.lastTrackedPath || path()
751
+ path: this.lastTrackedPath || path(),
752
+ reason
709
753
  });
710
754
  };
711
- this._emitPageDwell = emitDwell;
755
+ const evaluateDwell = () => {
756
+ if (document.visibilityState !== "visible") return;
757
+ if (Date.now() - state.lastActivityAt >= DWELL_ACTIVITY_IDLE_MS) {
758
+ freezeActivityIdle();
759
+ return;
760
+ }
761
+ emitDwell("milestone", false);
762
+ };
763
+ this._emitPageDwell = (force) => emitDwell("route", force !== false);
712
764
  const onVisibility = () => {
713
765
  if (document.visibilityState === "visible") {
714
766
  const idleMs = state.hiddenAt != null ? Date.now() - state.hiddenAt : 0;
@@ -726,35 +778,51 @@ var AmplifyUpSDK = class {
726
778
  );
727
779
  }
728
780
  }
781
+ noteActivity();
729
782
  state.visibleSince = Date.now();
783
+ state.activityIdle = false;
730
784
  } else {
731
- emitDwell(true);
785
+ emitDwell("hidden", true);
732
786
  state.hiddenAt = Date.now();
733
787
  state.visibleSince = null;
788
+ state.activityIdle = false;
734
789
  }
735
790
  };
736
791
  const onPageHide = () => {
737
- emitDwell(true);
792
+ emitDwell("unload", true);
738
793
  if (state.hiddenAt == null) {
739
794
  state.hiddenAt = Date.now();
740
795
  }
741
796
  state.visibleSince = null;
742
797
  };
798
+ const activityEvents = [
799
+ "pointerdown",
800
+ "keydown",
801
+ "scroll",
802
+ "touchstart"
803
+ ];
804
+ const onActivity = () => noteActivity();
743
805
  window.addEventListener("scroll", onScroll, { passive: true });
744
806
  document.addEventListener("visibilitychange", onVisibility);
745
807
  window.addEventListener("pagehide", onPageHide);
808
+ for (const evt of activityEvents) {
809
+ if (evt === "scroll") continue;
810
+ window.addEventListener(evt, onActivity, { passive: true });
811
+ }
746
812
  checkScroll();
747
813
  const heartbeat = setInterval(() => {
748
- if (document.visibilityState === "visible") {
749
- emitDwell(false);
750
- }
751
- }, 15e3);
814
+ evaluateDwell();
815
+ }, DWELL_POLL_MS);
752
816
  this.trackingIntervals.push(heartbeat);
753
817
  const previousCleanup = this.trackingCleanup;
754
818
  this.trackingCleanup = () => {
755
819
  window.removeEventListener("scroll", onScroll);
756
820
  document.removeEventListener("visibilitychange", onVisibility);
757
821
  window.removeEventListener("pagehide", onPageHide);
822
+ for (const evt of activityEvents) {
823
+ if (evt === "scroll") continue;
824
+ window.removeEventListener(evt, onActivity);
825
+ }
758
826
  if (scrollRaf != null) {
759
827
  window.cancelAnimationFrame(scrollRaf);
760
828
  scrollRaf = null;
@@ -780,6 +848,8 @@ var AmplifyUpSDK = class {
780
848
  state.visibleMs = 0;
781
849
  state.lastDwellSecondsSent = 0;
782
850
  state.hiddenAt = null;
851
+ state.lastActivityAt = Date.now();
852
+ state.activityIdle = false;
783
853
  state.visibleSince = typeof document !== "undefined" && document.visibilityState === "visible" ? Date.now() : null;
784
854
  this.componentViewSeen.clear();
785
855
  }
@@ -2509,6 +2579,53 @@ function useAmplifyUp() {
2509
2579
  }
2510
2580
  return context;
2511
2581
  }
2582
+ var ComponentRenderBoundary = class extends react.Component {
2583
+ constructor() {
2584
+ super(...arguments);
2585
+ this.state = { error: null };
2586
+ }
2587
+ static getDerivedStateFromError(error) {
2588
+ return { error };
2589
+ }
2590
+ componentDidCatch(error) {
2591
+ console.error(
2592
+ `[AmplifyRenderer] "${this.props.componentId}" (${this.props.nodeId}) threw while rendering:`,
2593
+ error
2594
+ );
2595
+ }
2596
+ render() {
2597
+ if (this.state.error) {
2598
+ if (process.env.NODE_ENV !== "production") {
2599
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2600
+ "div",
2601
+ {
2602
+ "data-amplifyup-render-error": "",
2603
+ "data-component-id": this.props.componentId,
2604
+ "data-node-id": this.props.nodeId,
2605
+ style: {
2606
+ margin: "1rem 0",
2607
+ padding: "0.75rem 1rem",
2608
+ border: "1px solid #fca5a5",
2609
+ borderRadius: "0.5rem",
2610
+ background: "#fef2f2",
2611
+ color: "#991b1b",
2612
+ fontSize: "0.875rem"
2613
+ },
2614
+ children: [
2615
+ 'Component "',
2616
+ this.props.componentId,
2617
+ '" failed to render:',
2618
+ " ",
2619
+ this.state.error.message
2620
+ ]
2621
+ }
2622
+ );
2623
+ }
2624
+ return null;
2625
+ }
2626
+ return this.props.children;
2627
+ }
2628
+ };
2512
2629
  function AmplifyRenderer({
2513
2630
  renderComponent,
2514
2631
  pageConfig: serverPageConfig,
@@ -2865,11 +2982,20 @@ function AmplifyRenderer({
2865
2982
  const slotsToPass = Object.keys(slotChildren).length > 0 ? slotChildren : void 0;
2866
2983
  console.log(`[AmplifyRenderer] Calling renderComponent for ${node.componentId}`, {
2867
2984
  hasSlots: !!slotsToPass,
2868
- slotKeys: slotsToPass ? Object.keys(slotsToPass) : []
2985
+ slotKeys: slotsToPass ? Object.keys(slotsToPass) : [],
2986
+ propKeys: Object.keys(node.props || {}),
2987
+ props: node.props || {}
2869
2988
  });
2870
- return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-node-id": node.id, "data-amplifyup-node": "", children: renderComponent(node.componentId, node.props || {}, slotsToPass, {
2871
- layoutNodeId: node.id
2872
- }) });
2989
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-node-id": node.id, "data-amplifyup-node": "", children: /* @__PURE__ */ jsxRuntime.jsx(
2990
+ ComponentRenderBoundary,
2991
+ {
2992
+ componentId: node.componentId,
2993
+ nodeId: node.id,
2994
+ children: renderComponent(node.componentId, node.props || {}, slotsToPass, {
2995
+ layoutNodeId: node.id
2996
+ })
2997
+ }
2998
+ ) });
2873
2999
  };
2874
3000
  const rawTree = pageConfig.layoutTree;
2875
3001
  const layoutTree = Array.isArray(rawTree) ? rawTree : rawTree && typeof rawTree === "object" && rawTree.componentId ? [rawTree] : [];