@aranova/tracking-next 0.4.2 → 0.5.1

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.js CHANGED
@@ -254,7 +254,7 @@ function getVisitorId() {
254
254
  }
255
255
  function getOrRotateSessionId(now = Date.now()) {
256
256
  if (typeof window === "undefined")
257
- return safeUuid();
257
+ return { id: safeUuid(), isNew: true };
258
258
  const raw = readLocalStorage(SESSION_STORAGE_KEY);
259
259
  if (raw) {
260
260
  try {
@@ -263,7 +263,7 @@ function getOrRotateSessionId(now = Date.now()) {
263
263
  if (now - parsed.last_event_at <= SESSION_IDLE_MS) {
264
264
  const refreshed = { id: parsed.id, last_event_at: now };
265
265
  writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(refreshed));
266
- return parsed.id;
266
+ return { id: parsed.id, isNew: false };
267
267
  }
268
268
  }
269
269
  } catch {
@@ -271,7 +271,7 @@ function getOrRotateSessionId(now = Date.now()) {
271
271
  }
272
272
  const fresh = { id: safeUuid(), last_event_at: now };
273
273
  writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(fresh));
274
- return fresh.id;
274
+ return { id: fresh.id, isNew: true };
275
275
  }
276
276
 
277
277
  // ../tracking-core/src/events/page-view.ts
@@ -371,6 +371,56 @@ function attachBfcacheRestore(client) {
371
371
  };
372
372
  }
373
373
 
374
+ // ../tracking-core/src/heartbeat.ts
375
+ function serializeValue(value) {
376
+ if (value instanceof RegExp) return value.source;
377
+ if (Array.isArray(value)) return value.map(serializeValue);
378
+ if (value !== null && typeof value === "object") {
379
+ const out = {};
380
+ for (const [k, v] of Object.entries(value)) {
381
+ out[k] = serializeValue(v);
382
+ }
383
+ return out;
384
+ }
385
+ return value;
386
+ }
387
+ function buildHeartbeatMetadata(surface, sdkVersion, packageName, triggers) {
388
+ const automaticNames = triggers ? Object.keys(triggers.automatic) : [];
389
+ const manualNames = triggers?.manual ? Object.keys(triggers.manual) : [];
390
+ let triggerConfig = null;
391
+ if (triggers) {
392
+ const cfg = {};
393
+ for (const [name, config] of Object.entries(triggers.automatic)) {
394
+ const serialized = serializeValue(config);
395
+ if (Object.keys(serialized).length > 0) {
396
+ cfg[name] = serialized;
397
+ }
398
+ }
399
+ if (triggers.manual) {
400
+ for (const [name, config] of Object.entries(triggers.manual)) {
401
+ if (config === void 0) continue;
402
+ const serialized = serializeValue(config);
403
+ if (Object.keys(serialized).length > 0) {
404
+ cfg[name] = serialized;
405
+ }
406
+ }
407
+ }
408
+ if (Object.keys(cfg).length > 0) {
409
+ triggerConfig = cfg;
410
+ }
411
+ }
412
+ return {
413
+ sdk_version: sdkVersion ?? "unknown",
414
+ package_name: packageName,
415
+ surface,
416
+ triggers: {
417
+ automatic: automaticNames,
418
+ manual: manualNames
419
+ },
420
+ trigger_config: triggerConfig
421
+ };
422
+ }
423
+
374
424
  // ../tracking-core/src/ingest.ts
375
425
  var DEFAULT_FLUSH_INTERVAL_MS = 2e3;
376
426
  var DEFAULT_MAX_QUEUE_SIZE = 10;
@@ -450,11 +500,33 @@ function createTrackingClient(config) {
450
500
  let firstPage = null;
451
501
  let destroyed = false;
452
502
  const visitorId = getVisitorId();
453
- let sessionId = getOrRotateSessionId();
503
+ const initialSession = getOrRotateSessionId();
504
+ let sessionId = initialSession.id;
454
505
  if (typeof window !== "undefined")
455
506
  firstPage = window.location.href;
507
+ function enqueueHeartbeat() {
508
+ const metadata = buildHeartbeatMetadata(
509
+ config.surface,
510
+ sdkVersion,
511
+ packageName,
512
+ config.triggers ?? null
513
+ );
514
+ queue.push({
515
+ event_type: "sdk_heartbeat",
516
+ page_url: typeof window === "undefined" ? null : window.location.href,
517
+ metadata,
518
+ occurred_at: (/* @__PURE__ */ new Date()).toISOString()
519
+ });
520
+ }
521
+ if (initialSession.isNew) {
522
+ enqueueHeartbeat();
523
+ }
456
524
  function buildSessionPayload() {
457
- sessionId = getOrRotateSessionId();
525
+ const rotated = getOrRotateSessionId();
526
+ if (rotated.isNew && rotated.id !== sessionId) {
527
+ enqueueHeartbeat();
528
+ }
529
+ sessionId = rotated.id;
458
530
  const params = readTrackingParams();
459
531
  const context = buildContext(config.surface, sdkVersion, packageName);
460
532
  return {
@@ -555,60 +627,144 @@ function createTrackingClient(config) {
555
627
  };
556
628
  }
557
629
 
558
- // ../tracking-core/src/events/contact-page-visit.ts
630
+ // ../tracking-core/src/events/cta-click.ts
559
631
  var import_zod2 = require("zod");
560
- var contactPageVisitMetadataSchema = import_zod2.z.object({
632
+ var ctaClickMetadataSchema = import_zod2.z.object({
633
+ cta_name: import_zod2.z.string(),
561
634
  page: import_zod2.z.object({
562
635
  path: import_zod2.z.string()
563
- })
564
- }).strict();
565
- var contactPageVisitConfigSchema = import_zod2.z.object({
566
- // RegExp is runtime-only so we wrap it via z.custom. Consumers pass a
567
- // real regex at createTracking() time; the factory validates with this
568
- // schema and then keeps the live RegExp reference for runtime matching.
569
- pathPattern: import_zod2.z.custom(
570
- (value) => value instanceof RegExp,
571
- { message: "pathPattern must be a RegExp" }
572
- )
636
+ }),
637
+ section: import_zod2.z.string().nullable().optional(),
638
+ destination_url: import_zod2.z.string().nullable().optional()
573
639
  }).strict();
640
+ var ctaClickConfigSchema = import_zod2.z.object({}).strict();
574
641
 
575
- // ../tracking-core/src/events/form-submit.ts
642
+ // ../tracking-core/src/events/sdk-heartbeat.ts
576
643
  var import_zod3 = require("zod");
577
- var formSubmitMetadataSchema = import_zod3.z.object({
578
- form: import_zod3.z.object({
579
- id: import_zod3.z.string(),
580
- action: import_zod3.z.string().nullable()
581
- }),
582
- page: import_zod3.z.object({
583
- path: import_zod3.z.string()
584
- })
644
+ var sdkHeartbeatTriggersSchema = import_zod3.z.object({
645
+ automatic: import_zod3.z.array(import_zod3.z.string()),
646
+ manual: import_zod3.z.array(import_zod3.z.string())
585
647
  }).strict();
586
- var formSubmitConfigSchema = import_zod3.z.object({}).strict();
648
+ var sdkHeartbeatMetadataSchema = import_zod3.z.object({
649
+ sdk_version: import_zod3.z.string(),
650
+ package_name: import_zod3.z.string().nullable(),
651
+ surface: import_zod3.z.enum(["next", "react", "script"]),
652
+ triggers: sdkHeartbeatTriggersSchema,
653
+ trigger_config: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.record(import_zod3.z.string(), import_zod3.z.unknown())).nullable().optional()
654
+ }).strict();
655
+ var sdkHeartbeatConfigSchema = import_zod3.z.object({}).strict();
587
656
 
588
- // ../tracking-core/src/events/phone-click.ts
657
+ // ../tracking-core/src/events/form-start.ts
589
658
  var import_zod4 = require("zod");
590
- var phoneClickMetadataSchema = import_zod4.z.object({
591
- element: import_zod4.z.object({
592
- tag: import_zod4.z.string(),
593
- text: import_zod4.z.string().nullable(),
594
- href: import_zod4.z.string()
659
+ var formStartMetadataSchema = import_zod4.z.object({
660
+ form: import_zod4.z.object({
661
+ id: import_zod4.z.string(),
662
+ action: import_zod4.z.string().nullable()
595
663
  }),
596
664
  page: import_zod4.z.object({
597
665
  path: import_zod4.z.string()
598
666
  })
599
667
  }).strict();
600
- var phoneClickConfigSchema = import_zod4.z.object({}).strict();
668
+ var formStartConfigSchema = import_zod4.z.object({
669
+ selector: import_zod4.z.string().optional()
670
+ }).strict();
601
671
 
602
- // ../tracking-core/src/events/time-on-site.ts
672
+ // ../tracking-core/src/events/form-submit.ts
603
673
  var import_zod5 = require("zod");
604
- var timeOnSiteMetadataSchema = import_zod5.z.object({
605
- duration_ms: import_zod5.z.number().int().nonnegative(),
674
+ var formSubmitMetadataSchema = import_zod5.z.object({
675
+ form: import_zod5.z.object({
676
+ id: import_zod5.z.string(),
677
+ action: import_zod5.z.string().nullable(),
678
+ fields: import_zod5.z.array(
679
+ import_zod5.z.object({
680
+ name: import_zod5.z.string(),
681
+ type: import_zod5.z.string(),
682
+ label: import_zod5.z.string().nullable(),
683
+ has_value: import_zod5.z.boolean()
684
+ })
685
+ ).optional()
686
+ }),
606
687
  page: import_zod5.z.object({
607
688
  path: import_zod5.z.string()
608
689
  })
609
690
  }).strict();
610
- var timeOnSiteConfigSchema = import_zod5.z.object({
611
- thresholdSeconds: import_zod5.z.number().int().positive()
691
+ var formSubmitConfigSchema = import_zod5.z.object({}).strict();
692
+
693
+ // ../tracking-core/src/events/multi-page-session.ts
694
+ var import_zod6 = require("zod");
695
+ var multiPageSessionMetadataSchema = import_zod6.z.object({
696
+ page_count: import_zod6.z.number().int().min(2),
697
+ page: import_zod6.z.object({
698
+ path: import_zod6.z.string()
699
+ })
700
+ }).strict();
701
+ var multiPageSessionConfigSchema = import_zod6.z.object({
702
+ pageThreshold: import_zod6.z.number().int().min(2)
703
+ }).strict();
704
+
705
+ // ../tracking-core/src/events/phone-click.ts
706
+ var import_zod7 = require("zod");
707
+ var phoneClickMetadataSchema = import_zod7.z.object({
708
+ phone_number: import_zod7.z.string(),
709
+ page: import_zod7.z.object({
710
+ path: import_zod7.z.string()
711
+ }),
712
+ section: import_zod7.z.string().nullable().optional()
713
+ }).strict();
714
+ var phoneClickConfigSchema = import_zod7.z.object({}).strict();
715
+
716
+ // ../tracking-core/src/events/scroll-depth.ts
717
+ var import_zod8 = require("zod");
718
+ var scrollDepthMetadataSchema = import_zod8.z.object({
719
+ depth_percent: import_zod8.z.number().int().min(1).max(100),
720
+ page: import_zod8.z.object({
721
+ path: import_zod8.z.string()
722
+ })
723
+ }).strict();
724
+ var scrollDepthConfigSchema = import_zod8.z.object({
725
+ thresholds: import_zod8.z.array(import_zod8.z.number().int().min(1).max(100)).min(1)
726
+ }).strict();
727
+
728
+ // ../tracking-core/src/events/specific-page-visit.ts
729
+ var import_zod9 = require("zod");
730
+ var SPECIFIC_PAGE_NAMES = [
731
+ "contact_page",
732
+ "about_page",
733
+ "services_page",
734
+ "booking_page",
735
+ "location_page",
736
+ "pricing_page",
737
+ "faq_page",
738
+ "testimonials_page"
739
+ ];
740
+ var specificPageNameSchema = import_zod9.z.enum(SPECIFIC_PAGE_NAMES);
741
+ var specificPageVisitMetadataSchema = import_zod9.z.object({
742
+ page_name: specificPageNameSchema,
743
+ page: import_zod9.z.object({
744
+ path: import_zod9.z.string()
745
+ })
746
+ }).strict();
747
+ var specificPageVisitConfigSchema = import_zod9.z.object({
748
+ pages: import_zod9.z.array(
749
+ import_zod9.z.object({
750
+ name: specificPageNameSchema,
751
+ pathPattern: import_zod9.z.custom((value) => value instanceof RegExp, {
752
+ message: "pathPattern must be a RegExp"
753
+ })
754
+ })
755
+ ).min(1)
756
+ }).strict();
757
+
758
+ // ../tracking-core/src/events/time-on-site.ts
759
+ var import_zod10 = require("zod");
760
+ var timeOnSiteMetadataSchema = import_zod10.z.object({
761
+ duration_ms: import_zod10.z.number().int().nonnegative(),
762
+ page: import_zod10.z.object({
763
+ path: import_zod10.z.string()
764
+ })
765
+ }).strict();
766
+ var timeOnSiteConfigSchema = import_zod10.z.object({
767
+ thresholdSeconds: import_zod10.z.number().int().positive()
612
768
  }).strict();
613
769
 
614
770
  // ../tracking-core/src/events/registry.ts
@@ -624,10 +780,31 @@ var EVENT_REGISTRY = {
624
780
  metadataSchema: timeOnSiteMetadataSchema,
625
781
  configSchema: timeOnSiteConfigSchema
626
782
  },
627
- contact_page_visit: {
783
+ specific_page_visit: {
784
+ kind: "automatic",
785
+ metadataSchema: specificPageVisitMetadataSchema,
786
+ configSchema: specificPageVisitConfigSchema
787
+ },
788
+ scroll_depth: {
789
+ kind: "automatic",
790
+ metadataSchema: scrollDepthMetadataSchema,
791
+ configSchema: scrollDepthConfigSchema
792
+ },
793
+ multi_page_session: {
794
+ kind: "automatic",
795
+ metadataSchema: multiPageSessionMetadataSchema,
796
+ configSchema: multiPageSessionConfigSchema
797
+ },
798
+ form_start: {
799
+ kind: "automatic",
800
+ metadataSchema: formStartMetadataSchema,
801
+ configSchema: formStartConfigSchema
802
+ },
803
+ // --- SDK-internal automatic (not consumer-configurable) ---
804
+ sdk_heartbeat: {
628
805
  kind: "automatic",
629
- metadataSchema: contactPageVisitMetadataSchema,
630
- configSchema: contactPageVisitConfigSchema
806
+ metadataSchema: sdkHeartbeatMetadataSchema,
807
+ configSchema: sdkHeartbeatConfigSchema
631
808
  },
632
809
  // --- manual triggers ---
633
810
  form_submit: {
@@ -639,6 +816,11 @@ var EVENT_REGISTRY = {
639
816
  kind: "manual",
640
817
  metadataSchema: phoneClickMetadataSchema,
641
818
  configSchema: phoneClickConfigSchema
819
+ },
820
+ cta_click: {
821
+ kind: "manual",
822
+ metadataSchema: ctaClickMetadataSchema,
823
+ configSchema: ctaClickConfigSchema
642
824
  }
643
825
  };
644
826
  var ALL_AUTOMATIC_EVENT_NAMES = Object.entries(EVENT_REGISTRY).filter(([, def]) => def.kind === "automatic").map(([name]) => name);
@@ -729,6 +911,144 @@ function attachTimeOnSite(client, config) {
729
911
  };
730
912
  }
731
913
 
914
+ // ../tracking-core/src/triggers/scroll-depth.ts
915
+ function attachScrollDepth(client, config) {
916
+ if (typeof window === "undefined" || typeof document === "undefined") {
917
+ return () => {
918
+ };
919
+ }
920
+ const thresholds = new Set(config.thresholds);
921
+ let firedForPath = /* @__PURE__ */ new Set();
922
+ let currentPath = window.location.pathname;
923
+ let rafId = null;
924
+ function getScrollPercent() {
925
+ const doc = document.documentElement;
926
+ const scrollTop = window.scrollY || doc.scrollTop;
927
+ const scrollHeight = doc.scrollHeight;
928
+ const clientHeight = doc.clientHeight;
929
+ if (scrollHeight <= clientHeight) return 100;
930
+ return Math.round((scrollTop + clientHeight) / scrollHeight * 100);
931
+ }
932
+ function checkThresholds() {
933
+ const percent = getScrollPercent();
934
+ for (const threshold of thresholds) {
935
+ if (percent >= threshold && !firedForPath.has(threshold)) {
936
+ firedForPath.add(threshold);
937
+ client.trackEvent({
938
+ eventType: "scroll_depth",
939
+ metadata: {
940
+ depth_percent: threshold,
941
+ page: { path: currentPath }
942
+ },
943
+ pageUrl: window.location.href,
944
+ occurredAt: null
945
+ });
946
+ }
947
+ }
948
+ }
949
+ function onScroll() {
950
+ if (rafId !== null) return;
951
+ rafId = requestAnimationFrame(() => {
952
+ rafId = null;
953
+ checkThresholds();
954
+ });
955
+ }
956
+ function resetIfPathChanged() {
957
+ const newPath = window.location.pathname;
958
+ if (newPath === currentPath) return;
959
+ currentPath = newPath;
960
+ firedForPath = /* @__PURE__ */ new Set();
961
+ setTimeout(checkThresholds, 0);
962
+ }
963
+ const originalPushState = history.pushState.bind(history);
964
+ const originalReplaceState = history.replaceState.bind(history);
965
+ function patchedPushState(...args) {
966
+ originalPushState(...args);
967
+ setTimeout(resetIfPathChanged, 0);
968
+ }
969
+ function patchedReplaceState(...args) {
970
+ originalReplaceState(...args);
971
+ setTimeout(resetIfPathChanged, 0);
972
+ }
973
+ history.pushState = patchedPushState;
974
+ history.replaceState = patchedReplaceState;
975
+ window.addEventListener("popstate", resetIfPathChanged);
976
+ window.addEventListener("scroll", onScroll, { passive: true });
977
+ setTimeout(checkThresholds, 0);
978
+ return () => {
979
+ if (rafId !== null) cancelAnimationFrame(rafId);
980
+ history.pushState = originalPushState;
981
+ history.replaceState = originalReplaceState;
982
+ window.removeEventListener("popstate", resetIfPathChanged);
983
+ window.removeEventListener("scroll", onScroll);
984
+ };
985
+ }
986
+
987
+ // ../tracking-core/src/triggers/form-start.ts
988
+ function attachFormStart(client, config) {
989
+ if (typeof window === "undefined" || typeof document === "undefined") {
990
+ return () => {
991
+ };
992
+ }
993
+ const selector = config.selector ?? "form";
994
+ let firedForms = /* @__PURE__ */ new Set();
995
+ let currentPath = window.location.pathname;
996
+ function getFormKey(form) {
997
+ if (form.id) return `id:${form.id}`;
998
+ const explicitAction = form.getAttribute("action");
999
+ if (explicitAction) return `action:${explicitAction}`;
1000
+ const forms = Array.from(document.querySelectorAll(selector));
1001
+ return `index:${forms.indexOf(form)}`;
1002
+ }
1003
+ function onFocusIn(event) {
1004
+ const target = event.target;
1005
+ if (!(target instanceof HTMLElement)) return;
1006
+ const form = target.closest(selector);
1007
+ if (!form || form.tagName !== "FORM") return;
1008
+ const key = getFormKey(form);
1009
+ if (firedForms.has(key)) return;
1010
+ firedForms.add(key);
1011
+ client.trackEvent({
1012
+ eventType: "form_start",
1013
+ metadata: {
1014
+ form: {
1015
+ id: form.id || "",
1016
+ action: form.getAttribute("action") ?? null
1017
+ },
1018
+ page: { path: window.location.pathname }
1019
+ },
1020
+ pageUrl: window.location.href,
1021
+ occurredAt: null
1022
+ });
1023
+ }
1024
+ function resetIfPathChanged() {
1025
+ const newPath = window.location.pathname;
1026
+ if (newPath === currentPath) return;
1027
+ currentPath = newPath;
1028
+ firedForms = /* @__PURE__ */ new Set();
1029
+ }
1030
+ const originalPushState = history.pushState.bind(history);
1031
+ const originalReplaceState = history.replaceState.bind(history);
1032
+ function patchedPushState(...args) {
1033
+ originalPushState(...args);
1034
+ setTimeout(resetIfPathChanged, 0);
1035
+ }
1036
+ function patchedReplaceState(...args) {
1037
+ originalReplaceState(...args);
1038
+ setTimeout(resetIfPathChanged, 0);
1039
+ }
1040
+ history.pushState = patchedPushState;
1041
+ history.replaceState = patchedReplaceState;
1042
+ window.addEventListener("popstate", resetIfPathChanged);
1043
+ document.addEventListener("focusin", onFocusIn);
1044
+ return () => {
1045
+ history.pushState = originalPushState;
1046
+ history.replaceState = originalReplaceState;
1047
+ window.removeEventListener("popstate", resetIfPathChanged);
1048
+ document.removeEventListener("focusin", onFocusIn);
1049
+ };
1050
+ }
1051
+
732
1052
  // src/ConsentBanner.tsx
733
1053
  var import_react = require("react");
734
1054
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -828,7 +1148,7 @@ function GoogleAdsTracking({ gtagId }) {
828
1148
  var import_navigation = require("next/navigation");
829
1149
 
830
1150
  // package.json
831
- var version = "0.4.2";
1151
+ var version = "0.5.1";
832
1152
 
833
1153
  // src/factory.tsx
834
1154
  var import_react3 = require("react");
@@ -855,7 +1175,7 @@ function createTracking(options) {
855
1175
  };
856
1176
  }
857
1177
  const TrackingContext = (0, import_react3.createContext)(null);
858
- const contactPageVisitConfig = triggers.automatic.contact_page_visit ?? null;
1178
+ const specificPageVisitConfig = triggers.automatic.specific_page_visit ?? null;
859
1179
  const timeOnSiteConfig = triggers.automatic.time_on_site ?? null;
860
1180
  function TrackingProvider({ children }) {
861
1181
  const pathname = (0, import_navigation.usePathname)();
@@ -868,6 +1188,7 @@ function createTracking(options) {
868
1188
  surface: "next",
869
1189
  packageName: "@aranova/tracking-next",
870
1190
  sdkVersion: version,
1191
+ triggers,
871
1192
  debug
872
1193
  }),
873
1194
  triggers,
@@ -881,29 +1202,34 @@ function createTracking(options) {
881
1202
  endpoint,
882
1203
  surface: "next",
883
1204
  packageName: "@aranova/tracking-next",
1205
+ triggers,
884
1206
  debug
885
1207
  });
886
1208
  fireManualPageView(rawClient);
887
1209
  }, [pathname, searchParams]);
888
- const lastContactPathRef = (0, import_react3.useRef)(null);
1210
+ const firedPagesRef = (0, import_react3.useRef)(/* @__PURE__ */ new Set());
889
1211
  (0, import_react3.useEffect)(() => {
890
- if (!contactPageVisitConfig || pathname === null) return;
891
- if (!contactPageVisitConfig.pathPattern.test(pathname)) return;
892
- if (lastContactPathRef.current === pathname) return;
893
- lastContactPathRef.current = pathname;
1212
+ if (!specificPageVisitConfig || pathname === null) return;
894
1213
  const rawClient = getOrCreateTrackingClient({
895
1214
  apiKey,
896
1215
  endpoint,
897
1216
  surface: "next",
898
1217
  packageName: "@aranova/tracking-next",
1218
+ triggers,
899
1219
  debug
900
1220
  });
901
- rawClient.trackEvent({
902
- eventType: "contact_page_visit",
903
- metadata: { page: { path: pathname } },
904
- pageUrl: null,
905
- occurredAt: null
906
- });
1221
+ for (const { name, pathPattern } of specificPageVisitConfig.pages) {
1222
+ if (!pathPattern.test(pathname)) continue;
1223
+ const key = `${name}:${pathname}`;
1224
+ if (firedPagesRef.current.has(key)) continue;
1225
+ firedPagesRef.current.add(key);
1226
+ rawClient.trackEvent({
1227
+ eventType: "specific_page_visit",
1228
+ metadata: { page_name: name, page: { path: pathname } },
1229
+ pageUrl: null,
1230
+ occurredAt: null
1231
+ });
1232
+ }
907
1233
  }, [pathname]);
908
1234
  (0, import_react3.useEffect)(() => {
909
1235
  const rawClient = getOrCreateTrackingClient({
@@ -911,6 +1237,7 @@ function createTracking(options) {
911
1237
  endpoint,
912
1238
  surface: "next",
913
1239
  packageName: "@aranova/tracking-next",
1240
+ triggers,
914
1241
  debug
915
1242
  });
916
1243
  return attachBfcacheRestore(rawClient);
@@ -922,10 +1249,81 @@ function createTracking(options) {
922
1249
  endpoint,
923
1250
  surface: "next",
924
1251
  packageName: "@aranova/tracking-next",
1252
+ triggers,
925
1253
  debug
926
1254
  });
927
1255
  return attachTimeOnSite(rawClient, timeOnSiteConfig);
928
1256
  }, []);
1257
+ const multiPageSessionConfig = triggers.automatic.multi_page_session ?? null;
1258
+ const mpsLastPathRef = (0, import_react3.useRef)(null);
1259
+ (0, import_react3.useEffect)(() => {
1260
+ if (!multiPageSessionConfig || pathname === null) return;
1261
+ if (pathname === mpsLastPathRef.current) return;
1262
+ mpsLastPathRef.current = pathname;
1263
+ const MPS_PATHS_KEY = "aranova_tracking_mps_paths";
1264
+ const MPS_SESSION_KEY = "aranova_tracking_mps_session";
1265
+ const MPS_FIRED_KEY = "aranova_tracking_mps_fired";
1266
+ try {
1267
+ const store = window.sessionStorage;
1268
+ const currentSession = getOrRotateSessionId().id;
1269
+ const storedSession = store.getItem(MPS_SESSION_KEY);
1270
+ if (storedSession !== currentSession) {
1271
+ store.setItem(MPS_SESSION_KEY, currentSession);
1272
+ store.removeItem(MPS_PATHS_KEY);
1273
+ store.removeItem(MPS_FIRED_KEY);
1274
+ }
1275
+ if (store.getItem(MPS_FIRED_KEY) === "1") return;
1276
+ const raw = store.getItem(MPS_PATHS_KEY);
1277
+ const paths = raw ? new Set(JSON.parse(raw)) : /* @__PURE__ */ new Set();
1278
+ paths.add(pathname);
1279
+ store.setItem(MPS_PATHS_KEY, JSON.stringify([...paths]));
1280
+ if (paths.size >= multiPageSessionConfig.pageThreshold) {
1281
+ store.setItem(MPS_FIRED_KEY, "1");
1282
+ const rawClient = getOrCreateTrackingClient({
1283
+ apiKey,
1284
+ endpoint,
1285
+ surface: "next",
1286
+ packageName: "@aranova/tracking-next",
1287
+ triggers,
1288
+ debug
1289
+ });
1290
+ rawClient.trackEvent({
1291
+ eventType: "multi_page_session",
1292
+ metadata: {
1293
+ page_count: paths.size,
1294
+ page: { path: pathname }
1295
+ },
1296
+ pageUrl: null,
1297
+ occurredAt: null
1298
+ });
1299
+ }
1300
+ } catch {
1301
+ }
1302
+ }, [pathname]);
1303
+ (0, import_react3.useEffect)(() => {
1304
+ const detachers = [];
1305
+ const rawClient = getOrCreateTrackingClient({
1306
+ apiKey,
1307
+ endpoint,
1308
+ surface: "next",
1309
+ packageName: "@aranova/tracking-next",
1310
+ triggers,
1311
+ debug
1312
+ });
1313
+ const scrollDepth = triggers.automatic.scroll_depth;
1314
+ if (scrollDepth) {
1315
+ detachers.push(attachScrollDepth(rawClient, scrollDepth));
1316
+ }
1317
+ const formStart = triggers.automatic.form_start;
1318
+ if (formStart) {
1319
+ detachers.push(attachFormStart(rawClient, formStart));
1320
+ }
1321
+ return () => {
1322
+ for (let i = detachers.length - 1; i >= 0; i--) {
1323
+ detachers[i]();
1324
+ }
1325
+ };
1326
+ }, []);
929
1327
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TrackingContext.Provider, { value: client, children });
930
1328
  }
931
1329
  function useTracking() {