@od-oneapp/analytics 2026.1.1301
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 +509 -0
- package/dist/ai-YMnynb-t.mjs +3347 -0
- package/dist/ai-YMnynb-t.mjs.map +1 -0
- package/dist/chunk-DQk6qfdC.mjs +18 -0
- package/dist/client-CTzJVFU5.mjs +9 -0
- package/dist/client-CTzJVFU5.mjs.map +1 -0
- package/dist/client-CcFTauAh.mjs +54 -0
- package/dist/client-CcFTauAh.mjs.map +1 -0
- package/dist/client-CeOLjbac.mjs +281 -0
- package/dist/client-CeOLjbac.mjs.map +1 -0
- package/dist/client-D339NFJS.mjs +267 -0
- package/dist/client-D339NFJS.mjs.map +1 -0
- package/dist/client-next.d.mts +62 -0
- package/dist/client-next.d.mts.map +1 -0
- package/dist/client-next.mjs +525 -0
- package/dist/client-next.mjs.map +1 -0
- package/dist/client.d.mts +30 -0
- package/dist/client.d.mts.map +1 -0
- package/dist/client.mjs +186 -0
- package/dist/client.mjs.map +1 -0
- package/dist/config-DPS6bSYo.d.mts +34 -0
- package/dist/config-DPS6bSYo.d.mts.map +1 -0
- package/dist/config-P6P5adJg.mjs +287 -0
- package/dist/config-P6P5adJg.mjs.map +1 -0
- package/dist/console-8bND3mMU.mjs +128 -0
- package/dist/console-8bND3mMU.mjs.map +1 -0
- package/dist/ecommerce-Cgu4wlux.mjs +993 -0
- package/dist/ecommerce-Cgu4wlux.mjs.map +1 -0
- package/dist/emitters-6-nKo8i-.mjs +208 -0
- package/dist/emitters-6-nKo8i-.mjs.map +1 -0
- package/dist/emitters-DldkVSPp.d.mts +12 -0
- package/dist/emitters-DldkVSPp.d.mts.map +1 -0
- package/dist/index-BfNWgfa5.d.mts +1494 -0
- package/dist/index-BfNWgfa5.d.mts.map +1 -0
- package/dist/index-BkIWe--N.d.mts +953 -0
- package/dist/index-BkIWe--N.d.mts.map +1 -0
- package/dist/index-jPzXRn52.d.mts +184 -0
- package/dist/index-jPzXRn52.d.mts.map +1 -0
- package/dist/manager-DvRRjza6.d.mts +76 -0
- package/dist/manager-DvRRjza6.d.mts.map +1 -0
- package/dist/posthog-bootstrap-CYfIy_WS.mjs +1769 -0
- package/dist/posthog-bootstrap-CYfIy_WS.mjs.map +1 -0
- package/dist/posthog-bootstrap-DWxFrxlt.d.mts +81 -0
- package/dist/posthog-bootstrap-DWxFrxlt.d.mts.map +1 -0
- package/dist/providers-http-client.d.mts +37 -0
- package/dist/providers-http-client.d.mts.map +1 -0
- package/dist/providers-http-client.mjs +320 -0
- package/dist/providers-http-client.mjs.map +1 -0
- package/dist/providers-http-server.d.mts +31 -0
- package/dist/providers-http-server.d.mts.map +1 -0
- package/dist/providers-http-server.mjs +297 -0
- package/dist/providers-http-server.mjs.map +1 -0
- package/dist/providers-http.d.mts +46 -0
- package/dist/providers-http.d.mts.map +1 -0
- package/dist/providers-http.mjs +4 -0
- package/dist/server-edge.d.mts +9 -0
- package/dist/server-edge.d.mts.map +1 -0
- package/dist/server-edge.mjs +373 -0
- package/dist/server-edge.mjs.map +1 -0
- package/dist/server-next.d.mts +67 -0
- package/dist/server-next.d.mts.map +1 -0
- package/dist/server-next.mjs +193 -0
- package/dist/server-next.mjs.map +1 -0
- package/dist/server.d.mts +10 -0
- package/dist/server.mjs +7 -0
- package/dist/service-cYtBBL8x.mjs +945 -0
- package/dist/service-cYtBBL8x.mjs.map +1 -0
- package/dist/shared.d.mts +16 -0
- package/dist/shared.d.mts.map +1 -0
- package/dist/shared.mjs +93 -0
- package/dist/shared.mjs.map +1 -0
- package/dist/types-BxBnNQ0V.d.mts +354 -0
- package/dist/types-BxBnNQ0V.d.mts.map +1 -0
- package/dist/types-CBvxUEaF.d.mts +216 -0
- package/dist/types-CBvxUEaF.d.mts.map +1 -0
- package/dist/types.d.mts +4 -0
- package/dist/types.mjs +0 -0
- package/dist/vercel-types-lwakUfoI.d.mts +102 -0
- package/dist/vercel-types-lwakUfoI.d.mts.map +1 -0
- package/package.json +129 -0
- package/src/client/index.ts +164 -0
- package/src/client/manager.ts +71 -0
- package/src/client/next/components.tsx +270 -0
- package/src/client/next/hooks.ts +217 -0
- package/src/client/next/manager.ts +141 -0
- package/src/client/next.ts +144 -0
- package/src/client-next.ts +101 -0
- package/src/client.ts +89 -0
- package/src/examples/ai-sdk-patterns.ts +583 -0
- package/src/examples/emitter-patterns.ts +476 -0
- package/src/examples/nextjs-emitter-patterns.tsx +403 -0
- package/src/next/app-router.tsx +564 -0
- package/src/next/client.ts +419 -0
- package/src/next/index.ts +84 -0
- package/src/next/middleware.ts +429 -0
- package/src/next/rsc.tsx +300 -0
- package/src/next/server.ts +253 -0
- package/src/next/types.d.ts +220 -0
- package/src/providers/base-provider.ts +419 -0
- package/src/providers/console/client.ts +10 -0
- package/src/providers/console/index.ts +152 -0
- package/src/providers/console/server.ts +6 -0
- package/src/providers/console/types.ts +15 -0
- package/src/providers/http/client.ts +464 -0
- package/src/providers/http/index.ts +30 -0
- package/src/providers/http/server.ts +396 -0
- package/src/providers/http/types.ts +135 -0
- package/src/providers/posthog/client.ts +518 -0
- package/src/providers/posthog/index.ts +11 -0
- package/src/providers/posthog/server.ts +329 -0
- package/src/providers/posthog/types.ts +104 -0
- package/src/providers/segment/client.ts +113 -0
- package/src/providers/segment/index.ts +11 -0
- package/src/providers/segment/server.ts +115 -0
- package/src/providers/segment/types.ts +51 -0
- package/src/providers/vercel/client.ts +102 -0
- package/src/providers/vercel/index.ts +11 -0
- package/src/providers/vercel/server.ts +89 -0
- package/src/providers/vercel/types.ts +27 -0
- package/src/server/index.ts +103 -0
- package/src/server/manager.ts +62 -0
- package/src/server/next.ts +210 -0
- package/src/server-edge.ts +442 -0
- package/src/server-next.ts +39 -0
- package/src/server.ts +106 -0
- package/src/shared/emitters/ai/README.md +981 -0
- package/src/shared/emitters/ai/events/agent.ts +130 -0
- package/src/shared/emitters/ai/events/artifacts.ts +167 -0
- package/src/shared/emitters/ai/events/chat.ts +126 -0
- package/src/shared/emitters/ai/events/chatbot-ecommerce.ts +133 -0
- package/src/shared/emitters/ai/events/completion.ts +103 -0
- package/src/shared/emitters/ai/events/content-generation.ts +347 -0
- package/src/shared/emitters/ai/events/conversation.ts +332 -0
- package/src/shared/emitters/ai/events/product-features.ts +1402 -0
- package/src/shared/emitters/ai/events/streaming.ts +114 -0
- package/src/shared/emitters/ai/events/tool.ts +93 -0
- package/src/shared/emitters/ai/index.ts +69 -0
- package/src/shared/emitters/ai/track-ai-sdk.ts +74 -0
- package/src/shared/emitters/ai/track-ai.ts +50 -0
- package/src/shared/emitters/ai/types.ts +1041 -0
- package/src/shared/emitters/ai/utils.ts +468 -0
- package/src/shared/emitters/ecommerce/events/cart-checkout.ts +106 -0
- package/src/shared/emitters/ecommerce/events/coupon.ts +49 -0
- package/src/shared/emitters/ecommerce/events/engagement.ts +61 -0
- package/src/shared/emitters/ecommerce/events/marketplace.ts +119 -0
- package/src/shared/emitters/ecommerce/events/order.ts +199 -0
- package/src/shared/emitters/ecommerce/events/product.ts +205 -0
- package/src/shared/emitters/ecommerce/events/registry.ts +123 -0
- package/src/shared/emitters/ecommerce/events/wishlist-sharing.ts +140 -0
- package/src/shared/emitters/ecommerce/index.ts +46 -0
- package/src/shared/emitters/ecommerce/track-ecommerce.ts +53 -0
- package/src/shared/emitters/ecommerce/types.ts +314 -0
- package/src/shared/emitters/ecommerce/utils.ts +216 -0
- package/src/shared/emitters/emitter-types.ts +974 -0
- package/src/shared/emitters/emitters.ts +292 -0
- package/src/shared/emitters/helpers.ts +419 -0
- package/src/shared/emitters/index.ts +66 -0
- package/src/shared/index.ts +142 -0
- package/src/shared/ingestion/index.ts +66 -0
- package/src/shared/ingestion/schemas.ts +386 -0
- package/src/shared/ingestion/service.ts +628 -0
- package/src/shared/node22-features.ts +848 -0
- package/src/shared/providers/console-provider.ts +160 -0
- package/src/shared/types/base-types.ts +54 -0
- package/src/shared/types/console-types.ts +19 -0
- package/src/shared/types/posthog-types.ts +131 -0
- package/src/shared/types/segment-types.ts +15 -0
- package/src/shared/types/types.ts +397 -0
- package/src/shared/types/vercel-types.ts +19 -0
- package/src/shared/utils/config-client.ts +19 -0
- package/src/shared/utils/config.ts +250 -0
- package/src/shared/utils/emitter-adapter.ts +212 -0
- package/src/shared/utils/manager.test.ts +36 -0
- package/src/shared/utils/manager.ts +1322 -0
- package/src/shared/utils/posthog-bootstrap.ts +136 -0
- package/src/shared/utils/posthog-client-utils.ts +48 -0
- package/src/shared/utils/posthog-next-utils.ts +282 -0
- package/src/shared/utils/posthog-server-utils.ts +210 -0
- package/src/shared/utils/rate-limit.ts +289 -0
- package/src/shared/utils/security.ts +545 -0
- package/src/shared/utils/validation-client.ts +161 -0
- package/src/shared/utils/validation.ts +399 -0
- package/src/shared.ts +155 -0
- package/src/types/index.ts +62 -0
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview PostHog client-side (browser) provider implementation
|
|
3
|
+
*
|
|
4
|
+
* Provides client-side integration with PostHog Analytics including feature flags support.
|
|
5
|
+
* Supports full PostHog tracking including capture, identify, reset, group, alias, and feature flags.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* This provider:
|
|
9
|
+
* - Uses PostHog JavaScript SDK for browser tracking
|
|
10
|
+
* - Supports bootstrap data for SSR optimization
|
|
11
|
+
* - Handles feature flags and experiments
|
|
12
|
+
* - Integrates with PostHog's analytics dashboard
|
|
13
|
+
*
|
|
14
|
+
* @module @repo/analytics/providers/posthog/client
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Simple console fallbacks - observability integration can be added later
|
|
18
|
+
import type {
|
|
19
|
+
BootstrapData,
|
|
20
|
+
EnhancedPostHogProvider,
|
|
21
|
+
ExperimentInfo,
|
|
22
|
+
PostHogConfig,
|
|
23
|
+
} from './types';
|
|
24
|
+
import type { AnalyticsProvider, ProviderConfig } from '../../shared/types/types';
|
|
25
|
+
const logDebug = (_message: string, _context?: any) => {
|
|
26
|
+
// No-op to avoid console warnings in production
|
|
27
|
+
// TODO: Add proper debug logging via observability package
|
|
28
|
+
};
|
|
29
|
+
const logError = (_message: string, _error?: Error, _context?: any) => {
|
|
30
|
+
// No-op to avoid console warnings in production
|
|
31
|
+
// TODO: Add proper error logging via observability package
|
|
32
|
+
};
|
|
33
|
+
const logWarn = (_message: string, _context?: any) => {
|
|
34
|
+
// No-op to avoid console warnings in production
|
|
35
|
+
// TODO: Add proper error logging via observability package
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
declare global {
|
|
39
|
+
interface Window {
|
|
40
|
+
posthog?: {
|
|
41
|
+
init: (apiKey: string, options?: any) => void;
|
|
42
|
+
capture: (event: string, properties?: any) => void;
|
|
43
|
+
identify: (userId: string, properties?: any) => void;
|
|
44
|
+
reset: () => void;
|
|
45
|
+
group: (groupType: string, groupKey: string, properties?: any) => void;
|
|
46
|
+
alias: (alias: string) => void;
|
|
47
|
+
people?: {
|
|
48
|
+
set: (properties: any) => void;
|
|
49
|
+
set_once: (properties: any) => void;
|
|
50
|
+
};
|
|
51
|
+
register: (properties: any) => void;
|
|
52
|
+
|
|
53
|
+
// Feature flag methods
|
|
54
|
+
isFeatureEnabled: (flag: string) => boolean;
|
|
55
|
+
getFeatureFlag: (flag: string) => any;
|
|
56
|
+
getFeatureFlagPayload: (flag: string) => any;
|
|
57
|
+
getAllFlags: () => Record<string, any>;
|
|
58
|
+
onFeatureFlags: (callback: (flags: string[], variants: Record<string, any>) => void) => void;
|
|
59
|
+
|
|
60
|
+
// Experiment methods
|
|
61
|
+
getActiveMatchingFeatureFlags: () => string[];
|
|
62
|
+
|
|
63
|
+
// Utility methods
|
|
64
|
+
get_distinct_id: () => string;
|
|
65
|
+
shutdown: () => Promise<void>;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export class PostHogClientProvider implements AnalyticsProvider, Partial<EnhancedPostHogProvider> {
|
|
71
|
+
readonly name = 'posthog';
|
|
72
|
+
private config: PostHogConfig;
|
|
73
|
+
private isInitialized = false;
|
|
74
|
+
private posthogInstance: any = null;
|
|
75
|
+
private retryQueue: { method: string; args: any[] }[] = [];
|
|
76
|
+
private isOnline = true;
|
|
77
|
+
private debugMode = false;
|
|
78
|
+
|
|
79
|
+
constructor(config: ProviderConfig) {
|
|
80
|
+
if (!config.apiKey) {
|
|
81
|
+
throw new Error('PostHog apiKey is required');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
this.config = {
|
|
85
|
+
apiKey: config.apiKey,
|
|
86
|
+
options: config.options,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Set up debug mode
|
|
90
|
+
this.debugMode =
|
|
91
|
+
config.options?.debug === true ||
|
|
92
|
+
(typeof window !== 'undefined' && window.location.search.includes('debug=posthog'));
|
|
93
|
+
|
|
94
|
+
// Monitor network status for retry queue
|
|
95
|
+
if (typeof window !== 'undefined') {
|
|
96
|
+
window.addEventListener('online', () => {
|
|
97
|
+
this.isOnline = true;
|
|
98
|
+
void this.flushRetryQueue();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
window.addEventListener('offline', () => {
|
|
102
|
+
this.isOnline = false;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async initialize(): Promise<void> {
|
|
108
|
+
if (this.isInitialized) return;
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
// Dynamically import PostHog (optional dependency)
|
|
112
|
+
const { default: posthog } = await import('posthog-js');
|
|
113
|
+
|
|
114
|
+
// Extract bootstrap data if provided
|
|
115
|
+
const bootstrap = this.config.options?.bootstrap;
|
|
116
|
+
|
|
117
|
+
// Initialize PostHog with enhanced options and all missing configurations
|
|
118
|
+
const initOptions: any = {
|
|
119
|
+
// Core PostHog configuration with PostHog recommended defaults
|
|
120
|
+
api_host: 'https://app.posthog.com',
|
|
121
|
+
capture_pageleave: true, // Important for engagement tracking
|
|
122
|
+
capture_pageview: false, // We handle pageviews manually
|
|
123
|
+
ui_host: 'https://app.posthog.com',
|
|
124
|
+
|
|
125
|
+
opt_in_site_apps: false, // Conservative default
|
|
126
|
+
// Privacy & GDPR compliance
|
|
127
|
+
person_profiles: 'identified_only', // GDPR-friendly default
|
|
128
|
+
respect_dnt: true, // Respect Do Not Track headers
|
|
129
|
+
|
|
130
|
+
// Performance optimizations
|
|
131
|
+
uuid_version: 'v7', // Better performance than v4
|
|
132
|
+
batch_flush_interval_ms: 10000, // 10 second batching
|
|
133
|
+
request_batching: true, // Batch requests for better performance
|
|
134
|
+
|
|
135
|
+
// Session recording with safe defaults
|
|
136
|
+
session_recording: {
|
|
137
|
+
maskAllInputs: false,
|
|
138
|
+
maskInputOptions: {
|
|
139
|
+
email: false,
|
|
140
|
+
number: false,
|
|
141
|
+
password: true, // Always mask passwords
|
|
142
|
+
text: false,
|
|
143
|
+
},
|
|
144
|
+
recordCanvas: false, // Performance consideration
|
|
145
|
+
recordCrossOriginIframes: false, // Conservative default
|
|
146
|
+
sampling: {
|
|
147
|
+
minimumDurationMs: 1000, // Only record sessions > 1s
|
|
148
|
+
sampleRate: 1, // Record all sessions by default
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
// Debug mode
|
|
153
|
+
debug: this.debugMode,
|
|
154
|
+
|
|
155
|
+
// Apply user configuration (this will override defaults)
|
|
156
|
+
...this.config.options,
|
|
157
|
+
|
|
158
|
+
// Add bootstrap data if available (must be last to not be overridden)
|
|
159
|
+
...(bootstrap && {
|
|
160
|
+
bootstrap: {
|
|
161
|
+
distinctID: bootstrap.distinctID,
|
|
162
|
+
},
|
|
163
|
+
}),
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
posthog.init(this.config.apiKey, initOptions);
|
|
167
|
+
|
|
168
|
+
// Store instance references
|
|
169
|
+
this.posthogInstance = posthog;
|
|
170
|
+
window.posthog = posthog as any;
|
|
171
|
+
|
|
172
|
+
this.isInitialized = true;
|
|
173
|
+
} catch {
|
|
174
|
+
throw new Error('PostHog JS SDK not available. Install with: npm install posthog-js');
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async track(event: string, properties: any = {}): Promise<void> {
|
|
179
|
+
if (!this.isInitialized || !this.posthogInstance) {
|
|
180
|
+
await this.queueEvent('track', [event, properties]);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
// Debug logging
|
|
186
|
+
await this.log('Tracking event:', event, properties);
|
|
187
|
+
|
|
188
|
+
// Validate properties in debug mode
|
|
189
|
+
if (this.debugMode) {
|
|
190
|
+
await this.validateEventProperties(event, properties);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
this.posthogInstance.capture(event, properties);
|
|
194
|
+
} catch (error) {
|
|
195
|
+
// Queue event if offline
|
|
196
|
+
if (!this.isOnline) {
|
|
197
|
+
await this.queueEvent('track', [event, properties]);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Enhanced error reporting
|
|
201
|
+
await this.reportError(error, 'track', { event, properties });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async identify(userId: string, traits: any = {}): Promise<void> {
|
|
206
|
+
if (!this.isInitialized || !this.posthogInstance) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
this.posthogInstance.identify(userId, traits);
|
|
212
|
+
} catch {
|
|
213
|
+
// Silently fail to avoid disrupting app flow
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async page(name?: string, properties: any = {}): Promise<void> {
|
|
218
|
+
if (!this.isInitialized || !this.posthogInstance) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
this.posthogInstance.capture('$pageview', {
|
|
224
|
+
$current_url: window.location.href,
|
|
225
|
+
$title: name ?? document.title,
|
|
226
|
+
...properties,
|
|
227
|
+
});
|
|
228
|
+
} catch {
|
|
229
|
+
// Silently fail to avoid disrupting app flow
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async group(groupId: string, traits: any = {}): Promise<void> {
|
|
234
|
+
if (!this.isInitialized || !this.posthogInstance) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
this.posthogInstance.group('company', groupId, traits);
|
|
240
|
+
} catch {
|
|
241
|
+
// Silently fail to avoid disrupting app flow
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async alias(userId: string, _previousId: string): Promise<void> {
|
|
246
|
+
if (!this.isInitialized || !this.posthogInstance) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
try {
|
|
251
|
+
this.posthogInstance.alias(userId);
|
|
252
|
+
} catch {
|
|
253
|
+
// Silently fail to avoid disrupting app flow
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Feature Flag Methods
|
|
258
|
+
async getAllFlags(userId?: string): Promise<Record<string, any>> {
|
|
259
|
+
if (!this.isInitialized || !this.posthogInstance) {
|
|
260
|
+
return {};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
if (userId) {
|
|
265
|
+
// If userId provided, identify first to ensure flags are for correct user
|
|
266
|
+
await this.identify(userId);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return this.posthogInstance.getAllFlags() ?? {};
|
|
270
|
+
} catch {
|
|
271
|
+
return {};
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async getFeatureFlag(flag: string, userId?: string): Promise<any> {
|
|
276
|
+
if (!this.isInitialized || !this.posthogInstance) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
if (userId) {
|
|
282
|
+
await this.identify(userId);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return this.posthogInstance.getFeatureFlag(flag);
|
|
286
|
+
} catch {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async isFeatureEnabled(flag: string, userId?: string): Promise<boolean> {
|
|
292
|
+
if (!this.isInitialized || !this.posthogInstance) {
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
try {
|
|
297
|
+
if (userId) {
|
|
298
|
+
await this.identify(userId);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return this.posthogInstance.isFeatureEnabled(flag) ?? false;
|
|
302
|
+
} catch {
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async getFeatureFlagPayload(flag: string, userId?: string): Promise<any | null> {
|
|
308
|
+
if (!this.isInitialized || !this.posthogInstance) {
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
try {
|
|
313
|
+
if (userId) {
|
|
314
|
+
await this.identify(userId);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return this.posthogInstance.getFeatureFlagPayload(flag) ?? null;
|
|
318
|
+
} catch {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async getActiveExperiments(userId?: string): Promise<ExperimentInfo[]> {
|
|
324
|
+
if (!this.isInitialized || !this.posthogInstance) {
|
|
325
|
+
return [];
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
try {
|
|
329
|
+
if (userId) {
|
|
330
|
+
await this.identify(userId);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const activeFlags = this.posthogInstance.getActiveMatchingFeatureFlags() ?? [];
|
|
334
|
+
return activeFlags.map((flag: string) => ({
|
|
335
|
+
key: flag,
|
|
336
|
+
payload: this.posthogInstance.getFeatureFlagPayload(flag),
|
|
337
|
+
variant: this.posthogInstance.getFeatureFlag(flag),
|
|
338
|
+
}));
|
|
339
|
+
} catch {
|
|
340
|
+
return [];
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Bootstrap method (not typically used on client, but included for completeness)
|
|
345
|
+
async getBootstrapData(distinctId: string): Promise<BootstrapData> {
|
|
346
|
+
if (!this.isInitialized || !this.posthogInstance) {
|
|
347
|
+
return { distinctID: distinctId };
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
try {
|
|
351
|
+
await this.getAllFlags();
|
|
352
|
+
|
|
353
|
+
return {
|
|
354
|
+
distinctID: distinctId,
|
|
355
|
+
};
|
|
356
|
+
} catch {
|
|
357
|
+
return { distinctID: distinctId };
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Utility Methods
|
|
362
|
+
reset(): void {
|
|
363
|
+
if (!this.isInitialized || !this.posthogInstance) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
try {
|
|
368
|
+
this.posthogInstance.reset();
|
|
369
|
+
} catch {
|
|
370
|
+
// Silently fail
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async shutdown(): Promise<void> {
|
|
375
|
+
if (!this.isInitialized || !this.posthogInstance) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
try {
|
|
380
|
+
if (this.posthogInstance.shutdown) {
|
|
381
|
+
await this.posthogInstance.shutdown();
|
|
382
|
+
}
|
|
383
|
+
} catch {
|
|
384
|
+
// Silently fail
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Get distinct ID for bootstrap purposes
|
|
389
|
+
getDistinctId(): string | null {
|
|
390
|
+
if (!this.isInitialized || !this.posthogInstance) {
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
try {
|
|
395
|
+
return this.posthogInstance.get_distinct_id();
|
|
396
|
+
} catch {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Enhanced error handling and utility methods
|
|
402
|
+
private async queueEvent(method: string, args: any[]) {
|
|
403
|
+
this.retryQueue.push({ args, method });
|
|
404
|
+
await this.log(`Queued ${method} event for retry: `, args);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
private async flushRetryQueue() {
|
|
408
|
+
await this.log(`Flushing retry queue with ${this.retryQueue.length} events`);
|
|
409
|
+
|
|
410
|
+
while (this.retryQueue.length > 0) {
|
|
411
|
+
const item = this.retryQueue.shift();
|
|
412
|
+
if (!item) break;
|
|
413
|
+
const { args, method } = item;
|
|
414
|
+
try {
|
|
415
|
+
await (this as any)[method](...args);
|
|
416
|
+
} catch {
|
|
417
|
+
// Re-queue failed events (limit retries)
|
|
418
|
+
if (this.retryQueue.length < 100) {
|
|
419
|
+
// Prevent infinite queue growth
|
|
420
|
+
await this.queueEvent(method, args);
|
|
421
|
+
}
|
|
422
|
+
break;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
private log(...args: any[]) {
|
|
428
|
+
if (this.debugMode) {
|
|
429
|
+
logDebug('PostHog Debug:', { args });
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
private async validateEventProperties(event: string, properties: any) {
|
|
434
|
+
// Check for reserved property names
|
|
435
|
+
const reserved = new Set(['$set', '$set_once', '$unset', 'distinct_id', '$groups']);
|
|
436
|
+
const conflicts = Object.keys(properties).filter(
|
|
437
|
+
key => reserved.has(key) && !key.startsWith('$'),
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
if (conflicts.length > 0) {
|
|
441
|
+
logWarn('PostHog: Properties may conflict with reserved names:', {
|
|
442
|
+
conflicts: conflicts.join(', '),
|
|
443
|
+
event,
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Check for circular references
|
|
448
|
+
try {
|
|
449
|
+
JSON.stringify(properties);
|
|
450
|
+
} catch (error) {
|
|
451
|
+
logWarn('PostHog: Event properties contain circular references:', {
|
|
452
|
+
event,
|
|
453
|
+
error: error instanceof Error ? error.message : String(error),
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
private async reportError(error: any, method: string, context: any) {
|
|
459
|
+
if (this.debugMode) {
|
|
460
|
+
logError('PostHog Error:', error instanceof Error ? error : new Error(String(error)), {
|
|
461
|
+
method,
|
|
462
|
+
context,
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Could emit error event to other analytics providers
|
|
467
|
+
// this.emit('analytics_error', { provider: 'posthog', method, error: error.message, context });
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Consent management methods
|
|
471
|
+
async optIn(): Promise<void> {
|
|
472
|
+
if (this.posthogInstance) {
|
|
473
|
+
this.posthogInstance.opt_in_capturing();
|
|
474
|
+
await this.log('User opted in to tracking');
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
async optOut(): Promise<void> {
|
|
479
|
+
if (this.posthogInstance) {
|
|
480
|
+
this.posthogInstance.opt_out_capturing();
|
|
481
|
+
await this.log('User opted out of tracking');
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
hasOptedOut(): boolean {
|
|
486
|
+
return this.posthogInstance ? this.posthogInstance.has_opted_out_capturing() : false;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Enhanced group analytics
|
|
490
|
+
async groupIdentify(
|
|
491
|
+
groupType: string,
|
|
492
|
+
groupKey: string,
|
|
493
|
+
groupProperties: any,
|
|
494
|
+
options?: { setAsDefault?: boolean },
|
|
495
|
+
): Promise<void> {
|
|
496
|
+
if (!this.isInitialized || !this.posthogInstance) {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
try {
|
|
501
|
+
await this.log('Group identify:', groupType, groupKey, groupProperties);
|
|
502
|
+
|
|
503
|
+
// Set group properties
|
|
504
|
+
this.posthogInstance.group(groupType, groupKey, groupProperties);
|
|
505
|
+
|
|
506
|
+
// Optionally set as default group for user
|
|
507
|
+
if (options?.setAsDefault) {
|
|
508
|
+
this.posthogInstance.register({
|
|
509
|
+
[`$groups`]: {
|
|
510
|
+
[groupType]: groupKey,
|
|
511
|
+
},
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
} catch (error) {
|
|
515
|
+
await this.reportError(error, 'groupIdentify', { groupKey, groupProperties, groupType });
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview PostHog provider shared logic and exports
|
|
3
|
+
*
|
|
4
|
+
* Barrel export file for PostHog Analytics provider implementations and types.
|
|
5
|
+
*
|
|
6
|
+
* @module @repo/analytics/providers/posthog
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Export provider implementations
|
|
10
|
+
// Export types
|
|
11
|
+
export type * from './types';
|