@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,329 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview PostHog server-side (Node.js) provider implementation
|
|
3
|
+
*
|
|
4
|
+
* Provides server-side integration with PostHog Analytics including feature flags support.
|
|
5
|
+
* Supports full PostHog tracking including capture, identify, group, alias, and feature flags.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* This provider:
|
|
9
|
+
* - Uses PostHog Node.js SDK for server-side tracking
|
|
10
|
+
* - Supports feature flags and experiments
|
|
11
|
+
* - Optimized for Next.js server components
|
|
12
|
+
* - Integrates with PostHog's API endpoint
|
|
13
|
+
*
|
|
14
|
+
* @module @repo/analytics/providers/posthog/server
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type {
|
|
18
|
+
BootstrapData,
|
|
19
|
+
EnhancedPostHogProvider,
|
|
20
|
+
ExperimentInfo,
|
|
21
|
+
PostHogConfig,
|
|
22
|
+
} from './types';
|
|
23
|
+
import type { AnalyticsProvider, ProviderConfig } from '../../shared/types/types';
|
|
24
|
+
|
|
25
|
+
export class PostHogServerProvider implements AnalyticsProvider, Partial<EnhancedPostHogProvider> {
|
|
26
|
+
readonly name = 'posthog';
|
|
27
|
+
private config: PostHogConfig;
|
|
28
|
+
private client: any = null;
|
|
29
|
+
private isInitialized = false;
|
|
30
|
+
|
|
31
|
+
constructor(config: ProviderConfig) {
|
|
32
|
+
if (!config.apiKey) {
|
|
33
|
+
throw new Error('PostHog apiKey is required');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
this.config = {
|
|
37
|
+
apiKey: config.apiKey,
|
|
38
|
+
options: {
|
|
39
|
+
// Server-side optimizations for Next.js
|
|
40
|
+
flushAt: 1,
|
|
41
|
+
flushInterval: 0,
|
|
42
|
+
...config.options,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async initialize(): Promise<void> {
|
|
48
|
+
if (this.isInitialized) return;
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
// Dynamically import PostHog Node.js SDK
|
|
52
|
+
const { PostHog } = await import('posthog-node');
|
|
53
|
+
|
|
54
|
+
// Extract client-side only options that don't apply to server
|
|
55
|
+
const {
|
|
56
|
+
api_host: _api_host,
|
|
57
|
+
autocapture: _autocapture,
|
|
58
|
+
bootstrap: _bootstrap,
|
|
59
|
+
capture_pageview: _capture_pageview,
|
|
60
|
+
cross_subdomain_cookie: _cross_subdomain_cookie,
|
|
61
|
+
disable_session_recording: _disable_session_recording,
|
|
62
|
+
fetch_options: _fetch_options,
|
|
63
|
+
loaded: _loaded,
|
|
64
|
+
persistence: _persistence,
|
|
65
|
+
ui_host: _ui_host,
|
|
66
|
+
...serverOptions
|
|
67
|
+
} = this.config.options ?? {};
|
|
68
|
+
|
|
69
|
+
this.client = new PostHog(this.config.apiKey, {
|
|
70
|
+
host: 'https://app.posthog.com',
|
|
71
|
+
...serverOptions,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
this.isInitialized = true;
|
|
75
|
+
} catch {
|
|
76
|
+
throw new Error('PostHog Node.js SDK not available. Install with: npm install posthog-node');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async track(event: string, properties: any = {}): Promise<void> {
|
|
81
|
+
if (!this.isInitialized || !this.client) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
this.client.capture({
|
|
87
|
+
distinctId: properties.userId ?? properties.distinctId ?? 'anonymous',
|
|
88
|
+
event,
|
|
89
|
+
properties,
|
|
90
|
+
});
|
|
91
|
+
} catch {
|
|
92
|
+
// Silently fail to avoid disrupting app flow
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async identify(userId: string, traits: any = {}): Promise<void> {
|
|
97
|
+
if (!this.isInitialized || !this.client) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
this.client.identify({
|
|
103
|
+
distinctId: userId,
|
|
104
|
+
properties: traits,
|
|
105
|
+
});
|
|
106
|
+
} catch {
|
|
107
|
+
// Silently fail to avoid disrupting app flow
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async page(name?: string, properties: any = {}): Promise<void> {
|
|
112
|
+
if (!this.isInitialized || !this.client) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
this.client.capture({
|
|
118
|
+
distinctId: properties.userId ?? properties.distinctId ?? 'anonymous',
|
|
119
|
+
event: '$pageview',
|
|
120
|
+
properties: {
|
|
121
|
+
$current_url: properties.url,
|
|
122
|
+
$title: name,
|
|
123
|
+
...properties,
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
} catch {
|
|
127
|
+
// Silently fail to avoid disrupting app flow
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async group(groupId: string, traits: any = {}): Promise<void> {
|
|
132
|
+
if (!this.isInitialized || !this.client) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
this.client.groupIdentify({
|
|
138
|
+
groupKey: groupId,
|
|
139
|
+
groupType: 'company',
|
|
140
|
+
properties: traits,
|
|
141
|
+
});
|
|
142
|
+
} catch {
|
|
143
|
+
// Silently fail to avoid disrupting app flow
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async alias(userId: string, previousId: string): Promise<void> {
|
|
148
|
+
if (!this.isInitialized || !this.client) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
this.client.alias({
|
|
154
|
+
alias: previousId,
|
|
155
|
+
distinctId: userId,
|
|
156
|
+
});
|
|
157
|
+
} catch {
|
|
158
|
+
// Silently fail to avoid disrupting app flow
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Feature Flag Methods (Server-Side)
|
|
163
|
+
async getAllFlags(userId: string): Promise<Record<string, any>> {
|
|
164
|
+
if (!this.isInitialized || !this.client) {
|
|
165
|
+
return {};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!userId) {
|
|
169
|
+
return {};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
const flags = await this.client.getAllFlags(userId);
|
|
174
|
+
return flags ?? {};
|
|
175
|
+
} catch {
|
|
176
|
+
return {};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async getFeatureFlag(flag: string, userId: string): Promise<any> {
|
|
181
|
+
if (!this.isInitialized || !this.client) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (!userId) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
return await this.client.getFeatureFlag(flag, userId);
|
|
191
|
+
} catch {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async isFeatureEnabled(flag: string, userId: string): Promise<boolean> {
|
|
197
|
+
if (!this.isInitialized || !this.client) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (!userId) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
const flagValue = await this.client.isFeatureEnabled(flag, userId);
|
|
207
|
+
return Boolean(flagValue);
|
|
208
|
+
} catch {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async getFeatureFlagPayload(flag: string, userId?: string): Promise<any | null> {
|
|
214
|
+
if (!this.isInitialized || !this.client) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (!userId) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
const payload = await this.client.getFeatureFlagPayload(flag, userId);
|
|
224
|
+
return payload ?? null;
|
|
225
|
+
} catch {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async getActiveExperiments(userId: string): Promise<ExperimentInfo[]> {
|
|
231
|
+
if (!this.isInitialized || !this.client) {
|
|
232
|
+
return [];
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (!userId) {
|
|
236
|
+
return [];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
const flags = await this.getAllFlags(userId);
|
|
241
|
+
const experiments: ExperimentInfo[] = [];
|
|
242
|
+
|
|
243
|
+
for (const [key, variant] of Object.entries(flags)) {
|
|
244
|
+
if (variant !== false) {
|
|
245
|
+
const payload = await this.getFeatureFlagPayload(key, userId);
|
|
246
|
+
experiments.push({
|
|
247
|
+
key,
|
|
248
|
+
payload: payload ?? undefined,
|
|
249
|
+
variant: typeof variant === 'object' ? JSON.stringify(variant) : String(variant),
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return experiments;
|
|
255
|
+
} catch {
|
|
256
|
+
return [];
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Bootstrap method - primary use case for server-side
|
|
261
|
+
async getBootstrapData(distinctId: string): Promise<BootstrapData> {
|
|
262
|
+
if (!this.isInitialized || !this.client) {
|
|
263
|
+
return { distinctID: distinctId };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
// Get all flags for the user
|
|
268
|
+
const featureFlags = await this.getAllFlags(distinctId);
|
|
269
|
+
|
|
270
|
+
// Get payloads for flags that have them
|
|
271
|
+
const featureFlagPayloads: Record<string, any> = {};
|
|
272
|
+
|
|
273
|
+
for (const [flagKey, flagValue] of Object.entries(featureFlags)) {
|
|
274
|
+
if (flagValue !== false) {
|
|
275
|
+
try {
|
|
276
|
+
const payload = await this.getFeatureFlagPayload(flagKey, distinctId);
|
|
277
|
+
if (payload) {
|
|
278
|
+
featureFlagPayloads[flagKey] = payload;
|
|
279
|
+
}
|
|
280
|
+
} catch {
|
|
281
|
+
// Continue if individual payload fetch fails - silently ignore
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return {
|
|
287
|
+
distinctID: distinctId,
|
|
288
|
+
};
|
|
289
|
+
} catch {
|
|
290
|
+
return { distinctID: distinctId };
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Utility Methods
|
|
295
|
+
reset(): void {
|
|
296
|
+
// Server-side reset doesn't make sense in the same way as client-side
|
|
297
|
+
// But we can provide a method for consistency
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
async shutdown(): Promise<void> {
|
|
301
|
+
if (!this.isInitialized || !this.client) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
try {
|
|
306
|
+
await this.client.shutdown();
|
|
307
|
+
} catch {
|
|
308
|
+
// Silently fail
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Server-specific helper methods
|
|
313
|
+
async forceFlush(): Promise<void> {
|
|
314
|
+
if (!this.isInitialized || !this.client) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
try {
|
|
319
|
+
await this.client.flush();
|
|
320
|
+
} catch {
|
|
321
|
+
// Silently fail
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Get the PostHog client for advanced usage
|
|
326
|
+
getClient(): any {
|
|
327
|
+
return this.client;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview PostHog provider type definitions
|
|
3
|
+
*
|
|
4
|
+
* Defines TypeScript types and interfaces for PostHog Analytics provider configuration.
|
|
5
|
+
* Includes feature flags, experiments, and bootstrap data types.
|
|
6
|
+
*
|
|
7
|
+
* @module @repo/analytics/providers/posthog/types
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export interface PostHogConfig {
|
|
11
|
+
apiKey: string;
|
|
12
|
+
options?: PostHogOptions | undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface PostHogOptions {
|
|
16
|
+
host?: string;
|
|
17
|
+
autocapture?: boolean;
|
|
18
|
+
capture_pageview?: boolean;
|
|
19
|
+
capture_pageleave?: boolean;
|
|
20
|
+
session_recording?: boolean;
|
|
21
|
+
debug?: boolean;
|
|
22
|
+
bootstrap?: BootstrapData;
|
|
23
|
+
person_profiles?: 'always' | 'never' | 'identified_only';
|
|
24
|
+
api?: {
|
|
25
|
+
host?: string;
|
|
26
|
+
};
|
|
27
|
+
ui?: {
|
|
28
|
+
host?: string;
|
|
29
|
+
};
|
|
30
|
+
feature_flag_request_timeout_ms?: number;
|
|
31
|
+
advanced_disable_decide?: boolean;
|
|
32
|
+
disable_surveys?: boolean;
|
|
33
|
+
disable_toolbar?: boolean;
|
|
34
|
+
disable_session_recording?: boolean;
|
|
35
|
+
property_blacklist?: string[];
|
|
36
|
+
sanitize_properties?: (properties: any, event: string) => any;
|
|
37
|
+
request_headers?: Record<string, string>;
|
|
38
|
+
respect_dnt?: boolean;
|
|
39
|
+
opt_out_capturing_by_default?: boolean;
|
|
40
|
+
opt_out_persistence_by_default?: boolean;
|
|
41
|
+
opt_out_useragent_filter?: boolean;
|
|
42
|
+
cross_subdomain_cookie?: boolean;
|
|
43
|
+
persistence?: 'localStorage' | 'cookie' | 'memory' | 'localStorage+cookie';
|
|
44
|
+
persistence_name?: string;
|
|
45
|
+
cookie_name?: string;
|
|
46
|
+
loaded?: (posthog: any) => void;
|
|
47
|
+
on_xhr_error?: (failedRequest: XMLHttpRequest) => void;
|
|
48
|
+
mask_all_element_attributes?: boolean;
|
|
49
|
+
mask_all_text?: boolean;
|
|
50
|
+
advanced_disable_feature_flags?: boolean;
|
|
51
|
+
advanced_disable_feature_flags_on_first_load?: boolean;
|
|
52
|
+
advanced_disable_toolbar_metrics?: boolean;
|
|
53
|
+
segment?: any;
|
|
54
|
+
name?: string;
|
|
55
|
+
|
|
56
|
+
// Server-specific options
|
|
57
|
+
flushAt?: number;
|
|
58
|
+
flushInterval?: number;
|
|
59
|
+
api_host?: string;
|
|
60
|
+
ui_host?: string;
|
|
61
|
+
fetch_options?: any;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface BootstrapData {
|
|
65
|
+
distinctID?: string;
|
|
66
|
+
isIdentifiedID?: boolean;
|
|
67
|
+
featureFlags?: Record<string, any>;
|
|
68
|
+
featureFlagPayloads?: Record<string, any>;
|
|
69
|
+
sessionRecording?: {
|
|
70
|
+
endpoint?: string;
|
|
71
|
+
};
|
|
72
|
+
surveys?: any[];
|
|
73
|
+
toolbarParams?: Record<string, any>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface ExperimentInfo {
|
|
77
|
+
key: string;
|
|
78
|
+
variant: string | boolean;
|
|
79
|
+
payload?: any;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface EnhancedPostHogProvider {
|
|
83
|
+
// Feature flag methods
|
|
84
|
+
isFeatureEnabled(flag: string, userId?: string): Promise<boolean>;
|
|
85
|
+
getFeatureFlag(flag: string, userId?: string): Promise<any>;
|
|
86
|
+
getFeatureFlagPayload(flag: string): any;
|
|
87
|
+
reloadFeatureFlags(): Promise<void>;
|
|
88
|
+
getAllFlags(userId?: string): Promise<Record<string, any>>;
|
|
89
|
+
|
|
90
|
+
// Experiment methods
|
|
91
|
+
getActiveExperiments(userId?: string): Promise<ExperimentInfo[]>;
|
|
92
|
+
|
|
93
|
+
// Bootstrap methods
|
|
94
|
+
getBootstrapData(distinctId: string): Promise<BootstrapData>;
|
|
95
|
+
|
|
96
|
+
// Session methods
|
|
97
|
+
startSessionRecording(): void;
|
|
98
|
+
stopSessionRecording(): void;
|
|
99
|
+
|
|
100
|
+
// Advanced methods
|
|
101
|
+
reset(): void;
|
|
102
|
+
register(properties: Record<string, any>): void;
|
|
103
|
+
unregister(property: string): void;
|
|
104
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Segment client-side (browser) provider implementation
|
|
3
|
+
*
|
|
4
|
+
* Provides client-side integration with Segment Analytics using @segment/analytics-next.
|
|
5
|
+
* Supports full Segment.io spec tracking including track, identify, page, group, and alias.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* This provider:
|
|
9
|
+
* - Uses @segment/analytics-next for browser tracking
|
|
10
|
+
* - Supports dynamic imports to avoid SSR issues
|
|
11
|
+
* - Handles initialization and event tracking
|
|
12
|
+
* - Integrates with Segment's CDN and API
|
|
13
|
+
*
|
|
14
|
+
* @module @repo/analytics/providers/segment/client
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { BaseAnalyticsProvider } from '../base-provider';
|
|
18
|
+
|
|
19
|
+
import type { SegmentConfig } from './types';
|
|
20
|
+
import type {
|
|
21
|
+
AnalyticsContext,
|
|
22
|
+
GroupTraits,
|
|
23
|
+
PageProperties,
|
|
24
|
+
Properties,
|
|
25
|
+
ProviderConfig,
|
|
26
|
+
UserTraits,
|
|
27
|
+
} from '../../shared/types/types';
|
|
28
|
+
|
|
29
|
+
// Type for AnalyticsBrowser instance
|
|
30
|
+
interface AnalyticsBrowserInstance {
|
|
31
|
+
track: (event: string, properties?: Properties) => Promise<unknown>;
|
|
32
|
+
identify: (userId: string, traits?: UserTraits) => Promise<unknown>;
|
|
33
|
+
page: (name?: string, properties?: PageProperties) => Promise<unknown>;
|
|
34
|
+
group: (groupId: string, traits?: GroupTraits) => Promise<unknown>;
|
|
35
|
+
alias: (userId: string, previousId: string) => Promise<unknown>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class SegmentClientProvider extends BaseAnalyticsProvider<SegmentConfig> {
|
|
39
|
+
readonly name = 'segment';
|
|
40
|
+
private analytics: AnalyticsBrowserInstance | null = null;
|
|
41
|
+
|
|
42
|
+
constructor(config: ProviderConfig) {
|
|
43
|
+
super(config);
|
|
44
|
+
|
|
45
|
+
if (!config.writeKey) {
|
|
46
|
+
throw new Error('Segment writeKey is required');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
this.config = {
|
|
50
|
+
options: config.options as Record<string, unknown>,
|
|
51
|
+
writeKey: config.writeKey,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
protected async doInitialize(): Promise<void> {
|
|
56
|
+
// Dynamically import Segment Analytics Next
|
|
57
|
+
const { AnalyticsBrowser } = await import(
|
|
58
|
+
/* webpackChunkName: "segment-analytics-browser" */
|
|
59
|
+
'@segment/analytics-next'
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// Initialize Analytics Browser
|
|
63
|
+
this.analytics = AnalyticsBrowser.load({
|
|
64
|
+
writeKey: this.config.writeKey,
|
|
65
|
+
...this.config.options,
|
|
66
|
+
}) as unknown as AnalyticsBrowserInstance;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
protected async doTrack(
|
|
70
|
+
event: string,
|
|
71
|
+
properties: Properties,
|
|
72
|
+
_context?: AnalyticsContext,
|
|
73
|
+
): Promise<void> {
|
|
74
|
+
if (!this.analytics) return;
|
|
75
|
+
await this.analytics.track(event, properties);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
protected async doIdentify(
|
|
79
|
+
userId: string,
|
|
80
|
+
traits: UserTraits,
|
|
81
|
+
_context?: AnalyticsContext,
|
|
82
|
+
): Promise<void> {
|
|
83
|
+
if (!this.analytics) return;
|
|
84
|
+
await this.analytics.identify(userId, traits);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
protected async doPage(
|
|
88
|
+
name: string | undefined,
|
|
89
|
+
properties: PageProperties,
|
|
90
|
+
_context?: AnalyticsContext,
|
|
91
|
+
): Promise<void> {
|
|
92
|
+
if (!this.analytics) return;
|
|
93
|
+
await this.analytics.page(name, properties);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
protected async doGroup(
|
|
97
|
+
groupId: string,
|
|
98
|
+
traits: GroupTraits,
|
|
99
|
+
_context?: AnalyticsContext,
|
|
100
|
+
): Promise<void> {
|
|
101
|
+
if (!this.analytics) return;
|
|
102
|
+
await this.analytics.group(groupId, traits);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
protected async doAlias(
|
|
106
|
+
userId: string,
|
|
107
|
+
previousId: string,
|
|
108
|
+
_context?: AnalyticsContext,
|
|
109
|
+
): Promise<void> {
|
|
110
|
+
if (!this.analytics) return;
|
|
111
|
+
await this.analytics.alias(userId, previousId);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Segment provider shared logic and exports
|
|
3
|
+
*
|
|
4
|
+
* Barrel export file for Segment Analytics provider implementations and types.
|
|
5
|
+
*
|
|
6
|
+
* @module @repo/analytics/providers/segment
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Export provider implementations
|
|
10
|
+
// Export types
|
|
11
|
+
export type * from './types';
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Segment server-side (Node.js) provider implementation
|
|
3
|
+
*
|
|
4
|
+
* Provides server-side integration with Segment Analytics using @segment/analytics-next.
|
|
5
|
+
* Note: analytics-next supports universal runtime environments including Node.js.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* This provider:
|
|
9
|
+
* - Uses @segment/analytics-next for server-side tracking
|
|
10
|
+
* - Supports Node.js runtime with flushAt: 1 for immediate sending
|
|
11
|
+
* - Handles initialization and event tracking
|
|
12
|
+
* - Integrates with Segment's API endpoint
|
|
13
|
+
*
|
|
14
|
+
* @module @repo/analytics/providers/segment/server
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { BaseAnalyticsProvider } from '../base-provider';
|
|
18
|
+
|
|
19
|
+
import type { SegmentConfig } from './types';
|
|
20
|
+
import type {
|
|
21
|
+
AnalyticsContext,
|
|
22
|
+
GroupTraits,
|
|
23
|
+
PageProperties,
|
|
24
|
+
Properties,
|
|
25
|
+
ProviderConfig,
|
|
26
|
+
UserTraits,
|
|
27
|
+
} from '../../shared/types/types';
|
|
28
|
+
|
|
29
|
+
// Type for Analytics server instance
|
|
30
|
+
interface AnalyticsServerInstance {
|
|
31
|
+
track: (params: { event: string; properties?: Properties }) => Promise<unknown>;
|
|
32
|
+
identify: (params: { userId: string; traits?: UserTraits }) => Promise<unknown>;
|
|
33
|
+
page: (params: { name?: string; properties?: PageProperties }) => Promise<unknown>;
|
|
34
|
+
group: (params: { groupId: string; traits?: GroupTraits }) => Promise<unknown>;
|
|
35
|
+
alias: (params: { userId: string; previousId: string }) => Promise<unknown>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class SegmentServerProvider extends BaseAnalyticsProvider<SegmentConfig> {
|
|
39
|
+
readonly name = 'segment';
|
|
40
|
+
private analytics: AnalyticsServerInstance | null = null;
|
|
41
|
+
|
|
42
|
+
constructor(config: ProviderConfig) {
|
|
43
|
+
super(config);
|
|
44
|
+
|
|
45
|
+
if (!config.writeKey) {
|
|
46
|
+
throw new Error('Segment writeKey is required');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
this.config = {
|
|
50
|
+
options: config.options as Record<string, unknown>,
|
|
51
|
+
writeKey: config.writeKey,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
protected async doInitialize(): Promise<void> {
|
|
56
|
+
// Dynamically import Segment Analytics Next
|
|
57
|
+
// analytics-next supports Node.js runtime with flushAt: 1 for server environments
|
|
58
|
+
const { Analytics } = await import(
|
|
59
|
+
/* webpackChunkName: "segment-analytics" */
|
|
60
|
+
'@segment/analytics-next'
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
this.analytics = new Analytics({
|
|
64
|
+
writeKey: this.config.writeKey,
|
|
65
|
+
// Note: flushAt is not supported in @segment/analytics-next
|
|
66
|
+
// Server-side events are sent immediately by default
|
|
67
|
+
...this.config.options,
|
|
68
|
+
}) as unknown as AnalyticsServerInstance;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
protected async doTrack(
|
|
72
|
+
event: string,
|
|
73
|
+
properties: Properties,
|
|
74
|
+
_context?: AnalyticsContext,
|
|
75
|
+
): Promise<void> {
|
|
76
|
+
if (!this.analytics) return;
|
|
77
|
+
await this.analytics.track({ event, properties });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
protected async doIdentify(
|
|
81
|
+
userId: string,
|
|
82
|
+
traits: UserTraits,
|
|
83
|
+
_context?: AnalyticsContext,
|
|
84
|
+
): Promise<void> {
|
|
85
|
+
if (!this.analytics) return;
|
|
86
|
+
await this.analytics.identify({ userId, traits });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
protected async doPage(
|
|
90
|
+
name: string | undefined,
|
|
91
|
+
properties: PageProperties,
|
|
92
|
+
_context?: AnalyticsContext,
|
|
93
|
+
): Promise<void> {
|
|
94
|
+
if (!this.analytics) return;
|
|
95
|
+
await this.analytics.page({ ...(name && { name }), properties });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
protected async doGroup(
|
|
99
|
+
groupId: string,
|
|
100
|
+
traits: GroupTraits,
|
|
101
|
+
_context?: AnalyticsContext,
|
|
102
|
+
): Promise<void> {
|
|
103
|
+
if (!this.analytics) return;
|
|
104
|
+
await this.analytics.group({ groupId, traits });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
protected async doAlias(
|
|
108
|
+
userId: string,
|
|
109
|
+
previousId: string,
|
|
110
|
+
_context?: AnalyticsContext,
|
|
111
|
+
): Promise<void> {
|
|
112
|
+
if (!this.analytics) return;
|
|
113
|
+
await this.analytics.alias({ userId, previousId });
|
|
114
|
+
}
|
|
115
|
+
}
|