@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
package/src/next/rsc.tsx
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview React Server Component (RSC) specific analytics utilities for Next.js 15
|
|
3
|
+
*
|
|
4
|
+
* Provides server-side analytics tracking with proper caching and data flow.
|
|
5
|
+
* Includes React Server Component integration, server actions, and PostHog bootstrap utilities.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* This module:
|
|
9
|
+
* - Uses React cache for request-scoped analytics instances
|
|
10
|
+
* - Provides server actions for client component integration
|
|
11
|
+
* - Supports PostHog bootstrap data generation
|
|
12
|
+
* - Extracts request context from headers
|
|
13
|
+
* - Avoids contamination by using dependency injection for Next.js runtime
|
|
14
|
+
*
|
|
15
|
+
* @module @od-oneapp/analytics/next/rsc
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { cache } from 'react';
|
|
19
|
+
|
|
20
|
+
import { getCompleteBootstrapData } from '../shared/utils/posthog-next-utils';
|
|
21
|
+
|
|
22
|
+
import { createNextJSServerAnalytics } from './server';
|
|
23
|
+
|
|
24
|
+
import type { BootstrapData } from '../shared/types/posthog-types';
|
|
25
|
+
import type { AnalyticsConfig, TrackingOptions } from '../shared/types/types';
|
|
26
|
+
import type { ReadonlyRequestCookies } from 'next/dist/server/web/spec-extension/adapters/request-cookies';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Cached analytics instance for RSC
|
|
30
|
+
* Uses React cache to ensure single instance per request
|
|
31
|
+
*/
|
|
32
|
+
const getAnalyticsInstance = cache(async (config: AnalyticsConfig) => {
|
|
33
|
+
const analytics = createNextJSServerAnalytics(config);
|
|
34
|
+
await analytics.initialize();
|
|
35
|
+
return analytics;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Track event from React Server Components
|
|
40
|
+
* Automatically extracts request context
|
|
41
|
+
*
|
|
42
|
+
* @param headersFn - Function to get request headers (from 'next/headers')
|
|
43
|
+
*/
|
|
44
|
+
export async function trackServerEvent(
|
|
45
|
+
event: string,
|
|
46
|
+
properties?: Record<string, unknown>,
|
|
47
|
+
options?: TrackingOptions & { config?: AnalyticsConfig; headersFn?: () => Promise<Headers> },
|
|
48
|
+
): Promise<void> {
|
|
49
|
+
const config = options?.config ?? getDefaultConfig();
|
|
50
|
+
const analytics = await getAnalyticsInstance(config);
|
|
51
|
+
|
|
52
|
+
// Extract request context
|
|
53
|
+
// Note: headersFn must be provided by the Next.js app to avoid contamination
|
|
54
|
+
if (!options?.headersFn) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
'trackServerEvent requires headersFn parameter. Import and pass headers from "next/headers" in your Next.js app.',
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
const requestHeaders = await options.headersFn();
|
|
60
|
+
const userAgent = requestHeaders.get('user-agent') ?? undefined;
|
|
61
|
+
const referer = requestHeaders.get('referer') ?? undefined;
|
|
62
|
+
|
|
63
|
+
const enhancedProperties = {
|
|
64
|
+
...properties,
|
|
65
|
+
server_side: true,
|
|
66
|
+
referer,
|
|
67
|
+
timestamp: new Date().toISOString(),
|
|
68
|
+
user_agent: userAgent,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
await analytics.track(event, enhancedProperties, options);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Identify user from React Server Components
|
|
76
|
+
*/
|
|
77
|
+
export async function identifyServerUser(
|
|
78
|
+
userId: string,
|
|
79
|
+
traits?: any,
|
|
80
|
+
options?: TrackingOptions & { config?: AnalyticsConfig },
|
|
81
|
+
): Promise<void> {
|
|
82
|
+
const config = options?.config ?? getDefaultConfig();
|
|
83
|
+
const analytics = await getAnalyticsInstance(config);
|
|
84
|
+
|
|
85
|
+
const enhancedTraits = {
|
|
86
|
+
...traits,
|
|
87
|
+
identified_at: new Date().toISOString(),
|
|
88
|
+
identified_from: 'server',
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
await analytics.identify(userId, enhancedTraits, options);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Track page view from React Server Components
|
|
96
|
+
*
|
|
97
|
+
* @param headersFn - Function to get request headers (from 'next/headers')
|
|
98
|
+
*/
|
|
99
|
+
export async function trackServerPageView(
|
|
100
|
+
pathname: string,
|
|
101
|
+
properties?: any,
|
|
102
|
+
options?: TrackingOptions & { config?: AnalyticsConfig; headersFn?: () => Promise<Headers> },
|
|
103
|
+
): Promise<void> {
|
|
104
|
+
const config = options?.config ?? getDefaultConfig();
|
|
105
|
+
const analytics = await getAnalyticsInstance(config);
|
|
106
|
+
|
|
107
|
+
// Note: headersFn must be provided by the Next.js app to avoid contamination
|
|
108
|
+
if (!options?.headersFn) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
'trackServerPageView requires headersFn parameter. Import and pass headers from "next/headers" in your Next.js app.',
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
const requestHeaders = await options.headersFn();
|
|
114
|
+
const userAgent = requestHeaders.get('user-agent') ?? undefined;
|
|
115
|
+
const referer = requestHeaders.get('referer') ?? undefined;
|
|
116
|
+
|
|
117
|
+
const enhancedProperties = {
|
|
118
|
+
...properties,
|
|
119
|
+
path: pathname,
|
|
120
|
+
referer,
|
|
121
|
+
server_rendered: true,
|
|
122
|
+
timestamp: new Date().toISOString(),
|
|
123
|
+
user_agent: userAgent,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
await analytics.page(pathname, enhancedProperties, options);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get PostHog bootstrap data for server components
|
|
131
|
+
* Includes distinct ID extraction and feature flag fetching
|
|
132
|
+
*
|
|
133
|
+
* @param cookiesFn - Function to get cookies (from 'next/headers')
|
|
134
|
+
*/
|
|
135
|
+
export function createGetServerBootstrapData(cookiesFn: () => any) {
|
|
136
|
+
return cache(
|
|
137
|
+
async (
|
|
138
|
+
apiKey: string,
|
|
139
|
+
options?: {
|
|
140
|
+
host?: string;
|
|
141
|
+
timeout?: number;
|
|
142
|
+
},
|
|
143
|
+
): Promise<BootstrapData> => {
|
|
144
|
+
const cookieStore = cookiesFn();
|
|
145
|
+
return await getCompleteBootstrapData(cookieStore, apiKey, options);
|
|
146
|
+
},
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Get PostHog bootstrap data (requires cookiesFn to be provided)
|
|
152
|
+
* Use createGetServerBootstrapData with cookies from 'next/headers' in your Next.js app
|
|
153
|
+
*/
|
|
154
|
+
export const getServerBootstrapData = (cookiesFn: () => ReadonlyRequestCookies) => {
|
|
155
|
+
return cache(
|
|
156
|
+
async (
|
|
157
|
+
apiKey: string,
|
|
158
|
+
options?: {
|
|
159
|
+
host?: string;
|
|
160
|
+
timeout?: number;
|
|
161
|
+
},
|
|
162
|
+
): Promise<BootstrapData> => {
|
|
163
|
+
const cookieStore = cookiesFn();
|
|
164
|
+
return await getCompleteBootstrapData(cookieStore, apiKey, options);
|
|
165
|
+
},
|
|
166
|
+
);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Analytics provider for React Server Components
|
|
171
|
+
* Use in server component layouts to provide analytics to children
|
|
172
|
+
*/
|
|
173
|
+
export async function ServerAnalyticsProvider({
|
|
174
|
+
children,
|
|
175
|
+
config,
|
|
176
|
+
posthogApiKey,
|
|
177
|
+
}: {
|
|
178
|
+
children: React.ReactNode;
|
|
179
|
+
config?: AnalyticsConfig;
|
|
180
|
+
posthogApiKey?: string;
|
|
181
|
+
}): Promise<React.ReactElement> {
|
|
182
|
+
// Initialize analytics on the server
|
|
183
|
+
if (config) {
|
|
184
|
+
await getAnalyticsInstance(config);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Get bootstrap data if PostHog is configured
|
|
188
|
+
// Note: getServerBootstrapData requires a cookiesFn to be provided
|
|
189
|
+
// This is intentionally left as a placeholder for implementation
|
|
190
|
+
if (posthogApiKey) {
|
|
191
|
+
// TODO: Implement bootstrap data fetching with proper cookies function
|
|
192
|
+
// const { cookies } = await import('next/headers');
|
|
193
|
+
// const bootstrapDataFn = getServerBootstrapData(cookies);
|
|
194
|
+
// await bootstrapDataFn(posthogApiKey);
|
|
195
|
+
void posthogApiKey; // Prevent unused variable warning
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Return children with any necessary context
|
|
199
|
+
// Note: In RSC, we can't use React context, but we can pass data via props
|
|
200
|
+
return children as React.ReactElement;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Higher-order component for tracking page views in server components
|
|
205
|
+
*/
|
|
206
|
+
export function withServerPageTracking<P extends object>(
|
|
207
|
+
Component: React.ComponentType<P>,
|
|
208
|
+
pageName?: string,
|
|
209
|
+
) {
|
|
210
|
+
return async function TrackedServerComponent(props: P) {
|
|
211
|
+
// Get pathname from props or use provided name
|
|
212
|
+
const pathname = pageName ?? (props as any).params?.pathname ?? 'unknown';
|
|
213
|
+
|
|
214
|
+
// Track page view
|
|
215
|
+
await trackServerPageView(pathname, {
|
|
216
|
+
component: Component.name,
|
|
217
|
+
props: Object.keys(props as any),
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
return <Component {...props} />;
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Get default analytics configuration
|
|
226
|
+
* Can be customized via environment variables
|
|
227
|
+
*/
|
|
228
|
+
function getDefaultConfig(): AnalyticsConfig {
|
|
229
|
+
const config: AnalyticsConfig = {
|
|
230
|
+
providers: {},
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// Add providers based on environment variables
|
|
234
|
+
if (process.env.NEXT_PUBLIC_POSTHOG_KEY) {
|
|
235
|
+
config.providers.posthog = {
|
|
236
|
+
apiKey: process.env.NEXT_PUBLIC_POSTHOG_KEY,
|
|
237
|
+
options: {
|
|
238
|
+
api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (process.env.NEXT_PUBLIC_SEGMENT_WRITE_KEY) {
|
|
244
|
+
config.providers.segment = {
|
|
245
|
+
writeKey: process.env.NEXT_PUBLIC_SEGMENT_WRITE_KEY,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Add console provider in development
|
|
250
|
+
if (process.env.NODE_ENV === 'development') {
|
|
251
|
+
config.providers.console = {
|
|
252
|
+
options: {
|
|
253
|
+
prefix: '[RSC Analytics]',
|
|
254
|
+
pretty: true,
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return config;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Server action for tracking events
|
|
264
|
+
* Can be called from client components
|
|
265
|
+
*/
|
|
266
|
+
export async function trackEventAction(
|
|
267
|
+
event: string,
|
|
268
|
+
properties?: any,
|
|
269
|
+
): Promise<{ success: boolean }> {
|
|
270
|
+
'use server';
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
await trackServerEvent(event, properties);
|
|
274
|
+
return { success: true };
|
|
275
|
+
} catch {
|
|
276
|
+
return { success: false };
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Server action for identifying users
|
|
282
|
+
* Can be called from client components
|
|
283
|
+
*/
|
|
284
|
+
export async function identifyUserAction(
|
|
285
|
+
userId: string,
|
|
286
|
+
traits?: any,
|
|
287
|
+
): Promise<{ success: boolean }> {
|
|
288
|
+
'use server';
|
|
289
|
+
|
|
290
|
+
try {
|
|
291
|
+
await identifyServerUser(userId, traits);
|
|
292
|
+
return { success: true };
|
|
293
|
+
} catch {
|
|
294
|
+
return { success: false };
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Re-export types for convenience
|
|
299
|
+
export type { BootstrapData } from '../shared/types/posthog-types';
|
|
300
|
+
export type { AnalyticsConfig, TrackingOptions } from '../shared/types/types';
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Next.js server-side analytics implementation
|
|
3
|
+
*
|
|
4
|
+
* Provides server-side analytics functionality for Next.js applications.
|
|
5
|
+
* Includes PostHog bootstrap data generation and server-side event tracking.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* This implementation is optimized for Next.js server components and API routes:
|
|
9
|
+
* - Supports PostHog bootstrap data generation
|
|
10
|
+
* - Extracts distinct IDs from cookies
|
|
11
|
+
* - Handles server-side event tracking
|
|
12
|
+
* - Integrates with Next.js server actions
|
|
13
|
+
*
|
|
14
|
+
* @module @od-oneapp/analytics/next/server
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { type AnalyticsManager } from '../shared/utils/manager';
|
|
18
|
+
import { createPostHogConfig, getCompleteBootstrapData } from '../shared/utils/posthog-next-utils';
|
|
19
|
+
|
|
20
|
+
import type { BootstrapData } from '../shared/types/posthog-types';
|
|
21
|
+
import type { AnalyticsConfig, TrackingOptions } from '../shared/types/types';
|
|
22
|
+
|
|
23
|
+
export interface NextJSServerAnalyticsConfig extends AnalyticsConfig {
|
|
24
|
+
nextjs?:
|
|
25
|
+
| {
|
|
26
|
+
// Enable debug mode
|
|
27
|
+
debug?: boolean | undefined;
|
|
28
|
+
|
|
29
|
+
// PostHog specific options
|
|
30
|
+
posthog?:
|
|
31
|
+
| {
|
|
32
|
+
// Cookies for distinct ID extraction
|
|
33
|
+
cookies?: any;
|
|
34
|
+
|
|
35
|
+
// API key for server-side operations
|
|
36
|
+
apiKey?: string | undefined;
|
|
37
|
+
|
|
38
|
+
// Host override
|
|
39
|
+
host?: string | undefined;
|
|
40
|
+
|
|
41
|
+
// Request timeout for server calls
|
|
42
|
+
timeout?: number | undefined;
|
|
43
|
+
}
|
|
44
|
+
| undefined;
|
|
45
|
+
}
|
|
46
|
+
| undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class NextJSServerAnalyticsManager {
|
|
50
|
+
private manager: AnalyticsManager | null = null;
|
|
51
|
+
private config: NextJSServerAnalyticsConfig;
|
|
52
|
+
private isInitialized = false;
|
|
53
|
+
|
|
54
|
+
constructor(config: NextJSServerAnalyticsConfig) {
|
|
55
|
+
this.config = config;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Initialize analytics for server-side usage
|
|
60
|
+
*/
|
|
61
|
+
async initialize(): Promise<void> {
|
|
62
|
+
if (this.isInitialized) return;
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
// Dynamically import server providers
|
|
66
|
+
const { createAnalyticsManager } = await import('../shared/utils/manager');
|
|
67
|
+
const { SegmentServerProvider } = await import('../providers/segment/server');
|
|
68
|
+
const { PostHogServerProvider } = await import('../providers/posthog/server');
|
|
69
|
+
const { VercelServerProvider } = await import('../providers/vercel/server');
|
|
70
|
+
const { ConsoleProvider } = await import('../providers/console/server');
|
|
71
|
+
|
|
72
|
+
const SERVER_PROVIDERS = {
|
|
73
|
+
console: (config: any) => new ConsoleProvider(config),
|
|
74
|
+
posthog: (config: any) => new PostHogServerProvider(config),
|
|
75
|
+
segment: (config: any) => new SegmentServerProvider(config),
|
|
76
|
+
vercel: (config: any) => new VercelServerProvider(config),
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
this.manager = createAnalyticsManager(this.config, SERVER_PROVIDERS);
|
|
80
|
+
await this.manager.initialize();
|
|
81
|
+
this.isInitialized = true;
|
|
82
|
+
|
|
83
|
+
// Server analytics initialized successfully
|
|
84
|
+
} catch {
|
|
85
|
+
// Failed to initialize server analytics
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Track an event
|
|
91
|
+
*/
|
|
92
|
+
async track(event: string, properties?: any, options?: TrackingOptions): Promise<void> {
|
|
93
|
+
if (!this.isInitialized) {
|
|
94
|
+
await this.initialize();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (this.manager) {
|
|
98
|
+
await this.manager.track(event, properties, options);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Identify a user
|
|
104
|
+
*/
|
|
105
|
+
async identify(userId: string, traits?: any, options?: TrackingOptions): Promise<void> {
|
|
106
|
+
if (!this.isInitialized) {
|
|
107
|
+
await this.initialize();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (this.manager) {
|
|
111
|
+
await this.manager.identify(userId, traits, options);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Track a page view
|
|
117
|
+
*/
|
|
118
|
+
async page(name?: string, properties?: any, options?: TrackingOptions): Promise<void> {
|
|
119
|
+
if (!this.isInitialized) {
|
|
120
|
+
await this.initialize();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (this.manager) {
|
|
124
|
+
await this.manager.page(name, properties, options);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Track a group
|
|
130
|
+
*/
|
|
131
|
+
async group(groupId: string, traits?: any, options?: TrackingOptions): Promise<void> {
|
|
132
|
+
if (!this.isInitialized) {
|
|
133
|
+
await this.initialize();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (this.manager) {
|
|
137
|
+
await this.manager.group(groupId, traits, options);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Alias a user
|
|
143
|
+
*/
|
|
144
|
+
async alias(userId: string, previousId: string, options?: TrackingOptions): Promise<void> {
|
|
145
|
+
if (!this.isInitialized) {
|
|
146
|
+
await this.initialize();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (this.manager) {
|
|
150
|
+
await this.manager.alias(userId, previousId, options);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get initialization status
|
|
156
|
+
*/
|
|
157
|
+
getStatus() {
|
|
158
|
+
return {
|
|
159
|
+
activeProviders: this.manager?.getActiveProviders() ?? [],
|
|
160
|
+
isInitialized: this.isInitialized,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Create a Next.js optimized analytics instance for server
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```typescript
|
|
170
|
+
* const analytics = createNextJSServerAnalytics({
|
|
171
|
+
* providers: { segment: { writeKey: process.env.SEGMENT_KEY! } },
|
|
172
|
+
* });
|
|
173
|
+
* await analytics.initialize();
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
export function createNextJSServerAnalytics(
|
|
177
|
+
config: NextJSServerAnalyticsConfig,
|
|
178
|
+
): NextJSServerAnalyticsManager {
|
|
179
|
+
return new NextJSServerAnalyticsManager(config);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Get PostHog bootstrap data for server-side rendering
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* ```typescript
|
|
187
|
+
* const bootstrap = await getPostHogBootstrapDataOnServer(cookies(), apiKey);
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
export async function getPostHogBootstrapDataOnServer(
|
|
191
|
+
cookies: any,
|
|
192
|
+
apiKey: string,
|
|
193
|
+
options?: {
|
|
194
|
+
host?: string;
|
|
195
|
+
timeout?: number;
|
|
196
|
+
},
|
|
197
|
+
): Promise<BootstrapData> {
|
|
198
|
+
return await getCompleteBootstrapData(cookies, apiKey, options);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Enhanced analytics manager with PostHog bootstrap for SSR
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```typescript
|
|
206
|
+
* const { analytics, bootstrapData } =
|
|
207
|
+
* await createNextJSServerAnalyticsWithBootstrap(cookies(), apiKey);
|
|
208
|
+
* const config = createPostHogConfigWithBootstrap(apiKey, bootstrapData);
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
export async function createNextJSServerAnalyticsWithBootstrap(
|
|
212
|
+
cookies: any,
|
|
213
|
+
apiKey: string,
|
|
214
|
+
options?: {
|
|
215
|
+
host?: string;
|
|
216
|
+
timeout?: number;
|
|
217
|
+
providers?: Record<string, any>;
|
|
218
|
+
nextjs?: Omit<NextJSServerAnalyticsConfig['nextjs'], 'posthog'>;
|
|
219
|
+
},
|
|
220
|
+
): Promise<{
|
|
221
|
+
analytics: NextJSServerAnalyticsManager;
|
|
222
|
+
bootstrapData: BootstrapData;
|
|
223
|
+
}> {
|
|
224
|
+
// Get bootstrap data
|
|
225
|
+
const bootstrapData = await getCompleteBootstrapData(cookies, apiKey, {
|
|
226
|
+
host: options?.host,
|
|
227
|
+
timeout: options?.timeout,
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// Create config with server-side settings
|
|
231
|
+
const config: NextJSServerAnalyticsConfig = {
|
|
232
|
+
providers: {
|
|
233
|
+
posthog: createPostHogConfig(apiKey, {
|
|
234
|
+
host: options?.host,
|
|
235
|
+
}),
|
|
236
|
+
...options?.providers,
|
|
237
|
+
},
|
|
238
|
+
nextjs: {
|
|
239
|
+
...options?.nextjs,
|
|
240
|
+
posthog: {
|
|
241
|
+
apiKey,
|
|
242
|
+
cookies,
|
|
243
|
+
host: options?.host,
|
|
244
|
+
timeout: options?.timeout,
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const analytics = new NextJSServerAnalyticsManager(config);
|
|
250
|
+
await analytics.initialize();
|
|
251
|
+
|
|
252
|
+
return { analytics, bootstrapData };
|
|
253
|
+
}
|