@jmp-technologies/analytics 0.1.12 → 0.1.16

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.d.cts CHANGED
@@ -1,3 +1,18 @@
1
+ type EarlyPageViewBeaconArgs = {
2
+ baseUrl: string;
3
+ trackingKey: string;
4
+ };
5
+ /**
6
+ * Capture landing referrer/UTMs and send an early page_view (fetch keepalive).
7
+ * Safe to call from inline script or module init.
8
+ */
9
+ declare function sendEarlyPageViewBeacon(args: EarlyPageViewBeaconArgs): boolean;
10
+ /** Inline script for Next.js `beforeInteractive` — earliest capture + beacon. */
11
+ declare function buildLandingCaptureInlineScript(args: {
12
+ baseUrl: string;
13
+ trackingKey: string;
14
+ }): string;
15
+
1
16
  type JmpEventType = "page_view" | "form_submit" | "call_click" | "email_click";
2
17
  type JmpAnalyticsConfig = {
3
18
  /** Dashboard origin, e.g. https://analytics.example.com (no trailing slash) */
@@ -306,4 +321,4 @@ type JmpContentClient = ReturnType<typeof createJmpContentClient>;
306
321
  */
307
322
  declare function createJmpContentClientFromServerEnv(overrides?: Partial<JmpContentConfig>): JmpContentClient | null;
308
323
 
309
- export { DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS, type JmpAnalytics, type JmpAnalyticsConfig, type JmpBlogPost, type JmpContentClient, type JmpContentConfig, type JmpContentWrapper, type JmpCredential, type JmpEventType, type JmpFaqItem, type JmpPartnerLogo, type JmpServerRequestLike, type JmpSiteProfile, type JmpTestimonial, type JmpTrustBundle, type JmpTrustSlot, type JmpTrustStat, type JmpTrustSummary, type LeadMetadata, type LeadMetadataValue, type TrackLeadOptions, type TrackPageViewOptions, type TrackServerPageViewOptions, createJmpAnalytics, createJmpContentClient, createJmpContentClientFromServerEnv, getJmpBlogPost, getJmpBlogPosts, getJmpBrowserAnalyticsConfigFromEnv, getJmpFaqs, getJmpRobotsTxt, getJmpServerContentConfigFromEnv, getJmpSiteProfile, getJmpTrustBundle, getOrCreateJmpAnalyticsFromBrowserEnv, isJmpBrowserAnalyticsEnvConfigured, isJmpServerContentEnvConfigured, registerJmpContentWrappers, registerJmpTrustCapabilities, trackJmpServerPageView };
324
+ export { DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS, type JmpAnalytics, type JmpAnalyticsConfig, type JmpBlogPost, type JmpContentClient, type JmpContentConfig, type JmpContentWrapper, type JmpCredential, type JmpEventType, type JmpFaqItem, type JmpPartnerLogo, type JmpServerRequestLike, type JmpSiteProfile, type JmpTestimonial, type JmpTrustBundle, type JmpTrustSlot, type JmpTrustStat, type JmpTrustSummary, type LeadMetadata, type LeadMetadataValue, type TrackLeadOptions, type TrackPageViewOptions, type TrackServerPageViewOptions, buildLandingCaptureInlineScript, createJmpAnalytics, createJmpContentClient, createJmpContentClientFromServerEnv, getJmpBlogPost, getJmpBlogPosts, getJmpBrowserAnalyticsConfigFromEnv, getJmpFaqs, getJmpRobotsTxt, getJmpServerContentConfigFromEnv, getJmpSiteProfile, getJmpTrustBundle, getOrCreateJmpAnalyticsFromBrowserEnv, isJmpBrowserAnalyticsEnvConfigured, isJmpServerContentEnvConfigured, registerJmpContentWrappers, registerJmpTrustCapabilities, sendEarlyPageViewBeacon, trackJmpServerPageView };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,18 @@
1
+ type EarlyPageViewBeaconArgs = {
2
+ baseUrl: string;
3
+ trackingKey: string;
4
+ };
5
+ /**
6
+ * Capture landing referrer/UTMs and send an early page_view (fetch keepalive).
7
+ * Safe to call from inline script or module init.
8
+ */
9
+ declare function sendEarlyPageViewBeacon(args: EarlyPageViewBeaconArgs): boolean;
10
+ /** Inline script for Next.js `beforeInteractive` — earliest capture + beacon. */
11
+ declare function buildLandingCaptureInlineScript(args: {
12
+ baseUrl: string;
13
+ trackingKey: string;
14
+ }): string;
15
+
1
16
  type JmpEventType = "page_view" | "form_submit" | "call_click" | "email_click";
2
17
  type JmpAnalyticsConfig = {
3
18
  /** Dashboard origin, e.g. https://analytics.example.com (no trailing slash) */
@@ -306,4 +321,4 @@ type JmpContentClient = ReturnType<typeof createJmpContentClient>;
306
321
  */
307
322
  declare function createJmpContentClientFromServerEnv(overrides?: Partial<JmpContentConfig>): JmpContentClient | null;
308
323
 
309
- export { DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS, type JmpAnalytics, type JmpAnalyticsConfig, type JmpBlogPost, type JmpContentClient, type JmpContentConfig, type JmpContentWrapper, type JmpCredential, type JmpEventType, type JmpFaqItem, type JmpPartnerLogo, type JmpServerRequestLike, type JmpSiteProfile, type JmpTestimonial, type JmpTrustBundle, type JmpTrustSlot, type JmpTrustStat, type JmpTrustSummary, type LeadMetadata, type LeadMetadataValue, type TrackLeadOptions, type TrackPageViewOptions, type TrackServerPageViewOptions, createJmpAnalytics, createJmpContentClient, createJmpContentClientFromServerEnv, getJmpBlogPost, getJmpBlogPosts, getJmpBrowserAnalyticsConfigFromEnv, getJmpFaqs, getJmpRobotsTxt, getJmpServerContentConfigFromEnv, getJmpSiteProfile, getJmpTrustBundle, getOrCreateJmpAnalyticsFromBrowserEnv, isJmpBrowserAnalyticsEnvConfigured, isJmpServerContentEnvConfigured, registerJmpContentWrappers, registerJmpTrustCapabilities, trackJmpServerPageView };
324
+ export { DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS, type JmpAnalytics, type JmpAnalyticsConfig, type JmpBlogPost, type JmpContentClient, type JmpContentConfig, type JmpContentWrapper, type JmpCredential, type JmpEventType, type JmpFaqItem, type JmpPartnerLogo, type JmpServerRequestLike, type JmpSiteProfile, type JmpTestimonial, type JmpTrustBundle, type JmpTrustSlot, type JmpTrustStat, type JmpTrustSummary, type LeadMetadata, type LeadMetadataValue, type TrackLeadOptions, type TrackPageViewOptions, type TrackServerPageViewOptions, buildLandingCaptureInlineScript, createJmpAnalytics, createJmpContentClient, createJmpContentClientFromServerEnv, getJmpBlogPost, getJmpBlogPosts, getJmpBrowserAnalyticsConfigFromEnv, getJmpFaqs, getJmpRobotsTxt, getJmpServerContentConfigFromEnv, getJmpSiteProfile, getJmpTrustBundle, getOrCreateJmpAnalyticsFromBrowserEnv, isJmpBrowserAnalyticsEnvConfigured, isJmpServerContentEnvConfigured, registerJmpContentWrappers, registerJmpTrustCapabilities, sendEarlyPageViewBeacon, trackJmpServerPageView };
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS,
3
+ buildLandingCaptureInlineScript,
3
4
  createJmpAnalytics,
4
5
  createJmpContentClient,
5
6
  createJmpContentClientFromServerEnv,
@@ -16,10 +17,12 @@ import {
16
17
  isJmpServerContentEnvConfigured,
17
18
  registerJmpContentWrappers,
18
19
  registerJmpTrustCapabilities,
20
+ sendEarlyPageViewBeacon,
19
21
  trackJmpServerPageView
20
- } from "./chunk-ST5BERIA.js";
22
+ } from "./chunk-AEQGPTDY.js";
21
23
  export {
22
24
  DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS,
25
+ buildLandingCaptureInlineScript,
23
26
  createJmpAnalytics,
24
27
  createJmpContentClient,
25
28
  createJmpContentClientFromServerEnv,
@@ -36,6 +39,7 @@ export {
36
39
  isJmpServerContentEnvConfigured,
37
40
  registerJmpContentWrappers,
38
41
  registerJmpTrustCapabilities,
42
+ sendEarlyPageViewBeacon,
39
43
  trackJmpServerPageView
40
44
  };
41
45
  //# sourceMappingURL=index.js.map
package/dist/next.cjs CHANGED
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  "use client";
3
+ var __create = Object.create;
3
4
  var __defProp = Object.defineProperty;
4
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
6
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
9
  var __export = (target, all) => {
8
10
  for (var name in all)
@@ -16,85 +18,267 @@ var __copyProps = (to, from, except, desc) => {
16
18
  }
17
19
  return to;
18
20
  };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
19
29
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
30
 
21
31
  // src/next.tsx
22
32
  var next_exports = {};
23
33
  __export(next_exports, {
34
+ JmpLandingCaptureScript: () => JmpLandingCaptureScript,
24
35
  JmpNextAnalytics: () => JmpNextAnalytics,
25
36
  JmpNextPageViewTracker: () => JmpNextPageViewTracker,
26
37
  JmpTrackedMailtoLink: () => JmpTrackedMailtoLink,
27
38
  JmpTrackedTelLink: () => JmpTrackedTelLink,
39
+ buildLandingCaptureInlineScript: () => buildLandingCaptureInlineScript,
28
40
  useJmpAnalytics: () => useJmpAnalytics
29
41
  });
30
42
  module.exports = __toCommonJS(next_exports);
43
+ var import_script = __toESM(require("next/script"), 1);
31
44
  var import_react2 = require("react");
32
45
  var import_navigation = require("next/navigation");
33
46
 
34
- // src/index.ts
35
- var DEFAULT_SOURCE = "direct";
36
- var SESSION_STORAGE_KEY = "jmp_analytics_sid";
37
- var LANDING_REFERRER_STORAGE_KEY = "jmp_analytics_landing_referer";
38
- var UTM_SESSION_STORAGE_KEY = "jmp_analytics_utm";
39
- var UTM_PARAM_KEYS = [
47
+ // src/landing-capture.ts
48
+ var JMP_SESSION_ID_KEY = "jmp_analytics_sid";
49
+ var JMP_LANDING_REFERER_KEY = "jmp_analytics_landing_referer";
50
+ var JMP_LANDING_REFERER_COOKIE = "jmp_lr";
51
+ var JMP_UTM_SESSION_KEY = "jmp_analytics_utm";
52
+ var JMP_EARLY_BEACON_PATH_KEY = "jmp_analytics_early_beacon_path";
53
+ var LANDING_REFERER_COOKIE_MAX_AGE_SEC = 30 * 60;
54
+ var JMP_UTM_PARAM_KEYS = [
40
55
  "utm_source",
41
56
  "utm_medium",
42
57
  "utm_campaign",
43
58
  "utm_content",
44
59
  "utm_term"
45
60
  ];
46
- function captureAndGetLandingReferer() {
47
- if (typeof window === "undefined") {
61
+ function refererHostFromUrl(referer) {
62
+ const trimmed = referer.trim();
63
+ try {
64
+ return new URL(trimmed).hostname.replace(/^www\./i, "").toLowerCase();
65
+ } catch {
66
+ try {
67
+ return new URL(`https://${trimmed}`).hostname.replace(/^www\./i, "").toLowerCase();
68
+ } catch {
69
+ return null;
70
+ }
71
+ }
72
+ }
73
+ function isExternalReferrerUrl(referer, siteHostname) {
74
+ const refHost = refererHostFromUrl(referer);
75
+ if (!refHost) {
76
+ return false;
77
+ }
78
+ const site = siteHostname.replace(/^www\./i, "").toLowerCase();
79
+ return refHost !== site && !refHost.endsWith(`.${site}`);
80
+ }
81
+ function registrableSiteDomain(siteHostname) {
82
+ const site = siteHostname.replace(/^www\./i, "").toLowerCase();
83
+ if (!site || site === "localhost" || site.endsWith(".local")) {
48
84
  return null;
49
85
  }
50
- try {
51
- const stored = sessionStorage.getItem(LANDING_REFERRER_STORAGE_KEY);
52
- if (stored?.trim()) {
53
- return stored.trim();
86
+ if (!site.includes(".")) {
87
+ return null;
88
+ }
89
+ return site;
90
+ }
91
+ function readLandingRefererCookie(siteHostname) {
92
+ if (typeof document === "undefined") {
93
+ return null;
94
+ }
95
+ const apex = registrableSiteDomain(siteHostname);
96
+ if (!apex) {
97
+ return null;
98
+ }
99
+ const prefix = `${JMP_LANDING_REFERER_COOKIE}=`;
100
+ for (const part of document.cookie.split(";")) {
101
+ const trimmed = part.trim();
102
+ if (!trimmed.startsWith(prefix)) {
103
+ continue;
54
104
  }
55
- const current = document.referrer?.trim();
56
- if (!current) {
105
+ try {
106
+ const value = decodeURIComponent(trimmed.slice(prefix.length)).trim();
107
+ return value.length > 0 ? value : null;
108
+ } catch {
57
109
  return null;
58
110
  }
59
- sessionStorage.setItem(LANDING_REFERRER_STORAGE_KEY, current);
60
- return current;
61
- } catch {
62
- return document.referrer?.trim() || null;
63
111
  }
112
+ return null;
64
113
  }
65
- function mergeSessionUtms(metadata) {
66
- if (typeof window === "undefined") {
114
+ function writeLandingRefererCookie(referer, siteHostname) {
115
+ if (typeof document === "undefined") {
67
116
  return;
68
117
  }
118
+ const apex = registrableSiteDomain(siteHostname);
119
+ if (!apex) {
120
+ return;
121
+ }
122
+ const value = encodeURIComponent(referer.trim().slice(0, 2048));
123
+ const secure = typeof window !== "undefined" && window.location.protocol === "https:" ? "; Secure" : "";
124
+ document.cookie = `${JMP_LANDING_REFERER_COOKIE}=${value}; Domain=.${apex}; Path=/; Max-Age=${LANDING_REFERER_COOKIE_MAX_AGE_SEC}; SameSite=Lax${secure}`;
125
+ }
126
+ function captureLandingAttribution(siteHostname) {
127
+ if (typeof document === "undefined" || typeof window === "undefined") {
128
+ return { landingReferer: null, utm: {} };
129
+ }
130
+ let landingReferer = null;
131
+ let utm = {};
69
132
  try {
133
+ const storedReferer = sessionStorage.getItem(JMP_LANDING_REFERER_KEY)?.trim() || null;
134
+ const cookieReferer = readLandingRefererCookie(siteHostname);
135
+ const preservedReferer = storedReferer || cookieReferer;
136
+ const currentReferer = document.referrer?.trim() || null;
137
+ if (currentReferer && isExternalReferrerUrl(currentReferer, siteHostname)) {
138
+ if (!storedReferer) {
139
+ sessionStorage.setItem(JMP_LANDING_REFERER_KEY, currentReferer);
140
+ }
141
+ writeLandingRefererCookie(currentReferer, siteHostname);
142
+ landingReferer = preservedReferer || currentReferer;
143
+ } else if (preservedReferer) {
144
+ landingReferer = preservedReferer;
145
+ if (!storedReferer) {
146
+ sessionStorage.setItem(JMP_LANDING_REFERER_KEY, preservedReferer);
147
+ }
148
+ }
70
149
  const params = new URLSearchParams(window.location.search);
71
- let stored = {};
72
- const raw = sessionStorage.getItem(UTM_SESSION_STORAGE_KEY);
73
- if (raw) {
74
- const parsed = JSON.parse(raw);
150
+ const rawUtm = sessionStorage.getItem(JMP_UTM_SESSION_KEY);
151
+ if (rawUtm) {
152
+ const parsed = JSON.parse(rawUtm);
75
153
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
76
- stored = parsed;
154
+ utm = parsed;
77
155
  }
78
156
  }
79
157
  let updated = false;
80
- for (const key of UTM_PARAM_KEYS) {
158
+ for (const key of JMP_UTM_PARAM_KEYS) {
81
159
  const value = params.get(key)?.trim();
82
160
  if (value) {
83
- stored[key] = value;
161
+ utm[key] = value;
84
162
  updated = true;
85
163
  }
86
164
  }
87
165
  if (updated) {
88
- sessionStorage.setItem(UTM_SESSION_STORAGE_KEY, JSON.stringify(stored));
166
+ sessionStorage.setItem(JMP_UTM_SESSION_KEY, JSON.stringify(utm));
89
167
  }
90
- for (const key of UTM_PARAM_KEYS) {
91
- if (metadata[key] === void 0 && stored[key]) {
92
- metadata[key] = stored[key];
93
- }
168
+ } catch {
169
+ }
170
+ return { landingReferer, utm };
171
+ }
172
+ function getOrCreateSessionIdFromStorage() {
173
+ if (typeof window === "undefined" || !globalThis.crypto?.randomUUID) {
174
+ return void 0;
175
+ }
176
+ try {
177
+ let id = sessionStorage.getItem(JMP_SESSION_ID_KEY);
178
+ if (!id) {
179
+ id = crypto.randomUUID();
180
+ sessionStorage.setItem(JMP_SESSION_ID_KEY, id);
94
181
  }
182
+ return id;
183
+ } catch {
184
+ return void 0;
185
+ }
186
+ }
187
+ function wasEarlyBeaconSentForPath(path) {
188
+ if (typeof window === "undefined") {
189
+ return false;
190
+ }
191
+ try {
192
+ return sessionStorage.getItem(JMP_EARLY_BEACON_PATH_KEY) === path;
193
+ } catch {
194
+ return false;
195
+ }
196
+ }
197
+ function markEarlyBeaconSentForPath(path) {
198
+ if (typeof window === "undefined") {
199
+ return;
200
+ }
201
+ try {
202
+ sessionStorage.setItem(JMP_EARLY_BEACON_PATH_KEY, path);
95
203
  } catch {
96
204
  }
97
205
  }
206
+ function sendEarlyPageViewBeacon(args) {
207
+ if (typeof window === "undefined" || typeof fetch === "undefined") {
208
+ return false;
209
+ }
210
+ const path = `${window.location.pathname}${window.location.search}` || "/";
211
+ if (wasEarlyBeaconSentForPath(path)) {
212
+ return false;
213
+ }
214
+ const { landingReferer, utm } = captureLandingAttribution(window.location.hostname);
215
+ const sessionId = getOrCreateSessionIdFromStorage();
216
+ const metadata = {
217
+ pageHostname: window.location.hostname,
218
+ earlyBeacon: true,
219
+ ...utm
220
+ };
221
+ if (landingReferer) {
222
+ metadata.referer = landingReferer;
223
+ }
224
+ const payload = {
225
+ type: "page_view",
226
+ path,
227
+ trackingKey: args.trackingKey,
228
+ ...sessionId ? { sessionId } : {},
229
+ metadata
230
+ };
231
+ const headers = {
232
+ "Content-Type": "application/json",
233
+ "X-JMP-Tracking-Key": args.trackingKey
234
+ };
235
+ if (landingReferer) {
236
+ headers["X-JMP-Visitor-Referer"] = landingReferer;
237
+ }
238
+ markEarlyBeaconSentForPath(path);
239
+ void fetch(`${args.baseUrl.replace(/\/$/, "")}/api/events`, {
240
+ method: "POST",
241
+ headers,
242
+ body: JSON.stringify(payload),
243
+ keepalive: true,
244
+ mode: "cors",
245
+ credentials: "omit"
246
+ }).catch(() => {
247
+ try {
248
+ sessionStorage.removeItem(JMP_EARLY_BEACON_PATH_KEY);
249
+ } catch {
250
+ }
251
+ });
252
+ return true;
253
+ }
254
+ function escapeInlineScript(value) {
255
+ return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
256
+ }
257
+ function buildLandingCaptureInlineScript(args) {
258
+ const baseUrl = escapeInlineScript(args.baseUrl.replace(/\/$/, ""));
259
+ const trackingKey = escapeInlineScript(args.trackingKey);
260
+ const cookieMaxAge = String(LANDING_REFERER_COOKIE_MAX_AGE_SEC);
261
+ 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)){if(!stored)sessionStorage.setItem('${JMP_LANDING_REFERER_KEY}',ref);writeCk(ref,h);lr=preserved||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){}})();`;
262
+ }
263
+
264
+ // src/index.ts
265
+ function captureAndGetLandingReferer() {
266
+ if (typeof window === "undefined") {
267
+ return null;
268
+ }
269
+ return captureLandingAttribution(window.location.hostname).landingReferer;
270
+ }
271
+ function mergeSessionUtms(metadata) {
272
+ if (typeof window === "undefined") {
273
+ return;
274
+ }
275
+ const { utm } = captureLandingAttribution(window.location.hostname);
276
+ for (const key of JMP_UTM_PARAM_KEYS) {
277
+ if (metadata[key] === void 0 && utm[key]) {
278
+ metadata[key] = utm[key];
279
+ }
280
+ }
281
+ }
98
282
  function splitPathAndSearch(pathInput) {
99
283
  const qIndex = pathInput.indexOf("?");
100
284
  if (qIndex === -1) {
@@ -118,21 +302,6 @@ function inferDevice() {
118
302
  }
119
303
  return window.innerWidth < 768 ? "mobile" : "desktop";
120
304
  }
121
- function getOrCreateSessionId() {
122
- if (typeof window === "undefined" || !globalThis.crypto?.randomUUID) {
123
- return void 0;
124
- }
125
- try {
126
- let id = sessionStorage.getItem(SESSION_STORAGE_KEY);
127
- if (!id) {
128
- id = crypto.randomUUID();
129
- sessionStorage.setItem(SESSION_STORAGE_KEY, id);
130
- }
131
- return id;
132
- } catch {
133
- return void 0;
134
- }
135
- }
136
305
  function createJmpAnalytics(config) {
137
306
  const baseUrl = normalizeBaseUrl(config.baseUrl);
138
307
  const resolvedFetch = config.fetchImpl ?? (typeof globalThis !== "undefined" && globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
@@ -156,7 +325,7 @@ function createJmpAnalytics(config) {
156
325
  headers["X-JMP-Visitor-Referer"] = landingReferer;
157
326
  }
158
327
  }
159
- const sid = getOrCreateSessionId();
328
+ const sid = getOrCreateSessionIdFromStorage();
160
329
  const payload = { ...body, ...sid ? { sessionId: sid } : {} };
161
330
  if (config.debug) {
162
331
  console.debug("[@jmp-technologies/analytics]", "POST", url, payload);
@@ -205,7 +374,7 @@ function createJmpAnalytics(config) {
205
374
  "Content-Type": "application/json",
206
375
  "X-JMP-Tracking-Key": config.trackingKey
207
376
  };
208
- const sid = getOrCreateSessionId();
377
+ const sid = getOrCreateSessionIdFromStorage();
209
378
  const payload = { ...body, ...sid ? { sessionId: sid } : {} };
210
379
  if (config.debug) {
211
380
  console.debug("[@jmp-technologies/analytics]", "POST", url, payload);
@@ -277,6 +446,9 @@ function createJmpAnalytics(config) {
277
446
  search = window.location.search.replace(/^\?/, "");
278
447
  }
279
448
  const path = search ? `${pathname}?${search}` : pathname;
449
+ if (wasEarlyBeaconSentForPath(path)) {
450
+ return Promise.resolve();
451
+ }
280
452
  if (shouldDedupePageView(pathname)) {
281
453
  return Promise.resolve();
282
454
  }
@@ -286,7 +458,7 @@ function createJmpAnalytics(config) {
286
458
  const params = new URLSearchParams(
287
459
  search ? `?${search}` : window.location.search
288
460
  );
289
- for (const key of UTM_PARAM_KEYS) {
461
+ for (const key of JMP_UTM_PARAM_KEYS) {
290
462
  const v = params.get(key);
291
463
  if (v && metadata[key] === void 0) {
292
464
  metadata[key] = v;
@@ -301,7 +473,6 @@ function createJmpAnalytics(config) {
301
473
  return send({
302
474
  type: "page_view",
303
475
  path: path || "/",
304
- source: options?.source ?? DEFAULT_SOURCE,
305
476
  device: options?.device ?? inferDevice(),
306
477
  country: options?.country,
307
478
  metadata
@@ -394,6 +565,13 @@ function createJmpAnalytics(config) {
394
565
  history.replaceState = replace;
395
566
  };
396
567
  }
568
+ if (typeof window !== "undefined") {
569
+ captureLandingAttribution(window.location.hostname);
570
+ sendEarlyPageViewBeacon({
571
+ baseUrl,
572
+ trackingKey: config.trackingKey
573
+ });
574
+ }
397
575
  return {
398
576
  track,
399
577
  trackPageView,
@@ -478,12 +656,35 @@ function JmpTrackedMailtoLink({
478
656
 
479
657
  // src/next.tsx
480
658
  var import_jsx_runtime2 = require("react/jsx-runtime");
659
+ function JmpLandingCaptureScript({
660
+ baseUrl = process.env.NEXT_PUBLIC_JMP_ANALYTICS_URL,
661
+ trackingKey = process.env.NEXT_PUBLIC_JMP_TRACKING_KEY
662
+ } = {}) {
663
+ const resolvedBase = baseUrl?.trim();
664
+ const resolvedKey = trackingKey?.trim();
665
+ if (!resolvedBase || !resolvedKey) {
666
+ return null;
667
+ }
668
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
669
+ import_script.default,
670
+ {
671
+ id: "jmp-landing-capture",
672
+ strategy: "beforeInteractive",
673
+ dangerouslySetInnerHTML: {
674
+ __html: buildLandingCaptureInlineScript({
675
+ baseUrl: resolvedBase,
676
+ trackingKey: resolvedKey
677
+ })
678
+ }
679
+ }
680
+ );
681
+ }
481
682
  function JmpNextPageViewTrackerInner() {
482
683
  const pathname = (0, import_navigation.usePathname)();
483
684
  const searchParams = (0, import_navigation.useSearchParams)();
484
685
  const query = searchParams?.toString() ?? "";
485
686
  const path = query ? `${pathname}?${query}` : pathname;
486
- (0, import_react2.useEffect)(() => {
687
+ (0, import_react2.useLayoutEffect)(() => {
487
688
  const client = getOrCreateJmpAnalyticsFromBrowserEnv();
488
689
  void client?.trackPageView({ path });
489
690
  }, [path]);
@@ -493,14 +694,19 @@ function JmpNextPageViewTracker() {
493
694
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react2.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(JmpNextPageViewTrackerInner, {}) });
494
695
  }
495
696
  function JmpNextAnalytics() {
496
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(JmpNextPageViewTracker, {});
697
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
698
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(JmpLandingCaptureScript, {}),
699
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(JmpNextPageViewTracker, {})
700
+ ] });
497
701
  }
498
702
  // Annotate the CommonJS export names for ESM import in node:
499
703
  0 && (module.exports = {
704
+ JmpLandingCaptureScript,
500
705
  JmpNextAnalytics,
501
706
  JmpNextPageViewTracker,
502
707
  JmpTrackedMailtoLink,
503
708
  JmpTrackedTelLink,
709
+ buildLandingCaptureInlineScript,
504
710
  useJmpAnalytics
505
711
  });
506
712
  //# sourceMappingURL=next.cjs.map