@metrone-io/sdk 1.0.1 → 1.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/dist/index.esm.js CHANGED
@@ -99,46 +99,51 @@ var Metrone = /** @class */ (function () {
99
99
  function Metrone(config) {
100
100
  var _this = this;
101
101
  this.isInitialized = false;
102
+ this.trackingActive = false;
102
103
  this.eventQueue = [];
103
104
  this.isOnline = true;
104
- this.version = '1.0.1';
105
+ this.version = '1.1.0';
105
106
  this.updateCheckInterval = null;
106
- this.config = __assign({ endpoint: 'https://api.metrone.io/v1/events', debug: false, autoTrack: true, autoUpdate: false, batchSize: 10, flushInterval: 5000, offlineQueue: true, maxQueueSize: 100, respectDoNotTrack: true, anonymizeIP: true, cookieConsent: 'optional' }, config);
107
- // Validate required config
107
+ this.flushTimerId = null;
108
+ this.boundVisibilityHandler = null;
109
+ this.boundBeforeUnloadHandler = null;
110
+ this.lastTrackedPath = '';
111
+ this.config = __assign({ endpoint: 'https://api.metrone.io/v1/events', debug: false, autoTrack: true, autoTrackSPA: false, autoUpdate: false, batchSize: 10, flushInterval: 5000, offlineQueue: true, maxQueueSize: 100, respectDoNotTrack: false, anonymizeIP: true, cookieConsent: 'optional' }, config);
108
112
  if (!this.config.apiKey) {
109
- throw new Error('API key is required');
113
+ throw new Error('[Metrone] API key is required');
110
114
  }
111
115
  var validPrefixes = ['metrone_live_', 'metrone_test_', 'mk_live_', 'mk_test_'];
112
116
  if (!validPrefixes.some(function (p) { return _this.config.apiKey.startsWith(p); })) {
113
- throw new Error('Invalid API key format. Must start with "metrone_live_", "metrone_test_", "mk_live_", or "mk_test_"');
117
+ throw new Error('[Metrone] Invalid API key format. Must start with "metrone_live_", "metrone_test_", "mk_live_", or "mk_test_"');
118
+ }
119
+ if (!this.config.batchEndpoint) {
120
+ this.config.batchEndpoint = this.config.endpoint.replace(/\/events\/?$/, '/events/batch');
114
121
  }
115
122
  this.sessionId = this.generateSessionId();
116
123
  this.isInitialized = true;
117
- // Check for Do Not Track
118
124
  if (this.config.respectDoNotTrack && this.isDoNotTrackEnabled()) {
119
- if (this.config.debug) {
120
- console.log('🔒 Do Not Track is enabled, analytics disabled');
121
- }
125
+ console.warn('[Metrone] Do Not Track detected — analytics disabled. Set respectDoNotTrack: false to override.');
122
126
  return;
123
127
  }
124
- // Check for cookie consent
125
128
  if (this.config.cookieConsent === 'required' && !this.hasConsent()) {
126
- if (this.config.debug) {
127
- console.log('🍪 Cookie consent required, analytics disabled');
128
- }
129
+ console.warn('[Metrone] Cookie consent required but not granted — analytics disabled.');
129
130
  return;
130
131
  }
131
- // Set up online/offline detection
132
+ this.trackingActive = true;
132
133
  this.setupOnlineDetection();
134
+ this.setupPageUnloadFlush();
133
135
  if (this.config.autoTrack) {
136
+ this.lastTrackedPath = window.location.pathname;
134
137
  this.pageview();
135
138
  }
136
- // Set up batch processing
139
+ if (this.config.autoTrackSPA) {
140
+ this.setupSPATracking();
141
+ }
137
142
  if (this.config.batchSize && this.config.batchSize > 1) {
138
143
  this.setupBatchProcessing();
139
144
  }
140
145
  if (this.config.debug) {
141
- console.log('Metrone SDK initialized:', {
146
+ console.log('[Metrone] SDK initialized:', {
142
147
  apiKey: this.config.apiKey.substring(0, 12) + '...',
143
148
  projectId: this.config.projectId,
144
149
  sessionId: this.sessionId,
@@ -147,11 +152,17 @@ var Metrone = /** @class */ (function () {
147
152
  });
148
153
  }
149
154
  }
155
+ /**
156
+ * Whether the SDK is actively tracking events (not disabled by DNT/consent).
157
+ */
158
+ Metrone.prototype.isActive = function () {
159
+ return this.isInitialized && this.trackingActive;
160
+ };
150
161
  /**
151
162
  * Track a page view
152
163
  */
153
164
  Metrone.prototype.pageview = function (url, title, metadata) {
154
- if (!this.isInitialized)
165
+ if (!this.isInitialized || !this.trackingActive)
155
166
  return;
156
167
  this.track('pageview', __assign({ page_url: url || window.location.href, page_path: window.location.pathname, page_title: title || document.title, referrer: document.referrer, source: 'web' }, metadata));
157
168
  };
@@ -160,9 +171,11 @@ var Metrone = /** @class */ (function () {
160
171
  */
161
172
  Metrone.prototype.track = function (eventName, data) {
162
173
  if (!this.isInitialized) {
163
- console.warn('Analytics SDK not initialized');
174
+ console.warn('[Metrone] SDK not initialized');
164
175
  return;
165
176
  }
177
+ if (!this.trackingActive)
178
+ return;
166
179
  var _a = (data !== null && data !== void 0 ? data : {}), source = _a.source, channel = _a.channel, ai_provider = _a.ai_provider, ai_call_id = _a.ai_call_id, ai_session_id = _a.ai_session_id, ai_intent = _a.ai_intent, ai_duration_sec = _a.ai_duration_sec, utm_source = _a.utm_source, utm_medium = _a.utm_medium, utm_campaign = _a.utm_campaign, utm_term = _a.utm_term, utm_content = _a.utm_content, page_path = _a.page_path, page_title = _a.page_title, dataPageUrl = _a.page_url, dataReferrer = _a.referrer, event_name = _a.event_name, rest = __rest(_a, ["source", "channel", "ai_provider", "ai_call_id", "ai_session_id", "ai_intent", "ai_duration_sec", "utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content", "page_path", "page_title", "page_url", "referrer", "event_name"]);
167
180
  var eventData = {
168
181
  event_type: eventName,
@@ -254,9 +267,6 @@ var Metrone = /** @class */ (function () {
254
267
  Metrone.prototype.trackAISession = function (data) {
255
268
  this.track("ai_session_".concat(data.action), __assign({ source: 'assistant', ai_provider: data.provider || 'unknown', ai_session_id: data.session_id, ai_duration_sec: data.duration }, data.metadata));
256
269
  };
257
- /**
258
- * Send event to analytics API
259
- */
260
270
  Metrone.prototype.sendEvent = function (eventData) {
261
271
  return __awaiter(this, void 0, void 0, function () {
262
272
  var requestData, error_1;
@@ -266,7 +276,6 @@ var Metrone = /** @class */ (function () {
266
276
  _a.trys.push([0, 6, , 7]);
267
277
  requestData = __assign(__assign({}, eventData), { api_key: this.config.apiKey });
268
278
  if (!(this.config.batchSize && this.config.batchSize > 1)) return [3 /*break*/, 3];
269
- // Add to batch queue
270
279
  this.eventQueue.push(requestData);
271
280
  if (!(this.eventQueue.length >= this.config.batchSize)) return [3 /*break*/, 2];
272
281
  return [4 /*yield*/, this.flushBatch()];
@@ -274,20 +283,14 @@ var Metrone = /** @class */ (function () {
274
283
  _a.sent();
275
284
  _a.label = 2;
276
285
  case 2: return [3 /*break*/, 5];
277
- case 3:
278
- // Send immediately
279
- return [4 /*yield*/, this.sendRequest(requestData)];
286
+ case 3: return [4 /*yield*/, this.sendRequest(requestData)];
280
287
  case 4:
281
- // Send immediately
282
288
  _a.sent();
283
289
  _a.label = 5;
284
290
  case 5: return [3 /*break*/, 7];
285
291
  case 6:
286
292
  error_1 = _a.sent();
287
- if (this.config.debug) {
288
- console.error('Failed to send analytics event:', error_1);
289
- }
290
- // Queue for retry if offline
293
+ console.warn('[Metrone] Failed to send event:', error_1.message);
291
294
  if (this.config.offlineQueue && !this.isOnline) {
292
295
  this.queueEvent(eventData);
293
296
  }
@@ -297,9 +300,6 @@ var Metrone = /** @class */ (function () {
297
300
  });
298
301
  });
299
302
  };
300
- /**
301
- * Send request to analytics API
302
- */
303
303
  Metrone.prototype.sendRequest = function (data) {
304
304
  return __awaiter(this, void 0, void 0, function () {
305
305
  var response;
@@ -307,30 +307,25 @@ var Metrone = /** @class */ (function () {
307
307
  switch (_a.label) {
308
308
  case 0: return [4 /*yield*/, fetch(this.config.endpoint, {
309
309
  method: 'POST',
310
- headers: {
311
- 'Content-Type': 'application/json',
312
- },
310
+ headers: { 'Content-Type': 'application/json' },
313
311
  body: JSON.stringify(data)
314
312
  })];
315
313
  case 1:
316
314
  response = _a.sent();
317
315
  if (!response.ok) {
318
- throw new Error("Analytics API error: ".concat(response.status, " ").concat(response.statusText));
316
+ throw new Error("".concat(response.status, " ").concat(response.statusText));
319
317
  }
320
318
  if (this.config.debug) {
321
- console.log('Analytics event sent:', data.event_type);
319
+ console.log('[Metrone] Event sent:', data.event_type);
322
320
  }
323
321
  return [2 /*return*/];
324
322
  }
325
323
  });
326
324
  });
327
325
  };
328
- /**
329
- * Flush batch of events
330
- */
331
326
  Metrone.prototype.flushBatch = function () {
332
327
  return __awaiter(this, void 0, void 0, function () {
333
- var batch, batchEndpoint, error_2;
328
+ var batch, error_2;
334
329
  var _a;
335
330
  return __generator(this, function (_b) {
336
331
  switch (_b.label) {
@@ -342,19 +337,16 @@ var Metrone = /** @class */ (function () {
342
337
  _b.label = 1;
343
338
  case 1:
344
339
  _b.trys.push([1, 3, , 4]);
345
- batchEndpoint = this.config.endpoint.replace(/\/events\/?$/, '/events/batch');
346
- return [4 /*yield*/, this.sendBatchRequest(batchEndpoint, batch)];
340
+ return [4 /*yield*/, this.sendBatchRequest(batch)];
347
341
  case 2:
348
342
  _b.sent();
349
343
  if (this.config.debug) {
350
- console.log("Batch sent: ".concat(batch.length, " events"));
344
+ console.log("[Metrone] Batch sent: ".concat(batch.length, " events"));
351
345
  }
352
346
  return [3 /*break*/, 4];
353
347
  case 3:
354
348
  error_2 = _b.sent();
355
- if (this.config.debug) {
356
- console.error('Failed to send batch:', error_2);
357
- }
349
+ console.warn('[Metrone] Failed to send batch:', error_2.message);
358
350
  if (this.config.offlineQueue && !this.isOnline) {
359
351
  (_a = this.eventQueue).unshift.apply(_a, batch);
360
352
  }
@@ -364,22 +356,20 @@ var Metrone = /** @class */ (function () {
364
356
  });
365
357
  });
366
358
  };
367
- Metrone.prototype.sendBatchRequest = function (endpoint, events) {
359
+ Metrone.prototype.sendBatchRequest = function (events) {
368
360
  return __awaiter(this, void 0, void 0, function () {
369
361
  var response;
370
362
  return __generator(this, function (_a) {
371
363
  switch (_a.label) {
372
- case 0: return [4 /*yield*/, fetch(endpoint, {
364
+ case 0: return [4 /*yield*/, fetch(this.config.batchEndpoint, {
373
365
  method: 'POST',
374
- headers: {
375
- 'Content-Type': 'application/json',
376
- },
366
+ headers: { 'Content-Type': 'application/json' },
377
367
  body: JSON.stringify(events)
378
368
  })];
379
369
  case 1:
380
370
  response = _a.sent();
381
371
  if (!response.ok) {
382
- throw new Error("Analytics API error: ".concat(response.status, " ").concat(response.statusText));
372
+ throw new Error("".concat(response.status, " ").concat(response.statusText));
383
373
  }
384
374
  return [2 /*return*/];
385
375
  }
@@ -387,20 +377,33 @@ var Metrone = /** @class */ (function () {
387
377
  });
388
378
  };
389
379
  /**
390
- * Queue event for offline processing
380
+ * Flush queued events using sendBeacon (survives page unload).
391
381
  */
382
+ Metrone.prototype.beaconFlush = function () {
383
+ if (this.eventQueue.length === 0)
384
+ return;
385
+ var batch = __spreadArray([], this.eventQueue, true);
386
+ this.eventQueue = [];
387
+ try {
388
+ var payload = JSON.stringify(batch);
389
+ var sent = navigator.sendBeacon(this.config.batchEndpoint, payload);
390
+ if (!sent && this.config.debug) {
391
+ console.warn('[Metrone] sendBeacon failed, events may be lost');
392
+ }
393
+ }
394
+ catch (_a) {
395
+ // sendBeacon not available or failed — nothing we can do at unload time
396
+ }
397
+ };
392
398
  Metrone.prototype.queueEvent = function (eventData) {
393
399
  if (this.eventQueue.length >= (this.config.maxQueueSize || 100)) {
394
- this.eventQueue.shift(); // Remove oldest event
400
+ this.eventQueue.shift();
395
401
  }
396
402
  this.eventQueue.push(eventData);
397
403
  };
398
- /**
399
- * Process offline queue when back online
400
- */
401
404
  Metrone.prototype.processQueue = function () {
402
405
  return __awaiter(this, void 0, void 0, function () {
403
- var queue, _i, queue_1, event_1, error_3;
406
+ var queue, _i, queue_1, event_1;
404
407
  return __generator(this, function (_a) {
405
408
  switch (_a.label) {
406
409
  case 0:
@@ -421,11 +424,7 @@ var Metrone = /** @class */ (function () {
421
424
  _a.sent();
422
425
  return [3 /*break*/, 5];
423
426
  case 4:
424
- error_3 = _a.sent();
425
- if (this.config.debug) {
426
- console.error('Failed to process queued event:', error_3);
427
- }
428
- // Re-queue failed events
427
+ _a.sent();
429
428
  this.eventQueue.push(event_1);
430
429
  return [3 /*break*/, 5];
431
430
  case 5:
@@ -433,58 +432,80 @@ var Metrone = /** @class */ (function () {
433
432
  return [3 /*break*/, 1];
434
433
  case 6:
435
434
  if (this.config.debug) {
436
- console.log("\uD83D\uDD04 Processed ".concat(queue.length, " queued events"));
435
+ console.log("[Metrone] Processed ".concat(queue.length, " queued events"));
437
436
  }
438
437
  return [2 /*return*/];
439
438
  }
440
439
  });
441
440
  });
442
441
  };
443
- /**
444
- * Set up online/offline detection
445
- */
446
442
  Metrone.prototype.setupOnlineDetection = function () {
447
443
  var _this = this;
448
444
  this.isOnline = navigator.onLine;
449
445
  window.addEventListener('online', function () {
450
446
  _this.isOnline = true;
451
447
  if (_this.config.debug) {
452
- console.log('🌐 Back online, processing queue');
448
+ console.log('[Metrone] Back online, processing queue');
453
449
  }
454
450
  _this.processQueue();
455
451
  });
456
452
  window.addEventListener('offline', function () {
457
453
  _this.isOnline = false;
458
454
  if (_this.config.debug) {
459
- console.log('📴 Gone offline, queuing events');
455
+ console.log('[Metrone] Gone offline, queuing events');
460
456
  }
461
457
  });
462
458
  };
463
- /**
464
- * Set up batch processing
465
- */
466
459
  Metrone.prototype.setupBatchProcessing = function () {
467
460
  var _this = this;
468
461
  if (this.config.flushInterval && this.config.flushInterval > 0) {
469
- setInterval(function () {
462
+ this.flushTimerId = window.setInterval(function () {
470
463
  _this.flushBatch();
471
464
  }, this.config.flushInterval);
472
465
  }
473
466
  };
474
467
  /**
475
- * Set up auto-updates
468
+ * Flush pending events on page hide / beforeunload using sendBeacon.
469
+ */
470
+ Metrone.prototype.setupPageUnloadFlush = function () {
471
+ var _this = this;
472
+ this.boundVisibilityHandler = function () {
473
+ if (document.visibilityState === 'hidden') {
474
+ _this.beaconFlush();
475
+ }
476
+ };
477
+ this.boundBeforeUnloadHandler = function () {
478
+ _this.beaconFlush();
479
+ };
480
+ document.addEventListener('visibilitychange', this.boundVisibilityHandler);
481
+ window.addEventListener('beforeunload', this.boundBeforeUnloadHandler);
482
+ };
483
+ /**
484
+ * Auto-track SPA route changes via History API and popstate.
476
485
  */
477
- Metrone.prototype.setupAutoUpdates = function () {
486
+ Metrone.prototype.setupSPATracking = function () {
478
487
  var _this = this;
479
- // Check for updates every hour
480
- this.updateCheckInterval = window.setInterval(function () {
481
- _this.checkForUpdates();
482
- }, 60 * 60 * 1000);
483
- // Initial check
484
- this.checkForUpdates();
488
+ var origPushState = history.pushState.bind(history);
489
+ var origReplaceState = history.replaceState.bind(history);
490
+ var onRouteChange = function () {
491
+ var newPath = window.location.pathname;
492
+ if (newPath !== _this.lastTrackedPath) {
493
+ _this.lastTrackedPath = newPath;
494
+ setTimeout(function () { return _this.pageview(); }, 0);
495
+ }
496
+ };
497
+ history.pushState = function (data, unused, url) {
498
+ origPushState(data, unused, url);
499
+ onRouteChange();
500
+ };
501
+ history.replaceState = function (data, unused, url) {
502
+ origReplaceState(data, unused, url);
503
+ onRouteChange();
504
+ };
505
+ window.addEventListener('popstate', onRouteChange);
485
506
  };
486
507
  /**
487
- * Check for SDK updates
508
+ * Check for SDK updates (only relevant for script-tag installations).
488
509
  */
489
510
  Metrone.prototype.checkForUpdates = function () {
490
511
  return __awaiter(this, void 0, void 0, function () {
@@ -492,21 +513,35 @@ var Metrone = /** @class */ (function () {
492
513
  return __generator(this, function (_a) {
493
514
  switch (_a.label) {
494
515
  case 0:
495
- _a.trys.push([0, 3, , 4]);
496
- return [4 /*yield*/, fetch('/api/analytics/version')];
516
+ if (this.isModuleContext()) {
517
+ if (this.config.debug) {
518
+ console.log('[Metrone] Auto-update is not supported in module/bundler context. Update via npm instead.');
519
+ }
520
+ return [2 /*return*/, {
521
+ available: false,
522
+ current: this.version,
523
+ latest: this.version,
524
+ features: [],
525
+ changelog: []
526
+ }];
527
+ }
528
+ _a.label = 1;
497
529
  case 1:
530
+ _a.trys.push([1, 4, , 5]);
531
+ return [4 /*yield*/, fetch('/api/analytics/version')];
532
+ case 2:
498
533
  response = _a.sent();
499
534
  return [4 /*yield*/, response.json()];
500
- case 2:
535
+ case 3:
501
536
  updateInfo = _a.sent();
502
537
  if (updateInfo.latest !== this.version) {
503
538
  this.handleUpdate(updateInfo);
504
539
  }
505
540
  return [2 /*return*/, updateInfo];
506
- case 3:
541
+ case 4:
507
542
  error_4 = _a.sent();
508
543
  if (this.config.debug) {
509
- console.log('Update check failed:', error_4);
544
+ console.log('[Metrone] Update check failed:', error_4);
510
545
  }
511
546
  return [2 /*return*/, {
512
547
  available: false,
@@ -515,87 +550,76 @@ var Metrone = /** @class */ (function () {
515
550
  features: [],
516
551
  changelog: []
517
552
  }];
518
- case 4: return [2 /*return*/];
553
+ case 5: return [2 /*return*/];
519
554
  }
520
555
  });
521
556
  });
522
557
  };
523
- /**
524
- * Handle SDK update
525
- */
526
558
  Metrone.prototype.handleUpdate = function (updateInfo) {
527
- if (this.config.autoUpdate !== false) {
559
+ if (this.config.autoUpdate !== false && !this.isModuleContext()) {
528
560
  this.loadNewVersion(updateInfo.latest);
529
561
  }
530
562
  else {
531
563
  this.notifyUpdate(updateInfo);
532
564
  }
533
565
  };
534
- /**
535
- * Load new SDK version
536
- */
537
566
  Metrone.prototype.loadNewVersion = function (version) {
538
567
  var _this = this;
539
568
  if (this.config.debug) {
540
- console.log("\uD83D\uDD04 Updating SDK to version ".concat(version));
569
+ console.log("[Metrone] Updating SDK to version ".concat(version));
541
570
  }
542
- // Reload the SDK script
543
571
  var script = document.createElement('script');
544
572
  script.src = '/js/metrone.js';
545
573
  script.onload = function () {
546
574
  if (_this.config.debug) {
547
- console.log("\u2705 SDK updated to version ".concat(version));
575
+ console.log("[Metrone] SDK updated to version ".concat(version));
548
576
  }
549
577
  };
550
578
  document.head.appendChild(script);
551
579
  };
552
- /**
553
- * Notify about available update
554
- */
555
580
  Metrone.prototype.notifyUpdate = function (updateInfo) {
556
581
  if (this.config.debug) {
557
- console.log("\uD83D\uDCE2 SDK update available: ".concat(updateInfo.latest));
558
- console.log('New features:', updateInfo.features);
582
+ console.log("[Metrone] SDK update available: ".concat(updateInfo.latest));
559
583
  }
560
- // Dispatch custom event for update notification
561
584
  window.dispatchEvent(new CustomEvent('metrone-update', {
562
585
  detail: updateInfo
563
586
  }));
564
587
  };
565
588
  /**
566
- * Generate session ID
589
+ * Detect whether the SDK is running inside a bundler/module context
590
+ * vs. a plain script-tag.
567
591
  */
592
+ Metrone.prototype.isModuleContext = function () {
593
+ try {
594
+ // If this code is part of a bundle, `module` or import.meta will be defined.
595
+ // The simplest heuristic: check if the current script tag has type="module"
596
+ // or if we're in a context where document.currentScript is null (bundled).
597
+ return typeof document === 'undefined' || document.currentScript === null;
598
+ }
599
+ catch (_a) {
600
+ return true;
601
+ }
602
+ };
568
603
  Metrone.prototype.generateSessionId = function () {
569
604
  var timestamp = Date.now().toString(36);
570
605
  var random = Math.random().toString(36).substring(2, 15);
571
606
  return "sess_".concat(timestamp, "_").concat(random);
572
607
  };
573
- /**
574
- * Check if Do Not Track is enabled
575
- */
576
608
  Metrone.prototype.isDoNotTrackEnabled = function () {
577
609
  return navigator.doNotTrack === '1' ||
578
610
  navigator.doNotTrack === 'yes' ||
579
611
  window.doNotTrack === '1';
580
612
  };
581
- /**
582
- * Check if user has given consent
583
- */
584
613
  Metrone.prototype.hasConsent = function () {
585
- // Check for common consent management platforms
586
614
  var consent = localStorage.getItem('cookie-consent') ||
587
615
  localStorage.getItem('gdpr-consent') ||
588
616
  localStorage.getItem('cc-consent');
589
617
  return consent === 'accepted' || consent === 'true';
590
618
  };
591
- /**
592
- * Request user consent
593
- */
594
619
  Metrone.prototype.requestConsent = function () {
595
620
  return __awaiter(this, void 0, void 0, function () {
596
621
  return __generator(this, function (_a) {
597
622
  return [2 /*return*/, new Promise(function (resolve) {
598
- // Dispatch custom event for consent request
599
623
  window.dispatchEvent(new CustomEvent('metrone-consent-request', {
600
624
  detail: { resolve: resolve }
601
625
  }));
@@ -603,45 +627,30 @@ var Metrone = /** @class */ (function () {
603
627
  });
604
628
  });
605
629
  };
606
- /**
607
- * Revoke user consent
608
- */
609
630
  Metrone.prototype.revokeConsent = function () {
610
631
  localStorage.removeItem('cookie-consent');
611
632
  localStorage.removeItem('gdpr-consent');
612
633
  localStorage.removeItem('cc-consent');
613
634
  if (this.config.debug) {
614
- console.log('🔒 Consent revoked, analytics disabled');
635
+ console.log('[Metrone] Consent revoked, analytics disabled');
615
636
  }
616
637
  };
617
- /**
618
- * Anonymize data
619
- */
620
638
  Metrone.prototype.anonymize = function (data) {
621
639
  var hash = this.simpleHash(data);
622
640
  return "anon_".concat(hash);
623
641
  };
624
- /**
625
- * Simple hash function
626
- */
627
642
  Metrone.prototype.simpleHash = function (str) {
628
643
  var hash = 0;
629
644
  for (var i = 0; i < str.length; i++) {
630
645
  var char = str.charCodeAt(i);
631
646
  hash = ((hash << 5) - hash) + char;
632
- hash = hash & hash; // Convert to 32-bit integer
647
+ hash = hash & hash;
633
648
  }
634
649
  return Math.abs(hash).toString(36);
635
650
  };
636
- /**
637
- * Check if running on mobile device
638
- */
639
651
  Metrone.prototype.isMobile = function () {
640
652
  return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
641
653
  };
642
- /**
643
- * Get screen size
644
- */
645
654
  Metrone.prototype.getScreenSize = function () {
646
655
  var width = window.screen.width;
647
656
  if (width < 768)
@@ -650,62 +659,54 @@ var Metrone = /** @class */ (function () {
650
659
  return 'tablet';
651
660
  return 'desktop';
652
661
  };
653
- /**
654
- * Flush pending events
655
- */
656
662
  Metrone.prototype.flush = function () {
657
663
  this.flushBatch();
658
664
  };
659
- /**
660
- * Destroy SDK instance
661
- */
662
665
  Metrone.prototype.destroy = function () {
663
666
  if (this.updateCheckInterval) {
664
667
  clearInterval(this.updateCheckInterval);
665
668
  }
666
- this.flush();
669
+ if (this.flushTimerId) {
670
+ clearInterval(this.flushTimerId);
671
+ }
672
+ if (this.boundVisibilityHandler) {
673
+ document.removeEventListener('visibilitychange', this.boundVisibilityHandler);
674
+ }
675
+ if (this.boundBeforeUnloadHandler) {
676
+ window.removeEventListener('beforeunload', this.boundBeforeUnloadHandler);
677
+ }
678
+ this.beaconFlush();
667
679
  this.isInitialized = false;
680
+ this.trackingActive = false;
668
681
  if (this.config.debug) {
669
- console.log('🗑️ Metrone SDK destroyed');
682
+ console.log('[Metrone] SDK destroyed');
670
683
  }
671
684
  };
672
- /**
673
- * Get current version
674
- */
675
685
  Metrone.prototype.getVersion = function () {
676
686
  return this.version;
677
687
  };
678
- /**
679
- * Get session ID
680
- */
681
688
  Metrone.prototype.getSessionId = function () {
682
689
  return this.sessionId;
683
690
  };
684
- /**
685
- * Get configuration
686
- */
687
691
  Metrone.prototype.getConfig = function () {
688
692
  return __assign({}, this.config);
689
693
  };
690
694
  return Metrone;
691
695
  }());
692
- // Global initialization function
696
+ var globalAnalytics = null;
693
697
  function initAnalytics(config) {
694
- return new Metrone(config);
698
+ globalAnalytics = new Metrone(config);
699
+ return globalAnalytics;
695
700
  }
696
- // Global instance for backward compatibility
697
- var globalAnalytics = null;
698
701
  function getAnalytics() {
699
702
  return globalAnalytics;
700
703
  }
701
- // Auto-initialize if config is provided via window
702
704
  if (typeof window !== 'undefined') {
703
705
  var config = window.MetroneConfig;
704
706
  if (config) {
705
707
  globalAnalytics = new Metrone(config);
706
708
  }
707
709
  }
708
- // Export for global usage
709
710
  if (typeof window !== 'undefined') {
710
711
  window.Metrone = Metrone;
711
712
  window.initAnalytics = initAnalytics;
@@ -718,7 +719,7 @@ if (typeof window !== 'undefined') {
718
719
  */
719
720
  // Core SDK
720
721
  // Version
721
- var VERSION = '1.0.1';
722
+ var VERSION = '1.1.0';
722
723
 
723
724
  export { Metrone, VERSION, Metrone as default, getAnalytics, initAnalytics };
724
725
  //# sourceMappingURL=index.esm.js.map