mixpanel-browser 2.74.0 → 2.75.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/.github/workflows/unit-tests.yml +1 -1
- package/CHANGELOG.md +5 -0
- package/README.md +2 -2
- package/dist/mixpanel-core.cjs.js +318 -20
- package/dist/mixpanel-recorder.js +127 -15
- package/dist/mixpanel-recorder.min.js +1 -1
- package/dist/mixpanel-recorder.min.js.map +1 -1
- package/dist/mixpanel-targeting.js +2576 -0
- package/dist/mixpanel-targeting.min.js +2 -0
- package/dist/mixpanel-targeting.min.js.map +1 -0
- package/dist/mixpanel-with-async-modules.cjs.d.ts +522 -0
- package/dist/mixpanel-with-async-modules.cjs.js +9700 -0
- package/dist/mixpanel-with-async-recorder.cjs.js +318 -20
- package/dist/mixpanel-with-recorder.js +435 -26
- package/dist/mixpanel-with-recorder.min.js +1 -1
- package/dist/mixpanel.amd.js +1020 -28
- package/dist/mixpanel.cjs.js +1020 -28
- package/dist/mixpanel.globals.js +318 -20
- package/dist/mixpanel.min.js +179 -172
- package/dist/mixpanel.module.js +1020 -28
- package/dist/mixpanel.umd.js +1020 -28
- package/dist/rrweb-bundled.js +119 -5
- package/dist/rrweb-compiled.js +116 -5
- package/package.json +4 -3
- package/rollup.config.mjs +34 -2
- package/src/config.js +1 -1
- package/src/flags/index.js +269 -8
- package/src/globals.js +14 -0
- package/src/loaders/loader-module.js +1 -0
- package/src/mixpanel-core.js +12 -3
- package/src/recorder/index.js +2 -1
- package/src/targeting/event-matcher.js +97 -0
- package/src/targeting/index.js +11 -0
- package/src/targeting/loader.js +36 -0
- package/src/utils.js +1 -8
- package/.claude/settings.local.json +0 -12
- /package/src/loaders/{loader-module-with-async-recorder.js → loader-module-with-async-modules.js} +0 -0
package/dist/mixpanel.module.js
CHANGED
|
@@ -23,6 +23,16 @@ if (typeof(window) === 'undefined') {
|
|
|
23
23
|
win = window;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Shared global window property names used across modules
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
// Targeting library global (used by flags and targeting modules)
|
|
31
|
+
var TARGETING_GLOBAL_NAME = '__mp_targeting';
|
|
32
|
+
|
|
33
|
+
// Recorder library global (used by recorder and mixpanel-core)
|
|
34
|
+
var RECORDER_GLOBAL_NAME = '__mp_recorder';
|
|
35
|
+
|
|
26
36
|
function _array_like_to_array(arr, len) {
|
|
27
37
|
if (len == null || len > arr.length) len = arr.length;
|
|
28
38
|
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
@@ -637,14 +647,16 @@ var Mirror = /*#__PURE__*/ function() {
|
|
|
637
647
|
return this.nodeMetaMap.get(n2) || null;
|
|
638
648
|
};
|
|
639
649
|
// removes the node from idNodeMap
|
|
640
|
-
//
|
|
641
|
-
_proto.removeNodeFromMap = function removeNodeFromMap(n2) {
|
|
650
|
+
// if permanent is true, also removes from nodeMetaMap
|
|
651
|
+
_proto.removeNodeFromMap = function removeNodeFromMap(n2, permanent) {
|
|
642
652
|
var _this = this;
|
|
653
|
+
if (permanent === void 0) permanent = false;
|
|
643
654
|
var id = this.getId(n2);
|
|
644
655
|
this.idNodeMap.delete(id);
|
|
656
|
+
if (permanent) this.nodeMetaMap.delete(n2);
|
|
645
657
|
if (n2.childNodes) {
|
|
646
658
|
n2.childNodes.forEach(function(childNode) {
|
|
647
|
-
return _this.removeNodeFromMap(childNode);
|
|
659
|
+
return _this.removeNodeFromMap(childNode, permanent);
|
|
648
660
|
});
|
|
649
661
|
}
|
|
650
662
|
};
|
|
@@ -10386,6 +10398,15 @@ var StyleSheetMirror = /*#__PURE__*/ function() {
|
|
|
10386
10398
|
_proto.generateId = function generateId() {
|
|
10387
10399
|
return this.id++;
|
|
10388
10400
|
};
|
|
10401
|
+
_proto.remove = function remove(stylesheet) {
|
|
10402
|
+
var id = this.styleIDMap.get(stylesheet);
|
|
10403
|
+
if (id !== void 0) {
|
|
10404
|
+
this.styleIDMap.delete(stylesheet);
|
|
10405
|
+
this.idStyleMap.delete(id);
|
|
10406
|
+
return true;
|
|
10407
|
+
}
|
|
10408
|
+
return false;
|
|
10409
|
+
};
|
|
10389
10410
|
return StyleSheetMirror;
|
|
10390
10411
|
}();
|
|
10391
10412
|
function getShadowHost(n2) {
|
|
@@ -10708,7 +10729,15 @@ var MutationBuffer = /*#__PURE__*/ function() {
|
|
|
10708
10729
|
}
|
|
10709
10730
|
};
|
|
10710
10731
|
while(_this.mapRemoves.length){
|
|
10711
|
-
_this.
|
|
10732
|
+
var removedNode = _this.mapRemoves.shift();
|
|
10733
|
+
if (removedNode.nodeName === "IFRAME") {
|
|
10734
|
+
try {
|
|
10735
|
+
_this.iframeManager.removeIframe(removedNode);
|
|
10736
|
+
} catch (e2) {}
|
|
10737
|
+
} else {
|
|
10738
|
+
_this.stylesheetManager.cleanupStylesheetsForRemovedNode(removedNode);
|
|
10739
|
+
}
|
|
10740
|
+
_this.mirror.removeNodeFromMap(removedNode);
|
|
10712
10741
|
}
|
|
10713
10742
|
for(var _iterator = _create_for_of_iterator_helper_loose(_this.movedSet), _step; !(_step = _iterator()).done;){
|
|
10714
10743
|
var n2 = _step.value;
|
|
@@ -11082,6 +11111,9 @@ var MutationBuffer = /*#__PURE__*/ function() {
|
|
|
11082
11111
|
this.shadowDomManager.reset();
|
|
11083
11112
|
this.canvasManager.reset();
|
|
11084
11113
|
};
|
|
11114
|
+
_proto.getDoc = function getDoc() {
|
|
11115
|
+
return this.doc;
|
|
11116
|
+
};
|
|
11085
11117
|
return MutationBuffer;
|
|
11086
11118
|
}();
|
|
11087
11119
|
function deepDelete(addsSet, n2) {
|
|
@@ -11182,6 +11214,14 @@ function initMutationObserver(options, rootEl) {
|
|
|
11182
11214
|
});
|
|
11183
11215
|
return observer;
|
|
11184
11216
|
}
|
|
11217
|
+
function removeMutationBufferForDoc(doc) {
|
|
11218
|
+
for(var i2 = mutationBuffers.length - 1; i2 >= 0; i2--){
|
|
11219
|
+
var buffer = mutationBuffers[i2];
|
|
11220
|
+
if (buffer.getDoc() === doc) {
|
|
11221
|
+
mutationBuffers.splice(i2, 1);
|
|
11222
|
+
}
|
|
11223
|
+
}
|
|
11224
|
+
}
|
|
11185
11225
|
function initMoveObserver(param) {
|
|
11186
11226
|
var mousemoveCb = param.mousemoveCb, sampling = param.sampling, doc = param.doc, mirror2 = param.mirror;
|
|
11187
11227
|
if (sampling.mousemove === false) {
|
|
@@ -12197,6 +12237,8 @@ var IframeManager = /*#__PURE__*/ function() {
|
|
|
12197
12237
|
__publicField$1(this, "crossOriginIframeMirror", new CrossOriginIframeMirror(genId));
|
|
12198
12238
|
__publicField$1(this, "crossOriginIframeStyleMirror");
|
|
12199
12239
|
__publicField$1(this, "crossOriginIframeRootIdMap", /* @__PURE__ */ new WeakMap());
|
|
12240
|
+
__publicField$1(this, "iframeContentDocumentMap", /* @__PURE__ */ new WeakMap());
|
|
12241
|
+
__publicField$1(this, "iframeObserverCleanupMap", /* @__PURE__ */ new WeakMap());
|
|
12200
12242
|
__publicField$1(this, "mirror");
|
|
12201
12243
|
__publicField$1(this, "mutationCb");
|
|
12202
12244
|
__publicField$1(this, "wrappedEmit");
|
|
@@ -12218,6 +12260,31 @@ var IframeManager = /*#__PURE__*/ function() {
|
|
|
12218
12260
|
this.iframes.set(iframeEl, true);
|
|
12219
12261
|
if (iframeEl.contentWindow) this.crossOriginIframeMap.set(iframeEl.contentWindow, iframeEl);
|
|
12220
12262
|
};
|
|
12263
|
+
_proto.getIframeContentDocument = function getIframeContentDocument(iframeEl) {
|
|
12264
|
+
return this.iframeContentDocumentMap.get(iframeEl);
|
|
12265
|
+
};
|
|
12266
|
+
_proto.setObserverCleanup = function setObserverCleanup(iframeEl, cleanup) {
|
|
12267
|
+
this.iframeObserverCleanupMap.set(iframeEl, cleanup);
|
|
12268
|
+
};
|
|
12269
|
+
_proto.getObserverCleanup = function getObserverCleanup(iframeEl) {
|
|
12270
|
+
return this.iframeObserverCleanupMap.get(iframeEl);
|
|
12271
|
+
};
|
|
12272
|
+
_proto.removeIframe = function removeIframe(iframeEl) {
|
|
12273
|
+
var storedDoc = this.iframeContentDocumentMap.get(iframeEl);
|
|
12274
|
+
if (storedDoc) {
|
|
12275
|
+
this.stylesheetManager.cleanupStylesheetsForRemovedNode(storedDoc);
|
|
12276
|
+
this.mirror.removeNodeFromMap(storedDoc, true);
|
|
12277
|
+
}
|
|
12278
|
+
this.iframes.delete(iframeEl);
|
|
12279
|
+
this.iframeContentDocumentMap.delete(iframeEl);
|
|
12280
|
+
var observerCleanup = this.iframeObserverCleanupMap.get(iframeEl);
|
|
12281
|
+
if (observerCleanup) {
|
|
12282
|
+
try {
|
|
12283
|
+
observerCleanup();
|
|
12284
|
+
} catch (e2) {}
|
|
12285
|
+
this.iframeObserverCleanupMap.delete(iframeEl);
|
|
12286
|
+
}
|
|
12287
|
+
};
|
|
12221
12288
|
_proto.addLoadListener = function addLoadListener(cb) {
|
|
12222
12289
|
this.loadListener = cb;
|
|
12223
12290
|
};
|
|
@@ -12236,6 +12303,9 @@ var IframeManager = /*#__PURE__*/ function() {
|
|
|
12236
12303
|
attributes: [],
|
|
12237
12304
|
isAttachIframe: true
|
|
12238
12305
|
});
|
|
12306
|
+
if (iframeEl.contentDocument) {
|
|
12307
|
+
this.iframeContentDocumentMap.set(iframeEl, iframeEl.contentDocument);
|
|
12308
|
+
}
|
|
12239
12309
|
if (this.recordCrossOriginIframes) (_a2 = iframeEl.contentWindow) == null ? void 0 : _a2.addEventListener("message", this.handleMessage.bind(this));
|
|
12240
12310
|
(_b = this.loadListener) == null ? void 0 : _b.call(this, iframeEl);
|
|
12241
12311
|
if (iframeEl.contentDocument && iframeEl.contentDocument.adoptedStyleSheets && iframeEl.contentDocument.adoptedStyleSheets.length > 0) this.stylesheetManager.adoptStyleSheets(iframeEl.contentDocument.adoptedStyleSheets, this.mirror.getId(iframeEl.contentDocument));
|
|
@@ -13148,6 +13218,41 @@ var StylesheetManager = /*#__PURE__*/ function() {
|
|
|
13148
13218
|
this.styleMirror.reset();
|
|
13149
13219
|
this.trackedLinkElements = /* @__PURE__ */ new WeakSet();
|
|
13150
13220
|
};
|
|
13221
|
+
/**
|
|
13222
|
+
* Cleans up stylesheets associated with a removed node.
|
|
13223
|
+
*
|
|
13224
|
+
* @param removedNode - The node that was removed from the DOM.
|
|
13225
|
+
*/ _proto.cleanupStylesheetsForRemovedNode = function cleanupStylesheetsForRemovedNode(removedNode) {
|
|
13226
|
+
var _this = this;
|
|
13227
|
+
try {
|
|
13228
|
+
if (removedNode.nodeType === Node.DOCUMENT_NODE) {
|
|
13229
|
+
var doc = removedNode;
|
|
13230
|
+
if (doc.adoptedStyleSheets) {
|
|
13231
|
+
for(var _iterator = _create_for_of_iterator_helper_loose(doc.adoptedStyleSheets), _step; !(_step = _iterator()).done;){
|
|
13232
|
+
var sheet = _step.value;
|
|
13233
|
+
this.styleMirror.remove(sheet);
|
|
13234
|
+
}
|
|
13235
|
+
}
|
|
13236
|
+
}
|
|
13237
|
+
if (removedNode.nodeName === "STYLE") {
|
|
13238
|
+
var styleEl = removedNode;
|
|
13239
|
+
if (styleEl.sheet) {
|
|
13240
|
+
this.styleMirror.remove(styleEl.sheet);
|
|
13241
|
+
}
|
|
13242
|
+
}
|
|
13243
|
+
if (removedNode.nodeName === "LINK" && removedNode.rel === "stylesheet") {
|
|
13244
|
+
var linkEl = removedNode;
|
|
13245
|
+
if (linkEl.sheet) {
|
|
13246
|
+
this.styleMirror.remove(linkEl.sheet);
|
|
13247
|
+
}
|
|
13248
|
+
}
|
|
13249
|
+
if (removedNode.childNodes) {
|
|
13250
|
+
removedNode.childNodes.forEach(function(child) {
|
|
13251
|
+
_this.cleanupStylesheetsForRemovedNode(child);
|
|
13252
|
+
});
|
|
13253
|
+
}
|
|
13254
|
+
} catch (e2) {}
|
|
13255
|
+
};
|
|
13151
13256
|
// TODO: take snapshot on stylesheet reload by applying event listener
|
|
13152
13257
|
_proto.trackStylesheetInLinkElement = function trackStylesheetInLinkElement(_linkEl) {};
|
|
13153
13258
|
return StylesheetManager;
|
|
@@ -13602,7 +13707,23 @@ function record(options) {
|
|
|
13602
13707
|
};
|
|
13603
13708
|
iframeManager.addLoadListener(function(iframeEl) {
|
|
13604
13709
|
try {
|
|
13605
|
-
|
|
13710
|
+
var iframeDoc = iframeEl.contentDocument;
|
|
13711
|
+
var iframeHandler = observe(iframeDoc);
|
|
13712
|
+
handlers.push(iframeHandler);
|
|
13713
|
+
var existingCleanup = iframeManager.getObserverCleanup(iframeEl);
|
|
13714
|
+
iframeManager.setObserverCleanup(iframeEl, function() {
|
|
13715
|
+
if (existingCleanup) {
|
|
13716
|
+
try {
|
|
13717
|
+
existingCleanup();
|
|
13718
|
+
} catch (e2) {}
|
|
13719
|
+
}
|
|
13720
|
+
try {
|
|
13721
|
+
iframeHandler();
|
|
13722
|
+
var idx = handlers.indexOf(iframeHandler);
|
|
13723
|
+
if (idx !== -1) handlers.splice(idx, 1);
|
|
13724
|
+
removeMutationBufferForDoc(iframeDoc);
|
|
13725
|
+
} catch (e2) {}
|
|
13726
|
+
});
|
|
13606
13727
|
} catch (error) {
|
|
13607
13728
|
console.warn(error);
|
|
13608
13729
|
}
|
|
@@ -13859,7 +13980,7 @@ function classMatchesRegex(node2, regex, checkAncestors) {
|
|
|
13859
13980
|
}
|
|
13860
13981
|
return classMatchesRegex(index.parentNode(node2), regex);
|
|
13861
13982
|
}
|
|
13862
|
-
function getDefaultExportFromCjs(x2) {
|
|
13983
|
+
function getDefaultExportFromCjs$3(x2) {
|
|
13863
13984
|
return x2 && x2.__esModule && Object.prototype.hasOwnProperty.call(x2, "default") ? x2["default"] : x2;
|
|
13864
13985
|
}
|
|
13865
13986
|
function getAugmentedNamespace(n) {
|
|
@@ -17974,7 +18095,7 @@ postcss.Node = Node2;
|
|
|
17974
18095
|
LazyResult2.registerPostcss(postcss);
|
|
17975
18096
|
var postcss_1 = postcss;
|
|
17976
18097
|
postcss.default = postcss;
|
|
17977
|
-
var postcss$1 = /* @__PURE__ */ getDefaultExportFromCjs(postcss_1);
|
|
18098
|
+
var postcss$1 = /* @__PURE__ */ getDefaultExportFromCjs$3(postcss_1);
|
|
17978
18099
|
postcss$1.stringify;
|
|
17979
18100
|
postcss$1.fromJSON;
|
|
17980
18101
|
postcss$1.plugin;
|
|
@@ -18851,7 +18972,7 @@ if (typeof Promise !== 'undefined' && Promise.toString().indexOf('[native code]'
|
|
|
18851
18972
|
|
|
18852
18973
|
var Config = {
|
|
18853
18974
|
DEBUG: false,
|
|
18854
|
-
LIB_VERSION: '2.
|
|
18975
|
+
LIB_VERSION: '2.75.0'
|
|
18855
18976
|
};
|
|
18856
18977
|
|
|
18857
18978
|
/* eslint camelcase: "off", eqeqeq: "off" */
|
|
@@ -19057,15 +19178,8 @@ _.isArray = nativeIsArray || function(obj) {
|
|
|
19057
19178
|
return toString.call(obj) === '[object Array]';
|
|
19058
19179
|
};
|
|
19059
19180
|
|
|
19060
|
-
// from a comment on http://dbj.org/dbj/?p=286
|
|
19061
|
-
// fails on only one very rare and deliberate custom object:
|
|
19062
|
-
// var bomb = { toString : undefined, valueOf: function(o) { return "function BOMBA!"; }};
|
|
19063
19181
|
_.isFunction = function(f) {
|
|
19064
|
-
|
|
19065
|
-
return /^\s*\bfunction\b/.test(f);
|
|
19066
|
-
} catch (x) {
|
|
19067
|
-
return false;
|
|
19068
|
-
}
|
|
19182
|
+
return typeof f === 'function';
|
|
19069
19183
|
};
|
|
19070
19184
|
|
|
19071
19185
|
_.isArguments = function(obj) {
|
|
@@ -23759,7 +23873,590 @@ Object.defineProperty(MixpanelRecorder.prototype, 'replayId', {
|
|
|
23759
23873
|
}
|
|
23760
23874
|
});
|
|
23761
23875
|
|
|
23762
|
-
win[
|
|
23876
|
+
win[RECORDER_GLOBAL_NAME] = MixpanelRecorder;
|
|
23877
|
+
|
|
23878
|
+
function getDefaultExportFromCjs (x) {
|
|
23879
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
23880
|
+
}
|
|
23881
|
+
|
|
23882
|
+
var logic$1 = {exports: {}};
|
|
23883
|
+
|
|
23884
|
+
/* globals define,module */
|
|
23885
|
+
var logic = logic$1.exports;
|
|
23886
|
+
|
|
23887
|
+
var hasRequiredLogic;
|
|
23888
|
+
|
|
23889
|
+
function requireLogic () {
|
|
23890
|
+
if (hasRequiredLogic) return logic$1.exports;
|
|
23891
|
+
hasRequiredLogic = 1;
|
|
23892
|
+
(function (module, exports) {
|
|
23893
|
+
(function(root, factory) {
|
|
23894
|
+
{
|
|
23895
|
+
module.exports = factory();
|
|
23896
|
+
}
|
|
23897
|
+
}(logic, function() {
|
|
23898
|
+
/* globals console:false */
|
|
23899
|
+
|
|
23900
|
+
if ( ! Array.isArray) {
|
|
23901
|
+
Array.isArray = function(arg) {
|
|
23902
|
+
return Object.prototype.toString.call(arg) === "[object Array]";
|
|
23903
|
+
};
|
|
23904
|
+
}
|
|
23905
|
+
|
|
23906
|
+
/**
|
|
23907
|
+
* Return an array that contains no duplicates (original not modified)
|
|
23908
|
+
* @param {array} array Original reference array
|
|
23909
|
+
* @return {array} New array with no duplicates
|
|
23910
|
+
*/
|
|
23911
|
+
function arrayUnique(array) {
|
|
23912
|
+
var a = [];
|
|
23913
|
+
for (var i=0, l=array.length; i<l; i++) {
|
|
23914
|
+
if (a.indexOf(array[i]) === -1) {
|
|
23915
|
+
a.push(array[i]);
|
|
23916
|
+
}
|
|
23917
|
+
}
|
|
23918
|
+
return a;
|
|
23919
|
+
}
|
|
23920
|
+
|
|
23921
|
+
var jsonLogic = {};
|
|
23922
|
+
var operations = {
|
|
23923
|
+
"==": function(a, b) {
|
|
23924
|
+
return a == b;
|
|
23925
|
+
},
|
|
23926
|
+
"===": function(a, b) {
|
|
23927
|
+
return a === b;
|
|
23928
|
+
},
|
|
23929
|
+
"!=": function(a, b) {
|
|
23930
|
+
return a != b;
|
|
23931
|
+
},
|
|
23932
|
+
"!==": function(a, b) {
|
|
23933
|
+
return a !== b;
|
|
23934
|
+
},
|
|
23935
|
+
">": function(a, b) {
|
|
23936
|
+
return a > b;
|
|
23937
|
+
},
|
|
23938
|
+
">=": function(a, b) {
|
|
23939
|
+
return a >= b;
|
|
23940
|
+
},
|
|
23941
|
+
"<": function(a, b, c) {
|
|
23942
|
+
return (c === undefined) ? a < b : (a < b) && (b < c);
|
|
23943
|
+
},
|
|
23944
|
+
"<=": function(a, b, c) {
|
|
23945
|
+
return (c === undefined) ? a <= b : (a <= b) && (b <= c);
|
|
23946
|
+
},
|
|
23947
|
+
"!!": function(a) {
|
|
23948
|
+
return jsonLogic.truthy(a);
|
|
23949
|
+
},
|
|
23950
|
+
"!": function(a) {
|
|
23951
|
+
return !jsonLogic.truthy(a);
|
|
23952
|
+
},
|
|
23953
|
+
"%": function(a, b) {
|
|
23954
|
+
return a % b;
|
|
23955
|
+
},
|
|
23956
|
+
"log": function(a) {
|
|
23957
|
+
console.log(a); return a;
|
|
23958
|
+
},
|
|
23959
|
+
"in": function(a, b) {
|
|
23960
|
+
if (!b || typeof b.indexOf === "undefined") return false;
|
|
23961
|
+
return (b.indexOf(a) !== -1);
|
|
23962
|
+
},
|
|
23963
|
+
"cat": function() {
|
|
23964
|
+
return Array.prototype.join.call(arguments, "");
|
|
23965
|
+
},
|
|
23966
|
+
"substr": function(source, start, end) {
|
|
23967
|
+
if (end < 0) {
|
|
23968
|
+
// JavaScript doesn't support negative end, this emulates PHP behavior
|
|
23969
|
+
var temp = String(source).substr(start);
|
|
23970
|
+
return temp.substr(0, temp.length + end);
|
|
23971
|
+
}
|
|
23972
|
+
return String(source).substr(start, end);
|
|
23973
|
+
},
|
|
23974
|
+
"+": function() {
|
|
23975
|
+
return Array.prototype.reduce.call(arguments, function(a, b) {
|
|
23976
|
+
return parseFloat(a, 10) + parseFloat(b, 10);
|
|
23977
|
+
}, 0);
|
|
23978
|
+
},
|
|
23979
|
+
"*": function() {
|
|
23980
|
+
return Array.prototype.reduce.call(arguments, function(a, b) {
|
|
23981
|
+
return parseFloat(a, 10) * parseFloat(b, 10);
|
|
23982
|
+
});
|
|
23983
|
+
},
|
|
23984
|
+
"-": function(a, b) {
|
|
23985
|
+
if (b === undefined) {
|
|
23986
|
+
return -a;
|
|
23987
|
+
} else {
|
|
23988
|
+
return a - b;
|
|
23989
|
+
}
|
|
23990
|
+
},
|
|
23991
|
+
"/": function(a, b) {
|
|
23992
|
+
return a / b;
|
|
23993
|
+
},
|
|
23994
|
+
"min": function() {
|
|
23995
|
+
return Math.min.apply(this, arguments);
|
|
23996
|
+
},
|
|
23997
|
+
"max": function() {
|
|
23998
|
+
return Math.max.apply(this, arguments);
|
|
23999
|
+
},
|
|
24000
|
+
"merge": function() {
|
|
24001
|
+
return Array.prototype.reduce.call(arguments, function(a, b) {
|
|
24002
|
+
return a.concat(b);
|
|
24003
|
+
}, []);
|
|
24004
|
+
},
|
|
24005
|
+
"var": function(a, b) {
|
|
24006
|
+
var not_found = (b === undefined) ? null : b;
|
|
24007
|
+
var data = this;
|
|
24008
|
+
if (typeof a === "undefined" || a==="" || a===null) {
|
|
24009
|
+
return data;
|
|
24010
|
+
}
|
|
24011
|
+
var sub_props = String(a).split(".");
|
|
24012
|
+
for (var i = 0; i < sub_props.length; i++) {
|
|
24013
|
+
if (data === null || data === undefined) {
|
|
24014
|
+
return not_found;
|
|
24015
|
+
}
|
|
24016
|
+
// Descending into data
|
|
24017
|
+
data = data[sub_props[i]];
|
|
24018
|
+
if (data === undefined) {
|
|
24019
|
+
return not_found;
|
|
24020
|
+
}
|
|
24021
|
+
}
|
|
24022
|
+
return data;
|
|
24023
|
+
},
|
|
24024
|
+
"missing": function() {
|
|
24025
|
+
/*
|
|
24026
|
+
Missing can receive many keys as many arguments, like {"missing:[1,2]}
|
|
24027
|
+
Missing can also receive *one* argument that is an array of keys,
|
|
24028
|
+
which typically happens if it's actually acting on the output of another command
|
|
24029
|
+
(like 'if' or 'merge')
|
|
24030
|
+
*/
|
|
24031
|
+
|
|
24032
|
+
var missing = [];
|
|
24033
|
+
var keys = Array.isArray(arguments[0]) ? arguments[0] : arguments;
|
|
24034
|
+
|
|
24035
|
+
for (var i = 0; i < keys.length; i++) {
|
|
24036
|
+
var key = keys[i];
|
|
24037
|
+
var value = jsonLogic.apply({"var": key}, this);
|
|
24038
|
+
if (value === null || value === "") {
|
|
24039
|
+
missing.push(key);
|
|
24040
|
+
}
|
|
24041
|
+
}
|
|
24042
|
+
|
|
24043
|
+
return missing;
|
|
24044
|
+
},
|
|
24045
|
+
"missing_some": function(need_count, options) {
|
|
24046
|
+
// missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence.
|
|
24047
|
+
var are_missing = jsonLogic.apply({"missing": options}, this);
|
|
24048
|
+
|
|
24049
|
+
if (options.length - are_missing.length >= need_count) {
|
|
24050
|
+
return [];
|
|
24051
|
+
} else {
|
|
24052
|
+
return are_missing;
|
|
24053
|
+
}
|
|
24054
|
+
},
|
|
24055
|
+
};
|
|
24056
|
+
|
|
24057
|
+
jsonLogic.is_logic = function(logic) {
|
|
24058
|
+
return (
|
|
24059
|
+
typeof logic === "object" && // An object
|
|
24060
|
+
logic !== null && // but not null
|
|
24061
|
+
! Array.isArray(logic) && // and not an array
|
|
24062
|
+
Object.keys(logic).length === 1 // with exactly one key
|
|
24063
|
+
);
|
|
24064
|
+
};
|
|
24065
|
+
|
|
24066
|
+
/*
|
|
24067
|
+
This helper will defer to the JsonLogic spec as a tie-breaker when different language interpreters define different behavior for the truthiness of primitives. E.g., PHP considers empty arrays to be falsy, but Javascript considers them to be truthy. JsonLogic, as an ecosystem, needs one consistent answer.
|
|
24068
|
+
|
|
24069
|
+
Spec and rationale here: http://jsonlogic.com/truthy
|
|
24070
|
+
*/
|
|
24071
|
+
jsonLogic.truthy = function(value) {
|
|
24072
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
24073
|
+
return false;
|
|
24074
|
+
}
|
|
24075
|
+
return !! value;
|
|
24076
|
+
};
|
|
24077
|
+
|
|
24078
|
+
|
|
24079
|
+
jsonLogic.get_operator = function(logic) {
|
|
24080
|
+
return Object.keys(logic)[0];
|
|
24081
|
+
};
|
|
24082
|
+
|
|
24083
|
+
jsonLogic.get_values = function(logic) {
|
|
24084
|
+
return logic[jsonLogic.get_operator(logic)];
|
|
24085
|
+
};
|
|
24086
|
+
|
|
24087
|
+
jsonLogic.apply = function(logic, data) {
|
|
24088
|
+
// Does this array contain logic? Only one way to find out.
|
|
24089
|
+
if (Array.isArray(logic)) {
|
|
24090
|
+
return logic.map(function(l) {
|
|
24091
|
+
return jsonLogic.apply(l, data);
|
|
24092
|
+
});
|
|
24093
|
+
}
|
|
24094
|
+
// You've recursed to a primitive, stop!
|
|
24095
|
+
if ( ! jsonLogic.is_logic(logic) ) {
|
|
24096
|
+
return logic;
|
|
24097
|
+
}
|
|
24098
|
+
|
|
24099
|
+
var op = jsonLogic.get_operator(logic);
|
|
24100
|
+
var values = logic[op];
|
|
24101
|
+
var i;
|
|
24102
|
+
var current;
|
|
24103
|
+
var scopedLogic;
|
|
24104
|
+
var scopedData;
|
|
24105
|
+
var initial;
|
|
24106
|
+
|
|
24107
|
+
// easy syntax for unary operators, like {"var" : "x"} instead of strict {"var" : ["x"]}
|
|
24108
|
+
if ( ! Array.isArray(values)) {
|
|
24109
|
+
values = [values];
|
|
24110
|
+
}
|
|
24111
|
+
|
|
24112
|
+
// 'if', 'and', and 'or' violate the normal rule of depth-first calculating consequents, let each manage recursion as needed.
|
|
24113
|
+
if (op === "if" || op == "?:") {
|
|
24114
|
+
/* 'if' should be called with a odd number of parameters, 3 or greater
|
|
24115
|
+
This works on the pattern:
|
|
24116
|
+
if( 0 ){ 1 }else{ 2 };
|
|
24117
|
+
if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 };
|
|
24118
|
+
if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 };
|
|
24119
|
+
|
|
24120
|
+
The implementation is:
|
|
24121
|
+
For pairs of values (0,1 then 2,3 then 4,5 etc)
|
|
24122
|
+
If the first evaluates truthy, evaluate and return the second
|
|
24123
|
+
If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3)
|
|
24124
|
+
given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false)
|
|
24125
|
+
given 0 parameters, return NULL (not great practice, but there was no Else)
|
|
24126
|
+
*/
|
|
24127
|
+
for (i = 0; i < values.length - 1; i += 2) {
|
|
24128
|
+
if ( jsonLogic.truthy( jsonLogic.apply(values[i], data) ) ) {
|
|
24129
|
+
return jsonLogic.apply(values[i+1], data);
|
|
24130
|
+
}
|
|
24131
|
+
}
|
|
24132
|
+
if (values.length === i+1) {
|
|
24133
|
+
return jsonLogic.apply(values[i], data);
|
|
24134
|
+
}
|
|
24135
|
+
return null;
|
|
24136
|
+
} else if (op === "and") { // Return first falsy, or last
|
|
24137
|
+
for (i=0; i < values.length; i+=1) {
|
|
24138
|
+
current = jsonLogic.apply(values[i], data);
|
|
24139
|
+
if ( ! jsonLogic.truthy(current)) {
|
|
24140
|
+
return current;
|
|
24141
|
+
}
|
|
24142
|
+
}
|
|
24143
|
+
return current; // Last
|
|
24144
|
+
} else if (op === "or") {// Return first truthy, or last
|
|
24145
|
+
for (i=0; i < values.length; i+=1) {
|
|
24146
|
+
current = jsonLogic.apply(values[i], data);
|
|
24147
|
+
if ( jsonLogic.truthy(current) ) {
|
|
24148
|
+
return current;
|
|
24149
|
+
}
|
|
24150
|
+
}
|
|
24151
|
+
return current; // Last
|
|
24152
|
+
} else if (op === "filter") {
|
|
24153
|
+
scopedData = jsonLogic.apply(values[0], data);
|
|
24154
|
+
scopedLogic = values[1];
|
|
24155
|
+
|
|
24156
|
+
if ( ! Array.isArray(scopedData)) {
|
|
24157
|
+
return [];
|
|
24158
|
+
}
|
|
24159
|
+
// Return only the elements from the array in the first argument,
|
|
24160
|
+
// that return truthy when passed to the logic in the second argument.
|
|
24161
|
+
// For parity with JavaScript, reindex the returned array
|
|
24162
|
+
return scopedData.filter(function(datum) {
|
|
24163
|
+
return jsonLogic.truthy( jsonLogic.apply(scopedLogic, datum));
|
|
24164
|
+
});
|
|
24165
|
+
} else if (op === "map") {
|
|
24166
|
+
scopedData = jsonLogic.apply(values[0], data);
|
|
24167
|
+
scopedLogic = values[1];
|
|
24168
|
+
|
|
24169
|
+
if ( ! Array.isArray(scopedData)) {
|
|
24170
|
+
return [];
|
|
24171
|
+
}
|
|
24172
|
+
|
|
24173
|
+
return scopedData.map(function(datum) {
|
|
24174
|
+
return jsonLogic.apply(scopedLogic, datum);
|
|
24175
|
+
});
|
|
24176
|
+
} else if (op === "reduce") {
|
|
24177
|
+
scopedData = jsonLogic.apply(values[0], data);
|
|
24178
|
+
scopedLogic = values[1];
|
|
24179
|
+
initial = typeof values[2] !== "undefined" ? jsonLogic.apply(values[2], data) : null;
|
|
24180
|
+
|
|
24181
|
+
if ( ! Array.isArray(scopedData)) {
|
|
24182
|
+
return initial;
|
|
24183
|
+
}
|
|
24184
|
+
|
|
24185
|
+
return scopedData.reduce(
|
|
24186
|
+
function(accumulator, current) {
|
|
24187
|
+
return jsonLogic.apply(
|
|
24188
|
+
scopedLogic,
|
|
24189
|
+
{current: current, accumulator: accumulator}
|
|
24190
|
+
);
|
|
24191
|
+
},
|
|
24192
|
+
initial
|
|
24193
|
+
);
|
|
24194
|
+
} else if (op === "all") {
|
|
24195
|
+
scopedData = jsonLogic.apply(values[0], data);
|
|
24196
|
+
scopedLogic = values[1];
|
|
24197
|
+
// All of an empty set is false. Note, some and none have correct fallback after the for loop
|
|
24198
|
+
if ( ! Array.isArray(scopedData) || ! scopedData.length) {
|
|
24199
|
+
return false;
|
|
24200
|
+
}
|
|
24201
|
+
for (i=0; i < scopedData.length; i+=1) {
|
|
24202
|
+
if ( ! jsonLogic.truthy( jsonLogic.apply(scopedLogic, scopedData[i]) )) {
|
|
24203
|
+
return false; // First falsy, short circuit
|
|
24204
|
+
}
|
|
24205
|
+
}
|
|
24206
|
+
return true; // All were truthy
|
|
24207
|
+
} else if (op === "none") {
|
|
24208
|
+
scopedData = jsonLogic.apply(values[0], data);
|
|
24209
|
+
scopedLogic = values[1];
|
|
24210
|
+
|
|
24211
|
+
if ( ! Array.isArray(scopedData) || ! scopedData.length) {
|
|
24212
|
+
return true;
|
|
24213
|
+
}
|
|
24214
|
+
for (i=0; i < scopedData.length; i+=1) {
|
|
24215
|
+
if ( jsonLogic.truthy( jsonLogic.apply(scopedLogic, scopedData[i]) )) {
|
|
24216
|
+
return false; // First truthy, short circuit
|
|
24217
|
+
}
|
|
24218
|
+
}
|
|
24219
|
+
return true; // None were truthy
|
|
24220
|
+
} else if (op === "some") {
|
|
24221
|
+
scopedData = jsonLogic.apply(values[0], data);
|
|
24222
|
+
scopedLogic = values[1];
|
|
24223
|
+
|
|
24224
|
+
if ( ! Array.isArray(scopedData) || ! scopedData.length) {
|
|
24225
|
+
return false;
|
|
24226
|
+
}
|
|
24227
|
+
for (i=0; i < scopedData.length; i+=1) {
|
|
24228
|
+
if ( jsonLogic.truthy( jsonLogic.apply(scopedLogic, scopedData[i]) )) {
|
|
24229
|
+
return true; // First truthy, short circuit
|
|
24230
|
+
}
|
|
24231
|
+
}
|
|
24232
|
+
return false; // None were truthy
|
|
24233
|
+
}
|
|
24234
|
+
|
|
24235
|
+
// Everyone else gets immediate depth-first recursion
|
|
24236
|
+
values = values.map(function(val) {
|
|
24237
|
+
return jsonLogic.apply(val, data);
|
|
24238
|
+
});
|
|
24239
|
+
|
|
24240
|
+
|
|
24241
|
+
// The operation is called with "data" bound to its "this" and "values" passed as arguments.
|
|
24242
|
+
// Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments
|
|
24243
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
|
|
24244
|
+
if (operations.hasOwnProperty(op) && typeof operations[op] === "function") {
|
|
24245
|
+
return operations[op].apply(data, values);
|
|
24246
|
+
} else if (op.indexOf(".") > 0) { // Contains a dot, and not in the 0th position
|
|
24247
|
+
var sub_ops = String(op).split(".");
|
|
24248
|
+
var operation = operations;
|
|
24249
|
+
for (i = 0; i < sub_ops.length; i++) {
|
|
24250
|
+
if (!operation.hasOwnProperty(sub_ops[i])) {
|
|
24251
|
+
throw new Error("Unrecognized operation " + op +
|
|
24252
|
+
" (failed at " + sub_ops.slice(0, i+1).join(".") + ")");
|
|
24253
|
+
}
|
|
24254
|
+
// Descending into operations
|
|
24255
|
+
operation = operation[sub_ops[i]];
|
|
24256
|
+
}
|
|
24257
|
+
|
|
24258
|
+
return operation.apply(data, values);
|
|
24259
|
+
}
|
|
24260
|
+
|
|
24261
|
+
throw new Error("Unrecognized operation " + op );
|
|
24262
|
+
};
|
|
24263
|
+
|
|
24264
|
+
jsonLogic.uses_data = function(logic) {
|
|
24265
|
+
var collection = [];
|
|
24266
|
+
|
|
24267
|
+
if (jsonLogic.is_logic(logic)) {
|
|
24268
|
+
var op = jsonLogic.get_operator(logic);
|
|
24269
|
+
var values = logic[op];
|
|
24270
|
+
|
|
24271
|
+
if ( ! Array.isArray(values)) {
|
|
24272
|
+
values = [values];
|
|
24273
|
+
}
|
|
24274
|
+
|
|
24275
|
+
if (op === "var") {
|
|
24276
|
+
// This doesn't cover the case where the arg to var is itself a rule.
|
|
24277
|
+
collection.push(values[0]);
|
|
24278
|
+
} else {
|
|
24279
|
+
// Recursion!
|
|
24280
|
+
values.forEach(function(val) {
|
|
24281
|
+
collection.push.apply(collection, jsonLogic.uses_data(val) );
|
|
24282
|
+
});
|
|
24283
|
+
}
|
|
24284
|
+
}
|
|
24285
|
+
|
|
24286
|
+
return arrayUnique(collection);
|
|
24287
|
+
};
|
|
24288
|
+
|
|
24289
|
+
jsonLogic.add_operation = function(name, code) {
|
|
24290
|
+
operations[name] = code;
|
|
24291
|
+
};
|
|
24292
|
+
|
|
24293
|
+
jsonLogic.rm_operation = function(name) {
|
|
24294
|
+
delete operations[name];
|
|
24295
|
+
};
|
|
24296
|
+
|
|
24297
|
+
jsonLogic.rule_like = function(rule, pattern) {
|
|
24298
|
+
// console.log("Is ". JSON.stringify(rule) . " like " . JSON.stringify(pattern) . "?");
|
|
24299
|
+
if (pattern === rule) {
|
|
24300
|
+
return true;
|
|
24301
|
+
} // TODO : Deep object equivalency?
|
|
24302
|
+
if (pattern === "@") {
|
|
24303
|
+
return true;
|
|
24304
|
+
} // Wildcard!
|
|
24305
|
+
if (pattern === "number") {
|
|
24306
|
+
return (typeof rule === "number");
|
|
24307
|
+
}
|
|
24308
|
+
if (pattern === "string") {
|
|
24309
|
+
return (typeof rule === "string");
|
|
24310
|
+
}
|
|
24311
|
+
if (pattern === "array") {
|
|
24312
|
+
// !logic test might be superfluous in JavaScript
|
|
24313
|
+
return Array.isArray(rule) && ! jsonLogic.is_logic(rule);
|
|
24314
|
+
}
|
|
24315
|
+
|
|
24316
|
+
if (jsonLogic.is_logic(pattern)) {
|
|
24317
|
+
if (jsonLogic.is_logic(rule)) {
|
|
24318
|
+
var pattern_op = jsonLogic.get_operator(pattern);
|
|
24319
|
+
var rule_op = jsonLogic.get_operator(rule);
|
|
24320
|
+
|
|
24321
|
+
if (pattern_op === "@" || pattern_op === rule_op) {
|
|
24322
|
+
// echo "\nOperators match, go deeper\n";
|
|
24323
|
+
return jsonLogic.rule_like(
|
|
24324
|
+
jsonLogic.get_values(rule, false),
|
|
24325
|
+
jsonLogic.get_values(pattern, false)
|
|
24326
|
+
);
|
|
24327
|
+
}
|
|
24328
|
+
}
|
|
24329
|
+
return false; // pattern is logic, rule isn't, can't be eq
|
|
24330
|
+
}
|
|
24331
|
+
|
|
24332
|
+
if (Array.isArray(pattern)) {
|
|
24333
|
+
if (Array.isArray(rule)) {
|
|
24334
|
+
if (pattern.length !== rule.length) {
|
|
24335
|
+
return false;
|
|
24336
|
+
}
|
|
24337
|
+
/*
|
|
24338
|
+
Note, array order MATTERS, because we're using this array test logic to consider arguments, where order can matter. (e.g., + is commutative, but '-' or 'if' or 'var' are NOT)
|
|
24339
|
+
*/
|
|
24340
|
+
for (var i = 0; i < pattern.length; i += 1) {
|
|
24341
|
+
// If any fail, we fail
|
|
24342
|
+
if ( ! jsonLogic.rule_like(rule[i], pattern[i])) {
|
|
24343
|
+
return false;
|
|
24344
|
+
}
|
|
24345
|
+
}
|
|
24346
|
+
return true; // If they *all* passed, we pass
|
|
24347
|
+
} else {
|
|
24348
|
+
return false; // Pattern is array, rule isn't
|
|
24349
|
+
}
|
|
24350
|
+
}
|
|
24351
|
+
|
|
24352
|
+
// Not logic, not array, not a === match for rule.
|
|
24353
|
+
return false;
|
|
24354
|
+
};
|
|
24355
|
+
|
|
24356
|
+
return jsonLogic;
|
|
24357
|
+
}));
|
|
24358
|
+
} (logic$1));
|
|
24359
|
+
return logic$1.exports;
|
|
24360
|
+
}
|
|
24361
|
+
|
|
24362
|
+
var logicExports = requireLogic();
|
|
24363
|
+
var jsonLogic = /*@__PURE__*/getDefaultExportFromCjs(logicExports);
|
|
24364
|
+
|
|
24365
|
+
/**
|
|
24366
|
+
* Shared helper to recursively lowercase strings in nested structures
|
|
24367
|
+
* @param {*} obj - Value to process
|
|
24368
|
+
* @param {boolean} lowercaseKeys - Whether to lowercase object keys
|
|
24369
|
+
* @returns {*} Processed value with lowercased strings
|
|
24370
|
+
*/
|
|
24371
|
+
var lowercaseJson = function(obj, lowercaseKeys) {
|
|
24372
|
+
if (obj === null || obj === undefined) {
|
|
24373
|
+
return obj;
|
|
24374
|
+
} else if (typeof obj === 'string') {
|
|
24375
|
+
return obj.toLowerCase();
|
|
24376
|
+
} else if (Array.isArray(obj)) {
|
|
24377
|
+
return obj.map(function(item) {
|
|
24378
|
+
return lowercaseJson(item, lowercaseKeys);
|
|
24379
|
+
});
|
|
24380
|
+
} else if (obj === Object(obj)) {
|
|
24381
|
+
var result = {};
|
|
24382
|
+
for (var key in obj) {
|
|
24383
|
+
if (obj.hasOwnProperty(key)) {
|
|
24384
|
+
var newKey = lowercaseKeys && typeof key === 'string' ? key.toLowerCase() : key;
|
|
24385
|
+
result[newKey] = lowercaseJson(obj[key], lowercaseKeys);
|
|
24386
|
+
}
|
|
24387
|
+
}
|
|
24388
|
+
return result;
|
|
24389
|
+
} else {
|
|
24390
|
+
return obj;
|
|
24391
|
+
}
|
|
24392
|
+
};
|
|
24393
|
+
|
|
24394
|
+
/**
|
|
24395
|
+
* Lowercase all string keys and values in a nested structure
|
|
24396
|
+
* @param {*} val - Value to process
|
|
24397
|
+
* @returns {*} Processed value with lowercased strings
|
|
24398
|
+
*/
|
|
24399
|
+
var lowercaseKeysAndValues = function(val) {
|
|
24400
|
+
return lowercaseJson(val, true);
|
|
24401
|
+
};
|
|
24402
|
+
|
|
24403
|
+
/**
|
|
24404
|
+
* Lowercase only leaf node string values in a nested structure (keys unchanged)
|
|
24405
|
+
* @param {*} val - Value to process
|
|
24406
|
+
* @returns {*} Processed value with lowercased leaf strings
|
|
24407
|
+
*/
|
|
24408
|
+
var lowercaseOnlyLeafNodes = function(val) {
|
|
24409
|
+
return lowercaseJson(val, false);
|
|
24410
|
+
};
|
|
24411
|
+
|
|
24412
|
+
/**
|
|
24413
|
+
* Check if an event matches the given criteria
|
|
24414
|
+
* @param {string} eventName - The name of the event being checked
|
|
24415
|
+
* @param {Object} properties - Event properties to evaluate against property filters
|
|
24416
|
+
* @param {Object} criteria - Criteria to match against, with:
|
|
24417
|
+
* - event_name: string - Required event name (case-sensitive match)
|
|
24418
|
+
* - property_filters: Object - Optional JsonLogic filters for properties
|
|
24419
|
+
* @returns {Object} Result object with:
|
|
24420
|
+
* - matches: boolean - Whether the event matches the criteria
|
|
24421
|
+
* - error: string|undefined - Error message if evaluation failed
|
|
24422
|
+
*/
|
|
24423
|
+
var eventMatchesCriteria = function(eventName, properties, criteria) {
|
|
24424
|
+
// Check exact event name match (case-sensitive)
|
|
24425
|
+
if (eventName !== criteria.event_name) {
|
|
24426
|
+
return { matches: false };
|
|
24427
|
+
}
|
|
24428
|
+
|
|
24429
|
+
// Evaluate property filters using JsonLogic
|
|
24430
|
+
var propertyFilters = criteria.property_filters;
|
|
24431
|
+
var filtersMatch = true; // default to true if no filters
|
|
24432
|
+
|
|
24433
|
+
if (propertyFilters && !_.isEmptyObject(propertyFilters)) {
|
|
24434
|
+
try {
|
|
24435
|
+
// Lowercase all keys and values in event properties for case-insensitive matching
|
|
24436
|
+
var lowercasedProperties = lowercaseKeysAndValues(properties || {});
|
|
24437
|
+
|
|
24438
|
+
// Lowercase only leaf nodes in JsonLogic filters (keep operators intact)
|
|
24439
|
+
var lowercasedFilters = lowercaseOnlyLeafNodes(propertyFilters);
|
|
24440
|
+
|
|
24441
|
+
filtersMatch = jsonLogic.apply(lowercasedFilters, lowercasedProperties);
|
|
24442
|
+
} catch (error) {
|
|
24443
|
+
return {
|
|
24444
|
+
matches: false,
|
|
24445
|
+
error: error.toString()
|
|
24446
|
+
};
|
|
24447
|
+
}
|
|
24448
|
+
}
|
|
24449
|
+
|
|
24450
|
+
return { matches: filtersMatch };
|
|
24451
|
+
};
|
|
24452
|
+
|
|
24453
|
+
// Create targeting library object
|
|
24454
|
+
var targetingLibrary = {};
|
|
24455
|
+
targetingLibrary['eventMatchesCriteria'] = eventMatchesCriteria;
|
|
24456
|
+
|
|
24457
|
+
// Set global Promise (use bracket notation to prevent minification)
|
|
24458
|
+
// This is the ONE AND ONLY global - matches recorder pattern
|
|
24459
|
+
win[TARGETING_GLOBAL_NAME] = Promise.resolve(targetingLibrary);
|
|
23763
24460
|
|
|
23764
24461
|
/** @const */ var DEFAULT_RAGE_CLICK_THRESHOLD_PX = 30;
|
|
23765
24462
|
/** @const */ var DEFAULT_RAGE_CLICK_TIMEOUT_MS = 1000;
|
|
@@ -24730,14 +25427,62 @@ Autocapture.prototype.stopDeadClickTracking = function() {
|
|
|
24730
25427
|
// TODO integrate error_reporter from mixpanel instance
|
|
24731
25428
|
safewrapClass(Autocapture);
|
|
24732
25429
|
|
|
24733
|
-
|
|
25430
|
+
/**
|
|
25431
|
+
* Get the promise-based targeting loader
|
|
25432
|
+
* @param {Function} loadExtraBundle - Function to load external bundle (callback-based)
|
|
25433
|
+
* @param {string} targetingSrc - URL to targeting bundle
|
|
25434
|
+
* @returns {Promise} Promise that resolves with targeting library
|
|
25435
|
+
*/
|
|
25436
|
+
var getTargetingPromise = function(loadExtraBundle, targetingSrc) {
|
|
25437
|
+
// Return existing promise if already initialized or loading
|
|
25438
|
+
if (win[TARGETING_GLOBAL_NAME] && typeof win[TARGETING_GLOBAL_NAME].then === 'function') {
|
|
25439
|
+
return win[TARGETING_GLOBAL_NAME];
|
|
25440
|
+
}
|
|
25441
|
+
|
|
25442
|
+
// Create loading promise and set it as the global immediately
|
|
25443
|
+
// This makes minified build behavior consistent with dev/CJS builds
|
|
25444
|
+
win[TARGETING_GLOBAL_NAME] = new Promise(function (resolve) {
|
|
25445
|
+
loadExtraBundle(targetingSrc, resolve);
|
|
25446
|
+
}).then(function () {
|
|
25447
|
+
var p = win[TARGETING_GLOBAL_NAME];
|
|
25448
|
+
if (p && typeof p.then === 'function') {
|
|
25449
|
+
return p;
|
|
25450
|
+
}
|
|
25451
|
+
throw new Error('targeting failed to load');
|
|
25452
|
+
}).catch(function (err) {
|
|
25453
|
+
delete win[TARGETING_GLOBAL_NAME];
|
|
25454
|
+
throw err;
|
|
25455
|
+
});
|
|
24734
25456
|
|
|
25457
|
+
return win[TARGETING_GLOBAL_NAME];
|
|
25458
|
+
};
|
|
25459
|
+
|
|
25460
|
+
var logger = console_with_prefix('flags');
|
|
24735
25461
|
var FLAGS_CONFIG_KEY = 'flags';
|
|
24736
25462
|
|
|
24737
25463
|
var CONFIG_CONTEXT = 'context';
|
|
24738
25464
|
var CONFIG_DEFAULTS = {};
|
|
24739
25465
|
CONFIG_DEFAULTS[CONFIG_CONTEXT] = {};
|
|
24740
25466
|
|
|
25467
|
+
/**
|
|
25468
|
+
* Generate a unique key for a pending first-time event
|
|
25469
|
+
* @param {string} flagKey - The flag key
|
|
25470
|
+
* @param {string} firstTimeEventHash - The first_time_event_hash from the pending event definition
|
|
25471
|
+
* @returns {string} Composite key in format "flagKey:firstTimeEventHash"
|
|
25472
|
+
*/
|
|
25473
|
+
var getPendingEventKey = function(flagKey, firstTimeEventHash) {
|
|
25474
|
+
return flagKey + ':' + firstTimeEventHash;
|
|
25475
|
+
};
|
|
25476
|
+
|
|
25477
|
+
/**
|
|
25478
|
+
* Extract the flag key from a pending event key
|
|
25479
|
+
* @param {string} eventKey - The composite event key in format "flagKey:firstTimeEventHash"
|
|
25480
|
+
* @returns {string} The flag key portion
|
|
25481
|
+
*/
|
|
25482
|
+
var getFlagKeyFromPendingEventKey = function(eventKey) {
|
|
25483
|
+
return eventKey.split(':')[0];
|
|
25484
|
+
};
|
|
25485
|
+
|
|
24741
25486
|
/**
|
|
24742
25487
|
* FeatureFlagManager: support for Mixpanel's feature flagging product
|
|
24743
25488
|
* @constructor
|
|
@@ -24749,6 +25494,8 @@ var FeatureFlagManager = function(initOptions) {
|
|
|
24749
25494
|
this.setMpConfig = initOptions.setConfigFunc;
|
|
24750
25495
|
this.getMpProperty = initOptions.getPropertyFunc;
|
|
24751
25496
|
this.track = initOptions.trackingFunc;
|
|
25497
|
+
this.loadExtraBundle = initOptions.loadExtraBundle || function() {};
|
|
25498
|
+
this.targetingSrc = initOptions.targetingSrc || '';
|
|
24752
25499
|
};
|
|
24753
25500
|
|
|
24754
25501
|
FeatureFlagManager.prototype.init = function() {
|
|
@@ -24761,6 +25508,8 @@ FeatureFlagManager.prototype.init = function() {
|
|
|
24761
25508
|
this.fetchFlags();
|
|
24762
25509
|
|
|
24763
25510
|
this.trackedFeatures = new Set();
|
|
25511
|
+
this.pendingFirstTimeEvents = {};
|
|
25512
|
+
this.activatedFirstTimeEvents = {};
|
|
24764
25513
|
};
|
|
24765
25514
|
|
|
24766
25515
|
FeatureFlagManager.prototype.getFullConfig = function() {
|
|
@@ -24841,17 +25590,78 @@ FeatureFlagManager.prototype.fetchFlags = function() {
|
|
|
24841
25590
|
throw new Error('No flags in API response');
|
|
24842
25591
|
}
|
|
24843
25592
|
var flags = new Map();
|
|
25593
|
+
var pendingFirstTimeEvents = {};
|
|
25594
|
+
|
|
25595
|
+
// Process flags from response
|
|
24844
25596
|
_.each(responseFlags, function(data, key) {
|
|
24845
|
-
|
|
24846
|
-
|
|
24847
|
-
|
|
24848
|
-
|
|
24849
|
-
|
|
24850
|
-
|
|
25597
|
+
// Check if this flag has any activated first-time events this session
|
|
25598
|
+
var hasActivatedEvent = false;
|
|
25599
|
+
var prefix = key + ':';
|
|
25600
|
+
_.each(this.activatedFirstTimeEvents, function(activated, eventKey) {
|
|
25601
|
+
if (eventKey.startsWith(prefix)) {
|
|
25602
|
+
hasActivatedEvent = true;
|
|
25603
|
+
}
|
|
24851
25604
|
});
|
|
24852
|
-
|
|
25605
|
+
|
|
25606
|
+
if (hasActivatedEvent) {
|
|
25607
|
+
// Preserve the activated variant, don't overwrite with server's current variant
|
|
25608
|
+
var currentFlag = this.flags && this.flags.get(key);
|
|
25609
|
+
if (currentFlag) {
|
|
25610
|
+
flags.set(key, currentFlag);
|
|
25611
|
+
}
|
|
25612
|
+
} else {
|
|
25613
|
+
// Use server's current variant
|
|
25614
|
+
flags.set(key, {
|
|
25615
|
+
'key': data['variant_key'],
|
|
25616
|
+
'value': data['variant_value'],
|
|
25617
|
+
'experiment_id': data['experiment_id'],
|
|
25618
|
+
'is_experiment_active': data['is_experiment_active'],
|
|
25619
|
+
'is_qa_tester': data['is_qa_tester']
|
|
25620
|
+
});
|
|
25621
|
+
}
|
|
25622
|
+
}, this);
|
|
25623
|
+
|
|
25624
|
+
// Process top-level pending_first_time_events array
|
|
25625
|
+
var topLevelDefinitions = responseBody['pending_first_time_events'];
|
|
25626
|
+
if (topLevelDefinitions && topLevelDefinitions.length > 0) {
|
|
25627
|
+
_.each(topLevelDefinitions, function(def) {
|
|
25628
|
+
var flagKey = def['flag_key'];
|
|
25629
|
+
var eventKey = getPendingEventKey(flagKey, def['first_time_event_hash']);
|
|
25630
|
+
|
|
25631
|
+
// Skip if this specific event has already been activated this session
|
|
25632
|
+
if (this.activatedFirstTimeEvents[eventKey]) {
|
|
25633
|
+
return;
|
|
25634
|
+
}
|
|
25635
|
+
|
|
25636
|
+
// Store pending event definition using composite key
|
|
25637
|
+
pendingFirstTimeEvents[eventKey] = {
|
|
25638
|
+
'flag_key': flagKey,
|
|
25639
|
+
'flag_id': def['flag_id'],
|
|
25640
|
+
'project_id': def['project_id'],
|
|
25641
|
+
'first_time_event_hash': def['first_time_event_hash'],
|
|
25642
|
+
'event_name': def['event_name'],
|
|
25643
|
+
'property_filters': def['property_filters'],
|
|
25644
|
+
'pending_variant': def['pending_variant']
|
|
25645
|
+
};
|
|
25646
|
+
}, this);
|
|
25647
|
+
}
|
|
25648
|
+
|
|
25649
|
+
// Preserve any activated orphaned flags (flags that were activated but are no longer in response)
|
|
25650
|
+
if (this.activatedFirstTimeEvents) {
|
|
25651
|
+
_.each(this.activatedFirstTimeEvents, function(activated, eventKey) {
|
|
25652
|
+
var flagKey = getFlagKeyFromPendingEventKey(eventKey);
|
|
25653
|
+
if (activated && !flags.has(flagKey) && this.flags && this.flags.has(flagKey)) {
|
|
25654
|
+
// Keep the activated flag even though it's not in the new response
|
|
25655
|
+
flags.set(flagKey, this.flags.get(flagKey));
|
|
25656
|
+
}
|
|
25657
|
+
}, this);
|
|
25658
|
+
}
|
|
25659
|
+
|
|
24853
25660
|
this.flags = flags;
|
|
25661
|
+
this.pendingFirstTimeEvents = pendingFirstTimeEvents;
|
|
24854
25662
|
this._traceparent = traceparent;
|
|
25663
|
+
|
|
25664
|
+
this._loadTargetingIfNeeded();
|
|
24855
25665
|
}.bind(this)).catch(function(error) {
|
|
24856
25666
|
this.markFetchComplete();
|
|
24857
25667
|
logger.error(error);
|
|
@@ -24875,6 +25685,177 @@ FeatureFlagManager.prototype.markFetchComplete = function() {
|
|
|
24875
25685
|
this._fetchInProgressStartTime = null;
|
|
24876
25686
|
};
|
|
24877
25687
|
|
|
25688
|
+
/**
|
|
25689
|
+
* Proactively load targeting bundle if any pending events have property filters
|
|
25690
|
+
*/
|
|
25691
|
+
FeatureFlagManager.prototype._loadTargetingIfNeeded = function() {
|
|
25692
|
+
var hasPropertyFilters = false;
|
|
25693
|
+
_.each(this.pendingFirstTimeEvents, function(evt) {
|
|
25694
|
+
if (evt['property_filters'] && !_.isEmptyObject(evt['property_filters'])) {
|
|
25695
|
+
hasPropertyFilters = true;
|
|
25696
|
+
}
|
|
25697
|
+
});
|
|
25698
|
+
|
|
25699
|
+
if (hasPropertyFilters) {
|
|
25700
|
+
this.getTargeting().then(function() {
|
|
25701
|
+
logger.log('targeting loaded for property filter evaluation');
|
|
25702
|
+
});
|
|
25703
|
+
}
|
|
25704
|
+
};
|
|
25705
|
+
|
|
25706
|
+
/**
|
|
25707
|
+
* Get the targeting library (initializes if not already loaded)
|
|
25708
|
+
* This method is primarily for testing - production code should rely on automatic loading
|
|
25709
|
+
* @returns {Promise} Promise that resolves with targeting library
|
|
25710
|
+
*/
|
|
25711
|
+
FeatureFlagManager.prototype.getTargeting = function() {
|
|
25712
|
+
return getTargetingPromise(
|
|
25713
|
+
this.loadExtraBundle.bind(this),
|
|
25714
|
+
this.targetingSrc
|
|
25715
|
+
).catch(function(error) {
|
|
25716
|
+
logger.error('Failed to load targeting: ' + error);
|
|
25717
|
+
}.bind(this));
|
|
25718
|
+
};
|
|
25719
|
+
|
|
25720
|
+
/**
|
|
25721
|
+
* Check if a tracked event matches any pending first-time events and activate the corresponding flag variant
|
|
25722
|
+
* @param {string} eventName - The name of the event being tracked
|
|
25723
|
+
* @param {Object} properties - Event properties to evaluate against property filters
|
|
25724
|
+
*
|
|
25725
|
+
* When a match is found (event name matches and property filters pass), this method:
|
|
25726
|
+
* - Switches the flag to the pending variant
|
|
25727
|
+
* - Marks the event as activated for this session
|
|
25728
|
+
* - Records the activation via the API (fire-and-forget)
|
|
25729
|
+
*/
|
|
25730
|
+
FeatureFlagManager.prototype.checkFirstTimeEvents = function(eventName, properties) {
|
|
25731
|
+
if (!this.pendingFirstTimeEvents || _.isEmptyObject(this.pendingFirstTimeEvents)) {
|
|
25732
|
+
return;
|
|
25733
|
+
}
|
|
25734
|
+
|
|
25735
|
+
// Check if targeting promise exists (either bundled or async loaded)
|
|
25736
|
+
if (win[TARGETING_GLOBAL_NAME] && _.isFunction(win[TARGETING_GLOBAL_NAME].then)) {
|
|
25737
|
+
win[TARGETING_GLOBAL_NAME].then(function(library) {
|
|
25738
|
+
this._processFirstTimeEventCheck(eventName, properties, library);
|
|
25739
|
+
}.bind(this)).catch(function() {
|
|
25740
|
+
// If targeting failed to load, process with null
|
|
25741
|
+
// Events without property filters will still match
|
|
25742
|
+
this._processFirstTimeEventCheck(eventName, properties, null);
|
|
25743
|
+
}.bind(this));
|
|
25744
|
+
} else {
|
|
25745
|
+
// No targeting available, process with null
|
|
25746
|
+
// Events without property filters will still match
|
|
25747
|
+
this._processFirstTimeEventCheck(eventName, properties, null);
|
|
25748
|
+
}
|
|
25749
|
+
};
|
|
25750
|
+
|
|
25751
|
+
/**
|
|
25752
|
+
* Internal method to process first-time event checks with loaded targeting library
|
|
25753
|
+
* @param {string} eventName - The name of the event being tracked
|
|
25754
|
+
* @param {Object} properties - Event properties to evaluate against property filters
|
|
25755
|
+
* @param {Object} targeting - The loaded targeting library
|
|
25756
|
+
*/
|
|
25757
|
+
FeatureFlagManager.prototype._processFirstTimeEventCheck = function(eventName, properties, targeting) {
|
|
25758
|
+
_.each(this.pendingFirstTimeEvents, function(pendingEvent, eventKey) {
|
|
25759
|
+
if (this.activatedFirstTimeEvents[eventKey]) {
|
|
25760
|
+
return;
|
|
25761
|
+
}
|
|
25762
|
+
|
|
25763
|
+
var flagKey = pendingEvent['flag_key'];
|
|
25764
|
+
|
|
25765
|
+
// Use targeting module to check if event matches
|
|
25766
|
+
var matchResult;
|
|
25767
|
+
|
|
25768
|
+
// If no targeting library and event has property filters, skip it
|
|
25769
|
+
if (!targeting && pendingEvent['property_filters'] && !_.isEmptyObject(pendingEvent['property_filters'])) {
|
|
25770
|
+
logger.warn('Skipping event check for "' + flagKey + '" - property filters require targeting library');
|
|
25771
|
+
return;
|
|
25772
|
+
}
|
|
25773
|
+
|
|
25774
|
+
// For simple events (no property filters), just check event name
|
|
25775
|
+
if (!targeting) {
|
|
25776
|
+
matchResult = {
|
|
25777
|
+
matches: eventName === pendingEvent['event_name'],
|
|
25778
|
+
error: null
|
|
25779
|
+
};
|
|
25780
|
+
} else {
|
|
25781
|
+
var criteria = {
|
|
25782
|
+
'event_name': pendingEvent['event_name'],
|
|
25783
|
+
'property_filters': pendingEvent['property_filters']
|
|
25784
|
+
};
|
|
25785
|
+
matchResult = targeting['eventMatchesCriteria'](
|
|
25786
|
+
eventName,
|
|
25787
|
+
properties,
|
|
25788
|
+
criteria
|
|
25789
|
+
);
|
|
25790
|
+
}
|
|
25791
|
+
|
|
25792
|
+
if (matchResult.error) {
|
|
25793
|
+
logger.error('Error checking first-time event for flag "' + flagKey + '": ' + matchResult.error);
|
|
25794
|
+
return;
|
|
25795
|
+
}
|
|
25796
|
+
|
|
25797
|
+
if (!matchResult.matches) {
|
|
25798
|
+
return;
|
|
25799
|
+
}
|
|
25800
|
+
|
|
25801
|
+
logger.log('First-time event matched for flag "' + flagKey + '": ' + eventName);
|
|
25802
|
+
|
|
25803
|
+
var newVariant = {
|
|
25804
|
+
'key': pendingEvent['pending_variant']['variant_key'],
|
|
25805
|
+
'value': pendingEvent['pending_variant']['variant_value'],
|
|
25806
|
+
'experiment_id': pendingEvent['pending_variant']['experiment_id'],
|
|
25807
|
+
'is_experiment_active': pendingEvent['pending_variant']['is_experiment_active']
|
|
25808
|
+
};
|
|
25809
|
+
|
|
25810
|
+
this.flags.set(flagKey, newVariant);
|
|
25811
|
+
this.activatedFirstTimeEvents[eventKey] = true;
|
|
25812
|
+
|
|
25813
|
+
this.recordFirstTimeEvent(
|
|
25814
|
+
pendingEvent['flag_id'],
|
|
25815
|
+
pendingEvent['project_id'],
|
|
25816
|
+
pendingEvent['first_time_event_hash']
|
|
25817
|
+
);
|
|
25818
|
+
}, this);
|
|
25819
|
+
};
|
|
25820
|
+
|
|
25821
|
+
FeatureFlagManager.prototype.getFirstTimeEventApiRoute = function(flagId) {
|
|
25822
|
+
// Construct URL: {api_host}/flags/{flagId}/first-time-events
|
|
25823
|
+
return this.getFullApiRoute() + '/' + flagId + '/first-time-events';
|
|
25824
|
+
};
|
|
25825
|
+
|
|
25826
|
+
FeatureFlagManager.prototype.recordFirstTimeEvent = function(flagId, projectId, firstTimeEventHash) {
|
|
25827
|
+
var distinctId = this.getMpProperty('distinct_id');
|
|
25828
|
+
var traceparent = generateTraceparent();
|
|
25829
|
+
|
|
25830
|
+
// Build URL with query string parameters
|
|
25831
|
+
var searchParams = new URLSearchParams();
|
|
25832
|
+
searchParams.set('mp_lib', 'web');
|
|
25833
|
+
searchParams.set('$lib_version', Config.LIB_VERSION);
|
|
25834
|
+
var url = this.getFirstTimeEventApiRoute(flagId) + '?' + searchParams.toString();
|
|
25835
|
+
|
|
25836
|
+
var payload = {
|
|
25837
|
+
'distinct_id': distinctId,
|
|
25838
|
+
'project_id': projectId,
|
|
25839
|
+
'first_time_event_hash': firstTimeEventHash
|
|
25840
|
+
};
|
|
25841
|
+
|
|
25842
|
+
logger.log('Recording first-time event for flag: ' + flagId);
|
|
25843
|
+
|
|
25844
|
+
// Fire-and-forget POST request
|
|
25845
|
+
this.fetch.call(win, url, {
|
|
25846
|
+
'method': 'POST',
|
|
25847
|
+
'headers': {
|
|
25848
|
+
'Content-Type': 'application/json',
|
|
25849
|
+
'Authorization': 'Basic ' + btoa(this.getMpConfig('token') + ':'),
|
|
25850
|
+
'traceparent': traceparent
|
|
25851
|
+
},
|
|
25852
|
+
'body': JSON.stringify(payload)
|
|
25853
|
+
}).catch(function(error) {
|
|
25854
|
+
// Silent failure - cohort sync will catch up
|
|
25855
|
+
logger.error('Failed to record first-time event for flag ' + flagId + ': ' + error);
|
|
25856
|
+
});
|
|
25857
|
+
};
|
|
25858
|
+
|
|
24878
25859
|
FeatureFlagManager.prototype.getVariant = function(featureName, fallback) {
|
|
24879
25860
|
if (!this.fetchPromise) {
|
|
24880
25861
|
return new Promise(function(resolve) {
|
|
@@ -24993,6 +25974,9 @@ FeatureFlagManager.prototype['update_context'] = FeatureFlagManager.prototype.up
|
|
|
24993
25974
|
// Deprecated method
|
|
24994
25975
|
FeatureFlagManager.prototype['get_feature_data'] = FeatureFlagManager.prototype.getFeatureData;
|
|
24995
25976
|
|
|
25977
|
+
// Exports intended only for testing
|
|
25978
|
+
FeatureFlagManager.prototype['getTargeting'] = FeatureFlagManager.prototype.getTargeting;
|
|
25979
|
+
|
|
24996
25980
|
/* eslint camelcase: "off" */
|
|
24997
25981
|
|
|
24998
25982
|
|
|
@@ -26462,6 +27446,7 @@ var DEFAULT_CONFIG = {
|
|
|
26462
27446
|
'record_min_ms': 0,
|
|
26463
27447
|
'record_sessions_percent': 0,
|
|
26464
27448
|
'recorder_src': 'https://cdn.mxpnl.com/libs/mixpanel-recorder.min.js',
|
|
27449
|
+
'targeting_src': 'https://cdn.mxpnl.com/libs/mixpanel-targeting.min.js',
|
|
26465
27450
|
'remote_settings_mode': SETTING_DISABLED // 'strict', 'fallback', 'disabled'
|
|
26466
27451
|
};
|
|
26467
27452
|
|
|
@@ -26691,7 +27676,9 @@ MixpanelLib.prototype._init = function(token, config, name) {
|
|
|
26691
27676
|
getConfigFunc: _.bind(this.get_config, this),
|
|
26692
27677
|
setConfigFunc: _.bind(this.set_config, this),
|
|
26693
27678
|
getPropertyFunc: _.bind(this.get_property, this),
|
|
26694
|
-
trackingFunc: _.bind(this.track, this)
|
|
27679
|
+
trackingFunc: _.bind(this.track, this),
|
|
27680
|
+
loadExtraBundle: load_extra_bundle,
|
|
27681
|
+
targetingSrc: this.get_config('targeting_src')
|
|
26695
27682
|
});
|
|
26696
27683
|
this.flags.init();
|
|
26697
27684
|
this['flags'] = this.flags;
|
|
@@ -26787,11 +27774,11 @@ MixpanelLib.prototype._check_and_start_session_recording = addOptOutCheckMixpane
|
|
|
26787
27774
|
|
|
26788
27775
|
var loadRecorder = _.bind(function(startNewIfInactive) {
|
|
26789
27776
|
var handleLoadedRecorder = _.bind(function() {
|
|
26790
|
-
this._recorder = this._recorder || new win[
|
|
27777
|
+
this._recorder = this._recorder || new win[RECORDER_GLOBAL_NAME](this);
|
|
26791
27778
|
this._recorder['resumeRecording'](startNewIfInactive);
|
|
26792
27779
|
}, this);
|
|
26793
27780
|
|
|
26794
|
-
if (_.isUndefined(win[
|
|
27781
|
+
if (_.isUndefined(win[RECORDER_GLOBAL_NAME])) {
|
|
26795
27782
|
load_extra_bundle(this.get_config('recorder_src'), handleLoadedRecorder);
|
|
26796
27783
|
} else {
|
|
26797
27784
|
handleLoadedRecorder();
|
|
@@ -27533,6 +28520,11 @@ MixpanelLib.prototype.track = addOptOutCheckMixpanelLib(function(event_name, pro
|
|
|
27533
28520
|
send_request_options: options
|
|
27534
28521
|
}, callback);
|
|
27535
28522
|
|
|
28523
|
+
// Check for first-time event matches
|
|
28524
|
+
if (this.flags && this.flags.checkFirstTimeEvents) {
|
|
28525
|
+
this.flags.checkFirstTimeEvents(event_name, properties);
|
|
28526
|
+
}
|
|
28527
|
+
|
|
27536
28528
|
return ret;
|
|
27537
28529
|
});
|
|
27538
28530
|
|