@stacksee/analytics 0.4.6 → 0.5.0
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/dist/adapters/client/browser-analytics.d.ts +42 -5
- package/dist/adapters/server/server-analytics.d.ts +50 -16
- package/dist/client-DTHZYkxx.js +90 -0
- package/dist/client.d.ts +2 -3
- package/dist/client.js +101 -59
- package/dist/core/events/types.d.ts +7 -1
- package/dist/providers/client.js +1 -1
- package/dist/providers/server.js +1 -1
- package/dist/server-DjEk1fUD.js +88 -0
- package/dist/server.d.ts +1 -1
- package/dist/server.js +97 -64
- package/package.json +1 -1
- package/readme.md +200 -12
- package/dist/client-RZPcOfAk.js +0 -86
- package/dist/server-CMRw9K0d.js +0 -84
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { AnalyticsConfig, EventContext } from '../../core/events/types.js';
|
|
2
2
|
type DefaultEventMap = Record<string, Record<string, unknown>>;
|
|
3
|
-
export declare class BrowserAnalytics<TEventMap extends DefaultEventMap = DefaultEventMap> {
|
|
3
|
+
export declare class BrowserAnalytics<TEventMap extends DefaultEventMap = DefaultEventMap, TUserTraits extends Record<string, unknown> = Record<string, unknown>> {
|
|
4
4
|
private providers;
|
|
5
5
|
private context;
|
|
6
6
|
private userId?;
|
|
7
7
|
private sessionId?;
|
|
8
|
+
private userTraits?;
|
|
8
9
|
private initialized;
|
|
9
10
|
private initializePromise?;
|
|
10
11
|
/**
|
|
@@ -78,11 +79,16 @@ export declare class BrowserAnalytics<TEventMap extends DefaultEventMap = Defaul
|
|
|
78
79
|
* sets user properties. This method should be called when a user logs in
|
|
79
80
|
* or when you want to associate events with a known user.
|
|
80
81
|
*
|
|
82
|
+
* **User Context (New):** User data (userId, email, traits) is automatically stored
|
|
83
|
+
* and included in all subsequent `track()` calls. This makes it easy for providers
|
|
84
|
+
* like Loops, Customer.io, or Intercom to access user information without passing
|
|
85
|
+
* it manually each time. The data is cleared when `reset()` is called (e.g., on logout).
|
|
86
|
+
*
|
|
81
87
|
* The method automatically ensures initialization but doesn't block execution
|
|
82
88
|
* if initialization is still in progress.
|
|
83
89
|
*
|
|
84
90
|
* @param userId Unique identifier for the user (e.g., database ID, email)
|
|
85
|
-
* @param traits Optional user properties and characteristics
|
|
91
|
+
* @param traits Optional user properties and characteristics (email, name, plan, etc.)
|
|
86
92
|
*
|
|
87
93
|
* @example
|
|
88
94
|
* ```typescript
|
|
@@ -92,7 +98,7 @@ export declare class BrowserAnalytics<TEventMap extends DefaultEventMap = Defaul
|
|
|
92
98
|
*
|
|
93
99
|
* @example
|
|
94
100
|
* ```typescript
|
|
95
|
-
* // Identify with user traits
|
|
101
|
+
* // Identify with user traits (recommended - enables email-based providers)
|
|
96
102
|
* analytics.identify('user-123', {
|
|
97
103
|
* email: 'john@example.com',
|
|
98
104
|
* name: 'John Doe',
|
|
@@ -103,6 +109,10 @@ export declare class BrowserAnalytics<TEventMap extends DefaultEventMap = Defaul
|
|
|
103
109
|
* notifications: false
|
|
104
110
|
* }
|
|
105
111
|
* });
|
|
112
|
+
*
|
|
113
|
+
* // Now all subsequent track() calls automatically include user context
|
|
114
|
+
* analytics.track('button_clicked', { buttonId: 'checkout' });
|
|
115
|
+
* // Providers receive: context.user = { userId: 'user-123', email: 'john@example.com', traits: {...} }
|
|
106
116
|
* ```
|
|
107
117
|
*
|
|
108
118
|
* @example
|
|
@@ -111,15 +121,25 @@ export declare class BrowserAnalytics<TEventMap extends DefaultEventMap = Defaul
|
|
|
111
121
|
* async function handleLogin(email: string, password: string) {
|
|
112
122
|
* const user = await login(email, password);
|
|
113
123
|
*
|
|
124
|
+
* // Identify user with full traits
|
|
114
125
|
* analytics.identify(user.id, {
|
|
115
126
|
* email: user.email,
|
|
116
127
|
* name: user.name,
|
|
128
|
+
* plan: user.plan,
|
|
129
|
+
* company: user.company,
|
|
117
130
|
* lastLogin: new Date().toISOString()
|
|
118
131
|
* });
|
|
132
|
+
*
|
|
133
|
+
* // All subsequent events now include this user context automatically
|
|
134
|
+
* }
|
|
135
|
+
*
|
|
136
|
+
* // In a logout handler - clear user context
|
|
137
|
+
* async function handleLogout() {
|
|
138
|
+
* analytics.reset(); // Clears userId and traits
|
|
119
139
|
* }
|
|
120
140
|
* ```
|
|
121
141
|
*/
|
|
122
|
-
identify(userId: string, traits?:
|
|
142
|
+
identify(userId: string, traits?: TUserTraits): void;
|
|
123
143
|
/**
|
|
124
144
|
* Tracks a custom event with properties.
|
|
125
145
|
*
|
|
@@ -128,6 +148,10 @@ export declare class BrowserAnalytics<TEventMap extends DefaultEventMap = Defaul
|
|
|
128
148
|
* configured providers. Events are enriched with context information like
|
|
129
149
|
* timestamp, user ID, session ID, and browser context.
|
|
130
150
|
*
|
|
151
|
+
* **User Context (New):** If `identify()` was called previously, user data (userId,
|
|
152
|
+
* email, traits) is automatically included in the event context sent to all providers.
|
|
153
|
+
* This happens transparently - you don't need to pass user data manually.
|
|
154
|
+
*
|
|
131
155
|
* If providers are configured, the method waits for all providers to complete
|
|
132
156
|
* tracking. Failed providers don't prevent others from succeeding.
|
|
133
157
|
*
|
|
@@ -146,6 +170,19 @@ export declare class BrowserAnalytics<TEventMap extends DefaultEventMap = Defaul
|
|
|
146
170
|
*
|
|
147
171
|
* @example
|
|
148
172
|
* ```typescript
|
|
173
|
+
* // User context is automatically included after identify()
|
|
174
|
+
* analytics.identify('user-123', {
|
|
175
|
+
* email: 'user@example.com',
|
|
176
|
+
* plan: 'pro'
|
|
177
|
+
* });
|
|
178
|
+
*
|
|
179
|
+
* // Now all events automatically include user context
|
|
180
|
+
* analytics.track('button_clicked', { buttonId: 'checkout' });
|
|
181
|
+
* // Providers receive: context.user = { userId: 'user-123', email: 'user@example.com', traits: {...} }
|
|
182
|
+
* ```
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```typescript
|
|
149
186
|
* // Track a purchase event
|
|
150
187
|
* await analytics.track('purchase_completed', {
|
|
151
188
|
* orderId: 'order-123',
|
|
@@ -409,7 +446,7 @@ export declare class BrowserAnalytics<TEventMap extends DefaultEventMap = Defaul
|
|
|
409
446
|
* });
|
|
410
447
|
* ```
|
|
411
448
|
*/
|
|
412
|
-
updateContext(context: Partial<EventContext
|
|
449
|
+
updateContext(context: Partial<EventContext<TUserTraits>>): void;
|
|
413
450
|
private getCategoryFromEventName;
|
|
414
451
|
private generateSessionId;
|
|
415
452
|
private getDeviceType;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { AnalyticsConfig, EventContext } from '../../core/events/types.js';
|
|
1
|
+
import { AnalyticsConfig, EventContext, UserContext } from '../../core/events/types.js';
|
|
2
2
|
type DefaultEventMap = Record<string, Record<string, unknown>>;
|
|
3
|
-
export declare class ServerAnalytics<TEventMap extends Record<string, Record<string, unknown>> = DefaultEventMap> {
|
|
3
|
+
export declare class ServerAnalytics<TEventMap extends Record<string, Record<string, unknown>> = DefaultEventMap, TUserTraits extends Record<string, unknown> = Record<string, unknown>> {
|
|
4
4
|
private providers;
|
|
5
5
|
private config;
|
|
6
6
|
private initialized;
|
|
@@ -129,12 +129,17 @@ export declare class ServerAnalytics<TEventMap extends Record<string, Record<str
|
|
|
129
129
|
* Server-side tracking typically includes additional context like IP addresses,
|
|
130
130
|
* user agents, and server-specific metadata that isn't available on the client.
|
|
131
131
|
*
|
|
132
|
+
* **User Context (New):** You can now pass user data (email, traits) with each event
|
|
133
|
+
* via `options.user` or `options.context.user`. This is useful for providers like
|
|
134
|
+
* Loops, Customer.io, or Intercom that require user identifiers.
|
|
135
|
+
*
|
|
132
136
|
* @param eventName Name of the event to track (must match your event definitions)
|
|
133
137
|
* @param properties Event-specific properties and data
|
|
134
|
-
* @param options Optional configuration including user ID, session ID, and context
|
|
138
|
+
* @param options Optional configuration including user ID, session ID, user context, and additional context
|
|
135
139
|
* @param options.userId User ID to associate with this event
|
|
136
140
|
* @param options.sessionId Session ID to associate with this event
|
|
137
|
-
* @param options.
|
|
141
|
+
* @param options.user User context including email and traits (automatically included in event context)
|
|
142
|
+
* @param options.context Additional context for this event (page, device, etc.)
|
|
138
143
|
* @returns Promise that resolves when tracking is complete for all providers
|
|
139
144
|
*
|
|
140
145
|
* @example
|
|
@@ -150,7 +155,7 @@ export declare class ServerAnalytics<TEventMap extends Record<string, Record<str
|
|
|
150
155
|
*
|
|
151
156
|
* @example
|
|
152
157
|
* ```typescript
|
|
153
|
-
* // Track with user context
|
|
158
|
+
* // Track with user context (recommended for email-based providers)
|
|
154
159
|
* await analytics.track('purchase_completed', {
|
|
155
160
|
* orderId: 'order-123',
|
|
156
161
|
* amount: 99.99,
|
|
@@ -158,33 +163,61 @@ export declare class ServerAnalytics<TEventMap extends Record<string, Record<str
|
|
|
158
163
|
* itemCount: 3
|
|
159
164
|
* }, {
|
|
160
165
|
* userId: 'user-456',
|
|
161
|
-
*
|
|
166
|
+
* user: {
|
|
167
|
+
* email: 'user@example.com',
|
|
168
|
+
* traits: {
|
|
169
|
+
* plan: 'pro',
|
|
170
|
+
* company: 'Acme Corp'
|
|
171
|
+
* }
|
|
172
|
+
* },
|
|
162
173
|
* context: {
|
|
163
174
|
* page: { path: '/checkout/complete' },
|
|
164
|
-
* device: { userAgent: req.headers['user-agent'] }
|
|
165
|
-
* ip: req.ip
|
|
175
|
+
* device: { userAgent: req.headers['user-agent'] }
|
|
166
176
|
* }
|
|
167
177
|
* });
|
|
178
|
+
* // Providers receive: context.user = { email: 'user@example.com', traits: {...} }
|
|
168
179
|
* ```
|
|
169
180
|
*
|
|
170
181
|
* @example
|
|
171
182
|
* ```typescript
|
|
172
|
-
* //
|
|
183
|
+
* // Alternative: Pass user via context.user
|
|
184
|
+
* await analytics.track('feature_used', {
|
|
185
|
+
* featureName: 'export'
|
|
186
|
+
* }, {
|
|
187
|
+
* userId: 'user-123',
|
|
188
|
+
* context: {
|
|
189
|
+
* user: {
|
|
190
|
+
* email: 'user@example.com'
|
|
191
|
+
* },
|
|
192
|
+
* page: { path: '/dashboard' }
|
|
193
|
+
* }
|
|
194
|
+
* });
|
|
195
|
+
* ```
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```typescript
|
|
199
|
+
* // In an Express.js route handler with user data
|
|
173
200
|
* app.post('/api/users', async (req, res) => {
|
|
174
201
|
* const user = await createUser(req.body);
|
|
175
202
|
*
|
|
176
|
-
* // Track user creation with
|
|
203
|
+
* // Track user creation with full user context
|
|
177
204
|
* await analytics.track('user_created', {
|
|
178
205
|
* userId: user.id,
|
|
179
206
|
* email: user.email,
|
|
180
207
|
* plan: user.plan
|
|
181
208
|
* }, {
|
|
182
209
|
* userId: user.id,
|
|
210
|
+
* user: {
|
|
211
|
+
* email: user.email,
|
|
212
|
+
* traits: {
|
|
213
|
+
* name: user.name,
|
|
214
|
+
* plan: user.plan,
|
|
215
|
+
* company: user.company
|
|
216
|
+
* }
|
|
217
|
+
* },
|
|
183
218
|
* context: {
|
|
184
219
|
* page: { path: req.path },
|
|
185
|
-
* device: { userAgent: req.headers['user-agent'] }
|
|
186
|
-
* ip: req.ip,
|
|
187
|
-
* server: { version: process.env.APP_VERSION }
|
|
220
|
+
* device: { userAgent: req.headers['user-agent'] }
|
|
188
221
|
* }
|
|
189
222
|
* });
|
|
190
223
|
*
|
|
@@ -210,7 +243,8 @@ export declare class ServerAnalytics<TEventMap extends Record<string, Record<str
|
|
|
210
243
|
track<TEventName extends string>(eventName: TEventName, properties: TEventName extends keyof TEventMap ? TEventMap[TEventName] : Record<string, unknown>, options?: {
|
|
211
244
|
userId?: string;
|
|
212
245
|
sessionId?: string;
|
|
213
|
-
context?: EventContext
|
|
246
|
+
context?: EventContext<TUserTraits>;
|
|
247
|
+
user?: UserContext<TUserTraits>;
|
|
214
248
|
}): Promise<void>;
|
|
215
249
|
/**
|
|
216
250
|
* Tracks a page view event from the server side.
|
|
@@ -273,7 +307,7 @@ export declare class ServerAnalytics<TEventMap extends Record<string, Record<str
|
|
|
273
307
|
* ```
|
|
274
308
|
*/
|
|
275
309
|
pageView(properties?: Record<string, unknown>, options?: {
|
|
276
|
-
context?: EventContext
|
|
310
|
+
context?: EventContext<TUserTraits>;
|
|
277
311
|
}): void;
|
|
278
312
|
/**
|
|
279
313
|
* Tracks when a user leaves a page from the server side.
|
|
@@ -335,7 +369,7 @@ export declare class ServerAnalytics<TEventMap extends Record<string, Record<str
|
|
|
335
369
|
* ```
|
|
336
370
|
*/
|
|
337
371
|
pageLeave(properties?: Record<string, unknown>, options?: {
|
|
338
|
-
context?: EventContext
|
|
372
|
+
context?: EventContext<TUserTraits>;
|
|
339
373
|
}): void;
|
|
340
374
|
/**
|
|
341
375
|
* Shuts down all analytics providers and flushes pending events.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
var o = Object.defineProperty;
|
|
2
|
+
var d = (a, e, s) => e in a ? o(a, e, { enumerable: !0, configurable: !0, writable: !0, value: s }) : a[e] = s;
|
|
3
|
+
var g = (a, e, s) => d(a, typeof e != "symbol" ? e + "" : e, s);
|
|
4
|
+
import { B as l } from "./base.provider-AfFL5W_P.js";
|
|
5
|
+
function t() {
|
|
6
|
+
return typeof window < "u" && typeof window.document < "u";
|
|
7
|
+
}
|
|
8
|
+
class n extends l {
|
|
9
|
+
constructor(s) {
|
|
10
|
+
super({ debug: s.debug, enabled: s.enabled });
|
|
11
|
+
g(this, "name", "PostHog-Client");
|
|
12
|
+
g(this, "posthog");
|
|
13
|
+
g(this, "initialized", !1);
|
|
14
|
+
g(this, "config");
|
|
15
|
+
this.config = s;
|
|
16
|
+
}
|
|
17
|
+
async initialize() {
|
|
18
|
+
if (this.isEnabled() && !this.initialized) {
|
|
19
|
+
if (!t()) {
|
|
20
|
+
this.log("Skipping initialization - not in browser environment");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (!this.config.token || typeof this.config.token != "string")
|
|
24
|
+
throw new Error("PostHog requires a token");
|
|
25
|
+
try {
|
|
26
|
+
const { default: s } = await import("posthog-js"), { token: i, debug: r, ...h } = this.config;
|
|
27
|
+
s.init(i, {
|
|
28
|
+
...h,
|
|
29
|
+
debug: r ?? this.debug
|
|
30
|
+
}), this.posthog = s, this.initialized = !0, this.log("Initialized successfully", this.config);
|
|
31
|
+
} catch (s) {
|
|
32
|
+
throw console.error("[PostHog-Client] Failed to initialize:", s), s;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
identify(s, i) {
|
|
37
|
+
!this.isEnabled() || !this.initialized || !this.posthog || (this.posthog.identify(s, i), this.log("Identified user", { userId: s, traits: i }));
|
|
38
|
+
}
|
|
39
|
+
track(s, i) {
|
|
40
|
+
var h, p;
|
|
41
|
+
if (!this.isEnabled() || !this.initialized || !this.posthog) return;
|
|
42
|
+
const r = {
|
|
43
|
+
...s.properties,
|
|
44
|
+
category: s.category,
|
|
45
|
+
timestamp: s.timestamp || Date.now(),
|
|
46
|
+
...s.userId && { userId: s.userId },
|
|
47
|
+
...s.sessionId && { sessionId: s.sessionId },
|
|
48
|
+
...(i == null ? void 0 : i.page) && { $current_url: i.page.path },
|
|
49
|
+
...(i == null ? void 0 : i.device) && { device: i.device },
|
|
50
|
+
...(i == null ? void 0 : i.utm) && { utm: i.utm },
|
|
51
|
+
// Include user email and traits as regular event properties
|
|
52
|
+
...((h = i == null ? void 0 : i.user) == null ? void 0 : h.email) && { user_email: i.user.email },
|
|
53
|
+
...((p = i == null ? void 0 : i.user) == null ? void 0 : p.traits) && { user_traits: i.user.traits }
|
|
54
|
+
};
|
|
55
|
+
this.posthog.capture(s.action, r), this.log("Tracked event", { event: s, context: i });
|
|
56
|
+
}
|
|
57
|
+
pageView(s, i) {
|
|
58
|
+
if (!this.isEnabled() || !this.initialized || !this.posthog || !t())
|
|
59
|
+
return;
|
|
60
|
+
const r = {
|
|
61
|
+
...s,
|
|
62
|
+
...(i == null ? void 0 : i.page) && {
|
|
63
|
+
path: i.page.path,
|
|
64
|
+
title: i.page.title,
|
|
65
|
+
referrer: i.page.referrer
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
this.posthog.capture("$pageview", r), this.log("Tracked page view", { properties: s, context: i });
|
|
69
|
+
}
|
|
70
|
+
pageLeave(s, i) {
|
|
71
|
+
if (!this.isEnabled() || !this.initialized || !this.posthog || !t())
|
|
72
|
+
return;
|
|
73
|
+
const r = {
|
|
74
|
+
...s,
|
|
75
|
+
...(i == null ? void 0 : i.page) && {
|
|
76
|
+
path: i.page.path,
|
|
77
|
+
title: i.page.title,
|
|
78
|
+
referrer: i.page.referrer
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
this.posthog.capture("$pageleave", r), this.log("Tracked page leave", { properties: s, context: i });
|
|
82
|
+
}
|
|
83
|
+
reset() {
|
|
84
|
+
!this.isEnabled() || !this.initialized || !this.posthog || !t() || (this.posthog.reset(), this.log("Reset user session"));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
export {
|
|
88
|
+
n as P,
|
|
89
|
+
t as i
|
|
90
|
+
};
|
package/dist/client.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { BrowserAnalytics } from './adapters/client/browser-analytics.js';
|
|
2
2
|
import { AnalyticsProvider } from './core/events/types.js';
|
|
3
3
|
import { EventMapFromCollection } from './core/events/index.js';
|
|
4
|
-
type DefaultEventMap = Record<string, Record<string, unknown>>;
|
|
5
4
|
export interface ClientAnalyticsConfig {
|
|
6
5
|
providers?: AnalyticsProvider[];
|
|
7
6
|
debug?: boolean;
|
|
@@ -35,12 +34,12 @@ export interface ClientAnalyticsConfig {
|
|
|
35
34
|
* });
|
|
36
35
|
* ```
|
|
37
36
|
*/
|
|
38
|
-
export declare function createClientAnalytics<TEvents = never>(config: ClientAnalyticsConfig): BrowserAnalytics<EventMapFromCollection<TEvents
|
|
37
|
+
export declare function createClientAnalytics<TEvents = never, TUserTraits extends Record<string, unknown> = Record<string, unknown>>(config: ClientAnalyticsConfig): BrowserAnalytics<EventMapFromCollection<TEvents>, TUserTraits>;
|
|
39
38
|
export { createClientAnalytics as createAnalytics };
|
|
40
39
|
/**
|
|
41
40
|
* Get the current analytics instance
|
|
42
41
|
*/
|
|
43
|
-
export declare function getAnalytics(): BrowserAnalytics<
|
|
42
|
+
export declare function getAnalytics(): BrowserAnalytics<Record<string, Record<string, unknown>>, Record<string, unknown>>;
|
|
44
43
|
/**
|
|
45
44
|
* Convenience function to track events
|
|
46
45
|
*/
|
package/dist/client.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
import { i as
|
|
5
|
-
import { P } from "./client-
|
|
6
|
-
import { B as
|
|
7
|
-
class
|
|
1
|
+
var u = Object.defineProperty;
|
|
2
|
+
var h = (t, e, i) => e in t ? u(t, e, { enumerable: !0, configurable: !0, writable: !0, value: i }) : t[e] = i;
|
|
3
|
+
var s = (t, e, i) => h(t, typeof e != "symbol" ? e + "" : e, i);
|
|
4
|
+
import { i as p } from "./client-DTHZYkxx.js";
|
|
5
|
+
import { P as b } from "./client-DTHZYkxx.js";
|
|
6
|
+
import { B as C } from "./base.provider-AfFL5W_P.js";
|
|
7
|
+
class f {
|
|
8
8
|
/**
|
|
9
9
|
* Creates a new BrowserAnalytics instance for client-side event tracking.
|
|
10
10
|
*
|
|
@@ -36,12 +36,13 @@ class p {
|
|
|
36
36
|
* ```
|
|
37
37
|
*/
|
|
38
38
|
constructor(e) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
s(this, "providers", []);
|
|
40
|
+
s(this, "context", {});
|
|
41
|
+
s(this, "userId");
|
|
42
|
+
s(this, "sessionId");
|
|
43
|
+
s(this, "userTraits");
|
|
44
|
+
s(this, "initialized", !1);
|
|
45
|
+
s(this, "initializePromise");
|
|
45
46
|
this.providers = e.providers, e.defaultContext && (this.context = { ...e.defaultContext }), this.sessionId = this.generateSessionId();
|
|
46
47
|
}
|
|
47
48
|
/**
|
|
@@ -75,7 +76,7 @@ class p {
|
|
|
75
76
|
* ```
|
|
76
77
|
*/
|
|
77
78
|
async initialize() {
|
|
78
|
-
if (
|
|
79
|
+
if (p() && !this.initialized)
|
|
79
80
|
return this.initializePromise ? this.initializePromise : (this.initializePromise = this._doInitialize(), this.initializePromise);
|
|
80
81
|
}
|
|
81
82
|
async _doInitialize() {
|
|
@@ -100,26 +101,31 @@ class p {
|
|
|
100
101
|
}
|
|
101
102
|
/**
|
|
102
103
|
* Identifies a user with optional traits.
|
|
103
|
-
*
|
|
104
|
+
*
|
|
104
105
|
* Associates subsequent events with the specified user ID and optionally
|
|
105
106
|
* sets user properties. This method should be called when a user logs in
|
|
106
107
|
* or when you want to associate events with a known user.
|
|
107
|
-
*
|
|
108
|
+
*
|
|
109
|
+
* **User Context (New):** User data (userId, email, traits) is automatically stored
|
|
110
|
+
* and included in all subsequent `track()` calls. This makes it easy for providers
|
|
111
|
+
* like Loops, Customer.io, or Intercom to access user information without passing
|
|
112
|
+
* it manually each time. The data is cleared when `reset()` is called (e.g., on logout).
|
|
113
|
+
*
|
|
108
114
|
* The method automatically ensures initialization but doesn't block execution
|
|
109
115
|
* if initialization is still in progress.
|
|
110
|
-
*
|
|
116
|
+
*
|
|
111
117
|
* @param userId Unique identifier for the user (e.g., database ID, email)
|
|
112
|
-
* @param traits Optional user properties and characteristics
|
|
113
|
-
*
|
|
118
|
+
* @param traits Optional user properties and characteristics (email, name, plan, etc.)
|
|
119
|
+
*
|
|
114
120
|
* @example
|
|
115
121
|
* ```typescript
|
|
116
122
|
* // Basic user identification
|
|
117
123
|
* analytics.identify('user-123');
|
|
118
124
|
* ```
|
|
119
|
-
*
|
|
125
|
+
*
|
|
120
126
|
* @example
|
|
121
127
|
* ```typescript
|
|
122
|
-
* // Identify with user traits
|
|
128
|
+
* // Identify with user traits (recommended - enables email-based providers)
|
|
123
129
|
* analytics.identify('user-123', {
|
|
124
130
|
* email: 'john@example.com',
|
|
125
131
|
* name: 'John Doe',
|
|
@@ -130,24 +136,38 @@ class p {
|
|
|
130
136
|
* notifications: false
|
|
131
137
|
* }
|
|
132
138
|
* });
|
|
139
|
+
*
|
|
140
|
+
* // Now all subsequent track() calls automatically include user context
|
|
141
|
+
* analytics.track('button_clicked', { buttonId: 'checkout' });
|
|
142
|
+
* // Providers receive: context.user = { userId: 'user-123', email: 'john@example.com', traits: {...} }
|
|
133
143
|
* ```
|
|
134
|
-
*
|
|
144
|
+
*
|
|
135
145
|
* @example
|
|
136
146
|
* ```typescript
|
|
137
147
|
* // In a login handler
|
|
138
148
|
* async function handleLogin(email: string, password: string) {
|
|
139
149
|
* const user = await login(email, password);
|
|
140
|
-
*
|
|
150
|
+
*
|
|
151
|
+
* // Identify user with full traits
|
|
141
152
|
* analytics.identify(user.id, {
|
|
142
153
|
* email: user.email,
|
|
143
154
|
* name: user.name,
|
|
155
|
+
* plan: user.plan,
|
|
156
|
+
* company: user.company,
|
|
144
157
|
* lastLogin: new Date().toISOString()
|
|
145
158
|
* });
|
|
159
|
+
*
|
|
160
|
+
* // All subsequent events now include this user context automatically
|
|
161
|
+
* }
|
|
162
|
+
*
|
|
163
|
+
* // In a logout handler - clear user context
|
|
164
|
+
* async function handleLogout() {
|
|
165
|
+
* analytics.reset(); // Clears userId and traits
|
|
146
166
|
* }
|
|
147
167
|
* ```
|
|
148
168
|
*/
|
|
149
169
|
identify(e, i) {
|
|
150
|
-
this.userId = e, this.ensureInitialized().catch((r) => {
|
|
170
|
+
this.userId = e, this.userTraits = i, this.ensureInitialized().catch((r) => {
|
|
151
171
|
console.error("[Analytics] Failed to initialize during identify:", r);
|
|
152
172
|
});
|
|
153
173
|
for (const r of this.providers)
|
|
@@ -155,15 +175,19 @@ class p {
|
|
|
155
175
|
}
|
|
156
176
|
/**
|
|
157
177
|
* Tracks a custom event with properties.
|
|
158
|
-
*
|
|
178
|
+
*
|
|
159
179
|
* This is the main method for tracking user interactions and business events.
|
|
160
180
|
* The method ensures initialization before tracking and sends the event to all
|
|
161
181
|
* configured providers. Events are enriched with context information like
|
|
162
182
|
* timestamp, user ID, session ID, and browser context.
|
|
163
|
-
*
|
|
183
|
+
*
|
|
184
|
+
* **User Context (New):** If `identify()` was called previously, user data (userId,
|
|
185
|
+
* email, traits) is automatically included in the event context sent to all providers.
|
|
186
|
+
* This happens transparently - you don't need to pass user data manually.
|
|
187
|
+
*
|
|
164
188
|
* If providers are configured, the method waits for all providers to complete
|
|
165
189
|
* tracking. Failed providers don't prevent others from succeeding.
|
|
166
|
-
*
|
|
190
|
+
*
|
|
167
191
|
* @param eventName Name of the event to track (must match your event definitions)
|
|
168
192
|
* @param properties Event-specific properties and data
|
|
169
193
|
* @returns Promise that resolves when tracking is complete for all providers
|
|
@@ -176,7 +200,20 @@ class p {
|
|
|
176
200
|
* page: '/landing'
|
|
177
201
|
* });
|
|
178
202
|
* ```
|
|
179
|
-
*
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```typescript
|
|
206
|
+
* // User context is automatically included after identify()
|
|
207
|
+
* analytics.identify('user-123', {
|
|
208
|
+
* email: 'user@example.com',
|
|
209
|
+
* plan: 'pro'
|
|
210
|
+
* });
|
|
211
|
+
*
|
|
212
|
+
* // Now all events automatically include user context
|
|
213
|
+
* analytics.track('button_clicked', { buttonId: 'checkout' });
|
|
214
|
+
* // Providers receive: context.user = { userId: 'user-123', email: 'user@example.com', traits: {...} }
|
|
215
|
+
* ```
|
|
216
|
+
*
|
|
180
217
|
* @example
|
|
181
218
|
* ```typescript
|
|
182
219
|
* // Track a purchase event
|
|
@@ -191,14 +228,14 @@ class p {
|
|
|
191
228
|
* paymentMethod: 'credit_card'
|
|
192
229
|
* });
|
|
193
230
|
* ```
|
|
194
|
-
*
|
|
231
|
+
*
|
|
195
232
|
* @example
|
|
196
233
|
* ```typescript
|
|
197
234
|
* // Fire-and-forget for non-critical events (client-side typical usage)
|
|
198
235
|
* analytics.track('feature_viewed', { feature: 'dashboard' });
|
|
199
236
|
* // Don't await - let it track in the background
|
|
200
237
|
* ```
|
|
201
|
-
*
|
|
238
|
+
*
|
|
202
239
|
* @example
|
|
203
240
|
* ```typescript
|
|
204
241
|
* // Error handling
|
|
@@ -220,17 +257,24 @@ class p {
|
|
|
220
257
|
timestamp: Date.now(),
|
|
221
258
|
userId: this.userId,
|
|
222
259
|
sessionId: this.sessionId
|
|
223
|
-
}, o =
|
|
260
|
+
}, o = {
|
|
261
|
+
...this.context,
|
|
262
|
+
user: this.userId || this.userTraits ? {
|
|
263
|
+
userId: this.userId,
|
|
264
|
+
email: this.userTraits && "email" in this.userTraits ? this.userTraits.email : void 0,
|
|
265
|
+
traits: this.userTraits
|
|
266
|
+
} : void 0
|
|
267
|
+
}, l = this.providers.map(async (d) => {
|
|
224
268
|
try {
|
|
225
|
-
await d.track(r,
|
|
226
|
-
} catch (
|
|
269
|
+
await d.track(r, o);
|
|
270
|
+
} catch (c) {
|
|
227
271
|
console.error(
|
|
228
272
|
`[Analytics] Provider ${d.name} failed to track event:`,
|
|
229
|
-
|
|
273
|
+
c
|
|
230
274
|
);
|
|
231
275
|
}
|
|
232
276
|
});
|
|
233
|
-
await Promise.all(
|
|
277
|
+
await Promise.all(l);
|
|
234
278
|
}
|
|
235
279
|
/**
|
|
236
280
|
* Tracks a page view event.
|
|
@@ -420,7 +464,7 @@ class p {
|
|
|
420
464
|
* ```
|
|
421
465
|
*/
|
|
422
466
|
reset() {
|
|
423
|
-
this.userId = void 0, this.sessionId = this.generateSessionId();
|
|
467
|
+
this.userId = void 0, this.userTraits = void 0, this.sessionId = this.generateSessionId();
|
|
424
468
|
for (const e of this.providers)
|
|
425
469
|
e.reset();
|
|
426
470
|
}
|
|
@@ -526,53 +570,51 @@ class p {
|
|
|
526
570
|
return e.indexOf("Chrome") !== -1 ? "Chrome" : e.indexOf("Safari") !== -1 ? "Safari" : e.indexOf("Firefox") !== -1 ? "Firefox" : e.indexOf("Edge") !== -1 ? "Edge" : "Unknown";
|
|
527
571
|
}
|
|
528
572
|
}
|
|
529
|
-
let
|
|
530
|
-
function
|
|
531
|
-
if (
|
|
532
|
-
return console.warn("[Analytics] Already initialized"),
|
|
573
|
+
let n = null;
|
|
574
|
+
function v(t) {
|
|
575
|
+
if (n)
|
|
576
|
+
return console.warn("[Analytics] Already initialized"), n;
|
|
533
577
|
const e = {
|
|
534
578
|
providers: t.providers || [],
|
|
535
579
|
debug: t.debug,
|
|
536
580
|
enabled: t.enabled
|
|
537
581
|
};
|
|
538
|
-
return
|
|
539
|
-
e
|
|
540
|
-
), s.initialize().catch((i) => {
|
|
582
|
+
return n = new f(e), n.initialize().catch((i) => {
|
|
541
583
|
console.error("[Analytics] Failed to initialize:", i);
|
|
542
|
-
}),
|
|
584
|
+
}), n;
|
|
543
585
|
}
|
|
544
586
|
function a() {
|
|
545
|
-
if (!
|
|
587
|
+
if (!n)
|
|
546
588
|
throw new Error(
|
|
547
589
|
"[Analytics] Not initialized. Call createAnalytics() first."
|
|
548
590
|
);
|
|
549
|
-
return
|
|
591
|
+
return n;
|
|
550
592
|
}
|
|
551
|
-
function
|
|
593
|
+
function y(t, e) {
|
|
552
594
|
return a().track(t, e);
|
|
553
595
|
}
|
|
554
|
-
function
|
|
596
|
+
function w(t, e) {
|
|
555
597
|
a().identify(t, e);
|
|
556
598
|
}
|
|
557
|
-
function
|
|
599
|
+
function x(t) {
|
|
558
600
|
a().pageView(t);
|
|
559
601
|
}
|
|
560
602
|
function z(t) {
|
|
561
603
|
a().pageLeave(t);
|
|
562
604
|
}
|
|
563
|
-
function
|
|
605
|
+
function I() {
|
|
564
606
|
a().reset();
|
|
565
607
|
}
|
|
566
608
|
export {
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
609
|
+
C as BaseAnalyticsProvider,
|
|
610
|
+
f as BrowserAnalytics,
|
|
611
|
+
b as PostHogClientProvider,
|
|
612
|
+
v as createAnalytics,
|
|
613
|
+
v as createClientAnalytics,
|
|
572
614
|
a as getAnalytics,
|
|
573
|
-
|
|
615
|
+
w as identify,
|
|
574
616
|
z as pageLeave,
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
617
|
+
x as pageView,
|
|
618
|
+
I as reset,
|
|
619
|
+
y as track
|
|
578
620
|
};
|
|
@@ -8,7 +8,13 @@ export interface BaseEvent {
|
|
|
8
8
|
sessionId?: string;
|
|
9
9
|
properties?: Record<string, unknown>;
|
|
10
10
|
}
|
|
11
|
-
export interface
|
|
11
|
+
export interface UserContext<TTraits extends Record<string, unknown> = Record<string, unknown>> {
|
|
12
|
+
userId?: string;
|
|
13
|
+
email?: string;
|
|
14
|
+
traits?: TTraits;
|
|
15
|
+
}
|
|
16
|
+
export interface EventContext<TTraits extends Record<string, unknown> = Record<string, unknown>> {
|
|
17
|
+
user?: UserContext<TTraits>;
|
|
12
18
|
page?: {
|
|
13
19
|
path: string;
|
|
14
20
|
title?: string;
|