@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.
Files changed (184) hide show
  1. package/README.md +509 -0
  2. package/dist/ai-YMnynb-t.mjs +3347 -0
  3. package/dist/ai-YMnynb-t.mjs.map +1 -0
  4. package/dist/chunk-DQk6qfdC.mjs +18 -0
  5. package/dist/client-CTzJVFU5.mjs +9 -0
  6. package/dist/client-CTzJVFU5.mjs.map +1 -0
  7. package/dist/client-CcFTauAh.mjs +54 -0
  8. package/dist/client-CcFTauAh.mjs.map +1 -0
  9. package/dist/client-CeOLjbac.mjs +281 -0
  10. package/dist/client-CeOLjbac.mjs.map +1 -0
  11. package/dist/client-D339NFJS.mjs +267 -0
  12. package/dist/client-D339NFJS.mjs.map +1 -0
  13. package/dist/client-next.d.mts +62 -0
  14. package/dist/client-next.d.mts.map +1 -0
  15. package/dist/client-next.mjs +525 -0
  16. package/dist/client-next.mjs.map +1 -0
  17. package/dist/client.d.mts +30 -0
  18. package/dist/client.d.mts.map +1 -0
  19. package/dist/client.mjs +186 -0
  20. package/dist/client.mjs.map +1 -0
  21. package/dist/config-DPS6bSYo.d.mts +34 -0
  22. package/dist/config-DPS6bSYo.d.mts.map +1 -0
  23. package/dist/config-P6P5adJg.mjs +287 -0
  24. package/dist/config-P6P5adJg.mjs.map +1 -0
  25. package/dist/console-8bND3mMU.mjs +128 -0
  26. package/dist/console-8bND3mMU.mjs.map +1 -0
  27. package/dist/ecommerce-Cgu4wlux.mjs +993 -0
  28. package/dist/ecommerce-Cgu4wlux.mjs.map +1 -0
  29. package/dist/emitters-6-nKo8i-.mjs +208 -0
  30. package/dist/emitters-6-nKo8i-.mjs.map +1 -0
  31. package/dist/emitters-DldkVSPp.d.mts +12 -0
  32. package/dist/emitters-DldkVSPp.d.mts.map +1 -0
  33. package/dist/index-BfNWgfa5.d.mts +1494 -0
  34. package/dist/index-BfNWgfa5.d.mts.map +1 -0
  35. package/dist/index-BkIWe--N.d.mts +953 -0
  36. package/dist/index-BkIWe--N.d.mts.map +1 -0
  37. package/dist/index-jPzXRn52.d.mts +184 -0
  38. package/dist/index-jPzXRn52.d.mts.map +1 -0
  39. package/dist/manager-DvRRjza6.d.mts +76 -0
  40. package/dist/manager-DvRRjza6.d.mts.map +1 -0
  41. package/dist/posthog-bootstrap-CYfIy_WS.mjs +1769 -0
  42. package/dist/posthog-bootstrap-CYfIy_WS.mjs.map +1 -0
  43. package/dist/posthog-bootstrap-DWxFrxlt.d.mts +81 -0
  44. package/dist/posthog-bootstrap-DWxFrxlt.d.mts.map +1 -0
  45. package/dist/providers-http-client.d.mts +37 -0
  46. package/dist/providers-http-client.d.mts.map +1 -0
  47. package/dist/providers-http-client.mjs +320 -0
  48. package/dist/providers-http-client.mjs.map +1 -0
  49. package/dist/providers-http-server.d.mts +31 -0
  50. package/dist/providers-http-server.d.mts.map +1 -0
  51. package/dist/providers-http-server.mjs +297 -0
  52. package/dist/providers-http-server.mjs.map +1 -0
  53. package/dist/providers-http.d.mts +46 -0
  54. package/dist/providers-http.d.mts.map +1 -0
  55. package/dist/providers-http.mjs +4 -0
  56. package/dist/server-edge.d.mts +9 -0
  57. package/dist/server-edge.d.mts.map +1 -0
  58. package/dist/server-edge.mjs +373 -0
  59. package/dist/server-edge.mjs.map +1 -0
  60. package/dist/server-next.d.mts +67 -0
  61. package/dist/server-next.d.mts.map +1 -0
  62. package/dist/server-next.mjs +193 -0
  63. package/dist/server-next.mjs.map +1 -0
  64. package/dist/server.d.mts +10 -0
  65. package/dist/server.mjs +7 -0
  66. package/dist/service-cYtBBL8x.mjs +945 -0
  67. package/dist/service-cYtBBL8x.mjs.map +1 -0
  68. package/dist/shared.d.mts +16 -0
  69. package/dist/shared.d.mts.map +1 -0
  70. package/dist/shared.mjs +93 -0
  71. package/dist/shared.mjs.map +1 -0
  72. package/dist/types-BxBnNQ0V.d.mts +354 -0
  73. package/dist/types-BxBnNQ0V.d.mts.map +1 -0
  74. package/dist/types-CBvxUEaF.d.mts +216 -0
  75. package/dist/types-CBvxUEaF.d.mts.map +1 -0
  76. package/dist/types.d.mts +4 -0
  77. package/dist/types.mjs +0 -0
  78. package/dist/vercel-types-lwakUfoI.d.mts +102 -0
  79. package/dist/vercel-types-lwakUfoI.d.mts.map +1 -0
  80. package/package.json +129 -0
  81. package/src/client/index.ts +164 -0
  82. package/src/client/manager.ts +71 -0
  83. package/src/client/next/components.tsx +270 -0
  84. package/src/client/next/hooks.ts +217 -0
  85. package/src/client/next/manager.ts +141 -0
  86. package/src/client/next.ts +144 -0
  87. package/src/client-next.ts +101 -0
  88. package/src/client.ts +89 -0
  89. package/src/examples/ai-sdk-patterns.ts +583 -0
  90. package/src/examples/emitter-patterns.ts +476 -0
  91. package/src/examples/nextjs-emitter-patterns.tsx +403 -0
  92. package/src/next/app-router.tsx +564 -0
  93. package/src/next/client.ts +419 -0
  94. package/src/next/index.ts +84 -0
  95. package/src/next/middleware.ts +429 -0
  96. package/src/next/rsc.tsx +300 -0
  97. package/src/next/server.ts +253 -0
  98. package/src/next/types.d.ts +220 -0
  99. package/src/providers/base-provider.ts +419 -0
  100. package/src/providers/console/client.ts +10 -0
  101. package/src/providers/console/index.ts +152 -0
  102. package/src/providers/console/server.ts +6 -0
  103. package/src/providers/console/types.ts +15 -0
  104. package/src/providers/http/client.ts +464 -0
  105. package/src/providers/http/index.ts +30 -0
  106. package/src/providers/http/server.ts +396 -0
  107. package/src/providers/http/types.ts +135 -0
  108. package/src/providers/posthog/client.ts +518 -0
  109. package/src/providers/posthog/index.ts +11 -0
  110. package/src/providers/posthog/server.ts +329 -0
  111. package/src/providers/posthog/types.ts +104 -0
  112. package/src/providers/segment/client.ts +113 -0
  113. package/src/providers/segment/index.ts +11 -0
  114. package/src/providers/segment/server.ts +115 -0
  115. package/src/providers/segment/types.ts +51 -0
  116. package/src/providers/vercel/client.ts +102 -0
  117. package/src/providers/vercel/index.ts +11 -0
  118. package/src/providers/vercel/server.ts +89 -0
  119. package/src/providers/vercel/types.ts +27 -0
  120. package/src/server/index.ts +103 -0
  121. package/src/server/manager.ts +62 -0
  122. package/src/server/next.ts +210 -0
  123. package/src/server-edge.ts +442 -0
  124. package/src/server-next.ts +39 -0
  125. package/src/server.ts +106 -0
  126. package/src/shared/emitters/ai/README.md +981 -0
  127. package/src/shared/emitters/ai/events/agent.ts +130 -0
  128. package/src/shared/emitters/ai/events/artifacts.ts +167 -0
  129. package/src/shared/emitters/ai/events/chat.ts +126 -0
  130. package/src/shared/emitters/ai/events/chatbot-ecommerce.ts +133 -0
  131. package/src/shared/emitters/ai/events/completion.ts +103 -0
  132. package/src/shared/emitters/ai/events/content-generation.ts +347 -0
  133. package/src/shared/emitters/ai/events/conversation.ts +332 -0
  134. package/src/shared/emitters/ai/events/product-features.ts +1402 -0
  135. package/src/shared/emitters/ai/events/streaming.ts +114 -0
  136. package/src/shared/emitters/ai/events/tool.ts +93 -0
  137. package/src/shared/emitters/ai/index.ts +69 -0
  138. package/src/shared/emitters/ai/track-ai-sdk.ts +74 -0
  139. package/src/shared/emitters/ai/track-ai.ts +50 -0
  140. package/src/shared/emitters/ai/types.ts +1041 -0
  141. package/src/shared/emitters/ai/utils.ts +468 -0
  142. package/src/shared/emitters/ecommerce/events/cart-checkout.ts +106 -0
  143. package/src/shared/emitters/ecommerce/events/coupon.ts +49 -0
  144. package/src/shared/emitters/ecommerce/events/engagement.ts +61 -0
  145. package/src/shared/emitters/ecommerce/events/marketplace.ts +119 -0
  146. package/src/shared/emitters/ecommerce/events/order.ts +199 -0
  147. package/src/shared/emitters/ecommerce/events/product.ts +205 -0
  148. package/src/shared/emitters/ecommerce/events/registry.ts +123 -0
  149. package/src/shared/emitters/ecommerce/events/wishlist-sharing.ts +140 -0
  150. package/src/shared/emitters/ecommerce/index.ts +46 -0
  151. package/src/shared/emitters/ecommerce/track-ecommerce.ts +53 -0
  152. package/src/shared/emitters/ecommerce/types.ts +314 -0
  153. package/src/shared/emitters/ecommerce/utils.ts +216 -0
  154. package/src/shared/emitters/emitter-types.ts +974 -0
  155. package/src/shared/emitters/emitters.ts +292 -0
  156. package/src/shared/emitters/helpers.ts +419 -0
  157. package/src/shared/emitters/index.ts +66 -0
  158. package/src/shared/index.ts +142 -0
  159. package/src/shared/ingestion/index.ts +66 -0
  160. package/src/shared/ingestion/schemas.ts +386 -0
  161. package/src/shared/ingestion/service.ts +628 -0
  162. package/src/shared/node22-features.ts +848 -0
  163. package/src/shared/providers/console-provider.ts +160 -0
  164. package/src/shared/types/base-types.ts +54 -0
  165. package/src/shared/types/console-types.ts +19 -0
  166. package/src/shared/types/posthog-types.ts +131 -0
  167. package/src/shared/types/segment-types.ts +15 -0
  168. package/src/shared/types/types.ts +397 -0
  169. package/src/shared/types/vercel-types.ts +19 -0
  170. package/src/shared/utils/config-client.ts +19 -0
  171. package/src/shared/utils/config.ts +250 -0
  172. package/src/shared/utils/emitter-adapter.ts +212 -0
  173. package/src/shared/utils/manager.test.ts +36 -0
  174. package/src/shared/utils/manager.ts +1322 -0
  175. package/src/shared/utils/posthog-bootstrap.ts +136 -0
  176. package/src/shared/utils/posthog-client-utils.ts +48 -0
  177. package/src/shared/utils/posthog-next-utils.ts +282 -0
  178. package/src/shared/utils/posthog-server-utils.ts +210 -0
  179. package/src/shared/utils/rate-limit.ts +289 -0
  180. package/src/shared/utils/security.ts +545 -0
  181. package/src/shared/utils/validation-client.ts +161 -0
  182. package/src/shared/utils/validation.ts +399 -0
  183. package/src/shared.ts +155 -0
  184. package/src/types/index.ts +62 -0
@@ -0,0 +1,136 @@
1
+ /**
2
+ * @fileoverview PostHog bootstrap utilities for Next.js server-side rendering
3
+ * PostHog bootstrap utilities for Next.js server-side rendering
4
+ * Enables consistent feature flag delivery from server to client
5
+ */
6
+
7
+ import type { BootstrapData, PostHogCookie } from '../types/posthog-types';
8
+
9
+ /**
10
+ * Generate a unique distinct ID (compatible with PostHog format)
11
+ */
12
+ export function generateDistinctId(): string {
13
+ // Use a similar format to PostHog's default distinct ID generation
14
+ const timestamp = Date.now().toString(36);
15
+ const randomStr = Math.random().toString(36).slice(2, 15);
16
+ return `${timestamp}-${randomStr}`;
17
+ }
18
+
19
+ /**
20
+ * Parse PostHog cookie to extract distinct ID
21
+ */
22
+ export function parsePostHogCookie(
23
+ cookieValue: string,
24
+ _projectApiKey: string,
25
+ ): PostHogCookie | null {
26
+ try {
27
+ const parsed = JSON.parse(cookieValue);
28
+
29
+ // Validate the cookie structure
30
+ if (parsed && typeof parsed.distinct_id === 'string') {
31
+ return parsed as PostHogCookie;
32
+ }
33
+
34
+ return null;
35
+ } catch {
36
+ // Silently fail - cookies may be malformed
37
+ return null;
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Get PostHog cookie name for a project
43
+ */
44
+ export function getPostHogCookieName(projectApiKey: string): string {
45
+ return `ph_${projectApiKey}_posthog`;
46
+ }
47
+
48
+ /**
49
+ * Extract distinct ID from cookies (Next.js compatible)
50
+ */
51
+ export function getDistinctIdFromCookies(
52
+ cookies: any, // Next.js cookies object or cookie string
53
+ projectApiKey: string,
54
+ ): string | null {
55
+ try {
56
+ const cookieName = getPostHogCookieName(projectApiKey);
57
+
58
+ // Handle Next.js cookies() object
59
+ if (cookies && typeof cookies.get === 'function') {
60
+ const cookie = cookies.get(cookieName);
61
+ if (cookie?.value) {
62
+ const parsed = parsePostHogCookie(cookie.value, projectApiKey);
63
+ return parsed?.distinct_id ?? null;
64
+ }
65
+ }
66
+
67
+ // Handle raw cookie string
68
+ if (typeof cookies === 'string') {
69
+ const cookieMatch = cookies.match(new RegExp(`${cookieName}=([^;]+)`));
70
+ if (cookieMatch?.[1]) {
71
+ const parsed = parsePostHogCookie(decodeURIComponent(cookieMatch[1]), projectApiKey);
72
+ return parsed?.distinct_id ?? null;
73
+ }
74
+ }
75
+
76
+ return null;
77
+ } catch {
78
+ // Silently fail - cookies may be malformed or missing
79
+ return null;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Create bootstrap data for PostHog client initialization
85
+ */
86
+ export function createBootstrapData(distinctId: string): BootstrapData {
87
+ return {
88
+ distinctID: distinctId,
89
+ };
90
+ }
91
+
92
+ /**
93
+ * Cached bootstrap data fetcher (React cache compatible)
94
+ */
95
+ const bootstrapCache = new Map<string, { data: BootstrapData; timestamp: number }>();
96
+ const CACHE_TTL = 30000; // 30 seconds
97
+
98
+ export function getCachedBootstrapData(distinctId: string): BootstrapData | null {
99
+ const cached = bootstrapCache.get(distinctId);
100
+
101
+ if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
102
+ return cached.data;
103
+ }
104
+
105
+ // Clean up expired entries
106
+ for (const [key, value] of bootstrapCache.entries()) {
107
+ if (Date.now() - value.timestamp >= CACHE_TTL) {
108
+ bootstrapCache.delete(key);
109
+ }
110
+ }
111
+
112
+ return null;
113
+ }
114
+
115
+ export function setCachedBootstrapData(distinctId: string, data: BootstrapData): void {
116
+ bootstrapCache.set(distinctId, {
117
+ data,
118
+ timestamp: Date.now(),
119
+ });
120
+ }
121
+
122
+ /**
123
+ * Validate bootstrap data structure
124
+ */
125
+ export function validateBootstrapData(data: any): data is BootstrapData {
126
+ return data && typeof data === 'object' && typeof data.distinctID === 'string';
127
+ }
128
+
129
+ /**
130
+ * Create minimal bootstrap data when full data is unavailable
131
+ */
132
+ export function createMinimalBootstrapData(distinctId?: string): BootstrapData {
133
+ return {
134
+ distinctID: distinctId ?? generateDistinctId(),
135
+ };
136
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * @fileoverview Client-safe PostHog utilities for Next.js
3
+ * Client-safe PostHog utilities for Next.js
4
+ * These utilities can be safely imported in client-side code
5
+ */
6
+
7
+ import { type BootstrapData } from '../types/posthog-types';
8
+
9
+ /**
10
+ * PostHog provider configuration helper for Next.js
11
+ * This is safe to use in client-side code
12
+ */
13
+ function createPostHogConfig(
14
+ apiKey: string,
15
+ options?: {
16
+ autocapture?: boolean;
17
+ bootstrap?: BootstrapData;
18
+ capture_pageview?: boolean;
19
+ debug?: boolean;
20
+ host?: string;
21
+ session_recording?: boolean;
22
+ },
23
+ ) {
24
+ return {
25
+ apiKey,
26
+ options: {
27
+ api_host: options?.host ?? 'https://app.posthog.com',
28
+ autocapture: options?.autocapture ?? true,
29
+ bootstrap: options?.bootstrap,
30
+ capture_pageview: options?.capture_pageview ?? false, // We handle manually
31
+ disable_session_recording: !(options?.session_recording ?? true),
32
+ loaded: options?.debug
33
+ ? (posthog: any) => {
34
+ // Only enable debug in development
35
+ if (options.debug) {
36
+ posthog.debug();
37
+ }
38
+ }
39
+ : undefined,
40
+ ...options,
41
+ },
42
+ };
43
+ }
44
+
45
+ /**
46
+ * Re-export client-safe utilities from posthog-bootstrap
47
+ */
48
+ export { createPostHogConfig };
@@ -0,0 +1,282 @@
1
+ /**
2
+ * @fileoverview Next.js specific utilities for PostHog integration
3
+ * Next.js specific utilities for PostHog integration
4
+ * Provides seamless server-side rendering and client hydration
5
+ */
6
+
7
+ import {
8
+ createBootstrapData,
9
+ createMinimalBootstrapData,
10
+ generateDistinctId,
11
+ getCachedBootstrapData,
12
+ getDistinctIdFromCookies,
13
+ setCachedBootstrapData,
14
+ } from './posthog-bootstrap';
15
+
16
+ // Dynamic import of React cache for Next.js compatibility
17
+ import type { BootstrapData } from '../types/posthog-types';
18
+
19
+ type CacheFunction = <T extends (...args: any[]) => any>(fn: T) => T;
20
+
21
+ let cache: CacheFunction;
22
+ try {
23
+ // eslint-disable-next-line @typescript-eslint/no-require-imports, prefer-destructuring
24
+ cache = require('react').cache;
25
+ } catch {
26
+ // Fallback for non-React environments
27
+ cache = <T extends (...args: any[]) => any>(fn: T): T => fn;
28
+ }
29
+
30
+ /**
31
+ * Create a PostHog client for server-side operations
32
+ */
33
+ async function createPostHogServerClient(
34
+ apiKey: string,
35
+ options?: {
36
+ host?: string | undefined;
37
+ timeout?: number | undefined;
38
+ flushAt?: number | undefined;
39
+ flushInterval?: number | undefined;
40
+ },
41
+ ) {
42
+ if (typeof window !== 'undefined') {
43
+ throw new TypeError('createPostHogServerClient should only be called on the server');
44
+ }
45
+
46
+ try {
47
+ const { PostHog } = await import('posthog-node');
48
+
49
+ return new PostHog(apiKey, {
50
+ flushAt: options?.flushAt ?? 1,
51
+ flushInterval: options?.flushInterval ?? 0,
52
+ host: options?.host ?? 'https://app.posthog.com',
53
+ ...(options?.timeout !== undefined && { timeout: options.timeout }),
54
+ });
55
+ } catch {
56
+ throw new Error('PostHog Node.js SDK not available. Install with: npm install posthog-node');
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Get or generate distinct ID for server-side operations
62
+ * Uses React cache to ensure consistent ID across server renders
63
+ */
64
+ const getOrGenerateDistinctId = cache(
65
+ async (
66
+ cookies: { get: (name: string) => { value: string } | undefined },
67
+ apiKey: string,
68
+ ): Promise<string> => {
69
+ // Try to get from cookies first
70
+ const existingId = getDistinctIdFromCookies(cookies, apiKey);
71
+ if (existingId) {
72
+ return existingId;
73
+ }
74
+
75
+ // Generate new ID
76
+ return generateDistinctId();
77
+ },
78
+ );
79
+
80
+ /**
81
+ * Fetch PostHog bootstrap data on the server
82
+ * Uses React cache to prevent duplicate requests
83
+ */
84
+ const getPostHogBootstrapData = cache(
85
+ async (
86
+ apiKey: string,
87
+ distinctId: string,
88
+ options?: {
89
+ host?: string | undefined;
90
+ timeout?: number | undefined;
91
+ },
92
+ ): Promise<BootstrapData> => {
93
+ // Check cache first
94
+ const cached = getCachedBootstrapData(distinctId);
95
+ if (cached) {
96
+ return cached;
97
+ }
98
+
99
+ try {
100
+ const client = await createPostHogServerClient(apiKey, {
101
+ host: options?.host,
102
+ // Add timeout for server-side requests
103
+ timeout: options?.timeout ?? 5000,
104
+ });
105
+
106
+ // Cleanup
107
+ await client.shutdown();
108
+
109
+ const bootstrapData = createBootstrapData(distinctId);
110
+
111
+ // Cache the result
112
+ setCachedBootstrapData(distinctId, bootstrapData);
113
+
114
+ return bootstrapData;
115
+ } catch {
116
+ // Return minimal data on error
117
+ const minimalData = createMinimalBootstrapData(distinctId);
118
+ setCachedBootstrapData(distinctId, minimalData);
119
+ return minimalData;
120
+ }
121
+ },
122
+ );
123
+
124
+ /**
125
+ * Complete bootstrap data fetcher for Next.js apps
126
+ * Handles cookies, distinct ID generation, and flag fetching
127
+ */
128
+ const getCompleteBootstrapData = cache(
129
+ async (
130
+ cookies: { get: (name: string) => { value: string } | undefined },
131
+ apiKey: string,
132
+ options?: {
133
+ host?: string | undefined;
134
+ timeout?: number | undefined;
135
+ fallbackToGenerated?: boolean | undefined;
136
+ },
137
+ ): Promise<BootstrapData> => {
138
+ try {
139
+ // Get or generate distinct ID
140
+ const distinctId = await getOrGenerateDistinctId(cookies, apiKey);
141
+
142
+ // Fetch bootstrap data
143
+ return await getPostHogBootstrapData(apiKey, distinctId, options);
144
+ } catch (error) {
145
+ if (options?.fallbackToGenerated !== false) {
146
+ // Fallback to generated ID with empty flags
147
+ const fallbackId = generateDistinctId();
148
+ return createMinimalBootstrapData(fallbackId);
149
+ }
150
+
151
+ throw error;
152
+ }
153
+ },
154
+ );
155
+
156
+ /**
157
+ * React Suspense-compatible PostHog bootstrap fetcher
158
+ */
159
+ function createPostHogSuspenseData(
160
+ cookies: { get: (name: string) => { value: string } | undefined },
161
+ apiKey: string,
162
+ options?: {
163
+ host?: string | undefined;
164
+ timeout?: number | undefined;
165
+ },
166
+ ) {
167
+ let status = 'pending';
168
+ let result: BootstrapData;
169
+ let error: unknown;
170
+
171
+ const suspender = (async () => {
172
+ try {
173
+ const data = await getCompleteBootstrapData(cookies, apiKey, options);
174
+ status = 'fulfilled';
175
+ result = data;
176
+ return data;
177
+ } catch (err) {
178
+ status = 'rejected';
179
+ error = err;
180
+ throw err;
181
+ }
182
+ })();
183
+
184
+ return {
185
+ read() {
186
+ if (status === 'pending') {
187
+ throw suspender;
188
+ } else if (status === 'rejected') {
189
+ throw error;
190
+ }
191
+ return result;
192
+ },
193
+ };
194
+ }
195
+
196
+ /**
197
+ * PostHog provider configuration helper for Next.js
198
+ */
199
+ function createPostHogConfig(
200
+ apiKey: string,
201
+ options?: {
202
+ host?: string | undefined;
203
+ autocapture?: boolean | undefined;
204
+ capture_pageview?: boolean | undefined;
205
+ session_recording?: boolean | undefined;
206
+ bootstrap?: BootstrapData | undefined;
207
+ debug?: boolean | undefined;
208
+ },
209
+ ) {
210
+ return {
211
+ apiKey,
212
+ options: {
213
+ api_host: options?.host ?? 'https://app.posthog.com',
214
+ autocapture: options?.autocapture ?? true,
215
+ bootstrap: options?.bootstrap,
216
+ capture_pageview: options?.capture_pageview ?? false, // We handle manually
217
+ disable_session_recording: !(options?.session_recording ?? true),
218
+ loaded: options?.debug
219
+ ? (posthog: { debug: () => void }) => {
220
+ // Only enable debug in development
221
+ if (options.debug) {
222
+ posthog.debug();
223
+ }
224
+ }
225
+ : undefined,
226
+ ...options,
227
+ },
228
+ };
229
+ }
230
+
231
+ /**
232
+ * Middleware helper for PostHog tracking
233
+ */
234
+ function _createPostHogMiddleware(apiKey: string) {
235
+ return async (
236
+ request: {
237
+ headers?: {
238
+ get?: (name: string) => string | null;
239
+ [key: string]: string | undefined | ((name: string) => string | null);
240
+ };
241
+ ip?: string;
242
+ cookies?: { get: (name: string) => { value: string } | undefined };
243
+ },
244
+ _response?: unknown,
245
+ ) => {
246
+ try {
247
+ // Extract user info from request
248
+ const userAgent = request.headers?.get?.('user-agent') ?? request.headers?.['user-agent'];
249
+ const referer = request.headers?.get?.('referer') ?? request.headers?.['referer'];
250
+ const ip = request.ip ?? request.headers?.get?.('x-forwarded-for') ?? 'unknown';
251
+
252
+ // Get distinct ID from cookies
253
+ const { cookies } = request;
254
+ const distinctId = getDistinctIdFromCookies(cookies, apiKey) ?? generateDistinctId();
255
+
256
+ // Create tracking context
257
+ return {
258
+ distinctId,
259
+ ip,
260
+ referer,
261
+ timestamp: new Date().toISOString(),
262
+ userAgent,
263
+ };
264
+ } catch {
265
+ return null;
266
+ }
267
+ };
268
+ }
269
+
270
+ /**
271
+ * Export commonly used utilities
272
+ */
273
+ export {
274
+ createPostHogConfig,
275
+ // eslint-disable-next-line no-used-underscore-vars/no-used-underscore-vars
276
+ _createPostHogMiddleware as createPostHogMiddleware,
277
+ createPostHogServerClient,
278
+ createPostHogSuspenseData,
279
+ getCompleteBootstrapData,
280
+ getOrGenerateDistinctId,
281
+ getPostHogBootstrapData,
282
+ };
@@ -0,0 +1,210 @@
1
+ /**
2
+ * @fileoverview Server-only PostHog utilities for Next.js
3
+ * Server-only PostHog utilities for Next.js
4
+ * These utilities should only be imported in server-side code
5
+ */
6
+
7
+ // Dynamic import of React cache for Next.js compatibility
8
+ import { type BootstrapData } from '../types/posthog-types';
9
+
10
+ import {
11
+ createBootstrapData,
12
+ createMinimalBootstrapData,
13
+ generateDistinctId,
14
+ getCachedBootstrapData,
15
+ getDistinctIdFromCookies,
16
+ setCachedBootstrapData,
17
+ } from './posthog-bootstrap';
18
+
19
+ let cache: any;
20
+ try {
21
+ // eslint-disable-next-line @typescript-eslint/no-require-imports, prefer-destructuring
22
+ cache = require('react').cache;
23
+ } catch {
24
+ // Fallback for non-React environments
25
+ cache = <T extends (...args: any[]) => any>(fn: T): T => fn;
26
+ }
27
+
28
+ /**
29
+ * Create a PostHog client for server-side operations
30
+ */
31
+ async function createPostHogServerClient(apiKey: string, options?: any) {
32
+ try {
33
+ const { PostHog } = await import('posthog-node');
34
+
35
+ return new PostHog(apiKey, {
36
+ flushAt: 1,
37
+ flushInterval: 0,
38
+ host: 'https://app.posthog.com',
39
+ ...options,
40
+ });
41
+ } catch {
42
+ throw new Error('PostHog Node.js SDK not available. Install with: npm install posthog-node');
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Get or generate distinct ID for server-side operations
48
+ * Uses React cache to ensure consistent ID across server renders
49
+ */
50
+ const getOrGenerateDistinctId = cache(async (cookies: any, apiKey: string): Promise<string> => {
51
+ // Try to get from cookies first
52
+ const existingId = getDistinctIdFromCookies(cookies, apiKey);
53
+ if (existingId) {
54
+ return existingId;
55
+ }
56
+
57
+ // Generate new ID
58
+ return generateDistinctId();
59
+ });
60
+
61
+ /**
62
+ * Fetch PostHog bootstrap data on the server
63
+ * Uses React cache to prevent duplicate requests
64
+ */
65
+ const getPostHogBootstrapData = cache(
66
+ async (
67
+ apiKey: string,
68
+ distinctId: string,
69
+ options?: {
70
+ host?: string;
71
+ timeout?: number;
72
+ },
73
+ ): Promise<BootstrapData> => {
74
+ // Check cache first
75
+ const cached = getCachedBootstrapData(distinctId);
76
+ if (cached) {
77
+ return cached;
78
+ }
79
+
80
+ try {
81
+ const client = await createPostHogServerClient(apiKey, {
82
+ host: options?.host,
83
+ // Add timeout for server-side requests
84
+ timeout: options?.timeout ?? 5000,
85
+ });
86
+
87
+ // Cleanup
88
+ await client.shutdown();
89
+
90
+ const bootstrapData = createBootstrapData(distinctId);
91
+
92
+ // Cache the result
93
+ setCachedBootstrapData(distinctId, bootstrapData);
94
+
95
+ return bootstrapData;
96
+ } catch {
97
+ // Return minimal data on error
98
+ const minimalData = createMinimalBootstrapData(distinctId);
99
+ setCachedBootstrapData(distinctId, minimalData);
100
+ return minimalData;
101
+ }
102
+ },
103
+ );
104
+
105
+ /**
106
+ * Complete bootstrap data fetcher for Next.js apps
107
+ * Handles cookies, distinct ID generation, and flag fetching
108
+ */
109
+ const getCompleteBootstrapData = cache(
110
+ async (
111
+ cookies: any,
112
+ apiKey: string,
113
+ options?: {
114
+ fallbackToGenerated?: boolean;
115
+ host?: string;
116
+ timeout?: number;
117
+ },
118
+ ): Promise<BootstrapData> => {
119
+ try {
120
+ // Get or generate distinct ID
121
+ const distinctId = await getOrGenerateDistinctId(cookies, apiKey);
122
+
123
+ // Fetch bootstrap data
124
+ return await getPostHogBootstrapData(apiKey, distinctId, options);
125
+ } catch (error: any) {
126
+ if (options?.fallbackToGenerated !== false) {
127
+ // Fallback to generated ID with empty flags
128
+ const fallbackId = generateDistinctId();
129
+ return createMinimalBootstrapData(fallbackId);
130
+ }
131
+
132
+ throw error;
133
+ }
134
+ },
135
+ );
136
+
137
+ // Commented out as currently unused but kept for potential future use
138
+ // /**
139
+ // * Middleware helper for PostHog tracking
140
+ // */
141
+ // function _createPostHogMiddleware(apiKey: string) {
142
+ // return async (request: any, _response?: any) => {
143
+ // try {
144
+ // // Extract user info from request
145
+ // const userAgent = request.headers?.get?.('user-agent') ?? request.headers?.['user-agent'];
146
+ // const referer = request.headers?.get?.('referer') ?? request.headers?.['referer'];
147
+ // const ip = request.ip ?? request.headers?.get?.('x-forwarded-for') ?? 'unknown';
148
+
149
+ // // Get distinct ID from cookies
150
+ // const { cookies } = request;
151
+ // const distinctId = getDistinctIdFromCookies(cookies, apiKey) ?? generateDistinctId();
152
+
153
+ // // Create tracking context
154
+ // return {
155
+ // distinctId,
156
+ // ip,
157
+ // referer,
158
+ // timestamp: new Date().toISOString(),
159
+ // userAgent,
160
+ // };
161
+ // } catch {
162
+ // return null;
163
+ // }
164
+ // };
165
+ // }
166
+
167
+ // /**
168
+ // * React Suspense-compatible PostHog bootstrap fetcher
169
+ // */
170
+ // function _createPostHogSuspenseData(
171
+ // cookies: any,
172
+ // apiKey: string,
173
+ // options?: {
174
+ // host?: string;
175
+ // timeout?: number;
176
+ // },
177
+ // ) {
178
+ // let status = 'pending';
179
+ // let result: BootstrapData;
180
+ // let error: any;
181
+
182
+ // // eslint-disable-next-line promise/prefer-await-to-then
183
+ // const suspender = getCompleteBootstrapData(cookies, apiKey, options).then(
184
+ // // eslint-disable-next-line promise/always-return
185
+ // (data: any) => {
186
+ // status = 'fulfilled';
187
+ // result = data;
188
+ // },
189
+ // (err: any) => {
190
+ // status = 'rejected';
191
+ // error = err;
192
+ // },
193
+ // );
194
+
195
+ // return {
196
+ // read() {
197
+ // if (status === 'pending') {
198
+ // throw suspender;
199
+ // } else if (status === 'rejected') {
200
+ // throw error;
201
+ // }
202
+ // return result;
203
+ // },
204
+ // };
205
+ // }
206
+
207
+ /**
208
+ * Export commonly used utilities (server-safe only)
209
+ */
210
+ export { getCompleteBootstrapData };