@leanbase-giangnd/js 0.3.2 → 0.4.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leanbase-giangnd/js",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "Leanbase browser SDK - event tracking, autocapture, and session replay",
5
5
  "repository": {
6
6
  "type": "git",
@@ -35,6 +35,7 @@
35
35
  "dependencies": {
36
36
  "@posthog/core": "workspace:*",
37
37
  "@rrweb/record": "2.0.0-alpha.17",
38
+ "@rrweb/rrweb-plugin-console-record": "2.0.0-alpha.17",
38
39
  "fflate": "^0.4.8"
39
40
  },
40
41
  "devDependencies": {
@@ -318,6 +318,11 @@ export class LazyLoadedSessionRecording {
318
318
  * Util to help developers working on this feature manually override
319
319
  */
320
320
  private _forceAllowLocalhostNetworkCapture = false
321
+ private _debug(...args: any[]) {
322
+ if (this._instance?.config?.debug) {
323
+ logger.info(...args)
324
+ }
325
+ }
321
326
  // "Started" is only true once we have a valid rrweb stop handler AND we have marked recording enabled.
322
327
  // If rrweb fails to initialize, we permanently disable replay for this page load.
323
328
  private _recording: { stop: listenerHandler } | undefined
@@ -537,31 +542,84 @@ export class LazyLoadedSessionRecording {
537
542
  }
538
543
  }
539
544
 
540
- private _gatherRRWebPlugins() {
545
+ private async _loadConsolePlugin(): Promise<RecordPlugin | null> {
546
+ try {
547
+ const mod: any = await import('@rrweb/rrweb-plugin-console-record')
548
+ const factory = mod?.getRecordConsolePlugin ?? mod?.default?.getRecordConsolePlugin
549
+
550
+ if (typeof factory === 'function') {
551
+ const plugin = factory()
552
+ this._debug('Console plugin loaded')
553
+ return plugin
554
+ }
555
+
556
+ logger.warn('console plugin factory unavailable after import')
557
+ } catch (e) {
558
+ logger.warn('could not load console plugin', e)
559
+ }
560
+
561
+ return null
562
+ }
563
+
564
+ private async _loadNetworkPlugin(
565
+ networkPayloadCapture: Pick<
566
+ NetworkRecordOptions,
567
+ 'recordHeaders' | 'recordBody' | 'recordPerformance' | 'payloadHostDenyList'
568
+ >
569
+ ): Promise<RecordPlugin | null> {
570
+ try {
571
+ const mod: any = await import('./network-plugin')
572
+ const factory = mod?.getRecordNetworkPlugin ?? mod?.default?.getRecordNetworkPlugin
573
+
574
+ if (typeof factory === 'function') {
575
+ const options = buildNetworkRequestOptions(this._instance.config, networkPayloadCapture)
576
+ const plugin = factory(options)
577
+ this._debug('Network plugin loaded')
578
+ return plugin
579
+ }
580
+
581
+ logger.warn('network plugin factory unavailable after import')
582
+ } catch (e) {
583
+ logger.warn('could not load network plugin', e)
584
+ }
585
+
586
+ return null
587
+ }
588
+
589
+ private async _gatherRRWebPlugins(): Promise<RecordPlugin[]> {
541
590
  const plugins: RecordPlugin[] = []
542
591
 
592
+ if (!window) {
593
+ return plugins
594
+ }
595
+
543
596
  if (this._isConsoleLogCaptureEnabled) {
544
- logger.info('Console log capture requested but console plugin is not bundled in this build yet.')
597
+ const consolePlugin = await this._loadConsolePlugin()
598
+ if (consolePlugin) {
599
+ plugins.push(consolePlugin)
600
+ }
545
601
  }
546
602
 
547
603
  if (this._networkPayloadCapture) {
548
604
  const canRecordNetwork = !isLocalhost() || this._forceAllowLocalhostNetworkCapture
549
605
 
550
606
  if (canRecordNetwork) {
551
- const assignableWindow: any = globalThis
552
- const networkFactory = assignableWindow.__PosthogExtensions__?.rrwebPlugins?.getRecordNetworkPlugin?.()
553
- if (typeof networkFactory === 'function') {
554
- plugins.push(
555
- networkFactory(buildNetworkRequestOptions(this._instance.config, this._networkPayloadCapture))
556
- )
557
- } else {
558
- logger.info('Network plugin factory not available yet; skipping network plugin')
607
+ const networkPlugin = await this._loadNetworkPlugin(this._networkPayloadCapture)
608
+ if (networkPlugin) {
609
+ plugins.push(networkPlugin)
559
610
  }
560
611
  } else {
561
- logger.info('NetworkCapture not started because we are on localhost.')
612
+ this._debug('NetworkCapture not started because we are on localhost.')
562
613
  }
563
614
  }
564
615
 
616
+ if (plugins.length > 0) {
617
+ this._debug(
618
+ 'Replay plugins loaded',
619
+ plugins.map((p) => p.name)
620
+ )
621
+ }
622
+
565
623
  return plugins
566
624
  }
567
625
 
@@ -956,13 +1014,29 @@ export class LazyLoadedSessionRecording {
956
1014
 
957
1015
  private _snapshotIngestionUrl(): string | null {
958
1016
  const endpointFor = (this._instance as any)?.requestRouter?.endpointFor
959
- if (typeof endpointFor !== 'function') {
1017
+
1018
+ // Prefer requestRouter (parity with Browser SDK)
1019
+ if (typeof endpointFor === 'function') {
1020
+ try {
1021
+ return endpointFor('api', this._endpoint)
1022
+ } catch {
1023
+ return null
1024
+ }
1025
+ }
1026
+
1027
+ // Fallback: construct from host/api_host if requestRouter is unavailable (older IIFE builds)
1028
+ const host = (this._instance.config.api_host || this._instance.config.host || '').trim()
1029
+ if (!host) {
960
1030
  return null
961
1031
  }
1032
+
962
1033
  try {
963
- return endpointFor('api', this._endpoint)
1034
+ // eslint-disable-next-line compat/compat
1035
+ return new URL(this._endpoint, host).href
964
1036
  } catch {
965
- return null
1037
+ const normalizedHost = host.endsWith('/') ? host.slice(0, -1) : host
1038
+ const normalizedEndpoint = this._endpoint.startsWith('/') ? this._endpoint : `/${this._endpoint}`
1039
+ return `${normalizedHost}${normalizedEndpoint}`
966
1040
  }
967
1041
  }
968
1042
 
@@ -1560,7 +1634,7 @@ export class LazyLoadedSessionRecording {
1560
1634
  return
1561
1635
  }
1562
1636
 
1563
- const activePlugins = this._gatherRRWebPlugins()
1637
+ const activePlugins = await this._gatherRRWebPlugins()
1564
1638
 
1565
1639
  let stopHandler: listenerHandler | undefined
1566
1640
  try {
@@ -25,6 +25,12 @@ export class SessionRecording {
25
25
  private _persistFlagsOnSessionListener: (() => void) | undefined = undefined
26
26
  private _lazyLoadedSessionRecording: LazyLoadedSessionRecording | undefined
27
27
 
28
+ private _debug(...args: any[]) {
29
+ if (this._instance?.config?.debug) {
30
+ log.info(...args)
31
+ }
32
+ }
33
+
28
34
  public get started(): boolean {
29
35
  return !!this._lazyLoadedSessionRecording?.isStarted
30
36
  }
@@ -66,8 +72,10 @@ export class SessionRecording {
66
72
 
67
73
  const canRunReplay = !isUndefined(Object.assign) && !isUndefined(Array.from)
68
74
  if (this._isRecordingEnabled && canRunReplay) {
75
+ this._debug('Session replay enabled; starting recorder')
69
76
  this._lazyLoadAndStart(startReason)
70
77
  } else {
78
+ this._debug('Session replay disabled; stopping recorder')
71
79
  this.stopRecording()
72
80
  }
73
81
  }
@@ -156,13 +164,8 @@ export class SessionRecording {
156
164
  log.info('skipping remote config with no sessionRecording', response)
157
165
  return
158
166
  }
159
- this._receivedFlags = true
160
-
161
- if (response.sessionRecording === false) {
162
- return
163
- }
164
-
165
167
  this._persistRemoteConfig(response)
168
+ this._receivedFlags = true
166
169
  this.startIfEnabledOrStop()
167
170
  }
168
171
 
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '0.3.2'
1
+ export const version = '0.4.0'