@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.
- package/README.md +1 -1
- package/cjs/boot/displayAlreadyInitializedError.d.ts +1 -1
- package/cjs/boot/init.d.ts +1 -1
- package/cjs/browser/cookie.d.ts +10 -0
- package/cjs/browser/cookie.js +13 -0
- package/cjs/browser/cookie.js.map +1 -1
- package/cjs/domain/configuration/configuration.js +4 -4
- package/cjs/domain/configuration/configuration.js.map +1 -1
- package/cjs/domain/configuration/endpointBuilder.js +16 -24
- package/cjs/domain/configuration/endpointBuilder.js.map +1 -1
- package/cjs/domain/configuration/transportConfiguration.js +5 -15
- package/cjs/domain/configuration/transportConfiguration.js.map +1 -1
- package/cjs/domain/intakeSites.d.ts +1 -7
- package/cjs/domain/intakeSites.js +2 -8
- package/cjs/domain/intakeSites.js.map +1 -1
- package/cjs/domain/session/oldCookiesMigration.d.ts +1 -1
- package/cjs/domain/session/oldCookiesMigration.js +1 -1
- package/cjs/domain/session/sessionManager.d.ts +1 -0
- package/cjs/domain/session/sessionManager.js +24 -6
- package/cjs/domain/session/sessionManager.js.map +1 -1
- package/cjs/domain/session/sessionState.js +2 -8
- package/cjs/domain/session/sessionState.js.map +1 -1
- package/cjs/domain/session/sessionStore.d.ts +1 -1
- package/cjs/domain/session/sessionStore.js +5 -1
- package/cjs/domain/session/sessionStore.js.map +1 -1
- package/cjs/domain/session/storeStrategies/sessionInCookie.d.ts +5 -1
- package/cjs/domain/session/storeStrategies/sessionInCookie.js +52 -3
- package/cjs/domain/session/storeStrategies/sessionInCookie.js.map +1 -1
- package/cjs/domain/session/storeStrategies/sessionInLocalStorage.d.ts +2 -0
- package/cjs/domain/session/storeStrategies/sessionInLocalStorage.js +1 -0
- package/cjs/domain/session/storeStrategies/sessionInLocalStorage.js.map +1 -1
- package/cjs/domain/session/storeStrategies/sessionStoreStrategy.d.ts +1 -1
- package/cjs/domain/session/storeStrategies/sessionStoreStrategy.js +1 -1
- package/cjs/domain/synthetics/syntheticsWorkerValues.d.ts +6 -6
- package/cjs/domain/synthetics/syntheticsWorkerValues.js +6 -6
- package/cjs/domain/synthetics/syntheticsWorkerValues.js.map +1 -1
- package/cjs/domain/tags.js +6 -5
- package/cjs/domain/tags.js.map +1 -1
- package/cjs/domain/telemetry/telemetry.js +4 -5
- package/cjs/domain/telemetry/telemetry.js.map +1 -1
- package/cjs/domain/telemetry/telemetryEvent.types.d.ts +1 -1
- package/cjs/index.d.ts +1 -1
- package/cjs/index.js +3 -2
- package/cjs/index.js.map +1 -1
- package/cjs/tools/display.js +1 -1
- package/cjs/tools/display.js.map +1 -1
- package/cjs/tools/experimentalFeatures.d.ts +2 -1
- package/cjs/tools/experimentalFeatures.js +1 -0
- package/cjs/tools/experimentalFeatures.js.map +1 -1
- package/cjs/tools/utils/browserDetection.d.ts +2 -0
- package/cjs/tools/utils/browserDetection.js +138 -0
- package/cjs/tools/utils/browserDetection.js.map +1 -1
- package/cjs/tools/utils/stringUtils.d.ts +15 -0
- package/cjs/tools/utils/stringUtils.js +36 -0
- package/cjs/tools/utils/stringUtils.js.map +1 -1
- package/cjs/transport/httpRequest.d.ts +1 -0
- package/cjs/transport/httpRequest.js +26 -4
- package/cjs/transport/httpRequest.js.map +1 -1
- package/cjs/transport/index.d.ts +1 -1
- package/cjs/transport/index.js +2 -1
- package/cjs/transport/index.js.map +1 -1
- package/esm/boot/displayAlreadyInitializedError.d.ts +1 -1
- package/esm/boot/init.d.ts +1 -1
- package/esm/browser/cookie.d.ts +10 -0
- package/esm/browser/cookie.js +13 -1
- package/esm/browser/cookie.js.map +1 -1
- package/esm/domain/configuration/configuration.js +5 -5
- package/esm/domain/configuration/configuration.js.map +1 -1
- package/esm/domain/configuration/endpointBuilder.js +16 -24
- package/esm/domain/configuration/endpointBuilder.js.map +1 -1
- package/esm/domain/configuration/transportConfiguration.js +6 -16
- package/esm/domain/configuration/transportConfiguration.js.map +1 -1
- package/esm/domain/intakeSites.d.ts +1 -7
- package/esm/domain/intakeSites.js +1 -7
- package/esm/domain/intakeSites.js.map +1 -1
- package/esm/domain/session/oldCookiesMigration.d.ts +1 -1
- package/esm/domain/session/oldCookiesMigration.js +1 -1
- package/esm/domain/session/sessionManager.d.ts +1 -0
- package/esm/domain/session/sessionManager.js +25 -7
- package/esm/domain/session/sessionManager.js.map +1 -1
- package/esm/domain/session/sessionState.js +2 -8
- package/esm/domain/session/sessionState.js.map +1 -1
- package/esm/domain/session/sessionStore.d.ts +1 -1
- package/esm/domain/session/sessionStore.js +5 -1
- package/esm/domain/session/sessionStore.js.map +1 -1
- package/esm/domain/session/storeStrategies/sessionInCookie.d.ts +5 -1
- package/esm/domain/session/storeStrategies/sessionInCookie.js +53 -4
- package/esm/domain/session/storeStrategies/sessionInCookie.js.map +1 -1
- package/esm/domain/session/storeStrategies/sessionInLocalStorage.d.ts +2 -0
- package/esm/domain/session/storeStrategies/sessionInLocalStorage.js +1 -1
- package/esm/domain/session/storeStrategies/sessionInLocalStorage.js.map +1 -1
- package/esm/domain/session/storeStrategies/sessionStoreStrategy.d.ts +1 -1
- package/esm/domain/session/storeStrategies/sessionStoreStrategy.js +1 -1
- package/esm/domain/synthetics/syntheticsWorkerValues.d.ts +6 -6
- package/esm/domain/synthetics/syntheticsWorkerValues.js +6 -6
- package/esm/domain/synthetics/syntheticsWorkerValues.js.map +1 -1
- package/esm/domain/tags.js +6 -5
- package/esm/domain/tags.js.map +1 -1
- package/esm/domain/telemetry/telemetry.js +4 -5
- package/esm/domain/telemetry/telemetry.js.map +1 -1
- package/esm/domain/telemetry/telemetryEvent.types.d.ts +1 -1
- package/esm/index.d.ts +1 -1
- package/esm/index.js +1 -1
- package/esm/index.js.map +1 -1
- package/esm/tools/display.js +1 -1
- package/esm/tools/display.js.map +1 -1
- package/esm/tools/experimentalFeatures.d.ts +2 -1
- package/esm/tools/experimentalFeatures.js +1 -0
- package/esm/tools/experimentalFeatures.js.map +1 -1
- package/esm/tools/utils/browserDetection.d.ts +2 -0
- package/esm/tools/utils/browserDetection.js +136 -0
- package/esm/tools/utils/browserDetection.js.map +1 -1
- package/esm/tools/utils/stringUtils.d.ts +15 -0
- package/esm/tools/utils/stringUtils.js +35 -0
- package/esm/tools/utils/stringUtils.js.map +1 -1
- package/esm/transport/httpRequest.d.ts +1 -0
- package/esm/transport/httpRequest.js +25 -4
- package/esm/transport/httpRequest.js.map +1 -1
- package/esm/transport/index.d.ts +1 -1
- package/esm/transport/index.js +1 -1
- package/esm/transport/index.js.map +1 -1
- package/package.json +4 -6
- package/src/boot/displayAlreadyInitializedError.ts +1 -1
- package/src/boot/init.ts +1 -1
- package/src/browser/cookie.ts +19 -1
- package/src/domain/configuration/configuration.ts +4 -4
- package/src/domain/configuration/endpointBuilder.ts +17 -27
- package/src/domain/configuration/transportConfiguration.ts +6 -18
- package/src/domain/intakeSites.ts +2 -17
- package/src/domain/session/oldCookiesMigration.ts +1 -1
- package/src/domain/session/sessionManager.ts +28 -7
- package/src/domain/session/sessionState.ts +2 -7
- package/src/domain/session/sessionStore.ts +6 -2
- package/src/domain/session/storeStrategies/sessionInCookie.ts +66 -4
- package/src/domain/session/storeStrategies/sessionInLocalStorage.ts +1 -1
- package/src/domain/session/storeStrategies/sessionStoreStrategy.ts +1 -1
- package/src/domain/synthetics/syntheticsWorkerValues.ts +9 -9
- package/src/domain/tags.ts +6 -6
- package/src/domain/telemetry/telemetry.ts +5 -5
- package/src/domain/telemetry/telemetryEvent.types.ts +1 -1
- package/src/index.ts +1 -0
- package/src/tools/display.ts +1 -1
- package/src/tools/experimentalFeatures.ts +1 -0
- package/src/tools/utils/browserDetection.ts +146 -0
- package/src/tools/utils/stringUtils.ts +34 -0
- package/src/transport/httpRequest.ts +31 -5
- package/src/transport/index.ts +1 -1
|
@@ -1,17 +1,2 @@
|
|
|
1
|
-
export type Site =
|
|
2
|
-
|
|
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 = '
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
71
|
+
sessionStateString,
|
|
57
72
|
configuration.trackAnonymousUser ? SESSION_COOKIE_EXPIRATION_DELAY : defaultTimeout,
|
|
58
73
|
options
|
|
59
74
|
)
|
|
60
75
|
}
|
|
61
76
|
|
|
62
|
-
|
|
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 = '
|
|
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 = '
|
|
5
|
-
export const SYNTHETICS_RESULT_ID_COOKIE_NAME = '
|
|
6
|
-
export const SYNTHETICS_INJECTS_RUM_COOKIE_NAME = '
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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).
|
|
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).
|
|
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).
|
|
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
|
|
package/src/domain/tags.ts
CHANGED
|
@@ -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
|
-
|
|
41
|
-
|
|
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
|
|
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
|
-
|
|
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[] = [
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
265
|
+
return false // No telemetry replication for custom sites
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
export function addTelemetryDebug(message: string, context?: Context) {
|
package/src/index.ts
CHANGED
package/src/tools/display.ts
CHANGED
|
@@ -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 = '
|
|
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
|