ua-browser 1.4.0-beta.1 → 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/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-beta.1"};
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
- return { browser: best.name, version: version != null ? version : "unknown" };
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,17 @@ 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;
181
271
  if (browser === void 0 || version === void 0) {
182
272
  const b = detectBrowser(ua);
183
273
  browser = browser != null ? browser : b.browser;
@@ -199,64 +289,19 @@ function detectEngine(ua, browser, version) {
199
289
  if (browser === "Edge") {
200
290
  engine = parseInt(version, 10) > 75 ? "Blink" : "EdgeHTML";
201
291
  }
202
- return engine;
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 };
203
294
  }
204
295
 
205
- // src/constants/os.ts
206
- var OS_DEFS = [
207
- { name: "WebOS", detect: /hpwOS/, versionPattern: /hpwOS\/([\d.]+)/ },
208
- { name: "Symbian", detect: /Symbian/, versionPattern: null },
209
- { name: "MeeGo", detect: /MeeGo/, versionPattern: null },
210
- { name: "BlackBerry", detect: /(BlackBerry|RIM)/, versionPattern: null },
211
- { name: "FreeBSD", detect: /FreeBSD/, versionPattern: null },
212
- { name: "Debian", detect: /Debian/, versionPattern: /Debian\/([\d.]+)/ },
213
- { name: "Ubuntu", detect: /Ubuntu/, versionPattern: null },
214
- // Linux must come before Chrome OS: Chrome OS UAs contain "X11", so Linux matches first,
215
- // then Chrome OS overrides it.
216
- { name: "Linux", detect: /(Linux|X11)/, versionPattern: null },
217
- { name: "Chrome OS", detect: /CrOS/, versionPattern: null },
218
- { name: "Tizen", detect: /Tizen/, versionPattern: /Tizen ([\d.]+)/ },
219
- { name: "iOS", detect: /like Mac OS X/, versionPattern: /OS ([\d_]+) like/ },
220
- { name: "MacOS", detect: /Macintosh/, versionPattern: /Mac OS X -?([\d_.]+)/ },
221
- // visionOS / tvOS must come AFTER iOS: their UAs also contain "like Mac OS X",
222
- // so they need to override iOS via the last-match-wins iteration.
223
- { name: "visionOS", detect: /visionOS/, versionPattern: /visionOS ([\d_]+)/ },
224
- { name: "tvOS", detect: /Apple TV/, versionPattern: /OS ([\d_]+) like/ },
225
- { name: "Android", detect: /(Android|Adr)/, versionPattern: /(?:Android|Adr) ([\d.]+)/ },
226
- // HarmonyOS must come after Android: HarmonyOS UAs include "Android", so Android matches
227
- // first, then HarmonyOS overrides it. versionPattern tries direct extraction first (5.0+
228
- // pure HarmonyOS UAs don't have Android token), then falls back to Android version + lookup.
229
- {
230
- name: "HarmonyOS",
231
- detect: /HarmonyOS/,
232
- versionPattern: [/HarmonyOS[\s/]([\d.]+)/, /Android ([\d.]+)[;)]/],
233
- versionLookup: { "10": "2", "11": "3", "12": "3", "13": "4" }
234
- },
235
- // OpenHarmony (open-source base) must come after HarmonyOS to override any earlier match.
236
- { name: "OpenHarmony", detect: /OpenHarmony/, versionPattern: /OpenHarmony[\s/]([\d.]+)/ },
237
- { name: "KaiOS", detect: /KAIOS/, versionPattern: /KAIOS\/([\d.]+)/ },
238
- // Windows must come before Windows Phone: Windows Phone UAs contain "Windows", so Windows
239
- // matches first, then Windows Phone overrides it.
240
- {
241
- name: "Windows",
242
- detect: /Windows/,
243
- versionPattern: /Windows NT ([\d.]+)/,
244
- versionLookup: {
245
- "10": "10",
246
- "6.4": "10",
247
- "6.3": "8.1",
248
- "6.2": "8",
249
- "6.1": "7",
250
- "6.0": "Vista",
251
- "5.2": "XP",
252
- "5.1": "XP",
253
- "5.0": "2000"
254
- }
255
- },
256
- { name: "Windows Phone", detect: /(IEMobile|Windows Phone)/, versionPattern: /Windows Phone(?: OS)? ([\d.]+)/ }
257
- ];
258
-
259
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
+ }
260
305
  function detectOs(ua, windowsVersion) {
261
306
  let matchedDef = null;
262
307
  for (const def of OS_DEFS) {
@@ -264,7 +309,7 @@ function detectOs(ua, windowsVersion) {
264
309
  matchedDef = def;
265
310
  }
266
311
  }
267
- if (!matchedDef) return { os: "unknown", osVersion: "unknown" };
312
+ if (!matchedDef) return { os: "unknown", osVersion: "unknown", osVersionName: "unknown" };
268
313
  let osVersion = "unknown";
269
314
  if (matchedDef.versionPattern) {
270
315
  const raw = Array.isArray(matchedDef.versionPattern) ? extractVersionFromPatterns(ua, matchedDef.versionPattern) : extractVersion(ua, matchedDef.versionPattern);
@@ -288,7 +333,8 @@ function detectOs(ua, windowsVersion) {
288
333
  if (matchedDef.name === "Windows" && windowsVersion) {
289
334
  osVersion = windowsVersion;
290
335
  }
291
- return { os: matchedDef.name, osVersion };
336
+ const osVersionName = matchedDef.versionNames ? lookupVersionName(matchedDef.versionNames, osVersion) : "unknown";
337
+ return { os: matchedDef.name, osVersion, osVersionName };
292
338
  }
293
339
 
294
340
  // src/constants/devices.ts
@@ -353,57 +399,93 @@ function detectDevice(ua, nav) {
353
399
  }
354
400
  return (_b = (_a = hwDetect()) != null ? _a : uaDetect()) != null ? _b : "PC";
355
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
+ }
356
438
 
357
439
  // src/constants/bots.ts
358
440
  var BOT_DEFS = [
359
441
  // Search engines
360
- { name: "Googlebot", detect: /Googlebot/ },
361
- { name: "Bingbot", detect: /(bingbot|BingPreview)/ },
362
- { name: "Baiduspider", detect: /(Baiduspider|BaiduMobaider)/ },
363
- { name: "Bytespider", detect: /Bytespider/ },
364
- { name: "YandexBot", detect: /YandexBot/ },
365
- { name: "DuckDuckBot", detect: /DuckDuckBot/ },
366
- { name: "Slurp", detect: /Slurp/ },
367
- { name: "Sogou", detect: /(Sogou|sogou).*[Ss]pider/ },
368
- { name: "360Spider", detect: /360Spider/ },
369
- { name: "PetalBot", detect: /PetalBot/ },
370
- { name: "Applebot-Extended", detect: /Applebot-Extended/ },
371
- { 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" },
372
454
  // Social media crawlers
373
- { name: "Facebookbot", detect: /(facebookexternalhit|FacebookBot)/ },
374
- { name: "Twitterbot", detect: /Twitterbot/ },
375
- { name: "LinkedInBot", detect: /LinkedInBot/ },
376
- { 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" },
377
459
  // Messaging link preview bots
378
- { name: "Slackbot", detect: /Slackbot/ },
379
- { name: "Discordbot", detect: /Discordbot/ },
380
- { name: "TelegramBot", detect: /TelegramBot/ },
381
- { 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" },
382
464
  // SEO tools
383
- { name: "SemrushBot", detect: /SemrushBot/ },
384
- { name: "AhrefsBot", detect: /AhrefsBot/ },
385
- { name: "MJ12bot", detect: /MJ12bot/ },
386
- { name: "ScreamingFrog", detect: /Screaming Frog/ },
387
- { 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" },
388
470
  // AI / LLM crawlers
389
- { name: "GPTBot", detect: /GPTBot/ },
390
- { name: "OAI-SearchBot", detect: /OAI-SearchBot/ },
391
- { name: "ChatGPT-User", detect: /ChatGPT-User/ },
392
- { name: "ClaudeBot", detect: /ClaudeBot/ },
393
- { name: "PerplexityBot", detect: /PerplexityBot/ },
394
- { name: "CCBot", detect: /CCBot/ },
395
- { name: "AdsBot", detect: /AdsBot-Google/ },
396
- { name: "Google-Extended", detect: /Google-Extended/ },
397
- { name: "Meta-ExternalAgent", detect: /meta-externalagent/i },
398
- { name: "Amazonbot", detect: /Amazonbot/ },
399
- { name: "Diffbot", detect: /Diffbot/ },
400
- { name: "cohere-ai", detect: /cohere-ai/ },
401
- { 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" },
402
484
  // Monitoring / archiving
403
- { name: "UptimeRobot", detect: /UptimeRobot/ },
404
- { name: "ia_archiver", detect: /ia_archiver/ },
485
+ { name: "UptimeRobot", detect: /UptimeRobot/, category: "monitoring" },
486
+ { name: "ia_archiver", detect: /ia_archiver/, category: "monitoring" },
405
487
  // Generic catch-all (must be last)
406
- { name: "GenericBot", detect: /(bot|crawler|spider|crawling|scraper)/i }
488
+ { name: "GenericBot", detect: /(bot|crawler|spider|crawling|scraper)/i, category: "generic" }
407
489
  ];
408
490
 
409
491
  // src/detectors/bot.ts
@@ -411,10 +493,10 @@ function detectBot(ua, customDefs) {
411
493
  const defs = customDefs ? [...BOT_DEFS.slice(0, -1), ...customDefs, BOT_DEFS[BOT_DEFS.length - 1]] : BOT_DEFS;
412
494
  for (const def of defs) {
413
495
  if (def.detect.test(ua)) {
414
- return { isBot: true, botName: def.name };
496
+ return { isBot: true, botName: def.name, botCategory: def.category };
415
497
  }
416
498
  }
417
- return { isBot: false, botName: "unknown" };
499
+ return { isBot: false, botName: "unknown", botCategory: "unknown" };
418
500
  }
419
501
 
420
502
  // src/constants/arch.ts
@@ -581,14 +663,15 @@ function parseUA(ua, options = {}) {
581
663
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
582
664
  const effectiveNav = (_a = options.ctx) != null ? _a : options.nav;
583
665
  const effectiveWindowsVersion = (_c = (_b = options.ctx) == null ? void 0 : _b.windowsVersion) != null ? _c : options.windowsVersion;
584
- const { browser: rawBrowser, version: rawVersion } = detectBrowser(ua);
585
- 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);
586
668
  let os = rawOs;
587
669
  let osVersion = rawOsVersion;
588
670
  const device = detectDevice(ua, effectiveNav);
671
+ const { vendor, model } = detectVendorModel(ua);
589
672
  const arch = detectArch(ua, (_d = options.ctx) != null ? _d : effectiveNav);
590
673
  const nav = effectiveNav;
591
- const { isBot, botName } = detectBot(ua, options.customBotDefs);
674
+ const { isBot, botName, botCategory } = detectBot(ua, options.customBotDefs);
592
675
  const isHeadless = detectHeadless(ua);
593
676
  const language = options.language || ((nav == null ? void 0 : nav.language) || (nav == null ? void 0 : nav.browserLanguage) ? getLanguage(nav) : "") || languageFromUA(ua);
594
677
  const platform = (nav == null ? void 0 : nav.platform) || platformFromUA(ua);
@@ -685,22 +768,39 @@ function parseUA(ua, options = {}) {
685
768
  }
686
769
  }
687
770
  }
688
- const engine = detectEngine(ua, browser, version);
771
+ const { engine, engineVersion } = detectEngine(ua, browser, version);
689
772
  const versionMajor = parseInt((_l = version.split(".")[0]) != null ? _l : "0", 10) || 0;
690
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;
691
785
  return {
692
786
  browser,
693
787
  version,
694
788
  versionMajor,
789
+ browserType: browser === "Brave" ? "browser" : browserType,
695
790
  engine,
791
+ engineVersion,
696
792
  os,
697
793
  osVersion,
794
+ osVersionName: finalOsVersionName,
698
795
  device,
796
+ vendor,
797
+ model,
699
798
  arch,
700
799
  isWebview: isWebview(ua),
701
800
  isHeadless,
702
801
  isBot,
703
802
  botName,
803
+ botCategory,
704
804
  language,
705
805
  platform,
706
806
  connectionType
@@ -970,6 +1070,7 @@ exports.detectDevice = detectDevice;
970
1070
  exports.detectEngine = detectEngine;
971
1071
  exports.detectHeadless = detectHeadless;
972
1072
  exports.detectOS = detectOs;
1073
+ exports.detectVendorModel = detectVendorModel;
973
1074
  exports.getEnvContext = getEnvContext;
974
1075
  exports.getLanguage = getLanguage;
975
1076
  exports.getNavContext = getNavContext;