@jmp-technologies/analytics 0.1.30 → 0.1.33

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 (86) hide show
  1. package/dist/astro/client.cjs +30 -2
  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 +1 -1
  6. package/dist/astro/content.cjs +84 -14
  7. package/dist/astro/content.cjs.map +1 -1
  8. package/dist/astro/content.d.cts +10 -8
  9. package/dist/astro/content.d.ts +10 -8
  10. package/dist/astro/content.js +6 -2
  11. package/dist/astro/integration.cjs.map +1 -1
  12. package/dist/astro/integration.js +2 -2
  13. package/dist/astro/middleware.d.cts +2 -2
  14. package/dist/astro/middleware.d.ts +2 -2
  15. package/dist/astro/register.cjs.map +1 -1
  16. package/dist/astro/register.js +2 -2
  17. package/dist/{chunk-ZEC22LFP.js → chunk-4Q6YOXKB.js} +2 -2
  18. package/dist/{chunk-LLTB7ZFC.js → chunk-AIOUOA24.js} +18 -8
  19. package/dist/chunk-AIOUOA24.js.map +1 -0
  20. package/dist/{chunk-FS4BZ7DR.js → chunk-FH5CJNQD.js} +36 -10
  21. package/dist/chunk-FH5CJNQD.js.map +1 -0
  22. package/dist/{chunk-YCVHRMM6.js → chunk-G5BMWPQ6.js} +2 -3
  23. package/dist/{chunk-YCVHRMM6.js.map → chunk-G5BMWPQ6.js.map} +1 -1
  24. package/dist/{chunk-LTPLLPOR.js → chunk-JMALYARK.js} +2 -2
  25. package/dist/{chunk-GTPKRBFR.js → chunk-KTPL3R5E.js} +132 -12
  26. package/dist/chunk-KTPL3R5E.js.map +1 -0
  27. package/dist/chunk-RI3KKDPX.js +75 -0
  28. package/dist/chunk-RI3KKDPX.js.map +1 -0
  29. package/dist/{create-jmp-analytics-BeuuU6as.d.ts → create-jmp-analytics-CwtcXBd7.d.ts} +1 -2
  30. package/dist/{create-jmp-analytics-Ce2CrhhR.d.cts → create-jmp-analytics-lkGcysG_.d.cts} +1 -2
  31. package/dist/index.cjs +133 -11
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +24 -12
  34. package/dist/index.d.ts +24 -12
  35. package/dist/index.js +9 -1
  36. package/dist/next/content.cjs +86 -14
  37. package/dist/next/content.cjs.map +1 -1
  38. package/dist/next/content.d.cts +6 -4
  39. package/dist/next/content.d.ts +6 -4
  40. package/dist/next/content.js +8 -2
  41. package/dist/next/content.js.map +1 -1
  42. package/dist/next/instrumentation.cjs.map +1 -1
  43. package/dist/next/instrumentation.js +1 -1
  44. package/dist/next/landing-capture-script-server.cjs +1 -2
  45. package/dist/next/landing-capture-script-server.cjs.map +1 -1
  46. package/dist/next/landing-capture-script-server.js +1 -1
  47. package/dist/next/landing-pages.cjs.map +1 -1
  48. package/dist/next/landing-pages.d.cts +3 -14
  49. package/dist/next/landing-pages.d.ts +3 -14
  50. package/dist/next/landing-pages.js +13 -65
  51. package/dist/next/landing-pages.js.map +1 -1
  52. package/dist/next/locations.cjs +737 -0
  53. package/dist/next/locations.cjs.map +1 -0
  54. package/dist/next/locations.d.cts +55 -0
  55. package/dist/next/locations.d.ts +55 -0
  56. package/dist/next/locations.js +158 -0
  57. package/dist/next/locations.js.map +1 -0
  58. package/dist/next/middleware.d.cts +2 -2
  59. package/dist/next/middleware.d.ts +2 -2
  60. package/dist/next/site-root-server.cjs +31 -4
  61. package/dist/next/site-root-server.cjs.map +1 -1
  62. package/dist/next/site-root-server.js +4 -4
  63. package/dist/next.cjs +44 -8
  64. package/dist/next.cjs.map +1 -1
  65. package/dist/next.d.cts +3 -3
  66. package/dist/next.d.ts +3 -3
  67. package/dist/next.js +3 -3
  68. package/dist/react.cjs +163 -127
  69. package/dist/react.cjs.map +1 -1
  70. package/dist/react.d.cts +2 -2
  71. package/dist/react.d.ts +2 -2
  72. package/dist/react.js +2 -2
  73. package/dist/{site-config-DIFmnwqv.d.cts → site-config-BMlEQ_8g.d.cts} +1 -1
  74. package/dist/{site-config-B4uEj4xX.d.ts → site-config-BbE6kGDd.d.ts} +1 -1
  75. package/dist/{track-page-view-CkgIp9-j.d.ts → track-page-view-BPWGGoMr.d.ts} +1 -1
  76. package/dist/{track-page-view-Bv0BAvxG.d.cts → track-page-view-DaNOaIxF.d.cts} +1 -1
  77. package/dist/{types-B2iP79lb.d.cts → types-BdC3SRH-.d.cts} +57 -2
  78. package/dist/{types-B2iP79lb.d.ts → types-BdC3SRH-.d.ts} +57 -2
  79. package/dist/utils-B4E-pcQ0.d.ts +15 -0
  80. package/dist/utils-DIHFgovG.d.cts +15 -0
  81. package/package.json +7 -2
  82. package/dist/chunk-FS4BZ7DR.js.map +0 -1
  83. package/dist/chunk-GTPKRBFR.js.map +0 -1
  84. package/dist/chunk-LLTB7ZFC.js.map +0 -1
  85. /package/dist/{chunk-ZEC22LFP.js.map → chunk-4Q6YOXKB.js.map} +0 -0
  86. /package/dist/{chunk-LTPLLPOR.js.map → chunk-JMALYARK.js.map} +0 -0
@@ -1,6 +1,8 @@
1
1
  import {
2
+ extractMailtoTarget,
3
+ extractTelTarget,
2
4
  getOrCreateJmpAnalyticsFromBrowserEnv
3
- } from "./chunk-GTPKRBFR.js";
5
+ } from "./chunk-KTPL3R5E.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 trackCallClickFromCurrentPath() {
13
+ function trackCallClickFromHref(href) {
12
14
  const path = typeof window !== "undefined" ? window.location.pathname : "/";
13
- void getOrCreateJmpAnalyticsFromBrowserEnv()?.trackCallClick(path);
15
+ const contactTarget = extractTelTarget(href);
16
+ void getOrCreateJmpAnalyticsFromBrowserEnv()?.trackCallClick(
17
+ path,
18
+ contactTarget ? { contactTarget } : void 0
19
+ );
14
20
  }
15
- function trackEmailClickFromCurrentPath() {
21
+ function trackEmailClickFromHref(href) {
16
22
  const path = typeof window !== "undefined" ? window.location.pathname : "/";
17
- void getOrCreateJmpAnalyticsFromBrowserEnv()?.trackEmailClick(path);
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
- trackCallClickFromCurrentPath();
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
- trackEmailClickFromCurrentPath();
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-LLTB7ZFC.js.map
71
+ //# sourceMappingURL=chunk-AIOUOA24.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":[]}
@@ -5,9 +5,11 @@ import {
5
5
  getJmpLandingPageDetail,
6
6
  getJmpLandingPageSitemap,
7
7
  getJmpLandingPages,
8
+ getJmpLocationDetail,
9
+ getJmpLocations,
8
10
  getJmpSiteProfile,
9
11
  getJmpTrustBundle
10
- } from "./chunk-GTPKRBFR.js";
12
+ } from "./chunk-KTPL3R5E.js";
11
13
  import {
12
14
  getJmpContentConfig,
13
15
  getJmpSiteDefinition
@@ -114,47 +116,47 @@ function listResolvedSocialUrls(social) {
114
116
  social.youtube
115
117
  ].filter((url) => Boolean(url));
116
118
  }
117
- async function loadBlogPosts() {
119
+ async function loadBlogPosts(locale) {
118
120
  const c = getJmpContentConfig();
119
121
  if (!c) {
120
122
  return [];
121
123
  }
122
124
  try {
123
- const posts = await getJmpBlogPosts(c);
125
+ const posts = await getJmpBlogPosts(c, locale ? { locale } : void 0);
124
126
  return sortBlogPosts(posts);
125
127
  } catch {
126
128
  return [];
127
129
  }
128
130
  }
129
- async function loadBlogPost(slug) {
131
+ async function loadBlogPost(slug, locale) {
130
132
  const c = getJmpContentConfig();
131
133
  if (!c) {
132
134
  return null;
133
135
  }
134
136
  try {
135
- return await getJmpBlogPost(c, slug);
137
+ return await getJmpBlogPost(c, slug, locale);
136
138
  } catch {
137
139
  return null;
138
140
  }
139
141
  }
140
- async function loadFaqs() {
142
+ async function loadFaqs(locale) {
141
143
  const c = getJmpContentConfig();
142
144
  if (!c) {
143
145
  return [];
144
146
  }
145
147
  try {
146
- const faqs = await getJmpFaqs(c);
148
+ const faqs = await getJmpFaqs(c, locale ? { locale } : void 0);
147
149
  return sortFaqs(faqs);
148
150
  } catch {
149
151
  return [];
150
152
  }
151
153
  }
152
- async function loadFaqBySlug(slug) {
154
+ async function loadFaqBySlug(slug, locale) {
153
155
  const normalized = slug.trim();
154
156
  if (!normalized) {
155
157
  return null;
156
158
  }
157
- const items = await loadFaqs();
159
+ const items = await loadFaqs(locale);
158
160
  return items.find((item) => item.slug === normalized || item.id === normalized) ?? null;
159
161
  }
160
162
  async function loadLandingPages(filters) {
@@ -190,6 +192,28 @@ async function loadLandingPageSitemap() {
190
192
  return [];
191
193
  }
192
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
+ }
193
217
  async function loadTrustBundle() {
194
218
  const c = getJmpContentConfig();
195
219
  if (!c) {
@@ -227,7 +251,9 @@ export {
227
251
  loadLandingPages,
228
252
  loadLandingPageDetail,
229
253
  loadLandingPageSitemap,
254
+ loadLocations,
255
+ loadLocationDetail,
230
256
  loadTrustBundle,
231
257
  filterFeaturedTrustQuotes
232
258
  };
233
- //# sourceMappingURL=chunk-FS4BZ7DR.js.map
259
+ //# sourceMappingURL=chunk-FH5CJNQD.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(locale?: string): Promise<JmpBlogPost[]> {\n const c = getJmpContentConfig()\n if (!c) {\n return []\n }\n try {\n const posts = await getJmpBlogPosts(c, locale ? { locale } : undefined)\n return sortBlogPosts(posts)\n } catch {\n return []\n }\n}\n\nexport async function loadBlogPost(\n slug: string,\n locale?: string\n): Promise<JmpBlogPost | null> {\n const c = getJmpContentConfig()\n if (!c) {\n return null\n }\n try {\n return await getJmpBlogPost(c, slug, locale)\n } catch {\n return null\n }\n}\n\nexport async function loadFaqs(locale?: string): Promise<JmpFaqItem[]> {\n const c = getJmpContentConfig()\n if (!c) {\n return []\n }\n try {\n const faqs = await getJmpFaqs(c, locale ? { locale } : undefined)\n return sortFaqs(faqs)\n } catch {\n return []\n }\n}\n\nexport async function loadFaqBySlug(\n slug: string,\n locale?: string\n): Promise<JmpFaqItem | null> {\n const normalized = slug.trim()\n if (!normalized) {\n return null\n }\n const items = await loadFaqs(locale)\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":[]}
@@ -16,8 +16,7 @@ function JmpLandingCaptureScriptServer({
16
16
  dangerouslySetInnerHTML: {
17
17
  __html: buildLandingCaptureInlineScript()
18
18
  },
19
- id: "jmp-landing-capture",
20
- suppressHydrationWarning: true
19
+ id: "jmp-landing-capture"
21
20
  }
22
21
  );
23
22
  }
@@ -25,4 +24,4 @@ function JmpLandingCaptureScriptServer({
25
24
  export {
26
25
  JmpLandingCaptureScriptServer
27
26
  };
28
- //# sourceMappingURL=chunk-YCVHRMM6.js.map
27
+ //# sourceMappingURL=chunk-G5BMWPQ6.js.map
@@ -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 suppressHydrationWarning\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,MACH,0BAAwB;AAAA;AAAA,EAC1B;AAEJ;","names":[]}
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
  getOrCreateJmpAnalyticsFromBrowserEnv,
3
3
  isJmpTrackingConsentGranted,
4
4
  subscribeJmpTrackingConsent
5
- } from "./chunk-GTPKRBFR.js";
5
+ } from "./chunk-KTPL3R5E.js";
6
6
  import {
7
7
  readJmpPublicEnvFromProcess
8
8
  } from "./chunk-AIFJ7DVO.js";
@@ -130,4 +130,4 @@ export {
130
130
  JmpNextAnalytics,
131
131
  JmpSiteRoot
132
132
  };
133
- //# sourceMappingURL=chunk-LTPLLPOR.js.map
133
+ //# sourceMappingURL=chunk-JMALYARK.js.map
@@ -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
- void trackCallClick(path);
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
- void trackEmailClick(path);
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}`)) {
@@ -959,20 +987,30 @@ function sortWrapperSlots(slots) {
959
987
  function afterContentWrapperUse(config) {
960
988
  scheduleContentWrapperSync(config);
961
989
  }
962
- async function getJmpBlogPosts(config) {
990
+ async function getJmpBlogPosts(config, filters) {
991
+ const params = new URLSearchParams();
992
+ if (filters?.locale) {
993
+ params.set("locale", filters.locale);
994
+ }
995
+ const query = params.toString();
963
996
  const data = await getContent(
964
997
  config,
965
- "/api/content/blog"
998
+ `/api/content/blog${query ? `?${query}` : ""}`
966
999
  );
967
1000
  markContentWrapper("blog");
968
1001
  afterContentWrapperUse(config);
969
1002
  return data.posts;
970
1003
  }
971
- async function getJmpBlogPost(config, slug) {
1004
+ async function getJmpBlogPost(config, slug, locale) {
972
1005
  try {
1006
+ const params = new URLSearchParams();
1007
+ if (locale) {
1008
+ params.set("locale", locale);
1009
+ }
1010
+ const query = params.toString();
973
1011
  const data = await getContent(
974
1012
  config,
975
- `/api/content/blog/${encodeURIComponent(slug)}`
1013
+ `/api/content/blog/${encodeURIComponent(slug)}${query ? `?${query}` : ""}`
976
1014
  );
977
1015
  markContentWrapper("blog");
978
1016
  afterContentWrapperUse(config);
@@ -984,15 +1022,62 @@ async function getJmpBlogPost(config, slug) {
984
1022
  throw error;
985
1023
  }
986
1024
  }
987
- async function getJmpFaqs(config) {
1025
+ async function getJmpBlogPostDetail(config, slug, locale) {
1026
+ try {
1027
+ const params = new URLSearchParams();
1028
+ if (locale) {
1029
+ params.set("locale", locale);
1030
+ }
1031
+ const query = params.toString();
1032
+ const data = await getContent(
1033
+ config,
1034
+ `/api/content/blog/${encodeURIComponent(slug)}${query ? `?${query}` : ""}`
1035
+ );
1036
+ markContentWrapper("blog");
1037
+ afterContentWrapperUse(config);
1038
+ return data;
1039
+ } catch (error) {
1040
+ if (error instanceof Error && error.message.includes("HTTP 404")) {
1041
+ return null;
1042
+ }
1043
+ throw error;
1044
+ }
1045
+ }
1046
+ async function getJmpFaqs(config, filters) {
1047
+ const params = new URLSearchParams();
1048
+ if (filters?.locale) {
1049
+ params.set("locale", filters.locale);
1050
+ }
1051
+ const query = params.toString();
988
1052
  const data = await getContent(
989
1053
  config,
990
- "/api/content/faqs"
1054
+ `/api/content/faqs${query ? `?${query}` : ""}`
991
1055
  );
992
1056
  markContentWrapper("faq");
993
1057
  afterContentWrapperUse(config);
994
1058
  return data.faqs;
995
1059
  }
1060
+ async function getJmpFaqDetail(config, slug, locale) {
1061
+ try {
1062
+ const params = new URLSearchParams();
1063
+ if (locale) {
1064
+ params.set("locale", locale);
1065
+ }
1066
+ const query = params.toString();
1067
+ const data = await getContent(
1068
+ config,
1069
+ `/api/content/faqs/${encodeURIComponent(slug)}${query ? `?${query}` : ""}`
1070
+ );
1071
+ markContentWrapper("faq");
1072
+ afterContentWrapperUse(config);
1073
+ return data;
1074
+ } catch (error) {
1075
+ if (error instanceof Error && error.message.includes("HTTP 404")) {
1076
+ return null;
1077
+ }
1078
+ throw error;
1079
+ }
1080
+ }
996
1081
  async function getJmpLandingPages(config, filters) {
997
1082
  const params = new URLSearchParams();
998
1083
  if (filters?.kind) {
@@ -1040,6 +1125,35 @@ async function getJmpLandingPageSitemap(config) {
1040
1125
  afterContentWrapperUse(config);
1041
1126
  return data.entries;
1042
1127
  }
1128
+ async function getJmpLocations(config) {
1129
+ const data = await getContent(
1130
+ config,
1131
+ "/api/content/locations"
1132
+ );
1133
+ markContentWrapper("locations");
1134
+ afterContentWrapperUse(config);
1135
+ return data.locations;
1136
+ }
1137
+ async function getJmpLocationDetail(config, slug) {
1138
+ const normalized = slug.trim();
1139
+ if (!normalized) {
1140
+ return null;
1141
+ }
1142
+ try {
1143
+ const detail = await getContent(
1144
+ config,
1145
+ `/api/content/locations/${encodeURIComponent(normalized)}`
1146
+ );
1147
+ markContentWrapper("locations");
1148
+ afterContentWrapperUse(config);
1149
+ return detail;
1150
+ } catch (error) {
1151
+ if (error instanceof Error && error.message.includes("HTTP 404")) {
1152
+ return null;
1153
+ }
1154
+ throw error;
1155
+ }
1156
+ }
1043
1157
  async function getJmpTrustBundle(config) {
1044
1158
  const bundle = await getContent(config, "/api/content/trust");
1045
1159
  markTrustBundleWrappers(bundle);
@@ -1118,9 +1232,9 @@ setContentWrapperSyncHandler(registerJmpContentWrappers);
1118
1232
  // src/content/client.ts
1119
1233
  function createJmpContentClient(config) {
1120
1234
  return {
1121
- getBlogPosts: () => getJmpBlogPosts(config),
1122
- getBlogPost: (slug) => getJmpBlogPost(config, slug),
1123
- getFaqs: () => getJmpFaqs(config),
1235
+ getBlogPosts: (filters) => getJmpBlogPosts(config, filters),
1236
+ getBlogPost: (slug, locale) => getJmpBlogPost(config, slug, locale),
1237
+ getFaqs: (filters) => getJmpFaqs(config, filters),
1124
1238
  getLandingPages: (filters) => getJmpLandingPages(config, filters),
1125
1239
  getLandingPageDetail: (kind, slug, locale) => getJmpLandingPageDetail(config, kind, slug, locale),
1126
1240
  getLandingPageSitemap: () => getJmpLandingPageSitemap(config),
@@ -1179,6 +1293,8 @@ function createJmpContentClientFromServerEnv(overrides) {
1179
1293
  export {
1180
1294
  BOOKING_LINK_HOSTS,
1181
1295
  classifyContactHref,
1296
+ extractTelTarget,
1297
+ extractMailtoTarget,
1182
1298
  JMP_ANALYTICS_SDK_VERSION,
1183
1299
  JMP_TRACKING_CONSENT_STORAGE_KEY,
1184
1300
  DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS,
@@ -1194,10 +1310,14 @@ export {
1194
1310
  resetContentWrapperDiscoveryForTests,
1195
1311
  getJmpBlogPosts,
1196
1312
  getJmpBlogPost,
1313
+ getJmpBlogPostDetail,
1197
1314
  getJmpFaqs,
1315
+ getJmpFaqDetail,
1198
1316
  getJmpLandingPages,
1199
1317
  getJmpLandingPageDetail,
1200
1318
  getJmpLandingPageSitemap,
1319
+ getJmpLocations,
1320
+ getJmpLocationDetail,
1201
1321
  getJmpTrustBundle,
1202
1322
  getJmpSiteProfile,
1203
1323
  getJmpRobotsTxt,
@@ -1210,4 +1330,4 @@ export {
1210
1330
  getOrCreateJmpAnalyticsFromBrowserEnv,
1211
1331
  createJmpContentClientFromServerEnv
1212
1332
  };
1213
- //# sourceMappingURL=chunk-GTPKRBFR.js.map
1333
+ //# sourceMappingURL=chunk-KTPL3R5E.js.map