@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.d.ts CHANGED
@@ -6127,6 +6127,10 @@ declare class Leanbase extends PostHogCore {
6127
6127
  consent: ConsentManager;
6128
6128
  sessionRecording?: SessionRecording$1;
6129
6129
  isRemoteConfigLoaded?: boolean;
6130
+ private _remoteConfigLoadAttempted;
6131
+ private _remoteConfigResolved;
6132
+ private _featureFlagsResolved;
6133
+ private _maybeStartedSessionRecording;
6130
6134
  personProcessingSetOncePropertiesSent: boolean;
6131
6135
  isLoaded: boolean;
6132
6136
  initialPageviewCaptured: boolean;
@@ -6137,6 +6141,7 @@ declare class Leanbase extends PostHogCore {
6137
6141
  capturePageLeave(): void;
6138
6142
  loadRemoteConfig(): Promise<void>;
6139
6143
  onRemoteConfig(config: RemoteConfig$1): void;
6144
+ private _maybeStartSessionRecording;
6140
6145
  fetch(url: string, options: PostHogFetchOptions): Promise<PostHogFetchResponse>;
6141
6146
  setConfig(config: Partial<LeanbaseConfig>): void;
6142
6147
  getLibraryId(): string;
package/dist/index.mjs CHANGED
@@ -1169,7 +1169,7 @@ const detectDeviceType = function (user_agent) {
1169
1169
  }
1170
1170
  };
1171
1171
 
1172
- var version = "0.2.2";
1172
+ var version = "0.2.3";
1173
1173
  var packageInfo = {
1174
1174
  version: version};
1175
1175
 
@@ -4446,6 +4446,11 @@ class LazyLoadedSessionRecording {
4446
4446
  });
4447
4447
  this._makeSamplingDecision(this.sessionId);
4448
4448
  await this._startRecorder();
4449
+ // If rrweb failed to load/start, do not proceed further.
4450
+ // This prevents installing listeners that assume rrweb is active.
4451
+ if (!this.isStarted) {
4452
+ return;
4453
+ }
4449
4454
  // calling addEventListener multiple times is safe and will not add duplicates
4450
4455
  addEventListener(win, 'beforeunload', this._onBeforeUnload);
4451
4456
  addEventListener(win, 'offline', this._onOffline);
@@ -4979,13 +4984,19 @@ class LazyLoadedSessionRecording {
4979
4984
  }
4980
4985
  });
4981
4986
  const activePlugins = this._gatherRRWebPlugins();
4982
- this._stopRrweb = rrwebRecord({
4983
- emit: event => {
4984
- this.onRRwebEmit(event);
4985
- },
4986
- plugins: activePlugins,
4987
- ...sessionRecordingOptions
4988
- });
4987
+ try {
4988
+ this._stopRrweb = rrwebRecord({
4989
+ emit: event => {
4990
+ this.onRRwebEmit(event);
4991
+ },
4992
+ plugins: activePlugins,
4993
+ ...sessionRecordingOptions
4994
+ });
4995
+ } catch (e) {
4996
+ logger.error('failed to start rrweb recorder', e);
4997
+ this._stopRrweb = undefined;
4998
+ return;
4999
+ }
4989
5000
  // We reset the last activity timestamp, resetting the idle timer
4990
5001
  this._lastActivityTimestamp = Date.now();
4991
5002
  // stay unknown if we're not sure if we're idle or not
@@ -5171,18 +5182,25 @@ class SessionRecording {
5171
5182
  // If extensions provide an init function, use it. Otherwise, fall back to the local LazyLoadedSessionRecording
5172
5183
  if (assignableWindow.__PosthogExtensions__?.initSessionRecording) {
5173
5184
  if (!this._lazyLoadedSessionRecording) {
5174
- this._lazyLoadedSessionRecording = assignableWindow.__PosthogExtensions__?.initSessionRecording(this._instance);
5175
- this._lazyLoadedSessionRecording._forceAllowLocalhostNetworkCapture = this._forceAllowLocalhostNetworkCapture;
5185
+ const maybeRecording = assignableWindow.__PosthogExtensions__?.initSessionRecording(this._instance);
5186
+ if (maybeRecording && typeof maybeRecording.start === 'function') {
5187
+ this._lazyLoadedSessionRecording = maybeRecording;
5188
+ this._lazyLoadedSessionRecording._forceAllowLocalhostNetworkCapture = this._forceAllowLocalhostNetworkCapture;
5189
+ } else {
5190
+ log.warn('initSessionRecording was present but did not return a recorder instance; falling back to local recorder');
5191
+ }
5176
5192
  }
5177
- try {
5178
- const maybePromise = this._lazyLoadedSessionRecording.start(startReason);
5179
- if (maybePromise && typeof maybePromise.catch === 'function') {
5180
- maybePromise.catch(e => logger$2.error('error starting session recording', e));
5193
+ if (this._lazyLoadedSessionRecording) {
5194
+ try {
5195
+ const maybePromise = this._lazyLoadedSessionRecording.start(startReason);
5196
+ if (maybePromise && typeof maybePromise.catch === 'function') {
5197
+ maybePromise.catch(e => logger$2.error('error starting session recording', e));
5198
+ }
5199
+ } catch (e) {
5200
+ logger$2.error('error starting session recording', e);
5181
5201
  }
5182
- } catch (e) {
5183
- logger$2.error('error starting session recording', e);
5202
+ return;
5184
5203
  }
5185
- return;
5186
5204
  }
5187
5205
  if (!this._lazyLoadedSessionRecording) {
5188
5206
  this._lazyLoadedSessionRecording = new LazyLoadedSessionRecording(this._instance);
@@ -5297,6 +5315,10 @@ class Leanbase extends PostHogCore {
5297
5315
  token
5298
5316
  });
5299
5317
  super(token, mergedConfig);
5318
+ this._remoteConfigLoadAttempted = false;
5319
+ this._remoteConfigResolved = false;
5320
+ this._featureFlagsResolved = false;
5321
+ this._maybeStartedSessionRecording = false;
5300
5322
  this.personProcessingSetOncePropertiesSent = false;
5301
5323
  this.isLoaded = false;
5302
5324
  this.config = mergedConfig;
@@ -5320,10 +5342,20 @@ class Leanbase extends PostHogCore {
5320
5342
  this.replayAutocapture.startIfEnabled();
5321
5343
  if (this.sessionManager && this.config.cookieless_mode !== 'always') {
5322
5344
  this.sessionRecording = new SessionRecording(this);
5323
- this.sessionRecording.startIfEnabledOrStop();
5324
5345
  }
5346
+ // Start session recording only once flags + remote config have been resolved.
5347
+ // This matches the PostHog browser SDK where replay activation is driven by remote config and flags.
5325
5348
  if (this.config.preloadFeatureFlags !== false) {
5326
- this.reloadFeatureFlags();
5349
+ this.reloadFeatureFlags({
5350
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
5351
+ cb: _err => {
5352
+ this._featureFlagsResolved = true;
5353
+ this._maybeStartSessionRecording();
5354
+ }
5355
+ });
5356
+ } else {
5357
+ // If feature flags preload is explicitly disabled, treat this requirement as satisfied.
5358
+ this._featureFlagsResolved = true;
5327
5359
  }
5328
5360
  this.config.loaded?.(this);
5329
5361
  if (this.config.capture_pageview) {
@@ -5333,9 +5365,26 @@ class Leanbase extends PostHogCore {
5333
5365
  }
5334
5366
  }, 1);
5335
5367
  }
5336
- addEventListener(document, 'DOMContentLoaded', () => {
5337
- this.loadRemoteConfig();
5338
- });
5368
+ const triggerRemoteConfigLoad = reason => {
5369
+ logger$2.info(`remote config load triggered via ${reason}`);
5370
+ void this.loadRemoteConfig();
5371
+ };
5372
+ if (document) {
5373
+ if (document.readyState === 'loading') {
5374
+ logger$2.info('remote config load deferred until DOMContentLoaded');
5375
+ const onDomReady = () => {
5376
+ document?.removeEventListener('DOMContentLoaded', onDomReady);
5377
+ triggerRemoteConfigLoad('dom');
5378
+ };
5379
+ addEventListener(document, 'DOMContentLoaded', onDomReady, {
5380
+ once: true
5381
+ });
5382
+ } else {
5383
+ triggerRemoteConfigLoad('immediate');
5384
+ }
5385
+ } else {
5386
+ triggerRemoteConfigLoad('no-document');
5387
+ }
5339
5388
  addEventListener(window, 'onpagehide' in self ? 'pagehide' : 'unload', this.capturePageLeave.bind(this), {
5340
5389
  passive: false
5341
5390
  });
@@ -5372,11 +5421,19 @@ class Leanbase extends PostHogCore {
5372
5421
  }
5373
5422
  }
5374
5423
  async loadRemoteConfig() {
5375
- if (!this.isRemoteConfigLoaded) {
5424
+ if (this._remoteConfigLoadAttempted) {
5425
+ return;
5426
+ }
5427
+ this._remoteConfigLoadAttempted = true;
5428
+ try {
5376
5429
  const remoteConfig = await this.reloadRemoteConfigAsync();
5377
5430
  if (remoteConfig) {
5378
5431
  this.onRemoteConfig(remoteConfig);
5379
5432
  }
5433
+ } finally {
5434
+ // Regardless of success/failure, we consider remote config "resolved" so replay isn't blocked forever.
5435
+ this._remoteConfigResolved = true;
5436
+ this._maybeStartSessionRecording();
5380
5437
  }
5381
5438
  }
5382
5439
  onRemoteConfig(config) {
@@ -5389,6 +5446,26 @@ class Leanbase extends PostHogCore {
5389
5446
  this.isRemoteConfigLoaded = true;
5390
5447
  this.replayAutocapture?.onRemoteConfig(config);
5391
5448
  this.sessionRecording?.onRemoteConfig(config);
5449
+ // Remote config has been applied; allow replay start if flags are also ready.
5450
+ this._remoteConfigResolved = true;
5451
+ this._maybeStartSessionRecording();
5452
+ }
5453
+ _maybeStartSessionRecording() {
5454
+ if (this._maybeStartedSessionRecording) {
5455
+ return;
5456
+ }
5457
+ if (!this.sessionRecording) {
5458
+ return;
5459
+ }
5460
+ if (!this._featureFlagsResolved || !this._remoteConfigResolved) {
5461
+ return;
5462
+ }
5463
+ this._maybeStartedSessionRecording = true;
5464
+ try {
5465
+ this.sessionRecording.startIfEnabledOrStop();
5466
+ } catch (e) {
5467
+ logger$2.error('Failed to start session recording', e);
5468
+ }
5392
5469
  }
5393
5470
  fetch(url, options) {
5394
5471
  const fetchFn = getFetch();