@leanbase-giangnd/js 0.1.2 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -230,7 +230,7 @@ core.PostHogPersistedProperty.Queue, core.PostHogPersistedProperty.FeatureFlagDe
230
230
 
231
231
  /* eslint-disable no-console */
232
232
  const PREFIX = '[Leanbase]';
233
- const logger$3 = {
233
+ const logger$2 = {
234
234
  info: (...args) => {
235
235
  if (typeof console !== 'undefined') {
236
236
  console.log(PREFIX, ...args);
@@ -528,7 +528,7 @@ function chooseCookieDomain(hostname, cross_subdomain) {
528
528
  if (!matchedSubDomain) {
529
529
  const originalMatch = originalCookieDomainFn(hostname);
530
530
  if (originalMatch !== matchedSubDomain) {
531
- logger$3.info('Warning: cookie subdomain discovery mismatch', originalMatch, matchedSubDomain);
531
+ logger$2.info('Warning: cookie subdomain discovery mismatch', originalMatch, matchedSubDomain);
532
532
  }
533
533
  matchedSubDomain = originalMatch;
534
534
  }
@@ -540,7 +540,7 @@ function chooseCookieDomain(hostname, cross_subdomain) {
540
540
  const cookieStore = {
541
541
  _is_supported: () => !!document,
542
542
  _error: function (msg) {
543
- logger$3.error('cookieStore error: ' + msg);
543
+ logger$2.error('cookieStore error: ' + msg);
544
544
  },
545
545
  _get: function (name) {
546
546
  if (!document) {
@@ -589,7 +589,7 @@ const cookieStore = {
589
589
  const new_cookie_val = name + '=' + encodeURIComponent(JSON.stringify(value)) + expires + '; SameSite=Lax; path=/' + cdomain + secure;
590
590
  // 4096 bytes is the size at which some browsers (e.g. firefox) will not store a cookie, warn slightly before that
591
591
  if (new_cookie_val.length > 4096 * 0.9) {
592
- logger$3.warn('cookieStore warning: large cookie, len=' + new_cookie_val.length);
592
+ logger$2.warn('cookieStore warning: large cookie, len=' + new_cookie_val.length);
593
593
  }
594
594
  document.cookie = new_cookie_val;
595
595
  return new_cookie_val;
@@ -631,13 +631,13 @@ const localStore = {
631
631
  supported = false;
632
632
  }
633
633
  if (!supported) {
634
- logger$3.error('localStorage unsupported; falling back to cookie store');
634
+ logger$2.error('localStorage unsupported; falling back to cookie store');
635
635
  }
636
636
  _localStorage_supported = supported;
637
637
  return supported;
638
638
  },
639
639
  _error: function (msg) {
640
- logger$3.error('localStorage error: ' + msg);
640
+ logger$2.error('localStorage error: ' + msg);
641
641
  },
642
642
  _get: function (name) {
643
643
  try {
@@ -723,7 +723,7 @@ const memoryStore = {
723
723
  return true;
724
724
  },
725
725
  _error: function (msg) {
726
- logger$3.error('memoryStorage error: ' + msg);
726
+ logger$2.error('memoryStorage error: ' + msg);
727
727
  },
728
728
  _get: function (name) {
729
729
  return memoryStorage[name] || null;
@@ -764,7 +764,7 @@ const sessionStore = {
764
764
  return sessionStorageSupported;
765
765
  },
766
766
  _error: function (msg) {
767
- logger$3.error('sessionStorage error: ', msg);
767
+ logger$2.error('sessionStorage error: ', msg);
768
768
  },
769
769
  _get: function (name) {
770
770
  try {
@@ -813,21 +813,6 @@ const convertToURL = url => {
813
813
  location.href = url;
814
814
  return location;
815
815
  };
816
- const formDataToQuery = function (formdata, arg_separator = '&') {
817
- let use_val;
818
- let use_key;
819
- const tph_arr = [];
820
- each(formdata, function (val, key) {
821
- // the key might be literally the string undefined for e.g. if {undefined: 'something'}
822
- if (core.isUndefined(val) || core.isUndefined(key) || key === 'undefined') {
823
- return;
824
- }
825
- use_val = encodeURIComponent(core.isFile(val) ? val.name : val.toString());
826
- use_key = encodeURIComponent(key);
827
- tph_arr[tph_arr.length] = use_key + '=' + use_val;
828
- });
829
- return tph_arr.join(arg_separator);
830
- };
831
816
  // NOTE: Once we get rid of IE11/op_mini we can start using URLSearchParams
832
817
  const getQueryParam = function (url, param) {
833
818
  const withoutHash = url.split('#')[0] || '';
@@ -851,7 +836,7 @@ const getQueryParam = function (url, param) {
851
836
  try {
852
837
  result = decodeURIComponent(result);
853
838
  } catch {
854
- logger$3.error('Skipping decoding for malformed query param: ' + result);
839
+ logger$2.error('Skipping decoding for malformed query param: ' + result);
855
840
  }
856
841
  return result.replace(/\+/g, ' ');
857
842
  }
@@ -1185,7 +1170,7 @@ const detectDeviceType = function (user_agent) {
1185
1170
  }
1186
1171
  };
1187
1172
 
1188
- var version = "0.1.2";
1173
+ var version = "0.1.5";
1189
1174
  var packageInfo = {
1190
1175
  version: version};
1191
1176
 
@@ -1450,7 +1435,7 @@ class LeanbasePersistence {
1450
1435
  this._storage = this._buildStorage(config);
1451
1436
  this.load();
1452
1437
  if (config.debug) {
1453
- logger$3.info('Persistence loaded', config['persistence'], {
1438
+ logger$2.info('Persistence loaded', config['persistence'], {
1454
1439
  ...this.props
1455
1440
  });
1456
1441
  }
@@ -1466,7 +1451,7 @@ class LeanbasePersistence {
1466
1451
  }
1467
1452
  _buildStorage(config) {
1468
1453
  if (CASE_INSENSITIVE_PERSISTENCE_TYPES.indexOf(config['persistence'].toLowerCase()) === -1) {
1469
- logger$3.info('Unknown persistence type ' + config['persistence'] + '; falling back to localStorage+cookie');
1454
+ logger$2.info('Unknown persistence type ' + config['persistence'] + '; falling back to localStorage+cookie');
1470
1455
  config['persistence'] = 'localStorage+cookie';
1471
1456
  }
1472
1457
  let store;
@@ -2102,7 +2087,7 @@ function getNestedSpanText(target) {
2102
2087
  text = `${text} ${getNestedSpanText(child)}`.trim();
2103
2088
  }
2104
2089
  } catch (e) {
2105
- logger$3.error('[AutoCapture]', e);
2090
+ logger$2.error('[AutoCapture]', e);
2106
2091
  }
2107
2092
  }
2108
2093
  });
@@ -2404,7 +2389,7 @@ class Autocapture {
2404
2389
  }
2405
2390
  _addDomEventHandlers() {
2406
2391
  if (!this.isBrowserSupported()) {
2407
- logger$3.info('Disabling Automatic Event Collection because this browser is not supported');
2392
+ logger$2.info('Disabling Automatic Event Collection because this browser is not supported');
2408
2393
  return;
2409
2394
  }
2410
2395
  if (!win || !document) {
@@ -2415,7 +2400,7 @@ class Autocapture {
2415
2400
  try {
2416
2401
  this._captureEvent(e);
2417
2402
  } catch (error) {
2418
- logger$3.error('Failed to capture event', error);
2403
+ logger$2.error('Failed to capture event', error);
2419
2404
  }
2420
2405
  };
2421
2406
  addEventListener(document, 'submit', handler, {
@@ -2600,7 +2585,7 @@ class SessionIdManager {
2600
2585
  this._windowIdGenerator = windowIdGenerator || uuidv7;
2601
2586
  const persistenceName = this._config['persistence_name'] || this._config['token'];
2602
2587
  const desiredTimeout = this._config['session_idle_timeout_seconds'] || DEFAULT_SESSION_IDLE_TIMEOUT_SECONDS;
2603
- this._sessionTimeoutMs = core.clampToRange(desiredTimeout, MIN_SESSION_IDLE_TIMEOUT_SECONDS, MAX_SESSION_IDLE_TIMEOUT_SECONDS, logger$3, DEFAULT_SESSION_IDLE_TIMEOUT_SECONDS) * 1000;
2588
+ this._sessionTimeoutMs = core.clampToRange(desiredTimeout, MIN_SESSION_IDLE_TIMEOUT_SECONDS, MAX_SESSION_IDLE_TIMEOUT_SECONDS, logger$2, DEFAULT_SESSION_IDLE_TIMEOUT_SECONDS) * 1000;
2604
2589
  instance.register({
2605
2590
  $configured_session_timeout_ms: this._sessionTimeoutMs
2606
2591
  });
@@ -2627,7 +2612,7 @@ class SessionIdManager {
2627
2612
  const sessionStartTimestamp = uuid7ToTimestampMs(this._config.bootstrap.sessionID);
2628
2613
  this._setSessionId(this._config.bootstrap.sessionID, new Date().getTime(), sessionStartTimestamp);
2629
2614
  } catch (e) {
2630
- logger$3.error('Invalid sessionID in bootstrap', e);
2615
+ logger$2.error('Invalid sessionID in bootstrap', e);
2631
2616
  }
2632
2617
  }
2633
2618
  this._listenToReloadWindow();
@@ -2768,7 +2753,7 @@ class SessionIdManager {
2768
2753
  if (noSessionId || activityTimeout || sessionPastMaximumLength) {
2769
2754
  sessionId = this._sessionIdGenerator();
2770
2755
  windowId = this._windowIdGenerator();
2771
- logger$3.info('new session ID generated', {
2756
+ logger$2.info('new session ID generated', {
2772
2757
  sessionId,
2773
2758
  windowId,
2774
2759
  changeReason: {
@@ -2957,10 +2942,10 @@ class PageViewManager {
2957
2942
  lastContentY = Math.ceil(lastContentY);
2958
2943
  maxContentY = Math.ceil(maxContentY);
2959
2944
  // if the maximum scroll height is near 0, then the percentage is 1
2960
- const lastScrollPercentage = maxScrollHeight <= 1 ? 1 : core.clampToRange(lastScrollY / maxScrollHeight, 0, 1, logger$3);
2961
- const maxScrollPercentage = maxScrollHeight <= 1 ? 1 : core.clampToRange(maxScrollY / maxScrollHeight, 0, 1, logger$3);
2962
- const lastContentPercentage = maxContentHeight <= 1 ? 1 : core.clampToRange(lastContentY / maxContentHeight, 0, 1, logger$3);
2963
- const maxContentPercentage = maxContentHeight <= 1 ? 1 : core.clampToRange(maxContentY / maxContentHeight, 0, 1, logger$3);
2945
+ const lastScrollPercentage = maxScrollHeight <= 1 ? 1 : core.clampToRange(lastScrollY / maxScrollHeight, 0, 1, logger$2);
2946
+ const maxScrollPercentage = maxScrollHeight <= 1 ? 1 : core.clampToRange(maxScrollY / maxScrollHeight, 0, 1, logger$2);
2947
+ const lastContentPercentage = maxContentHeight <= 1 ? 1 : core.clampToRange(lastContentY / maxContentHeight, 0, 1, logger$2);
2948
+ const maxContentPercentage = maxContentHeight <= 1 ? 1 : core.clampToRange(maxContentY / maxContentHeight, 0, 1, logger$2);
2964
2949
  properties = extend(properties, {
2965
2950
  $prev_pageview_last_scroll: lastScrollY,
2966
2951
  $prev_pageview_last_scroll_percentage: lastScrollPercentage,
@@ -3123,84 +3108,129 @@ const isLikelyBot = function (navigator, customBlockedUserAgents) {
3123
3108
  // It would be very bad if posthog-js caused a permission prompt to appear on every page load.
3124
3109
  };
3125
3110
 
3111
+ // Use a safe global target (prefer `win`, fallback to globalThis)
3112
+ const _target = win ?? globalThis;
3113
+ _target.__PosthogExtensions__ = _target.__PosthogExtensions__ || {};
3114
+ // Expose rrweb.record under the same contract
3115
+ _target.__PosthogExtensions__.rrweb = _target.__PosthogExtensions__.rrweb || {
3116
+ record: record.record
3117
+ };
3118
+ // Provide initSessionRecording if not present — return a new LazyLoadedSessionRecording when called
3119
+ _target.__PosthogExtensions__.initSessionRecording = _target.__PosthogExtensions__.initSessionRecording || (instance => {
3120
+ const factory = _target.__PosthogExtensions__._initSessionRecordingFactory;
3121
+ if (factory) {
3122
+ return factory(instance);
3123
+ }
3124
+ // If no factory is registered yet, return undefined — callers should handle lazy-loading.
3125
+ return undefined;
3126
+ });
3127
+ // Provide a no-op loadExternalDependency that calls the callback immediately (since rrweb is bundled)
3128
+ _target.__PosthogExtensions__.loadExternalDependency = _target.__PosthogExtensions__.loadExternalDependency || ((instance, scriptName, cb) => {
3129
+ if (cb) cb(undefined);
3130
+ });
3131
+ // Provide rrwebPlugins object with network plugin factory if not present
3132
+ _target.__PosthogExtensions__.rrwebPlugins = _target.__PosthogExtensions__.rrwebPlugins || {};
3133
+ // Default to undefined; the lazy-loaded recorder will register the real factory when it initializes.
3134
+ _target.__PosthogExtensions__.rrwebPlugins.getRecordNetworkPlugin = _target.__PosthogExtensions__.rrwebPlugins.getRecordNetworkPlugin || (() => undefined);
3135
+
3136
+ // Type definitions copied from @rrweb/types@2.0.0-alpha.17 and rrweb-snapshot@2.0.0-alpha.17
3137
+ // Both packages are MIT licensed: https://github.com/rrweb-io/rrweb
3138
+ //
3139
+ // These types are copied here to avoid requiring users to install peer dependencies
3140
+ // solely for TypeScript type information.
3141
+ //
3142
+ // Original sources:
3143
+ // - @rrweb/types: https://github.com/rrweb-io/rrweb/tree/main/packages/@rrweb/types
3144
+ // - rrweb-snapshot: https://github.com/rrweb-io/rrweb/tree/main/packages/rrweb-snapshot
3145
+ var NodeType;
3146
+ (function (NodeType) {
3147
+ NodeType[NodeType["Document"] = 0] = "Document";
3148
+ NodeType[NodeType["DocumentType"] = 1] = "DocumentType";
3149
+ NodeType[NodeType["Element"] = 2] = "Element";
3150
+ NodeType[NodeType["Text"] = 3] = "Text";
3151
+ NodeType[NodeType["CDATA"] = 4] = "CDATA";
3152
+ NodeType[NodeType["Comment"] = 5] = "Comment";
3153
+ })(NodeType || (NodeType = {}));
3154
+ var EventType;
3155
+ (function (EventType) {
3156
+ EventType[EventType["DomContentLoaded"] = 0] = "DomContentLoaded";
3157
+ EventType[EventType["Load"] = 1] = "Load";
3158
+ EventType[EventType["FullSnapshot"] = 2] = "FullSnapshot";
3159
+ EventType[EventType["IncrementalSnapshot"] = 3] = "IncrementalSnapshot";
3160
+ EventType[EventType["Meta"] = 4] = "Meta";
3161
+ EventType[EventType["Custom"] = 5] = "Custom";
3162
+ EventType[EventType["Plugin"] = 6] = "Plugin";
3163
+ })(EventType || (EventType = {}));
3164
+ var IncrementalSource;
3165
+ (function (IncrementalSource) {
3166
+ IncrementalSource[IncrementalSource["Mutation"] = 0] = "Mutation";
3167
+ IncrementalSource[IncrementalSource["MouseMove"] = 1] = "MouseMove";
3168
+ IncrementalSource[IncrementalSource["MouseInteraction"] = 2] = "MouseInteraction";
3169
+ IncrementalSource[IncrementalSource["Scroll"] = 3] = "Scroll";
3170
+ IncrementalSource[IncrementalSource["ViewportResize"] = 4] = "ViewportResize";
3171
+ IncrementalSource[IncrementalSource["Input"] = 5] = "Input";
3172
+ IncrementalSource[IncrementalSource["TouchMove"] = 6] = "TouchMove";
3173
+ IncrementalSource[IncrementalSource["MediaInteraction"] = 7] = "MediaInteraction";
3174
+ IncrementalSource[IncrementalSource["StyleSheetRule"] = 8] = "StyleSheetRule";
3175
+ IncrementalSource[IncrementalSource["CanvasMutation"] = 9] = "CanvasMutation";
3176
+ IncrementalSource[IncrementalSource["Font"] = 10] = "Font";
3177
+ IncrementalSource[IncrementalSource["Log"] = 11] = "Log";
3178
+ IncrementalSource[IncrementalSource["Drag"] = 12] = "Drag";
3179
+ IncrementalSource[IncrementalSource["StyleDeclaration"] = 13] = "StyleDeclaration";
3180
+ IncrementalSource[IncrementalSource["Selection"] = 14] = "Selection";
3181
+ IncrementalSource[IncrementalSource["AdoptedStyleSheet"] = 15] = "AdoptedStyleSheet";
3182
+ IncrementalSource[IncrementalSource["CustomElement"] = 16] = "CustomElement";
3183
+ })(IncrementalSource || (IncrementalSource = {}));
3184
+ var MouseInteractions;
3185
+ (function (MouseInteractions) {
3186
+ MouseInteractions[MouseInteractions["MouseUp"] = 0] = "MouseUp";
3187
+ MouseInteractions[MouseInteractions["MouseDown"] = 1] = "MouseDown";
3188
+ MouseInteractions[MouseInteractions["Click"] = 2] = "Click";
3189
+ MouseInteractions[MouseInteractions["ContextMenu"] = 3] = "ContextMenu";
3190
+ MouseInteractions[MouseInteractions["DblClick"] = 4] = "DblClick";
3191
+ MouseInteractions[MouseInteractions["Focus"] = 5] = "Focus";
3192
+ MouseInteractions[MouseInteractions["Blur"] = 6] = "Blur";
3193
+ MouseInteractions[MouseInteractions["TouchStart"] = 7] = "TouchStart";
3194
+ MouseInteractions[MouseInteractions["TouchMove_Departed"] = 8] = "TouchMove_Departed";
3195
+ MouseInteractions[MouseInteractions["TouchEnd"] = 9] = "TouchEnd";
3196
+ MouseInteractions[MouseInteractions["TouchCancel"] = 10] = "TouchCancel";
3197
+ })(MouseInteractions || (MouseInteractions = {}));
3198
+ var PointerTypes;
3199
+ (function (PointerTypes) {
3200
+ PointerTypes[PointerTypes["Mouse"] = 0] = "Mouse";
3201
+ PointerTypes[PointerTypes["Pen"] = 1] = "Pen";
3202
+ PointerTypes[PointerTypes["Touch"] = 2] = "Touch";
3203
+ })(PointerTypes || (PointerTypes = {}));
3204
+ var MediaInteractions;
3205
+ (function (MediaInteractions) {
3206
+ MediaInteractions[MediaInteractions["Play"] = 0] = "Play";
3207
+ MediaInteractions[MediaInteractions["Pause"] = 1] = "Pause";
3208
+ MediaInteractions[MediaInteractions["Seeked"] = 2] = "Seeked";
3209
+ MediaInteractions[MediaInteractions["VolumeChange"] = 3] = "VolumeChange";
3210
+ MediaInteractions[MediaInteractions["RateChange"] = 4] = "RateChange";
3211
+ })(MediaInteractions || (MediaInteractions = {}));
3212
+ var CanvasContext;
3213
+ (function (CanvasContext) {
3214
+ CanvasContext[CanvasContext["2D"] = 0] = "2D";
3215
+ CanvasContext[CanvasContext["WebGL"] = 1] = "WebGL";
3216
+ CanvasContext[CanvasContext["WebGL2"] = 2] = "WebGL2";
3217
+ })(CanvasContext || (CanvasContext = {}));
3218
+
3126
3219
  const _createLogger = prefix => {
3127
3220
  return {
3128
- info: (...args) => logger$3.info(prefix, ...args),
3129
- warn: (...args) => logger$3.warn(prefix, ...args),
3130
- error: (...args) => logger$3.error(prefix, ...args),
3131
- critical: (...args) => logger$3.critical(prefix, ...args),
3221
+ info: (...args) => logger$2.info(prefix, ...args),
3222
+ warn: (...args) => logger$2.warn(prefix, ...args),
3223
+ error: (...args) => logger$2.error(prefix, ...args),
3224
+ critical: (...args) => logger$2.critical(prefix, ...args),
3132
3225
  uninitializedWarning: methodName => {
3133
- logger$3.error(prefix, `You must initialize Leanbase before calling ${methodName}`);
3226
+ logger$2.error(prefix, `You must initialize Leanbase before calling ${methodName}`);
3134
3227
  },
3135
3228
  createLogger: additionalPrefix => _createLogger(`${prefix} ${additionalPrefix}`)
3136
3229
  };
3137
3230
  };
3138
- const logger$2 = _createLogger('[Leanbase]');
3231
+ const logger$1 = _createLogger('[Leanbase]');
3139
3232
  const createLogger = _createLogger;
3140
3233
 
3141
- function patch(source, name, replacement) {
3142
- try {
3143
- if (!(name in source)) {
3144
- return () => {
3145
- //
3146
- };
3147
- }
3148
- const original = source[name];
3149
- const wrapped = replacement(original);
3150
- if (core.isFunction(wrapped)) {
3151
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
3152
- wrapped.prototype = wrapped.prototype || {};
3153
- Object.defineProperties(wrapped, {
3154
- __posthog_wrapped__: {
3155
- enumerable: false,
3156
- value: true
3157
- }
3158
- });
3159
- }
3160
- source[name] = wrapped;
3161
- return () => {
3162
- source[name] = original;
3163
- };
3164
- } catch {
3165
- return () => {
3166
- //
3167
- };
3168
- }
3169
- }
3170
-
3171
- function hostnameFromURL(url) {
3172
- try {
3173
- if (typeof url === 'string') {
3174
- return new URL(url).hostname;
3175
- }
3176
- if ('url' in url) {
3177
- return new URL(url.url).hostname;
3178
- }
3179
- return url.hostname;
3180
- } catch {
3181
- return null;
3182
- }
3183
- }
3184
- function isHostOnDenyList(url, options) {
3185
- const hostname = hostnameFromURL(url);
3186
- const defaultNotDenied = {
3187
- hostname,
3188
- isHostDenied: false
3189
- };
3190
- if (!options.payloadHostDenyList?.length || !hostname?.trim().length) {
3191
- return defaultNotDenied;
3192
- }
3193
- for (const deny of options.payloadHostDenyList) {
3194
- if (hostname.endsWith(deny)) {
3195
- return {
3196
- hostname,
3197
- isHostDenied: true
3198
- };
3199
- }
3200
- }
3201
- return defaultNotDenied;
3202
- }
3203
-
3204
3234
  const LOGGER_PREFIX$2 = '[SessionRecording]';
3205
3235
  const REDACTED = 'redacted';
3206
3236
  const defaultNetworkOptions = {
@@ -3272,7 +3302,7 @@ function enforcePayloadSizeLimit(payload, headers, limit, description) {
3272
3302
  // people can have arbitrarily large payloads on their site, but we don't want to ingest them
3273
3303
  const limitPayloadSize = options => {
3274
3304
  // the smallest of 1MB or the specified limit if there is one
3275
- const limit = Math.min(1000000, options.payloadSizeLimitBytes ?? 1000000);
3305
+ const limit = Math.min(1000000, options.payloadSizeLimitBytes);
3276
3306
  return data => {
3277
3307
  if (data?.requestBody) {
3278
3308
  data.requestBody = enforcePayloadSizeLimit(data.requestBody, data.requestHeaders, limit, 'Request');
@@ -3330,7 +3360,7 @@ const buildNetworkRequestOptions = (instanceConfig, remoteNetworkOptions = {}) =
3330
3360
  const enforcedCleaningFn = d => payloadLimiter(ignorePostHogPaths(removeAuthorizationHeader(d), instanceConfig.host || ''));
3331
3361
  const hasDeprecatedMaskFunction = core.isFunction(sessionRecordingConfig.maskNetworkRequestFn);
3332
3362
  if (hasDeprecatedMaskFunction && core.isFunction(sessionRecordingConfig.maskCapturedNetworkRequestFn)) {
3333
- logger$2.warn('Both `maskNetworkRequestFn` and `maskCapturedNetworkRequestFn` are defined. `maskNetworkRequestFn` will be ignored.');
3363
+ logger$1.warn('Both `maskNetworkRequestFn` and `maskCapturedNetworkRequestFn` are defined. `maskNetworkRequestFn` will be ignored.');
3334
3364
  }
3335
3365
  if (hasDeprecatedMaskFunction) {
3336
3366
  sessionRecordingConfig.maskCapturedNetworkRequestFn = data => {
@@ -3357,666 +3387,6 @@ const buildNetworkRequestOptions = (instanceConfig, remoteNetworkOptions = {}) =
3357
3387
  };
3358
3388
  };
3359
3389
 
3360
- /// <reference lib="dom" />
3361
- const logger$1 = createLogger('[Recorder]');
3362
- const isNavigationTiming = entry => entry.entryType === 'navigation';
3363
- const isResourceTiming = entry => entry.entryType === 'resource';
3364
- function findLast(array, predicate) {
3365
- const length = array.length;
3366
- for (let i = length - 1; i >= 0; i -= 1) {
3367
- if (predicate(array[i])) {
3368
- return array[i];
3369
- }
3370
- }
3371
- return undefined;
3372
- }
3373
- function isDocument(value) {
3374
- return !!value && typeof value === 'object' && 'nodeType' in value && value.nodeType === 9;
3375
- }
3376
- function initPerformanceObserver(cb, win, options) {
3377
- // if we are only observing timings then we could have a single observer for all types, with buffer true,
3378
- // but we are going to filter by initiatorType _if we are wrapping fetch and xhr as the wrapped functions
3379
- // will deal with those.
3380
- // so we have a block which captures requests from before fetch/xhr is wrapped
3381
- // these are marked `isInitial` so playback can display them differently if needed
3382
- // they will never have method/status/headers/body because they are pre-wrapping that provides that
3383
- if (options.recordInitialRequests) {
3384
- const initialPerformanceEntries = win.performance.getEntries().filter(entry => isNavigationTiming(entry) || isResourceTiming(entry) && options.initiatorTypes.includes(entry.initiatorType));
3385
- cb({
3386
- requests: initialPerformanceEntries.flatMap(entry => prepareRequest({
3387
- entry,
3388
- method: undefined,
3389
- status: undefined,
3390
- networkRequest: {},
3391
- isInitial: true
3392
- })),
3393
- isInitial: true
3394
- });
3395
- }
3396
- const observer = new win.PerformanceObserver(entries => {
3397
- // if recordBody or recordHeaders is true then we don't want to record fetch or xhr here
3398
- // as the wrapped functions will do that. Otherwise, this filter becomes a noop
3399
- // because we do want to record them here
3400
- const wrappedInitiatorFilter = entry => options.recordBody || options.recordHeaders ? entry.initiatorType !== 'xmlhttprequest' && entry.initiatorType !== 'fetch' : true;
3401
- const performanceEntries = entries.getEntries().filter(entry => isNavigationTiming(entry) || isResourceTiming(entry) && options.initiatorTypes.includes(entry.initiatorType) &&
3402
- // TODO if we are _only_ capturing timing we don't want to filter initiator here
3403
- wrappedInitiatorFilter(entry));
3404
- cb({
3405
- requests: performanceEntries.flatMap(entry => prepareRequest({
3406
- entry,
3407
- method: undefined,
3408
- status: undefined,
3409
- networkRequest: {}
3410
- }))
3411
- });
3412
- });
3413
- // compat checked earlier
3414
- // eslint-disable-next-line compat/compat
3415
- const entryTypes = PerformanceObserver.supportedEntryTypes.filter(x => options.performanceEntryTypeToObserve.includes(x));
3416
- // initial records are gathered above, so we don't need to observe and buffer each type separately
3417
- observer.observe({
3418
- entryTypes
3419
- });
3420
- return () => {
3421
- observer.disconnect();
3422
- };
3423
- }
3424
- function shouldRecordHeaders(type, recordHeaders) {
3425
- return !!recordHeaders && (core.isBoolean(recordHeaders) || recordHeaders[type]);
3426
- }
3427
- function shouldRecordBody({
3428
- type,
3429
- recordBody,
3430
- headers,
3431
- url
3432
- }) {
3433
- function matchesContentType(contentTypes) {
3434
- const contentTypeHeader = Object.keys(headers).find(key => key.toLowerCase() === 'content-type');
3435
- const contentType = contentTypeHeader && headers[contentTypeHeader];
3436
- return contentTypes.some(ct => contentType?.includes(ct));
3437
- }
3438
- /**
3439
- * particularly in canvas applications we see many requests to blob URLs
3440
- * e.g. blob:https://video_url
3441
- * these blob/object URLs are local to the browser, we can never capture that body
3442
- * so we can just return false here
3443
- */
3444
- function isBlobURL(url) {
3445
- try {
3446
- if (typeof url === 'string') {
3447
- return url.startsWith('blob:');
3448
- }
3449
- if (url instanceof URL) {
3450
- return url.protocol === 'blob:';
3451
- }
3452
- if (url instanceof Request) {
3453
- return isBlobURL(url.url);
3454
- }
3455
- return false;
3456
- } catch {
3457
- return false;
3458
- }
3459
- }
3460
- if (!recordBody) return false;
3461
- if (isBlobURL(url)) return false;
3462
- if (core.isBoolean(recordBody)) return true;
3463
- if (core.isArray(recordBody)) return matchesContentType(recordBody);
3464
- const recordBodyType = recordBody[type];
3465
- if (core.isBoolean(recordBodyType)) return recordBodyType;
3466
- return matchesContentType(recordBodyType);
3467
- }
3468
- async function getRequestPerformanceEntry(win, initiatorType, url, start, end, attempt = 0) {
3469
- if (attempt > 10) {
3470
- logger$1.warn('Failed to get performance entry for request', {
3471
- url,
3472
- initiatorType
3473
- });
3474
- return null;
3475
- }
3476
- const urlPerformanceEntries = win.performance.getEntriesByName(url);
3477
- const performanceEntry = findLast(urlPerformanceEntries, entry => isResourceTiming(entry) && entry.initiatorType === initiatorType && (core.isUndefined(start) || entry.startTime >= start) && (core.isUndefined(end) || entry.startTime <= end));
3478
- if (!performanceEntry) {
3479
- await new Promise(resolve => setTimeout(resolve, 50 * attempt));
3480
- return getRequestPerformanceEntry(win, initiatorType, url, start, end, attempt + 1);
3481
- }
3482
- return performanceEntry;
3483
- }
3484
- /**
3485
- * According to MDN https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/response
3486
- * xhr response is typed as any but can be an ArrayBuffer, a Blob, a Document, a JavaScript object,
3487
- * or a string, depending on the value of XMLHttpRequest.responseType, that contains the response entity body.
3488
- *
3489
- * XHR request body is Document | XMLHttpRequestBodyInit | null | undefined
3490
- */
3491
- function _tryReadXHRBody({
3492
- body,
3493
- options,
3494
- url
3495
- }) {
3496
- if (core.isNullish(body)) {
3497
- return null;
3498
- }
3499
- const {
3500
- hostname,
3501
- isHostDenied
3502
- } = isHostOnDenyList(url, options);
3503
- if (isHostDenied) {
3504
- return hostname + ' is in deny list';
3505
- }
3506
- if (core.isString(body)) {
3507
- return body;
3508
- }
3509
- if (isDocument(body)) {
3510
- return body.textContent;
3511
- }
3512
- if (core.isFormData(body)) {
3513
- return formDataToQuery(body);
3514
- }
3515
- if (core.isObject(body)) {
3516
- try {
3517
- return JSON.stringify(body);
3518
- } catch {
3519
- return '[SessionReplay] Failed to stringify response object';
3520
- }
3521
- }
3522
- return '[SessionReplay] Cannot read body of type ' + toString.call(body);
3523
- }
3524
- function initXhrObserver(cb, win, options) {
3525
- if (!options.initiatorTypes.includes('xmlhttprequest')) {
3526
- return () => {
3527
- //
3528
- };
3529
- }
3530
- const recordRequestHeaders = shouldRecordHeaders('request', options.recordHeaders);
3531
- const recordResponseHeaders = shouldRecordHeaders('response', options.recordHeaders);
3532
- const restorePatch = patch(win.XMLHttpRequest.prototype, 'open',
3533
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
3534
- // @ts-ignore
3535
- originalOpen => {
3536
- return function (method, url, async = true, username, password) {
3537
- // because this function is returned in its actual context `this` _is_ an XMLHttpRequest
3538
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
3539
- // @ts-ignore
3540
- const xhr = this;
3541
- // check IE earlier than this, we only initialize if Request is present
3542
- // eslint-disable-next-line compat/compat
3543
- const req = new Request(url);
3544
- const networkRequest = {};
3545
- let start;
3546
- let end;
3547
- const requestHeaders = {};
3548
- const originalSetRequestHeader = xhr.setRequestHeader.bind(xhr);
3549
- xhr.setRequestHeader = (header, value) => {
3550
- requestHeaders[header] = value;
3551
- return originalSetRequestHeader(header, value);
3552
- };
3553
- if (recordRequestHeaders) {
3554
- networkRequest.requestHeaders = requestHeaders;
3555
- }
3556
- const originalSend = xhr.send.bind(xhr);
3557
- xhr.send = body => {
3558
- if (shouldRecordBody({
3559
- type: 'request',
3560
- headers: requestHeaders,
3561
- url,
3562
- recordBody: options.recordBody
3563
- })) {
3564
- networkRequest.requestBody = _tryReadXHRBody({
3565
- body,
3566
- options,
3567
- url
3568
- });
3569
- }
3570
- start = win.performance.now();
3571
- return originalSend(body);
3572
- };
3573
- const readyStateListener = () => {
3574
- if (xhr.readyState !== xhr.DONE) {
3575
- return;
3576
- }
3577
- // Clean up the listener immediately when done to prevent memory leaks
3578
- xhr.removeEventListener('readystatechange', readyStateListener);
3579
- end = win.performance.now();
3580
- const responseHeaders = {};
3581
- const rawHeaders = xhr.getAllResponseHeaders();
3582
- const headers = rawHeaders.trim().split(/[\r\n]+/);
3583
- headers.forEach(line => {
3584
- const parts = line.split(': ');
3585
- const header = parts.shift();
3586
- const value = parts.join(': ');
3587
- if (header) {
3588
- responseHeaders[header] = value;
3589
- }
3590
- });
3591
- if (recordResponseHeaders) {
3592
- networkRequest.responseHeaders = responseHeaders;
3593
- }
3594
- if (shouldRecordBody({
3595
- type: 'response',
3596
- headers: responseHeaders,
3597
- url,
3598
- recordBody: options.recordBody
3599
- })) {
3600
- networkRequest.responseBody = _tryReadXHRBody({
3601
- body: xhr.response,
3602
- options,
3603
- url
3604
- });
3605
- }
3606
- getRequestPerformanceEntry(win, 'xmlhttprequest', req.url, start, end).then(entry => {
3607
- const requests = prepareRequest({
3608
- entry,
3609
- method: method,
3610
- status: xhr?.status,
3611
- networkRequest,
3612
- start,
3613
- end,
3614
- url: url.toString(),
3615
- initiatorType: 'xmlhttprequest'
3616
- });
3617
- cb({
3618
- requests
3619
- });
3620
- }).catch(() => {
3621
- //
3622
- });
3623
- };
3624
- // This is very tricky code, and making it passive won't bring many performance benefits,
3625
- // so let's ignore the rule here.
3626
- // eslint-disable-next-line posthog-js/no-add-event-listener
3627
- xhr.addEventListener('readystatechange', readyStateListener);
3628
- originalOpen.call(xhr, method, url, async, username, password);
3629
- };
3630
- });
3631
- return () => {
3632
- restorePatch();
3633
- };
3634
- }
3635
- /**
3636
- * Check if this PerformanceEntry is either a PerformanceResourceTiming or a PerformanceNavigationTiming
3637
- * NB PerformanceNavigationTiming extends PerformanceResourceTiming
3638
- * Here we don't care which interface it implements as both expose `serverTimings`
3639
- */
3640
- const exposesServerTiming = event => !core.isNull(event) && (event.entryType === 'navigation' || event.entryType === 'resource');
3641
- function prepareRequest({
3642
- entry,
3643
- method,
3644
- status,
3645
- networkRequest,
3646
- isInitial,
3647
- start,
3648
- end,
3649
- url,
3650
- initiatorType
3651
- }) {
3652
- start = entry ? entry.startTime : start;
3653
- end = entry ? entry.responseEnd : end;
3654
- // kudos to sentry javascript sdk for excellent background on why to use Date.now() here
3655
- // https://github.com/getsentry/sentry-javascript/blob/e856e40b6e71a73252e788cd42b5260f81c9c88e/packages/utils/src/time.ts#L70
3656
- // can't start observer if performance.now() is not available
3657
- // eslint-disable-next-line compat/compat
3658
- const timeOrigin = Math.floor(Date.now() - performance.now());
3659
- // clickhouse can't ingest timestamps that are floats
3660
- // (in this case representing fractions of a millisecond we don't care about anyway)
3661
- // use timeOrigin if we really can't gather a start time
3662
- const timestamp = Math.floor(timeOrigin + (start || 0));
3663
- const entryJSON = entry ? entry.toJSON() : {
3664
- name: url
3665
- };
3666
- const requests = [{
3667
- ...entryJSON,
3668
- startTime: core.isUndefined(start) ? undefined : Math.round(start),
3669
- endTime: core.isUndefined(end) ? undefined : Math.round(end),
3670
- timeOrigin,
3671
- timestamp,
3672
- method: method,
3673
- initiatorType: initiatorType ? initiatorType : entry ? entry.initiatorType : undefined,
3674
- status,
3675
- requestHeaders: networkRequest.requestHeaders,
3676
- requestBody: networkRequest.requestBody,
3677
- responseHeaders: networkRequest.responseHeaders,
3678
- responseBody: networkRequest.responseBody,
3679
- isInitial
3680
- }];
3681
- if (exposesServerTiming(entry)) {
3682
- for (const timing of entry.serverTiming || []) {
3683
- requests.push({
3684
- timeOrigin,
3685
- timestamp,
3686
- startTime: Math.round(entry.startTime),
3687
- name: timing.name,
3688
- duration: timing.duration,
3689
- // the spec has a closed list of possible types
3690
- // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceEntry/entryType
3691
- // but, we need to know this was a server timing so that we know to
3692
- // match it to the appropriate navigation or resource timing
3693
- // that matching will have to be on timestamp and $current_url
3694
- entryType: 'serverTiming'
3695
- });
3696
- }
3697
- }
3698
- return requests;
3699
- }
3700
- const contentTypePrefixDenyList = ['video/', 'audio/'];
3701
- function _checkForCannotReadResponseBody({
3702
- r,
3703
- options,
3704
- url
3705
- }) {
3706
- if (r.headers.get('Transfer-Encoding') === 'chunked') {
3707
- return 'Chunked Transfer-Encoding is not supported';
3708
- }
3709
- // `get` and `has` are case-insensitive
3710
- // but return the header value with the casing that was supplied
3711
- const contentType = r.headers.get('Content-Type')?.toLowerCase();
3712
- const contentTypeIsDenied = contentTypePrefixDenyList.some(prefix => contentType?.startsWith(prefix));
3713
- if (contentType && contentTypeIsDenied) {
3714
- return `Content-Type ${contentType} is not supported`;
3715
- }
3716
- const {
3717
- hostname,
3718
- isHostDenied
3719
- } = isHostOnDenyList(url, options);
3720
- if (isHostDenied) {
3721
- return hostname + ' is in deny list';
3722
- }
3723
- return null;
3724
- }
3725
- function _tryReadBody(r) {
3726
- // there are now already multiple places where we're using Promise...
3727
- // eslint-disable-next-line compat/compat
3728
- return new Promise((resolve, reject) => {
3729
- const timeout = setTimeout(() => resolve('[SessionReplay] Timeout while trying to read body'), 500);
3730
- try {
3731
- r.clone().text().then(txt => resolve(txt), reason => reject(reason)).finally(() => clearTimeout(timeout));
3732
- } catch {
3733
- clearTimeout(timeout);
3734
- resolve('[SessionReplay] Failed to read body');
3735
- }
3736
- });
3737
- }
3738
- async function _tryReadRequestBody({
3739
- r,
3740
- options,
3741
- url
3742
- }) {
3743
- const {
3744
- hostname,
3745
- isHostDenied
3746
- } = isHostOnDenyList(url, options);
3747
- if (isHostDenied) {
3748
- return Promise.resolve(hostname + ' is in deny list');
3749
- }
3750
- return _tryReadBody(r);
3751
- }
3752
- async function _tryReadResponseBody({
3753
- r,
3754
- options,
3755
- url
3756
- }) {
3757
- const cannotReadBodyReason = _checkForCannotReadResponseBody({
3758
- r,
3759
- options,
3760
- url
3761
- });
3762
- if (!core.isNull(cannotReadBodyReason)) {
3763
- return Promise.resolve(cannotReadBodyReason);
3764
- }
3765
- return _tryReadBody(r);
3766
- }
3767
- function initFetchObserver(cb, win, options) {
3768
- if (!options.initiatorTypes.includes('fetch')) {
3769
- return () => {
3770
- //
3771
- };
3772
- }
3773
- const recordRequestHeaders = shouldRecordHeaders('request', options.recordHeaders);
3774
- const recordResponseHeaders = shouldRecordHeaders('response', options.recordHeaders);
3775
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
3776
- // @ts-ignore
3777
- const restorePatch = patch(win, 'fetch', originalFetch => {
3778
- return async function (url, init) {
3779
- // check IE earlier than this, we only initialize if Request is present
3780
- // eslint-disable-next-line compat/compat
3781
- const req = new Request(url, init);
3782
- let res;
3783
- const networkRequest = {};
3784
- let start;
3785
- let end;
3786
- try {
3787
- const requestHeaders = {};
3788
- req.headers.forEach((value, header) => {
3789
- requestHeaders[header] = value;
3790
- });
3791
- if (recordRequestHeaders) {
3792
- networkRequest.requestHeaders = requestHeaders;
3793
- }
3794
- if (shouldRecordBody({
3795
- type: 'request',
3796
- headers: requestHeaders,
3797
- url,
3798
- recordBody: options.recordBody
3799
- })) {
3800
- networkRequest.requestBody = await _tryReadRequestBody({
3801
- r: req,
3802
- options,
3803
- url
3804
- });
3805
- }
3806
- start = win.performance.now();
3807
- res = await originalFetch(req);
3808
- end = win.performance.now();
3809
- const responseHeaders = {};
3810
- res.headers.forEach((value, header) => {
3811
- responseHeaders[header] = value;
3812
- });
3813
- if (recordResponseHeaders) {
3814
- networkRequest.responseHeaders = responseHeaders;
3815
- }
3816
- if (shouldRecordBody({
3817
- type: 'response',
3818
- headers: responseHeaders,
3819
- url,
3820
- recordBody: options.recordBody
3821
- })) {
3822
- networkRequest.responseBody = await _tryReadResponseBody({
3823
- r: res,
3824
- options,
3825
- url
3826
- });
3827
- }
3828
- return res;
3829
- } finally {
3830
- getRequestPerformanceEntry(win, 'fetch', req.url, start, end).then(entry => {
3831
- const requests = prepareRequest({
3832
- entry,
3833
- method: req.method,
3834
- status: res?.status,
3835
- networkRequest,
3836
- start,
3837
- end,
3838
- url: req.url,
3839
- initiatorType: 'fetch'
3840
- });
3841
- cb({
3842
- requests
3843
- });
3844
- }).catch(() => {
3845
- //
3846
- });
3847
- }
3848
- };
3849
- });
3850
- return () => {
3851
- restorePatch();
3852
- };
3853
- }
3854
- let initialisedHandler = null;
3855
- function initNetworkObserver(callback, win,
3856
- // top window or in an iframe
3857
- options) {
3858
- if (!('performance' in win)) {
3859
- return () => {
3860
- //
3861
- };
3862
- }
3863
- if (initialisedHandler) {
3864
- logger$1.warn('Network observer already initialised, doing nothing');
3865
- return () => {
3866
- // the first caller should already have this handler and will be responsible for teardown
3867
- };
3868
- }
3869
- const networkOptions = options ? Object.assign({}, defaultNetworkOptions, options) : defaultNetworkOptions;
3870
- const cb = data => {
3871
- const requests = [];
3872
- data.requests.forEach(request => {
3873
- const maskedRequest = networkOptions.maskRequestFn(request);
3874
- if (maskedRequest) {
3875
- requests.push(maskedRequest);
3876
- }
3877
- });
3878
- if (requests.length > 0) {
3879
- callback({
3880
- ...data,
3881
- requests
3882
- });
3883
- }
3884
- };
3885
- const performanceObserver = initPerformanceObserver(cb, win, networkOptions);
3886
- // only wrap fetch and xhr if headers or body are being recorded
3887
- let xhrObserver = () => {};
3888
- let fetchObserver = () => {};
3889
- if (networkOptions.recordHeaders || networkOptions.recordBody) {
3890
- xhrObserver = initXhrObserver(cb, win, networkOptions);
3891
- fetchObserver = initFetchObserver(cb, win, networkOptions);
3892
- }
3893
- const teardown = () => {
3894
- performanceObserver();
3895
- xhrObserver();
3896
- fetchObserver();
3897
- // allow future observers to initialize after cleanup
3898
- initialisedHandler = null;
3899
- };
3900
- initialisedHandler = teardown;
3901
- return teardown;
3902
- }
3903
- // use the plugin name so that when this functionality is adopted into rrweb
3904
- // we can remove this plugin and use the core functionality with the same data
3905
- const NETWORK_PLUGIN_NAME = 'rrweb/network@1';
3906
- // TODO how should this be typed?
3907
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
3908
- // @ts-ignore
3909
- const getRecordNetworkPlugin = options => {
3910
- return {
3911
- name: NETWORK_PLUGIN_NAME,
3912
- observer: initNetworkObserver,
3913
- options: options
3914
- };
3915
- };
3916
- // rrweb/networ@1 ends
3917
-
3918
- // Use a safe global target (prefer `win`, fallback to globalThis)
3919
- const _target = win ?? globalThis;
3920
- _target.__PosthogExtensions__ = _target.__PosthogExtensions__ || {};
3921
- // Expose rrweb.record under the same contract
3922
- _target.__PosthogExtensions__.rrweb = _target.__PosthogExtensions__.rrweb || {
3923
- record: record.record
3924
- };
3925
- // Provide initSessionRecording if not present — return a new LazyLoadedSessionRecording when called
3926
- _target.__PosthogExtensions__.initSessionRecording = _target.__PosthogExtensions__.initSessionRecording || (instance => {
3927
- return new LazyLoadedSessionRecording(instance);
3928
- });
3929
- // Provide a no-op loadExternalDependency that calls the callback immediately (since rrweb is bundled)
3930
- _target.__PosthogExtensions__.loadExternalDependency = _target.__PosthogExtensions__.loadExternalDependency || ((instance, scriptName, cb) => {
3931
- if (cb) cb(undefined);
3932
- });
3933
- // Provide rrwebPlugins object with network plugin factory if not present
3934
- _target.__PosthogExtensions__.rrwebPlugins = _target.__PosthogExtensions__.rrwebPlugins || {};
3935
- _target.__PosthogExtensions__.rrwebPlugins.getRecordNetworkPlugin = _target.__PosthogExtensions__.rrwebPlugins.getRecordNetworkPlugin || (() => getRecordNetworkPlugin);
3936
-
3937
- // Type definitions copied from @rrweb/types@2.0.0-alpha.17 and rrweb-snapshot@2.0.0-alpha.17
3938
- // Both packages are MIT licensed: https://github.com/rrweb-io/rrweb
3939
- //
3940
- // These types are copied here to avoid requiring users to install peer dependencies
3941
- // solely for TypeScript type information.
3942
- //
3943
- // Original sources:
3944
- // - @rrweb/types: https://github.com/rrweb-io/rrweb/tree/main/packages/@rrweb/types
3945
- // - rrweb-snapshot: https://github.com/rrweb-io/rrweb/tree/main/packages/rrweb-snapshot
3946
- var NodeType;
3947
- (function (NodeType) {
3948
- NodeType[NodeType["Document"] = 0] = "Document";
3949
- NodeType[NodeType["DocumentType"] = 1] = "DocumentType";
3950
- NodeType[NodeType["Element"] = 2] = "Element";
3951
- NodeType[NodeType["Text"] = 3] = "Text";
3952
- NodeType[NodeType["CDATA"] = 4] = "CDATA";
3953
- NodeType[NodeType["Comment"] = 5] = "Comment";
3954
- })(NodeType || (NodeType = {}));
3955
- var EventType;
3956
- (function (EventType) {
3957
- EventType[EventType["DomContentLoaded"] = 0] = "DomContentLoaded";
3958
- EventType[EventType["Load"] = 1] = "Load";
3959
- EventType[EventType["FullSnapshot"] = 2] = "FullSnapshot";
3960
- EventType[EventType["IncrementalSnapshot"] = 3] = "IncrementalSnapshot";
3961
- EventType[EventType["Meta"] = 4] = "Meta";
3962
- EventType[EventType["Custom"] = 5] = "Custom";
3963
- EventType[EventType["Plugin"] = 6] = "Plugin";
3964
- })(EventType || (EventType = {}));
3965
- var IncrementalSource;
3966
- (function (IncrementalSource) {
3967
- IncrementalSource[IncrementalSource["Mutation"] = 0] = "Mutation";
3968
- IncrementalSource[IncrementalSource["MouseMove"] = 1] = "MouseMove";
3969
- IncrementalSource[IncrementalSource["MouseInteraction"] = 2] = "MouseInteraction";
3970
- IncrementalSource[IncrementalSource["Scroll"] = 3] = "Scroll";
3971
- IncrementalSource[IncrementalSource["ViewportResize"] = 4] = "ViewportResize";
3972
- IncrementalSource[IncrementalSource["Input"] = 5] = "Input";
3973
- IncrementalSource[IncrementalSource["TouchMove"] = 6] = "TouchMove";
3974
- IncrementalSource[IncrementalSource["MediaInteraction"] = 7] = "MediaInteraction";
3975
- IncrementalSource[IncrementalSource["StyleSheetRule"] = 8] = "StyleSheetRule";
3976
- IncrementalSource[IncrementalSource["CanvasMutation"] = 9] = "CanvasMutation";
3977
- IncrementalSource[IncrementalSource["Font"] = 10] = "Font";
3978
- IncrementalSource[IncrementalSource["Log"] = 11] = "Log";
3979
- IncrementalSource[IncrementalSource["Drag"] = 12] = "Drag";
3980
- IncrementalSource[IncrementalSource["StyleDeclaration"] = 13] = "StyleDeclaration";
3981
- IncrementalSource[IncrementalSource["Selection"] = 14] = "Selection";
3982
- IncrementalSource[IncrementalSource["AdoptedStyleSheet"] = 15] = "AdoptedStyleSheet";
3983
- IncrementalSource[IncrementalSource["CustomElement"] = 16] = "CustomElement";
3984
- })(IncrementalSource || (IncrementalSource = {}));
3985
- var MouseInteractions;
3986
- (function (MouseInteractions) {
3987
- MouseInteractions[MouseInteractions["MouseUp"] = 0] = "MouseUp";
3988
- MouseInteractions[MouseInteractions["MouseDown"] = 1] = "MouseDown";
3989
- MouseInteractions[MouseInteractions["Click"] = 2] = "Click";
3990
- MouseInteractions[MouseInteractions["ContextMenu"] = 3] = "ContextMenu";
3991
- MouseInteractions[MouseInteractions["DblClick"] = 4] = "DblClick";
3992
- MouseInteractions[MouseInteractions["Focus"] = 5] = "Focus";
3993
- MouseInteractions[MouseInteractions["Blur"] = 6] = "Blur";
3994
- MouseInteractions[MouseInteractions["TouchStart"] = 7] = "TouchStart";
3995
- MouseInteractions[MouseInteractions["TouchMove_Departed"] = 8] = "TouchMove_Departed";
3996
- MouseInteractions[MouseInteractions["TouchEnd"] = 9] = "TouchEnd";
3997
- MouseInteractions[MouseInteractions["TouchCancel"] = 10] = "TouchCancel";
3998
- })(MouseInteractions || (MouseInteractions = {}));
3999
- var PointerTypes;
4000
- (function (PointerTypes) {
4001
- PointerTypes[PointerTypes["Mouse"] = 0] = "Mouse";
4002
- PointerTypes[PointerTypes["Pen"] = 1] = "Pen";
4003
- PointerTypes[PointerTypes["Touch"] = 2] = "Touch";
4004
- })(PointerTypes || (PointerTypes = {}));
4005
- var MediaInteractions;
4006
- (function (MediaInteractions) {
4007
- MediaInteractions[MediaInteractions["Play"] = 0] = "Play";
4008
- MediaInteractions[MediaInteractions["Pause"] = 1] = "Pause";
4009
- MediaInteractions[MediaInteractions["Seeked"] = 2] = "Seeked";
4010
- MediaInteractions[MediaInteractions["VolumeChange"] = 3] = "VolumeChange";
4011
- MediaInteractions[MediaInteractions["RateChange"] = 4] = "RateChange";
4012
- })(MediaInteractions || (MediaInteractions = {}));
4013
- var CanvasContext;
4014
- (function (CanvasContext) {
4015
- CanvasContext[CanvasContext["2D"] = 0] = "2D";
4016
- CanvasContext[CanvasContext["WebGL"] = 1] = "WebGL";
4017
- CanvasContext[CanvasContext["WebGL2"] = 2] = "WebGL2";
4018
- })(CanvasContext || (CanvasContext = {}));
4019
-
4020
3390
  const DISABLED = 'disabled';
4021
3391
  const SAMPLED = 'sampled';
4022
3392
  const ACTIVE = 'active';
@@ -4424,7 +3794,7 @@ class MutationThrottler {
4424
3794
  refillInterval: 1000,
4425
3795
  // one second
4426
3796
  _onBucketRateLimited: this._onNodeRateLimited,
4427
- _logger: logger$2
3797
+ _logger: logger$1
4428
3798
  });
4429
3799
  }
4430
3800
  reset() {
@@ -4448,9 +3818,10 @@ function simpleHash(str) {
4448
3818
  * receives percent as a number between 0 and 1
4449
3819
  */
4450
3820
  function sampleOnProperty(prop, percent) {
4451
- return simpleHash(prop) % 100 < core.clampToRange(percent * 100, 0, 100, logger$2);
3821
+ return simpleHash(prop) % 100 < core.clampToRange(percent * 100, 0, 100, logger$1);
4452
3822
  }
4453
3823
 
3824
+ /* eslint-disable posthog-js/no-direct-function-check */
4454
3825
  const BASE_ENDPOINT = '/s/';
4455
3826
  const DEFAULT_CANVAS_QUALITY = 0.4;
4456
3827
  const DEFAULT_CANVAS_FPS = 4;
@@ -4461,6 +3832,19 @@ const ONE_KB = 1024;
4461
3832
  const ONE_MINUTE = 1000 * 60;
4462
3833
  const FIVE_MINUTES = ONE_MINUTE * 5;
4463
3834
  const RECORDING_IDLE_THRESHOLD_MS = FIVE_MINUTES;
3835
+ // Register a factory on the global extensions object so the extension shim can
3836
+ // instantiate a LazyLoadedSessionRecording without importing this module directly.
3837
+ try {
3838
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3839
+ const ext = globalThis.__PosthogExtensions__;
3840
+ if (ext) {
3841
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3842
+ ext._initSessionRecordingFactory = ext._initSessionRecordingFactory || (instance => new LazyLoadedSessionRecording(instance));
3843
+ }
3844
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
3845
+ } catch (e) {
3846
+ // ignore
3847
+ }
4464
3848
  const RECORDING_MAX_EVENT_SIZE = ONE_KB * ONE_KB * 0.9; // ~1mb (with some wiggle room)
4465
3849
  const RECORDING_BUFFER_TIMEOUT = 2000; // 2 seconds
4466
3850
  const SESSION_RECORDING_BATCH_KEY = 'recordings';
@@ -4739,7 +4123,13 @@ class LazyLoadedSessionRecording {
4739
4123
  if (this._networkPayloadCapture) {
4740
4124
  const canRecordNetwork = !isLocalhost() || this._forceAllowLocalhostNetworkCapture;
4741
4125
  if (canRecordNetwork) {
4742
- plugins.push(getRecordNetworkPlugin(buildNetworkRequestOptions(this._instance.config, this._networkPayloadCapture)));
4126
+ const assignableWindow = globalThis;
4127
+ const networkFactory = assignableWindow.__PosthogExtensions__?.rrwebPlugins?.getRecordNetworkPlugin?.();
4128
+ if (typeof networkFactory === 'function') {
4129
+ plugins.push(networkFactory(buildNetworkRequestOptions(this._instance.config, this._networkPayloadCapture)));
4130
+ } else {
4131
+ logger.info('Network plugin factory not available yet; skipping network plugin');
4132
+ }
4743
4133
  } else {
4744
4134
  logger.info('NetworkCapture not started because we are on localhost.');
4745
4135
  }
@@ -5489,9 +4879,9 @@ class LazyLoadedSessionRecording {
5489
4879
 
5490
4880
  const LOGGER_PREFIX = '[SessionRecording]';
5491
4881
  const log = {
5492
- info: (...args) => logger$3.info(LOGGER_PREFIX, ...args),
5493
- warn: (...args) => logger$3.warn(LOGGER_PREFIX, ...args),
5494
- error: (...args) => logger$3.error(LOGGER_PREFIX, ...args)
4882
+ info: (...args) => logger$2.info(LOGGER_PREFIX, ...args),
4883
+ warn: (...args) => logger$2.warn(LOGGER_PREFIX, ...args),
4884
+ error: (...args) => logger$2.error(LOGGER_PREFIX, ...args)
5495
4885
  };
5496
4886
  class SessionRecording {
5497
4887
  get started() {
@@ -5630,7 +5020,7 @@ class SessionRecording {
5630
5020
  if (this._lazyLoadedSessionRecording?.log) {
5631
5021
  this._lazyLoadedSessionRecording.log(message, level);
5632
5022
  } else {
5633
- logger$3.warn('log called before recorder was ready');
5023
+ logger$2.warn('log called before recorder was ready');
5634
5024
  }
5635
5025
  }
5636
5026
  _onScriptLoaded(startReason) {
@@ -5895,7 +5285,7 @@ class Leanbase extends core.PostHogCore {
5895
5285
  };
5896
5286
  properties['distinct_id'] = persistenceProps.distinct_id;
5897
5287
  if (!(core.isString(properties['distinct_id']) || core.isNumber(properties['distinct_id'])) || core.isEmptyString(properties['distinct_id'])) {
5898
- logger$3.error('Invalid distinct_id for replay event. This indicates a bug in your implementation');
5288
+ logger$2.error('Invalid distinct_id for replay event. This indicates a bug in your implementation');
5899
5289
  }
5900
5290
  return properties;
5901
5291
  }
@@ -5970,11 +5360,11 @@ class Leanbase extends core.PostHogCore {
5970
5360
  return;
5971
5361
  }
5972
5362
  if (core.isUndefined(event) || !core.isString(event)) {
5973
- logger$3.error('No event name provided to posthog.capture');
5363
+ logger$2.error('No event name provided to posthog.capture');
5974
5364
  return;
5975
5365
  }
5976
5366
  if (properties?.$current_url && !core.isString(properties?.$current_url)) {
5977
- logger$3.error('Invalid `$current_url` property provided to `posthog.capture`. Input must be a string. Ignoring provided value.');
5367
+ logger$2.error('Invalid `$current_url` property provided to `posthog.capture`. Input must be a string. Ignoring provided value.');
5978
5368
  delete properties?.$current_url;
5979
5369
  }
5980
5370
  this.sessionPersistence.update_search_keyword();