@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.
- package/dist/astro/client.cjs +256 -11
- package/dist/astro/client.cjs.map +1 -1
- package/dist/astro/client.d.cts +2 -2
- package/dist/astro/client.d.ts +2 -2
- package/dist/astro/client.js +2 -2
- package/dist/astro/content.cjs +308 -13
- package/dist/astro/content.cjs.map +1 -1
- package/dist/astro/content.d.cts +12 -3
- package/dist/astro/content.d.ts +12 -3
- package/dist/astro/content.js +9 -3
- package/dist/astro/integration.cjs +228 -10
- package/dist/astro/integration.cjs.map +1 -1
- package/dist/astro/integration.js +3 -3
- package/dist/astro/middleware.cjs +2 -1
- 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 +228 -10
- package/dist/astro/register.cjs.map +1 -1
- package/dist/astro/register.js +3 -3
- package/dist/{chunk-KZ4WVDGL.js → chunk-7623G3XL.js} +3 -3
- package/dist/{chunk-A4RRTY5D.js → chunk-AIFJ7DVO.js} +3 -2
- package/dist/{chunk-A4RRTY5D.js.map → chunk-AIFJ7DVO.js.map} +1 -1
- package/dist/{chunk-YCVHRMM6.js → chunk-G5BMWPQ6.js} +2 -3
- package/dist/{chunk-YCVHRMM6.js.map → chunk-G5BMWPQ6.js.map} +1 -1
- package/dist/{chunk-HODG4NJQ.js → chunk-HFLU7HQE.js} +2 -2
- package/dist/{chunk-PG3IOI5O.js → chunk-HKMDWTA2.js} +53 -6
- package/dist/chunk-HKMDWTA2.js.map +1 -0
- package/dist/{chunk-DNYJF7BG.js → chunk-KPTMZQCY.js} +3 -3
- package/dist/{chunk-FNWE2DT5.js → chunk-NDSPSAWQ.js} +18 -8
- package/dist/chunk-NDSPSAWQ.js.map +1 -0
- package/dist/{chunk-UVYF33XE.js → chunk-NI7R6QZQ.js} +293 -13
- package/dist/chunk-NI7R6QZQ.js.map +1 -0
- package/dist/chunk-RI3KKDPX.js +75 -0
- package/dist/chunk-RI3KKDPX.js.map +1 -0
- package/dist/{create-jmp-analytics-D4Qw_d6o.d.cts → create-jmp-analytics-D1dUsqCQ.d.cts} +1 -2
- package/dist/{create-jmp-analytics-DpG5Hdl5.d.ts → create-jmp-analytics-htx2oOEf.d.ts} +1 -2
- package/dist/index.cjs +295 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -6
- package/dist/index.d.ts +8 -6
- package/dist/index.js +10 -2
- package/dist/next/content.cjs +310 -13
- package/dist/next/content.cjs.map +1 -1
- package/dist/next/content.d.cts +7 -5
- package/dist/next/content.d.ts +7 -5
- package/dist/next/content.js +11 -3
- package/dist/next/content.js.map +1 -1
- package/dist/next/instrumentation.cjs +228 -10
- package/dist/next/instrumentation.cjs.map +1 -1
- package/dist/next/instrumentation.js +2 -2
- package/dist/next/landing-capture-script-server.cjs +1 -2
- package/dist/next/landing-capture-script-server.cjs.map +1 -1
- package/dist/next/landing-capture-script-server.js +1 -1
- package/dist/next/landing-pages.cjs +228 -12
- package/dist/next/landing-pages.cjs.map +1 -1
- package/dist/next/landing-pages.d.cts +4 -16
- package/dist/next/landing-pages.d.ts +4 -16
- package/dist/next/landing-pages.js +13 -67
- package/dist/next/landing-pages.js.map +1 -1
- package/dist/next/locations.cjs +737 -0
- package/dist/next/locations.cjs.map +1 -0
- package/dist/next/locations.d.cts +55 -0
- package/dist/next/locations.d.ts +55 -0
- package/dist/next/locations.js +158 -0
- package/dist/next/locations.js.map +1 -0
- package/dist/next/middleware.cjs +2 -1
- 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/site-root-server.cjs +259 -14
- package/dist/next/site-root-server.cjs.map +1 -1
- package/dist/next/site-root-server.js +5 -5
- package/dist/next.cjs +270 -17
- package/dist/next.cjs.map +1 -1
- package/dist/next.d.cts +3 -3
- package/dist/next.d.ts +3 -3
- package/dist/next.js +4 -4
- package/dist/react.cjs +389 -136
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +3 -3
- package/dist/{site-config-UTzDG-UG.d.ts → site-config-B7USGGzu.d.cts} +22 -2
- package/dist/{site-config-CJqYnZm7.d.cts → site-config-C4bcUoLI.d.ts} +22 -2
- package/dist/{track-page-view-tX0Magkd.d.cts → track-page-view-C4TbbULE.d.cts} +1 -1
- package/dist/{track-page-view-CMAqyhD_.d.ts → track-page-view-RiB-xgIx.d.ts} +1 -1
- package/dist/{types-B-111Wht.d.ts → types-DRljY1Yy.d.cts} +92 -2
- package/dist/{types-B-111Wht.d.cts → types-DRljY1Yy.d.ts} +92 -2
- package/dist/utils-BajG58pj.d.cts +15 -0
- package/dist/utils-CHT3SQFv.d.ts +15 -0
- package/package.json +7 -2
- package/dist/chunk-FNWE2DT5.js.map +0 -1
- package/dist/chunk-PG3IOI5O.js.map +0 -1
- package/dist/chunk-UVYF33XE.js.map +0 -1
- /package/dist/{chunk-KZ4WVDGL.js.map → chunk-7623G3XL.js.map} +0 -0
- /package/dist/{chunk-HODG4NJQ.js.map → chunk-HFLU7HQE.js.map} +0 -0
- /package/dist/{chunk-DNYJF7BG.js.map → chunk-KPTMZQCY.js.map} +0 -0
package/dist/react.cjs
CHANGED
|
@@ -28,6 +28,145 @@ __export(react_exports, {
|
|
|
28
28
|
module.exports = __toCommonJS(react_exports);
|
|
29
29
|
var import_react = require("react");
|
|
30
30
|
|
|
31
|
+
// src/contact-link-classifier.ts
|
|
32
|
+
var BOOKING_LINK_HOSTS = [
|
|
33
|
+
"calendly.com",
|
|
34
|
+
"cal.com",
|
|
35
|
+
"acuityscheduling.com",
|
|
36
|
+
"calendar.google.com",
|
|
37
|
+
"calendar.app.google",
|
|
38
|
+
"bookings.microsoft.com",
|
|
39
|
+
"outlook.office.com",
|
|
40
|
+
"outlook.office365.com",
|
|
41
|
+
"book.ms",
|
|
42
|
+
"setmore.com",
|
|
43
|
+
"simplybook.me",
|
|
44
|
+
"oncehub.com",
|
|
45
|
+
"scheduleonce.com",
|
|
46
|
+
"youcanbook.me",
|
|
47
|
+
"savvycal.com",
|
|
48
|
+
"tidycal.com",
|
|
49
|
+
"zohobookings.com",
|
|
50
|
+
"square.site",
|
|
51
|
+
"squareup.com",
|
|
52
|
+
"meetings.hubspot.com",
|
|
53
|
+
"jobber.com",
|
|
54
|
+
"housecallpro.com",
|
|
55
|
+
"servicetitan.com",
|
|
56
|
+
"thryv.com",
|
|
57
|
+
"honeybook.com",
|
|
58
|
+
"dubsado.com",
|
|
59
|
+
"fresha.com",
|
|
60
|
+
"booksy.com",
|
|
61
|
+
"vagaro.com",
|
|
62
|
+
"mindbodyonline.com",
|
|
63
|
+
"mindbody.io",
|
|
64
|
+
"janeapp.com",
|
|
65
|
+
"cliniko.com",
|
|
66
|
+
"practicebetter.io"
|
|
67
|
+
];
|
|
68
|
+
var WHATSAPP_HOSTS = ["wa.me", "api.whatsapp.com", "whatsapp.com"];
|
|
69
|
+
function hostnameMatches(host, pattern) {
|
|
70
|
+
const h = host.toLowerCase();
|
|
71
|
+
const p = pattern.toLowerCase();
|
|
72
|
+
return h === p || h.endsWith(`.${p}`);
|
|
73
|
+
}
|
|
74
|
+
function isMapsHost(host, pathname) {
|
|
75
|
+
if (hostnameMatches(host, "maps.apple.com")) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
if (hostnameMatches(host, "maps.app.goo.gl")) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
if (hostnameMatches(host, "google.com") || hostnameMatches(host, "google.co")) {
|
|
82
|
+
return pathname === "/maps" || pathname.startsWith("/maps/") || pathname.startsWith("/local");
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
function isWhatsAppHost(host, pathname) {
|
|
87
|
+
for (const pattern of WHATSAPP_HOSTS) {
|
|
88
|
+
if (!hostnameMatches(host, pattern)) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (pattern === "whatsapp.com") {
|
|
92
|
+
return pathname.startsWith("/send");
|
|
93
|
+
}
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
function isBookingHost(host) {
|
|
99
|
+
for (const pattern of BOOKING_LINK_HOSTS) {
|
|
100
|
+
if (hostnameMatches(host, pattern)) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
function classifyContactHref(href) {
|
|
107
|
+
const raw = href.trim();
|
|
108
|
+
if (!raw || raw.startsWith("#") || raw.startsWith("javascript:")) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const lower = raw.toLowerCase();
|
|
112
|
+
if (lower.startsWith("tel:")) {
|
|
113
|
+
return { kind: "call" };
|
|
114
|
+
}
|
|
115
|
+
if (lower.startsWith("mailto:")) {
|
|
116
|
+
return { kind: "email" };
|
|
117
|
+
}
|
|
118
|
+
if (lower.startsWith("sms:") || lower.startsWith("smsto:")) {
|
|
119
|
+
return { kind: "contact_click", channel: "sms", linkHost: null };
|
|
120
|
+
}
|
|
121
|
+
if (lower.startsWith("geo:")) {
|
|
122
|
+
return { kind: "contact_click", channel: "maps", linkHost: null };
|
|
123
|
+
}
|
|
124
|
+
if (raw.startsWith("/") || raw.startsWith("?")) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
let url;
|
|
128
|
+
try {
|
|
129
|
+
url = new URL(raw);
|
|
130
|
+
} catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
const host = url.hostname.toLowerCase();
|
|
137
|
+
const pathname = url.pathname.toLowerCase();
|
|
138
|
+
if (isWhatsAppHost(host, pathname)) {
|
|
139
|
+
return { kind: "contact_click", channel: "whatsapp", linkHost: host };
|
|
140
|
+
}
|
|
141
|
+
if (isMapsHost(host, pathname)) {
|
|
142
|
+
return { kind: "contact_click", channel: "maps", linkHost: host };
|
|
143
|
+
}
|
|
144
|
+
if (isBookingHost(host)) {
|
|
145
|
+
return { kind: "contact_click", channel: "booking", linkHost: host };
|
|
146
|
+
}
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
function extractTelTarget(href) {
|
|
150
|
+
const raw = href.trim();
|
|
151
|
+
if (!raw.toLowerCase().startsWith("tel:")) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
const target = decodeURIComponent(
|
|
155
|
+
raw.slice(4).split("?")[0]?.split(";")[0] ?? ""
|
|
156
|
+
).trim();
|
|
157
|
+
return target || null;
|
|
158
|
+
}
|
|
159
|
+
function extractMailtoTarget(href) {
|
|
160
|
+
const raw = href.trim();
|
|
161
|
+
if (!raw.toLowerCase().startsWith("mailto:")) {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
const target = decodeURIComponent(
|
|
165
|
+
raw.slice(7).split("?")[0]?.split(",")[0] ?? ""
|
|
166
|
+
).trim();
|
|
167
|
+
return target || null;
|
|
168
|
+
}
|
|
169
|
+
|
|
31
170
|
// src/landing-capture.ts
|
|
32
171
|
var JMP_SESSION_ID_KEY = "jmp_analytics_sid";
|
|
33
172
|
var JMP_LANDING_REFERER_KEY = "jmp_analytics_landing_referer";
|
|
@@ -166,125 +305,6 @@ function getOrCreateSessionIdFromStorage() {
|
|
|
166
305
|
}
|
|
167
306
|
}
|
|
168
307
|
|
|
169
|
-
// src/contact-link-classifier.ts
|
|
170
|
-
var BOOKING_LINK_HOSTS = [
|
|
171
|
-
"calendly.com",
|
|
172
|
-
"cal.com",
|
|
173
|
-
"acuityscheduling.com",
|
|
174
|
-
"calendar.google.com",
|
|
175
|
-
"calendar.app.google",
|
|
176
|
-
"bookings.microsoft.com",
|
|
177
|
-
"outlook.office.com",
|
|
178
|
-
"outlook.office365.com",
|
|
179
|
-
"book.ms",
|
|
180
|
-
"setmore.com",
|
|
181
|
-
"simplybook.me",
|
|
182
|
-
"oncehub.com",
|
|
183
|
-
"scheduleonce.com",
|
|
184
|
-
"youcanbook.me",
|
|
185
|
-
"savvycal.com",
|
|
186
|
-
"tidycal.com",
|
|
187
|
-
"zohobookings.com",
|
|
188
|
-
"square.site",
|
|
189
|
-
"squareup.com",
|
|
190
|
-
"meetings.hubspot.com",
|
|
191
|
-
"jobber.com",
|
|
192
|
-
"housecallpro.com",
|
|
193
|
-
"servicetitan.com",
|
|
194
|
-
"thryv.com",
|
|
195
|
-
"honeybook.com",
|
|
196
|
-
"dubsado.com",
|
|
197
|
-
"fresha.com",
|
|
198
|
-
"booksy.com",
|
|
199
|
-
"vagaro.com",
|
|
200
|
-
"mindbodyonline.com",
|
|
201
|
-
"mindbody.io",
|
|
202
|
-
"janeapp.com",
|
|
203
|
-
"cliniko.com",
|
|
204
|
-
"practicebetter.io"
|
|
205
|
-
];
|
|
206
|
-
var WHATSAPP_HOSTS = ["wa.me", "api.whatsapp.com", "whatsapp.com"];
|
|
207
|
-
function hostnameMatches(host, pattern) {
|
|
208
|
-
const h = host.toLowerCase();
|
|
209
|
-
const p = pattern.toLowerCase();
|
|
210
|
-
return h === p || h.endsWith(`.${p}`);
|
|
211
|
-
}
|
|
212
|
-
function isMapsHost(host, pathname) {
|
|
213
|
-
if (hostnameMatches(host, "maps.apple.com")) {
|
|
214
|
-
return true;
|
|
215
|
-
}
|
|
216
|
-
if (hostnameMatches(host, "maps.app.goo.gl")) {
|
|
217
|
-
return true;
|
|
218
|
-
}
|
|
219
|
-
if (hostnameMatches(host, "google.com") || hostnameMatches(host, "google.co")) {
|
|
220
|
-
return pathname === "/maps" || pathname.startsWith("/maps/") || pathname.startsWith("/local");
|
|
221
|
-
}
|
|
222
|
-
return false;
|
|
223
|
-
}
|
|
224
|
-
function isWhatsAppHost(host, pathname) {
|
|
225
|
-
for (const pattern of WHATSAPP_HOSTS) {
|
|
226
|
-
if (!hostnameMatches(host, pattern)) {
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
if (pattern === "whatsapp.com") {
|
|
230
|
-
return pathname.startsWith("/send");
|
|
231
|
-
}
|
|
232
|
-
return true;
|
|
233
|
-
}
|
|
234
|
-
return false;
|
|
235
|
-
}
|
|
236
|
-
function isBookingHost(host) {
|
|
237
|
-
for (const pattern of BOOKING_LINK_HOSTS) {
|
|
238
|
-
if (hostnameMatches(host, pattern)) {
|
|
239
|
-
return true;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
return false;
|
|
243
|
-
}
|
|
244
|
-
function classifyContactHref(href) {
|
|
245
|
-
const raw = href.trim();
|
|
246
|
-
if (!raw || raw.startsWith("#") || raw.startsWith("javascript:")) {
|
|
247
|
-
return null;
|
|
248
|
-
}
|
|
249
|
-
const lower = raw.toLowerCase();
|
|
250
|
-
if (lower.startsWith("tel:")) {
|
|
251
|
-
return { kind: "call" };
|
|
252
|
-
}
|
|
253
|
-
if (lower.startsWith("mailto:")) {
|
|
254
|
-
return { kind: "email" };
|
|
255
|
-
}
|
|
256
|
-
if (lower.startsWith("sms:") || lower.startsWith("smsto:")) {
|
|
257
|
-
return { kind: "contact_click", channel: "sms", linkHost: null };
|
|
258
|
-
}
|
|
259
|
-
if (lower.startsWith("geo:")) {
|
|
260
|
-
return { kind: "contact_click", channel: "maps", linkHost: null };
|
|
261
|
-
}
|
|
262
|
-
if (raw.startsWith("/") || raw.startsWith("?")) {
|
|
263
|
-
return null;
|
|
264
|
-
}
|
|
265
|
-
let url;
|
|
266
|
-
try {
|
|
267
|
-
url = new URL(raw);
|
|
268
|
-
} catch {
|
|
269
|
-
return null;
|
|
270
|
-
}
|
|
271
|
-
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
272
|
-
return null;
|
|
273
|
-
}
|
|
274
|
-
const host = url.hostname.toLowerCase();
|
|
275
|
-
const pathname = url.pathname.toLowerCase();
|
|
276
|
-
if (isWhatsAppHost(host, pathname)) {
|
|
277
|
-
return { kind: "contact_click", channel: "whatsapp", linkHost: host };
|
|
278
|
-
}
|
|
279
|
-
if (isMapsHost(host, pathname)) {
|
|
280
|
-
return { kind: "contact_click", channel: "maps", linkHost: host };
|
|
281
|
-
}
|
|
282
|
-
if (isBookingHost(host)) {
|
|
283
|
-
return { kind: "contact_click", channel: "booking", linkHost: host };
|
|
284
|
-
}
|
|
285
|
-
return null;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
308
|
// src/constants.ts
|
|
289
309
|
var JMP_ANALYTICS_SDK_VERSION = "0.1.26";
|
|
290
310
|
var JMP_TRACKING_CONSENT_STORAGE_KEY = "jmp_analytics_consent_v1";
|
|
@@ -700,14 +720,22 @@ function createJmpAnalytics(config) {
|
|
|
700
720
|
if (dedupeContactClick(`call_click:${path}`)) {
|
|
701
721
|
return;
|
|
702
722
|
}
|
|
703
|
-
|
|
723
|
+
const contactTarget = extractTelTarget(href);
|
|
724
|
+
void trackCallClick(
|
|
725
|
+
path,
|
|
726
|
+
contactTarget ? { contactTarget } : void 0
|
|
727
|
+
);
|
|
704
728
|
return;
|
|
705
729
|
}
|
|
706
730
|
if (classified.kind === "email") {
|
|
707
731
|
if (dedupeContactClick(`email_click:${path}`)) {
|
|
708
732
|
return;
|
|
709
733
|
}
|
|
710
|
-
|
|
734
|
+
const contactTarget = extractMailtoTarget(href);
|
|
735
|
+
void trackEmailClick(
|
|
736
|
+
path,
|
|
737
|
+
contactTarget ? { contactTarget } : void 0
|
|
738
|
+
);
|
|
711
739
|
return;
|
|
712
740
|
}
|
|
713
741
|
if (dedupeContactClick(`contact_click:${classified.channel}:${path}`)) {
|
|
@@ -753,6 +781,209 @@ function createJmpAnalytics(config) {
|
|
|
753
781
|
};
|
|
754
782
|
}
|
|
755
783
|
|
|
784
|
+
// src/content/manifest.ts
|
|
785
|
+
var JMP_CONTENT_MANIFEST_VERSION = 1;
|
|
786
|
+
var TRUST_SECTIONS = /* @__PURE__ */ new Set([
|
|
787
|
+
"summary",
|
|
788
|
+
"testimonials",
|
|
789
|
+
"reviews",
|
|
790
|
+
"stats",
|
|
791
|
+
"logos",
|
|
792
|
+
"credentials"
|
|
793
|
+
]);
|
|
794
|
+
function normalizeKey(value) {
|
|
795
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
796
|
+
}
|
|
797
|
+
function isValidExtraKey(key) {
|
|
798
|
+
return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(key);
|
|
799
|
+
}
|
|
800
|
+
var MAX_EXTERNAL_ADMIN_LINKS = 10;
|
|
801
|
+
var MAX_EXTERNAL_ADMIN_LINK_LABEL_LENGTH = 80;
|
|
802
|
+
var EXTERNAL_ADMIN_LINK_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
803
|
+
function tryNormalizeHttpUrl(input) {
|
|
804
|
+
if (typeof input !== "string" || !input.trim()) {
|
|
805
|
+
return null;
|
|
806
|
+
}
|
|
807
|
+
try {
|
|
808
|
+
const url = new URL(input.trim());
|
|
809
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
810
|
+
return null;
|
|
811
|
+
}
|
|
812
|
+
return url.toString().replace(/\/$/, "");
|
|
813
|
+
} catch {
|
|
814
|
+
return null;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
function normalizeShopifyExternalSource(shopify) {
|
|
818
|
+
if (!shopify || typeof shopify !== "object") {
|
|
819
|
+
return null;
|
|
820
|
+
}
|
|
821
|
+
const adminUrl = tryNormalizeHttpUrl(shopify.adminUrl);
|
|
822
|
+
const storeUrl = tryNormalizeHttpUrl(shopify.storeUrl);
|
|
823
|
+
if (!adminUrl && !storeUrl) {
|
|
824
|
+
return null;
|
|
825
|
+
}
|
|
826
|
+
return {
|
|
827
|
+
...adminUrl ? { adminUrl } : {},
|
|
828
|
+
...storeUrl ? { storeUrl } : {}
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
function normalizeExternalAdminLinkEntries(links) {
|
|
832
|
+
if (!Array.isArray(links)) {
|
|
833
|
+
return [];
|
|
834
|
+
}
|
|
835
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
836
|
+
const out = [];
|
|
837
|
+
for (const raw of links) {
|
|
838
|
+
if (!raw || typeof raw !== "object" || out.length >= MAX_EXTERNAL_ADMIN_LINKS) {
|
|
839
|
+
continue;
|
|
840
|
+
}
|
|
841
|
+
const id = typeof raw.id === "string" ? raw.id.trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "") : "";
|
|
842
|
+
const label = typeof raw.label === "string" ? raw.label.trim().slice(0, MAX_EXTERNAL_ADMIN_LINK_LABEL_LENGTH) : "";
|
|
843
|
+
const url = tryNormalizeHttpUrl(raw.url);
|
|
844
|
+
if (!id || !label || !url || !EXTERNAL_ADMIN_LINK_ID_PATTERN.test(id) || seenIds.has(id)) {
|
|
845
|
+
continue;
|
|
846
|
+
}
|
|
847
|
+
seenIds.add(id);
|
|
848
|
+
out.push({ id, label, url });
|
|
849
|
+
}
|
|
850
|
+
return out;
|
|
851
|
+
}
|
|
852
|
+
function normalizeExternalSources(externalSources) {
|
|
853
|
+
if (!externalSources || typeof externalSources !== "object") {
|
|
854
|
+
return null;
|
|
855
|
+
}
|
|
856
|
+
const studioUrl = tryNormalizeHttpUrl(externalSources.sanity?.studioUrl);
|
|
857
|
+
const shopify = normalizeShopifyExternalSource(externalSources.shopify);
|
|
858
|
+
const links = normalizeExternalAdminLinkEntries(externalSources.links);
|
|
859
|
+
if (!studioUrl && !shopify && links.length === 0) {
|
|
860
|
+
return null;
|
|
861
|
+
}
|
|
862
|
+
return {
|
|
863
|
+
...studioUrl ? { sanity: { studioUrl } } : {},
|
|
864
|
+
...shopify ? { shopify } : {},
|
|
865
|
+
...links.length > 0 ? { links } : {}
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
function normalizeJmpContentManifest(input) {
|
|
869
|
+
if (!input || typeof input !== "object") {
|
|
870
|
+
return null;
|
|
871
|
+
}
|
|
872
|
+
const routes = normalizeRoutes(input.routes);
|
|
873
|
+
const externalSources = normalizeExternalSources(input.externalSources);
|
|
874
|
+
const trust = normalizeTrust(input.trust);
|
|
875
|
+
const contact = normalizeContact(input.contact);
|
|
876
|
+
if (routes.length === 0 && !externalSources && !trust && !contact) {
|
|
877
|
+
return null;
|
|
878
|
+
}
|
|
879
|
+
return {
|
|
880
|
+
version: JMP_CONTENT_MANIFEST_VERSION,
|
|
881
|
+
...routes.length > 0 ? { routes } : {},
|
|
882
|
+
...externalSources ? { externalSources } : {},
|
|
883
|
+
...trust ? { trust } : {},
|
|
884
|
+
...contact ? { contact } : {}
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
function normalizeRoutes(routes) {
|
|
888
|
+
if (!Array.isArray(routes)) {
|
|
889
|
+
return [];
|
|
890
|
+
}
|
|
891
|
+
const seen = /* @__PURE__ */ new Set();
|
|
892
|
+
const out = [];
|
|
893
|
+
for (const raw of routes) {
|
|
894
|
+
if (!raw || typeof raw !== "object") continue;
|
|
895
|
+
const wrapper = typeof raw.wrapper === "string" ? raw.wrapper.trim().toLowerCase() : "";
|
|
896
|
+
const pattern = typeof raw.pattern === "string" ? raw.pattern.trim() : "";
|
|
897
|
+
if (!wrapper || !pattern) continue;
|
|
898
|
+
const dedupe = `${wrapper}:${pattern}`;
|
|
899
|
+
if (seen.has(dedupe)) continue;
|
|
900
|
+
seen.add(dedupe);
|
|
901
|
+
out.push({
|
|
902
|
+
wrapper,
|
|
903
|
+
pattern,
|
|
904
|
+
...typeof raw.index === "string" && raw.index.trim() ? { index: raw.index.trim() } : {},
|
|
905
|
+
...typeof raw.label === "string" && raw.label.trim() ? { label: raw.label.trim() } : {}
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
return out;
|
|
909
|
+
}
|
|
910
|
+
function normalizeTrust(trust) {
|
|
911
|
+
if (!trust || typeof trust !== "object") return null;
|
|
912
|
+
const blocks = normalizeTrustBlocks(trust.blocks);
|
|
913
|
+
const labels = normalizeTrustLabels(trust.labels);
|
|
914
|
+
const mergeTestimonialsReviews = trust.mergeTestimonialsReviews === true;
|
|
915
|
+
if (blocks.length === 0 && !labels && !mergeTestimonialsReviews) return null;
|
|
916
|
+
return {
|
|
917
|
+
...blocks.length > 0 ? { blocks } : {},
|
|
918
|
+
...labels ? { labels } : {},
|
|
919
|
+
...mergeTestimonialsReviews ? { mergeTestimonialsReviews: true } : {}
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
function normalizeTrustBlocks(blocks) {
|
|
923
|
+
if (!Array.isArray(blocks)) return [];
|
|
924
|
+
const seen = /* @__PURE__ */ new Set();
|
|
925
|
+
const out = [];
|
|
926
|
+
for (const raw of blocks) {
|
|
927
|
+
if (!raw || typeof raw !== "object") continue;
|
|
928
|
+
const section = typeof raw.section === "string" ? raw.section.trim().toLowerCase() : "";
|
|
929
|
+
if (!TRUST_SECTIONS.has(section) || seen.has(section)) continue;
|
|
930
|
+
seen.add(section);
|
|
931
|
+
out.push({
|
|
932
|
+
section,
|
|
933
|
+
...typeof raw.label === "string" && raw.label.trim() ? { label: raw.label.trim() } : {}
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
return out;
|
|
937
|
+
}
|
|
938
|
+
function normalizeTrustLabels(labels) {
|
|
939
|
+
if (!labels || typeof labels !== "object") return null;
|
|
940
|
+
const out = {};
|
|
941
|
+
for (const [key, value] of Object.entries(labels)) {
|
|
942
|
+
if (TRUST_SECTIONS.has(key) && typeof value === "string" && value.trim()) {
|
|
943
|
+
out[key] = value.trim();
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
947
|
+
}
|
|
948
|
+
function normalizeContact(contact) {
|
|
949
|
+
if (!contact || typeof contact !== "object") return null;
|
|
950
|
+
const extras = normalizeContactExtras(contact.extras);
|
|
951
|
+
return extras.length > 0 ? { extras } : null;
|
|
952
|
+
}
|
|
953
|
+
function normalizeContactExtras(extras) {
|
|
954
|
+
if (!Array.isArray(extras)) return [];
|
|
955
|
+
const seen = /* @__PURE__ */ new Set();
|
|
956
|
+
const out = [];
|
|
957
|
+
for (const raw of extras) {
|
|
958
|
+
if (!raw || typeof raw !== "object") continue;
|
|
959
|
+
const label = typeof raw.label === "string" ? raw.label.trim() : "";
|
|
960
|
+
const key = normalizeKey(
|
|
961
|
+
typeof raw.key === "string" && raw.key.trim() ? raw.key : label
|
|
962
|
+
);
|
|
963
|
+
if (!key || !label || !isValidExtraKey(key) || seen.has(key)) continue;
|
|
964
|
+
seen.add(key);
|
|
965
|
+
out.push({ key, label });
|
|
966
|
+
}
|
|
967
|
+
return out;
|
|
968
|
+
}
|
|
969
|
+
function stableJmpManifestFingerprint(manifest) {
|
|
970
|
+
return manifest ? JSON.stringify(manifest) : "";
|
|
971
|
+
}
|
|
972
|
+
function inferWrapperSlotsFromManifest(manifest) {
|
|
973
|
+
const slots = /* @__PURE__ */ new Set();
|
|
974
|
+
for (const route of manifest?.routes ?? []) {
|
|
975
|
+
slots.add(route.wrapper);
|
|
976
|
+
}
|
|
977
|
+
for (const block of manifest?.trust?.blocks ?? []) {
|
|
978
|
+
slots.add(`trust.${block.section}`);
|
|
979
|
+
slots.add("trust");
|
|
980
|
+
}
|
|
981
|
+
if (manifest?.contact?.extras?.length) {
|
|
982
|
+
slots.add("contact");
|
|
983
|
+
}
|
|
984
|
+
return [...slots];
|
|
985
|
+
}
|
|
986
|
+
|
|
756
987
|
// src/content/wrapper-discovery.ts
|
|
757
988
|
var WRAPPER_SLOT_ORDER = [
|
|
758
989
|
"blog",
|
|
@@ -772,16 +1003,22 @@ var syncHandler = null;
|
|
|
772
1003
|
function getDiscoveredContentWrappers() {
|
|
773
1004
|
return sortWrapperSlots([...discovered]);
|
|
774
1005
|
}
|
|
1006
|
+
function resolveContentManifestForReport(config) {
|
|
1007
|
+
return normalizeJmpContentManifest(config.contentManifest);
|
|
1008
|
+
}
|
|
775
1009
|
function resolveContentWrappersForReport(config) {
|
|
776
1010
|
const explicit = config.contentWrappers ?? [];
|
|
777
1011
|
const auto = getDiscoveredContentWrappers();
|
|
1012
|
+
const fromManifest = inferWrapperSlotsFromManifest(
|
|
1013
|
+
resolveContentManifestForReport(config)
|
|
1014
|
+
);
|
|
778
1015
|
if (config.contentWrapperMode === "manual") {
|
|
779
|
-
return sortWrapperSlots([...explicit]);
|
|
1016
|
+
return sortWrapperSlots([...explicit, ...fromManifest]);
|
|
780
1017
|
}
|
|
781
1018
|
if (config.contentWrapperMode === "explicit-only") {
|
|
782
|
-
return sortWrapperSlots([...explicit]);
|
|
1019
|
+
return sortWrapperSlots([...explicit, ...fromManifest]);
|
|
783
1020
|
}
|
|
784
|
-
const merged = /* @__PURE__ */ new Set([...explicit, ...auto]);
|
|
1021
|
+
const merged = /* @__PURE__ */ new Set([...explicit, ...auto, ...fromManifest]);
|
|
785
1022
|
return sortWrapperSlots([...merged]);
|
|
786
1023
|
}
|
|
787
1024
|
function hasReportedContentWrappers(dedupeKey) {
|
|
@@ -811,7 +1048,8 @@ async function registerJmpContentWrappers(config) {
|
|
|
811
1048
|
if (!wrappers.length) {
|
|
812
1049
|
return { ok: false, skipped: true };
|
|
813
1050
|
}
|
|
814
|
-
const
|
|
1051
|
+
const manifest = resolveContentManifestForReport(config);
|
|
1052
|
+
const dedupeKey = `${config.trackingKey}:${[...wrappers].sort().join(",")}:${stableJmpManifestFingerprint(manifest)}`;
|
|
815
1053
|
if (hasReportedContentWrappers(dedupeKey)) {
|
|
816
1054
|
return { ok: true, wrappers };
|
|
817
1055
|
}
|
|
@@ -824,7 +1062,17 @@ async function registerJmpContentWrappers(config) {
|
|
|
824
1062
|
}
|
|
825
1063
|
const url = `${baseUrl}/api/content/capabilities`;
|
|
826
1064
|
if (config.debug) {
|
|
827
|
-
console.debug("[@jmp-technologies/analytics]", "POST", url, {
|
|
1065
|
+
console.debug("[@jmp-technologies/analytics]", "POST", url, {
|
|
1066
|
+
wrappers,
|
|
1067
|
+
manifest
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
const body = {
|
|
1071
|
+
wrappers,
|
|
1072
|
+
sdkVersion: JMP_ANALYTICS_SDK_VERSION
|
|
1073
|
+
};
|
|
1074
|
+
if (manifest) {
|
|
1075
|
+
body.manifest = manifest;
|
|
828
1076
|
}
|
|
829
1077
|
const res = await resolvedFetch(url, {
|
|
830
1078
|
method: "POST",
|
|
@@ -832,10 +1080,7 @@ async function registerJmpContentWrappers(config) {
|
|
|
832
1080
|
"Content-Type": "application/json",
|
|
833
1081
|
"X-JMP-Tracking-Key": config.trackingKey
|
|
834
1082
|
},
|
|
835
|
-
body: JSON.stringify(
|
|
836
|
-
wrappers,
|
|
837
|
-
sdkVersion: JMP_ANALYTICS_SDK_VERSION
|
|
838
|
-
})
|
|
1083
|
+
body: JSON.stringify(body)
|
|
839
1084
|
});
|
|
840
1085
|
const text = await res.text();
|
|
841
1086
|
if (!res.ok) {
|
|
@@ -937,13 +1182,21 @@ var import_jsx_runtime = require("react/jsx-runtime");
|
|
|
937
1182
|
function useJmpAnalytics() {
|
|
938
1183
|
return (0, import_react.useMemo)(() => getOrCreateJmpAnalyticsFromBrowserEnv(), []);
|
|
939
1184
|
}
|
|
940
|
-
function
|
|
1185
|
+
function trackCallClickFromHref(href) {
|
|
941
1186
|
const path = typeof window !== "undefined" ? window.location.pathname : "/";
|
|
942
|
-
|
|
1187
|
+
const contactTarget = extractTelTarget(href);
|
|
1188
|
+
void getOrCreateJmpAnalyticsFromBrowserEnv()?.trackCallClick(
|
|
1189
|
+
path,
|
|
1190
|
+
contactTarget ? { contactTarget } : void 0
|
|
1191
|
+
);
|
|
943
1192
|
}
|
|
944
|
-
function
|
|
1193
|
+
function trackEmailClickFromHref(href) {
|
|
945
1194
|
const path = typeof window !== "undefined" ? window.location.pathname : "/";
|
|
946
|
-
|
|
1195
|
+
const contactTarget = extractMailtoTarget(href);
|
|
1196
|
+
void getOrCreateJmpAnalyticsFromBrowserEnv()?.trackEmailClick(
|
|
1197
|
+
path,
|
|
1198
|
+
contactTarget ? { contactTarget } : void 0
|
|
1199
|
+
);
|
|
947
1200
|
}
|
|
948
1201
|
function JmpTrackedTelLink({
|
|
949
1202
|
href,
|
|
@@ -957,7 +1210,7 @@ function JmpTrackedTelLink({
|
|
|
957
1210
|
"data-jmp-tracked": "true",
|
|
958
1211
|
...rest,
|
|
959
1212
|
onClick: (e) => {
|
|
960
|
-
|
|
1213
|
+
trackCallClickFromHref(href);
|
|
961
1214
|
onClick?.(e);
|
|
962
1215
|
}
|
|
963
1216
|
}
|
|
@@ -975,7 +1228,7 @@ function JmpTrackedMailtoLink({
|
|
|
975
1228
|
"data-jmp-tracked": "true",
|
|
976
1229
|
...rest,
|
|
977
1230
|
onClick: (e) => {
|
|
978
|
-
|
|
1231
|
+
trackEmailClickFromHref(href);
|
|
979
1232
|
onClick?.(e);
|
|
980
1233
|
}
|
|
981
1234
|
}
|