@jmp-technologies/analytics 0.1.20 → 0.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/dist/astro/client.cjs +93 -108
- package/dist/astro/client.cjs.map +1 -1
- package/dist/astro/client.d.cts +3 -12
- package/dist/astro/client.d.ts +3 -12
- package/dist/astro/client.js +4 -2
- package/dist/astro/client.js.map +1 -1
- package/dist/astro/content.cjs +106 -98
- package/dist/astro/content.cjs.map +1 -1
- package/dist/astro/content.d.cts +2 -2
- package/dist/astro/content.d.ts +2 -2
- package/dist/astro/content.js +3 -2
- package/dist/astro/integration.cjs +68 -60
- package/dist/astro/integration.cjs.map +1 -1
- package/dist/astro/integration.js +3 -2
- package/dist/astro/integration.js.map +1 -1
- package/dist/astro/middleware.cjs +66 -63
- package/dist/astro/middleware.cjs.map +1 -1
- package/dist/astro/middleware.d.cts +2 -2
- package/dist/astro/middleware.d.ts +2 -2
- package/dist/astro/middleware.js +2 -2
- package/dist/astro/register.cjs +68 -60
- package/dist/astro/register.cjs.map +1 -1
- package/dist/astro/register.js +3 -2
- package/dist/{chunk-UDNKWFBM.js → chunk-3U2BSIYX.js} +5 -3
- package/dist/{chunk-UDNKWFBM.js.map → chunk-3U2BSIYX.js.map} +1 -1
- package/dist/chunk-4OOWA55U.js +215 -0
- package/dist/chunk-4OOWA55U.js.map +1 -0
- package/dist/{chunk-GFCBZN6S.js → chunk-AHRVNEFV.js} +80 -276
- package/dist/chunk-AHRVNEFV.js.map +1 -0
- package/dist/{chunk-ALTSW5DB.js → chunk-DQ6A6HTY.js} +12 -8
- package/dist/chunk-DQ6A6HTY.js.map +1 -0
- package/dist/{chunk-QIWGUF7V.js → chunk-IYFVJ2EY.js} +6 -4
- package/dist/{chunk-QIWGUF7V.js.map → chunk-IYFVJ2EY.js.map} +1 -1
- package/dist/{chunk-NZS4M3V3.js → chunk-TEFKM3GW.js} +2 -2
- package/dist/chunk-TEFKM3GW.js.map +1 -0
- package/dist/create-jmp-analytics-BdGOgPxl.d.ts +70 -0
- package/dist/create-jmp-analytics-DxhHOmtR.d.cts +70 -0
- package/dist/env-public-B6Y0DG68.d.cts +14 -0
- package/dist/env-public-B6Y0DG68.d.ts +14 -0
- package/dist/index.cjs +203 -198
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -340
- package/dist/index.d.ts +16 -340
- package/dist/index.js +9 -5
- package/dist/landing-capture-C8pyZWuF.d.cts +4 -0
- package/dist/landing-capture-C8pyZWuF.d.ts +4 -0
- package/dist/next/content.cjs +106 -98
- package/dist/next/content.cjs.map +1 -1
- package/dist/next/content.d.cts +2 -1
- package/dist/next/content.d.ts +2 -1
- package/dist/next/content.js +3 -2
- package/dist/next/content.js.map +1 -1
- package/dist/next/instrumentation.cjs +68 -60
- package/dist/next/instrumentation.cjs.map +1 -1
- package/dist/next/instrumentation.js +4 -2
- package/dist/next/instrumentation.js.map +1 -1
- package/dist/next/middleware.cjs +66 -63
- package/dist/next/middleware.cjs.map +1 -1
- package/dist/next/middleware.d.cts +2 -2
- package/dist/next/middleware.d.ts +2 -2
- package/dist/next/middleware.js +2 -2
- package/dist/next.cjs +152 -123
- package/dist/next.cjs.map +1 -1
- package/dist/next.d.cts +15 -3
- package/dist/next.d.ts +15 -3
- package/dist/next.js +45 -7
- package/dist/next.js.map +1 -1
- package/dist/react.cjs +115 -118
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +2 -1
- package/dist/react.d.ts +2 -1
- package/dist/react.js +3 -2
- package/dist/site-config-BRuZWGSW.d.cts +46 -0
- package/dist/site-config-hkGMSeT7.d.ts +46 -0
- package/dist/{track-page-view-CoYo96n0.d.cts → track-page-view-BLS0YfL-.d.ts} +1 -1
- package/dist/{track-page-view-DAy0I0Vb.d.ts → track-page-view-DfCyWHqe.d.cts} +1 -1
- package/dist/types-BtiK_Acj.d.cts +224 -0
- package/dist/types-BtiK_Acj.d.ts +224 -0
- package/package.json +2 -4
- package/dist/chunk-ALTSW5DB.js.map +0 -1
- package/dist/chunk-GFCBZN6S.js.map +0 -1
- package/dist/chunk-NZS4M3V3.js.map +0 -1
|
@@ -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 { getOrCreateJmpAnalyticsFromBrowserEnv } from \"./index\"\n\nexport function useJmpAnalytics(): JmpAnalytics | null {\n return useMemo(() => getOrCreateJmpAnalyticsFromBrowserEnv(), [])\n}\n\nfunction trackCallClickFromCurrentPath(): void {\n const path =\n typeof window !== \"undefined\" ? window.location.pathname : \"/\"\n void getOrCreateJmpAnalyticsFromBrowserEnv()?.trackCallClick(path)\n}\n\nfunction trackEmailClickFromCurrentPath(): void {\n const path =\n typeof window !== \"undefined\" ? window.location.pathname : \"/\"\n void getOrCreateJmpAnalyticsFromBrowserEnv()?.trackEmailClick(path)\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 {...rest}\n onClick={(e) => {\n trackCallClickFromCurrentPath()\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 {...rest}\n onClick={(e) => {\n trackEmailClickFromCurrentPath()\n onClick?.(e)\n }}\n />\n )\n}\n"],"mappings":";;;;;AAEA,SAAS,eAA8C;AAiCnD;AA7BG,SAAS,kBAAuC;AACrD,SAAO,QAAQ,MAAM,sCAAsC,GAAG,CAAC,CAAC;AAClE;AAEA,SAAS,gCAAsC;AAC7C,QAAM,OACJ,OAAO,WAAW,cAAc,OAAO,SAAS,WAAW;AAC7D,OAAK,sCAAsC,GAAG,eAAe,IAAI;AACnE;AAEA,SAAS,iCAAuC;AAC9C,QAAM,OACJ,OAAO,WAAW,cAAc,OAAO,SAAS,WAAW;AAC7D,OAAK,sCAAsC,GAAG,gBAAgB,IAAI;AACpE;AASO,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA2B;AACzB,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACC,GAAG;AAAA,MACJ,SAAS,CAAC,MAAM;AACd,sCAA8B;AAC9B,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,MACC,GAAG;AAAA,MACJ,SAAS,CAAC,MAAM;AACd,uCAA+B;AAC/B,kBAAU,CAAC;AAAA,MACb;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getJmpBlogPost,
|
|
3
3
|
getJmpBlogPosts,
|
|
4
|
-
getJmpContentConfig,
|
|
5
4
|
getJmpFaqs,
|
|
6
|
-
getJmpSiteDefinition,
|
|
7
5
|
getJmpSiteProfile,
|
|
8
6
|
getJmpTrustBundle
|
|
9
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-AHRVNEFV.js";
|
|
8
|
+
import {
|
|
9
|
+
getJmpContentConfig,
|
|
10
|
+
getJmpSiteDefinition
|
|
11
|
+
} from "./chunk-4OOWA55U.js";
|
|
10
12
|
|
|
11
13
|
// src/content/site-content.ts
|
|
12
14
|
var DEFAULT_EXTRA_KEYS = {
|
|
@@ -168,4 +170,4 @@ export {
|
|
|
168
170
|
loadTrustBundle,
|
|
169
171
|
filterFeaturedTrustQuotes
|
|
170
172
|
};
|
|
171
|
-
//# sourceMappingURL=chunk-
|
|
173
|
+
//# sourceMappingURL=chunk-IYFVJ2EY.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/content/site-content.ts"],"sourcesContent":["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":"
|
|
1
|
+
{"version":3,"sources":["../src/content/site-content.ts"],"sourcesContent":["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":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getJmpContentConfig,
|
|
3
3
|
trackJmpServerPageView
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-4OOWA55U.js";
|
|
5
5
|
|
|
6
6
|
// src/server/track-page-view.ts
|
|
7
7
|
async function jmpTrackServerPageView(request) {
|
|
@@ -16,4 +16,4 @@ async function jmpTrackServerPageView(request) {
|
|
|
16
16
|
export {
|
|
17
17
|
jmpTrackServerPageView
|
|
18
18
|
};
|
|
19
|
-
//# sourceMappingURL=chunk-
|
|
19
|
+
//# sourceMappingURL=chunk-TEFKM3GW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/track-page-view.ts"],"sourcesContent":["import { trackJmpServerPageView } from \"./page-view\"\nimport type { JmpServerRequestLike } from \"../types\"\nimport { getJmpContentConfig } from \"../site-config\"\n\n/**\n * Records a server-side page view when JMP env is configured.\n * Framework-agnostic — pass any `Request`-like object (Next middleware, Astro `context.request`).\n */\nexport async function jmpTrackServerPageView(\n request: JmpServerRequestLike,\n): Promise<void> {\n const config = getJmpContentConfig()\n if (!config) {\n return\n }\n await trackJmpServerPageView(config, { request }).catch(() => {})\n}\n"],"mappings":";;;;;;AAQA,eAAsB,uBACpB,SACe;AACf,QAAM,SAAS,oBAAoB;AACnC,MAAI,CAAC,QAAQ;AACX;AAAA,EACF;AACA,QAAM,uBAAuB,QAAQ,EAAE,QAAQ,CAAC,EAAE,MAAM,MAAM;AAAA,EAAC,CAAC;AAClE;","names":[]}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { h as JmpAnalyticsConfig, i as JmpEventType, T as TrackPageViewOptions, j as TrackLeadOptions } from './types-BtiK_Acj.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Classify outbound contact links for auto-tracking.
|
|
5
|
+
*
|
|
6
|
+
* Keep channel set in sync with `lib/ingest/contact-click.ts` on the dashboard side.
|
|
7
|
+
*
|
|
8
|
+
* Channels detected from `href` alone (no widget / iframe detection):
|
|
9
|
+
*
|
|
10
|
+
* - `call` — `tel:` (delegates to existing `trackCallClick`)
|
|
11
|
+
* - `email` — `mailto:` (delegates to existing `trackEmailClick`)
|
|
12
|
+
* - `sms` — `sms:` / `smsto:`
|
|
13
|
+
* - `whatsapp` — `wa.me`, `api.whatsapp.com`, `whatsapp.com/send`
|
|
14
|
+
* - `maps` — Google Maps, Apple Maps, `maps.app.goo.gl`, `geo:`
|
|
15
|
+
* - `booking` — fixed allowlist of common scheduling hosts (see {@link BOOKING_LINK_HOSTS})
|
|
16
|
+
*
|
|
17
|
+
* `linkHost` returned in `contact_click` results is the hostname only — never the full URL
|
|
18
|
+
* (avoids leaking phone numbers in `tel:`/`sms:` URLs and PII-laden booking query strings).
|
|
19
|
+
*/
|
|
20
|
+
type ContactLinkChannel = "sms" | "whatsapp" | "maps" | "booking";
|
|
21
|
+
type ClassifiedContactLink = {
|
|
22
|
+
kind: "call";
|
|
23
|
+
} | {
|
|
24
|
+
kind: "email";
|
|
25
|
+
} | {
|
|
26
|
+
kind: "contact_click";
|
|
27
|
+
channel: ContactLinkChannel;
|
|
28
|
+
linkHost: string | null;
|
|
29
|
+
};
|
|
30
|
+
/** v1 booking hosts (~80%+ SMB coverage). Add hosts here when a client uses something rare. */
|
|
31
|
+
declare const BOOKING_LINK_HOSTS: readonly ["calendly.com", "cal.com", "acuityscheduling.com", "calendar.google.com", "calendar.app.google", "bookings.microsoft.com", "outlook.office.com", "outlook.office365.com", "book.ms", "setmore.com", "simplybook.me", "oncehub.com", "scheduleonce.com", "youcanbook.me", "savvycal.com", "tidycal.com", "zohobookings.com", "square.site", "squareup.com", "meetings.hubspot.com", "jobber.com", "housecallpro.com", "servicetitan.com", "thryv.com", "honeybook.com", "dubsado.com", "fresha.com", "booksy.com", "vagaro.com", "mindbodyonline.com", "mindbody.io", "janeapp.com", "cliniko.com", "practicebetter.io"];
|
|
32
|
+
/** Returns `null` when the link should not be tracked as a contact link. */
|
|
33
|
+
declare function classifyContactHref(href: string): ClassifiedContactLink | null;
|
|
34
|
+
|
|
35
|
+
/** Whether analytics events may be sent (after CMP / cookie banner). */
|
|
36
|
+
declare function isJmpTrackingConsentGranted(): boolean;
|
|
37
|
+
/** Subscribe to consent changes (for React `useSyncExternalStore`). */
|
|
38
|
+
declare function subscribeJmpTrackingConsent(listener: () => void): () => void;
|
|
39
|
+
/**
|
|
40
|
+
* Grant or deny analytics. Persists in `localStorage` when `requireConsent` is used.
|
|
41
|
+
* Call from your cookie banner before tracking runs.
|
|
42
|
+
*/
|
|
43
|
+
declare function setJmpTrackingConsent(granted: boolean): void;
|
|
44
|
+
|
|
45
|
+
declare function createJmpAnalytics(config: JmpAnalyticsConfig): {
|
|
46
|
+
track: (type: JmpEventType, payload: {
|
|
47
|
+
path: string;
|
|
48
|
+
metadata?: Record<string, unknown>;
|
|
49
|
+
source?: string;
|
|
50
|
+
device?: string;
|
|
51
|
+
country?: string;
|
|
52
|
+
}) => Promise<void>;
|
|
53
|
+
trackPageView: (options?: TrackPageViewOptions) => Promise<void>;
|
|
54
|
+
trackFormSubmit: (path: string, metadata?: Record<string, unknown>) => Promise<void>;
|
|
55
|
+
trackCallClick: (path: string, metadata?: Record<string, unknown>) => Promise<void>;
|
|
56
|
+
trackEmailClick: (path: string, metadata?: Record<string, unknown>) => Promise<void>;
|
|
57
|
+
trackContactClick: (path: string, options: {
|
|
58
|
+
channel: ContactLinkChannel;
|
|
59
|
+
linkHost?: string | null;
|
|
60
|
+
metadata?: Record<string, unknown>;
|
|
61
|
+
}) => Promise<void>;
|
|
62
|
+
trackLead: (options?: TrackLeadOptions) => Promise<void>;
|
|
63
|
+
subscribeToSpaNavigation: () => () => void;
|
|
64
|
+
subscribeContactLinkAutoTrack: () => () => void;
|
|
65
|
+
setTrackingConsent: typeof setJmpTrackingConsent;
|
|
66
|
+
isTrackingConsentGranted: typeof isJmpTrackingConsentGranted;
|
|
67
|
+
};
|
|
68
|
+
type JmpAnalytics = ReturnType<typeof createJmpAnalytics>;
|
|
69
|
+
|
|
70
|
+
export { BOOKING_LINK_HOSTS as B, type ClassifiedContactLink as C, type JmpAnalytics as J, subscribeJmpTrackingConsent as a, type ContactLinkChannel as b, classifyContactHref as c, createJmpAnalytics as d, isJmpTrackingConsentGranted as i, setJmpTrackingConsent as s };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { h as JmpAnalyticsConfig, i as JmpEventType, T as TrackPageViewOptions, j as TrackLeadOptions } from './types-BtiK_Acj.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Classify outbound contact links for auto-tracking.
|
|
5
|
+
*
|
|
6
|
+
* Keep channel set in sync with `lib/ingest/contact-click.ts` on the dashboard side.
|
|
7
|
+
*
|
|
8
|
+
* Channels detected from `href` alone (no widget / iframe detection):
|
|
9
|
+
*
|
|
10
|
+
* - `call` — `tel:` (delegates to existing `trackCallClick`)
|
|
11
|
+
* - `email` — `mailto:` (delegates to existing `trackEmailClick`)
|
|
12
|
+
* - `sms` — `sms:` / `smsto:`
|
|
13
|
+
* - `whatsapp` — `wa.me`, `api.whatsapp.com`, `whatsapp.com/send`
|
|
14
|
+
* - `maps` — Google Maps, Apple Maps, `maps.app.goo.gl`, `geo:`
|
|
15
|
+
* - `booking` — fixed allowlist of common scheduling hosts (see {@link BOOKING_LINK_HOSTS})
|
|
16
|
+
*
|
|
17
|
+
* `linkHost` returned in `contact_click` results is the hostname only — never the full URL
|
|
18
|
+
* (avoids leaking phone numbers in `tel:`/`sms:` URLs and PII-laden booking query strings).
|
|
19
|
+
*/
|
|
20
|
+
type ContactLinkChannel = "sms" | "whatsapp" | "maps" | "booking";
|
|
21
|
+
type ClassifiedContactLink = {
|
|
22
|
+
kind: "call";
|
|
23
|
+
} | {
|
|
24
|
+
kind: "email";
|
|
25
|
+
} | {
|
|
26
|
+
kind: "contact_click";
|
|
27
|
+
channel: ContactLinkChannel;
|
|
28
|
+
linkHost: string | null;
|
|
29
|
+
};
|
|
30
|
+
/** v1 booking hosts (~80%+ SMB coverage). Add hosts here when a client uses something rare. */
|
|
31
|
+
declare const BOOKING_LINK_HOSTS: readonly ["calendly.com", "cal.com", "acuityscheduling.com", "calendar.google.com", "calendar.app.google", "bookings.microsoft.com", "outlook.office.com", "outlook.office365.com", "book.ms", "setmore.com", "simplybook.me", "oncehub.com", "scheduleonce.com", "youcanbook.me", "savvycal.com", "tidycal.com", "zohobookings.com", "square.site", "squareup.com", "meetings.hubspot.com", "jobber.com", "housecallpro.com", "servicetitan.com", "thryv.com", "honeybook.com", "dubsado.com", "fresha.com", "booksy.com", "vagaro.com", "mindbodyonline.com", "mindbody.io", "janeapp.com", "cliniko.com", "practicebetter.io"];
|
|
32
|
+
/** Returns `null` when the link should not be tracked as a contact link. */
|
|
33
|
+
declare function classifyContactHref(href: string): ClassifiedContactLink | null;
|
|
34
|
+
|
|
35
|
+
/** Whether analytics events may be sent (after CMP / cookie banner). */
|
|
36
|
+
declare function isJmpTrackingConsentGranted(): boolean;
|
|
37
|
+
/** Subscribe to consent changes (for React `useSyncExternalStore`). */
|
|
38
|
+
declare function subscribeJmpTrackingConsent(listener: () => void): () => void;
|
|
39
|
+
/**
|
|
40
|
+
* Grant or deny analytics. Persists in `localStorage` when `requireConsent` is used.
|
|
41
|
+
* Call from your cookie banner before tracking runs.
|
|
42
|
+
*/
|
|
43
|
+
declare function setJmpTrackingConsent(granted: boolean): void;
|
|
44
|
+
|
|
45
|
+
declare function createJmpAnalytics(config: JmpAnalyticsConfig): {
|
|
46
|
+
track: (type: JmpEventType, payload: {
|
|
47
|
+
path: string;
|
|
48
|
+
metadata?: Record<string, unknown>;
|
|
49
|
+
source?: string;
|
|
50
|
+
device?: string;
|
|
51
|
+
country?: string;
|
|
52
|
+
}) => Promise<void>;
|
|
53
|
+
trackPageView: (options?: TrackPageViewOptions) => Promise<void>;
|
|
54
|
+
trackFormSubmit: (path: string, metadata?: Record<string, unknown>) => Promise<void>;
|
|
55
|
+
trackCallClick: (path: string, metadata?: Record<string, unknown>) => Promise<void>;
|
|
56
|
+
trackEmailClick: (path: string, metadata?: Record<string, unknown>) => Promise<void>;
|
|
57
|
+
trackContactClick: (path: string, options: {
|
|
58
|
+
channel: ContactLinkChannel;
|
|
59
|
+
linkHost?: string | null;
|
|
60
|
+
metadata?: Record<string, unknown>;
|
|
61
|
+
}) => Promise<void>;
|
|
62
|
+
trackLead: (options?: TrackLeadOptions) => Promise<void>;
|
|
63
|
+
subscribeToSpaNavigation: () => () => void;
|
|
64
|
+
subscribeContactLinkAutoTrack: () => () => void;
|
|
65
|
+
setTrackingConsent: typeof setJmpTrackingConsent;
|
|
66
|
+
isTrackingConsentGranted: typeof isJmpTrackingConsentGranted;
|
|
67
|
+
};
|
|
68
|
+
type JmpAnalytics = ReturnType<typeof createJmpAnalytics>;
|
|
69
|
+
|
|
70
|
+
export { BOOKING_LINK_HOSTS as B, type ClassifiedContactLink as C, type JmpAnalytics as J, subscribeJmpTrackingConsent as a, type ContactLinkChannel as b, classifyContactHref as c, createJmpAnalytics as d, isJmpTrackingConsentGranted as i, setJmpTrackingConsent as s };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public env vars for JMP — supports Next.js (`NEXT_PUBLIC_*`) and Astro (`PUBLIC_*`).
|
|
3
|
+
* Server/build: `process.env`. Astro client bundles: pass overrides from `import.meta.env`.
|
|
4
|
+
*/
|
|
5
|
+
type JmpPublicEnv = {
|
|
6
|
+
analyticsUrl?: string;
|
|
7
|
+
trackingKey?: string;
|
|
8
|
+
requireConsent?: boolean;
|
|
9
|
+
autoContactLinks?: boolean;
|
|
10
|
+
};
|
|
11
|
+
/** Read dashboard URL + tracking key from Node / build-time env. */
|
|
12
|
+
declare function readJmpPublicEnvFromProcess(overrides?: Partial<JmpPublicEnv>): JmpPublicEnv;
|
|
13
|
+
|
|
14
|
+
export { type JmpPublicEnv as J, readJmpPublicEnvFromProcess as r };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public env vars for JMP — supports Next.js (`NEXT_PUBLIC_*`) and Astro (`PUBLIC_*`).
|
|
3
|
+
* Server/build: `process.env`. Astro client bundles: pass overrides from `import.meta.env`.
|
|
4
|
+
*/
|
|
5
|
+
type JmpPublicEnv = {
|
|
6
|
+
analyticsUrl?: string;
|
|
7
|
+
trackingKey?: string;
|
|
8
|
+
requireConsent?: boolean;
|
|
9
|
+
autoContactLinks?: boolean;
|
|
10
|
+
};
|
|
11
|
+
/** Read dashboard URL + tracking key from Node / build-time env. */
|
|
12
|
+
declare function readJmpPublicEnvFromProcess(overrides?: Partial<JmpPublicEnv>): JmpPublicEnv;
|
|
13
|
+
|
|
14
|
+
export { type JmpPublicEnv as J, readJmpPublicEnvFromProcess as r };
|