@stacksee/analytics 0.9.7 → 0.10.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/server.js CHANGED
@@ -1,24 +1,24 @@
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);
1
+ var h = Object.defineProperty;
2
+ var f = (a, r, e) => r in a ? h(a, r, { enumerable: !0, configurable: !0, writable: !0, value: e }) : a[r] = e;
3
+ var o = (a, r, e) => f(a, typeof r != "symbol" ? r + "" : r, e);
4
4
  import { P as w } from "./server-DjEk1fUD.js";
5
- import { B as z } from "./base.provider-AfFL5W_P.js";
5
+ import { B as x } from "./base.provider-AfFL5W_P.js";
6
6
  class v {
7
7
  /**
8
8
  * Creates a new ServerAnalytics instance for server-side event tracking.
9
- *
9
+ *
10
10
  * The server analytics instance is designed for Node.js environments including
11
11
  * long-running servers, serverless functions, and edge computing environments.
12
- *
12
+ *
13
13
  * @param config Analytics configuration including providers and default context
14
14
  * @param config.providers Array of analytics provider instances (e.g., PostHogServerProvider)
15
15
  * @param config.defaultContext Optional default context to include with all events
16
- *
16
+ *
17
17
  * @example
18
18
  * ```typescript
19
19
  * import { ServerAnalytics } from '@stacksee/analytics/server';
20
20
  * import { PostHogServerProvider } from '@stacksee/analytics/providers/posthog';
21
- *
21
+ *
22
22
  * const analytics = new ServerAnalytics({
23
23
  * providers: [
24
24
  * new PostHogServerProvider({
@@ -30,75 +30,117 @@ class v {
30
30
  * app: { version: '1.0.0', environment: 'production' }
31
31
  * }
32
32
  * });
33
- *
33
+ *
34
34
  * analytics.initialize();
35
35
  * ```
36
36
  */
37
- constructor(e) {
38
- s(this, "providers", []);
39
- s(this, "config");
40
- s(this, "initialized", !1);
41
- this.config = e, this.providers = e.providers;
37
+ constructor(r) {
38
+ o(this, "providerConfigs", []);
39
+ o(this, "config");
40
+ o(this, "initialized", !1);
41
+ this.config = r, this.providerConfigs = this.normalizeProviders(r.providers);
42
+ }
43
+ /**
44
+ * Normalizes provider configurations into a consistent internal format
45
+ */
46
+ normalizeProviders(r) {
47
+ const e = [
48
+ "initialize",
49
+ "identify",
50
+ "track",
51
+ "pageView",
52
+ "pageLeave",
53
+ "reset"
54
+ ];
55
+ return r.map((i) => {
56
+ if ("initialize" in i && "track" in i)
57
+ return {
58
+ provider: i,
59
+ enabledMethods: new Set(e)
60
+ };
61
+ const t = i;
62
+ t.methods && t.exclude && console.warn(
63
+ `[Analytics] Provider ${t.provider.name} has both 'methods' and 'exclude' specified. Using 'methods' and ignoring 'exclude'.`
64
+ );
65
+ let n;
66
+ return t.methods ? n = new Set(t.methods) : t.exclude ? n = new Set(
67
+ e.filter(
68
+ (l) => {
69
+ var d;
70
+ return !((d = t.exclude) != null && d.includes(l));
71
+ }
72
+ )
73
+ ) : n = new Set(e), {
74
+ provider: t.provider,
75
+ enabledMethods: n
76
+ };
77
+ });
78
+ }
79
+ /**
80
+ * Checks if a method should be called on a provider based on routing configuration
81
+ */
82
+ shouldCallMethod(r, e) {
83
+ return r.enabledMethods.has(e);
42
84
  }
43
85
  /**
44
86
  * Initializes all analytics providers.
45
- *
87
+ *
46
88
  * This method must be called before tracking events. It initializes all configured
47
89
  * providers synchronously. Unlike the browser version, server initialization is
48
90
  * typically synchronous as providers don't need to load external scripts.
49
- *
91
+ *
50
92
  * The method is safe to call multiple times and will not re-initialize if already done.
51
- *
93
+ *
52
94
  * @example
53
95
  * ```typescript
54
96
  * const analytics = new ServerAnalytics({ providers: [] });
55
- *
97
+ *
56
98
  * // Initialize before tracking events
57
99
  * analytics.initialize();
58
- *
100
+ *
59
101
  * // Now ready to track events
60
102
  * await analytics.track('api_request', { endpoint: '/users' });
61
103
  * ```
62
- *
104
+ *
63
105
  * @example
64
106
  * ```typescript
65
107
  * // In a serverless function
66
108
  * export async function handler(req, res) {
67
109
  * const analytics = new ServerAnalytics({ providers: [] });
68
110
  * analytics.initialize(); // Quick synchronous initialization
69
- *
111
+ *
70
112
  * await analytics.track('function_invoked', {
71
113
  * path: req.path,
72
114
  * method: req.method
73
115
  * });
74
- *
116
+ *
75
117
  * await analytics.shutdown(); // Important for serverless
76
118
  * }
77
119
  * ```
78
120
  */
79
121
  initialize() {
80
122
  if (!this.initialized) {
81
- for (const e of this.providers)
82
- e.initialize();
123
+ for (const r of this.providerConfigs)
124
+ r.provider.initialize();
83
125
  this.initialized = !0;
84
126
  }
85
127
  }
86
128
  /**
87
129
  * Identifies a user with optional traits.
88
- *
130
+ *
89
131
  * Associates subsequent events with the specified user ID and optionally
90
132
  * sets user properties. This method is typically called when processing
91
133
  * authentication or when you have user context available on the server.
92
- *
134
+ *
93
135
  * @param userId Unique identifier for the user (e.g., database ID, email)
94
136
  * @param traits Optional user properties and characteristics
95
- *
137
+ *
96
138
  * @example
97
139
  * ```typescript
98
140
  * // Basic user identification
99
141
  * analytics.identify('user-123');
100
142
  * ```
101
- *
143
+ *
102
144
  * @example
103
145
  * ```typescript
104
146
  * // Identify with user traits from database
@@ -111,27 +153,27 @@ class v {
111
153
  * lastSeenAt: new Date().toISOString()
112
154
  * });
113
155
  * ```
114
- *
156
+ *
115
157
  * @example
116
158
  * ```typescript
117
159
  * // In an API authentication middleware
118
160
  * async function authMiddleware(req, res, next) {
119
161
  * const user = await getUserFromToken(req.headers.authorization);
120
- *
162
+ *
121
163
  * analytics.identify(user.id, {
122
164
  * email: user.email,
123
165
  * role: user.role,
124
166
  * organization: user.organization
125
167
  * });
126
- *
168
+ *
127
169
  * req.user = user;
128
170
  * next();
129
171
  * }
130
172
  * ```
131
173
  */
132
- identify(e, r) {
133
- for (const t of this.providers)
134
- t.identify(e, r);
174
+ identify(r, e) {
175
+ for (const i of this.providerConfigs)
176
+ this.shouldCallMethod(i, "identify") && i.provider.identify(r, e);
135
177
  }
136
178
  /**
137
179
  * Tracks a custom event with properties and optional context.
@@ -254,30 +296,30 @@ class v {
254
296
  * }
255
297
  * ```
256
298
  */
257
- async track(e, r, t) {
258
- var n;
299
+ async track(r, e, i) {
300
+ var d;
259
301
  if (!this.initialized) {
260
302
  console.warn("[Analytics] Not initialized. Call initialize() first.");
261
303
  return;
262
304
  }
263
- const a = {
264
- action: e,
265
- category: this.getCategoryFromEventName(e),
266
- properties: r,
305
+ const t = {
306
+ action: r,
307
+ category: this.getCategoryFromEventName(r),
308
+ properties: e,
267
309
  timestamp: Date.now(),
268
- userId: t == null ? void 0 : t.userId,
269
- sessionId: t == null ? void 0 : t.sessionId
270
- }, d = {
310
+ userId: i == null ? void 0 : i.userId,
311
+ sessionId: i == null ? void 0 : i.sessionId
312
+ }, n = {
271
313
  ...this.config.defaultContext,
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) => {
314
+ ...i == null ? void 0 : i.context,
315
+ user: (i == null ? void 0 : i.user) || ((d = i == null ? void 0 : i.context) == null ? void 0 : d.user)
316
+ }, l = this.providerConfigs.filter((s) => this.shouldCallMethod(s, "track")).map(async (s) => {
275
317
  try {
276
- await c.track(a, d);
277
- } catch (f) {
318
+ await s.provider.track(t, n);
319
+ } catch (c) {
278
320
  console.error(
279
- `[Analytics] Provider ${c.name} failed to track event:`,
280
- f
321
+ `[Analytics] Provider ${s.provider.name} failed to track event:`,
322
+ c
281
323
  );
282
324
  }
283
325
  });
@@ -285,21 +327,21 @@ class v {
285
327
  }
286
328
  /**
287
329
  * Tracks a page view event from the server side.
288
- *
330
+ *
289
331
  * Server-side page view tracking is useful for server-rendered applications,
290
332
  * SSR frameworks, or when you want to ensure page views are tracked even
291
333
  * if client-side JavaScript fails.
292
- *
334
+ *
293
335
  * @param properties Optional properties to include with the page view
294
336
  * @param options Optional configuration including context
295
337
  * @param options.context Additional context for this page view
296
- *
338
+ *
297
339
  * @example
298
340
  * ```typescript
299
341
  * // Basic server-side page view
300
342
  * analytics.pageView();
301
343
  * ```
302
- *
344
+ *
303
345
  * @example
304
346
  * ```typescript
305
347
  * // Page view with server context
@@ -323,7 +365,7 @@ class v {
323
365
  * }
324
366
  * });
325
367
  * ```
326
- *
368
+ *
327
369
  * @example
328
370
  * ```typescript
329
371
  * // In a Next.js API route or middleware
@@ -343,35 +385,35 @@ class v {
343
385
  * }
344
386
  * ```
345
387
  */
346
- pageView(e, r) {
388
+ pageView(r, e) {
347
389
  if (!this.initialized) return;
348
- const t = {
390
+ const i = {
349
391
  ...this.config.defaultContext,
350
- ...r == null ? void 0 : r.context
392
+ ...e == null ? void 0 : e.context
351
393
  };
352
- for (const a of this.providers)
353
- a.pageView(e, t);
394
+ for (const t of this.providerConfigs)
395
+ this.shouldCallMethod(t, "pageView") && t.provider.pageView(r, i);
354
396
  }
355
397
  /**
356
398
  * Tracks when a user leaves a page from the server side.
357
- *
399
+ *
358
400
  * Server-side page leave tracking is less common than client-side but can be
359
401
  * useful in certain scenarios like tracking session timeouts, or when combined
360
402
  * with server-side session management.
361
- *
403
+ *
362
404
  * Note: Not all analytics providers support page leave events. The method
363
405
  * will only call providers that implement the pageLeave method.
364
- *
406
+ *
365
407
  * @param properties Optional properties to include with the page leave event
366
408
  * @param options Optional configuration including context
367
409
  * @param options.context Additional context for this page leave
368
- *
410
+ *
369
411
  * @example
370
412
  * ```typescript
371
413
  * // Basic page leave tracking
372
414
  * analytics.pageLeave();
373
415
  * ```
374
- *
416
+ *
375
417
  * @example
376
418
  * ```typescript
377
419
  * // Page leave with session context
@@ -392,63 +434,63 @@ class v {
392
434
  * }
393
435
  * });
394
436
  * ```
395
- *
437
+ *
396
438
  * @example
397
439
  * ```typescript
398
440
  * // In a session cleanup job
399
441
  * async function cleanupExpiredSessions() {
400
442
  * const expiredSessions = await getExpiredSessions();
401
- *
443
+ *
402
444
  * for (const session of expiredSessions) {
403
445
  * analytics.pageLeave({
404
446
  * sessionId: session.id,
405
447
  * duration: session.duration,
406
448
  * reason: 'expired'
407
449
  * });
408
- *
450
+ *
409
451
  * await removeSession(session.id);
410
452
  * }
411
453
  * }
412
454
  * ```
413
455
  */
414
- pageLeave(e, r) {
456
+ pageLeave(r, e) {
415
457
  if (!this.initialized) return;
416
- const t = {
458
+ const i = {
417
459
  ...this.config.defaultContext,
418
- ...r == null ? void 0 : r.context
460
+ ...e == null ? void 0 : e.context
419
461
  };
420
- for (const a of this.providers)
421
- a.pageLeave && a.pageLeave(e, t);
462
+ for (const t of this.providerConfigs)
463
+ this.shouldCallMethod(t, "pageLeave") && t.provider.pageLeave && t.provider.pageLeave(r, i);
422
464
  }
423
465
  /**
424
466
  * Shuts down all analytics providers and flushes pending events.
425
- *
467
+ *
426
468
  * This method is crucial for server environments, especially serverless functions,
427
469
  * as it ensures all events are sent before the process terminates. Some providers
428
470
  * batch events and need an explicit flush to send them.
429
- *
471
+ *
430
472
  * Always call this method before your server shuts down or before a serverless
431
473
  * function completes execution.
432
- *
474
+ *
433
475
  * @returns Promise that resolves when all providers have been shut down
434
- *
476
+ *
435
477
  * @example
436
478
  * ```typescript
437
479
  * // Basic shutdown
438
480
  * await analytics.shutdown();
439
481
  * ```
440
- *
482
+ *
441
483
  * @example
442
484
  * ```typescript
443
485
  * // In a serverless function
444
486
  * export async function handler(event, context) {
445
487
  * const analytics = new ServerAnalytics({ providers: [] });
446
488
  * analytics.initialize();
447
- *
489
+ *
448
490
  * try {
449
491
  * // Process the event
450
492
  * await processEvent(event);
451
- *
493
+ *
452
494
  * // Track completion
453
495
  * await analytics.track('function_completed', {
454
496
  * duration: Date.now() - startTime,
@@ -463,20 +505,20 @@ class v {
463
505
  * // Always shutdown to flush events
464
506
  * await analytics.shutdown();
465
507
  * }
466
- *
508
+ *
467
509
  * return { statusCode: 200 };
468
510
  * }
469
511
  * ```
470
- *
512
+ *
471
513
  * @example
472
514
  * ```typescript
473
515
  * // In an Express.js server
474
516
  * const server = app.listen(3000);
475
- *
517
+ *
476
518
  * // Graceful shutdown
477
519
  * process.on('SIGTERM', async () => {
478
520
  * console.log('Shutting down gracefully...');
479
- *
521
+ *
480
522
  * server.close(async () => {
481
523
  * // Flush analytics events before exit
482
524
  * await analytics.shutdown();
@@ -484,45 +526,45 @@ class v {
484
526
  * });
485
527
  * });
486
528
  * ```
487
- *
529
+ *
488
530
  * @example
489
531
  * ```typescript
490
532
  * // With Vercel's waitUntil
491
533
  * import { waitUntil } from '@vercel/functions';
492
- *
534
+ *
493
535
  * export default async function handler(req, res) {
494
536
  * // Process request
495
537
  * const result = await processRequest(req);
496
- *
538
+ *
497
539
  * // Track in background without blocking response
498
540
  * waitUntil(
499
541
  * analytics.track('api_request', { endpoint: req.url })
500
542
  * .then(() => analytics.shutdown())
501
543
  * );
502
- *
544
+ *
503
545
  * return res.json(result);
504
546
  * }
505
547
  * ```
506
548
  */
507
549
  async shutdown() {
508
- const e = this.providers.map((r) => "shutdown" in r && typeof r.shutdown == "function" ? r.shutdown() : Promise.resolve());
509
- await Promise.all(e);
550
+ const r = this.providerConfigs.map((e) => "shutdown" in e.provider && typeof e.provider.shutdown == "function" ? e.provider.shutdown() : Promise.resolve());
551
+ await Promise.all(r);
510
552
  }
511
- getCategoryFromEventName(e) {
512
- const r = e.split("_");
513
- return r.length > 1 && r[0] ? r[0] : "engagement";
553
+ getCategoryFromEventName(r) {
554
+ const e = r.split("_");
555
+ return e.length > 1 && e[0] ? e[0] : "engagement";
514
556
  }
515
557
  }
516
- function g(i) {
517
- const e = {
518
- providers: i.providers || [],
519
- debug: i.debug,
520
- enabled: i.enabled
521
- }, r = new v(e);
522
- return r.initialize(), r;
558
+ function g(a) {
559
+ const r = {
560
+ providers: a.providers || [],
561
+ debug: a.debug,
562
+ enabled: a.enabled
563
+ }, e = new v(r);
564
+ return e.initialize(), e;
523
565
  }
524
566
  export {
525
- z as BaseAnalyticsProvider,
567
+ x as BaseAnalyticsProvider,
526
568
  w as PostHogServerProvider,
527
569
  v as ServerAnalytics,
528
570
  g as createServerAnalytics
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stacksee/analytics",
3
- "version": "0.9.7",
3
+ "version": "0.10.0",
4
4
  "description": "A highly typed, provider-agnostic analytics library for TypeScript applications",
5
5
  "type": "module",
6
6
  "exports": {