helm-analytics 1.1.0 → 5.0.1

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 (3) hide show
  1. package/README.md +34 -0
  2. package/index.js +67 -8
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -36,3 +36,37 @@ app.post('/purchase', (req, res) => {
36
36
  res.json({ success: true });
37
37
  });
38
38
  ```
39
+
40
+ ## Custom Event Tracking (NEW v5)
41
+
42
+ Track specific business events with custom properties:
43
+
44
+ ```javascript
45
+ app.post('/signup', (req, res) => {
46
+ // ... logic ...
47
+ helm.trackEvent(req, 'signup_success', { plan: 'growth', method: 'google' });
48
+ res.json({ status: 'ok' });
49
+ });
50
+ ```
51
+
52
+ ## Session Stitching
53
+
54
+ To link server-side events back to the browser session, ensure you pass the `sessionId` (retrieved from `sessionStorage.getItem('helm_session_id')`) in the `X-Helm-Session-Id` header of your frontend API calls.
55
+
56
+ ## Features
57
+
58
+ - **Non-blocking**: Uses non-blocking fetch calls for event ingestion.
59
+ - **Shield Mode**: Active defense against bots and malicious traffic.
60
+ - **Auto-UTM Extraction**: Automatically captures marketing campaign data from query strings.
61
+ - **Web Vitals**: Track LCP, CLS, and FID performance metrics from the server.
62
+ - **Custom Events**: Track business milestones beyond pageviews.
63
+ - **Session Stitching**: Maintain user journey continuity via headers.
64
+
65
+ ## Configuration
66
+
67
+ You can also set the Site ID and API URL via environment variables:
68
+
69
+ ```bash
70
+ export HELM_SITE_ID="your-uuid"
71
+ export HELM_API_URL="https://analytics.your-domain.com"
72
+ ```
package/index.js CHANGED
@@ -3,7 +3,9 @@ const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch
3
3
  class HelmAnalytics {
4
4
  constructor(options = {}) {
5
5
  this.siteId = options.siteId || process.env.HELM_SITE_ID;
6
- this.apiUrl = (options.apiUrl || 'https://api-sentinel.getmusterup.com').replace(/\/$/, '').replace(/\/track$/, '');
6
+ this.apiKey = options.apiKey || process.env.HELM_API_KEY;
7
+ const baseApi = options.apiUrl || process.env.HELM_API_URL || 'https://api.helm-analytics.com';
8
+ this.apiUrl = baseApi.replace(/\/$/, '').replace(/\/track$/, '');
7
9
 
8
10
  if (!this.siteId) {
9
11
  console.warn('HelmAnalytics: No Site ID provided. Tracking will be disabled.');
@@ -22,10 +24,15 @@ class HelmAnalytics {
22
24
  url: payload.url
23
25
  };
24
26
 
27
+ const headers = { 'Content-Type': 'application/json' };
28
+ if (this.apiKey) {
29
+ headers['Authorization'] = `Bearer ${this.apiKey}`;
30
+ }
31
+
25
32
  const res = await fetch(`${this.apiUrl}/api/shield/decision`, {
26
33
  method: 'POST',
27
34
  body: JSON.stringify(checkPayload),
28
- headers: { 'Content-Type': 'application/json' }
35
+ headers: headers
29
36
  });
30
37
 
31
38
  if (res.ok) {
@@ -42,19 +49,34 @@ class HelmAnalytics {
42
49
  }
43
50
 
44
51
  // Generic track method
45
- async track(req, eventType = 'pageview', metadata = {}, shield = false) {
52
+ async track(req, eventType = 'pageview', options = {}) {
46
53
  if (!this.siteId) return true;
54
+
55
+ const { metadata = {}, shield = false, pageTitle = '', screenWidth = 0, performance = {} } = options;
47
56
 
48
57
  try {
58
+ const url = req.originalUrl || req.url || '';
59
+ const query = req.query || {};
60
+
61
+ // Auto-extract UTMs
62
+ const utmParams = {};
63
+ Object.keys(query).forEach(key => {
64
+ if (key.startsWith('utm_')) utmParams[key] = query[key];
65
+ });
66
+
49
67
  const payload = {
50
68
  siteId: this.siteId,
51
- url: req.originalUrl || req.url,
69
+ sessionId: req.headers['x-helm-session-id'] || req.sessionId || '',
70
+ url: url,
52
71
  userAgent: req.headers['user-agent'] || '',
53
72
  referrer: req.headers['referer'] || req.headers['referrer'] || '',
54
- // ClientIP logic: Headers or socket
55
73
  clientIp: req.headers['x-forwarded-for'] ? req.headers['x-forwarded-for'].split(',')[0].trim() : (req.socket ? req.socket.remoteAddress : ''),
56
74
  eventType: eventType,
75
+ pageTitle: pageTitle,
76
+ screenWidth: screenWidth,
57
77
  isServerSide: true,
78
+ ...utmParams,
79
+ ...performance,
58
80
  ...metadata
59
81
  };
60
82
 
@@ -62,16 +84,21 @@ class HelmAnalytics {
62
84
  if (shield) {
63
85
  const { allowed, reason } = await this.checkShield(payload);
64
86
  if (!allowed) {
65
- console.warn(`Helm Shield Blocked: ${payload.clientIp} Reason: ${reason}`);
87
+ console.warn(`[Helm Shield] Blocked IP: ${payload.clientIp} Reason: ${reason}`);
66
88
  return false;
67
89
  }
68
90
  }
69
91
 
70
- // Fire and forget (don't await unless we want to ensure delivery, usually non-blocking is preferred for tracking)
92
+ const headers = { 'Content-Type': 'application/json' };
93
+ if (this.apiKey) {
94
+ headers['Authorization'] = `Bearer ${this.apiKey}`;
95
+ }
96
+
97
+ // Fire and forget
71
98
  fetch(`${this.apiUrl}/track`, {
72
99
  method: 'POST',
73
100
  body: JSON.stringify(payload),
74
- headers: { 'Content-Type': 'application/json' }
101
+ headers: headers
75
102
  }).catch(err => {});
76
103
 
77
104
  return true;
@@ -81,6 +108,38 @@ class HelmAnalytics {
81
108
  }
82
109
  }
83
110
 
111
+ // Custom event tracking
112
+ async trackEvent(req, eventName, properties = {}) {
113
+ if (!this.siteId) return true;
114
+
115
+ try {
116
+ const payload = {
117
+ siteId: this.siteId,
118
+ sessionId: req.headers['x-helm-session-id'] || req.sessionId || '',
119
+ eventName: eventName,
120
+ properties: properties,
121
+ url: req.originalUrl || req.url,
122
+ referrer: req.headers['referer'] || req.headers['referrer'] || '',
123
+ isServerSide: true
124
+ };
125
+
126
+ const headers = { 'Content-Type': 'application/json' };
127
+ if (this.apiKey) {
128
+ headers['Authorization'] = `Bearer ${this.apiKey}`;
129
+ }
130
+
131
+ fetch(`${this.apiUrl}/track/event`, {
132
+ method: 'POST',
133
+ body: JSON.stringify(payload),
134
+ headers: headers
135
+ }).catch(err => {});
136
+
137
+ return true;
138
+ } catch (err) {
139
+ return true;
140
+ }
141
+ }
142
+
84
143
  // Express Middleware
85
144
  middleware(options = {}) {
86
145
  const shield = options.shield || false;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "helm-analytics",
3
- "version": "1.1.0",
4
- "description": "Official Node.js Middleware for Helm Analytics",
3
+ "version": "5.0.1",
4
+ "description": "Official Node.js SDK for Helm Analytics",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
7
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -22,6 +22,6 @@
22
22
  },
23
23
  "repository": {
24
24
  "type": "git",
25
- "url": "https://github.com/Sentinel-Analytics/sentinel-mvp/tree/master/sdk/node"
25
+ "url": "https://github.com/Helm-Analytics/sentinel-mvp/tree/main/sdk/node"
26
26
  }
27
27
  }