@reservamos/browser-analytics 1.1.7 → 1.1.8-alpha.1
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/CHANGELOG.md +13 -0
- package/README.md +16 -10
- package/dist/browser-analytics.cjs +2 -2
- package/dist/browser-analytics.cjs.map +1 -1
- package/dist/browser-analytics.esm.js +500 -81
- package/dist/browser-analytics.esm.js.map +1 -1
- package/dist/browser-analytics.iife.js +2 -2
- package/dist/browser-analytics.iife.js.map +1 -1
- package/package.json +2 -2
- package/src/events/sharedSchemas/tripSchema.ts +3 -8
- package/src/init.botDetection.test.ts +187 -0
- package/src/init.test.ts +288 -0
- package/src/init.ts +47 -12
- package/src/services/deferredBotDetection.ts +60 -0
- package/src/services/fingerprint.test.ts +36 -0
- package/src/services/fingerprint.ts +38 -20
- package/src/services/mixpanel.test.ts +103 -0
- package/src/services/mixpanel.ts +70 -21
- package/src/services/trackingLifecycle.ts +54 -0
- package/src/services/validator/validator.test.ts +79 -0
- package/src/track.readiness.test.ts +131 -0
- package/src/track.recovery.test.ts +339 -0
- package/src/track.storage.test.ts +127 -0
- package/src/track.test.ts +435 -66
- package/src/track.ts +174 -25
- package/src/util/pendingWork.test.ts +164 -0
- package/src/util/pendingWork.ts +148 -0
- package/src/util/snapshotEventData.ts +15 -0
|
@@ -11260,9 +11260,9 @@ MixpanelRecorder.prototype._flushEvents = addOptOutCheckMixpanelLib(function(dat
|
|
|
11260
11260
|
if (deviceId) {
|
|
11261
11261
|
reqParams["$device_id"] = deviceId;
|
|
11262
11262
|
}
|
|
11263
|
-
var
|
|
11264
|
-
if (
|
|
11265
|
-
reqParams["$user_id"] =
|
|
11263
|
+
var userId2 = this._mixpanel.get_property("$user_id");
|
|
11264
|
+
if (userId2) {
|
|
11265
|
+
reqParams["$user_id"] = userId2;
|
|
11266
11266
|
}
|
|
11267
11267
|
if (CompressionStream) {
|
|
11268
11268
|
var jsonStream = new Blob([eventsJson], { type: "application/json" }).stream();
|
|
@@ -13339,6 +13339,50 @@ function loadNoop(_src, onload) {
|
|
|
13339
13339
|
onload();
|
|
13340
13340
|
}
|
|
13341
13341
|
var mixpanel = init_as_module(loadNoop);
|
|
13342
|
+
let generation = 0;
|
|
13343
|
+
let initialized = false;
|
|
13344
|
+
let userId;
|
|
13345
|
+
const subscribers = /* @__PURE__ */ new Set();
|
|
13346
|
+
const initializationSubscribers = /* @__PURE__ */ new Set();
|
|
13347
|
+
function invalidate$1() {
|
|
13348
|
+
generation++;
|
|
13349
|
+
subscribers.forEach((notify) => notify());
|
|
13350
|
+
}
|
|
13351
|
+
function beginInitialization() {
|
|
13352
|
+
if (initialized) invalidate$1();
|
|
13353
|
+
initialized = true;
|
|
13354
|
+
userId = void 0;
|
|
13355
|
+
initializationSubscribers.forEach((notify) => notify());
|
|
13356
|
+
}
|
|
13357
|
+
function identify$2(nextUserId) {
|
|
13358
|
+
if (userId !== nextUserId) {
|
|
13359
|
+
userId = nextUserId;
|
|
13360
|
+
invalidate$1();
|
|
13361
|
+
}
|
|
13362
|
+
}
|
|
13363
|
+
function capture() {
|
|
13364
|
+
const captured = generation;
|
|
13365
|
+
return () => captured === generation;
|
|
13366
|
+
}
|
|
13367
|
+
function onChange(notify) {
|
|
13368
|
+
subscribers.add(notify);
|
|
13369
|
+
return () => {
|
|
13370
|
+
subscribers.delete(notify);
|
|
13371
|
+
};
|
|
13372
|
+
}
|
|
13373
|
+
function onInitialization(notify) {
|
|
13374
|
+
initializationSubscribers.add(notify);
|
|
13375
|
+
return () => {
|
|
13376
|
+
initializationSubscribers.delete(notify);
|
|
13377
|
+
};
|
|
13378
|
+
}
|
|
13379
|
+
const trackingLifecycle = {
|
|
13380
|
+
beginInitialization,
|
|
13381
|
+
identify: identify$2,
|
|
13382
|
+
capture,
|
|
13383
|
+
onChange,
|
|
13384
|
+
onInitialization
|
|
13385
|
+
};
|
|
13342
13386
|
function createIdentifyReporter() {
|
|
13343
13387
|
const reported = /* @__PURE__ */ new Set();
|
|
13344
13388
|
return (stage, reason, httpStatus) => {
|
|
@@ -13373,16 +13417,16 @@ function waitForIdentifyReady(report) {
|
|
|
13373
13417
|
if (!active) return;
|
|
13374
13418
|
active = false;
|
|
13375
13419
|
clearTimeout(timer);
|
|
13376
|
-
window.removeEventListener("Tracker Ready",
|
|
13420
|
+
window.removeEventListener("Tracker Ready", onReady2);
|
|
13377
13421
|
if (!ready) report("readiness", "timeout");
|
|
13378
13422
|
resolve(ready);
|
|
13379
13423
|
};
|
|
13380
|
-
const
|
|
13424
|
+
const onReady2 = () => {
|
|
13381
13425
|
if (mixpanelService.isReady()) finish(true);
|
|
13382
13426
|
};
|
|
13383
13427
|
const timer = setTimeout(() => finish(false), 5e3);
|
|
13384
|
-
window.addEventListener("Tracker Ready",
|
|
13385
|
-
|
|
13428
|
+
window.addEventListener("Tracker Ready", onReady2);
|
|
13429
|
+
onReady2();
|
|
13386
13430
|
});
|
|
13387
13431
|
}
|
|
13388
13432
|
function waitForIdentifyStage(stage, timeout, run, report) {
|
|
@@ -13427,32 +13471,63 @@ function observeIdentifyResult(stage, report, onTimeout) {
|
|
|
13427
13471
|
}, 5e3);
|
|
13428
13472
|
return { complete };
|
|
13429
13473
|
}
|
|
13430
|
-
const version = "1.1.
|
|
13474
|
+
const version = "1.1.8-alpha.1";
|
|
13431
13475
|
const MIXPANEL_DISTINCT_ID_CACHE_KEY = "mp_distinct_id";
|
|
13476
|
+
const readinessSubscribers$1 = /* @__PURE__ */ new Set();
|
|
13477
|
+
let initializing = false;
|
|
13478
|
+
let initialization$2 = 0;
|
|
13479
|
+
let initializedConfig;
|
|
13480
|
+
function onReady$1(callback) {
|
|
13481
|
+
readinessSubscribers$1.add(callback);
|
|
13482
|
+
return () => {
|
|
13483
|
+
readinessSubscribers$1.delete(callback);
|
|
13484
|
+
};
|
|
13485
|
+
}
|
|
13432
13486
|
const SDK_SUPER_PROPERTIES = {
|
|
13433
13487
|
"Event Source": "sdk",
|
|
13434
13488
|
"Reservamos Version": version
|
|
13435
13489
|
};
|
|
13436
13490
|
function init$2(mixpanelToken, debug = false, proxyUrl) {
|
|
13491
|
+
validateConfiguration(mixpanelToken, proxyUrl);
|
|
13492
|
+
const attempt = ++initialization$2;
|
|
13493
|
+
initializing = true;
|
|
13494
|
+
trackingLifecycle.beginInitialization();
|
|
13495
|
+
if (initializedConfig && window.mixpanel === mixpanel) {
|
|
13496
|
+
mixpanel.set_config({ debug });
|
|
13497
|
+
initializing = false;
|
|
13498
|
+
readinessSubscribers$1.forEach((notify) => notify());
|
|
13499
|
+
return Promise.resolve();
|
|
13500
|
+
}
|
|
13437
13501
|
return new Promise((resolve) => {
|
|
13438
13502
|
mixpanel.init(mixpanelToken, {
|
|
13439
13503
|
debug,
|
|
13440
13504
|
loaded: () => {
|
|
13505
|
+
if (attempt !== initialization$2) {
|
|
13506
|
+
resolve();
|
|
13507
|
+
return;
|
|
13508
|
+
}
|
|
13441
13509
|
window.mixpanel = mixpanel;
|
|
13442
13510
|
mixpanel.register({ ...SDK_SUPER_PROPERTIES });
|
|
13511
|
+
initializedConfig = { token: mixpanelToken, proxyUrl };
|
|
13512
|
+
initializing = false;
|
|
13513
|
+
readinessSubscribers$1.forEach((notify) => notify());
|
|
13443
13514
|
resolve();
|
|
13444
13515
|
},
|
|
13445
13516
|
...proxyUrl && { api_host: proxyUrl }
|
|
13446
13517
|
});
|
|
13447
13518
|
});
|
|
13448
13519
|
}
|
|
13449
|
-
function
|
|
13450
|
-
mixpanel.
|
|
13451
|
-
|
|
13452
|
-
|
|
13453
|
-
|
|
13454
|
-
report == null ? void 0 : report("cache", "unavailable");
|
|
13520
|
+
function validateConfiguration(token, proxyUrl) {
|
|
13521
|
+
if (initializedConfig && window.mixpanel === mixpanel && (initializedConfig.token !== token || initializedConfig.proxyUrl !== proxyUrl)) {
|
|
13522
|
+
throw new Error(
|
|
13523
|
+
"Changing the Mixpanel project or proxy requires a new page session."
|
|
13524
|
+
);
|
|
13455
13525
|
}
|
|
13526
|
+
}
|
|
13527
|
+
function identify$1(userId2, properties, report) {
|
|
13528
|
+
trackingLifecycle.identify(userId2);
|
|
13529
|
+
mixpanel.identify(userId2);
|
|
13530
|
+
if (!updateCachedDistinctId(userId2)) report == null ? void 0 : report("cache", "unavailable");
|
|
13456
13531
|
if (!report) {
|
|
13457
13532
|
mixpanel.people.set(properties);
|
|
13458
13533
|
return;
|
|
@@ -13482,7 +13557,7 @@ function setPeopleProperties(properties) {
|
|
|
13482
13557
|
mixpanel.people.set(properties);
|
|
13483
13558
|
}
|
|
13484
13559
|
function isReady() {
|
|
13485
|
-
return window.mixpanel !== void 0;
|
|
13560
|
+
return !initializing && window.mixpanel !== void 0;
|
|
13486
13561
|
}
|
|
13487
13562
|
function track(eventName, properties) {
|
|
13488
13563
|
if (!isReady()) {
|
|
@@ -13491,22 +13566,31 @@ function track(eventName, properties) {
|
|
|
13491
13566
|
mixpanel.track(eventName, { ...properties, "Reservamos Version": version });
|
|
13492
13567
|
}
|
|
13493
13568
|
const getMixpanelDistinctId = () => {
|
|
13494
|
-
|
|
13495
|
-
|
|
13496
|
-
|
|
13569
|
+
var _a;
|
|
13570
|
+
const distinctId = (_a = mixpanel == null ? void 0 : mixpanel.get_distinct_id) == null ? void 0 : _a.call(mixpanel);
|
|
13571
|
+
if (distinctId) {
|
|
13497
13572
|
updateCachedDistinctId(distinctId);
|
|
13498
13573
|
return distinctId;
|
|
13499
|
-
} else if (cachedId) {
|
|
13500
|
-
return cachedId;
|
|
13501
13574
|
}
|
|
13502
|
-
|
|
13575
|
+
try {
|
|
13576
|
+
return localStorage.getItem(MIXPANEL_DISTINCT_ID_CACHE_KEY);
|
|
13577
|
+
} catch {
|
|
13578
|
+
return null;
|
|
13579
|
+
}
|
|
13503
13580
|
};
|
|
13504
13581
|
function updateCachedDistinctId(distinctId) {
|
|
13505
|
-
|
|
13582
|
+
try {
|
|
13583
|
+
localStorage.setItem(MIXPANEL_DISTINCT_ID_CACHE_KEY, distinctId);
|
|
13584
|
+
return true;
|
|
13585
|
+
} catch {
|
|
13586
|
+
return false;
|
|
13587
|
+
}
|
|
13506
13588
|
}
|
|
13507
13589
|
const mixpanelService = {
|
|
13508
13590
|
init: init$2,
|
|
13591
|
+
validateConfiguration,
|
|
13509
13592
|
isReady,
|
|
13593
|
+
onReady: onReady$1,
|
|
13510
13594
|
track,
|
|
13511
13595
|
identify: identify$1,
|
|
13512
13596
|
attachProperty,
|
|
@@ -13517,6 +13601,18 @@ const DEFAULT_CACHE_NAME = "default_fingerprint_cache";
|
|
|
13517
13601
|
const DEFAULT_CACHE_TIME_IN_DAYS = 7;
|
|
13518
13602
|
const PROXY_QUERY_PARAM = "apiKey=<apiKey>&version=<version>&loaderVersion=<loaderVersion>";
|
|
13519
13603
|
let fingerprintAgentReady = false;
|
|
13604
|
+
let initialization$1 = 0;
|
|
13605
|
+
const readinessSubscribers = /* @__PURE__ */ new Set();
|
|
13606
|
+
function onReady(notify) {
|
|
13607
|
+
readinessSubscribers.add(notify);
|
|
13608
|
+
return () => {
|
|
13609
|
+
readinessSubscribers.delete(notify);
|
|
13610
|
+
};
|
|
13611
|
+
}
|
|
13612
|
+
function invalidate() {
|
|
13613
|
+
initialization$1++;
|
|
13614
|
+
fingerprintAgentReady = false;
|
|
13615
|
+
}
|
|
13520
13616
|
const getExpirationDate = (cacheTimeInDays) => {
|
|
13521
13617
|
const currentDate = /* @__PURE__ */ new Date();
|
|
13522
13618
|
const futureDate = new Date(
|
|
@@ -13528,8 +13624,8 @@ const getCachedFingerprint = () => {
|
|
|
13528
13624
|
return readCachedFingerprint();
|
|
13529
13625
|
};
|
|
13530
13626
|
function readCachedFingerprint(context) {
|
|
13531
|
-
const { cacheName = DEFAULT_CACHE_NAME } = window.fingerprintConfig;
|
|
13532
13627
|
try {
|
|
13628
|
+
const { cacheName = DEFAULT_CACHE_NAME } = window.fingerprintConfig ?? {};
|
|
13533
13629
|
const cachedData = JSON.parse(localStorage.getItem(cacheName) || "null");
|
|
13534
13630
|
if (cachedData && cachedData.expiresAt > Date.now()) {
|
|
13535
13631
|
return cachedData.fingerprint;
|
|
@@ -13562,7 +13658,8 @@ async function initFingerprint(apiKey, proxyUrl) {
|
|
|
13562
13658
|
customEndpointArray.unshift(proxyUrl);
|
|
13563
13659
|
customScriptEndpointArray.unshift(buildProxyUrl(proxyUrl));
|
|
13564
13660
|
}
|
|
13565
|
-
|
|
13661
|
+
invalidate();
|
|
13662
|
+
const attempt = initialization$1;
|
|
13566
13663
|
window.fingerprintConfig = {};
|
|
13567
13664
|
const client = new FpjsClient({
|
|
13568
13665
|
loadOptions: {
|
|
@@ -13574,42 +13671,37 @@ async function initFingerprint(apiKey, proxyUrl) {
|
|
|
13574
13671
|
window.fpClient = client;
|
|
13575
13672
|
try {
|
|
13576
13673
|
await client.init();
|
|
13577
|
-
if (window.fpClient === client) {
|
|
13674
|
+
if (window.fpClient === client && attempt === initialization$1) {
|
|
13578
13675
|
fingerprintAgentReady = true;
|
|
13676
|
+
readinessSubscribers.forEach((notify) => notify());
|
|
13579
13677
|
}
|
|
13580
13678
|
} catch (error) {
|
|
13581
|
-
if (window.fpClient === client) {
|
|
13679
|
+
if (window.fpClient === client && attempt === initialization$1) {
|
|
13582
13680
|
fingerprintAgentReady = false;
|
|
13583
13681
|
}
|
|
13584
13682
|
console.error("Error initializing identification service:", error);
|
|
13585
13683
|
throw error;
|
|
13586
13684
|
}
|
|
13587
13685
|
}
|
|
13588
|
-
async function getFingerprint(cacheOnly = false) {
|
|
13589
|
-
return readFingerprint(cacheOnly);
|
|
13686
|
+
async function getFingerprint(cacheOnly = false, context) {
|
|
13687
|
+
return readFingerprint(cacheOnly, context);
|
|
13590
13688
|
}
|
|
13591
13689
|
function getFingerprintForIdentify(context) {
|
|
13592
13690
|
return readFingerprint(false, context);
|
|
13593
13691
|
}
|
|
13594
13692
|
async function readFingerprint(cacheOnly, context) {
|
|
13595
|
-
if (!window.fingerprintConfig) {
|
|
13596
|
-
if (cacheOnly) {
|
|
13597
|
-
return "";
|
|
13598
|
-
}
|
|
13599
|
-
throw new Error("Fingerprint configuration is not initialized.");
|
|
13600
|
-
}
|
|
13601
13693
|
const cachedFingerprint = readCachedFingerprint(context);
|
|
13602
13694
|
if (cachedFingerprint) {
|
|
13603
13695
|
return cachedFingerprint;
|
|
13604
13696
|
}
|
|
13605
|
-
if (cacheOnly || !isFingerprintReady()) {
|
|
13697
|
+
if (cacheOnly || !window.fingerprintConfig || !isFingerprintReady()) {
|
|
13606
13698
|
return "";
|
|
13607
13699
|
}
|
|
13608
13700
|
const { visitorId } = await requestIdentification(false, context);
|
|
13609
13701
|
return visitorId;
|
|
13610
13702
|
}
|
|
13611
|
-
async function identifyVisitor(ignoreCache = false) {
|
|
13612
|
-
return requestIdentification(ignoreCache);
|
|
13703
|
+
async function identifyVisitor(ignoreCache = false, context) {
|
|
13704
|
+
return requestIdentification(ignoreCache, context);
|
|
13613
13705
|
}
|
|
13614
13706
|
async function requestIdentification(ignoreCache, context) {
|
|
13615
13707
|
if (!isFingerprintReady()) {
|
|
@@ -13620,9 +13712,8 @@ async function requestIdentification(ignoreCache, context) {
|
|
|
13620
13712
|
if (!context || context.isActive()) {
|
|
13621
13713
|
try {
|
|
13622
13714
|
setCachedFingerprint(result.visitorId);
|
|
13623
|
-
} catch
|
|
13624
|
-
|
|
13625
|
-
context.report("cache", "unavailable");
|
|
13715
|
+
} catch {
|
|
13716
|
+
context == null ? void 0 : context.report("cache", "unavailable");
|
|
13626
13717
|
}
|
|
13627
13718
|
if (typeof result.visitorFound === "boolean") {
|
|
13628
13719
|
try {
|
|
@@ -13655,8 +13746,168 @@ const fingerprintService = {
|
|
|
13655
13746
|
getFingerprint,
|
|
13656
13747
|
identifyVisitor,
|
|
13657
13748
|
isFingerprintReady,
|
|
13749
|
+
onReady,
|
|
13750
|
+
invalidate,
|
|
13658
13751
|
getCachedFingerprint
|
|
13659
13752
|
};
|
|
13753
|
+
trackingLifecycle.onInitialization(invalidate);
|
|
13754
|
+
function createPendingWorkQueue(options) {
|
|
13755
|
+
const entries = /* @__PURE__ */ new Set();
|
|
13756
|
+
let bytes = 0;
|
|
13757
|
+
let active = 0;
|
|
13758
|
+
let draining = false;
|
|
13759
|
+
let unsubscribe;
|
|
13760
|
+
let timer;
|
|
13761
|
+
function diagnostic(entry, reason) {
|
|
13762
|
+
console.error("Tracking recovery diagnostic", {
|
|
13763
|
+
eventName: entry.eventName,
|
|
13764
|
+
stage: options.stage,
|
|
13765
|
+
reason
|
|
13766
|
+
});
|
|
13767
|
+
}
|
|
13768
|
+
function resolveForeground(entry) {
|
|
13769
|
+
var _a;
|
|
13770
|
+
(_a = entry.resolve) == null ? void 0 : _a.call(entry);
|
|
13771
|
+
entry.resolve = void 0;
|
|
13772
|
+
}
|
|
13773
|
+
function close(entry, reason) {
|
|
13774
|
+
if (entry.state === "closed") return;
|
|
13775
|
+
entry.state = "closed";
|
|
13776
|
+
entries.delete(entry);
|
|
13777
|
+
bytes -= entry.bytes;
|
|
13778
|
+
resolveForeground(entry);
|
|
13779
|
+
if (reason) diagnostic(entry, reason);
|
|
13780
|
+
}
|
|
13781
|
+
function schedule() {
|
|
13782
|
+
clearTimeout(timer);
|
|
13783
|
+
timer = void 0;
|
|
13784
|
+
if (entries.size === 0) {
|
|
13785
|
+
unsubscribe == null ? void 0 : unsubscribe();
|
|
13786
|
+
unsubscribe = void 0;
|
|
13787
|
+
return;
|
|
13788
|
+
}
|
|
13789
|
+
let next = Infinity;
|
|
13790
|
+
for (const entry of entries) {
|
|
13791
|
+
next = Math.min(next, entry.deadline);
|
|
13792
|
+
if (entry.resolve) next = Math.min(next, entry.foregroundDeadline);
|
|
13793
|
+
}
|
|
13794
|
+
timer = setTimeout(drain, Math.max(0, next - Date.now()));
|
|
13795
|
+
}
|
|
13796
|
+
function drain() {
|
|
13797
|
+
if (draining) return;
|
|
13798
|
+
draining = true;
|
|
13799
|
+
try {
|
|
13800
|
+
for (const entry of entries) {
|
|
13801
|
+
if (!entry.isCurrent()) close(entry, "identity_changed");
|
|
13802
|
+
else if (Date.now() >= entry.deadline) close(entry, "timeout");
|
|
13803
|
+
else if (entry.resolve && Date.now() >= entry.foregroundDeadline) {
|
|
13804
|
+
resolveForeground(entry);
|
|
13805
|
+
}
|
|
13806
|
+
}
|
|
13807
|
+
for (const entry of entries) {
|
|
13808
|
+
if (active >= options.maxActive || !options.isReady()) break;
|
|
13809
|
+
if (entry.state !== "waiting") continue;
|
|
13810
|
+
entry.state = "active";
|
|
13811
|
+
active++;
|
|
13812
|
+
const isActive = () => entry.state === "active" && Date.now() < entry.deadline && entry.isCurrent();
|
|
13813
|
+
const finish = (failed = false) => {
|
|
13814
|
+
if (failed && isActive()) diagnostic(entry, "failed");
|
|
13815
|
+
close(entry);
|
|
13816
|
+
active--;
|
|
13817
|
+
drain();
|
|
13818
|
+
};
|
|
13819
|
+
try {
|
|
13820
|
+
void Promise.resolve(entry.run(isActive)).then(
|
|
13821
|
+
() => finish(),
|
|
13822
|
+
() => finish(true)
|
|
13823
|
+
);
|
|
13824
|
+
} catch {
|
|
13825
|
+
finish(true);
|
|
13826
|
+
}
|
|
13827
|
+
}
|
|
13828
|
+
} finally {
|
|
13829
|
+
draining = false;
|
|
13830
|
+
schedule();
|
|
13831
|
+
}
|
|
13832
|
+
}
|
|
13833
|
+
function enqueue(work) {
|
|
13834
|
+
if (work.bytes > options.maxBytes) {
|
|
13835
|
+
diagnostic(work, "capacity");
|
|
13836
|
+
return Promise.resolve();
|
|
13837
|
+
}
|
|
13838
|
+
while (entries.size >= options.maxEntries || bytes + work.bytes > options.maxBytes) {
|
|
13839
|
+
const oldest = [...entries].find((entry) => entry.state === "waiting");
|
|
13840
|
+
if (!oldest) {
|
|
13841
|
+
diagnostic(work, "capacity");
|
|
13842
|
+
return Promise.resolve();
|
|
13843
|
+
}
|
|
13844
|
+
close(oldest, "capacity");
|
|
13845
|
+
}
|
|
13846
|
+
return new Promise((resolve) => {
|
|
13847
|
+
const entry = {
|
|
13848
|
+
...work,
|
|
13849
|
+
state: "waiting",
|
|
13850
|
+
deadline: Date.now() + options.lifetime,
|
|
13851
|
+
foregroundDeadline: Date.now() + options.wait,
|
|
13852
|
+
resolve
|
|
13853
|
+
};
|
|
13854
|
+
entries.add(entry);
|
|
13855
|
+
bytes += entry.bytes;
|
|
13856
|
+
unsubscribe ?? (unsubscribe = options.subscribe(drain));
|
|
13857
|
+
drain();
|
|
13858
|
+
});
|
|
13859
|
+
}
|
|
13860
|
+
return { enqueue };
|
|
13861
|
+
}
|
|
13862
|
+
const observations = createPendingWorkQueue({
|
|
13863
|
+
isReady: () => fingerprintService.isFingerprintReady(),
|
|
13864
|
+
subscribe(notify) {
|
|
13865
|
+
const stopReady = fingerprintService.onReady(notify);
|
|
13866
|
+
const stopIdentity = trackingLifecycle.onChange(notify);
|
|
13867
|
+
return () => {
|
|
13868
|
+
stopReady();
|
|
13869
|
+
stopIdentity();
|
|
13870
|
+
};
|
|
13871
|
+
},
|
|
13872
|
+
maxEntries: 100,
|
|
13873
|
+
maxBytes: 1024 * 1024,
|
|
13874
|
+
maxActive: 3,
|
|
13875
|
+
lifetime: 1e4,
|
|
13876
|
+
wait: 0,
|
|
13877
|
+
stage: "fingerprint"
|
|
13878
|
+
});
|
|
13879
|
+
function deferBotDetection(eventName, distinctId, isCurrent) {
|
|
13880
|
+
void observations.enqueue({
|
|
13881
|
+
eventName,
|
|
13882
|
+
bytes: new Blob([eventName, distinctId ?? ""]).size,
|
|
13883
|
+
isCurrent,
|
|
13884
|
+
async run(isActive) {
|
|
13885
|
+
let stage = "fingerprint";
|
|
13886
|
+
try {
|
|
13887
|
+
const identification = await fingerprintService.identifyVisitor(true, {
|
|
13888
|
+
isActive,
|
|
13889
|
+
report: () => void 0
|
|
13890
|
+
});
|
|
13891
|
+
if (!isActive() || !identification.requestId) return;
|
|
13892
|
+
stage = "relay";
|
|
13893
|
+
await botDetectionRelayService.relay({
|
|
13894
|
+
requestId: identification.requestId,
|
|
13895
|
+
visitorId: identification.visitorId,
|
|
13896
|
+
...distinctId && { distinctId },
|
|
13897
|
+
eventName
|
|
13898
|
+
});
|
|
13899
|
+
} catch {
|
|
13900
|
+
if (isActive()) {
|
|
13901
|
+
console.error("Bot Detection diagnostic", {
|
|
13902
|
+
eventName,
|
|
13903
|
+
stage,
|
|
13904
|
+
reason: "failed"
|
|
13905
|
+
});
|
|
13906
|
+
}
|
|
13907
|
+
}
|
|
13908
|
+
}
|
|
13909
|
+
});
|
|
13910
|
+
}
|
|
13660
13911
|
let environment;
|
|
13661
13912
|
let appName;
|
|
13662
13913
|
const origin = window.location.origin;
|
|
@@ -13795,7 +14046,7 @@ const tripSchema = z.object({
|
|
|
13795
14046
|
"Departure Origin Terminal": stringField("Departure Origin Terminal"),
|
|
13796
14047
|
"Departure Price": numberField("Departure Price"),
|
|
13797
14048
|
"Departure Route": stringField("Departure Route"),
|
|
13798
|
-
"Departure Stops":
|
|
14049
|
+
"Departure Stops": z.number().int(),
|
|
13799
14050
|
"Departure Time": dateField("Departure Time"),
|
|
13800
14051
|
"Departure Transport Type": stringField("Departure Transport Type"),
|
|
13801
14052
|
"Departure Transporter": stringField("Departure Transporter"),
|
|
@@ -13809,7 +14060,7 @@ const tripSchema = z.object({
|
|
|
13809
14060
|
"Return Origin Terminal": stringField("Return Origin Terminal").optional(),
|
|
13810
14061
|
"Return Price": numberField("Return Price").optional(),
|
|
13811
14062
|
"Return Route": stringField("Return Route").optional(),
|
|
13812
|
-
"Return Stops":
|
|
14063
|
+
"Return Stops": z.number().int().optional(),
|
|
13813
14064
|
"Return Time": dateField("Return Time").optional(),
|
|
13814
14065
|
"Return Transport Type": stringField("Return Transport Type").optional(),
|
|
13815
14066
|
"Return Transporter": stringField("Return Transporter").optional(),
|
|
@@ -14074,6 +14325,7 @@ const validatorService = {
|
|
|
14074
14325
|
parseIdentifyProps,
|
|
14075
14326
|
validateProps
|
|
14076
14327
|
};
|
|
14328
|
+
let initialization = 0;
|
|
14077
14329
|
function onLoaded() {
|
|
14078
14330
|
window.dispatchEvent(new CustomEvent("Tracker Ready"));
|
|
14079
14331
|
}
|
|
@@ -14088,22 +14340,49 @@ async function init$1(config) {
|
|
|
14088
14340
|
identifyProxyUrl,
|
|
14089
14341
|
appName: appName2
|
|
14090
14342
|
} = config;
|
|
14343
|
+
mixpanelService.validateConfiguration(mixpanelToken, mixpanelProxyUrl);
|
|
14344
|
+
const attempt = ++initialization;
|
|
14345
|
+
const isCurrent = () => attempt === initialization;
|
|
14091
14346
|
await mixpanelService.init(mixpanelToken, debug, mixpanelProxyUrl);
|
|
14092
|
-
|
|
14093
|
-
await fingerprintService.initFingerprint(
|
|
14094
|
-
identificationKey,
|
|
14095
|
-
identifyProxyUrl
|
|
14096
|
-
);
|
|
14097
|
-
} catch (error) {
|
|
14098
|
-
console.error("Error initializing identification service:", error);
|
|
14099
|
-
}
|
|
14100
|
-
await geolocationService.init();
|
|
14347
|
+
if (!isCurrent()) return;
|
|
14101
14348
|
configService.setEnvironment(isSandbox ? "sandbox" : "prod");
|
|
14102
14349
|
if (appName2) {
|
|
14103
14350
|
configService.setAppName(appName2);
|
|
14104
14351
|
}
|
|
14105
14352
|
a$1.setConfig(configService.getCoreAPIConfig());
|
|
14106
14353
|
onLoaded();
|
|
14354
|
+
if (!isCurrent()) return;
|
|
14355
|
+
await Promise.all([
|
|
14356
|
+
initializeOptionalStage(
|
|
14357
|
+
"fingerprint",
|
|
14358
|
+
() => isCurrent() ? fingerprintService.initFingerprint(
|
|
14359
|
+
identificationKey,
|
|
14360
|
+
identifyProxyUrl
|
|
14361
|
+
) : Promise.resolve()
|
|
14362
|
+
),
|
|
14363
|
+
initializeOptionalStage(
|
|
14364
|
+
"geolocation",
|
|
14365
|
+
() => isCurrent() ? geolocationService.init() : Promise.resolve()
|
|
14366
|
+
)
|
|
14367
|
+
]);
|
|
14368
|
+
}
|
|
14369
|
+
function initializeOptionalStage(stage, initialize) {
|
|
14370
|
+
return new Promise((resolve) => {
|
|
14371
|
+
let active = true;
|
|
14372
|
+
const finish = (reason) => {
|
|
14373
|
+
if (!active) return;
|
|
14374
|
+
active = false;
|
|
14375
|
+
clearTimeout(timer);
|
|
14376
|
+
if (reason)
|
|
14377
|
+
console.error("SDK initialization diagnostic", { stage, reason });
|
|
14378
|
+
resolve();
|
|
14379
|
+
};
|
|
14380
|
+
const timer = setTimeout(() => finish("timeout"), 3e3);
|
|
14381
|
+
void Promise.resolve().then(initialize).then(
|
|
14382
|
+
() => finish(),
|
|
14383
|
+
() => finish("failed")
|
|
14384
|
+
);
|
|
14385
|
+
});
|
|
14107
14386
|
}
|
|
14108
14387
|
function isTrackerReady() {
|
|
14109
14388
|
return mixpanelService.isReady();
|
|
@@ -14310,6 +14589,20 @@ const geolocationService = {
|
|
|
14310
14589
|
getCoordinates,
|
|
14311
14590
|
getGeolocationSource
|
|
14312
14591
|
};
|
|
14592
|
+
function snapshotEventData(value) {
|
|
14593
|
+
if (Array.isArray(value))
|
|
14594
|
+
return value.map((item) => snapshotEventData(item));
|
|
14595
|
+
if (value instanceof Date) return new Date(value.getTime());
|
|
14596
|
+
if (value !== null && typeof value === "object") {
|
|
14597
|
+
return Object.fromEntries(
|
|
14598
|
+
Object.entries(value).map(([key, item]) => [
|
|
14599
|
+
key,
|
|
14600
|
+
snapshotEventData(item)
|
|
14601
|
+
])
|
|
14602
|
+
);
|
|
14603
|
+
}
|
|
14604
|
+
return value;
|
|
14605
|
+
}
|
|
14313
14606
|
const FP_TRIGGER_EVENTS = ["Search", "View Results"];
|
|
14314
14607
|
const EXTRA_UTM_FIELDS = {
|
|
14315
14608
|
utm_brand: "UTM Brand",
|
|
@@ -14359,32 +14652,137 @@ function trackEventError(eventName, error, payload) {
|
|
|
14359
14652
|
}
|
|
14360
14653
|
});
|
|
14361
14654
|
}
|
|
14362
|
-
async function tryTrackEvent(trackEventCallback) {
|
|
14363
|
-
if (mixpanelService.isReady()) {
|
|
14655
|
+
async function tryTrackEvent(trackEventCallback, eventName = "Track Event Error") {
|
|
14656
|
+
if (!mixpanelService.isReady()) {
|
|
14657
|
+
const ready = await new Promise((resolve) => {
|
|
14658
|
+
const finish = (isReady2) => {
|
|
14659
|
+
clearTimeout(timer);
|
|
14660
|
+
window.removeEventListener("Tracker Ready", onReady2);
|
|
14661
|
+
unsubscribe();
|
|
14662
|
+
resolve(isReady2);
|
|
14663
|
+
};
|
|
14664
|
+
const onReady2 = () => {
|
|
14665
|
+
if (mixpanelService.isReady()) finish(true);
|
|
14666
|
+
};
|
|
14667
|
+
const timer = setTimeout(() => finish(mixpanelService.isReady()), 5e3);
|
|
14668
|
+
const unsubscribe = mixpanelService.onReady(onReady2);
|
|
14669
|
+
window.addEventListener("Tracker Ready", onReady2);
|
|
14670
|
+
onReady2();
|
|
14671
|
+
});
|
|
14672
|
+
if (!ready) {
|
|
14673
|
+
console.error("Tracking diagnostic", {
|
|
14674
|
+
eventName,
|
|
14675
|
+
stage: "readiness",
|
|
14676
|
+
reason: "timeout"
|
|
14677
|
+
});
|
|
14678
|
+
return;
|
|
14679
|
+
}
|
|
14680
|
+
}
|
|
14681
|
+
try {
|
|
14364
14682
|
await trackEventCallback();
|
|
14365
|
-
}
|
|
14366
|
-
|
|
14367
|
-
|
|
14683
|
+
} catch {
|
|
14684
|
+
console.error("Tracking diagnostic", {
|
|
14685
|
+
eventName,
|
|
14686
|
+
stage: "submission",
|
|
14687
|
+
reason: "failed"
|
|
14368
14688
|
});
|
|
14369
14689
|
}
|
|
14370
14690
|
}
|
|
14371
|
-
|
|
14372
|
-
|
|
14373
|
-
|
|
14691
|
+
const pendingEvents = createPendingWorkQueue({
|
|
14692
|
+
isReady: () => mixpanelService.isReady(),
|
|
14693
|
+
subscribe(notify) {
|
|
14694
|
+
const stopReady = mixpanelService.onReady(notify);
|
|
14695
|
+
const stopIdentity = trackingLifecycle.onChange(notify);
|
|
14696
|
+
window.addEventListener("Tracker Ready", notify);
|
|
14697
|
+
return () => {
|
|
14698
|
+
stopReady();
|
|
14699
|
+
stopIdentity();
|
|
14700
|
+
window.removeEventListener("Tracker Ready", notify);
|
|
14701
|
+
};
|
|
14702
|
+
},
|
|
14703
|
+
maxEntries: 100,
|
|
14704
|
+
maxBytes: 1024 * 1024,
|
|
14705
|
+
maxActive: 100,
|
|
14706
|
+
lifetime: 6e4,
|
|
14707
|
+
wait: 5e3,
|
|
14708
|
+
stage: "readiness"
|
|
14709
|
+
});
|
|
14710
|
+
function trackEvent(eventName, eventProperties, meta = {}) {
|
|
14711
|
+
const isCurrent = trackingLifecycle.capture();
|
|
14712
|
+
const trackingParams = extractTrackingParams();
|
|
14713
|
+
if (mixpanelService.isReady()) {
|
|
14714
|
+
return submitEvent(
|
|
14715
|
+
eventName,
|
|
14716
|
+
eventProperties,
|
|
14717
|
+
meta,
|
|
14718
|
+
isCurrent,
|
|
14719
|
+
trackingParams
|
|
14720
|
+
);
|
|
14374
14721
|
}
|
|
14722
|
+
try {
|
|
14723
|
+
validatorService.parseEventProps(eventName, eventProperties);
|
|
14724
|
+
} catch (error) {
|
|
14725
|
+
trackEventError(eventName, error, eventProperties);
|
|
14726
|
+
return Promise.resolve();
|
|
14727
|
+
}
|
|
14728
|
+
let snapshot2;
|
|
14729
|
+
let bytes;
|
|
14730
|
+
try {
|
|
14731
|
+
const serialized = JSON.stringify(
|
|
14732
|
+
{ eventName, eventProperties, meta, trackingParams },
|
|
14733
|
+
(_key, value) => {
|
|
14734
|
+
if (typeof value === "function" || typeof value === "symbol")
|
|
14735
|
+
throw new Error();
|
|
14736
|
+
return value === void 0 ? null : value;
|
|
14737
|
+
}
|
|
14738
|
+
);
|
|
14739
|
+
bytes = new Blob([serialized]).size;
|
|
14740
|
+
if (bytes > 1024 * 1024) throw new Error();
|
|
14741
|
+
snapshot2 = snapshotEventData({ eventProperties, meta });
|
|
14742
|
+
} catch {
|
|
14743
|
+
console.error("Tracking recovery diagnostic", {
|
|
14744
|
+
eventName,
|
|
14745
|
+
stage: "readiness",
|
|
14746
|
+
reason: "snapshot_unavailable"
|
|
14747
|
+
});
|
|
14748
|
+
return Promise.resolve();
|
|
14749
|
+
}
|
|
14750
|
+
return pendingEvents.enqueue({
|
|
14751
|
+
eventName,
|
|
14752
|
+
bytes,
|
|
14753
|
+
isCurrent,
|
|
14754
|
+
run: (isActive) => submitEvent(
|
|
14755
|
+
eventName,
|
|
14756
|
+
snapshot2.eventProperties,
|
|
14757
|
+
snapshot2.meta,
|
|
14758
|
+
isActive,
|
|
14759
|
+
trackingParams
|
|
14760
|
+
)
|
|
14761
|
+
});
|
|
14762
|
+
}
|
|
14763
|
+
async function submitEvent(eventName, eventProperties, meta, isCurrent, trackingParams) {
|
|
14764
|
+
const observationContext = trackingLifecycle.capture();
|
|
14375
14765
|
try {
|
|
14376
14766
|
validatorService.parseEventProps(eventName, eventProperties);
|
|
14377
14767
|
const distinctId = mixpanelService.getMixpanelDistinctId();
|
|
14378
14768
|
const botDetectionEnabled = botDetectionRelayService.isEnabledFor(eventName);
|
|
14379
|
-
let fingerprint;
|
|
14380
|
-
|
|
14381
|
-
|
|
14382
|
-
|
|
14383
|
-
|
|
14384
|
-
|
|
14769
|
+
let fingerprint = "";
|
|
14770
|
+
if (botDetectionEnabled || FP_TRIGGER_EVENTS.includes(eventName)) {
|
|
14771
|
+
try {
|
|
14772
|
+
fingerprint = fingerprintService.getCachedFingerprint() ?? "";
|
|
14773
|
+
} catch (error) {
|
|
14774
|
+
console.error("Error reading cached fingerprint:", error);
|
|
14775
|
+
}
|
|
14385
14776
|
} else {
|
|
14386
|
-
|
|
14387
|
-
|
|
14777
|
+
try {
|
|
14778
|
+
fingerprint = await fingerprintService.getFingerprint(true);
|
|
14779
|
+
} catch {
|
|
14780
|
+
console.error("Tracking diagnostic", {
|
|
14781
|
+
eventName,
|
|
14782
|
+
stage: "fingerprint",
|
|
14783
|
+
reason: "failed"
|
|
14784
|
+
});
|
|
14785
|
+
}
|
|
14388
14786
|
}
|
|
14389
14787
|
const defaultCoordinates = {};
|
|
14390
14788
|
const coordinates = geolocationService.getCoordinates();
|
|
@@ -14399,7 +14797,6 @@ async function trackEvent(eventName, eventProperties, meta = {}) {
|
|
|
14399
14797
|
const defaultProperties = {
|
|
14400
14798
|
"User Fingerprint": fingerprint
|
|
14401
14799
|
};
|
|
14402
|
-
const trackingParams = extractTrackingParams();
|
|
14403
14800
|
const simplifiedData = flattenEventData(eventProperties);
|
|
14404
14801
|
const properties = {
|
|
14405
14802
|
...defaultProperties,
|
|
@@ -14408,20 +14805,42 @@ async function trackEvent(eventName, eventProperties, meta = {}) {
|
|
|
14408
14805
|
...defaultCoordinates,
|
|
14409
14806
|
...meta
|
|
14410
14807
|
};
|
|
14411
|
-
|
|
14412
|
-
|
|
14413
|
-
|
|
14414
|
-
|
|
14415
|
-
|
|
14416
|
-
|
|
14417
|
-
|
|
14808
|
+
if (!isCurrent()) return;
|
|
14809
|
+
try {
|
|
14810
|
+
mixpanelService.track(eventName, properties);
|
|
14811
|
+
} catch {
|
|
14812
|
+
console.error("Tracking diagnostic", {
|
|
14813
|
+
eventName,
|
|
14814
|
+
stage: "submission",
|
|
14815
|
+
reason: "uncertain"
|
|
14418
14816
|
});
|
|
14817
|
+
return;
|
|
14818
|
+
}
|
|
14819
|
+
if (botDetectionEnabled) {
|
|
14820
|
+
deferBotDetection(eventName, distinctId, observationContext);
|
|
14821
|
+
} else if (FP_TRIGGER_EVENTS.includes(eventName)) {
|
|
14822
|
+
void refreshFingerprint(eventName, observationContext);
|
|
14419
14823
|
}
|
|
14420
14824
|
} catch (error) {
|
|
14421
14825
|
console.error(`Error tracking event '${eventName}':`, error);
|
|
14422
14826
|
trackEventError(eventName, error, eventProperties);
|
|
14423
14827
|
}
|
|
14424
14828
|
}
|
|
14829
|
+
async function refreshFingerprint(eventName, isCurrent) {
|
|
14830
|
+
const deadline = Date.now() + 1e4;
|
|
14831
|
+
try {
|
|
14832
|
+
await fingerprintService.getFingerprint(false, {
|
|
14833
|
+
isActive: () => isCurrent() && Date.now() < deadline,
|
|
14834
|
+
report: () => void 0
|
|
14835
|
+
});
|
|
14836
|
+
} catch {
|
|
14837
|
+
console.error("Tracking diagnostic", {
|
|
14838
|
+
eventName,
|
|
14839
|
+
stage: "fingerprint",
|
|
14840
|
+
reason: "failed"
|
|
14841
|
+
});
|
|
14842
|
+
}
|
|
14843
|
+
}
|
|
14425
14844
|
function extractTrackingParams() {
|
|
14426
14845
|
try {
|
|
14427
14846
|
const urlParams = new URLSearchParams(window.location.search);
|
|
@@ -14556,7 +14975,7 @@ function mapProperties(properties) {
|
|
|
14556
14975
|
);
|
|
14557
14976
|
return { ...parsedProperties, ...additionalProperties };
|
|
14558
14977
|
}
|
|
14559
|
-
const identifyWithLocation = async (
|
|
14978
|
+
const identifyWithLocation = async (userId2, properties, report) => {
|
|
14560
14979
|
var _a, _b, _c;
|
|
14561
14980
|
let observer;
|
|
14562
14981
|
try {
|
|
@@ -14593,7 +15012,7 @@ const identifyWithLocation = async (userId, properties, report) => {
|
|
|
14593
15012
|
body: JSON.stringify({
|
|
14594
15013
|
profile_params: {
|
|
14595
15014
|
...properties,
|
|
14596
|
-
distinct_id:
|
|
15015
|
+
distinct_id: userId2
|
|
14597
15016
|
}
|
|
14598
15017
|
})
|
|
14599
15018
|
}
|
|
@@ -14607,13 +15026,13 @@ const identifyWithLocation = async (userId, properties, report) => {
|
|
|
14607
15026
|
else report("datalake", "failed");
|
|
14608
15027
|
}
|
|
14609
15028
|
};
|
|
14610
|
-
async function identify(
|
|
15029
|
+
async function identify(userId2, properties = {}) {
|
|
14611
15030
|
const report = createIdentifyReporter();
|
|
14612
15031
|
if (!await waitForIdentifyReady(report)) return;
|
|
14613
15032
|
try {
|
|
14614
15033
|
try {
|
|
14615
15034
|
validatorService.parseIdentifyProps(properties);
|
|
14616
|
-
if (!
|
|
15035
|
+
if (!userId2) {
|
|
14617
15036
|
report("validation", "invalid_input");
|
|
14618
15037
|
return;
|
|
14619
15038
|
}
|
|
@@ -14632,7 +15051,7 @@ async function identify(userId, properties = {}) {
|
|
|
14632
15051
|
}
|
|
14633
15052
|
const mappedProps = mapProperties(properties);
|
|
14634
15053
|
try {
|
|
14635
|
-
mixpanelService.identify(
|
|
15054
|
+
mixpanelService.identify(userId2, mappedProps, report);
|
|
14636
15055
|
} catch {
|
|
14637
15056
|
report("mixpanel", "failed");
|
|
14638
15057
|
}
|
|
@@ -14652,7 +15071,7 @@ async function identify(userId, properties = {}) {
|
|
|
14652
15071
|
} catch {
|
|
14653
15072
|
report("mixpanel", "failed");
|
|
14654
15073
|
} finally {
|
|
14655
|
-
void identifyWithLocation(
|
|
15074
|
+
void identifyWithLocation(userId2, properties, report);
|
|
14656
15075
|
}
|
|
14657
15076
|
}
|
|
14658
15077
|
z.object({
|