@amplifyup/sdk 0.1.17 → 0.1.19

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/react.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode } from 'react';
3
- import { P as PageConfig, A as AmplifyUpConfig } from './types-B2Swk8BW.mjs';
4
- export { Z as ZoneConfig } from './types-B2Swk8BW.mjs';
3
+ import { P as PageConfig, A as AmplifyUpConfig } from './types-Cy5aWQ5c.mjs';
4
+ export { Z as ZoneConfig } from './types-Cy5aWQ5c.mjs';
5
5
  export { AmplifyRenderer } from './AmplifyRenderer.mjs';
6
6
  export { i as isComposerPreview, a as isPreviewRequest } from './preview-BMrIgUvp.mjs';
7
7
  export { fetchPageConfigServer } from './server.mjs';
package/dist/react.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode } from 'react';
3
- import { P as PageConfig, A as AmplifyUpConfig } from './types-B2Swk8BW.js';
4
- export { Z as ZoneConfig } from './types-B2Swk8BW.js';
3
+ import { P as PageConfig, A as AmplifyUpConfig } from './types-Cy5aWQ5c.js';
4
+ export { Z as ZoneConfig } from './types-Cy5aWQ5c.js';
5
5
  export { AmplifyRenderer } from './AmplifyRenderer.js';
6
6
  export { i as isComposerPreview, a as isPreviewRequest } from './preview-BMrIgUvp.js';
7
7
  export { fetchPageConfigServer } from './server.js';
package/dist/react.js CHANGED
@@ -368,6 +368,9 @@ var AmplifyUpSDK = class {
368
368
  this.attachedEventListeners = [];
369
369
  this.trackingIntervals = [];
370
370
  this.trackingCleanup = null;
371
+ this.componentViewObservers = [];
372
+ this.componentViewSeen = /* @__PURE__ */ new Set();
373
+ this.pageEngagementActive = false;
371
374
  this.adapter = adapter || new AWSAdapter();
372
375
  if (typeof window !== "undefined") {
373
376
  this.isVisualEditorMode = this.detectVisualEditorMode();
@@ -540,6 +543,7 @@ var AmplifyUpSDK = class {
540
543
  this.initialPageSent = true;
541
544
  const url = window.location.pathname;
542
545
  this.page(url);
546
+ this.setupPageEngagement();
543
547
  }
544
548
  }
545
549
  if (this.config?.debug && this.initialized) {
@@ -572,6 +576,168 @@ var AmplifyUpSDK = class {
572
576
  }
573
577
  this.trackingIntervals.forEach((id) => clearInterval(id));
574
578
  this.trackingIntervals = [];
579
+ this.cleanupComponentViewTracking();
580
+ this.pageEngagementActive = false;
581
+ }
582
+ /** Current page path for engagement attribution. */
583
+ currentPath() {
584
+ if (typeof window === "undefined") return "/";
585
+ return window.location.pathname || "/";
586
+ }
587
+ /**
588
+ * Automatic page-level Scroll Depth + Time on Page (dwell heartbeat).
589
+ */
590
+ setupPageEngagement() {
591
+ if (typeof window === "undefined" || typeof document === "undefined") return;
592
+ if (this.isPreviewMode() || this.pageEngagementActive) return;
593
+ this.pageEngagementActive = true;
594
+ const milestones = [25, 50, 75, 100];
595
+ const reached = /* @__PURE__ */ new Set();
596
+ let visibleMs = 0;
597
+ let visibleSince = document.visibilityState === "visible" ? Date.now() : null;
598
+ let lastDwellSecondsSent = 0;
599
+ let scrollRaf = null;
600
+ const path = () => this.currentPath();
601
+ const measureScrollPercent = () => {
602
+ const doc = document.documentElement;
603
+ const body = document.body;
604
+ const scrollTop = window.scrollY || doc.scrollTop || body?.scrollTop || 0;
605
+ const scrollHeight = Math.max(
606
+ doc.scrollHeight,
607
+ body?.scrollHeight || 0,
608
+ doc.offsetHeight,
609
+ body?.offsetHeight || 0
610
+ );
611
+ const clientHeight = window.innerHeight || doc.clientHeight || 0;
612
+ const traversable = Math.max(scrollHeight - clientHeight, 0);
613
+ if (traversable <= 0) return 100;
614
+ return Math.min(100, Math.round(scrollTop / traversable * 100));
615
+ };
616
+ const checkScroll = () => {
617
+ if (this.isPreviewMode()) return;
618
+ const pct = measureScrollPercent();
619
+ for (const milestone of milestones) {
620
+ if (pct >= milestone && !reached.has(milestone)) {
621
+ reached.add(milestone);
622
+ this.track("Scroll Depth", {
623
+ percentage: milestone,
624
+ path: path()
625
+ });
626
+ }
627
+ }
628
+ };
629
+ const onScroll = () => {
630
+ if (scrollRaf != null) return;
631
+ scrollRaf = window.requestAnimationFrame(() => {
632
+ scrollRaf = null;
633
+ checkScroll();
634
+ });
635
+ };
636
+ const accumulateVisible = () => {
637
+ if (visibleSince == null) return;
638
+ visibleMs += Date.now() - visibleSince;
639
+ visibleSince = Date.now();
640
+ };
641
+ const emitDwell = (force = false) => {
642
+ if (this.isPreviewMode()) return;
643
+ accumulateVisible();
644
+ const seconds = Math.floor(visibleMs / 1e3);
645
+ if (seconds <= 0) return;
646
+ if (!force && seconds <= lastDwellSecondsSent) return;
647
+ lastDwellSecondsSent = seconds;
648
+ this.track("Time on Page", {
649
+ seconds,
650
+ path: path()
651
+ });
652
+ };
653
+ const onVisibility = () => {
654
+ if (document.visibilityState === "visible") {
655
+ visibleSince = Date.now();
656
+ } else {
657
+ emitDwell(true);
658
+ visibleSince = null;
659
+ }
660
+ };
661
+ const onPageHide = () => {
662
+ emitDwell(true);
663
+ };
664
+ window.addEventListener("scroll", onScroll, { passive: true });
665
+ document.addEventListener("visibilitychange", onVisibility);
666
+ window.addEventListener("pagehide", onPageHide);
667
+ checkScroll();
668
+ const heartbeat = setInterval(() => {
669
+ if (document.visibilityState === "visible") {
670
+ emitDwell(false);
671
+ }
672
+ }, 15e3);
673
+ this.trackingIntervals.push(heartbeat);
674
+ const previousCleanup = this.trackingCleanup;
675
+ this.trackingCleanup = () => {
676
+ window.removeEventListener("scroll", onScroll);
677
+ document.removeEventListener("visibilitychange", onVisibility);
678
+ window.removeEventListener("pagehide", onPageHide);
679
+ if (scrollRaf != null) {
680
+ window.cancelAnimationFrame(scrollRaf);
681
+ scrollRaf = null;
682
+ }
683
+ previousCleanup?.();
684
+ this.pageEngagementActive = false;
685
+ };
686
+ if (this.config?.debug) {
687
+ console.log("[AmplifyUp SDK] Page engagement tracking enabled");
688
+ }
689
+ }
690
+ /**
691
+ * Automatic Component View when component hosts enter the viewport.
692
+ */
693
+ setupComponentViewTracking() {
694
+ if (typeof document === "undefined" || typeof IntersectionObserver === "undefined") {
695
+ return;
696
+ }
697
+ if (this.isPreviewMode()) return;
698
+ this.cleanupComponentViewTracking();
699
+ const hosts = document.querySelectorAll("[data-amplifyup-component]");
700
+ if (hosts.length === 0) {
701
+ if (this.config?.debug) {
702
+ console.log("[AmplifyUp SDK] No component hosts found for Component View");
703
+ }
704
+ return;
705
+ }
706
+ const observer = new IntersectionObserver(
707
+ (entries) => {
708
+ entries.forEach((entry) => {
709
+ if (!entry.isIntersecting) return;
710
+ const el = entry.target;
711
+ const componentId = el.getAttribute("data-amplifyup-component") || el.getAttribute("data-component-id");
712
+ if (!componentId || this.componentViewSeen.has(componentId)) {
713
+ observer.unobserve(el);
714
+ return;
715
+ }
716
+ this.componentViewSeen.add(componentId);
717
+ const componentName = el.getAttribute("data-component-name") || void 0;
718
+ this.track("Component View", {
719
+ componentId,
720
+ componentName,
721
+ path: this.currentPath()
722
+ });
723
+ observer.unobserve(el);
724
+ });
725
+ },
726
+ { threshold: 0.5 }
727
+ );
728
+ hosts.forEach((host) => observer.observe(host));
729
+ this.componentViewObservers.push(observer);
730
+ if (this.config?.debug) {
731
+ console.log(
732
+ "[AmplifyUp SDK] Component View tracking enabled for",
733
+ hosts.length,
734
+ "hosts"
735
+ );
736
+ }
737
+ }
738
+ cleanupComponentViewTracking() {
739
+ this.componentViewObservers.forEach((obs) => obs.disconnect());
740
+ this.componentViewObservers = [];
575
741
  }
576
742
  /**
577
743
  * Track a page view
@@ -611,9 +777,13 @@ var AmplifyUpSDK = class {
611
777
  instanceId,
612
778
  trackingId: this.config?.trackingId,
613
779
  url: typeof window !== "undefined" ? window.location.href : void 0,
780
+ path: properties?.path || (typeof window !== "undefined" ? window.location.pathname : void 0),
614
781
  // Timestamp is included in event payload
615
782
  ...properties
616
783
  };
784
+ if (!trackProperties.path && typeof window !== "undefined") {
785
+ trackProperties.path = window.location.pathname;
786
+ }
617
787
  this.adapter.track(eventName, trackProperties);
618
788
  }
619
789
  /**
@@ -902,6 +1072,13 @@ Available Console Commands:
902
1072
  if (pageConfig?.eventTracking) {
903
1073
  this.setupEventTracking(pageConfig.eventTracking);
904
1074
  }
1075
+ if (typeof window !== "undefined") {
1076
+ window.setTimeout(() => this.setupComponentViewTracking(), 150);
1077
+ window.setTimeout(() => {
1078
+ this.setupComponentViewTracking();
1079
+ this.refreshEventTracking();
1080
+ }, 800);
1081
+ }
905
1082
  return pageConfig;
906
1083
  }
907
1084
  /**
@@ -909,10 +1086,10 @@ Available Console Commands:
909
1086
  */
910
1087
  setupEventTracking(eventConfig) {
911
1088
  if (typeof document === "undefined") return;
912
- this.cleanupEventTracking();
913
- this.eventTrackingConfig = eventConfig;
1089
+ this.detachEventListeners();
1090
+ this.eventTrackingConfig = Array.isArray(eventConfig) ? [...eventConfig] : [];
914
1091
  if (this.config?.debug) {
915
- console.log("[AmplifyUp SDK] Setting up event tracking:", eventConfig.length, "events");
1092
+ console.log("[AmplifyUp SDK] Setting up event tracking:", this.eventTrackingConfig.length, "events");
916
1093
  }
917
1094
  if (document.readyState === "loading") {
918
1095
  document.addEventListener("DOMContentLoaded", () => this.attachEventListeners());
@@ -927,19 +1104,35 @@ Available Console Commands:
927
1104
  if (typeof document === "undefined") return;
928
1105
  this.eventTrackingConfig.forEach((eventConfig) => {
929
1106
  if (eventConfig.enabled === false) return;
1107
+ if (!eventConfig.selector) {
1108
+ if (this.config?.debug) {
1109
+ console.warn(`[AmplifyUp SDK] Event ${eventConfig.eventName} has no selector, skipping`);
1110
+ }
1111
+ return;
1112
+ }
930
1113
  let targetElements = [];
931
- if (eventConfig.selector) {
932
- try {
933
- targetElements = document.querySelectorAll(eventConfig.selector);
934
- } catch (e) {
1114
+ try {
1115
+ const escapeAttr = (value) => typeof CSS !== "undefined" && typeof CSS.escape === "function" ? CSS.escape(value) : value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
1116
+ const scopeRoot = eventConfig.componentId ? document.querySelector(
1117
+ `[data-amplifyup-component="${escapeAttr(eventConfig.componentId)}"], [data-component-id="${escapeAttr(eventConfig.componentId)}"]`
1118
+ ) : document;
1119
+ if (!scopeRoot) {
935
1120
  if (this.config?.debug) {
936
- console.warn(`[AmplifyUp SDK] Invalid selector: ${eventConfig.selector}`, e);
1121
+ console.log(
1122
+ `[AmplifyUp SDK] Component host not found for ${eventConfig.componentId}`
1123
+ );
937
1124
  }
938
1125
  return;
939
1126
  }
940
- } else {
1127
+ targetElements = Array.from(
1128
+ scopeRoot.querySelectorAll(eventConfig.selector)
1129
+ );
1130
+ if (scopeRoot instanceof Element && scopeRoot !== document.documentElement && scopeRoot.matches?.(eventConfig.selector)) {
1131
+ targetElements.unshift(scopeRoot);
1132
+ }
1133
+ } catch (e) {
941
1134
  if (this.config?.debug) {
942
- console.warn(`[AmplifyUp SDK] Event ${eventConfig.eventName} has no selector, skipping`);
1135
+ console.warn(`[AmplifyUp SDK] Invalid selector: ${eventConfig.selector}`, e);
943
1136
  }
944
1137
  return;
945
1138
  }
@@ -995,8 +1188,15 @@ Available Console Commands:
995
1188
  handleTrackedEvent(event, config, element) {
996
1189
  const properties = {
997
1190
  eventType: config.eventType,
998
- selector: config.selector
1191
+ selector: config.selector,
1192
+ path: this.currentPath()
999
1193
  };
1194
+ if (config.componentId) {
1195
+ properties.componentId = config.componentId;
1196
+ }
1197
+ if (config.componentName) {
1198
+ properties.componentName = config.componentName;
1199
+ }
1000
1200
  if (config.properties && config.properties.length > 0) {
1001
1201
  config.properties.forEach((prop) => {
1002
1202
  const dataAttrValue = element.getAttribute(`data-${prop}`);
@@ -1067,9 +1267,9 @@ Available Console Commands:
1067
1267
  element._amplifyupViewObserver = observer;
1068
1268
  }
1069
1269
  /**
1070
- * Clean up all attached event listeners
1270
+ * Detach listeners/observers without clearing the saved event config.
1071
1271
  */
1072
- cleanupEventTracking() {
1272
+ detachEventListeners() {
1073
1273
  this.attachedEventListeners.forEach(({ element, eventType, handler }) => {
1074
1274
  element.removeEventListener(eventType, handler);
1075
1275
  });
@@ -1083,6 +1283,12 @@ Available Console Commands:
1083
1283
  }
1084
1284
  });
1085
1285
  }
1286
+ }
1287
+ /**
1288
+ * Clean up all attached event listeners and clear config
1289
+ */
1290
+ cleanupEventTracking() {
1291
+ this.detachEventListeners();
1086
1292
  this.eventTrackingConfig = [];
1087
1293
  if (this.config?.debug) {
1088
1294
  console.log("[AmplifyUp SDK] Cleaned up event tracking");
@@ -1092,11 +1298,13 @@ Available Console Commands:
1092
1298
  * Manually trigger event tracking setup (useful after dynamic content loads)
1093
1299
  */
1094
1300
  refreshEventTracking() {
1095
- if (this.eventTrackingConfig.length > 0) {
1096
- this.cleanupEventTracking();
1097
- const config = [...this.eventTrackingConfig];
1098
- this.eventTrackingConfig = config;
1099
- this.attachEventListeners();
1301
+ if (this.eventTrackingConfig.length === 0) return;
1302
+ const config = [...this.eventTrackingConfig];
1303
+ this.detachEventListeners();
1304
+ this.eventTrackingConfig = config;
1305
+ this.attachEventListeners();
1306
+ if (this.config?.debug) {
1307
+ console.log("[AmplifyUp SDK] Refreshed event tracking:", config.length, "events");
1100
1308
  }
1101
1309
  }
1102
1310
  /** Origins allowed to send Composer postMessage commands to the SDK. */
@@ -2089,6 +2297,7 @@ function getSDK(adapter) {
2089
2297
  return sdkInstance;
2090
2298
  }
2091
2299
  var trackContentView = (contentId, contentType, properties) => getSDK().trackContentView(contentId, contentType, properties);
2300
+ var refreshEventTracking = () => getSDK().refreshEventTracking();
2092
2301
  getSDK();
2093
2302
  function AmplifyRenderer({
2094
2303
  renderComponent,
@@ -2129,6 +2338,32 @@ function AmplifyRenderer({
2129
2338
  });
2130
2339
  }
2131
2340
  }, [pageConfig]);
2341
+ react.useEffect(() => {
2342
+ if (!pageConfig?.eventTracking?.length) return;
2343
+ const sdk = getSDK();
2344
+ if (!sdk.isInitialized()) return;
2345
+ sdk.setupEventTracking(pageConfig.eventTracking);
2346
+ const t1 = window.setTimeout(() => {
2347
+ sdk.setupComponentViewTracking();
2348
+ refreshEventTracking();
2349
+ }, 200);
2350
+ const t2 = window.setTimeout(() => {
2351
+ sdk.setupComponentViewTracking();
2352
+ refreshEventTracking();
2353
+ }, 1e3);
2354
+ return () => {
2355
+ window.clearTimeout(t1);
2356
+ window.clearTimeout(t2);
2357
+ };
2358
+ }, [pageConfig?.id]);
2359
+ react.useEffect(() => {
2360
+ if (!pageConfig) return;
2361
+ const t = window.setTimeout(() => {
2362
+ getSDK().setupComponentViewTracking();
2363
+ refreshEventTracking();
2364
+ }, 100);
2365
+ return () => window.clearTimeout(t);
2366
+ }, [variantCache, pageConfig?.id]);
2132
2367
  react.useEffect(() => {
2133
2368
  const isVisualEditorMode = () => {
2134
2369
  if (typeof window === "undefined") return false;
@@ -2334,6 +2569,31 @@ function AmplifyRenderer({
2334
2569
  pageConfig.layout?.header !== false;
2335
2570
  pageConfig.layout?.footer !== false;
2336
2571
  const renderNode = (node) => {
2572
+ const zoneIdFromProps = node.componentId === "AmplifyZone" ? String(node.props?.zoneId || "") : "";
2573
+ if (node.componentId === "AmplifyZone") {
2574
+ const contentSlot = node.slots?.find((s) => s.slotId === "content") || node.slots?.[0];
2575
+ const children = Array.isArray(contentSlot?.children) ? contentSlot.children : [];
2576
+ return /* @__PURE__ */ jsxRuntime.jsx(
2577
+ "div",
2578
+ {
2579
+ "data-node-id": node.id,
2580
+ "data-amplifyup-node": "",
2581
+ "data-amplifyup-component": "AmplifyZone",
2582
+ "data-amplifyup-zone": zoneIdFromProps || void 0,
2583
+ "data-zone-id": zoneIdFromProps || void 0,
2584
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2585
+ "div",
2586
+ {
2587
+ "data-slot-id": "content",
2588
+ "data-amplifyup-slot": "content",
2589
+ "data-parent-node-id": node.id,
2590
+ "data-parent-component-id": "AmplifyZone",
2591
+ children: children.filter(Boolean).map((child) => /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: renderNode(child) }, child.id))
2592
+ }
2593
+ )
2594
+ }
2595
+ );
2596
+ }
2337
2597
  let slotChildren = {};
2338
2598
  console.log(`[AmplifyRenderer] Rendering node ${node.id} (${node.componentId})`, {
2339
2599
  hasSlots: !!(node.slots && node.slots.length > 0),