@jmp-technologies/analytics 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.
Files changed (40) hide show
  1. package/README.md +3 -1
  2. package/dist/{chunk-3ELQETAA.js → chunk-EPNF76VK.js} +2 -2
  3. package/dist/{chunk-MUHLIN7P.js → chunk-GB37RVJJ.js} +339 -103
  4. package/dist/chunk-GB37RVJJ.js.map +1 -0
  5. package/dist/index.cjs +348 -104
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.cts +112 -35
  8. package/dist/index.d.ts +112 -35
  9. package/dist/index.js +21 -5
  10. package/dist/next/content.cjs +322 -0
  11. package/dist/next/content.cjs.map +1 -0
  12. package/dist/next/content.d.cts +37 -0
  13. package/dist/next/content.d.ts +37 -0
  14. package/dist/next/content.js +172 -0
  15. package/dist/next/content.js.map +1 -0
  16. package/dist/next/instrumentation.cjs +131 -0
  17. package/dist/next/instrumentation.cjs.map +1 -0
  18. package/dist/next/instrumentation.d.cts +7 -0
  19. package/dist/next/instrumentation.d.ts +7 -0
  20. package/dist/next/instrumentation.js +18 -0
  21. package/dist/next/instrumentation.js.map +1 -0
  22. package/dist/next/middleware.cjs +147 -0
  23. package/dist/next/middleware.cjs.map +1 -0
  24. package/dist/next/middleware.d.cts +13 -0
  25. package/dist/next/middleware.d.ts +13 -0
  26. package/dist/next/middleware.js +22 -0
  27. package/dist/next/middleware.js.map +1 -0
  28. package/dist/next.cjs +322 -86
  29. package/dist/next.cjs.map +1 -1
  30. package/dist/next.d.cts +24 -8
  31. package/dist/next.d.ts +24 -8
  32. package/dist/next.js +41 -13
  33. package/dist/next.js.map +1 -1
  34. package/dist/react.cjs +279 -69
  35. package/dist/react.cjs.map +1 -1
  36. package/dist/react.js +2 -2
  37. package/docs/legal-and-privacy.md +1 -0
  38. package/package.json +17 -2
  39. package/dist/chunk-MUHLIN7P.js.map +0 -1
  40. /package/dist/{chunk-3ELQETAA.js.map → chunk-EPNF76VK.js.map} +0 -0
package/dist/next.cjs CHANGED
@@ -34,9 +34,13 @@ __export(next_exports, {
34
34
  JmpLandingCaptureScript: () => JmpLandingCaptureScript,
35
35
  JmpNextAnalytics: () => JmpNextAnalytics,
36
36
  JmpNextPageViewTracker: () => JmpNextPageViewTracker,
37
+ JmpSiteRoot: () => JmpSiteRoot,
37
38
  JmpTrackedMailtoLink: () => JmpTrackedMailtoLink,
38
39
  JmpTrackedTelLink: () => JmpTrackedTelLink,
39
40
  buildLandingCaptureInlineScript: () => buildLandingCaptureInlineScript,
41
+ isJmpTrackingConsentGranted: () => isJmpTrackingConsentGranted,
42
+ setJmpTrackingConsent: () => setJmpTrackingConsent,
43
+ subscribeJmpTrackingConsent: () => subscribeJmpTrackingConsent,
40
44
  useJmpAnalytics: () => useJmpAnalytics
41
45
  });
42
46
  module.exports = __toCommonJS(next_exports);
@@ -49,7 +53,6 @@ var JMP_SESSION_ID_KEY = "jmp_analytics_sid";
49
53
  var JMP_LANDING_REFERER_KEY = "jmp_analytics_landing_referer";
50
54
  var JMP_LANDING_REFERER_COOKIE = "jmp_lr";
51
55
  var JMP_UTM_SESSION_KEY = "jmp_analytics_utm";
52
- var JMP_EARLY_BEACON_PATH_KEY = "jmp_analytics_early_beacon_path";
53
56
  var LANDING_REFERER_COOKIE_MAX_AGE_SEC = 30 * 60;
54
57
  var JMP_UTM_PARAM_KEYS = [
55
58
  "utm_source",
@@ -182,84 +185,174 @@ function getOrCreateSessionIdFromStorage() {
182
185
  return void 0;
183
186
  }
184
187
  }
185
- function wasEarlyBeaconSentForPath(path) {
186
- if (typeof window === "undefined") {
187
- return false;
188
+ function buildLandingCaptureInlineScript() {
189
+ const cookieMaxAge = String(LANDING_REFERER_COOKIE_MAX_AGE_SEC);
190
+ return `(function(){try{var h=location.hostname;var ref=document.referrer||'';function rh(u){try{return new URL(u).hostname.replace(/^www\\./i,'').toLowerCase()}catch(e){try{return new URL('https://'+u).hostname.replace(/^www\\./i,'').toLowerCase()}catch(e2){return null}}}function ext(r){var x=rh(r);if(!x)return false;var s=h.replace(/^www\\./i,'').toLowerCase();return x!==s&&x.slice(-('.'+s).length)!==('.'+s)}function apexDom(hn){var s=hn.replace(/^www\\./i,'').toLowerCase();if(!s||s==='localhost'||/\\.local$/i.test(s)||s.indexOf('.')<0)return null;return s}function readCk(hn){var apex=apexDom(hn);if(!apex)return '';var pre='${JMP_LANDING_REFERER_COOKIE}=';var parts=document.cookie.split(';');for(var i=0;i<parts.length;i++){var t=parts[i].trim();if(t.indexOf(pre)===0){try{return decodeURIComponent(t.slice(pre.length)).trim()}catch(e){return ''}}}return ''}function writeCk(r,hn){var apex=apexDom(hn);if(!apex)return;var sec=location.protocol==='https:'?'; Secure':'';document.cookie='${JMP_LANDING_REFERER_COOKIE}='+encodeURIComponent(r.slice(0,2048))+'; Domain=.'+apex+'; Path=/; Max-Age=${cookieMaxAge}; SameSite=Lax'+sec}var stored=sessionStorage.getItem('${JMP_LANDING_REFERER_KEY}')||'';var ck=readCk(h);var preserved=stored||ck;var lr='';if(ref&&ext(ref)){sessionStorage.setItem('${JMP_LANDING_REFERER_KEY}',ref);writeCk(ref,h);lr=ref}else{lr=preserved||'';if(lr&&!stored)sessionStorage.setItem('${JMP_LANDING_REFERER_KEY}',lr)}var sid=sessionStorage.getItem('${JMP_SESSION_ID_KEY}');if(!sid&&crypto&&crypto.randomUUID){sid=crypto.randomUUID();sessionStorage.setItem('${JMP_SESSION_ID_KEY}',sid)}try{var sp=new URLSearchParams(location.search);var utm={};var updated=false;['utm_source','utm_medium','utm_campaign','utm_content','utm_term'].forEach(function(n){var v=sp.get(n);if(v){utm[n]=v;updated=true}});if(updated)sessionStorage.setItem('${JMP_UTM_SESSION_KEY}',JSON.stringify(utm))}catch(e){}}catch(e){}})();`;
191
+ }
192
+
193
+ // src/contact-link-classifier.ts
194
+ var BOOKING_LINK_HOSTS = [
195
+ "calendly.com",
196
+ "cal.com",
197
+ "acuityscheduling.com",
198
+ "calendar.google.com",
199
+ "calendar.app.google",
200
+ "bookings.microsoft.com",
201
+ "outlook.office.com",
202
+ "outlook.office365.com",
203
+ "book.ms",
204
+ "setmore.com",
205
+ "simplybook.me",
206
+ "oncehub.com",
207
+ "scheduleonce.com",
208
+ "youcanbook.me",
209
+ "savvycal.com",
210
+ "tidycal.com",
211
+ "zohobookings.com",
212
+ "square.site",
213
+ "squareup.com",
214
+ "meetings.hubspot.com",
215
+ "jobber.com",
216
+ "housecallpro.com",
217
+ "servicetitan.com",
218
+ "thryv.com",
219
+ "honeybook.com",
220
+ "dubsado.com",
221
+ "fresha.com",
222
+ "booksy.com",
223
+ "vagaro.com",
224
+ "mindbodyonline.com",
225
+ "mindbody.io",
226
+ "janeapp.com",
227
+ "cliniko.com",
228
+ "practicebetter.io"
229
+ ];
230
+ var WHATSAPP_HOSTS = ["wa.me", "api.whatsapp.com", "whatsapp.com"];
231
+ function hostnameMatches(host, pattern) {
232
+ const h = host.toLowerCase();
233
+ const p = pattern.toLowerCase();
234
+ return h === p || h.endsWith(`.${p}`);
235
+ }
236
+ function isMapsHost(host, pathname) {
237
+ if (hostnameMatches(host, "maps.apple.com")) {
238
+ return true;
188
239
  }
189
- try {
190
- return sessionStorage.getItem(JMP_EARLY_BEACON_PATH_KEY) === path;
191
- } catch {
192
- return false;
240
+ if (hostnameMatches(host, "maps.app.goo.gl")) {
241
+ return true;
193
242
  }
243
+ if (hostnameMatches(host, "google.com") || hostnameMatches(host, "google.co")) {
244
+ return pathname === "/maps" || pathname.startsWith("/maps/") || pathname.startsWith("/local");
245
+ }
246
+ return false;
194
247
  }
195
- function markEarlyBeaconSentForPath(path) {
196
- if (typeof window === "undefined") {
197
- return;
248
+ function isWhatsAppHost(host, pathname) {
249
+ for (const pattern of WHATSAPP_HOSTS) {
250
+ if (!hostnameMatches(host, pattern)) {
251
+ continue;
252
+ }
253
+ if (pattern === "whatsapp.com") {
254
+ return pathname.startsWith("/send");
255
+ }
256
+ return true;
257
+ }
258
+ return false;
259
+ }
260
+ function isBookingHost(host) {
261
+ for (const pattern of BOOKING_LINK_HOSTS) {
262
+ if (hostnameMatches(host, pattern)) {
263
+ return true;
264
+ }
265
+ }
266
+ return false;
267
+ }
268
+ function classifyContactHref(href) {
269
+ const raw = href.trim();
270
+ if (!raw || raw.startsWith("#") || raw.startsWith("javascript:")) {
271
+ return null;
272
+ }
273
+ const lower = raw.toLowerCase();
274
+ if (lower.startsWith("tel:")) {
275
+ return { kind: "call" };
276
+ }
277
+ if (lower.startsWith("mailto:")) {
278
+ return { kind: "email" };
279
+ }
280
+ if (lower.startsWith("sms:") || lower.startsWith("smsto:")) {
281
+ return { kind: "contact_click", channel: "sms", linkHost: null };
282
+ }
283
+ if (lower.startsWith("geo:")) {
284
+ return { kind: "contact_click", channel: "maps", linkHost: null };
285
+ }
286
+ if (raw.startsWith("/") || raw.startsWith("?")) {
287
+ return null;
198
288
  }
289
+ let url;
199
290
  try {
200
- sessionStorage.setItem(JMP_EARLY_BEACON_PATH_KEY, path);
291
+ url = new URL(raw);
201
292
  } catch {
293
+ return null;
294
+ }
295
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
296
+ return null;
297
+ }
298
+ const host = url.hostname.toLowerCase();
299
+ const pathname = url.pathname.toLowerCase();
300
+ if (isWhatsAppHost(host, pathname)) {
301
+ return { kind: "contact_click", channel: "whatsapp", linkHost: host };
202
302
  }
303
+ if (isMapsHost(host, pathname)) {
304
+ return { kind: "contact_click", channel: "maps", linkHost: host };
305
+ }
306
+ if (isBookingHost(host)) {
307
+ return { kind: "contact_click", channel: "booking", linkHost: host };
308
+ }
309
+ return null;
203
310
  }
204
- function sendEarlyPageViewBeacon(args) {
205
- if (typeof window === "undefined" || typeof fetch === "undefined") {
311
+
312
+ // src/index.ts
313
+ var JMP_TRACKING_CONSENT_STORAGE_KEY = "jmp_analytics_consent_v1";
314
+ var moduleTrackingConsentGranted = true;
315
+ var trackingConsentListeners = /* @__PURE__ */ new Set();
316
+ function readStoredTrackingConsent() {
317
+ if (typeof window === "undefined") {
206
318
  return false;
207
319
  }
208
- const path = `${window.location.pathname}${window.location.search}` || "/";
209
- if (wasEarlyBeaconSentForPath(path)) {
320
+ try {
321
+ return localStorage.getItem(JMP_TRACKING_CONSENT_STORAGE_KEY) === "granted";
322
+ } catch {
210
323
  return false;
211
324
  }
212
- const { landingReferer, utm } = captureLandingAttribution(window.location.hostname);
213
- const sessionId = getOrCreateSessionIdFromStorage();
214
- const metadata = {
215
- pageHostname: window.location.hostname,
216
- earlyBeacon: true,
217
- ...utm
218
- };
219
- if (landingReferer) {
220
- metadata.referer = landingReferer;
221
- }
222
- const payload = {
223
- type: "page_view",
224
- path,
225
- trackingKey: args.trackingKey,
226
- ...sessionId ? { sessionId } : {},
227
- metadata
228
- };
229
- const headers = {
230
- "Content-Type": "application/json",
231
- "X-JMP-Tracking-Key": args.trackingKey
325
+ }
326
+ function isJmpTrackingConsentGranted() {
327
+ return moduleTrackingConsentGranted;
328
+ }
329
+ function subscribeJmpTrackingConsent(listener) {
330
+ trackingConsentListeners.add(listener);
331
+ return () => {
332
+ trackingConsentListeners.delete(listener);
232
333
  };
233
- if (landingReferer) {
234
- headers["X-JMP-Visitor-Referer"] = landingReferer;
235
- }
236
- markEarlyBeaconSentForPath(path);
237
- void fetch(`${args.baseUrl.replace(/\/$/, "")}/api/events`, {
238
- method: "POST",
239
- headers,
240
- body: JSON.stringify(payload),
241
- keepalive: true,
242
- mode: "cors",
243
- credentials: "omit"
244
- }).catch(() => {
334
+ }
335
+ function setJmpTrackingConsent(granted) {
336
+ if (moduleTrackingConsentGranted === granted) {
337
+ return;
338
+ }
339
+ moduleTrackingConsentGranted = granted;
340
+ if (typeof window !== "undefined") {
245
341
  try {
246
- sessionStorage.removeItem(JMP_EARLY_BEACON_PATH_KEY);
342
+ localStorage.setItem(
343
+ JMP_TRACKING_CONSENT_STORAGE_KEY,
344
+ granted ? "granted" : "denied"
345
+ );
247
346
  } catch {
248
347
  }
249
- });
250
- return true;
251
- }
252
- function escapeInlineScript(value) {
253
- return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
254
- }
255
- function buildLandingCaptureInlineScript(args) {
256
- const baseUrl = escapeInlineScript(args.baseUrl.replace(/\/$/, ""));
257
- const trackingKey = escapeInlineScript(args.trackingKey);
258
- const cookieMaxAge = String(LANDING_REFERER_COOKIE_MAX_AGE_SEC);
259
- return `(function(){try{var k='${trackingKey}',b='${baseUrl}';if(!k||!b)return;var p=location.pathname+location.search||'/';var sentKey='${JMP_EARLY_BEACON_PATH_KEY}';if(sessionStorage.getItem(sentKey)===p)return;var h=location.hostname;var ref=document.referrer||'';function rh(u){try{return new URL(u).hostname.replace(/^www\\./i,'').toLowerCase()}catch(e){try{return new URL('https://'+u).hostname.replace(/^www\\./i,'').toLowerCase()}catch(e2){return null}}}function ext(r){var x=rh(r);if(!x)return false;var s=h.replace(/^www\\./i,'').toLowerCase();return x!==s&&x.slice(-('.'+s).length)!==('.'+s)}function apexDom(hn){var s=hn.replace(/^www\\./i,'').toLowerCase();if(!s||s==='localhost'||/\\.local$/i.test(s)||s.indexOf('.')<0)return null;return s}function readCk(hn){var apex=apexDom(hn);if(!apex)return '';var pre='${JMP_LANDING_REFERER_COOKIE}=';var parts=document.cookie.split(';');for(var i=0;i<parts.length;i++){var t=parts[i].trim();if(t.indexOf(pre)===0){try{return decodeURIComponent(t.slice(pre.length)).trim()}catch(e){return ''}}}return ''}function writeCk(r,hn){var apex=apexDom(hn);if(!apex)return;var sec=location.protocol==='https:'?'; Secure':'';document.cookie='${JMP_LANDING_REFERER_COOKIE}='+encodeURIComponent(r.slice(0,2048))+'; Domain=.'+apex+'; Path=/; Max-Age=${cookieMaxAge}; SameSite=Lax'+sec}var stored=sessionStorage.getItem('${JMP_LANDING_REFERER_KEY}')||'';var ck=readCk(h);var preserved=stored||ck;var lr='';if(ref&&ext(ref)){sessionStorage.setItem('${JMP_LANDING_REFERER_KEY}',ref);writeCk(ref,h);lr=ref}else{lr=preserved||'';if(lr&&!stored)sessionStorage.setItem('${JMP_LANDING_REFERER_KEY}',lr)}var sid=sessionStorage.getItem('${JMP_SESSION_ID_KEY}');if(!sid&&crypto&&crypto.randomUUID){sid=crypto.randomUUID();sessionStorage.setItem('${JMP_SESSION_ID_KEY}',sid)}var md={pageHostname:h,earlyBeacon:true};try{var sp=new URLSearchParams(location.search);['utm_source','utm_medium','utm_campaign','utm_content','utm_term'].forEach(function(n){var v=sp.get(n);if(v)md[n]=v})}catch(e){}if(lr)md.referer=lr;var body=JSON.stringify({type:'page_view',path:p,trackingKey:k,sessionId:sid||undefined,metadata:md});var hd={'Content-Type':'application/json','X-JMP-Tracking-Key':k};if(lr)hd['X-JMP-Visitor-Referer']=lr;sessionStorage.setItem(sentKey,p);fetch(b+'/api/events',{method:'POST',headers:hd,body:body,keepalive:true,mode:'cors',credentials:'omit'}).catch(function(){sessionStorage.removeItem(sentKey)})}catch(e){}})();`;
348
+ if (granted) {
349
+ captureLandingAttribution(window.location.hostname);
350
+ }
351
+ }
352
+ for (const listener of trackingConsentListeners) {
353
+ listener();
354
+ }
260
355
  }
261
-
262
- // src/index.ts
263
356
  function captureAndGetLandingReferer() {
264
357
  if (typeof window === "undefined") {
265
358
  return null;
@@ -309,9 +402,16 @@ function createJmpAnalytics(config) {
309
402
  );
310
403
  }
311
404
  const fetchFn = resolvedFetch;
405
+ moduleTrackingConsentGranted = config.requireConsent ? readStoredTrackingConsent() : true;
406
+ function canSend() {
407
+ return moduleTrackingConsentGranted;
408
+ }
312
409
  let lastDedupePath = "";
313
410
  let lastDedupeAt = 0;
314
411
  async function send(body) {
412
+ if (!canSend()) {
413
+ return;
414
+ }
315
415
  const url = `${baseUrl}/api/events`;
316
416
  const headers = {
317
417
  "Content-Type": "application/json",
@@ -367,6 +467,9 @@ function createJmpAnalytics(config) {
367
467
  }
368
468
  }
369
469
  async function sendLead(body) {
470
+ if (!canSend()) {
471
+ return;
472
+ }
370
473
  const url = `${baseUrl}/api/leads`;
371
474
  const headers = {
372
475
  "Content-Type": "application/json",
@@ -429,6 +532,9 @@ function createJmpAnalytics(config) {
429
532
  return false;
430
533
  }
431
534
  function trackPageView(options) {
535
+ if (!canSend()) {
536
+ return Promise.resolve();
537
+ }
432
538
  const pathFromOptions = options?.path?.trim();
433
539
  let pathname = "/";
434
540
  let search = "";
@@ -444,9 +550,6 @@ function createJmpAnalytics(config) {
444
550
  search = window.location.search.replace(/^\?/, "");
445
551
  }
446
552
  const path = search ? `${pathname}?${search}` : pathname;
447
- if (wasEarlyBeaconSentForPath(path)) {
448
- return Promise.resolve();
449
- }
450
553
  if (shouldDedupePageView(pathname)) {
451
554
  return Promise.resolve();
452
555
  }
@@ -497,6 +600,20 @@ function createJmpAnalytics(config) {
497
600
  metadata: metadata ?? {}
498
601
  });
499
602
  }
603
+ function trackContactClick(path, options) {
604
+ const metadata = {
605
+ ...options.metadata && typeof options.metadata === "object" ? options.metadata : {},
606
+ channel: options.channel
607
+ };
608
+ if (options.linkHost) {
609
+ metadata.linkHost = options.linkHost;
610
+ }
611
+ return send({
612
+ type: "contact_click",
613
+ path: path || "/",
614
+ metadata
615
+ });
616
+ }
500
617
  function trackLead(options) {
501
618
  const path = options?.path ?? (typeof window !== "undefined" ? window.location.pathname : "/");
502
619
  const body = { path: path || "/" };
@@ -529,7 +646,20 @@ function createJmpAnalytics(config) {
529
646
  if (type === "call_click") {
530
647
  return trackCallClick(payload.path, payload.metadata);
531
648
  }
532
- return trackEmailClick(payload.path, payload.metadata);
649
+ if (type === "email_click") {
650
+ return trackEmailClick(payload.path, payload.metadata);
651
+ }
652
+ const meta = payload.metadata ?? {};
653
+ const channel = typeof meta.channel === "string" ? meta.channel : "";
654
+ const linkHost = typeof meta.linkHost === "string" ? meta.linkHost : null;
655
+ if (channel !== "sms" && channel !== "whatsapp" && channel !== "maps" && channel !== "booking") {
656
+ return Promise.resolve();
657
+ }
658
+ return trackContactClick(payload.path, {
659
+ channel,
660
+ linkHost,
661
+ metadata: meta
662
+ });
533
663
  }
534
664
  function subscribeToSpaNavigation() {
535
665
  if (typeof window === "undefined") {
@@ -563,12 +693,75 @@ function createJmpAnalytics(config) {
563
693
  history.replaceState = replace;
564
694
  };
565
695
  }
566
- if (typeof window !== "undefined") {
696
+ let lastContactClickKey = "";
697
+ let lastContactClickAt = 0;
698
+ function subscribeContactLinkAutoTrack() {
699
+ if (typeof window === "undefined" || typeof document === "undefined") {
700
+ return () => {
701
+ };
702
+ }
703
+ const handler = (event) => {
704
+ if (!canSend()) {
705
+ return;
706
+ }
707
+ const target = event.target;
708
+ if (!(target instanceof Element)) {
709
+ return;
710
+ }
711
+ const anchor = target.closest("a[href]");
712
+ if (!(anchor instanceof HTMLAnchorElement)) {
713
+ return;
714
+ }
715
+ if (anchor.closest("[data-jmp-no-track]")) {
716
+ return;
717
+ }
718
+ const href = anchor.getAttribute("href") ?? "";
719
+ const classified = classifyContactHref(href);
720
+ if (!classified) {
721
+ return;
722
+ }
723
+ const path = window.location.pathname;
724
+ if (classified.kind === "call") {
725
+ if (dedupeContactClick(`call_click:${path}`)) {
726
+ return;
727
+ }
728
+ void trackCallClick(path);
729
+ return;
730
+ }
731
+ if (classified.kind === "email") {
732
+ if (dedupeContactClick(`email_click:${path}`)) {
733
+ return;
734
+ }
735
+ void trackEmailClick(path);
736
+ return;
737
+ }
738
+ if (dedupeContactClick(`contact_click:${classified.channel}:${path}`)) {
739
+ return;
740
+ }
741
+ void trackContactClick(path, {
742
+ channel: classified.channel,
743
+ linkHost: classified.linkHost
744
+ });
745
+ };
746
+ document.addEventListener("click", handler, { capture: true });
747
+ return () => {
748
+ document.removeEventListener("click", handler, { capture: true });
749
+ };
750
+ }
751
+ function dedupeContactClick(key) {
752
+ const now = Date.now();
753
+ if (key === lastContactClickKey && now - lastContactClickAt < 1e3) {
754
+ return true;
755
+ }
756
+ lastContactClickKey = key;
757
+ lastContactClickAt = now;
758
+ return false;
759
+ }
760
+ if (typeof window !== "undefined" && canSend()) {
567
761
  captureLandingAttribution(window.location.hostname);
568
- sendEarlyPageViewBeacon({
569
- baseUrl,
570
- trackingKey: config.trackingKey
571
- });
762
+ }
763
+ if (typeof window !== "undefined" && config.autoTrackContactLinks && canSend()) {
764
+ subscribeContactLinkAutoTrack();
572
765
  }
573
766
  return {
574
767
  track,
@@ -576,25 +769,47 @@ function createJmpAnalytics(config) {
576
769
  trackFormSubmit,
577
770
  trackCallClick,
578
771
  trackEmailClick,
772
+ trackContactClick,
579
773
  trackLead,
580
- subscribeToSpaNavigation
774
+ subscribeToSpaNavigation,
775
+ subscribeContactLinkAutoTrack,
776
+ setTrackingConsent: setJmpTrackingConsent,
777
+ isTrackingConsentGranted: isJmpTrackingConsentGranted
581
778
  };
582
779
  }
583
780
  var DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS = 800;
584
- function readProcessEnv(key) {
781
+ function readNextPublicEnv(key) {
585
782
  if (typeof process === "undefined" || !process.env) return void 0;
586
- const v = process.env[key];
783
+ let v;
784
+ switch (key) {
785
+ case "NEXT_PUBLIC_JMP_ANALYTICS_URL":
786
+ v = process.env.NEXT_PUBLIC_JMP_ANALYTICS_URL;
787
+ break;
788
+ case "NEXT_PUBLIC_JMP_TRACKING_KEY":
789
+ v = process.env.NEXT_PUBLIC_JMP_TRACKING_KEY;
790
+ break;
791
+ case "NEXT_PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT":
792
+ v = process.env.NEXT_PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT;
793
+ break;
794
+ case "NEXT_PUBLIC_JMP_AUTO_CONTACT_LINKS":
795
+ v = process.env.NEXT_PUBLIC_JMP_AUTO_CONTACT_LINKS;
796
+ break;
797
+ }
587
798
  return typeof v === "string" && v.length > 0 ? v : void 0;
588
799
  }
589
800
  function getJmpBrowserAnalyticsConfigFromEnv(overrides) {
590
- const baseUrl = readProcessEnv("NEXT_PUBLIC_JMP_ANALYTICS_URL");
591
- const trackingKey = readProcessEnv("NEXT_PUBLIC_JMP_TRACKING_KEY");
801
+ const baseUrl = readNextPublicEnv("NEXT_PUBLIC_JMP_ANALYTICS_URL");
802
+ const trackingKey = readNextPublicEnv("NEXT_PUBLIC_JMP_TRACKING_KEY");
592
803
  if (!baseUrl || !trackingKey) return null;
804
+ const requireConsentFromEnv = readNextPublicEnv("NEXT_PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT") === "true";
805
+ const autoTrackContactLinksFromEnv = readNextPublicEnv("NEXT_PUBLIC_JMP_AUTO_CONTACT_LINKS") === "true";
593
806
  return {
594
807
  baseUrl,
595
808
  trackingKey,
596
809
  pageViewDedupeMs: DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS,
597
- ...overrides
810
+ ...overrides,
811
+ requireConsent: overrides?.requireConsent ?? requireConsentFromEnv,
812
+ autoTrackContactLinks: overrides?.autoTrackContactLinks ?? autoTrackContactLinksFromEnv
598
813
  };
599
814
  }
600
815
  var jmpAnalyticsBrowserSingleton = null;
@@ -655,12 +870,9 @@ function JmpTrackedMailtoLink({
655
870
  // src/next.tsx
656
871
  var import_jsx_runtime2 = require("react/jsx-runtime");
657
872
  function JmpLandingCaptureScript({
658
- baseUrl = process.env.NEXT_PUBLIC_JMP_ANALYTICS_URL,
659
873
  trackingKey = process.env.NEXT_PUBLIC_JMP_TRACKING_KEY
660
874
  } = {}) {
661
- const resolvedBase = baseUrl?.trim();
662
- const resolvedKey = trackingKey?.trim();
663
- if (!resolvedBase || !resolvedKey) {
875
+ if (!trackingKey?.trim()) {
664
876
  return null;
665
877
  }
666
878
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
@@ -669,10 +881,7 @@ function JmpLandingCaptureScript({
669
881
  id: "jmp-landing-capture",
670
882
  strategy: "beforeInteractive",
671
883
  dangerouslySetInnerHTML: {
672
- __html: buildLandingCaptureInlineScript({
673
- baseUrl: resolvedBase,
674
- trackingKey: resolvedKey
675
- })
884
+ __html: buildLandingCaptureInlineScript()
676
885
  }
677
886
  }
678
887
  );
@@ -691,20 +900,47 @@ function JmpNextPageViewTrackerInner() {
691
900
  function JmpNextPageViewTracker() {
692
901
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react2.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(JmpNextPageViewTrackerInner, {}) });
693
902
  }
694
- function JmpNextAnalytics() {
903
+ function useJmpTrackingActive(deferUntilConsent) {
904
+ const consentGranted = (0, import_react2.useSyncExternalStore)(
905
+ subscribeJmpTrackingConsent,
906
+ () => isJmpTrackingConsentGranted(),
907
+ () => false
908
+ );
909
+ return !deferUntilConsent || consentGranted;
910
+ }
911
+ function JmpNextAnalytics({
912
+ deferUntilConsent = process.env.NEXT_PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT === "true"
913
+ } = {}) {
914
+ const active = useJmpTrackingActive(deferUntilConsent);
915
+ if (!active) {
916
+ return null;
917
+ }
695
918
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
696
919
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(JmpLandingCaptureScript, {}),
697
920
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(JmpNextPageViewTracker, {})
698
921
  ] });
699
922
  }
923
+ function JmpSiteRoot({
924
+ children,
925
+ deferUntilConsent
926
+ }) {
927
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
928
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(JmpNextAnalytics, { deferUntilConsent }),
929
+ children
930
+ ] });
931
+ }
700
932
  // Annotate the CommonJS export names for ESM import in node:
701
933
  0 && (module.exports = {
702
934
  JmpLandingCaptureScript,
703
935
  JmpNextAnalytics,
704
936
  JmpNextPageViewTracker,
937
+ JmpSiteRoot,
705
938
  JmpTrackedMailtoLink,
706
939
  JmpTrackedTelLink,
707
940
  buildLandingCaptureInlineScript,
941
+ isJmpTrackingConsentGranted,
942
+ setJmpTrackingConsent,
943
+ subscribeJmpTrackingConsent,
708
944
  useJmpAnalytics
709
945
  });
710
946
  //# sourceMappingURL=next.cjs.map