posthog-js 1.297.2 → 1.297.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.
Files changed (56) hide show
  1. package/dist/array.full.es5.js +1 -1
  2. package/dist/array.full.js +1 -1
  3. package/dist/array.full.no-external.js +1 -1
  4. package/dist/array.js +1 -1
  5. package/dist/array.no-external.js +1 -1
  6. package/dist/customizations.full.js +1 -1
  7. package/dist/lazy-recorder.js +1 -1
  8. package/dist/main.js +1 -1
  9. package/dist/module.full.js +1 -1
  10. package/dist/module.full.no-external.js +1 -1
  11. package/dist/module.js +1 -1
  12. package/dist/module.no-external.js +1 -1
  13. package/dist/posthog-recorder.js +1 -1
  14. package/dist/src/extensions/exception-autocapture/chunk-ids.d.ts +4 -0
  15. package/dist/src/extensions/exception-autocapture/error-conversion.d.ts +62 -0
  16. package/dist/src/extensions/exception-autocapture/stack-trace.d.ts +31 -0
  17. package/dist/src/extensions/exception-autocapture/type-checking.d.ts +10 -0
  18. package/dist/src/extensions/replay/config.d.ts +9 -0
  19. package/dist/src/extensions/replay/mutation-throttler.d.ts +18 -0
  20. package/dist/src/extensions/replay/sessionrecording-utils.d.ts +19 -0
  21. package/dist/src/extensions/replay/sessionrecording-wrapper.d.ts +75 -0
  22. package/dist/src/extensions/replay/sessionrecording.d.ts +163 -0
  23. package/dist/src/extensions/replay/triggerMatching.d.ts +99 -0
  24. package/lib/package.json +1 -1
  25. package/lib/src/extensions/exception-autocapture/chunk-ids.d.ts +4 -0
  26. package/lib/src/extensions/exception-autocapture/chunk-ids.js +44 -0
  27. package/lib/src/extensions/exception-autocapture/chunk-ids.js.map +1 -0
  28. package/lib/src/extensions/exception-autocapture/error-conversion.d.ts +62 -0
  29. package/lib/src/extensions/exception-autocapture/error-conversion.js +324 -0
  30. package/lib/src/extensions/exception-autocapture/error-conversion.js.map +1 -0
  31. package/lib/src/extensions/exception-autocapture/stack-trace.d.ts +31 -0
  32. package/lib/src/extensions/exception-autocapture/stack-trace.js +283 -0
  33. package/lib/src/extensions/exception-autocapture/stack-trace.js.map +1 -0
  34. package/lib/src/extensions/exception-autocapture/type-checking.d.ts +10 -0
  35. package/lib/src/extensions/exception-autocapture/type-checking.js +59 -0
  36. package/lib/src/extensions/exception-autocapture/type-checking.js.map +1 -0
  37. package/lib/src/extensions/replay/config.d.ts +9 -0
  38. package/lib/src/extensions/replay/config.js +248 -0
  39. package/lib/src/extensions/replay/config.js.map +1 -0
  40. package/lib/src/extensions/replay/mutation-throttler.d.ts +18 -0
  41. package/lib/src/extensions/replay/mutation-throttler.js +96 -0
  42. package/lib/src/extensions/replay/mutation-throttler.js.map +1 -0
  43. package/lib/src/extensions/replay/sessionrecording-utils.d.ts +19 -0
  44. package/lib/src/extensions/replay/sessionrecording-utils.js +157 -0
  45. package/lib/src/extensions/replay/sessionrecording-utils.js.map +1 -0
  46. package/lib/src/extensions/replay/sessionrecording-wrapper.d.ts +75 -0
  47. package/lib/src/extensions/replay/sessionrecording-wrapper.js +285 -0
  48. package/lib/src/extensions/replay/sessionrecording-wrapper.js.map +1 -0
  49. package/lib/src/extensions/replay/sessionrecording.d.ts +163 -0
  50. package/lib/src/extensions/replay/sessionrecording.js +1208 -0
  51. package/lib/src/extensions/replay/sessionrecording.js.map +1 -0
  52. package/lib/src/extensions/replay/triggerMatching.d.ts +99 -0
  53. package/lib/src/extensions/replay/triggerMatching.js +390 -0
  54. package/lib/src/extensions/replay/triggerMatching.js.map +1 -0
  55. package/lib/tsconfig.tsbuildinfo +1 -1
  56. package/package.json +2 -2
@@ -0,0 +1,75 @@
1
+ import { PostHog } from '../../posthog-core';
2
+ import { Properties, RemoteConfig, SessionStartReason } from '../../types';
3
+ import { type eventWithTime } from '@rrweb/types';
4
+ import { SessionRecordingStatus, TriggerType } from './triggerMatching';
5
+ /**
6
+ * This only exists to let us test changes to sessionrecording.ts before rolling them out to everyone
7
+ * it should not be depended on in other ways, since i'm going to delete it long before the end of September 2025
8
+ */
9
+ export declare class SessionRecordingWrapper {
10
+ private readonly _instance;
11
+ _forceAllowLocalhostNetworkCapture: boolean;
12
+ private _receivedFlags;
13
+ private _persistFlagsOnSessionListener;
14
+ private _lazyLoadedSessionRecording;
15
+ get started(): boolean;
16
+ /**
17
+ * defaults to buffering mode until a flags response is received
18
+ * once a flags response is received status can be disabled, active or sampled
19
+ */
20
+ get status(): SessionRecordingStatus;
21
+ constructor(_instance: PostHog);
22
+ private get _isRecordingEnabled();
23
+ startIfEnabledOrStop(startReason?: SessionStartReason): void;
24
+ /**
25
+ * session recording waits until it receives remote config before loading the script
26
+ * this is to ensure we can control the script name remotely
27
+ * and because we wait until we have local and remote config to determine if we should start at all
28
+ * if start is called and there is no remote config then we wait until there is
29
+ */
30
+ private _lazyLoadAndStart;
31
+ stopRecording(): void;
32
+ private _resetSampling;
33
+ private _persistRemoteConfig;
34
+ onRemoteConfig(response: RemoteConfig): void;
35
+ log(message: string, level?: 'log' | 'warn' | 'error'): void;
36
+ private get _scriptName();
37
+ private _onScriptLoaded;
38
+ /**
39
+ * this is maintained on the public API only because it has always been on the public API
40
+ * if you are calling this directly you are certainly doing something wrong
41
+ * @deprecated
42
+ */
43
+ onRRwebEmit(rawEvent: eventWithTime): void;
44
+ /**
45
+ * this ignores the linked flag config and (if other conditions are met) causes capture to start
46
+ *
47
+ * It is not usual to call this directly,
48
+ * instead call `posthog.startSessionRecording({linked_flag: true})`
49
+ * */
50
+ overrideLinkedFlag(): void;
51
+ /**
52
+ * this ignores the sampling config and (if other conditions are met) causes capture to start
53
+ *
54
+ * It is not usual to call this directly,
55
+ * instead call `posthog.startSessionRecording({sampling: true})`
56
+ * */
57
+ overrideSampling(): void;
58
+ /**
59
+ * this ignores the URL/Event trigger config and (if other conditions are met) causes capture to start
60
+ *
61
+ * It is not usual to call this directly,
62
+ * instead call `posthog.startSessionRecording({trigger: 'url' | 'event'})`
63
+ * */
64
+ overrideTrigger(triggerType: TriggerType): void;
65
+ get sdkDebugProperties(): Properties;
66
+ /**
67
+ * This adds a custom event to the session recording
68
+ *
69
+ * It is not intended for arbitrary public use - playback only displays known custom events
70
+ * And is exposed on the public interface only so that other parts of the SDK are able to use it
71
+ *
72
+ * if you are calling this from client code, you're probably looking for `posthog.capture('$custom_event', {...})`
73
+ */
74
+ tryAddCustomEvent(tag: string, payload: any): boolean;
75
+ }
@@ -0,0 +1,285 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.SessionRecordingWrapper = void 0;
15
+ var constants_1 = require("../../constants");
16
+ var core_1 = require("@posthog/core");
17
+ var logger_1 = require("../../utils/logger");
18
+ var globals_1 = require("../../utils/globals");
19
+ var triggerMatching_1 = require("./triggerMatching");
20
+ var LOGGER_PREFIX = '[SessionRecording]';
21
+ var logger = (0, logger_1.createLogger)(LOGGER_PREFIX);
22
+ /**
23
+ * This only exists to let us test changes to sessionrecording.ts before rolling them out to everyone
24
+ * it should not be depended on in other ways, since i'm going to delete it long before the end of September 2025
25
+ */
26
+ var SessionRecordingWrapper = /** @class */ (function () {
27
+ function SessionRecordingWrapper(_instance) {
28
+ this._instance = _instance;
29
+ this._forceAllowLocalhostNetworkCapture = false;
30
+ this._receivedFlags = false;
31
+ this._persistFlagsOnSessionListener = undefined;
32
+ if (!this._instance.sessionManager) {
33
+ logger.error('started without valid sessionManager');
34
+ throw new Error(LOGGER_PREFIX + ' started without valid sessionManager. This is a bug.');
35
+ }
36
+ if (this._instance.config.cookieless_mode === 'always') {
37
+ throw new Error(LOGGER_PREFIX + ' cannot be used with cookieless_mode="always"');
38
+ }
39
+ }
40
+ Object.defineProperty(SessionRecordingWrapper.prototype, "started", {
41
+ get: function () {
42
+ var _a;
43
+ return !!((_a = this._lazyLoadedSessionRecording) === null || _a === void 0 ? void 0 : _a.isStarted);
44
+ },
45
+ enumerable: false,
46
+ configurable: true
47
+ });
48
+ Object.defineProperty(SessionRecordingWrapper.prototype, "status", {
49
+ /**
50
+ * defaults to buffering mode until a flags response is received
51
+ * once a flags response is received status can be disabled, active or sampled
52
+ */
53
+ get: function () {
54
+ if (this._lazyLoadedSessionRecording) {
55
+ return this._lazyLoadedSessionRecording.status;
56
+ }
57
+ if (this._receivedFlags && !this._isRecordingEnabled) {
58
+ return triggerMatching_1.DISABLED;
59
+ }
60
+ return triggerMatching_1.LAZY_LOADING;
61
+ },
62
+ enumerable: false,
63
+ configurable: true
64
+ });
65
+ Object.defineProperty(SessionRecordingWrapper.prototype, "_isRecordingEnabled", {
66
+ get: function () {
67
+ var _a;
68
+ var enabled_server_side = !!((_a = this._instance.get_property(constants_1.SESSION_RECORDING_REMOTE_CONFIG)) === null || _a === void 0 ? void 0 : _a.enabled);
69
+ var enabled_client_side = !this._instance.config.disable_session_recording;
70
+ var isDisabled = this._instance.config.disable_session_recording || this._instance.consent.isOptedOut();
71
+ return globals_1.window && enabled_server_side && enabled_client_side && !isDisabled;
72
+ },
73
+ enumerable: false,
74
+ configurable: true
75
+ });
76
+ SessionRecordingWrapper.prototype.startIfEnabledOrStop = function (startReason) {
77
+ var _a;
78
+ if (this._isRecordingEnabled && ((_a = this._lazyLoadedSessionRecording) === null || _a === void 0 ? void 0 : _a.isStarted)) {
79
+ return;
80
+ }
81
+ // According to the rrweb docs, rrweb is not supported on IE11 and below:
82
+ // "rrweb does not support IE11 and below because it uses the MutationObserver API, which was supported by these browsers."
83
+ // https://github.com/rrweb-io/rrweb/blob/master/guide.md#compatibility-note
84
+ //
85
+ // However, MutationObserver does exist on IE11, it just doesn't work well and does not detect all changes.
86
+ // Instead, when we load "recorder.js", the first JS error is about "Object.assign" and "Array.from" being undefined.
87
+ // Thus instead of MutationObserver, we look for this function and block recording if it's undefined.
88
+ var canRunReplay = !(0, core_1.isUndefined)(Object.assign) && !(0, core_1.isUndefined)(Array.from);
89
+ if (this._isRecordingEnabled && canRunReplay) {
90
+ this._lazyLoadAndStart(startReason);
91
+ logger.info('starting');
92
+ }
93
+ else {
94
+ this.stopRecording();
95
+ }
96
+ };
97
+ /**
98
+ * session recording waits until it receives remote config before loading the script
99
+ * this is to ensure we can control the script name remotely
100
+ * and because we wait until we have local and remote config to determine if we should start at all
101
+ * if start is called and there is no remote config then we wait until there is
102
+ */
103
+ SessionRecordingWrapper.prototype._lazyLoadAndStart = function (startReason) {
104
+ var _this = this;
105
+ var _a, _b, _c, _d, _e;
106
+ // by checking `_isRecordingEnabled` here we know that
107
+ // we have stored remote config and client config to read
108
+ // replay waits for both local and remote config before starting
109
+ if (!this._isRecordingEnabled) {
110
+ return;
111
+ }
112
+ // If recorder.js is already loaded (if array.full.js snippet is used or posthog-js/dist/recorder is
113
+ // imported), don't load the script. Otherwise, remotely import recorder.js from cdn since it hasn't been loaded.
114
+ if (!((_b = (_a = globals_1.assignableWindow === null || globals_1.assignableWindow === void 0 ? void 0 : globals_1.assignableWindow.__PosthogExtensions__) === null || _a === void 0 ? void 0 : _a.rrweb) === null || _b === void 0 ? void 0 : _b.record) ||
115
+ !((_c = globals_1.assignableWindow.__PosthogExtensions__) === null || _c === void 0 ? void 0 : _c.initSessionRecording)) {
116
+ (_e = (_d = globals_1.assignableWindow.__PosthogExtensions__) === null || _d === void 0 ? void 0 : _d.loadExternalDependency) === null || _e === void 0 ? void 0 : _e.call(_d, this._instance, this._scriptName, function (err) {
117
+ if (err) {
118
+ return logger.error('could not load recorder', err);
119
+ }
120
+ _this._onScriptLoaded(startReason);
121
+ });
122
+ }
123
+ else {
124
+ this._onScriptLoaded(startReason);
125
+ }
126
+ };
127
+ SessionRecordingWrapper.prototype.stopRecording = function () {
128
+ var _a;
129
+ (_a = this._lazyLoadedSessionRecording) === null || _a === void 0 ? void 0 : _a.stop();
130
+ };
131
+ SessionRecordingWrapper.prototype._resetSampling = function () {
132
+ var _a;
133
+ (_a = this._instance.persistence) === null || _a === void 0 ? void 0 : _a.unregister(constants_1.SESSION_RECORDING_IS_SAMPLED);
134
+ };
135
+ SessionRecordingWrapper.prototype._persistRemoteConfig = function (response) {
136
+ var _this = this;
137
+ var _a, _b;
138
+ if (this._instance.persistence) {
139
+ var persistence_1 = this._instance.persistence;
140
+ var persistResponse = function () {
141
+ var _a;
142
+ var sessionRecordingConfigResponse = response.sessionRecording === false ? undefined : response.sessionRecording;
143
+ var receivedSampleRate = sessionRecordingConfigResponse === null || sessionRecordingConfigResponse === void 0 ? void 0 : sessionRecordingConfigResponse.sampleRate;
144
+ var parsedSampleRate = (0, core_1.isNullish)(receivedSampleRate) ? null : parseFloat(receivedSampleRate);
145
+ if ((0, core_1.isNullish)(parsedSampleRate)) {
146
+ _this._resetSampling();
147
+ }
148
+ var receivedMinimumDuration = sessionRecordingConfigResponse === null || sessionRecordingConfigResponse === void 0 ? void 0 : sessionRecordingConfigResponse.minimumDurationMilliseconds;
149
+ persistence_1.register((_a = {},
150
+ _a[constants_1.SESSION_RECORDING_REMOTE_CONFIG] = __assign(__assign({ enabled: !!sessionRecordingConfigResponse }, sessionRecordingConfigResponse), { networkPayloadCapture: __assign({ capturePerformance: response.capturePerformance }, sessionRecordingConfigResponse === null || sessionRecordingConfigResponse === void 0 ? void 0 : sessionRecordingConfigResponse.networkPayloadCapture), canvasRecording: {
151
+ enabled: sessionRecordingConfigResponse === null || sessionRecordingConfigResponse === void 0 ? void 0 : sessionRecordingConfigResponse.recordCanvas,
152
+ fps: sessionRecordingConfigResponse === null || sessionRecordingConfigResponse === void 0 ? void 0 : sessionRecordingConfigResponse.canvasFps,
153
+ quality: sessionRecordingConfigResponse === null || sessionRecordingConfigResponse === void 0 ? void 0 : sessionRecordingConfigResponse.canvasQuality,
154
+ }, sampleRate: parsedSampleRate, minimumDurationMilliseconds: (0, core_1.isUndefined)(receivedMinimumDuration)
155
+ ? null
156
+ : receivedMinimumDuration, endpoint: sessionRecordingConfigResponse === null || sessionRecordingConfigResponse === void 0 ? void 0 : sessionRecordingConfigResponse.endpoint, triggerMatchType: sessionRecordingConfigResponse === null || sessionRecordingConfigResponse === void 0 ? void 0 : sessionRecordingConfigResponse.triggerMatchType, masking: sessionRecordingConfigResponse === null || sessionRecordingConfigResponse === void 0 ? void 0 : sessionRecordingConfigResponse.masking, urlTriggers: sessionRecordingConfigResponse === null || sessionRecordingConfigResponse === void 0 ? void 0 : sessionRecordingConfigResponse.urlTriggers }),
157
+ _a));
158
+ };
159
+ persistResponse();
160
+ // in case we see multiple flags responses, we should only use the response from the most recent one
161
+ (_a = this._persistFlagsOnSessionListener) === null || _a === void 0 ? void 0 : _a.call(this);
162
+ // we 100% know there is a session manager by this point
163
+ this._persistFlagsOnSessionListener = (_b = this._instance.sessionManager) === null || _b === void 0 ? void 0 : _b.onSessionId(persistResponse);
164
+ }
165
+ };
166
+ SessionRecordingWrapper.prototype.onRemoteConfig = function (response) {
167
+ if (!('sessionRecording' in response)) {
168
+ // if sessionRecording is not in the response, we do nothing
169
+ logger.info('skipping remote config with no sessionRecording', response);
170
+ return;
171
+ }
172
+ if (response.sessionRecording === false) {
173
+ // remotely disabled
174
+ this._receivedFlags = true;
175
+ return;
176
+ }
177
+ this._persistRemoteConfig(response);
178
+ this._receivedFlags = true;
179
+ // TODO how do we send a custom message with the received remote config like we used to for debug
180
+ this.startIfEnabledOrStop();
181
+ };
182
+ SessionRecordingWrapper.prototype.log = function (message, level) {
183
+ var _a;
184
+ if (level === void 0) { level = 'log'; }
185
+ if ((_a = this._lazyLoadedSessionRecording) === null || _a === void 0 ? void 0 : _a.log) {
186
+ this._lazyLoadedSessionRecording.log(message, level);
187
+ }
188
+ else {
189
+ logger.warn('log called before recorder was ready');
190
+ }
191
+ };
192
+ Object.defineProperty(SessionRecordingWrapper.prototype, "_scriptName", {
193
+ get: function () {
194
+ var _a, _b, _c;
195
+ var remoteConfig = (_b = (_a = this._instance) === null || _a === void 0 ? void 0 : _a.persistence) === null || _b === void 0 ? void 0 : _b.get_property(constants_1.SESSION_RECORDING_REMOTE_CONFIG);
196
+ return ((_c = remoteConfig === null || remoteConfig === void 0 ? void 0 : remoteConfig.scriptConfig) === null || _c === void 0 ? void 0 : _c.script) || 'lazy-recorder';
197
+ },
198
+ enumerable: false,
199
+ configurable: true
200
+ });
201
+ SessionRecordingWrapper.prototype._onScriptLoaded = function (startReason) {
202
+ var _a, _b;
203
+ if (!((_a = globals_1.assignableWindow.__PosthogExtensions__) === null || _a === void 0 ? void 0 : _a.initSessionRecording)) {
204
+ throw Error('Called on script loaded before session recording is available');
205
+ }
206
+ if (!this._lazyLoadedSessionRecording) {
207
+ this._lazyLoadedSessionRecording = (_b = globals_1.assignableWindow.__PosthogExtensions__) === null || _b === void 0 ? void 0 : _b.initSessionRecording(this._instance);
208
+ this._lazyLoadedSessionRecording._forceAllowLocalhostNetworkCapture =
209
+ this._forceAllowLocalhostNetworkCapture;
210
+ }
211
+ this._lazyLoadedSessionRecording.start(startReason);
212
+ };
213
+ /**
214
+ * this is maintained on the public API only because it has always been on the public API
215
+ * if you are calling this directly you are certainly doing something wrong
216
+ * @deprecated
217
+ */
218
+ SessionRecordingWrapper.prototype.onRRwebEmit = function (rawEvent) {
219
+ var _a, _b;
220
+ (_b = (_a = this._lazyLoadedSessionRecording) === null || _a === void 0 ? void 0 : _a.onRRwebEmit) === null || _b === void 0 ? void 0 : _b.call(_a, rawEvent);
221
+ };
222
+ /**
223
+ * this ignores the linked flag config and (if other conditions are met) causes capture to start
224
+ *
225
+ * It is not usual to call this directly,
226
+ * instead call `posthog.startSessionRecording({linked_flag: true})`
227
+ * */
228
+ SessionRecordingWrapper.prototype.overrideLinkedFlag = function () {
229
+ var _a;
230
+ // TODO what if this gets called before lazy loading is done
231
+ (_a = this._lazyLoadedSessionRecording) === null || _a === void 0 ? void 0 : _a.overrideLinkedFlag();
232
+ };
233
+ /**
234
+ * this ignores the sampling config and (if other conditions are met) causes capture to start
235
+ *
236
+ * It is not usual to call this directly,
237
+ * instead call `posthog.startSessionRecording({sampling: true})`
238
+ * */
239
+ SessionRecordingWrapper.prototype.overrideSampling = function () {
240
+ var _a;
241
+ // TODO what if this gets called before lazy loading is done
242
+ (_a = this._lazyLoadedSessionRecording) === null || _a === void 0 ? void 0 : _a.overrideSampling();
243
+ };
244
+ /**
245
+ * this ignores the URL/Event trigger config and (if other conditions are met) causes capture to start
246
+ *
247
+ * It is not usual to call this directly,
248
+ * instead call `posthog.startSessionRecording({trigger: 'url' | 'event'})`
249
+ * */
250
+ SessionRecordingWrapper.prototype.overrideTrigger = function (triggerType) {
251
+ var _a;
252
+ // TODO what if this gets called before lazy loading is done
253
+ (_a = this._lazyLoadedSessionRecording) === null || _a === void 0 ? void 0 : _a.overrideTrigger(triggerType);
254
+ };
255
+ Object.defineProperty(SessionRecordingWrapper.prototype, "sdkDebugProperties", {
256
+ /*
257
+ * whenever we capture an event, we add these properties to the event
258
+ * these are used to debug issues with the session recording
259
+ * when looking at the event feed for a session
260
+ */
261
+ get: function () {
262
+ var _a;
263
+ return (((_a = this._lazyLoadedSessionRecording) === null || _a === void 0 ? void 0 : _a.sdkDebugProperties) || {
264
+ $recording_status: this.status,
265
+ });
266
+ },
267
+ enumerable: false,
268
+ configurable: true
269
+ });
270
+ /**
271
+ * This adds a custom event to the session recording
272
+ *
273
+ * It is not intended for arbitrary public use - playback only displays known custom events
274
+ * And is exposed on the public interface only so that other parts of the SDK are able to use it
275
+ *
276
+ * if you are calling this from client code, you're probably looking for `posthog.capture('$custom_event', {...})`
277
+ */
278
+ SessionRecordingWrapper.prototype.tryAddCustomEvent = function (tag, payload) {
279
+ var _a;
280
+ return !!((_a = this._lazyLoadedSessionRecording) === null || _a === void 0 ? void 0 : _a.tryAddCustomEvent(tag, payload));
281
+ };
282
+ return SessionRecordingWrapper;
283
+ }());
284
+ exports.SessionRecordingWrapper = SessionRecordingWrapper;
285
+ //# sourceMappingURL=sessionrecording-wrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionrecording-wrapper.js","sourceRoot":"","sources":["../../../../src/extensions/replay/sessionrecording-wrapper.ts"],"names":[],"mappings":"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","sourcesContent":["import { SESSION_RECORDING_IS_SAMPLED, SESSION_RECORDING_REMOTE_CONFIG } from '../../constants'\nimport { PostHog } from '../../posthog-core'\nimport { Properties, RemoteConfig, SessionRecordingPersistedConfig, SessionStartReason } from '../../types'\nimport { type eventWithTime } from '@rrweb/types'\n\nimport { isNullish, isUndefined } from '@posthog/core'\nimport { createLogger } from '../../utils/logger'\nimport {\n assignableWindow,\n LazyLoadedSessionRecordingInterface,\n PostHogExtensionKind,\n window,\n} from '../../utils/globals'\nimport { DISABLED, LAZY_LOADING, SessionRecordingStatus, TriggerType } from './triggerMatching'\n\nconst LOGGER_PREFIX = '[SessionRecording]'\nconst logger = createLogger(LOGGER_PREFIX)\n\n/**\n * This only exists to let us test changes to sessionrecording.ts before rolling them out to everyone\n * it should not be depended on in other ways, since i'm going to delete it long before the end of September 2025\n */\nexport class SessionRecordingWrapper {\n _forceAllowLocalhostNetworkCapture: boolean = false\n\n private _receivedFlags: boolean = false\n\n private _persistFlagsOnSessionListener: (() => void) | undefined = undefined\n private _lazyLoadedSessionRecording: LazyLoadedSessionRecordingInterface | undefined\n\n public get started(): boolean {\n return !!this._lazyLoadedSessionRecording?.isStarted\n }\n\n /**\n * defaults to buffering mode until a flags response is received\n * once a flags response is received status can be disabled, active or sampled\n */\n get status(): SessionRecordingStatus {\n if (this._lazyLoadedSessionRecording) {\n return this._lazyLoadedSessionRecording.status\n }\n\n if (this._receivedFlags && !this._isRecordingEnabled) {\n return DISABLED\n }\n\n return LAZY_LOADING\n }\n\n constructor(private readonly _instance: PostHog) {\n if (!this._instance.sessionManager) {\n logger.error('started without valid sessionManager')\n throw new Error(LOGGER_PREFIX + ' started without valid sessionManager. This is a bug.')\n }\n\n if (this._instance.config.cookieless_mode === 'always') {\n throw new Error(LOGGER_PREFIX + ' cannot be used with cookieless_mode=\"always\"')\n }\n }\n\n private get _isRecordingEnabled() {\n const enabled_server_side = !!this._instance.get_property(SESSION_RECORDING_REMOTE_CONFIG)?.enabled\n const enabled_client_side = !this._instance.config.disable_session_recording\n const isDisabled = this._instance.config.disable_session_recording || this._instance.consent.isOptedOut()\n return window && enabled_server_side && enabled_client_side && !isDisabled\n }\n\n startIfEnabledOrStop(startReason?: SessionStartReason) {\n if (this._isRecordingEnabled && this._lazyLoadedSessionRecording?.isStarted) {\n return\n }\n\n // According to the rrweb docs, rrweb is not supported on IE11 and below:\n // \"rrweb does not support IE11 and below because it uses the MutationObserver API, which was supported by these browsers.\"\n // https://github.com/rrweb-io/rrweb/blob/master/guide.md#compatibility-note\n //\n // However, MutationObserver does exist on IE11, it just doesn't work well and does not detect all changes.\n // Instead, when we load \"recorder.js\", the first JS error is about \"Object.assign\" and \"Array.from\" being undefined.\n // Thus instead of MutationObserver, we look for this function and block recording if it's undefined.\n const canRunReplay = !isUndefined(Object.assign) && !isUndefined(Array.from)\n if (this._isRecordingEnabled && canRunReplay) {\n this._lazyLoadAndStart(startReason)\n logger.info('starting')\n } else {\n this.stopRecording()\n }\n }\n\n /**\n * session recording waits until it receives remote config before loading the script\n * this is to ensure we can control the script name remotely\n * and because we wait until we have local and remote config to determine if we should start at all\n * if start is called and there is no remote config then we wait until there is\n */\n private _lazyLoadAndStart(startReason?: SessionStartReason) {\n // by checking `_isRecordingEnabled` here we know that\n // we have stored remote config and client config to read\n // replay waits for both local and remote config before starting\n if (!this._isRecordingEnabled) {\n return\n }\n\n // If recorder.js is already loaded (if array.full.js snippet is used or posthog-js/dist/recorder is\n // imported), don't load the script. Otherwise, remotely import recorder.js from cdn since it hasn't been loaded.\n if (\n !assignableWindow?.__PosthogExtensions__?.rrweb?.record ||\n !assignableWindow.__PosthogExtensions__?.initSessionRecording\n ) {\n assignableWindow.__PosthogExtensions__?.loadExternalDependency?.(\n this._instance,\n this._scriptName,\n (err) => {\n if (err) {\n return logger.error('could not load recorder', err)\n }\n this._onScriptLoaded(startReason)\n }\n )\n } else {\n this._onScriptLoaded(startReason)\n }\n }\n\n stopRecording() {\n this._lazyLoadedSessionRecording?.stop()\n }\n\n private _resetSampling() {\n this._instance.persistence?.unregister(SESSION_RECORDING_IS_SAMPLED)\n }\n\n private _persistRemoteConfig(response: RemoteConfig): void {\n if (this._instance.persistence) {\n const persistence = this._instance.persistence\n\n const persistResponse = () => {\n const sessionRecordingConfigResponse =\n response.sessionRecording === false ? undefined : response.sessionRecording\n\n const receivedSampleRate = sessionRecordingConfigResponse?.sampleRate\n\n const parsedSampleRate = isNullish(receivedSampleRate) ? null : parseFloat(receivedSampleRate)\n if (isNullish(parsedSampleRate)) {\n this._resetSampling()\n }\n\n const receivedMinimumDuration = sessionRecordingConfigResponse?.minimumDurationMilliseconds\n\n persistence.register({\n [SESSION_RECORDING_REMOTE_CONFIG]: {\n enabled: !!sessionRecordingConfigResponse,\n ...sessionRecordingConfigResponse,\n networkPayloadCapture: {\n capturePerformance: response.capturePerformance,\n ...sessionRecordingConfigResponse?.networkPayloadCapture,\n },\n canvasRecording: {\n enabled: sessionRecordingConfigResponse?.recordCanvas,\n fps: sessionRecordingConfigResponse?.canvasFps,\n quality: sessionRecordingConfigResponse?.canvasQuality,\n },\n sampleRate: parsedSampleRate,\n minimumDurationMilliseconds: isUndefined(receivedMinimumDuration)\n ? null\n : receivedMinimumDuration,\n endpoint: sessionRecordingConfigResponse?.endpoint,\n triggerMatchType: sessionRecordingConfigResponse?.triggerMatchType,\n masking: sessionRecordingConfigResponse?.masking,\n urlTriggers: sessionRecordingConfigResponse?.urlTriggers,\n } satisfies SessionRecordingPersistedConfig,\n })\n }\n\n persistResponse()\n\n // in case we see multiple flags responses, we should only use the response from the most recent one\n this._persistFlagsOnSessionListener?.()\n // we 100% know there is a session manager by this point\n this._persistFlagsOnSessionListener = this._instance.sessionManager?.onSessionId(persistResponse)\n }\n }\n\n onRemoteConfig(response: RemoteConfig) {\n if (!('sessionRecording' in response)) {\n // if sessionRecording is not in the response, we do nothing\n logger.info('skipping remote config with no sessionRecording', response)\n return\n }\n if (response.sessionRecording === false) {\n // remotely disabled\n this._receivedFlags = true\n return\n }\n\n this._persistRemoteConfig(response)\n this._receivedFlags = true\n // TODO how do we send a custom message with the received remote config like we used to for debug\n this.startIfEnabledOrStop()\n }\n\n log(message: string, level: 'log' | 'warn' | 'error' = 'log') {\n if (this._lazyLoadedSessionRecording?.log) {\n this._lazyLoadedSessionRecording.log(message, level)\n } else {\n logger.warn('log called before recorder was ready')\n }\n }\n\n private get _scriptName(): PostHogExtensionKind {\n const remoteConfig: SessionRecordingPersistedConfig | undefined = this._instance?.persistence?.get_property(\n SESSION_RECORDING_REMOTE_CONFIG\n )\n return (remoteConfig?.scriptConfig?.script as PostHogExtensionKind) || 'lazy-recorder'\n }\n\n private _onScriptLoaded(startReason?: SessionStartReason) {\n if (!assignableWindow.__PosthogExtensions__?.initSessionRecording) {\n throw Error('Called on script loaded before session recording is available')\n }\n\n if (!this._lazyLoadedSessionRecording) {\n this._lazyLoadedSessionRecording = assignableWindow.__PosthogExtensions__?.initSessionRecording(\n this._instance\n )\n ;(this._lazyLoadedSessionRecording as any)._forceAllowLocalhostNetworkCapture =\n this._forceAllowLocalhostNetworkCapture\n }\n\n this._lazyLoadedSessionRecording.start(startReason)\n }\n\n /**\n * this is maintained on the public API only because it has always been on the public API\n * if you are calling this directly you are certainly doing something wrong\n * @deprecated\n */\n onRRwebEmit(rawEvent: eventWithTime) {\n this._lazyLoadedSessionRecording?.onRRwebEmit?.(rawEvent)\n }\n\n /**\n * this ignores the linked flag config and (if other conditions are met) causes capture to start\n *\n * It is not usual to call this directly,\n * instead call `posthog.startSessionRecording({linked_flag: true})`\n * */\n public overrideLinkedFlag() {\n // TODO what if this gets called before lazy loading is done\n this._lazyLoadedSessionRecording?.overrideLinkedFlag()\n }\n\n /**\n * this ignores the sampling config and (if other conditions are met) causes capture to start\n *\n * It is not usual to call this directly,\n * instead call `posthog.startSessionRecording({sampling: true})`\n * */\n public overrideSampling() {\n // TODO what if this gets called before lazy loading is done\n this._lazyLoadedSessionRecording?.overrideSampling()\n }\n\n /**\n * this ignores the URL/Event trigger config and (if other conditions are met) causes capture to start\n *\n * It is not usual to call this directly,\n * instead call `posthog.startSessionRecording({trigger: 'url' | 'event'})`\n * */\n public overrideTrigger(triggerType: TriggerType) {\n // TODO what if this gets called before lazy loading is done\n this._lazyLoadedSessionRecording?.overrideTrigger(triggerType)\n }\n\n /*\n * whenever we capture an event, we add these properties to the event\n * these are used to debug issues with the session recording\n * when looking at the event feed for a session\n */\n get sdkDebugProperties(): Properties {\n return (\n this._lazyLoadedSessionRecording?.sdkDebugProperties || {\n $recording_status: this.status,\n }\n )\n }\n\n /**\n * This adds a custom event to the session recording\n *\n * It is not intended for arbitrary public use - playback only displays known custom events\n * And is exposed on the public interface only so that other parts of the SDK are able to use it\n *\n * if you are calling this from client code, you're probably looking for `posthog.capture('$custom_event', {...})`\n */\n tryAddCustomEvent(tag: string, payload: any): boolean {\n return !!this._lazyLoadedSessionRecording?.tryAddCustomEvent(tag, payload)\n }\n}\n"]}
@@ -0,0 +1,163 @@
1
+ import { PostHog } from '../../posthog-core';
2
+ import { Properties, RemoteConfig, SessionStartReason } from '../../types';
3
+ import { EventType, type eventWithTime, IncrementalSource } from '@rrweb/types';
4
+ import { SessionRecordingStatus, TriggerType } from './triggerMatching';
5
+ export declare const RECORDING_IDLE_THRESHOLD_MS: number;
6
+ export declare const RECORDING_MAX_EVENT_SIZE: number;
7
+ export declare const RECORDING_BUFFER_TIMEOUT = 2000;
8
+ export declare const SESSION_RECORDING_BATCH_KEY = "recordings";
9
+ export interface SnapshotBuffer {
10
+ size: number;
11
+ data: any[];
12
+ sessionId: string;
13
+ windowId: string;
14
+ }
15
+ export type compressedFullSnapshotEvent = {
16
+ type: EventType.FullSnapshot;
17
+ data: string;
18
+ };
19
+ export type compressedIncrementalSnapshotEvent = {
20
+ type: EventType.IncrementalSnapshot;
21
+ data: {
22
+ source: IncrementalSource;
23
+ texts: string;
24
+ attributes: string;
25
+ removes: string;
26
+ adds: string;
27
+ };
28
+ };
29
+ export type compressedIncrementalStyleSnapshotEvent = {
30
+ type: EventType.IncrementalSnapshot;
31
+ data: {
32
+ source: IncrementalSource.StyleSheetRule;
33
+ id?: number;
34
+ styleId?: number;
35
+ replace?: string;
36
+ replaceSync?: string;
37
+ adds?: string;
38
+ removes?: string;
39
+ };
40
+ };
41
+ export type compressedEvent = compressedIncrementalStyleSnapshotEvent | compressedFullSnapshotEvent | compressedIncrementalSnapshotEvent;
42
+ export type compressedEventWithTime = compressedEvent & {
43
+ timestamp: number;
44
+ delay?: number;
45
+ cv: '2024-10';
46
+ };
47
+ export declare class SessionRecording {
48
+ private readonly _instance;
49
+ private _endpoint;
50
+ private _flushBufferTimer?;
51
+ private _statusMatcher;
52
+ private _receivedFlags;
53
+ private _buffer;
54
+ private _queuedRRWebEvents;
55
+ private _mutationThrottler?;
56
+ private _captureStarted;
57
+ private _stopRrweb;
58
+ private _isIdle;
59
+ private _lastActivityTimestamp;
60
+ private _windowId;
61
+ private _sessionId;
62
+ get sessionId(): string;
63
+ private _linkedFlagMatching;
64
+ private _urlTriggerMatching;
65
+ private _eventTriggerMatching;
66
+ private _triggerMatching;
67
+ private _fullSnapshotTimer?;
68
+ private _removePageViewCaptureHook;
69
+ private _onSessionIdListener;
70
+ private _persistFlagsOnSessionListener;
71
+ private _samplingSessionListener;
72
+ private _lastHref?;
73
+ private _removeEventTriggerCaptureHook;
74
+ _forceAllowLocalhostNetworkCapture: boolean;
75
+ private get _sessionIdleThresholdMilliseconds();
76
+ get started(): boolean;
77
+ private get _sessionManager();
78
+ private get _fullSnapshotIntervalMillis();
79
+ private get _isSampled();
80
+ private get _sessionDuration();
81
+ private get _isRecordingEnabled();
82
+ private get _isConsoleLogCaptureEnabled();
83
+ private get _canvasRecording();
84
+ private get _networkPayloadCapture();
85
+ private get _masking();
86
+ private get _sampleRate();
87
+ private get _minimumDuration();
88
+ /**
89
+ * defaults to buffering mode until a flags response is received
90
+ * once a flags response is received status can be disabled, active or sampled
91
+ */
92
+ get status(): SessionRecordingStatus;
93
+ constructor(_instance: PostHog);
94
+ private _onBeforeUnload;
95
+ private _onOffline;
96
+ private _onOnline;
97
+ private _onVisibilityChange;
98
+ startIfEnabledOrStop(startReason?: SessionStartReason): void;
99
+ stopRecording(): void;
100
+ private _resetSampling;
101
+ private _makeSamplingDecision;
102
+ onRemoteConfig(response: RemoteConfig): void;
103
+ /**
104
+ * This might be called more than once so needs to be idempotent
105
+ */
106
+ private _setupSampling;
107
+ private _persistRemoteConfig;
108
+ log(message: string, level?: 'log' | 'warn' | 'error'): void;
109
+ private _startCapture;
110
+ private get _scriptName();
111
+ private _isInteractiveEvent;
112
+ private _updateWindowAndSessionIds;
113
+ private _tryRRWebMethod;
114
+ /**
115
+ * This adds a custom event to the session recording
116
+ *
117
+ * It is not intended for arbitrary public use - playback only displays known custom events
118
+ * And is exposed on the public interface only so that other parts of the SDK are able to use it
119
+ *
120
+ * if you are calling this from client code, you're probably looking for `posthog.capture('$custom_event', {...})`
121
+ */
122
+ tryAddCustomEvent(tag: string, payload: any): boolean;
123
+ private _tryTakeFullSnapshot;
124
+ private _onScriptLoaded;
125
+ private _scheduleFullSnapshot;
126
+ private _gatherRRWebPlugins;
127
+ onRRwebEmit(rawEvent: eventWithTime): void;
128
+ private _pageViewFallBack;
129
+ private _processQueuedEvents;
130
+ private _maskUrl;
131
+ private _clearBufferBeforeMostRecentMeta;
132
+ private _clearBuffer;
133
+ private _flushBuffer;
134
+ private _captureSnapshotBuffered;
135
+ private _captureSnapshot;
136
+ private _activateTrigger;
137
+ private _pauseRecording;
138
+ private _resumeRecording;
139
+ private _addEventTriggerListener;
140
+ /**
141
+ * this ignores the linked flag config and (if other conditions are met) causes capture to start
142
+ *
143
+ * It is not usual to call this directly,
144
+ * instead call `posthog.startSessionRecording({linked_flag: true})`
145
+ * */
146
+ overrideLinkedFlag(): void;
147
+ /**
148
+ * this ignores the sampling config and (if other conditions are met) causes capture to start
149
+ *
150
+ * It is not usual to call this directly,
151
+ * instead call `posthog.startSessionRecording({sampling: true})`
152
+ * */
153
+ overrideSampling(): void;
154
+ /**
155
+ * this ignores the URL/Event trigger config and (if other conditions are met) causes capture to start
156
+ *
157
+ * It is not usual to call this directly,
158
+ * instead call `posthog.startSessionRecording({trigger: 'url' | 'event'})`
159
+ * */
160
+ overrideTrigger(triggerType: TriggerType): void;
161
+ private _reportStarted;
162
+ get sdkDebugProperties(): Properties;
163
+ }