@jmp-technologies/analytics 0.1.24 → 0.1.26

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 (102) hide show
  1. package/README.md +22 -1
  2. package/dist/astro/client.cjs +103 -0
  3. package/dist/astro/client.cjs.map +1 -1
  4. package/dist/astro/client.d.cts +2 -2
  5. package/dist/astro/client.d.ts +2 -2
  6. package/dist/astro/client.js +5 -3
  7. package/dist/astro/client.js.map +1 -1
  8. package/dist/astro/content.cjs +185 -1
  9. package/dist/astro/content.cjs.map +1 -1
  10. package/dist/astro/content.d.cts +2 -2
  11. package/dist/astro/content.d.ts +2 -2
  12. package/dist/astro/content.js +4 -3
  13. package/dist/astro/integration.cjs +56 -10
  14. package/dist/astro/integration.cjs.map +1 -1
  15. package/dist/astro/integration.js +4 -3
  16. package/dist/astro/integration.js.map +1 -1
  17. package/dist/astro/middleware.cjs.map +1 -1
  18. package/dist/astro/middleware.d.cts +2 -2
  19. package/dist/astro/middleware.d.ts +2 -2
  20. package/dist/astro/middleware.js +2 -2
  21. package/dist/astro/register.cjs +56 -10
  22. package/dist/astro/register.cjs.map +1 -1
  23. package/dist/astro/register.js +4 -3
  24. package/dist/chunk-2KAGOVRZ.js +45 -0
  25. package/dist/chunk-2KAGOVRZ.js.map +1 -0
  26. package/dist/{chunk-MOX7RJUU.js → chunk-A4RRTY5D.js} +1 -1
  27. package/dist/{chunk-MOX7RJUU.js.map → chunk-A4RRTY5D.js.map} +1 -1
  28. package/dist/{chunk-7MLPSL6T.js → chunk-HODG4NJQ.js} +2 -2
  29. package/dist/{chunk-CDORZ2YJ.js → chunk-PKGUBYZR.js} +3 -24
  30. package/dist/chunk-PKGUBYZR.js.map +1 -0
  31. package/dist/{chunk-2AUF7ENL.js → chunk-RCOHQAGL.js} +160 -157
  32. package/dist/chunk-RCOHQAGL.js.map +1 -0
  33. package/dist/chunk-TCZ6TC2L.js +149 -0
  34. package/dist/chunk-TCZ6TC2L.js.map +1 -0
  35. package/dist/{chunk-WCI6VOVY.js → chunk-TFHOTBOL.js} +3 -3
  36. package/dist/{chunk-ZP4L5DCM.js → chunk-WFTTIRDJ.js} +2 -2
  37. package/dist/{chunk-GPPQ3TYI.js → chunk-XQZOGWF2.js} +3 -3
  38. package/dist/chunk-YCVHRMM6.js +28 -0
  39. package/dist/chunk-YCVHRMM6.js.map +1 -0
  40. package/dist/{create-jmp-analytics-BdGOgPxl.d.ts → create-jmp-analytics-JKAsJAWb.d.ts} +1 -1
  41. package/dist/{create-jmp-analytics-DxhHOmtR.d.cts → create-jmp-analytics-Mau-oxji.d.cts} +1 -1
  42. package/dist/index.cjs +157 -12
  43. package/dist/index.cjs.map +1 -1
  44. package/dist/index.d.cts +13 -7
  45. package/dist/index.d.ts +13 -7
  46. package/dist/index.js +13 -3
  47. package/dist/next/content.cjs +185 -1
  48. package/dist/next/content.cjs.map +1 -1
  49. package/dist/next/content.d.cts +2 -2
  50. package/dist/next/content.d.ts +2 -2
  51. package/dist/next/content.js +4 -3
  52. package/dist/next/content.js.map +1 -1
  53. package/dist/next/google-tag.cjs +80 -0
  54. package/dist/next/google-tag.cjs.map +1 -0
  55. package/dist/next/google-tag.d.cts +17 -0
  56. package/dist/next/google-tag.d.ts +17 -0
  57. package/dist/next/google-tag.js +8 -0
  58. package/dist/next/google-tag.js.map +1 -0
  59. package/dist/next/instrumentation.cjs +56 -10
  60. package/dist/next/instrumentation.cjs.map +1 -1
  61. package/dist/next/instrumentation.js +3 -2
  62. package/dist/next/instrumentation.js.map +1 -1
  63. package/dist/next/landing-capture-script-server.cjs +61 -0
  64. package/dist/next/landing-capture-script-server.cjs.map +1 -0
  65. package/dist/next/landing-capture-script-server.d.cts +14 -0
  66. package/dist/next/landing-capture-script-server.d.ts +14 -0
  67. package/dist/next/landing-capture-script-server.js +8 -0
  68. package/dist/next/landing-capture-script-server.js.map +1 -0
  69. package/dist/next/middleware.cjs.map +1 -1
  70. package/dist/next/middleware.d.cts +2 -2
  71. package/dist/next/middleware.d.ts +2 -2
  72. package/dist/next/middleware.js +2 -2
  73. package/dist/next/site-root-server.cjs +351 -82
  74. package/dist/next/site-root-server.cjs.map +1 -1
  75. package/dist/next/site-root-server.d.cts +9 -1
  76. package/dist/next/site-root-server.d.ts +9 -1
  77. package/dist/next/site-root-server.js +51 -16
  78. package/dist/next/site-root-server.js.map +1 -1
  79. package/dist/next.cjs +210 -85
  80. package/dist/next.cjs.map +1 -1
  81. package/dist/next.d.cts +8 -15
  82. package/dist/next.d.ts +8 -15
  83. package/dist/next.js +11 -7
  84. package/dist/react.cjs +103 -0
  85. package/dist/react.cjs.map +1 -1
  86. package/dist/react.d.cts +2 -2
  87. package/dist/react.d.ts +2 -2
  88. package/dist/react.js +4 -3
  89. package/dist/{site-config-DpMgrfeN.d.ts → site-config-CXUdGLqw.d.cts} +6 -3
  90. package/dist/{site-config-BXs91Yex.d.cts → site-config-w_Db9YSk.d.ts} +6 -3
  91. package/dist/{track-page-view-Cr8I4Unm.d.cts → track-page-view-CkuSYuco.d.cts} +1 -1
  92. package/dist/{track-page-view-CcPlzQNB.d.ts → track-page-view-QRSk8vap.d.ts} +1 -1
  93. package/dist/{types-BtiK_Acj.d.cts → types-C1eq8E5u.d.cts} +14 -4
  94. package/dist/{types-BtiK_Acj.d.ts → types-C1eq8E5u.d.ts} +14 -4
  95. package/docs/astro-integration.md +1 -1
  96. package/package.json +7 -2
  97. package/dist/chunk-2AUF7ENL.js.map +0 -1
  98. package/dist/chunk-CDORZ2YJ.js.map +0 -1
  99. /package/dist/{chunk-7MLPSL6T.js.map → chunk-HODG4NJQ.js.map} +0 -0
  100. /package/dist/{chunk-WCI6VOVY.js.map → chunk-TFHOTBOL.js.map} +0 -0
  101. /package/dist/{chunk-ZP4L5DCM.js.map → chunk-WFTTIRDJ.js.map} +0 -0
  102. /package/dist/{chunk-GPPQ3TYI.js.map → chunk-XQZOGWF2.js.map} +0 -0
@@ -34,6 +34,235 @@ __export(site_root_server_exports, {
34
34
  });
35
35
  module.exports = __toCommonJS(site_root_server_exports);
36
36
 
37
+ // src/constants.ts
38
+ var JMP_ANALYTICS_SDK_VERSION = "0.1.25";
39
+ var JMP_TRACKING_CONSENT_STORAGE_KEY = "jmp_analytics_consent_v1";
40
+ var DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS = 2500;
41
+
42
+ // src/http.ts
43
+ function responseLooksLikeHtml(body) {
44
+ const s = body.trimStart();
45
+ return s.startsWith("<!") || s.toLowerCase().startsWith("<html") || s.startsWith("<") && /<html[\s>]/i.test(s.slice(0, 500));
46
+ }
47
+ function normalizeBaseUrl(baseUrl) {
48
+ return baseUrl.replace(/\/$/, "");
49
+ }
50
+ function resolveFetch(fetchImpl, context) {
51
+ const resolved = fetchImpl ?? (typeof globalThis !== "undefined" && globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
52
+ if (!resolved) {
53
+ throw new Error(`${context}: fetch is not available; pass fetchImpl in config`);
54
+ }
55
+ return resolved;
56
+ }
57
+ function formatApiErrorMessage(status, text, prefix) {
58
+ if (responseLooksLikeHtml(text)) {
59
+ return `${prefix} HTTP ${status} \u2014 response was HTML (not the JSON API). Check baseUrl points at the JMP Analytics dashboard.`;
60
+ }
61
+ let parsed = {};
62
+ try {
63
+ parsed = JSON.parse(text);
64
+ } catch {
65
+ parsed = { error: text };
66
+ }
67
+ const detail = parsed.error ?? text;
68
+ return [
69
+ `${prefix} HTTP ${status}`,
70
+ parsed.code ? `[${parsed.code}]` : null,
71
+ detail,
72
+ parsed.hint ? `Hint: ${parsed.hint}` : null,
73
+ parsed.expectedDomain ? `Domain in JMP admin: ${parsed.expectedDomain}` : null
74
+ ].filter(Boolean).join(" \u2014 ");
75
+ }
76
+
77
+ // src/content/fetch.ts
78
+ async function getContent(config, path) {
79
+ const text = await getContentText(config, path);
80
+ try {
81
+ return JSON.parse(text);
82
+ } catch {
83
+ const baseUrl = normalizeBaseUrl(config.baseUrl);
84
+ const url = `${baseUrl}${path}`;
85
+ throw new Error(`JMP content HTTP 200 \u2014 ${url} returned invalid JSON.`);
86
+ }
87
+ }
88
+ async function getContentText(config, path) {
89
+ const baseUrl = normalizeBaseUrl(config.baseUrl);
90
+ const resolvedFetch = config.fetchImpl ?? (typeof globalThis !== "undefined" && globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
91
+ if (!resolvedFetch) {
92
+ throw new Error("JMP content: fetch is not available; pass fetchImpl in config");
93
+ }
94
+ const url = `${baseUrl}${path}`;
95
+ if (config.debug) {
96
+ console.debug("[@jmp-technologies/analytics]", "GET", url);
97
+ }
98
+ const res = await resolvedFetch(url, {
99
+ method: "GET",
100
+ headers: {
101
+ "X-JMP-Tracking-Key": config.trackingKey
102
+ }
103
+ });
104
+ const text = await res.text();
105
+ if (!res.ok) {
106
+ if (responseLooksLikeHtml(text)) {
107
+ throw new Error(
108
+ `JMP content HTTP ${res.status} \u2014 ${url} returned HTML (not the JSON API). Set baseUrl to your JMP Analytics dashboard origin.`
109
+ );
110
+ }
111
+ let parsed = {};
112
+ try {
113
+ parsed = JSON.parse(text);
114
+ } catch {
115
+ parsed = { error: text };
116
+ }
117
+ throw new Error(
118
+ [`JMP content HTTP ${res.status}`, parsed.error, parsed.hint].filter(Boolean).join(" \u2014 ")
119
+ );
120
+ }
121
+ return text;
122
+ }
123
+
124
+ // src/content/wrapper-discovery.ts
125
+ var WRAPPER_SLOT_ORDER = [
126
+ "blog",
127
+ "faq",
128
+ "contact",
129
+ "trust",
130
+ "trust.summary",
131
+ "trust.testimonials",
132
+ "trust.reviews",
133
+ "trust.stats",
134
+ "trust.logos",
135
+ "trust.credentials"
136
+ ];
137
+ var discovered = /* @__PURE__ */ new Set();
138
+ var syncTimer = null;
139
+ var pendingSyncConfig = null;
140
+ var reportedDedupeKeys = /* @__PURE__ */ new Set();
141
+ var syncHandler = null;
142
+ function markContentWrapper(slot) {
143
+ const normalized = slot.trim().toLowerCase();
144
+ if (!normalized) {
145
+ return;
146
+ }
147
+ discovered.add(normalized);
148
+ }
149
+ function getDiscoveredContentWrappers() {
150
+ return sortWrapperSlots([...discovered]);
151
+ }
152
+ function resolveContentWrappersForReport(config) {
153
+ const explicit = config.contentWrappers ?? [];
154
+ const auto = getDiscoveredContentWrappers();
155
+ if (config.contentWrapperMode === "manual") {
156
+ return sortWrapperSlots([...explicit]);
157
+ }
158
+ if (config.contentWrapperMode === "explicit-only") {
159
+ return sortWrapperSlots([...explicit]);
160
+ }
161
+ const merged = /* @__PURE__ */ new Set([...explicit, ...auto]);
162
+ return sortWrapperSlots([...merged]);
163
+ }
164
+ function hasReportedContentWrappers(dedupeKey) {
165
+ return reportedDedupeKeys.has(dedupeKey);
166
+ }
167
+ function rememberReportedContentWrappers(dedupeKey) {
168
+ reportedDedupeKeys.add(dedupeKey);
169
+ }
170
+ function setContentWrapperSyncHandler(handler) {
171
+ syncHandler = handler;
172
+ }
173
+ function scheduleContentWrapperSync(config) {
174
+ if (!syncHandler) {
175
+ return;
176
+ }
177
+ pendingSyncConfig = config;
178
+ if (syncTimer) {
179
+ clearTimeout(syncTimer);
180
+ }
181
+ syncTimer = setTimeout(() => {
182
+ syncTimer = null;
183
+ const cfg = pendingSyncConfig;
184
+ pendingSyncConfig = null;
185
+ if (!cfg || !syncHandler) {
186
+ return;
187
+ }
188
+ void syncHandler(cfg).catch(() => {
189
+ });
190
+ }, 750);
191
+ }
192
+ function sortWrapperSlots(slots) {
193
+ const order = new Map(WRAPPER_SLOT_ORDER.map((slot, index) => [slot, index]));
194
+ return [...new Set(slots)].sort((a, b) => {
195
+ const ai = order.get(a) ?? 999;
196
+ const bi = order.get(b) ?? 999;
197
+ if (ai !== bi) {
198
+ return ai - bi;
199
+ }
200
+ return a.localeCompare(b);
201
+ });
202
+ }
203
+
204
+ // src/content/api.ts
205
+ function afterContentWrapperUse(config) {
206
+ scheduleContentWrapperSync(config);
207
+ }
208
+ async function getJmpSiteProfile(config) {
209
+ const data = await getContent(
210
+ config,
211
+ "/api/content/site"
212
+ );
213
+ markContentWrapper("contact");
214
+ afterContentWrapperUse(config);
215
+ return data.site;
216
+ }
217
+ async function registerJmpContentWrappers(config) {
218
+ const wrappers = resolveContentWrappersForReport(config);
219
+ if (!wrappers.length) {
220
+ return { ok: false, skipped: true };
221
+ }
222
+ const dedupeKey = `${config.trackingKey}:${[...wrappers].sort().join(",")}`;
223
+ if (hasReportedContentWrappers(dedupeKey)) {
224
+ return { ok: true, wrappers };
225
+ }
226
+ const baseUrl = normalizeBaseUrl(config.baseUrl);
227
+ const resolvedFetch = config.fetchImpl ?? (typeof globalThis !== "undefined" && globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
228
+ if (!resolvedFetch) {
229
+ throw new Error(
230
+ "JMP content wrappers: fetch is not available; pass fetchImpl in config"
231
+ );
232
+ }
233
+ const url = `${baseUrl}/api/content/capabilities`;
234
+ if (config.debug) {
235
+ console.debug("[@jmp-technologies/analytics]", "POST", url, { wrappers });
236
+ }
237
+ const res = await resolvedFetch(url, {
238
+ method: "POST",
239
+ headers: {
240
+ "Content-Type": "application/json",
241
+ "X-JMP-Tracking-Key": config.trackingKey
242
+ },
243
+ body: JSON.stringify({
244
+ wrappers,
245
+ sdkVersion: JMP_ANALYTICS_SDK_VERSION
246
+ })
247
+ });
248
+ const text = await res.text();
249
+ if (!res.ok) {
250
+ let parsed = {};
251
+ try {
252
+ parsed = JSON.parse(text);
253
+ } catch {
254
+ parsed = { error: text };
255
+ }
256
+ throw new Error(
257
+ [`JMP content wrappers HTTP ${res.status}`, parsed.error, parsed.hint].filter(Boolean).join(" \u2014 ")
258
+ );
259
+ }
260
+ rememberReportedContentWrappers(dedupeKey);
261
+ const data = JSON.parse(text);
262
+ return { ok: true, wrappers: data.wrappers ?? wrappers };
263
+ }
264
+ setContentWrapperSyncHandler(registerJmpContentWrappers);
265
+
37
266
  // src/env-public.ts
38
267
  function firstDefined(...values) {
39
268
  for (const v of values) {
@@ -84,11 +313,22 @@ var registeredSite = null;
84
313
  function getJmpSiteDefinition() {
85
314
  return registeredSite;
86
315
  }
316
+ function getJmpContentConfig() {
317
+ const { analyticsUrl, trackingKey } = readJmpPublicEnvFromProcess();
318
+ if (!analyticsUrl || !trackingKey) {
319
+ return null;
320
+ }
321
+ const site = getJmpSiteDefinition();
322
+ return {
323
+ baseUrl: analyticsUrl,
324
+ trackingKey,
325
+ contentWrappers: site?.contentWrappers ?? void 0
326
+ };
327
+ }
87
328
 
88
329
  // src/next.tsx
89
- var import_script = __toESM(require("next/script"), 1);
90
- var import_react2 = require("react");
91
- var import_navigation = require("next/navigation");
330
+ var import_react3 = require("react");
331
+ var import_navigation2 = require("next/navigation");
92
332
 
93
333
  // src/landing-capture.ts
94
334
  var JMP_SESSION_ID_KEY = "jmp_analytics_sid";
@@ -232,10 +472,6 @@ function buildLandingCaptureInlineScript() {
232
472
  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){}})();`;
233
473
  }
234
474
 
235
- // src/constants.ts
236
- var JMP_TRACKING_CONSENT_STORAGE_KEY = "jmp_analytics_consent_v1";
237
- var DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS = 2500;
238
-
239
475
  // src/tracker/page-view-dedupe.ts
240
476
  var JMP_PAGE_VIEW_DEDUPE_STORAGE_KEY = "jmp_analytics_last_pv";
241
477
  var moduleLastPath = "";
@@ -433,41 +669,6 @@ function setJmpTrackingConsent(granted) {
433
669
  }
434
670
  }
435
671
 
436
- // src/http.ts
437
- function responseLooksLikeHtml(body) {
438
- const s = body.trimStart();
439
- return s.startsWith("<!") || s.toLowerCase().startsWith("<html") || s.startsWith("<") && /<html[\s>]/i.test(s.slice(0, 500));
440
- }
441
- function normalizeBaseUrl(baseUrl) {
442
- return baseUrl.replace(/\/$/, "");
443
- }
444
- function resolveFetch(fetchImpl, context) {
445
- const resolved = fetchImpl ?? (typeof globalThis !== "undefined" && globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
446
- if (!resolved) {
447
- throw new Error(`${context}: fetch is not available; pass fetchImpl in config`);
448
- }
449
- return resolved;
450
- }
451
- function formatApiErrorMessage(status, text, prefix) {
452
- if (responseLooksLikeHtml(text)) {
453
- return `${prefix} HTTP ${status} \u2014 response was HTML (not the JSON API). Check baseUrl points at the JMP Analytics dashboard.`;
454
- }
455
- let parsed = {};
456
- try {
457
- parsed = JSON.parse(text);
458
- } catch {
459
- parsed = { error: text };
460
- }
461
- const detail = parsed.error ?? text;
462
- return [
463
- `${prefix} HTTP ${status}`,
464
- parsed.code ? `[${parsed.code}]` : null,
465
- detail,
466
- parsed.hint ? `Hint: ${parsed.hint}` : null,
467
- parsed.expectedDomain ? `Domain in JMP admin: ${parsed.expectedDomain}` : null
468
- ].filter(Boolean).join(" \u2014 ");
469
- }
470
-
471
672
  // src/tracker/helpers.ts
472
673
  function captureAndGetLandingReferer() {
473
674
  if (typeof window === "undefined") {
@@ -875,33 +1076,57 @@ function getOrCreateJmpAnalyticsForNextApp(overrides) {
875
1076
  var import_react = require("react");
876
1077
  var import_jsx_runtime = require("react/jsx-runtime");
877
1078
 
878
- // src/next.tsx
1079
+ // src/next/google-tag.tsx
1080
+ var import_script = __toESM(require("next/script"), 1);
1081
+ var import_navigation = require("next/navigation");
1082
+ var import_react2 = require("react");
879
1083
  var import_jsx_runtime2 = require("react/jsx-runtime");
880
- function JmpLandingCaptureScript({
881
- trackingKey = readJmpPublicEnvFromProcess().trackingKey
882
- } = {}) {
883
- if (!trackingKey?.trim()) {
884
- return null;
885
- }
886
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
887
- import_script.default,
888
- {
889
- id: "jmp-landing-capture",
890
- strategy: "beforeInteractive",
891
- dangerouslySetInnerHTML: {
892
- __html: buildLandingCaptureInlineScript()
1084
+ function JmpGoogleTag({ gaId }) {
1085
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
1086
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1087
+ import_script.default,
1088
+ {
1089
+ src: `https://www.googletagmanager.com/gtag/js?id=${gaId}`,
1090
+ strategy: "lazyOnload"
893
1091
  }
1092
+ ),
1093
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_script.default, { id: "jmp-ga4-init", strategy: "lazyOnload", children: `
1094
+ window.dataLayer = window.dataLayer || [];
1095
+ function gtag(){dataLayer.push(arguments);}
1096
+ gtag('js', new Date());
1097
+ gtag('config', '${gaId}', { send_page_view: true });
1098
+ ` }),
1099
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(JmpGoogleTagPagePath, { gaId })
1100
+ ] });
1101
+ }
1102
+ function JmpGoogleTagPagePath({ gaId }) {
1103
+ const pathname = (0, import_navigation.usePathname)();
1104
+ const isFirstNavigation = (0, import_react2.useRef)(true);
1105
+ (0, import_react2.useEffect)(() => {
1106
+ if (isFirstNavigation.current) {
1107
+ isFirstNavigation.current = false;
1108
+ return;
894
1109
  }
895
- );
1110
+ const report = () => {
1111
+ window.gtag?.("config", gaId, { page_path: pathname || "/" });
1112
+ };
1113
+ report();
1114
+ const timer = window.setTimeout(report, 100);
1115
+ return () => window.clearTimeout(timer);
1116
+ }, [gaId, pathname]);
1117
+ return null;
896
1118
  }
1119
+
1120
+ // src/next.tsx
1121
+ var import_jsx_runtime3 = require("react/jsx-runtime");
897
1122
  function JmpNextPageViewTrackerInner({
898
1123
  configOverrides
899
1124
  }) {
900
- const pathname = (0, import_navigation.usePathname)();
901
- const searchParams = (0, import_navigation.useSearchParams)();
1125
+ const pathname = (0, import_navigation2.usePathname)();
1126
+ const searchParams = (0, import_navigation2.useSearchParams)();
902
1127
  const query = searchParams?.toString() ?? "";
903
1128
  const path = query ? `${pathname}?${query}` : pathname;
904
- (0, import_react2.useLayoutEffect)(() => {
1129
+ (0, import_react3.useLayoutEffect)(() => {
905
1130
  const client = getOrCreateJmpAnalyticsForNextApp(configOverrides);
906
1131
  void client?.trackPageView({ path });
907
1132
  }, [path, configOverrides]);
@@ -910,10 +1135,10 @@ function JmpNextPageViewTrackerInner({
910
1135
  function JmpNextPageViewTracker({
911
1136
  configOverrides
912
1137
  }) {
913
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react2.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(JmpNextPageViewTrackerInner, { configOverrides }) });
1138
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react3.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(JmpNextPageViewTrackerInner, { configOverrides }) });
914
1139
  }
915
1140
  function useJmpTrackingActive(deferUntilConsent) {
916
- const consentGranted = (0, import_react2.useSyncExternalStore)(
1141
+ const consentGranted = (0, import_react3.useSyncExternalStore)(
917
1142
  subscribeJmpTrackingConsent,
918
1143
  () => isJmpTrackingConsentGranted(),
919
1144
  () => false
@@ -933,7 +1158,7 @@ function jmpConfigOverridesFromProps(props) {
933
1158
  function JmpNextAnalyticsBootstrap({
934
1159
  configOverrides
935
1160
  }) {
936
- (0, import_react2.useLayoutEffect)(() => {
1161
+ (0, import_react3.useLayoutEffect)(() => {
937
1162
  getOrCreateJmpAnalyticsForNextApp(configOverrides);
938
1163
  }, [configOverrides]);
939
1164
  return null;
@@ -944,17 +1169,16 @@ function JmpNextAnalytics({
944
1169
  trackingKey
945
1170
  } = {}) {
946
1171
  const active = useJmpTrackingActive(deferUntilConsent);
947
- const configOverrides = (0, import_react2.useMemo)(
1172
+ const configOverrides = (0, import_react3.useMemo)(
948
1173
  () => jmpConfigOverridesFromProps({ analyticsUrl, trackingKey }),
949
1174
  [analyticsUrl, trackingKey]
950
1175
  );
951
1176
  if (!active) {
952
1177
  return null;
953
1178
  }
954
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
955
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(JmpNextAnalyticsBootstrap, { configOverrides }),
956
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(JmpLandingCaptureScript, { trackingKey }),
957
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(JmpNextPageViewTracker, { configOverrides })
1179
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1180
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(JmpNextAnalyticsBootstrap, { configOverrides }),
1181
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(JmpNextPageViewTracker, { configOverrides })
958
1182
  ] });
959
1183
  }
960
1184
  function JmpSiteRoot({
@@ -963,8 +1187,8 @@ function JmpSiteRoot({
963
1187
  analyticsUrl,
964
1188
  trackingKey
965
1189
  }) {
966
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
967
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1190
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1191
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
968
1192
  JmpNextAnalytics,
969
1193
  {
970
1194
  deferUntilConsent,
@@ -976,23 +1200,68 @@ function JmpSiteRoot({
976
1200
  ] });
977
1201
  }
978
1202
 
1203
+ // src/next/landing-capture-script-server.tsx
1204
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1205
+ function JmpLandingCaptureScriptServer({
1206
+ trackingKey
1207
+ } = {}) {
1208
+ if (!trackingKey?.trim()) {
1209
+ return null;
1210
+ }
1211
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1212
+ "script",
1213
+ {
1214
+ dangerouslySetInnerHTML: {
1215
+ __html: buildLandingCaptureInlineScript()
1216
+ },
1217
+ id: "jmp-landing-capture",
1218
+ suppressHydrationWarning: true
1219
+ }
1220
+ );
1221
+ }
1222
+
979
1223
  // src/next/site-root-server.tsx
980
- var import_jsx_runtime3 = require("react/jsx-runtime");
981
- function JmpSiteRootServer({
1224
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1225
+ async function resolveGaMeasurementId(gaFromPortal) {
1226
+ const fromEnv = process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID?.trim();
1227
+ if (fromEnv) {
1228
+ return fromEnv;
1229
+ }
1230
+ if (!gaFromPortal) {
1231
+ return null;
1232
+ }
1233
+ const config = getJmpContentConfig();
1234
+ if (!config) {
1235
+ return null;
1236
+ }
1237
+ try {
1238
+ const profile = await getJmpSiteProfile(config);
1239
+ return profile.ga_measurement_id?.trim() || null;
1240
+ } catch {
1241
+ return null;
1242
+ }
1243
+ }
1244
+ async function JmpSiteRootServer({
982
1245
  children,
983
- deferUntilConsent
1246
+ deferUntilConsent,
1247
+ gaFromPortal = true
984
1248
  }) {
985
1249
  const pub = readJmpPublicEnvFromProcess();
986
1250
  const site = getJmpSiteDefinition();
987
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
988
- JmpSiteRoot,
989
- {
990
- analyticsUrl: pub.analyticsUrl ?? site?.analyticsPortalUrl,
991
- trackingKey: pub.trackingKey,
992
- deferUntilConsent,
993
- children
994
- }
995
- );
1251
+ const gaId = await resolveGaMeasurementId(gaFromPortal);
1252
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1253
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(JmpLandingCaptureScriptServer, { trackingKey: pub.trackingKey }),
1254
+ gaId ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(JmpGoogleTag, { gaId }) : null,
1255
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1256
+ JmpSiteRoot,
1257
+ {
1258
+ analyticsUrl: pub.analyticsUrl ?? site?.analyticsPortalUrl,
1259
+ trackingKey: pub.trackingKey,
1260
+ deferUntilConsent,
1261
+ children
1262
+ }
1263
+ )
1264
+ ] });
996
1265
  }
997
1266
  // Annotate the CommonJS export names for ESM import in node:
998
1267
  0 && (module.exports = {