@levi123/trackers 4.0.0-dev.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 +220 -0
- package/dist/esm/index.js +402 -0
- package/dist/esm/index.mjs +402 -0
- package/dist/esm/tooling/tsconfig/dist/callback-worker.d.ts +1 -0
- package/dist/esm/tooling/tsconfig/dist/data/callbackStore.d.ts +20 -0
- package/dist/esm/tooling/tsconfig/dist/data/eventStore.d.ts +14 -0
- package/dist/esm/tooling/tsconfig/dist/data/index.d.ts +2 -0
- package/dist/esm/tooling/tsconfig/dist/debug/index.d.ts +9 -0
- package/dist/esm/tooling/tsconfig/dist/event-worker.d.ts +1 -0
- package/dist/esm/tooling/tsconfig/dist/handlers/event-worker/click-handler.d.ts +2 -0
- package/dist/esm/tooling/tsconfig/dist/handlers/event-worker/index.d.ts +5 -0
- package/dist/esm/tooling/tsconfig/dist/handlers/event-worker/page-view-handler.d.ts +2 -0
- package/dist/esm/tooling/tsconfig/dist/handlers/event-worker/performance-entry-handler.d.ts +2 -0
- package/dist/esm/tooling/tsconfig/dist/handlers/event-worker/performance-handler.d.ts +2 -0
- package/dist/esm/tooling/tsconfig/dist/handlers/event-worker/scroll-handler.d.ts +2 -0
- package/dist/esm/tooling/tsconfig/dist/handlers/index.d.ts +1 -0
- package/dist/esm/tooling/tsconfig/dist/index.d.ts +2 -0
- package/dist/esm/tooling/tsconfig/dist/tracking-sdk.d.ts +54 -0
- package/dist/esm/tooling/tsconfig/dist/types/callback.d.ts +43 -0
- package/dist/esm/tooling/tsconfig/dist/types/event.d.ts +91 -0
- package/dist/esm/tooling/tsconfig/dist/types/index.d.ts +4 -0
- package/dist/esm/tooling/tsconfig/dist/types/tracking.d.ts +16 -0
- package/dist/esm/tooling/tsconfig/dist/types/worker.d.ts +21 -0
- package/dist/esm/tooling/tsconfig/dist/utils/event-worker.d.ts +1 -0
- package/dist/esm/tooling/tsconfig/dist/utils/index.d.ts +1 -0
- package/dist/umd/index.js +1 -0
- package/dist/umd/tooling/tsconfig/dist/callback-worker.d.ts +1 -0
- package/dist/umd/tooling/tsconfig/dist/data/callbackStore.d.ts +20 -0
- package/dist/umd/tooling/tsconfig/dist/data/eventStore.d.ts +14 -0
- package/dist/umd/tooling/tsconfig/dist/data/index.d.ts +2 -0
- package/dist/umd/tooling/tsconfig/dist/debug/index.d.ts +9 -0
- package/dist/umd/tooling/tsconfig/dist/event-worker.d.ts +1 -0
- package/dist/umd/tooling/tsconfig/dist/handlers/event-worker/click-handler.d.ts +2 -0
- package/dist/umd/tooling/tsconfig/dist/handlers/event-worker/index.d.ts +5 -0
- package/dist/umd/tooling/tsconfig/dist/handlers/event-worker/page-view-handler.d.ts +2 -0
- package/dist/umd/tooling/tsconfig/dist/handlers/event-worker/performance-entry-handler.d.ts +2 -0
- package/dist/umd/tooling/tsconfig/dist/handlers/event-worker/performance-handler.d.ts +2 -0
- package/dist/umd/tooling/tsconfig/dist/handlers/event-worker/scroll-handler.d.ts +2 -0
- package/dist/umd/tooling/tsconfig/dist/handlers/index.d.ts +1 -0
- package/dist/umd/tooling/tsconfig/dist/index.d.ts +2 -0
- package/dist/umd/tooling/tsconfig/dist/tracking-sdk.d.ts +54 -0
- package/dist/umd/tooling/tsconfig/dist/types/callback.d.ts +43 -0
- package/dist/umd/tooling/tsconfig/dist/types/event.d.ts +91 -0
- package/dist/umd/tooling/tsconfig/dist/types/index.d.ts +4 -0
- package/dist/umd/tooling/tsconfig/dist/types/tracking.d.ts +16 -0
- package/dist/umd/tooling/tsconfig/dist/types/worker.d.ts +21 -0
- package/dist/umd/tooling/tsconfig/dist/utils/event-worker.d.ts +1 -0
- package/dist/umd/tooling/tsconfig/dist/utils/index.d.ts +1 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# @levi123/trackers
|
|
2
|
+
|
|
3
|
+
A high-performance tracking SDK with dual Web Worker architecture for optimal performance and scalability.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Dual Worker Architecture**: Separate workers for event processing and callback handling
|
|
8
|
+
- **TypeScript Support**: Full TypeScript definitions and type safety
|
|
9
|
+
- **Performance Optimized**: Non-blocking event processing using Web Workers
|
|
10
|
+
- **Fault Tolerant**: Isolated workers prevent crashes from affecting the main thread
|
|
11
|
+
- **Scalable**: Easy to extend with additional workers for specific event types
|
|
12
|
+
- **Modern API**: Clean, intuitive API with callback support
|
|
13
|
+
|
|
14
|
+
## Architecture
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
Page Event (click/load)
|
|
18
|
+
↓
|
|
19
|
+
Main Thread (tracking-sdk.js)
|
|
20
|
+
↓ postMessage
|
|
21
|
+
Event Worker (event-worker.js)
|
|
22
|
+
→ Process logic → Calculate metrics
|
|
23
|
+
↓ postMessage
|
|
24
|
+
Callback Worker (callback-worker.js)
|
|
25
|
+
→ Execute callbacks → API calls → Logging
|
|
26
|
+
↓ postMessage (summaries)
|
|
27
|
+
Main Thread
|
|
28
|
+
→ User callbacks → Console/UI updates
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install @levi123/trackers
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
### Basic Setup
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import { TrackingSDK } from '@levi123/trackers';
|
|
43
|
+
|
|
44
|
+
// Initialize the SDK
|
|
45
|
+
TrackingSDK.init({
|
|
46
|
+
apiEndpoint: 'https://your-analytics-server.com/track',
|
|
47
|
+
events: ['pageLoad', 'click', 'performance'],
|
|
48
|
+
workerTimeout: 5000,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Register callbacks
|
|
52
|
+
TrackingSDK.on('performanceResult', (data) => {
|
|
53
|
+
console.log('Performance metrics:', data.metrics);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
TrackingSDK.on('clickTracked', (data) => {
|
|
57
|
+
console.log('Click tracked:', data.data);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
TrackingSDK.on('error', (error) => {
|
|
61
|
+
console.error('Tracking error:', error);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Track custom events
|
|
65
|
+
TrackingSDK.trackEvent('customEvent', {
|
|
66
|
+
userId: '123',
|
|
67
|
+
action: 'button_click',
|
|
68
|
+
timestamp: Date.now(),
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Advanced Configuration
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { TrackingSDK, type TrackingConfig } from '@levi123/trackers';
|
|
76
|
+
|
|
77
|
+
const config: TrackingConfig = {
|
|
78
|
+
apiEndpoint: 'https://analytics.example.com/track',
|
|
79
|
+
events: ['pageLoad', 'click', 'performance', 'scroll'],
|
|
80
|
+
workerTimeout: 10000,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
TrackingSDK.init(config);
|
|
84
|
+
|
|
85
|
+
// Get SDK status
|
|
86
|
+
const status = TrackingSDK.getStatus();
|
|
87
|
+
console.log('Worker status:', status.workerStatus);
|
|
88
|
+
console.log('Registered callbacks:', status.registeredCallbacks);
|
|
89
|
+
|
|
90
|
+
// Cleanup when done
|
|
91
|
+
TrackingSDK.destroy();
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Callback Management
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// Register callback and get unsubscribe function
|
|
98
|
+
const unsubscribe = TrackingSDK.on('pageLoad', (data) => {
|
|
99
|
+
console.log('Page loaded:', data.data);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Unsubscribe when no longer needed
|
|
103
|
+
unsubscribe();
|
|
104
|
+
|
|
105
|
+
// Register multiple callbacks for the same event
|
|
106
|
+
TrackingSDK.on('click', callback1);
|
|
107
|
+
TrackingSDK.on('click', callback2);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## API Reference
|
|
111
|
+
|
|
112
|
+
### TrackingSDK
|
|
113
|
+
|
|
114
|
+
#### Methods
|
|
115
|
+
|
|
116
|
+
- `init(config?: Partial<TrackingConfig>)` - Initialize the SDK
|
|
117
|
+
- `trackEvent(eventName: string, data: EventData)` - Track a custom event
|
|
118
|
+
- `on(eventType: string, callback: EventCallback)` - Register a callback
|
|
119
|
+
- `getStatus()` - Get current SDK status
|
|
120
|
+
- `destroy()` - Clean up and destroy the SDK
|
|
121
|
+
|
|
122
|
+
#### Configuration
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
interface TrackingConfig {
|
|
126
|
+
apiEndpoint: string;
|
|
127
|
+
events: string[];
|
|
128
|
+
workerTimeout: number;
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
#### Event Data
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
interface EventData {
|
|
136
|
+
timestamp: number;
|
|
137
|
+
[key: string]: any;
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### Callback Data
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
interface CallbackData {
|
|
145
|
+
type: string;
|
|
146
|
+
data: ProcessedEventData;
|
|
147
|
+
metrics?: PerformanceMetrics;
|
|
148
|
+
timestamp: number;
|
|
149
|
+
processingTime: number;
|
|
150
|
+
workerId: string;
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Built-in Event Types
|
|
155
|
+
|
|
156
|
+
- `pageLoad` - Page load events
|
|
157
|
+
- `pageLoadComplete` - Complete page load with performance metrics
|
|
158
|
+
- `userClick` - User click events
|
|
159
|
+
- `performance` - Performance metrics
|
|
160
|
+
- `performanceEntry` - Performance observer entries
|
|
161
|
+
- `scroll` - Scroll events (throttled)
|
|
162
|
+
|
|
163
|
+
## Worker Files
|
|
164
|
+
|
|
165
|
+
The package includes three JavaScript files that need to be served from your public directory:
|
|
166
|
+
|
|
167
|
+
- `event-worker.js` - Processes tracking events
|
|
168
|
+
- `callback-worker.js` - Handles callbacks and communication
|
|
169
|
+
- `tracking-sdk.js` - Main SDK file (for non-module usage)
|
|
170
|
+
|
|
171
|
+
## Browser Support
|
|
172
|
+
|
|
173
|
+
- Modern browsers with Web Worker support
|
|
174
|
+
- ES6+ features required
|
|
175
|
+
- TypeScript support for development
|
|
176
|
+
|
|
177
|
+
## Performance Benefits
|
|
178
|
+
|
|
179
|
+
1. **Non-blocking Processing**: Event processing happens in workers
|
|
180
|
+
2. **Parallel Execution**: Event and callback workers run simultaneously
|
|
181
|
+
3. **Fault Isolation**: Worker crashes don't affect the main thread
|
|
182
|
+
4. **Scalable Architecture**: Easy to add more workers for specific tasks
|
|
183
|
+
|
|
184
|
+
## Development
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
# Install dependencies
|
|
188
|
+
npm install
|
|
189
|
+
|
|
190
|
+
# Build the package
|
|
191
|
+
npm run build
|
|
192
|
+
|
|
193
|
+
# Run tests
|
|
194
|
+
npm test
|
|
195
|
+
|
|
196
|
+
# Lint code
|
|
197
|
+
npm run lint
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
MIT
|
|
203
|
+
|
|
204
|
+
## Contributing
|
|
205
|
+
|
|
206
|
+
1. Fork the repository
|
|
207
|
+
2. Create a feature branch
|
|
208
|
+
3. Make your changes
|
|
209
|
+
4. Add tests
|
|
210
|
+
5. Submit a pull request
|
|
211
|
+
|
|
212
|
+
## Changelog
|
|
213
|
+
|
|
214
|
+
### 0.0.0
|
|
215
|
+
|
|
216
|
+
- Initial release
|
|
217
|
+
- Dual worker architecture
|
|
218
|
+
- TypeScript support
|
|
219
|
+
- Basic event tracking
|
|
220
|
+
- Performance monitoring
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
class DebugTrackers {
|
|
3
|
+
constructor(enableLog) {
|
|
4
|
+
this.enableLog = !!enableLog;
|
|
5
|
+
}
|
|
6
|
+
log(message, ...args) {
|
|
7
|
+
this.enableLog && console.log(message, ...args);
|
|
8
|
+
}
|
|
9
|
+
warn(message, ...args) {
|
|
10
|
+
this.enableLog && console.warn(message, ...args);
|
|
11
|
+
}
|
|
12
|
+
error(message, ...args) {
|
|
13
|
+
this.enableLog && console.error(message, ...args);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const debugTrackers = new DebugTrackers();
|
|
17
|
+
|
|
18
|
+
// ----- Enums -----
|
|
19
|
+
var ITrackingEvent;
|
|
20
|
+
(function (ITrackingEvent) {
|
|
21
|
+
ITrackingEvent["PAGE_LOAD"] = "pageLoad";
|
|
22
|
+
ITrackingEvent["CLICK"] = "click";
|
|
23
|
+
ITrackingEvent["PERFORMANCE"] = "performance";
|
|
24
|
+
ITrackingEvent["PERFORMANCE_ENTRY"] = "performanceEntry";
|
|
25
|
+
ITrackingEvent["SCROLL"] = "scroll";
|
|
26
|
+
ITrackingEvent["MANUAL_CLICK"] = "manualClick";
|
|
27
|
+
})(ITrackingEvent || (ITrackingEvent = {}));
|
|
28
|
+
|
|
29
|
+
class GXTrackingSDK {
|
|
30
|
+
constructor() {
|
|
31
|
+
this.config = {
|
|
32
|
+
events: [ITrackingEvent.PAGE_LOAD, ITrackingEvent.CLICK],
|
|
33
|
+
workerTimeout: 5000,
|
|
34
|
+
};
|
|
35
|
+
this.isInitialized = false;
|
|
36
|
+
this.eventWorker = null;
|
|
37
|
+
this.callbackWorker = null;
|
|
38
|
+
this.callbacks = {};
|
|
39
|
+
this.workerStatus = {
|
|
40
|
+
eventWorker: 'idle',
|
|
41
|
+
callbackWorker: 'idle',
|
|
42
|
+
};
|
|
43
|
+
this.perEventCallbacks = new Map();
|
|
44
|
+
// Private event handlers
|
|
45
|
+
this.handlePageLoad = () => {
|
|
46
|
+
this.trackEvent(ITrackingEvent.PAGE_LOAD, {
|
|
47
|
+
timestamp: Date.now(),
|
|
48
|
+
url: window.location.href,
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
this.handleClick = (e) => {
|
|
52
|
+
const target = e.target;
|
|
53
|
+
if (target.tagName !== 'SCRIPT' && target.tagName !== 'STYLE')
|
|
54
|
+
return;
|
|
55
|
+
this.trackEvent(ITrackingEvent.CLICK, {
|
|
56
|
+
target: target.tagName,
|
|
57
|
+
targetId: target.id || '',
|
|
58
|
+
targetClass: target.className || '',
|
|
59
|
+
x: e.clientX,
|
|
60
|
+
y: e.clientY,
|
|
61
|
+
timestamp: Date.now(),
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
if (typeof window !== 'undefined' && !window.Worker) {
|
|
65
|
+
debugTrackers.warn('Web Worker not supported. Falling back to main thread.');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
init(userConfig = {}) {
|
|
70
|
+
if (this.isInitialized) {
|
|
71
|
+
debugTrackers.warn('SDK already initialized');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// Merge config
|
|
75
|
+
Object.assign(this.config, userConfig);
|
|
76
|
+
debugTrackers.log('🚀 GXTrackingSDK initialized with config:', this.config);
|
|
77
|
+
// Initialize workers
|
|
78
|
+
this.initWorkers();
|
|
79
|
+
const handleStartTracking = () => {
|
|
80
|
+
setTimeout(() => {
|
|
81
|
+
this.isInitialized = true;
|
|
82
|
+
this.startTracking();
|
|
83
|
+
debugTrackers.log('✅ All workers are running');
|
|
84
|
+
}, 0);
|
|
85
|
+
};
|
|
86
|
+
if (document.readyState === 'complete') {
|
|
87
|
+
handleStartTracking();
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
window.addEventListener('load', handleStartTracking);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
initWorkers() {
|
|
94
|
+
this.initEventWorker();
|
|
95
|
+
this.initCallbackWorker();
|
|
96
|
+
}
|
|
97
|
+
initEventWorker() {
|
|
98
|
+
try {
|
|
99
|
+
// Create Event Worker from package source (bundled by the app/bundler)
|
|
100
|
+
this.eventWorker = new Worker(new URL('./event-worker.ts', import.meta.url), {
|
|
101
|
+
type: 'module',
|
|
102
|
+
});
|
|
103
|
+
this.eventWorker.onmessage = (e) => {
|
|
104
|
+
const { type, data } = e.data;
|
|
105
|
+
this.workerStatus.eventWorker = 'active';
|
|
106
|
+
switch (type) {
|
|
107
|
+
case 'processed':
|
|
108
|
+
this.sendToCallbackWorker({
|
|
109
|
+
type: 'processedResult',
|
|
110
|
+
event: data.event,
|
|
111
|
+
eventType: data.event,
|
|
112
|
+
processedData: data.processedData,
|
|
113
|
+
timestamp: Date.now(),
|
|
114
|
+
});
|
|
115
|
+
break;
|
|
116
|
+
case 'error':
|
|
117
|
+
this.handleError(data);
|
|
118
|
+
break;
|
|
119
|
+
default:
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
this.eventWorker.onerror = (error) => {
|
|
123
|
+
this.workerStatus.eventWorker = 'error';
|
|
124
|
+
this.handleError({ message: 'Event Worker crashed', error });
|
|
125
|
+
};
|
|
126
|
+
// Send initial config to Event Worker
|
|
127
|
+
this.sendToEventWorker({ type: 'init', config: this.config });
|
|
128
|
+
this.workerStatus.eventWorker = 'ready';
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
this.handleError({ message: 'Failed to create Event Worker', error });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
initCallbackWorker() {
|
|
135
|
+
try {
|
|
136
|
+
this.callbackWorker = new Worker(new URL('./callback-worker.ts', import.meta.url), {
|
|
137
|
+
type: 'module',
|
|
138
|
+
});
|
|
139
|
+
this.callbackWorker.onmessage = (e) => {
|
|
140
|
+
const { type, data } = e.data;
|
|
141
|
+
this.workerStatus.callbackWorker = 'active';
|
|
142
|
+
switch (type) {
|
|
143
|
+
case 'executeCallbacks': {
|
|
144
|
+
// Execute registered callbacks on main thread and send result back
|
|
145
|
+
const { eventType, requestId, payload } = e.data || {};
|
|
146
|
+
const callbacks = (eventType && this.callbacks[eventType]) || [];
|
|
147
|
+
let successCount = 0;
|
|
148
|
+
let errorCount = 0;
|
|
149
|
+
const start = Date.now();
|
|
150
|
+
const perEventId = (payload && payload.data && payload.data.eventId);
|
|
151
|
+
const perEventCb = perEventId ? this.perEventCallbacks.get(perEventId) : undefined;
|
|
152
|
+
if (perEventCb) {
|
|
153
|
+
try {
|
|
154
|
+
perEventCb(payload);
|
|
155
|
+
successCount++;
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
errorCount++;
|
|
159
|
+
}
|
|
160
|
+
finally {
|
|
161
|
+
this.perEventCallbacks.delete(perEventId);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (callbacks.length > 0) {
|
|
165
|
+
callbacks.forEach((cb) => {
|
|
166
|
+
try {
|
|
167
|
+
cb(payload);
|
|
168
|
+
successCount++;
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
errorCount++;
|
|
172
|
+
// also forward error to error handlers if any
|
|
173
|
+
if (this.callbacks.error) {
|
|
174
|
+
this.callbacks.error.forEach((eCb) => {
|
|
175
|
+
try {
|
|
176
|
+
eCb({
|
|
177
|
+
type: 'error',
|
|
178
|
+
data: {
|
|
179
|
+
message: 'Callback error',
|
|
180
|
+
error: this.serializeError(err),
|
|
181
|
+
},
|
|
182
|
+
timestamp: Date.now(),
|
|
183
|
+
processingTime: 0,
|
|
184
|
+
workerId: 'tracking-sdk',
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
catch (_a) { }
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
const executionTime = Date.now() - start;
|
|
194
|
+
// Respond to worker with results
|
|
195
|
+
this.sendToCallbackWorker({
|
|
196
|
+
type: 'executionResult',
|
|
197
|
+
requestId,
|
|
198
|
+
successCount,
|
|
199
|
+
errorCount,
|
|
200
|
+
executionTime,
|
|
201
|
+
});
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
case 'callbackReady':
|
|
205
|
+
debugTrackers.log('📨 Callback processed successfully');
|
|
206
|
+
break;
|
|
207
|
+
case 'error':
|
|
208
|
+
this.handleError(data);
|
|
209
|
+
break;
|
|
210
|
+
default:
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
this.callbackWorker.onerror = (error) => {
|
|
214
|
+
this.workerStatus.callbackWorker = 'error';
|
|
215
|
+
this.handleError({ message: 'Callback Worker crashed', error });
|
|
216
|
+
};
|
|
217
|
+
// Send initial config to Callback Worker
|
|
218
|
+
this.sendToCallbackWorker({
|
|
219
|
+
type: 'init',
|
|
220
|
+
config: this.config,
|
|
221
|
+
callbacks: Object.keys(this.callbacks), // Inform about registered callbacks
|
|
222
|
+
});
|
|
223
|
+
this.workerStatus.callbackWorker = 'ready';
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
this.handleError({ message: 'Failed to create Callback Worker', error });
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
startTracking() {
|
|
230
|
+
if (typeof window === 'undefined')
|
|
231
|
+
return;
|
|
232
|
+
debugTrackers.log('👁️ Starting event tracking...', this.config.events);
|
|
233
|
+
// Track page load events
|
|
234
|
+
if (this.config.events.includes(ITrackingEvent.PAGE_LOAD)) {
|
|
235
|
+
this.handlePageLoad();
|
|
236
|
+
}
|
|
237
|
+
// Track user clicks
|
|
238
|
+
this.config.events.includes(ITrackingEvent.CLICK) && document.addEventListener('click', this.handleClick);
|
|
239
|
+
// Track performance metrics
|
|
240
|
+
if (this.config.events.includes(ITrackingEvent.PERFORMANCE) && 'PerformanceObserver' in window) {
|
|
241
|
+
const observer = new PerformanceObserver((list) => {
|
|
242
|
+
list.getEntries().forEach((entry) => {
|
|
243
|
+
this.trackEvent(ITrackingEvent.PERFORMANCE_ENTRY, {
|
|
244
|
+
name: entry.name,
|
|
245
|
+
entryType: entry.entryType,
|
|
246
|
+
duration: Math.round(entry.duration),
|
|
247
|
+
startTime: Math.round(entry.startTime),
|
|
248
|
+
timestamp: Date.now(),
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
observer.observe({
|
|
253
|
+
entryTypes: ['navigation', 'paint', 'resource', 'largest-contentful-paint'],
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
debugTrackers.log('✅ All event listeners attached');
|
|
257
|
+
}
|
|
258
|
+
trackEvent(eventName, data, callback) {
|
|
259
|
+
if (!this.isInitialized) {
|
|
260
|
+
debugTrackers.warn('SDK not initialized. Call init() first.');
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const eventId = data.eventId || `ev_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
264
|
+
if (callback) {
|
|
265
|
+
this.perEventCallbacks.set(eventId, callback);
|
|
266
|
+
}
|
|
267
|
+
const eventData = {
|
|
268
|
+
type: 'track',
|
|
269
|
+
event: eventName,
|
|
270
|
+
data: Object.assign(Object.assign({}, data), { timestamp: Date.now(), eventId }),
|
|
271
|
+
};
|
|
272
|
+
debugTrackers.log('📤 Sending event to Event Worker:', eventName, eventData.data);
|
|
273
|
+
this.sendToEventWorker(eventData);
|
|
274
|
+
}
|
|
275
|
+
// Communication methods
|
|
276
|
+
sendToEventWorker(message) {
|
|
277
|
+
if (!this.eventWorker) {
|
|
278
|
+
debugTrackers.error('Event Worker not initialized');
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
this.eventWorker.postMessage(message);
|
|
282
|
+
}
|
|
283
|
+
sendToCallbackWorker(message) {
|
|
284
|
+
if (!this.callbackWorker) {
|
|
285
|
+
debugTrackers.error('Callback Worker not initialized');
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
this.callbackWorker.postMessage(message);
|
|
289
|
+
}
|
|
290
|
+
// Callback registration
|
|
291
|
+
on(eventType, callback) {
|
|
292
|
+
if (!this.callbacks[eventType]) {
|
|
293
|
+
this.callbacks[eventType] = [];
|
|
294
|
+
}
|
|
295
|
+
this.callbacks[eventType].push(callback);
|
|
296
|
+
debugTrackers.log(`🔗 Registered callback for "${eventType}". Total: ${this.callbacks[eventType].length}`);
|
|
297
|
+
// Notify Callback Worker about new callback
|
|
298
|
+
this.sendToCallbackWorker({
|
|
299
|
+
type: 'registerCallback',
|
|
300
|
+
eventType: eventType,
|
|
301
|
+
callbackCount: this.callbacks[eventType].length,
|
|
302
|
+
});
|
|
303
|
+
return () => {
|
|
304
|
+
// Return unsubscribe function
|
|
305
|
+
if (!this.callbacks[eventType])
|
|
306
|
+
return;
|
|
307
|
+
const index = this.callbacks[eventType].indexOf(callback);
|
|
308
|
+
if (index > -1) {
|
|
309
|
+
this.callbacks[eventType].splice(index, 1);
|
|
310
|
+
debugTrackers.log(`❌ Unregistered callback for "${eventType}"`);
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
// Error handling
|
|
315
|
+
handleError(errorData) {
|
|
316
|
+
const serialized = this.serializeError(errorData);
|
|
317
|
+
if (this.callbacks.error) {
|
|
318
|
+
this.callbacks.error.forEach((cb) => cb(serialized));
|
|
319
|
+
}
|
|
320
|
+
this.sendToCallbackWorker({
|
|
321
|
+
type: 'error',
|
|
322
|
+
data: serialized,
|
|
323
|
+
timestamp: Date.now(),
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
serializeError(input) {
|
|
327
|
+
try {
|
|
328
|
+
if (!input)
|
|
329
|
+
return { message: 'Unknown error' };
|
|
330
|
+
if (input instanceof Error) {
|
|
331
|
+
return { message: input.message, name: input.name, stack: input.stack };
|
|
332
|
+
}
|
|
333
|
+
if (typeof input === 'string')
|
|
334
|
+
return { message: input };
|
|
335
|
+
if (input.message || input.error) {
|
|
336
|
+
const err = input.error;
|
|
337
|
+
const base = {
|
|
338
|
+
message: input.message || 'Error',
|
|
339
|
+
context: undefined,
|
|
340
|
+
};
|
|
341
|
+
if (err instanceof Error) {
|
|
342
|
+
return Object.assign(Object.assign({}, base), { error: { message: err.message, name: err.name, stack: err.stack } });
|
|
343
|
+
}
|
|
344
|
+
try {
|
|
345
|
+
return Object.assign(Object.assign({}, base), { error: JSON.parse(JSON.stringify(err)) });
|
|
346
|
+
}
|
|
347
|
+
catch (_a) {
|
|
348
|
+
return Object.assign(Object.assign({}, base), { error: String(err) });
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return JSON.parse(JSON.stringify(input));
|
|
352
|
+
}
|
|
353
|
+
catch (_b) {
|
|
354
|
+
return { message: 'Unserializable error' };
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
// Health check
|
|
358
|
+
getStatus() {
|
|
359
|
+
return {
|
|
360
|
+
isInitialized: this.isInitialized,
|
|
361
|
+
workerStatus: this.workerStatus,
|
|
362
|
+
registeredCallbacks: Object.keys(this.callbacks),
|
|
363
|
+
totalCallbacks: Object.values(this.callbacks).reduce((sum, cbs) => sum + cbs.length, 0),
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
// Cleanup
|
|
367
|
+
destroy() {
|
|
368
|
+
debugTrackers.log('🧹 Destroying GXTrackingSDK...');
|
|
369
|
+
// Remove event listeners
|
|
370
|
+
if (typeof window !== 'undefined') {
|
|
371
|
+
window.removeEventListener('load', this.handlePageLoad);
|
|
372
|
+
document.removeEventListener('click', this.handleClick);
|
|
373
|
+
}
|
|
374
|
+
// Terminate workers
|
|
375
|
+
if (this.eventWorker) {
|
|
376
|
+
this.eventWorker.terminate();
|
|
377
|
+
this.eventWorker = null;
|
|
378
|
+
debugTrackers.log('🔌 Event Worker terminated');
|
|
379
|
+
}
|
|
380
|
+
if (this.callbackWorker) {
|
|
381
|
+
this.callbackWorker.terminate();
|
|
382
|
+
this.callbackWorker = null;
|
|
383
|
+
debugTrackers.log('🔌 Callback Worker terminated');
|
|
384
|
+
}
|
|
385
|
+
// Clear callbacks
|
|
386
|
+
this.callbacks = {};
|
|
387
|
+
this.isInitialized = false;
|
|
388
|
+
this.workerStatus = {
|
|
389
|
+
eventWorker: 'destroyed',
|
|
390
|
+
callbackWorker: 'destroyed',
|
|
391
|
+
};
|
|
392
|
+
debugTrackers.log('✅ GXTrackingSDK destroyed successfully');
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
// Create and expose the SDK instance
|
|
396
|
+
const gxTrackingSDK = new GXTrackingSDK();
|
|
397
|
+
// Make it available globally
|
|
398
|
+
if (typeof window !== 'undefined') {
|
|
399
|
+
window.GXTrackingSDK = gxTrackingSDK;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export { ITrackingEvent, gxTrackingSDK };
|