@networkpro/web
Version:
Locking Down Networks, Unlocking Confidence™ | Security, Networking, Privacy — Network Pro Strategies
137 lines (116 loc) • 3.91 kB
JavaScript
/* ==========================================================================
src/lib/stores/posthog.js
Copyright © 2025 Network Pro Strategies (Network Pro™)
SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
========================================================================== */
/**
* @file posthog.js
* @description Privacy-aware PostHog tracking store with reactive state and safe API surface.
* @module src/lib/stores
*/
import {
remindUserToReconsent,
trackingPreferences,
} from '$lib/stores/trackingPreferences.js';
import { get, writable } from 'svelte/store';
/**
* Tracks whether tracking is allowed based on cookies, DNT/GPC, and user preference.
* @type {import("svelte/store").Writable<boolean>}
*/
export const trackingEnabled = writable(false);
/**
* Determines if the user should be reminded to re-consent (after 6 months).
* @type {import("svelte/store").Writable<boolean>}
*/
export const showReminder = writable(false);
/** @type {boolean} Internal one-time init guard */
let initialized = false;
/** @type {import("posthog-js").PostHog | null} Loaded PostHog instance */
let ph = null;
/**
* Initializes the PostHog analytics client if tracking is permitted.
* Uses dynamic import to avoid SSR failures.
* @returns {Promise<void>}
*/
export async function initPostHog() {
if (initialized || typeof window === 'undefined') return;
const isDev = import.meta.env.MODE === 'development';
if (isDev) {
console.info('[PostHog] Skipping init in development mode.');
return;
}
initialized = true;
const { enabled } = get(trackingPreferences);
trackingEnabled.set(enabled);
showReminder.set(get(remindUserToReconsent)); // use derived store instead
if (!enabled) {
console.log('[PostHog] Tracking is disabled — skipping init.');
return;
}
try {
const posthogModule = await import('posthog-js');
ph = posthogModule.default;
// cspell:disable-next-line
ph.init('phc_Qshfo6AXzh4pS7aPigfqyeo4qj1qlyh7gDuHDeVMSR0', {
api_host: '/relay-MSR0/',
ui_host: 'https://us.posthog.com',
autocapture: true,
capture_pageview: false,
person_profiles: 'identified_only',
loaded: (phInstance) => {
if (!enabled) {
console.log(
'[PostHog] ⛔ User opted out — calling opt_out_capturing()',
);
phInstance.opt_out_capturing();
} else {
console.log('[PostHog] ✅ Tracking enabled');
}
},
});
ph.capture('$pageview');
} catch (err) {
console.warn('[PostHog] Failed to initialize:', err);
}
}
/**
* Conditionally captures an event if tracking is enabled.
* @param {string} event - The event name to track
* @param {Record<string, any>} [properties={}] - Optional event properties
*/
export function capture(event, properties = {}) {
const isDev = import.meta.env.MODE === 'development';
if (isDev || ph === null || !get(trackingEnabled)) return;
try {
ph.capture(event, properties);
} catch (err) {
console.warn(`[PostHog] capture(${event}) failed:`, err);
}
}
/**
* Conditionally identifies a user if tracking is enabled.
* @param {string} id - Unique user identifier
* @param {Record<string, any>} [properties={}] - Optional user traits
*/
export function identify(id, properties = {}) {
const isDev = import.meta.env.MODE === 'development';
if (isDev || ph === null || !get(trackingEnabled)) return;
try {
ph.identify(id, properties);
} catch (err) {
console.warn(`[PostHog] identify(${id}) failed:`, err);
}
}
/**
* For test cleanup only — resets internal state.
* No-op in production.
* @returns {void}
*/
export function _resetPostHog() {
if (import.meta.env.MODE === 'production') {
console.warn('[PostHog] _resetPostHog() called in production — no-op');
return;
}
initialized = false;
ph = null;
}