@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/next/landing-capture-script-server.tsx"],"sourcesContent":["import { buildLandingCaptureInlineScript } from \"../landing-capture\"\n\ntype JmpLandingCaptureScriptServerProps = {\n trackingKey?: string\n}\n\n/**\n * SSR-only landing referrer / UTM capture. Render in root layout `<head>`.\n *\n * Do not use `next/script` or a client component — React 19 will warn and\n * deferred client mounts miss the first referrer anyway.\n */\nexport function JmpLandingCaptureScriptServer({\n trackingKey,\n}: JmpLandingCaptureScriptServerProps = {}) {\n if (!trackingKey?.trim()) {\n return null\n }\n\n return (\n <script\n dangerouslySetInnerHTML={{\n __html: buildLandingCaptureInlineScript(),\n }}\n id=\"jmp-landing-capture\"\n
|
|
1
|
+
{"version":3,"sources":["../src/next/landing-capture-script-server.tsx"],"sourcesContent":["import { buildLandingCaptureInlineScript } from \"../landing-capture\"\n\ntype JmpLandingCaptureScriptServerProps = {\n trackingKey?: string\n}\n\n/**\n * SSR-only landing referrer / UTM capture. Render in root layout `<head>`.\n *\n * Do not use `next/script` or a client component — React 19 will warn and\n * deferred client mounts miss the first referrer anyway.\n */\nexport function JmpLandingCaptureScriptServer({\n trackingKey,\n}: JmpLandingCaptureScriptServerProps = {}) {\n if (!trackingKey?.trim()) {\n return null\n }\n\n return (\n <script\n dangerouslySetInnerHTML={{\n __html: buildLandingCaptureInlineScript(),\n }}\n id=\"jmp-landing-capture\"\n />\n )\n}\n"],"mappings":";;;;;AAoBI;AARG,SAAS,8BAA8B;AAAA,EAC5C;AACF,IAAwC,CAAC,GAAG;AAC1C,MAAI,CAAC,aAAa,KAAK,GAAG;AACxB,WAAO;AAAA,EACT;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,yBAAyB;AAAA,QACvB,QAAQ,gCAAgC;AAAA,MAC1C;AAAA,MACA,IAAG;AAAA;AAAA,EACL;AAEJ;","names":[]}
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
getHeader,
|
|
3
3
|
getJmpContentConfig,
|
|
4
4
|
trackJmpServerPageView
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-AIFJ7DVO.js";
|
|
6
6
|
|
|
7
7
|
// src/server/should-track-server-page-view.ts
|
|
8
8
|
function shouldSkipServerPageViewRequest(request) {
|
|
@@ -56,4 +56,4 @@ async function jmpTrackServerPageView(request) {
|
|
|
56
56
|
export {
|
|
57
57
|
jmpTrackServerPageView
|
|
58
58
|
};
|
|
59
|
-
//# sourceMappingURL=chunk-
|
|
59
|
+
//# sourceMappingURL=chunk-HFLU7HQE.js.map
|
|
@@ -5,13 +5,15 @@ import {
|
|
|
5
5
|
getJmpLandingPageDetail,
|
|
6
6
|
getJmpLandingPageSitemap,
|
|
7
7
|
getJmpLandingPages,
|
|
8
|
+
getJmpLocationDetail,
|
|
9
|
+
getJmpLocations,
|
|
8
10
|
getJmpSiteProfile,
|
|
9
11
|
getJmpTrustBundle
|
|
10
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-NI7R6QZQ.js";
|
|
11
13
|
import {
|
|
12
14
|
getJmpContentConfig,
|
|
13
15
|
getJmpSiteDefinition
|
|
14
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-AIFJ7DVO.js";
|
|
15
17
|
|
|
16
18
|
// src/content/site-content.ts
|
|
17
19
|
var DEFAULT_EXTRA_KEYS = {
|
|
@@ -19,7 +21,8 @@ var DEFAULT_EXTRA_KEYS = {
|
|
|
19
21
|
dashboardDemoUrl: ["dashboard-demo-url", "sample-dashboard-url"],
|
|
20
22
|
profilePhotoSrc: ["profile-photo-url", "profile_photo_url"],
|
|
21
23
|
hours: ["hours", "business-hours"],
|
|
22
|
-
serviceArea: ["service-area", "service_area", "service-area-text"]
|
|
24
|
+
serviceArea: ["service-area", "service_area", "service-area-text"],
|
|
25
|
+
googleMapsEmbedUrl: ["google-maps-embed-url", "google_maps_embed_url"]
|
|
23
26
|
};
|
|
24
27
|
function sortBlogPosts(posts) {
|
|
25
28
|
return [...posts].sort((a, b) => {
|
|
@@ -70,7 +73,13 @@ function resolveSiteContact(profile) {
|
|
|
70
73
|
...site?.contactExtraKeys
|
|
71
74
|
};
|
|
72
75
|
const email = profile?.email?.trim() || fallback.email || "";
|
|
73
|
-
const
|
|
76
|
+
const social = {
|
|
77
|
+
linkedin: profile?.social?.linkedin?.trim() || fallback.linkedin || null,
|
|
78
|
+
instagram: profile?.social?.instagram?.trim() || fallback.instagram || null,
|
|
79
|
+
facebook: profile?.social?.facebook?.trim() || fallback.facebook || null,
|
|
80
|
+
x: profile?.social?.x?.trim() || fallback.x || null,
|
|
81
|
+
youtube: profile?.social?.youtube?.trim() || fallback.youtube || null
|
|
82
|
+
};
|
|
74
83
|
const phone = profile?.phone?.trim() || fallback.phone || null;
|
|
75
84
|
return {
|
|
76
85
|
displayName: profile?.display_name?.trim() || profile?.name?.trim() || fallback.displayName || "",
|
|
@@ -78,8 +87,12 @@ function resolveSiteContact(profile) {
|
|
|
78
87
|
dashboardDemoUrl: readProfileExtra(profile, extraKeys.dashboardDemoUrl ?? []) || fallback.dashboardDemoUrl || "",
|
|
79
88
|
profilePhotoSrc: readProfileExtra(profile, extraKeys.profilePhotoSrc ?? []) || fallback.profilePhotoSrc || "",
|
|
80
89
|
email,
|
|
81
|
-
linkedin,
|
|
90
|
+
linkedin: social.linkedin,
|
|
91
|
+
social,
|
|
82
92
|
phone,
|
|
93
|
+
address: profile?.address?.trim() || fallback.address || null,
|
|
94
|
+
googleMapsUrl: profile?.google_maps_url?.trim() || fallback.googleMapsUrl || null,
|
|
95
|
+
googleMapsEmbedUrl: readProfileExtra(profile, extraKeys.googleMapsEmbedUrl ?? []) || fallback.googleMapsEmbedUrl || null,
|
|
83
96
|
hours: readProfileExtra(profile, extraKeys.hours ?? []) || fallback.hours || null,
|
|
84
97
|
serviceArea: readProfileExtra(profile, extraKeys.serviceArea ?? []) || fallback.serviceArea || null,
|
|
85
98
|
privacyUrl: profile?.legal?.privacy_policy_url?.trim() || fallback.privacyUrl || "/privacy",
|
|
@@ -94,6 +107,15 @@ async function getResolvedSiteContact() {
|
|
|
94
107
|
const profile = await loadSiteProfile();
|
|
95
108
|
return resolveSiteContact(profile);
|
|
96
109
|
}
|
|
110
|
+
function listResolvedSocialUrls(social) {
|
|
111
|
+
return [
|
|
112
|
+
social.linkedin,
|
|
113
|
+
social.instagram,
|
|
114
|
+
social.facebook,
|
|
115
|
+
social.x,
|
|
116
|
+
social.youtube
|
|
117
|
+
].filter((url) => Boolean(url));
|
|
118
|
+
}
|
|
97
119
|
async function loadBlogPosts() {
|
|
98
120
|
const c = getJmpContentConfig();
|
|
99
121
|
if (!c) {
|
|
@@ -170,6 +192,28 @@ async function loadLandingPageSitemap() {
|
|
|
170
192
|
return [];
|
|
171
193
|
}
|
|
172
194
|
}
|
|
195
|
+
async function loadLocations() {
|
|
196
|
+
const c = getJmpContentConfig();
|
|
197
|
+
if (!c) {
|
|
198
|
+
return [];
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
return await getJmpLocations(c);
|
|
202
|
+
} catch {
|
|
203
|
+
return [];
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
async function loadLocationDetail(slug) {
|
|
207
|
+
const c = getJmpContentConfig();
|
|
208
|
+
if (!c) {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
try {
|
|
212
|
+
return await getJmpLocationDetail(c, slug);
|
|
213
|
+
} catch {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
173
217
|
async function loadTrustBundle() {
|
|
174
218
|
const c = getJmpContentConfig();
|
|
175
219
|
if (!c) {
|
|
@@ -199,6 +243,7 @@ export {
|
|
|
199
243
|
loadSiteProfile,
|
|
200
244
|
resolveSiteContact,
|
|
201
245
|
getResolvedSiteContact,
|
|
246
|
+
listResolvedSocialUrls,
|
|
202
247
|
loadBlogPosts,
|
|
203
248
|
loadBlogPost,
|
|
204
249
|
loadFaqs,
|
|
@@ -206,7 +251,9 @@ export {
|
|
|
206
251
|
loadLandingPages,
|
|
207
252
|
loadLandingPageDetail,
|
|
208
253
|
loadLandingPageSitemap,
|
|
254
|
+
loadLocations,
|
|
255
|
+
loadLocationDetail,
|
|
209
256
|
loadTrustBundle,
|
|
210
257
|
filterFeaturedTrustQuotes
|
|
211
258
|
};
|
|
212
|
-
//# sourceMappingURL=chunk-
|
|
259
|
+
//# sourceMappingURL=chunk-HKMDWTA2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/content/site-content.ts"],"sourcesContent":["import {\n getJmpBlogPost,\n getJmpBlogPosts,\n getJmpFaqs,\n getJmpLandingPageDetail,\n getJmpLandingPageSitemap,\n getJmpLandingPages,\n getJmpLocations,\n getJmpLocationDetail,\n getJmpSiteProfile,\n getJmpTrustBundle,\n type JmpBlogPost,\n type JmpFaqItem,\n type JmpLandingPage,\n type JmpLandingPageDetail,\n type JmpLandingPageKind,\n type JmpLandingPageSitemapEntry,\n type JmpWebsiteLocation,\n type JmpWebsiteLocationDetail,\n type JmpSiteProfile,\n type JmpTestimonial,\n type JmpTrustBundle,\n} from \"../index\"\nimport {\n getJmpContentConfig,\n getJmpSiteDefinition,\n type JmpSiteSocialLinks,\n} from \"../site-config\"\n\nexport type {\n JmpBlogPost,\n JmpFaqItem,\n JmpLandingPage,\n JmpLandingPageDetail,\n JmpLandingPageKind,\n JmpLandingPageSitemapEntry,\n JmpWebsiteLocation,\n JmpWebsiteLocationDetail,\n JmpSiteProfile,\n JmpTestimonial,\n JmpTrustBundle,\n}\n\nexport { getJmpContentConfig } from \"../site-config\"\n\nexport type { JmpSiteSocialLinks } from \"../site-config\"\n\nexport type ResolvedSiteContact = {\n displayName: string\n email: string\n founderName: string\n dashboardDemoUrl: string\n profilePhotoSrc: string\n linkedin: string | null\n social: JmpSiteSocialLinks\n phone: string | null\n address: string | null\n googleMapsUrl: string | null\n googleMapsEmbedUrl: string | null\n hours: string | null\n serviceArea: string | null\n privacyUrl: string\n termsUrl: string\n cta: { label: string | null; url: string | null }\n}\n\nconst DEFAULT_EXTRA_KEYS = {\n founderName: [\"founder-name\", \"founder_name\"],\n dashboardDemoUrl: [\"dashboard-demo-url\", \"sample-dashboard-url\"],\n profilePhotoSrc: [\"profile-photo-url\", \"profile_photo_url\"],\n hours: [\"hours\", \"business-hours\"],\n serviceArea: [\"service-area\", \"service_area\", \"service-area-text\"],\n googleMapsEmbedUrl: [\"google-maps-embed-url\", \"google_maps_embed_url\"],\n} as const\n\nfunction sortBlogPosts(posts: JmpBlogPost[]): JmpBlogPost[] {\n return [...posts].sort((a, b) => {\n const da = a.published_at ?? a.updated_at\n const db = b.published_at ?? b.updated_at\n return db.localeCompare(da)\n })\n}\n\nfunction sortFaqs(items: JmpFaqItem[]): JmpFaqItem[] {\n return [...items].sort((a, b) => a.sort_order - b.sort_order)\n}\n\nfunction readProfileExtra(\n profile: JmpSiteProfile | null,\n keys: readonly string[],\n): string | null {\n for (const key of keys) {\n const value = profile?.extras?.[key]?.trim()\n if (value) {\n return value\n }\n }\n return null\n}\n\n/** Resolve a path or URL against {@link defineJmpSite}'s `siteUrl`. */\nexport function absoluteSiteAssetUrl(src: string): string {\n const trimmed = src.trim()\n if (trimmed.startsWith(\"http://\") || trimmed.startsWith(\"https://\")) {\n return trimmed\n }\n const siteUrl = getJmpSiteDefinition()?.siteUrl?.replace(/\\/$/, \"\")\n if (!siteUrl) {\n return trimmed.startsWith(\"/\") ? trimmed : `/${trimmed}`\n }\n return `${siteUrl}${trimmed.startsWith(\"/\") ? trimmed : `/${trimmed}`}`\n}\n\nexport async function loadSiteProfile(): Promise<JmpSiteProfile | null> {\n const config = getJmpContentConfig()\n if (!config) {\n return null\n }\n try {\n return await getJmpSiteProfile(config)\n } catch {\n return null\n }\n}\n\nexport function resolveSiteContact(profile: JmpSiteProfile | null): ResolvedSiteContact {\n const site = getJmpSiteDefinition()\n const fallback = site?.contactFallback ?? {}\n const extraKeys = {\n ...DEFAULT_EXTRA_KEYS,\n ...site?.contactExtraKeys,\n }\n\n const email = profile?.email?.trim() || fallback.email || \"\"\n const social: JmpSiteSocialLinks = {\n linkedin:\n profile?.social?.linkedin?.trim() || fallback.linkedin || null,\n instagram:\n profile?.social?.instagram?.trim() || fallback.instagram || null,\n facebook:\n profile?.social?.facebook?.trim() || fallback.facebook || null,\n x: profile?.social?.x?.trim() || fallback.x || null,\n youtube:\n profile?.social?.youtube?.trim() || fallback.youtube || null,\n }\n const phone = profile?.phone?.trim() || fallback.phone || null\n\n return {\n displayName:\n profile?.display_name?.trim() ||\n profile?.name?.trim() ||\n fallback.displayName ||\n \"\",\n founderName:\n readProfileExtra(profile, extraKeys.founderName ?? []) ||\n fallback.founderName ||\n \"\",\n dashboardDemoUrl:\n readProfileExtra(profile, extraKeys.dashboardDemoUrl ?? []) ||\n fallback.dashboardDemoUrl ||\n \"\",\n profilePhotoSrc:\n readProfileExtra(profile, extraKeys.profilePhotoSrc ?? []) ||\n fallback.profilePhotoSrc ||\n \"\",\n email,\n linkedin: social.linkedin,\n social,\n phone,\n address: profile?.address?.trim() || fallback.address || null,\n googleMapsUrl:\n profile?.google_maps_url?.trim() || fallback.googleMapsUrl || null,\n googleMapsEmbedUrl:\n readProfileExtra(profile, extraKeys.googleMapsEmbedUrl ?? []) ||\n fallback.googleMapsEmbedUrl ||\n null,\n hours:\n readProfileExtra(profile, extraKeys.hours ?? []) || fallback.hours || null,\n serviceArea:\n readProfileExtra(profile, extraKeys.serviceArea ?? []) ||\n fallback.serviceArea ||\n null,\n privacyUrl: profile?.legal?.privacy_policy_url?.trim() || fallback.privacyUrl || \"/privacy\",\n termsUrl: profile?.legal?.terms_url?.trim() || fallback.termsUrl || \"/terms\",\n cta: {\n label: profile?.cta?.label?.trim() || null,\n url: profile?.cta?.url?.trim() || null,\n },\n }\n}\n\nexport async function getResolvedSiteContact(): Promise<ResolvedSiteContact> {\n const profile = await loadSiteProfile()\n return resolveSiteContact(profile)\n}\n\n/** Non-null social profile URLs in a stable order for JSON-LD `sameAs`. */\nexport function listResolvedSocialUrls(social: JmpSiteSocialLinks): string[] {\n return [\n social.linkedin,\n social.instagram,\n social.facebook,\n social.x,\n social.youtube,\n ].filter((url): url is string => Boolean(url))\n}\n\nexport async function loadBlogPosts(): Promise<JmpBlogPost[]> {\n const c = getJmpContentConfig()\n if (!c) {\n return []\n }\n try {\n const posts = await getJmpBlogPosts(c)\n return sortBlogPosts(posts)\n } catch {\n return []\n }\n}\n\nexport async function loadBlogPost(slug: string): Promise<JmpBlogPost | null> {\n const c = getJmpContentConfig()\n if (!c) {\n return null\n }\n try {\n return await getJmpBlogPost(c, slug)\n } catch {\n return null\n }\n}\n\nexport async function loadFaqs(): Promise<JmpFaqItem[]> {\n const c = getJmpContentConfig()\n if (!c) {\n return []\n }\n try {\n const faqs = await getJmpFaqs(c)\n return sortFaqs(faqs)\n } catch {\n return []\n }\n}\n\nexport async function loadFaqBySlug(slug: string): Promise<JmpFaqItem | null> {\n const normalized = slug.trim()\n if (!normalized) {\n return null\n }\n const items = await loadFaqs()\n return (\n items.find((item) => item.slug === normalized || item.id === normalized) ?? null\n )\n}\n\nexport async function loadLandingPages(filters?: {\n kind?: JmpLandingPageKind\n locale?: string\n}): Promise<JmpLandingPage[]> {\n const c = getJmpContentConfig()\n if (!c) {\n return []\n }\n try {\n return await getJmpLandingPages(c, filters)\n } catch {\n return []\n }\n}\n\nexport async function loadLandingPageDetail(\n kind: JmpLandingPageKind,\n slug: string,\n locale?: string\n): Promise<JmpLandingPageDetail | null> {\n const c = getJmpContentConfig()\n if (!c) {\n return null\n }\n try {\n return await getJmpLandingPageDetail(c, kind, slug, locale)\n } catch {\n return null\n }\n}\n\nexport async function loadLandingPageSitemap(): Promise<JmpLandingPageSitemapEntry[]> {\n const c = getJmpContentConfig()\n if (!c) {\n return []\n }\n try {\n return await getJmpLandingPageSitemap(c)\n } catch {\n return []\n }\n}\n\nexport async function loadLocations(): Promise<JmpWebsiteLocation[]> {\n const c = getJmpContentConfig()\n if (!c) {\n return []\n }\n try {\n return await getJmpLocations(c)\n } catch {\n return []\n }\n}\n\nexport async function loadLocationDetail(\n slug: string\n): Promise<JmpWebsiteLocationDetail | null> {\n const c = getJmpContentConfig()\n if (!c) {\n return null\n }\n try {\n return await getJmpLocationDetail(c, slug)\n } catch {\n return null\n }\n}\n\nexport async function loadTrustBundle(): Promise<JmpTrustBundle | null> {\n const c = getJmpContentConfig()\n if (!c) {\n return null\n }\n try {\n return await getJmpTrustBundle(c)\n } catch {\n return null\n }\n}\n\nexport function filterFeaturedTrustQuotes(\n testimonials: JmpTestimonial[],\n placement: \"testimonials\" | \"reviews\",\n limit = 2,\n): JmpTestimonial[] {\n return testimonials\n .filter((item) => {\n if (!item.featured) {\n return false\n }\n const bucket = item.placement?.trim().toLowerCase()\n if (placement === \"reviews\") {\n return bucket === \"reviews\"\n }\n return !bucket || bucket === \"testimonials\"\n })\n .sort((a, b) => a.sort_order - b.sort_order)\n .slice(0, limit)\n}\n"],"mappings":"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","names":[]}
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
getOrCreateJmpAnalyticsFromBrowserEnv,
|
|
3
3
|
isJmpTrackingConsentGranted,
|
|
4
4
|
subscribeJmpTrackingConsent
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-NI7R6QZQ.js";
|
|
6
6
|
import {
|
|
7
7
|
readJmpPublicEnvFromProcess
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-AIFJ7DVO.js";
|
|
9
9
|
|
|
10
10
|
// src/next.tsx
|
|
11
11
|
import {
|
|
@@ -130,4 +130,4 @@ export {
|
|
|
130
130
|
JmpNextAnalytics,
|
|
131
131
|
JmpSiteRoot
|
|
132
132
|
};
|
|
133
|
-
//# sourceMappingURL=chunk-
|
|
133
|
+
//# sourceMappingURL=chunk-KPTMZQCY.js.map
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
+
extractMailtoTarget,
|
|
3
|
+
extractTelTarget,
|
|
2
4
|
getOrCreateJmpAnalyticsFromBrowserEnv
|
|
3
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-NI7R6QZQ.js";
|
|
4
6
|
|
|
5
7
|
// src/react.tsx
|
|
6
8
|
import { useMemo } from "react";
|
|
@@ -8,13 +10,21 @@ import { jsx } from "react/jsx-runtime";
|
|
|
8
10
|
function useJmpAnalytics() {
|
|
9
11
|
return useMemo(() => getOrCreateJmpAnalyticsFromBrowserEnv(), []);
|
|
10
12
|
}
|
|
11
|
-
function
|
|
13
|
+
function trackCallClickFromHref(href) {
|
|
12
14
|
const path = typeof window !== "undefined" ? window.location.pathname : "/";
|
|
13
|
-
|
|
15
|
+
const contactTarget = extractTelTarget(href);
|
|
16
|
+
void getOrCreateJmpAnalyticsFromBrowserEnv()?.trackCallClick(
|
|
17
|
+
path,
|
|
18
|
+
contactTarget ? { contactTarget } : void 0
|
|
19
|
+
);
|
|
14
20
|
}
|
|
15
|
-
function
|
|
21
|
+
function trackEmailClickFromHref(href) {
|
|
16
22
|
const path = typeof window !== "undefined" ? window.location.pathname : "/";
|
|
17
|
-
|
|
23
|
+
const contactTarget = extractMailtoTarget(href);
|
|
24
|
+
void getOrCreateJmpAnalyticsFromBrowserEnv()?.trackEmailClick(
|
|
25
|
+
path,
|
|
26
|
+
contactTarget ? { contactTarget } : void 0
|
|
27
|
+
);
|
|
18
28
|
}
|
|
19
29
|
function JmpTrackedTelLink({
|
|
20
30
|
href,
|
|
@@ -28,7 +38,7 @@ function JmpTrackedTelLink({
|
|
|
28
38
|
"data-jmp-tracked": "true",
|
|
29
39
|
...rest,
|
|
30
40
|
onClick: (e) => {
|
|
31
|
-
|
|
41
|
+
trackCallClickFromHref(href);
|
|
32
42
|
onClick?.(e);
|
|
33
43
|
}
|
|
34
44
|
}
|
|
@@ -46,7 +56,7 @@ function JmpTrackedMailtoLink({
|
|
|
46
56
|
"data-jmp-tracked": "true",
|
|
47
57
|
...rest,
|
|
48
58
|
onClick: (e) => {
|
|
49
|
-
|
|
59
|
+
trackEmailClickFromHref(href);
|
|
50
60
|
onClick?.(e);
|
|
51
61
|
}
|
|
52
62
|
}
|
|
@@ -58,4 +68,4 @@ export {
|
|
|
58
68
|
JmpTrackedTelLink,
|
|
59
69
|
JmpTrackedMailtoLink
|
|
60
70
|
};
|
|
61
|
-
//# sourceMappingURL=chunk-
|
|
71
|
+
//# sourceMappingURL=chunk-NDSPSAWQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react.tsx"],"sourcesContent":["\"use client\"\n\nimport { useMemo, type ComponentPropsWithoutRef } from \"react\"\nimport type { JmpAnalytics } from \"./index\"\nimport { extractMailtoTarget, extractTelTarget } from \"./contact-link-classifier\"\nimport { getOrCreateJmpAnalyticsFromBrowserEnv } from \"./index\"\n\nexport function useJmpAnalytics(): JmpAnalytics | null {\n return useMemo(() => getOrCreateJmpAnalyticsFromBrowserEnv(), [])\n}\n\nfunction trackCallClickFromHref(href: string): void {\n const path =\n typeof window !== \"undefined\" ? window.location.pathname : \"/\"\n const contactTarget = extractTelTarget(href)\n void getOrCreateJmpAnalyticsFromBrowserEnv()?.trackCallClick(\n path,\n contactTarget ? { contactTarget } : undefined\n )\n}\n\nfunction trackEmailClickFromHref(href: string): void {\n const path =\n typeof window !== \"undefined\" ? window.location.pathname : \"/\"\n const contactTarget = extractMailtoTarget(href)\n void getOrCreateJmpAnalyticsFromBrowserEnv()?.trackEmailClick(\n path,\n contactTarget ? { contactTarget } : undefined\n )\n}\n\nexport type JmpTrackedTelLinkProps = ComponentPropsWithoutRef<\"a\"> & {\n href: string\n}\n\n/**\n * `tel:` (or other) link that records a `call_click` with the current path.\n */\nexport function JmpTrackedTelLink({\n href,\n onClick,\n ...rest\n}: JmpTrackedTelLinkProps) {\n return (\n <a\n href={href}\n data-jmp-tracked=\"true\"\n {...rest}\n onClick={(e) => {\n trackCallClickFromHref(href)\n onClick?.(e)\n }}\n />\n )\n}\n\nexport type JmpTrackedMailtoLinkProps = ComponentPropsWithoutRef<\"a\"> & {\n href: string\n}\n\n/**\n * `mailto:` link that records an `email_click` with the current path.\n */\nexport function JmpTrackedMailtoLink({\n href,\n onClick,\n ...rest\n}: JmpTrackedMailtoLinkProps) {\n return (\n <a\n href={href}\n data-jmp-tracked=\"true\"\n {...rest}\n onClick={(e) => {\n trackEmailClickFromHref(href)\n onClick?.(e)\n }}\n />\n )\n}\n"],"mappings":";;;;;;;AAEA,SAAS,eAA8C;AA0CnD;AArCG,SAAS,kBAAuC;AACrD,SAAO,QAAQ,MAAM,sCAAsC,GAAG,CAAC,CAAC;AAClE;AAEA,SAAS,uBAAuB,MAAoB;AAClD,QAAM,OACJ,OAAO,WAAW,cAAc,OAAO,SAAS,WAAW;AAC7D,QAAM,gBAAgB,iBAAiB,IAAI;AAC3C,OAAK,sCAAsC,GAAG;AAAA,IAC5C;AAAA,IACA,gBAAgB,EAAE,cAAc,IAAI;AAAA,EACtC;AACF;AAEA,SAAS,wBAAwB,MAAoB;AACnD,QAAM,OACJ,OAAO,WAAW,cAAc,OAAO,SAAS,WAAW;AAC7D,QAAM,gBAAgB,oBAAoB,IAAI;AAC9C,OAAK,sCAAsC,GAAG;AAAA,IAC5C;AAAA,IACA,gBAAgB,EAAE,cAAc,IAAI;AAAA,EACtC;AACF;AASO,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA2B;AACzB,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,oBAAiB;AAAA,MAChB,GAAG;AAAA,MACJ,SAAS,CAAC,MAAM;AACd,+BAAuB,IAAI;AAC3B,kBAAU,CAAC;AAAA,MACb;AAAA;AAAA,EACF;AAEJ;AASO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA8B;AAC5B,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,oBAAiB;AAAA,MAChB,GAAG;AAAA,MACJ,SAAS,CAAC,MAAM;AACd,gCAAwB,IAAI;AAC5B,kBAAU,CAAC;AAAA,MACb;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
readJmpPublicEnvFromProcess,
|
|
11
11
|
resolveFetch,
|
|
12
12
|
responseLooksLikeHtml
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-AIFJ7DVO.js";
|
|
14
14
|
|
|
15
15
|
// src/contact-link-classifier.ts
|
|
16
16
|
var BOOKING_LINK_HOSTS = [
|
|
@@ -130,6 +130,26 @@ function classifyContactHref(href) {
|
|
|
130
130
|
}
|
|
131
131
|
return null;
|
|
132
132
|
}
|
|
133
|
+
function extractTelTarget(href) {
|
|
134
|
+
const raw = href.trim();
|
|
135
|
+
if (!raw.toLowerCase().startsWith("tel:")) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
const target = decodeURIComponent(
|
|
139
|
+
raw.slice(4).split("?")[0]?.split(";")[0] ?? ""
|
|
140
|
+
).trim();
|
|
141
|
+
return target || null;
|
|
142
|
+
}
|
|
143
|
+
function extractMailtoTarget(href) {
|
|
144
|
+
const raw = href.trim();
|
|
145
|
+
if (!raw.toLowerCase().startsWith("mailto:")) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
const target = decodeURIComponent(
|
|
149
|
+
raw.slice(7).split("?")[0]?.split(",")[0] ?? ""
|
|
150
|
+
).trim();
|
|
151
|
+
return target || null;
|
|
152
|
+
}
|
|
133
153
|
|
|
134
154
|
// src/constants.ts
|
|
135
155
|
var JMP_ANALYTICS_SDK_VERSION = "0.1.26";
|
|
@@ -517,14 +537,22 @@ function createJmpAnalytics(config) {
|
|
|
517
537
|
if (dedupeContactClick(`call_click:${path}`)) {
|
|
518
538
|
return;
|
|
519
539
|
}
|
|
520
|
-
|
|
540
|
+
const contactTarget = extractTelTarget(href);
|
|
541
|
+
void trackCallClick(
|
|
542
|
+
path,
|
|
543
|
+
contactTarget ? { contactTarget } : void 0
|
|
544
|
+
);
|
|
521
545
|
return;
|
|
522
546
|
}
|
|
523
547
|
if (classified.kind === "email") {
|
|
524
548
|
if (dedupeContactClick(`email_click:${path}`)) {
|
|
525
549
|
return;
|
|
526
550
|
}
|
|
527
|
-
|
|
551
|
+
const contactTarget = extractMailtoTarget(href);
|
|
552
|
+
void trackEmailClick(
|
|
553
|
+
path,
|
|
554
|
+
contactTarget ? { contactTarget } : void 0
|
|
555
|
+
);
|
|
528
556
|
return;
|
|
529
557
|
}
|
|
530
558
|
if (dedupeContactClick(`contact_click:${classified.channel}:${path}`)) {
|
|
@@ -570,6 +598,209 @@ function createJmpAnalytics(config) {
|
|
|
570
598
|
};
|
|
571
599
|
}
|
|
572
600
|
|
|
601
|
+
// src/content/manifest.ts
|
|
602
|
+
var JMP_CONTENT_MANIFEST_VERSION = 1;
|
|
603
|
+
var TRUST_SECTIONS = /* @__PURE__ */ new Set([
|
|
604
|
+
"summary",
|
|
605
|
+
"testimonials",
|
|
606
|
+
"reviews",
|
|
607
|
+
"stats",
|
|
608
|
+
"logos",
|
|
609
|
+
"credentials"
|
|
610
|
+
]);
|
|
611
|
+
function normalizeKey(value) {
|
|
612
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
613
|
+
}
|
|
614
|
+
function isValidExtraKey(key) {
|
|
615
|
+
return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(key);
|
|
616
|
+
}
|
|
617
|
+
var MAX_EXTERNAL_ADMIN_LINKS = 10;
|
|
618
|
+
var MAX_EXTERNAL_ADMIN_LINK_LABEL_LENGTH = 80;
|
|
619
|
+
var EXTERNAL_ADMIN_LINK_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
620
|
+
function tryNormalizeHttpUrl(input) {
|
|
621
|
+
if (typeof input !== "string" || !input.trim()) {
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
624
|
+
try {
|
|
625
|
+
const url = new URL(input.trim());
|
|
626
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
627
|
+
return null;
|
|
628
|
+
}
|
|
629
|
+
return url.toString().replace(/\/$/, "");
|
|
630
|
+
} catch {
|
|
631
|
+
return null;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
function normalizeShopifyExternalSource(shopify) {
|
|
635
|
+
if (!shopify || typeof shopify !== "object") {
|
|
636
|
+
return null;
|
|
637
|
+
}
|
|
638
|
+
const adminUrl = tryNormalizeHttpUrl(shopify.adminUrl);
|
|
639
|
+
const storeUrl = tryNormalizeHttpUrl(shopify.storeUrl);
|
|
640
|
+
if (!adminUrl && !storeUrl) {
|
|
641
|
+
return null;
|
|
642
|
+
}
|
|
643
|
+
return {
|
|
644
|
+
...adminUrl ? { adminUrl } : {},
|
|
645
|
+
...storeUrl ? { storeUrl } : {}
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
function normalizeExternalAdminLinkEntries(links) {
|
|
649
|
+
if (!Array.isArray(links)) {
|
|
650
|
+
return [];
|
|
651
|
+
}
|
|
652
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
653
|
+
const out = [];
|
|
654
|
+
for (const raw of links) {
|
|
655
|
+
if (!raw || typeof raw !== "object" || out.length >= MAX_EXTERNAL_ADMIN_LINKS) {
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
658
|
+
const id = typeof raw.id === "string" ? raw.id.trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "") : "";
|
|
659
|
+
const label = typeof raw.label === "string" ? raw.label.trim().slice(0, MAX_EXTERNAL_ADMIN_LINK_LABEL_LENGTH) : "";
|
|
660
|
+
const url = tryNormalizeHttpUrl(raw.url);
|
|
661
|
+
if (!id || !label || !url || !EXTERNAL_ADMIN_LINK_ID_PATTERN.test(id) || seenIds.has(id)) {
|
|
662
|
+
continue;
|
|
663
|
+
}
|
|
664
|
+
seenIds.add(id);
|
|
665
|
+
out.push({ id, label, url });
|
|
666
|
+
}
|
|
667
|
+
return out;
|
|
668
|
+
}
|
|
669
|
+
function normalizeExternalSources(externalSources) {
|
|
670
|
+
if (!externalSources || typeof externalSources !== "object") {
|
|
671
|
+
return null;
|
|
672
|
+
}
|
|
673
|
+
const studioUrl = tryNormalizeHttpUrl(externalSources.sanity?.studioUrl);
|
|
674
|
+
const shopify = normalizeShopifyExternalSource(externalSources.shopify);
|
|
675
|
+
const links = normalizeExternalAdminLinkEntries(externalSources.links);
|
|
676
|
+
if (!studioUrl && !shopify && links.length === 0) {
|
|
677
|
+
return null;
|
|
678
|
+
}
|
|
679
|
+
return {
|
|
680
|
+
...studioUrl ? { sanity: { studioUrl } } : {},
|
|
681
|
+
...shopify ? { shopify } : {},
|
|
682
|
+
...links.length > 0 ? { links } : {}
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
function normalizeJmpContentManifest(input) {
|
|
686
|
+
if (!input || typeof input !== "object") {
|
|
687
|
+
return null;
|
|
688
|
+
}
|
|
689
|
+
const routes = normalizeRoutes(input.routes);
|
|
690
|
+
const externalSources = normalizeExternalSources(input.externalSources);
|
|
691
|
+
const trust = normalizeTrust(input.trust);
|
|
692
|
+
const contact = normalizeContact(input.contact);
|
|
693
|
+
if (routes.length === 0 && !externalSources && !trust && !contact) {
|
|
694
|
+
return null;
|
|
695
|
+
}
|
|
696
|
+
return {
|
|
697
|
+
version: JMP_CONTENT_MANIFEST_VERSION,
|
|
698
|
+
...routes.length > 0 ? { routes } : {},
|
|
699
|
+
...externalSources ? { externalSources } : {},
|
|
700
|
+
...trust ? { trust } : {},
|
|
701
|
+
...contact ? { contact } : {}
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
function normalizeRoutes(routes) {
|
|
705
|
+
if (!Array.isArray(routes)) {
|
|
706
|
+
return [];
|
|
707
|
+
}
|
|
708
|
+
const seen = /* @__PURE__ */ new Set();
|
|
709
|
+
const out = [];
|
|
710
|
+
for (const raw of routes) {
|
|
711
|
+
if (!raw || typeof raw !== "object") continue;
|
|
712
|
+
const wrapper = typeof raw.wrapper === "string" ? raw.wrapper.trim().toLowerCase() : "";
|
|
713
|
+
const pattern = typeof raw.pattern === "string" ? raw.pattern.trim() : "";
|
|
714
|
+
if (!wrapper || !pattern) continue;
|
|
715
|
+
const dedupe = `${wrapper}:${pattern}`;
|
|
716
|
+
if (seen.has(dedupe)) continue;
|
|
717
|
+
seen.add(dedupe);
|
|
718
|
+
out.push({
|
|
719
|
+
wrapper,
|
|
720
|
+
pattern,
|
|
721
|
+
...typeof raw.index === "string" && raw.index.trim() ? { index: raw.index.trim() } : {},
|
|
722
|
+
...typeof raw.label === "string" && raw.label.trim() ? { label: raw.label.trim() } : {}
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
return out;
|
|
726
|
+
}
|
|
727
|
+
function normalizeTrust(trust) {
|
|
728
|
+
if (!trust || typeof trust !== "object") return null;
|
|
729
|
+
const blocks = normalizeTrustBlocks(trust.blocks);
|
|
730
|
+
const labels = normalizeTrustLabels(trust.labels);
|
|
731
|
+
const mergeTestimonialsReviews = trust.mergeTestimonialsReviews === true;
|
|
732
|
+
if (blocks.length === 0 && !labels && !mergeTestimonialsReviews) return null;
|
|
733
|
+
return {
|
|
734
|
+
...blocks.length > 0 ? { blocks } : {},
|
|
735
|
+
...labels ? { labels } : {},
|
|
736
|
+
...mergeTestimonialsReviews ? { mergeTestimonialsReviews: true } : {}
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
function normalizeTrustBlocks(blocks) {
|
|
740
|
+
if (!Array.isArray(blocks)) return [];
|
|
741
|
+
const seen = /* @__PURE__ */ new Set();
|
|
742
|
+
const out = [];
|
|
743
|
+
for (const raw of blocks) {
|
|
744
|
+
if (!raw || typeof raw !== "object") continue;
|
|
745
|
+
const section = typeof raw.section === "string" ? raw.section.trim().toLowerCase() : "";
|
|
746
|
+
if (!TRUST_SECTIONS.has(section) || seen.has(section)) continue;
|
|
747
|
+
seen.add(section);
|
|
748
|
+
out.push({
|
|
749
|
+
section,
|
|
750
|
+
...typeof raw.label === "string" && raw.label.trim() ? { label: raw.label.trim() } : {}
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
return out;
|
|
754
|
+
}
|
|
755
|
+
function normalizeTrustLabels(labels) {
|
|
756
|
+
if (!labels || typeof labels !== "object") return null;
|
|
757
|
+
const out = {};
|
|
758
|
+
for (const [key, value] of Object.entries(labels)) {
|
|
759
|
+
if (TRUST_SECTIONS.has(key) && typeof value === "string" && value.trim()) {
|
|
760
|
+
out[key] = value.trim();
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
764
|
+
}
|
|
765
|
+
function normalizeContact(contact) {
|
|
766
|
+
if (!contact || typeof contact !== "object") return null;
|
|
767
|
+
const extras = normalizeContactExtras(contact.extras);
|
|
768
|
+
return extras.length > 0 ? { extras } : null;
|
|
769
|
+
}
|
|
770
|
+
function normalizeContactExtras(extras) {
|
|
771
|
+
if (!Array.isArray(extras)) return [];
|
|
772
|
+
const seen = /* @__PURE__ */ new Set();
|
|
773
|
+
const out = [];
|
|
774
|
+
for (const raw of extras) {
|
|
775
|
+
if (!raw || typeof raw !== "object") continue;
|
|
776
|
+
const label = typeof raw.label === "string" ? raw.label.trim() : "";
|
|
777
|
+
const key = normalizeKey(
|
|
778
|
+
typeof raw.key === "string" && raw.key.trim() ? raw.key : label
|
|
779
|
+
);
|
|
780
|
+
if (!key || !label || !isValidExtraKey(key) || seen.has(key)) continue;
|
|
781
|
+
seen.add(key);
|
|
782
|
+
out.push({ key, label });
|
|
783
|
+
}
|
|
784
|
+
return out;
|
|
785
|
+
}
|
|
786
|
+
function stableJmpManifestFingerprint(manifest) {
|
|
787
|
+
return manifest ? JSON.stringify(manifest) : "";
|
|
788
|
+
}
|
|
789
|
+
function inferWrapperSlotsFromManifest(manifest) {
|
|
790
|
+
const slots = /* @__PURE__ */ new Set();
|
|
791
|
+
for (const route of manifest?.routes ?? []) {
|
|
792
|
+
slots.add(route.wrapper);
|
|
793
|
+
}
|
|
794
|
+
for (const block of manifest?.trust?.blocks ?? []) {
|
|
795
|
+
slots.add(`trust.${block.section}`);
|
|
796
|
+
slots.add("trust");
|
|
797
|
+
}
|
|
798
|
+
if (manifest?.contact?.extras?.length) {
|
|
799
|
+
slots.add("contact");
|
|
800
|
+
}
|
|
801
|
+
return [...slots];
|
|
802
|
+
}
|
|
803
|
+
|
|
573
804
|
// src/content/fetch.ts
|
|
574
805
|
async function getContent(config, path) {
|
|
575
806
|
const text = await getContentText(config, path);
|
|
@@ -684,16 +915,22 @@ function inferTrustWrapperSlots(bundle) {
|
|
|
684
915
|
function getDiscoveredContentWrappers() {
|
|
685
916
|
return sortWrapperSlots([...discovered]);
|
|
686
917
|
}
|
|
918
|
+
function resolveContentManifestForReport(config) {
|
|
919
|
+
return normalizeJmpContentManifest(config.contentManifest);
|
|
920
|
+
}
|
|
687
921
|
function resolveContentWrappersForReport(config) {
|
|
688
922
|
const explicit = config.contentWrappers ?? [];
|
|
689
923
|
const auto = getDiscoveredContentWrappers();
|
|
924
|
+
const fromManifest = inferWrapperSlotsFromManifest(
|
|
925
|
+
resolveContentManifestForReport(config)
|
|
926
|
+
);
|
|
690
927
|
if (config.contentWrapperMode === "manual") {
|
|
691
|
-
return sortWrapperSlots([...explicit]);
|
|
928
|
+
return sortWrapperSlots([...explicit, ...fromManifest]);
|
|
692
929
|
}
|
|
693
930
|
if (config.contentWrapperMode === "explicit-only") {
|
|
694
|
-
return sortWrapperSlots([...explicit]);
|
|
931
|
+
return sortWrapperSlots([...explicit, ...fromManifest]);
|
|
695
932
|
}
|
|
696
|
-
const merged = /* @__PURE__ */ new Set([...explicit, ...auto]);
|
|
933
|
+
const merged = /* @__PURE__ */ new Set([...explicit, ...auto, ...fromManifest]);
|
|
697
934
|
return sortWrapperSlots([...merged]);
|
|
698
935
|
}
|
|
699
936
|
function hasReportedContentWrappers(dedupeKey) {
|
|
@@ -831,6 +1068,35 @@ async function getJmpLandingPageSitemap(config) {
|
|
|
831
1068
|
afterContentWrapperUse(config);
|
|
832
1069
|
return data.entries;
|
|
833
1070
|
}
|
|
1071
|
+
async function getJmpLocations(config) {
|
|
1072
|
+
const data = await getContent(
|
|
1073
|
+
config,
|
|
1074
|
+
"/api/content/locations"
|
|
1075
|
+
);
|
|
1076
|
+
markContentWrapper("locations");
|
|
1077
|
+
afterContentWrapperUse(config);
|
|
1078
|
+
return data.locations;
|
|
1079
|
+
}
|
|
1080
|
+
async function getJmpLocationDetail(config, slug) {
|
|
1081
|
+
const normalized = slug.trim();
|
|
1082
|
+
if (!normalized) {
|
|
1083
|
+
return null;
|
|
1084
|
+
}
|
|
1085
|
+
try {
|
|
1086
|
+
const detail = await getContent(
|
|
1087
|
+
config,
|
|
1088
|
+
`/api/content/locations/${encodeURIComponent(normalized)}`
|
|
1089
|
+
);
|
|
1090
|
+
markContentWrapper("locations");
|
|
1091
|
+
afterContentWrapperUse(config);
|
|
1092
|
+
return detail;
|
|
1093
|
+
} catch (error) {
|
|
1094
|
+
if (error instanceof Error && error.message.includes("HTTP 404")) {
|
|
1095
|
+
return null;
|
|
1096
|
+
}
|
|
1097
|
+
throw error;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
834
1100
|
async function getJmpTrustBundle(config) {
|
|
835
1101
|
const bundle = await getContent(config, "/api/content/trust");
|
|
836
1102
|
markTrustBundleWrappers(bundle);
|
|
@@ -854,7 +1120,8 @@ async function registerJmpContentWrappers(config) {
|
|
|
854
1120
|
if (!wrappers.length) {
|
|
855
1121
|
return { ok: false, skipped: true };
|
|
856
1122
|
}
|
|
857
|
-
const
|
|
1123
|
+
const manifest = resolveContentManifestForReport(config);
|
|
1124
|
+
const dedupeKey = `${config.trackingKey}:${[...wrappers].sort().join(",")}:${stableJmpManifestFingerprint(manifest)}`;
|
|
858
1125
|
if (hasReportedContentWrappers(dedupeKey)) {
|
|
859
1126
|
return { ok: true, wrappers };
|
|
860
1127
|
}
|
|
@@ -867,7 +1134,17 @@ async function registerJmpContentWrappers(config) {
|
|
|
867
1134
|
}
|
|
868
1135
|
const url = `${baseUrl}/api/content/capabilities`;
|
|
869
1136
|
if (config.debug) {
|
|
870
|
-
console.debug("[@jmp-technologies/analytics]", "POST", url, {
|
|
1137
|
+
console.debug("[@jmp-technologies/analytics]", "POST", url, {
|
|
1138
|
+
wrappers,
|
|
1139
|
+
manifest
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
const body = {
|
|
1143
|
+
wrappers,
|
|
1144
|
+
sdkVersion: JMP_ANALYTICS_SDK_VERSION
|
|
1145
|
+
};
|
|
1146
|
+
if (manifest) {
|
|
1147
|
+
body.manifest = manifest;
|
|
871
1148
|
}
|
|
872
1149
|
const res = await resolvedFetch(url, {
|
|
873
1150
|
method: "POST",
|
|
@@ -875,10 +1152,7 @@ async function registerJmpContentWrappers(config) {
|
|
|
875
1152
|
"Content-Type": "application/json",
|
|
876
1153
|
"X-JMP-Tracking-Key": config.trackingKey
|
|
877
1154
|
},
|
|
878
|
-
body: JSON.stringify(
|
|
879
|
-
wrappers,
|
|
880
|
-
sdkVersion: JMP_ANALYTICS_SDK_VERSION
|
|
881
|
-
})
|
|
1155
|
+
body: JSON.stringify(body)
|
|
882
1156
|
});
|
|
883
1157
|
const text = await res.text();
|
|
884
1158
|
if (!res.ok) {
|
|
@@ -962,6 +1236,8 @@ function createJmpContentClientFromServerEnv(overrides) {
|
|
|
962
1236
|
export {
|
|
963
1237
|
BOOKING_LINK_HOSTS,
|
|
964
1238
|
classifyContactHref,
|
|
1239
|
+
extractTelTarget,
|
|
1240
|
+
extractMailtoTarget,
|
|
965
1241
|
JMP_ANALYTICS_SDK_VERSION,
|
|
966
1242
|
JMP_TRACKING_CONSENT_STORAGE_KEY,
|
|
967
1243
|
DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS,
|
|
@@ -969,6 +1245,8 @@ export {
|
|
|
969
1245
|
subscribeJmpTrackingConsent,
|
|
970
1246
|
setJmpTrackingConsent,
|
|
971
1247
|
createJmpAnalytics,
|
|
1248
|
+
normalizeJmpContentManifest,
|
|
1249
|
+
stableJmpManifestFingerprint,
|
|
972
1250
|
markContentWrapper,
|
|
973
1251
|
inferTrustWrapperSlots,
|
|
974
1252
|
getDiscoveredContentWrappers,
|
|
@@ -979,6 +1257,8 @@ export {
|
|
|
979
1257
|
getJmpLandingPages,
|
|
980
1258
|
getJmpLandingPageDetail,
|
|
981
1259
|
getJmpLandingPageSitemap,
|
|
1260
|
+
getJmpLocations,
|
|
1261
|
+
getJmpLocationDetail,
|
|
982
1262
|
getJmpTrustBundle,
|
|
983
1263
|
getJmpSiteProfile,
|
|
984
1264
|
getJmpRobotsTxt,
|
|
@@ -991,4 +1271,4 @@ export {
|
|
|
991
1271
|
getOrCreateJmpAnalyticsFromBrowserEnv,
|
|
992
1272
|
createJmpContentClientFromServerEnv
|
|
993
1273
|
};
|
|
994
|
-
//# sourceMappingURL=chunk-
|
|
1274
|
+
//# sourceMappingURL=chunk-NI7R6QZQ.js.map
|