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