@sonordev/site-kit 2.7.2 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AnalyticsProvider-GWZR3ID7.mjs +4 -0
- package/dist/{AnalyticsProvider-VAAWDXH3.mjs.map → AnalyticsProvider-GWZR3ID7.mjs.map} +1 -1
- package/dist/AnalyticsProvider-NBKOHK5M.js +25 -0
- package/dist/{AnalyticsProvider-MY64KGIH.js.map → AnalyticsProvider-NBKOHK5M.js.map} +1 -1
- package/dist/SitemapSync-4RV7BJJF.js +13 -0
- package/dist/{SitemapSync-4Y65CMUK.js.map → SitemapSync-4RV7BJJF.js.map} +1 -1
- package/dist/SitemapSync-ZCK4GWTJ.mjs +4 -0
- package/dist/{SitemapSync-3ISOAVDT.mjs.map → SitemapSync-ZCK4GWTJ.mjs.map} +1 -1
- package/dist/analytics/index.d.mts +12 -2
- package/dist/analytics/index.d.ts +12 -2
- package/dist/analytics/index.js +9 -9
- package/dist/analytics/index.mjs +2 -2
- package/dist/{chunk-5OX347TD.js → chunk-4LC4KETS.js} +5 -5
- package/dist/{chunk-5OX347TD.js.map → chunk-4LC4KETS.js.map} +1 -1
- package/dist/{chunk-UOB7WK3K.mjs → chunk-5QLM6LZ2.mjs} +20 -5
- package/dist/chunk-5QLM6LZ2.mjs.map +1 -0
- package/dist/{chunk-W5QMQMUS.mjs → chunk-FO5EUANA.mjs} +3 -3
- package/dist/{chunk-W5QMQMUS.mjs.map → chunk-FO5EUANA.mjs.map} +1 -1
- package/dist/{chunk-ZUCVEQZB.mjs → chunk-GGOHMLL5.mjs} +14 -9
- package/dist/chunk-GGOHMLL5.mjs.map +1 -0
- package/dist/{chunk-QBNA75GS.js → chunk-HANRPNKG.js} +20 -5
- package/dist/chunk-HANRPNKG.js.map +1 -0
- package/dist/{chunk-6EEXEVSE.mjs → chunk-IM57FXP7.mjs} +17 -5
- package/dist/chunk-IM57FXP7.mjs.map +1 -0
- package/dist/{chunk-JNXQX2R6.js → chunk-LFGJIGLN.js} +14 -9
- package/dist/chunk-LFGJIGLN.js.map +1 -0
- package/dist/{chunk-LWJDQ2BT.js → chunk-LPSWITNZ.js} +4 -4
- package/dist/{chunk-LWJDQ2BT.js.map → chunk-LPSWITNZ.js.map} +1 -1
- package/dist/{chunk-XCQ7BEZG.mjs → chunk-RSUNHEBH.mjs} +3 -3
- package/dist/{chunk-XCQ7BEZG.mjs.map → chunk-RSUNHEBH.mjs.map} +1 -1
- package/dist/{chunk-EJXWFTKA.js → chunk-SYNJOJTP.js} +17 -5
- package/dist/chunk-SYNJOJTP.js.map +1 -0
- package/dist/commerce/index.js +48 -48
- package/dist/commerce/index.mjs +3 -3
- package/dist/forms/index.d.mts +8 -0
- package/dist/forms/index.d.ts +8 -0
- package/dist/forms/index.js +3 -0
- package/dist/forms/index.js.map +1 -1
- package/dist/forms/index.mjs +3 -0
- package/dist/forms/index.mjs.map +1 -1
- package/dist/index.js +28 -28
- package/dist/index.mjs +3 -3
- package/dist/layout/client.d.mts +1 -1
- package/dist/layout/client.d.ts +1 -1
- package/dist/layout/client.js +2 -2
- package/dist/layout/client.mjs +1 -1
- package/dist/layout/index.d.mts +1 -1
- package/dist/layout/index.d.ts +1 -1
- package/dist/layout/index.js +2 -2
- package/dist/layout/index.mjs +1 -1
- package/dist/seo/client.js +2 -2
- package/dist/seo/client.mjs +1 -1
- package/dist/seo/index.js +2 -2
- package/dist/seo/index.mjs +1 -1
- package/dist/{types-5RCOK10v.d.ts → types-YXQAZGpe.d.mts} +9 -0
- package/dist/{types-5RCOK10v.d.mts → types-YXQAZGpe.d.ts} +9 -0
- package/package.json +1 -1
- package/dist/AnalyticsProvider-MY64KGIH.js +0 -25
- package/dist/AnalyticsProvider-VAAWDXH3.mjs +0 -4
- package/dist/SitemapSync-3ISOAVDT.mjs +0 -4
- package/dist/SitemapSync-4Y65CMUK.js +0 -13
- package/dist/chunk-6EEXEVSE.mjs.map +0 -1
- package/dist/chunk-EJXWFTKA.js.map +0 -1
- package/dist/chunk-JNXQX2R6.js.map +0 -1
- package/dist/chunk-QBNA75GS.js.map +0 -1
- package/dist/chunk-UOB7WK3K.mjs.map +0 -1
- package/dist/chunk-ZUCVEQZB.mjs.map +0 -1
|
@@ -51,7 +51,7 @@ function SiteKitIdentityProvider({
|
|
|
51
51
|
return /* @__PURE__ */ jsx(SiteKitIdentityContext.Provider, { value, children });
|
|
52
52
|
}
|
|
53
53
|
var LazyAnalyticsProvider = dynamic(
|
|
54
|
-
() => import('./AnalyticsProvider-
|
|
54
|
+
() => import('./AnalyticsProvider-GWZR3ID7.mjs').then((m) => ({ default: m.AnalyticsProvider })),
|
|
55
55
|
{ ssr: false }
|
|
56
56
|
);
|
|
57
57
|
var LazyEngageWidget = dynamic(
|
|
@@ -63,7 +63,7 @@ var LazySignalBridge = dynamic(
|
|
|
63
63
|
{ ssr: false }
|
|
64
64
|
);
|
|
65
65
|
var LazySitemapSync = dynamic(
|
|
66
|
-
() => import('./SitemapSync-
|
|
66
|
+
() => import('./SitemapSync-ZCK4GWTJ.mjs').then((m) => ({ default: m.SitemapSync })),
|
|
67
67
|
{ ssr: false }
|
|
68
68
|
);
|
|
69
69
|
function SiteKitClientProviders({
|
|
@@ -77,10 +77,25 @@ function SiteKitClientProviders({
|
|
|
77
77
|
sitemapSync = true,
|
|
78
78
|
debug = false
|
|
79
79
|
}) {
|
|
80
|
+
const analyticsConfig = typeof analytics === "object" ? analytics : {};
|
|
81
|
+
const explicitSite = analyticsConfig.site;
|
|
82
|
+
const envSiteUrl = typeof process !== "undefined" ? process.env.NEXT_PUBLIC_SITE_URL : void 0;
|
|
83
|
+
let resolvedSite = explicitSite ?? "";
|
|
84
|
+
if (!resolvedSite && envSiteUrl) {
|
|
85
|
+
try {
|
|
86
|
+
resolvedSite = new URL(envSiteUrl).host;
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (!resolvedSite && typeof window !== "undefined") {
|
|
91
|
+
resolvedSite = window.location.host;
|
|
92
|
+
}
|
|
93
|
+
const site = resolvedSite;
|
|
80
94
|
if (typeof window !== "undefined") {
|
|
81
95
|
window.__SITE_KIT_API_URL__ = apiUrl;
|
|
82
96
|
window.__SITE_KIT_API_KEY__ = apiKey;
|
|
83
97
|
window.__SITE_KIT_DEBUG__ = debug;
|
|
98
|
+
window.__SITE_KIT_SITE__ = site;
|
|
84
99
|
}
|
|
85
100
|
const [visitorId] = useState(() => getOrCreateVisitorId());
|
|
86
101
|
const [sessionId] = useState(() => getOrCreateSessionId());
|
|
@@ -106,7 +121,6 @@ function SiteKitClientProviders({
|
|
|
106
121
|
);
|
|
107
122
|
}
|
|
108
123
|
if (analytics) {
|
|
109
|
-
const analyticsConfig = typeof analytics === "object" ? analytics : {};
|
|
110
124
|
content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
111
125
|
content,
|
|
112
126
|
/* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
|
|
@@ -114,6 +128,7 @@ function SiteKitClientProviders({
|
|
|
114
128
|
{
|
|
115
129
|
apiUrl,
|
|
116
130
|
apiKey,
|
|
131
|
+
site,
|
|
117
132
|
trackPageViews: analyticsConfig.trackPageViews !== false,
|
|
118
133
|
trackWebVitals: analyticsConfig.trackWebVitals !== false,
|
|
119
134
|
trackScrollDepth: analyticsConfig.trackScrollDepth !== false,
|
|
@@ -152,5 +167,5 @@ function SiteKitClientProviders({
|
|
|
152
167
|
}
|
|
153
168
|
|
|
154
169
|
export { SiteKitClientProviders };
|
|
155
|
-
//# sourceMappingURL=chunk-
|
|
156
|
-
//# sourceMappingURL=chunk-
|
|
170
|
+
//# sourceMappingURL=chunk-5QLM6LZ2.mjs.map
|
|
171
|
+
//# sourceMappingURL=chunk-5QLM6LZ2.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/identity.tsx","../src/layout/SiteKitClientProviders.tsx"],"names":["jsx"],"mappings":"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","file":"chunk-5QLM6LZ2.mjs","sourcesContent":["/**\n * @sonordev/site-kit/shared — Unified Identity Management\n *\n * Single source of truth for visitor and session IDs across\n * Analytics, Signal, and Engage modules.\n *\n * Storage keys:\n * localStorage: _sk_vid (visitor ID, persists ~forever)\n * sessionStorage: _sk_sid (session ID, per-tab)\n * sessionStorage: _sk_stime (last activity timestamp for timeout)\n *\n * Storage keys are prefixed with `_sk_*`.\n */\n\n'use client'\n\nimport React, { createContext, useContext, useMemo } from 'react'\n\n// ============================================\n// ID Generation\n// ============================================\n\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID()\n }\n // Fallback for older environments\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0\n return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16)\n })\n}\n\n/**\n * Get or create a persistent visitor ID.\n * Stored in localStorage under `_sk_vid`.\n */\nexport function getOrCreateVisitorId(): string {\n if (typeof window === 'undefined') return ''\n\n let visitorId = localStorage.getItem('_sk_vid')\n\n if (!visitorId) {\n visitorId = generateId()\n }\n\n localStorage.setItem('_sk_vid', visitorId)\n return visitorId\n}\n\n/**\n * Get or create a session ID with inactivity timeout.\n * Stored in sessionStorage under `_sk_sid`.\n * Session expires after `timeoutMinutes` of inactivity (default 30).\n */\nexport function getOrCreateSessionId(timeoutMinutes = 30): string {\n if (typeof window === 'undefined') return ''\n\n const now = Date.now()\n const timeoutMs = timeoutMinutes * 60 * 1000\n\n const existingSession = sessionStorage.getItem('_sk_sid')\n const lastActivity = sessionStorage.getItem('_sk_stime')\n\n if (existingSession && lastActivity) {\n const elapsed = now - parseInt(lastActivity, 10)\n if (elapsed < timeoutMs) {\n // Session still active — refresh timestamp\n sessionStorage.setItem('_sk_stime', now.toString())\n sessionStorage.setItem('_sk_sid', existingSession)\n return existingSession\n }\n }\n\n // New session (first visit or timeout expired)\n const newSession = generateId()\n sessionStorage.setItem('_sk_sid', newSession)\n sessionStorage.setItem('_sk_stime', now.toString())\n return newSession\n}\n\n// ============================================\n// React Context\n// ============================================\n\nexport interface SiteKitIdentity {\n visitorId: string\n sessionId: string\n}\n\nconst SiteKitIdentityContext = createContext<SiteKitIdentity | null>(null)\n\nexport interface SiteKitIdentityProviderProps {\n visitorId: string\n sessionId: string\n children: React.ReactNode\n}\n\nexport function SiteKitIdentityProvider({\n visitorId,\n sessionId,\n children,\n}: SiteKitIdentityProviderProps) {\n const value = useMemo(() => ({ visitorId, sessionId }), [visitorId, sessionId])\n\n return (\n <SiteKitIdentityContext.Provider value={value}>\n {children}\n </SiteKitIdentityContext.Provider>\n )\n}\n\n/**\n * Access the shared visitor/session identity.\n * Falls back to generating IDs directly if used outside of SiteKitIdentityProvider.\n */\nexport function useSiteKitIdentity(): SiteKitIdentity {\n const context = useContext(SiteKitIdentityContext)\n if (context) return context\n\n // Fallback for standalone usage outside SiteKitLayout\n return {\n visitorId: getOrCreateVisitorId(),\n sessionId: getOrCreateSessionId(),\n }\n}\n","'use client'\n\n/**\n * SiteKitClientProviders — Client Island\n *\n * This is the 'use client' boundary for SiteKitLayout.\n * It composes the client-only providers (Analytics, Engage, Signal, SitemapSync)\n * that require React context or browser APIs.\n *\n * All modules are lazy-loaded via next/dynamic so their JS is only downloaded\n * when the feature is enabled — keeping the initial bundle lightweight.\n *\n * All modules share a unified identity (visitor ID + session ID) via\n * SiteKitIdentityProvider, ensuring consistent tracking across Analytics,\n * Signal, and Engage.\n *\n * Props are passed down from the server-side SiteKitLayout component,\n * which reads env vars at render time.\n */\n\nimport React, { Suspense, useCallback, useRef, useState, type ReactNode } from 'react'\nimport dynamic from 'next/dynamic'\nimport {\n SiteKitIdentityProvider,\n getOrCreateVisitorId,\n getOrCreateSessionId,\n} from '../shared/identity'\nimport type { AnalyticsConfig, EngageConfig, SignalConfig } from './types'\n\n// Lazy-load feature modules — JS only downloaded when feature is enabled\nconst LazyAnalyticsProvider = dynamic(\n () => import('../analytics/AnalyticsProvider').then(m => ({ default: m.AnalyticsProvider })),\n { ssr: false },\n)\nconst LazyEngageWidget = dynamic(\n () => import('../engage/EngageWidget').then(m => ({ default: m.EngageWidget })),\n { ssr: false },\n)\nconst LazySignalBridge = dynamic(\n () => import('../signal/SignalBridge').then(m => ({ default: m.SignalBridge })),\n { ssr: false },\n)\nconst LazySitemapSync = dynamic(\n () => import('../SitemapSync').then(m => ({ default: m.SitemapSync })),\n { ssr: false },\n)\n\nexport interface SiteKitClientProvidersProps {\n children: ReactNode\n apiKey: string\n apiUrl: string\n projectId?: string\n analytics?: boolean | AnalyticsConfig\n engage?: boolean | EngageConfig\n signal?: boolean | SignalConfig\n sitemapSync?: boolean\n debug?: boolean\n}\n\nexport function SiteKitClientProviders({\n children,\n apiKey,\n apiUrl,\n projectId,\n analytics = true,\n engage = true,\n signal = false,\n sitemapSync = true,\n debug = false,\n}: SiteKitClientProvidersProps) {\n // Hoist analytics config so the `site` dimension is resolvable up here.\n const analyticsConfig: AnalyticsConfig =\n typeof analytics === 'object' ? analytics : {}\n\n // Resolve the sub-site identifier — the dimension that lets one Sonor project\n // host many sites (e.g. truepowersystems.com + ohiopowerstudies.com under one\n // TPS project). Precedence: explicit `analytics.site` > NEXT_PUBLIC_SITE_URL\n // host > window.location.host. Always a string (possibly empty).\n const explicitSite = analyticsConfig.site\n const envSiteUrl =\n typeof process !== 'undefined'\n ? process.env.NEXT_PUBLIC_SITE_URL\n : undefined\n let resolvedSite = explicitSite ?? ''\n if (!resolvedSite && envSiteUrl) {\n try {\n resolvedSite = new URL(envSiteUrl).host\n } catch {\n /* malformed URL — fall through to client-side host */\n }\n }\n if (!resolvedSite && typeof window !== 'undefined') {\n resolvedSite = window.location.host\n }\n const site = resolvedSite\n\n // Set window globals for modules that still read from them.\n if (typeof window !== 'undefined') {\n ;(window as any).__SITE_KIT_API_URL__ = apiUrl\n ;(window as any).__SITE_KIT_API_KEY__ = apiKey\n ;(window as any).__SITE_KIT_DEBUG__ = debug\n ;(window as any).__SITE_KIT_SITE__ = site\n }\n\n // Unified identity — single source of truth for all modules\n const [visitorId] = useState(() => getOrCreateVisitorId())\n const [sessionId] = useState(() => getOrCreateSessionId())\n\n // Page metadata bridge: Analytics writes, Signal reads\n const pageMetadataRef = useRef<Record<string, unknown> | null>(null)\n const onPageMetadata = useCallback((metadata: Record<string, unknown>) => {\n pageMetadataRef.current = metadata\n }, [])\n\n let content = <>{children}</>\n\n // Wrap with SignalBridge if enabled.\n // NOTE: SignalBridge still wraps {content} and is dynamic(ssr:false) — the\n // same SSR-de-opt caveat as Analytics applies. It is left wrapping because\n // A/B experiment variant swapping may depend on enclosing page content; if a\n // site enables `signal` and SSR breaks, convert this to a sibling too.\n if (signal) {\n const signalConfig: SignalConfig =\n typeof signal === 'object' ? signal : {}\n content = (\n <LazySignalBridge\n enabled\n realtime={signalConfig.realtime !== false}\n experiments={signalConfig.experiments !== false}\n behaviorTracking={signalConfig.behaviorTracking !== false}\n visitorId={visitorId}\n sessionId={sessionId}\n pageMetadataRef={pageMetadataRef}\n >\n {content}\n </LazySignalBridge>\n )\n }\n\n // Add Analytics if enabled (renders alongside, doesn't wrap).\n //\n // CRITICAL: LazyAnalyticsProvider is dynamic(..., { ssr: false }). Wrapping\n // {content} inside an ssr:false component makes the entire page tree skip\n // server rendering — the route de-opts to 100% client rendering and the\n // hero / LCP element only paints after hydration (mobile LCP ~4s, Lighthouse\n // ~85). Analytics tracks via effects + a metadata ref, so it never needs to\n // wrap page content — mount it as a childless sibling, exactly like Engage.\n if (analytics) {\n content = (\n <>\n {content}\n <Suspense fallback={null}>\n <LazyAnalyticsProvider\n apiUrl={apiUrl}\n apiKey={apiKey}\n site={site}\n trackPageViews={analyticsConfig.trackPageViews !== false}\n trackWebVitals={analyticsConfig.trackWebVitals !== false}\n trackScrollDepth={analyticsConfig.trackScrollDepth !== false}\n trackClicks={analyticsConfig.trackClicks !== false}\n debug={debug}\n externalVisitorId={visitorId}\n externalSessionId={sessionId}\n onPageMetadata={onPageMetadata}\n />\n </Suspense>\n </>\n )\n }\n\n // Add Engage widget if enabled (renders alongside, doesn't wrap)\n if (engage) {\n const engageConfig: EngageConfig =\n typeof engage === 'object' ? engage : {}\n content = (\n <>\n {content}\n <LazyEngageWidget\n apiUrl={apiUrl}\n apiKey={apiKey}\n projectId={projectId}\n position={engageConfig.position || 'bottom-right'}\n chatEnabled={engageConfig.chatEnabled !== false}\n />\n </>\n )\n }\n\n // Add SitemapSync if enabled\n if (sitemapSync) {\n content = (\n <>\n {content}\n <LazySitemapSync debug={debug} />\n </>\n )\n }\n\n return (\n <SiteKitIdentityProvider visitorId={visitorId} sessionId={sessionId}>\n {content}\n </SiteKitIdentityProvider>\n )\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { useAnalytics } from './chunk-
|
|
2
|
+
import { useAnalytics } from './chunk-IM57FXP7.mjs';
|
|
3
3
|
import { useCallback, useEffect } from 'react';
|
|
4
4
|
|
|
5
5
|
function useContactTracking(options = {}) {
|
|
@@ -65,5 +65,5 @@ function ContactTracking({ debug = false }) {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
export { ContactTracking, useContactTracking };
|
|
68
|
-
//# sourceMappingURL=chunk-
|
|
69
|
-
//# sourceMappingURL=chunk-
|
|
68
|
+
//# sourceMappingURL=chunk-FO5EUANA.mjs.map
|
|
69
|
+
//# sourceMappingURL=chunk-FO5EUANA.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/analytics/useContactTracking.ts"],"names":[],"mappings":"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","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/analytics/useContactTracking.ts"],"names":[],"mappings":"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","file":"chunk-FO5EUANA.mjs","sourcesContent":["/**\n * @sonordev/site-kit/analytics - Contact Tracking Hook\n * \n * Tracks phone calls and email clicks as conversions.\n * Use this hook to automatically track tel: and mailto: links,\n * or manually track contact interactions.\n */\n\n'use client'\n\nimport { useCallback, useEffect } from 'react'\nimport { useAnalytics } from './AnalyticsProvider'\n\nexport interface UseContactTrackingOptions {\n /** Automatically track all tel: and mailto: links (default: true) */\n autoTrack?: boolean\n /** Debug mode - logs events to console */\n debug?: boolean\n}\n\nexport interface ContactTrackingReturn {\n /** Manually track a phone call click */\n trackPhoneClick: (phoneNumber: string, metadata?: Record<string, unknown>) => void\n /** Manually track an email click */\n trackEmailClick: (email: string, metadata?: Record<string, unknown>) => void\n}\n\n/**\n * Hook for tracking phone and email contact interactions as conversions.\n * \n * @example\n * ```tsx\n * // Auto-track all tel: and mailto: links\n * useContactTracking()\n * \n * // Manual tracking\n * const { trackPhoneClick, trackEmailClick } = useContactTracking()\n * trackPhoneClick('513-555-1234')\n * trackEmailClick('hello@example.com')\n * ```\n */\nexport function useContactTracking(options: UseContactTrackingOptions = {}): ContactTrackingReturn {\n const { autoTrack = true, debug = false } = options\n const { trackConversion } = useAnalytics()\n \n const trackPhoneClick = useCallback((phoneNumber: string, metadata?: Record<string, unknown>) => {\n if (debug) console.log('[Analytics] Phone click:', phoneNumber)\n \n trackConversion({\n type: 'phone_call',\n metadata: {\n phoneNumber: phoneNumber.replace(/\\D/g, ''), // Normalize to digits\n rawNumber: phoneNumber,\n ...metadata,\n },\n })\n }, [trackConversion, debug])\n \n const trackEmailClick = useCallback((email: string, metadata?: Record<string, unknown>) => {\n if (debug) console.log('[Analytics] Email click:', email)\n \n trackConversion({\n type: 'email_click',\n metadata: {\n email,\n ...metadata,\n },\n })\n }, [trackConversion, debug])\n \n // Auto-track tel: and mailto: links\n useEffect(() => {\n if (!autoTrack || typeof document === 'undefined') return\n \n const handleClick = (e: MouseEvent) => {\n const target = e.target as HTMLElement\n const anchor = target.closest('a')\n if (!anchor) return\n \n const href = anchor.getAttribute('href')\n if (!href) return\n \n if (href.startsWith('tel:')) {\n const phone = href.replace('tel:', '').trim()\n trackPhoneClick(phone, {\n linkText: anchor.textContent?.trim(),\n source: 'auto',\n })\n } else if (href.startsWith('mailto:')) {\n const email = href.replace('mailto:', '').split('?')[0].trim()\n trackEmailClick(email, {\n linkText: anchor.textContent?.trim(),\n source: 'auto',\n })\n }\n }\n \n document.addEventListener('click', handleClick, { capture: true })\n \n return () => {\n document.removeEventListener('click', handleClick, { capture: true })\n }\n }, [autoTrack, trackPhoneClick, trackEmailClick])\n \n return {\n trackPhoneClick,\n trackEmailClick,\n }\n}\n\n/**\n * Component wrapper for contact tracking\n * Simply include this component to auto-track all tel: and mailto: links\n * \n * @example\n * ```tsx\n * <ContactTracking />\n * ```\n */\nexport function ContactTracking({ debug = false }: { debug?: boolean }) {\n useContactTracking({ autoTrack: true, debug })\n return null\n}\n"]}
|
|
@@ -14,10 +14,11 @@ function inferPageType(urlPath) {
|
|
|
14
14
|
return "page";
|
|
15
15
|
}
|
|
16
16
|
function getApiConfig() {
|
|
17
|
-
if (typeof window === "undefined") return { apiUrl: "", apiKey: "" };
|
|
17
|
+
if (typeof window === "undefined") return { apiUrl: "", apiKey: "", site: "" };
|
|
18
18
|
const apiUrl = window.__SITE_KIT_API_URL__ || "https://api.sonor.io";
|
|
19
19
|
const apiKey = window.__SITE_KIT_API_KEY__ || "";
|
|
20
|
-
|
|
20
|
+
const site = window.__SITE_KIT_SITE__ || window.location.host || "";
|
|
21
|
+
return { apiUrl, apiKey, site };
|
|
21
22
|
}
|
|
22
23
|
async function parseSitemap(sitemapUrl, debug) {
|
|
23
24
|
try {
|
|
@@ -68,7 +69,7 @@ async function parseSitemap(sitemapUrl, debug) {
|
|
|
68
69
|
return [];
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
|
-
async function syncToPortal(entries, apiUrl, apiKey, debug, mode = "additive") {
|
|
72
|
+
async function syncToPortal(entries, apiUrl, apiKey, site, debug, mode = "additive") {
|
|
72
73
|
if (!apiKey) {
|
|
73
74
|
if (debug) console.warn("[SitemapSync] No API key configured");
|
|
74
75
|
return { success: false, created: 0, updated: 0 };
|
|
@@ -91,7 +92,11 @@ async function syncToPortal(entries, apiUrl, apiKey, debug, mode = "additive") {
|
|
|
91
92
|
changefreq: e.changefreq,
|
|
92
93
|
pageType: inferPageType(e.path)
|
|
93
94
|
})),
|
|
94
|
-
mode
|
|
95
|
+
mode,
|
|
96
|
+
// Sub-site host — tagged on every seo_pages row so the dashboard
|
|
97
|
+
// can scope the page tree to one microsite. Resolved by
|
|
98
|
+
// SiteKitClientProviders (see __SITE_KIT_SITE__).
|
|
99
|
+
...site ? { site } : {}
|
|
95
100
|
})
|
|
96
101
|
});
|
|
97
102
|
if (!response.ok) {
|
|
@@ -123,15 +128,15 @@ function SitemapSync({
|
|
|
123
128
|
useEffect(() => {
|
|
124
129
|
const effectiveMode = "additive";
|
|
125
130
|
const doSync = async () => {
|
|
126
|
-
const { apiUrl, apiKey } = getApiConfig();
|
|
131
|
+
const { apiUrl, apiKey, site } = getApiConfig();
|
|
127
132
|
if (!apiKey) {
|
|
128
133
|
if (debug) console.warn("[SitemapSync] No API key found");
|
|
129
134
|
return;
|
|
130
135
|
}
|
|
131
|
-
if (debug) console.log("[SitemapSync] Starting sitemap sync...");
|
|
136
|
+
if (debug) console.log("[SitemapSync] Starting sitemap sync...", site ? `(site: ${site})` : "");
|
|
132
137
|
const entries = await parseSitemap(sitemapUrl, debug);
|
|
133
138
|
if (entries.length > 0) {
|
|
134
|
-
await syncToPortal(entries, apiUrl, apiKey, debug, effectiveMode);
|
|
139
|
+
await syncToPortal(entries, apiUrl, apiKey, site, debug, effectiveMode);
|
|
135
140
|
}
|
|
136
141
|
};
|
|
137
142
|
if (!hasSyncedRef.current) {
|
|
@@ -151,5 +156,5 @@ function SitemapSync({
|
|
|
151
156
|
}
|
|
152
157
|
|
|
153
158
|
export { SitemapSync };
|
|
154
|
-
//# sourceMappingURL=chunk-
|
|
155
|
-
//# sourceMappingURL=chunk-
|
|
159
|
+
//# sourceMappingURL=chunk-GGOHMLL5.mjs.map
|
|
160
|
+
//# sourceMappingURL=chunk-GGOHMLL5.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/SitemapSync.tsx"],"names":[],"mappings":"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","file":"chunk-GGOHMLL5.mjs","sourcesContent":["/**\n * SitemapSync - Automatically sync sitemap.xml to Portal API\n *\n * Client-only component. Lives at package root so the main entry does not\n * pull in seo/ (and server-only code). For use in SiteKitProvider and\n * from @sonordev/site-kit/seo.\n *\n * @example\n * ```tsx\n * import { SitemapSync } from '@sonordev/site-kit'\n * // or\n * import { SitemapSync } from '@sonordev/site-kit/seo'\n * ```\n */\n\n'use client'\n\nimport { useEffect, useRef } from 'react'\n\ninterface SitemapSyncProps {\n /** Custom sitemap URL (defaults to /sitemap.xml) */\n sitemapUrl?: string\n /** How often to re-sync in minutes (0 = only on mount, default: 60) */\n syncInterval?: number\n /** Enable debug logging */\n debug?: boolean\n /**\n * Sync mode:\n * - 'additive' (default for runtime): adds/updates pages but never deletes existing ones\n * - 'full-replace': replaces the entire page set (use only for build-time sync via createSitemap)\n */\n mode?: 'additive' | 'full-replace'\n}\n\ninterface SitemapEntry {\n path: string\n lastmod?: string\n changefreq?: string\n priority?: number\n pageType?: string\n}\n\n/**\n * Infer page type from URL path pattern for Signal context.\n */\nfunction inferPageType(urlPath: string): string {\n if (urlPath === '/' || urlPath === '') return 'homepage'\n if (/^\\/services(\\/|$)/i.test(urlPath)) return 'service'\n if (/^\\/compare(\\/|$)/i.test(urlPath)) return 'comparison'\n if (/^\\/(insights|blog)(\\/|$)/i.test(urlPath)) return 'blog'\n if (/^\\/industries(\\/|$)/i.test(urlPath)) return 'industry'\n if (/^\\/work(\\/|$)/i.test(urlPath)) return 'portfolio'\n if (/^\\/faq(\\/|$)/i.test(urlPath)) return 'faq'\n if (/^\\/pricing(\\/|$)/i.test(urlPath)) return 'pricing'\n return 'page'\n}\n\nfunction getApiConfig() {\n if (typeof window === 'undefined') return { apiUrl: '', apiKey: '', site: '' }\n\n const apiUrl = (window as any).__SITE_KIT_API_URL__ || 'https://api.sonor.io'\n const apiKey = (window as any).__SITE_KIT_API_KEY__ || ''\n // Sub-site host — set by SiteKitClientProviders from analytics.site config,\n // NEXT_PUBLIC_SITE_URL host, or window.location.host. Tagged on every\n // seo_pages row from this sitemap so the dashboard can scope the page tree\n // when one Sonor project hosts many sites.\n const site = (window as any).__SITE_KIT_SITE__ || window.location.host || ''\n return { apiUrl, apiKey, site }\n}\n\nasync function parseSitemap(sitemapUrl: string, debug: boolean): Promise<SitemapEntry[]> {\n try {\n const response = await fetch(sitemapUrl)\n if (!response.ok) {\n if (debug) console.warn('[SitemapSync] Failed to fetch sitemap:', response.status)\n return []\n }\n\n const text = await response.text()\n const parser = new DOMParser()\n const doc = parser.parseFromString(text, 'application/xml')\n\n const parseError = doc.querySelector('parsererror')\n if (parseError) {\n if (debug) console.warn('[SitemapSync] Sitemap parse error:', parseError.textContent)\n return []\n }\n\n const urls = doc.querySelectorAll('url')\n const entries: SitemapEntry[] = []\n\n urls.forEach(url => {\n const loc = url.querySelector('loc')?.textContent?.trim()\n if (!loc) return\n\n let path: string\n try {\n const urlObj = new URL(loc)\n path = urlObj.pathname\n } catch {\n path = loc.startsWith('/') ? loc : `/${loc}`\n }\n\n if (path !== '/' && path.endsWith('/')) {\n path = path.slice(0, -1)\n }\n\n // Reject malformed paths (embedded domains, double protocols, etc.)\n if (path.includes('://') || /\\/\\/[a-z0-9.-]+\\.[a-z]{2,}/i.test(path)) {\n if (debug) console.warn(`[SitemapSync] Skipping malformed path: ${path}`)\n return\n }\n\n const lastmod = url.querySelector('lastmod')?.textContent || undefined\n const changefreq = url.querySelector('changefreq')?.textContent || undefined\n const priorityStr = url.querySelector('priority')?.textContent\n const priority = priorityStr ? parseFloat(priorityStr) : undefined\n\n entries.push({ path, lastmod, changefreq, priority })\n })\n\n if (debug) {\n console.log(`[SitemapSync] Parsed ${entries.length} entries from sitemap`)\n }\n\n return entries\n } catch (error) {\n if (debug) console.error('[SitemapSync] Error parsing sitemap:', error)\n return []\n }\n}\n\nasync function syncToPortal(\n entries: SitemapEntry[],\n apiUrl: string,\n apiKey: string,\n site: string,\n debug: boolean,\n mode: 'additive' | 'full-replace' = 'additive'\n): Promise<{ success: boolean; created: number; updated: number }> {\n if (!apiKey) {\n if (debug) console.warn('[SitemapSync] No API key configured')\n return { success: false, created: 0, updated: 0 }\n }\n\n if (entries.length === 0) {\n if (debug) console.log('[SitemapSync] No entries to sync')\n return { success: true, created: 0, updated: 0 }\n }\n\n try {\n const response = await fetch(`${apiUrl}/api/public/seo/register-sitemap`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n body: JSON.stringify({\n entries: entries.map(e => ({\n path: e.path,\n priority: e.priority,\n changefreq: e.changefreq,\n pageType: inferPageType(e.path),\n })),\n mode,\n // Sub-site host — tagged on every seo_pages row so the dashboard\n // can scope the page tree to one microsite. Resolved by\n // SiteKitClientProviders (see __SITE_KIT_SITE__).\n ...(site ? { site } : {}),\n }),\n })\n\n if (!response.ok) {\n if (debug) console.error('[SitemapSync] API error:', response.status, response.statusText)\n return { success: false, created: 0, updated: 0 }\n }\n\n const result = await response.json()\n\n if (debug) {\n console.log(`[SitemapSync] Synced: ${result.created} created, ${result.updated} updated`)\n }\n\n return {\n success: true,\n created: result.created || 0,\n updated: result.updated || 0,\n }\n } catch (error) {\n if (debug) console.error('[SitemapSync] Sync error:', error)\n return { success: false, created: 0, updated: 0 }\n }\n}\n\nexport function SitemapSync({\n sitemapUrl = '/sitemap.xml',\n syncInterval = 60,\n debug = false,\n mode = 'additive',\n}: SitemapSyncProps) {\n const hasSyncedRef = useRef(false)\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null)\n\n useEffect(() => {\n // Build-time sync via createSitemap is the canonical source of truth, but\n // it can fail during ISR revalidation (app/ dir unavailable on serverless).\n // Runtime SitemapSync in additive mode is a safe fallback — it adds/updates\n // pages that may be missing without ever deleting existing ones.\n // Full-replace is never safe at runtime — always force additive.\n const effectiveMode: 'additive' | 'full-replace' = 'additive'\n\n const doSync = async () => {\n const { apiUrl, apiKey, site } = getApiConfig()\n\n if (!apiKey) {\n if (debug) console.warn('[SitemapSync] No API key found')\n return\n }\n\n if (debug) console.log('[SitemapSync] Starting sitemap sync...', site ? `(site: ${site})` : '')\n\n const entries = await parseSitemap(sitemapUrl, debug)\n\n if (entries.length > 0) {\n await syncToPortal(entries, apiUrl, apiKey, site, debug, effectiveMode)\n }\n }\n\n if (!hasSyncedRef.current) {\n hasSyncedRef.current = true\n doSync()\n }\n\n if (syncInterval > 0) {\n intervalRef.current = setInterval(doSync, syncInterval * 60 * 1000)\n }\n\n return () => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current)\n }\n }\n }, [sitemapUrl, syncInterval, debug, mode])\n\n return null\n}\n"]}
|
|
@@ -57,7 +57,7 @@ function SiteKitIdentityProvider({
|
|
|
57
57
|
return /* @__PURE__ */ jsxRuntime.jsx(SiteKitIdentityContext.Provider, { value, children });
|
|
58
58
|
}
|
|
59
59
|
var LazyAnalyticsProvider = dynamic__default.default(
|
|
60
|
-
() => import('./AnalyticsProvider-
|
|
60
|
+
() => import('./AnalyticsProvider-NBKOHK5M.js').then((m) => ({ default: m.AnalyticsProvider })),
|
|
61
61
|
{ ssr: false }
|
|
62
62
|
);
|
|
63
63
|
var LazyEngageWidget = dynamic__default.default(
|
|
@@ -69,7 +69,7 @@ var LazySignalBridge = dynamic__default.default(
|
|
|
69
69
|
{ ssr: false }
|
|
70
70
|
);
|
|
71
71
|
var LazySitemapSync = dynamic__default.default(
|
|
72
|
-
() => import('./SitemapSync-
|
|
72
|
+
() => import('./SitemapSync-4RV7BJJF.js').then((m) => ({ default: m.SitemapSync })),
|
|
73
73
|
{ ssr: false }
|
|
74
74
|
);
|
|
75
75
|
function SiteKitClientProviders({
|
|
@@ -83,10 +83,25 @@ function SiteKitClientProviders({
|
|
|
83
83
|
sitemapSync = true,
|
|
84
84
|
debug = false
|
|
85
85
|
}) {
|
|
86
|
+
const analyticsConfig = typeof analytics === "object" ? analytics : {};
|
|
87
|
+
const explicitSite = analyticsConfig.site;
|
|
88
|
+
const envSiteUrl = typeof process !== "undefined" ? process.env.NEXT_PUBLIC_SITE_URL : void 0;
|
|
89
|
+
let resolvedSite = explicitSite ?? "";
|
|
90
|
+
if (!resolvedSite && envSiteUrl) {
|
|
91
|
+
try {
|
|
92
|
+
resolvedSite = new URL(envSiteUrl).host;
|
|
93
|
+
} catch {
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (!resolvedSite && typeof window !== "undefined") {
|
|
97
|
+
resolvedSite = window.location.host;
|
|
98
|
+
}
|
|
99
|
+
const site = resolvedSite;
|
|
86
100
|
if (typeof window !== "undefined") {
|
|
87
101
|
window.__SITE_KIT_API_URL__ = apiUrl;
|
|
88
102
|
window.__SITE_KIT_API_KEY__ = apiKey;
|
|
89
103
|
window.__SITE_KIT_DEBUG__ = debug;
|
|
104
|
+
window.__SITE_KIT_SITE__ = site;
|
|
90
105
|
}
|
|
91
106
|
const [visitorId] = react.useState(() => getOrCreateVisitorId());
|
|
92
107
|
const [sessionId] = react.useState(() => getOrCreateSessionId());
|
|
@@ -112,7 +127,6 @@ function SiteKitClientProviders({
|
|
|
112
127
|
);
|
|
113
128
|
}
|
|
114
129
|
if (analytics) {
|
|
115
|
-
const analyticsConfig = typeof analytics === "object" ? analytics : {};
|
|
116
130
|
content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
117
131
|
content,
|
|
118
132
|
/* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -120,6 +134,7 @@ function SiteKitClientProviders({
|
|
|
120
134
|
{
|
|
121
135
|
apiUrl,
|
|
122
136
|
apiKey,
|
|
137
|
+
site,
|
|
123
138
|
trackPageViews: analyticsConfig.trackPageViews !== false,
|
|
124
139
|
trackWebVitals: analyticsConfig.trackWebVitals !== false,
|
|
125
140
|
trackScrollDepth: analyticsConfig.trackScrollDepth !== false,
|
|
@@ -158,5 +173,5 @@ function SiteKitClientProviders({
|
|
|
158
173
|
}
|
|
159
174
|
|
|
160
175
|
exports.SiteKitClientProviders = SiteKitClientProviders;
|
|
161
|
-
//# sourceMappingURL=chunk-
|
|
162
|
-
//# sourceMappingURL=chunk-
|
|
176
|
+
//# sourceMappingURL=chunk-HANRPNKG.js.map
|
|
177
|
+
//# sourceMappingURL=chunk-HANRPNKG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/identity.tsx","../src/layout/SiteKitClientProviders.tsx"],"names":["createContext","useMemo","jsx","dynamic","useState","useRef","useCallback","Fragment","jsxs","Suspense"],"mappings":"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","file":"chunk-HANRPNKG.js","sourcesContent":["/**\n * @sonordev/site-kit/shared — Unified Identity Management\n *\n * Single source of truth for visitor and session IDs across\n * Analytics, Signal, and Engage modules.\n *\n * Storage keys:\n * localStorage: _sk_vid (visitor ID, persists ~forever)\n * sessionStorage: _sk_sid (session ID, per-tab)\n * sessionStorage: _sk_stime (last activity timestamp for timeout)\n *\n * Storage keys are prefixed with `_sk_*`.\n */\n\n'use client'\n\nimport React, { createContext, useContext, useMemo } from 'react'\n\n// ============================================\n// ID Generation\n// ============================================\n\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID()\n }\n // Fallback for older environments\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0\n return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16)\n })\n}\n\n/**\n * Get or create a persistent visitor ID.\n * Stored in localStorage under `_sk_vid`.\n */\nexport function getOrCreateVisitorId(): string {\n if (typeof window === 'undefined') return ''\n\n let visitorId = localStorage.getItem('_sk_vid')\n\n if (!visitorId) {\n visitorId = generateId()\n }\n\n localStorage.setItem('_sk_vid', visitorId)\n return visitorId\n}\n\n/**\n * Get or create a session ID with inactivity timeout.\n * Stored in sessionStorage under `_sk_sid`.\n * Session expires after `timeoutMinutes` of inactivity (default 30).\n */\nexport function getOrCreateSessionId(timeoutMinutes = 30): string {\n if (typeof window === 'undefined') return ''\n\n const now = Date.now()\n const timeoutMs = timeoutMinutes * 60 * 1000\n\n const existingSession = sessionStorage.getItem('_sk_sid')\n const lastActivity = sessionStorage.getItem('_sk_stime')\n\n if (existingSession && lastActivity) {\n const elapsed = now - parseInt(lastActivity, 10)\n if (elapsed < timeoutMs) {\n // Session still active — refresh timestamp\n sessionStorage.setItem('_sk_stime', now.toString())\n sessionStorage.setItem('_sk_sid', existingSession)\n return existingSession\n }\n }\n\n // New session (first visit or timeout expired)\n const newSession = generateId()\n sessionStorage.setItem('_sk_sid', newSession)\n sessionStorage.setItem('_sk_stime', now.toString())\n return newSession\n}\n\n// ============================================\n// React Context\n// ============================================\n\nexport interface SiteKitIdentity {\n visitorId: string\n sessionId: string\n}\n\nconst SiteKitIdentityContext = createContext<SiteKitIdentity | null>(null)\n\nexport interface SiteKitIdentityProviderProps {\n visitorId: string\n sessionId: string\n children: React.ReactNode\n}\n\nexport function SiteKitIdentityProvider({\n visitorId,\n sessionId,\n children,\n}: SiteKitIdentityProviderProps) {\n const value = useMemo(() => ({ visitorId, sessionId }), [visitorId, sessionId])\n\n return (\n <SiteKitIdentityContext.Provider value={value}>\n {children}\n </SiteKitIdentityContext.Provider>\n )\n}\n\n/**\n * Access the shared visitor/session identity.\n * Falls back to generating IDs directly if used outside of SiteKitIdentityProvider.\n */\nexport function useSiteKitIdentity(): SiteKitIdentity {\n const context = useContext(SiteKitIdentityContext)\n if (context) return context\n\n // Fallback for standalone usage outside SiteKitLayout\n return {\n visitorId: getOrCreateVisitorId(),\n sessionId: getOrCreateSessionId(),\n }\n}\n","'use client'\n\n/**\n * SiteKitClientProviders — Client Island\n *\n * This is the 'use client' boundary for SiteKitLayout.\n * It composes the client-only providers (Analytics, Engage, Signal, SitemapSync)\n * that require React context or browser APIs.\n *\n * All modules are lazy-loaded via next/dynamic so their JS is only downloaded\n * when the feature is enabled — keeping the initial bundle lightweight.\n *\n * All modules share a unified identity (visitor ID + session ID) via\n * SiteKitIdentityProvider, ensuring consistent tracking across Analytics,\n * Signal, and Engage.\n *\n * Props are passed down from the server-side SiteKitLayout component,\n * which reads env vars at render time.\n */\n\nimport React, { Suspense, useCallback, useRef, useState, type ReactNode } from 'react'\nimport dynamic from 'next/dynamic'\nimport {\n SiteKitIdentityProvider,\n getOrCreateVisitorId,\n getOrCreateSessionId,\n} from '../shared/identity'\nimport type { AnalyticsConfig, EngageConfig, SignalConfig } from './types'\n\n// Lazy-load feature modules — JS only downloaded when feature is enabled\nconst LazyAnalyticsProvider = dynamic(\n () => import('../analytics/AnalyticsProvider').then(m => ({ default: m.AnalyticsProvider })),\n { ssr: false },\n)\nconst LazyEngageWidget = dynamic(\n () => import('../engage/EngageWidget').then(m => ({ default: m.EngageWidget })),\n { ssr: false },\n)\nconst LazySignalBridge = dynamic(\n () => import('../signal/SignalBridge').then(m => ({ default: m.SignalBridge })),\n { ssr: false },\n)\nconst LazySitemapSync = dynamic(\n () => import('../SitemapSync').then(m => ({ default: m.SitemapSync })),\n { ssr: false },\n)\n\nexport interface SiteKitClientProvidersProps {\n children: ReactNode\n apiKey: string\n apiUrl: string\n projectId?: string\n analytics?: boolean | AnalyticsConfig\n engage?: boolean | EngageConfig\n signal?: boolean | SignalConfig\n sitemapSync?: boolean\n debug?: boolean\n}\n\nexport function SiteKitClientProviders({\n children,\n apiKey,\n apiUrl,\n projectId,\n analytics = true,\n engage = true,\n signal = false,\n sitemapSync = true,\n debug = false,\n}: SiteKitClientProvidersProps) {\n // Hoist analytics config so the `site` dimension is resolvable up here.\n const analyticsConfig: AnalyticsConfig =\n typeof analytics === 'object' ? analytics : {}\n\n // Resolve the sub-site identifier — the dimension that lets one Sonor project\n // host many sites (e.g. truepowersystems.com + ohiopowerstudies.com under one\n // TPS project). Precedence: explicit `analytics.site` > NEXT_PUBLIC_SITE_URL\n // host > window.location.host. Always a string (possibly empty).\n const explicitSite = analyticsConfig.site\n const envSiteUrl =\n typeof process !== 'undefined'\n ? process.env.NEXT_PUBLIC_SITE_URL\n : undefined\n let resolvedSite = explicitSite ?? ''\n if (!resolvedSite && envSiteUrl) {\n try {\n resolvedSite = new URL(envSiteUrl).host\n } catch {\n /* malformed URL — fall through to client-side host */\n }\n }\n if (!resolvedSite && typeof window !== 'undefined') {\n resolvedSite = window.location.host\n }\n const site = resolvedSite\n\n // Set window globals for modules that still read from them.\n if (typeof window !== 'undefined') {\n ;(window as any).__SITE_KIT_API_URL__ = apiUrl\n ;(window as any).__SITE_KIT_API_KEY__ = apiKey\n ;(window as any).__SITE_KIT_DEBUG__ = debug\n ;(window as any).__SITE_KIT_SITE__ = site\n }\n\n // Unified identity — single source of truth for all modules\n const [visitorId] = useState(() => getOrCreateVisitorId())\n const [sessionId] = useState(() => getOrCreateSessionId())\n\n // Page metadata bridge: Analytics writes, Signal reads\n const pageMetadataRef = useRef<Record<string, unknown> | null>(null)\n const onPageMetadata = useCallback((metadata: Record<string, unknown>) => {\n pageMetadataRef.current = metadata\n }, [])\n\n let content = <>{children}</>\n\n // Wrap with SignalBridge if enabled.\n // NOTE: SignalBridge still wraps {content} and is dynamic(ssr:false) — the\n // same SSR-de-opt caveat as Analytics applies. It is left wrapping because\n // A/B experiment variant swapping may depend on enclosing page content; if a\n // site enables `signal` and SSR breaks, convert this to a sibling too.\n if (signal) {\n const signalConfig: SignalConfig =\n typeof signal === 'object' ? signal : {}\n content = (\n <LazySignalBridge\n enabled\n realtime={signalConfig.realtime !== false}\n experiments={signalConfig.experiments !== false}\n behaviorTracking={signalConfig.behaviorTracking !== false}\n visitorId={visitorId}\n sessionId={sessionId}\n pageMetadataRef={pageMetadataRef}\n >\n {content}\n </LazySignalBridge>\n )\n }\n\n // Add Analytics if enabled (renders alongside, doesn't wrap).\n //\n // CRITICAL: LazyAnalyticsProvider is dynamic(..., { ssr: false }). Wrapping\n // {content} inside an ssr:false component makes the entire page tree skip\n // server rendering — the route de-opts to 100% client rendering and the\n // hero / LCP element only paints after hydration (mobile LCP ~4s, Lighthouse\n // ~85). Analytics tracks via effects + a metadata ref, so it never needs to\n // wrap page content — mount it as a childless sibling, exactly like Engage.\n if (analytics) {\n content = (\n <>\n {content}\n <Suspense fallback={null}>\n <LazyAnalyticsProvider\n apiUrl={apiUrl}\n apiKey={apiKey}\n site={site}\n trackPageViews={analyticsConfig.trackPageViews !== false}\n trackWebVitals={analyticsConfig.trackWebVitals !== false}\n trackScrollDepth={analyticsConfig.trackScrollDepth !== false}\n trackClicks={analyticsConfig.trackClicks !== false}\n debug={debug}\n externalVisitorId={visitorId}\n externalSessionId={sessionId}\n onPageMetadata={onPageMetadata}\n />\n </Suspense>\n </>\n )\n }\n\n // Add Engage widget if enabled (renders alongside, doesn't wrap)\n if (engage) {\n const engageConfig: EngageConfig =\n typeof engage === 'object' ? engage : {}\n content = (\n <>\n {content}\n <LazyEngageWidget\n apiUrl={apiUrl}\n apiKey={apiKey}\n projectId={projectId}\n position={engageConfig.position || 'bottom-right'}\n chatEnabled={engageConfig.chatEnabled !== false}\n />\n </>\n )\n }\n\n // Add SitemapSync if enabled\n if (sitemapSync) {\n content = (\n <>\n {content}\n <LazySitemapSync debug={debug} />\n </>\n )\n }\n\n return (\n <SiteKitIdentityProvider visitorId={visitorId} sessionId={sessionId}>\n {content}\n </SiteKitIdentityProvider>\n )\n}\n"]}
|
|
@@ -9,7 +9,7 @@ function getApiConfig() {
|
|
|
9
9
|
const apiKey = typeof window !== "undefined" ? window.__SITE_KIT_API_KEY__ : void 0;
|
|
10
10
|
return { apiUrl, apiKey };
|
|
11
11
|
}
|
|
12
|
-
function WebVitals({ apiUrl: propApiUrl, apiKey: propApiKey, debug = false }) {
|
|
12
|
+
function WebVitals({ apiUrl: propApiUrl, apiKey: propApiKey, site: propSite, debug = false }) {
|
|
13
13
|
const pathname = usePathname();
|
|
14
14
|
useEffect(() => {
|
|
15
15
|
import('./web-vitals-KPICZIEF.mjs').then(({ onCLS, onLCP, onTTFB, onINP, onFCP }) => {
|
|
@@ -22,12 +22,14 @@ function WebVitals({ apiUrl: propApiUrl, apiKey: propApiKey, debug = false }) {
|
|
|
22
22
|
const { apiUrl: globalApiUrl, apiKey: globalApiKey } = getApiConfig();
|
|
23
23
|
const apiUrl = propApiUrl || globalApiUrl;
|
|
24
24
|
const apiKey = propApiKey || globalApiKey;
|
|
25
|
+
const site = propSite || (typeof window !== "undefined" ? window.__SITE_KIT_SITE__ || window.location.host : "");
|
|
25
26
|
if (!apiKey) {
|
|
26
27
|
if (debug) console.warn("[Analytics] No API key configured for Web Vitals");
|
|
27
28
|
return;
|
|
28
29
|
}
|
|
29
30
|
for (const [name, value] of Object.entries(vitals)) {
|
|
30
31
|
const data = {
|
|
32
|
+
site,
|
|
31
33
|
pagePath: pathname,
|
|
32
34
|
metricName: name,
|
|
33
35
|
metricValue: value,
|
|
@@ -84,7 +86,7 @@ function WebVitals({ apiUrl: propApiUrl, apiKey: propApiKey, debug = false }) {
|
|
|
84
86
|
}).catch((err) => {
|
|
85
87
|
if (debug) console.warn("[Analytics] Web Vitals import failed:", err);
|
|
86
88
|
});
|
|
87
|
-
}, [pathname, propApiUrl, propApiKey, debug]);
|
|
89
|
+
}, [pathname, propApiUrl, propApiKey, propSite, debug]);
|
|
88
90
|
return null;
|
|
89
91
|
}
|
|
90
92
|
function getRating(name, value) {
|
|
@@ -507,10 +509,12 @@ function AnalyticsProvider({
|
|
|
507
509
|
debug = false,
|
|
508
510
|
externalVisitorId,
|
|
509
511
|
externalSessionId,
|
|
510
|
-
onPageMetadata
|
|
512
|
+
onPageMetadata,
|
|
513
|
+
site: propSite
|
|
511
514
|
}) {
|
|
512
515
|
const pathname = usePathname();
|
|
513
516
|
const [queryString, setQueryString] = useState("");
|
|
517
|
+
const site = propSite || (typeof window !== "undefined" ? window.__SITE_KIT_SITE__ || window.location.host : "");
|
|
514
518
|
useEffect(() => {
|
|
515
519
|
if (typeof window === "undefined") return;
|
|
516
520
|
setQueryString(window.location.search);
|
|
@@ -599,6 +603,7 @@ function AnalyticsProvider({
|
|
|
599
603
|
const pageView = {
|
|
600
604
|
sessionId: sessionIdRef.current,
|
|
601
605
|
visitorId: visitorIdRef.current,
|
|
606
|
+
site,
|
|
602
607
|
pagePath: normalizedPath,
|
|
603
608
|
pageTitle: document.title,
|
|
604
609
|
referrer: document.referrer || null,
|
|
@@ -670,6 +675,7 @@ function AnalyticsProvider({
|
|
|
670
675
|
const sessionData = {
|
|
671
676
|
sessionId: sessionIdRef.current,
|
|
672
677
|
visitorId: visitorIdRef.current,
|
|
678
|
+
site,
|
|
673
679
|
action: isNewSession ? "start" : "update",
|
|
674
680
|
lastPage: pathname,
|
|
675
681
|
userAgent: getUserAgent(),
|
|
@@ -716,6 +722,7 @@ function AnalyticsProvider({
|
|
|
716
722
|
const duration = Math.round((Date.now() - journeyStartTimeRef.current) / 1e3);
|
|
717
723
|
const payload = JSON.stringify({
|
|
718
724
|
sessionId: sessionIdRef.current,
|
|
725
|
+
site,
|
|
719
726
|
action: "end",
|
|
720
727
|
duration,
|
|
721
728
|
lastPage: pathname,
|
|
@@ -782,6 +789,7 @@ function AnalyticsProvider({
|
|
|
782
789
|
const payload = JSON.stringify({
|
|
783
790
|
sessionId: sessionIdRef.current,
|
|
784
791
|
visitorId: visitorIdRef.current,
|
|
792
|
+
site,
|
|
785
793
|
pagePath: pathname,
|
|
786
794
|
maxDepthPercent: maxDepth,
|
|
787
795
|
timeTo25: milestone25,
|
|
@@ -859,6 +867,7 @@ function AnalyticsProvider({
|
|
|
859
867
|
const elementText = target.textContent?.slice(0, 50) || null;
|
|
860
868
|
const clickData = {
|
|
861
869
|
sessionId: sessionIdRef.current,
|
|
870
|
+
site,
|
|
862
871
|
pagePath: pathname,
|
|
863
872
|
xPercent,
|
|
864
873
|
yPercent,
|
|
@@ -905,6 +914,7 @@ function AnalyticsProvider({
|
|
|
905
914
|
const event = {
|
|
906
915
|
sessionId: sessionIdRef.current,
|
|
907
916
|
visitorId: visitorIdRef.current,
|
|
917
|
+
site,
|
|
908
918
|
eventName: options.name,
|
|
909
919
|
eventCategory: options.category,
|
|
910
920
|
eventLabel: options.label,
|
|
@@ -945,6 +955,7 @@ function AnalyticsProvider({
|
|
|
945
955
|
const conversion = {
|
|
946
956
|
sessionId: sessionIdRef.current,
|
|
947
957
|
visitorId: visitorIdRef.current,
|
|
958
|
+
site,
|
|
948
959
|
conversionType: options.type,
|
|
949
960
|
value: options.value,
|
|
950
961
|
currency: options.currency,
|
|
@@ -986,6 +997,7 @@ function AnalyticsProvider({
|
|
|
986
997
|
{
|
|
987
998
|
apiUrl: propApiUrl,
|
|
988
999
|
apiKey: propApiKey,
|
|
1000
|
+
site,
|
|
989
1001
|
debug
|
|
990
1002
|
}
|
|
991
1003
|
),
|
|
@@ -1008,5 +1020,5 @@ function useTrackEvent() {
|
|
|
1008
1020
|
}
|
|
1009
1021
|
|
|
1010
1022
|
export { AnalyticsProvider, WebVitals, useAnalytics, useAnalyticsOptional, useTrackEvent };
|
|
1011
|
-
//# sourceMappingURL=chunk-
|
|
1012
|
-
//# sourceMappingURL=chunk-
|
|
1023
|
+
//# sourceMappingURL=chunk-IM57FXP7.mjs.map
|
|
1024
|
+
//# sourceMappingURL=chunk-IM57FXP7.mjs.map
|