helm-analytics 5.0.0 → 5.0.2
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/README.md +11 -0
- package/index.js +47 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,5 +57,16 @@ To link server-side events back to the browser session, ensure you pass the `ses
|
|
|
57
57
|
|
|
58
58
|
- **Non-blocking**: Uses non-blocking fetch calls for event ingestion.
|
|
59
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.
|
|
60
62
|
- **Custom Events**: Track business milestones beyond pageviews.
|
|
61
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.
|
|
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:
|
|
35
|
+
headers: headers
|
|
29
36
|
});
|
|
30
37
|
|
|
31
38
|
if (res.ok) {
|
|
@@ -42,20 +49,34 @@ class HelmAnalytics {
|
|
|
42
49
|
}
|
|
43
50
|
|
|
44
51
|
// Generic track method
|
|
45
|
-
async track(req, eventType = 'pageview',
|
|
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
|
-
sessionId: req.headers['x-helm-session-id'] || req.sessionId || '',
|
|
52
|
-
url:
|
|
69
|
+
sessionId: req.headers['x-helm-session-id'] || req.sessionId || '',
|
|
70
|
+
url: url,
|
|
53
71
|
userAgent: req.headers['user-agent'] || '',
|
|
54
72
|
referrer: req.headers['referer'] || req.headers['referrer'] || '',
|
|
55
|
-
// ClientIP logic: Headers or socket
|
|
56
73
|
clientIp: req.headers['x-forwarded-for'] ? req.headers['x-forwarded-for'].split(',')[0].trim() : (req.socket ? req.socket.remoteAddress : ''),
|
|
57
74
|
eventType: eventType,
|
|
75
|
+
pageTitle: pageTitle,
|
|
76
|
+
screenWidth: screenWidth,
|
|
58
77
|
isServerSide: true,
|
|
78
|
+
...utmParams,
|
|
79
|
+
...performance,
|
|
59
80
|
...metadata
|
|
60
81
|
};
|
|
61
82
|
|
|
@@ -68,11 +89,20 @@ class HelmAnalytics {
|
|
|
68
89
|
}
|
|
69
90
|
}
|
|
70
91
|
|
|
92
|
+
const headers = {
|
|
93
|
+
'Content-Type': 'application/json',
|
|
94
|
+
'User-Agent': payload.userAgent || 'HelmAnalytics/NodeSDK',
|
|
95
|
+
'X-Forwarded-For': payload.clientIp || ''
|
|
96
|
+
};
|
|
97
|
+
if (this.apiKey) {
|
|
98
|
+
headers['Authorization'] = `Bearer ${this.apiKey}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
71
101
|
// Fire and forget
|
|
72
102
|
fetch(`${this.apiUrl}/track`, {
|
|
73
103
|
method: 'POST',
|
|
74
104
|
body: JSON.stringify(payload),
|
|
75
|
-
headers:
|
|
105
|
+
headers: headers
|
|
76
106
|
}).catch(err => {});
|
|
77
107
|
|
|
78
108
|
return true;
|
|
@@ -97,10 +127,19 @@ class HelmAnalytics {
|
|
|
97
127
|
isServerSide: true
|
|
98
128
|
};
|
|
99
129
|
|
|
130
|
+
const headers = {
|
|
131
|
+
'Content-Type': 'application/json',
|
|
132
|
+
'User-Agent': payload.userAgent || 'HelmAnalytics/NodeSDK',
|
|
133
|
+
'X-Forwarded-For': payload.clientIp || ''
|
|
134
|
+
};
|
|
135
|
+
if (this.apiKey) {
|
|
136
|
+
headers['Authorization'] = `Bearer ${this.apiKey}`;
|
|
137
|
+
}
|
|
138
|
+
|
|
100
139
|
fetch(`${this.apiUrl}/track/event`, {
|
|
101
140
|
method: 'POST',
|
|
102
141
|
body: JSON.stringify(payload),
|
|
103
|
-
headers:
|
|
142
|
+
headers: headers
|
|
104
143
|
}).catch(err => {});
|
|
105
144
|
|
|
106
145
|
return true;
|