@posthog/core 1.29.15 → 1.30.1
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.
|
@@ -1,9 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hints from sources outside the User-Agent string. These let us identify Brave
|
|
3
|
+
* on desktop / Android — Chromium-based with no UA marker, but it exposes
|
|
4
|
+
* `navigator.brave`. (Brave on iOS is detected via its `Brave/X` UA marker —
|
|
5
|
+
* WebKit doesn't expose `navigator.brave` — so no hint is needed there.)
|
|
6
|
+
*/
|
|
7
|
+
export interface BrowserDetectionHints {
|
|
8
|
+
brave?: boolean;
|
|
9
|
+
}
|
|
1
10
|
/**
|
|
2
11
|
* This function detects which browser is running this script.
|
|
3
12
|
* The order of the checks are important since many user agents
|
|
4
13
|
* include keywords used in later checks.
|
|
14
|
+
*
|
|
15
|
+
* `hints` is an optional bag of out-of-band signals (`navigator.brave`) used to
|
|
16
|
+
* detect browsers that intentionally do not identify themselves in the UA
|
|
17
|
+
* string. When omitted, only UA-string detection runs — preserving the previous
|
|
18
|
+
* behaviour.
|
|
5
19
|
*/
|
|
6
|
-
export declare const detectBrowser: (user_agent: string, vendor: string | undefined) => string;
|
|
20
|
+
export declare const detectBrowser: (user_agent: string, vendor: string | undefined, hints?: BrowserDetectionHints) => string;
|
|
7
21
|
/**
|
|
8
22
|
* This function detects which browser version is running this script,
|
|
9
23
|
* parsing major and minor version (e.g., 42.1). User agent strings from:
|
|
@@ -12,7 +26,7 @@ export declare const detectBrowser: (user_agent: string, vendor: string | undefi
|
|
|
12
26
|
* `navigator.vendor` is passed in and used to help with detecting certain browsers
|
|
13
27
|
* NB `navigator.vendor` is deprecated and not present in every browser
|
|
14
28
|
*/
|
|
15
|
-
export declare const detectBrowserVersion: (userAgent: string, vendor: string | undefined) => number | null;
|
|
29
|
+
export declare const detectBrowserVersion: (userAgent: string, vendor: string | undefined, hints?: BrowserDetectionHints) => number | null;
|
|
16
30
|
export declare const detectOS: (user_agent: string) => [string, string];
|
|
17
31
|
export declare const detectDevice: (user_agent: string) => string;
|
|
18
32
|
export declare const detectDeviceType: (user_agent: string, options?: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-agent-utils.d.ts","sourceRoot":"","sources":["../../src/utils/user-agent-utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"user-agent-utils.d.ts","sourceRoot":"","sources":["../../src/utils/user-agent-utils.ts"],"names":[],"mappings":"AA+DA;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IAGpC,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAuCD;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,GACxB,YAAY,MAAM,EAClB,QAAQ,MAAM,GAAG,SAAS,EAC1B,QAAQ,qBAAqB,KAC5B,MAmFF,CAAA;AAkCD;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAC/B,WAAW,MAAM,EACjB,QAAQ,MAAM,GAAG,SAAS,EAC1B,QAAQ,qBAAqB,KAC5B,MAAM,GAAG,IAmBX,CAAA;AA0FD,eAAO,MAAM,QAAQ,GAAa,YAAY,MAAM,KAAG,CAAC,MAAM,EAAE,MAAM,CAUrE,CAAA;AAED,eAAO,MAAM,YAAY,GAAa,YAAY,MAAM,KAAG,MAuD1D,CAAA;AAED,eAAO,MAAM,gBAAgB,GAC3B,YAAY,MAAM,EAClB,UAAU;IACR,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B,KACA,MA4BF,CAAA"}
|
|
@@ -71,8 +71,19 @@ const GENERIC_MOBILE = GENERIC + ' ' + MOBILE.toLowerCase();
|
|
|
71
71
|
const GENERIC_TABLET = GENERIC + ' ' + TABLET.toLowerCase();
|
|
72
72
|
const KONQUEROR = 'Konqueror';
|
|
73
73
|
const OCULUS_BROWSER = 'Oculus Browser';
|
|
74
|
+
const VIVALDI = 'Vivaldi';
|
|
75
|
+
const YANDEX = 'Yandex';
|
|
76
|
+
const WHALE = 'Whale';
|
|
77
|
+
const DUCKDUCKGO = 'DuckDuckGo';
|
|
78
|
+
const PALE_MOON = 'Pale Moon';
|
|
79
|
+
const WATERFOX = 'Waterfox';
|
|
80
|
+
const BRAVE = 'Brave';
|
|
74
81
|
const BROWSER_VERSION_REGEX_SUFFIX = '(\\d+(\\.\\d+)?)';
|
|
75
82
|
const DEFAULT_BROWSER_VERSION_REGEX = new RegExp('Version/' + BROWSER_VERSION_REGEX_SUFFIX);
|
|
83
|
+
function browserFromHints(hints) {
|
|
84
|
+
if (hints?.brave) return BRAVE;
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
76
87
|
const XBOX_REGEX = new RegExp(XBOX, 'i');
|
|
77
88
|
const PLAYSTATION_REGEX = new RegExp(PLAYSTATION + ' \\w+', 'i');
|
|
78
89
|
const NINTENDO_REGEX = new RegExp(NINTENDO + ' \\w+', 'i');
|
|
@@ -94,8 +105,10 @@ function isSafari(userAgent) {
|
|
|
94
105
|
return (0, external_string_utils_js_namespaceObject.includes)(userAgent, SAFARI) && !(0, external_string_utils_js_namespaceObject.includes)(userAgent, CHROME) && !(0, external_string_utils_js_namespaceObject.includes)(userAgent, ANDROID);
|
|
95
106
|
}
|
|
96
107
|
const safariCheck = (ua, vendor)=>vendor && (0, external_string_utils_js_namespaceObject.includes)(vendor, APPLE) || isSafari(ua);
|
|
97
|
-
const detectBrowser = function(user_agent, vendor) {
|
|
108
|
+
const detectBrowser = function(user_agent, vendor, hints) {
|
|
98
109
|
vendor = vendor || '';
|
|
110
|
+
const fromHints = browserFromHints(hints);
|
|
111
|
+
if (fromHints) return fromHints;
|
|
99
112
|
if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, ' OPR/') && (0, external_string_utils_js_namespaceObject.includes)(user_agent, 'Mini')) return OPERA_MINI;
|
|
100
113
|
if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, ' OPR/')) return OPERA;
|
|
101
114
|
if (BLACKBERRY_REGEX.test(user_agent)) return BLACKBERRY;
|
|
@@ -103,6 +116,10 @@ const detectBrowser = function(user_agent, vendor) {
|
|
|
103
116
|
if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, 'OculusBrowser')) return OCULUS_BROWSER;
|
|
104
117
|
else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, SAMSUNG_BROWSER)) return SAMSUNG_INTERNET;
|
|
105
118
|
else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, EDGE) || (0, external_string_utils_js_namespaceObject.includes)(user_agent, 'Edg/')) return MICROSOFT_EDGE;
|
|
119
|
+
else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, VIVALDI + '/')) return VIVALDI;
|
|
120
|
+
else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, 'YaBrowser/')) return YANDEX;
|
|
121
|
+
else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, WHALE + '/')) return WHALE;
|
|
122
|
+
else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, DUCKDUCKGO + '/') || (0, external_string_utils_js_namespaceObject.includes)(user_agent, 'Ddg/')) return DUCKDUCKGO;
|
|
106
123
|
else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, 'FBIOS')) return FACEBOOK + ' ' + MOBILE;
|
|
107
124
|
else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, 'UCWEB') || (0, external_string_utils_js_namespaceObject.includes)(user_agent, 'UCBrowser')) return 'UC Browser';
|
|
108
125
|
else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, 'CriOS')) return CHROME_IOS;
|
|
@@ -111,7 +128,10 @@ const detectBrowser = function(user_agent, vendor) {
|
|
|
111
128
|
else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, ANDROID) && (0, external_string_utils_js_namespaceObject.includes)(user_agent, SAFARI)) return ANDROID_MOBILE;
|
|
112
129
|
else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, 'FxiOS')) return FIREFOX_IOS;
|
|
113
130
|
else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent.toLowerCase(), KONQUEROR.toLowerCase())) return KONQUEROR;
|
|
131
|
+
else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, BRAVE + '/')) return BRAVE;
|
|
114
132
|
else if (safariCheck(user_agent, vendor)) return (0, external_string_utils_js_namespaceObject.includes)(user_agent, MOBILE) ? MOBILE_SAFARI : SAFARI;
|
|
133
|
+
else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, 'PaleMoon/')) return PALE_MOON;
|
|
134
|
+
else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, WATERFOX + '/')) return WATERFOX;
|
|
115
135
|
else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, FIREFOX)) return FIREFOX;
|
|
116
136
|
else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, 'MSIE') || (0, external_string_utils_js_namespaceObject.includes)(user_agent, 'Trident/')) return INTERNET_EXPLORER;
|
|
117
137
|
else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, 'Gecko')) return FIREFOX;
|
|
@@ -164,6 +184,27 @@ const versionRegexes = {
|
|
|
164
184
|
[OCULUS_BROWSER]: [
|
|
165
185
|
new RegExp('OculusBrowser\\/' + BROWSER_VERSION_REGEX_SUFFIX)
|
|
166
186
|
],
|
|
187
|
+
[VIVALDI]: [
|
|
188
|
+
new RegExp(VIVALDI + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)
|
|
189
|
+
],
|
|
190
|
+
[YANDEX]: [
|
|
191
|
+
new RegExp('YaBrowser\\/' + BROWSER_VERSION_REGEX_SUFFIX)
|
|
192
|
+
],
|
|
193
|
+
[WHALE]: [
|
|
194
|
+
new RegExp(WHALE + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)
|
|
195
|
+
],
|
|
196
|
+
[BRAVE]: [
|
|
197
|
+
new RegExp(BRAVE + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)
|
|
198
|
+
],
|
|
199
|
+
[DUCKDUCKGO]: [
|
|
200
|
+
new RegExp('(DuckDuckGo|Ddg)\\/' + BROWSER_VERSION_REGEX_SUFFIX)
|
|
201
|
+
],
|
|
202
|
+
[PALE_MOON]: [
|
|
203
|
+
new RegExp('PaleMoon\\/' + BROWSER_VERSION_REGEX_SUFFIX)
|
|
204
|
+
],
|
|
205
|
+
[WATERFOX]: [
|
|
206
|
+
new RegExp(WATERFOX + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)
|
|
207
|
+
],
|
|
167
208
|
[INTERNET_EXPLORER]: [
|
|
168
209
|
new RegExp('(rv:|MSIE )' + BROWSER_VERSION_REGEX_SUFFIX)
|
|
169
210
|
],
|
|
@@ -171,8 +212,8 @@ const versionRegexes = {
|
|
|
171
212
|
new RegExp('rv:' + BROWSER_VERSION_REGEX_SUFFIX)
|
|
172
213
|
]
|
|
173
214
|
};
|
|
174
|
-
const detectBrowserVersion = function(userAgent, vendor) {
|
|
175
|
-
const browser = detectBrowser(userAgent, vendor);
|
|
215
|
+
const detectBrowserVersion = function(userAgent, vendor, hints) {
|
|
216
|
+
const browser = detectBrowser(userAgent, vendor, hints);
|
|
176
217
|
const regexes = versionRegexes[browser];
|
|
177
218
|
if ((0, external_type_utils_js_namespaceObject.isUndefined)(regexes)) return null;
|
|
178
219
|
for(let i = 0; i < regexes.length; i++){
|
|
@@ -39,8 +39,19 @@ const GENERIC_MOBILE = GENERIC + ' ' + MOBILE.toLowerCase();
|
|
|
39
39
|
const GENERIC_TABLET = GENERIC + ' ' + TABLET.toLowerCase();
|
|
40
40
|
const KONQUEROR = 'Konqueror';
|
|
41
41
|
const OCULUS_BROWSER = 'Oculus Browser';
|
|
42
|
+
const VIVALDI = 'Vivaldi';
|
|
43
|
+
const YANDEX = 'Yandex';
|
|
44
|
+
const WHALE = 'Whale';
|
|
45
|
+
const DUCKDUCKGO = 'DuckDuckGo';
|
|
46
|
+
const PALE_MOON = 'Pale Moon';
|
|
47
|
+
const WATERFOX = 'Waterfox';
|
|
48
|
+
const BRAVE = 'Brave';
|
|
42
49
|
const BROWSER_VERSION_REGEX_SUFFIX = '(\\d+(\\.\\d+)?)';
|
|
43
50
|
const DEFAULT_BROWSER_VERSION_REGEX = new RegExp('Version/' + BROWSER_VERSION_REGEX_SUFFIX);
|
|
51
|
+
function browserFromHints(hints) {
|
|
52
|
+
if (hints?.brave) return BRAVE;
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
44
55
|
const XBOX_REGEX = new RegExp(XBOX, 'i');
|
|
45
56
|
const PLAYSTATION_REGEX = new RegExp(PLAYSTATION + ' \\w+', 'i');
|
|
46
57
|
const NINTENDO_REGEX = new RegExp(NINTENDO + ' \\w+', 'i');
|
|
@@ -62,8 +73,10 @@ function isSafari(userAgent) {
|
|
|
62
73
|
return includes(userAgent, SAFARI) && !includes(userAgent, CHROME) && !includes(userAgent, ANDROID);
|
|
63
74
|
}
|
|
64
75
|
const safariCheck = (ua, vendor)=>vendor && includes(vendor, APPLE) || isSafari(ua);
|
|
65
|
-
const detectBrowser = function(user_agent, vendor) {
|
|
76
|
+
const detectBrowser = function(user_agent, vendor, hints) {
|
|
66
77
|
vendor = vendor || '';
|
|
78
|
+
const fromHints = browserFromHints(hints);
|
|
79
|
+
if (fromHints) return fromHints;
|
|
67
80
|
if (includes(user_agent, ' OPR/') && includes(user_agent, 'Mini')) return OPERA_MINI;
|
|
68
81
|
if (includes(user_agent, ' OPR/')) return OPERA;
|
|
69
82
|
if (BLACKBERRY_REGEX.test(user_agent)) return BLACKBERRY;
|
|
@@ -71,6 +84,10 @@ const detectBrowser = function(user_agent, vendor) {
|
|
|
71
84
|
if (includes(user_agent, 'OculusBrowser')) return OCULUS_BROWSER;
|
|
72
85
|
else if (includes(user_agent, SAMSUNG_BROWSER)) return SAMSUNG_INTERNET;
|
|
73
86
|
else if (includes(user_agent, EDGE) || includes(user_agent, 'Edg/')) return MICROSOFT_EDGE;
|
|
87
|
+
else if (includes(user_agent, VIVALDI + '/')) return VIVALDI;
|
|
88
|
+
else if (includes(user_agent, 'YaBrowser/')) return YANDEX;
|
|
89
|
+
else if (includes(user_agent, WHALE + '/')) return WHALE;
|
|
90
|
+
else if (includes(user_agent, DUCKDUCKGO + '/') || includes(user_agent, 'Ddg/')) return DUCKDUCKGO;
|
|
74
91
|
else if (includes(user_agent, 'FBIOS')) return FACEBOOK + ' ' + MOBILE;
|
|
75
92
|
else if (includes(user_agent, 'UCWEB') || includes(user_agent, 'UCBrowser')) return 'UC Browser';
|
|
76
93
|
else if (includes(user_agent, 'CriOS')) return CHROME_IOS;
|
|
@@ -79,7 +96,10 @@ const detectBrowser = function(user_agent, vendor) {
|
|
|
79
96
|
else if (includes(user_agent, ANDROID) && includes(user_agent, SAFARI)) return ANDROID_MOBILE;
|
|
80
97
|
else if (includes(user_agent, 'FxiOS')) return FIREFOX_IOS;
|
|
81
98
|
else if (includes(user_agent.toLowerCase(), KONQUEROR.toLowerCase())) return KONQUEROR;
|
|
99
|
+
else if (includes(user_agent, BRAVE + '/')) return BRAVE;
|
|
82
100
|
else if (safariCheck(user_agent, vendor)) return includes(user_agent, MOBILE) ? MOBILE_SAFARI : SAFARI;
|
|
101
|
+
else if (includes(user_agent, 'PaleMoon/')) return PALE_MOON;
|
|
102
|
+
else if (includes(user_agent, WATERFOX + '/')) return WATERFOX;
|
|
83
103
|
else if (includes(user_agent, FIREFOX)) return FIREFOX;
|
|
84
104
|
else if (includes(user_agent, 'MSIE') || includes(user_agent, 'Trident/')) return INTERNET_EXPLORER;
|
|
85
105
|
else if (includes(user_agent, 'Gecko')) return FIREFOX;
|
|
@@ -132,6 +152,27 @@ const versionRegexes = {
|
|
|
132
152
|
[OCULUS_BROWSER]: [
|
|
133
153
|
new RegExp('OculusBrowser\\/' + BROWSER_VERSION_REGEX_SUFFIX)
|
|
134
154
|
],
|
|
155
|
+
[VIVALDI]: [
|
|
156
|
+
new RegExp(VIVALDI + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)
|
|
157
|
+
],
|
|
158
|
+
[YANDEX]: [
|
|
159
|
+
new RegExp('YaBrowser\\/' + BROWSER_VERSION_REGEX_SUFFIX)
|
|
160
|
+
],
|
|
161
|
+
[WHALE]: [
|
|
162
|
+
new RegExp(WHALE + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)
|
|
163
|
+
],
|
|
164
|
+
[BRAVE]: [
|
|
165
|
+
new RegExp(BRAVE + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)
|
|
166
|
+
],
|
|
167
|
+
[DUCKDUCKGO]: [
|
|
168
|
+
new RegExp('(DuckDuckGo|Ddg)\\/' + BROWSER_VERSION_REGEX_SUFFIX)
|
|
169
|
+
],
|
|
170
|
+
[PALE_MOON]: [
|
|
171
|
+
new RegExp('PaleMoon\\/' + BROWSER_VERSION_REGEX_SUFFIX)
|
|
172
|
+
],
|
|
173
|
+
[WATERFOX]: [
|
|
174
|
+
new RegExp(WATERFOX + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)
|
|
175
|
+
],
|
|
135
176
|
[INTERNET_EXPLORER]: [
|
|
136
177
|
new RegExp('(rv:|MSIE )' + BROWSER_VERSION_REGEX_SUFFIX)
|
|
137
178
|
],
|
|
@@ -139,8 +180,8 @@ const versionRegexes = {
|
|
|
139
180
|
new RegExp('rv:' + BROWSER_VERSION_REGEX_SUFFIX)
|
|
140
181
|
]
|
|
141
182
|
};
|
|
142
|
-
const detectBrowserVersion = function(userAgent, vendor) {
|
|
143
|
-
const browser = detectBrowser(userAgent, vendor);
|
|
183
|
+
const detectBrowserVersion = function(userAgent, vendor, hints) {
|
|
184
|
+
const browser = detectBrowser(userAgent, vendor, hints);
|
|
144
185
|
const regexes = versionRegexes[browser];
|
|
145
186
|
if (isUndefined(regexes)) return null;
|
|
146
187
|
for(let i = 0; i < regexes.length; i++){
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@posthog/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.30.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
}
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@posthog/types": "1.
|
|
70
|
+
"@posthog/types": "1.378.0"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"@rslib/core": "0.10.6",
|
|
@@ -50,10 +50,36 @@ const GENERIC_MOBILE = GENERIC + ' ' + MOBILE.toLowerCase()
|
|
|
50
50
|
const GENERIC_TABLET = GENERIC + ' ' + TABLET.toLowerCase()
|
|
51
51
|
const KONQUEROR = 'Konqueror'
|
|
52
52
|
const OCULUS_BROWSER = 'Oculus Browser'
|
|
53
|
+
const VIVALDI = 'Vivaldi'
|
|
54
|
+
const YANDEX = 'Yandex'
|
|
55
|
+
const WHALE = 'Whale'
|
|
56
|
+
const DUCKDUCKGO = 'DuckDuckGo'
|
|
57
|
+
const PALE_MOON = 'Pale Moon'
|
|
58
|
+
const WATERFOX = 'Waterfox'
|
|
59
|
+
const BRAVE = 'Brave'
|
|
53
60
|
|
|
54
61
|
const BROWSER_VERSION_REGEX_SUFFIX = '(\\d+(\\.\\d+)?)'
|
|
55
62
|
const DEFAULT_BROWSER_VERSION_REGEX = new RegExp('Version/' + BROWSER_VERSION_REGEX_SUFFIX)
|
|
56
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Hints from sources outside the User-Agent string. These let us identify Brave
|
|
66
|
+
* on desktop / Android — Chromium-based with no UA marker, but it exposes
|
|
67
|
+
* `navigator.brave`. (Brave on iOS is detected via its `Brave/X` UA marker —
|
|
68
|
+
* WebKit doesn't expose `navigator.brave` — so no hint is needed there.)
|
|
69
|
+
*/
|
|
70
|
+
export interface BrowserDetectionHints {
|
|
71
|
+
// Set to `true` when `navigator.brave` exists. This is the sync detection
|
|
72
|
+
// signal Brave recommends. Not available on iOS — see UA fallback below.
|
|
73
|
+
brave?: boolean
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function browserFromHints(hints: BrowserDetectionHints | undefined): string | null {
|
|
77
|
+
if (hints?.brave) {
|
|
78
|
+
return BRAVE
|
|
79
|
+
}
|
|
80
|
+
return null
|
|
81
|
+
}
|
|
82
|
+
|
|
57
83
|
const XBOX_REGEX = new RegExp(XBOX, 'i')
|
|
58
84
|
const PLAYSTATION_REGEX = new RegExp(PLAYSTATION + ' \\w+', 'i')
|
|
59
85
|
const NINTENDO_REGEX = new RegExp(NINTENDO + ' \\w+', 'i')
|
|
@@ -88,10 +114,27 @@ const safariCheck = (ua: string, vendor?: string) => (vendor && includes(vendor,
|
|
|
88
114
|
* This function detects which browser is running this script.
|
|
89
115
|
* The order of the checks are important since many user agents
|
|
90
116
|
* include keywords used in later checks.
|
|
117
|
+
*
|
|
118
|
+
* `hints` is an optional bag of out-of-band signals (`navigator.brave`) used to
|
|
119
|
+
* detect browsers that intentionally do not identify themselves in the UA
|
|
120
|
+
* string. When omitted, only UA-string detection runs — preserving the previous
|
|
121
|
+
* behaviour.
|
|
91
122
|
*/
|
|
92
|
-
export const detectBrowser = function (
|
|
123
|
+
export const detectBrowser = function (
|
|
124
|
+
user_agent: string,
|
|
125
|
+
vendor: string | undefined,
|
|
126
|
+
hints?: BrowserDetectionHints
|
|
127
|
+
): string {
|
|
93
128
|
vendor = vendor || '' // vendor is undefined for at least IE9
|
|
94
129
|
|
|
130
|
+
// Out-of-band signals win over UA sniffing because desktop Brave is
|
|
131
|
+
// deliberately invisible in the UA string and would otherwise be
|
|
132
|
+
// misdetected as Chrome.
|
|
133
|
+
const fromHints = browserFromHints(hints)
|
|
134
|
+
if (fromHints) {
|
|
135
|
+
return fromHints
|
|
136
|
+
}
|
|
137
|
+
|
|
95
138
|
if (includes(user_agent, ' OPR/') && includes(user_agent, 'Mini')) {
|
|
96
139
|
return OPERA_MINI
|
|
97
140
|
} else if (includes(user_agent, ' OPR/')) {
|
|
@@ -113,6 +156,17 @@ export const detectBrowser = function (user_agent: string, vendor: string | unde
|
|
|
113
156
|
return SAMSUNG_INTERNET
|
|
114
157
|
} else if (includes(user_agent, EDGE) || includes(user_agent, 'Edg/')) {
|
|
115
158
|
return MICROSOFT_EDGE
|
|
159
|
+
}
|
|
160
|
+
// Chromium forks that DO stamp themselves into the UA. These must be
|
|
161
|
+
// checked before Chrome because their UA also contains `Chrome/`.
|
|
162
|
+
else if (includes(user_agent, VIVALDI + '/')) {
|
|
163
|
+
return VIVALDI
|
|
164
|
+
} else if (includes(user_agent, 'YaBrowser/')) {
|
|
165
|
+
return YANDEX
|
|
166
|
+
} else if (includes(user_agent, WHALE + '/')) {
|
|
167
|
+
return WHALE
|
|
168
|
+
} else if (includes(user_agent, DUCKDUCKGO + '/') || includes(user_agent, 'Ddg/')) {
|
|
169
|
+
return DUCKDUCKGO
|
|
116
170
|
} else if (includes(user_agent, 'FBIOS')) {
|
|
117
171
|
return FACEBOOK + ' ' + MOBILE
|
|
118
172
|
} else if (includes(user_agent, 'UCWEB') || includes(user_agent, 'UCBrowser')) {
|
|
@@ -129,8 +183,21 @@ export const detectBrowser = function (user_agent: string, vendor: string | unde
|
|
|
129
183
|
return FIREFOX_IOS
|
|
130
184
|
} else if (includes(user_agent.toLowerCase(), KONQUEROR.toLowerCase())) {
|
|
131
185
|
return KONQUEROR
|
|
186
|
+
}
|
|
187
|
+
// Brave on iOS does stamp itself into the UA as `Brave/X` — desktop and
|
|
188
|
+
// Android Brave intentionally do not. Must come before the Safari branch
|
|
189
|
+
// because iOS Brave's UA otherwise looks like Mobile Safari.
|
|
190
|
+
else if (includes(user_agent, BRAVE + '/')) {
|
|
191
|
+
return BRAVE
|
|
132
192
|
} else if (safariCheck(user_agent, vendor)) {
|
|
133
193
|
return includes(user_agent, MOBILE) ? MOBILE_SAFARI : SAFARI
|
|
194
|
+
}
|
|
195
|
+
// Firefox forks that stamp themselves into the UA. Must precede the
|
|
196
|
+
// generic Firefox check because they also include `Firefox/` (or `Gecko`).
|
|
197
|
+
else if (includes(user_agent, 'PaleMoon/')) {
|
|
198
|
+
return PALE_MOON
|
|
199
|
+
} else if (includes(user_agent, WATERFOX + '/')) {
|
|
200
|
+
return WATERFOX
|
|
134
201
|
} else if (includes(user_agent, FIREFOX)) {
|
|
135
202
|
return FIREFOX
|
|
136
203
|
} else if (includes(user_agent, 'MSIE') || includes(user_agent, 'Trident/')) {
|
|
@@ -159,6 +226,17 @@ const versionRegexes: Record<string, RegExp[]> = {
|
|
|
159
226
|
[ANDROID_MOBILE]: [new RegExp('android\\s' + BROWSER_VERSION_REGEX_SUFFIX, 'i')],
|
|
160
227
|
[SAMSUNG_INTERNET]: [new RegExp(SAMSUNG_BROWSER + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)],
|
|
161
228
|
[OCULUS_BROWSER]: [new RegExp('OculusBrowser\\/' + BROWSER_VERSION_REGEX_SUFFIX)],
|
|
229
|
+
[VIVALDI]: [new RegExp(VIVALDI + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)],
|
|
230
|
+
[YANDEX]: [new RegExp('YaBrowser\\/' + BROWSER_VERSION_REGEX_SUFFIX)],
|
|
231
|
+
[WHALE]: [new RegExp(WHALE + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)],
|
|
232
|
+
// Brave on iOS exposes itself as `Brave/X.X` in the UA. Desktop / Android
|
|
233
|
+
// Brave don't, which is why hint-based Brave detection returns a null
|
|
234
|
+
// version: we have no UA marker to parse.
|
|
235
|
+
[BRAVE]: [new RegExp(BRAVE + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)],
|
|
236
|
+
// DuckDuckGo on iOS uses `Ddg/`, on Android/desktop preview it uses `DuckDuckGo/`.
|
|
237
|
+
[DUCKDUCKGO]: [new RegExp('(DuckDuckGo|Ddg)\\/' + BROWSER_VERSION_REGEX_SUFFIX)],
|
|
238
|
+
[PALE_MOON]: [new RegExp('PaleMoon\\/' + BROWSER_VERSION_REGEX_SUFFIX)],
|
|
239
|
+
[WATERFOX]: [new RegExp(WATERFOX + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)],
|
|
162
240
|
[INTERNET_EXPLORER]: [new RegExp('(rv:|MSIE )' + BROWSER_VERSION_REGEX_SUFFIX)],
|
|
163
241
|
Mozilla: [new RegExp('rv:' + BROWSER_VERSION_REGEX_SUFFIX)],
|
|
164
242
|
}
|
|
@@ -171,8 +249,16 @@ const versionRegexes: Record<string, RegExp[]> = {
|
|
|
171
249
|
* `navigator.vendor` is passed in and used to help with detecting certain browsers
|
|
172
250
|
* NB `navigator.vendor` is deprecated and not present in every browser
|
|
173
251
|
*/
|
|
174
|
-
export const detectBrowserVersion = function (
|
|
175
|
-
|
|
252
|
+
export const detectBrowserVersion = function (
|
|
253
|
+
userAgent: string,
|
|
254
|
+
vendor: string | undefined,
|
|
255
|
+
hints?: BrowserDetectionHints
|
|
256
|
+
): number | null {
|
|
257
|
+
const browser = detectBrowser(userAgent, vendor, hints)
|
|
258
|
+
|
|
259
|
+
// Desktop / Android Brave has no parseable UA version, so it returns null
|
|
260
|
+
// below: its `versionRegexes` entry only matches the iOS `Brave/` marker
|
|
261
|
+
// (absent from a desktop Chrome UA).
|
|
176
262
|
const regexes: RegExp[] | undefined = versionRegexes[browser as keyof typeof versionRegexes]
|
|
177
263
|
if (isUndefined(regexes)) {
|
|
178
264
|
return null
|