@leanbase-giangnd/js 0.0.3 → 0.0.5
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 +102 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +103 -11
- package/dist/index.mjs.map +1 -1
- package/dist/leanbase.iife.js +102 -10
- package/dist/leanbase.iife.js.map +1 -1
- package/package.json +3 -3
- package/src/extensions/replay/external/lazy-loaded-session-recorder.ts +27 -8
- package/src/extensions/replay/external/snapshot-transport.ts +50 -0
- package/src/leanbase.ts +31 -1
- package/src/version.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leanbase-giangnd/js",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Leanbase browser SDK - event tracking, autocapture, and session replay",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"jest-environment-jsdom": "^29.7.0",
|
|
32
32
|
"rollup": "^4.44.1",
|
|
33
33
|
"rimraf": "^6.0.1",
|
|
34
|
-
"@posthog-tooling/
|
|
35
|
-
"@posthog-tooling/
|
|
34
|
+
"@posthog-tooling/tsconfig-base": "1.0.0",
|
|
35
|
+
"@posthog-tooling/rollup-utils": "1.0.0"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"clean": "rimraf dist coverage",
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
SESSION_RECORDING_REMOTE_CONFIG,
|
|
44
44
|
SESSION_RECORDING_URL_TRIGGER_ACTIVATED_SESSION,
|
|
45
45
|
} from '../../../constants'
|
|
46
|
+
import { sendSnapshotDirect } from './snapshot-transport'
|
|
46
47
|
import { Leanbase } from '../../../leanbase'
|
|
47
48
|
import {
|
|
48
49
|
CaptureResult,
|
|
@@ -1019,14 +1020,32 @@ export class LazyLoadedSessionRecording {
|
|
|
1019
1020
|
}
|
|
1020
1021
|
}
|
|
1021
1022
|
|
|
1022
|
-
private _captureSnapshot(properties: Properties) {
|
|
1023
|
-
//
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1023
|
+
private async _captureSnapshot(properties: Properties) {
|
|
1024
|
+
// Send snapshots directly to the recorder endpoint to avoid the core
|
|
1025
|
+
// analytics batching and persisted queue. The lifecycle gate in
|
|
1026
|
+
// SessionRecording ensures this method is only called when recording
|
|
1027
|
+
// is allowed.
|
|
1028
|
+
try {
|
|
1029
|
+
const url = this._snapshotUrl()
|
|
1030
|
+
// include minimal metadata expected by /s/ endpoint
|
|
1031
|
+
const payload = {
|
|
1032
|
+
$snapshot_bytes: properties.$snapshot_bytes,
|
|
1033
|
+
$snapshot_data: properties.$snapshot_data,
|
|
1034
|
+
$session_id: properties.$session_id,
|
|
1035
|
+
$window_id: properties.$window_id,
|
|
1036
|
+
$lib: properties.$lib,
|
|
1037
|
+
$lib_version: properties.$lib_version,
|
|
1038
|
+
// include distinct_id from persistence for server-side association
|
|
1039
|
+
distinct_id: this._instance.persistence?.get_property('distinct_id'),
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
await sendSnapshotDirect(this._instance, url, payload)
|
|
1043
|
+
} catch (err) {
|
|
1044
|
+
try {
|
|
1045
|
+
// eslint-disable-next-line no-console
|
|
1046
|
+
console.debug('[SessionRecording] snapshot send failed', err)
|
|
1047
|
+
} catch {}
|
|
1048
|
+
}
|
|
1030
1049
|
}
|
|
1031
1050
|
|
|
1032
1051
|
private _snapshotUrl(): string {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { gzipSync, strToU8 } from 'fflate'
|
|
2
|
+
import { Leanbase } from '../../../leanbase'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Leanbase-only SnapshotTransport
|
|
6
|
+
* Sends gzip-compressed snapshot payloads directly to the /s/ endpoint.
|
|
7
|
+
* This bypasses `posthog.capture()` and the analytics /batch/ queue.
|
|
8
|
+
*
|
|
9
|
+
* Reasoning: session replay snapshots must not enter the batch/persisted
|
|
10
|
+
* queues managed by @posthog/core. This transport posts directly to the
|
|
11
|
+
* recorder endpoint and uses the instance.fetch() implementation so it
|
|
12
|
+
* runs in the same environment the SDK uses.
|
|
13
|
+
*/
|
|
14
|
+
export async function sendSnapshotDirect(instance: Leanbase, url: string, payload: unknown) {
|
|
15
|
+
const fetchFn: any =
|
|
16
|
+
instance && (instance as any).fetch ? (instance as any).fetch.bind(instance) : (globalThis as any).fetch
|
|
17
|
+
|
|
18
|
+
const body = JSON.stringify(payload)
|
|
19
|
+
const compressed = gzipSync(strToU8(body))
|
|
20
|
+
|
|
21
|
+
const headers: Record<string, string> = {
|
|
22
|
+
'Content-Encoding': 'gzip',
|
|
23
|
+
'Content-Type': 'application/json',
|
|
24
|
+
Accept: 'application/json',
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!fetchFn) {
|
|
28
|
+
// best-effort: if no fetch available, fail silently (recorder should not crash app)
|
|
29
|
+
try {
|
|
30
|
+
// eslint-disable-next-line no-console
|
|
31
|
+
console.debug('[SnapshotTransport] no fetch available, dropping snapshot')
|
|
32
|
+
} catch {}
|
|
33
|
+
return { status: 0 }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
// Use the instance.fetch wrapper so environment-specific shims are used.
|
|
38
|
+
return await fetchFn(url, {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
body: compressed,
|
|
41
|
+
headers,
|
|
42
|
+
})
|
|
43
|
+
} catch (err) {
|
|
44
|
+
try {
|
|
45
|
+
// eslint-disable-next-line no-console
|
|
46
|
+
console.debug('[SnapshotTransport] send failed', err)
|
|
47
|
+
} catch {}
|
|
48
|
+
return { status: 0 }
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/leanbase.ts
CHANGED
|
@@ -36,7 +36,7 @@ import { decompressSync, strFromU8 } from 'fflate'
|
|
|
36
36
|
import Config from './config'
|
|
37
37
|
import { Autocapture } from './autocapture'
|
|
38
38
|
import { logger } from './leanbase-logger'
|
|
39
|
-
import { COOKIELESS_MODE_FLAG_PROPERTY, USER_STATE } from './constants'
|
|
39
|
+
import { COOKIELESS_MODE_FLAG_PROPERTY, USER_STATE, SESSION_RECORDING_REMOTE_CONFIG } from './constants'
|
|
40
40
|
import { getEventProperties } from './utils/event-utils'
|
|
41
41
|
import { SessionIdManager } from './sessionid'
|
|
42
42
|
import { SessionPropsManager } from './session-props'
|
|
@@ -293,6 +293,36 @@ export class Leanbase extends PostHogCore {
|
|
|
293
293
|
)
|
|
294
294
|
} catch {}
|
|
295
295
|
|
|
296
|
+
// If remote config has explicitly disabled session recording, drop snapshot events
|
|
297
|
+
try {
|
|
298
|
+
// Read persisted remote config that SessionRecording stores
|
|
299
|
+
const persisted: any = this.get_property(SESSION_RECORDING_REMOTE_CONFIG)
|
|
300
|
+
const serverAllowsRecording = !(
|
|
301
|
+
(persisted && persisted.enabled === false) ||
|
|
302
|
+
this.config.disable_session_recording === true
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
if (!serverAllowsRecording && hasSnapshot) {
|
|
306
|
+
// remove snapshot events from the batch before sending to /batch/
|
|
307
|
+
parsed.batch = parsed.batch.filter((item: any) => !(item && item.event === '$snapshot'))
|
|
308
|
+
// If no events remain, short-circuit and avoid sending an empty batch
|
|
309
|
+
if (!parsed.batch.length) {
|
|
310
|
+
try {
|
|
311
|
+
// eslint-disable-next-line no-console
|
|
312
|
+
console.debug(
|
|
313
|
+
'[Leanbase.fetch] sessionRecording disabled, dropping snapshot-only batch'
|
|
314
|
+
)
|
|
315
|
+
} catch {}
|
|
316
|
+
return { status: 200, json: async () => ({}) } as any
|
|
317
|
+
}
|
|
318
|
+
// re-encode the body so the underlying fetch receives the modified batch
|
|
319
|
+
try {
|
|
320
|
+
const newBody = JSON.stringify(parsed)
|
|
321
|
+
options = { ...options, body: newBody }
|
|
322
|
+
} catch {}
|
|
323
|
+
}
|
|
324
|
+
} catch {}
|
|
325
|
+
|
|
296
326
|
if (hasSnapshot) {
|
|
297
327
|
const host = (this.config && this.config.host) || ''
|
|
298
328
|
const newUrl = host ? `${host.replace(/\/$/, '')}/s/` : url
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '0.0.
|
|
1
|
+
export const version = '0.0.5'
|