helm-analytics 1.0.0 → 5.0.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.
Files changed (3) hide show
  1. package/README.md +25 -2
  2. package/index.js +85 -13
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -17,8 +17,8 @@ const HelmAnalytics = require('helm-analytics');
17
17
  const app = express();
18
18
  const helm = new HelmAnalytics({ siteId: 'YOUR_SITE_ID' });
19
19
 
20
- // Use as middleware for all routes
21
- app.use(helm.middleware());
20
+ // Use as middleware (with optional Shield filtering)
21
+ app.use(helm.middleware({ shield: true })); // Set to true to block requests from bad actors
22
22
 
23
23
  app.get('/', (req, res) => {
24
24
  res.send('Hello World');
@@ -36,3 +36,26 @@ 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
+ - **Custom Events**: Track business milestones beyond pageviews.
61
+ - **Session Stitching**: Maintain user journey continuity via headers.
package/index.js CHANGED
@@ -3,20 +3,52 @@ 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/track';
6
+ this.apiUrl = (options.apiUrl || 'https://api-sentinel.getmusterup.com').replace(/\/$/, '').replace(/\/track$/, '');
7
7
 
8
8
  if (!this.siteId) {
9
9
  console.warn('HelmAnalytics: No Site ID provided. Tracking will be disabled.');
10
10
  }
11
11
  }
12
12
 
13
+ // Check if request should be blocked
14
+ async checkShield(payload) {
15
+ if (!this.siteId) return { allowed: true };
16
+
17
+ try {
18
+ const checkPayload = {
19
+ siteId: payload.siteId,
20
+ ip: payload.clientIp,
21
+ userAgent: payload.userAgent,
22
+ url: payload.url
23
+ };
24
+
25
+ const res = await fetch(`${this.apiUrl}/api/shield/decision`, {
26
+ method: 'POST',
27
+ body: JSON.stringify(checkPayload),
28
+ headers: { 'Content-Type': 'application/json' }
29
+ });
30
+
31
+ if (res.ok) {
32
+ const decision = await res.json();
33
+ if (decision.action === 'block') {
34
+ return { allowed: false, reason: decision.reason };
35
+ }
36
+ }
37
+ } catch (err) {
38
+ // Fail open
39
+ console.error('Helm Shield Error:', err);
40
+ }
41
+ return { allowed: true };
42
+ }
43
+
13
44
  // Generic track method
14
- async track(req, eventType = 'pageview', metadata = {}) {
15
- if (!this.siteId) return;
45
+ async track(req, eventType = 'pageview', metadata = {}, shield = false) {
46
+ if (!this.siteId) return true;
16
47
 
17
48
  try {
18
49
  const payload = {
19
50
  siteId: this.siteId,
51
+ sessionId: req.headers['x-helm-session-id'] || req.sessionId || '', // Support session stitching
20
52
  url: req.originalUrl || req.url,
21
53
  userAgent: req.headers['user-agent'] || '',
22
54
  referrer: req.headers['referer'] || req.headers['referrer'] || '',
@@ -27,25 +59,65 @@ class HelmAnalytics {
27
59
  ...metadata
28
60
  };
29
61
 
30
- // Fire and forget (don't await in middleware usually, but here we define the promise)
31
- fetch(this.apiUrl, {
62
+ // Shield Checking (Blocking)
63
+ if (shield) {
64
+ const { allowed, reason } = await this.checkShield(payload);
65
+ if (!allowed) {
66
+ console.warn(`[Helm Shield] Blocked IP: ${payload.clientIp} Reason: ${reason}`);
67
+ return false;
68
+ }
69
+ }
70
+
71
+ // Fire and forget
72
+ fetch(`${this.apiUrl}/track`, {
32
73
  method: 'POST',
33
74
  body: JSON.stringify(payload),
34
75
  headers: { 'Content-Type': 'application/json' }
35
- }).catch(err => {
36
- // Silent error in background
37
- });
76
+ }).catch(err => {});
77
+
78
+ return true;
38
79
 
39
80
  } catch (err) {
40
- // Catch synchronous errors
81
+ return true; // Fail open
82
+ }
83
+ }
84
+
85
+ // Custom event tracking
86
+ async trackEvent(req, eventName, properties = {}) {
87
+ if (!this.siteId) return true;
88
+
89
+ try {
90
+ const payload = {
91
+ siteId: this.siteId,
92
+ sessionId: req.headers['x-helm-session-id'] || req.sessionId || '',
93
+ eventName: eventName,
94
+ properties: properties,
95
+ url: req.originalUrl || req.url,
96
+ referrer: req.headers['referer'] || req.headers['referrer'] || '',
97
+ isServerSide: true
98
+ };
99
+
100
+ fetch(`${this.apiUrl}/track/event`, {
101
+ method: 'POST',
102
+ body: JSON.stringify(payload),
103
+ headers: { 'Content-Type': 'application/json' }
104
+ }).catch(err => {});
105
+
106
+ return true;
107
+ } catch (err) {
108
+ return true;
41
109
  }
42
110
  }
43
111
 
44
112
  // Express Middleware
45
- middleware() {
46
- return (req, res, next) => {
47
- // don't track static files if easy to detect, but usually backend handles API routes
48
- this.track(req);
113
+ middleware(options = {}) {
114
+ const shield = options.shield || false;
115
+
116
+ return async (req, res, next) => {
117
+ const allowed = await this.track(req, 'pageview', {}, shield);
118
+ if (!allowed) {
119
+ return res.status(403).send('Forbidden by Helm Aegis');
120
+ }
49
121
  next();
50
122
  };
51
123
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "helm-analytics",
3
- "version": "1.0.0",
4
- "description": "Official Node.js Middleware for Helm Analytics",
3
+ "version": "5.0.0",
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
  }