@jmp-technologies/analytics 0.1.27 → 0.1.32

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 (100) hide show
  1. package/dist/astro/client.cjs +256 -11
  2. package/dist/astro/client.cjs.map +1 -1
  3. package/dist/astro/client.d.cts +2 -2
  4. package/dist/astro/client.d.ts +2 -2
  5. package/dist/astro/client.js +2 -2
  6. package/dist/astro/content.cjs +308 -13
  7. package/dist/astro/content.cjs.map +1 -1
  8. package/dist/astro/content.d.cts +12 -3
  9. package/dist/astro/content.d.ts +12 -3
  10. package/dist/astro/content.js +9 -3
  11. package/dist/astro/integration.cjs +228 -10
  12. package/dist/astro/integration.cjs.map +1 -1
  13. package/dist/astro/integration.js +3 -3
  14. package/dist/astro/middleware.cjs +2 -1
  15. package/dist/astro/middleware.cjs.map +1 -1
  16. package/dist/astro/middleware.d.cts +2 -2
  17. package/dist/astro/middleware.d.ts +2 -2
  18. package/dist/astro/middleware.js +2 -2
  19. package/dist/astro/register.cjs +228 -10
  20. package/dist/astro/register.cjs.map +1 -1
  21. package/dist/astro/register.js +3 -3
  22. package/dist/{chunk-KZ4WVDGL.js → chunk-7623G3XL.js} +3 -3
  23. package/dist/{chunk-A4RRTY5D.js → chunk-AIFJ7DVO.js} +3 -2
  24. package/dist/{chunk-A4RRTY5D.js.map → chunk-AIFJ7DVO.js.map} +1 -1
  25. package/dist/{chunk-YCVHRMM6.js → chunk-G5BMWPQ6.js} +2 -3
  26. package/dist/{chunk-YCVHRMM6.js.map → chunk-G5BMWPQ6.js.map} +1 -1
  27. package/dist/{chunk-HODG4NJQ.js → chunk-HFLU7HQE.js} +2 -2
  28. package/dist/{chunk-PG3IOI5O.js → chunk-HKMDWTA2.js} +53 -6
  29. package/dist/chunk-HKMDWTA2.js.map +1 -0
  30. package/dist/{chunk-DNYJF7BG.js → chunk-KPTMZQCY.js} +3 -3
  31. package/dist/{chunk-FNWE2DT5.js → chunk-NDSPSAWQ.js} +18 -8
  32. package/dist/chunk-NDSPSAWQ.js.map +1 -0
  33. package/dist/{chunk-UVYF33XE.js → chunk-NI7R6QZQ.js} +293 -13
  34. package/dist/chunk-NI7R6QZQ.js.map +1 -0
  35. package/dist/chunk-RI3KKDPX.js +75 -0
  36. package/dist/chunk-RI3KKDPX.js.map +1 -0
  37. package/dist/{create-jmp-analytics-D4Qw_d6o.d.cts → create-jmp-analytics-D1dUsqCQ.d.cts} +1 -2
  38. package/dist/{create-jmp-analytics-DpG5Hdl5.d.ts → create-jmp-analytics-htx2oOEf.d.ts} +1 -2
  39. package/dist/index.cjs +295 -12
  40. package/dist/index.cjs.map +1 -1
  41. package/dist/index.d.cts +8 -6
  42. package/dist/index.d.ts +8 -6
  43. package/dist/index.js +10 -2
  44. package/dist/next/content.cjs +310 -13
  45. package/dist/next/content.cjs.map +1 -1
  46. package/dist/next/content.d.cts +7 -5
  47. package/dist/next/content.d.ts +7 -5
  48. package/dist/next/content.js +11 -3
  49. package/dist/next/content.js.map +1 -1
  50. package/dist/next/instrumentation.cjs +228 -10
  51. package/dist/next/instrumentation.cjs.map +1 -1
  52. package/dist/next/instrumentation.js +2 -2
  53. package/dist/next/landing-capture-script-server.cjs +1 -2
  54. package/dist/next/landing-capture-script-server.cjs.map +1 -1
  55. package/dist/next/landing-capture-script-server.js +1 -1
  56. package/dist/next/landing-pages.cjs +228 -12
  57. package/dist/next/landing-pages.cjs.map +1 -1
  58. package/dist/next/landing-pages.d.cts +4 -16
  59. package/dist/next/landing-pages.d.ts +4 -16
  60. package/dist/next/landing-pages.js +13 -67
  61. package/dist/next/landing-pages.js.map +1 -1
  62. package/dist/next/locations.cjs +737 -0
  63. package/dist/next/locations.cjs.map +1 -0
  64. package/dist/next/locations.d.cts +55 -0
  65. package/dist/next/locations.d.ts +55 -0
  66. package/dist/next/locations.js +158 -0
  67. package/dist/next/locations.js.map +1 -0
  68. package/dist/next/middleware.cjs +2 -1
  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 +259 -14
  74. package/dist/next/site-root-server.cjs.map +1 -1
  75. package/dist/next/site-root-server.js +5 -5
  76. package/dist/next.cjs +270 -17
  77. package/dist/next.cjs.map +1 -1
  78. package/dist/next.d.cts +3 -3
  79. package/dist/next.d.ts +3 -3
  80. package/dist/next.js +4 -4
  81. package/dist/react.cjs +389 -136
  82. package/dist/react.cjs.map +1 -1
  83. package/dist/react.d.cts +2 -2
  84. package/dist/react.d.ts +2 -2
  85. package/dist/react.js +3 -3
  86. package/dist/{site-config-UTzDG-UG.d.ts → site-config-B7USGGzu.d.cts} +22 -2
  87. package/dist/{site-config-CJqYnZm7.d.cts → site-config-C4bcUoLI.d.ts} +22 -2
  88. package/dist/{track-page-view-tX0Magkd.d.cts → track-page-view-C4TbbULE.d.cts} +1 -1
  89. package/dist/{track-page-view-CMAqyhD_.d.ts → track-page-view-RiB-xgIx.d.ts} +1 -1
  90. package/dist/{types-B-111Wht.d.ts → types-DRljY1Yy.d.cts} +92 -2
  91. package/dist/{types-B-111Wht.d.cts → types-DRljY1Yy.d.ts} +92 -2
  92. package/dist/utils-BajG58pj.d.cts +15 -0
  93. package/dist/utils-CHT3SQFv.d.ts +15 -0
  94. package/package.json +7 -2
  95. package/dist/chunk-FNWE2DT5.js.map +0 -1
  96. package/dist/chunk-PG3IOI5O.js.map +0 -1
  97. package/dist/chunk-UVYF33XE.js.map +0 -1
  98. /package/dist/{chunk-KZ4WVDGL.js.map → chunk-7623G3XL.js.map} +0 -0
  99. /package/dist/{chunk-HODG4NJQ.js.map → chunk-HFLU7HQE.js.map} +0 -0
  100. /package/dist/{chunk-DNYJF7BG.js.map → chunk-KPTMZQCY.js.map} +0 -0
package/dist/next.cjs CHANGED
@@ -390,6 +390,26 @@ function classifyContactHref(href) {
390
390
  }
391
391
  return null;
392
392
  }
393
+ function extractTelTarget(href) {
394
+ const raw = href.trim();
395
+ if (!raw.toLowerCase().startsWith("tel:")) {
396
+ return null;
397
+ }
398
+ const target = decodeURIComponent(
399
+ raw.slice(4).split("?")[0]?.split(";")[0] ?? ""
400
+ ).trim();
401
+ return target || null;
402
+ }
403
+ function extractMailtoTarget(href) {
404
+ const raw = href.trim();
405
+ if (!raw.toLowerCase().startsWith("mailto:")) {
406
+ return null;
407
+ }
408
+ const target = decodeURIComponent(
409
+ raw.slice(7).split("?")[0]?.split(",")[0] ?? ""
410
+ ).trim();
411
+ return target || null;
412
+ }
393
413
 
394
414
  // src/consent.ts
395
415
  var moduleTrackingConsentGranted = true;
@@ -776,14 +796,22 @@ function createJmpAnalytics(config) {
776
796
  if (dedupeContactClick(`call_click:${path}`)) {
777
797
  return;
778
798
  }
779
- void trackCallClick(path);
799
+ const contactTarget = extractTelTarget(href);
800
+ void trackCallClick(
801
+ path,
802
+ contactTarget ? { contactTarget } : void 0
803
+ );
780
804
  return;
781
805
  }
782
806
  if (classified.kind === "email") {
783
807
  if (dedupeContactClick(`email_click:${path}`)) {
784
808
  return;
785
809
  }
786
- void trackEmailClick(path);
810
+ const contactTarget = extractMailtoTarget(href);
811
+ void trackEmailClick(
812
+ path,
813
+ contactTarget ? { contactTarget } : void 0
814
+ );
787
815
  return;
788
816
  }
789
817
  if (dedupeContactClick(`contact_click:${classified.channel}:${path}`)) {
@@ -835,6 +863,209 @@ function getJmpSiteDefinition() {
835
863
  return registeredSite;
836
864
  }
837
865
 
866
+ // src/content/manifest.ts
867
+ var JMP_CONTENT_MANIFEST_VERSION = 1;
868
+ var TRUST_SECTIONS = /* @__PURE__ */ new Set([
869
+ "summary",
870
+ "testimonials",
871
+ "reviews",
872
+ "stats",
873
+ "logos",
874
+ "credentials"
875
+ ]);
876
+ function normalizeKey(value) {
877
+ return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
878
+ }
879
+ function isValidExtraKey(key) {
880
+ return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(key);
881
+ }
882
+ var MAX_EXTERNAL_ADMIN_LINKS = 10;
883
+ var MAX_EXTERNAL_ADMIN_LINK_LABEL_LENGTH = 80;
884
+ var EXTERNAL_ADMIN_LINK_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
885
+ function tryNormalizeHttpUrl(input) {
886
+ if (typeof input !== "string" || !input.trim()) {
887
+ return null;
888
+ }
889
+ try {
890
+ const url = new URL(input.trim());
891
+ if (url.protocol !== "https:" && url.protocol !== "http:") {
892
+ return null;
893
+ }
894
+ return url.toString().replace(/\/$/, "");
895
+ } catch {
896
+ return null;
897
+ }
898
+ }
899
+ function normalizeShopifyExternalSource(shopify) {
900
+ if (!shopify || typeof shopify !== "object") {
901
+ return null;
902
+ }
903
+ const adminUrl = tryNormalizeHttpUrl(shopify.adminUrl);
904
+ const storeUrl = tryNormalizeHttpUrl(shopify.storeUrl);
905
+ if (!adminUrl && !storeUrl) {
906
+ return null;
907
+ }
908
+ return {
909
+ ...adminUrl ? { adminUrl } : {},
910
+ ...storeUrl ? { storeUrl } : {}
911
+ };
912
+ }
913
+ function normalizeExternalAdminLinkEntries(links) {
914
+ if (!Array.isArray(links)) {
915
+ return [];
916
+ }
917
+ const seenIds = /* @__PURE__ */ new Set();
918
+ const out = [];
919
+ for (const raw of links) {
920
+ if (!raw || typeof raw !== "object" || out.length >= MAX_EXTERNAL_ADMIN_LINKS) {
921
+ continue;
922
+ }
923
+ const id = typeof raw.id === "string" ? raw.id.trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "") : "";
924
+ const label = typeof raw.label === "string" ? raw.label.trim().slice(0, MAX_EXTERNAL_ADMIN_LINK_LABEL_LENGTH) : "";
925
+ const url = tryNormalizeHttpUrl(raw.url);
926
+ if (!id || !label || !url || !EXTERNAL_ADMIN_LINK_ID_PATTERN.test(id) || seenIds.has(id)) {
927
+ continue;
928
+ }
929
+ seenIds.add(id);
930
+ out.push({ id, label, url });
931
+ }
932
+ return out;
933
+ }
934
+ function normalizeExternalSources(externalSources) {
935
+ if (!externalSources || typeof externalSources !== "object") {
936
+ return null;
937
+ }
938
+ const studioUrl = tryNormalizeHttpUrl(externalSources.sanity?.studioUrl);
939
+ const shopify = normalizeShopifyExternalSource(externalSources.shopify);
940
+ const links = normalizeExternalAdminLinkEntries(externalSources.links);
941
+ if (!studioUrl && !shopify && links.length === 0) {
942
+ return null;
943
+ }
944
+ return {
945
+ ...studioUrl ? { sanity: { studioUrl } } : {},
946
+ ...shopify ? { shopify } : {},
947
+ ...links.length > 0 ? { links } : {}
948
+ };
949
+ }
950
+ function normalizeJmpContentManifest(input) {
951
+ if (!input || typeof input !== "object") {
952
+ return null;
953
+ }
954
+ const routes = normalizeRoutes(input.routes);
955
+ const externalSources = normalizeExternalSources(input.externalSources);
956
+ const trust = normalizeTrust(input.trust);
957
+ const contact = normalizeContact(input.contact);
958
+ if (routes.length === 0 && !externalSources && !trust && !contact) {
959
+ return null;
960
+ }
961
+ return {
962
+ version: JMP_CONTENT_MANIFEST_VERSION,
963
+ ...routes.length > 0 ? { routes } : {},
964
+ ...externalSources ? { externalSources } : {},
965
+ ...trust ? { trust } : {},
966
+ ...contact ? { contact } : {}
967
+ };
968
+ }
969
+ function normalizeRoutes(routes) {
970
+ if (!Array.isArray(routes)) {
971
+ return [];
972
+ }
973
+ const seen = /* @__PURE__ */ new Set();
974
+ const out = [];
975
+ for (const raw of routes) {
976
+ if (!raw || typeof raw !== "object") continue;
977
+ const wrapper = typeof raw.wrapper === "string" ? raw.wrapper.trim().toLowerCase() : "";
978
+ const pattern = typeof raw.pattern === "string" ? raw.pattern.trim() : "";
979
+ if (!wrapper || !pattern) continue;
980
+ const dedupe = `${wrapper}:${pattern}`;
981
+ if (seen.has(dedupe)) continue;
982
+ seen.add(dedupe);
983
+ out.push({
984
+ wrapper,
985
+ pattern,
986
+ ...typeof raw.index === "string" && raw.index.trim() ? { index: raw.index.trim() } : {},
987
+ ...typeof raw.label === "string" && raw.label.trim() ? { label: raw.label.trim() } : {}
988
+ });
989
+ }
990
+ return out;
991
+ }
992
+ function normalizeTrust(trust) {
993
+ if (!trust || typeof trust !== "object") return null;
994
+ const blocks = normalizeTrustBlocks(trust.blocks);
995
+ const labels = normalizeTrustLabels(trust.labels);
996
+ const mergeTestimonialsReviews = trust.mergeTestimonialsReviews === true;
997
+ if (blocks.length === 0 && !labels && !mergeTestimonialsReviews) return null;
998
+ return {
999
+ ...blocks.length > 0 ? { blocks } : {},
1000
+ ...labels ? { labels } : {},
1001
+ ...mergeTestimonialsReviews ? { mergeTestimonialsReviews: true } : {}
1002
+ };
1003
+ }
1004
+ function normalizeTrustBlocks(blocks) {
1005
+ if (!Array.isArray(blocks)) return [];
1006
+ const seen = /* @__PURE__ */ new Set();
1007
+ const out = [];
1008
+ for (const raw of blocks) {
1009
+ if (!raw || typeof raw !== "object") continue;
1010
+ const section = typeof raw.section === "string" ? raw.section.trim().toLowerCase() : "";
1011
+ if (!TRUST_SECTIONS.has(section) || seen.has(section)) continue;
1012
+ seen.add(section);
1013
+ out.push({
1014
+ section,
1015
+ ...typeof raw.label === "string" && raw.label.trim() ? { label: raw.label.trim() } : {}
1016
+ });
1017
+ }
1018
+ return out;
1019
+ }
1020
+ function normalizeTrustLabels(labels) {
1021
+ if (!labels || typeof labels !== "object") return null;
1022
+ const out = {};
1023
+ for (const [key, value] of Object.entries(labels)) {
1024
+ if (TRUST_SECTIONS.has(key) && typeof value === "string" && value.trim()) {
1025
+ out[key] = value.trim();
1026
+ }
1027
+ }
1028
+ return Object.keys(out).length > 0 ? out : null;
1029
+ }
1030
+ function normalizeContact(contact) {
1031
+ if (!contact || typeof contact !== "object") return null;
1032
+ const extras = normalizeContactExtras(contact.extras);
1033
+ return extras.length > 0 ? { extras } : null;
1034
+ }
1035
+ function normalizeContactExtras(extras) {
1036
+ if (!Array.isArray(extras)) return [];
1037
+ const seen = /* @__PURE__ */ new Set();
1038
+ const out = [];
1039
+ for (const raw of extras) {
1040
+ if (!raw || typeof raw !== "object") continue;
1041
+ const label = typeof raw.label === "string" ? raw.label.trim() : "";
1042
+ const key = normalizeKey(
1043
+ typeof raw.key === "string" && raw.key.trim() ? raw.key : label
1044
+ );
1045
+ if (!key || !label || !isValidExtraKey(key) || seen.has(key)) continue;
1046
+ seen.add(key);
1047
+ out.push({ key, label });
1048
+ }
1049
+ return out;
1050
+ }
1051
+ function stableJmpManifestFingerprint(manifest) {
1052
+ return manifest ? JSON.stringify(manifest) : "";
1053
+ }
1054
+ function inferWrapperSlotsFromManifest(manifest) {
1055
+ const slots = /* @__PURE__ */ new Set();
1056
+ for (const route of manifest?.routes ?? []) {
1057
+ slots.add(route.wrapper);
1058
+ }
1059
+ for (const block of manifest?.trust?.blocks ?? []) {
1060
+ slots.add(`trust.${block.section}`);
1061
+ slots.add("trust");
1062
+ }
1063
+ if (manifest?.contact?.extras?.length) {
1064
+ slots.add("contact");
1065
+ }
1066
+ return [...slots];
1067
+ }
1068
+
838
1069
  // src/content/wrapper-discovery.ts
839
1070
  var WRAPPER_SLOT_ORDER = [
840
1071
  "blog",
@@ -854,16 +1085,22 @@ var syncHandler = null;
854
1085
  function getDiscoveredContentWrappers() {
855
1086
  return sortWrapperSlots([...discovered]);
856
1087
  }
1088
+ function resolveContentManifestForReport(config) {
1089
+ return normalizeJmpContentManifest(config.contentManifest);
1090
+ }
857
1091
  function resolveContentWrappersForReport(config) {
858
1092
  const explicit = config.contentWrappers ?? [];
859
1093
  const auto = getDiscoveredContentWrappers();
1094
+ const fromManifest = inferWrapperSlotsFromManifest(
1095
+ resolveContentManifestForReport(config)
1096
+ );
860
1097
  if (config.contentWrapperMode === "manual") {
861
- return sortWrapperSlots([...explicit]);
1098
+ return sortWrapperSlots([...explicit, ...fromManifest]);
862
1099
  }
863
1100
  if (config.contentWrapperMode === "explicit-only") {
864
- return sortWrapperSlots([...explicit]);
1101
+ return sortWrapperSlots([...explicit, ...fromManifest]);
865
1102
  }
866
- const merged = /* @__PURE__ */ new Set([...explicit, ...auto]);
1103
+ const merged = /* @__PURE__ */ new Set([...explicit, ...auto, ...fromManifest]);
867
1104
  return sortWrapperSlots([...merged]);
868
1105
  }
869
1106
  function hasReportedContentWrappers(dedupeKey) {
@@ -893,7 +1130,8 @@ async function registerJmpContentWrappers(config) {
893
1130
  if (!wrappers.length) {
894
1131
  return { ok: false, skipped: true };
895
1132
  }
896
- const dedupeKey = `${config.trackingKey}:${[...wrappers].sort().join(",")}`;
1133
+ const manifest = resolveContentManifestForReport(config);
1134
+ const dedupeKey = `${config.trackingKey}:${[...wrappers].sort().join(",")}:${stableJmpManifestFingerprint(manifest)}`;
897
1135
  if (hasReportedContentWrappers(dedupeKey)) {
898
1136
  return { ok: true, wrappers };
899
1137
  }
@@ -906,7 +1144,17 @@ async function registerJmpContentWrappers(config) {
906
1144
  }
907
1145
  const url = `${baseUrl}/api/content/capabilities`;
908
1146
  if (config.debug) {
909
- console.debug("[@jmp-technologies/analytics]", "POST", url, { wrappers });
1147
+ console.debug("[@jmp-technologies/analytics]", "POST", url, {
1148
+ wrappers,
1149
+ manifest
1150
+ });
1151
+ }
1152
+ const body = {
1153
+ wrappers,
1154
+ sdkVersion: JMP_ANALYTICS_SDK_VERSION
1155
+ };
1156
+ if (manifest) {
1157
+ body.manifest = manifest;
910
1158
  }
911
1159
  const res = await resolvedFetch(url, {
912
1160
  method: "POST",
@@ -914,10 +1162,7 @@ async function registerJmpContentWrappers(config) {
914
1162
  "Content-Type": "application/json",
915
1163
  "X-JMP-Tracking-Key": config.trackingKey
916
1164
  },
917
- body: JSON.stringify({
918
- wrappers,
919
- sdkVersion: JMP_ANALYTICS_SDK_VERSION
920
- })
1165
+ body: JSON.stringify(body)
921
1166
  });
922
1167
  const text = await res.text();
923
1168
  if (!res.ok) {
@@ -987,13 +1232,21 @@ function getOrCreateJmpAnalyticsForNextApp(overrides) {
987
1232
  // src/react.tsx
988
1233
  var import_react = require("react");
989
1234
  var import_jsx_runtime = require("react/jsx-runtime");
990
- function trackCallClickFromCurrentPath() {
1235
+ function trackCallClickFromHref(href) {
991
1236
  const path = typeof window !== "undefined" ? window.location.pathname : "/";
992
- void getOrCreateJmpAnalyticsFromBrowserEnv()?.trackCallClick(path);
1237
+ const contactTarget = extractTelTarget(href);
1238
+ void getOrCreateJmpAnalyticsFromBrowserEnv()?.trackCallClick(
1239
+ path,
1240
+ contactTarget ? { contactTarget } : void 0
1241
+ );
993
1242
  }
994
- function trackEmailClickFromCurrentPath() {
1243
+ function trackEmailClickFromHref(href) {
995
1244
  const path = typeof window !== "undefined" ? window.location.pathname : "/";
996
- void getOrCreateJmpAnalyticsFromBrowserEnv()?.trackEmailClick(path);
1245
+ const contactTarget = extractMailtoTarget(href);
1246
+ void getOrCreateJmpAnalyticsFromBrowserEnv()?.trackEmailClick(
1247
+ path,
1248
+ contactTarget ? { contactTarget } : void 0
1249
+ );
997
1250
  }
998
1251
  function JmpTrackedTelLink({
999
1252
  href,
@@ -1007,7 +1260,7 @@ function JmpTrackedTelLink({
1007
1260
  "data-jmp-tracked": "true",
1008
1261
  ...rest,
1009
1262
  onClick: (e) => {
1010
- trackCallClickFromCurrentPath();
1263
+ trackCallClickFromHref(href);
1011
1264
  onClick?.(e);
1012
1265
  }
1013
1266
  }
@@ -1025,7 +1278,7 @@ function JmpTrackedMailtoLink({
1025
1278
  "data-jmp-tracked": "true",
1026
1279
  ...rest,
1027
1280
  onClick: (e) => {
1028
- trackEmailClickFromCurrentPath();
1281
+ trackEmailClickFromHref(href);
1029
1282
  onClick?.(e);
1030
1283
  }
1031
1284
  }