@leanbase-giangnd/js 0.1.5 → 0.2.3
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 +270 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.mjs +270 -23
- package/dist/index.mjs.map +1 -1
- package/dist/leanbase.iife.js +5418 -92
- package/dist/leanbase.iife.js.map +1 -1
- package/package.json +1 -1
- package/src/extensions/replay/extension-shim.ts +102 -3
- package/src/extensions/replay/external/lazy-loaded-session-recorder.ts +66 -13
- package/src/extensions/replay/session-recording.ts +59 -2
- package/src/leanbase.ts +68 -6
- package/src/utils/index.ts +3 -0
- package/src/version.ts +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var core = require('@posthog/core');
|
|
4
|
-
var record = require('@rrweb/record');
|
|
5
4
|
var fflate = require('fflate');
|
|
6
5
|
|
|
7
6
|
const breaker = {};
|
|
@@ -17,6 +16,8 @@ global?.fetch;
|
|
|
17
16
|
global?.XMLHttpRequest && 'withCredentials' in new global.XMLHttpRequest() ? global.XMLHttpRequest : undefined;
|
|
18
17
|
global?.AbortController;
|
|
19
18
|
const userAgent = navigator$1?.userAgent;
|
|
19
|
+
// assignableWindow mirrors browser package's assignableWindow for extension loading shims
|
|
20
|
+
const assignableWindow = win ?? {};
|
|
20
21
|
function eachArray(obj, iterator, thisArg) {
|
|
21
22
|
if (core.isArray(obj)) {
|
|
22
23
|
if (nativeForEach && obj.forEach === nativeForEach) {
|
|
@@ -1170,7 +1171,7 @@ const detectDeviceType = function (user_agent) {
|
|
|
1170
1171
|
}
|
|
1171
1172
|
};
|
|
1172
1173
|
|
|
1173
|
-
var version = "0.
|
|
1174
|
+
var version = "0.2.3";
|
|
1174
1175
|
var packageInfo = {
|
|
1175
1176
|
version: version};
|
|
1176
1177
|
|
|
@@ -3108,12 +3109,101 @@ const isLikelyBot = function (navigator, customBlockedUserAgents) {
|
|
|
3108
3109
|
// It would be very bad if posthog-js caused a permission prompt to appear on every page load.
|
|
3109
3110
|
};
|
|
3110
3111
|
|
|
3112
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
3113
|
+
// We avoid importing '@rrweb/record' at module load time to prevent IIFE builds
|
|
3114
|
+
// from requiring a top-level global. Instead, expose a lazy proxy that will
|
|
3115
|
+
// dynamically import the module the first time it's used.
|
|
3116
|
+
let _cachedRRWeb = null;
|
|
3117
|
+
async function _loadRRWebModule() {
|
|
3118
|
+
if (_cachedRRWeb) return _cachedRRWeb;
|
|
3119
|
+
try {
|
|
3120
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3121
|
+
const mod = await import('@rrweb/record');
|
|
3122
|
+
_cachedRRWeb = mod;
|
|
3123
|
+
return _cachedRRWeb;
|
|
3124
|
+
} catch (e) {
|
|
3125
|
+
return null;
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
// queue for method calls before rrweb loads
|
|
3129
|
+
const _queuedCalls = [];
|
|
3130
|
+
// Create a proxy function that delegates to the real rrweb.record when called
|
|
3131
|
+
const rrwebRecordProxy = function (...args) {
|
|
3132
|
+
let realStop;
|
|
3133
|
+
let calledReal = false;
|
|
3134
|
+
// Start loading asynchronously and call the real record when available
|
|
3135
|
+
void (async () => {
|
|
3136
|
+
const mod = await _loadRRWebModule();
|
|
3137
|
+
const real = mod && (mod.record ?? mod.default?.record);
|
|
3138
|
+
if (real) {
|
|
3139
|
+
try {
|
|
3140
|
+
calledReal = true;
|
|
3141
|
+
realStop = real(...args);
|
|
3142
|
+
// flush any queued calls that were waiting for rrweb
|
|
3143
|
+
while (_queuedCalls.length) {
|
|
3144
|
+
try {
|
|
3145
|
+
const fn = _queuedCalls.shift();
|
|
3146
|
+
fn();
|
|
3147
|
+
} catch (e) {
|
|
3148
|
+
// ignore
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
} catch (e) {
|
|
3152
|
+
// ignore
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
})();
|
|
3156
|
+
// return a stop function that will call the real stop when available
|
|
3157
|
+
return () => {
|
|
3158
|
+
if (realStop) {
|
|
3159
|
+
try {
|
|
3160
|
+
realStop();
|
|
3161
|
+
} catch (e) {
|
|
3162
|
+
// ignore
|
|
3163
|
+
}
|
|
3164
|
+
} else if (!calledReal) {
|
|
3165
|
+
// If rrweb hasn't been initialised yet, queue a stop request that will
|
|
3166
|
+
// call the real stop once available.
|
|
3167
|
+
_queuedCalls.push(() => {
|
|
3168
|
+
try {
|
|
3169
|
+
realStop?.();
|
|
3170
|
+
} catch (e) {
|
|
3171
|
+
// ignore
|
|
3172
|
+
}
|
|
3173
|
+
});
|
|
3174
|
+
}
|
|
3175
|
+
};
|
|
3176
|
+
};
|
|
3177
|
+
// methods that can be called on the rrweb.record object - queue until real module is available
|
|
3178
|
+
rrwebRecordProxy.addCustomEvent = function (tag, payload) {
|
|
3179
|
+
const call = () => {
|
|
3180
|
+
try {
|
|
3181
|
+
const real = _cachedRRWeb && (_cachedRRWeb.record ?? _cachedRRWeb.default?.record);
|
|
3182
|
+
real?.addCustomEvent?.(tag, payload);
|
|
3183
|
+
} catch (e) {
|
|
3184
|
+
// ignore
|
|
3185
|
+
}
|
|
3186
|
+
};
|
|
3187
|
+
if (_cachedRRWeb) call();else _queuedCalls.push(call);
|
|
3188
|
+
};
|
|
3189
|
+
rrwebRecordProxy.takeFullSnapshot = function () {
|
|
3190
|
+
const call = () => {
|
|
3191
|
+
try {
|
|
3192
|
+
const real = _cachedRRWeb && (_cachedRRWeb.record ?? _cachedRRWeb.default?.record);
|
|
3193
|
+
real?.takeFullSnapshot?.();
|
|
3194
|
+
} catch (e) {
|
|
3195
|
+
// ignore
|
|
3196
|
+
}
|
|
3197
|
+
};
|
|
3198
|
+
if (_cachedRRWeb) call();else _queuedCalls.push(call);
|
|
3199
|
+
};
|
|
3111
3200
|
// Use a safe global target (prefer `win`, fallback to globalThis)
|
|
3112
3201
|
const _target = win ?? globalThis;
|
|
3113
3202
|
_target.__PosthogExtensions__ = _target.__PosthogExtensions__ || {};
|
|
3114
|
-
// Expose rrweb.record under the same contract
|
|
3203
|
+
// Expose rrweb.record under the same contract. We provide a lazy proxy so
|
|
3204
|
+
// builds that execute this file don't require rrweb at module evaluation time.
|
|
3115
3205
|
_target.__PosthogExtensions__.rrweb = _target.__PosthogExtensions__.rrweb || {
|
|
3116
|
-
record:
|
|
3206
|
+
record: rrwebRecordProxy
|
|
3117
3207
|
};
|
|
3118
3208
|
// Provide initSessionRecording if not present — return a new LazyLoadedSessionRecording when called
|
|
3119
3209
|
_target.__PosthogExtensions__.initSessionRecording = _target.__PosthogExtensions__.initSessionRecording || (instance => {
|
|
@@ -3866,7 +3956,41 @@ function getRRWebRecord() {
|
|
|
3866
3956
|
} catch {
|
|
3867
3957
|
// ignore
|
|
3868
3958
|
}
|
|
3869
|
-
return
|
|
3959
|
+
// If we've previously loaded rrweb via dynamic import, return the cached reference
|
|
3960
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3961
|
+
const cached = getRRWebRecord._cachedRRWebRecord;
|
|
3962
|
+
return cached;
|
|
3963
|
+
}
|
|
3964
|
+
async function loadRRWeb() {
|
|
3965
|
+
try {
|
|
3966
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3967
|
+
const ext = globalThis.__PosthogExtensions__;
|
|
3968
|
+
if (ext && ext.rrweb && ext.rrweb.record) {
|
|
3969
|
+
;
|
|
3970
|
+
getRRWebRecord._cachedRRWebRecord = ext.rrweb.record;
|
|
3971
|
+
return ext.rrweb.record;
|
|
3972
|
+
}
|
|
3973
|
+
// If already cached, return it
|
|
3974
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3975
|
+
const already = getRRWebRecord._cachedRRWebRecord;
|
|
3976
|
+
if (already) {
|
|
3977
|
+
return already;
|
|
3978
|
+
}
|
|
3979
|
+
// Dynamic import - let the bundler (IIFE build) include rrweb in the bundle or allow lazy-load
|
|
3980
|
+
// Note: we intentionally use a dynamic import so rrweb is not referenced at the module top-level
|
|
3981
|
+
// which would cause IIFE builds to assume a global is present at script execution.
|
|
3982
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3983
|
+
const mod = await import('@rrweb/record');
|
|
3984
|
+
const rr = mod && (mod.record ?? (mod.default && mod.default.record));
|
|
3985
|
+
if (rr) {
|
|
3986
|
+
;
|
|
3987
|
+
getRRWebRecord._cachedRRWebRecord = rr;
|
|
3988
|
+
return rr;
|
|
3989
|
+
}
|
|
3990
|
+
} catch (e) {
|
|
3991
|
+
logger.error('could not dynamically load rrweb', e);
|
|
3992
|
+
}
|
|
3993
|
+
return null;
|
|
3870
3994
|
}
|
|
3871
3995
|
function gzipToString(data) {
|
|
3872
3996
|
return fflate.strFromU8(fflate.gzipSync(fflate.strToU8(JSON.stringify(data))), true);
|
|
@@ -4289,7 +4413,7 @@ class LazyLoadedSessionRecording {
|
|
|
4289
4413
|
const parsedConfig = core.isObject(persistedConfig) ? persistedConfig : JSON.parse(persistedConfig);
|
|
4290
4414
|
return parsedConfig;
|
|
4291
4415
|
}
|
|
4292
|
-
start(startReason) {
|
|
4416
|
+
async start(startReason) {
|
|
4293
4417
|
const config = this._remoteConfig;
|
|
4294
4418
|
if (!config) {
|
|
4295
4419
|
logger.info('remote config must be stored in persistence before recording can start');
|
|
@@ -4323,7 +4447,12 @@ class LazyLoadedSessionRecording {
|
|
|
4323
4447
|
});
|
|
4324
4448
|
});
|
|
4325
4449
|
this._makeSamplingDecision(this.sessionId);
|
|
4326
|
-
this._startRecorder();
|
|
4450
|
+
await this._startRecorder();
|
|
4451
|
+
// If rrweb failed to load/start, do not proceed further.
|
|
4452
|
+
// This prevents installing listeners that assume rrweb is active.
|
|
4453
|
+
if (!this.isStarted) {
|
|
4454
|
+
return;
|
|
4455
|
+
}
|
|
4327
4456
|
// calling addEventListener multiple times is safe and will not add duplicates
|
|
4328
4457
|
addEventListener(win, 'beforeunload', this._onBeforeUnload);
|
|
4329
4458
|
addEventListener(win, 'offline', this._onOffline);
|
|
@@ -4779,7 +4908,7 @@ class LazyLoadedSessionRecording {
|
|
|
4779
4908
|
$sdk_debug_session_start: sessionStartTimestamp
|
|
4780
4909
|
};
|
|
4781
4910
|
}
|
|
4782
|
-
_startRecorder() {
|
|
4911
|
+
async _startRecorder() {
|
|
4783
4912
|
if (this._stopRrweb) {
|
|
4784
4913
|
return;
|
|
4785
4914
|
}
|
|
@@ -4835,7 +4964,12 @@ class LazyLoadedSessionRecording {
|
|
|
4835
4964
|
sessionRecordingOptions.maskTextSelector = this._masking.maskTextSelector ?? undefined;
|
|
4836
4965
|
sessionRecordingOptions.blockSelector = this._masking.blockSelector ?? undefined;
|
|
4837
4966
|
}
|
|
4838
|
-
|
|
4967
|
+
// Ensure rrweb is loaded (either via global extension or dynamic import)
|
|
4968
|
+
let rrwebRecord = getRRWebRecord();
|
|
4969
|
+
if (!rrwebRecord) {
|
|
4970
|
+
const loaded = await loadRRWeb();
|
|
4971
|
+
rrwebRecord = loaded ?? undefined;
|
|
4972
|
+
}
|
|
4839
4973
|
if (!rrwebRecord) {
|
|
4840
4974
|
logger.error('_startRecorder was called but rrwebRecord is not available. This indicates something has gone wrong.');
|
|
4841
4975
|
return;
|
|
@@ -4852,13 +4986,19 @@ class LazyLoadedSessionRecording {
|
|
|
4852
4986
|
}
|
|
4853
4987
|
});
|
|
4854
4988
|
const activePlugins = this._gatherRRWebPlugins();
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4989
|
+
try {
|
|
4990
|
+
this._stopRrweb = rrwebRecord({
|
|
4991
|
+
emit: event => {
|
|
4992
|
+
this.onRRwebEmit(event);
|
|
4993
|
+
},
|
|
4994
|
+
plugins: activePlugins,
|
|
4995
|
+
...sessionRecordingOptions
|
|
4996
|
+
});
|
|
4997
|
+
} catch (e) {
|
|
4998
|
+
logger.error('failed to start rrweb recorder', e);
|
|
4999
|
+
this._stopRrweb = undefined;
|
|
5000
|
+
return;
|
|
5001
|
+
}
|
|
4862
5002
|
// We reset the last activity timestamp, resetting the idle timer
|
|
4863
5003
|
this._lastActivityTimestamp = Date.now();
|
|
4864
5004
|
// stay unknown if we're not sure if we're idle or not
|
|
@@ -4877,6 +5017,7 @@ class LazyLoadedSessionRecording {
|
|
|
4877
5017
|
}
|
|
4878
5018
|
}
|
|
4879
5019
|
|
|
5020
|
+
/* eslint-disable posthog-js/no-direct-function-check */
|
|
4880
5021
|
const LOGGER_PREFIX = '[SessionRecording]';
|
|
4881
5022
|
const log = {
|
|
4882
5023
|
info: (...args) => logger$2.info(LOGGER_PREFIX, ...args),
|
|
@@ -4945,6 +5086,18 @@ class SessionRecording {
|
|
|
4945
5086
|
if (!this._isRecordingEnabled) {
|
|
4946
5087
|
return;
|
|
4947
5088
|
}
|
|
5089
|
+
// If extensions provide a loader, use it. Otherwise fallback to the local _onScriptLoaded which
|
|
5090
|
+
// will create the local LazyLoadedSessionRecording (so tests that mock it work correctly).
|
|
5091
|
+
const loader = assignableWindow.__PosthogExtensions__?.loadExternalDependency;
|
|
5092
|
+
if (typeof loader === 'function') {
|
|
5093
|
+
loader(this._instance, this._scriptName, err => {
|
|
5094
|
+
if (err) {
|
|
5095
|
+
return log.error('could not load recorder', err);
|
|
5096
|
+
}
|
|
5097
|
+
this._onScriptLoaded(startReason);
|
|
5098
|
+
});
|
|
5099
|
+
return;
|
|
5100
|
+
}
|
|
4948
5101
|
this._onScriptLoaded(startReason);
|
|
4949
5102
|
}
|
|
4950
5103
|
stopRecording() {
|
|
@@ -5023,12 +5176,47 @@ class SessionRecording {
|
|
|
5023
5176
|
logger$2.warn('log called before recorder was ready');
|
|
5024
5177
|
}
|
|
5025
5178
|
}
|
|
5179
|
+
get _scriptName() {
|
|
5180
|
+
const remoteConfig = this._instance?.persistence?.get_property(SESSION_RECORDING_REMOTE_CONFIG);
|
|
5181
|
+
return remoteConfig?.scriptConfig?.script || 'lazy-recorder';
|
|
5182
|
+
}
|
|
5026
5183
|
_onScriptLoaded(startReason) {
|
|
5184
|
+
// If extensions provide an init function, use it. Otherwise, fall back to the local LazyLoadedSessionRecording
|
|
5185
|
+
if (assignableWindow.__PosthogExtensions__?.initSessionRecording) {
|
|
5186
|
+
if (!this._lazyLoadedSessionRecording) {
|
|
5187
|
+
const maybeRecording = assignableWindow.__PosthogExtensions__?.initSessionRecording(this._instance);
|
|
5188
|
+
if (maybeRecording && typeof maybeRecording.start === 'function') {
|
|
5189
|
+
this._lazyLoadedSessionRecording = maybeRecording;
|
|
5190
|
+
this._lazyLoadedSessionRecording._forceAllowLocalhostNetworkCapture = this._forceAllowLocalhostNetworkCapture;
|
|
5191
|
+
} else {
|
|
5192
|
+
log.warn('initSessionRecording was present but did not return a recorder instance; falling back to local recorder');
|
|
5193
|
+
}
|
|
5194
|
+
}
|
|
5195
|
+
if (this._lazyLoadedSessionRecording) {
|
|
5196
|
+
try {
|
|
5197
|
+
const maybePromise = this._lazyLoadedSessionRecording.start(startReason);
|
|
5198
|
+
if (maybePromise && typeof maybePromise.catch === 'function') {
|
|
5199
|
+
maybePromise.catch(e => logger$2.error('error starting session recording', e));
|
|
5200
|
+
}
|
|
5201
|
+
} catch (e) {
|
|
5202
|
+
logger$2.error('error starting session recording', e);
|
|
5203
|
+
}
|
|
5204
|
+
return;
|
|
5205
|
+
}
|
|
5206
|
+
}
|
|
5027
5207
|
if (!this._lazyLoadedSessionRecording) {
|
|
5028
5208
|
this._lazyLoadedSessionRecording = new LazyLoadedSessionRecording(this._instance);
|
|
5029
5209
|
this._lazyLoadedSessionRecording._forceAllowLocalhostNetworkCapture = this._forceAllowLocalhostNetworkCapture;
|
|
5030
5210
|
}
|
|
5031
|
-
|
|
5211
|
+
// start may perform a dynamic import; handle both sync and Promise returns
|
|
5212
|
+
try {
|
|
5213
|
+
const maybePromise = this._lazyLoadedSessionRecording.start(startReason);
|
|
5214
|
+
if (maybePromise && typeof maybePromise.catch === 'function') {
|
|
5215
|
+
maybePromise.catch(e => logger$2.error('error starting session recording', e));
|
|
5216
|
+
}
|
|
5217
|
+
} catch (e) {
|
|
5218
|
+
logger$2.error('error starting session recording', e);
|
|
5219
|
+
}
|
|
5032
5220
|
}
|
|
5033
5221
|
/**
|
|
5034
5222
|
* this is maintained on the public API only because it has always been on the public API
|
|
@@ -5129,6 +5317,10 @@ class Leanbase extends core.PostHogCore {
|
|
|
5129
5317
|
token
|
|
5130
5318
|
});
|
|
5131
5319
|
super(token, mergedConfig);
|
|
5320
|
+
this._remoteConfigLoadAttempted = false;
|
|
5321
|
+
this._remoteConfigResolved = false;
|
|
5322
|
+
this._featureFlagsResolved = false;
|
|
5323
|
+
this._maybeStartedSessionRecording = false;
|
|
5132
5324
|
this.personProcessingSetOncePropertiesSent = false;
|
|
5133
5325
|
this.isLoaded = false;
|
|
5134
5326
|
this.config = mergedConfig;
|
|
@@ -5152,10 +5344,20 @@ class Leanbase extends core.PostHogCore {
|
|
|
5152
5344
|
this.replayAutocapture.startIfEnabled();
|
|
5153
5345
|
if (this.sessionManager && this.config.cookieless_mode !== 'always') {
|
|
5154
5346
|
this.sessionRecording = new SessionRecording(this);
|
|
5155
|
-
this.sessionRecording.startIfEnabledOrStop();
|
|
5156
5347
|
}
|
|
5348
|
+
// Start session recording only once flags + remote config have been resolved.
|
|
5349
|
+
// This matches the PostHog browser SDK where replay activation is driven by remote config and flags.
|
|
5157
5350
|
if (this.config.preloadFeatureFlags !== false) {
|
|
5158
|
-
this.reloadFeatureFlags(
|
|
5351
|
+
this.reloadFeatureFlags({
|
|
5352
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
5353
|
+
cb: _err => {
|
|
5354
|
+
this._featureFlagsResolved = true;
|
|
5355
|
+
this._maybeStartSessionRecording();
|
|
5356
|
+
}
|
|
5357
|
+
});
|
|
5358
|
+
} else {
|
|
5359
|
+
// If feature flags preload is explicitly disabled, treat this requirement as satisfied.
|
|
5360
|
+
this._featureFlagsResolved = true;
|
|
5159
5361
|
}
|
|
5160
5362
|
this.config.loaded?.(this);
|
|
5161
5363
|
if (this.config.capture_pageview) {
|
|
@@ -5165,9 +5367,26 @@ class Leanbase extends core.PostHogCore {
|
|
|
5165
5367
|
}
|
|
5166
5368
|
}, 1);
|
|
5167
5369
|
}
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5370
|
+
const triggerRemoteConfigLoad = reason => {
|
|
5371
|
+
logger$2.info(`remote config load triggered via ${reason}`);
|
|
5372
|
+
void this.loadRemoteConfig();
|
|
5373
|
+
};
|
|
5374
|
+
if (document) {
|
|
5375
|
+
if (document.readyState === 'loading') {
|
|
5376
|
+
logger$2.info('remote config load deferred until DOMContentLoaded');
|
|
5377
|
+
const onDomReady = () => {
|
|
5378
|
+
document?.removeEventListener('DOMContentLoaded', onDomReady);
|
|
5379
|
+
triggerRemoteConfigLoad('dom');
|
|
5380
|
+
};
|
|
5381
|
+
addEventListener(document, 'DOMContentLoaded', onDomReady, {
|
|
5382
|
+
once: true
|
|
5383
|
+
});
|
|
5384
|
+
} else {
|
|
5385
|
+
triggerRemoteConfigLoad('immediate');
|
|
5386
|
+
}
|
|
5387
|
+
} else {
|
|
5388
|
+
triggerRemoteConfigLoad('no-document');
|
|
5389
|
+
}
|
|
5171
5390
|
addEventListener(window, 'onpagehide' in self ? 'pagehide' : 'unload', this.capturePageLeave.bind(this), {
|
|
5172
5391
|
passive: false
|
|
5173
5392
|
});
|
|
@@ -5204,11 +5423,19 @@ class Leanbase extends core.PostHogCore {
|
|
|
5204
5423
|
}
|
|
5205
5424
|
}
|
|
5206
5425
|
async loadRemoteConfig() {
|
|
5207
|
-
if (
|
|
5426
|
+
if (this._remoteConfigLoadAttempted) {
|
|
5427
|
+
return;
|
|
5428
|
+
}
|
|
5429
|
+
this._remoteConfigLoadAttempted = true;
|
|
5430
|
+
try {
|
|
5208
5431
|
const remoteConfig = await this.reloadRemoteConfigAsync();
|
|
5209
5432
|
if (remoteConfig) {
|
|
5210
5433
|
this.onRemoteConfig(remoteConfig);
|
|
5211
5434
|
}
|
|
5435
|
+
} finally {
|
|
5436
|
+
// Regardless of success/failure, we consider remote config "resolved" so replay isn't blocked forever.
|
|
5437
|
+
this._remoteConfigResolved = true;
|
|
5438
|
+
this._maybeStartSessionRecording();
|
|
5212
5439
|
}
|
|
5213
5440
|
}
|
|
5214
5441
|
onRemoteConfig(config) {
|
|
@@ -5221,6 +5448,26 @@ class Leanbase extends core.PostHogCore {
|
|
|
5221
5448
|
this.isRemoteConfigLoaded = true;
|
|
5222
5449
|
this.replayAutocapture?.onRemoteConfig(config);
|
|
5223
5450
|
this.sessionRecording?.onRemoteConfig(config);
|
|
5451
|
+
// Remote config has been applied; allow replay start if flags are also ready.
|
|
5452
|
+
this._remoteConfigResolved = true;
|
|
5453
|
+
this._maybeStartSessionRecording();
|
|
5454
|
+
}
|
|
5455
|
+
_maybeStartSessionRecording() {
|
|
5456
|
+
if (this._maybeStartedSessionRecording) {
|
|
5457
|
+
return;
|
|
5458
|
+
}
|
|
5459
|
+
if (!this.sessionRecording) {
|
|
5460
|
+
return;
|
|
5461
|
+
}
|
|
5462
|
+
if (!this._featureFlagsResolved || !this._remoteConfigResolved) {
|
|
5463
|
+
return;
|
|
5464
|
+
}
|
|
5465
|
+
this._maybeStartedSessionRecording = true;
|
|
5466
|
+
try {
|
|
5467
|
+
this.sessionRecording.startIfEnabledOrStop();
|
|
5468
|
+
} catch (e) {
|
|
5469
|
+
logger$2.error('Failed to start session recording', e);
|
|
5470
|
+
}
|
|
5224
5471
|
}
|
|
5225
5472
|
fetch(url, options) {
|
|
5226
5473
|
const fetchFn = core.getFetch();
|