@od-oneapp/analytics 2026.2.2301-canary → 2026.2.2899-canary
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 +126 -345
- package/catalogs.d.mts +1815 -0
- package/catalogs.d.mts.map +1 -0
- package/catalogs.mjs +437 -0
- package/catalogs.mjs.map +1 -0
- package/{chunk-DQk6qfdC.mjs → client-CMc1iZBB.mjs} +8 -1
- package/{client-DK8twEdp.mjs.map → client-CMc1iZBB.mjs.map} +1 -1
- package/client-next.d.mts +4 -8
- package/client-next.d.mts.map +1 -1
- package/client-next.mjs +134 -944
- package/client-next.mjs.map +1 -1
- package/client.d.mts +6 -25
- package/client.d.mts.map +1 -1
- package/client.mjs +24 -140
- package/client.mjs.map +1 -1
- package/{posthog-bootstrap-DkPdn-hA.mjs → config-BX38D_1T.mjs} +343 -44
- package/config-BX38D_1T.mjs.map +1 -0
- package/{posthog-bootstrap-Bu1BfhVv.d.mts → config-CSo5ZON1.d.mts} +32 -9
- package/config-CSo5ZON1.d.mts.map +1 -0
- package/{console-BpU88FNF.mjs → console-BAFQVsN6.mjs} +2 -4
- package/{console-BpU88FNF.mjs.map → console-BAFQVsN6.mjs.map} +1 -1
- package/{index-DTvdqV7H.d.mts → index-BYVvvAQ8.d.mts} +23 -39
- package/index-BYVvvAQ8.d.mts.map +1 -0
- package/load-provider-L0Smj7SY.d.mts +9 -0
- package/load-provider-L0Smj7SY.d.mts.map +1 -0
- package/load-provider-X2ADGRb5.mjs +112 -0
- package/load-provider-X2ADGRb5.mjs.map +1 -0
- package/{manager-OJpSKwqb.d.mts → manager-B0CnygV0.d.mts} +19 -3
- package/manager-B0CnygV0.d.mts.map +1 -0
- package/package.json +23 -27
- package/providers-http-client.d.mts +1 -1
- package/providers-http-server.d.mts +1 -1
- package/server-next.d.mts +6 -66
- package/server-next.mjs +4 -192
- package/server.d.mts +6 -10
- package/server.mjs +4 -6
- package/{service-NuHnv30x.mjs → service-BNIvrglv.mjs} +27 -270
- package/service-BNIvrglv.mjs.map +1 -0
- package/{types-cMMfHIpi.d.mts → types-C12o4SgZ.d.mts} +2 -87
- package/types-C12o4SgZ.d.mts.map +1 -0
- package/types.d.mts +3 -4
- package/types.mjs +3 -0
- package/validation-DSvytucA.mjs +258 -0
- package/validation-DSvytucA.mjs.map +1 -0
- package/ai-Co8hBoEj.mjs +0 -3347
- package/ai-Co8hBoEj.mjs.map +0 -1
- package/client-B8gfgflr.mjs +0 -267
- package/client-B8gfgflr.mjs.map +0 -1
- package/client-C35AzV71.mjs +0 -296
- package/client-C35AzV71.mjs.map +0 -1
- package/client-DDehaDSz.mjs +0 -54
- package/client-DDehaDSz.mjs.map +0 -1
- package/client-DK8twEdp.mjs +0 -9
- package/config-6Mwe7b2O.mjs +0 -287
- package/config-6Mwe7b2O.mjs.map +0 -1
- package/config-Ciu7O6n1.d.mts +0 -34
- package/config-Ciu7O6n1.d.mts.map +0 -1
- package/ecommerce-DGG1FbiH.mjs +0 -993
- package/ecommerce-DGG1FbiH.mjs.map +0 -1
- package/emitters-BDSsleo_.d.mts +0 -12
- package/emitters-BDSsleo_.d.mts.map +0 -1
- package/emitters-BvEelkxS.mjs +0 -208
- package/emitters-BvEelkxS.mjs.map +0 -1
- package/index-BWhDEs8u.d.mts +0 -184
- package/index-BWhDEs8u.d.mts.map +0 -1
- package/index-Cp-N57Zb.d.mts +0 -953
- package/index-Cp-N57Zb.d.mts.map +0 -1
- package/index-DTvdqV7H.d.mts.map +0 -1
- package/manager-OJpSKwqb.d.mts.map +0 -1
- package/module-DVAU7zKb.mjs +0 -5850
- package/module-DVAU7zKb.mjs.map +0 -1
- package/posthog-bootstrap-Bu1BfhVv.d.mts.map +0 -1
- package/posthog-bootstrap-DkPdn-hA.mjs.map +0 -1
- package/server-edge.d.mts +0 -9
- package/server-edge.d.mts.map +0 -1
- package/server-edge.mjs +0 -373
- package/server-edge.mjs.map +0 -1
- package/server-next.d.mts.map +0 -1
- package/server-next.mjs.map +0 -1
- package/service-NuHnv30x.mjs.map +0 -1
- package/shared.d.mts +0 -16
- package/shared.d.mts.map +0 -1
- package/shared.mjs +0 -93
- package/shared.mjs.map +0 -1
- package/types-DEcTnnFe.d.mts +0 -216
- package/types-DEcTnnFe.d.mts.map +0 -1
- package/types-cMMfHIpi.d.mts.map +0 -1
- package/vercel-types-oM7Sn385.d.mts +0 -102
- package/vercel-types-oM7Sn385.d.mts.map +0 -1
package/README.md
CHANGED
|
@@ -1,91 +1,70 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @od-oneapp/analytics
|
|
2
2
|
|
|
3
|
-
> **Multi-provider analytics
|
|
4
|
-
> analytics**
|
|
3
|
+
> **Multi-provider analytics with Segment.io-style fan-out, type-safe event tracking, and Zod-validated catalogs**
|
|
5
4
|
|
|
6
5
|
[](https://www.typescriptlang.org/)
|
|
7
6
|
[](https://nodejs.org/)
|
|
8
7
|
|
|
9
8
|
## Quick Reference
|
|
10
9
|
|
|
11
|
-
**Status:** Development (Tests passing: 634/655, TypeCheck: ✅)
|
|
12
|
-
|
|
13
10
|
**Requirements:**
|
|
14
11
|
|
|
15
|
-
- Node.js 22.0.0+
|
|
12
|
+
- Node.js 22.0.0+
|
|
16
13
|
- pnpm 10.0.0+
|
|
17
14
|
- Next.js 15.0.0+ (if using Next.js integration)
|
|
18
15
|
|
|
19
16
|
**Exports:**
|
|
20
17
|
|
|
21
|
-
- **
|
|
22
|
-
- **
|
|
18
|
+
- **Client**: `./client`, `./client/next`
|
|
19
|
+
- **Server**: `./server`, `./server/next`
|
|
20
|
+
- **Types**: `./types`
|
|
21
|
+
- **Catalogs**: `./catalogs`
|
|
22
|
+
- **HTTP Provider**: `./providers/http`, `./providers/http/client`, `./providers/http/server`
|
|
23
23
|
|
|
24
24
|
**Key Features:**
|
|
25
25
|
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
- ✅ Next.js 15 (App Router, Server Components, hooks)
|
|
26
|
+
- Multi-Provider fan-out (PostHog, Segment, Vercel Analytics, HTTP, Console)
|
|
27
|
+
- Type-Safe Emitters (Segment.io specification)
|
|
28
|
+
- Zod-validated event catalogs (ecommerce, AI)
|
|
29
|
+
- Universal (client/server environments)
|
|
30
|
+
- Next.js App Router integration (hooks, components, provider)
|
|
31
|
+
- **Opt-in external providers** — only install what you use
|
|
33
32
|
|
|
34
33
|
---
|
|
35
34
|
|
|
36
35
|
## Installation
|
|
37
36
|
|
|
38
|
-
### Prerequisites
|
|
39
|
-
|
|
40
|
-
⚠️ **Required:** Node.js 22+ (package uses `structuredClone`, `AbortSignal.timeout`, `process.hrtime.bigint`)
|
|
41
|
-
|
|
42
37
|
```bash
|
|
43
|
-
|
|
44
|
-
node --version # Must be v22.0.0 or higher
|
|
45
|
-
|
|
46
|
-
# Install package
|
|
47
|
-
pnpm add @repo/analytics --filter=your-app
|
|
38
|
+
pnpm add @od-oneapp/analytics
|
|
48
39
|
```
|
|
49
40
|
|
|
50
|
-
###
|
|
41
|
+
### External Providers (opt-in)
|
|
51
42
|
|
|
52
|
-
Install only the
|
|
43
|
+
External analytics providers are **not bundled** with this package. Install only the ones you need:
|
|
53
44
|
|
|
54
45
|
```bash
|
|
55
|
-
# PostHog
|
|
56
|
-
pnpm add
|
|
57
|
-
|
|
58
|
-
# Vercel Analytics
|
|
59
|
-
pnpm add @vercel/analytics --filter=your-app
|
|
46
|
+
# PostHog
|
|
47
|
+
pnpm add @od-oneapp/integration-posthog
|
|
60
48
|
|
|
61
49
|
# Segment
|
|
62
|
-
pnpm add @
|
|
50
|
+
pnpm add @od-oneapp/integration-segment
|
|
63
51
|
|
|
64
|
-
#
|
|
52
|
+
# Vercel Analytics
|
|
53
|
+
pnpm add @od-oneapp/integration-vercel
|
|
65
54
|
```
|
|
66
55
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
## Quick Start
|
|
70
|
-
|
|
71
|
-
### Environment Variables
|
|
72
|
-
|
|
73
|
-
```bash
|
|
74
|
-
# .env.local
|
|
56
|
+
Built-in providers (`console`, `http`) are always available with no additional installation.
|
|
75
57
|
|
|
76
|
-
|
|
77
|
-
NEXT_PUBLIC_POSTHOG_KEY=phc_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
78
|
-
NEXT_PUBLIC_POSTHOG_HOST=https://app.posthog.com
|
|
58
|
+
If you configure a provider without installing its package, you'll get a clear error:
|
|
79
59
|
|
|
80
|
-
|
|
81
|
-
|
|
60
|
+
```
|
|
61
|
+
Analytics provider "posthog" is configured but its package is not installed.
|
|
62
|
+
Run: pnpm add @od-oneapp/integration-posthog
|
|
63
|
+
```
|
|
82
64
|
|
|
83
|
-
|
|
84
|
-
NEXT_PUBLIC_SEGMENT_WRITE_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
65
|
+
---
|
|
85
66
|
|
|
86
|
-
|
|
87
|
-
NEXT_PUBLIC_CONSOLE_ANALYTICS=true
|
|
88
|
-
```
|
|
67
|
+
## Quick Start
|
|
89
68
|
|
|
90
69
|
### Next.js Setup
|
|
91
70
|
|
|
@@ -93,7 +72,7 @@ NEXT_PUBLIC_CONSOLE_ANALYTICS=true
|
|
|
93
72
|
// app/providers.tsx
|
|
94
73
|
"use client";
|
|
95
74
|
|
|
96
|
-
import { AnalyticsProvider } from "@
|
|
75
|
+
import { AnalyticsProvider } from "@od-oneapp/analytics/client/next";
|
|
97
76
|
|
|
98
77
|
export function Providers({ children }: { children: React.ReactNode }) {
|
|
99
78
|
return (
|
|
@@ -104,14 +83,14 @@ export function Providers({ children }: { children: React.ReactNode }) {
|
|
|
104
83
|
apiKey: process.env.NEXT_PUBLIC_POSTHOG_KEY!,
|
|
105
84
|
options: {
|
|
106
85
|
debug: process.env.NODE_ENV === "development",
|
|
107
|
-
capture_pageview: false
|
|
108
|
-
}
|
|
86
|
+
capture_pageview: false,
|
|
87
|
+
},
|
|
109
88
|
},
|
|
110
89
|
console: {
|
|
111
|
-
enabled: process.env.NODE_ENV === "development"
|
|
112
|
-
}
|
|
90
|
+
enabled: process.env.NODE_ENV === "development",
|
|
91
|
+
},
|
|
113
92
|
},
|
|
114
|
-
debug: process.env.NODE_ENV === "development"
|
|
93
|
+
debug: process.env.NODE_ENV === "development",
|
|
115
94
|
}}
|
|
116
95
|
>
|
|
117
96
|
{children}
|
|
@@ -120,118 +99,76 @@ export function Providers({ children }: { children: React.ReactNode }) {
|
|
|
120
99
|
}
|
|
121
100
|
```
|
|
122
101
|
|
|
123
|
-
###
|
|
124
|
-
|
|
125
|
-
#### Client Component
|
|
102
|
+
### Client Component
|
|
126
103
|
|
|
127
104
|
```tsx
|
|
128
105
|
"use client";
|
|
129
106
|
|
|
130
|
-
import { useAnalytics, track } from "@
|
|
107
|
+
import { useAnalytics, track } from "@od-oneapp/analytics/client/next";
|
|
131
108
|
|
|
132
109
|
export function SignupButton() {
|
|
133
110
|
const analytics = useAnalytics();
|
|
134
111
|
|
|
135
112
|
const handleClick = async () => {
|
|
136
|
-
//
|
|
137
|
-
await analytics
|
|
113
|
+
// Direct tracking
|
|
114
|
+
await analytics?.track("Button Clicked", { button_name: "signup" });
|
|
138
115
|
|
|
139
|
-
//
|
|
116
|
+
// Emitter pattern
|
|
140
117
|
const event = track("CTA Clicked", { cta_type: "signup" });
|
|
141
|
-
await analytics
|
|
118
|
+
await analytics?.emit(event);
|
|
142
119
|
};
|
|
143
120
|
|
|
144
121
|
return <button onClick={handleClick}>Sign Up</button>;
|
|
145
122
|
}
|
|
146
123
|
```
|
|
147
124
|
|
|
148
|
-
|
|
125
|
+
### Server (API Route)
|
|
149
126
|
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
import { createServerAnalytics, ecommerce } from "@repo/analytics/server/next";
|
|
153
|
-
|
|
154
|
-
export async function POST(request: Request) {
|
|
155
|
-
const analytics = await createServerAnalytics({
|
|
156
|
-
providers: {
|
|
157
|
-
posthog: { apiKey: process.env.POSTHOG_API_KEY! }
|
|
158
|
-
}
|
|
159
|
-
});
|
|
127
|
+
```typescript
|
|
128
|
+
import { createServerAnalytics } from "@od-oneapp/analytics/server";
|
|
160
129
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
products: [{ product_id: "prod_abc", price: 299.99, quantity: 1 }]
|
|
167
|
-
})
|
|
168
|
-
);
|
|
130
|
+
const analytics = await createServerAnalytics({
|
|
131
|
+
providers: {
|
|
132
|
+
posthog: { apiKey: process.env.POSTHOG_API_KEY! },
|
|
133
|
+
},
|
|
134
|
+
});
|
|
169
135
|
|
|
170
|
-
|
|
171
|
-
}
|
|
136
|
+
await analytics.track("Order Completed", { orderId: "ord-123", total: 99.99 });
|
|
172
137
|
```
|
|
173
138
|
|
|
174
|
-
|
|
139
|
+
### Console + HTTP Only (no external deps needed)
|
|
175
140
|
|
|
176
141
|
```typescript
|
|
177
|
-
import {
|
|
178
|
-
|
|
179
|
-
// Product viewed
|
|
180
|
-
ecommerce.PRODUCT_VIEWED({
|
|
181
|
-
product_id: "prod_123",
|
|
182
|
-
name: "Premium Plan",
|
|
183
|
-
price: 299.99,
|
|
184
|
-
currency: "USD"
|
|
185
|
-
});
|
|
142
|
+
import { createClientAnalytics } from "@od-oneapp/analytics/client";
|
|
186
143
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
price: 299.99
|
|
144
|
+
const analytics = await createClientAnalytics({
|
|
145
|
+
providers: {
|
|
146
|
+
console: { enabled: true },
|
|
147
|
+
http: { options: { endpoint: "/api/v1/ingest" } },
|
|
148
|
+
},
|
|
193
149
|
});
|
|
194
150
|
|
|
195
|
-
|
|
196
|
-
ecommerce.ORDER_COMPLETED({
|
|
197
|
-
order_id: "order_789",
|
|
198
|
-
revenue: 499.97,
|
|
199
|
-
currency: "USD",
|
|
200
|
-
products: [
|
|
201
|
-
/* ... */
|
|
202
|
-
],
|
|
203
|
-
payment_method: "card"
|
|
204
|
-
});
|
|
151
|
+
await analytics.track("Page Loaded", { path: window.location.pathname });
|
|
205
152
|
```
|
|
206
153
|
|
|
207
|
-
|
|
154
|
+
### Event Catalogs (Zod-validated)
|
|
208
155
|
|
|
209
156
|
```typescript
|
|
210
|
-
import {
|
|
157
|
+
import { ecommerceCatalog, aiCatalog } from "@od-oneapp/analytics/catalogs";
|
|
211
158
|
|
|
212
|
-
//
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
159
|
+
// Type-safe ecommerce events
|
|
160
|
+
const event = ecommerceCatalog.track("Product Viewed", {
|
|
161
|
+
product_id: "sku-123",
|
|
162
|
+
name: "Premium Plan",
|
|
163
|
+
price: 29.99,
|
|
164
|
+
currency: "USD",
|
|
218
165
|
});
|
|
219
166
|
|
|
220
|
-
//
|
|
221
|
-
|
|
222
|
-
conversation_id: "
|
|
167
|
+
// Type-safe AI events
|
|
168
|
+
const aiEvent = aiCatalog.track("Chat Message Sent", {
|
|
169
|
+
conversation_id: "conv-123",
|
|
223
170
|
model_id: "gpt-4",
|
|
224
|
-
|
|
225
|
-
attempt_number: 2,
|
|
226
|
-
reason: "user-requested"
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
// Conversation branched
|
|
230
|
-
ai.CONVERSATION_BRANCHED({
|
|
231
|
-
conversation_id: "conv_123",
|
|
232
|
-
parent_conversation_id: "conv_122",
|
|
233
|
-
branch_point_message_id: "msg_450",
|
|
234
|
-
reason: "explore-alternative"
|
|
171
|
+
message_length: 150,
|
|
235
172
|
});
|
|
236
173
|
```
|
|
237
174
|
|
|
@@ -239,39 +176,35 @@ ai.CONVERSATION_BRANCHED({
|
|
|
239
176
|
|
|
240
177
|
## Architecture
|
|
241
178
|
|
|
242
|
-
### Four-File Export Pattern
|
|
243
|
-
|
|
244
179
|
```
|
|
245
|
-
@
|
|
246
|
-
├── /client → Browser-only (
|
|
247
|
-
├── /server → Node.js server (
|
|
248
|
-
├── /client/next → Next.js client (hooks, provider, components)
|
|
249
|
-
├── /server/next → Next.js server (
|
|
250
|
-
├── /
|
|
251
|
-
|
|
180
|
+
@od-oneapp/analytics
|
|
181
|
+
├── /client → Browser-only (built-in registry + dynamic external loader)
|
|
182
|
+
├── /server → Node.js server (built-in registry + dynamic external loader)
|
|
183
|
+
├── /client/next → Next.js client (hooks, provider, components, dynamic imports)
|
|
184
|
+
├── /server/next → Next.js server (re-exports from /server)
|
|
185
|
+
├── /types → TypeScript types + MissingProviderPackageError
|
|
186
|
+
├── /catalogs → Zod-validated event catalogs
|
|
187
|
+
└── /providers/http → Built-in HTTP provider
|
|
252
188
|
```
|
|
253
189
|
|
|
254
190
|
**Import Rules:**
|
|
255
191
|
|
|
256
|
-
-
|
|
257
|
-
-
|
|
258
|
-
-
|
|
259
|
-
-
|
|
260
|
-
|
|
261
|
-
**Edge Runtime Limitations:**
|
|
192
|
+
- Client components: `@od-oneapp/analytics/client/next`
|
|
193
|
+
- Server components/API routes: `@od-oneapp/analytics/server/next`
|
|
194
|
+
- Non-Next.js client: `@od-oneapp/analytics/client`
|
|
195
|
+
- Non-Next.js server: `@od-oneapp/analytics/server`
|
|
262
196
|
|
|
263
|
-
|
|
197
|
+
**Provider Architecture:**
|
|
264
198
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
- ❌ No batch processing optimization
|
|
199
|
+
| Provider | Type | Package |
|
|
200
|
+
|----------|------|---------|
|
|
201
|
+
| Console | Built-in | (included) |
|
|
202
|
+
| HTTP | Built-in | (included) |
|
|
203
|
+
| PostHog | Opt-in | `@od-oneapp/integration-posthog` |
|
|
204
|
+
| Segment | Opt-in | `@od-oneapp/integration-segment` |
|
|
205
|
+
| Vercel | Opt-in | `@od-oneapp/integration-vercel` |
|
|
273
206
|
|
|
274
|
-
|
|
207
|
+
External providers are loaded dynamically at runtime via `import()` — only when configured. If the package isn't installed, a `MissingProviderPackageError` is thrown with the install command.
|
|
275
208
|
|
|
276
209
|
---
|
|
277
210
|
|
|
@@ -280,230 +213,78 @@ For full features, use `/server` or `/server/next` in Node.js environments.
|
|
|
280
213
|
### Analytics Manager
|
|
281
214
|
|
|
282
215
|
```typescript
|
|
283
|
-
// Create analytics instance
|
|
284
216
|
const analytics = await createClientAnalytics(config);
|
|
285
217
|
|
|
286
|
-
// Track events
|
|
287
218
|
await analytics.track("Event Name", { property: "value" });
|
|
288
|
-
|
|
289
|
-
// Identify users
|
|
290
219
|
await analytics.identify("user_123", { email: "user@example.com" });
|
|
291
|
-
|
|
292
|
-
// Track page views
|
|
293
220
|
await analytics.page("/pricing", { title: "Pricing Page" });
|
|
294
|
-
|
|
295
|
-
// Group/organization
|
|
296
221
|
await analytics.group("org_456", { name: "Acme Corp" });
|
|
297
222
|
|
|
298
|
-
//
|
|
223
|
+
// Emitter pattern
|
|
299
224
|
const event = track("Event", { data: "value" });
|
|
300
225
|
await analytics.emit(event);
|
|
301
226
|
|
|
302
227
|
// Batch processing
|
|
303
|
-
await analytics.emitBatch([event1, event2, event3]
|
|
304
|
-
concurrency: 5,
|
|
305
|
-
failFast: false
|
|
306
|
-
});
|
|
228
|
+
await analytics.emitBatch([event1, event2, event3]);
|
|
307
229
|
```
|
|
308
230
|
|
|
309
231
|
### React Hooks
|
|
310
232
|
|
|
311
233
|
```typescript
|
|
312
|
-
// Access analytics manager
|
|
313
234
|
const analytics = useAnalytics();
|
|
235
|
+
const track = useTrackEvent();
|
|
236
|
+
const identify = useIdentifyUser();
|
|
314
237
|
|
|
315
|
-
|
|
316
|
-
useTrackEvent("Page Viewed", { page: "/dashboard" });
|
|
317
|
-
|
|
318
|
-
// Identify user on mount
|
|
319
|
-
useIdentifyUser(user.id, { email: user.email });
|
|
320
|
-
|
|
321
|
-
// Auto page tracking
|
|
322
|
-
usePageTracking();
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
---
|
|
326
|
-
|
|
327
|
-
## Feature Flags (PostHog)
|
|
328
|
-
|
|
329
|
-
### Client-Side
|
|
330
|
-
|
|
331
|
-
```typescript
|
|
332
|
-
import { useAnalytics } from '@repo/analytics/client/next';
|
|
333
|
-
|
|
334
|
-
function FeatureFlag() {
|
|
335
|
-
const analytics = useAnalytics();
|
|
336
|
-
const provider = analytics.getProvider('posthog');
|
|
337
|
-
const [enabled, setEnabled] = useState(false);
|
|
338
|
-
|
|
339
|
-
useEffect(() => {
|
|
340
|
-
provider?.isFeatureEnabled('new-feature').then(setEnabled);
|
|
341
|
-
}, [provider]);
|
|
342
|
-
|
|
343
|
-
return enabled ? <NewFeature /> : <OldFeature />;
|
|
344
|
-
}
|
|
238
|
+
usePageTracking({ trackSearch: true });
|
|
345
239
|
```
|
|
346
240
|
|
|
347
|
-
###
|
|
241
|
+
### Tracked Components
|
|
348
242
|
|
|
349
243
|
```tsx
|
|
350
|
-
|
|
351
|
-
|
|
244
|
+
<TrackedButton eventName="Sign Up Clicked" properties={{ location: "header" }}>
|
|
245
|
+
Sign Up
|
|
246
|
+
</TrackedButton>
|
|
352
247
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
const provider = analytics.getProvider("posthog");
|
|
359
|
-
const showFeature = await provider?.getFeatureFlag("new-feature", "user_123");
|
|
360
|
-
|
|
361
|
-
return <div>{showFeature ? <New /> : <Old />}</div>;
|
|
362
|
-
}
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
---
|
|
366
|
-
|
|
367
|
-
## Troubleshooting
|
|
368
|
-
|
|
369
|
-
### Common Issues
|
|
370
|
-
|
|
371
|
-
**"Analytics not initialized"**
|
|
372
|
-
|
|
373
|
-
- Ensure you await `createClientAnalytics()` or `createServerAnalytics()`
|
|
374
|
-
|
|
375
|
-
**"Provider not available in this environment"**
|
|
376
|
-
|
|
377
|
-
- Check you're using correct import path (client vs server vs edge)
|
|
378
|
-
|
|
379
|
-
**Events not showing in PostHog**
|
|
380
|
-
|
|
381
|
-
- Verify API key is correct
|
|
382
|
-
- Enable debug mode: `options: { debug: true }`
|
|
383
|
-
- Check network tab for failed requests
|
|
384
|
-
|
|
385
|
-
**"structuredClone is not defined"**
|
|
386
|
-
|
|
387
|
-
- Upgrade to Node.js 22+ (required)
|
|
388
|
-
|
|
389
|
-
### Debug Mode
|
|
390
|
-
|
|
391
|
-
```typescript
|
|
392
|
-
const analytics = await createClientAnalytics({
|
|
393
|
-
providers: {
|
|
394
|
-
posthog: {
|
|
395
|
-
apiKey: "...",
|
|
396
|
-
options: { debug: true }
|
|
397
|
-
}
|
|
398
|
-
},
|
|
399
|
-
debug: true,
|
|
400
|
-
onInfo: (msg) => console.log("[Analytics]", msg),
|
|
401
|
-
onError: (err, ctx) => console.error("[Analytics Error]", err, ctx)
|
|
402
|
-
});
|
|
248
|
+
<TrackedLink href="/products" eventName="Product Link Clicked">
|
|
249
|
+
View Products
|
|
250
|
+
</TrackedLink>
|
|
403
251
|
```
|
|
404
252
|
|
|
405
|
-
###
|
|
406
|
-
|
|
407
|
-
```typescript
|
|
408
|
-
const health = await analytics.healthCheck(5000);
|
|
409
|
-
console.log("Healthy:", health.healthy);
|
|
410
|
-
console.log("Providers:", health.providers);
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
---
|
|
414
|
-
|
|
415
|
-
## Security
|
|
416
|
-
|
|
417
|
-
⚠️ **Important Security Notes:**
|
|
418
|
-
|
|
419
|
-
1. **Never track PII** - Avoid email, phone, SSN, credit cards
|
|
420
|
-
2. **Sanitize input** - Validate all properties before tracking
|
|
421
|
-
3. **Use env variables** - Never hardcode API keys
|
|
422
|
-
4. **Rotate keys** - Update API keys periodically
|
|
423
|
-
|
|
424
|
-
**Security Features (Implemented):**
|
|
425
|
-
|
|
426
|
-
- ✅ Input sanitization (`sanitizeProperties()`)
|
|
427
|
-
- ✅ PII detection and redaction (`containsPII()`, `redactPII()`)
|
|
428
|
-
- ✅ XSS protection (`stripHTML()`)
|
|
429
|
-
- ✅ Payload size limits (default: 100KB max)
|
|
430
|
-
- ✅ Dangerous key blocking (prevents `__proto__`, `constructor`)
|
|
431
|
-
- ✅ Rate limiting (token bucket algorithm, 100 calls/second default)
|
|
432
|
-
|
|
433
|
-
**Usage:**
|
|
253
|
+
### Error Handling
|
|
434
254
|
|
|
435
255
|
```typescript
|
|
436
|
-
import {
|
|
437
|
-
|
|
438
|
-
// Sanitize before tracking
|
|
439
|
-
const sanitized = sanitizeProperties(userInput, {
|
|
440
|
-
stripPII: true,
|
|
441
|
-
stripHTML: true,
|
|
442
|
-
maxPayloadSize: 50_000 // 50KB
|
|
443
|
-
});
|
|
256
|
+
import { MissingProviderPackageError } from "@od-oneapp/analytics/types";
|
|
444
257
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
258
|
+
try {
|
|
259
|
+
const analytics = await createClientAnalytics({
|
|
260
|
+
providers: { posthog: { apiKey: "..." } },
|
|
261
|
+
});
|
|
262
|
+
} catch (error) {
|
|
263
|
+
if (error instanceof MissingProviderPackageError) {
|
|
264
|
+
console.error(error.message);
|
|
265
|
+
// "Analytics provider "posthog" is configured but its package is not installed.
|
|
266
|
+
// Run: pnpm add @od-oneapp/integration-posthog"
|
|
267
|
+
}
|
|
448
268
|
}
|
|
449
269
|
```
|
|
450
270
|
|
|
451
271
|
---
|
|
452
272
|
|
|
453
|
-
##
|
|
273
|
+
## Security
|
|
454
274
|
|
|
455
|
-
-
|
|
456
|
-
-
|
|
457
|
-
-
|
|
458
|
-
-
|
|
275
|
+
- Input sanitization (`sanitizeProperties()`)
|
|
276
|
+
- PII detection and redaction (`containsPII()`, `redactPII()`)
|
|
277
|
+
- XSS protection (`stripHTML()`)
|
|
278
|
+
- Payload size limits (default: 100KB max)
|
|
279
|
+
- Rate limiting (token bucket algorithm)
|
|
459
280
|
|
|
460
281
|
---
|
|
461
282
|
|
|
462
283
|
## Development
|
|
463
284
|
|
|
464
285
|
```bash
|
|
465
|
-
|
|
466
|
-
pnpm
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
pnpm --filter=@repo/analytics test
|
|
470
|
-
|
|
471
|
-
# Type check
|
|
472
|
-
pnpm --filter=@repo/analytics typecheck
|
|
473
|
-
|
|
474
|
-
# Lint
|
|
475
|
-
pnpm --filter=@repo/analytics lint
|
|
476
|
-
|
|
477
|
-
# Coverage
|
|
478
|
-
pnpm --filter=@repo/analytics test:coverage
|
|
479
|
-
|
|
480
|
-
# Check circular deps
|
|
481
|
-
pnpm --filter=@repo/analytics circular
|
|
286
|
+
pnpm --filter @od-oneapp/analytics test
|
|
287
|
+
pnpm --filter @od-oneapp/analytics typecheck
|
|
288
|
+
pnpm --filter @od-oneapp/analytics lint
|
|
289
|
+
pnpm --filter @od-oneapp/analytics build
|
|
482
290
|
```
|
|
483
|
-
|
|
484
|
-
---
|
|
485
|
-
|
|
486
|
-
## Support
|
|
487
|
-
|
|
488
|
-
- **Issues**: [GitHub Issues](https://github.com/your-org/monorepo/issues)
|
|
489
|
-
- **Discussions**: [GitHub Discussions](https://github.com/your-org/monorepo/discussions)
|
|
490
|
-
|
|
491
|
-
---
|
|
492
|
-
|
|
493
|
-
## License
|
|
494
|
-
|
|
495
|
-
MIT License
|
|
496
|
-
|
|
497
|
-
---
|
|
498
|
-
|
|
499
|
-
**Built with ❤️ by the Platform Team**
|
|
500
|
-
|
|
501
|
-
## 📚 Comprehensive Documentation
|
|
502
|
-
|
|
503
|
-
For detailed documentation, see:
|
|
504
|
-
|
|
505
|
-
- **[Audit Reports](../../apps/docs/content/docs/audits/analytics/)** - Comprehensive audits, fixes, and security
|
|
506
|
-
reviews
|
|
507
|
-
- **[Technical Guides](../../apps/docs/content/docs/packages/analytics/)** - Implementation guides and best practices
|
|
508
|
-
|
|
509
|
-
All comprehensive documentation has been centralized in the docs app.
|