mixpanel-browser 2.75.0 → 2.77.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.
Files changed (62) hide show
  1. package/.claude/settings.local.json +14 -0
  2. package/.github/dependabot.yml +8 -0
  3. package/.github/workflows/integration-tests.yml +4 -4
  4. package/.github/workflows/unit-tests.yml +4 -4
  5. package/CHANGELOG.md +14 -0
  6. package/build.sh +10 -8
  7. package/dist/async-modules/mixpanel-recorder-DLKbUIEE.js +23669 -0
  8. package/dist/async-modules/mixpanel-recorder-wIWnMDLA.min.js +2 -0
  9. package/dist/async-modules/mixpanel-recorder-wIWnMDLA.min.js.map +1 -0
  10. package/dist/async-modules/mixpanel-targeting-CTcftSJC.min.js +2 -0
  11. package/dist/async-modules/mixpanel-targeting-CTcftSJC.min.js.map +1 -0
  12. package/dist/async-modules/mixpanel-targeting-CmVvUyFM.js +2520 -0
  13. package/dist/mixpanel-core.cjs.d.ts +70 -1
  14. package/dist/mixpanel-core.cjs.js +724 -426
  15. package/dist/mixpanel-recorder.js +791 -41
  16. package/dist/mixpanel-recorder.min.js +1 -1
  17. package/dist/mixpanel-recorder.min.js.map +1 -1
  18. package/dist/mixpanel-targeting.js +6 -62
  19. package/dist/mixpanel-targeting.min.js +1 -1
  20. package/dist/mixpanel-targeting.min.js.map +1 -1
  21. package/dist/mixpanel-with-async-modules.cjs.d.ts +70 -1
  22. package/dist/mixpanel-with-async-modules.cjs.js +724 -426
  23. package/dist/mixpanel-with-async-recorder.cjs.d.ts +70 -1
  24. package/dist/mixpanel-with-async-recorder.cjs.js +724 -426
  25. package/dist/mixpanel-with-recorder.d.ts +70 -1
  26. package/dist/mixpanel-with-recorder.js +1471 -450
  27. package/dist/mixpanel-with-recorder.min.d.ts +70 -1
  28. package/dist/mixpanel-with-recorder.min.js +1 -1
  29. package/dist/mixpanel.amd.d.ts +70 -1
  30. package/dist/mixpanel.amd.js +1473 -504
  31. package/dist/mixpanel.cjs.d.ts +70 -1
  32. package/dist/mixpanel.cjs.js +1473 -504
  33. package/dist/mixpanel.globals.js +724 -426
  34. package/dist/mixpanel.min.js +189 -182
  35. package/dist/mixpanel.module.d.ts +70 -1
  36. package/dist/mixpanel.module.js +1473 -504
  37. package/dist/mixpanel.umd.d.ts +70 -1
  38. package/dist/mixpanel.umd.js +1473 -504
  39. package/dist/rrweb-bundled.js +61 -9
  40. package/dist/rrweb-compiled.js +56 -9
  41. package/logo.svg +5 -0
  42. package/package.json +6 -4
  43. package/rollup.config.mjs +163 -46
  44. package/src/autocapture/index.js +10 -27
  45. package/src/config.js +9 -3
  46. package/src/flags/index.js +1 -2
  47. package/src/index.d.ts +70 -1
  48. package/src/mixpanel-core.js +77 -112
  49. package/src/recorder/index.js +1 -1
  50. package/src/recorder/recorder.js +5 -1
  51. package/src/recorder/rrweb-network-plugin.js +649 -0
  52. package/src/recorder/session-recording.js +36 -12
  53. package/src/recorder/utils.js +27 -1
  54. package/src/recorder-manager.js +324 -0
  55. package/src/request-batcher.js +1 -1
  56. package/src/targeting/event-matcher.js +2 -57
  57. package/src/targeting/index.js +1 -1
  58. package/src/targeting/loader.js +1 -1
  59. package/src/utils.js +13 -1
  60. package/testServer.js +69 -1
  61. package/src/globals.js +0 -14
  62. /package/src/loaders/{loader-module-with-async-recorder.d.ts → loader-module-with-async-modules.d.ts} +0 -0
@@ -9304,14 +9304,7 @@ class MutationBuffer {
9304
9304
  };
9305
9305
  while (this.mapRemoves.length) {
9306
9306
  const removedNode = this.mapRemoves.shift();
9307
- if (removedNode.nodeName === "IFRAME") {
9308
- try {
9309
- this.iframeManager.removeIframe(removedNode);
9310
- } catch (e2) {
9311
- }
9312
- } else {
9313
- this.stylesheetManager.cleanupStylesheetsForRemovedNode(removedNode);
9314
- }
9307
+ this.cleanupRemovedNode(removedNode);
9315
9308
  this.mirror.removeNodeFromMap(removedNode);
9316
9309
  }
9317
9310
  for (const n2 of this.movedSet) {
@@ -9620,6 +9613,22 @@ class MutationBuffer {
9620
9613
  }
9621
9614
  }
9622
9615
  });
9616
+ __publicField$1(this, "cleanupRemovedNode", (node2) => {
9617
+ if (node2.nodeName === "IFRAME") {
9618
+ try {
9619
+ this.iframeManager.removeIframe(node2);
9620
+ } catch (e2) {
9621
+ }
9622
+ } else {
9623
+ try {
9624
+ this.stylesheetManager.cleanupStylesheetsForRemovedNode(node2);
9625
+ } catch (e2) {
9626
+ }
9627
+ }
9628
+ node2.childNodes.forEach((child) => {
9629
+ this.cleanupRemovedNode(child);
9630
+ });
9631
+ });
9623
9632
  }
9624
9633
  init(options) {
9625
9634
  [
@@ -11844,6 +11853,35 @@ class ProcessedNodeManager {
11844
11853
  destroy() {
11845
11854
  }
11846
11855
  }
11856
+ function toOrigin(url) {
11857
+ try {
11858
+ const origin = new URL(url).origin;
11859
+ return origin !== "null" ? origin : null;
11860
+ } catch {
11861
+ return null;
11862
+ }
11863
+ }
11864
+ function buildAllowedOriginSet(origins) {
11865
+ if (!Array.isArray(origins) || origins.length === 0) {
11866
+ throw new Error(
11867
+ "[rrweb] allowedIframeOrigins must be a non-empty array of origin strings."
11868
+ );
11869
+ }
11870
+ const set = /* @__PURE__ */ new Set();
11871
+ for (let i2 = 0; i2 < origins.length; i2++) {
11872
+ const entry = origins[i2];
11873
+ if (typeof entry !== "string") {
11874
+ throw new Error(
11875
+ `[rrweb] allowedIframeOrigins[${i2}] must be a string, got ${typeof entry}.`
11876
+ );
11877
+ }
11878
+ const origin = toOrigin(entry);
11879
+ if (origin) {
11880
+ set.add(origin);
11881
+ }
11882
+ }
11883
+ return Object.freeze(set);
11884
+ }
11847
11885
  let wrappedEmit;
11848
11886
  let takeFullSnapshot$1;
11849
11887
  let canvasManager;
@@ -11884,6 +11922,7 @@ function record(options = {}) {
11884
11922
  recordDOM = true,
11885
11923
  recordCanvas = false,
11886
11924
  recordCrossOriginIframes = false,
11925
+ allowedIframeOrigins,
11887
11926
  recordAfter = options.recordAfter === "DOMContentLoaded" ? options.recordAfter : "load",
11888
11927
  userTriggeredOnInput = false,
11889
11928
  collectFonts = false,
@@ -11894,6 +11933,13 @@ function record(options = {}) {
11894
11933
  errorHandler: errorHandler2
11895
11934
  } = options;
11896
11935
  registerErrorHandler(errorHandler2);
11936
+ let validatedOrigins;
11937
+ if (recordCrossOriginIframes && allowedIframeOrigins && allowedIframeOrigins.length > 0) {
11938
+ validatedOrigins = buildAllowedOriginSet(allowedIframeOrigins);
11939
+ if (validatedOrigins.size === 0) {
11940
+ validatedOrigins = void 0;
11941
+ }
11942
+ }
11897
11943
  const inEmittingFrame = recordCrossOriginIframes ? window.parent === window : true;
11898
11944
  let passEmitsToParent = false;
11899
11945
  if (!inEmittingFrame) {
@@ -11981,7 +12027,13 @@ function record(options = {}) {
11981
12027
  origin: window.location.origin,
11982
12028
  isCheckout
11983
12029
  };
11984
- window.parent.postMessage(message, "*");
12030
+ if (validatedOrigins) {
12031
+ for (const targetOrigin of validatedOrigins) {
12032
+ window.parent.postMessage(message, targetOrigin);
12033
+ }
12034
+ } else {
12035
+ window.parent.postMessage(message, "*");
12036
+ }
11985
12037
  }
11986
12038
  if (e2.type === EventType.FullSnapshot) {
11987
12039
  lastFullSnapshotEvent = e2;
@@ -10695,13 +10695,7 @@ var MutationBuffer = /*#__PURE__*/ function() {
10695
10695
  };
10696
10696
  while(_this.mapRemoves.length){
10697
10697
  var removedNode = _this.mapRemoves.shift();
10698
- if (removedNode.nodeName === "IFRAME") {
10699
- try {
10700
- _this.iframeManager.removeIframe(removedNode);
10701
- } catch (e2) {}
10702
- } else {
10703
- _this.stylesheetManager.cleanupStylesheetsForRemovedNode(removedNode);
10704
- }
10698
+ _this.cleanupRemovedNode(removedNode);
10705
10699
  _this.mirror.removeNodeFromMap(removedNode);
10706
10700
  }
10707
10701
  for(var _iterator = _create_for_of_iterator_helper_loose(_this.movedSet), _step; !(_step = _iterator()).done;){
@@ -11021,6 +11015,20 @@ var MutationBuffer = /*#__PURE__*/ function() {
11021
11015
  }
11022
11016
  }
11023
11017
  });
11018
+ __publicField$1(this, "cleanupRemovedNode", function(node2) {
11019
+ if (node2.nodeName === "IFRAME") {
11020
+ try {
11021
+ _this.iframeManager.removeIframe(node2);
11022
+ } catch (e2) {}
11023
+ } else {
11024
+ try {
11025
+ _this.stylesheetManager.cleanupStylesheetsForRemovedNode(node2);
11026
+ } catch (e2) {}
11027
+ }
11028
+ node2.childNodes.forEach(function(child) {
11029
+ _this.cleanupRemovedNode(child);
11030
+ });
11031
+ });
11024
11032
  }
11025
11033
  var _proto = MutationBuffer.prototype;
11026
11034
  _proto.init = function init(options) {
@@ -13248,6 +13256,31 @@ var ProcessedNodeManager = /*#__PURE__*/ function() {
13248
13256
  _proto.destroy = function destroy() {};
13249
13257
  return ProcessedNodeManager;
13250
13258
  }();
13259
+ function toOrigin(url) {
13260
+ try {
13261
+ var origin = new URL(url).origin;
13262
+ return origin !== "null" ? origin : null;
13263
+ } catch (e) {
13264
+ return null;
13265
+ }
13266
+ }
13267
+ function buildAllowedOriginSet(origins) {
13268
+ if (!Array.isArray(origins) || origins.length === 0) {
13269
+ throw new Error("[rrweb] allowedIframeOrigins must be a non-empty array of origin strings.");
13270
+ }
13271
+ var set = /* @__PURE__ */ new Set();
13272
+ for(var i2 = 0; i2 < origins.length; i2++){
13273
+ var entry = origins[i2];
13274
+ if (typeof entry !== "string") {
13275
+ throw new Error("[rrweb] allowedIframeOrigins[" + i2 + "] must be a string, got " + (typeof entry === "undefined" ? "undefined" : _type_of(entry)) + ".");
13276
+ }
13277
+ var origin = toOrigin(entry);
13278
+ if (origin) {
13279
+ set.add(origin);
13280
+ }
13281
+ }
13282
+ return Object.freeze(set);
13283
+ }
13251
13284
  var wrappedEmit;
13252
13285
  var takeFullSnapshot$1;
13253
13286
  var canvasManager;
@@ -13269,10 +13302,17 @@ try {
13269
13302
  var mirror = createMirror$2();
13270
13303
  function record(options) {
13271
13304
  if (options === void 0) options = {};
13272
- var emit = options.emit, checkoutEveryNms = options.checkoutEveryNms, checkoutEveryNth = options.checkoutEveryNth, _options_blockClass = options.blockClass, blockClass = _options_blockClass === void 0 ? "rr-block" : _options_blockClass, _options_blockSelector = options.blockSelector, blockSelector = _options_blockSelector === void 0 ? null : _options_blockSelector, _options_ignoreClass = options.ignoreClass, ignoreClass = _options_ignoreClass === void 0 ? "rr-ignore" : _options_ignoreClass, _options_ignoreSelector = options.ignoreSelector, ignoreSelector = _options_ignoreSelector === void 0 ? null : _options_ignoreSelector, _options_maskTextClass = options.maskTextClass, maskTextClass = _options_maskTextClass === void 0 ? "rr-mask" : _options_maskTextClass, _options_maskTextSelector = options.maskTextSelector, maskTextSelector = _options_maskTextSelector === void 0 ? null : _options_maskTextSelector, _options_inlineStylesheet = options.inlineStylesheet, inlineStylesheet = _options_inlineStylesheet === void 0 ? true : _options_inlineStylesheet, maskAllInputs = options.maskAllInputs, _maskInputOptions = options.maskInputOptions, _slimDOMOptions = options.slimDOMOptions, maskInputFn = options.maskInputFn, maskTextFn = options.maskTextFn, hooks = options.hooks, packFn = options.packFn, _options_sampling = options.sampling, sampling = _options_sampling === void 0 ? {} : _options_sampling, _options_dataURLOptions = options.dataURLOptions, dataURLOptions = _options_dataURLOptions === void 0 ? {} : _options_dataURLOptions, mousemoveWait = options.mousemoveWait, _options_recordDOM = options.recordDOM, recordDOM = _options_recordDOM === void 0 ? true : _options_recordDOM, _options_recordCanvas = options.recordCanvas, recordCanvas = _options_recordCanvas === void 0 ? false : _options_recordCanvas, _options_recordCrossOriginIframes = options.recordCrossOriginIframes, recordCrossOriginIframes = _options_recordCrossOriginIframes === void 0 ? false : _options_recordCrossOriginIframes, _options_recordAfter = options.recordAfter, recordAfter = _options_recordAfter === void 0 ? options.recordAfter === "DOMContentLoaded" ? options.recordAfter : "load" : _options_recordAfter, _options_userTriggeredOnInput = options.userTriggeredOnInput, userTriggeredOnInput = _options_userTriggeredOnInput === void 0 ? false : _options_userTriggeredOnInput, _options_collectFonts = options.collectFonts, collectFonts = _options_collectFonts === void 0 ? false : _options_collectFonts, _options_inlineImages = options.inlineImages, inlineImages = _options_inlineImages === void 0 ? false : _options_inlineImages, plugins = options.plugins, _options_keepIframeSrcFn = options.keepIframeSrcFn, keepIframeSrcFn = _options_keepIframeSrcFn === void 0 ? function() {
13305
+ var emit = options.emit, checkoutEveryNms = options.checkoutEveryNms, checkoutEveryNth = options.checkoutEveryNth, _options_blockClass = options.blockClass, blockClass = _options_blockClass === void 0 ? "rr-block" : _options_blockClass, _options_blockSelector = options.blockSelector, blockSelector = _options_blockSelector === void 0 ? null : _options_blockSelector, _options_ignoreClass = options.ignoreClass, ignoreClass = _options_ignoreClass === void 0 ? "rr-ignore" : _options_ignoreClass, _options_ignoreSelector = options.ignoreSelector, ignoreSelector = _options_ignoreSelector === void 0 ? null : _options_ignoreSelector, _options_maskTextClass = options.maskTextClass, maskTextClass = _options_maskTextClass === void 0 ? "rr-mask" : _options_maskTextClass, _options_maskTextSelector = options.maskTextSelector, maskTextSelector = _options_maskTextSelector === void 0 ? null : _options_maskTextSelector, _options_inlineStylesheet = options.inlineStylesheet, inlineStylesheet = _options_inlineStylesheet === void 0 ? true : _options_inlineStylesheet, maskAllInputs = options.maskAllInputs, _maskInputOptions = options.maskInputOptions, _slimDOMOptions = options.slimDOMOptions, maskInputFn = options.maskInputFn, maskTextFn = options.maskTextFn, hooks = options.hooks, packFn = options.packFn, _options_sampling = options.sampling, sampling = _options_sampling === void 0 ? {} : _options_sampling, _options_dataURLOptions = options.dataURLOptions, dataURLOptions = _options_dataURLOptions === void 0 ? {} : _options_dataURLOptions, mousemoveWait = options.mousemoveWait, _options_recordDOM = options.recordDOM, recordDOM = _options_recordDOM === void 0 ? true : _options_recordDOM, _options_recordCanvas = options.recordCanvas, recordCanvas = _options_recordCanvas === void 0 ? false : _options_recordCanvas, _options_recordCrossOriginIframes = options.recordCrossOriginIframes, recordCrossOriginIframes = _options_recordCrossOriginIframes === void 0 ? false : _options_recordCrossOriginIframes, allowedIframeOrigins = options.allowedIframeOrigins, _options_recordAfter = options.recordAfter, recordAfter = _options_recordAfter === void 0 ? options.recordAfter === "DOMContentLoaded" ? options.recordAfter : "load" : _options_recordAfter, _options_userTriggeredOnInput = options.userTriggeredOnInput, userTriggeredOnInput = _options_userTriggeredOnInput === void 0 ? false : _options_userTriggeredOnInput, _options_collectFonts = options.collectFonts, collectFonts = _options_collectFonts === void 0 ? false : _options_collectFonts, _options_inlineImages = options.inlineImages, inlineImages = _options_inlineImages === void 0 ? false : _options_inlineImages, plugins = options.plugins, _options_keepIframeSrcFn = options.keepIframeSrcFn, keepIframeSrcFn = _options_keepIframeSrcFn === void 0 ? function() {
13273
13306
  return false;
13274
13307
  } : _options_keepIframeSrcFn, _options_ignoreCSSAttributes = options.ignoreCSSAttributes, ignoreCSSAttributes = _options_ignoreCSSAttributes === void 0 ? /* @__PURE__ */ new Set([]) : _options_ignoreCSSAttributes, errorHandler2 = options.errorHandler;
13275
13308
  registerErrorHandler(errorHandler2);
13309
+ var validatedOrigins;
13310
+ if (recordCrossOriginIframes && allowedIframeOrigins && allowedIframeOrigins.length > 0) {
13311
+ validatedOrigins = buildAllowedOriginSet(allowedIframeOrigins);
13312
+ if (validatedOrigins.size === 0) {
13313
+ validatedOrigins = void 0;
13314
+ }
13315
+ }
13276
13316
  var inEmittingFrame = recordCrossOriginIframes ? window.parent === window : true;
13277
13317
  var passEmitsToParent = false;
13278
13318
  if (!inEmittingFrame) {
@@ -13364,7 +13404,14 @@ function record(options) {
13364
13404
  origin: window.location.origin,
13365
13405
  isCheckout: isCheckout
13366
13406
  };
13367
- window.parent.postMessage(message, "*");
13407
+ if (validatedOrigins) {
13408
+ for(var _iterator = _create_for_of_iterator_helper_loose(validatedOrigins), _step; !(_step = _iterator()).done;){
13409
+ var targetOrigin = _step.value;
13410
+ window.parent.postMessage(message, targetOrigin);
13411
+ }
13412
+ } else {
13413
+ window.parent.postMessage(message, "*");
13414
+ }
13368
13415
  }
13369
13416
  if (e2.type === EventType.FullSnapshot) {
13370
13417
  lastFullSnapshotEvent = e2;
package/logo.svg ADDED
@@ -0,0 +1,5 @@
1
+ <svg viewBox="0 0 798 189" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path
3
+ d="M0 146.539H47.835V141.455H44.243C37.2513 141.455 35.5676 139.547 35.5676 132.572V69.5024C40.4425 61.244 47.0011 55.9521 55.2596 55.9521C65.6348 55.9521 71.985 63.5692 71.985 77.9694V132.588C71.985 139.579 70.2852 141.471 63.5181 141.471H59.7016V146.555H106.478V141.471H103.095C96.103 141.471 94.4192 139.563 94.4192 132.588V75.6282C94.4192 73.7199 94.4192 71.8116 94.2108 69.9194C98.8612 61.4524 105.853 55.9521 114.111 55.9521C124.487 55.9521 130.837 63.5692 130.837 77.9694V132.588C130.837 139.579 129.137 141.471 122.37 141.471H118.553V146.555H165.33V141.471H162.155C154.955 141.471 153.271 139.563 153.271 132.588V75.6282C153.271 54.2523 141.212 42.4018 123.637 42.4018C110.519 42.4018 99.0857 49.6019 92.9439 63.9861C89.3519 50.0189 78.7682 42.4018 64.7849 42.4018C52.293 42.4018 41.5008 49.169 35.5676 62.7193V44.9355H0V50.0189H4.23347C11.4336 50.0189 13.1173 51.9271 13.1173 58.9027V132.572C13.1173 139.563 11.4175 141.455 4.23347 141.455H0V146.539ZM193.473 28.0016C201.09 28.0016 207.44 21.6514 207.44 14.0344C207.44 6.41731 201.09 0.0671082 193.473 0.0671082C185.856 0.0671082 179.506 6.41731 179.506 14.0344C179.506 21.6514 185.856 28.0016 193.473 28.0016ZM169.772 146.539H216.757V141.455H213.582C206.59 141.455 204.906 139.547 204.906 132.572V44.9355H169.339V50.0189H173.572C180.772 50.0189 182.456 51.9271 182.456 58.9027V132.572C182.456 139.563 180.756 141.455 173.572 141.455H169.756V146.539H169.772ZM251.058 86.8533H264.608C261.224 84.7365 259.958 81.7699 258.258 76.2696L253.174 57.4274C250.849 48.9605 248.941 44.9355 239.624 44.9355H220.157V50.0189H222.915C228.623 50.0189 229.265 52.1356 230.965 58.4858L235.407 75.0028C237.732 83.0528 241.34 86.8533 251.074 86.8533H251.058ZM282.392 86.8533H295.942C305.676 86.8533 309.059 83.0367 311.401 75.0028L315.843 58.4858C317.542 52.1356 318.376 50.0189 323.893 50.0189H326.651V44.9355H307.392C297.866 44.9355 295.958 48.752 293.841 57.4274L288.758 76.2696C287.058 81.9784 285.775 84.7365 282.392 86.8533ZM264.608 104.637H282.392V86.8533H264.608V104.637ZM220.157 146.555H239.624C248.941 146.555 250.849 142.53 253.174 134.063L258.258 115.221C259.958 109.72 261.224 106.754 264.608 104.637H251.058C241.324 104.637 237.716 108.454 235.391 116.488L230.949 133.005C229.249 139.355 228.623 141.471 222.899 141.471H220.141V146.555H220.157ZM307.36 146.555H326.619V141.471H323.861C318.36 141.471 317.51 139.355 315.811 133.005L311.369 116.488C309.043 108.438 305.66 104.637 295.91 104.637H282.392C285.775 106.754 287.01 109.512 288.71 115.221L293.793 134.063C295.91 142.738 297.818 146.555 307.344 146.555H307.36ZM329.377 188.89H378.479V183.806H373.395C366.628 183.806 364.928 181.898 364.928 174.922V134.496C371.07 143.604 381.445 149.105 393.713 149.105C416.788 149.105 435.198 128.787 435.198 93.6525C435.198 61.9014 417.847 42.4339 395.397 42.4339C382.279 42.4339 371.263 50.0509 364.912 63.6012V44.9676H329.345V50.0509H333.578C340.57 50.0509 342.462 51.9592 342.462 58.9348V174.906C342.462 181.898 340.554 183.79 333.578 183.79H329.345V188.874L329.377 188.89ZM387.379 55.5352C401.137 55.5352 412.363 68.4441 412.363 94.0534C412.363 121.988 401.987 137.238 387.812 137.238C378.703 137.238 370.669 132.363 364.944 123.896V71.3947C370.445 61.0195 378.286 55.5192 387.379 55.5192V55.5352ZM468.665 148.672C482.423 148.672 493.424 142.113 501.682 127.296V129.621C501.682 142.321 509.732 147.822 523.058 147.822C527.083 147.822 532.167 147.405 535.55 146.346V141.055C533.85 141.471 532.583 141.696 531.317 141.696C526.025 141.696 524.325 138.521 524.325 133.437V79.2362C524.325 54.2523 510.358 42.4018 486.865 42.4018C471.198 42.4018 455.964 49.6019 446.23 57.219L449.614 63.1522C459.781 56.1606 469.723 52.1516 480.515 52.1516C494.482 52.1516 501.682 59.7687 501.682 77.5525V82.2029L475.64 92.3696C451.939 102.103 443.248 111.212 443.248 124.971C443.248 138.729 452.981 148.672 468.648 148.672H468.665ZM465.489 119.454C465.489 108.871 471.631 101.253 484.123 95.7532L501.698 88.1361V121.154C495.14 130.262 487.523 135.554 479.264 135.554C471.006 135.554 465.505 129.845 465.505 119.47L465.489 119.454ZM539.784 146.555H588.052V141.471H584.027C577.035 141.471 575.351 139.563 575.351 132.588V69.7109C580.226 61.244 587.41 55.9521 595.877 55.9521C606.878 55.9521 613.869 63.5692 613.869 77.9694V132.588C613.869 139.579 612.17 141.471 605.402 141.471H601.377V146.555H649.421V141.471H645.187C637.987 141.471 636.304 139.563 636.304 132.588V75.6282C636.304 54.2523 623.603 42.4018 605.611 42.4018C592.494 42.4018 581.477 49.169 575.335 62.7193V44.9355H539.768V50.0189H544.001C551.201 50.0189 552.885 51.9271 552.885 58.9027V132.572C552.885 139.563 551.185 141.455 544.001 141.455H539.768V146.539L539.784 146.555ZM704.456 149.089C718.423 149.089 731.557 142.738 741.082 135.121L737.907 130.471C729.44 136.613 719.915 139.355 711.031 139.355C689.446 139.355 676.746 124.121 676.746 94.4863V90.6698H743.632C743.215 62.0939 725.639 42.6263 700.03 42.6263C674.421 42.6263 653.879 65.7019 653.879 96.1861C653.879 129.637 672.93 149.105 704.472 149.105L704.456 149.089ZM700.223 47.9021C712.923 47.9021 720.748 61.6609 722.031 84.945H677.163C679.071 61.4524 687.955 47.9021 700.239 47.9021H700.223ZM748.266 146.539H797.368V141.455H792.493C785.501 141.455 783.818 139.547 783.818 132.572V2.60078H748.25V7.68415H752.483C759.475 7.68415 761.367 9.59242 761.367 16.568V132.572C761.367 139.563 759.459 141.455 752.483 141.455H748.25V146.539H748.266Z"
4
+ fill="black" />
5
+ </svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mixpanel-browser",
3
- "version": "2.75.0",
3
+ "version": "2.77.0",
4
4
  "description": "The official Mixpanel JavaScript browser client library",
5
5
  "main": "dist/mixpanel.cjs.js",
6
6
  "module": "dist/mixpanel.module.js",
@@ -91,8 +91,10 @@
91
91
  "webpack": "1.12.2"
92
92
  },
93
93
  "dependencies": {
94
- "@mixpanel/rrweb": "2.0.0-alpha.18.3",
95
- "@mixpanel/rrweb-plugin-console-record": "2.0.0-alpha.18.3",
96
- "json-logic-js": "2.0.5"
94
+ "@mixpanel/rrweb": "2.0.0-alpha.18.4",
95
+ "@mixpanel/rrweb-plugin-console-record": "2.0.0-alpha.18.4",
96
+ "@mixpanel/rrweb-utils": "2.0.0-alpha.18.4",
97
+ "json-logic-js": "2.0.5",
98
+ "@types/json-logic-js": "2.0.5"
97
99
  }
98
100
  }
package/rollup.config.mjs CHANGED
@@ -8,21 +8,52 @@ import nodeResolve from '@rollup/plugin-node-resolve';
8
8
  import swc from '@rollup/plugin-swc';
9
9
  import browserTestBuilds from './tests/browser/client/rollup.config.mjs';
10
10
 
11
+ // Capture the hash of async-loaded bundles so we can inject the correct filenames into the main build
12
+ const asyncBundleManifest = {};
13
+
11
14
  const COMPILED_RRWEB_PATH = `build/rrweb-compiled.js`;
12
15
  const BUNDLED_RRWEB_PATH = `build/rrweb-bundled.js`;
13
16
 
17
+ const ASYNC_MODULE_BUILD_PATH = `build/async-modules`;
18
+ const RECORDER_BUNDLE_NAME = `mixpanel_recorder`;
19
+ const RECORDER_MIN_BUNDLE_NAME = `mixpanel_recorder_min`;
20
+ const TARGETING_BUNDLE_NAME = `mixpanel_targeting`;
21
+ const TARGETING_MIN_BUNDLE_NAME = `mixpanel_targeting_min`;
22
+
23
+
14
24
  // Delete output files at build start so build errors don't silently use stale files
25
+ let hasCleanedOnFull = false;
15
26
  const cleanOnRebuild = () => {
16
- let outputFiles = [];
27
+ let filesToClean = [];
17
28
  return {
18
29
  name: `clean-on-rebuild`,
19
30
  options(opts) {
20
31
  const outputs = Array.isArray(opts.output) ? opts.output : [opts.output];
21
- outputFiles = outputs.map(o => o && o.file).filter(Boolean);
32
+ filesToClean = outputs.flatMap(o => {
33
+ if (o.file) {
34
+ return [o.file];
35
+ }
36
+
37
+ if (o.dir && o.entryFileNames) {
38
+ if (!fs.existsSync(o.dir)) return [];
39
+
40
+ // match against regexes like mixpanel-recorder-[hash].js to find the actual output file(s) to clean up
41
+ const regex = new RegExp(`^` + o.entryFileNames.replace(/\./g, `\\.`).replace(`[hash]`, `.+`));
42
+ return fs.readdirSync(o.dir).filter(f => regex.test(f)).map(f => path.join(o.dir, f));
43
+ }
44
+ return [];
45
+ });
22
46
  },
23
47
  buildStart() {
24
- // Delete outputs right before this specific build runs
25
- for (const file of outputFiles) {
48
+ if (process.env.FULL && !hasCleanedOnFull) {
49
+ if (fs.existsSync(`build`)) {
50
+ fs.rmSync(`build`, {recursive: true});
51
+ }
52
+ hasCleanedOnFull = true;
53
+ return;
54
+ }
55
+
56
+ for (const file of filesToClean) {
26
57
  if (fs.existsSync(file)) {
27
58
  fs.unlinkSync(file);
28
59
  }
@@ -31,10 +62,53 @@ const cleanOnRebuild = () => {
31
62
  };
32
63
  };
33
64
 
34
- const withCleanOnRebuild = (builds) => builds.map(build => ({
35
- ...build,
36
- plugins: [cleanOnRebuild(), ...(build.plugins || [])]
37
- }));
65
+ // Capture the resolved (hashed) output filenames from recorder/targeting builds.
66
+ // Stores them in `asyncBundleManifest` so `injectAsyncBundleNames` can inject them into the main build.
67
+ const writeToManifest = () => ({
68
+ name: `write-to-manifest`,
69
+ generateBundle(options, bundle) {
70
+ for (const chunk of Object.values(bundle)) {
71
+ if (chunk.type === `chunk`) {
72
+ asyncBundleManifest[options.name] = chunk.fileName;
73
+ }
74
+ }
75
+ }
76
+ });
77
+
78
+ // Replace __MP_*__ placeholders in src/config.js with build-time values.
79
+ // For the main library build (which runs after), they will be the real hashed filenames.
80
+ const injectAsyncBundleNames = () => ({
81
+ name: `inject-async-bundle-names`,
82
+ renderChunk(code, _chunk, outputOptions) {
83
+ const isMinified = outputOptions.file && outputOptions.file.endsWith(`.min.js`);
84
+
85
+ let recorderBundleName = RECORDER_BUNDLE_NAME;
86
+ let targetingBundleName = TARGETING_BUNDLE_NAME;
87
+ if (isMinified) {
88
+ recorderBundleName = RECORDER_MIN_BUNDLE_NAME;
89
+ targetingBundleName = TARGETING_MIN_BUNDLE_NAME;
90
+ }
91
+
92
+ if (!asyncBundleManifest[recorderBundleName] || !asyncBundleManifest[targetingBundleName]) {
93
+ throw new Error(`Async bundle names not found in manifest. Manifest contents: ${JSON.stringify(asyncBundleManifest)}`);
94
+ }
95
+
96
+ const replaced = code
97
+ .replace(`__MP_RECORDER_FILENAME__`, asyncBundleManifest[recorderBundleName])
98
+ .replace(`__MP_TARGETING_FILENAME__`, asyncBundleManifest[targetingBundleName]);
99
+ return { code: replaced, map: null };
100
+ }
101
+ });
102
+
103
+ const withBasePlugins = (builds) => builds.map((build) => {
104
+ return {
105
+ ...build,
106
+ plugins: [
107
+ cleanOnRebuild(),
108
+ ...(build.plugins || [])
109
+ ]
110
+ };
111
+ });
38
112
 
39
113
  const aliasRrweb = () => alias({
40
114
  entries: [
@@ -69,44 +143,47 @@ const COMMON_CLOSURE_FLAGS = {
69
143
 
70
144
  const MINIFY = process.env.MINIFY || process.env.FULL;
71
145
 
72
- // Main builds used to develop / iterate quickly
73
- const MAIN_BUILDS = [
74
- {
75
- 'input': `src/recorder/rrweb-entrypoint.js`,
76
- 'output': [
77
- {
78
- file: BUNDLED_RRWEB_PATH,
79
- format: `es`,
80
- }
81
- ],
82
- plugins: [nodeResolve({browser: true})]
83
- },
84
- {
85
- 'input': BUNDLED_RRWEB_PATH,
86
- 'output': [
87
- {
88
- file: COMPILED_RRWEB_PATH,
89
- format: `es`,
90
- }
91
- ],
92
- plugins: [swc({swc: {jsc: {target: `es5`}}})]
93
- },
94
-
95
- // IIFE recorder bundle that is loaded asynchronously
96
- // rrweb uses esbuild to minify, so do that here as well
146
+ /**
147
+ * Async bundles for the recorder and targeting modules.
148
+ *
149
+ * These are loaded on-demand by the main library at runtime via script injection.
150
+ * Filenames include a content hash so the main build can reference an exact, immutable
151
+ * bundle — guaranteeing the async module matches what the library expects.
152
+ * The hash is captured in `asyncBundleManifest` and injected into the main build
153
+ * via `injectAsyncBundleNames`.
154
+ *
155
+ * We also produce non-hashed "legacy" versions of each bundle for backward compatibility
156
+ * with customers who proxy specific filenames from the Mixpanel CDN.
157
+ */
158
+ const ASYNC_BUNDLE_BUILDS = [
159
+ // Recorder bundle (wraps rrweb)
97
160
  {
98
161
  input: `src/recorder/index.js`,
99
162
  output: [
163
+ {
164
+ dir: ASYNC_MODULE_BUILD_PATH,
165
+ entryFileNames: `mixpanel-recorder-[hash].js`,
166
+ name: RECORDER_BUNDLE_NAME,
167
+ format: `iife`,
168
+ },
100
169
  {
101
170
  file: `build/mixpanel-recorder.js`,
102
- name: `mixpanel_recorder`,
171
+ name: `legacy_recorder_bundle`,
103
172
  format: `iife`,
104
173
  },
105
174
  ...(MINIFY
106
175
  ? [
176
+ {
177
+ dir: ASYNC_MODULE_BUILD_PATH,
178
+ entryFileNames: `mixpanel-recorder-[hash].min.js`,
179
+ name: RECORDER_MIN_BUNDLE_NAME,
180
+ format: `iife`,
181
+ plugins: [esbuild({target: `es5`, minify: true, sourceMap: true})],
182
+ sourcemap: true,
183
+ },
107
184
  {
108
185
  file: `build/mixpanel-recorder.min.js`,
109
- name: `mixpanel_recorder`,
186
+ name: `legacy_recorder_min_bundle`,
110
187
  format: `iife`,
111
188
  plugins: [esbuild({target: `es5`, minify: true, sourceMap: true})],
112
189
  sourcemap: true,
@@ -114,23 +191,37 @@ const MAIN_BUILDS = [
114
191
  ]
115
192
  : []),
116
193
  ],
117
- plugins: [aliasRrweb()],
194
+ plugins: [aliasRrweb(), writeToManifest()],
118
195
  },
119
196
 
120
- // IIFE targeting bundle that is loaded asynchronously
197
+ // Targeting bundle (feature flags / experiments)
121
198
  {
122
199
  input: `src/targeting/index.js`,
123
200
  output: [
201
+ {
202
+ dir: ASYNC_MODULE_BUILD_PATH,
203
+ entryFileNames: `mixpanel-targeting-[hash].js`,
204
+ name: TARGETING_BUNDLE_NAME,
205
+ format: `iife`,
206
+ },
124
207
  {
125
208
  file: `build/mixpanel-targeting.js`,
126
- name: `mixpanel_targeting`,
209
+ name: `legacy_targeting_bundle`,
127
210
  format: `iife`,
128
211
  },
129
212
  ...(MINIFY
130
213
  ? [
214
+ {
215
+ dir: ASYNC_MODULE_BUILD_PATH,
216
+ entryFileNames: `mixpanel-targeting-[hash].min.js`,
217
+ name: TARGETING_MIN_BUNDLE_NAME,
218
+ format: `iife`,
219
+ plugins: [esbuild({target: `es5`, minify: true, sourceMap: true})],
220
+ sourcemap: true,
221
+ },
131
222
  {
132
223
  file: `build/mixpanel-targeting.min.js`,
133
- name: `mixpanel_targeting`,
224
+ name: `legacy_targeting_min_bundle`,
134
225
  format: `iife`,
135
226
  plugins: [esbuild({target: `es5`, minify: true, sourceMap: true})],
136
227
  sourcemap: true,
@@ -138,9 +229,35 @@ const MAIN_BUILDS = [
138
229
  ]
139
230
  : []),
140
231
  ],
141
- plugins: [commonjs(), nodeResolve({browser: true})],
232
+ plugins: [commonjs(), nodeResolve({browser: true}), writeToManifest()],
233
+ },
234
+ ];
235
+
236
+ // Main builds used to develop / iterate quickly
237
+ const MAIN_BUILDS = [
238
+ {
239
+ 'input': `src/recorder/rrweb-entrypoint.js`,
240
+ 'output': [
241
+ {
242
+ file: BUNDLED_RRWEB_PATH,
243
+ format: `es`,
244
+ }
245
+ ],
246
+ plugins: [nodeResolve({browser: true})]
247
+ },
248
+ {
249
+ 'input': BUNDLED_RRWEB_PATH,
250
+ 'output': [
251
+ {
252
+ file: COMPILED_RRWEB_PATH,
253
+ format: `es`,
254
+ }
255
+ ],
256
+ plugins: [swc({swc: {jsc: {target: `es5`}}})]
142
257
  },
143
258
 
259
+ ...ASYNC_BUNDLE_BUILDS,
260
+
144
261
  // IIFE main mixpanel build
145
262
  {
146
263
  input: `src/loaders/loader-globals.js`,
@@ -161,6 +278,7 @@ const MAIN_BUILDS = [
161
278
  : []),
162
279
  ],
163
280
  plugins: [
281
+ injectAsyncBundleNames(),
164
282
  nodeResolve({
165
283
  browser: true,
166
284
  main: true,
@@ -228,7 +346,6 @@ const ALL_BUILDS = [
228
346
  ],
229
347
  },
230
348
 
231
-
232
349
  // Modules builds that are bundled with the recorder and targeting
233
350
  {
234
351
  input: `src/loaders/loader-module.js`,
@@ -266,7 +383,6 @@ const ALL_BUILDS = [
266
383
  ],
267
384
  },
268
385
 
269
-
270
386
  // Alternative CJS builds without recorder
271
387
  {
272
388
  input: `src/loaders/loader-module-core.js`,
@@ -303,6 +419,7 @@ const ALL_BUILDS = [
303
419
  },
304
420
  ],
305
421
  plugins: [
422
+ injectAsyncBundleNames(),
306
423
  aliasRrweb(),
307
424
  nodeResolve({
308
425
  browser: true,
@@ -312,9 +429,9 @@ const ALL_BUILDS = [
312
429
  copyTypes(),
313
430
  ],
314
431
  },
315
-
316
- ...browserTestBuilds,
317
432
  ];
318
433
 
319
- const builds = process.env.FULL ? ALL_BUILDS : MAIN_BUILDS;
320
- export default withCleanOnRebuild(builds);
434
+ const srcBuilds = process.env.FULL ? ALL_BUILDS : MAIN_BUILDS;
435
+ const builds = [...srcBuilds, ...browserTestBuilds];
436
+
437
+ export default withBasePlugins(builds);
@@ -1,4 +1,4 @@
1
- import { _, document, safewrap, safewrapClass } from '../utils';
1
+ import { _, document, safewrap, safewrapClass, urlMatchesRegexList } from '../utils';
2
2
  import { window } from '../window';
3
3
  import {
4
4
  getPolyfillScrollEndFunction, getPropsForDOMEvent, logger, minDOMApisSupported,
@@ -112,27 +112,15 @@ Autocapture.prototype.getConfig = function(key) {
112
112
  };
113
113
 
114
114
  Autocapture.prototype.currentUrlBlocked = function() {
115
- var i;
116
115
  var currentUrl = _.info.currentUrl();
117
116
 
118
117
  var allowUrlRegexes = this.getConfig(CONFIG_ALLOW_URL_REGEXES) || [];
119
118
  if (allowUrlRegexes.length) {
120
119
  // we're using an allowlist, only track if current URL matches
121
- var allowed = false;
122
- for (i = 0; i < allowUrlRegexes.length; i++) {
123
- var allowRegex = allowUrlRegexes[i];
124
- try {
125
- if (currentUrl.match(allowRegex)) {
126
- allowed = true;
127
- break;
128
- }
129
- } catch (err) {
130
- logger.critical('Error while checking block URL regex: ' + allowRegex, err);
131
- return true;
132
- }
133
- }
134
- if (!allowed) {
135
- // wasn't allowed by any regex
120
+ try {
121
+ return !urlMatchesRegexList(currentUrl, allowUrlRegexes);
122
+ } catch (err) {
123
+ logger.critical('Error while checking block URL regexes: ', err);
136
124
  return true;
137
125
  }
138
126
  }
@@ -142,17 +130,12 @@ Autocapture.prototype.currentUrlBlocked = function() {
142
130
  return false;
143
131
  }
144
132
 
145
- for (i = 0; i < blockUrlRegexes.length; i++) {
146
- try {
147
- if (currentUrl.match(blockUrlRegexes[i])) {
148
- return true;
149
- }
150
- } catch (err) {
151
- logger.critical('Error while checking block URL regex: ' + blockUrlRegexes[i], err);
152
- return true;
153
- }
133
+ try {
134
+ return urlMatchesRegexList(currentUrl, blockUrlRegexes);
135
+ } catch (err) {
136
+ logger.critical('Error while checking block URL regexes: ', err);
137
+ return true;
154
138
  }
155
- return false;
156
139
  };
157
140
 
158
141
  Autocapture.prototype.pageviewTrackingConfig = function() {
package/src/config.js CHANGED
@@ -1,6 +1,12 @@
1
- var Config = {
1
+ export var Config = {
2
2
  DEBUG: false,
3
- LIB_VERSION: '2.75.0'
3
+ LIB_VERSION: '2.77.0'
4
4
  };
5
5
 
6
- export default Config;
6
+ // Window global names for async modules
7
+ export var TARGETING_GLOBAL_NAME = '__mp_targeting';
8
+ export var RECORDER_GLOBAL_NAME = '__mp_recorder';
9
+
10
+ // Constants that are injected at build-time for the names of async modules.
11
+ export var RECORDER_FILENAME = '__MP_RECORDER_FILENAME__';
12
+ export var TARGETING_FILENAME = '__MP_TARGETING_FILENAME__';
@@ -1,10 +1,9 @@
1
1
  import { _, console_with_prefix, generateTraceparent, safewrapClass } from '../utils'; // eslint-disable-line camelcase
2
2
  import { window } from '../window';
3
- import Config from '../config';
3
+ import { Config, TARGETING_GLOBAL_NAME } from '../config';
4
4
  import {
5
5
  getTargetingPromise
6
6
  } from '../targeting/loader';
7
- import { TARGETING_GLOBAL_NAME } from '../globals';
8
7
 
9
8
  var logger = console_with_prefix('flags');
10
9
  var FLAGS_CONFIG_KEY = 'flags';