@spotify-confidence/csr-common 0.18.9 → 0.18.10
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 +7 -0
- package/dist/index.cjs +3 -15
- package/dist/index.d.cts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +2 -15
- package/dist/{session-activity-HfuXKqGd.cjs → session-activity-B8swR5Wg.cjs} +29 -0
- package/dist/{session-activity-CJ1wae06.js → session-activity-tshS9Hua.js} +18 -1
- package/dist/{types-Bg0UKTVQ.d.cts → types-C3mO_2YH.d.cts} +1 -1
- package/dist/{types-Bg0UKTVQ.d.ts → types-C3mO_2YH.d.ts} +1 -1
- package/dist/uploader/index.cjs +2 -2
- package/dist/uploader/index.d.cts +1 -1
- package/dist/uploader/index.d.ts +1 -1
- package/dist/uploader/index.js +2 -2
- package/package.json +1 -1
- package/src/index.ts +1 -1
- package/src/uploader/client-context.test.ts +27 -0
- package/src/uploader/client-context.ts +3 -1
- package/src/url.ts +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.18.10](https://github.com/spotify/confidence-sdk-js/compare/csr-common-v0.18.9...csr-common-v0.18.10) (2026-09-09)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### 🐛 Bug Fixes
|
|
7
|
+
|
|
8
|
+
* strip query and hash from recording URLs ([#452](https://github.com/spotify/confidence-sdk-js/issues/452)) ([1ce37f3](https://github.com/spotify/confidence-sdk-js/commit/1ce37f3f43c05bfea4a90839a6de42314096bb40))
|
|
9
|
+
|
|
3
10
|
## [0.18.9](https://github.com/spotify/confidence-sdk-js/compare/csr-common-v0.18.8...csr-common-v0.18.9) (2026-09-07)
|
|
4
11
|
|
|
5
12
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,18 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_session_activity = require("./session-activity-
|
|
3
|
-
//#region src/url.ts
|
|
4
|
-
/**
|
|
5
|
-
* Extract only the pathname from a URL, stripping origin, query string, and
|
|
6
|
-
* hash. Used across recorder and analyzer to avoid capturing PII in route data.
|
|
7
|
-
*/
|
|
8
|
-
function stripUrl(url) {
|
|
9
|
-
try {
|
|
10
|
-
return new URL(url).pathname;
|
|
11
|
-
} catch (_e) {
|
|
12
|
-
return url;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
//#endregion
|
|
2
|
+
const require_session_activity = require("./session-activity-B8swR5Wg.cjs");
|
|
16
3
|
//#region src/custom-event-limits.ts
|
|
17
4
|
const MAX_KEY_LENGTH = 128;
|
|
18
5
|
const MAX_TAG_VALUE_LENGTH = 256;
|
|
@@ -51,7 +38,8 @@ exports.SerializedNodeType = require_session_activity.SerializedNodeType;
|
|
|
51
38
|
exports.isSessionActivityEvent = require_session_activity.isSessionActivityEvent;
|
|
52
39
|
exports.isUserInteractionEvent = require_session_activity.isUserInteractionEvent;
|
|
53
40
|
exports.isUserInteractionMetric = require_session_activity.isUserInteractionMetric;
|
|
54
|
-
exports.stripUrl = stripUrl;
|
|
41
|
+
exports.stripUrl = require_session_activity.stripUrl;
|
|
42
|
+
exports.stripUrlQueryAndHash = require_session_activity.stripUrlQueryAndHash;
|
|
55
43
|
exports.validateKey = validateKey;
|
|
56
44
|
exports.validateMeasureValue = validateMeasureValue;
|
|
57
45
|
exports.validateTagValue = validateTagValue;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as ClientContext, n as Frame, o as UserAgentContext } from "./types-
|
|
1
|
+
import { i as ClientContext, n as Frame, o as UserAgentContext } from "./types-C3mO_2YH.cjs";
|
|
2
2
|
|
|
3
3
|
//#region src/events.d.ts
|
|
4
4
|
/**
|
|
@@ -370,6 +370,8 @@ type RecordingEvent = {
|
|
|
370
370
|
};
|
|
371
371
|
//#endregion
|
|
372
372
|
//#region src/url.d.ts
|
|
373
|
+
/** Remove query strings and fragments, including from relative or malformed URLs. */
|
|
374
|
+
declare function stripUrlQueryAndHash(url: string): string;
|
|
373
375
|
/**
|
|
374
376
|
* Extract only the pathname from a URL, stripping origin, query string, and
|
|
375
377
|
* hash. Used across recorder and analyzer to avoid capturing PII in route data.
|
|
@@ -407,4 +409,4 @@ declare function validateKey(key: string): string | null;
|
|
|
407
409
|
declare function validateTagValue(value: string | undefined): string | null;
|
|
408
410
|
declare function validateMeasureValue(value: number | undefined): string | null;
|
|
409
411
|
//#endregion
|
|
410
|
-
export { type AwayGapCustomData, type ClickCustomData, type ClientContext, type ClipboardAction, type ClipboardPluginData, type ConsoleLogLevel, type ConsoleLogPluginData, type CustomEventData, type DeadClickCustomData, type DialogOpenedCustomData, type ElementDescriptor, type ErrorMessageCustomData, type FlagEvaluationPluginData, type FormFieldReEditCustomData, type Frame, type GraphQLRequestMetadata, type IdleGapCustomData, type IncrementalSnapshotData, IncrementalSource, type InputCustomData, MAX_DISTINCT_KEYS, MAX_KEY_LENGTH, MAX_TAG_VALUE_LENGTH, MAX_VALUES_PER_KEY, type MeasurePluginData, type MouseInteractionData, MouseInteractions, type NetworkRequestInitiator, type NetworkRequestPluginData, type OpaqueIncrementalData, type PluginEventData, type RageClickCustomData, RecordingCustomEventTag, type RecordingEvent, RecordingEventType, RecordingMetricKey, RecordingPluginName, type RouteChangeCustomData, type RouteChangePayload, type RouteChangePluginData, type RouteChangeTrigger, type ScrollBackCustomData, type SelectionData, type SelectionRange, SerializedNodeType, type TabRefocusCustomData, type TabUnfocusCustomData, type TabVisibilityPluginData, type TagPluginData, type UserAgentContext, isSessionActivityEvent, isUserInteractionEvent, isUserInteractionMetric, stripUrl, validateKey, validateMeasureValue, validateTagValue };
|
|
412
|
+
export { type AwayGapCustomData, type ClickCustomData, type ClientContext, type ClipboardAction, type ClipboardPluginData, type ConsoleLogLevel, type ConsoleLogPluginData, type CustomEventData, type DeadClickCustomData, type DialogOpenedCustomData, type ElementDescriptor, type ErrorMessageCustomData, type FlagEvaluationPluginData, type FormFieldReEditCustomData, type Frame, type GraphQLRequestMetadata, type IdleGapCustomData, type IncrementalSnapshotData, IncrementalSource, type InputCustomData, MAX_DISTINCT_KEYS, MAX_KEY_LENGTH, MAX_TAG_VALUE_LENGTH, MAX_VALUES_PER_KEY, type MeasurePluginData, type MouseInteractionData, MouseInteractions, type NetworkRequestInitiator, type NetworkRequestPluginData, type OpaqueIncrementalData, type PluginEventData, type RageClickCustomData, RecordingCustomEventTag, type RecordingEvent, RecordingEventType, RecordingMetricKey, RecordingPluginName, type RouteChangeCustomData, type RouteChangePayload, type RouteChangePluginData, type RouteChangeTrigger, type ScrollBackCustomData, type SelectionData, type SelectionRange, SerializedNodeType, type TabRefocusCustomData, type TabUnfocusCustomData, type TabVisibilityPluginData, type TagPluginData, type UserAgentContext, isSessionActivityEvent, isUserInteractionEvent, isUserInteractionMetric, stripUrl, stripUrlQueryAndHash, validateKey, validateMeasureValue, validateTagValue };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as ClientContext, n as Frame, o as UserAgentContext } from "./types-
|
|
1
|
+
import { i as ClientContext, n as Frame, o as UserAgentContext } from "./types-C3mO_2YH.js";
|
|
2
2
|
|
|
3
3
|
//#region src/events.d.ts
|
|
4
4
|
/**
|
|
@@ -370,6 +370,8 @@ type RecordingEvent = {
|
|
|
370
370
|
};
|
|
371
371
|
//#endregion
|
|
372
372
|
//#region src/url.d.ts
|
|
373
|
+
/** Remove query strings and fragments, including from relative or malformed URLs. */
|
|
374
|
+
declare function stripUrlQueryAndHash(url: string): string;
|
|
373
375
|
/**
|
|
374
376
|
* Extract only the pathname from a URL, stripping origin, query string, and
|
|
375
377
|
* hash. Used across recorder and analyzer to avoid capturing PII in route data.
|
|
@@ -407,4 +409,4 @@ declare function validateKey(key: string): string | null;
|
|
|
407
409
|
declare function validateTagValue(value: string | undefined): string | null;
|
|
408
410
|
declare function validateMeasureValue(value: number | undefined): string | null;
|
|
409
411
|
//#endregion
|
|
410
|
-
export { type AwayGapCustomData, type ClickCustomData, type ClientContext, type ClipboardAction, type ClipboardPluginData, type ConsoleLogLevel, type ConsoleLogPluginData, type CustomEventData, type DeadClickCustomData, type DialogOpenedCustomData, type ElementDescriptor, type ErrorMessageCustomData, type FlagEvaluationPluginData, type FormFieldReEditCustomData, type Frame, type GraphQLRequestMetadata, type IdleGapCustomData, type IncrementalSnapshotData, IncrementalSource, type InputCustomData, MAX_DISTINCT_KEYS, MAX_KEY_LENGTH, MAX_TAG_VALUE_LENGTH, MAX_VALUES_PER_KEY, type MeasurePluginData, type MouseInteractionData, MouseInteractions, type NetworkRequestInitiator, type NetworkRequestPluginData, type OpaqueIncrementalData, type PluginEventData, type RageClickCustomData, RecordingCustomEventTag, type RecordingEvent, RecordingEventType, RecordingMetricKey, RecordingPluginName, type RouteChangeCustomData, type RouteChangePayload, type RouteChangePluginData, type RouteChangeTrigger, type ScrollBackCustomData, type SelectionData, type SelectionRange, SerializedNodeType, type TabRefocusCustomData, type TabUnfocusCustomData, type TabVisibilityPluginData, type TagPluginData, type UserAgentContext, isSessionActivityEvent, isUserInteractionEvent, isUserInteractionMetric, stripUrl, validateKey, validateMeasureValue, validateTagValue };
|
|
412
|
+
export { type AwayGapCustomData, type ClickCustomData, type ClientContext, type ClipboardAction, type ClipboardPluginData, type ConsoleLogLevel, type ConsoleLogPluginData, type CustomEventData, type DeadClickCustomData, type DialogOpenedCustomData, type ElementDescriptor, type ErrorMessageCustomData, type FlagEvaluationPluginData, type FormFieldReEditCustomData, type Frame, type GraphQLRequestMetadata, type IdleGapCustomData, type IncrementalSnapshotData, IncrementalSource, type InputCustomData, MAX_DISTINCT_KEYS, MAX_KEY_LENGTH, MAX_TAG_VALUE_LENGTH, MAX_VALUES_PER_KEY, type MeasurePluginData, type MouseInteractionData, MouseInteractions, type NetworkRequestInitiator, type NetworkRequestPluginData, type OpaqueIncrementalData, type PluginEventData, type RageClickCustomData, RecordingCustomEventTag, type RecordingEvent, RecordingEventType, RecordingMetricKey, RecordingPluginName, type RouteChangeCustomData, type RouteChangePayload, type RouteChangePluginData, type RouteChangeTrigger, type ScrollBackCustomData, type SelectionData, type SelectionRange, SerializedNodeType, type TabRefocusCustomData, type TabUnfocusCustomData, type TabVisibilityPluginData, type TagPluginData, type UserAgentContext, isSessionActivityEvent, isUserInteractionEvent, isUserInteractionMetric, stripUrl, stripUrlQueryAndHash, validateKey, validateMeasureValue, validateTagValue };
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
//#region src/url.ts
|
|
3
|
-
/**
|
|
4
|
-
* Extract only the pathname from a URL, stripping origin, query string, and
|
|
5
|
-
* hash. Used across recorder and analyzer to avoid capturing PII in route data.
|
|
6
|
-
*/
|
|
7
|
-
function stripUrl(url) {
|
|
8
|
-
try {
|
|
9
|
-
return new URL(url).pathname;
|
|
10
|
-
} catch (_e) {
|
|
11
|
-
return url;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
//#endregion
|
|
1
|
+
import { a as stripUrl, c as MouseInteractions, d as RecordingPluginName, f as SerializedNodeType, i as RecordingMetricKey, l as RecordingCustomEventTag, n as isUserInteractionEvent, o as stripUrlQueryAndHash, r as isUserInteractionMetric, s as IncrementalSource, t as isSessionActivityEvent, u as RecordingEventType } from "./session-activity-tshS9Hua.js";
|
|
15
2
|
//#region src/custom-event-limits.ts
|
|
16
3
|
const MAX_KEY_LENGTH = 128;
|
|
17
4
|
const MAX_TAG_VALUE_LENGTH = 256;
|
|
@@ -36,4 +23,4 @@ function validateMeasureValue(value) {
|
|
|
36
23
|
return null;
|
|
37
24
|
}
|
|
38
25
|
//#endregion
|
|
39
|
-
export { IncrementalSource, MAX_DISTINCT_KEYS, MAX_KEY_LENGTH, MAX_TAG_VALUE_LENGTH, MAX_VALUES_PER_KEY, MouseInteractions, RecordingCustomEventTag, RecordingEventType, RecordingMetricKey, RecordingPluginName, SerializedNodeType, isSessionActivityEvent, isUserInteractionEvent, isUserInteractionMetric, stripUrl, validateKey, validateMeasureValue, validateTagValue };
|
|
26
|
+
export { IncrementalSource, MAX_DISTINCT_KEYS, MAX_KEY_LENGTH, MAX_TAG_VALUE_LENGTH, MAX_VALUES_PER_KEY, MouseInteractions, RecordingCustomEventTag, RecordingEventType, RecordingMetricKey, RecordingPluginName, SerializedNodeType, isSessionActivityEvent, isUserInteractionEvent, isUserInteractionMetric, stripUrl, stripUrlQueryAndHash, validateKey, validateMeasureValue, validateTagValue };
|
|
@@ -91,6 +91,23 @@ let MouseInteractions = /* @__PURE__ */ function(MouseInteractions) {
|
|
|
91
91
|
return MouseInteractions;
|
|
92
92
|
}({});
|
|
93
93
|
//#endregion
|
|
94
|
+
//#region src/url.ts
|
|
95
|
+
/** Remove query strings and fragments, including from relative or malformed URLs. */
|
|
96
|
+
function stripUrlQueryAndHash(url) {
|
|
97
|
+
return url.split(/[?#]/, 1)[0];
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Extract only the pathname from a URL, stripping origin, query string, and
|
|
101
|
+
* hash. Used across recorder and analyzer to avoid capturing PII in route data.
|
|
102
|
+
*/
|
|
103
|
+
function stripUrl(url) {
|
|
104
|
+
try {
|
|
105
|
+
return new URL(url).pathname;
|
|
106
|
+
} catch (_e) {
|
|
107
|
+
return url;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
94
111
|
//#region src/metrics.ts
|
|
95
112
|
const RecordingMetricKey = {
|
|
96
113
|
Click: "clicks",
|
|
@@ -210,3 +227,15 @@ Object.defineProperty(exports, "isUserInteractionMetric", {
|
|
|
210
227
|
return isUserInteractionMetric;
|
|
211
228
|
}
|
|
212
229
|
});
|
|
230
|
+
Object.defineProperty(exports, "stripUrl", {
|
|
231
|
+
enumerable: true,
|
|
232
|
+
get: function() {
|
|
233
|
+
return stripUrl;
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
Object.defineProperty(exports, "stripUrlQueryAndHash", {
|
|
237
|
+
enumerable: true,
|
|
238
|
+
get: function() {
|
|
239
|
+
return stripUrlQueryAndHash;
|
|
240
|
+
}
|
|
241
|
+
});
|
|
@@ -91,6 +91,23 @@ let MouseInteractions = /* @__PURE__ */ function(MouseInteractions) {
|
|
|
91
91
|
return MouseInteractions;
|
|
92
92
|
}({});
|
|
93
93
|
//#endregion
|
|
94
|
+
//#region src/url.ts
|
|
95
|
+
/** Remove query strings and fragments, including from relative or malformed URLs. */
|
|
96
|
+
function stripUrlQueryAndHash(url) {
|
|
97
|
+
return url.split(/[?#]/, 1)[0];
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Extract only the pathname from a URL, stripping origin, query string, and
|
|
101
|
+
* hash. Used across recorder and analyzer to avoid capturing PII in route data.
|
|
102
|
+
*/
|
|
103
|
+
function stripUrl(url) {
|
|
104
|
+
try {
|
|
105
|
+
return new URL(url).pathname;
|
|
106
|
+
} catch (_e) {
|
|
107
|
+
return url;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
94
111
|
//#region src/metrics.ts
|
|
95
112
|
const RecordingMetricKey = {
|
|
96
113
|
Click: "clicks",
|
|
@@ -150,4 +167,4 @@ const isUserInteractionEvent = (event) => {
|
|
|
150
167
|
const isUserInteractionMetric = (metricKey) => USER_INTERACTION_METRIC_KEYS.has(metricKey);
|
|
151
168
|
const isSessionActivityEvent = (event) => isObject(event) && event.type === 4 || isUserInteractionEvent(event);
|
|
152
169
|
//#endregion
|
|
153
|
-
export {
|
|
170
|
+
export { stripUrl as a, MouseInteractions as c, RecordingPluginName as d, SerializedNodeType as f, RecordingMetricKey as i, RecordingCustomEventTag as l, isUserInteractionEvent as n, stripUrlQueryAndHash as o, isUserInteractionMetric as r, IncrementalSource as s, isSessionActivityEvent as t, RecordingEventType as u };
|
|
@@ -22,7 +22,7 @@ type UserAgentContext = {
|
|
|
22
22
|
screenWidth?: number;
|
|
23
23
|
screenHeight?: number;
|
|
24
24
|
devicePixelRatio?: number; /** Initial document URI — without query/hash to avoid leaking PII. */
|
|
25
|
-
uri?: string;
|
|
25
|
+
uri?: string; /** Document referrer — without query/hash to avoid leaking PII. */
|
|
26
26
|
referrer?: string;
|
|
27
27
|
};
|
|
28
28
|
interface ClientContext {
|
|
@@ -22,7 +22,7 @@ type UserAgentContext = {
|
|
|
22
22
|
screenWidth?: number;
|
|
23
23
|
screenHeight?: number;
|
|
24
24
|
devicePixelRatio?: number; /** Initial document URI — without query/hash to avoid leaking PII. */
|
|
25
|
-
uri?: string;
|
|
25
|
+
uri?: string; /** Document referrer — without query/hash to avoid leaking PII. */
|
|
26
26
|
referrer?: string;
|
|
27
27
|
};
|
|
28
28
|
interface ClientContext {
|
package/dist/uploader/index.cjs
CHANGED
|
@@ -21,7 +21,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
21
21
|
enumerable: true
|
|
22
22
|
}) : target, mod));
|
|
23
23
|
//#endregion
|
|
24
|
-
const require_session_activity = require("../session-activity-
|
|
24
|
+
const require_session_activity = require("../session-activity-B8swR5Wg.cjs");
|
|
25
25
|
let bowser = require("bowser");
|
|
26
26
|
bowser = __toESM(bowser, 1);
|
|
27
27
|
//#region src/uploader/client-context.ts
|
|
@@ -46,7 +46,7 @@ function collectUserAgentContext() {
|
|
|
46
46
|
screenHeight: window.screen.height,
|
|
47
47
|
devicePixelRatio: window.devicePixelRatio,
|
|
48
48
|
uri: `${window.location.origin}${window.location.pathname}`,
|
|
49
|
-
referrer: document.referrer
|
|
49
|
+
referrer: require_session_activity.stripUrlQueryAndHash(document.referrer)
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
52
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ContextValue, i as ClientContext, o as UserAgentContext, r as Uploader, s as collectUserAgentContext, t as CreateUploaderOptions } from "../types-
|
|
1
|
+
import { a as ContextValue, i as ClientContext, o as UserAgentContext, r as Uploader, s as collectUserAgentContext, t as CreateUploaderOptions } from "../types-C3mO_2YH.cjs";
|
|
2
2
|
|
|
3
3
|
//#region src/uploader/create-uploader.d.ts
|
|
4
4
|
declare function createUploader(opts: CreateUploaderOptions): Promise<Uploader | null>;
|
package/dist/uploader/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ContextValue, i as ClientContext, o as UserAgentContext, r as Uploader, s as collectUserAgentContext, t as CreateUploaderOptions } from "../types-
|
|
1
|
+
import { a as ContextValue, i as ClientContext, o as UserAgentContext, r as Uploader, s as collectUserAgentContext, t as CreateUploaderOptions } from "../types-C3mO_2YH.js";
|
|
2
2
|
|
|
3
3
|
//#region src/uploader/create-uploader.d.ts
|
|
4
4
|
declare function createUploader(opts: CreateUploaderOptions): Promise<Uploader | null>;
|
package/dist/uploader/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as isSessionActivityEvent } from "../session-activity-
|
|
1
|
+
import { o as stripUrlQueryAndHash, t as isSessionActivityEvent } from "../session-activity-tshS9Hua.js";
|
|
2
2
|
import Bowser from "bowser";
|
|
3
3
|
//#region src/uploader/client-context.ts
|
|
4
4
|
function collectUserAgentContext() {
|
|
@@ -22,7 +22,7 @@ function collectUserAgentContext() {
|
|
|
22
22
|
screenHeight: window.screen.height,
|
|
23
23
|
devicePixelRatio: window.devicePixelRatio,
|
|
24
24
|
uri: `${window.location.origin}${window.location.pathname}`,
|
|
25
|
-
referrer: document.referrer
|
|
25
|
+
referrer: stripUrlQueryAndHash(document.referrer)
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
//#endregion
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -84,6 +84,33 @@ describe('collectUserAgentContext', () => {
|
|
|
84
84
|
});
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
+
it.each([
|
|
88
|
+
['https://referrer.example/path?email=alice@example.com#token', 'https://referrer.example/path'],
|
|
89
|
+
['https://referrer.example/path#token?email=alice@example.com', 'https://referrer.example/path'],
|
|
90
|
+
['https://referrer.example/path?email=alice@example.com', 'https://referrer.example/path'],
|
|
91
|
+
['https://referrer.example/path#token', 'https://referrer.example/path'],
|
|
92
|
+
['', ''],
|
|
93
|
+
['invalid-url?email=alice@example.com#token', 'invalid-url'],
|
|
94
|
+
])('strips query/hash from referrer %s', (referrer, expected) => {
|
|
95
|
+
stubBrowser({ document: { referrer } });
|
|
96
|
+
expect(collectUserAgentContext()?.referrer).toBe(expected);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('excludes query/hash from the initial document URI', () => {
|
|
100
|
+
stubBrowser({
|
|
101
|
+
window: {
|
|
102
|
+
location: {
|
|
103
|
+
origin: 'https://example.com',
|
|
104
|
+
pathname: '/path',
|
|
105
|
+
search: '?email=alice@example.com',
|
|
106
|
+
hash: '#token',
|
|
107
|
+
href: 'https://example.com/path?email=alice@example.com#token',
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
expect(collectUserAgentContext()?.uri).toBe('https://example.com/path');
|
|
112
|
+
});
|
|
113
|
+
|
|
87
114
|
it('flags `mobile` platform type as mobile=true', () => {
|
|
88
115
|
parse.mockReturnValueOnce({
|
|
89
116
|
os: {},
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Bowser from 'bowser';
|
|
2
|
+
import { stripUrlQueryAndHash } from '../url';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* JSON-shaped values accepted in a Context — matches `google.protobuf.Struct`.
|
|
@@ -31,6 +32,7 @@ export type UserAgentContext = {
|
|
|
31
32
|
devicePixelRatio?: number;
|
|
32
33
|
/** Initial document URI — without query/hash to avoid leaking PII. */
|
|
33
34
|
uri?: string;
|
|
35
|
+
/** Document referrer — without query/hash to avoid leaking PII. */
|
|
34
36
|
referrer?: string;
|
|
35
37
|
};
|
|
36
38
|
|
|
@@ -65,6 +67,6 @@ export function collectUserAgentContext(): UserAgentContext | undefined {
|
|
|
65
67
|
screenHeight: window.screen.height,
|
|
66
68
|
devicePixelRatio: window.devicePixelRatio,
|
|
67
69
|
uri: `${window.location.origin}${window.location.pathname}`,
|
|
68
|
-
referrer: document.referrer,
|
|
70
|
+
referrer: stripUrlQueryAndHash(document.referrer),
|
|
69
71
|
};
|
|
70
72
|
}
|
package/src/url.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/** Remove query strings and fragments, including from relative or malformed URLs. */
|
|
2
|
+
export function stripUrlQueryAndHash(url: string): string {
|
|
3
|
+
return url.split(/[?#]/, 1)[0];
|
|
4
|
+
}
|
|
5
|
+
|
|
1
6
|
/**
|
|
2
7
|
* Extract only the pathname from a URL, stripping origin, query string, and
|
|
3
8
|
* hash. Used across recorder and analyzer to avoid capturing PII in route data.
|