@jmp-technologies/analytics 0.1.20 → 0.1.22
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/README.md +7 -7
- package/dist/astro/client.cjs +93 -108
- package/dist/astro/client.cjs.map +1 -1
- package/dist/astro/client.d.cts +3 -12
- package/dist/astro/client.d.ts +3 -12
- package/dist/astro/client.js +4 -2
- package/dist/astro/client.js.map +1 -1
- package/dist/astro/content.cjs +106 -98
- package/dist/astro/content.cjs.map +1 -1
- package/dist/astro/content.d.cts +2 -2
- package/dist/astro/content.d.ts +2 -2
- package/dist/astro/content.js +3 -2
- package/dist/astro/integration.cjs +68 -60
- package/dist/astro/integration.cjs.map +1 -1
- package/dist/astro/integration.js +3 -2
- package/dist/astro/integration.js.map +1 -1
- package/dist/astro/middleware.cjs +66 -63
- package/dist/astro/middleware.cjs.map +1 -1
- package/dist/astro/middleware.d.cts +2 -2
- package/dist/astro/middleware.d.ts +2 -2
- package/dist/astro/middleware.js +2 -2
- package/dist/astro/register.cjs +68 -60
- package/dist/astro/register.cjs.map +1 -1
- package/dist/astro/register.js +3 -2
- package/dist/{chunk-UDNKWFBM.js → chunk-3U2BSIYX.js} +5 -3
- package/dist/{chunk-UDNKWFBM.js.map → chunk-3U2BSIYX.js.map} +1 -1
- package/dist/chunk-4OOWA55U.js +215 -0
- package/dist/chunk-4OOWA55U.js.map +1 -0
- package/dist/{chunk-GFCBZN6S.js → chunk-AHRVNEFV.js} +80 -276
- package/dist/chunk-AHRVNEFV.js.map +1 -0
- package/dist/{chunk-ALTSW5DB.js → chunk-DQ6A6HTY.js} +12 -8
- package/dist/chunk-DQ6A6HTY.js.map +1 -0
- package/dist/{chunk-QIWGUF7V.js → chunk-IYFVJ2EY.js} +6 -4
- package/dist/{chunk-QIWGUF7V.js.map → chunk-IYFVJ2EY.js.map} +1 -1
- package/dist/{chunk-NZS4M3V3.js → chunk-TEFKM3GW.js} +2 -2
- package/dist/chunk-TEFKM3GW.js.map +1 -0
- package/dist/create-jmp-analytics-BdGOgPxl.d.ts +70 -0
- package/dist/create-jmp-analytics-DxhHOmtR.d.cts +70 -0
- package/dist/env-public-B6Y0DG68.d.cts +14 -0
- package/dist/env-public-B6Y0DG68.d.ts +14 -0
- package/dist/index.cjs +203 -198
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -340
- package/dist/index.d.ts +16 -340
- package/dist/index.js +9 -5
- package/dist/landing-capture-C8pyZWuF.d.cts +4 -0
- package/dist/landing-capture-C8pyZWuF.d.ts +4 -0
- package/dist/next/content.cjs +106 -98
- package/dist/next/content.cjs.map +1 -1
- package/dist/next/content.d.cts +2 -1
- package/dist/next/content.d.ts +2 -1
- package/dist/next/content.js +3 -2
- package/dist/next/content.js.map +1 -1
- package/dist/next/instrumentation.cjs +68 -60
- package/dist/next/instrumentation.cjs.map +1 -1
- package/dist/next/instrumentation.js +4 -2
- package/dist/next/instrumentation.js.map +1 -1
- package/dist/next/middleware.cjs +66 -63
- package/dist/next/middleware.cjs.map +1 -1
- package/dist/next/middleware.d.cts +2 -2
- package/dist/next/middleware.d.ts +2 -2
- package/dist/next/middleware.js +2 -2
- package/dist/next.cjs +152 -123
- package/dist/next.cjs.map +1 -1
- package/dist/next.d.cts +15 -3
- package/dist/next.d.ts +15 -3
- package/dist/next.js +45 -7
- package/dist/next.js.map +1 -1
- package/dist/react.cjs +115 -118
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +2 -1
- package/dist/react.d.ts +2 -1
- package/dist/react.js +3 -2
- package/dist/site-config-BRuZWGSW.d.cts +46 -0
- package/dist/site-config-hkGMSeT7.d.ts +46 -0
- package/dist/{track-page-view-CoYo96n0.d.cts → track-page-view-BLS0YfL-.d.ts} +1 -1
- package/dist/{track-page-view-DAy0I0Vb.d.ts → track-page-view-DfCyWHqe.d.cts} +1 -1
- package/dist/types-BtiK_Acj.d.cts +224 -0
- package/dist/types-BtiK_Acj.d.ts +224 -0
- package/package.json +2 -4
- package/dist/chunk-ALTSW5DB.js.map +0 -1
- package/dist/chunk-GFCBZN6S.js.map +0 -1
- package/dist/chunk-NZS4M3V3.js.map +0 -1
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatApiErrorMessage,
|
|
3
|
+
normalizeBaseUrl,
|
|
4
|
+
readJmpPublicEnvFromProcess,
|
|
5
|
+
resolveFetch,
|
|
6
|
+
responseLooksLikeHtml
|
|
7
|
+
} from "./chunk-4OOWA55U.js";
|
|
8
|
+
|
|
1
9
|
// src/landing-capture.ts
|
|
2
10
|
var JMP_SESSION_ID_KEY = "jmp_analytics_sid";
|
|
3
11
|
var JMP_LANDING_REFERER_KEY = "jmp_analytics_landing_referer";
|
|
@@ -259,104 +267,12 @@ function classifyContactHref(href) {
|
|
|
259
267
|
return null;
|
|
260
268
|
}
|
|
261
269
|
|
|
262
|
-
// src/
|
|
263
|
-
|
|
264
|
-
if (typeof process === "undefined" || !process.env) return void 0;
|
|
265
|
-
const v = process.env[name];
|
|
266
|
-
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
267
|
-
}
|
|
268
|
-
function firstDefined(...values) {
|
|
269
|
-
for (const v of values) {
|
|
270
|
-
if (v) return v;
|
|
271
|
-
}
|
|
272
|
-
return void 0;
|
|
273
|
-
}
|
|
274
|
-
function readJmpPublicEnvFromProcess(overrides) {
|
|
275
|
-
const analyticsUrl = firstDefined(
|
|
276
|
-
overrides?.analyticsUrl,
|
|
277
|
-
readProcessEnv("NEXT_PUBLIC_JMP_ANALYTICS_URL"),
|
|
278
|
-
readProcessEnv("PUBLIC_JMP_ANALYTICS_URL")
|
|
279
|
-
);
|
|
280
|
-
const trackingKey = firstDefined(
|
|
281
|
-
overrides?.trackingKey,
|
|
282
|
-
readProcessEnv("NEXT_PUBLIC_JMP_TRACKING_KEY"),
|
|
283
|
-
readProcessEnv("PUBLIC_JMP_TRACKING_KEY")
|
|
284
|
-
);
|
|
285
|
-
const requireConsentRaw = firstDefined(
|
|
286
|
-
overrides?.requireConsent !== void 0 ? String(overrides.requireConsent) : void 0,
|
|
287
|
-
readProcessEnv("NEXT_PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT"),
|
|
288
|
-
readProcessEnv("PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT")
|
|
289
|
-
);
|
|
290
|
-
const autoContactRaw = firstDefined(
|
|
291
|
-
overrides?.autoContactLinks !== void 0 ? String(overrides.autoContactLinks) : void 0,
|
|
292
|
-
readProcessEnv("NEXT_PUBLIC_JMP_AUTO_CONTACT_LINKS"),
|
|
293
|
-
readProcessEnv("PUBLIC_JMP_AUTO_CONTACT_LINKS")
|
|
294
|
-
);
|
|
295
|
-
return {
|
|
296
|
-
analyticsUrl,
|
|
297
|
-
trackingKey,
|
|
298
|
-
requireConsent: requireConsentRaw === "true",
|
|
299
|
-
autoContactLinks: autoContactRaw === "true"
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
function readJmpPublicEnvFromImportMeta(meta, overrides) {
|
|
303
|
-
const get = (key) => {
|
|
304
|
-
const v = meta?.[key];
|
|
305
|
-
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
306
|
-
};
|
|
307
|
-
const analyticsUrl = firstDefined(
|
|
308
|
-
overrides?.analyticsUrl,
|
|
309
|
-
get("PUBLIC_JMP_ANALYTICS_URL"),
|
|
310
|
-
get("NEXT_PUBLIC_JMP_ANALYTICS_URL")
|
|
311
|
-
);
|
|
312
|
-
const trackingKey = firstDefined(
|
|
313
|
-
overrides?.trackingKey,
|
|
314
|
-
get("PUBLIC_JMP_TRACKING_KEY"),
|
|
315
|
-
get("NEXT_PUBLIC_JMP_TRACKING_KEY")
|
|
316
|
-
);
|
|
317
|
-
const requireConsentRaw = firstDefined(
|
|
318
|
-
overrides?.requireConsent !== void 0 ? String(overrides.requireConsent) : void 0,
|
|
319
|
-
get("PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT"),
|
|
320
|
-
get("NEXT_PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT")
|
|
321
|
-
);
|
|
322
|
-
const autoContactRaw = firstDefined(
|
|
323
|
-
overrides?.autoContactLinks !== void 0 ? String(overrides.autoContactLinks) : void 0,
|
|
324
|
-
get("PUBLIC_JMP_AUTO_CONTACT_LINKS"),
|
|
325
|
-
get("NEXT_PUBLIC_JMP_AUTO_CONTACT_LINKS")
|
|
326
|
-
);
|
|
327
|
-
return {
|
|
328
|
-
analyticsUrl,
|
|
329
|
-
trackingKey,
|
|
330
|
-
requireConsent: requireConsentRaw === "true",
|
|
331
|
-
autoContactLinks: autoContactRaw === "true"
|
|
332
|
-
};
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// src/site-config.ts
|
|
336
|
-
var registeredSite = null;
|
|
337
|
-
function defineJmpSite(definition) {
|
|
338
|
-
registeredSite = definition;
|
|
339
|
-
return definition;
|
|
340
|
-
}
|
|
341
|
-
function getJmpSiteDefinition() {
|
|
342
|
-
return registeredSite;
|
|
343
|
-
}
|
|
344
|
-
function getJmpContentConfig() {
|
|
345
|
-
const { analyticsUrl, trackingKey } = readJmpPublicEnvFromProcess();
|
|
346
|
-
if (!analyticsUrl || !trackingKey) {
|
|
347
|
-
return null;
|
|
348
|
-
}
|
|
349
|
-
const site = getJmpSiteDefinition();
|
|
350
|
-
return {
|
|
351
|
-
baseUrl: analyticsUrl,
|
|
352
|
-
trackingKey,
|
|
353
|
-
contentWrappers: site?.contentWrappers ?? void 0
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// src/index.ts
|
|
358
|
-
var JMP_ANALYTICS_SDK_VERSION = "0.1.20";
|
|
270
|
+
// src/constants.ts
|
|
271
|
+
var JMP_ANALYTICS_SDK_VERSION = "0.1.21";
|
|
359
272
|
var JMP_TRACKING_CONSENT_STORAGE_KEY = "jmp_analytics_consent_v1";
|
|
273
|
+
var DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS = 800;
|
|
274
|
+
|
|
275
|
+
// src/consent.ts
|
|
360
276
|
var moduleTrackingConsentGranted = true;
|
|
361
277
|
var trackingConsentListeners = /* @__PURE__ */ new Set();
|
|
362
278
|
function readStoredTrackingConsent() {
|
|
@@ -369,6 +285,9 @@ function readStoredTrackingConsent() {
|
|
|
369
285
|
return false;
|
|
370
286
|
}
|
|
371
287
|
}
|
|
288
|
+
function initJmpTrackingConsent(requireConsent) {
|
|
289
|
+
moduleTrackingConsentGranted = requireConsent ? readStoredTrackingConsent() : true;
|
|
290
|
+
}
|
|
372
291
|
function isJmpTrackingConsentGranted() {
|
|
373
292
|
return moduleTrackingConsentGranted;
|
|
374
293
|
}
|
|
@@ -399,6 +318,8 @@ function setJmpTrackingConsent(granted) {
|
|
|
399
318
|
listener();
|
|
400
319
|
}
|
|
401
320
|
}
|
|
321
|
+
|
|
322
|
+
// src/tracker/helpers.ts
|
|
402
323
|
function captureAndGetLandingReferer() {
|
|
403
324
|
if (typeof window === "undefined") {
|
|
404
325
|
return null;
|
|
@@ -426,56 +347,20 @@ function splitPathAndSearch(pathInput) {
|
|
|
426
347
|
search: pathInput.slice(qIndex + 1)
|
|
427
348
|
};
|
|
428
349
|
}
|
|
429
|
-
function responseLooksLikeHtml(body) {
|
|
430
|
-
const s = body.trimStart();
|
|
431
|
-
return s.startsWith("<!") || s.toLowerCase().startsWith("<html") || s.startsWith("<") && /<html[\s>]/i.test(s.slice(0, 500));
|
|
432
|
-
}
|
|
433
|
-
function normalizeBaseUrl(baseUrl) {
|
|
434
|
-
return baseUrl.replace(/\/$/, "");
|
|
435
|
-
}
|
|
436
|
-
function getHeader(headers, key) {
|
|
437
|
-
if (!headers) {
|
|
438
|
-
return null;
|
|
439
|
-
}
|
|
440
|
-
if (typeof headers.get === "function") {
|
|
441
|
-
return headers.get(key);
|
|
442
|
-
}
|
|
443
|
-
const record = headers;
|
|
444
|
-
const direct = record[key] ?? record[key.toLowerCase()];
|
|
445
|
-
if (Array.isArray(direct)) {
|
|
446
|
-
return direct[0] ?? null;
|
|
447
|
-
}
|
|
448
|
-
return direct ?? null;
|
|
449
|
-
}
|
|
450
|
-
function pathFromRequestUrl(url) {
|
|
451
|
-
if (!url) {
|
|
452
|
-
return "/";
|
|
453
|
-
}
|
|
454
|
-
try {
|
|
455
|
-
const u = new URL(url);
|
|
456
|
-
return `${u.pathname}${u.search}`;
|
|
457
|
-
} catch {
|
|
458
|
-
return url.startsWith("/") ? url : "/";
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
350
|
function inferDevice() {
|
|
462
351
|
if (typeof window === "undefined") {
|
|
463
352
|
return "unknown";
|
|
464
353
|
}
|
|
465
354
|
return window.innerWidth < 768 ? "mobile" : "desktop";
|
|
466
355
|
}
|
|
356
|
+
|
|
357
|
+
// src/tracker/create-jmp-analytics.ts
|
|
467
358
|
function createJmpAnalytics(config) {
|
|
468
359
|
const baseUrl = normalizeBaseUrl(config.baseUrl);
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
throw new Error(
|
|
472
|
-
"createJmpAnalytics: fetch is not available; pass fetchImpl in config"
|
|
473
|
-
);
|
|
474
|
-
}
|
|
475
|
-
const fetchFn = resolvedFetch;
|
|
476
|
-
moduleTrackingConsentGranted = config.requireConsent ? readStoredTrackingConsent() : true;
|
|
360
|
+
const fetchFn = resolveFetch(config.fetchImpl, "createJmpAnalytics");
|
|
361
|
+
initJmpTrackingConsent(config.requireConsent);
|
|
477
362
|
function canSend() {
|
|
478
|
-
return
|
|
363
|
+
return isJmpTrackingConsentGranted();
|
|
479
364
|
}
|
|
480
365
|
let lastDedupePath = "";
|
|
481
366
|
let lastDedupeAt = 0;
|
|
@@ -507,32 +392,9 @@ function createJmpAnalytics(config) {
|
|
|
507
392
|
});
|
|
508
393
|
if (!res.ok) {
|
|
509
394
|
const text = await res.text();
|
|
510
|
-
|
|
511
|
-
throw new Error(
|
|
512
|
-
`JMP Analytics HTTP ${res.status} \u2014 ${url} returned HTML (not the JSON API). Set baseUrl / NEXT_PUBLIC_JMP_ANALYTICS_URL to your JMP Analytics dashboard origin (the deployment that includes POST /api/events), not a marketing or client site unless that codebase ships the same API route.`
|
|
513
|
-
);
|
|
514
|
-
}
|
|
515
|
-
let parsed = {};
|
|
516
|
-
try {
|
|
517
|
-
parsed = JSON.parse(text);
|
|
518
|
-
} catch {
|
|
519
|
-
parsed = { error: text };
|
|
520
|
-
}
|
|
521
|
-
const detail = parsed.error ?? text;
|
|
522
|
-
const segments = [
|
|
523
|
-
`JMP Analytics HTTP ${res.status}`,
|
|
524
|
-
parsed.code ? `[${parsed.code}]` : null,
|
|
525
|
-
detail,
|
|
526
|
-
parsed.hint ? `Hint: ${parsed.hint}` : null,
|
|
527
|
-
parsed.expectedDomain ? `Domain in JMP admin: ${parsed.expectedDomain}` : null
|
|
528
|
-
].filter(Boolean);
|
|
529
|
-
const message = segments.join(" \u2014 ");
|
|
395
|
+
const message = formatApiErrorMessage(res.status, text, "JMP Analytics");
|
|
530
396
|
if (config.debug) {
|
|
531
|
-
console.debug("[@jmp-technologies/analytics]", "error response", {
|
|
532
|
-
status: res.status,
|
|
533
|
-
...parsed,
|
|
534
|
-
raw: text
|
|
535
|
-
});
|
|
397
|
+
console.debug("[@jmp-technologies/analytics]", "error response", { status: res.status, raw: text });
|
|
536
398
|
}
|
|
537
399
|
throw new Error(message);
|
|
538
400
|
}
|
|
@@ -559,32 +421,9 @@ function createJmpAnalytics(config) {
|
|
|
559
421
|
});
|
|
560
422
|
if (!res.ok) {
|
|
561
423
|
const text = await res.text();
|
|
562
|
-
|
|
563
|
-
throw new Error(
|
|
564
|
-
`JMP Analytics HTTP ${res.status} \u2014 ${url} returned HTML (not the JSON API). Set baseUrl / NEXT_PUBLIC_JMP_ANALYTICS_URL to your JMP Analytics dashboard origin (the deployment that includes POST /api/leads).`
|
|
565
|
-
);
|
|
566
|
-
}
|
|
567
|
-
let parsed = {};
|
|
568
|
-
try {
|
|
569
|
-
parsed = JSON.parse(text);
|
|
570
|
-
} catch {
|
|
571
|
-
parsed = { error: text };
|
|
572
|
-
}
|
|
573
|
-
const detail = parsed.error ?? text;
|
|
574
|
-
const segments = [
|
|
575
|
-
`JMP Analytics HTTP ${res.status}`,
|
|
576
|
-
parsed.code ? `[${parsed.code}]` : null,
|
|
577
|
-
detail,
|
|
578
|
-
parsed.hint ? `Hint: ${parsed.hint}` : null,
|
|
579
|
-
parsed.expectedDomain ? `Domain in JMP admin: ${parsed.expectedDomain}` : null
|
|
580
|
-
].filter(Boolean);
|
|
581
|
-
const message = segments.join(" \u2014 ");
|
|
424
|
+
const message = formatApiErrorMessage(res.status, text, "JMP Analytics");
|
|
582
425
|
if (config.debug) {
|
|
583
|
-
console.debug("[@jmp-technologies/analytics]", "error response", {
|
|
584
|
-
status: res.status,
|
|
585
|
-
...parsed,
|
|
586
|
-
raw: text
|
|
587
|
-
});
|
|
426
|
+
console.debug("[@jmp-technologies/analytics]", "error response", { status: res.status, raw: text });
|
|
588
427
|
}
|
|
589
428
|
throw new Error(message);
|
|
590
429
|
}
|
|
@@ -848,47 +687,55 @@ function createJmpAnalytics(config) {
|
|
|
848
687
|
isTrackingConsentGranted: isJmpTrackingConsentGranted
|
|
849
688
|
};
|
|
850
689
|
}
|
|
851
|
-
|
|
690
|
+
|
|
691
|
+
// src/content/fetch.ts
|
|
692
|
+
async function getContent(config, path) {
|
|
693
|
+
const text = await getContentText(config, path);
|
|
694
|
+
try {
|
|
695
|
+
return JSON.parse(text);
|
|
696
|
+
} catch {
|
|
697
|
+
const baseUrl = normalizeBaseUrl(config.baseUrl);
|
|
698
|
+
const url = `${baseUrl}${path}`;
|
|
699
|
+
throw new Error(`JMP content HTTP 200 \u2014 ${url} returned invalid JSON.`);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
async function getContentText(config, path) {
|
|
852
703
|
const baseUrl = normalizeBaseUrl(config.baseUrl);
|
|
853
704
|
const resolvedFetch = config.fetchImpl ?? (typeof globalThis !== "undefined" && globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
|
|
854
705
|
if (!resolvedFetch) {
|
|
855
|
-
throw new Error("JMP
|
|
856
|
-
}
|
|
857
|
-
const visitorUserAgent = options.userAgent ?? getHeader(options.request?.headers, "user-agent");
|
|
858
|
-
const visitorReferer = options.referer ?? getHeader(options.request?.headers, "referer");
|
|
859
|
-
const path = options.path ?? pathFromRequestUrl(options.request?.url);
|
|
860
|
-
const headers = {
|
|
861
|
-
"Content-Type": "application/json",
|
|
862
|
-
"X-JMP-Tracking-Key": config.trackingKey
|
|
863
|
-
};
|
|
864
|
-
if (visitorUserAgent) {
|
|
865
|
-
headers["X-JMP-Visitor-User-Agent"] = visitorUserAgent;
|
|
866
|
-
}
|
|
867
|
-
if (visitorReferer) {
|
|
868
|
-
headers["X-JMP-Visitor-Referer"] = visitorReferer;
|
|
869
|
-
}
|
|
870
|
-
const payload = {
|
|
871
|
-
type: "page_view",
|
|
872
|
-
path: path || "/",
|
|
873
|
-
metadata: options.metadata && typeof options.metadata === "object" ? options.metadata : {}
|
|
874
|
-
};
|
|
875
|
-
if (options.sessionId) {
|
|
876
|
-
payload.sessionId = options.sessionId;
|
|
706
|
+
throw new Error("JMP content: fetch is not available; pass fetchImpl in config");
|
|
877
707
|
}
|
|
878
|
-
const url = `${baseUrl}
|
|
708
|
+
const url = `${baseUrl}${path}`;
|
|
879
709
|
if (config.debug) {
|
|
880
|
-
console.debug("[@jmp-technologies/analytics]", "
|
|
710
|
+
console.debug("[@jmp-technologies/analytics]", "GET", url);
|
|
881
711
|
}
|
|
882
712
|
const res = await resolvedFetch(url, {
|
|
883
|
-
method: "
|
|
884
|
-
headers
|
|
885
|
-
|
|
713
|
+
method: "GET",
|
|
714
|
+
headers: {
|
|
715
|
+
"X-JMP-Tracking-Key": config.trackingKey
|
|
716
|
+
}
|
|
886
717
|
});
|
|
718
|
+
const text = await res.text();
|
|
887
719
|
if (!res.ok) {
|
|
888
|
-
|
|
889
|
-
|
|
720
|
+
if (responseLooksLikeHtml(text)) {
|
|
721
|
+
throw new Error(
|
|
722
|
+
`JMP content HTTP ${res.status} \u2014 ${url} returned HTML (not the JSON API). Set baseUrl to your JMP Analytics dashboard origin.`
|
|
723
|
+
);
|
|
724
|
+
}
|
|
725
|
+
let parsed = {};
|
|
726
|
+
try {
|
|
727
|
+
parsed = JSON.parse(text);
|
|
728
|
+
} catch {
|
|
729
|
+
parsed = { error: text };
|
|
730
|
+
}
|
|
731
|
+
throw new Error(
|
|
732
|
+
[`JMP content HTTP ${res.status}`, parsed.error, parsed.hint].filter(Boolean).join(" \u2014 ")
|
|
733
|
+
);
|
|
890
734
|
}
|
|
735
|
+
return text;
|
|
891
736
|
}
|
|
737
|
+
|
|
738
|
+
// src/content/api.ts
|
|
892
739
|
async function getJmpBlogPosts(config) {
|
|
893
740
|
const data = await getContent(
|
|
894
741
|
config,
|
|
@@ -984,6 +831,8 @@ async function registerJmpContentWrappers(config) {
|
|
|
984
831
|
const data = JSON.parse(text);
|
|
985
832
|
return { ok: true, wrappers: data.wrappers ?? wrappers };
|
|
986
833
|
}
|
|
834
|
+
|
|
835
|
+
// src/content/client.ts
|
|
987
836
|
function createJmpContentClient(config) {
|
|
988
837
|
return {
|
|
989
838
|
getBlogPosts: () => getJmpBlogPosts(config),
|
|
@@ -995,58 +844,16 @@ function createJmpContentClient(config) {
|
|
|
995
844
|
registerContentWrappers: () => registerJmpContentWrappers(config)
|
|
996
845
|
};
|
|
997
846
|
}
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
try {
|
|
1001
|
-
return JSON.parse(text);
|
|
1002
|
-
} catch {
|
|
1003
|
-
const baseUrl = normalizeBaseUrl(config.baseUrl);
|
|
1004
|
-
const url = `${baseUrl}${path}`;
|
|
1005
|
-
throw new Error(`JMP content HTTP 200 \u2014 ${url} returned invalid JSON.`);
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
async function getContentText(config, path) {
|
|
1009
|
-
const baseUrl = normalizeBaseUrl(config.baseUrl);
|
|
1010
|
-
const resolvedFetch = config.fetchImpl ?? (typeof globalThis !== "undefined" && globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
|
|
1011
|
-
if (!resolvedFetch) {
|
|
1012
|
-
throw new Error("JMP content: fetch is not available; pass fetchImpl in config");
|
|
1013
|
-
}
|
|
1014
|
-
const url = `${baseUrl}${path}`;
|
|
1015
|
-
if (config.debug) {
|
|
1016
|
-
console.debug("[@jmp-technologies/analytics]", "GET", url);
|
|
1017
|
-
}
|
|
1018
|
-
const res = await resolvedFetch(url, {
|
|
1019
|
-
method: "GET",
|
|
1020
|
-
headers: {
|
|
1021
|
-
"X-JMP-Tracking-Key": config.trackingKey
|
|
1022
|
-
}
|
|
1023
|
-
});
|
|
1024
|
-
const text = await res.text();
|
|
1025
|
-
if (!res.ok) {
|
|
1026
|
-
if (responseLooksLikeHtml(text)) {
|
|
1027
|
-
throw new Error(
|
|
1028
|
-
`JMP content HTTP ${res.status} \u2014 ${url} returned HTML (not the JSON API). Set baseUrl to your JMP Analytics dashboard origin.`
|
|
1029
|
-
);
|
|
1030
|
-
}
|
|
1031
|
-
let parsed = {};
|
|
1032
|
-
try {
|
|
1033
|
-
parsed = JSON.parse(text);
|
|
1034
|
-
} catch {
|
|
1035
|
-
parsed = { error: text };
|
|
1036
|
-
}
|
|
1037
|
-
throw new Error(
|
|
1038
|
-
[`JMP content HTTP ${res.status}`, parsed.error, parsed.hint].filter(Boolean).join(" \u2014 ")
|
|
1039
|
-
);
|
|
1040
|
-
}
|
|
1041
|
-
return text;
|
|
1042
|
-
}
|
|
1043
|
-
var DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS = 800;
|
|
847
|
+
|
|
848
|
+
// src/env/browser.ts
|
|
1044
849
|
function getJmpBrowserAnalyticsConfigFromEnv(overrides) {
|
|
1045
850
|
const pub = readJmpPublicEnvFromProcess();
|
|
1046
|
-
|
|
851
|
+
const baseUrl = overrides?.baseUrl ?? pub.analyticsUrl;
|
|
852
|
+
const trackingKey = overrides?.trackingKey ?? pub.trackingKey;
|
|
853
|
+
if (!baseUrl || !trackingKey) return null;
|
|
1047
854
|
return {
|
|
1048
|
-
baseUrl
|
|
1049
|
-
trackingKey
|
|
855
|
+
baseUrl,
|
|
856
|
+
trackingKey,
|
|
1050
857
|
pageViewDedupeMs: DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS,
|
|
1051
858
|
...overrides,
|
|
1052
859
|
requireConsent: overrides?.requireConsent ?? pub.requireConsent,
|
|
@@ -1055,8 +862,10 @@ function getJmpBrowserAnalyticsConfigFromEnv(overrides) {
|
|
|
1055
862
|
}
|
|
1056
863
|
function getJmpServerContentConfigFromEnv(overrides) {
|
|
1057
864
|
const pub = readJmpPublicEnvFromProcess();
|
|
1058
|
-
|
|
1059
|
-
|
|
865
|
+
const baseUrl = overrides?.baseUrl ?? pub.analyticsUrl;
|
|
866
|
+
const trackingKey = overrides?.trackingKey ?? pub.trackingKey;
|
|
867
|
+
if (!baseUrl || !trackingKey) return null;
|
|
868
|
+
return { baseUrl, trackingKey, ...overrides };
|
|
1060
869
|
}
|
|
1061
870
|
function isJmpBrowserAnalyticsEnvConfigured() {
|
|
1062
871
|
const pub = readJmpPublicEnvFromProcess();
|
|
@@ -1084,17 +893,13 @@ export {
|
|
|
1084
893
|
buildLandingCaptureInlineScript,
|
|
1085
894
|
BOOKING_LINK_HOSTS,
|
|
1086
895
|
classifyContactHref,
|
|
1087
|
-
readJmpPublicEnvFromImportMeta,
|
|
1088
|
-
defineJmpSite,
|
|
1089
|
-
getJmpSiteDefinition,
|
|
1090
|
-
getJmpContentConfig,
|
|
1091
896
|
JMP_ANALYTICS_SDK_VERSION,
|
|
1092
897
|
JMP_TRACKING_CONSENT_STORAGE_KEY,
|
|
898
|
+
DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS,
|
|
1093
899
|
isJmpTrackingConsentGranted,
|
|
1094
900
|
subscribeJmpTrackingConsent,
|
|
1095
901
|
setJmpTrackingConsent,
|
|
1096
902
|
createJmpAnalytics,
|
|
1097
|
-
trackJmpServerPageView,
|
|
1098
903
|
getJmpBlogPosts,
|
|
1099
904
|
getJmpBlogPost,
|
|
1100
905
|
getJmpFaqs,
|
|
@@ -1103,7 +908,6 @@ export {
|
|
|
1103
908
|
getJmpRobotsTxt,
|
|
1104
909
|
registerJmpContentWrappers,
|
|
1105
910
|
createJmpContentClient,
|
|
1106
|
-
DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS,
|
|
1107
911
|
getJmpBrowserAnalyticsConfigFromEnv,
|
|
1108
912
|
getJmpServerContentConfigFromEnv,
|
|
1109
913
|
isJmpBrowserAnalyticsEnvConfigured,
|
|
@@ -1111,4 +915,4 @@ export {
|
|
|
1111
915
|
getOrCreateJmpAnalyticsFromBrowserEnv,
|
|
1112
916
|
createJmpContentClientFromServerEnv
|
|
1113
917
|
};
|
|
1114
|
-
//# sourceMappingURL=chunk-
|
|
918
|
+
//# sourceMappingURL=chunk-AHRVNEFV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/landing-capture.ts","../src/contact-link-classifier.ts","../src/constants.ts","../src/consent.ts","../src/tracker/helpers.ts","../src/tracker/create-jmp-analytics.ts","../src/content/fetch.ts","../src/content/api.ts","../src/content/client.ts","../src/env/browser.ts"],"sourcesContent":["/** First-party landing attribution captured before React / useEffect (sessionStorage). */\n\nexport const JMP_SESSION_ID_KEY = \"jmp_analytics_sid\"\nexport const JMP_LANDING_REFERER_KEY = \"jmp_analytics_landing_referer\"\n/** Cross-subdomain landing referrer (www ↔ apex share sessionStorage origins). */\nexport const JMP_LANDING_REFERER_COOKIE = \"jmp_lr\"\nexport const JMP_UTM_SESSION_KEY = \"jmp_analytics_utm\"\n\nconst LANDING_REFERER_COOKIE_MAX_AGE_SEC = 30 * 60\n\nexport const JMP_UTM_PARAM_KEYS = [\n \"utm_source\",\n \"utm_medium\",\n \"utm_campaign\",\n \"utm_content\",\n \"utm_term\",\n] as const\n\nfunction refererHostFromUrl(referer: string): string | null {\n const trimmed = referer.trim()\n try {\n return new URL(trimmed).hostname.replace(/^www\\./i, \"\").toLowerCase()\n } catch {\n try {\n return new URL(`https://${trimmed}`).hostname\n .replace(/^www\\./i, \"\")\n .toLowerCase()\n } catch {\n return null\n }\n }\n}\n\nexport function isExternalReferrerUrl(\n referer: string,\n siteHostname: string\n): boolean {\n const refHost = refererHostFromUrl(referer)\n if (!refHost) {\n return false\n }\n const site = siteHostname.replace(/^www\\./i, \"\").toLowerCase()\n return refHost !== site && !refHost.endsWith(`.${site}`)\n}\n\n/** Apex hostname used for `Domain=.example.com` cookies (www + apex). */\nexport function registrableSiteDomain(siteHostname: string): string | null {\n const site = siteHostname.replace(/^www\\./i, \"\").toLowerCase()\n if (!site || site === \"localhost\" || site.endsWith(\".local\")) {\n return null\n }\n if (!site.includes(\".\")) {\n return null\n }\n return site\n}\n\nexport function readLandingRefererCookie(siteHostname: string): string | null {\n if (typeof document === \"undefined\") {\n return null\n }\n\n const apex = registrableSiteDomain(siteHostname)\n if (!apex) {\n return null\n }\n\n const prefix = `${JMP_LANDING_REFERER_COOKIE}=`\n for (const part of document.cookie.split(\";\")) {\n const trimmed = part.trim()\n if (!trimmed.startsWith(prefix)) {\n continue\n }\n try {\n const value = decodeURIComponent(trimmed.slice(prefix.length)).trim()\n return value.length > 0 ? value : null\n } catch {\n return null\n }\n }\n\n return null\n}\n\nexport function writeLandingRefererCookie(\n referer: string,\n siteHostname: string\n): void {\n if (typeof document === \"undefined\") {\n return\n }\n\n const apex = registrableSiteDomain(siteHostname)\n if (!apex) {\n return\n }\n\n const value = encodeURIComponent(referer.trim().slice(0, 2048))\n const secure =\n typeof window !== \"undefined\" && window.location.protocol === \"https:\"\n ? \"; Secure\"\n : \"\"\n\n document.cookie = `${JMP_LANDING_REFERER_COOKIE}=${value}; Domain=.${apex}; Path=/; Max-Age=${LANDING_REFERER_COOKIE_MAX_AGE_SEC}; SameSite=Lax${secure}`\n}\n\nexport function captureLandingAttribution(siteHostname: string): {\n landingReferer: string | null\n utm: Record<string, string>\n} {\n if (typeof document === \"undefined\" || typeof window === \"undefined\") {\n return { landingReferer: null, utm: {} }\n }\n\n let landingReferer: string | null = null\n let utm: Record<string, string> = {}\n\n try {\n const storedReferer =\n sessionStorage.getItem(JMP_LANDING_REFERER_KEY)?.trim() || null\n const cookieReferer = readLandingRefererCookie(siteHostname)\n const preservedReferer = storedReferer || cookieReferer\n const currentReferer = document.referrer?.trim() || null\n\n if (\n currentReferer &&\n isExternalReferrerUrl(currentReferer, siteHostname)\n ) {\n sessionStorage.setItem(JMP_LANDING_REFERER_KEY, currentReferer)\n writeLandingRefererCookie(currentReferer, siteHostname)\n landingReferer = currentReferer\n } else if (preservedReferer) {\n landingReferer = preservedReferer\n if (!storedReferer) {\n sessionStorage.setItem(JMP_LANDING_REFERER_KEY, preservedReferer)\n }\n }\n\n const params = new URLSearchParams(window.location.search)\n const rawUtm = sessionStorage.getItem(JMP_UTM_SESSION_KEY)\n if (rawUtm) {\n const parsed = JSON.parse(rawUtm) as unknown\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n utm = parsed as Record<string, string>\n }\n }\n\n let updated = false\n for (const key of JMP_UTM_PARAM_KEYS) {\n const value = params.get(key)?.trim()\n if (value) {\n utm[key] = value\n updated = true\n }\n }\n\n if (updated) {\n sessionStorage.setItem(JMP_UTM_SESSION_KEY, JSON.stringify(utm))\n }\n } catch {\n }\n\n return { landingReferer, utm }\n}\n\nexport function getOrCreateSessionIdFromStorage(): string | undefined {\n if (typeof window === \"undefined\" || !globalThis.crypto?.randomUUID) {\n return undefined\n }\n try {\n let id = sessionStorage.getItem(JMP_SESSION_ID_KEY)\n if (!id) {\n id = crypto.randomUUID()\n sessionStorage.setItem(JMP_SESSION_ID_KEY, id)\n }\n return id\n } catch {\n return undefined\n }\n}\n\n/** Inline script for Next.js `beforeInteractive` — capture referrer/UTM only (no network). */\nexport function buildLandingCaptureInlineScript(): string {\n const cookieMaxAge = String(LANDING_REFERER_COOKIE_MAX_AGE_SEC)\n\n 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){}})();`\n}\n","/**\n * Classify outbound contact links for auto-tracking.\n *\n * Keep channel set in sync with `lib/ingest/contact-click.ts` on the dashboard side.\n *\n * Channels detected from `href` alone (no widget / iframe detection):\n *\n * - `call` — `tel:` (delegates to existing `trackCallClick`)\n * - `email` — `mailto:` (delegates to existing `trackEmailClick`)\n * - `sms` — `sms:` / `smsto:`\n * - `whatsapp` — `wa.me`, `api.whatsapp.com`, `whatsapp.com/send`\n * - `maps` — Google Maps, Apple Maps, `maps.app.goo.gl`, `geo:`\n * - `booking` — fixed allowlist of common scheduling hosts (see {@link BOOKING_LINK_HOSTS})\n *\n * `linkHost` returned in `contact_click` results is the hostname only — never the full URL\n * (avoids leaking phone numbers in `tel:`/`sms:` URLs and PII-laden booking query strings).\n */\n\nexport type ContactLinkChannel = \"sms\" | \"whatsapp\" | \"maps\" | \"booking\"\n\nexport type ClassifiedContactLink =\n | { kind: \"call\" }\n | { kind: \"email\" }\n | { kind: \"contact_click\"; channel: ContactLinkChannel; linkHost: string | null }\n\n/** v1 booking hosts (~80%+ SMB coverage). Add hosts here when a client uses something rare. */\nexport const BOOKING_LINK_HOSTS = [\n \"calendly.com\",\n \"cal.com\",\n \"acuityscheduling.com\",\n \"calendar.google.com\",\n \"calendar.app.google\",\n \"bookings.microsoft.com\",\n \"outlook.office.com\",\n \"outlook.office365.com\",\n \"book.ms\",\n \"setmore.com\",\n \"simplybook.me\",\n \"oncehub.com\",\n \"scheduleonce.com\",\n \"youcanbook.me\",\n \"savvycal.com\",\n \"tidycal.com\",\n \"zohobookings.com\",\n \"square.site\",\n \"squareup.com\",\n \"meetings.hubspot.com\",\n \"jobber.com\",\n \"housecallpro.com\",\n \"servicetitan.com\",\n \"thryv.com\",\n \"honeybook.com\",\n \"dubsado.com\",\n \"fresha.com\",\n \"booksy.com\",\n \"vagaro.com\",\n \"mindbodyonline.com\",\n \"mindbody.io\",\n \"janeapp.com\",\n \"cliniko.com\",\n \"practicebetter.io\",\n] as const\n\nconst WHATSAPP_HOSTS = [\"wa.me\", \"api.whatsapp.com\", \"whatsapp.com\"] as const\n\nexport function hostnameMatches(host: string, pattern: string): boolean {\n const h = host.toLowerCase()\n const p = pattern.toLowerCase()\n return h === p || h.endsWith(`.${p}`)\n}\n\nfunction isMapsHost(host: string, pathname: string): boolean {\n if (hostnameMatches(host, \"maps.apple.com\")) {\n return true\n }\n if (hostnameMatches(host, \"maps.app.goo.gl\")) {\n return true\n }\n if (hostnameMatches(host, \"google.com\") || hostnameMatches(host, \"google.co\")) {\n return pathname === \"/maps\" || pathname.startsWith(\"/maps/\") || pathname.startsWith(\"/local\")\n }\n return false\n}\n\nfunction isWhatsAppHost(host: string, pathname: string): boolean {\n for (const pattern of WHATSAPP_HOSTS) {\n if (!hostnameMatches(host, pattern)) {\n continue\n }\n if (pattern === \"whatsapp.com\") {\n return pathname.startsWith(\"/send\")\n }\n return true\n }\n return false\n}\n\nfunction isBookingHost(host: string): boolean {\n for (const pattern of BOOKING_LINK_HOSTS) {\n if (hostnameMatches(host, pattern)) {\n return true\n }\n }\n return false\n}\n\n/** Returns `null` when the link should not be tracked as a contact link. */\nexport function classifyContactHref(href: string): ClassifiedContactLink | null {\n const raw = href.trim()\n if (!raw || raw.startsWith(\"#\") || raw.startsWith(\"javascript:\")) {\n return null\n }\n\n const lower = raw.toLowerCase()\n\n if (lower.startsWith(\"tel:\")) {\n return { kind: \"call\" }\n }\n if (lower.startsWith(\"mailto:\")) {\n return { kind: \"email\" }\n }\n if (lower.startsWith(\"sms:\") || lower.startsWith(\"smsto:\")) {\n return { kind: \"contact_click\", channel: \"sms\", linkHost: null }\n }\n if (lower.startsWith(\"geo:\")) {\n return { kind: \"contact_click\", channel: \"maps\", linkHost: null }\n }\n\n // Skip anchors/relative paths that aren't a known contact scheme.\n if (raw.startsWith(\"/\") || raw.startsWith(\"?\")) {\n return null\n }\n\n let url: URL\n try {\n url = new URL(raw)\n } catch {\n return null\n }\n\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n return null\n }\n\n const host = url.hostname.toLowerCase()\n const pathname = url.pathname.toLowerCase()\n\n if (isWhatsAppHost(host, pathname)) {\n return { kind: \"contact_click\", channel: \"whatsapp\", linkHost: host }\n }\n if (isMapsHost(host, pathname)) {\n return { kind: \"contact_click\", channel: \"maps\", linkHost: host }\n }\n if (isBookingHost(host)) {\n return { kind: \"contact_click\", channel: \"booking\", linkHost: host }\n }\n\n return null\n}\n","/** Published package version — sent with capabilities for admin readiness. */\nexport const JMP_ANALYTICS_SDK_VERSION = \"0.1.21\"\n\n/** `localStorage` key when {@link JmpAnalyticsConfig.requireConsent} is enabled. */\nexport const JMP_TRACKING_CONSENT_STORAGE_KEY = \"jmp_analytics_consent_v1\"\n\n/** Default `pageViewDedupeMs` when resolving browser config from env (Strict Mode / SPA). */\nexport const DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS = 800\n","import { captureLandingAttribution } from \"./landing-capture\"\nimport { JMP_TRACKING_CONSENT_STORAGE_KEY } from \"./constants\"\n\nlet moduleTrackingConsentGranted = true\nconst trackingConsentListeners = new Set<() => void>()\n\nfunction readStoredTrackingConsent(): boolean {\n if (typeof window === \"undefined\") {\n return false\n }\n try {\n return (\n localStorage.getItem(JMP_TRACKING_CONSENT_STORAGE_KEY) === \"granted\"\n )\n } catch {\n return false\n }\n}\n\n/** Initialize consent gate when creating a tracker (browser only). */\nexport function initJmpTrackingConsent(requireConsent: boolean | undefined): void {\n moduleTrackingConsentGranted = requireConsent\n ? readStoredTrackingConsent()\n : true\n}\n\n/** Whether analytics events may be sent (after CMP / cookie banner). */\nexport function isJmpTrackingConsentGranted(): boolean {\n return moduleTrackingConsentGranted\n}\n\n/** Subscribe to consent changes (for React `useSyncExternalStore`). */\nexport function subscribeJmpTrackingConsent(listener: () => void): () => void {\n trackingConsentListeners.add(listener)\n return () => {\n trackingConsentListeners.delete(listener)\n }\n}\n\n/**\n * Grant or deny analytics. Persists in `localStorage` when `requireConsent` is used.\n * Call from your cookie banner before tracking runs.\n */\nexport function setJmpTrackingConsent(granted: boolean): void {\n if (moduleTrackingConsentGranted === granted) {\n return\n }\n moduleTrackingConsentGranted = granted\n if (typeof window !== \"undefined\") {\n try {\n localStorage.setItem(\n JMP_TRACKING_CONSENT_STORAGE_KEY,\n granted ? \"granted\" : \"denied\"\n )\n } catch {\n // private mode\n }\n if (granted) {\n captureLandingAttribution(window.location.hostname)\n }\n }\n for (const listener of trackingConsentListeners) {\n listener()\n }\n}\n","import {\n captureLandingAttribution,\n JMP_UTM_PARAM_KEYS,\n} from \"../landing-capture\"\n\nexport function captureAndGetLandingReferer(): string | null {\n if (typeof window === \"undefined\") {\n return null\n }\n return captureLandingAttribution(window.location.hostname).landingReferer\n}\n\nexport function mergeSessionUtms(metadata: Record<string, unknown>) {\n if (typeof window === \"undefined\") {\n return\n }\n const { utm } = captureLandingAttribution(window.location.hostname)\n for (const key of JMP_UTM_PARAM_KEYS) {\n if (metadata[key] === undefined && utm[key]) {\n metadata[key] = utm[key]\n }\n }\n}\n\nexport function splitPathAndSearch(pathInput: string): {\n pathname: string\n search: string\n} {\n const qIndex = pathInput.indexOf(\"?\")\n if (qIndex === -1) {\n return { pathname: pathInput || \"/\", search: \"\" }\n }\n return {\n pathname: pathInput.slice(0, qIndex) || \"/\",\n search: pathInput.slice(qIndex + 1),\n }\n}\n\nexport function inferDevice(): string {\n if (typeof window === \"undefined\") {\n return \"unknown\"\n }\n return window.innerWidth < 768 ? \"mobile\" : \"desktop\"\n}\n","import {\n captureLandingAttribution,\n getOrCreateSessionIdFromStorage,\n JMP_UTM_PARAM_KEYS,\n} from \"../landing-capture\"\nimport { classifyContactHref } from \"../contact-link-classifier\"\nimport type { ContactLinkChannel } from \"../contact-link-classifier\"\nimport {\n initJmpTrackingConsent,\n isJmpTrackingConsentGranted,\n setJmpTrackingConsent,\n} from \"../consent\"\nimport {\n formatApiErrorMessage,\n normalizeBaseUrl,\n resolveFetch,\n} from \"../http\"\nimport type {\n JmpAnalyticsConfig,\n JmpEventType,\n TrackLeadOptions,\n TrackPageViewOptions,\n} from \"../types\"\nimport {\n captureAndGetLandingReferer,\n inferDevice,\n mergeSessionUtms,\n splitPathAndSearch,\n} from \"./helpers\"\n\nexport function createJmpAnalytics(config: JmpAnalyticsConfig) {\n const baseUrl = normalizeBaseUrl(config.baseUrl)\n const fetchFn = resolveFetch(config.fetchImpl, \"createJmpAnalytics\")\n\n initJmpTrackingConsent(config.requireConsent)\n\n function canSend(): boolean {\n return isJmpTrackingConsentGranted()\n }\n\n let lastDedupePath = \"\"\n let lastDedupeAt = 0\n\n async function send(body: Record<string, unknown>) {\n if (!canSend()) {\n return\n }\n\n const url = `${baseUrl}/api/events`\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-JMP-Tracking-Key\": config.trackingKey,\n }\n\n if (body.type === \"page_view\" && typeof window !== \"undefined\") {\n const landingReferer = captureAndGetLandingReferer()\n if (landingReferer) {\n headers[\"X-JMP-Visitor-Referer\"] = landingReferer\n }\n }\n\n const sid = getOrCreateSessionIdFromStorage()\n const payload = { ...body, ...(sid ? { sessionId: sid } : {}) }\n\n if (config.debug) {\n console.debug(\"[@jmp-technologies/analytics]\", \"POST\", url, payload)\n }\n\n const res = await fetchFn(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(payload),\n keepalive: true,\n })\n\n if (!res.ok) {\n const text = await res.text()\n const message = formatApiErrorMessage(res.status, text, \"JMP Analytics\")\n if (config.debug) {\n console.debug(\"[@jmp-technologies/analytics]\", \"error response\", { status: res.status, raw: text })\n }\n throw new Error(message)\n }\n }\n\n async function sendLead(body: Record<string, unknown>) {\n if (!canSend()) {\n return\n }\n\n const url = `${baseUrl}/api/leads`\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-JMP-Tracking-Key\": config.trackingKey,\n }\n\n const sid = getOrCreateSessionIdFromStorage()\n const payload = { ...body, ...(sid ? { sessionId: sid } : {}) }\n\n if (config.debug) {\n console.debug(\"[@jmp-technologies/analytics]\", \"POST\", url, payload)\n }\n\n const res = await fetchFn(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(payload),\n keepalive: true,\n })\n\n if (!res.ok) {\n const text = await res.text()\n const message = formatApiErrorMessage(res.status, text, \"JMP Analytics\")\n if (config.debug) {\n console.debug(\"[@jmp-technologies/analytics]\", \"error response\", { status: res.status, raw: text })\n }\n throw new Error(message)\n }\n }\n\n function shouldDedupePageView(path: string): boolean {\n const ms = config.pageViewDedupeMs ?? 0\n if (ms <= 0 || typeof window === \"undefined\") {\n return false\n }\n const now = Date.now()\n if (path === lastDedupePath && now - lastDedupeAt < ms) {\n return true\n }\n lastDedupePath = path\n lastDedupeAt = now\n return false\n }\n\n function trackPageView(options?: TrackPageViewOptions) {\n if (!canSend()) {\n return Promise.resolve()\n }\n\n const pathFromOptions = options?.path?.trim()\n let pathname = \"/\"\n let search = \"\"\n\n if (pathFromOptions) {\n const split = splitPathAndSearch(pathFromOptions)\n pathname = split.pathname\n search = split.search\n } else if (typeof window !== \"undefined\") {\n pathname = window.location.pathname\n search = window.location.search.replace(/^\\?/, \"\")\n }\n\n if (!search && typeof window !== \"undefined\") {\n search = window.location.search.replace(/^\\?/, \"\")\n }\n\n const path = search ? `${pathname}?${search}` : pathname\n\n if (shouldDedupePageView(pathname)) {\n return Promise.resolve()\n }\n\n const metadata: Record<string, unknown> =\n options?.metadata && typeof options.metadata === \"object\"\n ? { ...options.metadata }\n : {}\n\n if (typeof window !== \"undefined\") {\n metadata.pageHostname = window.location.hostname\n const params = new URLSearchParams(\n search ? `?${search}` : window.location.search\n )\n for (const key of JMP_UTM_PARAM_KEYS) {\n const v = params.get(key)\n if (v && metadata[key] === undefined) {\n metadata[key] = v\n }\n }\n mergeSessionUtms(metadata)\n const landingReferer = captureAndGetLandingReferer()\n if (landingReferer && metadata.referer === undefined) {\n metadata.referer = landingReferer\n }\n }\n\n return send({\n type: \"page_view\",\n path: path || \"/\",\n device: options?.device ?? inferDevice(),\n country: options?.country,\n metadata,\n })\n }\n\n function trackFormSubmit(\n path: string,\n metadata?: Record<string, unknown>\n ) {\n return send({\n type: \"form_submit\",\n path: path || \"/\",\n metadata: metadata ?? {},\n })\n }\n\n function trackCallClick(\n path: string,\n metadata?: Record<string, unknown>\n ) {\n return send({\n type: \"call_click\",\n path: path || \"/\",\n metadata: metadata ?? {},\n })\n }\n\n function trackEmailClick(\n path: string,\n metadata?: Record<string, unknown>\n ) {\n return send({\n type: \"email_click\",\n path: path || \"/\",\n metadata: metadata ?? {},\n })\n }\n\n function trackContactClick(\n path: string,\n options: {\n channel: ContactLinkChannel\n linkHost?: string | null\n metadata?: Record<string, unknown>\n }\n ) {\n const metadata: Record<string, unknown> = {\n ...(options.metadata && typeof options.metadata === \"object\"\n ? options.metadata\n : {}),\n channel: options.channel,\n }\n if (options.linkHost) {\n metadata.linkHost = options.linkHost\n }\n return send({\n type: \"contact_click\",\n path: path || \"/\",\n metadata,\n })\n }\n\n function trackLead(options?: TrackLeadOptions) {\n const path =\n options?.path ??\n (typeof window !== \"undefined\" ? window.location.pathname : \"/\")\n\n const body: Record<string, unknown> = { path: path || \"/\" }\n if (options?.name) body.name = options.name\n if (options?.email) body.email = options.email\n if (options?.phone) body.phone = options.phone\n if (options?.company) body.company = options.company\n if (options?.website) body.website = options.website\n if (options?.subject) body.subject = options.subject\n if (options?.message) body.message = options.message\n if (options?.metadata && typeof options.metadata === \"object\") {\n body.metadata = options.metadata\n }\n if (options?.sessionId) body.sessionId = options.sessionId\n\n return sendLead(body)\n }\n\n function track(\n type: JmpEventType,\n payload: {\n path: string\n metadata?: Record<string, unknown>\n source?: string\n device?: string\n country?: string\n }\n ) {\n if (type === \"page_view\") {\n return trackPageView({\n path: payload.path,\n source: payload.source,\n device: payload.device,\n country: payload.country,\n metadata: payload.metadata,\n })\n }\n if (type === \"form_submit\") {\n return trackFormSubmit(payload.path, payload.metadata)\n }\n if (type === \"call_click\") {\n return trackCallClick(payload.path, payload.metadata)\n }\n if (type === \"email_click\") {\n return trackEmailClick(payload.path, payload.metadata)\n }\n const meta = payload.metadata ?? {}\n const channel = typeof meta.channel === \"string\" ? meta.channel : \"\"\n const linkHost =\n typeof meta.linkHost === \"string\" ? (meta.linkHost as string) : null\n if (\n channel !== \"sms\" &&\n channel !== \"whatsapp\" &&\n channel !== \"maps\" &&\n channel !== \"booking\"\n ) {\n return Promise.resolve()\n }\n return trackContactClick(payload.path, {\n channel: channel as ContactLinkChannel,\n linkHost,\n metadata: meta,\n })\n }\n\n /**\n * Fire `page_view` on browser back/forward and when `history.pushState` /\n * `replaceState` are used (many SPAs). Call once after creating the tracker.\n * Returns an unsubscribe function.\n */\n function subscribeToSpaNavigation(): () => void {\n if (typeof window === \"undefined\") {\n return () => {}\n }\n\n let lastPath = window.location.pathname\n\n const run = () => {\n const path = window.location.pathname\n if (path !== lastPath) {\n lastPath = path\n void trackPageView({ path })\n }\n }\n\n window.addEventListener(\"popstate\", run)\n\n const push = history.pushState.bind(history)\n const replace = history.replaceState.bind(history)\n\n history.pushState = (...args: Parameters<History[\"pushState\"]>) => {\n const r = push(...args)\n queueMicrotask(run)\n return r\n }\n history.replaceState = (...args: Parameters<History[\"replaceState\"]>) => {\n const r = replace(...args)\n queueMicrotask(run)\n return r\n }\n\n return () => {\n window.removeEventListener(\"popstate\", run)\n history.pushState = push\n history.replaceState = replace\n }\n }\n\n let lastContactClickKey = \"\"\n let lastContactClickAt = 0\n\n /**\n * Capture-phase `click` listener that classifies outgoing links and records\n * the corresponding contact event. Returns an unsubscribe function. Safe to\n * call only in the browser; no-op on the server.\n */\n function subscribeContactLinkAutoTrack(): () => void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return () => {}\n }\n\n const handler = (event: MouseEvent) => {\n if (!canSend()) {\n return\n }\n const target = event.target\n if (!(target instanceof Element)) {\n return\n }\n const anchor = target.closest(\"a[href]\")\n if (!(anchor instanceof HTMLAnchorElement)) {\n return\n }\n if (anchor.closest(\"[data-jmp-no-track]\")) {\n return\n }\n\n const href = anchor.getAttribute(\"href\") ?? \"\"\n const classified = classifyContactHref(href)\n if (!classified) {\n return\n }\n\n const path = window.location.pathname\n\n if (classified.kind === \"call\") {\n if (dedupeContactClick(`call_click:${path}`)) {\n return\n }\n void trackCallClick(path)\n return\n }\n if (classified.kind === \"email\") {\n if (dedupeContactClick(`email_click:${path}`)) {\n return\n }\n void trackEmailClick(path)\n return\n }\n if (dedupeContactClick(`contact_click:${classified.channel}:${path}`)) {\n return\n }\n void trackContactClick(path, {\n channel: classified.channel,\n linkHost: classified.linkHost,\n })\n }\n\n document.addEventListener(\"click\", handler, { capture: true })\n return () => {\n document.removeEventListener(\"click\", handler, { capture: true } as EventListenerOptions)\n }\n }\n\n function dedupeContactClick(key: string): boolean {\n const now = Date.now()\n if (key === lastContactClickKey && now - lastContactClickAt < 1000) {\n return true\n }\n lastContactClickKey = key\n lastContactClickAt = now\n return false\n }\n\n if (typeof window !== \"undefined\" && canSend()) {\n captureLandingAttribution(window.location.hostname)\n }\n\n if (\n typeof window !== \"undefined\" &&\n config.autoTrackContactLinks &&\n canSend()\n ) {\n subscribeContactLinkAutoTrack()\n }\n\n return {\n track,\n trackPageView,\n trackFormSubmit,\n trackCallClick,\n trackEmailClick,\n trackContactClick,\n trackLead,\n subscribeToSpaNavigation,\n subscribeContactLinkAutoTrack,\n setTrackingConsent: setJmpTrackingConsent,\n isTrackingConsentGranted: isJmpTrackingConsentGranted,\n }\n}\n\nexport type JmpAnalytics = ReturnType<typeof createJmpAnalytics>\n","import { normalizeBaseUrl, resolveFetch, responseLooksLikeHtml } from \"../http\"\nimport type { JmpContentConfig } from \"../types\"\n\nexport async function getContent<T>(\n config: JmpContentConfig,\n path: string\n): Promise<T> {\n const text = await getContentText(config, path)\n\n try {\n return JSON.parse(text) as T\n } catch {\n const baseUrl = normalizeBaseUrl(config.baseUrl)\n const url = `${baseUrl}${path}`\n throw new Error(`JMP content HTTP 200 — ${url} returned invalid JSON.`)\n }\n}\n\nexport async function getContentText(\n config: JmpContentConfig,\n path: string\n): Promise<string> {\n const baseUrl = normalizeBaseUrl(config.baseUrl)\n const resolvedFetch =\n config.fetchImpl ??\n (typeof globalThis !== \"undefined\" && globalThis.fetch\n ? globalThis.fetch.bind(globalThis)\n : undefined)\n\n if (!resolvedFetch) {\n throw new Error(\"JMP content: fetch is not available; pass fetchImpl in config\")\n }\n\n const url = `${baseUrl}${path}`\n\n if (config.debug) {\n console.debug(\"[@jmp-technologies/analytics]\", \"GET\", url)\n }\n\n const res = await resolvedFetch(url, {\n method: \"GET\",\n headers: {\n \"X-JMP-Tracking-Key\": config.trackingKey,\n },\n })\n\n const text = await res.text()\n\n if (!res.ok) {\n if (responseLooksLikeHtml(text)) {\n throw new Error(\n `JMP content HTTP ${res.status} — ${url} returned HTML (not the JSON API). ` +\n `Set baseUrl to your JMP Analytics dashboard origin.`\n )\n }\n\n let parsed: { error?: string; hint?: string } = {}\n try {\n parsed = JSON.parse(text) as typeof parsed\n } catch {\n parsed = { error: text }\n }\n\n throw new Error(\n [`JMP content HTTP ${res.status}`, parsed.error, parsed.hint]\n .filter(Boolean)\n .join(\" — \")\n )\n }\n\n return text\n}\n","import { JMP_ANALYTICS_SDK_VERSION } from \"../constants\"\nimport { normalizeBaseUrl, resolveFetch } from \"../http\"\nimport type {\n JmpBlogPost,\n JmpContentConfig,\n JmpFaqItem,\n JmpSiteProfile,\n JmpTrustBundle,\n} from \"../types\"\nimport { getContent, getContentText } from \"./fetch\"\n\nexport async function getJmpBlogPosts(\n config: JmpContentConfig\n): Promise<JmpBlogPost[]> {\n const data = await getContent<{ posts: JmpBlogPost[] }>(\n config,\n \"/api/content/blog\"\n )\n return data.posts\n}\n\nexport async function getJmpBlogPost(\n config: JmpContentConfig,\n slug: string\n): Promise<JmpBlogPost | null> {\n try {\n const data = await getContent<{ post: JmpBlogPost }>(\n config,\n `/api/content/blog/${encodeURIComponent(slug)}`\n )\n return data.post\n } catch (error) {\n if (error instanceof Error && error.message.includes(\"HTTP 404\")) {\n return null\n }\n throw error\n }\n}\n\nexport async function getJmpFaqs(\n config: JmpContentConfig\n): Promise<JmpFaqItem[]> {\n const data = await getContent<{ faqs: JmpFaqItem[] }>(\n config,\n \"/api/content/faqs\"\n )\n return data.faqs\n}\n\nexport async function getJmpTrustBundle(\n config: JmpContentConfig\n): Promise<JmpTrustBundle> {\n return getContent<JmpTrustBundle>(config, \"/api/content/trust\")\n}\n\nexport async function getJmpSiteProfile(\n config: JmpContentConfig\n): Promise<JmpSiteProfile> {\n const data = await getContent<{ site: JmpSiteProfile }>(\n config,\n \"/api/content/site\"\n )\n return data.site\n}\n\nexport async function getJmpRobotsTxt(config: JmpContentConfig): Promise<string> {\n return getContentText(config, \"/api/content/robots\")\n}\n\nconst registeredContentWrappers = new Set<string>()\n\nfunction resolveContentWrappersFromConfig(\n config: JmpContentConfig\n): string[] {\n if (config.contentWrappers?.length) {\n return [...config.contentWrappers]\n }\n return []\n}\n\n/**\n * Tell the JMP dashboard which content wrappers this site wires (blog, faq, trust.*, contact).\n * Safe to call on every cold start; dedupes by tracking key + wrapper list.\n */\nexport async function registerJmpContentWrappers(\n config: JmpContentConfig\n): Promise<\n { ok: true; wrappers: string[] } | { ok: false; skipped: true }\n> {\n const wrappers = resolveContentWrappersFromConfig(config)\n if (!wrappers.length) {\n return { ok: false, skipped: true }\n }\n\n const dedupeKey = `${config.trackingKey}:${[...wrappers].sort().join(\",\")}`\n if (registeredContentWrappers.has(dedupeKey)) {\n return { ok: true, wrappers }\n }\n\n const baseUrl = normalizeBaseUrl(config.baseUrl)\n const resolvedFetch =\n config.fetchImpl ??\n (typeof globalThis !== \"undefined\" && globalThis.fetch\n ? globalThis.fetch.bind(globalThis)\n : undefined)\n\n if (!resolvedFetch) {\n throw new Error(\n \"JMP content wrappers: fetch is not available; pass fetchImpl in config\"\n )\n }\n\n const url = `${baseUrl}/api/content/capabilities`\n if (config.debug) {\n console.debug(\"[@jmp-technologies/analytics]\", \"POST\", url, { wrappers })\n }\n\n const res = await resolvedFetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-JMP-Tracking-Key\": config.trackingKey,\n },\n body: JSON.stringify({\n wrappers,\n sdkVersion: JMP_ANALYTICS_SDK_VERSION,\n }),\n })\n\n const text = await res.text()\n if (!res.ok) {\n let parsed: { error?: string; hint?: string } = {}\n try {\n parsed = JSON.parse(text) as typeof parsed\n } catch {\n parsed = { error: text }\n }\n throw new Error(\n [`JMP content wrappers HTTP ${res.status}`, parsed.error, parsed.hint]\n .filter(Boolean)\n .join(\" — \")\n )\n }\n\n registeredContentWrappers.add(dedupeKey)\n const data = JSON.parse(text) as { wrappers?: string[] }\n return { ok: true, wrappers: data.wrappers ?? wrappers }\n}\n","import {\n getJmpBlogPost,\n getJmpBlogPosts,\n getJmpFaqs,\n getJmpRobotsTxt,\n getJmpSiteProfile,\n getJmpTrustBundle,\n registerJmpContentWrappers,\n} from \"./api\"\nimport type { JmpContentConfig } from \"../types\"\n\nexport function createJmpContentClient(config: JmpContentConfig) {\n return {\n getBlogPosts: () => getJmpBlogPosts(config),\n getBlogPost: (slug: string) => getJmpBlogPost(config, slug),\n getFaqs: () => getJmpFaqs(config),\n getTrustBundle: () => getJmpTrustBundle(config),\n getSiteProfile: () => getJmpSiteProfile(config),\n getRobotsTxt: () => getJmpRobotsTxt(config),\n registerContentWrappers: () => registerJmpContentWrappers(config),\n }\n}\n\nexport type JmpContentClient = ReturnType<typeof createJmpContentClient>\n","import {\n createJmpAnalytics,\n type JmpAnalytics,\n} from \"../tracker/create-jmp-analytics\"\nimport { DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS } from \"../constants\"\nimport { readJmpPublicEnvFromProcess } from \"../env-public\"\nimport type { JmpAnalyticsConfig, JmpContentConfig } from \"../types\"\nimport { createJmpContentClient, type JmpContentClient } from \"../content/client\"\n\n/**\n * Browser / client bundle: reads `NEXT_PUBLIC_*` or Astro `PUBLIC_*` vars.\n * Use for `createJmpAnalytics` on the client. Returns `null` if incomplete.\n */\nexport function getJmpBrowserAnalyticsConfigFromEnv(\n overrides?: Partial<JmpAnalyticsConfig>\n): JmpAnalyticsConfig | null {\n const pub = readJmpPublicEnvFromProcess()\n const baseUrl = overrides?.baseUrl ?? pub.analyticsUrl\n const trackingKey = overrides?.trackingKey ?? pub.trackingKey\n if (!baseUrl || !trackingKey) return null\n return {\n baseUrl,\n trackingKey,\n pageViewDedupeMs: DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS,\n ...overrides,\n requireConsent: overrides?.requireConsent ?? pub.requireConsent,\n autoTrackContactLinks:\n overrides?.autoTrackContactLinks ?? pub.autoContactLinks,\n }\n}\n\n/**\n * Server / Node (SSR, Route Handlers, `getStaticProps`, etc.):\n * Same **`NEXT_PUBLIC_*`** vars as the browser (`NEXT_PUBLIC_JMP_ANALYTICS_URL` +\n * `NEXT_PUBLIC_JMP_TRACKING_KEY`). Next.js injects these on the server for RSC/fetches.\n * Returns `null` if incomplete.\n */\nexport function getJmpServerContentConfigFromEnv(\n overrides?: Partial<JmpContentConfig>\n): JmpContentConfig | null {\n const pub = readJmpPublicEnvFromProcess()\n const baseUrl = overrides?.baseUrl ?? pub.analyticsUrl\n const trackingKey = overrides?.trackingKey ?? pub.trackingKey\n if (!baseUrl || !trackingKey) return null\n return { baseUrl, trackingKey, ...overrides }\n}\n\nexport function isJmpBrowserAnalyticsEnvConfigured(): boolean {\n const pub = readJmpPublicEnvFromProcess()\n return Boolean(pub.analyticsUrl && pub.trackingKey)\n}\n\nexport function isJmpServerContentEnvConfigured(): boolean {\n return isJmpBrowserAnalyticsEnvConfigured()\n}\n\nlet jmpAnalyticsBrowserSingleton: JmpAnalytics | null = null\n\n/**\n * One `JmpAnalytics` instance per browser tab (module singleton). Safe for page views and events.\n * Returns `null` on the server or when public env vars are missing.\n */\nexport function getOrCreateJmpAnalyticsFromBrowserEnv(\n overrides?: Partial<JmpAnalyticsConfig>\n): JmpAnalytics | null {\n if (typeof window === \"undefined\") return null\n if (jmpAnalyticsBrowserSingleton) return jmpAnalyticsBrowserSingleton\n const config = getJmpBrowserAnalyticsConfigFromEnv(overrides)\n if (!config) return null\n jmpAnalyticsBrowserSingleton = createJmpAnalytics(config)\n return jmpAnalyticsBrowserSingleton\n}\n\n/**\n * `createJmpContentClient` using server env resolution. Returns `null` if env is incomplete.\n */\nexport function createJmpContentClientFromServerEnv(\n overrides?: Partial<JmpContentConfig>\n): JmpContentClient | null {\n const config = getJmpServerContentConfigFromEnv(overrides)\n if (!config) return null\n return createJmpContentClient(config)\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getOrCreateJmpAnalyticsFromBrowserEnv
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-AHRVNEFV.js";
|
|
4
4
|
|
|
5
5
|
// src/react.tsx
|
|
6
6
|
import { useMemo } from "react";
|
|
@@ -8,6 +8,14 @@ import { jsx } from "react/jsx-runtime";
|
|
|
8
8
|
function useJmpAnalytics() {
|
|
9
9
|
return useMemo(() => getOrCreateJmpAnalyticsFromBrowserEnv(), []);
|
|
10
10
|
}
|
|
11
|
+
function trackCallClickFromCurrentPath() {
|
|
12
|
+
const path = typeof window !== "undefined" ? window.location.pathname : "/";
|
|
13
|
+
void getOrCreateJmpAnalyticsFromBrowserEnv()?.trackCallClick(path);
|
|
14
|
+
}
|
|
15
|
+
function trackEmailClickFromCurrentPath() {
|
|
16
|
+
const path = typeof window !== "undefined" ? window.location.pathname : "/";
|
|
17
|
+
void getOrCreateJmpAnalyticsFromBrowserEnv()?.trackEmailClick(path);
|
|
18
|
+
}
|
|
11
19
|
function JmpTrackedTelLink({
|
|
12
20
|
href,
|
|
13
21
|
onClick,
|
|
@@ -19,9 +27,7 @@ function JmpTrackedTelLink({
|
|
|
19
27
|
href,
|
|
20
28
|
...rest,
|
|
21
29
|
onClick: (e) => {
|
|
22
|
-
|
|
23
|
-
const path = typeof window !== "undefined" ? window.location.pathname : "/";
|
|
24
|
-
void client?.trackCallClick(path);
|
|
30
|
+
trackCallClickFromCurrentPath();
|
|
25
31
|
onClick?.(e);
|
|
26
32
|
}
|
|
27
33
|
}
|
|
@@ -38,9 +44,7 @@ function JmpTrackedMailtoLink({
|
|
|
38
44
|
href,
|
|
39
45
|
...rest,
|
|
40
46
|
onClick: (e) => {
|
|
41
|
-
|
|
42
|
-
const path = typeof window !== "undefined" ? window.location.pathname : "/";
|
|
43
|
-
void client?.trackEmailClick(path);
|
|
47
|
+
trackEmailClickFromCurrentPath();
|
|
44
48
|
onClick?.(e);
|
|
45
49
|
}
|
|
46
50
|
}
|
|
@@ -52,4 +56,4 @@ export {
|
|
|
52
56
|
JmpTrackedTelLink,
|
|
53
57
|
JmpTrackedMailtoLink
|
|
54
58
|
};
|
|
55
|
-
//# sourceMappingURL=chunk-
|
|
59
|
+
//# sourceMappingURL=chunk-DQ6A6HTY.js.map
|