@spotify-confidence/session-recording 0.18.10 → 0.18.12

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/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.18.12](https://github.com/spotify/confidence-sdk-js/compare/session-recording-v0.18.11...session-recording-v0.18.12) (2026-09-07)
4
+
5
+
6
+ ### 🐛 Bug Fixes
7
+
8
+ * stop passive recording frames extending sessions ([#449](https://github.com/spotify/confidence-sdk-js/issues/449)) ([df763a1](https://github.com/spotify/confidence-sdk-js/commit/df763a10d6f16ce5b871530ca3fe8b5c153c16d7))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @spotify-confidence/csr-common bumped to 0.18.9
16
+ * @spotify-confidence/csr-recorder bumped to 0.17.17
17
+
18
+ ## [0.18.11](https://github.com/spotify/confidence-sdk-js/compare/session-recording-v0.18.10...session-recording-v0.18.11) (2026-08-28)
19
+
20
+
21
+ ### Dependencies
22
+
23
+ * The following workspace dependencies were updated
24
+ * dependencies
25
+ * @spotify-confidence/csr-common bumped to 0.18.8
26
+ * @spotify-confidence/csr-recorder bumped to 0.17.16
27
+
3
28
  ## [0.18.10](https://github.com/spotify/confidence-sdk-js/compare/session-recording-v0.18.9...session-recording-v0.18.10) (2026-08-20)
4
29
 
5
30
 
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_uploader = require("./uploader-CcDy4aME.cjs");
2
+ const require_uploader = require("./uploader-B6-mcWdA.cjs");
3
3
  const VALID_KEY_PATTERN = /^[a-zA-Z0-9_.-]+$/;
4
4
  function validateKey(key) {
5
5
  if (typeof key !== "string" || key.length === 0) return "key is empty";
@@ -21,7 +21,7 @@ function validateMeasureValue(value) {
21
21
  //#endregion
22
22
  //#region ../csr-recorder/src/types.ts
23
23
  const DEFAULT_MASK_SELECTORS = ["[data-csr-mask]"];
24
- const DEFAULT_BLOCK_SELECTORS = ["[data-csr-block]"];
24
+ const DEFAULT_BLOCK_SELECTORS = ["[data-csr-block]", "video"];
25
25
  //#endregion
26
26
  //#region ../csr-recorder/src/route-parameterizer.ts
27
27
  const SEGMENT_PATTERNS = [
@@ -118,7 +118,7 @@ var Recorder = class Recorder {
118
118
  if (typeof document !== "undefined") {
119
119
  this.visibilityHandler = () => {
120
120
  const data = {
121
- plugin: "csr:tabVisibility",
121
+ plugin: require_uploader.RecordingPluginName.TabVisibility,
122
122
  payload: { hidden: document.hidden }
123
123
  };
124
124
  this.onEvent({
@@ -140,7 +140,7 @@ var Recorder = class Recorder {
140
140
  }
141
141
  emitNetworkRequest(payload) {
142
142
  const data = {
143
- plugin: "csr:networkRequest",
143
+ plugin: require_uploader.RecordingPluginName.NetworkRequest,
144
144
  payload
145
145
  };
146
146
  this.onEvent({
@@ -248,7 +248,7 @@ var Recorder = class Recorder {
248
248
  const paramTo = this.parameterizeRoute(to);
249
249
  if (paramFrom === paramTo) return;
250
250
  const data = {
251
- plugin: "csr:routeChange",
251
+ plugin: require_uploader.RecordingPluginName.RouteChange,
252
252
  payload: {
253
253
  from: paramFrom,
254
254
  to: paramTo,
@@ -11138,6 +11138,63 @@ const ALL_CONSOLE_LEVELS = [
11138
11138
  "debug",
11139
11139
  "info"
11140
11140
  ];
11141
+ const BLOCKED_ELEMENT_ATTRIBUTE = "data-csr-blocked-element";
11142
+ function labelBlockedElement(node) {
11143
+ if (node.type === 2 && node.tagName !== void 0 && node.attributes !== void 0 && typeof node.attributes.rr_width === "string" && typeof node.attributes.rr_height === "string") {
11144
+ node.attributes[BLOCKED_ELEMENT_ATTRIBUTE] = node.tagName;
11145
+ node.tagName = "div";
11146
+ }
11147
+ node.childNodes?.forEach(labelBlockedElement);
11148
+ }
11149
+ /**
11150
+ * rrweb strips blocked elements down to their dimensions, but retains their
11151
+ * original tag name. Rebuild them as inert divs and keep the tag name as safe
11152
+ * metadata so players can render a useful placeholder label.
11153
+ */
11154
+ function blockedElementLabelsPlugin() {
11155
+ return {
11156
+ name: require_uploader.RecordingPluginName.BlockedElementLabels,
11157
+ options: {},
11158
+ observer: () => () => {},
11159
+ eventProcessor: (event) => {
11160
+ if (event.type === EventType.FullSnapshot) labelBlockedElement(event.data.node);
11161
+ else if (event.type === EventType.IncrementalSnapshot && event.data.source === IncrementalSource.Mutation) event.data.adds.forEach((add) => labelBlockedElement(add.node));
11162
+ return event;
11163
+ }
11164
+ };
11165
+ }
11166
+ /**
11167
+ * Record clipboard actions and their DOM target without reading clipboard
11168
+ * contents. The resulting rrweb Plugin events can explain otherwise
11169
+ * surprising input changes during analysis.
11170
+ */
11171
+ function clipboardActionsPlugin() {
11172
+ let getId;
11173
+ return {
11174
+ name: require_uploader.RecordingPluginName.Clipboard,
11175
+ options: {},
11176
+ getMirror: ({ nodeMirror }) => {
11177
+ getId = (node) => nodeMirror.getId(node);
11178
+ },
11179
+ observer: (callback, win) => {
11180
+ const handlers = [
11181
+ "copy",
11182
+ "cut",
11183
+ "paste"
11184
+ ].map((action) => {
11185
+ const handler = (event) => {
11186
+ callback({
11187
+ action,
11188
+ targetId: event.target instanceof win.Node ? getId?.(event.target) ?? -1 : -1
11189
+ });
11190
+ };
11191
+ win.document.addEventListener(action, handler, true);
11192
+ return () => win.document.removeEventListener(action, handler, true);
11193
+ });
11194
+ return () => handlers.forEach((remove) => remove());
11195
+ }
11196
+ };
11197
+ }
11141
11198
  /**
11142
11199
  * rrweb does not include modifier keys in mouse-interaction events. Capture
11143
11200
  * the native click first, then add its safe, non-text metadata to the rrweb
@@ -11146,7 +11203,7 @@ const ALL_CONSOLE_LEVELS = [
11146
11203
  function clickModifiersPlugin() {
11147
11204
  let pendingClick = null;
11148
11205
  return {
11149
- name: "csr/click-modifiers@1",
11206
+ name: require_uploader.RecordingPluginName.ClickModifiers,
11150
11207
  options: {},
11151
11208
  observer: (_callback, win) => {
11152
11209
  const onClick = (event) => {
@@ -11190,7 +11247,11 @@ var RrwebEngine = class {
11190
11247
  start(config, onEvent) {
11191
11248
  const maskSelectors = config.maskSelectors ?? DEFAULT_MASK_SELECTORS;
11192
11249
  const blockSelectors = config.blockSelectors ?? DEFAULT_BLOCK_SELECTORS;
11193
- const plugins = [clickModifiersPlugin()];
11250
+ const plugins = [
11251
+ clickModifiersPlugin(),
11252
+ clipboardActionsPlugin(),
11253
+ blockedElementLabelsPlugin()
11254
+ ];
11194
11255
  const { captureConsoleLogs } = config;
11195
11256
  if (captureConsoleLogs) {
11196
11257
  const levels = captureConsoleLogs === true ? ALL_CONSOLE_LEVELS : captureConsoleLogs.levels;
@@ -11265,7 +11326,7 @@ function observeFlags(onFlagWrite) {
11265
11326
  }
11266
11327
  //#endregion
11267
11328
  //#region src/version.ts
11268
- const SDK_VERSION = "0.18.10";
11329
+ const SDK_VERSION = "0.18.12";
11269
11330
  //#endregion
11270
11331
  //#region src/index.ts
11271
11332
  const DEFAULT_API_URL = "https://recording.confidence.dev";
@@ -11343,7 +11404,7 @@ function initSessionRecorder(options) {
11343
11404
  stopRecorder = record(sendEvent, recordingConfig);
11344
11405
  stopObservingFlags = observeFlags(({ flagKey, variant, assignmentOrigin }) => {
11345
11406
  const data = {
11346
- plugin: "csr:flagEvaluation",
11407
+ plugin: require_uploader.RecordingPluginName.FlagEvaluation,
11347
11408
  payload: {
11348
11409
  flagKey,
11349
11410
  variant,
@@ -11393,7 +11454,7 @@ function initSessionRecorder(options) {
11393
11454
  return;
11394
11455
  }
11395
11456
  const data = {
11396
- plugin: "csr:tag",
11457
+ plugin: require_uploader.RecordingPluginName.Tag,
11397
11458
  payload: value !== void 0 ? {
11398
11459
  key,
11399
11460
  value
@@ -11417,7 +11478,7 @@ function initSessionRecorder(options) {
11417
11478
  return;
11418
11479
  }
11419
11480
  const data = {
11420
- plugin: "csr:measure",
11481
+ plugin: require_uploader.RecordingPluginName.Measure,
11421
11482
  payload: value !== void 0 ? {
11422
11483
  key,
11423
11484
  value
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as createUploader } from "./uploader-CkAqZXJP.js";
1
+ import { r as RecordingPluginName, t as createUploader } from "./uploader-vqAzevKI.js";
2
2
  const VALID_KEY_PATTERN = /^[a-zA-Z0-9_.-]+$/;
3
3
  function validateKey(key) {
4
4
  if (typeof key !== "string" || key.length === 0) return "key is empty";
@@ -20,7 +20,7 @@ function validateMeasureValue(value) {
20
20
  //#endregion
21
21
  //#region ../csr-recorder/src/types.ts
22
22
  const DEFAULT_MASK_SELECTORS = ["[data-csr-mask]"];
23
- const DEFAULT_BLOCK_SELECTORS = ["[data-csr-block]"];
23
+ const DEFAULT_BLOCK_SELECTORS = ["[data-csr-block]", "video"];
24
24
  //#endregion
25
25
  //#region ../csr-recorder/src/route-parameterizer.ts
26
26
  const SEGMENT_PATTERNS = [
@@ -117,7 +117,7 @@ var Recorder = class Recorder {
117
117
  if (typeof document !== "undefined") {
118
118
  this.visibilityHandler = () => {
119
119
  const data = {
120
- plugin: "csr:tabVisibility",
120
+ plugin: RecordingPluginName.TabVisibility,
121
121
  payload: { hidden: document.hidden }
122
122
  };
123
123
  this.onEvent({
@@ -139,7 +139,7 @@ var Recorder = class Recorder {
139
139
  }
140
140
  emitNetworkRequest(payload) {
141
141
  const data = {
142
- plugin: "csr:networkRequest",
142
+ plugin: RecordingPluginName.NetworkRequest,
143
143
  payload
144
144
  };
145
145
  this.onEvent({
@@ -247,7 +247,7 @@ var Recorder = class Recorder {
247
247
  const paramTo = this.parameterizeRoute(to);
248
248
  if (paramFrom === paramTo) return;
249
249
  const data = {
250
- plugin: "csr:routeChange",
250
+ plugin: RecordingPluginName.RouteChange,
251
251
  payload: {
252
252
  from: paramFrom,
253
253
  to: paramTo,
@@ -11119,6 +11119,63 @@ const ALL_CONSOLE_LEVELS = [
11119
11119
  "debug",
11120
11120
  "info"
11121
11121
  ];
11122
+ const BLOCKED_ELEMENT_ATTRIBUTE = "data-csr-blocked-element";
11123
+ function labelBlockedElement(node) {
11124
+ if (node.type === 2 && node.tagName !== void 0 && node.attributes !== void 0 && typeof node.attributes.rr_width === "string" && typeof node.attributes.rr_height === "string") {
11125
+ node.attributes[BLOCKED_ELEMENT_ATTRIBUTE] = node.tagName;
11126
+ node.tagName = "div";
11127
+ }
11128
+ node.childNodes?.forEach(labelBlockedElement);
11129
+ }
11130
+ /**
11131
+ * rrweb strips blocked elements down to their dimensions, but retains their
11132
+ * original tag name. Rebuild them as inert divs and keep the tag name as safe
11133
+ * metadata so players can render a useful placeholder label.
11134
+ */
11135
+ function blockedElementLabelsPlugin() {
11136
+ return {
11137
+ name: RecordingPluginName.BlockedElementLabels,
11138
+ options: {},
11139
+ observer: () => () => {},
11140
+ eventProcessor: (event) => {
11141
+ if (event.type === EventType.FullSnapshot) labelBlockedElement(event.data.node);
11142
+ else if (event.type === EventType.IncrementalSnapshot && event.data.source === IncrementalSource.Mutation) event.data.adds.forEach((add) => labelBlockedElement(add.node));
11143
+ return event;
11144
+ }
11145
+ };
11146
+ }
11147
+ /**
11148
+ * Record clipboard actions and their DOM target without reading clipboard
11149
+ * contents. The resulting rrweb Plugin events can explain otherwise
11150
+ * surprising input changes during analysis.
11151
+ */
11152
+ function clipboardActionsPlugin() {
11153
+ let getId;
11154
+ return {
11155
+ name: RecordingPluginName.Clipboard,
11156
+ options: {},
11157
+ getMirror: ({ nodeMirror }) => {
11158
+ getId = (node) => nodeMirror.getId(node);
11159
+ },
11160
+ observer: (callback, win) => {
11161
+ const handlers = [
11162
+ "copy",
11163
+ "cut",
11164
+ "paste"
11165
+ ].map((action) => {
11166
+ const handler = (event) => {
11167
+ callback({
11168
+ action,
11169
+ targetId: event.target instanceof win.Node ? getId?.(event.target) ?? -1 : -1
11170
+ });
11171
+ };
11172
+ win.document.addEventListener(action, handler, true);
11173
+ return () => win.document.removeEventListener(action, handler, true);
11174
+ });
11175
+ return () => handlers.forEach((remove) => remove());
11176
+ }
11177
+ };
11178
+ }
11122
11179
  /**
11123
11180
  * rrweb does not include modifier keys in mouse-interaction events. Capture
11124
11181
  * the native click first, then add its safe, non-text metadata to the rrweb
@@ -11127,7 +11184,7 @@ const ALL_CONSOLE_LEVELS = [
11127
11184
  function clickModifiersPlugin() {
11128
11185
  let pendingClick = null;
11129
11186
  return {
11130
- name: "csr/click-modifiers@1",
11187
+ name: RecordingPluginName.ClickModifiers,
11131
11188
  options: {},
11132
11189
  observer: (_callback, win) => {
11133
11190
  const onClick = (event) => {
@@ -11171,7 +11228,11 @@ var RrwebEngine = class {
11171
11228
  start(config, onEvent) {
11172
11229
  const maskSelectors = config.maskSelectors ?? DEFAULT_MASK_SELECTORS;
11173
11230
  const blockSelectors = config.blockSelectors ?? DEFAULT_BLOCK_SELECTORS;
11174
- const plugins = [clickModifiersPlugin()];
11231
+ const plugins = [
11232
+ clickModifiersPlugin(),
11233
+ clipboardActionsPlugin(),
11234
+ blockedElementLabelsPlugin()
11235
+ ];
11175
11236
  const { captureConsoleLogs } = config;
11176
11237
  if (captureConsoleLogs) {
11177
11238
  const levels = captureConsoleLogs === true ? ALL_CONSOLE_LEVELS : captureConsoleLogs.levels;
@@ -11246,7 +11307,7 @@ function observeFlags(onFlagWrite) {
11246
11307
  }
11247
11308
  //#endregion
11248
11309
  //#region src/version.ts
11249
- const SDK_VERSION = "0.18.10";
11310
+ const SDK_VERSION = "0.18.12";
11250
11311
  //#endregion
11251
11312
  //#region src/index.ts
11252
11313
  const DEFAULT_API_URL = "https://recording.confidence.dev";
@@ -11324,7 +11385,7 @@ function initSessionRecorder(options) {
11324
11385
  stopRecorder = record(sendEvent, recordingConfig);
11325
11386
  stopObservingFlags = observeFlags(({ flagKey, variant, assignmentOrigin }) => {
11326
11387
  const data = {
11327
- plugin: "csr:flagEvaluation",
11388
+ plugin: RecordingPluginName.FlagEvaluation,
11328
11389
  payload: {
11329
11390
  flagKey,
11330
11391
  variant,
@@ -11374,7 +11435,7 @@ function initSessionRecorder(options) {
11374
11435
  return;
11375
11436
  }
11376
11437
  const data = {
11377
- plugin: "csr:tag",
11438
+ plugin: RecordingPluginName.Tag,
11378
11439
  payload: value !== void 0 ? {
11379
11440
  key,
11380
11441
  value
@@ -11398,7 +11459,7 @@ function initSessionRecorder(options) {
11398
11459
  return;
11399
11460
  }
11400
11461
  const data = {
11401
- plugin: "csr:measure",
11462
+ plugin: RecordingPluginName.Measure,
11402
11463
  payload: value !== void 0 ? {
11403
11464
  key,
11404
11465
  value
@@ -21,6 +21,93 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  enumerable: true
22
22
  }) : target, mod));
23
23
  //#endregion
24
+ //#region ../csr-common/src/events.ts
25
+ const RecordingCustomEventTag = {
26
+ RageClick: "csr:rageClick",
27
+ FormFieldReEdit: "csr:formFieldReEdit",
28
+ ScrollBack: "csr:scrollBack",
29
+ Click: "csr:click",
30
+ Input: "csr:input",
31
+ DeadClick: "csr:deadClick",
32
+ TabUnfocus: "csr:tabUnfocus",
33
+ TabRefocus: "csr:tabRefocus",
34
+ RouteChange: "csr:routeChange",
35
+ ErrorMessage: "csr:errorMessage",
36
+ DialogOpened: "csr:dialogOpened",
37
+ IdleGap: "csr:idleGap",
38
+ AwayGap: "csr:awayGap"
39
+ };
40
+ const RecordingPluginName = {
41
+ BlockedElementLabels: "csr/blocked-element-labels@1",
42
+ Clipboard: "csr/clipboard@1",
43
+ ClickModifiers: "csr/click-modifiers@1",
44
+ TabVisibility: "csr:tabVisibility",
45
+ ConsoleLog: "rrweb/console@1",
46
+ NetworkRequest: "csr:networkRequest",
47
+ RouteChange: "csr:routeChange",
48
+ Tag: "csr:tag",
49
+ Measure: "csr:measure",
50
+ FlagEvaluation: "csr:flagEvaluation"
51
+ };
52
+ //#endregion
53
+ //#region ../csr-common/src/metrics.ts
54
+ const RecordingMetricKey = {
55
+ Click: "clicks",
56
+ Input: "inputs",
57
+ RageClick: "rageClicks",
58
+ DeadClick: "deadClicks",
59
+ ScrollBack: "scrollBacks",
60
+ TabUnfocus: "tabUnfocuses",
61
+ ErrorMessage: "errorMessages",
62
+ FailedNetworkRequest: "networkRequestsFailed",
63
+ NetworkRequest: "networkRequests",
64
+ ConsoleLog: "consoleLogs",
65
+ ConsoleWarning: "consoleWarnings",
66
+ ConsoleError: "consoleErrors",
67
+ RouteChange: "routeChanges"
68
+ };
69
+ //#endregion
70
+ //#region ../csr-common/src/session-activity.ts
71
+ const USER_INTERACTION_SOURCES = new Set([
72
+ 1,
73
+ 2,
74
+ 3,
75
+ 5,
76
+ 6,
77
+ 7,
78
+ 12,
79
+ 14
80
+ ]);
81
+ const USER_INTERACTION_TAGS = new Set([
82
+ RecordingCustomEventTag.Click,
83
+ RecordingCustomEventTag.Input,
84
+ RecordingCustomEventTag.RageClick,
85
+ RecordingCustomEventTag.DeadClick,
86
+ RecordingCustomEventTag.ScrollBack,
87
+ RecordingCustomEventTag.TabUnfocus,
88
+ RecordingCustomEventTag.TabRefocus,
89
+ RecordingCustomEventTag.FormFieldReEdit,
90
+ RecordingCustomEventTag.RouteChange
91
+ ]);
92
+ const USER_INTERACTION_PLUGINS = new Set([RecordingPluginName.RouteChange]);
93
+ new Set([
94
+ RecordingMetricKey.Click,
95
+ RecordingMetricKey.Input,
96
+ RecordingMetricKey.RageClick,
97
+ RecordingMetricKey.DeadClick,
98
+ RecordingMetricKey.ScrollBack,
99
+ RecordingMetricKey.TabUnfocus,
100
+ RecordingMetricKey.RouteChange
101
+ ]);
102
+ const isObject = (value) => typeof value === "object" && value !== null;
103
+ const isUserInteractionEvent = (event) => {
104
+ if (!isObject(event) || !isObject(event.data)) return false;
105
+ if (event.type === 3) return typeof event.data.source === "number" && USER_INTERACTION_SOURCES.has(event.data.source);
106
+ if (event.type === 5) return typeof event.data.tag === "string" && USER_INTERACTION_TAGS.has(event.data.tag);
107
+ return event.type === 6 && typeof event.data.plugin === "string" && USER_INTERACTION_PLUGINS.has(event.data.plugin);
108
+ };
109
+ const isSessionActivityEvent = (event) => isObject(event) && event.type === 4 || isUserInteractionEvent(event);
110
+ //#endregion
24
111
  //#region ../csr-common/src/uploader/client-context.ts
25
112
  var import_es5 = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
26
113
  (function(e, t) {
@@ -1897,6 +1984,7 @@ async function createUploader(opts) {
1897
1984
  tabId: effectiveTabId,
1898
1985
  eventCounter: counter,
1899
1986
  data: event,
1987
+ userActivity: isSessionActivityEvent(event),
1900
1988
  ...nextAdoptionMeta ?? {}
1901
1989
  };
1902
1990
  counter += 1;
@@ -2060,4 +2148,21 @@ function writeCounter(counter) {
2060
2148
  sessionStorage.setItem(STORAGE_COUNTER, String(counter));
2061
2149
  }
2062
2150
  //#endregion
2063
- export { workerScript as n, createUploader as t };
2151
+ Object.defineProperty(exports, "RecordingPluginName", {
2152
+ enumerable: true,
2153
+ get: function() {
2154
+ return RecordingPluginName;
2155
+ }
2156
+ });
2157
+ Object.defineProperty(exports, "createUploader", {
2158
+ enumerable: true,
2159
+ get: function() {
2160
+ return createUploader;
2161
+ }
2162
+ });
2163
+ Object.defineProperty(exports, "workerScript", {
2164
+ enumerable: true,
2165
+ get: function() {
2166
+ return workerScript;
2167
+ }
2168
+ });
@@ -21,6 +21,93 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  enumerable: true
22
22
  }) : target, mod));
23
23
  //#endregion
24
+ //#region ../csr-common/src/events.ts
25
+ const RecordingCustomEventTag = {
26
+ RageClick: "csr:rageClick",
27
+ FormFieldReEdit: "csr:formFieldReEdit",
28
+ ScrollBack: "csr:scrollBack",
29
+ Click: "csr:click",
30
+ Input: "csr:input",
31
+ DeadClick: "csr:deadClick",
32
+ TabUnfocus: "csr:tabUnfocus",
33
+ TabRefocus: "csr:tabRefocus",
34
+ RouteChange: "csr:routeChange",
35
+ ErrorMessage: "csr:errorMessage",
36
+ DialogOpened: "csr:dialogOpened",
37
+ IdleGap: "csr:idleGap",
38
+ AwayGap: "csr:awayGap"
39
+ };
40
+ const RecordingPluginName = {
41
+ BlockedElementLabels: "csr/blocked-element-labels@1",
42
+ Clipboard: "csr/clipboard@1",
43
+ ClickModifiers: "csr/click-modifiers@1",
44
+ TabVisibility: "csr:tabVisibility",
45
+ ConsoleLog: "rrweb/console@1",
46
+ NetworkRequest: "csr:networkRequest",
47
+ RouteChange: "csr:routeChange",
48
+ Tag: "csr:tag",
49
+ Measure: "csr:measure",
50
+ FlagEvaluation: "csr:flagEvaluation"
51
+ };
52
+ //#endregion
53
+ //#region ../csr-common/src/metrics.ts
54
+ const RecordingMetricKey = {
55
+ Click: "clicks",
56
+ Input: "inputs",
57
+ RageClick: "rageClicks",
58
+ DeadClick: "deadClicks",
59
+ ScrollBack: "scrollBacks",
60
+ TabUnfocus: "tabUnfocuses",
61
+ ErrorMessage: "errorMessages",
62
+ FailedNetworkRequest: "networkRequestsFailed",
63
+ NetworkRequest: "networkRequests",
64
+ ConsoleLog: "consoleLogs",
65
+ ConsoleWarning: "consoleWarnings",
66
+ ConsoleError: "consoleErrors",
67
+ RouteChange: "routeChanges"
68
+ };
69
+ //#endregion
70
+ //#region ../csr-common/src/session-activity.ts
71
+ const USER_INTERACTION_SOURCES = new Set([
72
+ 1,
73
+ 2,
74
+ 3,
75
+ 5,
76
+ 6,
77
+ 7,
78
+ 12,
79
+ 14
80
+ ]);
81
+ const USER_INTERACTION_TAGS = new Set([
82
+ RecordingCustomEventTag.Click,
83
+ RecordingCustomEventTag.Input,
84
+ RecordingCustomEventTag.RageClick,
85
+ RecordingCustomEventTag.DeadClick,
86
+ RecordingCustomEventTag.ScrollBack,
87
+ RecordingCustomEventTag.TabUnfocus,
88
+ RecordingCustomEventTag.TabRefocus,
89
+ RecordingCustomEventTag.FormFieldReEdit,
90
+ RecordingCustomEventTag.RouteChange
91
+ ]);
92
+ const USER_INTERACTION_PLUGINS = new Set([RecordingPluginName.RouteChange]);
93
+ new Set([
94
+ RecordingMetricKey.Click,
95
+ RecordingMetricKey.Input,
96
+ RecordingMetricKey.RageClick,
97
+ RecordingMetricKey.DeadClick,
98
+ RecordingMetricKey.ScrollBack,
99
+ RecordingMetricKey.TabUnfocus,
100
+ RecordingMetricKey.RouteChange
101
+ ]);
102
+ const isObject = (value) => typeof value === "object" && value !== null;
103
+ const isUserInteractionEvent = (event) => {
104
+ if (!isObject(event) || !isObject(event.data)) return false;
105
+ if (event.type === 3) return typeof event.data.source === "number" && USER_INTERACTION_SOURCES.has(event.data.source);
106
+ if (event.type === 5) return typeof event.data.tag === "string" && USER_INTERACTION_TAGS.has(event.data.tag);
107
+ return event.type === 6 && typeof event.data.plugin === "string" && USER_INTERACTION_PLUGINS.has(event.data.plugin);
108
+ };
109
+ const isSessionActivityEvent = (event) => isObject(event) && event.type === 4 || isUserInteractionEvent(event);
110
+ //#endregion
24
111
  //#region ../csr-common/src/uploader/client-context.ts
25
112
  var import_es5 = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
26
113
  (function(e, t) {
@@ -1897,6 +1984,7 @@ async function createUploader(opts) {
1897
1984
  tabId: effectiveTabId,
1898
1985
  eventCounter: counter,
1899
1986
  data: event,
1987
+ userActivity: isSessionActivityEvent(event),
1900
1988
  ...nextAdoptionMeta ?? {}
1901
1989
  };
1902
1990
  counter += 1;
@@ -2060,15 +2148,4 @@ function writeCounter(counter) {
2060
2148
  sessionStorage.setItem(STORAGE_COUNTER, String(counter));
2061
2149
  }
2062
2150
  //#endregion
2063
- Object.defineProperty(exports, "createUploader", {
2064
- enumerable: true,
2065
- get: function() {
2066
- return createUploader;
2067
- }
2068
- });
2069
- Object.defineProperty(exports, "workerScript", {
2070
- enumerable: true,
2071
- get: function() {
2072
- return workerScript;
2073
- }
2074
- });
2151
+ export { workerScript as n, RecordingPluginName as r, createUploader as t };
package/dist/worker.cjs CHANGED
@@ -1,3 +1,3 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_uploader = require("./uploader-CcDy4aME.cjs");
2
+ const require_uploader = require("./uploader-B6-mcWdA.cjs");
3
3
  exports.workerScript = require_uploader.workerScript;
package/dist/worker.js CHANGED
@@ -1,2 +1,2 @@
1
- import { n as workerScript } from "./uploader-CkAqZXJP.js";
1
+ import { n as workerScript } from "./uploader-vqAzevKI.js";
2
2
  export { workerScript };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@spotify-confidence/session-recording",
3
3
  "license": "Apache-2.0",
4
- "version": "0.18.10",
4
+ "version": "0.18.12",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/spotify/confidence-sdk-js.git",
@@ -41,8 +41,8 @@
41
41
  }
42
42
  },
43
43
  "dependencies": {
44
- "@spotify-confidence/csr-common": "0.18.7",
45
- "@spotify-confidence/csr-recorder": "0.17.15"
44
+ "@spotify-confidence/csr-common": "0.18.9",
45
+ "@spotify-confidence/csr-recorder": "0.17.17"
46
46
  },
47
47
  "module": "./dist/index.js",
48
48
  "exports": {
package/src/index.ts CHANGED
@@ -2,6 +2,7 @@ import { record } from '@spotify-confidence/csr-recorder';
2
2
  import type { ConsoleLogLevel } from '@spotify-confidence/csr-common';
3
3
  import {
4
4
  RecordingEventType,
5
+ RecordingPluginName,
5
6
  type TagPluginData,
6
7
  type MeasurePluginData,
7
8
  type FlagEvaluationPluginData,
@@ -175,7 +176,7 @@ export function initSessionRecorder(options: InitSessionRecorderOptions): Sessio
175
176
 
176
177
  stopObservingFlags = observeFlags(({ flagKey, variant, assignmentOrigin }) => {
177
178
  const data: FlagEvaluationPluginData = {
178
- plugin: 'csr:flagEvaluation',
179
+ plugin: RecordingPluginName.FlagEvaluation,
179
180
  payload: { flagKey, variant, assignmentOrigin },
180
181
  };
181
182
  sendEvent?.({
@@ -225,7 +226,7 @@ export function initSessionRecorder(options: InitSessionRecorderOptions): Sessio
225
226
  return;
226
227
  }
227
228
  const data: TagPluginData = {
228
- plugin: 'csr:tag',
229
+ plugin: RecordingPluginName.Tag,
229
230
  payload: value !== undefined ? { key, value } : { key },
230
231
  };
231
232
  sendEvent?.({
@@ -246,7 +247,7 @@ export function initSessionRecorder(options: InitSessionRecorderOptions): Sessio
246
247
  return;
247
248
  }
248
249
  const data: MeasurePluginData = {
249
- plugin: 'csr:measure',
250
+ plugin: RecordingPluginName.Measure,
250
251
  payload: value !== undefined ? { key, value } : { key },
251
252
  };
252
253
  sendEvent?.({
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const SDK_VERSION = '0.18.10';
1
+ export const SDK_VERSION = '0.18.12';