@leanbase-giangnd/js 0.2.4 → 0.3.1
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 +104 -187
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +0 -38
- package/dist/index.mjs +104 -187
- package/dist/index.mjs.map +1 -1
- package/dist/leanbase.iife.js +104 -187
- package/dist/leanbase.iife.js.map +1 -1
- package/package.json +1 -1
- package/src/extensions/replay/extension-shim.ts +1 -114
- package/src/extensions/replay/external/lazy-loaded-session-recorder.ts +115 -36
- package/src/extensions/replay/external/mutation-throttler.ts +1 -4
- package/src/extensions/replay/session-recording.ts +0 -59
- package/src/version.ts +1 -1
package/dist/leanbase.iife.js
CHANGED
|
@@ -2846,7 +2846,7 @@ var leanbase = (function () {
|
|
|
2846
2846
|
}
|
|
2847
2847
|
};
|
|
2848
2848
|
|
|
2849
|
-
var version = "0.
|
|
2849
|
+
var version = "0.3.1";
|
|
2850
2850
|
var packageInfo = {
|
|
2851
2851
|
version: version};
|
|
2852
2852
|
|
|
@@ -4785,117 +4785,20 @@ var leanbase = (function () {
|
|
|
4785
4785
|
};
|
|
4786
4786
|
|
|
4787
4787
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
4788
|
-
// We avoid importing '@rrweb/record' at module load time to prevent IIFE builds
|
|
4789
|
-
// from requiring a top-level global. Instead, expose a lazy proxy that will
|
|
4790
|
-
// dynamically import the module the first time it's used.
|
|
4791
|
-
let _cachedRRWeb = null;
|
|
4792
|
-
async function _loadRRWebModule() {
|
|
4793
|
-
if (_cachedRRWeb) return _cachedRRWeb;
|
|
4794
|
-
try {
|
|
4795
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4796
|
-
const mod = await Promise.resolve().then(function () { return record$1; });
|
|
4797
|
-
_cachedRRWeb = mod;
|
|
4798
|
-
return _cachedRRWeb;
|
|
4799
|
-
} catch (e) {
|
|
4800
|
-
return null;
|
|
4801
|
-
}
|
|
4802
|
-
}
|
|
4803
|
-
// queue for method calls before rrweb loads
|
|
4804
|
-
const _queuedCalls = [];
|
|
4805
|
-
// Create a proxy function that delegates to the real rrweb.record when called
|
|
4806
|
-
const rrwebRecordProxy = function (...args) {
|
|
4807
|
-
let realStop;
|
|
4808
|
-
let calledReal = false;
|
|
4809
|
-
// Start loading asynchronously and call the real record when available
|
|
4810
|
-
void (async () => {
|
|
4811
|
-
const mod = await _loadRRWebModule();
|
|
4812
|
-
const real = mod && (mod.record ?? mod.default?.record);
|
|
4813
|
-
if (real) {
|
|
4814
|
-
try {
|
|
4815
|
-
calledReal = true;
|
|
4816
|
-
realStop = real(...args);
|
|
4817
|
-
// flush any queued calls that were waiting for rrweb
|
|
4818
|
-
while (_queuedCalls.length) {
|
|
4819
|
-
try {
|
|
4820
|
-
const fn = _queuedCalls.shift();
|
|
4821
|
-
fn();
|
|
4822
|
-
} catch (e) {
|
|
4823
|
-
// ignore
|
|
4824
|
-
}
|
|
4825
|
-
}
|
|
4826
|
-
} catch (e) {
|
|
4827
|
-
// ignore
|
|
4828
|
-
}
|
|
4829
|
-
}
|
|
4830
|
-
})();
|
|
4831
|
-
// return a stop function that will call the real stop when available
|
|
4832
|
-
return () => {
|
|
4833
|
-
if (realStop) {
|
|
4834
|
-
try {
|
|
4835
|
-
realStop();
|
|
4836
|
-
} catch (e) {
|
|
4837
|
-
// ignore
|
|
4838
|
-
}
|
|
4839
|
-
} else if (!calledReal) {
|
|
4840
|
-
// If rrweb hasn't been initialised yet, queue a stop request that will
|
|
4841
|
-
// call the real stop once available.
|
|
4842
|
-
_queuedCalls.push(() => {
|
|
4843
|
-
try {
|
|
4844
|
-
realStop?.();
|
|
4845
|
-
} catch (e) {
|
|
4846
|
-
// ignore
|
|
4847
|
-
}
|
|
4848
|
-
});
|
|
4849
|
-
}
|
|
4850
|
-
};
|
|
4851
|
-
};
|
|
4852
|
-
// methods that can be called on the rrweb.record object - queue until real module is available
|
|
4853
|
-
rrwebRecordProxy.addCustomEvent = function (tag, payload) {
|
|
4854
|
-
const call = () => {
|
|
4855
|
-
try {
|
|
4856
|
-
const real = _cachedRRWeb && (_cachedRRWeb.record ?? _cachedRRWeb.default?.record);
|
|
4857
|
-
real?.addCustomEvent?.(tag, payload);
|
|
4858
|
-
} catch (e) {
|
|
4859
|
-
// ignore
|
|
4860
|
-
}
|
|
4861
|
-
};
|
|
4862
|
-
if (_cachedRRWeb) call();else _queuedCalls.push(call);
|
|
4863
|
-
};
|
|
4864
|
-
rrwebRecordProxy.takeFullSnapshot = function () {
|
|
4865
|
-
const call = () => {
|
|
4866
|
-
try {
|
|
4867
|
-
const real = _cachedRRWeb && (_cachedRRWeb.record ?? _cachedRRWeb.default?.record);
|
|
4868
|
-
real?.takeFullSnapshot?.();
|
|
4869
|
-
} catch (e) {
|
|
4870
|
-
// ignore
|
|
4871
|
-
}
|
|
4872
|
-
};
|
|
4873
|
-
if (_cachedRRWeb) call();else _queuedCalls.push(call);
|
|
4874
|
-
};
|
|
4875
|
-
// Use a safe global target (prefer `win`, fallback to globalThis)
|
|
4876
4788
|
const _target = win ?? globalThis;
|
|
4877
4789
|
_target.__PosthogExtensions__ = _target.__PosthogExtensions__ || {};
|
|
4878
|
-
|
|
4879
|
-
// builds that execute this file don't require rrweb at module evaluation time.
|
|
4880
|
-
_target.__PosthogExtensions__.rrweb = _target.__PosthogExtensions__.rrweb || {
|
|
4881
|
-
record: rrwebRecordProxy
|
|
4882
|
-
};
|
|
4883
|
-
// Provide initSessionRecording if not present — return a new LazyLoadedSessionRecording when called
|
|
4790
|
+
_target.__PosthogExtensions__.rrweb = _target.__PosthogExtensions__.rrweb || {};
|
|
4884
4791
|
_target.__PosthogExtensions__.initSessionRecording = _target.__PosthogExtensions__.initSessionRecording || (instance => {
|
|
4885
4792
|
const factory = _target.__PosthogExtensions__._initSessionRecordingFactory;
|
|
4886
4793
|
if (factory) {
|
|
4887
4794
|
return factory(instance);
|
|
4888
4795
|
}
|
|
4889
|
-
// If no factory is registered yet, return undefined — callers should handle lazy-loading.
|
|
4890
4796
|
return undefined;
|
|
4891
4797
|
});
|
|
4892
|
-
// Provide a no-op loadExternalDependency that calls the callback immediately (since rrweb is bundled)
|
|
4893
4798
|
_target.__PosthogExtensions__.loadExternalDependency = _target.__PosthogExtensions__.loadExternalDependency || ((instance, scriptName, cb) => {
|
|
4894
4799
|
if (cb) cb(undefined);
|
|
4895
4800
|
});
|
|
4896
|
-
// Provide rrwebPlugins object with network plugin factory if not present
|
|
4897
4801
|
_target.__PosthogExtensions__.rrwebPlugins = _target.__PosthogExtensions__.rrwebPlugins || {};
|
|
4898
|
-
// Default to undefined; the lazy-loaded recorder will register the real factory when it initializes.
|
|
4899
4802
|
_target.__PosthogExtensions__.rrwebPlugins.getRecordNetworkPlugin = _target.__PosthogExtensions__.rrwebPlugins.getRecordNetworkPlugin || (() => undefined);
|
|
4900
4803
|
|
|
4901
4804
|
// Type definitions copied from @rrweb/types@2.0.0-alpha.17 and rrweb-snapshot@2.0.0-alpha.17
|
|
@@ -6150,10 +6053,8 @@ var leanbase = (function () {
|
|
|
6150
6053
|
return event;
|
|
6151
6054
|
}
|
|
6152
6055
|
};
|
|
6153
|
-
const configuredBucketSize = this._options.bucketSize ?? 100;
|
|
6154
|
-
const effectiveBucketSize = Math.max(configuredBucketSize - 1, 1);
|
|
6155
6056
|
this._rateLimiter = new BucketedRateLimiter({
|
|
6156
|
-
bucketSize:
|
|
6057
|
+
bucketSize: this._options.bucketSize ?? 100,
|
|
6157
6058
|
refillRate: this._options.refillRate ?? 10,
|
|
6158
6059
|
refillInterval: 1000,
|
|
6159
6060
|
// one second
|
|
@@ -6349,6 +6250,23 @@ var leanbase = (function () {
|
|
|
6349
6250
|
get sessionId() {
|
|
6350
6251
|
return this._sessionId;
|
|
6351
6252
|
}
|
|
6253
|
+
_disablePermanently(reason, error) {
|
|
6254
|
+
this._permanentlyDisabled = true;
|
|
6255
|
+
this._isFullyReady = false;
|
|
6256
|
+
this._mutationThrottler?.stop();
|
|
6257
|
+
this._mutationThrottler = undefined;
|
|
6258
|
+
this._queuedRRWebEvents = [];
|
|
6259
|
+
this._recording = undefined;
|
|
6260
|
+
this._stopRrweb = undefined;
|
|
6261
|
+
if (!this._loggedPermanentlyDisabled) {
|
|
6262
|
+
this._loggedPermanentlyDisabled = true;
|
|
6263
|
+
if (error) {
|
|
6264
|
+
logger.error(`replay disabled: ${reason}`, error);
|
|
6265
|
+
} else {
|
|
6266
|
+
logger.error(`replay disabled: ${reason}`);
|
|
6267
|
+
}
|
|
6268
|
+
}
|
|
6269
|
+
}
|
|
6352
6270
|
get _sessionManager() {
|
|
6353
6271
|
if (!this._instance.sessionManager) {
|
|
6354
6272
|
throw new Error(LOGGER_PREFIX$1 + ' must be started with a valid sessionManager.');
|
|
@@ -6380,6 +6298,8 @@ var leanbase = (function () {
|
|
|
6380
6298
|
*/
|
|
6381
6299
|
this._forceAllowLocalhostNetworkCapture = false;
|
|
6382
6300
|
this._stopRrweb = undefined;
|
|
6301
|
+
this._permanentlyDisabled = false;
|
|
6302
|
+
this._loggedPermanentlyDisabled = false;
|
|
6383
6303
|
this._lastActivityTimestamp = Date.now();
|
|
6384
6304
|
/**
|
|
6385
6305
|
* and a queue - that contains rrweb events that we want to send to rrweb, but rrweb wasn't able to accept them yet
|
|
@@ -6569,6 +6489,9 @@ var leanbase = (function () {
|
|
|
6569
6489
|
}
|
|
6570
6490
|
}
|
|
6571
6491
|
_tryAddCustomEvent(tag, payload) {
|
|
6492
|
+
if (!this.isStarted || !this._recording) {
|
|
6493
|
+
return false;
|
|
6494
|
+
}
|
|
6572
6495
|
const rrwebRecord = getRRWebRecord();
|
|
6573
6496
|
if (!rrwebRecord || typeof rrwebRecord.addCustomEvent !== 'function') {
|
|
6574
6497
|
return false;
|
|
@@ -6598,6 +6521,10 @@ var leanbase = (function () {
|
|
|
6598
6521
|
}
|
|
6599
6522
|
}
|
|
6600
6523
|
_processQueuedEvents() {
|
|
6524
|
+
if (!this.isStarted || !this._recording) {
|
|
6525
|
+
this._queuedRRWebEvents = [];
|
|
6526
|
+
return;
|
|
6527
|
+
}
|
|
6601
6528
|
if (this._queuedRRWebEvents.length) {
|
|
6602
6529
|
// if rrweb isn't ready to accept events earlier, then we queued them up.
|
|
6603
6530
|
// now that `emit` has been called rrweb should be ready to accept them.
|
|
@@ -6619,6 +6546,9 @@ var leanbase = (function () {
|
|
|
6619
6546
|
}
|
|
6620
6547
|
}
|
|
6621
6548
|
_tryTakeFullSnapshot() {
|
|
6549
|
+
if (!this.isStarted || !this._recording) {
|
|
6550
|
+
return false;
|
|
6551
|
+
}
|
|
6622
6552
|
const rrwebRecord = getRRWebRecord();
|
|
6623
6553
|
if (!rrwebRecord || typeof rrwebRecord.takeFullSnapshot !== 'function') {
|
|
6624
6554
|
return false;
|
|
@@ -6632,6 +6562,9 @@ var leanbase = (function () {
|
|
|
6632
6562
|
return this._instance.config.session_recording?.full_snapshot_interval_millis ?? FIVE_MINUTES;
|
|
6633
6563
|
}
|
|
6634
6564
|
_scheduleFullSnapshot() {
|
|
6565
|
+
if (!this.isStarted || !this._recording) {
|
|
6566
|
+
return;
|
|
6567
|
+
}
|
|
6635
6568
|
if (this._fullSnapshotTimer) {
|
|
6636
6569
|
clearInterval(this._fullSnapshotTimer);
|
|
6637
6570
|
}
|
|
@@ -6648,6 +6581,9 @@ var leanbase = (function () {
|
|
|
6648
6581
|
}, interval);
|
|
6649
6582
|
}
|
|
6650
6583
|
_pauseRecording() {
|
|
6584
|
+
if (!this.isStarted || !this._recording) {
|
|
6585
|
+
return;
|
|
6586
|
+
}
|
|
6651
6587
|
// we check _urlBlocked not status, since more than one thing can affect status
|
|
6652
6588
|
if (this._urlTriggerMatching.urlBlocked) {
|
|
6653
6589
|
return;
|
|
@@ -6665,6 +6601,9 @@ var leanbase = (function () {
|
|
|
6665
6601
|
});
|
|
6666
6602
|
}
|
|
6667
6603
|
_resumeRecording() {
|
|
6604
|
+
if (!this.isStarted || !this._recording) {
|
|
6605
|
+
return;
|
|
6606
|
+
}
|
|
6668
6607
|
// we check _urlBlocked not status, since more than one thing can affect status
|
|
6669
6608
|
if (!this._urlTriggerMatching.urlBlocked) {
|
|
6670
6609
|
return;
|
|
@@ -6678,6 +6617,9 @@ var leanbase = (function () {
|
|
|
6678
6617
|
logger.info('recording resumed');
|
|
6679
6618
|
}
|
|
6680
6619
|
_activateTrigger(triggerType) {
|
|
6620
|
+
if (!this.isStarted || !this._recording || !this._isFullyReady) {
|
|
6621
|
+
return;
|
|
6622
|
+
}
|
|
6681
6623
|
if (this._triggerMatching.triggerStatus(this.sessionId) === TRIGGER_PENDING) {
|
|
6682
6624
|
// status is stored separately for URL and event triggers
|
|
6683
6625
|
this._instance?.persistence?.register({
|
|
@@ -6688,7 +6630,7 @@ var leanbase = (function () {
|
|
|
6688
6630
|
}
|
|
6689
6631
|
}
|
|
6690
6632
|
get isStarted() {
|
|
6691
|
-
return !!this.
|
|
6633
|
+
return !!this._recording?.stop;
|
|
6692
6634
|
}
|
|
6693
6635
|
get _remoteConfig() {
|
|
6694
6636
|
const persistedConfig = this._instance.get_property(SESSION_RECORDING_REMOTE_CONFIG);
|
|
@@ -6699,6 +6641,9 @@ var leanbase = (function () {
|
|
|
6699
6641
|
return parsedConfig;
|
|
6700
6642
|
}
|
|
6701
6643
|
async start(startReason) {
|
|
6644
|
+
if (this._permanentlyDisabled) {
|
|
6645
|
+
return;
|
|
6646
|
+
}
|
|
6702
6647
|
this._isFullyReady = false;
|
|
6703
6648
|
const config = this._remoteConfig;
|
|
6704
6649
|
if (!config) {
|
|
@@ -6724,8 +6669,14 @@ var leanbase = (function () {
|
|
|
6724
6669
|
});
|
|
6725
6670
|
this._urlTriggerMatching.onConfig(config);
|
|
6726
6671
|
this._eventTriggerMatching.onConfig(config);
|
|
6727
|
-
|
|
6728
|
-
this.
|
|
6672
|
+
// Start rrweb first; only once we have a valid recorder do we install any listeners/timers.
|
|
6673
|
+
await this._startRecorder();
|
|
6674
|
+
// If rrweb failed to load/start, do not proceed further.
|
|
6675
|
+
// This prevents installing listeners that assume rrweb is active.
|
|
6676
|
+
if (!this.isStarted) {
|
|
6677
|
+
return;
|
|
6678
|
+
}
|
|
6679
|
+
// Now that rrweb has started, we can safely install replay side-effects.
|
|
6729
6680
|
this._linkedFlagMatching.onConfig(config, (flag, variant) => {
|
|
6730
6681
|
this._reportStarted('linked_flag_matched', {
|
|
6731
6682
|
flag,
|
|
@@ -6733,12 +6684,8 @@ var leanbase = (function () {
|
|
|
6733
6684
|
});
|
|
6734
6685
|
});
|
|
6735
6686
|
this._makeSamplingDecision(this.sessionId);
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
// This prevents installing listeners that assume rrweb is active.
|
|
6739
|
-
if (!this.isStarted) {
|
|
6740
|
-
return;
|
|
6741
|
-
}
|
|
6687
|
+
this._removeEventTriggerCaptureHook?.();
|
|
6688
|
+
this._addEventTriggerListener();
|
|
6742
6689
|
// Only start processing rrweb emits once the ingestion endpoint is available.
|
|
6743
6690
|
// If it isn't available, we must degrade to a no-op (never crash the host app).
|
|
6744
6691
|
this._isFullyReady = this._canCaptureSnapshots();
|
|
@@ -6817,7 +6764,8 @@ var leanbase = (function () {
|
|
|
6817
6764
|
this._mutationThrottler?.stop();
|
|
6818
6765
|
// Clear any queued rrweb events to prevent memory leaks from closures
|
|
6819
6766
|
this._queuedRRWebEvents = [];
|
|
6820
|
-
this.
|
|
6767
|
+
this._recording?.stop?.();
|
|
6768
|
+
this._recording = undefined;
|
|
6821
6769
|
this._stopRrweb = undefined;
|
|
6822
6770
|
this._isFullyReady = false;
|
|
6823
6771
|
logger.info('stopped');
|
|
@@ -6837,8 +6785,12 @@ var leanbase = (function () {
|
|
|
6837
6785
|
return !!this._snapshotIngestionUrl();
|
|
6838
6786
|
}
|
|
6839
6787
|
onRRwebEmit(rawEvent) {
|
|
6788
|
+
// First-line invariant gate: drop everything unless replay is truly started.
|
|
6789
|
+
if (!this.isStarted || !this._recording) {
|
|
6790
|
+
return;
|
|
6791
|
+
}
|
|
6840
6792
|
// Never process rrweb emits until we're fully ready.
|
|
6841
|
-
if (!this._isFullyReady
|
|
6793
|
+
if (!this._isFullyReady) {
|
|
6842
6794
|
return;
|
|
6843
6795
|
}
|
|
6844
6796
|
try {
|
|
@@ -6943,6 +6895,9 @@ var leanbase = (function () {
|
|
|
6943
6895
|
});
|
|
6944
6896
|
}
|
|
6945
6897
|
overrideLinkedFlag() {
|
|
6898
|
+
if (!this.isStarted || !this._recording || !this._isFullyReady) {
|
|
6899
|
+
return;
|
|
6900
|
+
}
|
|
6946
6901
|
this._linkedFlagMatching.linkedFlagSeen = true;
|
|
6947
6902
|
this._tryTakeFullSnapshot();
|
|
6948
6903
|
this._reportStarted('linked_flag_overridden');
|
|
@@ -6954,6 +6909,9 @@ var leanbase = (function () {
|
|
|
6954
6909
|
* instead call `posthog.startSessionRecording({sampling: true})`
|
|
6955
6910
|
* */
|
|
6956
6911
|
overrideSampling() {
|
|
6912
|
+
if (!this.isStarted || !this._recording || !this._isFullyReady) {
|
|
6913
|
+
return;
|
|
6914
|
+
}
|
|
6957
6915
|
this._instance.persistence?.register({
|
|
6958
6916
|
// short-circuits the `makeSamplingDecision` function in the session recording module
|
|
6959
6917
|
[SESSION_RECORDING_IS_SAMPLED]: this.sessionId
|
|
@@ -6968,6 +6926,9 @@ var leanbase = (function () {
|
|
|
6968
6926
|
* instead call `posthog.startSessionRecording({trigger: 'url' | 'event'})`
|
|
6969
6927
|
* */
|
|
6970
6928
|
overrideTrigger(triggerType) {
|
|
6929
|
+
if (!this.isStarted || !this._recording || !this._isFullyReady) {
|
|
6930
|
+
return;
|
|
6931
|
+
}
|
|
6971
6932
|
this._activateTrigger(triggerType);
|
|
6972
6933
|
}
|
|
6973
6934
|
_clearFlushBufferTimer() {
|
|
@@ -7100,6 +7061,9 @@ var leanbase = (function () {
|
|
|
7100
7061
|
return this._buffer;
|
|
7101
7062
|
}
|
|
7102
7063
|
_reportStarted(startReason, tagPayload) {
|
|
7064
|
+
if (!this.isStarted || !this._recording) {
|
|
7065
|
+
return;
|
|
7066
|
+
}
|
|
7103
7067
|
this._instance.registerForSession({
|
|
7104
7068
|
$session_recording_start_reason: startReason
|
|
7105
7069
|
});
|
|
@@ -7243,7 +7207,7 @@ var leanbase = (function () {
|
|
|
7243
7207
|
};
|
|
7244
7208
|
}
|
|
7245
7209
|
async _startRecorder() {
|
|
7246
|
-
if (this.
|
|
7210
|
+
if (this._permanentlyDisabled || this._recording) {
|
|
7247
7211
|
return;
|
|
7248
7212
|
}
|
|
7249
7213
|
// rrweb config info: https://github.com/rrweb-io/rrweb/blob/7d5d0033258d6c29599fb08412202d9a2c7b9413/src/record/index.ts#L28
|
|
@@ -7305,27 +7269,18 @@ var leanbase = (function () {
|
|
|
7305
7269
|
rrwebRecord = loaded ?? undefined;
|
|
7306
7270
|
}
|
|
7307
7271
|
if (!rrwebRecord) {
|
|
7308
|
-
|
|
7272
|
+
this._disablePermanently('rrweb record function unavailable');
|
|
7309
7273
|
return;
|
|
7310
7274
|
}
|
|
7311
|
-
this._mutationThrottler = this._mutationThrottler ?? new MutationThrottler(rrwebRecord, {
|
|
7312
|
-
refillRate: this._instance.config.session_recording?.__mutationThrottlerRefillRate,
|
|
7313
|
-
bucketSize: this._instance.config.session_recording?.__mutationThrottlerBucketSize,
|
|
7314
|
-
onBlockedNode: (id, node) => {
|
|
7315
|
-
const message = `Too many mutations on node '${id}'. Rate limiting. This could be due to SVG animations or something similar`;
|
|
7316
|
-
logger.info(message, {
|
|
7317
|
-
node: node
|
|
7318
|
-
});
|
|
7319
|
-
this.log(LOGGER_PREFIX$1 + ' ' + message, 'warn');
|
|
7320
|
-
}
|
|
7321
|
-
});
|
|
7322
7275
|
const activePlugins = this._gatherRRWebPlugins();
|
|
7276
|
+
let stopHandler;
|
|
7323
7277
|
try {
|
|
7324
|
-
|
|
7278
|
+
stopHandler = rrwebRecord({
|
|
7325
7279
|
emit: event => {
|
|
7326
7280
|
try {
|
|
7327
7281
|
this.onRRwebEmit(event);
|
|
7328
7282
|
} catch (e) {
|
|
7283
|
+
// never throw from rrweb emit handler
|
|
7329
7284
|
logger.error('error in rrweb emit handler', e);
|
|
7330
7285
|
}
|
|
7331
7286
|
},
|
|
@@ -7333,10 +7288,30 @@ var leanbase = (function () {
|
|
|
7333
7288
|
...sessionRecordingOptions
|
|
7334
7289
|
});
|
|
7335
7290
|
} catch (e) {
|
|
7336
|
-
|
|
7337
|
-
|
|
7291
|
+
this._disablePermanently('rrweb recorder threw during initialization', e);
|
|
7292
|
+
return;
|
|
7293
|
+
}
|
|
7294
|
+
if (typeof stopHandler !== 'function') {
|
|
7295
|
+
this._disablePermanently('rrweb recorder returned an invalid stop handler');
|
|
7338
7296
|
return;
|
|
7339
7297
|
}
|
|
7298
|
+
// Mark replay started only after rrweb has successfully returned a valid stop handler.
|
|
7299
|
+
this._recording = {
|
|
7300
|
+
stop: stopHandler
|
|
7301
|
+
};
|
|
7302
|
+
this._stopRrweb = stopHandler;
|
|
7303
|
+
// Only create mutation throttler once replay is truly started.
|
|
7304
|
+
this._mutationThrottler = this._mutationThrottler ?? new MutationThrottler(rrwebRecord, {
|
|
7305
|
+
refillRate: this._instance.config.session_recording?.__mutationThrottlerRefillRate,
|
|
7306
|
+
bucketSize: this._instance.config.session_recording?.__mutationThrottlerBucketSize,
|
|
7307
|
+
onBlockedNode: (id, node) => {
|
|
7308
|
+
const message = `Too many mutations on node '${id}'. Rate limiting. This could be due to SVG animations or something similar`;
|
|
7309
|
+
logger.info(message, {
|
|
7310
|
+
node: node
|
|
7311
|
+
});
|
|
7312
|
+
this.log(LOGGER_PREFIX$1 + ' ' + message, 'warn');
|
|
7313
|
+
}
|
|
7314
|
+
});
|
|
7340
7315
|
// We reset the last activity timestamp, resetting the idle timer
|
|
7341
7316
|
this._lastActivityTimestamp = Date.now();
|
|
7342
7317
|
// stay unknown if we're not sure if we're idle or not
|
|
@@ -7366,10 +7341,6 @@ var leanbase = (function () {
|
|
|
7366
7341
|
get started() {
|
|
7367
7342
|
return !!this._lazyLoadedSessionRecording?.isStarted;
|
|
7368
7343
|
}
|
|
7369
|
-
/**
|
|
7370
|
-
* defaults to buffering mode until a flags response is received
|
|
7371
|
-
* once a flags response is received status can be disabled, active or sampled
|
|
7372
|
-
*/
|
|
7373
7344
|
get status() {
|
|
7374
7345
|
if (this._lazyLoadedSessionRecording) {
|
|
7375
7346
|
return this._lazyLoadedSessionRecording.status;
|
|
@@ -7383,7 +7354,6 @@ var leanbase = (function () {
|
|
|
7383
7354
|
this._instance = _instance;
|
|
7384
7355
|
this._forceAllowLocalhostNetworkCapture = false;
|
|
7385
7356
|
this._receivedFlags = false;
|
|
7386
|
-
this._serverRecordingEnabled = false;
|
|
7387
7357
|
this._persistFlagsOnSessionListener = undefined;
|
|
7388
7358
|
if (!this._instance.sessionManager) {
|
|
7389
7359
|
log.error('started without valid sessionManager');
|
|
@@ -7406,26 +7376,14 @@ var leanbase = (function () {
|
|
|
7406
7376
|
const canRunReplay = !isUndefined(Object.assign) && !isUndefined(Array.from);
|
|
7407
7377
|
if (this._isRecordingEnabled && canRunReplay) {
|
|
7408
7378
|
this._lazyLoadAndStart(startReason);
|
|
7409
|
-
log.info('starting');
|
|
7410
7379
|
} else {
|
|
7411
7380
|
this.stopRecording();
|
|
7412
7381
|
}
|
|
7413
7382
|
}
|
|
7414
|
-
/**
|
|
7415
|
-
* session recording waits until it receives remote config before loading the script
|
|
7416
|
-
* this is to ensure we can control the script name remotely
|
|
7417
|
-
* and because we wait until we have local and remote config to determine if we should start at all
|
|
7418
|
-
* if start is called and there is no remote config then we wait until there is
|
|
7419
|
-
*/
|
|
7420
7383
|
_lazyLoadAndStart(startReason) {
|
|
7421
|
-
// by checking `_isRecordingEnabled` here we know that
|
|
7422
|
-
// we have stored remote config and client config to read
|
|
7423
|
-
// replay waits for both local and remote config before starting
|
|
7424
7384
|
if (!this._isRecordingEnabled) {
|
|
7425
7385
|
return;
|
|
7426
7386
|
}
|
|
7427
|
-
// If extensions provide a loader, use it. Otherwise fallback to the local _onScriptLoaded which
|
|
7428
|
-
// will create the local LazyLoadedSessionRecording (so tests that mock it work correctly).
|
|
7429
7387
|
const loader = assignableWindow.__PosthogExtensions__?.loadExternalDependency;
|
|
7430
7388
|
if (typeof loader === 'function') {
|
|
7431
7389
|
loader(this._instance, this._scriptName, err => {
|
|
@@ -7480,16 +7438,10 @@ var leanbase = (function () {
|
|
|
7480
7438
|
});
|
|
7481
7439
|
};
|
|
7482
7440
|
persistResponse();
|
|
7483
|
-
// in case we see multiple flags responses, we should only use the response from the most recent one
|
|
7484
7441
|
this._persistFlagsOnSessionListener?.();
|
|
7485
|
-
// we 100% know there is a session manager by this point
|
|
7486
7442
|
this._persistFlagsOnSessionListener = this._instance.sessionManager?.onSessionId(persistResponse);
|
|
7487
7443
|
}
|
|
7488
7444
|
}
|
|
7489
|
-
_clearRemoteConfig() {
|
|
7490
|
-
this._instance.persistence?.unregister(SESSION_RECORDING_REMOTE_CONFIG);
|
|
7491
|
-
this._resetSampling();
|
|
7492
|
-
}
|
|
7493
7445
|
onRemoteConfig(response) {
|
|
7494
7446
|
if (!('sessionRecording' in response)) {
|
|
7495
7447
|
// if sessionRecording is not in the response, we do nothing
|
|
@@ -7498,12 +7450,8 @@ var leanbase = (function () {
|
|
|
7498
7450
|
}
|
|
7499
7451
|
this._receivedFlags = true;
|
|
7500
7452
|
if (response.sessionRecording === false) {
|
|
7501
|
-
this._serverRecordingEnabled = false;
|
|
7502
|
-
this._clearRemoteConfig();
|
|
7503
|
-
this.stopRecording();
|
|
7504
7453
|
return;
|
|
7505
7454
|
}
|
|
7506
|
-
this._serverRecordingEnabled = true;
|
|
7507
7455
|
this._persistRemoteConfig(response);
|
|
7508
7456
|
this.startIfEnabledOrStop();
|
|
7509
7457
|
}
|
|
@@ -7564,54 +7512,23 @@ var leanbase = (function () {
|
|
|
7564
7512
|
onRRwebEmit(rawEvent) {
|
|
7565
7513
|
this._lazyLoadedSessionRecording?.onRRwebEmit?.(rawEvent);
|
|
7566
7514
|
}
|
|
7567
|
-
/**
|
|
7568
|
-
* this ignores the linked flag config and (if other conditions are met) causes capture to start
|
|
7569
|
-
*
|
|
7570
|
-
* It is not usual to call this directly,
|
|
7571
|
-
* instead call `posthog.startSessionRecording({linked_flag: true})`
|
|
7572
|
-
* */
|
|
7573
7515
|
overrideLinkedFlag() {
|
|
7574
7516
|
// TODO what if this gets called before lazy loading is done
|
|
7575
7517
|
this._lazyLoadedSessionRecording?.overrideLinkedFlag();
|
|
7576
7518
|
}
|
|
7577
|
-
/**
|
|
7578
|
-
* this ignores the sampling config and (if other conditions are met) causes capture to start
|
|
7579
|
-
*
|
|
7580
|
-
* It is not usual to call this directly,
|
|
7581
|
-
* instead call `posthog.startSessionRecording({sampling: true})`
|
|
7582
|
-
* */
|
|
7583
7519
|
overrideSampling() {
|
|
7584
7520
|
// TODO what if this gets called before lazy loading is done
|
|
7585
7521
|
this._lazyLoadedSessionRecording?.overrideSampling();
|
|
7586
7522
|
}
|
|
7587
|
-
/**
|
|
7588
|
-
* this ignores the URL/Event trigger config and (if other conditions are met) causes capture to start
|
|
7589
|
-
*
|
|
7590
|
-
* It is not usual to call this directly,
|
|
7591
|
-
* instead call `posthog.startSessionRecording({trigger: 'url' | 'event'})`
|
|
7592
|
-
* */
|
|
7593
7523
|
overrideTrigger(triggerType) {
|
|
7594
7524
|
// TODO what if this gets called before lazy loading is done
|
|
7595
7525
|
this._lazyLoadedSessionRecording?.overrideTrigger(triggerType);
|
|
7596
7526
|
}
|
|
7597
|
-
/*
|
|
7598
|
-
* whenever we capture an event, we add these properties to the event
|
|
7599
|
-
* these are used to debug issues with the session recording
|
|
7600
|
-
* when looking at the event feed for a session
|
|
7601
|
-
*/
|
|
7602
7527
|
get sdkDebugProperties() {
|
|
7603
7528
|
return this._lazyLoadedSessionRecording?.sdkDebugProperties || {
|
|
7604
7529
|
$recording_status: this.status
|
|
7605
7530
|
};
|
|
7606
7531
|
}
|
|
7607
|
-
/**
|
|
7608
|
-
* This adds a custom event to the session recording
|
|
7609
|
-
*
|
|
7610
|
-
* It is not intended for arbitrary public use - playback only displays known custom events
|
|
7611
|
-
* And is exposed on the public interface only so that other parts of the SDK are able to use it
|
|
7612
|
-
*
|
|
7613
|
-
* if you are calling this from client code, you're probably looking for `posthog.capture('$custom_event', {...})`
|
|
7614
|
-
*/
|
|
7615
7532
|
tryAddCustomEvent(tag, payload) {
|
|
7616
7533
|
return !!this._lazyLoadedSessionRecording?.tryAddCustomEvent(tag, payload);
|
|
7617
7534
|
}
|