@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,71 @@
1
+ /**
2
+ * @fileoverview Client Analytics Manager
3
+ *
4
+ * Client-side analytics manager with static provider registry. Provides
5
+ * factory functions for creating client analytics instances with support
6
+ * for multiple providers (Console, HTTP, Segment, Vercel).
7
+ *
8
+ * **Providers Supported**:
9
+ * - `console`: Console logging provider (development/debugging)
10
+ * - `http`: HTTP provider for remote ingestion (oneapp-api)
11
+ * - `segment`: Segment.io analytics provider
12
+ * - `vercel`: Vercel Analytics provider
13
+ *
14
+ * @module @od-oneapp/analytics/client/manager
15
+ */
16
+
17
+ import { SegmentClientProvider } from '@integrations/segment/analytics-provider/client';
18
+ import { VercelClientProvider } from '@integrations/vercel/analytics-provider/client';
19
+
20
+ import { ConsoleProvider } from '../providers/console/client';
21
+ import { HttpClientProvider } from '../providers/http/client';
22
+ import { createAnalyticsManager } from '../shared/utils/manager';
23
+
24
+ import type { AnalyticsConfig, AnalyticsManager, ProviderRegistry } from '../shared/types/types';
25
+
26
+ // Static provider registry for client environments
27
+ const CLIENT_PROVIDERS: ProviderRegistry = {
28
+ console: config => new ConsoleProvider(config),
29
+ http: config => new HttpClientProvider(config),
30
+ segment: config => new SegmentClientProvider(config),
31
+ vercel: config => new VercelClientProvider(config),
32
+ };
33
+
34
+ /**
35
+ * Create and initialize a client analytics instance
36
+ * This is the primary way to create analytics for client-side applications
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * const analytics = await createClientAnalytics({
41
+ * providers: {
42
+ * http: { endpoint: '/api/v1/ingest' },
43
+ * },
44
+ * });
45
+ * await analytics.track('Button Clicked', { variant: 'primary' });
46
+ * ```
47
+ * @param config - Analytics configuration including providers and settings
48
+ * @returns Promise resolving to initialized analytics manager
49
+ */
50
+ export async function createClientAnalytics(config: AnalyticsConfig): Promise<AnalyticsManager> {
51
+ const manager = createAnalyticsManager(config, CLIENT_PROVIDERS);
52
+ await manager.initialize();
53
+ return manager;
54
+ }
55
+
56
+ /**
57
+ * Create a client analytics instance without initializing
58
+ * Useful when you need to control initialization timing
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * const analytics = createClientAnalyticsUninitialized(config);
63
+ * // later inside a lazy effect
64
+ * await analytics.initialize();
65
+ * ```
66
+ * @param config - Analytics configuration including providers and settings
67
+ * @returns Uninitialized analytics manager instance
68
+ */
69
+ export function createClientAnalyticsUninitialized(config: AnalyticsConfig): AnalyticsManager {
70
+ return createAnalyticsManager(config, CLIENT_PROVIDERS);
71
+ }
@@ -0,0 +1,270 @@
1
+ /**
2
+ * @fileoverview Next.js Client Components for Analytics
3
+ *
4
+ * React components for integrating analytics into Next.js applications.
5
+ * Provides provider components, tracked UI elements, and higher-order
6
+ * components for automatic event tracking.
7
+ *
8
+ * **Components**:
9
+ * - `AnalyticsProvider`: Provider component for App Router
10
+ * - `TrackedButton`: Button component with automatic click tracking
11
+ * - `TrackedLink`: Link component with automatic click tracking
12
+ * - `withViewTracking`: HOC for tracking component views
13
+ *
14
+ * **Note**: This file uses Next.js hooks. For framework-agnostic components,
15
+ * use the base components or accept navigation functions as parameters.
16
+ *
17
+ * @module @od-oneapp/analytics/client/next/components
18
+ */
19
+
20
+ 'use client';
21
+
22
+ import { useCallback, useEffect, useRef } from 'react';
23
+
24
+ import type { Route } from 'next';
25
+
26
+ import { createClientObservability } from '@od-oneapp/observability/client/next';
27
+ import { useRouter } from 'next/navigation';
28
+
29
+ import { usePageTracking, useTrackEvent } from './hooks';
30
+ import { createNextJSClientAnalytics } from './manager';
31
+
32
+ import type { AnalyticsConfig, AnalyticsManager, Properties } from '../../shared/types/types';
33
+ import type { ObservabilityClient } from '@od-oneapp/observability/client/next';
34
+
35
+ // Global analytics instance
36
+ let globalAnalytics: AnalyticsManager | null = null;
37
+
38
+ // Global logger instance (from @od-oneapp/observability)
39
+ // Using minimal interface for optional fallback error logging
40
+
41
+ let logger: ObservabilityClient | null = null;
42
+
43
+ /**
44
+ * Analytics provider component for App Router.
45
+ *
46
+ * @remarks
47
+ * Wraps your application and provides analytics context to all child components.
48
+ * Automatically initializes analytics and optionally tracks page views.
49
+ *
50
+ * @param props - Component props
51
+ * @param props.children - React children to wrap
52
+ * @param props.config - Analytics configuration
53
+ * @param props.autoPageTracking - Whether to automatically track page views (default: true)
54
+ * @param props.pageTrackingOptions - Options for page tracking
55
+ *
56
+ * @example
57
+ * ```tsx
58
+ * export default function RootLayout({ children }) {
59
+ * return (
60
+ * <AnalyticsProvider
61
+ * config={{
62
+ * providers: {
63
+ * http: { options: { endpoint: process.env.NEXT_PUBLIC_ANALYTICS_ENDPOINT! } }
64
+ * }
65
+ * }}
66
+ * autoPageTracking={true}
67
+ * >
68
+ * {children}
69
+ * </AnalyticsProvider>
70
+ * );
71
+ * }
72
+ * ```
73
+ */
74
+ export function AnalyticsProvider({
75
+ autoPageTracking = true,
76
+ children,
77
+ config,
78
+ pageTrackingOptions,
79
+ }: {
80
+ children: React.ReactNode;
81
+ config: AnalyticsConfig;
82
+ autoPageTracking?: boolean;
83
+ pageTrackingOptions?: Parameters<typeof usePageTracking>[0];
84
+ }) {
85
+ // Initialize analytics
86
+ useEffect(() => {
87
+ if (!globalAnalytics) {
88
+ const initAnalytics = async () => {
89
+ try {
90
+ // Initialize logger if not already initialized
91
+ logger ??= await createClientObservability();
92
+
93
+ const instance = await createNextJSClientAnalytics(config);
94
+ globalAnalytics = instance;
95
+ } catch (error) {
96
+ if (logger) {
97
+ logger.captureException(error, {
98
+ message: 'Failed to initialize analytics',
99
+ });
100
+ }
101
+ }
102
+ };
103
+ void initAnalytics();
104
+ }
105
+ }, [config]);
106
+
107
+ // Auto page tracking
108
+ usePageTracking(autoPageTracking ? pageTrackingOptions : { skip: true });
109
+
110
+ return children as React.ReactElement;
111
+ }
112
+
113
+ /**
114
+ * Button component with automatic click tracking.
115
+ *
116
+ * @remarks
117
+ * Wraps a standard button element and automatically tracks click events
118
+ * when the button is clicked.
119
+ *
120
+ * @param props - Button props plus tracking props
121
+ * @param props.eventName - Name of the event to track
122
+ * @param props.properties - Additional properties to include in the event
123
+ * @param props.onClick - Optional click handler (tracking happens before this)
124
+ *
125
+ * @example
126
+ * ```tsx
127
+ * <TrackedButton
128
+ * eventName="Sign Up Clicked"
129
+ * properties={{ location: 'header', variant: 'primary' }}
130
+ * onClick={() => router.push('/signup')}
131
+ * >
132
+ * Sign Up
133
+ * </TrackedButton>
134
+ * ```
135
+ */
136
+ export function TrackedButton({
137
+ children,
138
+ eventName,
139
+ onClick,
140
+ properties,
141
+ ...props
142
+ }: React.ButtonHTMLAttributes<HTMLButtonElement> & {
143
+ eventName: string;
144
+ properties?: Properties;
145
+ }) {
146
+ const track = useTrackEvent();
147
+
148
+ const handleClick = useCallback(
149
+ (e: React.MouseEvent<HTMLButtonElement>) => {
150
+ track(eventName, properties);
151
+ onClick?.(e);
152
+ },
153
+ [track, eventName, properties, onClick],
154
+ );
155
+
156
+ return (
157
+ <button type="button" {...props} onClick={handleClick}>
158
+ {children}
159
+ </button>
160
+ );
161
+ }
162
+
163
+ /**
164
+ * Link component with automatic click tracking.
165
+ *
166
+ * @remarks
167
+ * Wraps a standard anchor element and automatically tracks click events
168
+ * when the link is clicked. Also handles internal navigation for Next.js.
169
+ *
170
+ * @param props - Anchor props plus tracking props
171
+ * @param props.eventName - Name of the event to track
172
+ * @param props.properties - Additional properties to include in the event
173
+ * @param props.href - Link URL
174
+ * @param props.onClick - Optional click handler (tracking happens before this)
175
+ *
176
+ * @example
177
+ * ```tsx
178
+ * <TrackedLink
179
+ * href="/products"
180
+ * eventName="Product Link Clicked"
181
+ * properties={{ linkText: 'View Products' }}
182
+ * >
183
+ * View Products
184
+ * </TrackedLink>
185
+ * ```
186
+ */
187
+ export function TrackedLink({
188
+ children,
189
+ eventName,
190
+ href,
191
+ onClick,
192
+ properties,
193
+ ...props
194
+ }: React.AnchorHTMLAttributes<HTMLAnchorElement> & {
195
+ eventName: string;
196
+ properties?: Properties;
197
+ }) {
198
+ const track = useTrackEvent();
199
+ const router = useRouter();
200
+
201
+ const handleClick = useCallback(
202
+ (e: React.MouseEvent<HTMLAnchorElement>) => {
203
+ // Track the event
204
+ const enhancedProperties = {
205
+ ...properties,
206
+ href,
207
+ link_text: typeof children === 'string' ? children : undefined,
208
+ };
209
+ track(eventName, enhancedProperties);
210
+
211
+ // Handle navigation
212
+ if (onClick) {
213
+ onClick(e);
214
+ } else if (href && !props.target && href.startsWith('/')) {
215
+ // Internal navigation
216
+ e.preventDefault();
217
+ router.push(href as Route);
218
+ }
219
+ },
220
+ [track, eventName, properties, href, onClick, router, children, props.target],
221
+ );
222
+
223
+ return (
224
+ <a {...props} href={href} onClick={handleClick}>
225
+ {children}
226
+ </a>
227
+ );
228
+ }
229
+
230
+ /**
231
+ * Higher-order component for tracking component views.
232
+ *
233
+ * @remarks
234
+ * Wraps a component and automatically tracks a view event when the component
235
+ * is first rendered. Useful for tracking page sections or feature usage.
236
+ *
237
+ * @param Component - The component to wrap
238
+ * @param eventName - Name of the event to track
239
+ * @param getProperties - Optional function to extract properties from component props
240
+ * @returns Wrapped component with automatic view tracking
241
+ *
242
+ * @example
243
+ * ```tsx
244
+ * const TrackedDashboard = withViewTracking(
245
+ * Dashboard,
246
+ * 'Dashboard Viewed',
247
+ * (props) => ({ userId: props.userId, plan: props.plan })
248
+ * );
249
+ * ```
250
+ */
251
+ export function withViewTracking<P extends object>(
252
+ Component: React.ComponentType<P>,
253
+ eventName: string,
254
+ getProperties?: (props: P) => Properties,
255
+ ) {
256
+ return function TrackedComponent(props: P) {
257
+ const track = useTrackEvent();
258
+ const tracked = useRef(false);
259
+
260
+ useEffect(() => {
261
+ if (!tracked.current) {
262
+ tracked.current = true;
263
+ const properties = getProperties ? getProperties(props) : {};
264
+ track(eventName, properties);
265
+ }
266
+ }, [track, props]);
267
+
268
+ return <Component {...props} />;
269
+ };
270
+ }
@@ -0,0 +1,217 @@
1
+ /**
2
+ * @fileoverview Next.js Client Hooks for Analytics
3
+ *
4
+ * React hooks for integrating analytics into Next.js applications.
5
+ * Provides hooks for accessing analytics instances, tracking events,
6
+ * and automatic page view tracking.
7
+ *
8
+ * **Hooks**:
9
+ * - `useAnalytics()`: Get or create analytics instance
10
+ * - `usePageTracking()`: Automatic page view tracking in App Router
11
+ * - `useTrackEvent()`: Hook for tracking events
12
+ * - `useIdentifyUser()`: Hook for user identification
13
+ * - `resetAnalytics()`: Utility to reset analytics (useful for testing)
14
+ *
15
+ * **Note**: These hooks use Next.js navigation hooks. For framework-agnostic hooks,
16
+ * use the base hooks from './hooks' or './manager'.
17
+ *
18
+ * @module @od-oneapp/analytics/client/next/hooks
19
+ */
20
+
21
+ 'use client';
22
+
23
+ // Note: These are Next.js hooks required for App Router integration
24
+ // Client components are already Next.js-specific, so this is acceptable
25
+ import { useCallback, useEffect, useRef, useState } from 'react';
26
+
27
+ import { useParams, usePathname, useSearchParams } from 'next/navigation';
28
+ // Type-only imports for Next.js types (compile-time only, no runtime dependency)
29
+
30
+ import { createNextJSClientAnalytics } from './manager';
31
+
32
+ import type { AnalyticsConfig, AnalyticsManager, TrackingOptions } from '../../shared/types/types';
33
+
34
+ // Global analytics instance
35
+ let globalAnalytics: AnalyticsManager | null = null;
36
+
37
+ /**
38
+ * Hook to get or create analytics instance.
39
+ *
40
+ * @remarks
41
+ * Returns the global analytics instance, creating it if it doesn't exist
42
+ * and config is provided. Returns null if analytics hasn't been initialized.
43
+ *
44
+ * @param config - Optional analytics configuration (only used on first call)
45
+ * @returns Analytics manager instance or null if not initialized
46
+ *
47
+ * @example
48
+ * ```tsx
49
+ * const analytics = useAnalytics(config);
50
+ * useEffect(() => {
51
+ * if (analytics) {
52
+ * analytics.track('ClientReady');
53
+ * }
54
+ * }, [analytics]);
55
+ * ```
56
+ */
57
+ export function useAnalytics(config?: AnalyticsConfig): AnalyticsManager | null {
58
+ const [analytics, setAnalytics] = useState<AnalyticsManager | null>(null);
59
+
60
+ useEffect(() => {
61
+ if (!globalAnalytics && config) {
62
+ const initAnalytics = async () => {
63
+ try {
64
+ // Note: Client-side observability handled by manager
65
+
66
+ const instance = await createNextJSClientAnalytics(config);
67
+ globalAnalytics = instance;
68
+ setAnalytics(instance);
69
+ } catch {
70
+ // Failed to initialize analytics - silently continue
71
+ // TODO: Add proper error logging via observability package
72
+ }
73
+ };
74
+ void initAnalytics();
75
+ } else if (globalAnalytics && analytics !== globalAnalytics) {
76
+ // Use setTimeout to avoid synchronous setState in effect
77
+ setTimeout(() => {
78
+ setAnalytics(globalAnalytics);
79
+ }, 0);
80
+ }
81
+ }, [config]);
82
+
83
+ return analytics;
84
+ }
85
+
86
+ /**
87
+ * Hook for automatic page view tracking in App Router.
88
+ *
89
+ * @remarks
90
+ * Automatically tracks page views when the pathname, search params, or route
91
+ * params change. Prevents duplicate tracking for the same page.
92
+ *
93
+ * @param options - Page tracking options
94
+ * @param options.trackSearch - Whether to include search params in tracking (default: false)
95
+ * @param options.trackParams - Whether to include route params in tracking (default: false)
96
+ * @param options.properties - Additional properties to include in page events
97
+ * @param options.skip - Whether to skip tracking (default: false)
98
+ *
99
+ * @example
100
+ * ```tsx
101
+ * usePageTracking({ trackSearch: true, trackParams: true });
102
+ * ```
103
+ */
104
+ export function usePageTracking(options?: {
105
+ trackSearch?: boolean;
106
+ trackParams?: boolean;
107
+ properties?: Record<string, any>;
108
+ skip?: boolean;
109
+ }) {
110
+ const pathname = usePathname();
111
+ const searchParams = useSearchParams();
112
+ const params = useParams();
113
+ const tracked = useRef<string>('');
114
+
115
+ useEffect(() => {
116
+ if (options?.skip || !globalAnalytics) return;
117
+
118
+ // Create unique key for this page view
119
+ const searchString = options?.trackSearch ? searchParams.toString() : '';
120
+ const pageKey = `${pathname}${searchString}`;
121
+
122
+ // Avoid duplicate tracking
123
+ if (tracked.current === pageKey) return;
124
+ tracked.current = pageKey;
125
+
126
+ // Build properties
127
+ const properties: Record<string, any> = {
128
+ ...options?.properties,
129
+ url: window.location.href,
130
+ path: pathname,
131
+ referrer: document.referrer,
132
+ title: document.title,
133
+ };
134
+
135
+ if (options?.trackSearch) {
136
+ properties.search = searchString;
137
+ properties.search_params = Object.fromEntries(searchParams.entries());
138
+ }
139
+
140
+ if (options?.trackParams && params) {
141
+ properties.route_params = params;
142
+ }
143
+
144
+ // Track page view
145
+ void globalAnalytics.page(pathname, properties);
146
+ }, [
147
+ pathname,
148
+ searchParams,
149
+ params,
150
+ options?.skip,
151
+ options?.trackSearch,
152
+ options?.trackParams,
153
+ options?.properties,
154
+ ]);
155
+ }
156
+
157
+ /**
158
+ * Hook for tracking events.
159
+ *
160
+ * @remarks
161
+ * Returns a callback function for tracking events. The callback is stable
162
+ * across renders and can be safely used in event handlers.
163
+ *
164
+ * @returns Callback function for tracking events
165
+ *
166
+ * @example
167
+ * ```tsx
168
+ * const track = useTrackEvent();
169
+ * track('CTA Clicked', { label: 'Request Demo' });
170
+ * ```
171
+ */
172
+ export function useTrackEvent() {
173
+ return useCallback((event: string, properties?: any, options?: TrackingOptions) => {
174
+ if (!globalAnalytics) return;
175
+ void globalAnalytics.track(event, properties, options);
176
+ }, []);
177
+ }
178
+
179
+ /**
180
+ * Hook for user identification.
181
+ *
182
+ * @remarks
183
+ * Returns a callback function for identifying users. The callback is stable
184
+ * across renders and can be safely used in event handlers.
185
+ *
186
+ * @returns Callback function for identifying users
187
+ *
188
+ * @example
189
+ * ```tsx
190
+ * const identify = useIdentifyUser();
191
+ * identify(user.id, { plan: user.plan });
192
+ * ```
193
+ */
194
+ export function useIdentifyUser() {
195
+ return useCallback((userId: string, traits?: any, options?: TrackingOptions) => {
196
+ if (!globalAnalytics) return;
197
+ void globalAnalytics.identify(userId, traits, options);
198
+ }, []);
199
+ }
200
+
201
+ /**
202
+ * Utility to reset analytics (useful for testing).
203
+ *
204
+ * @remarks
205
+ * Clears the global analytics instance. Useful in test environments to
206
+ * ensure clean state between tests.
207
+ *
208
+ * @example
209
+ * ```typescript
210
+ * beforeEach(() => {
211
+ * resetAnalytics();
212
+ * });
213
+ * ```
214
+ */
215
+ export function resetAnalytics() {
216
+ globalAnalytics = null;
217
+ }
@@ -0,0 +1,141 @@
1
+ /**
2
+ * @fileoverview Next.js Client Analytics Manager
3
+ *
4
+ * Next.js client analytics manager with truly dynamic provider loading.
5
+ * Only imports providers that are actually configured to avoid webpack
6
+ * bundling unused dependencies, reducing bundle size.
7
+ *
8
+ * **Features**:
9
+ * - Dynamic provider loading (only configured providers are bundled)
10
+ * - Code splitting via webpack chunks
11
+ * - Lazy initialization support
12
+ * - Next.js App Router optimized
13
+ *
14
+ * @module @od-oneapp/analytics/client/next/manager
15
+ */
16
+
17
+ // Simple console fallback - observability integration can be added later
18
+ import { createAnalyticsManager } from '../../shared/utils/manager';
19
+
20
+ import type { AnalyticsConfig, AnalyticsManager, ProviderRegistry } from '../../shared/types/types';
21
+ const logWarn = (_message: string, _context?: any) => {
22
+ // No-op to avoid console warnings in production
23
+ // TODO: Add proper error logging via observability package
24
+ };
25
+
26
+ /**
27
+ * Create a Next.js client analytics instance with runtime provider loading
28
+ * Only imports providers that are actually configured to avoid webpack bundling unused dependencies
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const analytics = await createNextJSClientAnalytics({
33
+ * providers: { posthog: { apiKey: process.env.NEXT_PUBLIC_POSTHOG_KEY! } },
34
+ * });
35
+ * await analytics.track('Dashboard Viewed');
36
+ * ```
37
+ */
38
+ export async function createNextJSClientAnalytics(
39
+ config: AnalyticsConfig,
40
+ ): Promise<AnalyticsManager> {
41
+ const CLIENT_PROVIDERS: ProviderRegistry = {};
42
+
43
+ // Get configured providers
44
+ const configuredProviders = Object.keys(config.providers);
45
+
46
+ // Only import configured providers using truly dynamic imports
47
+ const providerPromises = configuredProviders.map(async providerName => {
48
+ switch (providerName) {
49
+ case 'console': {
50
+ const module = await import(
51
+ /* webpackChunkName: "analytics-console" */ '../../providers/console/client'
52
+ );
53
+ CLIENT_PROVIDERS.console = config => new module.ConsoleProvider(config);
54
+ break;
55
+ }
56
+ case 'posthog': {
57
+ const module = await import(
58
+ /* webpackChunkName: "analytics-posthog" */ '../../providers/posthog/client'
59
+ );
60
+ CLIENT_PROVIDERS.posthog = config => new module.PostHogClientProvider(config);
61
+ break;
62
+ }
63
+ case 'segment': {
64
+ const module = await import(
65
+ /* webpackChunkName: "analytics-segment" */ '../../providers/segment/client'
66
+ );
67
+ CLIENT_PROVIDERS.segment = config => new module.SegmentClientProvider(config);
68
+ break;
69
+ }
70
+ case 'vercel': {
71
+ const module = await import(
72
+ /* webpackChunkName: "analytics-vercel" */ '../../providers/vercel/client'
73
+ );
74
+ CLIENT_PROVIDERS.vercel = config => new module.VercelClientProvider(config);
75
+ break;
76
+ }
77
+ default:
78
+ // Skip unknown providers
79
+ logWarn('Unknown analytics provider:', { providerName });
80
+ break;
81
+ }
82
+ });
83
+
84
+ // Wait for all configured providers to load
85
+ await Promise.all(providerPromises);
86
+
87
+ const manager = createAnalyticsManager(config, CLIENT_PROVIDERS);
88
+ await manager.initialize();
89
+ return manager;
90
+ }
91
+
92
+ /**
93
+ * Create a Next.js client analytics instance without initializing
94
+ *
95
+ * @example
96
+ * ```typescript
97
+ * const analytics = await createNextJSClientAnalyticsUninitialized(config);
98
+ * // run setup before first event
99
+ * await analytics.initialize();
100
+ * ```
101
+ */
102
+ export async function createNextJSClientAnalyticsUninitialized(
103
+ config: AnalyticsConfig,
104
+ ): Promise<AnalyticsManager> {
105
+ const CLIENT_PROVIDERS: ProviderRegistry = {};
106
+
107
+ // Get configured providers
108
+ const configuredProviders = Object.keys(config.providers);
109
+
110
+ // Dynamically import only configured providers
111
+ for (const providerName of configuredProviders) {
112
+ switch (providerName) {
113
+ case 'console': {
114
+ const { ConsoleProvider } = await import('../../providers/console/client');
115
+ CLIENT_PROVIDERS.console = config => new ConsoleProvider(config);
116
+ break;
117
+ }
118
+ case 'posthog': {
119
+ const { PostHogClientProvider } = await import('../../providers/posthog/client');
120
+ CLIENT_PROVIDERS.posthog = config => new PostHogClientProvider(config);
121
+ break;
122
+ }
123
+ case 'segment': {
124
+ const { SegmentClientProvider } = await import('../../providers/segment/client');
125
+ CLIENT_PROVIDERS.segment = config => new SegmentClientProvider(config);
126
+ break;
127
+ }
128
+ case 'vercel': {
129
+ const { VercelClientProvider } = await import('../../providers/vercel/client');
130
+ CLIENT_PROVIDERS.vercel = config => new VercelClientProvider(config);
131
+ break;
132
+ }
133
+ default:
134
+ // Skip unknown providers
135
+ logWarn('Unknown analytics provider:', { providerName });
136
+ break;
137
+ }
138
+ }
139
+
140
+ return createAnalyticsManager(config, CLIENT_PROVIDERS);
141
+ }