@leanbase-giangnd/js 0.1.2 → 0.2.2
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/LICENSE +37 -0
- package/dist/index.cjs +344 -784
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.mjs +345 -785
- package/dist/index.mjs.map +1 -1
- package/dist/leanbase.iife.js +5549 -911
- package/dist/leanbase.iife.js.map +1 -1
- package/package.json +46 -47
- package/src/extensions/replay/extension-shim.ts +117 -7
- package/src/extensions/replay/external/lazy-loaded-session-recorder.ts +72 -11
- package/src/extensions/replay/session-recording.ts +52 -2
- package/src/utils/index.ts +3 -0
- package/src/version.ts +1 -1
package/package.json
CHANGED
|
@@ -1,48 +1,47 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
2
|
+
"name": "@leanbase-giangnd/js",
|
|
3
|
+
"version": "0.2.2",
|
|
4
|
+
"description": "Leanbase browser SDK - event tracking, autocapture, and session replay",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"directory": "packages/leanbase"
|
|
8
|
+
},
|
|
9
|
+
"author": "leanbase",
|
|
10
|
+
"license": "Copyrighted by Leanflag Limited",
|
|
11
|
+
"main": "dist/index.cjs",
|
|
12
|
+
"module": "dist/index.mjs",
|
|
13
|
+
"types": "dist/index.d.ts",
|
|
14
|
+
"unpkg": "dist/leanbase.iife.js",
|
|
15
|
+
"jsdelivr": "dist/leanbase.iife.js",
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"src",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@rrweb/record": "2.0.0-alpha.17",
|
|
26
|
+
"fflate": "^0.4.8",
|
|
27
|
+
"@posthog/core": "1.3.1"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"jest": "^29.7.0",
|
|
31
|
+
"jest-environment-jsdom": "^29.7.0",
|
|
32
|
+
"rollup": "^4.44.1",
|
|
33
|
+
"rimraf": "^6.0.1",
|
|
34
|
+
"@posthog-tooling/tsconfig-base": "1.0.0",
|
|
35
|
+
"@posthog-tooling/rollup-utils": "1.0.0"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"clean": "rimraf dist coverage",
|
|
39
|
+
"test:unit": "jest -c jest.config.js",
|
|
40
|
+
"lint": "eslint src test",
|
|
41
|
+
"lint:fix": "eslint src test --fix",
|
|
42
|
+
"prebuild": "node -p \"'export const version = \\'' + require('./package.json').version + '\\''\" > src/version.ts",
|
|
43
|
+
"build": "rollup -c",
|
|
44
|
+
"dev": "rollup -c -w",
|
|
45
|
+
"package": "mkdir -p ../../target && pnpm pack --pack-destination ../../target"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -1,23 +1,132 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
1
2
|
import { window as win } from '../../utils'
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
|
|
4
|
+
// We avoid importing '@rrweb/record' at module load time to prevent IIFE builds
|
|
5
|
+
// from requiring a top-level global. Instead, expose a lazy proxy that will
|
|
6
|
+
// dynamically import the module the first time it's used.
|
|
7
|
+
|
|
8
|
+
let _cachedRRWeb: any | null = null
|
|
9
|
+
|
|
10
|
+
async function _loadRRWebModule(): Promise<any> {
|
|
11
|
+
if (_cachedRRWeb) return _cachedRRWeb
|
|
12
|
+
try {
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
const mod: any = await import('@rrweb/record')
|
|
15
|
+
_cachedRRWeb = mod
|
|
16
|
+
return _cachedRRWeb
|
|
17
|
+
} catch (e) {
|
|
18
|
+
return null
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// queue for method calls before rrweb loads
|
|
23
|
+
const _queuedCalls: Array<() => void> = []
|
|
24
|
+
|
|
25
|
+
// Create a proxy function that delegates to the real rrweb.record when called
|
|
26
|
+
const rrwebRecordProxy: any = function (...args: any[]) {
|
|
27
|
+
let realStop: (() => void) | undefined
|
|
28
|
+
let calledReal = false
|
|
29
|
+
|
|
30
|
+
// Start loading asynchronously and call the real record when available
|
|
31
|
+
void (async () => {
|
|
32
|
+
const mod = await _loadRRWebModule()
|
|
33
|
+
const real = mod && (mod.record ?? mod.default?.record)
|
|
34
|
+
if (real) {
|
|
35
|
+
try {
|
|
36
|
+
calledReal = true
|
|
37
|
+
realStop = real(...args)
|
|
38
|
+
// flush any queued calls that were waiting for rrweb
|
|
39
|
+
while (_queuedCalls.length) {
|
|
40
|
+
try {
|
|
41
|
+
const fn = _queuedCalls.shift()!
|
|
42
|
+
fn()
|
|
43
|
+
} catch (e) {
|
|
44
|
+
// ignore
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
} catch (e) {
|
|
48
|
+
// ignore
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
})()
|
|
52
|
+
|
|
53
|
+
// return a stop function that will call the real stop when available
|
|
54
|
+
return () => {
|
|
55
|
+
if (realStop) {
|
|
56
|
+
try {
|
|
57
|
+
realStop()
|
|
58
|
+
} catch (e) {
|
|
59
|
+
// ignore
|
|
60
|
+
}
|
|
61
|
+
} else if (!calledReal) {
|
|
62
|
+
// If rrweb hasn't been initialised yet, queue a stop request that will
|
|
63
|
+
// call the real stop once available.
|
|
64
|
+
_queuedCalls.push(() => {
|
|
65
|
+
try {
|
|
66
|
+
realStop?.()
|
|
67
|
+
} catch (e) {
|
|
68
|
+
// ignore
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// methods that can be called on the rrweb.record object - queue until real module is available
|
|
76
|
+
rrwebRecordProxy.addCustomEvent = function (tag?: string, payload?: any) {
|
|
77
|
+
const call = () => {
|
|
78
|
+
try {
|
|
79
|
+
const real = _cachedRRWeb && (_cachedRRWeb.record ?? _cachedRRWeb.default?.record)
|
|
80
|
+
real?.addCustomEvent?.(tag, payload)
|
|
81
|
+
} catch (e) {
|
|
82
|
+
// ignore
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (_cachedRRWeb) call()
|
|
86
|
+
else _queuedCalls.push(call)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
rrwebRecordProxy.takeFullSnapshot = function () {
|
|
90
|
+
const call = () => {
|
|
91
|
+
try {
|
|
92
|
+
const real = _cachedRRWeb && (_cachedRRWeb.record ?? _cachedRRWeb.default?.record)
|
|
93
|
+
real?.takeFullSnapshot?.()
|
|
94
|
+
} catch (e) {
|
|
95
|
+
// ignore
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (_cachedRRWeb) call()
|
|
99
|
+
else _queuedCalls.push(call)
|
|
100
|
+
}
|
|
101
|
+
// Delay importing heavy modules to avoid circular dependencies at build time.
|
|
102
|
+
// They will be required lazily when used at runtime.
|
|
103
|
+
// We avoid requiring the lazy-loaded recorder here to prevent circular dependencies during bundling.
|
|
104
|
+
// Instead, `LazyLoadedSessionRecording` will register a factory on the global under
|
|
105
|
+
// `__PosthogExtensions__._initSessionRecordingFactory` when it loads.
|
|
106
|
+
type InitSessionRecordingFactory = (instance: any) => any
|
|
5
107
|
|
|
6
108
|
// Use a safe global target (prefer `win`, fallback to globalThis)
|
|
7
109
|
const _target: any = (win as any) ?? (globalThis as any)
|
|
8
110
|
|
|
9
111
|
_target.__PosthogExtensions__ = _target.__PosthogExtensions__ || {}
|
|
10
112
|
|
|
11
|
-
// Expose rrweb.record under the same contract
|
|
113
|
+
// Expose rrweb.record under the same contract. We provide a lazy proxy so
|
|
114
|
+
// builds that execute this file don't require rrweb at module evaluation time.
|
|
12
115
|
_target.__PosthogExtensions__.rrweb = _target.__PosthogExtensions__.rrweb || {
|
|
13
|
-
record:
|
|
116
|
+
record: rrwebRecordProxy,
|
|
14
117
|
}
|
|
15
118
|
|
|
16
119
|
// Provide initSessionRecording if not present — return a new LazyLoadedSessionRecording when called
|
|
17
120
|
_target.__PosthogExtensions__.initSessionRecording =
|
|
18
121
|
_target.__PosthogExtensions__.initSessionRecording ||
|
|
19
122
|
((instance: any) => {
|
|
20
|
-
|
|
123
|
+
const factory: InitSessionRecordingFactory | undefined =
|
|
124
|
+
_target.__PosthogExtensions__._initSessionRecordingFactory
|
|
125
|
+
if (factory) {
|
|
126
|
+
return factory(instance)
|
|
127
|
+
}
|
|
128
|
+
// If no factory is registered yet, return undefined — callers should handle lazy-loading.
|
|
129
|
+
return undefined
|
|
21
130
|
})
|
|
22
131
|
|
|
23
132
|
// Provide a no-op loadExternalDependency that calls the callback immediately (since rrweb is bundled)
|
|
@@ -29,7 +138,8 @@ _target.__PosthogExtensions__.loadExternalDependency =
|
|
|
29
138
|
|
|
30
139
|
// Provide rrwebPlugins object with network plugin factory if not present
|
|
31
140
|
_target.__PosthogExtensions__.rrwebPlugins = _target.__PosthogExtensions__.rrwebPlugins || {}
|
|
141
|
+
// Default to undefined; the lazy-loaded recorder will register the real factory when it initializes.
|
|
32
142
|
_target.__PosthogExtensions__.rrwebPlugins.getRecordNetworkPlugin =
|
|
33
|
-
_target.__PosthogExtensions__.rrwebPlugins.getRecordNetworkPlugin || (() =>
|
|
143
|
+
_target.__PosthogExtensions__.rrwebPlugins.getRecordNetworkPlugin || (() => undefined)
|
|
34
144
|
|
|
35
145
|
export {}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable posthog-js/no-direct-function-check */
|
|
2
2
|
import '../extension-shim'
|
|
3
3
|
import { clampToRange, includes, isBoolean, isNullish, isNumber, isObject, isString, isUndefined } from '@posthog/core'
|
|
4
4
|
import type { recordOptions, rrwebRecord as rrwebRecordType } from '../types/rrweb'
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
RecordPlugin,
|
|
12
12
|
} from '../types/rrweb-types'
|
|
13
13
|
import { buildNetworkRequestOptions } from './config'
|
|
14
|
-
|
|
14
|
+
// network plugin factory will be provided via __PosthogExtensions__.rrwebPlugins.getRecordNetworkPlugin
|
|
15
15
|
import {
|
|
16
16
|
ACTIVE,
|
|
17
17
|
allMatchSessionRecordingStatus,
|
|
@@ -72,6 +72,21 @@ const FIVE_MINUTES = ONE_MINUTE * 5
|
|
|
72
72
|
|
|
73
73
|
export const RECORDING_IDLE_THRESHOLD_MS = FIVE_MINUTES
|
|
74
74
|
|
|
75
|
+
// Register a factory on the global extensions object so the extension shim can
|
|
76
|
+
// instantiate a LazyLoadedSessionRecording without importing this module directly.
|
|
77
|
+
try {
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
79
|
+
const ext = (globalThis as any).__PosthogExtensions__
|
|
80
|
+
if (ext) {
|
|
81
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
82
|
+
ext._initSessionRecordingFactory =
|
|
83
|
+
ext._initSessionRecordingFactory || ((instance: any) => new LazyLoadedSessionRecording(instance))
|
|
84
|
+
}
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
86
|
+
} catch (e) {
|
|
87
|
+
// ignore
|
|
88
|
+
}
|
|
89
|
+
|
|
75
90
|
export const RECORDING_MAX_EVENT_SIZE = ONE_KB * ONE_KB * 0.9 // ~1mb (with some wiggle room)
|
|
76
91
|
export const RECORDING_BUFFER_TIMEOUT = 2000 // 2 seconds
|
|
77
92
|
export const SESSION_RECORDING_BATCH_KEY = 'recordings'
|
|
@@ -129,7 +144,43 @@ function getRRWebRecord(): rrwebRecordType | undefined {
|
|
|
129
144
|
// ignore
|
|
130
145
|
}
|
|
131
146
|
|
|
132
|
-
|
|
147
|
+
// If we've previously loaded rrweb via dynamic import, return the cached reference
|
|
148
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
149
|
+
const cached = (getRRWebRecord as any)._cachedRRWebRecord as rrwebRecordType | undefined
|
|
150
|
+
return cached as unknown as rrwebRecordType | undefined
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function loadRRWeb(): Promise<rrwebRecordType | null> {
|
|
154
|
+
try {
|
|
155
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
156
|
+
const ext = (globalThis as any).__PosthogExtensions__
|
|
157
|
+
if (ext && ext.rrweb && ext.rrweb.record) {
|
|
158
|
+
;(getRRWebRecord as any)._cachedRRWebRecord = ext.rrweb.record as unknown as rrwebRecordType
|
|
159
|
+
return ext.rrweb.record as unknown as rrwebRecordType
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// If already cached, return it
|
|
163
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
164
|
+
const already = (getRRWebRecord as any)._cachedRRWebRecord as rrwebRecordType | undefined
|
|
165
|
+
if (already) {
|
|
166
|
+
return already
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Dynamic import - let the bundler (IIFE build) include rrweb in the bundle or allow lazy-load
|
|
170
|
+
// Note: we intentionally use a dynamic import so rrweb is not referenced at the module top-level
|
|
171
|
+
// which would cause IIFE builds to assume a global is present at script execution.
|
|
172
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
173
|
+
const mod: any = await import('@rrweb/record')
|
|
174
|
+
const rr = (mod && (mod.record ?? (mod.default && mod.default.record))) as rrwebRecordType
|
|
175
|
+
if (rr) {
|
|
176
|
+
;(getRRWebRecord as any)._cachedRRWebRecord = rr
|
|
177
|
+
return rr
|
|
178
|
+
}
|
|
179
|
+
} catch (e) {
|
|
180
|
+
logger.error('could not dynamically load rrweb', e)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return null
|
|
133
184
|
}
|
|
134
185
|
|
|
135
186
|
export type compressedFullSnapshotEvent = {
|
|
@@ -468,11 +519,15 @@ export class LazyLoadedSessionRecording {
|
|
|
468
519
|
const canRecordNetwork = !isLocalhost() || this._forceAllowLocalhostNetworkCapture
|
|
469
520
|
|
|
470
521
|
if (canRecordNetwork) {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
522
|
+
const assignableWindow: any = globalThis
|
|
523
|
+
const networkFactory = assignableWindow.__PosthogExtensions__?.rrwebPlugins?.getRecordNetworkPlugin?.()
|
|
524
|
+
if (typeof networkFactory === 'function') {
|
|
525
|
+
plugins.push(
|
|
526
|
+
networkFactory(buildNetworkRequestOptions(this._instance.config, this._networkPayloadCapture))
|
|
474
527
|
)
|
|
475
|
-
|
|
528
|
+
} else {
|
|
529
|
+
logger.info('Network plugin factory not available yet; skipping network plugin')
|
|
530
|
+
}
|
|
476
531
|
} else {
|
|
477
532
|
logger.info('NetworkCapture not started because we are on localhost.')
|
|
478
533
|
}
|
|
@@ -661,7 +716,7 @@ export class LazyLoadedSessionRecording {
|
|
|
661
716
|
return parsedConfig as SessionRecordingPersistedConfig
|
|
662
717
|
}
|
|
663
718
|
|
|
664
|
-
start(startReason?: SessionStartReason) {
|
|
719
|
+
async start(startReason?: SessionStartReason) {
|
|
665
720
|
const config = this._remoteConfig
|
|
666
721
|
if (!config) {
|
|
667
722
|
logger.info('remote config must be stored in persistence before recording can start')
|
|
@@ -702,7 +757,7 @@ export class LazyLoadedSessionRecording {
|
|
|
702
757
|
})
|
|
703
758
|
|
|
704
759
|
this._makeSamplingDecision(this.sessionId)
|
|
705
|
-
this._startRecorder()
|
|
760
|
+
await this._startRecorder()
|
|
706
761
|
|
|
707
762
|
// calling addEventListener multiple times is safe and will not add duplicates
|
|
708
763
|
addEventListener(window, 'beforeunload', this._onBeforeUnload)
|
|
@@ -1282,7 +1337,7 @@ export class LazyLoadedSessionRecording {
|
|
|
1282
1337
|
}
|
|
1283
1338
|
}
|
|
1284
1339
|
|
|
1285
|
-
private _startRecorder() {
|
|
1340
|
+
private async _startRecorder() {
|
|
1286
1341
|
if (this._stopRrweb) {
|
|
1287
1342
|
return
|
|
1288
1343
|
}
|
|
@@ -1333,7 +1388,13 @@ export class LazyLoadedSessionRecording {
|
|
|
1333
1388
|
sessionRecordingOptions.blockSelector = this._masking.blockSelector ?? undefined
|
|
1334
1389
|
}
|
|
1335
1390
|
|
|
1336
|
-
|
|
1391
|
+
// Ensure rrweb is loaded (either via global extension or dynamic import)
|
|
1392
|
+
let rrwebRecord = getRRWebRecord()
|
|
1393
|
+
if (!rrwebRecord) {
|
|
1394
|
+
const loaded = await loadRRWeb()
|
|
1395
|
+
rrwebRecord = loaded ?? undefined
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1337
1398
|
if (!rrwebRecord) {
|
|
1338
1399
|
logger.error(
|
|
1339
1400
|
'_startRecorder was called but rrwebRecord is not available. This indicates something has gone wrong.'
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable posthog-js/no-direct-function-check */
|
|
1
2
|
import { SESSION_RECORDING_IS_SAMPLED, SESSION_RECORDING_REMOTE_CONFIG } from '../../constants'
|
|
2
3
|
import { Leanbase } from '../../leanbase'
|
|
3
4
|
import { Properties, RemoteConfig, SessionRecordingPersistedConfig, SessionStartReason } from '../../types'
|
|
@@ -5,7 +6,7 @@ import { type eventWithTime } from './types/rrweb-types'
|
|
|
5
6
|
|
|
6
7
|
import { isNullish, isUndefined } from '@posthog/core'
|
|
7
8
|
import { logger } from '../../leanbase-logger'
|
|
8
|
-
import { window } from '../../utils'
|
|
9
|
+
import { assignableWindow, window } from '../../utils'
|
|
9
10
|
import { LazyLoadedSessionRecording } from './external/lazy-loaded-session-recorder'
|
|
10
11
|
import { DISABLED, LAZY_LOADING, SessionRecordingStatus, TriggerType } from './external/triggerMatching'
|
|
11
12
|
|
|
@@ -91,6 +92,19 @@ export class SessionRecording {
|
|
|
91
92
|
return
|
|
92
93
|
}
|
|
93
94
|
|
|
95
|
+
// If extensions provide a loader, use it. Otherwise fallback to the local _onScriptLoaded which
|
|
96
|
+
// will create the local LazyLoadedSessionRecording (so tests that mock it work correctly).
|
|
97
|
+
const loader = assignableWindow.__PosthogExtensions__?.loadExternalDependency
|
|
98
|
+
if (typeof loader === 'function') {
|
|
99
|
+
loader(this._instance, this._scriptName as any, (err: any) => {
|
|
100
|
+
if (err) {
|
|
101
|
+
return log.error('could not load recorder', err)
|
|
102
|
+
}
|
|
103
|
+
this._onScriptLoaded(startReason)
|
|
104
|
+
})
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
|
|
94
108
|
this._onScriptLoaded(startReason)
|
|
95
109
|
}
|
|
96
110
|
|
|
@@ -188,14 +202,50 @@ export class SessionRecording {
|
|
|
188
202
|
}
|
|
189
203
|
}
|
|
190
204
|
|
|
205
|
+
private get _scriptName() {
|
|
206
|
+
const remoteConfig: SessionRecordingPersistedConfig | undefined = this._instance?.persistence?.get_property(
|
|
207
|
+
SESSION_RECORDING_REMOTE_CONFIG
|
|
208
|
+
)
|
|
209
|
+
return (remoteConfig?.scriptConfig?.script as any) || 'lazy-recorder'
|
|
210
|
+
}
|
|
211
|
+
|
|
191
212
|
private _onScriptLoaded(startReason?: SessionStartReason) {
|
|
213
|
+
// If extensions provide an init function, use it. Otherwise, fall back to the local LazyLoadedSessionRecording
|
|
214
|
+
if (assignableWindow.__PosthogExtensions__?.initSessionRecording) {
|
|
215
|
+
if (!this._lazyLoadedSessionRecording) {
|
|
216
|
+
this._lazyLoadedSessionRecording = assignableWindow.__PosthogExtensions__?.initSessionRecording(
|
|
217
|
+
this._instance
|
|
218
|
+
)
|
|
219
|
+
;(this._lazyLoadedSessionRecording as any)._forceAllowLocalhostNetworkCapture =
|
|
220
|
+
this._forceAllowLocalhostNetworkCapture
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
const maybePromise: any = this._lazyLoadedSessionRecording!.start(startReason)
|
|
225
|
+
if (maybePromise && typeof maybePromise.catch === 'function') {
|
|
226
|
+
maybePromise.catch((e: any) => logger.error('error starting session recording', e))
|
|
227
|
+
}
|
|
228
|
+
} catch (e: any) {
|
|
229
|
+
logger.error('error starting session recording', e)
|
|
230
|
+
}
|
|
231
|
+
return
|
|
232
|
+
}
|
|
233
|
+
|
|
192
234
|
if (!this._lazyLoadedSessionRecording) {
|
|
193
235
|
this._lazyLoadedSessionRecording = new LazyLoadedSessionRecording(this._instance)
|
|
194
236
|
;(this._lazyLoadedSessionRecording as any)._forceAllowLocalhostNetworkCapture =
|
|
195
237
|
this._forceAllowLocalhostNetworkCapture
|
|
196
238
|
}
|
|
197
239
|
|
|
198
|
-
|
|
240
|
+
// start may perform a dynamic import; handle both sync and Promise returns
|
|
241
|
+
try {
|
|
242
|
+
const maybePromise: any = this._lazyLoadedSessionRecording!.start(startReason)
|
|
243
|
+
if (maybePromise && typeof maybePromise.catch === 'function') {
|
|
244
|
+
maybePromise.catch((e: any) => logger.error('error starting session recording', e))
|
|
245
|
+
}
|
|
246
|
+
} catch (e: any) {
|
|
247
|
+
logger.error('error starting session recording', e)
|
|
248
|
+
}
|
|
199
249
|
}
|
|
200
250
|
|
|
201
251
|
/**
|
package/src/utils/index.ts
CHANGED
|
@@ -22,6 +22,9 @@ export const AbortController = global?.AbortController
|
|
|
22
22
|
export const userAgent = navigator?.userAgent
|
|
23
23
|
export { win as window }
|
|
24
24
|
|
|
25
|
+
// assignableWindow mirrors browser package's assignableWindow for extension loading shims
|
|
26
|
+
export const assignableWindow: (Window & typeof globalThis) | any = win ?? ({} as any)
|
|
27
|
+
|
|
25
28
|
export function eachArray<E = any>(
|
|
26
29
|
obj: E[] | null | undefined,
|
|
27
30
|
iterator: (value: E, key: number) => void | Breaker,
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '0.
|
|
1
|
+
export const version = '0.2.2'
|