ua-browser 1.4.0-beta.1 → 1.4.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.
package/dist/index.cjs CHANGED
@@ -4,7 +4,79 @@ 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.1"};
8
+
9
+ // src/constants/os.ts
10
+ var OS_DEFS = [
11
+ { name: "WebOS", priority: 20, detect: /hpwOS/, versionPattern: /hpwOS\/([\d.]+)/ },
12
+ { name: "Symbian", priority: 20, detect: /Symbian/, versionPattern: null },
13
+ { name: "MeeGo", priority: 20, detect: /MeeGo/, versionPattern: null },
14
+ { name: "BlackBerry", priority: 20, detect: /(BlackBerry|RIM)/, versionPattern: null },
15
+ { name: "FreeBSD", priority: 20, detect: /FreeBSD/, versionPattern: null },
16
+ { name: "Debian", priority: 20, detect: /Debian/, versionPattern: /Debian\/([\d.]+)/ },
17
+ { name: "Ubuntu", priority: 20, detect: /Ubuntu/, versionPattern: null },
18
+ { name: "Linux", priority: 10, detect: /(Linux|X11)/, versionPattern: null },
19
+ { name: "Chrome OS", priority: 30, detect: /CrOS/, versionPattern: null },
20
+ { name: "Tizen", priority: 20, detect: /Tizen/, versionPattern: /Tizen ([\d.]+)/ },
21
+ { name: "iOS", priority: 20, detect: /like Mac OS X/, versionPattern: /OS ([\d_]+) like/ },
22
+ {
23
+ name: "MacOS",
24
+ priority: 20,
25
+ detect: /Macintosh/,
26
+ versionPattern: /Mac OS X -?([\d_.]+)/,
27
+ versionNames: {
28
+ "10.9": "Mavericks",
29
+ "10.10": "Yosemite",
30
+ "10.11": "El Capitan",
31
+ "10.12": "Sierra",
32
+ "10.13": "High Sierra",
33
+ "10.14": "Mojave",
34
+ "10.15": "Catalina",
35
+ "11": "Big Sur",
36
+ "12": "Monterey",
37
+ "13": "Ventura",
38
+ "14": "Sonoma",
39
+ "15": "Sequoia"
40
+ }
41
+ },
42
+ { name: "visionOS", priority: 30, detect: /visionOS/, versionPattern: /visionOS ([\d_]+)/ },
43
+ { name: "tvOS", priority: 30, detect: /Apple TV/, versionPattern: /OS ([\d_]+) like/ },
44
+ { name: "Android", priority: 20, detect: /(Android|Adr)/, versionPattern: /(?:Android|Adr) ([\d.]+)/ },
45
+ {
46
+ name: "HarmonyOS",
47
+ priority: 30,
48
+ detect: /HarmonyOS/,
49
+ versionPattern: [/HarmonyOS[\s/]([\d.]+)/, /Android ([\d.]+)[;)]/],
50
+ versionLookup: { "10": "2", "11": "3", "12": "3", "13": "4", "14": "4" }
51
+ },
52
+ { name: "OpenHarmony", priority: 30, detect: /OpenHarmony/, versionPattern: /OpenHarmony[\s/]([\d.]+)/ },
53
+ { name: "KaiOS", priority: 30, detect: /KAIOS/, versionPattern: /KAIOS\/([\d.]+)/ },
54
+ {
55
+ name: "Windows",
56
+ priority: 10,
57
+ detect: /Windows/,
58
+ versionPattern: /Windows NT ([\d.]+)/,
59
+ versionLookup: {
60
+ "10": "10",
61
+ "6.4": "10",
62
+ "6.3": "8.1",
63
+ "6.2": "8",
64
+ "6.1": "7",
65
+ "6.0": "Vista",
66
+ "5.2": "XP",
67
+ "5.1": "XP",
68
+ "5.0": "2000"
69
+ },
70
+ versionNames: {
71
+ "7": "Windows 7",
72
+ "8": "Windows 8",
73
+ "8.1": "Windows 8.1",
74
+ "10": "Windows 10",
75
+ "11": "Windows 11"
76
+ }
77
+ },
78
+ { name: "Windows Phone", priority: 30, detect: /(IEMobile|Windows Phone)/, versionPattern: /Windows Phone(?: OS)? ([\d.]+)/ }
79
+ ];
8
80
 
9
81
  // src/constants/browsers.ts
10
82
  var BROWSER_DEFS = [
@@ -151,7 +223,7 @@ function detectBrowser(ua) {
151
223
  }
152
224
  }
153
225
  }
154
- if (!best) return { browser: "unknown", version: "unknown" };
226
+ if (!best) return { browser: "unknown", version: "unknown", browserType: "unknown", priority: 0 };
155
227
  let version = null;
156
228
  if (best.chromeLookup) {
157
229
  version = lookupFromChromeVersion(ua, best.chromeLookup);
@@ -160,7 +232,9 @@ function detectBrowser(ua) {
160
232
  const patterns = Array.isArray(best.versionPattern) ? best.versionPattern : [best.versionPattern];
161
233
  version = extractVersionFromPatterns(ua, patterns);
162
234
  }
163
- return { browser: best.name, version: version != null ? version : "unknown" };
235
+ const p = best.priority;
236
+ const browserType = p >= 500 ? "app" : p >= 300 ? "brand" : "browser";
237
+ return { browser: best.name, version: version != null ? version : "unknown", browserType, priority: p };
164
238
  }
165
239
 
166
240
  // src/constants/engines.ts
@@ -176,8 +250,17 @@ var ENGINE_DEFS = [
176
250
  ];
177
251
 
178
252
  // src/detectors/engine.ts
253
+ var ENGINE_VERSION_RE = {
254
+ WebKit: /AppleWebKit\/([\d.]+)/,
255
+ Blink: /AppleWebKit\/([\d.]+)/,
256
+ ArkWeb: /AppleWebKit\/([\d.]+)/,
257
+ Gecko: /Gecko\/([\d.]+)/,
258
+ Trident: /Trident\/([\d.]+)/,
259
+ Presto: /Presto\/([\d.]+)/,
260
+ KHTML: /KHTML\/([\d.]+)/
261
+ };
179
262
  function detectEngine(ua, browser, version) {
180
- var _a, _b;
263
+ var _a, _b, _c, _d;
181
264
  if (browser === void 0 || version === void 0) {
182
265
  const b = detectBrowser(ua);
183
266
  browser = browser != null ? browser : b.browser;
@@ -199,72 +282,29 @@ function detectEngine(ua, browser, version) {
199
282
  if (browser === "Edge") {
200
283
  engine = parseInt(version, 10) > 75 ? "Blink" : "EdgeHTML";
201
284
  }
202
- return engine;
285
+ const engineVersion = ENGINE_VERSION_RE[engine] ? (_d = (_c = ENGINE_VERSION_RE[engine].exec(ua)) == null ? void 0 : _c[1]) != null ? _d : "unknown" : "unknown";
286
+ return { engine, engineVersion };
203
287
  }
204
288
 
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
289
  // src/detectors/os.ts
290
+ function lookupVersionName(map, version) {
291
+ const parts = version.split(".");
292
+ for (let len = parts.length; len >= 1; len--) {
293
+ const key = parts.slice(0, len).join(".");
294
+ if (Object.prototype.hasOwnProperty.call(map, key)) return map[key];
295
+ }
296
+ return "unknown";
297
+ }
260
298
  function detectOs(ua, windowsVersion) {
261
299
  let matchedDef = null;
300
+ let bestPriority = -1;
262
301
  for (const def of OS_DEFS) {
263
- if (def.detect.test(ua)) {
302
+ if (def.detect.test(ua) && def.priority > bestPriority) {
264
303
  matchedDef = def;
304
+ bestPriority = def.priority;
265
305
  }
266
306
  }
267
- if (!matchedDef) return { os: "unknown", osVersion: "unknown" };
307
+ if (!matchedDef) return { os: "unknown", osVersion: "unknown", osVersionName: "unknown" };
268
308
  let osVersion = "unknown";
269
309
  if (matchedDef.versionPattern) {
270
310
  const raw = Array.isArray(matchedDef.versionPattern) ? extractVersionFromPatterns(ua, matchedDef.versionPattern) : extractVersion(ua, matchedDef.versionPattern);
@@ -288,7 +328,8 @@ function detectOs(ua, windowsVersion) {
288
328
  if (matchedDef.name === "Windows" && windowsVersion) {
289
329
  osVersion = windowsVersion;
290
330
  }
291
- return { os: matchedDef.name, osVersion };
331
+ const osVersionName = matchedDef.versionNames ? lookupVersionName(matchedDef.versionNames, osVersion) : "unknown";
332
+ return { os: matchedDef.name, osVersion, osVersionName };
292
333
  }
293
334
 
294
335
  // src/constants/devices.ts
@@ -353,57 +394,93 @@ function detectDevice(ua, nav) {
353
394
  }
354
395
  return (_b = (_a = hwDetect()) != null ? _a : uaDetect()) != null ? _b : "PC";
355
396
  }
397
+ var VENDOR_MAP = [
398
+ [/^SM-|^GT-|^SGH-|^SCH-|^SHW-|^SPH-|^SAMSUNG /i, "Samsung"],
399
+ [/^Pixel |^Nexus /, "Google"],
400
+ [/^moto |^motorola /i, "Motorola"],
401
+ [/^HONOR /i, "Honor"],
402
+ [/^ELS-|^LYA-|^HMA-|^VOG-|^ANA-|^CLT-|^JNY-|^NOH-|^PLK-|^BAH-|^BKL-|^COL-|^DUB-|^FIG-|^KIW-|^MAR-|^VTR-|^WAS-/i, "Huawei"],
403
+ [/^CPH/, "OPPO"],
404
+ [/^RMX/, "Realme"],
405
+ [/^V\d{4}[A-Z]|^vivo /i, "Vivo"],
406
+ [/^iqoo/i, "Vivo"],
407
+ [/^Redmi |^POCO |^Mi /, "Xiaomi"],
408
+ [/^2\d{9}|^2\d{3}[A-Z]/, "Xiaomi"],
409
+ [/^OnePlus |^IN2|^KB2|^LE2/i, "OnePlus"],
410
+ [/^LM-|^LGE |^LG-/i, "LG"],
411
+ [/^HTC/i, "HTC"],
412
+ [/^Nokia/i, "Nokia"],
413
+ [/^XQ-/, "Sony"],
414
+ [/^ASUS_|^ZB6|^ZS6|^ZE[56]/i, "Asus"],
415
+ [/^Quest /i, "Meta"]
416
+ ];
417
+ var ANDROID_MODEL_RE = /Android [0-9.]+;(?:\s*U;)?(?:\s*[a-z]{2}[-_][a-zA-Z]{2,4};)?\s*([^;)]+?)(?:\s+Build\/|[;)])/;
418
+ function detectVendorModel(ua) {
419
+ if (/visionOS/.test(ua)) return { vendor: "Apple", model: "Apple Vision Pro" };
420
+ if (/iPhone/.test(ua)) return { vendor: "Apple", model: "iPhone" };
421
+ if (/iPad/.test(ua)) return { vendor: "Apple", model: "iPad" };
422
+ if (/iPod/.test(ua)) return { vendor: "Apple", model: "iPod touch" };
423
+ const m = ANDROID_MODEL_RE.exec(ua);
424
+ if (m) {
425
+ const model = m[1].trim();
426
+ for (const [re, vendor] of VENDOR_MAP) {
427
+ if (re.test(model)) return { vendor, model };
428
+ }
429
+ return { vendor: "unknown", model };
430
+ }
431
+ return { vendor: "unknown", model: "unknown" };
432
+ }
356
433
 
357
434
  // src/constants/bots.ts
358
435
  var BOT_DEFS = [
359
436
  // 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/ },
437
+ { name: "Googlebot", detect: /Googlebot/, category: "search-engine" },
438
+ { name: "Bingbot", detect: /(bingbot|BingPreview)/, category: "search-engine" },
439
+ { name: "Baiduspider", detect: /(Baiduspider|BaiduMobaider)/, category: "search-engine" },
440
+ { name: "Bytespider", detect: /Bytespider/, category: "search-engine" },
441
+ { name: "YandexBot", detect: /YandexBot/, category: "search-engine" },
442
+ { name: "DuckDuckBot", detect: /DuckDuckBot/, category: "search-engine" },
443
+ { name: "Slurp", detect: /Slurp/, category: "search-engine" },
444
+ { name: "Sogou", detect: /(Sogou|sogou).*[Ss]pider/, category: "search-engine" },
445
+ { name: "360Spider", detect: /360Spider/, category: "search-engine" },
446
+ { name: "PetalBot", detect: /PetalBot/, category: "search-engine" },
447
+ { name: "Applebot-Extended", detect: /Applebot-Extended/, category: "search-engine" },
448
+ { name: "Applebot", detect: /Applebot/, category: "search-engine" },
372
449
  // Social media crawlers
373
- { name: "Facebookbot", detect: /(facebookexternalhit|FacebookBot)/ },
374
- { name: "Twitterbot", detect: /Twitterbot/ },
375
- { name: "LinkedInBot", detect: /LinkedInBot/ },
376
- { name: "PinterestBot", detect: /Pinterest/ },
450
+ { name: "Facebookbot", detect: /(facebookexternalhit|FacebookBot)/, category: "social" },
451
+ { name: "Twitterbot", detect: /Twitterbot/, category: "social" },
452
+ { name: "LinkedInBot", detect: /LinkedInBot/, category: "social" },
453
+ { name: "PinterestBot", detect: /Pinterestbot/i, category: "social" },
377
454
  // Messaging link preview bots
378
- { name: "Slackbot", detect: /Slackbot/ },
379
- { name: "Discordbot", detect: /Discordbot/ },
380
- { name: "TelegramBot", detect: /TelegramBot/ },
381
- { name: "WhatsApp", detect: /WhatsApp/ },
455
+ { name: "Slackbot", detect: /Slackbot/, category: "link-preview" },
456
+ { name: "Discordbot", detect: /Discordbot/, category: "link-preview" },
457
+ { name: "TelegramBot", detect: /TelegramBot/, category: "link-preview" },
458
+ { name: "WhatsApp", detect: /WhatsApp/, category: "link-preview" },
382
459
  // 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/ },
460
+ { name: "SemrushBot", detect: /SemrushBot/, category: "seo-tool" },
461
+ { name: "AhrefsBot", detect: /AhrefsBot/, category: "seo-tool" },
462
+ { name: "MJ12bot", detect: /MJ12bot/, category: "seo-tool" },
463
+ { name: "ScreamingFrog", detect: /Screaming Frog/, category: "seo-tool" },
464
+ { name: "DataForSeoBot", detect: /DataForSeoBot/, category: "seo-tool" },
388
465
  // 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/ },
466
+ { name: "GPTBot", detect: /GPTBot/, category: "ai-llm" },
467
+ { name: "OAI-SearchBot", detect: /OAI-SearchBot/, category: "ai-llm" },
468
+ { name: "ChatGPT-User", detect: /ChatGPT-User/, category: "ai-llm" },
469
+ { name: "ClaudeBot", detect: /ClaudeBot/, category: "ai-llm" },
470
+ { name: "PerplexityBot", detect: /PerplexityBot/, category: "ai-llm" },
471
+ { name: "CCBot", detect: /CCBot/, category: "ai-llm" },
472
+ { name: "AdsBot", detect: /AdsBot-Google/, category: "ai-llm" },
473
+ { name: "Google-Extended", detect: /Google-Extended/, category: "ai-llm" },
474
+ { name: "Meta-ExternalAgent", detect: /meta-externalagent/i, category: "ai-llm" },
475
+ { name: "Amazonbot", detect: /Amazonbot/, category: "ai-llm" },
476
+ { name: "Diffbot", detect: /Diffbot/, category: "ai-llm" },
477
+ { name: "cohere-ai", detect: /cohere-ai/, category: "ai-llm" },
478
+ { name: "YouBot", detect: /YouBot/, category: "ai-llm" },
402
479
  // Monitoring / archiving
403
- { name: "UptimeRobot", detect: /UptimeRobot/ },
404
- { name: "ia_archiver", detect: /ia_archiver/ },
480
+ { name: "UptimeRobot", detect: /UptimeRobot/, category: "monitoring" },
481
+ { name: "ia_archiver", detect: /ia_archiver/, category: "monitoring" },
405
482
  // Generic catch-all (must be last)
406
- { name: "GenericBot", detect: /(bot|crawler|spider|crawling|scraper)/i }
483
+ { name: "GenericBot", detect: /(bot|crawler|spider|crawling|scraper)/i, category: "generic" }
407
484
  ];
408
485
 
409
486
  // src/detectors/bot.ts
@@ -411,10 +488,10 @@ function detectBot(ua, customDefs) {
411
488
  const defs = customDefs ? [...BOT_DEFS.slice(0, -1), ...customDefs, BOT_DEFS[BOT_DEFS.length - 1]] : BOT_DEFS;
412
489
  for (const def of defs) {
413
490
  if (def.detect.test(ua)) {
414
- return { isBot: true, botName: def.name };
491
+ return { isBot: true, botName: def.name, botCategory: def.category };
415
492
  }
416
493
  }
417
- return { isBot: false, botName: "unknown" };
494
+ return { isBot: false, botName: "unknown", botCategory: "unknown" };
418
495
  }
419
496
 
420
497
  // src/constants/arch.ts
@@ -502,6 +579,7 @@ var BRAND_TO_BROWSER = [
502
579
  ["Microsoft Edge", "Edge"],
503
580
  ["Opera", "Opera"],
504
581
  ["Vivaldi", "Vivaldi"],
582
+ ["Brave", "Brave"],
505
583
  ["Google Chrome", "Chrome"],
506
584
  ["Chromium", "Chromium"]
507
585
  ];
@@ -566,6 +644,82 @@ function normalizeBCP47(raw) {
566
644
  return p.toUpperCase();
567
645
  }).join("-");
568
646
  }
647
+ var ISO_639_1 = /* @__PURE__ */ new Set([
648
+ "af",
649
+ "am",
650
+ "ar",
651
+ "az",
652
+ "be",
653
+ "bg",
654
+ "bn",
655
+ "bs",
656
+ "ca",
657
+ "cs",
658
+ "cy",
659
+ "da",
660
+ "de",
661
+ "el",
662
+ "en",
663
+ "es",
664
+ "et",
665
+ "eu",
666
+ "fa",
667
+ "fi",
668
+ "fr",
669
+ "ga",
670
+ "gl",
671
+ "gu",
672
+ "he",
673
+ "hi",
674
+ "hr",
675
+ "hu",
676
+ "hy",
677
+ "id",
678
+ "is",
679
+ "it",
680
+ "ja",
681
+ "ka",
682
+ "kk",
683
+ "km",
684
+ "kn",
685
+ "ko",
686
+ "lt",
687
+ "lv",
688
+ "mk",
689
+ "ml",
690
+ "mn",
691
+ "mr",
692
+ "ms",
693
+ "mt",
694
+ "my",
695
+ "nb",
696
+ "ne",
697
+ "nl",
698
+ "no",
699
+ "pa",
700
+ "pl",
701
+ "pt",
702
+ "ro",
703
+ "ru",
704
+ "si",
705
+ "sk",
706
+ "sl",
707
+ "sq",
708
+ "sr",
709
+ "sv",
710
+ "sw",
711
+ "ta",
712
+ "te",
713
+ "th",
714
+ "tl",
715
+ "tr",
716
+ "uk",
717
+ "ur",
718
+ "uz",
719
+ "vi",
720
+ "zh",
721
+ "zu"
722
+ ]);
569
723
  function languageFromUA(ua) {
570
724
  const kwMatch = /\bLanguage\/([a-zA-Z]{2,3}(?:[-_][a-zA-Z]{2,4}){1,2})\b/i.exec(ua);
571
725
  if (kwMatch) return normalizeBCP47(kwMatch[1]);
@@ -575,20 +729,25 @@ function languageFromUA(ua) {
575
729
  const parts = m[1].replace(/_/g, "-").split("-");
576
730
  if (parts.length >= 2) return normalizeBCP47(m[1]);
577
731
  }
732
+ const bare = /[;(]\s*([a-z]{2,3})\s*[;)]/g;
733
+ while ((m = bare.exec(ua)) !== null) {
734
+ if (ISO_639_1.has(m[1])) return m[1];
735
+ }
578
736
  return "unknown";
579
737
  }
580
738
  function parseUA(ua, options = {}) {
581
739
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
582
740
  const effectiveNav = (_a = options.ctx) != null ? _a : options.nav;
583
741
  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);
742
+ const { browser: rawBrowser, version: rawVersion, browserType } = detectBrowser(ua);
743
+ const { os: rawOs, osVersion: rawOsVersion, osVersionName } = detectOs(ua, effectiveWindowsVersion);
586
744
  let os = rawOs;
587
745
  let osVersion = rawOsVersion;
588
746
  const device = detectDevice(ua, effectiveNav);
747
+ const { vendor, model } = detectVendorModel(ua);
589
748
  const arch = detectArch(ua, (_d = options.ctx) != null ? _d : effectiveNav);
590
749
  const nav = effectiveNav;
591
- const { isBot, botName } = detectBot(ua, options.customBotDefs);
750
+ const { isBot, botName, botCategory } = detectBot(ua, options.customBotDefs);
592
751
  const isHeadless = detectHeadless(ua);
593
752
  const language = options.language || ((nav == null ? void 0 : nav.language) || (nav == null ? void 0 : nav.browserLanguage) ? getLanguage(nav) : "") || languageFromUA(ua);
594
753
  const platform = (nav == null ? void 0 : nav.platform) || platformFromUA(ua);
@@ -630,20 +789,8 @@ function parseUA(ua, options = {}) {
630
789
  const opVer = (_h = (_g = /OPR\/([\d.]+)/.exec(ua)) != null ? _g : /OPT\/([\d.]+)/.exec(ua)) != null ? _h : /Opera\/([\d.]+)/.exec(ua);
631
790
  version = (_i = opVer == null ? void 0 : opVer[1]) != null ? _i : "unknown";
632
791
  }
633
- if (browser === "Chrome" && /\S+Browser\//.test(ua)) {
634
- const m = /(\S+Browser)\/([\d.]+)/.exec(ua);
635
- if (m) {
636
- browser = m[1];
637
- version = m[2];
638
- }
639
- }
640
- if (browser === "Firefox" && nav) {
641
- try {
642
- if (typeof clientInformation !== "undefined" || typeof u2f === "undefined") {
643
- browser = "Firefox Nightly";
644
- }
645
- } catch (e) {
646
- }
792
+ if (browser === "Firefox" && /Firefox\/[\d.]+a\d/.test(ua)) {
793
+ browser = "Firefox Nightly";
647
794
  }
648
795
  if (os === "iOS" && browser === "Safari") {
649
796
  const m = /Version\/([\d.]+)/.exec(ua);
@@ -685,22 +832,40 @@ function parseUA(ua, options = {}) {
685
832
  }
686
833
  }
687
834
  }
688
- const engine = detectEngine(ua, browser, version);
689
- const versionMajor = parseInt((_l = version.split(".")[0]) != null ? _l : "0", 10) || 0;
835
+ const { engine, engineVersion } = detectEngine(ua, browser, version);
836
+ const major = parseInt((_l = version.split(".")[0]) != null ? _l : "", 10);
837
+ const versionMajor = Number.isNaN(major) ? 0 : major;
690
838
  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";
839
+ const finalOsVersionName = os === "MacOS" || os === "Windows" ? (() => {
840
+ var _a2;
841
+ const map = (_a2 = OS_DEFS.find((d) => d.name === os)) == null ? void 0 : _a2.versionNames;
842
+ if (!map) return "unknown";
843
+ const parts = osVersion.split(".");
844
+ for (let len = parts.length; len >= 1; len--) {
845
+ const key = parts.slice(0, len).join(".");
846
+ if (Object.prototype.hasOwnProperty.call(map, key)) return map[key];
847
+ }
848
+ return "unknown";
849
+ })() : osVersionName;
691
850
  return {
692
851
  browser,
693
852
  version,
694
853
  versionMajor,
854
+ browserType: browser === "Brave" ? "browser" : browserType,
695
855
  engine,
856
+ engineVersion,
696
857
  os,
697
858
  osVersion,
859
+ osVersionName: finalOsVersionName,
698
860
  device,
861
+ vendor,
862
+ model,
699
863
  arch,
700
864
  isWebview: isWebview(ua),
701
865
  isHeadless,
702
866
  isBot,
703
867
  botName,
868
+ botCategory,
704
869
  language,
705
870
  platform,
706
871
  connectionType
@@ -910,7 +1075,7 @@ function deriveWindowsVersion2(platformVersion) {
910
1075
  return isNaN(major) ? null : major >= 13 ? "11" : "10";
911
1076
  }
912
1077
  function parseHeaders(headers) {
913
- var _a, _b, _c, _d, _e;
1078
+ var _a, _b, _c, _d, _e, _f;
914
1079
  const normalised = {};
915
1080
  for (const key of Object.keys(headers)) {
916
1081
  normalised[key.toLowerCase()] = headers[key];
@@ -927,12 +1092,24 @@ function parseHeaders(headers) {
927
1092
  const model = unquote(get("sec-ch-ua-model"));
928
1093
  const platformVersion = unquote(get("sec-ch-ua-platform-version"));
929
1094
  const platform = (_e = unquote(get("sec-ch-ua-platform"))) != null ? _e : "";
1095
+ const fullVersionListRaw = get("sec-ch-ua-full-version-list");
1096
+ const fullVersionList = [];
1097
+ if (fullVersionListRaw) {
1098
+ const re = /"([^"]+)";v="([^"]+)"/g;
1099
+ let m;
1100
+ while ((m = re.exec(fullVersionListRaw)) !== null) {
1101
+ fullVersionList.push({ brand: m[1], version: m[2] });
1102
+ }
1103
+ }
930
1104
  const highEntropyData = {};
931
1105
  if (architecture !== void 0) highEntropyData.architecture = architecture;
932
1106
  if (bitness !== void 0) highEntropyData.bitness = bitness;
933
1107
  if (model !== void 0) highEntropyData.model = model;
934
1108
  if (platformVersion !== void 0) highEntropyData.platformVersion = platformVersion;
1109
+ if (fullVersionList.length > 0) highEntropyData.fullVersionList = fullVersionList;
935
1110
  const isMobile = get("sec-ch-ua-mobile") === "?1";
1111
+ const secCHUA = (_f = get("sec-ch-ua")) != null ? _f : "";
1112
+ const hasBrave = /"Brave"/.test(secCHUA);
936
1113
  const windowsVersion = platform === "Windows" ? deriveWindowsVersion2(platformVersion) : null;
937
1114
  const ctx = {
938
1115
  userAgent: ua,
@@ -940,7 +1117,8 @@ function parseHeaders(headers) {
940
1117
  language,
941
1118
  maxTouchPoints: isMobile ? 1 : 0,
942
1119
  highEntropyData: Object.keys(highEntropyData).length > 0 ? highEntropyData : void 0,
943
- windowsVersion
1120
+ windowsVersion,
1121
+ hasBrave
944
1122
  };
945
1123
  return parseUA(ua, { ctx });
946
1124
  }
@@ -970,6 +1148,7 @@ exports.detectDevice = detectDevice;
970
1148
  exports.detectEngine = detectEngine;
971
1149
  exports.detectHeadless = detectHeadless;
972
1150
  exports.detectOS = detectOs;
1151
+ exports.detectVendorModel = detectVendorModel;
973
1152
  exports.getEnvContext = getEnvContext;
974
1153
  exports.getLanguage = getLanguage;
975
1154
  exports.getNavContext = getNavContext;