@amplitude/session-replay-browser 1.48.3 → 1.50.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/README.md +16 -2
- package/lib/cjs/config/local-config.d.ts +1 -0
- package/lib/cjs/config/local-config.d.ts.map +1 -1
- package/lib/cjs/config/local-config.js +1 -0
- package/lib/cjs/config/local-config.js.map +1 -1
- package/lib/cjs/config/types.d.ts +6 -0
- package/lib/cjs/config/types.d.ts.map +1 -1
- package/lib/cjs/config/types.js.map +1 -1
- package/lib/cjs/index.d.ts +1 -1
- package/lib/cjs/index.d.ts.map +1 -1
- package/lib/cjs/index.js +2 -2
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/session-replay-factory.d.ts.map +1 -1
- package/lib/cjs/session-replay-factory.js +2 -0
- package/lib/cjs/session-replay-factory.js.map +1 -1
- package/lib/cjs/session-replay.d.ts +22 -0
- package/lib/cjs/session-replay.d.ts.map +1 -1
- package/lib/cjs/session-replay.js +112 -36
- package/lib/cjs/session-replay.js.map +1 -1
- package/lib/cjs/typings/session-replay.d.ts +11 -0
- package/lib/cjs/typings/session-replay.d.ts.map +1 -1
- package/lib/cjs/typings/session-replay.js.map +1 -1
- package/lib/cjs/utils/rrweb.d.ts +1 -0
- package/lib/cjs/utils/rrweb.d.ts.map +1 -1
- package/lib/cjs/utils/rrweb.js.map +1 -1
- package/lib/cjs/version.d.ts +1 -1
- package/lib/cjs/version.js +1 -1
- package/lib/cjs/version.js.map +1 -1
- package/lib/esm/config/local-config.d.ts +1 -0
- package/lib/esm/config/local-config.d.ts.map +1 -1
- package/lib/esm/config/local-config.js +1 -0
- package/lib/esm/config/local-config.js.map +1 -1
- package/lib/esm/config/types.d.ts +6 -0
- package/lib/esm/config/types.d.ts.map +1 -1
- package/lib/esm/config/types.js.map +1 -1
- package/lib/esm/index.d.ts +1 -1
- package/lib/esm/index.d.ts.map +1 -1
- package/lib/esm/index.js +1 -1
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/session-replay-factory.d.ts.map +1 -1
- package/lib/esm/session-replay-factory.js +2 -0
- package/lib/esm/session-replay-factory.js.map +1 -1
- package/lib/esm/session-replay.d.ts +22 -0
- package/lib/esm/session-replay.d.ts.map +1 -1
- package/lib/esm/session-replay.js +112 -36
- package/lib/esm/session-replay.js.map +1 -1
- package/lib/esm/typings/session-replay.d.ts +11 -0
- package/lib/esm/typings/session-replay.d.ts.map +1 -1
- package/lib/esm/typings/session-replay.js.map +1 -1
- package/lib/esm/utils/rrweb.d.ts +1 -0
- package/lib/esm/utils/rrweb.d.ts.map +1 -1
- package/lib/esm/utils/rrweb.js.map +1 -1
- package/lib/esm/version.d.ts +1 -1
- package/lib/esm/version.js +1 -1
- package/lib/esm/version.js.map +1 -1
- package/lib/scripts/index-min.js +1 -1
- package/lib/scripts/index-min.js.gz +0 -0
- package/lib/scripts/index-min.js.map +1 -1
- package/lib/scripts/session-replay-browser-esm.js +1 -1
- package/lib/scripts/session-replay-browser-esm.js.gz +0 -0
- package/lib/scripts/session-replay-browser-min.js +1 -1
- package/lib/scripts/session-replay-browser-min.js.gz +0 -0
- package/lib/scripts/session-replay-browser-min.js.map +1 -1
- package/package.json +3 -3
|
@@ -44,6 +44,24 @@ var SessionReplay = /** @class */ (function () {
|
|
|
44
44
|
// Cache the dynamically imported record function
|
|
45
45
|
this.recordFunction = null;
|
|
46
46
|
this.recordEventsInFlight = false;
|
|
47
|
+
/**
|
|
48
|
+
* When false, capture is paused by a customer `stop()` (or `shutdown()`) call.
|
|
49
|
+
* Focus/targeting/session-change paths still invoke `recordEvents()`, but
|
|
50
|
+
* `getShouldRecord()` will refuse to start rrweb until `start()` or a new `init()`.
|
|
51
|
+
*/
|
|
52
|
+
this.recordingEnabled = true;
|
|
53
|
+
/**
|
|
54
|
+
* Bumped by `stop()` / `shutdown()` so an in-flight `_recordEvents()` that already
|
|
55
|
+
* passed `getShouldRecord()` cannot start rrweb after capture was paused.
|
|
56
|
+
*/
|
|
57
|
+
this.recordingGeneration = 0;
|
|
58
|
+
/**
|
|
59
|
+
* Set by customer `start()` so coordinated child iframes self-start instead of
|
|
60
|
+
* waiting for a parent signal that may never be resent. Kept until recording
|
|
61
|
+
* actually starts so a gated `start()` (targeting, sampling, remote capture)
|
|
62
|
+
* still self-starts when those gates later pass.
|
|
63
|
+
*/
|
|
64
|
+
this.startChildRecordingOnSetup = false;
|
|
47
65
|
this.pendingEmitEvents = [];
|
|
48
66
|
/** Current page URL, kept in sync with SPA navigations for URL-based masking */
|
|
49
67
|
this.currentPageUrl = '';
|
|
@@ -80,6 +98,9 @@ var SessionReplay = /** @class */ (function () {
|
|
|
80
98
|
};
|
|
81
99
|
this.focusListener = function () {
|
|
82
100
|
var _a;
|
|
101
|
+
if (!_this.recordingEnabled) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
83
104
|
if (_this.recordCancelCallback && _this.recordFunction) {
|
|
84
105
|
// Recording is already active. The on-focus full snapshot is tunable: when
|
|
85
106
|
// `captureFullSnapshotOnFocus` is false we skip it entirely so high focus-churn pages
|
|
@@ -335,6 +356,9 @@ var SessionReplay = /** @class */ (function () {
|
|
|
335
356
|
// Re-init should always tear down any previous URL-change subscription, even when the
|
|
336
357
|
// next config has no targeting config and we don't subscribe again.
|
|
337
358
|
(_a = this.urlChangeCleanup) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
359
|
+
// A new init always allows capture again. `stop()` only pauses the current instance.
|
|
360
|
+
this.recordingEnabled = true;
|
|
361
|
+
this.startChildRecordingOnSetup = false;
|
|
338
362
|
this.loggerProvider = new logger_1.SafeLoggerProvider(options.loggerProvider || new analytics_core_1.Logger());
|
|
339
363
|
Object.prototype.hasOwnProperty.call(options, 'logLevel') &&
|
|
340
364
|
this.loggerProvider.enable(options.logLevel);
|
|
@@ -696,6 +720,10 @@ var SessionReplay = /** @class */ (function () {
|
|
|
696
720
|
this.loggerProvider.warn("Session is not being recorded due to lack of config, please call sessionReplay.init.");
|
|
697
721
|
return false;
|
|
698
722
|
}
|
|
723
|
+
if (!this.recordingEnabled) {
|
|
724
|
+
this.loggerProvider.log("Session ".concat(this.identifiers.sessionId, " not being captured because recording was stopped."));
|
|
725
|
+
return false;
|
|
726
|
+
}
|
|
699
727
|
if (!this.config.captureEnabled) {
|
|
700
728
|
this.loggerProvider.log("Session ".concat(this.identifiers.sessionId, " not being captured due to capture being disabled for project or because the remote config could not be fetched."));
|
|
701
729
|
return false;
|
|
@@ -888,15 +916,17 @@ var SessionReplay = /** @class */ (function () {
|
|
|
888
916
|
});
|
|
889
917
|
});
|
|
890
918
|
};
|
|
919
|
+
SessionReplay.prototype.shouldAbandonRecordStart = function (generation) {
|
|
920
|
+
return !this.recordingEnabled || generation !== this.recordingGeneration;
|
|
921
|
+
};
|
|
891
922
|
SessionReplay.prototype._recordEvents = function (shouldLogMetadata) {
|
|
892
923
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
893
924
|
if (shouldLogMetadata === void 0) { shouldLogMetadata = true; }
|
|
894
925
|
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
895
|
-
var config, shouldRecord, sessionId, recordFunction, networkLoggingConfig, trackUrl, ignoredUrls, interactionConfig, loggingConfig, hooks, ugcFilterRules, crossOriginIframesEnabled, coordinateChildren, childMode,
|
|
896
|
-
var _m;
|
|
926
|
+
var config, shouldRecord, sessionId, generation, recordFunction, networkLoggingConfig, trackUrl, ignoredUrls, interactionConfig, loggingConfig, hooks, ugcFilterRules, crossOriginIframesEnabled, coordinateChildren, childMode, plugins, error_5;
|
|
897
927
|
var _this = this;
|
|
898
|
-
return tslib_1.__generator(this, function (
|
|
899
|
-
switch (
|
|
928
|
+
return tslib_1.__generator(this, function (_j) {
|
|
929
|
+
switch (_j.label) {
|
|
900
930
|
case 0:
|
|
901
931
|
config = this.config;
|
|
902
932
|
shouldRecord = this.getShouldRecord();
|
|
@@ -905,16 +935,20 @@ var SessionReplay = /** @class */ (function () {
|
|
|
905
935
|
return [2 /*return*/];
|
|
906
936
|
}
|
|
907
937
|
this.stopRecordingEvents();
|
|
938
|
+
generation = this.recordingGeneration;
|
|
908
939
|
return [4 /*yield*/, this.getRecordFunction()];
|
|
909
940
|
case 1:
|
|
910
|
-
recordFunction =
|
|
941
|
+
recordFunction = _j.sent();
|
|
911
942
|
// May be undefined if cannot import rrweb-record
|
|
912
|
-
if (!recordFunction) {
|
|
943
|
+
if (!recordFunction || this.shouldAbandonRecordStart(generation)) {
|
|
913
944
|
return [2 /*return*/];
|
|
914
945
|
}
|
|
915
946
|
return [4 /*yield*/, this.initializeNetworkObservers()];
|
|
916
947
|
case 2:
|
|
917
|
-
|
|
948
|
+
_j.sent();
|
|
949
|
+
if (this.shouldAbandonRecordStart(generation)) {
|
|
950
|
+
return [2 /*return*/];
|
|
951
|
+
}
|
|
918
952
|
networkLoggingConfig = (_b = config.loggingConfig) === null || _b === void 0 ? void 0 : _b.network;
|
|
919
953
|
trackUrl = (0, helpers_1.getServerUrl)(config.serverZone, config.trackServerUrl);
|
|
920
954
|
ignoredUrls = [constants_1.SESSION_REPLAY_SERVER_URL, constants_1.SESSION_REPLAY_EU_URL, constants_1.SESSION_REPLAY_STAGING_URL, trackUrl];
|
|
@@ -940,9 +974,9 @@ var SessionReplay = /** @class */ (function () {
|
|
|
940
974
|
: {};
|
|
941
975
|
ugcFilterRules = (interactionConfig === null || interactionConfig === void 0 ? void 0 : interactionConfig.enabled) && interactionConfig.ugcFilterRules ? interactionConfig.ugcFilterRules : [];
|
|
942
976
|
this.loggerProvider.log("Session Replay capture beginning for ".concat(sessionId, "."));
|
|
943
|
-
|
|
977
|
+
_j.label = 3;
|
|
944
978
|
case 3:
|
|
945
|
-
|
|
979
|
+
_j.trys.push([3, 5, , 6]);
|
|
946
980
|
crossOriginIframesEnabled = !!((_g = config.crossOriginIframes) === null || _g === void 0 ? void 0 : _g.enabled);
|
|
947
981
|
coordinateChildren = ((_h = config.crossOriginIframes) === null || _h === void 0 ? void 0 : _h.coordinateChildren) !== false;
|
|
948
982
|
childMode = crossOriginIframesEnabled && (0, cross_origin_iframes_1.isInIframe)();
|
|
@@ -950,7 +984,12 @@ var SessionReplay = /** @class */ (function () {
|
|
|
950
984
|
// Child mode: don't self-start; wait for a start signal from the parent.
|
|
951
985
|
// (The previous listener, if any, was already removed by stopRecordingEvents above.)
|
|
952
986
|
this.crossOriginParentSignalCleanup = (0, cross_origin_iframes_1.listenForParentSignals)({
|
|
953
|
-
onStart: function () {
|
|
987
|
+
onStart: function () {
|
|
988
|
+
if (!_this.recordingEnabled) {
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
_this._recordEventsInChildMode(recordFunction, sessionId, config, hooks);
|
|
992
|
+
},
|
|
954
993
|
onStop: function () {
|
|
955
994
|
try {
|
|
956
995
|
// Only cancel the rrweb recording — do NOT call stopRecordingEvents() here,
|
|
@@ -965,34 +1004,43 @@ var SessionReplay = /** @class */ (function () {
|
|
|
965
1004
|
}
|
|
966
1005
|
},
|
|
967
1006
|
});
|
|
1007
|
+
// Customer start() must not wait for a parent signal: the parent may already
|
|
1008
|
+
// be recording and will not send another start. Init still waits.
|
|
1009
|
+
if (this.startChildRecordingOnSetup) {
|
|
1010
|
+
this.startChildRecordingOnSetup = false;
|
|
1011
|
+
this._recordEventsInChildMode(recordFunction, sessionId, config, hooks);
|
|
1012
|
+
}
|
|
968
1013
|
return [2 /*return*/];
|
|
969
1014
|
}
|
|
970
|
-
|
|
971
|
-
_k = recordFunction;
|
|
972
|
-
_l = [tslib_1.__assign({}, this.buildRRWebRecordOptions(config, hooks, function (event) {
|
|
973
|
-
if (_this.shouldOptOut()) {
|
|
974
|
-
_this.loggerProvider.log("Opting session ".concat(sessionId, " out of recording due to optOut config."));
|
|
975
|
-
_this.stopRecordingEvents();
|
|
976
|
-
_this.sendEvents();
|
|
977
|
-
return;
|
|
978
|
-
}
|
|
979
|
-
if (event.type === rrweb_types_1.EventType.Meta) {
|
|
980
|
-
event.data.href = (0, helpers_1.getPageUrl)(event.data.href, ugcFilterRules);
|
|
981
|
-
}
|
|
982
|
-
if (_this.eventCompressor) {
|
|
983
|
-
// Schedule processing during idle time if the browser supports requestIdleCallback
|
|
984
|
-
_this.eventCompressor.enqueueEvent(event, sessionId);
|
|
985
|
-
}
|
|
986
|
-
else {
|
|
987
|
-
// eventCompressor is not yet ready (concurrent call racing _init()).
|
|
988
|
-
// Buffer the event so it can be flushed once the compressor is initialized.
|
|
989
|
-
_this.pendingEmitEvents.push({ event: event, sessionId: sessionId });
|
|
990
|
-
}
|
|
991
|
-
}, 'Error while capturing replay: '))];
|
|
992
|
-
_m = {};
|
|
1015
|
+
this.startChildRecordingOnSetup = false;
|
|
993
1016
|
return [4 /*yield*/, this.getRecordingPlugins(loggingConfig)];
|
|
994
1017
|
case 4:
|
|
995
|
-
|
|
1018
|
+
plugins = _j.sent();
|
|
1019
|
+
if (this.shouldAbandonRecordStart(generation)) {
|
|
1020
|
+
return [2 /*return*/];
|
|
1021
|
+
}
|
|
1022
|
+
this.recordCancelCallback = recordFunction(tslib_1.__assign(tslib_1.__assign({}, this.buildRRWebRecordOptions(config, hooks, function (event) {
|
|
1023
|
+
if (_this.shouldOptOut() || !_this.recordingEnabled) {
|
|
1024
|
+
_this.loggerProvider.log(_this.shouldOptOut()
|
|
1025
|
+
? "Opting session ".concat(sessionId, " out of recording due to optOut config.")
|
|
1026
|
+
: "Session Replay capture stopping for ".concat(sessionId, "."));
|
|
1027
|
+
_this.stopRecordingEvents();
|
|
1028
|
+
_this.sendEvents();
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
if (event.type === rrweb_types_1.EventType.Meta) {
|
|
1032
|
+
event.data.href = (0, helpers_1.getPageUrl)(event.data.href, ugcFilterRules);
|
|
1033
|
+
}
|
|
1034
|
+
if (_this.eventCompressor) {
|
|
1035
|
+
// Schedule processing during idle time if the browser supports requestIdleCallback
|
|
1036
|
+
_this.eventCompressor.enqueueEvent(event, sessionId);
|
|
1037
|
+
}
|
|
1038
|
+
else {
|
|
1039
|
+
// eventCompressor is not yet ready (concurrent call racing _init()).
|
|
1040
|
+
// Buffer the event so it can be flushed once the compressor is initialized.
|
|
1041
|
+
_this.pendingEmitEvents.push({ event: event, sessionId: sessionId });
|
|
1042
|
+
}
|
|
1043
|
+
}, 'Error while capturing replay: ')), { plugins: plugins, recordCrossOriginIframes: crossOriginIframesEnabled }));
|
|
996
1044
|
if (crossOriginIframesEnabled && !childMode && coordinateChildren) {
|
|
997
1045
|
if (!this.crossOriginIframeCoordinator) {
|
|
998
1046
|
this.crossOriginIframeCoordinator = new cross_origin_iframes_1.CrossOriginIframeCoordinator();
|
|
@@ -1005,7 +1053,7 @@ var SessionReplay = /** @class */ (function () {
|
|
|
1005
1053
|
}
|
|
1006
1054
|
return [3 /*break*/, 6];
|
|
1007
1055
|
case 5:
|
|
1008
|
-
error_5 =
|
|
1056
|
+
error_5 = _j.sent();
|
|
1009
1057
|
this.loggerProvider.warn('Failed to initialize session replay:', error_5);
|
|
1010
1058
|
return [3 /*break*/, 6];
|
|
1011
1059
|
case 6: return [2 /*return*/];
|
|
@@ -1016,7 +1064,7 @@ var SessionReplay = /** @class */ (function () {
|
|
|
1016
1064
|
SessionReplay.prototype.buildRRWebRecordOptions = function (config, hooks, emit, errorLogPrefix) {
|
|
1017
1065
|
var _this = this;
|
|
1018
1066
|
var privacyConfig = config.privacyConfig;
|
|
1019
|
-
return tslib_1.__assign(tslib_1.__assign({ emit: emit, inlineStylesheet: config.shouldInlineStylesheet, hooks: hooks, maskAllInputs: true, maskTextClass: constants_1.MASK_TEXT_CLASS, blockClass: constants_1.BLOCK_CLASS, blockSelector: this.getBlockSelectors(), applyBackgroundColorToBlockedElements: config.applyBackgroundColorToBlockedElements, maskInputFn: (0, helpers_1.maskFn)('input', privacyConfig, function () { return _this.currentPageUrl; }), maskTextFn: (0, helpers_1.maskFn)('text', privacyConfig, function () { return _this.currentPageUrl; }), maskAttributeFn: (0, helpers_1.maskAttributeFn)(privacyConfig, function () { return _this.currentPageUrl; }), maskTextSelector: this.getMaskTextSelectors() }, (config.fullSnapshotIntervalMs !== undefined && { checkoutEveryNms: config.fullSnapshotIntervalMs })), { recordCanvas: false, captureAdoptedStyleSheets: config.captureAdoptedStyleSheets,
|
|
1067
|
+
return tslib_1.__assign(tslib_1.__assign({ emit: emit, inlineStylesheet: config.shouldInlineStylesheet, inlineImages: config.inlineImages, hooks: hooks, maskAllInputs: true, maskTextClass: constants_1.MASK_TEXT_CLASS, blockClass: constants_1.BLOCK_CLASS, blockSelector: this.getBlockSelectors(), applyBackgroundColorToBlockedElements: config.applyBackgroundColorToBlockedElements, maskInputFn: (0, helpers_1.maskFn)('input', privacyConfig, function () { return _this.currentPageUrl; }), maskTextFn: (0, helpers_1.maskFn)('text', privacyConfig, function () { return _this.currentPageUrl; }), maskAttributeFn: (0, helpers_1.maskAttributeFn)(privacyConfig, function () { return _this.currentPageUrl; }), maskTextSelector: this.getMaskTextSelectors() }, (config.fullSnapshotIntervalMs !== undefined && { checkoutEveryNms: config.fullSnapshotIntervalMs })), { recordCanvas: false, captureAdoptedStyleSheets: config.captureAdoptedStyleSheets,
|
|
1020
1068
|
// Strip nodes that are never rendered by the rrweb replay player.
|
|
1021
1069
|
// None of these affect visual fidelity; omitting them reduces snapshot size.
|
|
1022
1070
|
slimDOMOptions: {
|
|
@@ -1075,6 +1123,31 @@ var SessionReplay = /** @class */ (function () {
|
|
|
1075
1123
|
var _a;
|
|
1076
1124
|
return (_a = this.identifiers) === null || _a === void 0 ? void 0 : _a.sessionId;
|
|
1077
1125
|
};
|
|
1126
|
+
SessionReplay.prototype.start = function () {
|
|
1127
|
+
return (0, analytics_core_1.returnWrapper)(this._start());
|
|
1128
|
+
};
|
|
1129
|
+
SessionReplay.prototype._start = function () {
|
|
1130
|
+
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
1131
|
+
return tslib_1.__generator(this, function (_a) {
|
|
1132
|
+
switch (_a.label) {
|
|
1133
|
+
case 0:
|
|
1134
|
+
this.recordingEnabled = true;
|
|
1135
|
+
this.startChildRecordingOnSetup = true;
|
|
1136
|
+
return [4 /*yield*/, this.recordEvents()];
|
|
1137
|
+
case 1:
|
|
1138
|
+
_a.sent();
|
|
1139
|
+
return [2 /*return*/];
|
|
1140
|
+
}
|
|
1141
|
+
});
|
|
1142
|
+
});
|
|
1143
|
+
};
|
|
1144
|
+
SessionReplay.prototype.stop = function () {
|
|
1145
|
+
this.recordingEnabled = false;
|
|
1146
|
+
this.startChildRecordingOnSetup = false;
|
|
1147
|
+
this.recordingGeneration++;
|
|
1148
|
+
this.stopRecordingEvents();
|
|
1149
|
+
this.sendEvents();
|
|
1150
|
+
};
|
|
1078
1151
|
SessionReplay.prototype.flush = function (useRetry) {
|
|
1079
1152
|
var _a;
|
|
1080
1153
|
if (useRetry === void 0) { useRetry = false; }
|
|
@@ -1091,6 +1164,9 @@ var SessionReplay = /** @class */ (function () {
|
|
|
1091
1164
|
};
|
|
1092
1165
|
SessionReplay.prototype.shutdown = function () {
|
|
1093
1166
|
var _a, _b;
|
|
1167
|
+
this.recordingEnabled = false;
|
|
1168
|
+
this.startChildRecordingOnSetup = false;
|
|
1169
|
+
this.recordingGeneration++;
|
|
1094
1170
|
(_a = this.urlChangeCleanup) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
1095
1171
|
(_b = this.crossOriginParentSignalCleanup) === null || _b === void 0 ? void 0 : _b.call(this);
|
|
1096
1172
|
this.crossOriginParentSignalCleanup = null;
|