@leanbase-giangnd/js 0.1.1 → 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 +173 -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/iife.ts +10 -0
- package/src/version.ts +1 -1
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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.
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
2961
|
-
const maxScrollPercentage = maxScrollHeight <= 1 ? 1 : core.clampToRange(maxScrollY / maxScrollHeight, 0, 1, logger$
|
|
2962
|
-
const lastContentPercentage = maxContentHeight <= 1 ? 1 : core.clampToRange(lastContentY / maxContentHeight, 0, 1, logger$
|
|
2963
|
-
const maxContentPercentage = maxContentHeight <= 1 ? 1 : core.clampToRange(maxContentY / maxContentHeight, 0, 1, logger$
|
|
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$
|
|
3129
|
-
warn: (...args) => logger$
|
|
3130
|
-
error: (...args) => logger$
|
|
3131
|
-
critical: (...args) => logger$
|
|
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$
|
|
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$
|
|
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
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
-
|
|
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$
|
|
5493
|
-
warn: (...args) => logger$
|
|
5494
|
-
error: (...args) => logger$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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();
|