ua-browser 1.4.0 → 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.mjs CHANGED
@@ -1,24 +1,23 @@
1
1
  // package.json
2
2
  var package_default = {
3
- version: "1.4.0"};
3
+ version: "1.4.1"};
4
4
 
5
5
  // src/constants/os.ts
6
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/ },
7
+ { name: "WebOS", priority: 20, detect: /hpwOS/, versionPattern: /hpwOS\/([\d.]+)/ },
8
+ { name: "Symbian", priority: 20, detect: /Symbian/, versionPattern: null },
9
+ { name: "MeeGo", priority: 20, detect: /MeeGo/, versionPattern: null },
10
+ { name: "BlackBerry", priority: 20, detect: /(BlackBerry|RIM)/, versionPattern: null },
11
+ { name: "FreeBSD", priority: 20, detect: /FreeBSD/, versionPattern: null },
12
+ { name: "Debian", priority: 20, detect: /Debian/, versionPattern: /Debian\/([\d.]+)/ },
13
+ { name: "Ubuntu", priority: 20, detect: /Ubuntu/, versionPattern: null },
14
+ { name: "Linux", priority: 10, detect: /(Linux|X11)/, versionPattern: null },
15
+ { name: "Chrome OS", priority: 30, detect: /CrOS/, versionPattern: null },
16
+ { name: "Tizen", priority: 20, detect: /Tizen/, versionPattern: /Tizen ([\d.]+)/ },
17
+ { name: "iOS", priority: 20, detect: /like Mac OS X/, versionPattern: /OS ([\d_]+) like/ },
20
18
  {
21
19
  name: "MacOS",
20
+ priority: 20,
22
21
  detect: /Macintosh/,
23
22
  versionPattern: /Mac OS X -?([\d_.]+)/,
24
23
  versionNames: {
@@ -36,27 +35,21 @@ var OS_DEFS = [
36
35
  "15": "Sequoia"
37
36
  }
38
37
  },
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.
38
+ { name: "visionOS", priority: 30, detect: /visionOS/, versionPattern: /visionOS ([\d_]+)/ },
39
+ { name: "tvOS", priority: 30, detect: /Apple TV/, versionPattern: /OS ([\d_]+) like/ },
40
+ { name: "Android", priority: 20, detect: /(Android|Adr)/, versionPattern: /(?:Android|Adr) ([\d.]+)/ },
47
41
  {
48
42
  name: "HarmonyOS",
43
+ priority: 30,
49
44
  detect: /HarmonyOS/,
50
45
  versionPattern: [/HarmonyOS[\s/]([\d.]+)/, /Android ([\d.]+)[;)]/],
51
- versionLookup: { "10": "2", "11": "3", "12": "3", "13": "4" }
46
+ versionLookup: { "10": "2", "11": "3", "12": "3", "13": "4", "14": "4" }
52
47
  },
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.
48
+ { name: "OpenHarmony", priority: 30, detect: /OpenHarmony/, versionPattern: /OpenHarmony[\s/]([\d.]+)/ },
49
+ { name: "KaiOS", priority: 30, detect: /KAIOS/, versionPattern: /KAIOS\/([\d.]+)/ },
58
50
  {
59
51
  name: "Windows",
52
+ priority: 10,
60
53
  detect: /Windows/,
61
54
  versionPattern: /Windows NT ([\d.]+)/,
62
55
  versionLookup: {
@@ -78,7 +71,7 @@ var OS_DEFS = [
78
71
  "11": "Windows 11"
79
72
  }
80
73
  },
81
- { name: "Windows Phone", detect: /(IEMobile|Windows Phone)/, versionPattern: /Windows Phone(?: OS)? ([\d.]+)/ }
74
+ { name: "Windows Phone", priority: 30, detect: /(IEMobile|Windows Phone)/, versionPattern: /Windows Phone(?: OS)? ([\d.]+)/ }
82
75
  ];
83
76
 
84
77
  // src/constants/browsers.ts
@@ -300,9 +293,11 @@ function lookupVersionName(map, version) {
300
293
  }
301
294
  function detectOs(ua, windowsVersion) {
302
295
  let matchedDef = null;
296
+ let bestPriority = -1;
303
297
  for (const def of OS_DEFS) {
304
- if (def.detect.test(ua)) {
298
+ if (def.detect.test(ua) && def.priority > bestPriority) {
305
299
  matchedDef = def;
300
+ bestPriority = def.priority;
306
301
  }
307
302
  }
308
303
  if (!matchedDef) return { os: "unknown", osVersion: "unknown", osVersionName: "unknown" };
@@ -451,7 +446,7 @@ var BOT_DEFS = [
451
446
  { name: "Facebookbot", detect: /(facebookexternalhit|FacebookBot)/, category: "social" },
452
447
  { name: "Twitterbot", detect: /Twitterbot/, category: "social" },
453
448
  { name: "LinkedInBot", detect: /LinkedInBot/, category: "social" },
454
- { name: "PinterestBot", detect: /Pinterest/, category: "social" },
449
+ { name: "PinterestBot", detect: /Pinterestbot/i, category: "social" },
455
450
  // Messaging link preview bots
456
451
  { name: "Slackbot", detect: /Slackbot/, category: "link-preview" },
457
452
  { name: "Discordbot", detect: /Discordbot/, category: "link-preview" },
@@ -580,6 +575,7 @@ var BRAND_TO_BROWSER = [
580
575
  ["Microsoft Edge", "Edge"],
581
576
  ["Opera", "Opera"],
582
577
  ["Vivaldi", "Vivaldi"],
578
+ ["Brave", "Brave"],
583
579
  ["Google Chrome", "Chrome"],
584
580
  ["Chromium", "Chromium"]
585
581
  ];
@@ -644,6 +640,82 @@ function normalizeBCP47(raw) {
644
640
  return p.toUpperCase();
645
641
  }).join("-");
646
642
  }
643
+ var ISO_639_1 = /* @__PURE__ */ new Set([
644
+ "af",
645
+ "am",
646
+ "ar",
647
+ "az",
648
+ "be",
649
+ "bg",
650
+ "bn",
651
+ "bs",
652
+ "ca",
653
+ "cs",
654
+ "cy",
655
+ "da",
656
+ "de",
657
+ "el",
658
+ "en",
659
+ "es",
660
+ "et",
661
+ "eu",
662
+ "fa",
663
+ "fi",
664
+ "fr",
665
+ "ga",
666
+ "gl",
667
+ "gu",
668
+ "he",
669
+ "hi",
670
+ "hr",
671
+ "hu",
672
+ "hy",
673
+ "id",
674
+ "is",
675
+ "it",
676
+ "ja",
677
+ "ka",
678
+ "kk",
679
+ "km",
680
+ "kn",
681
+ "ko",
682
+ "lt",
683
+ "lv",
684
+ "mk",
685
+ "ml",
686
+ "mn",
687
+ "mr",
688
+ "ms",
689
+ "mt",
690
+ "my",
691
+ "nb",
692
+ "ne",
693
+ "nl",
694
+ "no",
695
+ "pa",
696
+ "pl",
697
+ "pt",
698
+ "ro",
699
+ "ru",
700
+ "si",
701
+ "sk",
702
+ "sl",
703
+ "sq",
704
+ "sr",
705
+ "sv",
706
+ "sw",
707
+ "ta",
708
+ "te",
709
+ "th",
710
+ "tl",
711
+ "tr",
712
+ "uk",
713
+ "ur",
714
+ "uz",
715
+ "vi",
716
+ "zh",
717
+ "zu"
718
+ ]);
647
719
  function languageFromUA(ua) {
648
720
  const kwMatch = /\bLanguage\/([a-zA-Z]{2,3}(?:[-_][a-zA-Z]{2,4}){1,2})\b/i.exec(ua);
649
721
  if (kwMatch) return normalizeBCP47(kwMatch[1]);
@@ -653,6 +725,10 @@ function languageFromUA(ua) {
653
725
  const parts = m[1].replace(/_/g, "-").split("-");
654
726
  if (parts.length >= 2) return normalizeBCP47(m[1]);
655
727
  }
728
+ const bare = /[;(]\s*([a-z]{2,3})\s*[;)]/g;
729
+ while ((m = bare.exec(ua)) !== null) {
730
+ if (ISO_639_1.has(m[1])) return m[1];
731
+ }
656
732
  return "unknown";
657
733
  }
658
734
  function parseUA(ua, options = {}) {
@@ -709,20 +785,8 @@ function parseUA(ua, options = {}) {
709
785
  const opVer = (_h = (_g = /OPR\/([\d.]+)/.exec(ua)) != null ? _g : /OPT\/([\d.]+)/.exec(ua)) != null ? _h : /Opera\/([\d.]+)/.exec(ua);
710
786
  version = (_i = opVer == null ? void 0 : opVer[1]) != null ? _i : "unknown";
711
787
  }
712
- if (browser === "Chrome" && /\S+Browser\//.test(ua)) {
713
- const m = /(\S+Browser)\/([\d.]+)/.exec(ua);
714
- if (m) {
715
- browser = m[1];
716
- version = m[2];
717
- }
718
- }
719
- if (browser === "Firefox" && nav) {
720
- try {
721
- if (typeof clientInformation !== "undefined" || typeof u2f === "undefined") {
722
- browser = "Firefox Nightly";
723
- }
724
- } catch (e) {
725
- }
788
+ if (browser === "Firefox" && /Firefox\/[\d.]+a\d/.test(ua)) {
789
+ browser = "Firefox Nightly";
726
790
  }
727
791
  if (os === "iOS" && browser === "Safari") {
728
792
  const m = /Version\/([\d.]+)/.exec(ua);
@@ -765,7 +829,8 @@ function parseUA(ua, options = {}) {
765
829
  }
766
830
  }
767
831
  const { engine, engineVersion } = detectEngine(ua, browser, version);
768
- const versionMajor = parseInt((_l = version.split(".")[0]) != null ? _l : "0", 10) || 0;
832
+ const major = parseInt((_l = version.split(".")[0]) != null ? _l : "", 10);
833
+ const versionMajor = Number.isNaN(major) ? 0 : major;
769
834
  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
835
  const finalOsVersionName = os === "MacOS" || os === "Windows" ? (() => {
771
836
  var _a2;
@@ -1006,7 +1071,7 @@ function deriveWindowsVersion2(platformVersion) {
1006
1071
  return isNaN(major) ? null : major >= 13 ? "11" : "10";
1007
1072
  }
1008
1073
  function parseHeaders(headers) {
1009
- var _a, _b, _c, _d, _e;
1074
+ var _a, _b, _c, _d, _e, _f;
1010
1075
  const normalised = {};
1011
1076
  for (const key of Object.keys(headers)) {
1012
1077
  normalised[key.toLowerCase()] = headers[key];
@@ -1023,12 +1088,24 @@ function parseHeaders(headers) {
1023
1088
  const model = unquote(get("sec-ch-ua-model"));
1024
1089
  const platformVersion = unquote(get("sec-ch-ua-platform-version"));
1025
1090
  const platform = (_e = unquote(get("sec-ch-ua-platform"))) != null ? _e : "";
1091
+ const fullVersionListRaw = get("sec-ch-ua-full-version-list");
1092
+ const fullVersionList = [];
1093
+ if (fullVersionListRaw) {
1094
+ const re = /"([^"]+)";v="([^"]+)"/g;
1095
+ let m;
1096
+ while ((m = re.exec(fullVersionListRaw)) !== null) {
1097
+ fullVersionList.push({ brand: m[1], version: m[2] });
1098
+ }
1099
+ }
1026
1100
  const highEntropyData = {};
1027
1101
  if (architecture !== void 0) highEntropyData.architecture = architecture;
1028
1102
  if (bitness !== void 0) highEntropyData.bitness = bitness;
1029
1103
  if (model !== void 0) highEntropyData.model = model;
1030
1104
  if (platformVersion !== void 0) highEntropyData.platformVersion = platformVersion;
1105
+ if (fullVersionList.length > 0) highEntropyData.fullVersionList = fullVersionList;
1031
1106
  const isMobile = get("sec-ch-ua-mobile") === "?1";
1107
+ const secCHUA = (_f = get("sec-ch-ua")) != null ? _f : "";
1108
+ const hasBrave = /"Brave"/.test(secCHUA);
1032
1109
  const windowsVersion = platform === "Windows" ? deriveWindowsVersion2(platformVersion) : null;
1033
1110
  const ctx = {
1034
1111
  userAgent: ua,
@@ -1036,7 +1113,8 @@ function parseHeaders(headers) {
1036
1113
  language,
1037
1114
  maxTouchPoints: isMobile ? 1 : 0,
1038
1115
  highEntropyData: Object.keys(highEntropyData).length > 0 ? highEntropyData : void 0,
1039
- windowsVersion
1116
+ windowsVersion,
1117
+ hasBrave
1040
1118
  };
1041
1119
  return parseUA(ua, { ctx });
1042
1120
  }