@sessionsight/insights 1.0.0 → 1.0.1
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/_worker-bundle.d.ts +1 -0
- package/dist/iife.d.ts +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +13850 -0
- package/dist/recorder.d.ts +115 -0
- package/dist/sessionsight.js +72 -0
- package/dist/transport.d.ts +43 -0
- package/dist/types.d.ts +71 -0
- package/dist/worker-bridge.d.ts +52 -0
- package/dist/worker-inline.d.ts +8 -0
- package/dist/worker.d.ts +8 -0
- package/package.json +4 -1
- package/build.ts +0 -47
- package/src/_worker-bundle.d.ts +0 -3
- package/src/iife.ts +0 -3
- package/src/index.ts +0 -240
- package/src/recorder.ts +0 -1396
- package/src/transport.ts +0 -241
- package/src/types.ts +0 -56
- package/src/worker-bridge.ts +0 -315
- package/src/worker-inline.ts +0 -23
- package/src/worker.ts +0 -302
- package/tsconfig.json +0 -13
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { IngestPayload, PrivacyConfig } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* WebSocket-first transport with HTTP fallback.
|
|
4
|
+
*
|
|
5
|
+
* On init, opens a persistent WebSocket to /ws/ingest. While the socket is
|
|
6
|
+
* open, payloads are sent as WS messages (zero network tab noise). If the
|
|
7
|
+
* socket isn't ready yet, drops, or fails to connect, falls back to HTTP
|
|
8
|
+
* fetch. sendBeacon (page unload) always uses HTTP since the WS may be
|
|
9
|
+
* closing.
|
|
10
|
+
*/
|
|
11
|
+
export declare class Transport {
|
|
12
|
+
private apiUrl;
|
|
13
|
+
private publicApiKey;
|
|
14
|
+
private propertyId;
|
|
15
|
+
private killed;
|
|
16
|
+
private ws;
|
|
17
|
+
private wsReady;
|
|
18
|
+
private reconnectTimer;
|
|
19
|
+
private reconnectDelay;
|
|
20
|
+
private static readonly MAX_RECONNECT_DELAY;
|
|
21
|
+
private closed;
|
|
22
|
+
private onPrivacyConfig;
|
|
23
|
+
constructor(apiUrl: string, publicApiKey: string, propertyId: string);
|
|
24
|
+
/** Register a callback for when the server sends privacy configuration. */
|
|
25
|
+
onPrivacy(callback: (config: PrivacyConfig) => void): void;
|
|
26
|
+
isKilled(): boolean;
|
|
27
|
+
private connectWs;
|
|
28
|
+
private scheduleReconnect;
|
|
29
|
+
send(payload: IngestPayload): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Best-effort send for page unload. Always uses HTTP (sendBeacon or fetch)
|
|
32
|
+
* since the WebSocket may be in the process of closing.
|
|
33
|
+
*/
|
|
34
|
+
sendBeacon(payload: IngestPayload): void;
|
|
35
|
+
/** Tear down the WebSocket (called when the recorder stops). */
|
|
36
|
+
destroy(): void;
|
|
37
|
+
/**
|
|
38
|
+
* Split a payload into chunks that each fit under the keepalive size limit.
|
|
39
|
+
* Events that are individually larger than the limit get their own chunk.
|
|
40
|
+
*/
|
|
41
|
+
private chunkEvents;
|
|
42
|
+
private sendHttpChunk;
|
|
43
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export interface SessionSightConfig {
|
|
2
|
+
publicApiKey: string;
|
|
3
|
+
propertyId?: string;
|
|
4
|
+
apiUrl?: string;
|
|
5
|
+
autoRecord?: boolean;
|
|
6
|
+
enabled?: boolean | (() => boolean);
|
|
7
|
+
}
|
|
8
|
+
export interface PrivacyConfig {
|
|
9
|
+
privacyMode: 'default' | 'relaxed';
|
|
10
|
+
excludePages: string[];
|
|
11
|
+
}
|
|
12
|
+
export interface RecordOptions {
|
|
13
|
+
preRecordSecs?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface SessionMetadata {
|
|
16
|
+
url: string;
|
|
17
|
+
referrer: string;
|
|
18
|
+
userAgent: string;
|
|
19
|
+
screenWidth: number;
|
|
20
|
+
screenHeight: number;
|
|
21
|
+
language: string;
|
|
22
|
+
}
|
|
23
|
+
export interface IngestPayload {
|
|
24
|
+
sessionId: string;
|
|
25
|
+
propertyId: string;
|
|
26
|
+
visitorId: string;
|
|
27
|
+
events: any[];
|
|
28
|
+
metadata?: SessionMetadata;
|
|
29
|
+
userId?: string | null;
|
|
30
|
+
userProperties?: Record<string, string | number | boolean>;
|
|
31
|
+
seqStart?: number;
|
|
32
|
+
seqEnd?: number;
|
|
33
|
+
final?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/** Main thread → Worker */
|
|
36
|
+
export type WorkerInMessage = {
|
|
37
|
+
type: 'init';
|
|
38
|
+
apiUrl: string;
|
|
39
|
+
publicApiKey: string;
|
|
40
|
+
propertyId: string;
|
|
41
|
+
sessionId: string;
|
|
42
|
+
visitorId: string;
|
|
43
|
+
} | {
|
|
44
|
+
type: 'event';
|
|
45
|
+
event: any;
|
|
46
|
+
seq: number;
|
|
47
|
+
} | {
|
|
48
|
+
type: 'metadata';
|
|
49
|
+
metadata: SessionMetadata;
|
|
50
|
+
} | {
|
|
51
|
+
type: 'identify';
|
|
52
|
+
userId: string;
|
|
53
|
+
userProperties?: Record<string, string | number | boolean>;
|
|
54
|
+
} | {
|
|
55
|
+
type: 'flush';
|
|
56
|
+
} | {
|
|
57
|
+
type: 'flush-final';
|
|
58
|
+
};
|
|
59
|
+
/** Worker → Main thread */
|
|
60
|
+
export type WorkerOutMessage = {
|
|
61
|
+
type: 'ack';
|
|
62
|
+
seq: number;
|
|
63
|
+
} | {
|
|
64
|
+
type: 'privacy';
|
|
65
|
+
config: PrivacyConfig;
|
|
66
|
+
} | {
|
|
67
|
+
type: 'killed';
|
|
68
|
+
reason: string;
|
|
69
|
+
} | {
|
|
70
|
+
type: 'ready';
|
|
71
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkerBridge: main-thread coordinator for the Web Worker transport.
|
|
3
|
+
*
|
|
4
|
+
* Assigns sequence numbers to events, maintains a mirror buffer of unacked
|
|
5
|
+
* events for sendBeacon fallback, and relays worker messages (privacy config,
|
|
6
|
+
* kill signals) back to the recorder.
|
|
7
|
+
*
|
|
8
|
+
* Falls back to inline Transport when Workers are unavailable.
|
|
9
|
+
*/
|
|
10
|
+
import type { PrivacyConfig, SessionMetadata } from './types.js';
|
|
11
|
+
export declare class WorkerBridge {
|
|
12
|
+
private worker;
|
|
13
|
+
private seq;
|
|
14
|
+
private lastAckedSeq;
|
|
15
|
+
private mirrorBuffer;
|
|
16
|
+
private _killed;
|
|
17
|
+
private onPrivacyCallback;
|
|
18
|
+
private onKilledCallback;
|
|
19
|
+
private sessionId;
|
|
20
|
+
private propertyId;
|
|
21
|
+
private visitorId;
|
|
22
|
+
private apiUrl;
|
|
23
|
+
private publicApiKey;
|
|
24
|
+
private metadata;
|
|
25
|
+
private metadataSent;
|
|
26
|
+
private userId;
|
|
27
|
+
private userProperties;
|
|
28
|
+
private userPropertiesDirty;
|
|
29
|
+
private fallbackTransport;
|
|
30
|
+
private fallbackBuffer;
|
|
31
|
+
private fallbackFlushTimer;
|
|
32
|
+
constructor(apiUrl: string, publicApiKey: string, propertyId: string, sessionId: string, visitorId: string);
|
|
33
|
+
postEvent(event: any): void;
|
|
34
|
+
postMetadata(metadata: SessionMetadata): void;
|
|
35
|
+
postIdentify(userId: string, userProperties?: Record<string, string | number | boolean>): void;
|
|
36
|
+
/** Trigger an immediate flush (e.g. before page exclusion pause). */
|
|
37
|
+
flush(): void;
|
|
38
|
+
/**
|
|
39
|
+
* Page unload handler. Sends flush-final to worker AND fires sendBeacon
|
|
40
|
+
* with unacked events from the mirror buffer as a safety net.
|
|
41
|
+
*/
|
|
42
|
+
sendBeacon(): void;
|
|
43
|
+
onPrivacy(callback: (config: PrivacyConfig) => void): void;
|
|
44
|
+
onKilled(callback: () => void): void;
|
|
45
|
+
isKilled(): boolean;
|
|
46
|
+
destroy(): void;
|
|
47
|
+
private handleWorkerMessage;
|
|
48
|
+
private initFallback;
|
|
49
|
+
/** Switch from worker mode to fallback after a worker error. */
|
|
50
|
+
private switchToFallback;
|
|
51
|
+
private fallbackFlush;
|
|
52
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a Web Worker from an inlined blob URL.
|
|
3
|
+
*
|
|
4
|
+
* The WORKER_SOURCE string is imported from _worker-bundle.ts, which is generated
|
|
5
|
+
* by running `bun run build:worker` (or the full `bun run build`).
|
|
6
|
+
* Run `bun run build:worker` once before starting the dev server.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createInlineWorker(): Worker | null;
|
package/dist/worker.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionSight Insights Web Worker
|
|
3
|
+
*
|
|
4
|
+
* Owns the event buffer, flush lifecycle, and WebSocket/HTTP transport.
|
|
5
|
+
* Receives pre-masked events from the main thread via postMessage.
|
|
6
|
+
* This file is bundled into a string and inlined as a blob URL at build time.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sessionsight/insights",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Session replay, heatmaps, and user analytics SDK for SessionSight.",
|
|
5
5
|
"author": "SessionSight",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
"user-insights",
|
|
21
21
|
"sessionsight"
|
|
22
22
|
],
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
23
26
|
"type": "module",
|
|
24
27
|
"main": "./src/index.ts",
|
|
25
28
|
"types": "./src/index.ts",
|
package/build.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Build script for the SessionSight Insights SDK.
|
|
3
|
-
*
|
|
4
|
-
* Supports two modes:
|
|
5
|
-
* bun run build:worker — generate _worker-bundle.ts only (for dev)
|
|
6
|
-
* bun run build — full build (worker + ESM + types + IIFE)
|
|
7
|
-
*
|
|
8
|
-
* The _worker-bundle.ts file is kept around (gitignored) so the dev
|
|
9
|
-
* server can resolve the static import in worker-inline.ts.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { $ } from 'bun';
|
|
13
|
-
|
|
14
|
-
const workerOnly = process.argv.includes('--worker-only');
|
|
15
|
-
|
|
16
|
-
// Phase 1: Build the worker to a standalone minified bundle
|
|
17
|
-
const workerResult = await Bun.build({
|
|
18
|
-
entrypoints: ['src/worker.ts'],
|
|
19
|
-
format: 'esm',
|
|
20
|
-
target: 'browser',
|
|
21
|
-
minify: true,
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
if (!workerResult.success) {
|
|
25
|
-
console.error('Failed to build worker:');
|
|
26
|
-
for (const log of workerResult.logs) console.error(log);
|
|
27
|
-
process.exit(1);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const workerCode = await workerResult.outputs[0]!.text();
|
|
31
|
-
console.log(`Worker bundle: ${(workerCode.length / 1024).toFixed(1)}KB minified`);
|
|
32
|
-
|
|
33
|
-
// Phase 2: Write the generated bundle module
|
|
34
|
-
const bundleSource = `// AUTO-GENERATED by build.ts. Do not edit.\nexport const WORKER_SOURCE = ${JSON.stringify(workerCode)};\n`;
|
|
35
|
-
await Bun.write('src/_worker-bundle.ts', bundleSource);
|
|
36
|
-
console.log('Generated src/_worker-bundle.ts');
|
|
37
|
-
|
|
38
|
-
if (workerOnly) {
|
|
39
|
-
console.log('Worker build complete (dev mode).');
|
|
40
|
-
process.exit(0);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Phase 3: Build ESM, declarations, and IIFE
|
|
44
|
-
await $`bun build src/index.ts --outdir dist --format esm --target browser`;
|
|
45
|
-
await $`bunx tsc --emitDeclarationOnly --declaration --outDir dist`;
|
|
46
|
-
await $`bun build src/iife.ts --outfile dist/sessionsight.js --format iife --target browser --minify`;
|
|
47
|
-
console.log('Full build complete.');
|
package/src/_worker-bundle.d.ts
DELETED
package/src/iife.ts
DELETED
package/src/index.ts
DELETED
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
import { Recorder } from './recorder.js';
|
|
2
|
-
import { WorkerBridge } from './worker-bridge.js';
|
|
3
|
-
import type { SessionSightConfig, RecordOptions, PrivacyConfig } from './types.js';
|
|
4
|
-
import { normalizeApiUrl, getOrCreateVisitorId } from '@sessionsight/sdk-shared';
|
|
5
|
-
|
|
6
|
-
const PRIVACY_CACHE_KEY = 'sessionsight_privacy_config';
|
|
7
|
-
|
|
8
|
-
let recorder: Recorder | null = null;
|
|
9
|
-
let pendingConfig: { bridge: WorkerBridge; propertyId: string; autoRecord: boolean } | null = null;
|
|
10
|
-
let enabledGetter: (() => boolean) | null = null;
|
|
11
|
-
let enabledPollTimer: ReturnType<typeof setInterval> | null = null;
|
|
12
|
-
let lastEnabledValue: boolean | null = null;
|
|
13
|
-
let visibilityResurrectionListener: (() => void) | null = null;
|
|
14
|
-
/** The last privacy config received from the server, used for session resurrection. */
|
|
15
|
-
let lastPrivacyConfig: PrivacyConfig = { privacyMode: 'default', excludePages: [] };
|
|
16
|
-
|
|
17
|
-
/** Stored config for recreating the recorder after visibility-based session end. */
|
|
18
|
-
let lastInitConfig: { bridge: WorkerBridge; propertyId: string; autoRecord: boolean } | null = null;
|
|
19
|
-
let storedVisitorId: string = '';
|
|
20
|
-
|
|
21
|
-
/** Read cached privacy config from sessionStorage, or return defaults. */
|
|
22
|
-
function getCachedPrivacyConfig(propertyId: string): PrivacyConfig {
|
|
23
|
-
try {
|
|
24
|
-
const raw = sessionStorage.getItem(PRIVACY_CACHE_KEY);
|
|
25
|
-
if (raw) {
|
|
26
|
-
const parsed = JSON.parse(raw);
|
|
27
|
-
if (parsed.propertyId === propertyId) {
|
|
28
|
-
return { privacyMode: parsed.privacyMode, excludePages: parsed.excludePages };
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
} catch {}
|
|
32
|
-
return { privacyMode: 'default', excludePages: [] };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** Write privacy config to sessionStorage for use on subsequent page loads. */
|
|
36
|
-
function cachePrivacyConfig(propertyId: string, config: PrivacyConfig): void {
|
|
37
|
-
try {
|
|
38
|
-
sessionStorage.setItem(PRIVACY_CACHE_KEY, JSON.stringify({
|
|
39
|
-
propertyId,
|
|
40
|
-
privacyMode: config.privacyMode,
|
|
41
|
-
excludePages: config.excludePages,
|
|
42
|
-
}));
|
|
43
|
-
} catch {}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function pollEnabled(): void {
|
|
47
|
-
if (!enabledGetter) return;
|
|
48
|
-
const current = enabledGetter();
|
|
49
|
-
if (current === lastEnabledValue) return;
|
|
50
|
-
lastEnabledValue = current;
|
|
51
|
-
|
|
52
|
-
if (current) {
|
|
53
|
-
if (recorder || !pendingConfig) return;
|
|
54
|
-
const { bridge, propertyId, autoRecord } = pendingConfig;
|
|
55
|
-
pendingConfig = null;
|
|
56
|
-
recorder = new Recorder(bridge, propertyId, storedVisitorId, { privacyMode: lastPrivacyConfig.privacyMode, excludePages: lastPrivacyConfig.excludePages });
|
|
57
|
-
recorder.start(autoRecord);
|
|
58
|
-
} else {
|
|
59
|
-
if (!recorder) return;
|
|
60
|
-
pendingConfig = {
|
|
61
|
-
bridge: recorder.getBridge(),
|
|
62
|
-
propertyId: recorder.getPropertyId(),
|
|
63
|
-
autoRecord: true,
|
|
64
|
-
};
|
|
65
|
-
recorder.stop();
|
|
66
|
-
recorder = null;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* SDK-level visibility listener that lives independently of the recorder.
|
|
72
|
-
* When a recorder ends itself due to the tab being hidden for too long,
|
|
73
|
-
* this listener creates a fresh recorder when the tab becomes visible again.
|
|
74
|
-
*/
|
|
75
|
-
function handleVisibilityResurrection(): void {
|
|
76
|
-
if (document.visibilityState !== 'visible') return;
|
|
77
|
-
if (!recorder?.endedByVisibility) return;
|
|
78
|
-
if (!lastInitConfig) return;
|
|
79
|
-
|
|
80
|
-
// If using an enabled getter and it currently returns false, don't resurrect
|
|
81
|
-
if (enabledGetter && !enabledGetter()) return;
|
|
82
|
-
|
|
83
|
-
const { bridge, propertyId, autoRecord } = lastInitConfig;
|
|
84
|
-
recorder = new Recorder(bridge, propertyId, storedVisitorId, { privacyMode: lastPrivacyConfig.privacyMode, excludePages: lastPrivacyConfig.excludePages });
|
|
85
|
-
recorder.start(autoRecord);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function startPolling(): void {
|
|
89
|
-
if (enabledPollTimer) return;
|
|
90
|
-
enabledPollTimer = setInterval(pollEnabled, 1000);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function stopPolling(): void {
|
|
94
|
-
if (enabledPollTimer) {
|
|
95
|
-
clearInterval(enabledPollTimer);
|
|
96
|
-
enabledPollTimer = null;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const SessionSight = {
|
|
101
|
-
init(config: SessionSightConfig): void {
|
|
102
|
-
try {
|
|
103
|
-
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
104
|
-
console.warn('SessionSight: browser environment required. Skipping initialization in SSR/Node.');
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (recorder || pendingConfig) {
|
|
109
|
-
console.warn('SessionSight is already initialized.');
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (!config.publicApiKey) {
|
|
114
|
-
console.error('SessionSight: publicApiKey is required.');
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const propertyId = config.propertyId || 'dev';
|
|
119
|
-
const apiUrl = normalizeApiUrl(config.apiUrl || '');
|
|
120
|
-
const autoRecord = config.autoRecord !== false;
|
|
121
|
-
|
|
122
|
-
// Use cached privacy config if available, otherwise start with most restrictive defaults
|
|
123
|
-
const cachedConfig = getCachedPrivacyConfig(propertyId);
|
|
124
|
-
lastPrivacyConfig = cachedConfig;
|
|
125
|
-
const privacyMode = cachedConfig.privacyMode;
|
|
126
|
-
const excludePages = cachedConfig.excludePages;
|
|
127
|
-
|
|
128
|
-
// Generate session context
|
|
129
|
-
const sessionId = typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
|
|
130
|
-
? crypto.randomUUID()
|
|
131
|
-
: `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
132
|
-
storedVisitorId = getOrCreateVisitorId();
|
|
133
|
-
|
|
134
|
-
const bridge = new WorkerBridge(apiUrl, config.publicApiKey, propertyId, sessionId, storedVisitorId);
|
|
135
|
-
|
|
136
|
-
// Listen for server-delivered privacy config from the worker/WebSocket
|
|
137
|
-
bridge.onPrivacy((serverConfig) => {
|
|
138
|
-
lastPrivacyConfig = serverConfig;
|
|
139
|
-
cachePrivacyConfig(propertyId, serverConfig);
|
|
140
|
-
if (recorder) {
|
|
141
|
-
recorder.applyPrivacyConfig(serverConfig);
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// Store config for session resurrection after visibility-based session end
|
|
146
|
-
lastInitConfig = { bridge, propertyId, autoRecord };
|
|
147
|
-
|
|
148
|
-
// Register SDK-level visibility listener (lives independently of recorder lifecycle)
|
|
149
|
-
if (!visibilityResurrectionListener) {
|
|
150
|
-
visibilityResurrectionListener = handleVisibilityResurrection;
|
|
151
|
-
document.addEventListener('visibilitychange', visibilityResurrectionListener);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const enabledOption = config.enabled;
|
|
155
|
-
|
|
156
|
-
if (typeof enabledOption === 'function') {
|
|
157
|
-
enabledGetter = enabledOption;
|
|
158
|
-
const initialValue = enabledGetter();
|
|
159
|
-
lastEnabledValue = initialValue;
|
|
160
|
-
|
|
161
|
-
if (initialValue) {
|
|
162
|
-
recorder = new Recorder(bridge, propertyId, storedVisitorId, { privacyMode, excludePages });
|
|
163
|
-
recorder.start(autoRecord);
|
|
164
|
-
} else {
|
|
165
|
-
pendingConfig = { bridge, propertyId, autoRecord };
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
startPolling();
|
|
169
|
-
} else {
|
|
170
|
-
const enabled = enabledOption !== false;
|
|
171
|
-
if (enabled) {
|
|
172
|
-
recorder = new Recorder(bridge, propertyId, storedVisitorId, { privacyMode, excludePages });
|
|
173
|
-
recorder.start(autoRecord);
|
|
174
|
-
} else {
|
|
175
|
-
pendingConfig = { bridge, propertyId, autoRecord };
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
} catch (e) {
|
|
179
|
-
console.warn('SessionSight: failed to initialize', e);
|
|
180
|
-
}
|
|
181
|
-
},
|
|
182
|
-
|
|
183
|
-
setEnabled(enabled: boolean): void {
|
|
184
|
-
if (enabled) {
|
|
185
|
-
if (recorder) return;
|
|
186
|
-
if (!pendingConfig) {
|
|
187
|
-
console.warn('SessionSight: call init() before setEnabled().');
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
const { bridge, propertyId, autoRecord } = pendingConfig;
|
|
191
|
-
pendingConfig = null;
|
|
192
|
-
recorder = new Recorder(bridge, propertyId, storedVisitorId, { privacyMode: lastPrivacyConfig.privacyMode, excludePages: lastPrivacyConfig.excludePages });
|
|
193
|
-
recorder.start(autoRecord);
|
|
194
|
-
} else {
|
|
195
|
-
if (!recorder) return;
|
|
196
|
-
pendingConfig = {
|
|
197
|
-
bridge: recorder.getBridge(),
|
|
198
|
-
propertyId: recorder.getPropertyId(),
|
|
199
|
-
autoRecord: true,
|
|
200
|
-
};
|
|
201
|
-
recorder.stop();
|
|
202
|
-
recorder = null;
|
|
203
|
-
}
|
|
204
|
-
lastEnabledValue = enabled;
|
|
205
|
-
},
|
|
206
|
-
|
|
207
|
-
record(options?: RecordOptions): void {
|
|
208
|
-
if (recorder) {
|
|
209
|
-
recorder.beginRecording(options);
|
|
210
|
-
}
|
|
211
|
-
},
|
|
212
|
-
|
|
213
|
-
stop(): void {
|
|
214
|
-
stopPolling();
|
|
215
|
-
enabledGetter = null;
|
|
216
|
-
lastEnabledValue = null;
|
|
217
|
-
lastInitConfig = null;
|
|
218
|
-
if (visibilityResurrectionListener) {
|
|
219
|
-
document.removeEventListener('visibilitychange', visibilityResurrectionListener);
|
|
220
|
-
visibilityResurrectionListener = null;
|
|
221
|
-
}
|
|
222
|
-
if (recorder) {
|
|
223
|
-
recorder.stop();
|
|
224
|
-
recorder = null;
|
|
225
|
-
}
|
|
226
|
-
pendingConfig = null;
|
|
227
|
-
},
|
|
228
|
-
|
|
229
|
-
identify(userId: string, properties?: Record<string, string | number | boolean>): void {
|
|
230
|
-
if (recorder) {
|
|
231
|
-
recorder.identify(userId, properties);
|
|
232
|
-
}
|
|
233
|
-
},
|
|
234
|
-
|
|
235
|
-
getVisitorId(): string | null {
|
|
236
|
-
return recorder ? recorder.getVisitorId() : null;
|
|
237
|
-
},
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
export default SessionSight;
|