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