ua-browser 1.4.0-beta.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +2 -0
- package/README.md +2 -0
- package/dist/index.cjs +227 -111
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +65 -1
- package/dist/index.d.ts +65 -1
- package/dist/index.min.js +2 -2
- package/dist/index.min.js.map +1 -1
- package/dist/index.mjs +225 -112
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -182,6 +182,8 @@ import {
|
|
|
182
182
|
detectBot, // standalone bot detection
|
|
183
183
|
detectBrowser, // standalone browser detection
|
|
184
184
|
detectOS, // standalone OS detection
|
|
185
|
+
detectEngine, // standalone rendering engine detection
|
|
186
|
+
detectDevice, // standalone device type detection
|
|
185
187
|
detectArch, // standalone CPU architecture detection
|
|
186
188
|
detectHeadless, // standalone headless browser detection
|
|
187
189
|
satisfies, // condition-matching helper
|
package/README.md
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -4,7 +4,86 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
// package.json
|
|
6
6
|
var package_default = {
|
|
7
|
-
version: "1.4.0
|
|
7
|
+
version: "1.4.0"};
|
|
8
|
+
|
|
9
|
+
// src/constants/os.ts
|
|
10
|
+
var OS_DEFS = [
|
|
11
|
+
{ name: "WebOS", detect: /hpwOS/, versionPattern: /hpwOS\/([\d.]+)/ },
|
|
12
|
+
{ name: "Symbian", detect: /Symbian/, versionPattern: null },
|
|
13
|
+
{ name: "MeeGo", detect: /MeeGo/, versionPattern: null },
|
|
14
|
+
{ name: "BlackBerry", detect: /(BlackBerry|RIM)/, versionPattern: null },
|
|
15
|
+
{ name: "FreeBSD", detect: /FreeBSD/, versionPattern: null },
|
|
16
|
+
{ name: "Debian", detect: /Debian/, versionPattern: /Debian\/([\d.]+)/ },
|
|
17
|
+
{ name: "Ubuntu", detect: /Ubuntu/, versionPattern: null },
|
|
18
|
+
// Linux must come before Chrome OS: Chrome OS UAs contain "X11", so Linux matches first,
|
|
19
|
+
// then Chrome OS overrides it.
|
|
20
|
+
{ name: "Linux", detect: /(Linux|X11)/, versionPattern: null },
|
|
21
|
+
{ name: "Chrome OS", detect: /CrOS/, versionPattern: null },
|
|
22
|
+
{ name: "Tizen", detect: /Tizen/, versionPattern: /Tizen ([\d.]+)/ },
|
|
23
|
+
{ name: "iOS", detect: /like Mac OS X/, versionPattern: /OS ([\d_]+) like/ },
|
|
24
|
+
{
|
|
25
|
+
name: "MacOS",
|
|
26
|
+
detect: /Macintosh/,
|
|
27
|
+
versionPattern: /Mac OS X -?([\d_.]+)/,
|
|
28
|
+
versionNames: {
|
|
29
|
+
"10.9": "Mavericks",
|
|
30
|
+
"10.10": "Yosemite",
|
|
31
|
+
"10.11": "El Capitan",
|
|
32
|
+
"10.12": "Sierra",
|
|
33
|
+
"10.13": "High Sierra",
|
|
34
|
+
"10.14": "Mojave",
|
|
35
|
+
"10.15": "Catalina",
|
|
36
|
+
"11": "Big Sur",
|
|
37
|
+
"12": "Monterey",
|
|
38
|
+
"13": "Ventura",
|
|
39
|
+
"14": "Sonoma",
|
|
40
|
+
"15": "Sequoia"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
// visionOS / tvOS must come AFTER iOS: their UAs also contain "like Mac OS X",
|
|
44
|
+
// so they need to override iOS via the last-match-wins iteration.
|
|
45
|
+
{ name: "visionOS", detect: /visionOS/, versionPattern: /visionOS ([\d_]+)/ },
|
|
46
|
+
{ name: "tvOS", detect: /Apple TV/, versionPattern: /OS ([\d_]+) like/ },
|
|
47
|
+
{ name: "Android", detect: /(Android|Adr)/, versionPattern: /(?:Android|Adr) ([\d.]+)/ },
|
|
48
|
+
// HarmonyOS must come after Android: HarmonyOS UAs include "Android", so Android matches
|
|
49
|
+
// first, then HarmonyOS overrides it. versionPattern tries direct extraction first (5.0+
|
|
50
|
+
// pure HarmonyOS UAs don't have Android token), then falls back to Android version + lookup.
|
|
51
|
+
{
|
|
52
|
+
name: "HarmonyOS",
|
|
53
|
+
detect: /HarmonyOS/,
|
|
54
|
+
versionPattern: [/HarmonyOS[\s/]([\d.]+)/, /Android ([\d.]+)[;)]/],
|
|
55
|
+
versionLookup: { "10": "2", "11": "3", "12": "3", "13": "4" }
|
|
56
|
+
},
|
|
57
|
+
// OpenHarmony (open-source base) must come after HarmonyOS to override any earlier match.
|
|
58
|
+
{ name: "OpenHarmony", detect: /OpenHarmony/, versionPattern: /OpenHarmony[\s/]([\d.]+)/ },
|
|
59
|
+
{ name: "KaiOS", detect: /KAIOS/, versionPattern: /KAIOS\/([\d.]+)/ },
|
|
60
|
+
// Windows must come before Windows Phone: Windows Phone UAs contain "Windows", so Windows
|
|
61
|
+
// matches first, then Windows Phone overrides it.
|
|
62
|
+
{
|
|
63
|
+
name: "Windows",
|
|
64
|
+
detect: /Windows/,
|
|
65
|
+
versionPattern: /Windows NT ([\d.]+)/,
|
|
66
|
+
versionLookup: {
|
|
67
|
+
"10": "10",
|
|
68
|
+
"6.4": "10",
|
|
69
|
+
"6.3": "8.1",
|
|
70
|
+
"6.2": "8",
|
|
71
|
+
"6.1": "7",
|
|
72
|
+
"6.0": "Vista",
|
|
73
|
+
"5.2": "XP",
|
|
74
|
+
"5.1": "XP",
|
|
75
|
+
"5.0": "2000"
|
|
76
|
+
},
|
|
77
|
+
versionNames: {
|
|
78
|
+
"7": "Windows 7",
|
|
79
|
+
"8": "Windows 8",
|
|
80
|
+
"8.1": "Windows 8.1",
|
|
81
|
+
"10": "Windows 10",
|
|
82
|
+
"11": "Windows 11"
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
{ name: "Windows Phone", detect: /(IEMobile|Windows Phone)/, versionPattern: /Windows Phone(?: OS)? ([\d.]+)/ }
|
|
86
|
+
];
|
|
8
87
|
|
|
9
88
|
// src/constants/browsers.ts
|
|
10
89
|
var BROWSER_DEFS = [
|
|
@@ -151,7 +230,7 @@ function detectBrowser(ua) {
|
|
|
151
230
|
}
|
|
152
231
|
}
|
|
153
232
|
}
|
|
154
|
-
if (!best) return { browser: "unknown", version: "unknown" };
|
|
233
|
+
if (!best) return { browser: "unknown", version: "unknown", browserType: "unknown", priority: 0 };
|
|
155
234
|
let version = null;
|
|
156
235
|
if (best.chromeLookup) {
|
|
157
236
|
version = lookupFromChromeVersion(ua, best.chromeLookup);
|
|
@@ -160,7 +239,9 @@ function detectBrowser(ua) {
|
|
|
160
239
|
const patterns = Array.isArray(best.versionPattern) ? best.versionPattern : [best.versionPattern];
|
|
161
240
|
version = extractVersionFromPatterns(ua, patterns);
|
|
162
241
|
}
|
|
163
|
-
|
|
242
|
+
const p = best.priority;
|
|
243
|
+
const browserType = p >= 500 ? "app" : p >= 300 ? "brand" : "browser";
|
|
244
|
+
return { browser: best.name, version: version != null ? version : "unknown", browserType, priority: p };
|
|
164
245
|
}
|
|
165
246
|
|
|
166
247
|
// src/constants/engines.ts
|
|
@@ -176,8 +257,22 @@ var ENGINE_DEFS = [
|
|
|
176
257
|
];
|
|
177
258
|
|
|
178
259
|
// src/detectors/engine.ts
|
|
260
|
+
var ENGINE_VERSION_RE = {
|
|
261
|
+
WebKit: /AppleWebKit\/([\d.]+)/,
|
|
262
|
+
Blink: /AppleWebKit\/([\d.]+)/,
|
|
263
|
+
ArkWeb: /AppleWebKit\/([\d.]+)/,
|
|
264
|
+
Gecko: /Gecko\/([\d.]+)/,
|
|
265
|
+
Trident: /Trident\/([\d.]+)/,
|
|
266
|
+
Presto: /Presto\/([\d.]+)/,
|
|
267
|
+
KHTML: /KHTML\/([\d.]+)/
|
|
268
|
+
};
|
|
179
269
|
function detectEngine(ua, browser, version) {
|
|
180
|
-
var _a, _b;
|
|
270
|
+
var _a, _b, _c, _d;
|
|
271
|
+
if (browser === void 0 || version === void 0) {
|
|
272
|
+
const b = detectBrowser(ua);
|
|
273
|
+
browser = browser != null ? browser : b.browser;
|
|
274
|
+
version = version != null ? version : b.version;
|
|
275
|
+
}
|
|
181
276
|
let engine = "unknown";
|
|
182
277
|
for (const def of ENGINE_DEFS) {
|
|
183
278
|
if (def.detect.test(ua)) {
|
|
@@ -194,64 +289,19 @@ function detectEngine(ua, browser, version) {
|
|
|
194
289
|
if (browser === "Edge") {
|
|
195
290
|
engine = parseInt(version, 10) > 75 ? "Blink" : "EdgeHTML";
|
|
196
291
|
}
|
|
197
|
-
|
|
292
|
+
const engineVersion = ENGINE_VERSION_RE[engine] ? (_d = (_c = ENGINE_VERSION_RE[engine].exec(ua)) == null ? void 0 : _c[1]) != null ? _d : "unknown" : "unknown";
|
|
293
|
+
return { engine, engineVersion };
|
|
198
294
|
}
|
|
199
295
|
|
|
200
|
-
// src/constants/os.ts
|
|
201
|
-
var OS_DEFS = [
|
|
202
|
-
{ name: "WebOS", detect: /hpwOS/, versionPattern: /hpwOS\/([\d.]+)/ },
|
|
203
|
-
{ name: "Symbian", detect: /Symbian/, versionPattern: null },
|
|
204
|
-
{ name: "MeeGo", detect: /MeeGo/, versionPattern: null },
|
|
205
|
-
{ name: "BlackBerry", detect: /(BlackBerry|RIM)/, versionPattern: null },
|
|
206
|
-
{ name: "FreeBSD", detect: /FreeBSD/, versionPattern: null },
|
|
207
|
-
{ name: "Debian", detect: /Debian/, versionPattern: /Debian\/([\d.]+)/ },
|
|
208
|
-
{ name: "Ubuntu", detect: /Ubuntu/, versionPattern: null },
|
|
209
|
-
// Linux must come before Chrome OS: Chrome OS UAs contain "X11", so Linux matches first,
|
|
210
|
-
// then Chrome OS overrides it.
|
|
211
|
-
{ name: "Linux", detect: /(Linux|X11)/, versionPattern: null },
|
|
212
|
-
{ name: "Chrome OS", detect: /CrOS/, versionPattern: null },
|
|
213
|
-
{ name: "Tizen", detect: /Tizen/, versionPattern: /Tizen ([\d.]+)/ },
|
|
214
|
-
{ name: "iOS", detect: /like Mac OS X/, versionPattern: /OS ([\d_]+) like/ },
|
|
215
|
-
{ name: "MacOS", detect: /Macintosh/, versionPattern: /Mac OS X -?([\d_.]+)/ },
|
|
216
|
-
// visionOS / tvOS must come AFTER iOS: their UAs also contain "like Mac OS X",
|
|
217
|
-
// so they need to override iOS via the last-match-wins iteration.
|
|
218
|
-
{ name: "visionOS", detect: /visionOS/, versionPattern: /visionOS ([\d_]+)/ },
|
|
219
|
-
{ name: "tvOS", detect: /Apple TV/, versionPattern: /OS ([\d_]+) like/ },
|
|
220
|
-
{ name: "Android", detect: /(Android|Adr)/, versionPattern: /(?:Android|Adr) ([\d.]+)/ },
|
|
221
|
-
// HarmonyOS must come after Android: HarmonyOS UAs include "Android", so Android matches
|
|
222
|
-
// first, then HarmonyOS overrides it. versionPattern tries direct extraction first (5.0+
|
|
223
|
-
// pure HarmonyOS UAs don't have Android token), then falls back to Android version + lookup.
|
|
224
|
-
{
|
|
225
|
-
name: "HarmonyOS",
|
|
226
|
-
detect: /HarmonyOS/,
|
|
227
|
-
versionPattern: [/HarmonyOS[\s/]([\d.]+)/, /Android ([\d.]+)[;)]/],
|
|
228
|
-
versionLookup: { "10": "2", "11": "3", "12": "3", "13": "4" }
|
|
229
|
-
},
|
|
230
|
-
// OpenHarmony (open-source base) must come after HarmonyOS to override any earlier match.
|
|
231
|
-
{ name: "OpenHarmony", detect: /OpenHarmony/, versionPattern: /OpenHarmony[\s/]([\d.]+)/ },
|
|
232
|
-
{ name: "KaiOS", detect: /KAIOS/, versionPattern: /KAIOS\/([\d.]+)/ },
|
|
233
|
-
// Windows must come before Windows Phone: Windows Phone UAs contain "Windows", so Windows
|
|
234
|
-
// matches first, then Windows Phone overrides it.
|
|
235
|
-
{
|
|
236
|
-
name: "Windows",
|
|
237
|
-
detect: /Windows/,
|
|
238
|
-
versionPattern: /Windows NT ([\d.]+)/,
|
|
239
|
-
versionLookup: {
|
|
240
|
-
"10": "10",
|
|
241
|
-
"6.4": "10",
|
|
242
|
-
"6.3": "8.1",
|
|
243
|
-
"6.2": "8",
|
|
244
|
-
"6.1": "7",
|
|
245
|
-
"6.0": "Vista",
|
|
246
|
-
"5.2": "XP",
|
|
247
|
-
"5.1": "XP",
|
|
248
|
-
"5.0": "2000"
|
|
249
|
-
}
|
|
250
|
-
},
|
|
251
|
-
{ name: "Windows Phone", detect: /(IEMobile|Windows Phone)/, versionPattern: /Windows Phone(?: OS)? ([\d.]+)/ }
|
|
252
|
-
];
|
|
253
|
-
|
|
254
296
|
// src/detectors/os.ts
|
|
297
|
+
function lookupVersionName(map, version) {
|
|
298
|
+
const parts = version.split(".");
|
|
299
|
+
for (let len = parts.length; len >= 1; len--) {
|
|
300
|
+
const key = parts.slice(0, len).join(".");
|
|
301
|
+
if (Object.prototype.hasOwnProperty.call(map, key)) return map[key];
|
|
302
|
+
}
|
|
303
|
+
return "unknown";
|
|
304
|
+
}
|
|
255
305
|
function detectOs(ua, windowsVersion) {
|
|
256
306
|
let matchedDef = null;
|
|
257
307
|
for (const def of OS_DEFS) {
|
|
@@ -259,7 +309,7 @@ function detectOs(ua, windowsVersion) {
|
|
|
259
309
|
matchedDef = def;
|
|
260
310
|
}
|
|
261
311
|
}
|
|
262
|
-
if (!matchedDef) return { os: "unknown", osVersion: "unknown" };
|
|
312
|
+
if (!matchedDef) return { os: "unknown", osVersion: "unknown", osVersionName: "unknown" };
|
|
263
313
|
let osVersion = "unknown";
|
|
264
314
|
if (matchedDef.versionPattern) {
|
|
265
315
|
const raw = Array.isArray(matchedDef.versionPattern) ? extractVersionFromPatterns(ua, matchedDef.versionPattern) : extractVersion(ua, matchedDef.versionPattern);
|
|
@@ -283,7 +333,8 @@ function detectOs(ua, windowsVersion) {
|
|
|
283
333
|
if (matchedDef.name === "Windows" && windowsVersion) {
|
|
284
334
|
osVersion = windowsVersion;
|
|
285
335
|
}
|
|
286
|
-
|
|
336
|
+
const osVersionName = matchedDef.versionNames ? lookupVersionName(matchedDef.versionNames, osVersion) : "unknown";
|
|
337
|
+
return { os: matchedDef.name, osVersion, osVersionName };
|
|
287
338
|
}
|
|
288
339
|
|
|
289
340
|
// src/constants/devices.ts
|
|
@@ -348,57 +399,93 @@ function detectDevice(ua, nav) {
|
|
|
348
399
|
}
|
|
349
400
|
return (_b = (_a = hwDetect()) != null ? _a : uaDetect()) != null ? _b : "PC";
|
|
350
401
|
}
|
|
402
|
+
var VENDOR_MAP = [
|
|
403
|
+
[/^SM-|^GT-|^SGH-|^SCH-|^SHW-|^SPH-|^SAMSUNG /i, "Samsung"],
|
|
404
|
+
[/^Pixel |^Nexus /, "Google"],
|
|
405
|
+
[/^moto |^motorola /i, "Motorola"],
|
|
406
|
+
[/^HONOR /i, "Honor"],
|
|
407
|
+
[/^ELS-|^LYA-|^HMA-|^VOG-|^ANA-|^CLT-|^JNY-|^NOH-|^PLK-|^BAH-|^BKL-|^COL-|^DUB-|^FIG-|^KIW-|^MAR-|^VTR-|^WAS-/i, "Huawei"],
|
|
408
|
+
[/^CPH/, "OPPO"],
|
|
409
|
+
[/^RMX/, "Realme"],
|
|
410
|
+
[/^V\d{4}[A-Z]|^vivo /i, "Vivo"],
|
|
411
|
+
[/^iqoo/i, "Vivo"],
|
|
412
|
+
[/^Redmi |^POCO |^Mi /, "Xiaomi"],
|
|
413
|
+
[/^2\d{9}|^2\d{3}[A-Z]/, "Xiaomi"],
|
|
414
|
+
[/^OnePlus |^IN2|^KB2|^LE2/i, "OnePlus"],
|
|
415
|
+
[/^LM-|^LGE |^LG-/i, "LG"],
|
|
416
|
+
[/^HTC/i, "HTC"],
|
|
417
|
+
[/^Nokia/i, "Nokia"],
|
|
418
|
+
[/^XQ-/, "Sony"],
|
|
419
|
+
[/^ASUS_|^ZB6|^ZS6|^ZE[56]/i, "Asus"],
|
|
420
|
+
[/^Quest /i, "Meta"]
|
|
421
|
+
];
|
|
422
|
+
var ANDROID_MODEL_RE = /Android [0-9.]+;(?:\s*U;)?(?:\s*[a-z]{2}[-_][a-zA-Z]{2,4};)?\s*([^;)]+?)(?:\s+Build\/|[;)])/;
|
|
423
|
+
function detectVendorModel(ua) {
|
|
424
|
+
if (/visionOS/.test(ua)) return { vendor: "Apple", model: "Apple Vision Pro" };
|
|
425
|
+
if (/iPhone/.test(ua)) return { vendor: "Apple", model: "iPhone" };
|
|
426
|
+
if (/iPad/.test(ua)) return { vendor: "Apple", model: "iPad" };
|
|
427
|
+
if (/iPod/.test(ua)) return { vendor: "Apple", model: "iPod touch" };
|
|
428
|
+
const m = ANDROID_MODEL_RE.exec(ua);
|
|
429
|
+
if (m) {
|
|
430
|
+
const model = m[1].trim();
|
|
431
|
+
for (const [re, vendor] of VENDOR_MAP) {
|
|
432
|
+
if (re.test(model)) return { vendor, model };
|
|
433
|
+
}
|
|
434
|
+
return { vendor: "unknown", model };
|
|
435
|
+
}
|
|
436
|
+
return { vendor: "unknown", model: "unknown" };
|
|
437
|
+
}
|
|
351
438
|
|
|
352
439
|
// src/constants/bots.ts
|
|
353
440
|
var BOT_DEFS = [
|
|
354
441
|
// Search engines
|
|
355
|
-
{ name: "Googlebot", detect: /Googlebot
|
|
356
|
-
{ name: "Bingbot", detect: /(bingbot|BingPreview)
|
|
357
|
-
{ name: "Baiduspider", detect: /(Baiduspider|BaiduMobaider)
|
|
358
|
-
{ name: "Bytespider", detect: /Bytespider
|
|
359
|
-
{ name: "YandexBot", detect: /YandexBot
|
|
360
|
-
{ name: "DuckDuckBot", detect: /DuckDuckBot
|
|
361
|
-
{ name: "Slurp", detect: /Slurp
|
|
362
|
-
{ name: "Sogou", detect: /(Sogou|sogou).*[Ss]pider
|
|
363
|
-
{ name: "360Spider", detect: /360Spider
|
|
364
|
-
{ name: "PetalBot", detect: /PetalBot
|
|
365
|
-
{ name: "Applebot-Extended", detect: /Applebot-Extended
|
|
366
|
-
{ name: "Applebot", detect: /Applebot
|
|
442
|
+
{ name: "Googlebot", detect: /Googlebot/, category: "search-engine" },
|
|
443
|
+
{ name: "Bingbot", detect: /(bingbot|BingPreview)/, category: "search-engine" },
|
|
444
|
+
{ name: "Baiduspider", detect: /(Baiduspider|BaiduMobaider)/, category: "search-engine" },
|
|
445
|
+
{ name: "Bytespider", detect: /Bytespider/, category: "search-engine" },
|
|
446
|
+
{ name: "YandexBot", detect: /YandexBot/, category: "search-engine" },
|
|
447
|
+
{ name: "DuckDuckBot", detect: /DuckDuckBot/, category: "search-engine" },
|
|
448
|
+
{ name: "Slurp", detect: /Slurp/, category: "search-engine" },
|
|
449
|
+
{ name: "Sogou", detect: /(Sogou|sogou).*[Ss]pider/, category: "search-engine" },
|
|
450
|
+
{ name: "360Spider", detect: /360Spider/, category: "search-engine" },
|
|
451
|
+
{ name: "PetalBot", detect: /PetalBot/, category: "search-engine" },
|
|
452
|
+
{ name: "Applebot-Extended", detect: /Applebot-Extended/, category: "search-engine" },
|
|
453
|
+
{ name: "Applebot", detect: /Applebot/, category: "search-engine" },
|
|
367
454
|
// Social media crawlers
|
|
368
|
-
{ name: "Facebookbot", detect: /(facebookexternalhit|FacebookBot)
|
|
369
|
-
{ name: "Twitterbot", detect: /Twitterbot
|
|
370
|
-
{ name: "LinkedInBot", detect: /LinkedInBot
|
|
371
|
-
{ name: "PinterestBot", detect: /Pinterest
|
|
455
|
+
{ name: "Facebookbot", detect: /(facebookexternalhit|FacebookBot)/, category: "social" },
|
|
456
|
+
{ name: "Twitterbot", detect: /Twitterbot/, category: "social" },
|
|
457
|
+
{ name: "LinkedInBot", detect: /LinkedInBot/, category: "social" },
|
|
458
|
+
{ name: "PinterestBot", detect: /Pinterest/, category: "social" },
|
|
372
459
|
// Messaging link preview bots
|
|
373
|
-
{ name: "Slackbot", detect: /Slackbot
|
|
374
|
-
{ name: "Discordbot", detect: /Discordbot
|
|
375
|
-
{ name: "TelegramBot", detect: /TelegramBot
|
|
376
|
-
{ name: "WhatsApp", detect: /WhatsApp
|
|
460
|
+
{ name: "Slackbot", detect: /Slackbot/, category: "link-preview" },
|
|
461
|
+
{ name: "Discordbot", detect: /Discordbot/, category: "link-preview" },
|
|
462
|
+
{ name: "TelegramBot", detect: /TelegramBot/, category: "link-preview" },
|
|
463
|
+
{ name: "WhatsApp", detect: /WhatsApp/, category: "link-preview" },
|
|
377
464
|
// SEO tools
|
|
378
|
-
{ name: "SemrushBot", detect: /SemrushBot
|
|
379
|
-
{ name: "AhrefsBot", detect: /AhrefsBot
|
|
380
|
-
{ name: "MJ12bot", detect: /MJ12bot
|
|
381
|
-
{ name: "ScreamingFrog", detect: /Screaming Frog
|
|
382
|
-
{ name: "DataForSeoBot", detect: /DataForSeoBot
|
|
465
|
+
{ name: "SemrushBot", detect: /SemrushBot/, category: "seo-tool" },
|
|
466
|
+
{ name: "AhrefsBot", detect: /AhrefsBot/, category: "seo-tool" },
|
|
467
|
+
{ name: "MJ12bot", detect: /MJ12bot/, category: "seo-tool" },
|
|
468
|
+
{ name: "ScreamingFrog", detect: /Screaming Frog/, category: "seo-tool" },
|
|
469
|
+
{ name: "DataForSeoBot", detect: /DataForSeoBot/, category: "seo-tool" },
|
|
383
470
|
// AI / LLM crawlers
|
|
384
|
-
{ name: "GPTBot", detect: /GPTBot
|
|
385
|
-
{ name: "OAI-SearchBot", detect: /OAI-SearchBot
|
|
386
|
-
{ name: "ChatGPT-User", detect: /ChatGPT-User
|
|
387
|
-
{ name: "ClaudeBot", detect: /ClaudeBot
|
|
388
|
-
{ name: "PerplexityBot", detect: /PerplexityBot
|
|
389
|
-
{ name: "CCBot", detect: /CCBot
|
|
390
|
-
{ name: "AdsBot", detect: /AdsBot-Google
|
|
391
|
-
{ name: "Google-Extended", detect: /Google-Extended
|
|
392
|
-
{ name: "Meta-ExternalAgent", detect: /meta-externalagent/i },
|
|
393
|
-
{ name: "Amazonbot", detect: /Amazonbot
|
|
394
|
-
{ name: "Diffbot", detect: /Diffbot
|
|
395
|
-
{ name: "cohere-ai", detect: /cohere-ai
|
|
396
|
-
{ name: "YouBot", detect: /YouBot
|
|
471
|
+
{ name: "GPTBot", detect: /GPTBot/, category: "ai-llm" },
|
|
472
|
+
{ name: "OAI-SearchBot", detect: /OAI-SearchBot/, category: "ai-llm" },
|
|
473
|
+
{ name: "ChatGPT-User", detect: /ChatGPT-User/, category: "ai-llm" },
|
|
474
|
+
{ name: "ClaudeBot", detect: /ClaudeBot/, category: "ai-llm" },
|
|
475
|
+
{ name: "PerplexityBot", detect: /PerplexityBot/, category: "ai-llm" },
|
|
476
|
+
{ name: "CCBot", detect: /CCBot/, category: "ai-llm" },
|
|
477
|
+
{ name: "AdsBot", detect: /AdsBot-Google/, category: "ai-llm" },
|
|
478
|
+
{ name: "Google-Extended", detect: /Google-Extended/, category: "ai-llm" },
|
|
479
|
+
{ name: "Meta-ExternalAgent", detect: /meta-externalagent/i, category: "ai-llm" },
|
|
480
|
+
{ name: "Amazonbot", detect: /Amazonbot/, category: "ai-llm" },
|
|
481
|
+
{ name: "Diffbot", detect: /Diffbot/, category: "ai-llm" },
|
|
482
|
+
{ name: "cohere-ai", detect: /cohere-ai/, category: "ai-llm" },
|
|
483
|
+
{ name: "YouBot", detect: /YouBot/, category: "ai-llm" },
|
|
397
484
|
// Monitoring / archiving
|
|
398
|
-
{ name: "UptimeRobot", detect: /UptimeRobot
|
|
399
|
-
{ name: "ia_archiver", detect: /ia_archiver
|
|
485
|
+
{ name: "UptimeRobot", detect: /UptimeRobot/, category: "monitoring" },
|
|
486
|
+
{ name: "ia_archiver", detect: /ia_archiver/, category: "monitoring" },
|
|
400
487
|
// Generic catch-all (must be last)
|
|
401
|
-
{ name: "GenericBot", detect: /(bot|crawler|spider|crawling|scraper)/i }
|
|
488
|
+
{ name: "GenericBot", detect: /(bot|crawler|spider|crawling|scraper)/i, category: "generic" }
|
|
402
489
|
];
|
|
403
490
|
|
|
404
491
|
// src/detectors/bot.ts
|
|
@@ -406,10 +493,10 @@ function detectBot(ua, customDefs) {
|
|
|
406
493
|
const defs = customDefs ? [...BOT_DEFS.slice(0, -1), ...customDefs, BOT_DEFS[BOT_DEFS.length - 1]] : BOT_DEFS;
|
|
407
494
|
for (const def of defs) {
|
|
408
495
|
if (def.detect.test(ua)) {
|
|
409
|
-
return { isBot: true, botName: def.name };
|
|
496
|
+
return { isBot: true, botName: def.name, botCategory: def.category };
|
|
410
497
|
}
|
|
411
498
|
}
|
|
412
|
-
return { isBot: false, botName: "unknown" };
|
|
499
|
+
return { isBot: false, botName: "unknown", botCategory: "unknown" };
|
|
413
500
|
}
|
|
414
501
|
|
|
415
502
|
// src/constants/arch.ts
|
|
@@ -553,14 +640,22 @@ function platformFromUA(ua) {
|
|
|
553
640
|
}
|
|
554
641
|
return "unknown";
|
|
555
642
|
}
|
|
643
|
+
function normalizeBCP47(raw) {
|
|
644
|
+
const parts = raw.replace(/_/g, "-").split("-");
|
|
645
|
+
return parts.map((p, i) => {
|
|
646
|
+
if (i === 0) return p.toLowerCase();
|
|
647
|
+
if (p.length === 4) return p[0].toUpperCase() + p.slice(1).toLowerCase();
|
|
648
|
+
return p.toUpperCase();
|
|
649
|
+
}).join("-");
|
|
650
|
+
}
|
|
556
651
|
function languageFromUA(ua) {
|
|
652
|
+
const kwMatch = /\bLanguage\/([a-zA-Z]{2,3}(?:[-_][a-zA-Z]{2,4}){1,2})\b/i.exec(ua);
|
|
653
|
+
if (kwMatch) return normalizeBCP47(kwMatch[1]);
|
|
557
654
|
const re = /[;(]\s*([a-z]{2,3}(?:[-_][A-Za-z]{2,4})+)\s*[;)]/g;
|
|
558
655
|
let m;
|
|
559
656
|
while ((m = re.exec(ua)) !== null) {
|
|
560
657
|
const parts = m[1].replace(/_/g, "-").split("-");
|
|
561
|
-
if (parts.length >= 2)
|
|
562
|
-
return `${parts[0].toLowerCase()}-${parts.slice(1).map((p) => p.toUpperCase()).join("-")}`;
|
|
563
|
-
}
|
|
658
|
+
if (parts.length >= 2) return normalizeBCP47(m[1]);
|
|
564
659
|
}
|
|
565
660
|
return "unknown";
|
|
566
661
|
}
|
|
@@ -568,14 +663,15 @@ function parseUA(ua, options = {}) {
|
|
|
568
663
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
|
|
569
664
|
const effectiveNav = (_a = options.ctx) != null ? _a : options.nav;
|
|
570
665
|
const effectiveWindowsVersion = (_c = (_b = options.ctx) == null ? void 0 : _b.windowsVersion) != null ? _c : options.windowsVersion;
|
|
571
|
-
const { browser: rawBrowser, version: rawVersion } = detectBrowser(ua);
|
|
572
|
-
const { os: rawOs, osVersion: rawOsVersion } = detectOs(ua, effectiveWindowsVersion);
|
|
666
|
+
const { browser: rawBrowser, version: rawVersion, browserType } = detectBrowser(ua);
|
|
667
|
+
const { os: rawOs, osVersion: rawOsVersion, osVersionName } = detectOs(ua, effectiveWindowsVersion);
|
|
573
668
|
let os = rawOs;
|
|
574
669
|
let osVersion = rawOsVersion;
|
|
575
670
|
const device = detectDevice(ua, effectiveNav);
|
|
671
|
+
const { vendor, model } = detectVendorModel(ua);
|
|
576
672
|
const arch = detectArch(ua, (_d = options.ctx) != null ? _d : effectiveNav);
|
|
577
673
|
const nav = effectiveNav;
|
|
578
|
-
const { isBot, botName } = detectBot(ua, options.customBotDefs);
|
|
674
|
+
const { isBot, botName, botCategory } = detectBot(ua, options.customBotDefs);
|
|
579
675
|
const isHeadless = detectHeadless(ua);
|
|
580
676
|
const language = options.language || ((nav == null ? void 0 : nav.language) || (nav == null ? void 0 : nav.browserLanguage) ? getLanguage(nav) : "") || languageFromUA(ua);
|
|
581
677
|
const platform = (nav == null ? void 0 : nav.platform) || platformFromUA(ua);
|
|
@@ -672,22 +768,39 @@ function parseUA(ua, options = {}) {
|
|
|
672
768
|
}
|
|
673
769
|
}
|
|
674
770
|
}
|
|
675
|
-
const engine = detectEngine(ua, browser, version);
|
|
771
|
+
const { engine, engineVersion } = detectEngine(ua, browser, version);
|
|
676
772
|
const versionMajor = parseInt((_l = version.split(".")[0]) != null ? _l : "0", 10) || 0;
|
|
677
773
|
const connectionType = (_p = (_o = (_n = (_m = options.ctx) != null ? _m : options.nav) == null ? void 0 : _n.connection) == null ? void 0 : _o.effectiveType) != null ? _p : "unknown";
|
|
774
|
+
const finalOsVersionName = os === "MacOS" || os === "Windows" ? (() => {
|
|
775
|
+
var _a2;
|
|
776
|
+
const map = (_a2 = OS_DEFS.find((d) => d.name === os)) == null ? void 0 : _a2.versionNames;
|
|
777
|
+
if (!map) return "unknown";
|
|
778
|
+
const parts = osVersion.split(".");
|
|
779
|
+
for (let len = parts.length; len >= 1; len--) {
|
|
780
|
+
const key = parts.slice(0, len).join(".");
|
|
781
|
+
if (Object.prototype.hasOwnProperty.call(map, key)) return map[key];
|
|
782
|
+
}
|
|
783
|
+
return "unknown";
|
|
784
|
+
})() : osVersionName;
|
|
678
785
|
return {
|
|
679
786
|
browser,
|
|
680
787
|
version,
|
|
681
788
|
versionMajor,
|
|
789
|
+
browserType: browser === "Brave" ? "browser" : browserType,
|
|
682
790
|
engine,
|
|
791
|
+
engineVersion,
|
|
683
792
|
os,
|
|
684
793
|
osVersion,
|
|
794
|
+
osVersionName: finalOsVersionName,
|
|
685
795
|
device,
|
|
796
|
+
vendor,
|
|
797
|
+
model,
|
|
686
798
|
arch,
|
|
687
799
|
isWebview: isWebview(ua),
|
|
688
800
|
isHeadless,
|
|
689
801
|
isBot,
|
|
690
802
|
botName,
|
|
803
|
+
botCategory,
|
|
691
804
|
language,
|
|
692
805
|
platform,
|
|
693
806
|
connectionType
|
|
@@ -953,8 +1066,11 @@ exports.default = src_default;
|
|
|
953
1066
|
exports.detectArch = detectArch;
|
|
954
1067
|
exports.detectBot = detectBot;
|
|
955
1068
|
exports.detectBrowser = detectBrowser;
|
|
1069
|
+
exports.detectDevice = detectDevice;
|
|
1070
|
+
exports.detectEngine = detectEngine;
|
|
956
1071
|
exports.detectHeadless = detectHeadless;
|
|
957
1072
|
exports.detectOS = detectOs;
|
|
1073
|
+
exports.detectVendorModel = detectVendorModel;
|
|
958
1074
|
exports.getEnvContext = getEnvContext;
|
|
959
1075
|
exports.getLanguage = getLanguage;
|
|
960
1076
|
exports.getNavContext = getNavContext;
|