@jmp-technologies/analytics 0.1.26 → 0.1.27

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 (75) hide show
  1. package/README.md +43 -0
  2. package/bin/jmp-init.mjs +34 -0
  3. package/dist/astro/client.cjs +1 -1
  4. package/dist/astro/client.cjs.map +1 -1
  5. package/dist/astro/client.d.cts +2 -2
  6. package/dist/astro/client.d.ts +2 -2
  7. package/dist/astro/client.js +1 -1
  8. package/dist/astro/content.cjs +87 -1
  9. package/dist/astro/content.cjs.map +1 -1
  10. package/dist/astro/content.d.cts +9 -3
  11. package/dist/astro/content.d.ts +9 -3
  12. package/dist/astro/content.js +8 -2
  13. package/dist/astro/integration.cjs +1 -1
  14. package/dist/astro/integration.cjs.map +1 -1
  15. package/dist/astro/integration.js +2 -2
  16. package/dist/astro/middleware.d.cts +2 -2
  17. package/dist/astro/middleware.d.ts +2 -2
  18. package/dist/astro/register.cjs +1 -1
  19. package/dist/astro/register.cjs.map +1 -1
  20. package/dist/astro/register.js +2 -2
  21. package/dist/{chunk-PKGUBYZR.js → chunk-DNYJF7BG.js} +2 -2
  22. package/dist/{chunk-WFTTIRDJ.js → chunk-FNWE2DT5.js} +2 -2
  23. package/dist/{chunk-XQZOGWF2.js → chunk-KZ4WVDGL.js} +2 -2
  24. package/dist/{chunk-TFHOTBOL.js → chunk-PG3IOI5O.js} +41 -2
  25. package/dist/chunk-PG3IOI5O.js.map +1 -0
  26. package/dist/{chunk-RCOHQAGL.js → chunk-UVYF33XE.js} +55 -2
  27. package/dist/{chunk-RCOHQAGL.js.map → chunk-UVYF33XE.js.map} +1 -1
  28. package/dist/{create-jmp-analytics-Mau-oxji.d.cts → create-jmp-analytics-D4Qw_d6o.d.cts} +1 -1
  29. package/dist/{create-jmp-analytics-JKAsJAWb.d.ts → create-jmp-analytics-DpG5Hdl5.d.ts} +1 -1
  30. package/dist/index.cjs +57 -1
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +16 -7
  33. package/dist/index.d.ts +16 -7
  34. package/dist/index.js +7 -1
  35. package/dist/next/content.cjs +90 -1
  36. package/dist/next/content.cjs.map +1 -1
  37. package/dist/next/content.d.cts +7 -4
  38. package/dist/next/content.d.ts +7 -4
  39. package/dist/next/content.js +11 -2
  40. package/dist/next/content.js.map +1 -1
  41. package/dist/next/instrumentation.cjs +1 -1
  42. package/dist/next/instrumentation.cjs.map +1 -1
  43. package/dist/next/instrumentation.js +1 -1
  44. package/dist/next/landing-pages.cjs +659 -0
  45. package/dist/next/landing-pages.cjs.map +1 -0
  46. package/dist/next/landing-pages.d.cts +104 -0
  47. package/dist/next/landing-pages.d.ts +104 -0
  48. package/dist/next/landing-pages.js +299 -0
  49. package/dist/next/landing-pages.js.map +1 -0
  50. package/dist/next/middleware.d.cts +2 -2
  51. package/dist/next/middleware.d.ts +2 -2
  52. package/dist/next/site-root-server.cjs +1 -1
  53. package/dist/next/site-root-server.cjs.map +1 -1
  54. package/dist/next/site-root-server.js +3 -3
  55. package/dist/next.cjs +1 -1
  56. package/dist/next.cjs.map +1 -1
  57. package/dist/next.d.cts +3 -3
  58. package/dist/next.d.ts +3 -3
  59. package/dist/next.js +3 -3
  60. package/dist/react.cjs +1 -1
  61. package/dist/react.cjs.map +1 -1
  62. package/dist/react.d.cts +2 -2
  63. package/dist/react.d.ts +2 -2
  64. package/dist/react.js +2 -2
  65. package/dist/{site-config-CXUdGLqw.d.cts → site-config-CJqYnZm7.d.cts} +1 -1
  66. package/dist/{site-config-w_Db9YSk.d.ts → site-config-UTzDG-UG.d.ts} +1 -1
  67. package/dist/{track-page-view-QRSk8vap.d.ts → track-page-view-CMAqyhD_.d.ts} +1 -1
  68. package/dist/{track-page-view-CkuSYuco.d.cts → track-page-view-tX0Magkd.d.cts} +1 -1
  69. package/dist/{types-C1eq8E5u.d.cts → types-B-111Wht.d.cts} +41 -2
  70. package/dist/{types-C1eq8E5u.d.ts → types-B-111Wht.d.ts} +41 -2
  71. package/package.json +11 -2
  72. package/dist/chunk-TFHOTBOL.js.map +0 -1
  73. /package/dist/{chunk-PKGUBYZR.js.map → chunk-DNYJF7BG.js.map} +0 -0
  74. /package/dist/{chunk-WFTTIRDJ.js.map → chunk-FNWE2DT5.js.map} +0 -0
  75. /package/dist/{chunk-XQZOGWF2.js.map → chunk-KZ4WVDGL.js.map} +0 -0
@@ -28,6 +28,9 @@ __export(content_exports, {
28
28
  loadBlogPosts: () => loadBlogPosts2,
29
29
  loadFaqBySlug: () => loadFaqBySlug2,
30
30
  loadFaqs: () => loadFaqs2,
31
+ loadLandingPageDetail: () => loadLandingPageDetail2,
32
+ loadLandingPageSitemap: () => loadLandingPageSitemap2,
33
+ loadLandingPages: () => loadLandingPages2,
31
34
  loadSiteProfile: () => loadSiteProfile2,
32
35
  loadTrustBundle: () => loadTrustBundle2,
33
36
  resolveSiteContact: () => resolveSiteContact2
@@ -39,7 +42,7 @@ var import_react = require("react");
39
42
  var LANDING_REFERER_COOKIE_MAX_AGE_SEC = 30 * 60;
40
43
 
41
44
  // src/constants.ts
42
- var JMP_ANALYTICS_SDK_VERSION = "0.1.25";
45
+ var JMP_ANALYTICS_SDK_VERSION = "0.1.26";
43
46
 
44
47
  // src/http.ts
45
48
  function responseLooksLikeHtml(body) {
@@ -254,6 +257,53 @@ async function getJmpFaqs(config) {
254
257
  afterContentWrapperUse(config);
255
258
  return data.faqs;
256
259
  }
260
+ async function getJmpLandingPages(config, filters) {
261
+ const params = new URLSearchParams();
262
+ if (filters?.kind) {
263
+ params.set("kind", filters.kind);
264
+ }
265
+ if (filters?.locale) {
266
+ params.set("locale", filters.locale);
267
+ }
268
+ const query = params.toString();
269
+ const data = await getContent(
270
+ config,
271
+ `/api/content/landing-pages${query ? `?${query}` : ""}`
272
+ );
273
+ markContentWrapper("landing");
274
+ afterContentWrapperUse(config);
275
+ return data.pages;
276
+ }
277
+ async function getJmpLandingPageDetail(config, kind, slug, locale) {
278
+ const params = new URLSearchParams();
279
+ if (locale) {
280
+ params.set("locale", locale);
281
+ }
282
+ const query = params.toString();
283
+ try {
284
+ const detail = await getContent(
285
+ config,
286
+ `/api/content/landing-pages/${encodeURIComponent(kind)}/${encodeURIComponent(slug)}${query ? `?${query}` : ""}`
287
+ );
288
+ markContentWrapper("landing");
289
+ afterContentWrapperUse(config);
290
+ return detail;
291
+ } catch (error) {
292
+ if (error instanceof Error && error.message.includes("HTTP 404")) {
293
+ return null;
294
+ }
295
+ throw error;
296
+ }
297
+ }
298
+ async function getJmpLandingPageSitemap(config) {
299
+ const data = await getContent(
300
+ config,
301
+ "/api/content/landing-pages/sitemap"
302
+ );
303
+ markContentWrapper("landing");
304
+ afterContentWrapperUse(config);
305
+ return data.entries;
306
+ }
257
307
  async function getJmpTrustBundle(config) {
258
308
  const bundle = await getContent(config, "/api/content/trust");
259
309
  markTrustBundleWrappers(bundle);
@@ -505,6 +555,39 @@ async function loadFaqBySlug(slug) {
505
555
  const items = await loadFaqs();
506
556
  return items.find((item) => item.slug === normalized || item.id === normalized) ?? null;
507
557
  }
558
+ async function loadLandingPages(filters) {
559
+ const c = getJmpContentConfig();
560
+ if (!c) {
561
+ return [];
562
+ }
563
+ try {
564
+ return await getJmpLandingPages(c, filters);
565
+ } catch {
566
+ return [];
567
+ }
568
+ }
569
+ async function loadLandingPageDetail(kind, slug, locale) {
570
+ const c = getJmpContentConfig();
571
+ if (!c) {
572
+ return null;
573
+ }
574
+ try {
575
+ return await getJmpLandingPageDetail(c, kind, slug, locale);
576
+ } catch {
577
+ return null;
578
+ }
579
+ }
580
+ async function loadLandingPageSitemap() {
581
+ const c = getJmpContentConfig();
582
+ if (!c) {
583
+ return [];
584
+ }
585
+ try {
586
+ return await getJmpLandingPageSitemap(c);
587
+ } catch {
588
+ return [];
589
+ }
590
+ }
508
591
  async function loadTrustBundle() {
509
592
  const c = getJmpContentConfig();
510
593
  if (!c) {
@@ -539,6 +622,9 @@ var loadBlogPosts2 = (0, import_react.cache)(loadBlogPosts);
539
622
  var loadBlogPost2 = (0, import_react.cache)(loadBlogPost);
540
623
  var loadFaqs2 = (0, import_react.cache)(loadFaqs);
541
624
  var loadFaqBySlug2 = loadFaqBySlug;
625
+ var loadLandingPages2 = (0, import_react.cache)(loadLandingPages);
626
+ var loadLandingPageDetail2 = loadLandingPageDetail;
627
+ var loadLandingPageSitemap2 = (0, import_react.cache)(loadLandingPageSitemap);
542
628
  var loadTrustBundle2 = (0, import_react.cache)(loadTrustBundle);
543
629
  // Annotate the CommonJS export names for ESM import in node:
544
630
  0 && (module.exports = {
@@ -550,6 +636,9 @@ var loadTrustBundle2 = (0, import_react.cache)(loadTrustBundle);
550
636
  loadBlogPosts,
551
637
  loadFaqBySlug,
552
638
  loadFaqs,
639
+ loadLandingPageDetail,
640
+ loadLandingPageSitemap,
641
+ loadLandingPages,
553
642
  loadSiteProfile,
554
643
  loadTrustBundle,
555
644
  resolveSiteContact
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/next/content.ts","../../src/landing-capture.ts","../../src/constants.ts","../../src/http.ts","../../src/content/fetch.ts","../../src/content/wrapper-discovery.ts","../../src/content/api.ts","../../src/env-public.ts","../../src/site-config.ts","../../src/content/site-content.ts"],"sourcesContent":["import { cache } from \"react\"\n\nimport * as siteContent from \"../content/site-content\"\n\nexport type {\n JmpBlogPost,\n JmpFaqItem,\n JmpSiteProfile,\n JmpTestimonial,\n JmpTrustBundle,\n ResolvedSiteContact,\n} from \"../content/site-content\"\n\nexport { getJmpContentConfig } from \"../content/site-content\"\n\nexport const absoluteSiteAssetUrl = siteContent.absoluteSiteAssetUrl\nexport const resolveSiteContact = siteContent.resolveSiteContact\nexport const filterFeaturedTrustQuotes = siteContent.filterFeaturedTrustQuotes\n\nexport const loadSiteProfile = cache(siteContent.loadSiteProfile)\nexport const getResolvedSiteContact = cache(siteContent.getResolvedSiteContact)\nexport const loadBlogPosts = cache(siteContent.loadBlogPosts)\nexport const loadBlogPost = cache(siteContent.loadBlogPost)\nexport const loadFaqs = cache(siteContent.loadFaqs)\nexport const loadFaqBySlug = siteContent.loadFaqBySlug\nexport const loadTrustBundle = cache(siteContent.loadTrustBundle)\n","/** First-party landing attribution captured before React / useEffect (sessionStorage). */\n\nexport const JMP_SESSION_ID_KEY = \"jmp_analytics_sid\"\nexport const JMP_LANDING_REFERER_KEY = \"jmp_analytics_landing_referer\"\n/** Cross-subdomain landing referrer (www ↔ apex share sessionStorage origins). */\nexport const JMP_LANDING_REFERER_COOKIE = \"jmp_lr\"\nexport const JMP_UTM_SESSION_KEY = \"jmp_analytics_utm\"\n\nconst LANDING_REFERER_COOKIE_MAX_AGE_SEC = 30 * 60\n\nexport const JMP_UTM_PARAM_KEYS = [\n \"utm_source\",\n \"utm_medium\",\n \"utm_campaign\",\n \"utm_content\",\n \"utm_term\",\n] as const\n\nfunction refererHostFromUrl(referer: string): string | null {\n const trimmed = referer.trim()\n try {\n return new URL(trimmed).hostname.replace(/^www\\./i, \"\").toLowerCase()\n } catch {\n try {\n return new URL(`https://${trimmed}`).hostname\n .replace(/^www\\./i, \"\")\n .toLowerCase()\n } catch {\n return null\n }\n }\n}\n\nexport function isExternalReferrerUrl(\n referer: string,\n siteHostname: string\n): boolean {\n const refHost = refererHostFromUrl(referer)\n if (!refHost) {\n return false\n }\n const site = siteHostname.replace(/^www\\./i, \"\").toLowerCase()\n return refHost !== site && !refHost.endsWith(`.${site}`)\n}\n\n/** Apex hostname used for `Domain=.example.com` cookies (www + apex). */\nexport function registrableSiteDomain(siteHostname: string): string | null {\n const site = siteHostname.replace(/^www\\./i, \"\").toLowerCase()\n if (!site || site === \"localhost\" || site.endsWith(\".local\")) {\n return null\n }\n if (!site.includes(\".\")) {\n return null\n }\n return site\n}\n\nexport function readLandingRefererCookie(siteHostname: string): string | null {\n if (typeof document === \"undefined\") {\n return null\n }\n\n const apex = registrableSiteDomain(siteHostname)\n if (!apex) {\n return null\n }\n\n const prefix = `${JMP_LANDING_REFERER_COOKIE}=`\n for (const part of document.cookie.split(\";\")) {\n const trimmed = part.trim()\n if (!trimmed.startsWith(prefix)) {\n continue\n }\n try {\n const value = decodeURIComponent(trimmed.slice(prefix.length)).trim()\n return value.length > 0 ? value : null\n } catch {\n return null\n }\n }\n\n return null\n}\n\nexport function writeLandingRefererCookie(\n referer: string,\n siteHostname: string\n): void {\n if (typeof document === \"undefined\") {\n return\n }\n\n const apex = registrableSiteDomain(siteHostname)\n if (!apex) {\n return\n }\n\n const value = encodeURIComponent(referer.trim().slice(0, 2048))\n const secure =\n typeof window !== \"undefined\" && window.location.protocol === \"https:\"\n ? \"; Secure\"\n : \"\"\n\n document.cookie = `${JMP_LANDING_REFERER_COOKIE}=${value}; Domain=.${apex}; Path=/; Max-Age=${LANDING_REFERER_COOKIE_MAX_AGE_SEC}; SameSite=Lax${secure}`\n}\n\nexport function captureLandingAttribution(siteHostname: string): {\n landingReferer: string | null\n utm: Record<string, string>\n} {\n if (typeof document === \"undefined\" || typeof window === \"undefined\") {\n return { landingReferer: null, utm: {} }\n }\n\n let landingReferer: string | null = null\n let utm: Record<string, string> = {}\n\n try {\n const storedReferer =\n sessionStorage.getItem(JMP_LANDING_REFERER_KEY)?.trim() || null\n const cookieReferer = readLandingRefererCookie(siteHostname)\n const preservedReferer = storedReferer || cookieReferer\n const currentReferer = document.referrer?.trim() || null\n\n if (\n currentReferer &&\n isExternalReferrerUrl(currentReferer, siteHostname)\n ) {\n sessionStorage.setItem(JMP_LANDING_REFERER_KEY, currentReferer)\n writeLandingRefererCookie(currentReferer, siteHostname)\n landingReferer = currentReferer\n } else if (preservedReferer) {\n landingReferer = preservedReferer\n if (!storedReferer) {\n sessionStorage.setItem(JMP_LANDING_REFERER_KEY, preservedReferer)\n }\n }\n\n const params = new URLSearchParams(window.location.search)\n const rawUtm = sessionStorage.getItem(JMP_UTM_SESSION_KEY)\n if (rawUtm) {\n const parsed = JSON.parse(rawUtm) as unknown\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n utm = parsed as Record<string, string>\n }\n }\n\n let updated = false\n for (const key of JMP_UTM_PARAM_KEYS) {\n const value = params.get(key)?.trim()\n if (value) {\n utm[key] = value\n updated = true\n }\n }\n\n if (updated) {\n sessionStorage.setItem(JMP_UTM_SESSION_KEY, JSON.stringify(utm))\n }\n } catch {\n }\n\n return { landingReferer, utm }\n}\n\nexport function getOrCreateSessionIdFromStorage(): string | undefined {\n if (typeof window === \"undefined\" || !globalThis.crypto?.randomUUID) {\n return undefined\n }\n try {\n let id = sessionStorage.getItem(JMP_SESSION_ID_KEY)\n if (!id) {\n id = crypto.randomUUID()\n sessionStorage.setItem(JMP_SESSION_ID_KEY, id)\n }\n return id\n } catch {\n return undefined\n }\n}\n\n/** Inline script for Next.js `beforeInteractive` — capture referrer/UTM only (no network). */\nexport function buildLandingCaptureInlineScript(): string {\n const cookieMaxAge = String(LANDING_REFERER_COOKIE_MAX_AGE_SEC)\n\n return `(function(){try{var h=location.hostname;var ref=document.referrer||'';function rh(u){try{return new URL(u).hostname.replace(/^www\\\\./i,'').toLowerCase()}catch(e){try{return new URL('https://'+u).hostname.replace(/^www\\\\./i,'').toLowerCase()}catch(e2){return null}}}function ext(r){var x=rh(r);if(!x)return false;var s=h.replace(/^www\\\\./i,'').toLowerCase();return x!==s&&x.slice(-('.'+s).length)!==('.'+s)}function apexDom(hn){var s=hn.replace(/^www\\\\./i,'').toLowerCase();if(!s||s==='localhost'||/\\\\.local$/i.test(s)||s.indexOf('.')<0)return null;return s}function readCk(hn){var apex=apexDom(hn);if(!apex)return '';var pre='${JMP_LANDING_REFERER_COOKIE}=';var parts=document.cookie.split(';');for(var i=0;i<parts.length;i++){var t=parts[i].trim();if(t.indexOf(pre)===0){try{return decodeURIComponent(t.slice(pre.length)).trim()}catch(e){return ''}}}return ''}function writeCk(r,hn){var apex=apexDom(hn);if(!apex)return;var sec=location.protocol==='https:'?'; Secure':'';document.cookie='${JMP_LANDING_REFERER_COOKIE}='+encodeURIComponent(r.slice(0,2048))+'; Domain=.'+apex+'; Path=/; Max-Age=${cookieMaxAge}; SameSite=Lax'+sec}var stored=sessionStorage.getItem('${JMP_LANDING_REFERER_KEY}')||'';var ck=readCk(h);var preserved=stored||ck;var lr='';if(ref&&ext(ref)){sessionStorage.setItem('${JMP_LANDING_REFERER_KEY}',ref);writeCk(ref,h);lr=ref}else{lr=preserved||'';if(lr&&!stored)sessionStorage.setItem('${JMP_LANDING_REFERER_KEY}',lr)}var sid=sessionStorage.getItem('${JMP_SESSION_ID_KEY}');if(!sid&&crypto&&crypto.randomUUID){sid=crypto.randomUUID();sessionStorage.setItem('${JMP_SESSION_ID_KEY}',sid)}try{var sp=new URLSearchParams(location.search);var utm={};var updated=false;['utm_source','utm_medium','utm_campaign','utm_content','utm_term'].forEach(function(n){var v=sp.get(n);if(v){utm[n]=v;updated=true}});if(updated)sessionStorage.setItem('${JMP_UTM_SESSION_KEY}',JSON.stringify(utm))}catch(e){}}catch(e){}})();`\n}\n","/** Published package version — sent with capabilities for admin readiness. */\nexport const JMP_ANALYTICS_SDK_VERSION = \"0.1.25\"\n\n/** `localStorage` key when {@link JmpAnalyticsConfig.requireConsent} is enabled. */\nexport const JMP_TRACKING_CONSENT_STORAGE_KEY = \"jmp_analytics_consent_v1\"\n\n/** Default `pageViewDedupeMs` when resolving browser config from env (Strict Mode / SPA). */\nexport const DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS = 2500\n\n/** Marks links tracked explicitly — skips delegated `autoTrackContactLinks` handler. */\nexport const JMP_TRACKED_LINK_ATTR = \"data-jmp-tracked\"\n","import type { JmpServerRequestLike } from \"./types\"\n\n/** CDN / Next.js often return HTML error pages; API errors are JSON */\nexport function responseLooksLikeHtml(body: string): boolean {\n const s = body.trimStart()\n return (\n s.startsWith(\"<!\") ||\n s.toLowerCase().startsWith(\"<html\") ||\n (s.startsWith(\"<\") && /<html[\\s>]/i.test(s.slice(0, 500)))\n )\n}\n\nexport function normalizeBaseUrl(baseUrl: string): string {\n return baseUrl.replace(/\\/$/, \"\")\n}\n\nexport function getHeader(\n headers: JmpServerRequestLike[\"headers\"] | undefined,\n key: string\n): string | null {\n if (!headers) {\n return null\n }\n if (typeof (headers as Pick<Headers, \"get\">).get === \"function\") {\n return (headers as Pick<Headers, \"get\">).get(key)\n }\n const record = headers as Record<string, string | string[] | undefined | null>\n const direct = record[key] ?? record[key.toLowerCase()]\n if (Array.isArray(direct)) {\n return direct[0] ?? null\n }\n return direct ?? null\n}\n\nexport function pathFromRequestUrl(url: string | undefined): string {\n if (!url) {\n return \"/\"\n }\n try {\n const u = new URL(url)\n return `${u.pathname}${u.search}`\n } catch {\n return url.startsWith(\"/\") ? url : \"/\"\n }\n}\n\nexport function resolveFetch(\n fetchImpl: typeof fetch | undefined,\n context: string\n): typeof fetch {\n const resolved =\n fetchImpl ??\n (typeof globalThis !== \"undefined\" && globalThis.fetch\n ? globalThis.fetch.bind(globalThis)\n : undefined)\n if (!resolved) {\n throw new Error(`${context}: fetch is not available; pass fetchImpl in config`)\n }\n return resolved\n}\n\ntype ApiErrorJson = {\n error?: string\n code?: string\n hint?: string\n expectedDomain?: string\n}\n\nexport function formatApiErrorMessage(\n status: number,\n text: string,\n prefix: string\n): string {\n if (responseLooksLikeHtml(text)) {\n return `${prefix} HTTP ${status} — response was HTML (not the JSON API). Check baseUrl points at the JMP Analytics dashboard.`\n }\n let parsed: ApiErrorJson = {}\n try {\n parsed = JSON.parse(text) as ApiErrorJson\n } catch {\n parsed = { error: text }\n }\n const detail = parsed.error ?? text\n return [\n `${prefix} HTTP ${status}`,\n parsed.code ? `[${parsed.code}]` : null,\n detail,\n parsed.hint ? `Hint: ${parsed.hint}` : null,\n parsed.expectedDomain\n ? `Domain in JMP admin: ${parsed.expectedDomain}`\n : null,\n ]\n .filter(Boolean)\n .join(\" — \")\n}\n","import { normalizeBaseUrl, resolveFetch, responseLooksLikeHtml } from \"../http\"\nimport type { JmpContentConfig } from \"../types\"\n\nexport async function getContent<T>(\n config: JmpContentConfig,\n path: string\n): Promise<T> {\n const text = await getContentText(config, path)\n\n try {\n return JSON.parse(text) as T\n } catch {\n const baseUrl = normalizeBaseUrl(config.baseUrl)\n const url = `${baseUrl}${path}`\n throw new Error(`JMP content HTTP 200 — ${url} returned invalid JSON.`)\n }\n}\n\nexport async function getContentText(\n config: JmpContentConfig,\n path: string\n): Promise<string> {\n const baseUrl = normalizeBaseUrl(config.baseUrl)\n const resolvedFetch =\n config.fetchImpl ??\n (typeof globalThis !== \"undefined\" && globalThis.fetch\n ? globalThis.fetch.bind(globalThis)\n : undefined)\n\n if (!resolvedFetch) {\n throw new Error(\"JMP content: fetch is not available; pass fetchImpl in config\")\n }\n\n const url = `${baseUrl}${path}`\n\n if (config.debug) {\n console.debug(\"[@jmp-technologies/analytics]\", \"GET\", url)\n }\n\n const res = await resolvedFetch(url, {\n method: \"GET\",\n headers: {\n \"X-JMP-Tracking-Key\": config.trackingKey,\n },\n })\n\n const text = await res.text()\n\n if (!res.ok) {\n if (responseLooksLikeHtml(text)) {\n throw new Error(\n `JMP content HTTP ${res.status} — ${url} returned HTML (not the JSON API). ` +\n `Set baseUrl to your JMP Analytics dashboard origin.`\n )\n }\n\n let parsed: { error?: string; hint?: string } = {}\n try {\n parsed = JSON.parse(text) as typeof parsed\n } catch {\n parsed = { error: text }\n }\n\n throw new Error(\n [`JMP content HTTP ${res.status}`, parsed.error, parsed.hint]\n .filter(Boolean)\n .join(\" — \")\n )\n }\n\n return text\n}\n","import type { JmpContentConfig, JmpContentWrapper, JmpTrustBundle } from \"../types\"\n\n/** Stable report order (matches dashboard `CLIENT_REPORTABLE_WRAPPER_SLOTS`). */\nconst WRAPPER_SLOT_ORDER: readonly string[] = [\n \"blog\",\n \"faq\",\n \"contact\",\n \"trust\",\n \"trust.summary\",\n \"trust.testimonials\",\n \"trust.reviews\",\n \"trust.stats\",\n \"trust.logos\",\n \"trust.credentials\",\n]\n\nconst discovered = new Set<string>()\nlet syncTimer: ReturnType<typeof setTimeout> | null = null\nlet pendingSyncConfig: JmpContentConfig | null = null\n\nconst reportedDedupeKeys = new Set<string>()\nlet syncHandler: ((config: JmpContentConfig) => Promise<unknown>) | null = null\n\nexport function markContentWrapper(slot: JmpContentWrapper | string): void {\n const normalized = slot.trim().toLowerCase()\n if (!normalized) {\n return\n }\n discovered.add(normalized)\n}\n\nexport function markTrustBundleWrappers(bundle: JmpTrustBundle): void {\n for (const slot of inferTrustWrapperSlots(bundle)) {\n discovered.add(slot)\n }\n}\n\nexport function inferTrustWrapperSlots(bundle: JmpTrustBundle): string[] {\n const slots = new Set<string>([\"trust\"])\n const { summary, testimonials, stats, logos, credentials } = bundle\n\n const hasSummary =\n summary.google_rating != null ||\n summary.google_review_count != null ||\n Boolean(summary.google_reviews_url?.trim()) ||\n Boolean(summary.yelp_url?.trim()) ||\n Boolean(summary.trustpilot_url?.trim()) ||\n Boolean(summary.disclaimer?.trim())\n\n if (hasSummary) {\n slots.add(\"trust.summary\")\n }\n\n let hasTestimonials = false\n let hasReviews = false\n for (const item of testimonials) {\n const bucket = item.placement?.trim().toLowerCase()\n if (bucket === \"reviews\") {\n hasReviews = true\n } else {\n hasTestimonials = true\n }\n }\n if (hasTestimonials) {\n slots.add(\"trust.testimonials\")\n }\n if (hasReviews) {\n slots.add(\"trust.reviews\")\n }\n if (stats.length > 0) {\n slots.add(\"trust.stats\")\n }\n if (logos.length > 0) {\n slots.add(\"trust.logos\")\n }\n if (credentials.length > 0) {\n slots.add(\"trust.credentials\")\n }\n\n return sortWrapperSlots([...slots])\n}\n\nexport function getDiscoveredContentWrappers(): string[] {\n return sortWrapperSlots([...discovered])\n}\n\nexport function resolveContentWrappersForReport(\n config: JmpContentConfig\n): string[] {\n const explicit = config.contentWrappers ?? []\n const auto = getDiscoveredContentWrappers()\n\n if (config.contentWrapperMode === \"manual\") {\n return sortWrapperSlots([...explicit])\n }\n\n if (config.contentWrapperMode === \"explicit-only\") {\n return sortWrapperSlots([...explicit])\n }\n\n const merged = new Set<string>([...explicit, ...auto])\n return sortWrapperSlots([...merged])\n}\n\nexport function hasReportedContentWrappers(dedupeKey: string): boolean {\n return reportedDedupeKeys.has(dedupeKey)\n}\n\nexport function rememberReportedContentWrappers(dedupeKey: string): void {\n reportedDedupeKeys.add(dedupeKey)\n}\n\nexport function setContentWrapperSyncHandler(\n handler: (config: JmpContentConfig) => Promise<unknown>\n): void {\n syncHandler = handler\n}\n\n/**\n * Debounced POST after content APIs run (build SSG, first page hit, etc.).\n */\nexport function scheduleContentWrapperSync(config: JmpContentConfig): void {\n if (!syncHandler) {\n return\n }\n pendingSyncConfig = config\n if (syncTimer) {\n clearTimeout(syncTimer)\n }\n syncTimer = setTimeout(() => {\n syncTimer = null\n const cfg = pendingSyncConfig\n pendingSyncConfig = null\n if (!cfg || !syncHandler) {\n return\n }\n void syncHandler(cfg).catch(() => {})\n }, 750)\n}\n\nexport function resetContentWrapperDiscoveryForTests(): void {\n discovered.clear()\n reportedDedupeKeys.clear()\n syncHandler = null\n if (syncTimer) {\n clearTimeout(syncTimer)\n syncTimer = null\n }\n pendingSyncConfig = null\n}\n\nfunction sortWrapperSlots(slots: string[]): string[] {\n const order = new Map(WRAPPER_SLOT_ORDER.map((slot, index) => [slot, index]))\n return [...new Set(slots)].sort((a, b) => {\n const ai = order.get(a) ?? 999\n const bi = order.get(b) ?? 999\n if (ai !== bi) {\n return ai - bi\n }\n return a.localeCompare(b)\n })\n}\n","import { JMP_ANALYTICS_SDK_VERSION } from \"../constants\"\nimport { normalizeBaseUrl, resolveFetch } from \"../http\"\nimport type {\n JmpBlogPost,\n JmpContentConfig,\n JmpFaqItem,\n JmpSiteProfile,\n JmpTrustBundle,\n} from \"../types\"\nimport { getContent, getContentText } from \"./fetch\"\nimport {\n hasReportedContentWrappers,\n markContentWrapper,\n markTrustBundleWrappers,\n rememberReportedContentWrappers,\n resolveContentWrappersForReport,\n scheduleContentWrapperSync,\n setContentWrapperSyncHandler,\n} from \"./wrapper-discovery\"\n\nexport {\n getDiscoveredContentWrappers,\n inferTrustWrapperSlots,\n markContentWrapper,\n markTrustBundleWrappers,\n resetContentWrapperDiscoveryForTests,\n} from \"./wrapper-discovery\"\n\nfunction afterContentWrapperUse(config: JmpContentConfig): void {\n scheduleContentWrapperSync(config)\n}\n\nexport async function getJmpBlogPosts(\n config: JmpContentConfig\n): Promise<JmpBlogPost[]> {\n const data = await getContent<{ posts: JmpBlogPost[] }>(\n config,\n \"/api/content/blog\"\n )\n markContentWrapper(\"blog\")\n afterContentWrapperUse(config)\n return data.posts\n}\n\nexport async function getJmpBlogPost(\n config: JmpContentConfig,\n slug: string\n): Promise<JmpBlogPost | null> {\n try {\n const data = await getContent<{ post: JmpBlogPost }>(\n config,\n `/api/content/blog/${encodeURIComponent(slug)}`\n )\n markContentWrapper(\"blog\")\n afterContentWrapperUse(config)\n return data.post\n } catch (error) {\n if (error instanceof Error && error.message.includes(\"HTTP 404\")) {\n return null\n }\n throw error\n }\n}\n\nexport async function getJmpFaqs(\n config: JmpContentConfig\n): Promise<JmpFaqItem[]> {\n const data = await getContent<{ faqs: JmpFaqItem[] }>(\n config,\n \"/api/content/faqs\"\n )\n markContentWrapper(\"faq\")\n afterContentWrapperUse(config)\n return data.faqs\n}\n\nexport async function getJmpTrustBundle(\n config: JmpContentConfig\n): Promise<JmpTrustBundle> {\n const bundle = await getContent<JmpTrustBundle>(config, \"/api/content/trust\")\n markTrustBundleWrappers(bundle)\n afterContentWrapperUse(config)\n return bundle\n}\n\nexport async function getJmpSiteProfile(\n config: JmpContentConfig\n): Promise<JmpSiteProfile> {\n const data = await getContent<{ site: JmpSiteProfile }>(\n config,\n \"/api/content/site\"\n )\n markContentWrapper(\"contact\")\n afterContentWrapperUse(config)\n return data.site\n}\n\nexport async function getJmpRobotsTxt(config: JmpContentConfig): Promise<string> {\n return getContentText(config, \"/api/content/robots\")\n}\n\n/**\n * Tell the JMP dashboard which content wrappers this site wires (blog, faq, trust.*, contact).\n * Uses auto-discovered slots from content API usage unless `contentWrapperMode` is `manual`.\n * Safe to call on every cold start; dedupes by tracking key + wrapper list.\n */\nexport async function registerJmpContentWrappers(\n config: JmpContentConfig\n): Promise<\n { ok: true; wrappers: string[] } | { ok: false; skipped: true }\n> {\n const wrappers = resolveContentWrappersForReport(config)\n if (!wrappers.length) {\n return { ok: false, skipped: true }\n }\n\n const dedupeKey = `${config.trackingKey}:${[...wrappers].sort().join(\",\")}`\n if (hasReportedContentWrappers(dedupeKey)) {\n return { ok: true, wrappers }\n }\n\n const baseUrl = normalizeBaseUrl(config.baseUrl)\n const resolvedFetch =\n config.fetchImpl ??\n (typeof globalThis !== \"undefined\" && globalThis.fetch\n ? globalThis.fetch.bind(globalThis)\n : undefined)\n\n if (!resolvedFetch) {\n throw new Error(\n \"JMP content wrappers: fetch is not available; pass fetchImpl in config\"\n )\n }\n\n const url = `${baseUrl}/api/content/capabilities`\n if (config.debug) {\n console.debug(\"[@jmp-technologies/analytics]\", \"POST\", url, { wrappers })\n }\n\n const res = await resolvedFetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-JMP-Tracking-Key\": config.trackingKey,\n },\n body: JSON.stringify({\n wrappers,\n sdkVersion: JMP_ANALYTICS_SDK_VERSION,\n }),\n })\n\n const text = await res.text()\n if (!res.ok) {\n let parsed: { error?: string; hint?: string } = {}\n try {\n parsed = JSON.parse(text) as typeof parsed\n } catch {\n parsed = { error: text }\n }\n throw new Error(\n [`JMP content wrappers HTTP ${res.status}`, parsed.error, parsed.hint]\n .filter(Boolean)\n .join(\" — \")\n )\n }\n\n rememberReportedContentWrappers(dedupeKey)\n const data = JSON.parse(text) as { wrappers?: string[] }\n return { ok: true, wrappers: data.wrappers ?? wrappers }\n}\n\nsetContentWrapperSyncHandler(registerJmpContentWrappers)\n","/**\n * Public env vars for JMP — supports Next.js (`NEXT_PUBLIC_*`) and Astro (`PUBLIC_*`).\n * Server/build: `process.env`. Astro client bundles: pass overrides from `import.meta.env`.\n */\n\nexport type JmpPublicEnv = {\n analyticsUrl?: string\n trackingKey?: string\n requireConsent?: boolean\n autoContactLinks?: boolean\n}\n\nfunction firstDefined(...values: (string | undefined)[]): string | undefined {\n for (const v of values) {\n if (v) return v\n }\n return undefined\n}\n\n/** Static reads so Next.js / Vite inline `NEXT_PUBLIC_*` into client bundles. */\nfunction readJmpPublicEnvFromProcessEnv(): Omit<JmpPublicEnv, never> {\n if (typeof process === \"undefined\" || !process.env) {\n return {}\n }\n const { env } = process\n const analyticsUrl = firstDefined(\n env.NEXT_PUBLIC_JMP_ANALYTICS_URL,\n env.PUBLIC_JMP_ANALYTICS_URL,\n )\n const trackingKey = firstDefined(\n env.NEXT_PUBLIC_JMP_TRACKING_KEY,\n env.PUBLIC_JMP_TRACKING_KEY,\n )\n const requireConsentRaw = firstDefined(\n env.NEXT_PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT,\n env.PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT,\n )\n const autoContactRaw = firstDefined(\n env.NEXT_PUBLIC_JMP_AUTO_CONTACT_LINKS,\n env.PUBLIC_JMP_AUTO_CONTACT_LINKS,\n )\n\n return {\n analyticsUrl,\n trackingKey,\n requireConsent: requireConsentRaw === \"true\",\n autoContactLinks: autoContactRaw === \"true\",\n }\n}\n\n/** Read dashboard URL + tracking key from Node / build-time env. */\nexport function readJmpPublicEnvFromProcess(\n overrides?: Partial<JmpPublicEnv>,\n): JmpPublicEnv {\n const fromEnv = readJmpPublicEnvFromProcessEnv()\n\n return {\n analyticsUrl: firstDefined(overrides?.analyticsUrl, fromEnv.analyticsUrl),\n trackingKey: firstDefined(overrides?.trackingKey, fromEnv.trackingKey),\n requireConsent: overrides?.requireConsent ?? fromEnv.requireConsent,\n autoContactLinks:\n overrides?.autoContactLinks ?? fromEnv.autoContactLinks,\n }\n}\n\n/** Read from Astro/Vite `import.meta.env` in client scripts. */\nexport function readJmpPublicEnvFromImportMeta(\n meta: Record<string, unknown> | undefined,\n overrides?: Partial<JmpPublicEnv>,\n): JmpPublicEnv {\n const get = (key: string): string | undefined => {\n const v = meta?.[key]\n return typeof v === \"string\" && v.length > 0 ? v : undefined\n }\n\n const analyticsUrl = firstDefined(\n overrides?.analyticsUrl,\n get(\"PUBLIC_JMP_ANALYTICS_URL\"),\n get(\"NEXT_PUBLIC_JMP_ANALYTICS_URL\"),\n )\n const trackingKey = firstDefined(\n overrides?.trackingKey,\n get(\"PUBLIC_JMP_TRACKING_KEY\"),\n get(\"NEXT_PUBLIC_JMP_TRACKING_KEY\"),\n )\n const requireConsentRaw = firstDefined(\n overrides?.requireConsent !== undefined\n ? String(overrides.requireConsent)\n : undefined,\n get(\"PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT\"),\n get(\"NEXT_PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT\"),\n )\n const autoContactRaw = firstDefined(\n overrides?.autoContactLinks !== undefined\n ? String(overrides.autoContactLinks)\n : undefined,\n get(\"PUBLIC_JMP_AUTO_CONTACT_LINKS\"),\n get(\"NEXT_PUBLIC_JMP_AUTO_CONTACT_LINKS\"),\n )\n\n return {\n analyticsUrl,\n trackingKey,\n requireConsent: requireConsentRaw === \"true\",\n autoContactLinks: autoContactRaw === \"true\",\n }\n}\n","import type { JmpContentConfig, JmpContentWrapper } from \"./types\"\nimport { readJmpPublicEnvFromProcess } from \"./env-public\"\n\n/** Per-site values used when the dashboard profile is missing or fields are empty. */\nexport type JmpSiteContactFallback = {\n displayName?: string\n founderName?: string\n email?: string\n phone?: string | null\n linkedin?: string | null\n hours?: string | null\n serviceArea?: string | null\n dashboardDemoUrl?: string\n profilePhotoSrc?: string\n privacyUrl?: string\n termsUrl?: string\n}\n\nexport type JmpSiteDefinition = {\n /**\n * Optional override. Wrappers are auto-discovered when content loaders run\n * (`loadBlogPosts`, `loadFaqs`, etc.) and reported to the dashboard.\n */\n contentWrappers?: JmpContentWrapper[]\n /**\n * Public site origin (no trailing slash), e.g. `https://example.com`.\n * Used to resolve relative profile image paths in structured data.\n */\n siteUrl?: string\n /**\n * JMP dashboard origin (no trailing slash), e.g. `https://portal.example.com`.\n * Fallback when `NEXT_PUBLIC_JMP_ANALYTICS_URL` is missing from the client bundle.\n */\n analyticsPortalUrl?: string\n contactFallback?: JmpSiteContactFallback\n /**\n * `extras` keys to read from the dashboard site profile (hyphenated keys from admin).\n */\n contactExtraKeys?: {\n founderName?: readonly string[]\n dashboardDemoUrl?: readonly string[]\n profilePhotoSrc?: readonly string[]\n hours?: readonly string[]\n serviceArea?: readonly string[]\n }\n}\n\nlet registeredSite: JmpSiteDefinition | null = null\n\n/**\n * Register site-specific JMP integration options once (typically from `jmp.config.ts`).\n * Call at module load time before server content or instrumentation runs.\n */\nexport function defineJmpSite(definition: JmpSiteDefinition): JmpSiteDefinition {\n registeredSite = definition\n return definition\n}\n\nexport function getJmpSiteDefinition(): JmpSiteDefinition | null {\n return registeredSite\n}\n\n/** Server content + lead config: env vars + wrappers from {@link defineJmpSite}. */\nexport function getJmpContentConfig(): JmpContentConfig | null {\n const { analyticsUrl, trackingKey } = readJmpPublicEnvFromProcess()\n if (!analyticsUrl || !trackingKey) {\n return null\n }\n const site = getJmpSiteDefinition()\n return {\n baseUrl: analyticsUrl,\n trackingKey,\n contentWrappers: site?.contentWrappers ?? undefined,\n }\n}\n","import {\n getJmpBlogPost,\n getJmpBlogPosts,\n getJmpFaqs,\n getJmpSiteProfile,\n getJmpTrustBundle,\n type JmpBlogPost,\n type JmpFaqItem,\n type JmpSiteProfile,\n type JmpTestimonial,\n type JmpTrustBundle,\n} from \"../index\"\nimport { getJmpContentConfig, getJmpSiteDefinition } from \"../site-config\"\n\nexport type {\n JmpBlogPost,\n JmpFaqItem,\n JmpSiteProfile,\n JmpTestimonial,\n JmpTrustBundle,\n}\n\nexport { getJmpContentConfig } 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 phone: 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} 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 linkedin =\n profile?.social?.linkedin?.trim() || fallback.linkedin || null\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,\n phone,\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\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 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":["absoluteSiteAssetUrl","filterFeaturedTrustQuotes","getResolvedSiteContact","loadBlogPost","loadBlogPosts","loadFaqBySlug","loadFaqs","loadSiteProfile","loadTrustBundle","resolveSiteContact","absoluteSiteAssetUrl","resolveSiteContact","filterFeaturedTrustQuotes","loadSiteProfile","getResolvedSiteContact","loadBlogPosts","loadBlogPost","loadFaqs","loadFaqBySlug","loadTrustBundle"]}
1
+ {"version":3,"sources":["../../src/next/content.ts","../../src/landing-capture.ts","../../src/constants.ts","../../src/http.ts","../../src/content/fetch.ts","../../src/content/wrapper-discovery.ts","../../src/content/api.ts","../../src/env-public.ts","../../src/site-config.ts","../../src/content/site-content.ts"],"sourcesContent":["import { cache } from \"react\"\n\nimport * as siteContent from \"../content/site-content\"\n\nexport type {\n JmpBlogPost,\n JmpFaqItem,\n JmpLandingPage,\n JmpLandingPageDetail,\n JmpLandingPageKind,\n JmpLandingPageSitemapEntry,\n JmpSiteProfile,\n JmpTestimonial,\n JmpTrustBundle,\n ResolvedSiteContact,\n} from \"../content/site-content\"\n\nexport { getJmpContentConfig } from \"../content/site-content\"\n\nexport const absoluteSiteAssetUrl = siteContent.absoluteSiteAssetUrl\nexport const resolveSiteContact = siteContent.resolveSiteContact\nexport const filterFeaturedTrustQuotes = siteContent.filterFeaturedTrustQuotes\n\nexport const loadSiteProfile = cache(siteContent.loadSiteProfile)\nexport const getResolvedSiteContact = cache(siteContent.getResolvedSiteContact)\nexport const loadBlogPosts = cache(siteContent.loadBlogPosts)\nexport const loadBlogPost = cache(siteContent.loadBlogPost)\nexport const loadFaqs = cache(siteContent.loadFaqs)\nexport const loadFaqBySlug = siteContent.loadFaqBySlug\nexport const loadLandingPages = cache(siteContent.loadLandingPages)\nexport const loadLandingPageDetail = siteContent.loadLandingPageDetail\nexport const loadLandingPageSitemap = cache(siteContent.loadLandingPageSitemap)\nexport const loadTrustBundle = cache(siteContent.loadTrustBundle)\n","/** First-party landing attribution captured before React / useEffect (sessionStorage). */\n\nexport const JMP_SESSION_ID_KEY = \"jmp_analytics_sid\"\nexport const JMP_LANDING_REFERER_KEY = \"jmp_analytics_landing_referer\"\n/** Cross-subdomain landing referrer (www ↔ apex share sessionStorage origins). */\nexport const JMP_LANDING_REFERER_COOKIE = \"jmp_lr\"\nexport const JMP_UTM_SESSION_KEY = \"jmp_analytics_utm\"\n\nconst LANDING_REFERER_COOKIE_MAX_AGE_SEC = 30 * 60\n\nexport const JMP_UTM_PARAM_KEYS = [\n \"utm_source\",\n \"utm_medium\",\n \"utm_campaign\",\n \"utm_content\",\n \"utm_term\",\n] as const\n\nfunction refererHostFromUrl(referer: string): string | null {\n const trimmed = referer.trim()\n try {\n return new URL(trimmed).hostname.replace(/^www\\./i, \"\").toLowerCase()\n } catch {\n try {\n return new URL(`https://${trimmed}`).hostname\n .replace(/^www\\./i, \"\")\n .toLowerCase()\n } catch {\n return null\n }\n }\n}\n\nexport function isExternalReferrerUrl(\n referer: string,\n siteHostname: string\n): boolean {\n const refHost = refererHostFromUrl(referer)\n if (!refHost) {\n return false\n }\n const site = siteHostname.replace(/^www\\./i, \"\").toLowerCase()\n return refHost !== site && !refHost.endsWith(`.${site}`)\n}\n\n/** Apex hostname used for `Domain=.example.com` cookies (www + apex). */\nexport function registrableSiteDomain(siteHostname: string): string | null {\n const site = siteHostname.replace(/^www\\./i, \"\").toLowerCase()\n if (!site || site === \"localhost\" || site.endsWith(\".local\")) {\n return null\n }\n if (!site.includes(\".\")) {\n return null\n }\n return site\n}\n\nexport function readLandingRefererCookie(siteHostname: string): string | null {\n if (typeof document === \"undefined\") {\n return null\n }\n\n const apex = registrableSiteDomain(siteHostname)\n if (!apex) {\n return null\n }\n\n const prefix = `${JMP_LANDING_REFERER_COOKIE}=`\n for (const part of document.cookie.split(\";\")) {\n const trimmed = part.trim()\n if (!trimmed.startsWith(prefix)) {\n continue\n }\n try {\n const value = decodeURIComponent(trimmed.slice(prefix.length)).trim()\n return value.length > 0 ? value : null\n } catch {\n return null\n }\n }\n\n return null\n}\n\nexport function writeLandingRefererCookie(\n referer: string,\n siteHostname: string\n): void {\n if (typeof document === \"undefined\") {\n return\n }\n\n const apex = registrableSiteDomain(siteHostname)\n if (!apex) {\n return\n }\n\n const value = encodeURIComponent(referer.trim().slice(0, 2048))\n const secure =\n typeof window !== \"undefined\" && window.location.protocol === \"https:\"\n ? \"; Secure\"\n : \"\"\n\n document.cookie = `${JMP_LANDING_REFERER_COOKIE}=${value}; Domain=.${apex}; Path=/; Max-Age=${LANDING_REFERER_COOKIE_MAX_AGE_SEC}; SameSite=Lax${secure}`\n}\n\nexport function captureLandingAttribution(siteHostname: string): {\n landingReferer: string | null\n utm: Record<string, string>\n} {\n if (typeof document === \"undefined\" || typeof window === \"undefined\") {\n return { landingReferer: null, utm: {} }\n }\n\n let landingReferer: string | null = null\n let utm: Record<string, string> = {}\n\n try {\n const storedReferer =\n sessionStorage.getItem(JMP_LANDING_REFERER_KEY)?.trim() || null\n const cookieReferer = readLandingRefererCookie(siteHostname)\n const preservedReferer = storedReferer || cookieReferer\n const currentReferer = document.referrer?.trim() || null\n\n if (\n currentReferer &&\n isExternalReferrerUrl(currentReferer, siteHostname)\n ) {\n sessionStorage.setItem(JMP_LANDING_REFERER_KEY, currentReferer)\n writeLandingRefererCookie(currentReferer, siteHostname)\n landingReferer = currentReferer\n } else if (preservedReferer) {\n landingReferer = preservedReferer\n if (!storedReferer) {\n sessionStorage.setItem(JMP_LANDING_REFERER_KEY, preservedReferer)\n }\n }\n\n const params = new URLSearchParams(window.location.search)\n const rawUtm = sessionStorage.getItem(JMP_UTM_SESSION_KEY)\n if (rawUtm) {\n const parsed = JSON.parse(rawUtm) as unknown\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n utm = parsed as Record<string, string>\n }\n }\n\n let updated = false\n for (const key of JMP_UTM_PARAM_KEYS) {\n const value = params.get(key)?.trim()\n if (value) {\n utm[key] = value\n updated = true\n }\n }\n\n if (updated) {\n sessionStorage.setItem(JMP_UTM_SESSION_KEY, JSON.stringify(utm))\n }\n } catch {\n }\n\n return { landingReferer, utm }\n}\n\nexport function getOrCreateSessionIdFromStorage(): string | undefined {\n if (typeof window === \"undefined\" || !globalThis.crypto?.randomUUID) {\n return undefined\n }\n try {\n let id = sessionStorage.getItem(JMP_SESSION_ID_KEY)\n if (!id) {\n id = crypto.randomUUID()\n sessionStorage.setItem(JMP_SESSION_ID_KEY, id)\n }\n return id\n } catch {\n return undefined\n }\n}\n\n/** Inline script for Next.js `beforeInteractive` — capture referrer/UTM only (no network). */\nexport function buildLandingCaptureInlineScript(): string {\n const cookieMaxAge = String(LANDING_REFERER_COOKIE_MAX_AGE_SEC)\n\n return `(function(){try{var h=location.hostname;var ref=document.referrer||'';function rh(u){try{return new URL(u).hostname.replace(/^www\\\\./i,'').toLowerCase()}catch(e){try{return new URL('https://'+u).hostname.replace(/^www\\\\./i,'').toLowerCase()}catch(e2){return null}}}function ext(r){var x=rh(r);if(!x)return false;var s=h.replace(/^www\\\\./i,'').toLowerCase();return x!==s&&x.slice(-('.'+s).length)!==('.'+s)}function apexDom(hn){var s=hn.replace(/^www\\\\./i,'').toLowerCase();if(!s||s==='localhost'||/\\\\.local$/i.test(s)||s.indexOf('.')<0)return null;return s}function readCk(hn){var apex=apexDom(hn);if(!apex)return '';var pre='${JMP_LANDING_REFERER_COOKIE}=';var parts=document.cookie.split(';');for(var i=0;i<parts.length;i++){var t=parts[i].trim();if(t.indexOf(pre)===0){try{return decodeURIComponent(t.slice(pre.length)).trim()}catch(e){return ''}}}return ''}function writeCk(r,hn){var apex=apexDom(hn);if(!apex)return;var sec=location.protocol==='https:'?'; Secure':'';document.cookie='${JMP_LANDING_REFERER_COOKIE}='+encodeURIComponent(r.slice(0,2048))+'; Domain=.'+apex+'; Path=/; Max-Age=${cookieMaxAge}; SameSite=Lax'+sec}var stored=sessionStorage.getItem('${JMP_LANDING_REFERER_KEY}')||'';var ck=readCk(h);var preserved=stored||ck;var lr='';if(ref&&ext(ref)){sessionStorage.setItem('${JMP_LANDING_REFERER_KEY}',ref);writeCk(ref,h);lr=ref}else{lr=preserved||'';if(lr&&!stored)sessionStorage.setItem('${JMP_LANDING_REFERER_KEY}',lr)}var sid=sessionStorage.getItem('${JMP_SESSION_ID_KEY}');if(!sid&&crypto&&crypto.randomUUID){sid=crypto.randomUUID();sessionStorage.setItem('${JMP_SESSION_ID_KEY}',sid)}try{var sp=new URLSearchParams(location.search);var utm={};var updated=false;['utm_source','utm_medium','utm_campaign','utm_content','utm_term'].forEach(function(n){var v=sp.get(n);if(v){utm[n]=v;updated=true}});if(updated)sessionStorage.setItem('${JMP_UTM_SESSION_KEY}',JSON.stringify(utm))}catch(e){}}catch(e){}})();`\n}\n","/** Published package version — sent with capabilities for admin readiness. */\nexport const JMP_ANALYTICS_SDK_VERSION = \"0.1.26\"\n\n/** `localStorage` key when {@link JmpAnalyticsConfig.requireConsent} is enabled. */\nexport const JMP_TRACKING_CONSENT_STORAGE_KEY = \"jmp_analytics_consent_v1\"\n\n/** Default `pageViewDedupeMs` when resolving browser config from env (Strict Mode / SPA). */\nexport const DEFAULT_JMP_BROWSER_PAGE_VIEW_DEDUPE_MS = 2500\n\n/** Marks links tracked explicitly — skips delegated `autoTrackContactLinks` handler. */\nexport const JMP_TRACKED_LINK_ATTR = \"data-jmp-tracked\"\n","import type { JmpServerRequestLike } from \"./types\"\n\n/** CDN / Next.js often return HTML error pages; API errors are JSON */\nexport function responseLooksLikeHtml(body: string): boolean {\n const s = body.trimStart()\n return (\n s.startsWith(\"<!\") ||\n s.toLowerCase().startsWith(\"<html\") ||\n (s.startsWith(\"<\") && /<html[\\s>]/i.test(s.slice(0, 500)))\n )\n}\n\nexport function normalizeBaseUrl(baseUrl: string): string {\n return baseUrl.replace(/\\/$/, \"\")\n}\n\nexport function getHeader(\n headers: JmpServerRequestLike[\"headers\"] | undefined,\n key: string\n): string | null {\n if (!headers) {\n return null\n }\n if (typeof (headers as Pick<Headers, \"get\">).get === \"function\") {\n return (headers as Pick<Headers, \"get\">).get(key)\n }\n const record = headers as Record<string, string | string[] | undefined | null>\n const direct = record[key] ?? record[key.toLowerCase()]\n if (Array.isArray(direct)) {\n return direct[0] ?? null\n }\n return direct ?? null\n}\n\nexport function pathFromRequestUrl(url: string | undefined): string {\n if (!url) {\n return \"/\"\n }\n try {\n const u = new URL(url)\n return `${u.pathname}${u.search}`\n } catch {\n return url.startsWith(\"/\") ? url : \"/\"\n }\n}\n\nexport function resolveFetch(\n fetchImpl: typeof fetch | undefined,\n context: string\n): typeof fetch {\n const resolved =\n fetchImpl ??\n (typeof globalThis !== \"undefined\" && globalThis.fetch\n ? globalThis.fetch.bind(globalThis)\n : undefined)\n if (!resolved) {\n throw new Error(`${context}: fetch is not available; pass fetchImpl in config`)\n }\n return resolved\n}\n\ntype ApiErrorJson = {\n error?: string\n code?: string\n hint?: string\n expectedDomain?: string\n}\n\nexport function formatApiErrorMessage(\n status: number,\n text: string,\n prefix: string\n): string {\n if (responseLooksLikeHtml(text)) {\n return `${prefix} HTTP ${status} — response was HTML (not the JSON API). Check baseUrl points at the JMP Analytics dashboard.`\n }\n let parsed: ApiErrorJson = {}\n try {\n parsed = JSON.parse(text) as ApiErrorJson\n } catch {\n parsed = { error: text }\n }\n const detail = parsed.error ?? text\n return [\n `${prefix} HTTP ${status}`,\n parsed.code ? `[${parsed.code}]` : null,\n detail,\n parsed.hint ? `Hint: ${parsed.hint}` : null,\n parsed.expectedDomain\n ? `Domain in JMP admin: ${parsed.expectedDomain}`\n : null,\n ]\n .filter(Boolean)\n .join(\" — \")\n}\n","import { normalizeBaseUrl, resolveFetch, responseLooksLikeHtml } from \"../http\"\nimport type { JmpContentConfig } from \"../types\"\n\nexport async function getContent<T>(\n config: JmpContentConfig,\n path: string\n): Promise<T> {\n const text = await getContentText(config, path)\n\n try {\n return JSON.parse(text) as T\n } catch {\n const baseUrl = normalizeBaseUrl(config.baseUrl)\n const url = `${baseUrl}${path}`\n throw new Error(`JMP content HTTP 200 — ${url} returned invalid JSON.`)\n }\n}\n\nexport async function getContentText(\n config: JmpContentConfig,\n path: string\n): Promise<string> {\n const baseUrl = normalizeBaseUrl(config.baseUrl)\n const resolvedFetch =\n config.fetchImpl ??\n (typeof globalThis !== \"undefined\" && globalThis.fetch\n ? globalThis.fetch.bind(globalThis)\n : undefined)\n\n if (!resolvedFetch) {\n throw new Error(\"JMP content: fetch is not available; pass fetchImpl in config\")\n }\n\n const url = `${baseUrl}${path}`\n\n if (config.debug) {\n console.debug(\"[@jmp-technologies/analytics]\", \"GET\", url)\n }\n\n const res = await resolvedFetch(url, {\n method: \"GET\",\n headers: {\n \"X-JMP-Tracking-Key\": config.trackingKey,\n },\n })\n\n const text = await res.text()\n\n if (!res.ok) {\n if (responseLooksLikeHtml(text)) {\n throw new Error(\n `JMP content HTTP ${res.status} — ${url} returned HTML (not the JSON API). ` +\n `Set baseUrl to your JMP Analytics dashboard origin.`\n )\n }\n\n let parsed: { error?: string; hint?: string } = {}\n try {\n parsed = JSON.parse(text) as typeof parsed\n } catch {\n parsed = { error: text }\n }\n\n throw new Error(\n [`JMP content HTTP ${res.status}`, parsed.error, parsed.hint]\n .filter(Boolean)\n .join(\" — \")\n )\n }\n\n return text\n}\n","import type { JmpContentConfig, JmpContentWrapper, JmpTrustBundle } from \"../types\"\n\n/** Stable report order (matches dashboard `CLIENT_REPORTABLE_WRAPPER_SLOTS`). */\nconst WRAPPER_SLOT_ORDER: readonly string[] = [\n \"blog\",\n \"faq\",\n \"contact\",\n \"trust\",\n \"trust.summary\",\n \"trust.testimonials\",\n \"trust.reviews\",\n \"trust.stats\",\n \"trust.logos\",\n \"trust.credentials\",\n]\n\nconst discovered = new Set<string>()\nlet syncTimer: ReturnType<typeof setTimeout> | null = null\nlet pendingSyncConfig: JmpContentConfig | null = null\n\nconst reportedDedupeKeys = new Set<string>()\nlet syncHandler: ((config: JmpContentConfig) => Promise<unknown>) | null = null\n\nexport function markContentWrapper(slot: JmpContentWrapper | string): void {\n const normalized = slot.trim().toLowerCase()\n if (!normalized) {\n return\n }\n discovered.add(normalized)\n}\n\nexport function markTrustBundleWrappers(bundle: JmpTrustBundle): void {\n for (const slot of inferTrustWrapperSlots(bundle)) {\n discovered.add(slot)\n }\n}\n\nexport function inferTrustWrapperSlots(bundle: JmpTrustBundle): string[] {\n const slots = new Set<string>([\"trust\"])\n const { summary, testimonials, stats, logos, credentials } = bundle\n\n const hasSummary =\n summary.google_rating != null ||\n summary.google_review_count != null ||\n Boolean(summary.google_reviews_url?.trim()) ||\n Boolean(summary.yelp_url?.trim()) ||\n Boolean(summary.trustpilot_url?.trim()) ||\n Boolean(summary.disclaimer?.trim())\n\n if (hasSummary) {\n slots.add(\"trust.summary\")\n }\n\n let hasTestimonials = false\n let hasReviews = false\n for (const item of testimonials) {\n const bucket = item.placement?.trim().toLowerCase()\n if (bucket === \"reviews\") {\n hasReviews = true\n } else {\n hasTestimonials = true\n }\n }\n if (hasTestimonials) {\n slots.add(\"trust.testimonials\")\n }\n if (hasReviews) {\n slots.add(\"trust.reviews\")\n }\n if (stats.length > 0) {\n slots.add(\"trust.stats\")\n }\n if (logos.length > 0) {\n slots.add(\"trust.logos\")\n }\n if (credentials.length > 0) {\n slots.add(\"trust.credentials\")\n }\n\n return sortWrapperSlots([...slots])\n}\n\nexport function getDiscoveredContentWrappers(): string[] {\n return sortWrapperSlots([...discovered])\n}\n\nexport function resolveContentWrappersForReport(\n config: JmpContentConfig\n): string[] {\n const explicit = config.contentWrappers ?? []\n const auto = getDiscoveredContentWrappers()\n\n if (config.contentWrapperMode === \"manual\") {\n return sortWrapperSlots([...explicit])\n }\n\n if (config.contentWrapperMode === \"explicit-only\") {\n return sortWrapperSlots([...explicit])\n }\n\n const merged = new Set<string>([...explicit, ...auto])\n return sortWrapperSlots([...merged])\n}\n\nexport function hasReportedContentWrappers(dedupeKey: string): boolean {\n return reportedDedupeKeys.has(dedupeKey)\n}\n\nexport function rememberReportedContentWrappers(dedupeKey: string): void {\n reportedDedupeKeys.add(dedupeKey)\n}\n\nexport function setContentWrapperSyncHandler(\n handler: (config: JmpContentConfig) => Promise<unknown>\n): void {\n syncHandler = handler\n}\n\n/**\n * Debounced POST after content APIs run (build SSG, first page hit, etc.).\n */\nexport function scheduleContentWrapperSync(config: JmpContentConfig): void {\n if (!syncHandler) {\n return\n }\n pendingSyncConfig = config\n if (syncTimer) {\n clearTimeout(syncTimer)\n }\n syncTimer = setTimeout(() => {\n syncTimer = null\n const cfg = pendingSyncConfig\n pendingSyncConfig = null\n if (!cfg || !syncHandler) {\n return\n }\n void syncHandler(cfg).catch(() => {})\n }, 750)\n}\n\nexport function resetContentWrapperDiscoveryForTests(): void {\n discovered.clear()\n reportedDedupeKeys.clear()\n syncHandler = null\n if (syncTimer) {\n clearTimeout(syncTimer)\n syncTimer = null\n }\n pendingSyncConfig = null\n}\n\nfunction sortWrapperSlots(slots: string[]): string[] {\n const order = new Map(WRAPPER_SLOT_ORDER.map((slot, index) => [slot, index]))\n return [...new Set(slots)].sort((a, b) => {\n const ai = order.get(a) ?? 999\n const bi = order.get(b) ?? 999\n if (ai !== bi) {\n return ai - bi\n }\n return a.localeCompare(b)\n })\n}\n","import { JMP_ANALYTICS_SDK_VERSION } from \"../constants\"\nimport { normalizeBaseUrl, resolveFetch } from \"../http\"\nimport type {\n JmpBlogPost,\n JmpContentConfig,\n JmpFaqItem,\n JmpLandingPage,\n JmpLandingPageDetail,\n JmpLandingPageKind,\n JmpLandingPageSitemapEntry,\n JmpSiteProfile,\n JmpTrustBundle,\n} from \"../types\"\nimport { getContent, getContentText } from \"./fetch\"\nimport {\n hasReportedContentWrappers,\n markContentWrapper,\n markTrustBundleWrappers,\n rememberReportedContentWrappers,\n resolveContentWrappersForReport,\n scheduleContentWrapperSync,\n setContentWrapperSyncHandler,\n} from \"./wrapper-discovery\"\n\nexport {\n getDiscoveredContentWrappers,\n inferTrustWrapperSlots,\n markContentWrapper,\n markTrustBundleWrappers,\n resetContentWrapperDiscoveryForTests,\n} from \"./wrapper-discovery\"\n\nfunction afterContentWrapperUse(config: JmpContentConfig): void {\n scheduleContentWrapperSync(config)\n}\n\nexport async function getJmpBlogPosts(\n config: JmpContentConfig\n): Promise<JmpBlogPost[]> {\n const data = await getContent<{ posts: JmpBlogPost[] }>(\n config,\n \"/api/content/blog\"\n )\n markContentWrapper(\"blog\")\n afterContentWrapperUse(config)\n return data.posts\n}\n\nexport async function getJmpBlogPost(\n config: JmpContentConfig,\n slug: string\n): Promise<JmpBlogPost | null> {\n try {\n const data = await getContent<{ post: JmpBlogPost }>(\n config,\n `/api/content/blog/${encodeURIComponent(slug)}`\n )\n markContentWrapper(\"blog\")\n afterContentWrapperUse(config)\n return data.post\n } catch (error) {\n if (error instanceof Error && error.message.includes(\"HTTP 404\")) {\n return null\n }\n throw error\n }\n}\n\nexport async function getJmpFaqs(\n config: JmpContentConfig\n): Promise<JmpFaqItem[]> {\n const data = await getContent<{ faqs: JmpFaqItem[] }>(\n config,\n \"/api/content/faqs\"\n )\n markContentWrapper(\"faq\")\n afterContentWrapperUse(config)\n return data.faqs\n}\n\nexport async function getJmpLandingPages(\n config: JmpContentConfig,\n filters?: { kind?: JmpLandingPageKind; locale?: string }\n): Promise<JmpLandingPage[]> {\n const params = new URLSearchParams()\n if (filters?.kind) {\n params.set(\"kind\", filters.kind)\n }\n if (filters?.locale) {\n params.set(\"locale\", filters.locale)\n }\n const query = params.toString()\n const data = await getContent<{ pages: JmpLandingPage[] }>(\n config,\n `/api/content/landing-pages${query ? `?${query}` : \"\"}`\n )\n markContentWrapper(\"landing\")\n afterContentWrapperUse(config)\n return data.pages\n}\n\nexport async function getJmpLandingPageDetail(\n config: JmpContentConfig,\n kind: JmpLandingPageKind,\n slug: string,\n locale?: string\n): Promise<JmpLandingPageDetail | null> {\n const params = new URLSearchParams()\n if (locale) {\n params.set(\"locale\", locale)\n }\n const query = params.toString()\n try {\n const detail = await getContent<JmpLandingPageDetail>(\n config,\n `/api/content/landing-pages/${encodeURIComponent(kind)}/${encodeURIComponent(slug)}${query ? `?${query}` : \"\"}`\n )\n markContentWrapper(\"landing\")\n afterContentWrapperUse(config)\n return detail\n } catch (error) {\n if (error instanceof Error && error.message.includes(\"HTTP 404\")) {\n return null\n }\n throw error\n }\n}\n\nexport async function getJmpLandingPageSitemap(\n config: JmpContentConfig\n): Promise<JmpLandingPageSitemapEntry[]> {\n const data = await getContent<{ entries: JmpLandingPageSitemapEntry[] }>(\n config,\n \"/api/content/landing-pages/sitemap\"\n )\n markContentWrapper(\"landing\")\n afterContentWrapperUse(config)\n return data.entries\n}\n\nexport async function getJmpTrustBundle(\n config: JmpContentConfig\n): Promise<JmpTrustBundle> {\n const bundle = await getContent<JmpTrustBundle>(config, \"/api/content/trust\")\n markTrustBundleWrappers(bundle)\n afterContentWrapperUse(config)\n return bundle\n}\n\nexport async function getJmpSiteProfile(\n config: JmpContentConfig\n): Promise<JmpSiteProfile> {\n const data = await getContent<{ site: JmpSiteProfile }>(\n config,\n \"/api/content/site\"\n )\n markContentWrapper(\"contact\")\n afterContentWrapperUse(config)\n return data.site\n}\n\nexport async function getJmpRobotsTxt(config: JmpContentConfig): Promise<string> {\n return getContentText(config, \"/api/content/robots\")\n}\n\n/**\n * Tell the JMP dashboard which content wrappers this site wires (blog, faq, trust.*, contact).\n * Uses auto-discovered slots from content API usage unless `contentWrapperMode` is `manual`.\n * Safe to call on every cold start; dedupes by tracking key + wrapper list.\n */\nexport async function registerJmpContentWrappers(\n config: JmpContentConfig\n): Promise<\n { ok: true; wrappers: string[] } | { ok: false; skipped: true }\n> {\n const wrappers = resolveContentWrappersForReport(config)\n if (!wrappers.length) {\n return { ok: false, skipped: true }\n }\n\n const dedupeKey = `${config.trackingKey}:${[...wrappers].sort().join(\",\")}`\n if (hasReportedContentWrappers(dedupeKey)) {\n return { ok: true, wrappers }\n }\n\n const baseUrl = normalizeBaseUrl(config.baseUrl)\n const resolvedFetch =\n config.fetchImpl ??\n (typeof globalThis !== \"undefined\" && globalThis.fetch\n ? globalThis.fetch.bind(globalThis)\n : undefined)\n\n if (!resolvedFetch) {\n throw new Error(\n \"JMP content wrappers: fetch is not available; pass fetchImpl in config\"\n )\n }\n\n const url = `${baseUrl}/api/content/capabilities`\n if (config.debug) {\n console.debug(\"[@jmp-technologies/analytics]\", \"POST\", url, { wrappers })\n }\n\n const res = await resolvedFetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-JMP-Tracking-Key\": config.trackingKey,\n },\n body: JSON.stringify({\n wrappers,\n sdkVersion: JMP_ANALYTICS_SDK_VERSION,\n }),\n })\n\n const text = await res.text()\n if (!res.ok) {\n let parsed: { error?: string; hint?: string } = {}\n try {\n parsed = JSON.parse(text) as typeof parsed\n } catch {\n parsed = { error: text }\n }\n throw new Error(\n [`JMP content wrappers HTTP ${res.status}`, parsed.error, parsed.hint]\n .filter(Boolean)\n .join(\" — \")\n )\n }\n\n rememberReportedContentWrappers(dedupeKey)\n const data = JSON.parse(text) as { wrappers?: string[] }\n return { ok: true, wrappers: data.wrappers ?? wrappers }\n}\n\nsetContentWrapperSyncHandler(registerJmpContentWrappers)\n","/**\n * Public env vars for JMP — supports Next.js (`NEXT_PUBLIC_*`) and Astro (`PUBLIC_*`).\n * Server/build: `process.env`. Astro client bundles: pass overrides from `import.meta.env`.\n */\n\nexport type JmpPublicEnv = {\n analyticsUrl?: string\n trackingKey?: string\n requireConsent?: boolean\n autoContactLinks?: boolean\n}\n\nfunction firstDefined(...values: (string | undefined)[]): string | undefined {\n for (const v of values) {\n if (v) return v\n }\n return undefined\n}\n\n/** Static reads so Next.js / Vite inline `NEXT_PUBLIC_*` into client bundles. */\nfunction readJmpPublicEnvFromProcessEnv(): Omit<JmpPublicEnv, never> {\n if (typeof process === \"undefined\" || !process.env) {\n return {}\n }\n const { env } = process\n const analyticsUrl = firstDefined(\n env.NEXT_PUBLIC_JMP_ANALYTICS_URL,\n env.PUBLIC_JMP_ANALYTICS_URL,\n )\n const trackingKey = firstDefined(\n env.NEXT_PUBLIC_JMP_TRACKING_KEY,\n env.PUBLIC_JMP_TRACKING_KEY,\n )\n const requireConsentRaw = firstDefined(\n env.NEXT_PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT,\n env.PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT,\n )\n const autoContactRaw = firstDefined(\n env.NEXT_PUBLIC_JMP_AUTO_CONTACT_LINKS,\n env.PUBLIC_JMP_AUTO_CONTACT_LINKS,\n )\n\n return {\n analyticsUrl,\n trackingKey,\n requireConsent: requireConsentRaw === \"true\",\n autoContactLinks: autoContactRaw === \"true\",\n }\n}\n\n/** Read dashboard URL + tracking key from Node / build-time env. */\nexport function readJmpPublicEnvFromProcess(\n overrides?: Partial<JmpPublicEnv>,\n): JmpPublicEnv {\n const fromEnv = readJmpPublicEnvFromProcessEnv()\n\n return {\n analyticsUrl: firstDefined(overrides?.analyticsUrl, fromEnv.analyticsUrl),\n trackingKey: firstDefined(overrides?.trackingKey, fromEnv.trackingKey),\n requireConsent: overrides?.requireConsent ?? fromEnv.requireConsent,\n autoContactLinks:\n overrides?.autoContactLinks ?? fromEnv.autoContactLinks,\n }\n}\n\n/** Read from Astro/Vite `import.meta.env` in client scripts. */\nexport function readJmpPublicEnvFromImportMeta(\n meta: Record<string, unknown> | undefined,\n overrides?: Partial<JmpPublicEnv>,\n): JmpPublicEnv {\n const get = (key: string): string | undefined => {\n const v = meta?.[key]\n return typeof v === \"string\" && v.length > 0 ? v : undefined\n }\n\n const analyticsUrl = firstDefined(\n overrides?.analyticsUrl,\n get(\"PUBLIC_JMP_ANALYTICS_URL\"),\n get(\"NEXT_PUBLIC_JMP_ANALYTICS_URL\"),\n )\n const trackingKey = firstDefined(\n overrides?.trackingKey,\n get(\"PUBLIC_JMP_TRACKING_KEY\"),\n get(\"NEXT_PUBLIC_JMP_TRACKING_KEY\"),\n )\n const requireConsentRaw = firstDefined(\n overrides?.requireConsent !== undefined\n ? String(overrides.requireConsent)\n : undefined,\n get(\"PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT\"),\n get(\"NEXT_PUBLIC_JMP_ANALYTICS_REQUIRE_CONSENT\"),\n )\n const autoContactRaw = firstDefined(\n overrides?.autoContactLinks !== undefined\n ? String(overrides.autoContactLinks)\n : undefined,\n get(\"PUBLIC_JMP_AUTO_CONTACT_LINKS\"),\n get(\"NEXT_PUBLIC_JMP_AUTO_CONTACT_LINKS\"),\n )\n\n return {\n analyticsUrl,\n trackingKey,\n requireConsent: requireConsentRaw === \"true\",\n autoContactLinks: autoContactRaw === \"true\",\n }\n}\n","import type { JmpContentConfig, JmpContentWrapper } from \"./types\"\nimport { readJmpPublicEnvFromProcess } from \"./env-public\"\n\n/** Per-site values used when the dashboard profile is missing or fields are empty. */\nexport type JmpSiteContactFallback = {\n displayName?: string\n founderName?: string\n email?: string\n phone?: string | null\n linkedin?: string | null\n hours?: string | null\n serviceArea?: string | null\n dashboardDemoUrl?: string\n profilePhotoSrc?: string\n privacyUrl?: string\n termsUrl?: string\n}\n\nexport type JmpSiteDefinition = {\n /**\n * Optional override. Wrappers are auto-discovered when content loaders run\n * (`loadBlogPosts`, `loadFaqs`, etc.) and reported to the dashboard.\n */\n contentWrappers?: JmpContentWrapper[]\n /**\n * Public site origin (no trailing slash), e.g. `https://example.com`.\n * Used to resolve relative profile image paths in structured data.\n */\n siteUrl?: string\n /**\n * JMP dashboard origin (no trailing slash), e.g. `https://portal.example.com`.\n * Fallback when `NEXT_PUBLIC_JMP_ANALYTICS_URL` is missing from the client bundle.\n */\n analyticsPortalUrl?: string\n contactFallback?: JmpSiteContactFallback\n /**\n * `extras` keys to read from the dashboard site profile (hyphenated keys from admin).\n */\n contactExtraKeys?: {\n founderName?: readonly string[]\n dashboardDemoUrl?: readonly string[]\n profilePhotoSrc?: readonly string[]\n hours?: readonly string[]\n serviceArea?: readonly string[]\n }\n}\n\nlet registeredSite: JmpSiteDefinition | null = null\n\n/**\n * Register site-specific JMP integration options once (typically from `jmp.config.ts`).\n * Call at module load time before server content or instrumentation runs.\n */\nexport function defineJmpSite(definition: JmpSiteDefinition): JmpSiteDefinition {\n registeredSite = definition\n return definition\n}\n\nexport function getJmpSiteDefinition(): JmpSiteDefinition | null {\n return registeredSite\n}\n\n/** Server content + lead config: env vars + wrappers from {@link defineJmpSite}. */\nexport function getJmpContentConfig(): JmpContentConfig | null {\n const { analyticsUrl, trackingKey } = readJmpPublicEnvFromProcess()\n if (!analyticsUrl || !trackingKey) {\n return null\n }\n const site = getJmpSiteDefinition()\n return {\n baseUrl: analyticsUrl,\n trackingKey,\n contentWrappers: site?.contentWrappers ?? undefined,\n }\n}\n","import {\n getJmpBlogPost,\n getJmpBlogPosts,\n getJmpFaqs,\n getJmpLandingPageDetail,\n getJmpLandingPageSitemap,\n getJmpLandingPages,\n getJmpSiteProfile,\n getJmpTrustBundle,\n type JmpBlogPost,\n type JmpFaqItem,\n type JmpLandingPage,\n type JmpLandingPageDetail,\n type JmpLandingPageKind,\n type JmpLandingPageSitemapEntry,\n type JmpSiteProfile,\n type JmpTestimonial,\n type JmpTrustBundle,\n} from \"../index\"\nimport { getJmpContentConfig, getJmpSiteDefinition } from \"../site-config\"\n\nexport type {\n JmpBlogPost,\n JmpFaqItem,\n JmpLandingPage,\n JmpLandingPageDetail,\n JmpLandingPageKind,\n JmpLandingPageSitemapEntry,\n JmpSiteProfile,\n JmpTestimonial,\n JmpTrustBundle,\n}\n\nexport { getJmpContentConfig } 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 phone: 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} 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 linkedin =\n profile?.social?.linkedin?.trim() || fallback.linkedin || null\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,\n phone,\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\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 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":["absoluteSiteAssetUrl","filterFeaturedTrustQuotes","getResolvedSiteContact","loadBlogPost","loadBlogPosts","loadFaqBySlug","loadFaqs","loadLandingPageDetail","loadLandingPageSitemap","loadLandingPages","loadSiteProfile","loadTrustBundle","resolveSiteContact","absoluteSiteAssetUrl","resolveSiteContact","filterFeaturedTrustQuotes","loadSiteProfile","getResolvedSiteContact","loadBlogPosts","loadBlogPost","loadFaqs","loadFaqBySlug","loadLandingPages","loadLandingPageDetail","loadLandingPageSitemap","loadTrustBundle"]}
@@ -1,7 +1,7 @@
1
- import { absoluteSiteAssetUrl as absoluteSiteAssetUrl$1, filterFeaturedTrustQuotes as filterFeaturedTrustQuotes$1, getResolvedSiteContact as getResolvedSiteContact$1, loadBlogPost as loadBlogPost$1, loadBlogPosts as loadBlogPosts$1, loadFaqBySlug as loadFaqBySlug$1, loadFaqs as loadFaqs$1, loadSiteProfile as loadSiteProfile$1, loadTrustBundle as loadTrustBundle$1, resolveSiteContact as resolveSiteContact$1 } from '../astro/content.cjs';
1
+ import { absoluteSiteAssetUrl as absoluteSiteAssetUrl$1, filterFeaturedTrustQuotes as filterFeaturedTrustQuotes$1, getResolvedSiteContact as getResolvedSiteContact$1, loadBlogPost as loadBlogPost$1, loadBlogPosts as loadBlogPosts$1, loadFaqBySlug as loadFaqBySlug$1, loadFaqs as loadFaqs$1, loadLandingPageDetail as loadLandingPageDetail$1, loadLandingPageSitemap as loadLandingPageSitemap$1, loadLandingPages as loadLandingPages$1, loadSiteProfile as loadSiteProfile$1, loadTrustBundle as loadTrustBundle$1, resolveSiteContact as resolveSiteContact$1 } from '../astro/content.cjs';
2
2
  export { ResolvedSiteContact } from '../astro/content.cjs';
3
- export { J as JmpBlogPost, a as JmpFaqItem, b as JmpSiteProfile, c as JmpTestimonial, d as JmpTrustBundle } from '../types-C1eq8E5u.cjs';
4
- export { g as getJmpContentConfig } from '../site-config-CXUdGLqw.cjs';
3
+ export { J as JmpBlogPost, a as JmpFaqItem, b as JmpLandingPage, c as JmpLandingPageDetail, d as JmpLandingPageKind, e as JmpLandingPageSitemapEntry, f as JmpSiteProfile, g as JmpTestimonial, h as JmpTrustBundle } from '../types-B-111Wht.cjs';
4
+ export { g as getJmpContentConfig } from '../site-config-CJqYnZm7.cjs';
5
5
 
6
6
  declare const absoluteSiteAssetUrl: typeof absoluteSiteAssetUrl$1;
7
7
  declare const resolveSiteContact: typeof resolveSiteContact$1;
@@ -12,6 +12,9 @@ declare const loadBlogPosts: typeof loadBlogPosts$1;
12
12
  declare const loadBlogPost: typeof loadBlogPost$1;
13
13
  declare const loadFaqs: typeof loadFaqs$1;
14
14
  declare const loadFaqBySlug: typeof loadFaqBySlug$1;
15
+ declare const loadLandingPages: typeof loadLandingPages$1;
16
+ declare const loadLandingPageDetail: typeof loadLandingPageDetail$1;
17
+ declare const loadLandingPageSitemap: typeof loadLandingPageSitemap$1;
15
18
  declare const loadTrustBundle: typeof loadTrustBundle$1;
16
19
 
17
- export { absoluteSiteAssetUrl, filterFeaturedTrustQuotes, getResolvedSiteContact, loadBlogPost, loadBlogPosts, loadFaqBySlug, loadFaqs, loadSiteProfile, loadTrustBundle, resolveSiteContact };
20
+ export { absoluteSiteAssetUrl, filterFeaturedTrustQuotes, getResolvedSiteContact, loadBlogPost, loadBlogPosts, loadFaqBySlug, loadFaqs, loadLandingPageDetail, loadLandingPageSitemap, loadLandingPages, loadSiteProfile, loadTrustBundle, resolveSiteContact };
@@ -1,7 +1,7 @@
1
- import { absoluteSiteAssetUrl as absoluteSiteAssetUrl$1, filterFeaturedTrustQuotes as filterFeaturedTrustQuotes$1, getResolvedSiteContact as getResolvedSiteContact$1, loadBlogPost as loadBlogPost$1, loadBlogPosts as loadBlogPosts$1, loadFaqBySlug as loadFaqBySlug$1, loadFaqs as loadFaqs$1, loadSiteProfile as loadSiteProfile$1, loadTrustBundle as loadTrustBundle$1, resolveSiteContact as resolveSiteContact$1 } from '../astro/content.js';
1
+ import { absoluteSiteAssetUrl as absoluteSiteAssetUrl$1, filterFeaturedTrustQuotes as filterFeaturedTrustQuotes$1, getResolvedSiteContact as getResolvedSiteContact$1, loadBlogPost as loadBlogPost$1, loadBlogPosts as loadBlogPosts$1, loadFaqBySlug as loadFaqBySlug$1, loadFaqs as loadFaqs$1, loadLandingPageDetail as loadLandingPageDetail$1, loadLandingPageSitemap as loadLandingPageSitemap$1, loadLandingPages as loadLandingPages$1, loadSiteProfile as loadSiteProfile$1, loadTrustBundle as loadTrustBundle$1, resolveSiteContact as resolveSiteContact$1 } from '../astro/content.js';
2
2
  export { ResolvedSiteContact } from '../astro/content.js';
3
- export { J as JmpBlogPost, a as JmpFaqItem, b as JmpSiteProfile, c as JmpTestimonial, d as JmpTrustBundle } from '../types-C1eq8E5u.js';
4
- export { g as getJmpContentConfig } from '../site-config-w_Db9YSk.js';
3
+ export { J as JmpBlogPost, a as JmpFaqItem, b as JmpLandingPage, c as JmpLandingPageDetail, d as JmpLandingPageKind, e as JmpLandingPageSitemapEntry, f as JmpSiteProfile, g as JmpTestimonial, h as JmpTrustBundle } from '../types-B-111Wht.js';
4
+ export { g as getJmpContentConfig } from '../site-config-UTzDG-UG.js';
5
5
 
6
6
  declare const absoluteSiteAssetUrl: typeof absoluteSiteAssetUrl$1;
7
7
  declare const resolveSiteContact: typeof resolveSiteContact$1;
@@ -12,6 +12,9 @@ declare const loadBlogPosts: typeof loadBlogPosts$1;
12
12
  declare const loadBlogPost: typeof loadBlogPost$1;
13
13
  declare const loadFaqs: typeof loadFaqs$1;
14
14
  declare const loadFaqBySlug: typeof loadFaqBySlug$1;
15
+ declare const loadLandingPages: typeof loadLandingPages$1;
16
+ declare const loadLandingPageDetail: typeof loadLandingPageDetail$1;
17
+ declare const loadLandingPageSitemap: typeof loadLandingPageSitemap$1;
15
18
  declare const loadTrustBundle: typeof loadTrustBundle$1;
16
19
 
17
- export { absoluteSiteAssetUrl, filterFeaturedTrustQuotes, getResolvedSiteContact, loadBlogPost, loadBlogPosts, loadFaqBySlug, loadFaqs, loadSiteProfile, loadTrustBundle, resolveSiteContact };
20
+ export { absoluteSiteAssetUrl, filterFeaturedTrustQuotes, getResolvedSiteContact, loadBlogPost, loadBlogPosts, loadFaqBySlug, loadFaqs, loadLandingPageDetail, loadLandingPageSitemap, loadLandingPages, loadSiteProfile, loadTrustBundle, resolveSiteContact };
@@ -6,11 +6,14 @@ import {
6
6
  loadBlogPosts,
7
7
  loadFaqBySlug,
8
8
  loadFaqs,
9
+ loadLandingPageDetail,
10
+ loadLandingPageSitemap,
11
+ loadLandingPages,
9
12
  loadSiteProfile,
10
13
  loadTrustBundle,
11
14
  resolveSiteContact
12
- } from "../chunk-TFHOTBOL.js";
13
- import "../chunk-RCOHQAGL.js";
15
+ } from "../chunk-PG3IOI5O.js";
16
+ import "../chunk-UVYF33XE.js";
14
17
  import "../chunk-TCZ6TC2L.js";
15
18
  import {
16
19
  getJmpContentConfig
@@ -27,6 +30,9 @@ var loadBlogPosts2 = cache(loadBlogPosts);
27
30
  var loadBlogPost2 = cache(loadBlogPost);
28
31
  var loadFaqs2 = cache(loadFaqs);
29
32
  var loadFaqBySlug2 = loadFaqBySlug;
33
+ var loadLandingPages2 = cache(loadLandingPages);
34
+ var loadLandingPageDetail2 = loadLandingPageDetail;
35
+ var loadLandingPageSitemap2 = cache(loadLandingPageSitemap);
30
36
  var loadTrustBundle2 = cache(loadTrustBundle);
31
37
  export {
32
38
  absoluteSiteAssetUrl2 as absoluteSiteAssetUrl,
@@ -37,6 +43,9 @@ export {
37
43
  loadBlogPosts2 as loadBlogPosts,
38
44
  loadFaqBySlug2 as loadFaqBySlug,
39
45
  loadFaqs2 as loadFaqs,
46
+ loadLandingPageDetail2 as loadLandingPageDetail,
47
+ loadLandingPageSitemap2 as loadLandingPageSitemap,
48
+ loadLandingPages2 as loadLandingPages,
40
49
  loadSiteProfile2 as loadSiteProfile,
41
50
  loadTrustBundle2 as loadTrustBundle,
42
51
  resolveSiteContact2 as resolveSiteContact
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/next/content.ts"],"sourcesContent":["import { cache } from \"react\"\n\nimport * as siteContent from \"../content/site-content\"\n\nexport type {\n JmpBlogPost,\n JmpFaqItem,\n JmpSiteProfile,\n JmpTestimonial,\n JmpTrustBundle,\n ResolvedSiteContact,\n} from \"../content/site-content\"\n\nexport { getJmpContentConfig } from \"../content/site-content\"\n\nexport const absoluteSiteAssetUrl = siteContent.absoluteSiteAssetUrl\nexport const resolveSiteContact = siteContent.resolveSiteContact\nexport const filterFeaturedTrustQuotes = siteContent.filterFeaturedTrustQuotes\n\nexport const loadSiteProfile = cache(siteContent.loadSiteProfile)\nexport const getResolvedSiteContact = cache(siteContent.getResolvedSiteContact)\nexport const loadBlogPosts = cache(siteContent.loadBlogPosts)\nexport const loadBlogPost = cache(siteContent.loadBlogPost)\nexport const loadFaqs = cache(siteContent.loadFaqs)\nexport const loadFaqBySlug = siteContent.loadFaqBySlug\nexport const loadTrustBundle = cache(siteContent.loadTrustBundle)\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA,SAAS,aAAa;AAef,IAAMA,wBAAmC;AACzC,IAAMC,sBAAiC;AACvC,IAAMC,6BAAwC;AAE9C,IAAMC,mBAAkB,MAAkB,eAAe;AACzD,IAAMC,0BAAyB,MAAkB,sBAAsB;AACvE,IAAMC,iBAAgB,MAAkB,aAAa;AACrD,IAAMC,gBAAe,MAAkB,YAAY;AACnD,IAAMC,YAAW,MAAkB,QAAQ;AAC3C,IAAMC,iBAA4B;AAClC,IAAMC,mBAAkB,MAAkB,eAAe;","names":["absoluteSiteAssetUrl","resolveSiteContact","filterFeaturedTrustQuotes","loadSiteProfile","getResolvedSiteContact","loadBlogPosts","loadBlogPost","loadFaqs","loadFaqBySlug","loadTrustBundle"]}
1
+ {"version":3,"sources":["../../src/next/content.ts"],"sourcesContent":["import { cache } from \"react\"\n\nimport * as siteContent from \"../content/site-content\"\n\nexport type {\n JmpBlogPost,\n JmpFaqItem,\n JmpLandingPage,\n JmpLandingPageDetail,\n JmpLandingPageKind,\n JmpLandingPageSitemapEntry,\n JmpSiteProfile,\n JmpTestimonial,\n JmpTrustBundle,\n ResolvedSiteContact,\n} from \"../content/site-content\"\n\nexport { getJmpContentConfig } from \"../content/site-content\"\n\nexport const absoluteSiteAssetUrl = siteContent.absoluteSiteAssetUrl\nexport const resolveSiteContact = siteContent.resolveSiteContact\nexport const filterFeaturedTrustQuotes = siteContent.filterFeaturedTrustQuotes\n\nexport const loadSiteProfile = cache(siteContent.loadSiteProfile)\nexport const getResolvedSiteContact = cache(siteContent.getResolvedSiteContact)\nexport const loadBlogPosts = cache(siteContent.loadBlogPosts)\nexport const loadBlogPost = cache(siteContent.loadBlogPost)\nexport const loadFaqs = cache(siteContent.loadFaqs)\nexport const loadFaqBySlug = siteContent.loadFaqBySlug\nexport const loadLandingPages = cache(siteContent.loadLandingPages)\nexport const loadLandingPageDetail = siteContent.loadLandingPageDetail\nexport const loadLandingPageSitemap = cache(siteContent.loadLandingPageSitemap)\nexport const loadTrustBundle = cache(siteContent.loadTrustBundle)\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,aAAa;AAmBf,IAAMA,wBAAmC;AACzC,IAAMC,sBAAiC;AACvC,IAAMC,6BAAwC;AAE9C,IAAMC,mBAAkB,MAAkB,eAAe;AACzD,IAAMC,0BAAyB,MAAkB,sBAAsB;AACvE,IAAMC,iBAAgB,MAAkB,aAAa;AACrD,IAAMC,gBAAe,MAAkB,YAAY;AACnD,IAAMC,YAAW,MAAkB,QAAQ;AAC3C,IAAMC,iBAA4B;AAClC,IAAMC,oBAAmB,MAAkB,gBAAgB;AAC3D,IAAMC,yBAAoC;AAC1C,IAAMC,0BAAyB,MAAkB,sBAAsB;AACvE,IAAMC,mBAAkB,MAAkB,eAAe;","names":["absoluteSiteAssetUrl","resolveSiteContact","filterFeaturedTrustQuotes","loadSiteProfile","getResolvedSiteContact","loadBlogPosts","loadBlogPost","loadFaqs","loadFaqBySlug","loadLandingPages","loadLandingPageDetail","loadLandingPageSitemap","loadTrustBundle"]}
@@ -28,7 +28,7 @@ module.exports = __toCommonJS(instrumentation_exports);
28
28
  var LANDING_REFERER_COOKIE_MAX_AGE_SEC = 30 * 60;
29
29
 
30
30
  // src/constants.ts
31
- var JMP_ANALYTICS_SDK_VERSION = "0.1.25";
31
+ var JMP_ANALYTICS_SDK_VERSION = "0.1.26";
32
32
 
33
33
  // src/http.ts
34
34
  function normalizeBaseUrl(baseUrl) {