@plushanalytics/react-native-session-replay 3.0.0 → 3.0.4
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/README.md +59 -2
- package/android/src/main/java/com/posthogreactnativesessionreplay/PosthogReactNativeSessionReplayModule.kt +125 -27
- package/ios/PosthogReactNativeSessionReplay.mm +3 -0
- package/ios/PosthogReactNativeSessionReplay.swift +187 -36
- package/lib/commonjs/adapters/mobileReplayAdapter.js +55 -18
- package/lib/commonjs/adapters/mobileReplayAdapter.js.map +1 -1
- package/lib/commonjs/client.js +2 -1
- package/lib/commonjs/client.js.map +1 -1
- package/lib/commonjs/nativeBridge.js +5 -0
- package/lib/commonjs/nativeBridge.js.map +1 -1
- package/lib/module/adapters/mobileReplayAdapter.js +55 -18
- package/lib/module/adapters/mobileReplayAdapter.js.map +1 -1
- package/lib/module/client.js +2 -1
- package/lib/module/client.js.map +1 -1
- package/lib/module/nativeBridge.js +4 -0
- package/lib/module/nativeBridge.js.map +1 -1
- package/lib/typescript/commonjs/src/adapters/mobileReplayAdapter.d.ts +2 -1
- package/lib/typescript/commonjs/src/adapters/mobileReplayAdapter.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/client.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/index.d.ts +1 -1
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/nativeBridge.d.ts +2 -0
- package/lib/typescript/commonjs/src/nativeBridge.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/types.d.ts +16 -5
- package/lib/typescript/commonjs/src/types.d.ts.map +1 -1
- package/lib/typescript/module/src/adapters/mobileReplayAdapter.d.ts +2 -1
- package/lib/typescript/module/src/adapters/mobileReplayAdapter.d.ts.map +1 -1
- package/lib/typescript/module/src/client.d.ts.map +1 -1
- package/lib/typescript/module/src/index.d.ts +1 -1
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/nativeBridge.d.ts +2 -0
- package/lib/typescript/module/src/nativeBridge.d.ts.map +1 -1
- package/lib/typescript/module/src/types.d.ts +16 -5
- package/lib/typescript/module/src/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/adapters/mobileReplayAdapter.ts +112 -19
- package/src/client.ts +2 -1
- package/src/index.tsx +2 -0
- package/src/nativeBridge.ts +8 -1
- package/src/types.ts +19 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,SAAS,EACT,WAAW,EACX,YAAY,EACZ,eAAe,EACf,SAAS,EACT,cAAc,IAAI,kBAAkB,EACpC,oBAAoB,IAAI,UAAU,EAClC,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,uBAAuB,IAAI,2BAA2B,EACtD,cAAc,EACd,YAAY,EACZ,mBAAmB,EACpB,MAAM,4BAA4B,CAAC;AAEpC,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,SAAS,EACT,WAAW,EACX,YAAY,EACZ,eAAe,EACf,SAAS,EACT,cAAc,IAAI,kBAAkB,EACpC,oBAAoB,IAAI,UAAU,EAClC,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,uBAAuB,IAAI,2BAA2B,EACtD,cAAc,EACd,YAAY,EACZ,mBAAmB,EACpB,MAAM,4BAA4B,CAAC;AAEpC,MAAM,MAAM,0BAA0B,GAAG;IACvC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,0BAA0B,CAAC;IACrC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,mBAAmB,GAAG;IAErD,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,2BAA2B,GAAG;IAElE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,kBAAkB,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG;IAC9C,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;CAC3C,CAAC;AAEF,YAAY,EACV,aAAa,EACb,SAAS,EACT,WAAW,EACX,YAAY,EACZ,eAAe,EACf,SAAS,EACT,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,YAAY,EACZ,mBAAmB,EACpB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,kBAAkB,EAAE,uBAAuB,GAAG,aAAa,CAAC,GAAG;IAC/F,qBAAqB,EAAE,CAAC,OAAO,CAAC,EAAE,uBAAuB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5E,WAAW,EAAE,CAAC,OAAO,CAAC,EAAE,uBAAuB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACnE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,34 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
MobileReplayConfig,
|
|
3
|
+
ReplayRecorderAdapter,
|
|
4
|
+
SessionRecordingOptions,
|
|
5
|
+
SessionReplayConfig,
|
|
6
|
+
} from "../types";
|
|
2
7
|
import * as replayModule from "../nativeBridge";
|
|
3
8
|
|
|
4
9
|
type AdapterDefaults = {
|
|
5
10
|
apiKey: string;
|
|
6
11
|
endpoint: string;
|
|
12
|
+
replayConfigDefaults?: SessionReplayConfig;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type StartArgs = {
|
|
16
|
+
onSessionId?: (sessionId: string) => void;
|
|
17
|
+
options?: SessionRecordingOptions;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type NormalizedReplayConfig = {
|
|
21
|
+
enabled: boolean;
|
|
22
|
+
maskAllTextInputs: boolean;
|
|
23
|
+
maskAllImages: boolean;
|
|
24
|
+
maskAllSandboxedViews: boolean;
|
|
25
|
+
captureLog: boolean;
|
|
26
|
+
captureNetworkTelemetry: boolean;
|
|
27
|
+
throttleDelayMs: number;
|
|
7
28
|
};
|
|
8
29
|
|
|
9
30
|
const DEFAULT_SNAPSHOT_PATH = "/v1/events/batch";
|
|
31
|
+
const DEFAULT_THROTTLE_DELAY_MS = 1000;
|
|
10
32
|
const UUID_PATTERN =
|
|
11
33
|
/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
12
34
|
|
|
@@ -36,22 +58,11 @@ function generateUuid(): string {
|
|
|
36
58
|
|
|
37
59
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
|
|
38
60
|
const random = Math.floor(Math.random() * 16);
|
|
39
|
-
const
|
|
40
|
-
return
|
|
61
|
+
const resolved = char === "x" ? random : (random & 0x3) | 0x8;
|
|
62
|
+
return resolved.toString(16);
|
|
41
63
|
});
|
|
42
64
|
}
|
|
43
65
|
|
|
44
|
-
function normalizeReplayConfig(config: MobileReplayConfig | undefined): Record<string, unknown> {
|
|
45
|
-
return {
|
|
46
|
-
maskAllTextInputs: config?.maskAllTextInputs ?? true,
|
|
47
|
-
maskAllImages: config?.maskAllImages ?? true,
|
|
48
|
-
maskAllSandboxedViews: config?.maskAllSandboxedViews ?? true,
|
|
49
|
-
captureLog: config?.captureLog ?? true,
|
|
50
|
-
captureNetworkTelemetry: config?.captureNetworkTelemetry ?? true,
|
|
51
|
-
throttleDelayMs: config?.throttleDelayMs ?? 1000
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
66
|
function resolveSessionId(options?: SessionRecordingOptions): string {
|
|
56
67
|
const requestedSessionId = options?.sessionId?.trim();
|
|
57
68
|
if (requestedSessionId && isUuid(requestedSessionId)) {
|
|
@@ -75,14 +86,93 @@ function assertNoPosthogDotCom(host: string): void {
|
|
|
75
86
|
}
|
|
76
87
|
}
|
|
77
88
|
|
|
89
|
+
function resolveMaskingValue(
|
|
90
|
+
overrideConfig: MobileReplayConfig | undefined,
|
|
91
|
+
defaultConfig: SessionReplayConfig | undefined,
|
|
92
|
+
maskingKey: keyof NonNullable<SessionReplayConfig["masking"]>,
|
|
93
|
+
legacyKey: "maskAllTextInputs" | "maskAllImages" | "maskAllSandboxedViews"
|
|
94
|
+
): boolean {
|
|
95
|
+
const overrideMaskingValue = overrideConfig?.masking?.[maskingKey];
|
|
96
|
+
if (typeof overrideMaskingValue === "boolean") {
|
|
97
|
+
return overrideMaskingValue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const overrideLegacyValue = overrideConfig?.[legacyKey];
|
|
101
|
+
if (typeof overrideLegacyValue === "boolean") {
|
|
102
|
+
return overrideLegacyValue;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const defaultMaskingValue = defaultConfig?.masking?.[maskingKey];
|
|
106
|
+
if (typeof defaultMaskingValue === "boolean") {
|
|
107
|
+
return defaultMaskingValue;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function resolveReplayConfig(
|
|
114
|
+
defaultConfig: SessionReplayConfig | undefined,
|
|
115
|
+
overrideConfig: MobileReplayConfig | undefined
|
|
116
|
+
): NormalizedReplayConfig {
|
|
117
|
+
const enabled = overrideConfig?.enabled ?? defaultConfig?.enabled ?? true;
|
|
118
|
+
const captureLog = overrideConfig?.captureLog ?? defaultConfig?.captureLog ?? true;
|
|
119
|
+
const captureNetworkTelemetry =
|
|
120
|
+
overrideConfig?.captureNetworkTelemetry ?? defaultConfig?.captureNetworkTelemetry ?? true;
|
|
121
|
+
|
|
122
|
+
const requestedThrottleDelayMs =
|
|
123
|
+
overrideConfig?.throttleDelayMs ?? defaultConfig?.throttleDelayMs ?? DEFAULT_THROTTLE_DELAY_MS;
|
|
124
|
+
const throttleDelayMs =
|
|
125
|
+
Number.isFinite(requestedThrottleDelayMs) && requestedThrottleDelayMs >= 0
|
|
126
|
+
? requestedThrottleDelayMs
|
|
127
|
+
: DEFAULT_THROTTLE_DELAY_MS;
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
enabled,
|
|
131
|
+
maskAllTextInputs: resolveMaskingValue(
|
|
132
|
+
overrideConfig,
|
|
133
|
+
defaultConfig,
|
|
134
|
+
"textInputs",
|
|
135
|
+
"maskAllTextInputs"
|
|
136
|
+
),
|
|
137
|
+
maskAllImages: resolveMaskingValue(overrideConfig, defaultConfig, "images", "maskAllImages"),
|
|
138
|
+
maskAllSandboxedViews: resolveMaskingValue(
|
|
139
|
+
overrideConfig,
|
|
140
|
+
defaultConfig,
|
|
141
|
+
"sandboxedViews",
|
|
142
|
+
"maskAllSandboxedViews"
|
|
143
|
+
),
|
|
144
|
+
captureLog,
|
|
145
|
+
captureNetworkTelemetry,
|
|
146
|
+
throttleDelayMs,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function toNativeReplayConfig(config: NormalizedReplayConfig): Record<string, unknown> {
|
|
151
|
+
return {
|
|
152
|
+
maskAllTextInputs: config.maskAllTextInputs,
|
|
153
|
+
maskAllImages: config.maskAllImages,
|
|
154
|
+
maskAllSandboxedViews: config.maskAllSandboxedViews,
|
|
155
|
+
captureLog: config.captureLog,
|
|
156
|
+
captureNetworkTelemetry: config.captureNetworkTelemetry,
|
|
157
|
+
throttleDelayMs: config.throttleDelayMs,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
78
161
|
export function createMobileReplayRecorderAdapter(defaults: AdapterDefaults): ReplayRecorderAdapter {
|
|
79
162
|
assertNonEmpty(defaults.apiKey, "apiKey");
|
|
80
163
|
assertNonEmpty(defaults.endpoint, "endpoint");
|
|
81
164
|
|
|
82
165
|
return {
|
|
83
|
-
start: async (
|
|
166
|
+
start: async (args: StartArgs) => {
|
|
167
|
+
const options = args.options;
|
|
168
|
+
const resolvedReplayConfig = resolveReplayConfig(defaults.replayConfigDefaults, options?.replayConfig);
|
|
169
|
+
if (!resolvedReplayConfig.enabled) {
|
|
170
|
+
return () => {};
|
|
171
|
+
}
|
|
172
|
+
|
|
84
173
|
const host = normalizeHost(options?.replayHost?.trim() || defaults.endpoint);
|
|
85
174
|
assertNoPosthogDotCom(host);
|
|
175
|
+
|
|
86
176
|
const snapshotPath = normalizeSnapshotPath(options?.replaySnapshotPath);
|
|
87
177
|
const activeSessionId = resolveSessionId(options);
|
|
88
178
|
|
|
@@ -93,21 +183,24 @@ export function createMobileReplayRecorderAdapter(defaults: AdapterDefaults): Re
|
|
|
93
183
|
distinctId: options?.userId?.trim() || "",
|
|
94
184
|
anonymousId: "",
|
|
95
185
|
sdkVersion: "plushanalytics-react-native",
|
|
96
|
-
flushAt: 20
|
|
186
|
+
flushAt: 20,
|
|
97
187
|
};
|
|
98
188
|
|
|
99
189
|
await replayModule.start(
|
|
100
190
|
activeSessionId,
|
|
101
191
|
sdkOptions,
|
|
102
|
-
|
|
192
|
+
toNativeReplayConfig(resolvedReplayConfig),
|
|
103
193
|
{ endpoint: snapshotPath }
|
|
104
194
|
);
|
|
105
195
|
await replayModule.startSession(activeSessionId);
|
|
106
|
-
onSessionId?.(activeSessionId);
|
|
196
|
+
args.onSessionId?.(activeSessionId);
|
|
107
197
|
|
|
108
198
|
return () => {
|
|
109
199
|
void replayModule.endSession();
|
|
110
200
|
};
|
|
111
|
-
}
|
|
201
|
+
},
|
|
202
|
+
flush: async () => {
|
|
203
|
+
await replayModule.flush();
|
|
204
|
+
},
|
|
112
205
|
};
|
|
113
206
|
}
|
package/src/client.ts
CHANGED
|
@@ -30,7 +30,8 @@ export function createPlushAnalyticsClient(config: PlushAnalyticsConfig): PlushA
|
|
|
30
30
|
config.replay?.defaultRecorder ??
|
|
31
31
|
createMobileReplayRecorderAdapter({
|
|
32
32
|
apiKey: config.apiKey,
|
|
33
|
-
endpoint
|
|
33
|
+
endpoint,
|
|
34
|
+
replayConfigDefaults: config.sessionReplayConfig
|
|
34
35
|
})
|
|
35
36
|
}
|
|
36
37
|
};
|
package/src/index.tsx
CHANGED
package/src/nativeBridge.ts
CHANGED
|
@@ -40,6 +40,10 @@ export function endSession(): Promise<void> {
|
|
|
40
40
|
return PosthogReactNativeSessionReplay.endSession();
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
export function flush(): Promise<void> {
|
|
44
|
+
return PosthogReactNativeSessionReplay.flush();
|
|
45
|
+
}
|
|
46
|
+
|
|
43
47
|
export function isEnabled(): Promise<boolean> {
|
|
44
48
|
return PosthogReactNativeSessionReplay.isEnabled();
|
|
45
49
|
}
|
|
@@ -56,13 +60,15 @@ export interface PostHogReactNativeSessionReplayModule {
|
|
|
56
60
|
sessionId: string,
|
|
57
61
|
sdkOptions: { [key: string]: any }, // options from SDK such as apiKey
|
|
58
62
|
sdkReplayConfig: { [key: string]: any }, // config from SDK
|
|
59
|
-
decideReplayConfig: { [key: string]: any } //
|
|
63
|
+
decideReplayConfig: { [key: string]: any } // replay runtime config
|
|
60
64
|
) => Promise<void>;
|
|
61
65
|
|
|
62
66
|
startSession: (sessionId: string) => Promise<void>;
|
|
63
67
|
|
|
64
68
|
endSession: () => Promise<void>;
|
|
65
69
|
|
|
70
|
+
flush: () => Promise<void>;
|
|
71
|
+
|
|
66
72
|
isEnabled: () => Promise<boolean>;
|
|
67
73
|
|
|
68
74
|
identify: (distinctId: string, anonymousId: string) => Promise<void>;
|
|
@@ -72,6 +78,7 @@ const PostHogReactNativeSessionReplay: PostHogReactNativeSessionReplayModule = {
|
|
|
72
78
|
start,
|
|
73
79
|
startSession,
|
|
74
80
|
endSession,
|
|
81
|
+
flush,
|
|
75
82
|
isEnabled,
|
|
76
83
|
identify,
|
|
77
84
|
};
|
package/src/types.ts
CHANGED
|
@@ -18,15 +18,27 @@ import type {
|
|
|
18
18
|
UnifiedBatchPayload
|
|
19
19
|
} from "@plushanalytics/javascript";
|
|
20
20
|
|
|
21
|
-
export type
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
export type SessionReplayMaskingConfig = {
|
|
22
|
+
textInputs?: boolean;
|
|
23
|
+
images?: boolean;
|
|
24
|
+
sandboxedViews?: boolean;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type SessionReplayConfig = {
|
|
28
|
+
enabled?: boolean;
|
|
29
|
+
masking?: SessionReplayMaskingConfig;
|
|
25
30
|
captureLog?: boolean;
|
|
26
31
|
captureNetworkTelemetry?: boolean;
|
|
27
32
|
throttleDelayMs?: number;
|
|
28
33
|
};
|
|
29
34
|
|
|
35
|
+
export type MobileReplayConfig = SessionReplayConfig & {
|
|
36
|
+
// Legacy flat options are still supported for backward compatibility.
|
|
37
|
+
maskAllTextInputs?: boolean;
|
|
38
|
+
maskAllImages?: boolean;
|
|
39
|
+
maskAllSandboxedViews?: boolean;
|
|
40
|
+
};
|
|
41
|
+
|
|
30
42
|
export type SessionRecordingOptions = CoreSessionRecordingOptions & {
|
|
31
43
|
// Used by the bundled replay adapter (optional, replay still works without these when you pass a custom recorder).
|
|
32
44
|
replayHost?: string;
|
|
@@ -34,7 +46,9 @@ export type SessionRecordingOptions = CoreSessionRecordingOptions & {
|
|
|
34
46
|
replayConfig?: MobileReplayConfig;
|
|
35
47
|
};
|
|
36
48
|
|
|
37
|
-
export type PlushAnalyticsConfig = CoreConfig
|
|
49
|
+
export type PlushAnalyticsConfig = CoreConfig & {
|
|
50
|
+
sessionReplayConfig?: SessionReplayConfig;
|
|
51
|
+
};
|
|
38
52
|
|
|
39
53
|
export type {
|
|
40
54
|
EventEnvelope,
|