@leanbase-giangnd/js 0.2.2 → 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 CHANGED
@@ -1171,7 +1171,7 @@ const detectDeviceType = function (user_agent) {
1171
1171
  }
1172
1172
  };
1173
1173
 
1174
- var version = "0.2.2";
1174
+ var version = "0.2.3";
1175
1175
  var packageInfo = {
1176
1176
  version: version};
1177
1177
 
@@ -4448,6 +4448,11 @@ class LazyLoadedSessionRecording {
4448
4448
  });
4449
4449
  this._makeSamplingDecision(this.sessionId);
4450
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
+ }
4451
4456
  // calling addEventListener multiple times is safe and will not add duplicates
4452
4457
  addEventListener(win, 'beforeunload', this._onBeforeUnload);
4453
4458
  addEventListener(win, 'offline', this._onOffline);
@@ -4981,13 +4986,19 @@ class LazyLoadedSessionRecording {
4981
4986
  }
4982
4987
  });
4983
4988
  const activePlugins = this._gatherRRWebPlugins();
4984
- this._stopRrweb = rrwebRecord({
4985
- emit: event => {
4986
- this.onRRwebEmit(event);
4987
- },
4988
- plugins: activePlugins,
4989
- ...sessionRecordingOptions
4990
- });
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
+ }
4991
5002
  // We reset the last activity timestamp, resetting the idle timer
4992
5003
  this._lastActivityTimestamp = Date.now();
4993
5004
  // stay unknown if we're not sure if we're idle or not
@@ -5173,18 +5184,25 @@ class SessionRecording {
5173
5184
  // If extensions provide an init function, use it. Otherwise, fall back to the local LazyLoadedSessionRecording
5174
5185
  if (assignableWindow.__PosthogExtensions__?.initSessionRecording) {
5175
5186
  if (!this._lazyLoadedSessionRecording) {
5176
- this._lazyLoadedSessionRecording = assignableWindow.__PosthogExtensions__?.initSessionRecording(this._instance);
5177
- this._lazyLoadedSessionRecording._forceAllowLocalhostNetworkCapture = this._forceAllowLocalhostNetworkCapture;
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
+ }
5178
5194
  }
5179
- try {
5180
- const maybePromise = this._lazyLoadedSessionRecording.start(startReason);
5181
- if (maybePromise && typeof maybePromise.catch === 'function') {
5182
- maybePromise.catch(e => logger$2.error('error starting session recording', e));
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);
5183
5203
  }
5184
- } catch (e) {
5185
- logger$2.error('error starting session recording', e);
5204
+ return;
5186
5205
  }
5187
- return;
5188
5206
  }
5189
5207
  if (!this._lazyLoadedSessionRecording) {
5190
5208
  this._lazyLoadedSessionRecording = new LazyLoadedSessionRecording(this._instance);
@@ -5299,6 +5317,10 @@ class Leanbase extends core.PostHogCore {
5299
5317
  token
5300
5318
  });
5301
5319
  super(token, mergedConfig);
5320
+ this._remoteConfigLoadAttempted = false;
5321
+ this._remoteConfigResolved = false;
5322
+ this._featureFlagsResolved = false;
5323
+ this._maybeStartedSessionRecording = false;
5302
5324
  this.personProcessingSetOncePropertiesSent = false;
5303
5325
  this.isLoaded = false;
5304
5326
  this.config = mergedConfig;
@@ -5322,10 +5344,20 @@ class Leanbase extends core.PostHogCore {
5322
5344
  this.replayAutocapture.startIfEnabled();
5323
5345
  if (this.sessionManager && this.config.cookieless_mode !== 'always') {
5324
5346
  this.sessionRecording = new SessionRecording(this);
5325
- this.sessionRecording.startIfEnabledOrStop();
5326
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.
5327
5350
  if (this.config.preloadFeatureFlags !== false) {
5328
- 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;
5329
5361
  }
5330
5362
  this.config.loaded?.(this);
5331
5363
  if (this.config.capture_pageview) {
@@ -5335,9 +5367,26 @@ class Leanbase extends core.PostHogCore {
5335
5367
  }
5336
5368
  }, 1);
5337
5369
  }
5338
- addEventListener(document, 'DOMContentLoaded', () => {
5339
- this.loadRemoteConfig();
5340
- });
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
+ }
5341
5390
  addEventListener(window, 'onpagehide' in self ? 'pagehide' : 'unload', this.capturePageLeave.bind(this), {
5342
5391
  passive: false
5343
5392
  });
@@ -5374,11 +5423,19 @@ class Leanbase extends core.PostHogCore {
5374
5423
  }
5375
5424
  }
5376
5425
  async loadRemoteConfig() {
5377
- if (!this.isRemoteConfigLoaded) {
5426
+ if (this._remoteConfigLoadAttempted) {
5427
+ return;
5428
+ }
5429
+ this._remoteConfigLoadAttempted = true;
5430
+ try {
5378
5431
  const remoteConfig = await this.reloadRemoteConfigAsync();
5379
5432
  if (remoteConfig) {
5380
5433
  this.onRemoteConfig(remoteConfig);
5381
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();
5382
5439
  }
5383
5440
  }
5384
5441
  onRemoteConfig(config) {
@@ -5391,6 +5448,26 @@ class Leanbase extends core.PostHogCore {
5391
5448
  this.isRemoteConfigLoaded = true;
5392
5449
  this.replayAutocapture?.onRemoteConfig(config);
5393
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
+ }
5394
5471
  }
5395
5472
  fetch(url, options) {
5396
5473
  const fetchFn = core.getFetch();