@kubesense/kubesense-browser-rum-core 1.4.0 → 1.6.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.
Files changed (57) hide show
  1. package/README.md +8 -1
  2. package/cjs/boot/preStartRum.js +20 -2
  3. package/cjs/boot/preStartRum.js.map +1 -1
  4. package/cjs/domain/configuration/configuration.d.ts +32 -1
  5. package/cjs/domain/configuration/configuration.js +2 -0
  6. package/cjs/domain/configuration/configuration.js.map +1 -1
  7. package/cjs/domain/configuration/index.d.ts +2 -0
  8. package/cjs/domain/configuration/index.js +2 -0
  9. package/cjs/domain/configuration/index.js.map +1 -1
  10. package/cjs/domain/configuration/remoteSdkConfiguration.d.ts +18 -0
  11. package/cjs/domain/configuration/remoteSdkConfiguration.js +142 -0
  12. package/cjs/domain/configuration/remoteSdkConfiguration.js.map +1 -0
  13. package/cjs/domain/configuration/sessionReplayRoutes.d.ts +35 -0
  14. package/cjs/domain/configuration/sessionReplayRoutes.js +70 -0
  15. package/cjs/domain/configuration/sessionReplayRoutes.js.map +1 -0
  16. package/cjs/domain/contexts/defaultContext.js +1 -1
  17. package/cjs/domain/getSessionReplayUrl.d.ts +1 -1
  18. package/cjs/domain/getSessionReplayUrl.js +3 -3
  19. package/cjs/domain/getSessionReplayUrl.js.map +1 -1
  20. package/cjs/domain/tracing/tracer.types.d.ts +1 -1
  21. package/cjs/index.d.ts +2 -1
  22. package/cjs/index.js +4 -1
  23. package/cjs/index.js.map +1 -1
  24. package/cjs/rumEvent.types.d.ts +4 -0
  25. package/esm/boot/preStartRum.js +21 -3
  26. package/esm/boot/preStartRum.js.map +1 -1
  27. package/esm/domain/configuration/configuration.d.ts +32 -1
  28. package/esm/domain/configuration/configuration.js +2 -0
  29. package/esm/domain/configuration/configuration.js.map +1 -1
  30. package/esm/domain/configuration/index.d.ts +2 -0
  31. package/esm/domain/configuration/index.js +2 -0
  32. package/esm/domain/configuration/index.js.map +1 -1
  33. package/esm/domain/configuration/remoteSdkConfiguration.d.ts +18 -0
  34. package/esm/domain/configuration/remoteSdkConfiguration.js +137 -0
  35. package/esm/domain/configuration/remoteSdkConfiguration.js.map +1 -0
  36. package/esm/domain/configuration/sessionReplayRoutes.d.ts +35 -0
  37. package/esm/domain/configuration/sessionReplayRoutes.js +65 -0
  38. package/esm/domain/configuration/sessionReplayRoutes.js.map +1 -0
  39. package/esm/domain/contexts/defaultContext.js +1 -1
  40. package/esm/domain/getSessionReplayUrl.d.ts +1 -1
  41. package/esm/domain/getSessionReplayUrl.js +2 -2
  42. package/esm/domain/getSessionReplayUrl.js.map +1 -1
  43. package/esm/domain/tracing/tracer.types.d.ts +1 -1
  44. package/esm/index.d.ts +2 -1
  45. package/esm/index.js +1 -0
  46. package/esm/index.js.map +1 -1
  47. package/esm/rumEvent.types.d.ts +4 -0
  48. package/package.json +3 -3
  49. package/src/boot/preStartRum.ts +25 -2
  50. package/src/domain/configuration/configuration.ts +36 -1
  51. package/src/domain/configuration/index.ts +2 -0
  52. package/src/domain/configuration/remoteSdkConfiguration.ts +190 -0
  53. package/src/domain/configuration/sessionReplayRoutes.ts +96 -0
  54. package/src/domain/getSessionReplayUrl.ts +3 -3
  55. package/src/domain/tracing/tracer.types.ts +1 -1
  56. package/src/index.ts +2 -0
  57. package/src/rumEvent.types.ts +4 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubesense/kubesense-browser-rum-core",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "Shared RUM event-collection logic for the Kubesense Browser SDK (consumed by the rum and rum-slim packages).",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -19,7 +19,7 @@
19
19
  "build": "node ../../scripts/build/build-package.ts --modules"
20
20
  },
21
21
  "dependencies": {
22
- "@kubesense/kubesense-browser-core": "1.4.0"
22
+ "@kubesense/kubesense-browser-core": "1.6.0"
23
23
  },
24
24
  "devDependencies": {
25
25
  "ajv": "8.17.1"
@@ -35,5 +35,5 @@
35
35
  "publishConfig": {
36
36
  "access": "public"
37
37
  },
38
- "gitHead": "af1a0950e2441f45b4303b2dc17b09f9cd0d999d"
38
+ "gitHead": "5b90a5a0ea4ee972c079b9a55c45d05c44f14688"
39
39
  }
@@ -24,6 +24,8 @@ import {
24
24
  validateAndBuildRumConfiguration,
25
25
  fetchAndApplyRemoteConfiguration,
26
26
  serializeRumConfiguration,
27
+ applyCachedSdkConfiguration,
28
+ scheduleSdkConfigurationRefresh,
27
29
  } from '../domain/configuration'
28
30
  import type { ViewOptions } from '../domain/view/trackViews'
29
31
  import type {
@@ -105,6 +107,22 @@ export function createPreStartStrategy(
105
107
  initConfiguration = overrideInitConfigurationForBridge(initConfiguration)
106
108
  }
107
109
 
110
+ const remoteSdkConfigurationEnabled = !!initConfiguration.enableRemoteSdkConfiguration
111
+ if (remoteSdkConfigurationEnabled) {
112
+ // Reading the cached document is synchronous and network-free, so it does not delay
113
+ // initialization. The document fetched below only applies from the next page load.
114
+ const cachedSdkConfiguration = applyCachedSdkConfiguration(initConfiguration)
115
+ initConfiguration = cachedSdkConfiguration.initConfiguration
116
+
117
+ if (!cachedSdkConfiguration.rumEnabled) {
118
+ // RUM is switched off from the dashboard. Still refresh the document, otherwise the
119
+ // switch would be one-way: the SDK could never learn that it has been turned back on.
120
+ cachedInitConfiguration = initConfiguration
121
+ scheduleSdkConfigurationRefresh(initConfiguration).catch(monitorError)
122
+ return
123
+ }
124
+ }
125
+
108
126
  // Update the exposed initConfiguration to reflect the bridge and remote configuration overrides
109
127
  cachedInitConfiguration = initConfiguration
110
128
  addTelemetryConfiguration(serializeRumConfiguration(initConfiguration))
@@ -127,10 +145,10 @@ export function createPreStartStrategy(
127
145
  if (configuration.compressIntakeRequests && !eventBridgeAvailable && startDeflateWorker) {
128
146
  deflateWorker = startDeflateWorker(
129
147
  configuration,
130
- 'Kubsesense RUM',
148
+ 'KubeSense RUM',
131
149
  // Worker initialization can fail asynchronously, especially in Firefox where even CSP
132
150
  // issues are reported asynchronously. For now, the SDK will continue its execution even if
133
- // data won't be sent to Kubsesense. We could improve this behavior in the future.
151
+ // data won't be sent to KubeSense. We could improve this behavior in the future.
134
152
  noop
135
153
  )
136
154
  if (!deflateWorker) {
@@ -148,6 +166,11 @@ export function createPreStartStrategy(
148
166
 
149
167
  trackingConsentState.tryToInit(configuration.trackingConsent)
150
168
  tryStartRum()
169
+
170
+ if (remoteSdkConfigurationEnabled) {
171
+ // Fire-and-forget, after the SDK is running: refreshes the cache for the next page load.
172
+ scheduleSdkConfigurationRefresh(initConfiguration).catch(monitorError)
173
+ }
151
174
  }
152
175
 
153
176
  const addDurationVital = (vital: DurationVital) => {
@@ -17,6 +17,8 @@ import type { RumEvent } from '../../rumEvent.types'
17
17
  import type { RumPlugin } from '../plugins'
18
18
  import { isTracingOption } from '../tracing/tracer'
19
19
  import type { PropagatorType, TracingOption } from '../tracing/tracer.types'
20
+ import type { SessionReplayRoutes } from './sessionReplayRoutes'
21
+ import { validateSessionReplayRoutes } from './sessionReplayRoutes'
20
22
 
21
23
  export const DEFAULT_PROPAGATOR_TYPES: PropagatorType[] = ['tracecontext', 'kubesense']
22
24
 
@@ -61,7 +63,7 @@ export interface RumInitConfiguration extends InitConfiguration {
61
63
  propagateTraceBaggage?: boolean | undefined
62
64
 
63
65
  /**
64
- * Access to every event collected by the RUM SDK before they are sent to Kubsesense.
66
+ * Access to every event collected by the RUM SDK before they are sent to KubeSense.
65
67
  * It allows:
66
68
  * - Enrich your RUM events with additional context attributes
67
69
  * - Modify your RUM events to modify their content, or redact sensitive sequences (see the list of editable properties)
@@ -112,6 +114,18 @@ export interface RumInitConfiguration extends InitConfiguration {
112
114
  */
113
115
  remoteConfigurationProxy?: string | undefined
114
116
 
117
+ /**
118
+ * Enable dashboard-managed SDK settings served by the Kubesense collector.
119
+ *
120
+ * The configuration document is fetched asynchronously after the SDK starts and cached in
121
+ * local storage, so it never delays initialization. Cached settings are applied on the next
122
+ * page load. Local `init()` values are used until a document has been fetched, and any
123
+ * setting absent from the document keeps its local value.
124
+ *
125
+ * @defaultValue false
126
+ */
127
+ enableRemoteSdkConfiguration?: boolean | undefined
128
+
115
129
  // tracing options
116
130
  /**
117
131
  * A list of request URLs used to inject tracing headers.
@@ -175,6 +189,25 @@ export interface RumInitConfiguration extends InitConfiguration {
175
189
  */
176
190
  startSessionReplayRecordingManually?: boolean | undefined
177
191
 
192
+ /**
193
+ * Restrict Session Replay recording to specific routes.
194
+ *
195
+ * Recording starts and stops as the user navigates: it runs only while the current route
196
+ * matches `include` (when provided) and does not match `exclude`. Rules are matched against
197
+ * the path (`pathname` + `search` + `hash`), and string entries match by prefix, so
198
+ * `/checkout` also covers `/checkout/payment`.
199
+ *
200
+ * These rules only narrow recording — a session that was not sampled in by
201
+ * `sessionReplaySampleRate` is never recorded, whatever the routes say.
202
+ *
203
+ * @category Session Replay
204
+ * @example
205
+ * ```ts
206
+ * sessionReplayRoutes: { include: ['/checkout', '/signup'], exclude: ['/checkout/card'] }
207
+ * ```
208
+ */
209
+ sessionReplayRoutes?: SessionReplayRoutes | undefined
210
+
178
211
  /**
179
212
  * Enables privacy control for action names.
180
213
  *
@@ -305,6 +338,7 @@ export interface RumConfiguration extends Configuration {
305
338
  enablePrivacyForActionName: boolean
306
339
  sessionReplaySampleRate: number
307
340
  startSessionReplayRecordingManually: boolean
341
+ sessionReplayRoutes: SessionReplayRoutes | undefined
308
342
  trackUserInteractions: boolean
309
343
  trackViewsManually: boolean
310
344
  trackResources: boolean
@@ -372,6 +406,7 @@ export function validateAndBuildRumConfiguration(
372
406
  initConfiguration.startSessionReplayRecordingManually !== undefined
373
407
  ? !!initConfiguration.startSessionReplayRecordingManually
374
408
  : sessionReplaySampleRate === 0,
409
+ sessionReplayRoutes: validateSessionReplayRoutes(initConfiguration.sessionReplayRoutes),
375
410
  traceSampleRate: initConfiguration.traceSampleRate ?? 100,
376
411
  rulePsr: isNumber(initConfiguration.traceSampleRate) ? initConfiguration.traceSampleRate / 100 : undefined,
377
412
  allowedTracingUrls,
@@ -1,2 +1,4 @@
1
1
  export * from './configuration'
2
2
  export * from './remoteConfiguration'
3
+ export * from './remoteSdkConfiguration'
4
+ export * from './sessionReplayRoutes'
@@ -0,0 +1,190 @@
1
+ import type { RemoteSdkConfig } from '@kubesense/kubesense-browser-core'
2
+ import {
3
+ DefaultPrivacyLevel,
4
+ TraceContextInjection,
5
+ getCachedSdkConfig,
6
+ readBoolean,
7
+ readEnum,
8
+ readSampleRate,
9
+ readString,
10
+ refreshSdkConfig,
11
+ } from '@kubesense/kubesense-browser-core'
12
+ import type { RumInitConfiguration } from './configuration'
13
+ import type { SessionReplayRoutes } from './sessionReplayRoutes'
14
+
15
+ /**
16
+ * Applies the dashboard-managed configuration document to the user's init configuration.
17
+ *
18
+ * Only scalar settings travel: anything that is a function or a matcher (`beforeSend`,
19
+ * `allowedTracingUrls`, `excludedActivityUrls`, plugins) stays in application code. Identity and
20
+ * endpoint settings (`clientToken`, `applicationId`, `kubesenseRumEndpoint`, `proxy`) are also
21
+ * excluded — they determine where the configuration request itself goes, and letting them be
22
+ * remotely rewritten would be a redirection risk.
23
+ *
24
+ * Every field is applied only when present and valid, so the user's own init configuration —
25
+ * and failing that, the SDK default — always wins over a missing or malformed remote value.
26
+ */
27
+
28
+ const PRIVACY_LEVELS = [
29
+ DefaultPrivacyLevel.ALLOW,
30
+ DefaultPrivacyLevel.MASK,
31
+ DefaultPrivacyLevel.MASK_USER_INPUT,
32
+ ] as const
33
+
34
+ const TRACE_CONTEXT_INJECTIONS = [TraceContextInjection.ALL, TraceContextInjection.SAMPLED] as const
35
+
36
+ export interface RemoteSdkConfigurationResult {
37
+ initConfiguration: RumInitConfiguration
38
+ /** `false` when the dashboard has switched RUM off entirely. */
39
+ rumEnabled: boolean
40
+ }
41
+
42
+ /**
43
+ * Read the cached document and merge it into the init configuration. Synchronous and
44
+ * network-free: this runs on the initialization path.
45
+ */
46
+ export function applyCachedSdkConfiguration(initConfiguration: RumInitConfiguration): RemoteSdkConfigurationResult {
47
+ const remoteConfig = getCachedSdkConfig()
48
+ if (!remoteConfig) {
49
+ return { initConfiguration, rumEnabled: true }
50
+ }
51
+ return applySdkConfiguration(initConfiguration, remoteConfig)
52
+ }
53
+
54
+ export function applySdkConfiguration(
55
+ initConfiguration: RumInitConfiguration,
56
+ remoteConfig: RemoteSdkConfig
57
+ ): RemoteSdkConfigurationResult {
58
+ const features = remoteConfig.features
59
+ if (readBoolean(features as { [key: string]: unknown } | undefined, 'rum') === false) {
60
+ return { initConfiguration, rumEnabled: false }
61
+ }
62
+
63
+ const applied: RumInitConfiguration = { ...initConfiguration }
64
+
65
+ applyRumSection(applied, remoteConfig.rum)
66
+ applySessionReplaySection(applied, remoteConfig.sessionReplay)
67
+ applyProfilingSection(applied, remoteConfig.profiling)
68
+ applyFeatureSwitches(applied, features)
69
+
70
+ return { initConfiguration: applied, rumEnabled: true }
71
+ }
72
+
73
+ function applyRumSection(applied: RumInitConfiguration, rum: { [key: string]: unknown } | undefined) {
74
+ if (!rum) {
75
+ return
76
+ }
77
+
78
+ assign(applied, 'sessionSampleRate', readSampleRate(rum, 'sessionSampleRate'))
79
+ assign(applied, 'telemetrySampleRate', readSampleRate(rum, 'telemetrySampleRate'))
80
+ assign(applied, 'traceSampleRate', readSampleRate(rum, 'traceSampleRate'))
81
+
82
+ assign(applied, 'trackUserInteractions', readBoolean(rum, 'trackUserInteractions'))
83
+ assign(applied, 'trackResources', readBoolean(rum, 'trackResources'))
84
+ assign(applied, 'trackLongTasks', readBoolean(rum, 'trackLongTasks'))
85
+ assign(applied, 'trackViewsManually', readBoolean(rum, 'trackViewsManually'))
86
+ assign(applied, 'trackBfcacheViews', readBoolean(rum, 'trackBfcacheViews'))
87
+ assign(applied, 'trackAnonymousUser', readBoolean(rum, 'trackAnonymousUser'))
88
+ assign(applied, 'trackSessionAcrossSubdomains', readBoolean(rum, 'trackSessionAcrossSubdomains'))
89
+ assign(applied, 'enablePrivacyForActionName', readBoolean(rum, 'enablePrivacyForActionName'))
90
+ assign(applied, 'propagateTraceBaggage', readBoolean(rum, 'propagateTraceBaggage'))
91
+
92
+ assign(applied, 'defaultPrivacyLevel', readEnum(rum, 'defaultPrivacyLevel', PRIVACY_LEVELS))
93
+ assign(applied, 'traceContextInjection', readEnum(rum, 'traceContextInjection', TRACE_CONTEXT_INJECTIONS))
94
+ assign(applied, 'actionNameAttribute', readString(rum, 'actionNameAttribute'))
95
+
96
+ assign(applied, 'service', readString(rum, 'service'))
97
+ assign(applied, 'env', readString(rum, 'env'))
98
+ assign(applied, 'version', readString(rum, 'version'))
99
+ }
100
+
101
+ function applySessionReplaySection(
102
+ applied: RumInitConfiguration,
103
+ sessionReplay: { [key: string]: unknown } | undefined
104
+ ) {
105
+ if (!sessionReplay) {
106
+ return
107
+ }
108
+
109
+ assign(applied, 'sessionReplaySampleRate', readSampleRate(sessionReplay, 'sampleRate'))
110
+ assign(applied, 'startSessionReplayRecordingManually', readBoolean(sessionReplay, 'startRecordingManually'))
111
+ assign(applied, 'defaultPrivacyLevel', readEnum(sessionReplay, 'defaultPrivacyLevel', PRIVACY_LEVELS))
112
+ assign(applied, 'sessionReplayRoutes', readRoutes(sessionReplay.routes))
113
+ }
114
+
115
+ /**
116
+ * Route rules arriving as JSON can only be plain strings (prefix matches). Regexes and functions
117
+ * are deliberately not accepted from the dashboard: they cannot be expressed safely in JSON and a
118
+ * pathological pattern would run on every view change.
119
+ */
120
+ function readRoutes(routes: unknown): SessionReplayRoutes | undefined {
121
+ if (!routes || typeof routes !== 'object' || Array.isArray(routes)) {
122
+ return undefined
123
+ }
124
+
125
+ const include = readStringList((routes as { include?: unknown }).include)
126
+ const exclude = readStringList((routes as { exclude?: unknown }).exclude)
127
+
128
+ if (!include && !exclude) {
129
+ return undefined
130
+ }
131
+
132
+ return { include, exclude }
133
+ }
134
+
135
+ function readStringList(value: unknown): string[] | undefined {
136
+ if (!Array.isArray(value)) {
137
+ return undefined
138
+ }
139
+ const strings = value.filter((item): item is string => typeof item === 'string' && item.length > 0)
140
+ return strings.length > 0 ? strings : undefined
141
+ }
142
+
143
+ function applyProfilingSection(applied: RumInitConfiguration, profiling: { [key: string]: unknown } | undefined) {
144
+ if (!profiling) {
145
+ return
146
+ }
147
+ assign(applied, 'profilingSampleRate', readSampleRate(profiling, 'sampleRate'))
148
+ }
149
+
150
+ /**
151
+ * Feature switches are applied last so they always win over the per-section sample rates above.
152
+ * Turning a feature off is expressed through the setting that stops all of its collection.
153
+ */
154
+ function applyFeatureSwitches(applied: RumInitConfiguration, features: RemoteSdkConfig['features']) {
155
+ const source = features as { [key: string]: unknown } | undefined
156
+
157
+ if (readBoolean(source, 'sessionReplay') === false) {
158
+ applied.sessionReplaySampleRate = 0
159
+ applied.startSessionReplayRecordingManually = true
160
+ // Drop any route rules too: with replay off they are dead weight, and leaving them behind
161
+ // would suggest recording still happens somewhere.
162
+ applied.sessionReplayRoutes = undefined
163
+ }
164
+
165
+ if (readBoolean(source, 'profiling') === false) {
166
+ applied.profilingSampleRate = 0
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Assigning only defined values keeps the "absent means keep the local value" guarantee in one
172
+ * place instead of at every call site.
173
+ */
174
+ function assign<K extends keyof RumInitConfiguration>(
175
+ target: RumInitConfiguration,
176
+ key: K,
177
+ value: RumInitConfiguration[K] | undefined
178
+ ) {
179
+ if (value !== undefined) {
180
+ target[key] = value
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Refresh the cache for the next page load. Must be called *after* the SDK has started so it
186
+ * never delays initialization.
187
+ */
188
+ export function scheduleSdkConfigurationRefresh(initConfiguration: RumInitConfiguration) {
189
+ return refreshSdkConfig(initConfiguration, initConfiguration.applicationId)
190
+ }
@@ -0,0 +1,96 @@
1
+ import type { MatchOption } from '@kubesense/kubesense-browser-core'
2
+ import { display, isMatchOption, matchList } from '@kubesense/kubesense-browser-core'
3
+
4
+ /**
5
+ * Route-scoped Session Replay.
6
+ *
7
+ * Recording is normally decided once per session by `sessionReplaySampleRate`. These rules add a
8
+ * second, per-view gate so a session that *is* eligible only gets recorded while the user is on
9
+ * a matching route.
10
+ *
11
+ * Rules can only ever narrow recording, never widen it: a session that lost the replay sampling
12
+ * draw is never recorded, whatever the routes say.
13
+ */
14
+ export interface SessionReplayRoutes {
15
+ /** Record only on these routes. When omitted, every route is eligible. */
16
+ include?: MatchOption[] | undefined
17
+ /** Never record on these routes. Takes precedence over `include`. */
18
+ exclude?: MatchOption[] | undefined
19
+ }
20
+
21
+ /**
22
+ * Rules are matched against the **path** — `pathname` + `search` + `hash` — not the full URL, so
23
+ * a dashboard entry reads the way a route is written (`/checkout`). String options match by
24
+ * prefix, so `/checkout` also covers `/checkout/payment`.
25
+ */
26
+ export function getRoutePath(location: Location | URL = window.location): string {
27
+ return `${location.pathname}${location.search}${location.hash}`
28
+ }
29
+
30
+ /**
31
+ * Returns whether recording is allowed on the given path.
32
+ *
33
+ * No rules at all means "record everywhere", which keeps the behaviour of an SDK that has never
34
+ * been configured with routes.
35
+ */
36
+ export function isRouteRecordable(routes: SessionReplayRoutes | undefined, path: string): boolean {
37
+ if (!routes) {
38
+ return true
39
+ }
40
+
41
+ // Exclusions win, so a route can always be carved out of a broad `include`.
42
+ if (routes.exclude && routes.exclude.length > 0 && matchList(routes.exclude, path, true)) {
43
+ return false
44
+ }
45
+
46
+ if (routes.include && routes.include.length > 0) {
47
+ return matchList(routes.include, path, true)
48
+ }
49
+
50
+ return true
51
+ }
52
+
53
+ /**
54
+ * Validates the user-provided option. An invalid entry is dropped rather than silently treated
55
+ * as "match nothing", which would turn a typo into a total loss of replay.
56
+ */
57
+ export function validateSessionReplayRoutes(routes: unknown): SessionReplayRoutes | undefined {
58
+ if (routes === undefined || routes === null) {
59
+ return undefined
60
+ }
61
+
62
+ if (typeof routes !== 'object' || Array.isArray(routes)) {
63
+ display.error('Session Replay routes should be an object with optional include and exclude arrays')
64
+ return undefined
65
+ }
66
+
67
+ const include = validateMatchOptionList((routes as SessionReplayRoutes).include, 'include')
68
+ const exclude = validateMatchOptionList((routes as SessionReplayRoutes).exclude, 'exclude')
69
+
70
+ if (!include && !exclude) {
71
+ return undefined
72
+ }
73
+
74
+ return { include, exclude }
75
+ }
76
+
77
+ function validateMatchOptionList(list: unknown, name: string): MatchOption[] | undefined {
78
+ if (list === undefined) {
79
+ return undefined
80
+ }
81
+
82
+ if (!Array.isArray(list)) {
83
+ display.error(`Session Replay routes "${name}" should be an array`)
84
+ return undefined
85
+ }
86
+
87
+ const validOptions = list.filter((item) => {
88
+ if (isMatchOption(item)) {
89
+ return true
90
+ }
91
+ display.error(`Session Replay routes "${name}" entries should be a string, RegExp or function`)
92
+ return false
93
+ }) as MatchOption[]
94
+
95
+ return validOptions.length > 0 ? validOptions : undefined
96
+ }
@@ -25,14 +25,14 @@ export function getSessionReplayUrl(
25
25
  parameters.push(`from=${viewContext.startClocks.timeStamp}`)
26
26
  }
27
27
 
28
- const origin = getKubsesenseSiteUrl(configuration)
28
+ const origin = getKubeSenseSiteUrl(configuration)
29
29
  const path = `/rum/replay/sessions/${sessionId}`
30
30
  return `${origin}${path}?${parameters.join('&')}`
31
31
  }
32
32
 
33
- export function getKubsesenseSiteUrl(rumConfiguration: RumConfiguration) {
33
+ export function getKubeSenseSiteUrl(rumConfiguration: RumConfiguration) {
34
34
  const site = rumConfiguration.kubesenseRumEndpoint
35
- const subdomain = rumConfiguration.subdomain
35
+ const subdomain = rumConfiguration.subdomain
36
36
  return `https://${subdomain ? `${subdomain}.` : ''}${site}`
37
37
  }
38
38
 
@@ -1,7 +1,7 @@
1
1
  import type { MatchOption } from '@kubesense/kubesense-browser-core'
2
2
 
3
3
  /**
4
- * kubesense: Kubsesense (x-kubesense-*)
4
+ * kubesense: KubeSense (x-kubesense-*)
5
5
  * tracecontext: W3C Trace Context (traceparent, tracestate)
6
6
  * b3: B3 Single Header (b3)
7
7
  * b3multi: B3 Multiple Headers (X-B3-*)
package/src/index.ts CHANGED
@@ -48,7 +48,9 @@ export type {
48
48
  RumConfiguration,
49
49
  FeatureFlagsForEvents,
50
50
  RemoteConfiguration,
51
+ SessionReplayRoutes,
51
52
  } from './domain/configuration'
53
+ export { isRouteRecordable, getRoutePath } from './domain/configuration'
52
54
  export { DEFAULT_PROGRAMMATIC_ACTION_NAME_ATTRIBUTE } from './domain/action/actionNameConstants'
53
55
  export { STABLE_ATTRIBUTES } from './domain/getSelectorFromElement'
54
56
  export * from './browser/htmlDomUtils'
@@ -1519,6 +1519,10 @@ export interface CommonProperties {
1519
1519
  * Email of the user
1520
1520
  */
1521
1521
  readonly email?: string
1522
+ /**
1523
+ * Mobile phone number of the user
1524
+ */
1525
+ readonly mobile?: string
1522
1526
  /**
1523
1527
  * Identifier of the user across sessions
1524
1528
  */