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