@stacksee/analytics 0.4.5 → 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.
@@ -8,7 +8,13 @@ export interface BaseEvent {
8
8
  sessionId?: string;
9
9
  properties?: Record<string, unknown>;
10
10
  }
11
- export interface EventContext {
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;
@@ -1,5 +1,5 @@
1
1
  import { B as e } from "../base.provider-AfFL5W_P.js";
2
- import { P as t } from "../client-RZPcOfAk.js";
2
+ import { P as t } from "../client-DTHZYkxx.js";
3
3
  export {
4
4
  e as BaseAnalyticsProvider,
5
5
  t as PostHogClientProvider
@@ -1,5 +1,5 @@
1
1
  import { B as e } from "../base.provider-AfFL5W_P.js";
2
- import { P as a } from "../server-CMRw9K0d.js";
2
+ import { P as a } from "../server-DjEk1fUD.js";
3
3
  export {
4
4
  e as BaseAnalyticsProvider,
5
5
  a as PostHogServerProvider
@@ -0,0 +1,88 @@
1
+ var p = Object.defineProperty;
2
+ var u = (r, e, s) => e in r ? p(r, e, { enumerable: !0, configurable: !0, writable: !0, value: s }) : r[e] = s;
3
+ var a = (r, e, s) => u(r, typeof e != "symbol" ? e + "" : e, s);
4
+ import { B as g } from "./base.provider-AfFL5W_P.js";
5
+ import { PostHog as n } from "posthog-node";
6
+ class y extends g {
7
+ constructor(s) {
8
+ super({ debug: s.debug, enabled: s.enabled });
9
+ a(this, "name", "PostHog-Server");
10
+ a(this, "client");
11
+ a(this, "initialized", !1);
12
+ a(this, "config");
13
+ this.config = s;
14
+ }
15
+ initialize() {
16
+ if (this.isEnabled() && !this.initialized) {
17
+ if (!this.config.apiKey || typeof this.config.apiKey != "string")
18
+ throw new Error("PostHog requires an apiKey");
19
+ try {
20
+ const { apiKey: s, ...i } = this.config;
21
+ this.client = new n(s, {
22
+ host: "https://app.posthog.com",
23
+ flushAt: 20,
24
+ flushInterval: 1e4,
25
+ ...i
26
+ }), this.initialized = !0, this.log("Initialized successfully", this.config);
27
+ } catch (s) {
28
+ throw console.error("[PostHog-Server] Failed to initialize:", s), s;
29
+ }
30
+ }
31
+ }
32
+ identify(s, i) {
33
+ !this.isEnabled() || !this.initialized || !this.client || (this.client.identify({
34
+ distinctId: s,
35
+ properties: i
36
+ }), this.log("Identified user", { userId: s, traits: i }));
37
+ }
38
+ track(s, i) {
39
+ var t, h, d;
40
+ if (!this.isEnabled() || !this.initialized || !this.client) return;
41
+ const l = {
42
+ ...s.properties,
43
+ category: s.category,
44
+ timestamp: s.timestamp ? new Date(s.timestamp) : /* @__PURE__ */ new Date(),
45
+ ...s.sessionId && { sessionId: s.sessionId },
46
+ ...(i == null ? void 0 : i.page) && {
47
+ $current_url: i.page.path,
48
+ $page_title: i.page.title,
49
+ $referrer: i.page.referrer
50
+ },
51
+ ...(i == null ? void 0 : i.device) && { device: i.device },
52
+ ...(i == null ? void 0 : i.utm) && { utm: i.utm },
53
+ // Include user email and traits as regular event properties
54
+ ...((t = i == null ? void 0 : i.user) == null ? void 0 : t.email) && { user_email: i.user.email },
55
+ ...((h = i == null ? void 0 : i.user) == null ? void 0 : h.traits) && { user_traits: i.user.traits }
56
+ };
57
+ this.client.capture({
58
+ distinctId: s.userId || ((d = i == null ? void 0 : i.user) == null ? void 0 : d.userId) || "anonymous",
59
+ event: s.action,
60
+ properties: l
61
+ }), this.log("Tracked event", { event: s, context: i });
62
+ }
63
+ pageView(s, i) {
64
+ if (!this.isEnabled() || !this.initialized || !this.client) return;
65
+ const l = {
66
+ ...s,
67
+ ...(i == null ? void 0 : i.page) && {
68
+ path: i.page.path,
69
+ title: i.page.title,
70
+ referrer: i.page.referrer
71
+ }
72
+ };
73
+ this.client.capture({
74
+ distinctId: "anonymous",
75
+ event: "$pageview",
76
+ properties: l
77
+ }), this.log("Tracked page view", { properties: s, context: i });
78
+ }
79
+ async reset() {
80
+ !this.isEnabled() || !this.initialized || !this.client || (await this.client.flush(), this.log("Flushed pending events"));
81
+ }
82
+ async shutdown() {
83
+ this.client && (await this.client.shutdown(), this.log("Shutdown complete"));
84
+ }
85
+ }
86
+ export {
87
+ y as P
88
+ };
package/dist/server.d.ts CHANGED
@@ -34,5 +34,5 @@ export interface ServerAnalyticsConfig {
34
34
  * }, { userId: 'user-123' });
35
35
  * ```
36
36
  */
37
- export declare function createServerAnalytics<TEvents = never>(config: ServerAnalyticsConfig): ServerAnalytics<EventMapFromCollection<TEvents>>;
37
+ export declare function createServerAnalytics<TEvents = never, TUserTraits extends Record<string, unknown> = Record<string, unknown>>(config: ServerAnalyticsConfig): ServerAnalytics<EventMapFromCollection<TEvents>, TUserTraits>;
38
38
  export { ServerAnalytics };
package/dist/server.js CHANGED
@@ -1,9 +1,9 @@
1
- var l = Object.defineProperty;
2
- var f = (r, e, t) => e in r ? l(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t;
3
- var s = (r, e, t) => f(r, typeof e != "symbol" ? e + "" : e, t);
4
- import { P as m } from "./server-CMRw9K0d.js";
5
- import { B as x } from "./base.provider-AfFL5W_P.js";
6
- class u {
1
+ var u = Object.defineProperty;
2
+ var o = (i, e, r) => e in i ? u(i, e, { enumerable: !0, configurable: !0, writable: !0, value: r }) : i[e] = r;
3
+ var s = (i, e, r) => o(i, typeof e != "symbol" ? e + "" : e, r);
4
+ import { P as w } from "./server-DjEk1fUD.js";
5
+ import { B as z } from "./base.provider-AfFL5W_P.js";
6
+ class v {
7
7
  /**
8
8
  * Creates a new ServerAnalytics instance for server-side event tracking.
9
9
  *
@@ -129,28 +129,33 @@ class u {
129
129
  * }
130
130
  * ```
131
131
  */
132
- identify(e, t) {
133
- for (const i of this.providers)
134
- i.identify(e, t);
132
+ identify(e, r) {
133
+ for (const t of this.providers)
134
+ t.identify(e, r);
135
135
  }
136
136
  /**
137
137
  * Tracks a custom event with properties and optional context.
138
- *
138
+ *
139
139
  * This is the main method for tracking business events on the server side.
140
140
  * The method sends the event to all configured providers and waits for completion.
141
141
  * Failed providers don't prevent others from succeeding.
142
- *
142
+ *
143
143
  * Server-side tracking typically includes additional context like IP addresses,
144
144
  * user agents, and server-specific metadata that isn't available on the client.
145
- *
145
+ *
146
+ * **User Context (New):** You can now pass user data (email, traits) with each event
147
+ * via `options.user` or `options.context.user`. This is useful for providers like
148
+ * Loops, Customer.io, or Intercom that require user identifiers.
149
+ *
146
150
  * @param eventName Name of the event to track (must match your event definitions)
147
151
  * @param properties Event-specific properties and data
148
- * @param options Optional configuration including user ID, session ID, and context
152
+ * @param options Optional configuration including user ID, session ID, user context, and additional context
149
153
  * @param options.userId User ID to associate with this event
150
154
  * @param options.sessionId Session ID to associate with this event
151
- * @param options.context Additional context for this event
155
+ * @param options.user User context including email and traits (automatically included in event context)
156
+ * @param options.context Additional context for this event (page, device, etc.)
152
157
  * @returns Promise that resolves when tracking is complete for all providers
153
- *
158
+ *
154
159
  * @example
155
160
  * ```typescript
156
161
  * // Basic event tracking
@@ -161,10 +166,10 @@ class u {
161
166
  * statusCode: 200
162
167
  * });
163
168
  * ```
164
- *
169
+ *
165
170
  * @example
166
171
  * ```typescript
167
- * // Track with user context
172
+ * // Track with user context (recommended for email-based providers)
168
173
  * await analytics.track('purchase_completed', {
169
174
  * orderId: 'order-123',
170
175
  * amount: 99.99,
@@ -172,40 +177,68 @@ class u {
172
177
  * itemCount: 3
173
178
  * }, {
174
179
  * userId: 'user-456',
175
- * sessionId: 'session-789',
180
+ * user: {
181
+ * email: 'user@example.com',
182
+ * traits: {
183
+ * plan: 'pro',
184
+ * company: 'Acme Corp'
185
+ * }
186
+ * },
176
187
  * context: {
177
188
  * page: { path: '/checkout/complete' },
178
- * device: { userAgent: req.headers['user-agent'] },
179
- * ip: req.ip
189
+ * device: { userAgent: req.headers['user-agent'] }
180
190
  * }
181
191
  * });
192
+ * // Providers receive: context.user = { email: 'user@example.com', traits: {...} }
182
193
  * ```
183
- *
194
+ *
195
+ * @example
196
+ * ```typescript
197
+ * // Alternative: Pass user via context.user
198
+ * await analytics.track('feature_used', {
199
+ * featureName: 'export'
200
+ * }, {
201
+ * userId: 'user-123',
202
+ * context: {
203
+ * user: {
204
+ * email: 'user@example.com'
205
+ * },
206
+ * page: { path: '/dashboard' }
207
+ * }
208
+ * });
209
+ * ```
210
+ *
184
211
  * @example
185
212
  * ```typescript
186
- * // In an Express.js route handler
213
+ * // In an Express.js route handler with user data
187
214
  * app.post('/api/users', async (req, res) => {
188
215
  * const user = await createUser(req.body);
189
- *
190
- * // Track user creation with server context
216
+ *
217
+ * // Track user creation with full user context
191
218
  * await analytics.track('user_created', {
192
219
  * userId: user.id,
193
220
  * email: user.email,
194
221
  * plan: user.plan
195
222
  * }, {
196
223
  * userId: user.id,
224
+ * user: {
225
+ * email: user.email,
226
+ * traits: {
227
+ * name: user.name,
228
+ * plan: user.plan,
229
+ * company: user.company
230
+ * }
231
+ * },
197
232
  * context: {
198
233
  * page: { path: req.path },
199
- * device: { userAgent: req.headers['user-agent'] },
200
- * ip: req.ip,
201
- * server: { version: process.env.APP_VERSION }
234
+ * device: { userAgent: req.headers['user-agent'] }
202
235
  * }
203
236
  * });
204
- *
237
+ *
205
238
  * res.json(user);
206
239
  * });
207
240
  * ```
208
- *
241
+ *
209
242
  * @example
210
243
  * ```typescript
211
244
  * // Error handling in tracking
@@ -221,7 +254,8 @@ class u {
221
254
  * }
222
255
  * ```
223
256
  */
224
- async track(e, t, i) {
257
+ async track(e, r, t) {
258
+ var n;
225
259
  if (!this.initialized) {
226
260
  console.warn("[Analytics] Not initialized. Call initialize() first.");
227
261
  return;
@@ -229,24 +263,25 @@ class u {
229
263
  const a = {
230
264
  action: e,
231
265
  category: this.getCategoryFromEventName(e),
232
- properties: t,
266
+ properties: r,
233
267
  timestamp: Date.now(),
234
- userId: i == null ? void 0 : i.userId,
235
- sessionId: i == null ? void 0 : i.sessionId
236
- }, c = {
268
+ userId: t == null ? void 0 : t.userId,
269
+ sessionId: t == null ? void 0 : t.sessionId
270
+ }, d = {
237
271
  ...this.config.defaultContext,
238
- ...i == null ? void 0 : i.context
239
- }, o = this.providers.map(async (n) => {
272
+ ...t == null ? void 0 : t.context,
273
+ user: (t == null ? void 0 : t.user) || ((n = t == null ? void 0 : t.context) == null ? void 0 : n.user)
274
+ }, l = this.providers.map(async (c) => {
240
275
  try {
241
- await n.track(a, c);
242
- } catch (d) {
276
+ await c.track(a, d);
277
+ } catch (f) {
243
278
  console.error(
244
- `[Analytics] Provider ${n.name} failed to track event:`,
245
- d
279
+ `[Analytics] Provider ${c.name} failed to track event:`,
280
+ f
246
281
  );
247
282
  }
248
283
  });
249
- await Promise.all(o);
284
+ await Promise.all(l);
250
285
  }
251
286
  /**
252
287
  * Tracks a page view event from the server side.
@@ -308,14 +343,14 @@ class u {
308
343
  * }
309
344
  * ```
310
345
  */
311
- pageView(e, t) {
346
+ pageView(e, r) {
312
347
  if (!this.initialized) return;
313
- const i = {
348
+ const t = {
314
349
  ...this.config.defaultContext,
315
- ...t == null ? void 0 : t.context
350
+ ...r == null ? void 0 : r.context
316
351
  };
317
352
  for (const a of this.providers)
318
- a.pageView(e, i);
353
+ a.pageView(e, t);
319
354
  }
320
355
  /**
321
356
  * Tracks when a user leaves a page from the server side.
@@ -376,14 +411,14 @@ class u {
376
411
  * }
377
412
  * ```
378
413
  */
379
- pageLeave(e, t) {
414
+ pageLeave(e, r) {
380
415
  if (!this.initialized) return;
381
- const i = {
416
+ const t = {
382
417
  ...this.config.defaultContext,
383
- ...t == null ? void 0 : t.context
418
+ ...r == null ? void 0 : r.context
384
419
  };
385
420
  for (const a of this.providers)
386
- a.pageLeave && a.pageLeave(e, i);
421
+ a.pageLeave && a.pageLeave(e, t);
387
422
  }
388
423
  /**
389
424
  * Shuts down all analytics providers and flushes pending events.
@@ -470,27 +505,25 @@ class u {
470
505
  * ```
471
506
  */
472
507
  async shutdown() {
473
- const e = this.providers.map((t) => "shutdown" in t && typeof t.shutdown == "function" ? t.shutdown() : Promise.resolve());
508
+ const e = this.providers.map((r) => "shutdown" in r && typeof r.shutdown == "function" ? r.shutdown() : Promise.resolve());
474
509
  await Promise.all(e);
475
510
  }
476
511
  getCategoryFromEventName(e) {
477
- const t = e.split("_");
478
- return t.length > 1 && t[0] ? t[0] : "engagement";
512
+ const r = e.split("_");
513
+ return r.length > 1 && r[0] ? r[0] : "engagement";
479
514
  }
480
515
  }
481
- function h(r) {
516
+ function g(i) {
482
517
  const e = {
483
- providers: r.providers || [],
484
- debug: r.debug,
485
- enabled: r.enabled
486
- }, t = new u(
487
- e
488
- );
489
- return t.initialize(), t;
518
+ providers: i.providers || [],
519
+ debug: i.debug,
520
+ enabled: i.enabled
521
+ }, r = new v(e);
522
+ return r.initialize(), r;
490
523
  }
491
524
  export {
492
- x as BaseAnalyticsProvider,
493
- m as PostHogServerProvider,
494
- u as ServerAnalytics,
495
- h as createServerAnalytics
525
+ z as BaseAnalyticsProvider,
526
+ w as PostHogServerProvider,
527
+ v as ServerAnalytics,
528
+ g as createServerAnalytics
496
529
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stacksee/analytics",
3
- "version": "0.4.5",
3
+ "version": "0.5.0",
4
4
  "description": "A highly typed, provider-agnostic analytics library for TypeScript applications",
5
5
  "type": "module",
6
6
  "exports": {