@pratham.raval/browser-core 6.22.3 → 6.22.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 (147) hide show
  1. package/README.md +1 -1
  2. package/cjs/boot/displayAlreadyInitializedError.d.ts +1 -1
  3. package/cjs/boot/init.d.ts +1 -1
  4. package/cjs/browser/cookie.d.ts +10 -0
  5. package/cjs/browser/cookie.js +13 -0
  6. package/cjs/browser/cookie.js.map +1 -1
  7. package/cjs/domain/configuration/configuration.js +4 -4
  8. package/cjs/domain/configuration/configuration.js.map +1 -1
  9. package/cjs/domain/configuration/endpointBuilder.js +16 -24
  10. package/cjs/domain/configuration/endpointBuilder.js.map +1 -1
  11. package/cjs/domain/configuration/transportConfiguration.js +5 -15
  12. package/cjs/domain/configuration/transportConfiguration.js.map +1 -1
  13. package/cjs/domain/intakeSites.d.ts +1 -7
  14. package/cjs/domain/intakeSites.js +2 -8
  15. package/cjs/domain/intakeSites.js.map +1 -1
  16. package/cjs/domain/session/oldCookiesMigration.d.ts +1 -1
  17. package/cjs/domain/session/oldCookiesMigration.js +1 -1
  18. package/cjs/domain/session/sessionManager.d.ts +1 -0
  19. package/cjs/domain/session/sessionManager.js +24 -6
  20. package/cjs/domain/session/sessionManager.js.map +1 -1
  21. package/cjs/domain/session/sessionState.js +2 -8
  22. package/cjs/domain/session/sessionState.js.map +1 -1
  23. package/cjs/domain/session/sessionStore.d.ts +1 -1
  24. package/cjs/domain/session/sessionStore.js +5 -1
  25. package/cjs/domain/session/sessionStore.js.map +1 -1
  26. package/cjs/domain/session/storeStrategies/sessionInCookie.d.ts +5 -1
  27. package/cjs/domain/session/storeStrategies/sessionInCookie.js +52 -3
  28. package/cjs/domain/session/storeStrategies/sessionInCookie.js.map +1 -1
  29. package/cjs/domain/session/storeStrategies/sessionInLocalStorage.d.ts +2 -0
  30. package/cjs/domain/session/storeStrategies/sessionInLocalStorage.js +1 -0
  31. package/cjs/domain/session/storeStrategies/sessionInLocalStorage.js.map +1 -1
  32. package/cjs/domain/session/storeStrategies/sessionStoreStrategy.d.ts +1 -1
  33. package/cjs/domain/session/storeStrategies/sessionStoreStrategy.js +1 -1
  34. package/cjs/domain/synthetics/syntheticsWorkerValues.d.ts +6 -6
  35. package/cjs/domain/synthetics/syntheticsWorkerValues.js +6 -6
  36. package/cjs/domain/synthetics/syntheticsWorkerValues.js.map +1 -1
  37. package/cjs/domain/tags.js +6 -5
  38. package/cjs/domain/tags.js.map +1 -1
  39. package/cjs/domain/telemetry/telemetry.js +4 -5
  40. package/cjs/domain/telemetry/telemetry.js.map +1 -1
  41. package/cjs/domain/telemetry/telemetryEvent.types.d.ts +1 -1
  42. package/cjs/index.d.ts +1 -1
  43. package/cjs/index.js +3 -2
  44. package/cjs/index.js.map +1 -1
  45. package/cjs/tools/display.js +1 -1
  46. package/cjs/tools/display.js.map +1 -1
  47. package/cjs/tools/experimentalFeatures.d.ts +2 -1
  48. package/cjs/tools/experimentalFeatures.js +1 -0
  49. package/cjs/tools/experimentalFeatures.js.map +1 -1
  50. package/cjs/tools/utils/browserDetection.d.ts +2 -0
  51. package/cjs/tools/utils/browserDetection.js +138 -0
  52. package/cjs/tools/utils/browserDetection.js.map +1 -1
  53. package/cjs/tools/utils/stringUtils.d.ts +15 -0
  54. package/cjs/tools/utils/stringUtils.js +36 -0
  55. package/cjs/tools/utils/stringUtils.js.map +1 -1
  56. package/cjs/transport/httpRequest.d.ts +1 -0
  57. package/cjs/transport/httpRequest.js +26 -4
  58. package/cjs/transport/httpRequest.js.map +1 -1
  59. package/cjs/transport/index.d.ts +1 -1
  60. package/cjs/transport/index.js +2 -1
  61. package/cjs/transport/index.js.map +1 -1
  62. package/esm/boot/displayAlreadyInitializedError.d.ts +1 -1
  63. package/esm/boot/init.d.ts +1 -1
  64. package/esm/browser/cookie.d.ts +10 -0
  65. package/esm/browser/cookie.js +13 -1
  66. package/esm/browser/cookie.js.map +1 -1
  67. package/esm/domain/configuration/configuration.js +5 -5
  68. package/esm/domain/configuration/configuration.js.map +1 -1
  69. package/esm/domain/configuration/endpointBuilder.js +16 -24
  70. package/esm/domain/configuration/endpointBuilder.js.map +1 -1
  71. package/esm/domain/configuration/transportConfiguration.js +6 -16
  72. package/esm/domain/configuration/transportConfiguration.js.map +1 -1
  73. package/esm/domain/intakeSites.d.ts +1 -7
  74. package/esm/domain/intakeSites.js +1 -7
  75. package/esm/domain/intakeSites.js.map +1 -1
  76. package/esm/domain/session/oldCookiesMigration.d.ts +1 -1
  77. package/esm/domain/session/oldCookiesMigration.js +1 -1
  78. package/esm/domain/session/sessionManager.d.ts +1 -0
  79. package/esm/domain/session/sessionManager.js +25 -7
  80. package/esm/domain/session/sessionManager.js.map +1 -1
  81. package/esm/domain/session/sessionState.js +2 -8
  82. package/esm/domain/session/sessionState.js.map +1 -1
  83. package/esm/domain/session/sessionStore.d.ts +1 -1
  84. package/esm/domain/session/sessionStore.js +5 -1
  85. package/esm/domain/session/sessionStore.js.map +1 -1
  86. package/esm/domain/session/storeStrategies/sessionInCookie.d.ts +5 -1
  87. package/esm/domain/session/storeStrategies/sessionInCookie.js +53 -4
  88. package/esm/domain/session/storeStrategies/sessionInCookie.js.map +1 -1
  89. package/esm/domain/session/storeStrategies/sessionInLocalStorage.d.ts +2 -0
  90. package/esm/domain/session/storeStrategies/sessionInLocalStorage.js +1 -1
  91. package/esm/domain/session/storeStrategies/sessionInLocalStorage.js.map +1 -1
  92. package/esm/domain/session/storeStrategies/sessionStoreStrategy.d.ts +1 -1
  93. package/esm/domain/session/storeStrategies/sessionStoreStrategy.js +1 -1
  94. package/esm/domain/synthetics/syntheticsWorkerValues.d.ts +6 -6
  95. package/esm/domain/synthetics/syntheticsWorkerValues.js +6 -6
  96. package/esm/domain/synthetics/syntheticsWorkerValues.js.map +1 -1
  97. package/esm/domain/tags.js +6 -5
  98. package/esm/domain/tags.js.map +1 -1
  99. package/esm/domain/telemetry/telemetry.js +4 -5
  100. package/esm/domain/telemetry/telemetry.js.map +1 -1
  101. package/esm/domain/telemetry/telemetryEvent.types.d.ts +1 -1
  102. package/esm/index.d.ts +1 -1
  103. package/esm/index.js +1 -1
  104. package/esm/index.js.map +1 -1
  105. package/esm/tools/display.js +1 -1
  106. package/esm/tools/display.js.map +1 -1
  107. package/esm/tools/experimentalFeatures.d.ts +2 -1
  108. package/esm/tools/experimentalFeatures.js +1 -0
  109. package/esm/tools/experimentalFeatures.js.map +1 -1
  110. package/esm/tools/utils/browserDetection.d.ts +2 -0
  111. package/esm/tools/utils/browserDetection.js +136 -0
  112. package/esm/tools/utils/browserDetection.js.map +1 -1
  113. package/esm/tools/utils/stringUtils.d.ts +15 -0
  114. package/esm/tools/utils/stringUtils.js +35 -0
  115. package/esm/tools/utils/stringUtils.js.map +1 -1
  116. package/esm/transport/httpRequest.d.ts +1 -0
  117. package/esm/transport/httpRequest.js +25 -4
  118. package/esm/transport/httpRequest.js.map +1 -1
  119. package/esm/transport/index.d.ts +1 -1
  120. package/esm/transport/index.js +1 -1
  121. package/esm/transport/index.js.map +1 -1
  122. package/package.json +4 -6
  123. package/src/boot/displayAlreadyInitializedError.ts +1 -1
  124. package/src/boot/init.ts +1 -1
  125. package/src/browser/cookie.ts +19 -1
  126. package/src/domain/configuration/configuration.ts +4 -4
  127. package/src/domain/configuration/endpointBuilder.ts +17 -27
  128. package/src/domain/configuration/transportConfiguration.ts +6 -18
  129. package/src/domain/intakeSites.ts +2 -17
  130. package/src/domain/session/oldCookiesMigration.ts +1 -1
  131. package/src/domain/session/sessionManager.ts +28 -7
  132. package/src/domain/session/sessionState.ts +2 -7
  133. package/src/domain/session/sessionStore.ts +6 -2
  134. package/src/domain/session/storeStrategies/sessionInCookie.ts +66 -4
  135. package/src/domain/session/storeStrategies/sessionInLocalStorage.ts +1 -1
  136. package/src/domain/session/storeStrategies/sessionStoreStrategy.ts +1 -1
  137. package/src/domain/synthetics/syntheticsWorkerValues.ts +9 -9
  138. package/src/domain/tags.ts +6 -6
  139. package/src/domain/telemetry/telemetry.ts +5 -5
  140. package/src/domain/telemetry/telemetryEvent.types.ts +1 -1
  141. package/src/index.ts +1 -0
  142. package/src/tools/display.ts +1 -1
  143. package/src/tools/experimentalFeatures.ts +1 -0
  144. package/src/tools/utils/browserDetection.ts +146 -0
  145. package/src/tools/utils/stringUtils.ts +34 -0
  146. package/src/transport/httpRequest.ts +31 -5
  147. package/src/transport/index.ts +1 -1
@@ -1,17 +1,2 @@
1
- export type Site =
2
- | 'datadoghq.com'
3
- | 'us3.datadoghq.com'
4
- | 'us5.datadoghq.com'
5
- | 'datadoghq.eu'
6
- | 'ddog-gov.com'
7
- | 'ap1.datadoghq.com'
8
- | 'ap2.datadoghq.com'
9
-
10
- export const INTAKE_SITE_STAGING: Site = 'datad0g.com' as Site
11
- export const INTAKE_SITE_FED_STAGING: Site = 'dd0g-gov.com' as Site
12
- export const INTAKE_SITE_US1: Site = 'datadoghq.com'
13
- export const INTAKE_SITE_EU1: Site = 'datadoghq.eu'
14
- export const INTAKE_SITE_US1_FED: Site = 'ddog-gov.com'
15
-
16
- export const PCI_INTAKE_HOST_US1 = 'pci.browser-intake-datadoghq.com'
17
- export const INTAKE_URL_PARAMETERS = ['ddsource', 'dd-api-key', 'dd-request-id']
1
+ export type Site = string
2
+ export const INTAKE_URL_PARAMETERS = ['mdsource', 'md-api-key', 'md-request-id']
@@ -4,7 +4,7 @@ import { SESSION_STORE_KEY } from './storeStrategies/sessionStoreStrategy'
4
4
  import type { SessionState } from './sessionState'
5
5
  import { expandSessionState, isSessionStarted } from './sessionState'
6
6
 
7
- export const OLD_SESSION_COOKIE_NAME = '_dd'
7
+ export const OLD_SESSION_COOKIE_NAME = '_md'
8
8
  export const OLD_RUM_COOKIE_NAME = '_dd_r'
9
9
  export const OLD_LOGS_COOKIE_NAME = '_dd_l'
10
10
 
@@ -14,12 +14,13 @@ import { getCurrentSite } from '../../browser/cookie'
14
14
  import { ExperimentalFeature, isExperimentalFeatureEnabled } from '../../tools/experimentalFeatures'
15
15
  import { findLast } from '../../tools/utils/polyfills'
16
16
  import { monitorError } from '../../tools/monitor'
17
- import { SESSION_NOT_TRACKED, SESSION_TIME_OUT_DELAY } from './sessionConstants'
17
+ import { SESSION_NOT_TRACKED, SESSION_TIME_OUT_DELAY, SessionPersistence } from './sessionConstants'
18
18
  import { startSessionStore } from './sessionStore'
19
19
  import type { SessionState } from './sessionState'
20
20
  import { toSessionState } from './sessionState'
21
21
  import { retrieveSessionCookie } from './storeStrategies/sessionInCookie'
22
22
  import { SESSION_STORE_KEY } from './storeStrategies/sessionStoreStrategy'
23
+ import { retrieveSessionFromLocalStorage } from './storeStrategies/sessionInLocalStorage'
23
24
 
24
25
  export interface SessionManager<TrackingType extends string> {
25
26
  findSession: (
@@ -38,6 +39,7 @@ export interface SessionContext<TrackingType extends string> extends Context {
38
39
  trackingType: TrackingType
39
40
  isReplayForced: boolean
40
41
  anonymousId: string | undefined
42
+ created: string | undefined
41
43
  }
42
44
 
43
45
  export const VISIBILITY_CHECK_DELAY = ONE_MINUTE
@@ -91,7 +93,7 @@ export function startSessionManager<TrackingType extends string>(
91
93
  if (trackingConsentState.isGranted()) {
92
94
  sessionStore.expandOrRenewSession()
93
95
  } else {
94
- sessionStore.expire()
96
+ sessionStore.expire(false)
95
97
  }
96
98
  })
97
99
 
@@ -107,13 +109,14 @@ export function startSessionManager<TrackingType extends string>(
107
109
  const session = sessionStore.getSession()
108
110
 
109
111
  if (!session) {
110
- reportUnexpectedSessionState().catch(() => void 0) // Ignore errors
112
+ reportUnexpectedSessionState(configuration).catch(() => void 0) // Ignore errors
111
113
 
112
114
  return {
113
115
  id: 'invalid',
114
116
  trackingType: SESSION_NOT_TRACKED as TrackingType,
115
117
  isReplayForced: false,
116
118
  anonymousId: undefined,
119
+ created: undefined,
117
120
  }
118
121
  }
119
122
 
@@ -122,6 +125,7 @@ export function startSessionManager<TrackingType extends string>(
122
125
  trackingType: session[productKey] as TrackingType,
123
126
  isReplayForced: !!session.forcedReplay,
124
127
  anonymousId: session.anonymousId,
128
+ created: session.created,
125
129
  }
126
130
  }
127
131
 
@@ -172,16 +176,33 @@ function trackResume(configuration: Configuration, cb: () => void) {
172
176
  stopCallbacks.push(stop)
173
177
  }
174
178
 
175
- async function reportUnexpectedSessionState() {
176
- const rawSession = retrieveSessionCookie()
179
+ async function reportUnexpectedSessionState(configuration: Configuration) {
180
+ const sessionStoreStrategyType = configuration.sessionStoreStrategyType
181
+ if (!sessionStoreStrategyType) {
182
+ return
183
+ }
184
+
185
+ let rawSession
186
+ let cookieContext
187
+
188
+ if (sessionStoreStrategyType.type === SessionPersistence.COOKIE) {
189
+ rawSession = retrieveSessionCookie(sessionStoreStrategyType.cookieOptions)
190
+
191
+ cookieContext = {
192
+ cookie: await getSessionCookies(),
193
+ currentDomain: `${window.location.protocol}//${window.location.hostname}`,
194
+ }
195
+ } else {
196
+ rawSession = retrieveSessionFromLocalStorage()
197
+ }
177
198
  // monitor-until: forever, could be handy to troubleshoot issues until session manager rework
178
199
  addTelemetryDebug('Unexpected session state', {
200
+ sessionStoreStrategyType: sessionStoreStrategyType.type,
179
201
  session: rawSession,
180
202
  isSyntheticsTest: isSyntheticsTest(),
181
203
  createdTimestamp: rawSession?.created,
182
204
  expireTimestamp: rawSession?.expire,
183
- cookie: await getSessionCookies(),
184
- currentDomain: `${window.location.protocol}//${window.location.hostname}`,
205
+ ...cookieContext,
185
206
  })
186
207
  }
187
208
 
@@ -1,7 +1,6 @@
1
1
  import { isEmptyObject } from '../../tools/utils/objectUtils'
2
2
  import { objectEntries } from '../../tools/utils/polyfills'
3
3
  import { dateNow } from '../../tools/utils/timeUtils'
4
- import { generateUUID } from '../../tools/utils/stringUtils'
5
4
  import type { Configuration } from '../configuration'
6
5
  import { SESSION_EXPIRATION_DELAY, SESSION_TIME_OUT_DELAY } from './sessionConstants'
7
6
  import { isValidSessionString, SESSION_ENTRY_REGEXP, SESSION_ENTRY_SEPARATOR } from './sessionStateValidation'
@@ -23,12 +22,8 @@ export function getExpiredSessionState(
23
22
  const expiredSessionState: SessionState = {
24
23
  isExpired: EXPIRED,
25
24
  }
26
- if (configuration.trackAnonymousUser) {
27
- if (previousSessionState?.anonymousId) {
28
- expiredSessionState.anonymousId = previousSessionState?.anonymousId
29
- } else {
30
- expiredSessionState.anonymousId = generateUUID()
31
- }
25
+ if (configuration.trackAnonymousUser && previousSessionState?.anonymousId) {
26
+ expiredSessionState.anonymousId = previousSessionState?.anonymousId
32
27
  }
33
28
  return expiredSessionState
34
29
  }
@@ -26,7 +26,7 @@ export interface SessionStore {
26
26
  renewObservable: Observable<void>
27
27
  expireObservable: Observable<void>
28
28
  sessionStateUpdateObservable: Observable<{ previousState: SessionState; newState: SessionState }>
29
- expire: () => void
29
+ expire: (hasConsent?: boolean) => void
30
30
  stop: () => void
31
31
  updateSessionState: (state: Partial<SessionState>) => void
32
32
  }
@@ -170,6 +170,7 @@ export function startSessionStore<TrackingType extends string>(
170
170
  {
171
171
  process: (sessionState) => {
172
172
  if (isSessionInNotStartedState(sessionState)) {
173
+ sessionState.anonymousId = generateUUID()
173
174
  return getExpiredSessionState(sessionState, configuration)
174
175
  }
175
176
  },
@@ -231,8 +232,11 @@ export function startSessionStore<TrackingType extends string>(
231
232
  expireObservable,
232
233
  sessionStateUpdateObservable,
233
234
  restartSession: startSession,
234
- expire: () => {
235
+ expire: (hasConsent?: boolean) => {
235
236
  cancelExpandOrRenewSession()
237
+ if (hasConsent === false && sessionCache) {
238
+ delete sessionCache.anonymousId
239
+ }
236
240
  sessionStoreStrategy.expireSession(sessionCache)
237
241
  synchronizeSession(getExpiredSessionState(sessionCache, configuration))
238
242
  },
@@ -1,6 +1,8 @@
1
+ import { isExperimentalFeatureEnabled, ExperimentalFeature } from '../../../tools/experimentalFeatures'
2
+ import { isEmptyObject } from '../../../tools/utils/objectUtils'
1
3
  import { isChromium } from '../../../tools/utils/browserDetection'
2
4
  import type { CookieOptions } from '../../../browser/cookie'
3
- import { getCurrentSite, areCookiesAuthorized, getCookie, setCookie } from '../../../browser/cookie'
5
+ import { getCurrentSite, areCookiesAuthorized, getCookies, setCookie, getCookie } from '../../../browser/cookie'
4
6
  import type { InitConfiguration, Configuration } from '../../configuration'
5
7
  import { tryOldCookiesMigration } from '../oldCookiesMigration'
6
8
  import {
@@ -14,6 +16,8 @@ import { toSessionString, toSessionState, getExpiredSessionState } from '../sess
14
16
  import type { SessionStoreStrategy, SessionStoreStrategyType } from './sessionStoreStrategy'
15
17
  import { SESSION_STORE_KEY } from './sessionStoreStrategy'
16
18
 
19
+ const SESSION_COOKIE_VERSION = 0
20
+
17
21
  export function selectCookieStrategy(initConfiguration: InitConfiguration): SessionStoreStrategyType | undefined {
18
22
  const cookieOptions = buildCookieOptions(initConfiguration)
19
23
  return cookieOptions && areCookiesAuthorized(cookieOptions)
@@ -30,7 +34,7 @@ export function initCookieStrategy(configuration: Configuration, cookieOptions:
30
34
  isLockEnabled: isChromium(),
31
35
  persistSession: (sessionState: SessionState) =>
32
36
  storeSessionCookie(cookieOptions, configuration, sessionState, SESSION_EXPIRATION_DELAY),
33
- retrieveSession: retrieveSessionCookie,
37
+ retrieveSession: () => retrieveSessionCookie(cookieOptions),
34
38
  expireSession: (sessionState: SessionState) =>
35
39
  storeSessionCookie(
36
40
  cookieOptions,
@@ -51,15 +55,34 @@ function storeSessionCookie(
51
55
  sessionState: SessionState,
52
56
  defaultTimeout: number
53
57
  ) {
58
+ let sessionStateString = toSessionString(sessionState)
59
+
60
+ if (isExperimentalFeatureEnabled(ExperimentalFeature.ENCODE_COOKIE_OPTIONS)) {
61
+ sessionStateString = toSessionString({
62
+ ...sessionState,
63
+ // deleting a cookie is writing a new cookie with an empty value
64
+ // we don't want to store the cookie options in this case otherwise the cookie will not be deleted
65
+ ...(!isEmptyObject(sessionState) ? { c: encodeCookieOptions(options) } : {}),
66
+ })
67
+ }
68
+
54
69
  setCookie(
55
70
  SESSION_STORE_KEY,
56
- toSessionString(sessionState),
71
+ sessionStateString,
57
72
  configuration.trackAnonymousUser ? SESSION_COOKIE_EXPIRATION_DELAY : defaultTimeout,
58
73
  options
59
74
  )
60
75
  }
61
76
 
62
- export function retrieveSessionCookie(): SessionState {
77
+ /**
78
+ * Retrieve the session state from the cookie that was set with the same cookie options
79
+ * If there is no match, return the first cookie, because that's how `getCookie()` works
80
+ */
81
+ export function retrieveSessionCookie(cookieOptions: CookieOptions): SessionState {
82
+ if (isExperimentalFeatureEnabled(ExperimentalFeature.ENCODE_COOKIE_OPTIONS)) {
83
+ return retrieveSessionCookieFromEncodedCookie(cookieOptions)
84
+ }
85
+
63
86
  const sessionString = getCookie(SESSION_STORE_KEY)
64
87
  const sessionState = toSessionState(sessionString)
65
88
  return sessionState
@@ -83,3 +106,42 @@ export function buildCookieOptions(initConfiguration: InitConfiguration): Cookie
83
106
 
84
107
  return cookieOptions
85
108
  }
109
+
110
+ function encodeCookieOptions(cookieOptions: CookieOptions): string {
111
+ const domainCount = cookieOptions.domain ? cookieOptions.domain.split('.').length - 1 : 0
112
+
113
+ /* eslint-disable no-bitwise */
114
+ let byte = 0
115
+ byte |= SESSION_COOKIE_VERSION << 5 // Store version in upper 3 bits
116
+ byte |= domainCount << 1 // Store domain count in next 4 bits
117
+ byte |= cookieOptions.crossSite ? 1 : 0 // Store useCrossSiteScripting in next bit
118
+ /* eslint-enable no-bitwise */
119
+
120
+ return byte.toString(16) // Convert to hex string
121
+ }
122
+
123
+ /**
124
+ * Retrieve the session state from the cookie that was set with the same cookie options.
125
+ * If there is no match, fallback to the first cookie, (because that's how `getCookie()` works)
126
+ * and this allows to keep the current session id when we release this feature.
127
+ */
128
+ function retrieveSessionCookieFromEncodedCookie(cookieOptions: CookieOptions): SessionState {
129
+ const cookies = getCookies(SESSION_STORE_KEY)
130
+ const opts = encodeCookieOptions(cookieOptions)
131
+
132
+ let sessionState: SessionState | undefined
133
+
134
+ // reverse the cookies so that if there is no match, the cookie returned is the first one
135
+ for (const cookie of cookies.reverse()) {
136
+ sessionState = toSessionState(cookie)
137
+
138
+ if (sessionState.c === opts) {
139
+ break
140
+ }
141
+ }
142
+
143
+ // remove the cookie options from the session state as this is not part of the session state
144
+ delete sessionState?.c
145
+
146
+ return sessionState ?? {}
147
+ }
@@ -34,7 +34,7 @@ function persistInLocalStorage(sessionState: SessionState) {
34
34
  localStorage.setItem(SESSION_STORE_KEY, toSessionString(sessionState))
35
35
  }
36
36
 
37
- function retrieveSessionFromLocalStorage(): SessionState {
37
+ export function retrieveSessionFromLocalStorage(): SessionState {
38
38
  const sessionString = localStorage.getItem(SESSION_STORE_KEY)
39
39
  return toSessionState(sessionString)
40
40
  }
@@ -2,7 +2,7 @@ import type { CookieOptions } from '../../../browser/cookie'
2
2
  import type { SessionPersistence } from '../sessionConstants'
3
3
  import type { SessionState } from '../sessionState'
4
4
 
5
- export const SESSION_STORE_KEY = '_dd_s'
5
+ export const SESSION_STORE_KEY = '_md_s'
6
6
 
7
7
  export type SessionStoreStrategyType =
8
8
  | { type: typeof SessionPersistence.COOKIE; cookieOptions: CookieOptions }
@@ -1,14 +1,14 @@
1
1
  import { getInitCookie } from '../../browser/cookie'
2
2
  import { globalObject, isWorkerEnvironment } from '../../tools/globalObject'
3
3
 
4
- export const SYNTHETICS_TEST_ID_COOKIE_NAME = 'datadog-synthetics-public-id'
5
- export const SYNTHETICS_RESULT_ID_COOKIE_NAME = 'datadog-synthetics-result-id'
6
- export const SYNTHETICS_INJECTS_RUM_COOKIE_NAME = 'datadog-synthetics-injects-rum'
4
+ export const SYNTHETICS_TEST_ID_COOKIE_NAME = 'motadata-synthetics-public-id'
5
+ export const SYNTHETICS_RESULT_ID_COOKIE_NAME = 'motadata-synthetics-result-id'
6
+ export const SYNTHETICS_INJECTS_RUM_COOKIE_NAME = 'motadata-synthetics-injects-rum'
7
7
 
8
8
  export interface BrowserWindow extends Window {
9
- _DATADOG_SYNTHETICS_PUBLIC_ID?: unknown
10
- _DATADOG_SYNTHETICS_RESULT_ID?: unknown
11
- _DATADOG_SYNTHETICS_INJECTS_RUM?: unknown
9
+ _MOTADATA_SYNTHETICS_PUBLIC_ID?: unknown
10
+ _MOTADATA_SYNTHETICS_RESULT_ID?: unknown
11
+ _MOTADATA_SYNTHETICS_INJECTS_RUM?: unknown
12
12
  }
13
13
 
14
14
  export function willSyntheticsInjectRum(): boolean {
@@ -18,18 +18,18 @@ export function willSyntheticsInjectRum(): boolean {
18
18
  }
19
19
 
20
20
  return Boolean(
21
- (globalObject as BrowserWindow)._DATADOG_SYNTHETICS_INJECTS_RUM || getInitCookie(SYNTHETICS_INJECTS_RUM_COOKIE_NAME)
21
+ (globalObject as BrowserWindow)._MOTADATA_SYNTHETICS_INJECTS_RUM || getInitCookie(SYNTHETICS_INJECTS_RUM_COOKIE_NAME)
22
22
  )
23
23
  }
24
24
 
25
25
  export function getSyntheticsTestId(): string | undefined {
26
- const value = (window as BrowserWindow)._DATADOG_SYNTHETICS_PUBLIC_ID || getInitCookie(SYNTHETICS_TEST_ID_COOKIE_NAME)
26
+ const value = (window as BrowserWindow)._MOTADATA_SYNTHETICS_PUBLIC_ID || getInitCookie(SYNTHETICS_TEST_ID_COOKIE_NAME)
27
27
  return typeof value === 'string' ? value : undefined
28
28
  }
29
29
 
30
30
  export function getSyntheticsResultId(): string | undefined {
31
31
  const value =
32
- (window as BrowserWindow)._DATADOG_SYNTHETICS_RESULT_ID || getInitCookie(SYNTHETICS_RESULT_ID_COOKIE_NAME)
32
+ (window as BrowserWindow)._MOTADATA_SYNTHETICS_RESULT_ID || getInitCookie(SYNTHETICS_RESULT_ID_COOKIE_NAME)
33
33
  return typeof value === 'string' ? value : undefined
34
34
  }
35
35
 
@@ -36,15 +36,15 @@ export function buildTag(key: string, rawValue?: string) {
36
36
  // warning.
37
37
  const tag = rawValue ? `${key}:${rawValue}` : key
38
38
 
39
- if (tag.length > TAG_SIZE_LIMIT || hasForbiddenCharacters(tag)) {
40
- display.warn(
41
- `Tag ${tag} doesn't meet tag requirements and will be sanitized. ${MORE_DETAILS} ${DOCS_ORIGIN}/getting_started/tagging/#defining-tags`
42
- )
43
- }
39
+ // if (tag.length > TAG_SIZE_LIMIT || hasForbiddenCharacters(tag)) {
40
+ // display.warn(
41
+ // `Tag ${tag} doesn't meet tag requirements and will be sanitized. ${MORE_DETAILS} ${DOCS_ORIGIN}/getting_started/tagging/#defining-tags`
42
+ // )
43
+ // }
44
44
 
45
45
  // Let the backend do most of the sanitization, but still make sure multiple tags can't be crafted
46
46
  // by forging a value containing commas.
47
- return sanitizeTag(tag)
47
+ return tag
48
48
  }
49
49
 
50
50
  export function sanitizeTag(tag: string) {
@@ -5,7 +5,7 @@ import { toStackTraceString } from '../../tools/stackTrace/handlingStack'
5
5
  import { getExperimentalFeatures } from '../../tools/experimentalFeatures'
6
6
  import type { Configuration } from '../configuration'
7
7
  import { buildTags } from '../tags'
8
- import { INTAKE_SITE_STAGING, INTAKE_SITE_US1_FED } from '../intakeSites'
8
+
9
9
  import { BufferedObservable, Observable } from '../../tools/observable'
10
10
  import { clocksNow } from '../../tools/utils/timeUtils'
11
11
  import { displayIfDebugEnabled, startMonitorErrorCollection } from '../../tools/monitor'
@@ -74,7 +74,7 @@ export const enum TelemetryMetrics {
74
74
 
75
75
  const METRIC_SAMPLE_RATE = 1
76
76
 
77
- const TELEMETRY_EXCLUDED_SITES: string[] = [INTAKE_SITE_US1_FED]
77
+ const TELEMETRY_EXCLUDED_SITES: string[] = []
78
78
 
79
79
  let telemetryObservable: BufferedObservable<{ rawEvent: RawTelemetryEvent; metricName?: string }> | undefined
80
80
 
@@ -188,7 +188,7 @@ export function startTelemetryCollection(
188
188
  service: telemetryService,
189
189
  version: __BUILD_ENV__SDK_VERSION__,
190
190
  source: 'browser' as const,
191
- _dd: {
191
+ _md: {
192
192
  format_version: 2 as const,
193
193
  },
194
194
  telemetry: combine(rawEvent, {
@@ -196,7 +196,7 @@ export function startTelemetryCollection(
196
196
  connectivity: getConnectivity(),
197
197
  sdk_setup: __BUILD_ENV__SDK_SETUP__,
198
198
  }) as TelemetryEvent['telemetry'],
199
- ddtags: buildTags(configuration).join(','),
199
+ mdtags: buildTags(configuration).join(','),
200
200
  experimental_features: Array.from(getExperimentalFeatures()),
201
201
  }
202
202
 
@@ -262,7 +262,7 @@ export function resetTelemetry() {
262
262
  * but keep replicating staging events for reliability
263
263
  */
264
264
  function isTelemetryReplicationAllowed(configuration: Configuration) {
265
- return configuration.site === INTAKE_SITE_STAGING
265
+ return false // No telemetry replication for custom sites
266
266
  }
267
267
 
268
268
  export function addTelemetryDebug(message: string, context?: Context) {
@@ -540,7 +540,7 @@ export interface CommonTelemetryProperties {
540
540
  /**
541
541
  * Internal properties
542
542
  */
543
- _dd: {
543
+ _md: {
544
544
  /**
545
545
  * Version of the RUM event format
546
546
  */
package/src/index.ts CHANGED
@@ -68,6 +68,7 @@ export {
68
68
  BridgeCapability,
69
69
  createBatch,
70
70
  createFlushController,
71
+ setCurrentSite
71
72
  } from './transport'
72
73
  export * from './tools/display'
73
74
  export type { Encoder, EncoderResult } from './tools/encoder'
@@ -41,7 +41,7 @@ Object.keys(ConsoleApiName).forEach((name) => {
41
41
  originalConsoleMethods[name as ConsoleApiName] = globalConsole[name as ConsoleApiName]
42
42
  })
43
43
 
44
- const PREFIX = 'Datadog Browser SDK:'
44
+ const PREFIX = 'Motadata Browser SDK:'
45
45
 
46
46
  export const display: Display = {
47
47
  debug: originalConsoleMethods.debug.bind(globalConsole, PREFIX),
@@ -18,6 +18,7 @@ export enum ExperimentalFeature {
18
18
  USE_TREE_WALKER_FOR_ACTION_NAME = 'use_tree_walker_for_action_name',
19
19
  FEATURE_OPERATION_VITAL = 'feature_operation_vital',
20
20
  SHORT_SESSION_INVESTIGATION = 'short_session_investigation',
21
+ ENCODE_COOKIE_OPTIONS = 'encode_cookie_options',
21
22
  }
22
23
 
23
24
  const enabledExperimentalFeatures: Set<ExperimentalFeature> = new Set()
@@ -1,3 +1,126 @@
1
+ // // Exported only for tests
2
+ // export const enum Browser {
3
+ // CHROMIUM,
4
+ // SAFARI,
5
+ // OTHER,
6
+ // }
7
+ //
8
+ // export function isChromium() {
9
+ // return detectBrowserCached() === Browser.CHROMIUM
10
+ // }
11
+ //
12
+ // export function isSafari() {
13
+ // return detectBrowserCached() === Browser.SAFARI
14
+ // }
15
+ //
16
+ // let browserCache: Browser | undefined
17
+ // function detectBrowserCached() {
18
+ // return browserCache ?? (browserCache = detectBrowser())
19
+ // }
20
+ //
21
+ // // Exported only for tests
22
+ // export function detectBrowser(browserWindow: Window = window) {
23
+ // const userAgent = browserWindow.navigator.userAgent
24
+ // if ((browserWindow as any).chrome || /HeadlessChrome/.test(userAgent)) {
25
+ // return Browser.CHROMIUM
26
+ // }
27
+ //
28
+ // if (
29
+ // // navigator.vendor is deprecated, but it is the most resilient way we found to detect
30
+ // // "Apple maintained browsers" (AKA Safari). If one day it gets removed, we still have the
31
+ // // useragent test as a semi-working fallback.
32
+ // browserWindow.navigator.vendor?.indexOf('Apple') === 0 ||
33
+ // (/safari/i.test(userAgent) && !/chrome|android/i.test(userAgent))
34
+ // ) {
35
+ // return Browser.SAFARI
36
+ // }
37
+ //
38
+ // return Browser.OTHER
39
+ // }
40
+ //
41
+ // /**
42
+ // * Get detailed browser name for X-Browser-Name header
43
+ // * Returns one of: Chrome, Firefox, Edge, Opera, Safari, other
44
+ // */
45
+ // let detailedBrowserNameCache: string | undefined
46
+ // export function getDetailedBrowserName(browserWindow: Window = window): string {
47
+ // return detailedBrowserNameCache ?? (detailedBrowserNameCache = detectDetailedBrowserName(browserWindow))
48
+ // }
49
+ //
50
+ // // Exported only for tests
51
+ // export function detectDetailedBrowserName(browserWindow: Window = window): string {
52
+ // // Try Client Hints API first for more accurate detection
53
+ // const clientHintsBrowser = parseClientHints(browserWindow)
54
+ // if (clientHintsBrowser) {
55
+ // return clientHintsBrowser
56
+ // }
57
+ //
58
+ // // Fallback to user agent parsing
59
+ // return parseUserAgent(browserWindow)
60
+ // }
61
+ //
62
+ // function parseClientHints(browserWindow: Window = window): string {
63
+ //
64
+ // // fallback to userAgent
65
+ // const ua = browserWindow.navigator.userAgent
66
+ //
67
+ // if (/edg/i.test(ua)) {
68
+ // return 'Edge';
69
+ // }
70
+ // if (/opr|opera/i.test(ua)) {return 'Opera'}
71
+ // if (/chrome/i.test(ua)) {return 'Chrome'}
72
+ // if (/safari/i.test(ua)) {return 'Safari'}
73
+ // if (/firefox/i.test(ua)) {return 'Firefox'}
74
+ //
75
+ // return 'other'
76
+ // }
77
+ //
78
+ // function parseUserAgent(browserWindow: Window = window): string {
79
+ // const userAgent = browserWindow.navigator.userAgent
80
+ //
81
+ // // Firefox detection
82
+ // if (/Firefox\//i.test(userAgent)) {
83
+ // return 'Firefox'
84
+ // }
85
+ //
86
+ // // Safari detection (must come before Chrome check)
87
+ // if (
88
+ // (browserWindow.navigator.vendor?.indexOf('Apple') === 0 ||
89
+ // (/safari/i.test(userAgent) && !/chrome|android/i.test(userAgent)))
90
+ // ) {
91
+ // return 'Safari'
92
+ // }
93
+ //
94
+ // // Chrome-based browsers detection
95
+ // if (/Chrome\//i.test(userAgent) || (browserWindow as any).chrome || /HeadlessChrome/.test(userAgent)) {
96
+ // // Edge detection
97
+ // if (/Edg\//i.test(userAgent)) {
98
+ // return 'Edge'
99
+ // }
100
+ //
101
+ // // Opera detection
102
+ // if (/OPR\//i.test(userAgent) || /Opera\//i.test(userAgent)) {
103
+ // return 'Opera'
104
+ // }
105
+ //
106
+ // // Brave detection (fallback - Brave shows as Chrome in user agent)
107
+ // // Note: This is less reliable than Client Hints, but better than nothing
108
+ // if (/Brave\//i.test(userAgent)) {
109
+ // return 'other'
110
+ // }
111
+ //
112
+ // // Default Chrome detection
113
+ // return 'Chrome'
114
+ // }
115
+ //
116
+ // // All other browsers
117
+ // return 'other'
118
+ // }
119
+
120
+
121
+ // eslint-disable-next-line import/no-extraneous-dependencies, local-rules/disallow-side-effects, local-rules/enforce-prod-deps-imports
122
+ import { UAParser } from 'ua-parser-js';
123
+
1
124
  // Exported only for tests
2
125
  export const enum Browser {
3
126
  CHROMIUM,
@@ -37,3 +160,26 @@ export function detectBrowser(browserWindow: Window = window) {
37
160
 
38
161
  return Browser.OTHER
39
162
  }
163
+
164
+ /**
165
+ * Get detailed browser name for X-Browser-Name header
166
+ * Uses ua-parser-js for accurate browser detection
167
+ */
168
+ let detailedBrowserNameCache: string | undefined
169
+
170
+ export function getDetailedBrowserName(browserWindow: Window = window): string {
171
+ return (
172
+ detailedBrowserNameCache ??
173
+ (detailedBrowserNameCache = detectDetailedBrowserName(browserWindow))
174
+ )
175
+ }
176
+
177
+ export function detectDetailedBrowserName(browserWindow: Window = window): string {
178
+ const ua = browserWindow.navigator.userAgent
179
+ const parser = new UAParser(ua)
180
+ const result = parser.getResult()
181
+
182
+ // Return the browser name from ua-parser-js
183
+ // If browser name is undefined or empty, return 'Unknown Browser'
184
+ return result.browser.name || 'Unknown Browser'
185
+ }
@@ -11,6 +11,10 @@ export function generateUUID(placeholder?: string): string {
11
11
 
12
12
  const COMMA_SEPARATED_KEY_VALUE = /([\w-]+)\s*=\s*([^;]+)/g
13
13
 
14
+ /**
15
+ * Returns the value of the key with the given name
16
+ * If there are multiple values with the same key, returns the first one
17
+ */
14
18
  export function findCommaSeparatedValue(rawString: string, name: string): string | undefined {
15
19
  COMMA_SEPARATED_KEY_VALUE.lastIndex = 0
16
20
  while (true) {
@@ -25,6 +29,36 @@ export function findCommaSeparatedValue(rawString: string, name: string): string
25
29
  }
26
30
  }
27
31
 
32
+ /**
33
+ * Returns a map of all the values with the given key
34
+ * If there are multiple values with the same key, returns all the values
35
+ */
36
+ export function findAllCommaSeparatedValues(rawString: string): Map<string, string[]> {
37
+ const result = new Map<string, string[]>()
38
+ COMMA_SEPARATED_KEY_VALUE.lastIndex = 0
39
+ while (true) {
40
+ const match = COMMA_SEPARATED_KEY_VALUE.exec(rawString)
41
+ if (match) {
42
+ const key = match[1]
43
+ const value = match[2]
44
+ if (result.has(key)) {
45
+ result.get(key)!.push(value)
46
+ } else {
47
+ result.set(key, [value])
48
+ }
49
+ } else {
50
+ break
51
+ }
52
+ }
53
+ return result
54
+ }
55
+
56
+ /**
57
+ * Returns a map of the values with the given key
58
+ * ⚠️ If there are multiple values with the same key, returns the LAST one
59
+ *
60
+ * @deprecated use `findAllCommaSeparatedValues()` instead
61
+ */
28
62
  export function findCommaSeparatedValues(rawString: string): Map<string, string> {
29
63
  const result = new Map<string, string>()
30
64
  COMMA_SEPARATED_KEY_VALUE.lastIndex = 0