@webticks/core 0.1.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/README.md +98 -0
- package/injector.js +27 -0
- package/package.json +41 -0
- package/platform-adapters.js +112 -0
- package/tracker.js +265 -0
- package/types.js +61 -0
package/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# WebTicks Core - Usage Examples
|
|
2
|
+
|
|
3
|
+
## Browser Usage (No Changes Required!)
|
|
4
|
+
|
|
5
|
+
Your existing React/Next.js packages continue to work exactly as before:
|
|
6
|
+
|
|
7
|
+
```jsx
|
|
8
|
+
import WebTicksAnalytics from '@webticks/react';
|
|
9
|
+
|
|
10
|
+
function App() {
|
|
11
|
+
return (
|
|
12
|
+
<div>
|
|
13
|
+
<WebTicksAnalytics />
|
|
14
|
+
{/* Your app */}
|
|
15
|
+
</div>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Server-Side Usage
|
|
21
|
+
|
|
22
|
+
### Option 1: Express Middleware (Automatic)
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
import express from 'express';
|
|
26
|
+
import { createServerMiddleware } from '@webticks/core/server';
|
|
27
|
+
|
|
28
|
+
const app = express();
|
|
29
|
+
|
|
30
|
+
// Automatically track all HTTP requests
|
|
31
|
+
app.use(createServerMiddleware({
|
|
32
|
+
backendUrl: 'https://api.example.com/track'
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
app.listen(3000);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Option 2: Manual Tracking
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
import { AnalyticsTracker } from '@webticks/core/tracker';
|
|
42
|
+
|
|
43
|
+
const tracker = new AnalyticsTracker({
|
|
44
|
+
backendUrl: 'https://api.example.com/track'
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Track server requests
|
|
48
|
+
tracker.trackServerRequest({
|
|
49
|
+
method: 'GET',
|
|
50
|
+
path: '/api/users',
|
|
51
|
+
query: { page: 1 },
|
|
52
|
+
headers: { 'user-agent': 'Mozilla/5.0' }
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Track custom events
|
|
56
|
+
tracker.trackEvent('database_query', {
|
|
57
|
+
table: 'users',
|
|
58
|
+
duration: 45
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Send immediately (useful for serverless)
|
|
62
|
+
await tracker.sendQueue();
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Next.js Example
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
// pages/api/users.js
|
|
69
|
+
import { AnalyticsTracker } from '@webticks/core/tracker';
|
|
70
|
+
|
|
71
|
+
const tracker = new AnalyticsTracker({
|
|
72
|
+
backendUrl: process.env.ANALYTICS_URL
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export default async function handler(req, res) {
|
|
76
|
+
tracker.trackServerRequest({
|
|
77
|
+
method: req.method,
|
|
78
|
+
path: req.url,
|
|
79
|
+
query: req.query,
|
|
80
|
+
headers: {
|
|
81
|
+
'user-agent': req.headers['user-agent']
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Your API logic here
|
|
86
|
+
const data = await getUsers();
|
|
87
|
+
|
|
88
|
+
res.status(200).json(data);
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Key Benefits
|
|
93
|
+
|
|
94
|
+
✅ **Centralized Logic**: All tracking logic lives in `@webticks/core`
|
|
95
|
+
✅ **Environment Agnostic**: Works in browser and Node.js
|
|
96
|
+
✅ **Zero Breaking Changes**: Existing packages work without modifications
|
|
97
|
+
✅ **Easy Updates**: Fix bugs once, all environments benefit
|
|
98
|
+
✅ **Flexible**: Works with any framework (Express, Koa, Fastify, serverless, etc.)
|
package/injector.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { AnalyticsTracker } from "./tracker.js";
|
|
2
|
+
|
|
3
|
+
export default function inject() {
|
|
4
|
+
// Only auto-inject in browser environments
|
|
5
|
+
if (typeof window === 'undefined') {
|
|
6
|
+
console.warn("webticks auto-inject skipped: Not in a browser environment.");
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (window.webticks) {
|
|
11
|
+
console.warn("webticks tracker already initialized.");
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const config = {
|
|
16
|
+
backendUrl: "https://api.example.com/track"
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const tracker = new AnalyticsTracker(config);
|
|
20
|
+
tracker.autoTrackPageViews();
|
|
21
|
+
window.webticks = tracker;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Only auto-execute in browser
|
|
25
|
+
if (typeof window !== 'undefined') {
|
|
26
|
+
inject();
|
|
27
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@webticks/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Lightweight analytics library for modern web applications with seamless event tracking and page view monitoring",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "injector.js",
|
|
7
|
+
"module": "injector.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./injector.js",
|
|
10
|
+
"./inject": "./injector.js",
|
|
11
|
+
"./tracker": "./tracker.js",
|
|
12
|
+
"./platform-adapters": "./platform-adapters.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"tracker.js",
|
|
16
|
+
"injector.js",
|
|
17
|
+
"platform-adapters.js",
|
|
18
|
+
"types.js",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"keywords": [
|
|
22
|
+
"analytics",
|
|
23
|
+
"tracking",
|
|
24
|
+
"web-analytics",
|
|
25
|
+
"event-tracking",
|
|
26
|
+
"page-views",
|
|
27
|
+
"metrics",
|
|
28
|
+
"telemetry"
|
|
29
|
+
],
|
|
30
|
+
"author": "Celerinc Team",
|
|
31
|
+
"license": "MPL-2.0",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/Celerinc/webticks.git",
|
|
35
|
+
"directory": "packages/core"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/Celerinc/webticks#readme",
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/Celerinc/webticks/issues"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform adapters to abstract browser vs Node.js environment differences
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Environment detection
|
|
6
|
+
export function isServer() {
|
|
7
|
+
return typeof window === 'undefined';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function isBrowser() {
|
|
11
|
+
return typeof window !== 'undefined';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Browser-specific adapter
|
|
16
|
+
*/
|
|
17
|
+
export class BrowserAdapter {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.storage = window.localStorage;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Generate or retrieve user ID
|
|
23
|
+
getUserId() {
|
|
24
|
+
let userId = this.storage.getItem('webticks_uid');
|
|
25
|
+
if (!userId) {
|
|
26
|
+
userId = crypto.randomUUID();
|
|
27
|
+
this.storage.setItem('webticks_uid', userId);
|
|
28
|
+
}
|
|
29
|
+
return userId;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Send HTTP request
|
|
33
|
+
async sendRequest(url, data, appId) {
|
|
34
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
35
|
+
|
|
36
|
+
// Add webticks-app-id header if appId is provided
|
|
37
|
+
if (appId) {
|
|
38
|
+
headers['webticks-app-id'] = appId;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return fetch(url, {
|
|
42
|
+
method: 'POST',
|
|
43
|
+
headers: headers,
|
|
44
|
+
body: JSON.stringify(data)
|
|
45
|
+
}).catch((err) => console.warn('Error sending request:', err));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Get current path
|
|
49
|
+
getCurrentPath() {
|
|
50
|
+
return window.location.href;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Setup auto-tracking (browser-specific)
|
|
54
|
+
setupAutoTracking(tracker) {
|
|
55
|
+
// Patch history API
|
|
56
|
+
tracker.originalPushState = window.history.pushState;
|
|
57
|
+
tracker.originalReplaceState = window.history.replaceState;
|
|
58
|
+
|
|
59
|
+
window.history.pushState = (...args) => {
|
|
60
|
+
const result = tracker.originalPushState.apply(window.history, args);
|
|
61
|
+
tracker.checkPageChange();
|
|
62
|
+
if (typeof window.history.onpushstate === "function") {
|
|
63
|
+
window.history.onpushstate({ state: args[0] });
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
window.history.replaceState = (...args) => {
|
|
69
|
+
const result = tracker.originalReplaceState.apply(window.history, args);
|
|
70
|
+
tracker.checkPageChange();
|
|
71
|
+
if (typeof window.history.onreplacestate === "function") {
|
|
72
|
+
window.history.onreplacestate({ state: args[0] });
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
window.addEventListener('popstate', tracker.checkPageChange);
|
|
78
|
+
document.addEventListener('visibilitychange', tracker.handleVisibilityChange);
|
|
79
|
+
window.addEventListener('pagehide', tracker.handlePageHide);
|
|
80
|
+
|
|
81
|
+
// Track initial page view
|
|
82
|
+
tracker.lastPath = window.location.href;
|
|
83
|
+
tracker.trackPageView(tracker.lastPath);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Cleanup auto-tracking
|
|
87
|
+
cleanupAutoTracking(tracker) {
|
|
88
|
+
if (tracker.originalPushState) {
|
|
89
|
+
window.history.pushState = tracker.originalPushState;
|
|
90
|
+
}
|
|
91
|
+
if (tracker.originalReplaceState) {
|
|
92
|
+
window.history.replaceState = tracker.originalReplaceState;
|
|
93
|
+
}
|
|
94
|
+
window.removeEventListener('popstate', tracker.checkPageChange);
|
|
95
|
+
document.removeEventListener('visibilitychange', tracker.handleVisibilityChange);
|
|
96
|
+
window.removeEventListener('pagehide', tracker.handlePageHide);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Factory function to get the appropriate adapter
|
|
102
|
+
* Core package only provides browser adapter
|
|
103
|
+
* For Node.js, use @webticks/node package
|
|
104
|
+
*/
|
|
105
|
+
export function getPlatformAdapter() {
|
|
106
|
+
if (isBrowser()) {
|
|
107
|
+
return new BrowserAdapter();
|
|
108
|
+
}
|
|
109
|
+
// For server-side, users should use @webticks/node package
|
|
110
|
+
console.warn('Running in server environment. Please use @webticks/node package for server-side tracking.');
|
|
111
|
+
return null;
|
|
112
|
+
}
|
package/tracker.js
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { getPlatformAdapter, isServer, isBrowser } from './platform-adapters.js';
|
|
2
|
+
|
|
3
|
+
// Import type definitions
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {import('./types.js').Event} Event
|
|
6
|
+
* @typedef {import('./types.js').PageViewEvent} PageViewEvent
|
|
7
|
+
* @typedef {import('./types.js').CustomEvent} CustomEvent
|
|
8
|
+
* @typedef {import('./types.js').ServerRequestEvent} ServerRequestEvent
|
|
9
|
+
* @typedef {import('./types.js').AnalyticsBatch} AnalyticsBatch
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export class AnalyticsTracker {
|
|
13
|
+
/**
|
|
14
|
+
* @param {Object} config - Tracker configuration
|
|
15
|
+
* @param {string} config.backendUrl - URL to send analytics data
|
|
16
|
+
* @param {string} [config.appId] - Application ID for tracking (can also be set via WEBTICKS_APP_ID env variable)
|
|
17
|
+
*/
|
|
18
|
+
constructor(config) {
|
|
19
|
+
this.config = config || { backendUrl: "/api/track" };
|
|
20
|
+
|
|
21
|
+
// Get appId from config or environment variable
|
|
22
|
+
this.appId = this.config.appId || this.getAppIdFromEnv();
|
|
23
|
+
|
|
24
|
+
/** @type {Event[]} */
|
|
25
|
+
this.eventQueue = [];
|
|
26
|
+
this.lastPath = "";
|
|
27
|
+
this.batchSendInterval = 10000;
|
|
28
|
+
this.sendTimer = null;
|
|
29
|
+
/** @type {string|null} */
|
|
30
|
+
this.userId = null;
|
|
31
|
+
/** @type {string|null} */
|
|
32
|
+
this.sessionId = null; // Session ID stored in memory
|
|
33
|
+
this.adapter = getPlatformAdapter();
|
|
34
|
+
|
|
35
|
+
// Bind methods
|
|
36
|
+
this.checkPageChange = this.checkPageChange.bind(this);
|
|
37
|
+
this.sendQueue = this.sendQueue.bind(this);
|
|
38
|
+
this.handleVisibilityChange = this.handleVisibilityChange.bind(this);
|
|
39
|
+
this.handlePageHide = this.handlePageHide.bind(this);
|
|
40
|
+
|
|
41
|
+
this.initializeUser();
|
|
42
|
+
this.initializeSession();
|
|
43
|
+
|
|
44
|
+
console.log(`AnalyticsTracker initialized in ${isServer() ? 'Node.js' : 'Browser'} environment.`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get app ID from environment variable
|
|
49
|
+
* Works in both browser (import.meta.env) and Node.js (process.env)
|
|
50
|
+
*/
|
|
51
|
+
getAppIdFromEnv() {
|
|
52
|
+
// Browser environment (Vite, etc.)
|
|
53
|
+
if (typeof import.meta !== 'undefined' && import.meta.env) {
|
|
54
|
+
return import.meta.env.VITE_WEBTICKS_APP_ID || import.meta.env.WEBTICKS_APP_ID;
|
|
55
|
+
}
|
|
56
|
+
// Node.js environment
|
|
57
|
+
if (typeof process !== 'undefined' && process.env) {
|
|
58
|
+
return process.env.WEBTICKS_APP_ID;
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
initializeUser() {
|
|
64
|
+
// Skip if adapter is not available (will be initialized later in server environments)
|
|
65
|
+
if (!this.adapter) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!this.userId) {
|
|
70
|
+
this.userId = this.adapter.getUserId();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Initialize session ID
|
|
76
|
+
* Session is stored in memory and will be destroyed when the page/instance is closed
|
|
77
|
+
*/
|
|
78
|
+
initializeSession() {
|
|
79
|
+
// Generate new session ID using crypto (available in both browser and Node.js)
|
|
80
|
+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
81
|
+
this.sessionId = crypto.randomUUID();
|
|
82
|
+
console.log(`Session initialized: ${this.sessionId}`);
|
|
83
|
+
} else {
|
|
84
|
+
// Fallback for older environments
|
|
85
|
+
this.sessionId = this.generateFallbackId();
|
|
86
|
+
console.warn('crypto.randomUUID not available, using fallback ID generation');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Generate fallback ID if crypto.randomUUID is not available
|
|
92
|
+
*/
|
|
93
|
+
generateFallbackId() {
|
|
94
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
|
95
|
+
const r = Math.random() * 16 | 0;
|
|
96
|
+
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
97
|
+
return v.toString(16);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Automatically track page views (browser) or HTTP requests (server)
|
|
103
|
+
* Works in both environments!
|
|
104
|
+
*/
|
|
105
|
+
autoTrackPageViews() {
|
|
106
|
+
if (isServer()) {
|
|
107
|
+
console.log("Setting up automatic server-side tracking...");
|
|
108
|
+
} else {
|
|
109
|
+
console.log("Setting up automatic page view tracking...");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Use adapter to setup platform-specific tracking
|
|
113
|
+
this.adapter.setupAutoTracking(this);
|
|
114
|
+
|
|
115
|
+
// Start the batch send timer
|
|
116
|
+
this.sendTimer = setInterval(this.sendQueue, this.batchSendInterval);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Check for page changes (browser-only)
|
|
121
|
+
*/
|
|
122
|
+
checkPageChange() {
|
|
123
|
+
if (isServer()) return;
|
|
124
|
+
|
|
125
|
+
const currentPath = this.adapter.getCurrentPath();
|
|
126
|
+
if (currentPath !== this.lastPath) {
|
|
127
|
+
console.log(`Page change detected: ${this.lastPath} -> ${currentPath}`);
|
|
128
|
+
this.lastPath = currentPath;
|
|
129
|
+
this.trackPageView(currentPath);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Handle visibility changes (browser-only)
|
|
135
|
+
*/
|
|
136
|
+
handleVisibilityChange() {
|
|
137
|
+
if (isServer()) return;
|
|
138
|
+
|
|
139
|
+
if (document.hidden) {
|
|
140
|
+
this.trackEvent('visibility_change', { visible: false });
|
|
141
|
+
} else {
|
|
142
|
+
this.trackEvent('visibility_change', { visible: true });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Handle page hide event (browser-only)
|
|
148
|
+
*/
|
|
149
|
+
handlePageHide() {
|
|
150
|
+
console.log("Page hidden, attempting final batch send.");
|
|
151
|
+
this.sendQueue();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Track a page view
|
|
156
|
+
* @param {string} path - The path/URL to track
|
|
157
|
+
* @returns {PageViewEvent} The created event
|
|
158
|
+
*/
|
|
159
|
+
trackPageView(path) {
|
|
160
|
+
/** @type {PageViewEvent} */
|
|
161
|
+
const event = {
|
|
162
|
+
requestId: crypto.randomUUID ? crypto.randomUUID() : this.generateFallbackId(),
|
|
163
|
+
type: 'pageview',
|
|
164
|
+
path: path,
|
|
165
|
+
timestamp: new Date().toISOString()
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
this.eventQueue.push(event);
|
|
169
|
+
return event;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Track a custom event
|
|
174
|
+
* @param {string} eventName - Name of the event
|
|
175
|
+
* @param {Object} [details={}] - Event details/metadata
|
|
176
|
+
* @returns {CustomEvent} The created event
|
|
177
|
+
*/
|
|
178
|
+
trackEvent(eventName, details = {}) {
|
|
179
|
+
/** @type {CustomEvent} */
|
|
180
|
+
const event = {
|
|
181
|
+
requestId: crypto.randomUUID ? crypto.randomUUID() : this.generateFallbackId(),
|
|
182
|
+
type: 'custom',
|
|
183
|
+
name: eventName,
|
|
184
|
+
details: details,
|
|
185
|
+
path: isBrowser() ? window.location.href : null,
|
|
186
|
+
timestamp: new Date().toISOString()
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
this.eventQueue.push(event);
|
|
190
|
+
return event;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Track a server-side HTTP request (server-only)
|
|
195
|
+
* @param {Object} requestData - Request information
|
|
196
|
+
* @param {string} requestData.method - HTTP method (GET, POST, etc.)
|
|
197
|
+
* @param {string} requestData.path - Request path
|
|
198
|
+
* @param {Object} [requestData.query] - Query parameters
|
|
199
|
+
* @param {Object} [requestData.headers] - Request headers
|
|
200
|
+
* @returns {ServerRequestEvent} The created event
|
|
201
|
+
*/
|
|
202
|
+
trackServerRequest(requestData) {
|
|
203
|
+
/** @type {ServerRequestEvent} */
|
|
204
|
+
const event = {
|
|
205
|
+
requestId: crypto.randomUUID ? crypto.randomUUID() : this.generateFallbackId(),
|
|
206
|
+
type: 'server_request',
|
|
207
|
+
method: requestData.method,
|
|
208
|
+
path: requestData.path,
|
|
209
|
+
query: requestData.query,
|
|
210
|
+
headers: requestData.headers,
|
|
211
|
+
timestamp: new Date().toISOString()
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
this.eventQueue.push(event);
|
|
215
|
+
return event;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Send queued events to the backend
|
|
220
|
+
*/
|
|
221
|
+
async sendQueue() {
|
|
222
|
+
if (this.eventQueue.length === 0) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const eventsToSend = [...this.eventQueue];
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
const response = await this.adapter.sendRequest(this.config.backendUrl, {
|
|
230
|
+
uid: this.userId,
|
|
231
|
+
sessionId: this.sessionId,
|
|
232
|
+
events: eventsToSend,
|
|
233
|
+
datetime: new Date().toISOString()
|
|
234
|
+
}, this.appId);
|
|
235
|
+
|
|
236
|
+
if (response.ok) {
|
|
237
|
+
// Clear queue on success
|
|
238
|
+
this.eventQueue = [];
|
|
239
|
+
} else {
|
|
240
|
+
console.error(`Failed to send analytics batch: ${response.status}`);
|
|
241
|
+
// Keep events in queue to retry
|
|
242
|
+
this.eventQueue = [...eventsToSend];
|
|
243
|
+
}
|
|
244
|
+
} catch (err) {
|
|
245
|
+
console.error("Failed to send analytics batch:", err);
|
|
246
|
+
// Keep events in queue to retry
|
|
247
|
+
this.eventQueue = [...eventsToSend];
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Cleans up listeners and timers
|
|
253
|
+
*/
|
|
254
|
+
destroy() {
|
|
255
|
+
console.log("Destroying tracker...");
|
|
256
|
+
|
|
257
|
+
// Stop the batch sender
|
|
258
|
+
if (this.sendTimer) {
|
|
259
|
+
clearInterval(this.sendTimer);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Use adapter to cleanup platform-specific tracking
|
|
263
|
+
this.adapter.cleanupAutoTracking(this);
|
|
264
|
+
}
|
|
265
|
+
}
|
package/types.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for WebTicks Analytics Events
|
|
3
|
+
* Using JSDoc for type safety in JavaScript
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Base Event interface - common fields for all events
|
|
8
|
+
* @typedef {Object} BaseEvent
|
|
9
|
+
* @property {string} requestId - Unique identifier for this specific request
|
|
10
|
+
* @property {string} type - Type of event ('pageview' | 'custom' | 'server_request')
|
|
11
|
+
* @property {string} timestamp - ISO timestamp when the event was created
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Page View Event - tracks page navigation
|
|
16
|
+
* @typedef {Object} PageViewEvent
|
|
17
|
+
* @property {string} requestId - Unique identifier for this specific request
|
|
18
|
+
* @property {'pageview'} type - Event type identifier
|
|
19
|
+
* @property {string} path - The URL/path that was viewed
|
|
20
|
+
* @property {string} timestamp - ISO timestamp when the event was created
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Custom Event - tracks custom user interactions
|
|
25
|
+
* @typedef {Object} CustomEvent
|
|
26
|
+
* @property {string} requestId - Unique identifier for this specific request
|
|
27
|
+
* @property {'custom'} type - Event type identifier
|
|
28
|
+
* @property {string} name - Name of the custom event
|
|
29
|
+
* @property {Object} details - Additional event metadata
|
|
30
|
+
* @property {string|null} path - Current page URL (browser only)
|
|
31
|
+
* @property {string} timestamp - ISO timestamp when the event was created
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Server Request Event - tracks server-side HTTP requests
|
|
36
|
+
* @typedef {Object} ServerRequestEvent
|
|
37
|
+
* @property {string} requestId - Unique identifier for this specific request
|
|
38
|
+
* @property {'server_request'} type - Event type identifier
|
|
39
|
+
* @property {string} method - HTTP method (GET, POST, etc.)
|
|
40
|
+
* @property {string} path - Request path
|
|
41
|
+
* @property {Object} [query] - Query parameters
|
|
42
|
+
* @property {Object} [headers] - Request headers
|
|
43
|
+
* @property {string} timestamp - ISO timestamp when the event was created
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Union type for all possible events
|
|
48
|
+
* @typedef {PageViewEvent | CustomEvent | ServerRequestEvent} Event
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Analytics batch payload sent to backend
|
|
53
|
+
* @typedef {Object} AnalyticsBatch
|
|
54
|
+
* @property {string} uid - User ID
|
|
55
|
+
* @property {string} sessionId - Session ID
|
|
56
|
+
* @property {Event[]} events - Array of events to track
|
|
57
|
+
* @property {string} datetime - ISO timestamp when the batch was sent
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
// Export types for use in other modules
|
|
61
|
+
export { };
|